@jini-ai/ui 0.5.5 → 0.6.10

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 (263) hide show
  1. package/CHANGELOG.md +91 -2
  2. package/dist/core/theme/validation.js +40 -38
  3. package/dist/features/a2ui/renderer.js +15 -13
  4. package/dist/features/about/react/components/AboutTab.js +64 -34
  5. package/dist/features/about/rules.d.ts +2 -2
  6. package/dist/features/about/rules.js +156 -129
  7. package/dist/features/admin-widgets/components/ImagePreviewModal.d.ts +4 -3
  8. package/dist/features/admin-widgets/components/ImagePreviewModal.js +6 -10
  9. package/dist/features/admin-widgets/components/InfoTip.d.ts +3 -2
  10. package/dist/features/admin-widgets/components/InfoTip.hooks.js +2 -0
  11. package/dist/features/admin-widgets/components/InfoTip.js +34 -7
  12. package/dist/features/admin-widgets/components/InfoTip.rules.d.ts +13 -0
  13. package/dist/features/admin-widgets/components/InfoTip.rules.js +43 -10
  14. package/dist/features/admin-widgets/components/Lightbox/Lightbox.d.ts +30 -0
  15. package/dist/features/admin-widgets/components/Lightbox/Lightbox.hooks.d.ts +40 -0
  16. package/dist/features/admin-widgets/components/Lightbox/Lightbox.hooks.js +128 -0
  17. package/dist/features/admin-widgets/components/Lightbox/Lightbox.js +18 -0
  18. package/dist/features/admin-widgets/components/MiddleTruncate.d.ts +14 -0
  19. package/dist/features/admin-widgets/components/MiddleTruncate.js +12 -0
  20. package/dist/features/admin-widgets/components/MiddleTruncate.rules.d.ts +20 -0
  21. package/dist/features/admin-widgets/components/MiddleTruncate.rules.js +21 -0
  22. package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.d.ts +18 -0
  23. package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.hooks.d.ts +16 -0
  24. package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.hooks.js +45 -0
  25. package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.js +18 -0
  26. package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.rules.d.ts +32 -0
  27. package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.rules.js +26 -0
  28. package/dist/features/admin-widgets/components/SuggestInput/SuggestInput.hooks.js +29 -14
  29. package/dist/features/admin-widgets/index.d.ts +14 -2
  30. package/dist/features/admin-widgets/index.js +7 -1
  31. package/dist/features/admin-widgets/styles/admin-widgets.css +1 -0
  32. package/dist/features/asset-grid/react/components/AssetCard.js +7 -1
  33. package/dist/features/asset-grid/react/components/AssetGrid.d.ts +1 -1
  34. package/dist/features/asset-grid/react/components/AssetGrid.js +38 -18
  35. package/dist/features/asset-grid/react/components/useAssetGridFilters.d.ts +21 -0
  36. package/dist/features/asset-grid/react/components/useAssetGridFilters.js +19 -0
  37. package/dist/features/asset-grid/react/hooks/useAssetGridKeyboardShortcuts.js +16 -11
  38. package/dist/features/asset-grid/react/hooks/useAssetGridLiveUpdates.d.ts +2 -0
  39. package/dist/features/asset-grid/react/hooks/useAssetGridLiveUpdates.js +35 -11
  40. package/dist/features/asset-tree-browser/react/components/AssetTreeBrowser.d.ts +6 -6
  41. package/dist/features/asset-tree-browser/react/components/AssetTreeBrowser.js +10 -129
  42. package/dist/features/asset-tree-browser/react/components/AssetTreeBrowserParts.d.ts +50 -0
  43. package/dist/features/asset-tree-browser/react/components/AssetTreeBrowserParts.js +95 -0
  44. package/dist/features/asset-tree-browser/react/components/useAssetTreeBrowserController.d.ts +56 -0
  45. package/dist/features/asset-tree-browser/react/components/useAssetTreeBrowserController.js +80 -0
  46. package/dist/features/asset-tree-browser/rules.js +40 -22
  47. package/dist/features/browser-chrome/rules.js +74 -70
  48. package/dist/features/charts/browser.js +11 -5
  49. package/dist/features/charts/react.js +2 -2
  50. package/dist/features/charts/table.js +2 -2
  51. package/dist/features/command-palette/react/hooks/useCommandPalette.js +3 -9
  52. package/dist/features/connectors/ConnectorsBrowser.d.ts +4 -4
  53. package/dist/features/connectors/ConnectorsBrowser.js +26 -14
  54. package/dist/features/connectors/components/ConnectorCard.js +67 -38
  55. package/dist/features/connectors/components/ConnectorDetailDrawer.js +62 -31
  56. package/dist/features/connectors/components/ConnectorGrid.js +11 -6
  57. package/dist/features/connectors/hooks/useConnectorAuthorization.js +6 -0
  58. package/dist/features/connectors/hooks/useConnectorCatalog.d.ts +2 -0
  59. package/dist/features/connectors/hooks/useConnectorCatalog.js +32 -13
  60. package/dist/features/connectors/hooks/useWiredConnectorsBrowser.d.ts +3 -3
  61. package/dist/features/connectors/rules.d.ts +8 -1
  62. package/dist/features/connectors/rules.js +42 -20
  63. package/dist/features/connectors/types.d.ts +11 -0
  64. package/dist/features/execution/constants.d.ts +4 -10
  65. package/dist/features/execution/constants.js +9 -94
  66. package/dist/features/execution/react/components/ByokProviderForm.d.ts +2 -4
  67. package/dist/features/execution/react/components/ByokProviderForm.js +140 -43
  68. package/dist/features/execution/react/components/ExecutionTab.d.ts +1 -2
  69. package/dist/features/execution/react/components/ExecutionTab.js +52 -30
  70. package/dist/features/execution/react/components/LocalCliAgentCard.js +12 -3
  71. package/dist/features/execution/react/components/LocalCliAgentList.js +14 -3
  72. package/dist/features/execution/react/components/LocalCliAgentSettings.js +14 -3
  73. package/dist/features/execution/react/hooks/useLocalCliAgentCard.js +54 -36
  74. package/dist/features/execution/react/hooks/useReasoningControl.js +114 -78
  75. package/dist/features/execution/rules.d.ts +2 -2
  76. package/dist/features/execution/rules.js +31 -15
  77. package/dist/features/execution/types.d.ts +4 -42
  78. package/dist/features/execution/types.js +0 -17
  79. package/dist/features/external-mcp/react/components/ExternalMcpTab.d.ts +6 -2
  80. package/dist/features/external-mcp/react/components/ExternalMcpTab.js +9 -2
  81. package/dist/features/file-dropzone/react/components/FileDropzone.js +24 -25
  82. package/dist/features/file-dropzone/react/components/FileDropzoneLightbox.d.ts +2 -2
  83. package/dist/features/file-dropzone/react/components/FileDropzoneLightbox.js +35 -30
  84. package/dist/features/file-dropzone/react/components/FileDropzoneThumbnailGrid.js +30 -24
  85. package/dist/features/file-dropzone/react/components/FileDropzoneZone.d.ts +14 -0
  86. package/dist/features/file-dropzone/react/components/FileDropzoneZone.js +26 -0
  87. package/dist/features/html-editor/canvas-content-wrapper.js +13 -13
  88. package/dist/features/html-editor/canvas-embed-placeholders.js +46 -37
  89. package/dist/features/html-editor/css.js +76 -69
  90. package/dist/features/html-editor/editor-blocks.js +47 -33
  91. package/dist/features/html-editor/editor-serialization.js +6 -5
  92. package/dist/features/html-editor/protected-elements.js +39 -28
  93. package/dist/features/html-editor/react/hooks/useInteractiveHtmlEditor.js +4 -4
  94. package/dist/features/html-editor/source-splice.js +51 -30
  95. package/dist/features/i18n/dictionaries/settings-dialog.ja.js +1 -0
  96. package/dist/features/i18n/dictionaries/settings-dialog.zh-CN.js +1 -0
  97. package/dist/features/i18n/dictionaries/settings-dialog.zh-TW.js +1 -0
  98. package/dist/features/i18n/locale.js +20 -16
  99. package/dist/features/iframe-pool/react/dom-sync.js +8 -6
  100. package/dist/features/integrations/react/components/CodexInstallToggleButton.js +19 -4
  101. package/dist/features/integrations/react/components/IntegrationsTab.js +60 -14
  102. package/dist/features/interactive-ui/interactive-ui.css +1 -1
  103. package/dist/features/interactive-ui/providers/shadcn/data-table.js +10 -7
  104. package/dist/features/lexical-rich-text-editor/mention-parser.js +60 -57
  105. package/dist/features/lexical-rich-text-editor/react/hooks/useKeyboardCommands.js +33 -38
  106. package/dist/features/lexical-rich-text-editor/rules.js +87 -74
  107. package/dist/features/lexical-rich-text-editor/serialize.js +27 -25
  108. package/dist/features/mcp-ui/secret-card.js +111 -61
  109. package/dist/features/mcp-ui/surfaces/confirmation.js +39 -21
  110. package/dist/features/mcp-ui/surfaces/document.d.ts +1 -1
  111. package/dist/features/mcp-ui/surfaces/document.js +10 -2
  112. package/dist/features/mcp-ui/surfaces/fields.js +30 -50
  113. package/dist/features/mcp-ui/surfaces/form.d.ts +6 -0
  114. package/dist/features/mcp-ui/surfaces/form.js +43 -21
  115. package/dist/features/mcp-ui/surfaces/outcome.js +30 -20
  116. package/dist/features/mcp-ui/surfaces/text-input.js +33 -21
  117. package/dist/features/mcp-ui/surfaces/tokens.d.ts +6 -1
  118. package/dist/features/mcp-ui/surfaces/tokens.js +5 -2
  119. package/dist/features/media-providers/constants.d.ts +1 -0
  120. package/dist/features/media-providers/constants.js +2 -81
  121. package/dist/features/media-providers/index.d.ts +1 -1
  122. package/dist/features/media-providers/ports.d.ts +2 -0
  123. package/dist/features/media-providers/react/components/MediaProviderCard.d.ts +22 -0
  124. package/dist/features/media-providers/react/components/MediaProviderCard.js +50 -0
  125. package/dist/features/media-providers/react/components/MediaProvidersTab.d.ts +5 -6
  126. package/dist/features/media-providers/react/components/MediaProvidersTab.js +40 -72
  127. package/dist/features/media-providers/react/components/media-providers-tab-labels.d.ts +11 -0
  128. package/dist/features/media-providers/react/components/media-providers-tab-labels.js +57 -0
  129. package/dist/features/media-providers/react/hooks/useMediaProvidersTab.d.ts +4 -2
  130. package/dist/features/media-providers/react/hooks/useMediaProvidersTab.js +37 -23
  131. package/dist/features/media-providers/rules.js +17 -10
  132. package/dist/features/media-providers/types.d.ts +4 -22
  133. package/dist/features/media-providers/types.js +3 -8
  134. package/dist/features/memory/formatters.js +127 -92
  135. package/dist/features/memory/react/components/MemoryConnectedPanel.js +92 -36
  136. package/dist/features/memory/react/components/MemoryList.js +37 -18
  137. package/dist/features/memory/react/hooks/useMemoryExtractions.store.js +42 -36
  138. package/dist/features/notifications/react/components/NotificationsTab.js +28 -28
  139. package/dist/features/observability/long-task.d.ts +0 -12
  140. package/dist/features/observability/long-task.js +15 -19
  141. package/dist/features/observability/resource-error.d.ts +0 -10
  142. package/dist/features/observability/resource-error.js +18 -13
  143. package/dist/features/observability/stuck-run.js +10 -6
  144. package/dist/features/observability/telemetry-url.d.ts +6 -0
  145. package/dist/features/observability/telemetry-url.js +16 -0
  146. package/dist/features/observability/white-screen.js +12 -24
  147. package/dist/features/panel-kit/fetch-query/adapter.react.d.ts +2 -2
  148. package/dist/features/panel-kit/fetch-query/adapter.react.js +4 -2
  149. package/dist/features/panel-kit/fetch-query/adapter.tanstack.js +46 -33
  150. package/dist/features/panel-kit/helpers/standing-draft-local-backup.js +3 -1
  151. package/dist/features/panel-kit/hooks/use-settings-slice.hooks.js +3 -1
  152. package/dist/features/privacy/react/components/PrivacyTab.js +24 -13
  153. package/dist/features/progress-card/components/ProgressCard.js +7 -3
  154. package/dist/features/progress-card/reference-adapters.js +59 -54
  155. package/dist/features/project-locations/react/components/ProjectLocationsTab.js +24 -15
  156. package/dist/features/project-locations/react/hooks/useProjectLocationsTab.d.ts +8 -0
  157. package/dist/features/project-locations/react/hooks/useProjectLocationsTab.js +31 -23
  158. package/dist/features/resource-dashboard/react/components/ResourceBoard.js +6 -47
  159. package/dist/features/resource-dashboard/react/components/ResourceBoardView.d.ts +2 -2
  160. package/dist/features/resource-dashboard/react/components/ResourceBoardView.js +3 -6
  161. package/dist/features/resource-dashboard/react/components/ResourceBoardViewParts.d.ts +15 -0
  162. package/dist/features/resource-dashboard/react/components/ResourceBoardViewParts.js +35 -0
  163. package/dist/features/resource-dashboard/react/components/ResourceCard.js +5 -16
  164. package/dist/features/resource-dashboard/react/components/ResourceCardParts.d.ts +34 -0
  165. package/dist/features/resource-dashboard/react/components/ResourceCardParts.js +34 -0
  166. package/dist/features/resource-dashboard/react/components/ResourceRowList.js +3 -1
  167. package/dist/features/resource-dashboard/react/components/ResourceRowListItem.js +2 -2
  168. package/dist/features/resource-dashboard/react/components/ResourceRowListItemParts.d.ts +19 -0
  169. package/dist/features/resource-dashboard/react/components/ResourceRowListItemParts.js +11 -0
  170. package/dist/features/resource-dashboard/react/components/ResourceRowListView.d.ts +1 -1
  171. package/dist/features/resource-dashboard/react/components/ResourceRowListView.js +4 -4
  172. package/dist/features/resource-dashboard/react/components/ResourceRowListViewParts.d.ts +10 -0
  173. package/dist/features/resource-dashboard/react/components/ResourceRowListViewParts.js +20 -0
  174. package/dist/features/resource-dashboard/react/components/resourceBoardProps.d.ts +48 -0
  175. package/dist/features/resource-dashboard/react/components/resourceBoardProps.js +70 -0
  176. package/dist/features/resource-dashboard/react/components/resourceRowListProps.d.ts +19 -0
  177. package/dist/features/resource-dashboard/react/components/resourceRowListProps.js +14 -0
  178. package/dist/features/resource-dashboard/react/components/translatedErrorLabelProps.d.ts +16 -0
  179. package/dist/features/resource-dashboard/react/components/translatedErrorLabelProps.js +13 -0
  180. package/dist/features/resource-dashboard/rules.js +2 -8
  181. package/dist/features/sketch-editor/dom.js +150 -99
  182. package/dist/features/sketch-editor/react/components/SketchEditor.js +26 -15
  183. package/dist/features/skills/react/components/SkillDraftForm.js +13 -19
  184. package/dist/features/skills/react/components/SkillDraftForm.labels.d.ts +31 -0
  185. package/dist/features/skills/react/components/SkillDraftForm.labels.js +54 -0
  186. package/dist/features/skills/react/components/SkillRow.js +38 -18
  187. package/dist/features/skills/react/components/SkillRow.labels.d.ts +7 -0
  188. package/dist/features/skills/react/components/SkillRow.labels.js +18 -0
  189. package/dist/features/skills/react/components/SkillsTab.js +24 -14
  190. package/dist/features/skills/react/components/SkillsTab.labels.d.ts +7 -0
  191. package/dist/features/skills/react/components/SkillsTab.labels.js +11 -0
  192. package/dist/features/skills/rules.js +32 -23
  193. package/dist/features/source-config-list/agent-handles.js +2 -1
  194. package/dist/features/source-config-list/dependencies.js +53 -49
  195. package/dist/features/source-config-list/index.d.ts +3 -3
  196. package/dist/features/source-config-list/react/components/SourceConfigAddForm.d.ts +6 -5
  197. package/dist/features/source-config-list/react/components/SourceConfigAddForm.js +38 -7
  198. package/dist/features/source-config-list/react/components/SourceConfigField.js +69 -25
  199. package/dist/features/source-config-list/react/components/SourceConfigItemCard.d.ts +6 -5
  200. package/dist/features/source-config-list/react/components/SourceConfigItemCard.js +76 -20
  201. package/dist/features/source-config-list/react/components/SourceConfigListView.d.ts +5 -3
  202. package/dist/features/source-config-list/react/components/SourceConfigListView.js +33 -3
  203. package/dist/features/source-config-list/react/components/SourceConfigTestControl.js +17 -1
  204. package/dist/features/source-config-list/react/components/SourceConfigTrustOptions.d.ts +11 -0
  205. package/dist/features/source-config-list/react/components/SourceConfigTrustOptions.js +12 -0
  206. package/dist/features/source-config-list/types.d.ts +14 -0
  207. package/dist/features/tab-launcher-menu/react/components/TabLauncherMenu.js +25 -1
  208. package/dist/features/tab-launcher-menu/react/hooks/useTabLauncherMenu.js +15 -16
  209. package/dist/features/tab-strip/react/components/TabBar.js +8 -1
  210. package/dist/features/tabbed-dialog/react/components/TabbedDialog.js +8 -30
  211. package/dist/features/tabbed-dialog/react/components/TabbedDialogChrome.d.ts +15 -0
  212. package/dist/features/tabbed-dialog/react/components/TabbedDialogChrome.js +8 -0
  213. package/dist/features/tabbed-dialog/react/components/TabbedDialogHeader.d.ts +12 -0
  214. package/dist/features/tabbed-dialog/react/components/TabbedDialogHeader.js +6 -0
  215. package/dist/features/tabbed-dialog/react/components/TabbedDialogSidebar.d.ts +27 -0
  216. package/dist/features/tabbed-dialog/react/components/TabbedDialogSidebar.js +12 -0
  217. package/dist/features/tabbed-dialog/react/components/tabbedDialogView.d.ts +43 -0
  218. package/dist/features/tabbed-dialog/react/components/tabbedDialogView.js +59 -0
  219. package/dist/features/version-manager/dependencies.d.ts +3 -1
  220. package/dist/features/version-manager/dependencies.js +3 -2
  221. package/dist/features/version-manager/react/components/VersionManagerModal.js +10 -6
  222. package/dist/features/version-manager/react/hooks/useVersionManager.js +7 -4
  223. package/dist/features/viewer-shell/react/components/CommentSidePanel.js +103 -50
  224. package/dist/features/viewer-shell/react/components/MarkdownSplitPane.js +28 -8
  225. package/dist/features/viewer-shell/react/hooks/useCommentReorder.js +22 -16
  226. package/dist/features/viewer-shell/rules.js +19 -23
  227. package/dist/react/components/CustomSelect.d.ts +38 -1
  228. package/dist/react/components/CustomSelect.js +94 -38
  229. package/dist/react/components/OnboardingDropdown.js +20 -8
  230. package/dist/react/components/RemixIcon.stylesheet.js +3 -1
  231. package/dist/react/components/Toast.js +13 -2
  232. package/dist/react/components/TooltipLayer.js +5 -14
  233. package/dist/react/components/WorkingDirPicker.js +78 -50
  234. package/dist/react/components/remixicon-font-recovery.js +38 -30
  235. package/dist/react/components/select-popover.d.ts +8 -6
  236. package/dist/react/components/select-popover.js +33 -29
  237. package/dist/react/mcp-ui/McpUiHost.d.ts +1 -1
  238. package/dist/react/mcp-ui/McpUiHost.js +10 -11
  239. package/dist/react/mcp-ui/host-message-source.d.ts +14 -2
  240. package/dist/react/mcp-ui/host-message-source.js +15 -5
  241. package/dist/react/mcp-ui/mcp-ui-host-frame.d.ts +41 -0
  242. package/dist/react/mcp-ui/mcp-ui-host-frame.js +39 -0
  243. package/dist/renderers/annotation-canvas/react/components/AnnotationCanvas.js +97 -69
  244. package/dist/renderers/preview-modal-shell/react/components/PreviewModalShell.js +59 -30
  245. package/dist/renderers/react/components/ArtifactView.js +9 -6
  246. package/dist/renderers/srcdoc/build.d.ts +4 -1
  247. package/dist/renderers/srcdoc/build.js +62 -48
  248. package/dist/styles/admin.css +29 -14
  249. package/dist/styles/lightbox.css +142 -0
  250. package/dist/styles/variables.css +128 -0
  251. package/dist/theme/apply.js +9 -5
  252. package/dist/utils/auto-open-file.d.ts +2 -1
  253. package/dist/utils/auto-open-file.js +69 -85
  254. package/dist/utils/markdown-scroll-sync.js +108 -87
  255. package/dist/utils/polygon-selection.js +25 -26
  256. package/dist/utils/smooth-scroll-to-top.d.ts +1 -1
  257. package/dist/utils/smooth-scroll-to-top.js +2 -32
  258. package/dist/utils/sse.js +26 -26
  259. package/dist/utils/unit-bezier.d.ts +7 -0
  260. package/dist/utils/unit-bezier.js +38 -0
  261. package/package.json +9 -12
  262. package/dist/features/admin-widgets/components/ImagePreviewModal.hooks.d.ts +0 -13
  263. package/dist/features/admin-widgets/components/ImagePreviewModal.hooks.js +0 -46
