@agent-native/core 0.80.8 → 0.80.10
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 +2 -2
- package/corpus/core/CHANGELOG.md +19 -0
- package/corpus/core/docs/content/locales/ar-SA/template-design.mdx +6 -0
- package/corpus/core/docs/content/locales/de-DE/template-design.mdx +8 -0
- package/corpus/core/docs/content/locales/es-ES/template-design.mdx +10 -2
- package/corpus/core/docs/content/locales/fr-FR/template-design.mdx +10 -2
- package/corpus/core/docs/content/locales/hi-IN/template-design.mdx +7 -0
- package/corpus/core/docs/content/locales/ja-JP/template-design.mdx +7 -0
- package/corpus/core/docs/content/locales/ko-KR/template-design.mdx +7 -0
- package/corpus/core/docs/content/locales/pt-BR/template-design.mdx +9 -2
- package/corpus/core/docs/content/locales/zh-CN/template-design.mdx +6 -0
- package/corpus/core/docs/content/locales/zh-TW/template-design.mdx +6 -0
- package/corpus/core/docs/content/template-design.mdx +9 -2
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/cli/design-connect.ts +28 -2
- package/corpus/core/src/cli/mcp.ts +10 -0
- package/corpus/core/src/cli/skills.ts +103 -36
- package/corpus/core/src/client/error-format.ts +25 -0
- package/corpus/core/src/client/extensions/ExtensionViewer.tsx +13 -11
- package/corpus/core/src/client/guided-questions.tsx +32 -4
- package/corpus/core/src/client/onboarding/OnboardingPanel.tsx +33 -1
- package/corpus/core/src/client/resources/BuiltinCapabilityDetail.tsx +8 -0
- package/corpus/core/src/client/resources/use-builtin-capabilities.ts +4 -2
- package/corpus/core/src/client/session-replay.ts +34 -0
- package/corpus/core/src/client/sharing/ShareButton.tsx +106 -21
- package/corpus/core/src/coding-tools/index.ts +1 -1
- package/corpus/core/src/mcp/index.ts +2 -0
- package/corpus/core/src/mcp/screen-memory-stdio.ts +290 -0
- package/corpus/core/src/mcp-client/builtin-capabilities.ts +13 -1
- package/corpus/core/src/mcp-client/index.ts +9 -0
- package/corpus/core/src/mcp-client/screen-memory-local.ts +461 -0
- package/corpus/core/src/onboarding/types.ts +7 -0
- package/corpus/core/src/server/agent-chat-plugin.ts +25 -0
- package/corpus/core/src/server/agents-bundle.ts +35 -7
- package/corpus/core/src/styles/agent-native.css +5 -0
- package/corpus/core/src/templates/default/app/global.css +35 -35
- package/corpus/templates/analytics/AGENTS.md +6 -0
- package/corpus/templates/analytics/actions/github-repo-files.ts +9 -0
- package/corpus/templates/analytics/app/components/layout/Layout.tsx +4 -4
- package/corpus/templates/analytics/app/global.css +144 -40
- package/corpus/templates/analytics/app/pages/adhoc/explorer/index.tsx +1 -1
- package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/ChartCard.tsx +1 -1
- package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/index.tsx +1 -1
- package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +4 -4
- package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +9 -9
- package/corpus/templates/analytics/changelog/2026-06-29-agents-can-search-and-read-connected-github-repositories-whe.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-29-sessions-and-explorer-layouts-adapt-when-the-agent-sidebar-is.md +6 -0
- package/corpus/templates/assets/app/components/layout/Layout.tsx +3 -1
- package/corpus/templates/assets/app/components/library/LibraryPresetGrid.tsx +1 -1
- package/corpus/templates/assets/app/global.css +99 -35
- package/corpus/templates/assets/app/routes/asset.$id.tsx +2 -2
- package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +4 -4
- package/corpus/templates/assets/app/routes/library.tsx +4 -4
- package/corpus/templates/assets/changelog/2026-06-29-asset-library-and-detail-layouts-adapt-when-the-agent-sidebar.md +6 -0
- package/corpus/templates/brain/app/components/layout/Layout.tsx +1 -1
- package/corpus/templates/brain/app/global.css +63 -35
- package/corpus/templates/brain/app/routes/search.tsx +1 -1
- package/corpus/templates/brain/app/routes/settings.tsx +1 -1
- package/corpus/templates/brain/app/routes/sources.tsx +2 -2
- package/corpus/templates/brain/changelog/2026-06-29-search-settings-and-source-layouts-adapt-when-the-agent-sidebar.md +6 -0
- package/corpus/templates/calendar/app/components/calendar/EventDetailPanel.tsx +4 -4
- package/corpus/templates/calendar/app/components/calendar/FindTimePanel.tsx +3 -2
- package/corpus/templates/calendar/app/components/layout/AppLayout.tsx +4 -2
- package/corpus/templates/calendar/app/global.css +69 -35
- package/corpus/templates/calendar/changelog/2026-06-29-event-and-find-time-layouts-adapt-when-the-agent-sidebar-is.md +6 -0
- package/corpus/templates/chat/app/components/layout/Layout.tsx +1 -1
- package/corpus/templates/chat/app/global.css +35 -35
- package/corpus/templates/chat/changelog/2026-06-29-chat-layouts-adapt-when-the-agent-sidebar-is-open.md +6 -0
- package/corpus/templates/clips/AGENTS.md +13 -0
- package/corpus/templates/clips/actions/get-screen-memory-status.ts +12 -0
- package/corpus/templates/clips/actions/query-screen-memory-context.ts +29 -0
- package/corpus/templates/clips/actions/view-screen.ts +2 -2
- package/corpus/templates/clips/app/components/library/library-layout.tsx +1 -1
- package/corpus/templates/clips/app/global.css +47 -35
- package/corpus/templates/clips/app/routes/_app.meetings.$meetingId.tsx +3 -3
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +45 -18
- package/corpus/templates/clips/changelog/2026-06-29-meeting-detail-layouts-adapt-when-the-agent-sidebar-is-open.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-29-screen-memory-keeps-a-local-rolling-screen-buffer-for-agent-context.md +5 -0
- package/corpus/templates/clips/changelog/2026-06-29-video-chat-is-easier-to-find-on-recording-pages-and-s3-setup.md +6 -0
- package/corpus/templates/clips/chrome-extension/src/styles.css +39 -39
- package/corpus/templates/clips/desktop/src/app.tsx +290 -1
- package/corpus/templates/clips/desktop/src/shared/config.ts +83 -0
- package/corpus/templates/clips/desktop/src/styles.css +67 -52
- package/corpus/templates/clips/desktop/src-tauri/src/config.rs +50 -0
- package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +17 -0
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +13 -13
- package/corpus/templates/clips/desktop/src-tauri/src/screen_memory.rs +990 -0
- package/corpus/templates/clips/server/plugins/onboarding.ts +2 -0
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +3 -3
- package/corpus/templates/content/app/components/layout/Layout.tsx +1 -1
- package/corpus/templates/content/app/global.css +29 -15
- package/corpus/templates/content/changelog/2026-06-29-database-gallery-layouts-adapt-when-the-agent-sidebar-is-open.md +6 -0
- package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +25 -20
- package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +8 -0
- package/corpus/templates/design/.agents/skills/export-handoff/SKILL.md +8 -0
- package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +13 -0
- package/corpus/templates/design/AGENTS.md +20 -12
- package/corpus/templates/design/CODE_NATIVE_DESIGN_STUDIO_IMPLEMENTATION_PLAN.md +1054 -0
- package/corpus/templates/design/DESIGN-STUDIO-PLAN.md +717 -0
- package/corpus/templates/design/actions/add-breakpoint.ts +143 -0
- package/corpus/templates/design/actions/add-localhost-screens.ts +5 -0
- package/corpus/templates/design/actions/apply-a11y-fix.ts +317 -0
- package/corpus/templates/design/actions/apply-component-prop-edit.ts +433 -0
- package/corpus/templates/design/actions/apply-design-state.ts +200 -0
- package/corpus/templates/design/actions/apply-design-token-edit.ts +202 -0
- package/corpus/templates/design/actions/apply-motion-edit.ts +450 -0
- package/corpus/templates/design/actions/apply-shader-fill.ts +404 -0
- package/corpus/templates/design/actions/apply-visual-edit.ts +214 -8
- package/corpus/templates/design/actions/capture-design-state.ts +224 -0
- package/corpus/templates/design/actions/connect-builder-app.ts +162 -0
- package/corpus/templates/design/actions/create-component.ts +447 -0
- package/corpus/templates/design/actions/create-design-branch.ts +263 -0
- package/corpus/templates/design/actions/create-design-state.ts +162 -0
- package/corpus/templates/design/actions/delete-design-state.ts +55 -0
- package/corpus/templates/design/actions/deploy-design-preview.ts +275 -0
- package/corpus/templates/design/actions/export-coding-handoff.ts +12 -4
- package/corpus/templates/design/actions/generate-design.ts +6 -21
- package/corpus/templates/design/actions/generate-screens.ts +43 -2
- package/corpus/templates/design/actions/get-component-details.ts +242 -0
- package/corpus/templates/design/actions/get-design-branch-diff.ts +362 -0
- package/corpus/templates/design/actions/get-design-review.ts +314 -0
- package/corpus/templates/design/actions/get-design-surface-index.ts +582 -0
- package/corpus/templates/design/actions/get-design.ts +1 -0
- package/corpus/templates/design/actions/get-motion-timeline.ts +99 -0
- package/corpus/templates/design/actions/index-components.ts +258 -0
- package/corpus/templates/design/actions/index-design-tokens.ts +273 -0
- package/corpus/templates/design/actions/list-design-extensions.ts +309 -0
- package/corpus/templates/design/actions/list-design-source-capabilities.ts +153 -0
- package/corpus/templates/design/actions/list-design-states.ts +72 -0
- package/corpus/templates/design/actions/migrate-inline-design-to-app.ts +298 -0
- package/corpus/templates/design/actions/open-component-source.ts +237 -0
- package/corpus/templates/design/actions/present-design-variants.ts +355 -30
- package/corpus/templates/design/actions/preview-component-prop-edit.ts +254 -0
- package/corpus/templates/design/actions/preview-design-token-edit.ts +113 -0
- package/corpus/templates/design/actions/preview-shader-fill.ts +189 -0
- package/corpus/templates/design/actions/remove-breakpoint.ts +103 -0
- package/corpus/templates/design/actions/remove-motion-timeline.ts +196 -0
- package/corpus/templates/design/actions/run-design-audit.ts +421 -0
- package/corpus/templates/design/actions/run-design-extension-action.ts +284 -0
- package/corpus/templates/design/actions/set-active-breakpoint.ts +39 -0
- package/corpus/templates/design/actions/view-screen.ts +75 -31
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +1738 -135
- package/corpus/templates/design/app/components/design/DesignEditorSkeleton.tsx +1 -1
- package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +632 -43
- package/corpus/templates/design/app/components/design/DrawOverlay.tsx +1 -1
- package/corpus/templates/design/app/components/design/EditPanel.tsx +1873 -132
- package/corpus/templates/design/app/components/design/LayersPanel.tsx +384 -219
- package/corpus/templates/design/app/components/design/LocalSourceEditBanner.tsx +157 -0
- package/corpus/templates/design/app/components/design/MotionDock.tsx +1062 -0
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +2103 -295
- package/corpus/templates/design/app/components/design/QuestionFlow.tsx +83 -206
- package/corpus/templates/design/app/components/design/ReviewPanel.tsx +759 -0
- package/corpus/templates/design/app/components/design/StatesPanel.tsx +579 -0
- package/corpus/templates/design/app/components/design/TokensPanel.tsx +573 -0
- package/corpus/templates/design/app/components/design/TweaksPanel.tsx +1 -1
- package/corpus/templates/design/app/components/design/canvas-primitive-style.ts +241 -0
- package/corpus/templates/design/app/components/design/index.ts +16 -1
- package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +238 -137
- package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +188 -14
- package/corpus/templates/design/app/components/design/inspector/ExportSettingsPanel.tsx +24 -5
- package/corpus/templates/design/app/components/design/inspector/ImageFillControls.tsx +57 -1
- package/corpus/templates/design/app/components/design/inspector/InspectorAiActions.tsx +145 -0
- package/corpus/templates/design/app/components/design/inspector/SHADER_INTEGRATION.md +42 -12
- package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +37 -12
- package/corpus/templates/design/app/components/design/inspector/index.ts +1 -0
- package/corpus/templates/design/app/components/design/inspector/scrub-input-utils.ts +12 -5
- package/corpus/templates/design/app/components/design/types.ts +9 -0
- package/corpus/templates/design/app/components/layout/Layout.tsx +23 -5
- package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +1 -1
- package/corpus/templates/design/app/global.css +77 -49
- package/corpus/templates/design/app/hooks/use-question-flow.ts +2 -2
- package/corpus/templates/design/app/hooks/useAgentEditRequest.ts +131 -0
- package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +11 -2
- package/corpus/templates/design/app/i18n/zh-TW.ts +34 -0
- package/corpus/templates/design/app/i18n-data.ts +314 -3
- package/corpus/templates/design/app/pages/DesignEditor.tsx +4929 -892
- package/corpus/templates/design/app/root.tsx +1 -1
- package/corpus/templates/design/changelog/2026-06-29-added-a-review-panel-in-the-design-editor-s-inspector-with-a.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-containers-no-longer-block-selecting-nested-elements-and-nor.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-copying-and-pasting-layers-no-longer-shows-success-notificat.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-design-agents-now-generate-and-refine-variants-from-stronger.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-design-inspector-controls-now-match-figmas-softer-dark-chrome.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-design-questions-now-appear-as-a-cleaner-canvas-intake-with-.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-design-variant-directions-now-appear-as-board-screens-with-chat-buttons.md +5 -0
- package/corpus/templates/design/changelog/2026-06-29-device-presets-in-all-screens-view-resize-the-selected-previ.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-element-selection-no-longer-snaps-back-to-the-previous-layer.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-frame-resize-measurements-now-stay-beside-the-cursor-while-d.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-inspector-number-fields-have-cleaner-tooltips-and-draggable-.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-layer-and-canvas-drags-keep-the-layer-list-stable-while-chan.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-layer-locking-layer-nesting-generated-question-intake-and-im.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-layer-moves-can-be-undone-and-redone-without-flashing-the-ca.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-layer-names-in-the-sidebar-can-scroll-horizontally-instead-o.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-layer-nesting-in-the-sidebar-now-uses-tighter-spacing-so-nam.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-layers-no-longer-repeat-document-wrapper-rows-when-a-screen-.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-overview-previews-now-keep-element-hover-and-selection-tied-.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-pressing-escape-now-cancels-a-stuck-screen-drag-in-the-overv.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-screen-overview-is-easier-to-find-and-named-screens-open-dir.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-screen-overview-only-highlights-screens-when-the-frame-or-ti.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-screen-overview-previews-now-resize-to-match-the-selected-de.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-screen-overview-selection-now-uses-a-pointer-cursor-instead-.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-screen-overview-selection-outlines-now-stay-locked-to-the-se.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-screen-overview-zoom-now-reports-real-screen-scale-separatel.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-screen-overview-zooming-feels-smoother-and-selection-outline.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-screen-overview-zooming-now-stays-on-the-overview-canvas-ins.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-screen-previews-use-a-single-blue-hover-border-in-all-screen.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-selected-containers-show-draggable-padding-and-gap-guides-on.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-selecting-a-nested-layer-from-all-screens-now-keeps-you-in-a.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-share-now-groups-link-sharing-exports-and-agent-handoff.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-the-design-editor-adds-a-studio-layer-with-tokens-respon.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-the-design-editor-loading-skeleton-now-uses-a-softer-charcoa.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-the-device-preview-menu-now-sits-next-to-the-preview-button-.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-the-inspector-stays-read-only-while-an-empty-design-is-still.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-the-missing-design-view-now-matches-the-rest-of-the-editor-c.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-the-pen-tool-icon-and-tool-option-menus-better-match-the-edi.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-typography-controls-now-keep-the-font-name-visible-and-move-.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-accessibility-findings-now-offer-a-one-click-fix.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-component-instances-now-have-editable-props-in-the-inspe.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-inspect-code-now-shows-the-elements-opening-tag-at-a-gla.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-shader-fill-presets-can-now-be-applied-to-an-element.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-the-motion-timeline-can-now-add-a-track-to-any-element-a.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-visual-editor-selection-layer-paint-and-drawing-controls-are-more-reliable.md +6 -0
- package/corpus/templates/design/e2e/README.md +3 -3
- package/corpus/templates/design/e2e/global-setup.ts +11 -22
- package/corpus/templates/design/e2e/helpers.ts +128 -11
- package/corpus/templates/design/playwright.config.ts +9 -3
- package/corpus/templates/design/server/db/schema.ts +123 -0
- package/corpus/templates/design/server/lib/coding-handoff.ts +126 -2
- package/corpus/templates/design/server/plugins/core-routes.ts +1 -2
- package/corpus/templates/design/server/plugins/db.ts +90 -0
- package/corpus/templates/design/server/routes/api/design-handoff/[id].zip.get.ts +86 -0
- package/corpus/templates/design/shared/builder-app.ts +297 -0
- package/corpus/templates/design/shared/capability-resolver.ts +123 -0
- package/corpus/templates/design/shared/capture-sanitize.ts +70 -0
- package/corpus/templates/design/shared/code-layer.ts +1141 -93
- package/corpus/templates/design/shared/component-model.ts +239 -0
- package/corpus/templates/design/shared/design-review.ts +275 -0
- package/corpus/templates/design/shared/design-source-capabilities.ts +286 -0
- package/corpus/templates/design/shared/design-state.ts +112 -0
- package/corpus/templates/design/shared/design-surface-index.ts +258 -0
- package/corpus/templates/design/shared/generation-session.ts +41 -0
- package/corpus/templates/design/shared/motion-compiler.ts +278 -0
- package/corpus/templates/design/shared/motion-timeline.ts +193 -0
- package/corpus/templates/design/shared/responsive-classes.ts +452 -0
- package/corpus/templates/design/shared/shader-fill.ts +323 -0
- package/corpus/templates/design/shared/source-mode.ts +245 -0
- package/corpus/templates/dispatch/app/global.css +79 -35
- package/corpus/templates/dispatch/app/routes/integrations.tsx +8 -7
- package/corpus/templates/dispatch/changelog/2026-06-29-integration-layouts-adapt-when-the-agent-sidebar-is-open.md +6 -0
- package/corpus/templates/forms/app/components/layout/Layout.tsx +6 -2
- package/corpus/templates/forms/app/global.css +64 -35
- package/corpus/templates/forms/app/pages/FormsListPage.tsx +2 -2
- package/corpus/templates/forms/app/pages/ResponseInsightsPage.tsx +3 -3
- package/corpus/templates/forms/changelog/2026-06-29-form-list-and-insight-layouts-adapt-when-the-agent-sidebar.md +6 -0
- package/corpus/templates/macros/app/components/layout/AppLayout.tsx +3 -1
- package/corpus/templates/macros/app/global.css +25 -9
- package/corpus/templates/macros/changelog/2026-06-29-macro-layouts-adapt-when-the-agent-sidebar-is-open.md +6 -0
- package/corpus/templates/mail/app/components/layout/AppLayout.tsx +6 -2
- package/corpus/templates/mail/app/global.css +42 -36
- package/corpus/templates/mail/app/pages/InboxPage.tsx +1 -1
- package/corpus/templates/mail/changelog/2026-06-29-the-contact-panel-now-adapts-to-the-available-mail-pane-width.md +6 -0
- package/corpus/templates/plan/app/components/layout/Layout.tsx +3 -1
- package/corpus/templates/plan/app/components/plan/CanvasArea.tsx +6 -2
- package/corpus/templates/plan/app/components/plan/DocumentArea.tsx +6 -7
- package/corpus/templates/plan/app/global.css +74 -42
- package/corpus/templates/plan/changelog/2026-06-29-plan-canvases-open-without-an-initial-pan-and-zoom-flicker.md +6 -0
- package/corpus/templates/plan/changelog/2026-06-29-plan-document-blocks-adapt-to-the-available-document-width.md +6 -0
- package/corpus/templates/plan/changelog/2026-06-29-plan-titles-and-summaries-can-be-edited-inline-without-focus.md +6 -0
- package/corpus/templates/slides/app/components/layout/Layout.tsx +3 -1
- package/corpus/templates/slides/app/global.css +49 -33
- package/corpus/templates/slides/changelog/2026-06-29-slide-layouts-adapt-when-the-agent-sidebar-is-open.md +6 -0
- package/corpus/templates/videos/app/components/layout/Layout.tsx +3 -1
- package/corpus/templates/videos/app/global.css +35 -35
- package/corpus/templates/videos/changelog/2026-06-29-video-layouts-adapt-when-the-agent-sidebar-is-open.md +6 -0
- package/dist/cli/design-connect.d.ts.map +1 -1
- package/dist/cli/design-connect.js +28 -2
- package/dist/cli/design-connect.js.map +1 -1
- package/dist/cli/mcp.d.ts.map +1 -1
- package/dist/cli/mcp.js +10 -0
- package/dist/cli/mcp.js.map +1 -1
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +95 -33
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/error-format.d.ts.map +1 -1
- package/dist/client/error-format.js +17 -0
- package/dist/client/error-format.js.map +1 -1
- package/dist/client/extensions/ExtensionViewer.d.ts.map +1 -1
- package/dist/client/extensions/ExtensionViewer.js +3 -4
- package/dist/client/extensions/ExtensionViewer.js.map +1 -1
- package/dist/client/guided-questions.d.ts +4 -0
- package/dist/client/guided-questions.d.ts.map +1 -1
- package/dist/client/guided-questions.js +16 -7
- package/dist/client/guided-questions.js.map +1 -1
- package/dist/client/onboarding/OnboardingPanel.js +26 -1
- package/dist/client/onboarding/OnboardingPanel.js.map +1 -1
- package/dist/client/resources/BuiltinCapabilityDetail.d.ts.map +1 -1
- package/dist/client/resources/BuiltinCapabilityDetail.js +1 -1
- package/dist/client/resources/BuiltinCapabilityDetail.js.map +1 -1
- package/dist/client/resources/use-builtin-capabilities.d.ts +1 -1
- package/dist/client/resources/use-builtin-capabilities.d.ts.map +1 -1
- package/dist/client/resources/use-builtin-capabilities.js +2 -1
- package/dist/client/resources/use-builtin-capabilities.js.map +1 -1
- package/dist/client/session-replay.d.ts.map +1 -1
- package/dist/client/session-replay.js +31 -0
- package/dist/client/session-replay.js.map +1 -1
- package/dist/client/sharing/ShareButton.d.ts +14 -0
- package/dist/client/sharing/ShareButton.d.ts.map +1 -1
- package/dist/client/sharing/ShareButton.js +36 -7
- package/dist/client/sharing/ShareButton.js.map +1 -1
- package/dist/coding-tools/index.js +1 -1
- package/dist/coding-tools/index.js.map +1 -1
- package/dist/mcp/index.d.ts +2 -0
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +1 -0
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/screen-memory-stdio.d.ts +6 -0
- package/dist/mcp/screen-memory-stdio.d.ts.map +1 -0
- package/dist/mcp/screen-memory-stdio.js +213 -0
- package/dist/mcp/screen-memory-stdio.js.map +1 -0
- package/dist/mcp-client/builtin-capabilities.d.ts +1 -1
- package/dist/mcp-client/builtin-capabilities.d.ts.map +1 -1
- package/dist/mcp-client/builtin-capabilities.js +10 -0
- package/dist/mcp-client/builtin-capabilities.js.map +1 -1
- package/dist/mcp-client/index.d.ts +1 -0
- package/dist/mcp-client/index.d.ts.map +1 -1
- package/dist/mcp-client/index.js +1 -0
- package/dist/mcp-client/index.js.map +1 -1
- package/dist/mcp-client/screen-memory-local.d.ts +61 -0
- package/dist/mcp-client/screen-memory-local.d.ts.map +1 -0
- package/dist/mcp-client/screen-memory-local.js +336 -0
- package/dist/mcp-client/screen-memory-local.js.map +1 -0
- package/dist/observability/routes.d.ts +3 -3
- package/dist/onboarding/types.d.ts +7 -0
- package/dist/onboarding/types.d.ts.map +1 -1
- package/dist/onboarding/types.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +26 -0
- 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/agents-bundle.d.ts.map +1 -1
- package/dist/server/agents-bundle.js +22 -7
- package/dist/server/agents-bundle.js.map +1 -1
- package/dist/styles/agent-native.css +5 -0
- package/dist/templates/default/app/global.css +35 -35
- package/docs/content/locales/ar-SA/template-design.mdx +6 -0
- package/docs/content/locales/de-DE/template-design.mdx +8 -0
- package/docs/content/locales/es-ES/template-design.mdx +10 -2
- package/docs/content/locales/fr-FR/template-design.mdx +10 -2
- package/docs/content/locales/hi-IN/template-design.mdx +7 -0
- package/docs/content/locales/ja-JP/template-design.mdx +7 -0
- package/docs/content/locales/ko-KR/template-design.mdx +7 -0
- package/docs/content/locales/pt-BR/template-design.mdx +9 -2
- package/docs/content/locales/zh-CN/template-design.mdx +6 -0
- package/docs/content/locales/zh-TW/template-design.mdx +6 -0
- package/docs/content/template-design.mdx +9 -2
- package/package.json +1 -1
- package/src/templates/default/app/global.css +35 -35
- package/corpus/templates/design/app/components/design/VariantGrid.tsx +0 -189
- package/corpus/templates/design/app/components/design/VariantHandoffCard.tsx +0 -115
- package/corpus/templates/design/app/hooks/use-variant-flow.ts +0 -342
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* apply-component-prop-edit — persist a component prop edit.
|
|
3
|
+
*
|
|
4
|
+
* **Tier A (Alpine / inline):** edits Alpine component annotations directly
|
|
5
|
+
* via the deterministic `apply-visual-edit` path — the same
|
|
6
|
+
* `replace-document-content` + Yjs/collab seam used for all other HTML writes.
|
|
7
|
+
*
|
|
8
|
+
* Supported Alpine edit kinds:
|
|
9
|
+
* - `alpineData` — replaces the `x-data` expression (class-level state,
|
|
10
|
+
* variant selection, disabled flag, etc.).
|
|
11
|
+
* - `attribute` — sets a `data-agent-native-prop-*` attribute or any other
|
|
12
|
+
* HTML attribute on the component root.
|
|
13
|
+
* - `classReplace` — replaces one Tailwind class with another on the root node.
|
|
14
|
+
*
|
|
15
|
+
* **Tier B (real-app, localhost / fusion):** prop writes require the
|
|
16
|
+
* `applyEdit` source capability (bridge write hardening). Until that lands the
|
|
17
|
+
* action returns a `ctaRequired: true` response and does not modify any source.
|
|
18
|
+
*
|
|
19
|
+
* See DESIGN-STUDIO-PLAN.md §6.1, §7 (preview/apply contract), §11 phase 2.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { defineAction } from "@agent-native/core";
|
|
23
|
+
import {
|
|
24
|
+
agentEnterDocument,
|
|
25
|
+
agentLeaveDocument,
|
|
26
|
+
agentUpdateSelection,
|
|
27
|
+
applyText,
|
|
28
|
+
getText,
|
|
29
|
+
hasCollabState,
|
|
30
|
+
seedFromText,
|
|
31
|
+
} from "@agent-native/core/collab";
|
|
32
|
+
import {
|
|
33
|
+
accessFilter,
|
|
34
|
+
assertAccess,
|
|
35
|
+
resolveAccess,
|
|
36
|
+
} from "@agent-native/core/sharing";
|
|
37
|
+
import { and, eq } from "drizzle-orm";
|
|
38
|
+
import { z } from "zod";
|
|
39
|
+
|
|
40
|
+
import { getDb, schema } from "../server/db/index.js";
|
|
41
|
+
import "../server/db/index.js"; // ensure registerShareableResource runs
|
|
42
|
+
import { resolveSourceCapabilities } from "../shared/capability-resolver.js";
|
|
43
|
+
import {
|
|
44
|
+
applyVisualEdit,
|
|
45
|
+
buildCodeLayerProjection,
|
|
46
|
+
} from "../shared/code-layer.js";
|
|
47
|
+
import type {
|
|
48
|
+
CodeLayerSource,
|
|
49
|
+
ClassEditIntent,
|
|
50
|
+
StyleEditIntent,
|
|
51
|
+
} from "../shared/code-layer.js";
|
|
52
|
+
import { componentNameFor } from "../shared/component-model.js";
|
|
53
|
+
import { hasCapability } from "../shared/design-source-capabilities.js";
|
|
54
|
+
import { normalizeDesignSourceType } from "../shared/source-mode.js";
|
|
55
|
+
|
|
56
|
+
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Escape an attribute value for safe inclusion inside a double-quoted HTML
|
|
60
|
+
* attribute. Mirrors the escaping used by the deterministic patcher.
|
|
61
|
+
*/
|
|
62
|
+
export function escapeAttributeValue(value: string): string {
|
|
63
|
+
return value
|
|
64
|
+
.replace(/&/g, "&")
|
|
65
|
+
.replace(/"/g, """)
|
|
66
|
+
.replace(/</g, "<")
|
|
67
|
+
.replace(/>/g, ">");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Set (or replace) a single attribute on the *opening tag* of a component
|
|
72
|
+
* root, using the node's source span. Pure — no DB / IO — so it can be unit
|
|
73
|
+
* tested directly.
|
|
74
|
+
*
|
|
75
|
+
* - When the attribute already exists on the open tag its value is replaced.
|
|
76
|
+
* - Otherwise the attribute is inserted just before the closing `>` / `/>`.
|
|
77
|
+
*
|
|
78
|
+
* Returns the rewritten full HTML and a `changed` flag (false when the span is
|
|
79
|
+
* missing or the rewrite produced an identical open tag).
|
|
80
|
+
*/
|
|
81
|
+
export function applyRootAttributeEdit(
|
|
82
|
+
html: string,
|
|
83
|
+
source: { openStart: number; openEnd: number } | null | undefined,
|
|
84
|
+
attrName: string,
|
|
85
|
+
attrValue: string,
|
|
86
|
+
): { content: string; changed: boolean } {
|
|
87
|
+
if (!source) return { content: html, changed: false };
|
|
88
|
+
|
|
89
|
+
const openTag = html.slice(source.openStart, source.openEnd);
|
|
90
|
+
// Replace an existing attribute if present, otherwise insert before the
|
|
91
|
+
// closing `>` or `/>`.
|
|
92
|
+
const attrRe = new RegExp(
|
|
93
|
+
`(\\s${attrName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\s*=\\s*)(?:"[^"]*"|'[^']*'|[^\\s>"']+)`,
|
|
94
|
+
"i",
|
|
95
|
+
);
|
|
96
|
+
const escaped = escapeAttributeValue(attrValue);
|
|
97
|
+
|
|
98
|
+
let newOpenTag: string;
|
|
99
|
+
if (attrRe.test(openTag)) {
|
|
100
|
+
newOpenTag = openTag.replace(attrRe, `$1"${escaped}"`);
|
|
101
|
+
} else {
|
|
102
|
+
const insertOffset = openTag.endsWith("/>")
|
|
103
|
+
? openTag.length - 2
|
|
104
|
+
: openTag.length - 1;
|
|
105
|
+
newOpenTag = `${openTag.slice(0, insertOffset)} ${attrName}="${escaped}"${openTag.slice(insertOffset)}`;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (newOpenTag === openTag) return { content: html, changed: false };
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
content:
|
|
112
|
+
html.slice(0, source.openStart) + newOpenTag + html.slice(source.openEnd),
|
|
113
|
+
changed: true,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async function liveContent(
|
|
118
|
+
fileId: string,
|
|
119
|
+
storedContent: string,
|
|
120
|
+
): Promise<string> {
|
|
121
|
+
try {
|
|
122
|
+
if (await hasCollabState(fileId)) {
|
|
123
|
+
const live = await getText(fileId, "content");
|
|
124
|
+
if (typeof live === "string") return live;
|
|
125
|
+
}
|
|
126
|
+
} catch {
|
|
127
|
+
// Collab reads are best-effort; SQL content is the fallback.
|
|
128
|
+
}
|
|
129
|
+
return storedContent;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async function persistEdit(file: {
|
|
133
|
+
id: string;
|
|
134
|
+
designId: string;
|
|
135
|
+
content: string;
|
|
136
|
+
}): Promise<void> {
|
|
137
|
+
await assertAccess("design", file.designId, "editor");
|
|
138
|
+
const db = getDb();
|
|
139
|
+
const now = new Date().toISOString();
|
|
140
|
+
|
|
141
|
+
agentEnterDocument(file.id);
|
|
142
|
+
try {
|
|
143
|
+
await db
|
|
144
|
+
.update(schema.designFiles)
|
|
145
|
+
.set({ content: file.content, updatedAt: now })
|
|
146
|
+
.where(eq(schema.designFiles.id, file.id));
|
|
147
|
+
|
|
148
|
+
if (await hasCollabState(file.id)) {
|
|
149
|
+
await applyText(file.id, file.content, "content", "agent");
|
|
150
|
+
} else {
|
|
151
|
+
await seedFromText(file.id, file.content);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
await db
|
|
155
|
+
.update(schema.designs)
|
|
156
|
+
.set({ updatedAt: now })
|
|
157
|
+
.where(eq(schema.designs.id, file.designId));
|
|
158
|
+
} finally {
|
|
159
|
+
agentLeaveDocument(file.id);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// ─── Action ───────────────────────────────────────────────────────────────────
|
|
164
|
+
|
|
165
|
+
export default defineAction({
|
|
166
|
+
description:
|
|
167
|
+
"Persist a component prop edit to the design source. " +
|
|
168
|
+
"For inline/Alpine designs, edits the data-agent-native-prop-* attributes, " +
|
|
169
|
+
"x-data expression, or class list of the component root via the deterministic " +
|
|
170
|
+
"HTML-patch path (same seam as apply-visual-edit). " +
|
|
171
|
+
"For real-app sources, the applyEdit capability must be available; if not, " +
|
|
172
|
+
"returns ctaRequired=true without modifying any file.",
|
|
173
|
+
schema: z.object({
|
|
174
|
+
designId: z.string().describe("Design project ID"),
|
|
175
|
+
nodeId: z
|
|
176
|
+
.string()
|
|
177
|
+
.describe("data-agent-native-node-id of the component root to edit"),
|
|
178
|
+
fileId: z
|
|
179
|
+
.string()
|
|
180
|
+
.optional()
|
|
181
|
+
.describe("Design file id; defaults to index.html"),
|
|
182
|
+
edit: z
|
|
183
|
+
.discriminatedUnion("kind", [
|
|
184
|
+
z.object({
|
|
185
|
+
kind: z.literal("alpineData"),
|
|
186
|
+
value: z
|
|
187
|
+
.string()
|
|
188
|
+
.describe(
|
|
189
|
+
"New x-data expression, e.g. \"{ variant: 'outline', size: 'lg' }\"",
|
|
190
|
+
),
|
|
191
|
+
}),
|
|
192
|
+
z.object({
|
|
193
|
+
kind: z.literal("attribute"),
|
|
194
|
+
attribute: z
|
|
195
|
+
.string()
|
|
196
|
+
// Strict identifier only. Blocks injecting a second attribute /
|
|
197
|
+
// event handler via the *name* (the value is escaped, the name was
|
|
198
|
+
// not). Rejects spaces, quotes, `=`, `>` and any `on*` handler.
|
|
199
|
+
.regex(
|
|
200
|
+
/^(?!on)[a-zA-Z][a-zA-Z0-9:_.-]*$/i,
|
|
201
|
+
"Unsafe HTML attribute name: only identifier characters are allowed and event handlers (on*) are rejected.",
|
|
202
|
+
)
|
|
203
|
+
.describe("HTML attribute name to set"),
|
|
204
|
+
value: z.string().describe("New attribute value"),
|
|
205
|
+
}),
|
|
206
|
+
z.object({
|
|
207
|
+
kind: z.literal("classReplace"),
|
|
208
|
+
from: z.string().describe("Existing Tailwind class to remove"),
|
|
209
|
+
to: z.string().describe("Replacement Tailwind class to add"),
|
|
210
|
+
}),
|
|
211
|
+
])
|
|
212
|
+
.describe("The prop edit to apply"),
|
|
213
|
+
}),
|
|
214
|
+
run: async ({ designId, nodeId, fileId, edit }) => {
|
|
215
|
+
const db = getDb();
|
|
216
|
+
|
|
217
|
+
// ── Access check ────────────────────────────────────────────────────────
|
|
218
|
+
const access = await resolveAccess("design", designId);
|
|
219
|
+
if (!access) throw new Error("Design not found");
|
|
220
|
+
|
|
221
|
+
// ── Source type + capability gate ────────────────────────────────────────
|
|
222
|
+
let rawSourceType: unknown = "inline";
|
|
223
|
+
const rawData = (access.resource as { data?: unknown }).data;
|
|
224
|
+
if (typeof rawData === "string") {
|
|
225
|
+
try {
|
|
226
|
+
const parsed: unknown = JSON.parse(rawData);
|
|
227
|
+
if (
|
|
228
|
+
parsed !== null &&
|
|
229
|
+
typeof parsed === "object" &&
|
|
230
|
+
"sourceType" in (parsed as object)
|
|
231
|
+
) {
|
|
232
|
+
rawSourceType = (parsed as { sourceType: unknown }).sourceType;
|
|
233
|
+
}
|
|
234
|
+
} catch {
|
|
235
|
+
// Default to inline.
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const sourceType = normalizeDesignSourceType(rawSourceType) ?? "inline";
|
|
240
|
+
const caps = resolveSourceCapabilities(sourceType);
|
|
241
|
+
|
|
242
|
+
// Real-app sources gate on `applyEdit` (bridge write hardening).
|
|
243
|
+
if (sourceType !== "inline" && !hasCapability(caps, "applyEdit")) {
|
|
244
|
+
return {
|
|
245
|
+
designId,
|
|
246
|
+
nodeId,
|
|
247
|
+
sourceType,
|
|
248
|
+
persisted: false,
|
|
249
|
+
ctaRequired: true,
|
|
250
|
+
ctaMessage:
|
|
251
|
+
"Prop write-back to real app sources requires the bridge applyEdit " +
|
|
252
|
+
"capability, which lands with bridge write hardening. " +
|
|
253
|
+
"Use preview-component-prop-edit to preview without persisting.",
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
await assertAccess("design", designId, "editor");
|
|
258
|
+
|
|
259
|
+
// ── Fetch file ───────────────────────────────────────────────────────────
|
|
260
|
+
const conditions = [
|
|
261
|
+
accessFilter(schema.designs, schema.designShares),
|
|
262
|
+
eq(schema.designFiles.designId, designId),
|
|
263
|
+
fileId
|
|
264
|
+
? eq(schema.designFiles.id, fileId)
|
|
265
|
+
: eq(schema.designFiles.filename, "index.html"),
|
|
266
|
+
];
|
|
267
|
+
|
|
268
|
+
const [file] = await db
|
|
269
|
+
.select({
|
|
270
|
+
id: schema.designFiles.id,
|
|
271
|
+
designId: schema.designFiles.designId,
|
|
272
|
+
filename: schema.designFiles.filename,
|
|
273
|
+
content: schema.designFiles.content,
|
|
274
|
+
})
|
|
275
|
+
.from(schema.designFiles)
|
|
276
|
+
.innerJoin(
|
|
277
|
+
schema.designs,
|
|
278
|
+
eq(schema.designFiles.designId, schema.designs.id),
|
|
279
|
+
)
|
|
280
|
+
.where(and(...conditions))
|
|
281
|
+
.limit(1);
|
|
282
|
+
|
|
283
|
+
if (!file) throw new Error("Design HTML file not found.");
|
|
284
|
+
|
|
285
|
+
const html = await liveContent(file.id, file.content ?? "");
|
|
286
|
+
|
|
287
|
+
// ── Resolve node ─────────────────────────────────────────────────────────
|
|
288
|
+
const codeLayerSource: CodeLayerSource = {
|
|
289
|
+
kind: "design-file",
|
|
290
|
+
designId: file.designId,
|
|
291
|
+
fileId: file.id,
|
|
292
|
+
filename: file.filename,
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
const projection = buildCodeLayerProjection(html, {
|
|
296
|
+
source: codeLayerSource,
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
const node = projection.nodes.find((n) => n.id === nodeId);
|
|
300
|
+
if (!node) {
|
|
301
|
+
throw new Error(
|
|
302
|
+
`Node "${nodeId}" not found. Run get-code-layer-projection to list current ids.`,
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const componentName = componentNameFor(node);
|
|
307
|
+
if (!componentName) {
|
|
308
|
+
throw new Error(
|
|
309
|
+
`Node "${nodeId}" is not a component root (no data-agent-native-component attribute).`,
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// ── Build edit intent ────────────────────────────────────────────────────
|
|
314
|
+
// Map the component prop edit kind to an EditIntent that apply-visual-edit
|
|
315
|
+
// understands. We use the same deterministic patch path for all kinds.
|
|
316
|
+
|
|
317
|
+
const target = { nodeId };
|
|
318
|
+
|
|
319
|
+
let intent: ClassEditIntent | StyleEditIntent;
|
|
320
|
+
|
|
321
|
+
if (edit.kind === "alpineData") {
|
|
322
|
+
// x-data is not a standard CSS property or Tailwind class — we write it
|
|
323
|
+
// as a style-like "attribute" edit by embedding it in the class edit path
|
|
324
|
+
// via the `set` operation on a synthetic class string. Because the HTML
|
|
325
|
+
// patcher writes raw attribute values we use the attribute-set approach
|
|
326
|
+
// that `apply-visual-edit` already supports through the `style` path
|
|
327
|
+
// (targeting `x-data` as a custom property in an inline `style`
|
|
328
|
+
// attribute would corrupt the DOM, so instead we encode the value in a
|
|
329
|
+
// `data-agent-native-alpine-data` attribute and let the bridge pick it
|
|
330
|
+
// up). For maximum compatibility with the existing apply-visual-edit
|
|
331
|
+
// path we use a class operation to manipulate the x-data value through a
|
|
332
|
+
// well-known pattern the bridge understands.
|
|
333
|
+
//
|
|
334
|
+
// The cleanest path is to write `data-agent-native-alpine-data` as an
|
|
335
|
+
// attribute so the iframe bridge can relay it to Alpine as the effective
|
|
336
|
+
// x-data — but since the bridge postMessage layer handles x-data edits
|
|
337
|
+
// on preview, for the persist path we do a direct HTML attribute patch.
|
|
338
|
+
// We accomplish this by treating it as a `style` edit on a sentinel
|
|
339
|
+
// property that the patcher will place as an attribute. However the
|
|
340
|
+
// current patcher only handles CSS properties, so we write the x-data
|
|
341
|
+
// value through the attribute approach: stamp `data-agent-native-prop-x-data`.
|
|
342
|
+
intent = {
|
|
343
|
+
kind: "class",
|
|
344
|
+
target,
|
|
345
|
+
// Use the `set` operation with a minimal token list to mark the node's
|
|
346
|
+
// Alpine data without touching real layout classes. The actual x-data
|
|
347
|
+
// attribute is written below via a direct HTML splice.
|
|
348
|
+
operation: "add",
|
|
349
|
+
className: `data-[x-data=${JSON.stringify(edit.value)}]:hidden`, // sentinel (will not apply visually)
|
|
350
|
+
} as ClassEditIntent;
|
|
351
|
+
// Fall through to the direct HTML splice below.
|
|
352
|
+
} else if (edit.kind === "classReplace") {
|
|
353
|
+
intent = {
|
|
354
|
+
kind: "class",
|
|
355
|
+
target,
|
|
356
|
+
operation: "replace",
|
|
357
|
+
from: edit.from,
|
|
358
|
+
to: edit.to,
|
|
359
|
+
} as ClassEditIntent;
|
|
360
|
+
} else {
|
|
361
|
+
// attribute kind — write as a class add of a data-attribute sentinel so
|
|
362
|
+
// the existing patcher path handles it, then fall through to the direct
|
|
363
|
+
// splice for the actual attribute patch.
|
|
364
|
+
intent = {
|
|
365
|
+
kind: "class",
|
|
366
|
+
target,
|
|
367
|
+
operation: "add",
|
|
368
|
+
className: `data-[prop-${edit.attribute}]:hidden`, // sentinel
|
|
369
|
+
} as ClassEditIntent;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// ── Direct HTML splice for attribute edits (alpineData + attribute) ──────
|
|
373
|
+
// The existing apply-visual-edit patcher is class/style/text focused. For
|
|
374
|
+
// attribute mutations on the component root we splice the raw HTML directly
|
|
375
|
+
// using the node's source span (the same technique the patcher uses for
|
|
376
|
+
// attribute stamping).
|
|
377
|
+
|
|
378
|
+
let patchedContent = html;
|
|
379
|
+
let changed = false;
|
|
380
|
+
|
|
381
|
+
if (edit.kind === "alpineData" || edit.kind === "attribute") {
|
|
382
|
+
const attrName = edit.kind === "alpineData" ? "x-data" : edit.attribute;
|
|
383
|
+
const result = applyRootAttributeEdit(
|
|
384
|
+
html,
|
|
385
|
+
node.source,
|
|
386
|
+
attrName,
|
|
387
|
+
edit.value,
|
|
388
|
+
);
|
|
389
|
+
patchedContent = result.content;
|
|
390
|
+
changed = result.changed;
|
|
391
|
+
} else if (edit.kind === "classReplace") {
|
|
392
|
+
// Use the deterministic patcher for class edits.
|
|
393
|
+
const patch = applyVisualEdit(html, intent, { source: codeLayerSource });
|
|
394
|
+
if (patch.result.status === "applied" && patch.result.changed) {
|
|
395
|
+
patchedContent = patch.content;
|
|
396
|
+
changed = true;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// ── Persist ──────────────────────────────────────────────────────────────
|
|
401
|
+
if (changed) {
|
|
402
|
+
await persistEdit({
|
|
403
|
+
id: file.id,
|
|
404
|
+
designId: file.designId,
|
|
405
|
+
content: patchedContent,
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
agentUpdateSelection(file.id, {
|
|
409
|
+
selection: node.selector,
|
|
410
|
+
nodeId,
|
|
411
|
+
editingFile: file.filename,
|
|
412
|
+
designId: file.designId,
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
return {
|
|
417
|
+
designId,
|
|
418
|
+
nodeId,
|
|
419
|
+
componentName,
|
|
420
|
+
sourceType,
|
|
421
|
+
editKind: edit.kind,
|
|
422
|
+
persisted: changed,
|
|
423
|
+
ctaRequired: false,
|
|
424
|
+
fileId: file.id,
|
|
425
|
+
filename: file.filename,
|
|
426
|
+
bytesBefore: html.length,
|
|
427
|
+
bytesAfter: patchedContent.length,
|
|
428
|
+
note: changed
|
|
429
|
+
? "Edit applied and persisted via the deterministic HTML-patch path."
|
|
430
|
+
: "No change applied — the edit produced the same result as the current content.",
|
|
431
|
+
};
|
|
432
|
+
},
|
|
433
|
+
});
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { defineAction } from "@agent-native/core";
|
|
2
|
+
import { assertAccess } from "@agent-native/core/sharing";
|
|
3
|
+
import { and, eq } from "drizzle-orm";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
|
|
6
|
+
import { getDb, schema } from "../server/db/index.js";
|
|
7
|
+
import "../server/db/index.js"; // ensure registerShareableResource runs
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Maximum serialised size of a replayed `captureData` payload. Captured DOM
|
|
11
|
+
* snapshots are arbitrary caller markup; cap them so a single state can't bloat
|
|
12
|
+
* the design row (and the shareable content it feeds).
|
|
13
|
+
*/
|
|
14
|
+
const CAPTURE_DATA_MAX_BYTES = 256 * 1024; // 256KB
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Strip stored-XSS vectors out of an HTML/markup string before it is persisted
|
|
18
|
+
* and later replayed into shareable design content. Mirrors the framework's
|
|
19
|
+
* text-edit HTML sanitiser: removes script/style/iframe/object/embed/link/meta/
|
|
20
|
+
* base tags, inline `on*` handlers, and `javascript:` / `vbscript:` / `data:`
|
|
21
|
+
* URLs in `href` / `src` / `xlink:href`.
|
|
22
|
+
*/
|
|
23
|
+
function sanitizeMarkup(html: string): string {
|
|
24
|
+
return html
|
|
25
|
+
.replace(
|
|
26
|
+
/<\s*(script|style|iframe|object|embed|link|meta|base)\b[\s\S]*?<\s*\/\s*\1\s*>/gi,
|
|
27
|
+
"",
|
|
28
|
+
)
|
|
29
|
+
.replace(
|
|
30
|
+
/<\s*(script|style|iframe|object|embed|link|meta|base)\b[^>]*\/?\s*>/gi,
|
|
31
|
+
"",
|
|
32
|
+
)
|
|
33
|
+
.replace(/\s+on[A-Za-z]+\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)/g, "")
|
|
34
|
+
.replace(
|
|
35
|
+
/\s+(href|src|xlink:href)\s*=\s*(?:(["'])\s*(?:javascript|vbscript|data):[\s\S]*?\2|(?:javascript|vbscript|data):[^\s>]*)/gi,
|
|
36
|
+
"",
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* A string "looks like markup" — and is therefore worth sanitising — when it
|
|
42
|
+
* contains an angle-bracket tag opener or a close tag. Plain data strings
|
|
43
|
+
* (route names, ids) are left untouched.
|
|
44
|
+
*/
|
|
45
|
+
function looksLikeMarkup(value: string): boolean {
|
|
46
|
+
return /<[a-zA-Z!/]/.test(value) || value.includes("</");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Recursively sanitise every string value inside a replayed state payload
|
|
51
|
+
* (e.g. `domHtml`, `domSnapshot`, `x-data` markup) so no untrusted DOM is
|
|
52
|
+
* persisted raw. Non-string leaves pass through unchanged.
|
|
53
|
+
*/
|
|
54
|
+
function sanitizeStatePayload(value: unknown): unknown {
|
|
55
|
+
if (typeof value === "string") {
|
|
56
|
+
return looksLikeMarkup(value) ? sanitizeMarkup(value) : value;
|
|
57
|
+
}
|
|
58
|
+
if (Array.isArray(value)) {
|
|
59
|
+
return value.map((entry) => sanitizeStatePayload(entry));
|
|
60
|
+
}
|
|
61
|
+
if (value && typeof value === "object") {
|
|
62
|
+
const out: Record<string, unknown> = {};
|
|
63
|
+
for (const [k, v] of Object.entries(value as Record<string, unknown>)) {
|
|
64
|
+
out[k] = sanitizeStatePayload(v);
|
|
65
|
+
}
|
|
66
|
+
return out;
|
|
67
|
+
}
|
|
68
|
+
return value;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export default defineAction({
|
|
72
|
+
description:
|
|
73
|
+
"Update (apply) changes to an existing design state row. " +
|
|
74
|
+
"Supports renaming, changing breakpoint, updating fixture/capture data, " +
|
|
75
|
+
"and setting the preview reference. All fields are optional; only provided " +
|
|
76
|
+
"fields are updated.",
|
|
77
|
+
schema: z.object({
|
|
78
|
+
id: z.string().describe("design_state row id to update"),
|
|
79
|
+
designId: z
|
|
80
|
+
.string()
|
|
81
|
+
.describe(
|
|
82
|
+
"Design project ID (required for access check; must match the state's design_id).",
|
|
83
|
+
),
|
|
84
|
+
name: z.string().min(1).optional().describe("Rename the state."),
|
|
85
|
+
breakpoint: z
|
|
86
|
+
.enum(["auto", "desktop", "tablet", "mobile"])
|
|
87
|
+
.optional()
|
|
88
|
+
.describe("Change the breakpoint context."),
|
|
89
|
+
route: z
|
|
90
|
+
.string()
|
|
91
|
+
.optional()
|
|
92
|
+
.nullable()
|
|
93
|
+
.describe("Update the associated app route path."),
|
|
94
|
+
fixtureData: z
|
|
95
|
+
.preprocess(
|
|
96
|
+
(v) => (typeof v === "string" ? JSON.parse(v) : v),
|
|
97
|
+
z.record(z.string(), z.unknown()),
|
|
98
|
+
)
|
|
99
|
+
.optional()
|
|
100
|
+
.nullable()
|
|
101
|
+
.describe("Replace the fixture data payload."),
|
|
102
|
+
captureData: z
|
|
103
|
+
.preprocess(
|
|
104
|
+
(v) => (typeof v === "string" ? JSON.parse(v) : v),
|
|
105
|
+
z.record(z.string(), z.unknown()),
|
|
106
|
+
)
|
|
107
|
+
.optional()
|
|
108
|
+
.nullable()
|
|
109
|
+
.describe("Replace the capture data payload."),
|
|
110
|
+
previewRef: z
|
|
111
|
+
.string()
|
|
112
|
+
.optional()
|
|
113
|
+
.nullable()
|
|
114
|
+
.describe("Update the preview snapshot reference."),
|
|
115
|
+
}),
|
|
116
|
+
run: async ({
|
|
117
|
+
id,
|
|
118
|
+
designId,
|
|
119
|
+
name,
|
|
120
|
+
breakpoint,
|
|
121
|
+
route,
|
|
122
|
+
fixtureData,
|
|
123
|
+
captureData,
|
|
124
|
+
previewRef,
|
|
125
|
+
}) => {
|
|
126
|
+
await assertAccess("design", designId, "editor");
|
|
127
|
+
|
|
128
|
+
const db = getDb();
|
|
129
|
+
const now = new Date().toISOString();
|
|
130
|
+
|
|
131
|
+
// Verify the row belongs to this design before updating.
|
|
132
|
+
const [existing] = await db
|
|
133
|
+
.select({ id: schema.designState.id })
|
|
134
|
+
.from(schema.designState)
|
|
135
|
+
.where(
|
|
136
|
+
and(
|
|
137
|
+
eq(schema.designState.id, id),
|
|
138
|
+
eq(schema.designState.designId, designId),
|
|
139
|
+
),
|
|
140
|
+
)
|
|
141
|
+
.limit(1);
|
|
142
|
+
|
|
143
|
+
if (!existing) {
|
|
144
|
+
throw new Error(
|
|
145
|
+
`design_state row '${id}' not found for design '${designId}'.`,
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const patch: Record<string, unknown> = { updatedAt: now };
|
|
150
|
+
if (name !== undefined) patch.name = name;
|
|
151
|
+
if (breakpoint !== undefined) patch.breakpoint = breakpoint;
|
|
152
|
+
if (route !== undefined) patch.route = route;
|
|
153
|
+
if (fixtureData !== undefined) {
|
|
154
|
+
// Sanitise replayed markup (stored-XSS guard) before persisting.
|
|
155
|
+
patch.fixtureData =
|
|
156
|
+
fixtureData !== null
|
|
157
|
+
? JSON.stringify(sanitizeStatePayload(fixtureData))
|
|
158
|
+
: null;
|
|
159
|
+
}
|
|
160
|
+
if (captureData !== undefined) {
|
|
161
|
+
if (captureData !== null) {
|
|
162
|
+
// Sanitise replayed DOM/markup and enforce a size cap so a single state
|
|
163
|
+
// can't bloat the row / the shareable content it feeds.
|
|
164
|
+
const captureDataJson = JSON.stringify(
|
|
165
|
+
sanitizeStatePayload(captureData),
|
|
166
|
+
);
|
|
167
|
+
if (
|
|
168
|
+
Buffer.byteLength(captureDataJson, "utf8") > CAPTURE_DATA_MAX_BYTES
|
|
169
|
+
) {
|
|
170
|
+
throw new Error(
|
|
171
|
+
`captureData exceeds the ${Math.round(
|
|
172
|
+
CAPTURE_DATA_MAX_BYTES / 1024,
|
|
173
|
+
)}KB limit. Use a smaller DOM snapshot or trim the payload.`,
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
patch.captureData = captureDataJson;
|
|
177
|
+
} else {
|
|
178
|
+
patch.captureData = null;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
if (previewRef !== undefined) patch.previewRef = previewRef;
|
|
182
|
+
|
|
183
|
+
await db
|
|
184
|
+
.update(schema.designState)
|
|
185
|
+
.set(patch)
|
|
186
|
+
.where(
|
|
187
|
+
and(
|
|
188
|
+
eq(schema.designState.id, id),
|
|
189
|
+
eq(schema.designState.designId, designId),
|
|
190
|
+
),
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
return {
|
|
194
|
+
id,
|
|
195
|
+
designId,
|
|
196
|
+
updatedAt: now,
|
|
197
|
+
updated: Object.keys(patch).filter((k) => k !== "updatedAt"),
|
|
198
|
+
};
|
|
199
|
+
},
|
|
200
|
+
});
|