@miragon/mcp-toolkit-ui 2.6.0 → 2.8.0

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 (511) hide show
  1. package/README.md +14 -5
  2. package/dist/app/adapt-data-widget.d.ts +1 -22
  3. package/dist/app/adapt-data-widget.d.ts.map +1 -1
  4. package/dist/app/adapt-data-widget.js +1 -35
  5. package/dist/app/adapt-data-widget.js.map +1 -1
  6. package/dist/app/builder/BuilderBanners.d.ts +7 -2
  7. package/dist/app/builder/BuilderBanners.d.ts.map +1 -1
  8. package/dist/app/builder/BuilderBanners.js +8 -3
  9. package/dist/app/builder/BuilderBanners.js.map +1 -1
  10. package/dist/app/builder/CanvasRow.d.ts +4 -2
  11. package/dist/app/builder/CanvasRow.d.ts.map +1 -1
  12. package/dist/app/builder/CanvasRow.js +29 -17
  13. package/dist/app/builder/CanvasRow.js.map +1 -1
  14. package/dist/app/builder/CatalogueSheet.d.ts.map +1 -1
  15. package/dist/app/builder/CatalogueSheet.js +14 -6
  16. package/dist/app/builder/CatalogueSheet.js.map +1 -1
  17. package/dist/app/builder/CellPropsSheet.d.ts +7 -3
  18. package/dist/app/builder/CellPropsSheet.d.ts.map +1 -1
  19. package/dist/app/builder/CellPropsSheet.js +10 -3
  20. package/dist/app/builder/CellPropsSheet.js.map +1 -1
  21. package/dist/app/builder/DiscardDialog.d.ts +18 -0
  22. package/dist/app/builder/DiscardDialog.d.ts.map +1 -0
  23. package/dist/app/builder/DiscardDialog.js +18 -0
  24. package/dist/app/builder/DiscardDialog.js.map +1 -0
  25. package/dist/app/builder/EmptyHint.d.ts +4 -0
  26. package/dist/app/builder/EmptyHint.d.ts.map +1 -1
  27. package/dist/app/builder/EmptyHint.js +6 -1
  28. package/dist/app/builder/EmptyHint.js.map +1 -1
  29. package/dist/app/builder/LayoutSurface.d.ts +33 -9
  30. package/dist/app/builder/LayoutSurface.d.ts.map +1 -1
  31. package/dist/app/builder/LayoutSurface.js +74 -23
  32. package/dist/app/builder/LayoutSurface.js.map +1 -1
  33. package/dist/app/builder/PipelineStrip.d.ts +5 -2
  34. package/dist/app/builder/PipelineStrip.d.ts.map +1 -1
  35. package/dist/app/builder/PipelineStrip.js +33 -15
  36. package/dist/app/builder/PipelineStrip.js.map +1 -1
  37. package/dist/app/builder/SaveDialog.d.ts +9 -1
  38. package/dist/app/builder/SaveDialog.d.ts.map +1 -1
  39. package/dist/app/builder/SaveDialog.js +9 -3
  40. package/dist/app/builder/SaveDialog.js.map +1 -1
  41. package/dist/app/builder/Toolbar.d.ts +14 -4
  42. package/dist/app/builder/Toolbar.d.ts.map +1 -1
  43. package/dist/app/builder/Toolbar.js +24 -7
  44. package/dist/app/builder/Toolbar.js.map +1 -1
  45. package/dist/app/builder/Workspace.d.ts +23 -3
  46. package/dist/app/builder/Workspace.d.ts.map +1 -1
  47. package/dist/app/builder/Workspace.js +58 -8
  48. package/dist/app/builder/Workspace.js.map +1 -1
  49. package/dist/app/builder/builder-model.d.ts +146 -3
  50. package/dist/app/builder/builder-model.d.ts.map +1 -1
  51. package/dist/app/builder/builder-model.js +196 -5
  52. package/dist/app/builder/builder-model.js.map +1 -1
  53. package/dist/app/builder/labels.d.ts +13 -62
  54. package/dist/app/builder/labels.d.ts.map +1 -1
  55. package/dist/app/builder/labels.js +9 -58
  56. package/dist/app/builder/labels.js.map +1 -1
  57. package/dist/app/builder/reveal.d.ts +10 -0
  58. package/dist/app/builder/reveal.d.ts.map +1 -0
  59. package/dist/app/builder/reveal.js +10 -0
  60. package/dist/app/builder/reveal.js.map +1 -0
  61. package/dist/app/builder/status-chip.d.ts.map +1 -1
  62. package/dist/app/builder/status-chip.js +3 -2
  63. package/dist/app/builder/status-chip.js.map +1 -1
  64. package/dist/app/builder/use-builder-catalogue.d.ts +5 -1
  65. package/dist/app/builder/use-builder-catalogue.d.ts.map +1 -1
  66. package/dist/app/builder/use-builder-catalogue.js +50 -17
  67. package/dist/app/builder/use-builder-catalogue.js.map +1 -1
  68. package/dist/app/builder/use-builder-session.d.ts +30 -0
  69. package/dist/app/builder/use-builder-session.d.ts.map +1 -0
  70. package/dist/app/builder/use-builder-session.js +58 -0
  71. package/dist/app/builder/use-builder-session.js.map +1 -0
  72. package/dist/app/builder/use-display-resolver.d.ts +17 -0
  73. package/dist/app/builder/use-display-resolver.d.ts.map +1 -0
  74. package/dist/app/builder/use-display-resolver.js +16 -0
  75. package/dist/app/builder/use-display-resolver.js.map +1 -0
  76. package/dist/app/builder/use-save-dashboard.d.ts +3 -1
  77. package/dist/app/builder/use-save-dashboard.d.ts.map +1 -1
  78. package/dist/app/builder/use-save-dashboard.js +3 -1
  79. package/dist/app/builder/use-save-dashboard.js.map +1 -1
  80. package/dist/app/index.d.ts +8 -3
  81. package/dist/app/index.d.ts.map +1 -1
  82. package/dist/app/index.js +8 -2
  83. package/dist/app/index.js.map +1 -1
  84. package/dist/app/layout-builder.d.ts +18 -25
  85. package/dist/app/layout-builder.d.ts.map +1 -1
  86. package/dist/app/layout-builder.js +84 -78
  87. package/dist/app/layout-builder.js.map +1 -1
  88. package/dist/app/mcp-app-view-chrome.d.ts +103 -20
  89. package/dist/app/mcp-app-view-chrome.d.ts.map +1 -1
  90. package/dist/app/mcp-app-view-chrome.js +125 -40
  91. package/dist/app/mcp-app-view-chrome.js.map +1 -1
  92. package/dist/app/mcp-app-view-states.d.ts +23 -0
  93. package/dist/app/mcp-app-view-states.d.ts.map +1 -0
  94. package/dist/app/mcp-app-view-states.js +55 -0
  95. package/dist/app/mcp-app-view-states.js.map +1 -0
  96. package/dist/app/mcp-app-view.d.ts +72 -5
  97. package/dist/app/mcp-app-view.d.ts.map +1 -1
  98. package/dist/app/mcp-app-view.js +241 -141
  99. package/dist/app/mcp-app-view.js.map +1 -1
  100. package/dist/app/mcp-toolkit-app.d.ts +8 -2
  101. package/dist/app/mcp-toolkit-app.d.ts.map +1 -1
  102. package/dist/app/mcp-toolkit-app.js +33 -4
  103. package/dist/app/mcp-toolkit-app.js.map +1 -1
  104. package/dist/app/mcp-use-host-bridge.d.ts +15 -6
  105. package/dist/app/mcp-use-host-bridge.d.ts.map +1 -1
  106. package/dist/app/mcp-use-host-bridge.js +51 -22
  107. package/dist/app/mcp-use-host-bridge.js.map +1 -1
  108. package/dist/app/model-context.d.ts +71 -0
  109. package/dist/app/model-context.d.ts.map +1 -0
  110. package/dist/app/model-context.js +101 -0
  111. package/dist/app/model-context.js.map +1 -0
  112. package/dist/app/use-build-mode-focus.d.ts +33 -0
  113. package/dist/app/use-build-mode-focus.d.ts.map +1 -0
  114. package/dist/app/use-build-mode-focus.js +59 -0
  115. package/dist/app/use-build-mode-focus.js.map +1 -0
  116. package/dist/app/use-view-actions.d.ts +31 -0
  117. package/dist/app/use-view-actions.d.ts.map +1 -0
  118. package/dist/app/use-view-actions.js +81 -0
  119. package/dist/app/use-view-actions.js.map +1 -0
  120. package/dist/app/use-view-lifecycle.d.ts +42 -0
  121. package/dist/app/use-view-lifecycle.d.ts.map +1 -0
  122. package/dist/app/use-view-lifecycle.js +81 -0
  123. package/dist/app/use-view-lifecycle.js.map +1 -0
  124. package/dist/app/view-lifecycle.d.ts +185 -0
  125. package/dist/app/view-lifecycle.d.ts.map +1 -0
  126. package/dist/app/view-lifecycle.js +207 -0
  127. package/dist/app/view-lifecycle.js.map +1 -0
  128. package/dist/app/widget-fixture.d.ts +10 -3
  129. package/dist/app/widget-fixture.d.ts.map +1 -1
  130. package/dist/app/widget-fixture.js +21 -8
  131. package/dist/app/widget-fixture.js.map +1 -1
  132. package/dist/app/widget-props-sheet.d.ts +6 -10
  133. package/dist/app/widget-props-sheet.d.ts.map +1 -1
  134. package/dist/app/widget-props-sheet.js +25 -20
  135. package/dist/app/widget-props-sheet.js.map +1 -1
  136. package/dist/app/widget-renderer.d.ts +41 -1
  137. package/dist/app/widget-renderer.d.ts.map +1 -1
  138. package/dist/app/widget-renderer.js +82 -31
  139. package/dist/app/widget-renderer.js.map +1 -1
  140. package/dist/components/DrillButton.d.ts +9 -6
  141. package/dist/components/DrillButton.d.ts.map +1 -1
  142. package/dist/components/DrillButton.js +9 -6
  143. package/dist/components/DrillButton.js.map +1 -1
  144. package/dist/components/EmptyState.d.ts +33 -0
  145. package/dist/components/EmptyState.d.ts.map +1 -0
  146. package/dist/components/EmptyState.js +19 -0
  147. package/dist/components/EmptyState.js.map +1 -0
  148. package/dist/components/ErrorState.d.ts +53 -0
  149. package/dist/components/ErrorState.d.ts.map +1 -0
  150. package/dist/components/ErrorState.js +77 -0
  151. package/dist/components/ErrorState.js.map +1 -0
  152. package/dist/components/FilterBar.d.ts +16 -5
  153. package/dist/components/FilterBar.d.ts.map +1 -1
  154. package/dist/components/FilterBar.js +13 -5
  155. package/dist/components/FilterBar.js.map +1 -1
  156. package/dist/components/GroupCard.d.ts.map +1 -1
  157. package/dist/components/GroupCard.js +2 -1
  158. package/dist/components/GroupCard.js.map +1 -1
  159. package/dist/components/KpiGrid.d.ts +70 -18
  160. package/dist/components/KpiGrid.d.ts.map +1 -1
  161. package/dist/components/KpiGrid.js +112 -23
  162. package/dist/components/KpiGrid.js.map +1 -1
  163. package/dist/components/ListFooter.d.ts +32 -4
  164. package/dist/components/ListFooter.d.ts.map +1 -1
  165. package/dist/components/ListFooter.js +35 -6
  166. package/dist/components/ListFooter.js.map +1 -1
  167. package/dist/components/PagedListFooter.d.ts +57 -0
  168. package/dist/components/PagedListFooter.d.ts.map +1 -0
  169. package/dist/components/PagedListFooter.js +27 -0
  170. package/dist/components/PagedListFooter.js.map +1 -0
  171. package/dist/components/Pills.d.ts +36 -12
  172. package/dist/components/Pills.d.ts.map +1 -1
  173. package/dist/components/Pills.js +29 -8
  174. package/dist/components/Pills.js.map +1 -1
  175. package/dist/components/SectionHeading.d.ts +3 -1
  176. package/dist/components/SectionHeading.d.ts.map +1 -1
  177. package/dist/components/SectionHeading.js +6 -2
  178. package/dist/components/SectionHeading.js.map +1 -1
  179. package/dist/components/Toast.d.ts +43 -0
  180. package/dist/components/Toast.d.ts.map +1 -0
  181. package/dist/components/Toast.js +106 -0
  182. package/dist/components/Toast.js.map +1 -0
  183. package/dist/components/WidgetHeader.d.ts +34 -10
  184. package/dist/components/WidgetHeader.d.ts.map +1 -1
  185. package/dist/components/WidgetHeader.js +11 -6
  186. package/dist/components/WidgetHeader.js.map +1 -1
  187. package/dist/hooks/index.d.ts +4 -4
  188. package/dist/hooks/index.d.ts.map +1 -1
  189. package/dist/hooks/index.js +1 -1
  190. package/dist/hooks/index.js.map +1 -1
  191. package/dist/hooks/tool-result-recovery-model.d.ts +155 -0
  192. package/dist/hooks/tool-result-recovery-model.d.ts.map +1 -0
  193. package/dist/hooks/tool-result-recovery-model.js +273 -0
  194. package/dist/hooks/tool-result-recovery-model.js.map +1 -0
  195. package/dist/hooks/use-tool-query.d.ts +56 -14
  196. package/dist/hooks/use-tool-query.d.ts.map +1 -1
  197. package/dist/hooks/use-tool-query.js +102 -25
  198. package/dist/hooks/use-tool-query.js.map +1 -1
  199. package/dist/hooks/use-tool-result-recovery.d.ts +61 -44
  200. package/dist/hooks/use-tool-result-recovery.d.ts.map +1 -1
  201. package/dist/hooks/use-tool-result-recovery.js +62 -110
  202. package/dist/hooks/use-tool-result-recovery.js.map +1 -1
  203. package/dist/hooks/use-view-data.d.ts +55 -7
  204. package/dist/hooks/use-view-data.d.ts.map +1 -1
  205. package/dist/hooks/use-view-data.js +81 -11
  206. package/dist/hooks/use-view-data.js.map +1 -1
  207. package/dist/hooks/use-view-tool-query.d.ts +12 -7
  208. package/dist/hooks/use-view-tool-query.d.ts.map +1 -1
  209. package/dist/hooks/use-view-tool-query.js +15 -14
  210. package/dist/hooks/use-view-tool-query.js.map +1 -1
  211. package/dist/index.d.ts +27 -13
  212. package/dist/index.d.ts.map +1 -1
  213. package/dist/index.js +29 -7
  214. package/dist/index.js.map +1 -1
  215. package/dist/lib/focus-return.d.ts +61 -0
  216. package/dist/lib/focus-return.d.ts.map +1 -0
  217. package/dist/lib/focus-return.js +46 -0
  218. package/dist/lib/focus-return.js.map +1 -0
  219. package/dist/lib/focus-ring.d.ts +18 -0
  220. package/dist/lib/focus-ring.d.ts.map +1 -0
  221. package/dist/lib/focus-ring.js +18 -0
  222. package/dist/lib/focus-ring.js.map +1 -0
  223. package/dist/lib/icon-stroke.d.ts +12 -0
  224. package/dist/lib/icon-stroke.d.ts.map +1 -0
  225. package/dist/lib/icon-stroke.js +12 -0
  226. package/dist/lib/icon-stroke.js.map +1 -0
  227. package/dist/lib/parse-tool-result.d.ts +23 -1
  228. package/dist/lib/parse-tool-result.d.ts.map +1 -1
  229. package/dist/lib/parse-tool-result.js +38 -2
  230. package/dist/lib/parse-tool-result.js.map +1 -1
  231. package/dist/lib/toast-model.d.ts +86 -0
  232. package/dist/lib/toast-model.d.ts.map +1 -0
  233. package/dist/lib/toast-model.js +102 -0
  234. package/dist/lib/toast-model.js.map +1 -0
  235. package/dist/lib/tone-utils.d.ts +95 -9
  236. package/dist/lib/tone-utils.d.ts.map +1 -1
  237. package/dist/lib/tone-utils.js +100 -9
  238. package/dist/lib/tone-utils.js.map +1 -1
  239. package/dist/lib/tool-query.d.ts +107 -0
  240. package/dist/lib/tool-query.d.ts.map +1 -0
  241. package/dist/lib/tool-query.js +182 -0
  242. package/dist/lib/tool-query.js.map +1 -0
  243. package/dist/lib/tool-result-observer.d.ts +108 -0
  244. package/dist/lib/tool-result-observer.d.ts.map +1 -0
  245. package/dist/lib/tool-result-observer.js +158 -0
  246. package/dist/lib/tool-result-observer.js.map +1 -0
  247. package/dist/lib/typography.d.ts +37 -0
  248. package/dist/lib/typography.d.ts.map +1 -0
  249. package/dist/lib/typography.js +41 -0
  250. package/dist/lib/typography.js.map +1 -0
  251. package/dist/lib/utils.d.ts.map +1 -1
  252. package/dist/lib/utils.js +11 -1
  253. package/dist/lib/utils.js.map +1 -1
  254. package/dist/locale/index.d.ts +3 -1
  255. package/dist/locale/index.d.ts.map +1 -1
  256. package/dist/locale/index.js +2 -1
  257. package/dist/locale/index.js.map +1 -1
  258. package/dist/locale/locale-provider.d.ts +42 -1
  259. package/dist/locale/locale-provider.d.ts.map +1 -1
  260. package/dist/locale/locale-provider.js +44 -3
  261. package/dist/locale/locale-provider.js.map +1 -1
  262. package/dist/locale/message-types.d.ts +336 -0
  263. package/dist/locale/message-types.d.ts.map +1 -0
  264. package/dist/locale/message-types.js +7 -0
  265. package/dist/locale/message-types.js.map +1 -0
  266. package/dist/locale/messages-de.d.ts +9 -0
  267. package/dist/locale/messages-de.d.ts.map +1 -0
  268. package/dist/locale/messages-de.js +185 -0
  269. package/dist/locale/messages-de.js.map +1 -0
  270. package/dist/locale/messages-en.d.ts +8 -0
  271. package/dist/locale/messages-en.d.ts.map +1 -0
  272. package/dist/locale/messages-en.js +184 -0
  273. package/dist/locale/messages-en.js.map +1 -0
  274. package/dist/locale/toolkit-messages.d.ts +57 -0
  275. package/dist/locale/toolkit-messages.d.ts.map +1 -0
  276. package/dist/locale/toolkit-messages.js +103 -0
  277. package/dist/locale/toolkit-messages.js.map +1 -0
  278. package/dist/primitives/alert.d.ts +1 -1
  279. package/dist/primitives/alert.d.ts.map +1 -1
  280. package/dist/primitives/alert.js +6 -2
  281. package/dist/primitives/alert.js.map +1 -1
  282. package/dist/primitives/badge.d.ts +1 -1
  283. package/dist/primitives/badge.d.ts.map +1 -1
  284. package/dist/primitives/badge.js +19 -6
  285. package/dist/primitives/badge.js.map +1 -1
  286. package/dist/primitives/button.d.ts +1 -1
  287. package/dist/primitives/button.d.ts.map +1 -1
  288. package/dist/primitives/button.js +12 -8
  289. package/dist/primitives/button.js.map +1 -1
  290. package/dist/primitives/card.js +4 -4
  291. package/dist/primitives/card.js.map +1 -1
  292. package/dist/primitives/dialog.d.ts +7 -3
  293. package/dist/primitives/dialog.d.ts.map +1 -1
  294. package/dist/primitives/dialog.js +22 -8
  295. package/dist/primitives/dialog.js.map +1 -1
  296. package/dist/primitives/input.d.ts.map +1 -1
  297. package/dist/primitives/input.js +4 -1
  298. package/dist/primitives/input.js.map +1 -1
  299. package/dist/primitives/scroll-area.js +1 -1
  300. package/dist/primitives/scroll-area.js.map +1 -1
  301. package/dist/primitives/select.d.ts.map +1 -1
  302. package/dist/primitives/select.js +6 -3
  303. package/dist/primitives/select.js.map +1 -1
  304. package/dist/primitives/sheet.d.ts +3 -1
  305. package/dist/primitives/sheet.d.ts.map +1 -1
  306. package/dist/primitives/sheet.js +17 -7
  307. package/dist/primitives/sheet.js.map +1 -1
  308. package/dist/primitives/skeleton.js +1 -1
  309. package/dist/primitives/skeleton.js.map +1 -1
  310. package/dist/primitives/switch.d.ts.map +1 -1
  311. package/dist/primitives/switch.js +2 -1
  312. package/dist/primitives/switch.js.map +1 -1
  313. package/dist/primitives/tabs.d.ts.map +1 -1
  314. package/dist/primitives/tabs.js +5 -3
  315. package/dist/primitives/tabs.js.map +1 -1
  316. package/dist/providers/query-provider.d.ts +32 -1
  317. package/dist/providers/query-provider.d.ts.map +1 -1
  318. package/dist/providers/query-provider.js +53 -4
  319. package/dist/providers/query-provider.js.map +1 -1
  320. package/dist/render-cases-primitives.js +1 -1
  321. package/dist/render-cases-primitives.js.map +1 -1
  322. package/dist/render-cases.d.ts.map +1 -1
  323. package/dist/render-cases.js +48 -6
  324. package/dist/render-cases.js.map +1 -1
  325. package/dist/render-effects.d.ts +42 -0
  326. package/dist/render-effects.d.ts.map +1 -0
  327. package/dist/render-effects.js +81 -0
  328. package/dist/render-effects.js.map +1 -0
  329. package/dist/render-scan.d.ts +25 -0
  330. package/dist/render-scan.d.ts.map +1 -0
  331. package/dist/render-scan.js +66 -0
  332. package/dist/render-scan.js.map +1 -0
  333. package/dist/theme/cd-tokens.d.ts +66 -0
  334. package/dist/theme/cd-tokens.d.ts.map +1 -0
  335. package/dist/theme/cd-tokens.js +64 -0
  336. package/dist/theme/cd-tokens.js.map +1 -0
  337. package/dist/theme/create-theme.d.ts +214 -39
  338. package/dist/theme/create-theme.d.ts.map +1 -1
  339. package/dist/theme/create-theme.js +204 -13
  340. package/dist/theme/create-theme.js.map +1 -1
  341. package/dist/theme/fonts.d.ts +38 -0
  342. package/dist/theme/fonts.d.ts.map +1 -0
  343. package/dist/theme/fonts.js +56 -0
  344. package/dist/theme/fonts.js.map +1 -0
  345. package/dist/theme/host-fonts.d.ts +39 -0
  346. package/dist/theme/host-fonts.d.ts.map +1 -0
  347. package/dist/theme/host-fonts.js +44 -0
  348. package/dist/theme/host-fonts.js.map +1 -0
  349. package/dist/theme/index.d.ts +8 -1
  350. package/dist/theme/index.d.ts.map +1 -1
  351. package/dist/theme/index.js +5 -1
  352. package/dist/theme/index.js.map +1 -1
  353. package/dist/theme/portal-container.d.ts +16 -0
  354. package/dist/theme/portal-container.d.ts.map +1 -0
  355. package/dist/theme/portal-container.js +19 -0
  356. package/dist/theme/portal-container.js.map +1 -0
  357. package/dist/theme/presets.d.ts +24 -9
  358. package/dist/theme/presets.d.ts.map +1 -1
  359. package/dist/theme/presets.js +44 -11
  360. package/dist/theme/presets.js.map +1 -1
  361. package/dist/theme/theme-provider.d.ts +27 -9
  362. package/dist/theme/theme-provider.d.ts.map +1 -1
  363. package/dist/theme/theme-provider.js +42 -20
  364. package/dist/theme/theme-provider.js.map +1 -1
  365. package/package.json +11 -5
  366. package/src/app/adapt-data-widget.tsx +2 -42
  367. package/src/app/builder/BuilderBanners.tsx +26 -13
  368. package/src/app/builder/CanvasRow.tsx +79 -32
  369. package/src/app/builder/CatalogueSheet.tsx +58 -35
  370. package/src/app/builder/CellPropsSheet.tsx +17 -2
  371. package/src/app/builder/DiscardDialog.tsx +59 -0
  372. package/src/app/builder/EmptyHint.tsx +16 -11
  373. package/src/app/builder/LayoutSurface.tsx +207 -67
  374. package/src/app/builder/PipelineStrip.tsx +265 -156
  375. package/src/app/builder/SaveDialog.tsx +25 -14
  376. package/src/app/builder/Toolbar.tsx +154 -57
  377. package/src/app/builder/Workspace.tsx +226 -56
  378. package/src/app/builder/builder-feedback.test.tsx +75 -0
  379. package/src/app/builder/builder-model.test.ts +5 -0
  380. package/src/app/builder/builder-model.ts +346 -6
  381. package/src/app/builder/builder-model.ux.test.ts +456 -0
  382. package/src/app/builder/builder-ux.test.tsx +844 -0
  383. package/src/app/builder/labels.ts +15 -121
  384. package/src/app/builder/reveal.ts +10 -0
  385. package/src/app/builder/status-chip.tsx +8 -4
  386. package/src/app/builder/use-builder-catalogue.ts +62 -19
  387. package/src/app/builder/use-builder-session.ts +68 -0
  388. package/src/app/builder/use-display-resolver.ts +24 -0
  389. package/src/app/builder/use-save-dashboard.ts +5 -0
  390. package/src/app/index.ts +20 -3
  391. package/src/app/layout-builder.tsx +193 -186
  392. package/src/app/mcp-app-view-chrome.test.tsx +487 -14
  393. package/src/app/mcp-app-view-chrome.tsx +328 -100
  394. package/src/app/mcp-app-view-states.tsx +140 -0
  395. package/src/app/mcp-app-view.effects.test.tsx +292 -0
  396. package/src/app/mcp-app-view.test.tsx +478 -0
  397. package/src/app/mcp-app-view.tsx +447 -190
  398. package/src/app/mcp-toolkit-app.test.tsx +40 -0
  399. package/src/app/mcp-toolkit-app.tsx +39 -8
  400. package/src/app/mcp-use-host-bridge.test.tsx +180 -0
  401. package/src/app/mcp-use-host-bridge.tsx +67 -24
  402. package/src/app/model-context.test.tsx +116 -0
  403. package/src/app/model-context.tsx +125 -0
  404. package/src/app/use-build-mode-focus.ts +62 -0
  405. package/src/app/use-view-actions.ts +89 -0
  406. package/src/app/use-view-lifecycle.ts +122 -0
  407. package/src/app/view-lifecycle.test.ts +443 -0
  408. package/src/app/view-lifecycle.ts +334 -0
  409. package/src/app/widget-fixture.effects.test.tsx +51 -0
  410. package/src/app/widget-fixture.render.test.tsx +81 -0
  411. package/src/app/widget-fixture.tsx +34 -11
  412. package/src/app/widget-props-sheet.tsx +67 -39
  413. package/src/app/widget-renderer.render.test.tsx +131 -0
  414. package/src/app/widget-renderer.tsx +136 -38
  415. package/src/components/DrillButton.tsx +24 -10
  416. package/src/components/EmptyState.tsx +83 -0
  417. package/src/components/ErrorState.tsx +204 -0
  418. package/src/components/FilterBar.test.tsx +82 -0
  419. package/src/components/FilterBar.tsx +56 -27
  420. package/src/components/GroupCard.tsx +3 -2
  421. package/src/components/KpiGrid.test.tsx +279 -0
  422. package/src/components/KpiGrid.tsx +329 -91
  423. package/src/components/ListFooter.test.tsx +116 -0
  424. package/src/components/ListFooter.tsx +110 -12
  425. package/src/components/PagedListFooter.test.tsx +140 -0
  426. package/src/components/PagedListFooter.tsx +107 -0
  427. package/src/components/Pills.test.tsx +77 -0
  428. package/src/components/Pills.tsx +78 -16
  429. package/src/components/SectionHeading.tsx +9 -3
  430. package/src/components/Toast.tsx +206 -0
  431. package/src/components/WidgetHeader.test.tsx +78 -0
  432. package/src/components/WidgetHeader.tsx +63 -16
  433. package/src/components/feedback-states.test.tsx +314 -0
  434. package/src/components/tone-figures.test.tsx +58 -0
  435. package/src/fonts.css +19 -0
  436. package/src/globals.css +9 -165
  437. package/src/hooks/index.ts +6 -3
  438. package/src/hooks/tool-result-recovery-model.test.ts +744 -0
  439. package/src/hooks/tool-result-recovery-model.ts +418 -0
  440. package/src/hooks/use-tool-query.effects.test.tsx +205 -0
  441. package/src/hooks/use-tool-query.ts +156 -33
  442. package/src/hooks/use-tool-result-recovery.effects.test.tsx +141 -0
  443. package/src/hooks/use-tool-result-recovery.test.ts +28 -5
  444. package/src/hooks/use-tool-result-recovery.ts +101 -131
  445. package/src/hooks/use-view-data.test.tsx +169 -0
  446. package/src/hooks/use-view-data.ts +131 -10
  447. package/src/hooks/use-view-tool-query.ts +25 -19
  448. package/src/i18n-coverage.test.tsx +425 -0
  449. package/src/index.ts +102 -13
  450. package/src/lib/focus-return.test.ts +288 -0
  451. package/src/lib/focus-return.ts +96 -0
  452. package/src/lib/focus-ring.ts +20 -0
  453. package/src/lib/icon-stroke.test.ts +59 -0
  454. package/src/lib/icon-stroke.ts +13 -0
  455. package/src/lib/parse-tool-result.test.ts +46 -1
  456. package/src/lib/parse-tool-result.ts +40 -2
  457. package/src/lib/toast-model.test.ts +233 -0
  458. package/src/lib/toast-model.ts +165 -0
  459. package/src/lib/tone-utils.test.ts +125 -16
  460. package/src/lib/tone-utils.ts +144 -15
  461. package/src/lib/tool-query.test.ts +569 -0
  462. package/src/lib/tool-query.ts +253 -0
  463. package/src/lib/tool-result-observer.test.ts +296 -0
  464. package/src/lib/tool-result-observer.ts +211 -0
  465. package/src/lib/typography.test.ts +45 -0
  466. package/src/lib/typography.ts +47 -0
  467. package/src/lib/utils.ts +12 -1
  468. package/src/locale/index.ts +14 -1
  469. package/src/locale/locale-provider.test.tsx +87 -0
  470. package/src/locale/locale-provider.tsx +90 -3
  471. package/src/locale/message-types.ts +337 -0
  472. package/src/locale/messages-de.ts +198 -0
  473. package/src/locale/messages-en.ts +196 -0
  474. package/src/locale/toolkit-messages.test.ts +349 -0
  475. package/src/locale/toolkit-messages.ts +143 -0
  476. package/src/no-hardcoded-copy.test.ts +432 -0
  477. package/src/primitives/alert.tsx +7 -2
  478. package/src/primitives/badge.tsx +19 -7
  479. package/src/primitives/button.tsx +13 -8
  480. package/src/primitives/card.tsx +4 -4
  481. package/src/primitives/dialog.tsx +42 -8
  482. package/src/primitives/input.tsx +5 -3
  483. package/src/primitives/scroll-area.tsx +1 -1
  484. package/src/primitives/select.tsx +7 -4
  485. package/src/primitives/sheet.tsx +33 -6
  486. package/src/primitives/skeleton.tsx +1 -1
  487. package/src/primitives/switch.tsx +4 -2
  488. package/src/primitives/tabs.tsx +9 -6
  489. package/src/providers/query-provider.test.tsx +154 -0
  490. package/src/providers/query-provider.tsx +66 -5
  491. package/src/render-cases-primitives.tsx +1 -1
  492. package/src/render-cases.tsx +82 -6
  493. package/src/render-effects.tsx +96 -0
  494. package/src/render-scan.ts +75 -0
  495. package/src/theme/cd-tokens.generated.css +48 -0
  496. package/src/theme/cd-tokens.test.ts +109 -0
  497. package/src/theme/cd-tokens.ts +67 -0
  498. package/src/theme/create-theme.test.ts +253 -5
  499. package/src/theme/create-theme.ts +341 -41
  500. package/src/theme/fonts.test.ts +85 -0
  501. package/src/theme/fonts.ts +72 -0
  502. package/src/theme/host-fonts.test.ts +135 -0
  503. package/src/theme/host-fonts.ts +52 -0
  504. package/src/theme/index.ts +15 -1
  505. package/src/theme/portal-container.ts +20 -0
  506. package/src/theme/presets.ts +44 -11
  507. package/src/theme/theme-css.test.ts +619 -0
  508. package/src/theme/theme-provider.test.tsx +82 -0
  509. package/src/theme/theme-provider.tsx +60 -24
  510. package/src/theme.css +344 -0
  511. package/ui-catalog.json +783 -49
