@miragon/mcp-toolkit-ui 2.7.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 +7 -4
- 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.js +1 -1
- 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 +21 -7
- 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 +11 -4
- 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/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 +2 -1
- package/dist/app/builder/PipelineStrip.d.ts.map +1 -1
- package/dist/app/builder/PipelineStrip.js +26 -13
- 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 +8 -2
- package/dist/app/builder/SaveDialog.js.map +1 -1
- package/dist/app/builder/Toolbar.d.ts +12 -4
- package/dist/app/builder/Toolbar.d.ts.map +1 -1
- package/dist/app/builder/Toolbar.js +18 -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 +143 -1
- package/dist/app/builder/builder-model.d.ts.map +1 -1
- package/dist/app/builder/builder-model.js +191 -1
- package/dist/app/builder/builder-model.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/use-builder-catalogue.d.ts +4 -1
- package/dist/app/builder/use-builder-catalogue.d.ts.map +1 -1
- package/dist/app/builder/use-builder-catalogue.js +44 -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/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 +13 -25
- package/dist/app/layout-builder.d.ts.map +1 -1
- package/dist/app/layout-builder.js +75 -81
- package/dist/app/layout-builder.js.map +1 -1
- package/dist/app/mcp-app-view-chrome.d.ts +12 -3
- package/dist/app/mcp-app-view-chrome.d.ts.map +1 -1
- package/dist/app/mcp-app-view-chrome.js +21 -5
- package/dist/app/mcp-app-view-chrome.js.map +1 -1
- package/dist/app/mcp-app-view-states.d.ts +11 -3
- package/dist/app/mcp-app-view-states.d.ts.map +1 -1
- package/dist/app/mcp-app-view-states.js +29 -8
- package/dist/app/mcp-app-view-states.js.map +1 -1
- package/dist/app/mcp-app-view.d.ts +33 -1
- package/dist/app/mcp-app-view.d.ts.map +1 -1
- package/dist/app/mcp-app-view.js +181 -127
- package/dist/app/mcp-app-view.js.map +1 -1
- package/dist/app/mcp-toolkit-app.d.ts +6 -1
- package/dist/app/mcp-toolkit-app.d.ts.map +1 -1
- package/dist/app/mcp-toolkit-app.js +8 -1
- 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 +4 -0
- package/dist/app/use-view-lifecycle.d.ts.map +1 -1
- package/dist/app/use-view-lifecycle.js +2 -0
- package/dist/app/use-view-lifecycle.js.map +1 -1
- package/dist/app/view-lifecycle.d.ts +80 -4
- package/dist/app/view-lifecycle.d.ts.map +1 -1
- package/dist/app/view-lifecycle.js +90 -3
- package/dist/app/view-lifecycle.js.map +1 -1
- 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 +3 -1
- package/dist/app/widget-props-sheet.d.ts.map +1 -1
- package/dist/app/widget-props-sheet.js +22 -10
- package/dist/app/widget-props-sheet.js.map +1 -1
- package/dist/app/widget-renderer.js +1 -1
- 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 +8 -6
- package/dist/components/DrillButton.js.map +1 -1
- package/dist/components/EmptyState.d.ts.map +1 -1
- package/dist/components/EmptyState.js +4 -2
- package/dist/components/EmptyState.js.map +1 -1
- package/dist/components/ErrorState.d.ts +5 -2
- package/dist/components/ErrorState.d.ts.map +1 -1
- package/dist/components/ErrorState.js +25 -8
- package/dist/components/ErrorState.js.map +1 -1
- package/dist/components/FilterBar.d.ts +15 -4
- package/dist/components/FilterBar.d.ts.map +1 -1
- package/dist/components/FilterBar.js +11 -6
- package/dist/components/FilterBar.js.map +1 -1
- package/dist/components/KpiGrid.d.ts +66 -21
- package/dist/components/KpiGrid.d.ts.map +1 -1
- package/dist/components/KpiGrid.js +101 -34
- package/dist/components/KpiGrid.js.map +1 -1
- package/dist/components/ListFooter.d.ts +31 -4
- package/dist/components/ListFooter.d.ts.map +1 -1
- package/dist/components/ListFooter.js +30 -11
- 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 +27 -7
- package/dist/components/Pills.d.ts.map +1 -1
- package/dist/components/Pills.js +21 -6
- 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 +9 -3
- package/dist/components/Toast.d.ts.map +1 -1
- package/dist/components/Toast.js +31 -16
- package/dist/components/Toast.js.map +1 -1
- 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 +47 -52
- package/dist/hooks/use-tool-result-recovery.d.ts.map +1 -1
- package/dist/hooks/use-tool-result-recovery.js +58 -120
- package/dist/hooks/use-tool-result-recovery.js.map +1 -1
- package/dist/hooks/use-view-data.d.ts +52 -10
- package/dist/hooks/use-view-data.d.ts.map +1 -1
- package/dist/hooks/use-view-data.js +81 -16
- 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 +17 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -6
- 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/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 +41 -1
- package/dist/lib/toast-model.d.ts.map +1 -1
- package/dist/lib/toast-model.js +67 -1
- package/dist/lib/toast-model.js.map +1 -1
- package/dist/lib/tone-utils.d.ts +58 -6
- package/dist/lib/tone-utils.d.ts.map +1 -1
- package/dist/lib/tone-utils.js +66 -4
- 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 +1 -1
- package/dist/locale/index.d.ts.map +1 -1
- package/dist/locale/message-types.d.ts +117 -4
- package/dist/locale/message-types.d.ts.map +1 -1
- package/dist/locale/messages-de.d.ts.map +1 -1
- package/dist/locale/messages-de.js +44 -10
- package/dist/locale/messages-de.js.map +1 -1
- package/dist/locale/messages-en.d.ts.map +1 -1
- package/dist/locale/messages-en.js +44 -10
- package/dist/locale/messages-en.js.map +1 -1
- package/dist/locale/toolkit-messages.d.ts +19 -1
- package/dist/locale/toolkit-messages.d.ts.map +1 -1
- package/dist/locale/toolkit-messages.js +54 -1
- package/dist/locale/toolkit-messages.js.map +1 -1
- package/dist/primitives/alert.d.ts +1 -1
- package/dist/primitives/alert.d.ts.map +1 -1
- package/dist/primitives/alert.js +2 -0
- 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 +9 -4
- package/dist/primitives/badge.js.map +1 -1
- package/dist/primitives/button.d.ts +1 -1
- package/dist/primitives/button.js +1 -1
- package/dist/primitives/button.js.map +1 -1
- package/dist/primitives/dialog.d.ts +1 -1
- package/dist/primitives/dialog.d.ts.map +1 -1
- package/dist/primitives/dialog.js +10 -3
- package/dist/primitives/dialog.js.map +1 -1
- package/dist/primitives/select.js +1 -1
- package/dist/primitives/select.js.map +1 -1
- package/dist/primitives/sheet.d.ts +1 -1
- package/dist/primitives/sheet.d.ts.map +1 -1
- package/dist/primitives/sheet.js +6 -2
- package/dist/primitives/sheet.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 +30 -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/theme/create-theme.d.ts +19 -1
- package/dist/theme/create-theme.d.ts.map +1 -1
- package/dist/theme/create-theme.js +15 -0
- package/dist/theme/create-theme.js.map +1 -1
- package/package.json +5 -4
- package/src/app/adapt-data-widget.tsx +2 -42
- package/src/app/builder/BuilderBanners.tsx +1 -1
- package/src/app/builder/CanvasRow.tsx +63 -17
- package/src/app/builder/CatalogueSheet.tsx +50 -29
- package/src/app/builder/CellPropsSheet.tsx +17 -2
- package/src/app/builder/DiscardDialog.tsx +59 -0
- package/src/app/builder/LayoutSurface.tsx +206 -66
- package/src/app/builder/PipelineStrip.tsx +253 -153
- package/src/app/builder/SaveDialog.tsx +14 -2
- package/src/app/builder/Toolbar.tsx +144 -59
- package/src/app/builder/Workspace.tsx +226 -58
- package/src/app/builder/builder-model.ts +341 -2
- 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/reveal.ts +10 -0
- package/src/app/builder/use-builder-catalogue.ts +56 -19
- package/src/app/builder/use-builder-session.ts +68 -0
- package/src/app/builder/use-display-resolver.ts +24 -0
- package/src/app/index.ts +12 -2
- package/src/app/layout-builder.tsx +178 -191
- package/src/app/mcp-app-view-chrome.test.tsx +141 -1
- package/src/app/mcp-app-view-chrome.tsx +56 -6
- package/src/app/mcp-app-view-states.tsx +51 -11
- package/src/app/mcp-app-view.effects.test.tsx +292 -0
- package/src/app/mcp-app-view.test.tsx +222 -2
- package/src/app/mcp-app-view.tsx +267 -117
- package/src/app/mcp-toolkit-app.test.tsx +40 -0
- package/src/app/mcp-toolkit-app.tsx +8 -1
- 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 +6 -0
- package/src/app/view-lifecycle.test.ts +216 -0
- package/src/app/view-lifecycle.ts +165 -5
- 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 +57 -16
- package/src/app/widget-renderer.tsx +1 -1
- package/src/components/DrillButton.tsx +23 -10
- package/src/components/EmptyState.tsx +6 -4
- package/src/components/ErrorState.tsx +31 -9
- package/src/components/FilterBar.test.tsx +82 -0
- package/src/components/FilterBar.tsx +52 -26
- package/src/components/KpiGrid.test.tsx +279 -0
- package/src/components/KpiGrid.tsx +297 -90
- package/src/components/ListFooter.test.tsx +116 -0
- package/src/components/ListFooter.tsx +102 -15
- 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 +63 -10
- package/src/components/SectionHeading.tsx +9 -3
- package/src/components/Toast.tsx +37 -19
- package/src/components/WidgetHeader.test.tsx +78 -0
- package/src/components/WidgetHeader.tsx +63 -16
- package/src/components/feedback-states.test.tsx +98 -9
- package/src/components/tone-figures.test.tsx +8 -4
- 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 +3 -4
- package/src/hooks/use-tool-result-recovery.ts +84 -142
- package/src/hooks/use-view-data.test.tsx +118 -9
- package/src/hooks/use-view-data.ts +128 -18
- package/src/hooks/use-view-tool-query.ts +25 -19
- package/src/i18n-coverage.test.tsx +104 -2
- package/src/index.ts +48 -11
- package/src/lib/focus-return.test.ts +288 -0
- package/src/lib/focus-return.ts +96 -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 +166 -1
- package/src/lib/toast-model.ts +104 -2
- package/src/lib/tone-utils.test.ts +86 -10
- package/src/lib/tone-utils.ts +89 -8
- 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 +1 -0
- package/src/locale/message-types.ts +118 -2
- package/src/locale/messages-de.ts +49 -10
- package/src/locale/messages-en.ts +49 -10
- package/src/locale/toolkit-messages.test.ts +133 -0
- package/src/locale/toolkit-messages.ts +63 -2
- package/src/primitives/alert.tsx +3 -0
- package/src/primitives/badge.tsx +9 -4
- package/src/primitives/button.tsx +1 -1
- package/src/primitives/dialog.tsx +10 -1
- package/src/primitives/select.tsx +1 -1
- package/src/primitives/sheet.tsx +7 -0
- 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 +38 -6
- package/src/render-effects.tsx +96 -0
- package/src/theme/cd-tokens.test.ts +3 -1
- package/src/theme/create-theme.test.ts +10 -0
- package/src/theme/create-theme.ts +28 -0
- package/src/theme/theme-css.test.ts +51 -0
- package/src/theme.css +31 -1
- package/ui-catalog.json +448 -41
package/src/lib/toast-model.ts
CHANGED
|
@@ -4,12 +4,28 @@
|
|
|
4
4
|
* FITNESS.md) and the component stays a thin view over them.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import { resolveTone } from "./tone-utils.js"
|
|
8
|
+
|
|
7
9
|
/** Status tone of a toast — the four status roles (`--success` … `--info`). */
|
|
8
10
|
export type ToastTone = "success" | "warning" | "danger" | "info"
|
|
9
11
|
|
|
12
|
+
/**
|
|
13
|
+
* The toast tone of any tone name: the four status tones as is, a retired
|
|
14
|
+
* name through `resolveTone` (`error` → `danger`), and `neutral` or anything
|
|
15
|
+
* unknown as `info`. Keeps a toast whose tone arrives untyped (plain JS, wire
|
|
16
|
+
* data) on the screen instead of crashing the view on a missing icon.
|
|
17
|
+
*/
|
|
18
|
+
export function resolveToastTone(name: unknown): ToastTone {
|
|
19
|
+
const tone = resolveTone(name, "info")
|
|
20
|
+
return tone === "neutral" ? "info" : tone
|
|
21
|
+
}
|
|
22
|
+
|
|
10
23
|
/** What a caller passes to `useToast().show(...)`. */
|
|
11
24
|
export interface ToastOptions {
|
|
12
|
-
/**
|
|
25
|
+
/**
|
|
26
|
+
* Status tone; drives the edge, the icon and the default duration. Default
|
|
27
|
+
* `"info"`. An untyped retired name still resolves ({@link resolveToastTone}).
|
|
28
|
+
*/
|
|
13
29
|
tone?: ToastTone
|
|
14
30
|
/** The message. Say what happened, in one short sentence ("Job retried."). */
|
|
15
31
|
title: string
|
|
@@ -43,7 +59,7 @@ export function toastDuration(tone: ToastTone, explicit?: number): number {
|
|
|
43
59
|
|
|
44
60
|
/** Normalise caller options into a list item. */
|
|
45
61
|
export function createToast(id: number, options: ToastOptions): ToastItem {
|
|
46
|
-
const tone = options.tone
|
|
62
|
+
const tone = resolveToastTone(options.tone)
|
|
47
63
|
return {
|
|
48
64
|
id,
|
|
49
65
|
tone,
|
|
@@ -61,3 +77,89 @@ export function toastReducer(state: readonly ToastItem[], action: ToastAction):
|
|
|
61
77
|
const next = state.filter((t) => t.id !== action.id)
|
|
62
78
|
return next.length === state.length ? (state as ToastItem[]) : next
|
|
63
79
|
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* The clock a {@link createToastTimer} runs on. The default reads the global
|
|
83
|
+
* timers at call time, so fake timers installed in a test take effect.
|
|
84
|
+
*/
|
|
85
|
+
export interface ToastClock {
|
|
86
|
+
now: () => number
|
|
87
|
+
setTimeout: (callback: () => void, ms: number) => unknown
|
|
88
|
+
clearTimeout: (handle: unknown) => void
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const SYSTEM_CLOCK: ToastClock = {
|
|
92
|
+
now: () => Date.now(),
|
|
93
|
+
setTimeout: (callback, ms) => globalThis.setTimeout(callback, ms),
|
|
94
|
+
clearTimeout: (handle) => globalThis.clearTimeout(handle as ReturnType<typeof setTimeout>),
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** The countdown of one toast (see {@link createToastTimer}). */
|
|
98
|
+
export interface ToastTimer {
|
|
99
|
+
/**
|
|
100
|
+
* Hold the toast for `reason` (`"pointer"`, `"focus"`): the countdown
|
|
101
|
+
* pauses and keeps the time that was left.
|
|
102
|
+
*/
|
|
103
|
+
hold: (reason: string) => void
|
|
104
|
+
/** Release `reason`; once nothing holds the toast, it resumes with the time that was left. */
|
|
105
|
+
release: (reason: string) => void
|
|
106
|
+
/** The time left in ms — frozen while held. */
|
|
107
|
+
remaining: () => number
|
|
108
|
+
/** Stop for good (the toast left early or unmounted); `onExpire` never fires afterwards. */
|
|
109
|
+
cancel: () => void
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* A pausable countdown: starts at once, pauses while anything holds it
|
|
114
|
+
* (hover, keyboard focus — so a reader can finish), and on release RESUMES
|
|
115
|
+
* WITH THE REMAINING TIME instead of restarting the full duration. Fires
|
|
116
|
+
* `onExpire` once.
|
|
117
|
+
*/
|
|
118
|
+
export function createToastTimer(
|
|
119
|
+
duration: number,
|
|
120
|
+
onExpire: () => void,
|
|
121
|
+
clock: ToastClock = SYSTEM_CLOCK,
|
|
122
|
+
): ToastTimer {
|
|
123
|
+
let left = Math.max(duration, 0)
|
|
124
|
+
let startedAt = 0
|
|
125
|
+
let handle: unknown
|
|
126
|
+
let running = false
|
|
127
|
+
let finished = false
|
|
128
|
+
const holds = new Set<string>()
|
|
129
|
+
|
|
130
|
+
const elapsed = () => (running ? clock.now() - startedAt : 0)
|
|
131
|
+
const start = () => {
|
|
132
|
+
if (finished || running || holds.size > 0) return
|
|
133
|
+
running = true
|
|
134
|
+
startedAt = clock.now()
|
|
135
|
+
handle = clock.setTimeout(() => {
|
|
136
|
+
running = false
|
|
137
|
+
finished = true
|
|
138
|
+
left = 0
|
|
139
|
+
onExpire()
|
|
140
|
+
}, left)
|
|
141
|
+
}
|
|
142
|
+
const pause = () => {
|
|
143
|
+
if (!running) return
|
|
144
|
+
left = Math.max(left - elapsed(), 0)
|
|
145
|
+
running = false
|
|
146
|
+
clock.clearTimeout(handle)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
start()
|
|
150
|
+
return {
|
|
151
|
+
hold: (reason) => {
|
|
152
|
+
holds.add(reason)
|
|
153
|
+
pause()
|
|
154
|
+
},
|
|
155
|
+
release: (reason) => {
|
|
156
|
+
holds.delete(reason)
|
|
157
|
+
start()
|
|
158
|
+
},
|
|
159
|
+
remaining: () => Math.max(left - elapsed(), 0),
|
|
160
|
+
cancel: () => {
|
|
161
|
+
pause()
|
|
162
|
+
finished = true
|
|
163
|
+
},
|
|
164
|
+
}
|
|
165
|
+
}
|
|
@@ -1,18 +1,29 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest"
|
|
2
2
|
import {
|
|
3
|
+
isToneVariant,
|
|
4
|
+
resolveTone,
|
|
5
|
+
TONE_ALIASES,
|
|
3
6
|
TONE_BORDER,
|
|
4
7
|
TONE_DOT,
|
|
5
8
|
TONE_ICON,
|
|
9
|
+
TONE_INK,
|
|
6
10
|
TONE_SOFT,
|
|
7
11
|
TONE_TEXT,
|
|
12
|
+
TONE_TINT,
|
|
13
|
+
TONE_VARIANTS,
|
|
8
14
|
type ToneVariant,
|
|
9
15
|
} from "./tone-utils.js"
|
|
10
16
|
|
|
11
17
|
const ALL_TONES: ToneVariant[] = ["neutral", "info", "success", "warning", "danger"]
|
|
12
18
|
const STATUS_TONES = ["info", "success", "warning", "danger"] as const
|
|
13
|
-
const ALL_MAPS = { TONE_SOFT, TONE_DOT,
|
|
19
|
+
const ALL_MAPS = { TONE_SOFT, TONE_TINT, TONE_DOT, TONE_INK, TONE_BORDER, TONE_ICON }
|
|
14
20
|
|
|
15
21
|
describe("tone-utils", () => {
|
|
22
|
+
it("has ONE vocabulary of five tones, in severity order", () => {
|
|
23
|
+
expect(TONE_VARIANTS).toEqual(["danger", "warning", "success", "info", "neutral"])
|
|
24
|
+
expect([...TONE_VARIANTS].sort()).toEqual([...ALL_TONES].sort())
|
|
25
|
+
})
|
|
26
|
+
|
|
16
27
|
it("covers every ToneVariant in each map (no missing tones)", () => {
|
|
17
28
|
for (const map of Object.values(ALL_MAPS)) {
|
|
18
29
|
expect(Object.keys(map).sort()).toEqual([...ALL_TONES].sort())
|
|
@@ -30,39 +41,104 @@ describe("tone-utils", () => {
|
|
|
30
41
|
it("routes each status tone to its three roles (colour, tint, ink)", () => {
|
|
31
42
|
for (const tone of STATUS_TONES) {
|
|
32
43
|
expect(TONE_SOFT[tone]).toBe(`bg-${tone}-soft text-foreground`)
|
|
33
|
-
expect(
|
|
44
|
+
expect(TONE_TINT[tone]).toBe(`bg-${tone}-soft`)
|
|
45
|
+
expect(TONE_INK[tone]).toBe(`text-${tone}-ink`)
|
|
34
46
|
expect(TONE_DOT[tone]).toBe(`bg-${tone}`)
|
|
35
47
|
expect(TONE_BORDER[tone]).toBe(`border-${tone}`)
|
|
36
48
|
expect(TONE_ICON[tone]).toBe(`text-${tone}`)
|
|
37
49
|
}
|
|
38
50
|
})
|
|
39
51
|
|
|
52
|
+
it("keeps TONE_TEXT as the deprecated name of the very same TONE_INK map", () => {
|
|
53
|
+
expect(TONE_TEXT).toBe(TONE_INK)
|
|
54
|
+
})
|
|
55
|
+
|
|
40
56
|
it("never sets text in a raw status colour outside the icon map (CI §3.3)", () => {
|
|
41
57
|
// Success and warning reach 1.67:1 / 1.83:1 on white; text takes the
|
|
42
58
|
// tone's -ink role. TONE_ICON is the one deliberate exception (SVG
|
|
43
59
|
// currentColor, always next to a readable label).
|
|
44
60
|
const rawStatusText = /\btext-(success|warning|danger|info)(?![-\w])/
|
|
45
|
-
for (const [name, map] of Object.entries({
|
|
61
|
+
for (const [name, map] of Object.entries({
|
|
62
|
+
TONE_SOFT,
|
|
63
|
+
TONE_TINT,
|
|
64
|
+
TONE_DOT,
|
|
65
|
+
TONE_INK,
|
|
66
|
+
TONE_BORDER,
|
|
67
|
+
})) {
|
|
46
68
|
for (const tone of ALL_TONES) {
|
|
47
69
|
expect(map[tone], `${name}:${tone}`).not.toMatch(rawStatusText)
|
|
48
70
|
}
|
|
49
71
|
}
|
|
50
72
|
})
|
|
51
73
|
|
|
52
|
-
it("puts the foreground on every
|
|
74
|
+
it("puts the foreground on every tint, never the ink or the status colour (CI §3.3)", () => {
|
|
53
75
|
// "-soft: Text darauf --cd-schwarz, Rand in der Statusfarbe" — the rim
|
|
54
|
-
// (TONE_BORDER) carries the tone
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
expect(TONE_SOFT[tone]).
|
|
76
|
+
// (TONE_BORDER) carries the tone; a neutral count on the muted tint is a
|
|
77
|
+
// figure and stays in the foreground too.
|
|
78
|
+
for (const tone of ALL_TONES) {
|
|
79
|
+
expect(TONE_SOFT[tone]).toBe(`${TONE_TINT[tone]} text-foreground`)
|
|
80
|
+
expect(TONE_TINT[tone]).not.toMatch(/\btext-/)
|
|
58
81
|
}
|
|
59
82
|
})
|
|
60
83
|
|
|
61
84
|
it("routes the neutral tone to the muted ramp", () => {
|
|
62
|
-
expect(TONE_SOFT.neutral).toBe("bg-muted text-
|
|
85
|
+
expect(TONE_SOFT.neutral).toBe("bg-muted text-foreground")
|
|
86
|
+
expect(TONE_TINT.neutral).toBe("bg-muted")
|
|
63
87
|
expect(TONE_DOT.neutral).toBe("bg-muted-foreground")
|
|
64
|
-
expect(
|
|
88
|
+
expect(TONE_INK.neutral).toBe("text-foreground")
|
|
65
89
|
expect(TONE_BORDER.neutral).toBe("border-border")
|
|
66
90
|
expect(TONE_ICON.neutral).toBe("text-muted-foreground")
|
|
67
91
|
})
|
|
68
92
|
})
|
|
93
|
+
|
|
94
|
+
describe("the tone vocabulary: retired names map onto the five tones", () => {
|
|
95
|
+
it("pins every alias (one red, one amber: critical/error/destructive are danger)", () => {
|
|
96
|
+
expect(TONE_ALIASES).toEqual({
|
|
97
|
+
critical: "danger",
|
|
98
|
+
error: "danger",
|
|
99
|
+
destructive: "danger",
|
|
100
|
+
failure: "danger",
|
|
101
|
+
warn: "warning",
|
|
102
|
+
ok: "success",
|
|
103
|
+
positive: "success",
|
|
104
|
+
notice: "info",
|
|
105
|
+
default: "neutral",
|
|
106
|
+
muted: "neutral",
|
|
107
|
+
secondary: "neutral",
|
|
108
|
+
})
|
|
109
|
+
for (const tone of Object.values(TONE_ALIASES)) expect(isToneVariant(tone)).toBe(true)
|
|
110
|
+
for (const alias of Object.keys(TONE_ALIASES)) expect(isToneVariant(alias)).toBe(false)
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
it("keeps a tone as it is", () => {
|
|
114
|
+
for (const tone of ALL_TONES) expect(resolveTone(tone)).toBe(tone)
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
it("maps every alias onto its tone, ignoring case and padding", () => {
|
|
118
|
+
for (const [alias, tone] of Object.entries(TONE_ALIASES)) {
|
|
119
|
+
expect(resolveTone(alias), alias).toBe(tone)
|
|
120
|
+
}
|
|
121
|
+
expect(resolveTone("Critical")).toBe("danger")
|
|
122
|
+
expect(resolveTone(" WARN ")).toBe("warning")
|
|
123
|
+
expect(resolveTone(" Danger")).toBe("danger")
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
it("falls back to neutral (or the caller's fallback) for anything else", () => {
|
|
127
|
+
for (const unknown of ["", " ", "purple", "constructor", "toString", "__proto__"]) {
|
|
128
|
+
expect(resolveTone(unknown), JSON.stringify(unknown)).toBe("neutral")
|
|
129
|
+
}
|
|
130
|
+
for (const notAString of [undefined, null, 3, true, {}, ["danger"]]) {
|
|
131
|
+
expect(resolveTone(notAString)).toBe("neutral")
|
|
132
|
+
}
|
|
133
|
+
expect(resolveTone("purple", "info")).toBe("info")
|
|
134
|
+
expect(resolveTone(undefined, "warning")).toBe("warning")
|
|
135
|
+
expect(resolveTone("critical", "info")).toBe("danger")
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
it("isToneVariant accepts exactly the five tones", () => {
|
|
139
|
+
for (const tone of ALL_TONES) expect(isToneVariant(tone)).toBe(true)
|
|
140
|
+
for (const other of ["Danger", "critical", "", undefined, null, 1, { tone: "info" }]) {
|
|
141
|
+
expect(isToneVariant(other)).toBe(false)
|
|
142
|
+
}
|
|
143
|
+
})
|
|
144
|
+
})
|
package/src/lib/tone-utils.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Single source of truth for the semantic "tones" used across the tone-aware
|
|
3
|
-
* components (`Pills`, `
|
|
3
|
+
* components (`Pills`, `StatusBadge`, `KpiGrid`, `Badge`, …).
|
|
4
|
+
*
|
|
5
|
+
* ONE vocabulary: `success | warning | danger | info | neutral`
|
|
6
|
+
* ({@link TONE_VARIANTS}). Retired or foreign names (`critical`, `error`,
|
|
7
|
+
* `destructive`, …) map onto it through {@link resolveTone}, so a tone that
|
|
8
|
+
* arrives as data (a server-built KPI cell) never renders without a colour.
|
|
4
9
|
*
|
|
5
10
|
* Every status tone has THREE roles (Miragon CI §3.3), each its own token in
|
|
6
11
|
* `theme.css` with light and dark values:
|
|
@@ -8,14 +13,14 @@
|
|
|
8
13
|
* | Role | Token | Map |
|
|
9
14
|
* | ---------------------------- | --------------- | -------------- |
|
|
10
15
|
* | fill, dot, border, icon | `--<tone>` | `TONE_DOT`, `TONE_BORDER`, `TONE_ICON` |
|
|
11
|
-
* | tint (badge/banner surface) | `--<tone>-soft` | `TONE_SOFT` (text on it: the foreground) |
|
|
12
|
-
* | text on a plain surface | `--<tone>-ink` | `
|
|
16
|
+
* | tint (badge/banner surface) | `--<tone>-soft` | `TONE_SOFT`, `TONE_TINT` (text on it: the foreground) |
|
|
17
|
+
* | text on a plain surface | `--<tone>-ink` | `TONE_INK` |
|
|
13
18
|
*
|
|
14
19
|
* Status is never text in its own colour: success and warning reach only
|
|
15
20
|
* 1.67:1 / 1.83:1 on white, so their ink is the foreground and the tone shows
|
|
16
21
|
* as a dot, icon or border NEXT to the text. On a tint the text is always the
|
|
17
22
|
* foreground (CI §3.3: "-soft: Text darauf --cd-schwarz, Rand in der
|
|
18
|
-
* Statusfarbe"), so pair `TONE_SOFT` with `TONE_BORDER`. `
|
|
23
|
+
* Statusfarbe"), so pair `TONE_SOFT` with `TONE_BORDER`. `TONE_INK` is for
|
|
19
24
|
* a tone's words on a plain surface (a field error); figures — counts, KPI
|
|
20
25
|
* values, trends — never take it: they stay in the foreground next to a
|
|
21
26
|
* `TONE_DOT`. In dark mode the colours are the `-hell` values and the tints
|
|
@@ -27,20 +32,90 @@
|
|
|
27
32
|
*/
|
|
28
33
|
export type ToneVariant = "neutral" | "info" | "success" | "warning" | "danger"
|
|
29
34
|
|
|
35
|
+
/** Every tone, in severity order (for loops, pickers and validation). */
|
|
36
|
+
export const TONE_VARIANTS: readonly ToneVariant[] = [
|
|
37
|
+
"danger",
|
|
38
|
+
"warning",
|
|
39
|
+
"success",
|
|
40
|
+
"info",
|
|
41
|
+
"neutral",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Retired or foreign tone names and the tone each one means. One red and one
|
|
46
|
+
* amber (CI §3.3): `critical`, `error` and `destructive` are `danger`; there
|
|
47
|
+
* is no separate severity above it. The names on the left are deprecated:
|
|
48
|
+
* write the {@link ToneVariant} on the right. The map stays so data written
|
|
49
|
+
* against an older vocabulary (a widget-shell `critical`, a shadcn
|
|
50
|
+
* `destructive`) keeps its colour.
|
|
51
|
+
*/
|
|
52
|
+
export const TONE_ALIASES = {
|
|
53
|
+
critical: "danger",
|
|
54
|
+
error: "danger",
|
|
55
|
+
destructive: "danger",
|
|
56
|
+
failure: "danger",
|
|
57
|
+
warn: "warning",
|
|
58
|
+
ok: "success",
|
|
59
|
+
positive: "success",
|
|
60
|
+
notice: "info",
|
|
61
|
+
default: "neutral",
|
|
62
|
+
muted: "neutral",
|
|
63
|
+
secondary: "neutral",
|
|
64
|
+
} as const satisfies Record<string, ToneVariant>
|
|
65
|
+
|
|
66
|
+
/** A name {@link resolveTone} maps onto a tone (see {@link TONE_ALIASES}). */
|
|
67
|
+
export type ToneAlias = keyof typeof TONE_ALIASES
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* What a tone-aware component accepts: a {@link ToneVariant}, or a retired
|
|
71
|
+
* name that {@link resolveTone} maps onto one.
|
|
72
|
+
*/
|
|
73
|
+
export type ToneName = ToneVariant | ToneAlias
|
|
74
|
+
|
|
75
|
+
const TONE_SET: ReadonlySet<string> = new Set(TONE_VARIANTS)
|
|
76
|
+
|
|
77
|
+
/** Whether `value` is one of the five tones (a type guard for data from the wire). */
|
|
78
|
+
export function isToneVariant(value: unknown): value is ToneVariant {
|
|
79
|
+
return typeof value === "string" && TONE_SET.has(value)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Map any tone name onto the vocabulary: a {@link ToneVariant} as is, a
|
|
84
|
+
* retired name ({@link TONE_ALIASES}, case-insensitive) onto its tone, and
|
|
85
|
+
* anything else — unknown, empty, not a string — onto `fallback` (default
|
|
86
|
+
* `"neutral"`, the tone without a state).
|
|
87
|
+
*/
|
|
88
|
+
export function resolveTone(name: unknown, fallback: ToneVariant = "neutral"): ToneVariant {
|
|
89
|
+
if (typeof name !== "string") return fallback
|
|
90
|
+
const key = name.trim().toLowerCase()
|
|
91
|
+
if (TONE_SET.has(key)) return key as ToneVariant
|
|
92
|
+
return Object.hasOwn(TONE_ALIASES, key) ? TONE_ALIASES[key as ToneAlias] : fallback
|
|
93
|
+
}
|
|
94
|
+
|
|
30
95
|
/**
|
|
31
96
|
* Tinted surface + its text (the foreground: CI §3.3) — badges, pills,
|
|
32
97
|
* banners. Pair it with {@link TONE_BORDER}: the rim carries the tone, and in
|
|
33
|
-
* dark mode (no tints) it is all that does.
|
|
98
|
+
* dark mode (no tints) it is all that does. The neutral tint is the muted
|
|
99
|
+
* surface, its text the foreground too (a count on it is a figure).
|
|
34
100
|
*/
|
|
35
101
|
export const TONE_SOFT: Record<ToneVariant, string> = {
|
|
36
|
-
neutral: "bg-muted text-
|
|
102
|
+
neutral: "bg-muted text-foreground",
|
|
37
103
|
info: "bg-info-soft text-foreground",
|
|
38
104
|
success: "bg-success-soft text-foreground",
|
|
39
105
|
warning: "bg-warning-soft text-foreground",
|
|
40
106
|
danger: "bg-danger-soft text-foreground",
|
|
41
107
|
}
|
|
42
108
|
|
|
43
|
-
/**
|
|
109
|
+
/** The tint alone, for a surface that sets its text itself (a soft KPI card). */
|
|
110
|
+
export const TONE_TINT: Record<ToneVariant, string> = {
|
|
111
|
+
neutral: "bg-muted",
|
|
112
|
+
info: "bg-info-soft",
|
|
113
|
+
success: "bg-success-soft",
|
|
114
|
+
warning: "bg-warning-soft",
|
|
115
|
+
danger: "bg-danger-soft",
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** Solid dot colour — a status dot next to a label, a bar, a meter fill. */
|
|
44
119
|
export const TONE_DOT: Record<ToneVariant, string> = {
|
|
45
120
|
neutral: "bg-muted-foreground",
|
|
46
121
|
info: "bg-info",
|
|
@@ -56,7 +131,7 @@ export const TONE_DOT: Record<ToneVariant, string> = {
|
|
|
56
131
|
* {@link TONE_ICON} to show the tone. Never on a tint (use {@link TONE_SOFT})
|
|
57
132
|
* and never for figures (they stay in the foreground next to a dot).
|
|
58
133
|
*/
|
|
59
|
-
export const
|
|
134
|
+
export const TONE_INK: Record<ToneVariant, string> = {
|
|
60
135
|
neutral: "text-foreground",
|
|
61
136
|
info: "text-info-ink",
|
|
62
137
|
success: "text-success-ink",
|
|
@@ -64,6 +139,12 @@ export const TONE_TEXT: Record<ToneVariant, string> = {
|
|
|
64
139
|
danger: "text-danger-ink",
|
|
65
140
|
}
|
|
66
141
|
|
|
142
|
+
/**
|
|
143
|
+
* @deprecated Renamed to {@link TONE_INK} after the role it maps onto
|
|
144
|
+
* (`--<tone>-ink`); the same object, kept for existing imports.
|
|
145
|
+
*/
|
|
146
|
+
export const TONE_TEXT: Record<ToneVariant, string> = TONE_INK
|
|
147
|
+
|
|
67
148
|
/** Border colour in the tone — the rim of a tinted badge, banner or toast. */
|
|
68
149
|
export const TONE_BORDER: Record<ToneVariant, string> = {
|
|
69
150
|
neutral: "border-border",
|