@parallelworks/ui 0.18.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 (349) hide show
  1. package/README.md +86 -6
  2. package/dist/ai/adapter/memoryStore.d.ts +8 -0
  3. package/dist/ai/adapter/memoryStore.js +152 -0
  4. package/dist/ai/adapter/mock/index.d.ts +2 -0
  5. package/dist/ai/adapter/mock/index.js +3 -0
  6. package/dist/ai/adapter/mock/mockAdapter.d.ts +4 -0
  7. package/dist/ai/adapter/mock/mockAdapter.js +81 -0
  8. package/dist/ai/adapter/openai/createOpenAIStream.d.ts +9 -0
  9. package/dist/ai/adapter/openai/createOpenAIStream.js +33 -0
  10. package/dist/ai/adapter/openai/errors.d.ts +3 -0
  11. package/dist/ai/adapter/openai/errors.js +37 -0
  12. package/dist/ai/adapter/openai/index.d.ts +6 -0
  13. package/dist/ai/adapter/openai/index.js +6 -0
  14. package/dist/ai/adapter/openai/openaiAdapter.d.ts +9 -0
  15. package/dist/ai/adapter/openai/openaiAdapter.js +54 -0
  16. package/dist/ai/adapter/openai/responses.d.ts +4 -0
  17. package/dist/ai/adapter/openai/responses.js +108 -0
  18. package/dist/ai/adapter/openai/sse.d.ts +24 -0
  19. package/dist/ai/adapter/openai/sse.js +112 -0
  20. package/dist/ai/adapter/openai/wire.d.ts +40 -0
  21. package/dist/ai/adapter/types.d.ts +131 -0
  22. package/dist/ai/adapter/types.js +11 -0
  23. package/dist/ai/agent/classifier.d.ts +2 -0
  24. package/dist/ai/agent/classifier.js +12 -0
  25. package/dist/ai/agent/diff.d.ts +35 -0
  26. package/dist/ai/agent/diff.js +223 -0
  27. package/dist/ai/agent/diffHighlight.d.ts +6 -0
  28. package/dist/ai/agent/diffHighlight.js +101 -0
  29. package/dist/ai/agent/index.d.ts +6 -0
  30. package/dist/ai/agent/permissions.d.ts +11 -0
  31. package/dist/ai/agent/permissions.js +29 -0
  32. package/dist/ai/agent/toolfmt.d.ts +18 -0
  33. package/dist/ai/agent/toolfmt.js +383 -0
  34. package/dist/ai/agent/transcript.d.ts +38 -0
  35. package/dist/ai/agent/transcript.js +127 -0
  36. package/dist/ai/ai.css +325 -0
  37. package/dist/ai/components/AllocationSelector.d.ts +4 -0
  38. package/dist/ai/components/AllocationSelector.js +56 -0
  39. package/dist/ai/components/AttachmentManager.d.ts +1 -0
  40. package/dist/ai/components/AttachmentManager.js +144 -0
  41. package/dist/ai/components/AttachmentUpload.d.ts +24 -0
  42. package/dist/ai/components/AttachmentUpload.js +179 -0
  43. package/dist/ai/components/BlockedGroupBanner.d.ts +1 -0
  44. package/dist/ai/components/BlockedGroupBanner.js +29 -0
  45. package/dist/ai/components/BranchNavigator.d.ts +8 -0
  46. package/dist/ai/components/BranchNavigator.js +57 -0
  47. package/dist/ai/components/ChatEmptyState.d.ts +3 -0
  48. package/dist/ai/components/ChatEmptyState.js +171 -0
  49. package/dist/ai/components/ChatInput.d.ts +45 -0
  50. package/dist/ai/components/ChatInput.js +265 -0
  51. package/dist/ai/components/ChatLayout.d.ts +5 -0
  52. package/dist/ai/components/ChatLayout.js +15 -0
  53. package/dist/ai/components/ChatMessage.d.ts +24 -0
  54. package/dist/ai/components/ChatMessage.js +338 -0
  55. package/dist/ai/components/ChatMessageList.d.ts +35 -0
  56. package/dist/ai/components/ChatMessageList.js +301 -0
  57. package/dist/ai/components/ChatSidebar.d.ts +5 -0
  58. package/dist/ai/components/ChatSidebar.js +345 -0
  59. package/dist/ai/components/ChatThread.d.ts +3 -0
  60. package/dist/ai/components/ChatThread.js +198 -0
  61. package/dist/ai/components/ComposerChrome.d.ts +18 -0
  62. package/dist/ai/components/ComposerChrome.js +50 -0
  63. package/dist/ai/components/ComposerFrame.d.ts +16 -0
  64. package/dist/ai/components/ComposerFrame.js +33 -0
  65. package/dist/ai/components/DragOverlay.d.ts +5 -0
  66. package/dist/ai/components/DragOverlay.js +20 -0
  67. package/dist/ai/components/ModelSelector.d.ts +6 -0
  68. package/dist/ai/components/ModelSelector.js +343 -0
  69. package/dist/ai/components/ShareDialog.d.ts +11 -0
  70. package/dist/ai/components/ShareDialog.js +260 -0
  71. package/dist/ai/components/SlashMenu.d.ts +28 -0
  72. package/dist/ai/components/SlashMenu.js +91 -0
  73. package/dist/ai/components/agent/ActivityLine.d.ts +21 -0
  74. package/dist/ai/components/agent/ActivityLine.js +151 -0
  75. package/dist/ai/components/agent/AgentMessageParts.d.ts +54 -0
  76. package/dist/ai/components/agent/AgentMessageParts.js +721 -0
  77. package/dist/ai/components/agent/PermissionPicker.d.ts +11 -0
  78. package/dist/ai/components/agent/PermissionPicker.js +38 -0
  79. package/dist/ai/components/attachmentKeys.d.ts +4 -0
  80. package/dist/ai/components/attachmentKeys.js +10 -0
  81. package/dist/ai/core/ChatProvider.d.ts +66 -0
  82. package/dist/ai/core/ChatProvider.js +848 -0
  83. package/dist/ai/core/KeyboardShortcutsProvider.d.ts +5 -0
  84. package/dist/ai/core/KeyboardShortcutsProvider.js +9 -0
  85. package/dist/ai/core/chatReducer.d.ts +114 -0
  86. package/dist/ai/core/chatReducer.js +223 -0
  87. package/dist/ai/core/config.d.ts +47 -0
  88. package/dist/ai/core/config.js +45 -0
  89. package/dist/ai/core/events.d.ts +1 -0
  90. package/dist/ai/core/events.js +4 -0
  91. package/dist/ai/core/exportConversation.d.ts +4 -0
  92. package/dist/ai/core/exportConversation.js +42 -0
  93. package/dist/ai/core/greeting.d.ts +3 -0
  94. package/dist/ai/core/greeting.js +38 -0
  95. package/dist/ai/core/parts.d.ts +11 -0
  96. package/dist/ai/core/parts.js +84 -0
  97. package/dist/ai/core/useDragDrop.d.ts +14 -0
  98. package/dist/ai/core/useDragDrop.js +36 -0
  99. package/dist/ai/core/useKeyboardShortcuts.d.ts +12 -0
  100. package/dist/ai/core/useKeyboardShortcuts.js +77 -0
  101. package/dist/ai/core/useSSEStream.d.ts +34 -0
  102. package/dist/ai/core/useSSEStream.js +98 -0
  103. package/dist/ai/index.d.ts +40 -0
  104. package/dist/ai/index.js +44 -0
  105. package/dist/ai/strings.d.ts +167 -0
  106. package/dist/ai/strings.js +175 -0
  107. package/dist/ai/types.d.ts +236 -0
  108. package/dist/ai/ui/Avatar.d.ts +8 -0
  109. package/dist/ai/ui/Avatar.js +35 -0
  110. package/dist/ai/ui/Dropdown.d.ts +31 -0
  111. package/dist/ai/ui/Dropdown.js +110 -0
  112. package/dist/ai/ui/Markdown.d.ts +8 -0
  113. package/dist/ai/ui/Markdown.js +79 -0
  114. package/dist/ai/ui/Notice.d.ts +21 -0
  115. package/dist/ai/ui/Notice.js +77 -0
  116. package/dist/ai/ui/buttons.d.ts +1 -0
  117. package/dist/ai/ui/codeBreaks.d.ts +16 -0
  118. package/dist/ai/ui/codeBreaks.js +36 -0
  119. package/dist/ai/ui/prose.d.ts +1 -0
  120. package/dist/ai/ui/prose.js +13 -0
  121. package/dist/ai/ui/sidebar.d.ts +81 -0
  122. package/dist/ai/ui/sidebar.js +148 -0
  123. package/dist/ai/utils.d.ts +4 -0
  124. package/dist/ai/utils.js +23 -0
  125. package/dist/assets/{editor.worker-CuBRIwi-.js → editor.worker-BmHUIVG4.js} +194 -163
  126. package/dist/assets/{json.worker-8ytS02vz.js → json.worker-D8bJ92nw.js} +384 -338
  127. package/dist/assets/{yaml.worker-DJC-VUcv.js → yaml.worker-C4gyGAko.js} +15657 -13823
  128. package/dist/components/AddMenu.js +46 -0
  129. package/dist/components/Avatar.d.ts +1 -1
  130. package/dist/components/Avatar.js +46 -0
  131. package/dist/{chunks/BareModal-BtRiQezg.js → components/BareModal.js} +1 -1
  132. package/dist/components/Breadcrumbs.d.ts +1 -1
  133. package/dist/components/Breadcrumbs.js +83 -0
  134. package/dist/components/Button.js +83 -0
  135. package/dist/components/Callout.js +23 -0
  136. package/dist/components/Card.js +31 -0
  137. package/dist/components/CollapsiblePanel.js +77 -0
  138. package/dist/components/ConfirmModal.js +74 -0
  139. package/dist/components/CopyToClipboard.js +81 -0
  140. package/dist/{chunks/CreateModal-BY6DLmur.js → components/CreateModal.js} +10 -189
  141. package/dist/components/DescriptionList.js +42 -0
  142. package/dist/components/Dropdown.d.ts +1 -1
  143. package/dist/components/Dropdown.js +349 -0
  144. package/dist/components/EmptyState.d.ts +1 -1
  145. package/dist/components/EmptyState.js +27 -0
  146. package/dist/components/Filter.js +32 -0
  147. package/dist/components/FilterPill.js +13 -0
  148. package/dist/components/IconButton.js +34 -0
  149. package/dist/components/Indicator.d.ts +2 -2
  150. package/dist/{chunks/Indicator-hBSIxOPK.js → components/Indicator.js} +1 -1
  151. package/dist/components/Input.d.ts +1 -1
  152. package/dist/components/Input.js +88 -0
  153. package/dist/components/LetterBadge.d.ts +1 -1
  154. package/dist/components/LetterBadge.js +13 -0
  155. package/dist/components/Listbox.js +123 -0
  156. package/dist/{chunks/Loader-BXoXTZGc.js → components/Loader.js} +1 -1
  157. package/dist/components/Markdown.js +15 -0
  158. package/dist/components/MarkdownImpl.d.ts +1 -1
  159. package/dist/components/MultiSelect.js +171 -0
  160. package/dist/components/Overview.js +39 -0
  161. package/dist/components/PageHeader.js +194 -0
  162. package/dist/components/Pagination.js +35 -0
  163. package/dist/components/Provider.d.ts +16 -65
  164. package/dist/{chunks/Provider-Ghua9PkN.js → components/Provider.js} +54 -20
  165. package/dist/{chunks/RequiredMark-DfLdYbYZ.js → components/RequiredMark.js} +1 -1
  166. package/dist/components/SectionHeader.js +11 -0
  167. package/dist/components/SettingModeOption.js +30 -0
  168. package/dist/components/SettingRow.d.ts +1 -1
  169. package/dist/components/SettingRow.js +35 -0
  170. package/dist/components/SettingsCard.js +31 -0
  171. package/dist/components/SettingsGroup.d.ts +1 -1
  172. package/dist/components/SettingsGroup.js +17 -0
  173. package/dist/components/Sort.js +42 -0
  174. package/dist/components/StatusBadge.js +63 -0
  175. package/dist/components/SwitchToggle.js +59 -0
  176. package/dist/{chunks/SwitchToggleSmall-FcsaYSTs.js → components/SwitchToggleSmall.js} +1 -1
  177. package/dist/{chunks/Table-4OHjdKdO.js → components/Table.js} +7 -5
  178. package/dist/components/Toggle.js +20 -0
  179. package/dist/{chunks/Tooltip-Crxo_eU_.js → components/Tooltip.js} +6 -4
  180. package/dist/{chunks/StatusBadge-C1Blx_cW.js → components/dropdownUtils.js} +2 -63
  181. package/dist/components/focus.d.ts +2 -0
  182. package/dist/components/focus.js +7 -0
  183. package/dist/components/ghostButton.js +7 -0
  184. package/dist/components/inputClasses.js +4 -0
  185. package/dist/components/keys.d.ts +12 -0
  186. package/dist/components/keys.js +27 -0
  187. package/dist/components/relativeTime.js +32 -0
  188. package/dist/{chunks/useCssIsDark-DHr05dG2.js → components/useCssIsDark.js} +1 -1
  189. package/dist/duration.d.ts +3 -0
  190. package/dist/duration.js +17 -0
  191. package/dist/editor/Editor.js +13 -0
  192. package/dist/editor/EditorField.js +53 -0
  193. package/dist/editor/FormCodePanel.js +173 -0
  194. package/dist/editor/Monaco.d.ts +1 -20
  195. package/dist/editor/Monaco.js +112 -0
  196. package/dist/editor/index.d.ts +2 -1
  197. package/dist/editor/index.js +7 -2
  198. package/dist/editor/themes.js +91 -0
  199. package/dist/editor/workers.js +29 -0
  200. package/dist/editor/yaml.d.ts +5 -0
  201. package/dist/editor/yaml.js +15 -0
  202. package/dist/engine.d.ts +98 -0
  203. package/dist/file-explorer/FileExplorer.d.ts +11 -2
  204. package/dist/file-explorer/FileExplorer.js +1402 -0
  205. package/dist/file-explorer/FileExplorerContext.d.ts +1 -1
  206. package/dist/file-explorer/FileExplorerContext.js +370 -0
  207. package/dist/file-explorer/FilePreview/CodePreviewEditor.d.ts +1 -1
  208. package/dist/{chunks/CodePreviewEditor-4lx3kE-w.js → file-explorer/FilePreview/CodePreviewEditor.js} +3 -2
  209. package/dist/file-explorer/FilePreview/Inline.js +45 -0
  210. package/dist/file-explorer/FilePreview/StreamedBadge.js +16 -0
  211. package/dist/file-explorer/FilePreview/Viewer.js +526 -0
  212. package/dist/file-explorer/FilePreview/index.js +266 -0
  213. package/dist/file-explorer/FilePreview/parse.js +63 -0
  214. package/dist/file-explorer/FilePreview/previewCache.js +98 -0
  215. package/dist/file-explorer/FilePreview/previewType.d.ts +1 -1
  216. package/dist/file-explorer/FilePreview/previewType.js +151 -0
  217. package/dist/file-explorer/FilePreview/readCappedBytes.js +196 -0
  218. package/dist/file-explorer/FilePreview/useObjectBlobUrl.js +95 -0
  219. package/dist/file-explorer/FilePreview/useObjectText.js +109 -0
  220. package/dist/file-explorer/FilePreview/usePresignedUrl.js +73 -0
  221. package/dist/file-explorer/FileRow.d.ts +1 -1
  222. package/dist/file-explorer/FileRow.js +165 -0
  223. package/dist/file-explorer/FileRowsWindow.js +92 -0
  224. package/dist/file-explorer/FileTable.js +172 -0
  225. package/dist/file-explorer/FileTableHeaders.js +49 -0
  226. package/dist/file-explorer/LoadMoreRow.js +46 -0
  227. package/dist/file-explorer/TreeView.js +376 -0
  228. package/dist/file-explorer/helpers.d.ts +4 -4
  229. package/dist/file-explorer/helpers.js +537 -0
  230. package/dist/file-explorer/index.d.ts +3 -3
  231. package/dist/file-explorer/index.js +7 -5705
  232. package/dist/file-explorer/lib/batch.js +39 -0
  233. package/dist/file-explorer/lib/errors.js +9 -0
  234. package/dist/file-explorer/lib/paging.js +124 -0
  235. package/dist/file-explorer/lib/types.d.ts +6 -1
  236. package/dist/file-explorer/lib/utils.d.ts +1 -3
  237. package/dist/file-explorer/lib/utils.js +322 -0
  238. package/dist/file-explorer/toasts.js +400 -0
  239. package/dist/file-explorer/treeRows.js +73 -0
  240. package/dist/file-explorer/useNearEnd.d.ts +1 -1
  241. package/dist/file-explorer/useNearEnd.js +27 -0
  242. package/dist/form/FieldWrapper.d.ts +2 -2
  243. package/dist/form/FieldWrapper.js +54 -0
  244. package/dist/form/Fields/BooleanField.js +51 -0
  245. package/dist/form/Fields/CheckboxGroupField.js +72 -0
  246. package/dist/form/Fields/ColorField.js +33 -0
  247. package/dist/form/Fields/DropdownField.js +49 -0
  248. package/dist/form/Fields/DurationField.js +130 -0
  249. package/dist/form/Fields/LabelField.js +19 -0
  250. package/dist/form/Fields/MultiDropdownField.js +40 -0
  251. package/dist/form/Fields/NumberField.js +51 -0
  252. package/dist/form/Fields/ObjectField.js +54 -0
  253. package/dist/form/Fields/RadioField.js +49 -0
  254. package/dist/form/Fields/RangeField.js +58 -0
  255. package/dist/form/Fields/ResourceOptionIcon.d.ts +1 -1
  256. package/dist/form/Fields/ResourceOptionIcon.js +21 -0
  257. package/dist/form/Fields/StepField.js +6 -0
  258. package/dist/form/Fields/StringField.js +37 -0
  259. package/dist/form/Fields/TextAreaField.js +31 -0
  260. package/dist/form/Fields/durationValidity.js +11 -0
  261. package/dist/form/Form.d.ts +4 -21
  262. package/dist/form/Form.js +643 -0
  263. package/dist/form/SpecialTypes/DropdownWrapper.js +30 -0
  264. package/dist/form/SpecialTypes/FormikCustomDropdown.js +122 -0
  265. package/dist/form/SpecialTypes/FormikCustomInput.js +111 -0
  266. package/dist/form/SpecialTypes/FormikMultiSelectDropdown.js +31 -0
  267. package/dist/form/SpecialTypes/FormikNumericInput.js +60 -0
  268. package/dist/form/Wizard/WizardContainer.js +138 -0
  269. package/dist/form/Wizard/WizardNavigation.js +92 -0
  270. package/dist/form/Wizard/WizardStepContent.js +35 -0
  271. package/dist/form/Wizard/WizardStepIndicator.js +108 -0
  272. package/dist/form/Wizard/useWizardState.js +118 -0
  273. package/dist/form/Wizard/utils.js +34 -0
  274. package/dist/form/fieldContext.js +26 -0
  275. package/dist/form/fieldRegistry.js +50 -0
  276. package/dist/form/index.d.ts +11 -12
  277. package/dist/form/index.js +18 -2445
  278. package/dist/form/lib.d.ts +6 -1
  279. package/dist/{chunks/FieldWrapper-Bef01EWO.js → form/lib.js} +5 -104
  280. package/dist/form/types/fieldComponentTypes.d.ts +3 -2
  281. package/dist/form/types/fieldTypes.d.ts +6 -20
  282. package/dist/form/useParsedOpts.d.ts +1 -1
  283. package/dist/form/useParsedOpts.js +62 -0
  284. package/dist/form/usePrevious.js +11 -0
  285. package/dist/form/utils/checkShowIfObject.js +14 -0
  286. package/dist/form/utils/{slurmDuration.d.ts → duration.d.ts} +2 -2
  287. package/dist/form/utils/duration.js +69 -0
  288. package/dist/form/utils/getParentValue.js +23 -0
  289. package/dist/form/utils/getPlaceholder.js +9 -0
  290. package/dist/form/utils/getValueUsingPath.js +23 -0
  291. package/dist/form/utils/secondaryField.js +22 -0
  292. package/dist/graph/AnnotationBanner.js +141 -0
  293. package/dist/graph/Collapse.js +41 -0
  294. package/dist/{workflow/index.js → graph/DependencyGraph.js} +76 -1163
  295. package/dist/graph/JobSummary.js +104 -0
  296. package/dist/{workflow → graph}/MatrixGroup.d.ts +2 -2
  297. package/dist/graph/MatrixGroup.js +107 -0
  298. package/dist/graph/Reveal.js +57 -0
  299. package/dist/graph/TreeView.js +711 -0
  300. package/dist/{workflow → graph}/index.d.ts +1 -1
  301. package/dist/graph/index.js +2 -0
  302. package/dist/{workflow → graph}/types.d.ts +3 -3
  303. package/dist/graph/types.js +6 -0
  304. package/dist/icons.d.ts +20 -21
  305. package/dist/index.d.ts +38 -40
  306. package/dist/index.js +47 -599
  307. package/dist/list/ActionToolbar.js +184 -0
  308. package/dist/list/HoverCard.js +73 -0
  309. package/dist/list/ListPage.js +145 -0
  310. package/dist/list/ListPager.js +59 -0
  311. package/dist/list/ListTable.d.ts +1 -1
  312. package/dist/list/ListTable.js +163 -0
  313. package/dist/list/ListViewControls.d.ts +2 -2
  314. package/dist/list/ListViewControls.js +372 -0
  315. package/dist/list/RowContextMenu.js +350 -0
  316. package/dist/list/UserHoverCard.js +117 -0
  317. package/dist/list/index.d.ts +9 -9
  318. package/dist/list/index.js +11 -2
  319. package/dist/list/listSearch.js +137 -0
  320. package/dist/list/listView.js +210 -0
  321. package/dist/list/textEntry.js +10 -0
  322. package/dist/logviewer/LogViewer.d.ts +1 -1
  323. package/dist/logviewer/LogViewer.js +750 -0
  324. package/dist/logviewer/index.d.ts +1 -0
  325. package/dist/logviewer/index.js +3 -742
  326. package/dist/logviewer/segments.d.ts +9 -0
  327. package/dist/logviewer/segments.js +80 -0
  328. package/dist/styles/fonts.css +4 -4
  329. package/dist/styles/theme.css +6 -4
  330. package/dist/styles.css +1 -1
  331. package/dist/theme/index.js +2 -2
  332. package/package.json +38 -14
  333. package/LICENSE +0 -202
  334. package/dist/chunks/Monaco-C1POOdVv.js +0 -1046
  335. package/dist/chunks/PageHeader-l49p1nM9.js +0 -690
  336. package/dist/chunks/SwitchToggle-CHPP8uZQ.js +0 -755
  337. package/dist/chunks/editor-9_uVtUyy.js +0 -347
  338. package/dist/chunks/list-qrojZZNv.js +0 -1411
  339. package/dist/editor/usesGitlab.d.ts +0 -97
  340. package/dist/form/Fields/BucketReadOnlyField.d.ts +0 -7
  341. package/dist/form/types/workflow-schema.d.ts +0 -1145
  342. package/dist/form/utils/pwResolver.d.ts +0 -18
  343. package/dist/{chunks/MarkdownImpl-Bq4QhrtA.js → components/MarkdownImpl.js} +1 -1
  344. /package/dist/{workflow → graph}/AnnotationBanner.d.ts +0 -0
  345. /package/dist/{workflow → graph}/Collapse.d.ts +0 -0
  346. /package/dist/{workflow → graph}/DependencyGraph.d.ts +0 -0
  347. /package/dist/{workflow → graph}/JobSummary.d.ts +0 -0
  348. /package/dist/{workflow → graph}/Reveal.d.ts +0 -0
  349. /package/dist/{workflow → graph}/TreeView.d.ts +0 -0
