@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
@@ -4,7 +4,11 @@ import * as React from "react"
4
4
  import { XIcon } from "lucide-react"
5
5
  import { Dialog as DialogPrimitive } from "radix-ui"
6
6
 
7
+ import { FOCUS_RING } from "../lib/focus-ring.js"
8
+ import { useFocusReturn } from "../lib/focus-return.js"
9
+ import { useToolkitMessages } from "../locale/locale-provider.js"
7
10
  import { cn } from "../lib/utils.js"
11
+ import { usePortalContainer } from "../theme/portal-container.js"
8
12
  import { Button } from "./button.js"
9
13
 
10
14
  function Dialog({ ...props }: React.ComponentProps<typeof DialogPrimitive.Root>) {
@@ -15,8 +19,19 @@ function DialogTrigger({ ...props }: React.ComponentProps<typeof DialogPrimitive
15
19
  return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />
16
20
  }
17
21
 
18
- function DialogPortal({ ...props }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
19
- return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
22
+ function DialogPortal({
23
+ container,
24
+ ...props
25
+ }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
26
+ // Default to the nearest ThemeProvider scope (theme + mode), not <body>.
27
+ const portalContainer = usePortalContainer()
28
+ return (
29
+ <DialogPrimitive.Portal
30
+ data-slot="dialog-portal"
31
+ container={container ?? portalContainer ?? undefined}
32
+ {...props}
33
+ />
34
+ )
20
35
  }
21
36
 
22
37
  function DialogClose({ ...props }: React.ComponentProps<typeof DialogPrimitive.Close>) {
@@ -31,7 +46,7 @@ function DialogOverlay({
31
46
  <DialogPrimitive.Overlay
32
47
  data-slot="dialog-overlay"
33
48
  className={cn(
34
- "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
49
+ "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0 bg-scrim fixed inset-0 z-50 backdrop-blur-sm",
35
50
  className,
36
51
  )}
37
52
  {...props}
@@ -43,29 +58,42 @@ function DialogContent({
43
58
  className,
44
59
  children,
45
60
  showCloseButton = true,
61
+ closeLabel,
62
+ onOpenAutoFocus,
63
+ onCloseAutoFocus,
46
64
  ...props
47
65
  }: React.ComponentProps<typeof DialogPrimitive.Content> & {
48
66
  showCloseButton?: boolean
67
+ /** Accessible name of the close button. Default: the locale's "Close". */
68
+ closeLabel?: string
49
69
  }) {
70
+ const messages = useToolkitMessages()
71
+ // Without a DialogTrigger, Radix returns the focus nowhere: hand it back to
72
+ // the element that opened it (lib/focus-return.ts).
73
+ const focusReturn = useFocusReturn({ onOpenAutoFocus, onCloseAutoFocus })
50
74
  return (
51
75
  <DialogPortal data-slot="dialog-portal">
52
76
  <DialogOverlay />
53
77
  <DialogPrimitive.Content
54
78
  data-slot="dialog-content"
55
79
  className={cn(
56
- "bg-background data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 outline-none sm:max-w-lg",
80
+ "bg-popover text-popover-foreground dark:border-input data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 shadow-3 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-md border p-6 outline-hidden duration-(--motion-base) sm:max-w-lg",
57
81
  className,
58
82
  )}
59
83
  {...props}
84
+ {...focusReturn}
60
85
  >
61
86
  {children}
62
87
  {showCloseButton && (
63
88
  <DialogPrimitive.Close
64
89
  data-slot="dialog-close"
65
- className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
90
+ className={cn(
91
+ "text-muted-foreground hover:text-foreground absolute top-4 right-4 rounded-sm transition-colors disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
92
+ FOCUS_RING,
93
+ )}
66
94
  >
67
95
  <XIcon />
68
- <span className="sr-only">Close</span>
96
+ <span className="sr-only">{closeLabel ?? messages.common.close}</span>
69
97
  </DialogPrimitive.Close>
70
98
  )}
71
99
  </DialogPrimitive.Content>
@@ -86,11 +114,15 @@ function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
86
114
  function DialogFooter({
87
115
  className,
88
116
  showCloseButton = false,
117
+ closeLabel,
89
118
  children,
90
119
  ...props
91
120
  }: React.ComponentProps<"div"> & {
92
121
  showCloseButton?: boolean
122
+ /** Label of the footer close button. Default: the locale's "Close". */
123
+ closeLabel?: string
93
124
  }) {
125
+ const messages = useToolkitMessages()
94
126
  return (
95
127
  <div
96
128
  data-slot="dialog-footer"
@@ -100,7 +132,7 @@ function DialogFooter({
100
132
  {children}
101
133
  {showCloseButton && (
102
134
  <DialogPrimitive.Close asChild>
103
- <Button variant="outline">Close</Button>
135
+ <Button variant="outline">{closeLabel ?? messages.common.close}</Button>
104
136
  </DialogPrimitive.Close>
105
137
  )}
106
138
  </div>
@@ -111,7 +143,9 @@ function DialogTitle({ className, ...props }: React.ComponentProps<typeof Dialog
111
143
  return (
112
144
  <DialogPrimitive.Title
113
145
  data-slot="dialog-title"
114
- className={cn("text-lg leading-none font-semibold", className)}
146
+ // The dialog's heading row (CI §8: weight 600) on the type scale's
147
+ // section step (21 px / 600, the CI h3).
148
+ className={cn("text-section", className)}
115
149
  {...props}
116
150
  />
117
151
  )
@@ -8,9 +8,11 @@ function Input({ className, type, ...props }: React.ComponentProps<"input">) {
8
8
  type={type}
9
9
  data-slot="input"
10
10
  className={cn(
11
- "border-input selection:bg-primary selection:text-primary-foreground file:text-foreground placeholder:text-muted-foreground dark:bg-input/30 h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
12
- "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
13
- "aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40",
11
+ // Input border in --input (3:1 on its surface); focus turns border and a
12
+ // 1px ring --focus: a 2px opaque blue edge (CI §8).
13
+ "border-input selection:bg-primary selection:text-primary-foreground file:text-foreground placeholder:text-muted-foreground bg-background h-9 w-full min-w-0 rounded-md border px-3 py-1 text-base outline-hidden transition-[color,box-shadow,border-color] file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
14
+ "focus-visible:border-focus focus-visible:ring-focus focus-visible:ring-1",
15
+ "aria-invalid:border-danger aria-invalid:ring-danger aria-invalid:ring-1",
14
16
  className,
15
17
  )}
16
18
  {...props}
@@ -16,7 +16,7 @@ function ScrollArea({
16
16
  >
17
17
  <ScrollAreaPrimitive.Viewport
18
18
  data-slot="scroll-area-viewport"
19
- className="focus-visible:ring-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1"
19
+ className="focus-visible:ring-focus size-full rounded-[inherit] outline-hidden transition-[color,box-shadow] focus-visible:ring-2 focus-visible:ring-inset"
20
20
  >
21
21
  {children}
22
22
  </ScrollAreaPrimitive.Viewport>
@@ -3,6 +3,7 @@ import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react"
3
3
  import { Select as SelectPrimitive } from "radix-ui"
4
4
 
5
5
  import { cn } from "../lib/utils.js"
6
+ import { usePortalContainer } from "../theme/portal-container.js"
6
7
 
7
8
  function Select({ ...props }: React.ComponentProps<typeof SelectPrimitive.Root>) {
8
9
  return <SelectPrimitive.Root data-slot="select" {...props} />
@@ -29,7 +30,7 @@ function SelectTrigger({
29
30
  data-slot="select-trigger"
30
31
  data-size={size}
31
32
  className={cn(
32
- "border-input focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[placeholder]:text-muted-foreground dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:ring-destructive/40 [&_svg:not([class*='text-'])]:text-muted-foreground flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
33
+ "border-input focus-visible:border-focus focus-visible:ring-focus aria-invalid:border-danger aria-invalid:ring-danger data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground bg-background hover:border-focus flex w-fit items-center justify-between gap-2 rounded-md border px-3 py-2 text-sm whitespace-nowrap outline-hidden transition-[color,box-shadow,border-color] focus-visible:ring-1 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:ring-1 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
33
34
  className,
34
35
  )}
35
36
  {...props}
@@ -49,12 +50,14 @@ function SelectContent({
49
50
  align = "center",
50
51
  ...props
51
52
  }: React.ComponentProps<typeof SelectPrimitive.Content>) {
53
+ // Portal into the nearest ThemeProvider scope (theme + mode), not <body>.
54
+ const portalContainer = usePortalContainer()
52
55
  return (
53
- <SelectPrimitive.Portal>
56
+ <SelectPrimitive.Portal container={portalContainer ?? undefined}>
54
57
  <SelectPrimitive.Content
55
58
  data-slot="select-content"
56
59
  className={cn(
57
- "bg-popover text-popover-foreground data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-md",
60
+ "bg-popover text-popover-foreground dark:border-input data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 shadow-3 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border",
58
61
  position === "popper" &&
59
62
  "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
60
63
  className,
@@ -83,7 +86,7 @@ function SelectLabel({ className, ...props }: React.ComponentProps<typeof Select
83
86
  return (
84
87
  <SelectPrimitive.Label
85
88
  data-slot="select-label"
86
- className={cn("text-muted-foreground px-2 py-1.5 text-xs", className)}
89
+ className={cn("text-muted-foreground text-small px-2 py-1.5", className)}
87
90
  {...props}
88
91
  />
89
92
  )
@@ -4,7 +4,11 @@ import * as React from "react"
4
4
  import { XIcon } from "lucide-react"
5
5
  import { Dialog as SheetPrimitive } from "radix-ui"
6
6
 
7
+ import { FOCUS_RING } from "../lib/focus-ring.js"
8
+ import { useFocusReturn } from "../lib/focus-return.js"
9
+ import { useToolkitMessages } from "../locale/locale-provider.js"
7
10
  import { cn } from "../lib/utils.js"
11
+ import { usePortalContainer } from "../theme/portal-container.js"
8
12
 
9
13
  function Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive.Root>) {
10
14
  return <SheetPrimitive.Root data-slot="sheet" {...props} />
@@ -18,8 +22,16 @@ function SheetClose({ ...props }: React.ComponentProps<typeof SheetPrimitive.Clo
18
22
  return <SheetPrimitive.Close data-slot="sheet-close" {...props} />
19
23
  }
20
24
 
21
- function SheetPortal({ ...props }: React.ComponentProps<typeof SheetPrimitive.Portal>) {
22
- return <SheetPrimitive.Portal data-slot="sheet-portal" {...props} />
25
+ function SheetPortal({ container, ...props }: React.ComponentProps<typeof SheetPrimitive.Portal>) {
26
+ // Default to the nearest ThemeProvider scope (theme + mode), not <body>.
27
+ const portalContainer = usePortalContainer()
28
+ return (
29
+ <SheetPrimitive.Portal
30
+ data-slot="sheet-portal"
31
+ container={container ?? portalContainer ?? undefined}
32
+ {...props}
33
+ />
34
+ )
23
35
  }
24
36
 
25
37
  function SheetOverlay({
@@ -30,7 +42,7 @@ function SheetOverlay({
30
42
  <SheetPrimitive.Overlay
31
43
  data-slot="sheet-overlay"
32
44
  className={cn(
33
- "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
45
+ "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0 bg-scrim fixed inset-0 z-50 backdrop-blur-sm",
34
46
  className,
35
47
  )}
36
48
  {...props}
@@ -43,18 +55,27 @@ function SheetContent({
43
55
  children,
44
56
  side = "right",
45
57
  showCloseButton = true,
58
+ closeLabel,
59
+ onOpenAutoFocus,
60
+ onCloseAutoFocus,
46
61
  ...props
47
62
  }: React.ComponentProps<typeof SheetPrimitive.Content> & {
48
63
  side?: "top" | "right" | "bottom" | "left"
49
64
  showCloseButton?: boolean
65
+ /** Accessible name of the close button. Default: the locale's "Close". */
66
+ closeLabel?: string
50
67
  }) {
68
+ const messages = useToolkitMessages()
69
+ // Without a SheetTrigger, Radix returns the focus nowhere: hand it back to
70
+ // the element that opened it (lib/focus-return.ts).
71
+ const focusReturn = useFocusReturn({ onOpenAutoFocus, onCloseAutoFocus })
51
72
  return (
52
73
  <SheetPortal>
53
74
  <SheetOverlay />
54
75
  <SheetPrimitive.Content
55
76
  data-slot="sheet-content"
56
77
  className={cn(
57
- "bg-background data-[state=closed]:animate-out data-[state=open]:animate-in fixed z-50 flex flex-col gap-4 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
78
+ "bg-popover text-popover-foreground dark:border-input data-[state=closed]:animate-out data-[state=open]:animate-in shadow-3 fixed z-50 flex flex-col gap-4 transition data-[state=closed]:duration-(--motion-base) data-[state=open]:duration-(--motion-slow)",
58
79
  side === "right" &&
59
80
  "data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm",
60
81
  side === "left" &&
@@ -66,12 +87,18 @@ function SheetContent({
66
87
  className,
67
88
  )}
68
89
  {...props}
90
+ {...focusReturn}
69
91
  >
70
92
  {children}
71
93
  {showCloseButton && (
72
- <SheetPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-secondary absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none">
94
+ <SheetPrimitive.Close
95
+ className={cn(
96
+ "text-muted-foreground hover:text-foreground absolute top-4 right-4 rounded-sm transition-colors disabled:pointer-events-none",
97
+ FOCUS_RING,
98
+ )}
99
+ >
73
100
  <XIcon className="size-4" />
74
- <span className="sr-only">Close</span>
101
+ <span className="sr-only">{closeLabel ?? messages.common.close}</span>
75
102
  </SheetPrimitive.Close>
76
103
  )}
77
104
  </SheetPrimitive.Content>
@@ -4,7 +4,7 @@ function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
4
4
  return (
5
5
  <div
6
6
  data-slot="skeleton"
7
- className={cn("bg-muted animate-pulse rounded-md", className)}
7
+ className={cn("bg-border animate-pulse rounded-md", className)}
8
8
  {...props}
9
9
  />
10
10
  )
@@ -1,6 +1,7 @@
1
1
  import * as React from "react"
2
2
  import { Switch as SwitchPrimitive } from "radix-ui"
3
3
 
4
+ import { FOCUS_RING } from "../lib/focus-ring.js"
4
5
  import { cn } from "../lib/utils.js"
5
6
 
6
7
  function Switch({ className, ...props }: React.ComponentProps<typeof SwitchPrimitive.Root>) {
@@ -8,14 +9,15 @@ function Switch({ className, ...props }: React.ComponentProps<typeof SwitchPrimi
8
9
  <SwitchPrimitive.Root
9
10
  data-slot="switch"
10
11
  className={cn(
11
- "peer focus-visible:ring-ring focus-visible:ring-offset-background data-[state=checked]:bg-primary data-[state=unchecked]:bg-input inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-xs transition-colors focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50",
12
+ "peer data-[state=checked]:bg-primary data-[state=unchecked]:bg-input inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors disabled:cursor-not-allowed disabled:opacity-50",
13
+ FOCUS_RING,
12
14
  className,
13
15
  )}
14
16
  {...props}
15
17
  >
16
18
  <SwitchPrimitive.Thumb
17
19
  data-slot="switch-thumb"
18
- className="bg-background pointer-events-none block h-4 w-4 rounded-full shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0"
20
+ className="bg-primary-foreground shadow-1 pointer-events-none block h-4 w-4 rounded-full ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0"
19
21
  />
20
22
  </SwitchPrimitive.Root>
21
23
  )
@@ -2,6 +2,7 @@ import * as React from "react"
2
2
  import { cva, type VariantProps } from "class-variance-authority"
3
3
  import { Tabs as TabsPrimitive } from "radix-ui"
4
4
 
5
+ import { FOCUS_RING, FOCUS_RING_INSET } from "../lib/focus-ring.js"
5
6
  import { cn } from "../lib/utils.js"
6
7
 
7
8
  function Tabs({
@@ -21,7 +22,7 @@ function Tabs({
21
22
  }
22
23
 
23
24
  const tabsListVariants = cva(
24
- "group/tabs-list inline-flex w-fit items-center justify-center rounded-lg p-[3px] text-muted-foreground group-data-[orientation=horizontal]/tabs:h-9 group-data-[orientation=vertical]/tabs:h-fit group-data-[orientation=vertical]/tabs:flex-col data-[variant=line]:rounded-none",
25
+ "group/tabs-list inline-flex w-fit items-center justify-center rounded-md p-[3px] text-muted-foreground group-data-[orientation=horizontal]/tabs:h-9 group-data-[orientation=vertical]/tabs:h-fit group-data-[orientation=vertical]/tabs:flex-col data-[variant=line]:rounded-none",
25
26
  {
26
27
  variants: {
27
28
  variant: {
@@ -55,10 +56,11 @@ function TabsTrigger({ className, ...props }: React.ComponentProps<typeof TabsPr
55
56
  <TabsPrimitive.Trigger
56
57
  data-slot="tabs-trigger"
57
58
  className={cn(
58
- "text-foreground/60 hover:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring dark:text-muted-foreground dark:hover:text-foreground relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-all group-data-[orientation=vertical]/tabs:w-full group-data-[orientation=vertical]/tabs:justify-start focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 group-data-[variant=default]/tabs-list:data-[state=active]:shadow-sm group-data-[variant=line]/tabs-list:data-[state=active]:shadow-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
59
- "group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent dark:group-data-[variant=line]/tabs-list:data-[state=active]:border-transparent dark:group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent",
60
- "data-[state=active]:bg-background data-[state=active]:text-foreground dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 dark:data-[state=active]:text-foreground",
61
- "after:bg-foreground after:absolute after:opacity-0 after:transition-opacity group-data-[orientation=horizontal]/tabs:after:inset-x-0 group-data-[orientation=horizontal]/tabs:after:bottom-[-5px] group-data-[orientation=horizontal]/tabs:after:h-0.5 group-data-[orientation=vertical]/tabs:after:inset-y-0 group-data-[orientation=vertical]/tabs:after:-right-1 group-data-[orientation=vertical]/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-[state=active]:after:opacity-100",
59
+ "text-muted-foreground hover:text-foreground group-data-[variant=default]/tabs-list:data-[state=active]:shadow-1 relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-sm border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-all group-data-[orientation=vertical]/tabs:w-full group-data-[orientation=vertical]/tabs:justify-start disabled:pointer-events-none disabled:opacity-50 group-data-[variant=line]/tabs-list:data-[state=active]:shadow-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
60
+ FOCUS_RING,
61
+ "group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-[state=active]:border-transparent group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent",
62
+ "data-[state=active]:border-border data-[state=active]:bg-background data-[state=active]:text-foreground",
63
+ "after:bg-primary after:absolute after:opacity-0 after:transition-opacity group-data-[orientation=horizontal]/tabs:after:inset-x-0 group-data-[orientation=horizontal]/tabs:after:bottom-[-5px] group-data-[orientation=horizontal]/tabs:after:h-0.5 group-data-[orientation=vertical]/tabs:after:inset-y-0 group-data-[orientation=vertical]/tabs:after:-right-1 group-data-[orientation=vertical]/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-[state=active]:after:opacity-100",
62
64
  className,
63
65
  )}
64
66
  {...props}
@@ -67,10 +69,11 @@ function TabsTrigger({ className, ...props }: React.ComponentProps<typeof TabsPr
67
69
  }
68
70
 
69
71
  function TabsContent({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Content>) {
72
+ // Radix makes the panel a tab stop (tabIndex 0), so it needs a visible focus.
70
73
  return (
71
74
  <TabsPrimitive.Content
72
75
  data-slot="tabs-content"
73
- className={cn("flex-1 outline-none", className)}
76
+ className={cn("flex-1", FOCUS_RING_INSET, className)}
74
77
  {...props}
75
78
  />
76
79
  )
@@ -0,0 +1,154 @@
1
+ import { QueryClient, QueryClientProvider, useQueryClient } from "@tanstack/react-query"
2
+ import { renderToStaticMarkup } from "react-dom/server"
3
+ import { describe, expect, it, vi } from "vitest"
4
+ import { createStandaloneHostBridge, HostBridgeProvider } from "../app/host-bridge.js"
5
+ import { useToolQueryKey } from "../hooks/use-tool-query.js"
6
+ import {
7
+ AppQueryProvider,
8
+ queryClient,
9
+ useCallTool,
10
+ useToolNameInQueryKey,
11
+ } from "./query-provider.js"
12
+
13
+ /**
14
+ * The tool transport and client of the data hooks (toolkit#176 N25, K18):
15
+ * `AppQueryProvider`'s `callTool`, else the nearest HostBridge — so the
16
+ * documented HostBridgeProvider mount fetches instead of idling.
17
+ */
18
+
19
+ type Caller = ReturnType<typeof useCallTool>
20
+
21
+ function probe(tree: (node: React.ReactNode) => React.ReactNode): {
22
+ callTool: Caller
23
+ client: QueryClient
24
+ } {
25
+ let callTool: Caller
26
+ let client: QueryClient | undefined
27
+ function Probe() {
28
+ callTool = useCallTool()
29
+ client = useQueryClient()
30
+ return null
31
+ }
32
+ renderToStaticMarkup(tree(<Probe />))
33
+ if (!client) throw new Error("probe did not render")
34
+ return { callTool, client }
35
+ }
36
+
37
+ describe("useCallTool", () => {
38
+ it("AppQueryProvider's callTool wins over a bridge", async () => {
39
+ const explicit = vi.fn(() => Promise.resolve("explicit"))
40
+ const bridgeCall = vi.fn(() => Promise.resolve("bridge"))
41
+ const bridge = createStandaloneHostBridge({ callTool: bridgeCall })
42
+ const { callTool } = probe((node) => (
43
+ <HostBridgeProvider bridge={bridge}>
44
+ <AppQueryProvider callTool={explicit}>{node}</AppQueryProvider>
45
+ </HostBridgeProvider>
46
+ ))
47
+ await expect(callTool?.("t", { a: 1 })).resolves.toBe("explicit")
48
+ expect(explicit).toHaveBeenCalledWith("t", { a: 1 })
49
+ expect(bridgeCall).not.toHaveBeenCalled()
50
+ })
51
+
52
+ it("falls back to the nearest HostBridgeProvider's callTool (N25)", async () => {
53
+ const bridgeCall = vi.fn(() => Promise.resolve("bridge"))
54
+ const bridge = createStandaloneHostBridge({ callTool: bridgeCall })
55
+ const { callTool } = probe((node) => (
56
+ <AppQueryProvider>
57
+ <HostBridgeProvider bridge={bridge}>{node}</HostBridgeProvider>
58
+ </AppQueryProvider>
59
+ ))
60
+ await expect(callTool?.("orders_get", { id: 7 })).resolves.toBe("bridge")
61
+ expect(bridgeCall).toHaveBeenCalledWith("orders_get", { id: 7 })
62
+ })
63
+
64
+ it("is undefined with neither (the query then fails with NoToolTransportError)", () => {
65
+ const { callTool } = probe((node) => <AppQueryProvider>{node}</AppQueryProvider>)
66
+ expect(callTool).toBeUndefined()
67
+ })
68
+ })
69
+
70
+ describe("AppQueryProvider", () => {
71
+ it("shares the toolkit's client by default", () => {
72
+ expect(probe((node) => <AppQueryProvider>{node}</AppQueryProvider>).client).toBe(queryClient)
73
+ })
74
+
75
+ it("takes a client of its own, e.g. one per mount", () => {
76
+ const own = new QueryClient()
77
+ expect(probe((node) => <AppQueryProvider client={own}>{node}</AppQueryProvider>).client).toBe(
78
+ own,
79
+ )
80
+ })
81
+
82
+ it("a nested provider without client keeps its parent's, like toolNameInQueryKey", () => {
83
+ const own = new QueryClient()
84
+ const other = new QueryClient()
85
+ const nested = probe((node) => (
86
+ <AppQueryProvider client={own}>
87
+ <AppQueryProvider callTool={vi.fn()}>{node}</AppQueryProvider>
88
+ </AppQueryProvider>
89
+ ))
90
+ expect(nested.client).toBe(own)
91
+ const replaced = probe((node) => (
92
+ <AppQueryProvider client={own}>
93
+ <AppQueryProvider client={other}>{node}</AppQueryProvider>
94
+ </AppQueryProvider>
95
+ ))
96
+ expect(replaced.client).toBe(other)
97
+ })
98
+
99
+ it("a plain TanStack QueryClientProvider is not inherited: the shared client stays", () => {
100
+ const foreign = new QueryClient()
101
+ const { client } = probe((node) => (
102
+ <QueryClientProvider client={foreign}>
103
+ <AppQueryProvider>{node}</AppQueryProvider>
104
+ </QueryClientProvider>
105
+ ))
106
+ expect(client).toBe(queryClient)
107
+ })
108
+ })
109
+
110
+ describe("AppQueryProvider toolNameInQueryKey (K18)", () => {
111
+ /** The setting and the key `useToolQueryKey` builds under `tree`. */
112
+ function keyUnder(tree: (node: React.ReactNode) => React.ReactNode) {
113
+ let setting: boolean | undefined
114
+ let key: unknown[] | undefined
115
+ function Probe() {
116
+ setting = useToolNameInQueryKey()
117
+ key = useToolQueryKey(["feed"], "feed_data", { id: 1 })
118
+ return null
119
+ }
120
+ renderToStaticMarkup(tree(<Probe />))
121
+ return { setting, key }
122
+ }
123
+
124
+ it("is off by default: the 2.x key, outside a provider too", () => {
125
+ expect(keyUnder((node) => node)).toEqual({ setting: false, key: ["feed", { id: 1 }] })
126
+ expect(keyUnder((node) => <AppQueryProvider>{node}</AppQueryProvider>)).toEqual({
127
+ setting: false,
128
+ key: ["feed", { id: 1 }],
129
+ })
130
+ })
131
+
132
+ it("on, the hooks' key carries the tool name", () => {
133
+ expect(
134
+ keyUnder((node) => <AppQueryProvider toolNameInQueryKey>{node}</AppQueryProvider>),
135
+ ).toEqual({ setting: true, key: ["feed", "feed_data", { id: 1 }] })
136
+ })
137
+
138
+ it("a nested provider keeps its parent's setting unless it sets its own", () => {
139
+ expect(
140
+ keyUnder((node) => (
141
+ <AppQueryProvider toolNameInQueryKey>
142
+ <AppQueryProvider callTool={vi.fn()}>{node}</AppQueryProvider>
143
+ </AppQueryProvider>
144
+ )).setting,
145
+ ).toBe(true)
146
+ expect(
147
+ keyUnder((node) => (
148
+ <AppQueryProvider toolNameInQueryKey>
149
+ <AppQueryProvider toolNameInQueryKey={false}>{node}</AppQueryProvider>
150
+ </AppQueryProvider>
151
+ )).setting,
152
+ ).toBe(false)
153
+ })
154
+ })
@@ -1,5 +1,6 @@
1
- import { createContext, useContext, type ReactNode } from "react"
1
+ import { createContext, useContext, useMemo, type ReactNode } from "react"
2
2
  import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
3
+ import { useHostBridgeOrNull } from "../app/host-bridge.js"
3
4
 
4
5
  // `object` (not `Record<string, unknown>`) because generated tool input
5
6
  // interfaces lack an implicit index signature and TS rejects the assignment
@@ -8,6 +9,9 @@ type CallToolFn = (name: string, args: object) => Promise<unknown>
8
9
 
9
10
  const CallToolContext = createContext<CallToolFn | undefined>(undefined)
10
11
 
12
+ /** Whether the data hooks put the tool name into their cache keys (`toolQueryKey`). */
13
+ const ToolNameInQueryKeyContext = createContext(false)
14
+
11
15
  const queryClient = new QueryClient({
12
16
  defaultOptions: {
13
17
  queries: {
@@ -17,22 +21,79 @@ const queryClient = new QueryClient({
17
21
  },
18
22
  })
19
23
 
24
+ /** The client of the nearest `AppQueryProvider`, which a nested one without `client` keeps. */
25
+ const AppQueryClientContext = createContext<QueryClient>(queryClient)
26
+
27
+ /**
28
+ * The query client and tool transport of the toolkit's data hooks.
29
+ *
30
+ * `callTool` is how `useToolQuery` & co. reach the server; without it they
31
+ * fall back to the nearest `HostBridgeProvider`'s bridge (toolkit#176 N25).
32
+ * `client` replaces the shared module-level client, e.g. one per mount;
33
+ * omitted, a nested provider keeps its parent's client (as `McpAppView`'s
34
+ * own provider does), and the outermost one shares the toolkit's client
35
+ * (`queryClient`). `toolNameInQueryKey` puts the tool name into the hooks'
36
+ * cache keys (`[...queryKey, toolName, args]`, see `toolQueryKey`), so two
37
+ * tools never share an entry; omitted, a nested provider keeps its parent's
38
+ * setting, and the outermost one uses the 2.x key `[...queryKey, args]`. The
39
+ * next major makes the tool name the default.
40
+ */
20
41
  export function AppQueryProvider({
21
42
  children,
22
43
  callTool,
44
+ client,
45
+ toolNameInQueryKey,
23
46
  }: {
24
47
  children: ReactNode
25
48
  callTool?: CallToolFn
49
+ client?: QueryClient
50
+ toolNameInQueryKey?: boolean
26
51
  }) {
52
+ const inheritedClient = useAppQueryClient()
53
+ const inherited = useContext(ToolNameInQueryKeyContext)
54
+ const effective = client ?? inheritedClient
27
55
  return (
28
- <QueryClientProvider client={queryClient}>
29
- <CallToolContext.Provider value={callTool}>{children}</CallToolContext.Provider>
30
- </QueryClientProvider>
56
+ <AppQueryClientContext.Provider value={effective}>
57
+ <QueryClientProvider client={effective}>
58
+ <CallToolContext.Provider value={callTool}>
59
+ <ToolNameInQueryKeyContext.Provider value={toolNameInQueryKey ?? inherited}>
60
+ {children}
61
+ </ToolNameInQueryKeyContext.Provider>
62
+ </CallToolContext.Provider>
63
+ </QueryClientProvider>
64
+ </AppQueryClientContext.Provider>
31
65
  )
32
66
  }
33
67
 
68
+ /**
69
+ * The client an `AppQueryProvider` without `client` uses at this point: the
70
+ * nearest `AppQueryProvider`'s, else the toolkit's shared `queryClient`. A
71
+ * plain TanStack `QueryClientProvider` does not count.
72
+ */
73
+ export function useAppQueryClient(): QueryClient {
74
+ return useContext(AppQueryClientContext)
75
+ }
76
+
77
+ /**
78
+ * Whether the nearest `AppQueryProvider` puts the tool name into the data
79
+ * hooks' cache keys (`toolNameInQueryKey`); `false` outside one.
80
+ */
81
+ export function useToolNameInQueryKey(): boolean {
82
+ return useContext(ToolNameInQueryKeyContext)
83
+ }
84
+
85
+ /**
86
+ * The tool transport of the data hooks: `AppQueryProvider`'s `callTool`,
87
+ * else the nearest `HostBridgeProvider`'s `callTool`, else `undefined`.
88
+ */
34
89
  export function useCallTool(): CallToolFn | undefined {
35
- return useContext(CallToolContext)
90
+ const explicit = useContext(CallToolContext)
91
+ const bridge = useHostBridgeOrNull()
92
+ return useMemo<CallToolFn | undefined>(() => {
93
+ if (explicit) return explicit
94
+ if (!bridge) return undefined
95
+ return (name, args) => bridge.callTool(name, args as Record<string, unknown>)
96
+ }, [explicit, bridge])
36
97
  }
37
98
 
38
99
  export { queryClient }
@@ -226,7 +226,7 @@ export const PRIMITIVE_RENDER_CASES: RenderCase[] = [
226
226
  name: "Alert",
227
227
  marker: "RC:Alert",
228
228
  render: () => (
229
- <Alert variant="destructive">
229
+ <Alert variant="danger">
230
230
  <AlertTitle>RC:Alert</AlertTitle>
231
231
  <AlertDescription>The engine rejected the job.</AlertDescription>
232
232
  </Alert>