@parallelworks/ui 0.20.0 → 0.21.0

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 (154) hide show
  1. package/dist/ai/adapter/openai/createOpenAIStream.js +3 -2
  2. package/dist/ai/agent/diff.d.ts +0 -1
  3. package/dist/ai/agent/diff.js +3 -2
  4. package/dist/ai/agent/toolfmt.js +2 -3
  5. package/dist/ai/ai.css +5 -0
  6. package/dist/ai/components/AllocationSelector.js +3 -2
  7. package/dist/ai/components/AttachmentManager.js +1 -1
  8. package/dist/ai/components/BranchNavigator.js +4 -2
  9. package/dist/ai/components/ChatInput.d.ts +2 -5
  10. package/dist/ai/components/ChatInput.js +10 -7
  11. package/dist/ai/components/ChatMessage.js +7 -7
  12. package/dist/ai/components/ChatSidebar.d.ts +0 -1
  13. package/dist/ai/components/ChatSidebar.js +4 -6
  14. package/dist/ai/components/ChatThread.js +5 -8
  15. package/dist/ai/components/ModelSelector.js +3 -2
  16. package/dist/ai/components/ShareDialog.js +6 -3
  17. package/dist/ai/components/SlashMenu.d.ts +0 -1
  18. package/dist/ai/components/agent/ActivityLine.d.ts +0 -1
  19. package/dist/ai/components/agent/ActivityLine.js +2 -2
  20. package/dist/ai/components/agent/AgentMessageParts.d.ts +1 -26
  21. package/dist/ai/components/agent/AgentMessageParts.js +4 -5
  22. package/dist/ai/core/ChatProvider.d.ts +2 -6
  23. package/dist/ai/core/ChatProvider.js +42 -67
  24. package/dist/ai/core/KeyboardShortcutsProvider.js +1 -1
  25. package/dist/ai/core/chatReducer.d.ts +0 -6
  26. package/dist/ai/core/chatReducer.js +1 -15
  27. package/dist/ai/core/useKeyboardShortcuts.d.ts +3 -1
  28. package/dist/ai/core/useKeyboardShortcuts.js +9 -11
  29. package/dist/ai/core/useSSEStream.d.ts +0 -1
  30. package/dist/ai/core/useSSEStream.js +5 -17
  31. package/dist/ai/strings.d.ts +0 -1
  32. package/dist/ai/strings.js +0 -1
  33. package/dist/ai/types.d.ts +1 -1
  34. package/dist/ai/ui/Avatar.d.ts +1 -2
  35. package/dist/ai/ui/Avatar.js +7 -18
  36. package/dist/ai/ui/Markdown.d.ts +1 -3
  37. package/dist/ai/ui/Markdown.js +1 -2
  38. package/dist/ai/ui/sidebar.d.ts +0 -1
  39. package/dist/ai/ui/sidebar.js +3 -1
  40. package/dist/ai/utils.js +1 -1
  41. package/dist/components/Avatar.js +4 -2
  42. package/dist/components/BareModal.js +1 -5
  43. package/dist/components/Breadcrumbs.js +21 -14
  44. package/dist/components/CollapsiblePanel.d.ts +1 -3
  45. package/dist/components/CollapsiblePanel.js +5 -5
  46. package/dist/components/Dropdown.d.ts +0 -4
  47. package/dist/components/Dropdown.js +5 -6
  48. package/dist/components/Listbox.js +14 -18
  49. package/dist/components/MultiSelect.js +5 -8
  50. package/dist/components/Pagination.js +1 -1
  51. package/dist/components/Provider.d.ts +0 -8
  52. package/dist/components/SwitchToggle.js +3 -5
  53. package/dist/components/Table.d.ts +1 -3
  54. package/dist/components/dropdownUtils.js +5 -6
  55. package/dist/components/relativeTime.d.ts +0 -7
  56. package/dist/components/relativeTime.js +1 -1
  57. package/dist/duration.js +1 -1
  58. package/dist/editor/Editor.d.ts +1 -1
  59. package/dist/editor/Editor.js +1 -1
  60. package/dist/editor/EditorField.d.ts +0 -1
  61. package/dist/editor/EditorField.js +8 -8
  62. package/dist/editor/FormCodePanel.js +14 -20
  63. package/dist/editor/yamlLanguage.js +2 -2
  64. package/dist/file-explorer/FileExplorer.js +56 -59
  65. package/dist/file-explorer/FileExplorerContext.d.ts +1 -2
  66. package/dist/file-explorer/FileExplorerContext.js +92 -125
  67. package/dist/file-explorer/FilePreview/Inline.d.ts +1 -2
  68. package/dist/file-explorer/FilePreview/Inline.js +2 -2
  69. package/dist/file-explorer/FilePreview/Viewer.js +0 -4
  70. package/dist/file-explorer/FilePreview/index.d.ts +1 -2
  71. package/dist/file-explorer/FilePreview/index.js +3 -3
  72. package/dist/file-explorer/FilePreview/parse.d.ts +2 -1
  73. package/dist/file-explorer/FilePreview/useObjectBlobUrl.d.ts +5 -4
  74. package/dist/file-explorer/FilePreview/useObjectBlobUrl.js +23 -36
  75. package/dist/file-explorer/FilePreview/useObjectText.d.ts +1 -2
  76. package/dist/file-explorer/FilePreview/useObjectText.js +5 -7
  77. package/dist/file-explorer/FileRow.js +1 -1
  78. package/dist/file-explorer/FileRowsWindow.d.ts +5 -12
  79. package/dist/file-explorer/FileTable.d.ts +2 -6
  80. package/dist/file-explorer/LoadMoreRow.d.ts +8 -0
  81. package/dist/file-explorer/TreeView.d.ts +0 -1
  82. package/dist/file-explorer/TreeView.js +6 -6
  83. package/dist/file-explorer/helpers.js +2 -4
  84. package/dist/file-explorer/lib/fileExplorer.d.ts +0 -2
  85. package/dist/file-explorer/lib/types.d.ts +0 -1
  86. package/dist/file-explorer/lib/utils.js +15 -20
  87. package/dist/file-explorer/toasts.js +36 -34
  88. package/dist/form/Fields/BooleanField.js +5 -4
  89. package/dist/form/Fields/CheckboxGroupField.js +5 -4
  90. package/dist/form/Fields/ColorField.js +5 -4
  91. package/dist/form/Fields/DropdownField.d.ts +0 -1
  92. package/dist/form/Fields/DropdownField.js +10 -13
  93. package/dist/form/Fields/DurationField.js +2 -2
  94. package/dist/form/Fields/LabelField.js +5 -5
  95. package/dist/form/Fields/MultiDropdownField.d.ts +1 -1
  96. package/dist/form/Fields/MultiDropdownField.js +8 -11
  97. package/dist/form/Fields/NumberField.js +6 -5
  98. package/dist/form/Fields/ObjectField.d.ts +1 -2
  99. package/dist/form/Fields/ObjectField.js +22 -21
  100. package/dist/form/Fields/RadioField.js +4 -3
  101. package/dist/form/Fields/RangeField.js +7 -6
  102. package/dist/form/Fields/StringField.js +2 -1
  103. package/dist/form/Fields/TextAreaField.js +2 -1
  104. package/dist/form/Form.d.ts +7 -8
  105. package/dist/form/Form.js +52 -78
  106. package/dist/form/SpecialTypes/DropdownWrapper.d.ts +0 -2
  107. package/dist/form/SpecialTypes/DropdownWrapper.js +1 -3
  108. package/dist/form/SpecialTypes/FormikCustomDropdown.d.ts +1 -3
  109. package/dist/form/SpecialTypes/FormikCustomDropdown.js +4 -7
  110. package/dist/form/SpecialTypes/FormikCustomInput.js +13 -11
  111. package/dist/form/Wizard/WizardContainer.js +8 -25
  112. package/dist/form/Wizard/WizardNavigation.d.ts +1 -1
  113. package/dist/form/Wizard/WizardNavigation.js +5 -6
  114. package/dist/form/Wizard/WizardStepContent.d.ts +1 -1
  115. package/dist/form/Wizard/WizardStepContent.js +2 -2
  116. package/dist/form/Wizard/WizardStepIndicator.js +7 -17
  117. package/dist/form/Wizard/types.d.ts +4 -46
  118. package/dist/form/Wizard/useWizardState.d.ts +2 -10
  119. package/dist/form/Wizard/useWizardState.js +42 -92
  120. package/dist/form/Wizard/utils.js +6 -11
  121. package/dist/form/lib.js +28 -54
  122. package/dist/form/types/fieldTypes.d.ts +4 -5
  123. package/dist/form/utils/dependentOptions.d.ts +6 -0
  124. package/dist/form/utils/dependentOptions.js +21 -0
  125. package/dist/form/utils/getParentValue.d.ts +2 -1
  126. package/dist/form/utils/getParentValue.js +10 -15
  127. package/dist/form/utils/getValueUsingPath.js +1 -1
  128. package/dist/graph/AnnotationBanner.d.ts +1 -1
  129. package/dist/graph/AnnotationBanner.js +1 -1
  130. package/dist/graph/Collapse.d.ts +3 -3
  131. package/dist/graph/Collapse.js +3 -8
  132. package/dist/graph/DependencyGraph.d.ts +5 -4
  133. package/dist/graph/DependencyGraph.js +172 -222
  134. package/dist/graph/JobSummary.d.ts +10 -8
  135. package/dist/graph/JobSummary.js +13 -13
  136. package/dist/graph/MatrixGroup.d.ts +5 -17
  137. package/dist/graph/MatrixGroup.js +11 -22
  138. package/dist/graph/TreeView.d.ts +1 -3
  139. package/dist/graph/TreeView.js +104 -491
  140. package/dist/graph/types.d.ts +3 -2
  141. package/dist/graph/util.d.ts +5 -0
  142. package/dist/graph/util.js +18 -0
  143. package/dist/icons.d.ts +2 -2
  144. package/dist/icons.js +2 -2
  145. package/dist/index.d.ts +1 -1
  146. package/dist/list/ListViewControls.js +17 -35
  147. package/dist/list/RowContextMenu.js +7 -7
  148. package/dist/logviewer/LogViewer.js +7 -9
  149. package/dist/logviewer/segments.js +2 -2
  150. package/dist/styles/theme.css +1 -0
  151. package/dist/styles.css +1 -1
  152. package/dist/theme/index.js +5 -5
  153. package/package.json +28 -30
  154. package/dist/ai/ui/buttons.d.ts +0 -1