@@ -0,0 +1,75 @@
1
+ import type { ReactNode } from "react"
2
+ import { renderToStaticMarkup } from "react-dom/server"
3
+ import { describe, expect, it } from "vitest"
4
+ import { LocaleProvider } from "../../locale/locale-provider.js"
5
+ import { TOOLKIT_MESSAGES } from "../../locale/toolkit-messages.js"
6
+ import { attribute, decodeEntities, openingTags } from "../../render-scan.js"
7
+ import { BuilderBanners } from "./BuilderBanners.js"
8
+ import { EmptyHint } from "./EmptyHint.js"
9
+
10
+ /**
11
+ * The builder's feedback states on their SSR markup: a failure says what
12
+ * happened and what to do in catalog words, keeps the raw reason under
13
+ * "Details", and offers the next step; the empty state leads with the blue
14
+ * primary button (CI §9).
15
+ */
16
+
17
+ const de = TOOLKIT_MESSAGES.de
18
+ const inGerman = (node: ReactNode) =>
19
+ renderToStaticMarkup(<LocaleProvider locale="de-DE">{node}</LocaleProvider>)
20
+ const noop = (): void => {}
21
+ const RAW = "MCP error -32602: Tool get-builder-catalogue not found"
22
+
23
+ describe("BuilderBanners: catalogue failure", () => {
24
+ const html = inGerman(
25
+ <BuilderBanners
26
+ L={de.builder}
27
+ catalogueToolName="get-builder-catalogue"
28
+ refreshError={RAW}
29
+ validationIssues={[]}
30
+ onRetry={noop}
31
+ />,
32
+ )
33
+
34
+ it("is the kit's inline error state, announced as an alert", () => {
35
+ const root = openingTags(html, "div").find((t) => /data-slot="error-state"/.test(t)) ?? ""
36
+ expect(attribute(root, "role")).toBe("alert")
37
+ expect(attribute(root, "data-variant")).toBe("inline")
38
+ })
39
+
40
+ it("says what happened and what to do in German, with a retry", () => {
41
+ const text = decodeEntities(html)
42
+ expect(text).toContain(de.builder.catalogueError)
43
+ expect(text).toContain(de.builder.catalogueErrorHint)
44
+ expect(text).toContain(`${de.common.retry}</button>`)
45
+ })
46
+
47
+ it("keeps the raw reason under Details only, never in a sentence the user reads", () => {
48
+ const details = /<details[^>]*>([\s\S]*?)<\/details>/.exec(html)?.[1] ?? ""
49
+ expect(decodeEntities(details)).toContain(`get-builder-catalogue: ${RAW}`)
50
+ const outside = decodeEntities(html.replace(/<details[\s\S]*?<\/details>/g, ""))
51
+ expect(outside).not.toContain(RAW)
52
+ })
53
+
54
+ it("has no retry without a handler", () => {
55
+ const bare = inGerman(
56
+ <BuilderBanners
57
+ L={de.builder}
58
+ catalogueToolName="get-builder-catalogue"
59
+ refreshError={RAW}
60
+ validationIssues={[]}
61
+ />,
62
+ )
63
+ expect(bare).not.toContain("<button")
64
+ })
65
+ })
66
+
67
+ describe("EmptyHint", () => {
68
+ it("offers its one next step as the primary (blue) button", () => {
69
+ const html = inGerman(<EmptyHint L={de.builder} onGotoPipeline={noop} />)
70
+ const buttons = openingTags(html, "button")
71
+ expect(buttons).toHaveLength(1)
72
+ expect(attribute(buttons[0] ?? "", "data-variant")).toBe("default")
73
+ expect(decodeEntities(html)).toContain(`${de.builder.openDataSources}</button>`)
74
+ })
75
+ })
@@ -260,6 +260,11 @@ describe("formatLiveKeyValue", () => {
260
260
  expect(formatLiveKeyValue("abc")).toBe("live: abc")
261
261
  })
