@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
|
@@ -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) => (
|
|
@@ -29,7 +29,9 @@ function BlobBody({
|
|
|
29
29
|
payload: VistaBlobPayload;
|
|
30
30
|
render: (url: string) => ReactNode;
|
|
31
31
|
}>): ReactElement {
|
|
32
|
-
if (payload.url !== undefined)
|
|
32
|
+
if (payload.url !== undefined) {
|
|
33
|
+
return <>{render(payload.url)}</>;
|
|
34
|
+
}
|
|
33
35
|
// `validateBlobPayload` guarantees exactly one of url/path.
|
|
34
36
|
return <VistaFileUrl path={payload.path as string} render={render} />;
|
|
35
37
|
}
|
|
@@ -51,7 +53,11 @@ export function ImageSurface(payload: VistaBlobPayload): ReactElement {
|
|
|
51
53
|
<img
|
|
52
54
|
src={url}
|
|
53
55
|
alt={payload.alt ?? 'Preview'}
|
|
54
|
-
style={{
|
|
56
|
+
style={{
|
|
57
|
+
maxWidth: '100%',
|
|
58
|
+
maxHeight: '100%',
|
|
59
|
+
objectFit: 'contain',
|
|
60
|
+
}}
|
|
55
61
|
/>
|
|
56
62
|
</div>
|
|
57
63
|
)}
|
|
@@ -113,7 +119,11 @@ export function MediaSurface(payload: VistaBlobPayload): ReactElement {
|
|
|
113
119
|
>
|
|
114
120
|
{isAudio ? (
|
|
115
121
|
// eslint-disable-next-line jsx-a11y/media-has-caption
|
|
116
|
-
<audio
|
|
122
|
+
<audio
|
|
123
|
+
src={url}
|
|
124
|
+
controls
|
|
125
|
+
style={{ width: '100%', maxWidth: 520 }}
|
|
126
|
+
/>
|
|
117
127
|
) : (
|
|
118
128
|
// eslint-disable-next-line jsx-a11y/media-has-caption
|
|
119
129
|
<video
|
|
@@ -66,7 +66,9 @@ function asRecord(value: unknown): UnknownRecord | null {
|
|
|
66
66
|
*/
|
|
67
67
|
function unwrapEnvelope(body: unknown): UnknownRecord | null {
|
|
68
68
|
const record = asRecord(body);
|
|
69
|
-
if (record === null)
|
|
69
|
+
if (record === null) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
70
72
|
const inner = asRecord(record.data);
|
|
71
73
|
return inner ?? record;
|
|
72
74
|
}
|
|
@@ -82,14 +84,20 @@ export function extractServiceMessage(body: unknown, status: number): string {
|
|
|
82
84
|
record === null
|
|
83
85
|
? undefined
|
|
84
86
|
: (record.message ?? asRecord(record.error)?.message ?? record.error);
|
|
85
|
-
if (typeof candidate === 'string' && candidate.length > 0)
|
|
87
|
+
if (typeof candidate === 'string' && candidate.length > 0) {
|
|
88
|
+
return candidate;
|
|
89
|
+
}
|
|
86
90
|
if (Array.isArray(candidate)) {
|
|
87
91
|
const joined = candidate
|
|
88
92
|
.filter((m): m is string => typeof m === 'string')
|
|
89
93
|
.join(' ');
|
|
90
|
-
if (joined.length > 0)
|
|
94
|
+
if (joined.length > 0) {
|
|
95
|
+
return joined;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (typeof body === 'string' && body.length > 0) {
|
|
99
|
+
return body;
|
|
91
100
|
}
|
|
92
|
-
if (typeof body === 'string' && body.length > 0) return body;
|
|
93
101
|
return `The service rejected the submission (HTTP ${status}).`;
|
|
94
102
|
}
|
|
95
103
|
|
|
@@ -112,11 +120,15 @@ async function readBody(response: Response): Promise<unknown> {
|
|
|
112
120
|
*/
|
|
113
121
|
function toOutcome(body: unknown, ref: string): VistaFormOutcome | null {
|
|
114
122
|
const record = unwrapEnvelope(body);
|
|
115
|
-
if (record === null)
|
|
123
|
+
if (record === null) {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
116
126
|
const submitted =
|
|
117
127
|
record.submitted === true ||
|
|
118
128
|
(typeof record.status === 'string' && record.status === 'submitted');
|
|
119
|
-
if (!submitted)
|
|
129
|
+
if (!submitted) {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
120
132
|
const connectionId =
|
|
121
133
|
typeof record.connectionId === 'string' && record.connectionId.length > 0
|
|
122
134
|
? record.connectionId
|
|
@@ -133,7 +145,9 @@ function authHeaders(
|
|
|
133
145
|
Authorization: `Bearer ${accessToken}`,
|
|
134
146
|
Accept: 'application/json',
|
|
135
147
|
};
|
|
136
|
-
if (json)
|
|
148
|
+
if (json) {
|
|
149
|
+
headers['Content-Type'] = 'application/json';
|
|
150
|
+
}
|
|
137
151
|
return headers;
|
|
138
152
|
}
|
|
139
153
|
|
|
@@ -37,8 +37,12 @@ export interface VistaFileGroup {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
function compareDirs(a: string, b: string): number {
|
|
40
|
-
if (a === '')
|
|
41
|
-
|
|
40
|
+
if (a === '') {
|
|
41
|
+
return -1;
|
|
42
|
+
}
|
|
43
|
+
if (b === '') {
|
|
44
|
+
return 1;
|
|
45
|
+
}
|
|
42
46
|
return a.localeCompare(b);
|
|
43
47
|
}
|
|
44
48
|
|
|
@@ -50,8 +54,11 @@ export function groupFilesByFolder(
|
|
|
50
54
|
const slash = file.path.lastIndexOf('/');
|
|
51
55
|
const dir = slash < 0 ? '' : file.path.slice(0, slash);
|
|
52
56
|
const bucket = groups.get(dir);
|
|
53
|
-
if (bucket)
|
|
54
|
-
|
|
57
|
+
if (bucket) {
|
|
58
|
+
bucket.push(file);
|
|
59
|
+
} else {
|
|
60
|
+
groups.set(dir, [file]);
|
|
61
|
+
}
|
|
55
62
|
}
|
|
56
63
|
return [...groups.keys()].sort(compareDirs).map((dir) => ({
|
|
57
64
|
dir,
|
|
@@ -60,7 +60,9 @@ import {
|
|
|
60
60
|
*/
|
|
61
61
|
function validateMediaPayload(raw: unknown): VistaBlobPayload | null {
|
|
62
62
|
const payload = validateBlobPayload(raw);
|
|
63
|
-
if (payload === null)
|
|
63
|
+
if (payload === null) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
64
66
|
return payload.contentType === undefined ? null : payload;
|
|
65
67
|
}
|
|
66
68
|
|
|
@@ -35,7 +35,9 @@ function TextBody({
|
|
|
35
35
|
payload: VistaTextPayload;
|
|
36
36
|
render: (content: string) => ReactNode;
|
|
37
37
|
}>): ReactElement {
|
|
38
|
-
if (payload.content !== undefined)
|
|
38
|
+
if (payload.content !== undefined) {
|
|
39
|
+
return <>{render(payload.content)}</>;
|
|
40
|
+
}
|
|
39
41
|
// `validateTextPayload` guarantees exactly one of content/path.
|
|
40
42
|
return <VistaFileText path={payload.path as string} render={render} />;
|
|
41
43
|
}
|
|
@@ -73,10 +75,13 @@ export function CodeSurface(payload: VistaTextPayload): ReactElement {
|
|
|
73
75
|
* parses and shown VERBATIM when it does not (a malformed JSON file is shown as
|
|
74
76
|
* it is, never swallowed).
|
|
75
77
|
*/
|
|
76
|
-
export function JsonSurface({
|
|
78
|
+
export function JsonSurface({
|
|
79
|
+
data,
|
|
80
|
+
}: Readonly<{ data: unknown }>): ReactElement {
|
|
77
81
|
let text: string;
|
|
78
82
|
try {
|
|
79
|
-
|
|
83
|
+
const serialized: unknown = JSON.stringify(data, null, 2);
|
|
84
|
+
text = typeof serialized === 'string' ? serialized : String(data);
|
|
80
85
|
} catch {
|
|
81
86
|
text = String(data);
|
|
82
87
|
}
|
|
@@ -89,10 +94,18 @@ export function JsonSurface({ data }: Readonly<{ data: unknown }>): ReactElement
|
|
|
89
94
|
|
|
90
95
|
/** Colour a unified-diff line by its leading marker. */
|
|
91
96
|
function diffLineColor(line: string): string | undefined {
|
|
92
|
-
if (line.startsWith('+++') || line.startsWith('---'))
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
if (line.startsWith('
|
|
97
|
+
if (line.startsWith('+++') || line.startsWith('---')) {
|
|
98
|
+
return 'hsl(var(--ink-3))';
|
|
99
|
+
}
|
|
100
|
+
if (line.startsWith('@@')) {
|
|
101
|
+
return 'hsl(var(--info))';
|
|
102
|
+
}
|
|
103
|
+
if (line.startsWith('+')) {
|
|
104
|
+
return 'hsl(var(--success))';
|
|
105
|
+
}
|
|
106
|
+
if (line.startsWith('-')) {
|
|
107
|
+
return 'hsl(var(--destructive, var(--danger)))';
|
|
108
|
+
}
|
|
96
109
|
return undefined;
|
|
97
110
|
}
|
|
98
111
|
|
|
@@ -105,7 +118,7 @@ export function DiffSurface(payload: VistaTextPayload): ReactElement {
|
|
|
105
118
|
{content.split('\n').map((line, index) => (
|
|
106
119
|
<div
|
|
107
120
|
// A diff has no stable per-line identity other than its position.
|
|
108
|
-
|
|
121
|
+
|
|
109
122
|
key={index}
|
|
110
123
|
style={{ color: diffLineColor(line) }}
|
|
111
124
|
>
|
|
@@ -128,14 +128,18 @@ export function useVistaTabs({
|
|
|
128
128
|
activeRef.current = activeTabId;
|
|
129
129
|
|
|
130
130
|
const activate = useCallback((tabId: string): void => {
|
|
131
|
-
if (!tabsRef.current.some((t) => t.tabId === tabId))
|
|
131
|
+
if (!tabsRef.current.some((t) => t.tabId === tabId)) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
132
134
|
setActiveTabId(tabId);
|
|
133
135
|
}, []);
|
|
134
136
|
|
|
135
137
|
const open = useCallback((surface: VistaTab): string => {
|
|
136
138
|
setOpenedTabs((prev) => {
|
|
137
139
|
const index = prev.findIndex((t) => t.tabId === surface.tabId);
|
|
138
|
-
if (index === -1)
|
|
140
|
+
if (index === -1) {
|
|
141
|
+
return [...prev, surface];
|
|
142
|
+
}
|
|
139
143
|
// Re-opening the same tabId REPLACES it (the `VistaSurface` contract).
|
|
140
144
|
const next = [...prev];
|
|
141
145
|
next[index] = surface;
|
|
@@ -153,7 +157,9 @@ export function useVistaTabs({
|
|
|
153
157
|
// overwritten on the next reconcile, so it is REFUSED rather than
|
|
154
158
|
// silently lost. Ownership is decided synchronously from the ref, not
|
|
155
159
|
// inside the state updater (which React may invoke twice).
|
|
156
|
-
if (!openedTabsRef.current.some((t) => t.tabId === tabId))
|
|
160
|
+
if (!openedTabsRef.current.some((t) => t.tabId === tabId)) {
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
157
163
|
setOpenedTabs((prev) =>
|
|
158
164
|
prev.map((tab) => (tab.tabId === tabId ? { ...tab, ...patch } : tab)),
|
|
159
165
|
);
|
|
@@ -164,7 +170,9 @@ export function useVistaTabs({
|
|
|
164
170
|
|
|
165
171
|
const close = useCallback((tabId: string): boolean => {
|
|
166
172
|
const tab = tabsRef.current.find((t) => t.tabId === tabId);
|
|
167
|
-
if (tab === undefined || tab.pinned === true)
|
|
173
|
+
if (tab === undefined || tab.pinned === true) {
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
168
176
|
setOpenedTabs((prev) => prev.filter((t) => t.tabId !== tabId));
|
|
169
177
|
setClosedHostTabIds((prev) =>
|
|
170
178
|
prev.includes(tabId) ? prev : [...prev, tabId],
|
|
@@ -13,10 +13,8 @@
|
|
|
13
13
|
* pane is the intended rendering, and with a provider the host's renderer is
|
|
14
14
|
* the intended rendering. Neither is an error path.
|
|
15
15
|
*/
|
|
16
|
-
import {
|
|
17
|
-
|
|
18
|
-
type ReactNode,
|
|
19
|
-
} from 'react';
|
|
16
|
+
import { type ReactElement, type ReactNode } from 'react';
|
|
17
|
+
|
|
20
18
|
import {
|
|
21
19
|
VistaCodeRendererContext,
|
|
22
20
|
type VistaCodeRenderer,
|
|
@@ -25,7 +23,10 @@ import {
|
|
|
25
23
|
export function VistaCodeRendererProvider({
|
|
26
24
|
renderer,
|
|
27
25
|
children,
|
|
28
|
-
}: Readonly<{
|
|
26
|
+
}: Readonly<{
|
|
27
|
+
renderer: VistaCodeRenderer;
|
|
28
|
+
children: ReactNode;
|
|
29
|
+
}>): ReactElement {
|
|
29
30
|
return (
|
|
30
31
|
<VistaCodeRendererContext.Provider value={renderer}>
|
|
31
32
|
{children}
|
|
@@ -12,12 +12,8 @@
|
|
|
12
12
|
* provider renders a visible error, never a blank pane and never a silent
|
|
13
13
|
* "content unavailable".
|
|
14
14
|
*/
|
|
15
|
-
import {
|
|
16
|
-
|
|
17
|
-
useState,
|
|
18
|
-
type ReactElement,
|
|
19
|
-
type ReactNode,
|
|
20
|
-
} from 'react';
|
|
15
|
+
import { useEffect, useState, type ReactElement, type ReactNode } from 'react';
|
|
16
|
+
|
|
21
17
|
import {
|
|
22
18
|
VistaFileSourceContext,
|
|
23
19
|
useVistaFileSource,
|
|
@@ -43,10 +39,7 @@ type LoadState =
|
|
|
43
39
|
|
|
44
40
|
const LOADING: LoadState = { status: 'loading' };
|
|
45
41
|
|
|
46
|
-
function useResolvedFile(
|
|
47
|
-
path: string,
|
|
48
|
-
mode: 'text' | 'url',
|
|
49
|
-
): LoadState {
|
|
42
|
+
function useResolvedFile(path: string, mode: 'text' | 'url'): LoadState {
|
|
50
43
|
const source = useVistaFileSource();
|
|
51
44
|
const revision = source?.revision;
|
|
52
45
|
const [state, setState] = useState<LoadState>(LOADING);
|
|
@@ -66,10 +59,14 @@ function useResolvedFile(
|
|
|
66
59
|
mode === 'text' ? source.readText(path) : source.resolveUrl(path);
|
|
67
60
|
promise.then(
|
|
68
61
|
(value) => {
|
|
69
|
-
if (!cancelled)
|
|
62
|
+
if (!cancelled) {
|
|
63
|
+
setState({ status: 'ready', value });
|
|
64
|
+
}
|
|
70
65
|
},
|
|
71
66
|
(error: unknown) => {
|
|
72
|
-
if (cancelled)
|
|
67
|
+
if (cancelled) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
73
70
|
setState({
|
|
74
71
|
status: 'error',
|
|
75
72
|
message: error instanceof Error ? error.message : String(error),
|
|
@@ -122,7 +119,9 @@ export function VistaFileText({
|
|
|
122
119
|
render: (content: string) => ReactNode;
|
|
123
120
|
}>): ReactElement {
|
|
124
121
|
const state = useResolvedFile(path, 'text');
|
|
125
|
-
if (state.status !== 'ready')
|
|
122
|
+
if (state.status !== 'ready') {
|
|
123
|
+
return <LoadNotice state={state} />;
|
|
124
|
+
}
|
|
126
125
|
return <>{render(state.value)}</>;
|
|
127
126
|
}
|
|
128
127
|
|
|
@@ -135,6 +134,8 @@ export function VistaFileUrl({
|
|
|
135
134
|
render: (url: string) => ReactNode;
|
|
136
135
|
}>): ReactElement {
|
|
137
136
|
const state = useResolvedFile(path, 'url');
|
|
138
|
-
if (state.status !== 'ready')
|
|
137
|
+
if (state.status !== 'ready') {
|
|
138
|
+
return <LoadNotice state={state} />;
|
|
139
|
+
}
|
|
139
140
|
return <>{render(state.value)}</>;
|
|
140
141
|
}
|
|
@@ -26,14 +26,9 @@
|
|
|
26
26
|
* provider renders a visible refusal and cannot submit. Fail-closed — a form
|
|
27
27
|
* that silently posts nowhere would be worse than one that says it cannot.
|
|
28
28
|
*/
|
|
29
|
-
import {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
} from 'react';
|
|
33
|
-
import {
|
|
34
|
-
VistaFormHostContext,
|
|
35
|
-
type VistaFormHost,
|
|
36
|
-
} from './vista-form-context';
|
|
29
|
+
import { type ReactElement, type ReactNode } from 'react';
|
|
30
|
+
|
|
31
|
+
import { VistaFormHostContext, type VistaFormHost } from './vista-form-context';
|
|
37
32
|
|
|
38
33
|
/**
|
|
39
34
|
* Everything a settled elicitation reports. Closed, scalar-only, and
|