@xemahq/ui-kernel 0.9.2 → 0.11.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/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/index.d.ts +0 -1
- package/dist/lib/biome-host/index.d.ts.map +1 -1
- package/dist/lib/biome-host/index.js +0 -1
- package/dist/lib/biome-host/index.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 +10 -5
- 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-builder.d.ts +10 -2
- package/dist/registry/lib/session-context-builder.d.ts.map +1 -1
- package/dist/registry/lib/session-context-builder.js +14 -7
- package/dist/registry/lib/session-context-builder.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.map +1 -1
- package/dist/ui/chrome/page-shell.js +9 -3
- 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 +3 -3
- 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/index.ts +0 -1
- package/src/lib/biome-host/realtime-hooks.ts +1 -0
- package/src/lib/biome-host/session-contributions.ts +22 -19
- 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-builder.ts +33 -17
- 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.tsx +32 -8
- 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
- package/dist/lib/biome-host/session-profiles.d.ts +0 -18
- package/dist/lib/biome-host/session-profiles.d.ts.map +0 -1
- package/dist/lib/biome-host/session-profiles.js +0 -38
- package/dist/lib/biome-host/session-profiles.js.map +0 -1
- package/src/lib/biome-host/session-profiles.ts +0 -105
|
@@ -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
|
)}
|
|
@@ -30,11 +30,15 @@ interface GenericSurfaceField {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
function stringify(value: unknown): string {
|
|
33
|
-
if (typeof value === 'string')
|
|
33
|
+
if (typeof value === 'string') {
|
|
34
|
+
return value;
|
|
35
|
+
}
|
|
34
36
|
if (typeof value === 'number' || typeof value === 'boolean') {
|
|
35
37
|
return String(value);
|
|
36
38
|
}
|
|
37
|
-
if (value === null || value === undefined)
|
|
39
|
+
if (value === null || value === undefined) {
|
|
40
|
+
return '—';
|
|
41
|
+
}
|
|
38
42
|
try {
|
|
39
43
|
return JSON.stringify(value);
|
|
40
44
|
} catch {
|
|
@@ -43,9 +47,7 @@ function stringify(value: unknown): string {
|
|
|
43
47
|
}
|
|
44
48
|
|
|
45
49
|
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
46
|
-
return (
|
|
47
|
-
typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
48
|
-
);
|
|
50
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
/**
|
package/src/vista/VistaPanel.tsx
CHANGED
|
@@ -114,10 +114,16 @@ export function VistaTabBar({
|
|
|
114
114
|
indicator?: boolean;
|
|
115
115
|
icon?: OverflowTab['icon'];
|
|
116
116
|
} = { value: tab.tabId, label: tab.title };
|
|
117
|
-
if (tab.pinned === true)
|
|
118
|
-
|
|
117
|
+
if (tab.pinned === true) {
|
|
118
|
+
entry.pinned = true;
|
|
119
|
+
}
|
|
120
|
+
if (indicatorTabIds?.includes(tab.tabId) === true) {
|
|
121
|
+
entry.indicator = true;
|
|
122
|
+
}
|
|
119
123
|
const icon = surfaceKindIcon(tab.kind);
|
|
120
|
-
if (icon !== undefined)
|
|
124
|
+
if (icon !== undefined) {
|
|
125
|
+
entry.icon = icon;
|
|
126
|
+
}
|
|
121
127
|
return entry;
|
|
122
128
|
});
|
|
123
129
|
return (
|
|
@@ -143,7 +149,9 @@ export function VistaSurfaceBody({
|
|
|
143
149
|
tab,
|
|
144
150
|
}: Readonly<{ tab: VistaTab }>): ReactElement {
|
|
145
151
|
const resolution = vistaSurfaceRegistry.resolve(tab);
|
|
146
|
-
if (resolution.matched)
|
|
152
|
+
if (resolution.matched) {
|
|
153
|
+
return resolution.rendered;
|
|
154
|
+
}
|
|
147
155
|
return (
|
|
148
156
|
<SurfaceFallbackCard
|
|
149
157
|
kind={resolution.kind}
|
|
@@ -34,7 +34,9 @@ const STYLE_CSS = ':root{--x:1}';
|
|
|
34
34
|
function parseIframe(markup: string): Element {
|
|
35
35
|
const doc = new DOMParser().parseFromString(markup, 'text/html');
|
|
36
36
|
const iframe = doc.querySelector('iframe');
|
|
37
|
-
if (!iframe)
|
|
37
|
+
if (!iframe) {
|
|
38
|
+
throw new Error('no iframe rendered');
|
|
39
|
+
}
|
|
38
40
|
return iframe;
|
|
39
41
|
}
|
|
40
42
|
|
|
@@ -153,7 +153,9 @@ export function CustomUiFrame<TIntent>({
|
|
|
153
153
|
// re-render, per the HostProps contract). Before ready, the frameReady
|
|
154
154
|
// handler above pushes the current props — so no update is lost.
|
|
155
155
|
useEffect(() => {
|
|
156
|
-
if (!readyRef.current)
|
|
156
|
+
if (!readyRef.current) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
157
159
|
iframeRef.current?.contentWindow?.postMessage(
|
|
158
160
|
hostPropsMessage(componentProps),
|
|
159
161
|
'*',
|
|
@@ -52,10 +52,14 @@ export function classifyFrameMessage<TIntent>(
|
|
|
52
52
|
): ClassifiedFrameMessage<TIntent> {
|
|
53
53
|
// Gate 1: source identity. Reject if we have no frame yet or the message did
|
|
54
54
|
// not come from THIS frame's window.
|
|
55
|
-
if (frameWindow === null || event.source !== frameWindow)
|
|
55
|
+
if (frameWindow === null || event.source !== frameWindow) {
|
|
56
|
+
return IGNORED;
|
|
57
|
+
}
|
|
56
58
|
|
|
57
59
|
const data = event.data;
|
|
58
|
-
if (data === null || typeof data !== 'object')
|
|
60
|
+
if (data === null || typeof data !== 'object') {
|
|
61
|
+
return IGNORED;
|
|
62
|
+
}
|
|
59
63
|
const envelope = data as {
|
|
60
64
|
protocol?: unknown;
|
|
61
65
|
type?: unknown;
|
|
@@ -64,7 +68,9 @@ export function classifyFrameMessage<TIntent>(
|
|
|
64
68
|
};
|
|
65
69
|
|
|
66
70
|
// Gate 2: protocol version.
|
|
67
|
-
if (envelope.protocol !== CUSTOM_UI_FRAME_PROTOCOL)
|
|
71
|
+
if (envelope.protocol !== CUSTOM_UI_FRAME_PROTOCOL) {
|
|
72
|
+
return IGNORED;
|
|
73
|
+
}
|
|
68
74
|
|
|
69
75
|
// Gate 3: closed message-type set.
|
|
70
76
|
switch (envelope.type) {
|
|
@@ -104,7 +104,9 @@ describe('createNavigateIntentSet', () => {
|
|
|
104
104
|
kind: CustomUiIntentKind.Navigate,
|
|
105
105
|
href: '/apps/x',
|
|
106
106
|
});
|
|
107
|
-
if (intent === null)
|
|
107
|
+
if (intent === null) {
|
|
108
|
+
throw new Error('expected a parsed navigate intent');
|
|
109
|
+
}
|
|
108
110
|
set.perform(intent);
|
|
109
111
|
expect(navigate).toHaveBeenCalledWith({
|
|
110
112
|
kind: CustomUiIntentKind.Navigate,
|
|
@@ -72,13 +72,19 @@ export interface CustomUiIntentHandlers {
|
|
|
72
72
|
*/
|
|
73
73
|
function isHostRelativePath(href: string): boolean {
|
|
74
74
|
// Must be rooted at '/'.
|
|
75
|
-
if (!href.startsWith('/'))
|
|
75
|
+
if (!href.startsWith('/')) {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
76
78
|
// A backslash is a protocol-relative/scheme trick after WHATWG normalisation
|
|
77
79
|
// ("/\\evil.com" -> //evil.com -> foreign origin); whitespace / control
|
|
78
80
|
// chars can also confuse the parser. Reject any before resolving.
|
|
79
|
-
if (href.includes('\\'))
|
|
81
|
+
if (href.includes('\\')) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
80
84
|
// eslint-disable-next-line no-control-regex
|
|
81
|
-
if (/[\u0000-\u0020\u007f]/.test(href))
|
|
85
|
+
if (/[\u0000-\u0020\u007f]/.test(href)) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
82
88
|
let resolved: URL;
|
|
83
89
|
try {
|
|
84
90
|
resolved = new URL(href, window.location.origin);
|
|
@@ -96,12 +102,18 @@ function isHostRelativePath(href: string): boolean {
|
|
|
96
102
|
* an out-of-policy value all return `null` — the caller rejects it.
|
|
97
103
|
*/
|
|
98
104
|
export function parseCustomUiIntent(raw: unknown): CustomUiIntent | null {
|
|
99
|
-
if (raw === null || typeof raw !== 'object')
|
|
105
|
+
if (raw === null || typeof raw !== 'object') {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
100
108
|
const candidate = raw as { kind?: unknown; href?: unknown };
|
|
101
109
|
switch (candidate.kind) {
|
|
102
110
|
case CustomUiIntentKind.Navigate:
|
|
103
|
-
if (typeof candidate.href !== 'string')
|
|
104
|
-
|
|
111
|
+
if (typeof candidate.href !== 'string') {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
if (!isHostRelativePath(candidate.href)) {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
105
117
|
return { kind: CustomUiIntentKind.Navigate, href: candidate.href };
|
|
106
118
|
default:
|
|
107
119
|
return null;
|
|
@@ -118,15 +130,7 @@ export function performCustomUiIntent(
|
|
|
118
130
|
intent: CustomUiIntent,
|
|
119
131
|
handlers: CustomUiIntentHandlers,
|
|
120
132
|
): void {
|
|
121
|
-
|
|
122
|
-
case CustomUiIntentKind.Navigate:
|
|
123
|
-
handlers[CustomUiIntentKind.Navigate](intent);
|
|
124
|
-
return;
|
|
125
|
-
default: {
|
|
126
|
-
const exhaustive: never = intent.kind;
|
|
127
|
-
throw new Error(`Unhandled custom-UI intent kind: ${String(exhaustive)}`);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
133
|
+
handlers[CustomUiIntentKind.Navigate](intent);
|
|
130
134
|
}
|
|
131
135
|
|
|
132
136
|
/** The built-in intent set: host-relative navigation and nothing else. */
|
|
@@ -55,16 +55,22 @@ export function __resetSurfaceCatalogueSyncForTests(): void {
|
|
|
55
55
|
export async function syncSurfaceCatalogue(
|
|
56
56
|
fetchCatalogue: SurfaceCatalogueFetcher,
|
|
57
57
|
): Promise<void> {
|
|
58
|
-
if (synced)
|
|
58
|
+
if (synced) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
59
61
|
synced = true;
|
|
60
62
|
try {
|
|
61
63
|
const entries = await fetchCatalogue();
|
|
62
64
|
for (const entry of entries) {
|
|
63
65
|
// Only biome-contributed kinds need registering here; first-party kinds
|
|
64
66
|
// are already registered by the static builtins.
|
|
65
|
-
if (entry.source !== SurfaceKindSource.Biome)
|
|
67
|
+
if (entry.source !== SurfaceKindSource.Biome) {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
66
70
|
// Never shadow a biome's own (or an already-registered) renderer.
|
|
67
|
-
if (vistaSurfaceRegistry.has(entry.kind))
|
|
71
|
+
if (vistaSurfaceRegistry.has(entry.kind)) {
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
68
74
|
vistaSurfaceRegistry.register(entry.kind, {
|
|
69
75
|
version: entry.version,
|
|
70
76
|
validate: (payload) =>
|
|
@@ -48,10 +48,7 @@ import { captureValuesToStringMap } from '../../ui/capture/submit-values';
|
|
|
48
48
|
import { useSchemaValidation } from '../../ui/capture/use-schema-validation';
|
|
49
49
|
import { cn } from '../../ui/cn';
|
|
50
50
|
import { Button } from '../../ui/primitives/button';
|
|
51
|
-
import {
|
|
52
|
-
useVistaFormHost,
|
|
53
|
-
type VistaFormOutcome,
|
|
54
|
-
} from '../vista-form-context';
|
|
51
|
+
import { useVistaFormHost, type VistaFormOutcome } from '../vista-form-context';
|
|
55
52
|
|
|
56
53
|
import type { CaptureValues } from '../../ui/capture/field-spec';
|
|
57
54
|
import type { VistaFormPayload } from '../vista-payloads';
|
|
@@ -117,9 +114,13 @@ export function FormSurface(payload: Readonly<VistaFormPayload>): ReactElement {
|
|
|
117
114
|
}, []);
|
|
118
115
|
|
|
119
116
|
const handleSubmit = useCallback((): void => {
|
|
120
|
-
if (host === null)
|
|
117
|
+
if (host === null) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
121
120
|
setShowFieldErrors(true);
|
|
122
|
-
if (!isValid)
|
|
121
|
+
if (!isValid) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
123
124
|
setServiceError(null);
|
|
124
125
|
setPhase(FormPhase.Working);
|
|
125
126
|
// The values object is a PARAMETER here and nowhere else. It is never
|
|
@@ -138,7 +139,9 @@ export function FormSurface(payload: Readonly<VistaFormPayload>): ReactElement {
|
|
|
138
139
|
}, [host, isValid, payload.fields, payload.ref, settle, failed, values]);
|
|
139
140
|
|
|
140
141
|
const handleCheckStatus = useCallback((): void => {
|
|
141
|
-
if (host === null)
|
|
142
|
+
if (host === null) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
142
145
|
setServiceError(null);
|
|
143
146
|
setPhase(FormPhase.Working);
|
|
144
147
|
void host
|
|
@@ -167,10 +170,24 @@ export function FormSurface(payload: Readonly<VistaFormPayload>): ReactElement {
|
|
|
167
170
|
// an event, not a timer — there is no polling loop and no timeout.
|
|
168
171
|
const isLaunch = payload.launch !== undefined;
|
|
169
172
|
useEffect(() => {
|
|
170
|
-
if (!isLaunch || phase === FormPhase.Settled || host === null)
|
|
173
|
+
if (!isLaunch || phase === FormPhase.Settled || host === null) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
171
176
|
const onFocus = (): void => handleCheckStatus();
|
|
172
|
-
|
|
173
|
-
|
|
177
|
+
const addEventListener: unknown = Reflect.get(globalThis, 'addEventListener');
|
|
178
|
+
if (typeof addEventListener !== 'function') {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
Reflect.apply(addEventListener, globalThis, ['focus', onFocus]);
|
|
182
|
+
return () => {
|
|
183
|
+
const removeEventListener: unknown = Reflect.get(
|
|
184
|
+
globalThis,
|
|
185
|
+
'removeEventListener',
|
|
186
|
+
);
|
|
187
|
+
if (typeof removeEventListener === 'function') {
|
|
188
|
+
Reflect.apply(removeEventListener, globalThis, ['focus', onFocus]);
|
|
189
|
+
}
|
|
190
|
+
};
|
|
174
191
|
// `handleCheckStatus` is stable per (host, payload) and re-subscribing on a
|
|
175
192
|
// phase change is intended: a settled surface stops listening.
|
|
176
193
|
}, [isLaunch, phase, host, handleCheckStatus]);
|
|
@@ -9,9 +9,10 @@
|
|
|
9
9
|
import type { VistaTablePayload } from '../vista-payloads';
|
|
10
10
|
import type { ReactElement } from 'react';
|
|
11
11
|
|
|
12
|
-
|
|
13
12
|
function cellText(value: unknown): string {
|
|
14
|
-
if (value === null || value === undefined)
|
|
13
|
+
if (value === null || value === undefined) {
|
|
14
|
+
return '';
|
|
15
|
+
}
|
|
15
16
|
if (typeof value === 'object') {
|
|
16
17
|
try {
|
|
17
18
|
return JSON.stringify(value);
|
|
@@ -62,7 +63,7 @@ export function TableSurface({
|
|
|
62
63
|
<tr
|
|
63
64
|
// Rows carry no identity in the generic contract; position is the
|
|
64
65
|
// only stable key available.
|
|
65
|
-
|
|
66
|
+
|
|
66
67
|
key={rowIndex}
|
|
67
68
|
>
|
|
68
69
|
{columns.map((column, columnIndex) => (
|