@miragon/mcp-toolkit-ui 2.6.0 → 2.7.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 (348) hide show
  1. package/README.md +7 -1
  2. package/dist/app/builder/BuilderBanners.d.ts +7 -2
  3. package/dist/app/builder/BuilderBanners.d.ts.map +1 -1
  4. package/dist/app/builder/BuilderBanners.js +8 -3
  5. package/dist/app/builder/BuilderBanners.js.map +1 -1
  6. package/dist/app/builder/CanvasRow.d.ts.map +1 -1
  7. package/dist/app/builder/CanvasRow.js +12 -14
  8. package/dist/app/builder/CanvasRow.js.map +1 -1
  9. package/dist/app/builder/CatalogueSheet.d.ts.map +1 -1
  10. package/dist/app/builder/CatalogueSheet.js +6 -5
  11. package/dist/app/builder/CatalogueSheet.js.map +1 -1
  12. package/dist/app/builder/EmptyHint.d.ts +4 -0
  13. package/dist/app/builder/EmptyHint.d.ts.map +1 -1
  14. package/dist/app/builder/EmptyHint.js +6 -1
  15. package/dist/app/builder/EmptyHint.js.map +1 -1
  16. package/dist/app/builder/LayoutSurface.js +3 -3
  17. package/dist/app/builder/LayoutSurface.js.map +1 -1
  18. package/dist/app/builder/PipelineStrip.d.ts +3 -1
  19. package/dist/app/builder/PipelineStrip.d.ts.map +1 -1
  20. package/dist/app/builder/PipelineStrip.js +11 -6
  21. package/dist/app/builder/PipelineStrip.js.map +1 -1
  22. package/dist/app/builder/SaveDialog.d.ts.map +1 -1
  23. package/dist/app/builder/SaveDialog.js +2 -2
  24. package/dist/app/builder/SaveDialog.js.map +1 -1
  25. package/dist/app/builder/Toolbar.d.ts +3 -1
  26. package/dist/app/builder/Toolbar.d.ts.map +1 -1
  27. package/dist/app/builder/Toolbar.js +9 -3
  28. package/dist/app/builder/Toolbar.js.map +1 -1
  29. package/dist/app/builder/Workspace.js +2 -2
  30. package/dist/app/builder/Workspace.js.map +1 -1
  31. package/dist/app/builder/builder-model.d.ts +3 -2
  32. package/dist/app/builder/builder-model.d.ts.map +1 -1
  33. package/dist/app/builder/builder-model.js +5 -4
  34. package/dist/app/builder/builder-model.js.map +1 -1
  35. package/dist/app/builder/labels.d.ts +13 -62
  36. package/dist/app/builder/labels.d.ts.map +1 -1
  37. package/dist/app/builder/labels.js +9 -58
  38. package/dist/app/builder/labels.js.map +1 -1
  39. package/dist/app/builder/status-chip.d.ts.map +1 -1
  40. package/dist/app/builder/status-chip.js +3 -2
  41. package/dist/app/builder/status-chip.js.map +1 -1
  42. package/dist/app/builder/use-builder-catalogue.d.ts +1 -0
  43. package/dist/app/builder/use-builder-catalogue.d.ts.map +1 -1
  44. package/dist/app/builder/use-builder-catalogue.js +6 -0
  45. package/dist/app/builder/use-builder-catalogue.js.map +1 -1
  46. package/dist/app/builder/use-save-dashboard.d.ts +3 -1
  47. package/dist/app/builder/use-save-dashboard.d.ts.map +1 -1
  48. package/dist/app/builder/use-save-dashboard.js +3 -1
  49. package/dist/app/builder/use-save-dashboard.js.map +1 -1
  50. package/dist/app/index.d.ts +1 -1
  51. package/dist/app/index.d.ts.map +1 -1
  52. package/dist/app/index.js.map +1 -1
  53. package/dist/app/layout-builder.d.ts +7 -2
  54. package/dist/app/layout-builder.d.ts.map +1 -1
  55. package/dist/app/layout-builder.js +17 -5
  56. package/dist/app/layout-builder.js.map +1 -1
  57. package/dist/app/mcp-app-view-chrome.d.ts +94 -20
  58. package/dist/app/mcp-app-view-chrome.d.ts.map +1 -1
  59. package/dist/app/mcp-app-view-chrome.js +109 -40
  60. package/dist/app/mcp-app-view-chrome.js.map +1 -1
  61. package/dist/app/mcp-app-view-states.d.ts +15 -0
  62. package/dist/app/mcp-app-view-states.d.ts.map +1 -0
  63. package/dist/app/mcp-app-view-states.js +34 -0
  64. package/dist/app/mcp-app-view-states.js.map +1 -0
  65. package/dist/app/mcp-app-view.d.ts +40 -5
  66. package/dist/app/mcp-app-view.d.ts.map +1 -1
  67. package/dist/app/mcp-app-view.js +174 -128
  68. package/dist/app/mcp-app-view.js.map +1 -1
  69. package/dist/app/mcp-toolkit-app.d.ts +2 -1
  70. package/dist/app/mcp-toolkit-app.d.ts.map +1 -1
  71. package/dist/app/mcp-toolkit-app.js +25 -3
  72. package/dist/app/mcp-toolkit-app.js.map +1 -1
  73. package/dist/app/use-view-lifecycle.d.ts +38 -0
  74. package/dist/app/use-view-lifecycle.d.ts.map +1 -0
  75. package/dist/app/use-view-lifecycle.js +79 -0
  76. package/dist/app/use-view-lifecycle.js.map +1 -0
  77. package/dist/app/view-lifecycle.d.ts +109 -0
  78. package/dist/app/view-lifecycle.d.ts.map +1 -0
  79. package/dist/app/view-lifecycle.js +120 -0
  80. package/dist/app/view-lifecycle.js.map +1 -0
  81. package/dist/app/widget-props-sheet.d.ts +3 -9
  82. package/dist/app/widget-props-sheet.d.ts.map +1 -1
  83. package/dist/app/widget-props-sheet.js +6 -13
  84. package/dist/app/widget-props-sheet.js.map +1 -1
  85. package/dist/app/widget-renderer.d.ts +41 -1
  86. package/dist/app/widget-renderer.d.ts.map +1 -1
  87. package/dist/app/widget-renderer.js +82 -31
  88. package/dist/app/widget-renderer.js.map +1 -1
  89. package/dist/components/DrillButton.d.ts.map +1 -1
  90. package/dist/components/DrillButton.js +2 -1
  91. package/dist/components/DrillButton.js.map +1 -1
  92. package/dist/components/EmptyState.d.ts +33 -0
  93. package/dist/components/EmptyState.d.ts.map +1 -0
  94. package/dist/components/EmptyState.js +17 -0
  95. package/dist/components/EmptyState.js.map +1 -0
  96. package/dist/components/ErrorState.d.ts +50 -0
  97. package/dist/components/ErrorState.d.ts.map +1 -0
  98. package/dist/components/ErrorState.js +60 -0
  99. package/dist/components/ErrorState.js.map +1 -0
  100. package/dist/components/FilterBar.d.ts +1 -1
  101. package/dist/components/FilterBar.d.ts.map +1 -1
  102. package/dist/components/FilterBar.js +7 -4
  103. package/dist/components/FilterBar.js.map +1 -1
  104. package/dist/components/GroupCard.d.ts.map +1 -1
  105. package/dist/components/GroupCard.js +2 -1
  106. package/dist/components/GroupCard.js.map +1 -1
  107. package/dist/components/KpiGrid.d.ts +10 -3
  108. package/dist/components/KpiGrid.d.ts.map +1 -1
  109. package/dist/components/KpiGrid.js +31 -9
  110. package/dist/components/KpiGrid.js.map +1 -1
  111. package/dist/components/ListFooter.d.ts +3 -2
  112. package/dist/components/ListFooter.d.ts.map +1 -1
  113. package/dist/components/ListFooter.js +14 -4
  114. package/dist/components/ListFooter.js.map +1 -1
  115. package/dist/components/Pills.d.ts +10 -6
  116. package/dist/components/Pills.d.ts.map +1 -1
  117. package/dist/components/Pills.js +12 -6
  118. package/dist/components/Pills.js.map +1 -1
  119. package/dist/components/Toast.d.ts +37 -0
  120. package/dist/components/Toast.d.ts.map +1 -0
  121. package/dist/components/Toast.js +91 -0
  122. package/dist/components/Toast.js.map +1 -0
  123. package/dist/components/WidgetHeader.js +1 -1
  124. package/dist/hooks/use-tool-result-recovery.d.ts +23 -1
  125. package/dist/hooks/use-tool-result-recovery.d.ts.map +1 -1
  126. package/dist/hooks/use-tool-result-recovery.js +21 -7
  127. package/dist/hooks/use-tool-result-recovery.js.map +1 -1
  128. package/dist/hooks/use-view-data.d.ts +6 -0
  129. package/dist/hooks/use-view-data.d.ts.map +1 -1
  130. package/dist/hooks/use-view-data.js +6 -1
  131. package/dist/hooks/use-view-data.js.map +1 -1
  132. package/dist/index.d.ts +12 -5
  133. package/dist/index.d.ts.map +1 -1
  134. package/dist/index.js +14 -4
  135. package/dist/index.js.map +1 -1
  136. package/dist/lib/focus-ring.d.ts +18 -0
  137. package/dist/lib/focus-ring.d.ts.map +1 -0
  138. package/dist/lib/focus-ring.js +18 -0
  139. package/dist/lib/focus-ring.js.map +1 -0
  140. package/dist/lib/icon-stroke.d.ts +12 -0
  141. package/dist/lib/icon-stroke.d.ts.map +1 -0
  142. package/dist/lib/icon-stroke.js +12 -0
  143. package/dist/lib/icon-stroke.js.map +1 -0
  144. package/dist/lib/toast-model.d.ts +46 -0
  145. package/dist/lib/toast-model.d.ts.map +1 -0
  146. package/dist/lib/toast-model.js +36 -0
  147. package/dist/lib/toast-model.js.map +1 -0
  148. package/dist/lib/tone-utils.d.ts +43 -9
  149. package/dist/lib/tone-utils.d.ts.map +1 -1
  150. package/dist/lib/tone-utils.js +36 -7
  151. package/dist/lib/tone-utils.js.map +1 -1
  152. package/dist/locale/index.d.ts +3 -1
  153. package/dist/locale/index.d.ts.map +1 -1
  154. package/dist/locale/index.js +2 -1
  155. package/dist/locale/index.js.map +1 -1
  156. package/dist/locale/locale-provider.d.ts +42 -1
  157. package/dist/locale/locale-provider.d.ts.map +1 -1
  158. package/dist/locale/locale-provider.js +44 -3
  159. package/dist/locale/locale-provider.js.map +1 -1
  160. package/dist/locale/message-types.d.ts +223 -0
  161. package/dist/locale/message-types.d.ts.map +1 -0
  162. package/dist/locale/message-types.js +7 -0
  163. package/dist/locale/message-types.js.map +1 -0
  164. package/dist/locale/messages-de.d.ts +9 -0
  165. package/dist/locale/messages-de.d.ts.map +1 -0
  166. package/dist/locale/messages-de.js +151 -0
  167. package/dist/locale/messages-de.js.map +1 -0
  168. package/dist/locale/messages-en.d.ts +8 -0
  169. package/dist/locale/messages-en.d.ts.map +1 -0
  170. package/dist/locale/messages-en.js +150 -0
  171. package/dist/locale/messages-en.js.map +1 -0
  172. package/dist/locale/toolkit-messages.d.ts +39 -0
  173. package/dist/locale/toolkit-messages.d.ts.map +1 -0
  174. package/dist/locale/toolkit-messages.js +50 -0
  175. package/dist/locale/toolkit-messages.js.map +1 -0
  176. package/dist/primitives/alert.d.ts.map +1 -1
  177. package/dist/primitives/alert.js +4 -2
  178. package/dist/primitives/alert.js.map +1 -1
  179. package/dist/primitives/badge.d.ts +1 -1
  180. package/dist/primitives/badge.d.ts.map +1 -1
  181. package/dist/primitives/badge.js +14 -6
  182. package/dist/primitives/badge.js.map +1 -1
  183. package/dist/primitives/button.d.ts.map +1 -1
  184. package/dist/primitives/button.js +12 -8
  185. package/dist/primitives/button.js.map +1 -1
  186. package/dist/primitives/card.js +4 -4
  187. package/dist/primitives/card.js.map +1 -1
  188. package/dist/primitives/dialog.d.ts +7 -3
  189. package/dist/primitives/dialog.d.ts.map +1 -1
  190. package/dist/primitives/dialog.js +14 -7
  191. package/dist/primitives/dialog.js.map +1 -1
  192. package/dist/primitives/input.d.ts.map +1 -1
  193. package/dist/primitives/input.js +4 -1
  194. package/dist/primitives/input.js.map +1 -1
  195. package/dist/primitives/scroll-area.js +1 -1
  196. package/dist/primitives/scroll-area.js.map +1 -1
  197. package/dist/primitives/select.d.ts.map +1 -1
  198. package/dist/primitives/select.js +5 -2
  199. package/dist/primitives/select.js.map +1 -1
  200. package/dist/primitives/sheet.d.ts +3 -1
  201. package/dist/primitives/sheet.d.ts.map +1 -1
  202. package/dist/primitives/sheet.js +12 -6
  203. package/dist/primitives/sheet.js.map +1 -1
  204. package/dist/primitives/skeleton.js +1 -1
  205. package/dist/primitives/skeleton.js.map +1 -1
  206. package/dist/primitives/switch.d.ts.map +1 -1
  207. package/dist/primitives/switch.js +2 -1
  208. package/dist/primitives/switch.js.map +1 -1
  209. package/dist/primitives/tabs.d.ts.map +1 -1
  210. package/dist/primitives/tabs.js +5 -3
  211. package/dist/primitives/tabs.js.map +1 -1
  212. package/dist/render-cases.d.ts.map +1 -1
  213. package/dist/render-cases.js +20 -2
  214. package/dist/render-cases.js.map +1 -1
  215. package/dist/render-scan.d.ts +25 -0
  216. package/dist/render-scan.d.ts.map +1 -0
  217. package/dist/render-scan.js +66 -0
  218. package/dist/render-scan.js.map +1 -0
  219. package/dist/theme/cd-tokens.d.ts +66 -0
  220. package/dist/theme/cd-tokens.d.ts.map +1 -0
  221. package/dist/theme/cd-tokens.js +64 -0
  222. package/dist/theme/cd-tokens.js.map +1 -0
  223. package/dist/theme/create-theme.d.ts +196 -39
  224. package/dist/theme/create-theme.d.ts.map +1 -1
  225. package/dist/theme/create-theme.js +189 -13
  226. package/dist/theme/create-theme.js.map +1 -1
  227. package/dist/theme/fonts.d.ts +38 -0
  228. package/dist/theme/fonts.d.ts.map +1 -0
  229. package/dist/theme/fonts.js +56 -0
  230. package/dist/theme/fonts.js.map +1 -0
  231. package/dist/theme/host-fonts.d.ts +39 -0
  232. package/dist/theme/host-fonts.d.ts.map +1 -0
  233. package/dist/theme/host-fonts.js +44 -0
  234. package/dist/theme/host-fonts.js.map +1 -0
  235. package/dist/theme/index.d.ts +8 -1
  236. package/dist/theme/index.d.ts.map +1 -1
  237. package/dist/theme/index.js +5 -1
  238. package/dist/theme/index.js.map +1 -1
  239. package/dist/theme/portal-container.d.ts +16 -0
  240. package/dist/theme/portal-container.d.ts.map +1 -0
  241. package/dist/theme/portal-container.js +19 -0
  242. package/dist/theme/portal-container.js.map +1 -0
  243. package/dist/theme/presets.d.ts +24 -9
  244. package/dist/theme/presets.d.ts.map +1 -1
  245. package/dist/theme/presets.js +44 -11
  246. package/dist/theme/presets.js.map +1 -1
  247. package/dist/theme/theme-provider.d.ts +27 -9
  248. package/dist/theme/theme-provider.d.ts.map +1 -1
  249. package/dist/theme/theme-provider.js +42 -20
  250. package/dist/theme/theme-provider.js.map +1 -1
  251. package/package.json +9 -4
  252. package/src/app/builder/BuilderBanners.tsx +26 -13
  253. package/src/app/builder/CanvasRow.tsx +22 -21
  254. package/src/app/builder/CatalogueSheet.tsx +16 -14
  255. package/src/app/builder/EmptyHint.tsx +16 -11
  256. package/src/app/builder/LayoutSurface.tsx +7 -7
  257. package/src/app/builder/PipelineStrip.tsx +26 -17
  258. package/src/app/builder/SaveDialog.tsx +11 -12
  259. package/src/app/builder/Toolbar.tsx +19 -7
  260. package/src/app/builder/Workspace.tsx +5 -3
  261. package/src/app/builder/builder-feedback.test.tsx +75 -0
  262. package/src/app/builder/builder-model.test.ts +5 -0
  263. package/src/app/builder/builder-model.ts +5 -4
  264. package/src/app/builder/labels.ts +15 -121
  265. package/src/app/builder/status-chip.tsx +8 -4
  266. package/src/app/builder/use-builder-catalogue.ts +6 -0
  267. package/src/app/builder/use-save-dashboard.ts +5 -0
  268. package/src/app/index.ts +8 -1
  269. package/src/app/layout-builder.tsx +23 -3
  270. package/src/app/mcp-app-view-chrome.test.tsx +347 -14
  271. package/src/app/mcp-app-view-chrome.tsx +278 -100
  272. package/src/app/mcp-app-view-states.tsx +100 -0
  273. package/src/app/mcp-app-view.test.tsx +258 -0
  274. package/src/app/mcp-app-view.tsx +292 -185
  275. package/src/app/mcp-toolkit-app.tsx +31 -7
  276. package/src/app/use-view-lifecycle.ts +116 -0
  277. package/src/app/view-lifecycle.test.ts +227 -0
  278. package/src/app/view-lifecycle.ts +174 -0
  279. package/src/app/widget-props-sheet.tsx +13 -26
  280. package/src/app/widget-renderer.render.test.tsx +131 -0
  281. package/src/app/widget-renderer.tsx +136 -38
  282. package/src/components/DrillButton.tsx +2 -1
  283. package/src/components/EmptyState.tsx +81 -0
  284. package/src/components/ErrorState.tsx +182 -0
  285. package/src/components/FilterBar.tsx +9 -6
  286. package/src/components/GroupCard.tsx +3 -2
  287. package/src/components/KpiGrid.tsx +75 -44
  288. package/src/components/ListFooter.tsx +17 -6
  289. package/src/components/Pills.tsx +20 -11
  290. package/src/components/Toast.tsx +188 -0
  291. package/src/components/WidgetHeader.tsx +1 -1
  292. package/src/components/feedback-states.test.tsx +225 -0
  293. package/src/components/tone-figures.test.tsx +54 -0
  294. package/src/fonts.css +19 -0
  295. package/src/globals.css +9 -165
  296. package/src/hooks/use-tool-result-recovery.test.ts +25 -1
  297. package/src/hooks/use-tool-result-recovery.ts +34 -6
  298. package/src/hooks/use-view-data.test.tsx +60 -0
  299. package/src/hooks/use-view-data.ts +12 -1
  300. package/src/i18n-coverage.test.tsx +323 -0
  301. package/src/index.ts +57 -5
  302. package/src/lib/focus-ring.ts +20 -0
  303. package/src/lib/icon-stroke.test.ts +59 -0
  304. package/src/lib/icon-stroke.ts +13 -0
  305. package/src/lib/toast-model.test.ts +68 -0
  306. package/src/lib/toast-model.ts +63 -0
  307. package/src/lib/tone-utils.test.ts +47 -14
  308. package/src/lib/tone-utils.ts +61 -13
  309. package/src/locale/index.ts +13 -1
  310. package/src/locale/locale-provider.test.tsx +87 -0
  311. package/src/locale/locale-provider.tsx +90 -3
  312. package/src/locale/message-types.ts +221 -0
  313. package/src/locale/messages-de.ts +159 -0
  314. package/src/locale/messages-en.ts +157 -0
  315. package/src/locale/toolkit-messages.test.ts +216 -0
  316. package/src/locale/toolkit-messages.ts +82 -0
  317. package/src/no-hardcoded-copy.test.ts +432 -0
  318. package/src/primitives/alert.tsx +4 -2
  319. package/src/primitives/badge.tsx +14 -7
  320. package/src/primitives/button.tsx +13 -8
  321. package/src/primitives/card.tsx +4 -4
  322. package/src/primitives/dialog.tsx +32 -7
  323. package/src/primitives/input.tsx +5 -3
  324. package/src/primitives/scroll-area.tsx +1 -1
  325. package/src/primitives/select.tsx +6 -3
  326. package/src/primitives/sheet.tsx +26 -6
  327. package/src/primitives/skeleton.tsx +1 -1
  328. package/src/primitives/switch.tsx +4 -2
  329. package/src/primitives/tabs.tsx +9 -6
  330. package/src/render-cases.tsx +45 -1
  331. package/src/render-scan.ts +75 -0
  332. package/src/theme/cd-tokens.generated.css +48 -0
  333. package/src/theme/cd-tokens.test.ts +107 -0
  334. package/src/theme/cd-tokens.ts +67 -0
  335. package/src/theme/create-theme.test.ts +243 -5
  336. package/src/theme/create-theme.ts +313 -41
  337. package/src/theme/fonts.test.ts +85 -0
  338. package/src/theme/fonts.ts +72 -0
  339. package/src/theme/host-fonts.test.ts +135 -0
  340. package/src/theme/host-fonts.ts +52 -0
  341. package/src/theme/index.ts +15 -1
  342. package/src/theme/portal-container.ts +20 -0
  343. package/src/theme/presets.ts +44 -11
  344. package/src/theme/theme-css.test.ts +568 -0
  345. package/src/theme/theme-provider.test.tsx +82 -0
  346. package/src/theme/theme-provider.tsx +60 -24
  347. package/src/theme.css +314 -0
  348. package/ui-catalog.json +344 -17