@@ -0,0 +1,196 @@
1
+ //#region src/file-explorer/FilePreview/readCappedBytes.ts
2
+ /** Read a Response body but stop once it passes `maxBytes` (returning `tooLarge`), so an object whose listed size is missing or under-reported can't be pulled fully into the tab. */
3
+ async function readCappedBytes(response, maxBytes) {
4
+ const reader = response.body?.getReader();
5
+ if (!reader) {
6
+ const header = response.headers.get("content-length");
7
+ const declared = header === null ? NaN : Number(header);
8
+ if (!Number.isFinite(declared) || declared > maxBytes) return {
9
+ bytes: /* @__PURE__ */ new Uint8Array(0),
10
+ tooLarge: true
11
+ };
12
+ const buffer = new Uint8Array(await response.arrayBuffer());
13
+ return buffer.byteLength > maxBytes ? {
14
+ bytes: /* @__PURE__ */ new Uint8Array(0),
15
+ tooLarge: true
16
+ } : {
17
+ bytes: buffer,
18
+ tooLarge: false
19
+ };
20
+ }
21
+ const chunks = [];
22
+ let total = 0;
23
+ for (let chunk = await reader.read(); !chunk.done; chunk = await reader.read()) {
24
+ total += chunk.value.byteLength;
25
+ if (total > maxBytes) {
26
+ await reader.cancel();
27
+ return {
28
+ bytes: /* @__PURE__ */ new Uint8Array(0),
29
+ tooLarge: true
30
+ };
31
+ }
32
+ chunks.push(chunk.value);
33
+ }
34
+ const bytes = new Uint8Array(total);
35
+ let offset = 0;
36
+ for (const chunk of chunks) {
37
+ bytes.set(chunk, offset);
38
+ offset += chunk.byteLength;
39
+ }
40
+ return {
41
+ bytes,
42
+ tooLarge: false
43
+ };
44
+ }
45
+ function countNewlines(text) {
46
+ let count = 0;
47
+ for (let i = 0; i < text.length; i++) if (text[i] === "\n") count++;
48
+ return count;
49
+ }
50
+ /** Lines a preview will render; a trailing newline doesn't open another line. */
51
+ function countLines(text) {
52
+ if (text === "") return 0;
53
+ return countNewlines(text) + (text.endsWith("\n") ? 0 : 1);
54
+ }
55
+ function capLines(text, maxLines) {
56
+ if (maxLines === void 0) return text;
57
+ let count = 0;
58
+ for (let i = 0; i < text.length; i++) if (text[i] === "\n") {
59
+ count++;
60
+ if (count >= maxLines) return text.slice(0, i + 1);
61
+ }
62
+ return text;
63
+ }
64
+ /** Reading one line past the cap is what makes `truncated` exact: a body that ends
65
+ * right at `maxLines` is complete, not cut short. */
66
+ function capToLines(text, maxLines) {
67
+ if (maxLines === void 0) return {
68
+ text,
69
+ tooLarge: false,
70
+ truncated: false
71
+ };
72
+ const capped = capLines(text, maxLines);
73
+ return {
74
+ text: capped,
75
+ tooLarge: false,
76
+ truncated: capped.length < text.length
77
+ };
78
+ }
79
+ /** What a read yields when the byte cap trips mid-stream. Complete lines already read
80
+ * are a usable preview, so the cap cuts them short rather than refusing; only a body
81
+ * with no line break leaves nothing to show. */
82
+ function capAtBytes(text, lines, maxLines) {
83
+ if (maxLines === void 0 || lines === 0) return {
84
+ text: "",
85
+ tooLarge: true,
86
+ truncated: false
87
+ };
88
+ return {
89
+ text: capLines(text, lines),
90
+ tooLarge: false,
91
+ truncated: true
92
+ };
93
+ }
94
+ /** Read a Response as text capped at `maxBytes`; when `maxLines` is given, cancel the stream
95
+ * once that many `\n` arrive. Cutting on `\n` keeps the returned prefix on a clean UTF-8 boundary. */
96
+ async function readCappedText(response, maxBytes, maxLines) {
97
+ const probeLines = maxLines === void 0 ? void 0 : maxLines + 1;
98
+ const reader = response.body?.getReader();
99
+ if (!reader) {
100
+ const { bytes, tooLarge } = await readCappedBytes(response, maxBytes);
101
+ if (tooLarge) return {
102
+ text: "",
103
+ tooLarge: true,
104
+ truncated: false
105
+ };
106
+ return capToLines(new TextDecoder().decode(bytes), maxLines);
107
+ }
108
+ const decoder = new TextDecoder();
109
+ let text = "";
110
+ let lines = 0;
111
+ let total = 0;
112
+ for (let chunk = await reader.read(); !chunk.done; chunk = await reader.read()) {
113
+ total += chunk.value.byteLength;
114
+ if (total > maxBytes) {
115
+ await reader.cancel();
116
+ return capAtBytes(text, lines, maxLines);
117
+ }
118
+ const piece = decoder.decode(chunk.value, { stream: true });
119
+ text += piece;
120
+ if (probeLines !== void 0) {
121
+ lines += countNewlines(piece);
122
+ if (lines >= probeLines) {
123
+ await reader.cancel();
124
+ return capToLines(text, maxLines);
125
+ }
126
+ }
127
+ }
128
+ text += decoder.decode();
129
+ return capToLines(text, maxLines);
130
+ }
131
+ /** Keep the last `maxLines` lines, ignoring a single trailing newline so a file
132
+ * ending in one doesn't spend a line on the empty remainder. */
133
+ function lastLines(text, maxLines) {
134
+ const end = text.endsWith("\n") ? text.length - 1 : text.length;
135
+ let count = 0;
136
+ for (let i = end - 1; i >= 0; i--) if (text[i] === "\n") {
137
+ count++;
138
+ if (count === maxLines) return {
139
+ text: text.slice(i + 1),
140
+ truncated: true
141
+ };
142
+ }
143
+ return {
144
+ text,
145
+ truncated: false
146
+ };
147
+ }
148
+ /** Range header for reading the last `maxBytes` of an object. A closed range is a
149
+ * CORS-safelisted header value, so it travels without a preflight that a bucket's
150
+ * CORS rules may not answer; the suffix form is not safelisted, and is only reached
151
+ * for workspace files, which are same-origin and list no size. Closing the range at
152
+ * the listed size also bounds the window: left open, an object that grew since the
153
+ * listing would serve more than `maxBytes` and the read would refuse it. */
154
+ function tailRangeFor(knownSize, maxBytes) {
155
+ if (typeof knownSize !== "number") return `bytes=-${maxBytes}`;
156
+ const start = knownSize - maxBytes;
157
+ return start > 0 ? `bytes=${start}-${knownSize - 1}` : null;
158
+ }
159
+ /** What a ranged response actually delivered: whether its first line arrives cut in
160
+ * half, or `null` when the body can't be the object's end at all. */
161
+ function tailWindow(response, maxBytes) {
162
+ if (response.status === 206) {
163
+ const range = response.headers.get("content-range");
164
+ return { startsMidObject: range === null || !range.startsWith("bytes 0-") };
165
+ }
166
+ const length = response.headers.get("content-length");
167
+ if (length === null) return null;
168
+ const size = Number(length);
169
+ return Number.isFinite(size) && size <= maxBytes ? { startsMidObject: false } : null;
170
+ }
171
+ /** Read a range covering the end of an object as text. `startsMidObject` says the
172
+ * range began at an arbitrary byte, so the first line arrives cut in half. */
173
+ async function readTailText(response, maxBytes, maxLines, startsMidObject) {
174
+ const { bytes, tooLarge } = await readCappedBytes(response, maxBytes);
175
+ if (tooLarge) return {
176
+ text: "",
177
+ tooLarge: true,
178
+ truncated: false
179
+ };
180
+ let text = new TextDecoder().decode(bytes);
181
+ if (startsMidObject) {
182
+ const firstBreak = text.indexOf("\n");
183
+ if (firstBreak !== -1) text = text.slice(firstBreak + 1);
184
+ }
185
+ const limited = maxLines === void 0 ? {
186
+ text,
187
+ truncated: false
188
+ } : lastLines(text, maxLines);
189
+ return {
190
+ text: limited.text,
191
+ tooLarge: false,
192
+ truncated: startsMidObject || limited.truncated
193
+ };
194
+ }
195
+ //#endregion
196
+ export { countLines, lastLines, readCappedBytes, readCappedText, readTailText, tailRangeFor, tailWindow };
@@ -0,0 +1,95 @@
1
+ import { CorsError } from "../lib/errors.js";
2
+ import { fetchPreviewObject, loadPreviewContent, peekPreviewContent } from "./previewCache.js";
3
+ import { readCappedBytes } from "./readCappedBytes.js";
4
+ import { useEffect, useState } from "react";
5
+ //#region src/file-explorer/FilePreview/useObjectBlobUrl.ts
6
+ function toState(result) {
7
+ return {
8
+ blobUrl: result.blobUrl,
9
+ loading: false,
10
+ error: null,
11
+ tooLarge: result.tooLarge,
12
+ corsError: false
13
+ };
14
+ }
15
+ function disposeBlob(result) {
16
+ if (result.blobUrl) URL.revokeObjectURL(result.blobUrl);
17
+ }
18
+ /** Fetch an object into a blob URL with a forced MIME type so e.g. PDFs render inline even when served as octet-stream; cached per `cacheKey` so repeated mounts share one download and one blob URL, revoked when the cache drops it. */
19
+ function useObjectBlobUrl(url, { enabled, maxBytes, knownSize, mimeType, cacheKey }) {
20
+ const [state, setState] = useState({
21
+ blobUrl: null,
22
+ loading: enabled,
23
+ error: null,
24
+ tooLarge: false,
25
+ corsError: false
26
+ });
27
+ const tooLarge = typeof knownSize === "number" && knownSize > maxBytes;
28
+ const key = enabled ? cacheKey : null;
29
+ useEffect(() => {
30
+ if (!enabled || !url || !key) return;
31
+ if (tooLarge) {
32
+ setState({
33
+ blobUrl: null,
34
+ loading: false,
35
+ error: null,
36
+ tooLarge: true,
37
+ corsError: false
38
+ });
39
+ return;
40
+ }
41
+ let cancelled = false;
42
+ const record = loadPreviewContent(key, async (signal) => {
43
+ const response = await fetchPreviewObject(key, url, signal);
44
+ const { bytes, tooLarge: exceeded } = await readCappedBytes(response, maxBytes);
45
+ if (exceeded) return {
46
+ blobUrl: null,
47
+ tooLarge: true
48
+ };
49
+ return {
50
+ blobUrl: URL.createObjectURL(new Blob([bytes], { type: mimeType })),
51
+ tooLarge: false
52
+ };
53
+ }, disposeBlob);
54
+ setState(record.value ? toState(record.value) : {
55
+ blobUrl: null,
56
+ loading: true,
57
+ error: null,
58
+ tooLarge: false,
59
+ corsError: false
60
+ });
61
+ record.promise.then((result) => {
62
+ if (!cancelled) setState(toState(result));
63
+ }).catch((err) => {
64
+ if (err instanceof DOMException && err.name === "AbortError") return;
65
+ if (!cancelled) setState({
66
+ blobUrl: null,
67
+ loading: false,
68
+ error: err instanceof Error ? err.message : "Failed to load file",
69
+ tooLarge: false,
70
+ corsError: err instanceof CorsError
71
+ });
72
+ });
73
+ return () => {
74
+ cancelled = true;
75
+ };
76
+ }, [
77
+ url,
78
+ enabled,
79
+ key,
80
+ maxBytes,
81
+ mimeType,
82
+ tooLarge
83
+ ]);
84
+ if (tooLarge) return {
85
+ blobUrl: null,
86
+ loading: false,
87
+ error: null,
88
+ tooLarge: true,
89
+ corsError: false
90
+ };
91
+ const cached = key ? peekPreviewContent(key) : void 0;
92
+ return cached ? toState(cached) : state;
93
+ }
94
+ //#endregion
95
+ export { useObjectBlobUrl };
@@ -0,0 +1,109 @@
1
+ import { CorsError } from "../lib/errors.js";
2
+ import { fetchPreviewObject, loadPreviewContent, peekPreviewContent } from "./previewCache.js";
3
+ import { readCappedText, readTailText, tailRangeFor, tailWindow } from "./readCappedBytes.js";
4
+ import { useEffect, useState } from "react";
5
+ //#region src/file-explorer/FilePreview/useObjectText.ts
6
+ var IDLE = {
7
+ text: null,
8
+ loading: false,
9
+ error: null,
10
+ tooLarge: false,
11
+ truncated: false,
12
+ corsError: false
13
+ };
14
+ function toState(result) {
15
+ return {
16
+ ...IDLE,
17
+ ...result
18
+ };
19
+ }
20
+ async function readObjectText(signal, { url, cacheKey, maxBytes, maxLines, knownSize, tail }) {
21
+ const range = tail ? tailRangeFor(knownSize, maxBytes) : null;
22
+ const response = await fetchPreviewObject(cacheKey, url, signal, range ? { Range: range } : void 0);
23
+ let startsMidObject = false;
24
+ if (range) {
25
+ const window = tailWindow(response, maxBytes);
26
+ if (!window) throw new Error("range request was not honored");
27
+ startsMidObject = window.startsMidObject;
28
+ }
29
+ const result = tail ? await readTailText(response, maxBytes, maxLines, startsMidObject) : await readCappedText(response, maxBytes, maxLines);
30
+ return {
31
+ text: result.tooLarge ? null : result.text,
32
+ tooLarge: result.tooLarge,
33
+ truncated: result.truncated
34
+ };
35
+ }
36
+ /** Fetch a text object from a presigned URL, cached per `cacheKey` so repeated mounts
37
+ * download it once. `maxLines` stops the read early and `maxBytes` cuts short what it
38
+ * hasn't. `tail` reads the end of the object instead of the start, and caches
39
+ * separately because the same object yields different text. */
40
+ function useObjectText(url, { enabled, maxBytes, maxLines, knownSize, cacheKey, tail = false }) {
41
+ const tooLarge = maxLines === void 0 && !tail && typeof knownSize === "number" && knownSize > maxBytes;
42
+ const key = enabled ? tail ? `${cacheKey}#tail` : cacheKey : null;
43
+ const [state, setState] = useState({
44
+ ...IDLE,
45
+ loading: enabled
46
+ });
47
+ const [stateKey, setStateKey] = useState(key);
48
+ if (stateKey !== key) {
49
+ setStateKey(key);
50
+ setState({
51
+ ...IDLE,
52
+ loading: key !== null
53
+ });
54
+ }
55
+ useEffect(() => {
56
+ if (!enabled || !url || !key) return;
57
+ if (tooLarge) {
58
+ setState({
59
+ ...IDLE,
60
+ tooLarge: true
61
+ });
62
+ return;
63
+ }
64
+ let cancelled = false;
65
+ const record = loadPreviewContent(key, (signal) => readObjectText(signal, {
66
+ url,
67
+ cacheKey,
68
+ maxBytes,
69
+ maxLines,
70
+ knownSize,
71
+ tail
72
+ }));
73
+ setState(record.value ? toState(record.value) : {
74
+ ...IDLE,
75
+ loading: true
76
+ });
77
+ record.promise.then((result) => {
78
+ if (!cancelled) setState(toState(result));
79
+ }).catch((err) => {
80
+ if (err instanceof DOMException && err.name === "AbortError") return;
81
+ if (!cancelled) setState({
82
+ ...IDLE,
83
+ error: err instanceof Error ? err.message : "Failed to load file",
84
+ corsError: err instanceof CorsError
85
+ });
86
+ });
87
+ return () => {
88
+ cancelled = true;
89
+ };
90
+ }, [
91
+ url,
92
+ enabled,
93
+ key,
94
+ maxBytes,
95
+ maxLines,
96
+ tooLarge,
97
+ tail,
98
+ cacheKey,
99
+ knownSize
100
+ ]);
101
+ if (tooLarge) return {
102
+ ...IDLE,
103
+ tooLarge: true
104
+ };
105
+ const cached = key ? peekPreviewContent(key) : void 0;
106
+ return cached ? toState(cached) : state;
107
+ }
108
+ //#endregion
109
+ export { useObjectText };
@@ -0,0 +1,73 @@
1
+ import { loadPreviewUrl, peekPreviewUrl } from "./previewCache.js";
2
+ import { useEffect, useRef, useState } from "react";
3
+ //#region src/file-explorer/FilePreview/usePresignedUrl.ts
4
+ function toState([url, error], linkErrorMessage) {
5
+ if (error || !url) return {
6
+ url: null,
7
+ loading: false,
8
+ error: error?.message ?? linkErrorMessage
9
+ };
10
+ return {
11
+ url,
12
+ loading: false,
13
+ error: null
14
+ };
15
+ }
16
+ /** Fetch a presigned URL per file, cached so repeated mounts share one request; callbacks come from refs so the effect keys only on file identity. */
17
+ function usePresignedUrl(node, getPresignedUrl, { enabled, linkErrorMessage }) {
18
+ const path = enabled ? node?.path ?? null : null;
19
+ const [state, setState] = useState({
20
+ url: null,
21
+ loading: false,
22
+ error: null
23
+ });
24
+ const [statePath, setStatePath] = useState(path);
25
+ if (statePath !== path) {
26
+ setStatePath(path);
27
+ setState({
28
+ url: null,
29
+ loading: path !== null,
30
+ error: null
31
+ });
32
+ }
33
+ const getUrlRef = useRef(getPresignedUrl);
34
+ getUrlRef.current = getPresignedUrl;
35
+ const linkErrorRef = useRef(linkErrorMessage);
36
+ linkErrorRef.current = linkErrorMessage;
37
+ const nodeRef = useRef(node);
38
+ nodeRef.current = node;
39
+ useEffect(() => {
40
+ const node = nodeRef.current;
41
+ if (!path || !node) {
42
+ setState({
43
+ url: null,
44
+ loading: false,
45
+ error: null
46
+ });
47
+ return;
48
+ }
49
+ let cancelled = false;
50
+ const record = loadPreviewUrl(path, () => getUrlRef.current(node));
51
+ setState(record.value ? toState(record.value, linkErrorRef.current) : {
52
+ url: null,
53
+ loading: true,
54
+ error: null
55
+ });
56
+ record.promise.then((result) => {
57
+ if (!cancelled) setState(toState(result, linkErrorRef.current));
58
+ }).catch((err) => {
59
+ if (!cancelled) setState({
60
+ url: null,
61
+ loading: false,
62
+ error: err instanceof Error ? err.message : linkErrorRef.current
63
+ });
64
+ });
65
+ return () => {
66
+ cancelled = true;
67
+ };
68
+ }, [path]);
69
+ const cached = path ? peekPreviewUrl(path) : void 0;
70
+ return cached ? toState(cached, linkErrorRef.current) : state;
71
+ }
72
+ //#endregion
73
+ export { usePresignedUrl };
@@ -1,5 +1,5 @@
1
- import { type OpenMenu, type RowMenuItem } from '../list/index.js';
2
1
  import type { ReactNode } from 'react';
