@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
@@ -1,25 +1,498 @@
1
+ import type { ReactNode } from "react"
1
2
  import { renderToStaticMarkup } from "react-dom/server"
2
3
  import { describe, expect, it } from "vitest"
3
- import { DEFAULT_LABELS, ViewPlaceholder } from "./mcp-app-view-chrome.js"
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 {
8
+ applyBuilderCommit,
9
+ DEFAULT_LABELS,
10
+ fullscreenAvailable,
11
+ StepErrors,
12
+ toolbarControls,
13
+ ViewToolbar,
14
+ type ViewData,
15
+ type ViewToolbarProps,
16
+ } from "./mcp-app-view-chrome.js"
17
+ import { ViewPlaceholder, type DescribeViewError } from "./mcp-app-view-states.js"
18
+ import { VIEW_FOCUS_TARGET } from "./use-build-mode-focus.js"
19
+ import { resolveViewLifecycle, RETRY_IDLE, type ViewLifecycleInput } from "./view-lifecycle.js"
4
20
 
5
- describe("ViewPlaceholder", () => {
6
- it("shows the loading skeleton while the rendering call is still open", () => {
7
- const html = renderToStaticMarkup(<ViewPlaceholder cancelled={false} labels={DEFAULT_LABELS} />)
21
+ const en = TOOLKIT_MESSAGES.en.view
22
+ const de = TOOLKIT_MESSAGES.de.view
23
+ const noop = (): void => {}
24
+ const asyncNoop = (): Promise<void> => Promise.resolve()
25
+
26
+ const placeholder = (lifecycle: Parameters<typeof ViewPlaceholder>[0]["lifecycle"], labels = en) =>
27
+ renderToStaticMarkup(<ViewPlaceholder lifecycle={lifecycle} labels={labels} onRetry={noop} />)
28
+
29
+ describe("ViewPlaceholder — one rendering per lifecycle phase (toolkit#176)", () => {
30
+ it("waiting: skeleton plus a status line, no error, no retry", () => {
31
+ const html = placeholder({ phase: "waiting", slow: false })
8
32
  expect(html).toContain('data-slot="skeleton"')
9
- expect(html).toContain(DEFAULT_LABELS.loading)
10
- expect(html).not.toContain(DEFAULT_LABELS.cancelled)
33
+ expect(html).toMatch(/role="status"[^>]*>.*Loading view…/)
34
+ expect(html).not.toContain(en.slowHint)
35
+ expect(html).not.toContain('role="alert"')
36
+ expect(html).not.toContain("<button")
37
+ })
38
+
39
+ it("waiting: the live region sits in no aria-busy subtree (AT may hold back the slow hint)", () => {
40
+ expect(placeholder({ phase: "waiting", slow: true })).not.toMatch(/aria-busy="true"/)
41
+ })
42
+
43
+ it("waiting too long: the slow hint joins the status line", () => {
44
+ const html = placeholder({ phase: "waiting", slow: true })
45
+ expect(html).toContain(en.slowHint)
46
+ expect(html).toContain('data-slot="slow-hint"')
47
+ expect(html).toContain('data-slot="skeleton"')
48
+ })
49
+
50
+ it("error: icon, title, what to do, the raw reason under Details, a retry — and NO skeleton (K14)", () => {
51
+ // The exact input McpAppView feeds for an isError rendering result.
52
+ const lifecycle = resolveViewLifecycle({
53
+ hasViewData: false,
54
+ callStatus: "error",
55
+ cancelled: false,
56
+ callErrorReason: "[503 ProcessEngineException] Engine is shutting down",
57
+ recoveryFailed: false,
58
+ retryState: RETRY_IDLE,
59
+ slow: true,
60
+ canRetry: true,
61
+ } satisfies ViewLifecycleInput)
62
+ const html = placeholder(lifecycle)
63
+ expect(html).not.toContain('data-slot="skeleton"')
64
+ expect(html).not.toContain(en.loading)
65
+ expect(html).toContain('role="alert"')
66
+ expect(html).toContain("<svg")
67
+ expect(decodeEntities(html)).toContain(en.errorTitle)
68
+ expect(html).toContain(en.errorHint)
69
+ expect(html).toMatch(/<summary[^>]*>Details<\/summary>/)
70
+ expect(html).toContain("[503 ProcessEngineException] Engine is shutting down")
71
+ expect(html).toContain(`${en.retry}</button>`)
72
+ })
73
+
74
+ it("error without a known originating tool: no retry button, and a hint that promises none", () => {
75
+ const html = placeholder({ phase: "error", reason: undefined, canRetry: false })
76
+ expect(decodeEntities(html)).toContain(en.errorTitle)
77
+ expect(html).toContain(en.errorHintNoRetry)
78
+ expect(html).not.toContain(en.errorHint)
79
+ expect(html).not.toContain("<button")
80
+ expect(html).not.toContain("<details")
81
+ const de = placeholder(
82
+ { phase: "error", reason: undefined, canRetry: false },
83
+ TOOLKIT_MESSAGES.de.view,
84
+ )
85
+ expect(de).toContain(TOOLKIT_MESSAGES.de.view.errorHintNoRetry)
86
+ expect(de).not.toContain(TOOLKIT_MESSAGES.de.view.errorHint)
87
+ })
88
+
89
+ it("error copy maps through describeError while the raw reason stays under Details", () => {
90
+ const html = renderToStaticMarkup(
91
+ <ViewPlaceholder
92
+ lifecycle={{ phase: "error", reason: "HTTP 503", canRetry: true }}
93
+ labels={en}
94
+ onRetry={noop}
95
+ describeError={(reason) =>
96
+ reason?.includes("503") ? { title: "The engine is down" } : undefined
97
+ }
98
+ />,
99
+ )
100
+ expect(html).toContain("The engine is down")
101
+ expect(html).not.toContain(en.errorTitle)
102
+ expect(html).toContain(en.errorHint)
103
+ expect(html).toContain("HTTP 503")
104
+ })
105
+
106
+ it("blocked, writing tool: a notice that the action ran, no button that would run it again (CI §9)", () => {
107
+ // The exact input McpAppView feeds when the policy keeps a stripped
108
+ // result of a tool reported as writing from being re-run.
109
+ const lifecycle = resolveViewLifecycle({
110
+ hasViewData: false,
111
+ callStatus: "pending",
112
+ cancelled: false,
113
+ callErrorReason: undefined,
114
+ recoveryFailed: false,
115
+ recoveryBlocked: true,
116
+ toolWrites: true,
117
+ retryState: RETRY_IDLE,
118
+ slow: false,
119
+ canRetry: true,
120
+ } satisfies ViewLifecycleInput)
121
+ expect(lifecycle).toEqual({
122
+ phase: "error",
123
+ reason: undefined,
124
+ canRetry: false,
125
+ kind: "blocked",
126
+ })
127
+ const html = decodeEntities(placeholder(lifecycle))
128
+ // Nothing failed: the info tone and a polite status, not a danger alert.
129
+ expect(html).toContain('data-tone="info"')
130
+ expect(html).toContain('role="status"')
131
+ expect(html).not.toContain('role="alert"')
132
+ expect(html).toContain("lucide-info")
133
+ expect(html).toContain(en.blockedTitle)
134
+ expect(html).toContain(en.blockedHintNoRetry)
135
+ expect(html).not.toContain(en.errorTitle)
136
+ expect(html).not.toContain(en.errorHint)
137
+ expect(html).not.toContain("<button")
138
+ expect(html).not.toContain("<details")
139
+ const german = decodeEntities(placeholder(lifecycle, de))
140
+ expect(german).toContain(de.blockedTitle)
141
+ expect(german).toContain(de.blockedHintNoRetry)
142
+ expect(german).not.toContain("<button")
143
+ })
144
+
145
+ it("blocked, tool not reported as writing: the notice offers to load the view, in its own words", () => {
146
+ const lifecycle = {
147
+ phase: "error",
148
+ reason: undefined,
149
+ canRetry: true,
150
+ kind: "blocked",
151
+ } as const
152
+ const html = decodeEntities(placeholder(lifecycle))
153
+ expect(html).toContain(en.blockedTitle)
154
+ expect(html).toContain(en.blockedHint)
155
+ expect(html).not.toContain(en.blockedHintNoRetry)
156
+ expect(html).toContain(`${en.blockedRetry}</button>`)
157
+ expect(html).not.toContain(`${en.retry}</button>`)
158
+ expect(decodeEntities(placeholder(lifecycle, de))).toContain(`${de.blockedRetry}</button>`)
159
+ })
160
+
161
+ it("describeError tells a failure from a block, and can word either", () => {
162
+ const seen: unknown[] = []
163
+ const describeError: DescribeViewError = (reason, info) => {
164
+ seen.push([reason, info])
165
+ return info?.kind === "blocked" ? { description: "Ask the operator." } : undefined
166
+ }
167
+ const render = (lifecycle: Parameters<typeof ViewPlaceholder>[0]["lifecycle"]) =>
168
+ decodeEntities(
169
+ renderToStaticMarkup(
170
+ <ViewPlaceholder
171
+ lifecycle={lifecycle}
172
+ labels={en}
173
+ onRetry={noop}
174
+ describeError={describeError}
175
+ />,
176
+ ),
177
+ )
178
+ const blocked = render({ phase: "error", reason: undefined, canRetry: false, kind: "blocked" })
179
+ expect(blocked).toContain(en.blockedTitle)
180
+ expect(blocked).toContain("Ask the operator.")
181
+ expect(blocked).not.toContain(en.blockedHintNoRetry)
182
+ const failed = render({ phase: "error", reason: "HTTP 503", canRetry: true })
183
+ expect(failed).toContain(en.errorHint)
184
+ expect(seen).toEqual([
185
+ [undefined, { kind: "blocked" }],
186
+ ["HTTP 503", { kind: "failed" }],
187
+ ])
11
188
  })
12
189
 
13
- it("replaces the skeleton with the cancellation notice once the host cancelled the call", () => {
14
- const html = renderToStaticMarkup(<ViewPlaceholder cancelled labels={DEFAULT_LABELS} />)
15
- expect(html).toContain(DEFAULT_LABELS.cancelled)
190
+ it("cancelled: the cancellation notice, no skeleton, no retry", () => {
191
+ const html = placeholder({ phase: "cancelled" })
192
+ expect(html).toContain(en.cancelled)
193
+ expect(html).toContain('role="status"')
16
194
  expect(html).not.toContain('data-slot="skeleton"')
17
- expect(html).not.toContain(DEFAULT_LABELS.loading)
195
+ expect(html).not.toContain("<button")
196
+ })
197
+
198
+ it("result: renders nothing (the view takes over)", () => {
199
+ expect(placeholder({ phase: "result" })).toBe("")
200
+ })
201
+
202
+ it("renders the German catalog and consumer overrides", () => {
203
+ expect(placeholder({ phase: "waiting", slow: true }, de)).toContain(de.slowHint)
204
+ expect(placeholder({ phase: "cancelled" }, { ...de, cancelled: "Abgebrochen." })).toContain(
205
+ "Abgebrochen.",
206
+ )
207
+ })
208
+
209
+ it("keeps the deprecated English DEFAULT_LABELS alias", () => {
210
+ expect(DEFAULT_LABELS).toBe(en)
211
+ })
212
+ })
213
+
214
+ const view: ViewData = {
215
+ title: "Process definitions",
216
+ context: { keys: {}, stepIds: [], stepData: {}, errors: [] },
217
+ layout: { rows: [] },
218
+ _refreshParams: { layout: { rows: [] } },
219
+ }
220
+
221
+ function toolbar(over: Partial<ViewToolbarProps> = {}, wrap = (n: ReactNode) => n) {
222
+ return renderToStaticMarkup(
223
+ wrap(
224
+ <ViewToolbar
225
+ hidden={false}
226
+ viewData={view}
227
+ builderAvailable={false}
228
+ isFullscreen={false}
229
+ canFullscreen
230
+ isRefreshing={false}
231
+ labels={en}
232
+ titleMode="show"
233
+ titleLevel={2}
234
+ onRefresh={asyncNoop}
235
+ onEnterBuildMode={noop}
236
+ onToggleFullscreen={asyncNoop}
237
+ {...over}
238
+ />,
239
+ ),
240
+ )
241
+ }
242
+
243
+ /**
244
+ * CI §8 chrome button (Icon-Button, Chrome): surface, `--cd-linie` border,
245
+ * shadow-1, blue on hover. The outline variant carries the surface, border and
246
+ * hover; the shadow is the chrome's own and must render.
247
+ */
248
+ function expectChromeButton(tag: string) {
249
+ expect(attribute(tag, "data-variant")).toBe("outline")
250
+ const classes = (attribute(tag, "class") ?? "").split(/\s+/)
251
+ expect(classes).toEqual(
252
+ expect.arrayContaining(["shadow-1", "border", "border-border", "bg-background"]),
253
+ )
254
+ expect(classes).toContain("hover:border-focus")
255
+ }
256
+
257
+ describe("ViewToolbar", () => {
258
+ it("renders Refresh and Fullscreen as 32 px CI chrome icon buttons named by aria-label", () => {
259
+ const html = toolbar()
260
+ const buttons = openingTags(html, "button")
261
+ expect(buttons.map((b) => attribute(b, "aria-label"))).toEqual([en.refresh, en.enterFullscreen])
262
+ for (const b of buttons) {
263
+ expect(attribute(b, "data-size")).toBe("icon-sm")
264
+ expect(attribute(b, "type")).toBe("button")
265
+ expectChromeButton(b)
266
+ }
267
+ // Icons are decorative (the name is on the button) and use the dense stroke.
268
+ for (const svg of openingTags(html, "svg")) {
269
+ expect(attribute(svg, "aria-hidden")).toBe("true")
270
+ expect(attribute(svg, "stroke-width")).toBe("2")
271
+ }
272
+ expect(html).toMatch(/data-slot="view-toolbar"[^>]*px-4/)
273
+ })
274
+
275
+ it("gates Fullscreen on the host's offer (toolkit#178 K22)", () => {
276
+ expect(toolbar({ canFullscreen: false })).not.toContain(en.enterFullscreen)
277
+ const exit = toolbar({ isFullscreen: true })
278
+ expect(exit).toContain(`aria-label="${en.exitFullscreen}"`)
279
+ expect(exit).toContain("lucide-minimize-2")
280
+ })
281
+
282
+ it("names the refresh button for its running state and disables it", () => {
283
+ const html = toolbar({ isRefreshing: true })
284
+ const refresh = openingTags(html, "button")[0] ?? ""
285
+ expect(attribute(refresh, "aria-label")).toBe(en.refreshing)
286
+ expect(refresh).toMatch(/\sdisabled=""/)
287
+ expect(html).toContain("motion-safe:animate-spin")
288
+ })
289
+
290
+ it("shows the edit button with its label and a decorative pencil", () => {
291
+ const html = toolbar({ builderAvailable: true })
292
+ expect(html).toContain(`${en.build}</button>`)
293
+ expect(html).toContain("lucide-pencil")
294
+ const edit = openingTags(html, "button")[0] ?? ""
295
+ expectChromeButton(edit)
296
+ // The focus target the builder hands the focus back to when it closes.
297
+ expect(VIEW_FOCUS_TARGET).toBe(`[data-slot="${attribute(edit, "data-slot")}"]`)
298
+ })
299
+
300
+ it("titles the view once: show (configurable level), hide, or slot", () => {
301
+ expect(toolbar()).toMatch(/<h2[^>]*>Process definitions<\/h2>/)
302
+ expect(toolbar({ titleLevel: 3 })).toMatch(/<h3[^>]*>Process definitions<\/h3>/)
303
+ expect(toolbar({ titleMode: "hide" })).not.toMatch(/<h\d/)
304
+ const slot = toolbar({
305
+ titleMode: "slot",
306
+ renderTitle: (t) => <span data-testid="slot">{`${t} (slot)`}</span>,
307
+ })
308
+ expect(slot).toContain("Process definitions (slot)")
309
+ expect(slot).not.toMatch(/<h\d/)
310
+ })
311
+
312
+ it("renders nothing in build mode or when it would be an empty bar", () => {
313
+ expect(toolbar({ hidden: true })).toBe("")
314
+ expect(
315
+ toolbar({
316
+ canFullscreen: false,
317
+ titleMode: "hide",
318
+ viewData: { ...view, _refreshParams: undefined },
319
+ }),
320
+ ).toBe("")
321
+ })
322
+
323
+ it("follows the German catalog", () => {
324
+ const html = toolbar({ labels: de })
325
+ expect(html).toContain(`aria-label="${de.refresh}"`)
326
+ expect(html).toContain(`aria-label="${de.enterFullscreen}"`)
327
+ })
328
+
329
+ it("stamps the time of data the view re-loaded itself, in words that carry its meaning (toolkit#176 N23)", () => {
330
+ expect(toolbar()).not.toContain('data-slot="view-data-as-of"')
331
+ const html = toolbar({ dataAsOf: "2:32 PM" })
332
+ const stamp = openingTags(html, "p").find(
333
+ (t) => attribute(t, "data-slot") === "view-data-as-of",
334
+ )
335
+ expect(stamp).toBeDefined()
336
+ expect(attribute(stamp ?? "", "class")).toContain("text-muted-foreground")
337
+ // Visible text only: no hover-only tooltip, no screen-reader-only extra
338
+ // (touch and keyboard users read exactly what everyone reads, once).
339
+ expect(attribute(stamp ?? "", "title")).toBeUndefined()
340
+ expect(html).not.toContain("sr-only")
341
+ expect(html).toContain(`>${en.dataAsOf("2:32 PM")}</p>`)
342
+ expect(en.dataAsOf("2:32 PM")).toBe("Reloaded at 2:32 PM")
343
+ expect(toolbar({ dataAsOf: "14:32", labels: de })).toContain(`>${de.dataAsOf("14:32")}</p>`)
344
+ })
345
+
346
+ it("puts the stamp under the title, never beside the controls, and the controls never shrink", () => {
347
+ const html = toolbar({ dataAsOf: "14:32", builderAvailable: true })
348
+ const bar = openingTags(html, "div")
349
+ // [toolbar, heading column, controls]: the stamp is the column's second line.
350
+ expect(bar.map((t) => attribute(t, "data-slot") ?? "")).toEqual([
351
+ "view-toolbar",
352
+ "view-heading",
353
+ "",
354
+ ])
355
+ expect(attribute(bar[1] ?? "", "class")).toBe("min-w-0 flex-1")
356
+ expect(html).toMatch(
357
+ /data-slot="view-heading"[^>]*><h2[^>]*>Process definitions<\/h2><p data-slot="view-data-as-of"/,
358
+ )
359
+ expect((attribute(bar[2] ?? "", "class") ?? "").split(" ")).toEqual(
360
+ expect.arrayContaining(["ml-auto", "shrink-0"]),
361
+ )
362
+ expect(attribute(bar[2] ?? "", "class")).not.toContain("min-w-0")
363
+ // Without a stamp the title sits straight in the row, as before.
364
+ const plain = toolbar({ builderAvailable: true })
365
+ expect(plain).not.toContain('data-slot="view-heading"')
366
+ expect(plain).toMatch(/data-slot="view-toolbar"[^>]*><h2/)
367
+ // The slot title takes the stamp under it as well.
368
+ const slot = toolbar({ titleMode: "slot", renderTitle: (t) => t, dataAsOf: "14:32" })
369
+ expect(slot).toMatch(/data-slot="view-heading"[^>]*><div[^>]*>Process definitions<\/div><p/)
370
+ })
371
+
372
+ it("keeps the bar for the stamp alone", () => {
373
+ const bare = {
374
+ canFullscreen: false,
375
+ titleMode: "hide" as const,
376
+ viewData: { ...view, _refreshParams: undefined },
377
+ }
378
+ expect(toolbar(bare)).toBe("")
379
+ expect(toolbar({ ...bare, dataAsOf: "14:32" })).toContain('data-slot="view-data-as-of"')
380
+ })
381
+ })
382
+
383
+ describe("toolbarControls / fullscreenAvailable", () => {
384
+ it("derives each control from the payload and the host", () => {
385
+ expect(
386
+ toolbarControls({
387
+ viewData: view,
388
+ builderAvailable: true,
389
+ canFullscreen: true,
390
+ titleMode: "show",
391
+ }),
392
+ ).toEqual({ title: true, refresh: true, edit: true, fullscreen: true, any: true })
393
+ expect(
394
+ toolbarControls({
395
+ viewData: { title: undefined, layout: undefined, _refreshParams: undefined },
396
+ builderAvailable: true,
397
+ canFullscreen: false,
398
+ titleMode: "show",
399
+ }),
400
+ ).toEqual({ title: false, refresh: false, edit: false, fullscreen: false, any: false })
401
+ expect(
402
+ toolbarControls({
403
+ viewData: { title: undefined, layout: { rows: [] }, _refreshParams: undefined },
404
+ builderAvailable: false,
405
+ canFullscreen: false,
406
+ titleMode: "slot",
407
+ }),
408
+ ).toMatchObject({ title: true, refresh: false, any: true })
409
+ })
410
+
411
+ it("offers fullscreen only when the host negotiates it, keeping the exit while in it", () => {
412
+ expect(fullscreenAvailable("inline", ["inline", "fullscreen"])).toBe(true)
413
+ expect(fullscreenAvailable("inline", ["inline"])).toBe(false)
414
+ expect(fullscreenAvailable("inline", [])).toBe(false)
415
+ expect(fullscreenAvailable("fullscreen", ["inline"])).toBe(true)
416
+ expect(fullscreenAvailable("pip", ["inline", "pip"])).toBe(false)
417
+ })
418
+ })
419
+
420
+ describe("StepErrors — an alert with step titles and a retry", () => {
421
+ const errors = [
422
+ { stepId: "inc", reason: "[503] Engine is shutting down" },
423
+ { stepId: "kpi", reason: "timeout" },
424
+ ]
425
+ const steps = [
426
+ { id: "inc", step: "camunda7:incidents" },
427
+ { id: "kpi", step: "camunda7:kpis" },
428
+ ]
429
+
430
+ it("lists each failed step by its human title, else its step id, plus what to do", () => {
431
+ const html = renderToStaticMarkup(
432
+ <LocaleProvider locale="de">
433
+ <StepErrors
434
+ hidden={false}
435
+ errors={errors}
436
+ steps={steps}
437
+ labels={de}
438
+ describeStep={({ step }) => (step === "camunda7:incidents" ? "Incidents" : undefined)}
439
+ onRetry={noop}
440
+ />
441
+ </LocaleProvider>,
442
+ )
443
+ expect(html).toContain('role="alert"')
444
+ expect(html).toContain(de.stepErrorsTitle)
445
+ expect(html).toMatch(/font-medium">Incidents<\/span>/)
446
+ expect(html).toMatch(/font-mono[^"]*">camunda7:kpis<\/span>/)
447
+ expect(html).not.toContain(">inc<")
448
+ expect(html).toContain(de.stepErrorsHint)
449
+ expect(html).toContain(`${de.retry}</button>`)
450
+ })
451
+
452
+ it("keeps the raw step reasons under Details, never in the alert text", () => {
453
+ const html = renderToStaticMarkup(
454
+ <StepErrors hidden={false} errors={errors} steps={steps} labels={en} onRetry={noop} />,
455
+ )
456
+ const [body, details = ""] = html.split("<details")
457
+ expect(body).not.toContain("Engine is shutting down")
458
+ expect(body).not.toContain("timeout")
459
+ expect(details).toMatch(/<summary[^>]*>Details<\/summary>/)
460
+ expect(details).toContain("camunda7:incidents: [503] Engine is shutting down")
461
+ expect(details).toContain("camunda7:kpis: timeout")
462
+ })
463
+
464
+ it("falls back to the step ref id and, without a refresh, offers no retry nor promises one", () => {
465
+ const html = renderToStaticMarkup(
466
+ <StepErrors hidden={false} errors={[{ stepId: "x", reason: "boom" }]} labels={en} />,
467
+ )
468
+ expect(html).toMatch(/font-mono[^"]*">x<\/span>/)
469
+ expect(html).not.toContain("<button")
470
+ expect(html).toContain(en.errorHintNoRetry)
471
+ expect(html).not.toContain(en.stepErrorsHint)
472
+ })
473
+
474
+ it("renders nothing without errors or in build mode", () => {
475
+ expect(renderToStaticMarkup(<StepErrors hidden={false} errors={[]} labels={en} />)).toBe("")
476
+ expect(renderToStaticMarkup(<StepErrors hidden errors={errors} labels={en} />)).toBe("")
477
+ })
478
+ })
479
+
480
+ describe("applyBuilderCommit", () => {
481
+ it("commits layout, title, context and the refresh params of the builder draft", () => {
482
+ const layout = { rows: [{ row: [{ widget: "a:b" }] }] }
483
+ const steps = [{ id: "s", step: "a:step" }]
484
+ const next = applyBuilderCommit(view, { layout, keys: { k: 1 }, steps, title: "New" })
485
+ expect(next.layout).toBe(layout)
486
+ expect(next.title).toBe("New")
487
+ expect(next.context).toBe(view.context)
488
+ expect(next._refreshParams).toEqual({ keys: { k: 1 }, steps, layout, title: "New" })
18
489
  })
19
490
 
20
- it("renders the consumer's label override", () => {
21
- const labels = { ...DEFAULT_LABELS, cancelled: "Abgebrochen." }
22
- const html = renderToStaticMarkup(<ViewPlaceholder cancelled labels={labels} />)
23
- expect(html).toContain("Abgebrochen.")
491
+ it("keeps the previous title and takes a new context when given", () => {
492
+ const context = { keys: { x: 1 }, stepIds: [], stepData: {}, errors: [] }
493
+ const next = applyBuilderCommit(view, { layout: { rows: [] }, context })
494
+ expect(next.title).toBe("Process definitions")
495
+ expect(next._refreshParams?.title).toBe("Process definitions")
496
+ expect(next.context).toBe(context)
24
497
  })
25
498
  })