@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
|
@@ -248,6 +248,92 @@ design-system indexing just to insert a component. Use
|
|
|
248
248
|
component/component set with provenance. Styles and variables still belong in
|
|
249
249
|
the Builder-backed design-system path above.
|
|
250
250
|
|
|
251
|
+
### Import from Figma (pixel-accurate frame import)
|
|
252
|
+
|
|
253
|
+
**When the user pastes a Figma frame/screen link and wants a real, editable
|
|
254
|
+
Design screen** (not a rendered image, not a component insert), use
|
|
255
|
+
`import-figma-frame` instead of `list-figma-library-assets` +
|
|
256
|
+
`insert-figma-library-asset`:
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
pnpm action import-figma-frame --figmaUrl "https://www.figma.com/design/<fileKey>/<name>?node-id=<id>"
|
|
260
|
+
# or
|
|
261
|
+
pnpm action import-figma-frame --fileKey "<fileKey>" --nodeId "12:34" --designId "<designId>"
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
- Accepts a full Figma URL (design/file/proto share links, including
|
|
265
|
+
`/branch/<key>/` branch URLs — the branch's own key is used automatically) or
|
|
266
|
+
an explicit `fileKey` + `nodeId`. If `nodeId` is omitted, the file's first
|
|
267
|
+
top-level frame is imported.
|
|
268
|
+
- Maps the node tree to real HTML/CSS: exact position/size, auto-layout as
|
|
269
|
+
flexbox, text (font, line-height, letter-spacing, case, decoration, align),
|
|
270
|
+
fills (solid/gradient/image, correctly layered and gradient-angle-derived,
|
|
271
|
+
not a default angle), strokes (including the CENTER/INSIDE/OUTSIDE
|
|
272
|
+
distinction), per-corner radii, shadows/blur, opacity, and blend modes.
|
|
273
|
+
Vector networks, boolean operations, and other structurally unsupported node
|
|
274
|
+
types are rendered as an exact PNG at 2x scale instead of an approximated
|
|
275
|
+
shape guess.
|
|
276
|
+
- Saves the result as a new screen via the same import path as other Design
|
|
277
|
+
imports (`saveImportedDesignFiles`), placed on the overview canvas.
|
|
278
|
+
- Returns a `fidelityReport` — `exactCount`, `approximated` (properties CSS
|
|
279
|
+
can only approximate: rotation, per-side stroke weights, radial/angular/
|
|
280
|
+
diamond gradients, blur radius scale), and `imageFallbacks` (subtrees
|
|
281
|
+
rendered as PNG instead of structural HTML). Read this back to the user when
|
|
282
|
+
a design has non-trivial fallbacks so they know what to expect if they later
|
|
283
|
+
edit that subtree.
|
|
284
|
+
- After import, treat the screen like any other: `view-screen`,
|
|
285
|
+
`get-design-snapshot`, `apply-visual-edit` / `edit-design` all work normally
|
|
286
|
+
on it.
|
|
287
|
+
- For a file's published FILL/TEXT/EFFECT/GRID styles (name, description, node
|
|
288
|
+
id — not full token values), use `get-figma-styles` with `fileUrl`/`fileKey`.
|
|
289
|
+
This is the file's Styles panel, not the Enterprise Variables API; full
|
|
290
|
+
design-token extraction still routes through the Builder-backed
|
|
291
|
+
`index-design-system-with-builder` path above.
|
|
292
|
+
|
|
293
|
+
#### Paste from Figma (Cmd+C/Cmd+V) vs. a copied frame link
|
|
294
|
+
|
|
295
|
+
A plain clipboard paste (Cmd+C in Figma, Cmd+V on the Design canvas) is
|
|
296
|
+
handled separately from `import-figma-frame`, because a clipboard paste and a
|
|
297
|
+
copied frame **link** carry fundamentally different information:
|
|
298
|
+
|
|
299
|
+
- **A copied frame link** (`?node-id=...`) names an exact node. Always exact —
|
|
300
|
+
use `import-figma-frame`.
|
|
301
|
+
- **A plain clipboard paste** only carries Figma's `figmeta` marker, which is
|
|
302
|
+
`{fileKey, pasteID, dataType}` — **no node id at all**, and `pasteID` is an
|
|
303
|
+
ephemeral, server-side identifier that the public REST API can't resolve
|
|
304
|
+
back to a node. So a clipboard paste can only ever get an exact node import
|
|
305
|
+
on a **best-effort match**, never a guarantee.
|
|
306
|
+
|
|
307
|
+
The canvas paste listener (`app/lib/figma-clipboard.ts` +
|
|
308
|
+
`import-figma-clipboard`) handles this automatically:
|
|
309
|
+
|
|
310
|
+
1. Decodes `figmeta` from the pasted HTML client-side
|
|
311
|
+
(`extractFigmeta`/`resolveFigmaPasteImportCall`) to decide whether to call
|
|
312
|
+
`import-figma-clipboard` (figmeta present) or the legacy
|
|
313
|
+
`import-design-source` HTML path (no figmeta — not a Figma paste, or an
|
|
314
|
+
older Figma client that doesn't emit the marker).
|
|
315
|
+
2. `import-figma-clipboard` fetches the file's shallow structure (top-level
|
|
316
|
+
frames + their direct children, `server/lib/figma-node-import.ts`'s
|
|
317
|
+
`fetchFileStructure(fileKey, 3)`) and heuristically matches it against the
|
|
318
|
+
pasted content's visible text (`server/lib/figma-clipboard-match.ts`):
|
|
319
|
+
a frame is only imported when its **name** or at least **two distinct
|
|
320
|
+
text-layer contents** appear verbatim in the paste. Anything ambiguous or
|
|
321
|
+
unmatched imports **nothing structural** — it never guesses and never
|
|
322
|
+
imports the whole file uninvited.
|
|
323
|
+
3. On a confident match, the matched node(s) are fetched and mapped through
|
|
324
|
+
the same `buildScreenFilesFromFigmaNodes` core `import-figma-frame` uses
|
|
325
|
+
(`strategy: "restNodes"` in the result, with a `fidelityReport`).
|
|
326
|
+
4. Otherwise it falls back to the legacy visible-HTML paste
|
|
327
|
+
(`strategy: "htmlFallback"`), and reports why via `matchStatus`
|
|
328
|
+
(`"ambiguous"`, `"none"`, or `"error"`) and `figmaApiKeyMissing` (no
|
|
329
|
+
`FIGMA_ACCESS_TOKEN` configured). The canvas paste toast surfaces a hint in
|
|
330
|
+
both cases: connect the Figma access token, or paste a frame **link**
|
|
331
|
+
instead for a guaranteed-exact import.
|
|
332
|
+
|
|
333
|
+
Tell users who want guaranteed pixel-exact imports to copy a frame **link**
|
|
334
|
+
("Copy link to selection" in Figma), not just Cmd+C — a plain paste is
|
|
335
|
+
convenient but only best-effort.
|
|
336
|
+
|
|
251
337
|
### Source: Brand Analysis (combines website + notes)
|
|
252
338
|
|
|
253
339
|
```bash
|
|
@@ -17,6 +17,9 @@ How to export designs and generate handoff documentation for developers converti
|
|
|
17
17
|
with the editor's Download SVG command. The editor's own Download SVG
|
|
18
18
|
command captures the live browser DOM for the most faithful snapshot; use
|
|
19
19
|
the action when you need agent-side SVG export without a live browser.
|
|
20
|
+
**This is not importable into Figma as editable vectors** — Figma cannot
|
|
21
|
+
parse `foreignObject` content, so it stays an opaque embedded HTML blob.
|
|
22
|
+
Use `export-design-as-figma-svg` (below) when the destination is Figma.
|
|
20
23
|
- **PNG**: there is no PNG export action. Point the user to the editor's
|
|
21
24
|
download menu (Download PNG) — PNG export is a client-side rasterization of
|
|
22
25
|
the live canvas and is not exposed as an agent action.
|
|
@@ -88,6 +91,60 @@ pnpm action export-pdf --id <designId>
|
|
|
88
91
|
|
|
89
92
|
Returns all design data and files needed for the client to render a PDF.
|
|
90
93
|
|
|
94
|
+
## Export to Figma (SVG)
|
|
95
|
+
|
|
96
|
+
`export-design-as-figma-svg` exports a design screen (or a selected
|
|
97
|
+
element's subtree) as a genuinely VECTOR SVG document — real
|
|
98
|
+
`<rect>`/`<path>`/`<text>`/`<image>` markup with
|
|
99
|
+
`<linearGradient>`/`<radialGradient>`/`<filter>` defs. Figma's SVG importer
|
|
100
|
+
parses this into normal, editable layers (rect/path/gradients/filters stay
|
|
101
|
+
editable). This is a different artifact from `export-svg` above, whose
|
|
102
|
+
`foreignObject` wrapper Figma cannot import as vectors at all.
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
pnpm action export-design-as-figma-svg --designId <designId>
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Optional args:
|
|
109
|
+
|
|
110
|
+
- `fileId` / `filename` — pick a specific screen (defaults to `index.html`).
|
|
111
|
+
- `nodeId` — scope the export to one selected element's subtree via its
|
|
112
|
+
`data-agent-native-node-id`, instead of the whole screen.
|
|
113
|
+
- `embedImages` (default `true`) — fetch and inline `http(s)` image sources
|
|
114
|
+
and background-images as `data:` URIs, so the SVG is self-contained for
|
|
115
|
+
clipboard paste. Set `false` to keep absolute URLs instead.
|
|
116
|
+
|
|
117
|
+
Returns `{ svg, filename, report, filePath? }`. `report` classifies every
|
|
118
|
+
element as `vectorized`, `approximated` (mapped with a documented caveat —
|
|
119
|
+
e.g. a non-square gradient angle, a non-uniform border, a radial gradient's
|
|
120
|
+
shape/position), `rasterized` (video/canvas/iframe content, and any element
|
|
121
|
+
with `backdrop-filter`, which SVG cannot express — embedded as a cropped
|
|
122
|
+
screenshot instead), or `omitted`. If no headless Chromium binary is
|
|
123
|
+
available in the current environment (expected in hosted/serverless
|
|
124
|
+
deploys), the action returns `{ ok: false, reason }` instead of throwing —
|
|
125
|
+
fall back to `export-svg` or `export-html`.
|
|
126
|
+
|
|
127
|
+
**Vectorized-text caveat**: Figma converts every imported SVG `<text>`
|
|
128
|
+
element to outlined vector paths on paste/drag-import. The exported
|
|
129
|
+
geometry is pixel-exact, but text pasted from this export is no longer
|
|
130
|
+
live, editable type in Figma — it's outlines, the same way any other
|
|
131
|
+
SVG-authoring tool's text becomes outlines on import. This is a Figma
|
|
132
|
+
import limitation, not a defect in the export; the report's
|
|
133
|
+
`vectorizedTextCaveat` field carries this note for the agent/user.
|
|
134
|
+
|
|
135
|
+
**Getting it into Figma**: two supported paths —
|
|
136
|
+
|
|
137
|
+
1. **Copy, then paste into Figma.** In the editor, right-click a selected
|
|
138
|
+
element or the canvas and choose **Copy as SVG** (Copy/Paste as ▸ Copy as
|
|
139
|
+
SVG). This writes the SVG markup to the system clipboard as `text/plain`
|
|
140
|
+
(the MIME Figma's own paste handler reads for "paste as vector shapes")
|
|
141
|
+
plus `image/svg+xml` as a secondary representation. Paste directly into a
|
|
142
|
+
Figma canvas.
|
|
143
|
+
2. **Download, then drag-import.** Figma's file browser also accepts a
|
|
144
|
+
plain `.svg` file dropped/imported directly — save the `svg` string
|
|
145
|
+
returned by the action to a `.svg` file and drag it into a Figma page the
|
|
146
|
+
same way you'd import any other SVG asset.
|
|
147
|
+
|
|
91
148
|
## Coding Handoff
|
|
92
149
|
|
|
93
150
|
When a user wants to convert an Alpine.js + Tailwind prototype into production
|
|
@@ -208,6 +208,17 @@ the connected app's text/code files through the bridge
|
|
|
208
208
|
- Saves are conflict-checked against the file's on-disk version — a file that
|
|
209
209
|
changed since it was read fails with a version conflict instead of being
|
|
210
210
|
overwritten.
|
|
211
|
+
- React/TSX canvas edits use build/debug provenance to locate the responsible
|
|
212
|
+
source, but structural meaning belongs to the coding agent. Do not apply a
|
|
213
|
+
generic AST reparent/group/ungroup transform. Hand off the exact subject and
|
|
214
|
+
target source anchors plus their runtime relationship; repeated `.map()`
|
|
215
|
+
instances, shared components, dynamic expressions, and cross-file edits
|
|
216
|
+
always require semantic inspection.
|
|
217
|
+
- For every semantic React write, read the file first, pass that exact
|
|
218
|
+
`versionHash` to `write-local-file` with `requireExpectedVersionHash: true`,
|
|
219
|
+
re-read and re-plan if it conflicts, and
|
|
220
|
+
verify the resulting HMR/runtime state before treating the preview as saved.
|
|
221
|
+
Human write consent remains mandatory and cannot be granted by an agent.
|
|
211
222
|
|
|
212
223
|
## Verification
|
|
213
224
|
|
|
@@ -67,6 +67,17 @@ patterns live in `.agents/skills/`.
|
|
|
67
67
|
or pass it as an action parameter. Figma styles and variables are design-system
|
|
68
68
|
inputs, not draggable media assets; route full file/design-system extraction
|
|
69
69
|
through Builder-backed indexing.
|
|
70
|
+
- To import a Figma frame/screen as a real, editable Design screen (not a
|
|
71
|
+
rendered image), use `import-figma-frame` with a `figmaUrl` (or
|
|
72
|
+
`fileKey`/`nodeId`) — it maps position, auto-layout, text, fills/gradients,
|
|
73
|
+
strokes, corner radii, effects, opacity, and blend modes pixel-accurately,
|
|
74
|
+
falling back to an exact PNG only for vector networks/boolean ops/unsupported
|
|
75
|
+
node types, and saves the result as a new screen. Read the returned
|
|
76
|
+
`fidelityReport` (`approximated`, `imageFallbacks`) back to the user when
|
|
77
|
+
non-trivial. Use `get-figma-styles` for a file's published style names (not
|
|
78
|
+
the Enterprise Variables API; full token extraction still routes through
|
|
79
|
+
Builder-backed indexing). See the `design-systems` skill's "Import from
|
|
80
|
+
Figma" section.
|
|
70
81
|
- Use Alpine.js and Tailwind CDN for interactive prototypes. Prefer Alpine
|
|
71
82
|
directives over raw inline event handlers.
|
|
72
83
|
- Navigate between prototype screens with Alpine state (`x-show`), a
|
|
@@ -112,9 +123,14 @@ patterns live in `.agents/skills/`.
|
|
|
112
123
|
workflow.
|
|
113
124
|
- Persist useful work early: create/update the design and files as soon as a
|
|
114
125
|
coherent candidate exists, then iterate.
|
|
115
|
-
- For
|
|
116
|
-
design shell, call `show-design-questions
|
|
117
|
-
|
|
126
|
+
- For a brief or ambiguous _new_ design prompt, ask before generating:
|
|
127
|
+
create/open the design shell, call `show-design-questions` with a small
|
|
128
|
+
tailored set, stop while the main canvas shows the questions, then continue
|
|
129
|
+
from the user's answers. Skip asking when the prompt is already specific,
|
|
130
|
+
it's a tweak/edit to an existing design, the user already answered a
|
|
131
|
+
question set for this design and is now iterating, or they say "decide for
|
|
132
|
+
me"/"surprise me"/"just build it" — see the `design-generation` skill for
|
|
133
|
+
how to size and word the questions.
|
|
118
134
|
- For multiple screens/states, call `generate-screens` first. It opens the
|
|
119
135
|
infinite screen overview and returns target filenames plus `canvasFrame`
|
|
120
136
|
placements. Then call `generate-design` with those files and pass the matching
|
|
@@ -202,12 +218,27 @@ patterns live in `.agents/skills/`.
|
|
|
202
218
|
layers: text, classes, styles, attributes, source order, and small structural
|
|
203
219
|
changes. Use it for selected-element edits before falling back to full
|
|
204
220
|
`update-design` / `generate-design` rewrites.
|
|
221
|
+
- For localhost React/TSX screens, treat compiler/debug metadata (project-relative
|
|
222
|
+
source file, line, column, component, and runtime multiplicity) as evidence
|
|
223
|
+
for locating source, not as permission to run a generic AST transform.
|
|
224
|
+
Compiler tooling may verify an anchor, classify a literal edit, and validate
|
|
225
|
+
syntax. Reparenting, grouping/ungrouping, wrappers, dynamic expressions,
|
|
226
|
+
repeated `.map()` instances, shared components, and cross-file changes must
|
|
227
|
+
be handed to the coding agent with both runtime relationships and exact
|
|
228
|
+
source anchors so it can inspect the surrounding program semantics.
|
|
229
|
+
- A localhost React handoff must read each source file first, write with the
|
|
230
|
+
exact returned `versionHash` as `expectedVersionHash` with
|
|
231
|
+
`requireExpectedVersionHash: true`, re-read and re-plan on conflict, and leave the
|
|
232
|
+
optimistic canvas preview in place until the dev server/HMR confirms the
|
|
233
|
+
runtime result. Never report a semantic canvas change as persisted merely
|
|
234
|
+
because it was submitted to the coding agent.
|
|
205
235
|
- Prefer `data-agent-native-layer-name="Readable name"` on meaningful elements.
|
|
206
236
|
The projection uses it before semantic/text fallbacks, and layer renames should
|
|
207
237
|
persist by updating that attribute.
|
|
208
|
-
-
|
|
209
|
-
|
|
210
|
-
|
|
238
|
+
- Inline/Alpine screens continue to use deterministic HTML code-layer edits.
|
|
239
|
+
Localhost React/TSX screens use the semantic coding-agent handoff above;
|
|
240
|
+
deterministic direct React writes remain intentionally limited to narrowly
|
|
241
|
+
proven literal edits and never include generic structural transforms.
|
|
211
242
|
|
|
212
243
|
## Code Workspace
|
|
213
244
|
|
|
@@ -282,12 +313,16 @@ patterns live in `.agents/skills/`.
|
|
|
282
313
|
and live app captures. See the same skill section.
|
|
283
314
|
- **Components**: `create-component` promotes a selected element into a
|
|
284
315
|
recognised reusable component; `index-components` scans a design's HTML for
|
|
285
|
-
existing component annotations; `
|
|
316
|
+
existing component annotations; `list-design-components` scans all HTML
|
|
317
|
+
screens for swap targets; `get-component-details`,
|
|
286
318
|
`preview-component-prop-edit`, `apply-component-prop-edit`, and
|
|
287
319
|
`open-component-source` inspect, preview, persist, and navigate to a
|
|
288
|
-
component instance.
|
|
289
|
-
|
|
290
|
-
|
|
320
|
+
component instance. Use `go-to-main-component` to select the earliest known
|
|
321
|
+
instance, `swap-component-instance` to replace an inline/Alpine instance
|
|
322
|
+
while preserving same-named prop overrides, and
|
|
323
|
+
`detach-component-instance` to turn an instance into plain editable markup.
|
|
324
|
+
See the `design-generation` skill's "Component reuse" section — promote a
|
|
325
|
+
3+ times repeated pattern instead of inventing another near-duplicate.
|
|
291
326
|
|
|
292
327
|
## Full App Building
|
|
293
328
|
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
type CanvasFrameGeometry,
|
|
26
26
|
type CanvasFramePlacement,
|
|
27
27
|
} from "../shared/canvas-frames.js";
|
|
28
|
+
import { isUniqueConstraintViolation } from "../shared/db-conflict.js";
|
|
28
29
|
import {
|
|
29
30
|
makeLocalhostRouteId,
|
|
30
31
|
titleFromRoutePath,
|
|
@@ -488,6 +489,18 @@ export default defineAction({
|
|
|
488
489
|
height: number;
|
|
489
490
|
}> = [];
|
|
490
491
|
const placementIntents: PlacementIntent[] = [];
|
|
492
|
+
// Duplicate-route guard: `existingFiles`/`existingByFilename`/the
|
|
493
|
+
// route-candidate lookups below are all snapshotted ONCE above the loop
|
|
494
|
+
// and never refreshed as new files are inserted mid-loop, so two entries
|
|
495
|
+
// in the SAME `requestedRoutes` call that resolve to the same route
|
|
496
|
+
// (repeated `paths`, or a `routeId` and a `path` naming the same route)
|
|
497
|
+
// each independently see "no existing match" and each insert a fresh
|
|
498
|
+
// `design_files` row — two overlapping screens for one route. Track
|
|
499
|
+
// routeIds already processed THIS call (keyed with width/height so an
|
|
500
|
+
// intentional multi-viewport request for the same route still creates
|
|
501
|
+
// its distinct variants) and skip later duplicates outright.
|
|
502
|
+
const seenRouteRequestKeys = new Set<string>();
|
|
503
|
+
let placementIndex = 0;
|
|
491
504
|
|
|
492
505
|
for (let index = 0; index < requestedRoutes.length; index += 1) {
|
|
493
506
|
const input = requestedRoutes[index]!;
|
|
@@ -505,6 +518,9 @@ export default defineAction({
|
|
|
505
518
|
);
|
|
506
519
|
const routeId =
|
|
507
520
|
input.routeId ?? manifestRoute?.id ?? makeLocalhostRouteId(path);
|
|
521
|
+
const routeRequestKey = `${routeId}::${input.width ?? ""}x${input.height ?? ""}`;
|
|
522
|
+
if (seenRouteRequestKeys.has(routeRequestKey)) continue;
|
|
523
|
+
seenRouteRequestKeys.add(routeRequestKey);
|
|
508
524
|
const title =
|
|
509
525
|
input.title ?? manifestRoute?.title ?? titleFromRoutePath(path);
|
|
510
526
|
const sourceFile = input.sourceFile ?? manifestRoute?.sourceFile;
|
|
@@ -619,7 +635,9 @@ export default defineAction({
|
|
|
619
635
|
const filename =
|
|
620
636
|
existing?.filename ??
|
|
621
637
|
uniqueFilename(path, usedFilenames, preferredFilename);
|
|
622
|
-
|
|
638
|
+
// Reassigned below if a concurrent request wins the insert race for
|
|
639
|
+
// this exact (designId, filename) pair — see the `else` branch.
|
|
640
|
+
let fileId = existing?.id ?? nanoid();
|
|
623
641
|
const existingScreenMetadata = existing
|
|
624
642
|
? metadataForFile(
|
|
625
643
|
existing.id,
|
|
@@ -656,16 +674,50 @@ export default defineAction({
|
|
|
656
674
|
await seedFromText(existing.id, url);
|
|
657
675
|
}
|
|
658
676
|
} else {
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
677
|
+
try {
|
|
678
|
+
await db.insert(schema.designFiles).values({
|
|
679
|
+
id: fileId,
|
|
680
|
+
designId,
|
|
681
|
+
filename,
|
|
682
|
+
fileType: "html",
|
|
683
|
+
content: url,
|
|
684
|
+
createdAt: now,
|
|
685
|
+
updatedAt: now,
|
|
686
|
+
});
|
|
687
|
+
await seedFromText(fileId, url);
|
|
688
|
+
} catch (err) {
|
|
689
|
+
if (!isUniqueConstraintViolation(err)) throw err;
|
|
690
|
+
// Cross-request race: this snapshot's `existingFiles` query ran
|
|
691
|
+
// before a concurrent add-localhost-screens call (for the same
|
|
692
|
+
// route) committed its own insert, so both requests independently
|
|
693
|
+
// computed the same deterministic filename and both tried to
|
|
694
|
+
// create it. The `design_files_design_filename_unique_idx` unique
|
|
695
|
+
// index (see server/plugins/db.ts) turns the loser's insert into
|
|
696
|
+
// this error instead of a silent duplicate screen. Recover by
|
|
697
|
+
// adopting whichever row actually won the race and updating its
|
|
698
|
+
// content, the same way the `existing` branch above does.
|
|
699
|
+
const [winner] = await db
|
|
700
|
+
.select()
|
|
701
|
+
.from(schema.designFiles)
|
|
702
|
+
.where(
|
|
703
|
+
and(
|
|
704
|
+
eq(schema.designFiles.designId, designId),
|
|
705
|
+
eq(schema.designFiles.filename, filename),
|
|
706
|
+
),
|
|
707
|
+
)
|
|
708
|
+
.limit(1);
|
|
709
|
+
if (!winner) throw err;
|
|
710
|
+
fileId = winner.id;
|
|
711
|
+
await db
|
|
712
|
+
.update(schema.designFiles)
|
|
713
|
+
.set({ content: url, fileType: "html", updatedAt: now })
|
|
714
|
+
.where(eq(schema.designFiles.id, winner.id));
|
|
715
|
+
if (await hasCollabState(winner.id)) {
|
|
716
|
+
await applyText(winner.id, url, "content", "agent");
|
|
717
|
+
} else {
|
|
718
|
+
await seedFromText(winner.id, url);
|
|
719
|
+
}
|
|
720
|
+
}
|
|
669
721
|
}
|
|
670
722
|
|
|
671
723
|
savedScreens.push({
|
|
@@ -688,12 +740,13 @@ export default defineAction({
|
|
|
688
740
|
x:
|
|
689
741
|
input.x ??
|
|
690
742
|
existingFrame?.x ??
|
|
691
|
-
layoutStartX +
|
|
743
|
+
layoutStartX + placementIndex * (width + layoutGap),
|
|
692
744
|
y: input.y ?? existingFrame?.y ?? layoutStartY,
|
|
693
745
|
width,
|
|
694
746
|
height,
|
|
695
|
-
z: input.z ?? existingFrame?.z ??
|
|
747
|
+
z: input.z ?? existingFrame?.z ?? placementIndex,
|
|
696
748
|
};
|
|
749
|
+
placementIndex += 1;
|
|
697
750
|
placementIntents.push({
|
|
698
751
|
fileId,
|
|
699
752
|
filename,
|
|
@@ -12,11 +12,12 @@
|
|
|
12
12
|
* HTML attribute on the component root.
|
|
13
13
|
* - `classReplace` — replaces one Tailwind class with another on the root node.
|
|
14
14
|
*
|
|
15
|
-
* **
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
15
|
+
* **Real-app sources (localhost / fusion):** deliberately fail closed. This
|
|
16
|
+
* action's patcher operates on SQL-backed HTML design files; it must never be
|
|
17
|
+
* reused for compiled JSX/TSX source. Real-app prop persistence needs a
|
|
18
|
+
* dedicated consented, version-guarded bridge transform. Until that exists,
|
|
19
|
+
* callers may preview but this action returns `ctaRequired: true` without
|
|
20
|
+
* reading or modifying a design file.
|
|
20
21
|
*
|
|
21
22
|
* See DESIGN-STUDIO-PLAN.md §6.1, §7 (preview/apply contract), §11 phase 2.
|
|
22
23
|
*/
|
|
@@ -38,10 +39,11 @@ import { z } from "zod";
|
|
|
38
39
|
import { getDb, schema } from "../server/db/index.js";
|
|
39
40
|
import "../server/db/index.js"; // ensure registerShareableResource runs
|
|
40
41
|
import {
|
|
42
|
+
prepareInlineSourceEdit,
|
|
43
|
+
SourceWorkspaceEditConflictError,
|
|
41
44
|
writeInlineSourceFile,
|
|
42
45
|
type SourceWorkspaceFile,
|
|
43
46
|
} from "../server/source-workspace.js";
|
|
44
|
-
import { resolveSourceCapabilities } from "../shared/capability-resolver.js";
|
|
45
47
|
import {
|
|
46
48
|
applyVisualEdit,
|
|
47
49
|
buildCodeLayerProjection,
|
|
@@ -56,9 +58,7 @@ import {
|
|
|
56
58
|
componentNameFor,
|
|
57
59
|
componentNodeIdMatches,
|
|
58
60
|
} from "../shared/component-model.js";
|
|
59
|
-
import { hasCapability } from "../shared/design-source-capabilities.js";
|
|
60
61
|
import { designSourceTypeFromData } from "../shared/source-mode.js";
|
|
61
|
-
import { sourceContentHash } from "../shared/source-workspace.js";
|
|
62
62
|
|
|
63
63
|
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
64
64
|
|
|
@@ -174,8 +174,8 @@ export default defineAction({
|
|
|
174
174
|
"For inline/Alpine designs, edits the data-agent-native-prop-* attributes, " +
|
|
175
175
|
"x-data expression, or class list of the component root via the deterministic " +
|
|
176
176
|
"HTML-patch path (same seam as apply-visual-edit). " +
|
|
177
|
-
"For real-app sources,
|
|
178
|
-
"
|
|
177
|
+
"For real-app sources, returns ctaRequired=true without modifying any file; " +
|
|
178
|
+
"compiled source requires a dedicated consented bridge transform.",
|
|
179
179
|
schema: z.object({
|
|
180
180
|
designId: z.string().describe("Design project ID"),
|
|
181
181
|
nodeId: z
|
|
@@ -234,19 +234,21 @@ export default defineAction({
|
|
|
234
234
|
.optional(),
|
|
235
235
|
}),
|
|
236
236
|
run: async ({ designId, nodeId, fileId, edit, source }) => {
|
|
237
|
-
const db = getDb();
|
|
238
|
-
|
|
239
237
|
// ── Access check ────────────────────────────────────────────────────────
|
|
240
238
|
const access = await resolveAccess("design", designId);
|
|
241
239
|
if (!access) throw new Error("Design not found");
|
|
242
240
|
|
|
243
|
-
// ── Source type
|
|
241
|
+
// ── Source type gate ────────────────────────────────────────────────────
|
|
244
242
|
const rawData = (access.resource as { data?: unknown }).data;
|
|
245
243
|
const sourceType = designSourceTypeFromData(rawData);
|
|
246
|
-
const caps = resolveSourceCapabilities(sourceType);
|
|
247
244
|
|
|
248
|
-
//
|
|
249
|
-
|
|
245
|
+
// Fail closed for every real-app tier even if its generic capability map
|
|
246
|
+
// advertises applyEdit. This action only knows how to patch SQL-backed HTML;
|
|
247
|
+
// allowing localhost through here could report success for the mirror while
|
|
248
|
+
// leaving the real JSX/TSX file untouched. A future compiled-source action
|
|
249
|
+
// must perform consent, canonical path resolution, AST anchoring, and an
|
|
250
|
+
// expected-version bridge write as one dedicated transaction.
|
|
251
|
+
if (sourceType !== "inline") {
|
|
250
252
|
return {
|
|
251
253
|
designId,
|
|
252
254
|
nodeId,
|
|
@@ -254,13 +256,14 @@ export default defineAction({
|
|
|
254
256
|
persisted: false,
|
|
255
257
|
ctaRequired: true,
|
|
256
258
|
ctaMessage:
|
|
257
|
-
"Prop write-back to real app sources requires
|
|
258
|
-
"
|
|
259
|
+
"Prop write-back to real app sources requires a dedicated consented, " +
|
|
260
|
+
"version-guarded compiled-source transform. " +
|
|
259
261
|
"Use preview-component-prop-edit to preview without persisting.",
|
|
260
262
|
};
|
|
261
263
|
}
|
|
262
264
|
|
|
263
265
|
await assertAccess("design", designId, "editor");
|
|
266
|
+
const db = getDb();
|
|
264
267
|
|
|
265
268
|
// ── Fetch file ───────────────────────────────────────────────────────────
|
|
266
269
|
const conditions = [
|
|
@@ -289,12 +292,24 @@ export default defineAction({
|
|
|
289
292
|
|
|
290
293
|
if (!file) throw new Error("Design HTML file not found.");
|
|
291
294
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
file.
|
|
296
|
-
|
|
297
|
-
|
|
295
|
+
const workspaceFile: SourceWorkspaceFile = {
|
|
296
|
+
id: file.id,
|
|
297
|
+
designId: file.designId,
|
|
298
|
+
filename: file.filename,
|
|
299
|
+
fileType: "html",
|
|
300
|
+
content: file.content,
|
|
301
|
+
createdAt: null,
|
|
302
|
+
updatedAt: file.updatedAt,
|
|
303
|
+
};
|
|
304
|
+
let prepared: Awaited<ReturnType<typeof prepareInlineSourceEdit>>;
|
|
305
|
+
try {
|
|
306
|
+
prepared = await prepareInlineSourceEdit({
|
|
307
|
+
file: workspaceFile,
|
|
308
|
+
currentContent: source?.currentContent,
|
|
309
|
+
revision: source?.revision,
|
|
310
|
+
});
|
|
311
|
+
} catch (error) {
|
|
312
|
+
if (!(error instanceof SourceWorkspaceEditConflictError)) throw error;
|
|
298
313
|
return {
|
|
299
314
|
designId,
|
|
300
315
|
nodeId,
|
|
@@ -307,18 +322,12 @@ export default defineAction({
|
|
|
307
322
|
};
|
|
308
323
|
}
|
|
309
324
|
|
|
310
|
-
//
|
|
311
|
-
// the
|
|
312
|
-
//
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
: (file.content ?? "");
|
|
317
|
-
// Capture the hash of this EXACT base — the same string the transform
|
|
318
|
-
// below reads from — so persistEdit can pass it through as
|
|
319
|
-
// expectedVersionHash. A re-read at persist time would hash whatever is
|
|
320
|
-
// live "then" instead of proving this base is still current.
|
|
321
|
-
const baseVersionHash = sourceContentHash(html);
|
|
325
|
+
// The transform runs against the caller's working copy (when supplied),
|
|
326
|
+
// while the persist CAS uses the live hash that working copy is allowed to
|
|
327
|
+
// replace. Keeping those identities separate preserves rapid unsaved prop
|
|
328
|
+
// edits without weakening concurrent-writer rejection.
|
|
329
|
+
const html = prepared.content;
|
|
330
|
+
const baseVersionHash = prepared.expectedVersionHash;
|
|
322
331
|
|
|
323
332
|
// ── Resolve node ─────────────────────────────────────────────────────────
|
|
324
333
|
const codeLayerSource: CodeLayerSource = {
|
|
@@ -32,7 +32,7 @@ import { z } from "zod";
|
|
|
32
32
|
import { getDb, schema } from "../server/db/index.js";
|
|
33
33
|
import "../server/db/index.js"; // ensure registerShareableResource runs
|
|
34
34
|
import {
|
|
35
|
-
|
|
35
|
+
prepareInlineSourceEdit,
|
|
36
36
|
writeInlineSourceFile,
|
|
37
37
|
type SourceWorkspaceFile,
|
|
38
38
|
} from "../server/source-workspace.js";
|
|
@@ -51,7 +51,6 @@ import {
|
|
|
51
51
|
readTimelinePlaybackMode,
|
|
52
52
|
withTimelinePlaybackMode,
|
|
53
53
|
} from "../shared/motion-timeline.js";
|
|
54
|
-
import { sourceContentHash } from "../shared/source-workspace.js";
|
|
55
54
|
|
|
56
55
|
// ─── DoS-guard caps ──────────────────────────────────────────────────────────
|
|
57
56
|
//
|
|
@@ -343,7 +342,15 @@ export default defineAction({
|
|
|
343
342
|
.describe(
|
|
344
343
|
"Current open editor HTML for the target file. When supplied, the " +
|
|
345
344
|
"managed motion CSS is patched into this content instead of the " +
|
|
346
|
-
"last SQL snapshot so in-flight local edits are preserved."
|
|
345
|
+
"last SQL snapshot so in-flight local edits are preserved. " +
|
|
346
|
+
"revision must also be supplied.",
|
|
347
|
+
),
|
|
348
|
+
revision: z
|
|
349
|
+
.string()
|
|
350
|
+
.optional()
|
|
351
|
+
.describe(
|
|
352
|
+
"design_files.updatedAt value the currentContent is based on. " +
|
|
353
|
+
"Required when currentContent is supplied.",
|
|
347
354
|
),
|
|
348
355
|
}),
|
|
349
356
|
run: async ({
|
|
@@ -357,6 +364,7 @@ export default defineAction({
|
|
|
357
364
|
defaultEase,
|
|
358
365
|
includeContent,
|
|
359
366
|
currentContent: currentContentInput,
|
|
367
|
+
revision,
|
|
360
368
|
}) => {
|
|
361
369
|
const access = await assertAccess("design", designId, "editor");
|
|
362
370
|
|
|
@@ -378,6 +386,7 @@ export default defineAction({
|
|
|
378
386
|
filename: schema.designFiles.filename,
|
|
379
387
|
fileType: schema.designFiles.fileType,
|
|
380
388
|
content: schema.designFiles.content,
|
|
389
|
+
updatedAt: schema.designFiles.updatedAt,
|
|
381
390
|
})
|
|
382
391
|
.from(schema.designFiles)
|
|
383
392
|
.innerJoin(
|
|
@@ -398,40 +407,25 @@ export default defineAction({
|
|
|
398
407
|
const fileId = file.id;
|
|
399
408
|
const resolvedSourceRef = sourceRef ?? fileId;
|
|
400
409
|
|
|
401
|
-
//
|
|
402
|
-
//
|
|
403
|
-
//
|
|
404
|
-
//
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
// this transform's base was" for the persist's optimistic-concurrency
|
|
408
|
-
// check below — never a fresh re-hash of already-transformed content
|
|
409
|
-
// (that would trivially match itself and prove nothing raced).
|
|
410
|
-
let currentContent: string;
|
|
411
|
-
let baseVersionHash: string;
|
|
412
|
-
if (currentContentInput !== undefined) {
|
|
413
|
-
// Caller supplied their own editor snapshot as the patch base. It may
|
|
414
|
-
// already be stale relative to the live doc — that's fine.
|
|
415
|
-
// writeInlineSourceFile's own guard (via readLiveSourceFile at write
|
|
416
|
-
// time) correctly rejects the write if this base no longer matches
|
|
417
|
-
// live state, exactly as designed for every other caller-supplied-base
|
|
418
|
-
// action (see apply-component-prop-edit.ts / apply-shader-fill.ts).
|
|
419
|
-
currentContent = currentContentInput;
|
|
420
|
-
baseVersionHash = sourceContentHash(currentContent);
|
|
421
|
-
} else {
|
|
422
|
-
const workspaceFileForRead: SourceWorkspaceFile = {
|
|
410
|
+
// Keep the transform working copy separate from the live version it may
|
|
411
|
+
// replace. A caller snapshot can legitimately contain unsaved local edits
|
|
412
|
+
// beyond the matching SQL revision; the write CAS must guard the observed
|
|
413
|
+
// live base, not incorrectly hash those unsaved edits as if already live.
|
|
414
|
+
const prepared = await prepareInlineSourceEdit({
|
|
415
|
+
file: {
|
|
423
416
|
id: file.id,
|
|
424
417
|
designId: file.designId,
|
|
425
418
|
filename: file.filename,
|
|
426
419
|
fileType: file.fileType,
|
|
427
420
|
content: file.content,
|
|
428
421
|
createdAt: null,
|
|
429
|
-
updatedAt:
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
422
|
+
updatedAt: file.updatedAt,
|
|
423
|
+
},
|
|
424
|
+
currentContent: currentContentInput,
|
|
425
|
+
revision,
|
|
426
|
+
});
|
|
427
|
+
const currentContent = prepared.content;
|
|
428
|
+
const baseVersionHash = prepared.expectedVersionHash;
|
|
435
429
|
|
|
436
430
|
// ── 2. Compile tracks → CSS ─────────────────────────────────────────────
|
|
437
431
|
const inputTracks = tracks as MotionTrack[];
|