@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
|
@@ -38,7 +38,8 @@ import { z } from "zod";
|
|
|
38
38
|
|
|
39
39
|
import { getDb, schema } from "../server/db/index.js";
|
|
40
40
|
import {
|
|
41
|
-
|
|
41
|
+
prepareInlineSourceEdit,
|
|
42
|
+
SourceWorkspaceEditConflictError,
|
|
42
43
|
writeInlineSourceFile,
|
|
43
44
|
type SourceWorkspaceFile,
|
|
44
45
|
} from "../server/source-workspace.js";
|
|
@@ -58,7 +59,6 @@ import {
|
|
|
58
59
|
type ShaderPresetName,
|
|
59
60
|
validateDescriptor,
|
|
60
61
|
} from "../shared/shader-presets.js";
|
|
61
|
-
import { sourceContentHash } from "../shared/source-workspace.js";
|
|
62
62
|
|
|
63
63
|
// ─── Schema ──────────────────────────────────────────────────────────────────
|
|
64
64
|
|
|
@@ -163,14 +163,10 @@ interface ResolvedDesignFile {
|
|
|
163
163
|
* editor access. Mirrors `resolveEditableDesignFile` in apply-visual-edit.ts so
|
|
164
164
|
* the shader-fill write goes through the exact same ownership gate.
|
|
165
165
|
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
* base instead (same precedence as apply-motion-edit.ts / apply-component-
|
|
171
|
-
* prop-edit.ts), and its versionHash is computed directly from that exact
|
|
172
|
-
* string via `sourceContentHash` — not from a fresh live re-read, which would
|
|
173
|
-
* prove nothing about whether this specific base is still current.
|
|
166
|
+
* `prepareInlineSourceEdit` keeps the caller's working copy (the transform
|
|
167
|
+
* base) separate from the live hash it is allowed to replace (the write CAS).
|
|
168
|
+
* This preserves unsaved local edits based on the matching SQL revision while
|
|
169
|
+
* still rejecting a third, concurrently-edited live value.
|
|
174
170
|
*/
|
|
175
171
|
async function resolveEditableDesignFile(source: {
|
|
176
172
|
designId?: string;
|
|
@@ -241,36 +237,27 @@ async function resolveEditableDesignFile(source: {
|
|
|
241
237
|
"source.revision is required when source.currentContent is provided.",
|
|
242
238
|
);
|
|
243
239
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
filename: file.filename,
|
|
266
|
-
fileType: file.fileType,
|
|
267
|
-
content: file.content,
|
|
268
|
-
createdAt: null,
|
|
269
|
-
updatedAt: file.updatedAt,
|
|
270
|
-
};
|
|
271
|
-
const live = await readLiveSourceFile(workspaceFile);
|
|
272
|
-
content = live.content;
|
|
273
|
-
versionHash = live.versionHash;
|
|
240
|
+
const workspaceFile: SourceWorkspaceFile = {
|
|
241
|
+
id: file.id,
|
|
242
|
+
designId: file.designId,
|
|
243
|
+
filename: file.filename,
|
|
244
|
+
fileType: file.fileType,
|
|
245
|
+
content: file.content,
|
|
246
|
+
createdAt: null,
|
|
247
|
+
updatedAt: file.updatedAt,
|
|
248
|
+
};
|
|
249
|
+
let prepared: Awaited<ReturnType<typeof prepareInlineSourceEdit>>;
|
|
250
|
+
try {
|
|
251
|
+
prepared = await prepareInlineSourceEdit({
|
|
252
|
+
file: workspaceFile,
|
|
253
|
+
currentContent: source.currentContent,
|
|
254
|
+
revision: source.revision,
|
|
255
|
+
});
|
|
256
|
+
} catch (error) {
|
|
257
|
+
if (error instanceof SourceWorkspaceEditConflictError) {
|
|
258
|
+
throw new ShaderFillRevisionConflictError();
|
|
259
|
+
}
|
|
260
|
+
throw error;
|
|
274
261
|
}
|
|
275
262
|
|
|
276
263
|
return {
|
|
@@ -278,8 +265,8 @@ async function resolveEditableDesignFile(source: {
|
|
|
278
265
|
designId: file.designId,
|
|
279
266
|
filename: file.filename,
|
|
280
267
|
fileType: file.fileType,
|
|
281
|
-
content,
|
|
282
|
-
versionHash,
|
|
268
|
+
content: prepared.content,
|
|
269
|
+
versionHash: prepared.expectedVersionHash,
|
|
283
270
|
codeLayerSource: {
|
|
284
271
|
kind: "design-file",
|
|
285
272
|
designId: file.designId,
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* detach-component-instance — Figma's "Detach instance" (⌥⌘B).
|
|
3
|
+
*
|
|
4
|
+
* There is no separate component "definition"/template markup anywhere in
|
|
5
|
+
* this codebase (see `shared/component-model.ts`): a component instance is
|
|
6
|
+
* just an ordinary element carrying a `data-agent-native-component="Name"`
|
|
7
|
+
* annotation (plus optional `data-agent-native-prop-*` overrides), and every
|
|
8
|
+
* instance of the same name is an independently-duplicated copy of HTML —
|
|
9
|
+
* `component_index` only stores metadata (props/variants/runtime selectors),
|
|
10
|
+
* never markup. Detaching an instance therefore doesn't need to "inline a
|
|
11
|
+
* template" the way a real component-instantiation system would: the node's
|
|
12
|
+
* current rendered markup already IS the fully expanded content. Detach
|
|
13
|
+
* severs the *component-instance linkage* by stripping the annotation
|
|
14
|
+
* attributes, so `is-component-instance` checks (`isComponentInstance`,
|
|
15
|
+
* `index-components`, `get-component-details`) stop recognizing this node —
|
|
16
|
+
* exactly Figma's "instance becomes a plain layer" semantics — while leaving
|
|
17
|
+
* position, size, layout, classes, text, and Alpine (`x-data`) behavior
|
|
18
|
+
* completely untouched.
|
|
19
|
+
*
|
|
20
|
+
* Persists through the same deterministic HTML-patch + collab seam as
|
|
21
|
+
* `apply-component-prop-edit` / `apply-visual-edit` (`writeInlineSourceFile`
|
|
22
|
+
* with an `expectedVersionHash` CAS guard), so it participates in the
|
|
23
|
+
* editor's undo/collab machinery exactly like every other HTML-mutating
|
|
24
|
+
* action — there is no separate undo/history log in this codebase to hook
|
|
25
|
+
* into (see those actions' docs).
|
|
26
|
+
*
|
|
27
|
+
* Inline/Alpine designs only; real-app sources fail closed (same posture as
|
|
28
|
+
* `apply-component-prop-edit`).
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
import { defineAction } from "@agent-native/core/action";
|
|
32
|
+
import {
|
|
33
|
+
agentEnterDocument,
|
|
34
|
+
agentLeaveDocument,
|
|
35
|
+
agentUpdateSelection,
|
|
36
|
+
} from "@agent-native/core/collab";
|
|
37
|
+
import {
|
|
38
|
+
accessFilter,
|
|
39
|
+
assertAccess,
|
|
40
|
+
resolveAccess,
|
|
41
|
+
} from "@agent-native/core/sharing";
|
|
42
|
+
import { and, eq } from "drizzle-orm";
|
|
43
|
+
import { z } from "zod";
|
|
44
|
+
|
|
45
|
+
import { getDb, schema } from "../server/db/index.js";
|
|
46
|
+
import "../server/db/index.js"; // ensure registerShareableResource runs
|
|
47
|
+
import {
|
|
48
|
+
writeInlineSourceFile,
|
|
49
|
+
type SourceWorkspaceFile,
|
|
50
|
+
} from "../server/source-workspace.js";
|
|
51
|
+
import { buildCodeLayerProjection } from "../shared/code-layer.js";
|
|
52
|
+
import type { CodeLayerSource } from "../shared/code-layer.js";
|
|
53
|
+
import { agentSelectionDescriptor } from "../shared/collab-selection.js";
|
|
54
|
+
import {
|
|
55
|
+
componentNameFor,
|
|
56
|
+
componentNodeIdMatches,
|
|
57
|
+
COMPONENT_NAME_ATTR,
|
|
58
|
+
COMPONENT_PROP_PREFIX,
|
|
59
|
+
} from "../shared/component-model.js";
|
|
60
|
+
import { designSourceTypeFromData } from "../shared/source-mode.js";
|
|
61
|
+
import { sourceContentHash } from "../shared/source-workspace.js";
|
|
62
|
+
|
|
63
|
+
// ─── Pure transform ────────────────────────────────────────────────────────
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Strip the `data-agent-native-component` annotation and every
|
|
67
|
+
* `data-agent-native-prop-*` override attribute from a node's opening tag,
|
|
68
|
+
* using its source span. Pure — no DB / IO — so it can be unit tested
|
|
69
|
+
* directly.
|
|
70
|
+
*
|
|
71
|
+
* Leaves everything else (classes, style, text, `x-data`, `id`,
|
|
72
|
+
* `data-agent-native-node-id`, …) untouched: the node keeps its current
|
|
73
|
+
* rendered appearance and behavior, it simply stops being recognized as a
|
|
74
|
+
* component instance.
|
|
75
|
+
*/
|
|
76
|
+
export function stripComponentAnnotations(
|
|
77
|
+
html: string,
|
|
78
|
+
source: { openStart: number; openEnd: number } | null | undefined,
|
|
79
|
+
): { content: string; changed: boolean; removedAttributes: string[] } {
|
|
80
|
+
if (!source) {
|
|
81
|
+
return { content: html, changed: false, removedAttributes: [] };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const openTag = html.slice(source.openStart, source.openEnd);
|
|
85
|
+
const removed: string[] = [];
|
|
86
|
+
let newOpenTag = openTag;
|
|
87
|
+
|
|
88
|
+
const componentAttrRe = new RegExp(
|
|
89
|
+
`\\s+${COMPONENT_NAME_ATTR}\\s*=\\s*(?:"[^"]*"|'[^']*'|[^\\s>"']+)`,
|
|
90
|
+
"i",
|
|
91
|
+
);
|
|
92
|
+
if (componentAttrRe.test(newOpenTag)) {
|
|
93
|
+
removed.push(COMPONENT_NAME_ATTR);
|
|
94
|
+
newOpenTag = newOpenTag.replace(componentAttrRe, "");
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const propAttrRe = new RegExp(
|
|
98
|
+
`\\s+${COMPONENT_PROP_PREFIX}[a-z0-9-]+\\s*=\\s*(?:"[^"]*"|'[^']*'|[^\\s>"']+)`,
|
|
99
|
+
"gi",
|
|
100
|
+
);
|
|
101
|
+
newOpenTag = newOpenTag.replace(propAttrRe, (match) => {
|
|
102
|
+
removed.push(match.trim().split("=")[0] ?? match.trim());
|
|
103
|
+
return "";
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
if (newOpenTag === openTag) {
|
|
107
|
+
return { content: html, changed: false, removedAttributes: [] };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
content:
|
|
112
|
+
html.slice(0, source.openStart) + newOpenTag + html.slice(source.openEnd),
|
|
113
|
+
changed: true,
|
|
114
|
+
removedAttributes: removed,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ─── Persistence ────────────────────────────────────────────────────────────
|
|
119
|
+
|
|
120
|
+
async function persistEdit(file: {
|
|
121
|
+
id: string;
|
|
122
|
+
designId: string;
|
|
123
|
+
filename: string;
|
|
124
|
+
content: string;
|
|
125
|
+
expectedVersionHash: string;
|
|
126
|
+
}): Promise<string> {
|
|
127
|
+
await assertAccess("design", file.designId, "editor");
|
|
128
|
+
|
|
129
|
+
agentEnterDocument(file.id);
|
|
130
|
+
try {
|
|
131
|
+
const workspaceFile: SourceWorkspaceFile = {
|
|
132
|
+
id: file.id,
|
|
133
|
+
designId: file.designId,
|
|
134
|
+
filename: file.filename,
|
|
135
|
+
fileType: "html",
|
|
136
|
+
content: file.content,
|
|
137
|
+
createdAt: null,
|
|
138
|
+
updatedAt: null,
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const result = await writeInlineSourceFile({
|
|
142
|
+
designId: file.designId,
|
|
143
|
+
file: workspaceFile,
|
|
144
|
+
content: file.content,
|
|
145
|
+
expectedVersionHash: file.expectedVersionHash,
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
return result.updatedAt;
|
|
149
|
+
} finally {
|
|
150
|
+
agentLeaveDocument(file.id);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// ─── Action ───────────────────────────────────────────────────────────────────
|
|
155
|
+
|
|
156
|
+
export default defineAction({
|
|
157
|
+
description:
|
|
158
|
+
"Detach a component instance (Figma's ⌥⌘B): strip the " +
|
|
159
|
+
"data-agent-native-component annotation and its data-agent-native-prop-* " +
|
|
160
|
+
"overrides from the selected instance root so it becomes a plain, " +
|
|
161
|
+
"unlinked element. Position, size, classes, text, and behavior are " +
|
|
162
|
+
"unchanged — only the component-instance linkage is removed. " +
|
|
163
|
+
"Inline/Alpine designs only.",
|
|
164
|
+
schema: z.object({
|
|
165
|
+
designId: z.string().describe("Design project ID"),
|
|
166
|
+
nodeId: z
|
|
167
|
+
.string()
|
|
168
|
+
.describe("data-agent-native-node-id of the component instance root"),
|
|
169
|
+
fileId: z
|
|
170
|
+
.string()
|
|
171
|
+
.optional()
|
|
172
|
+
.describe("Design file id; defaults to index.html"),
|
|
173
|
+
source: z
|
|
174
|
+
.object({
|
|
175
|
+
currentContent: z
|
|
176
|
+
.string()
|
|
177
|
+
.optional()
|
|
178
|
+
.describe("Latest editor HTML snapshot, when available."),
|
|
179
|
+
revision: z
|
|
180
|
+
.string()
|
|
181
|
+
.optional()
|
|
182
|
+
.describe(
|
|
183
|
+
"design_files.updatedAt value the currentContent is based on.",
|
|
184
|
+
),
|
|
185
|
+
})
|
|
186
|
+
.optional(),
|
|
187
|
+
}),
|
|
188
|
+
run: async ({ designId, nodeId, fileId, source }) => {
|
|
189
|
+
const access = await resolveAccess("design", designId);
|
|
190
|
+
if (!access) throw new Error("Design not found");
|
|
191
|
+
|
|
192
|
+
const rawData = (access.resource as { data?: unknown }).data;
|
|
193
|
+
const sourceType = designSourceTypeFromData(rawData);
|
|
194
|
+
|
|
195
|
+
if (sourceType !== "inline") {
|
|
196
|
+
return {
|
|
197
|
+
designId,
|
|
198
|
+
nodeId,
|
|
199
|
+
sourceType,
|
|
200
|
+
detached: false,
|
|
201
|
+
ctaRequired: true,
|
|
202
|
+
ctaMessage:
|
|
203
|
+
"Detach instance requires a dedicated consented, version-guarded " +
|
|
204
|
+
"compiled-source transform for real-app sources. Not yet available.",
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
await assertAccess("design", designId, "editor");
|
|
209
|
+
const db = getDb();
|
|
210
|
+
|
|
211
|
+
const conditions = [
|
|
212
|
+
accessFilter(schema.designs, schema.designShares),
|
|
213
|
+
eq(schema.designFiles.designId, designId),
|
|
214
|
+
fileId
|
|
215
|
+
? eq(schema.designFiles.id, fileId)
|
|
216
|
+
: eq(schema.designFiles.filename, "index.html"),
|
|
217
|
+
];
|
|
218
|
+
|
|
219
|
+
const [file] = await db
|
|
220
|
+
.select({
|
|
221
|
+
id: schema.designFiles.id,
|
|
222
|
+
designId: schema.designFiles.designId,
|
|
223
|
+
filename: schema.designFiles.filename,
|
|
224
|
+
content: schema.designFiles.content,
|
|
225
|
+
updatedAt: schema.designFiles.updatedAt,
|
|
226
|
+
})
|
|
227
|
+
.from(schema.designFiles)
|
|
228
|
+
.innerJoin(
|
|
229
|
+
schema.designs,
|
|
230
|
+
eq(schema.designFiles.designId, schema.designs.id),
|
|
231
|
+
)
|
|
232
|
+
.where(and(...conditions))
|
|
233
|
+
.limit(1);
|
|
234
|
+
|
|
235
|
+
if (!file) throw new Error("Design HTML file not found.");
|
|
236
|
+
|
|
237
|
+
if (
|
|
238
|
+
source?.currentContent &&
|
|
239
|
+
source.revision &&
|
|
240
|
+
file.updatedAt &&
|
|
241
|
+
source.revision !== file.updatedAt
|
|
242
|
+
) {
|
|
243
|
+
return {
|
|
244
|
+
designId,
|
|
245
|
+
nodeId,
|
|
246
|
+
detached: false,
|
|
247
|
+
conflict: true,
|
|
248
|
+
fileId: file.id,
|
|
249
|
+
filename: file.filename,
|
|
250
|
+
error:
|
|
251
|
+
"This file changed since this detach was prepared. Refresh the editor and try again.",
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const html =
|
|
256
|
+
typeof source?.currentContent === "string"
|
|
257
|
+
? source.currentContent
|
|
258
|
+
: (file.content ?? "");
|
|
259
|
+
const baseVersionHash = sourceContentHash(html);
|
|
260
|
+
|
|
261
|
+
const codeLayerSource: CodeLayerSource = {
|
|
262
|
+
kind: "design-file",
|
|
263
|
+
designId: file.designId,
|
|
264
|
+
fileId: file.id,
|
|
265
|
+
filename: file.filename,
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
const projection = buildCodeLayerProjection(html, {
|
|
269
|
+
source: codeLayerSource,
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
const node = projection.nodes.find((n) =>
|
|
273
|
+
componentNodeIdMatches(n, nodeId),
|
|
274
|
+
);
|
|
275
|
+
if (!node) {
|
|
276
|
+
throw new Error(
|
|
277
|
+
`Node "${nodeId}" not found. Run get-code-layer-projection to list current ids.`,
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const componentName = componentNameFor(node);
|
|
282
|
+
if (!componentName) {
|
|
283
|
+
throw new Error(
|
|
284
|
+
`Node "${nodeId}" is not a component root (no data-agent-native-component attribute) — nothing to detach.`,
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const {
|
|
289
|
+
content: patchedContent,
|
|
290
|
+
changed,
|
|
291
|
+
removedAttributes,
|
|
292
|
+
} = stripComponentAnnotations(html, node.source);
|
|
293
|
+
|
|
294
|
+
if (!changed) {
|
|
295
|
+
return {
|
|
296
|
+
designId,
|
|
297
|
+
nodeId,
|
|
298
|
+
componentName,
|
|
299
|
+
detached: false,
|
|
300
|
+
fileId: file.id,
|
|
301
|
+
filename: file.filename,
|
|
302
|
+
note: "No component annotation attributes found on this node's open tag.",
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const updatedAt = await persistEdit({
|
|
307
|
+
id: file.id,
|
|
308
|
+
designId: file.designId,
|
|
309
|
+
filename: file.filename,
|
|
310
|
+
content: patchedContent,
|
|
311
|
+
expectedVersionHash: baseVersionHash,
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
agentUpdateSelection(file.id, {
|
|
315
|
+
selection: agentSelectionDescriptor(
|
|
316
|
+
{ nodeId, selector: node.selector },
|
|
317
|
+
"Detaching instance",
|
|
318
|
+
),
|
|
319
|
+
nodeId,
|
|
320
|
+
editingFile: file.filename,
|
|
321
|
+
designId: file.designId,
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
return {
|
|
325
|
+
designId,
|
|
326
|
+
nodeId,
|
|
327
|
+
componentName,
|
|
328
|
+
detached: true,
|
|
329
|
+
removedAttributes,
|
|
330
|
+
fileId: file.id,
|
|
331
|
+
filename: file.filename,
|
|
332
|
+
updatedAt,
|
|
333
|
+
content: patchedContent,
|
|
334
|
+
note: `Detached from component "${componentName}". This node is now a plain element.`,
|
|
335
|
+
};
|
|
336
|
+
},
|
|
337
|
+
});
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* export-design-as-figma-svg — export a design screen (or a selected
|
|
3
|
+
* subtree) as a genuinely VECTOR SVG document that Figma imports as
|
|
4
|
+
* editable layers (rect/path/gradients/filters stay editable; `<text>` is
|
|
5
|
+
* vectorized to outline paths on import — see the returned report's
|
|
6
|
+
* `vectorizedTextCaveat`). This is a different artifact from `export-svg`,
|
|
7
|
+
* which wraps the standalone HTML in a `foreignObject` for the editor's own
|
|
8
|
+
* "Download SVG" parity — Figma cannot import that as vectors at all, it
|
|
9
|
+
* stays an opaque embedded HTML blob.
|
|
10
|
+
*
|
|
11
|
+
* Resolution mirrors `take-design-screenshot.ts`: a single HTML screen
|
|
12
|
+
* (`fileId`, or `designId` + `filename` defaulting to `index.html`), using
|
|
13
|
+
* live collab content when the screen is actively being edited. `nodeId`
|
|
14
|
+
* additionally scopes the export to one selected element's subtree via its
|
|
15
|
+
* `data-agent-native-node-id` — the same attribute the editor stamps on
|
|
16
|
+
* selectable layers.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { defineAction } from "@agent-native/core";
|
|
20
|
+
import { getText, hasCollabState } from "@agent-native/core/collab";
|
|
21
|
+
import { accessFilter } from "@agent-native/core/sharing";
|
|
22
|
+
import { and, eq } from "drizzle-orm";
|
|
23
|
+
import { z } from "zod";
|
|
24
|
+
|
|
25
|
+
import { getDb, schema } from "../server/db/index.js";
|
|
26
|
+
import { trySaveExportFile } from "../server/lib/design-export.js";
|
|
27
|
+
import {
|
|
28
|
+
renderDesignToFigmaSvg,
|
|
29
|
+
safeFigmaSvgFilename,
|
|
30
|
+
} from "../server/lib/design-to-figma-svg.js";
|
|
31
|
+
import { isMissingBrowserError } from "../server/lib/playwright-runtime.js";
|
|
32
|
+
import "../server/db/index.js"; // ensure registerShareableResource runs
|
|
33
|
+
|
|
34
|
+
async function liveContent(
|
|
35
|
+
fileId: string,
|
|
36
|
+
storedContent: string,
|
|
37
|
+
): Promise<string> {
|
|
38
|
+
try {
|
|
39
|
+
if (await hasCollabState(fileId)) {
|
|
40
|
+
const live = await getText(fileId, "content");
|
|
41
|
+
if (typeof live === "string") return live;
|
|
42
|
+
}
|
|
43
|
+
} catch {
|
|
44
|
+
// SQL content is the deterministic fallback.
|
|
45
|
+
}
|
|
46
|
+
return storedContent;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Model-actionable message when no headless Chromium binary is available. */
|
|
50
|
+
export function chromiumUnavailableReason(err: unknown): string {
|
|
51
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
52
|
+
return (
|
|
53
|
+
"A headless Chromium browser is not available in this environment, so the " +
|
|
54
|
+
"vector SVG export cannot run here (this is expected in hosted/serverless " +
|
|
55
|
+
"deploys, which do not bundle a Chromium binary). Fall back to `export-svg` " +
|
|
56
|
+
"(a foreignObject-wrapped HTML snapshot — not Figma-importable as vectors, " +
|
|
57
|
+
"but still a usable static preview) or `export-html`. " +
|
|
58
|
+
`(${detail})`
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export default defineAction({
|
|
63
|
+
description:
|
|
64
|
+
"Export a design screen (or a selected element's subtree via nodeId) as a " +
|
|
65
|
+
"genuinely vector SVG document — real <rect>/<path>/<text>/<image> markup " +
|
|
66
|
+
"with <linearGradient>/<radialGradient>/<filter> defs, which Figma imports " +
|
|
67
|
+
"as normal EDITABLE layers (unlike `export-svg`'s foreignObject wrapper, " +
|
|
68
|
+
"which Figma cannot import as vectors). Returns the SVG string plus an " +
|
|
69
|
+
"export report classifying each element as vectorized, approximated, " +
|
|
70
|
+
"rasterized, or omitted. Note: Figma converts all imported SVG <text> to " +
|
|
71
|
+
"outlined vector paths on paste/drag-import — geometry stays pixel-exact " +
|
|
72
|
+
"but the text is no longer live/editable type in Figma; see the report's " +
|
|
73
|
+
"`vectorizedTextCaveat`.",
|
|
74
|
+
schema: z.object({
|
|
75
|
+
designId: z
|
|
76
|
+
.string()
|
|
77
|
+
.optional()
|
|
78
|
+
.describe("Design project id. Required unless fileId is provided."),
|
|
79
|
+
fileId: z
|
|
80
|
+
.string()
|
|
81
|
+
.optional()
|
|
82
|
+
.describe(
|
|
83
|
+
"Specific design_files.id to export. Takes priority over designId/filename.",
|
|
84
|
+
),
|
|
85
|
+
filename: z
|
|
86
|
+
.string()
|
|
87
|
+
.optional()
|
|
88
|
+
.default("index.html")
|
|
89
|
+
.describe(
|
|
90
|
+
"Filename to export when fileId is not provided. Defaults to index.html.",
|
|
91
|
+
),
|
|
92
|
+
nodeId: z
|
|
93
|
+
.string()
|
|
94
|
+
.optional()
|
|
95
|
+
.describe(
|
|
96
|
+
"Scope the export to one element's subtree via its data-agent-native-node-id, " +
|
|
97
|
+
"instead of the whole screen.",
|
|
98
|
+
),
|
|
99
|
+
embedImages: z
|
|
100
|
+
.boolean()
|
|
101
|
+
.optional()
|
|
102
|
+
.default(true)
|
|
103
|
+
.describe(
|
|
104
|
+
"Fetch and inline http(s) image sources/background-images as data: URIs, so the " +
|
|
105
|
+
"SVG is self-contained for clipboard paste. Set false to keep absolute URLs.",
|
|
106
|
+
),
|
|
107
|
+
width: z.coerce
|
|
108
|
+
.number()
|
|
109
|
+
.int()
|
|
110
|
+
.positive()
|
|
111
|
+
.optional()
|
|
112
|
+
.describe(
|
|
113
|
+
"Render viewport width in px. Defaults to the screen's natural width or 1440.",
|
|
114
|
+
),
|
|
115
|
+
height: z.coerce
|
|
116
|
+
.number()
|
|
117
|
+
.int()
|
|
118
|
+
.positive()
|
|
119
|
+
.optional()
|
|
120
|
+
.describe(
|
|
121
|
+
"Render viewport height in px. Defaults to the screen's natural height or 1200.",
|
|
122
|
+
),
|
|
123
|
+
}),
|
|
124
|
+
readOnly: true,
|
|
125
|
+
http: { method: "POST" },
|
|
126
|
+
run: async ({
|
|
127
|
+
designId,
|
|
128
|
+
fileId,
|
|
129
|
+
filename,
|
|
130
|
+
nodeId,
|
|
131
|
+
embedImages,
|
|
132
|
+
width,
|
|
133
|
+
height,
|
|
134
|
+
}) => {
|
|
135
|
+
if (!designId && !fileId) {
|
|
136
|
+
throw new Error("designId or fileId is required.");
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const db = getDb();
|
|
140
|
+
const conditions = [
|
|
141
|
+
accessFilter(schema.designs, schema.designShares),
|
|
142
|
+
fileId
|
|
143
|
+
? eq(schema.designFiles.id, fileId)
|
|
144
|
+
: eq(schema.designFiles.designId, designId ?? ""),
|
|
145
|
+
];
|
|
146
|
+
if (!fileId) {
|
|
147
|
+
conditions.push(
|
|
148
|
+
eq(schema.designFiles.filename, filename ?? "index.html"),
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const [file] = await db
|
|
153
|
+
.select({
|
|
154
|
+
id: schema.designFiles.id,
|
|
155
|
+
designId: schema.designFiles.designId,
|
|
156
|
+
filename: schema.designFiles.filename,
|
|
157
|
+
fileType: schema.designFiles.fileType,
|
|
158
|
+
content: schema.designFiles.content,
|
|
159
|
+
})
|
|
160
|
+
.from(schema.designFiles)
|
|
161
|
+
.innerJoin(
|
|
162
|
+
schema.designs,
|
|
163
|
+
eq(schema.designFiles.designId, schema.designs.id),
|
|
164
|
+
)
|
|
165
|
+
.where(and(...conditions))
|
|
166
|
+
.limit(1);
|
|
167
|
+
|
|
168
|
+
if (!file) {
|
|
169
|
+
const err = new Error("Design file not found") as Error & {
|
|
170
|
+
statusCode: number;
|
|
171
|
+
};
|
|
172
|
+
err.statusCode = 404;
|
|
173
|
+
throw err;
|
|
174
|
+
}
|
|
175
|
+
if (file.fileType !== "html") {
|
|
176
|
+
throw new Error(
|
|
177
|
+
`export-design-as-figma-svg only supports HTML files (got "${file.fileType}").`,
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const [designRow] = await db
|
|
182
|
+
.select({ title: schema.designs.title })
|
|
183
|
+
.from(schema.designs)
|
|
184
|
+
.where(eq(schema.designs.id, file.designId))
|
|
185
|
+
.limit(1);
|
|
186
|
+
|
|
187
|
+
const html = await liveContent(file.id, file.content ?? "");
|
|
188
|
+
|
|
189
|
+
let result: Awaited<ReturnType<typeof renderDesignToFigmaSvg>>;
|
|
190
|
+
try {
|
|
191
|
+
result = await renderDesignToFigmaSvg({
|
|
192
|
+
html,
|
|
193
|
+
width: width ?? 1440,
|
|
194
|
+
height: height ?? 1200,
|
|
195
|
+
title: designRow?.title ?? file.filename,
|
|
196
|
+
rootSelector: nodeId ? `[data-agent-native-node-id="${nodeId}"]` : null,
|
|
197
|
+
embedImages: embedImages ?? true,
|
|
198
|
+
});
|
|
199
|
+
} catch (err) {
|
|
200
|
+
if (isMissingBrowserError(err)) {
|
|
201
|
+
return { ok: false, reason: chromiumUnavailableReason(err) };
|
|
202
|
+
}
|
|
203
|
+
throw err;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const filenameOut = safeFigmaSvgFilename(designRow?.title ?? file.filename);
|
|
207
|
+
const saveResult = await trySaveExportFile(filenameOut, result.svg);
|
|
208
|
+
|
|
209
|
+
return {
|
|
210
|
+
ok: true,
|
|
211
|
+
designId: file.designId,
|
|
212
|
+
fileId: file.id,
|
|
213
|
+
filename: filenameOut,
|
|
214
|
+
svg: result.svg,
|
|
215
|
+
report: result.report,
|
|
216
|
+
...saveResult,
|
|
217
|
+
};
|
|
218
|
+
},
|
|
219
|
+
});
|