@tutti-os/workspace-file-reference 0.0.13 → 0.0.14
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-7C3RZYQE.js +286 -0
- package/dist/chunk-7C3RZYQE.js.map +1 -0
- package/dist/{chunk-LS7P3J6Z.js → chunk-CD3YNHYJ.js} +433 -3
- package/dist/chunk-CD3YNHYJ.js.map +1 -0
- package/dist/contracts/index.d.ts +124 -1
- package/dist/core/index.d.ts +97 -2
- package/dist/core/index.js +31 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +31 -1
- package/dist/react/index.d.ts +75 -2
- package/dist/react/index.js +6 -2
- package/dist/referenceSourceAggregator-yyyLVOPi.d.ts +42 -0
- package/dist/ui/index.d.ts +13 -1
- package/dist/ui/index.js +981 -7
- package/dist/ui/index.js.map +1 -1
- package/package.json +5 -4
- package/dist/chunk-LS7P3J6Z.js.map +0 -1
- package/dist/chunk-PAR2R2G5.js +0 -23
- package/dist/chunk-PAR2R2G5.js.map +0 -1
package/dist/ui/index.js
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
|
+
ROOT_CHILDREN_KEY,
|
|
3
|
+
createReferenceSourcePickerController,
|
|
2
4
|
normalizeDirectoryPath,
|
|
3
5
|
useWorkspaceFileReferencePickerView
|
|
4
|
-
} from "../chunk-
|
|
5
|
-
import
|
|
6
|
+
} from "../chunk-CD3YNHYJ.js";
|
|
7
|
+
import {
|
|
8
|
+
WORKSPACE_ROOT_GROUP_NODE_ID,
|
|
9
|
+
nodeRefKey,
|
|
10
|
+
selectedReferenceToWorkspaceFileReference
|
|
11
|
+
} from "../chunk-7C3RZYQE.js";
|
|
6
12
|
|
|
7
13
|
// src/ui/internal/reference/WorkspaceFileReferencePicker.tsx
|
|
8
14
|
import { useId } from "react";
|
|
@@ -45,7 +51,7 @@ import {
|
|
|
45
51
|
} from "@tutti-os/ui-system";
|
|
46
52
|
import { AddIcon as AddLinedIcon } from "@tutti-os/ui-system/icons";
|
|
47
53
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
48
|
-
var workspaceFileReferenceTreeIndent =
|
|
54
|
+
var workspaceFileReferenceTreeIndent = 28;
|
|
49
55
|
var workspaceFileReferenceTreeCollapseDurationMs = 200;
|
|
50
56
|
function WorkspaceFileReferencePickerBrowserPane({
|
|
51
57
|
browseRootEntries,
|
|
@@ -197,7 +203,7 @@ function WorkspaceFileReferencePickerTreeEntry({
|
|
|
197
203
|
"div",
|
|
198
204
|
{
|
|
199
205
|
className: cn(
|
|
200
|
-
"
|
|
206
|
+
"flex items-center gap-2 rounded-[6px] py-1.5 pr-1 transition-colors",
|
|
201
207
|
"nodrag [-webkit-app-region:no-drag]",
|
|
202
208
|
focused || selected ? "bg-transparency-block" : "hover:bg-transparency-block"
|
|
203
209
|
),
|
|
@@ -225,11 +231,11 @@ function WorkspaceFileReferencePickerTreeEntry({
|
|
|
225
231
|
}
|
|
226
232
|
)
|
|
227
233
|
}
|
|
228
|
-
) :
|
|
234
|
+
) : null,
|
|
229
235
|
/* @__PURE__ */ jsxs(
|
|
230
236
|
"button",
|
|
231
237
|
{
|
|
232
|
-
className: "nodrag flex min-w-0 items-center gap-2 text-left [-webkit-app-region:no-drag]",
|
|
238
|
+
className: "nodrag flex min-w-0 flex-1 items-center gap-2 text-left [-webkit-app-region:no-drag]",
|
|
233
239
|
type: "button",
|
|
234
240
|
onClick: (event) => {
|
|
235
241
|
event.stopPropagation();
|
|
@@ -252,7 +258,7 @@ function WorkspaceFileReferencePickerTreeEntry({
|
|
|
252
258
|
size: "icon-sm",
|
|
253
259
|
type: "button",
|
|
254
260
|
variant: "ghost",
|
|
255
|
-
className: "nodrag [-webkit-app-region:no-drag]",
|
|
261
|
+
className: "nodrag shrink-0 [-webkit-app-region:no-drag]",
|
|
256
262
|
onClick: (event) => {
|
|
257
263
|
event.stopPropagation();
|
|
258
264
|
onFocusPath(entry.path);
|
|
@@ -670,7 +676,975 @@ function WorkspaceFileReferencePicker({
|
|
|
670
676
|
}
|
|
671
677
|
return createPortal(dialog, document.body);
|
|
672
678
|
}
|
|
679
|
+
|
|
680
|
+
// src/ui/internal/reference/ReferenceSourcePicker.tsx
|
|
681
|
+
import {
|
|
682
|
+
useEffect as useEffect3,
|
|
683
|
+
useId as useId2,
|
|
684
|
+
useRef as useRef2,
|
|
685
|
+
useState as useState3
|
|
686
|
+
} from "react";
|
|
687
|
+
import { createPortal as createPortal2 } from "react-dom";
|
|
688
|
+
import {
|
|
689
|
+
ArrowRightIcon as ArrowRightIcon2,
|
|
690
|
+
Badge as Badge2,
|
|
691
|
+
Button as Button4,
|
|
692
|
+
Card as Card2,
|
|
693
|
+
CardContent as CardContent2,
|
|
694
|
+
CardHeader as CardHeader2,
|
|
695
|
+
CardTitle as CardTitle2,
|
|
696
|
+
CheckIcon as CheckIcon2,
|
|
697
|
+
CloseIcon as CloseIcon2,
|
|
698
|
+
FileIcon as FileIcon3,
|
|
699
|
+
FolderFilledIcon as FolderFilledIcon3,
|
|
700
|
+
Input as Input2,
|
|
701
|
+
ResizableHandle,
|
|
702
|
+
ResizablePanel,
|
|
703
|
+
ResizablePanelGroup,
|
|
704
|
+
ScrollArea as ScrollArea2,
|
|
705
|
+
SearchIcon as SearchIcon2,
|
|
706
|
+
Spinner as Spinner2,
|
|
707
|
+
cn as cn3
|
|
708
|
+
} from "@tutti-os/ui-system";
|
|
709
|
+
import { AddIcon } from "@tutti-os/ui-system/icons";
|
|
710
|
+
|
|
711
|
+
// src/react/internal/reference/useReferenceSourcePickerView.ts
|
|
712
|
+
import { useCallback, useEffect as useEffect2, useMemo, useRef, useState as useState2 } from "react";
|
|
713
|
+
import { useSnapshot } from "valtio";
|
|
714
|
+
import {
|
|
715
|
+
sortWorkspaceFileEntriesForArrangeMode
|
|
716
|
+
} from "@tutti-os/workspace-file-manager/services";
|
|
717
|
+
var WORKSPACE_ROOT_GROUP_LABEL = "\u5DE5\u4F5C\u533A";
|
|
718
|
+
function useReferenceSourcePickerView({
|
|
719
|
+
aggregator,
|
|
720
|
+
workspaceId,
|
|
721
|
+
open,
|
|
722
|
+
onClose,
|
|
723
|
+
onConfirm
|
|
724
|
+
}) {
|
|
725
|
+
const readSnapshot = useSnapshot;
|
|
726
|
+
const scope = useMemo(() => ({ workspaceId }), [workspaceId]);
|
|
727
|
+
const controller = useMemo(
|
|
728
|
+
() => createReferenceSourcePickerController({ aggregator, scope }),
|
|
729
|
+
[aggregator, scope]
|
|
730
|
+
);
|
|
731
|
+
const snapshot = readSnapshot(controller.store);
|
|
732
|
+
const [breadcrumbBySource, setBreadcrumbBySource] = useState2({});
|
|
733
|
+
const [focusedNode, setFocusedNode] = useState2(null);
|
|
734
|
+
const [arrangeMode, setArrangeMode] = useState2("none");
|
|
735
|
+
const [expandedSources, setExpandedSources] = useState2({});
|
|
736
|
+
const expandSeededRef = useRef(false);
|
|
737
|
+
const sortNodes = useCallback(
|
|
738
|
+
(nodes) => {
|
|
739
|
+
if (arrangeMode === "none") {
|
|
740
|
+
return [...nodes];
|
|
741
|
+
}
|
|
742
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
743
|
+
const fileEntries = nodes.map((node) => {
|
|
744
|
+
const key = nodeRefKey(node.ref);
|
|
745
|
+
byKey.set(key, node);
|
|
746
|
+
return {
|
|
747
|
+
hasChildren: node.kind === "folder",
|
|
748
|
+
kind: node.kind === "folder" ? "directory" : "file",
|
|
749
|
+
mtimeMs: node.mtimeMs ?? null,
|
|
750
|
+
name: node.displayName,
|
|
751
|
+
path: key,
|
|
752
|
+
sizeBytes: node.sizeBytes ?? null
|
|
753
|
+
};
|
|
754
|
+
});
|
|
755
|
+
return sortWorkspaceFileEntriesForArrangeMode(fileEntries, arrangeMode).map((entry) => byKey.get(entry.path)).filter((node) => node !== void 0);
|
|
756
|
+
},
|
|
757
|
+
[arrangeMode]
|
|
758
|
+
);
|
|
759
|
+
const autoEnteredSourcesRef = useRef(/* @__PURE__ */ new Set());
|
|
760
|
+
useEffect2(() => {
|
|
761
|
+
if (!open) {
|
|
762
|
+
return;
|
|
763
|
+
}
|
|
764
|
+
controller.reset();
|
|
765
|
+
controller.open();
|
|
766
|
+
setBreadcrumbBySource({});
|
|
767
|
+
setFocusedNode(null);
|
|
768
|
+
setExpandedSources({});
|
|
769
|
+
expandSeededRef.current = false;
|
|
770
|
+
autoEnteredSourcesRef.current = /* @__PURE__ */ new Set();
|
|
771
|
+
return () => {
|
|
772
|
+
controller.close();
|
|
773
|
+
};
|
|
774
|
+
}, [controller, open]);
|
|
775
|
+
const activeSourceId = snapshot.activeSourceId;
|
|
776
|
+
const activeTab = useMemo(
|
|
777
|
+
() => snapshot.tabs.find((tab) => tab.sourceId === activeSourceId) ?? null,
|
|
778
|
+
[activeSourceId, snapshot.tabs]
|
|
779
|
+
);
|
|
780
|
+
const capabilities = activeTab?.capabilities ?? null;
|
|
781
|
+
const breadcrumb = activeSourceId ? breadcrumbBySource[activeSourceId] ?? [] : [];
|
|
782
|
+
const currentNode = breadcrumb.at(-1) ?? null;
|
|
783
|
+
const currentKey = currentNode ? nodeRefKey(currentNode.ref) : ROOT_CHILDREN_KEY;
|
|
784
|
+
const activeTabState = activeSourceId ? snapshot.bySource[activeSourceId] : void 0;
|
|
785
|
+
const isSearch = activeTabState?.mode === "search" && activeTabState.searchQuery.trim() !== "";
|
|
786
|
+
const currentChildren = activeTabState?.childrenByKey[currentKey];
|
|
787
|
+
const currentEntries = useMemo(
|
|
788
|
+
() => sortNodes(currentChildren?.entries ?? []),
|
|
789
|
+
[currentChildren?.entries, sortNodes]
|
|
790
|
+
);
|
|
791
|
+
const searchResults = useMemo(
|
|
792
|
+
() => sortNodes(activeTabState?.searchEntries ?? []),
|
|
793
|
+
[activeTabState?.searchEntries, sortNodes]
|
|
794
|
+
);
|
|
795
|
+
const sidebarGroupsBySource = useMemo(() => {
|
|
796
|
+
const result = {};
|
|
797
|
+
for (const tab of snapshot.tabs) {
|
|
798
|
+
const sourceId = tab.sourceId;
|
|
799
|
+
const provided = aggregator.getLoadedSource(sourceId)?.listSidebarGroups?.(scope);
|
|
800
|
+
if (provided && provided.length > 0) {
|
|
801
|
+
result[sourceId] = provided;
|
|
802
|
+
continue;
|
|
803
|
+
}
|
|
804
|
+
const root = snapshot.bySource[sourceId]?.childrenByKey[ROOT_CHILDREN_KEY];
|
|
805
|
+
const folders = (root?.entries ?? []).filter(
|
|
806
|
+
(node) => node.kind === "folder"
|
|
807
|
+
);
|
|
808
|
+
if (tab.capabilities.navigable) {
|
|
809
|
+
result[sourceId] = folders;
|
|
810
|
+
} else {
|
|
811
|
+
const workspaceRoot = {
|
|
812
|
+
ref: { sourceId, nodeId: WORKSPACE_ROOT_GROUP_NODE_ID },
|
|
813
|
+
kind: "folder",
|
|
814
|
+
displayName: WORKSPACE_ROOT_GROUP_LABEL
|
|
815
|
+
};
|
|
816
|
+
result[sourceId] = [workspaceRoot, ...folders];
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
return result;
|
|
820
|
+
}, [snapshot.tabs, snapshot.bySource, aggregator, scope]);
|
|
821
|
+
const sidebarGroups = activeSourceId ? sidebarGroupsBySource[activeSourceId] ?? [] : [];
|
|
822
|
+
const selectedGroupKey = currentNode != null ? nodeRefKey(currentNode.ref) : activeSourceId && !capabilities?.navigable ? nodeRefKey({
|
|
823
|
+
sourceId: activeSourceId,
|
|
824
|
+
nodeId: WORKSPACE_ROOT_GROUP_NODE_ID
|
|
825
|
+
}) : null;
|
|
826
|
+
const setActiveSource = useCallback(
|
|
827
|
+
(sourceId) => {
|
|
828
|
+
controller.setActiveSource(sourceId);
|
|
829
|
+
setFocusedNode(null);
|
|
830
|
+
},
|
|
831
|
+
[controller]
|
|
832
|
+
);
|
|
833
|
+
const enterFolder = useCallback(
|
|
834
|
+
(node) => {
|
|
835
|
+
const sourceId = node.ref.sourceId;
|
|
836
|
+
if (node.kind !== "folder" || !sourceId || node.ref.nodeId === WORKSPACE_ROOT_GROUP_NODE_ID) {
|
|
837
|
+
return;
|
|
838
|
+
}
|
|
839
|
+
controller.ensureChildren(node);
|
|
840
|
+
setBreadcrumbBySource((current) => {
|
|
841
|
+
const stack = current[sourceId] ?? [];
|
|
842
|
+
const index = stack.findIndex(
|
|
843
|
+
(item) => nodeRefKey(item.ref) === nodeRefKey(node.ref)
|
|
844
|
+
);
|
|
845
|
+
const nextStack = index >= 0 ? stack.slice(0, index + 1) : [...stack, node];
|
|
846
|
+
return { ...current, [sourceId]: nextStack };
|
|
847
|
+
});
|
|
848
|
+
setFocusedNode(null);
|
|
849
|
+
},
|
|
850
|
+
[controller]
|
|
851
|
+
);
|
|
852
|
+
useEffect2(() => {
|
|
853
|
+
if (!open || !activeSourceId || !capabilities?.navigable) {
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
856
|
+
if (autoEnteredSourcesRef.current.has(activeSourceId)) {
|
|
857
|
+
return;
|
|
858
|
+
}
|
|
859
|
+
const stack = breadcrumbBySource[activeSourceId] ?? [];
|
|
860
|
+
if (stack.length > 0) {
|
|
861
|
+
autoEnteredSourcesRef.current.add(activeSourceId);
|
|
862
|
+
return;
|
|
863
|
+
}
|
|
864
|
+
const firstGroup = sidebarGroups[0];
|
|
865
|
+
if (!firstGroup) {
|
|
866
|
+
return;
|
|
867
|
+
}
|
|
868
|
+
autoEnteredSourcesRef.current.add(activeSourceId);
|
|
869
|
+
enterFolder(firstGroup);
|
|
870
|
+
}, [
|
|
871
|
+
open,
|
|
872
|
+
activeSourceId,
|
|
873
|
+
capabilities?.navigable,
|
|
874
|
+
sidebarGroups,
|
|
875
|
+
breadcrumbBySource,
|
|
876
|
+
enterFolder
|
|
877
|
+
]);
|
|
878
|
+
useEffect2(() => {
|
|
879
|
+
if (!open || expandSeededRef.current || !activeSourceId) {
|
|
880
|
+
return;
|
|
881
|
+
}
|
|
882
|
+
expandSeededRef.current = true;
|
|
883
|
+
setExpandedSources(
|
|
884
|
+
(current) => current[activeSourceId] != null ? current : { ...current, [activeSourceId]: true }
|
|
885
|
+
);
|
|
886
|
+
}, [open, activeSourceId]);
|
|
887
|
+
const navigateToBreadcrumb = useCallback(
|
|
888
|
+
(index) => {
|
|
889
|
+
if (!activeSourceId) {
|
|
890
|
+
return;
|
|
891
|
+
}
|
|
892
|
+
setBreadcrumbBySource((current) => {
|
|
893
|
+
const stack = current[activeSourceId] ?? [];
|
|
894
|
+
return { ...current, [activeSourceId]: stack.slice(0, index + 1) };
|
|
895
|
+
});
|
|
896
|
+
const target = (breadcrumbBySource[activeSourceId] ?? [])[index] ?? null;
|
|
897
|
+
controller.ensureChildren(target);
|
|
898
|
+
setFocusedNode(null);
|
|
899
|
+
},
|
|
900
|
+
[activeSourceId, breadcrumbBySource, controller]
|
|
901
|
+
);
|
|
902
|
+
const navigateToRoot = useCallback(
|
|
903
|
+
(sourceId) => {
|
|
904
|
+
const sid = sourceId ?? activeSourceId;
|
|
905
|
+
if (!sid) {
|
|
906
|
+
return;
|
|
907
|
+
}
|
|
908
|
+
setBreadcrumbBySource((current) => ({ ...current, [sid]: [] }));
|
|
909
|
+
controller.ensureSourceRoot(sid);
|
|
910
|
+
setFocusedNode(null);
|
|
911
|
+
},
|
|
912
|
+
[activeSourceId, controller]
|
|
913
|
+
);
|
|
914
|
+
const selectGroup = useCallback(
|
|
915
|
+
(node) => {
|
|
916
|
+
const sourceId = node.ref.sourceId;
|
|
917
|
+
if (!sourceId) {
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
920
|
+
if (sourceId !== snapshot.activeSourceId) {
|
|
921
|
+
controller.setActiveSource(sourceId);
|
|
922
|
+
}
|
|
923
|
+
if (node.ref.nodeId === WORKSPACE_ROOT_GROUP_NODE_ID) {
|
|
924
|
+
navigateToRoot(sourceId);
|
|
925
|
+
return;
|
|
926
|
+
}
|
|
927
|
+
enterFolder(node);
|
|
928
|
+
},
|
|
929
|
+
[controller, snapshot.activeSourceId, enterFolder, navigateToRoot]
|
|
930
|
+
);
|
|
931
|
+
const isSourceExpanded = useCallback(
|
|
932
|
+
(sourceId) => expandedSources[sourceId] ?? false,
|
|
933
|
+
[expandedSources]
|
|
934
|
+
);
|
|
935
|
+
const toggleSourceExpanded = useCallback(
|
|
936
|
+
(sourceId) => {
|
|
937
|
+
const willExpand = !(expandedSources[sourceId] ?? false);
|
|
938
|
+
setExpandedSources((current) => ({
|
|
939
|
+
...current,
|
|
940
|
+
[sourceId]: !(current[sourceId] ?? false)
|
|
941
|
+
}));
|
|
942
|
+
if (willExpand && !aggregator.getLoadedSource(sourceId)?.listSidebarGroups) {
|
|
943
|
+
controller.ensureSourceRoot(sourceId);
|
|
944
|
+
}
|
|
945
|
+
},
|
|
946
|
+
[aggregator, controller, expandedSources]
|
|
947
|
+
);
|
|
948
|
+
const isSelected = useCallback(
|
|
949
|
+
(node) => snapshot.selection.some(
|
|
950
|
+
(item) => nodeRefKey(item.ref) === nodeRefKey(node.ref)
|
|
951
|
+
),
|
|
952
|
+
[snapshot.selection]
|
|
953
|
+
);
|
|
954
|
+
const [isConfirming, setIsConfirming] = useState2(false);
|
|
955
|
+
const confirm = useCallback(async () => {
|
|
956
|
+
if (isConfirming) {
|
|
957
|
+
return;
|
|
958
|
+
}
|
|
959
|
+
setIsConfirming(true);
|
|
960
|
+
try {
|
|
961
|
+
const selected = await controller.confirm();
|
|
962
|
+
onConfirm(selected.map(selectedReferenceToWorkspaceFileReference));
|
|
963
|
+
onClose();
|
|
964
|
+
} finally {
|
|
965
|
+
setIsConfirming(false);
|
|
966
|
+
}
|
|
967
|
+
}, [controller, isConfirming, onClose, onConfirm]);
|
|
968
|
+
return {
|
|
969
|
+
tabs: snapshot.tabs,
|
|
970
|
+
activeSourceId,
|
|
971
|
+
activeTabLabel: activeTab?.label ?? "",
|
|
972
|
+
capabilities,
|
|
973
|
+
// 内容区递归就地树:当前选中二级节点的子条目(本地根时为源根子条目)。
|
|
974
|
+
currentEntries,
|
|
975
|
+
// 搜索态:扁平搜索结果。
|
|
976
|
+
searchResults,
|
|
977
|
+
expandedKeys: activeTabState?.expandedKeys ?? {},
|
|
978
|
+
childrenByKey: activeTabState?.childrenByKey ?? {},
|
|
979
|
+
toggleNode: (node) => controller.toggleNode(node),
|
|
980
|
+
sortNodes,
|
|
981
|
+
isLoadingTabs: snapshot.isLoadingTabs,
|
|
982
|
+
breadcrumb,
|
|
983
|
+
currentNode,
|
|
984
|
+
sidebarGroups,
|
|
985
|
+
sidebarGroupsBySource,
|
|
986
|
+
isSourceExpanded,
|
|
987
|
+
toggleSourceExpanded,
|
|
988
|
+
selectedGroupKey,
|
|
989
|
+
arrangeMode,
|
|
990
|
+
setArrangeMode,
|
|
991
|
+
isSearch,
|
|
992
|
+
searchQuery: activeTabState?.searchQuery ?? "",
|
|
993
|
+
isLoading: isSearch ? activeTabState?.isSearchLoading ?? false : currentChildren?.loading ?? false,
|
|
994
|
+
hasMore: !isSearch && Boolean(currentChildren?.nextCursor),
|
|
995
|
+
focusedNode,
|
|
996
|
+
selection: snapshot.selection,
|
|
997
|
+
selectionCount: snapshot.selection.length,
|
|
998
|
+
setActiveSource,
|
|
999
|
+
enterFolder,
|
|
1000
|
+
selectGroup,
|
|
1001
|
+
navigateToBreadcrumb,
|
|
1002
|
+
navigateToRoot,
|
|
1003
|
+
setFocusedNode,
|
|
1004
|
+
setSearchQuery: (query) => controller.setSearchQuery(query),
|
|
1005
|
+
toggleSelection: (node) => controller.toggleSelection(node),
|
|
1006
|
+
loadMore: () => controller.loadMore(currentNode),
|
|
1007
|
+
isSelected,
|
|
1008
|
+
confirm,
|
|
1009
|
+
isConfirming
|
|
1010
|
+
};
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
// src/ui/internal/reference/ReferenceSourcePicker.tsx
|
|
1014
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1015
|
+
var L = {
|
|
1016
|
+
sourceColumn: "\u5206\u7C7B",
|
|
1017
|
+
selectGroupHint: "\u4ECE\u5DE6\u4FA7\u9009\u62E9\u4E00\u4E2A\u76EE\u5F55",
|
|
1018
|
+
previewSource: "\u4EA7\u51FA\u6765\u6E90",
|
|
1019
|
+
previewModified: "\u4EA7\u51FA\u65F6\u95F4",
|
|
1020
|
+
previewSize: "\u6587\u4EF6\u5927\u5C0F",
|
|
1021
|
+
previewHierarchy: "\u6240\u5C5E\u5C42\u7EA7",
|
|
1022
|
+
reference: "\u5F15\u7528",
|
|
1023
|
+
loadMore: "\u52A0\u8F7D\u66F4\u591A",
|
|
1024
|
+
emptyPreview: "\u9009\u62E9\u4E00\u4E2A\u6587\u4EF6\u67E5\u770B\u8BE6\u60C5"
|
|
1025
|
+
};
|
|
1026
|
+
function autoFitPanelWidth(groupEl, contentEl, panel, trailingPx) {
|
|
1027
|
+
if (!groupEl || !contentEl || !panel) {
|
|
1028
|
+
return;
|
|
1029
|
+
}
|
|
1030
|
+
const groupWidth = groupEl.clientWidth;
|
|
1031
|
+
if (groupWidth <= 0) {
|
|
1032
|
+
return;
|
|
1033
|
+
}
|
|
1034
|
+
const contentLeft = contentEl.getBoundingClientRect().left;
|
|
1035
|
+
const labels = contentEl.querySelectorAll(
|
|
1036
|
+
"[data-autofit-label]"
|
|
1037
|
+
);
|
|
1038
|
+
let maxRight = 0;
|
|
1039
|
+
labels.forEach((label) => {
|
|
1040
|
+
const right = label.getBoundingClientRect().left - contentLeft + label.scrollWidth;
|
|
1041
|
+
if (right > maxRight) {
|
|
1042
|
+
maxRight = right;
|
|
1043
|
+
}
|
|
1044
|
+
});
|
|
1045
|
+
if (maxRight <= 0) {
|
|
1046
|
+
return;
|
|
1047
|
+
}
|
|
1048
|
+
const naturalWidth = maxRight + trailingPx;
|
|
1049
|
+
panel.resize(Math.min(80, naturalWidth / groupWidth * 100));
|
|
1050
|
+
}
|
|
1051
|
+
function ReferenceSourcePicker({
|
|
1052
|
+
aggregator,
|
|
1053
|
+
copy,
|
|
1054
|
+
onClose,
|
|
1055
|
+
onConfirm,
|
|
1056
|
+
open,
|
|
1057
|
+
workspaceId
|
|
1058
|
+
}) {
|
|
1059
|
+
const titleId = useId2();
|
|
1060
|
+
const view = useReferenceSourcePickerView({
|
|
1061
|
+
aggregator,
|
|
1062
|
+
workspaceId,
|
|
1063
|
+
open,
|
|
1064
|
+
onClose,
|
|
1065
|
+
onConfirm
|
|
1066
|
+
});
|
|
1067
|
+
const layoutRef = useRef2(null);
|
|
1068
|
+
const sidebarContentRef = useRef2(null);
|
|
1069
|
+
const middleContentRef = useRef2(null);
|
|
1070
|
+
const sidebarPanelRef = useRef2(null);
|
|
1071
|
+
const middlePanelRef = useRef2(null);
|
|
1072
|
+
if (!open) {
|
|
1073
|
+
return null;
|
|
1074
|
+
}
|
|
1075
|
+
const hasSelectedGroup = view.selectedGroupKey != null;
|
|
1076
|
+
const fitSidebar = () => autoFitPanelWidth(
|
|
1077
|
+
layoutRef.current,
|
|
1078
|
+
sidebarContentRef.current,
|
|
1079
|
+
sidebarPanelRef.current,
|
|
1080
|
+
36
|
|
1081
|
+
);
|
|
1082
|
+
const fitMiddle = () => autoFitPanelWidth(
|
|
1083
|
+
layoutRef.current,
|
|
1084
|
+
middleContentRef.current,
|
|
1085
|
+
middlePanelRef.current,
|
|
1086
|
+
56
|
|
1087
|
+
);
|
|
1088
|
+
const dialog = /* @__PURE__ */ jsx4(
|
|
1089
|
+
"div",
|
|
1090
|
+
{
|
|
1091
|
+
className: "nodrag fixed inset-0 grid place-items-center bg-[var(--backdrop)] px-3 py-4 backdrop-blur-md [-webkit-app-region:no-drag] sm:px-6 sm:py-8",
|
|
1092
|
+
style: { zIndex: "var(--z-panel)" },
|
|
1093
|
+
onClick: onClose,
|
|
1094
|
+
children: /* @__PURE__ */ jsxs4(
|
|
1095
|
+
Card2,
|
|
1096
|
+
{
|
|
1097
|
+
"aria-labelledby": titleId,
|
|
1098
|
+
"aria-modal": "true",
|
|
1099
|
+
className: "nodrag flex h-[min(88vh,46rem)] w-full max-w-5xl flex-col gap-0 overflow-hidden border-[var(--line-1)] bg-[var(--background-fronted)] py-0 text-[var(--text-primary)] shadow-panel [-webkit-app-region:no-drag]",
|
|
1100
|
+
role: "dialog",
|
|
1101
|
+
onClick: (event) => event.stopPropagation(),
|
|
1102
|
+
children: [
|
|
1103
|
+
/* @__PURE__ */ jsx4(CardHeader2, { className: "gap-3 px-4 pt-4 pb-4 sm:px-6", children: /* @__PURE__ */ jsxs4("div", { className: "flex items-start justify-between gap-4", children: [
|
|
1104
|
+
/* @__PURE__ */ jsx4(CardTitle2, { id: titleId, children: copy.t("referencePicker.title") }),
|
|
1105
|
+
/* @__PURE__ */ jsx4(
|
|
1106
|
+
Button4,
|
|
1107
|
+
{
|
|
1108
|
+
"aria-label": copy.t("actions.cancel"),
|
|
1109
|
+
size: "icon-sm",
|
|
1110
|
+
type: "button",
|
|
1111
|
+
variant: "ghost",
|
|
1112
|
+
onClick: onClose,
|
|
1113
|
+
children: /* @__PURE__ */ jsx4(CloseIcon2, { size: 16 })
|
|
1114
|
+
}
|
|
1115
|
+
)
|
|
1116
|
+
] }) }),
|
|
1117
|
+
/* @__PURE__ */ jsx4(CardContent2, { className: "flex min-h-0 flex-1 overflow-hidden border-t border-[var(--line-1)] p-0", children: /* @__PURE__ */ jsx4("div", { ref: layoutRef, className: "flex min-h-0 flex-1", children: /* @__PURE__ */ jsxs4(
|
|
1118
|
+
ResizablePanelGroup,
|
|
1119
|
+
{
|
|
1120
|
+
className: "min-h-0 flex-1",
|
|
1121
|
+
orientation: "horizontal",
|
|
1122
|
+
defaultLayout: { sidebar: 2, middle: 5, preview: 3 },
|
|
1123
|
+
children: [
|
|
1124
|
+
/* @__PURE__ */ jsx4(
|
|
1125
|
+
ResizablePanel,
|
|
1126
|
+
{
|
|
1127
|
+
id: "sidebar",
|
|
1128
|
+
className: "min-h-0 border-r border-[var(--line-1)]",
|
|
1129
|
+
defaultSize: 20,
|
|
1130
|
+
minSize: "180px",
|
|
1131
|
+
panelRef: (handle) => {
|
|
1132
|
+
sidebarPanelRef.current = handle;
|
|
1133
|
+
},
|
|
1134
|
+
children: /* @__PURE__ */ jsx4(SourceSidebar, { contentRef: sidebarContentRef, view })
|
|
1135
|
+
}
|
|
1136
|
+
),
|
|
1137
|
+
/* @__PURE__ */ jsx4(
|
|
1138
|
+
ResizableHandle,
|
|
1139
|
+
{
|
|
1140
|
+
disableDoubleClick: true,
|
|
1141
|
+
withHandle: true,
|
|
1142
|
+
onDoubleClick: fitSidebar
|
|
1143
|
+
}
|
|
1144
|
+
),
|
|
1145
|
+
/* @__PURE__ */ jsx4(
|
|
1146
|
+
ResizablePanel,
|
|
1147
|
+
{
|
|
1148
|
+
id: "middle",
|
|
1149
|
+
className: "min-h-0",
|
|
1150
|
+
defaultSize: 50,
|
|
1151
|
+
minSize: "260px",
|
|
1152
|
+
panelRef: (handle) => {
|
|
1153
|
+
middlePanelRef.current = handle;
|
|
1154
|
+
},
|
|
1155
|
+
children: /* @__PURE__ */ jsxs4("div", { className: "flex h-full min-h-0 flex-col", children: [
|
|
1156
|
+
/* @__PURE__ */ jsx4("div", { className: "flex items-center gap-2 border-b border-[var(--line-1)] p-3", children: /* @__PURE__ */ jsxs4("div", { className: "relative flex-1", children: [
|
|
1157
|
+
/* @__PURE__ */ jsx4(SearchIcon2, { className: "pointer-events-none absolute top-1/2 left-3 size-4 -translate-y-1/2 text-[var(--text-tertiary)]" }),
|
|
1158
|
+
/* @__PURE__ */ jsx4(
|
|
1159
|
+
Input2,
|
|
1160
|
+
{
|
|
1161
|
+
className: "pl-9",
|
|
1162
|
+
placeholder: copy.t(
|
|
1163
|
+
"referencePicker.searchPlaceholder"
|
|
1164
|
+
),
|
|
1165
|
+
value: view.searchQuery,
|
|
1166
|
+
onChange: (event) => view.setSearchQuery(event.target.value)
|
|
1167
|
+
}
|
|
1168
|
+
)
|
|
1169
|
+
] }) }),
|
|
1170
|
+
/* @__PURE__ */ jsx4(ScrollArea2, { className: "min-h-0 flex-1", children: /* @__PURE__ */ jsxs4(
|
|
1171
|
+
"div",
|
|
1172
|
+
{
|
|
1173
|
+
ref: middleContentRef,
|
|
1174
|
+
className: "flex flex-col gap-[2px] p-3",
|
|
1175
|
+
children: [
|
|
1176
|
+
view.isLoading ? /* @__PURE__ */ jsx4(Feedback, { children: /* @__PURE__ */ jsx4(Spinner2, { size: 16 }) }) : view.isSearch ? (
|
|
1177
|
+
// 搜索:扁平结果
|
|
1178
|
+
view.searchResults.length === 0 ? /* @__PURE__ */ jsx4(Feedback, { children: copy.t("referencePicker.emptyDirectory") }) : view.searchResults.map((node) => /* @__PURE__ */ jsx4(
|
|
1179
|
+
SearchResultRow,
|
|
1180
|
+
{
|
|
1181
|
+
focused: isFocused(view.focusedNode, node),
|
|
1182
|
+
node,
|
|
1183
|
+
selected: view.isSelected(node),
|
|
1184
|
+
onFocus: view.setFocusedNode,
|
|
1185
|
+
onToggle: view.toggleSelection
|
|
1186
|
+
},
|
|
1187
|
+
nodeRefKey(node.ref)
|
|
1188
|
+
))
|
|
1189
|
+
) : !hasSelectedGroup ? /* @__PURE__ */ jsx4(Feedback, { children: L.selectGroupHint }) : view.currentEntries.length === 0 ? /* @__PURE__ */ jsx4(Feedback, { children: copy.t("referencePicker.emptyDirectory") }) : (
|
|
1190
|
+
// 浏览:就地递归展开树(复刻 agent 引用面板文件树交互)
|
|
1191
|
+
view.currentEntries.map((node) => /* @__PURE__ */ jsx4(
|
|
1192
|
+
TreeNodeRow,
|
|
1193
|
+
{
|
|
1194
|
+
copy,
|
|
1195
|
+
depth: 0,
|
|
1196
|
+
node,
|
|
1197
|
+
view
|
|
1198
|
+
},
|
|
1199
|
+
nodeRefKey(node.ref)
|
|
1200
|
+
))
|
|
1201
|
+
),
|
|
1202
|
+
view.hasMore && hasSelectedGroup && !view.isSearch ? /* @__PURE__ */ jsx4(
|
|
1203
|
+
Button4,
|
|
1204
|
+
{
|
|
1205
|
+
className: "mt-1 w-full",
|
|
1206
|
+
size: "sm",
|
|
1207
|
+
type: "button",
|
|
1208
|
+
variant: "ghost",
|
|
1209
|
+
onClick: view.loadMore,
|
|
1210
|
+
children: L.loadMore
|
|
1211
|
+
}
|
|
1212
|
+
) : null
|
|
1213
|
+
]
|
|
1214
|
+
}
|
|
1215
|
+
) })
|
|
1216
|
+
] })
|
|
1217
|
+
}
|
|
1218
|
+
),
|
|
1219
|
+
/* @__PURE__ */ jsx4(
|
|
1220
|
+
ResizableHandle,
|
|
1221
|
+
{
|
|
1222
|
+
disableDoubleClick: true,
|
|
1223
|
+
withHandle: true,
|
|
1224
|
+
onDoubleClick: fitMiddle
|
|
1225
|
+
}
|
|
1226
|
+
),
|
|
1227
|
+
/* @__PURE__ */ jsx4(
|
|
1228
|
+
ResizablePanel,
|
|
1229
|
+
{
|
|
1230
|
+
id: "preview",
|
|
1231
|
+
className: "min-h-0 border-l border-[var(--line-1)]",
|
|
1232
|
+
defaultSize: 30,
|
|
1233
|
+
minSize: "200px",
|
|
1234
|
+
children: /* @__PURE__ */ jsx4(
|
|
1235
|
+
PreviewInfoPane,
|
|
1236
|
+
{
|
|
1237
|
+
node: view.focusedNode,
|
|
1238
|
+
sourceLabel: view.activeTabLabel,
|
|
1239
|
+
hierarchy: view.breadcrumb,
|
|
1240
|
+
onReference: view.toggleSelection,
|
|
1241
|
+
referenced: view.focusedNode ? view.isSelected(view.focusedNode) : false
|
|
1242
|
+
}
|
|
1243
|
+
)
|
|
1244
|
+
}
|
|
1245
|
+
)
|
|
1246
|
+
]
|
|
1247
|
+
}
|
|
1248
|
+
) }) }),
|
|
1249
|
+
/* @__PURE__ */ jsx4(
|
|
1250
|
+
Footer,
|
|
1251
|
+
{
|
|
1252
|
+
cancelLabel: copy.t("actions.cancel"),
|
|
1253
|
+
confirmLabel: copy.t("referencePicker.confirm"),
|
|
1254
|
+
countLabel: copy.t("referencePicker.selectedCount", {
|
|
1255
|
+
count: view.selectionCount
|
|
1256
|
+
}),
|
|
1257
|
+
disabled: view.selectionCount === 0,
|
|
1258
|
+
loading: view.isConfirming,
|
|
1259
|
+
selection: view.selection,
|
|
1260
|
+
onClose,
|
|
1261
|
+
onConfirm: () => void view.confirm()
|
|
1262
|
+
}
|
|
1263
|
+
)
|
|
1264
|
+
]
|
|
1265
|
+
}
|
|
1266
|
+
)
|
|
1267
|
+
}
|
|
1268
|
+
);
|
|
1269
|
+
if (typeof document === "undefined") {
|
|
1270
|
+
return dialog;
|
|
1271
|
+
}
|
|
1272
|
+
return createPortal2(dialog, document.body);
|
|
1273
|
+
}
|
|
1274
|
+
function SourceSidebar({
|
|
1275
|
+
view,
|
|
1276
|
+
contentRef
|
|
1277
|
+
}) {
|
|
1278
|
+
return /* @__PURE__ */ jsx4(ScrollArea2, { className: "h-full min-h-0 w-full", children: /* @__PURE__ */ jsxs4("div", { ref: contentRef, className: "flex flex-col gap-0.5 p-2", children: [
|
|
1279
|
+
/* @__PURE__ */ jsx4("p", { className: "px-2 py-1 text-[11px] font-semibold text-[var(--text-tertiary)]", children: L.sourceColumn }),
|
|
1280
|
+
view.tabs.map((tab) => {
|
|
1281
|
+
const active = tab.sourceId === view.activeSourceId;
|
|
1282
|
+
const expanded = view.isSourceExpanded(tab.sourceId);
|
|
1283
|
+
const groups = view.sidebarGroupsBySource[tab.sourceId] ?? [];
|
|
1284
|
+
return /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-0.5", children: [
|
|
1285
|
+
/* @__PURE__ */ jsxs4(
|
|
1286
|
+
"button",
|
|
1287
|
+
{
|
|
1288
|
+
"aria-expanded": expanded,
|
|
1289
|
+
className: cn3(
|
|
1290
|
+
"flex items-center gap-1.5 rounded-[6px] px-2 py-1.5 text-left text-[13px] font-semibold transition-colors hover:bg-transparency-block",
|
|
1291
|
+
active ? "text-[var(--text-primary)]" : "text-[var(--text-secondary)]"
|
|
1292
|
+
),
|
|
1293
|
+
type: "button",
|
|
1294
|
+
onClick: () => view.toggleSourceExpanded(tab.sourceId),
|
|
1295
|
+
children: [
|
|
1296
|
+
/* @__PURE__ */ jsx4(
|
|
1297
|
+
ArrowRightIcon2,
|
|
1298
|
+
{
|
|
1299
|
+
className: cn3(
|
|
1300
|
+
"size-3 shrink-0 text-[var(--text-tertiary)] transition-transform",
|
|
1301
|
+
expanded && "rotate-90"
|
|
1302
|
+
)
|
|
1303
|
+
}
|
|
1304
|
+
),
|
|
1305
|
+
/* @__PURE__ */ jsx4("span", { className: "truncate", "data-autofit-label": true, children: tab.label })
|
|
1306
|
+
]
|
|
1307
|
+
}
|
|
1308
|
+
),
|
|
1309
|
+
expanded ? /* @__PURE__ */ jsx4("div", { className: "flex flex-col gap-0.5", children: groups.length === 0 ? /* @__PURE__ */ jsx4("p", { className: "px-2 py-1.5 pl-7 text-[12px] text-[var(--text-tertiary)]", children: view.isLoadingTabs ? "\u2026" : "" }) : groups.map((group) => {
|
|
1310
|
+
const key = nodeRefKey(group.ref);
|
|
1311
|
+
const selected = key === view.selectedGroupKey;
|
|
1312
|
+
return /* @__PURE__ */ jsxs4(
|
|
1313
|
+
"button",
|
|
1314
|
+
{
|
|
1315
|
+
className: cn3(
|
|
1316
|
+
"flex items-center gap-2 rounded-[6px] py-1.5 pr-2 pl-7 text-left text-[13px] transition-colors hover:bg-transparency-block",
|
|
1317
|
+
selected ? "bg-transparency-block text-[var(--text-primary)]" : "text-[var(--text-secondary)]"
|
|
1318
|
+
),
|
|
1319
|
+
type: "button",
|
|
1320
|
+
onClick: () => view.selectGroup(group),
|
|
1321
|
+
children: [
|
|
1322
|
+
group.iconUrl ? /* @__PURE__ */ jsx4(
|
|
1323
|
+
"img",
|
|
1324
|
+
{
|
|
1325
|
+
alt: "",
|
|
1326
|
+
className: "size-4 shrink-0 rounded-[3px] object-cover",
|
|
1327
|
+
src: group.iconUrl
|
|
1328
|
+
}
|
|
1329
|
+
) : /* @__PURE__ */ jsx4(FolderFilledIcon3, { className: "size-4 shrink-0 text-[var(--rich-text-folder)]" }),
|
|
1330
|
+
/* @__PURE__ */ jsx4(
|
|
1331
|
+
"span",
|
|
1332
|
+
{
|
|
1333
|
+
className: "min-w-0 flex-1 truncate",
|
|
1334
|
+
"data-autofit-label": true,
|
|
1335
|
+
children: group.displayName
|
|
1336
|
+
}
|
|
1337
|
+
),
|
|
1338
|
+
group.childCount != null ? /* @__PURE__ */ jsx4("span", { className: "shrink-0 text-[11px] text-[var(--text-tertiary)]", children: group.childCount }) : null
|
|
1339
|
+
]
|
|
1340
|
+
},
|
|
1341
|
+
key
|
|
1342
|
+
);
|
|
1343
|
+
}) }) : null
|
|
1344
|
+
] }, tab.sourceId);
|
|
1345
|
+
})
|
|
1346
|
+
] }) });
|
|
1347
|
+
}
|
|
1348
|
+
function SearchResultRow({
|
|
1349
|
+
node,
|
|
1350
|
+
focused,
|
|
1351
|
+
selected,
|
|
1352
|
+
onFocus,
|
|
1353
|
+
onToggle
|
|
1354
|
+
}) {
|
|
1355
|
+
const isFolder = node.kind === "folder";
|
|
1356
|
+
return /* @__PURE__ */ jsxs4(
|
|
1357
|
+
"div",
|
|
1358
|
+
{
|
|
1359
|
+
className: cn3(
|
|
1360
|
+
"grid grid-cols-[minmax(0,1fr)_auto] items-center gap-3 rounded-[6px] border py-2.5 pr-1 pl-3 transition-colors",
|
|
1361
|
+
focused || selected ? "border-border bg-transparency-block" : "border-transparent bg-transparent hover:border-border/70 hover:bg-transparency-block"
|
|
1362
|
+
),
|
|
1363
|
+
children: [
|
|
1364
|
+
/* @__PURE__ */ jsxs4(
|
|
1365
|
+
"button",
|
|
1366
|
+
{
|
|
1367
|
+
className: "flex min-w-0 items-center gap-3 text-left",
|
|
1368
|
+
type: "button",
|
|
1369
|
+
onClick: () => onFocus(node),
|
|
1370
|
+
children: [
|
|
1371
|
+
/* @__PURE__ */ jsx4("span", { className: "grid size-9 shrink-0 place-items-center rounded-lg bg-[var(--transparency-block)] text-[var(--text-tertiary)]", children: isFolder ? /* @__PURE__ */ jsx4(FolderFilledIcon3, { className: "size-4 text-[var(--rich-text-folder)]" }) : /* @__PURE__ */ jsx4(FileIcon3, { className: "size-4 text-[var(--text-tertiary)]" }) }),
|
|
1372
|
+
/* @__PURE__ */ jsxs4("span", { className: "min-w-0", children: [
|
|
1373
|
+
/* @__PURE__ */ jsx4("span", { className: "block truncate text-[13px] font-medium text-[var(--text-primary)]", children: node.displayName }),
|
|
1374
|
+
/* @__PURE__ */ jsx4("span", { className: "block truncate text-[11px] text-[var(--text-secondary)]", children: node.ref.nodeId })
|
|
1375
|
+
] })
|
|
1376
|
+
]
|
|
1377
|
+
}
|
|
1378
|
+
),
|
|
1379
|
+
/* @__PURE__ */ jsx4(
|
|
1380
|
+
Button4,
|
|
1381
|
+
{
|
|
1382
|
+
"aria-label": node.displayName,
|
|
1383
|
+
"aria-pressed": selected,
|
|
1384
|
+
size: "icon-sm",
|
|
1385
|
+
type: "button",
|
|
1386
|
+
variant: "ghost",
|
|
1387
|
+
onClick: () => {
|
|
1388
|
+
onFocus(node);
|
|
1389
|
+
onToggle(node);
|
|
1390
|
+
},
|
|
1391
|
+
children: selected ? /* @__PURE__ */ jsx4(CheckIcon2, { size: 14 }) : /* @__PURE__ */ jsx4(AddIcon, { className: "text-[var(--text-secondary)]", size: 16 })
|
|
1392
|
+
}
|
|
1393
|
+
)
|
|
1394
|
+
]
|
|
1395
|
+
}
|
|
1396
|
+
);
|
|
1397
|
+
}
|
|
1398
|
+
function PreviewInfoPane({
|
|
1399
|
+
node,
|
|
1400
|
+
sourceLabel,
|
|
1401
|
+
hierarchy,
|
|
1402
|
+
referenced,
|
|
1403
|
+
onReference
|
|
1404
|
+
}) {
|
|
1405
|
+
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: [
|
|
1406
|
+
/* @__PURE__ */ jsx4("div", { className: "grid aspect-[3/2] w-full place-items-center rounded-[8px] border border-[var(--line-2,var(--border-2))] bg-[var(--transparency-block)]", children: /* @__PURE__ */ jsx4(FileIcon3, { className: "size-9 text-[var(--text-tertiary)]" }) }),
|
|
1407
|
+
/* @__PURE__ */ jsx4("p", { className: "truncate text-[15px] font-semibold", children: node.displayName }),
|
|
1408
|
+
/* @__PURE__ */ jsxs4("dl", { className: "space-y-2 text-[13px]", children: [
|
|
1409
|
+
/* @__PURE__ */ jsx4(InfoRow, { label: L.previewSource, children: /* @__PURE__ */ jsx4(Badge2, { variant: "secondary", children: sourceLabel }) }),
|
|
1410
|
+
node.mtimeMs != null ? /* @__PURE__ */ jsx4(InfoRow, { label: L.previewModified, children: formatDateTime(node.mtimeMs) }) : null,
|
|
1411
|
+
node.sizeBytes != null ? /* @__PURE__ */ jsx4(InfoRow, { label: L.previewSize, children: formatBytes(node.sizeBytes) }) : null
|
|
1412
|
+
] }),
|
|
1413
|
+
hierarchy.length > 0 ? /* @__PURE__ */ jsxs4("div", { className: "space-y-1.5", children: [
|
|
1414
|
+
/* @__PURE__ */ jsx4("p", { className: "text-[11px] font-semibold text-[var(--text-tertiary)]", children: L.previewHierarchy }),
|
|
1415
|
+
/* @__PURE__ */ jsx4("div", { className: "flex flex-wrap gap-1", children: hierarchy.map((crumb) => /* @__PURE__ */ jsx4(Badge2, { variant: "secondary", children: crumb.displayName }, nodeRefKey(crumb.ref))) })
|
|
1416
|
+
] }) : null,
|
|
1417
|
+
/* @__PURE__ */ jsx4("div", { className: "mt-auto flex justify-end", children: /* @__PURE__ */ jsx4(
|
|
1418
|
+
Button4,
|
|
1419
|
+
{
|
|
1420
|
+
type: "button",
|
|
1421
|
+
variant: referenced ? "secondary" : void 0,
|
|
1422
|
+
onClick: () => onReference(node),
|
|
1423
|
+
children: L.reference
|
|
1424
|
+
}
|
|
1425
|
+
) })
|
|
1426
|
+
] }) : /* @__PURE__ */ jsx4(Feedback, { children: L.emptyPreview }) });
|
|
1427
|
+
}
|
|
1428
|
+
function InfoRow({
|
|
1429
|
+
label,
|
|
1430
|
+
children
|
|
1431
|
+
}) {
|
|
1432
|
+
return /* @__PURE__ */ jsxs4("div", { className: "flex items-center justify-between gap-3", children: [
|
|
1433
|
+
/* @__PURE__ */ jsx4("dt", { className: "text-[var(--text-secondary)]", children: label }),
|
|
1434
|
+
/* @__PURE__ */ jsx4("dd", { className: "min-w-0 truncate text-right text-[var(--text-primary)]", children })
|
|
1435
|
+
] });
|
|
1436
|
+
}
|
|
1437
|
+
function Footer({
|
|
1438
|
+
cancelLabel,
|
|
1439
|
+
confirmLabel,
|
|
1440
|
+
countLabel,
|
|
1441
|
+
disabled,
|
|
1442
|
+
loading = false,
|
|
1443
|
+
selection,
|
|
1444
|
+
onClose,
|
|
1445
|
+
onConfirm
|
|
1446
|
+
}) {
|
|
1447
|
+
return /* @__PURE__ */ jsxs4("div", { className: "flex items-center justify-between gap-3 border-t border-[var(--line-1)] px-4 py-3 sm:px-6", children: [
|
|
1448
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex min-w-0 items-center gap-2", children: [
|
|
1449
|
+
/* @__PURE__ */ jsx4("span", { className: "text-[13px] text-[var(--text-secondary)]", children: countLabel }),
|
|
1450
|
+
selection.slice(0, 2).map((node) => /* @__PURE__ */ jsx4(Badge2, { variant: "secondary", children: /* @__PURE__ */ jsx4("span", { className: "truncate", children: node.displayName }) }, nodeRefKey(node.ref))),
|
|
1451
|
+
selection.length > 2 ? /* @__PURE__ */ jsxs4(Badge2, { variant: "secondary", children: [
|
|
1452
|
+
"+",
|
|
1453
|
+
selection.length - 2
|
|
1454
|
+
] }) : null
|
|
1455
|
+
] }),
|
|
1456
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2", children: [
|
|
1457
|
+
/* @__PURE__ */ jsx4(Button4, { type: "button", variant: "secondary", onClick: onClose, children: cancelLabel }),
|
|
1458
|
+
/* @__PURE__ */ jsxs4(
|
|
1459
|
+
Button4,
|
|
1460
|
+
{
|
|
1461
|
+
disabled: disabled || loading,
|
|
1462
|
+
type: "button",
|
|
1463
|
+
onClick: onConfirm,
|
|
1464
|
+
children: [
|
|
1465
|
+
loading ? /* @__PURE__ */ jsx4(Spinner2, { className: "text-current", size: 14 }) : null,
|
|
1466
|
+
confirmLabel
|
|
1467
|
+
]
|
|
1468
|
+
}
|
|
1469
|
+
)
|
|
1470
|
+
] })
|
|
1471
|
+
] });
|
|
1472
|
+
}
|
|
1473
|
+
function Feedback({ children }) {
|
|
1474
|
+
return /* @__PURE__ */ jsx4("div", { className: "grid min-h-0 flex-1 place-items-center px-4 py-8 text-center text-[13px] text-[var(--text-secondary)]", children });
|
|
1475
|
+
}
|
|
1476
|
+
var TREE_INDENT = 28;
|
|
1477
|
+
var TREE_COLLAPSE_DURATION_MS = 200;
|
|
1478
|
+
function isFocused(focused, node) {
|
|
1479
|
+
return focused ? nodeRefKey(focused.ref) === nodeRefKey(node.ref) : false;
|
|
1480
|
+
}
|
|
1481
|
+
function TreeNodeRow({
|
|
1482
|
+
node,
|
|
1483
|
+
depth,
|
|
1484
|
+
view,
|
|
1485
|
+
copy
|
|
1486
|
+
}) {
|
|
1487
|
+
const key = nodeRefKey(node.ref);
|
|
1488
|
+
const isFolder = node.kind === "folder";
|
|
1489
|
+
const expanded = view.expandedKeys[key] ?? false;
|
|
1490
|
+
const childState = view.childrenByKey[key];
|
|
1491
|
+
const childEntries = view.sortNodes(childState?.entries ?? []);
|
|
1492
|
+
const selected = view.isSelected(node);
|
|
1493
|
+
const focused = isFocused(view.focusedNode, node);
|
|
1494
|
+
const [shouldRenderChildContent, setShouldRenderChildContent] = useState3(expanded);
|
|
1495
|
+
useEffect3(() => {
|
|
1496
|
+
if (expanded) {
|
|
1497
|
+
setShouldRenderChildContent(true);
|
|
1498
|
+
return;
|
|
1499
|
+
}
|
|
1500
|
+
const timeoutId = window.setTimeout(() => {
|
|
1501
|
+
setShouldRenderChildContent(false);
|
|
1502
|
+
}, TREE_COLLAPSE_DURATION_MS);
|
|
1503
|
+
return () => window.clearTimeout(timeoutId);
|
|
1504
|
+
}, [expanded]);
|
|
1505
|
+
const shouldBuildChildContent = expanded || shouldRenderChildContent;
|
|
1506
|
+
const childContent = shouldBuildChildContent ? childState?.loading ? /* @__PURE__ */ jsxs4(
|
|
1507
|
+
"div",
|
|
1508
|
+
{
|
|
1509
|
+
className: "flex items-center gap-2 px-2 py-2 text-[11px] text-[var(--text-secondary)]",
|
|
1510
|
+
style: { paddingLeft: `${(depth + 1) * TREE_INDENT + 12}px` },
|
|
1511
|
+
children: [
|
|
1512
|
+
/* @__PURE__ */ jsx4(Spinner2, { className: "text-[var(--text-secondary)]", size: 14 }),
|
|
1513
|
+
/* @__PURE__ */ jsx4("span", { children: copy.t("referencePicker.loading") })
|
|
1514
|
+
]
|
|
1515
|
+
}
|
|
1516
|
+
) : childEntries.length > 0 ? /* @__PURE__ */ jsx4("div", { className: "space-y-0.5", children: childEntries.map((child) => /* @__PURE__ */ jsx4(
|
|
1517
|
+
TreeNodeRow,
|
|
1518
|
+
{
|
|
1519
|
+
copy,
|
|
1520
|
+
depth: depth + 1,
|
|
1521
|
+
node: child,
|
|
1522
|
+
view
|
|
1523
|
+
},
|
|
1524
|
+
nodeRefKey(child.ref)
|
|
1525
|
+
)) }) : childState?.loaded ? /* @__PURE__ */ jsx4(
|
|
1526
|
+
"div",
|
|
1527
|
+
{
|
|
1528
|
+
className: "px-2 py-2 text-[11px] text-[var(--text-secondary)]",
|
|
1529
|
+
style: { paddingLeft: `${(depth + 1) * TREE_INDENT + 12}px` },
|
|
1530
|
+
children: copy.t("referencePicker.emptyDirectory")
|
|
1531
|
+
}
|
|
1532
|
+
) : null : null;
|
|
1533
|
+
return /* @__PURE__ */ jsxs4("div", { children: [
|
|
1534
|
+
/* @__PURE__ */ jsxs4(
|
|
1535
|
+
"div",
|
|
1536
|
+
{
|
|
1537
|
+
className: cn3(
|
|
1538
|
+
"flex items-center gap-2 rounded-[6px] py-1.5 pr-1 transition-colors",
|
|
1539
|
+
focused || selected ? "bg-transparency-block" : "hover:bg-transparency-block"
|
|
1540
|
+
),
|
|
1541
|
+
style: { paddingLeft: `${depth * TREE_INDENT + 8}px` },
|
|
1542
|
+
children: [
|
|
1543
|
+
isFolder ? /* @__PURE__ */ jsx4(
|
|
1544
|
+
"button",
|
|
1545
|
+
{
|
|
1546
|
+
"aria-label": node.displayName,
|
|
1547
|
+
className: "grid size-5 shrink-0 place-items-center rounded-sm text-[var(--text-secondary)] hover:bg-[var(--transparency-hover)]",
|
|
1548
|
+
type: "button",
|
|
1549
|
+
onClick: () => view.toggleNode(node),
|
|
1550
|
+
children: /* @__PURE__ */ jsx4(
|
|
1551
|
+
ArrowRightIcon2,
|
|
1552
|
+
{
|
|
1553
|
+
className: cn3(
|
|
1554
|
+
"size-3.5 transition-transform",
|
|
1555
|
+
expanded && "rotate-90"
|
|
1556
|
+
)
|
|
1557
|
+
}
|
|
1558
|
+
)
|
|
1559
|
+
}
|
|
1560
|
+
) : null,
|
|
1561
|
+
/* @__PURE__ */ jsxs4(
|
|
1562
|
+
"button",
|
|
1563
|
+
{
|
|
1564
|
+
className: "flex min-w-0 flex-1 items-center gap-2 text-left",
|
|
1565
|
+
type: "button",
|
|
1566
|
+
onClick: () => {
|
|
1567
|
+
view.setFocusedNode(node);
|
|
1568
|
+
if (isFolder) {
|
|
1569
|
+
view.toggleNode(node);
|
|
1570
|
+
}
|
|
1571
|
+
},
|
|
1572
|
+
children: [
|
|
1573
|
+
isFolder ? /* @__PURE__ */ jsx4(FolderFilledIcon3, { className: "size-4 shrink-0 text-[var(--rich-text-folder)]" }) : /* @__PURE__ */ jsx4(FileIcon3, { className: "size-4 shrink-0 text-[var(--text-tertiary)]" }),
|
|
1574
|
+
/* @__PURE__ */ jsx4(
|
|
1575
|
+
"span",
|
|
1576
|
+
{
|
|
1577
|
+
className: "truncate text-[13px] text-[var(--text-primary)]",
|
|
1578
|
+
"data-autofit-label": true,
|
|
1579
|
+
children: node.displayName
|
|
1580
|
+
}
|
|
1581
|
+
)
|
|
1582
|
+
]
|
|
1583
|
+
}
|
|
1584
|
+
),
|
|
1585
|
+
/* @__PURE__ */ jsx4(
|
|
1586
|
+
Button4,
|
|
1587
|
+
{
|
|
1588
|
+
"aria-label": node.displayName,
|
|
1589
|
+
"aria-pressed": selected,
|
|
1590
|
+
className: "shrink-0",
|
|
1591
|
+
size: "icon-sm",
|
|
1592
|
+
type: "button",
|
|
1593
|
+
variant: "ghost",
|
|
1594
|
+
onClick: () => {
|
|
1595
|
+
view.setFocusedNode(node);
|
|
1596
|
+
view.toggleSelection(node);
|
|
1597
|
+
},
|
|
1598
|
+
children: selected ? /* @__PURE__ */ jsx4(CheckIcon2, { size: 14 }) : /* @__PURE__ */ jsx4(AddIcon, { className: "text-[var(--text-secondary)]", size: 16 })
|
|
1599
|
+
}
|
|
1600
|
+
)
|
|
1601
|
+
]
|
|
1602
|
+
}
|
|
1603
|
+
),
|
|
1604
|
+
isFolder ? /* @__PURE__ */ jsx4(
|
|
1605
|
+
"div",
|
|
1606
|
+
{
|
|
1607
|
+
className: cn3(
|
|
1608
|
+
"grid transition-[grid-template-rows] duration-200 ease-out motion-reduce:transition-none",
|
|
1609
|
+
expanded ? "grid-rows-[1fr]" : "grid-rows-[0fr]",
|
|
1610
|
+
childContent && "mt-[2px]"
|
|
1611
|
+
),
|
|
1612
|
+
children: /* @__PURE__ */ jsx4(
|
|
1613
|
+
"div",
|
|
1614
|
+
{
|
|
1615
|
+
"aria-hidden": expanded ? void 0 : "true",
|
|
1616
|
+
className: cn3(
|
|
1617
|
+
"min-h-0 overflow-hidden transition-[opacity,transform] duration-200 ease-out motion-reduce:transition-none",
|
|
1618
|
+
expanded ? "translate-y-0 opacity-100" : "-translate-y-1 opacity-0"
|
|
1619
|
+
),
|
|
1620
|
+
inert: expanded ? void 0 : true,
|
|
1621
|
+
children: childContent
|
|
1622
|
+
}
|
|
1623
|
+
)
|
|
1624
|
+
}
|
|
1625
|
+
) : null
|
|
1626
|
+
] });
|
|
1627
|
+
}
|
|
1628
|
+
function formatDateTime(ms) {
|
|
1629
|
+
const date = new Date(ms);
|
|
1630
|
+
const pad = (value) => String(value).padStart(2, "0");
|
|
1631
|
+
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}`;
|
|
1632
|
+
}
|
|
1633
|
+
function formatBytes(bytes) {
|
|
1634
|
+
if (bytes < 1024) {
|
|
1635
|
+
return `${bytes} B`;
|
|
1636
|
+
}
|
|
1637
|
+
const units = ["KB", "MB", "GB"];
|
|
1638
|
+
let value = bytes / 1024;
|
|
1639
|
+
let unitIndex = 0;
|
|
1640
|
+
while (value >= 1024 && unitIndex < units.length - 1) {
|
|
1641
|
+
value /= 1024;
|
|
1642
|
+
unitIndex += 1;
|
|
1643
|
+
}
|
|
1644
|
+
return `${value.toFixed(1)} ${units[unitIndex]}`;
|
|
1645
|
+
}
|
|
673
1646
|
export {
|
|
1647
|
+
ReferenceSourcePicker,
|
|
674
1648
|
WorkspaceFileReferencePicker
|
|
675
1649
|
};
|
|
676
1650
|
//# sourceMappingURL=index.js.map
|