@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
|
@@ -12,6 +12,8 @@ export const en: ToolkitMessages = {
|
|
|
12
12
|
details: "Details",
|
|
13
13
|
dismiss: "Close notification",
|
|
14
14
|
notifications: "Notifications",
|
|
15
|
+
loadFailed: "This content couldn't be loaded",
|
|
16
|
+
loadFailedHint: "Try again. If it fails again, ask in the chat.",
|
|
15
17
|
},
|
|
16
18
|
view: {
|
|
17
19
|
loading: "Loading view…",
|
|
@@ -26,6 +28,11 @@ export const en: ToolkitMessages = {
|
|
|
26
28
|
errorHint: "Try again. If it fails again, ask in the chat.",
|
|
27
29
|
errorHintNoRetry: "Ask in the chat to open this view again.",
|
|
28
30
|
retry: "Try again",
|
|
31
|
+
blockedTitle: "The chat didn't pass the result to this view",
|
|
32
|
+
blockedHint: "Load the view yourself, or ask in the chat for the current state.",
|
|
33
|
+
blockedHintNoRetry:
|
|
34
|
+
"The action already ran, so the view doesn't start it again. Ask in the chat for the current state.",
|
|
35
|
+
blockedRetry: "Load view",
|
|
29
36
|
errorDetails: "Details",
|
|
30
37
|
stepErrorsTitle: "Some data couldn't be loaded",
|
|
31
38
|
stepErrorsHint: "Try again to load the missing data.",
|
|
@@ -33,14 +40,21 @@ export const en: ToolkitMessages = {
|
|
|
33
40
|
refreshFailedHint: "You're seeing the last loaded data. Try again in a moment.",
|
|
34
41
|
fullscreenFailed: "Couldn't switch the display mode.",
|
|
35
42
|
fullscreenFailedHint: "The view stays as it is. Try again in a moment.",
|
|
43
|
+
dataAsOf: (time) => `Reloaded at ${time}`,
|
|
36
44
|
},
|
|
37
45
|
listFooter: {
|
|
38
46
|
showing: ({ shown, total, noun }) => `Showing ${shown} of ${total} ${noun}`.trimEnd(),
|
|
39
47
|
defaultNoun: "rows",
|
|
40
48
|
loadMore: "Load more",
|
|
41
49
|
loadingMore: "Loading…",
|
|
50
|
+
updating: "Updating…",
|
|
51
|
+
noResults: "No entries",
|
|
52
|
+
refreshFailed: "Couldn't update the list",
|
|
53
|
+
refreshFailedHint: "You're seeing the previous result. Try again in a moment.",
|
|
54
|
+
loadMoreFailed: "Couldn't load more entries",
|
|
55
|
+
loadMoreFailedHint: "The entries above are current. Try again in a moment.",
|
|
42
56
|
},
|
|
43
|
-
filterBar: { searchPlaceholder: "Filter…" },
|
|
57
|
+
filterBar: { searchPlaceholder: "Filter…", searchLabel: "Filter", chipsLabel: "Filters" },
|
|
44
58
|
pills: { live: "Live" },
|
|
45
59
|
renderer: {
|
|
46
60
|
widgetUnavailable: "This element isn't available in this version.",
|
|
@@ -52,13 +66,37 @@ export const en: ToolkitMessages = {
|
|
|
52
66
|
builderBadge: "Editing",
|
|
53
67
|
previewBadge: "Preview",
|
|
54
68
|
catalogue: "Catalogue",
|
|
55
|
-
save: "Save",
|
|
69
|
+
save: "Save as dashboard",
|
|
56
70
|
done: "Done",
|
|
71
|
+
apply: "Apply",
|
|
72
|
+
applyHint: "Shows your changes in this view and saves nothing.",
|
|
73
|
+
discard: "Discard",
|
|
74
|
+
discardHint: "Leaves the editor. The view stays as it was.",
|
|
75
|
+
discardHintStandalone: "Resets your changes.",
|
|
76
|
+
saveHint: "Saves the layout as a dashboard you can open again later.",
|
|
77
|
+
unsavedChanges: "Unsaved changes",
|
|
78
|
+
discardDialogTitle: "Discard your changes?",
|
|
79
|
+
discardDialogDescription:
|
|
80
|
+
"You lose your changes to this view. The view stays as it was before you edited it.",
|
|
81
|
+
discardDialogDescriptionSaved:
|
|
82
|
+
"The view goes back to how it was before you edited it. Your saved dashboard keeps your changes.",
|
|
83
|
+
keepEditing: "Keep editing",
|
|
84
|
+
discardConfirm: "Discard changes",
|
|
85
|
+
advanced: "Advanced",
|
|
86
|
+
advancedHint: "Shows the data sources, the catalogue and the technical IDs.",
|
|
87
|
+
renameTabNamed: (label) => `Rename tab “${label}”`,
|
|
88
|
+
removeTabNamed: (label) => `Remove tab “${label}”`,
|
|
89
|
+
rowTarget: "New elements go here",
|
|
90
|
+
stepProvides: "provides",
|
|
91
|
+
keyValueFor: (key) => (key ? `Value of ${key}` : "Value"),
|
|
57
92
|
viewLayoutTab: "Layout",
|
|
58
93
|
viewPipelineTab: "Data sources",
|
|
59
94
|
viewPreviewTab: "Preview",
|
|
60
|
-
saveDialogTitle: "Save dashboard",
|
|
61
|
-
saveDialogDescription:
|
|
95
|
+
saveDialogTitle: "Save as dashboard",
|
|
96
|
+
saveDialogDescription:
|
|
97
|
+
"Saves the layout and its data sources as a dashboard you can open again later. To change this view, use Apply.",
|
|
98
|
+
saveDialogDescriptionStandalone:
|
|
99
|
+
"Saves the layout and its data sources as a dashboard you can open again later.",
|
|
62
100
|
saveNameLabel: "Name",
|
|
63
101
|
saveDescriptionLabel: "Description",
|
|
64
102
|
saveConfirm: "Save dashboard",
|
|
@@ -75,10 +113,11 @@ export const en: ToolkitMessages = {
|
|
|
75
113
|
emptyKeys: "No keys yet.",
|
|
76
114
|
emptySteps: "No steps yet.",
|
|
77
115
|
statusUpToDate: "Up to date",
|
|
78
|
-
statusPending: "
|
|
79
|
-
statusRefreshing: "
|
|
116
|
+
statusPending: "Updating soon…",
|
|
117
|
+
statusRefreshing: "Updating…",
|
|
80
118
|
paletteHeader: "Elements",
|
|
81
|
-
|
|
119
|
+
paletteLoading: "Loading elements…",
|
|
120
|
+
paletteEmpty: "No elements available yet. Elements appear as soon as their data is there.",
|
|
82
121
|
canvasEmpty: "Drag an element here or click one in the list.",
|
|
83
122
|
canvasDropHint: "Drop to add",
|
|
84
123
|
addRow: "Add row",
|
|
@@ -117,9 +156,9 @@ export const en: ToolkitMessages = {
|
|
|
117
156
|
saveNamePlaceholder: "Name of the dashboard",
|
|
118
157
|
saveDescriptionPlaceholder: "Optional",
|
|
119
158
|
saved: (name) => `Dashboard “${name}” saved.`,
|
|
120
|
-
renameHint: "
|
|
121
|
-
renameTitle: "
|
|
122
|
-
autoApplies: "Changes
|
|
159
|
+
renameHint: "Rename a tab with F2, a double-click or its pencil button.",
|
|
160
|
+
renameTitle: "F2 or double-click to rename",
|
|
161
|
+
autoApplies: "Changes show in the preview right away",
|
|
123
162
|
noMoreKeys: "No more keys available.",
|
|
124
163
|
parsedType: "Detected type",
|
|
125
164
|
valueType: (code) =>
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest"
|
|
2
2
|
import {
|
|
3
|
+
formatClockTime,
|
|
3
4
|
formatCount,
|
|
5
|
+
resolveFormatLocale,
|
|
4
6
|
resolveToolkitLocale,
|
|
5
7
|
resolveToolkitMessages,
|
|
6
8
|
TOOLKIT_LOCALES,
|
|
@@ -24,6 +26,9 @@ const SAMPLES: Record<string, unknown[]> = {
|
|
|
24
26
|
configureElementSet: [2],
|
|
25
27
|
unsupportedType: ["array"],
|
|
26
28
|
valueType: ["num"],
|
|
29
|
+
renameTabNamed: ["Übersicht"],
|
|
30
|
+
removeTabNamed: ["Übersicht"],
|
|
31
|
+
keyValueFor: ["demo:id"],
|
|
27
32
|
}
|
|
28
33
|
|
|
29
34
|
/** Every message of a catalog as `[path, rendered text]`. */
|
|
@@ -155,6 +160,40 @@ describe("toolkit catalogs", () => {
|
|
|
155
160
|
expect(de.propsSheet.unsupportedType("array")).toBe(
|
|
156
161
|
"Diesen Feldtyp (array) kannst du hier nicht bearbeiten.",
|
|
157
162
|
)
|
|
163
|
+
expect(en.builder.renameTabNamed("Ops")).toBe("Rename tab “Ops”")
|
|
164
|
+
expect(de.builder.renameTabNamed("Ops")).toBe("Tab „Ops“ umbenennen")
|
|
165
|
+
expect(en.builder.removeTabNamed("Ops")).toBe("Remove tab “Ops”")
|
|
166
|
+
expect(de.builder.removeTabNamed("Ops")).toBe("Tab „Ops“ entfernen")
|
|
167
|
+
expect(en.builder.keyValueFor("demo:id")).toBe("Value of demo:id")
|
|
168
|
+
expect(de.builder.keyValueFor("demo:id")).toBe("Wert von demo:id")
|
|
169
|
+
// A key row without a name yet still has an accessible name.
|
|
170
|
+
expect(en.builder.keyValueFor("")).toBe("Value")
|
|
171
|
+
expect(de.builder.keyValueFor("")).toBe("Wert")
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
it("builder: names Apply, Discard and Save apart in user words (U18)", () => {
|
|
175
|
+
const { en, de } = TOOLKIT_MESSAGES
|
|
176
|
+
expect([en.builder.apply, en.builder.discard, en.builder.save]).toEqual([
|
|
177
|
+
"Apply",
|
|
178
|
+
"Discard",
|
|
179
|
+
"Save as dashboard",
|
|
180
|
+
])
|
|
181
|
+
expect([de.builder.apply, de.builder.discard, de.builder.save]).toEqual([
|
|
182
|
+
"Übernehmen",
|
|
183
|
+
"Verwerfen",
|
|
184
|
+
"Als Dashboard speichern",
|
|
185
|
+
])
|
|
186
|
+
expect(de.builder.advanced).toBe("Erweitert")
|
|
187
|
+
// The refresh state no longer reuses the Apply verb.
|
|
188
|
+
for (const m of [en.builder, de.builder]) {
|
|
189
|
+
expect(m.statusPending).not.toMatch(/apply|übernomm/i)
|
|
190
|
+
expect(m.statusRefreshing).not.toMatch(/apply|übernomm/i)
|
|
191
|
+
}
|
|
192
|
+
// Rename is announced with its keyboard path, not double-click alone.
|
|
193
|
+
expect(en.builder.renameHint).toContain("F2")
|
|
194
|
+
expect(de.builder.renameHint).toContain("F2")
|
|
195
|
+
expect(en.builder.renameTitle).toContain("F2")
|
|
196
|
+
expect(de.builder.renameTitle).toContain("F2")
|
|
158
197
|
})
|
|
159
198
|
})
|
|
160
199
|
|
|
@@ -203,6 +242,31 @@ describe("resolveToolkitMessages", () => {
|
|
|
203
242
|
})
|
|
204
243
|
})
|
|
205
244
|
|
|
245
|
+
describe("resolveToolkitMessages: the deprecated builder `done`", () => {
|
|
246
|
+
it("labels Apply with an override of `done`", () => {
|
|
247
|
+
const merged = resolveToolkitMessages("de", { builder: { done: "Los" } })
|
|
248
|
+
expect(merged.builder.apply).toBe("Los")
|
|
249
|
+
expect(merged.builder.done).toBe("Los")
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
it("keeps an `apply` override over `done`", () => {
|
|
253
|
+
const merged = resolveToolkitMessages("en", { builder: { done: "Finish", apply: "Show" } })
|
|
254
|
+
expect(merged.builder.apply).toBe("Show")
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
it("leaves Apply alone without a `done` override", () => {
|
|
258
|
+
expect(resolveToolkitMessages("en", { builder: { save: "Keep" } }).builder.apply).toBe(
|
|
259
|
+
TOOLKIT_MESSAGES.en.builder.apply,
|
|
260
|
+
)
|
|
261
|
+
expect(resolveToolkitMessages("de", { builder: { apply: "Zeigen" } }).builder.apply).toBe(
|
|
262
|
+
"Zeigen",
|
|
263
|
+
)
|
|
264
|
+
expect(resolveToolkitMessages("de", { view: { refresh: "Neu" } }).builder).toEqual(
|
|
265
|
+
TOOLKIT_MESSAGES.de.builder,
|
|
266
|
+
)
|
|
267
|
+
})
|
|
268
|
+
})
|
|
269
|
+
|
|
206
270
|
describe("formatCount", () => {
|
|
207
271
|
it("groups digits per locale", () => {
|
|
208
272
|
expect(formatCount(1234567, "en")).toBe("1,234,567")
|
|
@@ -214,3 +278,72 @@ describe("formatCount", () => {
|
|
|
214
278
|
expect(formatCount(1234, "not a locale!")).toBe("1,234")
|
|
215
279
|
})
|
|
216
280
|
})
|
|
281
|
+
|
|
282
|
+
describe("formatClockTime (the view's data-as-of stamp)", () => {
|
|
283
|
+
const at = Date.UTC(2026, 9, 11, 14, 32)
|
|
284
|
+
|
|
285
|
+
it("formats hours and minutes per locale in the host's time zone", () => {
|
|
286
|
+
expect(formatClockTime(at, "de-DE", "UTC")).toBe("14:32")
|
|
287
|
+
expect(formatClockTime(at, "de-DE", "Europe/Berlin")).toBe("16:32")
|
|
288
|
+
expect(formatClockTime(at, "en-GB", "UTC")).toBe("14:32")
|
|
289
|
+
})
|
|
290
|
+
|
|
291
|
+
it("never pads the hour of a 12-hour clock: 2:32 PM, not 02:32 PM", () => {
|
|
292
|
+
expect(formatClockTime(at, "en-US", "UTC")).toBe("2:32 PM")
|
|
293
|
+
expect(formatClockTime(Date.UTC(2026, 9, 11, 9, 5), "en-US", "UTC")).toBe("9:05 AM")
|
|
294
|
+
// Minutes keep two digits.
|
|
295
|
+
expect(formatClockTime(Date.UTC(2026, 9, 11, 14, 5), "de-DE", "UTC")).toBe("14:05")
|
|
296
|
+
})
|
|
297
|
+
|
|
298
|
+
it("a zone the runtime rejects drops the zone, never the locale", () => {
|
|
299
|
+
// Windows ids reach a view from some hosts; German stays a 24-hour clock.
|
|
300
|
+
expect(formatClockTime(at, "de", "W. Europe Standard Time")).toMatch(/^\d{2}:\d{2}$/)
|
|
301
|
+
expect(formatClockTime(at, "de-DE", "Not/AZone")).toMatch(/^\d{2}:\d{2}$/)
|
|
302
|
+
expect(formatClockTime(at, "en-US", "Not/AZone")).toMatch(/^[1-9]\d?:\d\d [AP]M$/)
|
|
303
|
+
})
|
|
304
|
+
|
|
305
|
+
it("an invalid tag falls back to English, in the host's zone when it is valid", () => {
|
|
306
|
+
expect(() => formatClockTime(at, "not a locale!", "UTC")).not.toThrow()
|
|
307
|
+
expect(formatClockTime(at, "not a locale!", "UTC")).toBe("2:32 PM")
|
|
308
|
+
expect(formatClockTime(at, "not a locale!", "Not/AZone")).toMatch(/^[1-9]\d?:\d\d [AP]M$/)
|
|
309
|
+
})
|
|
310
|
+
|
|
311
|
+
it("the catalogs stamp it in words that carry its meaning", () => {
|
|
312
|
+
expect(TOOLKIT_MESSAGES.en.view.dataAsOf("2:32 PM")).toBe("Reloaded at 2:32 PM")
|
|
313
|
+
expect(TOOLKIT_MESSAGES.de.view.dataAsOf("14:32")).toBe("Neu geladen um 14:32 Uhr")
|
|
314
|
+
})
|
|
315
|
+
})
|
|
316
|
+
|
|
317
|
+
describe("resolveFormatLocale (a time in the language of its sentence)", () => {
|
|
318
|
+
it("formats in the language the words come from, never the host's other language", () => {
|
|
319
|
+
// A German profile on an en-US host: German words, a German clock.
|
|
320
|
+
expect(resolveFormatLocale("de", "en-US")).toBe("de")
|
|
321
|
+
expect(resolveFormatLocale("en", "de-DE")).toBe("en")
|
|
322
|
+
expect(
|
|
323
|
+
formatClockTime(Date.UTC(2026, 9, 11, 14, 32), resolveFormatLocale("de", "en-US"), "UTC"),
|
|
324
|
+
).toBe("14:32")
|
|
325
|
+
})
|
|
326
|
+
|
|
327
|
+
it("keeps the regional tag of the same language: en-GB keeps its 24-hour clock", () => {
|
|
328
|
+
expect(resolveFormatLocale("en", "en-GB")).toBe("en-GB")
|
|
329
|
+
expect(resolveFormatLocale("en-GB", "en-GB")).toBe("en-GB")
|
|
330
|
+
expect(resolveFormatLocale("de", "DE_AT")).toBe("DE-AT")
|
|
331
|
+
expect(resolveFormatLocale(" de ", "de-CH ")).toBe("de-CH")
|
|
332
|
+
})
|
|
333
|
+
|
|
334
|
+
it("prefers the text locale's own region over the host's", () => {
|
|
335
|
+
expect(resolveFormatLocale("en-GB", "en-US")).toBe("en-GB")
|
|
336
|
+
expect(resolveFormatLocale("de-AT", "de-DE")).toBe("de-AT")
|
|
337
|
+
})
|
|
338
|
+
|
|
339
|
+
it("maps a language without a catalog onto the catalog's, like the words", () => {
|
|
340
|
+
// French words fall back to English, so does their clock.
|
|
341
|
+
expect(resolveFormatLocale("fr-FR", "fr-FR")).toBe("en")
|
|
342
|
+
expect(resolveFormatLocale(undefined, "de-DE")).toBe("en")
|
|
343
|
+
expect(resolveFormatLocale(undefined, "en-AU")).toBe("en-AU")
|
|
344
|
+
expect(resolveFormatLocale("de")).toBe("de")
|
|
345
|
+
// A bare tag or a lookalike prefix is no region of the language.
|
|
346
|
+
expect(resolveFormatLocale("en", "en")).toBe("en")
|
|
347
|
+
expect(resolveFormatLocale("de", "dex-DE")).toBe("de")
|
|
348
|
+
})
|
|
349
|
+
})
|
|
@@ -8,13 +8,14 @@
|
|
|
8
8
|
* Pure data + functions (no React, no `mcp-use/react`): browser-safe and
|
|
9
9
|
* shared by the root barrel and the `./app` shell alike.
|
|
10
10
|
*/
|
|
11
|
-
import type { ToolkitMessages } from "./message-types.js"
|
|
11
|
+
import type { BuilderMessages, ToolkitMessages } from "./message-types.js"
|
|
12
12
|
import { de } from "./messages-de.js"
|
|
13
13
|
import { en } from "./messages-en.js"
|
|
14
14
|
|
|
15
15
|
export type {
|
|
16
16
|
BuilderMessages,
|
|
17
17
|
CommonMessages,
|
|
18
|
+
FilterBarMessages,
|
|
18
19
|
ListFooterMessages,
|
|
19
20
|
PropsSheetMessages,
|
|
20
21
|
RendererMessages,
|
|
@@ -63,11 +64,24 @@ export function resolveToolkitMessages(
|
|
|
63
64
|
filterBar: { ...base.filterBar, ...override.filterBar },
|
|
64
65
|
pills: { ...base.pills, ...override.pills },
|
|
65
66
|
renderer: { ...base.renderer, ...override.renderer },
|
|
66
|
-
builder:
|
|
67
|
+
builder: mergeBuilder(base.builder, override.builder),
|
|
67
68
|
propsSheet: { ...base.propsSheet, ...override.propsSheet },
|
|
68
69
|
}
|
|
69
70
|
}
|
|
70
71
|
|
|
72
|
+
/**
|
|
73
|
+
* The builder section with its override. An override of the deprecated
|
|
74
|
+
* `done` still labels the commit button, Apply, unless it sets `apply` too.
|
|
75
|
+
*/
|
|
76
|
+
function mergeBuilder(
|
|
77
|
+
base: BuilderMessages,
|
|
78
|
+
override: Partial<BuilderMessages> | undefined,
|
|
79
|
+
): BuilderMessages {
|
|
80
|
+
const merged = { ...base, ...override }
|
|
81
|
+
if (override?.done !== undefined && override.apply === undefined) merged.apply = override.done
|
|
82
|
+
return merged
|
|
83
|
+
}
|
|
84
|
+
|
|
71
85
|
/**
|
|
72
86
|
* Format a count for display in `locale` (grouping separators per locale:
|
|
73
87
|
* `1,234` / `1.234`). An invalid tag falls back to English instead of
|
|
@@ -80,3 +94,50 @@ export function formatCount(value: number, locale: string | undefined): string {
|
|
|
80
94
|
return new Intl.NumberFormat("en").format(value)
|
|
81
95
|
}
|
|
82
96
|
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* The tag a toolkit sentence formats its numbers and times in: the language
|
|
100
|
+
* its words come from (`textLocale`, mapped onto a shipped catalog the way
|
|
101
|
+
* {@link resolveToolkitMessages} maps it), with the regional tag of that
|
|
102
|
+
* language from `textLocale` or else `hostLocale`, so `en-GB` keeps its
|
|
103
|
+
* 24-hour clock. A tag of another language never mixes in: German words
|
|
104
|
+
* never carry an English clock ("Neu geladen um 4:32 PM Uhr").
|
|
105
|
+
*/
|
|
106
|
+
export function resolveFormatLocale(textLocale: string | undefined, hostLocale?: string): string {
|
|
107
|
+
const language = resolveToolkitLocale(textLocale)
|
|
108
|
+
const regionOfLanguage = (tag: string | undefined): string | undefined => {
|
|
109
|
+
const normal = tag?.trim().replace(/_/g, "-")
|
|
110
|
+
if (!normal?.includes("-")) return undefined
|
|
111
|
+
return normal.toLowerCase().split("-")[0] === language ? normal : undefined
|
|
112
|
+
}
|
|
113
|
+
return regionOfLanguage(textLocale) ?? regionOfLanguage(hostLocale) ?? language
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Format the clock time (hours and minutes) of `epochMs` in `locale`, in the
|
|
118
|
+
* host's `timeZone` when it reports one: `14:32` / `2:32 PM`. The hour is
|
|
119
|
+
* `numeric`, so each locale pads it its own way (a 12-hour clock never shows
|
|
120
|
+
* `02:32 PM`). A zone the runtime rejects (a Windows id, say) drops the zone,
|
|
121
|
+
* never the locale; an invalid tag falls back to English. It never throws
|
|
122
|
+
* inside a render.
|
|
123
|
+
*/
|
|
124
|
+
export function formatClockTime(
|
|
125
|
+
epochMs: number,
|
|
126
|
+
locale: string | undefined,
|
|
127
|
+
timeZone?: string,
|
|
128
|
+
): string {
|
|
129
|
+
const clock: Intl.DateTimeFormatOptions = { hour: "numeric", minute: "2-digit" }
|
|
130
|
+
const attempts: readonly (readonly [string | undefined, string | undefined])[] = [
|
|
131
|
+
[locale, timeZone],
|
|
132
|
+
[locale, undefined],
|
|
133
|
+
["en", timeZone],
|
|
134
|
+
]
|
|
135
|
+
for (const [tag, zone] of attempts) {
|
|
136
|
+
try {
|
|
137
|
+
return new Intl.DateTimeFormat(tag, { ...clock, timeZone: zone }).format(epochMs)
|
|
138
|
+
} catch {
|
|
139
|
+
// The next attempt drops what the runtime rejected.
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return new Intl.DateTimeFormat("en", clock).format(epochMs)
|
|
143
|
+
}
|
package/src/primitives/alert.tsx
CHANGED
|
@@ -11,6 +11,9 @@ const alertVariants = cva(
|
|
|
11
11
|
default: "bg-card text-card-foreground",
|
|
12
12
|
// Tone model: tint, a border and icon in the danger colour; title and
|
|
13
13
|
// description in the foreground (CI §3.3: text on a tint is black).
|
|
14
|
+
danger:
|
|
15
|
+
"border-danger bg-danger-soft text-foreground *:data-[slot=alert-title]:text-foreground *:data-[slot=alert-description]:text-foreground [&>svg]:text-danger",
|
|
16
|
+
/** @deprecated Use `danger`: one name per tone. */
|
|
14
17
|
destructive:
|
|
15
18
|
"border-danger bg-danger-soft text-foreground *:data-[slot=alert-title]:text-foreground *:data-[slot=alert-description]:text-foreground [&>svg]:text-danger",
|
|
16
19
|
},
|
package/src/primitives/badge.tsx
CHANGED
|
@@ -6,16 +6,21 @@ import { FOCUS_RING } from "../lib/focus-ring.js"
|
|
|
6
6
|
import { TONE_BORDER, TONE_SOFT } from "../lib/tone-utils.js"
|
|
7
7
|
import { cn } from "../lib/utils.js"
|
|
8
8
|
|
|
9
|
-
// Badges and chips take radius sm (CI §6)
|
|
10
|
-
//
|
|
11
|
-
//
|
|
9
|
+
// Badges and chips take radius sm (CI §6) and the 13 px floor of the type
|
|
10
|
+
// scale (`text-small`; `leading-4` keeps the one line inside the 20 px badge).
|
|
11
|
+
// The status variants follow the tone model and its ONE vocabulary: tint +
|
|
12
|
+
// foreground text, rim in the tone (TONE_SOFT + TONE_BORDER; CI §3.3 "-soft:
|
|
13
|
+
// Text darauf --cd-schwarz"). The ink is never text on a tint. `danger` is the
|
|
14
|
+
// status red; `destructive` is its deprecated name, kept for existing calls.
|
|
12
15
|
const badgeVariants = cva(
|
|
13
|
-
`group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-sm border border-transparent px-2 py-0.5 text-
|
|
16
|
+
`group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-sm border border-transparent px-2 py-0.5 text-small leading-4 font-medium whitespace-nowrap transition-all ${FOCUS_RING} has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-danger [&>svg]:pointer-events-none [&>svg]:size-3!`,
|
|
14
17
|
{
|
|
15
18
|
variants: {
|
|
16
19
|
variant: {
|
|
17
20
|
default: "bg-primary text-primary-foreground [a]:hover:bg-primary-hover",
|
|
18
21
|
secondary: "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
|
|
22
|
+
danger: `${TONE_SOFT.danger} ${TONE_BORDER.danger}`,
|
|
23
|
+
/** @deprecated Use `danger`: one name per tone. */
|
|
19
24
|
destructive: `${TONE_SOFT.danger} ${TONE_BORDER.danger}`,
|
|
20
25
|
success: `${TONE_SOFT.success} ${TONE_BORDER.success}`,
|
|
21
26
|
warning: `${TONE_SOFT.warning} ${TONE_BORDER.warning}`,
|
|
@@ -25,7 +25,7 @@ const buttonVariants = cva(
|
|
|
25
25
|
},
|
|
26
26
|
size: {
|
|
27
27
|
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
|
28
|
-
xs: "h-6 gap-1 rounded-sm px-2 text-
|
|
28
|
+
xs: "h-6 gap-1 rounded-sm px-2 text-small has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3",
|
|
29
29
|
sm: "h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5",
|
|
30
30
|
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
|
31
31
|
icon: "size-9",
|
|
@@ -5,6 +5,7 @@ import { XIcon } from "lucide-react"
|
|
|
5
5
|
import { Dialog as DialogPrimitive } from "radix-ui"
|
|
6
6
|
|
|
7
7
|
import { FOCUS_RING } from "../lib/focus-ring.js"
|
|
8
|
+
import { useFocusReturn } from "../lib/focus-return.js"
|
|
8
9
|
import { useToolkitMessages } from "../locale/locale-provider.js"
|
|
9
10
|
import { cn } from "../lib/utils.js"
|
|
10
11
|
import { usePortalContainer } from "../theme/portal-container.js"
|
|
@@ -58,6 +59,8 @@ function DialogContent({
|
|
|
58
59
|
children,
|
|
59
60
|
showCloseButton = true,
|
|
60
61
|
closeLabel,
|
|
62
|
+
onOpenAutoFocus,
|
|
63
|
+
onCloseAutoFocus,
|
|
61
64
|
...props
|
|
62
65
|
}: React.ComponentProps<typeof DialogPrimitive.Content> & {
|
|
63
66
|
showCloseButton?: boolean
|
|
@@ -65,6 +68,9 @@ function DialogContent({
|
|
|
65
68
|
closeLabel?: string
|
|
66
69
|
}) {
|
|
67
70
|
const messages = useToolkitMessages()
|
|
71
|
+
// Without a DialogTrigger, Radix returns the focus nowhere: hand it back to
|
|
72
|
+
// the element that opened it (lib/focus-return.ts).
|
|
73
|
+
const focusReturn = useFocusReturn({ onOpenAutoFocus, onCloseAutoFocus })
|
|
68
74
|
return (
|
|
69
75
|
<DialogPortal data-slot="dialog-portal">
|
|
70
76
|
<DialogOverlay />
|
|
@@ -75,6 +81,7 @@ function DialogContent({
|
|
|
75
81
|
className,
|
|
76
82
|
)}
|
|
77
83
|
{...props}
|
|
84
|
+
{...focusReturn}
|
|
78
85
|
>
|
|
79
86
|
{children}
|
|
80
87
|
{showCloseButton && (
|
|
@@ -136,7 +143,9 @@ function DialogTitle({ className, ...props }: React.ComponentProps<typeof Dialog
|
|
|
136
143
|
return (
|
|
137
144
|
<DialogPrimitive.Title
|
|
138
145
|
data-slot="dialog-title"
|
|
139
|
-
|
|
146
|
+
// The dialog's heading row (CI §8: weight 600) on the type scale's
|
|
147
|
+
// section step (21 px / 600, the CI h3).
|
|
148
|
+
className={cn("text-section", className)}
|
|
140
149
|
{...props}
|
|
141
150
|
/>
|
|
142
151
|
)
|
|
@@ -86,7 +86,7 @@ function SelectLabel({ className, ...props }: React.ComponentProps<typeof Select
|
|
|
86
86
|
return (
|
|
87
87
|
<SelectPrimitive.Label
|
|
88
88
|
data-slot="select-label"
|
|
89
|
-
className={cn("text-muted-foreground px-2 py-1.5
|
|
89
|
+
className={cn("text-muted-foreground text-small px-2 py-1.5", className)}
|
|
90
90
|
{...props}
|
|
91
91
|
/>
|
|
92
92
|
)
|
package/src/primitives/sheet.tsx
CHANGED
|
@@ -5,6 +5,7 @@ import { XIcon } from "lucide-react"
|
|
|
5
5
|
import { Dialog as SheetPrimitive } from "radix-ui"
|
|
6
6
|
|
|
7
7
|
import { FOCUS_RING } from "../lib/focus-ring.js"
|
|
8
|
+
import { useFocusReturn } from "../lib/focus-return.js"
|
|
8
9
|
import { useToolkitMessages } from "../locale/locale-provider.js"
|
|
9
10
|
import { cn } from "../lib/utils.js"
|
|
10
11
|
import { usePortalContainer } from "../theme/portal-container.js"
|
|
@@ -55,6 +56,8 @@ function SheetContent({
|
|
|
55
56
|
side = "right",
|
|
56
57
|
showCloseButton = true,
|
|
57
58
|
closeLabel,
|
|
59
|
+
onOpenAutoFocus,
|
|
60
|
+
onCloseAutoFocus,
|
|
58
61
|
...props
|
|
59
62
|
}: React.ComponentProps<typeof SheetPrimitive.Content> & {
|
|
60
63
|
side?: "top" | "right" | "bottom" | "left"
|
|
@@ -63,6 +66,9 @@ function SheetContent({
|
|
|
63
66
|
closeLabel?: string
|
|
64
67
|
}) {
|
|
65
68
|
const messages = useToolkitMessages()
|
|
69
|
+
// Without a SheetTrigger, Radix returns the focus nowhere: hand it back to
|
|
70
|
+
// the element that opened it (lib/focus-return.ts).
|
|
71
|
+
const focusReturn = useFocusReturn({ onOpenAutoFocus, onCloseAutoFocus })
|
|
66
72
|
return (
|
|
67
73
|
<SheetPortal>
|
|
68
74
|
<SheetOverlay />
|
|
@@ -81,6 +87,7 @@ function SheetContent({
|
|
|
81
87
|
className,
|
|
82
88
|
)}
|
|
83
89
|
{...props}
|
|
90
|
+
{...focusReturn}
|
|
84
91
|
>
|
|
85
92
|
{children}
|
|
86
93
|
{showCloseButton && (
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { QueryClient, QueryClientProvider, useQueryClient } from "@tanstack/react-query"
|
|
2
|
+
import { renderToStaticMarkup } from "react-dom/server"
|
|
3
|
+
import { describe, expect, it, vi } from "vitest"
|
|
4
|
+
import { createStandaloneHostBridge, HostBridgeProvider } from "../app/host-bridge.js"
|
|
5
|
+
import { useToolQueryKey } from "../hooks/use-tool-query.js"
|
|
6
|
+
import {
|
|
7
|
+
AppQueryProvider,
|
|
8
|
+
queryClient,
|
|
9
|
+
useCallTool,
|
|
10
|
+
useToolNameInQueryKey,
|
|
11
|
+
} from "./query-provider.js"
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The tool transport and client of the data hooks (toolkit#176 N25, K18):
|
|
15
|
+
* `AppQueryProvider`'s `callTool`, else the nearest HostBridge — so the
|
|
16
|
+
* documented HostBridgeProvider mount fetches instead of idling.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
type Caller = ReturnType<typeof useCallTool>
|
|
20
|
+
|
|
21
|
+
function probe(tree: (node: React.ReactNode) => React.ReactNode): {
|
|
22
|
+
callTool: Caller
|
|
23
|
+
client: QueryClient
|
|
24
|
+
} {
|
|
25
|
+
let callTool: Caller
|
|
26
|
+
let client: QueryClient | undefined
|
|
27
|
+
function Probe() {
|
|
28
|
+
callTool = useCallTool()
|
|
29
|
+
client = useQueryClient()
|
|
30
|
+
return null
|
|
31
|
+
}
|
|
32
|
+
renderToStaticMarkup(tree(<Probe />))
|
|
33
|
+
if (!client) throw new Error("probe did not render")
|
|
34
|
+
return { callTool, client }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
describe("useCallTool", () => {
|
|
38
|
+
it("AppQueryProvider's callTool wins over a bridge", async () => {
|
|
39
|
+
const explicit = vi.fn(() => Promise.resolve("explicit"))
|
|
40
|
+
const bridgeCall = vi.fn(() => Promise.resolve("bridge"))
|
|
41
|
+
const bridge = createStandaloneHostBridge({ callTool: bridgeCall })
|
|
42
|
+
const { callTool } = probe((node) => (
|
|
43
|
+
<HostBridgeProvider bridge={bridge}>
|
|
44
|
+
<AppQueryProvider callTool={explicit}>{node}</AppQueryProvider>
|
|
45
|
+
</HostBridgeProvider>
|
|
46
|
+
))
|
|
47
|
+
await expect(callTool?.("t", { a: 1 })).resolves.toBe("explicit")
|
|
48
|
+
expect(explicit).toHaveBeenCalledWith("t", { a: 1 })
|
|
49
|
+
expect(bridgeCall).not.toHaveBeenCalled()
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it("falls back to the nearest HostBridgeProvider's callTool (N25)", async () => {
|
|
53
|
+
const bridgeCall = vi.fn(() => Promise.resolve("bridge"))
|
|
54
|
+
const bridge = createStandaloneHostBridge({ callTool: bridgeCall })
|
|
55
|
+
const { callTool } = probe((node) => (
|
|
56
|
+
<AppQueryProvider>
|
|
57
|
+
<HostBridgeProvider bridge={bridge}>{node}</HostBridgeProvider>
|
|
58
|
+
</AppQueryProvider>
|
|
59
|
+
))
|
|
60
|
+
await expect(callTool?.("orders_get", { id: 7 })).resolves.toBe("bridge")
|
|
61
|
+
expect(bridgeCall).toHaveBeenCalledWith("orders_get", { id: 7 })
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it("is undefined with neither (the query then fails with NoToolTransportError)", () => {
|
|
65
|
+
const { callTool } = probe((node) => <AppQueryProvider>{node}</AppQueryProvider>)
|
|
66
|
+
expect(callTool).toBeUndefined()
|
|
67
|
+
})
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
describe("AppQueryProvider", () => {
|
|
71
|
+
it("shares the toolkit's client by default", () => {
|
|
72
|
+
expect(probe((node) => <AppQueryProvider>{node}</AppQueryProvider>).client).toBe(queryClient)
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it("takes a client of its own, e.g. one per mount", () => {
|
|
76
|
+
const own = new QueryClient()
|
|
77
|
+
expect(probe((node) => <AppQueryProvider client={own}>{node}</AppQueryProvider>).client).toBe(
|
|
78
|
+
own,
|
|
79
|
+
)
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it("a nested provider without client keeps its parent's, like toolNameInQueryKey", () => {
|
|
83
|
+
const own = new QueryClient()
|
|
84
|
+
const other = new QueryClient()
|
|
85
|
+
const nested = probe((node) => (
|
|
86
|
+
<AppQueryProvider client={own}>
|
|
87
|
+
<AppQueryProvider callTool={vi.fn()}>{node}</AppQueryProvider>
|
|
88
|
+
</AppQueryProvider>
|
|
89
|
+
))
|
|
90
|
+
expect(nested.client).toBe(own)
|
|
91
|
+
const replaced = probe((node) => (
|
|
92
|
+
<AppQueryProvider client={own}>
|
|
93
|
+
<AppQueryProvider client={other}>{node}</AppQueryProvider>
|
|
94
|
+
</AppQueryProvider>
|
|
95
|
+
))
|
|
96
|
+
expect(replaced.client).toBe(other)
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
it("a plain TanStack QueryClientProvider is not inherited: the shared client stays", () => {
|
|
100
|
+
const foreign = new QueryClient()
|
|
101
|
+
const { client } = probe((node) => (
|
|
102
|
+
<QueryClientProvider client={foreign}>
|
|
103
|
+
<AppQueryProvider>{node}</AppQueryProvider>
|
|
104
|
+
</QueryClientProvider>
|
|
105
|
+
))
|
|
106
|
+
expect(client).toBe(queryClient)
|
|
107
|
+
})
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
describe("AppQueryProvider toolNameInQueryKey (K18)", () => {
|
|
111
|
+
/** The setting and the key `useToolQueryKey` builds under `tree`. */
|
|
112
|
+
function keyUnder(tree: (node: React.ReactNode) => React.ReactNode) {
|
|
113
|
+
let setting: boolean | undefined
|
|
114
|
+
let key: unknown[] | undefined
|
|
115
|
+
function Probe() {
|
|
116
|
+
setting = useToolNameInQueryKey()
|
|
117
|
+
key = useToolQueryKey(["feed"], "feed_data", { id: 1 })
|
|
118
|
+
return null
|
|
119
|
+
}
|
|
120
|
+
renderToStaticMarkup(tree(<Probe />))
|
|
121
|
+
return { setting, key }
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
it("is off by default: the 2.x key, outside a provider too", () => {
|
|
125
|
+
expect(keyUnder((node) => node)).toEqual({ setting: false, key: ["feed", { id: 1 }] })
|
|
126
|
+
expect(keyUnder((node) => <AppQueryProvider>{node}</AppQueryProvider>)).toEqual({
|
|
127
|
+
setting: false,
|
|
128
|
+
key: ["feed", { id: 1 }],
|
|
129
|
+
})
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
it("on, the hooks' key carries the tool name", () => {
|
|
133
|
+
expect(
|
|
134
|
+
keyUnder((node) => <AppQueryProvider toolNameInQueryKey>{node}</AppQueryProvider>),
|
|
135
|
+
).toEqual({ setting: true, key: ["feed", "feed_data", { id: 1 }] })
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
it("a nested provider keeps its parent's setting unless it sets its own", () => {
|
|
139
|
+
expect(
|
|
140
|
+
keyUnder((node) => (
|
|
141
|
+
<AppQueryProvider toolNameInQueryKey>
|
|
142
|
+
<AppQueryProvider callTool={vi.fn()}>{node}</AppQueryProvider>
|
|
143
|
+
</AppQueryProvider>
|
|
144
|
+
)).setting,
|
|
145
|
+
).toBe(true)
|
|
146
|
+
expect(
|
|
147
|
+
keyUnder((node) => (
|
|
148
|
+
<AppQueryProvider toolNameInQueryKey>
|
|
149
|
+
<AppQueryProvider toolNameInQueryKey={false}>{node}</AppQueryProvider>
|
|
150
|
+
</AppQueryProvider>
|
|
151
|
+
)).setting,
|
|
152
|
+
).toBe(false)
|
|
153
|
+
})
|
|
154
|
+
})
|