@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,211 @@
1
+ /**
2
+ * A passive observer of the MCP Apps (SEP-1865) host → view lifecycle
3
+ * notifications, read straight off the postMessage channel the view runtime
4
+ * itself listens on (toolkit#176 K13, N21).
5
+ *
6
+ * mcp-use 2.x latches only a result WITH `structuredContent` (or an
7
+ * `isError`); a content-only result, which claude.ai and Claude Desktop send,
8
+ * leaves its status `"pending"` forever, and its tool input merges partial and
9
+ * complete arguments into one field. Watching the notifications themselves
10
+ * tells the three cases apart that the latch cannot:
11
+ *
12
+ * - the host delivered a STRIPPED result (re-run the tool now, with the
13
+ * complete arguments it was called with),
14
+ * - the host delivered a structured result (the latch follows; never re-run),
15
+ * - nothing arrived yet (a slow tool on a compliant host: keep waiting).
16
+ *
17
+ * The last case is only certain when the observer was listening before the
18
+ * host answered `ui/initialize` ({@link ToolObservation.sawHandshake}): hosts
19
+ * send the lifecycle notifications after the handshake, so an observer that
20
+ * saw it has seen everything sent so far. `mountMcpToolkitApp` installs it
21
+ * ahead of `bootstrapView` for exactly that reason.
22
+ *
23
+ * Read-only: it never sends, never answers and never stops a message.
24
+ */
25
+
26
+ /** What the host's `ui/notifications/tool-result` carried. */
27
+ export type ObservedToolResult = "none" | "structured" | "stripped" | "error"
28
+
29
+ /** Everything the observer learned about the rendering call so far. */
30
+ export interface ToolObservation {
31
+ /**
32
+ * The observer saw the host answer `ui/initialize`, so it was listening
33
+ * before any lifecycle notification could be sent: a result it has not
34
+ * seen has not been delivered yet.
35
+ */
36
+ sawHandshake: boolean
37
+ /**
38
+ * The arguments of the complete `ui/notifications/tool-input` — never a
39
+ * partial stream (`tool-input-partial` is ignored). `undefined` until it
40
+ * arrived.
41
+ */
42
+ completeInput: Record<string, unknown> | undefined
43
+ /** What the first `ui/notifications/tool-result` carried. */
44
+ result: ObservedToolResult
45
+ /** The host sent `ui/notifications/tool-cancelled`. */
46
+ cancelled: boolean
47
+ }
48
+
49
+ export const EMPTY_TOOL_OBSERVATION: ToolObservation = Object.freeze({
50
+ sawHandshake: false,
51
+ completeInput: undefined,
52
+ result: "none",
53
+ cancelled: false,
54
+ })
55
+
56
+ /** A host → view message the observer acts on. */
57
+ export type HostLifecycleMessage =
58
+ | { kind: "handshake" }
59
+ | { kind: "tool-input"; arguments: Record<string, unknown> }
60
+ | { kind: "tool-result"; result: Exclude<ObservedToolResult, "none"> }
61
+ | { kind: "tool-cancelled" }
62
+
63
+ function isRecord(value: unknown): value is Record<string, unknown> {
64
+ return typeof value === "object" && value !== null && !Array.isArray(value)
65
+ }
66
+
67
+ /**
68
+ * Classify a tool-result notification the way mcp-use 2.x's latch does: an
69
+ * `isError` result is an error, a result with `structuredContent` (even
70
+ * `null`) latches as ready, and anything else is the stripped, content-only
71
+ * result the latch ignores.
72
+ */
73
+ export function classifyToolResult(
74
+ params: Record<string, unknown>,
75
+ ): Exclude<ObservedToolResult, "none"> {
76
+ if (params.isError === true) return "error"
77
+ return params.structuredContent === undefined ? "stripped" : "structured"
78
+ }
79
+
80
+ /** The host's answer to `ui/initialize`: a response carrying its capabilities or protocol version. */
81
+ function isHandshakeResponse(message: Record<string, unknown>): boolean {
82
+ if (!("id" in message) || !isRecord(message.result)) return false
83
+ return (
84
+ isRecord(message.result.hostCapabilities) || typeof message.result.protocolVersion === "string"
85
+ )
86
+ }
87
+
88
+ /**
89
+ * Decode one postMessage payload into the lifecycle message it is, or `null`
90
+ * for anything else (other methods, requests, responses, foreign traffic).
91
+ */
92
+ export function classifyHostMessage(data: unknown): HostLifecycleMessage | null {
93
+ if (!isRecord(data) || data.jsonrpc !== "2.0") return null
94
+ if (typeof data.method !== "string") {
95
+ return isHandshakeResponse(data) ? { kind: "handshake" } : null
96
+ }
97
+ const params = isRecord(data.params) ? data.params : {}
98
+ switch (data.method) {
99
+ case "ui/notifications/tool-input":
100
+ return {
101
+ kind: "tool-input",
102
+ arguments: isRecord(params.arguments) ? params.arguments : {},
103
+ }
104
+ case "ui/notifications/tool-result":
105
+ return { kind: "tool-result", result: classifyToolResult(params) }
106
+ case "ui/notifications/tool-cancelled":
107
+ return { kind: "tool-cancelled" }
108
+ default:
109
+ return null
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Fold one message into the observation. Like the mcp-use latch, the call
115
+ * settles once: after its result or cancellation, later inputs, results and
116
+ * cancellations change nothing.
117
+ */
118
+ export function reduceToolObservation(
119
+ state: ToolObservation,
120
+ message: HostLifecycleMessage,
121
+ ): ToolObservation {
122
+ if (message.kind === "handshake") {
123
+ return state.sawHandshake ? state : { ...state, sawHandshake: true }
124
+ }
125
+ if (state.result !== "none" || state.cancelled) return state
126
+ switch (message.kind) {
127
+ case "tool-input":
128
+ return { ...state, completeInput: message.arguments }
129
+ case "tool-result":
130
+ return { ...state, result: message.result }
131
+ case "tool-cancelled":
132
+ return { ...state, cancelled: true }
133
+ }
134
+ }
135
+
136
+ /** The `window` slice the observer listens on (any `EventTarget`, e.g. in tests). */
137
+ export interface MessageEventSource {
138
+ addEventListener(type: "message", listener: (event: Event) => void): void
139
+ removeEventListener(type: "message", listener: (event: Event) => void): void
140
+ }
141
+
142
+ /** A subscribable observation, shaped for `useSyncExternalStore`. */
143
+ export interface ToolResultObserver {
144
+ subscribe: (listener: () => void) => () => void
145
+ getSnapshot: () => ToolObservation
146
+ /** Stop listening; the last observation stays readable. */
147
+ dispose: () => void
148
+ }
149
+
150
+ /**
151
+ * Observe the lifecycle notifications arriving on `source`. `accept` filters
152
+ * the events (the view accepts only its host: `event.source === window.parent`,
153
+ * the same check the ext-apps transport makes).
154
+ */
155
+ export function createToolResultObserver(
156
+ source: MessageEventSource,
157
+ accept: (event: MessageEvent) => boolean = () => true,
158
+ ): ToolResultObserver {
159
+ let snapshot: ToolObservation = EMPTY_TOOL_OBSERVATION
160
+ const listeners = new Set<() => void>()
161
+ const onMessage = (received: Event) => {
162
+ // Only `message` events are subscribed: each one is a MessageEvent.
163
+ const event = received as MessageEvent
164
+ if (!accept(event)) return
165
+ const message = classifyHostMessage(event.data)
166
+ if (!message) return
167
+ const next = reduceToolObservation(snapshot, message)
168
+ if (next === snapshot) return
169
+ snapshot = next
170
+ for (const listener of [...listeners]) listener()
171
+ }
172
+ source.addEventListener("message", onMessage)
173
+ return {
174
+ subscribe: (listener) => {
175
+ listeners.add(listener)
176
+ return () => {
177
+ listeners.delete(listener)
178
+ }
179
+ },
180
+ getSnapshot: () => snapshot,
181
+ dispose: () => {
182
+ source.removeEventListener("message", onMessage)
183
+ },
184
+ }
185
+ }
186
+
187
+ let documentObserver: ToolResultObserver | null = null
188
+
189
+ /**
190
+ * The view document's observer: installed once and kept for the document's
191
+ * lifetime (one document renders one view). `null` outside a browser and in
192
+ * a top-level window, where no host frames the view.
193
+ *
194
+ * It must listen before the view runtime connects, so it sees the
195
+ * handshake. This module therefore installs it as the bundle evaluates
196
+ * (below), ahead of any `bootstrapView` call in the entry module, whatever
197
+ * the mount; `mountMcpToolkitApp` calls it again (idempotent). Installed any
198
+ * later, it still sees what arrives afterwards, and the recovery falls back
199
+ * to its grace timer for the rest.
200
+ */
201
+ export function installToolResultObserver(): ToolResultObserver | null {
202
+ if (documentObserver) return documentObserver
203
+ if (typeof window === "undefined" || window.parent === window) return null
204
+ const host = window.parent
205
+ documentObserver = createToolResultObserver(window, (event) => event.source === host)
206
+ return documentObserver
207
+ }
208
+
209
+ // Listen from the moment the view bundle evaluates (a passive listener; a
210
+ // no-op outside a framed browser document).
211
+ installToolResultObserver()
@@ -0,0 +1,45 @@
1
+ import { describe, expect, it } from "vitest"
2
+ import { MICRO_LABEL, TYPE_FLOOR_PX, TYPE_SCALE, TYPE_STEPS } from "./typography.js"
3
+ import { cn } from "./utils.js"
4
+
5
+ describe("the type scale (CI §4)", () => {
6
+ it("has five steps, each its own self-named utility", () => {
7
+ expect(TYPE_STEPS).toEqual(["title", "section", "body", "dense", "small"])
8
+ expect(TYPE_SCALE).toEqual({
9
+ title: "text-title",
10
+ section: "text-section",
11
+ body: "text-body",
12
+ dense: "text-dense",
13
+ small: "text-small",
14
+ })
15
+ })
16
+
17
+ it("puts the floor at the CI's smallest size and micro-labels on it, in normal case", () => {
18
+ expect(TYPE_FLOOR_PX).toBe(13)
19
+ expect(MICRO_LABEL).toBe("text-small font-semibold")
20
+ expect(MICRO_LABEL).not.toMatch(/uppercase|text-xs|text-\[/)
21
+ })
22
+ })
23
+
24
+ describe("cn knows the type scale (tailwind-merge would drop it as a colour)", () => {
25
+ it("keeps a scale size next to a text colour, in either order", () => {
26
+ for (const size of Object.values(TYPE_SCALE)) {
27
+ expect(cn(size, "text-muted-foreground")).toBe(`${size} text-muted-foreground`)
28
+ expect(cn("text-danger-ink", size)).toBe(`text-danger-ink ${size}`)
29
+ }
30
+ })
31
+
32
+ it("lets a later size win over an earlier one, scale or Tailwind", () => {
33
+ expect(cn("text-sm", "text-small")).toBe("text-small")
34
+ expect(cn("text-small", "text-xs")).toBe("text-xs")
35
+ expect(cn("text-dense", "text-title")).toBe("text-title")
36
+ expect(cn("md:text-sm", "md:text-dense")).toBe("md:text-dense")
37
+ })
38
+
39
+ it("keeps a leading set after the size (a size resets the line height before it)", () => {
40
+ expect(cn("text-small", "leading-4")).toBe("text-small leading-4")
41
+ expect(cn(MICRO_LABEL, "text-muted-foreground")).toBe(
42
+ "text-small font-semibold text-muted-foreground",
43
+ )
44
+ })
45
+ })
@@ -0,0 +1,47 @@
1
+ /**
2
+ * The type scale of the kit (modeler-tool-design §4, adapted for embedded
3
+ * widgets): five steps, each a role in `theme.css` (`--text-<step>` with its
4
+ * line height and weight) and a Tailwind utility of the same name.
5
+ *
6
+ * | Step | Utility | Size / weight / line height | Use |
7
+ * | --------- | -------------- | --------------------------- | ------------------------------------------ |
8
+ * | `title` | `text-title` | 28 px / 700 / 1.2 | page title (the CI h2; a widget has no h1 40 px) |
9
+ * | `section` | `text-section` | 21 px / 600 / 1.3 | section title (CI h3) |
10
+ * | `body` | `text-body` | 16 px / 400 / 1.6 | running text: state and toast descriptions |
11
+ * | `dense` | `text-dense` | 14 px / 400 / 1.5 | dense tables and controls (inputs, chips, count pills, the inline alert box): the documented product exception, never running text |
12
+ * | `small` | `text-small` | 13 px / 400 / 1.5 | captions, labels, hints, meta rows: the FLOOR |
13
+ *
14
+ * Nothing renders below 13 px — no `text-xs` (12 px), no `text-[10px]`
15
+ * micro-labels (`examples/test/design-system.test.ts` enforces it). A label
16
+ * that used to be a 10–11 px uppercase caption is {@link MICRO_LABEL}: 13 px,
17
+ * semibold, normal case.
18
+ *
19
+ * Combine the utilities through the kit's `cn`: plain tailwind-merge does not
20
+ * know these sizes and would drop `text-small` next to a text colour.
21
+ */
22
+
23
+ /** The five steps, smallest last. */
24
+ export const TYPE_STEPS = ["title", "section", "body", "dense", "small"] as const
25
+
26
+ /** One step of the type scale. */
27
+ export type TypeStep = (typeof TYPE_STEPS)[number]
28
+
29
+ /** The utility class per step (`TYPE_SCALE.small` → `"text-small"`). */
30
+ export const TYPE_SCALE: Readonly<Record<TypeStep, `text-${TypeStep}`>> = {
31
+ title: "text-title",
32
+ section: "text-section",
33
+ body: "text-body",
34
+ dense: "text-dense",
35
+ small: "text-small",
36
+ }
37
+
38
+ /** The smallest text the kit renders, in CSS px (CI §4 "klein"). */
39
+ export const TYPE_FLOOR_PX = 13
40
+
41
+ /**
42
+ * A caption above a value or a column: table heads, KPI labels, group
43
+ * labels. 13 px semibold in normal case — the CI floor, where these used to
44
+ * be 10–11 px uppercase. Typography only: add the colour
45
+ * (`text-muted-foreground`) at the call site.
46
+ */
47
+ export const MICRO_LABEL = "text-small font-semibold"
package/src/lib/utils.ts CHANGED
@@ -1,5 +1,16 @@
1
1
  import { type ClassValue, clsx } from "clsx"
2
- import { twMerge } from "tailwind-merge"
2
+ import { extendTailwindMerge } from "tailwind-merge"
3
+ import { TYPE_STEPS } from "./typography.js"
4
+
5
+ /**
6
+ * tailwind-merge that knows the kit's type scale. Out of the box it reads
7
+ * an unknown `text-small` as a text COLOUR and drops it next to
8
+ * `text-muted-foreground`; registered as font sizes, `text-small` conflicts
9
+ * only with other sizes (`text-sm`, `text-xs`), as it should.
10
+ */
11
+ const twMerge = extendTailwindMerge({
12
+ extend: { theme: { text: [...TYPE_STEPS] } },
13
+ })
3
14
 
4
15
  export function cn(...inputs: ClassValue[]) {
5
16
  return twMerge(clsx(inputs))
@@ -3,5 +3,18 @@
3
3
  // <LocaleProvider> and reads the active locale with useLocale/useTranslate,
4
4
  // without pulling in the host runtime. Pairs with `createTranslator` from
5
5
  // `@miragon/mcp-toolkit-core`.
6
- export { LocaleProvider, useLocale, useTranslate } from "./locale-provider.js"
6
+ export { LocaleProvider, useLocale, useTranslate, useToolkitMessages } from "./locale-provider.js"
7
7
  export type { LocaleContextValue, LocaleProviderProps, BoundTranslate } from "./locale-provider.js"
8
+ export { TOOLKIT_MESSAGES, formatCount, resolveToolkitMessages } from "./toolkit-messages.js"
9
+ export type {
10
+ BuilderMessages,
11
+ CommonMessages,
12
+ FilterBarMessages,
13
+ ListFooterMessages,
14
+ PropsSheetMessages,
15
+ RendererMessages,
16
+ ToolkitLocale,
17
+ ToolkitMessages,
18
+ ToolkitMessagesOverride,
19
+ ViewMessages,
20
+ } from "./toolkit-messages.js"
@@ -0,0 +1,87 @@
1
+ import type { ReactNode } from "react"
2
+ import { renderToStaticMarkup } from "react-dom/server"
3
+ import { describe, expect, it } from "vitest"
4
+ import { ListFooter } from "../components/ListFooter.js"
5
+ import {
6
+ LocaleProvider,
7
+ ToolkitLocaleFallback,
8
+ useLocale,
9
+ useToolkitLocale,
10
+ useToolkitMessages,
11
+ useTranslate,
12
+ type LocaleContextValue,
13
+ } from "./locale-provider.js"
14
+ import { TOOLKIT_MESSAGES } from "./toolkit-messages.js"
15
+
16
+ /**
17
+ * The locale contract: `LocaleProvider` sets the locale for everyone; the
18
+ * `ToolkitLocaleFallback` McpAppView mounts with the host's locale binds ONLY
19
+ * the toolkit's own strings and number format, so a consumer widget's
20
+ * `useLocale()`/`useTranslate()` keep their outside-a-provider behaviour.
21
+ */
22
+
23
+ function Probe() {
24
+ const messages = useToolkitMessages()
25
+ return (
26
+ <p>
27
+ {`locale=${useLocale()} toolkit=${useToolkitLocale()} t=${useTranslate()("k")} close=${messages.common.close}`}
28
+ </p>
29
+ )
30
+ }
31
+
32
+ const render = (node: ReactNode) => renderToStaticMarkup(node)
33
+
34
+ describe("ToolkitLocaleFallback", () => {
35
+ it("binds the toolkit's strings and number locale, not useLocale/useTranslate", () => {
36
+ expect(
37
+ render(
38
+ <ToolkitLocaleFallback locale="de-DE">
39
+ <Probe />
40
+ </ToolkitLocaleFallback>,
41
+ ),
42
+ ).toContain(`locale=en toolkit=de-DE t=k close=${TOOLKIT_MESSAGES.de.common.close}`)
43
+ })
44
+
45
+ it("yields to a LocaleProvider above or below it", () => {
46
+ const below = render(
47
+ <ToolkitLocaleFallback locale="de-DE">
48
+ <LocaleProvider locale="en-GB">
49
+ <Probe />
50
+ </LocaleProvider>
51
+ </ToolkitLocaleFallback>,
52
+ )
53
+ expect(below).toContain(
54
+ `locale=en-GB toolkit=en-GB t=k close=${TOOLKIT_MESSAGES.en.common.close}`,
55
+ )
56
+ const above = render(
57
+ <LocaleProvider locale="en">
58
+ <ToolkitLocaleFallback locale="de-DE">
59
+ <Probe />
60
+ </ToolkitLocaleFallback>
61
+ </LocaleProvider>,
62
+ )
63
+ expect(above).toContain(`locale=en toolkit=en t=k close=${TOOLKIT_MESSAGES.en.common.close}`)
64
+ })
65
+
66
+ it("keeps the defaults outside any provider", () => {
67
+ expect(render(<Probe />)).toContain(
68
+ `locale=en toolkit=en t=k close=${TOOLKIT_MESSAGES.en.common.close}`,
69
+ )
70
+ })
71
+
72
+ it("formats toolkit counts in the language of the sentence around them", () => {
73
+ const html = render(
74
+ <ToolkitLocaleFallback locale="de-DE">
75
+ <ListFooter shown={1200} total={4500} hasMore={false} onLoadMore={() => {}} />
76
+ </ToolkitLocaleFallback>,
77
+ )
78
+ expect(html).toContain("Zeigt 1.200 von 4.500 Zeilen")
79
+ })
80
+ })
81
+
82
+ describe("LocaleContextValue", () => {
83
+ it("accepts a hand-built value without messages (the field is optional)", () => {
84
+ const value: LocaleContextValue = { locale: "en", t: (key) => key }
85
+ expect(value.messages).toBeUndefined()
86
+ })
87
+ })
@@ -1,5 +1,10 @@
1
1
  import { createContext, useContext, useMemo, type ReactNode } from "react"
2
2
  import type { Translate } from "@miragon/mcp-toolkit-core"
3
+ import {
4
+ resolveToolkitMessages,
5
+ type ToolkitMessages,
6
+ type ToolkitMessagesOverride,
7
+ } from "./toolkit-messages.js"
3
8
 
4
9
  /**
5
10
  * A locale-bound translate: the active locale is already applied, so call sites
@@ -13,6 +18,14 @@ export interface LocaleContextValue {
13
18
  locale: string
14
19
  /** Translate a key in the active locale. */
15
20
  t: BoundTranslate
21
+ /**
22
+ * The toolkit's own strings for the active locale (its shipped `en`/`de`
23
+ * catalog, English for any other locale) with the provider's
24
+ * `toolkitMessages` override applied. Read it with {@link useToolkitMessages}.
25
+ * Optional on the type (a hand-built value may omit it); `LocaleProvider`
26
+ * always sets it.
27
+ */
28
+ messages?: ToolkitMessages
16
29
  }
17
30
 
18
31
  const DEFAULT_LOCALE = "en"
@@ -24,10 +37,22 @@ const DEFAULT_LOCALE = "en"
24
37
  * missing wrapper echoes the key (visible + greppable) instead of crashing a
25
38
  * widget that is perfectly renderable in English.
26
39
  */
27
- const DEFAULT_VALUE: LocaleContextValue = { locale: DEFAULT_LOCALE, t: (key) => key }
40
+ const DEFAULT_VALUE = {
41
+ locale: DEFAULT_LOCALE,
42
+ t: (key) => key,
43
+ messages: resolveToolkitMessages(DEFAULT_LOCALE),
44
+ } satisfies Required<LocaleContextValue>
28
45
 
29
46
  const LocaleContext = createContext<LocaleContextValue | null>(null)
30
47
 
48
+ /** The toolkit-only locale a {@link ToolkitLocaleFallback} binds (no `t`). */
49
+ interface ToolkitLocaleScope {
50
+ locale: string
51
+ messages: ToolkitMessages
52
+ }
53
+
54
+ const ToolkitLocaleFallbackContext = createContext<ToolkitLocaleScope | null>(null)
55
+
31
56
  /** Read the active locale tag from the nearest {@link LocaleProvider}. */
32
57
  export function useLocale(): string {
33
58
  return useContext(LocaleContext)?.locale ?? DEFAULT_VALUE.locale
@@ -42,6 +67,56 @@ export function useTranslate(): BoundTranslate {
42
67
  return useContext(LocaleContext)?.t ?? DEFAULT_VALUE.t
43
68
  }
44
69
 
70
+ /**
71
+ * The toolkit's own UI strings for the nearest {@link LocaleProvider}'s locale
72
+ * (English outside a provider). Every toolkit component reads its captions,
73
+ * labels and accessible names here, so a German locale renders no English
74
+ * toolkit chrome.
75
+ */
76
+ export function useToolkitMessages(): ToolkitMessages {
77
+ const provided = useContext(LocaleContext)
78
+ const fallback = useContext(ToolkitLocaleFallbackContext)
79
+ return provided?.messages ?? fallback?.messages ?? DEFAULT_VALUE.messages
80
+ }
81
+
82
+ /**
83
+ * The locale the toolkit's own text is in: the nearest {@link LocaleProvider}'s,
84
+ * else a {@link ToolkitLocaleFallback}'s (the host's, inside `McpAppView`),
85
+ * else `"en"`. Toolkit components format their numbers with it, so a count
86
+ * always matches the language of the sentence around it. Internal: consumer
87
+ * widgets read {@link useLocale}.
88
+ */
89
+ export function useToolkitLocale(): string {
90
+ const provided = useContext(LocaleContext)
91
+ const fallback = useContext(ToolkitLocaleFallbackContext)
92
+ return provided?.locale ?? fallback?.locale ?? DEFAULT_VALUE.locale
93
+ }
94
+
95
+ /**
96
+ * Binds ONLY the toolkit's own strings and number format to `locale` for a
97
+ * subtree — the fallback `McpAppView` mounts with the host's locale. It is not
98
+ * a {@link LocaleProvider}: {@link useLocale} and {@link useTranslate} keep
99
+ * their outside-a-provider behaviour (`"en"`, echoed keys) for consumer
100
+ * widgets, and any `LocaleProvider` above or below wins over it.
101
+ */
102
+ export function ToolkitLocaleFallback({
103
+ locale,
104
+ children,
105
+ }: {
106
+ locale: string
107
+ children: ReactNode
108
+ }): ReactNode {
109
+ const value = useMemo<ToolkitLocaleScope>(
110
+ () => ({ locale, messages: resolveToolkitMessages(locale) }),
111
+ [locale],
112
+ )
113
+ return (
114
+ <ToolkitLocaleFallbackContext.Provider value={value}>
115
+ {children}
116
+ </ToolkitLocaleFallbackContext.Provider>
117
+ )
118
+ }
119
+
45
120
  export interface LocaleProviderProps {
46
121
  /** The active locale tag (e.g. `"en"`, `"de"`). */
47
122
  locale: string
@@ -51,6 +126,12 @@ export interface LocaleProviderProps {
51
126
  * consumer wires the locale before any catalog exists.
52
127
  */
53
128
  translator?: Translate
129
+ /**
130
+ * Override individual toolkit strings for this subtree, per catalog section
131
+ * (e.g. `{ listFooter: { loadMore: "Show more" } }`). Omitted entries keep the
132
+ * shipped `en`/`de` catalog of `locale`.
133
+ */
134
+ toolkitMessages?: ToolkitMessagesOverride
54
135
  children: ReactNode
55
136
  }
56
137
 
@@ -69,13 +150,19 @@ export interface LocaleProviderProps {
69
150
  * <YourWidget />
70
151
  * </LocaleProvider>
71
152
  */
72
- export function LocaleProvider({ locale, translator, children }: LocaleProviderProps): ReactNode {
153
+ export function LocaleProvider({
154
+ locale,
155
+ translator,
156
+ toolkitMessages,
157
+ children,
158
+ }: LocaleProviderProps): ReactNode {
73
159
  const value = useMemo<LocaleContextValue>(
74
160
  () => ({
75
161
  locale,
76
162
  t: translator ? (key, params) => translator(locale, key, params) : (key) => key,
163
+ messages: resolveToolkitMessages(locale, toolkitMessages),
77
164
  }),
78
- [locale, translator],
165
+ [locale, translator, toolkitMessages],
79
166
  )
80
167
  return <LocaleContext.Provider value={value}>{children}</LocaleContext.Provider>
81
168
  }