@agent-native/core 0.92.10 → 0.92.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +23 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/production-agent.ts +181 -15
- package/corpus/core/src/agent/run-manager.ts +31 -4
- package/corpus/core/src/agent/run-store.ts +80 -0
- package/corpus/core/src/cli/design-connect.ts +433 -151
- package/corpus/core/src/cli/skills.ts +14 -0
- package/corpus/core/src/client/AssistantChat.tsx +25 -0
- package/corpus/core/src/client/MultiTabAssistantChat.tsx +40 -6
- package/corpus/core/src/client/agent-chat.ts +165 -1
- package/corpus/core/src/client/chat/index.ts +9 -1
- package/corpus/core/src/client/extensions/EmbeddedExtension.tsx +38 -2
- package/corpus/core/src/client/index.ts +5 -0
- package/corpus/core/src/client/rich-markdown-editor/useCollabReconcile.ts +18 -7
- package/corpus/core/src/collab/awareness-store.ts +23 -12
- package/corpus/core/src/collab/client.ts +16 -5
- package/corpus/core/src/collab/index.ts +4 -0
- package/corpus/core/src/collab/recent-edits.ts +65 -2
- package/corpus/core/src/index.browser.ts +1 -0
- package/corpus/core/src/server/agent-chat-plugin.ts +98 -11
- package/corpus/core/src/server/builder-design-systems.ts +30 -4
- package/corpus/templates/clips/.agents/skills/ai-video-tools/SKILL.md +1 -1
- package/corpus/templates/clips/.agents/skills/video-sharing/SKILL.md +12 -6
- package/corpus/templates/clips/AGENTS.md +4 -0
- package/corpus/templates/clips/actions/create-recording.ts +2 -0
- package/corpus/templates/clips/actions/lib/create-recording-schema.ts +5 -3
- package/corpus/templates/clips/actions/request-transcript.ts +102 -33
- package/corpus/templates/clips/app/components/library/library-grid.tsx +1 -0
- package/corpus/templates/clips/app/components/player/share-dialog.tsx +59 -19
- package/corpus/templates/clips/app/components/player/transcript-panel.tsx +48 -4
- package/corpus/templates/clips/app/components/player/video-player.tsx +17 -12
- package/corpus/templates/clips/app/i18n/ar-SA.ts +1 -0
- package/corpus/templates/clips/app/i18n/de-DE.ts +1 -0
- package/corpus/templates/clips/app/i18n/en-US.ts +1 -0
- package/corpus/templates/clips/app/i18n/es-ES.ts +1 -0
- package/corpus/templates/clips/app/i18n/fr-FR.ts +1 -0
- package/corpus/templates/clips/app/i18n/hi-IN.ts +1 -0
- package/corpus/templates/clips/app/i18n/ja-JP.ts +1 -0
- package/corpus/templates/clips/app/i18n/ko-KR.ts +1 -0
- package/corpus/templates/clips/app/i18n/pt-BR.ts +1 -0
- package/corpus/templates/clips/app/i18n/zh-CN.ts +1 -0
- package/corpus/templates/clips/app/i18n/zh-TW.ts +1 -0
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +70 -37
- package/corpus/templates/clips/app/routes/record.tsx +17 -4
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +2 -0
- package/corpus/templates/clips/changelog/2026-07-09-desktop-popover-sits-closer-to-the-menu-bar.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-09-desktop-recording-optimization-progress-stays-visible-until-the-clip-opens.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-09-fixed-large-video-file-uploads-failing-before-their-resumabl.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-09-fixed-new-video-uploads-defaulting-to-private.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-09-long-recordings-now-get-enough-time-to-download-their-media-.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-09-meeting-notes-no-longer-lowers-your-microphone-volume-for-ot.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-09-on-mobile-tapping-a-playing-clip-reveals-playback-controls-w.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-09-regenerate-any-clip-transcript-from-its-saved-recording-or-r.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-09-slack-previews-now-show-the-clip-length-alongside-its-descri.md +6 -0
- package/corpus/templates/clips/desktop/src/app.tsx +12 -2
- package/corpus/templates/clips/desktop/src/hooks/useMeetingTranscription.ts +8 -1
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +8 -5
- package/corpus/templates/clips/desktop/src/lib/transcription-engine.ts +5 -5
- package/corpus/templates/clips/desktop/src/lib/voice-dictation.ts +3 -0
- package/corpus/templates/clips/desktop/src/styles.css +6 -3
- package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +29 -11
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +1 -1
- package/corpus/templates/clips/desktop/src-tauri/src/system_audio.rs +103 -21
- package/corpus/templates/clips/desktop/src-tauri/src/whisper_speech.rs +117 -39
- package/corpus/templates/clips/server/lib/slack-unfurls.ts +20 -1
- package/corpus/templates/clips/server/lib/streaming-upload-mode.ts +10 -4
- package/corpus/templates/clips/shared/recording-core.ts +12 -0
- package/corpus/templates/content/AGENTS.md +7 -6
- package/corpus/templates/content/actions/_document-flush.ts +132 -17
- package/corpus/templates/content/actions/_notion-action-utils.ts +15 -0
- package/corpus/templates/content/actions/create-and-link-notion-page.ts +2 -0
- package/corpus/templates/content/actions/link-notion-page.ts +2 -0
- package/corpus/templates/content/actions/pull-document.ts +5 -7
- package/corpus/templates/content/actions/pull-notion-page.ts +2 -0
- package/corpus/templates/content/actions/push-notion-page.ts +10 -0
- package/corpus/templates/content/actions/resolve-notion-sync-conflict.ts +2 -0
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +88 -23
- package/corpus/templates/content/app/components/editor/VisualEditor.tsx +32 -2
- package/corpus/templates/content/app/hooks/use-notion.ts +6 -6
- package/corpus/templates/content/app/i18n/zh-TW.ts +1 -0
- package/corpus/templates/content/app/i18n-data.ts +18 -0
- package/corpus/templates/content/changelog/2026-07-09-notion-conflict-choices-and-version-restores-preserve-docume.md +6 -0
- package/corpus/templates/content/server/lib/notion-sync.ts +80 -64
- package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +29 -2
- package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +86 -0
- package/corpus/templates/design/.agents/skills/export-handoff/SKILL.md +57 -0
- package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +11 -0
- package/corpus/templates/design/AGENTS.md +45 -10
- package/corpus/templates/design/actions/add-localhost-screens.ts +66 -13
- package/corpus/templates/design/actions/apply-component-prop-edit.ts +45 -36
- package/corpus/templates/design/actions/apply-motion-edit.ts +25 -31
- package/corpus/templates/design/actions/apply-shader-fill.ts +29 -42
- package/corpus/templates/design/actions/detach-component-instance.ts +337 -0
- package/corpus/templates/design/actions/export-design-as-figma-svg.ts +219 -0
- package/corpus/templates/design/actions/generate-design.ts +79 -12
- package/corpus/templates/design/actions/generate-screens.ts +57 -12
- package/corpus/templates/design/actions/get-figma-styles.ts +115 -0
- package/corpus/templates/design/actions/go-to-main-component.ts +254 -0
- package/corpus/templates/design/actions/grant-localhost-write-consent.ts +4 -3
- package/corpus/templates/design/actions/import-design-source.ts +4 -31
- package/corpus/templates/design/actions/import-figma-clipboard.ts +134 -0
- package/corpus/templates/design/actions/import-figma-frame.ts +95 -0
- package/corpus/templates/design/actions/index-design-system-with-builder.ts +16 -1
- package/corpus/templates/design/actions/list-design-components.ts +104 -0
- package/corpus/templates/design/actions/list-figma-library-assets.ts +1 -24
- package/corpus/templates/design/actions/open-component-source.ts +2 -4
- package/corpus/templates/design/actions/present-design-variants.ts +412 -16
- package/corpus/templates/design/actions/preview-source-edit.ts +14 -1
- package/corpus/templates/design/actions/run-design-audit.ts +25 -3
- package/corpus/templates/design/actions/show-design-questions.ts +10 -0
- package/corpus/templates/design/actions/swap-component-instance.ts +544 -0
- package/corpus/templates/design/actions/take-design-screenshot.ts +45 -60
- package/corpus/templates/design/actions/write-local-file.ts +25 -1
- package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +152 -0
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +1077 -58
- package/corpus/templates/design/app/components/design/DesignEditorSkeleton.tsx +5 -5
- package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +73 -21
- package/corpus/templates/design/app/components/design/EditPanel.tsx +16 -11
- package/corpus/templates/design/app/components/design/KeyboardShortcutsPanel.tsx +364 -0
- package/corpus/templates/design/app/components/design/LayersPanel.tsx +180 -53
- package/corpus/templates/design/app/components/design/LocalhostWriteConsentDialog.tsx +0 -2
- package/corpus/templates/design/app/components/design/MotionDock.tsx +144 -21
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +1152 -167
- package/corpus/templates/design/app/components/design/QuestionFlow.tsx +27 -5
- package/corpus/templates/design/app/components/design/ReviewPanel.tsx +23 -2
- package/corpus/templates/design/app/components/design/TokensPanel.tsx +63 -49
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +1293 -54
- package/corpus/templates/design/app/components/design/bridge/embedded-wheel.bridge.ts +7 -0
- package/corpus/templates/design/app/components/design/bridge/hit-test.bridge.ts +6 -1
- package/corpus/templates/design/app/components/design/bridge/motion-preview.bridge.ts +23 -6
- package/corpus/templates/design/app/components/design/bridge/sample.bridge.ts +7 -0
- package/corpus/templates/design/app/components/design/code-workbench/CodeWorkbench.tsx +5 -0
- package/corpus/templates/design/app/components/design/code-workbench/editor/MonacoHost.tsx +14 -2
- package/corpus/templates/design/app/components/design/code-workbench/explorer/FileTree.tsx +37 -2
- package/corpus/templates/design/app/components/design/code-workbench/format/format-on-open-guard.ts +25 -0
- package/corpus/templates/design/app/components/design/code-workbench/format/format-on-open.ts +19 -1
- package/corpus/templates/design/app/components/design/code-workbench/model-registry.ts +43 -5
- package/corpus/templates/design/app/components/design/code-workbench/search/SearchView.tsx +27 -2
- package/corpus/templates/design/app/components/design/code-workbench/search/search-engine.ts +10 -1
- package/corpus/templates/design/app/components/design/code-workbench/store.tsx +19 -2
- package/corpus/templates/design/app/components/design/design-canvas/annotation-snapshot.ts +265 -0
- package/corpus/templates/design/app/components/design/design-canvas/annotation-submit.ts +16 -5
- package/corpus/templates/design/app/components/design/design-canvas/coordinate-transforms.ts +16 -2
- package/corpus/templates/design/app/components/design/design-canvas/embedded-frame.ts +7 -2
- package/corpus/templates/design/app/components/design/design-canvas/iframe-events.ts +3 -1
- package/corpus/templates/design/app/components/design/edit-panel/code-inspect-helpers.tsx +78 -8
- package/corpus/templates/design/app/components/design/edit-panel/component-section.tsx +451 -73
- package/corpus/templates/design/app/components/design/edit-panel/document-colors.ts +12 -4
- package/corpus/templates/design/app/components/design/edit-panel/effects-properties.tsx +398 -272
- package/corpus/templates/design/app/components/design/edit-panel/element-classification.ts +41 -7
- package/corpus/templates/design/app/components/design/edit-panel/element-identity.ts +16 -3
- package/corpus/templates/design/app/components/design/edit-panel/field-primitives.tsx +24 -4
- package/corpus/templates/design/app/components/design/edit-panel/fill-gradient-helpers.ts +222 -4
- package/corpus/templates/design/app/components/design/edit-panel/fill-properties.tsx +171 -78
- package/corpus/templates/design/app/components/design/edit-panel/inspector-controls.tsx +45 -5
- package/corpus/templates/design/app/components/design/edit-panel/layout-properties.tsx +299 -28
- package/corpus/templates/design/app/components/design/edit-panel/panel-primitives.tsx +120 -150
- package/corpus/templates/design/app/components/design/edit-panel/position-helpers.ts +53 -1
- package/corpus/templates/design/app/components/design/edit-panel/position-layout-properties.tsx +284 -117
- package/corpus/templates/design/app/components/design/edit-panel/selection-helpers.ts +81 -0
- package/corpus/templates/design/app/components/design/edit-panel/stroke-properties.tsx +19 -14
- package/corpus/templates/design/app/components/design/edit-panel/style-options.ts +7 -0
- package/corpus/templates/design/app/components/design/edit-panel/typography-helpers.ts +155 -0
- package/corpus/templates/design/app/components/design/edit-panel/typography-properties.tsx +268 -46
- package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +350 -31
- package/corpus/templates/design/app/components/design/inspector/ConstraintsWidget.tsx +13 -4
- package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +233 -23
- package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +72 -2
- package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +51 -11
- package/corpus/templates/design/app/components/design/inspector/ShaderFillsPanel.tsx +84 -11
- package/corpus/templates/design/app/components/design/inspector/index.ts +3 -0
- package/corpus/templates/design/app/components/design/keyboard-shortcuts.ts +552 -0
- package/corpus/templates/design/app/components/design/multi-screen/board-surface-html.ts +157 -0
- package/corpus/templates/design/app/components/design/multi-screen/frame-geometry.ts +53 -0
- package/corpus/templates/design/app/components/design/multi-screen/overview-layout.ts +184 -0
- package/corpus/templates/design/app/components/design/multi-screen/primitive-drop-target.ts +403 -26
- package/corpus/templates/design/app/components/design/multi-screen/types.ts +20 -1
- package/corpus/templates/design/app/components/design/types.ts +33 -0
- package/corpus/templates/design/app/components/editor/PromptDialog.tsx +59 -7
- package/corpus/templates/design/app/components/visual-editor/CanvasCommentPins.tsx +119 -17
- package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +275 -63
- package/corpus/templates/design/app/hooks/use-question-flow.ts +14 -3
- package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +67 -10
- package/corpus/templates/design/app/i18n/zh-TW.ts +91 -1
- package/corpus/templates/design/app/i18n-data.ts +1217 -10
- package/corpus/templates/design/app/i18n-keyboard-shortcuts.ts +577 -0
- package/corpus/templates/design/app/lib/agent-chat.ts +23 -0
- package/corpus/templates/design/app/lib/design-import.ts +24 -0
- package/corpus/templates/design/app/lib/figma-clipboard.ts +157 -0
- package/corpus/templates/design/app/lib/figma-svg-copy.ts +221 -0
- package/corpus/templates/design/app/pages/design-editor/code-layer-state.ts +143 -30
- package/corpus/templates/design/app/pages/design-editor/collab-sync.ts +15 -0
- package/corpus/templates/design/app/pages/design-editor/editor-session.ts +33 -0
- package/corpus/templates/design/app/pages/design-editor/editor-state.ts +38 -0
- package/corpus/templates/design/app/pages/design-editor/overview-annotation-context.ts +157 -0
- package/corpus/templates/design/app/pages/design-editor/overview-camera.ts +62 -6
- package/corpus/templates/design/app/pages/design-editor/pending-edits.ts +245 -0
- package/corpus/templates/design/app/pages/design-editor/react-semantic-handoff.ts +586 -0
- package/corpus/templates/design/app/pages/design-editor/screen-auto-layout.ts +169 -0
- package/corpus/templates/design/app/pages/design-editor/selection-state.ts +201 -1
- package/corpus/templates/design/changelog/2026-07-09-added-underline-strikethrough-and-text-case-uppercase-lowerc.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-annotate-now-draws-across-the-all-screens-canvas-without-swi.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-auto-layout-children-can-now-be-dragged-into-freeform-screen.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-board-shapes-now-stay-visible-and-nest-correctly-across-the-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-component-source-navigation-now-opens-reliably-from-the-insp.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-cross-screen-local-react-layer-moves-now-hand-off-exact-sour.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-cross-screen-moves-undo-and-redo-now-keep-both-screens-stabl.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-detach-a-component-instance-into-plain-editable-eleme.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-dragging-layers-into-auto-layout-now-respects-ignore-auto-la.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-drawing-annotations-sent-to-the-agent-now-include-a-rendered.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-editor-sidebars-now-use-figma-matched-default-widths-for-a-r.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-empty-frames-and-rectangles-now-expose-full-auto-layout-cont.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-export-any-design-screen-as-a-real-vector-svg-for-figma-with.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-grid-auto-layout-now-uses-true-responsive-rows-and-columns-w.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-hidden-and-locked-screens-now-behave-consistently-between-la.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-image-fills-now-preserve-valid-urls-and-layered-fit-settings.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-import-figma-frames-as-pixel-accurate-design-screens-via-a-s.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-inspect-code-now-hides-internal-styling-metadata-and-wraps-l.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-inspector-blur-and-shadow-effects-now-preserve-other-effects.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-jump-to-a-components-other-instances-from-the-inspecto.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-keyboard-shortcuts-are-now-discoverable-in-a-figma-style-bot.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-layer-selection-now-keeps-cmd-or-ctrl-toggles-and-shift-rang.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-layers-dropped-into-auto-layout-now-participate-correctly-in.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-local-live-edit-screens-now-enter-and-leave-full-view-withou.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-local-preview-urls-now-live-in-the-inspector-instead-of-taki.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-local-react-layers-can-now-be-hidden-and-locked-from-layers-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-local-react-layers-now-keep-exact-source-provenance-and-supp.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-local-react-visual-editing-now-loads-reliably-and-shows-live.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-paste-from-figma-cmd-v-now-imports-exact-editable-nodes-when.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-pressing-escape-after-selecting-a-screen-now-reveals-page-pr.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-right-click-overlapping-or-nested-objects-to-choose-the-exac.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-scale-edge-stretch-and-center-constraints-now-preserve-layer.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-screen-rows-can-now-be-reordered-in-layers-with-matching-can.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-screen-selection-stays-responsive-in-designs-with-hundreds-o.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-shift-a-now-enables-auto-layout-on-a-selected-screen-with-fl.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-swap-a-component-instance-for-another-from-the-libra.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-drawing-annotations-now-stay-available-to-retry-when-the.md +6 -0
- package/corpus/templates/design/e2e/global-setup.ts +3 -6
- package/corpus/templates/design/e2e/helpers.ts +27 -6
- package/corpus/templates/design/server/lib/design-to-figma-svg.ts +1412 -0
- package/corpus/templates/design/server/lib/figma-clipboard-match.ts +196 -0
- package/corpus/templates/design/server/lib/figma-node-import.ts +311 -0
- package/corpus/templates/design/server/lib/figma-node-to-html.ts +1334 -0
- package/corpus/templates/design/server/lib/figma-paste-fallback.ts +64 -0
- package/corpus/templates/design/server/lib/playwright-runtime.ts +78 -0
- package/corpus/templates/design/server/plugins/db.ts +41 -0
- package/corpus/templates/design/server/source-workspace.ts +64 -0
- package/corpus/templates/design/shared/board-file.ts +13 -2
- package/corpus/templates/design/shared/canvas-math.ts +231 -33
- package/corpus/templates/design/shared/code-layer.ts +122 -8
- package/corpus/templates/design/shared/component-library.ts +161 -0
- package/corpus/templates/design/shared/db-conflict.ts +27 -0
- package/corpus/templates/design/shared/design-source-capabilities.ts +2 -1
- package/corpus/templates/design/shared/figma-url.ts +149 -0
- package/corpus/templates/design/shared/pen-path.ts +16 -7
- package/corpus/templates/design/shared/source-mode.ts +1 -0
- package/corpus/templates/plan/app/components/plan/CanvasArea.tsx +7 -19
- package/corpus/templates/plan/changelog/2026-07-09-canvas-panning-now-keeps-zoom-fixed-even-at-the-edges.md +6 -0
- package/corpus/templates/slides/actions/index-design-system-with-builder.ts +16 -1
- package/corpus/templates/slides/app/context/DeckContext.tsx +311 -70
- package/corpus/templates/slides/changelog/2026-07-09-fixed-the-slide-rail-going-permanently-stale-during-a-long-a.md +6 -0
- package/dist/agent/production-agent.d.ts +79 -6
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +161 -14
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +29 -5
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/agent/run-store.d.ts +46 -0
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +61 -0
- package/dist/agent/run-store.js.map +1 -1
- package/dist/cli/design-connect.d.ts +6 -0
- package/dist/cli/design-connect.d.ts.map +1 -1
- package/dist/cli/design-connect.js +336 -139
- package/dist/cli/design-connect.js.map +1 -1
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +14 -0
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +2 -0
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +19 -3
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/client/MultiTabAssistantChat.js +38 -18
- package/dist/client/MultiTabAssistantChat.js.map +1 -1
- package/dist/client/agent-chat.d.ts +60 -0
- package/dist/client/agent-chat.d.ts.map +1 -1
- package/dist/client/agent-chat.js +122 -1
- package/dist/client/agent-chat.js.map +1 -1
- package/dist/client/chat/index.d.ts +1 -1
- package/dist/client/chat/index.d.ts.map +1 -1
- package/dist/client/chat/index.js +1 -1
- package/dist/client/chat/index.js.map +1 -1
- package/dist/client/extensions/EmbeddedExtension.d.ts +12 -0
- package/dist/client/extensions/EmbeddedExtension.d.ts.map +1 -1
- package/dist/client/extensions/EmbeddedExtension.js +36 -2
- package/dist/client/extensions/EmbeddedExtension.js.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/useCollabReconcile.js +18 -6
- package/dist/client/rich-markdown-editor/useCollabReconcile.js.map +1 -1
- package/dist/collab/awareness-store.d.ts +5 -0
- package/dist/collab/awareness-store.d.ts.map +1 -1
- package/dist/collab/awareness-store.js +19 -12
- package/dist/collab/awareness-store.js.map +1 -1
- package/dist/collab/client.d.ts.map +1 -1
- package/dist/collab/client.js +13 -5
- package/dist/collab/client.js.map +1 -1
- package/dist/collab/index.d.ts +1 -0
- package/dist/collab/index.d.ts.map +1 -1
- package/dist/collab/index.js +1 -0
- package/dist/collab/index.js.map +1 -1
- package/dist/collab/recent-edits.d.ts +3 -1
- package/dist/collab/recent-edits.d.ts.map +1 -1
- package/dist/collab/recent-edits.js +57 -2
- package/dist/collab/recent-edits.js.map +1 -1
- package/dist/index.browser.d.ts +1 -1
- package/dist/index.browser.d.ts.map +1 -1
- package/dist/index.browser.js +1 -1
- package/dist/index.browser.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +87 -10
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/builder-design-systems.d.ts +15 -0
- package/dist/server/builder-design-systems.d.ts.map +1 -1
- package/dist/server/builder-design-systems.js +9 -4
- package/dist/server/builder-design-systems.js.map +1 -1
- package/package.json +1 -1
|
@@ -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
|
+
});
|
|
@@ -57,23 +57,59 @@ function jsonValuesEqual(left: unknown, right: unknown): boolean {
|
|
|
57
57
|
return JSON.stringify(left) === JSON.stringify(right);
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
// Same-process mutex guarding the generation-session read-modify-write below.
|
|
61
|
+
// generate-screens' own tool description explicitly recommends fanning out
|
|
62
|
+
// parallel generate-design calls per returned frame ("fan out calls to
|
|
63
|
+
// generate-design for each returned frame"). Without this lock, two
|
|
64
|
+
// concurrent calls for the same designId both read the same pre-update
|
|
65
|
+
// session, each only marks its own frame done, and whichever writeAppState
|
|
66
|
+
// lands second silently discards the first call's frame-done update —
|
|
67
|
+
// application state has no CAS/versioning primitive (unlike designs.data's
|
|
68
|
+
// mutateDesignData), so a plain read-then-write here is a classic lost-update
|
|
69
|
+
// race. This mirrors the same-process serialization
|
|
70
|
+
// server/lib/design-data-mutation.ts uses (withDesignDataLock) for the
|
|
71
|
+
// designs.data column. Cross-process races remain (no CAS primitive exists
|
|
72
|
+
// for application state), but same-process fan-out from one agent turn is
|
|
73
|
+
// the realistic, explicitly-encouraged case this closes.
|
|
74
|
+
const generationSessionLocks = new Map<string, Promise<unknown>>();
|
|
75
|
+
|
|
76
|
+
function withGenerationSessionLock<T>(
|
|
77
|
+
designId: string,
|
|
78
|
+
callback: () => Promise<T>,
|
|
79
|
+
): Promise<T> {
|
|
80
|
+
const previous = generationSessionLocks.get(designId) ?? Promise.resolve();
|
|
81
|
+
const next = previous.then(callback, callback);
|
|
82
|
+
generationSessionLocks.set(designId, next);
|
|
83
|
+
const cleanup = () => {
|
|
84
|
+
if (generationSessionLocks.get(designId) === next) {
|
|
85
|
+
generationSessionLocks.delete(designId);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
next.then(cleanup, cleanup);
|
|
89
|
+
return next;
|
|
90
|
+
}
|
|
91
|
+
|
|
60
92
|
async function updateGenerationSessionForSavedFiles(
|
|
61
93
|
designId: string,
|
|
62
94
|
savedFilenames: string[],
|
|
63
95
|
) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
96
|
+
await withGenerationSessionLock(designId, async () => {
|
|
97
|
+
const key = designGenerationSessionKey(designId);
|
|
98
|
+
const rawSession = await readAppState(key).catch(() => null);
|
|
99
|
+
if (!rawSession || typeof rawSession !== "object") return;
|
|
100
|
+
const session = rawSession as unknown as DesignGenerationSession;
|
|
101
|
+
if (session.designId !== designId || !Array.isArray(session.frames)) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const nextSession = updateGenerationSessionWithSavedFiles(
|
|
106
|
+
session,
|
|
107
|
+
savedFilenames,
|
|
108
|
+
);
|
|
109
|
+
if (nextSession === session) return;
|
|
75
110
|
|
|
76
|
-
|
|
111
|
+
await writeAppState(key, nextSession as unknown as Record<string, unknown>);
|
|
112
|
+
});
|
|
77
113
|
}
|
|
78
114
|
|
|
79
115
|
const generateDesignAgentParameters = {
|
|
@@ -228,6 +264,37 @@ const generateDesignAction = defineAction({
|
|
|
228
264
|
message: "canvasFrames entries require fileId or filename",
|
|
229
265
|
}),
|
|
230
266
|
)
|
|
267
|
+
// Reject two placements for the same target in one call. Without
|
|
268
|
+
// this, mergeCanvasFramePlacements folds both entries into a single
|
|
269
|
+
// canvasFrames[fileId] value (last one wins), but the mutateDesignData
|
|
270
|
+
// isApplied check below verifies EVERY placedFrames entry against
|
|
271
|
+
// that single folded value — so the earlier, now-overwritten entry
|
|
272
|
+
// always fails the equality check. Since the mutate callback is
|
|
273
|
+
// deterministic, every retry recomputes the identical mismatch, so
|
|
274
|
+
// the whole action always fails with a "concurrent write conflicts"
|
|
275
|
+
// error after burning through all retries, even though nothing else
|
|
276
|
+
// was actually writing to the design.
|
|
277
|
+
.superRefine((frames, ctx) => {
|
|
278
|
+
const seen = new Map<string, number>();
|
|
279
|
+
frames.forEach((frame, index) => {
|
|
280
|
+
const key = frame.fileId
|
|
281
|
+
? `id:${frame.fileId}`
|
|
282
|
+
: `name:${frame.filename}`;
|
|
283
|
+
const firstIndex = seen.get(key);
|
|
284
|
+
if (firstIndex !== undefined) {
|
|
285
|
+
ctx.addIssue({
|
|
286
|
+
code: z.ZodIssueCode.custom,
|
|
287
|
+
path: [index],
|
|
288
|
+
message:
|
|
289
|
+
`canvasFrames entry ${index} duplicates the target already placed ` +
|
|
290
|
+
`at index ${firstIndex} (${frame.fileId ? `fileId ${frame.fileId}` : `filename ${frame.filename}`}). ` +
|
|
291
|
+
"Pass exactly one placement per screen.",
|
|
292
|
+
});
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
seen.set(key, index);
|
|
296
|
+
});
|
|
297
|
+
})
|
|
231
298
|
.optional(),
|
|
232
299
|
)
|
|
233
300
|
.optional()
|