@@ -187,7 +187,6 @@ function PreviewError({ onDownload }) {
187
187
  }
188
188
  function PdfView({ node, url, onDownload, storage, onCorsRetry }) {
189
189
  const { blobUrl, loading, error, tooLarge, corsError } = useObjectBlobUrl(url, {
190
- enabled: true,
191
190
  maxBytes: PDF_PREVIEW_MAX_BYTES,
192
191
  knownSize: node.size,
193
192
  mimeType: "application/pdf",
@@ -213,7 +212,6 @@ function CodeView({ node, url, onDownload, storage, onCorsRetry }) {
213
212
  const language = getMonacoLanguage(node);
214
213
  const [tail, setTail] = useState(false);
215
214
  const { text, loading, error, tooLarge, truncated, corsError } = useObjectText(url, {
216
- enabled: true,
217
215
  maxBytes: CODE_PREVIEW_MAX_BYTES,
218
216
  maxLines: CODE_PREVIEW_MAX_LINES,
219
217
  knownSize: node.size,
@@ -291,7 +289,6 @@ function toFencedCode(text, language) {
291
289
  }
292
290
  function CsvView({ node, url, onDownload, storage, onCorsRetry }) {
293
291
  const { text, loading, error, tooLarge, corsError } = useObjectText(url, {
294
- enabled: true,
295
292
  maxBytes: TABULAR_PREVIEW_MAX_BYTES,
296
293
  maxLines: 502,
297
294
  knownSize: node.size,
@@ -354,7 +351,6 @@ function CsvTable({ node, text }) {
354
351
  }
355
352
  function NotebookView({ node, url, onDownload, storage, onCorsRetry }) {
356
353
  const { text, loading, error, tooLarge, corsError } = useObjectText(url, {
357
- enabled: true,
358
354
  maxBytes: TABULAR_PREVIEW_MAX_BYTES,
359
355
  knownSize: node.size,
360
356
  cacheKey: node.path
@@ -9,7 +9,6 @@ interface FilePreviewProps {
9
9
  onClose: () => void;
10
10
  onDownload: (node: TreeNode) => void;
11
11
  onCopyUri: (node: TreeNode) => void;
12
- showSecurityIndicator?: boolean;
13
12
  }
14
- export declare function FilePreview({ open, node, siblings, storage, getPresignedUrl, onNavigate, onClose, onDownload, onCopyUri, showSecurityIndicator, }: FilePreviewProps): import("react").ReactPortal | null;
13
+ export declare function FilePreview({ open, node, siblings, storage, getPresignedUrl, onNavigate, onClose, onDownload, onCopyUri, }: FilePreviewProps): import("react").ReactPortal | null;
15
14
  export {};
@@ -16,11 +16,11 @@ import { createPortal } from "react-dom";
16
16
  function isDocumentView(kind) {
17
17
  return kind === "code" || kind === "csv" || kind === "notebook";
18
18
  }
19
- function FilePreview({ open, node, siblings, storage, getPresignedUrl, onNavigate, onClose, onDownload, onCopyUri, showSecurityIndicator = true }) {
19
+ function FilePreview({ open, node, siblings, storage, getPresignedUrl, onNavigate, onClose, onDownload, onCopyUri }) {
20
20
  const t = useStrings().fileExplorer;
21
21
  const [style, setStyle] = useLocalStorage("fileExplorer.previewStyle", "quicklook");
22
22
  const [zoom, setZoom] = useState(100);
23
- const { url, loading: urlLoading, error: urlError } = usePresignedUrl(open ? node : null, getPresignedUrl, {
23
+ const { url, loading: urlLoading, error: urlError } = usePresignedUrl(node, getPresignedUrl, {
24
24
  enabled: open,
25
25
  linkErrorMessage: t.preview.linkError
26
26
  });
@@ -76,7 +76,7 @@ function FilePreview({ open, node, siblings, storage, getPresignedUrl, onNavigat
76
76
  className: "truncate font-semibold",
77
77
  children: node.name
78
78
  }),
79
- showSecurityIndicator && url && /* @__PURE__ */ jsx(StreamedBadge, {})
79
+ url && /* @__PURE__ */ jsx(StreamedBadge, {})
80
80
  ]
81
81
  }), /* @__PURE__ */ jsxs("div", {
82
82
  className: "flex shrink-0 items-center gap-1.5",
@@ -9,7 +9,7 @@ export interface NotebookOutput {
9
9
  evalue?: string;
10
10
  traceback?: string[];
11
11
  }
12
- export interface NotebookCell {
12
+ interface NotebookCell {
13
13
  cell_type: string;
14
14
  source: string | string[];
15
15
  outputs?: NotebookOutput[];
@@ -22,3 +22,4 @@ export declare function parseNotebook(text: string): ParsedNotebook | null;
22
22
  /** nbformat `source`/`text` is a string or an array of lines; normalize to one string. */
23
23
  export declare function joinSource(source: unknown): string;
24
24
  export declare function stripAnsi(text: string): string;
25
+ export {};
@@ -1,13 +1,14 @@
1
- interface ObjectBlobState {
1
+ interface BlobResult {
2
2
  blobUrl: string | null;
3
+ tooLarge: boolean;
4
+ }
5
+ interface ObjectBlobState extends BlobResult {
3
6
  loading: boolean;
4
7
  error: string | null;
5
- tooLarge: boolean;
6
8
  corsError: boolean;
7
9
  }
8
10
  /** Fetch an object into a blob URL with a forced MIME type so e.g. PDFs render inline even when served as octet-stream; cached per `cacheKey` so repeated mounts share one download and one blob URL, revoked when the cache drops it. */
9
- export declare function useObjectBlobUrl(url: string | null, { enabled, maxBytes, knownSize, mimeType, cacheKey, }: {
10
- enabled: boolean;
11
+ export declare function useObjectBlobUrl(url: string, { maxBytes, knownSize, mimeType, cacheKey, }: {
11
12
  maxBytes: number;
12
13
  knownSize?: number | undefined;
13
14
  mimeType: string;
@@ -3,44 +3,40 @@ import { fetchPreviewObject, loadPreviewContent, peekPreviewContent } from "./pr
3
3
  import { readCappedBytes } from "./readCappedBytes.js";
4
4
  import { useEffect, useState } from "react";
5
5
  //#region src/file-explorer/FilePreview/useObjectBlobUrl.ts
6
+ var IDLE = {
7
+ blobUrl: null,
8
+ loading: false,
9
+ error: null,
10
+ tooLarge: false,
11
+ corsError: false
12
+ };
6
13
  function toState(result) {
7
14
  return {
8
- blobUrl: result.blobUrl,
9
- loading: false,
10
- error: null,
11
- tooLarge: result.tooLarge,
12
- corsError: false
15
+ ...IDLE,
16
+ ...result
13
17
  };
14
18
  }
15
19
  function disposeBlob(result) {
16
20
  if (result.blobUrl) URL.revokeObjectURL(result.blobUrl);
17
21
  }
18
22
  /** Fetch an object into a blob URL with a forced MIME type so e.g. PDFs render inline even when served as octet-stream; cached per `cacheKey` so repeated mounts share one download and one blob URL, revoked when the cache drops it. */
19
- function useObjectBlobUrl(url, { enabled, maxBytes, knownSize, mimeType, cacheKey }) {
23
+ function useObjectBlobUrl(url, { maxBytes, knownSize, mimeType, cacheKey }) {
20
24
  const [state, setState] = useState({
21
- blobUrl: null,
22
- loading: enabled,
23
- error: null,
24
- tooLarge: false,
25
- corsError: false
25
+ ...IDLE,
26
+ loading: true
26
27
  });
27
28
  const tooLarge = typeof knownSize === "number" && knownSize > maxBytes;
28
- const key = enabled ? cacheKey : null;
29
29
  useEffect(() => {
30
- if (!enabled || !url || !key) return;
31
30
  if (tooLarge) {
32
31
  setState({
33
- blobUrl: null,
34
- loading: false,
35
- error: null,
36
- tooLarge: true,
37
- corsError: false
32
+ ...IDLE,
33
+ tooLarge: true
38
34
  });
39
35
  return;
40
36
  }
41
37
  let cancelled = false;
42
- const record = loadPreviewContent(key, async (signal) => {
43
- const response = await fetchPreviewObject(key, url, signal);
38
+ const record = loadPreviewContent(cacheKey, async (signal) => {
39
+ const response = await fetchPreviewObject(cacheKey, url, signal);
44
40
  const { bytes, tooLarge: exceeded } = await readCappedBytes(response, maxBytes);
45
41
  if (exceeded) return {
46
42
  blobUrl: null,
@@ -52,21 +48,16 @@ function useObjectBlobUrl(url, { enabled, maxBytes, knownSize, mimeType, cacheKe
52
48
  };
53
49
  }, disposeBlob);
54
50
  setState(record.value ? toState(record.value) : {
55
- blobUrl: null,
56
- loading: true,
57
- error: null,
58
- tooLarge: false,
59
- corsError: false
51
+ ...IDLE,
52
+ loading: true
60
53
  });
61
54
  record.promise.then((result) => {
62
55
  if (!cancelled) setState(toState(result));
63
56
  }).catch((err) => {
64
57
  if (err instanceof DOMException && err.name === "AbortError") return;
65
58
  if (!cancelled) setState({
66
- blobUrl: null,
67
- loading: false,
59
+ ...IDLE,
68
60
  error: err instanceof Error ? err.message : "Failed to load file",
69
- tooLarge: false,
70
61
  corsError: err instanceof CorsError
71
62
  });
72
63
  });
@@ -75,20 +66,16 @@ function useObjectBlobUrl(url, { enabled, maxBytes, knownSize, mimeType, cacheKe
75
66
  };
76
67
  }, [
77
68
  url,
78
- enabled,
79
- key,
69
+ cacheKey,
80
70
  maxBytes,
81
71
  mimeType,
82
72
  tooLarge
83
73
  ]);
84
74
  if (tooLarge) return {
85
- blobUrl: null,
86
- loading: false,
87
- error: null,
88
- tooLarge: true,
89
- corsError: false
75
+ ...IDLE,
76
+ tooLarge: true
90
77
  };
91
- const cached = key ? peekPreviewContent(key) : void 0;
78
+ const cached = peekPreviewContent(cacheKey);
92
79
  return cached ? toState(cached) : state;
93
80
  }
94
81
  //#endregion
@@ -12,8 +12,7 @@ interface ObjectTextState extends TextResult {
12
12
  * download it once. `maxLines` stops the read early and `maxBytes` cuts short what it
13
13
  * hasn't. `tail` reads the end of the object instead of the start, and caches
14
14
  * separately because the same object yields different text. */
15
- export declare function useObjectText(url: string | null, { enabled, maxBytes, maxLines, knownSize, cacheKey, tail, }: {
16
- enabled: boolean;
15
+ export declare function useObjectText(url: string, { maxBytes, maxLines, knownSize, cacheKey, tail, }: {
17
16
  maxBytes: number;
18
17
  maxLines?: number | undefined;
19
18
  knownSize?: number | undefined;
@@ -37,23 +37,22 @@ async function readObjectText(signal, { url, cacheKey, maxBytes, maxLines, known
37
37
  * download it once. `maxLines` stops the read early and `maxBytes` cuts short what it
38
38
  * hasn't. `tail` reads the end of the object instead of the start, and caches
39
39
  * separately because the same object yields different text. */
40
- function useObjectText(url, { enabled, maxBytes, maxLines, knownSize, cacheKey, tail = false }) {
40
+ function useObjectText(url, { maxBytes, maxLines, knownSize, cacheKey, tail = false }) {
41
41
  const tooLarge = maxLines === void 0 && !tail && typeof knownSize === "number" && knownSize > maxBytes;
42
- const key = enabled ? tail ? `${cacheKey}#tail` : cacheKey : null;
42
+ const key = tail ? `${cacheKey}#tail` : cacheKey;
43
43
  const [state, setState] = useState({
44
44
  ...IDLE,
45
- loading: enabled
45
+ loading: true
46
46
  });
47
47
  const [stateKey, setStateKey] = useState(key);
48
48
  if (stateKey !== key) {
49
49
  setStateKey(key);
50
50
  setState({
51
51
  ...IDLE,
52
- loading: key !== null
52
+ loading: true
53
53
  });
54
54
  }
55
55
  useEffect(() => {
56
- if (!enabled || !url || !key) return;
57
56
  if (tooLarge) {
58
57
  setState({
59
58
  ...IDLE,
@@ -89,7 +88,6 @@ function useObjectText(url, { enabled, maxBytes, maxLines, knownSize, cacheKey,
89
88
  };
90
89
  }, [
91
90
  url,
92
- enabled,
93
91
  key,
94
92
  maxBytes,
95
93
  maxLines,
@@ -102,7 +100,7 @@ function useObjectText(url, { enabled, maxBytes, maxLines, knownSize, cacheKey,
102
100
  ...IDLE,
103
101
  tooLarge: true
104
102
  };
105
- const cached = key ? peekPreviewContent(key) : void 0;
103
+ const cached = peekPreviewContent(key);
106
104
  return cached ? toState(cached) : state;
107
105
  }
108
106
  //#endregion
@@ -111,7 +111,7 @@ function FileRow({ node, icon, rowId, rowIndex, active, checked, canPreview, get
111
111
  children: [/* @__PURE__ */ jsx("div", {
112
112
  className: "truncate text-[13px] font-medium leading-tight text-(--theme-app)",
113
113
  children: node.displayName || node.name
114
- }), !isDir && node.contentType && /* @__PURE__ */ jsx("div", {
114
+ }), isTwoLineRow(node) && /* @__PURE__ */ jsx("div", {
115
115
  className: "truncate text-xs leading-tight text-(--theme-muted-text-color)",
116
116
  children: node.contentType
117
117
  })]
@@ -1,4 +1,5 @@
1
1
  import type { OpenMenu, RowMenuItem } from '../list/index.js';
2
+ import { type LoadMoreState } from './LoadMoreRow.js';
2
3
  import type { TreeNode } from './lib/types.js';
3
4
  interface Span {
4
5
  start: number;
@@ -10,13 +11,10 @@ export declare function spacerHeights(items: readonly Span[], totalSize: number,
10
11
  top: number;
11
12
  bottom: number;
12
13
  };
13
- interface VirtualRow {
14
- index: number;
15
- start: number;
16
- size: number;
17
- }
18
14
  interface FileRowsWindowProps {
19
- items: readonly VirtualRow[];
15
+ items: readonly {
16
+ index: number;
17
+ }[];
20
18
  padTop: number;
21
19
  padBottom: number;
22
20
  nodes: TreeNode[];
@@ -25,12 +23,7 @@ interface FileRowsWindowProps {
25
23
  activeIndex: number;
26
24
  rowIdPrefix: string;
27
25
  scrollEl: HTMLElement | null;
28
- loadMore?: {
29
- loading: boolean;
30
- error: string | undefined;
31
- auto: boolean;
32
- onLoadMore: () => void;
33
- } | undefined;
26
+ loadMore?: LoadMoreState | undefined;
34
27
  getItems: (node: TreeNode) => RowMenuItem[];
35
28
  openMenu: OpenMenu;
36
29
  onOpen: (node: TreeNode) => void;
@@ -1,4 +1,5 @@
1
1
  import { type OpenMenu, type RowMenuItem } from '../list/index.js';
2
+ import { type LoadMoreState } from './LoadMoreRow.js';
2
3
  import type { TreeNode } from './lib/types.js';
3
4
  interface FileTableProps {
4
5
  nodes: TreeNode[];
@@ -12,12 +13,7 @@ interface FileTableProps {
12
13
  onToggleCheck: (node: TreeNode, checked: boolean) => void;
13
14
  onPreview: (node: TreeNode) => void;
14
15
  /** Set when the folder has pages left to fetch. */
15
- loadMore?: {
16
- loading: boolean;
17
- error: string | undefined;
18
- auto: boolean;
19
- onLoadMore: () => void;
20
- } | undefined;
16
+ loadMore?: LoadMoreState | undefined;
21
17
  }
22
18
  /** The file table. Give it a `key` of the folder path: a folder change must reset
23
19
  * the cursor, the scroll offset and the virtualizer's measurements, and a remount
@@ -1,5 +1,13 @@
1
1
  /** The virtualizer sizes this row from the constant, never by measuring it. */
2
2
  export declare const LOAD_MORE_ROW_HEIGHT = 45;
3
+ /** A folder's paging state, as the table's sentinel row needs it. */
4
+ export interface LoadMoreState {
5
+ loading: boolean;
6
+ error: string | undefined;
7
+ /** False past the auto-load cap, where the user asks for each page. */
8
+ auto: boolean;
9
+ onLoadMore: () => void;
10
+ }
3
11
  interface LoadMoreRowProps {
4
12
  rowId: string;
5
13
  rowIndex: number;
@@ -2,7 +2,6 @@ import type { TreeNode } from './lib/types.js';
2
2
  /** Row pitch, with `space-y-0.5` folded in: absolutely positioned rows cannot use
3
3
  * sibling margins. useTreePitchAudit warns if this drifts from the DOM. */
4
4
  export declare const TREE_ROW_HEIGHT = 30;
5
- export declare const TREE_SKELETON_HEIGHT = 64;
6
5
  interface TreeViewProps {
7
6
  rootNodes: TreeNode[];
8
7
  parentToChildrenMap: Record<string, TreeNode[]>;
@@ -16,8 +16,9 @@ var INDENT_PER_LEVEL = 21;
16
16
  var GUIDE_OFFSET = 9;
17
17
  var ROW_PAD_DIRECTORY = 8;
18
18
  var ROW_PAD_FILE = 28;
19
+ var TREE_SKELETON_HEIGHT = 64;
19
20
  function rowHeight(row) {
20
- return row.kind === "skeleton" ? 64 : 30;
21
+ return row.kind === "skeleton" ? TREE_SKELETON_HEIGHT : 30;
21
22
  }
22
23
  /** The sidebar tree, flattened and windowed. The flat array is the single source
23
24
  * of truth for rendering, arrow keys and ARIA. */
@@ -194,9 +195,6 @@ function RowIcons({ isDirectory, isExpanded, customIcon }) {
194
195
  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
196
  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
  }
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
198
  /** Click-only, unlike the table's sentinel: the tree is a navigation aid, and
201
199
  * growing it automatically as the user scrolls is what the cap exists to prevent. */
202
200
  function TreeMoreRow({ depth, hiddenFiles, partial, loading, failed, onReveal }) {
@@ -220,6 +218,9 @@ function TreeMoreRow({ depth, hiddenFiles, partial, loading, failed, onReveal })
220
218
  })]
221
219
  });
222
220
  }
221
+ /** Sized by the slot, not by its contents: react-loading-skeleton wraps each bar
222
+ * in an inline span, so a bar occupies a line box taller than its `height` and
223
+ * the overflow would sit on top of the absolutely positioned row below. */
223
224
  function TreeSkeletonRow({ depth }) {
224
225
  return /* @__PURE__ */ jsxs("div", {
225
226
  className: "relative h-full overflow-hidden",
@@ -312,7 +313,6 @@ function dispatchRowKey(e, { node, isDirectory, isExpanded, onSelect, onToggleEx
312
313
  var TreeRowItem = memo(function TreeRowItem({ row, isSelected, onToggleExpand, onSelect, onDropToFolder, onContextMenu, onKeyDown }) {
313
314
  const { node, depth, isExpanded } = row;
314
315
  const isDirectory = node.type === "directory";
315
- const isStorageNode = !!node.displayName && node.storageId;
316
316
  const { dragActive, dragHandlers } = useSpringLoadedDrop({
317
317
  node,
318
318
  dropPath: isDirectory ? node.path : getParentPath(node.path),
@@ -335,7 +335,7 @@ var TreeRowItem = memo(function TreeRowItem({ row, isSelected, onToggleExpand, o
335
335
  onDelegate: onKeyDown
336
336
  });
337
337
  const customIcon = getNodeIcon(node);
338
- const label = isDirectory && isStorageNode && node.displayName ? node.displayName : node.name;
338
+ const label = isDirectory && node.storageId && node.displayName ? node.displayName : node.name;
339
339
  return /* @__PURE__ */ jsxs("div", {
340
340
  className: cx("relative h-full rounded-md transition-[box-shadow,background-color] duration-150", dragActive && "ring-1 ring-(--theme-link)/60"),
341
341
  ...dragHandlers,
@@ -91,7 +91,7 @@ function getNodeIcon(node, storageIconUrl, storageIconAlt = "Storage icon") {
91
91
  }
92
92
  });
93
93
  }
94
- if (!isStorageNode && !isUserNode && node.type === "directory") return null;
94
+ if (!isStorageNode && node.type === "directory") return null;
95
95
  if (node.contentType) {
96
96
  for (const { pattern, icon } of contentTypeIconMap) if (pattern.test(node.contentType)) return icon;
97
97
  }
@@ -253,7 +253,6 @@ function CorsPropagatingPreview() {
253
253
  const [elapsedMs, setElapsedMs] = useState(0);
254
254
  useEffect(() => {
255
255
  const start = Date.now();
256
- setElapsedMs(0);
257
256
  const id = setInterval(() => {
258
257
  setElapsedMs(Date.now() - start);
259
258
  }, 500);
@@ -523,8 +522,7 @@ async function addCorsRules({ provision, notify, strings, csp, type, username, s
523
522
  const deadline = Date.now() + pollBudgetMs;
524
523
  await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
525
524
  while (Date.now() < deadline) {
526
- const result = await onRefresh({ silent: true });
527
- if (!result || !result.corsError) {
525
+ if (!(await onRefresh({ silent: true }))?.corsError) {
528
526
  await onRefresh();
529
527
  notify.success(strings.corsAdded);
530
528
  return;
@@ -8,7 +8,6 @@ export interface FileExplorerDeleteFilesResult {
8
8
  Errors?: unknown[] | undefined;
9
9
  }
10
10
  export interface IFileExplorerClient {
11
- getStorageName(): string;
12
11
  getExpiresAt(): Date;
13
12
  getFile(input: unknown): Promise<FileExplorerGetFileResult>;
14
13
  listDirectory(input: unknown): Promise<unknown>;
@@ -20,7 +19,6 @@ export interface IFileExplorerClient {
20
19
  }) => void, abortController?: AbortController): Promise<unknown>;
21
20
  }
22
21
  export interface IFileExplorerProvider {
23
- createClient: ((organization: string, username: string, name: string) => Promise<IFileExplorerClient | null>) | null;
24
22
  convertDataToStorageObjects?(data: unknown): TStorageObject[];
25
23
  /** Absent ⇒ the explorer treats one page as the whole directory. Method syntax for
26
24
  * the same bivariance reason as the two above. */
@@ -8,7 +8,6 @@ export type TStorage = {
8
8
  type?: string;
9
9
  created?: string;
10
10
  modified?: string;
11
- region?: string;
12
11
  csp?: string;
13
12
  imageUrl?: string;
14
13
  canWrite?: boolean | undefined;
@@ -10,14 +10,13 @@ function formatFileSize(bytes) {
10
10
  "TB"
11
11
  ];
12
12
  const i = Math.floor(Math.log(bytes) / Math.log(k));
13
- return Number.parseFloat((bytes / k ** i).toFixed(2)) + " " + sizes[i];
13
+ return `${Number.parseFloat((bytes / k ** i).toFixed(2))} ${sizes[i]}`;
14
14
  }
15
15
  function getFileExtension(filename) {
16
16
  return filename.match(/\.([a-zA-Z0-9]+)$/)?.[1]?.toLowerCase() ?? null;
17
17
  }
18
18
  function getNodeName(node) {
19
- const parts = removeTrailingSlash(node.path || "").split("/");
20
- return parts[parts.length - 1] ?? "";
19
+ return removeTrailingSlash(node.path).split("/").pop() ?? "";
21
20
  }
22
21
  function toNode(entry, storage, storagePath, storageName) {
23
22
  const relativePath = removeLeadingSlash(entry.path);
@@ -79,8 +78,8 @@ function createTreeBuilder() {
79
78
  };
80
79
  directories.set(dirPath, dir);
81
80
  }
82
- for (let i = dir.entries.length; i < entries.length; i++) {
83
- const node = toNode(entries[i], storage, storagePath, storageName);
81
+ for (const entry of entries.slice(dir.entries.length)) {
82
+ const node = toNode(entry, storage, storagePath, storageName);
84
83
  if (node.path === dirPath) continue;
85
84
  if (node.type === "directory") dir.directories.push(node);
86
85
  else dir.files.push(node);
@@ -124,14 +123,14 @@ function createTreeBuilder() {
124
123
  parentToChildrenMap: {},
125
124
  rootNodes: []
126
125
  };
127
- if (storages.length === 1 && !options.forceUserHierarchy) {
128
- const storage = storages[0];
129
- const storagePath = ensureTrailingSlash(storage.user ? `${storage.user}/${storage.name}` : storage.name);
130
- tree.rootNodes.push(addStorage(storage, storagePath, options, tree));
126
+ const onlyStorage = storages.length === 1 ? storages[0] : void 0;
127
+ if (onlyStorage && !options.forceUserHierarchy) {
128
+ const storagePath = ensureTrailingSlash(onlyStorage.user ? `${onlyStorage.user}/${onlyStorage.name}` : onlyStorage.name);
129
+ tree.rootNodes.push(addStorage(onlyStorage, storagePath, options, tree));
131
130
  } else {
132
131
  const userGroups = {};
133
132
  for (const storage of storages) {
134
- const username = storage.user ?? "";
133
+ const username = storage.user;
135
134
  if (!userGroups[username]) userGroups[username] = [];
136
135
  userGroups[username].push(storage);
137
136
  }
@@ -159,7 +158,7 @@ function createTreeBuilder() {
159
158
  };
160
159
  }
161
160
  function getNodeChildren(parentToChildrenMap, parentPath) {
162
- return parentToChildrenMap[parentPath.endsWith("/") ? parentPath : parentPath + "/"] || [];
161
+ return parentToChildrenMap[parentPath.endsWith("/") ? parentPath : `${parentPath}/`] || [];
163
162
  }
164
163
  function getParentPath(currentPath) {
165
164
  return ensureTrailingSlash(removeLastPathSegment(currentPath));
@@ -183,13 +182,13 @@ async function getUploadNodesFromDataTransferItems(items) {
183
182
  uploadNodes.push({
184
183
  name: file.name,
185
184
  file,
186
- relativePath: path ? path + file.name : file.name
185
+ relativePath: path + file.name
187
186
  });
188
187
  totalSize += file.size;
189
188
  resolve();
190
189
  }, reject);
191
190
  else if (entry.isDirectory) entry.createReader().readEntries(async (entries) => {
192
- const newPath = path ? path + entry.name + "/" : entry.name + "/";
191
+ const newPath = `${path}${entry.name}/`;
193
192
  if (entries.length === 0) uploadNodes.push({
194
193
  name: entry.name,
195
194
  file: new Blob(),
@@ -213,9 +212,7 @@ async function getUploadNodesFromDataTransferItems(items) {
213
212
  };
214
213
  }
215
214
  function calculateUploadNodesTotalSize(uploadNodes) {
216
- return uploadNodes.reduce((total, node) => {
217
- return total + (node.file?.size || 0);
218
- }, 0);
215
+ return uploadNodes.reduce((total, node) => total + node.file.size, 0);
219
216
  }
220
217
  function getObjectKeyFromNode(node) {
221
218
  return node.relativePath || node.path.split("/").filter(Boolean).slice(1).join("/");
@@ -261,12 +258,10 @@ function removeLastPathSegment(path) {
261
258
  return path.replace(/\/[^/]+\/?$/, "");
262
259
  }
263
260
  function ensureTrailingSlash(path) {
264
- return path.endsWith("/") ? path : path + "/";
261
+ return path.endsWith("/") ? path : `${path}/`;
265
262
  }
266
263
  function isClientValid(client) {
267
- if (!client?.getExpiresAt) return false;
268
- const expiresAt = client.getExpiresAt();
269
- return expiresAt && expiresAt > /* @__PURE__ */ new Date();
264
+ return client.getExpiresAt() > /* @__PURE__ */ new Date();
270
265
  }
271
266
  function getUploadSessionETA(totalLoadedBytes, totalSizeBytes, lastProgressTime, lastProgressBytes) {
272
267
  const now = Date.now();