2
+ import { type OpenMenu, type RowMenuItem } from '../list/index.js';
3
3
  import type { TreeNode } from './lib/types.js';
4
4
  export declare const ROW_HEIGHT_SHORT = 45;
5
5
  export declare const ROW_HEIGHT_TALL = 52.25;
@@ -0,0 +1,165 @@
1
+ import { useStrings } from "../components/Provider.js";
2
+ import { EyeIcon } from "../icons.js";
3
+ import { TOOLTIP_ID } from "../components/Tooltip.js";
4
+ import { MoreButton, useRowMenuActive } from "../list/RowContextMenu.js";
5
+ import { Table } from "../components/Table.js";
6
+ import { RowSelectCheckbox } from "../list/ListTable.js";
7
+ import { formatFileSize } from "./lib/utils.js";
8
+ import cx from "classnames";
9
+ import { jsx, jsxs } from "react/jsx-runtime";
10
+ import { DateTime } from "luxon";
11
+ var ROW_HEIGHT_TALL = 52.25;
12
+ function isTwoLineRow(node) {
13
+ return node.type !== "directory" && !!node.contentType;
14
+ }
15
+ /** Pure function of the node, so the virtualizer sizes rows without measuring. */
16
+ function fileRowHeight(node) {
17
+ return isTwoLineRow(node) ? ROW_HEIGHT_TALL : 45;
18
+ }
19
+ function isInteractiveTarget(e) {
20
+ return !!e.target.closest("a,button,input,label");
21
+ }
22
+ function lazyContextMenuProps(openMenu, getItems) {
23
+ return { onContextMenu: (e) => {
24
+ const items = getItems();
25
+ e.preventDefault();
26
+ if (items.length > 0) e.stopPropagation();
27
+ openMenu(e.clientX, e.clientY, items);
28
+ } };
29
+ }
30
+ function UserRow({ node, icon, storageCount, getItems, openMenu, onOpen }) {
31
+ const t = useStrings().fileExplorer;
32
+ const { active: menuActive, openMenu: openRowMenu } = useRowMenuActive(openMenu);
33
+ const handleRowClick = (e) => {
34
+ if (isInteractiveTarget(e)) return;
35
+ onOpen(node);
36
+ };
37
+ return /* @__PURE__ */ jsxs("tr", {
38
+ className: cx("group cursor-pointer", menuActive ? "theme-hover" : "hover:theme-hover"),
39
+ onClick: handleRowClick,
40
+ ...lazyContextMenuProps(openRowMenu, getItems),
41
+ children: [
42
+ /* @__PURE__ */ jsx(Table.Item, {
43
+ className: "py-2.5",
44
+ children: /* @__PURE__ */ jsxs("div", {
45
+ className: "flex min-w-0 items-center gap-3",
46
+ children: [/* @__PURE__ */ jsx("span", {
47
+ className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-full theme-muted-panel *:m-0",
48
+ children: icon
49
+ }), /* @__PURE__ */ jsx("span", {
50
+ className: "truncate text-[13px] font-medium text-(--theme-app)",
51
+ children: node.displayName || node.name
52
+ })]
53
+ })
54
+ }),
55
+ /* @__PURE__ */ jsx(Table.Item, {
56
+ className: "py-2.5 text-xs uppercase tracking-wider text-(--theme-muted-text-color)",
57
+ children: t.preview.userType
58
+ }),
59
+ /* @__PURE__ */ jsx(Table.Item, {
60
+ className: "py-2.5 tabular-nums text-(--theme-muted-text-color)",
61
+ children: storageCount
62
+ }),
63
+ /* @__PURE__ */ jsx(Table.Item, {
64
+ className: "relative py-2.5",
65
+ children: /* @__PURE__ */ jsx("div", {
66
+ className: "absolute inset-y-0 right-0 flex items-center pr-4",
67
+ children: /* @__PURE__ */ jsx(MoreButton, {
68
+ active: menuActive,
69
+ onOpen: (x, y) => openRowMenu(x, y, getItems())
70
+ })
71
+ })
72
+ })
73
+ ]
74
+ });
75
+ }
76
+ function FileRow({ node, icon, rowId, rowIndex, active, checked, canPreview, getItems, openMenu, onOpen, onToggleCheck, onPreview }) {
77
+ const t = useStrings().fileExplorer;
78
+ const { active: menuActive, openMenu: openRowMenu } = useRowMenuActive(openMenu);
79
+ const isDir = node.type === "directory";
80
+ const modifiedDt = node.modified ? DateTime.fromISO(node.modified) : null;
81
+ const typeLabel = node.root ? node.storageType ?? node.type : node.type;
82
+ const handleRowClick = (e) => {
83
+ if (isInteractiveTarget(e)) return;
84
+ onOpen(node);
85
+ };
86
+ return /* @__PURE__ */ jsxs("tr", {
87
+ id: rowId,
88
+ "aria-rowindex": rowIndex,
89
+ "aria-selected": checked,
90
+ className: cx("group", checked ? "bg-blue-500/5" : menuActive ? "theme-hover" : "hover:theme-hover", active && "group-focus-visible/grid:ring-1 group-focus-visible/grid:ring-inset group-focus-visible/grid:ring-(--theme-link)", "cursor-pointer"),
91
+ onClick: handleRowClick,
92
+ style: { height: fileRowHeight(node) },
93
+ ...lazyContextMenuProps(openRowMenu, getItems),
94
+ children: [
95
+ /* @__PURE__ */ jsx(Table.Item, {
96
+ className: "py-2.5",
97
+ children: /* @__PURE__ */ jsxs("div", {
98
+ className: "flex min-w-0 items-center gap-2.5",
99
+ children: [
100
+ /* @__PURE__ */ jsx(RowSelectCheckbox, {
101
+ checked,
102
+ label: t.preview.select(node.name),
103
+ onToggle: () => onToggleCheck(node, !checked)
104
+ }),
105
+ /* @__PURE__ */ jsx("span", {
106
+ className: "flex h-5 w-5 shrink-0 items-center justify-center *:m-0",
107
+ children: icon
108
+ }),
109
+ /* @__PURE__ */ jsxs("div", {
110
+ className: "min-w-0",
111
+ children: [/* @__PURE__ */ jsx("div", {
112
+ className: "truncate text-[13px] font-medium leading-tight text-(--theme-app)",
113
+ children: node.displayName || node.name
114
+ }), !isDir && node.contentType && /* @__PURE__ */ jsx("div", {
115
+ className: "truncate text-xs leading-tight text-(--theme-muted-text-color)",
116
+ children: node.contentType
117
+ })]
118
+ })
119
+ ]
120
+ })
121
+ }),
122
+ /* @__PURE__ */ jsx(Table.Item, {
123
+ className: "truncate py-2.5 text-sm tabular-nums text-(--theme-muted-text-color)",
124
+ children: typeof node.size === "number" ? formatFileSize(node.size) : "—"
125
+ }),
126
+ /* @__PURE__ */ jsx(Table.Item, {
127
+ className: "truncate py-2.5 text-xs uppercase tracking-wider text-(--theme-muted-text-color)",
128
+ children: typeLabel
129
+ }),
130
+ /* @__PURE__ */ jsx(Table.Item, {
131
+ className: "truncate py-2.5 text-sm text-(--theme-muted-text-color)",
132
+ children: /* @__PURE__ */ jsx("span", {
133
+ "data-tooltip-id": TOOLTIP_ID,
134
+ "data-tooltip-content": node.modified ?? "",
135
+ "data-tooltip-place": "top",
136
+ children: !isDir && modifiedDt?.isValid ? modifiedDt.toRelative() : "—"
137
+ })
138
+ }),
139
+ /* @__PURE__ */ jsx(Table.Item, {
140
+ className: "relative py-2.5",
141
+ children: /* @__PURE__ */ jsxs("div", {
142
+ className: "absolute inset-y-0 right-0 flex items-center gap-0.5 pr-4",
143
+ children: [!isDir && canPreview && /* @__PURE__ */ jsx("button", {
144
+ type: "button",
145
+ "aria-label": t.preview.preview,
146
+ "data-tooltip-id": "global-tooltip",
147
+ "data-tooltip-content": t.preview.preview,
148
+ "data-tooltip-place": "bottom",
149
+ onClick: (e) => {
150
+ e.stopPropagation();
151
+ onPreview(node);
152
+ },
153
+ className: cx("rounded p-1 text-(--theme-muted-text-color) transition-colors hover:bg-(--theme-muted-panel-bg) hover:text-(--theme-app) [&>svg]:h-4 [&>svg]:w-4", menuActive ? "opacity-100" : "opacity-0 group-hover:opacity-100 focus-visible:opacity-100"),
154
+ children: /* @__PURE__ */ jsx(EyeIcon, {})
155
+ }), /* @__PURE__ */ jsx(MoreButton, {
156
+ active: menuActive,
157
+ onOpen: (x, y) => openRowMenu(x, y, getItems())
158
+ })]
159
+ })
160
+ })
161
+ ]
162
+ });
163
+ }
164
+ //#endregion
165
+ export { FileRow, ROW_HEIGHT_TALL, UserRow, fileRowHeight, isTwoLineRow };