@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
@@ -1,23 +1,39 @@
1
- import { useState, useCallback } from "react"
1
+ import { useState, useCallback, useMemo, type ReactNode } from "react"
2
+ import { LucideProvider } from "lucide-react"
2
3
  import { ToolCancelledError, useDisplayMode, useHostContext, useToolContext } from "mcp-use/react"
3
- import type { LayoutConfig, PipelineStepRef } from "@miragon/mcp-toolkit-core"
4
+ import { ToastProvider, useToast } from "../components/Toast.js"
5
+ import { ToolkitLocaleFallback, useToolkitMessages } from "../locale/locale-provider.js"
6
+ import type { ViewMessages } from "../locale/toolkit-messages.js"
4
7
  import { AppQueryProvider, queryClient } from "../providers/query-provider.js"
5
- import { useToolResultRecovery } from "../hooks/use-tool-result-recovery.js"
6
- import { parseToolResult } from "../lib/parse-tool-result.js"
8
+ import {
9
+ resolveRecoveryToolName,
10
+ useToolResultRecovery,
11
+ } from "../hooks/use-tool-result-recovery.js"
12
+ import { ICON_STROKE } from "../lib/icon-stroke.js"
7
13
  import { useHostBridge } from "./host-bridge.js"
8
14
  import {
9
- DEFAULT_LABELS,
15
+ applyBuilderCommit,
16
+ fullscreenAvailable,
10
17
  isCompleteViewData,
11
18
  StepErrors,
12
19
  ViewBody,
13
- ViewPlaceholder,
14
20
  ViewToolbar,
21
+ type BuilderCommit,
22
+ type DescribeStep,
15
23
  type McpAppViewLabels,
16
24
  type ViewData,
25
+ type ViewTitleLevel,
26
+ type ViewTitleMode,
17
27
  } from "./mcp-app-view-chrome.js"
28
+ import { ViewPlaceholder, type DescribeViewError } from "./mcp-app-view-states.js"
29
+ import type { LayoutBuilderLabels } from "./layout-builder.js"
30
+ import { useRetryFocus, useViewLifecycle } from "./use-view-lifecycle.js"
31
+ import { DEFAULT_SLOW_HINT_MS, decodeViewResult } from "./view-lifecycle.js"
18
32
  import type { WidgetComponent } from "./widget-renderer.js"
19
33
 
20
- export type { McpAppViewLabels } from "./mcp-app-view-chrome.js"
34
+ export type { McpAppViewLabels, ViewTitleLevel, ViewTitleMode } from "./mcp-app-view-chrome.js"
35
+ export type { DescribeStep } from "./mcp-app-view-chrome.js"
36
+ export type { DescribeViewError } from "./mcp-app-view-states.js"
21
37
 
22
38
  export interface McpAppViewProps {
23
39
  /**
@@ -46,222 +62,313 @@ export interface McpAppViewProps {
46
62
  */
47
63
  builderEnabled?: boolean
48
64
  /**
49
- * Override UI strings (loading, refresh, fullscreen, build).
50
- * Defaults to English.
65
+ * Override chrome strings (loading, error, refresh, fullscreen, edit, …).
66
+ * Defaults to the active locale's toolkit catalog: the nearest
67
+ * `LocaleProvider`, else the host's locale (`en`/`de`, English otherwise).
51
68
  */
52
69
  labels?: McpAppViewLabels
70
+ /** Override strings of the embedded view builder (defaults: the locale's catalog). */
71
+ builderLabels?: LayoutBuilderLabels
72
+ /**
73
+ * How the toolbar shows the view title, so a view carries ONE title:
74
+ * `"show"` (default) renders it as a heading, `"hide"` leaves the title to
75
+ * the widget's own header, `"slot"` renders {@link McpAppViewProps.renderTitle}.
76
+ */
77
+ titleMode?: ViewTitleMode
78
+ /** Heading level of the toolbar title in `"show"` mode. Default `2`. */
79
+ titleLevel?: ViewTitleLevel
80
+ /** Renders the toolbar title in `"slot"` mode; receives the view's title. */
81
+ renderTitle?: (title: string | undefined) => ReactNode
82
+ /** Delay before the loading placeholder adds its "taking longer" hint. Default 8000 ms. */
83
+ slowHintAfterMs?: number
84
+ /**
85
+ * Map a raw rendering error (the tool's `isError` text) onto human text.
86
+ * The raw reason always stays available under "Details".
87
+ */
88
+ describeError?: DescribeViewError
89
+ /** Human title of a pipeline step in the failed-steps alert (default: the step id). */
90
+ describeStep?: DescribeStep
53
91
  }
