@jini-ai/ui 0.5.4 → 0.5.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (270) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/core/theme/validation.js +40 -38
  3. package/dist/features/a2ui/renderer.js +15 -13
  4. package/dist/features/about/react/components/AboutTab.js +64 -34
  5. package/dist/features/about/rules.d.ts +2 -2
  6. package/dist/features/about/rules.js +156 -129
  7. package/dist/features/admin-widgets/components/ImagePreviewModal.d.ts +4 -3
  8. package/dist/features/admin-widgets/components/ImagePreviewModal.js +6 -10
  9. package/dist/features/admin-widgets/components/InfoTip.d.ts +3 -2
  10. package/dist/features/admin-widgets/components/InfoTip.hooks.js +2 -0
  11. package/dist/features/admin-widgets/components/InfoTip.js +34 -7
  12. package/dist/features/admin-widgets/components/InfoTip.rules.d.ts +13 -0
  13. package/dist/features/admin-widgets/components/InfoTip.rules.js +43 -10
  14. package/dist/features/admin-widgets/components/Lightbox/Lightbox.d.ts +30 -0
  15. package/dist/features/admin-widgets/components/Lightbox/Lightbox.hooks.d.ts +40 -0
  16. package/dist/features/admin-widgets/components/Lightbox/Lightbox.hooks.js +128 -0
  17. package/dist/features/admin-widgets/components/Lightbox/Lightbox.js +18 -0
  18. package/dist/features/admin-widgets/components/MiddleTruncate.d.ts +14 -0
  19. package/dist/features/admin-widgets/components/MiddleTruncate.js +12 -0
  20. package/dist/features/admin-widgets/components/MiddleTruncate.rules.d.ts +20 -0
  21. package/dist/features/admin-widgets/components/MiddleTruncate.rules.js +21 -0
  22. package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.d.ts +18 -0
  23. package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.hooks.d.ts +16 -0
  24. package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.hooks.js +45 -0
  25. package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.js +18 -0
  26. package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.rules.d.ts +32 -0
  27. package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.rules.js +26 -0
  28. package/dist/features/admin-widgets/components/Select/Select.hooks.d.ts +21 -0
  29. package/dist/features/admin-widgets/components/Select/Select.hooks.js +24 -2
  30. package/dist/features/admin-widgets/components/Select/Select.js +4 -3
  31. package/dist/features/admin-widgets/components/SuggestInput/SuggestInput.hooks.js +29 -14
  32. package/dist/features/admin-widgets/index.d.ts +14 -2
  33. package/dist/features/admin-widgets/index.js +7 -1
  34. package/dist/features/admin-widgets/styles/admin-widgets.css +1 -0
  35. package/dist/features/asset-grid/react/components/AssetCard.js +7 -1
  36. package/dist/features/asset-grid/react/components/AssetGrid.d.ts +1 -1
  37. package/dist/features/asset-grid/react/components/AssetGrid.js +38 -18
  38. package/dist/features/asset-grid/react/components/useAssetGridFilters.d.ts +21 -0
  39. package/dist/features/asset-grid/react/components/useAssetGridFilters.js +19 -0
  40. package/dist/features/asset-grid/react/hooks/useAssetGridKeyboardShortcuts.js +16 -11
  41. package/dist/features/asset-grid/react/hooks/useAssetGridLiveUpdates.d.ts +2 -0
  42. package/dist/features/asset-grid/react/hooks/useAssetGridLiveUpdates.js +35 -11
  43. package/dist/features/asset-tree-browser/react/components/AssetTreeBrowser.d.ts +6 -6
  44. package/dist/features/asset-tree-browser/react/components/AssetTreeBrowser.js +10 -129
  45. package/dist/features/asset-tree-browser/react/components/AssetTreeBrowserParts.d.ts +50 -0
  46. package/dist/features/asset-tree-browser/react/components/AssetTreeBrowserParts.js +95 -0
  47. package/dist/features/asset-tree-browser/react/components/useAssetTreeBrowserController.d.ts +56 -0
  48. package/dist/features/asset-tree-browser/react/components/useAssetTreeBrowserController.js +80 -0
  49. package/dist/features/asset-tree-browser/rules.js +40 -22
  50. package/dist/features/browser-chrome/rules.js +74 -70
  51. package/dist/features/charts/browser.js +11 -5
  52. package/dist/features/charts/react.js +2 -2
  53. package/dist/features/charts/table.js +2 -2
  54. package/dist/features/command-palette/react/hooks/useCommandPalette.js +3 -9
  55. package/dist/features/connectors/ConnectorsBrowser.d.ts +4 -4
  56. package/dist/features/connectors/ConnectorsBrowser.js +26 -14
  57. package/dist/features/connectors/components/ConnectorCard.js +67 -38
  58. package/dist/features/connectors/components/ConnectorDetailDrawer.js +62 -31
  59. package/dist/features/connectors/components/ConnectorGrid.js +11 -6
  60. package/dist/features/connectors/hooks/useConnectorAuthorization.js +6 -0
  61. package/dist/features/connectors/hooks/useConnectorCatalog.d.ts +2 -0
  62. package/dist/features/connectors/hooks/useConnectorCatalog.js +32 -13
  63. package/dist/features/connectors/hooks/useWiredConnectorsBrowser.d.ts +3 -3
  64. package/dist/features/connectors/rules.d.ts +8 -1
  65. package/dist/features/connectors/rules.js +42 -20
  66. package/dist/features/connectors/types.d.ts +11 -0
  67. package/dist/features/execution/constants.d.ts +4 -10
  68. package/dist/features/execution/constants.js +9 -94
  69. package/dist/features/execution/react/components/ByokProviderForm.d.ts +2 -4
  70. package/dist/features/execution/react/components/ByokProviderForm.js +140 -43
  71. package/dist/features/execution/react/components/ExecutionTab.d.ts +1 -2
  72. package/dist/features/execution/react/components/ExecutionTab.js +52 -30
  73. package/dist/features/execution/react/components/LocalCliAgentCard.js +12 -3
  74. package/dist/features/execution/react/components/LocalCliAgentList.js +14 -3
  75. package/dist/features/execution/react/components/LocalCliAgentSettings.js +14 -3
  76. package/dist/features/execution/react/hooks/useLocalCliAgentCard.js +54 -36
  77. package/dist/features/execution/react/hooks/useReasoningControl.d.ts +1 -1
  78. package/dist/features/execution/react/hooks/useReasoningControl.js +116 -75
  79. package/dist/features/execution/rules.d.ts +13 -2
  80. package/dist/features/execution/rules.js +51 -11
  81. package/dist/features/execution/types.d.ts +6 -42
  82. package/dist/features/execution/types.js +0 -17
  83. package/dist/features/external-mcp/react/components/ExternalMcpTab.d.ts +6 -2
  84. package/dist/features/external-mcp/react/components/ExternalMcpTab.js +9 -2
  85. package/dist/features/file-dropzone/react/components/FileDropzone.js +24 -25
  86. package/dist/features/file-dropzone/react/components/FileDropzoneLightbox.d.ts +2 -2
  87. package/dist/features/file-dropzone/react/components/FileDropzoneLightbox.js +35 -30
  88. package/dist/features/file-dropzone/react/components/FileDropzoneThumbnailGrid.js +30 -24
  89. package/dist/features/file-dropzone/react/components/FileDropzoneZone.d.ts +14 -0
  90. package/dist/features/file-dropzone/react/components/FileDropzoneZone.js +26 -0
  91. package/dist/features/html-editor/canvas-content-wrapper.js +13 -13
  92. package/dist/features/html-editor/canvas-embed-placeholders.js +46 -37
  93. package/dist/features/html-editor/css.js +76 -69
  94. package/dist/features/html-editor/editor-blocks.js +47 -33
  95. package/dist/features/html-editor/editor-serialization.js +6 -5
  96. package/dist/features/html-editor/protected-elements.js +39 -28
  97. package/dist/features/html-editor/react/hooks/useInteractiveHtmlEditor.js +4 -4
  98. package/dist/features/html-editor/source-splice.js +51 -30
  99. package/dist/features/i18n/dictionaries/settings-dialog.ja.js +1 -0
  100. package/dist/features/i18n/dictionaries/settings-dialog.zh-CN.js +1 -0
  101. package/dist/features/i18n/dictionaries/settings-dialog.zh-TW.js +1 -0
  102. package/dist/features/i18n/locale.js +20 -16
  103. package/dist/features/iframe-pool/react/dom-sync.js +8 -6
  104. package/dist/features/integrations/react/components/CodexInstallToggleButton.js +19 -4
  105. package/dist/features/integrations/react/components/IntegrationsTab.js +60 -14
  106. package/dist/features/interactive-ui/interactive-ui.css +1 -1
  107. package/dist/features/interactive-ui/providers/native/data-table.manifest.d.ts +6 -6
  108. package/dist/features/interactive-ui/providers/shadcn/data-table.js +10 -7
  109. package/dist/features/interactive-ui/providers/shadcn/data-table.manifest.d.ts +6 -6
  110. package/dist/features/lexical-rich-text-editor/mention-parser.js +60 -57
  111. package/dist/features/lexical-rich-text-editor/react/hooks/useKeyboardCommands.js +33 -38
  112. package/dist/features/lexical-rich-text-editor/rules.js +87 -74
  113. package/dist/features/lexical-rich-text-editor/serialize.js +27 -25
  114. package/dist/features/mcp-ui/secret-card.js +111 -61
  115. package/dist/features/mcp-ui/surfaces/confirmation.js +39 -21
  116. package/dist/features/mcp-ui/surfaces/document.d.ts +1 -1
  117. package/dist/features/mcp-ui/surfaces/document.js +10 -2
  118. package/dist/features/mcp-ui/surfaces/fields.js +30 -50
  119. package/dist/features/mcp-ui/surfaces/form.d.ts +6 -0
  120. package/dist/features/mcp-ui/surfaces/form.js +43 -21
  121. package/dist/features/mcp-ui/surfaces/outcome.js +30 -20
  122. package/dist/features/mcp-ui/surfaces/text-input.js +33 -21
  123. package/dist/features/mcp-ui/surfaces/tokens.d.ts +6 -1
  124. package/dist/features/mcp-ui/surfaces/tokens.js +5 -2
  125. package/dist/features/media-providers/constants.d.ts +1 -0
  126. package/dist/features/media-providers/constants.js +2 -81
  127. package/dist/features/media-providers/index.d.ts +1 -1
  128. package/dist/features/media-providers/ports.d.ts +2 -0
  129. package/dist/features/media-providers/react/components/MediaProviderCard.d.ts +22 -0
  130. package/dist/features/media-providers/react/components/MediaProviderCard.js +50 -0
  131. package/dist/features/media-providers/react/components/MediaProvidersTab.d.ts +5 -6
  132. package/dist/features/media-providers/react/components/MediaProvidersTab.js +40 -72
  133. package/dist/features/media-providers/react/components/media-providers-tab-labels.d.ts +11 -0
  134. package/dist/features/media-providers/react/components/media-providers-tab-labels.js +57 -0
  135. package/dist/features/media-providers/react/hooks/useMediaProvidersTab.d.ts +4 -2
  136. package/dist/features/media-providers/react/hooks/useMediaProvidersTab.js +37 -23
  137. package/dist/features/media-providers/rules.js +17 -10
  138. package/dist/features/media-providers/types.d.ts +4 -22
  139. package/dist/features/media-providers/types.js +3 -8
  140. package/dist/features/memory/formatters.js +127 -92
  141. package/dist/features/memory/react/components/MemoryConnectedPanel.js +92 -36
  142. package/dist/features/memory/react/components/MemoryList.js +37 -18
  143. package/dist/features/memory/react/hooks/useMemoryExtractions.store.js +42 -36
  144. package/dist/features/notifications/react/components/NotificationsTab.js +28 -28
  145. package/dist/features/observability/long-task.d.ts +0 -12
  146. package/dist/features/observability/long-task.js +15 -19
  147. package/dist/features/observability/resource-error.d.ts +0 -10
  148. package/dist/features/observability/resource-error.js +18 -13
  149. package/dist/features/observability/stuck-run.js +10 -6
  150. package/dist/features/observability/telemetry-url.d.ts +6 -0
  151. package/dist/features/observability/telemetry-url.js +16 -0
  152. package/dist/features/observability/white-screen.js +12 -24
  153. package/dist/features/panel-kit/fetch-query/adapter.react.d.ts +2 -2
  154. package/dist/features/panel-kit/fetch-query/adapter.react.js +4 -2
  155. package/dist/features/panel-kit/fetch-query/adapter.tanstack.js +46 -33
  156. package/dist/features/panel-kit/helpers/standing-draft-local-backup.js +3 -1
  157. package/dist/features/panel-kit/hooks/use-settings-slice.hooks.js +3 -1
  158. package/dist/features/privacy/react/components/PrivacyTab.js +24 -13
  159. package/dist/features/progress-card/components/ProgressCard.js +7 -3
  160. package/dist/features/progress-card/reference-adapters.js +59 -54
  161. package/dist/features/project-locations/react/components/ProjectLocationsTab.js +24 -15
  162. package/dist/features/project-locations/react/hooks/useProjectLocationsTab.d.ts +8 -0
  163. package/dist/features/project-locations/react/hooks/useProjectLocationsTab.js +31 -23
  164. package/dist/features/resource-dashboard/react/components/ResourceBoard.js +6 -47
  165. package/dist/features/resource-dashboard/react/components/ResourceBoardView.d.ts +2 -2
  166. package/dist/features/resource-dashboard/react/components/ResourceBoardView.js +3 -6
  167. package/dist/features/resource-dashboard/react/components/ResourceBoardViewParts.d.ts +15 -0
  168. package/dist/features/resource-dashboard/react/components/ResourceBoardViewParts.js +35 -0
  169. package/dist/features/resource-dashboard/react/components/ResourceCard.js +5 -16
  170. package/dist/features/resource-dashboard/react/components/ResourceCardParts.d.ts +34 -0
  171. package/dist/features/resource-dashboard/react/components/ResourceCardParts.js +34 -0
  172. package/dist/features/resource-dashboard/react/components/ResourceRowList.js +3 -1
  173. package/dist/features/resource-dashboard/react/components/ResourceRowListItem.js +2 -2
  174. package/dist/features/resource-dashboard/react/components/ResourceRowListItemParts.d.ts +19 -0
  175. package/dist/features/resource-dashboard/react/components/ResourceRowListItemParts.js +11 -0
  176. package/dist/features/resource-dashboard/react/components/ResourceRowListView.d.ts +1 -1
  177. package/dist/features/resource-dashboard/react/components/ResourceRowListView.js +4 -4
  178. package/dist/features/resource-dashboard/react/components/ResourceRowListViewParts.d.ts +10 -0
  179. package/dist/features/resource-dashboard/react/components/ResourceRowListViewParts.js +20 -0
  180. package/dist/features/resource-dashboard/react/components/resourceBoardProps.d.ts +48 -0
  181. package/dist/features/resource-dashboard/react/components/resourceBoardProps.js +70 -0
  182. package/dist/features/resource-dashboard/react/components/resourceRowListProps.d.ts +19 -0
  183. package/dist/features/resource-dashboard/react/components/resourceRowListProps.js +14 -0
  184. package/dist/features/resource-dashboard/react/components/translatedErrorLabelProps.d.ts +16 -0
  185. package/dist/features/resource-dashboard/react/components/translatedErrorLabelProps.js +13 -0
  186. package/dist/features/resource-dashboard/rules.js +2 -8
  187. package/dist/features/sketch-editor/dom.js +150 -99
  188. package/dist/features/sketch-editor/react/components/SketchEditor.js +26 -15
  189. package/dist/features/skills/react/components/SkillDraftForm.js +13 -19
  190. package/dist/features/skills/react/components/SkillDraftForm.labels.d.ts +31 -0
  191. package/dist/features/skills/react/components/SkillDraftForm.labels.js +54 -0
  192. package/dist/features/skills/react/components/SkillRow.js +38 -18
  193. package/dist/features/skills/react/components/SkillRow.labels.d.ts +7 -0
  194. package/dist/features/skills/react/components/SkillRow.labels.js +18 -0
  195. package/dist/features/skills/react/components/SkillsTab.js +24 -14
  196. package/dist/features/skills/react/components/SkillsTab.labels.d.ts +7 -0
  197. package/dist/features/skills/react/components/SkillsTab.labels.js +11 -0
  198. package/dist/features/skills/rules.js +32 -23
  199. package/dist/features/source-config-list/agent-handles.js +2 -1
  200. package/dist/features/source-config-list/dependencies.js +53 -49
  201. package/dist/features/source-config-list/index.d.ts +3 -3
  202. package/dist/features/source-config-list/react/components/SourceConfigAddForm.d.ts +6 -5
  203. package/dist/features/source-config-list/react/components/SourceConfigAddForm.js +38 -7
  204. package/dist/features/source-config-list/react/components/SourceConfigField.js +69 -25
  205. package/dist/features/source-config-list/react/components/SourceConfigItemCard.d.ts +6 -5
  206. package/dist/features/source-config-list/react/components/SourceConfigItemCard.js +76 -20
  207. package/dist/features/source-config-list/react/components/SourceConfigListView.d.ts +5 -3
  208. package/dist/features/source-config-list/react/components/SourceConfigListView.js +33 -3
  209. package/dist/features/source-config-list/react/components/SourceConfigTestControl.js +17 -1
  210. package/dist/features/source-config-list/react/components/SourceConfigTrustOptions.d.ts +11 -0
  211. package/dist/features/source-config-list/react/components/SourceConfigTrustOptions.js +12 -0
  212. package/dist/features/source-config-list/types.d.ts +14 -0
  213. package/dist/features/tab-launcher-menu/react/components/TabLauncherMenu.js +25 -1
  214. package/dist/features/tab-launcher-menu/react/hooks/useTabLauncherMenu.js +15 -16
  215. package/dist/features/tab-strip/react/components/TabBar.js +8 -1
  216. package/dist/features/tabbed-dialog/react/components/TabbedDialog.js +8 -30
  217. package/dist/features/tabbed-dialog/react/components/TabbedDialogChrome.d.ts +15 -0
  218. package/dist/features/tabbed-dialog/react/components/TabbedDialogChrome.js +8 -0
  219. package/dist/features/tabbed-dialog/react/components/TabbedDialogHeader.d.ts +12 -0
  220. package/dist/features/tabbed-dialog/react/components/TabbedDialogHeader.js +6 -0
  221. package/dist/features/tabbed-dialog/react/components/TabbedDialogSidebar.d.ts +27 -0
  222. package/dist/features/tabbed-dialog/react/components/TabbedDialogSidebar.js +12 -0
  223. package/dist/features/tabbed-dialog/react/components/tabbedDialogView.d.ts +43 -0
  224. package/dist/features/tabbed-dialog/react/components/tabbedDialogView.js +59 -0
  225. package/dist/features/version-manager/dependencies.d.ts +3 -1
  226. package/dist/features/version-manager/dependencies.js +3 -2
  227. package/dist/features/version-manager/react/components/VersionManagerModal.js +10 -6
  228. package/dist/features/version-manager/react/hooks/useVersionManager.js +7 -4
  229. package/dist/features/viewer-shell/react/components/CommentSidePanel.js +103 -50
  230. package/dist/features/viewer-shell/react/components/MarkdownSplitPane.js +28 -8
  231. package/dist/features/viewer-shell/react/hooks/useCommentReorder.js +22 -16
  232. package/dist/features/viewer-shell/rules.js +19 -23
  233. package/dist/react/components/CustomSelect.d.ts +38 -1
  234. package/dist/react/components/CustomSelect.js +94 -38
  235. package/dist/react/components/OnboardingDropdown.js +20 -8
  236. package/dist/react/components/RemixIcon.stylesheet.js +3 -1
  237. package/dist/react/components/Toast.js +13 -2
  238. package/dist/react/components/TooltipLayer.js +5 -14
  239. package/dist/react/components/WorkingDirPicker.js +78 -50
  240. package/dist/react/components/remixicon-font-recovery.js +38 -30
  241. package/dist/react/components/select-popover.d.ts +8 -6
  242. package/dist/react/components/select-popover.js +33 -29
  243. package/dist/react/mcp-ui/McpUiHost.d.ts +1 -1
  244. package/dist/react/mcp-ui/McpUiHost.js +10 -11
  245. package/dist/react/mcp-ui/host-message-source.d.ts +14 -2
  246. package/dist/react/mcp-ui/host-message-source.js +15 -5
  247. package/dist/react/mcp-ui/mcp-ui-host-frame.d.ts +41 -0
  248. package/dist/react/mcp-ui/mcp-ui-host-frame.js +39 -0
  249. package/dist/renderers/annotation-canvas/react/components/AnnotationCanvas.js +97 -69
  250. package/dist/renderers/preview-modal-shell/react/components/PreviewModalShell.js +59 -30
  251. package/dist/renderers/preview-modal-shell/react/components/icons.js +4 -1
  252. package/dist/renderers/react/components/ArtifactView.js +9 -6
  253. package/dist/renderers/srcdoc/build.d.ts +4 -1
  254. package/dist/renderers/srcdoc/build.js +62 -48
  255. package/dist/styles/admin.css +29 -14
  256. package/dist/styles/lightbox.css +142 -0
  257. package/dist/styles/variables.css +128 -0
  258. package/dist/theme/apply.js +9 -5
  259. package/dist/utils/auto-open-file.d.ts +2 -1
  260. package/dist/utils/auto-open-file.js +69 -85
  261. package/dist/utils/markdown-scroll-sync.js +108 -87
  262. package/dist/utils/polygon-selection.js +25 -26
  263. package/dist/utils/smooth-scroll-to-top.d.ts +1 -1
  264. package/dist/utils/smooth-scroll-to-top.js +2 -32
  265. package/dist/utils/sse.js +26 -26
  266. package/dist/utils/unit-bezier.d.ts +7 -0
  267. package/dist/utils/unit-bezier.js +38 -0
  268. package/package.json +7 -10
  269. package/dist/features/admin-widgets/components/ImagePreviewModal.hooks.d.ts +0 -13
  270. package/dist/features/admin-widgets/components/ImagePreviewModal.hooks.js +0 -46
