@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,128 @@
1
+ import { useEffect, useRef } from "react";
2
+ /**
3
+ * Lifecycle for the shared `Lightbox`: native `<dialog>` open/close, focus capture and return,
4
+ * arrow-key navigation, Escape and backdrop dismissal. Absorbs the open/close and focus logic the
5
+ * media lightboxes and `ImagePreviewModal` each carried, so every caller gets the same behavior.
6
+ */
7
+ /** Idempotent native open. `showModal()` throws on an already-open dialog in real browsers; the
8
+ * attribute fallback covers DOM environments without it (jsdom). */
9
+ export function openLightboxDialog(dialog) {
10
+ if (typeof dialog.showModal === "function") {
11
+ if (!dialog.open)
12
+ dialog.showModal();
13
+ return;
14
+ }
15
+ dialog.setAttribute("open", "");
16
+ }
17
+ /** The close-side mirror of {@link openLightboxDialog} — same fallback and already-closed guard. */
18
+ export function closeLightboxDialog(dialog) {
19
+ if (typeof dialog.close === "function") {
20
+ if (dialog.open)
21
+ dialog.close();
22
+ return;
23
+ }
24
+ dialog.removeAttribute("open");
25
+ }
26
+ /** Marks the stage's empty area: the dialog fills the viewport, so "backdrop" is any click that
27
+ * lands on the dialog itself or the stage around the media rather than on a control or the media. */
28
+ export const LIGHTBOX_DISMISS_ATTR = "data-jini-lightbox-dismiss";
29
+ function isDismissTarget(target) {
30
+ return target instanceof Element && (target.tagName === "DIALOG" || target.hasAttribute(LIGHTBOX_DISMISS_ATTR));
31
+ }
32
+ function navState(input) {
33
+ const count = input.count ?? 0;
34
+ const index = input.index ?? 0;
35
+ const navigable = count > 1 && typeof input.onNavigate === "function";
36
+ return { count, index, navigable, hasPrev: navigable && index > 0, hasNext: navigable && index + 1 < count };
37
+ }
38
+ export function useLightbox(input) {
39
+ const dialogRef = useRef(null);
40
+ const closeRef = useRef(null);
41
+ const prevRef = useRef(null);
42
+ const nextRef = useRef(null);
43
+ // Captured when the dialog opens, before focus moves inside: by construction the element that
44
+ // opened it (a card's expand button). Restored on close so keyboard users land where they were.
45
+ const triggerRef = useRef(null);
46
+ const pendingFocusRef = useRef(null);
47
+ const nav = navState(input);
48
+ useEffect(() => {
49
+ pendingFocusRef.current?.current?.focus({ preventScroll: true });
50
+ pendingFocusRef.current = null;
51
+ }, [input.index]);
52
+ useEffect(() => {
53
+ const dialog = dialogRef.current;
54
+ if (!dialog)
55
+ return;
56
+ if (input.open) {
57
+ triggerRef.current = dialog.ownerDocument.activeElement;
58
+ openLightboxDialog(dialog);
59
+ // Close exists at every position (a single item has no arrows), so it is the stable initial
60
+ // focus target. Only opening focuses it; navigation preserves the operator's current focus.
61
+ closeRef.current?.focus({ preventScroll: true });
62
+ return;
63
+ }
64
+ closeLightboxDialog(dialog);
65
+ const trigger = triggerRef.current;
66
+ trigger?.focus?.({ preventScroll: true });
67
+ // Keyed on `open`, not `index`: navigating while open must not reopen the dialog (showModal on
68
+ // an open dialog throws) or recapture its trigger from inside the lightbox.
69
+ // eslint-disable-next-line react-hooks/exhaustive-deps
70
+ }, [input.open]);
71
+ function go(target) {
72
+ // Clamps rather than wrapping: staying on the last asset is the less surprising default for an
73
+ // operator navigating a specific set of uploads, not a slideshow.
74
+ if (!nav.navigable || target < 0 || target >= nav.count)
75
+ return;
76
+ // An arrow becomes disabled at its end, and a disabled focused button drops focus out of the
77
+ // dialog. Hand focus to its neighbour first; interior moves leave focus where the operator put it.
78
+ // The neighbour may itself be disabled until the caller re-renders, so the move is applied
79
+ // after the index changes.
80
+ const active = dialogRef.current?.ownerDocument.activeElement;
81
+ if (target === 0 && active === prevRef.current)
82
+ pendingFocusRef.current = nextRef;
83
+ if (target === nav.count - 1 && active === nextRef.current)
84
+ pendingFocusRef.current = prevRef;
85
+ input.onNavigate?.(target);
86
+ }
87
+ // Listens on the document rather than the dialog: when the focused media is swapped out (an
88
+ // image that failed over to a video) focus drops to the body, and arrow keys must keep working.
89
+ useEffect(() => {
90
+ const dialog = dialogRef.current;
91
+ if (!dialog || !input.open)
92
+ return;
93
+ const onKey = (event) => {
94
+ if (event.key !== "ArrowLeft" && event.key !== "ArrowRight")
95
+ return;
96
+ // Known limitation: a focused `<video controls>` also seeks on these keys natively, so the
97
+ // press both seeks and navigates. Callers know their media; the shell does not.
98
+ event.preventDefault();
99
+ go(nav.index + (event.key === "ArrowRight" ? 1 : -1));
100
+ };
101
+ dialog.ownerDocument.addEventListener("keydown", onKey);
102
+ return () => dialog.ownerDocument.removeEventListener("keydown", onKey);
103
+ // eslint-disable-next-line react-hooks/exhaustive-deps
104
+ }, [input.open, nav.index, nav.count, input.onNavigate]);
105
+ return {
106
+ dialogRef,
107
+ closeRef,
108
+ prevRef,
109
+ nextRef,
110
+ navigable: nav.navigable,
111
+ hasPrev: nav.hasPrev,
112
+ hasNext: nav.hasNext,
113
+ counter: nav.count > 1 ? `${nav.index + 1} / ${nav.count}` : "",
114
+ previous: () => go(nav.index - 1),
115
+ next: () => go(nav.index + 1),
116
+ // Always prevented so the `open` prop stays the single source of truth; Escape routes through
117
+ // onClose and the caller's state change is what actually closes the dialog.
118
+ handleCancel: (event) => {
119
+ event.preventDefault();
120
+ input.onClose();
121
+ },
122
+ handleClick: (event) => {
123
+ if (isDismissTarget(event.target))
124
+ input.onClose();
125
+ },
126
+ };
127
+ }
128
+ //# sourceMappingURL=Lightbox.hooks.js.map
@@ -0,0 +1,18 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useId } from "react";
3
+ import { Icon } from "../../../../react/components/Icon.js";
4
+ import { LIGHTBOX_DISMISS_ATTR, useLightbox } from "./Lightbox.hooks.js";
5
+ const DEFAULT_LABELS = { close: "Close preview", previous: "Previous", next: "Next" };
6
+ // Native dialog supplies the focus trap, Escape and top-layer stacking, so there is no hand-built
7
+ // overlay or z-index policy. The dialog itself fills the viewport as the dark gray stage (rather
8
+ // than a card over a dim backdrop) so the media can take nearly all of the screen.
9
+ /** Full-viewport media lightbox: title and counter top-left, close top-right, the media as large
10
+ * as fits, caller actions bottom-left and the previous/next pair together bottom-right. */
11
+ export function Lightbox(props) {
12
+ const { open, title, children, actions, className, partAttrs = {}, useLightboxHook = useLightbox } = props;
13
+ const labels = { ...DEFAULT_LABELS, ...props.labels };
14
+ const titleId = useId();
15
+ const vm = useLightboxHook({ open, onClose: props.onClose, index: props.index, count: props.count, onNavigate: props.onNavigate });
16
+ return (_jsx("dialog", { ref: vm.dialogRef, className: className ? `jini-lightbox ${className}` : "jini-lightbox", "aria-labelledby": open ? titleId : undefined, onCancel: vm.handleCancel, onClick: vm.handleClick, ...partAttrs.dialog, children: open ? (_jsxs(_Fragment, { children: [_jsxs("header", { className: "jini-lightbox-header", children: [_jsx("h2", { id: titleId, className: "jini-lightbox-title", title: title, children: title }), vm.counter ? _jsx("span", { className: "jini-lightbox-counter", children: vm.counter }) : null, _jsx("button", { type: "button", ref: vm.closeRef, className: "jini-lightbox-button jini-lightbox-close", "aria-label": labels.close, onClick: props.onClose, ...partAttrs.close, children: _jsx(Icon, { name: "close", size: 16, strokeWidth: 1.8 }) })] }), _jsx("div", { className: "jini-lightbox-stage", [LIGHTBOX_DISMISS_ATTR]: "", children: children }), actions || vm.navigable ? (_jsxs("footer", { className: "jini-lightbox-footer", children: [_jsx("div", { className: "jini-lightbox-actions", children: actions }), vm.navigable ? (_jsxs("div", { className: "jini-lightbox-nav", children: [_jsx("button", { type: "button", ref: vm.prevRef, className: "jini-lightbox-button", "aria-label": labels.previous, disabled: !vm.hasPrev, onClick: vm.previous, ...partAttrs.previous, children: _jsx(Icon, { name: "chevron-left", size: 18, strokeWidth: 1.8 }) }), _jsx("button", { type: "button", ref: vm.nextRef, className: "jini-lightbox-button", "aria-label": labels.next, disabled: !vm.hasNext, onClick: vm.next, ...partAttrs.next, children: _jsx(Icon, { name: "chevron-right", size: 18, strokeWidth: 1.8 }) })] })) : null] })) : null] })) : null }));
17
+ }
18
+ //# sourceMappingURL=Lightbox.js.map
@@ -0,0 +1,14 @@
1
+ export interface MiddleTruncateProps {
2
+ /** The full text, e.g. a file name. */
3
+ text: string;
4
+ /** Stem characters kept beside the extension; see `splitMiddleTruncation`. */
5
+ keepChars?: number | undefined;
6
+ className?: string | undefined;
7
+ }
8
+ /**
9
+ * One line of text that ellipsizes in the middle, keeping the end (a file's extension) visible.
10
+ * The two halves are separate boxes, so a screen reader may read them as two pieces: name the
11
+ * enclosing control with the full text (`aria-label`) rather than relying on this content.
12
+ */
13
+ export declare function MiddleTruncate({ text, keepChars, className }: MiddleTruncateProps): import("react").JSX.Element;
14
+ //# sourceMappingURL=MiddleTruncate.d.ts.map
@@ -0,0 +1,12 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { splitMiddleTruncation } from "./MiddleTruncate.rules.js";
3
+ /**
4
+ * One line of text that ellipsizes in the middle, keeping the end (a file's extension) visible.
5
+ * The two halves are separate boxes, so a screen reader may read them as two pieces: name the
6
+ * enclosing control with the full text (`aria-label`) rather than relying on this content.
7
+ */
8
+ export function MiddleTruncate({ text, keepChars, className }) {
9
+ const { head, tail } = splitMiddleTruncation({ text }, keepChars === undefined ? {} : { keepChars });
10
+ return (_jsxs("span", { className: className ? `jini-middle-truncate ${className}` : "jini-middle-truncate", children: [_jsx("span", { className: "jini-middle-truncate__head", children: head }), tail ? _jsx("span", { className: "jini-middle-truncate__tail", children: tail }) : null] }));
11
+ }
12
+ //# sourceMappingURL=MiddleTruncate.js.map
@@ -0,0 +1,20 @@
1
+ export interface MiddleTruncationParts {
2
+ /** The part that shrinks with an ellipsis when space runs out. */
3
+ head: string;
4
+ /** The part that always stays visible: the extension plus a few characters before it. */
5
+ tail: string;
6
+ }
7
+ /**
8
+ * Splits a name so CSS can cut it in the MIDDLE: `head` ellipsizes, `tail` never shrinks, so
9
+ * `woodnest-cabin-booking.mp4` narrows to `woodnest-cabin-bo…ing.mp4` and the extension stays
10
+ * readable. CSS does the measuring, so the same split works at any card width.
11
+ * @param required.text The full name.
12
+ * @param optional.keepChars Characters of the stem kept beside the extension.
13
+ * @complexity O(n) in the name's length.
14
+ */
15
+ export declare function splitMiddleTruncation({ text }: {
16
+ text: string;
17
+ }, { keepChars }?: {
18
+ keepChars?: number;
19
+ }): MiddleTruncationParts;
20
+ //# sourceMappingURL=MiddleTruncate.rules.d.ts.map
@@ -0,0 +1,21 @@
1
+ /** Longest trailing ".ext" still treated as an extension; a longer dotted tail is just text. */
2
+ const MAX_EXTENSION_CHARS = 10;
3
+ /**
4
+ * Splits a name so CSS can cut it in the MIDDLE: `head` ellipsizes, `tail` never shrinks, so
5
+ * `woodnest-cabin-booking.mp4` narrows to `woodnest-cabin-bo…ing.mp4` and the extension stays
6
+ * readable. CSS does the measuring, so the same split works at any card width.
7
+ * @param required.text The full name.
8
+ * @param optional.keepChars Characters of the stem kept beside the extension.
9
+ * @complexity O(n) in the name's length.
10
+ */
11
+ export function splitMiddleTruncation({ text }, { keepChars = 3 } = {}) {
12
+ const dot = text.lastIndexOf(".");
13
+ // A leading dot (".env") is a hidden-file name, not an extension.
14
+ const extensionChars = dot > 0 && text.length - dot <= MAX_EXTENSION_CHARS ? text.length - dot : 0;
15
+ const tailChars = extensionChars + keepChars;
16
+ // No extension to protect, or a name too short to cut: one piece that ellipsizes at its end.
17
+ if (extensionChars === 0 || text.length <= tailChars + 1)
18
+ return { head: text, tail: "" };
19
+ return { head: text.slice(0, text.length - tailChars), tail: text.slice(text.length - tailChars) };
20
+ }
21
+ //# sourceMappingURL=MiddleTruncate.rules.js.map
@@ -0,0 +1,18 @@
1
+ import { useScrollJump } from "./ScrollJump.hooks.js";
2
+ export interface ScrollJumpButtonsProps {
3
+ /** The scrolling element the buttons control (an admin shell's `<main>`); `null` renders nothing. */
4
+ container: HTMLElement | null;
5
+ /** Accessible names, already translated by the host. */
6
+ labels?: {
7
+ toTop: string;
8
+ toBottom: string;
9
+ } | undefined;
10
+ useJump?: typeof useScrollJump | undefined;
11
+ }
12
+ /**
13
+ * A quiet ↑/↓ pair for long pages. Hidden on short pages; ↑ hides at the top and ↓ at the bottom.
14
+ * Position it with the host's CSS (`--jini-scroll-jump-right`/`-bottom`/`-direction`) so it
15
+ * clears that host's own floating controls.
16
+ */
17
+ export declare function ScrollJumpButtons({ container, labels, useJump }: ScrollJumpButtonsProps): import("react").JSX.Element | null;
18
+ //# sourceMappingURL=ScrollJump.d.ts.map
@@ -0,0 +1,16 @@
1
+ import { type ScrollJumpVisibility } from "./ScrollJump.rules.js";
2
+ export interface ScrollJumpController extends ScrollJumpVisibility {
3
+ jump: (edge: "top" | "bottom") => void;
4
+ }
5
+ /**
6
+ * Tracks whether `container` is tall enough for jump buttons and which edges it is away from.
7
+ * Scroll events alone miss content that loads or grows without scrolling (a media grid filling in),
8
+ * so a ResizeObserver watches the container and each direct child, and a MutationObserver
9
+ * re-observes when a route swaps the children out.
10
+ */
11
+ export declare function useScrollJump({ container }: {
12
+ container: HTMLElement | null;
13
+ }, { reducedMotion }?: {
14
+ reducedMotion?: () => boolean;
15
+ }): ScrollJumpController;
16
+ //# sourceMappingURL=ScrollJump.hooks.d.ts.map
@@ -0,0 +1,45 @@
1
+ import { useCallback, useEffect, useState } from "react";
2
+ import { scrollJumpBehavior, scrollJumpTarget, scrollJumpVisibility } from "./ScrollJump.rules.js";
3
+ const HIDDEN = { showTop: false, showBottom: false };
4
+ const REDUCED_MOTION_QUERY = "(prefers-reduced-motion: reduce)";
5
+ const prefersReducedMotion = () => typeof window !== "undefined" && (window.matchMedia?.(REDUCED_MOTION_QUERY).matches ?? false);
6
+ /**
7
+ * Tracks whether `container` is tall enough for jump buttons and which edges it is away from.
8
+ * Scroll events alone miss content that loads or grows without scrolling (a media grid filling in),
9
+ * so a ResizeObserver watches the container and each direct child, and a MutationObserver
10
+ * re-observes when a route swaps the children out.
11
+ */
12
+ export function useScrollJump({ container }, { reducedMotion = prefersReducedMotion } = {}) {
13
+ const [visibility, setVisibility] = useState(HIDDEN);
14
+ useEffect(() => {
15
+ if (!container)
16
+ return;
17
+ // Same-value updates keep the previous object so a scroll that changes nothing does not re-render.
18
+ const update = () => setVisibility((previous) => {
19
+ const next = scrollJumpVisibility(container);
20
+ return next.showTop === previous.showTop && next.showBottom === previous.showBottom ? previous : next;
21
+ });
22
+ update();
23
+ container.addEventListener("scroll", update, { passive: true });
24
+ const resize = typeof ResizeObserver === "function" ? new ResizeObserver(update) : null;
25
+ const observeChildren = () => {
26
+ resize?.disconnect();
27
+ resize?.observe(container);
28
+ for (const child of Array.from(container.children))
29
+ resize?.observe(child);
30
+ };
31
+ observeChildren();
32
+ const mutation = typeof MutationObserver === "function" ? new MutationObserver(() => { observeChildren(); update(); }) : null;
33
+ mutation?.observe(container, { childList: true });
34
+ return () => {
35
+ container.removeEventListener("scroll", update);
36
+ resize?.disconnect();
37
+ mutation?.disconnect();
38
+ };
39
+ }, [container]);
40
+ const jump = useCallback((edge) => {
41
+ container?.scrollTo({ top: scrollJumpTarget({ edge, scrollHeight: container.scrollHeight }), behavior: scrollJumpBehavior({ reducedMotion: reducedMotion() }) });
42
+ }, [container, reducedMotion]);
43
+ return { ...(container ? visibility : HIDDEN), jump };
44
+ }
45
+ //# sourceMappingURL=ScrollJump.hooks.js.map
@@ -0,0 +1,18 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useScrollJump } from "./ScrollJump.hooks.js";
3
+ const DEFAULT_LABELS = { toTop: "Scroll to top", toBottom: "Scroll to bottom" };
4
+ /** An edge button that does not apply keeps its slot (CSS `visibility: hidden`, which also drops it
5
+ * from the tab order), so the other button never slides under the pointer that just clicked. */
6
+ const offProps = (show) => (show ? {} : { "data-off": "" });
7
+ /**
8
+ * A quiet ↑/↓ pair for long pages. Hidden on short pages; ↑ hides at the top and ↓ at the bottom.
9
+ * Position it with the host's CSS (`--jini-scroll-jump-right`/`-bottom`/`-direction`) so it
10
+ * clears that host's own floating controls.
11
+ */
12
+ export function ScrollJumpButtons({ container, labels = DEFAULT_LABELS, useJump = useScrollJump }) {
13
+ const { showTop, showBottom, jump } = useJump({ container });
14
+ if (!showTop && !showBottom)
15
+ return null;
16
+ return (_jsxs("div", { className: "jini-scroll-jump", children: [_jsx("button", { type: "button", className: "jini-scroll-jump__btn", ...offProps(showTop), "aria-label": labels.toTop, title: labels.toTop, onClick: () => jump("top"), children: _jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: _jsx("path", { d: "m18 15-6-6-6 6" }) }) }), _jsx("button", { type: "button", className: "jini-scroll-jump__btn", ...offProps(showBottom), "aria-label": labels.toBottom, title: labels.toBottom, onClick: () => jump("bottom"), children: _jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: _jsx("path", { d: "m6 9 6 6 6-6" }) }) })] }));
17
+ }
18
+ //# sourceMappingURL=ScrollJump.js.map
@@ -0,0 +1,32 @@
1
+ /** A page must be at least this many viewports tall before the jump buttons are worth their space. */
2
+ export declare const SCROLL_JUMP_MIN_VIEWPORTS = 2;
3
+ /** Within this many px of an edge counts as AT that edge (fractional scroll positions never hit 0 exactly). */
4
+ export declare const SCROLL_JUMP_EDGE_SLACK_PX = 8;
5
+ export interface ScrollMetrics {
6
+ scrollTop: number;
7
+ scrollHeight: number;
8
+ clientHeight: number;
9
+ }
10
+ export interface ScrollJumpVisibility {
11
+ showTop: boolean;
12
+ showBottom: boolean;
13
+ }
14
+ /**
15
+ * Which jump buttons a scroll container shows: none on a short page, ↑ only once scrolled away from
16
+ * the top, ↓ only while there is more below.
17
+ * @complexity O(1).
18
+ */
19
+ export declare function scrollJumpVisibility({ scrollTop, scrollHeight, clientHeight }: ScrollMetrics, { minViewports, edgeSlackPx }?: {
20
+ minViewports?: number;
21
+ edgeSlackPx?: number;
22
+ }): ScrollJumpVisibility;
23
+ /** Reduced-motion users get an instant jump instead of a long animated scroll. */
24
+ export declare function scrollJumpBehavior({ reducedMotion }: {
25
+ reducedMotion: boolean;
26
+ }): ScrollBehavior;
27
+ /** The `scrollTo` target for an edge: 0 for the top, the full content height for the bottom. */
28
+ export declare function scrollJumpTarget({ edge, scrollHeight }: {
29
+ edge: "top" | "bottom";
30
+ scrollHeight: number;
31
+ }): number;
32
+ //# sourceMappingURL=ScrollJump.rules.d.ts.map
@@ -0,0 +1,26 @@
1
+ /** A page must be at least this many viewports tall before the jump buttons are worth their space. */
2
+ export const SCROLL_JUMP_MIN_VIEWPORTS = 2;
3
+ /** Within this many px of an edge counts as AT that edge (fractional scroll positions never hit 0 exactly). */
4
+ export const SCROLL_JUMP_EDGE_SLACK_PX = 8;
5
+ /**
6
+ * Which jump buttons a scroll container shows: none on a short page, ↑ only once scrolled away from
7
+ * the top, ↓ only while there is more below.
8
+ * @complexity O(1).
9
+ */
10
+ export function scrollJumpVisibility({ scrollTop, scrollHeight, clientHeight }, { minViewports = SCROLL_JUMP_MIN_VIEWPORTS, edgeSlackPx = SCROLL_JUMP_EDGE_SLACK_PX } = {}) {
11
+ if (clientHeight <= 0 || scrollHeight < clientHeight * minViewports)
12
+ return { showTop: false, showBottom: false };
13
+ return {
14
+ showTop: scrollTop > edgeSlackPx,
15
+ showBottom: scrollHeight - scrollTop - clientHeight > edgeSlackPx,
16
+ };
17
+ }
18
+ /** Reduced-motion users get an instant jump instead of a long animated scroll. */
19
+ export function scrollJumpBehavior({ reducedMotion }) {
20
+ return reducedMotion ? "auto" : "smooth";
21
+ }
22
+ /** The `scrollTo` target for an edge: 0 for the top, the full content height for the bottom. */
23
+ export function scrollJumpTarget({ edge, scrollHeight }) {
24
+ return edge === "top" ? 0 : scrollHeight;
25
+ }
26
+ //# sourceMappingURL=ScrollJump.rules.js.map
@@ -13,6 +13,13 @@ export function useSuggestInput({ value, suggestions }, { limit } = {}) {
13
13
  const matches = matchSuggestions({ typed: value, suggestions }, { limit });
14
14
  const visible = open && matches.length > 0;
15
15
  const current = highlighted < matches.length ? highlighted : -1;
16
+ const close = () => { setOpen(false); setHighlighted(-1); };
17
+ const move = (step) => {
18
+ setOpen(true);
19
+ const next = nextHighlight({ current, step, count: matches.length });
20
+ setHighlighted(next);
21
+ setScrollTarget({ index: next });
22
+ };
16
23
  return {
17
24
  open: visible,
18
25
  matches,
@@ -20,33 +27,41 @@ export function useSuggestInput({ value, suggestions }, { limit } = {}) {
20
27
  setHighlighted,
21
28
  scrollTarget,
22
29
  openList: () => setOpen(true),
23
- closeList: () => { setOpen(false); setHighlighted(-1); },
30
+ closeList: close,
24
31
  handleKey: (key) => {
25
- if (key === 'ArrowDown' || key === 'ArrowUp') {
32
+ const step = arrowStep(key);
33
+ if (step !== undefined) {
26
34
  if (matches.length === 0)
27
35
  return { consumed: false };
28
- setOpen(true);
29
- const step = key === 'ArrowDown' ? 1 : -1;
30
- const next = (current + step + matches.length + (current === -1 && step === -1 ? 1 : 0)) % matches.length;
31
- setHighlighted(next);
32
- setScrollTarget({ index: next });
36
+ move(step);
33
37
  return { consumed: true };
34
38
  }
35
39
  if (key === 'Escape' && visible) {
36
- setOpen(false);
37
- setHighlighted(-1);
40
+ close();
38
41
  return { consumed: true };
39
42
  }
40
- const picked = current >= 0 ? matches[current] : undefined;
41
- if (key === 'Enter' && visible && picked !== undefined) {
42
- setOpen(false);
43
- setHighlighted(-1);
44
- return { consumed: true, picked };
43
+ if (key === 'Enter' && visible && current >= 0) {
44
+ close();
45
+ return { consumed: true, picked: matches[current] };
45
46
  }
46
47
  return { consumed: false };
47
48
  },
48
49
  };
49
50
  }
51
+ /** +1 for ArrowDown, -1 for ArrowUp, undefined for every other key. */
52
+ function arrowStep(key) {
53
+ if (key === 'ArrowDown')
54
+ return 1;
55
+ if (key === 'ArrowUp')
56
+ return -1;
57
+ return undefined;
58
+ }
59
+ /** The row an arrow key moves to, wrapping at either end; from no highlight, ArrowUp lands on the last row. */
60
+ function nextHighlight({ current, step, count }) {
61
+ if (current === -1)
62
+ return step === 1 ? 0 : count - 1;
63
+ return (current + step + count) % count;
64
+ }
50
65
  /** Default scroller: `block: 'nearest'` moves the list only as far as needed. Optional call because
51
66
  * non-browser DOMs (jsdom in host test suites) don't implement `scrollIntoView`. */
52
67
  export function scrollOptionIntoView(option) {
@@ -14,8 +14,10 @@ export type { SeeMoreProps, SeeMoreView } from './components/SeeMore/SeeMore.js'
14
14
  export { useSeeMoreClamp } from './components/SeeMore/SeeMore.hooks.js';
15
15
  export { ImagePreviewModal } from './components/ImagePreviewModal.js';
16
16
  export type { ImagePreviewModalProps } from './components/ImagePreviewModal.js';
17
- export { useImagePreviewModal } from './components/ImagePreviewModal.hooks.js';
18
- export type { ImagePreviewModalController } from './components/ImagePreviewModal.hooks.js';
17
+ export { Lightbox } from './components/Lightbox/Lightbox.js';
18
+ export type { LightboxProps } from './components/Lightbox/Lightbox.js';
19
+ export { useLightbox, openLightboxDialog, closeLightboxDialog } from './components/Lightbox/Lightbox.hooks.js';
20
+ export type { LightboxController, LightboxHookInput } from './components/Lightbox/Lightbox.hooks.js';
19
21
  export { ComingSoonPanel } from './components/ComingSoonPanel.js';
20
22
  export type { ComingSoonPanelProps } from './components/ComingSoonPanel.js';
21
23
  export { ComingSoonNotice } from './components/ComingSoonNotice.js';
@@ -26,4 +28,14 @@ export { useSuggestInput, matchSuggestions } from './components/SuggestInput/Sug
26
28
  export type { SuggestInputController } from './components/SuggestInput/SuggestInput.hooks.js';
27
29
  export { useDebouncedHtmlPreview } from './components/useDebouncedHtmlPreview.hooks.js';
28
30
  export type { HtmlPreviewPort, HtmlPreviewState } from './components/useDebouncedHtmlPreview.hooks.js';
31
+ export { MiddleTruncate } from './components/MiddleTruncate.js';
32
+ export type { MiddleTruncateProps } from './components/MiddleTruncate.js';
33
+ export { splitMiddleTruncation } from './components/MiddleTruncate.rules.js';
34
+ export type { MiddleTruncationParts } from './components/MiddleTruncate.rules.js';
35
+ export { ScrollJumpButtons } from './components/ScrollJump/ScrollJump.js';
36
+ export type { ScrollJumpButtonsProps } from './components/ScrollJump/ScrollJump.js';
37
+ export { useScrollJump } from './components/ScrollJump/ScrollJump.hooks.js';
38
+ export type { ScrollJumpController } from './components/ScrollJump/ScrollJump.hooks.js';
39
+ export { scrollJumpVisibility, scrollJumpBehavior, scrollJumpTarget } from './components/ScrollJump/ScrollJump.rules.js';
40
+ export type { ScrollJumpVisibility, ScrollMetrics } from './components/ScrollJump/ScrollJump.rules.js';
29
41
  //# sourceMappingURL=index.d.ts.map
@@ -7,10 +7,16 @@ export { placeInfoTipBubble, infoTipBubbleStyle } from './components/InfoTip.rul
7
7
  export { SeeMore, resolveSeeMoreView } from './components/SeeMore/SeeMore.js';
8
8
  export { useSeeMoreClamp } from './components/SeeMore/SeeMore.hooks.js';
9
9
  export { ImagePreviewModal } from './components/ImagePreviewModal.js';
10
- export { useImagePreviewModal } from './components/ImagePreviewModal.hooks.js';
10
+ export { Lightbox } from './components/Lightbox/Lightbox.js';
11
+ export { useLightbox, openLightboxDialog, closeLightboxDialog } from './components/Lightbox/Lightbox.hooks.js';
11
12
  export { ComingSoonPanel } from './components/ComingSoonPanel.js';
12
13
  export { ComingSoonNotice } from './components/ComingSoonNotice.js';
13
14
  export { SuggestInput } from './components/SuggestInput/SuggestInput.js';
14
15
  export { useSuggestInput, matchSuggestions } from './components/SuggestInput/SuggestInput.hooks.js';
15
16
  export { useDebouncedHtmlPreview } from './components/useDebouncedHtmlPreview.hooks.js';
17
+ export { MiddleTruncate } from './components/MiddleTruncate.js';
18
+ export { splitMiddleTruncation } from './components/MiddleTruncate.rules.js';
19
+ export { ScrollJumpButtons } from './components/ScrollJump/ScrollJump.js';
20
+ export { useScrollJump } from './components/ScrollJump/ScrollJump.hooks.js';
21
+ export { scrollJumpVisibility, scrollJumpBehavior, scrollJumpTarget } from './components/ScrollJump/ScrollJump.rules.js';
16
22
  //# sourceMappingURL=index.js.map
@@ -1,2 +1,3 @@
1
1
  /* Existing ui subpath continues to load the merged admin styles. */
2
2
  @import "../../../styles/admin.css";
3
+ @import "../../../styles/lightbox.css";
@@ -3,9 +3,15 @@ import { useCallback } from 'react';
3
3
  import { useT } from '../../../i18n/index.js';
4
4
  import { useInView } from '../../../../react/hooks/useInView.js';
5
5
  import { resolveCheckboxClickAction, resolvePreviewClickAction } from '../../rules.js';
6
+ function renderCardActions({ asset, renderCardExtra, onDeleteAsset, t }, _options = {}) {
7
+ if (!renderCardExtra && !onDeleteAsset)
8
+ return null;
9
+ return (_jsxs("div", { className: "asset-grid-card-actions", children: [renderCardExtra ? renderCardExtra(asset) : _jsx("span", {}), onDeleteAsset ? (_jsx("button", { type: "button", className: "asset-grid-card-delete-btn", onClick: () => onDeleteAsset(asset.id), children: t('Remove') })) : null] }));
10
+ }
6
11
  export function AssetCard({ asset, index, selected, title, subtitle, kindLabel, sourceLabel, renderThumbnail, renderThumbnailPlaceholder, onToggle, onRange, onPreview, onDeleteAsset, renderCardExtra, }) {
7
12
  const t = useT();
8
13
  const { ref, inView } = useInView({ once: true, rootMargin: '300px' });
14
+ const selectionValue = selected ? 'true' : 'false';
9
15
  const handlePreviewClick = useCallback((e) => {
10
16
  const action = resolvePreviewClickAction(e);
11
17
  if (action === 'toggle')
@@ -22,6 +28,6 @@ export function AssetCard({ asset, index, selected, title, subtitle, kindLabel,
22
28
  else
23
29
  onToggle(asset.id, index);
24
30
  }, [asset.id, index, onToggle, onRange]);
25
- return (_jsxs("figure", { className: "asset-grid-card", "data-asset-grid-id": asset.id, "data-selected": selected ? 'true' : 'false', children: [_jsxs("div", { className: "asset-grid-card-thumb", children: [_jsx("div", { ref: ref, className: "asset-grid-card-thumb-lazy", children: inView ? renderThumbnail(asset) : (renderThumbnailPlaceholder?.(asset) ?? null) }), _jsx("button", { type: "button", className: "asset-grid-card-preview-btn", onClick: handlePreviewClick, "aria-label": t('Preview {title}', { title }), children: _jsx("span", { className: "asset-grid-card-preview-overlay", "aria-hidden": true, children: _jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("circle", { cx: "11", cy: "11", r: "7" }), _jsx("path", { d: "m21 21-4.3-4.3" })] }) }) }), _jsx("button", { type: "button", className: "asset-grid-card-select-check", "data-checked": selected ? 'true' : 'false', "aria-pressed": selected, "aria-label": selected ? t('Deselect asset') : t('Select asset'), onClick: handleCheckboxClick, children: selected ? (_jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "3", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: _jsx("path", { d: "M20 6 9 17l-5-5" }) })) : null }), sourceLabel ? (_jsx("span", { className: "asset-grid-card-badge", "data-badge": "source", children: sourceLabel })) : null, kindLabel ? (_jsx("span", { className: "asset-grid-card-badge", "data-badge": "kind", children: kindLabel })) : null] }), _jsxs("figcaption", { className: "asset-grid-card-meta", children: [_jsx("button", { type: "button", className: "asset-grid-card-title", title: title, onClick: () => onPreview(asset.id), children: title }), subtitle ? _jsx("span", { className: "asset-grid-card-subtitle", children: subtitle }) : null] }), renderCardExtra || onDeleteAsset ? (_jsxs("div", { className: "asset-grid-card-actions", children: [renderCardExtra ? renderCardExtra(asset) : _jsx("span", {}), onDeleteAsset ? (_jsx("button", { type: "button", className: "asset-grid-card-delete-btn", onClick: () => onDeleteAsset(asset.id), children: t('Remove') })) : null] })) : null] }));
31
+ return (_jsxs("figure", { className: "asset-grid-card", "data-asset-grid-id": asset.id, "data-selected": selectionValue, children: [_jsxs("div", { className: "asset-grid-card-thumb", children: [_jsx("div", { ref: ref, className: "asset-grid-card-thumb-lazy", children: inView ? renderThumbnail(asset) : (renderThumbnailPlaceholder?.(asset) ?? null) }), _jsx("button", { type: "button", className: "asset-grid-card-preview-btn", onClick: handlePreviewClick, "aria-label": t('Preview {title}', { title }), children: _jsx("span", { className: "asset-grid-card-preview-overlay", "aria-hidden": true, children: _jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("circle", { cx: "11", cy: "11", r: "7" }), _jsx("path", { d: "m21 21-4.3-4.3" })] }) }) }), _jsx("button", { type: "button", className: "asset-grid-card-select-check", "data-checked": selectionValue, "aria-pressed": selected, "aria-label": selected ? t('Deselect asset') : t('Select asset'), onClick: handleCheckboxClick, children: selected ? (_jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "3", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: _jsx("path", { d: "M20 6 9 17l-5-5" }) })) : null }), sourceLabel ? (_jsx("span", { className: "asset-grid-card-badge", "data-badge": "source", children: sourceLabel })) : null, kindLabel ? (_jsx("span", { className: "asset-grid-card-badge", "data-badge": "kind", children: kindLabel })) : null] }), _jsxs("figcaption", { className: "asset-grid-card-meta", children: [_jsx("button", { type: "button", className: "asset-grid-card-title", title: title, onClick: () => onPreview(asset.id), children: title }), subtitle ? _jsx("span", { className: "asset-grid-card-subtitle", children: subtitle }) : null] }), renderCardActions({ asset, renderCardExtra, onDeleteAsset, t })] }));
26
32
  }
