@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
@@ -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
@@ -2,38 +2,19 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
2
2
  import { useEffect, useRef } from 'react';
3
3
  import { agentHandleProps } from '@jini-ai/agentic';
4
4
  import { useT } from '../../i18n/index.js';
5
- import { getConnectorDisplayToolCount, getDisplayableConnectorAccountLabel, statusLabel, toolsBadgeTranslation, } from '../rules.js';
5
+ import { connectorActionFlags, getConnectorDisplayToolCount, getDisplayableConnectorAccountLabel, statusLabel, toolsBadgeTranslation, } from '../rules.js';
6
6
  import { Icon } from '../../../react/components/Icon.js';
7
7
  import { ConnectorLogo } from './ConnectorLogo.js';
8
- /**
9
- * Modal detail drawer with a paginated tool list. Keeps its own
10
- * escape-key-to-close and body-scroll-lock behavior inline (standard modal
11
- * a11y idioms, not business/transport logic) — see
12
- * `packages/ui/source-map.md`'s purity-check note for why these two
13
- * `document`/`window` touches were kept here rather than routed through
14
- * `dependencies.ts`.
15
- */
16
- export function ConnectorDetailDrawer({ connector, disabled, pendingAction, authorizationPending, authorizationCancelFailed, authorizationError, toolsPreviewLoading, toolsLoaded, onClose, onConnect, onDisconnect, onCancelAuthorization, onLoadMoreTools, onOpenExternalUrl, getCategoryLabel = (category) => category, getDisplayableAccountLabel = getDisplayableConnectorAccountLabel, cancelFailedMessage, continueInBrowserLabel, agentHandle, }) {
8
+ function ConnectorDrawerHead({ connector, categoryLabel, isAuthorizationPending, toolsLoaded, closeBtnRef, onClose, agentHandle, }) {
17
9
  const t = useT();
18
- const resolvedCancelFailedMessage = cancelFailedMessage ?? t("Couldn't cancel authorization. Try again.");
19
- const resolvedContinueInBrowserLabel = continueInBrowserLabel ?? t('Continue in browser');
20
- const isConnected = connector.status === 'connected';
21
- const isConnecting = pendingAction === 'connect';
22
- const isDisconnecting = pendingAction === 'disconnect';
23
- const isAuthorizationPending = !isConnected && authorizationPending !== undefined;
24
- const isPending = pendingAction !== null || isAuthorizationPending;
25
- const canConnect = !disabled && !isPending && connector.status === 'available';
26
- const canDisconnect = !disabled && !isPending && isConnected;
27
- const accountLabel = getDisplayableAccountLabel(connector);
28
- const actualToolCount = connector.tools.length;
29
- const toolCount = getConnectorDisplayToolCount(connector);
30
- const isLoadingTools = toolsPreviewLoading || !toolsLoaded;
31
- const toolDetailsUnavailable = toolsLoaded && actualToolCount === 0 && toolCount > 0;
32
- const showToolsBadge = connector.toolCount !== undefined || actualToolCount > 0 || toolsLoaded;
33
- const closeBtnRef = useRef(null);
34
- const categoryLabel = getCategoryLabel(connector.category);
35
- const toolsBadge = toolsBadgeTranslation(toolCount);
10
+ const showToolsBadge = connector.toolCount !== undefined || connector.tools.length > 0 || toolsLoaded;
11
+ const toolsBadge = toolsBadgeTranslation(getConnectorDisplayToolCount(connector));
36
12
  const toolsBadgeLabel = t(toolsBadge.key, toolsBadge.vars);
13
+ const statusTone = isAuthorizationPending ? 'pending' : connector.status;
14
+ return (_jsxs("header", { className: "connector-drawer-head", children: [_jsx(ConnectorLogo, { connectorId: connector.id, connectorName: connector.name, logoUrl: connector.logoUrl, size: "lg" }), _jsxs("div", { className: "connector-drawer-titles", children: [_jsxs("div", { className: "connector-drawer-eyebrow", children: [_jsx("span", { children: categoryLabel }), _jsx("span", { className: "connector-meta-dot", "aria-hidden": true, children: "\u00B7" }), _jsx("span", { children: connector.provider })] }), _jsx("h2", { id: "connector-drawer-title", children: connector.name }), _jsxs("div", { className: "connector-drawer-status", children: [_jsxs("span", { className: `connector-status-pill status-${statusTone}`, children: [_jsx("span", { className: "connector-status-dot", "aria-hidden": true }), isAuthorizationPending ? t('Authorization pending') : t(statusLabel(connector.status))] }), showToolsBadge ? (_jsx("span", { className: "connector-drawer-tool-count-chip", title: toolsBadgeLabel, children: _jsx("span", { children: toolsBadgeLabel }) })) : null] })] }), _jsx("button", { ref: closeBtnRef, type: "button", className: "ghost connector-drawer-close", onClick: onClose, "aria-label": t('Close'), "data-testid": "connector-drawer-close", ...agentHandleProps({}, { base: agentHandle, action: 'close', role: 'button', label: t('Close') }), children: _jsx(Icon, { name: "close", size: 14 }) })] }));
15
+ }
16
+ function ConnectorDrawerAbout({ description, isAuthorizationPending, redirectUrl, continueInBrowserLabel, onOpenExternalUrl, }) {
17
+ const t = useT();
37
18
  function continueAuthorization(event) {
38
19
  event.stopPropagation();
39
20
  // `continueAuthorization`'s only call site (the "continue in browser"
@@ -43,8 +24,44 @@ export function ConnectorDetailDrawer({ connector, disabled, pendingAction, auth
43
24
  // re-check here was dead code for every real click, removed rather
44
25
  // than tested around (same pattern as this package's `ConnectorCard.tsx`
45
26
  // precedent; see packages/ui/source-map.md's 2026-07-22 dated entry).
46
- onOpenExternalUrl?.(authorizationPending.redirectUrl);
27
+ onOpenExternalUrl?.(redirectUrl);
47
28
  }
29
+ return (_jsxs("section", { className: "connector-drawer-section", children: [_jsx("h3", { className: "connector-drawer-section-title", children: t('About') }), _jsx("p", { className: "connector-drawer-description", children: description }), isAuthorizationPending ? (_jsxs("div", { className: "connector-authorization-block", role: "status", children: [_jsx("p", { className: "connector-authorization-hint", children: t('Authorization in progress. It should finish shortly.') }), redirectUrl ? (_jsx("button", { type: "button", className: "connector-authorization-link", onClick: continueAuthorization, children: continueInBrowserLabel ?? t('Continue in browser') })) : null] })) : null] }));
30
+ }
31
+ function ConnectorDrawerAlert({ message }) {
32
+ return (_jsx("p", { className: "connector-authorization-hint connector-authorization-error", role: "alert", children: message }));
33
+ }
34
+ function ConnectorDrawerDetails({ connector, categoryLabel, accountLabel, flags, onDisconnect, agentHandle }) {
35
+ const t = useT();
36
+ const { isDisconnecting } = flags;
37
+ return (_jsxs("section", { className: "connector-drawer-section", children: [_jsxs("div", { className: "connector-drawer-section-head", children: [_jsx("h3", { className: "connector-drawer-section-title", children: t('Details') }), flags.isConnected ? (_jsxs("button", { type: "button", className: `ghost connector-drawer-inline-action connector-action is-disconnect${isDisconnecting ? ' is-loading' : ''}`, disabled: !flags.canDisconnect, "aria-busy": isDisconnecting || undefined, onClick: () => onDisconnect(connector.id), ...agentHandleProps({}, { base: agentHandle, action: 'disconnect', role: 'button', label: t('Disconnect') }), children: [isDisconnecting ? _jsx(Icon, { name: "spinner", size: 12 }) : null, _jsx("span", { children: t('Disconnect') })] })) : null] }), _jsxs("dl", { className: "connector-drawer-details", children: [_jsxs("div", { children: [_jsx("dt", { children: t('Status') }), _jsx("dd", { children: t(statusLabel(connector.status)) })] }), _jsxs("div", { children: [_jsx("dt", { children: t('Category') }), _jsx("dd", { children: categoryLabel })] }), _jsxs("div", { children: [_jsx("dt", { children: t('Provider') }), _jsx("dd", { children: connector.provider })] }), accountLabel ? (_jsxs("div", { children: [_jsx("dt", { children: t('Account') }), _jsx("dd", { children: accountLabel })] })) : null, connector.lastError ? (_jsxs("div", { className: "connector-drawer-details-error", children: [_jsx("dt", { children: t('Error') }), _jsx("dd", { children: connector.lastError })] })) : null] })] }));
38
+ }
39
+ function ConnectorDrawerTool({ tool }) {
40
+ return (_jsxs("li", { className: "connector-drawer-tool", children: [_jsxs("div", { className: "connector-drawer-tool-head", children: [_jsx("span", { className: "connector-drawer-tool-title", children: tool.title || tool.name }), _jsx("span", { className: `connector-drawer-tool-badge side-${tool.safety.sideEffect}`, title: tool.safety.reason, children: tool.safety.sideEffect })] }), tool.description ? _jsx("p", { className: "connector-drawer-tool-desc", children: tool.description }) : null, _jsx("code", { className: "connector-drawer-tool-name", children: tool.name })] }));
41
+ }
42
+ /** The tools section body: loading, unavailable, empty, or the list with an optional load-more. */
43
+ function ConnectorDrawerToolsBody({ connector, toolsPreviewLoading, toolsLoaded, onLoadMoreTools }) {
44
+ const t = useT();
45
+ const actualToolCount = connector.tools.length;
46
+ const toolCount = getConnectorDisplayToolCount(connector);
47
+ if (toolsPreviewLoading || !toolsLoaded) {
48
+ return (_jsxs("p", { className: "connector-drawer-empty", children: [_jsx(Icon, { name: "spinner", size: 12 }), " ", t('Loading tools…')] }));
49
+ }
50
+ if (actualToolCount === 0 && toolCount > 0) {
51
+ return _jsx("p", { className: "connector-drawer-empty", children: t('Tool details unavailable ({count})', { count: toolCount }) });
52
+ }
53
+ if (actualToolCount === 0)
54
+ return _jsx("p", { className: "connector-drawer-empty", children: t('No tools available') });
55
+ return (_jsxs(_Fragment, { children: [_jsx("ul", { className: "connector-drawer-tools", children: connector.tools.map((tool) => (_jsx(ConnectorDrawerTool, { tool: tool }, tool.name))) }), connector.toolsNextCursor ? (_jsx("button", { type: "button", className: "ghost connector-drawer-load-more", disabled: toolsPreviewLoading, onClick: () => onLoadMoreTools(connector.id, connector.toolsNextCursor), children: _jsx("span", { children: t('Load more tools') }) })) : null] }));
56
+ }
57
+ function ConnectorDrawerFoot({ connectorId, flags, onConnect, onCancelAuthorization, agentHandle }) {
58
+ const t = useT();
59
+ const { isAuthorizationPending } = flags;
60
+ const isBusy = flags.isConnecting || isAuthorizationPending;
61
+ return (_jsxs("footer", { className: "connector-drawer-foot", children: [_jsxs("button", { type: "button", className: `primary connector-action is-connect${isBusy ? ' is-loading' : ''}`, disabled: !flags.canConnect, "aria-busy": isBusy || undefined, onClick: () => onConnect(connectorId), ...agentHandleProps({}, { base: agentHandle, action: 'connect', role: 'button', label: t('Connect') }), children: [isBusy ? _jsx(Icon, { name: "spinner", size: 12 }) : null, _jsx("span", { children: isAuthorizationPending ? t('Authorization pending') : t('Connect') })] }), isAuthorizationPending ? (_jsx("button", { type: "button", className: "ghost connector-action is-cancel-authorization", onClick: () => onCancelAuthorization(connectorId), ...agentHandleProps({}, { base: agentHandle, action: 'cancel-authorization', role: 'button', label: t('Cancel authorization') }), children: _jsx("span", { children: t('Cancel authorization') }) })) : null] }));
62
+ }
63
+ /** Escape-to-close, initial focus on the close button, and body-scroll lock for the drawer's lifetime. */
64
+ function useDrawerModalBehavior({ onClose, closeBtnRef, }) {
48
65
  useEffect(() => {
49
66
  function onKey(e) {
50
67
  if (e.key === 'Escape') {
@@ -61,10 +78,24 @@ export function ConnectorDetailDrawer({ connector, disabled, pendingAction, auth
61
78
  document.body.style.overflow = previousOverflow;
62
79
  };
63
80
  }, [onClose]);
64
- const statusTone = isAuthorizationPending ? 'pending' : connector.status;
81
+ }
82
+ /**
83
+ * Modal detail drawer with a paginated tool list. Keeps its own
84
+ * escape-key-to-close and body-scroll-lock behavior inline (standard modal
85
+ * a11y idioms, not business/transport logic) — see
86
+ * `packages/ui/source-map.md`'s purity-check note for why these two
87
+ * `document`/`window` touches were kept here rather than routed through
88
+ * `dependencies.ts`.
89
+ */
90
+ export function ConnectorDetailDrawer({ connector, disabled, pendingAction, authorizationPending, authorizationCancelFailed, authorizationError, toolsPreviewLoading, toolsLoaded, onClose, onConnect, onDisconnect, onCancelAuthorization, onLoadMoreTools, onOpenExternalUrl, getCategoryLabel = (category) => category, getDisplayableAccountLabel = getDisplayableConnectorAccountLabel, cancelFailedMessage, continueInBrowserLabel, agentHandle, }) {
91
+ const t = useT();
92
+ const flags = connectorActionFlags({ connector, pendingAction, authorizationPending, disabled });
93
+ const closeBtnRef = useRef(null);
94
+ const categoryLabel = getCategoryLabel(connector.category);
95
+ useDrawerModalBehavior({ onClose, closeBtnRef });
65
96
  return (_jsx("div", { className: "connector-drawer-backdrop", role: "presentation", onMouseDown: (e) => {
66
97
  if (e.target === e.currentTarget)
67
98
  onClose();
68
- }, children: _jsxs("aside", { className: "connector-drawer", role: "dialog", "aria-modal": "true", "aria-labelledby": "connector-drawer-title", "data-testid": "connector-drawer", onClick: (e) => e.stopPropagation(), ...agentHandleProps({}, { base: agentHandle, ...({ role: 'region', label: connector.name }) }), children: [_jsxs("header", { className: "connector-drawer-head", children: [_jsx(ConnectorLogo, { connectorId: connector.id, connectorName: connector.name, logoUrl: connector.logoUrl, size: "lg" }), _jsxs("div", { className: "connector-drawer-titles", children: [_jsxs("div", { className: "connector-drawer-eyebrow", children: [_jsx("span", { children: categoryLabel }), _jsx("span", { className: "connector-meta-dot", "aria-hidden": true, children: "\u00B7" }), _jsx("span", { children: connector.provider })] }), _jsx("h2", { id: "connector-drawer-title", children: connector.name }), _jsxs("div", { className: "connector-drawer-status", children: [_jsxs("span", { className: `connector-status-pill status-${statusTone}`, children: [_jsx("span", { className: "connector-status-dot", "aria-hidden": true }), isAuthorizationPending ? t('Authorization pending') : t(statusLabel(connector.status))] }), showToolsBadge ? (_jsx("span", { className: "connector-drawer-tool-count-chip", title: toolsBadgeLabel, children: _jsx("span", { children: toolsBadgeLabel }) })) : null] })] }), _jsx("button", { ref: closeBtnRef, type: "button", className: "ghost connector-drawer-close", onClick: onClose, "aria-label": t('Close'), "data-testid": "connector-drawer-close", ...agentHandleProps({}, { base: agentHandle, ...({ action: 'close', role: 'button', label: t('Close') }) }), children: _jsx(Icon, { name: "close", size: 14 }) })] }), _jsxs("div", { className: "connector-drawer-body", children: [connector.description ? (_jsxs("section", { className: "connector-drawer-section", children: [_jsx("h3", { className: "connector-drawer-section-title", children: t('About') }), _jsx("p", { className: "connector-drawer-description", children: connector.description }), isAuthorizationPending ? (_jsxs("div", { className: "connector-authorization-block", role: "status", children: [_jsx("p", { className: "connector-authorization-hint", children: t('Authorization in progress. It should finish shortly.') }), authorizationPending.redirectUrl ? (_jsx("button", { type: "button", className: "connector-authorization-link", onClick: continueAuthorization, children: resolvedContinueInBrowserLabel })) : null] })) : null] })) : null, authorizationError ? (_jsx("p", { className: "connector-authorization-hint connector-authorization-error", role: "alert", children: authorizationError })) : null, authorizationCancelFailed ? (_jsx("p", { className: "connector-authorization-hint connector-authorization-error", role: "alert", children: resolvedCancelFailedMessage })) : null, _jsxs("section", { className: "connector-drawer-section", children: [_jsxs("div", { className: "connector-drawer-section-head", children: [_jsx("h3", { className: "connector-drawer-section-title", children: t('Details') }), isConnected ? (_jsxs("button", { type: "button", className: `ghost connector-drawer-inline-action connector-action is-disconnect${isDisconnecting ? ' is-loading' : ''}`, disabled: !canDisconnect, "aria-busy": isDisconnecting || undefined, onClick: () => onDisconnect(connector.id), ...agentHandleProps({}, { base: agentHandle, ...({ action: 'disconnect', role: 'button', label: t('Disconnect') }) }), children: [isDisconnecting ? _jsx(Icon, { name: "spinner", size: 12 }) : null, _jsx("span", { children: t('Disconnect') })] })) : null] }), _jsxs("dl", { className: "connector-drawer-details", children: [_jsxs("div", { children: [_jsx("dt", { children: t('Status') }), _jsx("dd", { children: t(statusLabel(connector.status)) })] }), _jsxs("div", { children: [_jsx("dt", { children: t('Category') }), _jsx("dd", { children: categoryLabel })] }), _jsxs("div", { children: [_jsx("dt", { children: t('Provider') }), _jsx("dd", { children: connector.provider })] }), accountLabel ? (_jsxs("div", { children: [_jsx("dt", { children: t('Account') }), _jsx("dd", { children: accountLabel })] })) : null, connector.lastError ? (_jsxs("div", { className: "connector-drawer-details-error", children: [_jsx("dt", { children: t('Error') }), _jsx("dd", { children: connector.lastError })] })) : null] })] }), _jsxs("section", { className: "connector-drawer-section", children: [_jsxs("h3", { className: "connector-drawer-section-title", children: [t('Tools'), " ", _jsx("span", { className: "connector-drawer-count", children: toolCount })] }), isLoadingTools ? (_jsxs("p", { className: "connector-drawer-empty", children: [_jsx(Icon, { name: "spinner", size: 12 }), " ", t('Loading tools…')] })) : toolDetailsUnavailable ? (_jsx("p", { className: "connector-drawer-empty", children: t('Tool details unavailable ({count})', { count: toolCount }) })) : actualToolCount === 0 ? (_jsx("p", { className: "connector-drawer-empty", children: t('No tools available') })) : (_jsxs(_Fragment, { children: [_jsx("ul", { className: "connector-drawer-tools", children: connector.tools.map((tool) => (_jsxs("li", { className: "connector-drawer-tool", children: [_jsxs("div", { className: "connector-drawer-tool-head", children: [_jsx("span", { className: "connector-drawer-tool-title", children: tool.title || tool.name }), _jsx("span", { className: `connector-drawer-tool-badge side-${tool.safety.sideEffect}`, title: tool.safety.reason, children: tool.safety.sideEffect })] }), tool.description ? _jsx("p", { className: "connector-drawer-tool-desc", children: tool.description }) : null, _jsx("code", { className: "connector-drawer-tool-name", children: tool.name })] }, tool.name))) }), connector.toolsNextCursor ? (_jsx("button", { type: "button", className: "ghost connector-drawer-load-more", disabled: toolsPreviewLoading, onClick: () => onLoadMoreTools(connector.id, connector.toolsNextCursor), children: _jsx("span", { children: t('Load more tools') }) })) : null] }))] })] }), !isConnected ? (_jsxs("footer", { className: "connector-drawer-foot", children: [_jsxs("button", { type: "button", className: `primary connector-action is-connect${isConnecting || isAuthorizationPending ? ' is-loading' : ''}`, disabled: !canConnect, "aria-busy": isConnecting || isAuthorizationPending || undefined, onClick: () => onConnect(connector.id), ...agentHandleProps({}, { base: agentHandle, ...({ action: 'connect', role: 'button', label: t('Connect') }) }), children: [isConnecting || isAuthorizationPending ? _jsx(Icon, { name: "spinner", size: 12 }) : null, _jsx("span", { children: isAuthorizationPending ? t('Authorization pending') : t('Connect') })] }), isAuthorizationPending ? (_jsx("button", { type: "button", className: "ghost connector-action is-cancel-authorization", onClick: () => onCancelAuthorization(connector.id), ...agentHandleProps({}, { base: agentHandle, ...({ action: 'cancel-authorization', role: 'button', label: t('Cancel authorization') }) }), children: _jsx("span", { children: t('Cancel authorization') }) })) : null] })) : null] }) }));
99
+ }, children: _jsxs("aside", { className: "connector-drawer", role: "dialog", "aria-modal": "true", "aria-labelledby": "connector-drawer-title", "data-testid": "connector-drawer", onClick: (e) => e.stopPropagation(), ...agentHandleProps({}, { base: agentHandle, role: 'region', label: connector.name }), children: [_jsx(ConnectorDrawerHead, { connector: connector, categoryLabel: categoryLabel, isAuthorizationPending: flags.isAuthorizationPending, toolsLoaded: toolsLoaded, closeBtnRef: closeBtnRef, onClose: onClose, agentHandle: agentHandle }), _jsxs("div", { className: "connector-drawer-body", children: [connector.description ? (_jsx(ConnectorDrawerAbout, { description: connector.description, isAuthorizationPending: flags.isAuthorizationPending, redirectUrl: flags.redirectUrl, continueInBrowserLabel: continueInBrowserLabel, onOpenExternalUrl: onOpenExternalUrl })) : null, authorizationError ? _jsx(ConnectorDrawerAlert, { message: authorizationError }) : null, authorizationCancelFailed ? (_jsx(ConnectorDrawerAlert, { message: cancelFailedMessage ?? t("Couldn't cancel authorization. Try again.") })) : null, _jsx(ConnectorDrawerDetails, { connector: connector, categoryLabel: categoryLabel, accountLabel: getDisplayableAccountLabel(connector), flags: flags, onDisconnect: onDisconnect, agentHandle: agentHandle }), _jsxs("section", { className: "connector-drawer-section", children: [_jsxs("h3", { className: "connector-drawer-section-title", children: [t('Tools'), " ", _jsx("span", { className: "connector-drawer-count", children: getConnectorDisplayToolCount(connector) })] }), _jsx(ConnectorDrawerToolsBody, { connector: connector, toolsPreviewLoading: toolsPreviewLoading, toolsLoaded: toolsLoaded, onLoadMoreTools: onLoadMoreTools })] })] }), !flags.isConnected ? (_jsx(ConnectorDrawerFoot, { connectorId: connector.id, flags: flags, onConnect: onConnect, onCancelAuthorization: onCancelAuthorization, agentHandle: agentHandle })) : null] }) }));
69
100
  }
70
101
  //# sourceMappingURL=ConnectorDetailDrawer.js.map
@@ -3,13 +3,18 @@ import { agentHandleProps, buildAgentListHandles } from '@jini-ai/agentic';
3
3
  import { useT } from '../../i18n/index.js';
4
4
  import { ConnectorCard } from './ConnectorCard.js';
5
5
  import { ConnectorGate } from './ConnectorGate.js';
6
+ /** Search-empty state: the no-match title/body and a clear-search action, each copy line host-overridable. */
7
+ function ConnectorGridEmpty({ searchQuery, onClearSearch, emptyNoMatchTitle, emptyNoMatchBody, emptyNoMatchAction, agentHandle, }) {
8
+ const t = useT();
9
+ const title = emptyNoMatchTitle
10
+ ? emptyNoMatchTitle(searchQuery.trim())
11
+ : t('No connectors match "{query}"', { query: searchQuery.trim() });
12
+ const action = emptyNoMatchAction ?? t('Clear search');
13
+ return (_jsxs("div", { className: "tab-empty connectors-empty", role: "status", "aria-live": "polite", "data-testid": "connectors-empty", children: [_jsx("p", { className: "connectors-empty-title", children: title }), _jsx("p", { className: "connectors-empty-body", children: emptyNoMatchBody ?? t('Try a different search term.') }), _jsx("button", { type: "button", className: "ghost connectors-empty-action", onClick: onClearSearch, ...agentHandleProps({}, { base: agentHandle, action: 'clear-search', role: 'button', label: action }), children: action })] }));
14
+ }
6
15
  /** Card grid + the search-empty-state and locked-gate overlays. */
7
16
  export function ConnectorGrid({ connectors, locked, hasNoResults, searchQuery, pendingConnectorAction, authorizationPending, authorizationCancelFailed, toolsLoaded, onConnect, onDisconnect, onCancelAuthorization, onOpenDetails, onOpenExternalUrl, getCategoryLabel, onClearSearch, gate, emptyNoMatchTitle, emptyNoMatchBody, emptyNoMatchAction, agentHandle, }) {
8
- const t = useT();
9
- const resolvedEmptyNoMatchTitle = emptyNoMatchTitle ?? ((query) => t('No connectors match "{query}"', { query }));
10
- const resolvedEmptyNoMatchBody = emptyNoMatchBody ?? t('Try a different search term.');
11
- const resolvedEmptyNoMatchAction = emptyNoMatchAction ?? t('Clear search');
12
- const cardHandles = agentHandle ? buildAgentListHandles({ prefix: agentHandle, ids: connectors.map((c) => c.id) }) : undefined;
13
- return (_jsxs("div", { className: `connector-grid-wrap${locked ? ' is-masked' : ''}`, "data-testid": "connector-grid-wrap", children: [hasNoResults && !locked ? (_jsxs("div", { className: "tab-empty connectors-empty", role: "status", "aria-live": "polite", "data-testid": "connectors-empty", children: [_jsx("p", { className: "connectors-empty-title", children: resolvedEmptyNoMatchTitle(searchQuery.trim()) }), _jsx("p", { className: "connectors-empty-body", children: resolvedEmptyNoMatchBody }), _jsx("button", { type: "button", className: "ghost connectors-empty-action", onClick: onClearSearch, ...agentHandleProps({}, { base: agentHandle, ...({ action: 'clear-search', role: 'button', label: resolvedEmptyNoMatchAction }) }), children: resolvedEmptyNoMatchAction })] })) : (_jsx("div", { className: "connector-grid", "aria-hidden": locked || undefined, children: connectors.map((connector, index) => (_jsx(ConnectorCard, { connector: connector, disabled: locked, pendingAction: pendingConnectorAction?.connectorId === connector.id ? pendingConnectorAction.action : null, authorizationPending: authorizationPending[connector.id], authorizationCancelFailed: authorizationCancelFailed[connector.id] === true, toolsLoaded: toolsLoaded, onConnect: onConnect, onDisconnect: onDisconnect, onCancelAuthorization: onCancelAuthorization, onOpenDetails: onOpenDetails, ...(getCategoryLabel ? { getCategoryLabel } : {}), ...(onOpenExternalUrl ? { onOpenExternalUrl } : {}), ...(cardHandles?.[index] ? { agentHandle: cardHandles[index] } : {}) }, connector.id))) })), locked && gate ? _jsx(ConnectorGate, { ...gate }) : null] }));
17
+ const cardHandles = agentHandle ? buildAgentListHandles({ prefix: agentHandle, ids: connectors.map((c) => c.id) }) : [];
18
+ return (_jsxs("div", { className: `connector-grid-wrap${locked ? ' is-masked' : ''}`, "data-testid": "connector-grid-wrap", children: [hasNoResults && !locked ? (_jsx(ConnectorGridEmpty, { searchQuery: searchQuery, onClearSearch: onClearSearch, emptyNoMatchTitle: emptyNoMatchTitle, emptyNoMatchBody: emptyNoMatchBody, emptyNoMatchAction: emptyNoMatchAction, agentHandle: agentHandle })) : (_jsx("div", { className: "connector-grid", "aria-hidden": locked || undefined, children: connectors.map((connector, index) => (_jsx(ConnectorCard, { connector: connector, disabled: locked, pendingAction: pendingConnectorAction?.connectorId === connector.id ? pendingConnectorAction.action : null, authorizationPending: authorizationPending[connector.id], authorizationCancelFailed: authorizationCancelFailed[connector.id] === true, toolsLoaded: toolsLoaded, onConnect: onConnect, onDisconnect: onDisconnect, onCancelAuthorization: onCancelAuthorization, onOpenDetails: onOpenDetails, ...(getCategoryLabel ? { getCategoryLabel } : {}), ...(onOpenExternalUrl ? { onOpenExternalUrl } : {}), ...(cardHandles[index] ? { agentHandle: cardHandles[index] } : {}) }, connector.id))) })), locked && gate ? _jsx(ConnectorGate, { ...gate }) : null] }));
14
19
  }
15
20
  //# sourceMappingURL=ConnectorGrid.js.map
@@ -152,6 +152,12 @@ export function useConnectorAuthorization(port, authPendingStorage, authBridge,
152
152
  // (not stripped) as a guard against a future refactor breaking
153
153
  // that invariant — see packages/ui/archived provenance ledger's 2026-07-22
154
154
  // dated entry for the full proof.
155
+ // Correction (2026-10-21 coverage pass): the proof above misses one interleaving. While this
156
+ // sweep's `cancelConnectorAuthorization` call is still awaiting, a `runConnectorAction(id,
157
+ // 'connect')` that resolves with `{ error }` sets `authError[id]`; the cancel then succeeds and
158
+ // this clear removes that NEWER error. Reproduced in
159
+ // `__tests__/hooks/useConnectorAuthorization.stale-race.test.ts`; flagged for an owner decision
160
+ // on whether the sweep should leave a fresher error alone.
155
161
  setAuthError((curr) => {
156
162
  if (curr[connectorId] === undefined)
157
163
  return curr;
@@ -19,6 +19,8 @@ export interface ConnectorCatalogController {
19
19
  * fetch (so already-configured connectors render immediately), then a lazy
20
20
  * enrichment pass gated on `unlocked` — heavier, only worth it once the
21
21
  * host's provider is actually configured.
22
+ * Failed reads retain the current catalog, settle their loading state and
23
+ * report a fixed diagnostic; host/provider errors may contain credentials.
22
24
  */
23
25
  export declare function useConnectorCatalog(port: ConnectorsPort, options: ConnectorCatalogOptions): ConnectorCatalogController;
24
26
  //# sourceMappingURL=useConnectorCatalog.d.ts.map
@@ -5,6 +5,8 @@ import { mergeConnectors } from '../rules.js';
5
5
  * fetch (so already-configured connectors render immediately), then a lazy
6
6
  * enrichment pass gated on `unlocked` — heavier, only worth it once the
7
7
  * host's provider is actually configured.
8
+ * Failed reads retain the current catalog, settle their loading state and
9
+ * report a fixed diagnostic; host/provider errors may contain credentials.
8
10
  */
9
11
  export function useConnectorCatalog(port, options) {
10
12
  const { unlocked, refreshKey = 0 } = options;
@@ -16,12 +18,20 @@ export function useConnectorCatalog(port, options) {
16
18
  let cancelled = false;
17
19
  setLoading(true);
18
20
  setEnriched(false);
19
- (async () => {
20
- const next = await port.fetchConnectors();
21
- if (cancelled)
22
- return;
23
- setConnectors((curr) => mergeConnectors(curr, next));
24
- setLoading(false);
21
+ void (async () => {
22
+ try {
23
+ const next = await port.fetchConnectors();
24
+ if (!cancelled)
25
+ setConnectors((curr) => mergeConnectors(curr, next));
26
+ }
27
+ catch {
28
+ if (!cancelled)
29
+ console.error('[connectors] Catalog load failed.');
30
+ }
31
+ finally {
32
+ if (!cancelled)
33
+ setLoading(false);
34
+ }
25
35
  })();
26
36
  return () => {
27
37
  cancelled = true;
@@ -38,13 +48,22 @@ export function useConnectorCatalog(port, options) {
38
48
  return;
39
49
  let cancelled = false;
40
50
  setEnriching(true);
41
- (async () => {
42
- const next = await port.fetchConnectorEnrichment({ refresh: true });
43
- if (cancelled)
44
- return;
45
- setConnectors((curr) => mergeConnectors(curr, next));
46
- setEnriched(true);
47
- setEnriching(false);
51
+ void (async () => {
52
+ try {
53
+ const next = await port.fetchConnectorEnrichment({ refresh: true });
54
+ if (cancelled)
55
+ return;
56
+ setConnectors((curr) => mergeConnectors(curr, next));
57
+ setEnriched(true);
58
+ }
59
+ catch {
60
+ if (!cancelled)
61
+ console.error('[connectors] Catalog enrichment failed.');
62
+ }
63
+ finally {
64
+ if (!cancelled)
65
+ setEnriching(false);
66
+ }
48
67
  })();
49
68
  return () => {
50
69
  cancelled = true;
@@ -11,9 +11,9 @@ export interface UseWiredConnectorsBrowserParams {
11
11
  onConnectorsChanged?: (() => void) | undefined;
12
12
  onConnectorAuthResult?: ((event: ConnectorAuthResultEvent) => void) | undefined;
13
13
  /** Custom hook overrides for dependency injection / testing — mirrors `ConnectorsBrowserProps`. */
14
- useConnectorCatalog?: typeof useConnectorCatalog;
15
- useConnectorAuthorization?: typeof useConnectorAuthorization;
16
- useConnectorDetail?: typeof useConnectorDetail;
14
+ useConnectorCatalog?: typeof useConnectorCatalog | undefined;
15
+ useConnectorAuthorization?: typeof useConnectorAuthorization | undefined;
16
+ useConnectorDetail?: typeof useConnectorDetail | undefined;
17
17
  }
18
18
  export interface WiredConnectorsBrowserController {
19
19
  /** The resolved dependency bundle (host-supplied, or the package's fake) — still needed by the host
@@ -1,10 +1,17 @@
1
1
  /** Connector callback-origin validation and search/display sorting; no React, transport or DOM. */
2
- import type { Connector, ConnectorAuthorizationPendingState, ConnectorActionResult, ConnectorStatusMap, ConnectorPanelAlert, ConnectorStatus, ProviderTab } from './types.js';
2
+ import type { Connector, ConnectorAction, ConnectorActionFlags, ConnectorAuthorizationPending, ConnectorAuthorizationPendingState, ConnectorActionResult, ConnectorStatusMap, ConnectorPanelAlert, ConnectorStatus, ProviderTab } from './types.js';
3
3
  /** Restricts the catalog to a single provider tab's `match` predicate, defaulting to the first tab. */
4
4
  export declare function scopeConnectorsToProvider(connectors: Connector[], providerTabs: readonly ProviderTab[], selectedProviderId: string): Connector[];
5
5
  export declare function mergeConnectors(current: Connector[], incoming: Connector[]): Connector[];
6
6
  export declare function pruneConnectorAuthorizationPending(pending: ConnectorAuthorizationPendingState, nowMs: number): ConnectorAuthorizationPendingState;
7
7
  export declare function parseConnectorAuthorizationPendingState(raw: string): ConnectorAuthorizationPendingState;
8
+ /** Derives the connect/disconnect/authorization-pending flags a card or drawer renders its actions from. */
9
+ export declare function connectorActionFlags({ connector, pendingAction, authorizationPending, disabled, }: {
10
+ connector: Connector;
11
+ pendingAction: ConnectorAction | null;
12
+ authorizationPending: ConnectorAuthorizationPending | undefined;
13
+ disabled: boolean;
14
+ }): ConnectorActionFlags;
8
15
  export declare function updateConnectorAuthorizationPendingFromConnectResponse(pending: ConnectorAuthorizationPendingState, response: ConnectorActionResult, nowMs: number): ConnectorAuthorizationPendingState;
9
16
  export declare function updateConnectorAuthorizationPendingFromStatuses(pending: ConnectorAuthorizationPendingState, statuses: ConnectorStatusMap, nowMs: number): ConnectorAuthorizationPendingState;
10
17
  export declare function clearConnectorAuthorizationErrorsForConnected(errors: Record<string, string>, statuses: ConnectorStatusMap): Record<string, string>;
@@ -45,32 +45,54 @@ export function pruneConnectorAuthorizationPending(pending, nowMs) {
45
45
  }
46
46
  return next;
47
47
  }
48
+ function isPlainRecord(value) {
49
+ return !!value && typeof value === 'object' && !Array.isArray(value);
50
+ }
51
+ function isNonBlankString(value) {
52
+ return typeof value === 'string' && !!value.trim();
53
+ }
54
+ function parsePendingEntry(state) {
55
+ if (!isPlainRecord(state))
56
+ return {};
57
+ const { expiresAt, redirectUrl } = state;
58
+ return {
59
+ ...(isNonBlankString(expiresAt) ? { expiresAt } : {}),
60
+ ...(isNonBlankString(redirectUrl) ? { redirectUrl } : {}),
61
+ };
62
+ }
48
63
  export function parseConnectorAuthorizationPendingState(raw) {
64
+ let parsed;
49
65
  try {
50
- const parsed = JSON.parse(raw);
51
- if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
52
- return {};
53
- const pending = {};
54
- for (const [connectorId, state] of Object.entries(parsed)) {
55
- if (!connectorId)
56
- continue;
57
- if (state && typeof state === 'object' && !Array.isArray(state)) {
58
- const expiresAt = state.expiresAt;
59
- const redirectUrl = state.redirectUrl;
60
- pending[connectorId] = {
61
- ...(typeof expiresAt === 'string' && expiresAt.trim() ? { expiresAt } : {}),
62
- ...(typeof redirectUrl === 'string' && redirectUrl.trim() ? { redirectUrl } : {}),
63
- };
64
- }
65
- else {
66
- pending[connectorId] = {};
67
- }
68
- }
69
- return pending;
66
+ parsed = JSON.parse(raw);
70
67
  }
71
68
  catch {
72
69
  return {};
73
70
  }
71
+ if (!isPlainRecord(parsed))
72
+ return {};
73
+ const pending = {};
74
+ for (const [connectorId, state] of Object.entries(parsed)) {
75
+ if (!connectorId)
76
+ continue;
77
+ pending[connectorId] = parsePendingEntry(state);
78
+ }
79
+ return pending;
80
+ }
81
+ /** Derives the connect/disconnect/authorization-pending flags a card or drawer renders its actions from. */
82
+ export function connectorActionFlags({ connector, pendingAction, authorizationPending, disabled, }) {
83
+ const isConnected = connector.status === 'connected';
84
+ const isAuthorizationPending = !isConnected && authorizationPending !== undefined;
85
+ const isPending = pendingAction !== null || isAuthorizationPending;
86
+ const canAct = !disabled && !isPending;
87
+ return {
88
+ isConnecting: pendingAction === 'connect',
89
+ isDisconnecting: pendingAction === 'disconnect',
90
+ isConnected,
91
+ isAuthorizationPending,
92
+ canConnect: canAct && connector.status === 'available',
93
+ canDisconnect: canAct && isConnected,
94
+ redirectUrl: isAuthorizationPending ? authorizationPending.redirectUrl : undefined,
95
+ };
74
96
  }
75
97
  export function updateConnectorAuthorizationPendingFromConnectResponse(pending, response, nowMs) {
76
98
  if (!response.connector)