@agent-native/core 0.92.10 → 0.92.11
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/core/CHANGELOG.md +23 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/production-agent.ts +181 -15
- package/corpus/core/src/agent/run-manager.ts +31 -4
- package/corpus/core/src/agent/run-store.ts +80 -0
- package/corpus/core/src/cli/design-connect.ts +433 -151
- package/corpus/core/src/cli/skills.ts +14 -0
- package/corpus/core/src/client/AssistantChat.tsx +25 -0
- package/corpus/core/src/client/MultiTabAssistantChat.tsx +40 -6
- package/corpus/core/src/client/agent-chat.ts +165 -1
- package/corpus/core/src/client/chat/index.ts +9 -1
- package/corpus/core/src/client/extensions/EmbeddedExtension.tsx +38 -2
- package/corpus/core/src/client/index.ts +5 -0
- package/corpus/core/src/client/rich-markdown-editor/useCollabReconcile.ts +18 -7
- package/corpus/core/src/collab/awareness-store.ts +23 -12
- package/corpus/core/src/collab/client.ts +16 -5
- package/corpus/core/src/collab/index.ts +4 -0
- package/corpus/core/src/collab/recent-edits.ts +65 -2
- package/corpus/core/src/index.browser.ts +1 -0
- package/corpus/core/src/server/agent-chat-plugin.ts +98 -11
- package/corpus/core/src/server/builder-design-systems.ts +30 -4
- package/corpus/templates/clips/.agents/skills/ai-video-tools/SKILL.md +1 -1
- package/corpus/templates/clips/.agents/skills/video-sharing/SKILL.md +12 -6
- package/corpus/templates/clips/AGENTS.md +4 -0
- package/corpus/templates/clips/actions/create-recording.ts +2 -0
- package/corpus/templates/clips/actions/lib/create-recording-schema.ts +5 -3
- package/corpus/templates/clips/actions/request-transcript.ts +102 -33
- package/corpus/templates/clips/app/components/library/library-grid.tsx +1 -0
- package/corpus/templates/clips/app/components/player/share-dialog.tsx +59 -19
- package/corpus/templates/clips/app/components/player/transcript-panel.tsx +48 -4
- package/corpus/templates/clips/app/components/player/video-player.tsx +17 -12
- package/corpus/templates/clips/app/i18n/ar-SA.ts +1 -0
- package/corpus/templates/clips/app/i18n/de-DE.ts +1 -0
- package/corpus/templates/clips/app/i18n/en-US.ts +1 -0
- package/corpus/templates/clips/app/i18n/es-ES.ts +1 -0
- package/corpus/templates/clips/app/i18n/fr-FR.ts +1 -0
- package/corpus/templates/clips/app/i18n/hi-IN.ts +1 -0
- package/corpus/templates/clips/app/i18n/ja-JP.ts +1 -0
- package/corpus/templates/clips/app/i18n/ko-KR.ts +1 -0
- package/corpus/templates/clips/app/i18n/pt-BR.ts +1 -0
- package/corpus/templates/clips/app/i18n/zh-CN.ts +1 -0
- package/corpus/templates/clips/app/i18n/zh-TW.ts +1 -0
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +70 -37
- package/corpus/templates/clips/app/routes/record.tsx +17 -4
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +2 -0
- package/corpus/templates/clips/changelog/2026-07-09-desktop-popover-sits-closer-to-the-menu-bar.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-09-desktop-recording-optimization-progress-stays-visible-until-the-clip-opens.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-09-fixed-large-video-file-uploads-failing-before-their-resumabl.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-09-fixed-new-video-uploads-defaulting-to-private.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-09-long-recordings-now-get-enough-time-to-download-their-media-.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-09-meeting-notes-no-longer-lowers-your-microphone-volume-for-ot.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-09-on-mobile-tapping-a-playing-clip-reveals-playback-controls-w.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-09-regenerate-any-clip-transcript-from-its-saved-recording-or-r.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-09-slack-previews-now-show-the-clip-length-alongside-its-descri.md +6 -0
- package/corpus/templates/clips/desktop/src/app.tsx +12 -2
- package/corpus/templates/clips/desktop/src/hooks/useMeetingTranscription.ts +8 -1
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +8 -5
- package/corpus/templates/clips/desktop/src/lib/transcription-engine.ts +5 -5
- package/corpus/templates/clips/desktop/src/lib/voice-dictation.ts +3 -0
- package/corpus/templates/clips/desktop/src/styles.css +6 -3
- package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +29 -11
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +1 -1
- package/corpus/templates/clips/desktop/src-tauri/src/system_audio.rs +103 -21
- package/corpus/templates/clips/desktop/src-tauri/src/whisper_speech.rs +117 -39
- package/corpus/templates/clips/server/lib/slack-unfurls.ts +20 -1
- package/corpus/templates/clips/server/lib/streaming-upload-mode.ts +10 -4
- package/corpus/templates/clips/shared/recording-core.ts +12 -0
- package/corpus/templates/content/AGENTS.md +7 -6
- package/corpus/templates/content/actions/_document-flush.ts +132 -17
- package/corpus/templates/content/actions/_notion-action-utils.ts +15 -0
- package/corpus/templates/content/actions/create-and-link-notion-page.ts +2 -0
- package/corpus/templates/content/actions/link-notion-page.ts +2 -0
- package/corpus/templates/content/actions/pull-document.ts +5 -7
- package/corpus/templates/content/actions/pull-notion-page.ts +2 -0
- package/corpus/templates/content/actions/push-notion-page.ts +10 -0
- package/corpus/templates/content/actions/resolve-notion-sync-conflict.ts +2 -0
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +88 -23
- package/corpus/templates/content/app/components/editor/VisualEditor.tsx +32 -2
- package/corpus/templates/content/app/hooks/use-notion.ts +6 -6
- package/corpus/templates/content/app/i18n/zh-TW.ts +1 -0
- package/corpus/templates/content/app/i18n-data.ts +18 -0
- package/corpus/templates/content/changelog/2026-07-09-notion-conflict-choices-and-version-restores-preserve-docume.md +6 -0
- package/corpus/templates/content/server/lib/notion-sync.ts +80 -64
- package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +29 -2
- package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +86 -0
- package/corpus/templates/design/.agents/skills/export-handoff/SKILL.md +57 -0
- package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +11 -0
- package/corpus/templates/design/AGENTS.md +45 -10
- package/corpus/templates/design/actions/add-localhost-screens.ts +66 -13
- package/corpus/templates/design/actions/apply-component-prop-edit.ts +45 -36
- package/corpus/templates/design/actions/apply-motion-edit.ts +25 -31
- package/corpus/templates/design/actions/apply-shader-fill.ts +29 -42
- package/corpus/templates/design/actions/detach-component-instance.ts +337 -0
- package/corpus/templates/design/actions/export-design-as-figma-svg.ts +219 -0
- package/corpus/templates/design/actions/generate-design.ts +79 -12
- package/corpus/templates/design/actions/generate-screens.ts +57 -12
- package/corpus/templates/design/actions/get-figma-styles.ts +115 -0
- package/corpus/templates/design/actions/go-to-main-component.ts +254 -0
- package/corpus/templates/design/actions/grant-localhost-write-consent.ts +4 -3
- package/corpus/templates/design/actions/import-design-source.ts +4 -31
- package/corpus/templates/design/actions/import-figma-clipboard.ts +134 -0
- package/corpus/templates/design/actions/import-figma-frame.ts +95 -0
- package/corpus/templates/design/actions/index-design-system-with-builder.ts +16 -1
- package/corpus/templates/design/actions/list-design-components.ts +104 -0
- package/corpus/templates/design/actions/list-figma-library-assets.ts +1 -24
- package/corpus/templates/design/actions/open-component-source.ts +2 -4
- package/corpus/templates/design/actions/present-design-variants.ts +412 -16
- package/corpus/templates/design/actions/preview-source-edit.ts +14 -1
- package/corpus/templates/design/actions/run-design-audit.ts +25 -3
- package/corpus/templates/design/actions/show-design-questions.ts +10 -0
- package/corpus/templates/design/actions/swap-component-instance.ts +544 -0
- package/corpus/templates/design/actions/take-design-screenshot.ts +45 -60
- package/corpus/templates/design/actions/write-local-file.ts +25 -1
- package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +152 -0
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +1077 -58
- package/corpus/templates/design/app/components/design/DesignEditorSkeleton.tsx +5 -5
- package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +73 -21
- package/corpus/templates/design/app/components/design/EditPanel.tsx +16 -11
- package/corpus/templates/design/app/components/design/KeyboardShortcutsPanel.tsx +364 -0
- package/corpus/templates/design/app/components/design/LayersPanel.tsx +180 -53
- package/corpus/templates/design/app/components/design/LocalhostWriteConsentDialog.tsx +0 -2
- package/corpus/templates/design/app/components/design/MotionDock.tsx +144 -21
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +1152 -167
- package/corpus/templates/design/app/components/design/QuestionFlow.tsx +27 -5
- package/corpus/templates/design/app/components/design/ReviewPanel.tsx +23 -2
- package/corpus/templates/design/app/components/design/TokensPanel.tsx +63 -49
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +1293 -54
- package/corpus/templates/design/app/components/design/bridge/embedded-wheel.bridge.ts +7 -0
- package/corpus/templates/design/app/components/design/bridge/hit-test.bridge.ts +6 -1
- package/corpus/templates/design/app/components/design/bridge/motion-preview.bridge.ts +23 -6
- package/corpus/templates/design/app/components/design/bridge/sample.bridge.ts +7 -0
- package/corpus/templates/design/app/components/design/code-workbench/CodeWorkbench.tsx +5 -0
- package/corpus/templates/design/app/components/design/code-workbench/editor/MonacoHost.tsx +14 -2
- package/corpus/templates/design/app/components/design/code-workbench/explorer/FileTree.tsx +37 -2
- package/corpus/templates/design/app/components/design/code-workbench/format/format-on-open-guard.ts +25 -0
- package/corpus/templates/design/app/components/design/code-workbench/format/format-on-open.ts +19 -1
- package/corpus/templates/design/app/components/design/code-workbench/model-registry.ts +43 -5
- package/corpus/templates/design/app/components/design/code-workbench/search/SearchView.tsx +27 -2
- package/corpus/templates/design/app/components/design/code-workbench/search/search-engine.ts +10 -1
- package/corpus/templates/design/app/components/design/code-workbench/store.tsx +19 -2
- package/corpus/templates/design/app/components/design/design-canvas/annotation-snapshot.ts +265 -0
- package/corpus/templates/design/app/components/design/design-canvas/annotation-submit.ts +16 -5
- package/corpus/templates/design/app/components/design/design-canvas/coordinate-transforms.ts +16 -2
- package/corpus/templates/design/app/components/design/design-canvas/embedded-frame.ts +7 -2
- package/corpus/templates/design/app/components/design/design-canvas/iframe-events.ts +3 -1
- package/corpus/templates/design/app/components/design/edit-panel/code-inspect-helpers.tsx +78 -8
- package/corpus/templates/design/app/components/design/edit-panel/component-section.tsx +451 -73
- package/corpus/templates/design/app/components/design/edit-panel/document-colors.ts +12 -4
- package/corpus/templates/design/app/components/design/edit-panel/effects-properties.tsx +398 -272
- package/corpus/templates/design/app/components/design/edit-panel/element-classification.ts +41 -7
- package/corpus/templates/design/app/components/design/edit-panel/element-identity.ts +16 -3
- package/corpus/templates/design/app/components/design/edit-panel/field-primitives.tsx +24 -4
- package/corpus/templates/design/app/components/design/edit-panel/fill-gradient-helpers.ts +222 -4
- package/corpus/templates/design/app/components/design/edit-panel/fill-properties.tsx +171 -78
- package/corpus/templates/design/app/components/design/edit-panel/inspector-controls.tsx +45 -5
- package/corpus/templates/design/app/components/design/edit-panel/layout-properties.tsx +299 -28
- package/corpus/templates/design/app/components/design/edit-panel/panel-primitives.tsx +120 -150
- package/corpus/templates/design/app/components/design/edit-panel/position-helpers.ts +53 -1
- package/corpus/templates/design/app/components/design/edit-panel/position-layout-properties.tsx +284 -117
- package/corpus/templates/design/app/components/design/edit-panel/selection-helpers.ts +81 -0
- package/corpus/templates/design/app/components/design/edit-panel/stroke-properties.tsx +19 -14
- package/corpus/templates/design/app/components/design/edit-panel/style-options.ts +7 -0
- package/corpus/templates/design/app/components/design/edit-panel/typography-helpers.ts +155 -0
- package/corpus/templates/design/app/components/design/edit-panel/typography-properties.tsx +268 -46
- package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +350 -31
- package/corpus/templates/design/app/components/design/inspector/ConstraintsWidget.tsx +13 -4
- package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +233 -23
- package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +72 -2
- package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +51 -11
- package/corpus/templates/design/app/components/design/inspector/ShaderFillsPanel.tsx +84 -11
- package/corpus/templates/design/app/components/design/inspector/index.ts +3 -0
- package/corpus/templates/design/app/components/design/keyboard-shortcuts.ts +552 -0
- package/corpus/templates/design/app/components/design/multi-screen/board-surface-html.ts +157 -0
- package/corpus/templates/design/app/components/design/multi-screen/frame-geometry.ts +53 -0
- package/corpus/templates/design/app/components/design/multi-screen/overview-layout.ts +184 -0
- package/corpus/templates/design/app/components/design/multi-screen/primitive-drop-target.ts +403 -26
- package/corpus/templates/design/app/components/design/multi-screen/types.ts +20 -1
- package/corpus/templates/design/app/components/design/types.ts +33 -0
- package/corpus/templates/design/app/components/editor/PromptDialog.tsx +59 -7
- package/corpus/templates/design/app/components/visual-editor/CanvasCommentPins.tsx +119 -17
- package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +275 -63
- package/corpus/templates/design/app/hooks/use-question-flow.ts +14 -3
- package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +67 -10
- package/corpus/templates/design/app/i18n/zh-TW.ts +91 -1
- package/corpus/templates/design/app/i18n-data.ts +1217 -10
- package/corpus/templates/design/app/i18n-keyboard-shortcuts.ts +577 -0
- package/corpus/templates/design/app/lib/agent-chat.ts +23 -0
- package/corpus/templates/design/app/lib/design-import.ts +24 -0
- package/corpus/templates/design/app/lib/figma-clipboard.ts +157 -0
- package/corpus/templates/design/app/lib/figma-svg-copy.ts +221 -0
- package/corpus/templates/design/app/pages/design-editor/code-layer-state.ts +143 -30
- package/corpus/templates/design/app/pages/design-editor/collab-sync.ts +15 -0
- package/corpus/templates/design/app/pages/design-editor/editor-session.ts +33 -0
- package/corpus/templates/design/app/pages/design-editor/editor-state.ts +38 -0
- package/corpus/templates/design/app/pages/design-editor/overview-annotation-context.ts +157 -0
- package/corpus/templates/design/app/pages/design-editor/overview-camera.ts +62 -6
- package/corpus/templates/design/app/pages/design-editor/pending-edits.ts +245 -0
- package/corpus/templates/design/app/pages/design-editor/react-semantic-handoff.ts +586 -0
- package/corpus/templates/design/app/pages/design-editor/screen-auto-layout.ts +169 -0
- package/corpus/templates/design/app/pages/design-editor/selection-state.ts +201 -1
- package/corpus/templates/design/changelog/2026-07-09-added-underline-strikethrough-and-text-case-uppercase-lowerc.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-annotate-now-draws-across-the-all-screens-canvas-without-swi.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-auto-layout-children-can-now-be-dragged-into-freeform-screen.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-board-shapes-now-stay-visible-and-nest-correctly-across-the-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-component-source-navigation-now-opens-reliably-from-the-insp.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-cross-screen-local-react-layer-moves-now-hand-off-exact-sour.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-cross-screen-moves-undo-and-redo-now-keep-both-screens-stabl.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-detach-a-component-instance-into-plain-editable-eleme.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-dragging-layers-into-auto-layout-now-respects-ignore-auto-la.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-drawing-annotations-sent-to-the-agent-now-include-a-rendered.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-editor-sidebars-now-use-figma-matched-default-widths-for-a-r.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-empty-frames-and-rectangles-now-expose-full-auto-layout-cont.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-export-any-design-screen-as-a-real-vector-svg-for-figma-with.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-grid-auto-layout-now-uses-true-responsive-rows-and-columns-w.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-hidden-and-locked-screens-now-behave-consistently-between-la.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-image-fills-now-preserve-valid-urls-and-layered-fit-settings.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-import-figma-frames-as-pixel-accurate-design-screens-via-a-s.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-inspect-code-now-hides-internal-styling-metadata-and-wraps-l.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-inspector-blur-and-shadow-effects-now-preserve-other-effects.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-jump-to-a-components-other-instances-from-the-inspecto.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-keyboard-shortcuts-are-now-discoverable-in-a-figma-style-bot.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-layer-selection-now-keeps-cmd-or-ctrl-toggles-and-shift-rang.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-layers-dropped-into-auto-layout-now-participate-correctly-in.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-local-live-edit-screens-now-enter-and-leave-full-view-withou.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-local-preview-urls-now-live-in-the-inspector-instead-of-taki.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-local-react-layers-can-now-be-hidden-and-locked-from-layers-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-local-react-layers-now-keep-exact-source-provenance-and-supp.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-local-react-visual-editing-now-loads-reliably-and-shows-live.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-paste-from-figma-cmd-v-now-imports-exact-editable-nodes-when.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-pressing-escape-after-selecting-a-screen-now-reveals-page-pr.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-right-click-overlapping-or-nested-objects-to-choose-the-exac.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-scale-edge-stretch-and-center-constraints-now-preserve-layer.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-screen-rows-can-now-be-reordered-in-layers-with-matching-can.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-screen-selection-stays-responsive-in-designs-with-hundreds-o.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-shift-a-now-enables-auto-layout-on-a-selected-screen-with-fl.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-swap-a-component-instance-for-another-from-the-libra.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-drawing-annotations-now-stay-available-to-retry-when-the.md +6 -0
- package/corpus/templates/design/e2e/global-setup.ts +3 -6
- package/corpus/templates/design/e2e/helpers.ts +27 -6
- package/corpus/templates/design/server/lib/design-to-figma-svg.ts +1412 -0
- package/corpus/templates/design/server/lib/figma-clipboard-match.ts +196 -0
- package/corpus/templates/design/server/lib/figma-node-import.ts +311 -0
- package/corpus/templates/design/server/lib/figma-node-to-html.ts +1334 -0
- package/corpus/templates/design/server/lib/figma-paste-fallback.ts +64 -0
- package/corpus/templates/design/server/lib/playwright-runtime.ts +78 -0
- package/corpus/templates/design/server/plugins/db.ts +41 -0
- package/corpus/templates/design/server/source-workspace.ts +64 -0
- package/corpus/templates/design/shared/board-file.ts +13 -2
- package/corpus/templates/design/shared/canvas-math.ts +231 -33
- package/corpus/templates/design/shared/code-layer.ts +122 -8
- package/corpus/templates/design/shared/component-library.ts +161 -0
- package/corpus/templates/design/shared/db-conflict.ts +27 -0
- package/corpus/templates/design/shared/design-source-capabilities.ts +2 -1
- package/corpus/templates/design/shared/figma-url.ts +149 -0
- package/corpus/templates/design/shared/pen-path.ts +16 -7
- package/corpus/templates/design/shared/source-mode.ts +1 -0
- package/corpus/templates/plan/app/components/plan/CanvasArea.tsx +7 -19
- package/corpus/templates/plan/changelog/2026-07-09-canvas-panning-now-keeps-zoom-fixed-even-at-the-edges.md +6 -0
- package/corpus/templates/slides/actions/index-design-system-with-builder.ts +16 -1
- package/corpus/templates/slides/app/context/DeckContext.tsx +311 -70
- package/corpus/templates/slides/changelog/2026-07-09-fixed-the-slide-rail-going-permanently-stale-during-a-long-a.md +6 -0
- package/dist/agent/production-agent.d.ts +79 -6
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +161 -14
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +29 -5
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/agent/run-store.d.ts +46 -0
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +61 -0
- package/dist/agent/run-store.js.map +1 -1
- package/dist/cli/design-connect.d.ts +6 -0
- package/dist/cli/design-connect.d.ts.map +1 -1
- package/dist/cli/design-connect.js +336 -139
- package/dist/cli/design-connect.js.map +1 -1
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +14 -0
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +2 -0
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +19 -3
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/client/MultiTabAssistantChat.js +38 -18
- package/dist/client/MultiTabAssistantChat.js.map +1 -1
- package/dist/client/agent-chat.d.ts +60 -0
- package/dist/client/agent-chat.d.ts.map +1 -1
- package/dist/client/agent-chat.js +122 -1
- package/dist/client/agent-chat.js.map +1 -1
- package/dist/client/chat/index.d.ts +1 -1
- package/dist/client/chat/index.d.ts.map +1 -1
- package/dist/client/chat/index.js +1 -1
- package/dist/client/chat/index.js.map +1 -1
- package/dist/client/extensions/EmbeddedExtension.d.ts +12 -0
- package/dist/client/extensions/EmbeddedExtension.d.ts.map +1 -1
- package/dist/client/extensions/EmbeddedExtension.js +36 -2
- package/dist/client/extensions/EmbeddedExtension.js.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/useCollabReconcile.js +18 -6
- package/dist/client/rich-markdown-editor/useCollabReconcile.js.map +1 -1
- package/dist/collab/awareness-store.d.ts +5 -0
- package/dist/collab/awareness-store.d.ts.map +1 -1
- package/dist/collab/awareness-store.js +19 -12
- package/dist/collab/awareness-store.js.map +1 -1
- package/dist/collab/client.d.ts.map +1 -1
- package/dist/collab/client.js +13 -5
- package/dist/collab/client.js.map +1 -1
- package/dist/collab/index.d.ts +1 -0
- package/dist/collab/index.d.ts.map +1 -1
- package/dist/collab/index.js +1 -0
- package/dist/collab/index.js.map +1 -1
- package/dist/collab/recent-edits.d.ts +3 -1
- package/dist/collab/recent-edits.d.ts.map +1 -1
- package/dist/collab/recent-edits.js +57 -2
- package/dist/collab/recent-edits.js.map +1 -1
- package/dist/index.browser.d.ts +1 -1
- package/dist/index.browser.d.ts.map +1 -1
- package/dist/index.browser.js +1 -1
- package/dist/index.browser.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +87 -10
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/builder-design-systems.d.ts +15 -0
- package/dist/server/builder-design-systems.d.ts.map +1 -1
- package/dist/server/builder-design-systems.js +9 -4
- package/dist/server/builder-design-systems.js.map +1 -1
- package/package.json +1 -1
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
IconEraser,
|
|
4
4
|
IconArrowBackUp,
|
|
5
5
|
IconArrowForwardUp,
|
|
6
|
+
IconLoader2,
|
|
6
7
|
IconSend,
|
|
7
8
|
IconCursorText,
|
|
8
9
|
IconX,
|
|
@@ -12,6 +13,7 @@ import {
|
|
|
12
13
|
useRef,
|
|
13
14
|
useCallback,
|
|
14
15
|
useEffect,
|
|
16
|
+
useLayoutEffect,
|
|
15
17
|
type ReactNode,
|
|
16
18
|
} from "react";
|
|
17
19
|
import { createPortal } from "react-dom";
|
|
@@ -69,6 +71,42 @@ interface DrawOverlayProps {
|
|
|
69
71
|
) => void;
|
|
70
72
|
/** Called when the user cancels / closes the draw mode */
|
|
71
73
|
onClose: () => void;
|
|
74
|
+
/**
|
|
75
|
+
* True while the caller is capturing/compositing/uploading the annotated
|
|
76
|
+
* screenshot for a just-submitted drawing (see the design app's
|
|
77
|
+
* design-canvas/annotation-snapshot.ts). Disables Send and swaps its icon
|
|
78
|
+
* for a spinner so a slow capture can't be triggered twice from the same
|
|
79
|
+
* drawing. The entire overlay becomes inert while true so edits made after
|
|
80
|
+
* the submitted snapshot cannot be erased when that submission is later
|
|
81
|
+
* confirmed and the caller clears the batch.
|
|
82
|
+
*/
|
|
83
|
+
sending?: boolean;
|
|
84
|
+
/**
|
|
85
|
+
* Bump this (e.g. a counter) exactly when the caller wants to discard the
|
|
86
|
+
* current strokes/text annotations — after `onClose` or a confirmed Send.
|
|
87
|
+
* Strokes/text are intentionally NOT cleared merely because `visible`
|
|
88
|
+
* turns false: `visible` can toggle off for reasons that have nothing to
|
|
89
|
+
* do with the user wanting to discard their work (switching tools, views,
|
|
90
|
+
* or side panels), and doing so silently would destroy annotations the
|
|
91
|
+
* user never got a chance to send. Only an in-progress (uncommitted)
|
|
92
|
+
* gesture is reset on hide; a pending text label is committed so its typed
|
|
93
|
+
* content survives.
|
|
94
|
+
*/
|
|
95
|
+
clearSignal?: number;
|
|
96
|
+
/**
|
|
97
|
+
* Identity of the canvas this annotation batch belongs to. When the host
|
|
98
|
+
* reuses one DrawOverlay instance for a different screen, the old batch is
|
|
99
|
+
* cleared with a warning instead of being silently reinterpreted against
|
|
100
|
+
* the new screen's pixels and submitted with the wrong screenshot.
|
|
101
|
+
*/
|
|
102
|
+
scopeKey?: string;
|
|
103
|
+
/**
|
|
104
|
+
* Keep the canvas DOM/bitmap alive while hidden. Use for the one active
|
|
105
|
+
* editor overlay whose work must survive mode/view toggles; leave false for
|
|
106
|
+
* large preview grids so every dormant screen does not allocate a canvas
|
|
107
|
+
* and ResizeObserver.
|
|
108
|
+
*/
|
|
109
|
+
retainSurfaceWhenHidden?: boolean;
|
|
72
110
|
}
|
|
73
111
|
|
|
74
112
|
const PRESET_COLORS = [
|
|
@@ -111,6 +149,18 @@ interface PendingTextInput {
|
|
|
111
149
|
/** Fractional y (0..1) of the visual rect. */
|
|
112
150
|
yFrac: number;
|
|
113
151
|
value: string;
|
|
152
|
+
/**
|
|
153
|
+
* Monotonic generation id. A click that opens a new pending text box while
|
|
154
|
+
* an earlier one is still focused fires pointerdown (which replaces the
|
|
155
|
+
* ref synchronously) before the outgoing box's native `blur` event runs.
|
|
156
|
+
* Without this, that trailing blur reads the *new* (empty) pending input
|
|
157
|
+
* out of the ref and wipes it via `commitTextAnnotation`'s unconditional
|
|
158
|
+
* `setPendingTextInput(null)` before the user can type anything. Callers
|
|
159
|
+
* pass the generation captured at their own render so a stale blur/Enter/
|
|
160
|
+
* Escape from a since-replaced box is a no-op instead of clobbering the
|
|
161
|
+
* newer one.
|
|
162
|
+
*/
|
|
163
|
+
generation: number;
|
|
114
164
|
}
|
|
115
165
|
|
|
116
166
|
/**
|
|
@@ -200,6 +250,10 @@ export function DrawOverlay({
|
|
|
200
250
|
zoom = 100,
|
|
201
251
|
onSend,
|
|
202
252
|
onClose,
|
|
253
|
+
sending = false,
|
|
254
|
+
clearSignal,
|
|
255
|
+
scopeKey,
|
|
256
|
+
retainSurfaceWhenHidden = false,
|
|
203
257
|
}: DrawOverlayProps) {
|
|
204
258
|
const t = useT();
|
|
205
259
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
@@ -227,12 +281,14 @@ export function DrawOverlay({
|
|
|
227
281
|
const [textMode, setTextMode] = useState(false);
|
|
228
282
|
const [textInput, setTextInput] = useState<PendingTextInput | null>(null);
|
|
229
283
|
const textInputStateRef = useRef<PendingTextInput | null>(null);
|
|
284
|
+
const textInputGenerationRef = useRef(0);
|
|
230
285
|
const textInputRef = useRef<HTMLInputElement>(null);
|
|
231
286
|
// Escape cancels the pending text annotation, but unmounting the input also
|
|
232
287
|
// fires its blur handler, which would commit the very annotation the user
|
|
233
288
|
// just cancelled. This flag lets the blur handler skip that commit.
|
|
234
289
|
const cancelingTextRef = useRef(false);
|
|
235
290
|
const [instruction, setInstruction] = useState("");
|
|
291
|
+
const instructionRef = useRef("");
|
|
236
292
|
const drawing = useRef(false);
|
|
237
293
|
const canvasSizeRef = useRef({ w: 0, h: 0 });
|
|
238
294
|
// Resize tick: bumped by ResizeObserver so the redraw effect re-runs when
|
|
@@ -241,6 +297,36 @@ export function DrawOverlay({
|
|
|
241
297
|
|
|
242
298
|
const scale = Math.max(zoom / 100, 0.01);
|
|
243
299
|
|
|
300
|
+
// A host can still unmount the active editor entirely (route/design close,
|
|
301
|
+
// or focused canvas teardown) rather than merely hiding this component.
|
|
302
|
+
// Losing unsent work in that teardown must never be silent. Normal
|
|
303
|
+
// overview/focused visibility changes retain their active surface and do
|
|
304
|
+
// not hit this cleanup; only a real component unmount does.
|
|
305
|
+
useEffect(() => {
|
|
306
|
+
return () => {
|
|
307
|
+
// A true unmount never runs the `!visible` effect above (the prop
|
|
308
|
+
// never transitions — the component is simply gone), so a still-open
|
|
309
|
+
// pending text box never gets its usual commit-on-hide chance. Count
|
|
310
|
+
// it here too, or a label the user was mid-typing would vanish
|
|
311
|
+
// without even counting toward the warning.
|
|
312
|
+
const pendingText = textInputStateRef.current?.value.trim();
|
|
313
|
+
const discardedCount =
|
|
314
|
+
strokesRef.current.length +
|
|
315
|
+
textAnnotationsRef.current.length +
|
|
316
|
+
(pendingText ? 1 : 0) +
|
|
317
|
+
(instructionRef.current.trim() ? 1 : 0);
|
|
318
|
+
if (discardedCount > 0) {
|
|
319
|
+
toast(
|
|
320
|
+
t("visualEditor.annotationsDiscardedOnViewChange", {
|
|
321
|
+
count: discardedCount,
|
|
322
|
+
}),
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps -- must only run
|
|
327
|
+
// its cleanup on actual unmount, not on every `t`/locale change.
|
|
328
|
+
}, []);
|
|
329
|
+
|
|
244
330
|
const cancelScheduledStrokeFrame = useCallback(() => {
|
|
245
331
|
if (currentStrokeFrameRef.current === null) return;
|
|
246
332
|
window.cancelAnimationFrame(currentStrokeFrameRef.current);
|
|
@@ -287,22 +373,35 @@ export function DrawOverlay({
|
|
|
287
373
|
[],
|
|
288
374
|
);
|
|
289
375
|
|
|
290
|
-
|
|
376
|
+
const clearAnnotationState = useCallback(() => {
|
|
377
|
+
resetActiveStroke();
|
|
378
|
+
strokesRef.current = [];
|
|
379
|
+
textAnnotationsRef.current = [];
|
|
380
|
+
setStrokes([]);
|
|
381
|
+
setTextAnnotations([]);
|
|
382
|
+
setRedoStack([]);
|
|
383
|
+
setPendingTextInput(null);
|
|
384
|
+
setTextMode(false);
|
|
385
|
+
instructionRef.current = "";
|
|
386
|
+
setInstruction("");
|
|
387
|
+
cancelingTextRef.current = false;
|
|
388
|
+
lastCreatedAtRef.current = 0;
|
|
389
|
+
textInputGenerationRef.current = 0;
|
|
390
|
+
}, [resetActiveStroke, setPendingTextInput]);
|
|
391
|
+
|
|
392
|
+
// Hiding ends whatever the user was mid-gesture on, but must NOT discard
|
|
393
|
+
// already-committed strokes/text: `visible` can turn false for reasons
|
|
394
|
+
// that have nothing to do with the user wanting to abandon their
|
|
395
|
+
// annotations (switching tools, views, or side panels). Only reset the
|
|
396
|
+
// transient in-flight state here — the committed `strokes`/
|
|
397
|
+
// `textAnnotations` arrays are cleared exclusively by `clearSignal` below.
|
|
291
398
|
useEffect(() => {
|
|
292
399
|
if (!visible) {
|
|
293
400
|
resetActiveStroke();
|
|
294
|
-
strokesRef.current = [];
|
|
295
|
-
textAnnotationsRef.current = [];
|
|
296
|
-
setStrokes([]);
|
|
297
|
-
setTextAnnotations([]);
|
|
298
|
-
setRedoStack([]);
|
|
299
|
-
setPendingTextInput(null);
|
|
300
401
|
setTextMode(false);
|
|
301
|
-
setInstruction("");
|
|
302
402
|
cancelingTextRef.current = false;
|
|
303
|
-
lastCreatedAtRef.current = 0;
|
|
304
403
|
}
|
|
305
|
-
}, [resetActiveStroke,
|
|
404
|
+
}, [resetActiveStroke, visible]);
|
|
306
405
|
|
|
307
406
|
// Comment-pin mode can temporarily leave the toolbar visible while making
|
|
308
407
|
// the drawing surface inert. Never retain a half-finished gesture across
|
|
@@ -320,26 +419,34 @@ export function DrawOverlay({
|
|
|
320
419
|
|
|
321
420
|
const shouldFocusTextInput = textInput !== null;
|
|
322
421
|
useEffect(() => {
|
|
323
|
-
if (!shouldFocusTextInput) return;
|
|
422
|
+
if (!visible || !shouldFocusTextInput) return;
|
|
324
423
|
const id = window.requestAnimationFrame(() => {
|
|
325
424
|
textInputRef.current?.focus();
|
|
326
425
|
});
|
|
327
426
|
return () => window.cancelAnimationFrame(id);
|
|
328
|
-
}, [shouldFocusTextInput]);
|
|
427
|
+
}, [shouldFocusTextInput, visible]);
|
|
329
428
|
|
|
330
|
-
//
|
|
331
|
-
//
|
|
429
|
+
// Retained editor surfaces keep their canvas mounted while hidden, so a
|
|
430
|
+
// hidden-first host installs its observer and the bitmap does not flash
|
|
431
|
+
// blank when the user re-enters annotate mode. Non-retained preview-grid
|
|
432
|
+
// overlays attach only while visible to avoid idle canvas/observer cost.
|
|
332
433
|
useEffect(() => {
|
|
434
|
+
if (!visible && !retainSurfaceWhenHidden) return;
|
|
333
435
|
const canvas = canvasRef.current;
|
|
334
436
|
if (!canvas) return;
|
|
335
437
|
if (typeof ResizeObserver === "undefined") return;
|
|
336
438
|
const ro = new ResizeObserver(() => setResizeTick((t) => t + 1));
|
|
337
439
|
ro.observe(canvas);
|
|
338
440
|
return () => ro.disconnect();
|
|
339
|
-
}, []);
|
|
340
|
-
|
|
341
|
-
// Redraw
|
|
342
|
-
|
|
441
|
+
}, [retainSurfaceWhenHidden, visible]);
|
|
442
|
+
|
|
443
|
+
// Redraw before paint whenever strokes change, the canvas is resized, or a
|
|
444
|
+
// preserved batch becomes visible again. The visible dependency is
|
|
445
|
+
// intentional even though the DOM stays mounted: browsers may discard a
|
|
446
|
+
// hidden canvas backing store under memory pressure, and re-entry must
|
|
447
|
+
// never expose a blank frame before the next pointer event.
|
|
448
|
+
useLayoutEffect(() => {
|
|
449
|
+
if (!visible) return;
|
|
343
450
|
const canvas = canvasRef.current;
|
|
344
451
|
if (!canvas) return;
|
|
345
452
|
const ctx = canvas.getContext("2d");
|
|
@@ -387,22 +494,109 @@ export function DrawOverlay({
|
|
|
387
494
|
// which changes getBoundingClientRect() without firing ResizeObserver — the
|
|
388
495
|
// effect must re-run on zoom change to redraw the fraction-based strokes at
|
|
389
496
|
// the new visual size.
|
|
390
|
-
}, [strokes, currentStroke, color, lineWidth, resizeTick, zoom]);
|
|
497
|
+
}, [strokes, currentStroke, color, lineWidth, resizeTick, visible, zoom]);
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* Commits the pending text annotation (if any) and clears the pending
|
|
501
|
+
* input. `expectedGeneration`, when passed, guards against a stale
|
|
502
|
+
* blur/Enter/Escape belonging to a pending text box that has since been
|
|
503
|
+
* replaced or already cleared — see `PendingTextInput.generation`. Called
|
|
504
|
+
* with no argument to force-commit whatever is currently pending (e.g.
|
|
505
|
+
* before opening a new text box, or right before Send).
|
|
506
|
+
*/
|
|
507
|
+
const commitTextAnnotation = useCallback(
|
|
508
|
+
(expectedGeneration?: number) => {
|
|
509
|
+
const pendingText = textInputStateRef.current;
|
|
510
|
+
if (
|
|
511
|
+
!pendingText ||
|
|
512
|
+
(expectedGeneration !== undefined &&
|
|
513
|
+
pendingText.generation !== expectedGeneration)
|
|
514
|
+
) {
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
// Clear the authoritative pending value first. Enter, blur, and Send can
|
|
518
|
+
// all occur in the same browser turn; any later handler becomes a no-op
|
|
519
|
+
// instead of duplicating the label.
|
|
520
|
+
setPendingTextInput(null);
|
|
521
|
+
if (!pendingText.value.trim()) return;
|
|
522
|
+
|
|
523
|
+
const ann: DrawAnnotation = {
|
|
524
|
+
id: crypto.randomUUID(),
|
|
525
|
+
type: "text",
|
|
526
|
+
text: pendingText.value.trim(),
|
|
527
|
+
// Store fractional position so the label stays anchored across zoom changes.
|
|
528
|
+
position: { x: pendingText.xFrac, y: pendingText.yFrac },
|
|
529
|
+
color,
|
|
530
|
+
lineWidth,
|
|
531
|
+
createdAt: nextCreatedAt(),
|
|
532
|
+
};
|
|
533
|
+
const nextTexts = [...textAnnotationsRef.current, ann];
|
|
534
|
+
textAnnotationsRef.current = nextTexts;
|
|
535
|
+
setTextAnnotations(nextTexts);
|
|
536
|
+
// A new text annotation also clears the redo stack.
|
|
537
|
+
setRedoStack([]);
|
|
538
|
+
},
|
|
539
|
+
[color, lineWidth, nextCreatedAt, setPendingTextInput],
|
|
540
|
+
);
|
|
541
|
+
|
|
542
|
+
// Hiding removes the pending-text input from interaction. Commit whatever
|
|
543
|
+
// was typed explicitly so a still-open label survives an unrelated exit
|
|
544
|
+
// instead of relying on incidental native blur ordering.
|
|
545
|
+
useEffect(() => {
|
|
546
|
+
if (!visible) commitTextAnnotation();
|
|
547
|
+
}, [visible, commitTextAnnotation]);
|
|
548
|
+
|
|
549
|
+
// The explicit-discard path: the caller bumps `clearSignal` exactly when
|
|
550
|
+
// the user deliberately closes the overlay (X/Escape) or a Send is
|
|
551
|
+
// confirmed delivered. A scope change below is the only other reset path,
|
|
552
|
+
// guarding against submitting one screen's geometry against another.
|
|
553
|
+
const lastClearSignalRef = useRef(clearSignal);
|
|
554
|
+
useLayoutEffect(() => {
|
|
555
|
+
if (clearSignal === lastClearSignalRef.current) return;
|
|
556
|
+
lastClearSignalRef.current = clearSignal;
|
|
557
|
+
clearAnnotationState();
|
|
558
|
+
}, [clearAnnotationState, clearSignal]);
|
|
559
|
+
|
|
560
|
+
const lastScopeKeyRef = useRef(scopeKey);
|
|
561
|
+
useLayoutEffect(() => {
|
|
562
|
+
if (scopeKey === lastScopeKeyRef.current) return;
|
|
563
|
+
lastScopeKeyRef.current = scopeKey;
|
|
564
|
+
const pendingText = textInputStateRef.current?.value.trim();
|
|
565
|
+
const discardedCount =
|
|
566
|
+
strokesRef.current.length +
|
|
567
|
+
textAnnotationsRef.current.length +
|
|
568
|
+
(pendingText ? 1 : 0) +
|
|
569
|
+
(instructionRef.current.trim() ? 1 : 0);
|
|
570
|
+
if (discardedCount > 0) {
|
|
571
|
+
toast(
|
|
572
|
+
t("visualEditor.annotationsDiscardedOnViewChange", {
|
|
573
|
+
count: discardedCount,
|
|
574
|
+
}),
|
|
575
|
+
);
|
|
576
|
+
}
|
|
577
|
+
clearAnnotationState();
|
|
578
|
+
}, [clearAnnotationState, scopeKey, t]);
|
|
391
579
|
|
|
392
580
|
const handlePointerDown = useCallback(
|
|
393
581
|
(e: React.PointerEvent) => {
|
|
394
582
|
const rect = canvasRef.current?.getBoundingClientRect();
|
|
395
583
|
if (!rect || rect.width <= 0 || rect.height <= 0) return;
|
|
396
|
-
if (!canvasInteractive || drawing.current) return;
|
|
584
|
+
if (!canvasInteractive || sending || drawing.current) return;
|
|
397
585
|
if (e.pointerType === "mouse" && e.button !== 0) return;
|
|
398
586
|
|
|
399
587
|
if (textMode) {
|
|
400
588
|
e.preventDefault();
|
|
401
589
|
cancelingTextRef.current = false;
|
|
590
|
+
// Commit whatever text box was still open (using the current ref,
|
|
591
|
+
// which still holds the outgoing box at this point) before replacing
|
|
592
|
+
// it, so clicking a new spot never silently drops the previous label.
|
|
593
|
+
commitTextAnnotation();
|
|
594
|
+
textInputGenerationRef.current += 1;
|
|
402
595
|
setPendingTextInput({
|
|
403
596
|
xFrac: (e.clientX - rect.left) / rect.width,
|
|
404
597
|
yFrac: (e.clientY - rect.top) / rect.height,
|
|
405
598
|
value: "",
|
|
599
|
+
generation: textInputGenerationRef.current,
|
|
406
600
|
});
|
|
407
601
|
return;
|
|
408
602
|
}
|
|
@@ -422,7 +616,15 @@ export function DrawOverlay({
|
|
|
422
616
|
// gesture. The pointer-id guard still prevents cross-pointer mixing.
|
|
423
617
|
}
|
|
424
618
|
},
|
|
425
|
-
[
|
|
619
|
+
[
|
|
620
|
+
canvasInteractive,
|
|
621
|
+
color,
|
|
622
|
+
commitTextAnnotation,
|
|
623
|
+
lineWidth,
|
|
624
|
+
sending,
|
|
625
|
+
setPendingTextInput,
|
|
626
|
+
textMode,
|
|
627
|
+
],
|
|
426
628
|
);
|
|
427
629
|
|
|
428
630
|
const handlePointerMove = useCallback(
|
|
@@ -529,6 +731,7 @@ export function DrawOverlay({
|
|
|
529
731
|
* creation order and pushes it onto the unified redo stack.
|
|
530
732
|
*/
|
|
531
733
|
const undo = () => {
|
|
734
|
+
if (sending) return;
|
|
532
735
|
const currentStrokes = strokesRef.current;
|
|
533
736
|
const currentTexts = textAnnotationsRef.current;
|
|
534
737
|
const lastStroke =
|
|
@@ -558,6 +761,7 @@ export function DrawOverlay({
|
|
|
558
761
|
};
|
|
559
762
|
|
|
560
763
|
const redo = () => {
|
|
764
|
+
if (sending) return;
|
|
561
765
|
setRedoStack((stack) => {
|
|
562
766
|
if (stack.length === 0) return stack;
|
|
563
767
|
const top = stack[stack.length - 1];
|
|
@@ -581,6 +785,7 @@ export function DrawOverlay({
|
|
|
581
785
|
};
|
|
582
786
|
|
|
583
787
|
const clear = () => {
|
|
788
|
+
if (sending) return;
|
|
584
789
|
if (strokes.length === 0 && textAnnotations.length === 0) return;
|
|
585
790
|
const prevStrokes = strokesRef.current;
|
|
586
791
|
const prevTexts = textAnnotationsRef.current;
|
|
@@ -609,32 +814,8 @@ export function DrawOverlay({
|
|
|
609
814
|
});
|
|
610
815
|
};
|
|
611
816
|
|
|
612
|
-
const commitTextAnnotation = () => {
|
|
613
|
-
const pendingText = textInputStateRef.current;
|
|
614
|
-
// Clear the authoritative pending value first. Enter, blur, and Send can
|
|
615
|
-
// all occur in the same browser turn; any later handler becomes a no-op
|
|
616
|
-
// instead of duplicating the label.
|
|
617
|
-
setPendingTextInput(null);
|
|
618
|
-
if (!pendingText || !pendingText.value.trim()) return;
|
|
619
|
-
|
|
620
|
-
const ann: DrawAnnotation = {
|
|
621
|
-
id: crypto.randomUUID(),
|
|
622
|
-
type: "text",
|
|
623
|
-
text: pendingText.value.trim(),
|
|
624
|
-
// Store fractional position so the label stays anchored across zoom changes.
|
|
625
|
-
position: { x: pendingText.xFrac, y: pendingText.yFrac },
|
|
626
|
-
color,
|
|
627
|
-
lineWidth,
|
|
628
|
-
createdAt: nextCreatedAt(),
|
|
629
|
-
};
|
|
630
|
-
const nextTexts = [...textAnnotationsRef.current, ann];
|
|
631
|
-
textAnnotationsRef.current = nextTexts;
|
|
632
|
-
setTextAnnotations(nextTexts);
|
|
633
|
-
// A new text annotation also clears the redo stack.
|
|
634
|
-
setRedoStack([]);
|
|
635
|
-
};
|
|
636
|
-
|
|
637
817
|
const send = () => {
|
|
818
|
+
if (sending) return;
|
|
638
819
|
const canvas = canvasRef.current;
|
|
639
820
|
if (!canvas) return;
|
|
640
821
|
// A focused text label normally commits on blur before the Send click, but
|
|
@@ -680,7 +861,7 @@ export function DrawOverlay({
|
|
|
680
861
|
});
|
|
681
862
|
};
|
|
682
863
|
|
|
683
|
-
if (!visible) return null;
|
|
864
|
+
if (!visible && !retainSurfaceWhenHidden) return null;
|
|
684
865
|
|
|
685
866
|
const hasContent =
|
|
686
867
|
strokes.length > 0 ||
|
|
@@ -706,7 +887,10 @@ export function DrawOverlay({
|
|
|
706
887
|
type="button"
|
|
707
888
|
aria-label={preset.label}
|
|
708
889
|
data-testid={`draw-color-${preset.label.toLowerCase()}`}
|
|
709
|
-
onClick={() =>
|
|
890
|
+
onClick={() => {
|
|
891
|
+
if (!sending) setColor(preset.color);
|
|
892
|
+
}}
|
|
893
|
+
disabled={sending}
|
|
710
894
|
className={cn(
|
|
711
895
|
"h-5 w-5 cursor-pointer rounded-full",
|
|
712
896
|
color === preset.color
|
|
@@ -732,7 +916,10 @@ export function DrawOverlay({
|
|
|
732
916
|
type="button"
|
|
733
917
|
aria-label={lw.label}
|
|
734
918
|
data-testid={`draw-line-width-${lw.label.toLowerCase()}`}
|
|
735
|
-
onClick={() =>
|
|
919
|
+
onClick={() => {
|
|
920
|
+
if (!sending) setLineWidth(lw.value);
|
|
921
|
+
}}
|
|
922
|
+
disabled={sending}
|
|
736
923
|
className={cn(
|
|
737
924
|
"flex h-6 w-6 cursor-pointer items-center justify-center rounded",
|
|
738
925
|
lineWidth === lw.value
|
|
@@ -760,7 +947,10 @@ export function DrawOverlay({
|
|
|
760
947
|
type="button"
|
|
761
948
|
aria-label={t("visualEditor.typeAnywhereOnCanvas")}
|
|
762
949
|
data-testid="draw-text-mode"
|
|
763
|
-
onClick={() =>
|
|
950
|
+
onClick={() => {
|
|
951
|
+
if (!sending) setTextMode(!textMode);
|
|
952
|
+
}}
|
|
953
|
+
disabled={sending}
|
|
764
954
|
className={cn(
|
|
765
955
|
"flex h-6 w-6 cursor-pointer items-center justify-center rounded",
|
|
766
956
|
textMode
|
|
@@ -784,7 +974,7 @@ export function DrawOverlay({
|
|
|
784
974
|
aria-label={t("visualEditor.undoStroke")}
|
|
785
975
|
data-testid="draw-undo"
|
|
786
976
|
onClick={undo}
|
|
787
|
-
disabled={!canUndo}
|
|
977
|
+
disabled={!canUndo || sending}
|
|
788
978
|
className="flex h-6 w-6 cursor-pointer items-center justify-center rounded text-muted-foreground hover:text-foreground disabled:cursor-default disabled:opacity-30"
|
|
789
979
|
>
|
|
790
980
|
<IconArrowBackUp className="h-3.5 w-3.5" />
|
|
@@ -801,7 +991,7 @@ export function DrawOverlay({
|
|
|
801
991
|
aria-label={t("visualEditor.redoStroke")}
|
|
802
992
|
data-testid="draw-redo"
|
|
803
993
|
onClick={redo}
|
|
804
|
-
disabled={!canRedo}
|
|
994
|
+
disabled={!canRedo || sending}
|
|
805
995
|
className="flex h-6 w-6 cursor-pointer items-center justify-center rounded text-muted-foreground hover:text-foreground disabled:cursor-default disabled:opacity-30"
|
|
806
996
|
>
|
|
807
997
|
<IconArrowForwardUp className="h-3.5 w-3.5" />
|
|
@@ -818,7 +1008,9 @@ export function DrawOverlay({
|
|
|
818
1008
|
aria-label={t("visualEditor.clearAll")}
|
|
819
1009
|
data-testid="draw-clear-all"
|
|
820
1010
|
onClick={clear}
|
|
821
|
-
disabled={
|
|
1011
|
+
disabled={
|
|
1012
|
+
sending || (strokes.length === 0 && textAnnotations.length === 0)
|
|
1013
|
+
}
|
|
822
1014
|
className="flex h-6 w-6 cursor-pointer items-center justify-center rounded text-muted-foreground hover:text-foreground disabled:cursor-default disabled:opacity-30"
|
|
823
1015
|
>
|
|
824
1016
|
<IconEraser className="h-3.5 w-3.5" />
|
|
@@ -832,12 +1024,17 @@ export function DrawOverlay({
|
|
|
832
1024
|
{/* Instruction input */}
|
|
833
1025
|
<Input
|
|
834
1026
|
value={instruction}
|
|
835
|
-
onChange={(e) =>
|
|
1027
|
+
onChange={(e) => {
|
|
1028
|
+
if (sending) return;
|
|
1029
|
+
instructionRef.current = e.target.value;
|
|
1030
|
+
setInstruction(e.target.value);
|
|
1031
|
+
}}
|
|
836
1032
|
onKeyDown={(e) => {
|
|
837
|
-
if (e.key === "Enter" && hasContent) send();
|
|
1033
|
+
if (e.key === "Enter" && hasContent && !sending) send();
|
|
838
1034
|
if (e.key === "Escape") onClose();
|
|
839
1035
|
}}
|
|
840
1036
|
placeholder={t("visualEditor.tellAgentWhatToDo")}
|
|
1037
|
+
disabled={sending}
|
|
841
1038
|
className="h-7 w-56 border-border bg-background text-xs"
|
|
842
1039
|
/>
|
|
843
1040
|
|
|
@@ -847,10 +1044,14 @@ export function DrawOverlay({
|
|
|
847
1044
|
data-testid="draw-send"
|
|
848
1045
|
className="h-7 gap-1 px-3 !text-[11px] cursor-pointer"
|
|
849
1046
|
onClick={send}
|
|
850
|
-
disabled={!hasContent}
|
|
1047
|
+
disabled={!hasContent || sending}
|
|
851
1048
|
>
|
|
852
|
-
|
|
853
|
-
|
|
1049
|
+
{sending ? (
|
|
1050
|
+
<IconLoader2 className="h-3 w-3 animate-spin" />
|
|
1051
|
+
) : (
|
|
1052
|
+
<IconSend className="h-3 w-3" />
|
|
1053
|
+
)}
|
|
1054
|
+
{sending ? t("visualEditor.sendingDrawing") : t("visualEditor.send")}
|
|
854
1055
|
</Button>
|
|
855
1056
|
|
|
856
1057
|
{/* Close */}
|
|
@@ -861,6 +1062,7 @@ export function DrawOverlay({
|
|
|
861
1062
|
aria-label={t("visualEditor.exitDrawMode")}
|
|
862
1063
|
data-testid="draw-exit"
|
|
863
1064
|
onClick={onClose}
|
|
1065
|
+
disabled={sending}
|
|
864
1066
|
className="flex h-6 w-6 cursor-pointer items-center justify-center rounded text-muted-foreground hover:text-foreground"
|
|
865
1067
|
>
|
|
866
1068
|
<IconX className="h-3.5 w-3.5" />
|
|
@@ -875,9 +1077,13 @@ export function DrawOverlay({
|
|
|
875
1077
|
<div
|
|
876
1078
|
ref={containerRef}
|
|
877
1079
|
data-draw-overlay
|
|
1080
|
+
aria-hidden={!visible}
|
|
878
1081
|
className={cn(
|
|
879
1082
|
"absolute inset-0 z-[100]",
|
|
880
|
-
|
|
1083
|
+
visible ? "visible" : "invisible pointer-events-none",
|
|
1084
|
+
canvasInteractive && !sending
|
|
1085
|
+
? "pointer-events-auto"
|
|
1086
|
+
: "pointer-events-none",
|
|
881
1087
|
)}
|
|
882
1088
|
>
|
|
883
1089
|
{/* Drawing canvas */}
|
|
@@ -887,7 +1093,7 @@ export function DrawOverlay({
|
|
|
887
1093
|
className={cn(
|
|
888
1094
|
"absolute inset-0 h-full w-full touch-none",
|
|
889
1095
|
textMode ? "cursor-text" : "cursor-crosshair",
|
|
890
|
-
!canvasInteractive && "pointer-events-none",
|
|
1096
|
+
(!canvasInteractive || sending) && "pointer-events-none",
|
|
891
1097
|
)}
|
|
892
1098
|
onPointerDown={handlePointerDown}
|
|
893
1099
|
onPointerMove={handlePointerMove}
|
|
@@ -946,6 +1152,7 @@ export function DrawOverlay({
|
|
|
946
1152
|
ref={textInputRef}
|
|
947
1153
|
value={textInput.value}
|
|
948
1154
|
onChange={(e) =>
|
|
1155
|
+
!sending &&
|
|
949
1156
|
setPendingTextInput((prev) =>
|
|
950
1157
|
prev ? { ...prev, value: e.target.value } : null,
|
|
951
1158
|
)
|
|
@@ -957,7 +1164,11 @@ export function DrawOverlay({
|
|
|
957
1164
|
cancelingTextRef.current = false;
|
|
958
1165
|
return;
|
|
959
1166
|
}
|
|
960
|
-
|
|
1167
|
+
// Pass this render's generation so a trailing blur that
|
|
1168
|
+
// arrives after pointerdown has already opened a *newer*
|
|
1169
|
+
// text box (see PendingTextInput.generation) is a no-op
|
|
1170
|
+
// instead of wiping the box the user just started typing.
|
|
1171
|
+
commitTextAnnotation(textInput.generation);
|
|
961
1172
|
}}
|
|
962
1173
|
onKeyDown={(e) => {
|
|
963
1174
|
if (e.key === "Enter") {
|
|
@@ -965,7 +1176,7 @@ export function DrawOverlay({
|
|
|
965
1176
|
// Committing unmounts the focused input, which can emit a
|
|
966
1177
|
// blur event in the same turn. Skip that second commit.
|
|
967
1178
|
cancelingTextRef.current = true;
|
|
968
|
-
commitTextAnnotation();
|
|
1179
|
+
commitTextAnnotation(textInput.generation);
|
|
969
1180
|
}
|
|
970
1181
|
if (e.key === "Escape") {
|
|
971
1182
|
cancelingTextRef.current = true;
|
|
@@ -973,6 +1184,7 @@ export function DrawOverlay({
|
|
|
973
1184
|
}
|
|
974
1185
|
}}
|
|
975
1186
|
className="h-7 w-48 border-primary bg-background text-sm"
|
|
1187
|
+
disabled={sending}
|
|
976
1188
|
autoFocus
|
|
977
1189
|
placeholder={t("visualEditor.typeAnnotationFancy")}
|
|
978
1190
|
/>
|
|
@@ -980,7 +1192,7 @@ export function DrawOverlay({
|
|
|
980
1192
|
);
|
|
981
1193
|
})()}
|
|
982
1194
|
|
|
983
|
-
<DrawToolbarPortal>{toolbar}</DrawToolbarPortal>
|
|
1195
|
+
{visible ? <DrawToolbarPortal>{toolbar}</DrawToolbarPortal> : null}
|
|
984
1196
|
</div>
|
|
985
1197
|
);
|
|
986
1198
|
}
|
|
@@ -54,13 +54,24 @@ export function useQuestionFlow(
|
|
|
54
54
|
|
|
55
55
|
const sendContinuation = useCallback(
|
|
56
56
|
(message: string, context?: string) => {
|
|
57
|
+
// Always request `newTab` (mirroring useAgentGenerating.submit's
|
|
58
|
+
// default). Without it, when there is no continuationTabId yet the
|
|
59
|
+
// message goes to whatever tab is currently active, but the id we
|
|
60
|
+
// return here would still be a freshly generated one that was never
|
|
61
|
+
// actually used — trackAgentGeneration/onContinue would then watch a
|
|
62
|
+
// tabId that never matches real chatRunning events, so the design
|
|
63
|
+
// "generating" UI silently desyncs (false "stopped, please retry"
|
|
64
|
+
// toasts, completion never detected). Passing tabId only when we have
|
|
65
|
+
// a continuationTabId still reuses that existing thread (addOptimistic
|
|
66
|
+
// thread is idempotent for known ids); omitting it lets a fresh id be
|
|
67
|
+
// generated and actually created, so the returned tabId is always the
|
|
68
|
+
// real destination thread.
|
|
57
69
|
const tabId = sendToDesignAgentChat({
|
|
58
70
|
message,
|
|
59
71
|
context,
|
|
60
72
|
submit: true,
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
: {}),
|
|
73
|
+
newTab: true,
|
|
74
|
+
...(continuationTabId ? { tabId: continuationTabId } : {}),
|
|
64
75
|
});
|
|
65
76
|
onContinue?.(tabId);
|
|
66
77
|
flow.clear();
|