@agent-native/core 0.90.1 → 0.90.2
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 +14 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/client/AgentPanel.tsx +1 -1
- package/corpus/core/src/client/rich-markdown-editor/useCollabReconcile.ts +11 -6
- package/corpus/core/src/client/use-action.ts +39 -2
- package/corpus/core/src/client/use-db-sync.ts +44 -18
- package/corpus/core/src/db/create-get-db.ts +32 -2
- package/corpus/core/src/server/framework-request-handler.ts +52 -4
- package/corpus/templates/assets/.agents/skills/asset-generation/SKILL.md +5 -0
- package/corpus/templates/assets/.agents/skills/logo-composite/SKILL.md +34 -6
- package/corpus/templates/assets/AGENTS.md +23 -4
- package/corpus/templates/assets/actions/_generation-preset-settings.ts +48 -0
- package/corpus/templates/assets/actions/_helpers.ts +8 -1
- package/corpus/templates/assets/actions/_preset-skeleton-validation.ts +64 -0
- package/corpus/templates/assets/actions/create-generation-preset.ts +11 -3
- package/corpus/templates/assets/actions/generate-image-batch.ts +7 -2
- package/corpus/templates/assets/actions/generate-image.ts +342 -25
- package/corpus/templates/assets/actions/get-library.ts +4 -3
- package/corpus/templates/assets/actions/navigate.ts +2 -1
- package/corpus/templates/assets/actions/refresh-generation-run.ts +5 -1
- package/corpus/templates/assets/actions/update-asset.ts +1 -0
- package/corpus/templates/assets/actions/update-generation-preset.ts +8 -1
- package/corpus/templates/assets/actions/view-screen.ts +8 -1
- package/corpus/templates/assets/app/components/generation/GenerationResults.tsx +9 -1
- package/corpus/templates/assets/app/hooks/use-image-model-menu.ts +1 -0
- package/corpus/templates/assets/app/hooks/use-navigation-state.ts +11 -0
- package/corpus/templates/assets/app/i18n/zh-TW.ts +64 -0
- package/corpus/templates/assets/app/i18n-data.ts +650 -0
- package/corpus/templates/assets/app/routes/asset.$id.tsx +2 -1
- package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +44 -33
- package/corpus/templates/assets/app/routes/brand-kits.$id_.presets.$presetId.tsx +1474 -0
- package/corpus/templates/assets/app/routes/library.tsx +43 -1
- package/corpus/templates/assets/server/lib/generation.ts +366 -44
- package/corpus/templates/assets/server/lib/image-processing.ts +252 -0
- package/corpus/templates/assets/server/lib/preset-skeleton.ts +137 -0
- package/corpus/templates/assets/shared/api.ts +47 -0
- package/corpus/templates/clips/app/components/library/library-layout.tsx +7 -2
- package/corpus/templates/clips/app/components/library/sort-menu.tsx +1 -1
- package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +114 -33
- package/corpus/templates/clips/app/routes/_app.library._index.tsx +27 -1
- package/corpus/templates/clips/app/routes/record.tsx +157 -68
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/abort.post.ts +5 -5
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +20 -3
- package/corpus/templates/clips/shared/finalize-recovery.ts +23 -3
- package/corpus/templates/content/AGENTS.md +52 -52
- package/corpus/templates/content/actions/_builder-cms-read-client.ts +188 -8
- package/corpus/templates/content/actions/_builder-docs-client.ts +41 -3
- package/corpus/templates/content/actions/_database-source-utils.ts +998 -160
- package/corpus/templates/content/actions/_database-utils.ts +44 -4
- package/corpus/templates/content/actions/_notion-action-utils.ts +6 -2
- package/corpus/templates/content/actions/add-content-database-source-field-property.ts +23 -1
- package/corpus/templates/content/actions/attach-content-database-source.ts +85 -12
- package/corpus/templates/content/actions/change-content-database-source-role.ts +9 -1
- package/corpus/templates/content/actions/delete-content-database.ts +1 -0
- package/corpus/templates/content/actions/get-document.ts +11 -2
- package/corpus/templates/content/actions/list-content-databases.ts +57 -9
- package/corpus/templates/content/actions/prepare-builder-source-review.ts +23 -6
- package/corpus/templates/content/actions/refresh-content-database-source.ts +26 -3
- package/corpus/templates/content/actions/restore-content-database.ts +1 -0
- package/corpus/templates/content/actions/update-document.ts +43 -0
- package/corpus/templates/content/app/components/editor/BuilderBodySyncingNotice.tsx +21 -0
- package/corpus/templates/content/app/components/editor/DocumentDatabase.tsx +0 -1
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +70 -28
- package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +69 -17
- package/corpus/templates/content/app/components/editor/VisualEditor.tsx +4 -4
- package/corpus/templates/content/app/components/editor/body-hydration.ts +112 -0
- package/corpus/templates/content/app/components/editor/builder-body-hydration-pump.ts +52 -0
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +9076 -2876
- package/corpus/templates/content/app/components/editor/database-sources/BuilderSourceReviewDialog.tsx +41 -1
- package/corpus/templates/content/app/components/editor/extensions/ImageBlock.tsx +36 -11
- package/corpus/templates/content/app/components/editor/previewDocumentSaveController.ts +21 -1
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +11 -1
- package/corpus/templates/content/app/components/sidebar/DocumentTreeItem.tsx +11 -8
- package/corpus/templates/content/app/global.css +41 -0
- package/corpus/templates/content/app/hooks/use-content-database.ts +346 -42
- package/corpus/templates/content/app/hooks/use-db-sync.ts +9 -0
- package/corpus/templates/content/app/hooks/use-documents.ts +47 -0
- package/corpus/templates/content/app/hooks/use-notion.ts +12 -10
- package/corpus/templates/content/app/i18n/zh-TW.ts +35 -0
- package/corpus/templates/content/app/i18n-data.ts +355 -0
- package/corpus/templates/content/app/root.tsx +22 -0
- package/corpus/templates/content/changelog/2026-07-01-builder-database-sources-keep-loading-rows-in-the-background.md +6 -0
- package/corpus/templates/content/changelog/2026-07-01-builder-source-attach-starts-with-usable-rows.md +6 -0
- package/corpus/templates/content/changelog/2026-07-01-builder-source-attachment-now-stays-connected-after-large.md +6 -0
- package/corpus/templates/content/changelog/2026-07-01-builder-source-refreshes-now-show-progress-for-large-docs-blog.md +6 -0
- package/corpus/templates/content/changelog/2026-07-01-database-pages-now-delete-to-trash-without-route-errors.md +6 -0
- package/corpus/templates/content/changelog/2026-07-02-builder-source-refreshes-recover-from-stalled-background-row.md +6 -0
- package/corpus/templates/content/changelog/2026-07-02-builder-sourced-rows-open-with-sync-protection.md +6 -0
- package/corpus/templates/content/changelog/2026-07-02-database-rows-and-pages-open-faster-from-recently-loaded-dat.md +6 -0
- package/corpus/templates/content/changelog/2026-07-02-preview-panels-close-reliably-narrow-sidebar-row-actions-sta.md +6 -0
- package/corpus/templates/content/changelog/2026-07-06-builder-sync-progress-no-longer-shows-continue.md +6 -0
- package/corpus/templates/content/shared/api.ts +13 -0
- package/corpus/templates/content/shared/builder-mdx.ts +16 -3
- package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +23 -13
- package/corpus/templates/design/.agents/skills/responsive-breakpoints/SKILL.md +98 -0
- package/corpus/templates/design/.agents/skills/shader-fills/SKILL.md +162 -0
- package/corpus/templates/design/AGENTS.md +9 -2
- package/corpus/templates/design/actions/add-breakpoint.ts +6 -3
- package/corpus/templates/design/actions/apply-a11y-fix.ts +57 -37
- package/corpus/templates/design/actions/apply-component-prop-edit.ts +41 -20
- package/corpus/templates/design/actions/apply-motion-edit.ts +251 -38
- package/corpus/templates/design/actions/apply-shader-fill.ts +90 -66
- package/corpus/templates/design/actions/apply-visual-edit.ts +282 -55
- package/corpus/templates/design/actions/create-component.ts +29 -60
- package/corpus/templates/design/actions/create-file.ts +9 -2
- package/corpus/templates/design/actions/duplicate-design.ts +31 -2
- package/corpus/templates/design/actions/edit-design.ts +36 -38
- package/corpus/templates/design/actions/generate-design-title.ts +87 -0
- package/corpus/templates/design/actions/generate-design.ts +53 -18
- package/corpus/templates/design/actions/generate-screens.ts +108 -4
- package/corpus/templates/design/actions/get-motion-timeline.ts +35 -7
- package/corpus/templates/design/actions/insert-asset.ts +34 -29
- package/corpus/templates/design/actions/insert-design-native-asset.ts +129 -32
- package/corpus/templates/design/actions/insert-figma-library-asset.ts +35 -30
- package/corpus/templates/design/actions/present-design-variants.ts +7 -2
- package/corpus/templates/design/actions/remove-motion-timeline.ts +31 -36
- package/corpus/templates/design/actions/update-file.ts +190 -75
- package/corpus/templates/design/app/components/design/BreakpointBar.tsx +403 -0
- package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +469 -222
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +708 -53
- package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +83 -3
- package/corpus/templates/design/app/components/design/EditPanel.tsx +1091 -6948
- package/corpus/templates/design/app/components/design/LayersPanel.tsx +419 -85
- package/corpus/templates/design/app/components/design/MotionDock.tsx +1124 -103
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +2610 -1398
- package/corpus/templates/design/app/components/design/ReadOnlyDesignBanner.tsx +22 -0
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +2182 -235
- package/corpus/templates/design/app/components/design/bridge/motion-preview.bridge.ts +187 -11
- package/corpus/templates/design/app/components/design/bridge/shader-fill-preview.bridge.ts +376 -1
- package/corpus/templates/design/app/components/design/bridge/shader-runtime.bridge.ts +746 -0
- package/corpus/templates/design/app/components/design/canvas-primitive-style.ts +17 -0
- package/corpus/templates/design/app/components/design/code-workbench/SideBar.tsx +1 -1
- package/corpus/templates/design/app/components/design/code-workbench/explorer/FileTree.tsx +1 -3
- package/corpus/templates/design/app/components/design/code-workbench/quickinput/QuickInput.tsx +1 -1
- package/corpus/templates/design/app/components/design/edit-panel/appearance-properties.tsx +518 -0
- package/corpus/templates/design/app/components/design/edit-panel/code-inspect-helpers.tsx +562 -0
- package/corpus/templates/design/app/components/design/edit-panel/component-section.tsx +763 -0
- package/corpus/templates/design/app/components/design/edit-panel/document-colors.ts +94 -0
- package/corpus/templates/design/app/components/design/edit-panel/effects-properties.tsx +780 -0
- package/corpus/templates/design/app/components/design/edit-panel/element-classification.ts +446 -0
- package/corpus/templates/design/app/components/design/edit-panel/element-identity.ts +79 -0
- package/corpus/templates/design/app/components/design/edit-panel/field-primitives.tsx +315 -0
- package/corpus/templates/design/app/components/design/edit-panel/fill-gradient-helpers.ts +344 -0
- package/corpus/templates/design/app/components/design/edit-panel/fill-properties.tsx +594 -0
- package/corpus/templates/design/app/components/design/edit-panel/frame-presets-panel.tsx +119 -0
- package/corpus/templates/design/app/components/design/edit-panel/inspector-controls.tsx +248 -0
- package/corpus/templates/design/app/components/design/edit-panel/interaction-state-helpers.ts +45 -0
- package/corpus/templates/design/app/components/design/edit-panel/layout-properties.tsx +693 -0
- package/corpus/templates/design/app/components/design/edit-panel/panel-primitives.tsx +724 -0
- package/corpus/templates/design/app/components/design/edit-panel/position-helpers.ts +207 -0
- package/corpus/templates/design/app/components/design/edit-panel/position-layout-properties.tsx +628 -0
- package/corpus/templates/design/app/components/design/edit-panel/selection-helpers.ts +91 -0
- package/corpus/templates/design/app/components/design/edit-panel/stroke-properties.tsx +649 -0
- package/corpus/templates/design/app/components/design/edit-panel/style-change-types.ts +118 -0
- package/corpus/templates/design/app/components/design/edit-panel/style-options.ts +88 -0
- package/corpus/templates/design/app/components/design/edit-panel/transform-helpers.ts +100 -0
- package/corpus/templates/design/app/components/design/edit-panel/typography-helpers.ts +117 -0
- package/corpus/templates/design/app/components/design/edit-panel/typography-properties.tsx +501 -0
- package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +91 -40
- package/corpus/templates/design/app/components/design/inspector/BreakpointOverrideIndicator.tsx +82 -0
- package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +189 -87
- package/corpus/templates/design/app/components/design/inspector/GlslShaderPanel.tsx +1097 -0
- package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +44 -0
- package/corpus/templates/design/app/components/design/inspector/InteractionStatePanel.tsx +258 -0
- package/corpus/templates/design/app/components/design/inspector/MotionKeyframeDiamond.tsx +144 -0
- package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +99 -9
- package/corpus/templates/design/app/components/design/inspector/frame-size-presets.ts +116 -0
- package/corpus/templates/design/app/components/design/inspector/index.ts +26 -0
- package/corpus/templates/design/app/components/design/inspector/scrub-input-utils.ts +29 -0
- package/corpus/templates/design/app/components/design/multi-screen/alt-hover-measurement.ts +171 -0
- package/corpus/templates/design/app/components/design/multi-screen/board-surface-html.ts +220 -0
- package/corpus/templates/design/app/components/design/multi-screen/chrome-transitions.ts +35 -0
- package/corpus/templates/design/app/components/design/multi-screen/cross-screen-drop.ts +134 -0
- package/corpus/templates/design/app/components/design/multi-screen/culling.ts +185 -0
- package/corpus/templates/design/app/components/design/multi-screen/gradient-overlay-geometry.ts +120 -0
- package/corpus/templates/design/app/components/design/multi-screen/types.ts +892 -0
- package/corpus/templates/design/app/components/design/multi-screen/vector-edit-geometry.ts +21 -0
- package/corpus/templates/design/app/components/design/types.ts +15 -0
- package/corpus/templates/design/app/components/editor/PromptDialog.tsx +109 -5
- package/corpus/templates/design/app/global.css +8 -8
- package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +259 -2
- package/corpus/templates/design/app/i18n/zh-TW.ts +163 -9
- package/corpus/templates/design/app/i18n-breakpoints.ts +205 -0
- package/corpus/templates/design/app/i18n-data.ts +1664 -152
- package/corpus/templates/design/app/i18n-motion.ts +563 -0
- package/corpus/templates/design/app/lib/design-import.ts +73 -0
- package/corpus/templates/design/app/pages/Index.tsx +41 -22
- package/corpus/templates/design/changelog/2026-06-29-design-inspector-controls-now-match-figmas-softer-dark-chrome.md +1 -1
- package/corpus/templates/design/changelog/2026-06-30-design-now-has-a-figma-style-left-rail-for-file-agent-assets.md +1 -1
- package/corpus/templates/design/changelog/2026-06-30-design-tools-now-use-a-quieter-figma-style-list-layout.md +1 -1
- package/corpus/templates/design/changelog/2026-06-30-multi-select-outlines-now-stay-consistent-across-all-screens.md +1 -1
- package/corpus/templates/design/changelog/2026-07-03-canvas-interactions-now-match-figma-rotation-aware-resizing-.md +1 -1
- package/corpus/templates/design/changelog/2026-07-03-layers-panel-matches-figma-top-layer-on-top-drag-with-auto-s.md +1 -1
- package/corpus/templates/design/changelog/2026-07-05-designs-get-ai-generated-names-from-your-prompt.md +6 -0
- package/corpus/templates/design/changelog/2026-07-05-local-code-screens-keep-their-connection-when-duplicated-and.md +6 -0
- package/corpus/templates/design/changelog/2026-07-05-press-a-to-draw-screens-on-the-canvas-inside-a-screen-it-dr.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-3d-rotation-and-perspective-controls-on-every-element.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-ai-generated-and-duplicated-screens-now-get-their-editor-sel.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-applying-shaders-and-fills-in-quick-succession-can-no-longer.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-breakpoint-frames-in-the-overview-now-support-click-to-selec.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-breakpoint-segments-in-the-inspector-now-show-a-device-icon-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-breakpoint-targeting-now-lives-in-one-compact-control-in-the.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-canvas-edits-can-no-longer-be-overwritten-by-stale-collabora.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-canvas-edits-inside-screens-save-reliably-again.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-cleaner-inspector-removed-the-ask-ai-box-from-the-design-tab.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-clicking-empty-canvas-space-now-deselects-an-element-inside-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-dragging-an-element-onto-a-rectangle-now-nests-it-inside-wit.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-dragging-screens-and-objects-on-the-canvas-is-now-much-smoot.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-drawing-a-shape-or-text-on-the-canvas-no-longer-flashes-away.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-dropping-an-element-between-two-others-inside-a-screen-now-s.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-duplicating-a-screen-now-places-the-copy-exactly-where-you-d.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-edit-gradients-directly-on-elements-inside-screens-scale-str.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-edits-from-collaborators-and-the-agent-now-update-the-canvas.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-a-bug-where-most-style-position-and-text-edits-briefly.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-a-confusing-raw-error-message-when-a-file-upload-fails.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-a-rare-case-where-an-edit-could-be-silently-overwritte.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-inserted-assets-sometimes-disappearing-reappearing-or-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-inspector-value-scrubbing-producing-erratic-numbers-an.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-padding-gap-and-min-max-size-scrubbing-not-updating-th.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-text-stroke-color-turning-black-and-glyphs-disappearin.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-the-collapsed-effects-and-stroke-inspector-sections-sh.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-the-new-design-dialog-closing-unexpectedly-when-using-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-the-typography-section-missing-when-selecting-text-nes.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-full-view-no-longer-flashes-back-to-the-overview-on-entry-no.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-keyframe-animation-timeline-per-property-keyframes-easing-cu.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-moving-restyling-and-scrubbing-values-on-elements-in-ai-gene.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-padding-and-corner-radius-value-scrubbing-now-snaps-to-whole.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-padding-handles-now-only-resize-on-the-handle-line-itself-sh.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-preview-and-edit-hover-focus-and-pressed-states-with-real-cs.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-promoting-an-element-to-a-component-now-works-correctly-for-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-reduced-url-history-churn-and-selection-overlay-flicker-whil.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-removed-the-decorative-dot-before-screen-names-in-the-overvi.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-responsive-breakpoints-edit-at-any-width-with-cascading-over.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-screen-labels-no-longer-turn-blue-when-you-hover-inside-the-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-scrolling-a-screen-with-an-element-selected-stays-smooth-on-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-selecting-multiple-elements-and-dragging-now-moves-the-whole.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-selection-borders-handles-and-value-readouts-now-stay-the-sa.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-selection-handles-and-editing-chrome-now-stay-a-constant-siz.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-shader-fills-and-effects-ai-authored-or-preset-glsl-shaders-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-text-drawn-on-the-canvas-now-defaults-to-white-and-inter-in-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-text-dropped-into-a-shape-now-stays-readable-instead-of-disa.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-the-add-tweak-controls-prompt-no-longer-jumps-to-the-corner-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-the-padding-value-readout-now-stays-visible-while-you-hover-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-the-selected-element-attachment-now-clears-from-the-chat-com.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-the-zoom-menu-is-more-compact-with-tight-rows-and-right-alig.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-undo-and-redo-no-longer-flash-the-screen-changes-now-apply-i.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-zoom-presets-like-zoom-to-50-now-stay-on-the-focused-screen-.md +6 -0
- package/corpus/templates/design/server/lib/import-design-files.ts +11 -2
- package/corpus/templates/design/server/source-workspace.ts +139 -59
- package/corpus/templates/design/shared/board-file.ts +11 -2
- package/corpus/templates/design/shared/breakpoint-media.ts +543 -0
- package/corpus/templates/design/shared/canvas-math.ts +354 -35
- package/corpus/templates/design/shared/code-layer.ts +277 -8
- package/corpus/templates/design/shared/design-state.ts +11 -4
- package/corpus/templates/design/shared/interaction-states.ts +548 -0
- package/corpus/templates/design/shared/motion-compiler.ts +186 -23
- package/corpus/templates/design/shared/motion-easing.ts +294 -0
- package/corpus/templates/design/shared/motion-timeline.ts +400 -34
- package/corpus/templates/design/shared/prompt-title.ts +95 -0
- package/corpus/templates/design/shared/responsive-classes.ts +448 -10
- package/corpus/templates/design/shared/screen-annotation.ts +46 -0
- package/corpus/templates/design/shared/shader-fills.ts +1009 -0
- package/corpus/templates/design/shared/shader-presets.ts +852 -4
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +15 -2
- package/corpus/templates/slides/app/components/editor/ImportButton.tsx +10 -1
- package/corpus/templates/slides/app/lib/upload-response.ts +71 -0
- package/dist/client/AgentPanel.js +1 -1
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/useCollabReconcile.js +11 -6
- package/dist/client/rich-markdown-editor/useCollabReconcile.js.map +1 -1
- package/dist/client/use-action.d.ts +3 -0
- package/dist/client/use-action.d.ts.map +1 -1
- package/dist/client/use-action.js +34 -3
- package/dist/client/use-action.js.map +1 -1
- package/dist/client/use-db-sync.d.ts +4 -0
- package/dist/client/use-db-sync.d.ts.map +1 -1
- package/dist/client/use-db-sync.js +37 -18
- package/dist/client/use-db-sync.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/db/create-get-db.d.ts +25 -0
- package/dist/db/create-get-db.d.ts.map +1 -1
- package/dist/db/create-get-db.js +25 -2
- package/dist/db/create-get-db.js.map +1 -1
- package/dist/observability/routes.d.ts +3 -3
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/framework-request-handler.d.ts.map +1 -1
- package/dist/server/framework-request-handler.js +37 -1
- package/dist/server/framework-request-handler.js.map +1 -1
- package/package.json +1 -1
- package/corpus/templates/design/app/components/design/inspector/InspectorAiActions.tsx +0 -153
- package/corpus/templates/design/app/hooks/useAgentEditRequest.ts +0 -133
- package/corpus/templates/design/app/pages/DesignEditor.tsx +0 -22924
|
@@ -0,0 +1,763 @@
|
|
|
1
|
+
import { useActionMutation, useActionQuery } from "@agent-native/core/client";
|
|
2
|
+
import { propNameToDataAttribute } from "@shared/component-model";
|
|
3
|
+
import {
|
|
4
|
+
IconArrowRight,
|
|
5
|
+
IconCode,
|
|
6
|
+
IconExternalLink,
|
|
7
|
+
IconLoader2,
|
|
8
|
+
} from "@tabler/icons-react";
|
|
9
|
+
import { useQueryClient } from "@tanstack/react-query";
|
|
10
|
+
import { useCallback, useEffect, useRef } from "react";
|
|
11
|
+
import { toast } from "sonner";
|
|
12
|
+
|
|
13
|
+
import { Button } from "@/components/ui/button";
|
|
14
|
+
import { Input } from "@/components/ui/input";
|
|
15
|
+
import { Label } from "@/components/ui/label";
|
|
16
|
+
import {
|
|
17
|
+
Select,
|
|
18
|
+
SelectContent,
|
|
19
|
+
SelectItem,
|
|
20
|
+
SelectTrigger,
|
|
21
|
+
SelectValue,
|
|
22
|
+
} from "@/components/ui/select";
|
|
23
|
+
import { Switch } from "@/components/ui/switch";
|
|
24
|
+
import {
|
|
25
|
+
Tooltip,
|
|
26
|
+
TooltipContent,
|
|
27
|
+
TooltipTrigger,
|
|
28
|
+
} from "@/components/ui/tooltip";
|
|
29
|
+
|
|
30
|
+
import {
|
|
31
|
+
canRebuildAlpineDataLosslessly,
|
|
32
|
+
isBooleanPropValue,
|
|
33
|
+
parseAlpineDataObject,
|
|
34
|
+
replaceAlpineDataKeyValue,
|
|
35
|
+
serializeAlpineDataObject,
|
|
36
|
+
} from "./code-inspect-helpers";
|
|
37
|
+
|
|
38
|
+
// ─── Make it real — inline upgrade card (§3, §6.6) ──────────────────────────
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Payload shape returned by `connect-builder-app`. Only the fields used by
|
|
42
|
+
* the card UI are typed here; the action may return additional fields.
|
|
43
|
+
*/
|
|
44
|
+
interface ConnectBuilderAppResult {
|
|
45
|
+
connected: boolean;
|
|
46
|
+
builderEnabled: boolean;
|
|
47
|
+
connectUrl: string;
|
|
48
|
+
appHost: string;
|
|
49
|
+
branchProjectId?: string;
|
|
50
|
+
cta: {
|
|
51
|
+
kind: "connect-builder" | "configure-project";
|
|
52
|
+
label: string;
|
|
53
|
+
description: string;
|
|
54
|
+
primaryAction: string;
|
|
55
|
+
connectUrl: string;
|
|
56
|
+
} | null;
|
|
57
|
+
message: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Inline "Make it real" upgrade card.
|
|
62
|
+
*
|
|
63
|
+
* Rendered wherever a real-app-only control is reached on an inline design
|
|
64
|
+
* (Component source jump, token write-back, live captures, etc.). Queries
|
|
65
|
+
* `connect-builder-app` to determine the current connection state, then
|
|
66
|
+
* offers the appropriate CTA:
|
|
67
|
+
*
|
|
68
|
+
* - Not connected → "Connect Builder.io" button (opens connectUrl)
|
|
69
|
+
* - Connected, no project → "Open Builder settings" (configure project ID)
|
|
70
|
+
* - Fully enabled → "Make it real" button (calls migrate-inline-design-to-app)
|
|
71
|
+
*
|
|
72
|
+
* The card is progressively disclosed: it only mounts when a gated control is
|
|
73
|
+
* actually reached, so it never appears for users who are already on a real-app
|
|
74
|
+
* source (`localhost` / `fusion`) or whose `sourceCapabilities` already include
|
|
75
|
+
* the needed capability.
|
|
76
|
+
*
|
|
77
|
+
* Matches the design-editor panel chrome: dashed-border, accent tint, small
|
|
78
|
+
* text at 10px — same idiom as the existing `ctaRequired` block in
|
|
79
|
+
* ComponentSection.
|
|
80
|
+
*/
|
|
81
|
+
function MakeItRealCard({
|
|
82
|
+
designId,
|
|
83
|
+
featureLabel,
|
|
84
|
+
}: {
|
|
85
|
+
/** The active design id — required to call connect-builder-app. */
|
|
86
|
+
designId: string;
|
|
87
|
+
/**
|
|
88
|
+
* Short human-readable label for the gated feature (e.g. "token write-back",
|
|
89
|
+
* "component source jump", "live captures"). Shown in the card body so the
|
|
90
|
+
* user understands exactly what they're unlocking.
|
|
91
|
+
*/
|
|
92
|
+
featureLabel: string;
|
|
93
|
+
}) {
|
|
94
|
+
const { data, isLoading } = useActionQuery<ConnectBuilderAppResult>(
|
|
95
|
+
"connect-builder-app",
|
|
96
|
+
{ designId },
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
const migrateMutation = useActionMutation("migrate-inline-design-to-app");
|
|
100
|
+
|
|
101
|
+
// While fetching status, show a muted placeholder that matches the card
|
|
102
|
+
// height so the inspector doesn't jump when the data arrives.
|
|
103
|
+
if (isLoading || !data) {
|
|
104
|
+
return (
|
|
105
|
+
<div className="flex h-7 items-center rounded-[5px] bg-[var(--design-editor-control-bg)] px-2">
|
|
106
|
+
<div className="h-3 w-28 animate-pulse rounded bg-muted/40" />
|
|
107
|
+
</div>
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Determine which CTA to show.
|
|
112
|
+
const cta = data.cta;
|
|
113
|
+
|
|
114
|
+
// Already fully enabled — no CTA needed (caller should already have gated
|
|
115
|
+
// this component away, but guard here for safety).
|
|
116
|
+
if (!cta) return null;
|
|
117
|
+
|
|
118
|
+
const isPending = migrateMutation.isPending;
|
|
119
|
+
const migrateError = migrateMutation.error;
|
|
120
|
+
|
|
121
|
+
// "Make it real" primary action: open the connect URL or migrate.
|
|
122
|
+
const handlePrimary = () => {
|
|
123
|
+
if (cta.kind === "connect-builder") {
|
|
124
|
+
// Open the Builder OAuth connect flow in a new tab. The user completes
|
|
125
|
+
// it there and comes back; the card will re-query on next render.
|
|
126
|
+
window.open(cta.connectUrl, "_blank", "noopener,noreferrer");
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
if (cta.kind === "configure-project") {
|
|
130
|
+
window.open(cta.connectUrl, "_blank", "noopener,noreferrer");
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const handleMigrate = () => {
|
|
136
|
+
migrateMutation.mutate({ designId });
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
// Migration result — show branch link.
|
|
140
|
+
const migrateResult = migrateMutation.data as
|
|
141
|
+
| {
|
|
142
|
+
status: "processing";
|
|
143
|
+
branchName?: string;
|
|
144
|
+
url?: string;
|
|
145
|
+
message?: string;
|
|
146
|
+
}
|
|
147
|
+
| undefined;
|
|
148
|
+
|
|
149
|
+
if (migrateResult?.status === "processing" && migrateResult.url) {
|
|
150
|
+
return (
|
|
151
|
+
<div className="flex items-center gap-2 rounded-[5px] border border-[var(--design-editor-control-border)] bg-[var(--design-editor-control-bg)] px-2 py-1.5">
|
|
152
|
+
<IconLoader2 className="size-3.5 shrink-0 animate-spin text-[var(--design-editor-accent-color)]" />
|
|
153
|
+
<p className="min-w-0 flex-1 truncate text-[10px] text-muted-foreground">
|
|
154
|
+
{migrateResult.message ??
|
|
155
|
+
`Generating ${migrateResult.branchName ?? "React app"}.`}
|
|
156
|
+
</p>
|
|
157
|
+
<a
|
|
158
|
+
href={migrateResult.url}
|
|
159
|
+
target="_blank"
|
|
160
|
+
rel="noopener noreferrer"
|
|
161
|
+
className="inline-flex h-6 shrink-0 items-center gap-1 rounded-md px-1.5 text-[10px] font-semibold text-[var(--design-editor-accent-color)] hover:bg-[var(--design-editor-panel-raised-bg)]"
|
|
162
|
+
>
|
|
163
|
+
{"Open" /* i18n-ignore make-it-real card */}
|
|
164
|
+
<IconExternalLink className="size-2.5" />
|
|
165
|
+
</a>
|
|
166
|
+
</div>
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const summary =
|
|
171
|
+
cta.kind === "configure-project"
|
|
172
|
+
? `Choose a Builder project to enable ${featureLabel}.`
|
|
173
|
+
: `Connect Builder to enable ${featureLabel}.`;
|
|
174
|
+
const primaryLabel =
|
|
175
|
+
cta.kind === "configure-project"
|
|
176
|
+
? "Choose" /* i18n-ignore make-it-real card */
|
|
177
|
+
: "Connect"; /* i18n-ignore make-it-real card */
|
|
178
|
+
|
|
179
|
+
return (
|
|
180
|
+
<div className="space-y-1">
|
|
181
|
+
<div className="flex items-center gap-2 rounded-[5px] border border-dashed border-[var(--design-editor-control-border)] bg-[var(--design-editor-control-bg)]/70 px-2 py-1.5">
|
|
182
|
+
<span
|
|
183
|
+
className="size-1.5 shrink-0 rounded-full bg-[var(--design-editor-accent-color)]"
|
|
184
|
+
aria-hidden="true"
|
|
185
|
+
/>
|
|
186
|
+
<p
|
|
187
|
+
className="min-w-0 flex-1 truncate text-[10px] text-muted-foreground"
|
|
188
|
+
title={summary}
|
|
189
|
+
>
|
|
190
|
+
{summary}
|
|
191
|
+
</p>
|
|
192
|
+
<Button
|
|
193
|
+
type="button"
|
|
194
|
+
size="sm"
|
|
195
|
+
onClick={handlePrimary}
|
|
196
|
+
title={cta.primaryAction}
|
|
197
|
+
className="h-6 shrink-0 gap-1 rounded-md bg-[var(--design-editor-accent-color)] px-1.5 text-[10px] font-semibold text-white hover:bg-[var(--design-editor-accent-hover-color)]"
|
|
198
|
+
>
|
|
199
|
+
{primaryLabel}
|
|
200
|
+
<IconArrowRight className="size-2.5" />
|
|
201
|
+
</Button>
|
|
202
|
+
|
|
203
|
+
{/* When Builder is fully connected, also offer direct migration */}
|
|
204
|
+
{data.connected && data.builderEnabled && (
|
|
205
|
+
<Button
|
|
206
|
+
type="button"
|
|
207
|
+
variant="ghost"
|
|
208
|
+
size="sm"
|
|
209
|
+
onClick={handleMigrate}
|
|
210
|
+
disabled={isPending}
|
|
211
|
+
className="h-6 shrink-0 gap-1 rounded-md px-1.5 text-[10px] font-semibold text-muted-foreground hover:text-foreground disabled:cursor-wait disabled:opacity-60"
|
|
212
|
+
>
|
|
213
|
+
{isPending ? (
|
|
214
|
+
<>
|
|
215
|
+
<IconLoader2 className="size-2.5 animate-spin" />
|
|
216
|
+
{"Generating" /* i18n-ignore make-it-real card */}
|
|
217
|
+
</>
|
|
218
|
+
) : (
|
|
219
|
+
<>{"Generate" /* i18n-ignore make-it-real card */}</>
|
|
220
|
+
)}
|
|
221
|
+
</Button>
|
|
222
|
+
)}
|
|
223
|
+
</div>
|
|
224
|
+
{migrateError ? (
|
|
225
|
+
<p className="px-2 text-[10px] text-destructive">
|
|
226
|
+
{migrateError instanceof Error
|
|
227
|
+
? migrateError.message
|
|
228
|
+
: /* i18n-ignore make-it-real card */ "Migration failed. Please try again."}
|
|
229
|
+
</p>
|
|
230
|
+
) : null}
|
|
231
|
+
</div>
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// ─── Component section (§6.1) ─────────────────────────────────────────────────
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Shape returned by `get-component-details`. Only the fields the UI needs are
|
|
239
|
+
* typed here; the action may return additional fields.
|
|
240
|
+
*/
|
|
241
|
+
interface ComponentDetailsResult {
|
|
242
|
+
nodeId: string;
|
|
243
|
+
name: string;
|
|
244
|
+
sourceType: string;
|
|
245
|
+
observedProps: Array<{ name: string; value: string }>;
|
|
246
|
+
persistedVariants: Record<string, string[]>;
|
|
247
|
+
sourceLocation?: { filePath: string; exportName?: string } | null;
|
|
248
|
+
/** Component instance shape, including the Alpine `x-data` expression. */
|
|
249
|
+
instance?: {
|
|
250
|
+
alpineData?: string | null;
|
|
251
|
+
nodeId?: string;
|
|
252
|
+
selector?: string;
|
|
253
|
+
} | null;
|
|
254
|
+
capabilities: {
|
|
255
|
+
canResolveToFile: boolean;
|
|
256
|
+
hasFullIndex: boolean;
|
|
257
|
+
canEditProps: boolean;
|
|
258
|
+
ctaRequired: boolean;
|
|
259
|
+
ctaMessage?: string;
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Contextual COMPONENT section rendered inside the Design tab when the
|
|
265
|
+
* selected element is a component instance (carries
|
|
266
|
+
* `data-agent-native-component`).
|
|
267
|
+
*
|
|
268
|
+
* Shows: component name, source path (when capability available), observed
|
|
269
|
+
* prop values, variant/size/state controls from `get-component-details`, and
|
|
270
|
+
* an "Edit component source" action. Real-app features are gated by the
|
|
271
|
+
* capabilities returned by the action; Alpine gets a lightweight read-only
|
|
272
|
+
* view plus a Connect-Builder CTA.
|
|
273
|
+
*
|
|
274
|
+
* Matches the workbench artboard spec in DESIGN-STUDIO-PLAN.md §6.1.
|
|
275
|
+
*/
|
|
276
|
+
export function ComponentSection({
|
|
277
|
+
designId,
|
|
278
|
+
fileId,
|
|
279
|
+
activeContent,
|
|
280
|
+
activeFileUpdatedAt,
|
|
281
|
+
nodeId,
|
|
282
|
+
onComponentPropApplied,
|
|
283
|
+
sourceCapabilities = [],
|
|
284
|
+
}: {
|
|
285
|
+
designId: string;
|
|
286
|
+
fileId?: string;
|
|
287
|
+
activeContent?: string;
|
|
288
|
+
activeFileUpdatedAt?: string | null;
|
|
289
|
+
nodeId: string;
|
|
290
|
+
onComponentPropApplied?: (
|
|
291
|
+
fileId: string,
|
|
292
|
+
content: string,
|
|
293
|
+
updatedAt?: string,
|
|
294
|
+
) => void;
|
|
295
|
+
/** Capability names advertised by the current source. */
|
|
296
|
+
sourceCapabilities?: string[];
|
|
297
|
+
}) {
|
|
298
|
+
const queryClient = useQueryClient();
|
|
299
|
+
const detailsParams = { designId, nodeId, ...(fileId ? { fileId } : {}) };
|
|
300
|
+
const detailsKey = ["action", "get-component-details", detailsParams];
|
|
301
|
+
const latestSourceRef = useRef<{
|
|
302
|
+
content: string;
|
|
303
|
+
revision?: string | null;
|
|
304
|
+
}>({
|
|
305
|
+
content: activeContent ?? "",
|
|
306
|
+
revision: activeFileUpdatedAt ?? null,
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
useEffect(() => {
|
|
310
|
+
latestSourceRef.current = {
|
|
311
|
+
content: activeContent ?? "",
|
|
312
|
+
revision: activeFileUpdatedAt ?? null,
|
|
313
|
+
};
|
|
314
|
+
}, [activeContent, activeFileUpdatedAt, fileId, nodeId]);
|
|
315
|
+
|
|
316
|
+
const { data, isLoading, error, refetch } =
|
|
317
|
+
useActionQuery<ComponentDetailsResult>(
|
|
318
|
+
"get-component-details",
|
|
319
|
+
detailsParams,
|
|
320
|
+
{ refetchOnMount: "always" },
|
|
321
|
+
);
|
|
322
|
+
|
|
323
|
+
const openSourceMutation = useActionMutation("open-component-source");
|
|
324
|
+
const applyPropMutation = useActionMutation("apply-component-prop-edit");
|
|
325
|
+
|
|
326
|
+
const postComponentPropPreview = useCallback(
|
|
327
|
+
(attribute: string, value: string) => {
|
|
328
|
+
if (typeof document === "undefined") return;
|
|
329
|
+
|
|
330
|
+
const iframe = document.querySelector<HTMLIFrameElement>(
|
|
331
|
+
"iframe[data-design-preview-iframe]",
|
|
332
|
+
);
|
|
333
|
+
iframe?.contentWindow?.postMessage(
|
|
334
|
+
{
|
|
335
|
+
type: "style-change",
|
|
336
|
+
selector: data?.instance?.selector ?? "",
|
|
337
|
+
nodeId: data?.instance?.nodeId ?? nodeId,
|
|
338
|
+
attributeOverrides: { [attribute]: value },
|
|
339
|
+
},
|
|
340
|
+
"*",
|
|
341
|
+
);
|
|
342
|
+
},
|
|
343
|
+
[data?.instance?.nodeId, data?.instance?.selector, nodeId],
|
|
344
|
+
);
|
|
345
|
+
|
|
346
|
+
// Persist a single prop change through apply-component-prop-edit. Attribute
|
|
347
|
+
// props also preview immediately in the iframe so the selected component
|
|
348
|
+
// changes without waiting for the write/refetch round-trip.
|
|
349
|
+
const persistPropEdit = (
|
|
350
|
+
edit:
|
|
351
|
+
| { kind: "alpineData"; value: string }
|
|
352
|
+
| { kind: "attribute"; attribute: string; value: string },
|
|
353
|
+
optimistic: (prev: ComponentDetailsResult) => ComponentDetailsResult,
|
|
354
|
+
) => {
|
|
355
|
+
queryClient.setQueryData<ComponentDetailsResult>(detailsKey, (prev) =>
|
|
356
|
+
prev ? optimistic(prev) : prev,
|
|
357
|
+
);
|
|
358
|
+
if (edit.kind === "attribute") {
|
|
359
|
+
postComponentPropPreview(edit.attribute, edit.value);
|
|
360
|
+
}
|
|
361
|
+
const latestSource = latestSourceRef.current;
|
|
362
|
+
applyPropMutation.mutate(
|
|
363
|
+
{
|
|
364
|
+
designId,
|
|
365
|
+
nodeId,
|
|
366
|
+
...(fileId ? { fileId } : {}),
|
|
367
|
+
edit,
|
|
368
|
+
...(latestSource.content
|
|
369
|
+
? {
|
|
370
|
+
source: {
|
|
371
|
+
currentContent: latestSource.content,
|
|
372
|
+
...(latestSource.revision
|
|
373
|
+
? { revision: latestSource.revision }
|
|
374
|
+
: {}),
|
|
375
|
+
},
|
|
376
|
+
}
|
|
377
|
+
: {}),
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
onSuccess: (result) => {
|
|
381
|
+
const response = result as {
|
|
382
|
+
content?: unknown;
|
|
383
|
+
fileId?: unknown;
|
|
384
|
+
updatedAt?: unknown;
|
|
385
|
+
conflict?: unknown;
|
|
386
|
+
error?: unknown;
|
|
387
|
+
};
|
|
388
|
+
if (response.conflict) {
|
|
389
|
+
toast.error(
|
|
390
|
+
typeof response.error === "string"
|
|
391
|
+
? response.error
|
|
392
|
+
: "This file changed since this component prop edit was prepared. Refresh and try again.",
|
|
393
|
+
);
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
if (
|
|
397
|
+
typeof response.fileId === "string" &&
|
|
398
|
+
typeof response.content === "string"
|
|
399
|
+
) {
|
|
400
|
+
const updatedAt =
|
|
401
|
+
typeof response.updatedAt === "string"
|
|
402
|
+
? response.updatedAt
|
|
403
|
+
: undefined;
|
|
404
|
+
latestSourceRef.current = {
|
|
405
|
+
content: response.content,
|
|
406
|
+
revision: updatedAt ?? latestSourceRef.current.revision,
|
|
407
|
+
};
|
|
408
|
+
onComponentPropApplied?.(
|
|
409
|
+
response.fileId,
|
|
410
|
+
response.content,
|
|
411
|
+
updatedAt,
|
|
412
|
+
);
|
|
413
|
+
}
|
|
414
|
+
},
|
|
415
|
+
onSettled: () => {
|
|
416
|
+
void queryClient.invalidateQueries({
|
|
417
|
+
queryKey: ["action", "get-design"],
|
|
418
|
+
});
|
|
419
|
+
void queryClient.invalidateQueries({ queryKey: detailsKey });
|
|
420
|
+
void refetch();
|
|
421
|
+
},
|
|
422
|
+
},
|
|
423
|
+
);
|
|
424
|
+
};
|
|
425
|
+
|
|
426
|
+
useEffect(() => {
|
|
427
|
+
if (typeof document === "undefined") return;
|
|
428
|
+
|
|
429
|
+
const handleMessage = (event: MessageEvent) => {
|
|
430
|
+
if (
|
|
431
|
+
(event.data as { type?: unknown } | null)?.type === "element-select"
|
|
432
|
+
) {
|
|
433
|
+
void refetch();
|
|
434
|
+
}
|
|
435
|
+
};
|
|
436
|
+
window.addEventListener("message", handleMessage);
|
|
437
|
+
return () => {
|
|
438
|
+
window.removeEventListener("message", handleMessage);
|
|
439
|
+
};
|
|
440
|
+
}, [refetch]);
|
|
441
|
+
|
|
442
|
+
// While loading, show a compact skeleton that matches the section width.
|
|
443
|
+
if (isLoading) {
|
|
444
|
+
return (
|
|
445
|
+
<section className="shrink-0 border-t border-[var(--design-editor-control-border)] first:border-t-0">
|
|
446
|
+
<div className="flex min-h-9 items-center gap-2 px-3">
|
|
447
|
+
<div className="h-3 w-24 animate-pulse rounded bg-muted/50" />
|
|
448
|
+
</div>
|
|
449
|
+
<div className="space-y-1.5 px-3 pb-3 pt-0.5">
|
|
450
|
+
<div className="h-5 w-full animate-pulse rounded bg-muted/40" />
|
|
451
|
+
<div className="h-5 w-3/4 animate-pulse rounded bg-muted/40" />
|
|
452
|
+
</div>
|
|
453
|
+
</section>
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// Hard error (node not found, no access, etc.) — collapse silently so
|
|
458
|
+
// the rest of the inspector is not disrupted.
|
|
459
|
+
if (error || !data) return null;
|
|
460
|
+
|
|
461
|
+
const {
|
|
462
|
+
name,
|
|
463
|
+
sourceType,
|
|
464
|
+
sourceLocation,
|
|
465
|
+
observedProps,
|
|
466
|
+
persistedVariants,
|
|
467
|
+
instance,
|
|
468
|
+
capabilities,
|
|
469
|
+
} = data;
|
|
470
|
+
|
|
471
|
+
// ── Editable prop model ───────────────────────────────────────────────────
|
|
472
|
+
// Inline/Alpine designs persist through apply-component-prop-edit. Two write
|
|
473
|
+
// surfaces:
|
|
474
|
+
// • x-data keys → kind "alpineData" (rewrites the whole object)
|
|
475
|
+
// • data-prop-* attrs → kind "attribute" (data-agent-native-prop-<kebab>)
|
|
476
|
+
// Real-app sources keep the deeper source-prop controls gated as-is, so for
|
|
477
|
+
// non-inline sources the controls are read-only here.
|
|
478
|
+
const isInline = sourceType === "inline";
|
|
479
|
+
const editingEnabled = isInline && capabilities.canEditProps; // gated; real-app stays read-only for now
|
|
480
|
+
const alpineData = parseAlpineDataObject(instance?.alpineData);
|
|
481
|
+
|
|
482
|
+
// Each editable row: name + current value + how it persists + its options.
|
|
483
|
+
type PropRow = {
|
|
484
|
+
name: string;
|
|
485
|
+
value: string;
|
|
486
|
+
/** Variant/enum options when the prop is a known group. */
|
|
487
|
+
options?: string[];
|
|
488
|
+
/** Persist surface for this prop. */
|
|
489
|
+
surface: "alpineData" | "attribute";
|
|
490
|
+
};
|
|
491
|
+
|
|
492
|
+
const rows: PropRow[] = [];
|
|
493
|
+
const seen = new Set<string>();
|
|
494
|
+
|
|
495
|
+
// 1) Alpine x-data keys come first — they drive the live variant/state.
|
|
496
|
+
if (alpineData) {
|
|
497
|
+
for (const [key, value] of Object.entries(alpineData)) {
|
|
498
|
+
rows.push({
|
|
499
|
+
name: key,
|
|
500
|
+
value,
|
|
501
|
+
options: persistedVariants[key],
|
|
502
|
+
surface: "alpineData",
|
|
503
|
+
});
|
|
504
|
+
seen.add(key);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// 2) data-agent-native-prop-* attributes not already covered by x-data.
|
|
509
|
+
for (const prop of observedProps) {
|
|
510
|
+
if (seen.has(prop.name)) continue;
|
|
511
|
+
rows.push({
|
|
512
|
+
name: prop.name,
|
|
513
|
+
value: prop.value,
|
|
514
|
+
options: persistedVariants[prop.name],
|
|
515
|
+
surface: "attribute",
|
|
516
|
+
});
|
|
517
|
+
seen.add(prop.name);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// 3) persistedVariant groups with no observed value yet (default to first).
|
|
521
|
+
for (const [group, options] of Object.entries(persistedVariants)) {
|
|
522
|
+
if (seen.has(group)) continue;
|
|
523
|
+
rows.push({
|
|
524
|
+
name: group,
|
|
525
|
+
value: options[0] ?? "",
|
|
526
|
+
options,
|
|
527
|
+
surface: alpineData ? "alpineData" : "attribute",
|
|
528
|
+
});
|
|
529
|
+
seen.add(group);
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
const hasRows = rows.length > 0;
|
|
533
|
+
|
|
534
|
+
// Build the apply-component-prop-edit payload + optimistic cache patch for a
|
|
535
|
+
// single prop change.
|
|
536
|
+
const commitProp = (row: PropRow, nextValue: string) => {
|
|
537
|
+
if (!editingEnabled || nextValue === row.value) return;
|
|
538
|
+
|
|
539
|
+
if (row.surface === "alpineData") {
|
|
540
|
+
// Surgically replace only the edited key's value inside the original
|
|
541
|
+
// x-data string so methods, nested objects, escaped strings, quoted
|
|
542
|
+
// keys, and whitespace survive byte-for-byte. A full
|
|
543
|
+
// parse→mutate→serialize round-trip would drop anything
|
|
544
|
+
// parseAlpineDataObject can't model (e.g. `toggle() { … }`).
|
|
545
|
+
const original = instance?.alpineData ?? "";
|
|
546
|
+
const surgical = replaceAlpineDataKeyValue(original, row.name, nextValue);
|
|
547
|
+
|
|
548
|
+
let serialized: string;
|
|
549
|
+
if (surgical != null) {
|
|
550
|
+
serialized = surgical;
|
|
551
|
+
} else if (canRebuildAlpineDataLosslessly(original)) {
|
|
552
|
+
// The key isn't present yet (or there is no original literal). Rebuild
|
|
553
|
+
// from the flat map — safe here precisely because the original holds
|
|
554
|
+
// nothing richer than the flat literals serialize already preserves.
|
|
555
|
+
const nextData = { ...(alpineData ?? {}), [row.name]: nextValue };
|
|
556
|
+
serialized = serializeAlpineDataObject(nextData);
|
|
557
|
+
} else {
|
|
558
|
+
// The original carries content (methods / nested / expressions) we
|
|
559
|
+
// can't rewrite for this key without dropping it. Fail safe: skip the
|
|
560
|
+
// edit rather than persist a lossy rewrite, and tell the user why so
|
|
561
|
+
// the change doesn't silently vanish.
|
|
562
|
+
toast.error(
|
|
563
|
+
// i18n-ignore
|
|
564
|
+
"Can’t safely edit this prop inline — this component’s Alpine state is too complex. Edit the source instead.",
|
|
565
|
+
);
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
const nextSerialized = serialized;
|
|
570
|
+
persistPropEdit(
|
|
571
|
+
{ kind: "alpineData", value: nextSerialized },
|
|
572
|
+
(prev) => ({
|
|
573
|
+
...prev,
|
|
574
|
+
instance: { ...(prev.instance ?? {}), alpineData: nextSerialized },
|
|
575
|
+
observedProps: prev.observedProps.map((p) =>
|
|
576
|
+
p.name === row.name ? { ...p, value: nextValue } : p,
|
|
577
|
+
),
|
|
578
|
+
}),
|
|
579
|
+
);
|
|
580
|
+
} else {
|
|
581
|
+
persistPropEdit(
|
|
582
|
+
{
|
|
583
|
+
kind: "attribute",
|
|
584
|
+
attribute: propNameToDataAttribute(row.name),
|
|
585
|
+
value: nextValue,
|
|
586
|
+
},
|
|
587
|
+
(prev) => {
|
|
588
|
+
const exists = prev.observedProps.some((p) => p.name === row.name);
|
|
589
|
+
return {
|
|
590
|
+
...prev,
|
|
591
|
+
observedProps: exists
|
|
592
|
+
? prev.observedProps.map((p) =>
|
|
593
|
+
p.name === row.name ? { ...p, value: nextValue } : p,
|
|
594
|
+
)
|
|
595
|
+
: [...prev.observedProps, { name: row.name, value: nextValue }],
|
|
596
|
+
};
|
|
597
|
+
},
|
|
598
|
+
);
|
|
599
|
+
}
|
|
600
|
+
};
|
|
601
|
+
|
|
602
|
+
// ── Capability gates ──
|
|
603
|
+
const canJumpToSource =
|
|
604
|
+
capabilities.canResolveToFile &&
|
|
605
|
+
Boolean(sourceLocation?.filePath) &&
|
|
606
|
+
sourceCapabilities.includes("resolveNodeToFile");
|
|
607
|
+
|
|
608
|
+
// ── Source chip text ──
|
|
609
|
+
const sourceChip = sourceLocation?.exportName
|
|
610
|
+
? `${sourceLocation.exportName} — ${sourceLocation.filePath}`
|
|
611
|
+
: (sourceLocation?.filePath ?? null);
|
|
612
|
+
|
|
613
|
+
return (
|
|
614
|
+
<section
|
|
615
|
+
className="shrink-0 border-t border-[var(--design-editor-control-border)] first:border-t-0"
|
|
616
|
+
data-testid="component-section"
|
|
617
|
+
>
|
|
618
|
+
{/* ── Section header ── */}
|
|
619
|
+
<div className="flex min-h-9 items-center gap-2 px-3">
|
|
620
|
+
{/* Accent diamond matching the workbench artboard component rows */}
|
|
621
|
+
<span
|
|
622
|
+
className="size-2 shrink-0 rotate-45 rounded-[2px] bg-[var(--design-editor-component-color)]"
|
|
623
|
+
aria-hidden="true"
|
|
624
|
+
/>
|
|
625
|
+
<h3 className="min-w-0 flex-1 truncate !text-[11px] font-semibold text-foreground">
|
|
626
|
+
{name}
|
|
627
|
+
</h3>
|
|
628
|
+
{/* Jump-to-source action */}
|
|
629
|
+
<Tooltip>
|
|
630
|
+
<TooltipTrigger asChild>
|
|
631
|
+
<Button
|
|
632
|
+
type="button"
|
|
633
|
+
variant="ghost"
|
|
634
|
+
size="icon"
|
|
635
|
+
className="size-6 rounded-md text-muted-foreground hover:text-foreground disabled:cursor-not-allowed disabled:opacity-40"
|
|
636
|
+
disabled={!canJumpToSource}
|
|
637
|
+
aria-label={
|
|
638
|
+
"Edit component source" /* i18n-ignore design inspector action */
|
|
639
|
+
}
|
|
640
|
+
onClick={() => {
|
|
641
|
+
openSourceMutation.mutate({
|
|
642
|
+
designId,
|
|
643
|
+
nodeId,
|
|
644
|
+
...(fileId ? { fileId } : {}),
|
|
645
|
+
});
|
|
646
|
+
}}
|
|
647
|
+
>
|
|
648
|
+
<IconExternalLink className="size-3.5" />
|
|
649
|
+
</Button>
|
|
650
|
+
</TooltipTrigger>
|
|
651
|
+
<TooltipContent>
|
|
652
|
+
{
|
|
653
|
+
canJumpToSource
|
|
654
|
+
? "Edit component source" /* i18n-ignore design inspector action */
|
|
655
|
+
: (capabilities.ctaMessage ??
|
|
656
|
+
"Source jump needs a connected app") /* i18n-ignore design inspector tooltip */
|
|
657
|
+
}
|
|
658
|
+
</TooltipContent>
|
|
659
|
+
</Tooltip>
|
|
660
|
+
</div>
|
|
661
|
+
|
|
662
|
+
{/* ── Body ── */}
|
|
663
|
+
<div className="space-y-1.5 px-3 pb-3 pt-0.5 !text-[11px]">
|
|
664
|
+
{/* Source path chip */}
|
|
665
|
+
{sourceChip && (
|
|
666
|
+
<div
|
|
667
|
+
className="flex items-center gap-1 rounded bg-[var(--design-editor-control-bg)] px-2 py-1"
|
|
668
|
+
title={sourceChip}
|
|
669
|
+
>
|
|
670
|
+
<IconCode className="size-3 shrink-0 text-muted-foreground/60" />
|
|
671
|
+
<span className="min-w-0 flex-1 truncate font-mono text-[10px] text-muted-foreground">
|
|
672
|
+
{sourceChip}
|
|
673
|
+
</span>
|
|
674
|
+
</div>
|
|
675
|
+
)}
|
|
676
|
+
|
|
677
|
+
{/* Typed prop controls. Inline/Alpine designs are editable and persist
|
|
678
|
+
through apply-component-prop-edit; real-app sources are read-only
|
|
679
|
+
until the deeper source-prop controls land. */}
|
|
680
|
+
{hasRows && (
|
|
681
|
+
<div className="space-y-1">
|
|
682
|
+
<p className="text-[10px] font-semibold uppercase tracking-wide text-muted-foreground/70">
|
|
683
|
+
{"Props" /* i18n-ignore design inspector label */}
|
|
684
|
+
</p>
|
|
685
|
+
{rows.map((row) => {
|
|
686
|
+
const hasOptions = (row.options?.length ?? 0) > 0;
|
|
687
|
+
const isBoolean = !hasOptions && isBooleanPropValue(row.value);
|
|
688
|
+
const disabled = !editingEnabled || applyPropMutation.isPending;
|
|
689
|
+
return (
|
|
690
|
+
<div key={row.name} className="flex items-center gap-1.5">
|
|
691
|
+
<Label className="w-[64px] shrink-0 truncate !text-[11px] font-medium capitalize text-muted-foreground">
|
|
692
|
+
{row.name}
|
|
693
|
+
</Label>
|
|
694
|
+
{hasOptions ? (
|
|
695
|
+
// Dropdown for variant / enum groups.
|
|
696
|
+
<Select
|
|
697
|
+
value={row.value || row.options![0] || ""}
|
|
698
|
+
onValueChange={(v) => commitProp(row, v)}
|
|
699
|
+
disabled={disabled}
|
|
700
|
+
>
|
|
701
|
+
<SelectTrigger className="h-6 min-w-0 flex-1 rounded-md border-[var(--design-editor-control-border)] bg-[var(--design-editor-control-bg)] px-1.5 !text-[11px] shadow-none focus:ring-1 focus:ring-[var(--design-editor-accent-color)]">
|
|
702
|
+
<SelectValue />
|
|
703
|
+
</SelectTrigger>
|
|
704
|
+
<SelectContent>
|
|
705
|
+
{row.options!.map((opt) => (
|
|
706
|
+
<SelectItem
|
|
707
|
+
key={opt}
|
|
708
|
+
value={opt}
|
|
709
|
+
className="!text-[11px]"
|
|
710
|
+
>
|
|
711
|
+
{opt}
|
|
712
|
+
</SelectItem>
|
|
713
|
+
))}
|
|
714
|
+
</SelectContent>
|
|
715
|
+
</Select>
|
|
716
|
+
) : isBoolean ? (
|
|
717
|
+
// Toggle for boolean props.
|
|
718
|
+
<div className="flex min-w-0 flex-1 items-center">
|
|
719
|
+
<Switch
|
|
720
|
+
checked={row.value.trim().toLowerCase() === "true"}
|
|
721
|
+
onCheckedChange={(checked) =>
|
|
722
|
+
commitProp(row, checked ? "true" : "false")
|
|
723
|
+
}
|
|
724
|
+
disabled={disabled}
|
|
725
|
+
className="h-4 w-7 [&>span]:size-3 [&>span]:data-[state=checked]:translate-x-3"
|
|
726
|
+
aria-label={
|
|
727
|
+
row.name /* i18n-ignore dynamic prop name */
|
|
728
|
+
}
|
|
729
|
+
/>
|
|
730
|
+
</div>
|
|
731
|
+
) : (
|
|
732
|
+
// Text input for string props (e.g. a label).
|
|
733
|
+
<Input
|
|
734
|
+
defaultValue={row.value}
|
|
735
|
+
key={`${row.name}:${row.value}`}
|
|
736
|
+
disabled={disabled}
|
|
737
|
+
onBlur={(e) => commitProp(row, e.target.value)}
|
|
738
|
+
onKeyDown={(e) => {
|
|
739
|
+
if (e.key === "Enter") {
|
|
740
|
+
e.preventDefault();
|
|
741
|
+
e.currentTarget.blur();
|
|
742
|
+
}
|
|
743
|
+
}}
|
|
744
|
+
className="h-6 min-w-0 flex-1 rounded-md border-[var(--design-editor-control-border)] bg-[var(--design-editor-control-bg)] px-1.5 !text-[11px] shadow-none focus-visible:ring-1 focus-visible:ring-[var(--design-editor-accent-color)] md:!text-[11px]"
|
|
745
|
+
/>
|
|
746
|
+
)}
|
|
747
|
+
</div>
|
|
748
|
+
);
|
|
749
|
+
})}
|
|
750
|
+
</div>
|
|
751
|
+
)}
|
|
752
|
+
|
|
753
|
+
{/* Connect-Builder CTA (only when prop editing is actually gated). */}
|
|
754
|
+
{capabilities.ctaRequired && !editingEnabled && (
|
|
755
|
+
<MakeItRealCard
|
|
756
|
+
designId={designId}
|
|
757
|
+
featureLabel="component source jump and typed prop metadata"
|
|
758
|
+
/>
|
|
759
|
+
)}
|
|
760
|
+
</div>
|
|
761
|
+
</section>
|
|
762
|
+
);
|
|
763
|
+
}
|