@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,8 +1,8 @@
|
|
|
1
1
|
import { FileQuestion, type LucideIcon } from 'lucide-react';
|
|
2
2
|
import { type ReactNode } from 'react';
|
|
3
3
|
|
|
4
|
-
import { useHostBridge, type PageBackTarget } from '../../lib/biome-host';
|
|
5
4
|
import EmptyState from './EmptyState';
|
|
5
|
+
import { useHostBridge, type PageBackTarget } from '../../lib/biome-host';
|
|
6
6
|
import { Tabs, TabsList, TabsTrigger } from '../primitives/tabs';
|
|
7
7
|
|
|
8
8
|
interface TabDef {
|
|
@@ -44,38 +44,52 @@ interface PageShellProps {
|
|
|
44
44
|
* page-meta store) and renders an optional tab strip + action row above the
|
|
45
45
|
* scrollable content canvas. Host-framework-agnostic: reaches the page-meta
|
|
46
46
|
* store through the kernel `HostBridge` singleton, never a host import.
|
|
47
|
+
*
|
|
48
|
+
* ── WHY THIS DISPATCHES INSTEAD OF BRANCHING ──────────────────────────────
|
|
49
|
+
*
|
|
50
|
+
* `embedded` means "the host page already owns the topbar — render only my
|
|
51
|
+
* body". But `usePageMeta` used to be called UNCONDITIONALLY, above the
|
|
52
|
+
* `embedded` check, and the page-meta store is a STACK ("nested pages can push
|
|
53
|
+
* their own meta; on unmount the outer meta is restored"). So an embedded shell
|
|
54
|
+
* pushed its own title, description, eyebrow, backTo and topbarActions OVER its
|
|
55
|
+
* host's — the precise opposite of what embedding means. The host's back button
|
|
56
|
+
* and title silently became the child's for as long as the child was mounted.
|
|
57
|
+
*
|
|
58
|
+
* A hook cannot be called conditionally, so the fix is structural: this
|
|
59
|
+
* component calls NO hooks and only chooses. The embedded variant never reaches
|
|
60
|
+
* the page-meta hook because it is a different component, not a later branch.
|
|
47
61
|
*/
|
|
48
|
-
export default function PageShell({
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
62
|
+
export default function PageShell(props: Readonly<PageShellProps>) {
|
|
63
|
+
return props.embedded ? (
|
|
64
|
+
<EmbeddedPageShell {...props} />
|
|
65
|
+
) : (
|
|
66
|
+
<FullPageShell {...props} />
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Embedded body. Registers NO page meta — the host page owns the topbar.
|
|
72
|
+
* Renders only the child content plus an optional sub-tab strip + actions.
|
|
73
|
+
*/
|
|
74
|
+
function EmbeddedPageShell({
|
|
52
75
|
children,
|
|
53
76
|
actions,
|
|
54
77
|
tabs,
|
|
55
78
|
activeTab,
|
|
56
79
|
onTabChange,
|
|
57
|
-
maxWidth,
|
|
58
|
-
embedded,
|
|
59
|
-
eyebrow,
|
|
60
|
-
backTo,
|
|
61
|
-
topbarActions,
|
|
62
80
|
}: Readonly<PageShellProps>) {
|
|
63
|
-
|
|
64
|
-
// PageShell call the page-meta hook directly; PageShell wraps the same
|
|
65
|
-
// hook so consumers don't need to register meta twice.
|
|
66
|
-
useHostBridge().pageMeta.usePageMeta({ title, description, eyebrow, backTo, topbarActions });
|
|
67
|
-
if (embedded) {
|
|
68
|
-
// Embedded mode: the host page already provides the title + tabs +
|
|
69
|
-
// context, so we render ONLY the child content + any sub-tab strip +
|
|
70
|
-
// actions inline. This eliminates the legacy duplicate icon-and-title
|
|
71
|
-
// row that previously stole vertical space.
|
|
81
|
+
{
|
|
72
82
|
return (
|
|
73
83
|
<div className="flex h-full min-h-0 flex-col bg-paper">
|
|
74
84
|
{(tabs && tabs.length > 0) || actions ? (
|
|
75
85
|
<div className="shrink-0 border-b border-rule bg-paper-elev/40">
|
|
76
86
|
<div className="flex items-center gap-3 px-4 py-2 lg:px-6">
|
|
77
87
|
{tabs && tabs.length > 0 && (
|
|
78
|
-
<Tabs
|
|
88
|
+
<Tabs
|
|
89
|
+
value={activeTab}
|
|
90
|
+
onValueChange={onTabChange}
|
|
91
|
+
className="flex-1 min-w-0"
|
|
92
|
+
>
|
|
79
93
|
<div className="overflow-x-auto">
|
|
80
94
|
<TabsList className="h-7 bg-transparent p-0 gap-0">
|
|
81
95
|
{tabs.map((tab) => (
|
|
@@ -86,7 +100,9 @@ export default function PageShell({
|
|
|
86
100
|
>
|
|
87
101
|
{tab.label}
|
|
88
102
|
{tab.count !== undefined && (
|
|
89
|
-
<span className="ml-1.5 text-caption tabular-nums opacity-70">
|
|
103
|
+
<span className="ml-1.5 text-caption tabular-nums opacity-70">
|
|
104
|
+
{tab.count}
|
|
105
|
+
</span>
|
|
90
106
|
)}
|
|
91
107
|
{tab.indicator && (
|
|
92
108
|
<span className="ml-1.5 inline-block h-1.5 w-1.5 rounded-full bg-warning" />
|
|
@@ -98,16 +114,50 @@ export default function PageShell({
|
|
|
98
114
|
</Tabs>
|
|
99
115
|
)}
|
|
100
116
|
{!tabs && <div className="flex-1" />}
|
|
101
|
-
{actions &&
|
|
117
|
+
{actions && (
|
|
118
|
+
<div className="flex items-center gap-2 shrink-0">
|
|
119
|
+
{actions}
|
|
120
|
+
</div>
|
|
121
|
+
)}
|
|
102
122
|
</div>
|
|
103
123
|
</div>
|
|
104
124
|
) : null}
|
|
105
125
|
<div className="flex-1 overflow-auto min-h-0">
|
|
106
|
-
<div className="flex h-full min-h-0 flex-col px-4 pb-4 pt-3 lg:px-6">
|
|
126
|
+
<div className="flex h-full min-h-0 flex-col px-4 pb-4 pt-3 lg:px-6">
|
|
127
|
+
{children}
|
|
128
|
+
</div>
|
|
107
129
|
</div>
|
|
108
130
|
</div>
|
|
109
131
|
);
|
|
110
132
|
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Full page. Owns the topbar meta for the route it renders.
|
|
137
|
+
*/
|
|
138
|
+
function FullPageShell({
|
|
139
|
+
title,
|
|
140
|
+
description,
|
|
141
|
+
children,
|
|
142
|
+
actions,
|
|
143
|
+
tabs,
|
|
144
|
+
activeTab,
|
|
145
|
+
onTabChange,
|
|
146
|
+
maxWidth,
|
|
147
|
+
eyebrow,
|
|
148
|
+
backTo,
|
|
149
|
+
topbarActions,
|
|
150
|
+
}: Readonly<PageShellProps>) {
|
|
151
|
+
// Single source of truth for the topbar title. Pages that bypass
|
|
152
|
+
// PageShell call the page-meta hook directly; PageShell wraps the same
|
|
153
|
+
// hook so consumers don't need to register meta twice.
|
|
154
|
+
useHostBridge().pageMeta.usePageMeta({
|
|
155
|
+
title,
|
|
156
|
+
description,
|
|
157
|
+
eyebrow,
|
|
158
|
+
backTo,
|
|
159
|
+
topbarActions,
|
|
160
|
+
});
|
|
111
161
|
|
|
112
162
|
return (
|
|
113
163
|
<div className={`flex h-full min-h-0 flex-col ${maxWidth ?? ''}`}>
|
|
@@ -116,7 +166,11 @@ export default function PageShell({
|
|
|
116
166
|
<div className="shrink-0 border-b border-border/50 px-4 sm:px-5 lg:px-8">
|
|
117
167
|
<div className="flex min-h-[34px] flex-col gap-2 py-1.5 sm:flex-row sm:items-center sm:gap-3 sm:py-0">
|
|
118
168
|
{tabs && tabs.length > 0 && (
|
|
119
|
-
<Tabs
|
|
169
|
+
<Tabs
|
|
170
|
+
value={activeTab}
|
|
171
|
+
onValueChange={onTabChange}
|
|
172
|
+
className="flex-1 min-w-0"
|
|
173
|
+
>
|
|
120
174
|
<div className="overflow-x-auto">
|
|
121
175
|
<TabsList className="h-7 bg-transparent p-0 gap-0">
|
|
122
176
|
{tabs.map((tab) => (
|
|
@@ -127,7 +181,9 @@ export default function PageShell({
|
|
|
127
181
|
>
|
|
128
182
|
{tab.label}
|
|
129
183
|
{tab.count !== undefined && (
|
|
130
|
-
<span className="ml-1.5 text-caption tabular-nums opacity-60">
|
|
184
|
+
<span className="ml-1.5 text-caption tabular-nums opacity-60">
|
|
185
|
+
{tab.count}
|
|
186
|
+
</span>
|
|
131
187
|
)}
|
|
132
188
|
{tab.indicator && (
|
|
133
189
|
<span className="ml-1.5 inline-block h-1.5 w-1.5 rounded-full bg-warning" />
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type { ReactNode } from 'react';
|
|
2
|
-
|
|
3
1
|
import { cn } from '../cn';
|
|
4
2
|
import { getStatusStyle } from '../design-tokens';
|
|
5
3
|
import { Badge } from '../primitives/badge';
|
|
6
4
|
|
|
5
|
+
import type { ReactNode } from 'react';
|
|
6
|
+
|
|
7
7
|
export type StatusBadgeSize = 'xs' | 'sm' | 'md';
|
|
8
8
|
|
|
9
9
|
export interface StatusBadgeProps {
|
|
@@ -68,8 +68,12 @@ export default function StatusBadge({
|
|
|
68
68
|
className,
|
|
69
69
|
)}
|
|
70
70
|
>
|
|
71
|
-
{dot &&
|
|
72
|
-
|
|
71
|
+
{dot && (
|
|
72
|
+
<span className={cn('shrink-0 rounded-full', sz.dot, style.dot)} />
|
|
73
|
+
)}
|
|
74
|
+
{children ?? (
|
|
75
|
+
<span className="capitalize">{status.replace(/_/g, ' ')}</span>
|
|
76
|
+
)}
|
|
73
77
|
</Badge>
|
|
74
78
|
);
|
|
75
79
|
}
|
package/src/ui/design-tokens.ts
CHANGED
|
@@ -142,8 +142,16 @@ export const STATUS_STYLES: Record<string, StatusStyle> = {
|
|
|
142
142
|
manual_review: WARNING_STYLE,
|
|
143
143
|
};
|
|
144
144
|
|
|
145
|
+
/**
|
|
146
|
+
* Style for an unrecognised status. Same value `STATUS_STYLES.pending` carries —
|
|
147
|
+
* referenced as the fragment rather than re-read out of the open `Record`, so
|
|
148
|
+
* the fallback is structurally guaranteed instead of being a second lookup that
|
|
149
|
+
* can itself miss (e.g. if the `pending` key were ever renamed).
|
|
150
|
+
*/
|
|
151
|
+
const DEFAULT_STATUS_STYLE: StatusStyle = INFO_STYLE;
|
|
152
|
+
|
|
145
153
|
export function getStatusStyle(status: string): StatusStyle {
|
|
146
|
-
return STATUS_STYLES[status] ??
|
|
154
|
+
return STATUS_STYLES[status] ?? DEFAULT_STATUS_STYLE;
|
|
147
155
|
}
|
|
148
156
|
|
|
149
157
|
// ── Priority Styles ────────────────────────────────────────────────
|
|
@@ -156,6 +164,17 @@ export interface PriorityStyle {
|
|
|
156
164
|
label: string;
|
|
157
165
|
}
|
|
158
166
|
|
|
167
|
+
// Lowest priority — also the style for an unrecognised priority. Named so the
|
|
168
|
+
// map entry and `getPriorityStyle`'s fallback are the same proven value rather
|
|
169
|
+
// than a second `Record` lookup that can itself miss.
|
|
170
|
+
const PRIORITY_LOW_STYLE: PriorityStyle = {
|
|
171
|
+
badge: 'bg-paper-elev text-ink-3 border-border',
|
|
172
|
+
text: 'text-ink-3',
|
|
173
|
+
bg: 'bg-muted/30',
|
|
174
|
+
border: 'border-border',
|
|
175
|
+
label: 'Low',
|
|
176
|
+
};
|
|
177
|
+
|
|
159
178
|
export const PRIORITY_STYLES: Record<string, PriorityStyle> = {
|
|
160
179
|
p0: {
|
|
161
180
|
badge: 'bg-destructive text-white border-destructive',
|
|
@@ -178,17 +197,11 @@ export const PRIORITY_STYLES: Record<string, PriorityStyle> = {
|
|
|
178
197
|
border: 'border-warning/30',
|
|
179
198
|
label: 'Medium',
|
|
180
199
|
},
|
|
181
|
-
p3:
|
|
182
|
-
badge: 'bg-paper-elev text-ink-3 border-border',
|
|
183
|
-
text: 'text-ink-3',
|
|
184
|
-
bg: 'bg-muted/30',
|
|
185
|
-
border: 'border-border',
|
|
186
|
-
label: 'Low',
|
|
187
|
-
},
|
|
200
|
+
p3: PRIORITY_LOW_STYLE,
|
|
188
201
|
};
|
|
189
202
|
|
|
190
203
|
export function getPriorityStyle(priority: string): PriorityStyle {
|
|
191
|
-
return PRIORITY_STYLES[priority] ??
|
|
204
|
+
return PRIORITY_STYLES[priority] ?? PRIORITY_LOW_STYLE;
|
|
192
205
|
}
|
|
193
206
|
|
|
194
207
|
// ── Severity Styles ────────────────────────────────────────────────
|
|
@@ -285,15 +298,28 @@ export const IMPORTANCE_STYLES: Record<number, { badge: string; label: string; s
|
|
|
285
298
|
|
|
286
299
|
// ── Risk Level Styles ──────────────────────────────────────────────
|
|
287
300
|
|
|
288
|
-
export
|
|
289
|
-
|
|
301
|
+
export interface RiskLevelStyle {
|
|
302
|
+
badge: string;
|
|
303
|
+
text: string;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Lowest risk — also the style for an unrecognised level. Named for the same
|
|
307
|
+
// reason as PRIORITY_LOW_STYLE: the fallback must be a proven value, not a
|
|
308
|
+
// second lookup into the open `Record`.
|
|
309
|
+
const RISK_LOW_STYLE: RiskLevelStyle = {
|
|
310
|
+
badge: 'bg-success/15 text-success dark:text-success border-success/30',
|
|
311
|
+
text: 'text-success dark:text-success',
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
export const RISK_LEVEL_STYLES: Record<string, RiskLevelStyle> = {
|
|
315
|
+
low: RISK_LOW_STYLE,
|
|
290
316
|
medium: { badge: 'bg-warning/15 text-warning dark:text-warning border-warning/30', text: 'text-warning dark:text-warning' },
|
|
291
317
|
high: { badge: 'bg-warning/15 text-warning dark:text-warning border-warning/30', text: 'text-warning dark:text-warning' },
|
|
292
318
|
critical: { badge: 'bg-destructive/15 text-destructive dark:text-destructive border-destructive/30', text: 'text-destructive dark:text-destructive' },
|
|
293
319
|
};
|
|
294
320
|
|
|
295
|
-
export function getRiskLevelStyle(level: string):
|
|
296
|
-
return RISK_LEVEL_STYLES[level] ??
|
|
321
|
+
export function getRiskLevelStyle(level: string): RiskLevelStyle {
|
|
322
|
+
return RISK_LEVEL_STYLES[level] ?? RISK_LOW_STYLE;
|
|
297
323
|
}
|
|
298
324
|
|
|
299
325
|
// ── Agent Source Styles ────────────────────────────────────────────
|
package/src/ui/index.ts
CHANGED
|
@@ -92,6 +92,11 @@ export {
|
|
|
92
92
|
type EmptyStateLinkComponent,
|
|
93
93
|
} from './chrome/EmptyState';
|
|
94
94
|
export { StateCard } from './chrome/StateCard';
|
|
95
|
+
export {
|
|
96
|
+
RecordCardList,
|
|
97
|
+
type RecordCardField,
|
|
98
|
+
type RecordCardListProps,
|
|
99
|
+
} from './chrome/RecordCardList';
|
|
95
100
|
export { default as PageHeader, PageToolbar } from './chrome/PageHeader';
|
|
96
101
|
export { default as LoadingState, type LoadingStateVariant } from './chrome/LoadingState';
|
|
97
102
|
export { default as AsyncBoundary } from './chrome/AsyncBoundary';
|
|
@@ -3,10 +3,10 @@ import { ChevronsUpDown, Loader2, Search } from 'lucide-react';
|
|
|
3
3
|
import { useEffect, useRef, useState, type ReactNode } from 'react';
|
|
4
4
|
|
|
5
5
|
import { cn } from '../cn';
|
|
6
|
-
import { useDebouncedValue } from '../hooks/use-debounced-value';
|
|
7
6
|
import { Button } from './button';
|
|
8
7
|
import { Input } from './input';
|
|
9
8
|
import { Popover, PopoverContent, PopoverTrigger } from './popover';
|
|
9
|
+
import { useDebouncedValue } from '../hooks/use-debounced-value';
|
|
10
10
|
|
|
11
11
|
export interface AsyncComboboxProps<T> {
|
|
12
12
|
/** Unique key prefix for query caching to avoid collisions between combobox instances. */
|
|
@@ -99,7 +99,9 @@ export function AsyncCombobox<T>({
|
|
|
99
99
|
};
|
|
100
100
|
|
|
101
101
|
const showTypeToSearchHint =
|
|
102
|
-
!isLoading &&
|
|
102
|
+
!isLoading &&
|
|
103
|
+
minSearchLength > 0 &&
|
|
104
|
+
debouncedSearch.length < minSearchLength;
|
|
103
105
|
const showEmptyMessage = !isLoading && searchEnabled && results.length === 0;
|
|
104
106
|
|
|
105
107
|
return (
|
|
@@ -156,7 +158,9 @@ export function AsyncCombobox<T>({
|
|
|
156
158
|
)}
|
|
157
159
|
|
|
158
160
|
{showEmptyMessage && (
|
|
159
|
-
<p className="text-body-1 text-ink-3 text-center py-4">
|
|
161
|
+
<p className="text-body-1 text-ink-3 text-center py-4">
|
|
162
|
+
{emptyMessage}
|
|
163
|
+
</p>
|
|
160
164
|
)}
|
|
161
165
|
|
|
162
166
|
{!isLoading &&
|
|
@@ -1,16 +1,25 @@
|
|
|
1
|
-
import
|
|
2
|
-
import * as React from "react";
|
|
1
|
+
import * as React from 'react';
|
|
3
2
|
|
|
4
3
|
import { cn } from '../cn';
|
|
5
4
|
import { badgeVariants } from './badge-variants';
|
|
6
5
|
|
|
7
|
-
|
|
6
|
+
import type { VariantProps } from 'class-variance-authority';
|
|
7
|
+
|
|
8
|
+
export interface BadgeProps
|
|
9
|
+
extends React.HTMLAttributes<HTMLSpanElement>,
|
|
10
|
+
VariantProps<typeof badgeVariants> {}
|
|
8
11
|
|
|
9
12
|
const Badge = React.forwardRef<HTMLSpanElement, BadgeProps>(
|
|
10
13
|
({ className, variant, ...props }, ref) => {
|
|
11
|
-
return
|
|
14
|
+
return (
|
|
15
|
+
<span
|
|
16
|
+
ref={ref}
|
|
17
|
+
className={cn(badgeVariants({ variant }), className)}
|
|
18
|
+
{...props}
|
|
19
|
+
/>
|
|
20
|
+
);
|
|
12
21
|
},
|
|
13
22
|
);
|
|
14
|
-
Badge.displayName =
|
|
23
|
+
Badge.displayName = 'Badge';
|
|
15
24
|
|
|
16
25
|
export { Badge };
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { Slot } from
|
|
2
|
-
import
|
|
3
|
-
import * as React from "react";
|
|
1
|
+
import { Slot } from '@radix-ui/react-slot';
|
|
2
|
+
import * as React from 'react';
|
|
4
3
|
|
|
5
4
|
import { cn } from '../cn';
|
|
6
5
|
import { buttonVariants } from './button-variants';
|
|
7
6
|
|
|
7
|
+
import type { VariantProps } from 'class-variance-authority';
|
|
8
|
+
|
|
8
9
|
export interface ButtonProps
|
|
9
10
|
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
10
11
|
VariantProps<typeof buttonVariants> {
|
|
@@ -13,10 +14,16 @@ export interface ButtonProps
|
|
|
13
14
|
|
|
14
15
|
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
15
16
|
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
16
|
-
const Comp = asChild ? Slot :
|
|
17
|
-
return
|
|
17
|
+
const Comp = asChild ? Slot : 'button';
|
|
18
|
+
return (
|
|
19
|
+
<Comp
|
|
20
|
+
className={cn(buttonVariants({ variant, size, className }))}
|
|
21
|
+
ref={ref}
|
|
22
|
+
{...props}
|
|
23
|
+
/>
|
|
24
|
+
);
|
|
18
25
|
},
|
|
19
26
|
);
|
|
20
|
-
Button.displayName =
|
|
27
|
+
Button.displayName = 'Button';
|
|
21
28
|
|
|
22
29
|
export { Button };
|
|
@@ -83,7 +83,9 @@ export function OverflowTabs({
|
|
|
83
83
|
// Measure container width and react to resizes.
|
|
84
84
|
useEffect(() => {
|
|
85
85
|
const el = containerRef.current;
|
|
86
|
-
if (!el)
|
|
86
|
+
if (!el) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
87
89
|
const ro = new ResizeObserver((entries) => {
|
|
88
90
|
const w = entries[0]?.contentRect.width ?? el.clientWidth;
|
|
89
91
|
setContainerWidth(w);
|
|
@@ -97,7 +99,9 @@ export function OverflowTabs({
|
|
|
97
99
|
// when the tab list itself changes (new tabs, label edits).
|
|
98
100
|
useLayoutEffect(() => {
|
|
99
101
|
const el = measureRef.current;
|
|
100
|
-
if (!el)
|
|
102
|
+
if (!el) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
101
105
|
const children = Array.from(el.children) as HTMLElement[];
|
|
102
106
|
setTabWidths(children.map((c) => c.getBoundingClientRect().width));
|
|
103
107
|
}, [tabs]);
|
|
@@ -118,36 +122,53 @@ export function OverflowTabs({
|
|
|
118
122
|
const vis: OverflowTab[] = [];
|
|
119
123
|
const over: OverflowTab[] = [];
|
|
120
124
|
let used = 0;
|
|
121
|
-
for (
|
|
122
|
-
const
|
|
125
|
+
for (const [i, tab] of tabs.entries()) {
|
|
126
|
+
const natural = tabWidths[i];
|
|
127
|
+
if (natural === undefined) {
|
|
128
|
+
// Unreachable: the length-parity guard above already returned. Kept as a
|
|
129
|
+
// real check because the alternative — treating a missing measurement as
|
|
130
|
+
// 0px — would lay tabs out against a budget they don't actually fit.
|
|
131
|
+
// Fall back to the same all-visible layout the parity guard renders.
|
|
132
|
+
return { visible: tabs, overflow: [] as readonly OverflowTab[] };
|
|
133
|
+
}
|
|
134
|
+
const w = natural + (vis.length > 0 ? gap : 0);
|
|
123
135
|
// A pinned tab is never collapsed — it stays visible whatever the budget
|
|
124
136
|
// says, so a panel's permanent first tab can't hide behind "More…".
|
|
125
|
-
if (
|
|
126
|
-
vis.push(
|
|
137
|
+
if (tab.pinned === true || used + w <= budget) {
|
|
138
|
+
vis.push(tab);
|
|
127
139
|
used += w;
|
|
128
140
|
} else {
|
|
129
|
-
over.push(
|
|
141
|
+
over.push(tab);
|
|
130
142
|
}
|
|
131
143
|
}
|
|
132
144
|
// Force the active tab into the visible set if it ended up in overflow.
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
145
|
+
// `find` (not `findIndex` + lookup) so the hit is typed as present.
|
|
146
|
+
const activeTab = tabs.find((t) => t.value === value);
|
|
147
|
+
if (activeTab !== undefined) {
|
|
136
148
|
const inVisible = vis.some((t) => t.value === activeTab.value);
|
|
137
149
|
if (!inVisible) {
|
|
138
150
|
// Swap the last visible for the active one to keep the budget OK.
|
|
139
151
|
const dropped = vis.pop();
|
|
140
|
-
if (dropped)
|
|
152
|
+
if (dropped) {
|
|
153
|
+
over.unshift(dropped);
|
|
154
|
+
}
|
|
141
155
|
vis.push(activeTab);
|
|
142
156
|
const removeIdx = over.findIndex((t) => t.value === activeTab.value);
|
|
143
|
-
if (removeIdx !== -1)
|
|
157
|
+
if (removeIdx !== -1) {
|
|
158
|
+
over.splice(removeIdx, 1);
|
|
159
|
+
}
|
|
144
160
|
}
|
|
145
161
|
}
|
|
146
162
|
return { visible: vis, overflow: over };
|
|
147
163
|
}, [tabs, tabWidths, containerWidth, gap, moreTriggerWidth, value]);
|
|
148
164
|
|
|
149
165
|
return (
|
|
150
|
-
<div
|
|
166
|
+
<div
|
|
167
|
+
className={cn(
|
|
168
|
+
'relative -mb-px flex items-end border-b border-rule',
|
|
169
|
+
className,
|
|
170
|
+
)}
|
|
171
|
+
>
|
|
151
172
|
{/* Off-screen measurement layer — renders ALL tabs so widths are real. */}
|
|
152
173
|
<div
|
|
153
174
|
ref={measureRef}
|
|
@@ -156,7 +177,12 @@ export function OverflowTabs({
|
|
|
156
177
|
style={{ visibility: 'hidden' }}
|
|
157
178
|
>
|
|
158
179
|
{tabs.map((t) => (
|
|
159
|
-
<TabPill
|
|
180
|
+
<TabPill
|
|
181
|
+
key={`measure-${t.value}`}
|
|
182
|
+
tab={t}
|
|
183
|
+
active={false}
|
|
184
|
+
onClick={() => undefined}
|
|
185
|
+
/>
|
|
160
186
|
))}
|
|
161
187
|
</div>
|
|
162
188
|
|
|
@@ -199,11 +225,15 @@ export function OverflowTabs({
|
|
|
199
225
|
<DropdownMenuItem
|
|
200
226
|
key={t.value}
|
|
201
227
|
onClick={() => onChange(t.value)}
|
|
202
|
-
className={cn(
|
|
228
|
+
className={cn(
|
|
229
|
+
t.value === value && 'bg-accent/60 text-foreground',
|
|
230
|
+
)}
|
|
203
231
|
>
|
|
204
232
|
<span className="flex-1 truncate">{t.label}</span>
|
|
205
233
|
{t.count !== undefined && (
|
|
206
|
-
<span className="ml-2 text-caption tabular-nums text-ink-4">
|
|
234
|
+
<span className="ml-2 text-caption tabular-nums text-ink-4">
|
|
235
|
+
{t.count}
|
|
236
|
+
</span>
|
|
207
237
|
)}
|
|
208
238
|
{t.indicator && (
|
|
209
239
|
<span
|
|
@@ -96,10 +96,14 @@ function ScaledBezel({ bezel, children }: ScaledBezelProps) {
|
|
|
96
96
|
|
|
97
97
|
useLayoutEffect(() => {
|
|
98
98
|
const el = containerRef.current;
|
|
99
|
-
if (!el)
|
|
99
|
+
if (!el) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
100
102
|
const compute = (): void => {
|
|
101
103
|
const { clientWidth, clientHeight } = el;
|
|
102
|
-
if (clientWidth === 0 || clientHeight === 0)
|
|
104
|
+
if (clientWidth === 0 || clientHeight === 0) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
103
107
|
// Leave a tiny margin so the shadow is not clipped.
|
|
104
108
|
const margin = 24;
|
|
105
109
|
const availW = Math.max(0, clientWidth - margin);
|
|
@@ -163,10 +167,14 @@ function ResponsiveViewport({
|
|
|
163
167
|
|
|
164
168
|
useLayoutEffect(() => {
|
|
165
169
|
const el = containerRef.current;
|
|
166
|
-
if (!el)
|
|
170
|
+
if (!el) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
167
173
|
const compute = (): void => {
|
|
168
174
|
const { clientWidth, clientHeight } = el;
|
|
169
|
-
if (clientWidth === 0 || clientHeight === 0)
|
|
175
|
+
if (clientWidth === 0 || clientHeight === 0) {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
170
178
|
const margin = 24;
|
|
171
179
|
const availW = Math.max(0, clientWidth - margin);
|
|
172
180
|
const availH = Math.max(0, clientHeight - margin);
|
|
@@ -30,7 +30,10 @@ export interface TagMultiSelectProps {
|
|
|
30
30
|
disabled?: boolean;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
const CHIP_CLASSES: Record<
|
|
33
|
+
const CHIP_CLASSES: Record<
|
|
34
|
+
NonNullable<TagMultiSelectProps['chipVariant']>,
|
|
35
|
+
string
|
|
36
|
+
> = {
|
|
34
37
|
default: 'bg-primary/10 text-primary border-primary/20',
|
|
35
38
|
secondary: 'bg-secondary text-secondary-foreground border-secondary',
|
|
36
39
|
success: 'bg-success/10 text-success border-success/20',
|
|
@@ -63,7 +66,9 @@ export function TagMultiSelect({
|
|
|
63
66
|
|
|
64
67
|
const filteredOptions = useMemo(() => {
|
|
65
68
|
const q = search.trim().toLowerCase();
|
|
66
|
-
if (!q)
|
|
69
|
+
if (!q) {
|
|
70
|
+
return options;
|
|
71
|
+
}
|
|
67
72
|
return options.filter(
|
|
68
73
|
(o) =>
|
|
69
74
|
o.label.toLowerCase().includes(q) ||
|
|
@@ -93,7 +98,9 @@ export function TagMultiSelect({
|
|
|
93
98
|
};
|
|
94
99
|
|
|
95
100
|
const addCustom = () => {
|
|
96
|
-
if (!canAddCustom)
|
|
101
|
+
if (!canAddCustom) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
97
104
|
onChange([...value, normalizedSearch]);
|
|
98
105
|
setSearch('');
|
|
99
106
|
inputRef.current?.focus();
|
|
@@ -104,8 +111,13 @@ export function TagMultiSelect({
|
|
|
104
111
|
e.preventDefault();
|
|
105
112
|
if (canAddCustom) {
|
|
106
113
|
addCustom();
|
|
107
|
-
} else
|
|
108
|
-
|
|
114
|
+
} else {
|
|
115
|
+
// Enter with exactly one remaining match selects it.
|
|
116
|
+
const onlyMatch =
|
|
117
|
+
filteredOptions.length === 1 ? filteredOptions[0] : undefined;
|
|
118
|
+
if (onlyMatch !== undefined) {
|
|
119
|
+
toggle(onlyMatch.value);
|
|
120
|
+
}
|
|
109
121
|
}
|
|
110
122
|
}
|
|
111
123
|
};
|
|
@@ -137,7 +149,9 @@ export function TagMultiSelect({
|
|
|
137
149
|
variant="outline"
|
|
138
150
|
className={cn('text-body-1 gap-1 pr-1', chipClass)}
|
|
139
151
|
>
|
|
140
|
-
<span className="truncate max-w-[160px]">
|
|
152
|
+
<span className="truncate max-w-[160px]">
|
|
153
|
+
{opt?.label ?? val}
|
|
154
|
+
</span>
|
|
141
155
|
<button
|
|
142
156
|
type="button"
|
|
143
157
|
onClick={(e) => remove(val, e)}
|
|
@@ -183,7 +197,9 @@ export function TagMultiSelect({
|
|
|
183
197
|
<ScrollArea className="max-h-56">
|
|
184
198
|
<div className="p-1">
|
|
185
199
|
{filteredOptions.length === 0 && !canAddCustom && (
|
|
186
|
-
<p className="text-body-1 text-ink-3 text-center py-3">
|
|
200
|
+
<p className="text-body-1 text-ink-3 text-center py-3">
|
|
201
|
+
{emptyMessage}
|
|
202
|
+
</p>
|
|
187
203
|
)}
|
|
188
204
|
|
|
189
205
|
{filteredOptions.map((opt) => {
|
|
@@ -209,7 +225,9 @@ export function TagMultiSelect({
|
|
|
209
225
|
{selected && <Check className="h-4 w-4" />}
|
|
210
226
|
</span>
|
|
211
227
|
<span className="flex-1 min-w-0">
|
|
212
|
-
<span className="font-medium block truncate">
|
|
228
|
+
<span className="font-medium block truncate">
|
|
229
|
+
{opt.label}
|
|
230
|
+
</span>
|
|
213
231
|
{opt.hint && (
|
|
214
232
|
<span className="text-body-1 text-ink-3 block truncate">
|
|
215
233
|
{opt.hint}
|
|
@@ -228,7 +246,10 @@ export function TagMultiSelect({
|
|
|
228
246
|
>
|
|
229
247
|
<Plus className="h-4 w-4 text-ink-3" />
|
|
230
248
|
<span>
|
|
231
|
-
Add
|
|
249
|
+
Add{' '}
|
|
250
|
+
<span className="font-mono font-medium">
|
|
251
|
+
{normalizedSearch}
|
|
252
|
+
</span>
|
|
232
253
|
</span>
|
|
233
254
|
</button>
|
|
234
255
|
)}
|