@jini-ai/ui 0.5.4 → 0.5.6

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 (270) hide show
  1. package/CHANGELOG.md +10 -0
  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/Select/Select.hooks.d.ts +21 -0
  29. package/dist/features/admin-widgets/components/Select/Select.hooks.js +24 -2
  30. package/dist/features/admin-widgets/components/Select/Select.js +4 -3
  31. package/dist/features/admin-widgets/components/SuggestInput/SuggestInput.hooks.js +29 -14
  32. package/dist/features/admin-widgets/index.d.ts +14 -2
  33. package/dist/features/admin-widgets/index.js +7 -1
  34. package/dist/features/admin-widgets/styles/admin-widgets.css +1 -0
  35. package/dist/features/asset-grid/react/components/AssetCard.js +7 -1
  36. package/dist/features/asset-grid/react/components/AssetGrid.d.ts +1 -1
  37. package/dist/features/asset-grid/react/components/AssetGrid.js +38 -18
  38. package/dist/features/asset-grid/react/components/useAssetGridFilters.d.ts +21 -0
  39. package/dist/features/asset-grid/react/components/useAssetGridFilters.js +19 -0
  40. package/dist/features/asset-grid/react/hooks/useAssetGridKeyboardShortcuts.js +16 -11
  41. package/dist/features/asset-grid/react/hooks/useAssetGridLiveUpdates.d.ts +2 -0
  42. package/dist/features/asset-grid/react/hooks/useAssetGridLiveUpdates.js +35 -11
  43. package/dist/features/asset-tree-browser/react/components/AssetTreeBrowser.d.ts +6 -6
  44. package/dist/features/asset-tree-browser/react/components/AssetTreeBrowser.js +10 -129
  45. package/dist/features/asset-tree-browser/react/components/AssetTreeBrowserParts.d.ts +50 -0
  46. package/dist/features/asset-tree-browser/react/components/AssetTreeBrowserParts.js +95 -0
  47. package/dist/features/asset-tree-browser/react/components/useAssetTreeBrowserController.d.ts +56 -0
  48. package/dist/features/asset-tree-browser/react/components/useAssetTreeBrowserController.js +80 -0
  49. package/dist/features/asset-tree-browser/rules.js +40 -22
  50. package/dist/features/browser-chrome/rules.js +74 -70
  51. package/dist/features/charts/browser.js +11 -5
  52. package/dist/features/charts/react.js +2 -2
  53. package/dist/features/charts/table.js +2 -2
  54. package/dist/features/command-palette/react/hooks/useCommandPalette.js +3 -9
  55. package/dist/features/connectors/ConnectorsBrowser.d.ts +4 -4
  56. package/dist/features/connectors/ConnectorsBrowser.js +26 -14
  57. package/dist/features/connectors/components/ConnectorCard.js +67 -38
  58. package/dist/features/connectors/components/ConnectorDetailDrawer.js +62 -31
  59. package/dist/features/connectors/components/ConnectorGrid.js +11 -6
  60. package/dist/features/connectors/hooks/useConnectorAuthorization.js +6 -0
  61. package/dist/features/connectors/hooks/useConnectorCatalog.d.ts +2 -0
  62. package/dist/features/connectors/hooks/useConnectorCatalog.js +32 -13
  63. package/dist/features/connectors/hooks/useWiredConnectorsBrowser.d.ts +3 -3
  64. package/dist/features/connectors/rules.d.ts +8 -1
  65. package/dist/features/connectors/rules.js +42 -20
  66. package/dist/features/connectors/types.d.ts +11 -0
  67. package/dist/features/execution/constants.d.ts +4 -10
  68. package/dist/features/execution/constants.js +9 -94
  69. package/dist/features/execution/react/components/ByokProviderForm.d.ts +2 -4
  70. package/dist/features/execution/react/components/ByokProviderForm.js +140 -43
  71. package/dist/features/execution/react/components/ExecutionTab.d.ts +1 -2
  72. package/dist/features/execution/react/components/ExecutionTab.js +52 -30
  73. package/dist/features/execution/react/components/LocalCliAgentCard.js +12 -3
  74. package/dist/features/execution/react/components/LocalCliAgentList.js +14 -3
  75. package/dist/features/execution/react/components/LocalCliAgentSettings.js +14 -3
  76. package/dist/features/execution/react/hooks/useLocalCliAgentCard.js +54 -36
  77. package/dist/features/execution/react/hooks/useReasoningControl.d.ts +1 -1
  78. package/dist/features/execution/react/hooks/useReasoningControl.js +116 -75
  79. package/dist/features/execution/rules.d.ts +13 -2
  80. package/dist/features/execution/rules.js +51 -11
  81. package/dist/features/execution/types.d.ts +6 -42
  82. package/dist/features/execution/types.js +0 -17
  83. package/dist/features/external-mcp/react/components/ExternalMcpTab.d.ts +6 -2
  84. package/dist/features/external-mcp/react/components/ExternalMcpTab.js +9 -2
  85. package/dist/features/file-dropzone/react/components/FileDropzone.js +24 -25
  86. package/dist/features/file-dropzone/react/components/FileDropzoneLightbox.d.ts +2 -2
  87. package/dist/features/file-dropzone/react/components/FileDropzoneLightbox.js +35 -30
  88. package/dist/features/file-dropzone/react/components/FileDropzoneThumbnailGrid.js +30 -24
  89. package/dist/features/file-dropzone/react/components/FileDropzoneZone.d.ts +14 -0
  90. package/dist/features/file-dropzone/react/components/FileDropzoneZone.js +26 -0
  91. package/dist/features/html-editor/canvas-content-wrapper.js +13 -13
  92. package/dist/features/html-editor/canvas-embed-placeholders.js +46 -37
  93. package/dist/features/html-editor/css.js +76 -69
  94. package/dist/features/html-editor/editor-blocks.js +47 -33
  95. package/dist/features/html-editor/editor-serialization.js +6 -5
  96. package/dist/features/html-editor/protected-elements.js +39 -28
  97. package/dist/features/html-editor/react/hooks/useInteractiveHtmlEditor.js +4 -4
  98. package/dist/features/html-editor/source-splice.js +51 -30
  99. package/dist/features/i18n/dictionaries/settings-dialog.ja.js +1 -0
  100. package/dist/features/i18n/dictionaries/settings-dialog.zh-CN.js +1 -0
  101. package/dist/features/i18n/dictionaries/settings-dialog.zh-TW.js +1 -0
  102. package/dist/features/i18n/locale.js +20 -16
  103. package/dist/features/iframe-pool/react/dom-sync.js +8 -6
  104. package/dist/features/integrations/react/components/CodexInstallToggleButton.js +19 -4
  105. package/dist/features/integrations/react/components/IntegrationsTab.js +60 -14
  106. package/dist/features/interactive-ui/interactive-ui.css +1 -1
  107. package/dist/features/interactive-ui/providers/native/data-table.manifest.d.ts +6 -6
  108. package/dist/features/interactive-ui/providers/shadcn/data-table.js +10 -7
  109. package/dist/features/interactive-ui/providers/shadcn/data-table.manifest.d.ts +6 -6
  110. package/dist/features/lexical-rich-text-editor/mention-parser.js +60 -57
  111. package/dist/features/lexical-rich-text-editor/react/hooks/useKeyboardCommands.js +33 -38
  112. package/dist/features/lexical-rich-text-editor/rules.js +87 -74
  113. package/dist/features/lexical-rich-text-editor/serialize.js +27 -25
  114. package/dist/features/mcp-ui/secret-card.js +111 -61
  115. package/dist/features/mcp-ui/surfaces/confirmation.js +39 -21
  116. package/dist/features/mcp-ui/surfaces/document.d.ts +1 -1
  117. package/dist/features/mcp-ui/surfaces/document.js +10 -2
  118. package/dist/features/mcp-ui/surfaces/fields.js +30 -50
  119. package/dist/features/mcp-ui/surfaces/form.d.ts +6 -0
  120. package/dist/features/mcp-ui/surfaces/form.js +43 -21
  121. package/dist/features/mcp-ui/surfaces/outcome.js +30 -20
  122. package/dist/features/mcp-ui/surfaces/text-input.js +33 -21
  123. package/dist/features/mcp-ui/surfaces/tokens.d.ts +6 -1
  124. package/dist/features/mcp-ui/surfaces/tokens.js +5 -2
  125. package/dist/features/media-providers/constants.d.ts +1 -0
  126. package/dist/features/media-providers/constants.js +2 -81
  127. package/dist/features/media-providers/index.d.ts +1 -1
  128. package/dist/features/media-providers/ports.d.ts +2 -0
  129. package/dist/features/media-providers/react/components/MediaProviderCard.d.ts +22 -0
  130. package/dist/features/media-providers/react/components/MediaProviderCard.js +50 -0
  131. package/dist/features/media-providers/react/components/MediaProvidersTab.d.ts +5 -6
  132. package/dist/features/media-providers/react/components/MediaProvidersTab.js +40 -72
  133. package/dist/features/media-providers/react/components/media-providers-tab-labels.d.ts +11 -0
  134. package/dist/features/media-providers/react/components/media-providers-tab-labels.js +57 -0
  135. package/dist/features/media-providers/react/hooks/useMediaProvidersTab.d.ts +4 -2
  136. package/dist/features/media-providers/react/hooks/useMediaProvidersTab.js +37 -23
  137. package/dist/features/media-providers/rules.js +17 -10
  138. package/dist/features/media-providers/types.d.ts +4 -22
  139. package/dist/features/media-providers/types.js +3 -8
  140. package/dist/features/memory/formatters.js +127 -92
  141. package/dist/features/memory/react/components/MemoryConnectedPanel.js +92 -36
  142. package/dist/features/memory/react/components/MemoryList.js +37 -18
  143. package/dist/features/memory/react/hooks/useMemoryExtractions.store.js +42 -36
  144. package/dist/features/notifications/react/components/NotificationsTab.js +28 -28
  145. package/dist/features/observability/long-task.d.ts +0 -12
  146. package/dist/features/observability/long-task.js +15 -19
  147. package/dist/features/observability/resource-error.d.ts +0 -10
  148. package/dist/features/observability/resource-error.js +18 -13
  149. package/dist/features/observability/stuck-run.js +10 -6
  150. package/dist/features/observability/telemetry-url.d.ts +6 -0
  151. package/dist/features/observability/telemetry-url.js +16 -0
  152. package/dist/features/observability/white-screen.js +12 -24
  153. package/dist/features/panel-kit/fetch-query/adapter.react.d.ts +2 -2
  154. package/dist/features/panel-kit/fetch-query/adapter.react.js +4 -2
  155. package/dist/features/panel-kit/fetch-query/adapter.tanstack.js +46 -33
  156. package/dist/features/panel-kit/helpers/standing-draft-local-backup.js +3 -1
  157. package/dist/features/panel-kit/hooks/use-settings-slice.hooks.js +3 -1
  158. package/dist/features/privacy/react/components/PrivacyTab.js +24 -13
  159. package/dist/features/progress-card/components/ProgressCard.js +7 -3
  160. package/dist/features/progress-card/reference-adapters.js +59 -54
  161. package/dist/features/project-locations/react/components/ProjectLocationsTab.js +24 -15
  162. package/dist/features/project-locations/react/hooks/useProjectLocationsTab.d.ts +8 -0
  163. package/dist/features/project-locations/react/hooks/useProjectLocationsTab.js +31 -23
  164. package/dist/features/resource-dashboard/react/components/ResourceBoard.js +6 -47
  165. package/dist/features/resource-dashboard/react/components/ResourceBoardView.d.ts +2 -2
  166. package/dist/features/resource-dashboard/react/components/ResourceBoardView.js +3 -6
  167. package/dist/features/resource-dashboard/react/components/ResourceBoardViewParts.d.ts +15 -0
  168. package/dist/features/resource-dashboard/react/components/ResourceBoardViewParts.js +35 -0
  169. package/dist/features/resource-dashboard/react/components/ResourceCard.js +5 -16
  170. package/dist/features/resource-dashboard/react/components/ResourceCardParts.d.ts +34 -0
  171. package/dist/features/resource-dashboard/react/components/ResourceCardParts.js +34 -0
  172. package/dist/features/resource-dashboard/react/components/ResourceRowList.js +3 -1
  173. package/dist/features/resource-dashboard/react/components/ResourceRowListItem.js +2 -2
  174. package/dist/features/resource-dashboard/react/components/ResourceRowListItemParts.d.ts +19 -0
  175. package/dist/features/resource-dashboard/react/components/ResourceRowListItemParts.js +11 -0
  176. package/dist/features/resource-dashboard/react/components/ResourceRowListView.d.ts +1 -1
  177. package/dist/features/resource-dashboard/react/components/ResourceRowListView.js +4 -4
  178. package/dist/features/resource-dashboard/react/components/ResourceRowListViewParts.d.ts +10 -0
  179. package/dist/features/resource-dashboard/react/components/ResourceRowListViewParts.js +20 -0
  180. package/dist/features/resource-dashboard/react/components/resourceBoardProps.d.ts +48 -0
  181. package/dist/features/resource-dashboard/react/components/resourceBoardProps.js +70 -0
  182. package/dist/features/resource-dashboard/react/components/resourceRowListProps.d.ts +19 -0
  183. package/dist/features/resource-dashboard/react/components/resourceRowListProps.js +14 -0
  184. package/dist/features/resource-dashboard/react/components/translatedErrorLabelProps.d.ts +16 -0
  185. package/dist/features/resource-dashboard/react/components/translatedErrorLabelProps.js +13 -0
  186. package/dist/features/resource-dashboard/rules.js +2 -8
  187. package/dist/features/sketch-editor/dom.js +150 -99
  188. package/dist/features/sketch-editor/react/components/SketchEditor.js +26 -15
  189. package/dist/features/skills/react/components/SkillDraftForm.js +13 -19
  190. package/dist/features/skills/react/components/SkillDraftForm.labels.d.ts +31 -0
  191. package/dist/features/skills/react/components/SkillDraftForm.labels.js +54 -0
  192. package/dist/features/skills/react/components/SkillRow.js +38 -18
  193. package/dist/features/skills/react/components/SkillRow.labels.d.ts +7 -0
  194. package/dist/features/skills/react/components/SkillRow.labels.js +18 -0
  195. package/dist/features/skills/react/components/SkillsTab.js +24 -14
  196. package/dist/features/skills/react/components/SkillsTab.labels.d.ts +7 -0
  197. package/dist/features/skills/react/components/SkillsTab.labels.js +11 -0
  198. package/dist/features/skills/rules.js +32 -23
  199. package/dist/features/source-config-list/agent-handles.js +2 -1
  200. package/dist/features/source-config-list/dependencies.js +53 -49
  201. package/dist/features/source-config-list/index.d.ts +3 -3
  202. package/dist/features/source-config-list/react/components/SourceConfigAddForm.d.ts +6 -5
  203. package/dist/features/source-config-list/react/components/SourceConfigAddForm.js +38 -7
  204. package/dist/features/source-config-list/react/components/SourceConfigField.js +69 -25
  205. package/dist/features/source-config-list/react/components/SourceConfigItemCard.d.ts +6 -5
  206. package/dist/features/source-config-list/react/components/SourceConfigItemCard.js +76 -20
  207. package/dist/features/source-config-list/react/components/SourceConfigListView.d.ts +5 -3
  208. package/dist/features/source-config-list/react/components/SourceConfigListView.js +33 -3
  209. package/dist/features/source-config-list/react/components/SourceConfigTestControl.js +17 -1
  210. package/dist/features/source-config-list/react/components/SourceConfigTrustOptions.d.ts +11 -0
  211. package/dist/features/source-config-list/react/components/SourceConfigTrustOptions.js +12 -0
  212. package/dist/features/source-config-list/types.d.ts +14 -0
  213. package/dist/features/tab-launcher-menu/react/components/TabLauncherMenu.js +25 -1
  214. package/dist/features/tab-launcher-menu/react/hooks/useTabLauncherMenu.js +15 -16
  215. package/dist/features/tab-strip/react/components/TabBar.js +8 -1
  216. package/dist/features/tabbed-dialog/react/components/TabbedDialog.js +8 -30
  217. package/dist/features/tabbed-dialog/react/components/TabbedDialogChrome.d.ts +15 -0
  218. package/dist/features/tabbed-dialog/react/components/TabbedDialogChrome.js +8 -0
  219. package/dist/features/tabbed-dialog/react/components/TabbedDialogHeader.d.ts +12 -0
  220. package/dist/features/tabbed-dialog/react/components/TabbedDialogHeader.js +6 -0
  221. package/dist/features/tabbed-dialog/react/components/TabbedDialogSidebar.d.ts +27 -0
  222. package/dist/features/tabbed-dialog/react/components/TabbedDialogSidebar.js +12 -0
  223. package/dist/features/tabbed-dialog/react/components/tabbedDialogView.d.ts +43 -0
  224. package/dist/features/tabbed-dialog/react/components/tabbedDialogView.js +59 -0
  225. package/dist/features/version-manager/dependencies.d.ts +3 -1
  226. package/dist/features/version-manager/dependencies.js +3 -2
  227. package/dist/features/version-manager/react/components/VersionManagerModal.js +10 -6
  228. package/dist/features/version-manager/react/hooks/useVersionManager.js +7 -4
  229. package/dist/features/viewer-shell/react/components/CommentSidePanel.js +103 -50
  230. package/dist/features/viewer-shell/react/components/MarkdownSplitPane.js +28 -8
  231. package/dist/features/viewer-shell/react/hooks/useCommentReorder.js +22 -16
  232. package/dist/features/viewer-shell/rules.js +19 -23
  233. package/dist/react/components/CustomSelect.d.ts +38 -1
  234. package/dist/react/components/CustomSelect.js +94 -38
  235. package/dist/react/components/OnboardingDropdown.js +20 -8
  236. package/dist/react/components/RemixIcon.stylesheet.js +3 -1
  237. package/dist/react/components/Toast.js +13 -2
  238. package/dist/react/components/TooltipLayer.js +5 -14
  239. package/dist/react/components/WorkingDirPicker.js +78 -50
  240. package/dist/react/components/remixicon-font-recovery.js +38 -30
  241. package/dist/react/components/select-popover.d.ts +8 -6
  242. package/dist/react/components/select-popover.js +33 -29
  243. package/dist/react/mcp-ui/McpUiHost.d.ts +1 -1
  244. package/dist/react/mcp-ui/McpUiHost.js +10 -11
  245. package/dist/react/mcp-ui/host-message-source.d.ts +14 -2
  246. package/dist/react/mcp-ui/host-message-source.js +15 -5
  247. package/dist/react/mcp-ui/mcp-ui-host-frame.d.ts +41 -0
  248. package/dist/react/mcp-ui/mcp-ui-host-frame.js +39 -0
  249. package/dist/renderers/annotation-canvas/react/components/AnnotationCanvas.js +97 -69
  250. package/dist/renderers/preview-modal-shell/react/components/PreviewModalShell.js +59 -30
  251. package/dist/renderers/preview-modal-shell/react/components/icons.js +4 -1
  252. package/dist/renderers/react/components/ArtifactView.js +9 -6
  253. package/dist/renderers/srcdoc/build.d.ts +4 -1
  254. package/dist/renderers/srcdoc/build.js +62 -48
  255. package/dist/styles/admin.css +29 -14
  256. package/dist/styles/lightbox.css +142 -0
  257. package/dist/styles/variables.css +128 -0
  258. package/dist/theme/apply.js +9 -5
  259. package/dist/utils/auto-open-file.d.ts +2 -1
  260. package/dist/utils/auto-open-file.js +69 -85
  261. package/dist/utils/markdown-scroll-sync.js +108 -87
  262. package/dist/utils/polygon-selection.js +25 -26
  263. package/dist/utils/smooth-scroll-to-top.d.ts +1 -1
  264. package/dist/utils/smooth-scroll-to-top.js +2 -32
  265. package/dist/utils/sse.js +26 -26
  266. package/dist/utils/unit-bezier.d.ts +7 -0
  267. package/dist/utils/unit-bezier.js +38 -0
  268. package/package.json +7 -10
  269. package/dist/features/admin-widgets/components/ImagePreviewModal.hooks.d.ts +0 -13
  270. package/dist/features/admin-widgets/components/ImagePreviewModal.hooks.js +0 -46
