@parallelworks/ui 0.19.0 → 0.19.1

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.
Files changed (217) hide show
  1. package/dist/{chunks/memoryStore-CC91S5LP.js → ai/adapter/memoryStore.js} +2 -2
  2. package/dist/ai/adapter/mock/index.js +2 -80
  3. package/dist/ai/adapter/mock/mockAdapter.js +81 -0
  4. package/dist/ai/adapter/openai/createOpenAIStream.js +33 -0
  5. package/dist/ai/adapter/openai/errors.js +37 -0
  6. package/dist/ai/adapter/openai/index.js +5 -333
  7. package/dist/ai/adapter/openai/openaiAdapter.js +54 -0
  8. package/dist/ai/adapter/openai/responses.js +108 -0
  9. package/dist/ai/adapter/openai/sse.js +112 -0
  10. package/dist/{chunks/types-Yy0NrerF.js → ai/adapter/types.js} +1 -1
  11. package/dist/ai/agent/classifier.js +12 -0
  12. package/dist/ai/agent/diff.js +223 -0
  13. package/dist/ai/agent/diffHighlight.js +101 -0
  14. package/dist/ai/agent/permissions.js +29 -0
  15. package/dist/ai/agent/toolfmt.js +383 -0
  16. package/dist/ai/agent/transcript.js +127 -0
  17. package/dist/ai/components/AllocationSelector.js +56 -0
  18. package/dist/ai/components/AttachmentManager.js +144 -0
  19. package/dist/ai/components/AttachmentUpload.js +179 -0
  20. package/dist/ai/components/BlockedGroupBanner.js +29 -0
  21. package/dist/ai/components/BranchNavigator.js +57 -0
  22. package/dist/ai/components/ChatEmptyState.js +171 -0
  23. package/dist/ai/components/ChatInput.js +265 -0
  24. package/dist/ai/components/ChatLayout.js +15 -0
  25. package/dist/ai/components/ChatMessage.js +338 -0
  26. package/dist/ai/components/ChatMessageList.js +301 -0
  27. package/dist/ai/components/ChatSidebar.js +345 -0
  28. package/dist/ai/components/ChatThread.js +198 -0
  29. package/dist/ai/components/ComposerChrome.js +50 -0
  30. package/dist/ai/components/ComposerFrame.js +33 -0
  31. package/dist/ai/components/DragOverlay.js +20 -0
  32. package/dist/ai/components/ModelSelector.js +343 -0
  33. package/dist/ai/components/ShareDialog.js +260 -0
  34. package/dist/ai/components/SlashMenu.js +91 -0
  35. package/dist/ai/components/agent/ActivityLine.js +151 -0
  36. package/dist/ai/components/agent/AgentMessageParts.js +721 -0
  37. package/dist/ai/components/agent/PermissionPicker.js +38 -0
  38. package/dist/ai/components/attachmentKeys.js +10 -0
  39. package/dist/ai/core/ChatProvider.js +848 -0
  40. package/dist/ai/core/KeyboardShortcutsProvider.js +9 -0
  41. package/dist/ai/core/chatReducer.js +223 -0
  42. package/dist/ai/core/config.js +45 -0
  43. package/dist/ai/core/events.js +4 -0
  44. package/dist/ai/core/exportConversation.js +42 -0
  45. package/dist/ai/core/greeting.js +38 -0
  46. package/dist/ai/core/parts.js +84 -0
  47. package/dist/ai/core/useDragDrop.js +36 -0
  48. package/dist/ai/core/useKeyboardShortcuts.js +77 -0
  49. package/dist/ai/core/useSSEStream.js +98 -0
  50. package/dist/ai/index.js +43 -6644
  51. package/dist/ai/strings.js +175 -0
  52. package/dist/ai/ui/Avatar.js +35 -0
  53. package/dist/ai/ui/Dropdown.js +110 -0
  54. package/dist/ai/ui/Markdown.js +79 -0
  55. package/dist/ai/ui/Notice.js +77 -0
  56. package/dist/ai/ui/codeBreaks.js +36 -0
  57. package/dist/ai/ui/prose.js +13 -0
  58. package/dist/ai/ui/sidebar.js +148 -0
  59. package/dist/ai/utils.js +23 -0
  60. package/dist/components/AddMenu.js +46 -0
  61. package/dist/components/Avatar.js +46 -0
  62. package/dist/{chunks/BareModal-BtRiQezg.js → components/BareModal.js} +1 -1
  63. package/dist/components/Breadcrumbs.js +83 -0
  64. package/dist/components/Button.js +83 -0
  65. package/dist/components/Callout.js +23 -0
  66. package/dist/components/Card.js +31 -0
  67. package/dist/components/CollapsiblePanel.js +77 -0
  68. package/dist/{chunks/ConfirmModal-aOR4O1QE.js → components/ConfirmModal.js} +4 -9
  69. package/dist/components/CopyToClipboard.js +81 -0
  70. package/dist/{chunks/Markdown-CM-FenNE.js → components/CreateModal.js} +9 -116
  71. package/dist/components/DescriptionList.js +42 -0
  72. package/dist/components/Dropdown.js +349 -0
  73. package/dist/components/EmptyState.js +27 -0
  74. package/dist/components/Filter.js +32 -0
  75. package/dist/components/FilterPill.js +13 -0
  76. package/dist/components/IconButton.js +34 -0
  77. package/dist/{chunks/Indicator-hBSIxOPK.js → components/Indicator.js} +1 -1
  78. package/dist/components/Input.js +88 -0
  79. package/dist/components/LetterBadge.js +13 -0
  80. package/dist/components/Listbox.js +123 -0
  81. package/dist/{chunks/Loader-BXoXTZGc.js → components/Loader.js} +1 -1
  82. package/dist/components/Markdown.js +15 -0
  83. package/dist/components/MultiSelect.js +171 -0
  84. package/dist/components/Overview.js +39 -0
  85. package/dist/components/PageHeader.js +194 -0
  86. package/dist/components/Pagination.js +35 -0
  87. package/dist/{chunks/Provider-CDYurraE.js → components/Provider.js} +1 -1
  88. package/dist/{chunks/RequiredMark-DfLdYbYZ.js → components/RequiredMark.js} +1 -1
  89. package/dist/components/SectionHeader.js +11 -0
  90. package/dist/components/SettingModeOption.js +30 -0
  91. package/dist/components/SettingRow.js +35 -0
  92. package/dist/components/SettingsCard.js +31 -0
  93. package/dist/components/SettingsGroup.js +17 -0
  94. package/dist/components/Sort.js +42 -0
  95. package/dist/components/StatusBadge.js +63 -0
  96. package/dist/components/SwitchToggle.js +59 -0
  97. package/dist/{chunks/SwitchToggleSmall-FcsaYSTs.js → components/SwitchToggleSmall.js} +1 -1
  98. package/dist/{chunks/Table-DU2MW8xz.js → components/Table.js} +3 -3
  99. package/dist/components/Toggle.js +20 -0
  100. package/dist/{chunks/Tooltip-K5HJr4_p.js → components/Tooltip.js} +2 -27
  101. package/dist/{chunks/StatusBadge-C1Blx_cW.js → components/dropdownUtils.js} +2 -63
  102. package/dist/{chunks/focus-D_iZJTW1.js → components/focus.js} +1 -1
  103. package/dist/components/ghostButton.js +7 -0
  104. package/dist/components/inputClasses.js +4 -0
  105. package/dist/components/keys.js +27 -0
  106. package/dist/components/relativeTime.js +32 -0
  107. package/dist/{chunks/useCssIsDark-DHr05dG2.js → components/useCssIsDark.js} +1 -1
  108. package/dist/{chunks/duration-BOv8Phl8.js → duration.js} +1 -1
  109. package/dist/editor/Editor.js +13 -0
  110. package/dist/editor/EditorField.js +53 -0
  111. package/dist/editor/FormCodePanel.js +173 -0
  112. package/dist/{chunks/Monaco-DJ8MW-Dq.js → editor/Monaco.js} +4 -2
  113. package/dist/editor/index.js +6 -1
  114. package/dist/editor/themes.js +91 -0
  115. package/dist/editor/workers.js +29 -0
  116. package/dist/editor/yaml.js +15 -0
  117. package/dist/file-explorer/FileExplorer.js +1402 -0
  118. package/dist/file-explorer/FileExplorerContext.js +370 -0
  119. package/dist/{chunks/CodePreviewEditor-YqQBz1YL.js → file-explorer/FilePreview/CodePreviewEditor.js} +3 -2
  120. package/dist/file-explorer/FilePreview/Inline.js +45 -0
  121. package/dist/file-explorer/FilePreview/StreamedBadge.js +16 -0
  122. package/dist/file-explorer/FilePreview/Viewer.js +526 -0
  123. package/dist/file-explorer/FilePreview/index.js +266 -0
  124. package/dist/file-explorer/FilePreview/parse.js +63 -0
  125. package/dist/file-explorer/FilePreview/previewCache.js +98 -0
  126. package/dist/file-explorer/FilePreview/previewType.js +151 -0
  127. package/dist/file-explorer/FilePreview/readCappedBytes.js +196 -0
  128. package/dist/file-explorer/FilePreview/useObjectBlobUrl.js +95 -0
  129. package/dist/file-explorer/FilePreview/useObjectText.js +109 -0
  130. package/dist/file-explorer/FilePreview/usePresignedUrl.js +73 -0
  131. package/dist/file-explorer/FileRow.js +165 -0
  132. package/dist/file-explorer/FileRowsWindow.js +92 -0
  133. package/dist/file-explorer/FileTable.js +172 -0
  134. package/dist/file-explorer/FileTableHeaders.js +49 -0
  135. package/dist/file-explorer/LoadMoreRow.js +46 -0
  136. package/dist/file-explorer/TreeView.js +376 -0
  137. package/dist/file-explorer/helpers.js +537 -0
  138. package/dist/file-explorer/index.js +7 -5671
  139. package/dist/file-explorer/lib/batch.js +39 -0
  140. package/dist/file-explorer/lib/errors.js +9 -0
  141. package/dist/file-explorer/lib/paging.js +124 -0
  142. package/dist/file-explorer/lib/utils.js +322 -0
  143. package/dist/file-explorer/toasts.js +400 -0
  144. package/dist/file-explorer/treeRows.js +73 -0
  145. package/dist/file-explorer/useNearEnd.js +27 -0
  146. package/dist/form/FieldWrapper.js +54 -0
  147. package/dist/form/Fields/BooleanField.js +51 -0
  148. package/dist/form/Fields/CheckboxGroupField.js +72 -0
  149. package/dist/form/Fields/ColorField.js +33 -0
  150. package/dist/form/Fields/DropdownField.js +49 -0
  151. package/dist/form/Fields/DurationField.js +130 -0
  152. package/dist/form/Fields/LabelField.js +19 -0
  153. package/dist/form/Fields/MultiDropdownField.js +40 -0
  154. package/dist/form/Fields/NumberField.js +51 -0
  155. package/dist/form/Fields/ObjectField.js +54 -0
  156. package/dist/form/Fields/RadioField.js +49 -0
  157. package/dist/form/Fields/RangeField.js +58 -0
  158. package/dist/form/Fields/ResourceOptionIcon.js +21 -0
  159. package/dist/form/Fields/StepField.js +6 -0
  160. package/dist/form/Fields/StringField.js +37 -0
  161. package/dist/form/Fields/TextAreaField.js +31 -0
  162. package/dist/form/Fields/durationValidity.js +11 -0
  163. package/dist/form/Form.js +643 -0
  164. package/dist/form/SpecialTypes/DropdownWrapper.js +30 -0
  165. package/dist/form/SpecialTypes/FormikCustomDropdown.js +122 -0
  166. package/dist/form/SpecialTypes/FormikCustomInput.js +111 -0
  167. package/dist/form/SpecialTypes/FormikMultiSelectDropdown.js +31 -0
  168. package/dist/form/SpecialTypes/FormikNumericInput.js +60 -0
  169. package/dist/form/Wizard/WizardContainer.js +138 -0
  170. package/dist/form/Wizard/WizardNavigation.js +92 -0
  171. package/dist/form/Wizard/WizardStepContent.js +35 -0
  172. package/dist/form/Wizard/WizardStepIndicator.js +108 -0
  173. package/dist/form/Wizard/useWizardState.js +118 -0
  174. package/dist/form/Wizard/utils.js +34 -0
  175. package/dist/form/fieldContext.js +26 -0
  176. package/dist/form/fieldRegistry.js +50 -0
  177. package/dist/form/index.js +17 -2321
  178. package/dist/{chunks/FieldWrapper-Cszwg-4B.js → form/lib.js} +2 -97
  179. package/dist/form/useParsedOpts.js +62 -0
  180. package/dist/form/usePrevious.js +11 -0
  181. package/dist/form/utils/checkShowIfObject.js +14 -0
  182. package/dist/form/utils/duration.js +69 -0
  183. package/dist/form/utils/getParentValue.js +23 -0
  184. package/dist/form/utils/getPlaceholder.js +9 -0
  185. package/dist/form/utils/getValueUsingPath.js +23 -0
  186. package/dist/form/utils/secondaryField.js +22 -0
  187. package/dist/graph/AnnotationBanner.js +141 -0
  188. package/dist/graph/Collapse.js +41 -0
  189. package/dist/graph/DependencyGraph.js +1398 -0
  190. package/dist/graph/JobSummary.js +104 -0
  191. package/dist/graph/MatrixGroup.js +107 -0
  192. package/dist/graph/Reveal.js +57 -0
  193. package/dist/graph/TreeView.js +711 -0
  194. package/dist/graph/index.js +1 -2519
  195. package/dist/graph/types.js +6 -0
  196. package/dist/index.js +46 -599
  197. package/dist/list/ActionToolbar.js +184 -0
  198. package/dist/list/HoverCard.js +73 -0
  199. package/dist/list/ListPage.js +145 -0
  200. package/dist/list/ListPager.js +59 -0
  201. package/dist/list/ListTable.js +163 -0
  202. package/dist/list/ListViewControls.js +372 -0
  203. package/dist/list/RowContextMenu.js +350 -0
  204. package/dist/list/UserHoverCard.js +117 -0
  205. package/dist/list/index.js +11 -2
  206. package/dist/list/listSearch.js +137 -0
  207. package/dist/list/listView.js +210 -0
  208. package/dist/list/textEntry.js +10 -0
  209. package/dist/{chunks/logviewer-BTKuQWzw.js → logviewer/LogViewer.js} +4 -82
  210. package/dist/logviewer/index.js +2 -1
  211. package/dist/logviewer/segments.js +80 -0
  212. package/package.json +1 -1
  213. package/dist/chunks/PageHeader-0kG_iypP.js +0 -692
  214. package/dist/chunks/SwitchToggle-DShNdcao.js +0 -758
  215. package/dist/chunks/editor-Dk1rbV75.js +0 -361
  216. package/dist/chunks/list-CJm875m7.js +0 -1418
  217. /package/dist/{chunks/MarkdownImpl-CyeTVvSt.js → components/MarkdownImpl.js} +0 -0
