@agent-native/core 0.80.8 → 0.80.9
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 +12 -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/mcp.ts +10 -0
- package/corpus/core/src/cli/skills.ts +58 -12
- 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/actions/apply-visual-edit.ts +24 -4
- 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-design.ts +1 -0
- package/corpus/templates/design/actions/present-design-variants.ts +355 -30
- package/corpus/templates/design/actions/view-screen.ts +75 -31
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +329 -69
- package/corpus/templates/design/app/components/design/DesignEditorSkeleton.tsx +1 -1
- package/corpus/templates/design/app/components/design/EditPanel.tsx +479 -77
- package/corpus/templates/design/app/components/design/LayersPanel.tsx +380 -216
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +1050 -196
- package/corpus/templates/design/app/components/design/QuestionFlow.tsx +83 -206
- package/corpus/templates/design/app/components/design/TweaksPanel.tsx +1 -1
- package/corpus/templates/design/app/components/design/index.ts +0 -1
- package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +225 -133
- package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +122 -11
- 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/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/global.css +77 -49
- package/corpus/templates/design/app/hooks/use-question-flow.ts +2 -2
- package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +8 -1
- package/corpus/templates/design/app/i18n/zh-TW.ts +3 -0
- package/corpus/templates/design/app/i18n-data.ts +45 -3
- package/corpus/templates/design/app/pages/DesignEditor.tsx +2602 -657
- package/corpus/templates/design/app/root.tsx +1 -1
- 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-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-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-locking-layer-nesting-generated-question-intake-and-im.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-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-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/e2e/README.md +3 -3
- package/corpus/templates/design/e2e/global-setup.ts +11 -22
- package/corpus/templates/design/e2e/helpers.ts +122 -11
- package/corpus/templates/design/playwright.config.ts +9 -3
- 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/routes/api/design-handoff/[id].zip.get.ts +86 -0
- package/corpus/templates/design/shared/code-layer.ts +125 -22
- package/corpus/templates/design/shared/generation-session.ts +41 -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/DocumentArea.tsx +6 -7
- package/corpus/templates/plan/app/global.css +74 -42
- 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/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 +58 -12
- 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/collab/routes.d.ts +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +2 -2
- 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/notifications/routes.d.ts +2 -2
- package/dist/observability/routes.d.ts +8 -8
- 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/resources/handlers.d.ts +2 -2
- 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/server/transcribe-voice.d.ts +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
|
@@ -56,10 +56,20 @@ const sourceSchema = z.preprocess(
|
|
|
56
56
|
}),
|
|
57
57
|
);
|
|
58
58
|
|
|
59
|
-
const targetSchema = z
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
59
|
+
const targetSchema = z
|
|
60
|
+
.object({
|
|
61
|
+
nodeId: z.string().optional(),
|
|
62
|
+
selector: z.string().optional(),
|
|
63
|
+
})
|
|
64
|
+
.superRefine((target, ctx) => {
|
|
65
|
+
if (!target.nodeId && !target.selector) {
|
|
66
|
+
ctx.addIssue({
|
|
67
|
+
code: z.ZodIssueCode.custom,
|
|
68
|
+
path: ["nodeId"],
|
|
69
|
+
message: "target.nodeId or target.selector is required",
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
});
|
|
63
73
|
|
|
64
74
|
const intentSchema = z.preprocess(
|
|
65
75
|
parseJsonString,
|
|
@@ -168,6 +178,16 @@ async function resolveEditableDesignFile(
|
|
|
168
178
|
if (file.fileType !== "html") {
|
|
169
179
|
throw new Error("Visual code-layer edits only support HTML files for now.");
|
|
170
180
|
}
|
|
181
|
+
if (source.designId && file.designId !== source.designId) {
|
|
182
|
+
throw new Error(
|
|
183
|
+
`source.designId "${source.designId}" does not match file "${file.id}"`,
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
if (source.filename && file.filename !== source.filename) {
|
|
187
|
+
throw new Error(
|
|
188
|
+
`source.filename "${source.filename}" does not match file "${file.id}"`,
|
|
189
|
+
);
|
|
190
|
+
}
|
|
171
191
|
|
|
172
192
|
await assertAccess("design", file.designId, "editor");
|
|
173
193
|
|
|
@@ -11,6 +11,7 @@ import { z } from "zod";
|
|
|
11
11
|
import { schema } from "../server/db/index.js";
|
|
12
12
|
import {
|
|
13
13
|
buildCodingHandoffPrompt,
|
|
14
|
+
buildHandoffZipUrl,
|
|
14
15
|
buildRawHandoffUrl,
|
|
15
16
|
normalizeHandoffFormat,
|
|
16
17
|
} from "../server/lib/coding-handoff.js";
|
|
@@ -30,9 +31,9 @@ function designDeepLink(designId: string): string {
|
|
|
30
31
|
|
|
31
32
|
export default defineAction({
|
|
32
33
|
description:
|
|
33
|
-
"Turn a design into code: create
|
|
34
|
-
"
|
|
35
|
-
"
|
|
34
|
+
"Turn a design into code: create an agent handoff for a design project. " +
|
|
35
|
+
"Returns tokenized raw and ZIP URLs external agents can fetch, plus a " +
|
|
36
|
+
"ready-to-copy prompt. The bundle reflects the design's CURRENT " +
|
|
36
37
|
"state — live editor (collab) content plus the user's applied visual " +
|
|
37
38
|
"tweaks resolved into the HTML :root — so the generated code matches the " +
|
|
38
39
|
"tuned design, not the original generated tokens. This is the canonical " +
|
|
@@ -56,7 +57,7 @@ export default defineAction({
|
|
|
56
57
|
mcpApp: {
|
|
57
58
|
compactCatalog: true,
|
|
58
59
|
resource: embedApp({
|
|
59
|
-
title: "Design
|
|
60
|
+
title: "Design agent handoff",
|
|
60
61
|
description: "Open the design in the real Design editor.",
|
|
61
62
|
iframeTitle: "Agent-Native Design",
|
|
62
63
|
openLabel: "Open design",
|
|
@@ -97,8 +98,14 @@ export default defineAction({
|
|
|
97
98
|
origin: resolvedOrigin,
|
|
98
99
|
format: handoffFormat,
|
|
99
100
|
});
|
|
101
|
+
const zipUrl = buildHandoffZipUrl({
|
|
102
|
+
id,
|
|
103
|
+
token,
|
|
104
|
+
origin: resolvedOrigin,
|
|
105
|
+
});
|
|
100
106
|
const prompt = buildCodingHandoffPrompt({
|
|
101
107
|
rawUrl,
|
|
108
|
+
zipUrl,
|
|
102
109
|
title: design.title,
|
|
103
110
|
fileCount: snapshot.files.length,
|
|
104
111
|
});
|
|
@@ -106,6 +113,7 @@ export default defineAction({
|
|
|
106
113
|
return {
|
|
107
114
|
designId: id,
|
|
108
115
|
rawUrl,
|
|
116
|
+
zipUrl,
|
|
109
117
|
prompt,
|
|
110
118
|
clipboardText: prompt,
|
|
111
119
|
format: handoffFormat,
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { defineAction, embedApp } from "@agent-native/core";
|
|
2
2
|
import {
|
|
3
|
-
deleteAppState,
|
|
4
3
|
readAppState,
|
|
5
4
|
writeAppState,
|
|
6
5
|
} from "@agent-native/core/application-state";
|
|
@@ -26,6 +25,7 @@ import {
|
|
|
26
25
|
import {
|
|
27
26
|
designGenerationSessionKey,
|
|
28
27
|
type DesignGenerationSession,
|
|
28
|
+
updateGenerationSessionWithSavedFiles,
|
|
29
29
|
} from "../shared/generation-session.js";
|
|
30
30
|
|
|
31
31
|
/** Editor deep link so external agents can surface "Open design". */
|
|
@@ -57,28 +57,13 @@ async function updateGenerationSessionForSavedFiles(
|
|
|
57
57
|
const session = rawSession as unknown as DesignGenerationSession;
|
|
58
58
|
if (session.designId !== designId || !Array.isArray(session.frames)) return;
|
|
59
59
|
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
? {
|
|
64
|
-
...frame,
|
|
65
|
-
status: "done" as const,
|
|
66
|
-
step: "Saved",
|
|
67
|
-
progress: 1,
|
|
68
|
-
}
|
|
69
|
-
: frame,
|
|
60
|
+
const nextSession = updateGenerationSessionWithSavedFiles(
|
|
61
|
+
session,
|
|
62
|
+
savedFilenames,
|
|
70
63
|
);
|
|
71
|
-
|
|
72
|
-
if (allDone) {
|
|
73
|
-
await deleteAppState(key);
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
64
|
+
if (nextSession === session) return;
|
|
76
65
|
|
|
77
|
-
await writeAppState(key,
|
|
78
|
-
...session,
|
|
79
|
-
status: "generating",
|
|
80
|
-
frames,
|
|
81
|
-
} as unknown as Record<string, unknown>);
|
|
66
|
+
await writeAppState(key, nextSession as unknown as Record<string, unknown>);
|
|
82
67
|
}
|
|
83
68
|
|
|
84
69
|
export default defineAction({
|
|
@@ -51,7 +51,7 @@ const requestedScreenSchema = z
|
|
|
51
51
|
.optional()
|
|
52
52
|
.describe("Target filename for this screen, such as onboarding.html"),
|
|
53
53
|
role: z.enum(["screen", "variant"]).optional().default("screen"),
|
|
54
|
-
variantOf: z.string().optional(),
|
|
54
|
+
variantOf: z.string().trim().min(1).optional(),
|
|
55
55
|
})
|
|
56
56
|
.superRefine((screen, ctx) => {
|
|
57
57
|
if (screen.role === "variant" && !screen.variantOf) {
|
|
@@ -80,7 +80,48 @@ export default defineAction({
|
|
|
80
80
|
screens: z
|
|
81
81
|
.preprocess(
|
|
82
82
|
(value) => (typeof value === "string" ? JSON.parse(value) : value),
|
|
83
|
-
z
|
|
83
|
+
z
|
|
84
|
+
.array(requestedScreenSchema)
|
|
85
|
+
.min(1)
|
|
86
|
+
.max(8)
|
|
87
|
+
.superRefine((screens, ctx) => {
|
|
88
|
+
const requestAnchors = new Map<
|
|
89
|
+
string,
|
|
90
|
+
{ index: number; role: DesignGenerationFrame["role"] }
|
|
91
|
+
>();
|
|
92
|
+
screens.forEach((screen, index) => {
|
|
93
|
+
const anchors = [screen.frameId, screen.filename, screen.title];
|
|
94
|
+
for (const anchor of anchors) {
|
|
95
|
+
if (!anchor || requestAnchors.has(anchor)) continue;
|
|
96
|
+
requestAnchors.set(anchor, {
|
|
97
|
+
index,
|
|
98
|
+
role: screen.role,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
screens.forEach((screen, index) => {
|
|
104
|
+
if (screen.role !== "variant" || !screen.variantOf) return;
|
|
105
|
+
const anchor = requestAnchors.get(screen.variantOf);
|
|
106
|
+
if (!anchor) return;
|
|
107
|
+
if (anchor.index === index) {
|
|
108
|
+
ctx.addIssue({
|
|
109
|
+
code: z.ZodIssueCode.custom,
|
|
110
|
+
path: [index, "variantOf"],
|
|
111
|
+
message: "variant screens cannot point to themselves",
|
|
112
|
+
});
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
if (anchor.role === "variant") {
|
|
116
|
+
ctx.addIssue({
|
|
117
|
+
code: z.ZodIssueCode.custom,
|
|
118
|
+
path: [index, "variantOf"],
|
|
119
|
+
message:
|
|
120
|
+
"variant screens must anchor to a base screen, not another variant",
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
}),
|
|
84
125
|
)
|
|
85
126
|
.describe("Screens or variants that should be generated on the canvas"),
|
|
86
127
|
designSystemId: z
|
|
@@ -1,64 +1,245 @@
|
|
|
1
1
|
import { defineAction, embedApp } from "@agent-native/core";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
deleteAppState,
|
|
4
|
+
writeAppState,
|
|
5
|
+
writeAppStateForCurrentTab,
|
|
6
|
+
} from "@agent-native/core/application-state";
|
|
7
|
+
import { seedFromText } from "@agent-native/core/collab";
|
|
3
8
|
import { buildDeepLink } from "@agent-native/core/server";
|
|
4
9
|
import { assertAccess } from "@agent-native/core/sharing";
|
|
10
|
+
import { eq } from "drizzle-orm";
|
|
11
|
+
import { nanoid } from "nanoid";
|
|
5
12
|
import { z } from "zod";
|
|
6
13
|
|
|
7
14
|
import "../server/db/index.js"; // ensure registerShareableResource runs
|
|
15
|
+
import { getDb, schema } from "../server/db/index.js";
|
|
16
|
+
import {
|
|
17
|
+
mergeCanvasFramePlacements,
|
|
18
|
+
type CanvasFramePlacement,
|
|
19
|
+
} from "../shared/canvas-frames.js";
|
|
20
|
+
|
|
21
|
+
const VARIANT_GAP = 96;
|
|
22
|
+
const MAX_COLUMNS = 3;
|
|
23
|
+
const MOBILE_WIDTH = 390;
|
|
24
|
+
const MOBILE_HEIGHT = 844;
|
|
25
|
+
const TABLET_WIDTH = 768;
|
|
26
|
+
const TABLET_HEIGHT = 1024;
|
|
27
|
+
const DESKTOP_WIDTH = 1280;
|
|
28
|
+
const DESKTOP_HEIGHT = 900;
|
|
8
29
|
|
|
9
30
|
function designDeepLink(designId: string): string {
|
|
10
31
|
return buildDeepLink({
|
|
11
32
|
app: "design",
|
|
12
33
|
view: "editor",
|
|
13
|
-
params: { designId },
|
|
14
|
-
|
|
15
|
-
// Claude Code) so the editor shows a copyable paste-back summary after the
|
|
16
|
-
// user picks — the choice can't ride a host chat bridge there.
|
|
17
|
-
to: `/design/${encodeURIComponent(designId)}?handoff=chat`,
|
|
34
|
+
params: { designId, editorView: "overview" },
|
|
35
|
+
to: `/design/${encodeURIComponent(designId)}?view=overview`,
|
|
18
36
|
});
|
|
19
37
|
}
|
|
20
38
|
|
|
21
39
|
const FALLBACK_INSTRUCTIONS =
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
|
|
26
|
-
'editorial one") — honor that too instead of insisting on the paste-back.';
|
|
40
|
+
"The generated directions have been saved as normal screens on the Design " +
|
|
41
|
+
"overview board. The chat shows one button per screen. Ask the user to pick " +
|
|
42
|
+
"a screen by name if the inline buttons are not available; after they pick, " +
|
|
43
|
+
"delete the other variant screens and continue from the kept screen.";
|
|
27
44
|
|
|
28
45
|
const variantSchema = z.object({
|
|
29
46
|
id: z.string().min(1).describe("Stable variant id, e.g. 'minimal-focus'"),
|
|
30
47
|
label: z
|
|
31
48
|
.string()
|
|
32
49
|
.min(1)
|
|
33
|
-
.describe("Short user-facing
|
|
50
|
+
.describe("Short user-facing screen name, e.g. 'One-Line Focus'"),
|
|
34
51
|
content: z
|
|
35
52
|
.string()
|
|
36
53
|
.min(1)
|
|
37
54
|
.describe(
|
|
38
|
-
"Complete self-contained HTML document for this variant. Inline the CSS needed for the preview; avoid relying on external CSS/script CDNs because
|
|
55
|
+
"Complete self-contained HTML document for this variant. Inline the CSS needed for the preview; avoid relying on external CSS/script CDNs because app sandboxes may block them.",
|
|
56
|
+
),
|
|
57
|
+
width: z
|
|
58
|
+
.number()
|
|
59
|
+
.positive()
|
|
60
|
+
.optional()
|
|
61
|
+
.describe(
|
|
62
|
+
"Optional source viewport/artboard width for the overview frame.",
|
|
63
|
+
),
|
|
64
|
+
height: z
|
|
65
|
+
.number()
|
|
66
|
+
.positive()
|
|
67
|
+
.optional()
|
|
68
|
+
.describe(
|
|
69
|
+
"Optional source viewport/artboard height for the overview frame.",
|
|
39
70
|
),
|
|
40
71
|
});
|
|
41
72
|
|
|
73
|
+
interface VariantScreen {
|
|
74
|
+
id: string;
|
|
75
|
+
variantId: string;
|
|
76
|
+
label: string;
|
|
77
|
+
filename: string;
|
|
78
|
+
width: number;
|
|
79
|
+
height: number;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
83
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function parseJsonRecord(value: string | null | undefined) {
|
|
87
|
+
if (!value) return {};
|
|
88
|
+
try {
|
|
89
|
+
const parsed = JSON.parse(value);
|
|
90
|
+
return isRecord(parsed) ? parsed : {};
|
|
91
|
+
} catch {
|
|
92
|
+
return {};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function slugify(value: string, fallback: string) {
|
|
97
|
+
return (
|
|
98
|
+
value
|
|
99
|
+
.trim()
|
|
100
|
+
.toLowerCase()
|
|
101
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
102
|
+
.replace(/^-+|-+$/g, "")
|
|
103
|
+
.slice(0, 52) || fallback
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function optionName(index: number) {
|
|
108
|
+
return `Option ${String.fromCharCode(65 + index)}`;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function uniqueFilename(preferred: string, used: Set<string>) {
|
|
112
|
+
const dot = preferred.lastIndexOf(".");
|
|
113
|
+
const stem = dot > 0 ? preferred.slice(0, dot) : preferred;
|
|
114
|
+
const ext = dot > 0 ? preferred.slice(dot) : ".html";
|
|
115
|
+
let candidate = `${stem}${ext}`;
|
|
116
|
+
let suffix = 2;
|
|
117
|
+
while (used.has(candidate)) {
|
|
118
|
+
candidate = `${stem}-${suffix}${ext}`;
|
|
119
|
+
suffix += 1;
|
|
120
|
+
}
|
|
121
|
+
used.add(candidate);
|
|
122
|
+
return candidate;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function firstCssPixelValue(content: string, property: string) {
|
|
126
|
+
const escaped = property.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
127
|
+
const pattern = new RegExp(`${escaped}\\s*:\\s*(\\d{2,4})px`, "i");
|
|
128
|
+
const match = content.match(pattern);
|
|
129
|
+
if (!match) return undefined;
|
|
130
|
+
const value = Number(match[1]);
|
|
131
|
+
return Number.isFinite(value) ? value : undefined;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function boundedDimension(value: unknown, min: number, max: number) {
|
|
135
|
+
return typeof value === "number" && Number.isFinite(value)
|
|
136
|
+
? Math.min(max, Math.max(min, Math.round(value)))
|
|
137
|
+
: undefined;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function inferVariantSize(variant: z.infer<typeof variantSchema>) {
|
|
141
|
+
const explicitWidth = boundedDimension(variant.width, 240, 1920);
|
|
142
|
+
const explicitHeight = boundedDimension(variant.height, 240, 3000);
|
|
143
|
+
if (explicitWidth && explicitHeight) {
|
|
144
|
+
return { width: explicitWidth, height: explicitHeight };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const content = variant.content;
|
|
148
|
+
const cssWidth =
|
|
149
|
+
firstCssPixelValue(content, "width") ??
|
|
150
|
+
firstCssPixelValue(content, "max-width") ??
|
|
151
|
+
firstCssPixelValue(content, "min-width");
|
|
152
|
+
const cssHeight =
|
|
153
|
+
firstCssPixelValue(content, "height") ??
|
|
154
|
+
firstCssPixelValue(content, "min-height");
|
|
155
|
+
const inferredWidth = boundedDimension(cssWidth, 240, 1920);
|
|
156
|
+
const inferredHeight = boundedDimension(cssHeight, 240, 3000);
|
|
157
|
+
if (inferredWidth && inferredWidth <= 560) {
|
|
158
|
+
return {
|
|
159
|
+
width: explicitWidth ?? inferredWidth,
|
|
160
|
+
height: explicitHeight ?? inferredHeight ?? MOBILE_HEIGHT,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
if (inferredWidth && inferredHeight) {
|
|
164
|
+
return {
|
|
165
|
+
width: explicitWidth ?? inferredWidth,
|
|
166
|
+
height: explicitHeight ?? inferredHeight,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const lowercase = content.toLowerCase();
|
|
171
|
+
if (
|
|
172
|
+
/\b(?:mobile|phone|iphone|android)\b/.test(lowercase) ||
|
|
173
|
+
/\b(?:max-w-sm|max-w-md|w-\[(?:360|375|390|393|414)px\])\b/.test(lowercase)
|
|
174
|
+
) {
|
|
175
|
+
return {
|
|
176
|
+
width: explicitWidth ?? MOBILE_WIDTH,
|
|
177
|
+
height: explicitHeight ?? MOBILE_HEIGHT,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
if (/\b(?:tablet|ipad)\b/.test(lowercase)) {
|
|
181
|
+
return {
|
|
182
|
+
width: explicitWidth ?? TABLET_WIDTH,
|
|
183
|
+
height: explicitHeight ?? TABLET_HEIGHT,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return {
|
|
188
|
+
width: explicitWidth ?? DESKTOP_WIDTH,
|
|
189
|
+
height: explicitHeight ?? DESKTOP_HEIGHT,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function placeVariantScreens(screens: VariantScreen[]) {
|
|
194
|
+
const placements: CanvasFramePlacement[] = [];
|
|
195
|
+
const columns = Math.min(MAX_COLUMNS, Math.max(1, screens.length));
|
|
196
|
+
let rowY = 0;
|
|
197
|
+
|
|
198
|
+
for (let rowStart = 0; rowStart < screens.length; rowStart += columns) {
|
|
199
|
+
const row = screens.slice(rowStart, rowStart + columns);
|
|
200
|
+
let x = 0;
|
|
201
|
+
let rowHeight = 0;
|
|
202
|
+
|
|
203
|
+
for (const [offset, screen] of row.entries()) {
|
|
204
|
+
placements.push({
|
|
205
|
+
fileId: screen.id,
|
|
206
|
+
filename: screen.filename,
|
|
207
|
+
x,
|
|
208
|
+
y: rowY,
|
|
209
|
+
width: screen.width,
|
|
210
|
+
height: screen.height,
|
|
211
|
+
z: rowStart + offset,
|
|
212
|
+
});
|
|
213
|
+
x += screen.width + VARIANT_GAP;
|
|
214
|
+
rowHeight = Math.max(rowHeight, screen.height);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
rowY += rowHeight + VARIANT_GAP;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return placements;
|
|
221
|
+
}
|
|
222
|
+
|
|
42
223
|
export default defineAction({
|
|
43
224
|
description:
|
|
44
|
-
"Present generated design directions
|
|
45
|
-
"
|
|
46
|
-
"sweet spot). Use this for design
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
|
|
225
|
+
"Present generated design directions as normal screens on the Design " +
|
|
226
|
+
"overview board and ask the user to choose one with inline chat buttons. " +
|
|
227
|
+
"Provide 2-5 variants (3 is the sweet spot). Use this for design " +
|
|
228
|
+
"exploration before follow-up refinement. After the user's choice, keep " +
|
|
229
|
+
"the chosen screen, delete the other generated variant screens, and " +
|
|
230
|
+
"continue from the kept screen.",
|
|
50
231
|
schema: z.object({
|
|
51
232
|
designId: z.string().describe("Design project ID to show variants for"),
|
|
52
233
|
prompt: z
|
|
53
234
|
.string()
|
|
54
235
|
.optional()
|
|
55
|
-
.describe("Caption shown above the variant
|
|
236
|
+
.describe("Caption shown in chat above the variant choice buttons"),
|
|
56
237
|
variants: z
|
|
57
238
|
.array(variantSchema)
|
|
58
239
|
.min(2)
|
|
59
240
|
.max(5)
|
|
60
241
|
.describe(
|
|
61
|
-
"2-5 concise, visually distinct generated design options to
|
|
242
|
+
"2-5 concise, visually distinct generated design options to place as overview screens (3 is the sweet spot). Inline CSS so all options render in the app preview.",
|
|
62
243
|
),
|
|
63
244
|
}),
|
|
64
245
|
mcpApp: {
|
|
@@ -66,30 +247,174 @@ export default defineAction({
|
|
|
66
247
|
resource: embedApp({
|
|
67
248
|
title: "Design directions",
|
|
68
249
|
description:
|
|
69
|
-
"Open the Design editor with
|
|
250
|
+
"Open the Design editor with generated directions on the overview board.",
|
|
70
251
|
iframeTitle: "Agent-Native Design",
|
|
71
|
-
openLabel: "Open
|
|
252
|
+
openLabel: "Open screen overview",
|
|
72
253
|
height: 720,
|
|
73
254
|
}),
|
|
74
255
|
},
|
|
75
256
|
run: async ({ designId, prompt, variants }) => {
|
|
76
257
|
await assertAccess("design", designId, "editor");
|
|
77
258
|
|
|
78
|
-
|
|
259
|
+
const db = getDb();
|
|
260
|
+
const now = new Date().toISOString();
|
|
261
|
+
const [design] = await db
|
|
262
|
+
.select({ data: schema.designs.data })
|
|
263
|
+
.from(schema.designs)
|
|
264
|
+
.where(eq(schema.designs.id, designId))
|
|
265
|
+
.limit(1);
|
|
266
|
+
const existingFiles = await db
|
|
267
|
+
.select()
|
|
268
|
+
.from(schema.designFiles)
|
|
269
|
+
.where(eq(schema.designFiles.designId, designId));
|
|
270
|
+
const usedFilenames = new Set(existingFiles.map((file) => file.filename));
|
|
271
|
+
const variantSetId = nanoid();
|
|
272
|
+
const screens: VariantScreen[] = [];
|
|
273
|
+
|
|
274
|
+
for (let index = 0; index < variants.length; index += 1) {
|
|
275
|
+
const variant = variants[index]!;
|
|
276
|
+
const label = variant.label.trim() || optionName(index);
|
|
277
|
+
const slug = slugify(label, `option-${index + 1}`);
|
|
278
|
+
const preferredFilename = `variant-${slug}.html`;
|
|
279
|
+
const filename = uniqueFilename(preferredFilename, usedFilenames);
|
|
280
|
+
const fileId = nanoid();
|
|
281
|
+
const { width, height } = inferVariantSize(variant);
|
|
282
|
+
|
|
283
|
+
await db.insert(schema.designFiles).values({
|
|
284
|
+
id: fileId,
|
|
285
|
+
designId,
|
|
286
|
+
filename,
|
|
287
|
+
fileType: "html",
|
|
288
|
+
content: variant.content,
|
|
289
|
+
createdAt: now,
|
|
290
|
+
updatedAt: now,
|
|
291
|
+
});
|
|
292
|
+
await seedFromText(fileId, variant.content);
|
|
293
|
+
|
|
294
|
+
screens.push({
|
|
295
|
+
id: fileId,
|
|
296
|
+
variantId: variant.id,
|
|
297
|
+
label,
|
|
298
|
+
filename,
|
|
299
|
+
width,
|
|
300
|
+
height,
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const placements = placeVariantScreens(screens);
|
|
305
|
+
await db.transaction(async (tx) => {
|
|
306
|
+
const [currentDesign] = await tx
|
|
307
|
+
.select({ data: schema.designs.data })
|
|
308
|
+
.from(schema.designs)
|
|
309
|
+
.where(eq(schema.designs.id, designId))
|
|
310
|
+
.limit(1);
|
|
311
|
+
const prevData = parseJsonRecord(currentDesign?.data ?? design?.data);
|
|
312
|
+
const mergedFrames = mergeCanvasFramePlacements({
|
|
313
|
+
existing: prevData.canvasFrames,
|
|
314
|
+
placements,
|
|
315
|
+
resolveFileId: (placement) => placement.fileId,
|
|
316
|
+
});
|
|
317
|
+
const previousMetadata = isRecord(prevData.screenMetadata)
|
|
318
|
+
? { ...prevData.screenMetadata }
|
|
319
|
+
: {};
|
|
320
|
+
const previousVariantSets = isRecord(prevData.designVariantSets)
|
|
321
|
+
? { ...prevData.designVariantSets }
|
|
322
|
+
: {};
|
|
323
|
+
for (const screen of screens) {
|
|
324
|
+
previousMetadata[screen.id] = {
|
|
325
|
+
sourceType: "inline",
|
|
326
|
+
previewState: "preview",
|
|
327
|
+
title: screen.label,
|
|
328
|
+
width: screen.width,
|
|
329
|
+
height: screen.height,
|
|
330
|
+
variantSetId,
|
|
331
|
+
variantId: screen.variantId,
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
previousVariantSets[variantSetId] = {
|
|
335
|
+
id: variantSetId,
|
|
336
|
+
prompt: prompt ?? "Pick a direction",
|
|
337
|
+
createdAt: now,
|
|
338
|
+
screens: screens.map((screen) => ({
|
|
339
|
+
id: screen.id,
|
|
340
|
+
variantId: screen.variantId,
|
|
341
|
+
label: screen.label,
|
|
342
|
+
filename: screen.filename,
|
|
343
|
+
width: screen.width,
|
|
344
|
+
height: screen.height,
|
|
345
|
+
})),
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
await tx
|
|
349
|
+
.update(schema.designs)
|
|
350
|
+
.set({
|
|
351
|
+
data: JSON.stringify({
|
|
352
|
+
...prevData,
|
|
353
|
+
canvasFrames: mergedFrames.canvasFrames,
|
|
354
|
+
screenMetadata: previousMetadata,
|
|
355
|
+
designVariantSets: previousVariantSets,
|
|
356
|
+
updatedAt: now,
|
|
357
|
+
}),
|
|
358
|
+
updatedAt: now,
|
|
359
|
+
})
|
|
360
|
+
.where(eq(schema.designs.id, designId));
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
await writeAppState("navigate", {
|
|
364
|
+
view: "editor",
|
|
79
365
|
designId,
|
|
80
|
-
|
|
81
|
-
|
|
366
|
+
editorView: "overview",
|
|
367
|
+
path: `/design/${encodeURIComponent(designId)}?view=overview`,
|
|
368
|
+
});
|
|
369
|
+
await writeAppStateForCurrentTab("guided-questions", {
|
|
370
|
+
title: prompt ?? "Pick a direction",
|
|
371
|
+
description:
|
|
372
|
+
"All options are on the board. Choose the one to keep and I will continue from it.",
|
|
373
|
+
submitLabel: "Use selected direction",
|
|
374
|
+
submitMessage: "Use this design direction.",
|
|
375
|
+
skipLabel: "Show another set",
|
|
376
|
+
skipMessage: "None of these directions are right.",
|
|
377
|
+
questions: [
|
|
378
|
+
{
|
|
379
|
+
id: "variant",
|
|
380
|
+
type: "text-options",
|
|
381
|
+
question: "Which screen should I keep?",
|
|
382
|
+
required: true,
|
|
383
|
+
allowOther: false,
|
|
384
|
+
includeExplore: false,
|
|
385
|
+
includeDecide: false,
|
|
386
|
+
submitOnSelect: true,
|
|
387
|
+
options: screens.map((screen, index) => {
|
|
388
|
+
const otherScreens = screens
|
|
389
|
+
.filter((other) => other.id !== screen.id)
|
|
390
|
+
.map(
|
|
391
|
+
(other) =>
|
|
392
|
+
`${other.label} (${other.filename}, file id ${other.id})`,
|
|
393
|
+
)
|
|
394
|
+
.join("; ");
|
|
395
|
+
return {
|
|
396
|
+
label: screen.label || optionName(index),
|
|
397
|
+
value:
|
|
398
|
+
`Keep "${screen.label}" (${screen.filename}, file id ${screen.id}) ` +
|
|
399
|
+
`from variant set ${variantSetId}. Delete the other variant screens: ${otherScreens}.`,
|
|
400
|
+
};
|
|
401
|
+
}),
|
|
402
|
+
},
|
|
403
|
+
],
|
|
82
404
|
});
|
|
405
|
+
await deleteAppState("design-variants").catch(() => false);
|
|
83
406
|
|
|
84
407
|
return {
|
|
85
408
|
designId,
|
|
86
409
|
prompt: prompt ?? "Pick a direction",
|
|
87
|
-
|
|
88
|
-
|
|
410
|
+
variantSetId,
|
|
411
|
+
count: screens.length,
|
|
412
|
+
screens,
|
|
413
|
+
path: `/design/${encodeURIComponent(designId)}?view=overview`,
|
|
89
414
|
embed: true,
|
|
90
415
|
fallbackInstructions: FALLBACK_INSTRUCTIONS,
|
|
91
416
|
nextRequiredAction:
|
|
92
|
-
"Wait for the user to pick a variant
|
|
417
|
+
"Wait for the user to pick a screen in chat. Then delete the unchosen variant screens with delete-file and continue from the chosen screen.",
|
|
93
418
|
};
|
|
94
419
|
},
|
|
95
420
|
link: ({ result }) => {
|
|
@@ -98,7 +423,7 @@ export default defineAction({
|
|
|
98
423
|
if (!designId) return null;
|
|
99
424
|
return {
|
|
100
425
|
url: designDeepLink(designId),
|
|
101
|
-
label: "Open
|
|
426
|
+
label: "Open screen overview",
|
|
102
427
|
view: "editor",
|
|
103
428
|
};
|
|
104
429
|
},
|