@agent-native/core 0.148.0 → 0.149.0

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.
Files changed (165) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/changelog/2026-08-10-wide-tables-in-analytics-chat-now-scroll-horizontally-within.md +6 -0
  3. package/corpus/templates/calendar/app/components/calendar/DayView.tsx +13 -53
  4. package/corpus/templates/calendar/app/components/calendar/WeekView.tsx +11 -70
  5. package/corpus/templates/calendar/app/lib/event-layout.ts +122 -0
  6. package/corpus/templates/calendar/app/routes/_app.availability.tsx +1 -1
  7. package/corpus/templates/calendar/changelog/2026-08-10-opening-availability-settings-from-the-agent-lands-on-the-av.md +6 -0
  8. package/corpus/templates/calendar/vitest.config.ts +6 -1
  9. package/corpus/templates/clips/actions/lib/audio-only-transcription.ts +3 -1
  10. package/corpus/templates/clips/app/components/player/video-player.tsx +44 -4
  11. package/corpus/templates/clips/changelog/2026-08-10-fixed-transcription-failures-for-recordings-without-captured.md +6 -0
  12. package/corpus/templates/clips/changelog/2026-08-10-mobile-clips-can-now-enter-fullscreen-on-iphone-safari.md +6 -0
  13. package/corpus/templates/content/actions/_property-utils.ts +11 -1
  14. package/corpus/templates/content/actions/view-screen.ts +62 -12
  15. package/corpus/templates/content/server/plugins/agent-chat.ts +2 -0
  16. package/corpus/templates/design/.agents/skills/design-templates/SKILL.md +22 -0
  17. package/corpus/templates/design/AGENTS.md +3 -0
  18. package/corpus/templates/design/actions/create-design-from-template.ts +19 -0
  19. package/corpus/templates/design/actions/get-design-snapshot.ts +18 -0
  20. package/corpus/templates/design/actions/get-design-template.ts +178 -0
  21. package/corpus/templates/design/actions/list-designs.ts +3 -1
  22. package/corpus/templates/design/actions/view-screen.ts +31 -0
  23. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +225 -214
  24. package/corpus/templates/design/app/components/design/multi-screen/types.ts +3 -0
  25. package/corpus/templates/design/app/i18n-data.ts +78 -0
  26. package/corpus/templates/design/app/lib/design-authors.ts +64 -0
  27. package/corpus/templates/design/app/pages/Index.tsx +114 -4
  28. package/corpus/templates/design/app/pages/design-editor/generation-prompt-directives.ts +1 -0
  29. package/corpus/templates/design/changelog/2026-08-07-alt-dragging-an-element-to-copy-it-now-keeps-the-copy-visibl.md +6 -0
  30. package/corpus/templates/design/changelog/2026-08-07-design-cards-now-show-who-created-each-design-with-a-filter-.md +6 -0
  31. package/corpus/templates/design/changelog/2026-08-07-drawing-a-shape-on-the-overview-canvas-now-keeps-the-shape-s.md +6 -0
  32. package/corpus/templates/design/changelog/2026-08-07-hold-option-alt-while-dragging-a-frame-on-the-overview-canva.md +6 -0
  33. package/corpus/templates/design/server/lib/design-template-data.ts +143 -0
  34. package/corpus/templates/design/server/plugins/agent-chat.ts +1 -0
  35. package/corpus/templates/forms/AGENTS.md +2 -2
  36. package/corpus/templates/forms/actions/create-form.ts +1 -1
  37. package/corpus/templates/forms/actions/update-form.ts +5 -5
  38. package/corpus/templates/forms/changelog/2026-08-10-forms-now-keep-agent-applied-field-changes-visible-after-an-.md +6 -0
  39. package/corpus/templates/forms/changelog/2026-08-10-published-form-links-always-use-the-working-public-form-url.md +6 -0
  40. package/corpus/templates/forms/server/plugins/agent-chat.ts +2 -2
  41. package/corpus/templates/slides/AGENTS.md +4 -3
  42. package/corpus/templates/slides/actions/import-google-slides-reference.ts +56 -10
  43. package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +12 -74
  44. package/corpus/templates/slides/app/components/editor/GeneratingSlidePreview.tsx +5 -1
  45. package/corpus/templates/slides/app/components/editor/GoogleDriveConnectionCta.tsx +15 -4
  46. package/corpus/templates/slides/app/components/editor/NewDeckReferenceStep.tsx +116 -55
  47. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +1 -0
  48. package/corpus/templates/slides/app/components/onboarding/FirstDeckOnboardingFlow.tsx +143 -18
  49. package/corpus/templates/slides/app/i18n/en-US.ts +6 -0
  50. package/corpus/templates/slides/app/lib/generation-state.ts +7 -1
  51. package/corpus/templates/slides/app/pages/DeckEditor.tsx +32 -14
  52. package/corpus/templates/slides/app/pages/Index.tsx +110 -128
  53. package/corpus/templates/slides/changelog/2026-08-10-imported-reference-decks-now-show-clear-success-and-stay-sel.md +6 -0
  54. package/corpus/templates/slides/changelog/2026-08-10-pasted-google-slides-links-import-after-reconnecting-google-.md +6 -0
  55. package/corpus/templates/slides/server/handlers/google-docs-auth.ts +4 -0
  56. package/corpus/templates/slides/server/lib/google-docs-oauth.ts +13 -0
  57. package/corpus/templates/slides/server/plugins/agent-chat.ts +1 -1
  58. package/dist/agent/engine/registry.d.ts +8 -2
  59. package/dist/agent/engine/registry.js +63 -29
  60. package/dist/agent/production-agent.js +7 -0
  61. package/dist/agent/run-manager.d.ts +5 -4
  62. package/dist/agent/run-manager.js +6 -5
  63. package/dist/cli/claude-code-participant.d.ts +2 -1
  64. package/dist/cli/claude-code-participant.js +19 -0
  65. package/dist/cli/code-agent-executor.js +223 -0
  66. package/dist/cli/create-workspace.js +3 -3
  67. package/dist/cli/create.d.ts +3 -2
  68. package/dist/cli/create.js +20 -24
  69. package/dist/client/AgentPanel.js +8 -4
  70. package/dist/client/AssistantChat.js +34 -1
  71. package/dist/client/agent-chat-adapter.d.ts +1 -0
  72. package/dist/client/agent-chat-adapter.js +10 -2
  73. package/dist/client/blocks/library/diagram.js +1 -1
  74. package/dist/client/blocks/library/sanitize-html.js +66 -2
  75. package/dist/client/blocks/library/wireframe-kit.js +69 -0
  76. package/dist/client/chat/markdown-renderer.d.ts +1 -0
  77. package/dist/client/chat/markdown-renderer.js +4 -0
  78. package/dist/client/chat/message-components.d.ts +1 -0
  79. package/dist/client/chat/message-components.js +9 -1
  80. package/dist/client/chat/tool-call-display.d.ts +8 -0
  81. package/dist/client/chat/tool-call-display.js +30 -21
  82. package/dist/client/chat-first/app-pane.js +1 -1
  83. package/dist/client/chat-first/apps-rail.d.ts +1 -1
  84. package/dist/client/chat-first/apps-rail.js +12 -12
  85. package/dist/client/chat-first/chat-history.d.ts +1 -1
  86. package/dist/client/chat-first/chat-history.js +2 -0
  87. package/dist/client/chat-first/copy.js +1 -0
  88. package/dist/client/chat-first/primary-nav.js +1 -1
  89. package/dist/client/chat-first/types.d.ts +1 -0
  90. package/dist/client/resources/McpConnectionSuggestion.js +2 -2
  91. package/dist/client/settings/SettingsTabsPage.d.ts +3 -1
  92. package/dist/client/settings/SettingsTabsPage.js +2 -2
  93. package/dist/client/sse-event-processor.d.ts +8 -0
  94. package/dist/client/sse-event-processor.js +13 -6
  95. package/dist/collab/struct-routes.d.ts +1 -1
  96. package/dist/deploy/build.d.ts +5 -7
  97. package/dist/deploy/build.js +19 -12
  98. package/dist/localization/default-messages.js +1 -1
  99. package/dist/observability/routes.d.ts +3 -3
  100. package/dist/provider-api/actions/provider-api.d.ts +2 -2
  101. package/dist/scripts/call-agent.js +3 -3
  102. package/dist/scripts/describe-workspace-apps.js +1 -1
  103. package/dist/secrets/routes.d.ts +6 -6
  104. package/dist/server/agent-capabilities.js +0 -0
  105. package/dist/server/agent-chat/context-tools.js +1 -1
  106. package/dist/server/agent-chat/prompt-resources.js +1 -1
  107. package/dist/server/agent-chat-plugin.js +1 -1
  108. package/dist/server/auth.js +190 -42
  109. package/dist/server/better-auth-migrations.js +53 -0
  110. package/dist/server/http-response-telemetry.js +7 -1
  111. package/dist/server/onboarding-html.js +119 -33
  112. package/dist/server/transcribe-voice.d.ts +1 -1
  113. package/dist/shared/password-policy.d.ts +2 -1
  114. package/dist/shared/password-policy.js +2 -1
  115. package/dist/styles/agent-conversation.css +20 -3
  116. package/dist/styles/agent-native.css +14 -2
  117. package/dist/styles/blocks.css +132 -0
  118. package/dist/templates/default/app/i18n/ar-SA.ts +1 -1
  119. package/dist/templates/default/app/i18n/de-DE.ts +1 -1
  120. package/dist/templates/default/app/i18n/en-US.ts +1 -1
  121. package/dist/templates/default/app/i18n/es-ES.ts +1 -1
  122. package/dist/templates/default/app/i18n/fr-FR.ts +1 -1
  123. package/dist/templates/default/app/i18n/hi-IN.ts +1 -1
  124. package/dist/templates/default/app/i18n/ja-JP.ts +1 -1
  125. package/dist/templates/default/app/i18n/ko-KR.ts +1 -1
  126. package/dist/templates/default/app/i18n/pt-BR.ts +1 -1
  127. package/dist/templates/default/app/i18n/zh-CN.ts +1 -1
  128. package/dist/templates/default/app/i18n/zh-TW.ts +1 -1
  129. package/dist/templates/default/pnpm-workspace.yaml +1 -0
  130. package/dist/templates/workspace-root/pnpm-workspace.yaml +2 -0
  131. package/docs/content/actions.mdx +4 -1
  132. package/docs/content/agent-surfaces.mdx +8 -6
  133. package/docs/content/automations.mdx +1 -1
  134. package/docs/content/cloneable-saas.mdx +6 -6
  135. package/docs/content/database.mdx +6 -1
  136. package/docs/content/deployment.mdx +10 -8
  137. package/docs/content/docs-components.mdx +22 -14
  138. package/docs/content/drop-in-agent.mdx +1 -1
  139. package/docs/content/environment-variables.mdx +4 -3
  140. package/docs/content/getting-started-actions.mdx +5 -4
  141. package/docs/content/getting-started-pages.mdx +7 -6
  142. package/docs/content/getting-started.mdx +4 -3
  143. package/docs/content/notifications.mdx +6 -6
  144. package/docs/content/progress.mdx +5 -5
  145. package/docs/content/routing.mdx +3 -1
  146. package/docs/content/server-database.mdx +13 -4
  147. package/docs/content/server-overview.mdx +9 -9
  148. package/docs/content/server-plugins.mdx +10 -4
  149. package/docs/content/server-routes.mdx +7 -2
  150. package/docs/content/tracking.mdx +4 -4
  151. package/docs/content/what-is-agent-native.mdx +4 -4
  152. package/package.json +2 -2
  153. package/src/templates/default/app/i18n/ar-SA.ts +1 -1
  154. package/src/templates/default/app/i18n/de-DE.ts +1 -1
  155. package/src/templates/default/app/i18n/en-US.ts +1 -1
  156. package/src/templates/default/app/i18n/es-ES.ts +1 -1
  157. package/src/templates/default/app/i18n/fr-FR.ts +1 -1
  158. package/src/templates/default/app/i18n/hi-IN.ts +1 -1
  159. package/src/templates/default/app/i18n/ja-JP.ts +1 -1
  160. package/src/templates/default/app/i18n/ko-KR.ts +1 -1
  161. package/src/templates/default/app/i18n/pt-BR.ts +1 -1
  162. package/src/templates/default/app/i18n/zh-CN.ts +1 -1
  163. package/src/templates/default/app/i18n/zh-TW.ts +1 -1
  164. package/src/templates/default/pnpm-workspace.yaml +1 -0
  165. package/src/templates/workspace-root/pnpm-workspace.yaml +2 -0