@@ -0,0 +1,12 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { Icon } from '../../../../react/components/Icon.js';
3
+ /** One sidebar nav button. */
4
+ export function TabbedDialogNavItem({ tab, active, isModal, onSelect }) {
5
+ return (_jsxs("button", { type: "button", className: `jini-tabbed-dialog-nav-item${active ? ' active' : ''}`, "aria-pressed": active, onClick: () => onSelect(tab.id), "data-testid": `settings-dialog-nav-${tab.id}`, "data-agent-element": isModal ? undefined : `tab-${tab.id}`, "data-agent-role": isModal ? undefined : 'button', "data-agent-label": isModal ? undefined : tab.label, children: [_jsx("span", { className: "jini-tabbed-dialog-nav-icon", "aria-hidden": "true", children: tab.icon }), _jsxs("span", { className: "jini-tabbed-dialog-nav-text", children: [_jsx("strong", { children: tab.label }), tab.navHint ? _jsx("small", { children: tab.navHint }) : null] })] }));
6
+ }
7
+ /** The collapse toggle plus the sidebar nav it controls. */
8
+ export function TabbedDialogSidebar({ tabs, activeTabId, isModal, collapsed, onToggleCollapsed, onSelect, labels, }) {
9
+ const sidebarToggleLabel = collapsed ? labels.expandSidebarLabel : labels.collapseSidebarLabel;
10
+ return (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", className: "jini-tabbed-dialog-sidebar-toggle", onClick: onToggleCollapsed, "aria-label": sidebarToggleLabel, "aria-pressed": collapsed, "aria-controls": "jini-tabbed-dialog-sidebar", title: sidebarToggleLabel, children: _jsx(Icon, { name: collapsed ? 'chevron-right' : 'chevron-left', size: 15, strokeWidth: 2 }) }), _jsx("aside", { id: "jini-tabbed-dialog-sidebar", className: "jini-tabbed-dialog-sidebar", "aria-label": labels.sidebarAriaLabel, "aria-hidden": collapsed ? true : undefined, children: tabs.map((tab) => (_jsx(TabbedDialogNavItem, { tab: tab, active: tab.id === activeTabId, isModal: isModal, onSelect: onSelect }, tab.id))) })] }));
11
+ }
12
+ //# sourceMappingURL=TabbedDialogSidebar.js.map
@@ -0,0 +1,43 @@
1
+ import type { MouseEvent } from 'react';
2
+ import type { I18nContextValue } from '../../../i18n/index.js';
3
+ import type { TabbedDialogChromeLabels } from '../../types.js';
4
+ /** Every chrome label resolved to a concrete string. */
5
+ export type ResolvedTabbedDialogLabels = Required<TabbedDialogChromeLabels>;
6
+ /**
7
+ * Fills each chrome label the host left unset (absent or `undefined`) with its
8
+ * product-neutral default, translated through `t`.
9
+ */
10
+ export declare function resolveTabbedDialogLabels({ t }: {
11
+ t: I18nContextValue['t'];
12
+ }, { labels }?: {
13
+ labels?: TabbedDialogChromeLabels | undefined;
14
+ }): ResolvedTabbedDialogLabels;
15
+ /**
16
+ * `presentation` wins when set; otherwise a dialog with a close handler is a
17
+ * modal and one without is inline (see `TabbedDialogProps.presentation`).
18
+ */
19
+ export declare function resolveIsModal({ hasOnClose }: {
20
+ hasOnClose: boolean;
21
+ }, { presentation }?: {
22
+ presentation?: 'modal' | 'inline' | undefined;
23
+ }): boolean;
24
+ /** The dialog root's class list: base class plus state modifiers plus the host's own. */
25
+ export declare function buildTabbedDialogClassName({ sidebarCollapsed, fullscreen }: {
26
+ sidebarCollapsed: boolean;
27
+ fullscreen: boolean;
28
+ }, { className }?: {
29
+ className?: string | undefined;
30
+ }): string;
31
+ /**
32
+ * The dialog root's role/modality props. A modal is a `dialog` with
33
+ * `aria-modal` that swallows its own clicks (so only a backdrop click closes
34
+ * it); inline is a plain `region` with neither.
35
+ */
36
+ export declare function dialogRootRoleProps({ isModal }: {
37
+ isModal: boolean;
38
+ }): {
39
+ role: 'dialog' | 'region';
40
+ 'aria-modal': true | undefined;
41
+ onClick: ((event: Pick<MouseEvent, 'stopPropagation'>) => void) | undefined;
42
+ };
43
+ //# sourceMappingURL=tabbedDialogView.d.ts.map
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Fills each chrome label the host left unset (absent or `undefined`) with its
3
+ * product-neutral default, translated through `t`.
4
+ */
5
+ export function resolveTabbedDialogLabels({ t }, { labels } = {}) {
6
+ const defaults = {
7
+ // No brand/product word defaults to a bare `''` here on purpose — this component has no
8
+ // opinion about what it's a dialog FOR. A host that wants a kicker (as `SettingsDialogShell`
9
+ // does, with `t('Settings')`) supplies one via `labels.kicker`.
10
+ kicker: '',
11
+ welcomeKicker: t('Welcome'),
12
+ welcomeTitle: t('Get started'),
13
+ welcomeSubtitle: t('Set up your preferences before you begin.'),
14
+ closeLabel: t('Close'),
15
+ fullscreenLabel: t('Fullscreen'),
16
+ exitFullscreenLabel: t('Exit fullscreen'),
17
+ collapseSidebarLabel: t('Collapse sidebar'),
18
+ expandSidebarLabel: t('Expand sidebar'),
19
+ sidebarAriaLabel: t('Sections'),
20
+ };
21
+ const resolved = { ...defaults };
22
+ for (const key of Object.keys(defaults)) {
23
+ resolved[key] = labels?.[key] ?? defaults[key];
24
+ }
25
+ return resolved;
26
+ }
27
+ /**
28
+ * `presentation` wins when set; otherwise a dialog with a close handler is a
29
+ * modal and one without is inline (see `TabbedDialogProps.presentation`).
30
+ */
31
+ export function resolveIsModal({ hasOnClose }, { presentation } = {}) {
32
+ return (presentation ?? (hasOnClose ? 'modal' : 'inline')) === 'modal';
33
+ }
34
+ /** The dialog root's class list: base class plus state modifiers plus the host's own. */
35
+ export function buildTabbedDialogClassName({ sidebarCollapsed, fullscreen }, { className } = {}) {
36
+ return [
37
+ 'jini-tabbed-dialog',
38
+ sidebarCollapsed ? 'jini-tabbed-dialog-sidebar-collapsed' : '',
39
+ fullscreen ? 'jini-tabbed-dialog-fullscreen' : '',
40
+ className,
41
+ ]
42
+ .filter(Boolean)
43
+ .join(' ');
44
+ }
45
+ /** Keeps a click inside the modal dialog from reaching the backdrop's close handler. */
46
+ function stopPropagation(event) {
47
+ event.stopPropagation();
48
+ }
49
+ /**
50
+ * The dialog root's role/modality props. A modal is a `dialog` with
51
+ * `aria-modal` that swallows its own clicks (so only a backdrop click closes
52
+ * it); inline is a plain `region` with neither.
53
+ */
54
+ export function dialogRootRoleProps({ isModal }) {
55
+ if (!isModal)
56
+ return { role: 'region', 'aria-modal': undefined, onClick: undefined };
57
+ return { role: 'dialog', 'aria-modal': true, onClick: stopPropagation };
58
+ }
59
+ //# sourceMappingURL=tabbedDialogView.js.map
@@ -7,7 +7,9 @@ import type { VersionManagerClipboardPort, VersionManagerDependencies, VersionMa
7
7
  * transport call") — a real host binds its own REST/daemon calls to
8
8
  * `VersionManagerPort` instead.
9
9
  */
10
- export declare function createFakeVersionManagerPort<TVersion extends VersionRecord = VersionRecord>(seed?: ReadonlyMap<string, TVersion[]>): VersionManagerPort<TVersion>;
10
+ export declare function createFakeVersionManagerPort<TVersion extends VersionRecord = VersionRecord>(seed?: ReadonlyMap<string, TVersion[]>, { content: seedContent }?: {
11
+ content?: ReadonlyMap<string, string>;
12
+ }): VersionManagerPort<TVersion>;
11
13
  /** Reuses `features/viewer-shell/`'s real browser clipboard implementation
12
14
  * — clipboard access is a generic browser API with no backend shape to
13
15
  * fake, same reasoning that feature's own `dependencies.ts` documents. */
@@ -6,9 +6,10 @@ import { createBrowserViewerClipboard } from '../viewer-shell/index.js';
6
6
  * transport call") — a real host binds its own REST/daemon calls to
7
7
  * `VersionManagerPort` instead.
8
8
  */
9
- export function createFakeVersionManagerPort(seed) {
9
+ export function createFakeVersionManagerPort(seed, { content: seedContent } = {}) {
10
10
  const store = new Map(seed ? [...seed.entries()].map(([k, v]) => [k, [...v]]) : []);
11
- const content = new Map();
11
+ // Content uses the same scope/name key as metadata, followed by the version id.
12
+ const content = new Map(seedContent);
12
13
  const key = (fileRef) => `${fileRef.scopeId}:${fileRef.name}`;
13
14
  return {
14
15
  async listVersions(fileRef) {
@@ -30,9 +30,6 @@ export function VersionManagerModal({ fileRef, currentContent, onClose, onRestor
30
30
  const { copied, copy } = useCopyToClipboard(dependencies.clipboard);
31
31
  const { versions, visibleVersions, versionById, selectedVersion, search, setSearch, showSearch, loading, loadingContent, error, previewDocument, frameReady, markFrameLoaded, restoring, restoreDisabled, restore, openInNewTab, viewportPresets: presets, viewport, setViewport, selectVersion, prefetchVersion, } = controller;
32
32
  const versionCountLabel = versions.length === 1 ? t('1 version') : t('{count} versions', { count: versions.length });
33
- const selectedPrompt = selectedVersion?.prompt?.trim() ?? '';
34
- const selectedDate = selectedVersion ? formatVersionDateTime(selectedVersion.createdAt, locale) : fileRef.name;
35
- const selectedRestoredFrom = selectedVersion ? restoredFromVersion(selectedVersion, versionById) : null;
36
33
  // Layered Escape handling: close the modal only when neither popover is
37
34
  // consuming the keystroke — each popover owns its own Escape dismissal
38
35
  // via `useDismissOnOutsideOrEscape`, so this only needs to know whether
@@ -62,8 +59,15 @@ export function VersionManagerModal({ fileRef, currentContent, onClose, onRestor
62
59
  return createPortal(_jsx("div", { className: "jini-modal-backdrop jini-version-modal-backdrop", role: "presentation", onMouseDown: (event) => {
63
60
  if (event.target === event.currentTarget)
64
61
  onClose();
65
- }, children: _jsxs("div", { className: "jini-version-modal", role: "dialog", "aria-modal": "true", "aria-label": t('Version history'), children: [_jsx(VersionSidebar, { countLabel: versionCountLabel, search: search, onSearchChange: setSearch, showSearch: showSearch, loading: loading, versions: versions, visibleVersions: visibleVersions, selectedVersionId: selectedVersion?.id ?? null, onSelect: selectVersion, onPrefetch: prefetchVersion, formatDate: (version) => formatVersionDateTime(version.createdAt, locale), sourceLabel: (version) => t(versionSourceLabel(version.source)), sourceClassName: (version) => versionSourceClassName(version.source), restoredFrom: (version) => restoredFromVersion(version, versionById) }), _jsxs("div", { className: "jini-version-main", children: [_jsxs("header", { className: "jini-version-head", children: [_jsx("div", { className: "jini-version-meta", children: _jsxs("div", { className: "jini-version-meta-row", children: [selectedVersion?.current ? (_jsx("span", { className: "jini-version-current-badge", children: t('Current') })) : null, selectedVersion ? (_jsx("span", { className: `jini-version-source-badge ${versionSourceClassName(selectedVersion.source)}`, children: t(versionSourceLabel(selectedVersion.source)) })) : null, _jsx("span", { className: "jini-version-selected-date", children: selectedDate }), selectedRestoredFrom ? (_jsx("span", { className: "jini-version-restored-from", children: t('Restored from v{version}', { version: selectedRestoredFrom.version }) })) : null, _jsx(VersionPromptPopover, { prompt: selectedPrompt, disabled: !selectedVersion, copied: copied, onCopy: (text) => void copy(text), onOpenChange: (open) => {
66
- promptOpenRef.current = open;
67
- } })] }) }), _jsxs("div", { className: "jini-version-actions", children: [selectedVersion && !selectedVersion.current ? (_jsx(VersionRestoreControl, { disabled: restoreDisabled, restoring: restoring, onRestore: () => void restore() })) : null, _jsx(ViewportToggleGroup, { presets: presets, viewport: viewport, onViewport: setViewport, ariaLabel: t('Preview viewport') }), openInNewTab ? (_jsx("button", { type: "button", className: "jini-viewer-action jini-viewer-action-icon", "aria-label": t('Open in new tab'), title: t('Open in new tab'), disabled: loadingContent, onClick: openInNewTab, children: t('Open') })) : null, _jsx("button", { type: "button", className: "jini-viewer-action jini-viewer-action-icon", "aria-label": t('Close'), title: t('Close'), onClick: onClose, children: t('Close') })] })] }), _jsx(VersionPreviewFrame, { previewDocument: previewDocument, frameReady: frameReady, onFrameLoad: () => markFrameLoaded(previewDocument), viewport: viewport, viewportPresets: presets, title: selectedVersion ? `${fileRef.name} · v${selectedVersion.version}` : fileRef.name, error: error, loading: loading, loadingContent: loadingContent })] })] }) }), document.body);
62
+ }, children: _jsxs("div", { className: "jini-version-modal", role: "dialog", "aria-modal": "true", "aria-label": t('Version history'), children: [_jsx(VersionSidebar, { countLabel: versionCountLabel, search: search, onSearchChange: setSearch, showSearch: showSearch, loading: loading, versions: versions, visibleVersions: visibleVersions, selectedVersionId: selectedVersion ? selectedVersion.id : null, onSelect: selectVersion, onPrefetch: prefetchVersion, formatDate: (version) => formatVersionDateTime(version.createdAt, locale), sourceLabel: (version) => t(versionSourceLabel(version.source)), sourceClassName: (version) => versionSourceClassName(version.source), restoredFrom: (version) => restoredFromVersion(version, versionById) }), _jsxs("div", { className: "jini-version-main", children: [_jsxs("header", { className: "jini-version-head", children: [_jsx(VersionMetadata, { selectedVersion: selectedVersion, versionById: versionById, fileName: fileRef.name, copied: copied, onCopy: (text) => void copy(text), onOpenChange: (open) => {
63
+ promptOpenRef.current = open;
64
+ } }), _jsxs("div", { className: "jini-version-actions", children: [selectedVersion && !selectedVersion.current ? (_jsx(VersionRestoreControl, { disabled: restoreDisabled, restoring: restoring, onRestore: () => void restore() })) : null, _jsx(ViewportToggleGroup, { presets: presets, viewport: viewport, onViewport: setViewport, ariaLabel: t('Preview viewport') }), openInNewTab ? (_jsx("button", { type: "button", className: "jini-viewer-action jini-viewer-action-icon", "aria-label": t('Open in new tab'), title: t('Open in new tab'), disabled: loadingContent, onClick: openInNewTab, children: t('Open') })) : null, _jsx("button", { type: "button", className: "jini-viewer-action jini-viewer-action-icon", "aria-label": t('Close'), title: t('Close'), onClick: onClose, children: t('Close') })] })] }), _jsx(VersionPreviewFrame, { previewDocument: previewDocument, frameReady: frameReady, onFrameLoad: () => markFrameLoaded(previewDocument), viewport: viewport, viewportPresets: presets, title: selectedVersion ? `${fileRef.name} · v${selectedVersion.version}` : fileRef.name, error: error, loading: loading, loadingContent: loadingContent })] })] }) }), document.body);
65
+ }
66
+ function VersionMetadata({ selectedVersion, versionById, fileName, copied, onCopy, onOpenChange, }) {
67
+ const { t, locale } = useI18n();
68
+ const selectedPrompt = selectedVersion?.prompt?.trim() ?? '';
69
+ const selectedDate = selectedVersion ? formatVersionDateTime(selectedVersion.createdAt, locale) : fileName;
70
+ const selectedRestoredFrom = selectedVersion ? restoredFromVersion(selectedVersion, versionById) : null;
71
+ return (_jsx("div", { className: "jini-version-meta", children: _jsxs("div", { className: "jini-version-meta-row", children: [selectedVersion?.current ? (_jsx("span", { className: "jini-version-current-badge", children: t('Current') })) : null, selectedVersion ? (_jsx("span", { className: `jini-version-source-badge ${versionSourceClassName(selectedVersion.source)}`, children: t(versionSourceLabel(selectedVersion.source)) })) : null, _jsx("span", { className: "jini-version-selected-date", children: selectedDate }), selectedRestoredFrom ? (_jsx("span", { className: "jini-version-restored-from", children: t('Restored from v{version}', { version: selectedRestoredFrom.version }) })) : null, _jsx(VersionPromptPopover, { prompt: selectedPrompt, disabled: !selectedVersion, copied: copied, onCopy: onCopy, onOpenChange: onOpenChange })] }) }));
68
72
  }
69
73
  //# sourceMappingURL=VersionManagerModal.js.map
@@ -4,6 +4,11 @@ import { DEFAULT_VIEWPORT_PRESETS } from '../../../viewer-shell/index.js';
4
4
  import { PREVIEW_LOAD_FALLBACK_MS, SEARCH_VISIBLE_THRESHOLD, } from '../../constants.js';
5
5
  import { defaultVersionManagerDependencies } from '../../dependencies.js';
6
6
  import { buildVersionIndex, contentMatchesSelection, filterVersionsBySearch, formatVersionDateTime, isRestoreDisabled, restoredFromVersion, resolveSelectedVersion, shouldShowVersionSearch, sortVersionsDescending, versionSourceLabel, } from '../../rules.js';
7
+ function seedCurrentVersionContent({ currentVersion, currentContent, contentCache, }) {
8
+ if (currentVersion && currentContent != null && !contentCache.has(currentVersion.id)) {
9
+ contentCache.set(currentVersion.id, currentContent);
10
+ }
11
+ }
7
12
  /**
8
13
  * Owns the whole version-manager state machine: list fetch, selection,
9
14
  * content cache/prefetch, search, restore, and viewport choice. Kept as one
@@ -102,9 +107,7 @@ export function useVersionManager(dependencies, options) {
102
107
  const nextVersions = sortVersionsDescending(result);
103
108
  setVersions(nextVersions);
104
109
  const currentVersion = nextVersions.find((version) => version.current);
105
- if (currentVersion && currentContent != null && !contentCacheRef.current.has(currentVersion.id)) {
106
- contentCacheRef.current.set(currentVersion.id, currentContent);
107
- }
110
+ seedCurrentVersionContent({ currentVersion, currentContent, contentCache: contentCacheRef.current });
108
111
  const nextSelected = (preferredId ? nextVersions.find((version) => version.id === preferredId) : null) ??
109
112
  currentVersion ??
110
113
  nextVersions[0] ??
@@ -207,7 +210,7 @@ export function useVersionManager(dependencies, options) {
207
210
  // this hook already has.
208
211
  contentCacheRef.current.set(restoredVersion.id, selectedContent);
209
212
  preserveErrorOnNextSelectionRef.current = true;
210
- await loadVersions(result.version?.id ?? selectedVersion.id);
213
+ await loadVersions(restoredVersion.id);
211
214
  setError(result.warning.message);
212
215
  return;
213
216
  }
@@ -22,30 +22,28 @@ export function CommentSidePanel({ comments, selectedIds, activeCommentId, colla
22
22
  const reorder = useCommentReorderHook(orderedIds, onReorder);
23
23
  const visibleSelectedIds = visibleSelectedCommentIds(comments, selectedIds);
24
24
  const selectedCount = visibleSelectedIds.size;
25
- const allSelected = comments.length > 0 && selectedCount === comments.length;
26
25
  const commentsLabel = t('Comments');
27
- const canCreateComment = Boolean(onCreateComment) && newCommentDraft.trim().length > 0 && !sending && !sendDisabled;
28
- const collapsedRailRef = useRef(null);
29
- const expandedToggleRef = useRef(null);
30
- const pendingToggleFocusRef = useRef(null);
31
26
  const panelId = useId();
32
- const formatTs = formatTimestamp ?? ((ts) => {
33
- const translation = relativeCommentTimeTranslation(ts);
34
- return t(translation.key, translation.vars);
27
+ const formatTs = useCommentTimestampFormatter(formatTimestamp);
28
+ const { collapsedRailRef, expandedToggleRef, handleCollapsedChange } = useCollapseToggleFocus({
29
+ collapsed,
30
+ onCollapsedChange,
35
31
  });
36
- async function submitNewComment() {
37
- if (!onCreateComment || !newCommentDraft.trim())
38
- return;
39
- const saved = await onCreateComment(newCommentDraft.trim());
40
- if (saved)
41
- setNewCommentDraft('');
32
+ if (collapsed) {
33
+ return (_jsx(CommentSideRail, { railRef: collapsedRailRef, commentsLabel: commentsLabel, commentCount: comments.length, onExpand: () => handleCollapsedChange(false, 'expanded') }));
42
34
  }
35
+ return (_jsxs("aside", { id: panelId, className: "comment-side-panel", "data-testid": "comment-side-panel", "aria-label": commentsLabel, children: [_jsx(CommentSideHeader, { toggleRef: expandedToggleRef, panelId: panelId, commentsLabel: commentsLabel, commentCount: comments.length, selectedCount: selectedCount, onSelectAll: onSelectAll, onCollapse: () => handleCollapsedChange(true, 'collapsed') }), _jsx("div", { className: "comment-side-list", onDragLeave: reorder.onDragLeaveContainer, children: comments.length === 0 ? (_jsx("div", { className: "comment-side-empty", children: t('No comments yet') })) : (comments.map((comment, index) => (_jsx(CommentSideItem, { comment: comment, index: index, selected: visibleSelectedIds.has(comment.id), active: comment.id === activeCommentId, reorder: reorder, onReply: onReply, onToggleSelect: onToggleSelect, getCommentLabel: getCommentLabel, getCommentTimestamp: getCommentTimestamp, getCommentBody: getCommentBody, getCommentAttachments: getCommentAttachments, resolveAttachmentUrl: resolveAttachmentUrl, formatTs: formatTs }, comment.id)))) }), _jsx(CommentSideSelectBar, { selectedCount: selectedCount, sending: sending, sendDisabled: sendDisabled, queueOnSend: queueOnSend, onClearSelection: onClearSelection, onSendSelected: onSendSelected }), composer ? _jsx("div", { className: "comment-side-composer", children: composer }) : null, _jsx(CommentSideCreateForm, { renderCreateForm: renderCreateForm, draft: newCommentDraft, onDraftChange: setNewCommentDraft, onCreateComment: onCreateComment, sending: sending, sendDisabled: sendDisabled })] }));
36
+ }
37
+ /** Moves focus onto whichever collapse/expand control replaced the one the
38
+ * user just clicked, once the `collapsed` change has re-rendered. */
39
+ function useCollapseToggleFocus({ collapsed, onCollapsedChange, }) {
40
+ const collapsedRailRef = useRef(null);
41
+ const expandedToggleRef = useRef(null);
42
+ const pendingToggleFocusRef = useRef(null);
43
43
  useEffect(() => {
44
- const target = pendingToggleFocusRef.current === 'collapsed'
45
- ? collapsedRailRef.current
46
- : pendingToggleFocusRef.current === 'expanded'
47
- ? expandedToggleRef.current
48
- : null;
44
+ const targets = { collapsed: collapsedRailRef.current, expanded: expandedToggleRef.current };
45
+ const pending = pendingToggleFocusRef.current;
46
+ const target = pending ? targets[pending] : null;
49
47
  if (!target)
50
48
  return;
51
49
  pendingToggleFocusRef.current = null;
@@ -55,37 +53,92 @@ export function CommentSidePanel({ comments, selectedIds, activeCommentId, colla
55
53
  pendingToggleFocusRef.current = nextFocusTarget;
56
54
  onCollapsedChange(nextCollapsed);
57
55
  }
58
- if (collapsed) {
59
- return (_jsxs("button", { ref: collapsedRailRef, type: "button", className: "comment-side-rail", "data-testid": "comment-side-collapsed-rail", "aria-label": t('Show {label}', { label: commentsLabel }), "aria-expanded": false, title: t('Show {label}', { label: commentsLabel }), onClick: () => handleCollapsedChange(false, 'expanded'), children: [_jsx(RemixIcon, { name: "message-3-line", size: 15 }), _jsx("span", { children: commentsLabel }), comments.length > 0 ? _jsx("strong", { children: comments.length }) : null] }));
56
+ return { collapsedRailRef, expandedToggleRef, handleCollapsedChange };
57
+ }
58
+ function useCommentTimestampFormatter(formatTimestamp) {
59
+ const t = useT();
60
+ return (formatTimestamp ??
61
+ ((ts) => {
62
+ const translation = relativeCommentTimeTranslation(ts);
63
+ return t(translation.key, translation.vars);
64
+ }));
65
+ }
66
+ function CommentSideRail({ railRef, commentsLabel, commentCount, onExpand, }) {
67
+ const t = useT();
68
+ const showLabel = t('Show {label}', { label: commentsLabel });
69
+ return (_jsxs("button", { ref: railRef, type: "button", className: "comment-side-rail", "data-testid": "comment-side-collapsed-rail", "aria-label": showLabel, "aria-expanded": false, title: showLabel, onClick: onExpand, children: [_jsx(RemixIcon, { name: "message-3-line", size: 15 }), _jsx("span", { children: commentsLabel }), commentCount > 0 ? _jsx("strong", { children: commentCount }) : null] }));
70
+ }
71
+ function CommentSideHeader({ toggleRef, panelId, commentsLabel, commentCount, selectedCount, onSelectAll, onCollapse, }) {
72
+ const t = useT();
73
+ const hideLabel = t('Hide {label}', { label: commentsLabel });
74
+ const allSelected = commentCount > 0 && selectedCount === commentCount;
75
+ return (_jsxs("div", { className: "comment-side-header", children: [_jsxs("div", { className: "comment-side-title", children: [_jsx(RemixIcon, { name: "message-3-line", size: 15 }), _jsx("span", { children: commentsLabel })] }), _jsxs("div", { className: "comment-side-header-actions", children: [commentCount > 0 ? (_jsx("button", { type: "button", className: "comment-side-select-all", disabled: allSelected, onClick: onSelectAll, children: t('Select all') })) : null, _jsx("button", { ref: toggleRef, type: "button", className: "comment-side-collapse", "aria-label": hideLabel, "aria-controls": panelId, "aria-expanded": true, title: hideLabel, onClick: onCollapse, children: _jsx(Icon, { name: "chevron-right", size: 14 }) })] })] }));
76
+ }
77
+ /** ` comment-side-item-drop-<edge>` while another comment is dragged over
78
+ * `commentId` with a known edge, else `''`. */
79
+ function dropEdgeClassName(dragState, commentId) {
80
+ const showsDropEdge = dragState?.overId === commentId && dragState.draggingId !== commentId && dragState.edge;
81
+ return showsDropEdge ? ` comment-side-item-drop-${dragState.edge}` : '';
82
+ }
83
+ function commentItemClassName({ selected, active, isDragging, dropClass, }) {
84
+ return `comment-side-item${selected ? ' selected' : ''}${active ? ' active' : ''}${isDragging ? ' dragging' : ''}${dropClass}`;
85
+ }
86
+ function CommentSideItem({ comment, index, selected, active, reorder, onReply, onToggleSelect, getCommentLabel, getCommentTimestamp, getCommentBody, getCommentAttachments, resolveAttachmentUrl, formatTs, }) {
87
+ const t = useT();
88
+ const className = commentItemClassName({
89
+ selected,
90
+ active,
91
+ isDragging: reorder.dragState?.draggingId === comment.id,
92
+ dropClass: dropEdgeClassName(reorder.dragState, comment.id),
93
+ });
94
+ const attachments = getCommentAttachments?.(comment) ?? [];
95
+ const dragLabel = t('Drag to reorder');
96
+ return (_jsxs("div", { className: className, "data-testid": "comment-side-item", "data-comment-id": comment.id, "aria-current": active ? 'true' : undefined, role: "button", tabIndex: 0, onDragOver: (event) => reorder.onDragOver(event, comment.id), onDrop: (event) => reorder.onDrop(event, comment.id), onClick: () => onReply(comment), onKeyDown: (event) => {
97
+ if (event.key !== 'Enter' && event.key !== ' ')
98
+ return;
99
+ event.preventDefault();
100
+ onReply(comment);
101
+ }, children: [_jsxs("div", { className: "comment-side-item-head", children: [_jsx("button", { type: "button", className: "comment-side-drag-handle", title: dragLabel, "aria-label": dragLabel, draggable: reorder.canReorder, disabled: !reorder.canReorder, onClick: (event) => event.stopPropagation(), onDragStart: (event) => reorder.onDragStart(event, comment.id), onDragEnd: reorder.clear, children: _jsx(Icon, { name: "grip-vertical", size: 13 }) }), _jsx("span", { className: "comment-side-author", children: _jsx("strong", { children: `${index + 1}. ${getCommentLabel(comment, index)}` }) }), _jsx("span", { className: "comment-side-time", children: formatTs(getCommentTimestamp(comment)) }), _jsx("button", { type: "button", className: `comment-side-check${selected ? ' checked' : ''}`, "aria-label": selected ? t('Deselect') : t('Select'), "aria-pressed": selected, onClick: (event) => {
102
+ event.stopPropagation();
103
+ onToggleSelect(comment.id);
104
+ }, children: selected ? _jsx(Icon, { name: "check", size: 11 }) : null })] }), _jsx("div", { className: "comment-side-body", children: getCommentBody(comment) }), resolveAttachmentUrl && attachments.length > 0 ? (_jsx(CommentSideAttachments, { attachments: attachments, resolveAttachmentUrl: resolveAttachmentUrl })) : null] }));
105
+ }
106
+ function CommentSideAttachments({ attachments, resolveAttachmentUrl, }) {
107
+ return (_jsx("div", { className: "comment-side-attachments", children: attachments.map((attachment) => {
108
+ const url = resolveAttachmentUrl(attachment);
109
+ return (_jsx("a", { className: "comment-side-attachment", "data-testid": "comment-side-attachment", href: url, target: "_blank", rel: "noopener noreferrer", "aria-label": attachment.name, title: attachment.name, onClick: (event) => event.stopPropagation(), children: _jsx("img", { src: url, alt: attachment.name }) }, attachment.path));
110
+ }) }));
111
+ }
112
+ function CommentSideSelectBar({ selectedCount, sending, sendDisabled, queueOnSend, onClearSelection, onSendSelected, }) {
113
+ const t = useT();
114
+ if (selectedCount === 0)
115
+ return null;
116
+ return (_jsxs("div", { className: "comment-side-selectbar", "data-testid": "comment-side-selectbar", children: [_jsx("span", { className: "comment-side-selectcount", children: t('{n} selected', { n: selectedCount }) }), _jsx("button", { type: "button", className: "comment-side-clear", onClick: onClearSelection, children: t('Clear') }), _jsx("button", { type: "button", className: "comment-side-send", "data-testid": "comment-side-send-claude", disabled: sending || sendDisabled, onClick: () => void onSendSelected(), children: sending ? t('Sending…') : queueOnSend ? t('Queue') : t('Send to chat') })] }));
117
+ }
118
+ function CommentSideCreateForm({ renderCreateForm, draft, onDraftChange, onCreateComment, sending, sendDisabled, }) {
119
+ const t = useT();
120
+ if (!renderCreateForm || !onCreateComment)
121
+ return null;
122
+ // Bind the narrowed callback: TypeScript drops the narrowing inside the
123
+ // nested async `submitNewComment` closure below.
124
+ const createComment = onCreateComment;
125
+ const canCreateComment = draft.trim().length > 0 && !sending && !sendDisabled;
126
+ const addLabel = t('Add a comment…');
127
+ async function submitNewComment() {
128
+ if (!draft.trim())
129
+ return;
130
+ const saved = await createComment(draft.trim());
131
+ if (saved)
132
+ onDraftChange('');
60
133
  }
61
- return (_jsxs("aside", { id: panelId, className: "comment-side-panel", "data-testid": "comment-side-panel", "aria-label": commentsLabel, children: [_jsxs("div", { className: "comment-side-header", children: [_jsxs("div", { className: "comment-side-title", children: [_jsx(RemixIcon, { name: "message-3-line", size: 15 }), _jsx("span", { children: commentsLabel })] }), _jsxs("div", { className: "comment-side-header-actions", children: [comments.length > 0 ? (_jsx("button", { type: "button", className: "comment-side-select-all", disabled: allSelected, onClick: onSelectAll, children: t('Select all') })) : null, _jsx("button", { ref: expandedToggleRef, type: "button", className: "comment-side-collapse", "aria-label": t('Hide {label}', { label: commentsLabel }), "aria-controls": panelId, "aria-expanded": true, title: t('Hide {label}', { label: commentsLabel }), onClick: () => handleCollapsedChange(true, 'collapsed'), children: _jsx(Icon, { name: "chevron-right", size: 14 }) })] })] }), _jsx("div", { className: "comment-side-list", onDragLeave: reorder.onDragLeaveContainer, children: comments.length === 0 ? (_jsx("div", { className: "comment-side-empty", children: t('No comments yet') })) : (comments.map((comment, index) => {
62
- const selected = visibleSelectedIds.has(comment.id);
63
- const active = comment.id === activeCommentId;
64
- const isDragging = reorder.dragState?.draggingId === comment.id;
65
- const dropClass = reorder.dragState?.overId === comment.id && reorder.dragState.draggingId !== comment.id && reorder.dragState.edge
66
- ? ` comment-side-item-drop-${reorder.dragState.edge}`
67
- : '';
68
- const attachments = getCommentAttachments?.(comment) ?? [];
69
- return (_jsxs("div", { className: `comment-side-item${selected ? ' selected' : ''}${active ? ' active' : ''}${isDragging ? ' dragging' : ''}${dropClass}`, "data-testid": "comment-side-item", "data-comment-id": comment.id, "aria-current": active ? 'true' : undefined, role: "button", tabIndex: 0, onDragOver: (event) => reorder.onDragOver(event, comment.id), onDrop: (event) => reorder.onDrop(event, comment.id), onClick: () => onReply(comment), onKeyDown: (event) => {
70
- if (event.key !== 'Enter' && event.key !== ' ')
71
- return;
72
- event.preventDefault();
73
- onReply(comment);
74
- }, children: [_jsxs("div", { className: "comment-side-item-head", children: [_jsx("button", { type: "button", className: "comment-side-drag-handle", title: t('Drag to reorder'), "aria-label": t('Drag to reorder'), draggable: reorder.canReorder, disabled: !reorder.canReorder, onClick: (event) => event.stopPropagation(), onDragStart: (event) => reorder.onDragStart(event, comment.id), onDragEnd: reorder.clear, children: _jsx(Icon, { name: "grip-vertical", size: 13 }) }), _jsx("span", { className: "comment-side-author", children: _jsx("strong", { children: `${index + 1}. ${getCommentLabel(comment, index)}` }) }), _jsx("span", { className: "comment-side-time", children: formatTs(getCommentTimestamp(comment)) }), _jsx("button", { type: "button", className: `comment-side-check${selected ? ' checked' : ''}`, "aria-label": selected ? t('Deselect') : t('Select'), "aria-pressed": selected, onClick: (event) => {
75
- event.stopPropagation();
76
- onToggleSelect(comment.id);
77
- }, children: selected ? _jsx(Icon, { name: "check", size: 11 }) : null })] }), _jsx("div", { className: "comment-side-body", children: getCommentBody(comment) }), resolveAttachmentUrl && attachments.length > 0 ? (_jsx("div", { className: "comment-side-attachments", children: attachments.map((attachment) => {
78
- const url = resolveAttachmentUrl(attachment);
79
- return (_jsx("a", { className: "comment-side-attachment", "data-testid": "comment-side-attachment", href: url, target: "_blank", rel: "noopener noreferrer", "aria-label": attachment.name, title: attachment.name, onClick: (event) => event.stopPropagation(), children: _jsx("img", { src: url, alt: attachment.name }) }, attachment.path));
80
- }) })) : null] }, comment.id));
81
- })) }), selectedCount > 0 ? (_jsxs("div", { className: "comment-side-selectbar", "data-testid": "comment-side-selectbar", children: [_jsx("span", { className: "comment-side-selectcount", children: t('{n} selected', { n: selectedCount }) }), _jsx("button", { type: "button", className: "comment-side-clear", onClick: onClearSelection, children: t('Clear') }), _jsx("button", { type: "button", className: "comment-side-send", "data-testid": "comment-side-send-claude", disabled: sending || sendDisabled, onClick: () => void onSendSelected(), children: sending ? t('Sending…') : queueOnSend ? t('Queue') : t('Send to chat') })] })) : null, composer ? _jsx("div", { className: "comment-side-composer", children: composer }) : null, renderCreateForm && onCreateComment ? (_jsx("form", { className: "comment-side-new-comment composer", onSubmit: (event) => {
82
- event.preventDefault();
83
- void submitNewComment();
84
- }, children: _jsxs("div", { className: "composer-shell comment-side-new-comment-shell", children: [_jsx("div", { className: "composer-input-wrap", children: _jsx("div", { className: "composer-textarea-layer", children: _jsx("textarea", { value: newCommentDraft, placeholder: t('Add a comment…'), "aria-label": t('Add a comment…'), onChange: (event) => setNewCommentDraft(event.target.value), onKeyDown: (event) => {
85
- if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {
86
- event.preventDefault();
87
- void submitNewComment();
88
- }
89
- } }) }) }), _jsxs("div", { className: "composer-row comment-side-new-comment-actions", children: [_jsx("button", { type: "button", className: "icon-btn", title: t('Attach'), "aria-label": t('Attach'), disabled: true, children: _jsx(Icon, { name: "attach", size: 15 }) }), _jsx("span", { className: "composer-spacer" }), _jsxs("button", { type: "submit", className: `composer-send${sending ? ' is-sending' : ''}`, disabled: !canCreateComment, children: [_jsx(Icon, { name: "send", size: 13 }), _jsx("span", { children: sending ? t('Sending…') : t('Send') })] })] })] }) })) : null] }));
134
+ return (_jsx("form", { className: "comment-side-new-comment composer", onSubmit: (event) => {
135
+ event.preventDefault();
136
+ void submitNewComment();
137
+ }, children: _jsxs("div", { className: "composer-shell comment-side-new-comment-shell", children: [_jsx("div", { className: "composer-input-wrap", children: _jsx("div", { className: "composer-textarea-layer", children: _jsx("textarea", { value: draft, placeholder: addLabel, "aria-label": addLabel, onChange: (event) => onDraftChange(event.target.value), onKeyDown: (event) => {
138
+ if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {
139
+ event.preventDefault();
140
+ void submitNewComment();
141
+ }
142
+ } }) }) }), _jsxs("div", { className: "composer-row comment-side-new-comment-actions", children: [_jsx("button", { type: "button", className: "icon-btn", title: t('Attach'), "aria-label": t('Attach'), disabled: true, children: _jsx(Icon, { name: "attach", size: 15 }) }), _jsx("span", { className: "composer-spacer" }), _jsxs("button", { type: "submit", className: `composer-send${sending ? ' is-sending' : ''}`, disabled: !canCreateComment, children: [_jsx(Icon, { name: "send", size: 13 }), _jsx("span", { children: sending ? t('Sending…') : t('Send') })] })] })] }) }));
90
143
  }
91
144
  //# sourceMappingURL=CommentSidePanel.js.map
@@ -31,13 +31,33 @@ export function MarkdownSplitPane({ mode, onModeChange, sourceText, onSourceChan
31
31
  const showEditor = mode === 'source' || mode === 'split';
32
32
  const showPreview = mode === 'preview' || mode === 'split';
33
33
  const isLoading = loading || previewHtml === null;
34
- return (_jsx(ViewerShell, { kindClassName: "markdown-split-viewer", bodyClassName: `markdown-workbench markdown-workbench-${mode}`, toolbarLeft: _jsxs(_Fragment, { children: [toolbarLeftExtra, _jsx(SegmentedToggle, { className: "markdown-mode-tabs", ariaLabel: modeAriaLabel ?? t('Markdown view mode'), value: mode, onChange: onModeChange, options: [
35
- { value: 'source', label: sourceLabel ?? t('Source') },
36
- { value: 'split', label: splitLabel ?? t('Split') },
37
- { value: 'preview', label: previewLabel ?? t('Preview') },
38
- ] })] }), toolbarActions: toolbarActions, children: isLoading ? (_jsx("div", { className: "viewer-empty", children: loadingLabel ?? t('Loading…') })) : (_jsxs(_Fragment, { children: [showEditor ? (_jsx("section", { className: "markdown-editor-pane", "aria-label": editorAriaLabel ?? t('Markdown editor'), children: _jsx("textarea", { ref: editorRef, className: "markdown-editor", value: sourceText, "aria-label": editorAriaLabel ?? t('Markdown editor'), spellCheck: true, onFocus: () => activatePane('editor'), onChange: (event) => {
39
- activatePane('editor');
40
- onSourceChange(event.currentTarget.value);
41
- }, onScroll: handleEditorScroll, ...editorTextareaProps }) })) : null, showPreview ? (_jsx("div", { className: "markdown-preview-pane-wrap", children: _jsx("section", { ref: previewRef, className: "markdown-preview-pane", "aria-label": previewAriaLabel ?? t('Markdown preview'), onPointerDown: () => activatePane('preview'), onWheel: () => activatePane('preview'), onTouchStart: () => activatePane('preview'), onKeyDown: () => activatePane('preview'), onFocus: () => activatePane('preview'), onScroll: handlePreviewScroll, children: _jsx("article", { className: "markdown-rendered", onClick: onPreviewClick, dangerouslySetInnerHTML: { __html: previewHtml } }) }) })) : null] })) }));
34
+ return (_jsx(ViewerShell, { kindClassName: "markdown-split-viewer", bodyClassName: `markdown-workbench markdown-workbench-${mode}`, toolbarLeft: _jsxs(_Fragment, { children: [toolbarLeftExtra, _jsx(MarkdownModeTabs, { mode: mode, onModeChange: onModeChange, sourceLabel: sourceLabel, splitLabel: splitLabel, previewLabel: previewLabel, modeAriaLabel: modeAriaLabel })] }), toolbarActions: toolbarActions, children: isLoading ? (_jsx("div", { className: "viewer-empty", children: loadingLabel ?? t('Loading…') })) : (_jsxs(_Fragment, { children: [showEditor ? (_jsx(MarkdownEditorPane, { editorRef: editorRef, sourceText: sourceText, onSourceChange: onSourceChange, ariaLabel: editorAriaLabel, onScroll: handleEditorScroll, activatePane: activatePane, textareaProps: editorTextareaProps })) : null, showPreview ? (_jsx(MarkdownPreviewPane, { previewRef: previewRef,
35
+ // Non-null assertion: `isLoading` (above) is true whenever
36
+ // `previewHtml === null`, and this JSX only renders in the
37
+ // `!isLoading` branch — so `previewHtml` is always a string
38
+ // here. TypeScript can't follow that indirect invariant
39
+ // through the derived `isLoading` boolean, hence the `!`.
40
+ html: previewHtml, ariaLabel: previewAriaLabel, onScroll: handlePreviewScroll, activatePane: activatePane, onPreviewClick: onPreviewClick })) : null] })) }));
41
+ }
42
+ function MarkdownModeTabs({ mode, onModeChange, sourceLabel, splitLabel, previewLabel, modeAriaLabel, }) {
43
+ const t = useT();
44
+ return (_jsx(SegmentedToggle, { className: "markdown-mode-tabs", ariaLabel: modeAriaLabel ?? t('Markdown view mode'), value: mode, onChange: onModeChange, options: [
45
+ { value: 'source', label: sourceLabel ?? t('Source') },
46
+ { value: 'split', label: splitLabel ?? t('Split') },
47
+ { value: 'preview', label: previewLabel ?? t('Preview') },
48
+ ] }));
49
+ }
50
+ function MarkdownEditorPane({ editorRef, sourceText, onSourceChange, ariaLabel, onScroll, activatePane, textareaProps, }) {
51
+ const t = useT();
52
+ const label = ariaLabel ?? t('Markdown editor');
53
+ return (_jsx("section", { className: "markdown-editor-pane", "aria-label": label, children: _jsx("textarea", { ref: editorRef, className: "markdown-editor", value: sourceText, "aria-label": label, spellCheck: true, onFocus: () => activatePane('editor'), onChange: (event) => {
54
+ activatePane('editor');
55
+ onSourceChange(event.currentTarget.value);
56
+ }, onScroll: onScroll, ...textareaProps }) }));
57
+ }
58
+ function MarkdownPreviewPane({ previewRef, html, ariaLabel, onScroll, activatePane, onPreviewClick, }) {
59
+ const t = useT();
60
+ const activatePreview = () => activatePane('preview');
61
+ return (_jsx("div", { className: "markdown-preview-pane-wrap", children: _jsx("section", { ref: previewRef, className: "markdown-preview-pane", "aria-label": ariaLabel ?? t('Markdown preview'), onPointerDown: activatePreview, onWheel: activatePreview, onTouchStart: activatePreview, onKeyDown: activatePreview, onFocus: activatePreview, onScroll: onScroll, children: _jsx("article", { className: "markdown-rendered", onClick: onPreviewClick, dangerouslySetInnerHTML: { __html: html } }) }) }));
42
62
  }
43
63
  //# sourceMappingURL=MarkdownSplitPane.js.map
@@ -26,31 +26,22 @@ export function useCommentReorder(orderedIds, onReorder) {
26
26
  return;
27
27
  event.preventDefault();
28
28
  event.dataTransfer.dropEffect = 'move';
29
- if (draggingId === targetId) {
30
- if (dragState?.overId !== targetId || dragState.edge !== null) {
31
- setDragState({ draggingId, overId: targetId, edge: null });
32
- }
33
- return;
34
- }
35
- const edge = dropEdgeForClientY(event.clientY, event.currentTarget.getBoundingClientRect());
36
- if (dragState?.draggingId !== draggingId || dragState.overId !== targetId || dragState.edge !== edge) {
37
- setDragState({ draggingId, overId: targetId, edge });
38
- }
29
+ // Hovering back over the dragged item itself shows no drop edge.
30
+ const edge = draggingId === targetId ? null : pointerDropEdge(event);
31
+ const next = { draggingId, overId: targetId, edge };
32
+ if (!isSameDragState(dragState, next))
33
+ setDragState(next);
39
34
  }
40
35
  function onDrop(event, targetId) {
41
36
  if (!canReorder)
42
37
  return;
43
38
  event.preventDefault();
44
- const draggingId = dragState?.draggingId ||
45
- event.dataTransfer.getData(COMMENT_SIDE_DRAG_MIME) ||
46
- event.dataTransfer.getData('text/plain');
39
+ const draggingId = dragState?.draggingId || draggingIdFromTransfer(event.dataTransfer);
47
40
  if (!draggingId || draggingId === targetId) {
48
41
  setDragState(null);
49
42
  return;
50
43
  }
51
- const edge = dragState?.overId === targetId && dragState.edge
52
- ? dragState.edge
53
- : dropEdgeForClientY(event.clientY, event.currentTarget.getBoundingClientRect());
44
+ const edge = cachedDropEdge(dragState, targetId) ?? pointerDropEdge(event);
54
45
  const nextIds = reorderCommentIds(orderedIds, draggingId, targetId, edge);
55
46
  if (nextIds.join('\0') !== orderedIds.join('\0')) {
56
47
  onReorder?.(nextIds);
@@ -68,4 +59,19 @@ export function useCommentReorder(orderedIds, onReorder) {
68
59
  }
69
60
  return { dragState, canReorder, onDragStart, onDragOver, onDrop, onDragLeaveContainer, clear };
70
61
  }
62
+ function pointerDropEdge(event) {
63
+ return dropEdgeForClientY(event.clientY, event.currentTarget.getBoundingClientRect());
64
+ }
65
+ /** Dragging id carried by the DOM payload: the comment mime type, then the
66
+ * `text/plain` copy `onDragStart` also writes. */
67
+ function draggingIdFromTransfer(dataTransfer) {
68
+ return dataTransfer.getData(COMMENT_SIDE_DRAG_MIME) || dataTransfer.getData('text/plain');
69
+ }
70
+ /** The edge already shown while hovering `targetId`, if any. */
71
+ function cachedDropEdge(dragState, targetId) {
72
+ return dragState?.overId === targetId ? dragState.edge : null;
73
+ }
74
+ function isSameDragState(current, next) {
75
+ return current?.draggingId === next.draggingId && current.overId === next.overId && current.edge === next.edge;
76
+ }
71
77
  //# sourceMappingURL=useCommentReorder.js.map
@@ -89,27 +89,10 @@ export function visibleSelectedCommentIds(comments, selectedIds) {
89
89
  * `number`. When true, callers should display the original text verbatim
90
90
  * instead of a "prettified" re-stringify. */
91
91
  export function hasPrecisionSensitiveJsonNumberText(text) {
92
- let inString = false;
93
- let escaped = false;
94
92
  const numberTokenPattern = /-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?/y;
95
93
  for (let i = 0; i < text.length;) {
96
- const char = text[i];
97
- if (inString) {
98
- if (escaped) {
99
- escaped = false;
100
- }
101
- else if (char === '\\') {
102
- escaped = true;
103
- }
104
- else if (char === '"') {
105
- inString = false;
106
- }
107
- i += 1;
108
- continue;
109
- }
110
- if (char === '"') {
111
- inString = true;
112
- i += 1;
94
+ if (text[i] === '"') {
95
+ i = indexAfterJsonString(text, i + 1);
113
96
  continue;
114
97
  }
115
98
  numberTokenPattern.lastIndex = i;
@@ -118,15 +101,28 @@ export function hasPrecisionSensitiveJsonNumberText(text) {
118
101
  i += 1;
119
102
  continue;
120
103
  }
121
- const token = match[0];
122
- if (isSignedNegativeZeroJsonNumberToken(token))
123
- return true;
124
- if (/[.eE]/.test(token) && isPrecisionSensitiveJsonNumberToken(token))
104
+ if (isPrecisionSensitiveNumberLiteral(match[0]))
125
105
  return true;
126
106
  i = numberTokenPattern.lastIndex;
127
107
  }
128
108
  return false;
129
109
  }
110
+ /** Index just past the closing quote of the JSON string whose body starts at
111
+ * `start` (honouring `\` escapes), or `text.length` when it is unterminated. */
112
+ function indexAfterJsonString(text, start) {
113
+ for (let i = start; i < text.length; i += 1) {
114
+ if (text[i] === '\\')
115
+ i += 1;
116
+ else if (text[i] === '"')
117
+ return i + 1;
118
+ }
119
+ return text.length;
120
+ }
121
+ function isPrecisionSensitiveNumberLiteral(token) {
122
+ if (isSignedNegativeZeroJsonNumberToken(token))
123
+ return true;
124
+ return /[.eE]/.test(token) && isPrecisionSensitiveJsonNumberToken(token);
125
+ }
130
126
  function isSignedNegativeZeroJsonNumberToken(token) {
131
127
  return /^-0(?:\.0+)?(?:[eE][+-]?\d+)?$/.test(token);
132
128
  }