@tutti-os/workspace-file-reference 0.0.32 → 0.0.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-BEBHG64Q.js → chunk-RJMPRZTF.js} +4 -3
- package/dist/chunk-RJMPRZTF.js.map +1 -0
- package/dist/react/index.d.ts +1 -1
- package/dist/react/index.js +1 -1
- package/dist/ui/index.js +105 -36
- package/dist/ui/index.js.map +1 -1
- package/package.json +5 -5
- package/dist/chunk-BEBHG64Q.js.map +0 -1
|
@@ -1253,7 +1253,7 @@ function createReferenceSourcePickerController(input) {
|
|
|
1253
1253
|
selection: []
|
|
1254
1254
|
});
|
|
1255
1255
|
},
|
|
1256
|
-
setActiveSource(sourceId) {
|
|
1256
|
+
setActiveSource(sourceId, scopeNodeId) {
|
|
1257
1257
|
if (!snapshot.tabs.some((tab) => tab.sourceId === sourceId)) {
|
|
1258
1258
|
return;
|
|
1259
1259
|
}
|
|
@@ -1261,6 +1261,7 @@ function createReferenceSourcePickerController(input) {
|
|
|
1261
1261
|
const carriedQuery = prevTab?.searchQuery ?? "";
|
|
1262
1262
|
const carriedFilters = prevTab?.searchFilters ?? [];
|
|
1263
1263
|
const trimmed = carriedQuery.trim();
|
|
1264
|
+
const nextScopeNodeId = scopeNodeId !== void 0 ? scopeNodeId : snapshot.bySource[sourceId]?.searchScopeNodeId ?? null;
|
|
1264
1265
|
cancelSearch();
|
|
1265
1266
|
setSnapshot({ activeSourceId: sourceId });
|
|
1266
1267
|
if (trimmed === "" && carriedFilters.length === 0) {
|
|
@@ -1271,6 +1272,7 @@ function createReferenceSourcePickerController(input) {
|
|
|
1271
1272
|
mode: "browse",
|
|
1272
1273
|
searchQuery: "",
|
|
1273
1274
|
searchFilters: [],
|
|
1275
|
+
searchScopeNodeId: nextScopeNodeId,
|
|
1274
1276
|
searchEntries: [],
|
|
1275
1277
|
searchHasMore: false,
|
|
1276
1278
|
isSearchLoading: false,
|
|
@@ -1281,7 +1283,6 @@ function createReferenceSourcePickerController(input) {
|
|
|
1281
1283
|
ensureRootLoaded(sourceId);
|
|
1282
1284
|
return;
|
|
1283
1285
|
}
|
|
1284
|
-
const nextScopeNodeId = snapshot.bySource[sourceId]?.searchScopeNodeId ?? null;
|
|
1285
1286
|
updateTab(sourceId, (tab) => ({
|
|
1286
1287
|
...tab,
|
|
1287
1288
|
searchQuery: carriedQuery,
|
|
@@ -1610,4 +1611,4 @@ export {
|
|
|
1610
1611
|
ROOT_CHILDREN_KEY,
|
|
1611
1612
|
createReferenceSourcePickerController
|
|
1612
1613
|
};
|
|
1613
|
-
//# sourceMappingURL=chunk-
|
|
1614
|
+
//# sourceMappingURL=chunk-RJMPRZTF.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/react/internal/reference/WorkspaceFileReferencePickerState.ts","../src/react/internal/reference/useWorkspaceFileReferencePickerView.ts","../src/react/internal/reference/WorkspaceFileReferencePickerController.ts","../src/react/internal/reference/referenceSourcePickerController.ts"],"sourcesContent":["import type {\n WorkspaceFileReference,\n WorkspaceFileReferencePrefetchReason,\n WorkspaceFileReferencePrefetchState,\n WorkspaceFileReferenceTreeSnapshot\n} from \"../../../contracts/index.ts\";\n\nexport const workspaceFileReferenceDefaultExpandedDepth = 4;\n\nexport interface WorkspaceFileReferenceDirectoryState {\n displayPath: string;\n entries: WorkspaceFileReference[];\n loaded: boolean;\n loading: boolean;\n prefetchReason?: WorkspaceFileReferencePrefetchReason | null;\n prefetchState?: WorkspaceFileReferencePrefetchState | null;\n}\n\nexport function normalizeDirectoryPath(path: string): string {\n if (!path || path === \"/\") {\n return \"/\";\n }\n return path.replace(/\\/+$/, \"\") || \"/\";\n}\n\nexport function collectVisibleTreeEntries(\n entries: readonly WorkspaceFileReference[],\n directoryStateByPath: Record<string, WorkspaceFileReferenceDirectoryState>,\n expandedFolderPaths: Record<string, boolean>\n): WorkspaceFileReference[] {\n const collected: WorkspaceFileReference[] = [];\n\n for (const entry of entries) {\n collected.push(entry);\n if (entry.kind !== \"folder\") {\n continue;\n }\n const folderKey = normalizeDirectoryPath(entry.path);\n if (!expandedFolderPaths[folderKey]) {\n continue;\n }\n const childEntries = directoryStateByPath[folderKey]?.entries ?? [];\n if (childEntries.length === 0) {\n continue;\n }\n collected.push(\n ...collectVisibleTreeEntries(\n childEntries,\n directoryStateByPath,\n expandedFolderPaths\n )\n );\n }\n\n return collected;\n}\n\nexport function mergeExpandedFolderPaths(\n current: Record<string, boolean>,\n prefetched: Record<string, boolean>\n): Record<string, boolean> {\n return {\n ...prefetched,\n ...current\n };\n}\n\nexport function mergePrefetchedDirectoryState(\n current: Record<string, WorkspaceFileReferenceDirectoryState>,\n prefetched: Record<string, WorkspaceFileReferenceDirectoryState>\n): Record<string, WorkspaceFileReferenceDirectoryState> {\n return {\n ...current,\n ...prefetched\n };\n}\n\nexport function createWorkspaceFileReferenceDirectoryStateFromSnapshot(\n snapshot: WorkspaceFileReferenceTreeSnapshot\n): Record<string, WorkspaceFileReferenceDirectoryState> {\n const stateByPath: Record<string, WorkspaceFileReferenceDirectoryState> = {};\n addReferenceDirectoryStateFromSnapshot(stateByPath, snapshot.directory);\n return stateByPath;\n}\n\nfunction addReferenceDirectoryStateFromSnapshot(\n stateByPath: Record<string, WorkspaceFileReferenceDirectoryState>,\n directory: WorkspaceFileReferenceTreeSnapshot[\"directory\"]\n) {\n const normalizedPath = normalizeDirectoryPath(directory.directoryPath);\n stateByPath[normalizedPath] = {\n displayPath: directory.directoryPath,\n entries: directory.entries.map((entry) => ({\n displayName: entry.displayName,\n kind: entry.kind,\n path: entry.path\n })),\n loaded: true,\n loading: false,\n prefetchReason: directory.prefetchReason,\n prefetchState: directory.prefetchState\n };\n\n for (const entry of directory.entries) {\n if (entry.kind !== \"folder\") {\n continue;\n }\n const folderKey = normalizeDirectoryPath(entry.path);\n if (entry.prefetchedDirectory) {\n addReferenceDirectoryStateFromSnapshot(\n stateByPath,\n entry.prefetchedDirectory\n );\n continue;\n }\n if (entry.prefetchState) {\n stateByPath[folderKey] = {\n displayPath: entry.path,\n entries: [],\n loaded: false,\n loading: false,\n prefetchReason: entry.prefetchReason,\n prefetchState: entry.prefetchState\n };\n }\n }\n}\n\nexport async function prefetchReferenceTree(input: {\n listDirectory: (path: string) => Promise<{\n displayPath: string;\n entries: WorkspaceFileReference[];\n normalizedPath: string;\n } | null>;\n maxDepth: number;\n path: string;\n depth?: number;\n}): Promise<{\n directoryStateByPath: Record<string, WorkspaceFileReferenceDirectoryState>;\n expandedFolderPaths: Record<string, boolean>;\n}> {\n const { depth = 1, listDirectory, maxDepth, path } = input;\n const listing = await listDirectory(path);\n if (!listing) {\n return {\n directoryStateByPath: {},\n expandedFolderPaths: {}\n };\n }\n\n const directoryStateByPath: Record<\n string,\n WorkspaceFileReferenceDirectoryState\n > = {\n [listing.normalizedPath]: {\n displayPath: listing.displayPath,\n entries: listing.entries,\n loaded: true,\n loading: false\n }\n };\n const expandedFolderPaths: Record<string, boolean> = {\n [listing.normalizedPath]: true\n };\n\n if (depth >= maxDepth) {\n return {\n directoryStateByPath,\n expandedFolderPaths\n };\n }\n\n for (const entry of listing.entries) {\n if (entry.kind !== \"folder\") {\n continue;\n }\n try {\n const childTree = await prefetchReferenceTree({\n depth: depth + 1,\n listDirectory,\n maxDepth,\n path: entry.path\n });\n Object.assign(directoryStateByPath, childTree.directoryStateByPath);\n Object.assign(expandedFolderPaths, childTree.expandedFolderPaths);\n } catch {\n // Skip unreadable branches so one protected folder does not blank the whole tree.\n }\n }\n\n return {\n directoryStateByPath,\n expandedFolderPaths\n };\n}\n","import {\n useCallback,\n useEffect,\n useEffectEvent,\n useMemo,\n useState\n} from \"react\";\nimport { useSnapshot } from \"valtio\";\nimport type {\n WorkspaceFileReferenceAdapter,\n WorkspaceFileReference\n} from \"../../../contracts/index.ts\";\nimport { uniqueWorkspaceFileReferences } from \"../../../core/index.ts\";\nimport {\n collectVisibleTreeEntries,\n normalizeDirectoryPath\n} from \"./WorkspaceFileReferencePickerState.ts\";\nimport {\n createWorkspaceFileReferencePickerController,\n type WorkspaceFileReferencePreviewState\n} from \"./WorkspaceFileReferencePickerController.ts\";\n\nexport type { WorkspaceFileReferencePreviewState };\n\nexport interface UseWorkspaceFileReferencePickerViewInput {\n fileAdapter?: WorkspaceFileReferenceAdapter;\n initialPath?: string | null;\n onClose: () => void;\n onConfirm: (refs: WorkspaceFileReference[]) => void;\n open: boolean;\n workspaceId: string;\n}\n\nexport function useWorkspaceFileReferencePickerView({\n fileAdapter,\n initialPath,\n onClose,\n onConfirm,\n open,\n workspaceId\n}: UseWorkspaceFileReferencePickerViewInput) {\n const readPickerSnapshot = useSnapshot as <T extends object>(store: T) => T;\n const trimmedInitialPath = initialPath?.trim() ?? \"\";\n const initialDirectoryPath = trimmedInitialPath\n ? normalizeDirectoryPath(trimmedInitialPath)\n : null;\n const [focusedPath, setFocusedPath] = useState<string | null>(null);\n const [selectedRefs, setSelectedRefs] = useState<WorkspaceFileReference[]>(\n []\n );\n\n const pickerController = useMemo(\n () =>\n createWorkspaceFileReferencePickerController({\n fileAdapter,\n workspaceId\n }),\n [fileAdapter, workspaceId]\n );\n const pickerSnapshot = readPickerSnapshot(pickerController.store);\n const {\n browseRootPath,\n directoryStateByPath,\n expandedFolderPaths,\n isBrowseLoading,\n isSearchLoading,\n mode,\n previewState,\n searchEntries,\n searchQuery\n } = pickerSnapshot;\n const isLoading = mode === \"search\" ? isSearchLoading : isBrowseLoading;\n\n const finalizeRequestedReferences = useEffectEvent(\n (refs: WorkspaceFileReference[]) => {\n onConfirm(uniqueWorkspaceFileReferences(refs));\n onClose();\n }\n );\n\n const setSearchQuery = useCallback(\n (query: string) => {\n pickerController.setSearchQuery(query);\n },\n [pickerController]\n );\n\n useEffect(() => {\n if (!open) {\n return;\n }\n\n pickerController.reset();\n pickerController.open();\n setFocusedPath(null);\n setSelectedRefs([]);\n return () => {\n pickerController.close();\n };\n }, [initialDirectoryPath, open, pickerController]);\n\n useEffect(() => {\n if (!open || mode !== \"browse\") {\n return;\n }\n pickerController.loadBrowseRoot();\n }, [mode, open, pickerController]);\n\n useEffect(() => {\n if (!open || !fileAdapter || mode !== \"browse\") {\n return;\n }\n if (\n fileAdapter.listDirectory ||\n fileAdapter.loadReferenceTree ||\n !fileAdapter.requestReferences\n ) {\n return;\n }\n let canceled = false;\n void fileAdapter.requestReferences({ workspaceId }).then((refs) => {\n if (!canceled) {\n finalizeRequestedReferences(refs);\n }\n });\n return () => {\n canceled = true;\n };\n }, [fileAdapter, finalizeRequestedReferences, mode, open, workspaceId]);\n\n useEffect(() => {\n if (\n !open ||\n mode !== \"browse\" ||\n pickerSnapshot.initialPathRevealed ||\n !initialDirectoryPath ||\n !browseRootPath\n ) {\n return;\n }\n\n let canceled = false;\n const reveal = async () => {\n const focusedInitialPath =\n await pickerController.revealInitialPath(initialDirectoryPath);\n if (!canceled && focusedInitialPath) {\n setFocusedPath(focusedInitialPath);\n }\n };\n\n void reveal();\n return () => {\n canceled = true;\n };\n }, [\n browseRootPath,\n initialDirectoryPath,\n mode,\n open,\n pickerController,\n pickerSnapshot.initialPathRevealed\n ]);\n\n const browseRootEntries = useMemo(\n () =>\n browseRootPath\n ? (directoryStateByPath[normalizeDirectoryPath(browseRootPath)]\n ?.entries ?? [])\n : [],\n [browseRootPath, directoryStateByPath]\n );\n\n const visibleEntries = useMemo(\n () =>\n mode === \"search\"\n ? searchEntries\n : collectVisibleTreeEntries(\n browseRootEntries,\n directoryStateByPath,\n expandedFolderPaths\n ),\n [\n browseRootEntries,\n directoryStateByPath,\n expandedFolderPaths,\n mode,\n searchEntries\n ]\n );\n\n useEffect(() => {\n if (!open) {\n return;\n }\n setFocusedPath((current) => {\n if (current && visibleEntries.some((entry) => entry.path === current)) {\n return current;\n }\n return visibleEntries[0]?.path ?? null;\n });\n }, [open, visibleEntries]);\n\n const focusedEntry =\n visibleEntries.find((entry) => entry.path === focusedPath) ??\n selectedRefs.find((entry) => entry.path === focusedPath) ??\n null;\n\n useEffect(() => {\n pickerController.setPreviewReference(open ? focusedEntry : null);\n }, [focusedEntry, open, pickerController]);\n\n const toggleRef = (ref: WorkspaceFileReference) => {\n setSelectedRefs((current) => {\n const existing = current.some((item) => item.path === ref.path);\n return existing\n ? current.filter((item) => item.path !== ref.path)\n : uniqueWorkspaceFileReferences([...current, ref]);\n });\n };\n\n const toggleFolder = (entry: WorkspaceFileReference) => {\n pickerController.toggleFolder(entry);\n };\n\n return {\n browseRootEntries,\n directoryStateByPath,\n expandedFolderPaths,\n focusedEntry,\n focusedPath,\n isLoading,\n mode,\n previewState,\n searchQuery,\n selectedRefs,\n visibleEntries,\n setFocusedPath,\n setSearchQuery,\n toggleFolder,\n toggleRef\n };\n}\n","import { proxy } from \"valtio/vanilla\";\nimport {\n createWorkspaceFilePreviewLoadedState,\n resolveWorkspaceFilePreviewReadiness\n} from \"@tutti-os/workspace-file-preview\";\nimport type {\n WorkspaceFilePreviewActivationTarget,\n WorkspaceFilePreviewReadonlyReason\n} from \"@tutti-os/workspace-file-preview\";\nimport type {\n WorkspaceFileReference,\n WorkspaceFileReferenceAdapter,\n WorkspaceFileReferencePreview\n} from \"../../../contracts/index.ts\";\nimport { uniqueWorkspaceFileReferences } from \"../../../core/index.ts\";\nimport {\n createWorkspaceFileReferenceDirectoryStateFromSnapshot,\n normalizeDirectoryPath,\n type WorkspaceFileReferenceDirectoryState\n} from \"./WorkspaceFileReferencePickerState.ts\";\n\nexport type WorkspaceFileReferencePickerMode = \"browse\" | \"search\";\n\nexport type WorkspaceFileReferencePreviewState =\n | { status: \"directory\"; reference: WorkspaceFileReference }\n | { status: \"empty\" }\n | { status: \"error\"; reference: WorkspaceFileReference }\n | { status: \"html\"; content: string; reference: WorkspaceFileReference }\n | { status: \"image\"; objectUrl: string; reference: WorkspaceFileReference }\n | { status: \"loading\"; reference: WorkspaceFileReference }\n | { status: \"video\"; objectUrl: string; reference: WorkspaceFileReference }\n | {\n maxSizeBytes?: number;\n reason: WorkspaceFilePreviewReadonlyReason;\n reference: WorkspaceFileReference;\n status: \"readonly\";\n }\n | { status: \"text\"; content: string; reference: WorkspaceFileReference }\n | { status: \"unsupported\"; reference: WorkspaceFileReference }\n | { status: \"unavailable\"; reference: WorkspaceFileReference };\n\nexport interface WorkspaceFileReferencePickerControllerSnapshot {\n browseError: Error | null;\n browseRootPath: string | null;\n directoryStateByPath: Record<string, WorkspaceFileReferenceDirectoryState>;\n expandedFolderPaths: Record<string, boolean>;\n initialPathRevealed: boolean;\n isBrowseLoading: boolean;\n isSearchLoading: boolean;\n mode: WorkspaceFileReferencePickerMode;\n previewState: WorkspaceFileReferencePreviewState;\n searchEntries: WorkspaceFileReference[];\n searchError: Error | null;\n searchQuery: string;\n}\n\nexport interface CreateWorkspaceFileReferencePickerControllerInput {\n fileAdapter?: WorkspaceFileReferenceAdapter;\n searchDebounceMs?: number;\n workspaceId: string;\n}\n\nexport interface WorkspaceFileReferencePickerController {\n close(): void;\n getSnapshot(): WorkspaceFileReferencePickerControllerSnapshot;\n loadBrowseRoot(): void;\n open(): void;\n reset(): void;\n revealInitialPath(initialDirectoryPath: string): Promise<string | null>;\n setPreviewReference(reference: WorkspaceFileReference | null): void;\n setSearchQuery(query: string): void;\n readonly store: WorkspaceFileReferencePickerControllerSnapshot;\n toggleFolder(entry: WorkspaceFileReference): void;\n}\n\nconst defaultDirectoryPath = \"/\";\nconst defaultSearchDebounceMs = 180;\n\nexport function createWorkspaceFileReferencePickerController(\n input: CreateWorkspaceFileReferencePickerControllerInput\n): WorkspaceFileReferencePickerController {\n const searchDebounceMs = input.searchDebounceMs ?? defaultSearchDebounceMs;\n // 浏览取数按 key 隔离失效:全局单调 ticket(nextBrowseSeq,永不回退/复用)派发,\n // latestBrowseSeqByKey 记录每个目录(及 reveal 批量)的最新 ticket。取数 resolve 时凭\n // 自身 ticket 是否仍为该 key 的最新值判定是否落库 —— 不同目录的并发取数互不作废\n // (否则全局单计数会让后发取数把先发的另一目录取数结果静默丢弃、令其 loading 永不\n // 清除)。cancelCurrentBrowse 清空该表使全部在途浏览取数失效。\n let nextBrowseSeq = 0;\n const latestBrowseSeqByKey = new Map<string, number>();\n const stampBrowse = (seqKey: string): number => {\n const sequence = ++nextBrowseSeq;\n latestBrowseSeqByKey.set(seqKey, sequence);\n return sequence;\n };\n const isBrowseStale = (seqKey: string, sequence: number): boolean =>\n !retained || latestBrowseSeqByKey.get(seqKey) !== sequence;\n /** reveal 批量加载用的固定 seq key(不与单目录 key 冲突)。 */\n const REVEAL_SEQ_KEY = \"\u0000reveal\";\n let previewObjectUrl: string | null = null;\n let previewSequence = 0;\n let retained = false;\n let searchAbortController: AbortController | null = null;\n let searchSequence = 0;\n let searchTimer: ReturnType<typeof setTimeout> | null = null;\n let snapshot: WorkspaceFileReferencePickerControllerSnapshot = {\n browseError: null,\n browseRootPath: null,\n directoryStateByPath: {},\n expandedFolderPaths: {},\n initialPathRevealed: false,\n isBrowseLoading: false,\n isSearchLoading: false,\n mode: \"browse\",\n previewState: { status: \"empty\" },\n searchEntries: [],\n searchError: null,\n searchQuery: \"\"\n };\n const store = proxy(snapshot);\n\n const setSnapshot = (\n update:\n | Partial<WorkspaceFileReferencePickerControllerSnapshot>\n | ((\n current: WorkspaceFileReferencePickerControllerSnapshot\n ) => WorkspaceFileReferencePickerControllerSnapshot)\n ) => {\n const next =\n typeof update === \"function\"\n ? update(snapshot)\n : { ...snapshot, ...update };\n if (next === snapshot) {\n return;\n }\n snapshot = next;\n Object.assign(store, next);\n };\n\n const loadDirectoryListing = async (path?: string | null) => {\n if (!input.fileAdapter?.listDirectory) {\n return null;\n }\n const listing = await input.fileAdapter.listDirectory({\n path: path ?? undefined,\n workspaceId: input.workspaceId\n });\n const displayPath =\n listing.directoryPath || listing.rootPath || path || defaultDirectoryPath;\n const normalizedPath = normalizeDirectoryPath(displayPath);\n\n return {\n displayPath,\n entries: uniqueWorkspaceFileReferences(listing.entries),\n normalizedPath\n };\n };\n\n const resolveMode = (query: string) =>\n query.trim().length > 0 && input.fileAdapter?.searchReferences\n ? \"search\"\n : \"browse\";\n\n const clearSearchTimer = () => {\n if (searchTimer === null) {\n return;\n }\n clearTimeout(searchTimer);\n searchTimer = null;\n };\n\n const cancelCurrentSearch = () => {\n clearSearchTimer();\n searchSequence += 1;\n searchAbortController?.abort();\n searchAbortController = null;\n };\n\n const cancelCurrentBrowse = () => {\n // 清空 ticket 表:在途取数 resolve 时其 key 已无最新 ticket(get→undefined),被丢弃。\n latestBrowseSeqByKey.clear();\n };\n\n const cancelCurrentPreview = () => {\n previewSequence += 1;\n if (!previewObjectUrl) {\n return;\n }\n URL.revokeObjectURL(previewObjectUrl);\n previewObjectUrl = null;\n };\n\n const clearSearchResults = () => {\n cancelCurrentSearch();\n setSnapshot({\n isSearchLoading: false,\n searchEntries: [],\n searchError: null\n });\n };\n\n const runSearch = async (query: string) => {\n if (!retained || !input.fileAdapter?.searchReferences) {\n return;\n }\n\n const sequence = ++searchSequence;\n searchAbortController?.abort();\n const abortController = new AbortController();\n searchAbortController = abortController;\n setSnapshot({\n isSearchLoading: true,\n searchError: null\n });\n\n try {\n const refs = await input.fileAdapter.searchReferences({\n query,\n signal: abortController.signal,\n workspaceId: input.workspaceId\n });\n if (!retained || sequence !== searchSequence) {\n return;\n }\n setSnapshot({\n isSearchLoading: false,\n searchEntries: uniqueWorkspaceFileReferences(refs),\n searchError: null\n });\n } catch (error) {\n if (isAbortError(error) || sequence !== searchSequence || !retained) {\n return;\n }\n setSnapshot({\n isSearchLoading: false,\n searchEntries: [],\n searchError: normalizeControllerError(\n error,\n \"Workspace file reference search failed\"\n )\n });\n } finally {\n if (sequence === searchSequence) {\n searchAbortController = null;\n }\n }\n };\n\n const scheduleSearch = () => {\n clearSearchTimer();\n const query = snapshot.searchQuery.trim();\n if (!retained || !input.fileAdapter?.searchReferences || !query) {\n clearSearchResults();\n return;\n }\n\n if (searchDebounceMs <= 0) {\n void runSearch(query);\n return;\n }\n\n searchTimer = setTimeout(() => {\n searchTimer = null;\n void runSearch(query);\n }, searchDebounceMs);\n };\n\n const loadBrowseRoot = async () => {\n if (\n !retained ||\n snapshot.mode !== \"browse\" ||\n !(\n input.fileAdapter?.listDirectory || input.fileAdapter?.loadReferenceTree\n )\n ) {\n return;\n }\n const activeBrowseRootPath = snapshot.browseRootPath;\n const normalizedRoot = activeBrowseRootPath\n ? normalizeDirectoryPath(activeBrowseRootPath)\n : null;\n if (\n normalizedRoot &&\n snapshot.directoryStateByPath[normalizedRoot]?.loaded\n ) {\n return;\n }\n\n const seqKey = normalizedRoot ?? \"\";\n const sequence = stampBrowse(seqKey);\n setSnapshot({\n browseError: null,\n isBrowseLoading: true\n });\n\n try {\n if (input.fileAdapter.loadReferenceTree) {\n const treeSnapshot = await input.fileAdapter.loadReferenceTree({\n path: activeBrowseRootPath ?? undefined,\n prefetchBudgetMs: 500,\n prefetchDepth: 4,\n workspaceId: input.workspaceId\n });\n if (isBrowseStale(seqKey, sequence)) {\n return;\n }\n setSnapshot({\n browseRootPath: normalizeDirectoryPath(\n treeSnapshot.directory.directoryPath\n ),\n directoryStateByPath:\n createWorkspaceFileReferenceDirectoryStateFromSnapshot(\n treeSnapshot\n ),\n isBrowseLoading: false\n });\n return;\n }\n\n const listing = await loadDirectoryListing(activeBrowseRootPath);\n if (isBrowseStale(seqKey, sequence) || !listing) {\n return;\n }\n setSnapshot((current) => ({\n ...current,\n browseRootPath: listing.normalizedPath,\n directoryStateByPath: {\n ...current.directoryStateByPath,\n [listing.normalizedPath]: {\n displayPath: listing.displayPath,\n entries: listing.entries,\n loaded: true,\n loading: false\n }\n },\n isBrowseLoading: false\n }));\n } catch (error) {\n if (isBrowseStale(seqKey, sequence)) {\n return;\n }\n setSnapshot({\n browseError: normalizeControllerError(\n error,\n \"Workspace file reference browse failed\"\n ),\n isBrowseLoading: false\n });\n }\n };\n\n const loadFolderChildren = async (folder: WorkspaceFileReference) => {\n const folderKey = normalizeDirectoryPath(folder.path);\n if (\n !retained ||\n snapshot.directoryStateByPath[folderKey]?.loaded ||\n snapshot.directoryStateByPath[folderKey]?.loading\n ) {\n return;\n }\n\n const sequence = stampBrowse(folderKey);\n setSnapshot((current) => ({\n ...current,\n directoryStateByPath: {\n ...current.directoryStateByPath,\n [folderKey]: {\n displayPath: folder.path,\n entries: current.directoryStateByPath[folderKey]?.entries ?? [],\n loaded: current.directoryStateByPath[folderKey]?.loaded ?? false,\n loading: true\n }\n }\n }));\n\n try {\n const listing = await loadDirectoryListing(folderKey);\n if (isBrowseStale(folderKey, sequence) || !listing) {\n return;\n }\n setSnapshot((current) => ({\n ...current,\n directoryStateByPath: {\n ...current.directoryStateByPath,\n [folderKey]: {\n displayPath: listing.displayPath,\n entries: listing.entries,\n loaded: true,\n loading: false\n }\n }\n }));\n } catch {\n if (isBrowseStale(folderKey, sequence)) {\n return;\n }\n setSnapshot((current) => ({\n ...current,\n directoryStateByPath: {\n ...current.directoryStateByPath,\n [folderKey]: {\n displayPath:\n current.directoryStateByPath[folderKey]?.displayPath ??\n folder.path,\n entries: current.directoryStateByPath[folderKey]?.entries ?? [],\n loaded: current.directoryStateByPath[folderKey]?.loaded ?? false,\n loading: false\n }\n }\n }));\n }\n };\n\n const loadReferencePreview = async (\n reference: WorkspaceFileReference,\n target: WorkspaceFilePreviewActivationTarget,\n sequence: number\n ) => {\n try {\n const preview = await input.fileAdapter?.readReferencePreview?.({\n reference,\n workspaceId: input.workspaceId\n });\n if (!retained || sequence !== previewSequence) {\n return;\n }\n if (!preview) {\n setSnapshot({\n previewState: { reference, status: \"unsupported\" }\n });\n return;\n }\n\n const nextState = createReferencePreviewState(reference, target, preview);\n if (!retained || sequence !== previewSequence) {\n if (nextState.status === \"image\" || nextState.status === \"video\") {\n URL.revokeObjectURL(nextState.objectUrl);\n }\n return;\n }\n if (nextState.status === \"image\" || nextState.status === \"video\") {\n previewObjectUrl = nextState.objectUrl;\n }\n setSnapshot({\n previewState: nextState\n });\n } catch {\n if (!retained || sequence !== previewSequence) {\n return;\n }\n setSnapshot({\n previewState: { reference, status: \"error\" }\n });\n }\n };\n\n const setPreviewReference = (reference: WorkspaceFileReference | null) => {\n cancelCurrentPreview();\n if (!retained || !reference) {\n setSnapshot({\n previewState: { status: \"empty\" }\n });\n return;\n }\n\n const readiness = resolveWorkspaceFilePreviewReadiness(reference);\n if (readiness.status === \"directory\") {\n setSnapshot({\n previewState: { reference, status: \"directory\" }\n });\n return;\n }\n if (readiness.status === \"unsupported\") {\n setSnapshot({\n previewState: { reference, status: \"unsupported\" }\n });\n return;\n }\n if (readiness.status === \"readonly\") {\n setSnapshot({\n previewState: {\n maxSizeBytes: readiness.maxSizeBytes,\n reason: readiness.reason,\n reference,\n status: \"readonly\"\n }\n });\n return;\n }\n\n if (!input.fileAdapter?.readReferencePreview) {\n setSnapshot({\n previewState: { reference, status: \"unavailable\" }\n });\n return;\n }\n\n const sequence = ++previewSequence;\n setSnapshot({\n previewState: { reference, status: \"loading\" }\n });\n void loadReferencePreview(reference, readiness.target, sequence);\n };\n\n return {\n close() {\n retained = false;\n cancelCurrentBrowse();\n cancelCurrentPreview();\n cancelCurrentSearch();\n setSnapshot({\n isBrowseLoading: false,\n isSearchLoading: false,\n previewState: { status: \"empty\" }\n });\n },\n getSnapshot() {\n return snapshot;\n },\n loadBrowseRoot() {\n void loadBrowseRoot();\n },\n open() {\n if (retained) {\n return;\n }\n retained = true;\n if (snapshot.mode === \"search\") {\n scheduleSearch();\n return;\n }\n void loadBrowseRoot();\n },\n async revealInitialPath(initialDirectoryPath) {\n if (\n !retained ||\n snapshot.mode !== \"browse\" ||\n snapshot.initialPathRevealed ||\n !snapshot.browseRootPath\n ) {\n return null;\n }\n\n const rootPath = normalizeDirectoryPath(snapshot.browseRootPath);\n if (!isPathInsideOrEqual(initialDirectoryPath, rootPath)) {\n setSnapshot({\n initialPathRevealed: true\n });\n return null;\n }\n\n const sequence = stampBrowse(REVEAL_SEQ_KEY);\n const loadedDirectories: Record<\n string,\n WorkspaceFileReferenceDirectoryState\n > = {};\n const expandedDirectories: Record<string, boolean> = {};\n const directoryState = (path: string) =>\n loadedDirectories[path] ?? snapshot.directoryStateByPath[path];\n\n const directoryPaths = directoryChainBetween(\n rootPath,\n initialDirectoryPath\n );\n for (const directoryPath of directoryPaths) {\n expandedDirectories[directoryPath] = true;\n }\n\n const listings = await Promise.all(\n directoryPaths\n .filter((directoryPath) => !directoryState(directoryPath)?.loaded)\n .map(async (directoryPath) => {\n try {\n return await loadDirectoryListing(directoryPath);\n } catch {\n return null;\n }\n })\n );\n\n for (const listing of listings) {\n if (!listing) {\n continue;\n }\n loadedDirectories[listing.normalizedPath] = {\n displayPath: listing.displayPath,\n entries: listing.entries,\n loaded: true,\n loading: false\n };\n }\n\n if (isBrowseStale(REVEAL_SEQ_KEY, sequence)) {\n return null;\n }\n setSnapshot((current) => ({\n ...current,\n directoryStateByPath: {\n ...current.directoryStateByPath,\n ...loadedDirectories\n },\n expandedFolderPaths: {\n ...current.expandedFolderPaths,\n ...expandedDirectories\n },\n initialPathRevealed: true\n }));\n return initialDirectoryPath;\n },\n reset() {\n cancelCurrentBrowse();\n cancelCurrentPreview();\n cancelCurrentSearch();\n setSnapshot({\n browseError: null,\n browseRootPath: null,\n directoryStateByPath: {},\n expandedFolderPaths: {},\n initialPathRevealed: false,\n isBrowseLoading: false,\n isSearchLoading: false,\n mode: \"browse\",\n previewState: { status: \"empty\" },\n searchEntries: [],\n searchError: null,\n searchQuery: \"\"\n });\n },\n setPreviewReference(reference) {\n setPreviewReference(reference);\n },\n setSearchQuery(query) {\n if (query === snapshot.searchQuery) {\n return;\n }\n const nextMode = resolveMode(query);\n setSnapshot({\n mode: nextMode,\n searchQuery: query,\n ...(nextMode === \"browse\" ? { isSearchLoading: false } : {})\n });\n if (nextMode === \"search\") {\n cancelCurrentBrowse();\n scheduleSearch();\n return;\n }\n clearSearchResults();\n void loadBrowseRoot();\n },\n get store() {\n return store;\n },\n toggleFolder(entry) {\n const folderKey = normalizeDirectoryPath(entry.path);\n const childState = snapshot.directoryStateByPath[folderKey];\n const nextExpanded = !(snapshot.expandedFolderPaths[folderKey] ?? false);\n\n setSnapshot((current) => ({\n ...current,\n expandedFolderPaths: {\n ...current.expandedFolderPaths,\n [folderKey]: nextExpanded\n }\n }));\n if (nextExpanded && !childState?.loaded && !childState?.loading) {\n void loadFolderChildren(entry);\n }\n }\n };\n}\n\nfunction isAbortError(error: unknown): boolean {\n return error instanceof Error && error.name === \"AbortError\";\n}\n\nfunction normalizeControllerError(\n error: unknown,\n fallbackMessage: string\n): Error {\n return error instanceof Error ? error : new Error(fallbackMessage);\n}\n\nfunction createReferencePreviewState(\n reference: WorkspaceFileReference,\n target: WorkspaceFilePreviewActivationTarget,\n preview: WorkspaceFileReferencePreview\n): WorkspaceFileReferencePreviewState {\n const loadedState = createWorkspaceFilePreviewLoadedState({\n bytes: preview.bytes,\n contentType: preview.contentType,\n entry: reference,\n renderHtml: true,\n target: {\n ...target,\n fileKind: preview.kind\n }\n });\n\n if (loadedState.status === \"image\") {\n return {\n objectUrl: URL.createObjectURL(\n new Blob([loadedState.bytes], {\n type: loadedState.contentType\n })\n ),\n reference,\n status: \"image\"\n };\n }\n if (loadedState.status === \"video\") {\n return {\n objectUrl: URL.createObjectURL(\n new Blob([loadedState.bytes], {\n type: loadedState.contentType\n })\n ),\n reference,\n status: \"video\"\n };\n }\n\n if (loadedState.status === \"text\") {\n return {\n content: loadedState.content,\n reference,\n status: \"text\"\n };\n }\n if (loadedState.status === \"html\") {\n return {\n content: loadedState.content,\n reference,\n status: \"html\"\n };\n }\n\n return {\n maxSizeBytes: loadedState.maxSizeBytes,\n reason: loadedState.reason,\n reference,\n status: \"readonly\"\n };\n}\n\nfunction isPathInsideOrEqual(path: string, root: string): boolean {\n const normalizedPath = normalizeDirectoryPath(path);\n const normalizedRoot = normalizeDirectoryPath(root);\n if (normalizedRoot === \"/\") {\n return normalizedPath.startsWith(\"/\");\n }\n return (\n normalizedPath === normalizedRoot ||\n normalizedPath.startsWith(`${normalizedRoot}/`)\n );\n}\n\nfunction directoryChainBetween(root: string, target: string): string[] {\n const normalizedRoot = normalizeDirectoryPath(root);\n let current = normalizeDirectoryPath(target);\n const chain: string[] = [];\n\n while (current && current !== normalizedRoot) {\n chain.unshift(current);\n const parent = dirnameDirectoryPath(current);\n if (parent === current) {\n break;\n }\n current = parent;\n }\n\n return chain;\n}\n\nfunction dirnameDirectoryPath(path: string): string {\n const normalized = normalizeDirectoryPath(path);\n if (normalized === \"/\") {\n return \"/\";\n }\n const index = normalized.lastIndexOf(\"/\");\n if (index <= 0) {\n return \"/\";\n }\n return normalized.slice(0, index);\n}\n","import { proxy } from \"valtio/vanilla\";\nimport type {\n NodeRef,\n ReferenceHandle,\n ReferenceLocateTarget,\n ReferenceNode,\n ReferenceScope,\n SelectedReference\n} from \"../../../contracts/referenceSource.ts\";\nimport type {\n ReferenceSourceAggregator,\n ReferenceSourceTab\n} from \"../../../core/referenceSourceAggregator.ts\";\nimport { SOURCE_ROOT_NODE_ID } from \"../../../core/referenceSourceAggregator.ts\";\nimport {\n appendReferencePage,\n nodeRefKey,\n sortReferenceNodes\n} from \"../../../core/referenceSourceUtils.ts\";\n\n/**\n * node-keyed 多源 picker 的逻辑层 controller(顶部分源 tab)。\n * 独立于现有 WorkspaceFileReferencePickerController —— issue-manager 不受影响。\n * 设计见 docs/architecture/agent-reference-source-services.md §2 / §3。\n *\n * 本层只管:tabs、per-source inline 展开树(node-keyed)、cursor 加载更多、\n * per-tab 搜索、跨 tab 选中集、confirm。预览/打开留待 UI 接入步骤。\n */\n\nexport type ReferenceSourcePickerMode = \"browse\" | \"search\";\n\nexport interface ReferenceSourceNodeChildrenState {\n /** 已累积的子节点(含多页 append)。 */\n entries: ReferenceNode[];\n nextCursor: string | null;\n loaded: boolean;\n loading: boolean;\n error: Error | null;\n}\n\nexport interface ReferenceSourceTabState {\n sourceId: string;\n expandedKeys: Record<string, boolean>;\n /** key = nodeRefKey;源根用 ROOT_CHILDREN_KEY。 */\n childrenByKey: Record<string, ReferenceSourceNodeChildrenState>;\n mode: ReferenceSourcePickerMode;\n searchQuery: string;\n /** 已选文件类型筛选分类 id(全局统一口径);与 searchQuery 一起构成「查询」。 */\n searchFilters: string[];\n /** 当前搜索限定的二级分组节点 nodeId(左栏选中分组);null = 跨整源搜索。 */\n searchScopeNodeId: string | null;\n searchEntries: ReferenceNode[];\n searchNextCursor: string | null;\n /**\n * 当前查询使用的结果上限(增长式分页:拉到底部时 +SEARCH_PAGE_SIZE 重查)。\n * 0 = 尚未发起查询;每次新查询(改关键词/筛选/分组/换源)重置为 SEARCH_PAGE_SIZE。\n */\n searchLimit: number;\n /** 是否可能还有更多结果(本页返回数 ≥ 请求上限,且未达全局上限)。 */\n searchHasMore: boolean;\n /** 正在加载下一页(增长 limit 重查在途);用于底部 spinner,不清空已有结果。 */\n isSearchLoadingMore: boolean;\n isSearchLoading: boolean;\n searchError: Error | null;\n}\n\nexport interface ReferenceSourcePickerSnapshot {\n isLoadingTabs: boolean;\n tabsError: Error | null;\n tabs: ReferenceSourceTab[];\n activeSourceId: string | null;\n bySource: Record<string, ReferenceSourceTabState>;\n /** 跨 tab 累积的选中文件节点,按选中顺序。 */\n selection: ReferenceNode[];\n}\n\n/** confirmGrouped 的产物:一个 navigable 源文件夹 = 一个 bundle(保留分组)。 */\nexport interface ReferenceConfirmBundle {\n /** 被选中的文件夹节点(提供 displayName / iconUrl / ref 作为 bundle 身份)。 */\n root: ReferenceNode;\n /**\n * 该 bundle 归一成的可被 agent 解析的领域句柄(见 ReferenceHandle)。\n * 由所属源 describeReferenceHandle 解码;发给 agent 的 `mention://workspace-reference/...`\n * 由它构造,替代把文件路径全部展开。源不支持解码时为 null。\n */\n handle: ReferenceHandle | null;\n}\n\nexport interface ReferenceConfirmGroupedResult {\n /** 单独选中的文件 + 非 navigable 源的文件夹(保持单条引用)。 */\n files: SelectedReference[];\n /** 每个 navigable 源文件夹一项,文件已折叠在内。 */\n bundles: ReferenceConfirmBundle[];\n}\n\nexport interface ReferenceSourcePickerController {\n readonly store: ReferenceSourcePickerSnapshot;\n getSnapshot(): ReferenceSourcePickerSnapshot;\n open(): void;\n close(): void;\n reset(): void;\n setActiveSource(sourceId: string, scopeNodeId?: string | null): void;\n /**\n * 把「定位目标」解析为从源根到目标的真实 ReferenceNode 路径(root → leaf)。\n * 纯数据解析:逐层 listChildren 找到真实节点(带 displayName 等),不改动任何 UI/导航态。\n * 源不支持定位 / 未找到时返回部分路径(可能为空)。供「打开即定位」一次性应用。\n */\n locatePath(target: ReferenceLocateTarget): Promise<ReferenceNode[]>;\n /** 确保某节点(null=当前源根)的子节点已加载(抽屉式导航进入用,不切换展开态)。 */\n ensureChildren(node: ReferenceNode | null): void;\n /** 确保指定源的根层级已加载(左栏可同时展开多源时,为非 active 源预载分组)。 */\n ensureSourceRoot(sourceId: string): void;\n /** 幂等展开某 folder 并确保其子节点已加载(定位目标默认展开用)。 */\n expandNode(node: ReferenceNode): void;\n toggleNode(node: ReferenceNode): void;\n loadMore(node: ReferenceNode | null): void;\n /**\n * 拉取指定源根层级的下一页(左栏二级分组分页用)。\n * 与 loadMore(null) 的区别:后者只作用于 active 源;本方法显式指定源,\n * 使左栏任一源(无论是否 active)都能继续拉取其分组。\n */\n loadMoreSourceRoot(sourceId: string): void;\n /**\n * 设置搜索关键词。scopeNodeId 指定把搜索限定在 active 源内某个二级分组(左栏选中分组)\n * 的节点 nodeId;缺省/null = 跨整源搜索。query 与 filters 任一非空即进入查询(平铺)态。\n */\n setSearchQuery(query: string, scopeNodeId?: string | null): void;\n /**\n * 设置已选文件类型筛选分类(全局统一口径)。与 setSearchQuery 同构:\n * 与现有关键词一起构成查询并(去抖)重搜;query 与 filters 同时为空才回浏览态。\n */\n setSearchFilters(filters: string[], scopeNodeId?: string | null): void;\n /** 搜索进行中切换左栏分组时更新限定范围并以现有关键词/筛选重搜。 */\n setSearchScope(scopeNodeId: string | null): void;\n /**\n * 加载更多查询结果(增长式分页):以同一关键词/筛选、更大的 limit 立即重查,\n * 结果整体替换(保留旧结果直到新结果就绪)。无更多 / 在途 / 非查询态时 no-op。\n */\n loadMoreSearch(): void;\n toggleSelection(node: ReferenceNode): void;\n toggleSingleSelectionAndExpand(node: ReferenceNode): void;\n clearSelection(): void;\n /**\n * 选中归一。文件 → 单条;文件夹按所属源区分:\n * - 本地(非 navigable)源:保持单条 folder 引用(filesystem 路径与目录一一对应);\n * - app/issue(navigable)源:其文件夹下文件在 filesystem 里不一定落在该目录路径下,\n * 故递归 listChildren 枚举,展开成多条文件引用。\n * 含异步枚举,故返回 Promise;结果按 path 去重、保序。\n */\n confirm(selection?: readonly ReferenceNode[]): Promise<SelectedReference[]>;\n /**\n * 与 confirm 同源,但保留分组:navigable 源的每个选中文件夹折叠成一个 bundle\n * (文件已递归展开在内),其余作为单条文件。供「文件夹 = 一个节点」的插入形态用。\n */\n confirmGrouped(\n selection?: readonly ReferenceNode[]\n ): Promise<ReferenceConfirmGroupedResult>;\n}\n\nexport interface CreateReferenceSourcePickerControllerInput {\n aggregator: ReferenceSourceAggregator;\n scope: ReferenceScope;\n searchDebounceMs?: number;\n}\n\n/** 源根 children 的 key(node===null 时)。 */\nexport const ROOT_CHILDREN_KEY = nodeRefKey({\n sourceId: \"\",\n nodeId: SOURCE_ROOT_NODE_ID\n});\n\nconst defaultSearchDebounceMs = 180;\n\n/** 查询(搜索/筛选)结果的初始/每页步长。拉到底部 +一页 重查(增长式分页)。 */\nexport const SEARCH_PAGE_SIZE = 30;\n/**\n * 查询结果的全局上限。增长式分页对每个源都用「同一关键词/筛选 + 更大 limit 重查」实现,\n * 由各源 daemon 自行夹取(本地 200、应用硬校验 ≤200、议题 ≤200),故统一封顶 200。\n */\nexport const SEARCH_MAX_LIMIT = 200;\n\nfunction emptyTabState(sourceId: string): ReferenceSourceTabState {\n return {\n sourceId,\n expandedKeys: {},\n childrenByKey: {},\n mode: \"browse\",\n searchQuery: \"\",\n searchFilters: [],\n searchScopeNodeId: null,\n searchEntries: [],\n searchNextCursor: null,\n searchLimit: 0,\n searchHasMore: false,\n isSearchLoadingMore: false,\n isSearchLoading: false,\n searchError: null\n };\n}\n\nfunction emptyChildrenState(): ReferenceSourceNodeChildrenState {\n return {\n entries: [],\n nextCursor: null,\n loaded: false,\n loading: false,\n error: null\n };\n}\n\nexport function createReferenceSourcePickerController(\n input: CreateReferenceSourcePickerControllerInput\n): ReferenceSourcePickerController {\n const { aggregator, scope } = input;\n const searchDebounceMs = input.searchDebounceMs ?? defaultSearchDebounceMs;\n\n let retained = false;\n // tabs 加载完成的 promise(供 revealTarget 等到 setActiveSource 可生效)。\n let tabsReady: Promise<void> = Promise.resolve();\n let tabsSequence = 0;\n // 浏览取数的「按 key 隔离」失效计数:全局单调 ticket(nextBrowseSeq,永不回退/复用)\n // 派发,latestBrowseSeqByKey 记录每个 (source, children-key) 的最新 ticket。\n // 取数 resolve 时凭自身 ticket 是否仍为该 key 的最新值判定是否落库 —— 这样不同\n // key 的并发取数互不作废(否则全局单计数会让后发的取数把先发的另一 key 取数结果\n // 静默丢弃、令其 loading 永不清除)。close/reset 清空该表使全部在途取数失效。\n let nextBrowseSeq = 0;\n const latestBrowseSeqByKey = new Map<string, number>();\n let searchSequence = 0;\n let searchAbortController: AbortController | null = null;\n let searchTimer: ReturnType<typeof setTimeout> | null = null;\n\n let snapshot: ReferenceSourcePickerSnapshot = {\n isLoadingTabs: false,\n tabsError: null,\n tabs: [],\n activeSourceId: null,\n bySource: {},\n selection: []\n };\n const store = proxy(snapshot);\n\n const setSnapshot = (\n update:\n | Partial<ReferenceSourcePickerSnapshot>\n | ((\n current: ReferenceSourcePickerSnapshot\n ) => ReferenceSourcePickerSnapshot)\n ) => {\n const next =\n typeof update === \"function\"\n ? update(snapshot)\n : { ...snapshot, ...update };\n if (next === snapshot) {\n return;\n }\n snapshot = next;\n Object.assign(store, next);\n };\n\n /** 不可变地更新某 tab 的状态。 */\n const updateTab = (\n sourceId: string,\n updater: (tab: ReferenceSourceTabState) => ReferenceSourceTabState\n ) => {\n setSnapshot((current) => {\n const existing = current.bySource[sourceId] ?? emptyTabState(sourceId);\n const nextTab = updater(existing);\n if (nextTab === existing) {\n return current;\n }\n return {\n ...current,\n bySource: { ...current.bySource, [sourceId]: nextTab }\n };\n });\n };\n\n const childrenKeyForNode = (node: ReferenceNode | null): string =>\n node ? nodeRefKey(node.ref) : ROOT_CHILDREN_KEY;\n\n const setChildrenState = (\n sourceId: string,\n key: string,\n patch: Partial<ReferenceSourceNodeChildrenState>\n ) => {\n updateTab(sourceId, (tab) => {\n const current = tab.childrenByKey[key] ?? emptyChildrenState();\n return {\n ...tab,\n childrenByKey: {\n ...tab.childrenByKey,\n [key]: { ...current, ...patch }\n }\n };\n });\n };\n\n const loadChildren = async (\n sourceId: string,\n node: ReferenceNode | null,\n options: { append: boolean }\n ) => {\n if (!retained) {\n return;\n }\n const key = childrenKeyForNode(node);\n const tab = snapshot.bySource[sourceId];\n const existing = tab?.childrenByKey[key];\n const cursor = options.append ? (existing?.nextCursor ?? null) : null;\n if (existing?.loading) {\n return;\n }\n if (options.append && !cursor) {\n return;\n }\n\n // ROOT_CHILDREN_KEY 等常量 key 在各源间复用,故 ticket 表按 (source, key) 命名空间。\n const seqKey = `${sourceId}\u0000${key}`;\n const sequence = ++nextBrowseSeq;\n latestBrowseSeqByKey.set(seqKey, sequence);\n setChildrenState(sourceId, key, { loading: true, error: null });\n\n try {\n const result = await aggregator.listChildren(\n scope,\n node ? node.ref : { sourceId, nodeId: SOURCE_ROOT_NODE_ID },\n { cursor }\n );\n if (!retained || latestBrowseSeqByKey.get(seqKey) !== sequence) {\n return;\n }\n // append 走 cursor 语义:保序 append + 去重,不重排已得项(不变式 #4)。\n // 首次加载则整体排序(folder 在前、按名)。\n const prior =\n snapshot.bySource[sourceId]?.childrenByKey[key]?.entries ?? [];\n // 源声明已排序(如「最近访问」按访问时间倒序)时保留其顺序,不再重排。\n const entries = options.append\n ? appendReferencePage(prior, result.entries)\n : result.ordered\n ? [...result.entries]\n : sortReferenceNodes(result.entries);\n setChildrenState(sourceId, key, {\n entries,\n nextCursor: result.nextCursor ?? null,\n loaded: true,\n loading: false,\n error: null\n });\n } catch (error) {\n if (!retained || latestBrowseSeqByKey.get(seqKey) !== sequence) {\n return;\n }\n setChildrenState(sourceId, key, {\n loading: false,\n error: normalizeError(error, \"load children failed\")\n });\n }\n };\n\n const ensureRootLoaded = (sourceId: string) => {\n const root = snapshot.bySource[sourceId]?.childrenByKey[ROOT_CHILDREN_KEY];\n if (root?.loaded || root?.loading) {\n return;\n }\n void loadChildren(sourceId, null, { append: false });\n };\n\n const loadTabs = async () => {\n if (!retained) {\n return;\n }\n const sequence = ++tabsSequence;\n setSnapshot({ isLoadingTabs: true, tabsError: null });\n try {\n const tabs = await aggregator.listSources(scope);\n if (!retained || sequence !== tabsSequence) {\n return;\n }\n const activeSourceId =\n snapshot.activeSourceId &&\n tabs.some((tab) => tab.sourceId === snapshot.activeSourceId)\n ? snapshot.activeSourceId\n : (tabs[0]?.sourceId ?? null);\n setSnapshot((current) => ({\n ...current,\n isLoadingTabs: false,\n tabs,\n activeSourceId,\n bySource: Object.fromEntries(\n tabs.map((tab) => [\n tab.sourceId,\n current.bySource[tab.sourceId] ?? emptyTabState(tab.sourceId)\n ])\n )\n }));\n if (activeSourceId) {\n ensureRootLoaded(activeSourceId);\n }\n } catch (error) {\n if (!retained || sequence !== tabsSequence) {\n return;\n }\n setSnapshot({\n isLoadingTabs: false,\n tabsError: normalizeError(error, \"load reference sources failed\")\n });\n }\n };\n\n const clearSearchTimer = () => {\n if (searchTimer !== null) {\n clearTimeout(searchTimer);\n searchTimer = null;\n }\n };\n\n const cancelSearch = () => {\n clearSearchTimer();\n searchSequence += 1;\n searchAbortController?.abort();\n searchAbortController = null;\n };\n\n const runSearch = async (\n sourceId: string,\n query: string,\n filters: string[],\n scopeNodeId: string | null,\n limit: number,\n loadingMore: boolean\n ) => {\n if (!retained) {\n return;\n }\n const sequence = ++searchSequence;\n searchAbortController?.abort();\n const abortController = new AbortController();\n searchAbortController = abortController;\n // 加载更多:只置底部 spinner、保留旧结果;新查询:置主 loading。\n updateTab(sourceId, (tab) => ({\n ...tab,\n ...(loadingMore\n ? { isSearchLoadingMore: true }\n : { isSearchLoading: true }),\n searchError: null\n }));\n try {\n const result = await aggregator.search(scope, sourceId, {\n query,\n limit,\n signal: abortController.signal,\n ...(filters.length > 0 ? { filters } : {}),\n ...(scopeNodeId == null ? {} : { withinNodeId: scopeNodeId })\n });\n if (!retained || sequence !== searchSequence) {\n return;\n }\n updateTab(sourceId, (tab) => ({\n ...tab,\n isSearchLoading: false,\n isSearchLoadingMore: false,\n searchEntries: sortReferenceNodes(result.entries),\n searchNextCursor: result.nextCursor ?? null,\n searchLimit: limit,\n // 本页返回数达到请求上限、且未触全局上限 → 认为可能还有更多(增长式分页启发式)。\n searchHasMore:\n result.entries.length >= limit && limit < SEARCH_MAX_LIMIT,\n searchError: null\n }));\n } catch (error) {\n if (isAbortError(error) || !retained || sequence !== searchSequence) {\n return;\n }\n updateTab(sourceId, (tab) => ({\n ...tab,\n isSearchLoading: false,\n isSearchLoadingMore: false,\n // 加载更多失败时保留已有结果,仅新查询失败才清空。\n ...(loadingMore ? {} : { searchEntries: [], searchHasMore: false }),\n searchError: normalizeError(error, \"reference search failed\")\n }));\n } finally {\n if (sequence === searchSequence) {\n searchAbortController = null;\n }\n }\n };\n\n /**\n * 递归枚举文件夹下的所有文件节点(app/issue 源专用:文件夹引用需展开成逐个文件)。\n * 走 listChildren + cursor 分页,深入子文件夹;按 nodeRefKey 去重兼防环。不设数量上限。\n */\n const collectFolderFiles = async (\n folder: ReferenceNode\n ): Promise<ReferenceNode[]> => {\n const files: ReferenceNode[] = [];\n const seen = new Set<string>();\n const walk = async (node: ReferenceNode): Promise<void> => {\n let cursor: string | null = null;\n do {\n const result = await aggregator.listChildren(scope, node.ref, {\n cursor\n });\n for (const entry of result.entries) {\n const key = nodeRefKey(entry.ref);\n if (seen.has(key)) {\n continue;\n }\n seen.add(key);\n if (entry.kind === \"folder\") {\n await walk(entry);\n } else {\n files.push(entry);\n }\n }\n cursor = result.nextCursor ?? null;\n } while (cursor);\n };\n await walk(folder);\n return files;\n };\n\n const scheduleSearch = (\n sourceId: string,\n query: string,\n filters: string[],\n scopeNodeId: string | null\n ) => {\n clearSearchTimer();\n // query 与 filters 同时为空才跳过 —— 仅选了筛选(query 空)也要发起查询。\n if (!retained || (!query && filters.length === 0)) {\n return;\n }\n // 新查询恒从首页(SEARCH_PAGE_SIZE)起。\n if (searchDebounceMs <= 0) {\n void runSearch(\n sourceId,\n query,\n filters,\n scopeNodeId,\n SEARCH_PAGE_SIZE,\n false\n );\n return;\n }\n searchTimer = setTimeout(() => {\n searchTimer = null;\n void runSearch(\n sourceId,\n query,\n filters,\n scopeNodeId,\n SEARCH_PAGE_SIZE,\n false\n );\n }, searchDebounceMs);\n };\n\n return {\n get store() {\n return store;\n },\n getSnapshot() {\n return snapshot;\n },\n open() {\n if (retained) {\n return;\n }\n retained = true;\n tabsReady = loadTabs();\n },\n close() {\n retained = false;\n cancelSearch();\n // 清空 ticket 表:在途取数 resolve 时其 key 已无最新 ticket(get→undefined),被丢弃。\n latestBrowseSeqByKey.clear();\n tabsSequence += 1;\n },\n reset() {\n cancelSearch();\n latestBrowseSeqByKey.clear();\n tabsSequence += 1;\n setSnapshot({\n isLoadingTabs: false,\n tabsError: null,\n tabs: [],\n activeSourceId: null,\n bySource: {},\n selection: []\n });\n },\n setActiveSource(sourceId, scopeNodeId) {\n if (!snapshot.tabs.some((tab) => tab.sourceId === sourceId)) {\n return;\n }\n // 跨源切换时把「当前查询」(切换前 active 源的关键词+筛选)带到目标源:\n // 有查询则在目标源已选分组范围内用同一查询重搜(切到对应 tab 即重搜),\n // 无查询则回浏览态加载源根。query 随 tab 走,搜索框/筛选在切源后仍可见。\n const prevTab = snapshot.activeSourceId\n ? snapshot.bySource[snapshot.activeSourceId]\n : undefined;\n const carriedQuery = prevTab?.searchQuery ?? \"\";\n const carriedFilters = prevTab?.searchFilters ?? [];\n const trimmed = carriedQuery.trim();\n const nextScopeNodeId =\n scopeNodeId !== undefined\n ? scopeNodeId\n : (snapshot.bySource[sourceId]?.searchScopeNodeId ?? null);\n cancelSearch();\n setSnapshot({ activeSourceId: sourceId });\n if (trimmed === \"\" && carriedFilters.length === 0) {\n // 全局查询为空:目标源回浏览态(清掉其可能残留的旧查询/结果),加载源根。\n updateTab(sourceId, (tab) =>\n tab.mode === \"browse\" &&\n tab.searchQuery === \"\" &&\n tab.searchFilters.length === 0\n ? tab\n : {\n ...tab,\n mode: \"browse\",\n searchQuery: \"\",\n searchFilters: [],\n searchScopeNodeId: nextScopeNodeId,\n searchEntries: [],\n searchHasMore: false,\n isSearchLoading: false,\n isSearchLoadingMore: false,\n searchError: null\n }\n );\n ensureRootLoaded(sourceId);\n return;\n }\n // 范围用目标源自身已选分组(尚未进过分组则 null=跨整源);随后左栏自动/手动\n // 进入分组会经 setSearchScope 再以新范围重搜(去抖窗口内合并,不重复请求)。\n updateTab(sourceId, (tab) => ({\n ...tab,\n searchQuery: carriedQuery,\n searchFilters: carriedFilters,\n searchScopeNodeId: nextScopeNodeId,\n mode: \"search\",\n // 沿用旧结果直到新结果就绪,避免切源瞬间闪空。\n isSearchLoading: true,\n searchError: null\n }));\n scheduleSearch(sourceId, trimmed, carriedFilters, nextScopeNodeId);\n },\n async locatePath(target) {\n // 等 tabs 就绪(view 在本 promise resolve 后再 setActiveSource,届时 tabs 已加载)。\n await tabsReady;\n if (!retained) {\n return [];\n }\n const refs = await aggregator.locateTarget(\n scope,\n target.sourceId,\n target.params\n );\n if (!refs || refs.length === 0) {\n return [];\n }\n // 逐层 listChildren,把不透明 NodeRef 解析成带 displayName 的真实节点。\n const path: ReferenceNode[] = [];\n let parent: NodeRef = {\n sourceId: target.sourceId,\n nodeId: SOURCE_ROOT_NODE_ID\n };\n for (const ref of refs) {\n const targetKey = nodeRefKey(ref);\n let cursor: string | null = null;\n let node: ReferenceNode | undefined;\n do {\n const { entries, nextCursor } = await aggregator.listChildren(\n scope,\n parent,\n { cursor }\n );\n node = entries.find((entry) => nodeRefKey(entry.ref) === targetKey);\n cursor = nextCursor ?? null;\n } while (!node && cursor);\n if (!node) {\n break;\n }\n path.push(node);\n parent = ref;\n }\n return path;\n },\n ensureChildren(node) {\n const sourceId = node ? node.ref.sourceId : snapshot.activeSourceId;\n if (!sourceId) {\n return;\n }\n const key = childrenKeyForNode(node);\n const childState = snapshot.bySource[sourceId]?.childrenByKey[key];\n if (!childState?.loaded && !childState?.loading) {\n void loadChildren(sourceId, node, { append: false });\n }\n },\n ensureSourceRoot(sourceId) {\n if (\n !sourceId ||\n !snapshot.tabs.some((tab) => tab.sourceId === sourceId)\n ) {\n return;\n }\n ensureRootLoaded(sourceId);\n },\n expandNode(node) {\n if (node.kind !== \"folder\") {\n return;\n }\n const sourceId = node.ref.sourceId;\n const key = nodeRefKey(node.ref);\n updateTab(sourceId, (tab) =>\n tab.expandedKeys[key] === true\n ? tab\n : {\n ...tab,\n expandedKeys: { ...tab.expandedKeys, [key]: true }\n }\n );\n const childState = snapshot.bySource[sourceId]?.childrenByKey[key];\n if (!childState?.loaded && !childState?.loading) {\n void loadChildren(sourceId, node, { append: false });\n }\n },\n toggleNode(node) {\n if (node.kind !== \"folder\") {\n return;\n }\n const sourceId = node.ref.sourceId;\n const key = nodeRefKey(node.ref);\n const wasExpanded =\n snapshot.bySource[sourceId]?.expandedKeys[key] ?? false;\n const nextExpanded = !wasExpanded;\n updateTab(sourceId, (tab) => ({\n ...tab,\n expandedKeys: { ...tab.expandedKeys, [key]: nextExpanded }\n }));\n const childState = snapshot.bySource[sourceId]?.childrenByKey[key];\n if (nextExpanded && !childState?.loaded && !childState?.loading) {\n void loadChildren(sourceId, node, { append: false });\n }\n },\n loadMore(node) {\n const sourceId = node ? node.ref.sourceId : snapshot.activeSourceId;\n if (!sourceId) {\n return;\n }\n void loadChildren(sourceId, node, { append: true });\n },\n loadMoreSourceRoot(sourceId) {\n if (!sourceId) {\n return;\n }\n void loadChildren(sourceId, null, { append: true });\n },\n setSearchQuery(query, scopeNodeId = null) {\n const sourceId = snapshot.activeSourceId;\n if (!sourceId) {\n return;\n }\n const filters = snapshot.bySource[sourceId]?.searchFilters ?? [];\n const trimmed = query.trim();\n // 查询态 = 关键词或筛选任一非空。\n const nextMode: ReferenceSourcePickerMode =\n trimmed || filters.length > 0 ? \"search\" : \"browse\";\n updateTab(sourceId, (tab) => ({\n ...tab,\n searchQuery: query,\n searchScopeNodeId: scopeNodeId,\n mode: nextMode,\n // 进入搜索:立刻置 loading(搜索是 debounce 的,否则键入到取数之间会先渲染空态,\n // 造成「空态 → spinner → 结果」闪烁)。保留上次 searchEntries,细化关键词时沿用旧结果而非闪空。\n ...(nextMode === \"browse\"\n ? { isSearchLoading: false, searchEntries: [], searchError: null }\n : { isSearchLoading: true, searchError: null })\n }));\n if (nextMode === \"search\") {\n scheduleSearch(sourceId, trimmed, filters, scopeNodeId);\n } else {\n cancelSearch();\n ensureRootLoaded(sourceId);\n }\n },\n setSearchFilters(filters, scopeNodeId = null) {\n const sourceId = snapshot.activeSourceId;\n if (!sourceId) {\n return;\n }\n const tab = snapshot.bySource[sourceId];\n const trimmed = tab?.searchQuery.trim() ?? \"\";\n const scopeId = scopeNodeId ?? tab?.searchScopeNodeId ?? null;\n const nextMode: ReferenceSourcePickerMode =\n trimmed || filters.length > 0 ? \"search\" : \"browse\";\n updateTab(sourceId, (current) => ({\n ...current,\n searchFilters: filters,\n searchScopeNodeId: scopeId,\n mode: nextMode,\n ...(nextMode === \"browse\"\n ? { isSearchLoading: false, searchEntries: [], searchError: null }\n : { isSearchLoading: true, searchError: null })\n }));\n if (nextMode === \"search\") {\n scheduleSearch(sourceId, trimmed, filters, scopeId);\n } else {\n cancelSearch();\n ensureRootLoaded(sourceId);\n }\n },\n setSearchScope(scopeNodeId) {\n // 搜索中切换左栏分组(如从某 app 切到另一 app):更新限定范围并以现有关键词/筛选重搜。\n const sourceId = snapshot.activeSourceId;\n if (!sourceId) {\n return;\n }\n const tab = snapshot.bySource[sourceId];\n if (!tab || tab.searchScopeNodeId === scopeNodeId) {\n return;\n }\n updateTab(sourceId, (current) => ({\n ...current,\n searchScopeNodeId: scopeNodeId\n }));\n const trimmed = tab.searchQuery.trim();\n const filters = tab.searchFilters;\n if (tab.mode === \"search\" && (trimmed || filters.length > 0)) {\n scheduleSearch(sourceId, trimmed, filters, scopeNodeId);\n }\n },\n loadMoreSearch() {\n const sourceId = snapshot.activeSourceId;\n if (!sourceId) {\n return;\n }\n const tab = snapshot.bySource[sourceId];\n if (\n !tab ||\n tab.mode !== \"search\" ||\n !tab.searchHasMore ||\n tab.isSearchLoadingMore\n ) {\n return;\n }\n const trimmed = tab.searchQuery.trim();\n if (!trimmed && tab.searchFilters.length === 0) {\n return;\n }\n const nextLimit = Math.min(\n (tab.searchLimit || SEARCH_PAGE_SIZE) + SEARCH_PAGE_SIZE,\n SEARCH_MAX_LIMIT\n );\n if (nextLimit <= tab.searchLimit) {\n return;\n }\n // 加载更多立即触发(不去抖),清掉在途去抖的新查询定时器以免相互覆盖。\n clearSearchTimer();\n void runSearch(\n sourceId,\n trimmed,\n tab.searchFilters,\n tab.searchScopeNodeId,\n nextLimit,\n true\n );\n },\n toggleSelection(node) {\n // 文件与文件夹都可作为引用选中(文件夹的展开在 confirm 时按源处理)。\n const key = nodeRefKey(node.ref);\n setSnapshot((current) => {\n const exists = current.selection.some(\n (item) => nodeRefKey(item.ref) === key\n );\n return {\n ...current,\n selection: exists\n ? current.selection.filter((item) => nodeRefKey(item.ref) !== key)\n : [...current.selection, node]\n };\n });\n },\n toggleSingleSelectionAndExpand(node) {\n const key = nodeRefKey(node.ref);\n setSnapshot((current) => {\n if (current.selection.length > 1) {\n return current;\n }\n const exists = current.selection.some(\n (item) => nodeRefKey(item.ref) === key\n );\n return {\n ...current,\n selection: exists ? [] : [node]\n };\n });\n if (node.kind === \"folder\") {\n const sourceId = node.ref.sourceId;\n const childKey = nodeRefKey(node.ref);\n updateTab(sourceId, (tab) =>\n tab.expandedKeys[childKey] === true\n ? tab\n : {\n ...tab,\n expandedKeys: { ...tab.expandedKeys, [childKey]: true }\n }\n );\n const childState = snapshot.bySource[sourceId]?.childrenByKey[childKey];\n if (!childState?.loaded && !childState?.loading) {\n void loadChildren(sourceId, node, { append: false });\n }\n }\n },\n clearSelection() {\n setSnapshot({ selection: [] });\n },\n async confirm(selection = snapshot.selection) {\n const resolved: SelectedReference[] = [];\n const seenPaths = new Set<string>();\n const push = (ref: SelectedReference) => {\n if (seenPaths.has(ref.path)) {\n return;\n }\n seenPaths.add(ref.path);\n resolved.push(ref);\n };\n for (const node of selection) {\n if (node.kind !== \"folder\") {\n push(aggregator.resolveSelection(node));\n continue;\n }\n const navigable =\n aggregator.getLoadedSource(node.ref.sourceId)?.capabilities\n .navigable ?? false;\n if (!navigable) {\n // 本地源:文件夹保持单条引用(目录路径在 filesystem 里有效)。\n push(aggregator.resolveSelection(node));\n continue;\n }\n // app/issue 源:文件夹下文件不一定落在该目录路径,递归枚举展开成逐个文件引用。\n const files = await collectFolderFiles(node);\n for (const fileNode of files) {\n push(aggregator.resolveSelection(fileNode));\n }\n }\n return resolved;\n },\n async confirmGrouped(selection = snapshot.selection) {\n const files: SelectedReference[] = [];\n const bundles: ReferenceConfirmBundle[] = [];\n const seenPaths = new Set<string>();\n const pushFile = (ref: SelectedReference) => {\n if (seenPaths.has(ref.path)) {\n return;\n }\n seenPaths.add(ref.path);\n files.push(ref);\n };\n for (const node of selection) {\n const source = aggregator.getLoadedSource(node.ref.sourceId);\n const navigable =\n node.kind === \"folder\" && (source?.capabilities.navigable ?? false);\n if (!navigable) {\n // 文件、或非 navigable 源的文件夹:保持单条引用。\n pushFile(aggregator.resolveSelection(node));\n continue;\n }\n // navigable 源文件夹:折叠成一个 bundle。句柄由源解码,供 agent 经\n // `mention://workspace-reference/...` + CLI 按需解析。**不再递归展开文件**——\n // agent 序列化走句柄、chip 数量取 childCount,故确认即时(大产物不再卡顿)。\n const handle = source?.describeReferenceHandle?.(node) ?? null;\n bundles.push({ root: node, handle });\n }\n return { files, bundles };\n }\n };\n}\n\nfunction isAbortError(error: unknown): boolean {\n return error instanceof Error && error.name === \"AbortError\";\n}\n\nfunction normalizeError(error: unknown, fallback: string): Error {\n return error instanceof Error ? error : new Error(fallback);\n}\n"],"mappings":"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","names":["proxy","defaultSearchDebounceMs","proxy","isAbortError"]}
|
package/dist/react/index.d.ts
CHANGED
|
@@ -172,7 +172,7 @@ interface ReferenceSourcePickerController {
|
|
|
172
172
|
open(): void;
|
|
173
173
|
close(): void;
|
|
174
174
|
reset(): void;
|
|
175
|
-
setActiveSource(sourceId: string): void;
|
|
175
|
+
setActiveSource(sourceId: string, scopeNodeId?: string | null): void;
|
|
176
176
|
/**
|
|
177
177
|
* 把「定位目标」解析为从源根到目标的真实 ReferenceNode 路径(root → leaf)。
|
|
178
178
|
* 纯数据解析:逐层 listChildren 找到真实节点(带 displayName 等),不改动任何 UI/导航态。
|
package/dist/react/index.js
CHANGED
package/dist/ui/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
createReferenceSourcePickerController,
|
|
4
4
|
normalizeDirectoryPath,
|
|
5
5
|
useWorkspaceFileReferencePickerView
|
|
6
|
-
} from "../chunk-
|
|
6
|
+
} from "../chunk-RJMPRZTF.js";
|
|
7
7
|
import {
|
|
8
8
|
REFERENCE_FILTER_CATEGORIES,
|
|
9
9
|
WORKSPACE_ROOT_GROUP_NODE_ID,
|
|
@@ -1014,14 +1014,17 @@ function useReferenceSourcePickerView({
|
|
|
1014
1014
|
if (!sourceId) {
|
|
1015
1015
|
return;
|
|
1016
1016
|
}
|
|
1017
|
+
const nextScopeNodeId = node.ref.nodeId === WORKSPACE_ROOT_GROUP_NODE_ID ? null : node.ref.nodeId;
|
|
1017
1018
|
if (sourceId !== snapshot.activeSourceId) {
|
|
1018
|
-
controller.setActiveSource(sourceId);
|
|
1019
|
+
controller.setActiveSource(sourceId, nextScopeNodeId);
|
|
1019
1020
|
}
|
|
1020
1021
|
if (node.ref.nodeId === WORKSPACE_ROOT_GROUP_NODE_ID) {
|
|
1022
|
+
controller.setSearchScope(null);
|
|
1021
1023
|
navigateToRoot(sourceId);
|
|
1022
1024
|
return;
|
|
1023
1025
|
}
|
|
1024
1026
|
controller.ensureChildren(node);
|
|
1027
|
+
controller.setSearchScope(nextScopeNodeId);
|
|
1025
1028
|
setBreadcrumbBySource((current) => ({ ...current, [sourceId]: [node] }));
|
|
1026
1029
|
setFocusedNode(null);
|
|
1027
1030
|
},
|
|
@@ -1123,6 +1126,8 @@ function useReferenceSourcePickerView({
|
|
|
1123
1126
|
setPreviewState({ node, status: "unsupported" });
|
|
1124
1127
|
return;
|
|
1125
1128
|
}
|
|
1129
|
+
const previewSizeBytes = preview.bytes.byteLength;
|
|
1130
|
+
const loadedSizeBytes = node.sizeBytes != null && node.sizeBytes > 0 ? node.sizeBytes : previewSizeBytes;
|
|
1126
1131
|
const loaded = createWorkspaceFilePreviewLoadedState({
|
|
1127
1132
|
bytes: preview.bytes,
|
|
1128
1133
|
contentType: preview.contentType,
|
|
@@ -1131,7 +1136,7 @@ function useReferenceSourcePickerView({
|
|
|
1131
1136
|
name: node.displayName,
|
|
1132
1137
|
path: node.ref.nodeId,
|
|
1133
1138
|
mtimeMs: node.mtimeMs ?? null,
|
|
1134
|
-
sizeBytes:
|
|
1139
|
+
sizeBytes: loadedSizeBytes
|
|
1135
1140
|
},
|
|
1136
1141
|
renderHtml: true,
|
|
1137
1142
|
target: {
|
|
@@ -1139,7 +1144,7 @@ function useReferenceSourcePickerView({
|
|
|
1139
1144
|
name: node.displayName,
|
|
1140
1145
|
path: node.ref.nodeId,
|
|
1141
1146
|
mtimeMs: node.mtimeMs ?? null,
|
|
1142
|
-
sizeBytes:
|
|
1147
|
+
sizeBytes: loadedSizeBytes
|
|
1143
1148
|
}
|
|
1144
1149
|
});
|
|
1145
1150
|
if (cancelled) {
|
|
@@ -1150,7 +1155,12 @@ function useReferenceSourcePickerView({
|
|
|
1150
1155
|
new Blob([loaded.bytes], { type: loaded.contentType })
|
|
1151
1156
|
);
|
|
1152
1157
|
previewObjectUrlRef.current = objectUrl;
|
|
1153
|
-
setPreviewState({
|
|
1158
|
+
setPreviewState({
|
|
1159
|
+
node,
|
|
1160
|
+
objectUrl,
|
|
1161
|
+
previewSizeBytes,
|
|
1162
|
+
status: "image"
|
|
1163
|
+
});
|
|
1154
1164
|
return;
|
|
1155
1165
|
}
|
|
1156
1166
|
if (loaded.status === "video") {
|
|
@@ -1158,19 +1168,35 @@ function useReferenceSourcePickerView({
|
|
|
1158
1168
|
new Blob([loaded.bytes], { type: loaded.contentType })
|
|
1159
1169
|
);
|
|
1160
1170
|
previewObjectUrlRef.current = objectUrl;
|
|
1161
|
-
setPreviewState({
|
|
1171
|
+
setPreviewState({
|
|
1172
|
+
node,
|
|
1173
|
+
objectUrl,
|
|
1174
|
+
previewSizeBytes,
|
|
1175
|
+
status: "video"
|
|
1176
|
+
});
|
|
1162
1177
|
return;
|
|
1163
1178
|
}
|
|
1164
1179
|
if (loaded.status === "text") {
|
|
1165
|
-
setPreviewState({
|
|
1180
|
+
setPreviewState({
|
|
1181
|
+
content: loaded.content,
|
|
1182
|
+
node,
|
|
1183
|
+
previewSizeBytes,
|
|
1184
|
+
status: "text"
|
|
1185
|
+
});
|
|
1166
1186
|
return;
|
|
1167
1187
|
}
|
|
1168
1188
|
if (loaded.status === "html") {
|
|
1169
|
-
setPreviewState({
|
|
1189
|
+
setPreviewState({
|
|
1190
|
+
content: loaded.content,
|
|
1191
|
+
node,
|
|
1192
|
+
previewSizeBytes,
|
|
1193
|
+
status: "html"
|
|
1194
|
+
});
|
|
1170
1195
|
return;
|
|
1171
1196
|
}
|
|
1172
1197
|
setPreviewState({
|
|
1173
1198
|
node,
|
|
1199
|
+
previewSizeBytes,
|
|
1174
1200
|
reason: loaded.reason,
|
|
1175
1201
|
...loaded.maxSizeBytes == null ? {} : { maxSizeBytes: loaded.maxSizeBytes },
|
|
1176
1202
|
status: "readonly"
|
|
@@ -1259,6 +1285,22 @@ function useReferenceSourcePickerView({
|
|
|
1259
1285
|
}
|
|
1260
1286
|
|
|
1261
1287
|
// src/ui/internal/reference/referenceSourcePickerPresentation.ts
|
|
1288
|
+
function formatHierarchyTitle(hierarchy) {
|
|
1289
|
+
if (hierarchy.length === 0) {
|
|
1290
|
+
return null;
|
|
1291
|
+
}
|
|
1292
|
+
return hierarchy.map((crumb) => crumb.displayName).join(" / ");
|
|
1293
|
+
}
|
|
1294
|
+
function formatReferenceNodePathText(node, hierarchy = []) {
|
|
1295
|
+
const decodedPath = decodeReferenceListFileNodeId(node.ref.nodeId);
|
|
1296
|
+
if (decodedPath) {
|
|
1297
|
+
return decodedPath;
|
|
1298
|
+
}
|
|
1299
|
+
if (node.kind === "folder" && node.ref.nodeId.startsWith("g:")) {
|
|
1300
|
+
return formatHierarchyTitle(completeHierarchy(hierarchy, node)) || node.displayName;
|
|
1301
|
+
}
|
|
1302
|
+
return node.contextLabel?.trim() || node.ref.nodeId;
|
|
1303
|
+
}
|
|
1262
1304
|
function formatReferencePreviewDateTime(ms, options = {}) {
|
|
1263
1305
|
const timeZone = options.timeZone ?? Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
1264
1306
|
const formatter = new Intl.DateTimeFormat(options.locale, {
|
|
@@ -1275,6 +1317,46 @@ function formatReferencePreviewDateTime(ms, options = {}) {
|
|
|
1275
1317
|
);
|
|
1276
1318
|
return `${parts.year}-${parts.month}-${parts.day} ${parts.hour}:${parts.minute}`;
|
|
1277
1319
|
}
|
|
1320
|
+
function resolveReferencePreviewSizeBytes(node, previewState) {
|
|
1321
|
+
const metadataSize = normalizedSizeBytes(node.sizeBytes);
|
|
1322
|
+
if (metadataSize != null && metadataSize > 0) {
|
|
1323
|
+
return metadataSize;
|
|
1324
|
+
}
|
|
1325
|
+
if (previewState.node && previewState.node.ref.sourceId === node.ref.sourceId && previewState.node.ref.nodeId === node.ref.nodeId) {
|
|
1326
|
+
const previewSize = normalizedSizeBytes(previewState.previewSizeBytes);
|
|
1327
|
+
if (previewSize != null && previewSize > 0) {
|
|
1328
|
+
return previewSize;
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
return metadataSize;
|
|
1332
|
+
}
|
|
1333
|
+
function completeHierarchy(hierarchy, node) {
|
|
1334
|
+
const last = hierarchy.at(-1);
|
|
1335
|
+
if (last && last.ref.sourceId === node.ref.sourceId && last.ref.nodeId === node.ref.nodeId) {
|
|
1336
|
+
return [...hierarchy];
|
|
1337
|
+
}
|
|
1338
|
+
return [...hierarchy, node];
|
|
1339
|
+
}
|
|
1340
|
+
function decodeReferenceListFileNodeId(nodeId) {
|
|
1341
|
+
if (!nodeId.startsWith("f:")) {
|
|
1342
|
+
return null;
|
|
1343
|
+
}
|
|
1344
|
+
try {
|
|
1345
|
+
const normalized = nodeId.slice(2).replace(/-/g, "+").replace(/_/g, "/");
|
|
1346
|
+
const padded = normalized.padEnd(
|
|
1347
|
+
normalized.length + (4 - normalized.length % 4) % 4,
|
|
1348
|
+
"="
|
|
1349
|
+
);
|
|
1350
|
+
const binary = atob(padded);
|
|
1351
|
+
const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));
|
|
1352
|
+
return new TextDecoder().decode(bytes);
|
|
1353
|
+
} catch {
|
|
1354
|
+
return null;
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
function normalizedSizeBytes(sizeBytes) {
|
|
1358
|
+
return typeof sizeBytes === "number" && Number.isFinite(sizeBytes) && sizeBytes >= 0 ? sizeBytes : null;
|
|
1359
|
+
}
|
|
1278
1360
|
|
|
1279
1361
|
// src/ui/internal/reference/ReferenceSourcePicker.tsx
|
|
1280
1362
|
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
@@ -1503,7 +1585,9 @@ function ReferenceSourcePicker({
|
|
|
1503
1585
|
},
|
|
1504
1586
|
nodeRefKey(node.ref)
|
|
1505
1587
|
))
|
|
1506
|
-
) :
|
|
1588
|
+
) : view.currentEntries.length === 0 ? /* @__PURE__ */ jsx4(Feedback, { children: copy.t(
|
|
1589
|
+
hasSelectedGroup ? "referencePicker.emptyDirectory" : "referencePicker.selectGroupHint"
|
|
1590
|
+
) }) : (
|
|
1507
1591
|
// 浏览:就地递归展开树(复刻 agent 引用面板文件树交互)
|
|
1508
1592
|
view.currentEntries.map((node) => /* @__PURE__ */ jsx4(
|
|
1509
1593
|
TreeNodeRow,
|
|
@@ -1559,6 +1643,7 @@ function ReferenceSourcePicker({
|
|
|
1559
1643
|
PreviewInfoPane,
|
|
1560
1644
|
{
|
|
1561
1645
|
copy,
|
|
1646
|
+
hierarchy: view.breadcrumb,
|
|
1562
1647
|
node: view.focusedNode,
|
|
1563
1648
|
previewState: view.previewState,
|
|
1564
1649
|
sourceLabel: view.activeTabLabel
|
|
@@ -1853,10 +1938,12 @@ function FullTextTooltip({
|
|
|
1853
1938
|
}
|
|
1854
1939
|
function PreviewInfoPane({
|
|
1855
1940
|
copy,
|
|
1941
|
+
hierarchy,
|
|
1856
1942
|
node,
|
|
1857
1943
|
previewState,
|
|
1858
1944
|
sourceLabel
|
|
1859
1945
|
}) {
|
|
1946
|
+
const sizeBytes = node ? resolveReferencePreviewSizeBytes(node, previewState) : null;
|
|
1860
1947
|
return /* @__PURE__ */ jsx4("aside", { className: "flex h-full min-h-0 w-full flex-col bg-[var(--background-fronted)]", children: node ? /* @__PURE__ */ jsxs4("div", { className: "flex min-h-0 flex-1 flex-col gap-4 p-4", children: [
|
|
1861
1948
|
/* @__PURE__ */ jsx4(
|
|
1862
1949
|
WorkspaceFilePreviewSurface,
|
|
@@ -1879,7 +1966,7 @@ function PreviewInfoPane({
|
|
|
1879
1966
|
),
|
|
1880
1967
|
/* @__PURE__ */ jsxs4("div", { className: "space-y-1", children: [
|
|
1881
1968
|
/* @__PURE__ */ jsx4("p", { className: "truncate text-[15px] font-semibold", children: node.displayName }),
|
|
1882
|
-
/* @__PURE__ */ jsx4(ReferencePathText, { node })
|
|
1969
|
+
/* @__PURE__ */ jsx4(ReferencePathText, { hierarchy, node })
|
|
1883
1970
|
] }),
|
|
1884
1971
|
/* @__PURE__ */ jsxs4("dl", { className: "space-y-2 text-[13px]", children: [
|
|
1885
1972
|
/* @__PURE__ */ jsx4(InfoRow, { label: copy.t("referencePicker.previewSource"), children: /* @__PURE__ */ jsx4(
|
|
@@ -1891,12 +1978,15 @@ function PreviewInfoPane({
|
|
|
1891
1978
|
}
|
|
1892
1979
|
) }),
|
|
1893
1980
|
node.mtimeMs != null ? /* @__PURE__ */ jsx4(InfoRow, { label: copy.t("referencePicker.previewModified"), children: formatReferencePreviewDateTime(node.mtimeMs) }) : null,
|
|
1894
|
-
|
|
1981
|
+
sizeBytes != null ? /* @__PURE__ */ jsx4(InfoRow, { label: copy.t("referencePicker.previewSize"), children: formatBytes(sizeBytes) }) : null
|
|
1895
1982
|
] })
|
|
1896
1983
|
] }) : /* @__PURE__ */ jsx4(Feedback, { children: copy.t("referencePicker.emptyPreview") }) });
|
|
1897
1984
|
}
|
|
1898
|
-
function ReferencePathText({
|
|
1899
|
-
|
|
1985
|
+
function ReferencePathText({
|
|
1986
|
+
hierarchy,
|
|
1987
|
+
node
|
|
1988
|
+
}) {
|
|
1989
|
+
const pathText = getReferenceNodePathText(node, hierarchy);
|
|
1900
1990
|
const lastSlashIndex = pathText.lastIndexOf("/");
|
|
1901
1991
|
if (lastSlashIndex <= 0 || lastSlashIndex === pathText.length - 1) {
|
|
1902
1992
|
return /* @__PURE__ */ jsx4(
|
|
@@ -1920,29 +2010,8 @@ function ReferencePathText({ node }) {
|
|
|
1920
2010
|
}
|
|
1921
2011
|
);
|
|
1922
2012
|
}
|
|
1923
|
-
function getReferenceNodePathText(node) {
|
|
1924
|
-
|
|
1925
|
-
if (decodedPath) {
|
|
1926
|
-
return decodedPath;
|
|
1927
|
-
}
|
|
1928
|
-
return node.contextLabel?.trim() || node.ref.nodeId;
|
|
1929
|
-
}
|
|
1930
|
-
function decodeReferenceListFileNodeId(nodeId) {
|
|
1931
|
-
if (!nodeId.startsWith("f:")) {
|
|
1932
|
-
return null;
|
|
1933
|
-
}
|
|
1934
|
-
try {
|
|
1935
|
-
const normalized = nodeId.slice(2).replace(/-/g, "+").replace(/_/g, "/");
|
|
1936
|
-
const padded = normalized.padEnd(
|
|
1937
|
-
normalized.length + (4 - normalized.length % 4) % 4,
|
|
1938
|
-
"="
|
|
1939
|
-
);
|
|
1940
|
-
const binary = atob(padded);
|
|
1941
|
-
const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));
|
|
1942
|
-
return new TextDecoder().decode(bytes);
|
|
1943
|
-
} catch {
|
|
1944
|
-
return null;
|
|
1945
|
-
}
|
|
2013
|
+
function getReferenceNodePathText(node, hierarchy) {
|
|
2014
|
+
return formatReferenceNodePathText(node, hierarchy);
|
|
1946
2015
|
}
|
|
1947
2016
|
function InfoRow({
|
|
1948
2017
|
label,
|