@parallelworks/ui 0.19.0 → 0.19.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (217) hide show
  1. package/dist/{chunks/memoryStore-CC91S5LP.js → ai/adapter/memoryStore.js} +2 -2
  2. package/dist/ai/adapter/mock/index.js +2 -80
  3. package/dist/ai/adapter/mock/mockAdapter.js +81 -0
  4. package/dist/ai/adapter/openai/createOpenAIStream.js +33 -0
  5. package/dist/ai/adapter/openai/errors.js +37 -0
  6. package/dist/ai/adapter/openai/index.js +5 -333
  7. package/dist/ai/adapter/openai/openaiAdapter.js +54 -0
  8. package/dist/ai/adapter/openai/responses.js +108 -0
  9. package/dist/ai/adapter/openai/sse.js +112 -0
  10. package/dist/{chunks/types-Yy0NrerF.js → ai/adapter/types.js} +1 -1
  11. package/dist/ai/agent/classifier.js +12 -0
  12. package/dist/ai/agent/diff.js +223 -0
  13. package/dist/ai/agent/diffHighlight.js +101 -0
  14. package/dist/ai/agent/permissions.js +29 -0
  15. package/dist/ai/agent/toolfmt.js +383 -0
  16. package/dist/ai/agent/transcript.js +127 -0
  17. package/dist/ai/components/AllocationSelector.js +56 -0
  18. package/dist/ai/components/AttachmentManager.js +144 -0
  19. package/dist/ai/components/AttachmentUpload.js +179 -0
  20. package/dist/ai/components/BlockedGroupBanner.js +29 -0
  21. package/dist/ai/components/BranchNavigator.js +57 -0
  22. package/dist/ai/components/ChatEmptyState.js +171 -0
  23. package/dist/ai/components/ChatInput.js +265 -0
  24. package/dist/ai/components/ChatLayout.js +15 -0
  25. package/dist/ai/components/ChatMessage.js +338 -0
  26. package/dist/ai/components/ChatMessageList.js +301 -0
  27. package/dist/ai/components/ChatSidebar.js +345 -0
  28. package/dist/ai/components/ChatThread.js +198 -0
  29. package/dist/ai/components/ComposerChrome.js +50 -0
  30. package/dist/ai/components/ComposerFrame.js +33 -0
  31. package/dist/ai/components/DragOverlay.js +20 -0
  32. package/dist/ai/components/ModelSelector.js +343 -0
  33. package/dist/ai/components/ShareDialog.js +260 -0
  34. package/dist/ai/components/SlashMenu.js +91 -0
  35. package/dist/ai/components/agent/ActivityLine.js +151 -0
  36. package/dist/ai/components/agent/AgentMessageParts.js +721 -0
  37. package/dist/ai/components/agent/PermissionPicker.js +38 -0
  38. package/dist/ai/components/attachmentKeys.js +10 -0
  39. package/dist/ai/core/ChatProvider.js +848 -0
  40. package/dist/ai/core/KeyboardShortcutsProvider.js +9 -0
  41. package/dist/ai/core/chatReducer.js +223 -0
  42. package/dist/ai/core/config.js +45 -0
  43. package/dist/ai/core/events.js +4 -0
  44. package/dist/ai/core/exportConversation.js +42 -0
  45. package/dist/ai/core/greeting.js +38 -0
  46. package/dist/ai/core/parts.js +84 -0
  47. package/dist/ai/core/useDragDrop.js +36 -0
  48. package/dist/ai/core/useKeyboardShortcuts.js +77 -0
  49. package/dist/ai/core/useSSEStream.js +98 -0
  50. package/dist/ai/index.js +43 -6644
  51. package/dist/ai/strings.js +175 -0
  52. package/dist/ai/ui/Avatar.js +35 -0
  53. package/dist/ai/ui/Dropdown.js +110 -0
  54. package/dist/ai/ui/Markdown.js +79 -0
  55. package/dist/ai/ui/Notice.js +77 -0
  56. package/dist/ai/ui/codeBreaks.js +36 -0
  57. package/dist/ai/ui/prose.js +13 -0
  58. package/dist/ai/ui/sidebar.js +148 -0
  59. package/dist/ai/utils.js +23 -0
  60. package/dist/components/AddMenu.js +46 -0
  61. package/dist/components/Avatar.js +46 -0
  62. package/dist/{chunks/BareModal-BtRiQezg.js → components/BareModal.js} +1 -1
  63. package/dist/components/Breadcrumbs.js +83 -0
  64. package/dist/components/Button.js +83 -0
  65. package/dist/components/Callout.js +23 -0
  66. package/dist/components/Card.js +31 -0
  67. package/dist/components/CollapsiblePanel.js +77 -0
  68. package/dist/{chunks/ConfirmModal-aOR4O1QE.js → components/ConfirmModal.js} +4 -9
  69. package/dist/components/CopyToClipboard.js +81 -0
  70. package/dist/{chunks/Markdown-CM-FenNE.js → components/CreateModal.js} +9 -116
  71. package/dist/components/DescriptionList.js +42 -0
  72. package/dist/components/Dropdown.js +349 -0
  73. package/dist/components/EmptyState.js +27 -0
  74. package/dist/components/Filter.js +32 -0
  75. package/dist/components/FilterPill.js +13 -0
  76. package/dist/components/IconButton.js +34 -0
  77. package/dist/{chunks/Indicator-hBSIxOPK.js → components/Indicator.js} +1 -1
  78. package/dist/components/Input.js +88 -0
  79. package/dist/components/LetterBadge.js +13 -0
  80. package/dist/components/Listbox.js +123 -0
  81. package/dist/{chunks/Loader-BXoXTZGc.js → components/Loader.js} +1 -1
  82. package/dist/components/Markdown.js +15 -0
  83. package/dist/components/MultiSelect.js +171 -0
  84. package/dist/components/Overview.js +39 -0
  85. package/dist/components/PageHeader.js +194 -0
  86. package/dist/components/Pagination.js +35 -0
  87. package/dist/{chunks/Provider-CDYurraE.js → components/Provider.js} +1 -1
  88. package/dist/{chunks/RequiredMark-DfLdYbYZ.js → components/RequiredMark.js} +1 -1
  89. package/dist/components/SectionHeader.js +11 -0
  90. package/dist/components/SettingModeOption.js +30 -0
  91. package/dist/components/SettingRow.js +35 -0
  92. package/dist/components/SettingsCard.js +31 -0
  93. package/dist/components/SettingsGroup.js +17 -0
  94. package/dist/components/Sort.js +42 -0
  95. package/dist/components/StatusBadge.js +63 -0
  96. package/dist/components/SwitchToggle.js +59 -0
  97. package/dist/{chunks/SwitchToggleSmall-FcsaYSTs.js → components/SwitchToggleSmall.js} +1 -1
  98. package/dist/{chunks/Table-DU2MW8xz.js → components/Table.js} +3 -3
  99. package/dist/components/Toggle.js +20 -0
  100. package/dist/{chunks/Tooltip-K5HJr4_p.js → components/Tooltip.js} +2 -27
  101. package/dist/{chunks/StatusBadge-C1Blx_cW.js → components/dropdownUtils.js} +2 -63
  102. package/dist/{chunks/focus-D_iZJTW1.js → components/focus.js} +1 -1
  103. package/dist/components/ghostButton.js +7 -0
  104. package/dist/components/inputClasses.js +4 -0
  105. package/dist/components/keys.js +27 -0
  106. package/dist/components/relativeTime.js +32 -0
  107. package/dist/{chunks/useCssIsDark-DHr05dG2.js → components/useCssIsDark.js} +1 -1
  108. package/dist/{chunks/duration-BOv8Phl8.js → duration.js} +1 -1
  109. package/dist/editor/Editor.js +13 -0
  110. package/dist/editor/EditorField.js +53 -0
  111. package/dist/editor/FormCodePanel.js +173 -0
  112. package/dist/{chunks/Monaco-DJ8MW-Dq.js → editor/Monaco.js} +4 -2
  113. package/dist/editor/index.js +6 -1
  114. package/dist/editor/themes.js +91 -0
  115. package/dist/editor/workers.js +29 -0
  116. package/dist/editor/yaml.js +15 -0
  117. package/dist/file-explorer/FileExplorer.js +1402 -0
  118. package/dist/file-explorer/FileExplorerContext.js +370 -0
  119. package/dist/{chunks/CodePreviewEditor-YqQBz1YL.js → file-explorer/FilePreview/CodePreviewEditor.js} +3 -2
  120. package/dist/file-explorer/FilePreview/Inline.js +45 -0
  121. package/dist/file-explorer/FilePreview/StreamedBadge.js +16 -0
  122. package/dist/file-explorer/FilePreview/Viewer.js +526 -0
  123. package/dist/file-explorer/FilePreview/index.js +266 -0
  124. package/dist/file-explorer/FilePreview/parse.js +63 -0
  125. package/dist/file-explorer/FilePreview/previewCache.js +98 -0
  126. package/dist/file-explorer/FilePreview/previewType.js +151 -0
  127. package/dist/file-explorer/FilePreview/readCappedBytes.js +196 -0
  128. package/dist/file-explorer/FilePreview/useObjectBlobUrl.js +95 -0
  129. package/dist/file-explorer/FilePreview/useObjectText.js +109 -0
  130. package/dist/file-explorer/FilePreview/usePresignedUrl.js +73 -0
  131. package/dist/file-explorer/FileRow.js +165 -0
  132. package/dist/file-explorer/FileRowsWindow.js +92 -0
  133. package/dist/file-explorer/FileTable.js +172 -0
  134. package/dist/file-explorer/FileTableHeaders.js +49 -0
  135. package/dist/file-explorer/LoadMoreRow.js +46 -0
  136. package/dist/file-explorer/TreeView.js +376 -0
  137. package/dist/file-explorer/helpers.js +537 -0
  138. package/dist/file-explorer/index.js +7 -5671
  139. package/dist/file-explorer/lib/batch.js +39 -0
  140. package/dist/file-explorer/lib/errors.js +9 -0
  141. package/dist/file-explorer/lib/paging.js +124 -0
  142. package/dist/file-explorer/lib/utils.js +322 -0
  143. package/dist/file-explorer/toasts.js +400 -0
  144. package/dist/file-explorer/treeRows.js +73 -0
  145. package/dist/file-explorer/useNearEnd.js +27 -0
  146. package/dist/form/FieldWrapper.js +54 -0
  147. package/dist/form/Fields/BooleanField.js +51 -0
  148. package/dist/form/Fields/CheckboxGroupField.js +72 -0
  149. package/dist/form/Fields/ColorField.js +33 -0
  150. package/dist/form/Fields/DropdownField.js +49 -0
  151. package/dist/form/Fields/DurationField.js +130 -0
  152. package/dist/form/Fields/LabelField.js +19 -0
  153. package/dist/form/Fields/MultiDropdownField.js +40 -0
  154. package/dist/form/Fields/NumberField.js +51 -0
  155. package/dist/form/Fields/ObjectField.js +54 -0
  156. package/dist/form/Fields/RadioField.js +49 -0
  157. package/dist/form/Fields/RangeField.js +58 -0
  158. package/dist/form/Fields/ResourceOptionIcon.js +21 -0
  159. package/dist/form/Fields/StepField.js +6 -0
  160. package/dist/form/Fields/StringField.js +37 -0
  161. package/dist/form/Fields/TextAreaField.js +31 -0
  162. package/dist/form/Fields/durationValidity.js +11 -0
  163. package/dist/form/Form.js +643 -0
  164. package/dist/form/SpecialTypes/DropdownWrapper.js +30 -0
  165. package/dist/form/SpecialTypes/FormikCustomDropdown.js +122 -0
  166. package/dist/form/SpecialTypes/FormikCustomInput.js +111 -0
  167. package/dist/form/SpecialTypes/FormikMultiSelectDropdown.js +31 -0
  168. package/dist/form/SpecialTypes/FormikNumericInput.js +60 -0
  169. package/dist/form/Wizard/WizardContainer.js +138 -0
  170. package/dist/form/Wizard/WizardNavigation.js +92 -0
  171. package/dist/form/Wizard/WizardStepContent.js +35 -0
  172. package/dist/form/Wizard/WizardStepIndicator.js +108 -0
  173. package/dist/form/Wizard/useWizardState.js +118 -0
  174. package/dist/form/Wizard/utils.js +34 -0
  175. package/dist/form/fieldContext.js +26 -0
  176. package/dist/form/fieldRegistry.js +50 -0
  177. package/dist/form/index.js +17 -2321
  178. package/dist/{chunks/FieldWrapper-Cszwg-4B.js → form/lib.js} +2 -97
  179. package/dist/form/useParsedOpts.js +62 -0
  180. package/dist/form/usePrevious.js +11 -0
  181. package/dist/form/utils/checkShowIfObject.js +14 -0
  182. package/dist/form/utils/duration.js +69 -0
  183. package/dist/form/utils/getParentValue.js +23 -0
  184. package/dist/form/utils/getPlaceholder.js +9 -0
  185. package/dist/form/utils/getValueUsingPath.js +23 -0
  186. package/dist/form/utils/secondaryField.js +22 -0
  187. package/dist/graph/AnnotationBanner.js +141 -0
  188. package/dist/graph/Collapse.js +41 -0
  189. package/dist/graph/DependencyGraph.js +1398 -0
  190. package/dist/graph/JobSummary.js +104 -0
  191. package/dist/graph/MatrixGroup.js +107 -0
  192. package/dist/graph/Reveal.js +57 -0
  193. package/dist/graph/TreeView.js +711 -0
  194. package/dist/graph/index.js +1 -2519
  195. package/dist/graph/types.js +6 -0
  196. package/dist/index.js +46 -599
  197. package/dist/list/ActionToolbar.js +184 -0
  198. package/dist/list/HoverCard.js +73 -0
  199. package/dist/list/ListPage.js +145 -0
  200. package/dist/list/ListPager.js +59 -0
  201. package/dist/list/ListTable.js +163 -0
  202. package/dist/list/ListViewControls.js +372 -0
  203. package/dist/list/RowContextMenu.js +350 -0
  204. package/dist/list/UserHoverCard.js +117 -0
  205. package/dist/list/index.js +11 -2
  206. package/dist/list/listSearch.js +137 -0
  207. package/dist/list/listView.js +210 -0
  208. package/dist/list/textEntry.js +10 -0
  209. package/dist/{chunks/logviewer-BTKuQWzw.js → logviewer/LogViewer.js} +4 -82
  210. package/dist/logviewer/index.js +2 -1
  211. package/dist/logviewer/segments.js +80 -0
  212. package/package.json +1 -1
  213. package/dist/chunks/PageHeader-0kG_iypP.js +0 -692
  214. package/dist/chunks/SwitchToggle-DShNdcao.js +0 -758
  215. package/dist/chunks/editor-Dk1rbV75.js +0 -361
  216. package/dist/chunks/list-CJm875m7.js +0 -1418
  217. /package/dist/{chunks/MarkdownImpl-CyeTVvSt.js → components/MarkdownImpl.js} +0 -0
