@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
@@ -18,15 +18,32 @@ import { renderCheckbox } from './checkbox.js';
18
18
  import { renderChoiceGroup } from './choice-group.js';
19
19
  import { renderSelect } from './select.js';
20
20
  import { renderTextInput } from './text-input.js';
21
- /** Only the properties present on a given field are forwarded, because `exactOptionalPropertyTypes` makes `{ hint: undefined }` and `{}` different types. */
21
+ /** The listed properties that are present on `source`, in listed order.
22
+ * Only the properties present on a given field are forwarded, because `exactOptionalPropertyTypes` makes `{ hint: undefined }` and `{}` different types.
23
+ * @complexity O(k) in the key count. */
24
+ function present(source, keys) {
25
+ const picked = {};
26
+ for (const key of keys) {
27
+ const value = source[key];
28
+ if (value !== undefined)
29
+ picked[key] = value;
30
+ }
31
+ return picked;
32
+ }
22
33
  function base(field) {
23
- return {
24
- name: field.name,
25
- label: field.label,
26
- ...(field.hint === undefined ? {} : { hint: field.hint }),
27
- ...(field.required === undefined ? {} : { required: field.required }),
28
- ...(field.disabled === undefined ? {} : { disabled: field.disabled }),
29
- };
34
+ return { name: field.name, label: field.label, ...present(field, ['hint', 'required', 'disabled']) };
35
+ }
36
+ /** One-of-N, drawn as radios or a select per {@link EnumField.presentation}. @complexity O(n) in the rendered length. */
37
+ function renderEnumField(field) {
38
+ if (field.presentation === 'radio') {
39
+ return renderChoiceGroup({
40
+ ...base(field),
41
+ selection: 'single',
42
+ options: field.options,
43
+ ...(field.value === undefined ? {} : { value: [field.value] }),
44
+ });
45
+ }
46
+ return renderSelect({ ...base(field), options: field.options, ...present(field, ['value', 'placeholder']) });
30
47
  }
31
48
  /**
32
49
  * Renders the control fragment for one field, dispatching on its kind.
@@ -39,52 +56,15 @@ function base(field) {
39
56
  export function renderFieldControl(field, text) {
40
57
  switch (field.kind) {
41
58
  case 'string':
42
- return renderTextInput({
43
- ...base(field),
44
- ...text,
45
- ...(field.value === undefined ? {} : { value: field.value }),
46
- ...(field.placeholder === undefined ? {} : { placeholder: field.placeholder }),
47
- ...(field.multiline === undefined ? {} : { multiline: field.multiline }),
48
- ...(field.rows === undefined ? {} : { rows: field.rows }),
49
- ...(field.secret === undefined ? {} : { secret: field.secret }),
50
- });
59
+ return renderTextInput({ ...base(field), ...text, ...present(field, ['value', 'placeholder', 'multiline', 'rows', 'secret']) });
51
60
  case 'number':
52
- return renderTextInput({
53
- ...base(field),
54
- inputType: 'number',
55
- ...(field.value === undefined ? {} : { value: field.value }),
56
- ...(field.placeholder === undefined ? {} : { placeholder: field.placeholder }),
57
- ...(field.min === undefined ? {} : { min: field.min }),
58
- ...(field.max === undefined ? {} : { max: field.max }),
59
- ...(field.step === undefined ? {} : { step: field.step }),
60
- });
61
+ return renderTextInput({ ...base(field), inputType: 'number', ...present(field, ['value', 'placeholder', 'min', 'max', 'step']) });
61
62
  case 'boolean':
62
- return renderCheckbox({
63
- ...base(field),
64
- ...(field.value === undefined ? {} : { value: field.value }),
65
- });
63
+ return renderCheckbox({ ...base(field), ...present(field, ['value']) });
66
64
  case 'enum':
67
- if (field.presentation === 'radio') {
68
- return renderChoiceGroup({
69
- ...base(field),
70
- selection: 'single',
71
- options: field.options,
72
- ...(field.value === undefined ? {} : { value: [field.value] }),
73
- });
74
- }
75
- return renderSelect({
76
- ...base(field),
77
- options: field.options,
78
- ...(field.value === undefined ? {} : { value: field.value }),
79
- ...(field.placeholder === undefined ? {} : { placeholder: field.placeholder }),
80
- });
65
+ return renderEnumField(field);
81
66
  case 'multi-enum':
82
- return renderChoiceGroup({
83
- ...base(field),
84
- selection: 'multiple',
85
- options: field.options,
86
- ...(field.value === undefined ? {} : { value: field.value }),
87
- });
67
+ return renderChoiceGroup({ ...base(field), selection: 'multiple', options: field.options, ...present(field, ['value']) });
88
68
  }
89
69
  }
90
70
  /** Projects fields down to what the inline script needs, so the generated JS carries no labels, hints, or option lists it will never read. */
