mirror of
https://github.com/langgenius/dify.git
synced 2026-01-18 21:10:00 +00:00
Compare commits
6 Commits
test/datas
...
refactor/l
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1cf3e599df | ||
|
|
a85946d3e7 | ||
|
|
9e4d3c75ae | ||
|
|
70bea85624 | ||
|
|
b75b7d6c61 | ||
|
|
e819b804ba |
@@ -18,6 +18,7 @@ import { useStore } from '@/app/components/app/store'
|
||||
import { PipelineFill, PipelineLine } from '@/app/components/base/icons/src/vender/pipeline'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import ExtraInfo from '@/app/components/datasets/extra-info'
|
||||
import { STORAGE_KEYS } from '@/config/storage-keys'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import DatasetDetailContext from '@/context/dataset-detail'
|
||||
import { useEventEmitterContextContext } from '@/context/event-emitter'
|
||||
@@ -25,6 +26,7 @@ import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
||||
import useDocumentTitle from '@/hooks/use-document-title'
|
||||
import { useDatasetDetail, useDatasetRelatedApps } from '@/service/knowledge/use-dataset'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import { storage } from '@/utils/storage'
|
||||
|
||||
export type IAppDetailLayoutProps = {
|
||||
children: React.ReactNode
|
||||
@@ -40,7 +42,7 @@ const DatasetDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
|
||||
const pathname = usePathname()
|
||||
const hideSideBar = pathname.endsWith('documents/create') || pathname.endsWith('documents/create-from-pipeline')
|
||||
const isPipelineCanvas = pathname.endsWith('/pipeline')
|
||||
const workflowCanvasMaximize = localStorage.getItem('workflow-canvas-maximize') === 'true'
|
||||
const workflowCanvasMaximize = storage.getBoolean(STORAGE_KEYS.WORKFLOW.CANVAS_MAXIMIZE, false)
|
||||
const [hideHeader, setHideHeader] = useState(workflowCanvasMaximize)
|
||||
const { eventEmitter } = useEventEmitterContextContext()
|
||||
|
||||
|
||||
@@ -5,9 +5,11 @@ import * as React from 'react'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { useShallow } from 'zustand/react/shallow'
|
||||
import { useStore as useAppStore } from '@/app/components/app/store'
|
||||
import { STORAGE_KEYS } from '@/config/storage-keys'
|
||||
import { useEventEmitterContextContext } from '@/context/event-emitter'
|
||||
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import { storage } from '@/utils/storage'
|
||||
import Divider from '../base/divider'
|
||||
import { getKeyboardKeyCodeBySystem } from '../workflow/utils'
|
||||
import AppInfo from './app-info'
|
||||
@@ -53,7 +55,7 @@ const AppDetailNav = ({
|
||||
const pathname = usePathname()
|
||||
const inWorkflowCanvas = pathname.endsWith('/workflow')
|
||||
const isPipelineCanvas = pathname.endsWith('/pipeline')
|
||||
const workflowCanvasMaximize = localStorage.getItem('workflow-canvas-maximize') === 'true'
|
||||
const workflowCanvasMaximize = storage.getBoolean(STORAGE_KEYS.WORKFLOW.CANVAS_MAXIMIZE, false)
|
||||
const [hideHeader, setHideHeader] = useState(workflowCanvasMaximize)
|
||||
const { eventEmitter } = useEventEmitterContextContext()
|
||||
|
||||
@@ -64,7 +66,7 @@ const AppDetailNav = ({
|
||||
|
||||
useEffect(() => {
|
||||
if (appSidebarExpand) {
|
||||
localStorage.setItem('app-detail-collapse-or-expand', appSidebarExpand)
|
||||
storage.set(STORAGE_KEYS.APP.DETAIL_COLLAPSE, appSidebarExpand)
|
||||
setAppSidebarExpand(appSidebarExpand)
|
||||
}
|
||||
}, [appSidebarExpand, setAppSidebarExpand])
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
import { usePathname } from 'next/navigation'
|
||||
import * as React from 'react'
|
||||
import { useState } from 'react'
|
||||
import { STORAGE_KEYS } from '@/config/storage-keys'
|
||||
import { useEventEmitterContextContext } from '@/context/event-emitter'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import { storage } from '@/utils/storage'
|
||||
import s from './index.module.css'
|
||||
|
||||
type HeaderWrapperProps = {
|
||||
@@ -18,7 +20,7 @@ const HeaderWrapper = ({
|
||||
// Check if the current path is a workflow canvas & fullscreen
|
||||
const inWorkflowCanvas = pathname.endsWith('/workflow')
|
||||
const isPipelineCanvas = pathname.endsWith('/pipeline')
|
||||
const workflowCanvasMaximize = localStorage.getItem('workflow-canvas-maximize') === 'true'
|
||||
const workflowCanvasMaximize = storage.getBoolean(STORAGE_KEYS.WORKFLOW.CANVAS_MAXIMIZE, false)
|
||||
const [hideHeader, setHideHeader] = useState(workflowCanvasMaximize)
|
||||
const { eventEmitter } = useEventEmitterContextContext()
|
||||
|
||||
|
||||
@@ -5,7 +5,9 @@ import {
|
||||
useCallback,
|
||||
} from 'react'
|
||||
import { useReactFlow, useStoreApi } from 'reactflow'
|
||||
import { STORAGE_KEYS } from '@/config/storage-keys'
|
||||
import { useEventEmitterContextContext } from '@/context/event-emitter'
|
||||
import { storage } from '@/utils/storage'
|
||||
import {
|
||||
CUSTOM_NODE,
|
||||
NODE_LAYOUT_HORIZONTAL_PADDING,
|
||||
@@ -342,7 +344,7 @@ export const useWorkflowCanvasMaximize = () => {
|
||||
return
|
||||
|
||||
setMaximizeCanvas(!maximizeCanvas)
|
||||
localStorage.setItem('workflow-canvas-maximize', String(!maximizeCanvas))
|
||||
storage.set(STORAGE_KEYS.WORKFLOW.CANVAS_MAXIMIZE, !maximizeCanvas)
|
||||
eventEmitter?.emit({
|
||||
type: 'workflow-canvas-maximize',
|
||||
payload: !maximizeCanvas,
|
||||
|
||||
@@ -26,12 +26,12 @@ const Wrap = ({
|
||||
isExpand,
|
||||
children,
|
||||
}: Props) => {
|
||||
const panelWidth = useStore(state => state.panelWidth)
|
||||
const nodePanelWidth = useStore(state => state.nodePanelWidth)
|
||||
const wrapStyle = (() => {
|
||||
if (isExpand) {
|
||||
return {
|
||||
...style,
|
||||
width: panelWidth - 1,
|
||||
width: nodePanelWidth - 1,
|
||||
}
|
||||
}
|
||||
return style
|
||||
|
||||
@@ -59,12 +59,14 @@ import {
|
||||
hasRetryNode,
|
||||
isSupportCustomRunForm,
|
||||
} from '@/app/components/workflow/utils'
|
||||
import { STORAGE_KEYS } from '@/config/storage-keys'
|
||||
import { useModalContext } from '@/context/modal-context'
|
||||
import { useAllBuiltInTools } from '@/service/use-tools'
|
||||
import { useAllTriggerPlugins } from '@/service/use-triggers'
|
||||
import { FlowType } from '@/types/common'
|
||||
import { canFindTool } from '@/utils'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import { storage } from '@/utils/storage'
|
||||
import { useResizePanel } from '../../hooks/use-resize-panel'
|
||||
import BeforeRunForm from '../before-run-form'
|
||||
import PanelWrap from '../before-run-form/panel-wrap'
|
||||
@@ -137,7 +139,7 @@ const BasePanel: FC<BasePanelProps> = ({
|
||||
const newValue = Math.max(400, Math.min(width, maxNodePanelWidth))
|
||||
|
||||
if (source === 'user')
|
||||
localStorage.setItem('workflow-node-panel-width', `${newValue}`)
|
||||
storage.set(STORAGE_KEYS.WORKFLOW.NODE_PANEL_WIDTH, newValue)
|
||||
|
||||
setNodePanelWidth(newValue)
|
||||
}, [maxNodePanelWidth, setNodePanelWidth])
|
||||
|
||||
@@ -18,7 +18,9 @@ import Tooltip from '@/app/components/base/tooltip'
|
||||
import { useEdgesInteractionsWithoutSync } from '@/app/components/workflow/hooks/use-edges-interactions-without-sync'
|
||||
import { useNodesInteractionsWithoutSync } from '@/app/components/workflow/hooks/use-nodes-interactions-without-sync'
|
||||
import { useStore } from '@/app/components/workflow/store'
|
||||
import { STORAGE_KEYS } from '@/config/storage-keys'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import { storage } from '@/utils/storage'
|
||||
import {
|
||||
useWorkflowInteractions,
|
||||
} from '../../hooks'
|
||||
@@ -56,7 +58,7 @@ const DebugAndPreview = () => {
|
||||
const setPanelWidth = useStore(s => s.setPreviewPanelWidth)
|
||||
const handleResize = useCallback((width: number, source: 'user' | 'system' = 'user') => {
|
||||
if (source === 'user')
|
||||
localStorage.setItem('debug-and-preview-panel-width', `${width}`)
|
||||
storage.set(STORAGE_KEYS.WORKFLOW.PREVIEW_PANEL_WIDTH, width)
|
||||
setPanelWidth(width)
|
||||
}, [setPanelWidth])
|
||||
const maxPanelWidth = useMemo(() => {
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import type { StateCreator } from 'zustand'
|
||||
import { STORAGE_KEYS } from '@/config/storage-keys'
|
||||
import { storage } from '@/utils/storage'
|
||||
|
||||
export type LayoutSliceShape = {
|
||||
workflowCanvasWidth?: number
|
||||
workflowCanvasHeight?: number
|
||||
setWorkflowCanvasWidth: (width: number) => void
|
||||
setWorkflowCanvasHeight: (height: number) => void
|
||||
// rightPanelWidth - otherPanelWidth = nodePanelWidth
|
||||
rightPanelWidth?: number
|
||||
setRightPanelWidth: (width: number) => void
|
||||
nodePanelWidth: number
|
||||
@@ -14,11 +15,11 @@ export type LayoutSliceShape = {
|
||||
setPreviewPanelWidth: (width: number) => void
|
||||
otherPanelWidth: number
|
||||
setOtherPanelWidth: (width: number) => void
|
||||
bottomPanelWidth: number // min-width = 400px; default-width = auto || 480px;
|
||||
bottomPanelWidth: number
|
||||
setBottomPanelWidth: (width: number) => void
|
||||
bottomPanelHeight: number
|
||||
setBottomPanelHeight: (height: number) => void
|
||||
variableInspectPanelHeight: number // min-height = 120px; default-height = 320px;
|
||||
variableInspectPanelHeight: number
|
||||
setVariableInspectPanelHeight: (height: number) => void
|
||||
maximizeCanvas: boolean
|
||||
setMaximizeCanvas: (maximize: boolean) => void
|
||||
@@ -31,9 +32,9 @@ export const createLayoutSlice: StateCreator<LayoutSliceShape> = set => ({
|
||||
setWorkflowCanvasHeight: height => set(() => ({ workflowCanvasHeight: height })),
|
||||
rightPanelWidth: undefined,
|
||||
setRightPanelWidth: width => set(() => ({ rightPanelWidth: width })),
|
||||
nodePanelWidth: localStorage.getItem('workflow-node-panel-width') ? Number.parseFloat(localStorage.getItem('workflow-node-panel-width')!) : 400,
|
||||
nodePanelWidth: storage.getNumber(STORAGE_KEYS.WORKFLOW.NODE_PANEL_WIDTH, 420),
|
||||
setNodePanelWidth: width => set(() => ({ nodePanelWidth: width })),
|
||||
previewPanelWidth: localStorage.getItem('debug-and-preview-panel-width') ? Number.parseFloat(localStorage.getItem('debug-and-preview-panel-width')!) : 400,
|
||||
previewPanelWidth: storage.getNumber(STORAGE_KEYS.WORKFLOW.PREVIEW_PANEL_WIDTH, 400),
|
||||
setPreviewPanelWidth: width => set(() => ({ previewPanelWidth: width })),
|
||||
otherPanelWidth: 400,
|
||||
setOtherPanelWidth: width => set(() => ({ otherPanelWidth: width })),
|
||||
@@ -41,8 +42,8 @@ export const createLayoutSlice: StateCreator<LayoutSliceShape> = set => ({
|
||||
setBottomPanelWidth: width => set(() => ({ bottomPanelWidth: width })),
|
||||
bottomPanelHeight: 324,
|
||||
setBottomPanelHeight: height => set(() => ({ bottomPanelHeight: height })),
|
||||
variableInspectPanelHeight: localStorage.getItem('workflow-variable-inpsect-panel-height') ? Number.parseFloat(localStorage.getItem('workflow-variable-inpsect-panel-height')!) : 320,
|
||||
variableInspectPanelHeight: storage.getNumber(STORAGE_KEYS.WORKFLOW.VARIABLE_INSPECT_PANEL_HEIGHT, 320),
|
||||
setVariableInspectPanelHeight: height => set(() => ({ variableInspectPanelHeight: height })),
|
||||
maximizeCanvas: localStorage.getItem('workflow-canvas-maximize') === 'true',
|
||||
maximizeCanvas: storage.getBoolean(STORAGE_KEYS.WORKFLOW.CANVAS_MAXIMIZE, false),
|
||||
setMaximizeCanvas: maximize => set(() => ({ maximizeCanvas: maximize })),
|
||||
})
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import type { StateCreator } from 'zustand'
|
||||
|
||||
export type PanelSliceShape = {
|
||||
panelWidth: number
|
||||
showFeaturesPanel: boolean
|
||||
setShowFeaturesPanel: (showFeaturesPanel: boolean) => void
|
||||
showWorkflowVersionHistoryPanel: boolean
|
||||
@@ -27,7 +26,6 @@ export type PanelSliceShape = {
|
||||
}
|
||||
|
||||
export const createPanelSlice: StateCreator<PanelSliceShape> = set => ({
|
||||
panelWidth: localStorage.getItem('workflow-node-panel-width') ? Number.parseFloat(localStorage.getItem('workflow-node-panel-width')!) : 420,
|
||||
showFeaturesPanel: false,
|
||||
setShowFeaturesPanel: showFeaturesPanel => set(() => ({ showFeaturesPanel })),
|
||||
showWorkflowVersionHistoryPanel: false,
|
||||
|
||||
@@ -5,6 +5,8 @@ import type {
|
||||
WorkflowRunningData,
|
||||
} from '@/app/components/workflow/types'
|
||||
import type { FileUploadConfigResponse } from '@/models/common'
|
||||
import { STORAGE_KEYS } from '@/config/storage-keys'
|
||||
import { storage } from '@/utils/storage'
|
||||
|
||||
type PreviewRunningData = WorkflowRunningData & {
|
||||
resultTabActive?: boolean
|
||||
@@ -63,10 +65,10 @@ export const createWorkflowSlice: StateCreator<WorkflowSliceShape> = set => ({
|
||||
setSelection: selection => set(() => ({ selection })),
|
||||
bundleNodeSize: null,
|
||||
setBundleNodeSize: bundleNodeSize => set(() => ({ bundleNodeSize })),
|
||||
controlMode: localStorage.getItem('workflow-operation-mode') === 'pointer' ? 'pointer' : 'hand',
|
||||
controlMode: storage.get<'pointer' | 'hand'>(STORAGE_KEYS.WORKFLOW.OPERATION_MODE) === 'pointer' ? 'pointer' : 'hand',
|
||||
setControlMode: (controlMode) => {
|
||||
set(() => ({ controlMode }))
|
||||
localStorage.setItem('workflow-operation-mode', controlMode)
|
||||
storage.set(STORAGE_KEYS.WORKFLOW.OPERATION_MODE, controlMode)
|
||||
},
|
||||
mousePosition: { pageX: 0, pageY: 0, elementX: 0, elementY: 0 },
|
||||
setMousePosition: mousePosition => set(() => ({ mousePosition })),
|
||||
|
||||
@@ -4,7 +4,9 @@ import {
|
||||
useCallback,
|
||||
useMemo,
|
||||
} from 'react'
|
||||
import { STORAGE_KEYS } from '@/config/storage-keys'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import { storage } from '@/utils/storage'
|
||||
import { useResizePanel } from '../nodes/_base/hooks/use-resize-panel'
|
||||
import { useStore } from '../store'
|
||||
import Panel from './panel'
|
||||
@@ -21,8 +23,8 @@ const VariableInspectPanel: FC = () => {
|
||||
return workflowCanvasHeight - 60
|
||||
}, [workflowCanvasHeight])
|
||||
|
||||
const handleResize = useCallback((width: number, height: number) => {
|
||||
localStorage.setItem('workflow-variable-inpsect-panel-height', `${height}`)
|
||||
const handleResize = useCallback((_width: number, height: number) => {
|
||||
storage.set(STORAGE_KEYS.WORKFLOW.VARIABLE_INSPECT_PANEL_HEIGHT, height)
|
||||
setVariableInspectPanelHeight(height)
|
||||
}, [setVariableInspectPanelHeight])
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { ModelParameterRule } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import { InputVarType } from '@/app/components/workflow/types'
|
||||
import { STORAGE_KEYS } from '@/config/storage-keys'
|
||||
import { PromptRole } from '@/models/debug'
|
||||
import { PipelineInputVarType } from '@/models/pipeline'
|
||||
import { AgentStrategy } from '@/types/app'
|
||||
@@ -179,7 +180,7 @@ export const CSRF_COOKIE_NAME = () => {
|
||||
return isSecure ? '__Host-csrf_token' : 'csrf_token'
|
||||
}
|
||||
export const CSRF_HEADER_NAME = 'X-CSRF-Token'
|
||||
export const ACCESS_TOKEN_LOCAL_STORAGE_NAME = 'access_token'
|
||||
export const ACCESS_TOKEN_LOCAL_STORAGE_NAME = STORAGE_KEYS.AUTH.ACCESS_TOKEN
|
||||
export const PASSPORT_LOCAL_STORAGE_NAME = (appCode: string) => `passport-${appCode}`
|
||||
export const PASSPORT_HEADER_NAME = 'X-App-Passport'
|
||||
|
||||
@@ -229,7 +230,7 @@ export const VAR_ITEM_TEMPLATE_IN_PIPELINE = {
|
||||
|
||||
export const appDefaultIconBackground = '#D5F5F6'
|
||||
|
||||
export const NEED_REFRESH_APP_LIST_KEY = 'needRefreshAppList'
|
||||
export const NEED_REFRESH_APP_LIST_KEY = STORAGE_KEYS.APP.NEED_REFRESH_LIST
|
||||
|
||||
export const DATASET_DEFAULT = {
|
||||
top_k: 4,
|
||||
|
||||
35
web/config/storage-keys.ts
Normal file
35
web/config/storage-keys.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
export const STORAGE_KEYS = {
|
||||
WORKFLOW: {
|
||||
NODE_PANEL_WIDTH: 'workflow-node-panel-width',
|
||||
PREVIEW_PANEL_WIDTH: 'debug-and-preview-panel-width',
|
||||
VARIABLE_INSPECT_PANEL_HEIGHT: 'workflow-variable-inspect-panel-height',
|
||||
CANVAS_MAXIMIZE: 'workflow-canvas-maximize',
|
||||
OPERATION_MODE: 'workflow-operation-mode',
|
||||
},
|
||||
APP: {
|
||||
SIDEBAR_COLLAPSE: 'webappSidebarCollapse',
|
||||
NEED_REFRESH_LIST: 'needRefreshAppList',
|
||||
DETAIL_COLLAPSE: 'app-detail-collapse-or-expand',
|
||||
},
|
||||
CONVERSATION: {
|
||||
ID_INFO: 'conversationIdInfo',
|
||||
},
|
||||
AUTH: {
|
||||
ACCESS_TOKEN: 'access_token',
|
||||
REFRESH_LOCK: 'is_other_tab_refreshing',
|
||||
LAST_REFRESH_TIME: 'last_refresh_time',
|
||||
},
|
||||
EDUCATION: {
|
||||
VERIFYING: 'educationVerifying',
|
||||
REVERIFY_PREV_EXPIRE_AT: 'education-reverify-prev-expire-at',
|
||||
REVERIFY_HAS_NOTICED: 'education-reverify-has-noticed',
|
||||
EXPIRED_HAS_NOTICED: 'education-expired-has-noticed',
|
||||
},
|
||||
CONFIG: {
|
||||
AUTO_GEN_MODEL: 'auto-gen-model',
|
||||
DEBUG_MODELS: 'app-debug-with-single-or-multiple-models',
|
||||
SETUP_STATUS: 'setup_status',
|
||||
},
|
||||
} as const
|
||||
|
||||
export type StorageKeys = typeof STORAGE_KEYS
|
||||
@@ -56,6 +56,9 @@
|
||||
"no-console": {
|
||||
"count": 16
|
||||
},
|
||||
"no-restricted-properties": {
|
||||
"count": 5
|
||||
},
|
||||
"react-hooks-extra/no-direct-set-state-in-use-effect": {
|
||||
"count": 4
|
||||
},
|
||||
@@ -74,6 +77,9 @@
|
||||
}
|
||||
},
|
||||
"app/(commonLayout)/app/(appDetailLayout)/[appId]/layout-main.tsx": {
|
||||
"no-restricted-globals": {
|
||||
"count": 1
|
||||
},
|
||||
"react-hooks-extra/no-direct-set-state-in-use-effect": {
|
||||
"count": 2
|
||||
},
|
||||
@@ -81,6 +87,11 @@
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/card-view.tsx": {
|
||||
"no-restricted-globals": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/time-range-picker/range-selector.tsx": {
|
||||
"react-hooks/preserve-manual-memoization": {
|
||||
"count": 1
|
||||
@@ -103,6 +114,9 @@
|
||||
}
|
||||
},
|
||||
"app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/layout-main.tsx": {
|
||||
"no-restricted-globals": {
|
||||
"count": 1
|
||||
},
|
||||
"ts/no-explicit-any": {
|
||||
"count": 1
|
||||
}
|
||||
@@ -117,12 +131,25 @@
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/(shareLayout)/webapp-reset-password/page.tsx": {
|
||||
"no-restricted-globals": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/(shareLayout)/webapp-signin/components/mail-and-code-auth.tsx": {
|
||||
"no-restricted-globals": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/(shareLayout)/webapp-signin/components/mail-and-password-auth.tsx": {
|
||||
"ts/no-explicit-any": {
|
||||
"count": 2
|
||||
}
|
||||
},
|
||||
"app/account/(commonLayout)/account-page/email-change-modal.tsx": {
|
||||
"no-restricted-globals": {
|
||||
"count": 1
|
||||
},
|
||||
"ts/no-explicit-any": {
|
||||
"count": 5
|
||||
}
|
||||
@@ -132,6 +159,11 @@
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/account/(commonLayout)/avatar.tsx": {
|
||||
"no-restricted-globals": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/account/(commonLayout)/delete-account/components/feed-back.tsx": {
|
||||
"react-hooks/preserve-manual-memoization": {
|
||||
"count": 1
|
||||
@@ -142,17 +174,33 @@
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/account/(commonLayout)/delete-account/index.tsx": {
|
||||
"no-restricted-globals": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/account/oauth/authorize/layout.tsx": {
|
||||
"ts/no-explicit-any": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/account/oauth/authorize/page.tsx": {
|
||||
"no-restricted-globals": {
|
||||
"count": 1
|
||||
},
|
||||
"ts/no-explicit-any": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/components/app-initializer.tsx": {
|
||||
"no-restricted-globals": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/components/app-sidebar/app-info.tsx": {
|
||||
"no-restricted-globals": {
|
||||
"count": 1
|
||||
},
|
||||
"ts/no-explicit-any": {
|
||||
"count": 1
|
||||
}
|
||||
@@ -353,6 +401,9 @@
|
||||
}
|
||||
},
|
||||
"app/components/app/configuration/config/automatic/get-automatic-res.tsx": {
|
||||
"no-restricted-globals": {
|
||||
"count": 6
|
||||
},
|
||||
"react-hooks-extra/no-direct-set-state-in-use-effect": {
|
||||
"count": 4
|
||||
},
|
||||
@@ -381,6 +432,9 @@
|
||||
}
|
||||
},
|
||||
"app/components/app/configuration/config/code-generator/get-code-generator-res.tsx": {
|
||||
"no-restricted-globals": {
|
||||
"count": 6
|
||||
},
|
||||
"react-hooks-extra/no-direct-set-state-in-use-effect": {
|
||||
"count": 4
|
||||
},
|
||||
@@ -485,6 +539,9 @@
|
||||
}
|
||||
},
|
||||
"app/components/app/configuration/debug/hooks.tsx": {
|
||||
"no-restricted-globals": {
|
||||
"count": 2
|
||||
},
|
||||
"react-hooks/refs": {
|
||||
"count": 7
|
||||
},
|
||||
@@ -541,12 +598,23 @@
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/components/app/create-app-dialog/app-list/index.tsx": {
|
||||
"no-restricted-globals": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/components/app/create-app-modal/index.spec.tsx": {
|
||||
"no-restricted-properties": {
|
||||
"count": 1
|
||||
},
|
||||
"ts/no-explicit-any": {
|
||||
"count": 7
|
||||
}
|
||||
},
|
||||
"app/components/app/create-app-modal/index.tsx": {
|
||||
"no-restricted-globals": {
|
||||
"count": 1
|
||||
},
|
||||
"react-hooks-extra/no-direct-set-state-in-use-effect": {
|
||||
"count": 1
|
||||
},
|
||||
@@ -555,6 +623,9 @@
|
||||
}
|
||||
},
|
||||
"app/components/app/create-from-dsl-modal/index.tsx": {
|
||||
"no-restricted-globals": {
|
||||
"count": 2
|
||||
},
|
||||
"react-hooks-extra/no-direct-set-state-in-use-effect": {
|
||||
"count": 2
|
||||
}
|
||||
@@ -620,11 +691,17 @@
|
||||
}
|
||||
},
|
||||
"app/components/app/switch-app-modal/index.spec.tsx": {
|
||||
"no-restricted-globals": {
|
||||
"count": 1
|
||||
},
|
||||
"ts/no-explicit-any": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/components/app/switch-app-modal/index.tsx": {
|
||||
"no-restricted-globals": {
|
||||
"count": 1
|
||||
},
|
||||
"react-hooks-extra/no-direct-set-state-in-use-effect": {
|
||||
"count": 1
|
||||
}
|
||||
@@ -663,6 +740,9 @@
|
||||
}
|
||||
},
|
||||
"app/components/apps/app-card.tsx": {
|
||||
"no-restricted-globals": {
|
||||
"count": 1
|
||||
},
|
||||
"react-hooks-extra/no-direct-set-state-in-use-effect": {
|
||||
"count": 1
|
||||
},
|
||||
@@ -674,11 +754,17 @@
|
||||
}
|
||||
},
|
||||
"app/components/apps/list.spec.tsx": {
|
||||
"no-restricted-globals": {
|
||||
"count": 3
|
||||
},
|
||||
"ts/no-explicit-any": {
|
||||
"count": 9
|
||||
}
|
||||
},
|
||||
"app/components/apps/list.tsx": {
|
||||
"no-restricted-globals": {
|
||||
"count": 2
|
||||
},
|
||||
"unused-imports/no-unused-vars": {
|
||||
"count": 1
|
||||
}
|
||||
@@ -806,7 +892,15 @@
|
||||
"count": 2
|
||||
}
|
||||
},
|
||||
"app/components/base/chat/chat-with-history/hooks.spec.tsx": {
|
||||
"no-restricted-globals": {
|
||||
"count": 4
|
||||
}
|
||||
},
|
||||
"app/components/base/chat/chat-with-history/hooks.tsx": {
|
||||
"no-restricted-globals": {
|
||||
"count": 2
|
||||
},
|
||||
"react-hooks-extra/no-direct-set-state-in-use-effect": {
|
||||
"count": 4
|
||||
},
|
||||
@@ -907,6 +1001,11 @@
|
||||
"count": 7
|
||||
}
|
||||
},
|
||||
"app/components/base/chat/embedded-chatbot/hooks.spec.tsx": {
|
||||
"no-restricted-globals": {
|
||||
"count": 3
|
||||
}
|
||||
},
|
||||
"app/components/base/chat/embedded-chatbot/hooks.tsx": {
|
||||
"react-hooks-extra/no-direct-set-state-in-use-effect": {
|
||||
"count": 6
|
||||
@@ -1671,7 +1770,15 @@
|
||||
"count": 4
|
||||
}
|
||||
},
|
||||
"app/components/billing/plan/index.spec.tsx": {
|
||||
"no-restricted-globals": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/components/billing/plan/index.tsx": {
|
||||
"no-restricted-globals": {
|
||||
"count": 1
|
||||
},
|
||||
"ts/no-explicit-any": {
|
||||
"count": 2
|
||||
}
|
||||
@@ -1701,6 +1808,11 @@
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/components/browser-initializer.tsx": {
|
||||
"no-restricted-properties": {
|
||||
"count": 2
|
||||
}
|
||||
},
|
||||
"app/components/custom/custom-web-app-brand/index.spec.tsx": {
|
||||
"ts/no-explicit-any": {
|
||||
"count": 7
|
||||
@@ -2112,6 +2224,9 @@
|
||||
}
|
||||
},
|
||||
"app/components/datasets/metadata/hooks/use-edit-dataset-metadata.ts": {
|
||||
"no-restricted-globals": {
|
||||
"count": 2
|
||||
},
|
||||
"react-hooks-extra/no-direct-set-state-in-use-effect": {
|
||||
"count": 1
|
||||
}
|
||||
@@ -2136,6 +2251,11 @@
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/components/datasets/metadata/metadata-document/info-group.tsx": {
|
||||
"no-restricted-globals": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/components/datasets/settings/form/index.tsx": {
|
||||
"react-hooks-extra/no-direct-set-state-in-use-effect": {
|
||||
"count": 2
|
||||
@@ -2244,6 +2364,11 @@
|
||||
"count": 9
|
||||
}
|
||||
},
|
||||
"app/components/header/account-dropdown/index.tsx": {
|
||||
"no-restricted-globals": {
|
||||
"count": 4
|
||||
}
|
||||
},
|
||||
"app/components/header/account-setting/data-source-page-new/card.tsx": {
|
||||
"react-hooks/immutability": {
|
||||
"count": 1
|
||||
@@ -2459,6 +2584,11 @@
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/components/header/maintenance-notice.tsx": {
|
||||
"no-restricted-globals": {
|
||||
"count": 2
|
||||
}
|
||||
},
|
||||
"app/components/header/nav/nav-selector/index.tsx": {
|
||||
"react-hooks/use-memo": {
|
||||
"count": 1
|
||||
@@ -2999,6 +3129,11 @@
|
||||
"count": 2
|
||||
}
|
||||
},
|
||||
"app/components/signin/countdown.tsx": {
|
||||
"no-restricted-globals": {
|
||||
"count": 4
|
||||
}
|
||||
},
|
||||
"app/components/tools/edit-custom-collection-modal/get-schema.tsx": {
|
||||
"ts/no-explicit-any": {
|
||||
"count": 1
|
||||
@@ -3198,6 +3333,9 @@
|
||||
}
|
||||
},
|
||||
"app/components/workflow/block-selector/featured-tools.tsx": {
|
||||
"no-restricted-properties": {
|
||||
"count": 3
|
||||
},
|
||||
"react-hooks-extra/no-direct-set-state-in-use-effect": {
|
||||
"count": 2
|
||||
},
|
||||
@@ -3206,6 +3344,9 @@
|
||||
}
|
||||
},
|
||||
"app/components/workflow/block-selector/featured-triggers.tsx": {
|
||||
"no-restricted-properties": {
|
||||
"count": 3
|
||||
},
|
||||
"react-hooks-extra/no-direct-set-state-in-use-effect": {
|
||||
"count": 2
|
||||
},
|
||||
@@ -3224,6 +3365,9 @@
|
||||
}
|
||||
},
|
||||
"app/components/workflow/block-selector/rag-tool-recommendations/index.tsx": {
|
||||
"no-restricted-properties": {
|
||||
"count": 3
|
||||
},
|
||||
"react-hooks-extra/no-direct-set-state-in-use-effect": {
|
||||
"count": 1
|
||||
}
|
||||
@@ -3540,6 +3684,11 @@
|
||||
"count": 2
|
||||
}
|
||||
},
|
||||
"app/components/workflow/nodes/_base/components/workflow-panel/index.spec.tsx": {
|
||||
"no-restricted-globals": {
|
||||
"count": 18
|
||||
}
|
||||
},
|
||||
"app/components/workflow/nodes/_base/components/workflow-panel/index.tsx": {
|
||||
"react-hooks-extra/no-direct-set-state-in-use-effect": {
|
||||
"count": 3
|
||||
@@ -3938,6 +4087,9 @@
|
||||
}
|
||||
},
|
||||
"app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-generator/index.tsx": {
|
||||
"no-restricted-globals": {
|
||||
"count": 6
|
||||
},
|
||||
"react-hooks-extra/no-direct-set-state-in-use-effect": {
|
||||
"count": 2
|
||||
}
|
||||
@@ -4359,6 +4511,11 @@
|
||||
"count": 7
|
||||
}
|
||||
},
|
||||
"app/components/workflow/panel/debug-and-preview/index.spec.tsx": {
|
||||
"no-restricted-globals": {
|
||||
"count": 15
|
||||
}
|
||||
},
|
||||
"app/components/workflow/panel/env-panel/variable-modal.tsx": {
|
||||
"react-hooks-extra/no-direct-set-state-in-use-effect": {
|
||||
"count": 4
|
||||
@@ -4627,7 +4784,15 @@
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/education-apply/education-apply-page.tsx": {
|
||||
"no-restricted-globals": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/education-apply/hooks.ts": {
|
||||
"no-restricted-globals": {
|
||||
"count": 2
|
||||
},
|
||||
"react-hooks-extra/no-direct-set-state-in-use-effect": {
|
||||
"count": 5
|
||||
}
|
||||
@@ -4637,6 +4802,11 @@
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/education-apply/user-info.tsx": {
|
||||
"no-restricted-globals": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/education-apply/verify-state-modal.tsx": {
|
||||
"react-hooks-extra/no-direct-set-state-in-use-effect": {
|
||||
"count": 1
|
||||
@@ -4653,15 +4823,33 @@
|
||||
}
|
||||
},
|
||||
"app/install/installForm.spec.tsx": {
|
||||
"no-restricted-globals": {
|
||||
"count": 1
|
||||
},
|
||||
"ts/no-explicit-any": {
|
||||
"count": 7
|
||||
}
|
||||
},
|
||||
"app/install/installForm.tsx": {
|
||||
"no-restricted-globals": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/reset-password/layout.tsx": {
|
||||
"ts/no-explicit-any": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/reset-password/page.tsx": {
|
||||
"no-restricted-globals": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/signin/components/mail-and-code-auth.tsx": {
|
||||
"no-restricted-globals": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/signin/components/mail-and-password-auth.tsx": {
|
||||
"ts/no-explicit-any": {
|
||||
"count": 1
|
||||
@@ -4682,6 +4870,11 @@
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/signin/utils/post-login-redirect.ts": {
|
||||
"no-restricted-globals": {
|
||||
"count": 3
|
||||
}
|
||||
},
|
||||
"app/signup/layout.tsx": {
|
||||
"ts/no-explicit-any": {
|
||||
"count": 1
|
||||
@@ -4698,21 +4891,36 @@
|
||||
}
|
||||
},
|
||||
"context/hooks/use-trigger-events-limit-modal.ts": {
|
||||
"no-restricted-globals": {
|
||||
"count": 2
|
||||
},
|
||||
"react-hooks-extra/no-direct-set-state-in-use-effect": {
|
||||
"count": 3
|
||||
}
|
||||
},
|
||||
"context/modal-context.test.tsx": {
|
||||
"no-restricted-globals": {
|
||||
"count": 4
|
||||
},
|
||||
"no-restricted-properties": {
|
||||
"count": 1
|
||||
},
|
||||
"ts/no-explicit-any": {
|
||||
"count": 3
|
||||
}
|
||||
},
|
||||
"context/modal-context.tsx": {
|
||||
"no-restricted-globals": {
|
||||
"count": 2
|
||||
},
|
||||
"ts/no-explicit-any": {
|
||||
"count": 5
|
||||
}
|
||||
},
|
||||
"context/provider-context.tsx": {
|
||||
"no-restricted-globals": {
|
||||
"count": 2
|
||||
},
|
||||
"ts/no-explicit-any": {
|
||||
"count": 1
|
||||
}
|
||||
@@ -4730,6 +4938,11 @@
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"hooks/use-import-dsl.ts": {
|
||||
"no-restricted-globals": {
|
||||
"count": 2
|
||||
}
|
||||
},
|
||||
"hooks/use-metadata.ts": {
|
||||
"ts/no-explicit-any": {
|
||||
"count": 1
|
||||
@@ -4914,6 +5127,11 @@
|
||||
"count": 2
|
||||
}
|
||||
},
|
||||
"service/refresh-token.ts": {
|
||||
"no-restricted-properties": {
|
||||
"count": 7
|
||||
}
|
||||
},
|
||||
"service/share.ts": {
|
||||
"ts/no-explicit-any": {
|
||||
"count": 4
|
||||
@@ -4978,6 +5196,11 @@
|
||||
"count": 2
|
||||
}
|
||||
},
|
||||
"service/webapp-auth.ts": {
|
||||
"no-restricted-globals": {
|
||||
"count": 6
|
||||
}
|
||||
},
|
||||
"service/workflow-payload.ts": {
|
||||
"ts/no-explicit-any": {
|
||||
"count": 10
|
||||
@@ -5089,6 +5312,16 @@
|
||||
"count": 4
|
||||
}
|
||||
},
|
||||
"utils/setup-status.spec.ts": {
|
||||
"no-restricted-globals": {
|
||||
"count": 11
|
||||
}
|
||||
},
|
||||
"utils/setup-status.ts": {
|
||||
"no-restricted-globals": {
|
||||
"count": 3
|
||||
}
|
||||
},
|
||||
"utils/tool-call.spec.ts": {
|
||||
"ts/no-explicit-any": {
|
||||
"count": 1
|
||||
|
||||
@@ -42,6 +42,40 @@ export default antfu(
|
||||
{
|
||||
rules: {
|
||||
'node/prefer-global/process': 'off',
|
||||
'no-restricted-globals': [
|
||||
'error',
|
||||
{
|
||||
name: 'localStorage',
|
||||
message: 'Use @/utils/storage instead. Direct localStorage access causes SSR issues.',
|
||||
},
|
||||
{
|
||||
name: 'sessionStorage',
|
||||
message: 'Use @/utils/storage instead. Direct sessionStorage access causes SSR issues.',
|
||||
},
|
||||
],
|
||||
'no-restricted-properties': [
|
||||
'error',
|
||||
{
|
||||
object: 'window',
|
||||
property: 'localStorage',
|
||||
message: 'Use @/utils/storage instead.',
|
||||
},
|
||||
{
|
||||
object: 'window',
|
||||
property: 'sessionStorage',
|
||||
message: 'Use @/utils/storage instead.',
|
||||
},
|
||||
{
|
||||
object: 'globalThis',
|
||||
property: 'localStorage',
|
||||
message: 'Use @/utils/storage instead.',
|
||||
},
|
||||
{
|
||||
object: 'globalThis',
|
||||
property: 'sessionStorage',
|
||||
message: 'Use @/utils/storage instead.',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
107
web/utils/storage.ts
Normal file
107
web/utils/storage.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
/* eslint-disable no-restricted-globals */
|
||||
import { isClient } from './client'
|
||||
|
||||
type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue }
|
||||
|
||||
let _isAvailable: boolean | null = null
|
||||
|
||||
function isLocalStorageAvailable(): boolean {
|
||||
if (_isAvailable !== null)
|
||||
return _isAvailable
|
||||
|
||||
if (!isClient) {
|
||||
_isAvailable = false
|
||||
return false
|
||||
}
|
||||
|
||||
try {
|
||||
const testKey = '__storage_test__'
|
||||
localStorage.setItem(testKey, 'test')
|
||||
localStorage.removeItem(testKey)
|
||||
_isAvailable = true
|
||||
return true
|
||||
}
|
||||
catch {
|
||||
_isAvailable = false
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
function get<T extends JsonValue>(key: string, defaultValue?: T): T | null {
|
||||
if (!isLocalStorageAvailable())
|
||||
return defaultValue ?? null
|
||||
|
||||
try {
|
||||
const item = localStorage.getItem(key)
|
||||
if (item === null)
|
||||
return defaultValue ?? null
|
||||
|
||||
try {
|
||||
return JSON.parse(item) as T
|
||||
}
|
||||
catch {
|
||||
return item as T
|
||||
}
|
||||
}
|
||||
catch {
|
||||
return defaultValue ?? null
|
||||
}
|
||||
}
|
||||
|
||||
function set<T extends JsonValue>(key: string, value: T): void {
|
||||
if (!isLocalStorageAvailable())
|
||||
return
|
||||
|
||||
try {
|
||||
const stringValue = typeof value === 'string' ? value : JSON.stringify(value)
|
||||
localStorage.setItem(key, stringValue)
|
||||
}
|
||||
catch {
|
||||
// Silent fail - localStorage may be full or disabled
|
||||
}
|
||||
}
|
||||
|
||||
function remove(key: string): void {
|
||||
if (!isLocalStorageAvailable())
|
||||
return
|
||||
|
||||
try {
|
||||
localStorage.removeItem(key)
|
||||
}
|
||||
catch {
|
||||
// Silent fail
|
||||
}
|
||||
}
|
||||
|
||||
function getNumber(key: string): number | null
|
||||
function getNumber(key: string, defaultValue: number): number
|
||||
function getNumber(key: string, defaultValue?: number): number | null {
|
||||
const value = get<string | number>(key)
|
||||
if (value === null)
|
||||
return defaultValue ?? null
|
||||
|
||||
const parsed = typeof value === 'number' ? value : Number.parseFloat(value as string)
|
||||
return Number.isNaN(parsed) ? (defaultValue ?? null) : parsed
|
||||
}
|
||||
|
||||
function getBoolean(key: string): boolean | null
|
||||
function getBoolean(key: string, defaultValue: boolean): boolean
|
||||
function getBoolean(key: string, defaultValue?: boolean): boolean | null {
|
||||
const value = get<string | boolean>(key)
|
||||
if (value === null)
|
||||
return defaultValue ?? null
|
||||
|
||||
if (typeof value === 'boolean')
|
||||
return value
|
||||
|
||||
return value === 'true'
|
||||
}
|
||||
|
||||
export const storage = {
|
||||
get,
|
||||
set,
|
||||
remove,
|
||||
getNumber,
|
||||
getBoolean,
|
||||
isAvailable: isLocalStorageAvailable,
|
||||
}
|
||||
Reference in New Issue
Block a user