@@ -0,0 +1,376 @@
1
+ import { useStrings } from "../components/Provider.js";
2
+ import { AngleRightIcon, FolderIcon, FolderOpenIcon } from "../icons.js";
3
+ import { positionKeys } from "../components/keys.js";
4
+ import { getParentPath } from "./lib/utils.js";
5
+ import { canPreview } from "./FilePreview/previewType.js";
6
+ import { getNodeIcon } from "./helpers.js";
7
+ import { findRowIndex, flattenTreeRows, nextFileLimit, stepRow } from "./treeRows.js";
8
+ import cx from "classnames";
9
+ import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
10
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
11
+ import Skeleton from "react-loading-skeleton";
12
+ import { useVirtualizer } from "@tanstack/react-virtual";
13
+ //#region src/file-explorer/TreeView.tsx
14
+ /** 9px margin + 12px padding, per level. */
15
+ var INDENT_PER_LEVEL = 21;
16
+ var GUIDE_OFFSET = 9;
17
+ var ROW_PAD_DIRECTORY = 8;
18
+ var ROW_PAD_FILE = 28;
19
+ function rowHeight(row) {
20
+ return row.kind === "skeleton" ? 64 : 30;
21
+ }
22
+ /** The sidebar tree, flattened and windowed. The flat array is the single source
23
+ * of truth for rendering, arrow keys and ARIA. */
24
+ function TreeView({ rootNodes, parentToChildrenMap, expandedPaths, selectedPath, loadingPaths, onToggleExpand, onSelect, onDropToFolder, onContextMenu, onPreviewFile, paging }) {
25
+ const [scrollEl, setScrollEl] = useState(null);
26
+ const [fileLimits, setFileLimits] = useState(() => /* @__PURE__ */ new Map());
27
+ const incompletePaths = paging?.incompletePaths;
28
+ const rows = useMemo(() => flattenTreeRows(rootNodes, parentToChildrenMap, expandedPaths, {
29
+ loadingPaths,
30
+ ...incompletePaths && { incompletePaths },
31
+ fileLimits
32
+ }), [
33
+ rootNodes,
34
+ parentToChildrenMap,
35
+ expandedPaths,
36
+ loadingPaths,
37
+ incompletePaths,
38
+ fileLimits
39
+ ]);
40
+ const revealMore = useCallback((row) => {
41
+ const path = row.node.path;
42
+ if ((row.hiddenFiles ?? 0) > 0) {
43
+ setFileLimits((prev) => new Map(prev).set(path, nextFileLimit(prev.get(path))));
44
+ return;
45
+ }
46
+ paging?.onLoadMore(row.node);
47
+ }, [paging]);
48
+ const offsets = useMemo(() => {
49
+ const out = new Array(rows.length);
50
+ let running = 0;
51
+ rows.forEach((row, i) => {
52
+ out[i] = running;
53
+ running += rowHeight(row);
54
+ });
55
+ return out;
56
+ }, [rows]);
57
+ const cursorRef = useRef(null);
58
+ const wantsFocusRef = useRef(false);
59
+ const pendingNavRef = useRef(/* @__PURE__ */ new Set());
60
+ useEffect(() => {
61
+ if (pendingNavRef.current.delete(selectedPath)) return;
62
+ cursorRef.current = selectedPath;
63
+ }, [selectedPath]);
64
+ const getRowKey = useCallback((index) => rows[index]?.key ?? index, [rows]);
65
+ const virtualizer = useVirtualizer({
66
+ count: rows.length,
67
+ getScrollElement: () => scrollEl,
68
+ estimateSize: (index) => {
69
+ const row = rows[index];
70
+ return row ? rowHeight(row) : 30;
71
+ },
72
+ getItemKey: getRowKey,
73
+ overscan: 10
74
+ });
75
+ const move = useCallback((from, direction) => {
76
+ const cursorIndex = findRowIndex(rows, cursorRef.current ?? "");
77
+ const index = cursorIndex >= 0 ? cursorIndex : findRowIndex(rows, from.path);
78
+ if (index === -1) return;
79
+ const target = stepRow(rows, index, direction);
80
+ if (!target) return;
81
+ cursorRef.current = target.node.path;
82
+ pendingNavRef.current.add(target.node.path);
83
+ virtualizer.scrollToIndex(findRowIndex(rows, target.node.path));
84
+ wantsFocusRef.current = true;
85
+ onSelect(target.node.path, target.node);
86
+ }, [
87
+ rows,
88
+ virtualizer,
89
+ onSelect
90
+ ]);
91
+ useEffect(() => {
92
+ if (!wantsFocusRef.current) return;
93
+ const index = findRowIndex(rows, cursorRef.current ?? "");
94
+ const row = index < 0 ? null : scrollEl?.querySelector(`[data-index="${index}"] [role="treeitem"]`);
95
+ if (row instanceof HTMLElement) {
96
+ wantsFocusRef.current = false;
97
+ row.focus();
98
+ return;
99
+ }
100
+ if (selectedPath === cursorRef.current) wantsFocusRef.current = false;
101
+ }, [
102
+ selectedPath,
103
+ rows,
104
+ scrollEl
105
+ ]);
106
+ const handleKeyDown = useCallback((e, node) => {
107
+ if (e.key === " " && node.type === "file" && canPreview(node)) {
108
+ e.preventDefault();
109
+ onPreviewFile(node);
110
+ return;
111
+ }
112
+ if (e.key === "ArrowDown") {
113
+ e.preventDefault();
114
+ move(node, 1);
115
+ } else if (e.key === "ArrowUp") {
116
+ e.preventDefault();
117
+ move(node, -1);
118
+ }
119
+ }, [move, onPreviewFile]);
120
+ const items = virtualizer.getVirtualItems();
121
+ useTreePitchAudit(scrollEl, rows, items);
122
+ const renderRow = (index, top, height) => {
123
+ const row = rows[index];
124
+ if (!row) return null;
125
+ return /* @__PURE__ */ jsx("div", {
126
+ "data-index": index,
127
+ className: "absolute inset-x-0",
128
+ style: {
129
+ top,
130
+ height
131
+ },
132
+ children: row.kind === "skeleton" ? /* @__PURE__ */ jsx(TreeSkeletonRow, { depth: row.depth }) : row.kind === "more" ? /* @__PURE__ */ jsx(TreeMoreRow, {
133
+ depth: row.depth,
134
+ hiddenFiles: row.hiddenFiles ?? 0,
135
+ partial: incompletePaths?.has(row.node.path) ?? false,
136
+ loading: paging?.isLoading(row.node.path) ?? false,
137
+ failed: paging?.hasFailed(row.node.path) ?? false,
138
+ onReveal: () => revealMore(row)
139
+ }) : /* @__PURE__ */ jsx(TreeRowItem, {
140
+ row,
141
+ isSelected: row.node.path === selectedPath,
142
+ onToggleExpand,
143
+ onSelect,
144
+ onDropToFolder,
145
+ onContextMenu,
146
+ onKeyDown: handleKeyDown
147
+ })
148
+ }, row.key);
149
+ };
150
+ const cursorIndex = findRowIndex(rows, cursorRef.current ?? selectedPath);
151
+ const cursorOutsideWindow = cursorIndex >= 0 && !items.some((item) => item.index === cursorIndex);
152
+ const slots = items.map((item) => ({
153
+ index: item.index,
154
+ top: item.start,
155
+ height: item.size
156
+ }));
157
+ if (cursorOutsideWindow) {
158
+ slots.push({
159
+ index: cursorIndex,
160
+ top: offsets[cursorIndex] ?? 0,
161
+ height: rowHeight(rows[cursorIndex])
162
+ });
163
+ slots.sort((a, b) => a.index - b.index);
164
+ }
165
+ return /* @__PURE__ */ jsx("div", {
166
+ ref: setScrollEl,
167
+ className: "overflow-auto flex-1 px-2 py-2",
168
+ role: "tree",
169
+ onDragOver: (e) => e.preventDefault(),
170
+ onDrop: (e) => e.preventDefault(),
171
+ children: /* @__PURE__ */ jsx("div", {
172
+ className: "relative w-full",
173
+ style: { height: virtualizer.getTotalSize() },
174
+ children: slots.map((slot) => renderRow(slot.index, slot.top, slot.height))
175
+ })
176
+ });
177
+ }
178
+ function indentStyle(depth, isDirectory) {
179
+ return { paddingLeft: depth * INDENT_PER_LEVEL + (isDirectory ? ROW_PAD_DIRECTORY : ROW_PAD_FILE) };
180
+ }
181
+ function IndentGuides({ depth }) {
182
+ if (depth === 0) return null;
183
+ return /* @__PURE__ */ jsx(Fragment$1, { children: positionKeys(depth, "indent").map((key, level) => /* @__PURE__ */ jsx("span", {
184
+ "aria-hidden": "true",
185
+ className: "pointer-events-none absolute inset-y-0 w-px bg-(--theme-border)/60",
186
+ style: { left: GUIDE_OFFSET + level * INDENT_PER_LEVEL }
187
+ }, key)) });
188
+ }
189
+ function RowIcons({ isDirectory, isExpanded, customIcon }) {
190
+ if (!isDirectory) return /* @__PURE__ */ jsx("div", {
191
+ className: "shrink-0",
192
+ children: customIcon
193
+ });
194
+ const folder = isExpanded ? /* @__PURE__ */ jsx(FolderOpenIcon, { className: "h-4 w-4 mr-1 text-sky-500" }) : /* @__PURE__ */ jsx(FolderIcon, { className: "h-4 w-4 mr-1 text-sky-500/80" });
195
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(AngleRightIcon, { className: cx("h-3.5 w-3.5 shrink-0 theme-muted-text transition-transform duration-150", isExpanded && "rotate-90") }), customIcon ?? folder] });
196
+ }
197
+ /** Sized by the slot, not by its contents: react-loading-skeleton wraps each bar
198
+ * in an inline span, so a bar occupies a line box taller than its `height` and
199
+ * the overflow would sit on top of the absolutely positioned row below. */
200
+ /** Click-only, unlike the table's sentinel: the tree is a navigation aid, and
201
+ * growing it automatically as the user scrolls is what the cap exists to prevent. */
202
+ function TreeMoreRow({ depth, hiddenFiles, partial, loading, failed, onReveal }) {
203
+ const t = useStrings().fileExplorer;
204
+ const label = () => {
205
+ if (failed) return t.retry;
206
+ if (hiddenFiles === 0) return t.loadMore;
207
+ return partial ? t.treeMoreCountPartial(hiddenFiles) : t.treeMoreCount(hiddenFiles);
208
+ };
209
+ return /* @__PURE__ */ jsxs("div", {
210
+ className: "relative h-full overflow-hidden",
211
+ children: [/* @__PURE__ */ jsx(IndentGuides, { depth }), /* @__PURE__ */ jsx("div", {
212
+ className: "flex h-full items-center pr-2 text-xs theme-muted-text",
213
+ style: { paddingLeft: depth * INDENT_PER_LEVEL + ROW_PAD_FILE },
214
+ children: loading ? /* @__PURE__ */ jsx("span", { children: t.loadingMore }) : /* @__PURE__ */ jsx("button", {
215
+ type: "button",
216
+ className: cx("cursor-pointer underline", failed ? "text-red-500" : "theme-link"),
217
+ onClick: onReveal,
218
+ children: label()
219
+ })
220
+ })]
221
+ });
222
+ }
223
+ function TreeSkeletonRow({ depth }) {
224
+ return /* @__PURE__ */ jsxs("div", {
225
+ className: "relative h-full overflow-hidden",
226
+ children: [/* @__PURE__ */ jsx(IndentGuides, { depth }), /* @__PURE__ */ jsx("div", {
227
+ className: "flex h-full flex-col justify-center gap-1.5 py-1 pr-2",
228
+ style: { paddingLeft: depth * INDENT_PER_LEVEL + ROW_PAD_FILE },
229
+ children: [
230
+ 0,
231
+ 1,
232
+ 2
233
+ ].map((i) => /* @__PURE__ */ jsx(Skeleton, {
234
+ height: 12,
235
+ containerClassName: "block leading-none"
236
+ }, i))
237
+ })]
238
+ });
239
+ }
240
+ /** Hovering a dragged file over a collapsed folder expands it after 400ms. */
241
+ function useSpringLoadedDrop({ node, dropPath, isDirectory, isExpanded, onToggleExpand, onDropToFolder }) {
242
+ const hoverCount = useRef(0);
243
+ const timer = useRef(null);
244
+ const [dragActive, setDragActive] = useState(false);
245
+ const clear = () => {
246
+ if (timer.current) {
247
+ clearTimeout(timer.current);
248
+ timer.current = null;
249
+ }
250
+ };
251
+ useEffect(() => () => {
252
+ if (timer.current) {
253
+ clearTimeout(timer.current);
254
+ timer.current = null;
255
+ }
256
+ }, []);
257
+ return {
258
+ dragActive,
259
+ dragHandlers: {
260
+ onDragEnter: (e) => {
261
+ e.preventDefault();
262
+ e.stopPropagation();
263
+ hoverCount.current += 1;
264
+ setDragActive(true);
265
+ if (isDirectory && !isExpanded && !timer.current) timer.current = setTimeout(() => {
266
+ onToggleExpand(node.path);
267
+ timer.current = null;
268
+ }, 400);
269
+ },
270
+ onDragOver: (e) => e.preventDefault(),
271
+ onDragLeave: (e) => {
272
+ e.preventDefault();
273
+ e.stopPropagation();
274
+ hoverCount.current -= 1;
275
+ if (hoverCount.current > 0) return;
276
+ setDragActive(false);
277
+ clear();
278
+ },
279
+ onDrop: (e) => {
280
+ e.stopPropagation();
281
+ e.preventDefault();
282
+ hoverCount.current = 0;
283
+ setDragActive(false);
284
+ clear();
285
+ onDropToFolder(dropPath, e.dataTransfer.items.length ? e.dataTransfer.items : e.dataTransfer.files);
286
+ }
287
+ }
288
+ };
289
+ }
290
+ /** Keys a row handles itself; the arrows that move between rows delegate upward,
291
+ * where the flat array is. The set is deliberately narrow:
292
+ * no Home/End, no typeahead, and ArrowLeft/Right only toggle a directory. */
293
+ function dispatchRowKey(e, { node, isDirectory, isExpanded, onSelect, onToggleExpand, onDelegate }) {
294
+ if (e.key === "Enter") {
295
+ e.preventDefault();
296
+ onSelect(e);
297
+ return;
298
+ }
299
+ if (e.key === " ") {
300
+ e.preventDefault();
301
+ if (!isDirectory && canPreview(node)) onDelegate(e, node);
302
+ else onSelect(e);
303
+ return;
304
+ }
305
+ if (e.key === "ArrowRight" && isDirectory && !isExpanded || e.key === "ArrowLeft" && isDirectory && isExpanded) {
306
+ e.preventDefault();
307
+ onToggleExpand(node.path);
308
+ return;
309
+ }
310
+ onDelegate(e, node);
311
+ }
312
+ var TreeRowItem = memo(function TreeRowItem({ row, isSelected, onToggleExpand, onSelect, onDropToFolder, onContextMenu, onKeyDown }) {
313
+ const { node, depth, isExpanded } = row;
314
+ const isDirectory = node.type === "directory";
315
+ const isStorageNode = !!node.displayName && node.storageId;
316
+ const { dragActive, dragHandlers } = useSpringLoadedDrop({
317
+ node,
318
+ dropPath: isDirectory ? node.path : getParentPath(node.path),
319
+ isDirectory,
320
+ isExpanded,
321
+ onToggleExpand,
322
+ onDropToFolder
323
+ });
324
+ const handleSelect = (e) => {
325
+ if (e.type === "click") e.preventDefault();
326
+ onSelect(node.path, node);
327
+ if (isDirectory) onToggleExpand(node.path);
328
+ };
329
+ const handleKeyDown = (e) => dispatchRowKey(e, {
330
+ node,
331
+ isDirectory,
332
+ isExpanded,
333
+ onSelect: handleSelect,
334
+ onToggleExpand,
335
+ onDelegate: onKeyDown
336
+ });
337
+ const customIcon = getNodeIcon(node);
338
+ const label = isDirectory && isStorageNode && node.displayName ? node.displayName : node.name;
339
+ return /* @__PURE__ */ jsxs("div", {
340
+ className: cx("relative h-full rounded-md transition-[box-shadow,background-color] duration-150", dragActive && "ring-1 ring-(--theme-link)/60"),
341
+ ...dragHandlers,
342
+ children: [/* @__PURE__ */ jsx(IndentGuides, { depth }), /* @__PURE__ */ jsxs("div", {
343
+ className: cx("group relative flex items-center gap-1.5 pr-2 py-1 rounded-md cursor-pointer", "transition-colors duration-150", "focus:outline-none focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-(--theme-link)", isSelected ? "bg-(--theme-element)/15 font-medium" : dragActive ? "theme-hover" : "hover:theme-hover"),
344
+ style: indentStyle(depth, isDirectory),
345
+ onClick: handleSelect,
346
+ onKeyDown: handleKeyDown,
347
+ onContextMenu: (e) => onContextMenu(e, node),
348
+ role: "treeitem",
349
+ "aria-level": depth + 1,
350
+ "aria-posinset": row.posInSet,
351
+ "aria-setsize": row.setSize,
352
+ "aria-selected": isSelected,
353
+ ...isDirectory && { "aria-expanded": isExpanded },
354
+ tabIndex: isSelected ? 0 : -1,
355
+ children: [/* @__PURE__ */ jsx(RowIcons, {
356
+ isDirectory,
357
+ isExpanded,
358
+ customIcon
359
+ }), /* @__PURE__ */ jsx("span", {
360
+ className: "text-sm truncate",
361
+ children: label
362
+ })]
363
+ })]
364
+ });
365
+ });
366
+ /** The height constants must equal the rendered pitch, or the scrollbar
367
+ * drifts from the content. Reports the measured value to use. */
368
+ function useTreePitchAudit(scrollEl, rows, items) {
369
+ useEffect(() => {}, [
370
+ scrollEl,
371
+ rows,
372
+ items
373
+ ]);
374
+ }
375
+ //#endregion
376
+ export { TreeView };