27
33
  //# sourceMappingURL=AssetCard.js.map
@@ -41,5 +41,5 @@ export interface AssetGridProps<TAsset extends AssetGridItem> {
41
41
  * pattern behind a photo library / DAM. See `packages/ui/source-map.md` for
42
42
  * the full retained-behavior manifest against the original `LibrarySection.tsx`.
43
43
  */
44
- export declare function AssetGrid<TAsset extends AssetGridItem>({ active, selectors, dependencies, kindFacets, sourceFacets, renderThumbnail, renderThumbnailPlaceholder, renderCardExtra, renderBulkActions, onDeleteAsset, onDeleteSelected, onPreview, isPreviewOpen, toolbarActions, emptyState, searchPlaceholder, searchDebounceMs, liveUpdateCoalesceMs, initialViewMode, useWiredAssetGridData: useWiredAssetGridDataHook, useAssetGridSelection: useAssetGridSelectionHook, }: AssetGridProps<TAsset>): import("react").JSX.Element;
44
+ export declare function AssetGrid<TAsset extends AssetGridItem>({ active, selectors, dependencies, kindFacets: configuredKindFacets, sourceFacets: configuredSourceFacets, renderThumbnail, renderThumbnailPlaceholder, renderCardExtra, renderBulkActions, onDeleteAsset, onDeleteSelected, onPreview, isPreviewOpen, toolbarActions, emptyState, searchPlaceholder, searchDebounceMs, liveUpdateCoalesceMs, initialViewMode, useWiredAssetGridData: useWiredAssetGridDataHook, useAssetGridSelection: useAssetGridSelectionHook, }: AssetGridProps<TAsset>): import("react").JSX.Element;
45
45
  //# sourceMappingURL=AssetGrid.d.ts.map