@miragon/mcp-toolkit-ui 2.6.0 → 2.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (511) hide show
  1. package/README.md +14 -5
  2. package/dist/app/adapt-data-widget.d.ts +1 -22
  3. package/dist/app/adapt-data-widget.d.ts.map +1 -1
  4. package/dist/app/adapt-data-widget.js +1 -35
  5. package/dist/app/adapt-data-widget.js.map +1 -1
  6. package/dist/app/builder/BuilderBanners.d.ts +7 -2
  7. package/dist/app/builder/BuilderBanners.d.ts.map +1 -1
  8. package/dist/app/builder/BuilderBanners.js +8 -3
  9. package/dist/app/builder/BuilderBanners.js.map +1 -1
  10. package/dist/app/builder/CanvasRow.d.ts +4 -2
  11. package/dist/app/builder/CanvasRow.d.ts.map +1 -1
  12. package/dist/app/builder/CanvasRow.js +29 -17
  13. package/dist/app/builder/CanvasRow.js.map +1 -1
  14. package/dist/app/builder/CatalogueSheet.d.ts.map +1 -1
  15. package/dist/app/builder/CatalogueSheet.js +14 -6
  16. package/dist/app/builder/CatalogueSheet.js.map +1 -1
  17. package/dist/app/builder/CellPropsSheet.d.ts +7 -3
  18. package/dist/app/builder/CellPropsSheet.d.ts.map +1 -1
  19. package/dist/app/builder/CellPropsSheet.js +10 -3
  20. package/dist/app/builder/CellPropsSheet.js.map +1 -1
  21. package/dist/app/builder/DiscardDialog.d.ts +18 -0
  22. package/dist/app/builder/DiscardDialog.d.ts.map +1 -0
  23. package/dist/app/builder/DiscardDialog.js +18 -0
  24. package/dist/app/builder/DiscardDialog.js.map +1 -0
  25. package/dist/app/builder/EmptyHint.d.ts +4 -0
  26. package/dist/app/builder/EmptyHint.d.ts.map +1 -1
  27. package/dist/app/builder/EmptyHint.js +6 -1
  28. package/dist/app/builder/EmptyHint.js.map +1 -1
  29. package/dist/app/builder/LayoutSurface.d.ts +33 -9
  30. package/dist/app/builder/LayoutSurface.d.ts.map +1 -1
  31. package/dist/app/builder/LayoutSurface.js +74 -23
  32. package/dist/app/builder/LayoutSurface.js.map +1 -1
  33. package/dist/app/builder/PipelineStrip.d.ts +5 -2
  34. package/dist/app/builder/PipelineStrip.d.ts.map +1 -1
  35. package/dist/app/builder/PipelineStrip.js +33 -15
  36. package/dist/app/builder/PipelineStrip.js.map +1 -1
  37. package/dist/app/builder/SaveDialog.d.ts +9 -1
  38. package/dist/app/builder/SaveDialog.d.ts.map +1 -1
  39. package/dist/app/builder/SaveDialog.js +9 -3
  40. package/dist/app/builder/SaveDialog.js.map +1 -1
  41. package/dist/app/builder/Toolbar.d.ts +14 -4
  42. package/dist/app/builder/Toolbar.d.ts.map +1 -1
  43. package/dist/app/builder/Toolbar.js +24 -7
  44. package/dist/app/builder/Toolbar.js.map +1 -1
  45. package/dist/app/builder/Workspace.d.ts +23 -3
  46. package/dist/app/builder/Workspace.d.ts.map +1 -1
  47. package/dist/app/builder/Workspace.js +58 -8
  48. package/dist/app/builder/Workspace.js.map +1 -1
  49. package/dist/app/builder/builder-model.d.ts +146 -3
  50. package/dist/app/builder/builder-model.d.ts.map +1 -1
  51. package/dist/app/builder/builder-model.js +196 -5
  52. package/dist/app/builder/builder-model.js.map +1 -1
  53. package/dist/app/builder/labels.d.ts +13 -62
  54. package/dist/app/builder/labels.d.ts.map +1 -1
  55. package/dist/app/builder/labels.js +9 -58
  56. package/dist/app/builder/labels.js.map +1 -1
  57. package/dist/app/builder/reveal.d.ts +10 -0
  58. package/dist/app/builder/reveal.d.ts.map +1 -0
  59. package/dist/app/builder/reveal.js +10 -0
  60. package/dist/app/builder/reveal.js.map +1 -0
  61. package/dist/app/builder/status-chip.d.ts.map +1 -1
  62. package/dist/app/builder/status-chip.js +3 -2
  63. package/dist/app/builder/status-chip.js.map +1 -1
  64. package/dist/app/builder/use-builder-catalogue.d.ts +5 -1
  65. package/dist/app/builder/use-builder-catalogue.d.ts.map +1 -1
  66. package/dist/app/builder/use-builder-catalogue.js +50 -17
  67. package/dist/app/builder/use-builder-catalogue.js.map +1 -1
  68. package/dist/app/builder/use-builder-session.d.ts +30 -0
  69. package/dist/app/builder/use-builder-session.d.ts.map +1 -0
  70. package/dist/app/builder/use-builder-session.js +58 -0
  71. package/dist/app/builder/use-builder-session.js.map +1 -0
  72. package/dist/app/builder/use-display-resolver.d.ts +17 -0
  73. package/dist/app/builder/use-display-resolver.d.ts.map +1 -0
  74. package/dist/app/builder/use-display-resolver.js +16 -0
  75. package/dist/app/builder/use-display-resolver.js.map +1 -0
  76. package/dist/app/builder/use-save-dashboard.d.ts +3 -1
  77. package/dist/app/builder/use-save-dashboard.d.ts.map +1 -1
  78. package/dist/app/builder/use-save-dashboard.js +3 -1
  79. package/dist/app/builder/use-save-dashboard.js.map +1 -1
  80. package/dist/app/index.d.ts +8 -3
  81. package/dist/app/index.d.ts.map +1 -1
  82. package/dist/app/index.js +8 -2
  83. package/dist/app/index.js.map +1 -1
  84. package/dist/app/layout-builder.d.ts +18 -25
  85. package/dist/app/layout-builder.d.ts.map +1 -1
  86. package/dist/app/layout-builder.js +84 -78
  87. package/dist/app/layout-builder.js.map +1 -1
  88. package/dist/app/mcp-app-view-chrome.d.ts +103 -20
  89. package/dist/app/mcp-app-view-chrome.d.ts.map +1 -1
  90. package/dist/app/mcp-app-view-chrome.js +125 -40
  91. package/dist/app/mcp-app-view-chrome.js.map +1 -1
  92. package/dist/app/mcp-app-view-states.d.ts +23 -0
  93. package/dist/app/mcp-app-view-states.d.ts.map +1 -0
  94. package/dist/app/mcp-app-view-states.js +55 -0
  95. package/dist/app/mcp-app-view-states.js.map +1 -0
  96. package/dist/app/mcp-app-view.d.ts +72 -5
  97. package/dist/app/mcp-app-view.d.ts.map +1 -1
  98. package/dist/app/mcp-app-view.js +241 -141
  99. package/dist/app/mcp-app-view.js.map +1 -1
  100. package/dist/app/mcp-toolkit-app.d.ts +8 -2
  101. package/dist/app/mcp-toolkit-app.d.ts.map +1 -1
  102. package/dist/app/mcp-toolkit-app.js +33 -4
  103. package/dist/app/mcp-toolkit-app.js.map +1 -1
  104. package/dist/app/mcp-use-host-bridge.d.ts +15 -6
  105. package/dist/app/mcp-use-host-bridge.d.ts.map +1 -1
  106. package/dist/app/mcp-use-host-bridge.js +51 -22
  107. package/dist/app/mcp-use-host-bridge.js.map +1 -1
  108. package/dist/app/model-context.d.ts +71 -0
  109. package/dist/app/model-context.d.ts.map +1 -0
  110. package/dist/app/model-context.js +101 -0
  111. package/dist/app/model-context.js.map +1 -0
  112. package/dist/app/use-build-mode-focus.d.ts +33 -0
  113. package/dist/app/use-build-mode-focus.d.ts.map +1 -0
  114. package/dist/app/use-build-mode-focus.js +59 -0
  115. package/dist/app/use-build-mode-focus.js.map +1 -0
  116. package/dist/app/use-view-actions.d.ts +31 -0
  117. package/dist/app/use-view-actions.d.ts.map +1 -0
  118. package/dist/app/use-view-actions.js +81 -0
  119. package/dist/app/use-view-actions.js.map +1 -0
  120. package/dist/app/use-view-lifecycle.d.ts +42 -0
  121. package/dist/app/use-view-lifecycle.d.ts.map +1 -0
  122. package/dist/app/use-view-lifecycle.js +81 -0
  123. package/dist/app/use-view-lifecycle.js.map +1 -0
  124. package/dist/app/view-lifecycle.d.ts +185 -0
  125. package/dist/app/view-lifecycle.d.ts.map +1 -0
  126. package/dist/app/view-lifecycle.js +207 -0
  127. package/dist/app/view-lifecycle.js.map +1 -0
  128. package/dist/app/widget-fixture.d.ts +10 -3
  129. package/dist/app/widget-fixture.d.ts.map +1 -1
  130. package/dist/app/widget-fixture.js +21 -8
  131. package/dist/app/widget-fixture.js.map +1 -1
  132. package/dist/app/widget-props-sheet.d.ts +6 -10
  133. package/dist/app/widget-props-sheet.d.ts.map +1 -1
  134. package/dist/app/widget-props-sheet.js +25 -20
  135. package/dist/app/widget-props-sheet.js.map +1 -1
  136. package/dist/app/widget-renderer.d.ts +41 -1
  137. package/dist/app/widget-renderer.d.ts.map +1 -1
  138. package/dist/app/widget-renderer.js +82 -31
  139. package/dist/app/widget-renderer.js.map +1 -1
  140. package/dist/components/DrillButton.d.ts +9 -6
  141. package/dist/components/DrillButton.d.ts.map +1 -1
  142. package/dist/components/DrillButton.js +9 -6
  143. package/dist/components/DrillButton.js.map +1 -1
  144. package/dist/components/EmptyState.d.ts +33 -0
  145. package/dist/components/EmptyState.d.ts.map +1 -0
  146. package/dist/components/EmptyState.js +19 -0
  147. package/dist/components/EmptyState.js.map +1 -0
  148. package/dist/components/ErrorState.d.ts +53 -0
  149. package/dist/components/ErrorState.d.ts.map +1 -0
  150. package/dist/components/ErrorState.js +77 -0
  151. package/dist/components/ErrorState.js.map +1 -0
  152. package/dist/components/FilterBar.d.ts +16 -5
  153. package/dist/components/FilterBar.d.ts.map +1 -1
  154. package/dist/components/FilterBar.js +13 -5
  155. package/dist/components/FilterBar.js.map +1 -1
  156. package/dist/components/GroupCard.d.ts.map +1 -1
  157. package/dist/components/GroupCard.js +2 -1
  158. package/dist/components/GroupCard.js.map +1 -1
  159. package/dist/components/KpiGrid.d.ts +70 -18
  160. package/dist/components/KpiGrid.d.ts.map +1 -1
  161. package/dist/components/KpiGrid.js +112 -23
  162. package/dist/components/KpiGrid.js.map +1 -1
  163. package/dist/components/ListFooter.d.ts +32 -4
  164. package/dist/components/ListFooter.d.ts.map +1 -1
  165. package/dist/components/ListFooter.js +35 -6
  166. package/dist/components/ListFooter.js.map +1 -1
  167. package/dist/components/PagedListFooter.d.ts +57 -0
  168. package/dist/components/PagedListFooter.d.ts.map +1 -0
  169. package/dist/components/PagedListFooter.js +27 -0
  170. package/dist/components/PagedListFooter.js.map +1 -0
  171. package/dist/components/Pills.d.ts +36 -12
  172. package/dist/components/Pills.d.ts.map +1 -1
  173. package/dist/components/Pills.js +29 -8
  174. package/dist/components/Pills.js.map +1 -1
  175. package/dist/components/SectionHeading.d.ts +3 -1
  176. package/dist/components/SectionHeading.d.ts.map +1 -1
  177. package/dist/components/SectionHeading.js +6 -2
  178. package/dist/components/SectionHeading.js.map +1 -1
  179. package/dist/components/Toast.d.ts +43 -0
  180. package/dist/components/Toast.d.ts.map +1 -0
  181. package/dist/components/Toast.js +106 -0
  182. package/dist/components/Toast.js.map +1 -0
  183. package/dist/components/WidgetHeader.d.ts +34 -10
  184. package/dist/components/WidgetHeader.d.ts.map +1 -1
  185. package/dist/components/WidgetHeader.js +11 -6
  186. package/dist/components/WidgetHeader.js.map +1 -1
  187. package/dist/hooks/index.d.ts +4 -4
  188. package/dist/hooks/index.d.ts.map +1 -1
  189. package/dist/hooks/index.js +1 -1
  190. package/dist/hooks/index.js.map +1 -1
  191. package/dist/hooks/tool-result-recovery-model.d.ts +155 -0
  192. package/dist/hooks/tool-result-recovery-model.d.ts.map +1 -0
  193. package/dist/hooks/tool-result-recovery-model.js +273 -0
  194. package/dist/hooks/tool-result-recovery-model.js.map +1 -0
  195. package/dist/hooks/use-tool-query.d.ts +56 -14
  196. package/dist/hooks/use-tool-query.d.ts.map +1 -1
  197. package/dist/hooks/use-tool-query.js +102 -25
  198. package/dist/hooks/use-tool-query.js.map +1 -1
  199. package/dist/hooks/use-tool-result-recovery.d.ts +61 -44
  200. package/dist/hooks/use-tool-result-recovery.d.ts.map +1 -1
  201. package/dist/hooks/use-tool-result-recovery.js +62 -110
  202. package/dist/hooks/use-tool-result-recovery.js.map +1 -1
  203. package/dist/hooks/use-view-data.d.ts +55 -7
  204. package/dist/hooks/use-view-data.d.ts.map +1 -1
  205. package/dist/hooks/use-view-data.js +81 -11
  206. package/dist/hooks/use-view-data.js.map +1 -1
  207. package/dist/hooks/use-view-tool-query.d.ts +12 -7
  208. package/dist/hooks/use-view-tool-query.d.ts.map +1 -1
  209. package/dist/hooks/use-view-tool-query.js +15 -14
  210. package/dist/hooks/use-view-tool-query.js.map +1 -1
  211. package/dist/index.d.ts +27 -13
  212. package/dist/index.d.ts.map +1 -1
  213. package/dist/index.js +29 -7
  214. package/dist/index.js.map +1 -1
  215. package/dist/lib/focus-return.d.ts +61 -0
  216. package/dist/lib/focus-return.d.ts.map +1 -0
  217. package/dist/lib/focus-return.js +46 -0
  218. package/dist/lib/focus-return.js.map +1 -0
  219. package/dist/lib/focus-ring.d.ts +18 -0
  220. package/dist/lib/focus-ring.d.ts.map +1 -0
  221. package/dist/lib/focus-ring.js +18 -0
  222. package/dist/lib/focus-ring.js.map +1 -0
  223. package/dist/lib/icon-stroke.d.ts +12 -0
  224. package/dist/lib/icon-stroke.d.ts.map +1 -0
  225. package/dist/lib/icon-stroke.js +12 -0
  226. package/dist/lib/icon-stroke.js.map +1 -0
  227. package/dist/lib/parse-tool-result.d.ts +23 -1
  228. package/dist/lib/parse-tool-result.d.ts.map +1 -1
  229. package/dist/lib/parse-tool-result.js +38 -2
  230. package/dist/lib/parse-tool-result.js.map +1 -1
  231. package/dist/lib/toast-model.d.ts +86 -0
  232. package/dist/lib/toast-model.d.ts.map +1 -0
  233. package/dist/lib/toast-model.js +102 -0
  234. package/dist/lib/toast-model.js.map +1 -0
  235. package/dist/lib/tone-utils.d.ts +95 -9
  236. package/dist/lib/tone-utils.d.ts.map +1 -1
  237. package/dist/lib/tone-utils.js +100 -9
  238. package/dist/lib/tone-utils.js.map +1 -1
  239. package/dist/lib/tool-query.d.ts +107 -0
  240. package/dist/lib/tool-query.d.ts.map +1 -0
  241. package/dist/lib/tool-query.js +182 -0
  242. package/dist/lib/tool-query.js.map +1 -0
  243. package/dist/lib/tool-result-observer.d.ts +108 -0
  244. package/dist/lib/tool-result-observer.d.ts.map +1 -0
  245. package/dist/lib/tool-result-observer.js +158 -0
  246. package/dist/lib/tool-result-observer.js.map +1 -0
  247. package/dist/lib/typography.d.ts +37 -0
  248. package/dist/lib/typography.d.ts.map +1 -0
  249. package/dist/lib/typography.js +41 -0
  250. package/dist/lib/typography.js.map +1 -0
  251. package/dist/lib/utils.d.ts.map +1 -1
  252. package/dist/lib/utils.js +11 -1
  253. package/dist/lib/utils.js.map +1 -1
  254. package/dist/locale/index.d.ts +3 -1
  255. package/dist/locale/index.d.ts.map +1 -1
  256. package/dist/locale/index.js +2 -1
  257. package/dist/locale/index.js.map +1 -1
  258. package/dist/locale/locale-provider.d.ts +42 -1
  259. package/dist/locale/locale-provider.d.ts.map +1 -1
  260. package/dist/locale/locale-provider.js +44 -3
  261. package/dist/locale/locale-provider.js.map +1 -1
  262. package/dist/locale/message-types.d.ts +336 -0
  263. package/dist/locale/message-types.d.ts.map +1 -0
  264. package/dist/locale/message-types.js +7 -0
  265. package/dist/locale/message-types.js.map +1 -0
  266. package/dist/locale/messages-de.d.ts +9 -0
  267. package/dist/locale/messages-de.d.ts.map +1 -0
  268. package/dist/locale/messages-de.js +185 -0
  269. package/dist/locale/messages-de.js.map +1 -0
  270. package/dist/locale/messages-en.d.ts +8 -0
  271. package/dist/locale/messages-en.d.ts.map +1 -0
  272. package/dist/locale/messages-en.js +184 -0
  273. package/dist/locale/messages-en.js.map +1 -0
  274. package/dist/locale/toolkit-messages.d.ts +57 -0
  275. package/dist/locale/toolkit-messages.d.ts.map +1 -0
  276. package/dist/locale/toolkit-messages.js +103 -0
  277. package/dist/locale/toolkit-messages.js.map +1 -0
  278. package/dist/primitives/alert.d.ts +1 -1
  279. package/dist/primitives/alert.d.ts.map +1 -1
  280. package/dist/primitives/alert.js +6 -2
  281. package/dist/primitives/alert.js.map +1 -1
  282. package/dist/primitives/badge.d.ts +1 -1
  283. package/dist/primitives/badge.d.ts.map +1 -1
  284. package/dist/primitives/badge.js +19 -6
  285. package/dist/primitives/badge.js.map +1 -1
  286. package/dist/primitives/button.d.ts +1 -1
  287. package/dist/primitives/button.d.ts.map +1 -1
  288. package/dist/primitives/button.js +12 -8
  289. package/dist/primitives/button.js.map +1 -1
  290. package/dist/primitives/card.js +4 -4
  291. package/dist/primitives/card.js.map +1 -1
  292. package/dist/primitives/dialog.d.ts +7 -3
  293. package/dist/primitives/dialog.d.ts.map +1 -1
  294. package/dist/primitives/dialog.js +22 -8
  295. package/dist/primitives/dialog.js.map +1 -1
  296. package/dist/primitives/input.d.ts.map +1 -1
  297. package/dist/primitives/input.js +4 -1
  298. package/dist/primitives/input.js.map +1 -1
  299. package/dist/primitives/scroll-area.js +1 -1
  300. package/dist/primitives/scroll-area.js.map +1 -1
  301. package/dist/primitives/select.d.ts.map +1 -1
  302. package/dist/primitives/select.js +6 -3
  303. package/dist/primitives/select.js.map +1 -1
  304. package/dist/primitives/sheet.d.ts +3 -1
  305. package/dist/primitives/sheet.d.ts.map +1 -1
  306. package/dist/primitives/sheet.js +17 -7
  307. package/dist/primitives/sheet.js.map +1 -1
  308. package/dist/primitives/skeleton.js +1 -1
  309. package/dist/primitives/skeleton.js.map +1 -1
  310. package/dist/primitives/switch.d.ts.map +1 -1
  311. package/dist/primitives/switch.js +2 -1
  312. package/dist/primitives/switch.js.map +1 -1
  313. package/dist/primitives/tabs.d.ts.map +1 -1
  314. package/dist/primitives/tabs.js +5 -3
  315. package/dist/primitives/tabs.js.map +1 -1
  316. package/dist/providers/query-provider.d.ts +32 -1
  317. package/dist/providers/query-provider.d.ts.map +1 -1
  318. package/dist/providers/query-provider.js +53 -4
  319. package/dist/providers/query-provider.js.map +1 -1
  320. package/dist/render-cases-primitives.js +1 -1
  321. package/dist/render-cases-primitives.js.map +1 -1
  322. package/dist/render-cases.d.ts.map +1 -1
  323. package/dist/render-cases.js +48 -6
  324. package/dist/render-cases.js.map +1 -1
  325. package/dist/render-effects.d.ts +42 -0
  326. package/dist/render-effects.d.ts.map +1 -0
  327. package/dist/render-effects.js +81 -0
  328. package/dist/render-effects.js.map +1 -0
  329. package/dist/render-scan.d.ts +25 -0
  330. package/dist/render-scan.d.ts.map +1 -0
  331. package/dist/render-scan.js +66 -0
  332. package/dist/render-scan.js.map +1 -0
  333. package/dist/theme/cd-tokens.d.ts +66 -0
  334. package/dist/theme/cd-tokens.d.ts.map +1 -0
  335. package/dist/theme/cd-tokens.js +64 -0
  336. package/dist/theme/cd-tokens.js.map +1 -0
  337. package/dist/theme/create-theme.d.ts +214 -39
  338. package/dist/theme/create-theme.d.ts.map +1 -1
  339. package/dist/theme/create-theme.js +204 -13
  340. package/dist/theme/create-theme.js.map +1 -1
  341. package/dist/theme/fonts.d.ts +38 -0
  342. package/dist/theme/fonts.d.ts.map +1 -0
  343. package/dist/theme/fonts.js +56 -0
  344. package/dist/theme/fonts.js.map +1 -0
  345. package/dist/theme/host-fonts.d.ts +39 -0
  346. package/dist/theme/host-fonts.d.ts.map +1 -0
  347. package/dist/theme/host-fonts.js +44 -0
  348. package/dist/theme/host-fonts.js.map +1 -0
  349. package/dist/theme/index.d.ts +8 -1
  350. package/dist/theme/index.d.ts.map +1 -1
  351. package/dist/theme/index.js +5 -1
  352. package/dist/theme/index.js.map +1 -1
  353. package/dist/theme/portal-container.d.ts +16 -0
  354. package/dist/theme/portal-container.d.ts.map +1 -0
  355. package/dist/theme/portal-container.js +19 -0
  356. package/dist/theme/portal-container.js.map +1 -0
  357. package/dist/theme/presets.d.ts +24 -9
  358. package/dist/theme/presets.d.ts.map +1 -1
  359. package/dist/theme/presets.js +44 -11
  360. package/dist/theme/presets.js.map +1 -1
  361. package/dist/theme/theme-provider.d.ts +27 -9
  362. package/dist/theme/theme-provider.d.ts.map +1 -1
  363. package/dist/theme/theme-provider.js +42 -20
  364. package/dist/theme/theme-provider.js.map +1 -1
  365. package/package.json +11 -5
  366. package/src/app/adapt-data-widget.tsx +2 -42
  367. package/src/app/builder/BuilderBanners.tsx +26 -13
  368. package/src/app/builder/CanvasRow.tsx +79 -32
  369. package/src/app/builder/CatalogueSheet.tsx +58 -35
  370. package/src/app/builder/CellPropsSheet.tsx +17 -2
  371. package/src/app/builder/DiscardDialog.tsx +59 -0
  372. package/src/app/builder/EmptyHint.tsx +16 -11
  373. package/src/app/builder/LayoutSurface.tsx +207 -67
  374. package/src/app/builder/PipelineStrip.tsx +265 -156
  375. package/src/app/builder/SaveDialog.tsx +25 -14
  376. package/src/app/builder/Toolbar.tsx +154 -57
  377. package/src/app/builder/Workspace.tsx +226 -56
  378. package/src/app/builder/builder-feedback.test.tsx +75 -0
  379. package/src/app/builder/builder-model.test.ts +5 -0
  380. package/src/app/builder/builder-model.ts +346 -6
  381. package/src/app/builder/builder-model.ux.test.ts +456 -0
  382. package/src/app/builder/builder-ux.test.tsx +844 -0
  383. package/src/app/builder/labels.ts +15 -121
  384. package/src/app/builder/reveal.ts +10 -0
  385. package/src/app/builder/status-chip.tsx +8 -4
  386. package/src/app/builder/use-builder-catalogue.ts +62 -19
  387. package/src/app/builder/use-builder-session.ts +68 -0
  388. package/src/app/builder/use-display-resolver.ts +24 -0
  389. package/src/app/builder/use-save-dashboard.ts +5 -0
  390. package/src/app/index.ts +20 -3
  391. package/src/app/layout-builder.tsx +193 -186
  392. package/src/app/mcp-app-view-chrome.test.tsx +487 -14
  393. package/src/app/mcp-app-view-chrome.tsx +328 -100
  394. package/src/app/mcp-app-view-states.tsx +140 -0
  395. package/src/app/mcp-app-view.effects.test.tsx +292 -0
  396. package/src/app/mcp-app-view.test.tsx +478 -0
  397. package/src/app/mcp-app-view.tsx +447 -190
  398. package/src/app/mcp-toolkit-app.test.tsx +40 -0
  399. package/src/app/mcp-toolkit-app.tsx +39 -8
  400. package/src/app/mcp-use-host-bridge.test.tsx +180 -0
  401. package/src/app/mcp-use-host-bridge.tsx +67 -24
  402. package/src/app/model-context.test.tsx +116 -0
  403. package/src/app/model-context.tsx +125 -0
  404. package/src/app/use-build-mode-focus.ts +62 -0
  405. package/src/app/use-view-actions.ts +89 -0
  406. package/src/app/use-view-lifecycle.ts +122 -0
  407. package/src/app/view-lifecycle.test.ts +443 -0
  408. package/src/app/view-lifecycle.ts +334 -0
  409. package/src/app/widget-fixture.effects.test.tsx +51 -0
  410. package/src/app/widget-fixture.render.test.tsx +81 -0
  411. package/src/app/widget-fixture.tsx +34 -11
  412. package/src/app/widget-props-sheet.tsx +67 -39
  413. package/src/app/widget-renderer.render.test.tsx +131 -0
  414. package/src/app/widget-renderer.tsx +136 -38
  415. package/src/components/DrillButton.tsx +24 -10
  416. package/src/components/EmptyState.tsx +83 -0
  417. package/src/components/ErrorState.tsx +204 -0
  418. package/src/components/FilterBar.test.tsx +82 -0
  419. package/src/components/FilterBar.tsx +56 -27
  420. package/src/components/GroupCard.tsx +3 -2
  421. package/src/components/KpiGrid.test.tsx +279 -0
  422. package/src/components/KpiGrid.tsx +329 -91
  423. package/src/components/ListFooter.test.tsx +116 -0
  424. package/src/components/ListFooter.tsx +110 -12
  425. package/src/components/PagedListFooter.test.tsx +140 -0
  426. package/src/components/PagedListFooter.tsx +107 -0
  427. package/src/components/Pills.test.tsx +77 -0
  428. package/src/components/Pills.tsx +78 -16
  429. package/src/components/SectionHeading.tsx +9 -3
  430. package/src/components/Toast.tsx +206 -0
  431. package/src/components/WidgetHeader.test.tsx +78 -0
  432. package/src/components/WidgetHeader.tsx +63 -16
  433. package/src/components/feedback-states.test.tsx +314 -0
  434. package/src/components/tone-figures.test.tsx +58 -0
  435. package/src/fonts.css +19 -0
  436. package/src/globals.css +9 -165
  437. package/src/hooks/index.ts +6 -3
  438. package/src/hooks/tool-result-recovery-model.test.ts +744 -0
  439. package/src/hooks/tool-result-recovery-model.ts +418 -0
  440. package/src/hooks/use-tool-query.effects.test.tsx +205 -0
  441. package/src/hooks/use-tool-query.ts +156 -33
  442. package/src/hooks/use-tool-result-recovery.effects.test.tsx +141 -0
  443. package/src/hooks/use-tool-result-recovery.test.ts +28 -5
  444. package/src/hooks/use-tool-result-recovery.ts +101 -131
  445. package/src/hooks/use-view-data.test.tsx +169 -0
  446. package/src/hooks/use-view-data.ts +131 -10
  447. package/src/hooks/use-view-tool-query.ts +25 -19
  448. package/src/i18n-coverage.test.tsx +425 -0
  449. package/src/index.ts +102 -13
  450. package/src/lib/focus-return.test.ts +288 -0
  451. package/src/lib/focus-return.ts +96 -0
  452. package/src/lib/focus-ring.ts +20 -0
  453. package/src/lib/icon-stroke.test.ts +59 -0
  454. package/src/lib/icon-stroke.ts +13 -0
  455. package/src/lib/parse-tool-result.test.ts +46 -1
  456. package/src/lib/parse-tool-result.ts +40 -2
  457. package/src/lib/toast-model.test.ts +233 -0
  458. package/src/lib/toast-model.ts +165 -0
  459. package/src/lib/tone-utils.test.ts +125 -16
  460. package/src/lib/tone-utils.ts +144 -15
  461. package/src/lib/tool-query.test.ts +569 -0
  462. package/src/lib/tool-query.ts +253 -0
  463. package/src/lib/tool-result-observer.test.ts +296 -0
  464. package/src/lib/tool-result-observer.ts +211 -0
  465. package/src/lib/typography.test.ts +45 -0
  466. package/src/lib/typography.ts +47 -0
  467. package/src/lib/utils.ts +12 -1
  468. package/src/locale/index.ts +14 -1
  469. package/src/locale/locale-provider.test.tsx +87 -0
  470. package/src/locale/locale-provider.tsx +90 -3
  471. package/src/locale/message-types.ts +337 -0
  472. package/src/locale/messages-de.ts +198 -0
  473. package/src/locale/messages-en.ts +196 -0
  474. package/src/locale/toolkit-messages.test.ts +349 -0
  475. package/src/locale/toolkit-messages.ts +143 -0
  476. package/src/no-hardcoded-copy.test.ts +432 -0
  477. package/src/primitives/alert.tsx +7 -2
  478. package/src/primitives/badge.tsx +19 -7
  479. package/src/primitives/button.tsx +13 -8
  480. package/src/primitives/card.tsx +4 -4
  481. package/src/primitives/dialog.tsx +42 -8
  482. package/src/primitives/input.tsx +5 -3
  483. package/src/primitives/scroll-area.tsx +1 -1
  484. package/src/primitives/select.tsx +7 -4
  485. package/src/primitives/sheet.tsx +33 -6
  486. package/src/primitives/skeleton.tsx +1 -1
  487. package/src/primitives/switch.tsx +4 -2
  488. package/src/primitives/tabs.tsx +9 -6
  489. package/src/providers/query-provider.test.tsx +154 -0
  490. package/src/providers/query-provider.tsx +66 -5
  491. package/src/render-cases-primitives.tsx +1 -1
  492. package/src/render-cases.tsx +82 -6
  493. package/src/render-effects.tsx +96 -0
  494. package/src/render-scan.ts +75 -0
  495. package/src/theme/cd-tokens.generated.css +48 -0
  496. package/src/theme/cd-tokens.test.ts +109 -0
  497. package/src/theme/cd-tokens.ts +67 -0
  498. package/src/theme/create-theme.test.ts +253 -5
  499. package/src/theme/create-theme.ts +341 -41
  500. package/src/theme/fonts.test.ts +85 -0
  501. package/src/theme/fonts.ts +72 -0
  502. package/src/theme/host-fonts.test.ts +135 -0
  503. package/src/theme/host-fonts.ts +52 -0
  504. package/src/theme/index.ts +15 -1
  505. package/src/theme/portal-container.ts +20 -0
  506. package/src/theme/presets.ts +44 -11
  507. package/src/theme/theme-css.test.ts +619 -0
  508. package/src/theme/theme-provider.test.tsx +82 -0
  509. package/src/theme/theme-provider.tsx +60 -24
  510. package/src/theme.css +344 -0
  511. package/ui-catalog.json +783 -49
