@xemahq/ui-kernel 0.10.0 → 0.11.1
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/dist/lib/biome-host/agent-validation.d.ts.map +1 -1
- package/dist/lib/biome-host/agent-validation.js +26 -13
- package/dist/lib/biome-host/agent-validation.js.map +1 -1
- package/dist/lib/biome-host/biome-registry.d.ts.map +1 -1
- package/dist/lib/biome-host/biome-registry.js +27 -10
- package/dist/lib/biome-host/biome-registry.js.map +1 -1
- package/dist/lib/biome-host/biome-scope-hooks.d.ts.map +1 -1
- package/dist/lib/biome-host/biome-scope-hooks.js +2 -1
- package/dist/lib/biome-host/biome-scope-hooks.js.map +1 -1
- package/dist/lib/biome-host/biome-scoped-query.d.ts.map +1 -1
- package/dist/lib/biome-host/biome-scoped-query.js +5 -3
- package/dist/lib/biome-host/biome-scoped-query.js.map +1 -1
- package/dist/lib/biome-host/frontend-biome.d.ts +1 -1
- package/dist/lib/biome-host/frontend-biome.d.ts.map +1 -1
- package/dist/lib/biome-host/host-bridge.d.ts +1 -1
- package/dist/lib/biome-host/host-bridge.d.ts.map +1 -1
- package/dist/lib/biome-host/host-bridge.js.map +1 -1
- package/dist/lib/biome-host/i18n-hooks.d.ts.map +1 -1
- package/dist/lib/biome-host/i18n-hooks.js.map +1 -1
- package/dist/lib/biome-host/realtime-hooks.d.ts.map +1 -1
- package/dist/lib/biome-host/realtime-hooks.js.map +1 -1
- package/dist/lib/biome-host/session-contributions.d.ts +1 -1
- package/dist/lib/biome-host/session-contributions.d.ts.map +1 -1
- package/dist/lib/biome-host/use-mutation-with-error-toast.d.ts.map +1 -1
- package/dist/lib/biome-host/use-mutation-with-error-toast.js +3 -2
- package/dist/lib/biome-host/use-mutation-with-error-toast.js.map +1 -1
- package/dist/lib/biome-host/use-page-state.d.ts.map +1 -1
- package/dist/lib/biome-host/use-page-state.js +19 -10
- package/dist/lib/biome-host/use-page-state.js.map +1 -1
- package/dist/lib/capabilities/capability-provider.d.ts +1 -1
- package/dist/lib/capabilities/capability-provider.d.ts.map +1 -1
- package/dist/lib/capabilities/capability-provider.js +2 -2
- package/dist/lib/capabilities/capability-provider.js.map +1 -1
- package/dist/lib/capabilities/use-capability.d.ts +1 -1
- package/dist/lib/capabilities/use-capability.d.ts.map +1 -1
- package/dist/lib/capabilities/use-capability.js +2 -1
- package/dist/lib/capabilities/use-capability.js.map +1 -1
- package/dist/lib/contribution-registry/contribution-registry.d.ts.map +1 -1
- package/dist/lib/contribution-registry/contribution-registry.js +2 -1
- package/dist/lib/contribution-registry/contribution-registry.js.map +1 -1
- package/dist/lib/system-bus/deeplink.d.ts.map +1 -1
- package/dist/lib/system-bus/deeplink.js +4 -2
- package/dist/lib/system-bus/deeplink.js.map +1 -1
- package/dist/lib/system-bus/host-ports.d.ts +2 -2
- package/dist/lib/system-bus/host-ports.d.ts.map +1 -1
- package/dist/lib/system-bus/intent-registry.d.ts.map +1 -1
- package/dist/lib/system-bus/intent-registry.js +6 -3
- package/dist/lib/system-bus/intent-registry.js.map +1 -1
- package/dist/lib/system-bus/intents.d.ts +1 -1
- package/dist/lib/system-bus/intents.d.ts.map +1 -1
- package/dist/lib/system-bus/palette.d.ts +1 -1
- package/dist/lib/system-bus/palette.d.ts.map +1 -1
- package/dist/lib/system-bus/system-bus-builder.d.ts.map +1 -1
- package/dist/lib/system-bus/system-bus-builder.js +1 -1
- package/dist/lib/system-bus/system-bus-builder.js.map +1 -1
- package/dist/lib/system-bus/windows.d.ts +1 -1
- package/dist/lib/system-bus/windows.d.ts.map +1 -1
- package/dist/offline/outbox.d.ts +1 -0
- package/dist/offline/outbox.d.ts.map +1 -1
- package/dist/offline/outbox.js +4 -1
- package/dist/offline/outbox.js.map +1 -1
- package/dist/offline/react-hooks.d.ts +1 -1
- package/dist/offline/react-hooks.d.ts.map +1 -1
- package/dist/offline/react-hooks.js.map +1 -1
- package/dist/org-db/components/DbResultTable.d.ts.map +1 -1
- package/dist/org-db/components/DbResultTable.js +15 -6
- package/dist/org-db/components/DbResultTable.js.map +1 -1
- package/dist/registry/lib/agent-validation-host.d.ts.map +1 -1
- package/dist/registry/lib/agent-validation-host.js +1 -1
- package/dist/registry/lib/agent-validation-host.js.map +1 -1
- package/dist/registry/lib/biome-slot.d.ts.map +1 -1
- package/dist/registry/lib/biome-slot.js +5 -3
- package/dist/registry/lib/biome-slot.js.map +1 -1
- package/dist/registry/lib/primitives/SessionEventCard.d.ts.map +1 -1
- package/dist/registry/lib/primitives/SessionEventCard.js +14 -2
- package/dist/registry/lib/primitives/SessionEventCard.js.map +1 -1
- package/dist/registry/lib/primitives/SessionEventTimeline.d.ts.map +1 -1
- package/dist/registry/lib/primitives/SessionEventTimeline.js +6 -3
- package/dist/registry/lib/primitives/SessionEventTimeline.js.map +1 -1
- package/dist/registry/lib/primitives/SessionTabbedDrawer.d.ts.map +1 -1
- package/dist/registry/lib/primitives/SessionTabbedDrawer.js +10 -5
- package/dist/registry/lib/primitives/SessionTabbedDrawer.js.map +1 -1
- package/dist/registry/lib/session-context-provider.d.ts +1 -1
- package/dist/registry/lib/session-context-provider.d.ts.map +1 -1
- package/dist/registry/lib/session-context-provider.js.map +1 -1
- package/dist/registry/lib/session-selectors.d.ts.map +1 -1
- package/dist/registry/lib/session-selectors.js +35 -20
- package/dist/registry/lib/session-selectors.js.map +1 -1
- package/dist/session/comments/CommentRail.d.ts.map +1 -1
- package/dist/session/comments/CommentRail.js.map +1 -1
- package/dist/session/shell/SessionWorkspaceShell.d.ts.map +1 -1
- package/dist/session/shell/SessionWorkspaceShell.js.map +1 -1
- package/dist/session-kit/approvals/ApprovalCard.d.ts.map +1 -1
- package/dist/session-kit/approvals/ApprovalCard.js +6 -2
- package/dist/session-kit/approvals/ApprovalCard.js.map +1 -1
- package/dist/session-kit/approvals/ApprovalsCenter.d.ts.map +1 -1
- package/dist/session-kit/approvals/ApprovalsCenter.js +6 -3
- package/dist/session-kit/approvals/ApprovalsCenter.js.map +1 -1
- package/dist/session-kit/combobox/Combobox.d.ts.map +1 -1
- package/dist/session-kit/combobox/Combobox.js +20 -6
- package/dist/session-kit/combobox/Combobox.js.map +1 -1
- package/dist/session-kit/display/MD.d.ts.map +1 -1
- package/dist/session-kit/display/MD.js +7 -4
- package/dist/session-kit/display/MD.js.map +1 -1
- package/dist/ui/branding/app-branding-provider.d.ts.map +1 -1
- package/dist/ui/branding/app-branding-provider.js.map +1 -1
- package/dist/ui/branding/sanitize.d.ts.map +1 -1
- package/dist/ui/branding/sanitize.js +12 -6
- package/dist/ui/branding/sanitize.js.map +1 -1
- package/dist/ui/capture/document-preview.d.ts +1 -1
- package/dist/ui/capture/document-preview.d.ts.map +1 -1
- package/dist/ui/capture/document-preview.js +8 -6
- package/dist/ui/capture/document-preview.js.map +1 -1
- package/dist/ui/capture/field-spec-to-zod.d.ts.map +1 -1
- package/dist/ui/capture/field-spec-to-zod.js +45 -16
- package/dist/ui/capture/field-spec-to-zod.js.map +1 -1
- package/dist/ui/capture/field-spec-validate.d.ts.map +1 -1
- package/dist/ui/capture/field-spec-validate.js +79 -38
- package/dist/ui/capture/field-spec-validate.js.map +1 -1
- package/dist/ui/capture/field-spec.d.ts.map +1 -1
- package/dist/ui/capture/field-spec.js +17 -6
- package/dist/ui/capture/field-spec.js.map +1 -1
- package/dist/ui/capture/schema-field-input.d.ts.map +1 -1
- package/dist/ui/capture/schema-field-input.js +23 -18
- package/dist/ui/capture/schema-field-input.js.map +1 -1
- package/dist/ui/capture/schema-form.d.ts +1 -1
- package/dist/ui/capture/schema-form.d.ts.map +1 -1
- package/dist/ui/capture/schema-form.js +7 -6
- package/dist/ui/capture/schema-form.js.map +1 -1
- package/dist/ui/capture/use-prefill.d.ts.map +1 -1
- package/dist/ui/capture/use-prefill.js +32 -14
- package/dist/ui/capture/use-prefill.js.map +1 -1
- package/dist/ui/capture/use-schema-validation.d.ts +1 -1
- package/dist/ui/capture/use-schema-validation.d.ts.map +1 -1
- package/dist/ui/capture/use-schema-validation.js +13 -8
- package/dist/ui/capture/use-schema-validation.js.map +1 -1
- package/dist/ui/capture/use-wizard.d.ts.map +1 -1
- package/dist/ui/capture/use-wizard.js +23 -8
- package/dist/ui/capture/use-wizard.js.map +1 -1
- package/dist/ui/capture/value-format.d.ts.map +1 -1
- package/dist/ui/capture/value-format.js +6 -3
- package/dist/ui/capture/value-format.js.map +1 -1
- package/dist/ui/capture/wizard.d.ts.map +1 -1
- package/dist/ui/capture/wizard.js +3 -3
- package/dist/ui/capture/wizard.js.map +1 -1
- package/dist/ui/charts/bar-chart.d.ts +1 -1
- package/dist/ui/charts/bar-chart.d.ts.map +1 -1
- package/dist/ui/charts/bar-chart.js +1 -1
- package/dist/ui/charts/bar-chart.js.map +1 -1
- package/dist/ui/charts/chart-frame.d.ts +2 -2
- package/dist/ui/charts/chart-frame.d.ts.map +1 -1
- package/dist/ui/charts/chart-frame.js +4 -3
- package/dist/ui/charts/chart-frame.js.map +1 -1
- package/dist/ui/charts/chart-utils.d.ts.map +1 -1
- package/dist/ui/charts/chart-utils.js +32 -14
- package/dist/ui/charts/chart-utils.js.map +1 -1
- package/dist/ui/charts/donut-chart.d.ts +1 -1
- package/dist/ui/charts/donut-chart.d.ts.map +1 -1
- package/dist/ui/charts/donut-chart.js +7 -3
- package/dist/ui/charts/donut-chart.js.map +1 -1
- package/dist/ui/charts/line-chart.d.ts +1 -1
- package/dist/ui/charts/line-chart.d.ts.map +1 -1
- package/dist/ui/charts/line-chart.js +7 -4
- package/dist/ui/charts/line-chart.js.map +1 -1
- package/dist/ui/charts/sparkline.d.ts +1 -1
- package/dist/ui/charts/sparkline.d.ts.map +1 -1
- package/dist/ui/charts/sparkline.js +10 -6
- package/dist/ui/charts/sparkline.js.map +1 -1
- package/dist/ui/chrome/AsyncBoundary.d.ts +2 -2
- package/dist/ui/chrome/AsyncBoundary.d.ts.map +1 -1
- package/dist/ui/chrome/AsyncBoundary.js +3 -3
- package/dist/ui/chrome/AsyncBoundary.js.map +1 -1
- package/dist/ui/chrome/EmptyState.d.ts +19 -8
- package/dist/ui/chrome/EmptyState.d.ts.map +1 -1
- package/dist/ui/chrome/EmptyState.js +6 -4
- package/dist/ui/chrome/EmptyState.js.map +1 -1
- package/dist/ui/chrome/RecordCardList.d.ts +19 -0
- package/dist/ui/chrome/RecordCardList.d.ts.map +1 -0
- package/dist/ui/chrome/RecordCardList.js +12 -0
- package/dist/ui/chrome/RecordCardList.js.map +1 -0
- package/dist/ui/chrome/page-shell.d.ts +1 -1
- package/dist/ui/chrome/page-shell.d.ts.map +1 -1
- package/dist/ui/chrome/page-shell.js +16 -5
- package/dist/ui/chrome/page-shell.js.map +1 -1
- package/dist/ui/chrome/status-badge.d.ts.map +1 -1
- package/dist/ui/chrome/status-badge.js +1 -1
- package/dist/ui/chrome/status-badge.js.map +1 -1
- package/dist/ui/design-tokens.d.ts +4 -6
- package/dist/ui/design-tokens.d.ts.map +1 -1
- package/dist/ui/design-tokens.js +17 -11
- package/dist/ui/design-tokens.js.map +1 -1
- package/dist/ui/index.d.ts +1 -0
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +1 -0
- package/dist/ui/index.js.map +1 -1
- package/dist/ui/primitives/async-combobox.d.ts.map +1 -1
- package/dist/ui/primitives/async-combobox.js +4 -2
- package/dist/ui/primitives/async-combobox.js.map +1 -1
- package/dist/ui/primitives/badge.d.ts +2 -2
- package/dist/ui/primitives/badge.d.ts.map +1 -1
- package/dist/ui/primitives/badge.js +3 -3
- package/dist/ui/primitives/badge.js.map +1 -1
- package/dist/ui/primitives/button.d.ts +2 -2
- package/dist/ui/primitives/button.d.ts.map +1 -1
- package/dist/ui/primitives/button.js +5 -5
- package/dist/ui/primitives/button.js.map +1 -1
- package/dist/ui/primitives/overflow-tabs.d.ts.map +1 -1
- package/dist/ui/primitives/overflow-tabs.js +19 -12
- package/dist/ui/primitives/overflow-tabs.js.map +1 -1
- package/dist/ui/primitives/preview-device-frame.js +8 -4
- package/dist/ui/primitives/preview-device-frame.js.map +1 -1
- package/dist/ui/primitives/tag-multi-select.d.ts.map +1 -1
- package/dist/ui/primitives/tag-multi-select.js +10 -5
- package/dist/ui/primitives/tag-multi-select.js.map +1 -1
- package/dist/ui/primitives/url-preview-chrome.d.ts.map +1 -1
- package/dist/ui/primitives/url-preview-chrome.js.map +1 -1
- package/dist/vista/GenericCatalogueSurface.d.ts.map +1 -1
- package/dist/vista/GenericCatalogueSurface.js +5 -3
- package/dist/vista/GenericCatalogueSurface.js.map +1 -1
- package/dist/vista/VistaPanel.d.ts.map +1 -1
- package/dist/vista/VistaPanel.js +8 -4
- package/dist/vista/VistaPanel.js.map +1 -1
- package/dist/vista/html/CustomUiFrame.d.ts.map +1 -1
- package/dist/vista/html/CustomUiFrame.js +2 -1
- package/dist/vista/html/CustomUiFrame.js.map +1 -1
- package/dist/vista/html/custom-ui-frame-bridge.d.ts.map +1 -1
- package/dist/vista/html/custom-ui-frame-bridge.js +6 -3
- package/dist/vista/html/custom-ui-frame-bridge.js.map +1 -1
- package/dist/vista/html/custom-ui-intents.d.ts.map +1 -1
- package/dist/vista/html/custom-ui-intents.js +13 -15
- package/dist/vista/html/custom-ui-intents.js.map +1 -1
- package/dist/vista/surface-catalogue.d.ts.map +1 -1
- package/dist/vista/surface-catalogue.js +6 -3
- package/dist/vista/surface-catalogue.js.map +1 -1
- package/dist/vista/surfaces/FormSurface.d.ts.map +1 -1
- package/dist/vista/surfaces/FormSurface.js +20 -7
- package/dist/vista/surfaces/FormSurface.js.map +1 -1
- package/dist/vista/surfaces/TableSurface.d.ts.map +1 -1
- package/dist/vista/surfaces/TableSurface.js +2 -1
- package/dist/vista/surfaces/TableSurface.js.map +1 -1
- package/dist/vista/surfaces/blob-surfaces.d.ts.map +1 -1
- package/dist/vista/surfaces/blob-surfaces.js +7 -2
- package/dist/vista/surfaces/blob-surfaces.js.map +1 -1
- package/dist/vista/surfaces/elicitation-transport.d.ts.map +1 -1
- package/dist/vista/surfaces/elicitation-transport.js +14 -7
- package/dist/vista/surfaces/elicitation-transport.js.map +1 -1
- package/dist/vista/surfaces/file-surface-utils.d.ts.map +1 -1
- package/dist/vista/surfaces/file-surface-utils.js +8 -4
- package/dist/vista/surfaces/file-surface-utils.js.map +1 -1
- package/dist/vista/surfaces/register-surfaces.js +2 -1
- package/dist/vista/surfaces/register-surfaces.js.map +1 -1
- package/dist/vista/surfaces/text-surfaces.d.ts +1 -1
- package/dist/vista/surfaces/text-surfaces.d.ts.map +1 -1
- package/dist/vista/surfaces/text-surfaces.js +13 -7
- package/dist/vista/surfaces/text-surfaces.js.map +1 -1
- package/dist/vista/use-vista-tabs.d.ts.map +1 -1
- package/dist/vista/use-vista-tabs.js +8 -4
- package/dist/vista/use-vista-tabs.js.map +1 -1
- package/dist/vista/vista-code-renderer.d.ts.map +1 -1
- package/dist/vista/vista-code-renderer.js.map +1 -1
- package/dist/vista/vista-file-source.d.ts.map +1 -1
- package/dist/vista/vista-file-source.js +9 -5
- package/dist/vista/vista-file-source.js.map +1 -1
- package/dist/vista/vista-form-host.d.ts.map +1 -1
- package/dist/vista/vista-form-host.js +1 -1
- package/dist/vista/vista-form-host.js.map +1 -1
- package/dist/vista/vista-model.d.ts.map +1 -1
- package/dist/vista/vista-model.js +5 -3
- package/dist/vista/vista-model.js.map +1 -1
- package/dist/vista/vista-open-context.d.ts.map +1 -1
- package/dist/vista/vista-open-context.js +1 -1
- package/dist/vista/vista-open-context.js.map +1 -1
- package/dist/vista/vista-payloads.d.ts.map +1 -1
- package/dist/vista/vista-payloads.js +127 -63
- package/dist/vista/vista-payloads.js.map +1 -1
- package/dist/vista/vista-tabs-manifest.d.ts.map +1 -1
- package/dist/vista/vista-tabs-manifest.js +18 -9
- package/dist/vista/vista-tabs-manifest.js.map +1 -1
- package/dist/vista/vista-tools.d.ts.map +1 -1
- package/dist/vista/vista-tools.js +12 -6
- package/dist/vista/vista-tools.js.map +1 -1
- package/package.json +7 -4
- package/src/lib/biome-host/agent-validation.ts +37 -13
- package/src/lib/biome-host/biome-registry.ts +48 -13
- package/src/lib/biome-host/biome-scope-hooks.ts +3 -1
- package/src/lib/biome-host/biome-scoped-query.ts +7 -6
- package/src/lib/biome-host/frontend-biome.ts +1 -1
- package/src/lib/biome-host/host-bridge.ts +5 -4
- package/src/lib/biome-host/i18n-hooks.ts +1 -0
- package/src/lib/biome-host/realtime-hooks.ts +1 -0
- package/src/lib/biome-host/session-contributions.ts +1 -1
- package/src/lib/biome-host/use-mutation-with-error-toast.ts +4 -2
- package/src/lib/biome-host/use-page-state.ts +27 -10
- package/src/lib/capabilities/capability-provider.tsx +12 -5
- package/src/lib/capabilities/use-capability.ts +8 -4
- package/src/lib/contribution-registry/contribution-registry.ts +3 -1
- package/src/lib/system-bus/deeplink.ts +14 -4
- package/src/lib/system-bus/host-ports.ts +3 -7
- package/src/lib/system-bus/intent-registry.ts +12 -8
- package/src/lib/system-bus/intents.ts +6 -2
- package/src/lib/system-bus/palette.ts +2 -1
- package/src/lib/system-bus/system-bus-builder.ts +4 -2
- package/src/lib/system-bus/windows.ts +1 -1
- package/src/offline/outbox.test.ts +12 -3
- package/src/offline/outbox.ts +5 -1
- package/src/offline/react-hooks.ts +5 -2
- package/src/org-db/components/DbResultTable.tsx +48 -15
- package/src/registry/lib/agent-validation-host.ts +1 -2
- package/src/registry/lib/biome-slot.tsx +7 -4
- package/src/registry/lib/primitives/SessionEventCard.tsx +29 -5
- package/src/registry/lib/primitives/SessionEventTimeline.tsx +9 -3
- package/src/registry/lib/primitives/SessionTabbedDrawer.tsx +15 -5
- package/src/registry/lib/session-context-provider.tsx +1 -1
- package/src/registry/lib/session-selectors.ts +66 -27
- package/src/session/comments/CommentRail.tsx +2 -2
- package/src/session/shell/SessionWorkspaceShell.tsx +2 -2
- package/src/session-kit/approvals/ApprovalCard.tsx +7 -2
- package/src/session-kit/approvals/ApprovalsCenter.tsx +13 -4
- package/src/session-kit/combobox/Combobox.tsx +42 -11
- package/src/session-kit/display/MD.tsx +14 -5
- package/src/ui/branding/app-branding-provider.tsx +2 -3
- package/src/ui/branding/sanitize.ts +18 -6
- package/src/ui/capture/document-preview.tsx +84 -19
- package/src/ui/capture/field-spec-to-zod.ts +62 -14
- package/src/ui/capture/field-spec-validate.ts +132 -43
- package/src/ui/capture/field-spec.test.ts +2 -2
- package/src/ui/capture/field-spec.ts +41 -10
- package/src/ui/capture/schema-field-input.tsx +218 -38
- package/src/ui/capture/schema-form.tsx +54 -13
- package/src/ui/capture/use-prefill.ts +50 -17
- package/src/ui/capture/use-schema-validation.ts +45 -13
- package/src/ui/capture/use-wizard.ts +57 -11
- package/src/ui/capture/value-format.ts +14 -4
- package/src/ui/capture/wizard.tsx +9 -5
- package/src/ui/charts/bar-chart.tsx +8 -4
- package/src/ui/charts/chart-frame.tsx +18 -5
- package/src/ui/charts/chart-utils.ts +47 -15
- package/src/ui/charts/donut-chart.tsx +24 -6
- package/src/ui/charts/line-chart.tsx +34 -12
- package/src/ui/charts/sparkline.tsx +17 -9
- package/src/ui/chrome/AsyncBoundary.tsx +14 -6
- package/src/ui/chrome/EmptyState.tsx +116 -43
- package/src/ui/chrome/RecordCardList.tsx +98 -0
- package/src/ui/chrome/page-shell.spec.tsx +117 -0
- package/src/ui/chrome/page-shell.tsx +81 -25
- package/src/ui/chrome/status-badge.tsx +8 -4
- package/src/ui/design-tokens.ts +39 -13
- package/src/ui/index.ts +5 -0
- package/src/ui/primitives/async-combobox.tsx +7 -3
- package/src/ui/primitives/badge.tsx +14 -5
- package/src/ui/primitives/button.tsx +13 -6
- package/src/ui/primitives/overflow-tabs.tsx +46 -16
- package/src/ui/primitives/preview-device-frame.tsx +12 -4
- package/src/ui/primitives/tag-multi-select.tsx +30 -9
- package/src/ui/primitives/url-preview-chrome.tsx +1 -0
- package/src/vista/GenericCatalogueSurface.tsx +7 -5
- package/src/vista/VistaPanel.tsx +12 -4
- package/src/vista/html/CustomUiFrame.spec.tsx +3 -1
- package/src/vista/html/CustomUiFrame.tsx +3 -1
- package/src/vista/html/custom-ui-frame-bridge.ts +9 -3
- package/src/vista/html/custom-ui-intents.spec.ts +3 -1
- package/src/vista/html/custom-ui-intents.ts +19 -15
- package/src/vista/surface-catalogue.ts +9 -3
- package/src/vista/surfaces/FormSurface.tsx +27 -10
- package/src/vista/surfaces/TableSurface.tsx +4 -3
- package/src/vista/surfaces/blob-surfaces.tsx +13 -3
- package/src/vista/surfaces/elicitation-transport.ts +21 -7
- package/src/vista/surfaces/file-surface-utils.ts +11 -4
- package/src/vista/surfaces/register-surfaces.tsx +3 -1
- package/src/vista/surfaces/text-surfaces.tsx +21 -8
- package/src/vista/use-vista-tabs.ts +12 -4
- package/src/vista/vista-code-renderer.tsx +6 -5
- package/src/vista/vista-file-source.tsx +15 -14
- package/src/vista/vista-form-host.tsx +3 -8
- package/src/vista/vista-model.ts +10 -3
- package/src/vista/vista-open-context.tsx +3 -8
- package/src/vista/vista-payloads.ts +186 -63
- package/src/vista/vista-tabs-manifest.ts +28 -13
- package/src/vista/vista-tools.ts +18 -6
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
2
2
|
// Sparkline — tiny inline trend line, pure SVG, no axes/legend (Wave 9.2)
|
|
3
3
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
4
|
-
import type { ReactNode } from 'react';
|
|
5
4
|
|
|
6
5
|
import { cn } from '../cn';
|
|
6
|
+
import { chartColorAt, defaultFormatValue, niceScale } from './chart-utils';
|
|
7
7
|
|
|
8
8
|
import type { ChartValueFormatter } from './chart-types';
|
|
9
|
-
import {
|
|
9
|
+
import type { ReactNode } from 'react';
|
|
10
10
|
|
|
11
11
|
const VIEW_W = 100;
|
|
12
12
|
const VIEW_H = 28;
|
|
@@ -36,8 +36,13 @@ export function Sparkline({
|
|
|
36
36
|
}: SparklineProps): ReactNode {
|
|
37
37
|
const format = formatValue ?? defaultFormatValue;
|
|
38
38
|
const finite = values.filter((v) => Number.isFinite(v));
|
|
39
|
+
// Read the endpoints BEFORE the empty guard so the guard itself narrows them:
|
|
40
|
+
// both are `undefined` exactly when `finite` is empty, which is the case the
|
|
41
|
+
// guard already returns for.
|
|
42
|
+
const first = finite[0];
|
|
43
|
+
const last = finite[finite.length - 1];
|
|
39
44
|
|
|
40
|
-
if (
|
|
45
|
+
if (first === undefined || last === undefined) {
|
|
41
46
|
return (
|
|
42
47
|
<span
|
|
43
48
|
role="img"
|
|
@@ -49,7 +54,9 @@ export function Sparkline({
|
|
|
49
54
|
);
|
|
50
55
|
}
|
|
51
56
|
|
|
52
|
-
const scale = niceScale(Math.min(...finite), Math.max(...finite), {
|
|
57
|
+
const scale = niceScale(Math.min(...finite), Math.max(...finite), {
|
|
58
|
+
pinZero: false,
|
|
59
|
+
});
|
|
53
60
|
const span = scale.max - scale.min;
|
|
54
61
|
const yToPx = (v: number): number =>
|
|
55
62
|
VIEW_H - PAD_Y - ((v - scale.min) / span) * (VIEW_H - 2 * PAD_Y);
|
|
@@ -60,17 +67,18 @@ export function Sparkline({
|
|
|
60
67
|
const segments: string[] = [];
|
|
61
68
|
let current: string[] = [];
|
|
62
69
|
values.forEach((v, i) => {
|
|
63
|
-
if (Number.isFinite(v))
|
|
64
|
-
|
|
70
|
+
if (Number.isFinite(v)) {
|
|
71
|
+
current.push(`${xToPx(i)},${yToPx(v)}`);
|
|
72
|
+
} else if (current.length > 0) {
|
|
65
73
|
segments.push(current.join(' '));
|
|
66
74
|
current = [];
|
|
67
75
|
}
|
|
68
76
|
});
|
|
69
|
-
if (current.length > 0)
|
|
77
|
+
if (current.length > 0) {
|
|
78
|
+
segments.push(current.join(' '));
|
|
79
|
+
}
|
|
70
80
|
|
|
71
81
|
const stroke = chartColorAt(0, color);
|
|
72
|
-
const first = finite[0];
|
|
73
|
-
const last = finite[finite.length - 1];
|
|
74
82
|
const summary =
|
|
75
83
|
ariaLabel ??
|
|
76
84
|
`${title}: trend of ${finite.length} points, from ${format(first)} to ${format(last)}.`;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type { LucideIcon } from 'lucide-react';
|
|
2
|
-
import type { ReactNode } from 'react';
|
|
3
|
-
|
|
4
|
-
import ErrorCard, { type ErrorMessageFormatter } from './ErrorCard';
|
|
5
1
|
import EmptyState from './EmptyState';
|
|
2
|
+
import ErrorCard, { type ErrorMessageFormatter } from './ErrorCard';
|
|
6
3
|
import LoadingState, { type LoadingStateVariant } from './LoadingState';
|
|
7
4
|
|
|
5
|
+
import type { LucideIcon } from 'lucide-react';
|
|
6
|
+
import type { ReactNode } from 'react';
|
|
7
|
+
|
|
8
8
|
interface AsyncBoundaryEmpty {
|
|
9
9
|
icon: LucideIcon;
|
|
10
10
|
title: string;
|
|
@@ -55,11 +55,19 @@ export default function AsyncBoundary({
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
if (error) {
|
|
58
|
-
return
|
|
58
|
+
return (
|
|
59
|
+
<ErrorCard error={error} onRetry={onRetry} formatError={formatError} />
|
|
60
|
+
);
|
|
59
61
|
}
|
|
60
62
|
|
|
61
63
|
if (isEmpty && empty) {
|
|
62
|
-
return
|
|
64
|
+
return (
|
|
65
|
+
<EmptyState
|
|
66
|
+
icon={empty.icon}
|
|
67
|
+
title={empty.title}
|
|
68
|
+
description={empty.description}
|
|
69
|
+
/>
|
|
70
|
+
);
|
|
63
71
|
}
|
|
64
72
|
|
|
65
73
|
return <>{children}</>;
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import type { LucideIcon } from 'lucide-react';
|
|
2
|
-
import type { ComponentType, ReactNode } from 'react';
|
|
3
|
-
|
|
4
1
|
import { cn } from '../cn';
|
|
5
2
|
import { Button } from '../primitives/button';
|
|
6
3
|
import { Card, CardContent } from '../primitives/card';
|
|
7
4
|
|
|
8
|
-
|
|
5
|
+
import type { LucideIcon } from 'lucide-react';
|
|
6
|
+
import type { ComponentType, ReactNode } from 'react';
|
|
7
|
+
|
|
8
|
+
export type EmptyStateVariant =
|
|
9
|
+
| 'not-configured'
|
|
10
|
+
| 'no-results'
|
|
11
|
+
| 'disabled-context';
|
|
9
12
|
|
|
10
13
|
/**
|
|
11
14
|
* Visual density.
|
|
@@ -45,67 +48,94 @@ interface EmptyStateSecondaryAction {
|
|
|
45
48
|
href: string;
|
|
46
49
|
}
|
|
47
50
|
|
|
48
|
-
interface
|
|
49
|
-
icon: LucideIcon;
|
|
51
|
+
interface EmptyStateCommonProps {
|
|
50
52
|
title: string;
|
|
51
53
|
description?: ReactNode;
|
|
54
|
+
action?: EmptyStateAction;
|
|
55
|
+
secondaryAction?: EmptyStateSecondaryAction;
|
|
56
|
+
/** Host link component for `secondaryAction.href` (defaults to `<a>`). */
|
|
57
|
+
linkComponent?: EmptyStateLinkComponent;
|
|
58
|
+
className?: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** `comfortable` / `dense` render an icon block, so the icon is required. */
|
|
62
|
+
interface EmptyStateIconicProps extends EmptyStateCommonProps {
|
|
63
|
+
size?: Exclude<EmptyStateSize, 'inline'>;
|
|
64
|
+
icon: LucideIcon;
|
|
52
65
|
/** Optional illustration or extra content above the title */
|
|
53
66
|
illustration?: ReactNode;
|
|
54
67
|
/** Visual variant — drives default iconBg/iconColor unless overridden. */
|
|
55
68
|
variant?: EmptyStateVariant;
|
|
56
|
-
size?: EmptyStateSize;
|
|
57
69
|
/** Icon tint — overrides variant defaults */
|
|
58
70
|
iconBg?: string;
|
|
59
71
|
iconColor?: string;
|
|
60
|
-
action?: EmptyStateAction;
|
|
61
|
-
secondaryAction?: EmptyStateSecondaryAction;
|
|
62
|
-
/** Host link component for `secondaryAction.href` (defaults to `<a>`). */
|
|
63
|
-
linkComponent?: EmptyStateLinkComponent;
|
|
64
|
-
className?: string;
|
|
65
72
|
}
|
|
66
73
|
|
|
67
|
-
|
|
74
|
+
/**
|
|
75
|
+
* `inline` is text-only by definition — no icon block, therefore no icon,
|
|
76
|
+
* illustration, variant or icon tint. They are typed `never` rather than
|
|
77
|
+
* silently ignored: an accepted-but-unrendered prop is how callers ended up
|
|
78
|
+
* importing a lucide icon that renders nowhere.
|
|
79
|
+
*/
|
|
80
|
+
interface EmptyStateInlineProps extends EmptyStateCommonProps {
|
|
81
|
+
size: 'inline';
|
|
82
|
+
icon?: never;
|
|
83
|
+
illustration?: never;
|
|
84
|
+
variant?: never;
|
|
85
|
+
iconBg?: never;
|
|
86
|
+
iconColor?: never;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
type EmptyStateProps = EmptyStateIconicProps | EmptyStateInlineProps;
|
|
90
|
+
|
|
91
|
+
const VARIANT_DEFAULTS: Record<
|
|
92
|
+
EmptyStateVariant,
|
|
93
|
+
{ iconBg: string; iconColor: string }
|
|
94
|
+
> = {
|
|
68
95
|
'not-configured': { iconBg: 'bg-info/10', iconColor: 'text-info' },
|
|
69
96
|
'no-results': { iconBg: 'bg-warning/10', iconColor: 'text-warning' },
|
|
70
97
|
'disabled-context': { iconBg: 'bg-muted', iconColor: 'text-ink-3' },
|
|
71
98
|
};
|
|
72
99
|
|
|
73
|
-
export default function EmptyState({
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
description,
|
|
77
|
-
illustration,
|
|
78
|
-
variant = 'not-configured',
|
|
79
|
-
size = 'comfortable',
|
|
80
|
-
iconBg,
|
|
81
|
-
iconColor,
|
|
82
|
-
action,
|
|
83
|
-
secondaryAction,
|
|
84
|
-
linkComponent,
|
|
85
|
-
className,
|
|
86
|
-
}: Readonly<EmptyStateProps>) {
|
|
100
|
+
export default function EmptyState(props: Readonly<EmptyStateProps>) {
|
|
101
|
+
const { title, description, action, secondaryAction, linkComponent, className } =
|
|
102
|
+
props;
|
|
87
103
|
const Link = linkComponent ?? DefaultLink;
|
|
88
|
-
const defaults = VARIANT_DEFAULTS[variant];
|
|
89
|
-
const resolvedIconBg = iconBg ?? defaults.iconBg;
|
|
90
|
-
const resolvedIconColor = iconColor ?? defaults.iconColor;
|
|
91
104
|
|
|
92
|
-
if (size === 'inline') {
|
|
105
|
+
if (props.size === 'inline') {
|
|
93
106
|
return (
|
|
94
|
-
<div
|
|
107
|
+
<div
|
|
108
|
+
className={cn(
|
|
109
|
+
'flex items-center justify-center px-3 py-6 text-center text-body-2 text-ink-4',
|
|
110
|
+
className,
|
|
111
|
+
)}
|
|
112
|
+
>
|
|
95
113
|
<div className="max-w-sm space-y-1">
|
|
96
114
|
<p className="text-ink-3">{title}</p>
|
|
97
115
|
{description && <p className="text-ink-4">{description}</p>}
|
|
98
116
|
{(action || secondaryAction) && (
|
|
99
117
|
<div className="mt-3 flex items-center justify-center gap-2">
|
|
100
118
|
{action && (
|
|
101
|
-
<Button
|
|
119
|
+
<Button
|
|
120
|
+
size="sm"
|
|
121
|
+
variant="outline"
|
|
122
|
+
onClick={action.onClick}
|
|
123
|
+
className="h-7 gap-1.5 text-body-2"
|
|
124
|
+
>
|
|
102
125
|
{action.icon && <action.icon className="h-3.5 w-3.5" />}
|
|
103
126
|
{action.label}
|
|
104
127
|
</Button>
|
|
105
128
|
)}
|
|
106
129
|
{secondaryAction && (
|
|
107
|
-
<Button
|
|
108
|
-
|
|
130
|
+
<Button
|
|
131
|
+
asChild
|
|
132
|
+
size="sm"
|
|
133
|
+
variant="ghost"
|
|
134
|
+
className="h-7 px-2 text-body-2"
|
|
135
|
+
>
|
|
136
|
+
<Link href={secondaryAction.href}>
|
|
137
|
+
{secondaryAction.label}
|
|
138
|
+
</Link>
|
|
109
139
|
</Button>
|
|
110
140
|
)}
|
|
111
141
|
</div>
|
|
@@ -115,6 +145,18 @@ export default function EmptyState({
|
|
|
115
145
|
);
|
|
116
146
|
}
|
|
117
147
|
|
|
148
|
+
const {
|
|
149
|
+
icon: Icon,
|
|
150
|
+
illustration,
|
|
151
|
+
variant = 'not-configured',
|
|
152
|
+
size = 'comfortable',
|
|
153
|
+
iconBg,
|
|
154
|
+
iconColor,
|
|
155
|
+
} = props;
|
|
156
|
+
const defaults = VARIANT_DEFAULTS[variant];
|
|
157
|
+
const resolvedIconBg = iconBg ?? defaults.iconBg;
|
|
158
|
+
const resolvedIconColor = iconColor ?? defaults.iconColor;
|
|
159
|
+
|
|
118
160
|
if (size === 'dense') {
|
|
119
161
|
return (
|
|
120
162
|
<div
|
|
@@ -124,24 +166,41 @@ export default function EmptyState({
|
|
|
124
166
|
)}
|
|
125
167
|
>
|
|
126
168
|
{illustration ?? (
|
|
127
|
-
<div
|
|
169
|
+
<div
|
|
170
|
+
className={cn(
|
|
171
|
+
'h-9 w-9 rounded-xl flex items-center justify-center mb-3',
|
|
172
|
+
resolvedIconBg,
|
|
173
|
+
)}
|
|
174
|
+
>
|
|
128
175
|
<Icon className={cn('h-4 w-4', resolvedIconColor)} />
|
|
129
176
|
</div>
|
|
130
177
|
)}
|
|
131
178
|
<p className="text-body-1 font-semibold text-ink">{title}</p>
|
|
132
179
|
{description && (
|
|
133
|
-
<p className="mt-1 max-w-md text-body-2 leading-snug text-ink-3">
|
|
180
|
+
<p className="mt-1 max-w-md text-body-2 leading-snug text-ink-3">
|
|
181
|
+
{description}
|
|
182
|
+
</p>
|
|
134
183
|
)}
|
|
135
184
|
{(action || secondaryAction) && (
|
|
136
185
|
<div className="mt-3 flex items-center gap-2">
|
|
137
186
|
{action && (
|
|
138
|
-
<Button
|
|
187
|
+
<Button
|
|
188
|
+
size="sm"
|
|
189
|
+
variant="outline"
|
|
190
|
+
onClick={action.onClick}
|
|
191
|
+
className="h-7 gap-1.5 text-body-2"
|
|
192
|
+
>
|
|
139
193
|
{action.icon && <action.icon className="h-3.5 w-3.5" />}
|
|
140
194
|
{action.label}
|
|
141
195
|
</Button>
|
|
142
196
|
)}
|
|
143
197
|
{secondaryAction && (
|
|
144
|
-
<Button
|
|
198
|
+
<Button
|
|
199
|
+
asChild
|
|
200
|
+
size="sm"
|
|
201
|
+
variant="ghost"
|
|
202
|
+
className="h-7 px-2 text-body-2"
|
|
203
|
+
>
|
|
145
204
|
<Link href={secondaryAction.href}>{secondaryAction.label}</Link>
|
|
146
205
|
</Button>
|
|
147
206
|
)}
|
|
@@ -152,21 +211,35 @@ export default function EmptyState({
|
|
|
152
211
|
}
|
|
153
212
|
|
|
154
213
|
return (
|
|
155
|
-
<Card
|
|
214
|
+
<Card
|
|
215
|
+
className={cn(
|
|
216
|
+
'border-2 border-dashed border-border/50 rounded-xl',
|
|
217
|
+
className,
|
|
218
|
+
)}
|
|
219
|
+
>
|
|
156
220
|
<CardContent className="flex flex-col items-center justify-center py-16 text-center">
|
|
157
221
|
{illustration ?? (
|
|
158
|
-
<div
|
|
222
|
+
<div
|
|
223
|
+
className={`h-16 w-16 rounded-2xl flex items-center justify-center mb-5 ${resolvedIconBg}`}
|
|
224
|
+
>
|
|
159
225
|
<Icon className={`h-7 w-7 ${resolvedIconColor}`} />
|
|
160
226
|
</div>
|
|
161
227
|
)}
|
|
162
228
|
<p className="text-subtitle font-semibold text-ink">{title}</p>
|
|
163
229
|
{description && (
|
|
164
|
-
<p className="text-body-1 mt-2 max-w-sm text-ink-3 leading-relaxed">
|
|
230
|
+
<p className="text-body-1 mt-2 max-w-sm text-ink-3 leading-relaxed">
|
|
231
|
+
{description}
|
|
232
|
+
</p>
|
|
165
233
|
)}
|
|
166
234
|
{(action || secondaryAction) && (
|
|
167
235
|
<div className="mt-6 flex items-center gap-3">
|
|
168
236
|
{action && (
|
|
169
|
-
<Button
|
|
237
|
+
<Button
|
|
238
|
+
size="sm"
|
|
239
|
+
variant="outline"
|
|
240
|
+
onClick={action.onClick}
|
|
241
|
+
className="gap-2 h-10 px-5"
|
|
242
|
+
>
|
|
170
243
|
{action.icon && <action.icon className="h-4 w-4" />}
|
|
171
244
|
{action.label}
|
|
172
245
|
</Button>
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { cn } from '../cn';
|
|
2
|
+
|
|
3
|
+
import type { ReactNode } from 'react';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* One label/value row inside a record card — mirrors one table column.
|
|
7
|
+
*
|
|
8
|
+
* `key` exists because a card's rows are keyed by the COLUMN, and a column's
|
|
9
|
+
* identity is its field key, not its user-facing label (two columns may carry
|
|
10
|
+
* the same label). It defaults to `label` for the common case where the label
|
|
11
|
+
* is already unique.
|
|
12
|
+
*/
|
|
13
|
+
export interface RecordCardField {
|
|
14
|
+
readonly key?: string;
|
|
15
|
+
readonly label: string;
|
|
16
|
+
readonly value: ReactNode;
|
|
17
|
+
/**
|
|
18
|
+
* `data-*` attributes for the row element — e.g. the same hook the equivalent
|
|
19
|
+
* table cell carries, so a click-to-context resolver lands on the identical
|
|
20
|
+
* node at every viewport width. Deliberately `data-*` only: styling stays in
|
|
21
|
+
* this primitive, so a caller cannot re-open the drift it closed.
|
|
22
|
+
*/
|
|
23
|
+
readonly rowAttributes?: Readonly<Record<`data-${string}`, string>>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface RecordCardListProps<T> {
|
|
27
|
+
readonly items: readonly T[];
|
|
28
|
+
readonly getKey: (item: T) => string;
|
|
29
|
+
/** Card header rendered above the field list (title, status, row toggle). */
|
|
30
|
+
readonly renderHeader?: (item: T) => ReactNode;
|
|
31
|
+
readonly getFields: (item: T) => readonly RecordCardField[];
|
|
32
|
+
/** Row actions, rendered right-aligned below a divider. */
|
|
33
|
+
readonly renderActions?: (item: T) => ReactNode;
|
|
34
|
+
/** Free block appended after the actions (expanded detail). */
|
|
35
|
+
readonly renderDetail?: (item: T) => ReactNode;
|
|
36
|
+
/** Extra classes for the list element. */
|
|
37
|
+
readonly className?: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Small-viewport (below `md`) layout for a record list: one stacked card per
|
|
42
|
+
* record with the table's columns rendered as label/value rows, instead of a
|
|
43
|
+
* horizontal-scroll-only table.
|
|
44
|
+
*
|
|
45
|
+
* This is the ONE definition of that card chrome — spacing, border, divider,
|
|
46
|
+
* label/value alignment. Callers name their columns and their actions; they do
|
|
47
|
+
* not restate the chrome. Deliberately NOT parameterised by card class: a
|
|
48
|
+
* per-caller class hook is how the three copies this replaced drifted apart in
|
|
49
|
+
* the first place.
|
|
50
|
+
*/
|
|
51
|
+
export function RecordCardList<T>(props: Readonly<RecordCardListProps<T>>) {
|
|
52
|
+
const {
|
|
53
|
+
items,
|
|
54
|
+
getKey,
|
|
55
|
+
renderHeader,
|
|
56
|
+
getFields,
|
|
57
|
+
renderActions,
|
|
58
|
+
renderDetail,
|
|
59
|
+
className,
|
|
60
|
+
} = props;
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<ul className={cn('space-y-2', className)}>
|
|
64
|
+
{items.map((item) => {
|
|
65
|
+
const actions = renderActions?.(item);
|
|
66
|
+
const detail = renderDetail?.(item);
|
|
67
|
+
return (
|
|
68
|
+
<li
|
|
69
|
+
key={getKey(item)}
|
|
70
|
+
className="space-y-2 rounded-lg border border-rule bg-paper-elev p-3"
|
|
71
|
+
>
|
|
72
|
+
{renderHeader?.(item)}
|
|
73
|
+
<dl className="space-y-1.5">
|
|
74
|
+
{getFields(item).map((field) => (
|
|
75
|
+
<div
|
|
76
|
+
key={field.key ?? field.label}
|
|
77
|
+
{...field.rowAttributes}
|
|
78
|
+
className="flex justify-between gap-3 text-sm"
|
|
79
|
+
>
|
|
80
|
+
<dt className="shrink-0 text-ink-3">{field.label}</dt>
|
|
81
|
+
<dd className="text-right text-ink-2">{field.value}</dd>
|
|
82
|
+
</div>
|
|
83
|
+
))}
|
|
84
|
+
</dl>
|
|
85
|
+
{actions ? (
|
|
86
|
+
<div className="flex items-center justify-end gap-2 border-t border-rule/50 pt-2">
|
|
87
|
+
{actions}
|
|
88
|
+
</div>
|
|
89
|
+
) : null}
|
|
90
|
+
{detail}
|
|
91
|
+
</li>
|
|
92
|
+
);
|
|
93
|
+
})}
|
|
94
|
+
</ul>
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export default RecordCardList;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An EMBEDDED PageShell must not touch the topbar.
|
|
3
|
+
*
|
|
4
|
+
* ── THE DEFECT ────────────────────────────────────────────────────────────
|
|
5
|
+
*
|
|
6
|
+
* `embedded` means "the host page already owns the topbar — render only my
|
|
7
|
+
* body". But `usePageMeta` was called UNCONDITIONALLY, above the `embedded`
|
|
8
|
+
* check, and the page-meta store is a STACK: "nested pages can push their own
|
|
9
|
+
* meta; on unmount the outer meta is restored".
|
|
10
|
+
*
|
|
11
|
+
* So an embedded shell pushed its own `title`, `description`, `eyebrow`,
|
|
12
|
+
* `backTo` and `topbarActions` OVER its host's — the precise opposite of what
|
|
13
|
+
* embedding means. The host's title and back button silently became the
|
|
14
|
+
* child's for as long as the child was mounted, and the page-meta contract
|
|
15
|
+
* ("never render a back button inside the page body… move it into
|
|
16
|
+
* usePageMeta") made that the ONLY back affordance on screen, so it pointed at
|
|
17
|
+
* the wrong parent.
|
|
18
|
+
*
|
|
19
|
+
* A hook cannot be called conditionally, so the fix is structural: `PageShell`
|
|
20
|
+
* calls no hooks and only chooses between two components. These tests pin the
|
|
21
|
+
* OBSERVABLE property — whether meta is registered — not the file layout, so
|
|
22
|
+
* any future refactor that reintroduces the push fails them.
|
|
23
|
+
*
|
|
24
|
+
* Runs under the host-web vitest mirror (this package carries no vitest dep).
|
|
25
|
+
*/
|
|
26
|
+
import { FileQuestion } from 'lucide-react';
|
|
27
|
+
import { renderToStaticMarkup } from 'react-dom/server';
|
|
28
|
+
import { describe, expect, it, vi, beforeEach } from 'vitest';
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
const registered: unknown[] = [];
|
|
32
|
+
|
|
33
|
+
// The kernel reaches the page-meta store through the HostBridge singleton,
|
|
34
|
+
// never a host import — so the bridge is the seam to observe.
|
|
35
|
+
vi.mock('../../lib/biome-host/host-bridge', async () => {
|
|
36
|
+
const actual = await vi.importActual<Record<string, unknown>>(
|
|
37
|
+
'../../lib/biome-host/host-bridge',
|
|
38
|
+
);
|
|
39
|
+
return {
|
|
40
|
+
...actual,
|
|
41
|
+
useHostBridge: () => ({
|
|
42
|
+
pageMeta: {
|
|
43
|
+
usePageMeta: (meta: unknown) => {
|
|
44
|
+
registered.push(meta);
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
}),
|
|
48
|
+
};
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const { default: PageShell } = await import('./page-shell');
|
|
52
|
+
|
|
53
|
+
beforeEach(() => {
|
|
54
|
+
registered.length = 0;
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const META = {
|
|
58
|
+
icon: FileQuestion,
|
|
59
|
+
title: 'Child Title',
|
|
60
|
+
description: 'Child description',
|
|
61
|
+
eyebrow: 'Child eyebrow',
|
|
62
|
+
backTo: { to: '/child-parent', label: 'Child parent' },
|
|
63
|
+
} as const;
|
|
64
|
+
|
|
65
|
+
describe('PageShell page-meta ownership', () => {
|
|
66
|
+
it('registers NOTHING when embedded — the host owns the topbar', () => {
|
|
67
|
+
// THE BUG, directly. Before the fix this registered the child's meta,
|
|
68
|
+
// replacing the host's title and back button on a stack-based store.
|
|
69
|
+
renderToStaticMarkup(
|
|
70
|
+
<PageShell {...META} embedded>
|
|
71
|
+
<p>body</p>
|
|
72
|
+
</PageShell>,
|
|
73
|
+
);
|
|
74
|
+
expect(registered).toEqual([]);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('registers its meta when NOT embedded — a full page owns its topbar', () => {
|
|
78
|
+
// The other half: the fix must not silence the normal case.
|
|
79
|
+
renderToStaticMarkup(
|
|
80
|
+
<PageShell {...META}>
|
|
81
|
+
<p>body</p>
|
|
82
|
+
</PageShell>,
|
|
83
|
+
);
|
|
84
|
+
expect(registered).toHaveLength(1);
|
|
85
|
+
expect(registered[0]).toMatchObject({
|
|
86
|
+
title: 'Child Title',
|
|
87
|
+
description: 'Child description',
|
|
88
|
+
eyebrow: 'Child eyebrow',
|
|
89
|
+
backTo: { to: '/child-parent' },
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('still renders the embedded body — not registering is not "render nothing"', () => {
|
|
94
|
+
const html = renderToStaticMarkup(
|
|
95
|
+
<PageShell {...META} embedded>
|
|
96
|
+
<p>embedded-body-marker</p>
|
|
97
|
+
</PageShell>,
|
|
98
|
+
);
|
|
99
|
+
expect(html).toContain('embedded-body-marker');
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('renders the embedded sub-tab strip, which is the child’s own chrome', () => {
|
|
103
|
+
// Embedding suppresses the TOPBAR meta, not the child's in-body tabs.
|
|
104
|
+
const html = renderToStaticMarkup(
|
|
105
|
+
<PageShell
|
|
106
|
+
{...META}
|
|
107
|
+
embedded
|
|
108
|
+
tabs={[{ value: 'a', label: 'Alpha' }]}
|
|
109
|
+
activeTab="a"
|
|
110
|
+
>
|
|
111
|
+
<p>body</p>
|
|
112
|
+
</PageShell>,
|
|
113
|
+
);
|
|
114
|
+
expect(html).toContain('Alpha');
|
|
115
|
+
expect(registered).toEqual([]);
|
|
116
|
+
});
|
|
117
|
+
});
|