@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
|
@@ -1,36 +1,44 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Presentational chrome for `McpAppView` — the
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* Split out of `mcp-app-view.tsx` so the shell
|
|
6
|
-
* host-sync and state logic while
|
|
2
|
+
* Presentational chrome for `McpAppView` — the toolbar, the failed-steps
|
|
3
|
+
* alert, and the build/render body — plus the view-envelope types they share.
|
|
4
|
+
* The pre-payload states (waiting / error / cancelled) live in
|
|
5
|
+
* `mcp-app-view-states.tsx`. Split out of `mcp-app-view.tsx` so the shell
|
|
6
|
+
* component keeps only the host-sync and state logic while these files stay
|
|
7
|
+
* hook-light.
|
|
7
8
|
*/
|
|
8
|
-
import {
|
|
9
|
+
import { createElement, type ReactNode } from "react"
|
|
10
|
+
import { LucideProvider, Maximize2, Minimize2, Pencil, RefreshCw } from "lucide-react"
|
|
9
11
|
import type { LayoutConfig, PipelineStepRef } from "@miragon/mcp-toolkit-core"
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
+
import { ErrorState } from "../components/ErrorState.js"
|
|
13
|
+
import { DENSE_ICON_STROKE } from "../lib/icon-stroke.js"
|
|
14
|
+
import { TOOLKIT_MESSAGES, type ViewMessages } from "../locale/toolkit-messages.js"
|
|
15
|
+
import { Button } from "../primitives/button.js"
|
|
16
|
+
import {
|
|
17
|
+
LayoutBuilder,
|
|
18
|
+
type LayoutBuilderLabels,
|
|
19
|
+
type LayoutBuilderProps,
|
|
20
|
+
} from "./layout-builder.js"
|
|
12
21
|
import { WidgetRenderer, type WidgetComponent } from "./widget-renderer.js"
|
|
13
22
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
enterFullscreen?: string
|
|
21
|
-
exitFullscreen?: string
|
|
22
|
-
build?: string
|
|
23
|
-
}
|
|
23
|
+
/**
|
|
24
|
+
* Override strings of the view chrome (placeholder, error state, toolbar,
|
|
25
|
+
* step errors). Every field defaults to the active locale's toolkit catalog
|
|
26
|
+
* (`en`/`de`, see `useToolkitMessages`).
|
|
27
|
+
*/
|
|
28
|
+
export type McpAppViewLabels = Partial<ViewMessages>
|
|
24
29
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
/**
|
|
31
|
+
* English chrome strings.
|
|
32
|
+
* @deprecated Read the active locale's strings with `useToolkitMessages().view`
|
|
33
|
+
* (or `TOOLKIT_MESSAGES.en.view`); this alias stays for existing imports.
|
|
34
|
+
*/
|
|
35
|
+
export const DEFAULT_LABELS: Required<McpAppViewLabels> = TOOLKIT_MESSAGES.en.view
|
|
36
|
+
|
|
37
|
+
/** How the toolbar shows the view title — so a view has exactly ONE title. */
|
|
38
|
+
export type ViewTitleMode = "show" | "hide" | "slot"
|
|
39
|
+
|
|
40
|
+
/** Heading level of the toolbar title. */
|
|
41
|
+
export type ViewTitleLevel = 1 | 2 | 3 | 4 | 5 | 6
|
|
34
42
|
|
|
35
43
|
export interface RefreshParams {
|
|
36
44
|
keys?: Record<string, unknown>
|
|
@@ -71,32 +79,40 @@ export function isCompleteViewData(value: unknown): value is ViewData {
|
|
|
71
79
|
return Boolean(v?.context && v?.layout)
|
|
72
80
|
}
|
|
73
81
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
82
|
+
/** What the LayoutBuilder hands back on Done. */
|
|
83
|
+
export interface BuilderCommit {
|
|
84
|
+
layout: LayoutConfig
|
|
85
|
+
keys?: Record<string, unknown>
|
|
86
|
+
steps?: PipelineStepRef[]
|
|
87
|
+
title?: string
|
|
88
|
+
context?: ViewData["context"]
|
|
89
|
+
}
|
|
77
90
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
</div>
|
|
91
|
-
)
|
|
91
|
+
/**
|
|
92
|
+
* Commit the builder's draft into the view payload so the renderer shows the
|
|
93
|
+
* just-built layout and Refresh re-issues it. Exported for tests.
|
|
94
|
+
*/
|
|
95
|
+
export function applyBuilderCommit(prev: ViewData, commit: BuilderCommit): ViewData {
|
|
96
|
+
const title = commit.title ?? prev.title
|
|
97
|
+
return {
|
|
98
|
+
...prev,
|
|
99
|
+
layout: commit.layout,
|
|
100
|
+
title,
|
|
101
|
+
context: commit.context ?? prev.context,
|
|
102
|
+
_refreshParams: { keys: commit.keys, steps: commit.steps, layout: commit.layout, title },
|
|
92
103
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Whether the Fullscreen toggle may render: the host offers fullscreen
|
|
108
|
+
* (mcp-use's negotiated `availableDisplayModes`, toolkit#178 K22), or the view
|
|
109
|
+
* is already there (its exit toggle must stay).
|
|
110
|
+
*/
|
|
111
|
+
export function fullscreenAvailable(
|
|
112
|
+
displayMode: string,
|
|
113
|
+
availableDisplayModes: readonly string[],
|
|
114
|
+
): boolean {
|
|
115
|
+
return displayMode === "fullscreen" || availableDisplayModes.includes("fullscreen")
|
|
100
116
|
}
|
|
101
117
|
|
|
102
118
|
// -------------------------------------------------------------------------- //
|
|
@@ -109,91 +125,244 @@ export interface ViewToolbarProps {
|
|
|
109
125
|
viewData: ViewData
|
|
110
126
|
builderAvailable: boolean
|
|
111
127
|
isFullscreen: boolean
|
|
128
|
+
/** The host offers fullscreen (see {@link fullscreenAvailable}). */
|
|
129
|
+
canFullscreen: boolean
|
|
112
130
|
isRefreshing: boolean
|
|
113
131
|
labels: Required<McpAppViewLabels>
|
|
132
|
+
titleMode: ViewTitleMode
|
|
133
|
+
titleLevel: ViewTitleLevel
|
|
134
|
+
renderTitle?: (title: string | undefined) => ReactNode
|
|
114
135
|
onRefresh: () => Promise<void>
|
|
115
136
|
onEnterBuildMode: () => void
|
|
116
137
|
onToggleFullscreen: () => Promise<void>
|
|
117
138
|
}
|
|
118
139
|
|
|
140
|
+
/** The toolbar title per {@link ViewTitleMode}: a heading, the consumer's slot, or nothing. */
|
|
141
|
+
function ToolbarTitle({
|
|
142
|
+
title,
|
|
143
|
+
mode,
|
|
144
|
+
level,
|
|
145
|
+
renderTitle,
|
|
146
|
+
}: {
|
|
147
|
+
title: string | undefined
|
|
148
|
+
mode: ViewTitleMode
|
|
149
|
+
level: ViewTitleLevel
|
|
150
|
+
renderTitle?: (title: string | undefined) => ReactNode
|
|
151
|
+
}) {
|
|
152
|
+
if (mode === "slot") return <div className="min-w-0 flex-1">{renderTitle?.(title)}</div>
|
|
153
|
+
if (mode === "hide" || !title) return null
|
|
154
|
+
return createElement(
|
|
155
|
+
`h${level}`,
|
|
156
|
+
{ className: "min-w-0 flex-1 truncate text-lg font-semibold tracking-tight" },
|
|
157
|
+
title,
|
|
158
|
+
)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** Which toolbar controls render — pure, so the gates are unit-testable. */
|
|
162
|
+
export function toolbarControls(input: {
|
|
163
|
+
viewData: Pick<ViewData, "_refreshParams" | "layout" | "title">
|
|
164
|
+
builderAvailable: boolean
|
|
165
|
+
canFullscreen: boolean
|
|
166
|
+
titleMode: ViewTitleMode
|
|
167
|
+
}): { title: boolean; refresh: boolean; edit: boolean; fullscreen: boolean; any: boolean } {
|
|
168
|
+
const refreshable = Boolean(input.viewData._refreshParams && input.viewData.layout)
|
|
169
|
+
const edit = input.builderAvailable && Boolean(input.viewData._refreshParams)
|
|
170
|
+
const title =
|
|
171
|
+
input.titleMode === "slot" || (input.titleMode === "show" && Boolean(input.viewData.title))
|
|
172
|
+
return {
|
|
173
|
+
title,
|
|
174
|
+
refresh: refreshable,
|
|
175
|
+
edit,
|
|
176
|
+
fullscreen: input.canFullscreen,
|
|
177
|
+
any: title || refreshable || edit || input.canFullscreen,
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* The CI §8 chrome button (Icon-Button, Chrome): surface, `--cd-linie` border,
|
|
183
|
+
* shadow-1, blue on hover. The `outline` variant brings the surface, the
|
|
184
|
+
* border and the blue hover (it is also CI §8's secondary button, which has no
|
|
185
|
+
* shadow), so the chrome adds `shadow-1` on top instead of changing it.
|
|
186
|
+
*/
|
|
187
|
+
const CHROME_BUTTON = { variant: "outline", className: "shadow-1" } as const
|
|
188
|
+
|
|
189
|
+
function RefreshButton({
|
|
190
|
+
isRefreshing,
|
|
191
|
+
labels,
|
|
192
|
+
onRefresh,
|
|
193
|
+
}: {
|
|
194
|
+
isRefreshing: boolean
|
|
195
|
+
labels: Required<McpAppViewLabels>
|
|
196
|
+
onRefresh: () => Promise<void>
|
|
197
|
+
}) {
|
|
198
|
+
const label = isRefreshing ? labels.refreshing : labels.refresh
|
|
199
|
+
return (
|
|
200
|
+
<Button
|
|
201
|
+
type="button"
|
|
202
|
+
{...CHROME_BUTTON}
|
|
203
|
+
size="icon-sm"
|
|
204
|
+
aria-label={label}
|
|
205
|
+
title={label}
|
|
206
|
+
disabled={isRefreshing}
|
|
207
|
+
onClick={() => {
|
|
208
|
+
void onRefresh()
|
|
209
|
+
}}
|
|
210
|
+
>
|
|
211
|
+
<RefreshCw
|
|
212
|
+
aria-hidden="true"
|
|
213
|
+
className={isRefreshing ? "motion-safe:animate-spin" : undefined}
|
|
214
|
+
/>
|
|
215
|
+
</Button>
|
|
216
|
+
)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function FullscreenButton({
|
|
220
|
+
isFullscreen,
|
|
221
|
+
labels,
|
|
222
|
+
onToggle,
|
|
223
|
+
}: {
|
|
224
|
+
isFullscreen: boolean
|
|
225
|
+
labels: Required<McpAppViewLabels>
|
|
226
|
+
onToggle: () => Promise<void>
|
|
227
|
+
}) {
|
|
228
|
+
const label = isFullscreen ? labels.exitFullscreen : labels.enterFullscreen
|
|
229
|
+
const Icon = isFullscreen ? Minimize2 : Maximize2
|
|
230
|
+
return (
|
|
231
|
+
<Button
|
|
232
|
+
type="button"
|
|
233
|
+
{...CHROME_BUTTON}
|
|
234
|
+
size="icon-sm"
|
|
235
|
+
aria-label={label}
|
|
236
|
+
title={label}
|
|
237
|
+
onClick={() => {
|
|
238
|
+
void onToggle()
|
|
239
|
+
}}
|
|
240
|
+
>
|
|
241
|
+
<Icon aria-hidden="true" />
|
|
242
|
+
</Button>
|
|
243
|
+
)
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* The view toolbar: the title (per {@link ViewTitleMode}, so a widget that
|
|
248
|
+
* renders its own heading is not titled twice), then Edit, Refresh and
|
|
249
|
+
* Fullscreen. Refresh and Fullscreen are 32 px icon buttons named by
|
|
250
|
+
* `aria-label`; Fullscreen renders only when the host offers it. All three are
|
|
251
|
+
* chrome buttons in the CI pattern (CI §8: surface, border, shadow-1, blue
|
|
252
|
+
* hover; {@link CHROME_BUTTON}). A 16 px inset keeps the bar off the frame
|
|
253
|
+
* edge, also in fullscreen.
|
|
254
|
+
*/
|
|
119
255
|
export function ViewToolbar({
|
|
120
256
|
hidden,
|
|
121
257
|
viewData,
|
|
122
258
|
builderAvailable,
|
|
123
259
|
isFullscreen,
|
|
260
|
+
canFullscreen,
|
|
124
261
|
isRefreshing,
|
|
125
262
|
labels,
|
|
263
|
+
titleMode,
|
|
264
|
+
titleLevel,
|
|
265
|
+
renderTitle,
|
|
126
266
|
onRefresh,
|
|
127
267
|
onEnterBuildMode,
|
|
128
268
|
onToggleFullscreen,
|
|
129
269
|
}: ViewToolbarProps) {
|
|
130
|
-
|
|
270
|
+
const show = toolbarControls({ viewData, builderAvailable, canFullscreen, titleMode })
|
|
271
|
+
if (hidden || !show.any) return null
|
|
131
272
|
return (
|
|
132
|
-
<
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
273
|
+
<LucideProvider strokeWidth={DENSE_ICON_STROKE}>
|
|
274
|
+
<div data-slot="view-toolbar" className="flex min-h-12 items-center gap-2 px-4 py-2">
|
|
275
|
+
<ToolbarTitle
|
|
276
|
+
title={viewData.title}
|
|
277
|
+
mode={titleMode}
|
|
278
|
+
level={titleLevel}
|
|
279
|
+
renderTitle={renderTitle}
|
|
280
|
+
/>
|
|
281
|
+
<div className="ml-auto flex shrink-0 items-center gap-1">
|
|
282
|
+
{show.edit && (
|
|
283
|
+
<Button type="button" {...CHROME_BUTTON} size="sm" onClick={onEnterBuildMode}>
|
|
284
|
+
<Pencil aria-hidden="true" />
|
|
285
|
+
{labels.build}
|
|
286
|
+
</Button>
|
|
287
|
+
)}
|
|
288
|
+
{show.refresh && (
|
|
289
|
+
<RefreshButton isRefreshing={isRefreshing} labels={labels} onRefresh={onRefresh} />
|
|
290
|
+
)}
|
|
291
|
+
{show.fullscreen && (
|
|
292
|
+
<FullscreenButton
|
|
293
|
+
isFullscreen={isFullscreen}
|
|
294
|
+
labels={labels}
|
|
295
|
+
onToggle={onToggleFullscreen}
|
|
146
296
|
/>
|
|
147
|
-
{isRefreshing ? labels.refreshing : labels.refresh}
|
|
148
|
-
</button>
|
|
149
|
-
)}
|
|
150
|
-
{builderAvailable && viewData._refreshParams && (
|
|
151
|
-
<button
|
|
152
|
-
onClick={onEnterBuildMode}
|
|
153
|
-
className="hover:bg-accent hover:text-accent-foreground inline-flex items-center gap-1.5 rounded-md border px-3 py-1.5 text-sm font-medium transition-colors"
|
|
154
|
-
>
|
|
155
|
-
<Pencil className="h-4 w-4" />
|
|
156
|
-
{labels.build}
|
|
157
|
-
</button>
|
|
158
|
-
)}
|
|
159
|
-
<button
|
|
160
|
-
onClick={() => {
|
|
161
|
-
void onToggleFullscreen()
|
|
162
|
-
}}
|
|
163
|
-
className="hover:bg-accent hover:text-accent-foreground inline-flex items-center gap-1.5 rounded-md border px-3 py-1.5 text-sm font-medium transition-colors"
|
|
164
|
-
>
|
|
165
|
-
{isFullscreen ? (
|
|
166
|
-
<Minimize2 aria-hidden="true" className="h-4 w-4" />
|
|
167
|
-
) : (
|
|
168
|
-
<Maximize2 aria-hidden="true" className="h-4 w-4" />
|
|
169
297
|
)}
|
|
170
|
-
|
|
171
|
-
</button>
|
|
298
|
+
</div>
|
|
172
299
|
</div>
|
|
173
|
-
</
|
|
300
|
+
</LucideProvider>
|
|
174
301
|
)
|
|
175
302
|
}
|
|
176
303
|
|
|
177
304
|
// -------------------------------------------------------------------------- //
|
|
178
|
-
//
|
|
305
|
+
// Failed data steps
|
|
179
306
|
// -------------------------------------------------------------------------- //
|
|
180
307
|
|
|
308
|
+
/** Resolves a human title for a pipeline step (`{ id, step }` from the view's steps). */
|
|
309
|
+
export type DescribeStep = (step: { id: string; step?: string }) => string | undefined
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* The failed data steps as ONE inline alert: what happened (the title), which
|
|
313
|
+
* data is affected (each step by its human title, else its step id), what you
|
|
314
|
+
* can do (retry, or ask in the chat when the view cannot refresh). The raw
|
|
315
|
+
* step reasons stay under "Details", out of the user copy.
|
|
316
|
+
*/
|
|
181
317
|
export function StepErrors({
|
|
182
318
|
hidden,
|
|
183
319
|
errors,
|
|
320
|
+
steps,
|
|
321
|
+
labels,
|
|
322
|
+
describeStep,
|
|
323
|
+
onRetry,
|
|
184
324
|
}: {
|
|
185
|
-
/** Build mode surfaces its own diagnostics; the shell
|
|
325
|
+
/** Build mode surfaces its own diagnostics; the shell alert hides. */
|
|
186
326
|
hidden: boolean
|
|
187
327
|
errors: ViewData["context"]["errors"]
|
|
328
|
+
/** The view's step refs (`_refreshParams.steps`), to name a failed step by its definition. */
|
|
329
|
+
steps?: PipelineStepRef[]
|
|
330
|
+
labels: Required<McpAppViewLabels>
|
|
331
|
+
describeStep?: DescribeStep
|
|
332
|
+
/** Re-runs the view (a refresh); omitted when the view cannot be refreshed. */
|
|
333
|
+
onRetry?: () => void
|
|
188
334
|
}) {
|
|
189
335
|
if (hidden || errors.length === 0) return null
|
|
336
|
+
const failed = errors.map((err) => {
|
|
337
|
+
const ref = { id: err.stepId, step: steps?.find((s) => s.id === err.stepId)?.step }
|
|
338
|
+
return { ref, human: describeStep?.(ref), reason: err.reason }
|
|
339
|
+
})
|
|
190
340
|
return (
|
|
191
|
-
<div className="
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
341
|
+
<div className="px-4 pb-4">
|
|
342
|
+
<ErrorState
|
|
343
|
+
variant="inline"
|
|
344
|
+
title={labels.stepErrorsTitle}
|
|
345
|
+
retryLabel={labels.retry}
|
|
346
|
+
onRetry={onRetry}
|
|
347
|
+
detailsLabel={labels.errorDetails}
|
|
348
|
+
details={failed.map(({ ref, reason }) => `${ref.step ?? ref.id}: ${reason}`).join("\n")}
|
|
349
|
+
description={
|
|
350
|
+
<>
|
|
351
|
+
<ul className="flex flex-col gap-1">
|
|
352
|
+
{failed.map(({ ref, human }) => (
|
|
353
|
+
<li key={ref.id} className="break-words">
|
|
354
|
+
{human ? (
|
|
355
|
+
<span className="font-medium">{human}</span>
|
|
356
|
+
) : (
|
|
357
|
+
<span className="font-mono text-xs">{ref.step ?? ref.id}</span>
|
|
358
|
+
)}
|
|
359
|
+
</li>
|
|
360
|
+
))}
|
|
361
|
+
</ul>
|
|
362
|
+
<p className="mt-1">{onRetry ? labels.stepErrorsHint : labels.errorHintNoRetry}</p>
|
|
363
|
+
</>
|
|
364
|
+
}
|
|
365
|
+
/>
|
|
197
366
|
</div>
|
|
198
367
|
)
|
|
199
368
|
}
|
|
@@ -208,9 +377,17 @@ export interface ViewBodyProps {
|
|
|
208
377
|
widgets: Record<string, WidgetComponent>
|
|
209
378
|
callTool: (name: string, args: object) => Promise<unknown>
|
|
210
379
|
onExit: LayoutBuilderProps["onExit"]
|
|
380
|
+
builderLabels?: LayoutBuilderLabels
|
|
211
381
|
}
|
|
212
382
|
|
|
213
|
-
export function ViewBody({
|
|
383
|
+
export function ViewBody({
|
|
384
|
+
buildMode,
|
|
385
|
+
viewData,
|
|
386
|
+
widgets,
|
|
387
|
+
callTool,
|
|
388
|
+
onExit,
|
|
389
|
+
builderLabels,
|
|
390
|
+
}: ViewBodyProps) {
|
|
214
391
|
if (buildMode) {
|
|
215
392
|
return (
|
|
216
393
|
<LayoutBuilder
|
|
@@ -220,6 +397,7 @@ export function ViewBody({ buildMode, viewData, widgets, callTool, onExit }: Vie
|
|
|
220
397
|
initialSteps={viewData._refreshParams?.steps}
|
|
221
398
|
widgets={widgets}
|
|
222
399
|
callTool={callTool}
|
|
400
|
+
labels={builderLabels}
|
|
223
401
|
onExit={onExit}
|
|
224
402
|
/>
|
|
225
403
|
)
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The pre-payload states of `McpAppView`, one per lifecycle phase
|
|
3
|
+
* (`view-lifecycle.ts`): waiting (skeleton + status line + slow hint),
|
|
4
|
+
* cancelled, and error (icon, title, what to do, raw reason under "Details",
|
|
5
|
+
* retry when the originating tool is known). Hook-free and SSR-renderable, so
|
|
6
|
+
* each phase is pinned by a test.
|
|
7
|
+
*/
|
|
8
|
+
import { ErrorState } from "../components/ErrorState.js"
|
|
9
|
+
import { Skeleton } from "../primitives/skeleton.js"
|
|
10
|
+
import type { McpAppViewLabels } from "./mcp-app-view-chrome.js"
|
|
11
|
+
import type { ViewLifecycle } from "./view-lifecycle.js"
|
|
12
|
+
|
|
13
|
+
/** Human text for a raw error reason (e.g. map "503" to "the engine is down"). */
|
|
14
|
+
export type DescribeViewError = (
|
|
15
|
+
reason: string | undefined,
|
|
16
|
+
) => { title?: string; description?: string } | undefined
|
|
17
|
+
|
|
18
|
+
export function ViewPlaceholder({
|
|
19
|
+
lifecycle,
|
|
20
|
+
labels,
|
|
21
|
+
onRetry,
|
|
22
|
+
describeError,
|
|
23
|
+
}: {
|
|
24
|
+
/** The current phase; `result` renders nothing (the view takes over). */
|
|
25
|
+
lifecycle: ViewLifecycle
|
|
26
|
+
labels: Required<McpAppViewLabels>
|
|
27
|
+
onRetry: () => void
|
|
28
|
+
describeError?: DescribeViewError
|
|
29
|
+
}) {
|
|
30
|
+
switch (lifecycle.phase) {
|
|
31
|
+
case "result":
|
|
32
|
+
return null
|
|
33
|
+
case "cancelled":
|
|
34
|
+
return (
|
|
35
|
+
<div className="px-4 py-4">
|
|
36
|
+
<p role="status" className="text-muted-foreground text-sm">
|
|
37
|
+
{labels.cancelled}
|
|
38
|
+
</p>
|
|
39
|
+
</div>
|
|
40
|
+
)
|
|
41
|
+
case "error":
|
|
42
|
+
return (
|
|
43
|
+
<ViewErrorState
|
|
44
|
+
reason={lifecycle.reason}
|
|
45
|
+
canRetry={lifecycle.canRetry}
|
|
46
|
+
labels={labels}
|
|
47
|
+
onRetry={onRetry}
|
|
48
|
+
describeError={describeError}
|
|
49
|
+
/>
|
|
50
|
+
)
|
|
51
|
+
case "waiting":
|
|
52
|
+
return <ViewWaiting slow={lifecycle.slow} labels={labels} />
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function ViewWaiting({ slow, labels }: { slow: boolean; labels: Required<McpAppViewLabels> }) {
|
|
57
|
+
// No aria-busy on an ancestor of the live region: assistive technology may
|
|
58
|
+
// hold back changes inside a busy subtree, and the slow hint is one.
|
|
59
|
+
return (
|
|
60
|
+
<div className="flex flex-col gap-4 px-4 py-4">
|
|
61
|
+
<Skeleton aria-hidden="true" className="h-8 w-48" />
|
|
62
|
+
<Skeleton aria-hidden="true" className="h-32 w-full" />
|
|
63
|
+
{/* One live region: the status line, then the slow hint when it appears. */}
|
|
64
|
+
<div role="status" className="text-muted-foreground flex flex-col gap-1 text-sm">
|
|
65
|
+
<p>{labels.loading}</p>
|
|
66
|
+
{slow && <p data-slot="slow-hint">{labels.slowHint}</p>}
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function ViewErrorState({
|
|
73
|
+
reason,
|
|
74
|
+
canRetry,
|
|
75
|
+
labels,
|
|
76
|
+
onRetry,
|
|
77
|
+
describeError,
|
|
78
|
+
}: {
|
|
79
|
+
reason: string | undefined
|
|
80
|
+
canRetry: boolean
|
|
81
|
+
labels: Required<McpAppViewLabels>
|
|
82
|
+
onRetry: () => void
|
|
83
|
+
describeError?: DescribeViewError
|
|
84
|
+
}) {
|
|
85
|
+
const human = describeError?.(reason)
|
|
86
|
+
// The hint never promises a retry the state does not offer.
|
|
87
|
+
const hint = canRetry ? labels.errorHint : labels.errorHintNoRetry
|
|
88
|
+
return (
|
|
89
|
+
<div className="px-4 py-4">
|
|
90
|
+
<ErrorState
|
|
91
|
+
title={human?.title ?? labels.errorTitle}
|
|
92
|
+
description={human?.description ?? hint}
|
|
93
|
+
details={reason}
|
|
94
|
+
detailsLabel={labels.errorDetails}
|
|
95
|
+
retryLabel={labels.retry}
|
|
96
|
+
onRetry={canRetry ? onRetry : undefined}
|
|
97
|
+
/>
|
|
98
|
+
</div>
|
|
99
|
+
)
|
|
100
|
+
}
|