@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,362 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* get-design-branch-diff — read action returning a code + visual diff for a
|
|
3
|
+
* design branch (§6.6 of DESIGN-STUDIO-PLAN.md).
|
|
4
|
+
*
|
|
5
|
+
* Two diff axes:
|
|
6
|
+
*
|
|
7
|
+
* 1. **Visual diff** — compares two `design_versions` snapshots (before and
|
|
8
|
+
* after branching). Always available when a `preSnapshotVersionId` is
|
|
9
|
+
* stored on the branch entry. Reuses the same lightweight snapshot-diff
|
|
10
|
+
* approach from `get-design-review.ts` (file presence + content hash).
|
|
11
|
+
*
|
|
12
|
+
* 2. **Code/branch diff** — available only when the source is `fusion` and
|
|
13
|
+
* `diffPatch` capability is advertised. For now this surfaces the branch
|
|
14
|
+
* metadata (name, url, status) and a clear `notAvailable` note when the
|
|
15
|
+
* bridge write path isn't yet hardened. Per the plan, real file-level code
|
|
16
|
+
* diffs land with bridge write hardening (phase 5); the action is structured
|
|
17
|
+
* to receive that data transparently once the bridge proves it.
|
|
18
|
+
*
|
|
19
|
+
* When neither diff axis is available (e.g. inline design without branches),
|
|
20
|
+
* returns `ctaRequired: true` with a "Make it real" CTA.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { defineAction } from "@agent-native/core";
|
|
24
|
+
import { resolveAccess } from "@agent-native/core/sharing";
|
|
25
|
+
import { desc, eq } from "drizzle-orm";
|
|
26
|
+
import { z } from "zod";
|
|
27
|
+
|
|
28
|
+
import { getDb, schema } from "../server/db/index.js";
|
|
29
|
+
import "../server/db/index.js"; // ensure registerShareableResource runs
|
|
30
|
+
import { resolveSourceCapabilities } from "../shared/capability-resolver.js";
|
|
31
|
+
import type {
|
|
32
|
+
VisualDiffEntry,
|
|
33
|
+
VisualDiffChangeKind,
|
|
34
|
+
} from "../shared/design-review.js";
|
|
35
|
+
import { hasCapability } from "../shared/design-source-capabilities.js";
|
|
36
|
+
import { normalizeDesignSourceType } from "../shared/source-mode.js";
|
|
37
|
+
|
|
38
|
+
// ─── Types ────────────────────────────────────────────────────────────────────
|
|
39
|
+
|
|
40
|
+
interface StoredBranchEntry {
|
|
41
|
+
branchName?: string;
|
|
42
|
+
projectId?: string;
|
|
43
|
+
url?: string;
|
|
44
|
+
status?: string;
|
|
45
|
+
purpose?: string | null;
|
|
46
|
+
preSnapshotVersionId?: string | null;
|
|
47
|
+
createdAt?: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
51
|
+
|
|
52
|
+
function parseDesignData(raw: unknown): Record<string, unknown> {
|
|
53
|
+
if (typeof raw !== "string") return {};
|
|
54
|
+
try {
|
|
55
|
+
const parsed: unknown = JSON.parse(raw);
|
|
56
|
+
if (
|
|
57
|
+
parsed !== null &&
|
|
58
|
+
typeof parsed === "object" &&
|
|
59
|
+
!Array.isArray(parsed)
|
|
60
|
+
) {
|
|
61
|
+
return parsed as Record<string, unknown>;
|
|
62
|
+
}
|
|
63
|
+
} catch {
|
|
64
|
+
// Stale JSON — return empty.
|
|
65
|
+
}
|
|
66
|
+
return {};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function parseBranches(
|
|
70
|
+
designData: Record<string, unknown>,
|
|
71
|
+
): StoredBranchEntry[] {
|
|
72
|
+
const raw = designData["branches"];
|
|
73
|
+
if (!Array.isArray(raw)) return [];
|
|
74
|
+
return raw.filter(
|
|
75
|
+
(b): b is StoredBranchEntry =>
|
|
76
|
+
b !== null && typeof b === "object" && !Array.isArray(b),
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Parse a design_versions snapshot JSON into a flat map of
|
|
82
|
+
* `{ filename: { bytes, contentHash } }` for structural comparison.
|
|
83
|
+
* Mirrors the approach in `get-design-review.ts`.
|
|
84
|
+
*/
|
|
85
|
+
function parseSnapshotFiles(
|
|
86
|
+
snapshotRaw: string,
|
|
87
|
+
): Record<string, { bytes: number; content: string | undefined }> {
|
|
88
|
+
try {
|
|
89
|
+
const obj = JSON.parse(snapshotRaw) as unknown;
|
|
90
|
+
if (!obj || typeof obj !== "object" || Array.isArray(obj)) return {};
|
|
91
|
+
const record = obj as Record<string, unknown>;
|
|
92
|
+
const files = record["files"];
|
|
93
|
+
if (!Array.isArray(files)) return {};
|
|
94
|
+
|
|
95
|
+
const out: Record<string, { bytes: number; content: string | undefined }> =
|
|
96
|
+
{};
|
|
97
|
+
for (const file of files) {
|
|
98
|
+
if (!file || typeof file !== "object") continue;
|
|
99
|
+
const f = file as Record<string, unknown>;
|
|
100
|
+
const name = typeof f["filename"] === "string" ? f["filename"] : "?";
|
|
101
|
+
const content =
|
|
102
|
+
typeof f["content"] === "string" ? f["content"] : undefined;
|
|
103
|
+
out[name] = {
|
|
104
|
+
content,
|
|
105
|
+
bytes: typeof content === "string" ? content.length : 0,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
return out;
|
|
109
|
+
} catch {
|
|
110
|
+
return {};
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Produce a visual diff entry list from two snapshot file maps. */
|
|
115
|
+
function diffSnapshotFiles(
|
|
116
|
+
baseFiles: Record<string, { bytes: number; content: string | undefined }>,
|
|
117
|
+
compareFiles: Record<string, { bytes: number; content: string | undefined }>,
|
|
118
|
+
): VisualDiffEntry[] {
|
|
119
|
+
const entries: VisualDiffEntry[] = [];
|
|
120
|
+
const baseKeys = new Set(Object.keys(baseFiles));
|
|
121
|
+
const compareKeys = new Set(Object.keys(compareFiles));
|
|
122
|
+
|
|
123
|
+
for (const key of compareKeys) {
|
|
124
|
+
if (!baseKeys.has(key)) {
|
|
125
|
+
entries.push({
|
|
126
|
+
id: `added:${key}`,
|
|
127
|
+
kind: "added" as VisualDiffChangeKind,
|
|
128
|
+
description: `File added: ${key}`,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
for (const key of baseKeys) {
|
|
134
|
+
if (!compareKeys.has(key)) {
|
|
135
|
+
entries.push({
|
|
136
|
+
id: `removed:${key}`,
|
|
137
|
+
kind: "removed" as VisualDiffChangeKind,
|
|
138
|
+
description: `File removed: ${key}`,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
for (const key of compareKeys) {
|
|
144
|
+
if (baseKeys.has(key)) {
|
|
145
|
+
const baseContent = baseFiles[key]?.content;
|
|
146
|
+
const compareContent = compareFiles[key]?.content;
|
|
147
|
+
const baseBytes = baseFiles[key]?.bytes ?? 0;
|
|
148
|
+
const compareBytes = compareFiles[key]?.bytes ?? 0;
|
|
149
|
+
if (baseContent !== compareContent) {
|
|
150
|
+
entries.push({
|
|
151
|
+
id: `modified:${key}`,
|
|
152
|
+
kind: "modified" as VisualDiffChangeKind,
|
|
153
|
+
description: `File modified: ${key} (${baseBytes}B → ${compareBytes}B)`,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return entries;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// ─── Action ───────────────────────────────────────────────────────────────────
|
|
163
|
+
|
|
164
|
+
export default defineAction({
|
|
165
|
+
description:
|
|
166
|
+
"Read action: return a code + visual diff for a design branch. " +
|
|
167
|
+
"Visual diff compares design_versions snapshots (before and after branching). " +
|
|
168
|
+
"Code/branch diff surfaces branch metadata (name, url, status) from Builder; " +
|
|
169
|
+
"file-level code diffs become available once bridge write hardening is complete " +
|
|
170
|
+
"(currently planned, not available). " +
|
|
171
|
+
"For inline/localhost designs without branches, returns ctaRequired=true with a " +
|
|
172
|
+
"Make-it-real upgrade CTA. " +
|
|
173
|
+
"Pass branchName to target a specific branch; omit for the most recent one.",
|
|
174
|
+
schema: z.object({
|
|
175
|
+
designId: z
|
|
176
|
+
.string()
|
|
177
|
+
.describe("Design project ID to retrieve the branch diff for"),
|
|
178
|
+
branchName: z
|
|
179
|
+
.string()
|
|
180
|
+
.optional()
|
|
181
|
+
.describe(
|
|
182
|
+
"Target branch name. Defaults to the most recently created branch when omitted.",
|
|
183
|
+
),
|
|
184
|
+
baseVersionId: z
|
|
185
|
+
.string()
|
|
186
|
+
.optional()
|
|
187
|
+
.describe(
|
|
188
|
+
"design_versions.id for the base (pre-branch) snapshot to diff against. " +
|
|
189
|
+
"Defaults to the branch's stored preSnapshotVersionId when omitted.",
|
|
190
|
+
),
|
|
191
|
+
compareVersionId: z
|
|
192
|
+
.string()
|
|
193
|
+
.optional()
|
|
194
|
+
.describe(
|
|
195
|
+
"design_versions.id for the compare (post-branch) snapshot. " +
|
|
196
|
+
"Defaults to the most recent design_version when omitted.",
|
|
197
|
+
),
|
|
198
|
+
}),
|
|
199
|
+
readOnly: true,
|
|
200
|
+
http: { method: "GET" },
|
|
201
|
+
run: async ({ designId, branchName, baseVersionId, compareVersionId }) => {
|
|
202
|
+
const db = getDb();
|
|
203
|
+
|
|
204
|
+
// ── Access check ────────────────────────────────────────────────────────
|
|
205
|
+
const access = await resolveAccess("design", designId);
|
|
206
|
+
if (!access) throw new Error("Design not found");
|
|
207
|
+
|
|
208
|
+
const resource = access.resource as { data?: unknown };
|
|
209
|
+
|
|
210
|
+
// ── Source type + capability check ──────────────────────────────────────
|
|
211
|
+
const designData = parseDesignData(resource.data);
|
|
212
|
+
const sourceType =
|
|
213
|
+
normalizeDesignSourceType(designData["sourceType"]) ?? "inline";
|
|
214
|
+
const caps = resolveSourceCapabilities(sourceType);
|
|
215
|
+
|
|
216
|
+
// For inline/localhost without branch capability, return a CTA.
|
|
217
|
+
if (!hasCapability(caps, "branch") && !hasCapability(caps, "diffPatch")) {
|
|
218
|
+
return {
|
|
219
|
+
designId,
|
|
220
|
+
sourceType,
|
|
221
|
+
ctaRequired: true,
|
|
222
|
+
ctaKind:
|
|
223
|
+
sourceType === "fusion"
|
|
224
|
+
? ("connect-builder" as const)
|
|
225
|
+
: ("make-it-real" as const),
|
|
226
|
+
ctaMessage:
|
|
227
|
+
sourceType === "fusion"
|
|
228
|
+
? "Builder is not yet connected. Connect Builder.io to view branch diffs."
|
|
229
|
+
: "Branch diffs require a Builder-hosted app. Use 'Make it real' to upgrade " +
|
|
230
|
+
"this inline design to a real-app source.",
|
|
231
|
+
branch: null,
|
|
232
|
+
visualDiff: [] as VisualDiffEntry[],
|
|
233
|
+
codeDiff: null,
|
|
234
|
+
baseVersionId: null,
|
|
235
|
+
compareVersionId: null,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// ── Resolve branch entry ─────────────────────────────────────────────────
|
|
240
|
+
const branches = parseBranches(designData);
|
|
241
|
+
|
|
242
|
+
let branch: StoredBranchEntry | null = null;
|
|
243
|
+
if (branchName) {
|
|
244
|
+
branch =
|
|
245
|
+
branches.find(
|
|
246
|
+
(b) => b.branchName?.toLowerCase() === branchName.toLowerCase(),
|
|
247
|
+
) ?? null;
|
|
248
|
+
} else {
|
|
249
|
+
// Default to the most recently created branch.
|
|
250
|
+
branch = branches.length > 0 ? branches[branches.length - 1]! : null;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (!branch) {
|
|
254
|
+
return {
|
|
255
|
+
designId,
|
|
256
|
+
sourceType,
|
|
257
|
+
ctaRequired: false,
|
|
258
|
+
ctaKind: null,
|
|
259
|
+
ctaMessage: null,
|
|
260
|
+
branch: null,
|
|
261
|
+
note: "No branch found for this design. Use create-design-branch to create one.",
|
|
262
|
+
visualDiff: [] as VisualDiffEntry[],
|
|
263
|
+
codeDiff: null,
|
|
264
|
+
baseVersionId: null,
|
|
265
|
+
compareVersionId: null,
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// ── Resolve version ids for the visual diff ──────────────────────────────
|
|
270
|
+
// Priority: explicit params > branch's pre-snapshot > most-recent version
|
|
271
|
+
let effectiveBaseId =
|
|
272
|
+
baseVersionId ?? branch.preSnapshotVersionId ?? undefined;
|
|
273
|
+
let effectiveCompareId = compareVersionId;
|
|
274
|
+
|
|
275
|
+
if (!effectiveCompareId) {
|
|
276
|
+
const [latestVersion] = await db
|
|
277
|
+
.select({ id: schema.designVersions.id })
|
|
278
|
+
.from(schema.designVersions)
|
|
279
|
+
.where(eq(schema.designVersions.designId, designId))
|
|
280
|
+
.orderBy(desc(schema.designVersions.createdAt))
|
|
281
|
+
.limit(1);
|
|
282
|
+
effectiveCompareId = latestVersion?.id;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// ── Visual diff ──────────────────────────────────────────────────────────
|
|
286
|
+
let visualDiff: VisualDiffEntry[] = [];
|
|
287
|
+
let resolvedBaseVersionId: string | null = null;
|
|
288
|
+
let resolvedCompareVersionId: string | null = null;
|
|
289
|
+
|
|
290
|
+
if (
|
|
291
|
+
effectiveBaseId &&
|
|
292
|
+
effectiveCompareId &&
|
|
293
|
+
effectiveBaseId !== effectiveCompareId
|
|
294
|
+
) {
|
|
295
|
+
const versionRows = await db
|
|
296
|
+
.select({
|
|
297
|
+
id: schema.designVersions.id,
|
|
298
|
+
snapshot: schema.designVersions.snapshot,
|
|
299
|
+
})
|
|
300
|
+
.from(schema.designVersions)
|
|
301
|
+
.where(eq(schema.designVersions.designId, designId));
|
|
302
|
+
|
|
303
|
+
const byId = Object.fromEntries(
|
|
304
|
+
versionRows.map((r) => [r.id, r.snapshot]),
|
|
305
|
+
);
|
|
306
|
+
|
|
307
|
+
const baseSnap = byId[effectiveBaseId];
|
|
308
|
+
const compareSnap = byId[effectiveCompareId];
|
|
309
|
+
|
|
310
|
+
if (baseSnap && compareSnap) {
|
|
311
|
+
const baseFiles = parseSnapshotFiles(baseSnap);
|
|
312
|
+
const compareFiles = parseSnapshotFiles(compareSnap);
|
|
313
|
+
visualDiff = diffSnapshotFiles(baseFiles, compareFiles);
|
|
314
|
+
resolvedBaseVersionId = effectiveBaseId;
|
|
315
|
+
resolvedCompareVersionId = effectiveCompareId;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// ── Code/branch diff ─────────────────────────────────────────────────────
|
|
320
|
+
// File-level code diffs land with bridge write hardening (phase 5).
|
|
321
|
+
// Surface what is available now: branch metadata + a clear not-available note.
|
|
322
|
+
const codeDiffAvailable = hasCapability(caps, "diffPatch");
|
|
323
|
+
const codeDiff = {
|
|
324
|
+
available: codeDiffAvailable,
|
|
325
|
+
branchName: branch.branchName ?? null,
|
|
326
|
+
projectId: branch.projectId ?? null,
|
|
327
|
+
url: branch.url ?? null,
|
|
328
|
+
status: branch.status ?? null,
|
|
329
|
+
note: codeDiffAvailable
|
|
330
|
+
? "Branch is connected. File-level diffs are available via the Builder Visual Editor."
|
|
331
|
+
: "File-level code diffs are planned pending bridge write hardening. " +
|
|
332
|
+
"The Builder Visual Editor at the branch URL shows the full code diff.",
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
return {
|
|
336
|
+
designId,
|
|
337
|
+
sourceType,
|
|
338
|
+
ctaRequired: false,
|
|
339
|
+
ctaKind: null,
|
|
340
|
+
ctaMessage: null,
|
|
341
|
+
branch: {
|
|
342
|
+
branchName: branch.branchName ?? null,
|
|
343
|
+
projectId: branch.projectId ?? null,
|
|
344
|
+
url: branch.url ?? null,
|
|
345
|
+
status: branch.status ?? null,
|
|
346
|
+
purpose: branch.purpose ?? null,
|
|
347
|
+
createdAt: branch.createdAt ?? null,
|
|
348
|
+
preSnapshotVersionId: branch.preSnapshotVersionId ?? null,
|
|
349
|
+
},
|
|
350
|
+
visualDiff,
|
|
351
|
+
visualDiffSummary: {
|
|
352
|
+
added: visualDiff.filter((d) => d.kind === "added").length,
|
|
353
|
+
removed: visualDiff.filter((d) => d.kind === "removed").length,
|
|
354
|
+
modified: visualDiff.filter((d) => d.kind === "modified").length,
|
|
355
|
+
total: visualDiff.length,
|
|
356
|
+
},
|
|
357
|
+
codeDiff,
|
|
358
|
+
baseVersionId: resolvedBaseVersionId,
|
|
359
|
+
compareVersionId: resolvedCompareVersionId,
|
|
360
|
+
};
|
|
361
|
+
},
|
|
362
|
+
});
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* get-design-review — read-only action returning the latest cached review for
|
|
3
|
+
* a design, optionally including a visual diff between two `design_versions`.
|
|
4
|
+
*
|
|
5
|
+
* The review combines:
|
|
6
|
+
* - A11yFindings from the most recent `design_review_snapshot` row (or empty
|
|
7
|
+
* if no audit has been run yet).
|
|
8
|
+
* - A structural visual diff between `baseVersionId` and `compareVersionId`
|
|
9
|
+
* when both are provided: each design_versions snapshot is parsed as JSON
|
|
10
|
+
* and compared for layer/style deltas using the existing `design_versions`
|
|
11
|
+
* table — no new tables needed.
|
|
12
|
+
*
|
|
13
|
+
* See DESIGN-STUDIO-PLAN.md §6.5 + §7 (Review surface).
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { defineAction } from "@agent-native/core";
|
|
17
|
+
import { accessFilter } from "@agent-native/core/sharing";
|
|
18
|
+
import { and, desc, eq } from "drizzle-orm";
|
|
19
|
+
import { z } from "zod";
|
|
20
|
+
|
|
21
|
+
import { getDb, schema } from "../server/db/index.js";
|
|
22
|
+
import "../server/db/index.js"; // ensure registerShareableResource runs
|
|
23
|
+
import type {
|
|
24
|
+
A11yFinding,
|
|
25
|
+
DesignReviewSnapshot,
|
|
26
|
+
DesignReviewStatus,
|
|
27
|
+
VisualDiffChangeKind,
|
|
28
|
+
VisualDiffEntry,
|
|
29
|
+
} from "../shared/design-review.js";
|
|
30
|
+
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
// Visual diff helpers (operate on stored JSON snapshots)
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Parse a design_versions snapshot JSON into a flat map of
|
|
37
|
+
* `{ nodeId: { ... style properties ... } }` for diff comparison.
|
|
38
|
+
*
|
|
39
|
+
* The snapshot format is whatever `generate-design` / `create-design-version`
|
|
40
|
+
* stores — typically an object with a `files` array. We extract a lightweight
|
|
41
|
+
* set of each file's token/style references for structural comparison.
|
|
42
|
+
*/
|
|
43
|
+
function parseSnapshotNodes(
|
|
44
|
+
snapshotRaw: string,
|
|
45
|
+
): Record<string, Record<string, unknown>> {
|
|
46
|
+
try {
|
|
47
|
+
const obj = JSON.parse(snapshotRaw) as unknown;
|
|
48
|
+
if (!obj || typeof obj !== "object" || Array.isArray(obj)) return {};
|
|
49
|
+
const record = obj as Record<string, unknown>;
|
|
50
|
+
|
|
51
|
+
// Flatten files → content strings into a map keyed by filename.
|
|
52
|
+
const files = record["files"];
|
|
53
|
+
if (!Array.isArray(files)) return {};
|
|
54
|
+
|
|
55
|
+
const out: Record<string, Record<string, unknown>> = {};
|
|
56
|
+
for (const file of files) {
|
|
57
|
+
if (!file || typeof file !== "object") continue;
|
|
58
|
+
const f = file as Record<string, unknown>;
|
|
59
|
+
const name = typeof f["filename"] === "string" ? f["filename"] : "?";
|
|
60
|
+
const content =
|
|
61
|
+
typeof f["content"] === "string" ? f["content"] : undefined;
|
|
62
|
+
out[name] = {
|
|
63
|
+
content,
|
|
64
|
+
bytes: typeof content === "string" ? content.length : 0,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
return out;
|
|
68
|
+
} catch {
|
|
69
|
+
return {};
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Produce a visual diff entry list by comparing two snapshot node maps. */
|
|
74
|
+
function diffSnapshotNodes(
|
|
75
|
+
baseNodes: Record<string, Record<string, unknown>>,
|
|
76
|
+
compareNodes: Record<string, Record<string, unknown>>,
|
|
77
|
+
): VisualDiffEntry[] {
|
|
78
|
+
const entries: VisualDiffEntry[] = [];
|
|
79
|
+
const baseKeys = new Set(Object.keys(baseNodes));
|
|
80
|
+
const compareKeys = new Set(Object.keys(compareNodes));
|
|
81
|
+
|
|
82
|
+
// Added files
|
|
83
|
+
for (const key of compareKeys) {
|
|
84
|
+
if (!baseKeys.has(key)) {
|
|
85
|
+
entries.push({
|
|
86
|
+
id: `added:${key}`,
|
|
87
|
+
kind: "added" as VisualDiffChangeKind,
|
|
88
|
+
description: `File added: ${key}`,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Removed files
|
|
94
|
+
for (const key of baseKeys) {
|
|
95
|
+
if (!compareKeys.has(key)) {
|
|
96
|
+
entries.push({
|
|
97
|
+
id: `removed:${key}`,
|
|
98
|
+
kind: "removed" as VisualDiffChangeKind,
|
|
99
|
+
description: `File removed: ${key}`,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Modified files (byte-length change as the cheapest proxy for content diff)
|
|
105
|
+
for (const key of compareKeys) {
|
|
106
|
+
if (baseKeys.has(key)) {
|
|
107
|
+
const baseBytes = baseNodes[key]?.["bytes"] ?? 0;
|
|
108
|
+
const compareBytes = compareNodes[key]?.["bytes"] ?? 0;
|
|
109
|
+
const baseContent = baseNodes[key]?.["content"];
|
|
110
|
+
const compareContent = compareNodes[key]?.["content"];
|
|
111
|
+
if (baseContent !== compareContent) {
|
|
112
|
+
entries.push({
|
|
113
|
+
id: `modified:${key}`,
|
|
114
|
+
kind: "modified" as VisualDiffChangeKind,
|
|
115
|
+
description: `File modified: ${key} (${baseBytes}B → ${compareBytes}B)`,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return entries;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ---------------------------------------------------------------------------
|
|
125
|
+
// Action
|
|
126
|
+
// ---------------------------------------------------------------------------
|
|
127
|
+
|
|
128
|
+
export default defineAction({
|
|
129
|
+
description:
|
|
130
|
+
"Return the latest accessibility review findings for a design, " +
|
|
131
|
+
"plus an optional structural visual diff between two design_versions. " +
|
|
132
|
+
"Pass baseVersionId and compareVersionId to include the diff; omit them " +
|
|
133
|
+
"for a findings-only response. Read-only — run-design-audit must be " +
|
|
134
|
+
"called first to populate the findings; this action reads the cached result.",
|
|
135
|
+
schema: z.object({
|
|
136
|
+
designId: z
|
|
137
|
+
.string()
|
|
138
|
+
.describe("Design project ID to retrieve the review for"),
|
|
139
|
+
sourceRef: z
|
|
140
|
+
.string()
|
|
141
|
+
.optional()
|
|
142
|
+
.describe(
|
|
143
|
+
"Opaque source ref (design_files.id or route id) to scope the review to a specific screen. " +
|
|
144
|
+
"When omitted, returns the most recent design-level snapshot.",
|
|
145
|
+
),
|
|
146
|
+
baseVersionId: z
|
|
147
|
+
.string()
|
|
148
|
+
.optional()
|
|
149
|
+
.describe(
|
|
150
|
+
"design_versions.id for the older (base) version to diff against.",
|
|
151
|
+
),
|
|
152
|
+
compareVersionId: z
|
|
153
|
+
.string()
|
|
154
|
+
.optional()
|
|
155
|
+
.describe(
|
|
156
|
+
"design_versions.id for the newer (compare) version. " +
|
|
157
|
+
"If omitted when baseVersionId is set, defaults to the most recent version.",
|
|
158
|
+
),
|
|
159
|
+
}),
|
|
160
|
+
readOnly: true,
|
|
161
|
+
http: { method: "GET" },
|
|
162
|
+
run: async ({ designId, sourceRef, baseVersionId, compareVersionId }) => {
|
|
163
|
+
const db = getDb();
|
|
164
|
+
|
|
165
|
+
// Verify access to the design.
|
|
166
|
+
const [design] = await db
|
|
167
|
+
.select({ id: schema.designs.id })
|
|
168
|
+
.from(schema.designs)
|
|
169
|
+
.where(
|
|
170
|
+
and(
|
|
171
|
+
accessFilter(schema.designs, schema.designShares),
|
|
172
|
+
eq(schema.designs.id, designId),
|
|
173
|
+
),
|
|
174
|
+
)
|
|
175
|
+
.limit(1);
|
|
176
|
+
|
|
177
|
+
if (!design) {
|
|
178
|
+
const err = new Error("Design not found") as Error & {
|
|
179
|
+
statusCode: number;
|
|
180
|
+
};
|
|
181
|
+
err.statusCode = 404;
|
|
182
|
+
throw err;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// -----------------------------------------------------------------------
|
|
186
|
+
// Load the latest review snapshot for this design (+ optional sourceRef).
|
|
187
|
+
// -----------------------------------------------------------------------
|
|
188
|
+
const snapshotConditions = [
|
|
189
|
+
eq(schema.designReviewSnapshot.designId, designId),
|
|
190
|
+
];
|
|
191
|
+
if (sourceRef) {
|
|
192
|
+
snapshotConditions.push(
|
|
193
|
+
eq(schema.designReviewSnapshot.sourceRef, sourceRef),
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const [snapshotRow] = await db
|
|
198
|
+
.select()
|
|
199
|
+
.from(schema.designReviewSnapshot)
|
|
200
|
+
.where(and(...snapshotConditions))
|
|
201
|
+
.orderBy(desc(schema.designReviewSnapshot.createdAt))
|
|
202
|
+
.limit(1);
|
|
203
|
+
|
|
204
|
+
let a11yFindings: A11yFinding[] = [];
|
|
205
|
+
let snapshotId: string | null = null;
|
|
206
|
+
let auditedAt: string | null = null;
|
|
207
|
+
let snapshotStatus: DesignReviewStatus = "pending";
|
|
208
|
+
|
|
209
|
+
if (snapshotRow) {
|
|
210
|
+
snapshotId = snapshotRow.id;
|
|
211
|
+
auditedAt = snapshotRow.createdAt ?? null;
|
|
212
|
+
snapshotStatus = (snapshotRow.status ?? "pending") as DesignReviewStatus;
|
|
213
|
+
try {
|
|
214
|
+
const parsed = snapshotRow.a11yFindings
|
|
215
|
+
? (JSON.parse(snapshotRow.a11yFindings) as unknown)
|
|
216
|
+
: [];
|
|
217
|
+
if (Array.isArray(parsed)) {
|
|
218
|
+
a11yFindings = parsed as A11yFinding[];
|
|
219
|
+
}
|
|
220
|
+
} catch {
|
|
221
|
+
a11yFindings = [];
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// -----------------------------------------------------------------------
|
|
226
|
+
// Visual diff between two design_versions (optional).
|
|
227
|
+
// -----------------------------------------------------------------------
|
|
228
|
+
let visualDiff: VisualDiffEntry[] = [];
|
|
229
|
+
let resolvedBaseVersionId: string | null = null;
|
|
230
|
+
let resolvedCompareVersionId: string | null = null;
|
|
231
|
+
|
|
232
|
+
const wantDiff = !!(baseVersionId || compareVersionId);
|
|
233
|
+
|
|
234
|
+
if (wantDiff) {
|
|
235
|
+
// Resolve compare version (most recent if not specified).
|
|
236
|
+
let effectiveCompareId = compareVersionId;
|
|
237
|
+
if (!effectiveCompareId) {
|
|
238
|
+
const [latestVersion] = await db
|
|
239
|
+
.select({ id: schema.designVersions.id })
|
|
240
|
+
.from(schema.designVersions)
|
|
241
|
+
.where(eq(schema.designVersions.designId, designId))
|
|
242
|
+
.orderBy(desc(schema.designVersions.createdAt))
|
|
243
|
+
.limit(1);
|
|
244
|
+
effectiveCompareId = latestVersion?.id;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (baseVersionId && effectiveCompareId) {
|
|
248
|
+
const versionRows = await db
|
|
249
|
+
.select({
|
|
250
|
+
id: schema.designVersions.id,
|
|
251
|
+
snapshot: schema.designVersions.snapshot,
|
|
252
|
+
})
|
|
253
|
+
.from(schema.designVersions)
|
|
254
|
+
.where(and(eq(schema.designVersions.designId, designId)));
|
|
255
|
+
|
|
256
|
+
const byId = Object.fromEntries(
|
|
257
|
+
versionRows.map((r) => [r.id, r.snapshot]),
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
const baseSnapshot = byId[baseVersionId];
|
|
261
|
+
const compareSnapshot = byId[effectiveCompareId];
|
|
262
|
+
|
|
263
|
+
if (baseSnapshot && compareSnapshot) {
|
|
264
|
+
const baseNodes = parseSnapshotNodes(baseSnapshot);
|
|
265
|
+
const compareNodes = parseSnapshotNodes(compareSnapshot);
|
|
266
|
+
visualDiff = diffSnapshotNodes(baseNodes, compareNodes);
|
|
267
|
+
resolvedBaseVersionId = baseVersionId;
|
|
268
|
+
resolvedCompareVersionId = effectiveCompareId;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// -----------------------------------------------------------------------
|
|
274
|
+
// Build the response shape matching DesignReviewSnapshot.
|
|
275
|
+
// -----------------------------------------------------------------------
|
|
276
|
+
const review: DesignReviewSnapshot = {
|
|
277
|
+
id: snapshotId ?? "",
|
|
278
|
+
designId,
|
|
279
|
+
sourceRef: sourceRef ?? null,
|
|
280
|
+
baseVersionId: resolvedBaseVersionId,
|
|
281
|
+
compareVersionId: resolvedCompareVersionId,
|
|
282
|
+
a11yFindings,
|
|
283
|
+
visualDiff,
|
|
284
|
+
status: snapshotStatus,
|
|
285
|
+
createdAt: auditedAt ?? new Date().toISOString(),
|
|
286
|
+
updatedAt: auditedAt ?? new Date().toISOString(),
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
return {
|
|
290
|
+
designId,
|
|
291
|
+
snapshotId,
|
|
292
|
+
auditedAt,
|
|
293
|
+
status: snapshotStatus,
|
|
294
|
+
a11yFindings,
|
|
295
|
+
a11ySummary: {
|
|
296
|
+
errors: a11yFindings.filter((f) => f.severity === "error").length,
|
|
297
|
+
warnings: a11yFindings.filter((f) => f.severity === "warning").length,
|
|
298
|
+
info: a11yFindings.filter((f) => f.severity === "info").length,
|
|
299
|
+
total: a11yFindings.length,
|
|
300
|
+
},
|
|
301
|
+
visualDiff,
|
|
302
|
+
diffSummary: {
|
|
303
|
+
added: visualDiff.filter((d) => d.kind === "added").length,
|
|
304
|
+
removed: visualDiff.filter((d) => d.kind === "removed").length,
|
|
305
|
+
modified: visualDiff.filter((d) => d.kind === "modified").length,
|
|
306
|
+
moved: visualDiff.filter((d) => d.kind === "moved").length,
|
|
307
|
+
total: visualDiff.length,
|
|
308
|
+
},
|
|
309
|
+
baseVersionId: resolvedBaseVersionId,
|
|
310
|
+
compareVersionId: resolvedCompareVersionId,
|
|
311
|
+
review,
|
|
312
|
+
};
|
|
313
|
+
},
|
|
314
|
+
});
|