@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
|
@@ -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()
|
|
@@ -2,10 +2,11 @@ import { defineAction, embedApp } from "@agent-native/core";
|
|
|
2
2
|
import { writeAppState } from "@agent-native/core/application-state";
|
|
3
3
|
import { buildDeepLink } from "@agent-native/core/server";
|
|
4
4
|
import { assertAccess } from "@agent-native/core/sharing";
|
|
5
|
+
import { eq } from "drizzle-orm";
|
|
5
6
|
import { nanoid } from "nanoid";
|
|
6
7
|
import { z } from "zod";
|
|
7
8
|
|
|
8
|
-
import "../server/db/index.js";
|
|
9
|
+
import { getDb, schema } from "../server/db/index.js";
|
|
9
10
|
import {
|
|
10
11
|
type AssignedCanvasRegion,
|
|
11
12
|
DEFAULT_ASSIGNED_REGION_GAP,
|
|
@@ -186,6 +187,28 @@ export default defineAction({
|
|
|
186
187
|
.min(1)
|
|
187
188
|
.max(8)
|
|
188
189
|
.superRefine((screens, ctx) => {
|
|
190
|
+
// Two screens sharing an explicit frameId would produce two
|
|
191
|
+
// DesignGenerationFrame entries with the same frameId (and thus
|
|
192
|
+
// the same derived agentId `agent-${frameId}`) once frames get
|
|
193
|
+
// built below, colliding agent presence/status tracking for both
|
|
194
|
+
// screens on the overview canvas.
|
|
195
|
+
const frameIdsSeen = new Map<string, number>();
|
|
196
|
+
screens.forEach((screen, index) => {
|
|
197
|
+
if (!screen.frameId) return;
|
|
198
|
+
const firstIndex = frameIdsSeen.get(screen.frameId);
|
|
199
|
+
if (firstIndex !== undefined) {
|
|
200
|
+
ctx.addIssue({
|
|
201
|
+
code: z.ZodIssueCode.custom,
|
|
202
|
+
path: [index, "frameId"],
|
|
203
|
+
message:
|
|
204
|
+
`duplicate frameId "${screen.frameId}" (already used at index ${firstIndex}); ` +
|
|
205
|
+
"each screen needs a distinct frameId",
|
|
206
|
+
});
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
frameIdsSeen.set(screen.frameId, index);
|
|
210
|
+
});
|
|
211
|
+
|
|
189
212
|
const requestAnchors = new Map<
|
|
190
213
|
string,
|
|
191
214
|
{ index: number; role: DesignGenerationFrame["role"] }
|
|
@@ -259,21 +282,43 @@ export default defineAction({
|
|
|
259
282
|
: DEFAULT_ASSIGNED_REGION_MAX_COLUMNS,
|
|
260
283
|
});
|
|
261
284
|
|
|
262
|
-
|
|
263
|
-
//
|
|
264
|
-
//
|
|
265
|
-
//
|
|
285
|
+
// Seed the used-filename set with the design's EXISTING files, not just
|
|
286
|
+
// names requested in this call. Without this, a requested/auto-slugged
|
|
287
|
+
// target (e.g. a screen titled "Onboarding" slugging to "onboarding.html")
|
|
288
|
+
// can silently collide with an already-saved screen: generate-design's
|
|
289
|
+
// existing-file lookup is keyed by filename, so the later generate-design
|
|
290
|
+
// call for that "new" target would UPDATE (overwrite) the pre-existing
|
|
291
|
+
// file instead of creating the new screen the agent intended.
|
|
292
|
+
const db = getDb();
|
|
293
|
+
const existingFiles = await db
|
|
294
|
+
.select({ filename: schema.designFiles.filename })
|
|
295
|
+
.from(schema.designFiles)
|
|
296
|
+
.where(eq(schema.designFiles.designId, designId));
|
|
297
|
+
const usedFilenames = new Set(
|
|
298
|
+
existingFiles
|
|
299
|
+
.map((file) => file.filename)
|
|
300
|
+
.filter((filename): filename is string => Boolean(filename)),
|
|
301
|
+
);
|
|
302
|
+
// Dedupe any filename (explicit or auto-generated) so two screens can
|
|
303
|
+
// never resolve to the same target file, and so no target collides with
|
|
304
|
+
// an existing screen — otherwise generate-design silently overwrites the
|
|
305
|
+
// other screen's content.
|
|
266
306
|
const dedupeFilename = (base: string): string => {
|
|
267
|
-
if (!
|
|
268
|
-
|
|
307
|
+
if (!usedFilenames.has(base)) {
|
|
308
|
+
usedFilenames.add(base);
|
|
269
309
|
return base;
|
|
270
310
|
}
|
|
271
|
-
const count = seenFilenames.get(base)! + 1;
|
|
272
|
-
seenFilenames.set(base, count);
|
|
273
311
|
const dot = base.lastIndexOf(".");
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
312
|
+
const stem = dot > 0 ? base.slice(0, dot) : base;
|
|
313
|
+
const ext = dot > 0 ? base.slice(dot) : "";
|
|
314
|
+
let count = 2;
|
|
315
|
+
let candidate = `${stem}-${count}${ext}`;
|
|
316
|
+
while (usedFilenames.has(candidate)) {
|
|
317
|
+
count += 1;
|
|
318
|
+
candidate = `${stem}-${count}${ext}`;
|
|
319
|
+
}
|
|
320
|
+
usedFilenames.add(candidate);
|
|
321
|
+
return candidate;
|
|
277
322
|
};
|
|
278
323
|
const requestedTargets = screens.map((screen, index) => {
|
|
279
324
|
if (screen.filename) return dedupeFilename(screen.filename);
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { defineAction } from "@agent-native/core";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
import { executeProviderApiRequest } from "../server/lib/provider-api.js";
|
|
5
|
+
import { parseFigmaFileKey } from "../shared/figma-url.js";
|
|
6
|
+
|
|
7
|
+
const schemaInput = z
|
|
8
|
+
.object({
|
|
9
|
+
fileUrl: z
|
|
10
|
+
.string()
|
|
11
|
+
.trim()
|
|
12
|
+
.optional()
|
|
13
|
+
.describe(
|
|
14
|
+
"Figma file URL, for example https://www.figma.com/design/<fileKey>/...",
|
|
15
|
+
),
|
|
16
|
+
fileKey: z
|
|
17
|
+
.string()
|
|
18
|
+
.trim()
|
|
19
|
+
.optional()
|
|
20
|
+
.describe("Figma file key. Used when fileUrl is omitted."),
|
|
21
|
+
})
|
|
22
|
+
.refine((value) => value.fileUrl || value.fileKey, {
|
|
23
|
+
message: "Pass fileUrl or fileKey.",
|
|
24
|
+
path: ["fileUrl"],
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
type FigmaProviderEnvelope = {
|
|
28
|
+
response?: {
|
|
29
|
+
ok?: boolean;
|
|
30
|
+
status?: number;
|
|
31
|
+
statusText?: string;
|
|
32
|
+
json?: unknown;
|
|
33
|
+
text?: string;
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
type FigmaStyleRecord = {
|
|
38
|
+
key?: unknown;
|
|
39
|
+
node_id?: unknown;
|
|
40
|
+
style_type?: unknown;
|
|
41
|
+
name?: unknown;
|
|
42
|
+
description?: unknown;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
function stringValue(value: unknown): string | null {
|
|
46
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function providerJson(envelope: unknown, label: string): unknown {
|
|
50
|
+
const response = (envelope as FigmaProviderEnvelope | null)?.response;
|
|
51
|
+
if (!response) throw new Error(`Figma ${label} response was empty.`);
|
|
52
|
+
if (response.ok === false) {
|
|
53
|
+
const detail =
|
|
54
|
+
stringValue(response.text) ||
|
|
55
|
+
response.statusText ||
|
|
56
|
+
`HTTP ${response.status ?? "error"}`;
|
|
57
|
+
throw new Error(`Figma ${label} request failed: ${detail}`);
|
|
58
|
+
}
|
|
59
|
+
return response.json;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export default defineAction({
|
|
63
|
+
description:
|
|
64
|
+
"Summarize a Figma file's published styles (fill/text/effect/grid) by name, description, and node id. This is the file's Styles panel, NOT the Enterprise Variables API — full design-token extraction (including variables/collections) routes through index-design-system-with-builder or import-file --format fig. Requires the saved FIGMA_ACCESS_TOKEN secret.",
|
|
65
|
+
schema: schemaInput,
|
|
66
|
+
http: { method: "GET" },
|
|
67
|
+
readOnly: true,
|
|
68
|
+
publicAgent: { expose: true, readOnly: true, requiresAuth: true },
|
|
69
|
+
run: async (args) => {
|
|
70
|
+
const fileKey =
|
|
71
|
+
parseFigmaFileKey(args.fileKey) ?? parseFigmaFileKey(args.fileUrl);
|
|
72
|
+
if (!fileKey) {
|
|
73
|
+
throw new Error("Could not find a Figma file key in the provided URL.");
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const envelope = await executeProviderApiRequest({
|
|
77
|
+
provider: "figma",
|
|
78
|
+
method: "GET",
|
|
79
|
+
path: `/files/${fileKey}/styles`,
|
|
80
|
+
maxBytes: 2 * 1024 * 1024,
|
|
81
|
+
});
|
|
82
|
+
const json = providerJson(envelope, "styles") as {
|
|
83
|
+
meta?: { styles?: unknown };
|
|
84
|
+
};
|
|
85
|
+
const records = Array.isArray(json.meta?.styles)
|
|
86
|
+
? (json.meta!.styles as FigmaStyleRecord[])
|
|
87
|
+
: [];
|
|
88
|
+
|
|
89
|
+
const styles = records
|
|
90
|
+
.filter((record) => record && typeof record === "object")
|
|
91
|
+
.map((record) => ({
|
|
92
|
+
key: stringValue(record.key),
|
|
93
|
+
nodeId: stringValue(record.node_id),
|
|
94
|
+
styleType: stringValue(record.style_type),
|
|
95
|
+
name: stringValue(record.name) ?? "Untitled style",
|
|
96
|
+
description: stringValue(record.description),
|
|
97
|
+
}));
|
|
98
|
+
|
|
99
|
+
const byType = styles.reduce<Record<string, number>>((acc, style) => {
|
|
100
|
+
const type = style.styleType ?? "UNKNOWN";
|
|
101
|
+
acc[type] = (acc[type] ?? 0) + 1;
|
|
102
|
+
return acc;
|
|
103
|
+
}, {});
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
source: "figma",
|
|
107
|
+
fileKey,
|
|
108
|
+
total: styles.length,
|
|
109
|
+
byType,
|
|
110
|
+
styles,
|
|
111
|
+
guidance:
|
|
112
|
+
"These are the file's published FILL/TEXT/EFFECT/GRID styles (name, description, node id), not the Enterprise Variables API. For full design-system token extraction (variables, collections, modes) use index-design-system-with-builder or import-file --format fig instead of hand-mapping these style records.",
|
|
113
|
+
};
|
|
114
|
+
},
|
|
115
|
+
});
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* go-to-main-component — Figma's "Go to main component".
|
|
3
|
+
*
|
|
4
|
+
* DESIGN NOTE — mapping onto this codebase's data model: there is no
|
|
5
|
+
* persisted "main component" concept anywhere here. A component is just a
|
|
6
|
+
* name (`data-agent-native-component="Name"`) that happens to be stamped on
|
|
7
|
+
* more than one element; every instance is an independently-duplicated copy
|
|
8
|
+
* of HTML (see `shared/component-model.ts`, `component_index` schema — it
|
|
9
|
+
* stores props/variants/runtime-selectors metadata, never a canonical
|
|
10
|
+
* definition). A true "main component" would require a data-model addition
|
|
11
|
+
* (e.g. an `isMain`/definition pointer on `component_index`) — out of scope
|
|
12
|
+
* here; flagged for a follow-up if promoting a specific instance as
|
|
13
|
+
* authoritative becomes a real need.
|
|
14
|
+
*
|
|
15
|
+
* Closest tractable equivalent: treat the EARLIEST instance of the same
|
|
16
|
+
* component name — scanning every design file in `createdAt` order, document
|
|
17
|
+
* order within each file — as the "main" analogue, mirroring the common case
|
|
18
|
+
* where a component's original occurrence predates its copies. If the given
|
|
19
|
+
* instance already IS that earliest one, this returns `isMain: true` instead
|
|
20
|
+
* of navigating (nothing to jump to). Otherwise it writes a `navigate`
|
|
21
|
+
* app-state command (same mechanism as `navigate` / `open-component-source`)
|
|
22
|
+
* so the editor selects the target instance, including switching design
|
|
23
|
+
* files when the main instance lives on a different screen.
|
|
24
|
+
*
|
|
25
|
+
* Navigation-only + inline/Alpine designs only (real-app sources return a
|
|
26
|
+
* CTA). Because this writes the transient `navigate` application-state
|
|
27
|
+
* command, it is exposed as the default POST mutation rather than a GET.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
import { defineAction } from "@agent-native/core/action";
|
|
31
|
+
import { writeAppState } from "@agent-native/core/application-state";
|
|
32
|
+
import { getText, hasCollabState } from "@agent-native/core/collab";
|
|
33
|
+
import { accessFilter, resolveAccess } from "@agent-native/core/sharing";
|
|
34
|
+
import { and, eq } from "drizzle-orm";
|
|
35
|
+
import { z } from "zod";
|
|
36
|
+
|
|
37
|
+
import { getDb, schema } from "../server/db/index.js";
|
|
38
|
+
import "../server/db/index.js"; // ensure registerShareableResource runs
|
|
39
|
+
import { buildCodeLayerProjection } from "../shared/code-layer.js";
|
|
40
|
+
import type { CodeLayerSource } from "../shared/code-layer.js";
|
|
41
|
+
import {
|
|
42
|
+
entriesForComponent,
|
|
43
|
+
scanComponentLibrary,
|
|
44
|
+
} from "../shared/component-library.js";
|
|
45
|
+
import {
|
|
46
|
+
componentNameFor,
|
|
47
|
+
componentNodeIdMatches,
|
|
48
|
+
} from "../shared/component-model.js";
|
|
49
|
+
import { designSourceTypeFromData } from "../shared/source-mode.js";
|
|
50
|
+
|
|
51
|
+
async function liveContent(
|
|
52
|
+
fileId: string,
|
|
53
|
+
storedContent: string,
|
|
54
|
+
): Promise<string> {
|
|
55
|
+
try {
|
|
56
|
+
if (await hasCollabState(fileId)) {
|
|
57
|
+
const live = await getText(fileId, "content");
|
|
58
|
+
if (typeof live === "string") return live;
|
|
59
|
+
}
|
|
60
|
+
} catch {
|
|
61
|
+
// Collab reads are best-effort; SQL content is the fallback.
|
|
62
|
+
}
|
|
63
|
+
return storedContent;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export default defineAction({
|
|
67
|
+
description:
|
|
68
|
+
"Resolve the 'main' instance of a component (Figma's Go to main " +
|
|
69
|
+
"component). This codebase has no separate component-definition markup " +
|
|
70
|
+
"— components are structurally duplicated HTML matched by name — so the " +
|
|
71
|
+
"earliest instance found across the design's files stands in for the " +
|
|
72
|
+
"main component. Returns isMain=true when the given instance already IS " +
|
|
73
|
+
"that earliest one; otherwise navigates the editor to it (which may " +
|
|
74
|
+
"switch design files).",
|
|
75
|
+
schema: z.object({
|
|
76
|
+
designId: z.string().describe("Design project ID"),
|
|
77
|
+
nodeId: z
|
|
78
|
+
.string()
|
|
79
|
+
.describe("data-agent-native-node-id of the component instance root"),
|
|
80
|
+
fileId: z
|
|
81
|
+
.string()
|
|
82
|
+
.optional()
|
|
83
|
+
.describe(
|
|
84
|
+
"Design file id the instance currently lives in; defaults to index.html",
|
|
85
|
+
),
|
|
86
|
+
}),
|
|
87
|
+
run: async ({ designId, nodeId, fileId }) => {
|
|
88
|
+
const access = await resolveAccess("design", designId);
|
|
89
|
+
if (!access) throw new Error("Design not found");
|
|
90
|
+
|
|
91
|
+
const rawData = (access.resource as { data?: unknown }).data;
|
|
92
|
+
const sourceType = designSourceTypeFromData(rawData);
|
|
93
|
+
|
|
94
|
+
if (sourceType !== "inline") {
|
|
95
|
+
return {
|
|
96
|
+
designId,
|
|
97
|
+
nodeId,
|
|
98
|
+
sourceType,
|
|
99
|
+
ctaRequired: true,
|
|
100
|
+
ctaMessage:
|
|
101
|
+
"Go to main component requires a connected Builder app for " +
|
|
102
|
+
"real-app sources. Not yet available.",
|
|
103
|
+
isMain: false,
|
|
104
|
+
navigated: false,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const db = getDb();
|
|
109
|
+
|
|
110
|
+
// ── Resolve the current node + component name ──────────────────────────
|
|
111
|
+
const conditions = [
|
|
112
|
+
accessFilter(schema.designs, schema.designShares),
|
|
113
|
+
eq(schema.designFiles.designId, designId),
|
|
114
|
+
fileId
|
|
115
|
+
? eq(schema.designFiles.id, fileId)
|
|
116
|
+
: eq(schema.designFiles.filename, "index.html"),
|
|
117
|
+
];
|
|
118
|
+
|
|
119
|
+
const [file] = await db
|
|
120
|
+
.select({
|
|
121
|
+
id: schema.designFiles.id,
|
|
122
|
+
designId: schema.designFiles.designId,
|
|
123
|
+
filename: schema.designFiles.filename,
|
|
124
|
+
content: schema.designFiles.content,
|
|
125
|
+
})
|
|
126
|
+
.from(schema.designFiles)
|
|
127
|
+
.innerJoin(
|
|
128
|
+
schema.designs,
|
|
129
|
+
eq(schema.designFiles.designId, schema.designs.id),
|
|
130
|
+
)
|
|
131
|
+
.where(and(...conditions))
|
|
132
|
+
.limit(1);
|
|
133
|
+
|
|
134
|
+
if (!file) throw new Error("Design HTML file not found.");
|
|
135
|
+
|
|
136
|
+
const currentHtml = await liveContent(file.id, file.content ?? "");
|
|
137
|
+
const codeLayerSource: CodeLayerSource = {
|
|
138
|
+
kind: "design-file",
|
|
139
|
+
designId: file.designId,
|
|
140
|
+
fileId: file.id,
|
|
141
|
+
filename: file.filename,
|
|
142
|
+
};
|
|
143
|
+
const currentProjection = buildCodeLayerProjection(currentHtml, {
|
|
144
|
+
source: codeLayerSource,
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
const node = currentProjection.nodes.find((n) =>
|
|
148
|
+
componentNodeIdMatches(n, nodeId),
|
|
149
|
+
);
|
|
150
|
+
if (!node) {
|
|
151
|
+
throw new Error(
|
|
152
|
+
`Node "${nodeId}" not found. Run get-code-layer-projection to list current ids.`,
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const componentName = componentNameFor(node);
|
|
157
|
+
if (!componentName) {
|
|
158
|
+
throw new Error(
|
|
159
|
+
`Node "${nodeId}" is not a component root (no data-agent-native-component attribute).`,
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// ── Scan every design file for instances of this component ─────────────
|
|
164
|
+
const allFiles = await db
|
|
165
|
+
.select({
|
|
166
|
+
id: schema.designFiles.id,
|
|
167
|
+
designId: schema.designFiles.designId,
|
|
168
|
+
filename: schema.designFiles.filename,
|
|
169
|
+
content: schema.designFiles.content,
|
|
170
|
+
})
|
|
171
|
+
.from(schema.designFiles)
|
|
172
|
+
.innerJoin(
|
|
173
|
+
schema.designs,
|
|
174
|
+
eq(schema.designFiles.designId, schema.designs.id),
|
|
175
|
+
)
|
|
176
|
+
.where(
|
|
177
|
+
and(
|
|
178
|
+
accessFilter(schema.designs, schema.designShares),
|
|
179
|
+
eq(schema.designFiles.designId, designId),
|
|
180
|
+
eq(schema.designFiles.fileType, "html"),
|
|
181
|
+
),
|
|
182
|
+
)
|
|
183
|
+
.orderBy(schema.designFiles.createdAt);
|
|
184
|
+
|
|
185
|
+
// Use the freshest content we already read for the current file so the
|
|
186
|
+
// instance we just resolved (possibly from a live collab doc) matches up
|
|
187
|
+
// with `node.id` exactly.
|
|
188
|
+
const filesForScan = allFiles.map((row) =>
|
|
189
|
+
row.id === file.id ? { ...row, content: currentHtml } : row,
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
const entries = scanComponentLibrary(filesForScan);
|
|
193
|
+
const matches = entriesForComponent(entries, componentName);
|
|
194
|
+
|
|
195
|
+
if (matches.length === 0) {
|
|
196
|
+
// Shouldn't happen (the current node itself matches), but guard anyway.
|
|
197
|
+
throw new Error(
|
|
198
|
+
`No instances of component "${componentName}" found across the design's files.`,
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const main = matches[0];
|
|
203
|
+
// Compare against the caller's own stable `nodeId` param (a durable
|
|
204
|
+
// data-agent-native-node-id), not `node.id` (an ephemeral id scoped to
|
|
205
|
+
// this projection call) — `scanComponentLibrary` resolves the same
|
|
206
|
+
// durable id for every entry, so this is an apples-to-apples comparison.
|
|
207
|
+
const isMain = main.fileId === file.id && main.nodeId === nodeId;
|
|
208
|
+
|
|
209
|
+
if (isMain) {
|
|
210
|
+
return {
|
|
211
|
+
designId,
|
|
212
|
+
nodeId,
|
|
213
|
+
componentName,
|
|
214
|
+
sourceType,
|
|
215
|
+
ctaRequired: false,
|
|
216
|
+
isMain: true,
|
|
217
|
+
instanceCount: matches.length,
|
|
218
|
+
navigated: false,
|
|
219
|
+
note:
|
|
220
|
+
matches.length === 1
|
|
221
|
+
? `"${componentName}" has only one instance — this is it.`
|
|
222
|
+
: `This is the earliest instance of "${componentName}" across the design.`,
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
await writeAppState("navigate", {
|
|
227
|
+
view: "editor",
|
|
228
|
+
designId,
|
|
229
|
+
editorView: "single",
|
|
230
|
+
fileId: main.fileId,
|
|
231
|
+
filename: main.filename,
|
|
232
|
+
selectedNodeId: main.nodeId,
|
|
233
|
+
inspectorTab: "design",
|
|
234
|
+
inspectorSection: "component",
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
return {
|
|
238
|
+
designId,
|
|
239
|
+
nodeId,
|
|
240
|
+
componentName,
|
|
241
|
+
sourceType,
|
|
242
|
+
ctaRequired: false,
|
|
243
|
+
isMain: false,
|
|
244
|
+
instanceCount: matches.length,
|
|
245
|
+
main: {
|
|
246
|
+
fileId: main.fileId,
|
|
247
|
+
filename: main.filename,
|
|
248
|
+
nodeId: main.nodeId,
|
|
249
|
+
selector: main.selector,
|
|
250
|
+
},
|
|
251
|
+
navigated: true,
|
|
252
|
+
};
|
|
253
|
+
},
|
|
254
|
+
});
|
|
@@ -19,7 +19,7 @@ export default defineAction({
|
|
|
19
19
|
"for a specific design + localhost connection. The grant scopes writes to the " +
|
|
20
20
|
"connection's rootPath and expires after 8 hours. Requires editor access on the design. " +
|
|
21
21
|
"The LocalhostWriteConsentDialog calls this after the user clicks 'Allow writes'.",
|
|
22
|
-
// This action
|
|
22
|
+
// This action persists the real bridgeToken that unlocks the local
|
|
23
23
|
// bridge's unrestricted /read-file, /write-file, and /apply-edit. It must
|
|
24
24
|
// only ever be triggered by a human clicking "Allow writes" in
|
|
25
25
|
// LocalhostWriteConsentDialog — never by the agent itself, or an agent
|
|
@@ -27,7 +27,9 @@ export default defineAction({
|
|
|
27
27
|
// human consent model entirely. `agentTool: false` hides it from every
|
|
28
28
|
// agent tool surface (in-app assistant, MCP, A2A) while keeping it
|
|
29
29
|
// callable from the frontend via `callAction` / `useActionMutation` and
|
|
30
|
-
// the raw `/_agent-native/actions/grant-localhost-write-consent` route.
|
|
30
|
+
// the raw `/_agent-native/actions/grant-localhost-write-consent` route. The
|
|
31
|
+
// token intentionally stays server-side: browser callers only need grant
|
|
32
|
+
// metadata because write-local-file adds bridge authentication itself.
|
|
31
33
|
agentTool: false,
|
|
32
34
|
schema: z.object({
|
|
33
35
|
designId: z.string().describe("Design ID."),
|
|
@@ -124,7 +126,6 @@ export default defineAction({
|
|
|
124
126
|
|
|
125
127
|
return {
|
|
126
128
|
grantId: existing?.id ?? grantId,
|
|
127
|
-
bridgeToken,
|
|
128
129
|
rootPath,
|
|
129
130
|
grantedUntil,
|
|
130
131
|
};
|
|
@@ -2,12 +2,12 @@ import { defineAction } from "@agent-native/core";
|
|
|
2
2
|
import { assertAccess } from "@agent-native/core/sharing";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
|
|
5
|
+
import { saveFigmaPasteHtmlFallback } from "../server/lib/figma-paste-fallback.js";
|
|
5
6
|
import {
|
|
6
7
|
normalizeImportedHtmlDocument,
|
|
7
8
|
resolveImportDesignId,
|
|
8
9
|
saveImportedDesignFiles,
|
|
9
10
|
} from "../server/lib/import-design-files.js";
|
|
10
|
-
import { parseVisibleClipboardHtml } from "../server/lib/visible-clipboard-html.js";
|
|
11
11
|
|
|
12
12
|
const MAX_HTML_IMPORT_BYTES = 2 * 1024 * 1024;
|
|
13
13
|
|
|
@@ -62,38 +62,11 @@ export default defineAction({
|
|
|
62
62
|
};
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
if (!parsed.fallbackHtml) {
|
|
67
|
-
throw new Error(
|
|
68
|
-
"No visible HTML was found in the clipboard. Copy a frame, then paste into the canvas.",
|
|
69
|
-
);
|
|
70
|
-
}
|
|
71
|
-
const saved = await saveImportedDesignFiles({
|
|
65
|
+
return saveFigmaPasteHtmlFallback({
|
|
72
66
|
designId: resolvedDesignId,
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
{
|
|
76
|
-
filename: baseFilename(originalName, "figma-paste"),
|
|
77
|
-
fileType: "html",
|
|
78
|
-
content: normalizeImportedHtmlDocument(
|
|
79
|
-
parsed.fallbackHtml,
|
|
80
|
-
"visible clipboard HTML",
|
|
81
|
-
),
|
|
82
|
-
source: {
|
|
83
|
-
sourceType: "figma-paste-html",
|
|
84
|
-
},
|
|
85
|
-
},
|
|
86
|
-
],
|
|
67
|
+
clipboardHtml: content,
|
|
68
|
+
originalName,
|
|
87
69
|
});
|
|
88
|
-
return {
|
|
89
|
-
...saved,
|
|
90
|
-
stats: {
|
|
91
|
-
sourceKind: "figma-paste",
|
|
92
|
-
format: "html",
|
|
93
|
-
frameCount: saved.files.length,
|
|
94
|
-
imageCount: 0,
|
|
95
|
-
},
|
|
96
|
-
};
|
|
97
70
|
},
|
|
98
71
|
link: ({ result }) => {
|
|
99
72
|
if (!result || typeof result !== "object") return null;
|