@@ -43,6 +43,8 @@ export default createAgentChatPlugin({
43
43
  resolveOrgId: async (event) => (await getOrgContext(event)).orgId,
44
44
  systemPrompt: `You are an AI document assistant. You manage documents, comments, media blocks, sharing, and connected Notion content through actions and shared application state.
45
45
 
46
+ The current screen is already included as bounded context on every message. Do not call view-screen at the start of a turn or repeatedly; call it only when that context is stale. Its document body is a preview, so call get-document when the full page content is required.
47
+
46
48
  Some less-common tool schemas are loaded on demand. Use tool-search with a specific query when you need a capability that is not already available as a direct tool.
47
49
 
48
50
  Provider-specific Content actions are shortcuts, not limits. If a first-class action cannot express the exact Notion endpoint, page/database/comment object, filter, request body, pagination mode, markdown endpoint, payload shape, or API version needed, call provider-api-catalog and provider-api-docs as needed, then call provider-api-request against the real Notion API. Use the raw provider API escape hatch instead of weakening the answer, broadening filters, or claiming Content cannot do something the underlying Notion API can do.
@@ -38,6 +38,28 @@ description: >-
38
38
  dimensions, linked design system, and locked-layer boundaries before
39
39
  reporting completion.
40
40
 
41
+ ## The Template On Follow-Up Requests
42
+
43
+ A copied screen is edited in place. After the first refinement saves, the
44
+ design's own files are the *result*, not the template — so a later turn that
45
+ reads only `get-design-snapshot` has no idea what the template specified, and
46
+ fonts and artboard dimensions drift a little further on each request.
47
+
48
+ The two facts that drift are cheap, so they arrive on their own. `view-screen`
49
+ reports `design.createdFromTemplate` on every turn for a template-created
50
+ design, carrying `lockedDimensions` per screen and `lockedFonts`. Both are
51
+ captured from the template at copy time, so they describe the template even
52
+ after the design has been edited many times. Honour them in every
53
+ `edit-design` pass: never resize the artboard, change a `canvasFrames` width or
54
+ height, switch the primary viewport, or substitute a typeface to fit new
55
+ content.
56
+
57
+ Call `get-design-template --designId="<id>"` when you need more than those two
58
+ facts — the template's original markup, or its locked layers — for example
59
+ before a structural edit or when the user asks how far the design has moved
60
+ from its template. The full template files are large, which is why they are a
61
+ deliberate second call rather than part of every turn.
62
+
41
63
  ## Locked Layers
42
64
 
43
65
  `data-agent-native-locked="true"` is authoritative. Keep each locked element
@@ -42,6 +42,9 @@ ladder.
42
42
  - Resolve templates or prior designs with `list-design-templates` and
43
43
  `list-designs`, copy with `create-design-from-template`, then inspect and
44
44
  adapt copied files with `get-design-snapshot` and `edit-design`.
45
+ - Copied template screens are edited in place. Preserve
46
+ `createdFromTemplate.lockedDimensions`/`lockedFonts` from `view-screen` in
47
+ every edit; `get-design-template` returns the original.
45
48
 
46
49
  ## Core Rules
47
50
 
@@ -11,8 +11,10 @@ import { z } from "zod";
11
11
 
12
12
  import { getDb, schema } from "../server/db/index.js";
13
13
  import {
14
+ extractTemplateFonts,
14
15
  redactTemplateDesignData,
15
16
  remapTemplateFileIds,
17
+ templateFileDimensions,
16
18
  } from "../server/lib/design-template-data.js";
17
19
  import { getDesignTemplatePreset } from "../shared/design-template-presets.js";
18
20
  import { countLockedLayersAcrossFiles } from "../shared/locked-layers.js";
@@ -149,6 +151,11 @@ export default defineAction({
149
151
  redactTemplateDesignData(templateData),
150
152
  fileIdMap,
151
153
  );
154
+ // The copied screens are edited in place, so the design's own files stop
155
+ // being evidence of what the template looked like after the first
156
+ // refinement. Capturing the small facts here — which template file backs
157
+ // each screen, its exact frame, and the declared fonts — is what lets
158
+ // every later turn restate them without re-reading the template.
152
159
  data.templateSource = {
153
160
  templateId,
154
161
  title: templateTitle,
@@ -158,6 +165,18 @@ export default defineAction({
158
165
  templateDesignSystemId,
159
166
  appliedDesignSystemId: linkedDesignSystemId,
160
167
  designSystemOverridden,
168
+ files: files.map((file) => {
169
+ const designFileId = fileIdMap.get(file.id)!;
170
+ const { width, height } = templateFileDimensions(data, designFileId);
171
+ return {
172
+ designFileId,
173
+ templateFileId: file.id,
174
+ filename: file.filename,
175
+ width,
176
+ height,
177
+ };
178
+ }),
179
+ fonts: extractTemplateFonts(files.map((file) => file.content).join("\n")),
161
180
  };
162
181
  if (prompt) data.templatePrompt = prompt;
163
182
 
@@ -5,6 +5,10 @@ import { z } from "zod";
5
5
 
6
6
  import { schema } from "../server/db/index.js";
7
7
  import { buildDesignSnapshot } from "../server/lib/design-snapshot.js";
8
+ import {
9
+ parseDesignTemplateData,
10
+ readDesignTemplateSource,
11
+ } from "../server/lib/design-template-data.js";
8
12
  import { lockedLayerSnapshots } from "../shared/locked-layers.js";
9
13
  import "../server/db/index.js"; // ensure registerShareableResource runs
10
14
 
@@ -66,6 +70,9 @@ export default defineAction({
66
70
  const design = access.resource as typeof schema.designs.$inferSelect;
67
71
 
68
72
  const snapshot = await buildDesignSnapshot(designId, design.data);
73
+ const templateSource = readDesignTemplateSource(
74
+ parseDesignTemplateData(design.data),
75
+ );
69
76
  const requestedFileId = fileId?.trim();
70
77
  const requestedFilename = filename?.trim();
71
78
  const files = requestedFileId
@@ -97,6 +104,17 @@ export default defineAction({
97
104
  projectType: design.projectType,
98
105
  designSystemId: design.designSystemId ?? null,
99
106
  updatedAt: design.updatedAt,
107
+ ...(templateSource
108
+ ? {
109
+ createdFromTemplate: {
110
+ templateId: templateSource.templateId,
111
+ title: templateSource.title,
112
+ note:
113
+ "These files are edited copies of the template, not the template itself. " +
114
+ `Call \`get-design-template --designId="${designId}"\` for the original canvas dimensions, typography, and locked layers, and preserve them in this edit.`,
115
+ },
116
+ }
117
+ : {}),
100
118
  files: files.map((f) => ({
101
119
  id: f.id,
102
120
  filename: f.filename,
@@ -0,0 +1,178 @@
1
+ import { defineAction } from "@agent-native/core";
2
+ import { resolveAccess } from "@agent-native/core/sharing";
3
+ import { eq } from "drizzle-orm";
4
+ import { z } from "zod";
5
+
6
+ import { getDb, schema } from "../server/db/index.js";
7
+ import {
8
+ parseDesignTemplateData,
9
+ readDesignTemplateSource,
10
+ templateFileDimensions,
11
+ } from "../server/lib/design-template-data.js";
12
+ import { getDesignTemplatePreset } from "../shared/design-template-presets.js";
13
+ import { lockedLayerSnapshots } from "../shared/locked-layers.js";
14
+
15
+ interface OriginalTemplateFile {
16
+ templateFileId: string;
17
+ filename: string;
18
+ fileType: string;
19
+ content: string;
20
+ width: number | null;
21
+ height: number | null;
22
+ }
23
+
24
+ export default defineAction({
25
+ description:
26
+ "Read the ORIGINAL, unmodified template a design was created from. " +
27
+ "Copied template screens are edited in place, so the design's own files stop showing what the template looked like as soon as the first refinement lands. " +
28
+ "Call this on any follow-up request against a template-created design to recover the template's exact canvas dimensions, typography, and markup before editing. " +
29
+ "Pass designId to resolve the template behind an open design, or templateId to read a template directly. Read-only.",
30
+ schema: z
31
+ .object({
32
+ designId: z
33
+ .string()
34
+ .optional()
35
+ .describe("Design created from a template; resolves its template"),
36
+ templateId: z
37
+ .string()
38
+ .optional()
39
+ .describe("Template to read directly when the design id is unknown"),
40
+ })
41
+ .refine((input) => input.designId || input.templateId, {
42
+ message: "Pass designId or templateId",
43
+ }),
44
+ readOnly: true,
45
+ http: { method: "GET" },
46
+ run: async ({ designId, templateId }) => {
47
+ let resolvedTemplateId = templateId;
48
+ let designFileIdByTemplateFileId = new Map<string, string>();
49
+ let instantiatedAt: string | null = null;
50
+
51
+ if (designId) {
52
+ const designAccess = await resolveAccess("design", designId);
53
+ if (!designAccess) throw new Error("Design not found");
54
+ const design =
55
+ designAccess.resource as typeof schema.designs.$inferSelect;
56
+ const source = readDesignTemplateSource(
57
+ parseDesignTemplateData(design.data),
58
+ );
59
+ if (!source) {
60
+ return {
61
+ designId,
62
+ fromTemplate: false as const,
63
+ message:
64
+ "This design was not created from a template; there is no original template to compare against.",
65
+ };
66
+ }
67
+ if (templateId && templateId !== source.templateId) {
68
+ throw new Error(
69
+ `Design ${designId} was created from template "${source.templateId}", not "${templateId}"`,
70
+ );
71
+ }
72
+ resolvedTemplateId = source.templateId;
73
+ instantiatedAt = source.instantiatedAt;
74
+ designFileIdByTemplateFileId = new Map(
75
+ source.files.map(
76
+ (file) => [file.templateFileId, file.designFileId] as const,
77
+ ),
78
+ );
79
+ }
80
+
81
+ if (!resolvedTemplateId) throw new Error("Pass designId or templateId");
82
+
83
+ const preset = getDesignTemplatePreset(resolvedTemplateId);
84
+ let title: string;
85
+ let category: string;
86
+ let description: string | null;
87
+ let templateDesignSystemId: string | null;
88
+ let files: OriginalTemplateFile[];
89
+
90
+ if (preset) {
91
+ title = preset.title;
92
+ category = preset.category;
93
+ description = preset.description;
94
+ templateDesignSystemId = null;
95
+ files = [
96
+ {
97
+ templateFileId: `file:${preset.id}`,
98
+ filename: preset.filename,
99
+ fileType: "html",
100
+ content: preset.content,
101
+ width: preset.width,
102
+ height: preset.height,
103
+ },
104
+ ];
105
+ } else {
106
+ const access = await resolveAccess("design-template", resolvedTemplateId);
107
+ if (!access) throw new Error("Template not found");
108
+ const template = access.resource;
109
+ title = String(template.title ?? "Untitled template");
110
+ category = String(template.category ?? "other");
111
+ description =
112
+ typeof template.description === "string" ? template.description : null;
113
+ const rawDesignSystemId =
114
+ typeof template.designSystemId === "string"
115
+ ? template.designSystemId
116
+ : null;
117
+ templateDesignSystemId = rawDesignSystemId
118
+ ? (await resolveAccess("design-system", rawDesignSystemId))
119
+ ? rawDesignSystemId
120
+ : null
121
+ : null;
122
+
123
+ const templateData = parseDesignTemplateData(
124
+ typeof template.data === "string" ? template.data : "{}",
125
+ );
126
+ const fallbackWidth =
127
+ typeof template.width === "number" ? template.width : null;
128
+ const fallbackHeight =
129
+ typeof template.height === "number" ? template.height : null;
130
+
131
+ const rows = await getDb()
132
+ .select({
133
+ id: schema.designTemplateFiles.id,
134
+ filename: schema.designTemplateFiles.filename,
135
+ fileType: schema.designTemplateFiles.fileType,
136
+ content: schema.designTemplateFiles.content,
137
+ })
138
+ .from(schema.designTemplateFiles)
139
+ .where(eq(schema.designTemplateFiles.templateId, resolvedTemplateId));
140
+
141
+ files = rows.map((row) => {
142
+ const frame = templateFileDimensions(templateData, row.id);
143
+ return {
144
+ templateFileId: row.id,
145
+ filename: row.filename,
146
+ fileType: row.fileType,
147
+ content: row.content,
148
+ width: frame.width ?? fallbackWidth,
149
+ height: frame.height ?? fallbackHeight,
150
+ };
151
+ });
152
+ }
153
+
154
+ return {
155
+ templateId: resolvedTemplateId,
156
+ title,
157
+ description,
158
+ category,
159
+ designSystemId: templateDesignSystemId,
160
+ isBuiltIn: Boolean(preset),
161
+ ...(designId
162
+ ? { designId, fromTemplate: true as const, instantiatedAt }
163
+ : {}),
164
+ files: files.map((file) => ({
165
+ ...file,
166
+ designFileId:
167
+ designFileIdByTemplateFileId.get(file.templateFileId) ?? null,
168
+ lockedLayers: lockedLayerSnapshots(file.content).map((layer) => ({
169
+ nodeId: layer.id,
170
+ layerName: layer.label,
171
+ })),
172
+ })),
173
+ fileCount: files.length,
174
+ nextRequiredAction:
175
+ "Treat these dimensions, fonts, and locked layers as authoritative. Apply the user's request with edit-design on the design's own files; do not resize the artboard or call generate-design.",
176
+ };
177
+ },
178
+ });
@@ -13,7 +13,7 @@ const PREVIEW_MAX_BYTES = 50_000;
13
13
  export default defineAction({
14
14
  description:
15
15
  "List all design projects accessible to the current user. " +
16
- "Returns title, id, project type, and timestamps.",
16
+ "Returns title, id, project type, owner email, and timestamps.",
17
17
  schema: z.object({
18
18
  compact: z
19
19
  .enum(["true", "false"])
@@ -43,6 +43,7 @@ export default defineAction({
43
43
  projectType: schema.designs.projectType,
44
44
  designSystemId: schema.designs.designSystemId,
45
45
  visibility: schema.designs.visibility,
46
+ ownerEmail: schema.designs.ownerEmail,
46
47
  createdAt: schema.designs.createdAt,
47
48
  updatedAt: schema.designs.updatedAt,
48
49
  })
@@ -106,6 +107,7 @@ export default defineAction({
106
107
  projectType: row.projectType,
107
108
  designSystemId: row.designSystemId,
108
109
  visibility: row.visibility,
110
+ ownerEmail: row.ownerEmail,
109
111
  createdAt: row.createdAt,
110
112
  updatedAt: row.updatedAt,
111
113
  };
@@ -28,6 +28,7 @@ import { eq } from "drizzle-orm";
28
28
  import { z } from "zod";
29
29
 
30
30
  import { getDb, schema } from "../server/db/index.js";
31
+ import { readDesignTemplateSource } from "../server/lib/design-template-data.js";
31
32
  import { parseCanvasFrameGeometryById } from "../shared/canvas-frames.js";
32
33
  import { getDesignTemplatePreset } from "../shared/design-template-presets.js";
33
34
  import { designGenerationSessionKey } from "../shared/generation-session.js";
@@ -332,6 +333,36 @@ export default defineAction({
332
333
  activeCodeFile: resolveActiveCodeFile(files, designSelection),
333
334
  canvasFrames: parseCanvasFrameGeometryById(data.canvasFrames),
334
335
  };
336
+ try {
337
+ const templateSource = readDesignTemplateSource(data);
338
+ if (templateSource) {
339
+ (screen.design as Record<string, unknown>).createdFromTemplate = {
340
+ templateId: templateSource.templateId,
341
+ title: templateSource.title,
342
+ category: templateSource.category,
343
+ instantiatedAt: templateSource.instantiatedAt,
344
+ designSystemId: templateSource.appliedDesignSystemId,
345
+ lockedDimensions: templateSource.files.map((file) => ({
346
+ designFileId: file.designFileId,
347
+ filename: file.filename,
348
+ width: file.width,
349
+ height: file.height,
350
+ })),
351
+ lockedFonts: templateSource.fonts,
352
+ note:
353
+ "The screens below are edited copies of this template, so their current content no longer shows what the template specified. " +
354
+ "The dimensions and fonts above come from the template and stay authoritative for every request, including this one: keep each screen at exactly those dimensions and keep those font families. " +
355
+ "Do not resize the artboard, change canvasFrames width or height, switch the primary viewport, or substitute a typeface to fit new content. " +
356
+ `Refine with edit-design; do not call generate-design. Call \`get-design-template --designId="${designId}"\` when you need the template's original markup or locked layers.`,
357
+ };
358
+ }
359
+ } catch (error) {
360
+ (screen.design as Record<string, unknown>).createdFromTemplate = {
361
+ unreadable:
362
+ error instanceof Error ? error.message : "unknown parse failure",
363
+ note: "This design claims a template that could not be read. Ask the user which template it came from instead of editing dimensions or typography.",
364
+ };
365
+ }
335
366
  const proposalPrefix = `${DESIGN_REPROMPT_PROPOSAL_STATE_PREFIX}${designId}:`;
336
367
  const pendingPrefix = `${DESIGN_REPROMPT_PENDING_STATE_PREFIX}${designId}:`;
337
368
  const [proposalEntries, pendingEntries] = await Promise.all([