@@ -20,44 +20,9 @@ export function useKeyboardCommands(popoverOpen, onSubmit, onPopoverKey) {
20
20
  const onPopoverKeyRef = useRef(onPopoverKey);
21
21
  onPopoverKeyRef.current = onPopoverKey;
22
22
  useEffect(() => {
23
- return mergeRegister(editor.registerCommand(KEY_ENTER_COMMAND, (e) => {
24
- // IME confirm Enter — let Lexical commit the composition.
25
- if (editor.isComposing())
26
- return false;
27
- if (e?.shiftKey) {
28
- editor.dispatchCommand(INSERT_LINE_BREAK_COMMAND, false);
29
- e.preventDefault();
30
- return true;
31
- }
32
- // Cmd/Ctrl+Enter force-submits even with a popover open.
33
- if (e?.metaKey || e?.ctrlKey) {
34
- e.preventDefault();
35
- onSubmitRef.current();
36
- return true;
37
- }
38
- if (popoverOpenRef.current) {
39
- e?.preventDefault();
40
- return onPopoverKeyRef.current('Enter');
41
- }
42
- e?.preventDefault();
43
- onSubmitRef.current();
44
- return true;
45
- }, COMMAND_PRIORITY_HIGH), editor.registerCommand(KEY_ARROW_DOWN_COMMAND, (e) => {
46
- if (!popoverOpenRef.current || editor.isComposing())
47
- return false;
48
- e?.preventDefault();
49
- return onPopoverKeyRef.current('ArrowDown');
50
- }, COMMAND_PRIORITY_HIGH), editor.registerCommand(KEY_ARROW_UP_COMMAND, (e) => {
51
- if (!popoverOpenRef.current || editor.isComposing())
52
- return false;
53
- e?.preventDefault();
54
- return onPopoverKeyRef.current('ArrowUp');
55
- }, COMMAND_PRIORITY_HIGH), editor.registerCommand(KEY_TAB_COMMAND, (e) => {
56
- if (!popoverOpenRef.current || editor.isComposing())
57
- return false;
58
- e?.preventDefault();
59
- return onPopoverKeyRef.current('Tab');
60
- }, COMMAND_PRIORITY_HIGH), editor.registerCommand(KEY_ESCAPE_COMMAND, () => {
23
+ const routing = { editor, popoverOpenRef, onSubmitRef, onPopoverKeyRef };
24
+ const navKey = (key) => (event) => routeNavKeyToPopover({ ...routing, event, key });
25
+ return mergeRegister(editor.registerCommand(KEY_ENTER_COMMAND, (event) => handleEnter({ ...routing, event }), COMMAND_PRIORITY_HIGH), editor.registerCommand(KEY_ARROW_DOWN_COMMAND, navKey('ArrowDown'), COMMAND_PRIORITY_HIGH), editor.registerCommand(KEY_ARROW_UP_COMMAND, navKey('ArrowUp'), COMMAND_PRIORITY_HIGH), editor.registerCommand(KEY_TAB_COMMAND, navKey('Tab'), COMMAND_PRIORITY_HIGH), editor.registerCommand(KEY_ESCAPE_COMMAND, () => {
61
26
  if (!popoverOpenRef.current)
62
27
  return false;
63
28
  return onPopoverKeyRef.current('Escape');
@@ -67,4 +32,34 @@ export function useKeyboardCommands(popoverOpen, onSubmit, onPopoverKey) {
67
32
  }, COMMAND_PRIORITY_HIGH));
68
33
  }, [editor]);
69
34
  }
35
+ function handleEnter(at) {
36
+ const { editor, event: e } = at;
37
+ // IME confirm Enter — let Lexical commit the composition.
38
+ if (editor.isComposing())
39
+ return false;
40
+ if (e?.shiftKey) {
41
+ editor.dispatchCommand(INSERT_LINE_BREAK_COMMAND, false);
42
+ e.preventDefault();
43
+ return true;
44
+ }
45
+ // Cmd/Ctrl+Enter force-submits even with a popover open.
46
+ if (e?.metaKey || e?.ctrlKey) {
47
+ e.preventDefault();
48
+ at.onSubmitRef.current();
49
+ return true;
50
+ }
51
+ e?.preventDefault();
52
+ if (at.popoverOpenRef.current)
53
+ return at.onPopoverKeyRef.current('Enter');
54
+ at.onSubmitRef.current();
55
+ return true;
56
+ }
57
+ /** Arrow-down/up/Tab go to the popover only while it's open and no IME
58
+ * composition is in progress; otherwise Lexical handles them. */
59
+ function routeNavKeyToPopover(at) {
60
+ if (!at.popoverOpenRef.current || at.editor.isComposing())
61
+ return false;
62
+ at.event?.preventDefault();
63
+ return at.onPopoverKeyRef.current(at.key);
64
+ }
70
65
  //# sourceMappingURL=useKeyboardCommands.js.map
@@ -205,6 +205,10 @@ function isUsableRect(r) {
205
205
  function toCaretRect(r) {
206
206
  return { top: r.top, bottom: r.bottom, left: r.left, right: r.right };
207
207
  }
208
+ /** A caret rect pinned to a box's left edge (zero width). */
209
+ function leftEdgeRect(r) {
210
+ return { top: r.top, bottom: r.bottom, left: r.left, right: r.left };
211
+ }
208
212
  /** Reads the caret's viewport rect via the native selection, with ordered
209
213
  * fallbacks for the collapsed-caret 0×0 case: (1) range bounding rect,
210
214
  * (2) range client-rects list, (3) a zero-width probe range cloned at the
@@ -213,99 +217,108 @@ export function readCaretRect(rootEl) {
213
217
  if (typeof window === 'undefined')
214
218
  return null;
215
219
  const winSel = window.getSelection();
216
- if (winSel && winSel.rangeCount > 0) {
217
- const range = winSel.getRangeAt(0);
218
- // jsdom does not implement Range geometry — guard every native call so a
219
- // missing method falls through to the element/root box instead of
220
- // throwing.
221
- if (typeof range.getBoundingClientRect === 'function') {
222
- const r = range.getBoundingClientRect();
223
- if (isUsableRect(r))
224
- return toCaretRect(r);
225
- }
226
- if (typeof range.getClientRects === 'function') {
227
- const rects = range.getClientRects();
228
- if (rects.length > 0 && isUsableRect(rects[0]))
229
- return toCaretRect(rects[0]);
230
- }
231
- const anchorNode = range.startContainer;
232
- if (anchorNode.nodeType === Node.TEXT_NODE) {
233
- try {
234
- const probe = document.createRange();
235
- const len = anchorNode.length;
236
- const off = Math.min(range.startOffset, len);
237
- probe.setStart(anchorNode, off);
238
- probe.setEnd(anchorNode, off);
239
- if (typeof probe.getBoundingClientRect === 'function') {
240
- const pr = probe.getBoundingClientRect();
241
- if (isUsableRect(pr))
242
- return toCaretRect(pr);
243
- }
244
- if (typeof probe.getClientRects === 'function') {
245
- const prList = probe.getClientRects();
246
- if (prList.length > 0 && isUsableRect(prList[0]))
247
- return toCaretRect(prList[0]);
248
- }
249
- }
250
- catch {
251
- /* fall through */
252
- }
253
- }
254
- const el = anchorNode.nodeType === Node.ELEMENT_NODE
255
- ? anchorNode
256
- : anchorNode.parentElement;
257
- if (el) {
258
- const er = el.getBoundingClientRect();
259
- if (isUsableRect(er)) {
260
- return { top: er.top, bottom: er.bottom, left: er.left, right: er.left };
261
- }
262
- }
220
+ const fromSelection = winSel && winSel.rangeCount > 0 ? caretRectFromRange(winSel.getRangeAt(0)) : null;
221
+ if (fromSelection)
222
+ return fromSelection;
223
+ return rootEl ? leftEdgeRect(rootEl.getBoundingClientRect()) : null;
224
+ }
225
+ /** Fallbacks (1)–(4) of `readCaretRect` for one live selection range. */
226
+ function caretRectFromRange(range) {
227
+ const fromRange = rangeGeometryRect(range);
228
+ if (fromRange)
229
+ return fromRange;
230
+ const anchorNode = range.startContainer;
231
+ if (anchorNode.nodeType === Node.TEXT_NODE) {
232
+ const probed = probeTextCaretRect({ text: anchorNode, offset: range.startOffset });
233
+ if (probed)
234
+ return probed;
235
+ }
236
+ return anchorElementRect(anchorNode);
237
+ }
238
+ /** Fallbacks (1) and (2): a range's bounding rect, then its first client rect. */
239
+ function rangeGeometryRect(range) {
240
+ // jsdom does not implement Range geometry — guard every native call so a
241
+ // missing method falls through to the element/root box instead of
242
+ // throwing.
243
+ if (typeof range.getBoundingClientRect === 'function') {
244
+ const r = range.getBoundingClientRect();
245
+ if (isUsableRect(r))
246
+ return toCaretRect(r);
263
247
  }
264
- if (rootEl) {
265
- const rr = rootEl.getBoundingClientRect();
266
- return { top: rr.top, bottom: rr.bottom, left: rr.left, right: rr.left };
248
+ if (typeof range.getClientRects === 'function') {
249
+ const rects = range.getClientRects();
250
+ if (rects.length > 0 && isUsableRect(rects[0]))
251
+ return toCaretRect(rects[0]);
267
252
  }
268
253
  return null;
269
254
  }
255
+ /** Fallback (3): geometry of a zero-width probe range at the anchor text offset. */
256
+ function probeTextCaretRect(at) {
257
+ try {
258
+ const probe = document.createRange();
259
+ const off = Math.min(at.offset, at.text.length);
260
+ probe.setStart(at.text, off);
261
+ probe.setEnd(at.text, off);
262
+ return rangeGeometryRect(probe);
263
+ }
264
+ catch {
265
+ /* fall through */
266
+ return null;
267
+ }
268
+ }
269
+ /** Fallback (4): the anchor element's box (or the text anchor's parent's). */
270
+ function anchorElementRect(anchorNode) {
271
+ const el = anchorNode.nodeType === Node.ELEMENT_NODE ? anchorNode : anchorNode.parentElement;
272
+ if (!el)
273
+ return null;
274
+ const er = el.getBoundingClientRect();
275
+ return isUsableRect(er) ? leftEdgeRect(er) : null;
276
+ }
270
277
  /** Default ABOVE the caret (a composer typically sits at a panel's bottom,
271
278
  * so above never occludes the input line); flip BELOW only when above lacks
272
279
  * room. Clamp horizontally into the viewport (or `boundary`, when given).
273
280
  * Caps height; the caller's popover scrolls internally. */
274
281
  export function computeCaretFloatingLayerPosition(caret, size, boundary, config) {
275
- const { gap, margin, hardMaxHeight, preferredWidth } = config;
282
+ const { margin } = config;
276
283
  const vw = window.innerWidth;
277
- const vh = window.innerHeight;
278
- const viewportAvailableWidth = vw - margin * 2;
279
- const boundaryAvailableWidth = boundary ? boundary.width - margin * 2 : viewportAvailableWidth;
284
+ const width = layerWidth({ vw, boundary, margin, preferredWidth: config.preferredWidth });
285
+ const vertical = verticalPlacement({
286
+ caret,
287
+ vh: window.innerHeight,
288
+ wantedH: size?.height ?? config.hardMaxHeight,
289
+ ...config,
290
+ });
291
+ const left = clampLeft({ caretLeft: caret.left, vw, boundary, margin, width });
292
+ return { left, top: vertical.top, width, maxHeight: vertical.maxHeight, placement: vertical.placement };
293
+ }
294
+ function layerWidth(at) {
295
+ const viewportAvailableWidth = at.vw - at.margin * 2;
296
+ const boundaryAvailableWidth = at.boundary
297
+ ? at.boundary.width - at.margin * 2
298
+ : viewportAvailableWidth;
280
299
  const availableWidth = Math.max(240, Math.min(viewportAvailableWidth, boundaryAvailableWidth));
281
- const width = Math.min(preferredWidth, availableWidth);
300
+ return Math.min(at.preferredWidth, availableWidth);
301
+ }
302
+ function verticalPlacement(at) {
303
+ const { caret, gap, margin, wantedH } = at;
282
304
  const spaceAbove = caret.top - gap - margin;
283
- const spaceBelow = vh - caret.bottom - gap - margin;
284
- const wantedH = size?.height ?? hardMaxHeight;
305
+ const spaceBelow = at.vh - caret.bottom - gap - margin;
285
306
  const aboveFits = spaceAbove >= Math.min(wantedH, 160);
286
307
  const placement = aboveFits || spaceAbove >= spaceBelow ? 'above' : 'below';
287
308
  const space = placement === 'above' ? spaceAbove : spaceBelow;
288
- const maxHeight = Math.max(120, Math.min(hardMaxHeight, space));
289
- let top;
290
- if (placement === 'above') {
291
- const h = Math.min(wantedH, maxHeight);
292
- top = caret.top - gap - h;
293
- if (top < margin)
294
- top = margin;
295
- }
296
- else {
297
- top = caret.bottom + gap;
298
- }
309
+ const maxHeight = Math.max(120, Math.min(at.hardMaxHeight, space));
310
+ if (placement === 'below')
311
+ return { top: caret.bottom + gap, maxHeight, placement };
312
+ const h = Math.min(wantedH, maxHeight);
313
+ return { top: Math.max(margin, caret.top - gap - h), maxHeight, placement };
314
+ }
315
+ function clampLeft(at) {
316
+ const { vw, boundary, margin, width } = at;
299
317
  const minLeft = boundary ? Math.max(margin, boundary.left + margin) : margin;
300
318
  const maxLeft = boundary
301
319
  ? Math.max(minLeft, Math.min(vw - margin - width, boundary.right - margin - width))
302
320
  : vw - margin - width;
303
- let left = caret.left;
304
- if (left > maxLeft)
305
- left = maxLeft;
306
- if (left < minLeft)
307
- left = minLeft;
308
- return { left, top, width, maxHeight, placement };
321
+ return Math.max(minLeft, Math.min(at.caretLeft, maxLeft));
309
322
  }
310
323
  /** Combines detection + caret-rect reading into the shape `RichTextInput`
311
324
  * reports upward on every editor update. */
@@ -9,32 +9,34 @@ import { $isMentionNode } from './mention-node.js';
9
9
  export function serializeRichText(state) {
10
10
  return state.read(() => {
11
11
  const mentions = [];
12
- const blocks = [];
13
- for (const block of $getRoot().getChildren()) {
14
- if (!$isParagraphNode(block)) {
15
- // Stray non-paragraph block (shouldn't happen): fall back to its text.
16
- blocks.push(block.getTextContent());
17
- continue;
18
- }
19
- let line = '';
20
- for (const child of block.getChildren()) {
21
- if ($isMentionNode(child)) {
22
- line += child.getToken();
23
- mentions.push(child.getEntity());
24
- }
25
- else if ($isLineBreakNode(child)) {
26
- line += '\n';
27
- }
28
- else if ($isTextNode(child)) {
29
- line += child.getTextContent();
30
- }
31
- else if ($isElementNode(child)) {
32
- line += child.getTextContent();
33
- }
34
- }
35
- blocks.push(line);
36
- }
12
+ const blocks = $getRoot()
13
+ .getChildren()
14
+ .map((block) => $isParagraphNode(block)
15
+ ? serializeParagraph({ paragraph: block, mentions })
16
+ : // Stray non-paragraph block (shouldn't happen): fall back to its text.
17
+ block.getTextContent());
37
18
  return { text: blocks.join('\n'), mentions };
38
19
  });
39
20
  }
21
+ /**
22
+ * One paragraph's wire text; appends each atomic mention's entity to
23
+ * `mentions` in document order. A non-mention decorator child contributes
24
+ * nothing.
25
+ */
26
+ function serializeParagraph(at) {
27
+ let line = '';
28
+ for (const child of at.paragraph.getChildren()) {
29
+ if ($isMentionNode(child)) {
30
+ line += child.getToken();
31
+ at.mentions.push(child.getEntity());
32
+ }
33
+ else if ($isLineBreakNode(child)) {
34
+ line += '\n';
35
+ }
36
+ else if ($isTextNode(child) || $isElementNode(child)) {
37
+ line += child.getTextContent();
38
+ }
39
+ }
40
+ return line;
41
+ }
40
42
  //# sourceMappingURL=serialize.js.map
@@ -27,51 +27,131 @@ function checkedFields(form) {
27
27
  return form.fields.map(field => ({ ...field }));
28
28
  }
29
29
  /** Classify before persistence; an abort wins over an already-buffered answer.
30
- * Trimming tests blankness only: rewriting a provider secret can corrupt intentional newlines.
31
- * @complexity O(n + b) before save in fields and submitted secret bytes; failure text delegates
32
- * to core's exact-secret redaction scan. Host persistence cost is external. */
33
- async function saveAnswer(required) {
34
- const { answer, fields, prep, ctx, spec, options, exchangeId } = required;
35
- if (ctx.signal.aborted)
36
- return { status: 'abandoned' };
30
+ * @complexity O(1). */
31
+ function answerStep(required) {
32
+ const { answer, signal } = required;
33
+ if (signal.aborted)
34
+ return { run: { status: 'abandoned' } };
37
35
  if (answer.status !== 'received')
38
- return { status: answer.status };
36
+ return { run: { status: answer.status } };
39
37
  if (answer.params[SURFACE_DISMISSED_PARAM] === true)
40
- return { status: 'cancelled' };
41
- const values = Object.fromEntries(fields.map(field => [field.name, answer.params[field.name]]));
38
+ return { run: { status: 'cancelled' } };
39
+ return { params: answer.params };
40
+ }
41
+ /** The exact submitted secret, or undefined when it is blank or malformed.
42
+ * Trimming tests blankness only: rewriting a provider secret can corrupt intentional newlines.
43
+ * @complexity O(b) in the submitted secret bytes. */
44
+ function secretValue(required) {
45
+ const { field, value } = required;
46
+ if (value !== undefined && typeof value !== 'string')
47
+ return undefined;
48
+ const secret = typeof value === 'string' ? value : '';
49
+ if (secret.trim() === '' && (secret !== '' || field.allowBlank !== true))
50
+ return undefined;
51
+ return secret;
52
+ }
53
+ /** Project only declared names; undefined means a required secret was blank or malformed.
54
+ * @complexity O(n + b) in fields and submitted secret bytes. */
55
+ function collectValues(required) {
56
+ const { fields, params } = required;
57
+ const values = Object.fromEntries(fields.map(field => [field.name, params[field.name]]));
42
58
  const secrets = [];
43
59
  for (const field of fields) {
44
60
  if (field.secret !== true)
45
61
  continue;
46
- const value = values[field.name];
47
- if (value !== undefined && typeof value !== 'string')
48
- return { status: 'blank' };
49
- const secret = typeof value === 'string' ? value : '';
50
- if (secret.trim() === '' && (secret !== '' || field.allowBlank !== true))
51
- return { status: 'blank' };
62
+ const secret = secretValue({ field, value: values[field.name] });
63
+ if (secret === undefined)
64
+ return undefined;
52
65
  values[field.name] = secret;
53
66
  secrets.push(secret);
54
67
  }
68
+ return { values, secrets };
69
+ }
70
+ /** Map a save exception to fixed, redacted text and log fixed metadata only.
71
+ * @complexity O(m + b): delegates to core's exact-secret redaction scan. */
72
+ function failedRun(required) {
73
+ const { error, secrets, signal, toolId, options, exchangeId } = required;
74
+ if (signal.aborted)
75
+ return { status: 'abandoned' };
76
+ let safeMessage = options.text?.saveFailure ?? 'Saving failed. Nothing was saved.';
77
+ try {
78
+ safeMessage = options.safeError?.(error) ?? safeMessage;
79
+ }
80
+ catch { /* An error mapper must not disclose a second exception containing the submitted value. */ }
81
+ // Even an accidentally permissive allowlist must not echo the submitted secret.
82
+ safeMessage = redactSecrets({ input: safeMessage }, { exactSecrets: secrets });
83
+ try {
84
+ options.logFailure?.({ toolId, exchangeId, saved: false });
85
+ }
86
+ catch { /* Logging must not replace the safe model result with an opaque logger exception. */ }
87
+ return { status: 'failed', safeMessage };
88
+ }
89
+ /** Classify, then persist; an abort wins over an already-buffered answer.
90
+ * @complexity O(n + b) before save in fields and submitted secret bytes; failure text delegates
91
+ * to core's exact-secret redaction scan. Host persistence cost is external. */
92
+ async function saveAnswer(required) {
93
+ const { answer, fields, prep, ctx, spec, options, exchangeId } = required;
94
+ const step = answerStep({ answer, signal: ctx.signal });
95
+ if ('run' in step)
96
+ return step.run;
97
+ const collected = collectValues({ fields, params: step.params });
98
+ if (collected === undefined)
99
+ return { status: 'blank' };
55
100
  if (ctx.signal.aborted)
56
101
  return { status: 'abandoned' };
102
+ const { values, secrets } = collected;
57
103
  try {
58
104
  return { status: 'saved', saved: await spec.save({ values, prep, signal: ctx.signal }) };
59
105
  }
60
106
  catch (error) {
107
+ return failedRun({ error, secrets, signal: ctx.signal, toolId: spec.toolId, options, exchangeId });
108
+ }
109
+ }
110
+ /** Fail closed without an emitter. @complexity O(1). @throws ToolInputError with fixed text. */
111
+ function requireEmitter(required, optional) {
112
+ if (optional.emitSurface)
113
+ return optional.emitSurface;
114
+ throw new ToolInputError({ message: required.options.text?.noEmitter ?? `${required.toolId}: this execution context has no interactive form channel (no emitSurface), so this form cannot be shown here. Nothing was changed.` });
115
+ }
116
+ /** The form resource: engine-owned routing params and dismissal around the host's copy.
117
+ * @complexity O(n + h) in fields and rendered HTML. */
118
+ function buildCardForm(required) {
119
+ const { form, fields, uri, framing, toolId, exchangeId, options } = required;
120
+ return buildFormSurface({ ...form, fields, uri, ...framing, toolName: toolId,
121
+ baseParams: { [SURFACE_EXCHANGE_ID_PARAM]: exchangeId },
122
+ cancel: { label: form.cancelLabel ?? options.text?.cancelLabel ?? 'Cancel', toolName: toolId,
123
+ params: { [SURFACE_EXCHANGE_ID_PARAM]: exchangeId, [SURFACE_DISMISSED_PARAM]: true } },
124
+ });
125
+ }
126
+ /** Open the exchange, emit the card and hold the call until the real save is reported.
127
+ * @complexity O(n + h + b) locally, plus host I/O. */
128
+ async function runCard(required) {
129
+ const { spec, options, deps, ctx, emit, prep, form, fields } = required;
130
+ const exchange = deps.surfaceExchanges.open({ binding: { toolId: spec.toolId, principalId: ctx.principal.id, channel: 'mcp-ui' }, emit });
131
+ const closeOnAbort = () => exchange.close({});
132
+ ctx.signal.addEventListener('abort', closeOnAbort, { once: true });
133
+ try {
134
+ // Close the open/register race as well as the prepare/abort race.
61
135
  if (ctx.signal.aborted)
62
- return { status: 'abandoned' };
63
- let safeMessage = options.text?.saveFailure ?? 'Saving failed. Nothing was saved.';
64
- try {
65
- safeMessage = options.safeError?.(error) ?? safeMessage;
66
- }
67
- catch { /* An error mapper must not disclose a second exception containing the submitted value. */ }
68
- // Even an accidentally permissive allowlist must not echo the submitted secret.
69
- safeMessage = redactSecrets({ input: safeMessage }, { exactSecrets: secrets });
70
- try {
71
- options.logFailure?.({ toolId: spec.toolId, exchangeId, saved: false });
72
- }
73
- catch { /* Logging must not replace the safe model result with an opaque logger exception. */ }
74
- return { status: 'failed', safeMessage };
136
+ return spec.result({ prep, run: { status: 'abandoned' } });
137
+ const uri = `ui://${options.uriHost ?? 'jini'}/secret-card/${encodeURIComponent(spec.toolId)}/${encodeURIComponent(exchange.id)}`;
138
+ const framing = options.frameSize === undefined ? {} : { preferredFrameSize: options.frameSize };
139
+ const resource = buildCardForm({ form, fields, uri, framing, toolId: spec.toolId, exchangeId: exchange.id, options });
140
+ return await deps.askThenReport({ exchange, confirmationEmission: { channel: 'mcp-ui', payload: { resource } },
141
+ handle: async (answer) => {
142
+ const run = await saveAnswer({ answer, fields, prep, ctx, spec, options, exchangeId: exchange.id });
143
+ const result = spec.result({ prep, run });
144
+ const outcome = spec.outcome({ prep, run });
145
+ if (outcome === undefined)
146
+ return { result };
147
+ return { result, outcome: { channel: 'mcp-ui', payload: { resource: buildOutcomeSurface({ ...outcome, uri, ...framing }) } } };
148
+ },
149
+ });
150
+ }
151
+ finally {
152
+ ctx.signal.removeEventListener('abort', closeOnAbort);
153
+ // Also covers rendering/projection failures before or inside askThenReport; close is idempotent.
154
+ exchange.close({});
75
155
  }
76
156
  }
77
157
  /**
@@ -92,8 +172,7 @@ export function defineSecretCardTool(required, options = {}) {
92
172
  toolId: spec.toolId,
93
173
  secretFieldNames: prep => checkedFields(spec.form({ prep })).filter(field => field.secret === true).map(field => field.name),
94
174
  handler: deps => async (ctx, optional = {}) => {
95
- if (!optional.emitSurface)
96
- throw new ToolInputError({ message: options.text?.noEmitter ?? `${spec.toolId}: this execution context has no interactive form channel (no emitSurface), so this form cannot be shown here. Nothing was changed.` });
175
+ const emit = requireEmitter({ toolId: spec.toolId, options }, optional);
97
176
  const prep = await spec.prepare({ ctx });
98
177
  if (ctx.signal.aborted)
99
178
  return spec.result({ prep, run: { status: 'abandoned' } });
@@ -101,36 +180,7 @@ export function defineSecretCardTool(required, options = {}) {
101
180
  const fields = checkedFields(form);
102
181
  if (ctx.signal.aborted)
103
182
  return spec.result({ prep, run: { status: 'abandoned' } });
104
- const exchange = deps.surfaceExchanges.open({ binding: { toolId: spec.toolId, principalId: ctx.principal.id, channel: 'mcp-ui' }, emit: optional.emitSurface });
105
- const closeOnAbort = () => exchange.close({});
106
- ctx.signal.addEventListener('abort', closeOnAbort, { once: true });
107
- try {
108
- // Close the open/register race as well as the prepare/abort race.
109
- if (ctx.signal.aborted)
110
- return spec.result({ prep, run: { status: 'abandoned' } });
111
- const uri = `ui://${options.uriHost ?? 'jini'}/secret-card/${encodeURIComponent(spec.toolId)}/${encodeURIComponent(exchange.id)}`;
112
- const framing = options.frameSize === undefined ? {} : { preferredFrameSize: options.frameSize };
113
- const resource = buildFormSurface({ ...form, fields, uri, ...framing, toolName: spec.toolId,
114
- baseParams: { [SURFACE_EXCHANGE_ID_PARAM]: exchange.id },
115
- cancel: { label: form.cancelLabel ?? options.text?.cancelLabel ?? 'Cancel', toolName: spec.toolId,
116
- params: { [SURFACE_EXCHANGE_ID_PARAM]: exchange.id, [SURFACE_DISMISSED_PARAM]: true } },
117
- });
118
- return await deps.askThenReport({ exchange, confirmationEmission: { channel: 'mcp-ui', payload: { resource } },
119
- handle: async (answer) => {
120
- const run = await saveAnswer({ answer, fields, prep, ctx, spec, options, exchangeId: exchange.id });
121
- const result = spec.result({ prep, run });
122
- const outcome = spec.outcome({ prep, run });
123
- if (outcome === undefined)
124
- return { result };
125
- return { result, outcome: { channel: 'mcp-ui', payload: { resource: buildOutcomeSurface({ ...outcome, uri, ...framing }) } } };
126
- },
127
- });
128
- }
129
- finally {
130
- ctx.signal.removeEventListener('abort', closeOnAbort);
131
- // Also covers rendering/projection failures before or inside askThenReport; close is idempotent.
132
- exchange.close({});
133
- }
183
+ return runCard({ spec, options, deps, ctx, emit, prep, form, fields });
134
184
  },
135
185
  };
136
186
  }