54
92
 
55
- export function McpAppView({
56
- widgets,
57
- refreshToolName = "refresh-view",
58
- builderEnabled,
59
- labels,
60
- }: McpAppViewProps) {
61
- const effectiveLabels = { ...DEFAULT_LABELS, ...labels }
62
- // Since mcp-use 2.x the shell reads the view channels through the dedicated
63
- // hooks (all require the surrounding `bootstrapView` mount that
64
- // `mountMcpToolkitApp` provides) and calls tools through the HostBridge that
65
- // `McpUseHostBridgeProvider` installs.
66
- const tool = useToolContext()
67
- const { displayMode, requestDisplayMode } = useDisplayMode()
68
- const { safeArea, hostContext } = useHostContext()
69
- const bridge = useHostBridge()
70
- const callTool = useCallback(
71
- (name: string, args: Record<string, unknown>) => bridge.callTool(name, args),
72
- [bridge],
93
+ /**
94
+ * The toolkit's MCP App view: lifecycle placeholder (waiting / error /
95
+ * cancelled), toolbar, failed-steps alert and the rendered layout (or the
96
+ * builder). Mounts a `ToastProvider` and binds the toolkit's own strings to
97
+ * the host's locale unless a `LocaleProvider` sets one, so the chrome follows
98
+ * the host language; `useLocale()` in consumer widgets is unaffected. Lucide
99
+ * icons draw at the CI stroke (2.5) throughout, the toast stack included.
100
+ */
101
+ export function McpAppView(props: McpAppViewProps) {
102
+ const { locale } = useHostContext()
103
+ return (
104
+ <ToolkitLocaleFallback locale={locale}>
105
+ {/* Wraps the ToastProvider, whose toast stack renders beside the shell. */}
106
+ <LucideProvider strokeWidth={ICON_STROKE}>
107
+ <ToastProvider>
108
+ <McpAppViewShell {...props} />
109
+ </ToastProvider>
110
+ </LucideProvider>
111
+ </ToolkitLocaleFallback>
73
112
  )
113
+ }
74
114
 
75
- const isPending = tool.status === "pending"
76
- // Since mcp-use 2.7.1 a host cancellation of the rendering call latches as a
77
- // terminal `status: "error"` — show it instead of the skeleton, and never
78
- // let the recovery below re-run the call the user stopped.
79
- const isCancelled = tool.status === "error" && tool.error instanceof ToolCancelledError
80
- const initialViewData = tool.status === "ready" ? (tool.toolOutput as ViewData) : undefined
81
- const toolInput = tool.toolInput
82
-
115
+ /**
116
+ * Host-delivered payload → local `viewData`, recovered payloads included.
117
+ * Render-phase setState (not an effect) so the React Compiler doesn't flag a
118
+ * cascading render.
119
+ */
120
+ function useHostViewData(
121
+ initialViewData: ViewData | undefined,
122
+ isPending: boolean,
123
+ recovered: ViewData | null,
124
+ ) {
83
125
  const [viewData, setViewData] = useState<ViewData | null>(null)
84
- const [isRefreshing, setIsRefreshing] = useState(false)
85
-
86
- // In-iframe build mode is purely a UI state — the LLM never sees the
87
- // catalogue, the user toggles into build by clicking the toolbar button.
88
- const [buildMode, setBuildMode] = useState(false)
89
-
90
- // Sync host-provided initialViewData → local viewData via render-phase
91
- // setState (rather than an effect) so the React Compiler doesn't flag a
92
- // cascading render. The guard against transient empty toolOutput stays:
93
- // widgetProps may briefly drop `context`/`layout` when displayMode toggles,
94
- // and we don't want that to clobber a valid viewData.
95
- //
96
126
  // `prev` MUST start as `null`, not `initialViewData`: hosts that expose the
97
127
  // complete tool output before the app's first render (e.g. window.openai
98
128
  // globals injected ahead of script execution) would otherwise never trip the
99
- // identity check — initialViewData never changes again, the sync never runs,
100
- // and the app idles on the loading skeleton forever.
101
- const [prevInitialViewData, setPrevInitialViewData] = useState<typeof initialViewData | null>(
102
- null,
103
- )
104
- if (initialViewData !== prevInitialViewData) {
105
- setPrevInitialViewData(initialViewData)
106
- if (!isPending && isCompleteViewData(initialViewData)) {
107
- setViewData(initialViewData)
108
- }
129
+ // identity check — the sync never runs and the app idles on the skeleton.
130
+ // The completeness guard stays: widgetProps may briefly drop
131
+ // `context`/`layout` when displayMode toggles.
132
+ const [prevInitial, setPrevInitial] = useState<ViewData | undefined | null>(null)
133
+ if (initialViewData !== prevInitial) {
134
+ setPrevInitial(initialViewData)
135
+ if (!isPending && isCompleteViewData(initialViewData)) setViewData(initialViewData)
109
136
  }
110
-
111
- // Hosts that strip `structuredContent` from the tool-result notification
112
- // (claude.ai / Claude Desktop) never latch a result under mcp-use 2.x — the
113
- // `useToolContext` latch ignores content-only results, so `isPending` stays
114
- // true forever. The recovery therefore also arms itself on a grace timer
115
- // once the originating tool is known, re-executes it via `callTool`
116
- // (responses carry `structuredContent` intact), and fills the gap.
117
- // Conforming hosts deliver a valid payload up front, so it never fires
118
- // there.
119
- const recovery = useToolResultRecovery<ViewData>({
120
- resultReady: !isPending,
121
- cancelled: isCancelled,
122
- props: initialViewData,
123
- isValid: isCompleteViewData,
124
- toolInput,
125
- hostContext,
126
- callTool,
127
- })
128
137
  const [prevRecovered, setPrevRecovered] = useState<ViewData | null>(null)
129
- if (recovery.data !== prevRecovered) {
130
- setPrevRecovered(recovery.data)
138
+ if (recovered !== prevRecovered) {
139
+ setPrevRecovered(recovered)
131
140
  // The host-delivered payload always wins; recovery only fills the gap.
132
- if (recovery.data && !viewData) setViewData(recovery.data)
141
+ if (recovered && !viewData) setViewData(recovered)
133
142
  }
143
+ return [viewData, setViewData] as const
144
+ }
134
145
 
135
- const toggleFullscreen = useCallback(async () => {
136
- const newMode = displayMode === "fullscreen" ? "inline" : "fullscreen"
137
- try {
138
- // Advisory since 2.x: the promise only confirms the host processed the
139
- // request. `displayMode` from `useDisplayMode` updates reactively when
140
- // (and only when) the host actually applies the change.
141
- await requestDisplayMode({ mode: newMode })
142
- } catch (e) {
143
- console.error("Failed to toggle display mode:", e)
144
- }
145
- }, [displayMode, requestDisplayMode])
146
-
147
- // Stable callTool callback (avoids a new object reference on every render)
148
- const callToolFn = useCallback(
149
- async (name: string, args: object) => {
150
- return callTool(name, args as Record<string, unknown>)
151
- },
152
- [callTool],
153
- )
154
-
146
+ /**
147
+ * Refresh via the refresh tool. A failure keeps the data and says so in a
148
+ * toast in catalog words only; the raw reason goes to the console, never to
149
+ * the user.
150
+ */
151
+ function useViewRefresh(options: {
152
+ viewData: ViewData | null
153
+ setViewData: (next: ViewData) => void
154
+ callTool: (name: string, args: Record<string, unknown>) => Promise<unknown>
155
+ refreshToolName: string
156
+ labels: Pick<ViewMessages, "refreshFailed" | "refreshFailedHint">
157
+ }) {
158
+ const { viewData, setViewData, callTool, refreshToolName, labels } = options
159
+ const toast = useToast()
160
+ const [isRefreshing, setIsRefreshing] = useState(false)
155
161
  const refreshView = useCallback(async () => {
156
162
  if (!viewData?._refreshParams) return
157
163
  setIsRefreshing(true)
164
+ let failure: unknown = null
158
165
  try {
159
166
  const result = await callTool(
160
167
  refreshToolName,
161
168
  viewData._refreshParams as unknown as Record<string, unknown>,
162
169
  )
163
- // Decode through the shared structured-first parser (one convention with
164
- // `useToolQuery`); a refresh returns the view envelope as `structuredContent`.
165
- const refreshed = parseToolResult<ViewData | null>(result)
166
- if (refreshed?.context && refreshed?.layout) {
167
- setViewData(refreshed)
168
- }
169
- void queryClient.invalidateQueries()
170
+ // One decoding convention with `useToolQuery` (structured-first), with a
171
+ // typed outcome: an unusable payload is a failure, never exception text.
172
+ const outcome = decodeViewResult(result, isCompleteViewData)
173
+ if (outcome.ok) setViewData(outcome.data)
174
+ else failure = outcome.reason ?? "the refresh tool returned no view"
175
+ // The call went through, so self-fetching widgets refresh as well;
176
+ // a tool error refreshes nothing.
177
+ if (outcome.ok || !outcome.isError) void queryClient.invalidateQueries()
170
178
  } catch (e) {
171
- console.error("Failed to refresh view:", e)
179
+ failure = e
172
180
  } finally {
173
181
  setIsRefreshing(false)
174
182
  }
175
- }, [callTool, refreshToolName, viewData])
183
+ if (failure === null) return
184
+ console.warn("[mcp-toolkit] refresh failed:", failure)
185
+ toast.show({
186
+ tone: "danger",
187
+ title: labels.refreshFailed,
188
+ description: labels.refreshFailedHint,
189
+ })
190
+ }, [callTool, labels, refreshToolName, setViewData, toast, viewData])
191
+ return { isRefreshing, refreshView }
192
+ }
193
+
194
+ /**
195
+ * The fullscreen toggle, gated on the host's offer. A rejected request shows
196
+ * a toast in catalog words; the raw reason goes to the console.
197
+ */
198
+ function useFullscreenToggle(
199
+ labels: Pick<ViewMessages, "fullscreenFailed" | "fullscreenFailedHint">,
200
+ ) {
201
+ const { displayMode, availableDisplayModes, requestDisplayMode } = useDisplayMode()
202
+ const toast = useToast()
203
+ const toggle = useCallback(async () => {
204
+ const mode = displayMode === "fullscreen" ? "inline" : "fullscreen"
205
+ try {
206
+ // Advisory since 2.x: `displayMode` updates when the host applies it.
207
+ await requestDisplayMode({ mode })
208
+ } catch (e) {
209
+ console.warn("[mcp-toolkit] display-mode request failed:", e)
210
+ toast.show({
211
+ tone: "danger",
212
+ title: labels.fullscreenFailed,
213
+ description: labels.fullscreenFailedHint,
214
+ })
215
+ }
216
+ }, [displayMode, labels, requestDisplayMode, toast])
217
+ return {
218
+ isFullscreen: displayMode === "fullscreen",
219
+ canFullscreen: fullscreenAvailable(displayMode, availableDisplayModes),
220
+ toggle,
221
+ }
222
+ }
223
+
224
+ function McpAppViewShell({
225
+ widgets,
226
+ refreshToolName = "refresh-view",
227
+ builderEnabled,
228
+ labels,
229
+ builderLabels,
230
+ titleMode = "show",
231
+ titleLevel = 2,
232
+ renderTitle,
233
+ slowHintAfterMs = DEFAULT_SLOW_HINT_MS,
234
+ describeError,
235
+ describeStep,
236
+ }: McpAppViewProps) {
237
+ const messages = useToolkitMessages()
238
+ const L = useMemo(() => ({ ...messages.view, ...labels }), [messages.view, labels])
239
+ // The mcp-use 2.x view channels (all need the `bootstrapView` mount
240
+ // `mountMcpToolkitApp` provides); tools go through the HostBridge.
241
+ const tool = useToolContext()
242
+ const { safeArea, hostContext } = useHostContext()
243
+ const bridge = useHostBridge()
244
+ const callTool = useCallback(
245
+ (name: string, args: Record<string, unknown>) => bridge.callTool(name, args),
246
+ [bridge],
247
+ )
248
+ // The widget-facing shape (`args: object`, so generated input interfaces
249
+ // without an index signature pass straight through).
250
+ const callToolFn = useCallback(
251
+ (name: string, args: object) => bridge.callTool(name, args as Record<string, unknown>),
252
+ [bridge],
253
+ )
254
+ const isPending = tool.status === "pending"
255
+ // Since mcp-use 2.7.1 a host cancellation latches as `status: "error"`.
256
+ const isCancelled = tool.status === "error" && tool.error instanceof ToolCancelledError
257
+ const isCallError = tool.status === "error" && !isCancelled
258
+ const initialViewData = tool.status === "ready" ? (tool.toolOutput as ViewData) : undefined
176
259
 
177
- // Called by LayoutBuilder when the user clicks Done. Commits the draft
178
- // layout (and any keys/steps edits) into the parent viewData so the
179
- // WidgetRenderer takes over showing the just-built layout.
260
+ // Hosts that strip `structuredContent` (claude.ai) never latch a result under
261
+ // mcp-use 2.x; the recovery re-executes the call once to fill the gap. It
262
+ // never fires on a cancelled or failed call (#176): those render their state.
263
+ const recovery = useToolResultRecovery<ViewData>({
264
+ resultReady: !isPending,
265
+ cancelled: isCancelled,
266
+ errored: isCallError,
267
+ props: initialViewData,
268
+ isValid: isCompleteViewData,
269
+ toolInput: tool.toolInput,
270
+ hostContext,
271
+ callTool,
272
+ })
273
+ const [viewData, setViewData] = useHostViewData(initialViewData, isPending, recovery.data)
274
+ const { lifecycle, retry: retryCall } = useViewLifecycle({
275
+ hasViewData: viewData !== null,
276
+ callStatus: tool.status,
277
+ cancelled: isCancelled,
278
+ callError: isCallError ? tool.error : undefined,
279
+ recoveryFailed: recovery.failed,
280
+ toolName: resolveRecoveryToolName(hostContext),
281
+ toolInput: tool.toolInput,
282
+ callTool,
283
+ isValid: isCompleteViewData,
284
+ onResult: setViewData,
285
+ slowHintAfterMs,
286
+ })
287
+ const { frameRef, retry } = useRetryFocus(lifecycle.phase, retryCall)
288
+ const { isRefreshing, refreshView } = useViewRefresh({
289
+ viewData,
290
+ setViewData,
291
+ callTool,
292
+ refreshToolName,
293
+ labels: L,
294
+ })
295
+ const fullscreen = useFullscreenToggle(L)
296
+ // In-iframe build mode is purely a UI state — the LLM never sees the catalogue.
297
+ const [buildMode, setBuildMode] = useState(false)
180
298
  const exitBuildMode = useCallback(
181
- (commit?: {
182
- layout: LayoutConfig
183
- keys?: Record<string, unknown>
184
- steps?: PipelineStepRef[]
185
- title?: string
186
- context?: ViewData["context"]
187
- }) => {
188
- if (commit) {
189
- setViewData((prev) =>
190
- prev
191
- ? {
192
- ...prev,
193
- layout: commit.layout,
194
- title: commit.title ?? prev.title,
195
- context: commit.context ?? prev.context,
196
- _refreshParams: {
197
- keys: commit.keys,
198
- steps: commit.steps,
199
- layout: commit.layout,
200
- title: commit.title ?? prev.title,
201
- },
202
- }
203
- : prev,
204
- )
205
- }
299
+ (commit?: BuilderCommit) => {
300
+ if (commit) setViewData((prev) => (prev ? applyBuilderCommit(prev, commit) : prev))
206
301
  setBuildMode(false)
207
302
  },
208
- [],
303
+ [setViewData],
209
304
  )
210
305
 
211
- // `isPending` must not gate the render: on hosts that strip
212
- // `structuredContent` the 2.x latch stays "pending" forever, and recovered
213
- // viewData still has to replace the skeleton.
306
+ // `isPending` must not gate the render: on stripping hosts the latch stays
307
+ // "pending" forever, and recovered viewData still has to replace it. The
308
+ // frame is the stable focus target of a retry (`useRetryFocus`).
214
309
  if (!viewData) {
215
- return <ViewPlaceholder cancelled={isCancelled} labels={effectiveLabels} />
310
+ return (
311
+ <div ref={frameRef} tabIndex={-1} data-slot="mcp-app-view" className="outline-hidden">
312
+ <ViewPlaceholder
313
+ lifecycle={lifecycle}
314
+ labels={L}
315
+ onRetry={retry}
316
+ describeError={describeError}
317
+ />
318
+ </div>
319
+ )
216
320
  }
217
321
 
218
- const isFullscreen = displayMode === "fullscreen"
219
-
220
- // Follow the server's own signal by default: render-view reports
221
- // `builderAvailable` (derived from `app.builder`), so the Build button only
222
- // shows when the catalogue/dashboard tools are actually registered. An
223
- // explicit `builderEnabled` prop forces the affordance on or off.
224
- const builderAvailable = builderEnabled ?? viewData.builderAvailable ?? false
225
-
226
322
  return (
227
- <main
228
- style={{
229
- // Fullscreen: the widget owns the viewport, so we take the full window
230
- // and handle our own scrolling. Inline: the host (Claude, ChatGPT) owns
231
- // sizing + scrolling and observes our intrinsic height via
232
- // `bootstrapView`'s auto-resize, so we let content dictate height and
233
- // avoid setting overflowY (which would clip our own content inside a
234
- // host that's already auto-sized to us).
235
- ...(isFullscreen ? { minHeight: "100vh", overflowY: "auto" as const } : null),
236
- paddingTop: safeArea.top,
237
- paddingRight: safeArea.right,
238
- paddingBottom: safeArea.bottom,
239
- paddingLeft: safeArea.left,
240
- }}
241
- >
242
- <ViewToolbar
243
- hidden={buildMode}
244
- viewData={viewData}
245
- builderAvailable={builderAvailable}
246
- isFullscreen={isFullscreen}
247
- isRefreshing={isRefreshing}
248
- labels={effectiveLabels}
249
- onRefresh={refreshView}
250
- onEnterBuildMode={() => setBuildMode(true)}
251
- onToggleFullscreen={toggleFullscreen}
252
- />
253
-
254
- <StepErrors hidden={buildMode} errors={viewData.context.errors} />
255
-
256
- <AppQueryProvider callTool={callToolFn}>
257
- <ViewBody
258
- buildMode={buildMode}
323
+ <div ref={frameRef} tabIndex={-1} data-slot="mcp-app-view" className="outline-hidden">
324
+ <main
325
+ style={{
326
+ // Fullscreen: the view owns the viewport and scrolls itself. Inline:
327
+ // the host sizes us from our intrinsic height (bootstrapView's
328
+ // auto-resize), so no overflow clipping there.
329
+ ...(fullscreen.isFullscreen ? { minHeight: "100vh", overflowY: "auto" as const } : null),
330
+ paddingTop: safeArea.top,
331
+ paddingRight: safeArea.right,
332
+ paddingBottom: safeArea.bottom,
333
+ paddingLeft: safeArea.left,
334
+ }}
335
+ >
336
+ <ViewToolbar
337
+ hidden={buildMode}
259
338
  viewData={viewData}
260
- widgets={widgets}
261
- callTool={callToolFn}
262
- onExit={exitBuildMode}
339
+ builderAvailable={builderEnabled ?? viewData.builderAvailable ?? false}
340
+ isFullscreen={fullscreen.isFullscreen}
341
+ canFullscreen={fullscreen.canFullscreen}
342
+ isRefreshing={isRefreshing}
343
+ labels={L}
344
+ titleMode={titleMode}
345
+ titleLevel={titleLevel}
346
+ renderTitle={renderTitle}
347
+ onRefresh={refreshView}
348
+ onEnterBuildMode={() => setBuildMode(true)}
349
+ onToggleFullscreen={fullscreen.toggle}
263
350
  />
264
- </AppQueryProvider>
265
- </main>
351
+
352
+ <StepErrors
353
+ hidden={buildMode}
354
+ errors={viewData.context.errors}
355
+ steps={viewData._refreshParams?.steps}
356
+ labels={L}
357
+ describeStep={describeStep}
358
+ onRetry={viewData._refreshParams ? () => void refreshView() : undefined}
359
+ />
360
+
361
+ <AppQueryProvider callTool={callToolFn}>
362
+ <ViewBody
363
+ buildMode={buildMode}
364
+ viewData={viewData}
365
+ widgets={widgets}
366
+ callTool={callToolFn}
367
+ builderLabels={builderLabels}
368
+ onExit={exitBuildMode}
369
+ />
370
+ </AppQueryProvider>
371
+ </main>
372
+ </div>
266
373
  )
267
374
  }
@@ -1,11 +1,33 @@
1
- import { ThemeProvider, bootstrapView } from "mcp-use/react"
1
+ import { useLayoutEffect, type ReactNode } from "react"
2
+ import { ThemeProvider, bootstrapView, useHostContext } from "mcp-use/react"
3
+ import { adoptHostFontVariables } from "../theme/host-fonts.js"
2
4
  import { McpAppView, type McpAppViewProps } from "./mcp-app-view.js"
3
5
  import { McpUseHostBridgeProvider } from "./mcp-use-host-bridge.js"
4
6
 
7
+ /**
8
+ * Keeps Geist first when the host sends its font. mcp-use's `ThemeProvider`
9
+ * writes the host's SEP-1865 style variables inline on `<html>`, including
10
+ * `--font-sans`/`--font-mono`, which share their names with the toolkit's font
11
+ * roles and would replace the Geist-first stacks whole. This component sits
12
+ * ABOVE that provider on purpose: a parent's layout effect runs after its
13
+ * children's, so the host variables are already applied when it moves the
14
+ * font pair into the fallback slot (`adoptHostFontVariables`). Both effects
15
+ * key on the same host-context snapshot, so they run in the same commit.
16
+ */
17
+ function HostFontRoles({ children }: { children: ReactNode }) {
18
+ const { hostContext } = useHostContext()
19
+ useLayoutEffect(() => {
20
+ if (typeof document === "undefined") return
21
+ adoptHostFontVariables(document.documentElement.style, hostContext?.styles?.variables)
22
+ }, [hostContext])
23
+ return children
24
+ }
25
+
5
26
  /**
6
27
  * Consumer-facing root for a toolkit-rendered MCP App. Composes the mcp-use
7
28
  * 2.x view plumbing around `McpAppView`: `ThemeProvider` applies the host's
8
- * theme/style variables to the document, and `McpUseHostBridgeProvider`
29
+ * theme/style variables to the document (with the host's font kept behind
30
+ * Geist, see `adoptHostFontVariables`), and `McpUseHostBridgeProvider`
9
31
  * installs the `HostBridge` (and view-scope flag) every toolkit widget below
10
32
  * resolves through `useHostBridge()`.
11
33
  *
@@ -16,11 +38,13 @@ import { McpUseHostBridgeProvider } from "./mcp-use-host-bridge.js"
16
38
  */
17
39
  export function McpToolkitApp(props: McpAppViewProps) {
18
40
  return (
19
- <ThemeProvider>
20
- <McpUseHostBridgeProvider>
21
- <McpAppView {...props} />
22
- </McpUseHostBridgeProvider>
23
- </ThemeProvider>
41
+ <HostFontRoles>
42
+ <ThemeProvider>
43
+ <McpUseHostBridgeProvider>
44
+ <McpAppView {...props} />
45
+ </McpUseHostBridgeProvider>
46
+ </ThemeProvider>
47
+ </HostFontRoles>
24
48
  )
25
49
  }
26
50