@miragon/mcp-toolkit-ui 2.5.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.
- package/README.md +8 -2
- package/dist/app/builder/BuilderBanners.d.ts +7 -2
- package/dist/app/builder/BuilderBanners.d.ts.map +1 -1
- package/dist/app/builder/BuilderBanners.js +8 -3
- package/dist/app/builder/BuilderBanners.js.map +1 -1
- package/dist/app/builder/CanvasRow.d.ts.map +1 -1
- package/dist/app/builder/CanvasRow.js +12 -14
- package/dist/app/builder/CanvasRow.js.map +1 -1
- package/dist/app/builder/CatalogueSheet.d.ts.map +1 -1
- package/dist/app/builder/CatalogueSheet.js +6 -5
- package/dist/app/builder/CatalogueSheet.js.map +1 -1
- package/dist/app/builder/EmptyHint.d.ts +4 -0
- package/dist/app/builder/EmptyHint.d.ts.map +1 -1
- package/dist/app/builder/EmptyHint.js +6 -1
- package/dist/app/builder/EmptyHint.js.map +1 -1
- package/dist/app/builder/LayoutSurface.js +3 -3
- package/dist/app/builder/LayoutSurface.js.map +1 -1
- package/dist/app/builder/PipelineStrip.d.ts +3 -1
- package/dist/app/builder/PipelineStrip.d.ts.map +1 -1
- package/dist/app/builder/PipelineStrip.js +11 -6
- package/dist/app/builder/PipelineStrip.js.map +1 -1
- package/dist/app/builder/SaveDialog.d.ts.map +1 -1
- package/dist/app/builder/SaveDialog.js +2 -2
- package/dist/app/builder/SaveDialog.js.map +1 -1
- package/dist/app/builder/Toolbar.d.ts +3 -1
- package/dist/app/builder/Toolbar.d.ts.map +1 -1
- package/dist/app/builder/Toolbar.js +9 -3
- package/dist/app/builder/Toolbar.js.map +1 -1
- package/dist/app/builder/Workspace.js +2 -2
- package/dist/app/builder/Workspace.js.map +1 -1
- package/dist/app/builder/builder-model.d.ts +3 -2
- package/dist/app/builder/builder-model.d.ts.map +1 -1
- package/dist/app/builder/builder-model.js +5 -4
- package/dist/app/builder/builder-model.js.map +1 -1
- package/dist/app/builder/labels.d.ts +13 -62
- package/dist/app/builder/labels.d.ts.map +1 -1
- package/dist/app/builder/labels.js +9 -58
- package/dist/app/builder/labels.js.map +1 -1
- package/dist/app/builder/status-chip.d.ts.map +1 -1
- package/dist/app/builder/status-chip.js +3 -2
- package/dist/app/builder/status-chip.js.map +1 -1
- package/dist/app/builder/use-builder-catalogue.d.ts +1 -0
- package/dist/app/builder/use-builder-catalogue.d.ts.map +1 -1
- package/dist/app/builder/use-builder-catalogue.js +6 -0
- package/dist/app/builder/use-builder-catalogue.js.map +1 -1
- package/dist/app/builder/use-save-dashboard.d.ts +3 -1
- package/dist/app/builder/use-save-dashboard.d.ts.map +1 -1
- package/dist/app/builder/use-save-dashboard.js +3 -1
- package/dist/app/builder/use-save-dashboard.js.map +1 -1
- package/dist/app/index.d.ts +1 -1
- package/dist/app/index.d.ts.map +1 -1
- package/dist/app/index.js.map +1 -1
- package/dist/app/layout-builder.d.ts +7 -2
- package/dist/app/layout-builder.d.ts.map +1 -1
- package/dist/app/layout-builder.js +17 -5
- package/dist/app/layout-builder.js.map +1 -1
- package/dist/app/mcp-app-view-chrome.d.ts +94 -20
- package/dist/app/mcp-app-view-chrome.d.ts.map +1 -1
- package/dist/app/mcp-app-view-chrome.js +109 -40
- package/dist/app/mcp-app-view-chrome.js.map +1 -1
- package/dist/app/mcp-app-view-states.d.ts +15 -0
- package/dist/app/mcp-app-view-states.d.ts.map +1 -0
- package/dist/app/mcp-app-view-states.js +34 -0
- package/dist/app/mcp-app-view-states.js.map +1 -0
- package/dist/app/mcp-app-view.d.ts +40 -5
- package/dist/app/mcp-app-view.d.ts.map +1 -1
- package/dist/app/mcp-app-view.js +174 -128
- package/dist/app/mcp-app-view.js.map +1 -1
- package/dist/app/mcp-toolkit-app.d.ts +2 -1
- package/dist/app/mcp-toolkit-app.d.ts.map +1 -1
- package/dist/app/mcp-toolkit-app.js +25 -3
- package/dist/app/mcp-toolkit-app.js.map +1 -1
- package/dist/app/use-view-lifecycle.d.ts +38 -0
- package/dist/app/use-view-lifecycle.d.ts.map +1 -0
- package/dist/app/use-view-lifecycle.js +79 -0
- package/dist/app/use-view-lifecycle.js.map +1 -0
- package/dist/app/view-lifecycle.d.ts +109 -0
- package/dist/app/view-lifecycle.d.ts.map +1 -0
- package/dist/app/view-lifecycle.js +120 -0
- package/dist/app/view-lifecycle.js.map +1 -0
- package/dist/app/widget-props-sheet.d.ts +3 -9
- package/dist/app/widget-props-sheet.d.ts.map +1 -1
- package/dist/app/widget-props-sheet.js +6 -13
- package/dist/app/widget-props-sheet.js.map +1 -1
- package/dist/app/widget-renderer.d.ts +41 -1
- package/dist/app/widget-renderer.d.ts.map +1 -1
- package/dist/app/widget-renderer.js +82 -31
- package/dist/app/widget-renderer.js.map +1 -1
- package/dist/components/DrillButton.d.ts.map +1 -1
- package/dist/components/DrillButton.js +2 -1
- package/dist/components/DrillButton.js.map +1 -1
- package/dist/components/EmptyState.d.ts +33 -0
- package/dist/components/EmptyState.d.ts.map +1 -0
- package/dist/components/EmptyState.js +17 -0
- package/dist/components/EmptyState.js.map +1 -0
- package/dist/components/ErrorState.d.ts +50 -0
- package/dist/components/ErrorState.d.ts.map +1 -0
- package/dist/components/ErrorState.js +60 -0
- package/dist/components/ErrorState.js.map +1 -0
- package/dist/components/FilterBar.d.ts +1 -1
- package/dist/components/FilterBar.d.ts.map +1 -1
- package/dist/components/FilterBar.js +7 -4
- package/dist/components/FilterBar.js.map +1 -1
- package/dist/components/GroupCard.d.ts.map +1 -1
- package/dist/components/GroupCard.js +2 -1
- package/dist/components/GroupCard.js.map +1 -1
- package/dist/components/KpiGrid.d.ts +10 -3
- package/dist/components/KpiGrid.d.ts.map +1 -1
- package/dist/components/KpiGrid.js +31 -9
- package/dist/components/KpiGrid.js.map +1 -1
- package/dist/components/ListFooter.d.ts +3 -2
- package/dist/components/ListFooter.d.ts.map +1 -1
- package/dist/components/ListFooter.js +14 -4
- package/dist/components/ListFooter.js.map +1 -1
- package/dist/components/Pills.d.ts +10 -6
- package/dist/components/Pills.d.ts.map +1 -1
- package/dist/components/Pills.js +12 -6
- package/dist/components/Pills.js.map +1 -1
- package/dist/components/Toast.d.ts +37 -0
- package/dist/components/Toast.d.ts.map +1 -0
- package/dist/components/Toast.js +91 -0
- package/dist/components/Toast.js.map +1 -0
- package/dist/components/WidgetHeader.js +1 -1
- package/dist/hooks/use-tool-result-recovery.d.ts +23 -1
- package/dist/hooks/use-tool-result-recovery.d.ts.map +1 -1
- package/dist/hooks/use-tool-result-recovery.js +21 -7
- package/dist/hooks/use-tool-result-recovery.js.map +1 -1
- package/dist/hooks/use-view-data.d.ts +6 -0
- package/dist/hooks/use-view-data.d.ts.map +1 -1
- package/dist/hooks/use-view-data.js +6 -1
- package/dist/hooks/use-view-data.js.map +1 -1
- package/dist/index.d.ts +12 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -4
- package/dist/index.js.map +1 -1
- package/dist/lib/focus-ring.d.ts +18 -0
- package/dist/lib/focus-ring.d.ts.map +1 -0
- package/dist/lib/focus-ring.js +18 -0
- package/dist/lib/focus-ring.js.map +1 -0
- package/dist/lib/icon-stroke.d.ts +12 -0
- package/dist/lib/icon-stroke.d.ts.map +1 -0
- package/dist/lib/icon-stroke.js +12 -0
- package/dist/lib/icon-stroke.js.map +1 -0
- package/dist/lib/toast-model.d.ts +46 -0
- package/dist/lib/toast-model.d.ts.map +1 -0
- package/dist/lib/toast-model.js +36 -0
- package/dist/lib/toast-model.js.map +1 -0
- package/dist/lib/tone-utils.d.ts +43 -9
- package/dist/lib/tone-utils.d.ts.map +1 -1
- package/dist/lib/tone-utils.js +36 -7
- package/dist/lib/tone-utils.js.map +1 -1
- package/dist/locale/index.d.ts +3 -1
- package/dist/locale/index.d.ts.map +1 -1
- package/dist/locale/index.js +2 -1
- package/dist/locale/index.js.map +1 -1
- package/dist/locale/locale-provider.d.ts +42 -1
- package/dist/locale/locale-provider.d.ts.map +1 -1
- package/dist/locale/locale-provider.js +44 -3
- package/dist/locale/locale-provider.js.map +1 -1
- package/dist/locale/message-types.d.ts +223 -0
- package/dist/locale/message-types.d.ts.map +1 -0
- package/dist/locale/message-types.js +7 -0
- package/dist/locale/message-types.js.map +1 -0
- package/dist/locale/messages-de.d.ts +9 -0
- package/dist/locale/messages-de.d.ts.map +1 -0
- package/dist/locale/messages-de.js +151 -0
- package/dist/locale/messages-de.js.map +1 -0
- package/dist/locale/messages-en.d.ts +8 -0
- package/dist/locale/messages-en.d.ts.map +1 -0
- package/dist/locale/messages-en.js +150 -0
- package/dist/locale/messages-en.js.map +1 -0
- package/dist/locale/toolkit-messages.d.ts +39 -0
- package/dist/locale/toolkit-messages.d.ts.map +1 -0
- package/dist/locale/toolkit-messages.js +50 -0
- package/dist/locale/toolkit-messages.js.map +1 -0
- package/dist/primitives/alert.d.ts.map +1 -1
- package/dist/primitives/alert.js +4 -2
- package/dist/primitives/alert.js.map +1 -1
- package/dist/primitives/badge.d.ts +1 -1
- package/dist/primitives/badge.d.ts.map +1 -1
- package/dist/primitives/badge.js +14 -6
- package/dist/primitives/badge.js.map +1 -1
- package/dist/primitives/button.d.ts.map +1 -1
- package/dist/primitives/button.js +12 -8
- package/dist/primitives/button.js.map +1 -1
- package/dist/primitives/card.js +4 -4
- package/dist/primitives/card.js.map +1 -1
- package/dist/primitives/dialog.d.ts +7 -3
- package/dist/primitives/dialog.d.ts.map +1 -1
- package/dist/primitives/dialog.js +14 -7
- package/dist/primitives/dialog.js.map +1 -1
- package/dist/primitives/input.d.ts.map +1 -1
- package/dist/primitives/input.js +4 -1
- package/dist/primitives/input.js.map +1 -1
- package/dist/primitives/scroll-area.js +1 -1
- package/dist/primitives/scroll-area.js.map +1 -1
- package/dist/primitives/select.d.ts.map +1 -1
- package/dist/primitives/select.js +5 -2
- package/dist/primitives/select.js.map +1 -1
- package/dist/primitives/sheet.d.ts +3 -1
- package/dist/primitives/sheet.d.ts.map +1 -1
- package/dist/primitives/sheet.js +12 -6
- package/dist/primitives/sheet.js.map +1 -1
- package/dist/primitives/skeleton.js +1 -1
- package/dist/primitives/skeleton.js.map +1 -1
- package/dist/primitives/switch.d.ts.map +1 -1
- package/dist/primitives/switch.js +2 -1
- package/dist/primitives/switch.js.map +1 -1
- package/dist/primitives/tabs.d.ts.map +1 -1
- package/dist/primitives/tabs.js +5 -3
- package/dist/primitives/tabs.js.map +1 -1
- package/dist/render-cases.d.ts.map +1 -1
- package/dist/render-cases.js +20 -2
- package/dist/render-cases.js.map +1 -1
- package/dist/render-scan.d.ts +25 -0
- package/dist/render-scan.d.ts.map +1 -0
- package/dist/render-scan.js +66 -0
- package/dist/render-scan.js.map +1 -0
- package/dist/theme/cd-tokens.d.ts +66 -0
- package/dist/theme/cd-tokens.d.ts.map +1 -0
- package/dist/theme/cd-tokens.js +64 -0
- package/dist/theme/cd-tokens.js.map +1 -0
- package/dist/theme/create-theme.d.ts +196 -39
- package/dist/theme/create-theme.d.ts.map +1 -1
- package/dist/theme/create-theme.js +189 -13
- package/dist/theme/create-theme.js.map +1 -1
- package/dist/theme/fonts.d.ts +38 -0
- package/dist/theme/fonts.d.ts.map +1 -0
- package/dist/theme/fonts.js +56 -0
- package/dist/theme/fonts.js.map +1 -0
- package/dist/theme/host-fonts.d.ts +39 -0
- package/dist/theme/host-fonts.d.ts.map +1 -0
- package/dist/theme/host-fonts.js +44 -0
- package/dist/theme/host-fonts.js.map +1 -0
- package/dist/theme/index.d.ts +8 -1
- package/dist/theme/index.d.ts.map +1 -1
- package/dist/theme/index.js +5 -1
- package/dist/theme/index.js.map +1 -1
- package/dist/theme/portal-container.d.ts +16 -0
- package/dist/theme/portal-container.d.ts.map +1 -0
- package/dist/theme/portal-container.js +19 -0
- package/dist/theme/portal-container.js.map +1 -0
- package/dist/theme/presets.d.ts +24 -9
- package/dist/theme/presets.d.ts.map +1 -1
- package/dist/theme/presets.js +44 -11
- package/dist/theme/presets.js.map +1 -1
- package/dist/theme/theme-provider.d.ts +27 -9
- package/dist/theme/theme-provider.d.ts.map +1 -1
- package/dist/theme/theme-provider.js +42 -20
- package/dist/theme/theme-provider.js.map +1 -1
- package/package.json +13 -8
- package/src/app/builder/BuilderBanners.tsx +26 -13
- package/src/app/builder/CanvasRow.tsx +22 -21
- package/src/app/builder/CatalogueSheet.tsx +16 -14
- package/src/app/builder/EmptyHint.tsx +16 -11
- package/src/app/builder/LayoutSurface.tsx +7 -7
- package/src/app/builder/PipelineStrip.tsx +26 -17
- package/src/app/builder/SaveDialog.tsx +11 -12
- package/src/app/builder/Toolbar.tsx +19 -7
- package/src/app/builder/Workspace.tsx +5 -3
- package/src/app/builder/builder-feedback.test.tsx +75 -0
- package/src/app/builder/builder-model.test.ts +5 -0
- package/src/app/builder/builder-model.ts +5 -4
- package/src/app/builder/labels.ts +15 -121
- package/src/app/builder/status-chip.tsx +8 -4
- package/src/app/builder/use-builder-catalogue.ts +6 -0
- package/src/app/builder/use-save-dashboard.ts +5 -0
- package/src/app/index.ts +8 -1
- package/src/app/layout-builder.tsx +23 -3
- package/src/app/mcp-app-view-chrome.test.tsx +347 -14
- package/src/app/mcp-app-view-chrome.tsx +278 -100
- package/src/app/mcp-app-view-states.tsx +100 -0
- package/src/app/mcp-app-view.test.tsx +258 -0
- package/src/app/mcp-app-view.tsx +292 -185
- package/src/app/mcp-toolkit-app.tsx +31 -7
- package/src/app/use-view-lifecycle.ts +116 -0
- package/src/app/view-lifecycle.test.ts +227 -0
- package/src/app/view-lifecycle.ts +174 -0
- package/src/app/widget-props-sheet.tsx +13 -26
- package/src/app/widget-renderer.render.test.tsx +131 -0
- package/src/app/widget-renderer.tsx +136 -38
- package/src/components/DrillButton.tsx +2 -1
- package/src/components/EmptyState.tsx +81 -0
- package/src/components/ErrorState.tsx +182 -0
- package/src/components/FilterBar.tsx +9 -6
- package/src/components/GroupCard.tsx +3 -2
- package/src/components/KpiGrid.tsx +75 -44
- package/src/components/ListFooter.tsx +17 -6
- package/src/components/Pills.tsx +20 -11
- package/src/components/Toast.tsx +188 -0
- package/src/components/WidgetHeader.tsx +1 -1
- package/src/components/feedback-states.test.tsx +225 -0
- package/src/components/tone-figures.test.tsx +54 -0
- package/src/fonts.css +19 -0
- package/src/globals.css +9 -165
- package/src/hooks/use-tool-result-recovery.test.ts +25 -1
- package/src/hooks/use-tool-result-recovery.ts +34 -6
- package/src/hooks/use-view-data.test.tsx +60 -0
- package/src/hooks/use-view-data.ts +12 -1
- package/src/i18n-coverage.test.tsx +323 -0
- package/src/index.ts +57 -5
- package/src/lib/focus-ring.ts +20 -0
- package/src/lib/icon-stroke.test.ts +59 -0
- package/src/lib/icon-stroke.ts +13 -0
- package/src/lib/toast-model.test.ts +68 -0
- package/src/lib/toast-model.ts +63 -0
- package/src/lib/tone-utils.test.ts +47 -14
- package/src/lib/tone-utils.ts +61 -13
- package/src/locale/index.ts +13 -1
- package/src/locale/locale-provider.test.tsx +87 -0
- package/src/locale/locale-provider.tsx +90 -3
- package/src/locale/message-types.ts +221 -0
- package/src/locale/messages-de.ts +159 -0
- package/src/locale/messages-en.ts +157 -0
- package/src/locale/toolkit-messages.test.ts +216 -0
- package/src/locale/toolkit-messages.ts +82 -0
- package/src/no-hardcoded-copy.test.ts +432 -0
- package/src/primitives/alert.tsx +4 -2
- package/src/primitives/badge.tsx +14 -7
- package/src/primitives/button.tsx +13 -8
- package/src/primitives/card.tsx +4 -4
- package/src/primitives/dialog.tsx +32 -7
- package/src/primitives/input.tsx +5 -3
- package/src/primitives/scroll-area.tsx +1 -1
- package/src/primitives/select.tsx +6 -3
- package/src/primitives/sheet.tsx +26 -6
- package/src/primitives/skeleton.tsx +1 -1
- package/src/primitives/switch.tsx +4 -2
- package/src/primitives/tabs.tsx +9 -6
- package/src/render-cases.tsx +45 -1
- package/src/render-scan.ts +75 -0
- package/src/theme/cd-tokens.generated.css +48 -0
- package/src/theme/cd-tokens.test.ts +107 -0
- package/src/theme/cd-tokens.ts +67 -0
- package/src/theme/create-theme.test.ts +243 -5
- package/src/theme/create-theme.ts +313 -41
- package/src/theme/fonts.test.ts +85 -0
- package/src/theme/fonts.ts +72 -0
- package/src/theme/host-fonts.test.ts +135 -0
- package/src/theme/host-fonts.ts +52 -0
- package/src/theme/index.ts +15 -1
- package/src/theme/portal-container.ts +20 -0
- package/src/theme/presets.ts +44 -11
- package/src/theme/theme-css.test.ts +568 -0
- package/src/theme/theme-provider.test.tsx +82 -0
- package/src/theme/theme-provider.tsx +60 -24
- package/src/theme.css +314 -0
- package/ui-catalog.json +344 -17
package/src/app/mcp-app-view.tsx
CHANGED
|
@@ -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
|
|
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 {
|
|
6
|
-
|
|
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
|
-
|
|
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
|
|
50
|
-
* Defaults to
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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 —
|
|
100
|
-
//
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
)
|
|
104
|
-
|
|
105
|
-
|
|
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 (
|
|
130
|
-
setPrevRecovered(
|
|
138
|
+
if (recovered !== prevRecovered) {
|
|
139
|
+
setPrevRecovered(recovered)
|
|
131
140
|
// The host-delivered payload always wins; recovery only fills the gap.
|
|
132
|
-
if (
|
|
141
|
+
if (recovered && !viewData) setViewData(recovered)
|
|
133
142
|
}
|
|
143
|
+
return [viewData, setViewData] as const
|
|
144
|
+
}
|
|
134
145
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
const
|
|
149
|
-
|
|
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
|
-
//
|
|
164
|
-
//
|
|
165
|
-
const
|
|
166
|
-
if (
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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
|
-
|
|
179
|
+
failure = e
|
|
172
180
|
} finally {
|
|
173
181
|
setIsRefreshing(false)
|
|
174
182
|
}
|
|
175
|
-
|
|
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
|
-
//
|
|
178
|
-
//
|
|
179
|
-
//
|
|
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
|
-
|
|
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
|
|
212
|
-
//
|
|
213
|
-
//
|
|
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
|
|
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
|
-
<
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
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
|
-
|
|
261
|
-
|
|
262
|
-
|
|
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
|
-
|
|
265
|
-
|
|
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 {
|
|
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
|
|
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
|
-
<
|
|
20
|
-
<
|
|
21
|
-
<
|
|
22
|
-
|
|
23
|
-
|
|
41
|
+
<HostFontRoles>
|
|
42
|
+
<ThemeProvider>
|
|
43
|
+
<McpUseHostBridgeProvider>
|
|
44
|
+
<McpAppView {...props} />
|
|
45
|
+
</McpUseHostBridgeProvider>
|
|
46
|
+
</ThemeProvider>
|
|
47
|
+
</HostFontRoles>
|
|
24
48
|
)
|
|
25
49
|
}
|
|
26
50
|
|