@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.
- package/dist/ai/adapter/openai/createOpenAIStream.js +3 -2
- package/dist/ai/agent/diff.d.ts +0 -1
- package/dist/ai/agent/diff.js +3 -2
- package/dist/ai/agent/toolfmt.js +2 -3
- package/dist/ai/ai.css +5 -0
- package/dist/ai/components/AllocationSelector.js +3 -2
- package/dist/ai/components/AttachmentManager.js +1 -1
- package/dist/ai/components/BranchNavigator.js +4 -2
- package/dist/ai/components/ChatInput.d.ts +2 -5
- package/dist/ai/components/ChatInput.js +10 -7
- package/dist/ai/components/ChatMessage.js +7 -7
- package/dist/ai/components/ChatSidebar.d.ts +0 -1
- package/dist/ai/components/ChatSidebar.js +4 -6
- package/dist/ai/components/ChatThread.js +5 -8
- package/dist/ai/components/ModelSelector.js +3 -2
- package/dist/ai/components/ShareDialog.js +6 -3
- package/dist/ai/components/SlashMenu.d.ts +0 -1
- package/dist/ai/components/agent/ActivityLine.d.ts +0 -1
- package/dist/ai/components/agent/ActivityLine.js +2 -2
- package/dist/ai/components/agent/AgentMessageParts.d.ts +1 -26
- package/dist/ai/components/agent/AgentMessageParts.js +4 -5
- package/dist/ai/core/ChatProvider.d.ts +2 -6
- package/dist/ai/core/ChatProvider.js +42 -67
- package/dist/ai/core/KeyboardShortcutsProvider.js +1 -1
- package/dist/ai/core/chatReducer.d.ts +0 -6
- package/dist/ai/core/chatReducer.js +1 -15
- package/dist/ai/core/useKeyboardShortcuts.d.ts +3 -1
- package/dist/ai/core/useKeyboardShortcuts.js +9 -11
- package/dist/ai/core/useSSEStream.d.ts +0 -1
- package/dist/ai/core/useSSEStream.js +5 -17
- package/dist/ai/strings.d.ts +0 -1
- package/dist/ai/strings.js +0 -1
- package/dist/ai/types.d.ts +1 -1
- package/dist/ai/ui/Avatar.d.ts +1 -2
- package/dist/ai/ui/Avatar.js +7 -18
- package/dist/ai/ui/Markdown.d.ts +1 -3
- package/dist/ai/ui/Markdown.js +1 -2
- package/dist/ai/ui/sidebar.d.ts +0 -1
- package/dist/ai/ui/sidebar.js +3 -1
- package/dist/ai/utils.js +1 -1
- package/dist/components/Avatar.js +4 -2
- package/dist/components/BareModal.js +1 -5
- package/dist/components/Breadcrumbs.js +21 -14
- package/dist/components/CollapsiblePanel.d.ts +1 -3
- package/dist/components/CollapsiblePanel.js +5 -5
- package/dist/components/Dropdown.d.ts +0 -4
- package/dist/components/Dropdown.js +5 -6
- package/dist/components/Listbox.js +14 -18
- package/dist/components/MultiSelect.js +5 -8
- package/dist/components/Pagination.js +1 -1
- package/dist/components/Provider.d.ts +0 -8
- package/dist/components/SwitchToggle.js +3 -5
- package/dist/components/Table.d.ts +1 -3
- package/dist/components/dropdownUtils.js +5 -6
- package/dist/components/relativeTime.d.ts +0 -7
- package/dist/components/relativeTime.js +1 -1
- package/dist/duration.js +1 -1
- package/dist/editor/Editor.d.ts +1 -1
- package/dist/editor/Editor.js +1 -1
- package/dist/editor/EditorField.d.ts +0 -1
- package/dist/editor/EditorField.js +8 -8
- package/dist/editor/FormCodePanel.js +14 -20
- package/dist/editor/yamlLanguage.js +2 -2
- package/dist/file-explorer/FileExplorer.js +56 -59
- package/dist/file-explorer/FileExplorerContext.d.ts +1 -2
- package/dist/file-explorer/FileExplorerContext.js +92 -125
- package/dist/file-explorer/FilePreview/Inline.d.ts +1 -2
- package/dist/file-explorer/FilePreview/Inline.js +2 -2
- package/dist/file-explorer/FilePreview/Viewer.js +0 -4
- package/dist/file-explorer/FilePreview/index.d.ts +1 -2
- package/dist/file-explorer/FilePreview/index.js +3 -3
- package/dist/file-explorer/FilePreview/parse.d.ts +2 -1
- package/dist/file-explorer/FilePreview/useObjectBlobUrl.d.ts +5 -4
- package/dist/file-explorer/FilePreview/useObjectBlobUrl.js +23 -36
- package/dist/file-explorer/FilePreview/useObjectText.d.ts +1 -2
- package/dist/file-explorer/FilePreview/useObjectText.js +5 -7
- package/dist/file-explorer/FileRow.js +1 -1
- package/dist/file-explorer/FileRowsWindow.d.ts +5 -12
- package/dist/file-explorer/FileTable.d.ts +2 -6
- package/dist/file-explorer/LoadMoreRow.d.ts +8 -0
- package/dist/file-explorer/TreeView.d.ts +0 -1
- package/dist/file-explorer/TreeView.js +6 -6
- package/dist/file-explorer/helpers.js +2 -4
- package/dist/file-explorer/lib/fileExplorer.d.ts +0 -2
- package/dist/file-explorer/lib/types.d.ts +0 -1
- package/dist/file-explorer/lib/utils.js +15 -20
- package/dist/file-explorer/toasts.js +36 -34
- package/dist/form/Fields/BooleanField.js +5 -4
- package/dist/form/Fields/CheckboxGroupField.js +5 -4
- package/dist/form/Fields/ColorField.js +5 -4
- package/dist/form/Fields/DropdownField.d.ts +0 -1
- package/dist/form/Fields/DropdownField.js +10 -13
- package/dist/form/Fields/DurationField.js +2 -2
- package/dist/form/Fields/LabelField.js +5 -5
- package/dist/form/Fields/MultiDropdownField.d.ts +1 -1
- package/dist/form/Fields/MultiDropdownField.js +8 -11
- package/dist/form/Fields/NumberField.js +6 -5
- package/dist/form/Fields/ObjectField.d.ts +1 -2
- package/dist/form/Fields/ObjectField.js +22 -21
- package/dist/form/Fields/RadioField.js +4 -3
- package/dist/form/Fields/RangeField.js +7 -6
- package/dist/form/Fields/StringField.js +2 -1
- package/dist/form/Fields/TextAreaField.js +2 -1
- package/dist/form/Form.d.ts +7 -8
- package/dist/form/Form.js +52 -78
- package/dist/form/SpecialTypes/DropdownWrapper.d.ts +0 -2
- package/dist/form/SpecialTypes/DropdownWrapper.js +1 -3
- package/dist/form/SpecialTypes/FormikCustomDropdown.d.ts +1 -3
- package/dist/form/SpecialTypes/FormikCustomDropdown.js +4 -7
- package/dist/form/SpecialTypes/FormikCustomInput.js +13 -11
- package/dist/form/Wizard/WizardContainer.js +8 -25
- package/dist/form/Wizard/WizardNavigation.d.ts +1 -1
- package/dist/form/Wizard/WizardNavigation.js +5 -6
- package/dist/form/Wizard/WizardStepContent.d.ts +1 -1
- package/dist/form/Wizard/WizardStepContent.js +2 -2
- package/dist/form/Wizard/WizardStepIndicator.js +7 -17
- package/dist/form/Wizard/types.d.ts +4 -46
- package/dist/form/Wizard/useWizardState.d.ts +2 -10
- package/dist/form/Wizard/useWizardState.js +42 -92
- package/dist/form/Wizard/utils.js +6 -11
- package/dist/form/lib.js +28 -54
- package/dist/form/types/fieldTypes.d.ts +4 -5
- package/dist/form/utils/dependentOptions.d.ts +6 -0
- package/dist/form/utils/dependentOptions.js +21 -0
- package/dist/form/utils/getParentValue.d.ts +2 -1
- package/dist/form/utils/getParentValue.js +10 -15
- package/dist/form/utils/getValueUsingPath.js +1 -1
- package/dist/graph/AnnotationBanner.d.ts +1 -1
- package/dist/graph/AnnotationBanner.js +1 -1
- package/dist/graph/Collapse.d.ts +3 -3
- package/dist/graph/Collapse.js +3 -8
- package/dist/graph/DependencyGraph.d.ts +5 -4
- package/dist/graph/DependencyGraph.js +172 -222
- package/dist/graph/JobSummary.d.ts +10 -8
- package/dist/graph/JobSummary.js +13 -13
- package/dist/graph/MatrixGroup.d.ts +5 -17
- package/dist/graph/MatrixGroup.js +11 -22
- package/dist/graph/TreeView.d.ts +1 -3
- package/dist/graph/TreeView.js +104 -491
- package/dist/graph/types.d.ts +3 -2
- package/dist/graph/util.d.ts +5 -0
- package/dist/graph/util.js +18 -0
- package/dist/icons.d.ts +2 -2
- package/dist/icons.js +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/list/ListViewControls.js +17 -35
- package/dist/list/RowContextMenu.js +7 -7
- package/dist/logviewer/LogViewer.js +7 -9
- package/dist/logviewer/segments.js +2 -2
- package/dist/styles/theme.css +1 -0
- package/dist/styles.css +1 -1
- package/dist/theme/index.js +5 -5
- package/package.json +28 -30
- 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,
|
|
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
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
9
|
-
|
|
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, {
|
|
23
|
+
function useObjectBlobUrl(url, { maxBytes, knownSize, mimeType, cacheKey }) {
|
|
20
24
|
const [state, setState] = useState({
|
|
21
|
-
|
|
22
|
-
loading:
|
|
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
|
-
|
|
34
|
-
|
|
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(
|
|
43
|
-
const response = await fetchPreviewObject(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
79
|
-
key,
|
|
69
|
+
cacheKey,
|
|
80
70
|
maxBytes,
|
|
81
71
|
mimeType,
|
|
82
72
|
tooLarge
|
|
83
73
|
]);
|
|
84
74
|
if (tooLarge) return {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
error: null,
|
|
88
|
-
tooLarge: true,
|
|
89
|
-
corsError: false
|
|
75
|
+
...IDLE,
|
|
76
|
+
tooLarge: true
|
|
90
77
|
};
|
|
91
|
-
const cached =
|
|
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
|
|
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, {
|
|
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 =
|
|
42
|
+
const key = tail ? `${cacheKey}#tail` : cacheKey;
|
|
43
43
|
const [state, setState] = useState({
|
|
44
44
|
...IDLE,
|
|
45
|
-
loading:
|
|
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:
|
|
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 =
|
|
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
|
-
}),
|
|
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
|
|
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" ?
|
|
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 &&
|
|
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 &&
|
|
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
|
-
|
|
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. */
|
|
@@ -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))
|
|
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
|
-
|
|
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 (
|
|
83
|
-
const node = toNode(
|
|
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
|
-
|
|
128
|
-
|
|
129
|
-
const storagePath = ensureTrailingSlash(
|
|
130
|
-
tree.rootNodes.push(addStorage(
|
|
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
|
|
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
|
|
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
|
-
|
|
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();
|