@@ -42,6 +42,12 @@ export interface FormSurfaceSpec {
42
42
  /** Styles the submit button as destructive. */
43
43
  readonly danger?: boolean;
44
44
  readonly submitLabel: string;
45
+ /** Alternative submit buttons collect the same fields, with explicit action params. */
46
+ readonly additionalSubmitActions?: readonly {
47
+ readonly id: string;
48
+ readonly label: string;
49
+ readonly params: Readonly<Record<string, unknown>>;
50
+ }[];
45
51
  /** The tool the collected values are sent to. */
46
52
  readonly toolName: string;
47
53
  /**
@@ -31,51 +31,61 @@ import { DEFAULT_SECRET_INPUT_TEXT } from './text-input.js';
31
31
  const DEFAULT_APP = { appName: 'jini-mcp-ui-form', appVersion: '1' };
32
32
  /** The form element's id, fixed so the emitted script can find it without a fragile selector. */
33
33
  const FORM_ELEMENT_ID = 'mcpui-form';
34
- /**
35
- * Renders the form dialog as a complete, self-contained HTML document.
36
- *
37
- * @param spec - See {@link FormSurfaceSpec}.
38
- * @returns The full HTML string.
39
- * @complexity O(n) in the number of fields plus the rendered length.
40
- */
41
- export function renderFormDocument(spec) {
42
- // A base param with a secret field's name would leak the same value through the emitted script,
43
- // even though the collected value overrides it at submit time. Reject that hidden prefill too.
34
+ /** A base param with a secret field's name would leak the same value through the emitted script,
35
+ * even though the collected value overrides it at submit time. Reject that hidden prefill too.
36
+ * @complexity O(n) in the field count. @throws Error with fixed text, never the field name or value. */
37
+ function assertNoSecretBaseParam(spec) {
44
38
  for (const field of spec.fields) {
45
39
  if (field.kind === 'string' && field.secret === true && spec.baseParams?.[field.name] !== undefined) {
46
40
  throw new Error('Secret fields cannot have a pre-filled value. Omit value.');
47
41
  }
48
42
  }
49
- const text = { ...DEFAULT_SURFACE_STATUS_TEXT, ...DEFAULT_SECRET_INPUT_TEXT, ...spec.text };
43
+ }
44
+ /** Submit, any alternative submits, then cancel. @complexity O(a) in the action count. */
45
+ function formActions(spec) {
50
46
  // Not `type: 'submit'` — see the runtime script below for why a submit-typed button inside this
51
47
  // particular `<form>` cannot work at all in production.
52
48
  const actions = [
53
49
  { id: 'submit', label: spec.submitLabel, variant: spec.danger === true ? 'danger' : 'primary' },
54
50
  ];
51
+ for (const action of spec.additionalSubmitActions ?? [])
52
+ actions.push({ id: `submit:${action.id}`, label: action.label, variant: 'neutral' });
55
53
  if (spec.cancel !== undefined)
56
54
  actions.push({ id: 'cancel', label: spec.cancel.label, variant: 'neutral' });
55
+ return actions;
56
+ }
57
+ /** Header, context, the `<form>` itself and the status region. @complexity O(n) in the rendered length. */
58
+ function formBodyHtml(spec, text) {
57
59
  const controls = spec.fields
58
60
  .map((field) => renderFieldControl(field, { showSecret: text.showSecret, hideSecret: text.hideSecret }))
59
61
  .join('\n');
60
62
  const warning = spec.warning === undefined ? '' : `<p class="mcpui-warning">${escapeHtml(spec.warning)}</p>`;
61
- const bodyHtml = [
63
+ return [
62
64
  renderSurfaceHeader(spec.description === undefined ? { title: spec.title } : { title: spec.title, description: spec.description }),
63
65
  renderDetailList(spec.details ?? []),
64
- `<form id="${FORM_ELEMENT_ID}" novalidate>\n<fieldset class="mcpui-fields">\n${controls}\n</fieldset>\n${warning}\n${renderActions(actions)}\n</form>`,
66
+ `<form id="${FORM_ELEMENT_ID}" novalidate>\n<fieldset class="mcpui-fields">\n${controls}\n</fieldset>\n${warning}\n${renderActions(formActions(spec))}\n</form>`,
65
67
  renderStatusRegion(),
66
68
  ]
67
69
  .filter((fragment) => fragment !== '')
68
70
  .join('\n');
69
- const cancelPlan = spec.cancel?.toolName === undefined
71
+ }
72
+ /** The cancel tool call, or null for a cancel that only tears the surface down. @complexity O(1). */
73
+ function cancelPlanOf(spec) {
74
+ return spec.cancel?.toolName === undefined
70
75
  ? null
71
76
  : { toolName: spec.cancel.toolName, params: spec.cancel.params ?? {} };
72
- const script = `(function () {
77
+ }
78
+ /** The inline runtime script that reads, validates and submits the fields. @complexity O(n) in the field count plus the script length. */
79
+ function formScript(spec, text) {
80
+ const cancelPlan = cancelPlanOf(spec);
81
+ return `(function () {
73
82
  "use strict";
74
83
  ${SURFACE_SCRIPT_PRELUDE}
75
84
  var FIELDS = ${escapeJsValue(toFieldReadSpecs(spec.fields))};
76
85
  var TOOL = ${escapeJsValue(spec.toolName)};
77
86
  var BASE_PARAMS = ${escapeJsValue(spec.baseParams ?? {})};
78
87
  var CANCEL = ${escapeJsValue(cancelPlan)};
88
+ var SUBMIT_ACTIONS = ${escapeJsValue(Object.fromEntries((spec.additionalSubmitActions ?? []).map(action => [`submit:${action.id}`, action.params])))};
79
89
  var TEXT = ${escapeJsValue(text)};
80
90
  var form = document.getElementById(${escapeJsValue(FORM_ELEMENT_ID)});
81
91
 
@@ -186,7 +196,7 @@ ${SURFACE_SCRIPT_PRELUDE}
186
196
  // says the dialog is no longer pending, where a retry cannot succeed (see reportCallFailure).
187
197
  var pending = false;
188
198
 
189
- function runSubmit() {
199
+ function runSubmit(actionParams) {
190
200
  if (pending) return;
191
201
  var params = {};
192
202
  var key;
@@ -201,6 +211,7 @@ ${SURFACE_SCRIPT_PRELUDE}
201
211
  setStatus(TEXT.missingPrefix + missing.join(", "), "invalid");
202
212
  return;
203
213
  }
214
+ for (key in actionParams) if (Object.prototype.hasOwnProperty.call(actionParams, key)) params[key] = actionParams[key];
204
215
  pending = true;
205
216
  setBusy(true);
206
217
  setStatus(TEXT.working, "pending");
@@ -225,14 +236,14 @@ ${SURFACE_SCRIPT_PRELUDE}
225
236
  var type = (target.getAttribute("type") || "text").toLowerCase();
226
237
  if (type === "checkbox" || type === "radio") return;
227
238
  event.preventDefault();
228
- runSubmit();
239
+ runSubmit({});
229
240
  });
230
241
 
231
242
  for (var b = 0; b < actionButtons.length; b++) {
232
243
  var action = actionButtons[b].getAttribute("data-mcpui-action");
233
- if (action === "submit") {
244
+ if (action === "submit" || Object.prototype.hasOwnProperty.call(SUBMIT_ACTIONS, action)) {
234
245
  actionButtons[b].addEventListener("click", function (event) {
235
- if (event.isTrusted === true) runSubmit();
246
+ if (event.isTrusted === true) runSubmit(SUBMIT_ACTIONS[event.currentTarget.getAttribute("data-mcpui-action")] || {});
236
247
  });
237
248
  continue;
238
249
  }
@@ -256,10 +267,21 @@ ${SURFACE_SCRIPT_PRELUDE}
256
267
  });
257
268
  }
258
269
  }());`;
270
+ }
271
+ /**
272
+ * Renders the form dialog as a complete, self-contained HTML document.
273
+ *
274
+ * @param spec - See {@link FormSurfaceSpec}.
275
+ * @returns The full HTML string.
276
+ * @complexity O(n) in the number of fields plus the rendered length.
277
+ */
278
+ export function renderFormDocument(spec) {
279
+ assertNoSecretBaseParam(spec);
280
+ const text = { ...DEFAULT_SURFACE_STATUS_TEXT, ...DEFAULT_SECRET_INPUT_TEXT, ...spec.text };
259
281
  return renderSurfaceDocument({
260
282
  title: spec.title,
261
- bodyHtml,
262
- script,
283
+ bodyHtml: formBodyHtml(spec, text),
284
+ script: formScript(spec, text),
263
285
  app: spec.app ?? DEFAULT_APP,
264
286
  ...(spec.lang === undefined ? {} : { lang: spec.lang }),
265
287
  ...(spec.tokens === undefined ? {} : { tokens: spec.tokens }),
@@ -48,6 +48,33 @@ import { renderActions, renderDetailList, renderStatusRegion, renderSurfaceDocum
48
48
  const DEFAULT_APP = { appName: 'jini-mcp-ui-outcome', appVersion: '1' };
49
49
  const DEFAULT_OPEN_LINK_LABEL = 'Open site';
50
50
  const OPEN_LINK_ACTION_ID = 'open-link';
51
+ /** The status region's `data-state` for an outcome state. @complexity O(1). */
52
+ function statusStateOf(state) {
53
+ if (state === 'success')
54
+ return 'done';
55
+ return state === 'partial' ? 'partial' : 'failed';
56
+ }
57
+ /** The open-link button, or nothing without a link. @complexity O(n) in the label length. */
58
+ function renderOpenLinkActions(spec) {
59
+ if (spec.openLinkUrl === undefined)
60
+ return '';
61
+ return renderActions([{ id: OPEN_LINK_ACTION_ID, label: spec.openLinkLabel ?? DEFAULT_OPEN_LINK_LABEL, variant: spec.state === 'success' ? 'primary' : 'neutral' }]);
62
+ }
63
+ /** The inline script: wires the open-link button, or nothing at all. @complexity O(n) in the URL length. */
64
+ function outcomeScript(openLinkUrl) {
65
+ // No prelude at all when there is nothing to wire up — a pure-display result needs no bridge
66
+ // calls and no `[data-mcpui-action]` scan.
67
+ if (openLinkUrl === undefined)
68
+ return '(function () { "use strict"; }());';
69
+ return `(function () {
70
+ "use strict";
71
+ ${SURFACE_SCRIPT_PRELUDE}
72
+ var OPEN_URL = ${escapeJsValue(openLinkUrl)};
73
+ for (var i = 0; i < actionButtons.length; i++) {
74
+ actionButtons[i].addEventListener("click", function () { api.openLink(OPEN_URL); });
75
+ }
76
+ }());`;
77
+ }
51
78
  /**
52
79
  * Renders the outcome document as a complete, self-contained HTML document.
53
80
  *
@@ -56,40 +83,23 @@ const OPEN_LINK_ACTION_ID = 'open-link';
56
83
  * @complexity O(n) in the rendered length.
57
84
  */
58
85
  export function renderOutcomeDocument(spec) {
59
- const statusState = spec.state === 'success' ? 'done' : spec.state === 'partial' ? 'partial' : 'failed';
60
86
  // Rendered directly into the status region rather than left for the script to set — this document
61
87
  // has nothing further to wait on (unlike a confirmation, which starts idle and only reaches "done"
62
88
  // after a click), so the true outcome should be visible from the FIRST paint, not a moment after
63
89
  // the script runs.
64
- const statusHtml = `<p class="mcpui-status" id="${SURFACE_STATUS_ELEMENT_ID}" role="status" aria-live="polite" data-state="${statusState}">${escapeHtml(spec.message)}</p>`;
65
- const hasOpenLink = spec.openLinkUrl !== undefined;
66
- const actionsHtml = hasOpenLink
67
- ? renderActions([{ id: OPEN_LINK_ACTION_ID, label: spec.openLinkLabel ?? DEFAULT_OPEN_LINK_LABEL, variant: spec.state === 'success' ? 'primary' : 'neutral' }])
68
- : '';
90
+ const statusHtml = `<p class="mcpui-status" id="${SURFACE_STATUS_ELEMENT_ID}" role="status" aria-live="polite" data-state="${statusStateOf(spec.state)}">${escapeHtml(spec.message)}</p>`;
69
91
  const bodyHtml = [
70
92
  renderSurfaceHeader(spec.description === undefined ? { title: spec.title } : { title: spec.title, description: spec.description }),
71
93
  renderDetailList(spec.details ?? [], { layout: 'outcome' }),
72
94
  statusHtml,
73
- actionsHtml,
95
+ renderOpenLinkActions(spec),
74
96
  ]
75
97
  .filter((fragment) => fragment !== '')
76
98
  .join('\n');
77
- const script = hasOpenLink
78
- ? `(function () {
79
- "use strict";
80
- ${SURFACE_SCRIPT_PRELUDE}
81
- var OPEN_URL = ${escapeJsValue(spec.openLinkUrl)};
82
- for (var i = 0; i < actionButtons.length; i++) {
83
- actionButtons[i].addEventListener("click", function () { api.openLink(OPEN_URL); });
84
- }
85
- }());`
86
- // No prelude at all when there is nothing to wire up — a pure-display result needs no bridge
87
- // calls and no `[data-mcpui-action]` scan.
88
- : '(function () { "use strict"; }());';
89
99
  return renderSurfaceDocument({
90
100
  title: spec.title,
91
101
  bodyHtml,
92
- script,
102
+ script: outcomeScript(spec.openLinkUrl),
93
103
  app: spec.app ?? DEFAULT_APP,
94
104
  ...(spec.lang === undefined ? {} : { lang: spec.lang }),
95
105
  ...(spec.tokens === undefined ? {} : { tokens: spec.tokens }),
@@ -29,6 +29,36 @@ function resolveInputType(props) {
29
29
  return 'number';
30
30
  return props.secret === true ? 'password' : 'text';
31
31
  }
32
+ /** The reveal toggle a multiline secret carries. @complexity O(n) in the label lengths. */
33
+ function renderSecretToggle(props, id) {
34
+ const showSecret = props.showSecret ?? DEFAULT_SECRET_INPUT_TEXT.showSecret;
35
+ const hideSecret = props.hideSecret ?? DEFAULT_SECRET_INPUT_TEXT.hideSecret;
36
+ return `<button type="button" class="mcpui-button" data-mcpui-secret-toggle="${escapeHtml(id)}" data-mcpui-show-secret="${escapeHtml(showSecret)}" data-mcpui-hide-secret="${escapeHtml(hideSecret)}" aria-controls="${escapeHtml(id)}" aria-pressed="false"${booleanAttribute('disabled', props.disabled)}>${escapeHtml(showSecret)}</button>`;
37
+ }
38
+ /** The `<textarea>` control, masked with a reveal toggle when secret. @complexity O(n) in the rendered length. */
39
+ function renderTextarea(props, required) {
40
+ const secret = props.secret === true;
41
+ return `<textarea class="mcpui-textarea"${required.common}${optionalAttribute('rows', props.rows)}` +
42
+ // Password inputs strip line breaks. CSS masking keeps the textarea's actual value intact,
43
+ // and disables browser text assistance that could retain or alter a pasted credential.
44
+ (secret ? ' data-mcpui-secret style="-webkit-text-security: disc;" autocomplete="off" spellcheck="false"' : '') +
45
+ `>${props.value === undefined ? '' : escapeHtml(String(props.value))}</textarea>` +
46
+ (secret ? renderSecretToggle(props, required.id) : '');
47
+ }
48
+ /** The single-line `<input>` control. @complexity O(n) in the rendered length. */
49
+ function renderInput(props, required) {
50
+ const { type, common } = required;
51
+ return `<input class="mcpui-input" type="${type}"${common}` +
52
+ optionalAttribute('value', props.value) +
53
+ // A stored secret (e.g. a cloud provider's access key or a database password) must never be
54
+ // autofilled with a saved website login. `new-password`, not `off`: Chrome ignores `off` on
55
+ // password inputs by design and fills the saved login anyway.
56
+ (type === 'password' ? optionalAttribute('autocomplete', 'new-password') : '') +
57
+ (type === 'number'
58
+ ? optionalAttribute('min', props.min) + optionalAttribute('max', props.max) + optionalAttribute('step', props.step)
59
+ : '') +
60
+ '>';
61
+ }
32
62
  /**
33
63
  * Renders one free-text field: its label, hint, and control, wrapped in `.mcpui-field`.
34
64
  *
@@ -49,27 +79,9 @@ export function renderTextInput(props) {
49
79
  booleanAttribute('disabled', props.disabled) +
50
80
  fieldDescribedBy(props);
51
81
  const type = resolveInputType(props);
52
- const isNumber = type === 'number';
53
- const secretMultiline = props.secret === true && props.multiline === true && !isNumber;
54
- const control = props.multiline === true && !isNumber
55
- ? `<textarea class="mcpui-textarea"${common}${optionalAttribute('rows', props.rows)}` +
56
- // Password inputs strip line breaks. CSS masking keeps the textarea's actual value intact,
57
- // and disables browser text assistance that could retain or alter a pasted credential.
58
- (secretMultiline ? ' data-mcpui-secret style="-webkit-text-security: disc;" autocomplete="off" spellcheck="false"' : '') +
59
- `>${props.value === undefined ? '' : escapeHtml(String(props.value))}</textarea>` +
60
- (secretMultiline
61
- ? `<button type="button" class="mcpui-button" data-mcpui-secret-toggle="${escapeHtml(id)}" data-mcpui-show-secret="${escapeHtml(props.showSecret ?? DEFAULT_SECRET_INPUT_TEXT.showSecret)}" data-mcpui-hide-secret="${escapeHtml(props.hideSecret ?? DEFAULT_SECRET_INPUT_TEXT.hideSecret)}" aria-controls="${escapeHtml(id)}" aria-pressed="false"${booleanAttribute('disabled', props.disabled)}>${escapeHtml(props.showSecret ?? DEFAULT_SECRET_INPUT_TEXT.showSecret)}</button>`
62
- : '')
63
- : `<input class="mcpui-input" type="${type}"${common}` +
64
- optionalAttribute('value', props.value) +
65
- // A stored secret (e.g. a cloud provider's access key or a database password) must never be
66
- // autofilled with a saved website login. `new-password`, not `off`: Chrome ignores `off` on
67
- // password inputs by design and fills the saved login anyway.
68
- (type === 'password' ? optionalAttribute('autocomplete', 'new-password') : '') +
69
- (isNumber
70
- ? optionalAttribute('min', props.min) + optionalAttribute('max', props.max) + optionalAttribute('step', props.step)
71
- : '') +
72
- '>';
82
+ const control = props.multiline === true && type !== 'number'
83
+ ? renderTextarea(props, { id, common })
84
+ : renderInput(props, { type, common });
73
85
  return `<div class="mcpui-field">\n${renderFieldLabel(props)}\n${control}\n</div>`;
74
86
  }
75
87
  //# sourceMappingURL=text-input.js.map
@@ -100,7 +100,12 @@ export declare const FORCE_LIGHT_SURFACE_THEME = true;
100
100
  * @returns A `:root { … }` block, then a `:root:root { … }` block holding the overrides (when any),
101
101
  * followed by a `@media (prefers-color-scheme: dark) { :root { … } }` block unless {@link FORCE_LIGHT_SURFACE_THEME} suppresses it, ready to paste into the first
102
102
  * `<style>` tag.
103
+ * @param options.forceLight - Defaults to {@link FORCE_LIGHT_SURFACE_THEME}. Exposed so the dark
104
+ * variant this function still knows how to emit is reachable (and verified) rather than dead code
105
+ * behind a module constant; every production caller keeps the default.
103
106
  * @complexity O(n) in the number of tokens.
104
107
  */
105
- export declare function renderTokenBlock(overrides?: Partial<Record<SurfaceTokenName, string>>): string;
108
+ export declare function renderTokenBlock(overrides?: Partial<Record<SurfaceTokenName, string>>, { forceLight }?: {
109
+ forceLight?: boolean;
110
+ }): string;
106
111
  //# sourceMappingURL=tokens.d.ts.map
@@ -148,9 +148,12 @@ export const FORCE_LIGHT_SURFACE_THEME = true;
148
148
  * @returns A `:root { … }` block, then a `:root:root { … }` block holding the overrides (when any),
149
149
  * followed by a `@media (prefers-color-scheme: dark) { :root { … } }` block unless {@link FORCE_LIGHT_SURFACE_THEME} suppresses it, ready to paste into the first
150
150
  * `<style>` tag.
151
+ * @param options.forceLight - Defaults to {@link FORCE_LIGHT_SURFACE_THEME}. Exposed so the dark
152
+ * variant this function still knows how to emit is reachable (and verified) rather than dead code
153
+ * behind a module constant; every production caller keeps the default.
151
154
  * @complexity O(n) in the number of tokens.
152
155
  */
153
- export function renderTokenBlock(overrides = {}) {
156
+ export function renderTokenBlock(overrides = {}, { forceLight = FORCE_LIGHT_SURFACE_THEME } = {}) {
154
157
  const declare = (entries) => entries.map(([name, value]) => ` ${name}: ${value};`).join('\n');
155
158
  const notOverridden = ([name]) => !(name in overrides);
156
159
  const light = declare([
@@ -162,7 +165,7 @@ export function renderTokenBlock(overrides = {}) {
162
165
  const overrideEntries = Object.entries(overrides);
163
166
  const overrideBlock = overrideEntries.length === 0 ? '' : `\n:root:root {\n${declare(overrideEntries)}\n}`;
164
167
  const lightBlock = `:root {\n${light}\n}${overrideBlock}`;
165
- if (FORCE_LIGHT_SURFACE_THEME)
168
+ if (forceLight)
166
169
  return lightBlock;
167
170
  const dark = declare([
168
171
  ...Object.entries(SURFACE_TOKENS_DARK).filter(notOverridden),
@@ -1,3 +1,4 @@
1
1
  import type { MediaProviderOption } from './types.js';
2
+ /** @deprecated Supply a host catalog. The former starter set is exported by integrations/media-providers/catalog. */
2
3
  export declare const DEFAULT_MEDIA_PROVIDER_CATALOG: readonly MediaProviderOption[];
3
4
  //# sourceMappingURL=constants.d.ts.map
@@ -1,82 +1,3 @@
1
- import { findProvider, canonicalMediaProviderId } from '@jini-ai/integrations/media-providers/catalog';
2
- /**
3
- * A small, vendor-shaped starting catalog — same convention as `execution/constants.ts`'s
4
- * `DEFAULT_PROVIDER_PRESETS`. Sourced by driving Open Design's own Settings → Media providers
5
- * panel directly (Playwright) and transcribing its real provider list, base URLs, and default
6
- * models. Pass your own `catalog` to `MediaProvidersTab` to replace this wholesale; Zana or any
7
- * other host is expected to do so once it has its own provider roster.
8
- */
9
- const starterCatalog = [
10
- {
11
- id: 'nano-banana',
12
- label: 'Nano Banana',
13
- models: ['gemini-3.1-flash-image-preview'],
14
- },
15
- {
16
- id: 'aihubmix',
17
- label: 'AIHubMix',
18
- models: ['gemini-3.1-flash-image-preview'],
19
- },
20
- {
21
- id: 'custom-image-api',
22
- label: 'Custom Media API',
23
- models: ['gemini-3.1-flash-image-preview'],
24
- },
25
- {
26
- id: 'elevenlabs',
27
- label: 'ElevenLabs',
28
- },
29
- {
30
- id: 'fal-ai',
31
- label: 'Fal.ai',
32
- models: ['gemini-3.1-flash-image-preview'],
33
- },
34
- {
35
- id: 'fishaudio',
36
- label: 'FishAudio',
37
- },
38
- {
39
- id: 'imagerouter',
40
- label: 'ImageRouter',
41
- models: ['gemini-3.1-flash-image-preview'],
42
- },
43
- {
44
- id: 'leonardo-ai',
45
- label: 'Leonardo.ai',
46
- },
47
- {
48
- id: 'minimax',
49
- label: 'MiniMax',
50
- },
51
- {
52
- id: 'openai',
53
- label: 'OpenAI',
54
- models: ['gpt-image-2', 'dall-e-3'],
55
- },
56
- {
57
- id: 'openrouter',
58
- label: 'OpenRouter',
59
- },
60
- {
61
- id: 'senseaudio',
62
- label: 'SenseAudio',
63
- },
64
- {
65
- id: 'tavily-search',
66
- label: 'Tavily Search',
67
- },
68
- {
69
- id: 'volcengine-ark',
70
- label: 'Volcengine Ark (Doubao)',
71
- },
72
- {
73
- id: 'xai-grok-imagine',
74
- label: 'xAI Grok Imagine',
75
- },
76
- ];
77
- export const DEFAULT_MEDIA_PROVIDER_CATALOG = starterCatalog.map((entry) => {
78
- const id = canonicalMediaProviderId({ id: entry.id });
79
- const provider = findProvider({ id });
80
- return { ...entry, id, label: provider.label, ...(provider.defaultBaseUrl ? { defaultBaseUrl: provider.defaultBaseUrl } : {}) };
81
- });
1
+ /** @deprecated Supply a host catalog. The former starter set is exported by integrations/media-providers/catalog. */
2
+ export const DEFAULT_MEDIA_PROVIDER_CATALOG = [];
82
3
  //# sourceMappingURL=constants.js.map
@@ -1,6 +1,6 @@
1
1
  export { DEFAULT_MEDIA_PROVIDER_CATALOG } from './constants.js';
2
2
  export { createFakeMediaProvidersPort, type FakeMediaProvidersPortOptions } from './dependencies.js';
3
- export type { MediaProvidersPort } from './ports.js';
3
+ export type { MediaProvidersPort, MediaProviderCatalogPolicy } from './ports.js';
4
4
  export { hasAnyConfiguredProvider, hasRecoverableFields, isEntryEmpty, isEntryPresent, isMarkerOnlyEntry, maskedKeyLabel, mergeDaemonProviders, resolveProviderBaseUrl, shouldSyncLocalProvidersToDaemon, sortProvidersByConfigured, isProviderBaseUrlInvalid, invalidBaseUrlProviderIds, } from './rules.js';
5
5
  export type { MediaProviderCredentials, MediaProviderMap, MediaProviderOption, MediaProvidersLoadState, MediaProvidersSaveState, } from './types.js';
6
6
  export { useMediaProvidersTab } from './react/hooks/useMediaProvidersTab.js';
@@ -1,3 +1,5 @@
1
+ /** Catalog policy is UI-owned; the shared contract lives once in core. */
2
+ export type { MediaProviderCatalogPolicy } from '@jini-ai/core';
1
3
  import type { MediaProviderMap } from './types.js';
2
4
  /**
3
5
  * The daemon-side transport this tab needs: reading the server's current
@@ -0,0 +1,22 @@
1
+ import type { I18nContextValue } from '../../../i18n/index.js';
2
+ import type { MediaProviderCredentials, MediaProviderOption } from '../../types.js';
3
+ import type { MediaProviderEditPatch } from '../hooks/useMediaProvidersTab.js';
4
+ import type { ResolvedMediaProvidersTabLabels } from './media-providers-tab-labels.js';
5
+ type Translate = I18nContextValue['t'];
6
+ export interface MediaProviderCardProps {
7
+ option: MediaProviderOption;
8
+ entry: MediaProviderCredentials;
9
+ labels: ResolvedMediaProvidersTabLabels;
10
+ t: Translate;
11
+ pending: boolean;
12
+ keyVisible: boolean;
13
+ showDividerBefore: boolean;
14
+ providerHandle: string | undefined;
15
+ onUpdate: (patch: MediaProviderEditPatch) => void;
16
+ onClear: () => void;
17
+ onToggleKeyVisible: () => void;
18
+ }
19
+ /** One provider's credential card: API key, base URL, optional model, clear, and the base-URL hint. */
20
+ export declare function MediaProviderCard(props: MediaProviderCardProps): import("react").JSX.Element;
21
+ export {};
22
+ //# sourceMappingURL=MediaProviderCard.d.ts.map
@@ -0,0 +1,50 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Fragment } from 'react';
3
+ import { agentHandleProps } from '@jini-ai/agentic';
4
+ import { Icon } from '../../../../react/components/Icon.js';
5
+ import { isEntryPresent, isMarkerOnlyEntry, isProviderBaseUrlInvalid, maskedKeyLabel, resolveProviderBaseUrl, } from '../../rules.js';
6
+ /** One provider's credential card: API key, base URL, optional model, clear, and the base-URL hint. */
7
+ export function MediaProviderCard(props) {
8
+ const { option, entry, labels, t, providerHandle, onUpdate } = props;
9
+ const saved = isMarkerOnlyEntry(entry);
10
+ // Guaranteed non-null: `isMarkerOnlyEntry` only holds when a server
11
+ // marker is present, and `maskedKeyLabel` always resolves a string
12
+ // for exactly that case — see both functions' doc comments.
13
+ const maskedLabel = saved ? maskedKeyLabel(entry) : null;
14
+ const fields = { option, entry, labels, providerHandle, onUpdate };
15
+ return (_jsxs(Fragment, { children: [props.showDividerBefore ? _jsx("hr", { className: "jini-media-provider-divider" }) : null, _jsxs("div", { className: "jini-media-provider-card", children: [_jsx(CardHead, { option: option, labels: labels, t: t, maskedLabel: maskedLabel, pending: props.pending }), _jsxs("div", { className: "jini-media-provider-fields", children: [_jsx(ApiKeyField, { ...fields, maskedLabel: maskedLabel, keyVisible: props.keyVisible, onToggleKeyVisible: props.onToggleKeyVisible }), _jsx(BaseUrlField, { ...fields }), option.models && option.models.length > 0 ? _jsx(ModelField, { ...fields, models: option.models }) : null, _jsx("button", { type: "button", className: "jini-button jini-button-ghost", disabled: !isEntryPresent(entry), "aria-label": `${option.label} ${labels.clearLabel}`, onClick: props.onClear, ...agentHandleProps({}, { base: providerHandle, ...({ action: 'clear', role: 'button', label: `${option.label} ${labels.clearLabel}` }) }), children: labels.clearLabel })] }), _jsx(BaseUrlHint, { entry: entry, option: option, labels: labels, t: t })] })] }));
16
+ }
17
+ function CardHead({ option, labels, t, maskedLabel, pending, }) {
18
+ // `maskedLabel` is non-null exactly when the entry is marker-only ("saved").
19
+ const saved = maskedLabel !== null;
20
+ return (_jsxs("div", { className: "jini-media-provider-card-head", children: [_jsx("strong", { children: option.label }), saved ? (
21
+ // `maskedLabel` is guaranteed non-null here — see its
22
+ // definition above — so no fallback is reachable to test.
23
+ _jsx("span", { className: "jini-field-status-badge jini-field-status-badge-success", children: t(labels.savedWithMaskTemplate, { mask: maskedLabel }) })) : null, !saved && pending ? _jsx("span", { className: "jini-field-status-badge", children: labels.unsavedLabel }) : null] }));
24
+ }
25
+ function ApiKeyField({ option, entry, labels, providerHandle, onUpdate, maskedLabel, keyVisible, onToggleKeyVisible, }) {
26
+ return (_jsxs("label", { className: "jini-media-provider-field jini-media-provider-field--secret", children: [_jsx("span", { className: "jini-field-label jini-sr-only", children: labels.apiKeyLabel }), _jsx("input", { className: "jini-input", type: keyVisible ? 'text' : 'password',
27
+ /* `new-password`, NOT `"off"` — Chrome deliberately ignores `off` on
28
+ credential-shaped fields, which is how a saved password gets autofilled into
29
+ an API-key box. Same defect, same fix, same reasoning as
30
+ `ByokProviderForm`'s own API-key input; see its comment. */
31
+ autoComplete: "new-password", spellCheck: false, placeholder: maskedLabel ?? labels.apiKeyPlaceholder, "aria-label": `${option.label} ${labels.apiKeyLabel}`, value: entry.apiKey ?? '', onChange: (event) => onUpdate({ apiKey: event.target.value }), ...agentHandleProps({}, { base: providerHandle, ...({ action: 'api-key', role: 'field', label: `${option.label} ${labels.apiKeyLabel}` }) }) }), _jsx("button", { type: "button", className: "jini-input-affix-btn", "aria-pressed": keyVisible, "aria-label": `${option.label} ${keyVisible ? labels.hideKeyLabel : labels.showKeyLabel}`, onClick: onToggleKeyVisible, ...agentHandleProps({}, { base: providerHandle, ...({ action: 'reveal-key', role: 'button', label: `${option.label} ${labels.showKeyLabel}` }) }), children: _jsx(Icon, { name: keyVisible ? 'eye-off' : 'eye', size: 14 }) })] }));
32
+ }
33
+ function BaseUrlField({ option, entry, labels, providerHandle, onUpdate }) {
34
+ return (_jsxs("label", { className: "jini-media-provider-field", children: [_jsx("span", { className: "jini-field-label jini-sr-only", children: labels.baseUrlLabel }), _jsx("input", { className: "jini-input", type: "url", inputMode: "url", spellCheck: false, placeholder: option.defaultBaseUrl || labels.baseUrlPlaceholder, "aria-label": `${option.label} ${labels.baseUrlLabel}`, value: entry.baseUrl ?? '', "aria-invalid": isProviderBaseUrlInvalid(entry) || undefined, onChange: (event) => onUpdate({ baseUrl: event.target.value }), ...agentHandleProps({}, { base: providerHandle, ...({ action: 'base-url', role: 'field', label: `${option.label} ${labels.baseUrlLabel}` }) }) })] }));
35
+ }
36
+ function ModelField({ option, entry, labels, providerHandle, onUpdate, models }) {
37
+ const modelListId = `jini-media-provider-models-${option.id}`;
38
+ return (_jsxs("label", { className: "jini-media-provider-field", children: [_jsx("span", { className: "jini-field-label jini-sr-only", children: labels.modelLabel }), _jsx("input", { className: "jini-input", list: modelListId, spellCheck: false, placeholder: labels.modelPlaceholder, "aria-label": `${option.label} ${labels.modelLabel}`, value: entry.model ?? '', onChange: (event) => onUpdate({ model: event.target.value }), ...agentHandleProps({}, { base: providerHandle, ...({ action: 'model', role: 'field', label: `${option.label} ${labels.modelLabel}` }) }) }), _jsx("datalist", { id: modelListId, children: models.map((model) => (_jsx("option", { value: model }, model))) })] }));
39
+ }
40
+ /** The invalid-URL alert, else the "uses X by default" hint while the field is blank, else nothing. */
41
+ function BaseUrlHint({ entry, option, labels, t, }) {
42
+ if (isProviderBaseUrlInvalid(entry)) {
43
+ return (_jsx("span", { className: "jini-field-hint jini-hint-error", role: "alert", children: labels.baseUrlInvalidLabel }));
44
+ }
45
+ const effectiveBaseUrl = resolveProviderBaseUrl(entry, option.defaultBaseUrl);
46
+ if ((entry.baseUrl ?? '').trim() || !effectiveBaseUrl)
47
+ return null;
48
+ return _jsx("span", { className: "jini-field-hint", children: t(labels.baseUrlDefaultHintTemplate, { url: effectiveBaseUrl }) });
49
+ }
50
+ //# sourceMappingURL=MediaProviderCard.js.map