@@ -2,27 +2,26 @@ import { DEFAULT_HOME_NAVIGATION_ENTRY, EMPTY_URL, HISTORY_LIMIT } from './const
2
2
  function escapeRegExp(value) {
3
3
  return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
4
4
  }
5
+ /** Resolves a `/`-rooted address: against `appOrigin` when it matches a configured app-route prefix, else as a local `file://` path. */
6
+ function resolveAbsolutePath(value, options) {
7
+ const { appRoutePrefixes = [], appOrigin } = options;
8
+ if (appOrigin && appRoutePrefixes.length > 0) {
9
+ const prefixPattern = new RegExp(`^/(${appRoutePrefixes.map(escapeRegExp).join('|')})(/|$)`);
10
+ if (prefixPattern.test(value))
11
+ return new URL(value, appOrigin).toString();
12
+ }
13
+ return `file://${encodeURI(value)}`;
14
+ }
5
15
  export function normalizeBrowserAddress(rawAddress, options = {}) {
6
16
  const value = rawAddress.trim();
7
- if (!value)
8
- return EMPTY_URL;
9
- if (value === EMPTY_URL)
17
+ if (!value || value === EMPTY_URL)
10
18
  return EMPTY_URL;
11
19
  if (/^(https?|file):\/\//i.test(value))
12
20
  return value;
13
- if (/^localhost(:\d+)?(\/.*)?$/i.test(value))
21
+ if (/^(localhost|127\.0\.0\.1|0\.0\.0\.0)(:\d+)?(\/.*)?$/i.test(value))
14
22
  return `http://${value}`;
15
- if (/^(127\.0\.0\.1|0\.0\.0\.0)(:\d+)?(\/.*)?$/i.test(value))
16
- return `http://${value}`;
17
- if (value.startsWith('/')) {
18
- const { appRoutePrefixes = [], appOrigin } = options;
19
- if (appOrigin && appRoutePrefixes.length > 0) {
20
- const prefixPattern = new RegExp(`^/(${appRoutePrefixes.map(escapeRegExp).join('|')})(/|$)`);
21
- if (prefixPattern.test(value))
22
- return new URL(value, appOrigin).toString();
23
- }
24
- return `file://${encodeURI(value)}`;
25
- }
23
+ if (value.startsWith('/'))
24
+ return resolveAbsolutePath(value, options);
26
25
  if (/^[\w.-]+\.[a-z]{2,}(:\d+)?(\/.*)?$/i.test(value))
27
26
  return `https://${value}`;
28
27
  return `https://www.google.com/search?q=${encodeURIComponent(value)}`;
@@ -137,37 +136,37 @@ export function mergeHistoryEntry(history, url, meta = {}, options = {}, now = D
137
136
  if (!isHistoryUrl(url))
138
137
  return history;
139
138
  const existing = history.find((entry) => sameUrl(entry.url, url));
140
- // `isHistoryUrl` above already excludes `EMPTY_URL`, so `labelFromUrl`'s
141
- // home-label branch can never trigger here — no `homeLabel` option to pass.
142
- const nextTitle = meta.title && meta.title.trim()
143
- ? meta.title.trim()
144
- : existing?.title || labelFromUrl(url);
145
- const nextIconUrl = cleanIconUrl(meta.iconUrl) || existing?.iconUrl || faviconUrl(url);
146
- const visitIncrement = options.countVisit === false ? 0 : 1;
147
- const entry = existing
148
- ? {
149
- ...existing,
150
- ...(nextIconUrl !== undefined ? { iconUrl: nextIconUrl } : {}),
151
- title: nextTitle,
152
- lastVisitedAt: visitIncrement > 0 ? now : existing.lastVisitedAt,
153
- visitCount: existing.visitCount + visitIncrement,
154
- }
155
- : {
156
- ...(nextIconUrl !== undefined ? { iconUrl: nextIconUrl } : {}),
157
- title: nextTitle,
158
- url,
159
- lastVisitedAt: now,
160
- visitCount: 1,
161
- };
162
- if (existing
163
- && existing.title === entry.title
164
- && existing.iconUrl === entry.iconUrl
165
- && existing.lastVisitedAt === entry.lastVisitedAt
166
- && existing.visitCount === entry.visitCount) {
139
+ const entry = buildHistoryEntry({ url, meta, now, countVisit: options.countVisit !== false }, { existing });
140
+ if (existing && sameHistoryEntryState(existing, entry))
167
141
  return history;
168
- }
169
142
  return [entry, ...history.filter((item) => !sameUrl(item.url, url))].slice(0, HISTORY_LIMIT);
170
143
  }
144
+ /** Builds the merged entry for `url`: an update of `existing` when present, otherwise a fresh first-visit entry. */
145
+ function buildHistoryEntry({ url, meta, now, countVisit }, { existing }) {
146
+ const resolved = resolveHistoryMeta({ url, meta }, { existing });
147
+ if (!existing)
148
+ return { ...resolved, url, lastVisitedAt: now, visitCount: 1 };
149
+ return {
150
+ ...existing,
151
+ ...resolved,
152
+ lastVisitedAt: countVisit ? now : existing.lastVisitedAt,
153
+ visitCount: existing.visitCount + (countVisit ? 1 : 0),
154
+ };
155
+ }
156
+ /** Title and icon for a merged entry: the caller's meta wins, then the existing entry's, then values derived from `url`. */
157
+ function resolveHistoryMeta({ url, meta }, { existing }) {
158
+ // `isHistoryUrl` above already excludes `EMPTY_URL`, so `labelFromUrl`'s
159
+ // home-label branch can never trigger here — no `homeLabel` option to pass.
160
+ const title = meta.title?.trim() || existing?.title || labelFromUrl(url);
161
+ const iconUrl = cleanIconUrl(meta.iconUrl) || existing?.iconUrl || faviconUrl(url);
162
+ return iconUrl !== undefined ? { iconUrl, title } : { title };
163
+ }
164
+ function sameHistoryEntryState(left, right) {
165
+ return (left.title === right.title
166
+ && left.iconUrl === right.iconUrl
167
+ && left.lastVisitedAt === right.lastVisitedAt
168
+ && left.visitCount === right.visitCount);
169
+ }
171
170
  // ---------------------------------------------------------------------------
172
171
  // Navigation stack
173
172
  // ---------------------------------------------------------------------------
@@ -201,6 +200,20 @@ export function recordNavigation(state, url, title, options = {}) {
201
200
  if (url !== EMPTY_URL && !isHistoryUrl(url))
202
201
  return state;
203
202
  const { navigationStack: stack, navigationIndex: index } = state;
203
+ const nextTitle = navigationTitle({ url }, { title, homeLabel: options.homeLabel });
204
+ const rejoin = findRejoinTarget({ state, url }, options);
205
+ if (rejoin) {
206
+ const next = stack.slice();
207
+ // `nextTitle` is only ever '' for EMPTY_URL with an explicit '' homeLabel;
208
+ // keep the rejoined entry's own title rather than blanking it.
209
+ next[rejoin.index] = { title: nextTitle || rejoin.entry.title, url };
210
+ return { navigationStack: next, navigationIndex: rejoin.index };
211
+ }
212
+ const base = index >= 0 ? stack.slice(0, index + 1) : [];
213
+ const nextStack = [...base, { title: nextTitle, url }].slice(-HISTORY_LIMIT);
214
+ return { navigationStack: nextStack, navigationIndex: nextStack.length - 1 };
215
+ }
216
+ function navigationTitle({ url }, { title, homeLabel }) {
204
217
  // Bug fix: this used to hardcode DEFAULT_HOME_NAVIGATION_ENTRY.title here,
205
218
  // ignoring a caller-supplied `options.homeLabel` — so a host configuring a
206
219
  // custom `homeEntry` (via useBrowserNavigationStack) would see the entry's
@@ -208,34 +221,25 @@ export function recordNavigation(state, url, title, options = {}) {
208
221
  // even though `initialNavigationState`/`labelFromUrl` both honor the
209
222
  // custom label correctly. Falls back to the generic default only when no
210
223
  // homeLabel is supplied.
211
- const nextTitle = url === EMPTY_URL
212
- ? (options.homeLabel ?? DEFAULT_HOME_NAVIGATION_ENTRY.title)
213
- : title && title.trim()
214
- ? title.trim()
215
- : labelFromUrl(url, options.homeLabel);
216
- const nextEntry = { title: nextTitle, url };
217
- const updateEntry = (entries, entryIndex) => {
218
- const existing = entries[entryIndex];
219
- const next = entries.slice();
220
- next[entryIndex] = { title: nextTitle || existing?.title || labelFromUrl(url, options.homeLabel), url };
221
- return next;
222
- };
223
- const currentEntry = index >= 0 ? stack[index] : undefined;
224
- const shouldReplacePending = Boolean(options.replacePendingTarget && options.pendingTarget && currentEntry && sameUrl(currentEntry.url, options.pendingTarget));
225
- if (currentEntry && (sameUrl(currentEntry.url, url) || shouldReplacePending)) {
226
- return { navigationStack: updateEntry(stack, index), navigationIndex: index };
227
- }
228
- const previousIndex = index - 1;
229
- if (previousIndex >= 0 && sameUrl(stack[previousIndex]?.url ?? '', url)) {
230
- return { navigationStack: updateEntry(stack, previousIndex), navigationIndex: previousIndex };
231
- }
232
- const nextIndex = index + 1;
233
- if (nextIndex < stack.length && sameUrl(stack[nextIndex]?.url ?? '', url)) {
234
- return { navigationStack: updateEntry(stack, nextIndex), navigationIndex: nextIndex };
224
+ if (url === EMPTY_URL)
225
+ return homeLabel ?? DEFAULT_HOME_NAVIGATION_ENTRY.title;
226
+ return title?.trim() || labelFromUrl(url, homeLabel);
227
+ }
228
+ /**
229
+ * The stack entry `url` should update in place instead of appending: the
230
+ * current entry (same url, or the pending-load target being replaced), else
231
+ * the adjacent back entry, else the adjacent forward entry. An out-of-range
232
+ * index reads as `undefined`, which never matches.
233
+ */
234
+ function findRejoinTarget({ state, url }, { replacePendingTarget, pendingTarget }) {
235
+ const { navigationStack: stack, navigationIndex: index } = state;
236
+ const current = stack[index];
237
+ if (current && (sameUrl(current.url, url) || (replacePendingTarget && pendingTarget && sameUrl(current.url, pendingTarget)))) {
238
+ return { index, entry: current };
235
239
  }
236
- const base = index >= 0 ? stack.slice(0, index + 1) : [];
237
- const nextStack = [...base, nextEntry].slice(-HISTORY_LIMIT);
238
- return { navigationStack: nextStack, navigationIndex: nextStack.length - 1 };
240
+ return [index - 1, index + 1]
241
+ .map((candidate) => ({ index: candidate, entry: stack[candidate] }))
242
+ .find((target) => target.entry !== undefined && sameUrl(target.entry.url, url));
239
243
  }
240
244
  export function updateCurrentNavigationTitle(state, title) {
241
245
  const trimmedTitle = title?.trim();
@@ -59,6 +59,12 @@ export function mountChart({ element, spec, registry }, options = {}) {
59
59
  function candidates() {
60
60
  return selectChartProviders({ registry, spec: input.spec }, { siteProvider: options.siteProvider }).filter(({ id }) => !failed.has(id));
61
61
  }
62
+ function isStaleMount({ token }, _options = {}) {
63
+ return destroyed || token !== generation;
64
+ }
65
+ function isCurrentUpdate({ current, updateRevision }, _options = {}) {
66
+ return !destroyed && active === current && updateRevision === revision;
67
+ }
62
68
  /** Async renderer errors arrive after mount; defer teardown outside the provider's commit. */
63
69
  function renderError(entry, error, token) {
64
70
  queueMicrotask(() => {
@@ -76,7 +82,7 @@ export function mountChart({ element, spec, registry }, options = {}) {
76
82
  let renderRevision = revision;
77
83
  try {
78
84
  const provider = await entry.load({});
79
- if (destroyed || token !== generation)
85
+ if (isStaleMount({ token }))
80
86
  return;
81
87
  const frame = element.ownerDocument.createElement('div');
82
88
  renderer.replaceChildren(frame);
@@ -84,13 +90,13 @@ export function mountChart({ element, spec, registry }, options = {}) {
84
90
  const instance = provider.mount({ element: frame, ...input }, { onError: (error) => renderError(entry, error, token) });
85
91
  active = { entry, instance };
86
92
  await instance.ready;
87
- if (destroyed || token !== generation || renderRevision !== revision)
93
+ if (isStaleMount({ token }) || renderRevision !== revision)
88
94
  return;
89
95
  fallback.hidden = true;
90
96
  return;
91
97
  }
92
98
  catch (error) {
93
- if (destroyed || token !== generation || renderRevision !== revision)
99
+ if (isStaleMount({ token }) || renderRevision !== revision)
94
100
  return;
95
101
  failed.add(entry.id);
96
102
  report(entry, error);
@@ -113,12 +119,12 @@ export function mountChart({ element, spec, registry }, options = {}) {
113
119
  const current = active;
114
120
  try {
115
121
  await current.instance.update(input);
116
- if (!destroyed && active === current && updateRevision === revision)
122
+ if (isCurrentUpdate({ current, updateRevision }))
117
123
  fallback.hidden = true;
118
124
  return;
119
125
  }
120
126
  catch (error) {
121
- if (destroyed || active !== current || updateRevision !== revision)
127
+ if (!isCurrentUpdate({ current, updateRevision }))
122
128
  return;
123
129
  failed.add(current.entry.id);
124
130
  report(current.entry, error);
@@ -13,8 +13,8 @@ export function ChartHost({ spec, registry, theme = DEFAULT_CHART_THEME, sitePro
13
13
  const instance = useRef(undefined);
14
14
  const previous = useRef({ spec, theme });
15
15
  useEffect(() => {
16
- if (!element.current)
17
- return;
16
+ // The container `<div>` below is rendered unconditionally, so React has always attached the ref
17
+ // by the time this effect runs; the old `if (!element.current) return` could never fire.
18
18
  const mounted = mountChart({ element: element.current, spec, registry }, {
19
19
  theme, siteProvider, ...(onProviderError ? { onProviderError } : {}),
20
20
  });
@@ -41,8 +41,8 @@ export function renderChartTable({ element, spec }, _options = {}) {
41
41
  const tr = body.insertRow();
42
42
  for (const { key } of columns) {
43
43
  const cell = tr.insertCell();
44
- const value = row[key];
45
- cell.textContent = value == null ? '' : typeof value === 'object' ? JSON.stringify(value) : String(value);
44
+ const value = row[key] ?? '';
45
+ cell.textContent = typeof value === 'object' ? JSON.stringify(value) : String(value);
46
46
  cell.dataset.numeric = String(numeric.has(key));
47
47
  }
48
48
  }
@@ -30,18 +30,12 @@ export function useCommandPalette(options, dependencies) {
30
30
  onClose();
31
31
  return;
32
32
  }
33
- if (event.key === 'ArrowDown') {
33
+ if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
34
34
  event.preventDefault();
35
35
  if (results.length === 0)
36
36
  return;
37
- setCursor((current) => nextCursor(current, results.length, 1));
38
- return;
39
- }
40
- if (event.key === 'ArrowUp') {
41
- event.preventDefault();
42
- if (results.length === 0)
43
- return;
44
- setCursor((current) => nextCursor(current, results.length, -1));
37
+ const direction = event.key === 'ArrowDown' ? 1 : -1;
38
+ setCursor((current) => nextCursor(current, results.length, direction));
45
39
  return;
46
40
  }
47
41
  if (event.key === 'Enter') {
@@ -1,8 +1,8 @@
1
1
  import type { ConnectorsDependencies } from './ports.js';
2
2
  import type { Connector, ConnectorAuthResultEvent, ProviderTab } from './types.js';
3
- import { useConnectorCatalog } from './hooks/useConnectorCatalog.js';
4
- import { useConnectorAuthorization } from './hooks/useConnectorAuthorization.js';
5
- import { useConnectorDetail } from './hooks/useConnectorDetail.js';
3
+ import type { useConnectorCatalog } from './hooks/useConnectorCatalog.js';
4
+ import type { useConnectorAuthorization } from './hooks/useConnectorAuthorization.js';
5
+ import type { useConnectorDetail } from './hooks/useConnectorDetail.js';
6
6
  import type { ConnectorGateProps } from './components/ConnectorGate.js';
7
7
  export interface ConnectorsBrowserProps {
8
8
  /** Whether the host's provider is configured (e.g. an API key is saved) — gates enrichment + unmasks the grid. */
@@ -36,5 +36,5 @@ export interface ConnectorsBrowserProps {
36
36
  * messages. Ported from OD's `ConnectorsBrowser.tsx` — see
37
37
  * `packages/ui/source-map.md` for full provenance.
38
38
  */
39
- export declare function ConnectorsBrowser({ unlocked, providerTabs, catalogRefreshKey, dependencies, getCategoryLabel, getDisplayableAccountLabel, gate, onProviderTabClick, onConnectorAuthResult, onConnectorsChanged, useConnectorCatalog: useConnectorCatalogHook, useConnectorAuthorization: useConnectorAuthorizationHook, useConnectorDetail: useConnectorDetailHook, agentHandle, }: ConnectorsBrowserProps): import("react").JSX.Element;
39
+ export declare function ConnectorsBrowser({ unlocked, providerTabs, catalogRefreshKey, dependencies, getCategoryLabel, getDisplayableAccountLabel, gate, onProviderTabClick, onConnectorAuthResult, onConnectorsChanged, useConnectorCatalog, useConnectorAuthorization, useConnectorDetail, agentHandle, }: ConnectorsBrowserProps): import("react").JSX.Element;
40
40
  //# sourceMappingURL=ConnectorsBrowser.d.ts.map
@@ -4,9 +4,6 @@ import { agentSubHandle } from '@jini-ai/agentic';
4
4
  import { useT } from '../i18n/index.js';
5
5
  import { AUTHORIZATION_CANCEL_FAILED_MESSAGE, DEFAULT_PROVIDER_TAB_ID, DEFAULT_PROVIDER_TABS, } from './constants.js';
6
6
  import { connectorPanelAlerts, scopeConnectorsToProvider, sortConnectorsForSearch, } from './rules.js';
7
- import { useConnectorCatalog } from './hooks/useConnectorCatalog.js';
8
- import { useConnectorAuthorization } from './hooks/useConnectorAuthorization.js';
9
- import { useConnectorDetail } from './hooks/useConnectorDetail.js';
10
7
  import { useWiredConnectorsBrowser } from './hooks/useWiredConnectorsBrowser.js';
11
8
  import { CenteredLoader } from '../../react/components/Loading.js';
12
9
  import { ProviderTabBar } from './components/ProviderTabBar.js';
@@ -14,6 +11,18 @@ import { ConnectorSearchBar } from './components/ConnectorSearchBar.js';
14
11
  import { ConnectorAlertList } from './components/ConnectorAlertList.js';
15
12
  import { ConnectorGrid } from './components/ConnectorGrid.js';
16
13
  import { ConnectorDetailDrawer } from './components/ConnectorDetailDrawer.js';
14
+ /** `{ agentHandle }` for a child derived from the browser's own handle, or `{}` when the host published none. */
15
+ function subHandleProp({ base, action }) {
16
+ return base ? { agentHandle: agentSubHandle({ base, action }) } : {};
17
+ }
18
+ /** The detail drawer for the connector currently opened, or nothing when none is. */
19
+ function ConnectorsDetailPane({ detail, authorization, unlocked, actions, getCategoryLabel, getDisplayableAccountLabel, agentHandle, }) {
20
+ const connector = detail.detailConnector;
21
+ if (!connector)
22
+ return null;
23
+ const { pendingConnectorAction } = authorization;
24
+ return (_jsx(ConnectorDetailDrawer, { connector: connector, disabled: !unlocked, pendingAction: pendingConnectorAction?.connectorId === connector.id ? pendingConnectorAction.action : null, authorizationPending: authorization.pending[connector.id], authorizationCancelFailed: authorization.cancelFailed[connector.id] === true, authorizationError: authorization.authError[connector.id] ?? null, toolsPreviewLoading: detail.toolPreviewLoading, toolsLoaded: detail.toolsLoaded, onClose: detail.closeDetails, ...actions, onLoadMoreTools: (connectorId, cursor) => void detail.loadMoreTools(connectorId, cursor), ...(getCategoryLabel ? { getCategoryLabel } : {}), ...(getDisplayableAccountLabel ? { getDisplayableAccountLabel } : {}), ...subHandleProp({ base: agentHandle, action: 'detail' }) }));
25
+ }
17
26
  /**
18
27
  * Connector cards + search — an OAuth integration marketplace UI. Owns its
19
28
  * own data lifecycle: fetches the catalog on mount, lazily enriches once
@@ -21,7 +30,7 @@ import { ConnectorDetailDrawer } from './components/ConnectorDetailDrawer.js';
21
30
  * messages. Ported from OD's `ConnectorsBrowser.tsx` — see
22
31
  * `packages/ui/source-map.md` for full provenance.
23
32
  */
24
- export function ConnectorsBrowser({ unlocked, providerTabs = DEFAULT_PROVIDER_TABS, catalogRefreshKey = 0, dependencies, getCategoryLabel, getDisplayableAccountLabel, gate, onProviderTabClick, onConnectorAuthResult, onConnectorsChanged, useConnectorCatalog: useConnectorCatalogHook = useConnectorCatalog, useConnectorAuthorization: useConnectorAuthorizationHook = useConnectorAuthorization, useConnectorDetail: useConnectorDetailHook = useConnectorDetail, agentHandle, }) {
33
+ export function ConnectorsBrowser({ unlocked, providerTabs = DEFAULT_PROVIDER_TABS, catalogRefreshKey, dependencies, getCategoryLabel, getDisplayableAccountLabel, gate, onProviderTabClick, onConnectorAuthResult, onConnectorsChanged, useConnectorCatalog, useConnectorAuthorization, useConnectorDetail, agentHandle, }) {
25
34
  const t = useT();
26
35
  const [filter, setFilter] = useState('');
27
36
  const [selectedProvider, setSelectedProvider] = useState(providerTabs[0]?.id ?? DEFAULT_PROVIDER_TAB_ID);
@@ -34,16 +43,15 @@ export function ConnectorsBrowser({ unlocked, providerTabs = DEFAULT_PROVIDER_TA
34
43
  dependencies,
35
44
  unlocked,
36
45
  catalogRefreshKey,
37
- ...(onConnectorsChanged ? { onConnectorsChanged } : {}),
38
- ...(onConnectorAuthResult ? { onConnectorAuthResult } : {}),
39
- useConnectorCatalog: useConnectorCatalogHook,
40
- useConnectorAuthorization: useConnectorAuthorizationHook,
41
- useConnectorDetail: useConnectorDetailHook,
46
+ onConnectorsChanged,
47
+ onConnectorAuthResult,
48
+ useConnectorCatalog,
49
+ useConnectorAuthorization,
50
+ useConnectorDetail,
42
51
  });
43
52
  const providerScopedConnectors = useMemo(() => scopeConnectorsToProvider(catalog.connectors, providerTabs, selectedProvider), [catalog.connectors, providerTabs, selectedProvider]);
44
53
  const filteredConnectors = useMemo(() => sortConnectorsForSearch(providerScopedConnectors, filter), [providerScopedConnectors, filter]);
45
- const hasQuery = filter.trim().length > 0;
46
- const hasNoResults = hasQuery && filteredConnectors.length === 0;
54
+ const hasNoResults = filter.trim().length > 0 && filteredConnectors.length === 0;
47
55
  const alerts = useMemo(() => connectorPanelAlerts(catalog.connectors, detail.detailConnectorId, authorization.authError, authorization.cancelFailed, t(AUTHORIZATION_CANCEL_FAILED_MESSAGE)), [catalog.connectors, detail.detailConnectorId, authorization.authError, authorization.cancelFailed, t]);
48
56
  const handleProviderTabSelect = useCallback((id) => {
49
57
  onProviderTabClick?.('provider_chip');
@@ -53,8 +61,12 @@ export function ConnectorsBrowser({ unlocked, providerTabs = DEFAULT_PROVIDER_TA
53
61
  onProviderTabClick?.('gate_card');
54
62
  gate?.onClick?.();
55
63
  }, [onProviderTabClick, gate]);
56
- return (_jsxs("div", { className: "tab-panel connectors-panel connectors-panel-embedded", children: [_jsxs("div", { className: "tab-panel-toolbar", children: [_jsx("div", { className: "toolbar-left connectors-heading", children: _jsxs("div", { children: [_jsx("h2", { children: t('Connectors') }), _jsx("p", { children: t('Connect third-party tools and services.') })] }) }), _jsxs("div", { className: "toolbar-right", children: [_jsx(ProviderTabBar, { tabs: providerTabs, selectedId: selectedProvider, onSelect: handleProviderTabSelect, ...(agentHandle ? { agentHandle: agentSubHandle({ base: agentHandle, action: 'provider' }) } : {}) }), _jsx(ConnectorSearchBar, { value: filter, onChange: setFilter, disabled: !unlocked, onFocus: () => onProviderTabClick?.('search_connectors'), ...(agentHandle ? { agentHandle: agentSubHandle({ base: agentHandle, action: 'search' }) } : {}) })] })] }), _jsx(ConnectorAlertList, { alerts: alerts, onOpenDetails: detail.openDetails }), catalog.loading ? (_jsx(CenteredLoader, { label: t('Loading…') })) : (_jsx(ConnectorGrid, { connectors: filteredConnectors, locked: !unlocked, hasNoResults: hasNoResults, searchQuery: filter, pendingConnectorAction: authorization.pendingConnectorAction, authorizationPending: authorization.pending, authorizationCancelFailed: authorization.cancelFailed, toolsLoaded: catalog.enriched, onConnect: (connectorId) => void authorization.runConnectorAction(connectorId, 'connect'), onDisconnect: (connectorId) => void authorization.runConnectorAction(connectorId, 'disconnect'), onCancelAuthorization: (connectorId) => void authorization.cancelAuthorization(connectorId), onOpenDetails: detail.openDetails, onOpenExternalUrl: (url) => void deps.data.openExternalUrl(url), ...(getCategoryLabel ? { getCategoryLabel } : {}), onClearSearch: () => setFilter(''), ...(gate ? { gate: { ...gate, onClick: handleGateClick } } : {}), ...(agentHandle ? { agentHandle: agentSubHandle({ base: agentHandle, action: 'connector' }) } : {}) })), detail.detailConnector ? (_jsx(ConnectorDetailDrawer, { connector: detail.detailConnector, disabled: !unlocked, pendingAction: authorization.pendingConnectorAction?.connectorId === detail.detailConnector.id
57
- ? authorization.pendingConnectorAction.action
58
- : null, authorizationPending: authorization.pending[detail.detailConnector.id], authorizationCancelFailed: authorization.cancelFailed[detail.detailConnector.id] === true, authorizationError: authorization.authError[detail.detailConnector.id] ?? null, toolsPreviewLoading: detail.toolPreviewLoading, toolsLoaded: detail.toolsLoaded, onClose: detail.closeDetails, onConnect: (connectorId) => void authorization.runConnectorAction(connectorId, 'connect'), onDisconnect: (connectorId) => void authorization.runConnectorAction(connectorId, 'disconnect'), onCancelAuthorization: (connectorId) => void authorization.cancelAuthorization(connectorId), onLoadMoreTools: (connectorId, cursor) => void detail.loadMoreTools(connectorId, cursor), onOpenExternalUrl: (url) => void deps.data.openExternalUrl(url), ...(getCategoryLabel ? { getCategoryLabel } : {}), ...(getDisplayableAccountLabel ? { getDisplayableAccountLabel } : {}), ...(agentHandle ? { agentHandle: agentSubHandle({ base: agentHandle, action: 'detail' }) } : {}) })) : null] }));
64
+ const actions = {
65
+ onConnect: (connectorId) => void authorization.runConnectorAction(connectorId, 'connect'),
66
+ onDisconnect: (connectorId) => void authorization.runConnectorAction(connectorId, 'disconnect'),
67
+ onCancelAuthorization: (connectorId) => void authorization.cancelAuthorization(connectorId),
68
+ onOpenExternalUrl: (url) => void deps.data.openExternalUrl(url),
69
+ };
70
+ return (_jsxs("div", { className: "tab-panel connectors-panel connectors-panel-embedded", children: [_jsxs("div", { className: "tab-panel-toolbar", children: [_jsx("div", { className: "toolbar-left connectors-heading", children: _jsxs("div", { children: [_jsx("h2", { children: t('Connectors') }), _jsx("p", { children: t('Connect third-party tools and services.') })] }) }), _jsxs("div", { className: "toolbar-right", children: [_jsx(ProviderTabBar, { tabs: providerTabs, selectedId: selectedProvider, onSelect: handleProviderTabSelect, ...subHandleProp({ base: agentHandle, action: 'provider' }) }), _jsx(ConnectorSearchBar, { value: filter, onChange: setFilter, disabled: !unlocked, onFocus: () => onProviderTabClick?.('search_connectors'), ...subHandleProp({ base: agentHandle, action: 'search' }) })] })] }), _jsx(ConnectorAlertList, { alerts: alerts, onOpenDetails: detail.openDetails }), catalog.loading ? (_jsx(CenteredLoader, { label: t('Loading…') })) : (_jsx(ConnectorGrid, { connectors: filteredConnectors, locked: !unlocked, hasNoResults: hasNoResults, searchQuery: filter, pendingConnectorAction: authorization.pendingConnectorAction, authorizationPending: authorization.pending, authorizationCancelFailed: authorization.cancelFailed, toolsLoaded: catalog.enriched, ...actions, onOpenDetails: detail.openDetails, ...(getCategoryLabel ? { getCategoryLabel } : {}), onClearSearch: () => setFilter(''), ...(gate ? { gate: { ...gate, onClick: handleGateClick } } : {}), ...subHandleProp({ base: agentHandle, action: 'connector' }) })), _jsx(ConnectorsDetailPane, { detail: detail, authorization: authorization, unlocked: unlocked, actions: actions, getCategoryLabel: getCategoryLabel, getDisplayableAccountLabel: getDisplayableAccountLabel, agentHandle: agentHandle })] }));
59
71
  }
60
72
  //# sourceMappingURL=ConnectorsBrowser.js.map
@@ -1,25 +1,77 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { agentHandleProps } from '@jini-ai/agentic';
3
3
  import { useT } from '../../i18n/index.js';
4
- import { getConnectorDisplayToolCount, statusLabel, toolsBadgeTranslation } from '../rules.js';
4
+ import { connectorActionFlags, getConnectorDisplayToolCount, statusLabel, toolsBadgeTranslation } from '../rules.js';
5
5
  import { Icon } from '../../../react/components/Icon.js';
6
6
  import { ConnectorLogo } from './ConnectorLogo.js';
7
- export function ConnectorCard({ connector, disabled = false, pendingAction, authorizationPending, authorizationCancelFailed, toolsLoaded, onConnect, onDisconnect, onCancelAuthorization, onOpenDetails, getCategoryLabel = (category) => category, cancelFailedMessage, continueInBrowserLabel, onOpenExternalUrl, agentHandle, }) {
7
+ function stopPropagation(event) {
8
+ event.stopPropagation();
9
+ }
10
+ /** Click handler for a card action: stops the click from also opening the card's details, then acts. */
11
+ function stopThen(onActivate) {
12
+ return (event) => {
13
+ stopPropagation(event);
14
+ onActivate();
15
+ };
16
+ }
17
+ function ConnectorCardTitleDot({ status, isAuthorizationPending }) {
18
+ const t = useT();
19
+ if (status === 'connected') {
20
+ return (_jsx("span", { className: `connector-status-dot connector-card-title-dot status-${status}`, "aria-label": t(statusLabel(status)), title: t(statusLabel(status)), role: "img" }));
21
+ }
22
+ if (!isAuthorizationPending)
23
+ return null;
24
+ return (_jsx("span", { className: "connector-status-dot connector-card-title-dot status-pending", "aria-label": t('Authorization pending'), title: t('Authorization pending'), role: "img" }));
25
+ }
26
+ function ConnectorCardToolsBadge({ connector, toolsLoaded }) {
8
27
  const t = useT();
9
- const resolvedCancelFailedMessage = cancelFailedMessage ?? t("Couldn't cancel authorization. Try again.");
10
- const resolvedContinueInBrowserLabel = continueInBrowserLabel ?? t('Continue in browser');
11
- const isConnecting = pendingAction === 'connect';
12
- const isDisconnecting = pendingAction === 'disconnect';
13
- const isConnected = connector.status === 'connected';
14
- const isAuthorizationPending = !isConnected && authorizationPending !== undefined;
15
- const isPending = pendingAction !== null || isAuthorizationPending;
16
- const canConnect = !disabled && !isPending && connector.status === 'available';
17
- const canDisconnect = !disabled && !isPending && isConnected;
18
- const toolCount = getConnectorDisplayToolCount(connector);
19
28
  const showToolsBadge = connector.toolCount !== undefined || connector.tools.length > 0 || toolsLoaded;
20
- const toolsBadge = toolsBadgeTranslation(toolCount);
29
+ const toolsBadge = toolsBadgeTranslation(getConnectorDisplayToolCount(connector));
21
30
  const toolsBadgeLabel = t(toolsBadge.key, toolsBadge.vars);
31
+ return (_jsx("span", { className: "connector-meta-tools", "aria-hidden": !showToolsBadge, children: showToolsBadge ? (_jsx("span", { className: "connector-tools-badge is-ready", title: toolsBadgeLabel, children: _jsx("span", { children: toolsBadgeLabel }) })) : null }));
32
+ }
33
+ function ConnectorDisconnectButton({ flags, disabled, agentHandle, onActivate }) {
34
+ const t = useT();
35
+ const { isDisconnecting } = flags;
36
+ return (_jsx("button", { type: "button", className: `icon-only connector-action is-disconnect${isDisconnecting ? ' is-loading' : ''}`, disabled: !flags.canDisconnect, "aria-busy": isDisconnecting || undefined, "aria-label": t('Disconnect'), title: t('Disconnect'), tabIndex: disabled ? -1 : undefined, onMouseDown: stopPropagation, onKeyDown: stopPropagation, onClick: stopThen(onActivate), ...agentHandleProps({}, { base: agentHandle, action: 'disconnect', role: 'button', label: t('Disconnect') }), children: _jsx(Icon, { name: isDisconnecting ? 'spinner' : 'close', size: 12 }) }));
37
+ }
38
+ function ConnectorConnectButton({ flags, disabled, agentHandle, onActivate }) {
39
+ const t = useT();
40
+ const { isAuthorizationPending } = flags;
41
+ const isBusy = flags.isConnecting || isAuthorizationPending;
42
+ return (_jsx("button", { type: "button", className: `icon-only connector-action is-connect${isBusy ? ' is-loading' : ''}`, disabled: !flags.canConnect, "aria-busy": isBusy || undefined, "aria-label": isAuthorizationPending ? t('Authorization pending') : t('Connect'), title: isAuthorizationPending ? t('Authorization in progress') : t('Connect'), tabIndex: disabled ? -1 : undefined, onMouseDown: stopPropagation, onKeyDown: stopPropagation, onClick: stopThen(onActivate), ...agentHandleProps({}, { base: agentHandle, action: 'connect', role: 'button', label: t('Connect') }), children: _jsx(Icon, { name: isBusy ? 'spinner' : 'plus', size: 12 }) }));
43
+ }
44
+ function ConnectorCancelAuthorizationButton({ onActivate }) {
45
+ const t = useT();
46
+ return (_jsx("button", { type: "button", className: "icon-only connector-action is-cancel-authorization", "aria-label": t('Cancel authorization'), title: t('Cancel authorization'), onMouseDown: stopPropagation, onKeyDown: stopPropagation, onClick: stopThen(onActivate), children: _jsx(Icon, { name: "close", size: 12 }) }));
47
+ }
48
+ function ConnectorStatusPill({ status }) {
49
+ const t = useT();
50
+ if (status !== 'error' && status !== 'disabled')
51
+ return null;
52
+ return _jsx("span", { className: `connector-status-pill status-${status}`, children: t(statusLabel(status)) });
53
+ }
54
+ /** The cancel-failed alert and the continue-in-browser link under the card body. */
55
+ function ConnectorCardHints({ authorizationCancelFailed, redirectUrl, cancelFailedMessage, continueInBrowserLabel, onOpenExternalUrl, }) {
56
+ const t = useT();
57
+ function continueAuthorization(event) {
58
+ stopPropagation(event);
59
+ // `continueAuthorization`'s only call site (the "continue in browser"
60
+ // button below) only ever renders while
61
+ // `isAuthorizationPending && authorizationPending.redirectUrl` already
62
+ // holds in that same render, and React re-binds this closure fresh on
63
+ // every render — so a re-check here was dead code for every real
64
+ // click, removed rather than tested around (see
65
+ // packages/ui/source-map.md's 2026-07-22 dated entry).
66
+ onOpenExternalUrl?.(redirectUrl);
67
+ }
68
+ return (_jsxs(_Fragment, { children: [authorizationCancelFailed ? (_jsx("p", { className: "connector-authorization-hint connector-authorization-error", role: "alert", children: cancelFailedMessage ?? t("Couldn't cancel authorization. Try again.") })) : null, redirectUrl ? (_jsx("button", { type: "button", className: "connector-authorization-link", title: t('Authorization in progress'), onClick: continueAuthorization, children: continueInBrowserLabel ?? t('Continue in browser') })) : null] }));
69
+ }
70
+ export function ConnectorCard({ connector, disabled = false, pendingAction, authorizationPending, authorizationCancelFailed, toolsLoaded, onConnect, onDisconnect, onCancelAuthorization, onOpenDetails, getCategoryLabel = (category) => category, cancelFailedMessage, continueInBrowserLabel, onOpenExternalUrl, agentHandle, }) {
71
+ const t = useT();
72
+ const flags = connectorActionFlags({ connector, pendingAction, authorizationPending, disabled });
22
73
  const categoryLabel = getCategoryLabel(connector.category);
74
+ const actionProps = { flags, disabled, agentHandle };
23
75
  function openDetails() {
24
76
  if (disabled)
25
77
  return;
@@ -33,29 +85,6 @@ export function ConnectorCard({ connector, disabled = false, pendingAction, auth
33
85
  event.preventDefault();
34
86
  openDetails();
35
87
  }
36
- function stop(event) {
37
- event.stopPropagation();
38
- }
39
- function continueAuthorization(event) {
40
- stop(event);
41
- // `continueAuthorization`'s only call site (the "continue in browser"
42
- // button below) only ever renders while
43
- // `isAuthorizationPending && authorizationPending.redirectUrl` already
44
- // holds in that same render, and React re-binds this closure fresh on
45
- // every render — so a re-check here was dead code for every real
46
- // click, removed rather than tested around (see
47
- // packages/ui/source-map.md's 2026-07-22 dated entry).
48
- onOpenExternalUrl?.(authorizationPending.redirectUrl);
49
- }
50
- return (_jsxs("article", { className: `connector-card status-${connector.status}${disabled ? ' is-locked' : ''}`, "data-connector-id": connector.id, role: "button", tabIndex: disabled ? -1 : 0, "aria-disabled": disabled || undefined, "aria-label": t('Open details for {name}', { name: connector.name }), onClick: openDetails, onKeyDown: onKeyActivate, ...agentHandleProps({}, { base: agentHandle, ...({ role: 'button', label: connector.name }) }), children: [_jsxs("div", { className: "connector-card-top", children: [_jsx(ConnectorLogo, { connectorId: connector.id, connectorName: connector.name, logoUrl: connector.logoUrl, size: "sm" }), _jsxs("div", { className: "connector-card-head", children: [_jsxs("h3", { className: "connector-card-title", children: [_jsx("span", { className: "connector-card-title-name", children: connector.name }), isConnected ? (_jsx("span", { className: `connector-status-dot connector-card-title-dot status-${connector.status}`, "aria-label": t(statusLabel(connector.status)), title: t(statusLabel(connector.status)), role: "img" })) : isAuthorizationPending ? (_jsx("span", { className: "connector-status-dot connector-card-title-dot status-pending", "aria-label": t('Authorization pending'), title: t('Authorization pending'), role: "img" })) : null] }), _jsxs("div", { className: "connector-meta", children: [_jsx("span", { className: "connector-meta-item connector-meta-category", title: categoryLabel, children: categoryLabel }), _jsx("span", { className: "connector-meta-tools", "aria-hidden": !showToolsBadge, children: showToolsBadge ? (_jsx("span", { className: "connector-tools-badge is-ready", title: toolsBadgeLabel, children: _jsx("span", { children: toolsBadgeLabel }) })) : null })] })] }), _jsxs("div", { className: "connector-card-actions", children: [isConnected ? (_jsx("button", { type: "button", className: `icon-only connector-action is-disconnect${isDisconnecting ? ' is-loading' : ''}`, disabled: !canDisconnect, "aria-busy": isDisconnecting || undefined, "aria-label": t('Disconnect'), title: t('Disconnect'), tabIndex: disabled ? -1 : undefined, onMouseDown: stop, onKeyDown: stop, onClick: (e) => {
51
- stop(e);
52
- onDisconnect(connector.id);
53
- }, ...agentHandleProps({}, { base: agentHandle, ...({ action: 'disconnect', role: 'button', label: t('Disconnect') }) }), children: _jsx(Icon, { name: isDisconnecting ? 'spinner' : 'close', size: 12 }) })) : (_jsx("button", { type: "button", className: `icon-only connector-action is-connect${isConnecting || isAuthorizationPending ? ' is-loading' : ''}`, disabled: !canConnect, "aria-busy": isConnecting || isAuthorizationPending || undefined, "aria-label": isAuthorizationPending ? t('Authorization pending') : t('Connect'), title: isAuthorizationPending ? t('Authorization in progress') : t('Connect'), tabIndex: disabled ? -1 : undefined, onMouseDown: stop, onKeyDown: stop, onClick: (e) => {
54
- stop(e);
55
- onConnect(connector.id);
56
- }, ...agentHandleProps({}, { base: agentHandle, ...({ action: 'connect', role: 'button', label: t('Connect') }) }), children: _jsx(Icon, { name: isConnecting || isAuthorizationPending ? 'spinner' : 'plus', size: 12 }) })), isAuthorizationPending ? (_jsx("button", { type: "button", className: "icon-only connector-action is-cancel-authorization", "aria-label": t('Cancel authorization'), title: t('Cancel authorization'), onMouseDown: stop, onKeyDown: stop, onClick: (e) => {
57
- stop(e);
58
- onCancelAuthorization(connector.id);
59
- }, children: _jsx(Icon, { name: "close", size: 12 }) })) : null, connector.status === 'error' || connector.status === 'disabled' ? (_jsx("span", { className: `connector-status-pill status-${connector.status}`, children: t(statusLabel(connector.status)) })) : null] })] }), authorizationCancelFailed ? (_jsx("p", { className: "connector-authorization-hint connector-authorization-error", role: "alert", children: resolvedCancelFailedMessage })) : null, isAuthorizationPending && authorizationPending.redirectUrl ? (_jsx("button", { type: "button", className: "connector-authorization-link", title: t('Authorization in progress'), onClick: continueAuthorization, children: resolvedContinueInBrowserLabel })) : null] }));
88
+ return (_jsxs("article", { className: `connector-card status-${connector.status}${disabled ? ' is-locked' : ''}`, "data-connector-id": connector.id, role: "button", tabIndex: disabled ? -1 : 0, "aria-disabled": disabled || undefined, "aria-label": t('Open details for {name}', { name: connector.name }), onClick: openDetails, onKeyDown: onKeyActivate, ...agentHandleProps({}, { base: agentHandle, role: 'button', label: connector.name }), children: [_jsxs("div", { className: "connector-card-top", children: [_jsx(ConnectorLogo, { connectorId: connector.id, connectorName: connector.name, logoUrl: connector.logoUrl, size: "sm" }), _jsxs("div", { className: "connector-card-head", children: [_jsxs("h3", { className: "connector-card-title", children: [_jsx("span", { className: "connector-card-title-name", children: connector.name }), _jsx(ConnectorCardTitleDot, { status: connector.status, isAuthorizationPending: flags.isAuthorizationPending })] }), _jsxs("div", { className: "connector-meta", children: [_jsx("span", { className: "connector-meta-item connector-meta-category", title: categoryLabel, children: categoryLabel }), _jsx(ConnectorCardToolsBadge, { connector: connector, toolsLoaded: toolsLoaded })] })] }), _jsxs("div", { className: "connector-card-actions", children: [flags.isConnected ? (_jsx(ConnectorDisconnectButton, { ...actionProps, onActivate: () => onDisconnect(connector.id) })) : (_jsx(ConnectorConnectButton, { ...actionProps, onActivate: () => onConnect(connector.id) })), flags.isAuthorizationPending ? (_jsx(ConnectorCancelAuthorizationButton, { onActivate: () => onCancelAuthorization(connector.id) })) : null, _jsx(ConnectorStatusPill, { status: connector.status })] })] }), _jsx(ConnectorCardHints, { authorizationCancelFailed: authorizationCancelFailed, redirectUrl: flags.redirectUrl, cancelFailedMessage: cancelFailedMessage, continueInBrowserLabel: continueInBrowserLabel, onOpenExternalUrl: onOpenExternalUrl })] }));
60
89
  }
61
90
  //# sourceMappingURL=ConnectorCard.js.map