@@ -0,0 +1,135 @@
1
+ /// <reference types="node" />
2
+ import { readFileSync } from "node:fs"
3
+ import { describe, expect, it } from "vitest"
4
+ import {
5
+ adoptHostFontVariables,
6
+ HOST_FONT_VARIABLES,
7
+ type InlineStyleTarget,
8
+ } from "./host-fonts.js"
9
+
10
+ /** `element.style` as a map: the inline custom properties of one element. */
11
+ function inlineStyle(initial: Record<string, string> = {}): InlineStyleTarget & {
12
+ props: Map<string, string>
13
+ } {
14
+ const props = new Map(Object.entries(initial))
15
+ return {
16
+ props,
17
+ getPropertyValue: (name) => props.get(name) ?? "",
18
+ setProperty: (name, value) => void props.set(name, value),
19
+ removeProperty: (name) => {
20
+ const old = props.get(name) ?? ""
21
+ props.delete(name)
22
+ return old
23
+ },
24
+ }
25
+ }
26
+
27
+ /** What mcp-use's ThemeProvider does with the host's style variables (ext-apps applyHostStyleVariables). */
28
+ function applyHostStyleVariables(
29
+ style: InlineStyleTarget,
30
+ variables: Record<string, string | undefined>,
31
+ ): void {
32
+ for (const [name, value] of Object.entries(variables)) {
33
+ if (value !== undefined) style.setProperty(name, value)
34
+ }
35
+ }
36
+
37
+ const rootRole = (name: string): string => {
38
+ const css = readFileSync(new URL("../theme.css", import.meta.url), "utf8").replace(
39
+ /\/\*[\s\S]*?\*\//g,
40
+ "",
41
+ )
42
+ const root = /(?:^|\})\s*:root\s*\{([^{}]*)\}/.exec(css)?.[1] ?? ""
43
+ const decl = new RegExp(`${name}:([^;]+);`).exec(root)?.[1]
44
+ if (!decl) throw new Error(`theme.css :root has no ${name}`)
45
+ return decl.replace(/\s+/g, " ").trim()
46
+ }
47
+
48
+ /**
49
+ * The font stack <html> renders with: an inline value of the role wins over
50
+ * theme.css; otherwise the `:root` stack with `var(--host-font-*, fallback)`
51
+ * resolved against the inline style.
52
+ */
53
+ function renderedStack(style: InlineStyleTarget, role: "--font-sans" | "--font-mono"): string {
54
+ const inline = style.getPropertyValue(role)
55
+ if (inline) return inline
56
+ return rootRole(role).replace(
57
+ /var\((--host-font-[a-z]+), ([^)]+)\)/,
58
+ (_all, slot: string, fallback: string) => style.getPropertyValue(slot) || fallback,
59
+ )
60
+ }
61
+
62
+ describe("adoptHostFontVariables (host font → fallback slot)", () => {
63
+ const HOST = { "--font-sans": '"Anthropic Sans", system-ui', "--font-mono": '"Host Mono"' }
64
+
65
+ it("keeps Geist first when a SEP-1865 host sends --font-sans/--font-mono", () => {
66
+ const html = inlineStyle()
67
+ applyHostStyleVariables(html, HOST)
68
+ // Without the toolkit's step the host replaces the whole stack:
69
+ expect(renderedStack(html, "--font-sans")).toBe('"Anthropic Sans", system-ui')
70
+ adoptHostFontVariables(html, HOST)
71
+ expect(renderedStack(html, "--font-sans")).toBe(
72
+ '"Geist", "Geist Variable", "Anthropic Sans", system-ui, system-ui, sans-serif',
73
+ )
74
+ expect(renderedStack(html, "--font-mono")).toBe(
75
+ '"Geist Mono", "Geist Mono Variable", "Host Mono", SFMono-Regular, Menlo, Consolas, monospace',
76
+ )
77
+ })
78
+
79
+ it("moves the host's values into the --host-font-* slots and clears the roles", () => {
80
+ const html = inlineStyle()
81
+ applyHostStyleVariables(html, HOST)
82
+ adoptHostFontVariables(html, HOST)
83
+ expect(Object.fromEntries(html.props)).toEqual({
84
+ "--host-font-sans": '"Anthropic Sans", system-ui',
85
+ "--host-font-mono": '"Host Mono"',
86
+ })
87
+ })
88
+
89
+ it("leaves an app's own inline brand font on the role alone", () => {
90
+ const html = inlineStyle({ "--font-sans": "Inter, sans-serif" })
91
+ adoptHostFontVariables(html, { "--font-sans": "HostSans" })
92
+ expect(html.props.get("--font-sans")).toBe("Inter, sans-serif")
93
+ expect(html.props.get("--host-font-sans")).toBe("HostSans")
94
+ })
95
+
96
+ it("compares the host's value and the inline role without surrounding whitespace", () => {
97
+ const html = inlineStyle({ "--font-sans": " HostSans " })
98
+ adoptHostFontVariables(html, { "--font-sans": "HostSans\n" })
99
+ expect(Object.fromEntries(html.props)).toEqual({ "--host-font-sans": "HostSans" })
100
+ })
101
+
102
+ it("is a no-op without host variables or with empty font values", () => {
103
+ const html = inlineStyle({ "--font-sans": "X" })
104
+ adoptHostFontVariables(html, undefined)
105
+ adoptHostFontVariables(html, null)
106
+ adoptHostFontVariables(html, { "--font-sans": " ", "--font-mono": undefined })
107
+ expect(Object.fromEntries(html.props)).toEqual({ "--font-sans": "X" })
108
+ expect(renderedStack(inlineStyle(), "--font-sans")).toBe(
109
+ '"Geist", "Geist Variable", ui-sans-serif, system-ui, sans-serif',
110
+ )
111
+ })
112
+
113
+ it("is idempotent (mcp-use re-applies on every host-context update)", () => {
114
+ const html = inlineStyle()
115
+ for (let i = 0; i < 2; i++) {
116
+ applyHostStyleVariables(html, HOST)
117
+ adoptHostFontVariables(html, HOST)
118
+ }
119
+ expect(html.props.has("--font-sans")).toBe(false)
120
+ expect(html.props.get("--host-font-sans")).toBe('"Anthropic Sans", system-ui')
121
+ })
122
+
123
+ it("names exactly the two SEP-1865 font variables", () => {
124
+ expect(HOST_FONT_VARIABLES).toEqual({
125
+ "--font-sans": "--host-font-sans",
126
+ "--font-mono": "--host-font-mono",
127
+ })
128
+ })
129
+
130
+ it("is wired ABOVE mcp-use's ThemeProvider in McpToolkitApp (its layout effect runs after)", () => {
131
+ const app = readFileSync(new URL("../app/mcp-toolkit-app.tsx", import.meta.url), "utf8")
132
+ expect(app).toMatch(/adoptHostFontVariables\(document\.documentElement\.style,/)
133
+ expect(app).toMatch(/<HostFontRoles>\s*<ThemeProvider>/)
134
+ })
135
+ })
@@ -0,0 +1,52 @@
1
+ /**
2
+ * The host's font, demoted to the fallback slot of the font roles.
3
+ *
4
+ * SEP-1865 hosts send their fonts as the style variables `--font-sans` and
5
+ * `--font-mono`, the same names as the toolkit's font roles. mcp-use writes
6
+ * them inline on `<html>`, where they beat the role stacks in `theme.css` and
7
+ * replace them whole, so Geist never renders even when it is loaded. The
8
+ * toolkit's rule is Geist first, the host's font next, the system stack last:
9
+ * {@link adoptHostFontVariables} moves the host's value to
10
+ * `--host-font-sans` / `--host-font-mono`, which the role stacks name right
11
+ * after Geist. `McpToolkitApp` runs it after every host-context update; an app
12
+ * with its own document layer calls it after it applied the host variables.
13
+ */
14
+
15
+ /** Each host font variable (SEP-1865 name) → where the toolkit keeps it. */
16
+ export const HOST_FONT_VARIABLES = {
17
+ "--font-sans": "--host-font-sans",
18
+ "--font-mono": "--host-font-mono",
19
+ } as const
20
+
21
+ /** The slice of `CSSStyleDeclaration` the helper touches (`element.style`). */
22
+ export interface InlineStyleTarget {
23
+ getPropertyValue(property: string): string
24
+ setProperty(property: string, value: string): void
25
+ removeProperty(property: string): string
26
+ }
27
+
28
+ /**
29
+ * Move the host's font variables off the role names on `style` (normally
30
+ * `document.documentElement.style`, where the host variables were applied):
31
+ * each non-empty `--font-sans` / `--font-mono` in `hostVariables` is written
32
+ * to its {@link HOST_FONT_VARIABLES} slot, and the role is cleared when it
33
+ * still holds exactly the host's value — an app's own inline brand font
34
+ * stays. Idempotent; a no-op without host variables.
35
+ *
36
+ * @example
37
+ * applyHostStyleVariables(vars) // the host layer writes --font-sans on <html>
38
+ * adoptHostFontVariables(document.documentElement.style, vars)
39
+ * // --font-sans: "Geist", "Geist Variable", <host font>, system-ui, sans-serif
40
+ */
41
+ export function adoptHostFontVariables(
42
+ style: InlineStyleTarget,
43
+ hostVariables: Readonly<Record<string, string | undefined>> | null | undefined,
44
+ ): void {
45
+ if (!hostVariables) return
46
+ for (const [role, slot] of Object.entries(HOST_FONT_VARIABLES)) {
47
+ const value = hostVariables[role]?.trim()
48
+ if (!value) continue
49
+ style.setProperty(slot, value)
50
+ if (style.getPropertyValue(role).trim() === value) style.removeProperty(role)
51
+ }
52
+ }
@@ -1,9 +1,23 @@
1
1
  // White-label theming. Browser-safe (React + DOM only, no `mcp-use/react`), so
2
2
  // it is re-exported from the root barrel — a consumer can theme the toolkit's
3
3
  // primitives without pulling in the host runtime.
4
- export { createTheme, tokensToVars, TOKEN_TO_CSS_VAR } from "./create-theme.js"
4
+ export {
5
+ createTheme,
6
+ tokensToVars,
7
+ resolveThemeVars,
8
+ TOKEN_TO_CSS_VAR,
9
+ ROLE_TOKENS,
10
+ MODE_INVARIANT_VARS,
11
+ } from "./create-theme.js"
5
12
  export type { ThemeTokens, ThemeDefinition, ThemeVars, CreateThemeOptions } from "./create-theme.js"
6
13
  export { ThemeProvider, useTheme } from "./theme-provider.js"
7
14
  export type { ThemeMode, ThemeContextValue, ThemeProviderProps } from "./theme-provider.js"
15
+ export { usePortalContainer } from "./portal-container.js"
8
16
  export { themePresets } from "./presets.js"
9
17
  export type { ThemePresetId } from "./presets.js"
18
+ export { CD_TOKENS, CD_TOKENS_VERSION } from "./cd-tokens.js"
19
+ export type { CdTokenName } from "./cd-tokens.js"
20
+ export { fontFaces, GEIST_FONT_FILES } from "./fonts.js"
21
+ export type { FontFacesOptions } from "./fonts.js"
22
+ export { adoptHostFontVariables, HOST_FONT_VARIABLES } from "./host-fonts.js"
23
+ export type { InlineStyleTarget } from "./host-fonts.js"
@@ -0,0 +1,20 @@
1
+ import { createContext, useContext } from "react"
2
+
3
+ /**
4
+ * The element portaled overlays (Dialog, Sheet, Select) render into. A
5
+ * {@link ThemeProvider} provides a node INSIDE its scope, so overlays opened
6
+ * from a themed subtree keep the theme's variables and its light/dark mode
7
+ * instead of falling back to `document.body` and the `:root` defaults
8
+ * (mcp-toolkit#178 N36). `null` — no provider, or before it mounted — means
9
+ * the overlay library's default (`document.body`).
10
+ */
11
+ export const PortalContainerContext = createContext<HTMLElement | null>(null)
12
+
13
+ /**
14
+ * The portal container of the nearest {@link ThemeProvider}, or `null`. Pass it
15
+ * as `container` to a portal of your own so it stays inside the theme scope;
16
+ * the toolkit's Dialog, Sheet and Select already do.
17
+ */
18
+ export function usePortalContainer(): HTMLElement | null {
19
+ return useContext(PortalContainerContext)
20
+ }
@@ -2,23 +2,35 @@ import { createTheme, type ThemeDefinition } from "./create-theme.js"
2
2
 
3
3
  /**
4
4
  * Ready-made white-label themes — a demonstration of {@link createTheme} and a
5
- * starting point a client can fork. Each preset sets a distinct brand `primary`,
6
- * a matching `ring`/`accent`, and a `radius`, plus a `dark` override that
7
- * brightens the brand colour for legibility on dark surfaces (mirroring how
8
- * `globals.css` brightens tokens under `.dark`).
5
+ * starting point a client can fork. Each preset sets its whole interactive
6
+ * family from one brand hue, in light AND dark: `primary` with its
7
+ * `primaryForeground` and `primaryHover`, `link`, `focus` (plus the legacy
8
+ * `ring`), a matching `accent`, and a `radius`. The dark side brightens the
9
+ * brand colour for legibility on dark surfaces. Every preset meets the same
10
+ * contrast bar as the default theme in both modes (`theme-css.test.ts`).
9
11
  *
10
- * Colours are authored in `oklch` to stay consistent with the toolkit's token
11
- * palette in `globals.css`. Apply one with `<ThemeProvider theme={themePresets.violet}>`.
12
+ * The toolkit's DEFAULT is the Miragon theme (theme.css, built on the vendored
13
+ * corporate-design tokens); it needs no preset. A full customer brand may set
14
+ * more roles than these presets do (the status `*Ink` colours); see
15
+ * docs/guides/white-labeling.md. Apply one with
16
+ * `<ThemeProvider theme={themePresets.emerald}>`.
12
17
  */
13
18
  export const themePresets = {
14
19
  /**
15
- * The toolkit's own brand: a confident indigo-blue with a slightly tighter
16
- * radius. A sensible default when a client has no brand of their own yet.
20
+ * An approximation of the Miragon blue from before the toolkit shipped the
21
+ * Miragon corporate design.
22
+ *
23
+ * @deprecated The Miragon theme is now the toolkit's default: render without
24
+ * a theme (or `<ThemeProvider>` without `theme`) to get the exact CI values.
25
+ * This preset is off-palette and will be removed in the next major.
17
26
  */
18
27
  miragon: createTheme(
19
28
  {
20
29
  primary: "oklch(0.52 0.21 264)",
21
30
  primaryForeground: "oklch(0.985 0 0)",
31
+ primaryHover: "oklch(0.46 0.2 264)",
32
+ link: "oklch(0.52 0.21 264)",
33
+ focus: "oklch(0.52 0.21 264)",
22
34
  accent: "oklch(0.95 0.03 264)",
23
35
  accentForeground: "oklch(0.32 0.12 264)",
24
36
  ring: "oklch(0.52 0.21 264)",
@@ -28,6 +40,9 @@ export const themePresets = {
28
40
  dark: {
29
41
  primary: "oklch(0.7 0.16 264)",
30
42
  primaryForeground: "oklch(0.18 0.02 264)",
43
+ primaryHover: "oklch(0.76 0.13 264)",
44
+ link: "oklch(0.7 0.16 264)",
45
+ focus: "oklch(0.7 0.16 264)",
31
46
  accent: "oklch(0.32 0.06 264)",
32
47
  accentForeground: "oklch(0.97 0 0)",
33
48
  ring: "oklch(0.7 0.16 264)",
@@ -35,11 +50,20 @@ export const themePresets = {
35
50
  },
36
51
  ),
37
52
 
38
- /** A vivid violet brand with rounder corners — playful, consumer-facing. */
53
+ /**
54
+ * A vivid violet brand with rounder corners — playful, consumer-facing.
55
+ *
56
+ * @deprecated The violet tech default is the look the Miragon design system
57
+ * rules out (anti-slop U9). Fork {@link createTheme} with your own brand
58
+ * values instead. Will be removed in the next major.
59
+ */
39
60
  violet: createTheme(
40
61
  {
41
62
  primary: "oklch(0.55 0.24 295)",
42
63
  primaryForeground: "oklch(0.985 0 0)",
64
+ primaryHover: "oklch(0.49 0.23 295)",
65
+ link: "oklch(0.55 0.24 295)",
66
+ focus: "oklch(0.55 0.24 295)",
43
67
  accent: "oklch(0.95 0.04 295)",
44
68
  accentForeground: "oklch(0.34 0.14 295)",
45
69
  ring: "oklch(0.55 0.24 295)",
@@ -49,6 +73,9 @@ export const themePresets = {
49
73
  dark: {
50
74
  primary: "oklch(0.72 0.18 295)",
51
75
  primaryForeground: "oklch(0.18 0.03 295)",
76
+ primaryHover: "oklch(0.78 0.15 295)",
77
+ link: "oklch(0.72 0.18 295)",
78
+ focus: "oklch(0.72 0.18 295)",
52
79
  accent: "oklch(0.33 0.08 295)",
53
80
  accentForeground: "oklch(0.97 0 0)",
54
81
  ring: "oklch(0.72 0.18 295)",
@@ -59,17 +86,23 @@ export const themePresets = {
59
86
  /** A grounded emerald brand with a crisp, near-square radius — enterprise. */
60
87
  emerald: createTheme(
61
88
  {
62
- primary: "oklch(0.58 0.15 162)",
89
+ primary: "oklch(0.5 0.12 162)",
63
90
  primaryForeground: "oklch(0.985 0 0)",
91
+ primaryHover: "oklch(0.44 0.11 162)",
92
+ link: "oklch(0.5 0.12 162)",
93
+ focus: "oklch(0.5 0.12 162)",
64
94
  accent: "oklch(0.95 0.04 162)",
65
95
  accentForeground: "oklch(0.34 0.1 162)",
66
- ring: "oklch(0.58 0.15 162)",
96
+ ring: "oklch(0.5 0.12 162)",
67
97
  radius: "0.375rem",
68
98
  },
69
99
  {
70
100
  dark: {
71
101
  primary: "oklch(0.72 0.15 162)",
72
102
  primaryForeground: "oklch(0.17 0.03 162)",
103
+ primaryHover: "oklch(0.78 0.14 162)",
104
+ link: "oklch(0.72 0.15 162)",
105
+ focus: "oklch(0.72 0.15 162)",
73
106
  accent: "oklch(0.32 0.07 162)",
74
107
  accentForeground: "oklch(0.97 0 0)",
75
108
  ring: "oklch(0.72 0.15 162)",