@schlessera/brain-ui-react 0.34.1 → 0.36.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/README.md +89 -4
- package/dist/components/activity/activity-page.d.ts +17 -5
- package/dist/components/activity/activity-page.d.ts.map +1 -1
- package/dist/components/activity/activity-page.js +144 -50
- package/dist/components/activity/activity-page.js.map +1 -1
- package/dist/components/activity/activity-run-detail.d.ts +16 -1
- package/dist/components/activity/activity-run-detail.d.ts.map +1 -1
- package/dist/components/activity/activity-run-detail.js +47 -4
- package/dist/components/activity/activity-run-detail.js.map +1 -1
- package/dist/components/activity/activity-run-list.d.ts +7 -1
- package/dist/components/activity/activity-run-list.d.ts.map +1 -1
- package/dist/components/activity/activity-run-list.js +16 -8
- package/dist/components/activity/activity-run-list.js.map +1 -1
- package/dist/components/activity/activity-views.d.ts +63 -0
- package/dist/components/activity/activity-views.d.ts.map +1 -0
- package/dist/components/activity/activity-views.js +52 -0
- package/dist/components/activity/activity-views.js.map +1 -0
- package/dist/components/activity/approval-card.d.ts +11 -0
- package/dist/components/activity/approval-card.d.ts.map +1 -0
- package/dist/components/activity/approval-card.js +46 -0
- package/dist/components/activity/approval-card.js.map +1 -0
- package/dist/components/activity/digest-card.d.ts +3 -0
- package/dist/components/activity/digest-card.d.ts.map +1 -1
- package/dist/components/activity/digest-card.js +20 -16
- package/dist/components/activity/digest-card.js.map +1 -1
- package/dist/components/activity/digest-summary.d.ts +19 -0
- package/dist/components/activity/digest-summary.d.ts.map +1 -0
- package/dist/components/activity/digest-summary.js +20 -0
- package/dist/components/activity/digest-summary.js.map +1 -0
- package/dist/components/activity/push-switch.d.ts +25 -0
- package/dist/components/activity/push-switch.d.ts.map +1 -0
- package/dist/components/activity/push-switch.js +14 -0
- package/dist/components/activity/push-switch.js.map +1 -0
- package/dist/components/activity/push-toggle.d.ts +7 -0
- package/dist/components/activity/push-toggle.d.ts.map +1 -1
- package/dist/components/activity/push-toggle.js +72 -47
- package/dist/components/activity/push-toggle.js.map +1 -1
- package/dist/components/activity/span-bits.d.ts.map +1 -1
- package/dist/components/activity/span-bits.js +9 -7
- package/dist/components/activity/span-bits.js.map +1 -1
- package/dist/components/chat/ask-user-card.d.ts +76 -3
- package/dist/components/chat/ask-user-card.d.ts.map +1 -1
- package/dist/components/chat/ask-user-card.js +208 -149
- package/dist/components/chat/ask-user-card.js.map +1 -1
- package/dist/components/chat/ask-user-typed.d.ts +42 -0
- package/dist/components/chat/ask-user-typed.d.ts.map +1 -0
- package/dist/components/chat/ask-user-typed.js +60 -0
- package/dist/components/chat/ask-user-typed.js.map +1 -0
- package/dist/components/chat/brain-markdown-links.d.ts +2 -1
- package/dist/components/chat/brain-markdown-links.d.ts.map +1 -1
- package/dist/components/chat/brain-markdown-links.js +3 -4
- package/dist/components/chat/brain-markdown-links.js.map +1 -1
- package/dist/components/chat/brain-markdown.d.ts.map +1 -1
- package/dist/components/chat/brain-markdown.js +4 -2
- package/dist/components/chat/brain-markdown.js.map +1 -1
- package/dist/components/chat/chat-page.d.ts.map +1 -1
- package/dist/components/chat/chat-page.js +24 -9
- package/dist/components/chat/chat-page.js.map +1 -1
- package/dist/components/chat/composer-view.d.ts +75 -0
- package/dist/components/chat/composer-view.d.ts.map +1 -0
- package/dist/components/chat/composer-view.js +31 -0
- package/dist/components/chat/composer-view.js.map +1 -0
- package/dist/components/chat/composer.d.ts +4 -0
- package/dist/components/chat/composer.d.ts.map +1 -1
- package/dist/components/chat/composer.js +76 -59
- package/dist/components/chat/composer.js.map +1 -1
- package/dist/components/chat/markdown-content.d.ts +16 -1
- package/dist/components/chat/markdown-content.d.ts.map +1 -1
- package/dist/components/chat/markdown-content.js +47 -3
- package/dist/components/chat/markdown-content.js.map +1 -1
- package/dist/components/chat/mermaid-block.d.ts +4 -0
- package/dist/components/chat/mermaid-block.d.ts.map +1 -1
- package/dist/components/chat/mermaid-block.js +13 -5
- package/dist/components/chat/mermaid-block.js.map +1 -1
- package/dist/components/chat/mermaid-share.d.ts +2 -1
- package/dist/components/chat/mermaid-share.d.ts.map +1 -1
- package/dist/components/chat/mermaid-share.js +7 -8
- package/dist/components/chat/mermaid-share.js.map +1 -1
- package/dist/components/chat/mermaid-viewer.d.ts.map +1 -1
- package/dist/components/chat/mermaid-viewer.js +3 -1
- package/dist/components/chat/mermaid-viewer.js.map +1 -1
- package/dist/components/chat/message-bubble.d.ts +8 -1
- package/dist/components/chat/message-bubble.d.ts.map +1 -1
- package/dist/components/chat/message-bubble.js +53 -24
- package/dist/components/chat/message-bubble.js.map +1 -1
- package/dist/components/chat/message-share.d.ts +2 -1
- package/dist/components/chat/message-share.d.ts.map +1 -1
- package/dist/components/chat/message-share.js +6 -7
- package/dist/components/chat/message-share.js.map +1 -1
- package/dist/components/chat/renderers/bind.d.ts +39 -0
- package/dist/components/chat/renderers/bind.d.ts.map +1 -0
- package/dist/components/chat/renderers/bind.js +43 -0
- package/dist/components/chat/renderers/bind.js.map +1 -0
- package/dist/components/chat/renderers/brain-ui-tools.d.ts +3 -0
- package/dist/components/chat/renderers/brain-ui-tools.d.ts.map +1 -0
- package/dist/components/chat/renderers/brain-ui-tools.js +41 -0
- package/dist/components/chat/renderers/brain-ui-tools.js.map +1 -0
- package/dist/components/chat/renderers/claude-tools.d.ts.map +1 -1
- package/dist/components/chat/renderers/claude-tools.js +3 -2
- package/dist/components/chat/renderers/claude-tools.js.map +1 -1
- package/dist/components/chat/renderers/index.d.ts +10 -2
- package/dist/components/chat/renderers/index.d.ts.map +1 -1
- package/dist/components/chat/renderers/index.js +15 -10
- package/dist/components/chat/renderers/index.js.map +1 -1
- package/dist/components/chat/session-drawer.d.ts +4 -0
- package/dist/components/chat/session-drawer.d.ts.map +1 -1
- package/dist/components/chat/session-drawer.js +55 -40
- package/dist/components/chat/session-drawer.js.map +1 -1
- package/dist/components/chat/session-list.d.ts +41 -0
- package/dist/components/chat/session-list.d.ts.map +1 -0
- package/dist/components/chat/session-list.js +10 -0
- package/dist/components/chat/session-list.js.map +1 -0
- package/dist/components/chat/share-block.d.ts.map +1 -1
- package/dist/components/chat/share-block.js +12 -11
- package/dist/components/chat/share-block.js.map +1 -1
- package/dist/components/chat/share-card.js +2 -2
- package/dist/components/chat/share-card.js.map +1 -1
- package/dist/components/chat/subagent-view.js +1 -1
- package/dist/components/chat/subagent-view.js.map +1 -1
- package/dist/components/chat/tool-call-timeline.d.ts.map +1 -1
- package/dist/components/chat/tool-call-timeline.js +37 -12
- package/dist/components/chat/tool-call-timeline.js.map +1 -1
- package/dist/components/chat/tool-cards/block-card.d.ts +28 -0
- package/dist/components/chat/tool-cards/block-card.d.ts.map +1 -0
- package/dist/components/chat/tool-cards/block-card.js +103 -0
- package/dist/components/chat/tool-cards/block-card.js.map +1 -0
- package/dist/components/chat/tool-cards/image-mask-card.d.ts +70 -0
- package/dist/components/chat/tool-cards/image-mask-card.d.ts.map +1 -0
- package/dist/components/chat/tool-cards/image-mask-card.js +142 -0
- package/dist/components/chat/tool-cards/image-mask-card.js.map +1 -0
- package/dist/components/chat/tool-cards/location-card.d.ts +45 -0
- package/dist/components/chat/tool-cards/location-card.d.ts.map +1 -0
- package/dist/components/chat/tool-cards/location-card.js +128 -0
- package/dist/components/chat/tool-cards/location-card.js.map +1 -0
- package/dist/components/chat/tool-views.d.ts +15 -4
- package/dist/components/chat/tool-views.d.ts.map +1 -1
- package/dist/components/chat/tool-views.js +22 -38
- package/dist/components/chat/tool-views.js.map +1 -1
- package/dist/components/chat/transcript-turn.d.ts +45 -0
- package/dist/components/chat/transcript-turn.d.ts.map +1 -0
- package/dist/components/chat/transcript-turn.js +25 -0
- package/dist/components/chat/transcript-turn.js.map +1 -0
- package/dist/components/chat/use-chat-commands.d.ts.map +1 -1
- package/dist/components/chat/use-chat-commands.js +13 -16
- package/dist/components/chat/use-chat-commands.js.map +1 -1
- package/dist/components/chat/welcome-state.d.ts +7 -0
- package/dist/components/chat/welcome-state.d.ts.map +1 -1
- package/dist/components/chat/welcome-state.js +13 -5
- package/dist/components/chat/welcome-state.js.map +1 -1
- package/dist/components/connectivity/connection-gate.d.ts.map +1 -1
- package/dist/components/connectivity/connection-gate.js +64 -6
- package/dist/components/connectivity/connection-gate.js.map +1 -1
- package/dist/components/connectivity/login-form.d.ts +31 -0
- package/dist/components/connectivity/login-form.d.ts.map +1 -0
- package/dist/components/connectivity/login-form.js +19 -0
- package/dist/components/connectivity/login-form.js.map +1 -0
- package/dist/components/connectivity/login-screen.d.ts +5 -2
- package/dist/components/connectivity/login-screen.d.ts.map +1 -1
- package/dist/components/connectivity/login-screen.js +41 -31
- package/dist/components/connectivity/login-screen.js.map +1 -1
- package/dist/components/dev/share-harness.js +1 -1
- package/dist/components/dev/share-harness.js.map +1 -1
- package/dist/components/files/file-panel.d.ts +1 -1
- package/dist/components/files/file-panel.d.ts.map +1 -1
- package/dist/components/files/file-panel.js +74 -0
- package/dist/components/files/file-panel.js.map +1 -1
- package/dist/components/files/file-tree-view.d.ts +45 -0
- package/dist/components/files/file-tree-view.d.ts.map +1 -0
- package/dist/components/files/file-tree-view.js +51 -0
- package/dist/components/files/file-tree-view.js.map +1 -0
- package/dist/components/files/file-tree.d.ts +6 -0
- package/dist/components/files/file-tree.d.ts.map +1 -1
- package/dist/components/files/file-tree.js +39 -43
- package/dist/components/files/file-tree.js.map +1 -1
- package/dist/components/files/file-viewer-binary.d.ts.map +1 -1
- package/dist/components/files/file-viewer-binary.js +3 -2
- package/dist/components/files/file-viewer-binary.js.map +1 -1
- package/dist/components/files/file-viewer-frame.d.ts +36 -0
- package/dist/components/files/file-viewer-frame.d.ts.map +1 -0
- package/dist/components/files/file-viewer-frame.js +26 -0
- package/dist/components/files/file-viewer-frame.js.map +1 -0
- package/dist/components/files/file-viewer.d.ts +5 -0
- package/dist/components/files/file-viewer.d.ts.map +1 -1
- package/dist/components/files/file-viewer.js +26 -32
- package/dist/components/files/file-viewer.js.map +1 -1
- package/dist/components/files/frontmatter-chips.d.ts +18 -0
- package/dist/components/files/frontmatter-chips.d.ts.map +1 -0
- package/dist/components/files/frontmatter-chips.js +11 -0
- package/dist/components/files/frontmatter-chips.js.map +1 -0
- package/dist/components/files/frontmatter-panel.d.ts +5 -1
- package/dist/components/files/frontmatter-panel.d.ts.map +1 -1
- package/dist/components/files/frontmatter-panel.js +7 -6
- package/dist/components/files/frontmatter-panel.js.map +1 -1
- package/dist/components/files/staleness.d.ts +18 -0
- package/dist/components/files/staleness.d.ts.map +1 -0
- package/dist/components/files/staleness.js +27 -0
- package/dist/components/files/staleness.js.map +1 -0
- package/dist/components/graph/graph-canvas.d.ts.map +1 -1
- package/dist/components/graph/graph-canvas.js +24 -1
- package/dist/components/graph/graph-canvas.js.map +1 -1
- package/dist/components/graph/graph-controls-column.d.ts +14 -0
- package/dist/components/graph/graph-controls-column.d.ts.map +1 -0
- package/dist/components/graph/graph-controls-column.js +44 -0
- package/dist/components/graph/graph-controls-column.js.map +1 -0
- package/dist/components/graph/graph-controls.d.ts +20 -5
- package/dist/components/graph/graph-controls.d.ts.map +1 -1
- package/dist/components/graph/graph-controls.js +58 -37
- package/dist/components/graph/graph-controls.js.map +1 -1
- package/dist/components/graph/graph-form.d.ts +82 -0
- package/dist/components/graph/graph-form.d.ts.map +1 -0
- package/dist/components/graph/graph-form.js +69 -0
- package/dist/components/graph/graph-form.js.map +1 -0
- package/dist/components/graph/graph-maintenance.d.ts.map +1 -1
- package/dist/components/graph/graph-maintenance.js +6 -10
- package/dist/components/graph/graph-maintenance.js.map +1 -1
- package/dist/components/graph/graph-node-rail.d.ts +13 -0
- package/dist/components/graph/graph-node-rail.d.ts.map +1 -0
- package/dist/components/graph/graph-node-rail.js +92 -0
- package/dist/components/graph/graph-node-rail.js.map +1 -0
- package/dist/components/graph/graph-page.d.ts +7 -0
- package/dist/components/graph/graph-page.d.ts.map +1 -1
- package/dist/components/graph/graph-page.js +102 -12
- package/dist/components/graph/graph-page.js.map +1 -1
- package/dist/components/graph/graph-scene.d.ts.map +1 -1
- package/dist/components/graph/graph-scene.js +100 -24
- package/dist/components/graph/graph-scene.js.map +1 -1
- package/dist/components/graph/lib/graph-helpers.d.ts +42 -12
- package/dist/components/graph/lib/graph-helpers.d.ts.map +1 -1
- package/dist/components/graph/lib/graph-helpers.js +33 -41
- package/dist/components/graph/lib/graph-helpers.js.map +1 -1
- package/dist/components/graph/node-card.d.ts +28 -0
- package/dist/components/graph/node-card.d.ts.map +1 -0
- package/dist/components/graph/node-card.js +7 -0
- package/dist/components/graph/node-card.js.map +1 -0
- package/dist/components/graph/node-popover.d.ts +6 -2
- package/dist/components/graph/node-popover.d.ts.map +1 -1
- package/dist/components/graph/node-popover.js +13 -31
- package/dist/components/graph/node-popover.js.map +1 -1
- package/dist/components/graph/use-graph-theme.d.ts +32 -4
- package/dist/components/graph/use-graph-theme.d.ts.map +1 -1
- package/dist/components/graph/use-graph-theme.js +44 -32
- package/dist/components/graph/use-graph-theme.js.map +1 -1
- package/dist/components/graph/use-node-actions.d.ts +26 -0
- package/dist/components/graph/use-node-actions.d.ts.map +1 -0
- package/dist/components/graph/use-node-actions.js +54 -0
- package/dist/components/graph/use-node-actions.js.map +1 -0
- package/dist/components/images/image-share.d.ts +2 -1
- package/dist/components/images/image-share.d.ts.map +1 -1
- package/dist/components/images/image-share.js +3 -3
- package/dist/components/images/image-share.js.map +1 -1
- package/dist/components/images/image-viewer.d.ts.map +1 -1
- package/dist/components/images/image-viewer.js +3 -1
- package/dist/components/images/image-viewer.js.map +1 -1
- package/dist/components/images/mask-editor.d.ts.map +1 -1
- package/dist/components/images/mask-editor.js +7 -5
- package/dist/components/images/mask-editor.js.map +1 -1
- package/dist/components/layout/app-shell.d.ts.map +1 -1
- package/dist/components/layout/app-shell.js +5 -1
- package/dist/components/layout/app-shell.js.map +1 -1
- package/dist/components/layout/desktop-palette.d.ts +23 -0
- package/dist/components/layout/desktop-palette.d.ts.map +1 -0
- package/dist/components/layout/desktop-palette.js +132 -0
- package/dist/components/layout/desktop-palette.js.map +1 -0
- package/dist/components/layout/key-cap.d.ts +9 -0
- package/dist/components/layout/key-cap.d.ts.map +1 -0
- package/dist/components/layout/key-cap.js +10 -0
- package/dist/components/layout/key-cap.js.map +1 -0
- package/dist/components/layout/mobile-tab-bar.d.ts +13 -0
- package/dist/components/layout/mobile-tab-bar.d.ts.map +1 -1
- package/dist/components/layout/mobile-tab-bar.js +58 -47
- package/dist/components/layout/mobile-tab-bar.js.map +1 -1
- package/dist/components/layout/shortcut-switch.d.ts +13 -0
- package/dist/components/layout/shortcut-switch.d.ts.map +1 -0
- package/dist/components/layout/shortcut-switch.js +22 -0
- package/dist/components/layout/shortcut-switch.js.map +1 -0
- package/dist/components/layout/side-rail.d.ts +24 -0
- package/dist/components/layout/side-rail.d.ts.map +1 -1
- package/dist/components/layout/side-rail.js +65 -27
- package/dist/components/layout/side-rail.js.map +1 -1
- package/dist/components/layout/slide-panel.d.ts +12 -2
- package/dist/components/layout/slide-panel.d.ts.map +1 -1
- package/dist/components/layout/slide-panel.js +11 -2
- package/dist/components/layout/slide-panel.js.map +1 -1
- package/dist/components/layout/theme.d.ts +22 -0
- package/dist/components/layout/theme.d.ts.map +1 -0
- package/dist/components/layout/theme.js +44 -0
- package/dist/components/layout/theme.js.map +1 -0
- package/dist/components/quick-actions/add-form.d.ts +44 -0
- package/dist/components/quick-actions/add-form.d.ts.map +1 -0
- package/dist/components/quick-actions/add-form.js +30 -0
- package/dist/components/quick-actions/add-form.js.map +1 -0
- package/dist/components/quick-actions/add-modal.d.ts +7 -0
- package/dist/components/quick-actions/add-modal.d.ts.map +1 -1
- package/dist/components/quick-actions/add-modal.js +66 -20
- package/dist/components/quick-actions/add-modal.js.map +1 -1
- package/dist/components/quick-actions/search-modal.d.ts.map +1 -1
- package/dist/components/quick-actions/search-modal.js +48 -19
- package/dist/components/quick-actions/search-modal.js.map +1 -1
- package/dist/components/quick-actions/streaming-modal.d.ts +6 -0
- package/dist/components/quick-actions/streaming-modal.d.ts.map +1 -1
- package/dist/components/quick-actions/streaming-modal.js +42 -17
- package/dist/components/quick-actions/streaming-modal.js.map +1 -1
- package/dist/components/quick-actions/streaming-output.d.ts +38 -0
- package/dist/components/quick-actions/streaming-output.d.ts.map +1 -0
- package/dist/components/quick-actions/streaming-output.js +22 -0
- package/dist/components/quick-actions/streaming-output.js.map +1 -0
- package/dist/components/quick-actions/whatsup-modal.d.ts +5 -0
- package/dist/components/quick-actions/whatsup-modal.d.ts.map +1 -1
- package/dist/components/quick-actions/whatsup-modal.js +36 -10
- package/dist/components/quick-actions/whatsup-modal.js.map +1 -1
- package/dist/components/settings/devices-agents-tab.d.ts +10 -0
- package/dist/components/settings/devices-agents-tab.d.ts.map +1 -0
- package/dist/components/settings/devices-agents-tab.js +131 -0
- package/dist/components/settings/devices-agents-tab.js.map +1 -0
- package/dist/components/settings/models-list.d.ts +39 -0
- package/dist/components/settings/models-list.d.ts.map +1 -0
- package/dist/components/settings/models-list.js +72 -0
- package/dist/components/settings/models-list.js.map +1 -0
- package/dist/components/settings/models-tab.d.ts +4 -0
- package/dist/components/settings/models-tab.d.ts.map +1 -1
- package/dist/components/settings/models-tab.js +52 -102
- package/dist/components/settings/models-tab.js.map +1 -1
- package/dist/components/settings/one-time-agent-credential.d.ts +3 -0
- package/dist/components/settings/one-time-agent-credential.d.ts.map +1 -0
- package/dist/components/settings/one-time-agent-credential.js +55 -0
- package/dist/components/settings/one-time-agent-credential.js.map +1 -0
- package/dist/components/settings/passkey-list.d.ts +35 -0
- package/dist/components/settings/passkey-list.d.ts.map +1 -0
- package/dist/components/settings/passkey-list.js +34 -0
- package/dist/components/settings/passkey-list.js.map +1 -0
- package/dist/components/settings/passkey-tab.d.ts +3 -0
- package/dist/components/settings/passkey-tab.d.ts.map +1 -1
- package/dist/components/settings/passkey-tab.js +53 -45
- package/dist/components/settings/passkey-tab.js.map +1 -1
- package/dist/components/settings/pi-accounts-list.d.ts +27 -0
- package/dist/components/settings/pi-accounts-list.d.ts.map +1 -0
- package/dist/components/settings/pi-accounts-list.js +15 -0
- package/dist/components/settings/pi-accounts-list.js.map +1 -0
- package/dist/components/settings/pi-accounts.d.ts +3 -0
- package/dist/components/settings/pi-accounts.d.ts.map +1 -1
- package/dist/components/settings/pi-accounts.js +73 -55
- package/dist/components/settings/pi-accounts.js.map +1 -1
- package/dist/components/settings/principal-list.d.ts +37 -0
- package/dist/components/settings/principal-list.d.ts.map +1 -0
- package/dist/components/settings/principal-list.js +18 -0
- package/dist/components/settings/principal-list.js.map +1 -0
- package/dist/components/settings/settings-panel.d.ts +10 -3
- package/dist/components/settings/settings-panel.d.ts.map +1 -1
- package/dist/components/settings/settings-panel.js +80 -6
- package/dist/components/settings/settings-panel.js.map +1 -1
- package/dist/components/settings/skills-list.d.ts +53 -0
- package/dist/components/settings/skills-list.d.ts.map +1 -0
- package/dist/components/settings/skills-list.js +28 -0
- package/dist/components/settings/skills-list.js.map +1 -0
- package/dist/components/settings/skills-tab.d.ts.map +1 -1
- package/dist/components/settings/skills-tab.js +65 -38
- package/dist/components/settings/skills-tab.js.map +1 -1
- package/dist/components/settings/tool-permissions-list.d.ts +18 -0
- package/dist/components/settings/tool-permissions-list.d.ts.map +1 -0
- package/dist/components/settings/tool-permissions-list.js +6 -0
- package/dist/components/settings/tool-permissions-list.js.map +1 -0
- package/dist/components/settings/tool-permissions.d.ts +2 -1
- package/dist/components/settings/tool-permissions.d.ts.map +1 -1
- package/dist/components/settings/tool-permissions.js +33 -16
- package/dist/components/settings/tool-permissions.js.map +1 -1
- package/dist/components/settings/web-search-chain.d.ts +41 -0
- package/dist/components/settings/web-search-chain.d.ts.map +1 -0
- package/dist/components/settings/web-search-chain.js +47 -0
- package/dist/components/settings/web-search-chain.js.map +1 -0
- package/dist/components/settings/web-search-settings.d.ts +4 -7
- package/dist/components/settings/web-search-settings.d.ts.map +1 -1
- package/dist/components/settings/web-search-settings.js +54 -76
- package/dist/components/settings/web-search-settings.js.map +1 -1
- package/dist/components/share/share-menu.d.ts.map +1 -1
- package/dist/components/share/share-menu.js +24 -4
- package/dist/components/share/share-menu.js.map +1 -1
- package/dist/components/voice/dictation-sheet.js +3 -3
- package/dist/components/voice/dictation-sheet.js.map +1 -1
- package/dist/components/voice/mic-button.js +1 -1
- package/dist/components/voice/mic-button.js.map +1 -1
- package/dist/components/voice/review-card.js +1 -1
- package/dist/components/voice/review-card.js.map +1 -1
- package/dist/config.d.ts +5 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +15 -5
- package/dist/config.js.map +1 -1
- package/dist/connection.d.ts +12 -0
- package/dist/connection.d.ts.map +1 -0
- package/dist/connection.js +421 -0
- package/dist/connection.js.map +1 -0
- package/dist/default-root.d.ts +3 -0
- package/dist/default-root.d.ts.map +1 -0
- package/dist/default-root.js +16 -0
- package/dist/default-root.js.map +1 -0
- package/dist/hooks/use-color-scheme.d.ts +14 -0
- package/dist/hooks/use-color-scheme.d.ts.map +1 -0
- package/dist/hooks/use-color-scheme.js +37 -0
- package/dist/hooks/use-color-scheme.js.map +1 -0
- package/dist/hooks/use-hash-routes.d.ts.map +1 -1
- package/dist/hooks/use-hash-routes.js +4 -2
- package/dist/hooks/use-hash-routes.js.map +1 -1
- package/dist/hooks/use-media-query.d.ts +7 -0
- package/dist/hooks/use-media-query.d.ts.map +1 -0
- package/dist/hooks/use-media-query.js +34 -0
- package/dist/hooks/use-media-query.js.map +1 -0
- package/dist/hooks/use-share-intake.d.ts.map +1 -1
- package/dist/hooks/use-share-intake.js +37 -12
- package/dist/hooks/use-share-intake.js.map +1 -1
- package/dist/hooks/use-vpn-status.d.ts +1 -1
- package/dist/hooks/use-vpn-status.d.ts.map +1 -1
- package/dist/hooks/use-vpn-status.js +20 -16
- package/dist/hooks/use-vpn-status.js.map +1 -1
- package/dist/hooks/use-websocket.d.ts +5 -24
- package/dist/hooks/use-websocket.d.ts.map +1 -1
- package/dist/hooks/use-websocket.js +11 -382
- package/dist/hooks/use-websocket.js.map +1 -1
- package/dist/hooks/websocket-handlers/activity.d.ts +2 -2
- package/dist/hooks/websocket-handlers/activity.d.ts.map +1 -1
- package/dist/hooks/websocket-handlers/activity.js +4 -5
- package/dist/hooks/websocket-handlers/activity.js.map +1 -1
- package/dist/hooks/websocket-handlers/chat.d.ts +1 -0
- package/dist/hooks/websocket-handlers/chat.d.ts.map +1 -1
- package/dist/hooks/websocket-handlers/chat.js +27 -5
- package/dist/hooks/websocket-handlers/chat.js.map +1 -1
- package/dist/hooks/websocket-handlers/connection.d.ts.map +1 -1
- package/dist/hooks/websocket-handlers/connection.js +4 -6
- package/dist/hooks/websocket-handlers/connection.js.map +1 -1
- package/dist/hooks/websocket-handlers/mask.d.ts +1 -1
- package/dist/hooks/websocket-handlers/mask.d.ts.map +1 -1
- package/dist/hooks/websocket-handlers/mask.js +2 -3
- package/dist/hooks/websocket-handlers/mask.js.map +1 -1
- package/dist/hooks/websocket-handlers/provider.d.ts.map +1 -1
- package/dist/hooks/websocket-handlers/provider.js +4 -6
- package/dist/hooks/websocket-handlers/provider.js.map +1 -1
- package/dist/hooks/websocket-handlers/types.d.ts +6 -2
- package/dist/hooks/websocket-handlers/types.d.ts.map +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -3
- package/dist/index.js.map +1 -1
- package/dist/lib/api-client.d.ts +321 -0
- package/dist/lib/api-client.d.ts.map +1 -1
- package/dist/lib/api-client.js +243 -213
- package/dist/lib/api-client.js.map +1 -1
- package/dist/lib/backend.d.ts +19 -2
- package/dist/lib/backend.d.ts.map +1 -1
- package/dist/lib/backend.js +15 -9
- package/dist/lib/backend.js.map +1 -1
- package/dist/lib/diff.d.ts +8 -10
- package/dist/lib/diff.d.ts.map +1 -1
- package/dist/lib/diff.js +26 -53
- package/dist/lib/diff.js.map +1 -1
- package/dist/lib/format-time.d.ts +3 -0
- package/dist/lib/format-time.d.ts.map +1 -0
- package/dist/lib/format-time.js +25 -0
- package/dist/lib/format-time.js.map +1 -0
- package/dist/lib/light-dark.d.ts +37 -0
- package/dist/lib/light-dark.d.ts.map +1 -0
- package/dist/lib/light-dark.js +83 -0
- package/dist/lib/light-dark.js.map +1 -0
- package/dist/lib/mermaid-theme.d.ts +7 -2
- package/dist/lib/mermaid-theme.d.ts.map +1 -1
- package/dist/lib/mermaid-theme.js +125 -203
- package/dist/lib/mermaid-theme.js.map +1 -1
- package/dist/lib/passkeys.d.ts +6 -3
- package/dist/lib/passkeys.d.ts.map +1 -1
- package/dist/lib/passkeys.js +10 -4
- package/dist/lib/passkeys.js.map +1 -1
- package/dist/lib/push-registration.d.ts +10 -0
- package/dist/lib/push-registration.d.ts.map +1 -0
- package/dist/lib/push-registration.js +44 -0
- package/dist/lib/push-registration.js.map +1 -0
- package/dist/lib/share-intake.d.ts +2 -1
- package/dist/lib/share-intake.d.ts.map +1 -1
- package/dist/lib/share-intake.js +3 -3
- package/dist/lib/share-intake.js.map +1 -1
- package/dist/lib/share.d.ts +4 -3
- package/dist/lib/share.d.ts.map +1 -1
- package/dist/lib/share.js +6 -7
- package/dist/lib/share.js.map +1 -1
- package/dist/lib/single-key.d.ts +30 -0
- package/dist/lib/single-key.d.ts.map +1 -0
- package/dist/lib/single-key.js +45 -0
- package/dist/lib/single-key.js.map +1 -0
- package/dist/lib/tool-names.d.ts +8 -6
- package/dist/lib/tool-names.d.ts.map +1 -1
- package/dist/lib/tool-names.js +17 -7
- package/dist/lib/tool-names.js.map +1 -1
- package/dist/root-context.d.ts +229 -0
- package/dist/root-context.d.ts.map +1 -0
- package/dist/root-context.js +33 -0
- package/dist/root-context.js.map +1 -0
- package/dist/root.d.ts +38 -0
- package/dist/root.d.ts.map +1 -0
- package/dist/root.js +47 -0
- package/dist/root.js.map +1 -0
- package/dist/stores/activity-state.d.ts +29 -0
- package/dist/stores/activity-state.d.ts.map +1 -0
- package/dist/stores/activity-state.js +282 -0
- package/dist/stores/activity-state.js.map +1 -0
- package/dist/stores/activity-store.d.ts +10 -41
- package/dist/stores/activity-store.d.ts.map +1 -1
- package/dist/stores/activity-store.js +9 -271
- package/dist/stores/activity-store.js.map +1 -1
- package/dist/stores/chat-state.d.ts +212 -0
- package/dist/stores/chat-state.d.ts.map +1 -0
- package/dist/stores/chat-state.js +466 -0
- package/dist/stores/chat-state.js.map +1 -0
- package/dist/stores/chat-store.d.ts +4 -162
- package/dist/stores/chat-store.d.ts.map +1 -1
- package/dist/stores/chat-store.js +7 -418
- package/dist/stores/chat-store.js.map +1 -1
- package/dist/stores/connection-state.d.ts +40 -0
- package/dist/stores/connection-state.d.ts.map +1 -0
- package/dist/stores/connection-state.js +30 -0
- package/dist/stores/connection-state.js.map +1 -0
- package/dist/stores/connection-store.d.ts +4 -40
- package/dist/stores/connection-store.d.ts.map +1 -1
- package/dist/stores/connection-store.js +7 -27
- package/dist/stores/connection-store.js.map +1 -1
- package/dist/stores/create-stores.d.ts +23 -0
- package/dist/stores/create-stores.d.ts.map +1 -0
- package/dist/stores/create-stores.js +28 -0
- package/dist/stores/create-stores.js.map +1 -0
- package/dist/stores/file-state.d.ts +53 -0
- package/dist/stores/file-state.d.ts.map +1 -0
- package/dist/stores/file-state.js +341 -0
- package/dist/stores/file-state.js.map +1 -0
- package/dist/stores/file-store.d.ts +4 -52
- package/dist/stores/file-store.d.ts.map +1 -1
- package/dist/stores/file-store.js +7 -300
- package/dist/stores/file-store.js.map +1 -1
- package/dist/stores/graph-state.d.ts +95 -0
- package/dist/stores/graph-state.d.ts.map +1 -0
- package/dist/stores/graph-state.js +252 -0
- package/dist/stores/graph-state.js.map +1 -0
- package/dist/stores/graph-store.d.ts +7 -83
- package/dist/stores/graph-store.d.ts.map +1 -1
- package/dist/stores/graph-store.js +8 -249
- package/dist/stores/graph-store.js.map +1 -1
- package/dist/stores/mask-state.d.ts +21 -0
- package/dist/stores/mask-state.d.ts.map +1 -0
- package/dist/stores/mask-state.js +9 -0
- package/dist/stores/mask-state.js.map +1 -0
- package/dist/stores/mask-store.d.ts +4 -21
- package/dist/stores/mask-store.d.ts.map +1 -1
- package/dist/stores/mask-store.js +7 -6
- package/dist/stores/mask-store.js.map +1 -1
- package/dist/stores/principal-state.d.ts +16 -0
- package/dist/stores/principal-state.d.ts.map +1 -0
- package/dist/stores/principal-state.js +31 -0
- package/dist/stores/principal-state.js.map +1 -0
- package/dist/stores/principal-store.d.ts +5 -0
- package/dist/stores/principal-store.d.ts.map +1 -0
- package/dist/stores/principal-store.js +8 -0
- package/dist/stores/principal-store.js.map +1 -0
- package/dist/stores/provider-state.d.ts +24 -0
- package/dist/stores/provider-state.d.ts.map +1 -0
- package/dist/stores/provider-state.js +68 -0
- package/dist/stores/provider-state.js.map +1 -0
- package/dist/stores/provider-store.d.ts +4 -23
- package/dist/stores/provider-store.d.ts.map +1 -1
- package/dist/stores/provider-store.js +7 -64
- package/dist/stores/provider-store.js.map +1 -1
- package/dist/stores/share-state.d.ts +29 -0
- package/dist/stores/share-state.d.ts.map +1 -0
- package/dist/stores/share-state.js +21 -0
- package/dist/stores/share-state.js.map +1 -0
- package/dist/stores/share-store.d.ts +4 -28
- package/dist/stores/share-store.d.ts.map +1 -1
- package/dist/stores/share-store.js +7 -18
- package/dist/stores/share-store.js.map +1 -1
- package/dist/stores/store-environment.d.ts +10 -0
- package/dist/stores/store-environment.d.ts.map +1 -0
- package/dist/stores/store-environment.js +2 -0
- package/dist/stores/store-environment.js.map +1 -0
- package/dist/stores/ui-state.d.ts +71 -0
- package/dist/stores/ui-state.d.ts.map +1 -0
- package/dist/stores/ui-state.js +58 -0
- package/dist/stores/ui-state.js.map +1 -0
- package/dist/stores/ui-store.d.ts +4 -45
- package/dist/stores/ui-store.d.ts.map +1 -1
- package/dist/stores/ui-store.js +7 -36
- package/dist/stores/ui-store.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/theme.css +150 -97
- package/dist/voice/asr-clients.d.ts +2 -1
- package/dist/voice/asr-clients.d.ts.map +1 -1
- package/dist/voice/asr-clients.js +16 -17
- package/dist/voice/asr-clients.js.map +1 -1
- package/dist/voice/use-dictation.d.ts.map +1 -1
- package/dist/voice/use-dictation.js +12 -8
- package/dist/voice/use-dictation.js.map +1 -1
- package/dist/voice/voice-state.d.ts +25 -0
- package/dist/voice/voice-state.d.ts.map +1 -0
- package/dist/voice/voice-state.js +29 -0
- package/dist/voice/voice-state.js.map +1 -0
- package/dist/voice/voice-store.d.ts +4 -25
- package/dist/voice/voice-store.d.ts.map +1 -1
- package/dist/voice/voice-store.js +7 -26
- package/dist/voice/voice-store.js.map +1 -1
- package/package.json +3 -2
- package/src/components/activity/activity-page.tsx +334 -160
- package/src/components/activity/activity-run-detail.tsx +81 -47
- package/src/components/activity/activity-run-list.tsx +27 -59
- package/src/components/activity/activity-views.tsx +155 -0
- package/src/components/activity/approval-card.tsx +80 -0
- package/src/components/activity/digest-card.tsx +25 -61
- package/src/components/activity/digest-summary.tsx +52 -0
- package/src/components/activity/push-switch.tsx +62 -0
- package/src/components/activity/push-toggle.tsx +51 -81
- package/src/components/activity/span-bits.tsx +8 -7
- package/src/components/chat/ask-user-card.tsx +292 -356
- package/src/components/chat/ask-user-typed.ts +77 -0
- package/src/components/chat/brain-markdown-links.tsx +4 -4
- package/src/components/chat/brain-markdown.tsx +4 -2
- package/src/components/chat/chat-page.tsx +38 -9
- package/src/components/chat/composer-view.tsx +194 -0
- package/src/components/chat/composer.tsx +117 -296
- package/src/components/chat/markdown-content.tsx +53 -2
- package/src/components/chat/mermaid-block.tsx +13 -5
- package/src/components/chat/mermaid-share.ts +8 -7
- package/src/components/chat/mermaid-viewer.tsx +3 -1
- package/src/components/chat/message-bubble.tsx +78 -94
- package/src/components/chat/message-share.ts +7 -7
- package/src/components/chat/renderers/bind.tsx +94 -0
- package/src/components/chat/renderers/brain-ui-tools.tsx +56 -0
- package/src/components/chat/renderers/claude-tools.tsx +3 -2
- package/src/components/chat/renderers/index.ts +15 -10
- package/src/components/chat/session-drawer.tsx +60 -138
- package/src/components/chat/session-list.tsx +113 -0
- package/src/components/chat/share-block.tsx +16 -12
- package/src/components/chat/share-card.tsx +2 -2
- package/src/components/chat/subagent-view.tsx +4 -4
- package/src/components/chat/tool-call-timeline.tsx +59 -22
- package/src/components/chat/tool-cards/block-card.tsx +145 -0
- package/src/components/chat/tool-cards/image-mask-card.tsx +214 -0
- package/src/components/chat/tool-cards/location-card.tsx +159 -0
- package/src/components/chat/tool-views.tsx +23 -73
- package/src/components/chat/transcript-turn.tsx +108 -0
- package/src/components/chat/use-chat-commands.ts +14 -16
- package/src/components/chat/welcome-state.tsx +36 -91
- package/src/components/connectivity/connection-gate.tsx +80 -9
- package/src/components/connectivity/login-form.tsx +122 -0
- package/src/components/connectivity/login-screen.tsx +45 -100
- package/src/components/dev/share-harness.tsx +2 -2
- package/src/components/files/file-panel.tsx +168 -0
- package/src/components/files/file-tree-view.tsx +127 -0
- package/src/components/files/file-tree.tsx +55 -109
- package/src/components/files/file-viewer-binary.tsx +3 -2
- package/src/components/files/file-viewer-frame.tsx +113 -0
- package/src/components/files/file-viewer.tsx +40 -95
- package/src/components/files/frontmatter-chips.tsx +42 -0
- package/src/components/files/frontmatter-panel.tsx +6 -71
- package/src/components/files/staleness.ts +29 -0
- package/src/components/graph/graph-canvas.tsx +24 -1
- package/src/components/graph/graph-controls-column.tsx +92 -0
- package/src/components/graph/graph-controls.tsx +83 -127
- package/src/components/graph/graph-form.tsx +183 -0
- package/src/components/graph/graph-maintenance.tsx +9 -14
- package/src/components/graph/graph-node-rail.tsx +147 -0
- package/src/components/graph/graph-page.tsx +189 -49
- package/src/components/graph/graph-scene.tsx +200 -30
- package/src/components/graph/lib/graph-helpers.ts +65 -43
- package/src/components/graph/node-card.tsx +64 -0
- package/src/components/graph/node-popover.tsx +28 -117
- package/src/components/graph/use-graph-theme.ts +69 -32
- package/src/components/graph/use-node-actions.ts +56 -0
- package/src/components/images/image-share.ts +4 -2
- package/src/components/images/image-viewer.tsx +3 -1
- package/src/components/images/mask-editor.tsx +7 -4
- package/src/components/layout/app-shell.tsx +7 -1
- package/src/components/layout/desktop-palette.tsx +151 -0
- package/src/components/layout/key-cap.tsx +15 -0
- package/src/components/layout/mobile-tab-bar.tsx +80 -157
- package/src/components/layout/shortcut-switch.tsx +32 -0
- package/src/components/layout/side-rail.tsx +70 -150
- package/src/components/layout/slide-panel.tsx +29 -2
- package/src/components/layout/theme.tsx +67 -0
- package/src/components/quick-actions/add-form.tsx +188 -0
- package/src/components/quick-actions/add-modal.tsx +68 -144
- package/src/components/quick-actions/search-modal.tsx +49 -19
- package/src/components/quick-actions/streaming-modal.tsx +44 -71
- package/src/components/quick-actions/streaming-output.tsx +112 -0
- package/src/components/quick-actions/whatsup-modal.tsx +38 -60
- package/src/components/settings/devices-agents-tab.tsx +139 -0
- package/src/components/settings/models-list.tsx +299 -0
- package/src/components/settings/models-tab.tsx +63 -337
- package/src/components/settings/one-time-agent-credential.tsx +131 -0
- package/src/components/settings/passkey-list.tsx +201 -0
- package/src/components/settings/passkey-tab.tsx +53 -197
- package/src/components/settings/pi-accounts-list.tsx +128 -0
- package/src/components/settings/pi-accounts.tsx +71 -170
- package/src/components/settings/principal-list.tsx +172 -0
- package/src/components/settings/settings-panel.tsx +245 -19
- package/src/components/settings/skills-list.tsx +276 -0
- package/src/components/settings/skills-tab.tsx +82 -269
- package/src/components/settings/tool-permissions-list.tsx +54 -0
- package/src/components/settings/tool-permissions.tsx +27 -46
- package/src/components/settings/web-search-chain.tsx +300 -0
- package/src/components/settings/web-search-settings.tsx +58 -328
- package/src/components/share/share-menu.tsx +19 -4
- package/src/components/voice/dictation-sheet.tsx +5 -5
- package/src/components/voice/mic-button.tsx +1 -1
- package/src/components/voice/review-card.tsx +1 -1
- package/src/config.ts +21 -7
- package/src/connection.ts +443 -0
- package/src/default-root.ts +16 -0
- package/src/hooks/use-color-scheme.ts +36 -0
- package/src/hooks/use-hash-routes.ts +4 -2
- package/src/hooks/use-media-query.ts +32 -0
- package/src/hooks/use-share-intake.ts +32 -12
- package/src/hooks/use-vpn-status.ts +23 -15
- package/src/hooks/use-websocket.ts +12 -426
- package/src/hooks/websocket-handlers/activity.ts +4 -5
- package/src/hooks/websocket-handlers/chat.ts +26 -5
- package/src/hooks/websocket-handlers/connection.ts +4 -6
- package/src/hooks/websocket-handlers/mask.ts +2 -3
- package/src/hooks/websocket-handlers/provider.ts +4 -6
- package/src/hooks/websocket-handlers/types.ts +6 -2
- package/src/index.ts +7 -3
- package/src/lib/api-client.ts +446 -365
- package/src/lib/backend.ts +20 -10
- package/src/lib/diff.ts +28 -62
- package/src/lib/format-time.ts +20 -0
- package/src/lib/light-dark.ts +83 -0
- package/src/lib/mermaid-theme.ts +183 -214
- package/src/lib/passkeys.ts +12 -4
- package/src/lib/push-registration.ts +46 -0
- package/src/lib/share-intake.ts +6 -3
- package/src/lib/share.ts +7 -4
- package/src/lib/single-key.ts +47 -0
- package/src/lib/tool-names.ts +31 -8
- package/src/root-context.tsx +40 -0
- package/src/root.ts +83 -0
- package/src/stores/activity-state.ts +295 -0
- package/src/stores/activity-store.ts +11 -285
- package/src/stores/chat-state.ts +795 -0
- package/src/stores/chat-store.ts +11 -705
- package/src/stores/connection-state.ts +78 -0
- package/src/stores/connection-store.ts +11 -76
- package/src/stores/create-stores.ts +31 -0
- package/src/stores/file-state.ts +399 -0
- package/src/stores/file-store.ts +9 -360
- package/src/stores/graph-state.ts +368 -0
- package/src/stores/graph-store.ts +10 -355
- package/src/stores/mask-state.ts +30 -0
- package/src/stores/mask-store.ts +10 -27
- package/src/stores/principal-state.ts +42 -0
- package/src/stores/principal-store.ts +11 -0
- package/src/stores/provider-state.ts +103 -0
- package/src/stores/provider-store.ts +10 -95
- package/src/stores/share-state.ts +56 -0
- package/src/stores/share-store.ts +11 -54
- package/src/stores/store-environment.ts +10 -0
- package/src/stores/ui-state.ts +143 -0
- package/src/stores/ui-store.ts +11 -93
- package/src/styles.css +4 -1
- package/src/theme.css +150 -97
- package/src/voice/asr-clients.ts +17 -17
- package/src/voice/use-dictation.ts +11 -8
- package/src/voice/voice-state.ts +57 -0
- package/src/voice/voice-store.ts +11 -55
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
import { BrainUiClient, type WebSocketClose } from "@schlessera/brain-ui-sdk/client";
|
|
2
|
+
import type { ServerMessage, ClientMessage, ServerLocationRequest } from "@schlessera/brain-ui-sdk/protocol";
|
|
3
|
+
import type { BrainUiServices } from "./root.js";
|
|
4
|
+
import { activeChat, type ChatState, type ChatKey } from "./stores/chat-state.js";
|
|
5
|
+
import { dispatchServerMessage } from "./hooks/websocket-handlers/index.js";
|
|
6
|
+
import { runStateForFrame } from "./hooks/websocket-handlers/chat.js";
|
|
7
|
+
import { REFUSAL_ATTEMPTS } from "./components/connectivity/connection-state.js";
|
|
8
|
+
|
|
9
|
+
/** Every callback and mutable queue belongs to the root supplied here. */
|
|
10
|
+
export function createWebSocketClient(root: BrainUiServices) {
|
|
11
|
+
let disposed = false;
|
|
12
|
+
let generation = 0;
|
|
13
|
+
let owners = 0;
|
|
14
|
+
let removeListeners: (() => void) | undefined;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Does this frame announce the identity of the conversation THIS client just
|
|
18
|
+
* started?
|
|
19
|
+
*
|
|
20
|
+
* The old test was `session_info || result` for any unknown session, which
|
|
21
|
+
* adopted whichever arrived first — an older background turn finishing, or
|
|
22
|
+
* another client's new session, would capture the user's draft and bind it to
|
|
23
|
+
* a transcript they never wrote. `draftId` is minted per draft turn and echoed
|
|
24
|
+
* on `session_info`, so a match is proof.
|
|
25
|
+
*
|
|
26
|
+
* A server too old to echo it sends no `draftId`, and the pre-existing
|
|
27
|
+
* behaviour applies rather than the draft never binding at all: correctness
|
|
28
|
+
* where the information exists, compatibility where it does not.
|
|
29
|
+
*/
|
|
30
|
+
function isOurDraftAnnouncement(
|
|
31
|
+
state: ChatState,
|
|
32
|
+
msg: ServerMessage
|
|
33
|
+
): boolean {
|
|
34
|
+
// A terminal result has no draft correlation. Another client's turn may
|
|
35
|
+
// finish before our session_info arrives, so it can never claim our draft.
|
|
36
|
+
if (msg.type !== "session_info") return false;
|
|
37
|
+
const pending = state.pendingDraftId;
|
|
38
|
+
if (msg.draftId) return msg.draftId === pending;
|
|
39
|
+
// No echo to compare against.
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Lazily open a session-scoped activity subscription (live subagent rows,
|
|
45
|
+
* server-stamped tool timing). Idempotent per connection; the subscribed set
|
|
46
|
+
* resets when a new socket's server_hello arrives.
|
|
47
|
+
*/
|
|
48
|
+
function ensureActivitySubscription(sessionId: string | null | undefined): void {
|
|
49
|
+
if (!sessionId) return;
|
|
50
|
+
const activity = root.stores.activity.getState();
|
|
51
|
+
if (!activity.supported || activity.subscribed[sessionId]) return;
|
|
52
|
+
activity.markSubscribed(sessionId);
|
|
53
|
+
wsClient?.send({ type: "activity_subscribe", view: "session", sessionId });
|
|
54
|
+
// The subscription snapshot carries OPEN runs; finished runs (history
|
|
55
|
+
// timing, past fan-outs) come from the REST side.
|
|
56
|
+
void root.stores.activity.loadSessionActivityHistory(sessionId);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Text and thinking deltas waiting to be applied, per buffer.
|
|
61
|
+
*
|
|
62
|
+
* A model streams tokens far faster than the display refreshes, and each socket
|
|
63
|
+
* frame arrives in its own macrotask, so React cannot batch them: one token was
|
|
64
|
+
* one store write, one render and one markdown re-parse of the growing message.
|
|
65
|
+
* Coalescing a frame's worth of tokens into a single write costs nothing
|
|
66
|
+
* visually — the screen could not have shown the intermediate states anyway.
|
|
67
|
+
*
|
|
68
|
+
* Consecutive same-kind chunks merge, so a burst of text becomes ONE appendText
|
|
69
|
+
* rather than one per token. Kind changes start a new chunk, which is what keeps
|
|
70
|
+
* interleaved thinking and text in their true chronological order.
|
|
71
|
+
*/
|
|
72
|
+
type PendingDelta = { kind: "text" | "thinking"; text: string };
|
|
73
|
+
const pendingDeltas = new Map<ChatKey, PendingDelta[]>();
|
|
74
|
+
let flushScheduled = false;
|
|
75
|
+
let frameId: number | undefined;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Apply every buffered delta, in arrival order, and clear the buffer.
|
|
79
|
+
*
|
|
80
|
+
* Exported so tests can force the frame rather than wait for one; production
|
|
81
|
+
* code never needs to call it, because every path that reads the transcript
|
|
82
|
+
* flushes first.
|
|
83
|
+
*/
|
|
84
|
+
function flushChatDeltas(): void {
|
|
85
|
+
flushDeltas();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function flushDeltas(): void {
|
|
89
|
+
if (frameId !== undefined && typeof cancelAnimationFrame === "function") cancelAnimationFrame(frameId);
|
|
90
|
+
frameId = undefined;
|
|
91
|
+
flushScheduled = false;
|
|
92
|
+
if (pendingDeltas.size === 0) return;
|
|
93
|
+
const batches = [...pendingDeltas.entries()];
|
|
94
|
+
pendingDeltas.clear();
|
|
95
|
+
const state = root.stores.chat.getState();
|
|
96
|
+
for (const [key, chunks] of batches) {
|
|
97
|
+
for (const chunk of chunks) {
|
|
98
|
+
if (chunk.kind === "text") state.appendText(key, chunk.text);
|
|
99
|
+
else state.appendThinking(key, chunk.text);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function enqueueDelta(key: ChatKey, kind: PendingDelta["kind"], text: string): void {
|
|
105
|
+
const chunks = pendingDeltas.get(key);
|
|
106
|
+
if (!chunks) {
|
|
107
|
+
pendingDeltas.set(key, [{ kind, text }]);
|
|
108
|
+
} else {
|
|
109
|
+
const last = chunks[chunks.length - 1];
|
|
110
|
+
if (last.kind === kind) last.text += text;
|
|
111
|
+
else chunks.push({ kind, text });
|
|
112
|
+
}
|
|
113
|
+
// Outside a browser (unit tests, SSR) there is no frame to wait for, so apply
|
|
114
|
+
// straight away and keep the handler's behaviour synchronous.
|
|
115
|
+
if (typeof requestAnimationFrame !== "function") {
|
|
116
|
+
flushDeltas();
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
if (!flushScheduled) {
|
|
120
|
+
flushScheduled = true;
|
|
121
|
+
frameId = requestAnimationFrame(flushDeltas);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function handleServerMessage(msg: ServerMessage) {
|
|
126
|
+
if (disposed) return;
|
|
127
|
+
// Every frame that is not itself a delta must see the transcript fully
|
|
128
|
+
// applied: the demux below reads buffer state, and the store records parts in
|
|
129
|
+
// arrival order, so a tool call landing ahead of buffered text would reorder
|
|
130
|
+
// the message.
|
|
131
|
+
if (msg.type !== "text_delta" && msg.type !== "thinking_delta") flushDeltas();
|
|
132
|
+
|
|
133
|
+
const state = root.stores.chat.getState();
|
|
134
|
+
|
|
135
|
+
// Activity stream frames feed their own store and never touch chat state.
|
|
136
|
+
if (
|
|
137
|
+
msg.type === "server_hello" ||
|
|
138
|
+
msg.type === "activity_snapshot" ||
|
|
139
|
+
msg.type === "activity_delta"
|
|
140
|
+
) {
|
|
141
|
+
const key = state.activeSessionId;
|
|
142
|
+
dispatchServerMessage(msg, {
|
|
143
|
+
stores: root.stores,
|
|
144
|
+
state,
|
|
145
|
+
frameSessionId: undefined,
|
|
146
|
+
frameTurnId: undefined,
|
|
147
|
+
key,
|
|
148
|
+
buffer: () => (key === null ? state.draft : state.buffers[key]),
|
|
149
|
+
enqueueDelta,
|
|
150
|
+
ensureActivitySubscription,
|
|
151
|
+
requestBrowserLocation,
|
|
152
|
+
resyncIfNeeded,
|
|
153
|
+
coldResumeIfNeeded,
|
|
154
|
+
markHistoryReplaced,
|
|
155
|
+
});
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Parallel-session demux (per-session buffers). Every frame is scoped by
|
|
160
|
+
// sessionId; a scoped frame lands in ITS session's buffer, so a background
|
|
161
|
+
// session's transcript keeps accumulating while another one is in view.
|
|
162
|
+
// Frames without a sessionId (legacy single-session servers, or a new
|
|
163
|
+
// session's pre-binding frames) apply to the buffer in view.
|
|
164
|
+
const frameSessionId = (msg as { sessionId?: string }).sessionId;
|
|
165
|
+
if (frameSessionId) {
|
|
166
|
+
// `detail` on a queued status is the host's queue-pressure note; it rides
|
|
167
|
+
// along so the session's badge can show WHY it is queued deep.
|
|
168
|
+
const queueNote =
|
|
169
|
+
msg.type === "status" && msg.status === "queued" ? msg.detail : undefined;
|
|
170
|
+
// `message_blocks` arrives AFTER the turn's result (D42); it must not
|
|
171
|
+
// reopen a finished session, and it must not touch a queued follow-up
|
|
172
|
+
// that may already be running, so it takes no part in run state.
|
|
173
|
+
if (msg.type !== "message_blocks") {
|
|
174
|
+
state.setRunState(frameSessionId, runStateForFrame(msg), queueNote);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Resolve the target buffer key for this frame.
|
|
179
|
+
let key: ChatKey;
|
|
180
|
+
if (!frameSessionId) {
|
|
181
|
+
key = state.activeSessionId; // null = the draft view
|
|
182
|
+
} else if (state.buffers[frameSessionId]) {
|
|
183
|
+
key = frameSessionId;
|
|
184
|
+
} else if (state.draft && isOurDraftAnnouncement(state, msg)) {
|
|
185
|
+
// A draft run just got its server identity: adopt the draft buffer.
|
|
186
|
+
state.bindDraftSession(frameSessionId);
|
|
187
|
+
key = frameSessionId;
|
|
188
|
+
} else if (msg.type === "session_info" && frameSessionId === state.activeSessionId) {
|
|
189
|
+
// The ACTIVE session announced itself but has no buffer yet — the cold
|
|
190
|
+
// reattach case (PWA relaunch mid-turn; the server may skip the history
|
|
191
|
+
// frame when a first turn has no history yet). Materialize the buffer so
|
|
192
|
+
// this session_info and every following delta land instead of vanishing.
|
|
193
|
+
state.setActiveSession(frameSessionId);
|
|
194
|
+
key = frameSessionId;
|
|
195
|
+
} else if (msg.type === "session_history") {
|
|
196
|
+
// History replay may create a buffer (cold resume / reattach).
|
|
197
|
+
key = frameSessionId;
|
|
198
|
+
} else {
|
|
199
|
+
// Scoped frame for a session with no buffer (never opened, or evicted):
|
|
200
|
+
// the badge above is all we track. The transcript heals via
|
|
201
|
+
// session_resume when the user opens it.
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const buffer = () => {
|
|
206
|
+
const s = root.stores.chat.getState();
|
|
207
|
+
return key === null ? s.draft : s.buffers[key];
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
dispatchServerMessage(msg, {
|
|
211
|
+
stores: root.stores,
|
|
212
|
+
state,
|
|
213
|
+
frameSessionId,
|
|
214
|
+
frameTurnId: (msg as { turnId?: string }).turnId,
|
|
215
|
+
key,
|
|
216
|
+
buffer,
|
|
217
|
+
enqueueDelta,
|
|
218
|
+
ensureActivitySubscription,
|
|
219
|
+
requestBrowserLocation,
|
|
220
|
+
resyncIfNeeded,
|
|
221
|
+
coldResumeIfNeeded,
|
|
222
|
+
markHistoryReplaced,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* The server asked the browser for its location (the agent called
|
|
228
|
+
* `get_current_location`). Read `navigator.geolocation` and reply over the same
|
|
229
|
+
* requestId. The browser shows its own permission prompt on first use; a denial,
|
|
230
|
+
* timeout, or unavailable position comes back as a `location_error` that the
|
|
231
|
+
* server turns into a tool error for the agent.
|
|
232
|
+
*/
|
|
233
|
+
function requestBrowserLocation(msg: ServerLocationRequest) {
|
|
234
|
+
const client = wsClient;
|
|
235
|
+
const requestedGeneration = generation;
|
|
236
|
+
const requestedSocket = root.stores.connection.getState().socketOpens;
|
|
237
|
+
const reply = (message: ClientMessage) => {
|
|
238
|
+
if (!disposed && requestedGeneration === generation && requestedSocket === root.stores.connection.getState().socketOpens && client === wsClient) client?.send(message);
|
|
239
|
+
};
|
|
240
|
+
const { requestId } = msg;
|
|
241
|
+
if (typeof navigator === "undefined" || !navigator.geolocation) {
|
|
242
|
+
reply({
|
|
243
|
+
type: "location_error",
|
|
244
|
+
requestId,
|
|
245
|
+
code: 0,
|
|
246
|
+
message: "Geolocation is not available in this browser.",
|
|
247
|
+
});
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
const opts = msg.options ?? {};
|
|
251
|
+
navigator.geolocation.getCurrentPosition(
|
|
252
|
+
(pos) => {
|
|
253
|
+
reply({
|
|
254
|
+
type: "location_response",
|
|
255
|
+
requestId,
|
|
256
|
+
coords: {
|
|
257
|
+
latitude: pos.coords.latitude,
|
|
258
|
+
longitude: pos.coords.longitude,
|
|
259
|
+
accuracy: pos.coords.accuracy,
|
|
260
|
+
altitude: pos.coords.altitude,
|
|
261
|
+
altitudeAccuracy: pos.coords.altitudeAccuracy,
|
|
262
|
+
heading: pos.coords.heading,
|
|
263
|
+
speed: pos.coords.speed,
|
|
264
|
+
},
|
|
265
|
+
timestamp: pos.timestamp,
|
|
266
|
+
});
|
|
267
|
+
},
|
|
268
|
+
(err) => {
|
|
269
|
+
reply({
|
|
270
|
+
type: "location_error",
|
|
271
|
+
requestId,
|
|
272
|
+
code: err.code,
|
|
273
|
+
message: err.message || "Failed to retrieve location.",
|
|
274
|
+
});
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
enableHighAccuracy: opts.enableHighAccuracy ?? false,
|
|
278
|
+
timeout: opts.timeoutMs ?? 15000,
|
|
279
|
+
maximumAge: opts.maximumAgeMs ?? 60000,
|
|
280
|
+
}
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Reconnection bookkeeping: after a WS drop, the rendered transcript may have
|
|
285
|
+
// gaps (deltas streamed while offline are lost). We heal by replaying the
|
|
286
|
+
// authoritative session history — immediately if nothing is streaming, or
|
|
287
|
+
// once the in-flight turn settles (result / idle status). The pending resync
|
|
288
|
+
// is keyed to the session that needs healing (the one that was active at the
|
|
289
|
+
// reconnect), so a background session settling first cannot consume it.
|
|
290
|
+
let wasDisconnected = false;
|
|
291
|
+
let resyncSessionId: string | null = null;
|
|
292
|
+
|
|
293
|
+
function handleSocketClose(close: WebSocketClose): void {
|
|
294
|
+
const connection = root.stores.connection.getState();
|
|
295
|
+
connection.recordWsClose(close.opened, close.code);
|
|
296
|
+
if (
|
|
297
|
+
!close.opened &&
|
|
298
|
+
root.stores.connection.getState().handshakeFailures >= REFUSAL_ATTEMPTS
|
|
299
|
+
) {
|
|
300
|
+
root.recheckVpn?.();
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function resyncIfNeeded(sessionId: string | null) {
|
|
305
|
+
if (!resyncSessionId || !sessionId || sessionId !== resyncSessionId) return;
|
|
306
|
+
resyncSessionId = null;
|
|
307
|
+
wsClient?.send({ type: "session_resume", sessionId });
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// A cold page / PWA relaunch restores only the stored session id, not the
|
|
311
|
+
// transcript. The server pushes a snapshot on connect only while a session is
|
|
312
|
+
// actively running; when it's idle (the common "agent finished while the phone
|
|
313
|
+
// slept" case) nothing arrives, so the client must fetch the finished
|
|
314
|
+
// session's history itself. Guarded to fire at most once per session id per
|
|
315
|
+
// connection — an empty session would otherwise re-trigger on every idle
|
|
316
|
+
// status and loop.
|
|
317
|
+
let coldResumedSessionId: string | null = null;
|
|
318
|
+
|
|
319
|
+
function markHistoryReplaced(key: ChatKey): void {
|
|
320
|
+
if (key !== null && key === resyncSessionId) resyncSessionId = null;
|
|
321
|
+
coldResumedSessionId = key;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function coldResumeIfNeeded(sessionId: string | null, messageCount: number) {
|
|
325
|
+
if (!sessionId || messageCount > 0) return;
|
|
326
|
+
if (coldResumedSessionId === sessionId) return;
|
|
327
|
+
coldResumedSessionId = sessionId;
|
|
328
|
+
wsClient?.send({ type: "session_resume", sessionId });
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function handleStatusChange(status: "connecting" | "connected" | "disconnected") {
|
|
332
|
+
root.stores.connection.getState().setWsStatus(status);
|
|
333
|
+
|
|
334
|
+
if (status === "connected") {
|
|
335
|
+
root.stores.connection.getState().noteSocketOpen();
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// A status change can be followed by a history replay that rewrites the
|
|
339
|
+
// buffer, so land whatever is still buffered before anything reads it. In a
|
|
340
|
+
// hidden tab requestAnimationFrame does not run at all, which is exactly the
|
|
341
|
+
// case where deltas could otherwise sit unapplied across a reconnect.
|
|
342
|
+
flushDeltas();
|
|
343
|
+
|
|
344
|
+
if (status === "disconnected") {
|
|
345
|
+
wasDisconnected = true;
|
|
346
|
+
// Allow a fresh cold-resume attempt after we reconnect.
|
|
347
|
+
coldResumedSessionId = null;
|
|
348
|
+
} else if (status === "connected" && wasDisconnected) {
|
|
349
|
+
wasDisconnected = false;
|
|
350
|
+
const chat = root.stores.chat.getState();
|
|
351
|
+
const active = activeChat(chat);
|
|
352
|
+
if (chat.activeSessionId && active.messages.length > 0) {
|
|
353
|
+
resyncSessionId = chat.activeSessionId;
|
|
354
|
+
// Not mid-stream: replay right away. Mid-stream: the flag holds until
|
|
355
|
+
// the running turn finishes (result or idle status).
|
|
356
|
+
if (!active.isStreaming) {
|
|
357
|
+
resyncIfNeeded(chat.activeSessionId);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
let wsClient: BrainUiClient | null = null;
|
|
364
|
+
|
|
365
|
+
function sendClientMessage(msg: ClientMessage): boolean {
|
|
366
|
+
if (!wsClient) return false;
|
|
367
|
+
if (root.stores.connection.getState().wsStatus !== "connected") return false;
|
|
368
|
+
return wsClient.send(msg);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/** Retry this root's socket, skipping any remaining backoff. */
|
|
372
|
+
function reconnectWebSocketNow(): void {
|
|
373
|
+
wsClient?.reconnectNow();
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
/** Acquire a connection lease. Several consumers may share one root safely. */
|
|
378
|
+
function connect(): () => void {
|
|
379
|
+
if (disposed) throw new Error("Cannot connect a disposed UI root");
|
|
380
|
+
owners++;
|
|
381
|
+
if (!wsClient) {
|
|
382
|
+
const connectedGeneration = ++generation;
|
|
383
|
+
const current = () => !disposed && connectedGeneration === generation;
|
|
384
|
+
root.stores.connection.getState().noteSocketOpen();
|
|
385
|
+
const client = new BrainUiClient({
|
|
386
|
+
url: root.wsUrl(),
|
|
387
|
+
handlers: { onAny: (message) => { if (current()) handleServerMessage(message); } },
|
|
388
|
+
onStatusChange: (status) => { if (current()) handleStatusChange(status); },
|
|
389
|
+
onClose: (close) => { if (current()) handleSocketClose(close); },
|
|
390
|
+
onProtocolError: (err) => {
|
|
391
|
+
if (!current()) return;
|
|
392
|
+
root.stores.connection.getState().reportError(
|
|
393
|
+
"PROTOCOL_ERROR",
|
|
394
|
+
err.frameType ? `Dropped a ${err.frameType} frame: ${err.detail}` : `Dropped an unreadable frame: ${err.detail}`,
|
|
395
|
+
);
|
|
396
|
+
},
|
|
397
|
+
});
|
|
398
|
+
wsClient = client;
|
|
399
|
+
client.connect();
|
|
400
|
+
const reconnectNow = () => { if (current()) client.reconnectNow(); };
|
|
401
|
+
const onVisible = () => { if (document.visibilityState === "visible") reconnectNow(); };
|
|
402
|
+
if (typeof window !== "undefined") window.addEventListener("online", reconnectNow);
|
|
403
|
+
if (typeof document !== "undefined") document.addEventListener("visibilitychange", onVisible);
|
|
404
|
+
removeListeners = () => {
|
|
405
|
+
if (typeof window !== "undefined") window.removeEventListener("online", reconnectNow);
|
|
406
|
+
if (typeof document !== "undefined") document.removeEventListener("visibilitychange", onVisible);
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
let released = false;
|
|
410
|
+
return () => {
|
|
411
|
+
if (released) return;
|
|
412
|
+
released = true;
|
|
413
|
+
if (--owners === 0) disconnect();
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
function disconnect() {
|
|
418
|
+
generation++;
|
|
419
|
+
removeListeners?.();
|
|
420
|
+
removeListeners = undefined;
|
|
421
|
+
const client = wsClient;
|
|
422
|
+
wsClient = null;
|
|
423
|
+
client?.close();
|
|
424
|
+
handleStatusChange("disconnected");
|
|
425
|
+
root.stores.activity.getState().setSupported(false);
|
|
426
|
+
root.stores.activity.getState().resetSubscriptions();
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
return {
|
|
430
|
+
connect,
|
|
431
|
+
send: sendClientMessage,
|
|
432
|
+
reconnectNow: reconnectWebSocketNow,
|
|
433
|
+
handleServerMessage,
|
|
434
|
+
flushChatDeltas,
|
|
435
|
+
dispose() {
|
|
436
|
+
if (disposed) return;
|
|
437
|
+
disposed = true;
|
|
438
|
+
disconnect();
|
|
439
|
+
resyncSessionId = null;
|
|
440
|
+
coldResumedSessionId = null;
|
|
441
|
+
},
|
|
442
|
+
};
|
|
443
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { defaultToolRendererRegistry, defaultAsrClientRegistry } from "@schlessera/brain-ui-sdk/client";
|
|
2
|
+
import { uiConfig, registerDevHandle } from "./config.js";
|
|
3
|
+
import { api } from "./lib/api-client.js";
|
|
4
|
+
import { createRoot } from "./root.js";
|
|
5
|
+
|
|
6
|
+
/** The single application root used by existing shells without a provider. */
|
|
7
|
+
export const defaultRoot = createRoot({ storagePrefix: "", api }, uiConfig, defaultToolRendererRegistry, defaultAsrClientRegistry);
|
|
8
|
+
|
|
9
|
+
registerDevHandle(() => {
|
|
10
|
+
if (typeof window === "undefined") return;
|
|
11
|
+
Object.assign(window, {
|
|
12
|
+
__chatStore: defaultRoot.stores.chat,
|
|
13
|
+
__graphStore: defaultRoot.stores.graph,
|
|
14
|
+
__activityStore: defaultRoot.stores.activity,
|
|
15
|
+
});
|
|
16
|
+
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
import { useUIStore } from "../stores/ui-store.js";
|
|
3
|
+
import { useMediaQuery } from "./use-media-query.js";
|
|
4
|
+
import { documentColorScheme, type ColorScheme } from "../lib/light-dark.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The scheme the page is drawing in — `light` or `dark` — for the parts of
|
|
8
|
+
* the app that read a token as a value rather than referencing it (the graph
|
|
9
|
+
* canvas, the diagram theme). Everything else lets `light-dark()` decide.
|
|
10
|
+
*
|
|
11
|
+
* Follows the store's preference and, under `system`, the OS: both are
|
|
12
|
+
* subscribed, and the answer is re-read from the root's computed
|
|
13
|
+
* `color-scheme` after `useApplyTheme` has written the attribute, so a host
|
|
14
|
+
* that sets `data-theme` itself is honoured too. The initial value is read
|
|
15
|
+
* synchronously, so the first paint after a stored preference is right.
|
|
16
|
+
*/
|
|
17
|
+
export function useColorScheme(): ColorScheme {
|
|
18
|
+
const preference = useUIStore((s) => s.theme);
|
|
19
|
+
const osDark = useMediaQuery("(prefers-color-scheme: dark)");
|
|
20
|
+
const [scheme, setScheme] = useState<ColorScheme>(() => expected(preference, osDark));
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
// `useApplyTheme` writes `data-theme` in the app root's effect, which runs
|
|
23
|
+
// after this one; the store says what it will write, and the document is
|
|
24
|
+
// re-read on the next frame in case a host wrote something else.
|
|
25
|
+
setScheme(expected(preference, osDark));
|
|
26
|
+
if (typeof requestAnimationFrame !== "function") return;
|
|
27
|
+
const frame = requestAnimationFrame(() => setScheme(documentColorScheme()));
|
|
28
|
+
return () => cancelAnimationFrame(frame);
|
|
29
|
+
}, [preference, osDark]);
|
|
30
|
+
return scheme;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function expected(preference: "system" | "light" | "dark", osDark: boolean): ColorScheme {
|
|
34
|
+
if (preference === "system") return osDark ? "dark" : "light";
|
|
35
|
+
return preference;
|
|
36
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useBrainUiRoot } from "../root-context.js";
|
|
1
2
|
import { useEffect, useRef, useState } from "react";
|
|
2
3
|
import { useFileStore } from "../stores/file-store.js";
|
|
3
4
|
import { useUIStore, type ActiveView } from "../stores/ui-store.js";
|
|
@@ -31,6 +32,7 @@ function viewForHash(hash: string): Exclude<ActiveView, "chat"> | null {
|
|
|
31
32
|
* stores.
|
|
32
33
|
*/
|
|
33
34
|
export function useHashRoutes(): void {
|
|
35
|
+
const root = useBrainUiRoot();
|
|
34
36
|
const [hash, setHash] = useState(() => window.location.hash);
|
|
35
37
|
const syncingFromHash = useRef(false);
|
|
36
38
|
const activeView = useUIStore((state) => state.activeView);
|
|
@@ -74,10 +76,10 @@ export function useHashRoutes(): void {
|
|
|
74
76
|
// below never runs and never clears the flag. It would then swallow the
|
|
75
77
|
// NEXT store-driven change, leaving the URL pointing at Activity while the
|
|
76
78
|
// app shows Chat, and a refresh would jump back.
|
|
77
|
-
if (
|
|
79
|
+
if (root.stores.ui.getState().activeView === view) return;
|
|
78
80
|
syncingFromHash.current = true;
|
|
79
81
|
setActiveView(view);
|
|
80
|
-
}, [hash, setActiveView]);
|
|
82
|
+
}, [hash, setActiveView, root]);
|
|
81
83
|
|
|
82
84
|
useEffect(() => {
|
|
83
85
|
// A hash-driven store write and this effect are flushed together. Skip
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Whether a media query matches, kept current as the window changes. Reads
|
|
5
|
+
* `false` where there is no `matchMedia` — the server, and the test DOM —
|
|
6
|
+
* so a component that branches on it renders its mobile shape there.
|
|
7
|
+
*/
|
|
8
|
+
export function useMediaQuery(query: string): boolean {
|
|
9
|
+
const [matches, setMatches] = useState(() => read(query));
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
|
|
12
|
+
let list: MediaQueryList;
|
|
13
|
+
try {
|
|
14
|
+
list = window.matchMedia(query);
|
|
15
|
+
} catch {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const update = () => setMatches(list.matches);
|
|
19
|
+
update();
|
|
20
|
+
list.addEventListener?.("change", update);
|
|
21
|
+
return () => list.removeEventListener?.("change", update);
|
|
22
|
+
}, [query]);
|
|
23
|
+
return matches;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function read(query: string): boolean {
|
|
27
|
+
try {
|
|
28
|
+
return typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia(query).matches;
|
|
29
|
+
} catch {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useBrainUiRoot } from "../root-context.js";
|
|
2
|
+
import { useCallback, useEffect, useRef } from "react";
|
|
2
3
|
import {
|
|
3
4
|
SHARE_STASH_TTL_MS,
|
|
4
5
|
type ShareIntakeResult,
|
|
@@ -9,8 +10,6 @@ import {
|
|
|
9
10
|
readShareLaunchParams,
|
|
10
11
|
type StoredShare,
|
|
11
12
|
} from "@schlessera/brain-ui-sdk/share-target";
|
|
12
|
-
import { useChatStore } from "../stores/chat-store.js";
|
|
13
|
-
import { useConnectionStore } from "../stores/connection-store.js";
|
|
14
13
|
import { useShareStore } from "../stores/share-store.js";
|
|
15
14
|
import { detectClientEnvironment } from "../lib/client-environment.js";
|
|
16
15
|
import {
|
|
@@ -23,7 +22,6 @@ import {
|
|
|
23
22
|
shareImagesToAttachments,
|
|
24
23
|
uploadShare,
|
|
25
24
|
} from "../lib/share-intake.js";
|
|
26
|
-
import { sendClientMessage } from "./use-websocket.js";
|
|
27
25
|
|
|
28
26
|
/**
|
|
29
27
|
* Pick up shares the service worker stashed, and file them once confirmed.
|
|
@@ -66,6 +64,13 @@ export function useShareIntake(): {
|
|
|
66
64
|
confirm: (record: StoredShare) => Promise<void>;
|
|
67
65
|
dismiss: (record: StoredShare) => void;
|
|
68
66
|
} {
|
|
67
|
+
const root = useBrainUiRoot();
|
|
68
|
+
const lifetime = useRef(new AbortController());
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
const controller = new AbortController();
|
|
71
|
+
lifetime.current = controller;
|
|
72
|
+
return () => controller.abort();
|
|
73
|
+
}, [root]);
|
|
69
74
|
const enqueue = useShareStore((s) => s.enqueue);
|
|
70
75
|
const remove = useShareStore((s) => s.remove);
|
|
71
76
|
const setBusy = useShareStore((s) => s.setBusy);
|
|
@@ -82,8 +87,12 @@ export function useShareIntake(): {
|
|
|
82
87
|
try {
|
|
83
88
|
for (const id of claimed) {
|
|
84
89
|
const record = await store.take(id);
|
|
90
|
+
if (disposed) {
|
|
91
|
+
if (record) await store.put(record);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
85
94
|
dropShareClaim(id);
|
|
86
|
-
if (record
|
|
95
|
+
if (record) enqueue(record);
|
|
87
96
|
}
|
|
88
97
|
|
|
89
98
|
// Orphans: a share whose landing page never ran (the app opened
|
|
@@ -91,8 +100,13 @@ export function useShareIntake(): {
|
|
|
91
100
|
// parameter was read) leaves a record nobody holds the id for. The
|
|
92
101
|
// stash is the only place it still exists.
|
|
93
102
|
for (const record of await store.list()) {
|
|
103
|
+
if (disposed) return;
|
|
94
104
|
const taken = await store.take(record.id);
|
|
95
|
-
if (
|
|
105
|
+
if (disposed) {
|
|
106
|
+
if (taken) await store.put(taken);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (taken) enqueue(taken);
|
|
96
110
|
}
|
|
97
111
|
|
|
98
112
|
// The service worker's own prune runs after a stash, so it can never
|
|
@@ -112,9 +126,10 @@ export function useShareIntake(): {
|
|
|
112
126
|
|
|
113
127
|
const confirm = useCallback(
|
|
114
128
|
async (record: StoredShare) => {
|
|
115
|
-
const
|
|
129
|
+
const signal = lifetime.current.signal;
|
|
130
|
+
const share = root.stores.share.getState();
|
|
116
131
|
if (share.busy) return;
|
|
117
|
-
if (
|
|
132
|
+
if (root.stores.connection.getState().wsStatus !== "connected") {
|
|
118
133
|
setError("Not connected yet — this will work as soon as the app reconnects.");
|
|
119
134
|
return;
|
|
120
135
|
}
|
|
@@ -123,17 +138,22 @@ export function useShareIntake(): {
|
|
|
123
138
|
setError(null);
|
|
124
139
|
setNotes([]);
|
|
125
140
|
try {
|
|
126
|
-
const outcome = await uploadShare(record);
|
|
141
|
+
const outcome = await uploadShare(record, root.request, root.apiBase(), signal);
|
|
142
|
+
if (signal.aborted) return;
|
|
127
143
|
if (!outcome.ok) {
|
|
128
144
|
setError(describeUploadError(outcome));
|
|
129
145
|
return;
|
|
130
146
|
}
|
|
131
147
|
|
|
132
148
|
const { attachments, errors } = await shareImagesToAttachments(record.files);
|
|
149
|
+
if (signal.aborted) {
|
|
150
|
+
for (const attachment of attachments) URL.revokeObjectURL(attachment.previewUrl);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
133
153
|
if (errors.length) setNotes(errors);
|
|
134
154
|
|
|
135
155
|
const prompt = buildSharePrompt(outcome.result as ShareIntakeResult);
|
|
136
|
-
const chat =
|
|
156
|
+
const chat = root.stores.chat.getState();
|
|
137
157
|
// Always start from a fresh draft: `activeSessionId` is restored from
|
|
138
158
|
// localStorage at store creation, so on a cold boot it already points
|
|
139
159
|
// at whatever session was last open.
|
|
@@ -153,7 +173,7 @@ export function useShareIntake(): {
|
|
|
153
173
|
);
|
|
154
174
|
chat.startAssistantMessage(null);
|
|
155
175
|
|
|
156
|
-
const sent =
|
|
176
|
+
const sent = root.connection.send({
|
|
157
177
|
type: "chat_message",
|
|
158
178
|
text: prompt,
|
|
159
179
|
...(attachments.length
|
|
@@ -172,7 +192,7 @@ export function useShareIntake(): {
|
|
|
172
192
|
setBusy(false);
|
|
173
193
|
}
|
|
174
194
|
},
|
|
175
|
-
[remove, setBusy, setError, setNotes]
|
|
195
|
+
[remove, setBusy, setError, setNotes, root]
|
|
176
196
|
);
|
|
177
197
|
|
|
178
198
|
const dismiss = useCallback(
|