@miragon/mcp-toolkit-ui 2.6.0 → 2.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -5
- package/dist/app/adapt-data-widget.d.ts +1 -22
- package/dist/app/adapt-data-widget.d.ts.map +1 -1
- package/dist/app/adapt-data-widget.js +1 -35
- package/dist/app/adapt-data-widget.js.map +1 -1
- 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 +4 -2
- package/dist/app/builder/CanvasRow.d.ts.map +1 -1
- package/dist/app/builder/CanvasRow.js +29 -17
- 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 +14 -6
- package/dist/app/builder/CatalogueSheet.js.map +1 -1
- package/dist/app/builder/CellPropsSheet.d.ts +7 -3
- package/dist/app/builder/CellPropsSheet.d.ts.map +1 -1
- package/dist/app/builder/CellPropsSheet.js +10 -3
- package/dist/app/builder/CellPropsSheet.js.map +1 -1
- package/dist/app/builder/DiscardDialog.d.ts +18 -0
- package/dist/app/builder/DiscardDialog.d.ts.map +1 -0
- package/dist/app/builder/DiscardDialog.js +18 -0
- package/dist/app/builder/DiscardDialog.js.map +1 -0
- 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.d.ts +33 -9
- package/dist/app/builder/LayoutSurface.d.ts.map +1 -1
- package/dist/app/builder/LayoutSurface.js +74 -23
- package/dist/app/builder/LayoutSurface.js.map +1 -1
- package/dist/app/builder/PipelineStrip.d.ts +5 -2
- package/dist/app/builder/PipelineStrip.d.ts.map +1 -1
- package/dist/app/builder/PipelineStrip.js +33 -15
- package/dist/app/builder/PipelineStrip.js.map +1 -1
- package/dist/app/builder/SaveDialog.d.ts +9 -1
- package/dist/app/builder/SaveDialog.d.ts.map +1 -1
- package/dist/app/builder/SaveDialog.js +9 -3
- package/dist/app/builder/SaveDialog.js.map +1 -1
- package/dist/app/builder/Toolbar.d.ts +14 -4
- package/dist/app/builder/Toolbar.d.ts.map +1 -1
- package/dist/app/builder/Toolbar.js +24 -7
- package/dist/app/builder/Toolbar.js.map +1 -1
- package/dist/app/builder/Workspace.d.ts +23 -3
- package/dist/app/builder/Workspace.d.ts.map +1 -1
- package/dist/app/builder/Workspace.js +58 -8
- package/dist/app/builder/Workspace.js.map +1 -1
- package/dist/app/builder/builder-model.d.ts +146 -3
- package/dist/app/builder/builder-model.d.ts.map +1 -1
- package/dist/app/builder/builder-model.js +196 -5
- 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/reveal.d.ts +10 -0
- package/dist/app/builder/reveal.d.ts.map +1 -0
- package/dist/app/builder/reveal.js +10 -0
- package/dist/app/builder/reveal.js.map +1 -0
- 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 +5 -1
- package/dist/app/builder/use-builder-catalogue.d.ts.map +1 -1
- package/dist/app/builder/use-builder-catalogue.js +50 -17
- package/dist/app/builder/use-builder-catalogue.js.map +1 -1
- package/dist/app/builder/use-builder-session.d.ts +30 -0
- package/dist/app/builder/use-builder-session.d.ts.map +1 -0
- package/dist/app/builder/use-builder-session.js +58 -0
- package/dist/app/builder/use-builder-session.js.map +1 -0
- package/dist/app/builder/use-display-resolver.d.ts +17 -0
- package/dist/app/builder/use-display-resolver.d.ts.map +1 -0
- package/dist/app/builder/use-display-resolver.js +16 -0
- package/dist/app/builder/use-display-resolver.js.map +1 -0
- 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 +8 -3
- package/dist/app/index.d.ts.map +1 -1
- package/dist/app/index.js +8 -2
- package/dist/app/index.js.map +1 -1
- package/dist/app/layout-builder.d.ts +18 -25
- package/dist/app/layout-builder.d.ts.map +1 -1
- package/dist/app/layout-builder.js +84 -78
- package/dist/app/layout-builder.js.map +1 -1
- package/dist/app/mcp-app-view-chrome.d.ts +103 -20
- package/dist/app/mcp-app-view-chrome.d.ts.map +1 -1
- package/dist/app/mcp-app-view-chrome.js +125 -40
- package/dist/app/mcp-app-view-chrome.js.map +1 -1
- package/dist/app/mcp-app-view-states.d.ts +23 -0
- package/dist/app/mcp-app-view-states.d.ts.map +1 -0
- package/dist/app/mcp-app-view-states.js +55 -0
- package/dist/app/mcp-app-view-states.js.map +1 -0
- package/dist/app/mcp-app-view.d.ts +72 -5
- package/dist/app/mcp-app-view.d.ts.map +1 -1
- package/dist/app/mcp-app-view.js +241 -141
- package/dist/app/mcp-app-view.js.map +1 -1
- package/dist/app/mcp-toolkit-app.d.ts +8 -2
- package/dist/app/mcp-toolkit-app.d.ts.map +1 -1
- package/dist/app/mcp-toolkit-app.js +33 -4
- package/dist/app/mcp-toolkit-app.js.map +1 -1
- package/dist/app/mcp-use-host-bridge.d.ts +15 -6
- package/dist/app/mcp-use-host-bridge.d.ts.map +1 -1
- package/dist/app/mcp-use-host-bridge.js +51 -22
- package/dist/app/mcp-use-host-bridge.js.map +1 -1
- package/dist/app/model-context.d.ts +71 -0
- package/dist/app/model-context.d.ts.map +1 -0
- package/dist/app/model-context.js +101 -0
- package/dist/app/model-context.js.map +1 -0
- package/dist/app/use-build-mode-focus.d.ts +33 -0
- package/dist/app/use-build-mode-focus.d.ts.map +1 -0
- package/dist/app/use-build-mode-focus.js +59 -0
- package/dist/app/use-build-mode-focus.js.map +1 -0
- package/dist/app/use-view-actions.d.ts +31 -0
- package/dist/app/use-view-actions.d.ts.map +1 -0
- package/dist/app/use-view-actions.js +81 -0
- package/dist/app/use-view-actions.js.map +1 -0
- package/dist/app/use-view-lifecycle.d.ts +42 -0
- package/dist/app/use-view-lifecycle.d.ts.map +1 -0
- package/dist/app/use-view-lifecycle.js +81 -0
- package/dist/app/use-view-lifecycle.js.map +1 -0
- package/dist/app/view-lifecycle.d.ts +185 -0
- package/dist/app/view-lifecycle.d.ts.map +1 -0
- package/dist/app/view-lifecycle.js +207 -0
- package/dist/app/view-lifecycle.js.map +1 -0
- package/dist/app/widget-fixture.d.ts +10 -3
- package/dist/app/widget-fixture.d.ts.map +1 -1
- package/dist/app/widget-fixture.js +21 -8
- package/dist/app/widget-fixture.js.map +1 -1
- package/dist/app/widget-props-sheet.d.ts +6 -10
- package/dist/app/widget-props-sheet.d.ts.map +1 -1
- package/dist/app/widget-props-sheet.js +25 -20
- 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 +9 -6
- package/dist/components/DrillButton.d.ts.map +1 -1
- package/dist/components/DrillButton.js +9 -6
- 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 +19 -0
- package/dist/components/EmptyState.js.map +1 -0
- package/dist/components/ErrorState.d.ts +53 -0
- package/dist/components/ErrorState.d.ts.map +1 -0
- package/dist/components/ErrorState.js +77 -0
- package/dist/components/ErrorState.js.map +1 -0
- package/dist/components/FilterBar.d.ts +16 -5
- package/dist/components/FilterBar.d.ts.map +1 -1
- package/dist/components/FilterBar.js +13 -5
- 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 +70 -18
- package/dist/components/KpiGrid.d.ts.map +1 -1
- package/dist/components/KpiGrid.js +112 -23
- package/dist/components/KpiGrid.js.map +1 -1
- package/dist/components/ListFooter.d.ts +32 -4
- package/dist/components/ListFooter.d.ts.map +1 -1
- package/dist/components/ListFooter.js +35 -6
- package/dist/components/ListFooter.js.map +1 -1
- package/dist/components/PagedListFooter.d.ts +57 -0
- package/dist/components/PagedListFooter.d.ts.map +1 -0
- package/dist/components/PagedListFooter.js +27 -0
- package/dist/components/PagedListFooter.js.map +1 -0
- package/dist/components/Pills.d.ts +36 -12
- package/dist/components/Pills.d.ts.map +1 -1
- package/dist/components/Pills.js +29 -8
- package/dist/components/Pills.js.map +1 -1
- package/dist/components/SectionHeading.d.ts +3 -1
- package/dist/components/SectionHeading.d.ts.map +1 -1
- package/dist/components/SectionHeading.js +6 -2
- package/dist/components/SectionHeading.js.map +1 -1
- package/dist/components/Toast.d.ts +43 -0
- package/dist/components/Toast.d.ts.map +1 -0
- package/dist/components/Toast.js +106 -0
- package/dist/components/Toast.js.map +1 -0
- package/dist/components/WidgetHeader.d.ts +34 -10
- package/dist/components/WidgetHeader.d.ts.map +1 -1
- package/dist/components/WidgetHeader.js +11 -6
- package/dist/components/WidgetHeader.js.map +1 -1
- package/dist/hooks/index.d.ts +4 -4
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +1 -1
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/tool-result-recovery-model.d.ts +155 -0
- package/dist/hooks/tool-result-recovery-model.d.ts.map +1 -0
- package/dist/hooks/tool-result-recovery-model.js +273 -0
- package/dist/hooks/tool-result-recovery-model.js.map +1 -0
- package/dist/hooks/use-tool-query.d.ts +56 -14
- package/dist/hooks/use-tool-query.d.ts.map +1 -1
- package/dist/hooks/use-tool-query.js +102 -25
- package/dist/hooks/use-tool-query.js.map +1 -1
- package/dist/hooks/use-tool-result-recovery.d.ts +61 -44
- package/dist/hooks/use-tool-result-recovery.d.ts.map +1 -1
- package/dist/hooks/use-tool-result-recovery.js +62 -110
- package/dist/hooks/use-tool-result-recovery.js.map +1 -1
- package/dist/hooks/use-view-data.d.ts +55 -7
- package/dist/hooks/use-view-data.d.ts.map +1 -1
- package/dist/hooks/use-view-data.js +81 -11
- package/dist/hooks/use-view-data.js.map +1 -1
- package/dist/hooks/use-view-tool-query.d.ts +12 -7
- package/dist/hooks/use-view-tool-query.d.ts.map +1 -1
- package/dist/hooks/use-view-tool-query.js +15 -14
- package/dist/hooks/use-view-tool-query.js.map +1 -1
- package/dist/index.d.ts +27 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +29 -7
- package/dist/index.js.map +1 -1
- package/dist/lib/focus-return.d.ts +61 -0
- package/dist/lib/focus-return.d.ts.map +1 -0
- package/dist/lib/focus-return.js +46 -0
- package/dist/lib/focus-return.js.map +1 -0
- 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/parse-tool-result.d.ts +23 -1
- package/dist/lib/parse-tool-result.d.ts.map +1 -1
- package/dist/lib/parse-tool-result.js +38 -2
- package/dist/lib/parse-tool-result.js.map +1 -1
- package/dist/lib/toast-model.d.ts +86 -0
- package/dist/lib/toast-model.d.ts.map +1 -0
- package/dist/lib/toast-model.js +102 -0
- package/dist/lib/toast-model.js.map +1 -0
- package/dist/lib/tone-utils.d.ts +95 -9
- package/dist/lib/tone-utils.d.ts.map +1 -1
- package/dist/lib/tone-utils.js +100 -9
- package/dist/lib/tone-utils.js.map +1 -1
- package/dist/lib/tool-query.d.ts +107 -0
- package/dist/lib/tool-query.d.ts.map +1 -0
- package/dist/lib/tool-query.js +182 -0
- package/dist/lib/tool-query.js.map +1 -0
- package/dist/lib/tool-result-observer.d.ts +108 -0
- package/dist/lib/tool-result-observer.d.ts.map +1 -0
- package/dist/lib/tool-result-observer.js +158 -0
- package/dist/lib/tool-result-observer.js.map +1 -0
- package/dist/lib/typography.d.ts +37 -0
- package/dist/lib/typography.d.ts.map +1 -0
- package/dist/lib/typography.js +41 -0
- package/dist/lib/typography.js.map +1 -0
- package/dist/lib/utils.d.ts.map +1 -1
- package/dist/lib/utils.js +11 -1
- package/dist/lib/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 +336 -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 +185 -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 +184 -0
- package/dist/locale/messages-en.js.map +1 -0
- package/dist/locale/toolkit-messages.d.ts +57 -0
- package/dist/locale/toolkit-messages.d.ts.map +1 -0
- package/dist/locale/toolkit-messages.js +103 -0
- package/dist/locale/toolkit-messages.js.map +1 -0
- package/dist/primitives/alert.d.ts +1 -1
- package/dist/primitives/alert.d.ts.map +1 -1
- package/dist/primitives/alert.js +6 -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 +19 -6
- package/dist/primitives/badge.js.map +1 -1
- package/dist/primitives/button.d.ts +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 +22 -8
- 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 +6 -3
- 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 +17 -7
- 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/providers/query-provider.d.ts +32 -1
- package/dist/providers/query-provider.d.ts.map +1 -1
- package/dist/providers/query-provider.js +53 -4
- package/dist/providers/query-provider.js.map +1 -1
- package/dist/render-cases-primitives.js +1 -1
- package/dist/render-cases-primitives.js.map +1 -1
- package/dist/render-cases.d.ts.map +1 -1
- package/dist/render-cases.js +48 -6
- package/dist/render-cases.js.map +1 -1
- package/dist/render-effects.d.ts +42 -0
- package/dist/render-effects.d.ts.map +1 -0
- package/dist/render-effects.js +81 -0
- package/dist/render-effects.js.map +1 -0
- 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 +214 -39
- package/dist/theme/create-theme.d.ts.map +1 -1
- package/dist/theme/create-theme.js +204 -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 +11 -5
- package/src/app/adapt-data-widget.tsx +2 -42
- package/src/app/builder/BuilderBanners.tsx +26 -13
- package/src/app/builder/CanvasRow.tsx +79 -32
- package/src/app/builder/CatalogueSheet.tsx +58 -35
- package/src/app/builder/CellPropsSheet.tsx +17 -2
- package/src/app/builder/DiscardDialog.tsx +59 -0
- package/src/app/builder/EmptyHint.tsx +16 -11
- package/src/app/builder/LayoutSurface.tsx +207 -67
- package/src/app/builder/PipelineStrip.tsx +265 -156
- package/src/app/builder/SaveDialog.tsx +25 -14
- package/src/app/builder/Toolbar.tsx +154 -57
- package/src/app/builder/Workspace.tsx +226 -56
- 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 +346 -6
- package/src/app/builder/builder-model.ux.test.ts +456 -0
- package/src/app/builder/builder-ux.test.tsx +844 -0
- package/src/app/builder/labels.ts +15 -121
- package/src/app/builder/reveal.ts +10 -0
- package/src/app/builder/status-chip.tsx +8 -4
- package/src/app/builder/use-builder-catalogue.ts +62 -19
- package/src/app/builder/use-builder-session.ts +68 -0
- package/src/app/builder/use-display-resolver.ts +24 -0
- package/src/app/builder/use-save-dashboard.ts +5 -0
- package/src/app/index.ts +20 -3
- package/src/app/layout-builder.tsx +193 -186
- package/src/app/mcp-app-view-chrome.test.tsx +487 -14
- package/src/app/mcp-app-view-chrome.tsx +328 -100
- package/src/app/mcp-app-view-states.tsx +140 -0
- package/src/app/mcp-app-view.effects.test.tsx +292 -0
- package/src/app/mcp-app-view.test.tsx +478 -0
- package/src/app/mcp-app-view.tsx +447 -190
- package/src/app/mcp-toolkit-app.test.tsx +40 -0
- package/src/app/mcp-toolkit-app.tsx +39 -8
- package/src/app/mcp-use-host-bridge.test.tsx +180 -0
- package/src/app/mcp-use-host-bridge.tsx +67 -24
- package/src/app/model-context.test.tsx +116 -0
- package/src/app/model-context.tsx +125 -0
- package/src/app/use-build-mode-focus.ts +62 -0
- package/src/app/use-view-actions.ts +89 -0
- package/src/app/use-view-lifecycle.ts +122 -0
- package/src/app/view-lifecycle.test.ts +443 -0
- package/src/app/view-lifecycle.ts +334 -0
- package/src/app/widget-fixture.effects.test.tsx +51 -0
- package/src/app/widget-fixture.render.test.tsx +81 -0
- package/src/app/widget-fixture.tsx +34 -11
- package/src/app/widget-props-sheet.tsx +67 -39
- package/src/app/widget-renderer.render.test.tsx +131 -0
- package/src/app/widget-renderer.tsx +136 -38
- package/src/components/DrillButton.tsx +24 -10
- package/src/components/EmptyState.tsx +83 -0
- package/src/components/ErrorState.tsx +204 -0
- package/src/components/FilterBar.test.tsx +82 -0
- package/src/components/FilterBar.tsx +56 -27
- package/src/components/GroupCard.tsx +3 -2
- package/src/components/KpiGrid.test.tsx +279 -0
- package/src/components/KpiGrid.tsx +329 -91
- package/src/components/ListFooter.test.tsx +116 -0
- package/src/components/ListFooter.tsx +110 -12
- package/src/components/PagedListFooter.test.tsx +140 -0
- package/src/components/PagedListFooter.tsx +107 -0
- package/src/components/Pills.test.tsx +77 -0
- package/src/components/Pills.tsx +78 -16
- package/src/components/SectionHeading.tsx +9 -3
- package/src/components/Toast.tsx +206 -0
- package/src/components/WidgetHeader.test.tsx +78 -0
- package/src/components/WidgetHeader.tsx +63 -16
- package/src/components/feedback-states.test.tsx +314 -0
- package/src/components/tone-figures.test.tsx +58 -0
- package/src/fonts.css +19 -0
- package/src/globals.css +9 -165
- package/src/hooks/index.ts +6 -3
- package/src/hooks/tool-result-recovery-model.test.ts +744 -0
- package/src/hooks/tool-result-recovery-model.ts +418 -0
- package/src/hooks/use-tool-query.effects.test.tsx +205 -0
- package/src/hooks/use-tool-query.ts +156 -33
- package/src/hooks/use-tool-result-recovery.effects.test.tsx +141 -0
- package/src/hooks/use-tool-result-recovery.test.ts +28 -5
- package/src/hooks/use-tool-result-recovery.ts +101 -131
- package/src/hooks/use-view-data.test.tsx +169 -0
- package/src/hooks/use-view-data.ts +131 -10
- package/src/hooks/use-view-tool-query.ts +25 -19
- package/src/i18n-coverage.test.tsx +425 -0
- package/src/index.ts +102 -13
- package/src/lib/focus-return.test.ts +288 -0
- package/src/lib/focus-return.ts +96 -0
- 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/parse-tool-result.test.ts +46 -1
- package/src/lib/parse-tool-result.ts +40 -2
- package/src/lib/toast-model.test.ts +233 -0
- package/src/lib/toast-model.ts +165 -0
- package/src/lib/tone-utils.test.ts +125 -16
- package/src/lib/tone-utils.ts +144 -15
- package/src/lib/tool-query.test.ts +569 -0
- package/src/lib/tool-query.ts +253 -0
- package/src/lib/tool-result-observer.test.ts +296 -0
- package/src/lib/tool-result-observer.ts +211 -0
- package/src/lib/typography.test.ts +45 -0
- package/src/lib/typography.ts +47 -0
- package/src/lib/utils.ts +12 -1
- package/src/locale/index.ts +14 -1
- package/src/locale/locale-provider.test.tsx +87 -0
- package/src/locale/locale-provider.tsx +90 -3
- package/src/locale/message-types.ts +337 -0
- package/src/locale/messages-de.ts +198 -0
- package/src/locale/messages-en.ts +196 -0
- package/src/locale/toolkit-messages.test.ts +349 -0
- package/src/locale/toolkit-messages.ts +143 -0
- package/src/no-hardcoded-copy.test.ts +432 -0
- package/src/primitives/alert.tsx +7 -2
- package/src/primitives/badge.tsx +19 -7
- package/src/primitives/button.tsx +13 -8
- package/src/primitives/card.tsx +4 -4
- package/src/primitives/dialog.tsx +42 -8
- package/src/primitives/input.tsx +5 -3
- package/src/primitives/scroll-area.tsx +1 -1
- package/src/primitives/select.tsx +7 -4
- package/src/primitives/sheet.tsx +33 -6
- package/src/primitives/skeleton.tsx +1 -1
- package/src/primitives/switch.tsx +4 -2
- package/src/primitives/tabs.tsx +9 -6
- package/src/providers/query-provider.test.tsx +154 -0
- package/src/providers/query-provider.tsx +66 -5
- package/src/render-cases-primitives.tsx +1 -1
- package/src/render-cases.tsx +82 -6
- package/src/render-effects.tsx +96 -0
- package/src/render-scan.ts +75 -0
- package/src/theme/cd-tokens.generated.css +48 -0
- package/src/theme/cd-tokens.test.ts +109 -0
- package/src/theme/cd-tokens.ts +67 -0
- package/src/theme/create-theme.test.ts +253 -5
- package/src/theme/create-theme.ts +341 -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 +619 -0
- package/src/theme/theme-provider.test.tsx +82 -0
- package/src/theme/theme-provider.tsx +60 -24
- package/src/theme.css +344 -0
- package/ui-catalog.json +783 -49
|
@@ -0,0 +1,478 @@
|
|
|
1
|
+
import type { ReactNode } from "react"
|
|
2
|
+
import { renderToStaticMarkup } from "react-dom/server"
|
|
3
|
+
import { QueryClient, useQueryClient } from "@tanstack/react-query"
|
|
4
|
+
import { ToolCancelledError, ToolError } from "mcp-use/react"
|
|
5
|
+
import { beforeEach, describe, expect, it, vi } from "vitest"
|
|
6
|
+
import { useToolQueryKey } from "../hooks/use-tool-query.js"
|
|
7
|
+
import { AppQueryProvider, queryClient } from "../providers/query-provider.js"
|
|
8
|
+
import type {
|
|
9
|
+
ToolResultRecoveryOptions,
|
|
10
|
+
ToolResultRecoveryState,
|
|
11
|
+
} from "../hooks/use-tool-result-recovery.js"
|
|
12
|
+
import { LocaleProvider, useLocale } from "../locale/locale-provider.js"
|
|
13
|
+
import { TOOLKIT_MESSAGES, type ToolkitMessages } from "../locale/toolkit-messages.js"
|
|
14
|
+
import { attribute, decodeEntities, openingTags, userFacingTexts } from "../render-scan.js"
|
|
15
|
+
import { createStandaloneHostBridge, HostBridgeProvider } from "./host-bridge.js"
|
|
16
|
+
import { McpAppView } from "./mcp-app-view.js"
|
|
17
|
+
import type { ViewData } from "./mcp-app-view-chrome.js"
|
|
18
|
+
import { McpUseViewContext } from "./model-context.js"
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* McpAppView's wiring, rendered through its real hooks (SSR, the house path):
|
|
22
|
+
* `useToolContext().status` → `useViewLifecycle` → the placeholder, the
|
|
23
|
+
* locale fallback, and the provider order behind the Lucide stroke. The
|
|
24
|
+
* mcp-use view hooks are the only mock; every phase here resolves on the
|
|
25
|
+
* first render, so no effect has to run. Guards toolkit#176 K14: an isError
|
|
26
|
+
* result must never fall back to the endless skeleton.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/** What the mocked mcp-use view hooks return on the next render. */
|
|
30
|
+
interface HostState {
|
|
31
|
+
tool: Record<string, unknown>
|
|
32
|
+
locale: string
|
|
33
|
+
hostContext: Record<string, unknown> | undefined
|
|
34
|
+
/** Replaces the recovery's state (it settles in effects, which SSR never runs). */
|
|
35
|
+
recovery: ToolResultRecoveryState<unknown> | null
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const host: HostState = vi.hoisted(() => ({
|
|
39
|
+
tool: {},
|
|
40
|
+
locale: "en-US",
|
|
41
|
+
hostContext: undefined,
|
|
42
|
+
recovery: null,
|
|
43
|
+
}))
|
|
44
|
+
|
|
45
|
+
// The real hook always runs (hook order stays fixed); a fixture state stands
|
|
46
|
+
// in for what its controller decides once the stripped result arrived.
|
|
47
|
+
vi.mock(import("../hooks/use-tool-result-recovery.js"), async (importOriginal) => {
|
|
48
|
+
const actual = await importOriginal()
|
|
49
|
+
return {
|
|
50
|
+
...actual,
|
|
51
|
+
useToolResultRecovery: <T,>(options: ToolResultRecoveryOptions) => {
|
|
52
|
+
const state = actual.useToolResultRecovery<T>(options)
|
|
53
|
+
return (host.recovery as ToolResultRecoveryState<T> | null) ?? state
|
|
54
|
+
},
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
// Only the fields McpAppView reads; the casts narrow the fixtures to the hook types.
|
|
59
|
+
vi.mock(import("mcp-use/react"), async (importOriginal) => {
|
|
60
|
+
const actual = await importOriginal()
|
|
61
|
+
const useToolContext = () => host.tool
|
|
62
|
+
const useHostContext = () => ({
|
|
63
|
+
locale: host.locale,
|
|
64
|
+
timeZone: "UTC",
|
|
65
|
+
safeArea: { top: 0, right: 0, bottom: 0, left: 0 },
|
|
66
|
+
hostContext: host.hostContext,
|
|
67
|
+
})
|
|
68
|
+
// The view's model-context node, made visible: a <meta> whose `content`
|
|
69
|
+
// the scans below never read as user-facing text.
|
|
70
|
+
const { createElement } = await import("react")
|
|
71
|
+
const ModelContext = ({ content }: { content: string }) =>
|
|
72
|
+
createElement("meta", { "data-model-context": content })
|
|
73
|
+
return {
|
|
74
|
+
...actual,
|
|
75
|
+
ModelContext: ModelContext as unknown as typeof actual.ModelContext,
|
|
76
|
+
useToolContext: useToolContext as unknown as typeof actual.useToolContext,
|
|
77
|
+
useHostContext: useHostContext as unknown as typeof actual.useHostContext,
|
|
78
|
+
useDisplayMode: () => ({
|
|
79
|
+
displayMode: "inline",
|
|
80
|
+
availableDisplayModes: ["inline", "fullscreen"],
|
|
81
|
+
requestDisplayMode: () => Promise.resolve(),
|
|
82
|
+
}),
|
|
83
|
+
}
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
// No toast can be raised during SSR, so the provider renders one fixture
|
|
87
|
+
// toast where McpAppView mounts it: the stroke its icons inherit is the
|
|
88
|
+
// stroke a real toast stack gets.
|
|
89
|
+
vi.mock(import("../components/Toast.js"), async (importOriginal) => {
|
|
90
|
+
const actual = await importOriginal()
|
|
91
|
+
const { createElement, Fragment } = await import("react")
|
|
92
|
+
const { createToast } = await import("../lib/toast-model.js")
|
|
93
|
+
const fixture = [createToast(1, { tone: "danger", title: "Fixture toast" })]
|
|
94
|
+
return {
|
|
95
|
+
...actual,
|
|
96
|
+
ToastProvider: ({ children }: { children: ReactNode }) =>
|
|
97
|
+
createElement(
|
|
98
|
+
Fragment,
|
|
99
|
+
null,
|
|
100
|
+
children,
|
|
101
|
+
createElement(actual.ToastViewport, { toasts: fixture, onDismiss: () => {} }),
|
|
102
|
+
),
|
|
103
|
+
}
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
const en = TOOLKIT_MESSAGES.en.view
|
|
107
|
+
const de = TOOLKIT_MESSAGES.de.view
|
|
108
|
+
const KNOWN_TOOL = { toolInfo: { tool: { name: "render-view" } } }
|
|
109
|
+
const REASON = "[503 ProcessEngineException] Engine is shutting down"
|
|
110
|
+
|
|
111
|
+
const bridge = createStandaloneHostBridge({ callTool: () => Promise.resolve({}) })
|
|
112
|
+
|
|
113
|
+
/** A consumer widget that prints the locale it reads. */
|
|
114
|
+
function LocaleProbe() {
|
|
115
|
+
return <span data-testid="probe">widget-locale:{useLocale()}</span>
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const VIEW: ViewData = {
|
|
119
|
+
title: "Process definitions",
|
|
120
|
+
context: {
|
|
121
|
+
keys: {},
|
|
122
|
+
stepIds: ["kpi"],
|
|
123
|
+
stepData: {},
|
|
124
|
+
errors: [{ stepId: "kpi", reason: "timeout" }],
|
|
125
|
+
},
|
|
126
|
+
layout: { rows: [{ row: [{ widget: "demo:probe" }] }] },
|
|
127
|
+
_refreshParams: { layout: { rows: [] }, steps: [{ id: "kpi", step: "demo:kpis" }] },
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Render McpAppView; `view` is the shell, `toasts` the toast stack after it. */
|
|
131
|
+
function render(wrap: (node: ReactNode) => ReactNode = (node) => node) {
|
|
132
|
+
const html = renderToStaticMarkup(
|
|
133
|
+
<HostBridgeProvider bridge={bridge}>
|
|
134
|
+
{wrap(<McpAppView widgets={{ "demo:probe": LocaleProbe }} />)}
|
|
135
|
+
</HostBridgeProvider>,
|
|
136
|
+
)
|
|
137
|
+
const at = html.indexOf('<div data-slot="toast-viewport"')
|
|
138
|
+
expect(at, "the fixture toast stack renders").toBeGreaterThan(0)
|
|
139
|
+
return { html, view: html.slice(0, at), toasts: html.slice(at) }
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const strokes = (html: string) => openingTags(html, "svg").map((t) => attribute(t, "stroke-width"))
|
|
143
|
+
|
|
144
|
+
const toolError = (text: string) =>
|
|
145
|
+
new ToolError({ isError: true, content: [{ type: "text", text }] })
|
|
146
|
+
|
|
147
|
+
beforeEach(() => {
|
|
148
|
+
host.tool = { status: "pending", toolInput: undefined, toolOutput: undefined }
|
|
149
|
+
host.locale = "en-US"
|
|
150
|
+
host.hostContext = KNOWN_TOOL
|
|
151
|
+
host.recovery = null
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
/** A tool the host names, with its annotations (SEP-1865 `toolInfo.tool`). */
|
|
155
|
+
const toolInfo = (name: string, annotations: Record<string, unknown>) => ({
|
|
156
|
+
toolInfo: { tool: { name, annotations } },
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
/** The recovery's state when a needed recovery did not run. */
|
|
160
|
+
const blockedBy = (reason: "policy" | "no-tool"): ToolResultRecoveryState<unknown> => ({
|
|
161
|
+
data: null,
|
|
162
|
+
failed: false,
|
|
163
|
+
blocked: reason,
|
|
164
|
+
recoveredAt: null,
|
|
165
|
+
args: null,
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
describe("McpAppView lifecycle wiring (toolkit#176)", () => {
|
|
169
|
+
it("an isError rendering result shows the error state with a retry, never the skeleton (K14)", () => {
|
|
170
|
+
host.tool = { status: "error", error: toolError(REASON), toolInput: {} }
|
|
171
|
+
const { view } = render()
|
|
172
|
+
expect(view).not.toContain('data-slot="skeleton"')
|
|
173
|
+
expect(view).not.toContain(en.loading)
|
|
174
|
+
expect(view).toContain('role="alert"')
|
|
175
|
+
expect(decodeEntities(view)).toContain(en.errorTitle)
|
|
176
|
+
expect(view).toContain(en.errorHint)
|
|
177
|
+
expect(view).toContain(`${en.retry}</button>`)
|
|
178
|
+
// The raw reason informs under Details; it is not the message.
|
|
179
|
+
expect(view).toMatch(/<details[^>]*><summary[^>]*>Details<\/summary><pre[^>]*>\[503/)
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
it("without the originating tool the error offers no retry and promises none", () => {
|
|
183
|
+
host.tool = { status: "error", error: toolError(REASON), toolInput: {} }
|
|
184
|
+
host.hostContext = undefined
|
|
185
|
+
const { view } = render()
|
|
186
|
+
expect(view).toContain('role="alert"')
|
|
187
|
+
expect(view).not.toContain("<button")
|
|
188
|
+
expect(view).toContain(en.errorHintNoRetry)
|
|
189
|
+
expect(view).not.toContain(en.errorHint)
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
it("a latched result without a view on such a host is an error, not an endless wait", () => {
|
|
193
|
+
host.tool = { status: "ready", toolOutput: { notAView: true }, toolInput: {} }
|
|
194
|
+
host.hostContext = undefined
|
|
195
|
+
const { view } = render()
|
|
196
|
+
expect(view).not.toContain('data-slot="skeleton"')
|
|
197
|
+
expect(view).toContain('role="alert"')
|
|
198
|
+
expect(view).toContain(en.errorHintNoRetry)
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
it("a stripped result of a writing tool the policy kept: the notice, never a button that writes again", () => {
|
|
202
|
+
// claude.ai strips the result of a "start and show" tool; "auto" never
|
|
203
|
+
// re-runs a tool reported as writing (readOnlyHint: false).
|
|
204
|
+
host.hostContext = toolInfo("start_and_show", { readOnlyHint: false })
|
|
205
|
+
host.recovery = blockedBy("policy")
|
|
206
|
+
const view = decodeEntities(render().view)
|
|
207
|
+
expect(view).not.toContain('data-slot="skeleton"')
|
|
208
|
+
expect(view).toContain('data-tone="info"')
|
|
209
|
+
expect(view).toContain(en.blockedTitle)
|
|
210
|
+
expect(view).toContain(en.blockedHintNoRetry)
|
|
211
|
+
expect(view).not.toContain(en.errorTitle)
|
|
212
|
+
expect(view).not.toContain("<button")
|
|
213
|
+
// The same for destructiveHint: true.
|
|
214
|
+
host.hostContext = toolInfo("start_and_show", { readOnlyHint: true, destructiveHint: true })
|
|
215
|
+
expect(render().view).not.toContain("<button")
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
it("a tool not reported as writing that a stricter policy kept: the notice offers to load the view", () => {
|
|
219
|
+
host.hostContext = toolInfo("show_list", {})
|
|
220
|
+
host.recovery = blockedBy("policy")
|
|
221
|
+
const view = decodeEntities(render().view)
|
|
222
|
+
expect(view).toContain(en.blockedTitle)
|
|
223
|
+
expect(view).toContain(en.blockedHint)
|
|
224
|
+
expect(view).toContain(`${en.blockedRetry}</button>`)
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
it("a recovery blocked for want of a tool name stays the plain error without retry", () => {
|
|
228
|
+
host.hostContext = undefined
|
|
229
|
+
host.recovery = blockedBy("no-tool")
|
|
230
|
+
const view = decodeEntities(render().view)
|
|
231
|
+
expect(view).toContain(en.errorTitle)
|
|
232
|
+
expect(view).toContain(en.errorHintNoRetry)
|
|
233
|
+
expect(view).not.toContain(en.blockedTitle)
|
|
234
|
+
expect(view).not.toContain("<button")
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
it("a failed recovery stays the plain error with its retry", () => {
|
|
238
|
+
host.recovery = { ...blockedBy("policy"), blocked: null, failed: true }
|
|
239
|
+
const view = decodeEntities(render().view)
|
|
240
|
+
expect(view).toContain(en.errorTitle)
|
|
241
|
+
expect(view).toContain(`${en.retry}</button>`)
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
it("a payload the view re-loaded itself carries the stamp under its title, in the host's words (N23)", () => {
|
|
245
|
+
const recoveredAt = Date.UTC(2026, 9, 11, 14, 32)
|
|
246
|
+
host.recovery = { data: VIEW, failed: false, blocked: null, recoveredAt, args: {} }
|
|
247
|
+
const view = decodeEntities(render().view)
|
|
248
|
+
expect(view).toContain(
|
|
249
|
+
`<p data-slot="view-data-as-of" class="text-muted-foreground truncate text-sm">Reloaded at 2:32 PM</p>`,
|
|
250
|
+
)
|
|
251
|
+
host.locale = "de-DE"
|
|
252
|
+
expect(decodeEntities(render().view)).toContain(">Neu geladen um 14:32 Uhr</p>")
|
|
253
|
+
// A host-delivered payload carries none.
|
|
254
|
+
host.recovery = null
|
|
255
|
+
host.tool = { status: "ready", toolOutput: VIEW, toolInput: {} }
|
|
256
|
+
expect(render().view).not.toContain('data-slot="view-data-as-of"')
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
it("the stamp's time speaks the language of its words, not the host's other one", () => {
|
|
260
|
+
const recoveredAt = Date.UTC(2026, 9, 11, 14, 32)
|
|
261
|
+
host.recovery = { data: VIEW, failed: false, blocked: null, recoveredAt, args: {} }
|
|
262
|
+
// A German profile (LocaleProvider) on an en-US host: never "4:32 PM Uhr".
|
|
263
|
+
host.locale = "en-US"
|
|
264
|
+
const german = render((node) => <LocaleProvider locale="de">{node}</LocaleProvider>)
|
|
265
|
+
expect(decodeEntities(german.view)).toContain(">Neu geladen um 14:32 Uhr</p>")
|
|
266
|
+
// The host's region of the same language stays: en-GB keeps its 24-hour clock.
|
|
267
|
+
host.locale = "en-GB"
|
|
268
|
+
const british = render((node) => <LocaleProvider locale="en">{node}</LocaleProvider>)
|
|
269
|
+
expect(decodeEntities(british.view)).toContain(">Reloaded at 14:32</p>")
|
|
270
|
+
})
|
|
271
|
+
|
|
272
|
+
it("a host cancellation shows the cancelled notice, no skeleton and no retry", () => {
|
|
273
|
+
host.tool = { status: "error", error: new ToolCancelledError("user"), toolInput: {} }
|
|
274
|
+
const { view } = render()
|
|
275
|
+
expect(view).toContain(en.cancelled)
|
|
276
|
+
expect(view).not.toContain('role="alert"')
|
|
277
|
+
expect(view).not.toContain('data-slot="skeleton"')
|
|
278
|
+
expect(view).not.toContain("<button")
|
|
279
|
+
})
|
|
280
|
+
|
|
281
|
+
it("an open call waits: skeleton and status line in a stable, focusable frame", () => {
|
|
282
|
+
const { view } = render()
|
|
283
|
+
expect(view).toContain('data-slot="skeleton"')
|
|
284
|
+
expect(view).toMatch(/role="status"[^>]*>.*Loading view…/)
|
|
285
|
+
const frame = openingTags(view, "div")[0] ?? ""
|
|
286
|
+
expect(attribute(frame, "data-slot")).toBe("mcp-app-view")
|
|
287
|
+
expect(attribute(frame, "tabindex")).toBe("-1")
|
|
288
|
+
})
|
|
289
|
+
|
|
290
|
+
it("a complete payload renders the view: toolbar, step alert, widgets", () => {
|
|
291
|
+
host.tool = { status: "ready", toolOutput: VIEW, toolInput: {} }
|
|
292
|
+
const { view } = render()
|
|
293
|
+
expect(view).not.toContain('data-slot="skeleton"')
|
|
294
|
+
expect(view).toContain('data-slot="view-toolbar"')
|
|
295
|
+
expect(view).toContain(`aria-label="${en.refresh}"`)
|
|
296
|
+
expect(decodeEntities(view)).toContain(en.stepErrorsTitle)
|
|
297
|
+
expect(view).toContain("widget-locale:")
|
|
298
|
+
// The frame stays the same element across placeholder and view.
|
|
299
|
+
expect(attribute(openingTags(view, "div")[0] ?? "", "data-slot")).toBe("mcp-app-view")
|
|
300
|
+
})
|
|
301
|
+
})
|
|
302
|
+
|
|
303
|
+
describe("McpAppView toolNameInQueryKey (toolkit#176 K18)", () => {
|
|
304
|
+
/** A widget that prints the cache key its data hooks would use. */
|
|
305
|
+
function KeyProbe() {
|
|
306
|
+
return <output>{JSON.stringify(useToolQueryKey(["feed"], "feed_data", { id: 1 }))}</output>
|
|
307
|
+
}
|
|
308
|
+
const keyIn = (props: { toolNameInQueryKey?: boolean }) =>
|
|
309
|
+
decodeEntities(
|
|
310
|
+
renderToStaticMarkup(
|
|
311
|
+
<HostBridgeProvider bridge={bridge}>
|
|
312
|
+
<McpAppView widgets={{ "demo:probe": KeyProbe }} {...props} />
|
|
313
|
+
</HostBridgeProvider>,
|
|
314
|
+
),
|
|
315
|
+
).match(/<output>(.*?)<\/output>/)?.[1]
|
|
316
|
+
|
|
317
|
+
it("hands the setting to the widgets' data hooks; omitted, the 2.x key stays", () => {
|
|
318
|
+
host.tool = { status: "ready", toolOutput: VIEW, toolInput: {} }
|
|
319
|
+
expect(keyIn({ toolNameInQueryKey: true })).toBe('["feed","feed_data",{"id":1}]')
|
|
320
|
+
expect(keyIn({})).toBe('["feed",{"id":1}]')
|
|
321
|
+
})
|
|
322
|
+
})
|
|
323
|
+
|
|
324
|
+
describe("McpAppView query client", () => {
|
|
325
|
+
/** A widget that tells which client its data hooks use. */
|
|
326
|
+
function clientIn(wrap: (node: ReactNode) => ReactNode) {
|
|
327
|
+
const clients: QueryClient[] = []
|
|
328
|
+
function ClientProbe() {
|
|
329
|
+
clients.push(useQueryClient())
|
|
330
|
+
return null
|
|
331
|
+
}
|
|
332
|
+
renderToStaticMarkup(
|
|
333
|
+
<HostBridgeProvider bridge={bridge}>
|
|
334
|
+
{wrap(<McpAppView widgets={{ "demo:probe": ClientProbe }} />)}
|
|
335
|
+
</HostBridgeProvider>,
|
|
336
|
+
)
|
|
337
|
+
expect(clients.length, "the widget rendered").toBeGreaterThan(0)
|
|
338
|
+
return clients[0]
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
it("its widgets fetch through an enclosing AppQueryProvider's client", () => {
|
|
342
|
+
host.tool = { status: "ready", toolOutput: VIEW, toolInput: {} }
|
|
343
|
+
const perMount = new QueryClient()
|
|
344
|
+
expect(clientIn((node) => <AppQueryProvider client={perMount}>{node}</AppQueryProvider>)).toBe(
|
|
345
|
+
perMount,
|
|
346
|
+
)
|
|
347
|
+
expect(
|
|
348
|
+
clientIn((node) => node),
|
|
349
|
+
"else the toolkit's shared client",
|
|
350
|
+
).toBe(queryClient)
|
|
351
|
+
})
|
|
352
|
+
})
|
|
353
|
+
|
|
354
|
+
describe("McpAppView model context (toolkit#176 K20)", () => {
|
|
355
|
+
/** Inside an mcp-use view, where the view's line joins the native tree. */
|
|
356
|
+
const inView = (node: ReactNode) => (
|
|
357
|
+
<McpUseViewContext.Provider value={true}>{node}</McpUseViewContext.Provider>
|
|
358
|
+
)
|
|
359
|
+
const contexts = (html: string) =>
|
|
360
|
+
openingTags(html, "meta").map((tag) =>
|
|
361
|
+
decodeEntities(attribute(tag, "data-model-context") ?? ""),
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
it("an error the user sees instead of the view reaches the model: what failed, and that it is an error", () => {
|
|
365
|
+
host.tool = { status: "error", error: toolError(REASON), toolInput: {} }
|
|
366
|
+
const { view } = render(inView)
|
|
367
|
+
expect(contexts(view)).toEqual([
|
|
368
|
+
`The view of render-view could not be shown (reason: ${REASON}). ` +
|
|
369
|
+
"The user sees an error message with a retry button instead of the view.",
|
|
370
|
+
])
|
|
371
|
+
})
|
|
372
|
+
|
|
373
|
+
it("a blocked recovery tells the model the tool ran and must not be re-run to show the view", () => {
|
|
374
|
+
host.hostContext = toolInfo("start_and_show", { readOnlyHint: false })
|
|
375
|
+
host.recovery = blockedBy("policy")
|
|
376
|
+
expect(contexts(render(inView).view)).toEqual([
|
|
377
|
+
"The view of start_and_show is not shown: start_and_show ran, but the host did not pass its " +
|
|
378
|
+
"result to the view, and the view did not re-run it. The user sees a note to ask in the chat " +
|
|
379
|
+
"for the current state instead of the view. Do not re-run start_and_show just to show the view.",
|
|
380
|
+
])
|
|
381
|
+
})
|
|
382
|
+
|
|
383
|
+
it("a cancelled call says so", () => {
|
|
384
|
+
host.tool = { status: "error", error: new ToolCancelledError("user"), toolInput: {} }
|
|
385
|
+
expect(contexts(render(inView).view)).toEqual([
|
|
386
|
+
"The view of render-view was cancelled before it rendered. The user sees a note that the call was cancelled.",
|
|
387
|
+
])
|
|
388
|
+
})
|
|
389
|
+
|
|
390
|
+
it("failed data steps of a shown view are reported; a waiting view reports nothing", () => {
|
|
391
|
+
host.tool = { status: "ready", toolOutput: VIEW, toolInput: {} }
|
|
392
|
+
expect(contexts(render(inView).view)).toEqual([
|
|
393
|
+
"The view of render-view is shown, but some of its data could not be loaded (kpi: timeout). " +
|
|
394
|
+
"The user sees an alert with a retry button.",
|
|
395
|
+
])
|
|
396
|
+
host.tool = { status: "pending", toolInput: undefined, toolOutput: undefined }
|
|
397
|
+
expect(contexts(render(inView).view)).toEqual([])
|
|
398
|
+
})
|
|
399
|
+
})
|
|
400
|
+
|
|
401
|
+
describe("McpAppView locale", () => {
|
|
402
|
+
it("speaks the host's language in its own chrome", () => {
|
|
403
|
+
host.locale = "de-DE"
|
|
404
|
+
host.tool = { status: "error", error: toolError(REASON), toolInput: {} }
|
|
405
|
+
const { view, toasts } = render()
|
|
406
|
+
expect(decodeEntities(view)).toContain(de.errorTitle)
|
|
407
|
+
expect(view).toContain(`${de.retry}</button>`)
|
|
408
|
+
// The toast stack below the view speaks it too (its close buttons).
|
|
409
|
+
expect(toasts).toContain(`aria-label="${TOOLKIT_MESSAGES.de.common.dismiss}"`)
|
|
410
|
+
})
|
|
411
|
+
|
|
412
|
+
it("leaves useLocale() of consumer widgets alone (no LocaleProvider of its own)", () => {
|
|
413
|
+
host.locale = "de-DE"
|
|
414
|
+
host.tool = { status: "ready", toolOutput: VIEW, toolInput: {} }
|
|
415
|
+
const { view } = render()
|
|
416
|
+
expect(view).toContain(`aria-label="${de.refresh}"`)
|
|
417
|
+
expect(view).toContain("widget-locale:en<")
|
|
418
|
+
})
|
|
419
|
+
|
|
420
|
+
it("follows a consumer LocaleProvider over the host's locale", () => {
|
|
421
|
+
host.locale = "de-DE"
|
|
422
|
+
host.tool = { status: "ready", toolOutput: VIEW, toolInput: {} }
|
|
423
|
+
const { view } = render((node) => <LocaleProvider locale="en">{node}</LocaleProvider>)
|
|
424
|
+
expect(view).toContain(`aria-label="${en.refresh}"`)
|
|
425
|
+
expect(view).toContain("widget-locale:en<")
|
|
426
|
+
})
|
|
427
|
+
|
|
428
|
+
it("renders no English and no raw reason as message text under a German host", () => {
|
|
429
|
+
host.locale = "de-DE"
|
|
430
|
+
host.tool = { status: "ready", toolOutput: VIEW, toolInput: {} }
|
|
431
|
+
const catalog = catalogStrings(TOOLKIT_MESSAGES.de)
|
|
432
|
+
const fixtures = [
|
|
433
|
+
VIEW.title ?? "",
|
|
434
|
+
"demo:kpis",
|
|
435
|
+
"kpi",
|
|
436
|
+
"timeout",
|
|
437
|
+
"Fixture toast",
|
|
438
|
+
"widget-locale:en",
|
|
439
|
+
]
|
|
440
|
+
const { html } = render()
|
|
441
|
+
const leaks = userFacingTexts(html)
|
|
442
|
+
.map(({ source, value }) => {
|
|
443
|
+
let rest = value
|
|
444
|
+
for (const known of [...catalog, ...fixtures]) rest = rest.split(known).join(" ")
|
|
445
|
+
return { source, value, rest: rest.trim() }
|
|
446
|
+
})
|
|
447
|
+
.filter(({ rest }) => /\p{L}/u.test(rest))
|
|
448
|
+
expect(leaks).toEqual([])
|
|
449
|
+
})
|
|
450
|
+
})
|
|
451
|
+
|
|
452
|
+
describe("McpAppView icon stroke (CI §11)", () => {
|
|
453
|
+
it("draws the view's icons and its toast stack at 2.5", () => {
|
|
454
|
+
host.tool = { status: "error", error: toolError(REASON), toolInput: {} }
|
|
455
|
+
const { view, toasts } = render()
|
|
456
|
+
expect(strokes(view).length).toBeGreaterThan(0)
|
|
457
|
+
expect(new Set(strokes(view))).toEqual(new Set(["2.5"]))
|
|
458
|
+
expect(strokes(toasts).length).toBeGreaterThan(0)
|
|
459
|
+
expect(new Set(strokes(toasts))).toEqual(new Set(["2.5"]))
|
|
460
|
+
})
|
|
461
|
+
|
|
462
|
+
it("narrows the dense toolbar to 2", () => {
|
|
463
|
+
host.tool = { status: "ready", toolOutput: VIEW, toolInput: {} }
|
|
464
|
+
const { view } = render()
|
|
465
|
+
const [toolbar] = view.split('data-slot="view-toolbar"').slice(1)
|
|
466
|
+
const bar = (toolbar ?? "").split("</div></div>")[0] ?? ""
|
|
467
|
+
expect(strokes(bar).length).toBeGreaterThan(0)
|
|
468
|
+
expect(new Set(strokes(bar))).toEqual(new Set(["2"]))
|
|
469
|
+
})
|
|
470
|
+
})
|
|
471
|
+
|
|
472
|
+
/** Every string value of a catalog (parametrised messages are skipped), longest first. */
|
|
473
|
+
function catalogStrings(messages: ToolkitMessages): string[] {
|
|
474
|
+
return Object.values(messages)
|
|
475
|
+
.flatMap((section) => Object.values(section as Record<string, unknown>))
|
|
476
|
+
.filter((v): v is string => typeof v === "string")
|
|
477
|
+
.sort((a, b) => b.length - a.length)
|
|
478
|
+
}
|