@agent-native/core 0.79.27 → 0.80.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +34 -0
- package/corpus/core/docs/content/locales/ar-SA/skills-guide.mdx +5 -0
- package/corpus/core/docs/content/locales/ar-SA/template-design.mdx +55 -1
- package/corpus/core/docs/content/locales/de-DE/skills-guide.mdx +5 -0
- package/corpus/core/docs/content/locales/de-DE/template-design.mdx +66 -1
- package/corpus/core/docs/content/locales/es-ES/skills-guide.mdx +5 -0
- package/corpus/core/docs/content/locales/es-ES/template-design.mdx +65 -1
- package/corpus/core/docs/content/locales/fr-FR/skills-guide.mdx +5 -0
- package/corpus/core/docs/content/locales/fr-FR/template-design.mdx +64 -1
- package/corpus/core/docs/content/locales/hi-IN/skills-guide.mdx +5 -0
- package/corpus/core/docs/content/locales/hi-IN/template-design.mdx +59 -1
- package/corpus/core/docs/content/locales/ja-JP/skills-guide.mdx +5 -0
- package/corpus/core/docs/content/locales/ja-JP/template-design.mdx +62 -1
- package/corpus/core/docs/content/locales/ko-KR/skills-guide.mdx +5 -0
- package/corpus/core/docs/content/locales/ko-KR/template-design.mdx +58 -1
- package/corpus/core/docs/content/locales/pt-BR/skills-guide.mdx +5 -0
- package/corpus/core/docs/content/locales/pt-BR/template-design.mdx +62 -1
- package/corpus/core/docs/content/locales/zh-CN/skills-guide.mdx +5 -0
- package/corpus/core/docs/content/locales/zh-CN/template-design.mdx +50 -1
- package/corpus/core/docs/content/locales/zh-TW/skills-guide.mdx +5 -0
- package/corpus/core/docs/content/locales/zh-TW/template-design.mdx +52 -1
- package/corpus/core/docs/content/skills-guide.mdx +5 -0
- package/corpus/core/docs/content/template-design.mdx +59 -1
- package/corpus/core/package.json +2 -1
- package/corpus/core/src/agent/engine/index.ts +1 -0
- package/corpus/core/src/agent/engine/registry.ts +40 -0
- package/corpus/core/src/agent/production-agent.ts +6 -2
- package/corpus/core/src/application-state/handlers.ts +1 -1
- package/corpus/core/src/brand-kit/index.ts +1 -1
- package/corpus/core/src/cli/design-connect.ts +490 -0
- package/corpus/core/src/cli/index.ts +16 -0
- package/corpus/core/src/cli/skills.ts +144 -3
- package/corpus/core/src/client/AgentPanel.tsx +1 -1
- package/corpus/core/src/client/AssistantChat.tsx +43 -7
- package/corpus/core/src/client/MultiTabAssistantChat.tsx +1 -1
- package/corpus/core/src/client/agent-chat.ts +27 -3
- package/corpus/core/src/client/blocks/library/MermaidBlock.tsx +1 -1
- package/corpus/core/src/client/blocks/library/diagram.tsx +2 -2
- package/corpus/core/src/client/blocks/library/wireframe.tsx +1 -1
- package/corpus/core/src/client/chat/message-components.tsx +1 -1
- package/corpus/core/src/client/chat-model-groups.ts +1 -2
- package/corpus/core/src/client/components/LiveCursorOverlay.tsx +33 -66
- package/corpus/core/src/client/composer/TiptapComposer.tsx +99 -24
- package/corpus/core/src/client/composer/useVoiceDictation.ts +429 -260
- package/corpus/core/src/client/extensions/portable-extension.ts +1 -1
- package/corpus/core/src/client/frame-protocol.ts +17 -2
- package/corpus/core/src/client/rich-markdown-editor/RegistryBlockNode.tsx +2 -2
- package/corpus/core/src/client/sharing/ShareButton.tsx +20 -15
- package/corpus/core/src/client/use-agent-engine-configured.ts +80 -28
- package/corpus/core/src/code-agents/transcript-normalizer.ts +1 -1
- package/corpus/core/src/extensions/path.ts +1 -4
- package/corpus/core/src/integrations/a2a-continuation-processor.ts +1 -1
- package/corpus/core/src/mcp/build-server.ts +1 -1
- package/corpus/core/src/org/handlers.ts +5 -5
- package/corpus/core/src/org/plugin.ts +2 -2
- package/corpus/core/src/scripts/call-agent.ts +1 -1
- package/corpus/core/src/scripts/db/safety.ts +1 -1
- package/corpus/core/src/server/auth.ts +3 -0
- package/corpus/core/src/server/builder-design-systems.ts +308 -0
- package/corpus/core/src/server/core-routes-plugin.ts +29 -3
- package/corpus/core/src/server/index.ts +8 -0
- package/corpus/core/src/server/transcribe-voice.ts +57 -22
- package/corpus/core/src/shared/mcp-embed-headers.ts +23 -1
- package/corpus/core/src/styles/agent-native.css +60 -4
- package/corpus/core/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +10 -0
- package/corpus/core/src/usage/store.ts +2 -2
- package/corpus/core/src/vite/client.ts +2 -0
- package/corpus/core/src/voice/index.ts +18 -0
- package/corpus/core/src/voice/voice-cleanup-prompt.ts +38 -0
- package/corpus/core/src/voice/voice-context.ts +217 -0
- package/corpus/core/src/voice/voice-replacements.ts +75 -0
- package/corpus/templates/analytics/AGENTS.md +4 -3
- package/corpus/templates/analytics/actions/dashboard-mutation-api.ts +298 -15
- package/corpus/templates/analytics/actions/get-sql-dashboard.ts +9 -4
- package/corpus/templates/analytics/actions/list-session-recordings.ts +6 -1
- package/corpus/templates/analytics/actions/mutate-dashboard.ts +18 -2
- package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +12 -2
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +557 -400
- package/corpus/templates/analytics/app/global.css +0 -4
- package/corpus/templates/analytics/app/hooks/use-replay-storage-status.ts +66 -0
- package/corpus/templates/analytics/app/i18n/zh-TW.ts +39 -0
- package/corpus/templates/analytics/app/i18n-data.ts +424 -0
- package/corpus/templates/analytics/app/pages/Settings.tsx +333 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/PanelEditorDialog.tsx +2 -1
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/dashboard-layout.ts +121 -3
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +82 -34
- package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +7 -0
- package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +144 -36
- package/corpus/templates/analytics/changelog/2026-06-28-ask-tab-now-matches-the-darker-app-background.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-28-dashboard-email-reports-now-use-cleaner-light-mode-snapshots.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-28-left-sidebar-collapses-into-an-animated-icon-rail.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-28-sessions-now-show-only-signed-in-recordings-with-playable-re.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-29-analytics-agents-can-now-place-new-charts-into-requested-das.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-29-analytics-queries-now-report-failed-source-query-errors-inst.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-29-charts-can-now-be-dropped-between-full-dashboard-rows-to-cre.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-29-dashboard-email-reports-include-the-full-dashboard-image-wit.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-29-main-surface-border-reaches-the-top.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-29-session-replay-now-records-playable-sessions-in-production-a.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-29-set-up-session-replay-storage-from-settings-connect-builder-.md +6 -0
- package/corpus/templates/analytics/scripts/cleanup-empty-session-recordings.ts +102 -0
- package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +3 -3
- package/corpus/templates/analytics/server/lib/dashboard-report.ts +75 -30
- package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +5 -2
- package/corpus/templates/analytics/server/lib/real-data-actions.ts +81 -20
- package/corpus/templates/analytics/server/lib/replay-storage.ts +79 -0
- package/corpus/templates/analytics/server/lib/s3-upload-provider.ts +255 -0
- package/corpus/templates/analytics/server/lib/session-replay.ts +53 -12
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +11 -2
- package/corpus/templates/analytics/server/plugins/onboarding.ts +97 -0
- package/corpus/templates/assets/app/components/layout/Sidebar.tsx +4 -4
- package/corpus/templates/assets/changelog/2026-06-28-left-sidebar-collapse-motion-and-footer-chrome-are-quieter.md +6 -0
- package/corpus/templates/brain/app/components/layout/Layout.tsx +29 -2
- package/corpus/templates/brain/app/components/layout/Sidebar.tsx +131 -42
- package/corpus/templates/brain/app/i18n/zh-TW.ts +2 -0
- package/corpus/templates/brain/app/i18n-data.ts +20 -0
- package/corpus/templates/brain/changelog/2026-06-28-left-sidebar-collapses-into-an-animated-icon-rail.md +6 -0
- package/corpus/templates/calendar/app/components/layout/AppLayout.tsx +29 -1
- package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +417 -299
- package/corpus/templates/calendar/app/i18n/zh-TW.ts +2 -0
- package/corpus/templates/calendar/app/i18n-data.ts +20 -0
- package/corpus/templates/calendar/changelog/2026-06-28-left-sidebar-collapses-into-an-animated-icon-rail.md +6 -0
- package/corpus/templates/chat/app/components/layout/Sidebar.tsx +6 -19
- package/corpus/templates/chat/changelog/2026-06-28-left-sidebar-collapse-motion-and-footer-chrome-are-quieter.md +6 -0
- package/corpus/templates/clips/AGENTS.md +4 -0
- package/corpus/templates/clips/actions/cleanup-transcript.ts +39 -9
- package/corpus/templates/clips/actions/finalize-meeting.ts +81 -12
- package/corpus/templates/clips/actions/lib/agents-md-context.ts +4 -1
- package/corpus/templates/clips/app/components/library/library-layout.tsx +3 -3
- package/corpus/templates/clips/app/components/meetings/share-meeting-dialog.tsx +1 -1
- package/corpus/templates/clips/app/components/player/share-dialog.tsx +2 -2
- package/corpus/templates/clips/changelog/2026-06-28-left-sidebar-footer-controls-use-less-divider-chrome.md +6 -0
- package/corpus/templates/clips/desktop/src/lib/personal-vocabulary.ts +13 -8
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +24 -0
- package/corpus/templates/clips/desktop/src/lib/transcription-capture.ts +105 -5
- package/corpus/templates/clips/desktop/src/lib/voice-dictation.ts +60 -1
- package/corpus/templates/content/actions/set-image-alt-text.ts +1 -1
- package/corpus/templates/content/app/components/editor/DocumentDatabase.tsx +1 -1
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +1 -1
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +5 -5
- package/corpus/templates/content/changelog/2026-06-28-left-sidebar-collapse-motion-and-footer-chrome-are-quieter.md +6 -0
- package/corpus/templates/content/shared/document-export.ts +1 -1
- package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +8 -18
- package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +154 -0
- package/corpus/templates/design/AGENTS.md +95 -5
- package/corpus/templates/design/DESIGN.md +82 -0
- package/corpus/templates/design/DEVELOPING.md +43 -0
- package/corpus/templates/design/actions/add-localhost-screens.ts +453 -0
- package/corpus/templates/design/actions/apply-shader.ts +277 -0
- package/corpus/templates/design/actions/apply-visual-edit.ts +303 -0
- package/corpus/templates/design/actions/connect-localhost.ts +183 -0
- package/corpus/templates/design/actions/edit-design.ts +195 -42
- package/corpus/templates/design/actions/generate-design.ts +71 -1
- package/corpus/templates/design/actions/generate-screens.ts +193 -0
- package/corpus/templates/design/actions/get-code-layer-projection.ts +191 -0
- package/corpus/templates/design/actions/get-shader.ts +119 -0
- package/corpus/templates/design/actions/index-design-system-with-builder.ts +259 -0
- package/corpus/templates/design/actions/list-localhost-connections.ts +95 -0
- package/corpus/templates/design/actions/navigate.ts +55 -2
- package/corpus/templates/design/actions/update-file.ts +9 -2
- package/corpus/templates/design/actions/view-screen.ts +68 -1
- package/corpus/templates/design/agent-native.app-skill.json +10 -0
- package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +719 -0
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +2120 -103
- package/corpus/templates/design/app/components/design/DesignEditorSkeleton.tsx +74 -38
- package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +401 -0
- package/corpus/templates/design/app/components/design/EditPanel.tsx +3966 -440
- package/corpus/templates/design/app/components/design/LayersPanel.tsx +1473 -0
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +3940 -147
- package/corpus/templates/design/app/components/design/QuestionFlow.tsx +700 -13
- package/corpus/templates/design/app/components/design/TweaksPanel.tsx +105 -63
- package/corpus/templates/design/app/components/design/index.ts +0 -5
- package/corpus/templates/design/app/components/design/inspector/AlignmentMatrix.tsx +353 -0
- package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +1589 -0
- package/corpus/templates/design/app/components/design/inspector/ConstraintsWidget.tsx +511 -0
- package/corpus/templates/design/app/components/design/inspector/ExportSettingsPanel.tsx +395 -0
- package/corpus/templates/design/app/components/design/inspector/FigmaColorPicker.tsx +2010 -0
- package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +601 -0
- package/corpus/templates/design/app/components/design/inspector/ImageFillControls.tsx +242 -0
- package/corpus/templates/design/app/components/design/inspector/SHADER_INTEGRATION.md +420 -0
- package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +238 -0
- package/corpus/templates/design/app/components/design/inspector/ShaderControls.stories.tsx +71 -0
- package/corpus/templates/design/app/components/design/inspector/ShaderControls.tsx +575 -0
- package/corpus/templates/design/app/components/design/inspector/ShaderEffectRow.tsx +106 -0
- package/corpus/templates/design/app/components/design/inspector/ShaderFillsPanel.tsx +423 -0
- package/corpus/templates/design/app/components/design/inspector/figma-icons.tsx +741 -0
- package/corpus/templates/design/app/components/design/inspector/index.ts +87 -0
- package/corpus/templates/design/app/components/design/inspector/scrub-input-utils.ts +201 -0
- package/corpus/templates/design/app/components/design/types.ts +23 -0
- package/corpus/templates/design/app/components/layout/Layout.tsx +24 -13
- package/corpus/templates/design/app/components/layout/Sidebar.tsx +4 -4
- package/corpus/templates/design/app/components/visual-editor/CanvasCommentPins.tsx +474 -36
- package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +22 -5
- package/corpus/templates/design/app/components/visual-editor/SaveStatusIndicator.tsx +24 -11
- package/corpus/templates/design/app/global.css +149 -5
- package/corpus/templates/design/app/hooks/use-agent-generating.ts +4 -0
- package/corpus/templates/design/app/hooks/use-navigation-state.ts +130 -8
- package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +329 -0
- package/corpus/templates/design/app/i18n/zh-TW.ts +277 -0
- package/corpus/templates/design/app/i18n-data.ts +3028 -1
- package/corpus/templates/design/app/pages/DesignEditor.tsx +5831 -904
- package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +57 -221
- package/corpus/templates/design/changelog/2026-06-28-annotate-mode-now-combines-drawing-and-comment-pins-in-one-c.md +6 -0
- package/corpus/templates/design/changelog/2026-06-28-design-editor-panel-borders-are-cleaner-so-the-canvas-edge-s.md +6 -0
- package/corpus/templates/design/changelog/2026-06-28-drawing-prompts-now-reliably-open-and-submit-to-the-design-a.md +6 -0
- package/corpus/templates/design/changelog/2026-06-28-editor-app-menus-now-use-an-outline-agent-native-mark-with-b.md +6 -0
- package/corpus/templates/design/changelog/2026-06-28-figma-style-code-design-mode.md +6 -0
- package/corpus/templates/design/changelog/2026-06-28-layer-selection-now-updates-chat-context-without-opening-the.md +6 -0
- package/corpus/templates/design/changelog/2026-06-28-layer-selections-now-preserve-hidden-and-locked-canvas-state.md +6 -0
- package/corpus/templates/design/changelog/2026-06-28-left-sidebar-collapse-motion-and-footer-chrome-are-quieter.md +6 -0
- package/corpus/templates/design/changelog/2026-06-28-live-cursors-now-keep-names-beside-a-cleaner-pointer-marker.md +6 -0
- package/corpus/templates/design/changelog/2026-06-28-mode-switching-now-lives-in-a-compact-bottom-right-icon-dock.md +6 -0
- package/corpus/templates/design/changelog/2026-06-28-screen-overview-now-handles-zooming-drawing-opening-and-dele.md +6 -0
- package/corpus/templates/design/changelog/2026-06-28-screen-overview-now-selects-screens-on-click-and-opens-them-.md +6 -0
- package/corpus/templates/design/changelog/2026-06-28-screen-overview-zooms-into-the-active-screen-with-smoother-e.md +6 -0
- package/corpus/templates/design/changelog/2026-06-28-selected-element-chips-in-chat-are-shorter-and-easier-to-sca.md +6 -0
- package/corpus/templates/design/changelog/2026-06-28-the-design-editor-toolbar-is-more-compact-and-the-app-menu-m.md +6 -0
- package/corpus/templates/design/changelog/2026-06-28-tweaks-now-live-at-the-top-of-the-design-sidebar-instead-of-.md +6 -0
- package/corpus/templates/design/changelog/2026-06-28-tweaks-now-live-in-the-right-inspector-beside-the-design-tab.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-active-sidebar-rows-use-soft-highlights.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-agents-can-now-open-screen-overview-focus-screens-and-add.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-canvas-selection-no-longer-flashes-broken-prototype-html-whe.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-command-menu-selections-are-clearer-in-dark-mode.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-design-agents-now-edit-broad-copy-changes-more-reliably.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-design-editor-extensions-now-run-inline-beside-the-artboard.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-design-mode-top-chrome-now-groups-collaborators-preview-shar.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-element-selection-now-stays-on-the-intended-layer-after-sele.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-screen-overview-full-view-controls-now-sit-above-frames-inst.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-screen-overview-tools-can-draw-shapes-create-screens-and-kee.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-sidebars-use-darker-chrome-and-chat-warns-immediately.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-the-pen-tool-now-creates-anchor-based-bezier-vectors-with-ha.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-visual-edit-can-open-localhost-routes-as-url-backed-screens.md +6 -0
- package/corpus/templates/design/package.json +1 -0
- package/corpus/templates/design/server/db/schema.ts +26 -0
- package/corpus/templates/design/server/handlers/import-figma-system.ts +38 -85
- package/corpus/templates/design/server/lib/design-snapshot.ts +9 -1
- package/corpus/templates/design/server/plugins/agent-chat.ts +3 -0
- package/corpus/templates/design/shared/api.ts +27 -0
- package/corpus/templates/design/shared/canvas-frames.ts +103 -0
- package/corpus/templates/design/shared/canvas-math.ts +1058 -0
- package/corpus/templates/design/shared/code-layer.ts +2412 -0
- package/corpus/templates/design/shared/color-utils.ts +180 -0
- package/corpus/templates/design/shared/generation-session.ts +61 -0
- package/corpus/templates/design/shared/html-content.ts +21 -0
- package/corpus/templates/design/shared/pen-path.ts +207 -0
- package/corpus/templates/design/shared/shader-presets.ts +782 -0
- package/corpus/templates/design/shared/shader-safety.ts +137 -0
- package/corpus/templates/design/shared/source-mode.ts +237 -0
- package/corpus/templates/forms/app/components/layout/Sidebar.tsx +149 -6
- package/corpus/templates/forms/app/i18n/ar-SA.ts +2 -0
- package/corpus/templates/forms/app/i18n/de-DE.ts +2 -0
- package/corpus/templates/forms/app/i18n/en-US.ts +2 -0
- package/corpus/templates/forms/app/i18n/es-ES.ts +2 -0
- package/corpus/templates/forms/app/i18n/fr-FR.ts +2 -0
- package/corpus/templates/forms/app/i18n/hi-IN.ts +2 -0
- package/corpus/templates/forms/app/i18n/ja-JP.ts +2 -0
- package/corpus/templates/forms/app/i18n/ko-KR.ts +2 -0
- package/corpus/templates/forms/app/i18n/pt-BR.ts +2 -0
- package/corpus/templates/forms/app/i18n/zh-CN.ts +2 -0
- package/corpus/templates/forms/app/i18n/zh-TW.ts +2 -0
- package/corpus/templates/forms/changelog/2026-06-28-left-sidebar-collapses-into-an-animated-icon-rail.md +6 -0
- package/corpus/templates/macros/app/components/layout/AppLayout.tsx +2 -2
- package/corpus/templates/macros/changelog/2026-06-28-left-sidebar-footer-controls-use-less-divider-chrome.md +6 -0
- package/corpus/templates/mail/app/components/layout/AppLayout.tsx +408 -247
- package/corpus/templates/mail/app/i18n/ar-SA.ts +4 -0
- package/corpus/templates/mail/app/i18n/de-DE.ts +4 -0
- package/corpus/templates/mail/app/i18n/en-US.ts +4 -0
- package/corpus/templates/mail/app/i18n/es-ES.ts +4 -0
- package/corpus/templates/mail/app/i18n/fr-FR.ts +4 -0
- package/corpus/templates/mail/app/i18n/hi-IN.ts +4 -0
- package/corpus/templates/mail/app/i18n/ja-JP.ts +4 -0
- package/corpus/templates/mail/app/i18n/ko-KR.ts +4 -0
- package/corpus/templates/mail/app/i18n/pt-BR.ts +4 -0
- package/corpus/templates/mail/app/i18n/zh-CN.ts +4 -0
- package/corpus/templates/mail/app/i18n/zh-TW.ts +4 -0
- package/corpus/templates/mail/app/lib/utils.ts +2 -2
- package/corpus/templates/mail/changelog/2026-06-28-pinned-sidebar-collapses-into-an-animated-icon-rail.md +6 -0
- package/corpus/templates/plan/app/components/layout/Sidebar.tsx +6 -8
- package/corpus/templates/plan/app/components/plan/wireframe/Wireframe.tsx +1 -1
- package/corpus/templates/plan/app/global.css +61 -78
- package/corpus/templates/plan/app/pages/PlansPage.tsx +8 -0
- package/corpus/templates/plan/changelog/2026-06-28-left-sidebar-collapse-motion-and-footer-chrome-are-quieter.md +6 -0
- package/corpus/templates/plan/changelog/2026-06-28-plans-use-the-shared-neutral-light-and-dark-theme.md +6 -0
- package/corpus/templates/plan/changelog/2026-06-28-sending-open-feedback-from-a-plan-now-opens-the-inline-plan-.md +6 -0
- package/corpus/templates/slides/.agents/skills/design-systems/SKILL.md +9 -10
- package/corpus/templates/slides/AGENTS.md +3 -2
- package/corpus/templates/slides/actions/import-file.ts +26 -17
- package/corpus/templates/slides/actions/index-design-system-with-builder.ts +259 -0
- package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +35 -167
- package/corpus/templates/slides/app/components/design-system/fig-import-response.ts +9 -13
- package/corpus/templates/slides/app/components/layout/Sidebar.tsx +5 -5
- package/corpus/templates/slides/app/context/DeckContext.tsx +1 -1
- package/corpus/templates/slides/app/i18n/ar-SA.ts +9 -37
- package/corpus/templates/slides/app/i18n/de-DE.ts +10 -25
- package/corpus/templates/slides/app/i18n/en-US.ts +9 -26
- package/corpus/templates/slides/app/i18n/es-ES.ts +10 -28
- package/corpus/templates/slides/app/i18n/fr-FR.ts +10 -28
- package/corpus/templates/slides/app/i18n/hi-IN.ts +9 -26
- package/corpus/templates/slides/app/i18n/ja-JP.ts +10 -22
- package/corpus/templates/slides/app/i18n/ko-KR.ts +9 -23
- package/corpus/templates/slides/app/i18n/pt-BR.ts +10 -28
- package/corpus/templates/slides/app/i18n/zh-CN.ts +9 -22
- package/corpus/templates/slides/app/i18n/zh-TW.ts +9 -22
- package/corpus/templates/slides/changelog/2026-06-28-left-sidebar-collapse-motion-and-footer-chrome-are-quieter.md +6 -0
- package/corpus/templates/slides/server/handlers/import-figma-system.ts +44 -12
- package/corpus/templates/slides/server/lib/chat-attachments.ts +1 -1
- package/corpus/templates/videos/app/components/NewCompositionPopover.tsx +1 -1
- package/corpus/templates/videos/app/components/Sidebar.tsx +2 -2
- package/corpus/templates/videos/app/components/layout/Layout.tsx +29 -2
- package/corpus/templates/videos/app/components/layout/NavSidebar.tsx +113 -30
- package/corpus/templates/videos/app/i18n/ar-SA.ts +2 -0
- package/corpus/templates/videos/app/i18n/de-DE.ts +2 -0
- package/corpus/templates/videos/app/i18n/en-US.ts +2 -0
- package/corpus/templates/videos/app/i18n/es-ES.ts +2 -0
- package/corpus/templates/videos/app/i18n/fr-FR.ts +2 -0
- package/corpus/templates/videos/app/i18n/hi-IN.ts +2 -0
- package/corpus/templates/videos/app/i18n/ja-JP.ts +2 -0
- package/corpus/templates/videos/app/i18n/ko-KR.ts +2 -0
- package/corpus/templates/videos/app/i18n/pt-BR.ts +2 -0
- package/corpus/templates/videos/app/i18n/zh-CN.ts +2 -0
- package/corpus/templates/videos/app/i18n/zh-TW.ts +2 -0
- package/corpus/templates/videos/changelog/2026-06-28-left-sidebar-collapses-into-an-animated-icon-rail.md +6 -0
- package/dist/agent/engine/index.d.ts +1 -1
- package/dist/agent/engine/index.d.ts.map +1 -1
- package/dist/agent/engine/index.js +1 -1
- package/dist/agent/engine/index.js.map +1 -1
- package/dist/agent/engine/registry.d.ts +9 -0
- package/dist/agent/engine/registry.d.ts.map +1 -1
- package/dist/agent/engine/registry.js +37 -0
- package/dist/agent/engine/registry.js.map +1 -1
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +4 -3
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/application-state/handlers.js +1 -1
- package/dist/application-state/handlers.js.map +1 -1
- package/dist/brand-kit/index.d.ts +1 -1
- package/dist/brand-kit/index.js +1 -1
- package/dist/brand-kit/index.js.map +1 -1
- package/dist/cli/design-connect.d.ts +52 -0
- package/dist/cli/design-connect.d.ts.map +1 -0
- package/dist/cli/design-connect.js +409 -0
- package/dist/cli/design-connect.js.map +1 -0
- package/dist/cli/index.js +15 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/skills.d.ts +3 -0
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +139 -3
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/AgentPanel.js +1 -1
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +38 -8
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/MultiTabAssistantChat.js +1 -1
- package/dist/client/MultiTabAssistantChat.js.map +1 -1
- package/dist/client/agent-chat.d.ts +6 -0
- package/dist/client/agent-chat.d.ts.map +1 -1
- package/dist/client/agent-chat.js +17 -3
- package/dist/client/agent-chat.js.map +1 -1
- package/dist/client/blocks/library/MermaidBlock.js +1 -1
- package/dist/client/blocks/library/MermaidBlock.js.map +1 -1
- package/dist/client/blocks/library/diagram.js +1 -1
- package/dist/client/blocks/library/diagram.js.map +1 -1
- package/dist/client/blocks/library/wireframe.js +1 -1
- package/dist/client/blocks/library/wireframe.js.map +1 -1
- package/dist/client/chat/message-components.js +1 -1
- package/dist/client/chat/message-components.js.map +1 -1
- package/dist/client/chat-model-groups.d.ts.map +1 -1
- package/dist/client/chat-model-groups.js +1 -2
- package/dist/client/chat-model-groups.js.map +1 -1
- package/dist/client/components/LiveCursorOverlay.d.ts +1 -2
- package/dist/client/components/LiveCursorOverlay.d.ts.map +1 -1
- package/dist/client/components/LiveCursorOverlay.js +19 -40
- package/dist/client/components/LiveCursorOverlay.js.map +1 -1
- package/dist/client/composer/TiptapComposer.d.ts +3 -1
- package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/client/composer/TiptapComposer.js +86 -24
- package/dist/client/composer/TiptapComposer.js.map +1 -1
- package/dist/client/composer/useVoiceDictation.d.ts +9 -0
- package/dist/client/composer/useVoiceDictation.d.ts.map +1 -1
- package/dist/client/composer/useVoiceDictation.js +154 -32
- package/dist/client/composer/useVoiceDictation.js.map +1 -1
- package/dist/client/extensions/portable-extension.d.ts +1 -1
- package/dist/client/extensions/portable-extension.d.ts.map +1 -1
- package/dist/client/extensions/portable-extension.js.map +1 -1
- package/dist/client/frame-protocol.d.ts +17 -2
- package/dist/client/frame-protocol.d.ts.map +1 -1
- package/dist/client/frame-protocol.js.map +1 -1
- package/dist/client/rich-markdown-editor/RegistryBlockNode.js +2 -2
- package/dist/client/rich-markdown-editor/RegistryBlockNode.js.map +1 -1
- package/dist/client/sharing/ShareButton.d.ts +2 -0
- package/dist/client/sharing/ShareButton.d.ts.map +1 -1
- package/dist/client/sharing/ShareButton.js +7 -6
- package/dist/client/sharing/ShareButton.js.map +1 -1
- package/dist/client/use-agent-engine-configured.d.ts +5 -0
- package/dist/client/use-agent-engine-configured.d.ts.map +1 -1
- package/dist/client/use-agent-engine-configured.js +51 -25
- package/dist/client/use-agent-engine-configured.js.map +1 -1
- package/dist/code-agents/transcript-normalizer.js +1 -1
- package/dist/code-agents/transcript-normalizer.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/extensions/path.d.ts +1 -1
- package/dist/extensions/path.d.ts.map +1 -1
- package/dist/extensions/path.js.map +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +2 -2
- package/dist/integrations/a2a-continuation-processor.js +1 -1
- package/dist/integrations/a2a-continuation-processor.js.map +1 -1
- package/dist/mcp/build-server.d.ts +1 -1
- package/dist/mcp/build-server.d.ts.map +1 -1
- package/dist/mcp/build-server.js.map +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +5 -5
- package/dist/org/handlers.js +5 -5
- package/dist/org/handlers.js.map +1 -1
- package/dist/org/plugin.js +2 -2
- package/dist/org/plugin.js.map +1 -1
- package/dist/scripts/call-agent.js +1 -1
- package/dist/scripts/call-agent.js.map +1 -1
- package/dist/scripts/db/safety.js +1 -1
- package/dist/scripts/db/safety.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +3 -0
- package/dist/server/auth.js.map +1 -1
- package/dist/server/builder-design-systems.d.ts +28 -0
- package/dist/server/builder-design-systems.d.ts.map +1 -0
- package/dist/server/builder-design-systems.js +217 -0
- package/dist/server/builder-design-systems.js.map +1 -0
- package/dist/server/core-routes-plugin.d.ts +1 -0
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +20 -3
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1 -0
- package/dist/server/index.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/server/transcribe-voice.d.ts.map +1 -1
- package/dist/server/transcribe-voice.js +40 -24
- package/dist/server/transcribe-voice.js.map +1 -1
- package/dist/shared/mcp-embed-headers.d.ts +1 -0
- package/dist/shared/mcp-embed-headers.d.ts.map +1 -1
- package/dist/shared/mcp-embed-headers.js +20 -1
- package/dist/shared/mcp-embed-headers.js.map +1 -1
- package/dist/styles/agent-native.css +60 -4
- package/dist/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +10 -0
- package/dist/usage/store.js +2 -2
- package/dist/usage/store.js.map +1 -1
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +2 -0
- package/dist/vite/client.js.map +1 -1
- package/dist/voice/index.d.ts +5 -0
- package/dist/voice/index.d.ts.map +1 -0
- package/dist/voice/index.js +4 -0
- package/dist/voice/index.js.map +1 -0
- package/dist/voice/voice-cleanup-prompt.d.ts +6 -0
- package/dist/voice/voice-cleanup-prompt.d.ts.map +1 -0
- package/dist/voice/voice-cleanup-prompt.js +26 -0
- package/dist/voice/voice-cleanup-prompt.js.map +1 -0
- package/dist/voice/voice-context.d.ts +25 -0
- package/dist/voice/voice-context.d.ts.map +1 -0
- package/dist/voice/voice-context.js +156 -0
- package/dist/voice/voice-context.js.map +1 -0
- package/dist/voice/voice-replacements.d.ts +4 -0
- package/dist/voice/voice-replacements.d.ts.map +1 -0
- package/dist/voice/voice-replacements.js +52 -0
- package/dist/voice/voice-replacements.js.map +1 -0
- package/docs/content/locales/ar-SA/skills-guide.mdx +5 -0
- package/docs/content/locales/ar-SA/template-design.mdx +55 -1
- package/docs/content/locales/de-DE/skills-guide.mdx +5 -0
- package/docs/content/locales/de-DE/template-design.mdx +66 -1
- package/docs/content/locales/es-ES/skills-guide.mdx +5 -0
- package/docs/content/locales/es-ES/template-design.mdx +65 -1
- package/docs/content/locales/fr-FR/skills-guide.mdx +5 -0
- package/docs/content/locales/fr-FR/template-design.mdx +64 -1
- package/docs/content/locales/hi-IN/skills-guide.mdx +5 -0
- package/docs/content/locales/hi-IN/template-design.mdx +59 -1
- package/docs/content/locales/ja-JP/skills-guide.mdx +5 -0
- package/docs/content/locales/ja-JP/template-design.mdx +62 -1
- package/docs/content/locales/ko-KR/skills-guide.mdx +5 -0
- package/docs/content/locales/ko-KR/template-design.mdx +58 -1
- package/docs/content/locales/pt-BR/skills-guide.mdx +5 -0
- package/docs/content/locales/pt-BR/template-design.mdx +62 -1
- package/docs/content/locales/zh-CN/skills-guide.mdx +5 -0
- package/docs/content/locales/zh-CN/template-design.mdx +50 -1
- package/docs/content/locales/zh-TW/skills-guide.mdx +5 -0
- package/docs/content/locales/zh-TW/template-design.mdx +52 -1
- package/docs/content/skills-guide.mdx +5 -0
- package/docs/content/template-design.mdx +59 -1
- package/package.json +2 -1
- package/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +10 -0
- package/corpus/templates/design/actions/import-figma-file.ts +0 -166
- package/corpus/templates/design/app/components/design/DesignToolbar.tsx +0 -339
- package/corpus/templates/design/app/components/design/PresentMode.tsx +0 -68
- package/corpus/templates/design/app/components/design/ViewportTabs.tsx +0 -149
- package/corpus/templates/design/app/components/design/ZoomControls.tsx +0 -157
- package/corpus/templates/slides/server/lib/fig-design-system.ts +0 -110
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* S3-compatible file upload provider.
|
|
3
|
+
*
|
|
4
|
+
* Works with AWS S3, Cloudflare R2, DigitalOcean Spaces, MinIO, Backblaze B2,
|
|
5
|
+
* and any other S3-compatible object storage. Uses SigV4 signing via Web Crypto
|
|
6
|
+
* — no SDK dependency.
|
|
7
|
+
*
|
|
8
|
+
* Env vars (S3_* or R2_* prefix, first found wins):
|
|
9
|
+
* S3_BUCKET | R2_BUCKET — required
|
|
10
|
+
* S3_ACCESS_KEY_ID | R2_ACCESS_KEY_ID — required
|
|
11
|
+
* S3_SECRET_ACCESS_KEY | R2_SECRET_ACCESS_KEY — required
|
|
12
|
+
* S3_ENDPOINT | R2_ENDPOINT — required (e.g. https://s3.us-east-1.amazonaws.com
|
|
13
|
+
* or https://<acct>.r2.cloudflarestorage.com)
|
|
14
|
+
* S3_REGION | R2_REGION — optional, default "auto"
|
|
15
|
+
* S3_PUBLIC_BASE_URL | R2_PUBLIC_BASE_URL — optional (for public read URLs)
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import type { FileUploadProvider } from "@agent-native/core/file-upload";
|
|
19
|
+
import { resolveSecret } from "@agent-native/core/server";
|
|
20
|
+
|
|
21
|
+
interface S3Config {
|
|
22
|
+
region: string;
|
|
23
|
+
bucket: string;
|
|
24
|
+
accessKeyId: string;
|
|
25
|
+
secretAccessKey: string;
|
|
26
|
+
endpoint: string;
|
|
27
|
+
publicBaseUrl: string | null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function cleanValue(value: string | null | undefined): string | undefined {
|
|
31
|
+
const cleaned = value?.trim();
|
|
32
|
+
return cleaned ? cleaned : undefined;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function buildS3Config(values: {
|
|
36
|
+
bucket?: string;
|
|
37
|
+
accessKeyId?: string;
|
|
38
|
+
secretAccessKey?: string;
|
|
39
|
+
endpoint?: string;
|
|
40
|
+
region?: string;
|
|
41
|
+
publicBaseUrl?: string;
|
|
42
|
+
}): S3Config | null {
|
|
43
|
+
const bucket = cleanValue(values.bucket);
|
|
44
|
+
const accessKeyId = cleanValue(values.accessKeyId);
|
|
45
|
+
const secretAccessKey = cleanValue(values.secretAccessKey);
|
|
46
|
+
const endpoint = cleanValue(values.endpoint);
|
|
47
|
+
if (!bucket || !accessKeyId || !secretAccessKey || !endpoint) return null;
|
|
48
|
+
return {
|
|
49
|
+
region: cleanValue(values.region) ?? "auto",
|
|
50
|
+
bucket,
|
|
51
|
+
accessKeyId,
|
|
52
|
+
secretAccessKey,
|
|
53
|
+
endpoint: endpoint.replace(/\/+$/, ""),
|
|
54
|
+
publicBaseUrl:
|
|
55
|
+
cleanValue(values.publicBaseUrl)?.replace(/\/+$/, "") ?? null,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function readS3EnvConfig(): S3Config | null {
|
|
60
|
+
const env = process.env;
|
|
61
|
+
return buildS3Config({
|
|
62
|
+
bucket: env.S3_BUCKET || env.R2_BUCKET,
|
|
63
|
+
accessKeyId: env.S3_ACCESS_KEY_ID || env.R2_ACCESS_KEY_ID,
|
|
64
|
+
secretAccessKey: env.S3_SECRET_ACCESS_KEY || env.R2_SECRET_ACCESS_KEY,
|
|
65
|
+
endpoint: env.S3_ENDPOINT || env.R2_ENDPOINT,
|
|
66
|
+
region: env.S3_REGION || env.R2_REGION,
|
|
67
|
+
publicBaseUrl: env.S3_PUBLIC_BASE_URL || env.R2_PUBLIC_BASE_URL,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function resolveS3Secret(primary: string, fallback: string) {
|
|
72
|
+
return (
|
|
73
|
+
cleanValue(await resolveSecret(primary).catch(() => null)) ??
|
|
74
|
+
cleanValue(await resolveSecret(fallback).catch(() => null))
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function readS3Config(): Promise<S3Config | null> {
|
|
79
|
+
return buildS3Config({
|
|
80
|
+
bucket: await resolveS3Secret("S3_BUCKET", "R2_BUCKET"),
|
|
81
|
+
accessKeyId: await resolveS3Secret("S3_ACCESS_KEY_ID", "R2_ACCESS_KEY_ID"),
|
|
82
|
+
secretAccessKey: await resolveS3Secret(
|
|
83
|
+
"S3_SECRET_ACCESS_KEY",
|
|
84
|
+
"R2_SECRET_ACCESS_KEY",
|
|
85
|
+
),
|
|
86
|
+
endpoint: await resolveS3Secret("S3_ENDPOINT", "R2_ENDPOINT"),
|
|
87
|
+
region: await resolveS3Secret("S3_REGION", "R2_REGION"),
|
|
88
|
+
publicBaseUrl: await resolveS3Secret(
|
|
89
|
+
"S3_PUBLIC_BASE_URL",
|
|
90
|
+
"R2_PUBLIC_BASE_URL",
|
|
91
|
+
),
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// ── SigV4 helpers (Web Crypto, no SDK) ────────────────────────────────
|
|
96
|
+
|
|
97
|
+
async function hmac(key: ArrayBuffer, msg: string): Promise<ArrayBuffer> {
|
|
98
|
+
const k = await crypto.subtle.importKey(
|
|
99
|
+
"raw",
|
|
100
|
+
key,
|
|
101
|
+
{ name: "HMAC", hash: "SHA-256" },
|
|
102
|
+
false,
|
|
103
|
+
["sign"],
|
|
104
|
+
);
|
|
105
|
+
return crypto.subtle.sign("HMAC", k, new TextEncoder().encode(msg));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async function sha256(data: Uint8Array): Promise<string> {
|
|
109
|
+
const ab = new ArrayBuffer(data.byteLength);
|
|
110
|
+
new Uint8Array(ab).set(data);
|
|
111
|
+
const buf = await crypto.subtle.digest("SHA-256", ab);
|
|
112
|
+
return toHex(buf);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function toHex(buf: ArrayBuffer): string {
|
|
116
|
+
const bytes = new Uint8Array(buf);
|
|
117
|
+
let out = "";
|
|
118
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
119
|
+
out += bytes[i].toString(16).padStart(2, "0");
|
|
120
|
+
}
|
|
121
|
+
return out;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async function deriveSigningKey(
|
|
125
|
+
secret: string,
|
|
126
|
+
dateStamp: string,
|
|
127
|
+
region: string,
|
|
128
|
+
): Promise<ArrayBuffer> {
|
|
129
|
+
const kSecret = new TextEncoder().encode(`AWS4${secret}`);
|
|
130
|
+
const kDate = await hmac(kSecret.buffer as ArrayBuffer, dateStamp);
|
|
131
|
+
const kRegion = await hmac(kDate, region);
|
|
132
|
+
const kService = await hmac(kRegion, "s3");
|
|
133
|
+
return hmac(kService, "aws4_request");
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function rfc3986(str: string): string {
|
|
137
|
+
return encodeURIComponent(str).replace(
|
|
138
|
+
/[!'()*]/g,
|
|
139
|
+
(c) => "%" + c.charCodeAt(0).toString(16).toUpperCase(),
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async function putObject(
|
|
144
|
+
cfg: S3Config,
|
|
145
|
+
key: string,
|
|
146
|
+
body: Uint8Array,
|
|
147
|
+
contentType: string,
|
|
148
|
+
): Promise<string> {
|
|
149
|
+
const now = new Date();
|
|
150
|
+
const amzDate =
|
|
151
|
+
now
|
|
152
|
+
.toISOString()
|
|
153
|
+
.replace(/[:-]|\.\d{3}/g, "")
|
|
154
|
+
.slice(0, 15) + "Z";
|
|
155
|
+
const dateStamp = amzDate.slice(0, 8);
|
|
156
|
+
const credentialScope = `${dateStamp}/${cfg.region}/s3/aws4_request`;
|
|
157
|
+
|
|
158
|
+
const hostUrl = new URL(cfg.endpoint);
|
|
159
|
+
const host = hostUrl.host;
|
|
160
|
+
const canonicalUri = `/${cfg.bucket}/${key.split("/").map(rfc3986).join("/")}`;
|
|
161
|
+
|
|
162
|
+
const payloadHash = await sha256(body);
|
|
163
|
+
|
|
164
|
+
const headers: Record<string, string> = {
|
|
165
|
+
host,
|
|
166
|
+
"content-type": contentType,
|
|
167
|
+
"x-amz-content-sha256": payloadHash,
|
|
168
|
+
"x-amz-date": amzDate,
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
const signedHeaderKeys = Object.keys(headers).sort();
|
|
172
|
+
const signedHeaders = signedHeaderKeys.join(";");
|
|
173
|
+
const canonicalHeaders =
|
|
174
|
+
signedHeaderKeys.map((k) => `${k}:${headers[k]}`).join("\n") + "\n";
|
|
175
|
+
|
|
176
|
+
const canonicalRequest = [
|
|
177
|
+
"PUT",
|
|
178
|
+
canonicalUri,
|
|
179
|
+
"", // no query string
|
|
180
|
+
canonicalHeaders,
|
|
181
|
+
signedHeaders,
|
|
182
|
+
payloadHash,
|
|
183
|
+
].join("\n");
|
|
184
|
+
|
|
185
|
+
const crHash = await sha256(new TextEncoder().encode(canonicalRequest));
|
|
186
|
+
const stringToSign = [
|
|
187
|
+
"AWS4-HMAC-SHA256",
|
|
188
|
+
amzDate,
|
|
189
|
+
credentialScope,
|
|
190
|
+
crHash,
|
|
191
|
+
].join("\n");
|
|
192
|
+
|
|
193
|
+
const signingKey = await deriveSigningKey(
|
|
194
|
+
cfg.secretAccessKey,
|
|
195
|
+
dateStamp,
|
|
196
|
+
cfg.region,
|
|
197
|
+
);
|
|
198
|
+
const signature = toHex(await hmac(signingKey, stringToSign));
|
|
199
|
+
|
|
200
|
+
const authorization =
|
|
201
|
+
`AWS4-HMAC-SHA256 Credential=${cfg.accessKeyId}/${credentialScope}, ` +
|
|
202
|
+
`SignedHeaders=${signedHeaders}, Signature=${signature}`;
|
|
203
|
+
|
|
204
|
+
const url = `${cfg.endpoint}${canonicalUri}`;
|
|
205
|
+
const res = await fetch(url, {
|
|
206
|
+
method: "PUT",
|
|
207
|
+
headers: {
|
|
208
|
+
...headers,
|
|
209
|
+
Authorization: authorization,
|
|
210
|
+
"Content-Length": String(body.byteLength),
|
|
211
|
+
},
|
|
212
|
+
body: body.buffer.slice(
|
|
213
|
+
body.byteOffset,
|
|
214
|
+
body.byteOffset + body.byteLength,
|
|
215
|
+
) as BodyInit,
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
if (!res.ok) {
|
|
219
|
+
const text = await res.text().catch(() => "");
|
|
220
|
+
throw new Error(
|
|
221
|
+
`S3 PutObject failed (${res.status}): ${text || res.statusText}`,
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return cfg.publicBaseUrl
|
|
226
|
+
? `${cfg.publicBaseUrl}/${key}`
|
|
227
|
+
: `${cfg.endpoint}/${cfg.bucket}/${key}`;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// ── Provider ──────────────────────────────────────────────────────────
|
|
231
|
+
|
|
232
|
+
export const s3FileUploadProvider: FileUploadProvider = {
|
|
233
|
+
id: "s3",
|
|
234
|
+
name: "S3-compatible storage",
|
|
235
|
+
isConfigured: () => readS3EnvConfig() !== null,
|
|
236
|
+
isConfiguredForRequest: async () => (await readS3Config()) !== null,
|
|
237
|
+
upload: async ({ data, filename, mimeType }) => {
|
|
238
|
+
const cfg = await readS3Config();
|
|
239
|
+
if (!cfg) throw new Error("S3 credentials are not configured");
|
|
240
|
+
|
|
241
|
+
const ext = filename?.split(".").pop() ?? "bin";
|
|
242
|
+
const stamp = Date.now();
|
|
243
|
+
const rand = Math.random().toString(36).slice(2, 10);
|
|
244
|
+
const objectKey = `replays/${stamp}-${rand}.${ext}`;
|
|
245
|
+
const contentType = mimeType || "application/octet-stream";
|
|
246
|
+
|
|
247
|
+
const bytes =
|
|
248
|
+
data instanceof Uint8Array
|
|
249
|
+
? data
|
|
250
|
+
: new Uint8Array(data as unknown as ArrayBuffer);
|
|
251
|
+
|
|
252
|
+
const publicUrl = await putObject(cfg, objectKey, bytes, contentType);
|
|
253
|
+
return { url: publicUrl, provider: "s3" };
|
|
254
|
+
},
|
|
255
|
+
};
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
readPrivateBlob,
|
|
9
9
|
type PrivateBlobHandle,
|
|
10
10
|
} from "@agent-native/core/private-blob";
|
|
11
|
-
import { recordChange } from "@agent-native/core/server";
|
|
11
|
+
import { recordChange, runWithRequestContext } from "@agent-native/core/server";
|
|
12
12
|
import {
|
|
13
13
|
accessFilter,
|
|
14
14
|
resolveAccess,
|
|
@@ -988,11 +988,21 @@ function rowToSessionRecordingSummary(
|
|
|
988
988
|
}
|
|
989
989
|
|
|
990
990
|
function hasVisibleSessionRecordingIdentity(row: any): boolean {
|
|
991
|
-
|
|
991
|
+
// /sessions intentionally lists signed-in, email-backed recordings only (see
|
|
992
|
+
// analytics CLAUDE.md + the "rejects anonymous recordings" spec). Keep this in
|
|
993
|
+
// sync with replayVisibleIdentityCondition().
|
|
994
|
+
return Boolean(replayEmail(row.userId) || replayEmail(row.userKey));
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
function hasPlayableSessionRecordingEvents(row: any): boolean {
|
|
998
|
+
return Number(row.chunkCount ?? 0) > 0 && Number(row.eventCount ?? 0) > 0;
|
|
992
999
|
}
|
|
993
1000
|
|
|
994
1001
|
function isVisibleSessionRecording(row: any): boolean {
|
|
995
|
-
return
|
|
1002
|
+
return (
|
|
1003
|
+
hasVisibleSessionRecordingIdentity(row) &&
|
|
1004
|
+
hasPlayableSessionRecordingEvents(row)
|
|
1005
|
+
);
|
|
996
1006
|
}
|
|
997
1007
|
|
|
998
1008
|
function mergeReplayMetadata(
|
|
@@ -1043,9 +1053,18 @@ function replayTextContains(column: unknown, query: string) {
|
|
|
1043
1053
|
}
|
|
1044
1054
|
|
|
1045
1055
|
function replayVisibleIdentityCondition() {
|
|
1056
|
+
// Email-backed identity only — /sessions lists signed-in recordings (see
|
|
1057
|
+
// analytics CLAUDE.md). Mirror of hasVisibleSessionRecordingIdentity().
|
|
1046
1058
|
return or(
|
|
1047
1059
|
replayTextContains(schema.sessionRecordings.userId, "@"),
|
|
1048
|
-
|
|
1060
|
+
replayTextContains(schema.sessionRecordings.userKey, "@"),
|
|
1061
|
+
);
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
function replayPlayableEventsCondition() {
|
|
1065
|
+
return and(
|
|
1066
|
+
gte(schema.sessionRecordings.chunkCount, 1),
|
|
1067
|
+
gte(schema.sessionRecordings.eventCount, 1),
|
|
1049
1068
|
);
|
|
1050
1069
|
}
|
|
1051
1070
|
|
|
@@ -1126,7 +1145,12 @@ export async function recordSessionReplayChunks(
|
|
|
1126
1145
|
lastIngestedAt: ingestedAt,
|
|
1127
1146
|
ownerEmail: key.ownerEmail,
|
|
1128
1147
|
orgId: key.orgId,
|
|
1129
|
-
|
|
1148
|
+
// Session replay is an org-analytics surface: a recording captured under
|
|
1149
|
+
// an org-scoped analytics key must be visible to everyone in that org
|
|
1150
|
+
// (via accessFilter's "org" branch), not only the key owner. Without an
|
|
1151
|
+
// org we fall back to owner-private. This is what makes /sessions show
|
|
1152
|
+
// recordings to teammates instead of only the single key owner.
|
|
1153
|
+
visibility: key.orgId ? "org" : "private",
|
|
1130
1154
|
})
|
|
1131
1155
|
.onConflictDoNothing();
|
|
1132
1156
|
|
|
@@ -1190,12 +1214,23 @@ export async function recordSessionReplayChunks(
|
|
|
1190
1214
|
413,
|
|
1191
1215
|
);
|
|
1192
1216
|
}
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1217
|
+
// Replay ingest is anonymous + cross-origin (no session), so blob storage
|
|
1218
|
+
// would otherwise have no request context and `resolveBuilderPrivateKey()`
|
|
1219
|
+
// (and any S3 provider's scoped-secret lookup) would resolve nothing —
|
|
1220
|
+
// every chunk upload then 503s and recordings persist as empty shells.
|
|
1221
|
+
// Run the upload in the public key owner's user/org scope so the org's
|
|
1222
|
+
// connected Builder (or S3) credential in `app_secrets` resolves. Mirrors
|
|
1223
|
+
// the resources upload precedent (core resources/handlers.ts).
|
|
1224
|
+
const chunk = await runWithRequestContext(
|
|
1225
|
+
{ userEmail: key.ownerEmail, orgId: key.orgId ?? undefined },
|
|
1226
|
+
() =>
|
|
1227
|
+
storeReplayChunkBlob(rawChunk, {
|
|
1228
|
+
publicKeyId: key.id,
|
|
1229
|
+
recordingId: recording.id,
|
|
1230
|
+
ownerEmail: key.ownerEmail,
|
|
1231
|
+
orgId: key.orgId,
|
|
1232
|
+
}),
|
|
1233
|
+
);
|
|
1199
1234
|
if (rawChunk.storageKind !== "blob" && chunk.storageKind === "blob") {
|
|
1200
1235
|
const ref = decodeReplayBlobRef(chunk.storageRef);
|
|
1201
1236
|
if (ref) uploadedBlobHandles.push(ref.handle);
|
|
@@ -1353,6 +1388,7 @@ export async function listSessionRecordings(
|
|
|
1353
1388
|
orgId: scope.orgId ?? undefined,
|
|
1354
1389
|
}),
|
|
1355
1390
|
replayVisibleIdentityCondition(),
|
|
1391
|
+
replayPlayableEventsCondition(),
|
|
1356
1392
|
];
|
|
1357
1393
|
if (filters.app)
|
|
1358
1394
|
conditions.push(eq(schema.sessionRecordings.app, filters.app));
|
|
@@ -1363,7 +1399,12 @@ export async function listSessionRecordings(
|
|
|
1363
1399
|
conditions.push(eq(schema.sessionRecordings.sessionId, filters.sessionId));
|
|
1364
1400
|
}
|
|
1365
1401
|
if (filters.userId) {
|
|
1366
|
-
conditions.push(
|
|
1402
|
+
conditions.push(
|
|
1403
|
+
or(
|
|
1404
|
+
eq(schema.sessionRecordings.userId, filters.userId),
|
|
1405
|
+
eq(schema.sessionRecordings.userKey, filters.userId),
|
|
1406
|
+
),
|
|
1407
|
+
);
|
|
1367
1408
|
}
|
|
1368
1409
|
if (filters.anonymousId) {
|
|
1369
1410
|
conditions.push(
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
import actionsRegistry from "../../.generated/actions-registry.js";
|
|
10
10
|
import { renderDataDictionary } from "../lib/data-dictionary-context";
|
|
11
11
|
import {
|
|
12
|
+
failedDataQueryAttemptMessage,
|
|
12
13
|
hasExplicitPartialDisclosure,
|
|
13
14
|
hasFailedCorpusWorkflowEvidence,
|
|
14
15
|
hasDataQueryAttempt,
|
|
@@ -39,6 +40,7 @@ const INITIAL_TOOL_NAMES = [
|
|
|
39
40
|
"get-sql-dashboard",
|
|
40
41
|
"mutate-dashboard",
|
|
41
42
|
"generate-chart",
|
|
43
|
+
"query-agent-native-analytics",
|
|
42
44
|
"bigquery",
|
|
43
45
|
"search-bigquery-schema",
|
|
44
46
|
"bigquery-table-info",
|
|
@@ -139,6 +141,13 @@ function realDataFinalGuard(context: AgentLoopFinalResponseGuardContext) {
|
|
|
139
141
|
}
|
|
140
142
|
if (hasDataQueryAttempt(context.toolResults)) return null;
|
|
141
143
|
if (isSafeNoDataAnalyticsResponse(context.text)) return null;
|
|
144
|
+
const failedQueryMessage = failedDataQueryAttemptMessage(context.toolResults);
|
|
145
|
+
if (failedQueryMessage) {
|
|
146
|
+
return {
|
|
147
|
+
retryMessage: failedQueryMessage,
|
|
148
|
+
fallbackMessage: failedQueryMessage,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
142
151
|
|
|
143
152
|
return {
|
|
144
153
|
retryMessage:
|
|
@@ -175,8 +184,8 @@ export default createAgentChatPlugin({
|
|
|
175
184
|
"SURFACE DIFFERENTIATION — You are the analytics assistant for definitions, deep-dive analysis, and action. For questions about what a metric, model, or table means, use the Data Dictionary and configured schema tools first. For trends, comparisons, anomalies, current data, or anything that requires querying live data, answer directly in chat with the relevant provider query, dashboard analysis, and inline charts when useful. " +
|
|
176
185
|
"DASHBOARD CREATION RULE — You may create dashboards, analyses, SQL panels, or other resources only when the user explicitly asks you to (e.g. 'build me a dashboard for...', 'create a new analysis', 'add a chart for...'). Never create any resource proactively during research, trend analysis, or answering questions. If you think a dashboard would be useful, suggest it and wait for explicit confirmation before creating anything. Never add new items to the sidebar or modify existing dashboards without an explicit user directive. " +
|
|
177
186
|
"DASHBOARD MUTATION RULE — For dashboard edits, default to `mutate-dashboard` with the typed `dashboard.*` script API so the main payload is a string and avoids native-array serialization traps. It can move panels by id, edit titles/SQL/config, insert, duplicate, remove, and patch dashboard fields in one atomic save. The script API is constrained: no variables/imports/loops/functions, only JSON-compatible arguments on documented dashboard methods. Do not count shifting `/panels/<index>` positions for ordinary dashboard edits unless the user specifically asks for low-level JSON-pointer operations. " +
|
|
178
|
-
"DASHBOARD READ RULE — `get-sql-dashboard` is compact by default: use its `panels` summaries plus `layout.panelOrder` and `layout.
|
|
179
|
-
'DASHBOARD REORDER RULE — For simple chart/section moves, use `mutate-dashboard` code such as `dashboard.panels(["panel-a","panel-b"]).moveToTop();`. Never count shifting `/panels/<index>` positions for ordinary \'move this chart\' requests. Use `get-sql-dashboard.layout.
|
|
187
|
+
"DASHBOARD READ RULE — `get-sql-dashboard` is compact by default: use its `panels` summaries plus `layout.panelOrder`, `layout.firstPanelIds`, and `layout.groups[].rows[].rowNumber/panelIds` for orientation and verification. Pass `includeConfig: true` only when you truly need full panel SQL/config. " +
|
|
188
|
+
'DASHBOARD REORDER RULE — For simple chart/section moves, use `mutate-dashboard` code such as `dashboard.panels(["panel-a","panel-b"]).moveToTop();`. For visible placement requests like "second row" or "next to return rates", use row-aware placement such as `dashboard.insertPanel({...}).nextTo("retention-over-time")`, `.atRow(2)`, or `dashboard.panel("panel-a").moveNextTo("panel-b")`; these keep panels in the intended rendered row and expand/rebalance that row when needed. Never count shifting `/panels/<index>` positions for ordinary \'move this chart\' requests. Use `get-sql-dashboard.layout.groups[].rows` as proof of visible row placement, not only flat `panelOrder`. ' +
|
|
180
189
|
"Use configured data sources and actions only. Call `data-source-status` when you need to know which providers are connected, and treat provider actions as unavailable for analysis if they return missing credentials, permission, syntax, quota, or network errors. " +
|
|
181
190
|
"The built-in `demo` dashboard source is a demo-environment Prometheus source reserved for the Node Exporter demo. It must never satisfy REAL_DATA_REQUIRED or be cited as user analytics evidence unless the user explicitly asks to inspect the demo dashboard. " +
|
|
182
191
|
"When the user names a provider or tool such as Jira, Pylon, HubSpot, Gong, Slack, Sentry, GA4, or BigQuery, that named source is authoritative for the turn: use that provider's real tool/API surface, not a warehouse or different-provider substitute, unless the user explicitly asks for the copy/fallback. For bounded lookups where a first-class action fully models the requested source, object, filter, and pagination need, that shortcut is fine. For broad provider searches, cross-source joins, corpus-wide counts, exact cohort coverage, or any answer where absence matters, do not start and stop with shortcuts; use the broad provider API/MCP and corpus/code workflow as the primary path. " +
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom onboarding plugin for Analytics.
|
|
3
|
+
*
|
|
4
|
+
* Overrides the framework's default onboarding plugin to add an optional
|
|
5
|
+
* "Session replay storage" step and register the S3 file upload provider. Must
|
|
6
|
+
* live in server/plugins/ so the framework skips its default onboarding plugin,
|
|
7
|
+
* and all registrations share the same module context as the onboarding route
|
|
8
|
+
* handlers (which read from the same in-memory Map).
|
|
9
|
+
*
|
|
10
|
+
* The step is `required: false` because session replay is opt-in: capturing
|
|
11
|
+
* replays only matters once a site embeds the tracking SDK with replay enabled,
|
|
12
|
+
* so we surface storage setup without forcing it into the onboarding checklist.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { registerFileUploadProvider } from "@agent-native/core/file-upload";
|
|
16
|
+
import {
|
|
17
|
+
createOnboardingPlugin,
|
|
18
|
+
registerOnboardingStep,
|
|
19
|
+
} from "@agent-native/core/onboarding";
|
|
20
|
+
|
|
21
|
+
import { hasRequestReplayStorage } from "../lib/replay-storage.js";
|
|
22
|
+
import { s3FileUploadProvider } from "../lib/s3-upload-provider.js";
|
|
23
|
+
|
|
24
|
+
const basePlugin = createOnboardingPlugin();
|
|
25
|
+
|
|
26
|
+
export default async (nitroApp: any): Promise<void> => {
|
|
27
|
+
// Mount the framework's default onboarding plugin (routes + default steps).
|
|
28
|
+
await basePlugin(nitroApp);
|
|
29
|
+
|
|
30
|
+
// Register S3-compatible file upload provider for session-replay chunks.
|
|
31
|
+
registerFileUploadProvider(s3FileUploadProvider);
|
|
32
|
+
|
|
33
|
+
// Add the optional "Session replay storage" onboarding step.
|
|
34
|
+
registerOnboardingStep({
|
|
35
|
+
id: "replay-storage",
|
|
36
|
+
order: 15,
|
|
37
|
+
required: false,
|
|
38
|
+
title: "Session replay storage",
|
|
39
|
+
description:
|
|
40
|
+
"Store session replay recordings with Builder.io or S3-compatible storage.",
|
|
41
|
+
methods: [
|
|
42
|
+
{
|
|
43
|
+
id: "builder",
|
|
44
|
+
kind: "builder-cli-auth",
|
|
45
|
+
label: "Connect Builder.io",
|
|
46
|
+
description:
|
|
47
|
+
"Builder.io's free tier includes object storage for replay chunks.",
|
|
48
|
+
primary: true,
|
|
49
|
+
badge: "free",
|
|
50
|
+
payload: { scope: "browser" },
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
id: "s3",
|
|
54
|
+
kind: "form",
|
|
55
|
+
label: "Use S3-compatible storage",
|
|
56
|
+
description:
|
|
57
|
+
"AWS S3, Cloudflare R2, DigitalOcean Spaces, MinIO, or any S3-compatible service.",
|
|
58
|
+
payload: {
|
|
59
|
+
writeScope: "workspace",
|
|
60
|
+
fields: [
|
|
61
|
+
{
|
|
62
|
+
key: "S3_ENDPOINT",
|
|
63
|
+
label: "Endpoint URL",
|
|
64
|
+
placeholder: "https://s3.us-east-1.amazonaws.com",
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
key: "S3_BUCKET",
|
|
68
|
+
label: "Bucket name",
|
|
69
|
+
placeholder: "my-replays-bucket",
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
key: "S3_ACCESS_KEY_ID",
|
|
73
|
+
label: "Access key ID",
|
|
74
|
+
placeholder: "AKIA...",
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
key: "S3_SECRET_ACCESS_KEY",
|
|
78
|
+
label: "Secret access key",
|
|
79
|
+
secret: true,
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
key: "S3_REGION",
|
|
83
|
+
label: "Region (optional)",
|
|
84
|
+
placeholder: "us-east-1",
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
key: "S3_PUBLIC_BASE_URL",
|
|
88
|
+
label: "Public base URL (optional)",
|
|
89
|
+
placeholder: "https://cdn.example.com",
|
|
90
|
+
},
|
|
91
|
+
],
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
isComplete: hasRequestReplayStorage,
|
|
96
|
+
});
|
|
97
|
+
};
|
|
@@ -425,7 +425,7 @@ export function Sidebar() {
|
|
|
425
425
|
return (
|
|
426
426
|
<aside
|
|
427
427
|
className={cn(
|
|
428
|
-
"flex h-full min-w-0 shrink-0 flex-col overflow-hidden border-e border-border bg-sidebar text-sidebar-foreground",
|
|
428
|
+
"flex h-full min-w-0 shrink-0 flex-col overflow-hidden border-e border-border bg-sidebar text-sidebar-foreground transition-[width] duration-200 ease-out",
|
|
429
429
|
collapsed ? "w-14" : "w-56",
|
|
430
430
|
)}
|
|
431
431
|
>
|
|
@@ -553,15 +553,15 @@ export function Sidebar() {
|
|
|
553
553
|
|
|
554
554
|
{!collapsed && (
|
|
555
555
|
<div className="mt-auto shrink-0">
|
|
556
|
-
<div className="
|
|
556
|
+
<div className="px-2 py-1">
|
|
557
557
|
<ExtensionsSidebarSection />
|
|
558
558
|
</div>
|
|
559
559
|
|
|
560
|
-
<div className="
|
|
560
|
+
<div className="px-3 py-2">
|
|
561
561
|
<OrgSwitcher />
|
|
562
562
|
</div>
|
|
563
563
|
|
|
564
|
-
<div className="
|
|
564
|
+
<div className="px-3 py-2">
|
|
565
565
|
<DevDatabaseLink />
|
|
566
566
|
<FeedbackButton />
|
|
567
567
|
</div>
|
|
@@ -20,11 +20,24 @@ import {
|
|
|
20
20
|
} from "@/components/ui/sheet";
|
|
21
21
|
import { TAB_ID } from "@/lib/tab-id";
|
|
22
22
|
|
|
23
|
+
const SIDEBAR_COLLAPSE_KEY = "brain.sidebar.collapsed";
|
|
24
|
+
|
|
25
|
+
function readSidebarCollapsed() {
|
|
26
|
+
if (typeof window === "undefined") return false;
|
|
27
|
+
try {
|
|
28
|
+
return window.localStorage.getItem(SIDEBAR_COLLAPSE_KEY) === "1";
|
|
29
|
+
} catch {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
23
34
|
export function Layout({ children }: { children: React.ReactNode }) {
|
|
24
35
|
const location = useLocation();
|
|
25
36
|
const navigate = useNavigate();
|
|
26
37
|
const t = useT();
|
|
27
38
|
const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false);
|
|
39
|
+
const [sidebarCollapsed, setSidebarCollapsed] =
|
|
40
|
+
useState(readSidebarCollapsed);
|
|
28
41
|
const isAskRoute = location.pathname === "/";
|
|
29
42
|
const chatHomeHandoffActive = useAgentChatHomeHandoff({
|
|
30
43
|
storageKey: "brain",
|
|
@@ -37,10 +50,24 @@ export function Layout({ children }: { children: React.ReactNode }) {
|
|
|
37
50
|
setMobileSidebarOpen(false);
|
|
38
51
|
}, [location.pathname]);
|
|
39
52
|
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
try {
|
|
55
|
+
window.localStorage.setItem(
|
|
56
|
+
SIDEBAR_COLLAPSE_KEY,
|
|
57
|
+
sidebarCollapsed ? "1" : "0",
|
|
58
|
+
);
|
|
59
|
+
} catch {
|
|
60
|
+
// Ignore storage failures; the in-memory preference still works.
|
|
61
|
+
}
|
|
62
|
+
}, [sidebarCollapsed]);
|
|
63
|
+
|
|
40
64
|
const sidebarFrame = (
|
|
41
65
|
<>
|
|
42
66
|
<div className="agent-layout-left-drawer hidden md:block">
|
|
43
|
-
<Sidebar
|
|
67
|
+
<Sidebar
|
|
68
|
+
collapsed={sidebarCollapsed}
|
|
69
|
+
onCollapsedChange={setSidebarCollapsed}
|
|
70
|
+
/>
|
|
44
71
|
</div>
|
|
45
72
|
<Sheet open={mobileSidebarOpen} onOpenChange={setMobileSidebarOpen}>
|
|
46
73
|
<SheetContent side="left" className="w-[min(18rem,88vw)] p-0">
|
|
@@ -50,7 +77,7 @@ export function Layout({ children }: { children: React.ReactNode }) {
|
|
|
50
77
|
<SheetDescription className="sr-only">
|
|
51
78
|
{t("navigation.brainNavigationDescription")}
|
|
52
79
|
</SheetDescription>
|
|
53
|
-
<Sidebar />
|
|
80
|
+
<Sidebar collapsed={false} collapsible={false} />
|
|
54
81
|
</SheetContent>
|
|
55
82
|
</Sheet>
|
|
56
83
|
</>
|