@agent-native/core 0.149.1 → 0.149.3
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 +1 -1
- package/corpus/templates/analytics/.agents/skills/actions/SKILL.md +54 -488
- package/corpus/templates/analytics/.agents/skills/actions/references/action-fields.md +136 -0
- package/corpus/templates/analytics/.agents/skills/actions/references/examples.md +94 -0
- package/corpus/templates/analytics/.agents/skills/actions/references/provider-apis.md +95 -0
- package/corpus/templates/analytics/.agents/skills/create-skill/SKILL.md +9 -35
- package/corpus/templates/analytics/.agents/skills/creative-context/SKILL.md +4 -2
- package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +2 -0
- package/corpus/templates/analytics/.agents/skills/frontend-design/SKILL.md +4 -5
- package/corpus/templates/analytics/.agents/skills/turn-into-app/SKILL.md +18 -31
- package/corpus/templates/analytics/AGENTS.md +25 -32
- package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +150 -46
- package/corpus/templates/analytics/app/global.css +1 -1
- package/corpus/templates/analytics/app/i18n-data.ts +37 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/PanelEditorDialog.tsx +1 -1
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +32 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/types.ts +2 -0
- package/corpus/templates/analytics/changelog/2026-08-10-dashboard-tables-can-be-copied-and-sorted-by-multiple-column.md +6 -0
- package/corpus/templates/analytics/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
- package/corpus/templates/analytics/changelog/2026-08-11-full-page-chat-composers-stay-at-a-focused-750px-width.md +6 -0
- package/corpus/templates/assets/.agents/skills/actions/SKILL.md +54 -488
- package/corpus/templates/assets/.agents/skills/actions/references/action-fields.md +136 -0
- package/corpus/templates/assets/.agents/skills/actions/references/examples.md +94 -0
- package/corpus/templates/assets/.agents/skills/actions/references/provider-apis.md +95 -0
- package/corpus/templates/assets/.agents/skills/create-skill/SKILL.md +9 -35
- package/corpus/templates/assets/.agents/skills/creative-context/SKILL.md +4 -2
- package/corpus/templates/assets/.agents/skills/frontend-design/SKILL.md +4 -5
- package/corpus/templates/assets/.agents/skills/turn-into-app/SKILL.md +18 -31
- package/corpus/templates/assets/AGENTS.md +6 -9
- package/corpus/templates/assets/app/global.css +1 -1
- package/corpus/templates/assets/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
- package/corpus/templates/assets/changelog/2026-08-11-full-page-chat-composers-stay-at-a-focused-750px-width.md +6 -0
- package/corpus/templates/brain/.agents/skills/actions/SKILL.md +54 -488
- package/corpus/templates/brain/.agents/skills/actions/references/action-fields.md +136 -0
- package/corpus/templates/brain/.agents/skills/actions/references/examples.md +94 -0
- package/corpus/templates/brain/.agents/skills/actions/references/provider-apis.md +95 -0
- package/corpus/templates/brain/.agents/skills/create-skill/SKILL.md +9 -35
- package/corpus/templates/brain/.agents/skills/frontend-design/SKILL.md +4 -5
- package/corpus/templates/brain/.agents/skills/turn-into-app/SKILL.md +18 -31
- package/corpus/templates/brain/AGENTS.md +5 -12
- package/corpus/templates/brain/app/global.css +1 -1
- package/corpus/templates/brain/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
- package/corpus/templates/brain/changelog/2026-08-11-full-page-chat-composers-stay-at-a-focused-750px-width.md +6 -0
- package/corpus/templates/calendar/.agents/skills/actions/SKILL.md +54 -488
- package/corpus/templates/calendar/.agents/skills/actions/references/action-fields.md +136 -0
- package/corpus/templates/calendar/.agents/skills/actions/references/examples.md +94 -0
- package/corpus/templates/calendar/.agents/skills/actions/references/provider-apis.md +95 -0
- package/corpus/templates/calendar/.agents/skills/create-skill/SKILL.md +9 -35
- package/corpus/templates/calendar/.agents/skills/frontend-design/SKILL.md +4 -5
- package/corpus/templates/calendar/.agents/skills/turn-into-app/SKILL.md +18 -31
- package/corpus/templates/calendar/AGENTS.md +8 -9
- package/corpus/templates/chat/.agents/skills/actions/SKILL.md +54 -488
- package/corpus/templates/chat/.agents/skills/actions/references/action-fields.md +136 -0
- package/corpus/templates/chat/.agents/skills/actions/references/examples.md +94 -0
- package/corpus/templates/chat/.agents/skills/actions/references/provider-apis.md +95 -0
- package/corpus/templates/chat/.agents/skills/create-skill/SKILL.md +9 -35
- package/corpus/templates/chat/.agents/skills/frontend-design/SKILL.md +4 -5
- package/corpus/templates/chat/.agents/skills/turn-into-app/SKILL.md +18 -31
- package/corpus/templates/chat/AGENTS.md +32 -80
- package/corpus/templates/chat/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
- package/corpus/templates/clips/.agents/skills/actions/SKILL.md +54 -488
- package/corpus/templates/clips/.agents/skills/actions/references/action-fields.md +136 -0
- package/corpus/templates/clips/.agents/skills/actions/references/examples.md +94 -0
- package/corpus/templates/clips/.agents/skills/actions/references/provider-apis.md +95 -0
- package/corpus/templates/clips/.agents/skills/create-skill/SKILL.md +9 -35
- package/corpus/templates/clips/.agents/skills/frontend-design/SKILL.md +4 -5
- package/corpus/templates/clips/.agents/skills/turn-into-app/SKILL.md +18 -31
- package/corpus/templates/clips/AGENTS.md +25 -31
- package/corpus/templates/clips/changelog/2026-08-10-clips-desktop-sign-in-now-starts-with-magic-links-and-google.md +6 -0
- package/corpus/templates/clips/chrome-extension/src/overlay.css +1 -0
- package/corpus/templates/clips/desktop/src/app.tsx +274 -137
- package/corpus/templates/clips/desktop/src/styles.css +35 -0
- package/corpus/templates/content/.agents/skills/actions/SKILL.md +54 -488
- package/corpus/templates/content/.agents/skills/actions/references/action-fields.md +136 -0
- package/corpus/templates/content/.agents/skills/actions/references/examples.md +94 -0
- package/corpus/templates/content/.agents/skills/actions/references/provider-apis.md +95 -0
- package/corpus/templates/content/.agents/skills/create-skill/SKILL.md +9 -35
- package/corpus/templates/content/.agents/skills/creative-context/SKILL.md +4 -2
- package/corpus/templates/content/.agents/skills/frontend-design/SKILL.md +4 -5
- package/corpus/templates/content/.agents/skills/turn-into-app/SKILL.md +18 -31
- package/corpus/templates/content/AGENTS.md +5 -9
- package/corpus/templates/content/app/global.css +1 -0
- package/corpus/templates/crm/.agents/skills/turn-into-app/SKILL.md +18 -31
- package/corpus/templates/crm/AGENTS.md +7 -11
- package/corpus/templates/crm/app/global.css +1 -1
- package/corpus/templates/crm/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
- package/corpus/templates/crm/changelog/2026-08-11-full-page-chat-composers-stay-at-a-focused-750px-width.md +6 -0
- package/corpus/templates/design/.agents/skills/actions/SKILL.md +54 -488
- package/corpus/templates/design/.agents/skills/actions/references/action-fields.md +136 -0
- package/corpus/templates/design/.agents/skills/actions/references/examples.md +94 -0
- package/corpus/templates/design/.agents/skills/actions/references/provider-apis.md +95 -0
- package/corpus/templates/design/.agents/skills/create-skill/SKILL.md +9 -35
- package/corpus/templates/design/.agents/skills/creative-context/SKILL.md +4 -2
- package/corpus/templates/design/.agents/skills/frontend-design/SKILL.md +4 -5
- package/corpus/templates/design/.agents/skills/turn-into-app/SKILL.md +18 -31
- package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +70 -3
- package/corpus/templates/design/AGENTS.md +13 -21
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +12 -0
- package/corpus/templates/design/app/components/design/LayersPanel.tsx +19 -2
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +115 -7
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +82 -7
- package/corpus/templates/design/app/components/design/design-trace.ts +120 -0
- package/corpus/templates/design/app/components/design/dnd-debug.ts +2 -1
- package/corpus/templates/design/app/components/design/edit-panel/position-layout-properties.tsx +40 -26
- package/corpus/templates/design/app/components/design/multi-screen/board-surface-html.ts +4 -1
- package/corpus/templates/design/app/components/design/multi-screen/frame-geometry.ts +12 -3
- package/corpus/templates/design/app/components/design/multi-screen/screen-content-cache.ts +4 -0
- package/corpus/templates/design/app/components/design/multi-screen/types.ts +7 -0
- package/corpus/templates/design/app/components/editor/PromptDialog.tsx +3 -2
- package/corpus/templates/design/app/components/layout/Sidebar.tsx +2 -2
- package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +3 -0
- package/corpus/templates/design/app/i18n-data.ts +1 -0
- package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +5 -5
- package/corpus/templates/design/app/pages/DesignSystems.tsx +3 -2
- package/corpus/templates/design/app/pages/design-editor/canvas-primitive-insert.ts +100 -13
- package/corpus/templates/design/app/pages/design-editor/cross-screen-text-color.ts +1 -1
- package/corpus/templates/design/app/pages/design-editor/data-operations.ts +14 -0
- package/corpus/templates/design/changelog/2026-08-10-marquee-selecting-layers-far-down-a-tall-screen-now-selects-.md +6 -0
- package/corpus/templates/design/changelog/2026-08-10-the-frame-tool-draws-a-plain-frame-by-default-with-screen-av.md +6 -0
- package/corpus/templates/design/e2e/global-setup.ts +27 -32
- package/corpus/templates/design/playwright.config.ts +3 -5
- package/corpus/templates/design/shared/breakpoint-media.ts +9 -2
- package/corpus/templates/dispatch/.agents/skills/actions/SKILL.md +54 -488
- package/corpus/templates/dispatch/.agents/skills/actions/references/action-fields.md +136 -0
- package/corpus/templates/dispatch/.agents/skills/actions/references/examples.md +94 -0
- package/corpus/templates/dispatch/.agents/skills/actions/references/provider-apis.md +95 -0
- package/corpus/templates/dispatch/.agents/skills/create-skill/SKILL.md +9 -35
- package/corpus/templates/dispatch/.agents/skills/frontend-design/SKILL.md +4 -5
- package/corpus/templates/dispatch/.agents/skills/turn-into-app/SKILL.md +18 -31
- package/corpus/templates/dispatch/AGENTS.md +9 -16
- package/corpus/templates/dispatch/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
- package/corpus/templates/factory/.agents/skills/actions/SKILL.md +54 -488
- package/corpus/templates/factory/.agents/skills/actions/references/action-fields.md +136 -0
- package/corpus/templates/factory/.agents/skills/actions/references/examples.md +94 -0
- package/corpus/templates/factory/.agents/skills/actions/references/provider-apis.md +95 -0
- package/corpus/templates/factory/.agents/skills/create-skill/SKILL.md +9 -35
- package/corpus/templates/factory/.agents/skills/frontend-design/SKILL.md +4 -5
- package/corpus/templates/factory/.agents/skills/turn-into-app/SKILL.md +18 -31
- package/corpus/templates/factory/AGENTS.md +10 -24
- package/corpus/templates/factory/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
- package/corpus/templates/forms/.agents/skills/actions/SKILL.md +54 -488
- package/corpus/templates/forms/.agents/skills/actions/references/action-fields.md +136 -0
- package/corpus/templates/forms/.agents/skills/actions/references/examples.md +94 -0
- package/corpus/templates/forms/.agents/skills/actions/references/provider-apis.md +95 -0
- package/corpus/templates/forms/.agents/skills/create-skill/SKILL.md +9 -35
- package/corpus/templates/forms/.agents/skills/frontend-design/SKILL.md +4 -5
- package/corpus/templates/forms/.agents/skills/turn-into-app/SKILL.md +18 -31
- package/corpus/templates/forms/AGENTS.md +11 -17
- package/corpus/templates/forms/app/global.css +1 -0
- package/corpus/templates/forms/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
- package/corpus/templates/forms/changelog/2026-08-11-full-page-chat-composers-stay-at-a-focused-750px-width.md +6 -0
- package/corpus/templates/macros/.agents/skills/frontend-design/SKILL.md +4 -5
- package/corpus/templates/macros/.agents/skills/turn-into-app/SKILL.md +18 -31
- package/corpus/templates/macros/AGENTS.md +8 -8
- package/corpus/templates/mail/.agents/skills/actions/SKILL.md +54 -488
- package/corpus/templates/mail/.agents/skills/actions/references/action-fields.md +136 -0
- package/corpus/templates/mail/.agents/skills/actions/references/examples.md +94 -0
- package/corpus/templates/mail/.agents/skills/actions/references/provider-apis.md +95 -0
- package/corpus/templates/mail/.agents/skills/create-skill/SKILL.md +9 -35
- package/corpus/templates/mail/.agents/skills/frontend-design/SKILL.md +4 -5
- package/corpus/templates/mail/.agents/skills/turn-into-app/SKILL.md +18 -31
- package/corpus/templates/mail/AGENTS.md +5 -6
- package/corpus/templates/plan/.agents/skills/actions/SKILL.md +54 -488
- package/corpus/templates/plan/.agents/skills/actions/references/action-fields.md +136 -0
- package/corpus/templates/plan/.agents/skills/actions/references/examples.md +94 -0
- package/corpus/templates/plan/.agents/skills/actions/references/provider-apis.md +95 -0
- package/corpus/templates/plan/.agents/skills/create-skill/SKILL.md +9 -35
- package/corpus/templates/plan/.agents/skills/frontend-design/SKILL.md +4 -5
- package/corpus/templates/plan/.agents/skills/turn-into-app/SKILL.md +18 -31
- package/corpus/templates/plan/AGENTS.md +5 -14
- package/corpus/templates/plan/app/global.css +1 -0
- package/corpus/templates/plan/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
- package/corpus/templates/plan/changelog/2026-08-11-plan-no-longer-reloads-when-runtime-state-changes.md +6 -0
- package/corpus/templates/plan/server/ui-plan-html.ts +4 -1
- package/corpus/templates/plan/vite.config.ts +5 -0
- package/corpus/templates/slides/.agents/skills/actions/SKILL.md +54 -488
- package/corpus/templates/slides/.agents/skills/actions/references/action-fields.md +136 -0
- package/corpus/templates/slides/.agents/skills/actions/references/examples.md +94 -0
- package/corpus/templates/slides/.agents/skills/actions/references/provider-apis.md +95 -0
- package/corpus/templates/slides/.agents/skills/create-deck/SKILL.md +2 -2
- package/corpus/templates/slides/.agents/skills/create-skill/SKILL.md +9 -35
- package/corpus/templates/slides/.agents/skills/creative-context/SKILL.md +6 -3
- package/corpus/templates/slides/.agents/skills/deck-management/SKILL.md +6 -1
- package/corpus/templates/slides/.agents/skills/frontend-design/SKILL.md +4 -5
- package/corpus/templates/slides/.agents/skills/image-generation-via-a2a/SKILL.md +3 -2
- package/corpus/templates/slides/.agents/skills/slide-editing/SKILL.md +3 -0
- package/corpus/templates/slides/.agents/skills/slide-images/SKILL.md +3 -3
- package/corpus/templates/slides/.agents/skills/turn-into-app/SKILL.md +18 -31
- package/corpus/templates/slides/AGENTS.md +33 -35
- package/corpus/templates/slides/actions/add-deck.ts +2 -0
- package/corpus/templates/slides/actions/add-slide.ts +15 -1
- package/corpus/templates/slides/actions/create-deck.ts +18 -6
- package/corpus/templates/slides/actions/get-deck-reference-context.ts +2 -2
- package/corpus/templates/slides/actions/get-deck.ts +83 -7
- package/corpus/templates/slides/actions/import-file.ts +2 -1
- package/corpus/templates/slides/actions/patch-deck.ts +165 -9
- package/corpus/templates/slides/actions/save-deck.ts +24 -1
- package/corpus/templates/slides/actions/update-slide.ts +1 -1
- package/corpus/templates/slides/app/components/comments/SlideCommentsPanel.tsx +2 -2
- package/corpus/templates/slides/app/components/deck/DeckCard.tsx +1 -23
- package/corpus/templates/slides/app/components/design-system/DesignSystemCard.tsx +2 -2
- package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +6 -6
- package/corpus/templates/slides/app/components/editor/AnimationsPanel.tsx +3 -3
- package/corpus/templates/slides/app/components/editor/DeckEditorSkeleton.tsx +4 -4
- package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +1 -1
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +432 -668
- package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +296 -259
- package/corpus/templates/slides/app/components/editor/GeneratingSlidePreview.tsx +1 -1
- package/corpus/templates/slides/app/components/editor/GoogleDriveConnectionCta.tsx +13 -1
- package/corpus/templates/slides/app/components/editor/NewDeckReferenceStep.tsx +56 -174
- package/corpus/templates/slides/app/components/editor/PromptDialog.tsx +241 -116
- package/corpus/templates/slides/app/components/editor/SlideContextToolbar.tsx +1 -1
- package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +42 -14
- package/corpus/templates/slides/app/components/editor/SpeakerNotesPanel.tsx +6 -12
- package/corpus/templates/slides/app/components/editor/editor-command-model.ts +35 -0
- package/corpus/templates/slides/app/components/layout/AgentWorkIndicator.tsx +1 -1
- package/corpus/templates/slides/app/components/layout/Layout.tsx +34 -35
- package/corpus/templates/slides/app/components/layout/Sidebar.tsx +8 -10
- package/corpus/templates/slides/app/components/layout/layout-route-policy.ts +2 -5
- package/corpus/templates/slides/app/components/onboarding/FirstDeckOnboardingFlow.tsx +45 -40
- package/corpus/templates/slides/app/components/presentation/PresentationView.tsx +63 -16
- package/corpus/templates/slides/app/context/DeckContext.tsx +2 -1
- package/corpus/templates/slides/app/global.css +76 -1
- package/corpus/templates/slides/app/hooks/use-generating-slide-preview.ts +25 -3
- package/corpus/templates/slides/app/i18n/en-US.ts +2 -0
- package/corpus/templates/slides/app/lib/create-deck-generation.ts +52 -23
- package/corpus/templates/slides/app/lib/recent-references.ts +23 -1
- package/corpus/templates/slides/app/lib/slide-animation-elements.ts +104 -3
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +17 -33
- package/corpus/templates/slides/app/pages/DesignSystems.tsx +2 -2
- package/corpus/templates/slides/app/pages/Index.tsx +315 -151
- package/corpus/templates/slides/app/root.tsx +43 -0
- package/corpus/templates/slides/changelog/2026-08-10-ai-editing-avatar-badge.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-10-generated-decks-keep-a-clear-human-readable-title.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-10-new-deck-creation-now-shows-progress-opens-the-editor-direct.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-10-presentation-reveals-now-follow-valid-targets.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-10-slides-editing-controls-are-consolidated-into-one-focused-me.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-10-slides-now-verify-and-restyle-every-imported-slide-in-one-pa.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-10-slides-only-suggests-a-google-connection-for-relevant-reques.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-10-the-google-slides-import-option-is-now-labeled-slides.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-11-faster-slides-chat-deck-reads.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-11-slides-chat-can-read-one-slide-without-loading-the-full-deck.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-11-slides-clearly-indicate-when-an-ai-agent-is-editing-alongsid.md +6 -0
- package/corpus/templates/slides/package.json +1 -0
- package/corpus/templates/slides/server/lib/chat-attachments.ts +1 -1
- package/corpus/templates/slides/server/lib/validate-slide-animations.ts +163 -0
- package/corpus/templates/slides/server/plugins/agent-chat.ts +8 -5
- package/corpus/templates/slides/shared/api.ts +47 -15
- package/corpus/templates/slides/shared/deck-title.ts +135 -0
- package/corpus/templates/tasks/.agents/skills/actions/SKILL.md +54 -488
- package/corpus/templates/tasks/.agents/skills/actions/references/action-fields.md +136 -0
- package/corpus/templates/tasks/.agents/skills/actions/references/examples.md +94 -0
- package/corpus/templates/tasks/.agents/skills/actions/references/provider-apis.md +95 -0
- package/corpus/templates/tasks/.agents/skills/agent-watchdog/SKILL.md +1 -0
- package/corpus/templates/tasks/.agents/skills/create-skill/SKILL.md +9 -35
- package/corpus/templates/tasks/.agents/skills/efficient-fable/SKILL.md +1 -0
- package/corpus/templates/tasks/.agents/skills/efficient-frontier/SKILL.md +1 -0
- package/corpus/templates/tasks/.agents/skills/frontend-design/SKILL.md +4 -5
- package/corpus/templates/tasks/.agents/skills/plan-arbiter/SKILL.md +1 -0
- package/corpus/templates/tasks/.agents/skills/plow-ahead/SKILL.md +1 -0
- package/corpus/templates/tasks/.agents/skills/quick-recap/SKILL.md +1 -0
- package/corpus/templates/tasks/.agents/skills/read-the-damn-docs/SKILL.md +1 -0
- package/corpus/templates/tasks/.agents/skills/stay-within-limits/SKILL.md +1 -0
- package/corpus/templates/tasks/.agents/skills/turn-into-app/SKILL.md +18 -31
- package/corpus/templates/tasks/AGENTS.md +5 -20
- package/corpus/templates/tasks/app/components/tasks/TaskListRow.tsx +4 -1
- package/dist/agent/engine/ai-sdk-engine.js +1 -1
- package/dist/agent/production-agent.d.ts +21 -1
- package/dist/agent/production-agent.js +505 -355
- package/dist/agent/run-manager.js +156 -14
- package/dist/agent/run-store.d.ts +11 -10
- package/dist/agent/run-store.js +17 -13
- package/dist/agent/tool-approval-store.d.ts +18 -0
- package/dist/agent/tool-approval-store.js +122 -0
- package/dist/agent/types.d.ts +1 -1
- package/dist/cli/code-agent-executor.js +11 -2
- package/dist/client/AgentPanel.js +31 -6
- package/dist/client/AssistantChat.d.ts +9 -4
- package/dist/client/AssistantChat.js +221 -149
- package/dist/client/active-run-state.d.ts +9 -1
- package/dist/client/active-run-state.js +34 -3
- package/dist/client/agent-chat/index.d.ts +1 -1
- package/dist/client/agent-chat-adapter.d.ts +8 -0
- package/dist/client/agent-chat-adapter.js +125 -28
- package/dist/client/agent-chat.d.ts +1 -1
- package/dist/client/chat/markdown-renderer.js +14 -2
- package/dist/client/chat/message-components.d.ts +13 -3
- package/dist/client/chat/message-components.js +57 -52
- package/dist/client/chat/run-recovery.d.ts +1 -0
- package/dist/client/chat/run-recovery.js +8 -0
- package/dist/client/chat/tool-call-display.d.ts +5 -5
- package/dist/client/chat/tool-call-display.js +9 -212
- package/dist/client/chat-first/apps-rail.js +3 -3
- package/dist/client/chat-first/browser-pane.js +4 -1
- package/dist/client/chat-first/chat-history.d.ts +3 -1
- package/dist/client/chat-first/chat-history.js +7 -4
- package/dist/client/chat-first/surface-tabs.d.ts +1 -1
- package/dist/client/chat-first/surface-tabs.js +52 -41
- package/dist/client/chat-first/types.d.ts +3 -0
- package/dist/client/chat-first.d.ts +10 -0
- package/dist/client/chat-first.js +37 -2
- package/dist/client/components/ui/message-scroller.js +4 -1
- package/dist/client/i18n.js +3 -1
- package/dist/client/resources/McpConnectionSuggestion.js +9 -1
- package/dist/client/sse-event-processor.d.ts +4 -2
- package/dist/client/sse-event-processor.js +46 -6
- package/dist/client/use-chat-models.js +156 -80
- package/dist/client/use-run-stuck-detection.d.ts +2 -0
- package/dist/client/use-run-stuck-detection.js +85 -9
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/integrations/adapters/slack.d.ts +0 -4
- package/dist/integrations/adapters/slack.js +6 -30
- package/dist/integrations/catalog.js +1 -1
- package/dist/integrations/pending-tasks-store.d.ts +0 -8
- package/dist/integrations/pending-tasks-store.js +0 -21
- package/dist/integrations/slack-manifest.d.ts +2 -2
- package/dist/integrations/slack-manifest.js +0 -3
- package/dist/integrations/webhook-handler.js +1 -1
- package/dist/mcp/screen-memory-stdio.d.ts +7 -7
- package/dist/observability/routes.d.ts +3 -3
- package/dist/provider-api/actions/custom-provider-registration.d.ts +4 -4
- package/dist/provider-api/actions/provider-api.d.ts +8 -8
- package/dist/scripts/docs/source-search.js +2 -2
- package/dist/server/agent-chat/framework-prompts.js +7 -2
- package/dist/server/agent-chat/prompt-resources.js +2 -2
- package/dist/server/agent-chat/skill-frontmatter.d.ts +31 -2
- package/dist/server/agent-chat/skill-frontmatter.js +41 -15
- package/dist/server/agent-chat-plugin.js +3 -3
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/agents-bundle.d.ts +16 -19
- package/dist/server/agents-bundle.js +19 -35
- package/dist/server/auth.js +262 -31
- package/dist/server/better-auth-instance.d.ts +1 -0
- package/dist/server/better-auth-instance.js +17 -2
- package/dist/server/onboarding-html.js +56 -9
- package/dist/server/prompts/framework-core-compact.d.ts +6 -1
- package/dist/server/prompts/framework-core-compact.js +12 -9
- package/dist/server/prompts/framework-core.d.ts +6 -1
- package/dist/server/prompts/framework-core.js +11 -9
- package/dist/server/prompts/index.d.ts +1 -1
- package/dist/server/prompts/index.js +1 -1
- package/dist/server/prompts/shared-rules.d.ts +15 -11
- package/dist/server/prompts/shared-rules.js +17 -13
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/shared/reasoning-effort.d.ts +8 -8
- package/dist/shared/reasoning-effort.js +10 -10
- package/dist/shared/streaming-text-smoothing.js +41 -11
- package/dist/styles/agent-native.css +19 -32
- package/dist/templates/chat/.agents/skills/actions/SKILL.md +54 -488
- package/dist/templates/chat/.agents/skills/actions/references/action-fields.md +136 -0
- package/dist/templates/chat/.agents/skills/actions/references/examples.md +94 -0
- package/dist/templates/chat/.agents/skills/actions/references/provider-apis.md +95 -0
- package/dist/templates/chat/.agents/skills/create-skill/SKILL.md +9 -35
- package/dist/templates/chat/.agents/skills/frontend-design/SKILL.md +4 -5
- package/dist/templates/chat/.agents/skills/turn-into-app/SKILL.md +18 -31
- package/dist/templates/chat/AGENTS.md +32 -80
- package/dist/templates/chat/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
- package/dist/templates/default/.agents/skills/actions/SKILL.md +54 -488
- package/dist/templates/default/.agents/skills/actions/references/action-fields.md +136 -0
- package/dist/templates/default/.agents/skills/actions/references/examples.md +94 -0
- package/dist/templates/default/.agents/skills/actions/references/provider-apis.md +95 -0
- package/dist/templates/default/.agents/skills/create-skill/SKILL.md +9 -35
- package/dist/templates/default/.agents/skills/frontend-design/SKILL.md +4 -5
- package/dist/templates/default/.agents/skills/turn-into-app/SKILL.md +18 -31
- package/dist/templates/headless/.agents/skills/actions/SKILL.md +54 -488
- package/dist/templates/headless/.agents/skills/actions/references/action-fields.md +136 -0
- package/dist/templates/headless/.agents/skills/actions/references/examples.md +94 -0
- package/dist/templates/headless/.agents/skills/actions/references/provider-apis.md +95 -0
- package/dist/templates/headless/.agents/skills/turn-into-app/SKILL.md +18 -31
- package/dist/templates/workspace-core/.agents/skills/actions/SKILL.md +54 -488
- package/dist/templates/workspace-core/.agents/skills/actions/references/action-fields.md +136 -0
- package/dist/templates/workspace-core/.agents/skills/actions/references/examples.md +94 -0
- package/dist/templates/workspace-core/.agents/skills/actions/references/provider-apis.md +95 -0
- package/dist/templates/workspace-core/.agents/skills/adding-workspace-apps/SKILL.md +1 -0
- package/dist/templates/workspace-core/.agents/skills/agent-page/SKILL.md +1 -0
- package/dist/templates/workspace-core/.agents/skills/create-skill/SKILL.md +9 -35
- package/dist/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +4 -5
- package/dist/templates/workspace-core/.agents/skills/recurring-jobs/SKILL.md +21 -29
- package/dist/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +18 -31
- package/dist/templates/workspace-core/.agents/skills/workspace-conventions/SKILL.md +7 -17
- package/dist/templates/workspace-core/.agents/skills/writing-agent-instructions/SKILL.md +3 -3
- package/dist/templates/workspace-core/AGENTS.md +7 -16
- package/docs/content/messaging.mdx +6 -7
- package/docs/content/template-chat.mdx +1 -1
- package/package.json +2 -2
- package/src/templates/chat/.agents/skills/actions/SKILL.md +54 -488
- package/src/templates/chat/.agents/skills/actions/references/action-fields.md +136 -0
- package/src/templates/chat/.agents/skills/actions/references/examples.md +94 -0
- package/src/templates/chat/.agents/skills/actions/references/provider-apis.md +95 -0
- package/src/templates/chat/.agents/skills/create-skill/SKILL.md +9 -35
- package/src/templates/chat/.agents/skills/frontend-design/SKILL.md +4 -5
- package/src/templates/chat/.agents/skills/turn-into-app/SKILL.md +18 -31
- package/src/templates/chat/AGENTS.md +32 -80
- package/src/templates/chat/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
- package/src/templates/default/.agents/skills/actions/SKILL.md +54 -488
- package/src/templates/default/.agents/skills/actions/references/action-fields.md +136 -0
- package/src/templates/default/.agents/skills/actions/references/examples.md +94 -0
- package/src/templates/default/.agents/skills/actions/references/provider-apis.md +95 -0
- package/src/templates/default/.agents/skills/create-skill/SKILL.md +9 -35
- package/src/templates/default/.agents/skills/frontend-design/SKILL.md +4 -5
- package/src/templates/default/.agents/skills/turn-into-app/SKILL.md +18 -31
- package/src/templates/headless/.agents/skills/actions/SKILL.md +54 -488
- package/src/templates/headless/.agents/skills/actions/references/action-fields.md +136 -0
- package/src/templates/headless/.agents/skills/actions/references/examples.md +94 -0
- package/src/templates/headless/.agents/skills/actions/references/provider-apis.md +95 -0
- package/src/templates/headless/.agents/skills/turn-into-app/SKILL.md +18 -31
- package/src/templates/workspace-core/.agents/skills/actions/SKILL.md +54 -488
- package/src/templates/workspace-core/.agents/skills/actions/references/action-fields.md +136 -0
- package/src/templates/workspace-core/.agents/skills/actions/references/examples.md +94 -0
- package/src/templates/workspace-core/.agents/skills/actions/references/provider-apis.md +95 -0
- package/src/templates/workspace-core/.agents/skills/adding-workspace-apps/SKILL.md +1 -0
- package/src/templates/workspace-core/.agents/skills/agent-page/SKILL.md +1 -0
- package/src/templates/workspace-core/.agents/skills/create-skill/SKILL.md +9 -35
- package/src/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +4 -5
- package/src/templates/workspace-core/.agents/skills/recurring-jobs/SKILL.md +21 -29
- package/src/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +18 -31
- package/src/templates/workspace-core/.agents/skills/workspace-conventions/SKILL.md +7 -17
- package/src/templates/workspace-core/.agents/skills/writing-agent-instructions/SKILL.md +3 -3
- package/src/templates/workspace-core/AGENTS.md +7 -16
|
@@ -13,39 +13,37 @@ import { PresenceBar } from "@agent-native/toolkit/collab-ui";
|
|
|
13
13
|
import {
|
|
14
14
|
IconArrowLeft,
|
|
15
15
|
IconPlayerPlay,
|
|
16
|
-
IconLayout,
|
|
17
16
|
IconLayoutSidebar,
|
|
18
17
|
IconPhoto,
|
|
19
18
|
IconHistory,
|
|
20
19
|
IconFolderOpen,
|
|
21
|
-
IconSettings,
|
|
22
|
-
IconSchema,
|
|
23
|
-
IconPencil,
|
|
24
|
-
IconTransform,
|
|
25
20
|
IconMessage,
|
|
26
|
-
IconBolt,
|
|
27
|
-
IconAdjustments,
|
|
28
|
-
IconPencilPlus,
|
|
29
|
-
IconPin,
|
|
30
|
-
IconTool,
|
|
31
21
|
IconDownload,
|
|
32
22
|
IconSun,
|
|
33
23
|
IconMoon,
|
|
34
24
|
IconDotsVertical,
|
|
35
25
|
IconLoader2,
|
|
36
|
-
|
|
37
|
-
|
|
26
|
+
IconBolt,
|
|
27
|
+
IconAdjustments,
|
|
28
|
+
IconPencilPlus,
|
|
29
|
+
IconPin,
|
|
30
|
+
IconBrandGoogle,
|
|
31
|
+
IconCode,
|
|
32
|
+
IconCopy,
|
|
33
|
+
IconFileTypePdf,
|
|
34
|
+
IconPlugConnected,
|
|
38
35
|
} from "@tabler/icons-react";
|
|
39
36
|
import { useTheme } from "next-themes";
|
|
40
|
-
import {
|
|
41
|
-
import { createPortal } from "react-dom";
|
|
37
|
+
import { useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
42
38
|
import { Link } from "react-router";
|
|
43
39
|
import { toast } from "sonner";
|
|
44
40
|
|
|
41
|
+
import { Button } from "@/components/ui/button";
|
|
45
42
|
import {
|
|
46
43
|
DropdownMenu,
|
|
47
44
|
DropdownMenuContent,
|
|
48
45
|
DropdownMenuGroup,
|
|
46
|
+
DropdownMenuLabel,
|
|
49
47
|
DropdownMenuItem,
|
|
50
48
|
DropdownMenuTrigger,
|
|
51
49
|
DropdownMenuSeparator,
|
|
@@ -56,25 +54,18 @@ import {
|
|
|
56
54
|
TooltipTrigger,
|
|
57
55
|
} from "@/components/ui/tooltip";
|
|
58
56
|
import { SaveStatusIndicator } from "@/components/visual-editor";
|
|
59
|
-
import type { Deck, Slide
|
|
60
|
-
import {
|
|
61
|
-
defaultSlideContent,
|
|
62
|
-
useDecks,
|
|
63
|
-
useSaveState,
|
|
64
|
-
} from "@/context/DeckContext";
|
|
65
|
-
import {
|
|
66
|
-
ASPECT_RATIO_VALUES,
|
|
67
|
-
type AspectRatio,
|
|
68
|
-
DEFAULT_ASPECT_RATIO,
|
|
69
|
-
} from "@/lib/aspect-ratios";
|
|
57
|
+
import type { Deck, Slide } from "@/context/DeckContext";
|
|
58
|
+
import { useSaveState } from "@/context/DeckContext";
|
|
70
59
|
import { getDeckShareLinkOrder } from "@/lib/deck-share-links";
|
|
71
60
|
import type { GoogleSlidesExportResult } from "@/lib/export-google-slides-client";
|
|
72
61
|
import { parseUploadResponse } from "@/lib/upload-response";
|
|
73
|
-
import { shortcutLabel } from "@/lib/utils";
|
|
74
62
|
|
|
75
|
-
import {
|
|
63
|
+
import {
|
|
64
|
+
registerEditorCommands,
|
|
65
|
+
type EditorCommand,
|
|
66
|
+
} from "./editor-command-model";
|
|
76
67
|
import { EditorActionCluster } from "./EditorActionCluster";
|
|
77
|
-
import { ExportMenu } from "./ExportMenu";
|
|
68
|
+
import { ExportMenu, type ExportMenuHandle } from "./ExportMenu";
|
|
78
69
|
interface EditorToolbarProps {
|
|
79
70
|
deck: Deck;
|
|
80
71
|
deckId: string;
|
|
@@ -90,16 +81,11 @@ interface EditorToolbarProps {
|
|
|
90
81
|
onToggleSidebar: () => void;
|
|
91
82
|
onGenerateImage: () => void;
|
|
92
83
|
onOpenAssetLibrary: () => void;
|
|
93
|
-
imageGenButtonRef: React.RefObject<HTMLButtonElement | null>;
|
|
94
|
-
assetsButtonRef: React.RefObject<HTMLButtonElement | null>;
|
|
95
|
-
historyOpen: boolean;
|
|
96
84
|
onShowHistory: () => void;
|
|
97
85
|
historyButtonRef: React.RefObject<HTMLButtonElement | null>;
|
|
98
86
|
currentSlide?: Slide;
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
slideIdOverride?: string,
|
|
102
|
-
) => void;
|
|
87
|
+
/** Host for the wide-layout style toolbar in this row. */
|
|
88
|
+
onWideContextToolbarSlotChange?: (element: HTMLDivElement | null) => void;
|
|
103
89
|
/** Active users on the current slide (from collab awareness) */
|
|
104
90
|
activeUsers?: CollabUser[];
|
|
105
91
|
/** Whether the agent has a durable presence entry on this slide */
|
|
@@ -142,10 +128,6 @@ interface EditorToolbarProps {
|
|
|
142
128
|
onExportPptx?: () => Promise<void> | void;
|
|
143
129
|
/** Create the deck in the user's Google Drive as native Google Slides */
|
|
144
130
|
onExportGoogleSlides?: () => Promise<GoogleSlidesExportResult>;
|
|
145
|
-
/** Active deck aspect ratio (defaults to 16:9 when omitted) */
|
|
146
|
-
aspectRatio?: AspectRatio;
|
|
147
|
-
/** Change the deck's aspect ratio */
|
|
148
|
-
onSetAspectRatio?: (ratio: AspectRatio) => void;
|
|
149
131
|
/** Insert a blank slide after the current one */
|
|
150
132
|
onAddEmptySlide?: () => void;
|
|
151
133
|
/** Duplicate the current slide */
|
|
@@ -158,92 +140,9 @@ interface EditorToolbarProps {
|
|
|
158
140
|
onAddSlideGeneratingChange?: (generating: boolean) => void;
|
|
159
141
|
}
|
|
160
142
|
|
|
161
|
-
const slideLayoutOptions: { value: SlideLayout; labelKey: string }[] = [
|
|
162
|
-
{ value: "title", labelKey: "editorToolbar.layoutTitle" },
|
|
163
|
-
{ value: "section", labelKey: "editorToolbar.layoutSection" },
|
|
164
|
-
{ value: "content", labelKey: "editorToolbar.layoutContent" },
|
|
165
|
-
{ value: "two-column", labelKey: "editorToolbar.layoutTwoColumn" },
|
|
166
|
-
{ value: "image", labelKey: "editorToolbar.layoutImage" },
|
|
167
|
-
{ value: "statement", labelKey: "editorToolbar.layoutStatement" },
|
|
168
|
-
{ value: "full-image", labelKey: "editorToolbar.layoutFullImage" },
|
|
169
|
-
{ value: "blank", labelKey: "editorToolbar.layoutBlank" },
|
|
170
|
-
];
|
|
171
|
-
|
|
172
|
-
const backgroundOptions = [
|
|
173
|
-
"bg-[#000000]",
|
|
174
|
-
"bg-[#0a0a0a]",
|
|
175
|
-
"bg-[#0f0f11]",
|
|
176
|
-
"bg-[#111114]",
|
|
177
|
-
"bg-[#141418]",
|
|
178
|
-
"bg-gradient-to-br from-[#000000] to-[#0a0a14]",
|
|
179
|
-
"bg-gradient-to-br from-[#0a0a0a] to-[#0f1a14]",
|
|
180
|
-
"bg-[#ffffff]",
|
|
181
|
-
];
|
|
182
|
-
|
|
183
|
-
const HEX_COLOR_PATTERN = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i;
|
|
184
143
|
const TOOLBAR_ICON_BUTTON_CLASS =
|
|
185
144
|
"inline-flex size-8 flex-shrink-0 items-center justify-center rounded-md transition-colors";
|
|
186
145
|
|
|
187
|
-
/** Native <input type="color"> only accepts 3/6-digit hex — fall back to
|
|
188
|
-
* black for gradients or other raw CSS values so the picker still opens. */
|
|
189
|
-
function toColorInputValue(background: string | undefined): string {
|
|
190
|
-
return background && HEX_COLOR_PATTERN.test(background)
|
|
191
|
-
? background
|
|
192
|
-
: "#000000";
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/** Popover anchored to a button ref */
|
|
196
|
-
function ToolbarPopover({
|
|
197
|
-
open,
|
|
198
|
-
anchorRef,
|
|
199
|
-
onClose,
|
|
200
|
-
children,
|
|
201
|
-
width = 160,
|
|
202
|
-
align = "right",
|
|
203
|
-
}: {
|
|
204
|
-
open: boolean;
|
|
205
|
-
anchorRef: React.RefObject<HTMLButtonElement | null>;
|
|
206
|
-
onClose: () => void;
|
|
207
|
-
children: React.ReactNode;
|
|
208
|
-
width?: number;
|
|
209
|
-
align?: "left" | "right";
|
|
210
|
-
}) {
|
|
211
|
-
const menuRef = useRef<HTMLDivElement>(null);
|
|
212
|
-
|
|
213
|
-
useEffect(() => {
|
|
214
|
-
if (!open) return;
|
|
215
|
-
const handleClick = (e: MouseEvent) => {
|
|
216
|
-
if (
|
|
217
|
-
menuRef.current &&
|
|
218
|
-
!menuRef.current.contains(e.target as Node) &&
|
|
219
|
-
anchorRef.current &&
|
|
220
|
-
!anchorRef.current.contains(e.target as Node)
|
|
221
|
-
) {
|
|
222
|
-
onClose();
|
|
223
|
-
}
|
|
224
|
-
};
|
|
225
|
-
document.addEventListener("mousedown", handleClick);
|
|
226
|
-
return () => document.removeEventListener("mousedown", handleClick);
|
|
227
|
-
}, [open, onClose, anchorRef]);
|
|
228
|
-
|
|
229
|
-
if (!open || !anchorRef.current) return null;
|
|
230
|
-
const rect = anchorRef.current.getBoundingClientRect();
|
|
231
|
-
const vw = window.innerWidth;
|
|
232
|
-
let left = align === "right" ? rect.right - width : rect.left;
|
|
233
|
-
left = Math.max(8, Math.min(left, vw - width - 8));
|
|
234
|
-
|
|
235
|
-
return createPortal(
|
|
236
|
-
<div
|
|
237
|
-
ref={menuRef}
|
|
238
|
-
className="fixed rounded-lg border border-border bg-popover shadow-xl z-[200] max-h-[80vh] overflow-y-auto animate-in fade-in-0 zoom-in-95 duration-150 origin-top-left"
|
|
239
|
-
style={{ top: rect.bottom + 4, left, width: Math.min(width, vw - 16) }}
|
|
240
|
-
>
|
|
241
|
-
{children}
|
|
242
|
-
</div>,
|
|
243
|
-
document.body,
|
|
244
|
-
);
|
|
245
|
-
}
|
|
246
|
-
|
|
247
146
|
export default function EditorToolbar({
|
|
248
147
|
deck,
|
|
249
148
|
deckId,
|
|
@@ -255,12 +154,10 @@ export default function EditorToolbar({
|
|
|
255
154
|
onToggleSidebar,
|
|
256
155
|
onGenerateImage,
|
|
257
156
|
onOpenAssetLibrary,
|
|
258
|
-
imageGenButtonRef,
|
|
259
|
-
assetsButtonRef,
|
|
260
157
|
onShowHistory,
|
|
261
158
|
historyButtonRef,
|
|
262
159
|
currentSlide,
|
|
263
|
-
|
|
160
|
+
onWideContextToolbarSlotChange,
|
|
264
161
|
activeUsers,
|
|
265
162
|
agentPresent,
|
|
266
163
|
agentActive,
|
|
@@ -282,8 +179,6 @@ export default function EditorToolbar({
|
|
|
282
179
|
onExportPdf,
|
|
283
180
|
onExportPptx,
|
|
284
181
|
onExportGoogleSlides,
|
|
285
|
-
aspectRatio,
|
|
286
|
-
onSetAspectRatio,
|
|
287
182
|
onAddEmptySlide,
|
|
288
183
|
onDuplicateCurrentSlide,
|
|
289
184
|
currentSlideId,
|
|
@@ -336,43 +231,30 @@ export default function EditorToolbar({
|
|
|
336
231
|
};
|
|
337
232
|
}, []);
|
|
338
233
|
|
|
339
|
-
const activeAspectRatio: AspectRatio = aspectRatio ?? DEFAULT_ASPECT_RATIO;
|
|
340
|
-
const isCustomBackground =
|
|
341
|
-
!!currentSlide?.background &&
|
|
342
|
-
!backgroundOptions.includes(currentSlide.background);
|
|
343
|
-
const [layoutOpen, setLayoutOpen] = useState(false);
|
|
344
|
-
const layoutRef = useRef<HTMLButtonElement>(null);
|
|
345
|
-
|
|
346
|
-
const handleLayoutSelect = (layout: SlideLayout) => {
|
|
347
|
-
if (!currentSlide || !onUpdateSlide) return;
|
|
348
|
-
if (currentSlide.layout !== layout) {
|
|
349
|
-
onUpdateSlide({ layout, content: defaultSlideContent[layout] });
|
|
350
|
-
}
|
|
351
|
-
setLayoutOpen(false);
|
|
352
|
-
};
|
|
353
|
-
const [toolsOpen, setToolsOpen] = useState(false);
|
|
354
234
|
// The contextual toolbar hosts the action cluster whenever it is on screen.
|
|
355
235
|
// That row rides on SlideEditor, which only mounts for a real slide, so an
|
|
356
236
|
// empty deck must keep this fallback or it has no way to add one.
|
|
357
237
|
const contextToolbarVisible = canEdit && Boolean(currentSlide);
|
|
358
|
-
const { undo, redo, canUndo, canRedo, getDeck } = useDecks();
|
|
359
238
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
|
239
|
+
const exportMenuRef = useRef<ExportMenuHandle>(null);
|
|
240
|
+
const titleMeasureRef = useRef<HTMLSpanElement>(null);
|
|
241
|
+
const [titleInputWidth, setTitleInputWidth] = useState(96);
|
|
360
242
|
const [importing, setImporting] = useState(false);
|
|
361
243
|
const { setTheme, resolvedTheme } = useTheme();
|
|
362
244
|
const [themeMounted, setThemeMounted] = useState(false);
|
|
363
245
|
useEffect(() => setThemeMounted(true), []);
|
|
364
246
|
const isDark = themeMounted ? resolvedTheme === "dark" : false;
|
|
365
|
-
// The secondary tools share an "active when something is on" indicator so
|
|
366
|
-
// the dot on the consolidated button reflects any of them.
|
|
367
|
-
const anyToolActive = Boolean(
|
|
368
|
-
animationsOpen || tweaksOpen || drawMode || pinMode,
|
|
369
|
-
);
|
|
370
|
-
|
|
371
|
-
const closeAll = () => {
|
|
372
|
-
setLayoutOpen(false);
|
|
373
|
-
setToolsOpen(false);
|
|
374
|
-
};
|
|
375
247
|
|
|
248
|
+
useLayoutEffect(() => {
|
|
249
|
+
const measuredWidth =
|
|
250
|
+
titleMeasureRef.current?.getBoundingClientRect().width;
|
|
251
|
+
if (typeof measuredWidth !== "number" || !Number.isFinite(measuredWidth)) {
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
setTitleInputWidth(
|
|
255
|
+
Math.min(500, Math.max(96, Math.ceil(measuredWidth) + 16)),
|
|
256
|
+
);
|
|
257
|
+
}, [deckTitle]);
|
|
376
258
|
const handleImportFile = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
377
259
|
const file = e.target.files?.[0];
|
|
378
260
|
if (!file) return;
|
|
@@ -449,8 +331,202 @@ export default function EditorToolbar({
|
|
|
449
331
|
}
|
|
450
332
|
};
|
|
451
333
|
|
|
334
|
+
const editorCommands = useMemo<EditorCommand[]>(() => {
|
|
335
|
+
const commands: EditorCommand[] = [];
|
|
336
|
+
if (canEdit) {
|
|
337
|
+
commands.push(
|
|
338
|
+
{
|
|
339
|
+
id: "generate-image",
|
|
340
|
+
group: "media",
|
|
341
|
+
label: t("editorToolbar.generateImage"),
|
|
342
|
+
keywords: ["image", "media", "ai"],
|
|
343
|
+
icon: IconPhoto,
|
|
344
|
+
run: onGenerateImage,
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
id: "asset-library",
|
|
348
|
+
group: "media",
|
|
349
|
+
label: t("editorToolbar.assetLibrary"),
|
|
350
|
+
keywords: ["image", "media", "assets"],
|
|
351
|
+
icon: IconFolderOpen,
|
|
352
|
+
run: onOpenAssetLibrary,
|
|
353
|
+
},
|
|
354
|
+
);
|
|
355
|
+
if (currentSlide && onToggleAnimations) {
|
|
356
|
+
commands.push({
|
|
357
|
+
id: "element-animations",
|
|
358
|
+
group: "slideTools",
|
|
359
|
+
label: t("editorToolbar.elementAnimations"),
|
|
360
|
+
keywords: ["animation", "motion"],
|
|
361
|
+
icon: IconBolt,
|
|
362
|
+
active: animationsOpen,
|
|
363
|
+
run: onToggleAnimations,
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
if (onToggleTweaks) {
|
|
367
|
+
commands.push({
|
|
368
|
+
id: "slide-tweaks",
|
|
369
|
+
group: "slideTools",
|
|
370
|
+
label: t("editorToolbar.tweaks"),
|
|
371
|
+
keywords: ["style", "inspect", "adjust"],
|
|
372
|
+
icon: IconAdjustments,
|
|
373
|
+
active: tweaksOpen,
|
|
374
|
+
run: onToggleTweaks,
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
if (onToggleDrawMode) {
|
|
378
|
+
commands.push({
|
|
379
|
+
id: "draw-on-slide",
|
|
380
|
+
group: "slideTools",
|
|
381
|
+
label: t("editorToolbar.drawOnSlide"),
|
|
382
|
+
keywords: ["annotate", "draw"],
|
|
383
|
+
icon: IconPencilPlus,
|
|
384
|
+
active: drawMode,
|
|
385
|
+
run: onToggleDrawMode,
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
if (onTogglePinMode) {
|
|
389
|
+
commands.push({
|
|
390
|
+
id: "pin-comments",
|
|
391
|
+
group: "slideTools",
|
|
392
|
+
label: t("editorToolbar.pinComments"),
|
|
393
|
+
keywords: ["comment", "pin"],
|
|
394
|
+
icon: IconPin,
|
|
395
|
+
active: pinMode,
|
|
396
|
+
run: onTogglePinMode,
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
if (onToggleComments) {
|
|
401
|
+
commands.push({
|
|
402
|
+
id: "comments",
|
|
403
|
+
group: "comments",
|
|
404
|
+
label: t("editorToolbar.comments"),
|
|
405
|
+
keywords: ["comment", "review"],
|
|
406
|
+
icon: IconMessage,
|
|
407
|
+
active: commentsOpen,
|
|
408
|
+
run: onToggleComments,
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
commands.push(
|
|
413
|
+
{
|
|
414
|
+
id: "download-html",
|
|
415
|
+
group: "deck",
|
|
416
|
+
label: t("editorExport.downloadHtml"),
|
|
417
|
+
keywords: ["export", "html", "download"],
|
|
418
|
+
icon: IconCode,
|
|
419
|
+
run: () => void exportMenuRef.current?.exportHtml(),
|
|
420
|
+
},
|
|
421
|
+
{
|
|
422
|
+
id: "export-pdf",
|
|
423
|
+
group: "deck",
|
|
424
|
+
label: t("editorExport.exportPdf"),
|
|
425
|
+
keywords: ["export", "pdf", "download"],
|
|
426
|
+
icon: IconFileTypePdf,
|
|
427
|
+
run: () => void onExportPdf?.(),
|
|
428
|
+
},
|
|
429
|
+
{
|
|
430
|
+
id: "export-pptx",
|
|
431
|
+
group: "deck",
|
|
432
|
+
label: t("editorExport.exportPptx"),
|
|
433
|
+
keywords: ["export", "powerpoint", "pptx", "download"],
|
|
434
|
+
icon: IconDownload,
|
|
435
|
+
run: () => void exportMenuRef.current?.exportPptx(),
|
|
436
|
+
},
|
|
437
|
+
);
|
|
438
|
+
if (onExportGoogleSlides) {
|
|
439
|
+
commands.push(
|
|
440
|
+
{
|
|
441
|
+
id: "connect-google",
|
|
442
|
+
group: "deck",
|
|
443
|
+
label: t("editorExport.connectGoogle"),
|
|
444
|
+
keywords: ["google", "drive", "connect"],
|
|
445
|
+
icon: IconPlugConnected,
|
|
446
|
+
run: () => void exportMenuRef.current?.connectGoogle(),
|
|
447
|
+
},
|
|
448
|
+
{
|
|
449
|
+
id: "open-in-google-slides",
|
|
450
|
+
group: "deck",
|
|
451
|
+
label: t("editorExport.openInGoogleSlides"),
|
|
452
|
+
keywords: ["google", "slides", "export"],
|
|
453
|
+
icon: IconBrandGoogle,
|
|
454
|
+
run: () => void exportMenuRef.current?.exportGoogleSlides(),
|
|
455
|
+
},
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
if (onDuplicateDeck) {
|
|
459
|
+
commands.push({
|
|
460
|
+
id: "duplicate-deck",
|
|
461
|
+
group: "deck",
|
|
462
|
+
label: t("editorExport.duplicateDeck"),
|
|
463
|
+
keywords: ["copy", "duplicate"],
|
|
464
|
+
icon: IconCopy,
|
|
465
|
+
run: onDuplicateDeck,
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
commands.push(
|
|
469
|
+
{
|
|
470
|
+
id: "import-file",
|
|
471
|
+
group: "other",
|
|
472
|
+
label: importing
|
|
473
|
+
? t("editorToolbar.importing")
|
|
474
|
+
: t("editorToolbar.importFile"),
|
|
475
|
+
keywords: ["import", "pptx", "docx", "pdf"],
|
|
476
|
+
icon: importing ? IconLoader2 : IconDownload,
|
|
477
|
+
run: () => fileInputRef.current?.click(),
|
|
478
|
+
},
|
|
479
|
+
{
|
|
480
|
+
id: "saved-versions",
|
|
481
|
+
group: "other",
|
|
482
|
+
label: t("editorToolbar.savedVersions"),
|
|
483
|
+
keywords: ["history", "versions", "restore"],
|
|
484
|
+
icon: IconHistory,
|
|
485
|
+
run: onShowHistory,
|
|
486
|
+
},
|
|
487
|
+
{
|
|
488
|
+
id: "toggle-theme",
|
|
489
|
+
group: "other",
|
|
490
|
+
label: isDark
|
|
491
|
+
? t("editorToolbar.lightTheme")
|
|
492
|
+
: t("editorToolbar.darkTheme"),
|
|
493
|
+
keywords: ["theme", "dark", "light", "mode"],
|
|
494
|
+
icon: isDark ? IconSun : IconMoon,
|
|
495
|
+
run: () => setTheme(isDark ? "light" : "dark"),
|
|
496
|
+
},
|
|
497
|
+
);
|
|
498
|
+
return commands;
|
|
499
|
+
}, [
|
|
500
|
+
animationsOpen,
|
|
501
|
+
canEdit,
|
|
502
|
+
commentsOpen,
|
|
503
|
+
currentSlide,
|
|
504
|
+
drawMode,
|
|
505
|
+
importing,
|
|
506
|
+
isDark,
|
|
507
|
+
onDuplicateDeck,
|
|
508
|
+
onExportGoogleSlides,
|
|
509
|
+
onExportPdf,
|
|
510
|
+
onGenerateImage,
|
|
511
|
+
onOpenAssetLibrary,
|
|
512
|
+
onShowHistory,
|
|
513
|
+
onToggleAnimations,
|
|
514
|
+
onToggleComments,
|
|
515
|
+
onToggleDrawMode,
|
|
516
|
+
onTogglePinMode,
|
|
517
|
+
onToggleTweaks,
|
|
518
|
+
pinMode,
|
|
519
|
+
setTheme,
|
|
520
|
+
t,
|
|
521
|
+
tweaksOpen,
|
|
522
|
+
]);
|
|
523
|
+
const editorCommandsRef = useRef<readonly EditorCommand[]>(editorCommands);
|
|
524
|
+
editorCommandsRef.current = editorCommands;
|
|
525
|
+
|
|
526
|
+
useEffect(() => registerEditorCommands(() => editorCommandsRef.current), []);
|
|
527
|
+
|
|
452
528
|
return (
|
|
453
|
-
<div className="flex h-11 shrink-0 items-center gap-1 overflow-x-auto whitespace-nowrap
|
|
529
|
+
<div className="deck-editor-toolbar flex h-11 shrink-0 items-center gap-1 overflow-x-auto whitespace-nowrap bg-background px-2 sm:px-3">
|
|
454
530
|
{/* Back button */}
|
|
455
531
|
<Tooltip>
|
|
456
532
|
<TooltipTrigger asChild>
|
|
@@ -502,22 +578,32 @@ export default function EditorToolbar({
|
|
|
502
578
|
)}
|
|
503
579
|
|
|
504
580
|
{/* Deck title */}
|
|
581
|
+
<span
|
|
582
|
+
ref={titleMeasureRef}
|
|
583
|
+
aria-hidden="true"
|
|
584
|
+
className="pointer-events-none fixed -left-[9999px] whitespace-pre text-sm font-medium opacity-0"
|
|
585
|
+
>
|
|
586
|
+
{deckTitle || " "}
|
|
587
|
+
</span>
|
|
505
588
|
<input
|
|
506
589
|
type="text"
|
|
507
590
|
value={deckTitle}
|
|
508
591
|
onChange={(e) => onTitleChange(e.target.value)}
|
|
509
|
-
|
|
592
|
+
style={{ width: `${titleInputWidth}px` }}
|
|
593
|
+
className="min-w-0 max-w-[500px] flex-shrink bg-transparent text-sm font-medium text-foreground/90 outline-none focus:text-foreground"
|
|
510
594
|
spellCheck={false}
|
|
511
595
|
/>
|
|
512
596
|
|
|
513
|
-
{/* Slide counter */}
|
|
514
|
-
<span className="text-xs text-muted-foreground/70 flex-shrink-0 hidden sm:inline">
|
|
515
|
-
{currentSlideIndex + 1}/{slideCount}
|
|
516
|
-
</span>
|
|
517
|
-
|
|
518
597
|
{/* Spacer */}
|
|
519
598
|
<div className="flex-1 min-w-2" />
|
|
520
599
|
|
|
600
|
+
<div
|
|
601
|
+
ref={onWideContextToolbarSlotChange}
|
|
602
|
+
data-context-toolbar-host="wide"
|
|
603
|
+
data-context-toolbar-visible={contextToolbarVisible ? "true" : "false"}
|
|
604
|
+
className="deck-editor-context-toolbar-host deck-editor-context-toolbar-host--wide"
|
|
605
|
+
/>
|
|
606
|
+
|
|
521
607
|
{/* "View only" badge — mirrors Google Slides' viewer chrome */}
|
|
522
608
|
{!canEdit && (
|
|
523
609
|
<span className="flex-shrink-0 inline-flex items-center gap-1 rounded-full border border-border bg-muted/50 px-2.5 py-1 text-xs font-medium text-muted-foreground">
|
|
@@ -525,407 +611,6 @@ export default function EditorToolbar({
|
|
|
525
611
|
</span>
|
|
526
612
|
)}
|
|
527
613
|
|
|
528
|
-
{/* Slide settings cog menu */}
|
|
529
|
-
{canEdit && currentSlide && onUpdateSlide && (
|
|
530
|
-
<>
|
|
531
|
-
<Tooltip>
|
|
532
|
-
<TooltipTrigger asChild>
|
|
533
|
-
<button
|
|
534
|
-
ref={layoutRef}
|
|
535
|
-
onClick={() => {
|
|
536
|
-
closeAll();
|
|
537
|
-
setLayoutOpen(!layoutOpen);
|
|
538
|
-
}}
|
|
539
|
-
className={`${TOOLBAR_ICON_BUTTON_CLASS} ${
|
|
540
|
-
layoutOpen
|
|
541
|
-
? "text-foreground/90 bg-accent"
|
|
542
|
-
: "text-muted-foreground hover:text-foreground/70 hover:bg-accent"
|
|
543
|
-
}`}
|
|
544
|
-
aria-label={t("editorToolbar.slideSettings")}
|
|
545
|
-
>
|
|
546
|
-
<IconSettings className="size-4" />
|
|
547
|
-
</button>
|
|
548
|
-
</TooltipTrigger>
|
|
549
|
-
<TooltipContent>{t("editorToolbar.slideSettings")}</TooltipContent>
|
|
550
|
-
</Tooltip>
|
|
551
|
-
<ToolbarPopover
|
|
552
|
-
open={layoutOpen}
|
|
553
|
-
anchorRef={layoutRef}
|
|
554
|
-
onClose={() => setLayoutOpen(false)}
|
|
555
|
-
width={220}
|
|
556
|
-
>
|
|
557
|
-
<div className="py-1.5">
|
|
558
|
-
{/* Layout section */}
|
|
559
|
-
<div className="px-3 py-1.5 text-[10px] font-medium text-muted-foreground uppercase tracking-wider">
|
|
560
|
-
{t("editorToolbar.layout")}
|
|
561
|
-
</div>
|
|
562
|
-
{slideLayoutOptions.map((opt) => (
|
|
563
|
-
<button
|
|
564
|
-
key={opt.value}
|
|
565
|
-
onClick={() => handleLayoutSelect(opt.value)}
|
|
566
|
-
className={`flex items-center gap-2 w-full px-3 py-1.5 text-xs transition-colors ${
|
|
567
|
-
currentSlide.layout === opt.value
|
|
568
|
-
? "text-[#609FF8] bg-accent/50"
|
|
569
|
-
: "text-muted-foreground hover:text-foreground hover:bg-accent/50"
|
|
570
|
-
}`}
|
|
571
|
-
>
|
|
572
|
-
<IconLayout className="w-3 h-3" />
|
|
573
|
-
{t(opt.labelKey)}
|
|
574
|
-
</button>
|
|
575
|
-
))}
|
|
576
|
-
|
|
577
|
-
{/* Background section */}
|
|
578
|
-
<div className="mx-2 my-1.5 border-t border-border" />
|
|
579
|
-
<div className="px-3 py-1.5 text-[10px] font-medium text-muted-foreground uppercase tracking-wider">
|
|
580
|
-
{t("editorToolbar.background")}
|
|
581
|
-
</div>
|
|
582
|
-
<div className="px-3 pb-2">
|
|
583
|
-
<div className="grid grid-cols-4 gap-2">
|
|
584
|
-
{backgroundOptions.map((bg, i) => (
|
|
585
|
-
<button
|
|
586
|
-
key={i}
|
|
587
|
-
onClick={() => {
|
|
588
|
-
onUpdateSlide!({ background: bg });
|
|
589
|
-
}}
|
|
590
|
-
className={`w-10 h-7 rounded-md border transition-all ${bg} ${
|
|
591
|
-
currentSlide.background === bg
|
|
592
|
-
? "border-[#609FF8] ring-1 ring-[#609FF8]/30"
|
|
593
|
-
: "border-border hover:border-foreground/20"
|
|
594
|
-
}`}
|
|
595
|
-
/>
|
|
596
|
-
))}
|
|
597
|
-
<label
|
|
598
|
-
className={`relative w-10 h-7 rounded-md border cursor-pointer overflow-hidden transition-all ${
|
|
599
|
-
isCustomBackground
|
|
600
|
-
? "border-[#609FF8] ring-1 ring-[#609FF8]/30"
|
|
601
|
-
: "border-border hover:border-foreground/20"
|
|
602
|
-
}`}
|
|
603
|
-
style={{
|
|
604
|
-
background: isCustomBackground
|
|
605
|
-
? currentSlide.background
|
|
606
|
-
: "conic-gradient(from 180deg, #ff0000, #ffff00, #00ff00, #00ffff, #0000ff, #ff00ff, #ff0000)",
|
|
607
|
-
}}
|
|
608
|
-
title={t("editorToolbar.customColor")}
|
|
609
|
-
>
|
|
610
|
-
<input
|
|
611
|
-
type="color"
|
|
612
|
-
value={toColorInputValue(currentSlide.background)}
|
|
613
|
-
onChange={(e) => {
|
|
614
|
-
onUpdateSlide!({ background: e.target.value });
|
|
615
|
-
}}
|
|
616
|
-
className="absolute inset-0 h-full w-full cursor-pointer opacity-0"
|
|
617
|
-
aria-label={t("editorToolbar.customColor")}
|
|
618
|
-
/>
|
|
619
|
-
</label>
|
|
620
|
-
</div>
|
|
621
|
-
</div>
|
|
622
|
-
|
|
623
|
-
{/* Image & Assets section */}
|
|
624
|
-
<div className="mx-2 my-1.5 border-t border-border" />
|
|
625
|
-
<div className="px-3 py-1.5 text-[10px] font-medium text-muted-foreground uppercase tracking-wider">
|
|
626
|
-
{t("editorToolbar.media")}
|
|
627
|
-
</div>
|
|
628
|
-
<button
|
|
629
|
-
ref={imageGenButtonRef}
|
|
630
|
-
onClick={() => {
|
|
631
|
-
onGenerateImage();
|
|
632
|
-
setLayoutOpen(false);
|
|
633
|
-
}}
|
|
634
|
-
className="flex items-center gap-2 w-full px-3 py-1.5 text-xs text-muted-foreground hover:text-foreground hover:bg-accent/50 transition-colors"
|
|
635
|
-
>
|
|
636
|
-
<IconPhoto className="w-3 h-3" />
|
|
637
|
-
{t("editorToolbar.generateImage")}
|
|
638
|
-
</button>
|
|
639
|
-
<button
|
|
640
|
-
ref={assetsButtonRef}
|
|
641
|
-
onClick={() => {
|
|
642
|
-
onOpenAssetLibrary();
|
|
643
|
-
setLayoutOpen(false);
|
|
644
|
-
}}
|
|
645
|
-
className="flex items-center gap-2 w-full px-3 py-1.5 text-xs text-muted-foreground hover:text-foreground hover:bg-accent/50 transition-colors"
|
|
646
|
-
>
|
|
647
|
-
<IconFolderOpen className="w-3 h-3" />
|
|
648
|
-
{t("editorToolbar.assetLibrary")}
|
|
649
|
-
</button>
|
|
650
|
-
|
|
651
|
-
{/* Diagrams section */}
|
|
652
|
-
<div className="mx-2 my-1.5 border-t border-border" />
|
|
653
|
-
<div className="px-3 py-1.5 text-[10px] font-medium text-muted-foreground uppercase tracking-wider">
|
|
654
|
-
{t("editorToolbar.diagrams")}
|
|
655
|
-
</div>
|
|
656
|
-
<button
|
|
657
|
-
onClick={async () => {
|
|
658
|
-
if (!onUpdateSlide || !currentSlide) return;
|
|
659
|
-
// Pin the slide this insert targets before the async
|
|
660
|
-
// import/parse below, so navigating to another slide in the
|
|
661
|
-
// meantime can't redirect where the diagram lands.
|
|
662
|
-
const targetSlideId = currentSlide.id;
|
|
663
|
-
const defaultMermaidDefinition = `graph TD
|
|
664
|
-
A[Start] --> B{Decision}
|
|
665
|
-
B -->|Yes| C[Action A]
|
|
666
|
-
B -->|No| D[Action B]
|
|
667
|
-
C --> E[End]
|
|
668
|
-
D --> E`;
|
|
669
|
-
try {
|
|
670
|
-
const { convertMermaidToExcalidraw } =
|
|
671
|
-
await import("./MermaidToExcalidrawPanel");
|
|
672
|
-
const data = await convertMermaidToExcalidraw(
|
|
673
|
-
defaultMermaidDefinition,
|
|
674
|
-
);
|
|
675
|
-
// The target slide may have been deleted while the import/
|
|
676
|
-
// parse above was in flight — patch-deck rejects the whole
|
|
677
|
-
// batch for a missing slide id, so re-check with the live
|
|
678
|
-
// deck (not the stale `currentSlide` this closure captured)
|
|
679
|
-
// and drop the result instead of enqueueing a dead op.
|
|
680
|
-
const stillExists = getDeck(deckId)?.slides.some(
|
|
681
|
-
(s) => s.id === targetSlideId,
|
|
682
|
-
);
|
|
683
|
-
if (!stillExists) return;
|
|
684
|
-
onUpdateSlide({ excalidrawData: data }, targetSlideId);
|
|
685
|
-
setLayoutOpen(false);
|
|
686
|
-
} catch (err) {
|
|
687
|
-
console.error("Insert Mermaid diagram failed:", err);
|
|
688
|
-
toast.error(t("editorToolbar.insertMermaidFailed"), {
|
|
689
|
-
description:
|
|
690
|
-
err instanceof Error ? err.message : undefined,
|
|
691
|
-
});
|
|
692
|
-
}
|
|
693
|
-
}}
|
|
694
|
-
className="flex items-center gap-2 w-full px-3 py-1.5 text-xs text-muted-foreground hover:text-foreground hover:bg-accent/50 transition-colors"
|
|
695
|
-
>
|
|
696
|
-
<IconSchema className="w-3 h-3" />
|
|
697
|
-
{t("editorToolbar.insertMermaidDiagram")}
|
|
698
|
-
</button>
|
|
699
|
-
<button
|
|
700
|
-
onClick={() => {
|
|
701
|
-
if (!onUpdateSlide) return;
|
|
702
|
-
onUpdateSlide({
|
|
703
|
-
excalidrawData: JSON.stringify({
|
|
704
|
-
elements: [],
|
|
705
|
-
appState: { viewBackgroundColor: "transparent" },
|
|
706
|
-
files: {},
|
|
707
|
-
}),
|
|
708
|
-
});
|
|
709
|
-
setLayoutOpen(false);
|
|
710
|
-
}}
|
|
711
|
-
className="flex items-center gap-2 w-full px-3 py-1.5 text-xs text-muted-foreground hover:text-foreground hover:bg-accent/50 transition-colors"
|
|
712
|
-
>
|
|
713
|
-
<IconPencil className="w-3 h-3" />
|
|
714
|
-
{t("editorToolbar.excalidrawCanvas")}
|
|
715
|
-
</button>
|
|
716
|
-
{typeof currentSlide?.content === "string" &&
|
|
717
|
-
currentSlide.content.includes('class="mermaid"') && (
|
|
718
|
-
<button
|
|
719
|
-
onClick={async () => {
|
|
720
|
-
if (!onUpdateSlide || !currentSlide) return;
|
|
721
|
-
// Same pinning as the insert handler above.
|
|
722
|
-
const targetSlideId = currentSlide.id;
|
|
723
|
-
try {
|
|
724
|
-
const match = currentSlide.content.match(
|
|
725
|
-
/<div\s+class="mermaid"[^>]*>([\s\S]*?)<\/div>/i,
|
|
726
|
-
);
|
|
727
|
-
if (!match) return;
|
|
728
|
-
const { convertMermaidToExcalidraw } =
|
|
729
|
-
await import("./MermaidToExcalidrawPanel");
|
|
730
|
-
const data = await convertMermaidToExcalidraw(
|
|
731
|
-
match[1].trim(),
|
|
732
|
-
);
|
|
733
|
-
// Same stale-target guard as the insert handler above.
|
|
734
|
-
const stillExists = getDeck(deckId)?.slides.some(
|
|
735
|
-
(s) => s.id === targetSlideId,
|
|
736
|
-
);
|
|
737
|
-
if (!stillExists) return;
|
|
738
|
-
onUpdateSlide({ excalidrawData: data }, targetSlideId);
|
|
739
|
-
setLayoutOpen(false);
|
|
740
|
-
} catch (err) {
|
|
741
|
-
console.error("Mermaid to Excalidraw failed:", err);
|
|
742
|
-
toast.error(t("editorToolbar.convertMermaidFailed"), {
|
|
743
|
-
description:
|
|
744
|
-
err instanceof Error ? err.message : undefined,
|
|
745
|
-
});
|
|
746
|
-
}
|
|
747
|
-
}}
|
|
748
|
-
className="flex items-center gap-2 w-full px-3 py-1.5 text-xs text-[hsl(var(--accent-cyan))] hover:bg-accent/50 transition-colors"
|
|
749
|
-
>
|
|
750
|
-
<IconTransform className="w-3 h-3" />
|
|
751
|
-
{t("editorToolbar.convertMermaidToExcalidraw")}
|
|
752
|
-
</button>
|
|
753
|
-
)}
|
|
754
|
-
{currentSlide?.excalidrawData && (
|
|
755
|
-
<button
|
|
756
|
-
onClick={() => {
|
|
757
|
-
if (!onUpdateSlide) return;
|
|
758
|
-
onUpdateSlide({ excalidrawData: undefined });
|
|
759
|
-
setLayoutOpen(false);
|
|
760
|
-
}}
|
|
761
|
-
className="flex items-center gap-2 w-full px-3 py-1.5 text-xs text-muted-foreground hover:text-muted-foreground hover:bg-accent/50 transition-colors"
|
|
762
|
-
>
|
|
763
|
-
<IconPencil className="w-3 h-3" />
|
|
764
|
-
{t("editorToolbar.removeExcalidrawCanvas")}
|
|
765
|
-
</button>
|
|
766
|
-
)}
|
|
767
|
-
|
|
768
|
-
{/* Transitions section */}
|
|
769
|
-
<div className="mx-2 my-1.5 border-t border-border" />
|
|
770
|
-
<div className="px-3 py-1.5 text-[10px] font-medium text-muted-foreground uppercase tracking-wider">
|
|
771
|
-
{t("editorToolbar.transition")}
|
|
772
|
-
</div>
|
|
773
|
-
<div className="px-3 pb-2.5 grid grid-cols-4 gap-1">
|
|
774
|
-
{(["instant", "fade", "slide", "zoom"] as const).map(
|
|
775
|
-
(transition) => {
|
|
776
|
-
const active =
|
|
777
|
-
transition === "instant"
|
|
778
|
-
? !currentSlide.transition ||
|
|
779
|
-
currentSlide.transition === "instant" ||
|
|
780
|
-
currentSlide.transition === "none"
|
|
781
|
-
: currentSlide.transition === transition;
|
|
782
|
-
return (
|
|
783
|
-
<button
|
|
784
|
-
key={transition}
|
|
785
|
-
onClick={() => onUpdateSlide!({ transition })}
|
|
786
|
-
className={`px-1.5 py-1 rounded text-[10px] font-medium capitalize border ${
|
|
787
|
-
active
|
|
788
|
-
? "bg-[#609FF8]/20 text-[#609FF8] border-[#609FF8]/30"
|
|
789
|
-
: "text-muted-foreground hover:text-foreground/70 hover:bg-accent/50 border-transparent"
|
|
790
|
-
}`}
|
|
791
|
-
>
|
|
792
|
-
{t(`editorToolbar.transition_${transition}`)}
|
|
793
|
-
</button>
|
|
794
|
-
);
|
|
795
|
-
},
|
|
796
|
-
)}
|
|
797
|
-
</div>
|
|
798
|
-
|
|
799
|
-
{/* Aspect Ratio section (deck-level) */}
|
|
800
|
-
{onSetAspectRatio && (
|
|
801
|
-
<>
|
|
802
|
-
<div className="mx-2 my-1.5 border-t border-border" />
|
|
803
|
-
<div className="px-3 py-1.5 text-[10px] font-medium text-muted-foreground uppercase tracking-wider">
|
|
804
|
-
{t("editorToolbar.aspectRatio")}
|
|
805
|
-
</div>
|
|
806
|
-
<div className="px-3 pb-2.5 grid grid-cols-4 gap-1">
|
|
807
|
-
{ASPECT_RATIO_VALUES.map((r) => {
|
|
808
|
-
const active = activeAspectRatio === r;
|
|
809
|
-
return (
|
|
810
|
-
<Tooltip key={r}>
|
|
811
|
-
<TooltipTrigger asChild>
|
|
812
|
-
<button
|
|
813
|
-
onClick={() => onSetAspectRatio(r)}
|
|
814
|
-
className={`px-1.5 py-1 rounded text-[10px] font-medium border ${
|
|
815
|
-
active
|
|
816
|
-
? "bg-[#609FF8]/20 text-[#609FF8] border-[#609FF8]/30"
|
|
817
|
-
: "text-muted-foreground hover:text-foreground hover:bg-accent/50 border-transparent"
|
|
818
|
-
}`}
|
|
819
|
-
>
|
|
820
|
-
{r}
|
|
821
|
-
</button>
|
|
822
|
-
</TooltipTrigger>
|
|
823
|
-
<TooltipContent>
|
|
824
|
-
{t("editorToolbar.setAspectRatio", { ratio: r })}
|
|
825
|
-
</TooltipContent>
|
|
826
|
-
</Tooltip>
|
|
827
|
-
);
|
|
828
|
-
})}
|
|
829
|
-
</div>
|
|
830
|
-
</>
|
|
831
|
-
)}
|
|
832
|
-
</div>
|
|
833
|
-
</ToolbarPopover>
|
|
834
|
-
</>
|
|
835
|
-
)}
|
|
836
|
-
|
|
837
|
-
{/* Slide tools palette — animations, tweaks, draw, comment-pin all live
|
|
838
|
-
* inside one popover so the toolbar doesn't drown in icons. Hidden in
|
|
839
|
-
* view-only mode since none of these affordances apply. */}
|
|
840
|
-
{canEdit &&
|
|
841
|
-
(onToggleAnimations ||
|
|
842
|
-
onToggleTweaks ||
|
|
843
|
-
onToggleDrawMode ||
|
|
844
|
-
onTogglePinMode) && (
|
|
845
|
-
<DropdownMenu
|
|
846
|
-
open={toolsOpen}
|
|
847
|
-
onOpenChange={(open) => {
|
|
848
|
-
if (open) setLayoutOpen(false);
|
|
849
|
-
setToolsOpen(open);
|
|
850
|
-
}}
|
|
851
|
-
>
|
|
852
|
-
<Tooltip>
|
|
853
|
-
<TooltipTrigger asChild>
|
|
854
|
-
<DropdownMenuTrigger asChild>
|
|
855
|
-
<button
|
|
856
|
-
className={`${TOOLBAR_ICON_BUTTON_CLASS} relative cursor-pointer ${
|
|
857
|
-
anyToolActive || toolsOpen
|
|
858
|
-
? "bg-accent text-foreground"
|
|
859
|
-
: "text-muted-foreground hover:text-foreground/70 hover:bg-accent"
|
|
860
|
-
}`}
|
|
861
|
-
aria-label={t("editorToolbar.slideTools")}
|
|
862
|
-
>
|
|
863
|
-
<IconTool className="size-4" />
|
|
864
|
-
{anyToolActive && !toolsOpen && (
|
|
865
|
-
<span className="absolute top-1 right-1 w-1.5 h-1.5 rounded-full bg-[#609FF8]" />
|
|
866
|
-
)}
|
|
867
|
-
</button>
|
|
868
|
-
</DropdownMenuTrigger>
|
|
869
|
-
</TooltipTrigger>
|
|
870
|
-
<TooltipContent>{t("editorToolbar.slideTools")}</TooltipContent>
|
|
871
|
-
</Tooltip>
|
|
872
|
-
<DropdownMenuContent align="end" className="w-52">
|
|
873
|
-
<DropdownMenuGroup>
|
|
874
|
-
{currentSlide && onToggleAnimations && (
|
|
875
|
-
<DropdownMenuItem
|
|
876
|
-
onSelect={onToggleAnimations}
|
|
877
|
-
className={
|
|
878
|
-
animationsOpen
|
|
879
|
-
? "bg-accent text-accent-foreground"
|
|
880
|
-
: undefined
|
|
881
|
-
}
|
|
882
|
-
>
|
|
883
|
-
<IconBolt className="size-4" />
|
|
884
|
-
{t("editorToolbar.elementAnimations")}
|
|
885
|
-
</DropdownMenuItem>
|
|
886
|
-
)}
|
|
887
|
-
{onToggleTweaks && (
|
|
888
|
-
<DropdownMenuItem
|
|
889
|
-
onSelect={onToggleTweaks}
|
|
890
|
-
className={
|
|
891
|
-
tweaksOpen
|
|
892
|
-
? "bg-accent text-accent-foreground"
|
|
893
|
-
: undefined
|
|
894
|
-
}
|
|
895
|
-
>
|
|
896
|
-
<IconAdjustments className="size-4" />
|
|
897
|
-
{t("editorToolbar.tweaks")}
|
|
898
|
-
</DropdownMenuItem>
|
|
899
|
-
)}
|
|
900
|
-
{onToggleDrawMode && (
|
|
901
|
-
<DropdownMenuItem
|
|
902
|
-
onSelect={onToggleDrawMode}
|
|
903
|
-
data-toolbar-draw-button
|
|
904
|
-
className={
|
|
905
|
-
drawMode ? "bg-accent text-accent-foreground" : undefined
|
|
906
|
-
}
|
|
907
|
-
>
|
|
908
|
-
<IconPencilPlus className="size-4" />
|
|
909
|
-
{t("editorToolbar.drawOnSlide")}
|
|
910
|
-
</DropdownMenuItem>
|
|
911
|
-
)}
|
|
912
|
-
{onTogglePinMode && (
|
|
913
|
-
<DropdownMenuItem
|
|
914
|
-
onSelect={onTogglePinMode}
|
|
915
|
-
data-toolbar-pin-button
|
|
916
|
-
className={
|
|
917
|
-
pinMode ? "bg-accent text-accent-foreground" : undefined
|
|
918
|
-
}
|
|
919
|
-
>
|
|
920
|
-
<IconPin className="size-4" />
|
|
921
|
-
{t("editorToolbar.pinComments")}
|
|
922
|
-
</DropdownMenuItem>
|
|
923
|
-
)}
|
|
924
|
-
</DropdownMenuGroup>
|
|
925
|
-
</DropdownMenuContent>
|
|
926
|
-
</DropdownMenu>
|
|
927
|
-
)}
|
|
928
|
-
|
|
929
614
|
{/* Save status — subtle "Saving…" / "Saved" / offline pill. Renders
|
|
930
615
|
nothing when idle. Only meaningful for editors. */}
|
|
931
616
|
{canEdit && (
|
|
@@ -941,45 +626,193 @@ export default function EditorToolbar({
|
|
|
941
626
|
activeUsers={activeUsers ?? []}
|
|
942
627
|
agentPresent={agentPresent}
|
|
943
628
|
agentActive={agentActive}
|
|
629
|
+
showAgentEditingDot={false}
|
|
944
630
|
currentUserEmail={currentUserEmail}
|
|
945
631
|
className="flex-shrink-0 mr-0.5"
|
|
946
632
|
/>
|
|
947
633
|
|
|
948
|
-
{/*
|
|
949
|
-
|
|
950
|
-
<
|
|
951
|
-
<
|
|
952
|
-
<
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
634
|
+
{/* Consolidated editor menu */}
|
|
635
|
+
<div className="flex-shrink-0">
|
|
636
|
+
<DropdownMenu>
|
|
637
|
+
<Tooltip>
|
|
638
|
+
<TooltipTrigger asChild>
|
|
639
|
+
<DropdownMenuTrigger asChild>
|
|
640
|
+
<Button
|
|
641
|
+
type="button"
|
|
642
|
+
variant="ghost"
|
|
643
|
+
size="icon"
|
|
644
|
+
ref={historyButtonRef}
|
|
645
|
+
className={`${TOOLBAR_ICON_BUTTON_CLASS} cursor-pointer text-muted-foreground hover:bg-accent hover:text-foreground/70`}
|
|
646
|
+
aria-label={t("editorToolbar.more")}
|
|
647
|
+
>
|
|
648
|
+
<IconDotsVertical className="size-4" />
|
|
649
|
+
</Button>
|
|
650
|
+
</DropdownMenuTrigger>
|
|
651
|
+
</TooltipTrigger>
|
|
652
|
+
<TooltipContent>{t("editorToolbar.more")}</TooltipContent>
|
|
653
|
+
</Tooltip>
|
|
654
|
+
<DropdownMenuContent
|
|
655
|
+
align="end"
|
|
656
|
+
className="max-h-[min(80vh,32rem)] w-64 overflow-y-auto"
|
|
657
|
+
>
|
|
658
|
+
{canEdit && (
|
|
659
|
+
<>
|
|
660
|
+
<DropdownMenuLabel>
|
|
661
|
+
{t("editorToolbar.media")}
|
|
662
|
+
</DropdownMenuLabel>
|
|
663
|
+
<DropdownMenuGroup>
|
|
664
|
+
<DropdownMenuItem onSelect={onGenerateImage}>
|
|
665
|
+
<IconPhoto className="size-4" />
|
|
666
|
+
{t("editorToolbar.generateImage")}
|
|
667
|
+
</DropdownMenuItem>
|
|
668
|
+
<DropdownMenuItem onSelect={onOpenAssetLibrary}>
|
|
669
|
+
<IconFolderOpen className="size-4" />
|
|
670
|
+
{t("editorToolbar.assetLibrary")}
|
|
671
|
+
</DropdownMenuItem>
|
|
672
|
+
</DropdownMenuGroup>
|
|
673
|
+
</>
|
|
674
|
+
)}
|
|
675
|
+
|
|
676
|
+
{canEdit &&
|
|
677
|
+
(onToggleAnimations ||
|
|
678
|
+
onToggleTweaks ||
|
|
679
|
+
onToggleDrawMode ||
|
|
680
|
+
onTogglePinMode) && (
|
|
681
|
+
<>
|
|
682
|
+
<DropdownMenuSeparator />
|
|
683
|
+
<DropdownMenuLabel>
|
|
684
|
+
{t("editorToolbar.slideTools")}
|
|
685
|
+
</DropdownMenuLabel>
|
|
686
|
+
<DropdownMenuGroup>
|
|
687
|
+
{currentSlide && onToggleAnimations && (
|
|
688
|
+
<DropdownMenuItem
|
|
689
|
+
onSelect={onToggleAnimations}
|
|
690
|
+
className={
|
|
691
|
+
animationsOpen
|
|
692
|
+
? "bg-accent text-accent-foreground"
|
|
693
|
+
: undefined
|
|
694
|
+
}
|
|
695
|
+
>
|
|
696
|
+
<IconBolt className="size-4" />
|
|
697
|
+
{t("editorToolbar.elementAnimations")}
|
|
698
|
+
</DropdownMenuItem>
|
|
699
|
+
)}
|
|
700
|
+
{onToggleTweaks && (
|
|
701
|
+
<DropdownMenuItem
|
|
702
|
+
onSelect={onToggleTweaks}
|
|
703
|
+
className={
|
|
704
|
+
tweaksOpen
|
|
705
|
+
? "bg-accent text-accent-foreground"
|
|
706
|
+
: undefined
|
|
707
|
+
}
|
|
708
|
+
>
|
|
709
|
+
<IconAdjustments className="size-4" />
|
|
710
|
+
{t("editorToolbar.tweaks")}
|
|
711
|
+
</DropdownMenuItem>
|
|
712
|
+
)}
|
|
713
|
+
{onToggleDrawMode && (
|
|
714
|
+
<DropdownMenuItem
|
|
715
|
+
onSelect={onToggleDrawMode}
|
|
716
|
+
data-toolbar-draw-button
|
|
717
|
+
className={
|
|
718
|
+
drawMode
|
|
719
|
+
? "bg-accent text-accent-foreground"
|
|
720
|
+
: undefined
|
|
721
|
+
}
|
|
722
|
+
>
|
|
723
|
+
<IconPencilPlus className="size-4" />
|
|
724
|
+
{t("editorToolbar.drawOnSlide")}
|
|
725
|
+
</DropdownMenuItem>
|
|
726
|
+
)}
|
|
727
|
+
{onTogglePinMode && (
|
|
728
|
+
<DropdownMenuItem
|
|
729
|
+
onSelect={onTogglePinMode}
|
|
730
|
+
data-toolbar-pin-button
|
|
731
|
+
className={
|
|
732
|
+
pinMode
|
|
733
|
+
? "bg-accent text-accent-foreground"
|
|
734
|
+
: undefined
|
|
735
|
+
}
|
|
736
|
+
>
|
|
737
|
+
<IconPin className="size-4" />
|
|
738
|
+
{t("editorToolbar.pinComments")}
|
|
739
|
+
</DropdownMenuItem>
|
|
740
|
+
)}
|
|
741
|
+
</DropdownMenuGroup>
|
|
742
|
+
</>
|
|
966
743
|
)}
|
|
967
|
-
</button>
|
|
968
|
-
</TooltipTrigger>
|
|
969
|
-
<TooltipContent>{t("editorToolbar.comments")}</TooltipContent>
|
|
970
|
-
</Tooltip>
|
|
971
|
-
)}
|
|
972
744
|
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
745
|
+
{onToggleComments && (
|
|
746
|
+
<>
|
|
747
|
+
<DropdownMenuSeparator />
|
|
748
|
+
<DropdownMenuLabel>
|
|
749
|
+
{t("editorToolbar.comments")}
|
|
750
|
+
</DropdownMenuLabel>
|
|
751
|
+
<DropdownMenuItem
|
|
752
|
+
onSelect={onToggleComments}
|
|
753
|
+
className={
|
|
754
|
+
commentsOpen
|
|
755
|
+
? "bg-accent text-accent-foreground"
|
|
756
|
+
: undefined
|
|
757
|
+
}
|
|
758
|
+
>
|
|
759
|
+
<IconMessage className="size-4" />
|
|
760
|
+
{t("editorToolbar.comments")}
|
|
761
|
+
{unresolvedCommentCount > 0 && (
|
|
762
|
+
<span className="ml-auto rounded-full bg-primary px-1.5 text-[10px] font-semibold text-primary-foreground">
|
|
763
|
+
{unresolvedCommentCount > 9
|
|
764
|
+
? "9+"
|
|
765
|
+
: unresolvedCommentCount}
|
|
766
|
+
</span>
|
|
767
|
+
)}
|
|
768
|
+
</DropdownMenuItem>
|
|
769
|
+
</>
|
|
770
|
+
)}
|
|
771
|
+
|
|
772
|
+
<DropdownMenuSeparator />
|
|
773
|
+
<ExportMenu
|
|
774
|
+
ref={exportMenuRef}
|
|
775
|
+
inline
|
|
776
|
+
deckId={deckId}
|
|
777
|
+
deckTitle={deckTitle}
|
|
778
|
+
onDuplicate={onDuplicateDeck ?? (() => {})}
|
|
779
|
+
onExportPdf={onExportPdf ?? (() => {})}
|
|
780
|
+
onExportPptx={onExportPptx ?? (() => {})}
|
|
781
|
+
onExportGoogleSlides={onExportGoogleSlides}
|
|
782
|
+
/>
|
|
783
|
+
<DropdownMenuSeparator />
|
|
784
|
+
<DropdownMenuItem
|
|
785
|
+
disabled={importing}
|
|
786
|
+
onSelect={() => fileInputRef.current?.click()}
|
|
787
|
+
>
|
|
788
|
+
{importing ? (
|
|
789
|
+
<IconLoader2 className="size-4 animate-spin" />
|
|
790
|
+
) : (
|
|
791
|
+
<IconDownload className="size-4" />
|
|
792
|
+
)}
|
|
793
|
+
{importing
|
|
794
|
+
? t("editorToolbar.importing")
|
|
795
|
+
: t("editorToolbar.importFile")}
|
|
796
|
+
</DropdownMenuItem>
|
|
797
|
+
<DropdownMenuItem onSelect={onShowHistory}>
|
|
798
|
+
<IconHistory className="size-4" />
|
|
799
|
+
{t("editorToolbar.savedVersions")}
|
|
800
|
+
</DropdownMenuItem>
|
|
801
|
+
<DropdownMenuSeparator />
|
|
802
|
+
<DropdownMenuItem
|
|
803
|
+
onSelect={() => setTheme(isDark ? "light" : "dark")}
|
|
804
|
+
>
|
|
805
|
+
{isDark ? (
|
|
806
|
+
<IconSun className="size-4" />
|
|
807
|
+
) : (
|
|
808
|
+
<IconMoon className="size-4" />
|
|
809
|
+
)}
|
|
810
|
+
{isDark
|
|
811
|
+
? t("editorToolbar.lightTheme")
|
|
812
|
+
: t("editorToolbar.darkTheme")}
|
|
813
|
+
</DropdownMenuItem>
|
|
814
|
+
</DropdownMenuContent>
|
|
815
|
+
</DropdownMenu>
|
|
983
816
|
</div>
|
|
984
817
|
|
|
985
818
|
{/* Framework share (ownership, per-user/org grants, visibility) */}
|
|
@@ -1018,7 +851,7 @@ export default function EditorToolbar({
|
|
|
1018
851
|
</div>
|
|
1019
852
|
{/* Present button — matches Share trigger height (h-9) */}
|
|
1020
853
|
<Link
|
|
1021
|
-
to={`/deck/${deckId}/present`}
|
|
854
|
+
to={`/deck/${deckId}/present?slide=${currentSlideIndex + 1}`}
|
|
1022
855
|
className="inline-flex h-9 flex-shrink-0 items-center justify-center gap-1.5 rounded-md border border-border bg-primary px-3 text-sm font-medium text-primary-foreground transition-colors hover:bg-primary/90"
|
|
1023
856
|
>
|
|
1024
857
|
<IconPlayerPlay className="w-3.5 h-3.5" />
|
|
@@ -1034,79 +867,10 @@ export default function EditorToolbar({
|
|
|
1034
867
|
className="hidden"
|
|
1035
868
|
/>
|
|
1036
869
|
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
<
|
|
1040
|
-
|
|
1041
|
-
<DropdownMenuTrigger asChild>
|
|
1042
|
-
<button
|
|
1043
|
-
ref={historyButtonRef}
|
|
1044
|
-
className={`${TOOLBAR_ICON_BUTTON_CLASS} cursor-pointer text-muted-foreground hover:bg-accent hover:text-foreground/70`}
|
|
1045
|
-
aria-label={t("editorToolbar.more")}
|
|
1046
|
-
>
|
|
1047
|
-
<IconDotsVertical className="size-4" />
|
|
1048
|
-
</button>
|
|
1049
|
-
</DropdownMenuTrigger>
|
|
1050
|
-
</TooltipTrigger>
|
|
1051
|
-
<TooltipContent>{t("editorToolbar.more")}</TooltipContent>
|
|
1052
|
-
</Tooltip>
|
|
1053
|
-
<DropdownMenuContent align="end" className="w-48">
|
|
1054
|
-
{canEdit && (
|
|
1055
|
-
<>
|
|
1056
|
-
<DropdownMenuItem
|
|
1057
|
-
disabled={!canUndo}
|
|
1058
|
-
onSelect={() => commitActiveEditThenRun(undo)}
|
|
1059
|
-
>
|
|
1060
|
-
<IconArrowBackUp className="w-4 h-4 mr-2" />
|
|
1061
|
-
{t("editorToolbar.undoWithShortcut", {
|
|
1062
|
-
shortcut: shortcutLabel("Cmd+Z"),
|
|
1063
|
-
})}
|
|
1064
|
-
</DropdownMenuItem>
|
|
1065
|
-
<DropdownMenuItem
|
|
1066
|
-
disabled={!canRedo}
|
|
1067
|
-
onSelect={() => commitActiveEditThenRun(redo)}
|
|
1068
|
-
>
|
|
1069
|
-
<IconArrowForwardUp className="w-4 h-4 mr-2" />
|
|
1070
|
-
{t("editorToolbar.redoWithShortcut", {
|
|
1071
|
-
shortcut: shortcutLabel("Cmd+Shift+Z"),
|
|
1072
|
-
})}
|
|
1073
|
-
</DropdownMenuItem>
|
|
1074
|
-
<DropdownMenuSeparator />
|
|
1075
|
-
</>
|
|
1076
|
-
)}
|
|
1077
|
-
<DropdownMenuItem
|
|
1078
|
-
disabled={importing}
|
|
1079
|
-
onSelect={() => fileInputRef.current?.click()}
|
|
1080
|
-
>
|
|
1081
|
-
{importing ? (
|
|
1082
|
-
<IconLoader2 className="w-4 h-4 mr-2 animate-spin" />
|
|
1083
|
-
) : (
|
|
1084
|
-
<IconDownload className="w-4 h-4 mr-2" />
|
|
1085
|
-
)}
|
|
1086
|
-
{importing
|
|
1087
|
-
? t("editorToolbar.importing")
|
|
1088
|
-
: t("editorToolbar.importFile")}
|
|
1089
|
-
</DropdownMenuItem>
|
|
1090
|
-
<DropdownMenuItem onClick={onShowHistory}>
|
|
1091
|
-
<IconHistory className="w-4 h-4 mr-2" />
|
|
1092
|
-
{t("editorToolbar.savedVersions")}
|
|
1093
|
-
</DropdownMenuItem>
|
|
1094
|
-
<DropdownMenuSeparator />
|
|
1095
|
-
<DropdownMenuItem onClick={() => setTheme(isDark ? "light" : "dark")}>
|
|
1096
|
-
{isDark ? (
|
|
1097
|
-
<IconSun className="w-4 h-4 mr-2" />
|
|
1098
|
-
) : (
|
|
1099
|
-
<IconMoon className="w-4 h-4 mr-2" />
|
|
1100
|
-
)}
|
|
1101
|
-
{isDark
|
|
1102
|
-
? t("editorToolbar.lightTheme")
|
|
1103
|
-
: t("editorToolbar.darkTheme")}
|
|
1104
|
-
</DropdownMenuItem>
|
|
1105
|
-
</DropdownMenuContent>
|
|
1106
|
-
</DropdownMenu>
|
|
1107
|
-
|
|
1108
|
-
<RunsTray pollMs={0} />
|
|
1109
|
-
<AgentToggleButton />
|
|
870
|
+
<div className="flex items-center gap-1">
|
|
871
|
+
<RunsTray pollMs={0} />
|
|
872
|
+
<AgentToggleButton />
|
|
873
|
+
</div>
|
|
1110
874
|
</div>
|
|
1111
875
|
);
|
|
1112
876
|
}
|