@tutti-os/workspace-file-reference 0.0.12 → 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/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-LS7P3J6Z.js";
5
- import "../chunk-PAR2R2G5.js";
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 = 24;
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
- "grid grid-cols-[auto_minmax(0,1fr)_auto] items-center gap-2 rounded-[6px] py-1.5 pr-1 pl-2 transition-colors",
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
- ) : /* @__PURE__ */ jsx("span", { className: "block size-5 shrink-0" }),
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