dify/web/app/components/workflow/skill/context.tsx
yyh fe17cbc1a8
feat(skill-editor): implement file tree, tab management, and dirty state tracking
Implement MVP features for skill editor based on design doc:
- Add Zustand store with Tab, FileTree, and Dirty slices
- Rewrite file tree using react-arborist for virtual scrolling
- Implement Tab↔FileTree sync with auto-reveal on tab activation
- Add upload functionality (new folder, upload file)
- Implement Monaco editor with dirty state tracking and Ctrl+S save
- Add i18n translations (en-US and zh-Hans)
2026-01-15 13:53:19 +08:00

53 lines
1.4 KiB
TypeScript

'use client'
import type { SkillEditorStore } from './store'
import {
useEffect,
useRef,
} from 'react'
import { useStore as useAppStore } from '@/app/components/app/store'
import {
createSkillEditorStore,
SkillEditorContext,
} from './store'
/**
* SkillEditorProvider
*
* Provides the SkillEditor store to all child components.
* The store is created once per mount and persists across view switches.
* When appId changes, the store is reset.
*/
export type SkillEditorProviderProps = {
children: React.ReactNode
}
export const SkillEditorProvider = ({ children }: SkillEditorProviderProps) => {
const storeRef = useRef<SkillEditorStore | undefined>(undefined)
const appDetail = useAppStore(s => s.appDetail)
const appId = appDetail?.id
const prevAppIdRef = useRef<string | undefined>(undefined)
// Create store on first render (pattern recommended by React)
if (storeRef.current === null || storeRef.current === undefined)
storeRef.current = createSkillEditorStore()
// Reset store when appId changes
useEffect(() => {
if (prevAppIdRef.current !== undefined && prevAppIdRef.current !== appId) {
// appId changed, reset the store
storeRef.current?.getState().reset()
}
prevAppIdRef.current = appId
}, [appId])
return (
<SkillEditorContext.Provider value={storeRef.current}>
{children}
</SkillEditorContext.Provider>
)
}
// Re-export for convenience
export { SkillEditorContext } from './store'