@agent-native/core 0.92.9 → 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 +29 -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/deploy/build.ts +133 -5
- 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/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/routes.d.ts +1 -1
- package/dist/deploy/build.d.ts +24 -0
- package/dist/deploy/build.d.ts.map +1 -1
- package/dist/deploy/build.js +102 -5
- package/dist/deploy/build.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/notifications/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +5 -5
- package/dist/progress/routes.d.ts +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
|
@@ -0,0 +1,544 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* swap-component-instance — Figma's "Swap instance".
|
|
3
|
+
*
|
|
4
|
+
* DESIGN NOTE — mapping onto this codebase's data model: there is no
|
|
5
|
+
* separate component "definition"/template anywhere here (see
|
|
6
|
+
* `shared/component-model.ts`) — every instance is an independently
|
|
7
|
+
* duplicated copy of HTML matched purely by its
|
|
8
|
+
* `data-agent-native-component="Name"` annotation. "Swap to component B"
|
|
9
|
+
* therefore means: find an existing instance of B somewhere in the design
|
|
10
|
+
* (any screen), copy ITS current markup in as a replacement for the selected
|
|
11
|
+
* instance of A, and carry over whichever `data-agent-native-prop-*`
|
|
12
|
+
* overrides the selected instance had that B's own instances also declare
|
|
13
|
+
* (Figma's "preserve same-named overrides").
|
|
14
|
+
*
|
|
15
|
+
* Scope decisions, deliberately conservative:
|
|
16
|
+
* - Requires at least one existing instance of the target component
|
|
17
|
+
* somewhere in the design (any file) to copy markup from. There is no
|
|
18
|
+
* component library/definition to fall back to — this is a hard
|
|
19
|
+
* requirement, not a data-model gap to route around.
|
|
20
|
+
* - Only `data-agent-native-prop-*` attribute overrides are carried over.
|
|
21
|
+
* The `x-data` Alpine expression is intentionally NOT merged: Alpine state
|
|
22
|
+
* can hold arbitrary JS (methods, nested objects, expressions) and this
|
|
23
|
+
* codebase already treats blind x-data rewrites as unsafe (see
|
|
24
|
+
* `component-section.tsx`'s `canRebuildAlpineDataLosslessly` bail-out) — the
|
|
25
|
+
* swapped-in instance keeps ITS OWN component's default `x-data` as-is.
|
|
26
|
+
* Prop names present on the old instance but not declared by the new
|
|
27
|
+
* component are dropped (`droppedProps`); prop names the new component
|
|
28
|
+
* declares that the old instance didn't override keep the new component's
|
|
29
|
+
* default (`defaultedProps`). Both are reported so the caller/UI can tell
|
|
30
|
+
* the user what changed.
|
|
31
|
+
* - The selected instance's own `data-agent-native-node-id` is preserved on
|
|
32
|
+
* the swapped-in markup (rather than keeping whatever id the copied
|
|
33
|
+
* instance had) so selection, undo, and any other node-id-keyed state keep
|
|
34
|
+
* addressing the same slot on the canvas.
|
|
35
|
+
*
|
|
36
|
+
* Persists through the same deterministic HTML-patch + collab seam as
|
|
37
|
+
* `apply-component-prop-edit` (`writeInlineSourceFile` + `expectedVersionHash`
|
|
38
|
+
* CAS guard) — same undo/collab posture as every other HTML-mutating action.
|
|
39
|
+
*
|
|
40
|
+
* Inline/Alpine designs only; real-app sources fail closed.
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
import { randomUUID } from "node:crypto";
|
|
44
|
+
|
|
45
|
+
import { defineAction } from "@agent-native/core/action";
|
|
46
|
+
import {
|
|
47
|
+
agentEnterDocument,
|
|
48
|
+
agentLeaveDocument,
|
|
49
|
+
agentUpdateSelection,
|
|
50
|
+
} from "@agent-native/core/collab";
|
|
51
|
+
import {
|
|
52
|
+
accessFilter,
|
|
53
|
+
assertAccess,
|
|
54
|
+
resolveAccess,
|
|
55
|
+
} from "@agent-native/core/sharing";
|
|
56
|
+
import { and, eq } from "drizzle-orm";
|
|
57
|
+
import { z } from "zod";
|
|
58
|
+
|
|
59
|
+
import { getDb, schema } from "../server/db/index.js";
|
|
60
|
+
import "../server/db/index.js"; // ensure registerShareableResource runs
|
|
61
|
+
import {
|
|
62
|
+
writeInlineSourceFile,
|
|
63
|
+
type SourceWorkspaceFile,
|
|
64
|
+
} from "../server/source-workspace.js";
|
|
65
|
+
import {
|
|
66
|
+
buildCodeLayerProjection,
|
|
67
|
+
type CodeLayerNode,
|
|
68
|
+
} from "../shared/code-layer.js";
|
|
69
|
+
import type { CodeLayerSource } from "../shared/code-layer.js";
|
|
70
|
+
import { agentSelectionDescriptor } from "../shared/collab-selection.js";
|
|
71
|
+
import {
|
|
72
|
+
componentNameFor,
|
|
73
|
+
componentNodeIdMatches,
|
|
74
|
+
extractProps,
|
|
75
|
+
propNameToDataAttribute,
|
|
76
|
+
} from "../shared/component-model.js";
|
|
77
|
+
import { designSourceTypeFromData } from "../shared/source-mode.js";
|
|
78
|
+
import { sourceContentHash } from "../shared/source-workspace.js";
|
|
79
|
+
import { applyRootAttributeEdit } from "./apply-component-prop-edit.js";
|
|
80
|
+
|
|
81
|
+
// ─── Pure markup helpers ────────────────────────────────────────────────────
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Find the end offset (exclusive) of the opening tag at the start of
|
|
85
|
+
* `markup`, respecting quoted attribute values that may themselves contain
|
|
86
|
+
* `>`. Returns `markup.length` if no unquoted `>` is found. Pure.
|
|
87
|
+
*/
|
|
88
|
+
export function findOpenTagEnd(markup: string): number {
|
|
89
|
+
let quote: '"' | "'" | null = null;
|
|
90
|
+
for (let i = 0; i < markup.length; i++) {
|
|
91
|
+
const ch = markup[i];
|
|
92
|
+
if (quote) {
|
|
93
|
+
if (ch === quote) quote = null;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (ch === '"' || ch === "'") {
|
|
97
|
+
quote = ch;
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (ch === ">") return i + 1;
|
|
101
|
+
}
|
|
102
|
+
return markup.length;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Set (or replace) a single attribute on a standalone markup string's opening
|
|
107
|
+
* tag — the same splice `applyRootAttributeEdit` performs against a full
|
|
108
|
+
* document, but for an already-extracted outerHTML fragment (e.g. copied from
|
|
109
|
+
* another instance elsewhere in the design). Pure — exported for tests.
|
|
110
|
+
*/
|
|
111
|
+
export function setAttributeOnMarkup(
|
|
112
|
+
markup: string,
|
|
113
|
+
attrName: string,
|
|
114
|
+
attrValue: string,
|
|
115
|
+
): string {
|
|
116
|
+
const openEnd = findOpenTagEnd(markup);
|
|
117
|
+
const result = applyRootAttributeEdit(
|
|
118
|
+
markup,
|
|
119
|
+
{ openStart: 0, openEnd },
|
|
120
|
+
attrName,
|
|
121
|
+
attrValue,
|
|
122
|
+
);
|
|
123
|
+
return result.content;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export interface SwapOverrideResult {
|
|
127
|
+
markup: string;
|
|
128
|
+
overriddenProps: string[];
|
|
129
|
+
droppedProps: string[];
|
|
130
|
+
defaultedProps: string[];
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Re-key every descendant in markup copied from another component instance.
|
|
135
|
+
* Keeping the copied `data-agent-native-node-id` values would create duplicate
|
|
136
|
+
* stable layer identities, so selection and the aggregate layer-owner map
|
|
137
|
+
* could jump back to the source instance after a swap. The root keeps the
|
|
138
|
+
* selected instance's id later in `mergeComponentSwapOverrides`; descendants
|
|
139
|
+
* receive fresh ids here.
|
|
140
|
+
*
|
|
141
|
+
* `createNodeId` is injectable so the pure behavior stays deterministic in
|
|
142
|
+
* tests while production uses cryptographically unique ids.
|
|
143
|
+
*/
|
|
144
|
+
export function reassignCopiedDescendantNodeIds(
|
|
145
|
+
markup: string,
|
|
146
|
+
createNodeId: () => string = () => `an-${randomUUID()}`,
|
|
147
|
+
): string {
|
|
148
|
+
const projection = buildCodeLayerProjection(markup);
|
|
149
|
+
const rootIds = new Set(projection.rootNodeIds);
|
|
150
|
+
const descendants = projection.nodes
|
|
151
|
+
.filter((node) => !rootIds.has(node.id) && node.source)
|
|
152
|
+
.sort(
|
|
153
|
+
(a, b) =>
|
|
154
|
+
(b.source?.openStart ?? Number.NEGATIVE_INFINITY) -
|
|
155
|
+
(a.source?.openStart ?? Number.NEGATIVE_INFINITY),
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
let content = markup;
|
|
159
|
+
for (const descendant of descendants) {
|
|
160
|
+
content = applyRootAttributeEdit(
|
|
161
|
+
content,
|
|
162
|
+
descendant.source,
|
|
163
|
+
"data-agent-native-node-id",
|
|
164
|
+
createNodeId(),
|
|
165
|
+
).content;
|
|
166
|
+
}
|
|
167
|
+
return content;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Apply the selected instance's `data-agent-native-prop-*` overrides onto a
|
|
172
|
+
* copy of the target component's markup, carrying over only prop names the
|
|
173
|
+
* target component ALSO declares, then stamp the selected instance's stable
|
|
174
|
+
* node id onto the result. Pure — exported for tests.
|
|
175
|
+
*/
|
|
176
|
+
export function mergeComponentSwapOverrides(
|
|
177
|
+
targetMarkup: string,
|
|
178
|
+
currentProps: Array<{ name: string; value: string }>,
|
|
179
|
+
targetDefaultProps: Array<{ name: string; value: string }>,
|
|
180
|
+
nodeId: string,
|
|
181
|
+
): SwapOverrideResult {
|
|
182
|
+
const targetNames = new Set(targetDefaultProps.map((p) => p.name));
|
|
183
|
+
const currentNames = new Set(currentProps.map((p) => p.name));
|
|
184
|
+
|
|
185
|
+
const overriddenProps: string[] = [];
|
|
186
|
+
const droppedProps: string[] = [];
|
|
187
|
+
const defaultedProps: string[] = [];
|
|
188
|
+
|
|
189
|
+
let markup = reassignCopiedDescendantNodeIds(targetMarkup);
|
|
190
|
+
|
|
191
|
+
for (const prop of currentProps) {
|
|
192
|
+
if (targetNames.has(prop.name)) {
|
|
193
|
+
markup = setAttributeOnMarkup(
|
|
194
|
+
markup,
|
|
195
|
+
propNameToDataAttribute(prop.name),
|
|
196
|
+
prop.value,
|
|
197
|
+
);
|
|
198
|
+
overriddenProps.push(prop.name);
|
|
199
|
+
} else {
|
|
200
|
+
droppedProps.push(prop.name);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
for (const prop of targetDefaultProps) {
|
|
205
|
+
if (!currentNames.has(prop.name)) defaultedProps.push(prop.name);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
markup = setAttributeOnMarkup(markup, "data-agent-native-node-id", nodeId);
|
|
209
|
+
|
|
210
|
+
return { markup, overriddenProps, droppedProps, defaultedProps };
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// ─── Persistence ────────────────────────────────────────────────────────────
|
|
214
|
+
|
|
215
|
+
async function persistEdit(file: {
|
|
216
|
+
id: string;
|
|
217
|
+
designId: string;
|
|
218
|
+
filename: string;
|
|
219
|
+
content: string;
|
|
220
|
+
expectedVersionHash: string;
|
|
221
|
+
}): Promise<string> {
|
|
222
|
+
await assertAccess("design", file.designId, "editor");
|
|
223
|
+
|
|
224
|
+
agentEnterDocument(file.id);
|
|
225
|
+
try {
|
|
226
|
+
const workspaceFile: SourceWorkspaceFile = {
|
|
227
|
+
id: file.id,
|
|
228
|
+
designId: file.designId,
|
|
229
|
+
filename: file.filename,
|
|
230
|
+
fileType: "html",
|
|
231
|
+
content: file.content,
|
|
232
|
+
createdAt: null,
|
|
233
|
+
updatedAt: null,
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
const result = await writeInlineSourceFile({
|
|
237
|
+
designId: file.designId,
|
|
238
|
+
file: workspaceFile,
|
|
239
|
+
content: file.content,
|
|
240
|
+
expectedVersionHash: file.expectedVersionHash,
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
return result.updatedAt;
|
|
244
|
+
} finally {
|
|
245
|
+
agentLeaveDocument(file.id);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// ─── Action ───────────────────────────────────────────────────────────────────
|
|
250
|
+
|
|
251
|
+
export default defineAction({
|
|
252
|
+
description:
|
|
253
|
+
"Swap a component instance for a different component from elsewhere in " +
|
|
254
|
+
"the design (Figma's Swap instance). Replaces the selected instance's " +
|
|
255
|
+
"markup with a copy of another existing instance of targetComponentName, " +
|
|
256
|
+
"carrying over data-agent-native-prop-* overrides whose prop name exists " +
|
|
257
|
+
"on both components. x-data (Alpine state) is not merged — the swapped-in " +
|
|
258
|
+
"instance keeps the target component's own x-data. Requires at least one " +
|
|
259
|
+
"existing instance of targetComponentName somewhere in the design. " +
|
|
260
|
+
"Inline/Alpine designs only.",
|
|
261
|
+
schema: z.object({
|
|
262
|
+
designId: z.string().describe("Design project ID"),
|
|
263
|
+
nodeId: z
|
|
264
|
+
.string()
|
|
265
|
+
.describe("data-agent-native-node-id of the component instance to swap"),
|
|
266
|
+
fileId: z
|
|
267
|
+
.string()
|
|
268
|
+
.optional()
|
|
269
|
+
.describe(
|
|
270
|
+
"Design file id the instance currently lives in; defaults to index.html",
|
|
271
|
+
),
|
|
272
|
+
targetComponentName: z
|
|
273
|
+
.string()
|
|
274
|
+
.min(1)
|
|
275
|
+
.describe(
|
|
276
|
+
"Name of the component to swap in, from list-design-components",
|
|
277
|
+
),
|
|
278
|
+
source: z
|
|
279
|
+
.object({
|
|
280
|
+
currentContent: z
|
|
281
|
+
.string()
|
|
282
|
+
.optional()
|
|
283
|
+
.describe("Latest editor HTML snapshot, when available."),
|
|
284
|
+
revision: z
|
|
285
|
+
.string()
|
|
286
|
+
.optional()
|
|
287
|
+
.describe(
|
|
288
|
+
"design_files.updatedAt value the currentContent is based on.",
|
|
289
|
+
),
|
|
290
|
+
})
|
|
291
|
+
.optional(),
|
|
292
|
+
}),
|
|
293
|
+
run: async ({ designId, nodeId, fileId, targetComponentName, source }) => {
|
|
294
|
+
const access = await resolveAccess("design", designId);
|
|
295
|
+
if (!access) throw new Error("Design not found");
|
|
296
|
+
|
|
297
|
+
const rawData = (access.resource as { data?: unknown }).data;
|
|
298
|
+
const sourceType = designSourceTypeFromData(rawData);
|
|
299
|
+
|
|
300
|
+
if (sourceType !== "inline") {
|
|
301
|
+
return {
|
|
302
|
+
designId,
|
|
303
|
+
nodeId,
|
|
304
|
+
sourceType,
|
|
305
|
+
swapped: false,
|
|
306
|
+
ctaRequired: true,
|
|
307
|
+
ctaMessage:
|
|
308
|
+
"Swap instance requires a dedicated consented, version-guarded " +
|
|
309
|
+
"compiled-source transform for real-app sources. Not yet available.",
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
await assertAccess("design", designId, "editor");
|
|
314
|
+
const db = getDb();
|
|
315
|
+
|
|
316
|
+
const conditions = [
|
|
317
|
+
accessFilter(schema.designs, schema.designShares),
|
|
318
|
+
eq(schema.designFiles.designId, designId),
|
|
319
|
+
fileId
|
|
320
|
+
? eq(schema.designFiles.id, fileId)
|
|
321
|
+
: eq(schema.designFiles.filename, "index.html"),
|
|
322
|
+
];
|
|
323
|
+
|
|
324
|
+
const [file] = await db
|
|
325
|
+
.select({
|
|
326
|
+
id: schema.designFiles.id,
|
|
327
|
+
designId: schema.designFiles.designId,
|
|
328
|
+
filename: schema.designFiles.filename,
|
|
329
|
+
content: schema.designFiles.content,
|
|
330
|
+
updatedAt: schema.designFiles.updatedAt,
|
|
331
|
+
})
|
|
332
|
+
.from(schema.designFiles)
|
|
333
|
+
.innerJoin(
|
|
334
|
+
schema.designs,
|
|
335
|
+
eq(schema.designFiles.designId, schema.designs.id),
|
|
336
|
+
)
|
|
337
|
+
.where(and(...conditions))
|
|
338
|
+
.limit(1);
|
|
339
|
+
|
|
340
|
+
if (!file) throw new Error("Design HTML file not found.");
|
|
341
|
+
|
|
342
|
+
if (
|
|
343
|
+
source?.currentContent &&
|
|
344
|
+
source.revision &&
|
|
345
|
+
file.updatedAt &&
|
|
346
|
+
source.revision !== file.updatedAt
|
|
347
|
+
) {
|
|
348
|
+
return {
|
|
349
|
+
designId,
|
|
350
|
+
nodeId,
|
|
351
|
+
swapped: false,
|
|
352
|
+
conflict: true,
|
|
353
|
+
fileId: file.id,
|
|
354
|
+
filename: file.filename,
|
|
355
|
+
error:
|
|
356
|
+
"This file changed since this swap was prepared. Refresh the editor and try again.",
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
const html =
|
|
361
|
+
typeof source?.currentContent === "string"
|
|
362
|
+
? source.currentContent
|
|
363
|
+
: (file.content ?? "");
|
|
364
|
+
const baseVersionHash = sourceContentHash(html);
|
|
365
|
+
|
|
366
|
+
const codeLayerSource: CodeLayerSource = {
|
|
367
|
+
kind: "design-file",
|
|
368
|
+
designId: file.designId,
|
|
369
|
+
fileId: file.id,
|
|
370
|
+
filename: file.filename,
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
const projection = buildCodeLayerProjection(html, {
|
|
374
|
+
source: codeLayerSource,
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
const node = projection.nodes.find((n) =>
|
|
378
|
+
componentNodeIdMatches(n, nodeId),
|
|
379
|
+
);
|
|
380
|
+
if (!node) {
|
|
381
|
+
throw new Error(
|
|
382
|
+
`Node "${nodeId}" not found. Run get-code-layer-projection to list current ids.`,
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
const componentName = componentNameFor(node);
|
|
387
|
+
if (!componentName) {
|
|
388
|
+
throw new Error(
|
|
389
|
+
`Node "${nodeId}" is not a component root (no data-agent-native-component attribute) — nothing to swap.`,
|
|
390
|
+
);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
if (componentName === targetComponentName) {
|
|
394
|
+
return {
|
|
395
|
+
designId,
|
|
396
|
+
nodeId,
|
|
397
|
+
componentName,
|
|
398
|
+
swapped: false,
|
|
399
|
+
note: `Already an instance of "${targetComponentName}".`,
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
if (!node.source) {
|
|
404
|
+
throw new Error(
|
|
405
|
+
`Node "${nodeId}" has no resolvable source span; cannot swap.`,
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// ── Find a markup source for the target component ──────────────────────
|
|
410
|
+
const allFiles = await db
|
|
411
|
+
.select({
|
|
412
|
+
id: schema.designFiles.id,
|
|
413
|
+
designId: schema.designFiles.designId,
|
|
414
|
+
filename: schema.designFiles.filename,
|
|
415
|
+
content: schema.designFiles.content,
|
|
416
|
+
})
|
|
417
|
+
.from(schema.designFiles)
|
|
418
|
+
.innerJoin(
|
|
419
|
+
schema.designs,
|
|
420
|
+
eq(schema.designFiles.designId, schema.designs.id),
|
|
421
|
+
)
|
|
422
|
+
.where(
|
|
423
|
+
and(
|
|
424
|
+
accessFilter(schema.designs, schema.designShares),
|
|
425
|
+
eq(schema.designFiles.designId, designId),
|
|
426
|
+
eq(schema.designFiles.fileType, "html"),
|
|
427
|
+
),
|
|
428
|
+
)
|
|
429
|
+
.orderBy(schema.designFiles.createdAt);
|
|
430
|
+
|
|
431
|
+
let targetNode: CodeLayerNode | undefined;
|
|
432
|
+
let targetHtml = "";
|
|
433
|
+
|
|
434
|
+
for (const row of allFiles) {
|
|
435
|
+
const rowHtml = row.id === file.id ? html : (row.content ?? "");
|
|
436
|
+
if (!rowHtml) continue;
|
|
437
|
+
const rowProjection =
|
|
438
|
+
row.id === file.id
|
|
439
|
+
? projection
|
|
440
|
+
: buildCodeLayerProjection(rowHtml, {
|
|
441
|
+
source: {
|
|
442
|
+
kind: "design-file",
|
|
443
|
+
designId: row.designId,
|
|
444
|
+
fileId: row.id,
|
|
445
|
+
filename: row.filename,
|
|
446
|
+
},
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
const match = rowProjection.nodes.find((n) => {
|
|
450
|
+
if (row.id === file.id && n.id === node.id) return false;
|
|
451
|
+
return componentNameFor(n) === targetComponentName;
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
if (match) {
|
|
455
|
+
targetNode = match;
|
|
456
|
+
targetHtml = rowHtml;
|
|
457
|
+
break;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
if (!targetNode) {
|
|
462
|
+
throw new Error(
|
|
463
|
+
`No existing instance of component "${targetComponentName}" found ` +
|
|
464
|
+
"anywhere in this design. Swap needs at least one existing " +
|
|
465
|
+
"instance to copy markup from — run list-design-components to see " +
|
|
466
|
+
"what's available.",
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
if (!targetNode.source) {
|
|
470
|
+
throw new Error(
|
|
471
|
+
`The matched "${targetComponentName}" instance has no resolvable source span; cannot swap.`,
|
|
472
|
+
);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
const targetMarkup = targetHtml.slice(
|
|
476
|
+
targetNode.source.start,
|
|
477
|
+
targetNode.source.end,
|
|
478
|
+
);
|
|
479
|
+
|
|
480
|
+
const currentProps = extractProps(node);
|
|
481
|
+
const targetDefaultProps = extractProps(targetNode);
|
|
482
|
+
|
|
483
|
+
const {
|
|
484
|
+
markup: mergedMarkup,
|
|
485
|
+
overriddenProps,
|
|
486
|
+
droppedProps,
|
|
487
|
+
defaultedProps,
|
|
488
|
+
} = mergeComponentSwapOverrides(
|
|
489
|
+
targetMarkup,
|
|
490
|
+
currentProps,
|
|
491
|
+
targetDefaultProps,
|
|
492
|
+
nodeId,
|
|
493
|
+
);
|
|
494
|
+
|
|
495
|
+
const patchedContent =
|
|
496
|
+
html.slice(0, node.source.start) +
|
|
497
|
+
mergedMarkup +
|
|
498
|
+
html.slice(node.source.end);
|
|
499
|
+
|
|
500
|
+
if (patchedContent === html) {
|
|
501
|
+
return {
|
|
502
|
+
designId,
|
|
503
|
+
nodeId,
|
|
504
|
+
componentName,
|
|
505
|
+
swapped: false,
|
|
506
|
+
note: "Swap produced no change.",
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
const updatedAt = await persistEdit({
|
|
511
|
+
id: file.id,
|
|
512
|
+
designId: file.designId,
|
|
513
|
+
filename: file.filename,
|
|
514
|
+
content: patchedContent,
|
|
515
|
+
expectedVersionHash: baseVersionHash,
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
agentUpdateSelection(file.id, {
|
|
519
|
+
selection: agentSelectionDescriptor(
|
|
520
|
+
{ nodeId, selector: node.selector },
|
|
521
|
+
"Swapping instance",
|
|
522
|
+
),
|
|
523
|
+
nodeId,
|
|
524
|
+
editingFile: file.filename,
|
|
525
|
+
designId: file.designId,
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
return {
|
|
529
|
+
designId,
|
|
530
|
+
nodeId,
|
|
531
|
+
fromComponent: componentName,
|
|
532
|
+
toComponent: targetComponentName,
|
|
533
|
+
swapped: true,
|
|
534
|
+
overriddenProps,
|
|
535
|
+
droppedProps,
|
|
536
|
+
defaultedProps,
|
|
537
|
+
fileId: file.id,
|
|
538
|
+
filename: file.filename,
|
|
539
|
+
updatedAt,
|
|
540
|
+
content: patchedContent,
|
|
541
|
+
note: `Swapped "${componentName}" for "${targetComponentName}".`,
|
|
542
|
+
};
|
|
543
|
+
},
|
|
544
|
+
});
|
|
@@ -125,75 +125,50 @@ function labelForWidth(widthPx: number): string {
|
|
|
125
125
|
return `desktop-${widthPx}`;
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
-
/**
|
|
129
|
-
|
|
128
|
+
/**
|
|
129
|
+
* Resolve the viewport list from the optional `widths` input, defaulting to
|
|
130
|
+
* desktop + mobile. `heights`, when provided, is matched index-for-index
|
|
131
|
+
* against `widths` so a caller that already knows the exact content height
|
|
132
|
+
* (e.g. the annotate-to-agent draw pipeline compositing over a specific
|
|
133
|
+
* on-screen rect) gets a screenshot with the same aspect ratio instead of the
|
|
134
|
+
* device-heuristic default — annotation coordinates are recorded in that
|
|
135
|
+
* exact rect's pixel space, so a mismatched aspect ratio would misalign the
|
|
136
|
+
* composited drawing against the screenshot content. A missing/undefined
|
|
137
|
+
* entry at a given index falls back to `heightForWidth` unchanged, so
|
|
138
|
+
* existing callers that only pass `widths` are unaffected.
|
|
139
|
+
*/
|
|
140
|
+
export function resolveViewports(
|
|
141
|
+
widths?: number[],
|
|
142
|
+
heights?: number[],
|
|
143
|
+
): ScreenshotViewport[] {
|
|
130
144
|
if (!widths || widths.length === 0) return DEFAULT_VIEWPORTS;
|
|
131
|
-
return widths.map((widthPx) => ({
|
|
145
|
+
return widths.map((widthPx, index) => ({
|
|
132
146
|
label: labelForWidth(widthPx),
|
|
133
147
|
widthPx,
|
|
134
|
-
heightPx: heightForWidth(widthPx),
|
|
148
|
+
heightPx: heights?.[index] || heightForWidth(widthPx),
|
|
135
149
|
}));
|
|
136
150
|
}
|
|
137
151
|
|
|
138
152
|
// ---------------------------------------------------------------------------
|
|
139
153
|
// Playwright loading (mirrors packages/core/src/cli/recap.ts's runShot: a
|
|
140
154
|
// dynamic import + system-Chrome fallback, so a missing browser binary is a
|
|
141
|
-
// clean, catchable failure rather than an unhandled module-resolution crash)
|
|
155
|
+
// clean, catchable failure rather than an unhandled module-resolution crash).
|
|
156
|
+
// The actual bootstrap lives in `playwright-runtime.ts` so other server-side
|
|
157
|
+
// Chromium consumers (e.g. the Figma SVG export's scene extractor in
|
|
158
|
+
// `design-to-figma-svg.ts`) share it instead of duplicating it; re-exported
|
|
159
|
+
// here for backward compatibility with this file's existing imports/spec.
|
|
142
160
|
// ---------------------------------------------------------------------------
|
|
143
161
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
return (await import(specifier)) as unknown as PlaywrightModule;
|
|
155
|
-
} catch {
|
|
156
|
-
// "@playwright/test" is a direct devDependency of this template (used by
|
|
157
|
-
// its own e2e suite) and re-exports the same chromium/Browser API.
|
|
158
|
-
return (await import("@playwright/test")) as unknown as PlaywrightModule;
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
const SYSTEM_CHROME_EXECUTABLES = [
|
|
163
|
-
"/usr/bin/google-chrome-stable",
|
|
164
|
-
"/usr/bin/google-chrome",
|
|
165
|
-
"/usr/bin/chromium-browser",
|
|
166
|
-
"/usr/bin/chromium",
|
|
167
|
-
];
|
|
168
|
-
|
|
169
|
-
/** Exported for tests — pure classifier for "no Chromium binary available" errors. */
|
|
170
|
-
export function isMissingBrowserError(err: unknown): boolean {
|
|
171
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
172
|
-
return /Executable doesn't exist|playwright install|browser.*not found|chromium.*not found/i.test(
|
|
173
|
-
message,
|
|
174
|
-
);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
async function launchChromium(
|
|
178
|
-
chromium: import("@playwright/test").BrowserType,
|
|
179
|
-
): Promise<import("@playwright/test").Browser> {
|
|
180
|
-
const launchOptions = { args: ["--no-sandbox"] };
|
|
181
|
-
try {
|
|
182
|
-
return await chromium.launch(launchOptions);
|
|
183
|
-
} catch (err) {
|
|
184
|
-
if (!isMissingBrowserError(err)) throw err;
|
|
185
|
-
const { existsSync } = await import("node:fs");
|
|
186
|
-
for (const executablePath of SYSTEM_CHROME_EXECUTABLES) {
|
|
187
|
-
if (!existsSync(executablePath)) continue;
|
|
188
|
-
try {
|
|
189
|
-
return await chromium.launch({ ...launchOptions, executablePath });
|
|
190
|
-
} catch {
|
|
191
|
-
// Try the next candidate; the original error is rethrown below.
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
throw err;
|
|
195
|
-
}
|
|
196
|
-
}
|
|
162
|
+
export {
|
|
163
|
+
importPlaywright,
|
|
164
|
+
isMissingBrowserError,
|
|
165
|
+
launchChromium,
|
|
166
|
+
} from "../server/lib/playwright-runtime.js";
|
|
167
|
+
import {
|
|
168
|
+
importPlaywright,
|
|
169
|
+
launchChromium,
|
|
170
|
+
type PlaywrightModule,
|
|
171
|
+
} from "../server/lib/playwright-runtime.js";
|
|
197
172
|
|
|
198
173
|
/** Human-readable, model-actionable message for the "no Chromium available" case. */
|
|
199
174
|
export function chromiumUnavailableReason(err: unknown): string {
|
|
@@ -510,10 +485,20 @@ export default defineAction({
|
|
|
510
485
|
"Viewport widths in px to render. Defaults to [1280, 375] (desktop + mobile). " +
|
|
511
486
|
"Height is derived per width using standard device aspect ratios.",
|
|
512
487
|
),
|
|
488
|
+
heights: z
|
|
489
|
+
.array(z.number().int().min(200).max(4096))
|
|
490
|
+
.optional()
|
|
491
|
+
.describe(
|
|
492
|
+
"Exact viewport heights in px, matched index-for-index against `widths`. " +
|
|
493
|
+
"Use when the caller needs the screenshot's aspect ratio to match a " +
|
|
494
|
+
"known on-screen rect exactly (e.g. compositing an overlay on top) " +
|
|
495
|
+
"instead of the device-heuristic default. Omit an index to fall back " +
|
|
496
|
+
"to the standard derived height for that width.",
|
|
497
|
+
),
|
|
513
498
|
}),
|
|
514
499
|
readOnly: true,
|
|
515
500
|
http: { method: "POST" },
|
|
516
|
-
run: async ({ designId, fileId, filename, widths }, ctx) => {
|
|
501
|
+
run: async ({ designId, fileId, filename, widths, heights }, ctx) => {
|
|
517
502
|
if (!designId && !fileId) {
|
|
518
503
|
throw new Error("designId or fileId is required.");
|
|
519
504
|
}
|
|
@@ -569,7 +554,7 @@ export default defineAction({
|
|
|
569
554
|
return { ok: false, reason: chromiumUnavailableReason(err) };
|
|
570
555
|
}
|
|
571
556
|
|
|
572
|
-
const viewports = resolveViewports(widths);
|
|
557
|
+
const viewports = resolveViewports(widths, heights);
|
|
573
558
|
let browser: import("@playwright/test").Browser | undefined;
|
|
574
559
|
try {
|
|
575
560
|
browser = await launchChromium(playwright.chromium);
|