@vendoai/ui 0.4.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/LICENSE +202 -0
- package/README.md +7 -0
- package/dist/chrome/activity-ledger.d.ts +14 -0
- package/dist/chrome/activity-ledger.d.ts.map +1 -0
- package/dist/chrome/activity-ledger.js +40 -0
- package/dist/chrome/activity-ledger.js.map +1 -0
- package/dist/chrome/activity-panel.d.ts +7 -0
- package/dist/chrome/activity-panel.d.ts.map +1 -0
- package/dist/chrome/activity-panel.js +27 -0
- package/dist/chrome/activity-panel.js.map +1 -0
- package/dist/chrome/activity-semantics.d.ts +42 -0
- package/dist/chrome/activity-semantics.d.ts.map +1 -0
- package/dist/chrome/activity-semantics.js +119 -0
- package/dist/chrome/activity-semantics.js.map +1 -0
- package/dist/chrome/approval-card.d.ts +22 -0
- package/dist/chrome/approval-card.d.ts.map +1 -0
- package/dist/chrome/approval-card.js +95 -0
- package/dist/chrome/approval-card.js.map +1 -0
- package/dist/chrome/approval-sheet.d.ts +23 -0
- package/dist/chrome/approval-sheet.d.ts.map +1 -0
- package/dist/chrome/approval-sheet.js +72 -0
- package/dist/chrome/approval-sheet.js.map +1 -0
- package/dist/chrome/automations-panel.d.ts +3 -0
- package/dist/chrome/automations-panel.d.ts.map +1 -0
- package/dist/chrome/automations-panel.js +305 -0
- package/dist/chrome/automations-panel.js.map +1 -0
- package/dist/chrome/build-beat.d.ts +70 -0
- package/dist/chrome/build-beat.d.ts.map +1 -0
- package/dist/chrome/build-beat.js +101 -0
- package/dist/chrome/build-beat.js.map +1 -0
- package/dist/chrome/chrome-css.d.ts +7 -0
- package/dist/chrome/chrome-css.d.ts.map +1 -0
- package/dist/chrome/chrome-css.js +2042 -0
- package/dist/chrome/chrome-css.js.map +1 -0
- package/dist/chrome/chrome-root.d.ts +13 -0
- package/dist/chrome/chrome-root.d.ts.map +1 -0
- package/dist/chrome/chrome-root.js +39 -0
- package/dist/chrome/chrome-root.js.map +1 -0
- package/dist/chrome/clipboard.d.ts +4 -0
- package/dist/chrome/clipboard.d.ts.map +1 -0
- package/dist/chrome/clipboard.js +49 -0
- package/dist/chrome/clipboard.js.map +1 -0
- package/dist/chrome/connect-card.d.ts +19 -0
- package/dist/chrome/connect-card.d.ts.map +1 -0
- package/dist/chrome/connect-card.js +57 -0
- package/dist/chrome/connect-card.js.map +1 -0
- package/dist/chrome/connect-dock.d.ts +25 -0
- package/dist/chrome/connect-dock.d.ts.map +1 -0
- package/dist/chrome/connect-dock.js +192 -0
- package/dist/chrome/connect-dock.js.map +1 -0
- package/dist/chrome/connected-accounts-panel.d.ts +14 -0
- package/dist/chrome/connected-accounts-panel.d.ts.map +1 -0
- package/dist/chrome/connected-accounts-panel.js +157 -0
- package/dist/chrome/connected-accounts-panel.js.map +1 -0
- package/dist/chrome/dev-mode.d.ts +5 -0
- package/dist/chrome/dev-mode.d.ts.map +1 -0
- package/dist/chrome/dev-mode.js +7 -0
- package/dist/chrome/dev-mode.js.map +1 -0
- package/dist/chrome/discoverability.d.ts +33 -0
- package/dist/chrome/discoverability.d.ts.map +1 -0
- package/dist/chrome/discoverability.js +63 -0
- package/dist/chrome/discoverability.js.map +1 -0
- package/dist/chrome/embeds.d.ts +21 -0
- package/dist/chrome/embeds.d.ts.map +1 -0
- package/dist/chrome/embeds.js +181 -0
- package/dist/chrome/embeds.js.map +1 -0
- package/dist/chrome/fluid-thinking.d.ts +15 -0
- package/dist/chrome/fluid-thinking.d.ts.map +1 -0
- package/dist/chrome/fluid-thinking.js +37 -0
- package/dist/chrome/fluid-thinking.js.map +1 -0
- package/dist/chrome/humanize.d.ts +46 -0
- package/dist/chrome/humanize.d.ts.map +1 -0
- package/dist/chrome/humanize.js +104 -0
- package/dist/chrome/humanize.js.map +1 -0
- package/dist/chrome/index.d.ts +39 -0
- package/dist/chrome/index.d.ts.map +1 -0
- package/dist/chrome/index.js +38 -0
- package/dist/chrome/index.js.map +1 -0
- package/dist/chrome/markdown.d.ts +23 -0
- package/dist/chrome/markdown.d.ts.map +1 -0
- package/dist/chrome/markdown.js +149 -0
- package/dist/chrome/markdown.js.map +1 -0
- package/dist/chrome/morph-toast.d.ts +43 -0
- package/dist/chrome/morph-toast.d.ts.map +1 -0
- package/dist/chrome/morph-toast.js +89 -0
- package/dist/chrome/morph-toast.js.map +1 -0
- package/dist/chrome/no-policy-notice.d.ts +8 -0
- package/dist/chrome/no-policy-notice.d.ts.map +1 -0
- package/dist/chrome/no-policy-notice.js +18 -0
- package/dist/chrome/no-policy-notice.js.map +1 -0
- package/dist/chrome/onest-font.gen.d.ts +10 -0
- package/dist/chrome/onest-font.gen.d.ts.map +1 -0
- package/dist/chrome/onest-font.gen.js +10 -0
- package/dist/chrome/onest-font.gen.js.map +1 -0
- package/dist/chrome/overlay-registry.d.ts +66 -0
- package/dist/chrome/overlay-registry.d.ts.map +1 -0
- package/dist/chrome/overlay-registry.js +103 -0
- package/dist/chrome/overlay-registry.js.map +1 -0
- package/dist/chrome/palette-hotkey.d.ts +42 -0
- package/dist/chrome/palette-hotkey.d.ts.map +1 -0
- package/dist/chrome/palette-hotkey.js +91 -0
- package/dist/chrome/palette-hotkey.js.map +1 -0
- package/dist/chrome/policy-notice-body.d.ts +3 -0
- package/dist/chrome/policy-notice-body.d.ts.map +1 -0
- package/dist/chrome/policy-notice-body.js +6 -0
- package/dist/chrome/policy-notice-body.js.map +1 -0
- package/dist/chrome/takeover-portal.d.ts +16 -0
- package/dist/chrome/takeover-portal.d.ts.map +1 -0
- package/dist/chrome/takeover-portal.js +21 -0
- package/dist/chrome/takeover-portal.js.map +1 -0
- package/dist/chrome/thread/attachments.d.ts +24 -0
- package/dist/chrome/thread/attachments.d.ts.map +1 -0
- package/dist/chrome/thread/attachments.js +50 -0
- package/dist/chrome/thread/attachments.js.map +1 -0
- package/dist/chrome/thread/composer.d.ts +63 -0
- package/dist/chrome/thread/composer.d.ts.map +1 -0
- package/dist/chrome/thread/composer.js +259 -0
- package/dist/chrome/thread/composer.js.map +1 -0
- package/dist/chrome/thread/index.d.ts +41 -0
- package/dist/chrome/thread/index.d.ts.map +1 -0
- package/dist/chrome/thread/index.js +258 -0
- package/dist/chrome/thread/index.js.map +1 -0
- package/dist/chrome/thread/message-data.d.ts +35 -0
- package/dist/chrome/thread/message-data.d.ts.map +1 -0
- package/dist/chrome/thread/message-data.js +127 -0
- package/dist/chrome/thread/message-data.js.map +1 -0
- package/dist/chrome/thread/message-list.d.ts +30 -0
- package/dist/chrome/thread/message-list.d.ts.map +1 -0
- package/dist/chrome/thread/message-list.js +11 -0
- package/dist/chrome/thread/message-list.js.map +1 -0
- package/dist/chrome/thread/message.d.ts +18 -0
- package/dist/chrome/thread/message.d.ts.map +1 -0
- package/dist/chrome/thread/message.js +57 -0
- package/dist/chrome/thread/message.js.map +1 -0
- package/dist/chrome/thread/parts.d.ts +53 -0
- package/dist/chrome/thread/parts.d.ts.map +1 -0
- package/dist/chrome/thread/parts.js +184 -0
- package/dist/chrome/thread/parts.js.map +1 -0
- package/dist/chrome/thread/scrolling.d.ts +26 -0
- package/dist/chrome/thread/scrolling.d.ts.map +1 -0
- package/dist/chrome/thread/scrolling.js +214 -0
- package/dist/chrome/thread/scrolling.js.map +1 -0
- package/dist/chrome/truncate.d.ts +12 -0
- package/dist/chrome/truncate.d.ts.map +1 -0
- package/dist/chrome/truncate.js +18 -0
- package/dist/chrome/truncate.js.map +1 -0
- package/dist/chrome/vendo-activities.d.ts +24 -0
- package/dist/chrome/vendo-activities.d.ts.map +1 -0
- package/dist/chrome/vendo-activities.js +38 -0
- package/dist/chrome/vendo-activities.js.map +1 -0
- package/dist/chrome/vendo-overlay.d.ts +63 -0
- package/dist/chrome/vendo-overlay.d.ts.map +1 -0
- package/dist/chrome/vendo-overlay.js +256 -0
- package/dist/chrome/vendo-overlay.js.map +1 -0
- package/dist/chrome/vendo-page.d.ts +3 -0
- package/dist/chrome/vendo-page.d.ts.map +1 -0
- package/dist/chrome/vendo-page.js +162 -0
- package/dist/chrome/vendo-page.js.map +1 -0
- package/dist/chrome/vendo-palette.d.ts +29 -0
- package/dist/chrome/vendo-palette.d.ts.map +1 -0
- package/dist/chrome/vendo-palette.js +95 -0
- package/dist/chrome/vendo-palette.js.map +1 -0
- package/dist/chrome/vendo-slot.d.ts +79 -0
- package/dist/chrome/vendo-slot.d.ts.map +1 -0
- package/dist/chrome/vendo-slot.js +153 -0
- package/dist/chrome/vendo-slot.js.map +1 -0
- package/dist/chrome/vendo-toasts.d.ts +40 -0
- package/dist/chrome/vendo-toasts.d.ts.map +1 -0
- package/dist/chrome/vendo-toasts.js +135 -0
- package/dist/chrome/vendo-toasts.js.map +1 -0
- package/dist/chrome/vendo-trigger.d.ts +24 -0
- package/dist/chrome/vendo-trigger.d.ts.map +1 -0
- package/dist/chrome/vendo-trigger.js +30 -0
- package/dist/chrome/vendo-trigger.js.map +1 -0
- package/dist/chrome/waiting-queue.d.ts +8 -0
- package/dist/chrome/waiting-queue.d.ts.map +1 -0
- package/dist/chrome/waiting-queue.js +21 -0
- package/dist/chrome/waiting-queue.js.map +1 -0
- package/dist/client-impl.d.ts +4 -0
- package/dist/client-impl.d.ts.map +1 -0
- package/dist/client-impl.js +160 -0
- package/dist/client-impl.js.map +1 -0
- package/dist/client.d.ts +136 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +2 -0
- package/dist/client.js.map +1 -0
- package/dist/context.d.ts +97 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +71 -0
- package/dist/context.js.map +1 -0
- package/dist/eject-templates/activities/index.tsx +113 -0
- package/dist/eject-templates/templates.json +29 -0
- package/dist/eject-templates/thread/attachments.tsx +69 -0
- package/dist/eject-templates/thread/composer.tsx +409 -0
- package/dist/eject-templates/thread/index.tsx +422 -0
- package/dist/eject-templates/thread/message-data.ts +151 -0
- package/dist/eject-templates/thread/message-list.tsx +123 -0
- package/dist/eject-templates/thread/message.tsx +149 -0
- package/dist/eject-templates/thread/parts.tsx +293 -0
- package/dist/eject-templates/thread/scrolling.ts +223 -0
- package/dist/embeds.d.ts +28 -0
- package/dist/embeds.d.ts.map +1 -0
- package/dist/embeds.js +2 -0
- package/dist/embeds.js.map +1 -0
- package/dist/hooks/index.d.ts +18 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +19 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/scripted-transport.d.ts +44 -0
- package/dist/hooks/scripted-transport.d.ts.map +1 -0
- package/dist/hooks/scripted-transport.js +58 -0
- package/dist/hooks/scripted-transport.js.map +1 -0
- package/dist/hooks/use-activity.d.ts +17 -0
- package/dist/hooks/use-activity.d.ts.map +1 -0
- package/dist/hooks/use-activity.js +50 -0
- package/dist/hooks/use-activity.js.map +1 -0
- package/dist/hooks/use-app.d.ts +19 -0
- package/dist/hooks/use-app.d.ts.map +1 -0
- package/dist/hooks/use-app.js +66 -0
- package/dist/hooks/use-app.js.map +1 -0
- package/dist/hooks/use-approvals.d.ts +13 -0
- package/dist/hooks/use-approvals.d.ts.map +1 -0
- package/dist/hooks/use-approvals.js +14 -0
- package/dist/hooks/use-approvals.js.map +1 -0
- package/dist/hooks/use-apps.d.ts +17 -0
- package/dist/hooks/use-apps.d.ts.map +1 -0
- package/dist/hooks/use-apps.js +30 -0
- package/dist/hooks/use-apps.js.map +1 -0
- package/dist/hooks/use-automations.d.ts +25 -0
- package/dist/hooks/use-automations.d.ts.map +1 -0
- package/dist/hooks/use-automations.js +30 -0
- package/dist/hooks/use-automations.js.map +1 -0
- package/dist/hooks/use-connections.d.ts +12 -0
- package/dist/hooks/use-connections.d.ts.map +1 -0
- package/dist/hooks/use-connections.js +15 -0
- package/dist/hooks/use-connections.js.map +1 -0
- package/dist/hooks/use-connector-catalog.d.ts +8 -0
- package/dist/hooks/use-connector-catalog.d.ts.map +1 -0
- package/dist/hooks/use-connector-catalog.js +52 -0
- package/dist/hooks/use-connector-catalog.js.map +1 -0
- package/dist/hooks/use-grants.d.ts +13 -0
- package/dist/hooks/use-grants.d.ts.map +1 -0
- package/dist/hooks/use-grants.js +14 -0
- package/dist/hooks/use-grants.js.map +1 -0
- package/dist/hooks/use-mobile-takeover.d.ts +22 -0
- package/dist/hooks/use-mobile-takeover.d.ts.map +1 -0
- package/dist/hooks/use-mobile-takeover.js +66 -0
- package/dist/hooks/use-mobile-takeover.js.map +1 -0
- package/dist/hooks/use-resource.d.ts +21 -0
- package/dist/hooks/use-resource.d.ts.map +1 -0
- package/dist/hooks/use-resource.js +79 -0
- package/dist/hooks/use-resource.js.map +1 -0
- package/dist/hooks/use-slot-app.d.ts +19 -0
- package/dist/hooks/use-slot-app.d.ts.map +1 -0
- package/dist/hooks/use-slot-app.js +18 -0
- package/dist/hooks/use-slot-app.js.map +1 -0
- package/dist/hooks/use-threads.d.ts +16 -0
- package/dist/hooks/use-threads.d.ts.map +1 -0
- package/dist/hooks/use-threads.js +15 -0
- package/dist/hooks/use-threads.js.map +1 -0
- package/dist/hooks/use-vendo-overlay.d.ts +31 -0
- package/dist/hooks/use-vendo-overlay.d.ts.map +1 -0
- package/dist/hooks/use-vendo-overlay.js +23 -0
- package/dist/hooks/use-vendo-overlay.js.map +1 -0
- package/dist/hooks/use-vendo-status.d.ts +6 -0
- package/dist/hooks/use-vendo-status.d.ts.map +1 -0
- package/dist/hooks/use-vendo-status.js +28 -0
- package/dist/hooks/use-vendo-status.js.map +1 -0
- package/dist/hooks/use-vendo-thread.d.ts +39 -0
- package/dist/hooks/use-vendo-thread.d.ts.map +1 -0
- package/dist/hooks/use-vendo-thread.js +185 -0
- package/dist/hooks/use-vendo-thread.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/kit/charts/bar.d.ts +16 -0
- package/dist/kit/charts/bar.d.ts.map +1 -0
- package/dist/kit/charts/bar.js +21 -0
- package/dist/kit/charts/bar.js.map +1 -0
- package/dist/kit/charts/donut.d.ts +16 -0
- package/dist/kit/charts/donut.d.ts.map +1 -0
- package/dist/kit/charts/donut.js +17 -0
- package/dist/kit/charts/donut.js.map +1 -0
- package/dist/kit/charts/line.d.ts +19 -0
- package/dist/kit/charts/line.d.ts.map +1 -0
- package/dist/kit/charts/line.js +21 -0
- package/dist/kit/charts/line.js.map +1 -0
- package/dist/kit/charts/progress.d.ts +12 -0
- package/dist/kit/charts/progress.d.ts.map +1 -0
- package/dist/kit/charts/progress.js +25 -0
- package/dist/kit/charts/progress.js.map +1 -0
- package/dist/kit/charts/sanitize.d.ts +24 -0
- package/dist/kit/charts/sanitize.d.ts.map +1 -0
- package/dist/kit/charts/sanitize.js +50 -0
- package/dist/kit/charts/sanitize.js.map +1 -0
- package/dist/kit/charts/sparkline.d.ts +11 -0
- package/dist/kit/charts/sparkline.d.ts.map +1 -0
- package/dist/kit/charts/sparkline.js +17 -0
- package/dist/kit/charts/sparkline.js.map +1 -0
- package/dist/kit/data/badge.d.ts +13 -0
- package/dist/kit/data/badge.d.ts.map +1 -0
- package/dist/kit/data/badge.js +12 -0
- package/dist/kit/data/badge.js.map +1 -0
- package/dist/kit/data/card-list.d.ts +23 -0
- package/dist/kit/data/card-list.d.ts.map +1 -0
- package/dist/kit/data/card-list.js +43 -0
- package/dist/kit/data/card-list.js.map +1 -0
- package/dist/kit/data/data-table.d.ts +32 -0
- package/dist/kit/data/data-table.d.ts.map +1 -0
- package/dist/kit/data/data-table.js +153 -0
- package/dist/kit/data/data-table.js.map +1 -0
- package/dist/kit/data/stat.d.ts +15 -0
- package/dist/kit/data/stat.d.ts.map +1 -0
- package/dist/kit/data/stat.js +28 -0
- package/dist/kit/data/stat.js.map +1 -0
- package/dist/kit/feedback/accordion.d.ts +15 -0
- package/dist/kit/feedback/accordion.d.ts.map +1 -0
- package/dist/kit/feedback/accordion.js +33 -0
- package/dist/kit/feedback/accordion.js.map +1 -0
- package/dist/kit/feedback/callout.d.ts +13 -0
- package/dist/kit/feedback/callout.d.ts.map +1 -0
- package/dist/kit/feedback/callout.js +24 -0
- package/dist/kit/feedback/callout.js.map +1 -0
- package/dist/kit/feedback/tabs.d.ts +14 -0
- package/dist/kit/feedback/tabs.d.ts.map +1 -0
- package/dist/kit/feedback/tabs.js +37 -0
- package/dist/kit/feedback/tabs.js.map +1 -0
- package/dist/kit/format.d.ts +60 -0
- package/dist/kit/format.d.ts.map +1 -0
- package/dist/kit/format.js +123 -0
- package/dist/kit/format.js.map +1 -0
- package/dist/kit/forms/button.d.ts +17 -0
- package/dist/kit/forms/button.d.ts.map +1 -0
- package/dist/kit/forms/button.js +33 -0
- package/dist/kit/forms/button.js.map +1 -0
- package/dist/kit/forms/checkbox.d.ts +9 -0
- package/dist/kit/forms/checkbox.d.ts.map +1 -0
- package/dist/kit/forms/checkbox.js +9 -0
- package/dist/kit/forms/checkbox.js.map +1 -0
- package/dist/kit/forms/date-picker.d.ts +13 -0
- package/dist/kit/forms/date-picker.d.ts.map +1 -0
- package/dist/kit/forms/date-picker.js +9 -0
- package/dist/kit/forms/date-picker.js.map +1 -0
- package/dist/kit/forms/disclaimer.d.ts +8 -0
- package/dist/kit/forms/disclaimer.d.ts.map +1 -0
- package/dist/kit/forms/disclaimer.js +20 -0
- package/dist/kit/forms/disclaimer.js.map +1 -0
- package/dist/kit/forms/field.d.ts +18 -0
- package/dist/kit/forms/field.d.ts.map +1 -0
- package/dist/kit/forms/field.js +19 -0
- package/dist/kit/forms/field.js.map +1 -0
- package/dist/kit/forms/form.d.ts +10 -0
- package/dist/kit/forms/form.d.ts.map +1 -0
- package/dist/kit/forms/form.js +9 -0
- package/dist/kit/forms/form.js.map +1 -0
- package/dist/kit/forms/input.d.ts +14 -0
- package/dist/kit/forms/input.d.ts.map +1 -0
- package/dist/kit/forms/input.js +9 -0
- package/dist/kit/forms/input.js.map +1 -0
- package/dist/kit/forms/select.d.ts +21 -0
- package/dist/kit/forms/select.d.ts.map +1 -0
- package/dist/kit/forms/select.js +35 -0
- package/dist/kit/forms/select.js.map +1 -0
- package/dist/kit/forms/textarea.d.ts +12 -0
- package/dist/kit/forms/textarea.d.ts.map +1 -0
- package/dist/kit/forms/textarea.js +9 -0
- package/dist/kit/forms/textarea.js.map +1 -0
- package/dist/kit/index.d.ts +42 -0
- package/dist/kit/index.d.ts.map +1 -0
- package/dist/kit/index.js +45 -0
- package/dist/kit/index.js.map +1 -0
- package/dist/kit/kit-prompt.d.ts +7 -0
- package/dist/kit/kit-prompt.d.ts.map +1 -0
- package/dist/kit/kit-prompt.js +7 -0
- package/dist/kit/kit-prompt.js.map +1 -0
- package/dist/kit/layout.d.ts +29 -0
- package/dist/kit/layout.d.ts.map +1 -0
- package/dist/kit/layout.js +64 -0
- package/dist/kit/layout.js.map +1 -0
- package/dist/kit/registry.d.ts +11 -0
- package/dist/kit/registry.d.ts.map +1 -0
- package/dist/kit/registry.js +34 -0
- package/dist/kit/registry.js.map +1 -0
- package/dist/kit/schema.d.ts +9 -0
- package/dist/kit/schema.d.ts.map +1 -0
- package/dist/kit/schema.js +9 -0
- package/dist/kit/schema.js.map +1 -0
- package/dist/kit/tokens.d.ts +33 -0
- package/dist/kit/tokens.d.ts.map +1 -0
- package/dist/kit/tokens.js +48 -0
- package/dist/kit/tokens.js.map +1 -0
- package/dist/kit/values.d.ts +55 -0
- package/dist/kit/values.d.ts.map +1 -0
- package/dist/kit/values.js +108 -0
- package/dist/kit/values.js.map +1 -0
- package/dist/theme.d.ts +25 -0
- package/dist/theme.d.ts.map +1 -0
- package/dist/theme.js +100 -0
- package/dist/theme.js.map +1 -0
- package/dist/tree/bindings.d.ts +4 -0
- package/dist/tree/bindings.d.ts.map +1 -0
- package/dist/tree/bindings.js +20 -0
- package/dist/tree/bindings.js.map +1 -0
- package/dist/tree/branded.d.ts +124 -0
- package/dist/tree/branded.d.ts.map +1 -0
- package/dist/tree/branded.js +319 -0
- package/dist/tree/branded.js.map +1 -0
- package/dist/tree/error-boundary.d.ts +22 -0
- package/dist/tree/error-boundary.d.ts.map +1 -0
- package/dist/tree/error-boundary.js +25 -0
- package/dist/tree/error-boundary.js.map +1 -0
- package/dist/tree/fluid-reveal.d.ts +22 -0
- package/dist/tree/fluid-reveal.d.ts.map +1 -0
- package/dist/tree/fluid-reveal.js +28 -0
- package/dist/tree/fluid-reveal.js.map +1 -0
- package/dist/tree/forming-skeleton.d.ts +14 -0
- package/dist/tree/forming-skeleton.d.ts.map +1 -0
- package/dist/tree/forming-skeleton.js +36 -0
- package/dist/tree/forming-skeleton.js.map +1 -0
- package/dist/tree/frames.d.ts +52 -0
- package/dist/tree/frames.d.ts.map +1 -0
- package/dist/tree/frames.js +143 -0
- package/dist/tree/frames.js.map +1 -0
- package/dist/tree/host-mount.d.ts +24 -0
- package/dist/tree/host-mount.d.ts.map +1 -0
- package/dist/tree/host-mount.js +0 -0
- package/dist/tree/host-mount.js.map +1 -0
- package/dist/tree/index.d.ts +9 -0
- package/dist/tree/index.d.ts.map +1 -0
- package/dist/tree/index.js +9 -0
- package/dist/tree/index.js.map +1 -0
- package/dist/tree/jail/JailedComponent.d.ts +41 -0
- package/dist/tree/jail/JailedComponent.d.ts.map +1 -0
- package/dist/tree/jail/JailedComponent.js +269 -0
- package/dist/tree/jail/JailedComponent.js.map +1 -0
- package/dist/tree/jail/runtime-bundle.gen.d.ts +3 -0
- package/dist/tree/jail/runtime-bundle.gen.d.ts.map +1 -0
- package/dist/tree/jail/runtime-bundle.gen.js +3 -0
- package/dist/tree/jail/runtime-bundle.gen.js.map +1 -0
- package/dist/tree/jail/runtime-entry.d.ts +18 -0
- package/dist/tree/jail/runtime-entry.d.ts.map +1 -0
- package/dist/tree/jail/runtime-entry.js +440 -0
- package/dist/tree/jail/runtime-entry.js.map +1 -0
- package/dist/tree/jail/viewport-css.d.ts +17 -0
- package/dist/tree/jail/viewport-css.d.ts.map +1 -0
- package/dist/tree/jail/viewport-css.js +32 -0
- package/dist/tree/jail/viewport-css.js.map +1 -0
- package/dist/tree/mcp-shim/entry.d.ts +2 -0
- package/dist/tree/mcp-shim/entry.d.ts.map +1 -0
- package/dist/tree/mcp-shim/entry.js +45 -0
- package/dist/tree/mcp-shim/entry.js.map +1 -0
- package/dist/tree/mcp-shim/http-open-card.d.ts +5 -0
- package/dist/tree/mcp-shim/http-open-card.d.ts.map +1 -0
- package/dist/tree/mcp-shim/http-open-card.js +61 -0
- package/dist/tree/mcp-shim/http-open-card.js.map +1 -0
- package/dist/tree/mcp-shim/shim-core.d.ts +58 -0
- package/dist/tree/mcp-shim/shim-core.d.ts.map +1 -0
- package/dist/tree/mcp-shim/shim-core.js +176 -0
- package/dist/tree/mcp-shim/shim-core.js.map +1 -0
- package/dist/tree/mcp-shim/theme.d.ts +8 -0
- package/dist/tree/mcp-shim/theme.d.ts.map +1 -0
- package/dist/tree/mcp-shim/theme.js +36 -0
- package/dist/tree/mcp-shim/theme.js.map +1 -0
- package/dist/tree/notice.d.ts +8 -0
- package/dist/tree/notice.d.ts.map +1 -0
- package/dist/tree/notice.js +30 -0
- package/dist/tree/notice.js.map +1 -0
- package/dist/tree/primitives.d.ts +30 -0
- package/dist/tree/primitives.d.ts.map +1 -0
- package/dist/tree/primitives.js +105 -0
- package/dist/tree/primitives.js.map +1 -0
- package/dist/tree/renderer-v2.d.ts +15 -0
- package/dist/tree/renderer-v2.d.ts.map +1 -0
- package/dist/tree/renderer-v2.js +71 -0
- package/dist/tree/renderer-v2.js.map +1 -0
- package/dist/tree/renderer.d.ts +77 -0
- package/dist/tree/renderer.d.ts.map +1 -0
- package/dist/tree/renderer.js +376 -0
- package/dist/tree/renderer.js.map +1 -0
- package/dist/voice/driver.d.ts +94 -0
- package/dist/voice/driver.d.ts.map +1 -0
- package/dist/voice/driver.js +2 -0
- package/dist/voice/driver.js.map +1 -0
- package/dist/voice/index.d.ts +7 -0
- package/dist/voice/index.d.ts.map +1 -0
- package/dist/voice/index.js +5 -0
- package/dist/voice/index.js.map +1 -0
- package/dist/voice/realtime-driver.d.ts +57 -0
- package/dist/voice/realtime-driver.d.ts.map +1 -0
- package/dist/voice/realtime-driver.js +592 -0
- package/dist/voice/realtime-driver.js.map +1 -0
- package/dist/voice/stage.d.ts +12 -0
- package/dist/voice/stage.d.ts.map +1 -0
- package/dist/voice/stage.js +259 -0
- package/dist/voice/stage.js.map +1 -0
- package/dist/voice/use-voice-approvals.d.ts +16 -0
- package/dist/voice/use-voice-approvals.d.ts.map +1 -0
- package/dist/voice/use-voice-approvals.js +77 -0
- package/dist/voice/use-voice-approvals.js.map +1 -0
- package/dist/voice/use-voice.d.ts +26 -0
- package/dist/voice/use-voice.d.ts.map +1 -0
- package/dist/voice/use-voice.js +201 -0
- package/dist/voice/use-voice.js.map +1 -0
- package/dist/voice/voice-act.d.ts +13 -0
- package/dist/voice/voice-act.d.ts.map +1 -0
- package/dist/voice/voice-act.js +301 -0
- package/dist/voice/voice-act.js.map +1 -0
- package/dist/voice/voice-blob.d.ts +17 -0
- package/dist/voice/voice-blob.d.ts.map +1 -0
- package/dist/voice/voice-blob.js +67 -0
- package/dist/voice/voice-blob.js.map +1 -0
- package/dist/voice/voice-consent.d.ts +17 -0
- package/dist/voice/voice-consent.d.ts.map +1 -0
- package/dist/voice/voice-consent.js +45 -0
- package/dist/voice/voice-consent.js.map +1 -0
- package/dist/voice/voice-drawer.d.ts +10 -0
- package/dist/voice/voice-drawer.d.ts.map +1 -0
- package/dist/voice/voice-drawer.js +22 -0
- package/dist/voice/voice-drawer.js.map +1 -0
- package/dist/wire-types.d.ts +238 -0
- package/dist/wire-types.d.ts.map +1 -0
- package/dist/wire-types.js +2 -0
- package/dist/wire-types.js.map +1 -0
- package/package.json +106 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ejected from @vendoai/ui v0.4.0 — yours to edit.
|
|
3
|
+
* Presentation only: data and wire logic still flow through @vendoai/ui
|
|
4
|
+
* imports, so protocol updates keep working while these pixels are yours.
|
|
5
|
+
*/
|
|
6
|
+
import { useId, useState } from "react";
|
|
7
|
+
import { useActivity } from "../hooks/use-activity.js";
|
|
8
|
+
import { useApprovals } from "../hooks/use-approvals.js";
|
|
9
|
+
import { useVendoTools } from "../context.js";
|
|
10
|
+
import { ActivityLedger } from "./activity-ledger.js";
|
|
11
|
+
import { ApprovalCard } from "./approval-card.js";
|
|
12
|
+
import { ChromeRoot } from "./chrome-root.js";
|
|
13
|
+
|
|
14
|
+
export interface VendoActivitiesProps {
|
|
15
|
+
/**
|
|
16
|
+
* Poll interval (ms) shared by the approvals queue and the activity feed, so
|
|
17
|
+
* an approval raised elsewhere (a thread turn, the MCP door, an automation)
|
|
18
|
+
* appears here on its own — no page remount. Default 5000; `0` disables
|
|
19
|
+
* polling (initial fetch only).
|
|
20
|
+
*/
|
|
21
|
+
pollMs?: number;
|
|
22
|
+
/** Cap on recent-activity rows shown (this piece is a feed, not the full
|
|
23
|
+
* paginated audit — that stays `ActivityPanel`). Default 8. */
|
|
24
|
+
maxItems?: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The shelf's drop-in feed of what the agent did + what it is waiting on
|
|
29
|
+
* (ui-usage-dx §2) — placeable in any host page. Pending approvals render as
|
|
30
|
+
* ONE card with a "1 of N" pager (ui-lane-panels pick B): deciding slides the
|
|
31
|
+
* next approval into place instead of stacking a wall of cards. Recent
|
|
32
|
+
* activity renders as the shared icon ledger below. When nothing has happened
|
|
33
|
+
* yet it shows a quiet one-line empty state rather than rendering nothing —
|
|
34
|
+
* hosts place this in their own pages, and an invisible component would read
|
|
35
|
+
* as broken.
|
|
36
|
+
*/
|
|
37
|
+
export function VendoActivities({ pollMs = 5000, maxItems = 8 }: VendoActivitiesProps = {}) {
|
|
38
|
+
const poll = pollMs > 0 ? { pollMs } : undefined;
|
|
39
|
+
const { pending, decide } = useApprovals(poll);
|
|
40
|
+
const { events, isLoading } = useActivity(poll);
|
|
41
|
+
const tools = useVendoTools();
|
|
42
|
+
const headingId = useId();
|
|
43
|
+
const recent = events.slice(0, Math.max(0, maxItems));
|
|
44
|
+
|
|
45
|
+
// The pager holds a POSITION, not an approval: deciding removes the current
|
|
46
|
+
// approval from `pending`, so the next one shifts into the same index and
|
|
47
|
+
// slides in (keyed by approval id). Clamp when the queue shrinks below it.
|
|
48
|
+
const [rawIndex, setRawIndex] = useState(0);
|
|
49
|
+
const index = Math.min(rawIndex, Math.max(0, pending.length - 1));
|
|
50
|
+
const current = pending[index];
|
|
51
|
+
const paged = pending.length > 1;
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<ChromeRoot>
|
|
55
|
+
<section aria-label="Vendo activity" style={{ display: "grid", gap: "14px" }}>
|
|
56
|
+
{current ? (
|
|
57
|
+
<section aria-labelledby={`${headingId}-approvals`} style={{ display: "grid", gap: "10px" }}>
|
|
58
|
+
<header>
|
|
59
|
+
<div className={paged ? "fl-approvals-pager" : undefined}>
|
|
60
|
+
<h2 id={`${headingId}-approvals`} className="fl-act-head-lbl" style={{ margin: 0, fontSize: "14px" }}>
|
|
61
|
+
Needs your approval
|
|
62
|
+
</h2>
|
|
63
|
+
{paged ? (
|
|
64
|
+
<>
|
|
65
|
+
<p className="fl-act-now" style={{ margin: 0, fontSize: "11.5px" }} aria-live="polite">
|
|
66
|
+
· {index + 1} of {pending.length}
|
|
67
|
+
</p>
|
|
68
|
+
<span className="fl-approvals-dots" aria-hidden="true">
|
|
69
|
+
{pending.map((approval, i) => (
|
|
70
|
+
<span key={approval.id} className={`fl-approvals-dot${i === index ? " fl-approvals-dot--on" : ""}`} />
|
|
71
|
+
))}
|
|
72
|
+
</span>
|
|
73
|
+
</>
|
|
74
|
+
) : null}
|
|
75
|
+
</div>
|
|
76
|
+
<p className="fl-act-now" style={{ margin: "3px 0 0", fontSize: "12.5px" }}>
|
|
77
|
+
An agent asked to act on your account. Review the exact request before it runs.
|
|
78
|
+
</p>
|
|
79
|
+
</header>
|
|
80
|
+
<div className="fl-approvals-stack">
|
|
81
|
+
<div key={current.id} className="fl-approvals-slide">
|
|
82
|
+
<ApprovalCard
|
|
83
|
+
approval={current}
|
|
84
|
+
onDecide={async decision => {
|
|
85
|
+
await decide(current.id, decision);
|
|
86
|
+
setRawIndex(value => Math.min(value, Math.max(0, pending.length - 2)));
|
|
87
|
+
}}
|
|
88
|
+
/>
|
|
89
|
+
</div>
|
|
90
|
+
{index < pending.length - 1 ? <div className="fl-approvals-ghost" aria-hidden="true" /> : null}
|
|
91
|
+
</div>
|
|
92
|
+
</section>
|
|
93
|
+
) : null}
|
|
94
|
+
<section className="fl-act" aria-labelledby={`${headingId}-recent`}>
|
|
95
|
+
<header className="fl-act-head" style={{ cursor: "default" }}>
|
|
96
|
+
<span className="fl-act-ic fl-act-tick" aria-hidden="true">✓</span>
|
|
97
|
+
<h2 id={`${headingId}-recent`} className="fl-act-head-lbl" style={{ margin: 0 }}>Recent activity</h2>
|
|
98
|
+
</header>
|
|
99
|
+
{recent.length === 0 ? (
|
|
100
|
+
<p className="fl-act-row fl-act-now">
|
|
101
|
+
{isLoading ? "Loading activity…" : "No recent agent activity yet."}
|
|
102
|
+
</p>
|
|
103
|
+
) : (
|
|
104
|
+
<>
|
|
105
|
+
<p className="fl-act-cap" style={{ margin: 0 }}>Actions performed as your account</p>
|
|
106
|
+
<ActivityLedger events={recent} tools={tools} />
|
|
107
|
+
</>
|
|
108
|
+
)}
|
|
109
|
+
</section>
|
|
110
|
+
</section>
|
|
111
|
+
</ChromeRoot>
|
|
112
|
+
);
|
|
113
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "0.4.0",
|
|
3
|
+
"surfaces": {
|
|
4
|
+
"thread": {
|
|
5
|
+
"description": "The conversation thread: composer, message list, parts, scrolling.",
|
|
6
|
+
"component": "VendoThread",
|
|
7
|
+
"sourceBase": "chrome/thread",
|
|
8
|
+
"sourceDir": "chrome/thread",
|
|
9
|
+
"files": [
|
|
10
|
+
"attachments.tsx",
|
|
11
|
+
"composer.tsx",
|
|
12
|
+
"index.tsx",
|
|
13
|
+
"message-data.ts",
|
|
14
|
+
"message-list.tsx",
|
|
15
|
+
"message.tsx",
|
|
16
|
+
"parts.tsx",
|
|
17
|
+
"scrolling.ts"
|
|
18
|
+
]
|
|
19
|
+
},
|
|
20
|
+
"activities": {
|
|
21
|
+
"description": "The placeable activity piece: approvals queue + recent-runs feed.",
|
|
22
|
+
"component": "VendoActivities",
|
|
23
|
+
"sourceBase": "chrome",
|
|
24
|
+
"files": [
|
|
25
|
+
"index.tsx"
|
|
26
|
+
]
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ejected from @vendoai/ui v0.4.0 — yours to edit.
|
|
3
|
+
* Presentation only: data and wire logic still flow through @vendoai/ui
|
|
4
|
+
* imports, so protocol updates keep working while these pixels are yours.
|
|
5
|
+
*/
|
|
6
|
+
import type { UIMessage } from "ai";
|
|
7
|
+
|
|
8
|
+
/** A picked File → an ai-SDK FileUIPart (data URL) so it can ride the turn.
|
|
9
|
+
Lane pick 2F — the optional `onProgress` (0..1) feeds the chip's read ring:
|
|
10
|
+
attachments are now read eagerly at attach time (progress visible, failures
|
|
11
|
+
surfaced per-chip with retry) instead of silently at send time. */
|
|
12
|
+
export function fileToPart(
|
|
13
|
+
file: File,
|
|
14
|
+
onProgress?: (fraction: number) => void,
|
|
15
|
+
): Promise<{ type: "file"; mediaType: string; filename: string; url: string }> {
|
|
16
|
+
return new Promise((resolve, reject) => {
|
|
17
|
+
const reader = new FileReader();
|
|
18
|
+
reader.onerror = () => reject(reader.error ?? new Error("file read failed"));
|
|
19
|
+
if (onProgress) {
|
|
20
|
+
reader.onprogress = event => {
|
|
21
|
+
if (event.lengthComputable && event.total > 0) onProgress(event.loaded / event.total);
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
reader.onload = () => {
|
|
25
|
+
onProgress?.(1);
|
|
26
|
+
resolve({
|
|
27
|
+
type: "file",
|
|
28
|
+
mediaType: file.type || "application/octet-stream",
|
|
29
|
+
filename: file.name,
|
|
30
|
+
url: String(reader.result),
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
reader.readAsDataURL(file);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** The `.fl-att-ext` badge text — the filename's extension, bounded so a long
|
|
38
|
+
or missing one still reads as a badge. */
|
|
39
|
+
export function fileExt(name: string | undefined): string {
|
|
40
|
+
const ext = name?.match(/\.([a-z0-9]+)$/i)?.[1];
|
|
41
|
+
return (ext ?? "file").slice(0, 4).toUpperCase();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function formatBytes(size: number): string {
|
|
45
|
+
if (size < 1024) return `${size} B`;
|
|
46
|
+
if (size < 1024 * 1024) return `${(size / 1024).toFixed(0)} KB`;
|
|
47
|
+
return `${(size / (1024 * 1024)).toFixed(1)} MB`;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export type FilePart = Extract<UIMessage["parts"][number], { type: "file" }>;
|
|
51
|
+
|
|
52
|
+
/** ENG-225 — a sent attachment in the transcript: images render as the designed
|
|
53
|
+
`.fl-msg-img` thumbnail, anything else as a `.fl-msg-file` download pill. */
|
|
54
|
+
export function SentAttachment({ part }: { part: FilePart }) {
|
|
55
|
+
const name = part.filename ?? "attachment";
|
|
56
|
+
if (part.mediaType?.startsWith("image/") === true) {
|
|
57
|
+
return (
|
|
58
|
+
<span className="fl-msg-img">
|
|
59
|
+
<img src={part.url} alt={name} />
|
|
60
|
+
</span>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
return (
|
|
64
|
+
<a className="fl-msg-file" href={part.url} download={name}>
|
|
65
|
+
<span className="fl-att-ext" aria-hidden="true">{fileExt(part.filename)}</span>
|
|
66
|
+
<span className="fl-att-name">{name}</span>
|
|
67
|
+
</a>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ejected from @vendoai/ui v0.4.0 — yours to edit.
|
|
3
|
+
* Presentation only: data and wire logic still flow through @vendoai/ui
|
|
4
|
+
* imports, so protocol updates keep working while these pixels are yours.
|
|
5
|
+
*/
|
|
6
|
+
import { useContext, useEffect, useRef, useState } from "react";
|
|
7
|
+
import type { KeyboardEvent as ReactKeyboardEvent } from "react";
|
|
8
|
+
import { ConnectDockButton, ConnectTray } from "../connect-dock.js";
|
|
9
|
+
import { PrefillScopeContext, registerPrefillConsumer } from "../overlay-registry.js";
|
|
10
|
+
import { fileExt, fileToPart, formatBytes } from "./attachments.js";
|
|
11
|
+
|
|
12
|
+
/** The message shape the composer commits — mirrors useVendoThread.sendMessage. */
|
|
13
|
+
type OutgoingMessage = { text: string; files?: Awaited<ReturnType<typeof fileToPart>>[] };
|
|
14
|
+
|
|
15
|
+
/** Lane pick 2F — one attachment's eager-read lifecycle (drives the chip ring). */
|
|
16
|
+
type AttachmentRead = {
|
|
17
|
+
status: "reading" | "ready" | "error";
|
|
18
|
+
/** 0..1 read progress; meaningful while `reading`. */
|
|
19
|
+
progress: number;
|
|
20
|
+
part?: Awaited<ReturnType<typeof fileToPart>>;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/** ENG-225 — drag-drop attach: only reacts to drags that actually carry files
|
|
24
|
+
(text selections dragged across the composer must not flash the drop zone).
|
|
25
|
+
Exported for the thread-level drop surface (lane pick 2E). */
|
|
26
|
+
export const dragHasFiles = (event: React.DragEvent) =>
|
|
27
|
+
Array.from(event.dataTransfer?.types ?? []).includes("Files");
|
|
28
|
+
|
|
29
|
+
/** All composer state and send/queue mechanics, lifted to the thread level so
|
|
30
|
+
the draft (and queued slot) survive the landing ↔ transcript flip. The
|
|
31
|
+
Composer component below is the matching presentation. */
|
|
32
|
+
export function useComposer({ busy, sendMessage }: {
|
|
33
|
+
busy: boolean;
|
|
34
|
+
sendMessage: (message: OutgoingMessage) => unknown;
|
|
35
|
+
}) {
|
|
36
|
+
const [draft, setDraft] = useState("");
|
|
37
|
+
const fileRef = useRef<HTMLInputElement>(null);
|
|
38
|
+
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
|
39
|
+
const [files, setFiles] = useState<File[]>([]);
|
|
40
|
+
// ENG-225 — drag-drop attach. A depth counter, not a boolean: dragging over
|
|
41
|
+
// the composer's children fires enter/leave pairs for every element crossed.
|
|
42
|
+
const [dragDepth, setDragDepth] = useState(0);
|
|
43
|
+
// ENG-225 — object-URL thumbnails for image attachments in the chip strip.
|
|
44
|
+
// Keyed by File identity; a URL is minted once per file and revoked only when
|
|
45
|
+
// that file leaves the set — never recreated for files still shown (which
|
|
46
|
+
// would briefly point a mounted <img> at a revoked URL, Devin review). The
|
|
47
|
+
// ref mirrors the state so the unmount cleanup revokes the final set.
|
|
48
|
+
const [attachmentPreviews, setAttachmentPreviews] = useState<Map<File, string>>(new Map());
|
|
49
|
+
const previewsRef = useRef(attachmentPreviews);
|
|
50
|
+
previewsRef.current = attachmentPreviews;
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
if (typeof URL.createObjectURL !== "function") return;
|
|
53
|
+
setAttachmentPreviews(prev => {
|
|
54
|
+
const next = new Map<File, string>();
|
|
55
|
+
for (const file of files) {
|
|
56
|
+
if (!file.type.startsWith("image/")) continue;
|
|
57
|
+
next.set(file, prev.get(file) ?? URL.createObjectURL(file));
|
|
58
|
+
}
|
|
59
|
+
// Revoke only URLs for files that are no longer attached.
|
|
60
|
+
for (const [file, url] of prev) {
|
|
61
|
+
if (!next.has(file)) URL.revokeObjectURL(url);
|
|
62
|
+
}
|
|
63
|
+
return next;
|
|
64
|
+
});
|
|
65
|
+
}, [files]);
|
|
66
|
+
// Final cleanup on unmount: revoke whatever is still live.
|
|
67
|
+
useEffect(() => () => {
|
|
68
|
+
for (const url of previewsRef.current.values()) URL.revokeObjectURL(url);
|
|
69
|
+
}, []);
|
|
70
|
+
// Lane pick 2F — attachments read EAGERLY at attach time. Each file's read
|
|
71
|
+
// progress (FileReader onprogress) drives the chip ring; a failed read marks
|
|
72
|
+
// that chip (inline retry) instead of surfacing only as a text line at send.
|
|
73
|
+
// The finished part is cached so send doesn't re-read. Keyed by File identity,
|
|
74
|
+
// mirroring the previews map; entries leave with their file.
|
|
75
|
+
const [attachmentReads, setAttachmentReads] = useState<Map<File, AttachmentRead>>(new Map());
|
|
76
|
+
const readsRef = useRef(attachmentReads);
|
|
77
|
+
readsRef.current = attachmentReads;
|
|
78
|
+
const startRead = (file: File) => {
|
|
79
|
+
setAttachmentReads(prev => {
|
|
80
|
+
const next = new Map(prev);
|
|
81
|
+
next.set(file, { status: "reading", progress: 0 });
|
|
82
|
+
return next;
|
|
83
|
+
});
|
|
84
|
+
fileToPart(file, fraction => {
|
|
85
|
+
setAttachmentReads(prev => {
|
|
86
|
+
const current = prev.get(file);
|
|
87
|
+
if (!current || current.status !== "reading") return prev;
|
|
88
|
+
const next = new Map(prev);
|
|
89
|
+
next.set(file, { ...current, progress: fraction });
|
|
90
|
+
return next;
|
|
91
|
+
});
|
|
92
|
+
}).then(
|
|
93
|
+
part => setAttachmentReads(prev => {
|
|
94
|
+
if (!prev.has(file)) return prev;
|
|
95
|
+
const next = new Map(prev);
|
|
96
|
+
next.set(file, { status: "ready", progress: 1, part });
|
|
97
|
+
return next;
|
|
98
|
+
}),
|
|
99
|
+
() => setAttachmentReads(prev => {
|
|
100
|
+
if (!prev.has(file)) return prev;
|
|
101
|
+
const next = new Map(prev);
|
|
102
|
+
next.set(file, { status: "error", progress: 0 });
|
|
103
|
+
return next;
|
|
104
|
+
}),
|
|
105
|
+
);
|
|
106
|
+
};
|
|
107
|
+
useEffect(() => {
|
|
108
|
+
setAttachmentReads(prev => {
|
|
109
|
+
const next = new Map<File, AttachmentRead>();
|
|
110
|
+
for (const file of files) {
|
|
111
|
+
const existing = prev.get(file);
|
|
112
|
+
if (existing) next.set(file, existing);
|
|
113
|
+
}
|
|
114
|
+
return next.size === prev.size && files.every(f => prev.has(f)) ? prev : next;
|
|
115
|
+
});
|
|
116
|
+
for (const file of files) {
|
|
117
|
+
if (!readsRef.current.has(file)) startRead(file);
|
|
118
|
+
}
|
|
119
|
+
// startRead closes over stable setters only; files is the real trigger.
|
|
120
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
121
|
+
}, [files]);
|
|
122
|
+
// ENG-225 — the connect dock's liquid tray, anchored over the composer.
|
|
123
|
+
const [dockOpen, setDockOpen] = useState(false);
|
|
124
|
+
const dockButtonRef = useRef<HTMLButtonElement>(null);
|
|
125
|
+
// ENG-215 — a message the user sent DURING a turn: it parks here (visible as a
|
|
126
|
+
// pill) and auto-sends the instant the turn finishes. A single slot — a second
|
|
127
|
+
// send while one is parked replaces it — because there is only ever one "next"
|
|
128
|
+
// turn. Stop stays the explicit interrupt; queueing never cancels the stream.
|
|
129
|
+
const [queued, setQueued] = useState<{ text: string; files: File[] } | null>(null);
|
|
130
|
+
const [attachError, setAttachError] = useState<string>();
|
|
131
|
+
|
|
132
|
+
// ENG-215 — commit a turn to the transport (attachment parts come from the
|
|
133
|
+
// eager-read cache when ready, else a fresh read). Used both by an immediate
|
|
134
|
+
// send and by the deferred flush of a queued message.
|
|
135
|
+
const dispatch = (text: string, pending: File[]) => {
|
|
136
|
+
void (async () => {
|
|
137
|
+
let parts: Awaited<ReturnType<typeof fileToPart>>[];
|
|
138
|
+
try {
|
|
139
|
+
parts = await Promise.all(pending.map(file => {
|
|
140
|
+
const cached = readsRef.current.get(file);
|
|
141
|
+
return cached?.status === "ready" && cached.part ? Promise.resolve(cached.part) : fileToPart(file);
|
|
142
|
+
}));
|
|
143
|
+
} catch (reason) {
|
|
144
|
+
// A file read failed — surface it and restore the message so it never
|
|
145
|
+
// vanishes silently.
|
|
146
|
+
setAttachError(reason instanceof Error ? reason.message : "Couldn't read an attachment.");
|
|
147
|
+
setDraft(current => current || text);
|
|
148
|
+
setFiles(current => (current.length > 0 ? current : pending));
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
setAttachError(undefined);
|
|
152
|
+
void sendMessage(parts.length > 0 ? { text, files: parts } : { text });
|
|
153
|
+
})();
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const send = (override?: string) => {
|
|
157
|
+
const text = (override ?? draft).trim();
|
|
158
|
+
const pending = files;
|
|
159
|
+
if (!text && pending.length === 0) return;
|
|
160
|
+
// The message leaves the input immediately (whether it sends now or parks).
|
|
161
|
+
setDraft("");
|
|
162
|
+
setFiles([]);
|
|
163
|
+
if (fileRef.current) fileRef.current.value = "";
|
|
164
|
+
if (busy) {
|
|
165
|
+
setQueued({ text, files: pending });
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
dispatch(text, pending);
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
// The enclosing overlay's prefill scope (null for embedded threads/pages):
|
|
172
|
+
// registry-delivered prompts are directed at one overlay's composer.
|
|
173
|
+
const prefillScope = useContext(PrefillScopeContext);
|
|
174
|
+
// The listeners below register once but must send with CURRENT composer
|
|
175
|
+
// state: a first-render `send` closure sees busy=false forever, so a remix
|
|
176
|
+
// fired mid-stream would dispatch concurrently instead of parking in the
|
|
177
|
+
// queued slot (the single-in-flight contract).
|
|
178
|
+
const sendRef = useRef(send);
|
|
179
|
+
sendRef.current = send;
|
|
180
|
+
// Remix bridge: a host affordance (slot remix, a trigger button, the legacy
|
|
181
|
+
// `vendo:prefill` event) opens this surface and hands it the request to
|
|
182
|
+
// type + send, so the whole build happens here — the one conversational
|
|
183
|
+
// place (08-ui §4). The registry consumer also drains a prompt parked while
|
|
184
|
+
// this composer was still mounting (overlay first open / fresh conversation).
|
|
185
|
+
useEffect(() => {
|
|
186
|
+
const prefill = (prompt: string, sendNow: boolean) => {
|
|
187
|
+
setDraft(prompt);
|
|
188
|
+
if (sendNow) queueMicrotask(() => sendRef.current(prompt));
|
|
189
|
+
};
|
|
190
|
+
const onPrefill = (event: Event) => {
|
|
191
|
+
const detail = (event as CustomEvent<{ prompt?: string; send?: boolean }>).detail;
|
|
192
|
+
if (typeof detail?.prompt !== "string") return;
|
|
193
|
+
prefill(detail.prompt, detail.send === true);
|
|
194
|
+
};
|
|
195
|
+
window.addEventListener("vendo:prefill", onPrefill);
|
|
196
|
+
const unregister = registerPrefillConsumer(parked => prefill(parked.prompt, parked.send), prefillScope);
|
|
197
|
+
return () => {
|
|
198
|
+
window.removeEventListener("vendo:prefill", onPrefill);
|
|
199
|
+
unregister();
|
|
200
|
+
};
|
|
201
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps -- sendRef tracks the latest send; scope is mount-stable
|
|
202
|
+
}, []);
|
|
203
|
+
|
|
204
|
+
// Flush the queued message the moment the active turn finishes. A ref-tracked
|
|
205
|
+
// busy edge keeps this from firing on unrelated re-renders.
|
|
206
|
+
const wasBusyRef = useRef(busy);
|
|
207
|
+
useEffect(() => {
|
|
208
|
+
if (wasBusyRef.current && !busy && queued) {
|
|
209
|
+
const pending = queued;
|
|
210
|
+
setQueued(null);
|
|
211
|
+
dispatch(pending.text, pending.files);
|
|
212
|
+
}
|
|
213
|
+
wasBusyRef.current = busy;
|
|
214
|
+
// dispatch is recreated each render but closes only over stable setters and
|
|
215
|
+
// thread.sendMessage; the busy edge + queued slot are the real triggers.
|
|
216
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
217
|
+
}, [busy, queued]);
|
|
218
|
+
|
|
219
|
+
// ENG-215 — autogrow: the textarea tracks its content height (CSS caps it at
|
|
220
|
+
// max-height and scrolls past that). Runs on every draft change, including the
|
|
221
|
+
// programmatic reset on send and the refill on edit.
|
|
222
|
+
useEffect(() => {
|
|
223
|
+
const node = textareaRef.current;
|
|
224
|
+
if (!node) return;
|
|
225
|
+
node.style.height = "auto";
|
|
226
|
+
node.style.height = `${node.scrollHeight}px`;
|
|
227
|
+
}, [draft]);
|
|
228
|
+
|
|
229
|
+
return {
|
|
230
|
+
draft, setDraft, files, setFiles, dragDepth, setDragDepth,
|
|
231
|
+
attachmentPreviews, attachmentReads, retryRead: startRead,
|
|
232
|
+
dockOpen, setDockOpen, dockButtonRef,
|
|
233
|
+
queued, setQueued, attachError, fileRef, textareaRef, send,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export type ComposerApi = ReturnType<typeof useComposer>;
|
|
238
|
+
|
|
239
|
+
export interface ComposerProps {
|
|
240
|
+
composer: ComposerApi;
|
|
241
|
+
busy: boolean;
|
|
242
|
+
/** The live transport status + error for the sr-only announcement span. */
|
|
243
|
+
status: string;
|
|
244
|
+
errorMessage?: string;
|
|
245
|
+
onStop: () => void;
|
|
246
|
+
onVoice?: (() => void) | undefined;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/** The message composer (08-ui §4): attachments, drag-drop, queueing, dock. */
|
|
250
|
+
export function Composer({ composer, busy, status, errorMessage, onStop, onVoice }: ComposerProps) {
|
|
251
|
+
const {
|
|
252
|
+
draft, setDraft, files, setFiles,
|
|
253
|
+
attachmentPreviews, attachmentReads, retryRead,
|
|
254
|
+
dockOpen, setDockOpen, dockButtonRef,
|
|
255
|
+
queued, setQueued, attachError, fileRef, textareaRef, send,
|
|
256
|
+
} = composer;
|
|
257
|
+
return (
|
|
258
|
+
<div className="fl-dock-anchor">
|
|
259
|
+
{dockOpen ? (
|
|
260
|
+
<ConnectTray
|
|
261
|
+
anchorRef={dockButtonRef}
|
|
262
|
+
onClose={() => {
|
|
263
|
+
setDockOpen(false);
|
|
264
|
+
queueMicrotask(() => dockButtonRef.current?.focus());
|
|
265
|
+
}}
|
|
266
|
+
/>
|
|
267
|
+
) : null}
|
|
268
|
+
{/* Lane pick 2E — drag-drop moved UP to the whole thread surface (see
|
|
269
|
+
VendoThread): the bar itself no longer owns enter/leave/drop. */}
|
|
270
|
+
<form
|
|
271
|
+
className="fl-composer"
|
|
272
|
+
aria-label="Message composer"
|
|
273
|
+
onSubmit={event => { event.preventDefault(); send(); }}
|
|
274
|
+
>
|
|
275
|
+
{attachError ? <div className="fl-att-error" role="alert">{attachError}</div> : null}
|
|
276
|
+
{queued ? (
|
|
277
|
+
<div className="fl-queued" role="status" aria-live="polite">
|
|
278
|
+
<span className="fl-queued-tag">Queued</span>
|
|
279
|
+
<span className="fl-queued-text">{queued.text || `${queued.files.length} attachment(s)`}</span>
|
|
280
|
+
<span className="fl-queued-hint">sends when the reply finishes</span>
|
|
281
|
+
{/* Lane pick 2B — Send now: stop the stream; the ENG-215 busy-edge
|
|
282
|
+
flush then dispatches this queued slot immediately. One code
|
|
283
|
+
path for both the polite wait and the deliberate interrupt. */}
|
|
284
|
+
<button type="button" className="fl-queued-now" onClick={onStop}>Send now</button>
|
|
285
|
+
<button type="button" className="fl-att-rm fl-queued-rm" aria-label="Cancel queued message" onClick={() => setQueued(null)}>×</button>
|
|
286
|
+
</div>
|
|
287
|
+
) : null}
|
|
288
|
+
{files.length > 0 ? (
|
|
289
|
+
<div className="fl-att-chips">
|
|
290
|
+
{files.map((file, i) => {
|
|
291
|
+
const preview = attachmentPreviews.get(file);
|
|
292
|
+
const read = attachmentReads.get(file);
|
|
293
|
+
// An errored image renders as the file-style error chip, so its
|
|
294
|
+
// remove button needs the file-chip placement too.
|
|
295
|
+
const asFileChip = preview === undefined || read?.status === "error";
|
|
296
|
+
const remove = (
|
|
297
|
+
<button type="button" className={`fl-att-rm${asFileChip ? " fl-att-rm-file" : ""}`} aria-label={`Remove ${file.name}`}
|
|
298
|
+
onClick={() => setFiles(current => current.filter((_, j) => j !== i))}>×</button>
|
|
299
|
+
);
|
|
300
|
+
// ENG-225 — images preview as the designed thumbnail chip; other
|
|
301
|
+
// files carry an extension badge plus name and size. An image whose
|
|
302
|
+
// READ failed falls through to the error file-chip below (retry in
|
|
303
|
+
// place) instead of silently posing as attachable — the object-URL
|
|
304
|
+
// thumbnail says nothing about whether FileReader could read it
|
|
305
|
+
// (AI-review catch).
|
|
306
|
+
if (preview !== undefined && read?.status !== "error") {
|
|
307
|
+
return (
|
|
308
|
+
<span className="fl-att-img" key={`${file.name}-${i}`}>
|
|
309
|
+
<img src={preview} alt={file.name} />
|
|
310
|
+
{remove}
|
|
311
|
+
</span>
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
// Lane pick 2F — the chip narrates its read: progress ring while
|
|
315
|
+
// reading, error + inline retry on failure, quiet size when ready.
|
|
316
|
+
const failed = read?.status === "error";
|
|
317
|
+
const reading = read?.status === "reading";
|
|
318
|
+
const ring = 2 * Math.PI * 7;
|
|
319
|
+
return (
|
|
320
|
+
<span className={`fl-att-file${failed ? " fl-att-file--error" : ""}`} key={`${file.name}-${i}`}>
|
|
321
|
+
{reading ? (
|
|
322
|
+
<span className="fl-att-ring" aria-hidden="true">
|
|
323
|
+
<svg width="18" height="18" viewBox="0 0 18 18">
|
|
324
|
+
<circle className="fl-att-ring-bg" cx="9" cy="9" r="7" />
|
|
325
|
+
<circle className="fl-att-ring-fg" cx="9" cy="9" r="7"
|
|
326
|
+
strokeDasharray={ring} strokeDashoffset={ring * (1 - (read?.progress ?? 0))} />
|
|
327
|
+
</svg>
|
|
328
|
+
</span>
|
|
329
|
+
) : (
|
|
330
|
+
<span className="fl-att-ext" aria-hidden="true">{fileExt(file.name)}</span>
|
|
331
|
+
)}
|
|
332
|
+
<span className="fl-att-meta">
|
|
333
|
+
<span className="fl-att-name">{file.name}</span>
|
|
334
|
+
{failed ? (
|
|
335
|
+
<small className="fl-att-fail" role="alert">
|
|
336
|
+
couldn’t read — <button type="button" className="fl-att-retry" onClick={() => retryRead(file)}>retry</button>
|
|
337
|
+
</small>
|
|
338
|
+
) : (
|
|
339
|
+
<small>{reading ? "reading…" : formatBytes(file.size)}</small>
|
|
340
|
+
)}
|
|
341
|
+
</span>
|
|
342
|
+
{remove}
|
|
343
|
+
</span>
|
|
344
|
+
);
|
|
345
|
+
})}
|
|
346
|
+
</div>
|
|
347
|
+
) : null}
|
|
348
|
+
<div className="fl-composer-row">
|
|
349
|
+
<input ref={fileRef} type="file" multiple hidden aria-hidden="true"
|
|
350
|
+
onChange={event => { if (event.target.files) setFiles(current => [...current, ...Array.from(event.target.files!)]); }} />
|
|
351
|
+
<ConnectDockButton ref={dockButtonRef} open={dockOpen} onToggle={() => setDockOpen(value => !value)} />
|
|
352
|
+
<button type="button" className="fl-icon-btn fl-attach" aria-label="Attach files" onClick={() => fileRef.current?.click()}>
|
|
353
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
|
354
|
+
<path d="m21.44 11.05-9.19 9.19a6 6 0 0 1-8.49-8.49l8.57-8.57A4 4 0 1 1 18 8.84l-8.59 8.57a2 2 0 0 1-2.83-2.83l8.49-8.48" />
|
|
355
|
+
</svg>
|
|
356
|
+
</button>
|
|
357
|
+
<label style={{ display: "contents" }}>
|
|
358
|
+
<span className="fl-sr-only">Message</span>
|
|
359
|
+
<textarea
|
|
360
|
+
ref={textareaRef}
|
|
361
|
+
aria-label="Message"
|
|
362
|
+
placeholder="Ask anything"
|
|
363
|
+
rows={1}
|
|
364
|
+
value={draft}
|
|
365
|
+
// ENG-215 — never disabled: typing (and queueing) stays live through
|
|
366
|
+
// the whole turn, and the composer never dumps focus to <body>.
|
|
367
|
+
onChange={event => setDraft(event.currentTarget.value)}
|
|
368
|
+
onKeyDown={(event: ReactKeyboardEvent<HTMLTextAreaElement>) => {
|
|
369
|
+
if (event.key === "Enter" && !event.shiftKey) { event.preventDefault(); send(); }
|
|
370
|
+
}}
|
|
371
|
+
/>
|
|
372
|
+
</label>
|
|
373
|
+
{onVoice ? (
|
|
374
|
+
<button type="button" className="fl-icon-btn" aria-label="Start voice" onClick={onVoice}>
|
|
375
|
+
<svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
|
376
|
+
<rect x="9" y="2" width="6" height="12" rx="3" /><path d="M5 10a7 7 0 0 0 14 0M12 19v3" />
|
|
377
|
+
</svg>
|
|
378
|
+
</button>
|
|
379
|
+
) : null}
|
|
380
|
+
{/* ENG-215 — Stop is the explicit interrupt (only mid-turn); Send is
|
|
381
|
+
always available and, during a turn, queues the message instead. */}
|
|
382
|
+
{busy ? (
|
|
383
|
+
<button className="fl-icon-btn fl-stop" type="button" aria-label="Stop" onClick={onStop}>
|
|
384
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><rect x="6" y="6" width="12" height="12" rx="2.5" /></svg>
|
|
385
|
+
<span className="fl-sr-only">Stop</span>
|
|
386
|
+
</button>
|
|
387
|
+
) : null}
|
|
388
|
+
<button className="fl-icon-btn fl-send" type="submit" aria-label="Send" disabled={!draft.trim() && files.length === 0}>
|
|
389
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
|
390
|
+
<path d="M12 19V5" /><path d="m5 12 7-7 7 7" />
|
|
391
|
+
</svg>
|
|
392
|
+
<span className="fl-sr-only">Send</span>
|
|
393
|
+
</button>
|
|
394
|
+
</div>
|
|
395
|
+
{/* Lane pick 2C — focus bloom: a one-line hint row that exists only
|
|
396
|
+
while the composer holds focus (pure CSS via :focus-within). Teaches
|
|
397
|
+
the hidden powers exactly when attention is on the bar. */}
|
|
398
|
+
<div className="fl-hintrow" aria-hidden="true">
|
|
399
|
+
<span><kbd className="fl-kbd">⇧↵</kbd> new line</span>
|
|
400
|
+
<span><kbd className="fl-kbd">⌘K</kbd> commands</span>
|
|
401
|
+
<span>drop files anywhere</span>
|
|
402
|
+
</div>
|
|
403
|
+
<span role="status" aria-live="polite" className="fl-sr-only">
|
|
404
|
+
{errorMessage !== undefined ? `error: ${errorMessage}` : status}
|
|
405
|
+
</span>
|
|
406
|
+
</form>
|
|
407
|
+
</div>
|
|
408
|
+
);
|
|
409
|
+
}
|