@agent-native/core 0.92.10 → 0.92.12
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 +29 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/production-agent.ts +214 -17
- package/corpus/core/src/agent/run-manager.ts +72 -14
- package/corpus/core/src/agent/run-store.ts +152 -27
- 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-adapter.ts +56 -12
- 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 +75 -9
- package/corpus/templates/clips/app/components/player/video-player.tsx +17 -12
- package/corpus/templates/clips/app/hooks/use-player-shortcuts.ts +1 -0
- 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/changelog/2026-07-10-fixed-copying-agent-responses-and-transcript-text-from-recor.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/_builder-cms-read-client.ts +82 -7
- package/corpus/templates/content/actions/_database-source-utils.ts +149 -30
- 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/add-content-database-source-field-property.ts +4 -7
- package/corpus/templates/content/actions/attach-content-database-source.ts +41 -17
- package/corpus/templates/content/actions/change-content-database-source-role.ts +46 -8
- 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/actions/suggest-source-join-key.ts +8 -1
- 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-builder-source-refreshes-now-keep-mapped-fields-such-as-topi.md +6 -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/content/shared/api.ts +1 -0
- 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 +82 -7
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +180 -17
- 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 +63 -14
- 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 +129 -22
- 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-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +48 -12
- package/dist/client/agent-chat-adapter.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/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.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/collab/struct-routes.d.ts +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/observability/routes.d.ts +5 -5
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +3 -3
- 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
|
@@ -244,6 +244,44 @@ export function shouldReplacePreviewAfterVisualStyleCommit(args: {
|
|
|
244
244
|
return !args.runtimeApplied && !args.runtimeStyleApplied;
|
|
245
245
|
}
|
|
246
246
|
|
|
247
|
+
export interface OptimisticTextDecorationLineEntry {
|
|
248
|
+
key: string;
|
|
249
|
+
value: string;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* BUG-DOUBLE-TOGGLE-RACE — Cmd+U (toggle underline) / Cmd+Shift+X (toggle
|
|
254
|
+
* strikethrough) commit through the SHORTHAND "textDecoration" property, but
|
|
255
|
+
* commitVisualStyles' synchronous optimistic patch to
|
|
256
|
+
* selectedElement.computedStyles only merges the exact key(s) it committed —
|
|
257
|
+
* it never decomposes "textDecoration" into the LONGHAND
|
|
258
|
+
* "textDecorationLine" the toggle reads to decide its next value.
|
|
259
|
+
* `computedStyles.textDecorationLine` only catches up once the bridge's
|
|
260
|
+
* async getComputedStyle round trip lands. A second toggle press within that
|
|
261
|
+
* window would otherwise recompute nextTextDecorationLineValue from the
|
|
262
|
+
* STALE pre-toggle value, land on the exact value the first press already
|
|
263
|
+
* committed, and get deduped as a no-op — consecutive toggles silently stop
|
|
264
|
+
* alternating.
|
|
265
|
+
*
|
|
266
|
+
* Resolves which value a toggle handler should treat as "current": a
|
|
267
|
+
* still-fresh optimistic value this same toggle family already recorded for
|
|
268
|
+
* the SAME selected element (`tracked.key === elementKey`) wins over the
|
|
269
|
+
* (possibly stale) computedStyles reading, since the async measurement that
|
|
270
|
+
* would refresh computedStyles may not have landed yet. A different/new
|
|
271
|
+
* element key (or no tracked entry) falls back to computedStyles, so newly
|
|
272
|
+
* selecting a different element always starts from ITS OWN real state.
|
|
273
|
+
*/
|
|
274
|
+
export function resolveOptimisticTextDecorationLine(
|
|
275
|
+
tracked: OptimisticTextDecorationLineEntry | null | undefined,
|
|
276
|
+
elementKey: string | undefined,
|
|
277
|
+
computedTextDecorationLine: string | undefined,
|
|
278
|
+
): string | undefined {
|
|
279
|
+
if (tracked && elementKey !== undefined && tracked.key === elementKey) {
|
|
280
|
+
return tracked.value;
|
|
281
|
+
}
|
|
282
|
+
return computedTextDecorationLine;
|
|
283
|
+
}
|
|
284
|
+
|
|
247
285
|
/**
|
|
248
286
|
* PF12: decide whether a style change from EditPanel (ScrubInput scrub tick /
|
|
249
287
|
* DesignColorPicker drag tick) should skip the expensive source commit
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import type { DrawAnnotation } from "@/components/visual-editor/DrawOverlay";
|
|
2
|
+
|
|
3
|
+
export interface OverviewAnnotationScreen {
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
x: number;
|
|
7
|
+
y: number;
|
|
8
|
+
width: number;
|
|
9
|
+
height: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface OverviewAnnotationViewportMap {
|
|
13
|
+
width: number;
|
|
14
|
+
height: number;
|
|
15
|
+
zoom: number;
|
|
16
|
+
screens: OverviewAnnotationScreen[];
|
|
17
|
+
board?: { x: number; y: number; width: number; height: number };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function escapeSelector(value: string) {
|
|
21
|
+
return typeof CSS !== "undefined" && typeof CSS.escape === "function"
|
|
22
|
+
? CSS.escape(value)
|
|
23
|
+
: value.replace(/["\\]/g, "\\$&");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function collectOverviewAnnotationViewportMap({
|
|
27
|
+
container,
|
|
28
|
+
screens,
|
|
29
|
+
zoom,
|
|
30
|
+
}: {
|
|
31
|
+
container: HTMLElement;
|
|
32
|
+
screens: readonly { id: string; name: string }[];
|
|
33
|
+
zoom: number;
|
|
34
|
+
}): OverviewAnnotationViewportMap {
|
|
35
|
+
const containerRect = container.getBoundingClientRect();
|
|
36
|
+
const relativeRect = (rect: DOMRect) => ({
|
|
37
|
+
x: rect.left - containerRect.left,
|
|
38
|
+
y: rect.top - containerRect.top,
|
|
39
|
+
width: rect.width,
|
|
40
|
+
height: rect.height,
|
|
41
|
+
});
|
|
42
|
+
const mappedScreens = screens.flatMap((screen) => {
|
|
43
|
+
const shell = container.querySelector<HTMLElement>(
|
|
44
|
+
`[data-frame-id="${escapeSelector(screen.id)}"]`,
|
|
45
|
+
);
|
|
46
|
+
return shell
|
|
47
|
+
? [
|
|
48
|
+
{
|
|
49
|
+
id: screen.id,
|
|
50
|
+
name: screen.name,
|
|
51
|
+
...relativeRect(shell.getBoundingClientRect()),
|
|
52
|
+
},
|
|
53
|
+
]
|
|
54
|
+
: [];
|
|
55
|
+
});
|
|
56
|
+
const boardSurface = container.querySelector<HTMLElement>(
|
|
57
|
+
"[data-board-surface-layer]",
|
|
58
|
+
);
|
|
59
|
+
return {
|
|
60
|
+
width: containerRect.width,
|
|
61
|
+
height: containerRect.height,
|
|
62
|
+
zoom,
|
|
63
|
+
screens: mappedScreens,
|
|
64
|
+
board: boardSurface
|
|
65
|
+
? relativeRect(boardSurface.getBoundingClientRect())
|
|
66
|
+
: undefined,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function annotationBounds(annotation: DrawAnnotation) {
|
|
71
|
+
if (annotation.type === "text") {
|
|
72
|
+
return {
|
|
73
|
+
left: annotation.position.x,
|
|
74
|
+
top: annotation.position.y,
|
|
75
|
+
right: annotation.position.x,
|
|
76
|
+
bottom: annotation.position.y,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
const values = Array.from(
|
|
80
|
+
annotation.pathData?.matchAll(/-?\d+(?:\.\d+)?/g) ?? [],
|
|
81
|
+
(match) => Number(match[0]),
|
|
82
|
+
);
|
|
83
|
+
const xs = values.filter((_, index) => index % 2 === 0);
|
|
84
|
+
const ys = values.filter((_, index) => index % 2 === 1);
|
|
85
|
+
if (!xs.length || !ys.length) return null;
|
|
86
|
+
return {
|
|
87
|
+
left: Math.min(...xs),
|
|
88
|
+
top: Math.min(...ys),
|
|
89
|
+
right: Math.max(...xs),
|
|
90
|
+
bottom: Math.max(...ys),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function mappedAnnotationRegions(
|
|
95
|
+
annotation: DrawAnnotation,
|
|
96
|
+
viewportMap: OverviewAnnotationViewportMap,
|
|
97
|
+
) {
|
|
98
|
+
const bounds = annotationBounds(annotation);
|
|
99
|
+
if (!bounds) return ["board"];
|
|
100
|
+
const screens = viewportMap.screens.filter(
|
|
101
|
+
(screen) =>
|
|
102
|
+
bounds.right >= screen.x &&
|
|
103
|
+
bounds.left <= screen.x + screen.width &&
|
|
104
|
+
bounds.bottom >= screen.y &&
|
|
105
|
+
bounds.top <= screen.y + screen.height,
|
|
106
|
+
);
|
|
107
|
+
return screens.length
|
|
108
|
+
? screens.map((screen) => `${screen.name} (${screen.id})`)
|
|
109
|
+
: ["board outside screens"];
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function formatOverviewAnnotationMessage({
|
|
113
|
+
designId,
|
|
114
|
+
designTitle,
|
|
115
|
+
annotations,
|
|
116
|
+
instruction,
|
|
117
|
+
viewportMap,
|
|
118
|
+
}: {
|
|
119
|
+
designId: string;
|
|
120
|
+
designTitle?: string;
|
|
121
|
+
annotations: DrawAnnotation[];
|
|
122
|
+
instruction: string;
|
|
123
|
+
viewportMap: OverviewAnnotationViewportMap;
|
|
124
|
+
}) {
|
|
125
|
+
const drawing = annotations
|
|
126
|
+
.map((annotation) => {
|
|
127
|
+
const regions = mappedAnnotationRegions(annotation, viewportMap).join(
|
|
128
|
+
", ",
|
|
129
|
+
);
|
|
130
|
+
return annotation.type === "path"
|
|
131
|
+
? `[stroke regions=${regions} color=${annotation.color} w=${annotation.lineWidth}] ${annotation.pathData}`
|
|
132
|
+
: `[label regions=${regions} text="${annotation.text ?? ""}" at ${annotation.position.x.toFixed(0)},${annotation.position.y.toFixed(0)}]`;
|
|
133
|
+
})
|
|
134
|
+
.join("\n");
|
|
135
|
+
const screenMap = viewportMap.screens
|
|
136
|
+
.map(
|
|
137
|
+
(screen) =>
|
|
138
|
+
`- ${screen.name} (${screen.id}): x=${screen.x.toFixed(1)}, y=${screen.y.toFixed(1)}, width=${screen.width.toFixed(1)}, height=${screen.height.toFixed(1)}`,
|
|
139
|
+
)
|
|
140
|
+
.join("\n");
|
|
141
|
+
const boardMap = viewportMap.board
|
|
142
|
+
? `Board surface: x=${viewportMap.board.x.toFixed(1)}, y=${viewportMap.board.y.toFixed(1)}, width=${viewportMap.board.width.toFixed(1)}, height=${viewportMap.board.height.toFixed(1)}`
|
|
143
|
+
: "Board surface: the viewport outside the mapped screen rectangles";
|
|
144
|
+
|
|
145
|
+
return [
|
|
146
|
+
`[Annotations on the all-screens canvas for design ${designId}${designTitle ? ` (${designTitle})` : ""}]`,
|
|
147
|
+
`Overview viewport: ${viewportMap.width.toFixed(0)}x${viewportMap.height.toFixed(0)} at ${viewportMap.zoom.toFixed(1)}% zoom`,
|
|
148
|
+
"Coordinates and screen bounds are viewport-relative at send time; their x/y positions encode the current canvas pan.",
|
|
149
|
+
"",
|
|
150
|
+
"[Screen map]",
|
|
151
|
+
screenMap || "- No screen rectangles were visible",
|
|
152
|
+
boardMap,
|
|
153
|
+
...(drawing ? ["", "[Drawing]", drawing] : []),
|
|
154
|
+
"",
|
|
155
|
+
instruction || "Apply these annotations to the design.",
|
|
156
|
+
].join("\n");
|
|
157
|
+
}
|
|
@@ -76,9 +76,11 @@ export function getScreenFrameOriginCanvas(args: {
|
|
|
76
76
|
* `getInitialFrameGeometry(index, ...)` fallback `getSelectedScreenGeometryForInspector`
|
|
77
77
|
* and `getScreenFrameOriginCanvas` above already use — as `FrameEntry[]` for
|
|
78
78
|
* `@shared/canvas-math` bounds/fit helpers (`getFrameGroupBounds`,
|
|
79
|
-
* `getCameraForBounds`). Optionally includes the
|
|
80
|
-
*
|
|
81
|
-
*
|
|
79
|
+
* `getCameraForBounds`). Optionally includes the bounds of actual board
|
|
80
|
+
* content. This must never receive the logical board hit-test surface: that
|
|
81
|
+
* surface is intentionally enormous and including it would make zoom-to-fit
|
|
82
|
+
* and adjacent-screen placement behave as though the design were 131,072px
|
|
83
|
+
* wide even when the board is empty.
|
|
82
84
|
*/
|
|
83
85
|
export function getAllScreenFrameEntries(args: {
|
|
84
86
|
overviewScreens: Array<{
|
|
@@ -87,7 +89,7 @@ export function getAllScreenFrameEntries(args: {
|
|
|
87
89
|
height?: number;
|
|
88
90
|
}>;
|
|
89
91
|
canvasFrameGeometryById: CanvasFrameGeometryById;
|
|
90
|
-
|
|
92
|
+
boardContentBounds?: FrameGeometry | null;
|
|
91
93
|
boardFileId?: string | null;
|
|
92
94
|
}): FrameEntry[] {
|
|
93
95
|
const entries: FrameEntry[] = args.overviewScreens.map((screen, index) => {
|
|
@@ -103,10 +105,10 @@ export function getAllScreenFrameEntries(args: {
|
|
|
103
105
|
});
|
|
104
106
|
if (
|
|
105
107
|
args.boardFileId &&
|
|
106
|
-
args.
|
|
108
|
+
args.boardContentBounds &&
|
|
107
109
|
!entries.some((entry) => entry.id === args.boardFileId)
|
|
108
110
|
) {
|
|
109
|
-
entries.push({ id: args.boardFileId, geometry: args.
|
|
111
|
+
entries.push({ id: args.boardFileId, geometry: args.boardContentBounds });
|
|
110
112
|
}
|
|
111
113
|
return entries;
|
|
112
114
|
}
|
|
@@ -436,3 +438,57 @@ export function shouldPopToOverviewOnZoomChange(args: {
|
|
|
436
438
|
if (args.suppressExplicitZoom) return false;
|
|
437
439
|
return shouldPopToOverviewOnZoomOut(args);
|
|
438
440
|
}
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* PASTE-HERE-IN-CONTENT: converts a viewport client point into a
|
|
444
|
+
* screen/iframe-local canvas point, given that iframe's own
|
|
445
|
+
* boundingClientRect and the zoom percent (0-100) currently scaling it.
|
|
446
|
+
*
|
|
447
|
+
* Shared by DesignEditor's getContextCanvasPoint (the background/board
|
|
448
|
+
* right-click path, reached through CanvasContextMenu's own
|
|
449
|
+
* onContextMenuCapture) and handleIframeContextMenu (a right-click that
|
|
450
|
+
* lands ON rendered screen content — an element, or empty in-screen space —
|
|
451
|
+
* which opens the menu imperatively via a ref and so bypasses
|
|
452
|
+
* onContextMenuCapture, and with it getCanvasPoint, entirely). Before this
|
|
453
|
+
* was wired into handleIframeContextMenu too, "Paste here" triggered from a
|
|
454
|
+
* right-click on actual screen content never got a canvasX/canvasY at all
|
|
455
|
+
* and silently fell back to the position-less cascade/offset paste instead
|
|
456
|
+
* of landing under the cursor — only a right-click on the empty canvas
|
|
457
|
+
* background (which still goes through onContextMenuCapture) worked.
|
|
458
|
+
*
|
|
459
|
+
* Returns null when there's no iframe rect to measure against or the zoom
|
|
460
|
+
* factor is non-positive (would divide by zero / invert the sign), matching
|
|
461
|
+
* "no reliable canvas point" — callers fall back to their own
|
|
462
|
+
* container-relative or position-less behavior in that case.
|
|
463
|
+
*/
|
|
464
|
+
export function computeIframeLocalCanvasPoint(args: {
|
|
465
|
+
clientX: number;
|
|
466
|
+
clientY: number;
|
|
467
|
+
iframeRect: { left: number; top: number } | null | undefined;
|
|
468
|
+
zoomPercent: number;
|
|
469
|
+
}): { x: number; y: number } | null {
|
|
470
|
+
if (!args.iframeRect || !(args.zoomPercent > 0)) return null;
|
|
471
|
+
const factor = args.zoomPercent / 100;
|
|
472
|
+
return {
|
|
473
|
+
x: Math.max(0, (args.clientX - args.iframeRect.left) / factor),
|
|
474
|
+
y: Math.max(0, (args.clientY - args.iframeRect.top) / factor),
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Reads MultiScreenCanvas's live imperative zoom from the exact inline
|
|
480
|
+
* transform written by `applyViewToDom` while a wheel/pinch gesture is still
|
|
481
|
+
* settling. React's `overviewCanvasZoom` intentionally lags those per-frame
|
|
482
|
+
* DOM writes until the gesture commit, so cursor-sensitive commands must use
|
|
483
|
+
* this value when it is available.
|
|
484
|
+
*/
|
|
485
|
+
export function readOverviewZoomPercentFromTransform(
|
|
486
|
+
transform: string | null | undefined,
|
|
487
|
+
fallbackZoomPercent: number,
|
|
488
|
+
): number {
|
|
489
|
+
const match = transform?.match(/(?:^|\s)scale\(\s*([-+]?\d*\.?\d+)\s*\)/i);
|
|
490
|
+
const scale = match ? Number(match[1]) : Number.NaN;
|
|
491
|
+
return Number.isFinite(scale) && scale > 0
|
|
492
|
+
? scale * 100
|
|
493
|
+
: fallbackZoomPercent;
|
|
494
|
+
}
|
|
@@ -18,6 +18,12 @@ import {
|
|
|
18
18
|
|
|
19
19
|
import type { ElementInfo } from "@/components/design/types";
|
|
20
20
|
|
|
21
|
+
import {
|
|
22
|
+
buildReactSemanticHandoff,
|
|
23
|
+
redactReactSourceAnchor,
|
|
24
|
+
type ReactSourceAnchor,
|
|
25
|
+
type ReactSourceScope,
|
|
26
|
+
} from "./react-semantic-handoff";
|
|
21
27
|
import { camelStyleProperty } from "./style-utils";
|
|
22
28
|
|
|
23
29
|
export interface PendingVisualStyleEdit {
|
|
@@ -26,6 +32,7 @@ export interface PendingVisualStyleEdit {
|
|
|
26
32
|
screenName: string;
|
|
27
33
|
selector: string;
|
|
28
34
|
sourceId?: string | null;
|
|
35
|
+
sourceAnchor?: ReactSourceAnchor;
|
|
29
36
|
tagName?: string | null;
|
|
30
37
|
classes: string[];
|
|
31
38
|
styles: Record<string, string>;
|
|
@@ -104,6 +111,7 @@ export interface PendingLiveTextEdit {
|
|
|
104
111
|
screenName: string;
|
|
105
112
|
selector: string;
|
|
106
113
|
sourceId?: string | null;
|
|
114
|
+
sourceAnchor?: ReactSourceAnchor;
|
|
107
115
|
tagName?: string | null;
|
|
108
116
|
classes: string[];
|
|
109
117
|
value: string;
|
|
@@ -120,13 +128,154 @@ export interface PendingLiveStructureEdit {
|
|
|
120
128
|
screenName: string;
|
|
121
129
|
selector: string;
|
|
122
130
|
sourceId?: string | null;
|
|
131
|
+
sourceAnchor?: ReactSourceAnchor;
|
|
123
132
|
anchorSelector: string;
|
|
124
133
|
anchorSourceId?: string | null;
|
|
134
|
+
anchorSourceAnchor?: ReactSourceAnchor;
|
|
125
135
|
placement: "before" | "after" | "inside";
|
|
126
136
|
requestId?: string;
|
|
127
137
|
updatedAt: number;
|
|
128
138
|
}
|
|
129
139
|
|
|
140
|
+
/**
|
|
141
|
+
* Convert bridge provenance into a bounded semantic source anchor. Runtime
|
|
142
|
+
* ids remain useful for correlating the live preview, but they are never
|
|
143
|
+
* treated as source identities by the coding-agent handoff.
|
|
144
|
+
*/
|
|
145
|
+
interface NormalizedResolvablePath {
|
|
146
|
+
value: string;
|
|
147
|
+
absolute: boolean;
|
|
148
|
+
caseInsensitive: boolean;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function normalizeResolvablePath(
|
|
152
|
+
rawValue: string | undefined,
|
|
153
|
+
): NormalizedResolvablePath | undefined {
|
|
154
|
+
const raw = rawValue?.trim().replace(/\\/g, "/");
|
|
155
|
+
if (!raw || raw.includes("\0")) return undefined;
|
|
156
|
+
|
|
157
|
+
let prefix = "";
|
|
158
|
+
let remainder = raw;
|
|
159
|
+
let absolute = false;
|
|
160
|
+
let caseInsensitive = false;
|
|
161
|
+
const drive = raw.match(/^([a-z]):(\/.*)?$/i);
|
|
162
|
+
if (drive) {
|
|
163
|
+
// `C:foo` is drive-relative and must not be treated as a project path.
|
|
164
|
+
if (!drive[2]?.startsWith("/")) return undefined;
|
|
165
|
+
prefix = `${drive[1]!.toUpperCase()}:/`;
|
|
166
|
+
remainder = drive[2].slice(1);
|
|
167
|
+
absolute = true;
|
|
168
|
+
caseInsensitive = true;
|
|
169
|
+
} else if (raw.startsWith("//")) {
|
|
170
|
+
const [server, share, ...rest] = raw.slice(2).split("/");
|
|
171
|
+
if (!server || !share) return undefined;
|
|
172
|
+
prefix = `//${server}/${share}`;
|
|
173
|
+
remainder = rest.join("/");
|
|
174
|
+
absolute = true;
|
|
175
|
+
caseInsensitive = true;
|
|
176
|
+
} else if (raw.startsWith("/")) {
|
|
177
|
+
prefix = "/";
|
|
178
|
+
remainder = raw.slice(1);
|
|
179
|
+
absolute = true;
|
|
180
|
+
} else if (/^[a-z]+:/i.test(raw)) {
|
|
181
|
+
// URL-like values and unsupported drive-relative paths are not files.
|
|
182
|
+
return undefined;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const segments: string[] = [];
|
|
186
|
+
for (const segment of remainder.split("/")) {
|
|
187
|
+
if (!segment || segment === ".") continue;
|
|
188
|
+
if (segment === "..") {
|
|
189
|
+
if (segments.length > 0) {
|
|
190
|
+
segments.pop();
|
|
191
|
+
} else if (!absolute) {
|
|
192
|
+
// A relative path may not escape its unknown project root.
|
|
193
|
+
return undefined;
|
|
194
|
+
}
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
segments.push(segment);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const suffix = segments.join("/");
|
|
201
|
+
const value = absolute
|
|
202
|
+
? prefix.endsWith("/")
|
|
203
|
+
? `${prefix}${suffix}`
|
|
204
|
+
: suffix
|
|
205
|
+
? `${prefix}/${suffix}`
|
|
206
|
+
: prefix
|
|
207
|
+
: suffix;
|
|
208
|
+
if (!value) return undefined;
|
|
209
|
+
return { value, absolute, caseInsensitive };
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function sourcePathRelativeToRoot(args: {
|
|
213
|
+
sourceFile: string;
|
|
214
|
+
rootPath?: string;
|
|
215
|
+
}): string | undefined {
|
|
216
|
+
const source = normalizeResolvablePath(args.sourceFile);
|
|
217
|
+
if (!source) return undefined;
|
|
218
|
+
if (!source.absolute) return source.value;
|
|
219
|
+
|
|
220
|
+
const root = normalizeResolvablePath(args.rootPath);
|
|
221
|
+
if (!root?.absolute || root.caseInsensitive !== source.caseInsensitive) {
|
|
222
|
+
return undefined;
|
|
223
|
+
}
|
|
224
|
+
const comparableSource = source.caseInsensitive
|
|
225
|
+
? source.value.toLowerCase()
|
|
226
|
+
: source.value;
|
|
227
|
+
const comparableRoot = root.caseInsensitive
|
|
228
|
+
? root.value.toLowerCase()
|
|
229
|
+
: root.value;
|
|
230
|
+
const rootPrefix = comparableRoot.endsWith("/")
|
|
231
|
+
? comparableRoot
|
|
232
|
+
: `${comparableRoot}/`;
|
|
233
|
+
if (!comparableSource.startsWith(rootPrefix)) return undefined;
|
|
234
|
+
const relative = source.value.slice(rootPrefix.length);
|
|
235
|
+
return normalizeResolvablePath(relative)?.value;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export function reactSourceAnchorForPendingEdit(args: {
|
|
239
|
+
info?: Pick<ElementInfo, "provenance" | "sourceId" | "selector"> | null;
|
|
240
|
+
id?: string;
|
|
241
|
+
runtimeMultiplicity?: number;
|
|
242
|
+
scope?: ReactSourceScope;
|
|
243
|
+
reason?: string;
|
|
244
|
+
rootPath?: string;
|
|
245
|
+
}): ReactSourceAnchor | undefined {
|
|
246
|
+
const provenance = args.info?.provenance;
|
|
247
|
+
const sourceFile = provenance?.sourceFile?.trim();
|
|
248
|
+
if (!sourceFile || !provenance?.line || !provenance.column) return undefined;
|
|
249
|
+
const runtimeMultiplicity =
|
|
250
|
+
Number.isInteger(args.runtimeMultiplicity) &&
|
|
251
|
+
(args.runtimeMultiplicity ?? 0) > 0
|
|
252
|
+
? args.runtimeMultiplicity!
|
|
253
|
+
: 1;
|
|
254
|
+
const relPath = sourcePathRelativeToRoot({
|
|
255
|
+
sourceFile,
|
|
256
|
+
rootPath: args.rootPath,
|
|
257
|
+
});
|
|
258
|
+
return {
|
|
259
|
+
id:
|
|
260
|
+
args.id?.trim() ||
|
|
261
|
+
args.info?.sourceId?.trim() ||
|
|
262
|
+
args.info?.selector?.trim() ||
|
|
263
|
+
undefined,
|
|
264
|
+
// Keep the raw Fiber value only in local state. Prompt serialization goes
|
|
265
|
+
// through redactReactSourceAnchor, which omits absolute paths until the
|
|
266
|
+
// connection root has resolved them to a safe project-relative relPath.
|
|
267
|
+
sourceFile,
|
|
268
|
+
...(relPath ? { relPath } : {}),
|
|
269
|
+
line: provenance.line,
|
|
270
|
+
column: provenance.column,
|
|
271
|
+
component: provenance.component,
|
|
272
|
+
runtimeMultiplicity,
|
|
273
|
+
...(args.reason?.trim() ? { reason: args.reason.trim() } : {}),
|
|
274
|
+
scope:
|
|
275
|
+
args.scope ?? (runtimeMultiplicity > 1 ? "repeated-render" : "unknown"),
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
|
|
130
279
|
export type PendingLiveNonStyleEdit =
|
|
131
280
|
| PendingLiveTextEdit
|
|
132
281
|
| PendingLiveStructureEdit;
|
|
@@ -289,6 +438,7 @@ export function formatPendingVisualStylePrompt(args: {
|
|
|
289
438
|
screenName: edit.screenName,
|
|
290
439
|
selector: edit.selector,
|
|
291
440
|
sourceId: edit.sourceId ?? null,
|
|
441
|
+
sourceAnchor: redactReactSourceAnchor(edit.sourceAnchor),
|
|
292
442
|
tagName: edit.tagName ?? null,
|
|
293
443
|
classes: edit.classes,
|
|
294
444
|
styles: edit.styles,
|
|
@@ -297,6 +447,21 @@ export function formatPendingVisualStylePrompt(args: {
|
|
|
297
447
|
const hasBreakpointScopedEdits = args.edits.some(
|
|
298
448
|
(edit) => edit.breakpoint && edit.breakpoint.upperBoundPx !== null,
|
|
299
449
|
);
|
|
450
|
+
const reactSourceAnchors = [
|
|
451
|
+
...args.edits.map((edit) => edit.sourceAnchor),
|
|
452
|
+
...(args.liveEdits ?? []).flatMap((edit) =>
|
|
453
|
+
edit.kind === "structure"
|
|
454
|
+
? [edit.sourceAnchor, edit.anchorSourceAnchor]
|
|
455
|
+
: [edit.sourceAnchor],
|
|
456
|
+
),
|
|
457
|
+
].filter((anchor): anchor is ReactSourceAnchor => Boolean(anchor));
|
|
458
|
+
const hasReactSourceAnchors = reactSourceAnchors.length > 0;
|
|
459
|
+
const hasRepeatedOrSharedReactScope = reactSourceAnchors.some(
|
|
460
|
+
(anchor) =>
|
|
461
|
+
(anchor.runtimeMultiplicity ?? 1) > 1 ||
|
|
462
|
+
anchor.scope === "repeated-render" ||
|
|
463
|
+
anchor.scope === "shared-component-definition",
|
|
464
|
+
);
|
|
300
465
|
const liveEditPayload = (args.liveEdits ?? []).map((edit) => {
|
|
301
466
|
if (edit.kind === "text") {
|
|
302
467
|
return {
|
|
@@ -306,12 +471,44 @@ export function formatPendingVisualStylePrompt(args: {
|
|
|
306
471
|
screenName: edit.screenName,
|
|
307
472
|
selector: edit.selector,
|
|
308
473
|
sourceId: edit.sourceId ?? null,
|
|
474
|
+
sourceAnchor: redactReactSourceAnchor(edit.sourceAnchor),
|
|
309
475
|
tagName: edit.tagName ?? null,
|
|
310
476
|
classes: edit.classes,
|
|
311
477
|
value: edit.value,
|
|
312
478
|
html: edit.html,
|
|
313
479
|
};
|
|
314
480
|
}
|
|
481
|
+
const subjectAnchor = edit.sourceAnchor
|
|
482
|
+
? { ...edit.sourceAnchor, id: "subject" }
|
|
483
|
+
: undefined;
|
|
484
|
+
const targetAnchor = edit.anchorSourceAnchor
|
|
485
|
+
? { ...edit.anchorSourceAnchor, id: "target" }
|
|
486
|
+
: undefined;
|
|
487
|
+
const semanticHandoff =
|
|
488
|
+
subjectAnchor && targetAnchor
|
|
489
|
+
? buildReactSemanticHandoff({
|
|
490
|
+
operation: edit.placement === "inside" ? "reparent" : "move",
|
|
491
|
+
desiredChange: `Move the selected runtime element ${edit.placement} the target runtime element.`,
|
|
492
|
+
sourceAnchors: [subjectAnchor, targetAnchor],
|
|
493
|
+
runtimeRelationship: {
|
|
494
|
+
kind: edit.placement,
|
|
495
|
+
subjectAnchorIds: ["subject"],
|
|
496
|
+
targetAnchorId: "target",
|
|
497
|
+
screenId: edit.screenId,
|
|
498
|
+
description: `${edit.selector} ${edit.placement} ${edit.anchorSelector}`,
|
|
499
|
+
},
|
|
500
|
+
// The packet intentionally starts without a hash: its execution
|
|
501
|
+
// contract requires read-local-file before every write.
|
|
502
|
+
versionHashes: [],
|
|
503
|
+
})
|
|
504
|
+
: {
|
|
505
|
+
ok: false as const,
|
|
506
|
+
rejection: {
|
|
507
|
+
code: "missing-source-provenance" as const,
|
|
508
|
+
reason:
|
|
509
|
+
"Exact subject and target source anchors were not both available for this React structure edit.",
|
|
510
|
+
},
|
|
511
|
+
};
|
|
315
512
|
return {
|
|
316
513
|
kind: edit.kind,
|
|
317
514
|
screenId: edit.screenId,
|
|
@@ -319,9 +516,14 @@ export function formatPendingVisualStylePrompt(args: {
|
|
|
319
516
|
screenName: edit.screenName,
|
|
320
517
|
selector: edit.selector,
|
|
321
518
|
sourceId: edit.sourceId ?? null,
|
|
519
|
+
sourceAnchor: redactReactSourceAnchor(edit.sourceAnchor),
|
|
322
520
|
anchorSelector: edit.anchorSelector,
|
|
323
521
|
anchorSourceId: edit.anchorSourceId ?? null,
|
|
522
|
+
anchorSourceAnchor: redactReactSourceAnchor(edit.anchorSourceAnchor),
|
|
324
523
|
placement: edit.placement,
|
|
524
|
+
...(semanticHandoff.ok
|
|
525
|
+
? { semanticHandoff: semanticHandoff.handoff }
|
|
526
|
+
: { semanticHandoffFailure: semanticHandoff.rejection }),
|
|
325
527
|
};
|
|
326
528
|
});
|
|
327
529
|
|
|
@@ -333,6 +535,12 @@ export function formatPendingVisualStylePrompt(args: {
|
|
|
333
535
|
: "",
|
|
334
536
|
"",
|
|
335
537
|
"Use the Design source tools to make the source match the current live canvas preview. Read each target screen, resolve source ids/selectors through the code-layer projection, then apply the style, text, and structure changes with focused source edits. Preserve layout, behavior, and unrelated styling.",
|
|
538
|
+
hasReactSourceAnchors
|
|
539
|
+
? "React sourceAnchor fields are source provenance; runtime source ids and selectors are correlation hints only. Verify every project-relative file, line, column, component, and surrounding control flow before editing. Never use a generic AST reparent, group, wrapper, or other structural transform. For semantic structure edits, follow the embedded semanticHandoff packet and use this exact guarded sequence: read-local-file, capture its versionHash, obtain human write consent, write-local-file with expectedVersionHash and requireExpectedVersionHash: true, then keep the preview pending until HMR proves the intended runtime relationship. On a version conflict, re-read and re-plan; never overwrite blindly."
|
|
540
|
+
: "",
|
|
541
|
+
hasRepeatedOrSharedReactScope
|
|
542
|
+
? "At least one React anchor is repeated at runtime or resolves to a shared component definition. Inspect map/conditional/component call sites and confirm whether the change should affect one instance or every instance before writing source."
|
|
543
|
+
: "",
|
|
336
544
|
hasBreakpointScopedEdits
|
|
337
545
|
? "Edits that carry a `breakpoint` field were made while a narrower breakpoint frame was active: apply them as width-scoped overrides (apply-visual-edit with `activeFrameWidthPx` set to breakpoint.activeWidthPx), NOT as base writes — base values must keep rendering at wider viewports."
|
|
338
546
|
: "",
|
|
@@ -361,6 +569,43 @@ export function resolveOverviewScreenSourceType(
|
|
|
361
569
|
);
|
|
362
570
|
}
|
|
363
571
|
|
|
572
|
+
export function shouldUseRuntimeLayerProjection(args: {
|
|
573
|
+
screen:
|
|
574
|
+
| { sourceType?: unknown; bridgeUrl?: string | null }
|
|
575
|
+
| null
|
|
576
|
+
| undefined;
|
|
577
|
+
fallbackSourceType?: DesignSourceType;
|
|
578
|
+
content: string;
|
|
579
|
+
}): boolean {
|
|
580
|
+
if (
|
|
581
|
+
resolveOverviewScreenSourceType(
|
|
582
|
+
args.screen,
|
|
583
|
+
args.fallbackSourceType ?? "inline",
|
|
584
|
+
) !== "localhost"
|
|
585
|
+
) {
|
|
586
|
+
return false;
|
|
587
|
+
}
|
|
588
|
+
try {
|
|
589
|
+
const url = new URL(args.content.trim());
|
|
590
|
+
return url.protocol === "http:" || url.protocol === "https:";
|
|
591
|
+
} catch {
|
|
592
|
+
return false;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
export function shouldPreferRuntimeLayerProjection(args: {
|
|
597
|
+
eligible: boolean;
|
|
598
|
+
runtimeNodeCount: number;
|
|
599
|
+
sourceNodeCount: number;
|
|
600
|
+
}): boolean {
|
|
601
|
+
// A hydrated localhost tree is the visible app's ground truth even when SSR
|
|
602
|
+
// happened to emit the same number of nodes (or more wrappers). Keep the
|
|
603
|
+
// source projection separately for writes; never use counts to decide which
|
|
604
|
+
// tree represents the live Layers panel.
|
|
605
|
+
void args.sourceNodeCount;
|
|
606
|
+
return args.eligible && args.runtimeNodeCount > 0;
|
|
607
|
+
}
|
|
608
|
+
|
|
364
609
|
export function shouldShowPendingVisualStyleApply(args: {
|
|
365
610
|
edits: readonly PendingVisualStyleEdit[];
|
|
366
611
|
liveEdits?: readonly PendingLiveNonStyleEdit[];
|