262
262
 
263
+ it("takes the localized prefix", () => {
264
+ expect(formatLiveKeyValue("abc", "vorhanden")).toBe("vorhanden: abc")
265
+ expect(formatLiveKeyValue(42, "vorhanden")).toBe("vorhanden: 42")
266
+ })
267
+
263
268
  it("JSON-stringifies non-string values", () => {
264
269
  expect(formatLiveKeyValue(42)).toBe("live: 42")
265
270
  expect(formatLiveKeyValue({ a: 1 })).toBe('live: {"a":1}')
@@ -7,8 +7,18 @@
7
7
  * `parseKeyValue ↔ keysToEntries ↔ entriesToKeys` round-trip and
8
8
  * `draftToLayout` — unit-testable in isolation.
9
9
  */
10
- import type { LayoutConfig, PipelineContext, RowDef } from "@miragon/mcp-toolkit-core"
11
- import { normalizeLayout } from "@miragon/mcp-toolkit-core"
10
+ import type {
11
+ DisplayText,
12
+ LayoutConfig,
13
+ PipelineContext,
14
+ PipelineStepRef,
15
+ RowDef,
16
+ } from "@miragon/mcp-toolkit-core"
17
+ import {
18
+ normalizeLayout,
19
+ PROPS_SCHEMA_DISPLAY_KEYWORD,
20
+ resolveLocalizedText,
21
+ } from "@miragon/mcp-toolkit-core"
12
22
 
13
23
  // -------------------------------------------------------------------------- //
14
24
  // Draft types
@@ -136,14 +146,15 @@ export function entriesToKeys(entries: KeyEntry[]): Record<string, unknown> {
136
146
  * Compact preview of a value as it currently lives in the pipeline.
137
147
  * Used as the placeholder when the editor's `rawValue` is empty so the
138
148
  * user can see what the server is actually working with right now
139
- * without the editor overwriting their (empty) input.
149
+ * without the editor overwriting their (empty) input. `prefix` is the
150
+ * localized word for "live" (the builder passes its catalog's).
140
151
  */
141
- export function formatLiveKeyValue(value: unknown): string | undefined {
152
+ export function formatLiveKeyValue(value: unknown, prefix = "live"): string | undefined {
142
153
  if (value === undefined) return undefined
143
154
  if (value === "") return undefined
144
- if (typeof value === "string") return `live: ${value}`
155
+ if (typeof value === "string") return `${prefix}: ${value}`
145
156
  try {
146
- return `live: ${JSON.stringify(value)}`
157
+ return `${prefix}: ${JSON.stringify(value)}`
147
158
  } catch {
148
159
  return undefined
149
160
  }
@@ -211,3 +222,332 @@ export function toPipelineContext(ctx: WireContext): PipelineContext {
211
222
  }
212
223
  return { steps, keys: ctx.keys, errors: ctx.errors }
213
224
  }
225
+
226
+ // -------------------------------------------------------------------------- //
227
+ // Display text — what the builder calls a widget, a step or a props field
228
+ // -------------------------------------------------------------------------- //
229
+
230
+ /** Resolves an i18n key in the active locale; `undefined` when it is unknown. */
231
+ export type DisplayKeyResolver = (key: string) => string | undefined
232
+
233
+ /**
234
+ * A {@link DisplayKeyResolver} over a translate function that echoes unknown
235
+ * keys (the `createTranslator` / `useTranslate` contract): the echo, or a
236
+ * blank result, means "unknown", so the caller falls back to its inline text.
237
+ */
238
+ export function keyResolverFrom(translate: (key: string) => string): DisplayKeyResolver {
239
+ return (key) => {
240
+ const text = translate(key).trim()
241
+ return text.length > 0 && text !== key ? text : undefined
242
+ }
243
+ }
244
+
245
+ /**
246
+ * The i18n key's text when the resolver knows it, else the inline (per-locale)
247
+ * text. Display text crosses the wire (the catalogue, a `propsSchema`), so a
248
+ * key that is not a string is ignored instead of reaching the translator.
249
+ */
250
+ function keyedText(
251
+ key: unknown,
252
+ text: DisplayText["title"],
253
+ locale: string,
254
+ resolveKey: DisplayKeyResolver | undefined,
255
+ ): string | undefined {
256
+ const viaKey = typeof key === "string" && resolveKey ? resolveKey(key) : undefined
257
+ return viaKey ?? resolveLocalizedText(text, locale)
258
+ }
259
+
260
+ /** How the builder names one entry: a label, the technical id as a quiet hint, a one-liner. */
261
+ export interface DisplayLabel {
262
+ /** The name people read: the entry's title, else its id. */
263
+ label: string
264
+ /** The technical id, set only when the label is a real title (so it is never shown twice). */
265
+ hint?: string
266
+ /** The one-line summary, when the entry has one. */
267
+ summary?: string
268
+ }
269
+
270
+ /**
271
+ * The builder's name for a widget or a step: its `titleKey` (when `resolveKey`
272
+ * knows it), its `title` in `locale`, else the id. The id comes back as `hint`
273
+ * only next to a real title. The `description` is the LLM's text and never
274
+ * appears here.
275
+ */
276
+ export function displayLabel(
277
+ entry: DisplayText,
278
+ id: string,
279
+ locale: string,
280
+ resolveKey?: DisplayKeyResolver,
281
+ ): DisplayLabel {
282
+ const title = keyedText(entry.titleKey, entry.title, locale, resolveKey)
283
+ const summary = keyedText(entry.summaryKey, entry.summary, locale, resolveKey)
284
+ return {
285
+ label: title ?? id,
286
+ ...(title !== undefined && title !== id ? { hint: id } : {}),
287
+ ...(summary !== undefined ? { summary } : {}),
288
+ }
289
+ }
290
+
291
+ /** A props-sheet field: its label, the raw property name as a hint, and its help line. */
292
+ export interface PropFieldText {
293
+ label: string
294
+ /** The property name, set only when the label differs from it. */
295
+ hint?: string
296
+ help?: string
297
+ }
298
+
299
+ function isRecord(value: unknown): value is Record<string, unknown> {
300
+ return typeof value === "object" && value !== null && !Array.isArray(value)
301
+ }
302
+
303
+ /**
304
+ * The props sheet's text for one `propsSchema` property. Label: the
305
+ * `x-display` title (key or text), else the standard JSON Schema `title`,
306
+ * else the property name. Help: the `x-display` summary, else the property's
307
+ * `description`.
308
+ */
309
+ export function propFieldText(
310
+ name: string,
311
+ prop: Record<string, unknown>,
312
+ locale: string,
313
+ resolveKey?: DisplayKeyResolver,
314
+ ): PropFieldText {
315
+ const raw = prop[PROPS_SCHEMA_DISPLAY_KEYWORD]
316
+ const display: DisplayText = isRecord(raw) ? raw : {}
317
+ const schemaTitle = typeof prop.title === "string" ? prop.title : undefined
318
+ const label =
319
+ keyedText(display.titleKey, display.title, locale, resolveKey) ??
320
+ resolveLocalizedText(schemaTitle, locale) ??
321
+ name
322
+ const schemaHelp = typeof prop.description === "string" ? prop.description : undefined
323
+ const help =
324
+ keyedText(display.summaryKey, display.summary, locale, resolveKey) ??
325
+ resolveLocalizedText(schemaHelp, locale)
326
+ return {
327
+ label,
328
+ ...(label !== name ? { hint: name } : {}),
329
+ ...(help !== undefined ? { help } : {}),
330
+ }
331
+ }
332
+
333
+ // -------------------------------------------------------------------------- //
334
+ // Draft status — what Apply, Discard and the unsaved marker compare
335
+ // -------------------------------------------------------------------------- //
336
+
337
+ /**
338
+ * Whether the draft holds at least one element on ANY tab, not only the
339
+ * active one: a new, still empty tab must not disable "Save as dashboard" or
340
+ * the preview while another tab has content.
341
+ */
342
+ export function draftHasCells(draft: DraftLayout): boolean {
343
+ const rows = draft.kind === "tabs" ? draft.tabs.flatMap((t) => t.rows) : draft.rows
344
+ return rows.some((r) => r.row.length > 0)
345
+ }
346
+
347
+ /**
348
+ * A comparable fingerprint of what Apply and Save hand on: the layout, the
349
+ * keys (blank names dropped, values parsed) and the steps that name a step.
350
+ * An empty key or step row the user has not filled in changes nothing.
351
+ */
352
+ export function builderSnapshot(
353
+ draft: DraftLayout,
354
+ keyEntries: KeyEntry[],
355
+ stepEntries: PipelineStepRef[],
356
+ ): string {
357
+ return JSON.stringify({
358
+ layout: draftToLayout(draft),
359
+ keys: entriesToKeys(keyEntries),
360
+ steps: stepEntries.filter((s) => s.step.trim().length > 0),
361
+ })
362
+ }
363
+
364
+ /** What Apply hands to `onExit`: the draft, plus the live context it was previewed with. */
365
+ export interface BuilderExitCommit {
366
+ layout: LayoutConfig
367
+ keys?: Record<string, unknown>
368
+ steps?: PipelineStepRef[]
369
+ title?: string
370
+ context?: WireContext
371
+ }
372
+
373
+ /**
374
+ * The commit of Apply: the layout, keys and steps exactly as a save would send
375
+ * them, and the live context in its wire shape (`stepIds` + keyed `stepData`)
376
+ * so the view renders the just-built layout without another round trip.
377
+ */
378
+ export function toExitCommit(
379
+ draft: DraftLayout,
380
+ keyEntries: KeyEntry[],
381
+ stepEntries: PipelineStepRef[],
382
+ title: string | undefined,
383
+ live: PipelineContext,
384
+ ): BuilderExitCommit {
385
+ return {
386
+ layout: draftToLayout(draft),
387
+ keys: entriesToKeys(keyEntries),
388
+ steps: stepEntries.filter((s) => s.step.trim().length > 0),
389
+ title,
390
+ context: {
391
+ keys: live.keys,
392
+ stepIds: Object.keys(live.steps),
393
+ stepData: Object.fromEntries(
394
+ Object.entries(live.steps).map(([id, result]) => [
395
+ id,
396
+ { data: result.data, keys: result.keys, _app: result._app, _dataType: result._dataType },
397
+ ]),
398
+ ),
399
+ errors: live.errors,
400
+ },
401
+ }
402
+ }
403
+
404
+ export interface DraftStatus {
405
+ /**
406
+ * The draft differs from the view as it was before editing: what Discard
407
+ * takes out of the view, so it decides whether Discard asks first.
408
+ */
409
+ changed: boolean
410
+ /**
411
+ * The draft has changes that would be lost: it differs from the view before
412
+ * editing AND from the last saved dashboard (a draft equal to either one
413
+ * loses nothing). Drives the unsaved marker and tells the discard
414
+ * confirmation whether a dashboard keeps the changes.
415
+ */
416
+ unsaved: boolean
417
+ }
418
+
419
+ /** Compares the current {@link builderSnapshot} with the pre-edit one and the last saved one. */
420
+ export function draftStatus(current: string, baseline: string, saved: string | null): DraftStatus {
421
+ const changed = current !== baseline
422
+ return { changed, unsaved: changed && current !== saved }
423
+ }
424
+
425
+ // -------------------------------------------------------------------------- //
426
+ // Leaving — Apply commits, Discard restores (asking first when the view loses changes)
427
+ // -------------------------------------------------------------------------- //
428
+
429
+ /** The builder's leave actions, built by {@link builderLeave}. */
430
+ export interface BuilderLeave {
431
+ /** Apply: hands the commit to `onExit`. Absent without `onExit` (no view to apply to). */
432
+ apply?: () => void
433
+ /** Discard: asks first when the view would lose changes, else leaves at once. */
434
+ discard: () => void
435
+ /** Discard without asking (the confirmation's own action). */
436
+ discardNow: () => void
437
+ }
438
+
439
+ /**
440
+ * The builder's leave actions. Apply hands `commit()` to `onExit`. Discard
441
+ * calls `onExit()` WITHOUT a commit, so the view stays as it was before
442
+ * editing; without `onExit` it calls `onReset()`, which restores the draft in
443
+ * place. Discard asks first (`confirm`) only when the draft `changed` the
444
+ * view: a draft that matches the view before editing loses nothing, even when
445
+ * it differs from a saved dashboard.
446
+ */
447
+ export function builderLeave({
448
+ changed,
449
+ onExit,
450
+ onReset,
451
+ commit,
452
+ confirm,
453
+ }: {
454
+ changed: boolean
455
+ onExit?: (commit?: BuilderExitCommit) => void
456
+ onReset: () => void
457
+ commit: () => BuilderExitCommit
458
+ confirm: () => void
459
+ }): BuilderLeave {
460
+ // Never hands its own arguments on: a click event must not reach `onExit` as a commit.
461
+ const discardNow = () => {
462
+ if (onExit) onExit()
463
+ else onReset()
464
+ }
465
+ return {
466
+ ...(onExit ? { apply: () => onExit(commit()) } : {}),
467
+ discard: () => {
468
+ if (changed) confirm()
469
+ else discardNow()
470
+ },
471
+ discardNow,
472
+ }
473
+ }
474
+
475
+ // -------------------------------------------------------------------------- //
476
+ // Keyboard — tab rename without a double-click
477
+ // -------------------------------------------------------------------------- //
478
+
479
+ /**
480
+ * What a key press on a tab trigger does: F2 renames any tab; Enter renames
481
+ * the tab that is already active (on another tab Enter keeps selecting it,
482
+ * the tabs' own behaviour). `null` leaves the key to the tabs.
483
+ */
484
+ export function tabTriggerKeyAction(key: string, isActive: boolean): "rename" | null {
485
+ if (key === "F2") return "rename"
486
+ if (key === "Enter" && isActive) return "rename"
487
+ return null
488
+ }
489
+
490
+ /** What a key press in a tab's rename field does: Enter commits, Escape cancels. */
491
+ export function renameFieldKeyAction(key: string): "commit" | "cancel" | null {
492
+ if (key === "Enter") return "commit"
493
+ if (key === "Escape") return "cancel"
494
+ return null
495
+ }
496
+
497
+ /**
498
+ * Hands the focus back to a tab once its rename field closes by keyboard.
499
+ * The tab strip holds it, by the tab's INDEX: a rename that changes the label
500
+ * mounts a new tab, so a request held by the tab itself would go with the old
501
+ * one and leave the focus on the document body.
502
+ */
503
+ export interface TabRefocus {
504
+ /** The trigger of tab `tabIdx` takes the focus the next time it attaches. */
505
+ request: (tabIdx: number) => void
506
+ /** The ref callback of tab `tabIdx`'s trigger: focuses it once while a request for it is open. */
507
+ attach: (tabIdx: number, el: { focus: () => void } | null) => void
508
+ /** The two props that wire tab `tabIdx` to this hand-back: its trigger's ref and its request. */
509
+ forTab: (tabIdx: number) => TabRefocusProps
510
+ }
511
+
512
+ /** What one tab gets from {@link TabRefocus.forTab}. */
513
+ export interface TabRefocusProps {
514
+ triggerRef: (el: { focus: () => void } | null) => void
515
+ onRefocus: () => void
516
+ }
517
+
518
+ /** A {@link TabRefocus} with no open request. */
519
+ export function createTabRefocus(): TabRefocus {
520
+ let pending: number | null = null
521
+ const request = (tabIdx: number) => {
522
+ pending = tabIdx
523
+ }
524
+ const attach = (tabIdx: number, el: { focus: () => void } | null) => {
525
+ if (el === null || pending !== tabIdx) return
526
+ pending = null
527
+ el.focus()
528
+ }
529
+ return {
530
+ request,
531
+ attach,
532
+ forTab: (tabIdx) => ({
533
+ triggerRef: (el) => attach(tabIdx, el),
534
+ onRefocus: () => request(tabIdx),
535
+ }),
536
+ }
537
+ }
538
+
539
+ // -------------------------------------------------------------------------- //
540
+ // Views — the data sources live behind "Advanced"
541
+ // -------------------------------------------------------------------------- //
542
+
543
+ export type BuilderView = "layout" | "pipeline" | "preview"
544
+
545
+ /** The builder's views in tab order: the data sources ("pipeline") only in advanced mode. */
546
+ export function builderViews(advanced: boolean): BuilderView[] {
547
+ return advanced ? ["layout", "pipeline", "preview"] : ["layout", "preview"]
548
+ }
549
+
550
+ /** The view to show after advanced mode switches: a view that is now hidden falls back to the layout. */
551
+ export function viewAfterAdvancedToggle(view: BuilderView, advanced: boolean): BuilderView {
552
+ return builderViews(advanced).includes(view) ? view : "layout"
553
+ }