@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,314 @@
|
|
|
1
|
+
import type { ReactNode } from "react"
|
|
2
|
+
import { renderToStaticMarkup } from "react-dom/server"
|
|
3
|
+
import { CircleCheck } from "lucide-react"
|
|
4
|
+
import { describe, expect, it } from "vitest"
|
|
5
|
+
import { LocaleProvider } from "../locale/locale-provider.js"
|
|
6
|
+
import { TOOLKIT_MESSAGES } from "../locale/toolkit-messages.js"
|
|
7
|
+
import { FOCUS_RING } from "../lib/focus-ring.js"
|
|
8
|
+
import { createToast } from "../lib/toast-model.js"
|
|
9
|
+
import { TONE_BORDER, TONE_ICON, TONE_SOFT } from "../lib/tone-utils.js"
|
|
10
|
+
import { attribute, openingTags } from "../render-scan.js"
|
|
11
|
+
import { EmptyState, type EmptyStateProps } from "./EmptyState.js"
|
|
12
|
+
import { ErrorState, type ErrorStateTone } from "./ErrorState.js"
|
|
13
|
+
import { ToastProvider, ToastViewport, useToast, type ToastApi } from "./Toast.js"
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Accessibility contract of the feedback primitives, pinned on their SSR
|
|
17
|
+
* markup (the house render path — no jsdom): roles, live regions, accessible
|
|
18
|
+
* names in the active locale, native focusable controls, nothing that steals
|
|
19
|
+
* focus.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
const de = TOOLKIT_MESSAGES.de
|
|
23
|
+
const inGerman = (node: ReactNode) =>
|
|
24
|
+
renderToStaticMarkup(<LocaleProvider locale="de-DE">{node}</LocaleProvider>)
|
|
25
|
+
const noop = (): void => {}
|
|
26
|
+
|
|
27
|
+
/** No positive tabindex and no autofocus anywhere — feedback never steals focus. */
|
|
28
|
+
function expectNoFocusTheft(html: string) {
|
|
29
|
+
expect(html).not.toMatch(/autofocus/i)
|
|
30
|
+
for (const value of html.match(/tabindex="(-?\d+)"/gi) ?? []) {
|
|
31
|
+
expect(Number(/"(-?\d+)"/.exec(value)?.[1])).toBeLessThanOrEqual(0)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
describe("Toast", () => {
|
|
36
|
+
const toasts = [
|
|
37
|
+
createToast(1, { tone: "success", title: "Job retried." }),
|
|
38
|
+
createToast(2, { tone: "danger", title: "Retry failed.", description: "Engine down (503)" }),
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
/** The stack's region, found by its slot (it has no role to query by). */
|
|
42
|
+
const regionOf = (html: string) =>
|
|
43
|
+
openingTags(html, "div").find((t) => attribute(t, "data-slot") === "toast-viewport") ?? ""
|
|
44
|
+
|
|
45
|
+
it("renders ONE persistent live region: aria-live=polite, each toast on its own", () => {
|
|
46
|
+
const html = inGerman(<ToastViewport toasts={toasts} onDismiss={noop} />)
|
|
47
|
+
const [region, ...others] = openingTags(html, "div").filter((t) => /aria-live=/.test(t))
|
|
48
|
+
expect(others).toHaveLength(0)
|
|
49
|
+
expect(attribute(region ?? "", "data-slot")).toBe("toast-viewport")
|
|
50
|
+
expect(attribute(region ?? "", "aria-live")).toBe("polite")
|
|
51
|
+
// Each new toast is announced on its own, not the whole stack again.
|
|
52
|
+
expect(attribute(region ?? "", "aria-atomic")).toBe("false")
|
|
53
|
+
expect(html).not.toContain('role="status"')
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it("keeps the region's attributes fixed from idle to the first toast", () => {
|
|
57
|
+
// An empty stack keeps the live region, so the first toast is announced.
|
|
58
|
+
// A role or name that switched on with that toast would make the browser
|
|
59
|
+
// rebuild the region in the same commit, with the toast already inside:
|
|
60
|
+
// not announced. So idle and busy carry exactly the same attributes…
|
|
61
|
+
const idle = regionOf(inGerman(<ToastViewport toasts={[]} onDismiss={noop} />))
|
|
62
|
+
const busy = regionOf(inGerman(<ToastViewport toasts={toasts} onDismiss={noop} />))
|
|
63
|
+
expect(idle).not.toBe("")
|
|
64
|
+
expect(busy).toBe(idle)
|
|
65
|
+
// …and no role or name at all: an idle stack is no second "status" beside
|
|
66
|
+
// a view's own loading status (a generic element takes no aria-label).
|
|
67
|
+
expect(attribute(idle, "role")).toBeUndefined()
|
|
68
|
+
expect(attribute(idle, "aria-label")).toBeUndefined()
|
|
69
|
+
const loading = inGerman(
|
|
70
|
+
<ToastProvider>
|
|
71
|
+
<p role="status">Ansicht wird geladen…</p>
|
|
72
|
+
</ToastProvider>,
|
|
73
|
+
)
|
|
74
|
+
expect(loading.match(/role="status"/g)).toHaveLength(1)
|
|
75
|
+
expect(loading.match(/aria-live="polite"/g)).toHaveLength(1)
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
it("shows the status as edge + icon next to plain text, never as coloured text", () => {
|
|
79
|
+
const html = inGerman(<ToastViewport toasts={toasts} onDismiss={noop} />)
|
|
80
|
+
const cards = openingTags(html, "div").filter((t) => /data-slot="toast"/.test(t))
|
|
81
|
+
expect(cards.map((t) => attribute(t, "data-tone"))).toEqual(["success", "danger"])
|
|
82
|
+
expect(cards[0]).toMatch(/border-l-success/)
|
|
83
|
+
expect(cards[1]).toMatch(/border-l-danger/)
|
|
84
|
+
for (const card of cards) expect(card).not.toMatch(/\btext-(success|danger|warning|info)\b/)
|
|
85
|
+
// The edge survives the dark 3:1 rim (a later dark: rule would paint over it).
|
|
86
|
+
expect(cards[0]).toMatch(/dark:border-input/)
|
|
87
|
+
expect(cards[0]).toMatch(/dark:border-l-success/)
|
|
88
|
+
// Icons are decorative and drawn in the tone colour; the text carries the message.
|
|
89
|
+
const svgs = openingTags(html, "svg")
|
|
90
|
+
for (const svg of svgs) expect(attribute(svg, "aria-hidden")).toBe("true")
|
|
91
|
+
expect(attribute(svgs[0] ?? "", "class")).toContain(TONE_ICON.success)
|
|
92
|
+
expect(attribute(svgs[2] ?? "", "class")).toContain(TONE_ICON.danger)
|
|
93
|
+
expect(html).toContain("Job retried.")
|
|
94
|
+
expect(html).toContain("Engine down (503)")
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
it("sets its words as running text (16 px), not in the dense-table size", () => {
|
|
98
|
+
const html = inGerman(<ToastViewport toasts={toasts} onDismiss={noop} />)
|
|
99
|
+
for (const card of openingTags(html, "div").filter((t) => /data-slot="toast"/.test(t))) {
|
|
100
|
+
expect(attribute(card, "class")).toMatch(/(^| )text-body( |$)/)
|
|
101
|
+
expect(attribute(card, "class")).not.toMatch(/\btext-dense\b/)
|
|
102
|
+
}
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
it("gives every toast a native close button named in the locale, and steals no focus", () => {
|
|
106
|
+
const html = inGerman(<ToastViewport toasts={toasts} onDismiss={noop} />)
|
|
107
|
+
const buttons = openingTags(html, "button")
|
|
108
|
+
expect(buttons).toHaveLength(2)
|
|
109
|
+
for (const b of buttons) {
|
|
110
|
+
expect(attribute(b, "type")).toBe("button")
|
|
111
|
+
expect(attribute(b, "aria-label")).toBe(de.common.dismiss)
|
|
112
|
+
expect(attribute(b, "class")).toContain(FOCUS_RING)
|
|
113
|
+
}
|
|
114
|
+
expectNoFocusTheft(html)
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
it("nests idempotently: one provider, one viewport", () => {
|
|
118
|
+
const html = renderToStaticMarkup(
|
|
119
|
+
<ToastProvider>
|
|
120
|
+
<ToastProvider>
|
|
121
|
+
<p>body</p>
|
|
122
|
+
</ToastProvider>
|
|
123
|
+
</ToastProvider>,
|
|
124
|
+
)
|
|
125
|
+
expect(html.match(/data-slot="toast-viewport"/g)).toHaveLength(1)
|
|
126
|
+
expect(html).toContain("<p>body</p>")
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
it("useToast outside a provider is a safe no-op", () => {
|
|
130
|
+
let api: ToastApi | undefined
|
|
131
|
+
function Probe() {
|
|
132
|
+
api = useToast()
|
|
133
|
+
return null
|
|
134
|
+
}
|
|
135
|
+
renderToStaticMarkup(<Probe />)
|
|
136
|
+
expect(api?.show({ title: "x" })).toBe(-1)
|
|
137
|
+
expect(() => api?.dismiss(1)).not.toThrow()
|
|
138
|
+
})
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
describe("EmptyState", () => {
|
|
142
|
+
it("is static (no live region), titles with h3 by default, hides its icon from AT", () => {
|
|
143
|
+
const html = inGerman(
|
|
144
|
+
<EmptyState
|
|
145
|
+
icon={<CircleCheck />}
|
|
146
|
+
tone="success"
|
|
147
|
+
title="Keine offenen Incidents"
|
|
148
|
+
description="Alles erledigt."
|
|
149
|
+
/>,
|
|
150
|
+
)
|
|
151
|
+
expect(html).not.toMatch(/role="(status|alert)"|aria-live/)
|
|
152
|
+
expect(html).toMatch(/<h3[^>]*>Keine offenen Incidents<\/h3>/)
|
|
153
|
+
const iconSlot = openingTags(html, "div").find((t) => /aria-hidden="true"/.test(t))
|
|
154
|
+
// The success tone on the icon (TONE_ICON), never on the words.
|
|
155
|
+
expect(iconSlot).toContain(TONE_ICON.success)
|
|
156
|
+
expect(html).not.toMatch(/text-success-ink/)
|
|
157
|
+
expect(html).toContain("Alles erledigt.")
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
it("sizes an icon to 24 px but lets a mark keep its own size-* class (the Komet needs room)", () => {
|
|
161
|
+
const html = renderToStaticMarkup(
|
|
162
|
+
<EmptyState icon={<svg className="size-12" aria-label="mark" />} title="Willkommen" />,
|
|
163
|
+
)
|
|
164
|
+
const slot = openingTags(html, "div").find((t) => /aria-hidden="true"/.test(t)) ?? ""
|
|
165
|
+
const slotClass = attribute(slot, "class") ?? ""
|
|
166
|
+
// The default applies only to an SVG without its own size class…
|
|
167
|
+
expect(slotClass).toContain("[&_svg:not([class*='size-'])]:size-6")
|
|
168
|
+
// …and never as a clamp that outranks the mark's class (0,1,1 beats 0,1,0).
|
|
169
|
+
expect(slotClass).not.toMatch(/\[&_svg\]:size-/)
|
|
170
|
+
expect(html).toContain('<svg class="size-12"')
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
it("takes the heading level of its surface and at most two actions", () => {
|
|
174
|
+
const html = renderToStaticMarkup(
|
|
175
|
+
<EmptyState
|
|
176
|
+
titleAs="h2"
|
|
177
|
+
title="Nothing here"
|
|
178
|
+
action={<button type="button">First</button>}
|
|
179
|
+
secondaryAction={<button type="button">Second</button>}
|
|
180
|
+
/>,
|
|
181
|
+
)
|
|
182
|
+
expect(html).toMatch(/<h2[^>]*>Nothing here<\/h2>/)
|
|
183
|
+
expect(openingTags(html, "button")).toHaveLength(2)
|
|
184
|
+
expect(renderToStaticMarkup(<EmptyState title="Bare" />)).not.toContain("<button")
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
it("sets its help line as running text (16 px), not in the dense-table size", () => {
|
|
188
|
+
const html = renderToStaticMarkup(<EmptyState title="Nothing" description="Add one." />)
|
|
189
|
+
const line = openingTags(html, "p").find((t) => /max-w-prose/.test(t)) ?? ""
|
|
190
|
+
expect(attribute(line, "class")).toMatch(/(^| )text-body( |$)/)
|
|
191
|
+
expect(html).not.toMatch(/\btext-dense\b/)
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
it("resolves an untyped tone: a retired success name colours the icon, anything else stays quiet", () => {
|
|
195
|
+
const iconClass = (tone: string) => {
|
|
196
|
+
const html = renderToStaticMarkup(
|
|
197
|
+
<EmptyState
|
|
198
|
+
icon={<CircleCheck />}
|
|
199
|
+
tone={tone as unknown as EmptyStateProps["tone"]}
|
|
200
|
+
title="x"
|
|
201
|
+
/>,
|
|
202
|
+
)
|
|
203
|
+
return openingTags(html, "div").find((t) => /aria-hidden="true"/.test(t)) ?? ""
|
|
204
|
+
}
|
|
205
|
+
expect(iconClass("ok")).toContain(TONE_ICON.success)
|
|
206
|
+
expect(iconClass("danger")).toContain(TONE_ICON.neutral)
|
|
207
|
+
expect(iconClass("purple")).toContain(TONE_ICON.neutral)
|
|
208
|
+
})
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
describe("ErrorState", () => {
|
|
212
|
+
it("announces itself (role=alert) with a heading, the hint, details and a retry in the locale", () => {
|
|
213
|
+
const html = inGerman(
|
|
214
|
+
<ErrorState
|
|
215
|
+
title="Die Ansicht konnte nicht geladen werden"
|
|
216
|
+
description="Versuch es noch mal."
|
|
217
|
+
details="[503 ProcessEngineException] Engine is shutting down"
|
|
218
|
+
onRetry={noop}
|
|
219
|
+
/>,
|
|
220
|
+
)
|
|
221
|
+
const root = openingTags(html, "div")[0] ?? ""
|
|
222
|
+
expect(attribute(root, "role")).toBe("alert")
|
|
223
|
+
expect(attribute(root, "data-variant")).toBe("block")
|
|
224
|
+
expect(html).toMatch(/<h2[^>]*>Die Ansicht konnte nicht geladen werden<\/h2>/)
|
|
225
|
+
// The raw reason sits collapsed in a native disclosure (keyboard-ready).
|
|
226
|
+
expect(html).toMatch(/<details[^>]*><summary[^>]*>Details<\/summary>/)
|
|
227
|
+
expect(html).not.toMatch(/<details[^>]*\sopen/)
|
|
228
|
+
const [retry] = openingTags(html, "button")
|
|
229
|
+
expect(attribute(retry ?? "", "type")).toBe("button")
|
|
230
|
+
expect(html).toContain(`${de.common.retry}</button>`)
|
|
231
|
+
expectNoFocusTheft(html)
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
it("supports a quieter status role, no role at all, and the inline alert form", () => {
|
|
235
|
+
const status = renderToStaticMarkup(<ErrorState title="Notice" role="status" tone="info" />)
|
|
236
|
+
expect(attribute(openingTags(status, "div")[0] ?? "", "role")).toBe("status")
|
|
237
|
+
const none = renderToStaticMarkup(<ErrorState title="Static" role="none" />)
|
|
238
|
+
expect(attribute(openingTags(none, "div")[0] ?? "", "role")).toBeUndefined()
|
|
239
|
+
const inline = renderToStaticMarkup(
|
|
240
|
+
<ErrorState variant="inline" title="Partial" tone="warning" />,
|
|
241
|
+
)
|
|
242
|
+
expect(inline).toMatch(/<p[^>]*>Partial<\/p>/)
|
|
243
|
+
expect(attribute(openingTags(inline, "div")[0] ?? "", "data-tone")).toBe("warning")
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
it.each(["danger", "warning", "info"] as const)(
|
|
247
|
+
"shows the %s tone as rim, tint and icon; every word on the tint is the foreground (CI §3.3)",
|
|
248
|
+
(tone) => {
|
|
249
|
+
const html = renderToStaticMarkup(
|
|
250
|
+
<ErrorState title="x" description="what to do" details="raw" tone={tone} onRetry={noop} />,
|
|
251
|
+
)
|
|
252
|
+
const rootClass = attribute(openingTags(html, "div")[0] ?? "", "class") ?? ""
|
|
253
|
+
expect(rootClass).toContain(TONE_SOFT[tone])
|
|
254
|
+
expect(rootClass).toContain(TONE_BORDER[tone])
|
|
255
|
+
expect(attribute(openingTags(html, "svg")[0] ?? "", "class")).toContain(TONE_ICON[tone])
|
|
256
|
+
// No quiet text, no ink and no status colour as text anywhere on the tint
|
|
257
|
+
// (the icons, which carry the tone colour, set aside).
|
|
258
|
+
const words = html.replace(/<svg[\s\S]*?<\/svg>/g, "")
|
|
259
|
+
expect(words).not.toMatch(/text-muted-foreground|text-\w+-ink|text-(danger|warning|info)\b/)
|
|
260
|
+
// The disclosure is a kit control: the kit's opaque focus ring.
|
|
261
|
+
expect(attribute(openingTags(html, "summary")[0] ?? "", "class")).toContain(FOCUS_RING)
|
|
262
|
+
},
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
it("has no retry without onRetry, and takes custom labels", () => {
|
|
266
|
+
expect(renderToStaticMarkup(<ErrorState title="x" />)).not.toContain("<button")
|
|
267
|
+
const html = renderToStaticMarkup(
|
|
268
|
+
<ErrorState
|
|
269
|
+
title="x"
|
|
270
|
+
details="raw"
|
|
271
|
+
detailsLabel="Technical"
|
|
272
|
+
onRetry={noop}
|
|
273
|
+
retryLabel="Reload"
|
|
274
|
+
actions={<a href="#help">Help</a>}
|
|
275
|
+
/>,
|
|
276
|
+
)
|
|
277
|
+
expect(html).toContain("Technical</summary>")
|
|
278
|
+
expect(html).toContain("Reload</button>")
|
|
279
|
+
expect(html).toContain('<a href="#help">Help</a>')
|
|
280
|
+
})
|
|
281
|
+
|
|
282
|
+
it("sets the block's description as running text (16 px); the inline alert box stays dense", () => {
|
|
283
|
+
const descriptionClass = (html: string) =>
|
|
284
|
+
/<div class="([^"]*)">what to do<\/div>/.exec(html)?.[1] ?? ""
|
|
285
|
+
const block = renderToStaticMarkup(<ErrorState title="x" description="what to do" />)
|
|
286
|
+
expect(descriptionClass(block)).toMatch(/(^| )text-body( |$)/)
|
|
287
|
+
const inline = renderToStaticMarkup(
|
|
288
|
+
<ErrorState variant="inline" title="x" description="what to do" />,
|
|
289
|
+
)
|
|
290
|
+
expect(descriptionClass(inline)).toMatch(/(^| )text-dense( |$)/)
|
|
291
|
+
})
|
|
292
|
+
|
|
293
|
+
it.each([
|
|
294
|
+
["error", "danger"],
|
|
295
|
+
["critical", "danger"],
|
|
296
|
+
["warn", "warning"],
|
|
297
|
+
["notice", "info"],
|
|
298
|
+
["success", "danger"],
|
|
299
|
+
["purple", "danger"],
|
|
300
|
+
])(
|
|
301
|
+
"resolves an untyped tone %j to its %s state instead of crashing on a missing icon",
|
|
302
|
+
(name, tone) => {
|
|
303
|
+
const html = renderToStaticMarkup(
|
|
304
|
+
<ErrorState title="x" tone={name as unknown as ErrorStateTone} />,
|
|
305
|
+
)
|
|
306
|
+
const root = openingTags(html, "div")[0] ?? ""
|
|
307
|
+
expect(attribute(root, "data-tone")).toBe(tone)
|
|
308
|
+
expect(attribute(root, "class")).toContain(TONE_BORDER[tone as ErrorStateTone])
|
|
309
|
+
expect(attribute(openingTags(html, "svg")[0] ?? "", "class")).toContain(
|
|
310
|
+
TONE_ICON[tone as ErrorStateTone],
|
|
311
|
+
)
|
|
312
|
+
},
|
|
313
|
+
)
|
|
314
|
+
})
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { renderToStaticMarkup } from "react-dom/server"
|
|
2
|
+
import { describe, expect, it } from "vitest"
|
|
3
|
+
import type { ToneVariant } from "../lib/tone-utils.js"
|
|
4
|
+
import { KpiGrid } from "./KpiGrid.js"
|
|
5
|
+
import { CountPill, LivePill } from "./Pills.js"
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Status model (owner decision 2e-f, CI §3.3): coloured digits are retired.
|
|
9
|
+
* A figure — a KPI value, a trend, a count — stays in the foreground; the tone
|
|
10
|
+
* shows as a dot, a border or a tint next to it. These components carry
|
|
11
|
+
* figures, so no tone colour may reach their text (SSR, the house render path).
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const STATUS = ["info", "success", "warning", "danger"] as const
|
|
15
|
+
const TONES: ToneVariant[] = ["neutral", ...STATUS]
|
|
16
|
+
/** Any tone used as a TEXT colour: the raw status colour or its -ink role. */
|
|
17
|
+
const TONE_AS_TEXT = /\btext-(?:success|warning|danger|info)(?:-ink)?\b/
|
|
18
|
+
|
|
19
|
+
describe("figures never take a tone colour", () => {
|
|
20
|
+
it("KpiGrid: trend lines and toned cells keep their text in the foreground", () => {
|
|
21
|
+
const html = renderToStaticMarkup(
|
|
22
|
+
<KpiGrid
|
|
23
|
+
cells={[
|
|
24
|
+
{ label: "Up", value: "12", trend: "+12% vs last week", trendDirection: "up" },
|
|
25
|
+
{ label: "Down", value: "3", trend: "-4% vs last week", trendDirection: "down" },
|
|
26
|
+
{ label: "Flat", value: "7", trend: "±0", trendDirection: "flat" },
|
|
27
|
+
...STATUS.map((tone) => ({ label: tone, value: "1", tone })),
|
|
28
|
+
]}
|
|
29
|
+
/>,
|
|
30
|
+
)
|
|
31
|
+
// Icons draw in the tone (SVG currentColor, TONE_ICON); no WORD or digit does.
|
|
32
|
+
const words = html.replace(/<svg[\s\S]*?<\/svg>/g, "")
|
|
33
|
+
expect(words).not.toMatch(TONE_AS_TEXT)
|
|
34
|
+
// The tone is still there — as a dot next to a label, an icon next to a trend.
|
|
35
|
+
expect(html).toMatch(/bg-danger\b/)
|
|
36
|
+
expect(html).toMatch(/bg-success\b/)
|
|
37
|
+
expect(html).toMatch(/<svg[^>]*class="lucide lucide-trending-up[^"]*text-danger"/)
|
|
38
|
+
expect(html).toMatch(/<svg[^>]*class="lucide lucide-trending-down[^"]*text-success"/)
|
|
39
|
+
// A toned trend reads in the foreground, an untoned one stays quiet.
|
|
40
|
+
expect(html).toMatch(/<span class="text-foreground">\+12% vs last week<\/span>/)
|
|
41
|
+
expect(html).toMatch(/<span class="text-muted-foreground">±0<\/span>/)
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it.each(TONES)(
|
|
45
|
+
"CountPill and LivePill (%s): number and label in the foreground, tone on the rim",
|
|
46
|
+
(tone) => {
|
|
47
|
+
const count = renderToStaticMarkup(<CountPill tone={tone}>42</CountPill>)
|
|
48
|
+
const live = renderToStaticMarkup(<LivePill tone={tone}>Live</LivePill>)
|
|
49
|
+
for (const html of [count, live]) {
|
|
50
|
+
expect(html).not.toMatch(TONE_AS_TEXT)
|
|
51
|
+
if (tone !== "neutral") {
|
|
52
|
+
expect(html).toContain("text-foreground")
|
|
53
|
+
expect(html).toContain(`border-${tone}`)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
)
|
|
58
|
+
})
|
package/src/fonts.css
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* @miragon/mcp-toolkit-ui/fonts.css — OPT-IN: Geist and Geist Mono, self-hosted
|
|
3
|
+
* through your bundler (no CDN). Import it next to globals.css/theme.css:
|
|
4
|
+
*
|
|
5
|
+
* @import "@miragon/mcp-toolkit-ui/globals.css";
|
|
6
|
+
* @import "@miragon/mcp-toolkit-ui/fonts.css";
|
|
7
|
+
*
|
|
8
|
+
* Cost: a bundler that inlines assets (vite-plugin-singlefile, MCP App views)
|
|
9
|
+
* embeds the woff2 files as base64 in every view. When a server can serve the
|
|
10
|
+
* files, prefer `fontFaces({ baseUrl })`, which references them by URL (and
|
|
11
|
+
* allow that origin in the view's CSP resourceDomains). The token stacks
|
|
12
|
+
* (--font-sans, --font-mono) name both families first, so either path works.
|
|
13
|
+
* A host's own font stays the fallback behind them: McpToolkitApp moves it
|
|
14
|
+
* to --host-font-sans/--host-font-mono (adoptHostFontVariables); an app with
|
|
15
|
+
* its own host-document layer calls that helper after applying the host's
|
|
16
|
+
* style variables, or the host's --font-sans replaces the whole stack.
|
|
17
|
+
*/
|
|
18
|
+
@import "@fontsource-variable/geist/wght.css";
|
|
19
|
+
@import "@fontsource-variable/geist-mono/wght.css";
|
package/src/globals.css
CHANGED
|
@@ -1,168 +1,12 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* @miragon/mcp-toolkit-ui/globals.css — the one-line stylesheet for an app
|
|
3
|
+
* entry: Tailwind, the toolkit's design tokens (./theme.css, Miragon by
|
|
4
|
+
* default) and the toolkit's own class sources. Fonts are opt-in: add
|
|
5
|
+
* `@import "@miragon/mcp-toolkit-ui/fonts.css"` or serve them with
|
|
6
|
+
* `fontFaces()`. A brand overrides the role variables after this import —
|
|
7
|
+
* see docs/guides/white-labeling.md.
|
|
8
|
+
*/
|
|
1
9
|
@import "tailwindcss";
|
|
2
|
-
@import "
|
|
3
|
-
@import "@fontsource-variable/geist";
|
|
10
|
+
@import "./theme.css";
|
|
4
11
|
|
|
5
12
|
@source "../**/*.{ts,tsx}";
|
|
6
|
-
|
|
7
|
-
@custom-variant dark (&:is(.dark *));
|
|
8
|
-
|
|
9
|
-
@theme inline {
|
|
10
|
-
--font-heading: var(--font-sans);
|
|
11
|
-
--font-sans: "Geist Variable", sans-serif;
|
|
12
|
-
--color-sidebar-ring: var(--sidebar-ring);
|
|
13
|
-
--color-sidebar-border: var(--sidebar-border);
|
|
14
|
-
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
15
|
-
--color-sidebar-accent: var(--sidebar-accent);
|
|
16
|
-
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
17
|
-
--color-sidebar-primary: var(--sidebar-primary);
|
|
18
|
-
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
19
|
-
--color-sidebar: var(--sidebar);
|
|
20
|
-
--color-chart-5: var(--chart-5);
|
|
21
|
-
--color-chart-4: var(--chart-4);
|
|
22
|
-
--color-chart-3: var(--chart-3);
|
|
23
|
-
--color-chart-2: var(--chart-2);
|
|
24
|
-
--color-chart-1: var(--chart-1);
|
|
25
|
-
--color-ring: var(--ring);
|
|
26
|
-
--color-input: var(--input);
|
|
27
|
-
--color-border: var(--border);
|
|
28
|
-
--color-destructive: var(--destructive);
|
|
29
|
-
--color-accent-foreground: var(--accent-foreground);
|
|
30
|
-
--color-accent: var(--accent);
|
|
31
|
-
--color-muted-foreground: var(--muted-foreground);
|
|
32
|
-
--color-muted: var(--muted);
|
|
33
|
-
--color-secondary-foreground: var(--secondary-foreground);
|
|
34
|
-
--color-secondary: var(--secondary);
|
|
35
|
-
--color-primary-foreground: var(--primary-foreground);
|
|
36
|
-
--color-primary: var(--primary);
|
|
37
|
-
--color-popover-foreground: var(--popover-foreground);
|
|
38
|
-
--color-popover: var(--popover);
|
|
39
|
-
--color-card-foreground: var(--card-foreground);
|
|
40
|
-
--color-card: var(--card);
|
|
41
|
-
--color-foreground: var(--foreground);
|
|
42
|
-
--color-background: var(--background);
|
|
43
|
-
/* Semantic status tones — the token surface behind ToneVariant/TONE_* maps.
|
|
44
|
-
Each tone exposes a readable foreground (`text-danger`) and a tinted
|
|
45
|
-
surface (`bg-danger-soft`); both carry light + dark values so tone-based
|
|
46
|
-
components (Pills, KpiGrid, WidgetHeader, FilterBar) re-theme automatically. */
|
|
47
|
-
--color-danger: var(--danger);
|
|
48
|
-
--color-danger-soft: var(--danger-soft);
|
|
49
|
-
--color-warning: var(--warning);
|
|
50
|
-
--color-warning-soft: var(--warning-soft);
|
|
51
|
-
--color-success: var(--success);
|
|
52
|
-
--color-success-soft: var(--success-soft);
|
|
53
|
-
--color-info: var(--info);
|
|
54
|
-
--color-info-soft: var(--info-soft);
|
|
55
|
-
--radius-sm: calc(var(--radius) * 0.6);
|
|
56
|
-
--radius-md: calc(var(--radius) * 0.8);
|
|
57
|
-
--radius-lg: var(--radius);
|
|
58
|
-
--radius-xl: calc(var(--radius) * 1.4);
|
|
59
|
-
--radius-2xl: calc(var(--radius) * 1.8);
|
|
60
|
-
--radius-3xl: calc(var(--radius) * 2.2);
|
|
61
|
-
--radius-4xl: calc(var(--radius) * 2.6);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
:root {
|
|
65
|
-
--background: oklch(1 0 0);
|
|
66
|
-
--foreground: oklch(0.145 0 0);
|
|
67
|
-
--card: oklch(1 0 0);
|
|
68
|
-
--card-foreground: oklch(0.145 0 0);
|
|
69
|
-
--popover: oklch(1 0 0);
|
|
70
|
-
--popover-foreground: oklch(0.145 0 0);
|
|
71
|
-
--primary: oklch(0.205 0 0);
|
|
72
|
-
--primary-foreground: oklch(0.985 0 0);
|
|
73
|
-
--secondary: oklch(0.97 0 0);
|
|
74
|
-
--secondary-foreground: oklch(0.205 0 0);
|
|
75
|
-
--muted: oklch(0.97 0 0);
|
|
76
|
-
--muted-foreground: oklch(0.556 0 0);
|
|
77
|
-
--accent: oklch(0.97 0 0);
|
|
78
|
-
--accent-foreground: oklch(0.205 0 0);
|
|
79
|
-
--destructive: oklch(0.577 0.245 27.325);
|
|
80
|
-
--border: oklch(0.922 0 0);
|
|
81
|
-
--input: oklch(0.922 0 0);
|
|
82
|
-
--ring: oklch(0.708 0 0);
|
|
83
|
-
--chart-1: oklch(0.87 0 0);
|
|
84
|
-
--chart-2: oklch(0.556 0 0);
|
|
85
|
-
--chart-3: oklch(0.439 0 0);
|
|
86
|
-
--chart-4: oklch(0.371 0 0);
|
|
87
|
-
--chart-5: oklch(0.269 0 0);
|
|
88
|
-
--radius: 0.625rem;
|
|
89
|
-
--sidebar: oklch(0.985 0 0);
|
|
90
|
-
--sidebar-foreground: oklch(0.145 0 0);
|
|
91
|
-
--sidebar-primary: oklch(0.205 0 0);
|
|
92
|
-
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
93
|
-
--sidebar-accent: oklch(0.97 0 0);
|
|
94
|
-
--sidebar-accent-foreground: oklch(0.205 0 0);
|
|
95
|
-
--sidebar-border: oklch(0.922 0 0);
|
|
96
|
-
--sidebar-ring: oklch(0.708 0 0);
|
|
97
|
-
/* Semantic status tones (light). Foreground = readable on a card; *-soft =
|
|
98
|
-
a tinted surface for badges/dots/icon tiles. Neutral tone reuses the
|
|
99
|
-
muted ramp and needs no dedicated token. */
|
|
100
|
-
--danger: oklch(0.55 0.21 27.3);
|
|
101
|
-
--danger-soft: oklch(0.95 0.04 27.3);
|
|
102
|
-
--warning: oklch(0.62 0.16 70);
|
|
103
|
-
--warning-soft: oklch(0.95 0.05 80);
|
|
104
|
-
--success: oklch(0.58 0.15 152);
|
|
105
|
-
--success-soft: oklch(0.95 0.05 152);
|
|
106
|
-
--info: oklch(0.55 0.2 255);
|
|
107
|
-
--info-soft: oklch(0.95 0.04 255);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
.dark {
|
|
111
|
-
--background: oklch(0.145 0 0);
|
|
112
|
-
--foreground: oklch(0.985 0 0);
|
|
113
|
-
--card: oklch(0.205 0 0);
|
|
114
|
-
--card-foreground: oklch(0.985 0 0);
|
|
115
|
-
--popover: oklch(0.205 0 0);
|
|
116
|
-
--popover-foreground: oklch(0.985 0 0);
|
|
117
|
-
--primary: oklch(0.922 0 0);
|
|
118
|
-
--primary-foreground: oklch(0.205 0 0);
|
|
119
|
-
--secondary: oklch(0.269 0 0);
|
|
120
|
-
--secondary-foreground: oklch(0.985 0 0);
|
|
121
|
-
--muted: oklch(0.269 0 0);
|
|
122
|
-
--muted-foreground: oklch(0.708 0 0);
|
|
123
|
-
--accent: oklch(0.269 0 0);
|
|
124
|
-
--accent-foreground: oklch(0.985 0 0);
|
|
125
|
-
--destructive: oklch(0.704 0.191 22.216);
|
|
126
|
-
--border: oklch(1 0 0 / 10%);
|
|
127
|
-
--input: oklch(1 0 0 / 15%);
|
|
128
|
-
--ring: oklch(0.556 0 0);
|
|
129
|
-
--chart-1: oklch(0.87 0 0);
|
|
130
|
-
--chart-2: oklch(0.556 0 0);
|
|
131
|
-
--chart-3: oklch(0.439 0 0);
|
|
132
|
-
--chart-4: oklch(0.371 0 0);
|
|
133
|
-
--chart-5: oklch(0.269 0 0);
|
|
134
|
-
--sidebar: oklch(0.205 0 0);
|
|
135
|
-
--sidebar-foreground: oklch(0.985 0 0);
|
|
136
|
-
--sidebar-primary: oklch(0.488 0.243 264.376);
|
|
137
|
-
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
138
|
-
--sidebar-accent: oklch(0.269 0 0);
|
|
139
|
-
--sidebar-accent-foreground: oklch(0.985 0 0);
|
|
140
|
-
--sidebar-border: oklch(1 0 0 / 10%);
|
|
141
|
-
--sidebar-ring: oklch(0.556 0 0);
|
|
142
|
-
/* Semantic status tones (dark). Brighter foregrounds for contrast on the
|
|
143
|
-
dark surface; *-soft is a low-alpha tint of the same hue. */
|
|
144
|
-
--danger: oklch(0.7 0.19 22.2);
|
|
145
|
-
--danger-soft: oklch(0.7 0.19 22.2 / 18%);
|
|
146
|
-
--warning: oklch(0.78 0.15 75);
|
|
147
|
-
--warning-soft: oklch(0.78 0.15 75 / 18%);
|
|
148
|
-
--success: oklch(0.72 0.17 152);
|
|
149
|
-
--success-soft: oklch(0.72 0.17 152 / 18%);
|
|
150
|
-
--info: oklch(0.7 0.16 255);
|
|
151
|
-
--info-soft: oklch(0.7 0.16 255 / 18%);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
* {
|
|
155
|
-
font-family: "Geist Variable", ui-sans-serif, system-ui, sans-serif !important;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
@layer base {
|
|
159
|
-
*,
|
|
160
|
-
*::before,
|
|
161
|
-
*::after {
|
|
162
|
-
@apply border-border outline-ring/50;
|
|
163
|
-
}
|
|
164
|
-
html,
|
|
165
|
-
body {
|
|
166
|
-
@apply bg-background text-foreground;
|
|
167
|
-
}
|
|
168
|
-
}
|
package/src/hooks/index.ts
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
export { useIsMobile } from "./use-mobile.js"
|
|
2
2
|
export { useDebouncedValue } from "./use-debounced-value.js"
|
|
3
|
-
export { useToolQuery, useToolMutation } from "./use-tool-query.js"
|
|
4
|
-
export type { UseToolQueryOptions } from "./use-tool-query.js"
|
|
3
|
+
export { useToolQuery, useToolQueryKey, useToolMutation } from "./use-tool-query.js"
|
|
4
|
+
export type { UseToolQueryOptions, UseToolMutationOptions } from "./use-tool-query.js"
|
|
5
5
|
export { useViewToolQuery } from "./use-view-tool-query.js"
|
|
6
6
|
export type { UseViewToolQueryOptions } from "./use-view-tool-query.js"
|
|
7
7
|
export { useViewData } from "./use-view-data.js"
|
|
8
|
-
export type { ViewDataResult } from "./use-view-data.js"
|
|
8
|
+
export type { ViewDataResult, UseViewDataOptions, ViewDataMode } from "./use-view-data.js"
|
|
9
9
|
export {
|
|
10
10
|
useToolResultRecovery,
|
|
11
11
|
resolveRecoveryToolName,
|
|
12
12
|
decodeRecoveredResult,
|
|
13
13
|
} from "./use-tool-result-recovery.js"
|
|
14
14
|
export type {
|
|
15
|
+
RecoveryBlockReason,
|
|
16
|
+
RecoveryPolicy,
|
|
15
17
|
ToolResultRecoveryOptions,
|
|
16
18
|
ToolResultRecoveryState,
|
|
19
|
+
ToolSafety,
|
|
17
20
|
} from "./use-tool-result-recovery.js"
|