@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,478 @@
1
+ import type { ReactNode } from "react"
2
+ import { renderToStaticMarkup } from "react-dom/server"
3
+ import { QueryClient, useQueryClient } from "@tanstack/react-query"
4
+ import { ToolCancelledError, ToolError } from "mcp-use/react"
5
+ import { beforeEach, describe, expect, it, vi } from "vitest"
6
+ import { useToolQueryKey } from "../hooks/use-tool-query.js"
7
+ import { AppQueryProvider, queryClient } from "../providers/query-provider.js"
8
+ import type {
9
+ ToolResultRecoveryOptions,
10
+ ToolResultRecoveryState,
11
+ } from "../hooks/use-tool-result-recovery.js"
12
+ import { LocaleProvider, useLocale } from "../locale/locale-provider.js"
13
+ import { TOOLKIT_MESSAGES, type ToolkitMessages } from "../locale/toolkit-messages.js"
14
+ import { attribute, decodeEntities, openingTags, userFacingTexts } from "../render-scan.js"
15
+ import { createStandaloneHostBridge, HostBridgeProvider } from "./host-bridge.js"
16
+ import { McpAppView } from "./mcp-app-view.js"
17
+ import type { ViewData } from "./mcp-app-view-chrome.js"
18
+ import { McpUseViewContext } from "./model-context.js"
19
+
20
+ /**
21
+ * McpAppView's wiring, rendered through its real hooks (SSR, the house path):
22
+ * `useToolContext().status` → `useViewLifecycle` → the placeholder, the
23
+ * locale fallback, and the provider order behind the Lucide stroke. The
24
+ * mcp-use view hooks are the only mock; every phase here resolves on the
25
+ * first render, so no effect has to run. Guards toolkit#176 K14: an isError
26
+ * result must never fall back to the endless skeleton.
27
+ */
28
+
29
+ /** What the mocked mcp-use view hooks return on the next render. */
30
+ interface HostState {
31
+ tool: Record<string, unknown>
32
+ locale: string
33
+ hostContext: Record<string, unknown> | undefined
34
+ /** Replaces the recovery's state (it settles in effects, which SSR never runs). */
35
+ recovery: ToolResultRecoveryState<unknown> | null
36
+ }
37
+
38
+ const host: HostState = vi.hoisted(() => ({
39
+ tool: {},
40
+ locale: "en-US",
41
+ hostContext: undefined,
42
+ recovery: null,
43
+ }))
44
+
45
+ // The real hook always runs (hook order stays fixed); a fixture state stands
46
+ // in for what its controller decides once the stripped result arrived.
47
+ vi.mock(import("../hooks/use-tool-result-recovery.js"), async (importOriginal) => {
48
+ const actual = await importOriginal()
49
+ return {
50
+ ...actual,
51
+ useToolResultRecovery: <T,>(options: ToolResultRecoveryOptions) => {
52
+ const state = actual.useToolResultRecovery<T>(options)
53
+ return (host.recovery as ToolResultRecoveryState<T> | null) ?? state
54
+ },
55
+ }
56
+ })
57
+
58
+ // Only the fields McpAppView reads; the casts narrow the fixtures to the hook types.
59
+ vi.mock(import("mcp-use/react"), async (importOriginal) => {
60
+ const actual = await importOriginal()
61
+ const useToolContext = () => host.tool
62
+ const useHostContext = () => ({
63
+ locale: host.locale,
64
+ timeZone: "UTC",
65
+ safeArea: { top: 0, right: 0, bottom: 0, left: 0 },
66
+ hostContext: host.hostContext,
67
+ })
68
+ // The view's model-context node, made visible: a <meta> whose `content`
69
+ // the scans below never read as user-facing text.
70
+ const { createElement } = await import("react")
71
+ const ModelContext = ({ content }: { content: string }) =>
72
+ createElement("meta", { "data-model-context": content })
73
+ return {
74
+ ...actual,
75
+ ModelContext: ModelContext as unknown as typeof actual.ModelContext,
76
+ useToolContext: useToolContext as unknown as typeof actual.useToolContext,
77
+ useHostContext: useHostContext as unknown as typeof actual.useHostContext,
78
+ useDisplayMode: () => ({
79
+ displayMode: "inline",
80
+ availableDisplayModes: ["inline", "fullscreen"],
81
+ requestDisplayMode: () => Promise.resolve(),
82
+ }),
83
+ }
84
+ })
85
+
86
+ // No toast can be raised during SSR, so the provider renders one fixture
87
+ // toast where McpAppView mounts it: the stroke its icons inherit is the
88
+ // stroke a real toast stack gets.
89
+ vi.mock(import("../components/Toast.js"), async (importOriginal) => {
90
+ const actual = await importOriginal()
91
+ const { createElement, Fragment } = await import("react")
92
+ const { createToast } = await import("../lib/toast-model.js")
93
+ const fixture = [createToast(1, { tone: "danger", title: "Fixture toast" })]
94
+ return {
95
+ ...actual,
96
+ ToastProvider: ({ children }: { children: ReactNode }) =>
97
+ createElement(
98
+ Fragment,
99
+ null,
100
+ children,
101
+ createElement(actual.ToastViewport, { toasts: fixture, onDismiss: () => {} }),
102
+ ),
103
+ }
104
+ })
105
+
106
+ const en = TOOLKIT_MESSAGES.en.view
107
+ const de = TOOLKIT_MESSAGES.de.view
108
+ const KNOWN_TOOL = { toolInfo: { tool: { name: "render-view" } } }
109
+ const REASON = "[503 ProcessEngineException] Engine is shutting down"
110
+
111
+ const bridge = createStandaloneHostBridge({ callTool: () => Promise.resolve({}) })
112
+
113
+ /** A consumer widget that prints the locale it reads. */
114
+ function LocaleProbe() {
115
+ return <span data-testid="probe">widget-locale:{useLocale()}</span>
116
+ }
117
+
118
+ const VIEW: ViewData = {
119
+ title: "Process definitions",
120
+ context: {
121
+ keys: {},
122
+ stepIds: ["kpi"],
123
+ stepData: {},
124
+ errors: [{ stepId: "kpi", reason: "timeout" }],
125
+ },
126
+ layout: { rows: [{ row: [{ widget: "demo:probe" }] }] },
127
+ _refreshParams: { layout: { rows: [] }, steps: [{ id: "kpi", step: "demo:kpis" }] },
128
+ }
129
+
130
+ /** Render McpAppView; `view` is the shell, `toasts` the toast stack after it. */
131
+ function render(wrap: (node: ReactNode) => ReactNode = (node) => node) {
132
+ const html = renderToStaticMarkup(
133
+ <HostBridgeProvider bridge={bridge}>
134
+ {wrap(<McpAppView widgets={{ "demo:probe": LocaleProbe }} />)}
135
+ </HostBridgeProvider>,
136
+ )
137
+ const at = html.indexOf('<div data-slot="toast-viewport"')
138
+ expect(at, "the fixture toast stack renders").toBeGreaterThan(0)
139
+ return { html, view: html.slice(0, at), toasts: html.slice(at) }
140
+ }
141
+
142
+ const strokes = (html: string) => openingTags(html, "svg").map((t) => attribute(t, "stroke-width"))
143
+
144
+ const toolError = (text: string) =>
145
+ new ToolError({ isError: true, content: [{ type: "text", text }] })
146
+
147
+ beforeEach(() => {
148
+ host.tool = { status: "pending", toolInput: undefined, toolOutput: undefined }
149
+ host.locale = "en-US"
150
+ host.hostContext = KNOWN_TOOL
151
+ host.recovery = null
152
+ })
153
+
154
+ /** A tool the host names, with its annotations (SEP-1865 `toolInfo.tool`). */
155
+ const toolInfo = (name: string, annotations: Record<string, unknown>) => ({
156
+ toolInfo: { tool: { name, annotations } },
157
+ })
158
+
159
+ /** The recovery's state when a needed recovery did not run. */
160
+ const blockedBy = (reason: "policy" | "no-tool"): ToolResultRecoveryState<unknown> => ({
161
+ data: null,
162
+ failed: false,
163
+ blocked: reason,
164
+ recoveredAt: null,
165
+ args: null,
166
+ })
167
+
168
+ describe("McpAppView lifecycle wiring (toolkit#176)", () => {
169
+ it("an isError rendering result shows the error state with a retry, never the skeleton (K14)", () => {
170
+ host.tool = { status: "error", error: toolError(REASON), toolInput: {} }
171
+ const { view } = render()
172
+ expect(view).not.toContain('data-slot="skeleton"')
173
+ expect(view).not.toContain(en.loading)
174
+ expect(view).toContain('role="alert"')
175
+ expect(decodeEntities(view)).toContain(en.errorTitle)
176
+ expect(view).toContain(en.errorHint)
177
+ expect(view).toContain(`${en.retry}</button>`)
178
+ // The raw reason informs under Details; it is not the message.
179
+ expect(view).toMatch(/<details[^>]*><summary[^>]*>Details<\/summary><pre[^>]*>\[503/)
180
+ })
181
+
182
+ it("without the originating tool the error offers no retry and promises none", () => {
183
+ host.tool = { status: "error", error: toolError(REASON), toolInput: {} }
184
+ host.hostContext = undefined
185
+ const { view } = render()
186
+ expect(view).toContain('role="alert"')
187
+ expect(view).not.toContain("<button")
188
+ expect(view).toContain(en.errorHintNoRetry)
189
+ expect(view).not.toContain(en.errorHint)
190
+ })
191
+
192
+ it("a latched result without a view on such a host is an error, not an endless wait", () => {
193
+ host.tool = { status: "ready", toolOutput: { notAView: true }, toolInput: {} }
194
+ host.hostContext = undefined
195
+ const { view } = render()
196
+ expect(view).not.toContain('data-slot="skeleton"')
197
+ expect(view).toContain('role="alert"')
198
+ expect(view).toContain(en.errorHintNoRetry)
199
+ })
200
+
201
+ it("a stripped result of a writing tool the policy kept: the notice, never a button that writes again", () => {
202
+ // claude.ai strips the result of a "start and show" tool; "auto" never
203
+ // re-runs a tool reported as writing (readOnlyHint: false).
204
+ host.hostContext = toolInfo("start_and_show", { readOnlyHint: false })
205
+ host.recovery = blockedBy("policy")
206
+ const view = decodeEntities(render().view)
207
+ expect(view).not.toContain('data-slot="skeleton"')
208
+ expect(view).toContain('data-tone="info"')
209
+ expect(view).toContain(en.blockedTitle)
210
+ expect(view).toContain(en.blockedHintNoRetry)
211
+ expect(view).not.toContain(en.errorTitle)
212
+ expect(view).not.toContain("<button")
213
+ // The same for destructiveHint: true.
214
+ host.hostContext = toolInfo("start_and_show", { readOnlyHint: true, destructiveHint: true })
215
+ expect(render().view).not.toContain("<button")
216
+ })
217
+
218
+ it("a tool not reported as writing that a stricter policy kept: the notice offers to load the view", () => {
219
+ host.hostContext = toolInfo("show_list", {})
220
+ host.recovery = blockedBy("policy")
221
+ const view = decodeEntities(render().view)
222
+ expect(view).toContain(en.blockedTitle)
223
+ expect(view).toContain(en.blockedHint)
224
+ expect(view).toContain(`${en.blockedRetry}</button>`)
225
+ })
226
+
227
+ it("a recovery blocked for want of a tool name stays the plain error without retry", () => {
228
+ host.hostContext = undefined
229
+ host.recovery = blockedBy("no-tool")
230
+ const view = decodeEntities(render().view)
231
+ expect(view).toContain(en.errorTitle)
232
+ expect(view).toContain(en.errorHintNoRetry)
233
+ expect(view).not.toContain(en.blockedTitle)
234
+ expect(view).not.toContain("<button")
235
+ })
236
+
237
+ it("a failed recovery stays the plain error with its retry", () => {
238
+ host.recovery = { ...blockedBy("policy"), blocked: null, failed: true }
239
+ const view = decodeEntities(render().view)
240
+ expect(view).toContain(en.errorTitle)
241
+ expect(view).toContain(`${en.retry}</button>`)
242
+ })
243
+
244
+ it("a payload the view re-loaded itself carries the stamp under its title, in the host's words (N23)", () => {
245
+ const recoveredAt = Date.UTC(2026, 9, 11, 14, 32)
246
+ host.recovery = { data: VIEW, failed: false, blocked: null, recoveredAt, args: {} }
247
+ const view = decodeEntities(render().view)
248
+ expect(view).toContain(
249
+ `<p data-slot="view-data-as-of" class="text-muted-foreground truncate text-sm">Reloaded at 2:32 PM</p>`,
250
+ )
251
+ host.locale = "de-DE"
252
+ expect(decodeEntities(render().view)).toContain(">Neu geladen um 14:32 Uhr</p>")
253
+ // A host-delivered payload carries none.
254
+ host.recovery = null
255
+ host.tool = { status: "ready", toolOutput: VIEW, toolInput: {} }
256
+ expect(render().view).not.toContain('data-slot="view-data-as-of"')
257
+ })
258
+
259
+ it("the stamp's time speaks the language of its words, not the host's other one", () => {
260
+ const recoveredAt = Date.UTC(2026, 9, 11, 14, 32)
261
+ host.recovery = { data: VIEW, failed: false, blocked: null, recoveredAt, args: {} }
262
+ // A German profile (LocaleProvider) on an en-US host: never "4:32 PM Uhr".
263
+ host.locale = "en-US"
264
+ const german = render((node) => <LocaleProvider locale="de">{node}</LocaleProvider>)
265
+ expect(decodeEntities(german.view)).toContain(">Neu geladen um 14:32 Uhr</p>")
266
+ // The host's region of the same language stays: en-GB keeps its 24-hour clock.
267
+ host.locale = "en-GB"
268
+ const british = render((node) => <LocaleProvider locale="en">{node}</LocaleProvider>)
269
+ expect(decodeEntities(british.view)).toContain(">Reloaded at 14:32</p>")
270
+ })
271
+
272
+ it("a host cancellation shows the cancelled notice, no skeleton and no retry", () => {
273
+ host.tool = { status: "error", error: new ToolCancelledError("user"), toolInput: {} }
274
+ const { view } = render()
275
+ expect(view).toContain(en.cancelled)
276
+ expect(view).not.toContain('role="alert"')
277
+ expect(view).not.toContain('data-slot="skeleton"')
278
+ expect(view).not.toContain("<button")
279
+ })
280
+
281
+ it("an open call waits: skeleton and status line in a stable, focusable frame", () => {
282
+ const { view } = render()
283
+ expect(view).toContain('data-slot="skeleton"')
284
+ expect(view).toMatch(/role="status"[^>]*>.*Loading view…/)
285
+ const frame = openingTags(view, "div")[0] ?? ""
286
+ expect(attribute(frame, "data-slot")).toBe("mcp-app-view")
287
+ expect(attribute(frame, "tabindex")).toBe("-1")
288
+ })
289
+
290
+ it("a complete payload renders the view: toolbar, step alert, widgets", () => {
291
+ host.tool = { status: "ready", toolOutput: VIEW, toolInput: {} }
292
+ const { view } = render()
293
+ expect(view).not.toContain('data-slot="skeleton"')
294
+ expect(view).toContain('data-slot="view-toolbar"')
295
+ expect(view).toContain(`aria-label="${en.refresh}"`)
296
+ expect(decodeEntities(view)).toContain(en.stepErrorsTitle)
297
+ expect(view).toContain("widget-locale:")
298
+ // The frame stays the same element across placeholder and view.
299
+ expect(attribute(openingTags(view, "div")[0] ?? "", "data-slot")).toBe("mcp-app-view")
300
+ })
301
+ })
302
+
303
+ describe("McpAppView toolNameInQueryKey (toolkit#176 K18)", () => {
304
+ /** A widget that prints the cache key its data hooks would use. */
305
+ function KeyProbe() {
306
+ return <output>{JSON.stringify(useToolQueryKey(["feed"], "feed_data", { id: 1 }))}</output>
307
+ }
308
+ const keyIn = (props: { toolNameInQueryKey?: boolean }) =>
309
+ decodeEntities(
310
+ renderToStaticMarkup(
311
+ <HostBridgeProvider bridge={bridge}>
312
+ <McpAppView widgets={{ "demo:probe": KeyProbe }} {...props} />
313
+ </HostBridgeProvider>,
314
+ ),
315
+ ).match(/<output>(.*?)<\/output>/)?.[1]
316
+
317
+ it("hands the setting to the widgets' data hooks; omitted, the 2.x key stays", () => {
318
+ host.tool = { status: "ready", toolOutput: VIEW, toolInput: {} }
319
+ expect(keyIn({ toolNameInQueryKey: true })).toBe('["feed","feed_data",{"id":1}]')
320
+ expect(keyIn({})).toBe('["feed",{"id":1}]')
321
+ })
322
+ })
323
+
324
+ describe("McpAppView query client", () => {
325
+ /** A widget that tells which client its data hooks use. */
326
+ function clientIn(wrap: (node: ReactNode) => ReactNode) {
327
+ const clients: QueryClient[] = []
328
+ function ClientProbe() {
329
+ clients.push(useQueryClient())
330
+ return null
331
+ }
332
+ renderToStaticMarkup(
333
+ <HostBridgeProvider bridge={bridge}>
334
+ {wrap(<McpAppView widgets={{ "demo:probe": ClientProbe }} />)}
335
+ </HostBridgeProvider>,
336
+ )
337
+ expect(clients.length, "the widget rendered").toBeGreaterThan(0)
338
+ return clients[0]
339
+ }
340
+
341
+ it("its widgets fetch through an enclosing AppQueryProvider's client", () => {
342
+ host.tool = { status: "ready", toolOutput: VIEW, toolInput: {} }
343
+ const perMount = new QueryClient()
344
+ expect(clientIn((node) => <AppQueryProvider client={perMount}>{node}</AppQueryProvider>)).toBe(
345
+ perMount,
346
+ )
347
+ expect(
348
+ clientIn((node) => node),
349
+ "else the toolkit's shared client",
350
+ ).toBe(queryClient)
351
+ })
352
+ })
353
+
354
+ describe("McpAppView model context (toolkit#176 K20)", () => {
355
+ /** Inside an mcp-use view, where the view's line joins the native tree. */
356
+ const inView = (node: ReactNode) => (
357
+ <McpUseViewContext.Provider value={true}>{node}</McpUseViewContext.Provider>
358
+ )
359
+ const contexts = (html: string) =>
360
+ openingTags(html, "meta").map((tag) =>
361
+ decodeEntities(attribute(tag, "data-model-context") ?? ""),
362
+ )
363
+
364
+ it("an error the user sees instead of the view reaches the model: what failed, and that it is an error", () => {
365
+ host.tool = { status: "error", error: toolError(REASON), toolInput: {} }
366
+ const { view } = render(inView)
367
+ expect(contexts(view)).toEqual([
368
+ `The view of render-view could not be shown (reason: ${REASON}). ` +
369
+ "The user sees an error message with a retry button instead of the view.",
370
+ ])
371
+ })
372
+
373
+ it("a blocked recovery tells the model the tool ran and must not be re-run to show the view", () => {
374
+ host.hostContext = toolInfo("start_and_show", { readOnlyHint: false })
375
+ host.recovery = blockedBy("policy")
376
+ expect(contexts(render(inView).view)).toEqual([
377
+ "The view of start_and_show is not shown: start_and_show ran, but the host did not pass its " +
378
+ "result to the view, and the view did not re-run it. The user sees a note to ask in the chat " +
379
+ "for the current state instead of the view. Do not re-run start_and_show just to show the view.",
380
+ ])
381
+ })
382
+
383
+ it("a cancelled call says so", () => {
384
+ host.tool = { status: "error", error: new ToolCancelledError("user"), toolInput: {} }
385
+ expect(contexts(render(inView).view)).toEqual([
386
+ "The view of render-view was cancelled before it rendered. The user sees a note that the call was cancelled.",
387
+ ])
388
+ })
389
+
390
+ it("failed data steps of a shown view are reported; a waiting view reports nothing", () => {
391
+ host.tool = { status: "ready", toolOutput: VIEW, toolInput: {} }
392
+ expect(contexts(render(inView).view)).toEqual([
393
+ "The view of render-view is shown, but some of its data could not be loaded (kpi: timeout). " +
394
+ "The user sees an alert with a retry button.",
395
+ ])
396
+ host.tool = { status: "pending", toolInput: undefined, toolOutput: undefined }
397
+ expect(contexts(render(inView).view)).toEqual([])
398
+ })
399
+ })
400
+
401
+ describe("McpAppView locale", () => {
402
+ it("speaks the host's language in its own chrome", () => {
403
+ host.locale = "de-DE"
404
+ host.tool = { status: "error", error: toolError(REASON), toolInput: {} }
405
+ const { view, toasts } = render()
406
+ expect(decodeEntities(view)).toContain(de.errorTitle)
407
+ expect(view).toContain(`${de.retry}</button>`)
408
+ // The toast stack below the view speaks it too (its close buttons).
409
+ expect(toasts).toContain(`aria-label="${TOOLKIT_MESSAGES.de.common.dismiss}"`)
410
+ })
411
+
412
+ it("leaves useLocale() of consumer widgets alone (no LocaleProvider of its own)", () => {
413
+ host.locale = "de-DE"
414
+ host.tool = { status: "ready", toolOutput: VIEW, toolInput: {} }
415
+ const { view } = render()
416
+ expect(view).toContain(`aria-label="${de.refresh}"`)
417
+ expect(view).toContain("widget-locale:en<")
418
+ })
419
+
420
+ it("follows a consumer LocaleProvider over the host's locale", () => {
421
+ host.locale = "de-DE"
422
+ host.tool = { status: "ready", toolOutput: VIEW, toolInput: {} }
423
+ const { view } = render((node) => <LocaleProvider locale="en">{node}</LocaleProvider>)
424
+ expect(view).toContain(`aria-label="${en.refresh}"`)
425
+ expect(view).toContain("widget-locale:en<")
426
+ })
427
+
428
+ it("renders no English and no raw reason as message text under a German host", () => {
429
+ host.locale = "de-DE"
430
+ host.tool = { status: "ready", toolOutput: VIEW, toolInput: {} }
431
+ const catalog = catalogStrings(TOOLKIT_MESSAGES.de)
432
+ const fixtures = [
433
+ VIEW.title ?? "",
434
+ "demo:kpis",
435
+ "kpi",
436
+ "timeout",
437
+ "Fixture toast",
438
+ "widget-locale:en",
439
+ ]
440
+ const { html } = render()
441
+ const leaks = userFacingTexts(html)
442
+ .map(({ source, value }) => {
443
+ let rest = value
444
+ for (const known of [...catalog, ...fixtures]) rest = rest.split(known).join(" ")
445
+ return { source, value, rest: rest.trim() }
446
+ })
447
+ .filter(({ rest }) => /\p{L}/u.test(rest))
448
+ expect(leaks).toEqual([])
449
+ })
450
+ })
451
+
452
+ describe("McpAppView icon stroke (CI §11)", () => {
453
+ it("draws the view's icons and its toast stack at 2.5", () => {
454
+ host.tool = { status: "error", error: toolError(REASON), toolInput: {} }
455
+ const { view, toasts } = render()
456
+ expect(strokes(view).length).toBeGreaterThan(0)
457
+ expect(new Set(strokes(view))).toEqual(new Set(["2.5"]))
458
+ expect(strokes(toasts).length).toBeGreaterThan(0)
459
+ expect(new Set(strokes(toasts))).toEqual(new Set(["2.5"]))
460
+ })
461
+
462
+ it("narrows the dense toolbar to 2", () => {
463
+ host.tool = { status: "ready", toolOutput: VIEW, toolInput: {} }
464
+ const { view } = render()
465
+ const [toolbar] = view.split('data-slot="view-toolbar"').slice(1)
466
+ const bar = (toolbar ?? "").split("</div></div>")[0] ?? ""
467
+ expect(strokes(bar).length).toBeGreaterThan(0)
468
+ expect(new Set(strokes(bar))).toEqual(new Set(["2"]))
469
+ })
470
+ })
471
+
472
+ /** Every string value of a catalog (parametrised messages are skipped), longest first. */
473
+ function catalogStrings(messages: ToolkitMessages): string[] {
474
+ return Object.values(messages)
475
+ .flatMap((section) => Object.values(section as Record<string, unknown>))
476
+ .filter((v): v is string => typeof v === "string")
477
+ .sort((a, b) => b.length - a.length)
478
+ }