@agent-native/core 0.92.9 → 0.92.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +29 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/production-agent.ts +181 -15
- package/corpus/core/src/agent/run-manager.ts +31 -4
- package/corpus/core/src/agent/run-store.ts +80 -0
- package/corpus/core/src/cli/design-connect.ts +433 -151
- package/corpus/core/src/cli/skills.ts +14 -0
- package/corpus/core/src/client/AssistantChat.tsx +25 -0
- package/corpus/core/src/client/MultiTabAssistantChat.tsx +40 -6
- package/corpus/core/src/client/agent-chat.ts +165 -1
- package/corpus/core/src/client/chat/index.ts +9 -1
- package/corpus/core/src/client/extensions/EmbeddedExtension.tsx +38 -2
- package/corpus/core/src/client/index.ts +5 -0
- package/corpus/core/src/client/rich-markdown-editor/useCollabReconcile.ts +18 -7
- package/corpus/core/src/collab/awareness-store.ts +23 -12
- package/corpus/core/src/collab/client.ts +16 -5
- package/corpus/core/src/collab/index.ts +4 -0
- package/corpus/core/src/collab/recent-edits.ts +65 -2
- package/corpus/core/src/deploy/build.ts +133 -5
- package/corpus/core/src/index.browser.ts +1 -0
- package/corpus/core/src/server/agent-chat-plugin.ts +98 -11
- package/corpus/core/src/server/builder-design-systems.ts +30 -4
- package/corpus/templates/clips/.agents/skills/ai-video-tools/SKILL.md +1 -1
- package/corpus/templates/clips/.agents/skills/video-sharing/SKILL.md +12 -6
- package/corpus/templates/clips/AGENTS.md +4 -0
- package/corpus/templates/clips/actions/create-recording.ts +2 -0
- package/corpus/templates/clips/actions/lib/create-recording-schema.ts +5 -3
- package/corpus/templates/clips/actions/request-transcript.ts +102 -33
- package/corpus/templates/clips/app/components/library/library-grid.tsx +1 -0
- package/corpus/templates/clips/app/components/player/share-dialog.tsx +59 -19
- package/corpus/templates/clips/app/components/player/transcript-panel.tsx +48 -4
- package/corpus/templates/clips/app/components/player/video-player.tsx +17 -12
- package/corpus/templates/clips/app/i18n/ar-SA.ts +1 -0
- package/corpus/templates/clips/app/i18n/de-DE.ts +1 -0
- package/corpus/templates/clips/app/i18n/en-US.ts +1 -0
- package/corpus/templates/clips/app/i18n/es-ES.ts +1 -0
- package/corpus/templates/clips/app/i18n/fr-FR.ts +1 -0
- package/corpus/templates/clips/app/i18n/hi-IN.ts +1 -0
- package/corpus/templates/clips/app/i18n/ja-JP.ts +1 -0
- package/corpus/templates/clips/app/i18n/ko-KR.ts +1 -0
- package/corpus/templates/clips/app/i18n/pt-BR.ts +1 -0
- package/corpus/templates/clips/app/i18n/zh-CN.ts +1 -0
- package/corpus/templates/clips/app/i18n/zh-TW.ts +1 -0
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +70 -37
- package/corpus/templates/clips/app/routes/record.tsx +17 -4
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +2 -0
- package/corpus/templates/clips/changelog/2026-07-09-desktop-popover-sits-closer-to-the-menu-bar.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-09-desktop-recording-optimization-progress-stays-visible-until-the-clip-opens.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-09-fixed-large-video-file-uploads-failing-before-their-resumabl.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-09-fixed-new-video-uploads-defaulting-to-private.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-09-long-recordings-now-get-enough-time-to-download-their-media-.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-09-meeting-notes-no-longer-lowers-your-microphone-volume-for-ot.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-09-on-mobile-tapping-a-playing-clip-reveals-playback-controls-w.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-09-regenerate-any-clip-transcript-from-its-saved-recording-or-r.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-09-slack-previews-now-show-the-clip-length-alongside-its-descri.md +6 -0
- package/corpus/templates/clips/desktop/src/app.tsx +12 -2
- package/corpus/templates/clips/desktop/src/hooks/useMeetingTranscription.ts +8 -1
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +8 -5
- package/corpus/templates/clips/desktop/src/lib/transcription-engine.ts +5 -5
- package/corpus/templates/clips/desktop/src/lib/voice-dictation.ts +3 -0
- package/corpus/templates/clips/desktop/src/styles.css +6 -3
- package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +29 -11
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +1 -1
- package/corpus/templates/clips/desktop/src-tauri/src/system_audio.rs +103 -21
- package/corpus/templates/clips/desktop/src-tauri/src/whisper_speech.rs +117 -39
- package/corpus/templates/clips/server/lib/slack-unfurls.ts +20 -1
- package/corpus/templates/clips/server/lib/streaming-upload-mode.ts +10 -4
- package/corpus/templates/clips/shared/recording-core.ts +12 -0
- package/corpus/templates/content/AGENTS.md +7 -6
- package/corpus/templates/content/actions/_document-flush.ts +132 -17
- package/corpus/templates/content/actions/_notion-action-utils.ts +15 -0
- package/corpus/templates/content/actions/create-and-link-notion-page.ts +2 -0
- package/corpus/templates/content/actions/link-notion-page.ts +2 -0
- package/corpus/templates/content/actions/pull-document.ts +5 -7
- package/corpus/templates/content/actions/pull-notion-page.ts +2 -0
- package/corpus/templates/content/actions/push-notion-page.ts +10 -0
- package/corpus/templates/content/actions/resolve-notion-sync-conflict.ts +2 -0
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +88 -23
- package/corpus/templates/content/app/components/editor/VisualEditor.tsx +32 -2
- package/corpus/templates/content/app/hooks/use-notion.ts +6 -6
- package/corpus/templates/content/app/i18n/zh-TW.ts +1 -0
- package/corpus/templates/content/app/i18n-data.ts +18 -0
- package/corpus/templates/content/changelog/2026-07-09-notion-conflict-choices-and-version-restores-preserve-docume.md +6 -0
- package/corpus/templates/content/server/lib/notion-sync.ts +80 -64
- package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +29 -2
- package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +86 -0
- package/corpus/templates/design/.agents/skills/export-handoff/SKILL.md +57 -0
- package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +11 -0
- package/corpus/templates/design/AGENTS.md +45 -10
- package/corpus/templates/design/actions/add-localhost-screens.ts +66 -13
- package/corpus/templates/design/actions/apply-component-prop-edit.ts +45 -36
- package/corpus/templates/design/actions/apply-motion-edit.ts +25 -31
- package/corpus/templates/design/actions/apply-shader-fill.ts +29 -42
- package/corpus/templates/design/actions/detach-component-instance.ts +337 -0
- package/corpus/templates/design/actions/export-design-as-figma-svg.ts +219 -0
- package/corpus/templates/design/actions/generate-design.ts +79 -12
- package/corpus/templates/design/actions/generate-screens.ts +57 -12
- package/corpus/templates/design/actions/get-figma-styles.ts +115 -0
- package/corpus/templates/design/actions/go-to-main-component.ts +254 -0
- package/corpus/templates/design/actions/grant-localhost-write-consent.ts +4 -3
- package/corpus/templates/design/actions/import-design-source.ts +4 -31
- package/corpus/templates/design/actions/import-figma-clipboard.ts +134 -0
- package/corpus/templates/design/actions/import-figma-frame.ts +95 -0
- package/corpus/templates/design/actions/index-design-system-with-builder.ts +16 -1
- package/corpus/templates/design/actions/list-design-components.ts +104 -0
- package/corpus/templates/design/actions/list-figma-library-assets.ts +1 -24
- package/corpus/templates/design/actions/open-component-source.ts +2 -4
- package/corpus/templates/design/actions/present-design-variants.ts +412 -16
- package/corpus/templates/design/actions/preview-source-edit.ts +14 -1
- package/corpus/templates/design/actions/run-design-audit.ts +25 -3
- package/corpus/templates/design/actions/show-design-questions.ts +10 -0
- package/corpus/templates/design/actions/swap-component-instance.ts +544 -0
- package/corpus/templates/design/actions/take-design-screenshot.ts +45 -60
- package/corpus/templates/design/actions/write-local-file.ts +25 -1
- package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +152 -0
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +1077 -58
- package/corpus/templates/design/app/components/design/DesignEditorSkeleton.tsx +5 -5
- package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +73 -21
- package/corpus/templates/design/app/components/design/EditPanel.tsx +16 -11
- package/corpus/templates/design/app/components/design/KeyboardShortcutsPanel.tsx +364 -0
- package/corpus/templates/design/app/components/design/LayersPanel.tsx +180 -53
- package/corpus/templates/design/app/components/design/LocalhostWriteConsentDialog.tsx +0 -2
- package/corpus/templates/design/app/components/design/MotionDock.tsx +144 -21
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +1152 -167
- package/corpus/templates/design/app/components/design/QuestionFlow.tsx +27 -5
- package/corpus/templates/design/app/components/design/ReviewPanel.tsx +23 -2
- package/corpus/templates/design/app/components/design/TokensPanel.tsx +63 -49
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +1293 -54
- package/corpus/templates/design/app/components/design/bridge/embedded-wheel.bridge.ts +7 -0
- package/corpus/templates/design/app/components/design/bridge/hit-test.bridge.ts +6 -1
- package/corpus/templates/design/app/components/design/bridge/motion-preview.bridge.ts +23 -6
- package/corpus/templates/design/app/components/design/bridge/sample.bridge.ts +7 -0
- package/corpus/templates/design/app/components/design/code-workbench/CodeWorkbench.tsx +5 -0
- package/corpus/templates/design/app/components/design/code-workbench/editor/MonacoHost.tsx +14 -2
- package/corpus/templates/design/app/components/design/code-workbench/explorer/FileTree.tsx +37 -2
- package/corpus/templates/design/app/components/design/code-workbench/format/format-on-open-guard.ts +25 -0
- package/corpus/templates/design/app/components/design/code-workbench/format/format-on-open.ts +19 -1
- package/corpus/templates/design/app/components/design/code-workbench/model-registry.ts +43 -5
- package/corpus/templates/design/app/components/design/code-workbench/search/SearchView.tsx +27 -2
- package/corpus/templates/design/app/components/design/code-workbench/search/search-engine.ts +10 -1
- package/corpus/templates/design/app/components/design/code-workbench/store.tsx +19 -2
- package/corpus/templates/design/app/components/design/design-canvas/annotation-snapshot.ts +265 -0
- package/corpus/templates/design/app/components/design/design-canvas/annotation-submit.ts +16 -5
- package/corpus/templates/design/app/components/design/design-canvas/coordinate-transforms.ts +16 -2
- package/corpus/templates/design/app/components/design/design-canvas/embedded-frame.ts +7 -2
- package/corpus/templates/design/app/components/design/design-canvas/iframe-events.ts +3 -1
- package/corpus/templates/design/app/components/design/edit-panel/code-inspect-helpers.tsx +78 -8
- package/corpus/templates/design/app/components/design/edit-panel/component-section.tsx +451 -73
- package/corpus/templates/design/app/components/design/edit-panel/document-colors.ts +12 -4
- package/corpus/templates/design/app/components/design/edit-panel/effects-properties.tsx +398 -272
- package/corpus/templates/design/app/components/design/edit-panel/element-classification.ts +41 -7
- package/corpus/templates/design/app/components/design/edit-panel/element-identity.ts +16 -3
- package/corpus/templates/design/app/components/design/edit-panel/field-primitives.tsx +24 -4
- package/corpus/templates/design/app/components/design/edit-panel/fill-gradient-helpers.ts +222 -4
- package/corpus/templates/design/app/components/design/edit-panel/fill-properties.tsx +171 -78
- package/corpus/templates/design/app/components/design/edit-panel/inspector-controls.tsx +45 -5
- package/corpus/templates/design/app/components/design/edit-panel/layout-properties.tsx +299 -28
- package/corpus/templates/design/app/components/design/edit-panel/panel-primitives.tsx +120 -150
- package/corpus/templates/design/app/components/design/edit-panel/position-helpers.ts +53 -1
- package/corpus/templates/design/app/components/design/edit-panel/position-layout-properties.tsx +284 -117
- package/corpus/templates/design/app/components/design/edit-panel/selection-helpers.ts +81 -0
- package/corpus/templates/design/app/components/design/edit-panel/stroke-properties.tsx +19 -14
- package/corpus/templates/design/app/components/design/edit-panel/style-options.ts +7 -0
- package/corpus/templates/design/app/components/design/edit-panel/typography-helpers.ts +155 -0
- package/corpus/templates/design/app/components/design/edit-panel/typography-properties.tsx +268 -46
- package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +350 -31
- package/corpus/templates/design/app/components/design/inspector/ConstraintsWidget.tsx +13 -4
- package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +233 -23
- package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +72 -2
- package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +51 -11
- package/corpus/templates/design/app/components/design/inspector/ShaderFillsPanel.tsx +84 -11
- package/corpus/templates/design/app/components/design/inspector/index.ts +3 -0
- package/corpus/templates/design/app/components/design/keyboard-shortcuts.ts +552 -0
- package/corpus/templates/design/app/components/design/multi-screen/board-surface-html.ts +157 -0
- package/corpus/templates/design/app/components/design/multi-screen/frame-geometry.ts +53 -0
- package/corpus/templates/design/app/components/design/multi-screen/overview-layout.ts +184 -0
- package/corpus/templates/design/app/components/design/multi-screen/primitive-drop-target.ts +403 -26
- package/corpus/templates/design/app/components/design/multi-screen/types.ts +20 -1
- package/corpus/templates/design/app/components/design/types.ts +33 -0
- package/corpus/templates/design/app/components/editor/PromptDialog.tsx +59 -7
- package/corpus/templates/design/app/components/visual-editor/CanvasCommentPins.tsx +119 -17
- package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +275 -63
- package/corpus/templates/design/app/hooks/use-question-flow.ts +14 -3
- package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +67 -10
- package/corpus/templates/design/app/i18n/zh-TW.ts +91 -1
- package/corpus/templates/design/app/i18n-data.ts +1217 -10
- package/corpus/templates/design/app/i18n-keyboard-shortcuts.ts +577 -0
- package/corpus/templates/design/app/lib/agent-chat.ts +23 -0
- package/corpus/templates/design/app/lib/design-import.ts +24 -0
- package/corpus/templates/design/app/lib/figma-clipboard.ts +157 -0
- package/corpus/templates/design/app/lib/figma-svg-copy.ts +221 -0
- package/corpus/templates/design/app/pages/design-editor/code-layer-state.ts +143 -30
- package/corpus/templates/design/app/pages/design-editor/collab-sync.ts +15 -0
- package/corpus/templates/design/app/pages/design-editor/editor-session.ts +33 -0
- package/corpus/templates/design/app/pages/design-editor/editor-state.ts +38 -0
- package/corpus/templates/design/app/pages/design-editor/overview-annotation-context.ts +157 -0
- package/corpus/templates/design/app/pages/design-editor/overview-camera.ts +62 -6
- package/corpus/templates/design/app/pages/design-editor/pending-edits.ts +245 -0
- package/corpus/templates/design/app/pages/design-editor/react-semantic-handoff.ts +586 -0
- package/corpus/templates/design/app/pages/design-editor/screen-auto-layout.ts +169 -0
- package/corpus/templates/design/app/pages/design-editor/selection-state.ts +201 -1
- package/corpus/templates/design/changelog/2026-07-09-added-underline-strikethrough-and-text-case-uppercase-lowerc.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-annotate-now-draws-across-the-all-screens-canvas-without-swi.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-auto-layout-children-can-now-be-dragged-into-freeform-screen.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-board-shapes-now-stay-visible-and-nest-correctly-across-the-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-component-source-navigation-now-opens-reliably-from-the-insp.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-cross-screen-local-react-layer-moves-now-hand-off-exact-sour.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-cross-screen-moves-undo-and-redo-now-keep-both-screens-stabl.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-detach-a-component-instance-into-plain-editable-eleme.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-dragging-layers-into-auto-layout-now-respects-ignore-auto-la.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-drawing-annotations-sent-to-the-agent-now-include-a-rendered.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-editor-sidebars-now-use-figma-matched-default-widths-for-a-r.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-empty-frames-and-rectangles-now-expose-full-auto-layout-cont.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-export-any-design-screen-as-a-real-vector-svg-for-figma-with.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-grid-auto-layout-now-uses-true-responsive-rows-and-columns-w.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-hidden-and-locked-screens-now-behave-consistently-between-la.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-image-fills-now-preserve-valid-urls-and-layered-fit-settings.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-import-figma-frames-as-pixel-accurate-design-screens-via-a-s.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-inspect-code-now-hides-internal-styling-metadata-and-wraps-l.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-inspector-blur-and-shadow-effects-now-preserve-other-effects.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-jump-to-a-components-other-instances-from-the-inspecto.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-keyboard-shortcuts-are-now-discoverable-in-a-figma-style-bot.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-layer-selection-now-keeps-cmd-or-ctrl-toggles-and-shift-rang.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-layers-dropped-into-auto-layout-now-participate-correctly-in.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-local-live-edit-screens-now-enter-and-leave-full-view-withou.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-local-preview-urls-now-live-in-the-inspector-instead-of-taki.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-local-react-layers-can-now-be-hidden-and-locked-from-layers-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-local-react-layers-now-keep-exact-source-provenance-and-supp.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-local-react-visual-editing-now-loads-reliably-and-shows-live.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-paste-from-figma-cmd-v-now-imports-exact-editable-nodes-when.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-pressing-escape-after-selecting-a-screen-now-reveals-page-pr.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-right-click-overlapping-or-nested-objects-to-choose-the-exac.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-scale-edge-stretch-and-center-constraints-now-preserve-layer.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-screen-rows-can-now-be-reordered-in-layers-with-matching-can.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-screen-selection-stays-responsive-in-designs-with-hundreds-o.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-shift-a-now-enables-auto-layout-on-a-selected-screen-with-fl.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-swap-a-component-instance-for-another-from-the-libra.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-drawing-annotations-now-stay-available-to-retry-when-the.md +6 -0
- package/corpus/templates/design/e2e/global-setup.ts +3 -6
- package/corpus/templates/design/e2e/helpers.ts +27 -6
- package/corpus/templates/design/server/lib/design-to-figma-svg.ts +1412 -0
- package/corpus/templates/design/server/lib/figma-clipboard-match.ts +196 -0
- package/corpus/templates/design/server/lib/figma-node-import.ts +311 -0
- package/corpus/templates/design/server/lib/figma-node-to-html.ts +1334 -0
- package/corpus/templates/design/server/lib/figma-paste-fallback.ts +64 -0
- package/corpus/templates/design/server/lib/playwright-runtime.ts +78 -0
- package/corpus/templates/design/server/plugins/db.ts +41 -0
- package/corpus/templates/design/server/source-workspace.ts +64 -0
- package/corpus/templates/design/shared/board-file.ts +13 -2
- package/corpus/templates/design/shared/canvas-math.ts +231 -33
- package/corpus/templates/design/shared/code-layer.ts +122 -8
- package/corpus/templates/design/shared/component-library.ts +161 -0
- package/corpus/templates/design/shared/db-conflict.ts +27 -0
- package/corpus/templates/design/shared/design-source-capabilities.ts +2 -1
- package/corpus/templates/design/shared/figma-url.ts +149 -0
- package/corpus/templates/design/shared/pen-path.ts +16 -7
- package/corpus/templates/design/shared/source-mode.ts +1 -0
- package/corpus/templates/plan/app/components/plan/CanvasArea.tsx +7 -19
- package/corpus/templates/plan/changelog/2026-07-09-canvas-panning-now-keeps-zoom-fixed-even-at-the-edges.md +6 -0
- package/corpus/templates/slides/actions/index-design-system-with-builder.ts +16 -1
- package/corpus/templates/slides/app/context/DeckContext.tsx +311 -70
- package/corpus/templates/slides/changelog/2026-07-09-fixed-the-slide-rail-going-permanently-stale-during-a-long-a.md +6 -0
- package/dist/agent/production-agent.d.ts +79 -6
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +161 -14
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +29 -5
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/agent/run-store.d.ts +46 -0
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +61 -0
- package/dist/agent/run-store.js.map +1 -1
- package/dist/cli/design-connect.d.ts +6 -0
- package/dist/cli/design-connect.d.ts.map +1 -1
- package/dist/cli/design-connect.js +336 -139
- package/dist/cli/design-connect.js.map +1 -1
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +14 -0
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +2 -0
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +19 -3
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/client/MultiTabAssistantChat.js +38 -18
- package/dist/client/MultiTabAssistantChat.js.map +1 -1
- package/dist/client/agent-chat.d.ts +60 -0
- package/dist/client/agent-chat.d.ts.map +1 -1
- package/dist/client/agent-chat.js +122 -1
- package/dist/client/agent-chat.js.map +1 -1
- package/dist/client/chat/index.d.ts +1 -1
- package/dist/client/chat/index.d.ts.map +1 -1
- package/dist/client/chat/index.js +1 -1
- package/dist/client/chat/index.js.map +1 -1
- package/dist/client/extensions/EmbeddedExtension.d.ts +12 -0
- package/dist/client/extensions/EmbeddedExtension.d.ts.map +1 -1
- package/dist/client/extensions/EmbeddedExtension.js +36 -2
- package/dist/client/extensions/EmbeddedExtension.js.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/useCollabReconcile.js +18 -6
- package/dist/client/rich-markdown-editor/useCollabReconcile.js.map +1 -1
- package/dist/collab/awareness-store.d.ts +5 -0
- package/dist/collab/awareness-store.d.ts.map +1 -1
- package/dist/collab/awareness-store.js +19 -12
- package/dist/collab/awareness-store.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/client.d.ts.map +1 -1
- package/dist/collab/client.js +13 -5
- package/dist/collab/client.js.map +1 -1
- package/dist/collab/index.d.ts +1 -0
- package/dist/collab/index.d.ts.map +1 -1
- package/dist/collab/index.js +1 -0
- package/dist/collab/index.js.map +1 -1
- package/dist/collab/recent-edits.d.ts +3 -1
- package/dist/collab/recent-edits.d.ts.map +1 -1
- package/dist/collab/recent-edits.js +57 -2
- package/dist/collab/recent-edits.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/deploy/build.d.ts +24 -0
- package/dist/deploy/build.d.ts.map +1 -1
- package/dist/deploy/build.js +102 -5
- package/dist/deploy/build.js.map +1 -1
- package/dist/index.browser.d.ts +1 -1
- package/dist/index.browser.d.ts.map +1 -1
- package/dist/index.browser.js +1 -1
- package/dist/index.browser.js.map +1 -1
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +5 -5
- package/dist/progress/routes.d.ts +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +87 -10
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/builder-design-systems.d.ts +15 -0
- package/dist/server/builder-design-systems.d.ts.map +1 -1
- package/dist/server/builder-design-systems.js +9 -4
- package/dist/server/builder-design-systems.js.map +1 -1
- package/package.json +1 -1
|
@@ -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;
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { defineAction } from "@agent-native/core";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
buildFigmaNodeCandidates,
|
|
6
|
+
extractVisibleTexts,
|
|
7
|
+
matchFigmaClipboardNodes,
|
|
8
|
+
} from "../server/lib/figma-clipboard-match.js";
|
|
9
|
+
import {
|
|
10
|
+
buildScreenFilesFromFigmaNodes,
|
|
11
|
+
fetchFileStructure,
|
|
12
|
+
fetchFigmaNodes,
|
|
13
|
+
summarizeFidelity,
|
|
14
|
+
} from "../server/lib/figma-node-import.js";
|
|
15
|
+
import { saveFigmaPasteHtmlFallback } from "../server/lib/figma-paste-fallback.js";
|
|
16
|
+
import { saveImportedDesignFiles } from "../server/lib/import-design-files.js";
|
|
17
|
+
import { parseVisibleClipboardHtml } from "../server/lib/visible-clipboard-html.js";
|
|
18
|
+
import { parseFigmaFileKey } from "../shared/figma-url.js";
|
|
19
|
+
|
|
20
|
+
const NODE_STRUCTURE_DEPTH = 3;
|
|
21
|
+
|
|
22
|
+
const CREDENTIAL_MISSING_RE = /credential not configured/i;
|
|
23
|
+
|
|
24
|
+
const AMBIGUOUS_GUIDANCE =
|
|
25
|
+
'Couldn\'t confidently match this paste to specific Figma nodes, so nothing was imported from the API. Paste a frame LINK instead (copy the frame in Figma, then "Copy link to selection") for an exact node import — or continue with the clipboard preview below.';
|
|
26
|
+
|
|
27
|
+
const KEY_MISSING_GUIDANCE =
|
|
28
|
+
"Connect your Figma access token (Settings > Secrets > Figma access token) to import this paste as exact, editable Figma nodes. Imported from the clipboard preview instead.";
|
|
29
|
+
|
|
30
|
+
export default defineAction({
|
|
31
|
+
description:
|
|
32
|
+
"Import a plain clipboard paste copied from Figma (Cmd+C in Figma, Cmd+V here). Figma's clipboard marker only carries a file key, not node ids, so this heuristically matches the pasted content against the file's top-level frames by name/text and imports exact REST nodes only on a confident match; otherwise it falls back to the lossy visible-HTML preview and reports why. For a guaranteed exact import, use import-figma-frame with a copied frame LINK (which does carry a node id) instead.",
|
|
33
|
+
schema: z.object({
|
|
34
|
+
designId: z
|
|
35
|
+
.string()
|
|
36
|
+
.optional()
|
|
37
|
+
.describe("Design id. Defaults to the active editor navigation state."),
|
|
38
|
+
figmetaFileKey: z
|
|
39
|
+
.string()
|
|
40
|
+
.trim()
|
|
41
|
+
.min(1)
|
|
42
|
+
.describe(
|
|
43
|
+
"The fileKey decoded from the clipboard's figmeta marker (see app/lib/figma-clipboard.ts's extractFigmeta).",
|
|
44
|
+
),
|
|
45
|
+
clipboardHtml: z
|
|
46
|
+
.string()
|
|
47
|
+
.describe(
|
|
48
|
+
"The raw clipboard HTML (still containing the hidden figmeta/figbuffer markers) — used both for the legacy visible-HTML fallback and for node-matching against visible text.",
|
|
49
|
+
),
|
|
50
|
+
originalName: z.string().optional(),
|
|
51
|
+
}),
|
|
52
|
+
run: async ({ designId, figmetaFileKey, clipboardHtml, originalName }) => {
|
|
53
|
+
const fileKey = parseFigmaFileKey(figmetaFileKey);
|
|
54
|
+
if (!fileKey) {
|
|
55
|
+
throw new Error("The clipboard's Figma file key could not be parsed.");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Legacy fallback text is needed either way: as the actual save payload
|
|
59
|
+
// when we fall back, and as the "visible text" signal the matcher scores
|
|
60
|
+
// candidate frames against.
|
|
61
|
+
const parsedClipboard = parseVisibleClipboardHtml(clipboardHtml);
|
|
62
|
+
if (!parsedClipboard.fallbackHtml) {
|
|
63
|
+
throw new Error(
|
|
64
|
+
"No visible HTML was found in the clipboard. Copy a frame, then paste into the canvas.",
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
const clipboardTexts = extractVisibleTexts(parsedClipboard.fallbackHtml);
|
|
68
|
+
|
|
69
|
+
let figmaApiKeyMissing = false;
|
|
70
|
+
let matchStatus: "matched" | "ambiguous" | "none" | "error" = "error";
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
const document = await fetchFileStructure(fileKey, NODE_STRUCTURE_DEPTH);
|
|
74
|
+
const candidates = buildFigmaNodeCandidates(document);
|
|
75
|
+
const matchResult = matchFigmaClipboardNodes(candidates, clipboardTexts);
|
|
76
|
+
matchStatus = matchResult.status;
|
|
77
|
+
|
|
78
|
+
if (matchResult.status === "matched") {
|
|
79
|
+
const nodeIds = matchResult.matches.map((match) => match.id);
|
|
80
|
+
const nodesById = await fetchFigmaNodes(fileKey, nodeIds);
|
|
81
|
+
const { files, fidelityEntries } = await buildScreenFilesFromFigmaNodes(
|
|
82
|
+
fileKey,
|
|
83
|
+
nodesById,
|
|
84
|
+
);
|
|
85
|
+
const saved = await saveImportedDesignFiles({
|
|
86
|
+
designId,
|
|
87
|
+
sourceType: "figma-clipboard-rest",
|
|
88
|
+
files,
|
|
89
|
+
});
|
|
90
|
+
return {
|
|
91
|
+
...saved,
|
|
92
|
+
strategy: "restNodes" as const,
|
|
93
|
+
figma: {
|
|
94
|
+
fileKey,
|
|
95
|
+
nodeIds,
|
|
96
|
+
matched: matchResult.matches,
|
|
97
|
+
},
|
|
98
|
+
fidelityReport: summarizeFidelity(fidelityEntries),
|
|
99
|
+
guidance:
|
|
100
|
+
"Review fidelityReport.imageFallbacks for subtrees rendered as PNG and fidelityReport.approximated for properties CSS cannot express exactly.",
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
} catch (error) {
|
|
104
|
+
figmaApiKeyMissing = CREDENTIAL_MISSING_RE.test(
|
|
105
|
+
error instanceof Error ? error.message : String(error),
|
|
106
|
+
);
|
|
107
|
+
if (!figmaApiKeyMissing) {
|
|
108
|
+
// A real (non-credential) REST failure — network error, revoked
|
|
109
|
+
// token, file access issue, etc. Still fall back to the honest
|
|
110
|
+
// clipboard preview rather than losing the paste entirely, but this
|
|
111
|
+
// isn't a "no confident match" case, so don't claim ambiguity.
|
|
112
|
+
matchStatus = "error";
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const saved = await saveFigmaPasteHtmlFallback({
|
|
117
|
+
designId,
|
|
118
|
+
clipboardHtml,
|
|
119
|
+
originalName,
|
|
120
|
+
});
|
|
121
|
+
return {
|
|
122
|
+
...saved,
|
|
123
|
+
strategy: "htmlFallback" as const,
|
|
124
|
+
figmaApiKeyMissing,
|
|
125
|
+
matchStatus,
|
|
126
|
+
figma: { fileKey },
|
|
127
|
+
guidance: figmaApiKeyMissing
|
|
128
|
+
? KEY_MISSING_GUIDANCE
|
|
129
|
+
: matchStatus === "ambiguous" || matchStatus === "none"
|
|
130
|
+
? AMBIGUOUS_GUIDANCE
|
|
131
|
+
: "Imported the clipboard's visible-HTML preview after a Figma API error. Paste a frame link for an exact import.",
|
|
132
|
+
};
|
|
133
|
+
},
|
|
134
|
+
});
|