@@ -0,0 +1,400 @@
1
+ import { useStrings } from "../components/Provider.js";
2
+ import { AlertIcon, AngleDownIcon, AngleUpIcon, CloseIcon, CloudUploadIcon, ExpandVerticalIcon, MinimizeIcon, SuccessIcon } from "../icons.js";
3
+ import { formatETA, formatFileSize } from "./lib/utils.js";
4
+ import cx from "classnames";
5
+ import { useMemo, useState } from "react";
6
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
7
+ //#region src/file-explorer/toasts.tsx
8
+ function getSessionStatusInfo(session, queueLength, strings) {
9
+ switch (session.status) {
10
+ case "queued": return {
11
+ icon: /* @__PURE__ */ jsx(CloudUploadIcon, { className: "w-5 h-5 text-blue-600" }),
12
+ title: strings.inQueue(session.queueIndex + 1, queueLength),
13
+ shortTitle: strings.queuedShort(session.queueIndex + 1, queueLength),
14
+ bgColor: "bg-blue-100"
15
+ };
16
+ case "uploading": return {
17
+ icon: /* @__PURE__ */ jsx(CloudUploadIcon, { className: "w-5 h-5 text-blue-600" }),
18
+ title: strings.uploading,
19
+ shortTitle: strings.uploading,
20
+ bgColor: "bg-blue-100"
21
+ };
22
+ case "completed": return {
23
+ icon: /* @__PURE__ */ jsx(SuccessIcon, { className: "w-5 h-5 text-green-600" }),
24
+ title: strings.completeTitle,
25
+ shortTitle: strings.completeShort,
26
+ bgColor: "bg-green-100"
27
+ };
28
+ case "failed": return {
29
+ icon: /* @__PURE__ */ jsx(AlertIcon, { className: "w-5 h-5 text-red-600" }),
30
+ title: strings.failedTitle,
31
+ shortTitle: strings.failedShort,
32
+ bgColor: "bg-red-100"
33
+ };
34
+ default: return {
35
+ icon: /* @__PURE__ */ jsx(CloudUploadIcon, { className: "w-5 h-5 theme-muted-text" }),
36
+ title: strings.unknownTitle,
37
+ shortTitle: strings.unknownShort,
38
+ bgColor: "bg-(--theme-muted-panel-bg)"
39
+ };
40
+ }
41
+ }
42
+ function FileProgressBar({ fileProgress, fileName, progressBarColorScheme, onCancelFile }) {
43
+ const strings = useStrings().fileExplorer.upload;
44
+ const [isHovered, setIsHovered] = useState(false);
45
+ const [showCancelConfirm, setShowCancelConfirm] = useState(false);
46
+ const percentage = fileProgress?.percentage || 0;
47
+ const loadedBytes = fileProgress?.loadedBytes || 0;
48
+ const totalBytes = fileProgress.fileSizeBytes || fileProgress?.totalBytes || 0;
49
+ const isCompleted = percentage === 100 || fileProgress.status === "completed";
50
+ const isCancelled = fileProgress.status === "cancelled";
51
+ const isFailed = fileProgress.status === "failed";
52
+ const isUploading = fileProgress.status === "uploading" || !isCompleted && !isCancelled && !isFailed;
53
+ const canCancel = isUploading && fileProgress.abortController && !isCompleted && !isCancelled && !isFailed;
54
+ const barColor = isCompleted ? progressBarColorScheme.completed : isCancelled ? progressBarColorScheme.cancelled : isFailed ? progressBarColorScheme.failed : progressBarColorScheme.uploading;
55
+ const handleCancelClick = (e) => {
56
+ e.stopPropagation();
57
+ setShowCancelConfirm(true);
58
+ };
59
+ const handleConfirmCancel = (e) => {
60
+ e.stopPropagation();
61
+ onCancelFile?.();
62
+ setShowCancelConfirm(false);
63
+ };
64
+ const handleKeepUploading = (e) => {
65
+ e.stopPropagation();
66
+ setShowCancelConfirm(false);
67
+ };
68
+ return /* @__PURE__ */ jsxs("div", {
69
+ className: "space-y-1",
70
+ children: [/* @__PURE__ */ jsxs("div", {
71
+ className: "flex items-center justify-between text-xs",
72
+ children: [/* @__PURE__ */ jsx("div", {
73
+ className: "text-xs theme-muted-text text-wrap max-w-[75%]",
74
+ children: fileName
75
+ }), /* @__PURE__ */ jsxs("div", {
76
+ className: "text-xs theme-muted-text text-right self-end",
77
+ children: [
78
+ isCancelled && strings.cancelled,
79
+ isCompleted && strings.completed,
80
+ isFailed && strings.failed,
81
+ isUploading && `${percentage.toFixed(2)}%`
82
+ ]
83
+ })]
84
+ }), /* @__PURE__ */ jsxs("div", {
85
+ className: "flex items-center gap-1",
86
+ children: [
87
+ /* @__PURE__ */ jsx("div", {
88
+ role: "none",
89
+ className: "relative flex-1",
90
+ onMouseEnter: () => setIsHovered(true),
91
+ onMouseLeave: () => setIsHovered(false),
92
+ children: /* @__PURE__ */ jsxs("div", {
93
+ className: cx("w-full bg-(--theme-border) rounded-full overflow-hidden transition-all duration-300 ease-in-out flex items-center", isHovered && !isCompleted ? "h-6" : "h-2"),
94
+ children: [/* @__PURE__ */ jsx("div", {
95
+ className: cx("h-full transition-all duration-300 relative flex items-center justify-center", barColor, isHovered && "opacity-80"),
96
+ style: { width: `${percentage}%` }
97
+ }), isHovered && !isCompleted && totalBytes > 0 && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("div", {
98
+ className: "absolute inset-0 flex items-center justify-center text-xs whitespace-nowrap p-0.5 z-10",
99
+ style: {
100
+ WebkitClipPath: `inset(0 ${100 - percentage}% 0 0)`,
101
+ color: "white"
102
+ },
103
+ children: [
104
+ formatFileSize(loadedBytes),
105
+ " / ",
106
+ formatFileSize(totalBytes)
107
+ ]
108
+ }), /* @__PURE__ */ jsxs("div", {
109
+ className: "absolute inset-0 flex items-center justify-center text-xs whitespace-nowrap p-0.5 z-10",
110
+ style: {
111
+ WebkitClipPath: `inset(0 0 0 ${percentage}%)`,
112
+ color: "black"
113
+ },
114
+ children: [
115
+ formatFileSize(loadedBytes),
116
+ " / ",
117
+ formatFileSize(totalBytes)
118
+ ]
119
+ })] })]
120
+ })
121
+ }),
122
+ !showCancelConfirm && canCancel && /* @__PURE__ */ jsx("button", {
123
+ type: "button",
124
+ "aria-label": `Cancel ${fileName}`,
125
+ className: "p-1 rounded-full hover:bg-red-100 relative cursor-pointer",
126
+ onClick: handleCancelClick,
127
+ title: `Cancel ${fileName}`,
128
+ children: /* @__PURE__ */ jsx(CloseIcon, { className: "w-4 h-4 text-red-500 hover:text-red-700" })
129
+ }),
130
+ showCancelConfirm && canCancel && /* @__PURE__ */ jsxs("div", {
131
+ className: "flex items-center gap-1",
132
+ children: [
133
+ /* @__PURE__ */ jsx("span", {
134
+ className: "text-xs theme-muted-text whitespace-nowrap",
135
+ children: "Cancel?"
136
+ }),
137
+ /* @__PURE__ */ jsx("button", {
138
+ type: "button",
139
+ className: "px-1.5 py-0.5 text-xs bg-(--theme-border) theme-muted-text rounded hover:theme-hover",
140
+ onClick: handleKeepUploading,
141
+ children: "No"
142
+ }),
143
+ /* @__PURE__ */ jsx("button", {
144
+ type: "button",
145
+ className: "!px-1.5 !py-0.5 text-xs btn btn-destructive cursor-pointer",
146
+ onClick: handleConfirmCancel,
147
+ children: "Yes"
148
+ })
149
+ ]
150
+ })
151
+ ]
152
+ })]
153
+ });
154
+ }
155
+ function UploadSessionToast({ session, queueLength, onCancel, onCancelFile }) {
156
+ const strings = useStrings().fileExplorer.upload;
157
+ const [isToastExpanded, setIsToastExpanded] = useState(false);
158
+ const [isFileListExpanded, setIsExpanded] = useState(session.uploadNodes.length <= 3);
159
+ const [showCancelConfirm, setShowCancelConfirm] = useState(false);
160
+ const statusInfo = useMemo(() => getSessionStatusInfo(session, queueLength, strings), [
161
+ session,
162
+ queueLength,
163
+ strings
164
+ ]);
165
+ const targetPath = `${session.targetPath}/`;
166
+ const isQueued = session.status === "queued";
167
+ const isCompleted = session.status === "completed";
168
+ const isCancelled = session.status === "cancelled";
169
+ const isFailed = session.status === "failed";
170
+ const isUploading = session.status === "uploading" || !isCompleted && !isCancelled && !isFailed;
171
+ const totalProgress = session.progress?.overallProgress || 0;
172
+ const totalSize = session.progress?.totalSizeBytes || 0;
173
+ const progressBarColorScheme = {
174
+ uploading: "theme-element",
175
+ completed: "bg-green-600",
176
+ failed: "bg-red-600",
177
+ cancelled: "bg-(--theme-muted-text-color)"
178
+ };
179
+ const barColor = isCompleted ? progressBarColorScheme.completed : isCancelled ? progressBarColorScheme.cancelled : isFailed ? progressBarColorScheme.failed : progressBarColorScheme.uploading;
180
+ const allFiles = session.progress?.allFiles ? Array.from(session.progress.allFiles.values()) : [];
181
+ const uploadingFiles = session.progress?.allFiles ? Array.from(session.progress.allFiles.values()).filter((file) => file.percentage && file.percentage >= 0 && file.percentage < 100) : [];
182
+ const shouldShowSessionCancelButton = !showCancelConfirm && (isUploading || isQueued) && session.uploadNodes.length > 1;
183
+ const estimatedSecondsLeft = session.progress?.estimatedSecondsLeft;
184
+ const showETA = isUploading && estimatedSecondsLeft && estimatedSecondsLeft > 0;
185
+ const handleExpandClick = (e) => {
186
+ e.stopPropagation();
187
+ setIsToastExpanded(!isToastExpanded);
188
+ };
189
+ const handleCancelClick = () => {
190
+ setShowCancelConfirm(true);
191
+ };
192
+ const handleConfirmCancel = () => {
193
+ onCancel?.();
194
+ setShowCancelConfirm(false);
195
+ };
196
+ const handleKeepUploading = () => {
197
+ setShowCancelConfirm(false);
198
+ };
199
+ return /* @__PURE__ */ jsx("div", {
200
+ className: "flex flex-col w-full transition-all duration-300 ease-in-out overflow-hidden",
201
+ children: /* @__PURE__ */ jsxs("div", {
202
+ className: "flex flex-col w-full space-y-3",
203
+ children: [!isToastExpanded && /* @__PURE__ */ jsxs("div", {
204
+ className: "flex items-center gap-3 flex-1",
205
+ children: [/* @__PURE__ */ jsx("div", {
206
+ className: cx("w-10 h-10 rounded-full flex items-center justify-center", statusInfo.bgColor),
207
+ children: statusInfo.icon
208
+ }), /* @__PURE__ */ jsxs("div", {
209
+ role: "none",
210
+ className: "flex-1 cursor-pointer",
211
+ onClick: handleExpandClick,
212
+ children: [
213
+ /* @__PURE__ */ jsxs("div", {
214
+ className: "flex items-center justify-between",
215
+ children: [/* @__PURE__ */ jsx("h3", {
216
+ className: "text-sm font-medium theme-text truncate",
217
+ children: statusInfo.shortTitle
218
+ }), /* @__PURE__ */ jsxs("div", {
219
+ className: "flex items-center justify-between gap-2",
220
+ children: [/* @__PURE__ */ jsxs("div", {
221
+ className: "text-xs theme-muted-text ml-2",
222
+ children: [totalProgress.toFixed(1), "%"]
223
+ }), /* @__PURE__ */ jsx("button", {
224
+ type: "button",
225
+ "aria-label": strings.expandDetails,
226
+ className: "flex items-center justify-between text-xs theme-muted-text hover:theme-text cursor-pointer",
227
+ onClick: handleExpandClick,
228
+ title: strings.expandDetails,
229
+ children: /* @__PURE__ */ jsx(ExpandVerticalIcon, { className: "w-4 h-4" })
230
+ })]
231
+ })]
232
+ }),
233
+ /* @__PURE__ */ jsxs("div", {
234
+ className: "flex items-center justify-between",
235
+ children: [/* @__PURE__ */ jsxs("div", {
236
+ className: "text-xs theme-muted-text mt-0.5",
237
+ children: [
238
+ session.progress?.completedFiles || 0,
239
+ " / ",
240
+ session.progress?.totalFiles || 0,
241
+ " ",
242
+ "files done"
243
+ ]
244
+ }), /* @__PURE__ */ jsx("div", {
245
+ className: "text-xs theme-muted-text mt-0.5",
246
+ children: showETA && formatETA(estimatedSecondsLeft)
247
+ })]
248
+ }),
249
+ /* @__PURE__ */ jsx("div", {
250
+ className: "mt-1 flex items-center gap-2",
251
+ children: /* @__PURE__ */ jsx("div", {
252
+ className: "flex-1 h-1.5 bg-(--theme-border) rounded-full overflow-hidden",
253
+ children: /* @__PURE__ */ jsx("div", {
254
+ className: cx("h-full transition-all duration-300", barColor),
255
+ style: { width: `${totalProgress}%` }
256
+ })
257
+ })
258
+ })
259
+ ]
260
+ })]
261
+ }), isToastExpanded && /* @__PURE__ */ jsxs(Fragment$1, { children: [
262
+ /* @__PURE__ */ jsxs("div", {
263
+ className: "flex items-center justify-between",
264
+ children: [/* @__PURE__ */ jsxs("div", {
265
+ className: "flex items-center gap-3",
266
+ children: [/* @__PURE__ */ jsx("div", {
267
+ className: cx("w-10 h-10 rounded-full flex items-center justify-center", statusInfo.bgColor),
268
+ children: statusInfo.icon
269
+ }), /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("h3", {
270
+ className: "text-sm font-semibold theme-text",
271
+ children: statusInfo.title
272
+ }), /* @__PURE__ */ jsxs("p", {
273
+ className: "text-xs theme-muted-text",
274
+ children: [
275
+ uploadingFiles.length > 0 && `${uploadingFiles.length} uploading `,
276
+ "(",
277
+ session.progress?.completedFiles || 0,
278
+ " / ",
279
+ session.progress?.totalFiles || 0,
280
+ " ",
281
+ "done)"
282
+ ]
283
+ })] })]
284
+ }), /* @__PURE__ */ jsx("button", {
285
+ type: "button",
286
+ "aria-label": strings.minimizeDetails,
287
+ className: "flex items-center justify-between text-xs theme-muted-text hover:theme-text cursor-pointer mr-2",
288
+ onClick: handleExpandClick,
289
+ title: strings.minimizeDetails,
290
+ children: /* @__PURE__ */ jsx(MinimizeIcon, { className: "w-4 h-4" })
291
+ })]
292
+ }),
293
+ /* @__PURE__ */ jsx("div", {
294
+ className: "mb-4 p-3 bg-(--theme-muted-panel-bg) rounded-lg",
295
+ children: /* @__PURE__ */ jsxs("div", {
296
+ className: "text-xs theme-muted-text space-y-1",
297
+ children: [
298
+ /* @__PURE__ */ jsxs("div", {
299
+ className: "flex justify-between",
300
+ children: [/* @__PURE__ */ jsx("span", { children: strings.destination }), /* @__PURE__ */ jsx("span", {
301
+ className: "font-medium truncate",
302
+ children: targetPath
303
+ })]
304
+ }),
305
+ /* @__PURE__ */ jsxs("div", {
306
+ className: "flex justify-between",
307
+ children: [/* @__PURE__ */ jsx("span", { children: session.uploadNodes.length > 1 ? strings.totalSize : strings.size }), /* @__PURE__ */ jsx("span", { children: formatFileSize(totalSize) })]
308
+ }),
309
+ /* @__PURE__ */ jsxs("div", {
310
+ className: "flex justify-between",
311
+ children: [/* @__PURE__ */ jsx("span", { children: strings.eta }), showETA ? formatETA(estimatedSecondsLeft) : strings.calculating]
312
+ })
313
+ ]
314
+ })
315
+ }),
316
+ isUploading && /* @__PURE__ */ jsx("div", {
317
+ className: "mb-4 p-3 bg-amber-50 border border-amber-200 rounded-lg",
318
+ children: /* @__PURE__ */ jsxs("div", {
319
+ className: "flex gap-2",
320
+ children: [/* @__PURE__ */ jsx(AlertIcon, { className: "w-4 h-4 text-amber-600 flex-shrink-0 mt-0.5" }), /* @__PURE__ */ jsx("p", {
321
+ className: "text-xs text-amber-800",
322
+ children: "Please don't refresh this page during upload to prevent any data loss."
323
+ })]
324
+ })
325
+ }),
326
+ session.uploadNodes.length > 1 && (isUploading || isQueued) && /* @__PURE__ */ jsxs("div", {
327
+ className: "space-y-2",
328
+ children: [/* @__PURE__ */ jsxs("div", {
329
+ className: "flex items-center justify-between text-xs theme-muted-text",
330
+ children: [/* @__PURE__ */ jsx("span", { children: strings.overallProgress }), /* @__PURE__ */ jsxs("span", { children: [totalProgress.toFixed(1), "%"] })]
331
+ }), /* @__PURE__ */ jsx("div", {
332
+ className: "h-2 w-full bg-(--theme-border) rounded-full overflow-hidden",
333
+ children: /* @__PURE__ */ jsx("div", {
334
+ className: cx("h-full transition-all duration-300", barColor),
335
+ style: { width: `${totalProgress}%` }
336
+ })
337
+ })]
338
+ }),
339
+ session.uploadNodes.length > 1 && /* @__PURE__ */ jsxs("div", {
340
+ className: "flex flex-col bg-(--theme-muted-panel-bg) rounded py-2 px-3 space-y-2 transition-all duration-300",
341
+ children: [/* @__PURE__ */ jsxs("button", {
342
+ type: "button",
343
+ className: "flex items-center justify-between text-xs theme-muted-text hover:theme-text cursor-pointer",
344
+ onClick: () => setIsExpanded(!isFileListExpanded),
345
+ children: [/* @__PURE__ */ jsxs("span", { children: [session.uploadNodes.length, " files"] }), isFileListExpanded ? /* @__PURE__ */ jsx(AngleUpIcon, { className: "w-4 h-4" }) : /* @__PURE__ */ jsx(AngleDownIcon, { className: "w-4 h-4" })]
346
+ }), isFileListExpanded && /* @__PURE__ */ jsx("div", {
347
+ className: "max-h-48 overflow-y-auto space-y-2",
348
+ children: session.uploadNodes.map((node) => {
349
+ const fileProgress = session.progress?.allFiles?.get(node.relativePath);
350
+ if (!fileProgress) return null;
351
+ return /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(FileProgressBar, {
352
+ fileProgress,
353
+ fileName: node.name,
354
+ progressBarColorScheme,
355
+ onCancelFile: () => onCancelFile?.(node.relativePath)
356
+ }) }, node.relativePath);
357
+ })
358
+ })]
359
+ }),
360
+ session.uploadNodes.length === 1 && allFiles.length > 0 && /* @__PURE__ */ jsx(FileProgressBar, {
361
+ fileProgress: allFiles[0],
362
+ fileName: session.uploadNodes[0].name,
363
+ progressBarColorScheme,
364
+ onCancelFile: () => onCancelFile?.(session.uploadNodes[0].relativePath)
365
+ }),
366
+ /* @__PURE__ */ jsxs("div", {
367
+ className: "flex items-center justify-end",
368
+ children: [shouldShowSessionCancelButton && /* @__PURE__ */ jsx("button", {
369
+ type: "button",
370
+ className: "!px-2 !py-1 text-xs btn btn-destructive cursor-pointer transition-colors",
371
+ onClick: handleCancelClick,
372
+ children: "Cancel"
373
+ }), showCancelConfirm && /* @__PURE__ */ jsxs("div", {
374
+ className: "flex items-center space-x-2",
375
+ children: [
376
+ /* @__PURE__ */ jsx("span", {
377
+ className: "text-xs theme-muted-text",
378
+ children: "Cancel all uploading files?"
379
+ }),
380
+ /* @__PURE__ */ jsx("button", {
381
+ type: "button",
382
+ className: "!px-2 !py-1 text-xs bg-(--theme-border) theme-muted-text rounded hover:theme-hover",
383
+ onClick: handleKeepUploading,
384
+ children: "No"
385
+ }),
386
+ /* @__PURE__ */ jsx("button", {
387
+ type: "button",
388
+ className: "!px-2 !py-1 text-xs btn btn-destructive cursor-pointer",
389
+ onClick: handleConfirmCancel,
390
+ children: "Yes"
391
+ })
392
+ ]
393
+ })]
394
+ })
395
+ ] })]
396
+ })
397
+ });
398
+ }
399
+ //#endregion
400
+ export { UploadSessionToast };
@@ -0,0 +1,73 @@
1
+ import { getNodeChildren } from "./lib/utils.js";
2
+ /** `stepRow` skips these, so neither kind is ever an arrow-key stop. */
3
+ function statusRow(kind, node, depth, hiddenFiles = 0) {
4
+ return {
5
+ kind,
6
+ key: `${node.path}\u0000${kind}`,
7
+ node,
8
+ depth,
9
+ posInSet: 1,
10
+ setSize: 1,
11
+ isExpanded: false,
12
+ hiddenFiles
13
+ };
14
+ }
15
+ function countFiles(children) {
16
+ return children.reduce((total, child) => child.type === "directory" ? total : total + 1, 0);
17
+ }
18
+ /** Flattens the tree to exactly the rows that should be on screen, so the sidebar
19
+ * can be windowed and keyboard navigation can move by index instead of by DOM. */
20
+ function flattenTreeRows(roots, parentToChildrenMap, expandedPaths, options) {
21
+ const rows = [];
22
+ const visit = (node, index, setSize, depth) => {
23
+ const isDirectory = node.type === "directory";
24
+ const children = isDirectory ? getNodeChildren(parentToChildrenMap, node.path) : [];
25
+ const isExpanded = isDirectory && expandedPaths.has(node.path);
26
+ rows.push({
27
+ kind: "node",
28
+ key: node.path,
29
+ node,
30
+ depth,
31
+ posInSet: index + 1,
32
+ setSize,
33
+ isExpanded
34
+ });
35
+ if (!isExpanded) return;
36
+ if (options?.loadingPaths?.has(node.path) && children.length === 0) {
37
+ rows.push(statusRow("skeleton", node, depth + 1));
38
+ return;
39
+ }
40
+ const limit = options?.fileLimits?.get(node.path) ?? 50;
41
+ walkChildren(children, depth + 1, limit);
42
+ const hiddenFiles = Math.max(0, countFiles(children) - limit);
43
+ if (hiddenFiles > 0 || options?.incompletePaths?.has(node.path)) rows.push(statusRow("more", node, depth + 1, hiddenFiles));
44
+ };
45
+ const walkChildren = (children, depth, limit) => {
46
+ let files = 0;
47
+ children.forEach((child, index) => {
48
+ if (child.type !== "directory") {
49
+ files += 1;
50
+ if (files > limit) return;
51
+ }
52
+ visit(child, index, children.length, depth);
53
+ });
54
+ };
55
+ walkChildren(roots, 0, 50);
56
+ return rows;
57
+ }
58
+ function nextFileLimit(current) {
59
+ return (current ?? 50) + 50;
60
+ }
61
+ /** Index of `path` among the visible rows, or -1. */
62
+ function findRowIndex(rows, path) {
63
+ return rows.findIndex((row) => row.kind === "node" && row.node.path === path);
64
+ }
65
+ /** The next selectable row in `direction`, skipping skeleton and more rows. */
66
+ function stepRow(rows, from, direction) {
67
+ for (let i = from + direction; i >= 0 && i < rows.length; i += direction) {
68
+ const row = rows[i];
69
+ if (row?.kind === "node") return row;
70
+ }
71
+ }
72
+ //#endregion
73
+ export { findRowIndex, flattenTreeRows, nextFileLimit, stepRow };
@@ -0,0 +1,27 @@
1
+ import { useEffect, useRef } from "react";
2
+ //#region src/file-explorer/useNearEnd.ts
3
+ /** Fires once the element comes within 100px of the bottom of `root`. */
4
+ function useNearEnd(ref, { active, root, onNear }) {
5
+ const latestOnNear = useRef(onNear);
6
+ useEffect(() => {
7
+ latestOnNear.current = onNear;
8
+ }, [onNear]);
9
+ useEffect(() => {
10
+ const el = ref.current;
11
+ if (!active || !el) return;
12
+ const observer = new IntersectionObserver((entries) => {
13
+ if (entries.some((entry) => entry.isIntersecting)) latestOnNear.current();
14
+ }, {
15
+ root,
16
+ rootMargin: "100px"
17
+ });
18
+ observer.observe(el);
19
+ return () => observer.disconnect();
20
+ }, [
21
+ ref,
22
+ active,
23
+ root
24
+ ]);
25
+ }
26
+ //#endregion
27
+ export { useNearEnd };
@@ -0,0 +1,54 @@
1
+ import { RequiredMark } from "../components/RequiredMark.js";
2
+ import { FieldProvider } from "./fieldContext.js";
3
+ import { resolvedFlag } from "./lib.js";
4
+ import cx from "classnames";
5
+ import { useId, useMemo } from "react";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ //#region src/form/FieldWrapper.tsx
8
+ function FieldWrapper({ label, tooltipComponent, description, labelPosition = "left", spaceCompact = false, optional = false, children }) {
9
+ const isOptional = Boolean(resolvedFlag(optional));
10
+ const uid = useId();
11
+ const id = `${uid}control`;
12
+ const labelId = `${uid}label`;
13
+ const descriptionId = description ? `${uid}description` : void 0;
14
+ const field = useMemo(() => ({
15
+ required: !isOptional,
16
+ id,
17
+ labelId,
18
+ descriptionId
19
+ }), [
20
+ isOptional,
21
+ id,
22
+ labelId,
23
+ descriptionId
24
+ ]);
25
+ return /* @__PURE__ */ jsxs("div", {
26
+ className: cx("flex w-full", labelPosition === "left" ? "items-start" : "items-left flex-col"),
27
+ children: [/* @__PURE__ */ jsxs("label", {
28
+ htmlFor: id,
29
+ id: labelId,
30
+ className: cx("relative", labelPosition === "left" ? "form-label" : "form-label-top", spaceCompact && "form-label-compact"),
31
+ children: [
32
+ label,
33
+ !isOptional && /* @__PURE__ */ jsx(RequiredMark, {}),
34
+ tooltipComponent
35
+ ]
36
+ }), /* @__PURE__ */ jsxs("div", {
37
+ className: "w-full",
38
+ children: [/* @__PURE__ */ jsx(FieldProvider, {
39
+ value: field,
40
+ children: typeof children === "function" ? children({
41
+ id,
42
+ labelId,
43
+ describedBy: descriptionId
44
+ }) : children
45
+ }), description && /* @__PURE__ */ jsx("div", {
46
+ id: descriptionId,
47
+ className: "text-xs theme-muted-text mt-1 leading-relaxed",
48
+ children: description
49
+ })]
50
+ })]
51
+ });
52
+ }
53
+ //#endregion
54
+ export { FieldWrapper };
@@ -0,0 +1,51 @@
1
+ import SwitchToggle from "../../components/SwitchToggle.js";
2
+ import { FieldWrapper } from "../FieldWrapper.js";
3
+ import { jsx } from "react/jsx-runtime";
4
+ import { useField, useFormikContext } from "formik";
5
+ //#region src/form/Fields/BooleanField.tsx
6
+ function BooleanField(props) {
7
+ const { field, label, labelPosition, missing, disabled, onChange, tooltipComponent, spaceCompact, setFormDirty } = props;
8
+ const [fieldState] = useField(field.name);
9
+ const { setFieldValue, setFieldTouched } = useFormikContext();
10
+ const handleChange = (val) => {
11
+ if (disabled) return;
12
+ if (onChange) onChange(val);
13
+ setFieldTouched(field.name, true);
14
+ setFieldValue(field.name, val);
15
+ setFormDirty(true);
16
+ };
17
+ const value = fieldState.value;
18
+ const options = field.options ? {
19
+ onOption: String(field.options.onOption ?? true),
20
+ offOption: String(field.options.offOption ?? false)
21
+ } : void 0;
22
+ const displayLabel = (option) => option.charAt(0).toUpperCase() + option.slice(1).toLowerCase();
23
+ return /* @__PURE__ */ jsx(FieldWrapper, {
24
+ optional: true,
25
+ label,
26
+ tooltipComponent: /* @__PURE__ */ jsx("div", { children: tooltipComponent }),
27
+ description: field.description,
28
+ labelPosition,
29
+ spaceCompact,
30
+ children: ({ id, describedBy }) => options ? /* @__PURE__ */ jsx(SwitchToggle, {
31
+ id,
32
+ "aria-describedby": describedBy,
33
+ onChange: (on) => handleChange(on ? options.onOption : options.offOption),
34
+ value: value === true || value === options.onOption,
35
+ yesLabel: displayLabel(options.onOption),
36
+ noLabel: displayLabel(options.offOption),
37
+ disabled,
38
+ invalid: missing
39
+ }) : /* @__PURE__ */ jsx(SwitchToggle, {
40
+ id,
41
+ "aria-describedby": describedBy,
42
+ datatip: field.disabled_message,
43
+ onChange: handleChange,
44
+ value: value === true || value === "true",
45
+ disabled,
46
+ invalid: missing
47
+ })
48
+ });
49
+ }
50
+ //#endregion
51
+ export { BooleanField as default };
@@ -0,0 +1,72 @@
1
+ import { FieldWrapper } from "../FieldWrapper.js";
2
+ import cx from "classnames";
3
+ import { useState } from "react";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ import { useField, useFormikContext } from "formik";
6
+ //#region src/form/Fields/CheckboxGroupField.tsx
7
+ function impliedFrom(values, implies = {}) {
8
+ const result = /* @__PURE__ */ new Set();
9
+ for (const value of values) for (const target of implies[value] ?? []) result.add(target);
10
+ return result;
11
+ }
12
+ function union(manual, forced) {
13
+ return [...manual, ...[...forced].filter((v) => !manual.includes(v))];
14
+ }
15
+ function CheckboxGroupField({ field, label, labelPosition, missing, disabled, onChange, tooltipComponent, spaceCompact, setFormDirty }) {
16
+ const [fieldState] = useField(field.name);
17
+ const { setFieldValue, setFieldTouched } = useFormikContext();
18
+ const implies = field.implies;
19
+ const [manual, setManual] = useState(() => {
20
+ const saved = fieldState.value || [];
21
+ const impliedBySaved = impliedFrom(saved, implies);
22
+ return saved.filter((v) => !impliedBySaved.has(v));
23
+ });
24
+ const forced = impliedFrom(manual, implies);
25
+ const handleToggle = (value) => {
26
+ if (disabled) return;
27
+ const nextManual = manual.includes(value) ? manual.filter((v) => v !== value) : [...manual, value];
28
+ const next = union(nextManual, impliedFrom(nextManual, implies));
29
+ setManual(nextManual);
30
+ setFieldTouched(field.name, true);
31
+ setFieldValue(field.name, next);
32
+ if (onChange) onChange(next);
33
+ setFormDirty(true);
34
+ };
35
+ return /* @__PURE__ */ jsx(FieldWrapper, {
36
+ optional: field.optional,
37
+ label,
38
+ tooltipComponent,
39
+ labelPosition,
40
+ spaceCompact,
41
+ children: ({ labelId, describedBy }) => /* @__PURE__ */ jsx("fieldset", {
42
+ "aria-labelledby": labelId,
43
+ "aria-describedby": describedBy,
44
+ className: "flex min-w-0 max-w-lg flex-col gap-1",
45
+ children: field.options?.map((option) => {
46
+ const locked = forced.has(option.value);
47
+ const optionDisabled = disabled || locked;
48
+ return /* @__PURE__ */ jsxs("label", {
49
+ className: cx("-mx-2 flex items-start gap-2.5 rounded-md px-2 py-1.5 transition-colors", optionDisabled ? "cursor-not-allowed opacity-60" : "cursor-pointer hover:bg-(--theme-hover) has-[:focus-visible]:bg-(--theme-hover)"),
50
+ children: [/* @__PURE__ */ jsx("input", {
51
+ type: "checkbox",
52
+ checked: manual.includes(option.value) || locked,
53
+ disabled: optionDisabled,
54
+ onChange: () => handleToggle(option.value),
55
+ className: cx("mt-px h-4 w-4 rounded accent-(--theme-link)", optionDisabled ? "cursor-not-allowed" : "cursor-pointer", missing && "invalid")
56
+ }), /* @__PURE__ */ jsxs("div", {
57
+ className: "flex min-w-0 flex-col",
58
+ children: [/* @__PURE__ */ jsx("span", {
59
+ className: "font-medium leading-tight theme-text",
60
+ children: option.label
61
+ }), option.description && /* @__PURE__ */ jsx("span", {
62
+ className: "mt-0.5 text-xs leading-snug theme-muted-text",
63
+ children: option.description
64
+ })]
65
+ })]
66
+ }, field.name + option.value);
67
+ })
68
+ })
69
+ });
70
+ }
71
+ //#endregion
72
+ export { CheckboxGroupField as default };