@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.
- package/dist/{chunks/memoryStore-CC91S5LP.js → ai/adapter/memoryStore.js} +2 -2
- package/dist/ai/adapter/mock/index.js +2 -80
- package/dist/ai/adapter/mock/mockAdapter.js +81 -0
- package/dist/ai/adapter/openai/createOpenAIStream.js +33 -0
- package/dist/ai/adapter/openai/errors.js +37 -0
- package/dist/ai/adapter/openai/index.js +5 -333
- package/dist/ai/adapter/openai/openaiAdapter.js +54 -0
- package/dist/ai/adapter/openai/responses.js +108 -0
- package/dist/ai/adapter/openai/sse.js +112 -0
- package/dist/{chunks/types-Yy0NrerF.js → ai/adapter/types.js} +1 -1
- package/dist/ai/agent/classifier.js +12 -0
- package/dist/ai/agent/diff.js +223 -0
- package/dist/ai/agent/diffHighlight.js +101 -0
- package/dist/ai/agent/permissions.js +29 -0
- package/dist/ai/agent/toolfmt.js +383 -0
- package/dist/ai/agent/transcript.js +127 -0
- package/dist/ai/components/AllocationSelector.js +56 -0
- package/dist/ai/components/AttachmentManager.js +144 -0
- package/dist/ai/components/AttachmentUpload.js +179 -0
- package/dist/ai/components/BlockedGroupBanner.js +29 -0
- package/dist/ai/components/BranchNavigator.js +57 -0
- package/dist/ai/components/ChatEmptyState.js +171 -0
- package/dist/ai/components/ChatInput.js +265 -0
- package/dist/ai/components/ChatLayout.js +15 -0
- package/dist/ai/components/ChatMessage.js +338 -0
- package/dist/ai/components/ChatMessageList.js +301 -0
- package/dist/ai/components/ChatSidebar.js +345 -0
- package/dist/ai/components/ChatThread.js +198 -0
- package/dist/ai/components/ComposerChrome.js +50 -0
- package/dist/ai/components/ComposerFrame.js +33 -0
- package/dist/ai/components/DragOverlay.js +20 -0
- package/dist/ai/components/ModelSelector.js +343 -0
- package/dist/ai/components/ShareDialog.js +260 -0
- package/dist/ai/components/SlashMenu.js +91 -0
- package/dist/ai/components/agent/ActivityLine.js +151 -0
- package/dist/ai/components/agent/AgentMessageParts.js +721 -0
- package/dist/ai/components/agent/PermissionPicker.js +38 -0
- package/dist/ai/components/attachmentKeys.js +10 -0
- package/dist/ai/core/ChatProvider.js +848 -0
- package/dist/ai/core/KeyboardShortcutsProvider.js +9 -0
- package/dist/ai/core/chatReducer.js +223 -0
- package/dist/ai/core/config.js +45 -0
- package/dist/ai/core/events.js +4 -0
- package/dist/ai/core/exportConversation.js +42 -0
- package/dist/ai/core/greeting.js +38 -0
- package/dist/ai/core/parts.js +84 -0
- package/dist/ai/core/useDragDrop.js +36 -0
- package/dist/ai/core/useKeyboardShortcuts.js +77 -0
- package/dist/ai/core/useSSEStream.js +98 -0
- package/dist/ai/index.js +43 -6644
- package/dist/ai/strings.js +175 -0
- package/dist/ai/ui/Avatar.js +35 -0
- package/dist/ai/ui/Dropdown.js +110 -0
- package/dist/ai/ui/Markdown.js +79 -0
- package/dist/ai/ui/Notice.js +77 -0
- package/dist/ai/ui/codeBreaks.js +36 -0
- package/dist/ai/ui/prose.js +13 -0
- package/dist/ai/ui/sidebar.js +148 -0
- package/dist/ai/utils.js +23 -0
- package/dist/components/AddMenu.js +46 -0
- package/dist/components/Avatar.js +46 -0
- package/dist/{chunks/BareModal-BtRiQezg.js → components/BareModal.js} +1 -1
- package/dist/components/Breadcrumbs.js +83 -0
- package/dist/components/Button.js +83 -0
- package/dist/components/Callout.js +23 -0
- package/dist/components/Card.js +31 -0
- package/dist/components/CollapsiblePanel.js +77 -0
- package/dist/{chunks/ConfirmModal-aOR4O1QE.js → components/ConfirmModal.js} +4 -9
- package/dist/components/CopyToClipboard.js +81 -0
- package/dist/{chunks/Markdown-CM-FenNE.js → components/CreateModal.js} +9 -116
- package/dist/components/DescriptionList.js +42 -0
- package/dist/components/Dropdown.js +349 -0
- package/dist/components/EmptyState.js +27 -0
- package/dist/components/Filter.js +32 -0
- package/dist/components/FilterPill.js +13 -0
- package/dist/components/IconButton.js +34 -0
- package/dist/{chunks/Indicator-hBSIxOPK.js → components/Indicator.js} +1 -1
- package/dist/components/Input.js +88 -0
- package/dist/components/LetterBadge.js +13 -0
- package/dist/components/Listbox.js +123 -0
- package/dist/{chunks/Loader-BXoXTZGc.js → components/Loader.js} +1 -1
- package/dist/components/Markdown.js +15 -0
- package/dist/components/MultiSelect.js +171 -0
- package/dist/components/Overview.js +39 -0
- package/dist/components/PageHeader.js +194 -0
- package/dist/components/Pagination.js +35 -0
- package/dist/{chunks/Provider-CDYurraE.js → components/Provider.js} +1 -1
- package/dist/{chunks/RequiredMark-DfLdYbYZ.js → components/RequiredMark.js} +1 -1
- package/dist/components/SectionHeader.js +11 -0
- package/dist/components/SettingModeOption.js +30 -0
- package/dist/components/SettingRow.js +35 -0
- package/dist/components/SettingsCard.js +31 -0
- package/dist/components/SettingsGroup.js +17 -0
- package/dist/components/Sort.js +42 -0
- package/dist/components/StatusBadge.js +63 -0
- package/dist/components/SwitchToggle.js +59 -0
- package/dist/{chunks/SwitchToggleSmall-FcsaYSTs.js → components/SwitchToggleSmall.js} +1 -1
- package/dist/{chunks/Table-DU2MW8xz.js → components/Table.js} +3 -3
- package/dist/components/Toggle.js +20 -0
- package/dist/{chunks/Tooltip-K5HJr4_p.js → components/Tooltip.js} +2 -27
- package/dist/{chunks/StatusBadge-C1Blx_cW.js → components/dropdownUtils.js} +2 -63
- package/dist/{chunks/focus-D_iZJTW1.js → components/focus.js} +1 -1
- package/dist/components/ghostButton.js +7 -0
- package/dist/components/inputClasses.js +4 -0
- package/dist/components/keys.js +27 -0
- package/dist/components/relativeTime.js +32 -0
- package/dist/{chunks/useCssIsDark-DHr05dG2.js → components/useCssIsDark.js} +1 -1
- package/dist/{chunks/duration-BOv8Phl8.js → duration.js} +1 -1
- package/dist/editor/Editor.js +13 -0
- package/dist/editor/EditorField.js +53 -0
- package/dist/editor/FormCodePanel.js +173 -0
- package/dist/{chunks/Monaco-DJ8MW-Dq.js → editor/Monaco.js} +4 -2
- package/dist/editor/index.js +6 -1
- package/dist/editor/themes.js +91 -0
- package/dist/editor/workers.js +29 -0
- package/dist/editor/yaml.js +15 -0
- package/dist/file-explorer/FileExplorer.js +1402 -0
- package/dist/file-explorer/FileExplorerContext.js +370 -0
- package/dist/{chunks/CodePreviewEditor-YqQBz1YL.js → file-explorer/FilePreview/CodePreviewEditor.js} +3 -2
- package/dist/file-explorer/FilePreview/Inline.js +45 -0
- package/dist/file-explorer/FilePreview/StreamedBadge.js +16 -0
- package/dist/file-explorer/FilePreview/Viewer.js +526 -0
- package/dist/file-explorer/FilePreview/index.js +266 -0
- package/dist/file-explorer/FilePreview/parse.js +63 -0
- package/dist/file-explorer/FilePreview/previewCache.js +98 -0
- package/dist/file-explorer/FilePreview/previewType.js +151 -0
- package/dist/file-explorer/FilePreview/readCappedBytes.js +196 -0
- package/dist/file-explorer/FilePreview/useObjectBlobUrl.js +95 -0
- package/dist/file-explorer/FilePreview/useObjectText.js +109 -0
- package/dist/file-explorer/FilePreview/usePresignedUrl.js +73 -0
- package/dist/file-explorer/FileRow.js +165 -0
- package/dist/file-explorer/FileRowsWindow.js +92 -0
- package/dist/file-explorer/FileTable.js +172 -0
- package/dist/file-explorer/FileTableHeaders.js +49 -0
- package/dist/file-explorer/LoadMoreRow.js +46 -0
- package/dist/file-explorer/TreeView.js +376 -0
- package/dist/file-explorer/helpers.js +537 -0
- package/dist/file-explorer/index.js +7 -5671
- package/dist/file-explorer/lib/batch.js +39 -0
- package/dist/file-explorer/lib/errors.js +9 -0
- package/dist/file-explorer/lib/paging.js +124 -0
- package/dist/file-explorer/lib/utils.js +322 -0
- package/dist/file-explorer/toasts.js +400 -0
- package/dist/file-explorer/treeRows.js +73 -0
- package/dist/file-explorer/useNearEnd.js +27 -0
- package/dist/form/FieldWrapper.js +54 -0
- package/dist/form/Fields/BooleanField.js +51 -0
- package/dist/form/Fields/CheckboxGroupField.js +72 -0
- package/dist/form/Fields/ColorField.js +33 -0
- package/dist/form/Fields/DropdownField.js +49 -0
- package/dist/form/Fields/DurationField.js +130 -0
- package/dist/form/Fields/LabelField.js +19 -0
- package/dist/form/Fields/MultiDropdownField.js +40 -0
- package/dist/form/Fields/NumberField.js +51 -0
- package/dist/form/Fields/ObjectField.js +54 -0
- package/dist/form/Fields/RadioField.js +49 -0
- package/dist/form/Fields/RangeField.js +58 -0
- package/dist/form/Fields/ResourceOptionIcon.js +21 -0
- package/dist/form/Fields/StepField.js +6 -0
- package/dist/form/Fields/StringField.js +37 -0
- package/dist/form/Fields/TextAreaField.js +31 -0
- package/dist/form/Fields/durationValidity.js +11 -0
- package/dist/form/Form.js +643 -0
- package/dist/form/SpecialTypes/DropdownWrapper.js +30 -0
- package/dist/form/SpecialTypes/FormikCustomDropdown.js +122 -0
- package/dist/form/SpecialTypes/FormikCustomInput.js +111 -0
- package/dist/form/SpecialTypes/FormikMultiSelectDropdown.js +31 -0
- package/dist/form/SpecialTypes/FormikNumericInput.js +60 -0
- package/dist/form/Wizard/WizardContainer.js +138 -0
- package/dist/form/Wizard/WizardNavigation.js +92 -0
- package/dist/form/Wizard/WizardStepContent.js +35 -0
- package/dist/form/Wizard/WizardStepIndicator.js +108 -0
- package/dist/form/Wizard/useWizardState.js +118 -0
- package/dist/form/Wizard/utils.js +34 -0
- package/dist/form/fieldContext.js +26 -0
- package/dist/form/fieldRegistry.js +50 -0
- package/dist/form/index.js +17 -2321
- package/dist/{chunks/FieldWrapper-Cszwg-4B.js → form/lib.js} +2 -97
- package/dist/form/useParsedOpts.js +62 -0
- package/dist/form/usePrevious.js +11 -0
- package/dist/form/utils/checkShowIfObject.js +14 -0
- package/dist/form/utils/duration.js +69 -0
- package/dist/form/utils/getParentValue.js +23 -0
- package/dist/form/utils/getPlaceholder.js +9 -0
- package/dist/form/utils/getValueUsingPath.js +23 -0
- package/dist/form/utils/secondaryField.js +22 -0
- package/dist/graph/AnnotationBanner.js +141 -0
- package/dist/graph/Collapse.js +41 -0
- package/dist/graph/DependencyGraph.js +1398 -0
- package/dist/graph/JobSummary.js +104 -0
- package/dist/graph/MatrixGroup.js +107 -0
- package/dist/graph/Reveal.js +57 -0
- package/dist/graph/TreeView.js +711 -0
- package/dist/graph/index.js +1 -2519
- package/dist/graph/types.js +6 -0
- package/dist/index.js +46 -599
- package/dist/list/ActionToolbar.js +184 -0
- package/dist/list/HoverCard.js +73 -0
- package/dist/list/ListPage.js +145 -0
- package/dist/list/ListPager.js +59 -0
- package/dist/list/ListTable.js +163 -0
- package/dist/list/ListViewControls.js +372 -0
- package/dist/list/RowContextMenu.js +350 -0
- package/dist/list/UserHoverCard.js +117 -0
- package/dist/list/index.js +11 -2
- package/dist/list/listSearch.js +137 -0
- package/dist/list/listView.js +210 -0
- package/dist/list/textEntry.js +10 -0
- package/dist/{chunks/logviewer-BTKuQWzw.js → logviewer/LogViewer.js} +4 -82
- package/dist/logviewer/index.js +2 -1
- package/dist/logviewer/segments.js +80 -0
- package/package.json +1 -1
- package/dist/chunks/PageHeader-0kG_iypP.js +0 -692
- package/dist/chunks/SwitchToggle-DShNdcao.js +0 -758
- package/dist/chunks/editor-Dk1rbV75.js +0 -361
- package/dist/chunks/list-CJm875m7.js +0 -1418
- /package/dist/{chunks/MarkdownImpl-CyeTVvSt.js → components/MarkdownImpl.js} +0 -0
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import { useStrings } from "../../components/Provider.js";
|
|
2
|
+
import { ChevronLeftIcon, ChevronRightIcon, CloseIcon, DownloadFileIcon, FileIcon, LinkIcon, ZoomInIcon, ZoomOutIcon } from "../../icons.js";
|
|
3
|
+
import { formatFileSize } from "../lib/utils.js";
|
|
4
|
+
import { getPreviewKind } from "./previewType.js";
|
|
5
|
+
import { StreamedBadge } from "./StreamedBadge.js";
|
|
6
|
+
import { usePresignedUrl } from "./usePresignedUrl.js";
|
|
7
|
+
import { FilePreviewViewer } from "./Viewer.js";
|
|
8
|
+
import cx from "classnames";
|
|
9
|
+
import { useCallback, useEffect, useState } from "react";
|
|
10
|
+
import { useLocalStorage } from "usehooks-ts";
|
|
11
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
12
|
+
import { createPortal } from "react-dom";
|
|
13
|
+
//#region src/file-explorer/FilePreview/index.tsx
|
|
14
|
+
/** Document views run their own chrome and text to the edge of the stage, which
|
|
15
|
+
* full-window bars would float on top of; they get the bars in flow instead. */
|
|
16
|
+
function isDocumentView(kind) {
|
|
17
|
+
return kind === "code" || kind === "csv" || kind === "notebook";
|
|
18
|
+
}
|
|
19
|
+
function FilePreview({ open, node, siblings, storage, getPresignedUrl, onNavigate, onClose, onDownload, onCopyUri, showSecurityIndicator = true }) {
|
|
20
|
+
const t = useStrings().fileExplorer;
|
|
21
|
+
const [style, setStyle] = useLocalStorage("fileExplorer.previewStyle", "quicklook");
|
|
22
|
+
const [zoom, setZoom] = useState(100);
|
|
23
|
+
const { url, loading: urlLoading, error: urlError } = usePresignedUrl(open ? node : null, getPresignedUrl, {
|
|
24
|
+
enabled: open,
|
|
25
|
+
linkErrorMessage: t.preview.linkError
|
|
26
|
+
});
|
|
27
|
+
const isOverlay = style === "overlay";
|
|
28
|
+
const index = node ? siblings.findIndex((s) => s.path === node.path) : -1;
|
|
29
|
+
const hasPrev = index > 0;
|
|
30
|
+
const hasNext = index > -1 && index < siblings.length - 1;
|
|
31
|
+
const goPrev = useCallback(() => {
|
|
32
|
+
const prev = index > 0 ? siblings[index - 1] : void 0;
|
|
33
|
+
if (prev) onNavigate(prev);
|
|
34
|
+
}, [
|
|
35
|
+
index,
|
|
36
|
+
siblings,
|
|
37
|
+
onNavigate
|
|
38
|
+
]);
|
|
39
|
+
const goNext = useCallback(() => {
|
|
40
|
+
const next = index > -1 ? siblings[index + 1] : void 0;
|
|
41
|
+
if (next) onNavigate(next);
|
|
42
|
+
}, [
|
|
43
|
+
index,
|
|
44
|
+
siblings,
|
|
45
|
+
onNavigate
|
|
46
|
+
]);
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
setZoom(100);
|
|
49
|
+
}, [node]);
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
if (!open) return;
|
|
52
|
+
const onKeyDown = (e) => {
|
|
53
|
+
if (e.key === "Escape") onClose();
|
|
54
|
+
else if (e.key === "ArrowRight") goNext();
|
|
55
|
+
else if (e.key === "ArrowLeft") goPrev();
|
|
56
|
+
};
|
|
57
|
+
document.addEventListener("keydown", onKeyDown);
|
|
58
|
+
return () => document.removeEventListener("keydown", onKeyDown);
|
|
59
|
+
}, [
|
|
60
|
+
open,
|
|
61
|
+
onClose,
|
|
62
|
+
goNext,
|
|
63
|
+
goPrev
|
|
64
|
+
]);
|
|
65
|
+
if (!open || !node) return null;
|
|
66
|
+
const kind = getPreviewKind(node);
|
|
67
|
+
const showZoom = kind === "image";
|
|
68
|
+
const floatingChrome = isOverlay && !isDocumentView(kind);
|
|
69
|
+
const topBar = /* @__PURE__ */ jsxs("div", {
|
|
70
|
+
className: cx("flex items-center justify-between gap-3 px-4 py-3", floatingChrome ? "absolute inset-x-0 top-0 z-10 bg-linear-to-b from-black/80 to-transparent text-white" : "shrink-0 border-b theme-border"),
|
|
71
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
72
|
+
className: "flex min-w-0 items-center gap-2.5",
|
|
73
|
+
children: [
|
|
74
|
+
/* @__PURE__ */ jsx(FileIcon, { className: cx("h-4 w-4 shrink-0", !floatingChrome && "theme-muted-text") }),
|
|
75
|
+
/* @__PURE__ */ jsx("span", {
|
|
76
|
+
className: "truncate font-semibold",
|
|
77
|
+
children: node.name
|
|
78
|
+
}),
|
|
79
|
+
showSecurityIndicator && url && /* @__PURE__ */ jsx(StreamedBadge, {})
|
|
80
|
+
]
|
|
81
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
82
|
+
className: "flex shrink-0 items-center gap-1.5",
|
|
83
|
+
children: [
|
|
84
|
+
/* @__PURE__ */ jsxs("div", {
|
|
85
|
+
className: cx("mr-1 flex items-center rounded-lg p-0.5", floatingChrome ? "bg-white/10" : "theme-muted-panel"),
|
|
86
|
+
children: [/* @__PURE__ */ jsx(SegButton, {
|
|
87
|
+
active: !isOverlay,
|
|
88
|
+
overlayChrome: floatingChrome,
|
|
89
|
+
onClick: () => setStyle("quicklook"),
|
|
90
|
+
children: t.preview.quickLook
|
|
91
|
+
}), /* @__PURE__ */ jsx(SegButton, {
|
|
92
|
+
active: isOverlay,
|
|
93
|
+
overlayChrome: floatingChrome,
|
|
94
|
+
onClick: () => setStyle("overlay"),
|
|
95
|
+
children: t.preview.fullWindow
|
|
96
|
+
})]
|
|
97
|
+
}),
|
|
98
|
+
/* @__PURE__ */ jsx(ChromeButton, {
|
|
99
|
+
overlay: floatingChrome,
|
|
100
|
+
label: t.preview.copyUri,
|
|
101
|
+
onClick: () => onCopyUri(node),
|
|
102
|
+
children: /* @__PURE__ */ jsx(LinkIcon, { className: "h-4 w-4" })
|
|
103
|
+
}),
|
|
104
|
+
/* @__PURE__ */ jsx(ChromeButton, {
|
|
105
|
+
overlay: floatingChrome,
|
|
106
|
+
label: t.preview.download,
|
|
107
|
+
onClick: () => onDownload(node),
|
|
108
|
+
children: /* @__PURE__ */ jsx(DownloadFileIcon, { className: "h-4 w-4" })
|
|
109
|
+
}),
|
|
110
|
+
/* @__PURE__ */ jsx(ChromeButton, {
|
|
111
|
+
overlay: floatingChrome,
|
|
112
|
+
label: t.preview.close,
|
|
113
|
+
onClick: onClose,
|
|
114
|
+
children: /* @__PURE__ */ jsx(CloseIcon, { className: "h-4 w-4" })
|
|
115
|
+
})
|
|
116
|
+
]
|
|
117
|
+
})]
|
|
118
|
+
});
|
|
119
|
+
const footer = /* @__PURE__ */ jsxs("div", {
|
|
120
|
+
className: cx("flex items-center justify-between gap-4 px-4 py-2.5", floatingChrome ? "absolute inset-x-0 bottom-0 z-10 bg-linear-to-t from-black/80 to-transparent text-white" : "shrink-0 border-t theme-border"),
|
|
121
|
+
children: [/* @__PURE__ */ jsxs("span", {
|
|
122
|
+
className: cx("text-xs", !floatingChrome && "theme-muted-text"),
|
|
123
|
+
children: [typeof node.size === "number" && formatFileSize(node.size), node.contentType && ` · ${node.contentType}`]
|
|
124
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
125
|
+
className: "flex items-center gap-3",
|
|
126
|
+
children: [showZoom && /* @__PURE__ */ jsxs("div", {
|
|
127
|
+
className: cx("flex items-center gap-1 rounded-lg p-0.5", floatingChrome ? "bg-white/10" : "theme-muted-panel"),
|
|
128
|
+
children: [
|
|
129
|
+
/* @__PURE__ */ jsx(MiniButton, {
|
|
130
|
+
overlay: floatingChrome,
|
|
131
|
+
label: t.preview.zoomOut,
|
|
132
|
+
onClick: () => setZoom((z) => Math.max(25, z - 25)),
|
|
133
|
+
children: /* @__PURE__ */ jsx(ZoomOutIcon, { className: "h-3.5 w-3.5" })
|
|
134
|
+
}),
|
|
135
|
+
/* @__PURE__ */ jsxs("span", {
|
|
136
|
+
className: "min-w-11 text-center text-xs tabular-nums",
|
|
137
|
+
children: [zoom, "%"]
|
|
138
|
+
}),
|
|
139
|
+
/* @__PURE__ */ jsx(MiniButton, {
|
|
140
|
+
overlay: floatingChrome,
|
|
141
|
+
label: t.preview.zoomIn,
|
|
142
|
+
onClick: () => setZoom((z) => Math.min(400, z + 25)),
|
|
143
|
+
children: /* @__PURE__ */ jsx(ZoomInIcon, { className: "h-3.5 w-3.5" })
|
|
144
|
+
})
|
|
145
|
+
]
|
|
146
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
147
|
+
className: "flex items-center gap-1",
|
|
148
|
+
children: [
|
|
149
|
+
/* @__PURE__ */ jsx(MiniButton, {
|
|
150
|
+
overlay: floatingChrome,
|
|
151
|
+
label: t.preview.previousFile,
|
|
152
|
+
disabled: !hasPrev,
|
|
153
|
+
onClick: goPrev,
|
|
154
|
+
children: /* @__PURE__ */ jsx(ChevronLeftIcon, { className: "h-4 w-4" })
|
|
155
|
+
}),
|
|
156
|
+
/* @__PURE__ */ jsx("span", {
|
|
157
|
+
className: "min-w-14 text-center text-xs tabular-nums",
|
|
158
|
+
children: index >= 0 ? `${index + 1} / ${siblings.length}` : ""
|
|
159
|
+
}),
|
|
160
|
+
/* @__PURE__ */ jsx(MiniButton, {
|
|
161
|
+
overlay: floatingChrome,
|
|
162
|
+
label: t.preview.nextFile,
|
|
163
|
+
disabled: !hasNext,
|
|
164
|
+
onClick: goNext,
|
|
165
|
+
children: /* @__PURE__ */ jsx(ChevronRightIcon, { className: "h-4 w-4" })
|
|
166
|
+
})
|
|
167
|
+
]
|
|
168
|
+
})]
|
|
169
|
+
})]
|
|
170
|
+
});
|
|
171
|
+
const stage = /* @__PURE__ */ jsx("div", {
|
|
172
|
+
className: "relative min-h-0 flex-1 overflow-hidden",
|
|
173
|
+
children: /* @__PURE__ */ jsx(FilePreviewViewer, {
|
|
174
|
+
node,
|
|
175
|
+
kind,
|
|
176
|
+
url,
|
|
177
|
+
urlLoading,
|
|
178
|
+
urlError,
|
|
179
|
+
zoom,
|
|
180
|
+
storage,
|
|
181
|
+
onDownload: () => onDownload(node)
|
|
182
|
+
}, node.path)
|
|
183
|
+
});
|
|
184
|
+
return createPortal(/* @__PURE__ */ jsxs("div", {
|
|
185
|
+
role: "none",
|
|
186
|
+
className: "fixed inset-0 z-9995 flex items-center justify-center",
|
|
187
|
+
style: {
|
|
188
|
+
backgroundColor: isOverlay ? "rgba(1,4,9,.94)" : "rgba(1,4,9,.62)",
|
|
189
|
+
backdropFilter: isOverlay ? void 0 : "blur(3px)",
|
|
190
|
+
WebkitBackdropFilter: isOverlay ? void 0 : "blur(3px)"
|
|
191
|
+
},
|
|
192
|
+
onClick: (e) => {
|
|
193
|
+
if (e.target === e.currentTarget) onClose();
|
|
194
|
+
},
|
|
195
|
+
children: [isOverlay && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(EdgeButton, {
|
|
196
|
+
side: "left",
|
|
197
|
+
label: t.preview.previousFile,
|
|
198
|
+
disabled: !hasPrev,
|
|
199
|
+
onClick: goPrev
|
|
200
|
+
}), /* @__PURE__ */ jsx(EdgeButton, {
|
|
201
|
+
side: "right",
|
|
202
|
+
label: t.preview.nextFile,
|
|
203
|
+
disabled: !hasNext,
|
|
204
|
+
onClick: goNext
|
|
205
|
+
})] }), /* @__PURE__ */ jsxs("div", {
|
|
206
|
+
role: "dialog",
|
|
207
|
+
"aria-modal": "true",
|
|
208
|
+
"aria-label": node.name,
|
|
209
|
+
className: cx("relative flex flex-col", isOverlay ? "h-screen w-screen" : "overflow-hidden rounded-2xl border theme-border shadow-2xl", floatingChrome ? "bg-transparent" : "bg-(--theme-panel-bg)"),
|
|
210
|
+
style: isOverlay ? void 0 : {
|
|
211
|
+
width: "min(1120px, 94vw)",
|
|
212
|
+
height: "min(780px, 90vh)"
|
|
213
|
+
},
|
|
214
|
+
children: [
|
|
215
|
+
topBar,
|
|
216
|
+
stage,
|
|
217
|
+
footer
|
|
218
|
+
]
|
|
219
|
+
})]
|
|
220
|
+
}), document.body);
|
|
221
|
+
}
|
|
222
|
+
function SegButton({ active, overlayChrome, onClick, children }) {
|
|
223
|
+
return /* @__PURE__ */ jsx("button", {
|
|
224
|
+
type: "button",
|
|
225
|
+
onClick,
|
|
226
|
+
className: cx("cursor-pointer whitespace-nowrap rounded-md px-2.5 py-1 text-[11.5px] font-medium transition-colors", active ? "bg-(--theme-element) text-(--theme-element-text)" : overlayChrome ? "text-white/70 hover:text-white" : "theme-muted-text hover:theme-text"),
|
|
227
|
+
children
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
function ChromeButton({ overlay, label, onClick, children }) {
|
|
231
|
+
return /* @__PURE__ */ jsx("button", {
|
|
232
|
+
type: "button",
|
|
233
|
+
"aria-label": label,
|
|
234
|
+
title: label,
|
|
235
|
+
onClick,
|
|
236
|
+
className: cx("flex h-8 w-8 cursor-pointer items-center justify-center rounded-lg transition-colors", overlay ? "text-white/90 hover:bg-white/15" : "theme-muted-text hover:theme-hover hover:theme-text"),
|
|
237
|
+
children
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
function MiniButton({ overlay, label, onClick, disabled, children }) {
|
|
241
|
+
return /* @__PURE__ */ jsx("button", {
|
|
242
|
+
type: "button",
|
|
243
|
+
"aria-label": label,
|
|
244
|
+
title: label,
|
|
245
|
+
onClick,
|
|
246
|
+
disabled,
|
|
247
|
+
className: cx("flex h-6 w-7 items-center justify-center rounded-md transition-colors", disabled ? "cursor-not-allowed opacity-40" : cx("cursor-pointer", overlay ? "hover:bg-white/15" : "hover:theme-hover")),
|
|
248
|
+
children
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
function EdgeButton({ side, label, disabled, onClick }) {
|
|
252
|
+
return /* @__PURE__ */ jsx("button", {
|
|
253
|
+
type: "button",
|
|
254
|
+
"aria-label": label,
|
|
255
|
+
disabled,
|
|
256
|
+
onClick: (e) => {
|
|
257
|
+
e.stopPropagation();
|
|
258
|
+
onClick();
|
|
259
|
+
},
|
|
260
|
+
className: cx("absolute top-1/2 z-20 flex h-13 w-13 -translate-y-1/2 items-center justify-center rounded-full text-white backdrop-blur", side === "left" ? "left-5" : "right-5", disabled ? "cursor-not-allowed opacity-30" : "cursor-pointer hover:bg-white/20"),
|
|
261
|
+
style: { backgroundColor: "rgba(22,27,34,.7)" },
|
|
262
|
+
children: side === "left" ? /* @__PURE__ */ jsx(ChevronLeftIcon, { className: "h-6 w-6" }) : /* @__PURE__ */ jsx(ChevronRightIcon, { className: "h-6 w-6" })
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
//#endregion
|
|
266
|
+
export { FilePreview };
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
//#region src/file-explorer/FilePreview/parse.ts
|
|
2
|
+
/** Parse CSV/TSV, honoring quoted fields (embedded delimiter/newline/escaped quote);
|
|
3
|
+
* `maxRows` stops early so a big file isn't walked in full for a preview. */
|
|
4
|
+
function parseDelimited(text, delimiter, maxRows) {
|
|
5
|
+
const rows = [];
|
|
6
|
+
let row = [];
|
|
7
|
+
let field = "";
|
|
8
|
+
let inQuotes = false;
|
|
9
|
+
for (let i = 0; i < text.length; i++) {
|
|
10
|
+
const char = text[i];
|
|
11
|
+
if (inQuotes) {
|
|
12
|
+
if (char === "\"") {
|
|
13
|
+
if (text[i + 1] === "\"") {
|
|
14
|
+
field += "\"";
|
|
15
|
+
i++;
|
|
16
|
+
} else inQuotes = false;
|
|
17
|
+
} else field += char;
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
if (char === "\"") inQuotes = true;
|
|
21
|
+
else if (char === delimiter) {
|
|
22
|
+
row.push(field);
|
|
23
|
+
field = "";
|
|
24
|
+
} else if (char === "\n" || char === "\r") {
|
|
25
|
+
if (char === "\r" && text[i + 1] === "\n") i++;
|
|
26
|
+
row.push(field);
|
|
27
|
+
rows.push(row);
|
|
28
|
+
row = [];
|
|
29
|
+
field = "";
|
|
30
|
+
if (maxRows !== void 0 && rows.length >= maxRows) break;
|
|
31
|
+
} else field += char;
|
|
32
|
+
}
|
|
33
|
+
if (field.length > 0 || row.length > 0) {
|
|
34
|
+
row.push(field);
|
|
35
|
+
rows.push(row);
|
|
36
|
+
}
|
|
37
|
+
while (rows.length > 0) {
|
|
38
|
+
const last = rows[rows.length - 1];
|
|
39
|
+
if (last && last.length === 1 && last[0] === "") rows.pop();
|
|
40
|
+
else break;
|
|
41
|
+
}
|
|
42
|
+
return rows;
|
|
43
|
+
}
|
|
44
|
+
function parseNotebook(text) {
|
|
45
|
+
try {
|
|
46
|
+
const parsed = JSON.parse(text);
|
|
47
|
+
if (!parsed || !Array.isArray(parsed.cells)) return null;
|
|
48
|
+
return parsed;
|
|
49
|
+
} catch {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/** nbformat `source`/`text` is a string or an array of lines; normalize to one string. */
|
|
54
|
+
function joinSource(source) {
|
|
55
|
+
if (Array.isArray(source)) return source.map((s) => typeof s === "string" ? s : "").join("");
|
|
56
|
+
return typeof source === "string" ? source : "";
|
|
57
|
+
}
|
|
58
|
+
var ANSI_ESCAPE = /\x1b\[[0-9;]*m/g;
|
|
59
|
+
function stripAnsi(text) {
|
|
60
|
+
return text.replace(ANSI_ESCAPE, "");
|
|
61
|
+
}
|
|
62
|
+
//#endregion
|
|
63
|
+
export { joinSource, parseDelimited, parseNotebook, stripAnsi };
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { CorsError } from "../lib/errors.js";
|
|
2
|
+
//#region src/file-explorer/FilePreview/previewCache.ts
|
|
3
|
+
var MAX_ENTRIES = 2;
|
|
4
|
+
var urlCache = /* @__PURE__ */ new Map();
|
|
5
|
+
var contentCache = /* @__PURE__ */ new Map();
|
|
6
|
+
function trim(cache, onEvict) {
|
|
7
|
+
while (cache.size > MAX_ENTRIES) {
|
|
8
|
+
const oldest = cache.keys().next().value;
|
|
9
|
+
if (oldest === void 0) return;
|
|
10
|
+
const evicted = cache.get(oldest);
|
|
11
|
+
cache.delete(oldest);
|
|
12
|
+
if (evicted !== void 0) onEvict(evicted);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function disposeContent(record) {
|
|
16
|
+
record.controller.abort();
|
|
17
|
+
if (record.value !== void 0) record.dispose?.(record.value);
|
|
18
|
+
}
|
|
19
|
+
function moveToNewest(cache, path, record) {
|
|
20
|
+
cache.delete(path);
|
|
21
|
+
cache.set(path, record);
|
|
22
|
+
}
|
|
23
|
+
/** Request a presigned URL for `path` once and remember the result; a failed or
|
|
24
|
+
* empty result is not cached so a later mount retries. */
|
|
25
|
+
function loadPreviewUrl(path, loader) {
|
|
26
|
+
const existing = urlCache.get(path);
|
|
27
|
+
if (existing) {
|
|
28
|
+
moveToNewest(urlCache, path, existing);
|
|
29
|
+
return existing;
|
|
30
|
+
}
|
|
31
|
+
const record = { promise: loader() };
|
|
32
|
+
record.promise.then((value) => {
|
|
33
|
+
if (value[0] && !value[1]) record.value = value;
|
|
34
|
+
else if (urlCache.get(path) === record) urlCache.delete(path);
|
|
35
|
+
}).catch(() => {
|
|
36
|
+
if (urlCache.get(path) === record) urlCache.delete(path);
|
|
37
|
+
});
|
|
38
|
+
urlCache.set(path, record);
|
|
39
|
+
trim(urlCache, () => {});
|
|
40
|
+
return record;
|
|
41
|
+
}
|
|
42
|
+
/** Fetch the object for `path` once and remember it; `dispose` frees the value
|
|
43
|
+
* (e.g. revokes a blob URL) when the entry is evicted. A rejected fetch is not
|
|
44
|
+
* cached so a later mount retries. */
|
|
45
|
+
function loadPreviewContent(path, loader, dispose) {
|
|
46
|
+
const existing = contentCache.get(path);
|
|
47
|
+
if (existing) {
|
|
48
|
+
moveToNewest(contentCache, path, existing);
|
|
49
|
+
return existing;
|
|
50
|
+
}
|
|
51
|
+
const controller = new AbortController();
|
|
52
|
+
const record = {
|
|
53
|
+
promise: loader(controller.signal),
|
|
54
|
+
controller,
|
|
55
|
+
dispose
|
|
56
|
+
};
|
|
57
|
+
record.promise.then((value) => {
|
|
58
|
+
record.value = value;
|
|
59
|
+
}).catch(() => {
|
|
60
|
+
if (contentCache.get(path) === record) contentCache.delete(path);
|
|
61
|
+
});
|
|
62
|
+
contentCache.set(path, record);
|
|
63
|
+
trim(contentCache, disposeContent);
|
|
64
|
+
return record;
|
|
65
|
+
}
|
|
66
|
+
/** Fetch a preview object over its signed URL; a 401/403 means the URL is stale
|
|
67
|
+
* or unauthorized, so drop it from the cache and let the next mount fetch a fresh
|
|
68
|
+
* one instead of replaying the dead one. */
|
|
69
|
+
async function fetchPreviewObject(path, url, signal, headers) {
|
|
70
|
+
let response;
|
|
71
|
+
try {
|
|
72
|
+
response = await fetch(url, {
|
|
73
|
+
signal,
|
|
74
|
+
...headers ? { headers } : {}
|
|
75
|
+
});
|
|
76
|
+
} catch (error) {
|
|
77
|
+
if (error instanceof TypeError) throw new CorsError();
|
|
78
|
+
throw error;
|
|
79
|
+
}
|
|
80
|
+
if (!response.ok) {
|
|
81
|
+
if (response.status === 401 || response.status === 403) urlCache.delete(path);
|
|
82
|
+
throw new Error(`Failed to load file (${response.status})`);
|
|
83
|
+
}
|
|
84
|
+
return response;
|
|
85
|
+
}
|
|
86
|
+
function peekPreviewUrl(path) {
|
|
87
|
+
return urlCache.get(path)?.value;
|
|
88
|
+
}
|
|
89
|
+
function peekPreviewContent(path) {
|
|
90
|
+
return contentCache.get(path)?.value;
|
|
91
|
+
}
|
|
92
|
+
function clearPreviewCache() {
|
|
93
|
+
for (const record of contentCache.values()) disposeContent(record);
|
|
94
|
+
contentCache.clear();
|
|
95
|
+
urlCache.clear();
|
|
96
|
+
}
|
|
97
|
+
//#endregion
|
|
98
|
+
export { clearPreviewCache, fetchPreviewObject, loadPreviewContent, loadPreviewUrl, peekPreviewContent, peekPreviewUrl };
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { getFileExtension } from "../lib/utils.js";
|
|
2
|
+
//#region src/file-explorer/FilePreview/previewType.ts
|
|
3
|
+
/** Short-lived so the link doesn't linger; an hour covers a viewing session. */
|
|
4
|
+
var PREVIEW_URL_EXPIRES_IN = 3600;
|
|
5
|
+
/** Cap text pulled into the tab; media streams through native elements uncapped. */
|
|
6
|
+
var CODE_PREVIEW_MAX_BYTES = 10485760;
|
|
7
|
+
var TABULAR_PREVIEW_MAX_BYTES = 20971520;
|
|
8
|
+
/** A line cap bounds what a text preview holds and renders; the byte cap above cuts
|
|
9
|
+
* short whatever the line cap hasn't, and refuses outright only content with no line
|
|
10
|
+
* break to cut on. Well under Monaco's 20MB and 300K-line thresholds, past which it
|
|
11
|
+
* stops tokenizing and highlighting dies. */
|
|
12
|
+
var CODE_PREVIEW_MAX_LINES = 5e3;
|
|
13
|
+
/** Fetched into a blob URL to render inline regardless of served content type; falls back to download above this. */
|
|
14
|
+
var PDF_PREVIEW_MAX_BYTES = 52428800;
|
|
15
|
+
var IMAGE_EXT = /* @__PURE__ */ new Set([
|
|
16
|
+
"png",
|
|
17
|
+
"jpg",
|
|
18
|
+
"jpeg",
|
|
19
|
+
"gif",
|
|
20
|
+
"webp",
|
|
21
|
+
"bmp",
|
|
22
|
+
"ico",
|
|
23
|
+
"avif",
|
|
24
|
+
"svg"
|
|
25
|
+
]);
|
|
26
|
+
var VIDEO_EXT = /* @__PURE__ */ new Set([
|
|
27
|
+
"mp4",
|
|
28
|
+
"mov",
|
|
29
|
+
"webm",
|
|
30
|
+
"m4v",
|
|
31
|
+
"ogv"
|
|
32
|
+
]);
|
|
33
|
+
var AUDIO_EXT = /* @__PURE__ */ new Set([
|
|
34
|
+
"mp3",
|
|
35
|
+
"wav",
|
|
36
|
+
"ogg",
|
|
37
|
+
"oga",
|
|
38
|
+
"flac",
|
|
39
|
+
"m4a",
|
|
40
|
+
"aac"
|
|
41
|
+
]);
|
|
42
|
+
var ARCHIVE_EXT = /* @__PURE__ */ new Set([
|
|
43
|
+
"zip",
|
|
44
|
+
"tar",
|
|
45
|
+
"gz",
|
|
46
|
+
"tgz",
|
|
47
|
+
"bz2",
|
|
48
|
+
"xz",
|
|
49
|
+
"7z",
|
|
50
|
+
"rar"
|
|
51
|
+
]);
|
|
52
|
+
var LANGUAGE_BY_EXT = {
|
|
53
|
+
py: "python",
|
|
54
|
+
ipy: "python",
|
|
55
|
+
js: "javascript",
|
|
56
|
+
jsx: "javascript",
|
|
57
|
+
ts: "typescript",
|
|
58
|
+
tsx: "typescript",
|
|
59
|
+
go: "go",
|
|
60
|
+
rs: "rust",
|
|
61
|
+
java: "java",
|
|
62
|
+
c: "c",
|
|
63
|
+
h: "c",
|
|
64
|
+
cpp: "cpp",
|
|
65
|
+
cc: "cpp",
|
|
66
|
+
hpp: "cpp",
|
|
67
|
+
cs: "csharp",
|
|
68
|
+
rb: "ruby",
|
|
69
|
+
php: "php",
|
|
70
|
+
swift: "swift",
|
|
71
|
+
kt: "kotlin",
|
|
72
|
+
scala: "scala",
|
|
73
|
+
r: "r",
|
|
74
|
+
jl: "julia",
|
|
75
|
+
sh: "shell",
|
|
76
|
+
bash: "shell",
|
|
77
|
+
zsh: "shell",
|
|
78
|
+
fish: "shell",
|
|
79
|
+
ps1: "powershell",
|
|
80
|
+
sql: "sql",
|
|
81
|
+
json: "json",
|
|
82
|
+
jsonl: "json",
|
|
83
|
+
yaml: "yaml",
|
|
84
|
+
yml: "yaml",
|
|
85
|
+
toml: "ini",
|
|
86
|
+
ini: "ini",
|
|
87
|
+
cfg: "ini",
|
|
88
|
+
conf: "ini",
|
|
89
|
+
env: "ini",
|
|
90
|
+
xml: "xml",
|
|
91
|
+
html: "html",
|
|
92
|
+
htm: "html",
|
|
93
|
+
css: "css",
|
|
94
|
+
scss: "scss",
|
|
95
|
+
md: "markdown",
|
|
96
|
+
markdown: "markdown",
|
|
97
|
+
tex: "plaintext",
|
|
98
|
+
txt: "plaintext",
|
|
99
|
+
log: "plaintext",
|
|
100
|
+
gitignore: "plaintext"
|
|
101
|
+
};
|
|
102
|
+
var LANGUAGE_BY_FILENAME = {
|
|
103
|
+
dockerfile: "dockerfile",
|
|
104
|
+
makefile: "plaintext"
|
|
105
|
+
};
|
|
106
|
+
function getMonacoLanguage(node) {
|
|
107
|
+
const ext = getFileExtension(node.name);
|
|
108
|
+
if (ext && LANGUAGE_BY_EXT[ext]) return LANGUAGE_BY_EXT[ext];
|
|
109
|
+
return LANGUAGE_BY_FILENAME[node.name.toLowerCase()] ?? "plaintext";
|
|
110
|
+
}
|
|
111
|
+
function isCodeFile(node) {
|
|
112
|
+
const ext = getFileExtension(node.name);
|
|
113
|
+
return ext !== null && ext in LANGUAGE_BY_EXT || node.name.toLowerCase() in LANGUAGE_BY_FILENAME;
|
|
114
|
+
}
|
|
115
|
+
function kindFromContentType(ct) {
|
|
116
|
+
if (ct === "application/pdf") return "pdf";
|
|
117
|
+
if (ct === "application/x-ipynb+json") return "notebook";
|
|
118
|
+
if (ct === "text/csv") return "csv";
|
|
119
|
+
if (ct.startsWith("image/")) return "image";
|
|
120
|
+
if (ct.startsWith("video/")) return "video";
|
|
121
|
+
if (ct.startsWith("audio/")) return "audio";
|
|
122
|
+
if (ct.startsWith("text/") || ct === "application/json" || ct === "application/xml") return "code";
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
function kindFromExtension(ext) {
|
|
126
|
+
if (ext === "pdf") return "pdf";
|
|
127
|
+
if (ext === "ipynb") return "notebook";
|
|
128
|
+
if (ext === "csv" || ext === "tsv") return "csv";
|
|
129
|
+
if (IMAGE_EXT.has(ext)) return "image";
|
|
130
|
+
if (VIDEO_EXT.has(ext)) return "video";
|
|
131
|
+
if (AUDIO_EXT.has(ext)) return "audio";
|
|
132
|
+
if (ARCHIVE_EXT.has(ext)) return "archive";
|
|
133
|
+
if (ext in LANGUAGE_BY_EXT) return "code";
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
/** Content type wins when present; extension is the fallback. */
|
|
137
|
+
function getPreviewKind(node) {
|
|
138
|
+
if (node.type !== "file") return "unsupported";
|
|
139
|
+
const ct = (node.contentType ?? "").split(";")[0]?.toLowerCase().trim() ?? "";
|
|
140
|
+
const ext = getFileExtension(node.name);
|
|
141
|
+
const ctKind = kindFromContentType(ct);
|
|
142
|
+
const extKind = ext !== null ? kindFromExtension(ext) : null;
|
|
143
|
+
if (ctKind === "code" && (extKind === "csv" || extKind === "notebook")) return extKind;
|
|
144
|
+
return ctKind ?? extKind ?? (isCodeFile(node) ? "code" : "unsupported");
|
|
145
|
+
}
|
|
146
|
+
function canPreview(node) {
|
|
147
|
+
const kind = getPreviewKind(node);
|
|
148
|
+
return kind !== "unsupported" && kind !== "archive";
|
|
149
|
+
}
|
|
150
|
+
//#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 };
|