@@ -1,29 +1,40 @@
1
1
  import type { CSSProperties } from "react"
2
2
 
3
3
  /**
4
- * A curated, brand-relevant subset of the toolkit's CSS-variable theming
5
- * surface (the full set lives in `packages/ui/src/globals.css`). These are the
6
- * tokens a white-label client almost always wants to change — the brand colour
7
- * pair, the surface/accent colours, the border/ring, and the shape/typography
8
- * knobs — expressed in plain, design-system terms.
4
+ * The toolkit's theming surface in plain, design-system terms: one optional
5
+ * field per ROLE variable defined in `packages/ui/src/theme.css` (the role-token
6
+ * contract, see {@link ROLE_TOKENS}), plus the legacy shadcn knobs it always had.
9
7
  *
10
- * Every field is **optional**: a theme overrides only the tokens it cares about
11
- * and inherits the rest from the toolkit defaults (`:root` / `.dark`). Colour
12
- * values are any valid CSS colour string (`oklch(...)`, `#rrggbb`, `hsl(...)`),
13
- * `radius` is any CSS length (e.g. `"0.5rem"`), and the font fields are CSS
14
- * `font-family` lists.
8
+ * The defaults are the Miragon theme — every role points at a vendored
9
+ * `--cd-*` corporate-design token, in light and in dark. A brand overrides
10
+ * only the roles it cares about and inherits the rest. Colour values are any
11
+ * CSS colour string (`#rrggbb`, `oklch(...)`, `color-mix(...)`), lengths any
12
+ * CSS length, fonts `font-family` lists, durations CSS times.
15
13
  *
16
- * Unlisted CSS variables (`--popover`, `--chart-*`, `--sidebar-*`, …) are
17
- * intentionally out of scope here: they are part of the theming surface but not
18
- * the brand essentials, so a client tunes them by hand in CSS when needed. See
19
- * {@link TOKEN_TO_CSS_VAR} for the exact field → variable mapping.
14
+ * Roles a component must never take from a host's palette — primary, link,
15
+ * focus, the status tones and the input border — are brand decisions; a host
16
+ * maps only the neutral roles (background, card, foreground, border, …).
17
+ *
18
+ * Unlisted variables (`--chart-*`, `--sidebar-*`, `--popover-foreground`, …)
19
+ * stay themeable in CSS. See {@link TOKEN_TO_CSS_VAR} for the exact mapping.
20
20
  */
