@parallelworks/ui 0.21.1 → 0.22.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/components/Provider.d.ts +1 -0
- package/dist/components/Provider.js +1 -0
- package/dist/file-explorer/FileExplorer.js +34 -0
- package/dist/file-explorer/FilePreview/Viewer.js +33 -18
- package/dist/file-explorer/FilePreview/previewType.d.ts +3 -1
- package/dist/file-explorer/FilePreview/previewType.js +18 -1
- package/dist/file-explorer/FilePreview/readCappedBytes.d.ts +4 -0
- package/dist/file-explorer/FilePreview/readCappedBytes.js +7 -1
- package/dist/file-explorer/FilePreview/useObjectText.js +3 -3
- package/dist/file-explorer/index.d.ts +1 -1
- package/dist/file-explorer/lib/types.d.ts +11 -0
- package/package.json +2 -2
|
@@ -137,6 +137,7 @@ var DEFAULTS = {
|
|
|
137
137
|
errorTitle: "Couldn't load preview",
|
|
138
138
|
previewError: "This file couldn't be previewed in your browser.",
|
|
139
139
|
tooLarge: "This file is too large to preview in the browser.",
|
|
140
|
+
tooLargeSize: (size) => `This file is ${size}, too large to preview in the browser.`,
|
|
140
141
|
downloadInstead: "Download instead",
|
|
141
142
|
archiveTitle: "Can't preview archives",
|
|
142
143
|
archiveMessage: "Archive files can't be previewed in the browser. Download the file to view its contents.",
|
|
@@ -187,6 +187,40 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
|
|
|
187
187
|
const [accessDrawerOpen, setAccessDrawerOpen] = useState(false);
|
|
188
188
|
const [connectionIssuesMessage, updateConnectionIssuesMessage] = useState(/* @__PURE__ */ new Map());
|
|
189
189
|
const [hasCorsIssue, setHasCorsIssue] = useState(/* @__PURE__ */ new Map());
|
|
190
|
+
const storageRoots = useMemo(() => {
|
|
191
|
+
const roots = /* @__PURE__ */ new Map();
|
|
192
|
+
for (const node of rootNodes) {
|
|
193
|
+
const candidates = node.root ? [node] : parentToChildrenMap[node.path] ?? [];
|
|
194
|
+
for (const root of candidates) if (root.root && root.storageId) roots.set(root.storageId, root.path);
|
|
195
|
+
}
|
|
196
|
+
return roots;
|
|
197
|
+
}, [rootNodes, parentToChildrenMap]);
|
|
198
|
+
const seenStorageRootsRef = useRef(storageRoots);
|
|
199
|
+
const forgetStorages = useEffectEvent((removed) => {
|
|
200
|
+
const isUnder = (path) => removed.some(([, root]) => path !== root && path.startsWith(root));
|
|
201
|
+
const isWithin = (path) => removed.some(([, root]) => path.startsWith(root));
|
|
202
|
+
for (const path of [...fetchesInFlightRef.current.keys()]) if (isWithin(path)) fetchesInFlightRef.current.delete(path);
|
|
203
|
+
setListings((prev) => removed.reduce((next, [, root]) => clearListingsUnder(next, root), prev));
|
|
204
|
+
setLoadingPaths((prev) => new Set([...prev].filter((path) => !isWithin(path))));
|
|
205
|
+
setExpandedPaths((prev) => new Set([...prev].filter((path) => !isUnder(path))));
|
|
206
|
+
const withoutRemoved = (prev) => {
|
|
207
|
+
const next = new Map(prev);
|
|
208
|
+
for (const [id] of removed) next.delete(id);
|
|
209
|
+
return next;
|
|
210
|
+
};
|
|
211
|
+
updateConnectionIssuesMessage(withoutRemoved);
|
|
212
|
+
setHasCorsIssue(withoutRemoved);
|
|
213
|
+
const selectedRoot = removed.find(([, root]) => normalizedSelectedPath.startsWith(root))?.[1];
|
|
214
|
+
if (selectedRoot) {
|
|
215
|
+
const parent = getParentPath(selectedRoot);
|
|
216
|
+
setSelectedPath(treeMap[parent] ? parent : "");
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
useEffect(() => {
|
|
220
|
+
const removed = [...seenStorageRootsRef.current].filter(([id]) => !storageRoots.has(id));
|
|
221
|
+
seenStorageRootsRef.current = storageRoots;
|
|
222
|
+
if (removed.length > 0) forgetStorages(removed);
|
|
223
|
+
}, [storageRoots]);
|
|
190
224
|
const [uploadModalOpen, setUploadModalOpen] = useState(false);
|
|
191
225
|
const [createFolderTarget, setCreateFolderTarget] = useState(null);
|
|
192
226
|
const [pendingUpload, setPendingUpload] = useState(null);
|
|
@@ -8,7 +8,7 @@ import { positionKeys, withPositionKeys } from "../../components/keys.js";
|
|
|
8
8
|
import Loader from "../../components/Loader.js";
|
|
9
9
|
import Markdown from "../../components/Markdown.js";
|
|
10
10
|
import { formatFileSize, getFileExtension } from "../lib/utils.js";
|
|
11
|
-
import {
|
|
11
|
+
import { getMonacoLanguage, previewLimitsFor } from "./previewType.js";
|
|
12
12
|
import { CorsIssuePreview } from "../helpers.js";
|
|
13
13
|
import { joinSource, parseDelimited, parseNotebook, stripAnsi } from "./parse.js";
|
|
14
14
|
import { countLines } from "./readCappedBytes.js";
|
|
@@ -69,7 +69,8 @@ function FilePreviewViewer({ node, kind, url, urlLoading, urlError, zoom = 100,
|
|
|
69
69
|
node,
|
|
70
70
|
url,
|
|
71
71
|
zoom,
|
|
72
|
-
onDownload
|
|
72
|
+
onDownload,
|
|
73
|
+
storage
|
|
73
74
|
});
|
|
74
75
|
case "video": return /* @__PURE__ */ jsx(VideoView, {
|
|
75
76
|
node,
|
|
@@ -112,8 +113,12 @@ function FilePreviewViewer({ node, kind, url, urlLoading, urlError, zoom = 100,
|
|
|
112
113
|
default: return null;
|
|
113
114
|
}
|
|
114
115
|
}
|
|
115
|
-
function ImageView({ node, url, zoom, onDownload }) {
|
|
116
|
+
function ImageView({ node, url, zoom, onDownload, storage }) {
|
|
116
117
|
const [hasError, setHasError] = useState(false);
|
|
118
|
+
if (typeof node.size === "number" && node.size > previewLimitsFor(storage).imageBytes) return /* @__PURE__ */ jsx(TooLargeInline, {
|
|
119
|
+
size: node.size,
|
|
120
|
+
onDownload
|
|
121
|
+
});
|
|
117
122
|
if (hasError) return /* @__PURE__ */ jsx(PreviewError, { onDownload });
|
|
118
123
|
const fit = zoom === 100;
|
|
119
124
|
return /* @__PURE__ */ jsx("div", {
|
|
@@ -191,7 +196,7 @@ function PreviewError({ onDownload }) {
|
|
|
191
196
|
}
|
|
192
197
|
function PdfView({ node, url, onDownload, storage, onCorsRetry }) {
|
|
193
198
|
const { blobUrl, loading, error, tooLarge, corsError } = useObjectBlobUrl(url, {
|
|
194
|
-
maxBytes:
|
|
199
|
+
maxBytes: previewLimitsFor(storage).pdfBytes,
|
|
195
200
|
knownSize: node.size,
|
|
196
201
|
mimeType: "application/pdf",
|
|
197
202
|
cacheKey: node.path
|
|
@@ -199,6 +204,7 @@ function PdfView({ node, url, onDownload, storage, onCorsRetry }) {
|
|
|
199
204
|
return /* @__PURE__ */ jsx(PreviewFetchState, {
|
|
200
205
|
loading,
|
|
201
206
|
tooLarge,
|
|
207
|
+
size: node.size,
|
|
202
208
|
error,
|
|
203
209
|
corsError,
|
|
204
210
|
storage,
|
|
@@ -215,9 +221,10 @@ function CodeView({ node, url, onDownload, storage, onCorsRetry }) {
|
|
|
215
221
|
const t = useStrings().fileExplorer;
|
|
216
222
|
const language = getMonacoLanguage(node);
|
|
217
223
|
const [tail, setTail] = useState(false);
|
|
224
|
+
const limits = previewLimitsFor(storage);
|
|
218
225
|
const { text, loading, error, tooLarge, truncated, corsError } = useObjectText(url, {
|
|
219
|
-
maxBytes:
|
|
220
|
-
maxLines:
|
|
226
|
+
maxBytes: limits.codeBytes,
|
|
227
|
+
maxLines: limits.codeLines,
|
|
221
228
|
knownSize: node.size,
|
|
222
229
|
cacheKey: node.path,
|
|
223
230
|
tail
|
|
@@ -262,6 +269,7 @@ function CodeView({ node, url, onDownload, storage, onCorsRetry }) {
|
|
|
262
269
|
children: /* @__PURE__ */ jsx(PreviewFetchState, {
|
|
263
270
|
loading,
|
|
264
271
|
tooLarge,
|
|
272
|
+
size: node.size,
|
|
265
273
|
error,
|
|
266
274
|
corsError,
|
|
267
275
|
storage,
|
|
@@ -292,15 +300,17 @@ function toFencedCode(text, language) {
|
|
|
292
300
|
return `${fence}${language}\n${text}\n${fence}`;
|
|
293
301
|
}
|
|
294
302
|
function CsvView({ node, url, onDownload, storage, onCorsRetry }) {
|
|
303
|
+
const limits = previewLimitsFor(storage);
|
|
295
304
|
const { text, loading, error, tooLarge, corsError } = useObjectText(url, {
|
|
296
|
-
maxBytes:
|
|
297
|
-
maxLines:
|
|
305
|
+
maxBytes: limits.tabularBytes,
|
|
306
|
+
maxLines: limits.tabularRows + 2,
|
|
298
307
|
knownSize: node.size,
|
|
299
308
|
cacheKey: node.path
|
|
300
309
|
});
|
|
301
310
|
return /* @__PURE__ */ jsx(PreviewFetchState, {
|
|
302
311
|
loading,
|
|
303
312
|
tooLarge,
|
|
313
|
+
size: node.size,
|
|
304
314
|
error,
|
|
305
315
|
corsError,
|
|
306
316
|
storage,
|
|
@@ -308,18 +318,19 @@ function CsvView({ node, url, onDownload, storage, onCorsRetry }) {
|
|
|
308
318
|
onDownload,
|
|
309
319
|
children: /* @__PURE__ */ jsx(CsvTable, {
|
|
310
320
|
node,
|
|
311
|
-
text: text ?? ""
|
|
321
|
+
text: text ?? "",
|
|
322
|
+
maxRows: limits.tabularRows
|
|
312
323
|
})
|
|
313
324
|
});
|
|
314
325
|
}
|
|
315
|
-
function CsvTable({ node, text }) {
|
|
326
|
+
function CsvTable({ node, text, maxRows }) {
|
|
316
327
|
const t = useStrings().fileExplorer;
|
|
317
328
|
const delimiter = getFileExtension(node.name) === "tsv" ? " " : ",";
|
|
318
|
-
const rows = parseDelimited(text, delimiter,
|
|
329
|
+
const rows = parseDelimited(text, delimiter, maxRows + 2);
|
|
319
330
|
const header = rows[0] ?? [];
|
|
320
331
|
const body = rows.slice(1);
|
|
321
|
-
const shown = body.slice(0,
|
|
322
|
-
const truncated = body.length >
|
|
332
|
+
const shown = body.slice(0, maxRows);
|
|
333
|
+
const truncated = body.length > maxRows;
|
|
323
334
|
const columnCount = Math.max(header.length, ...shown.map((r) => r.length));
|
|
324
335
|
return /* @__PURE__ */ jsxs("div", {
|
|
325
336
|
className: "flex h-full w-full flex-col bg-(--theme-app-bg)",
|
|
@@ -355,13 +366,14 @@ function CsvTable({ node, text }) {
|
|
|
355
366
|
}
|
|
356
367
|
function NotebookView({ node, url, onDownload, storage, onCorsRetry }) {
|
|
357
368
|
const { text, loading, error, tooLarge, corsError } = useObjectText(url, {
|
|
358
|
-
maxBytes:
|
|
369
|
+
maxBytes: previewLimitsFor(storage).notebookBytes,
|
|
359
370
|
knownSize: node.size,
|
|
360
371
|
cacheKey: node.path
|
|
361
372
|
});
|
|
362
373
|
return /* @__PURE__ */ jsx(PreviewFetchState, {
|
|
363
374
|
loading,
|
|
364
375
|
tooLarge,
|
|
376
|
+
size: node.size,
|
|
365
377
|
error,
|
|
366
378
|
corsError,
|
|
367
379
|
storage,
|
|
@@ -445,7 +457,7 @@ function PreviewLoading() {
|
|
|
445
457
|
children: /* @__PURE__ */ jsx(Loader, { size: 36 })
|
|
446
458
|
});
|
|
447
459
|
}
|
|
448
|
-
function PreviewFetchState({ loading, tooLarge, error, corsError, storage, onCorsRetry, onDownload, children }) {
|
|
460
|
+
function PreviewFetchState({ loading, tooLarge, size, error, corsError, storage, onCorsRetry, onDownload, children }) {
|
|
449
461
|
if (loading) return /* @__PURE__ */ jsx(PreviewLoading, {});
|
|
450
462
|
if (corsError) return /* @__PURE__ */ jsx("div", {
|
|
451
463
|
className: "flex h-full w-full items-center justify-center p-6",
|
|
@@ -454,7 +466,10 @@ function PreviewFetchState({ loading, tooLarge, error, corsError, storage, onCor
|
|
|
454
466
|
onRefresh: onCorsRetry
|
|
455
467
|
})
|
|
456
468
|
});
|
|
457
|
-
if (tooLarge) return /* @__PURE__ */ jsx(TooLargeInline, {
|
|
469
|
+
if (tooLarge) return /* @__PURE__ */ jsx(TooLargeInline, {
|
|
470
|
+
size,
|
|
471
|
+
onDownload
|
|
472
|
+
});
|
|
458
473
|
if (error) return /* @__PURE__ */ jsx(PreviewError, { onDownload });
|
|
459
474
|
return /* @__PURE__ */ jsx(Fragment$1, { children });
|
|
460
475
|
}
|
|
@@ -486,7 +501,7 @@ function NotPreviewable({ icon, title, message, onDownload }) {
|
|
|
486
501
|
})
|
|
487
502
|
});
|
|
488
503
|
}
|
|
489
|
-
function TooLargeInline({ onDownload }) {
|
|
504
|
+
function TooLargeInline({ size, onDownload }) {
|
|
490
505
|
const t = useStrings().fileExplorer;
|
|
491
506
|
return /* @__PURE__ */ jsx("div", {
|
|
492
507
|
className: "flex h-full items-center justify-center p-6",
|
|
@@ -494,7 +509,7 @@ function TooLargeInline({ onDownload }) {
|
|
|
494
509
|
className: "flex max-w-md flex-col items-center gap-3 text-center",
|
|
495
510
|
children: [/* @__PURE__ */ jsx("div", {
|
|
496
511
|
className: "text-sm theme-muted-text",
|
|
497
|
-
children: t.preview.tooLarge
|
|
512
|
+
children: typeof size === "number" ? t.preview.tooLargeSize(formatFileSize(size)) : t.preview.tooLarge
|
|
498
513
|
}), onDownload && /* @__PURE__ */ jsxs("button", {
|
|
499
514
|
type: "button",
|
|
500
515
|
className: "btn btn-info cursor-pointer",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { TreeNode } from '../lib/types.js';
|
|
1
|
+
import type { PreviewLimits, TreeNode, TStorage } from '../lib/types.js';
|
|
2
2
|
import { getFileExtension } from '../lib/utils.js';
|
|
3
3
|
export { getFileExtension };
|
|
4
4
|
export type PreviewKind = 'image' | 'video' | 'audio' | 'pdf' | 'code' | 'csv' | 'notebook' | 'archive' | 'unsupported';
|
|
@@ -15,6 +15,8 @@ export declare const CSV_PREVIEW_MAX_ROWS = 500;
|
|
|
15
15
|
export declare const CODE_PREVIEW_MAX_LINES = 5000;
|
|
16
16
|
/** Fetched into a blob URL to render inline regardless of served content type; falls back to download above this. */
|
|
17
17
|
export declare const PDF_PREVIEW_MAX_BYTES: number;
|
|
18
|
+
export declare const DEFAULT_PREVIEW_LIMITS: PreviewLimits;
|
|
19
|
+
export declare function previewLimitsFor(storage: TStorage | null | undefined): PreviewLimits;
|
|
18
20
|
export declare function getMonacoLanguage(node: TreeNode): string;
|
|
19
21
|
/** Content type wins when present; extension is the fallback. */
|
|
20
22
|
export declare function getPreviewKind(node: TreeNode): PreviewKind;
|
|
@@ -12,6 +12,21 @@ var TABULAR_PREVIEW_MAX_BYTES = 20971520;
|
|
|
12
12
|
var CODE_PREVIEW_MAX_LINES = 5e3;
|
|
13
13
|
/** Fetched into a blob URL to render inline regardless of served content type; falls back to download above this. */
|
|
14
14
|
var PDF_PREVIEW_MAX_BYTES = 52428800;
|
|
15
|
+
var DEFAULT_PREVIEW_LIMITS = {
|
|
16
|
+
codeBytes: CODE_PREVIEW_MAX_BYTES,
|
|
17
|
+
codeLines: CODE_PREVIEW_MAX_LINES,
|
|
18
|
+
tabularBytes: TABULAR_PREVIEW_MAX_BYTES,
|
|
19
|
+
tabularRows: 500,
|
|
20
|
+
imageBytes: Number.POSITIVE_INFINITY,
|
|
21
|
+
pdfBytes: PDF_PREVIEW_MAX_BYTES,
|
|
22
|
+
notebookBytes: TABULAR_PREVIEW_MAX_BYTES
|
|
23
|
+
};
|
|
24
|
+
function previewLimitsFor(storage) {
|
|
25
|
+
return {
|
|
26
|
+
...DEFAULT_PREVIEW_LIMITS,
|
|
27
|
+
...storage?.previewLimits
|
|
28
|
+
};
|
|
29
|
+
}
|
|
15
30
|
var IMAGE_EXT = /* @__PURE__ */ new Set([
|
|
16
31
|
"png",
|
|
17
32
|
"jpg",
|
|
@@ -97,6 +112,8 @@ var LANGUAGE_BY_EXT = {
|
|
|
97
112
|
tex: "plaintext",
|
|
98
113
|
txt: "plaintext",
|
|
99
114
|
log: "plaintext",
|
|
115
|
+
out: "plaintext",
|
|
116
|
+
err: "plaintext",
|
|
100
117
|
gitignore: "plaintext"
|
|
101
118
|
};
|
|
102
119
|
var LANGUAGE_BY_FILENAME = {
|
|
@@ -148,4 +165,4 @@ function canPreview(node) {
|
|
|
148
165
|
return kind !== "unsupported" && kind !== "archive";
|
|
149
166
|
}
|
|
150
167
|
//#endregion
|
|
151
|
-
export { CODE_PREVIEW_MAX_BYTES, CODE_PREVIEW_MAX_LINES, PDF_PREVIEW_MAX_BYTES, PREVIEW_URL_EXPIRES_IN, TABULAR_PREVIEW_MAX_BYTES, canPreview, getFileExtension, getMonacoLanguage, getPreviewKind };
|
|
168
|
+
export { CODE_PREVIEW_MAX_BYTES, CODE_PREVIEW_MAX_LINES, DEFAULT_PREVIEW_LIMITS, PDF_PREVIEW_MAX_BYTES, PREVIEW_URL_EXPIRES_IN, TABULAR_PREVIEW_MAX_BYTES, canPreview, getFileExtension, getMonacoLanguage, getPreviewKind, previewLimitsFor };
|
|
@@ -19,6 +19,10 @@ export declare function lastLines(text: string, maxLines: number): {
|
|
|
19
19
|
text: string;
|
|
20
20
|
truncated: boolean;
|
|
21
21
|
};
|
|
22
|
+
/** Range header for reading the start of an object that is larger than `maxBytes`,
|
|
23
|
+
* one byte past the cap so the read still sees the overflow and cuts on a line, as it
|
|
24
|
+
* does for an unranged body. The bytes past it never leave the server. */
|
|
25
|
+
export declare function headRangeFor(knownSize: number | undefined, maxBytes: number): string | null;
|
|
22
26
|
/** Range header for reading the last `maxBytes` of an object. A closed range is a
|
|
23
27
|
* CORS-safelisted header value, so it travels without a preflight that a bucket's
|
|
24
28
|
* CORS rules may not answer; the suffix form is not safelisted, and is only reached
|
|
@@ -145,6 +145,12 @@ function lastLines(text, maxLines) {
|
|
|
145
145
|
truncated: false
|
|
146
146
|
};
|
|
147
147
|
}
|
|
148
|
+
/** Range header for reading the start of an object that is larger than `maxBytes`,
|
|
149
|
+
* one byte past the cap so the read still sees the overflow and cuts on a line, as it
|
|
150
|
+
* does for an unranged body. The bytes past it never leave the server. */
|
|
151
|
+
function headRangeFor(knownSize, maxBytes) {
|
|
152
|
+
return typeof knownSize === "number" && knownSize > maxBytes + 1 ? `bytes=0-${maxBytes}` : null;
|
|
153
|
+
}
|
|
148
154
|
/** Range header for reading the last `maxBytes` of an object. A closed range is a
|
|
149
155
|
* CORS-safelisted header value, so it travels without a preflight that a bucket's
|
|
150
156
|
* CORS rules may not answer; the suffix form is not safelisted, and is only reached
|
|
@@ -193,4 +199,4 @@ async function readTailText(response, maxBytes, maxLines, startsMidObject) {
|
|
|
193
199
|
};
|
|
194
200
|
}
|
|
195
201
|
//#endregion
|
|
196
|
-
export { countLines, lastLines, readCappedBytes, readCappedText, readTailText, tailRangeFor, tailWindow };
|
|
202
|
+
export { countLines, headRangeFor, lastLines, readCappedBytes, readCappedText, readTailText, tailRangeFor, tailWindow };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CorsError } from "../lib/errors.js";
|
|
2
2
|
import { fetchPreviewObject, loadPreviewContent, peekPreviewContent } from "./previewCache.js";
|
|
3
|
-
import { readCappedText, readTailText, tailRangeFor, tailWindow } from "./readCappedBytes.js";
|
|
3
|
+
import { headRangeFor, readCappedText, readTailText, tailRangeFor, tailWindow } from "./readCappedBytes.js";
|
|
4
4
|
import { useEffect, useState } from "react";
|
|
5
5
|
//#region src/file-explorer/FilePreview/useObjectText.ts
|
|
6
6
|
var IDLE = {
|
|
@@ -18,10 +18,10 @@ function toState(result) {
|
|
|
18
18
|
};
|
|
19
19
|
}
|
|
20
20
|
async function readObjectText(signal, { url, cacheKey, maxBytes, maxLines, knownSize, tail }) {
|
|
21
|
-
const range = tail ? tailRangeFor(knownSize, maxBytes) :
|
|
21
|
+
const range = tail ? tailRangeFor(knownSize, maxBytes) : headRangeFor(knownSize, maxBytes);
|
|
22
22
|
const response = await fetchPreviewObject(cacheKey, url, signal, range ? { Range: range } : void 0);
|
|
23
23
|
let startsMidObject = false;
|
|
24
|
-
if (range) {
|
|
24
|
+
if (tail && range) {
|
|
25
25
|
const window = tailWindow(response, maxBytes);
|
|
26
26
|
if (!window) throw new Error("range request was not honored");
|
|
27
27
|
startsMidObject = window.startsMidObject;
|
|
@@ -6,5 +6,5 @@ export { CorsError } from './lib/errors.js';
|
|
|
6
6
|
export type { FileExplorerDeleteFilesResult, FileExplorerGetFileResult, IFileExplorerClient, IFileExplorerProvider, } from './lib/fileExplorer.js';
|
|
7
7
|
export type { TListPageRequest } from './lib/paging.js';
|
|
8
8
|
export { LIST_PAGE_SIZE } from './lib/paging.js';
|
|
9
|
-
export type { ExplorerObject, TreeNode, TStorage, TStorageObject, } from './lib/types.js';
|
|
9
|
+
export type { ExplorerObject, PreviewLimits, TreeNode, TStorage, TStorageObject, } from './lib/types.js';
|
|
10
10
|
export { attachmentDisposition, collectObjectKeysToDelete, getObjectKeyFromNode, isClientValid, removeLeadingAndTrailingSlashes, } from './lib/utils.js';
|
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
import type { IFileExplorerClient, IFileExplorerProvider } from './fileExplorer.js';
|
|
2
|
+
/** Above these a file is not fetched for preview; the pane offers a download instead. */
|
|
3
|
+
export type PreviewLimits = {
|
|
4
|
+
codeBytes: number;
|
|
5
|
+
codeLines: number;
|
|
6
|
+
tabularBytes: number;
|
|
7
|
+
tabularRows: number;
|
|
8
|
+
imageBytes: number;
|
|
9
|
+
pdfBytes: number;
|
|
10
|
+
notebookBytes: number;
|
|
11
|
+
};
|
|
2
12
|
export type TStorage = {
|
|
3
13
|
id: string;
|
|
4
14
|
name: string;
|
|
@@ -14,6 +24,7 @@ export type TStorage = {
|
|
|
14
24
|
canUpload?: boolean | undefined;
|
|
15
25
|
canShare?: boolean | undefined;
|
|
16
26
|
canManageAccess?: boolean | undefined;
|
|
27
|
+
previewLimits?: Partial<PreviewLimits> | undefined;
|
|
17
28
|
};
|
|
18
29
|
/** A file the host is asked to name or download: its storage, key and name. */
|
|
19
30
|
export interface ExplorerObject {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@parallelworks/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.0",
|
|
4
4
|
"description": "React UI components: a theme contract, icons, primitives, lists, forms, a job graph, a code editor, a log viewer, a file explorer and an AI chat, each on its own subpath",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
"anser": "^2.3.5",
|
|
91
91
|
"classnames": "^2.5.1",
|
|
92
92
|
"fast-deep-equal": "^3.1.3",
|
|
93
|
-
"framer-motion": "^
|
|
93
|
+
"framer-motion": "^14.0.0",
|
|
94
94
|
"luxon": "^3.7.2",
|
|
95
95
|
"re-resizable": "^6.11.2",
|
|
96
96
|
"react-animate-height": "^3.2.4",
|