"use client"; import type { DragEvent, MouseEvent } from "react"; import { TreeShellHost, type FileTreeShellDeleteSelectionPayload, type FileTreeShellExternalDropPayload, type FileTreeShellInternalDropPayload, type TreeShellMutationPayload, type TreeShellPickerCommand, type TreeRendererFamily, } from "@/components/sidebar/tree-shell-host"; import type { KernelFileTreeProjectionItem } from "@/lib/kernel-file-tree"; import type { SidebarTreeNode } from "@/lib/kernel-sidebar"; import type { FileTreeRow } from "@/lib/file-tree/types"; import type { PageTreeProjectionItem } from "@/lib/tree-projection"; import { cn } from "@/lib/utils"; export type { TreeRendererFamily } from "@/components/sidebar/tree-shell-host"; type SidebarPageTreeSurfaceProps = { mode: "page"; rendererFamily?: TreeRendererFamily; workspaceId: string | null; treeShellEnabled?: boolean; rows?: PageTreeProjectionItem[]; treeShellRows?: PageTreeProjectionItem[]; expanded: Set; activeId: string; focusedDocumentId?: string | null; className?: string; onToggleExpand: (id: string) => void; onMove: (nodeId: string, parentId: string | null, index: number) => void; onCreateChild: (parentId: string | null) => void; onContextMenu: (event: MouseEvent, node: SidebarTreeNode) => void; onNavigate?: (documentId: string) => void; onPageContextMenu?: (payload: { documentId: string; x: number; y: number }) => void; onPageExpandChange?: (payload: { documentId: string | null; expanded: boolean }) => void; onPageFocusChange?: (payload: { documentId: string | null }) => void; onTreeMutation?: (payload: TreeShellMutationPayload) => void; }; type SidebarFileTreeSurfaceProps = { mode: "filetree"; rendererFamily?: TreeRendererFamily; workspaceId: string | null; treeShellEnabled?: boolean; rows?: FileTreeRow[]; treeShellItems?: KernelFileTreeProjectionItem[]; activeId: string; className?: string; onRowClick: (row: FileTreeRow, event: MouseEvent) => void; onRowDoubleClick: (row: FileTreeRow, event: MouseEvent) => void; onRowContextMenu: (row: FileTreeRow, event: MouseEvent) => void; onRowDragStart?: (row: FileTreeRow, event: DragEvent) => void; onToggleExpand: (docId: string) => void; onToggleAssetFolderExpand?: (assetId: string) => void; onCreateChild: (parentId: string | null) => void; onBlankMouseDown?: (event: MouseEvent) => void; onDropFiles?: (payload: FileTreeShellExternalDropPayload) => void; onInternalDrop?: (payload: FileTreeShellInternalDropPayload) => void; onNavigate?: (documentId: string) => void; onFileTreeContextMenu?: (payload: { documentId: string | null; assetId: string | null; rowId: string | null; rowKind: string | null; x: number; y: number; }) => void; onFileTreeSelectionChange?: (payload: { selectedRowIds: string[]; anchorRowId: string | null; focusedRowId: string | null; }) => void; onFileTreeDeleteSelection?: (payload: FileTreeShellDeleteSelectionPayload) => void; onAssetOpen?: (payload: { assetId: string; documentId: string | null }) => void; onTreeMutation?: (payload: TreeShellMutationPayload) => void; }; export type SidebarTreeSurfaceProps = | SidebarPageTreeSurfaceProps | SidebarFileTreeSurfaceProps; export type TreePickerSurfaceItem = | { kind: "root"; id: null; title: string; subtitle?: string } | { kind: "doc"; id: string; title: string; subtitle?: string; depth?: number }; type TreePickerSurfaceProps = { rendererFamily?: TreeRendererFamily; workspaceId: string | null; treeShellEnabled?: boolean; activeDocumentId?: string | null; activePickerItemKey?: string | null; allowRootPick?: boolean; excludeIds?: string[]; pickerCommand?: TreeShellPickerCommand | null; treeShellItems?: TreePickerSurfaceItem[]; items: TreePickerSurfaceItem[]; highlighted: number; className?: string; emptyText?: string; onHighlight: (index: number) => void; onPick: (targetId: string | null) => void; onPickerFocusChange?: (payload: { itemKey: string | null; documentId: string | null }) => void; }; export function SidebarTreeSurface(props: SidebarTreeSurfaceProps) { const surfaceTestId = props.mode === "page" ? "sidebar-page-tree-shell" : "sidebar-file-tree-shell"; const rendererFamily = props.rendererFamily ?? "react"; const pageTreeFallback = (
页面树旧 React renderer 已退出;请使用 Rust tree shell 宿主。
); const fileTreeFallback = (
文件树旧 React renderer 已退出;请使用 Rust tree shell 宿主。
); const fallbackContent = props.mode === "page" ? ( pageTreeFallback ) : ( fileTreeFallback ); return ( {fallbackContent} ); } export function TreePickerSurface({ rendererFamily = "react", workspaceId, treeShellEnabled = true, activeDocumentId = null, activePickerItemKey = null, allowRootPick = false, excludeIds = [], pickerCommand = null, treeShellItems, items, highlighted, className, emptyText = "没有匹配结果", onHighlight, onPick, onPickerFocusChange, }: TreePickerSurfaceProps) { const hasItems = items.length > 0; const effectiveTreeShellItems = rendererFamily === "rust_family" ? (treeShellItems ?? items) : treeShellItems; return ( {!hasItems ? (
{emptyText}
) : ( items.map((item, index) => { const isRoot = item.kind === "root"; return ( ); }) )}
); }