21
21
  export interface ThemeTokens {
22
- /** Brand colour. Maps to `--primary`. */
22
+ /** Brand colour: primary button fill, selection. Maps to `--primary`. */
23
23
  primary?: string
24
24
  /** Text/icon colour on a `primary` surface. Maps to `--primary-foreground`. */
25
25
  primaryForeground?: string
26
- /** Accent colour for subtle highlights. Maps to `--accent`. */
26
+ /**
27
+ * Primary button fill on hover. Maps to `--primary-hover`. A theme that sets
28
+ * `primary` but never `primaryHover` gets `primary` at 90 % (the pre-role
29
+ * hover) from {@link resolveThemeVars}.
30
+ */
31
+ primaryHover?: string
32
+ /**
33
+ * Link and interactive text. Maps to `--link`. A theme that sets `primary`
34
+ * but never `link` gets links in `primary` (the pre-role link colour).
35
+ */
36
+ link?: string
37
+ /** Hover/selection highlight surface. Maps to `--accent`. */
27
38
  accent?: string
28
39
  /** Text colour on an `accent` surface. Maps to `--accent-foreground`. */
29
40
  accentForeground?: string
@@ -35,43 +46,269 @@ export interface ThemeTokens {
35
46
  card?: string
36
47
  /** Text colour on a `card` surface. Maps to `--card-foreground`. */
37
48
  cardForeground?: string
38
- /** Border colour for dividers/outlines. Maps to `--border`. */
49
+ /** Menu, select and dialog surface. Maps to `--popover`. */
50
+ popover?: string
51
+ /** Quiet surface (table heads, skeletons, tab rails). Maps to `--muted`. */
52
+ muted?: string
53
+ /** Secondary text — must keep 4.5:1 on `muted`. Maps to `--muted-foreground`. */
54
+ mutedForeground?: string
55
+ /** Secondary button / badge surface. Maps to `--secondary`. */
56
+ secondary?: string
57
+ /** Decorative dividers and outlines (no contrast claim). Maps to `--border`. */
39
58
  border?: string
40
- /** Focus-ring colour. Maps to `--ring`. */
59
+ /** Input border — must keep 3:1 on its surface. Maps to `--input`. */
60
+ input?: string
61
+ /** Legacy focus-ring colour (shadcn). Maps to `--ring`. */
41
62
  ring?: string
42
- /** Base corner radius (a CSS length). Maps to `--radius`. */
63
+ /**
64
+ * Focus ring and focused-input border, opaque, 3:1. Maps to `--focus`. A
65
+ * theme that sets `ring` but never `focus` gets its focus ring in `ring`.
66
+ */
67
+ focus?: string
68
+ /** Destructive action fill. Maps to `--destructive`. */
69
+ destructive?: string
70
+ /**
71
+ * Text/icon colour on a `destructive` fill (white by default; keep 4.5:1).
72
+ * Maps to `--destructive-foreground`.
73
+ */
74
+ destructiveForeground?: string
75
+ /** Success colour — fill, dot, border, icon (never text on light). Maps to `--success`. */
76
+ success?: string
77
+ /** Success tint surface. Maps to `--success-soft`. */
78
+ successSoft?: string
79
+ /** Text colour of the success tone. Maps to `--success-ink`. */
80
+ successInk?: string
81
+ /** Warning colour — fill, dot, border, icon (never text on light). Maps to `--warning`. */
82
+ warning?: string
83
+ /** Warning tint surface. Maps to `--warning-soft`. */
84
+ warningSoft?: string
85
+ /** Text colour of the warning tone. Maps to `--warning-ink`. */
86
+ warningInk?: string
87
+ /** Danger colour — fill, dot, border, icon. Maps to `--danger`. */
88
+ danger?: string
89
+ /** Danger tint surface. Maps to `--danger-soft`. */
90
+ dangerSoft?: string
91
+ /** Text colour of the danger tone. Maps to `--danger-ink`. */
92
+ dangerInk?: string
93
+ /** Info colour — fill, dot, border, icon. Maps to `--info`. */
94
+ info?: string
95
+ /** Info tint surface. Maps to `--info-soft`. */
96
+ infoSoft?: string
97
+ /** Text colour of the info tone. Maps to `--info-ink`. */
98
+ infoInk?: string
99
+ /**
100
+ * Base corner radius (the legacy shadcn knob). Drives `rounded-xl` and up;
101
+ * applied through a theme ({@link ThemeProvider}, {@link resolveThemeVars}
102
+ * or `toStyle()`) it also derives `radiusSm`/`radiusMd`/`radiusLg` when those
103
+ * are not set. In plain CSS `--radius` no longer reshapes `rounded-sm/md/lg`:
104
+ * set `--radius-sm`/`--radius-md`/`--radius-lg` there. Maps to `--radius`.
105
+ */
43
106
  radius?: string
107
+ /** Badges and chips. Maps to `--radius-sm` (`rounded-sm`). */
108
+ radiusSm?: string
109
+ /** The standard: buttons, inputs, cards, dialogs. Maps to `--radius-md` (`rounded-md`). */
110
+ radiusMd?: string
111
+ /** Large panels and tiles. Maps to `--radius-lg` (`rounded-lg`). */
112
+ radiusLg?: string
113
+ /** Pills and avatars. Maps to `--radius-pill` (`rounded-pill`). */
114
+ radiusPill?: string
115
+ /** Subtle elevation. Maps to `--shadow-1` (`shadow-1`). */
116
+ shadow1?: string
117
+ /** Cards, hovered primary buttons. Maps to `--shadow-2` (`shadow-2`). */
118
+ shadow2?: string
119
+ /** Popovers and dialogs. Maps to `--shadow-3` (`shadow-3`). */
120
+ shadow3?: string
121
+ /** Dialog/sheet backdrop. Maps to `--scrim` (`bg-scrim`). */
122
+ scrim?: string
123
+ /** Hover-speed transitions. Maps to `--motion-fast` (the default duration). */
124
+ motionFast?: string
125
+ /** Standard transitions. Maps to `--motion-base`. */
126
+ motionBase?: string
127
+ /** Large enter/leave transitions. Maps to `--motion-slow`. */
128
+ motionSlow?: string
129
+ /** Easing curve. Maps to `--ease` (the default timing function). */
130
+ ease?: string
44
131
  /** Body font-family list. Maps to `--font-sans`. */
45
132
  fontSans?: string
133
+ /** Code, ids and stack traces. Maps to `--font-mono`. */
134
+ fontMono?: string
46
135
  /** Heading font-family list. Maps to `--font-heading`. */
47
136
  fontHeading?: string
137
+ /** Page title size (CI h2, 28 px). Maps to `--text-title` (`text-title`). */
138
+ textTitle?: string
139
+ /** Section title size (CI h3, 21 px). Maps to `--text-section` (`text-section`). */
140
+ textSection?: string
141
+ /** Running text size (16 px). Maps to `--text-body` (`text-body`). */
142
+ textBody?: string
143
+ /** Dense tables and controls (14 px), never running text. Maps to `--text-dense` (`text-dense`). */
144
+ textDense?: string
145
+ /**
146
+ * Captions, labels and hints: the smallest text the kit renders (13 px;
147
+ * keep it at 13 px or more). Maps to `--text-small` (`text-small`).
148
+ */
149
+ textSmall?: string
48
150
  }
49
151
 
50
152
  /** The CSS custom-property name a {@link ThemeTokens} field resolves to. */
51
153
  type CssVarName = `--${string}`
52
154
 
53
155
  /**
54
- * The single source of truth mapping each curated {@link ThemeTokens} field to
55
- * its real CSS variable name from `globals.css`. `tokensToVars` walks this map,
56
- * so adding a field here (and to {@link ThemeTokens}) is all that is needed to
57
- * extend the public theming surface.
156
+ * The single source of truth mapping each {@link ThemeTokens} field to its real
157
+ * CSS variable name in `theme.css`. `tokensToVars` walks this map, so adding a
158
+ * field here (and to {@link ThemeTokens}) is all that is needed to extend the
159
+ * public theming surface.
58
160
  */
59
161
  export const TOKEN_TO_CSS_VAR = {
60
162
  primary: "--primary",
61
163
  primaryForeground: "--primary-foreground",
164
+ primaryHover: "--primary-hover",
165
+ link: "--link",
62
166
  accent: "--accent",
63
167
  accentForeground: "--accent-foreground",
64
168
  background: "--background",
65
169
  foreground: "--foreground",
66
170
  card: "--card",
67
171
  cardForeground: "--card-foreground",
172
+ popover: "--popover",
173
+ muted: "--muted",
174
+ mutedForeground: "--muted-foreground",
175
+ secondary: "--secondary",
68
176
  border: "--border",
177
+ input: "--input",
69
178
  ring: "--ring",
179
+ focus: "--focus",
180
+ destructive: "--destructive",
181
+ destructiveForeground: "--destructive-foreground",
182
+ success: "--success",
183
+ successSoft: "--success-soft",
184
+ successInk: "--success-ink",
185
+ warning: "--warning",
186
+ warningSoft: "--warning-soft",
187
+ warningInk: "--warning-ink",
188
+ danger: "--danger",
189
+ dangerSoft: "--danger-soft",
190
+ dangerInk: "--danger-ink",
191
+ info: "--info",
192
+ infoSoft: "--info-soft",
193
+ infoInk: "--info-ink",
70
194
  radius: "--radius",
195
+ radiusSm: "--radius-sm",
196
+ radiusMd: "--radius-md",
197
+ radiusLg: "--radius-lg",
198
+ radiusPill: "--radius-pill",
199
+ shadow1: "--shadow-1",
200
+ shadow2: "--shadow-2",
201
+ shadow3: "--shadow-3",
202
+ scrim: "--scrim",
203
+ motionFast: "--motion-fast",
204
+ motionBase: "--motion-base",
205
+ motionSlow: "--motion-slow",
206
+ ease: "--ease",
71
207
  fontSans: "--font-sans",
208
+ fontMono: "--font-mono",
72
209
  fontHeading: "--font-heading",
210
+ textTitle: "--text-title",
211
+ textSection: "--text-section",
212
+ textBody: "--text-body",
213
+ textDense: "--text-dense",
214
+ textSmall: "--text-small",
73
215
  } as const satisfies Record<keyof ThemeTokens, CssVarName>
74
216
 
217
+ /**
218
+ * The role-token contract: every variable the toolkit defines for components
219
+ * to use (`theme.css`), in light and — for colours — dark. Components in this
220
+ * package, and in apps built on it, style through these names only, never
221
+ * through raw colours or Tailwind palette classes.
222
+ */
223
+ export const ROLE_TOKENS = [
224
+ "--background",
225
+ "--card",
226
+ "--popover",
227
+ "--muted",
228
+ "--secondary",
229
+ "--foreground",
230
+ "--muted-foreground",
231
+ "--border",
232
+ "--input",
233
+ "--primary",
234
+ "--primary-foreground",
235
+ "--primary-hover",
236
+ "--link",
237
+ "--ring",
238
+ "--focus",
239
+ "--success",
240
+ "--warning",
241
+ "--danger",
242
+ "--info",
243
+ "--success-soft",
244
+ "--warning-soft",
245
+ "--danger-soft",
246
+ "--info-soft",
247
+ "--success-ink",
248
+ "--warning-ink",
249
+ "--danger-ink",
250
+ "--info-ink",
251
+ "--destructive",
252
+ "--radius-sm",
253
+ "--radius-md",
254
+ "--radius-lg",
255
+ "--radius-pill",
256
+ "--shadow-1",
257
+ "--shadow-2",
258
+ "--shadow-3",
259
+ "--scrim",
260
+ "--motion-fast",
261
+ "--motion-base",
262
+ "--motion-slow",
263
+ "--ease",
264
+ "--font-sans",
265
+ "--font-mono",
266
+ "--text-title",
267
+ "--text-section",
268
+ "--text-body",
269
+ "--text-dense",
270
+ "--text-small",
271
+ ] as const satisfies readonly CssVarName[]
272
+
273
+ /**
274
+ * Variables whose toolkit value is the same in light and dark (theme.css
275
+ * declares them once, or identically in both modes). A theme's LIGHT value for
276
+ * one of these also applies in dark mode — a brand's button colour, radius or
277
+ * font does not change with the mode. Every other variable (surfaces, text,
278
+ * links, focus, status tones) takes its dark value from the theme's `dark`
279
+ * tokens or, failing that, from the toolkit's dark defaults — never from the
280
+ * light value, which would put light surfaces into a dark scope.
281
+ * Pinned against theme.css by `theme-css.test.ts`.
282
+ */
283
+ export const MODE_INVARIANT_VARS: ReadonlySet<CssVarName> = new Set<CssVarName>([
284
+ "--primary",
285
+ "--primary-foreground",
286
+ "--primary-hover",
287
+ "--destructive",
288
+ "--destructive-foreground",
289
+ "--input",
290
+ "--radius",
291
+ "--radius-sm",
292
+ "--radius-md",
293
+ "--radius-lg",
294
+ "--radius-pill",
295
+ "--shadow-1",
296
+ "--shadow-2",
297
+ "--shadow-3",
298
+ "--motion-fast",
299
+ "--motion-base",
300
+ "--motion-slow",
301
+ "--ease",
302
+ "--font-sans",
303
+ "--font-mono",
304
+ "--font-heading",
305
+ "--text-title",
306
+ "--text-section",
307
+ "--text-body",
308
+ "--text-dense",
309
+ "--text-small",
310
+ ])
311
+
75
312
  /** A serializable map of CSS custom properties (`--primary` → value). */
76
313
  export type ThemeVars = Record<CssVarName, string>
77
314
 
@@ -85,14 +322,18 @@ export type ThemeVars = Record<CssVarName, string>
85
322
  * logged, or persisted and rehydrated.
86
323
  */
87
324
  export interface ThemeDefinition {
88
- /** CSS variables applied at the theme scope (overrides `:root`). */
325
+ /** CSS variables applied at the theme scope in light mode (overrides `:root`). */
89
326
  vars: ThemeVars
90
- /** Dark-mode CSS variables, applied under `.dark` at the theme scope. */
327
+ /** Dark-mode CSS variables, applied at the theme scope in dark mode. */
91
328
  darkVars?: ThemeVars
92
329
  /**
93
- * The light {@link vars} as a React `style` object (CSS custom properties are
94
- * valid `CSSProperties` keys). Handy for inlining onto a wrapper element
95
- * without the {@link ThemeProvider}.
330
+ * The theme's light-mode variables as a React `style` object (CSS custom
331
+ * properties are valid `CSSProperties` keys): exactly what
332
+ * {@link ThemeProvider} applies in light mode, i.e.
333
+ * {@link resolveThemeVars}`(theme, "light")` — {@link vars} plus the values
334
+ * derived from the legacy knobs (sm/md/lg from `radius`, …). Handy for
335
+ * inlining onto a wrapper element without the provider; for dark mode use
336
+ * {@link resolveThemeVars}`(theme, "dark")`.
96
337
  */
97
338
  toStyle(): CSSProperties
98
339
  }
@@ -119,29 +360,30 @@ export function tokensToVars(tokens: ThemeTokens): ThemeVars {
119
360
  /** Options for {@link createTheme}. */
120
361
  export interface CreateThemeOptions {
121
362
  /**
122
- * Dark-mode token overrides. Applied on top of the toolkit's `.dark` defaults
123
- * when the active mode is dark (see {@link ThemeProvider}). Provide only the
124
- * tokens that differ from light — typically a darker `background`/`card` and a
125
- * brighter `primary`.
363
+ * Dark-mode token overrides. In dark mode a theme scope gets the toolkit's
364
+ * dark defaults, the theme's mode-invariant light tokens (brand button
365
+ * colour, radii, fonts — see {@link MODE_INVARIANT_VARS}) and these. Provide
366
+ * what differs in dark — typically `background`, `card`, `link`.
126
367
  */
127
368
  dark?: ThemeTokens
128
369
  }
129
370
 
130
371
  /**
131
- * Build a serializable {@link ThemeDefinition} from a curated set of brand
372
+ * Build a serializable {@link ThemeDefinition} from a set of brand
132
373
  * {@link ThemeTokens}. This is the one switch a white-label client flips: pass
133
- * your brand's primary colour, radius, and (optionally) dark overrides, and
134
- * every toolkit primitive that reads the design tokens — `Card`, `Button`,
135
- * `Badge`, `bg-card`, `text-primary`, `rounded-lg` — re-skins to match.
374
+ * the brand's roles (and, optionally, dark overrides), and every toolkit
375
+ * primitive that reads the role tokens — `Card`, `Button`, `Badge`, `bg-card`,
376
+ * `text-link`, `rounded-md` — re-skins to match. Without a theme the toolkit
377
+ * renders in the Miragon theme.
136
378
  *
137
379
  * Pure and side-effect-free: it only maps fields to CSS-variable names. Apply
138
380
  * the result with {@link ThemeProvider} (recommended) or by spreading
139
- * `theme.toStyle()` onto a wrapper element.
381
+ * {@link resolveThemeVars}`(theme, mode)` onto a wrapper element.
140
382
  *
141
383
  * @example
142
384
  * const brand = createTheme(
143
- * { primary: "oklch(0.55 0.2 264)", radius: "0.5rem" },
144
- * { dark: { primary: "oklch(0.7 0.18 264)" } },
385
+ * { primary: "#0f766e", link: "#0f766e", focus: "#0f766e", radiusMd: "6px" },
386
+ * { dark: { link: "#5eead4", focus: "#5eead4" } },
145
387
  * )
146
388
  * // <ThemeProvider theme={brand}>…</ThemeProvider>
147
389
  */
@@ -150,9 +392,67 @@ export function createTheme(tokens: ThemeTokens, opts?: CreateThemeOptions): The
150
392
  const darkTokens = opts?.dark
151
393
  const darkVars = darkTokens ? tokensToVars(darkTokens) : undefined
152
394
 
153
- return {
395
+ const theme: ThemeDefinition = {
154
396
  vars,
155
397
  ...(darkVars && Object.keys(darkVars).length > 0 ? { darkVars } : {}),
156
- toStyle: () => vars,
398
+ toStyle: () => resolveThemeVars(theme, "light"),
399
+ }
400
+ return theme
401
+ }
402
+
403
+ /** Whether the theme sets `name` itself, in light or in dark. */
404
+ function declares(theme: ThemeDefinition, name: CssVarName): boolean {
405
+ return name in theme.vars || (theme.darkVars !== undefined && name in theme.darkVars)
406
+ }
407
+
408
+ /**
409
+ * Fill the roles a theme written against the pre-role API leaves unset, from
410
+ * the knobs it does set, so it keeps ONE brand instead of half Miragon:
411
+ *
412
+ * - the base `--radius` reshapes the sm/md/lg steps (CI ratio 6 : 12 : 16);
413
+ * - `--primary` gives `--primary-hover` (90 %, the old `bg-primary/90`) and
414
+ * `--link` (the old link button was `text-primary`);
415
+ * - `--ring` gives `--focus` (the old focus ring).
416
+ *
417
+ * A role the theme sets itself — in either mode — is never derived, and an
418
+ * explicit value always wins.
419
+ */
420
+ function withDerivedVars(vars: ThemeVars, theme: ThemeDefinition): ThemeVars {
421
+ const derived: ThemeVars = {}
422
+ const radius = vars["--radius"]
423
+ if (radius) {
424
+ derived["--radius-sm"] = `calc(${radius} * 0.5)`
425
+ derived["--radius-md"] = radius
426
+ derived["--radius-lg"] = `calc(${radius} * 4 / 3)`
427
+ }
428
+ const primary = vars["--primary"]
429
+ if (primary && !declares(theme, "--primary-hover")) {
430
+ derived["--primary-hover"] = `color-mix(in srgb, ${primary} 90%, transparent)`
431
+ }
432
+ if (primary && !declares(theme, "--link")) derived["--link"] = primary
433
+ const ring = vars["--ring"]
434
+ if (ring && !declares(theme, "--focus")) derived["--focus"] = ring
435
+ return { ...derived, ...vars }
436
+ }
437
+
438
+ /**
439
+ * The CSS variables a theme applies at its scope in `mode`. Light mode: the
440
+ * theme's `vars`. Dark mode: its mode-invariant light vars plus its `darkVars`
441
+ * — never the other light vars, which would beat the toolkit's dark defaults
442
+ * (mcp-toolkit#178 N35). Both modes add what the legacy knobs derive (a base
443
+ * `--radius` → sm/md/lg; `--primary` → hover and link; `--ring` → focus)
444
+ * where the theme does not set those roles itself.
445
+ * `null`/`undefined` yields `{}` (the toolkit defaults apply).
446
+ */
447
+ export function resolveThemeVars(
448
+ theme: ThemeDefinition | null | undefined,
449
+ mode: "light" | "dark",
450
+ ): ThemeVars {
451
+ if (!theme) return {}
452
+ if (mode === "light") return withDerivedVars(theme.vars, theme)
453
+ const carried: ThemeVars = {}
454
+ for (const [name, value] of Object.entries(theme.vars) as [CssVarName, string][]) {
455
+ if (MODE_INVARIANT_VARS.has(name)) carried[name] = value
157
456
  }
457
+ return withDerivedVars({ ...carried, ...theme.darkVars }, theme)
158
458
  }
@@ -0,0 +1,85 @@
1
+ /// <reference types="node" />
2
+ import { readFileSync } from "node:fs"
3
+ import { describe, expect, it } from "vitest"
4
+ import { GEIST_FONT_FILES, fontFaces } from "./fonts.js"
5
+
6
+ describe("fontFaces (opt-in Geist by URL)", () => {
7
+ // Built inside each test: a describe-scope value is computed once at
8
+ // collection, outside any mutant Stryker activates.
9
+ const css = () => fontFaces({ baseUrl: "https://mcp.example.com/fonts" })
10
+
11
+ it("declares Geist and Geist Mono — the families the font tokens name first", () => {
12
+ expect(css()).toContain('font-family: "Geist";')
13
+ expect(css()).toContain('font-family: "Geist Mono";')
14
+ expect(css().match(/@font-face \{/g)).toHaveLength(2)
15
+ })
16
+
17
+ it("points at the latin variable woff2 files under the base URL", () => {
18
+ expect(css()).toContain(
19
+ 'src: url("https://mcp.example.com/fonts/geist-latin-wght-normal.woff2") format("woff2");',
20
+ )
21
+ expect(css()).toContain(
22
+ 'src: url("https://mcp.example.com/fonts/geist-mono-latin-wght-normal.woff2") format("woff2");',
23
+ )
24
+ })
25
+
26
+ it("covers the whole weight axis, swaps by default and limits itself to latin", () => {
27
+ expect(css().match(/font-weight: 100 900;/g)).toHaveLength(2)
28
+ expect(css().match(/font-display: swap;/g)).toHaveLength(2)
29
+ expect(css().match(/font-style: normal;/g)).toHaveLength(2)
30
+ expect(css().match(/unicode-range: U\+0000-00FF,/g)).toHaveLength(2)
31
+ })
32
+
33
+ it("renders two complete @font-face rules, one declaration per line", () => {
34
+ const range =
35
+ "U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD"
36
+ const rule = (family: string, file: string) =>
37
+ [
38
+ "@font-face {",
39
+ ` font-family: "${family}";`,
40
+ " font-style: normal;",
41
+ " font-display: swap;",
42
+ " font-weight: 100 900;",
43
+ ` src: url("/f/${file}") format("woff2");`,
44
+ ` unicode-range: ${range};`,
45
+ "}",
46
+ ].join("\n")
47
+ expect(fontFaces({ baseUrl: "/f" })).toBe(
48
+ `${rule("Geist", "geist-latin-wght-normal.woff2")}\n${rule("Geist Mono", "geist-mono-latin-wght-normal.woff2")}`,
49
+ )
50
+ })
51
+
52
+ it("strips every leading slash of a file name but keeps its inner path", () => {
53
+ const out = fontFaces({ baseUrl: "/f", files: { sans: "//a.woff2", mono: "sub/b.woff2" } })
54
+ expect(out).toContain('url("/f/a.woff2")')
55
+ expect(out).toContain('url("/f/sub/b.woff2")')
56
+ })
57
+
58
+ it("joins base URL and file name with exactly one slash", () => {
59
+ const out = fontFaces({ baseUrl: "/fonts///", files: { sans: "/a.woff2", mono: "b.woff2" } })
60
+ expect(out).toContain('url("/fonts/a.woff2")')
61
+ expect(out).toContain('url("/fonts/b.woff2")')
62
+ })
63
+
64
+ it("honours a custom font-display strategy", () => {
65
+ expect(fontFaces({ baseUrl: "/f", display: "optional" })).toContain("font-display: optional;")
66
+ })
67
+
68
+ it('refuses URLs that could break out of url("…")', () => {
69
+ for (const baseUrl of ['/f")}body{', "/f'x", "/f x", "/f(", "/f\\", "/f<x>"]) {
70
+ expect(() => fontFaces({ baseUrl }), baseUrl).toThrow(/refusing a font URL/)
71
+ }
72
+ })
73
+
74
+ it("names the files @fontsource-variable ships, so a server can copy them as-is", () => {
75
+ for (const [pkg, file] of [
76
+ ["@fontsource-variable/geist", GEIST_FONT_FILES.sans],
77
+ ["@fontsource-variable/geist-mono", GEIST_FONT_FILES.mono],
78
+ ] as const) {
79
+ const shipped = readFileSync(
80
+ new URL(`../../node_modules/${pkg}/files/${file}`, import.meta.url),
81
+ )
82
+ expect(shipped.byteLength, `${pkg}/files/${file}`).toBeGreaterThan(1000)
83
+ }
84
+ })
85
+ })
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Opt-in Geist delivery by URL. The toolkit's font tokens name "Geist" and
3
+ * "Geist Mono" first but load no font: an app either imports
4
+ * `@miragon/mcp-toolkit-ui/fonts.css` (bundled by the build) or serves the
5
+ * woff2 files itself and declares them with {@link fontFaces}. The second path
6
+ * keeps MCP App views small — the files are fetched once and cached instead of
7
+ * inlined as base64 into every view; allow their origin in the view's CSP
8
+ * `resourceDomains`. Where the fonts never load, the host's font renders (if
9
+ * the host sends one, see `adoptHostFontVariables`), else the system stack.
10
+ */
11
+
12
+ /** The latin variable woff2 files, as `@fontsource-variable/geist(-mono)` ship them. */
13
+ export const GEIST_FONT_FILES = {
14
+ sans: "geist-latin-wght-normal.woff2",
15
+ mono: "geist-mono-latin-wght-normal.woff2",
16
+ } as const
17
+
18
+ /** The latin subset both files cover (identical to fontsource's `latin` range). */
19
+ const LATIN_UNICODE_RANGE =
20
+ "U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD"
21
+
22
+ /** Options for {@link fontFaces}. */
23
+ export interface FontFacesOptions {
24
+ /** Where the woff2 files are served from, e.g. `https://mcp.example.com/fonts`. */
25
+ baseUrl: string
26
+ /** File names under `baseUrl`. Defaults to {@link GEIST_FONT_FILES}. */
27
+ files?: { sans?: string; mono?: string }
28
+ /** `font-display` strategy. Default `"swap"`: text renders at once in the fallback. */
29
+ display?: "auto" | "block" | "swap" | "fallback" | "optional"
30
+ }
31
+
32
+ /** Characters that could break out of the CSS `url("…")` the helper writes. */
33
+ const UNSAFE_URL_CHARS = /["'\\()\s<>]/
34
+
35
+ function fontUrl(baseUrl: string, file: string): string {
36
+ const url = `${baseUrl.replace(/\/+$/, "")}/${file.replace(/^\/+/, "")}`
37
+ if (UNSAFE_URL_CHARS.test(url)) {
38
+ throw new Error(`fontFaces: refusing a font URL with quote, bracket or whitespace: ${url}`)
39
+ }
40
+ return url
41
+ }
42
+
43
+ function face(family: string, url: string, display: string): string {
44
+ return [
45
+ "@font-face {",
46
+ ` font-family: "${family}";`,
47
+ " font-style: normal;",
48
+ ` font-display: ${display};`,
49
+ " font-weight: 100 900;",
50
+ ` src: url("${url}") format("woff2");`,
51
+ ` unicode-range: ${LATIN_UNICODE_RANGE};`,
52
+ "}",
53
+ ].join("\n")
54
+ }
55
+
56
+ /**
57
+ * The `@font-face` rules for Geist and Geist Mono (latin, variable weight
58
+ * 100–900) served from `baseUrl`. Pure — inject the string where your app
59
+ * builds its document, e.g. `<style>{fontFaces({ baseUrl })}</style>`.
60
+ *
61
+ * @example
62
+ * fontFaces({ baseUrl: "https://mcp.example.com/fonts" })
63
+ * // @font-face { font-family: "Geist"; … src: url("https://mcp.example.com/fonts/geist-latin-wght-normal.woff2") … }
64
+ */
65
+ export function fontFaces({ baseUrl, files, display = "swap" }: FontFacesOptions): string {
66
+ const sans = files?.sans ?? GEIST_FONT_FILES.sans
67
+ const mono = files?.mono ?? GEIST_FONT_FILES.mono
68
+ return [
69
+ face("Geist", fontUrl(baseUrl, sans), display),
70
+ face("Geist Mono", fontUrl(baseUrl, mono), display),
71
+ ].join("\n")
72
+ }