@agent-native/core 0.145.8 → 0.146.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 (118) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/turn-into-app/SKILL.md +37 -11
  3. package/corpus/templates/analytics/.agents/skills/turn-into-app/references/fresh-project.md +13 -2
  4. package/corpus/templates/analytics/actions/get-sql-dashboard.ts +20 -4
  5. package/corpus/templates/analytics/actions/migrate-first-party-analytics-to-bigquery.ts +59 -24
  6. package/corpus/templates/analytics/changelog/2026-08-07-analytics-migrations-now-use-bounded-pressure-aware-backgrou.md +6 -0
  7. package/corpus/templates/analytics/changelog/2026-08-07-template-activity-charts-now-show-only-core-agent-native-app.md +6 -0
  8. package/corpus/templates/analytics/scripts/emit-netlify-dashboard-report-cron.ts +110 -0
  9. package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +13 -13
  10. package/corpus/templates/analytics/server/jobs/analytics-bigquery-backfill.ts +448 -0
  11. package/corpus/templates/analytics/server/jobs/analytics-rollup-backfill.ts +2 -2
  12. package/corpus/templates/analytics/server/lib/analytics-rollup-lock.ts +4 -5
  13. package/corpus/templates/analytics/server/lib/canonical-first-party-dashboard-repair.ts +35 -0
  14. package/corpus/templates/analytics/server/lib/dashboard-catalog.ts +3 -5
  15. package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +10 -4
  16. package/corpus/templates/analytics/server/lib/first-party-analytics-rollups.ts +4 -11
  17. package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +74 -28
  18. package/corpus/templates/analytics/server/plugins/analytics-rollup-backfill-jobs.ts +2 -5
  19. package/corpus/templates/analytics/server/plugins/db.ts +42 -0
  20. package/corpus/templates/analytics/server/routes/api/analytics-bigquery/backfill.post.ts +55 -0
  21. package/corpus/templates/analytics/server/routes/api/dashboard-agent-context.json.get.ts +22 -4
  22. package/corpus/templates/assets/.agents/skills/turn-into-app/SKILL.md +37 -11
  23. package/corpus/templates/assets/.agents/skills/turn-into-app/references/fresh-project.md +13 -2
  24. package/corpus/templates/brain/.agents/skills/turn-into-app/SKILL.md +37 -11
  25. package/corpus/templates/brain/.agents/skills/turn-into-app/references/fresh-project.md +13 -2
  26. package/corpus/templates/brain/server/lib/connectors.ts +38 -14
  27. package/corpus/templates/calendar/.agents/skills/turn-into-app/SKILL.md +37 -11
  28. package/corpus/templates/calendar/.agents/skills/turn-into-app/references/fresh-project.md +13 -2
  29. package/corpus/templates/chat/.agents/skills/turn-into-app/SKILL.md +37 -11
  30. package/corpus/templates/chat/.agents/skills/turn-into-app/references/fresh-project.md +13 -2
  31. package/corpus/templates/clips/.agents/skills/turn-into-app/SKILL.md +37 -11
  32. package/corpus/templates/clips/.agents/skills/turn-into-app/references/fresh-project.md +13 -2
  33. package/corpus/templates/content/.agents/skills/turn-into-app/SKILL.md +37 -11
  34. package/corpus/templates/content/.agents/skills/turn-into-app/references/fresh-project.md +13 -2
  35. package/corpus/templates/crm/.agents/skills/turn-into-app/SKILL.md +37 -11
  36. package/corpus/templates/crm/.agents/skills/turn-into-app/references/fresh-project.md +13 -2
  37. package/corpus/templates/design/.agents/skills/turn-into-app/SKILL.md +37 -11
  38. package/corpus/templates/design/.agents/skills/turn-into-app/references/fresh-project.md +13 -2
  39. package/corpus/templates/dispatch/.agents/skills/turn-into-app/SKILL.md +37 -11
  40. package/corpus/templates/dispatch/.agents/skills/turn-into-app/references/fresh-project.md +13 -2
  41. package/corpus/templates/factory/.agents/skills/turn-into-app/SKILL.md +37 -11
  42. package/corpus/templates/factory/.agents/skills/turn-into-app/references/fresh-project.md +13 -2
  43. package/corpus/templates/forms/.agents/skills/turn-into-app/SKILL.md +37 -11
  44. package/corpus/templates/forms/.agents/skills/turn-into-app/references/fresh-project.md +13 -2
  45. package/corpus/templates/macros/.agents/skills/turn-into-app/SKILL.md +37 -11
  46. package/corpus/templates/macros/.agents/skills/turn-into-app/references/fresh-project.md +13 -2
  47. package/corpus/templates/mail/.agents/skills/turn-into-app/SKILL.md +37 -11
  48. package/corpus/templates/mail/.agents/skills/turn-into-app/references/fresh-project.md +13 -2
  49. package/corpus/templates/plan/.agents/skills/turn-into-app/SKILL.md +37 -11
  50. package/corpus/templates/plan/.agents/skills/turn-into-app/references/fresh-project.md +13 -2
  51. package/corpus/templates/slides/.agents/skills/turn-into-app/SKILL.md +37 -11
  52. package/corpus/templates/slides/.agents/skills/turn-into-app/references/fresh-project.md +13 -2
  53. package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +13 -0
  54. package/corpus/templates/slides/app/components/editor/GeneratingSlidePreview.tsx +5 -0
  55. package/corpus/templates/slides/app/components/presentation/PresentationView.tsx +5 -0
  56. package/corpus/templates/slides/app/components/presentation/PresenterView.tsx +5 -0
  57. package/corpus/templates/slides/app/pages/DeckEditor.tsx +3 -0
  58. package/corpus/templates/slides/app/pages/Presentation.tsx +3 -0
  59. package/corpus/templates/slides/app/pages/SharedPresentation.tsx +5 -0
  60. package/corpus/templates/slides/app/routes/slide.tsx +10 -5
  61. package/corpus/templates/slides/changelog/2026-08-07-slide-previews-and-present-now-keep-deck-colors-consistent.md +6 -0
  62. package/corpus/templates/slides/server/db/schema.ts +1 -0
  63. package/corpus/templates/slides/server/handlers/share.ts +51 -3
  64. package/corpus/templates/slides/server/plugins/db.ts +5 -0
  65. package/corpus/templates/slides/shared/api.ts +2 -0
  66. package/corpus/templates/tasks/.agents/skills/turn-into-app/SKILL.md +37 -11
  67. package/corpus/templates/tasks/.agents/skills/turn-into-app/references/fresh-project.md +13 -2
  68. package/dist/cli/code-agent-executor.js +8 -3
  69. package/dist/client/ConnectBuilderCard.d.ts +7 -7
  70. package/dist/client/ConnectBuilderCard.js +45 -20
  71. package/dist/collab/struct-routes.d.ts +1 -1
  72. package/dist/config.d.ts +9 -2
  73. package/dist/config.js +38 -2
  74. package/dist/db/client.d.ts +13 -1
  75. package/dist/db/client.js +76 -2
  76. package/dist/db/create-get-db.js +2 -2
  77. package/dist/deploy/build.js +5 -1
  78. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  79. package/dist/notifications/routes.d.ts +2 -2
  80. package/dist/observability/routes.d.ts +1 -1
  81. package/dist/progress/routes.d.ts +1 -1
  82. package/dist/provider-api/actions/custom-provider-registration.d.ts +9 -9
  83. package/dist/provider-api/actions/provider-api.d.ts +8 -8
  84. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  85. package/dist/resources/handlers.d.ts +1 -1
  86. package/dist/scripts/docs/search.js +3 -2
  87. package/dist/server/agent-chat/prompt-resources.js +4 -3
  88. package/dist/server/agents-bundle.d.ts +20 -3
  89. package/dist/server/agents-bundle.js +57 -30
  90. package/dist/server/better-auth-instance.js +2 -2
  91. package/dist/shared/runtime-config.js +1 -1
  92. package/dist/templates/chat/.agents/skills/turn-into-app/SKILL.md +37 -11
  93. package/dist/templates/chat/.agents/skills/turn-into-app/references/fresh-project.md +13 -2
  94. package/dist/templates/default/.agents/skills/turn-into-app/SKILL.md +37 -11
  95. package/dist/templates/default/.agents/skills/turn-into-app/references/fresh-project.md +13 -2
  96. package/dist/templates/headless/.agents/skills/turn-into-app/SKILL.md +37 -11
  97. package/dist/templates/headless/.agents/skills/turn-into-app/references/fresh-project.md +13 -2
  98. package/dist/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +37 -11
  99. package/dist/templates/workspace-core/.agents/skills/turn-into-app/references/fresh-project.md +13 -2
  100. package/dist/vite/agent-native-config-loader.d.ts +10 -0
  101. package/dist/vite/agent-native-config-loader.js +56 -0
  102. package/dist/vite/agents-bundle-plugin.d.ts +5 -1
  103. package/dist/vite/agents-bundle-plugin.js +29 -6
  104. package/dist/vite/client.d.ts +4 -2
  105. package/dist/vite/client.js +4 -49
  106. package/dist/vite/index.d.ts +2 -2
  107. package/dist/vite/index.js +1 -1
  108. package/docs/content/agent-native-config.mdx +43 -17
  109. package/docs/content/environment-variables.mdx +1 -1
  110. package/package.json +3 -3
  111. package/src/templates/chat/.agents/skills/turn-into-app/SKILL.md +37 -11
  112. package/src/templates/chat/.agents/skills/turn-into-app/references/fresh-project.md +13 -2
  113. package/src/templates/default/.agents/skills/turn-into-app/SKILL.md +37 -11
  114. package/src/templates/default/.agents/skills/turn-into-app/references/fresh-project.md +13 -2
  115. package/src/templates/headless/.agents/skills/turn-into-app/SKILL.md +37 -11
  116. package/src/templates/headless/.agents/skills/turn-into-app/references/fresh-project.md +13 -2
  117. package/src/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +37 -11
  118. package/src/templates/workspace-core/.agents/skills/turn-into-app/references/fresh-project.md +13 -2
@@ -4,7 +4,7 @@ description: >-
4
4
  Turn visible project context, a proven thread, skill, or workflow into a
5
5
  runnable Agent-Native app with simple buttons, visible agent steps, preview,
6
6
  and deployment handoff. Use when a user invokes `/turn-into-app` or asks to
7
- make a workflow into an app.
7
+ make a workflow into an app, including from Claude or ChatGPT on the web.
8
8
  user-invocable: true
9
9
  scope: both
10
10
  metadata:
@@ -13,9 +13,32 @@ metadata:
13
13
 
14
14
  # Turn Into App
15
15
 
16
+ ## Host execution boundary
17
+
18
+ The build location depends on the host. Treat this as a hard routing rule:
19
+
20
+ - In Claude Web, ChatGPT Web, or a Claude/ChatGPT Project on the web, you are
21
+ the source analyst and handoff orchestrator. You must not build the app in
22
+ the host sandbox. Do not run `npm`, `pnpm`, `npx`, `agent-native create`, or
23
+ `add-app`; do not edit files, create artifacts, or start a local dev server.
24
+ After writing the bounded source brief, call the connected Dispatch action
25
+ `start-workspace-app-creation`. Pass the brief and repeatable workflow in
26
+ `prompt`, plus the inferred `appId`, `description`, `template`, and selected
27
+ `resourceIds` when available. This is the Builder handoff.
28
+ - Do not substitute the generic `create_workspace_app` MCP tool in an online
29
+ host. That tool is a local workspace scaffolder, not the Builder handoff.
30
+ - If `start-workspace-app-creation` is not available or Dispatch is not
31
+ authenticated, stop with the connector setup needed. Do not fall back to a
32
+ local build or claim that the app exists.
33
+ - In Claude Code, Codex Code, or another local code-agent runtime with a target
34
+ workspace, follow the local implementation steps below. The local agent may
35
+ scaffold, edit, run, and verify the app there.
36
+
16
37
  ## Default behavior
17
38
 
18
- This is an end-to-end build skill, not a request for an app proposal.
39
+ For a local code-agent runtime this is an end-to-end build skill, not a request
40
+ for an app proposal. For an online host, the end-to-end result is a verified
41
+ Builder handoff and the resulting workspace app, not code written in the host.
19
42
 
20
43
  - With no argument, choose the source in this order: visible project context,
21
44
  then the current thread. A fresh Claude or ChatGPT Project is a valid source
@@ -47,9 +70,12 @@ private web access, invent an importer, add fake OAuth, or scrape a logged-in
47
70
  page. If the needed context is not visible, ask for an export, transcript, or
48
71
  attachment and treat that artifact as imported source material.
49
72
 
50
- Deliver a fresh app in a new directory, implement the repeatable workflow with
51
- buttons and agent handoffs, start its dev server, verify the main path, and
52
- continue through build/deployment handoff. Do not stop at a plan.
73
+ For local code-agent runtimes, deliver a fresh app in a new directory,
74
+ implement the repeatable workflow with buttons and agent handoffs, start its dev
75
+ server, verify the main path, and continue through build/deployment handoff. Do
76
+ not stop at a plan. For online hosts, call Dispatch first after the source brief,
77
+ then report the returned Builder branch/path and verify the workspace app through
78
+ Dispatch when it becomes available.
53
79
 
54
80
  ## Fresh project context mode
55
81
 
@@ -190,12 +216,12 @@ Do not use `create` for an existing workspace; it scaffolds a new standalone
190
216
  workspace rather than adding an app to the current one.
191
217
 
192
218
  When the source came from a fresh external Project and the target is a Builder
193
- workspace, prefer the available Dispatch MCP workspace handoff after writing
194
- the source brief. Call `start-workspace-app-creation` with a concise prompt,
195
- the inferred app id, and the brief's repeatable workflow. Pass selected
196
- workspace resource IDs when they are available, rather than pasting entire
197
- knowledge files. Continue from the returned workspace path or Builder handoff
198
- and report the actual verification result.
219
+ workspace, the online host path is mandatory: after writing the source brief,
220
+ call `start-workspace-app-creation` with a concise prompt, the inferred app id,
221
+ and the brief's repeatable workflow. Pass selected workspace resource IDs when
222
+ they are available, rather than pasting entire knowledge files. Continue from
223
+ the returned workspace path or Builder branch handoff and report the actual
224
+ verification result. Do not run the local scaffold commands in this host mode.
199
225
 
200
226
  Use a first-party template only when it materially fits the workflow. Keep the
201
227
  new app independent from the source thread's working tree unless the user
@@ -51,13 +51,24 @@ also place the skill instructions or this reference in the Project
51
51
  instructions/knowledge files. Then add and authenticate the Dispatch MCP
52
52
  connector from the packaged `adapters/chatgpt-mcp/connector.json`.
53
53
 
54
+ For Claude Web, ChatGPT Web, and their web Projects, the host is an
55
+ orchestrator, not the build environment. After forming the source brief, call
56
+ the Dispatch `start-workspace-app-creation` action so Builder creates the app
57
+ in the connected workspace. Do not use the host's code interpreter, shell,
58
+ artifact editor, `create_workspace_app`, or local `pnpm`/`npm` commands. If the
59
+ handoff action is unavailable, stop and ask the user to authenticate Dispatch;
60
+ do not fall back to a local sandbox build. Claude Code and Codex Code are the
61
+ local-agent exception and may follow the implementation steps in the main
62
+ skill.
63
+
54
64
  In a new Project chat, say:
55
65
 
56
66
  ```text
57
67
  Turn this project into an app. Use the visible Project instructions,
58
68
  knowledge files, and any selected successful runs as the source. Create the
59
- app in the connected Agent-Native workspace, keep the source brief bounded,
60
- and report the real path and verification result.
69
+ app in the connected Agent-Native workspace through Dispatch and Builder, keep
70
+ the source brief bounded, and report the real Builder branch/path and
71
+ verification result. Do not build it in this chat's sandbox.
61
72
  ```
62
73
 
63
74
  If the host does not show the Project instructions or files to the model, stop
@@ -4,7 +4,7 @@ description: >-
4
4
  Turn visible project context, a proven thread, skill, or workflow into a
5
5
  runnable Agent-Native app with simple buttons, visible agent steps, preview,
6
6
  and deployment handoff. Use when a user invokes `/turn-into-app` or asks to
7
- make a workflow into an app.
7
+ make a workflow into an app, including from Claude or ChatGPT on the web.
8
8
  user-invocable: true
9
9
  scope: both
10
10
  metadata:
@@ -13,9 +13,32 @@ metadata:
13
13
 
14
14
  # Turn Into App
15
15
 
16
+ ## Host execution boundary
17
+
18
+ The build location depends on the host. Treat this as a hard routing rule:
19
+
20
+ - In Claude Web, ChatGPT Web, or a Claude/ChatGPT Project on the web, you are
21
+ the source analyst and handoff orchestrator. You must not build the app in
22
+ the host sandbox. Do not run `npm`, `pnpm`, `npx`, `agent-native create`, or
23
+ `add-app`; do not edit files, create artifacts, or start a local dev server.
24
+ After writing the bounded source brief, call the connected Dispatch action
25
+ `start-workspace-app-creation`. Pass the brief and repeatable workflow in
26
+ `prompt`, plus the inferred `appId`, `description`, `template`, and selected
27
+ `resourceIds` when available. This is the Builder handoff.
28
+ - Do not substitute the generic `create_workspace_app` MCP tool in an online
29
+ host. That tool is a local workspace scaffolder, not the Builder handoff.
30
+ - If `start-workspace-app-creation` is not available or Dispatch is not
31
+ authenticated, stop with the connector setup needed. Do not fall back to a
32
+ local build or claim that the app exists.
33
+ - In Claude Code, Codex Code, or another local code-agent runtime with a target
34
+ workspace, follow the local implementation steps below. The local agent may
35
+ scaffold, edit, run, and verify the app there.
36
+
16
37
  ## Default behavior
17
38
 
18
- This is an end-to-end build skill, not a request for an app proposal.
39
+ For a local code-agent runtime this is an end-to-end build skill, not a request
40
+ for an app proposal. For an online host, the end-to-end result is a verified
41
+ Builder handoff and the resulting workspace app, not code written in the host.
19
42
 
20
43
  - With no argument, choose the source in this order: visible project context,
21
44
  then the current thread. A fresh Claude or ChatGPT Project is a valid source
@@ -47,9 +70,12 @@ private web access, invent an importer, add fake OAuth, or scrape a logged-in
47
70
  page. If the needed context is not visible, ask for an export, transcript, or
48
71
  attachment and treat that artifact as imported source material.
49
72
 
50
- Deliver a fresh app in a new directory, implement the repeatable workflow with
51
- buttons and agent handoffs, start its dev server, verify the main path, and
52
- continue through build/deployment handoff. Do not stop at a plan.
73
+ For local code-agent runtimes, deliver a fresh app in a new directory,
74
+ implement the repeatable workflow with buttons and agent handoffs, start its dev
75
+ server, verify the main path, and continue through build/deployment handoff. Do
76
+ not stop at a plan. For online hosts, call Dispatch first after the source brief,
77
+ then report the returned Builder branch/path and verify the workspace app through
78
+ Dispatch when it becomes available.
53
79
 
54
80
  ## Fresh project context mode
55
81
 
@@ -190,12 +216,12 @@ Do not use `create` for an existing workspace; it scaffolds a new standalone
190
216
  workspace rather than adding an app to the current one.
191
217
 
192
218
  When the source came from a fresh external Project and the target is a Builder
193
- workspace, prefer the available Dispatch MCP workspace handoff after writing
194
- the source brief. Call `start-workspace-app-creation` with a concise prompt,
195
- the inferred app id, and the brief's repeatable workflow. Pass selected
196
- workspace resource IDs when they are available, rather than pasting entire
197
- knowledge files. Continue from the returned workspace path or Builder handoff
198
- and report the actual verification result.
219
+ workspace, the online host path is mandatory: after writing the source brief,
220
+ call `start-workspace-app-creation` with a concise prompt, the inferred app id,
221
+ and the brief's repeatable workflow. Pass selected workspace resource IDs when
222
+ they are available, rather than pasting entire knowledge files. Continue from
223
+ the returned workspace path or Builder branch handoff and report the actual
224
+ verification result. Do not run the local scaffold commands in this host mode.
199
225
 
200
226
  Use a first-party template only when it materially fits the workflow. Keep the
201
227
  new app independent from the source thread's working tree unless the user
@@ -51,13 +51,24 @@ also place the skill instructions or this reference in the Project
51
51
  instructions/knowledge files. Then add and authenticate the Dispatch MCP
52
52
  connector from the packaged `adapters/chatgpt-mcp/connector.json`.
53
53
 
54
+ For Claude Web, ChatGPT Web, and their web Projects, the host is an
55
+ orchestrator, not the build environment. After forming the source brief, call
56
+ the Dispatch `start-workspace-app-creation` action so Builder creates the app
57
+ in the connected workspace. Do not use the host's code interpreter, shell,
58
+ artifact editor, `create_workspace_app`, or local `pnpm`/`npm` commands. If the
59
+ handoff action is unavailable, stop and ask the user to authenticate Dispatch;
60
+ do not fall back to a local sandbox build. Claude Code and Codex Code are the
61
+ local-agent exception and may follow the implementation steps in the main
62
+ skill.
63
+
54
64
  In a new Project chat, say:
55
65
 
56
66
  ```text
57
67
  Turn this project into an app. Use the visible Project instructions,
58
68
  knowledge files, and any selected successful runs as the source. Create the
59
- app in the connected Agent-Native workspace, keep the source brief bounded,
60
- and report the real path and verification result.
69
+ app in the connected Agent-Native workspace through Dispatch and Builder, keep
70
+ the source brief bounded, and report the real Builder branch/path and
71
+ verification result. Do not build it in this chat's sandbox.
61
72
  ```
62
73
 
63
74
  If the host does not show the Project instructions or files to the model, stop
@@ -38,6 +38,8 @@ import type { Slide } from "@/context/DeckContext";
38
38
  import type { AspectRatio } from "@/lib/aspect-ratios";
39
39
  import { TAB_ID } from "@/lib/tab-id";
40
40
 
41
+ import type { DesignSystemData } from "../../../shared/api";
42
+
41
43
  interface EditorSidebarProps {
42
44
  slides: Slide[];
43
45
  activeSlideId: string;
@@ -53,6 +55,8 @@ interface EditorSidebarProps {
53
55
  recentEdits?: AttributedRecentEdit[];
54
56
  /** Deck aspect ratio (defaults to 16:9 when omitted) */
55
57
  aspectRatio?: AspectRatio;
58
+ /** Active deck design system used by slide content tokens. */
59
+ designSystem?: DesignSystemData;
56
60
  /** The next slide while the agent is preparing its HTML. */
57
61
  generatingSlide?: { index: number; content?: string | null };
58
62
  generatingSlideSelected?: boolean;
@@ -154,6 +158,7 @@ function SortableSlideThumb({
154
158
  registerButtonRef,
155
159
  presenceUsers = [],
156
160
  aspectRatio,
161
+ designSystem,
157
162
  onOverflowChange,
158
163
  readOnly = false,
159
164
  }: {
@@ -167,6 +172,7 @@ function SortableSlideThumb({
167
172
  registerButtonRef: (slideId: string, node: HTMLButtonElement | null) => void;
168
173
  presenceUsers?: CollabUser[];
169
174
  aspectRatio?: AspectRatio;
175
+ designSystem?: DesignSystemData;
170
176
  onOverflowChange: (info: SlideOverflowInfo) => void;
171
177
  }) {
172
178
  const t = useT();
@@ -234,6 +240,7 @@ function SortableSlideThumb({
234
240
  <SlideRenderer
235
241
  slide={slide}
236
242
  aspectRatio={aspectRatio}
243
+ designSystem={designSystem}
237
244
  onOverflowChange={onOverflowChange}
238
245
  />
239
246
  </div>
@@ -290,12 +297,14 @@ function SortableSlideThumb({
290
297
  function GeneratingSlideSkeleton({
291
298
  index,
292
299
  aspectRatio,
300
+ designSystem,
293
301
  content,
294
302
  selected,
295
303
  onSelect,
296
304
  }: {
297
305
  index: number;
298
306
  aspectRatio?: AspectRatio;
307
+ designSystem?: DesignSystemData;
299
308
  content?: string | null;
300
309
  selected?: boolean;
301
310
  onSelect?: () => void;
@@ -319,6 +328,7 @@ function GeneratingSlideSkeleton({
319
328
  <GeneratingSlidePreview
320
329
  content={content}
321
330
  aspectRatio={aspectRatio}
331
+ designSystem={designSystem}
322
332
  thumbnail
323
333
  />
324
334
  </div>
@@ -338,6 +348,7 @@ export default function EditorSidebar({
338
348
  slidePresence,
339
349
  recentEdits,
340
350
  aspectRatio,
351
+ designSystem,
341
352
  generatingSlide,
342
353
  generatingSlideSelected = false,
343
354
  onSelectGeneratingSlide,
@@ -508,6 +519,7 @@ export default function EditorSidebar({
508
519
  registerButtonRef={registerSlideButton}
509
520
  presenceUsers={slidePresence?.get(slide.id) ?? []}
510
521
  aspectRatio={aspectRatio}
522
+ designSystem={designSystem}
511
523
  onOverflowChange={(info) =>
512
524
  handleSlideOverflowChange(slide, info)
513
525
  }
@@ -518,6 +530,7 @@ export default function EditorSidebar({
518
530
  <GeneratingSlideSkeleton
519
531
  index={generatingSlide.index}
520
532
  aspectRatio={aspectRatio}
533
+ designSystem={designSystem}
521
534
  content={generatingSlide.content}
522
535
  selected={generatingSlideSelected}
523
536
  onSelect={onSelectGeneratingSlide}
@@ -7,6 +7,8 @@ import type { Slide } from "@/context/DeckContext";
7
7
  import type { AspectRatio } from "@/lib/aspect-ratios";
8
8
  import { cn } from "@/lib/utils";
9
9
 
10
+ import type { DesignSystemData } from "../../../shared/api";
11
+
10
12
  function SlideLoadingArtwork() {
11
13
  return (
12
14
  <div className="absolute inset-0 flex flex-col justify-center gap-4 bg-muted/30 p-[14%]">
@@ -25,11 +27,13 @@ export default function GeneratingSlidePreview({
25
27
  aspectRatio,
26
28
  thumbnail = true,
27
29
  className,
30
+ designSystem,
28
31
  }: {
29
32
  content?: string | null;
30
33
  aspectRatio?: AspectRatio;
31
34
  thumbnail?: boolean;
32
35
  className?: string;
36
+ designSystem?: DesignSystemData;
33
37
  }) {
34
38
  const t = useT();
35
39
  const cssRatio = (aspectRatio ?? "16:9").replace(":", " / ");
@@ -55,6 +59,7 @@ export default function GeneratingSlidePreview({
55
59
  slide={previewSlide}
56
60
  aspectRatio={aspectRatio}
57
61
  thumbnail={thumbnail}
62
+ designSystem={designSystem}
58
63
  className="h-full w-full"
59
64
  />
60
65
  ) : (
@@ -22,6 +22,7 @@ import {
22
22
  resolveSlideAnimationElement,
23
23
  } from "@/lib/slide-animation-elements";
24
24
 
25
+ import type { DesignSystemData } from "../../../shared/api";
25
26
  import { openPresentChannel, type PresentMessage } from "./present-channel";
26
27
 
27
28
  interface PresentationViewProps {
@@ -29,6 +30,7 @@ interface PresentationViewProps {
29
30
  deckId: string;
30
31
  startIndex?: number;
31
32
  aspectRatio?: AspectRatio;
33
+ designSystem?: DesignSystemData;
32
34
  }
33
35
 
34
36
  // ─── Element animation helpers ────────────────────────────────────────────────
@@ -182,6 +184,7 @@ export default function PresentationView({
182
184
  deckId,
183
185
  startIndex = 0,
184
186
  aspectRatio,
187
+ designSystem,
185
188
  }: PresentationViewProps) {
186
189
  const t = useT();
187
190
  const safeSlides = useMemo(
@@ -534,6 +537,7 @@ export default function PresentationView({
534
537
  slide={safeSlides[prevIndex]}
535
538
  thumbnail={false}
536
539
  aspectRatio={aspectRatio}
540
+ designSystem={designSystem}
537
541
  />
538
542
  </div>
539
543
  )}
@@ -548,6 +552,7 @@ export default function PresentationView({
548
552
  slide={displaySlide}
549
553
  thumbnail={false}
550
554
  aspectRatio={aspectRatio}
555
+ designSystem={designSystem}
551
556
  />
552
557
  </div>
553
558
 
@@ -6,6 +6,7 @@ import SlideRenderer from "@/components/deck/SlideRenderer";
6
6
  import type { Slide } from "@/context/DeckContext";
7
7
  import type { AspectRatio } from "@/lib/aspect-ratios";
8
8
 
9
+ import type { DesignSystemData } from "../../../shared/api";
9
10
  import { openPresentChannel, type PresentMessage } from "./present-channel";
10
11
 
11
12
  interface PresenterViewProps {
@@ -13,6 +14,7 @@ interface PresenterViewProps {
13
14
  deckId: string;
14
15
  startIndex?: number;
15
16
  aspectRatio?: AspectRatio;
17
+ designSystem?: DesignSystemData;
16
18
  }
17
19
 
18
20
  function formatElapsed(seconds: number) {
@@ -26,6 +28,7 @@ export default function PresenterView({
26
28
  deckId,
27
29
  startIndex = 0,
28
30
  aspectRatio,
31
+ designSystem,
29
32
  }: PresenterViewProps) {
30
33
  const t = useT();
31
34
  const [index, setIndex] = useState(startIndex);
@@ -136,6 +139,7 @@ export default function PresenterView({
136
139
  slide={current}
137
140
  thumbnail
138
141
  aspectRatio={aspectRatio}
142
+ designSystem={designSystem}
139
143
  />
140
144
  )}
141
145
  </div>
@@ -149,6 +153,7 @@ export default function PresenterView({
149
153
  slide={next}
150
154
  thumbnail
151
155
  aspectRatio={aspectRatio}
156
+ designSystem={designSystem}
152
157
  />
153
158
  </div>
154
159
  ) : (
@@ -1128,6 +1128,7 @@ export default function DeckEditor() {
1128
1128
  slidePresence={slidePresence}
1129
1129
  recentEdits={deckRecentEdits}
1130
1130
  aspectRatio={deck.aspectRatio}
1131
+ designSystem={designSystem}
1131
1132
  generatingSlide={
1132
1133
  generatingSlideVisible
1133
1134
  ? {
@@ -1166,6 +1167,7 @@ export default function DeckEditor() {
1166
1167
  <GeneratingSlidePreview
1167
1168
  content={streamedGeneratingSlideContent}
1168
1169
  aspectRatio={deck.aspectRatio}
1170
+ designSystem={designSystem}
1169
1171
  thumbnail={false}
1170
1172
  />
1171
1173
  </div>
@@ -1181,6 +1183,7 @@ export default function DeckEditor() {
1181
1183
  <GeneratingSlidePreview
1182
1184
  content={streamedGeneratingSlideContent}
1183
1185
  aspectRatio={deck.aspectRatio}
1186
+ designSystem={designSystem}
1184
1187
  thumbnail={false}
1185
1188
  />
1186
1189
  </div>
@@ -7,6 +7,7 @@ import PresentationView from "@/components/presentation/PresentationView";
7
7
  import PresenterView from "@/components/presentation/PresenterView";
8
8
  import { useDecks } from "@/context/DeckContext";
9
9
  import type { Deck } from "@/context/DeckContext";
10
+ import { useDeckDesignSystem } from "@/hooks/use-deck-design-system";
10
11
 
11
12
  export default function Presentation() {
12
13
  const { id } = useParams<{ id: string }>();
@@ -25,6 +26,7 @@ export default function Presentation() {
25
26
  const [searchParams] = useSearchParams();
26
27
  const contextDeck = getDeck(id || "");
27
28
  const deck = contextDeck ?? fallbackDeck;
29
+ const { designSystem } = useDeckDesignSystem(deck?.designSystemId);
28
30
 
29
31
  useEffect(() => {
30
32
  if (!id || loading || contextDeck) {
@@ -104,6 +106,7 @@ export default function Presentation() {
104
106
  deckId={id}
105
107
  startIndex={startSlide}
106
108
  aspectRatio={deck.aspectRatio}
109
+ designSystem={designSystem}
107
110
  />
108
111
  );
109
112
  }
@@ -7,6 +7,7 @@ import { useParams } from "react-router";
7
7
 
8
8
  import PresentationView from "@/components/presentation/PresentationView";
9
9
  import type { Slide } from "@/context/DeckContext";
10
+ import { mergeDesignSystemData } from "@/hooks/use-deck-design-system";
10
11
 
11
12
  interface SharedPresentationProps {
12
13
  initialDeck?: SharedDeckResponse | null;
@@ -81,6 +82,9 @@ export default function SharedPresentation({
81
82
  ...s,
82
83
  layout: s.layout as Slide["layout"],
83
84
  }));
85
+ const designSystem = deck.designSystem
86
+ ? mergeDesignSystemData(deck.designSystem)
87
+ : undefined;
84
88
 
85
89
  // Use a fake deckId that routes "exit" back to the share page itself
86
90
  return (
@@ -88,6 +92,7 @@ export default function SharedPresentation({
88
92
  slides={slides}
89
93
  deckId={`__shared__/${token}`}
90
94
  aspectRatio={deck.aspectRatio}
95
+ designSystem={designSystem}
91
96
  />
92
97
  );
93
98
  }
@@ -10,6 +10,7 @@ import { useSearchParams } from "react-router";
10
10
 
11
11
  import SlideRenderer from "@/components/deck/SlideRenderer";
12
12
  import type { Slide } from "@/context/DeckContext";
13
+ import { useDeckDesignSystem } from "@/hooks/use-deck-design-system";
13
14
  import messages from "@/i18n/en-US";
14
15
  import type { AspectRatio } from "@/lib/aspect-ratios";
15
16
 
@@ -42,8 +43,10 @@ export default function SlideRoute() {
42
43
  const [aspectRatio, setAspectRatio] = useState<AspectRatio | undefined>(
43
44
  undefined,
44
45
  );
46
+ const [designSystemId, setDesignSystemId] = useState<string | null>(null);
45
47
  const [error, setError] = useState<string | null>(null);
46
48
  const [loading, setLoading] = useState(true);
49
+ const { designSystem } = useDeckDesignSystem(designSystemId);
47
50
 
48
51
  const slideNumber =
49
52
  slideNumberParam !== null ? parseInt(slideNumberParam, 10) : null;
@@ -75,11 +78,11 @@ export default function SlideRoute() {
75
78
  return;
76
79
  }
77
80
 
78
- callAction<{ slides?: Slide[]; aspectRatio?: AspectRatio }>(
79
- "get-deck",
80
- { id: deckId },
81
- { method: "GET" },
82
- )
81
+ callAction<{
82
+ slides?: Slide[];
83
+ aspectRatio?: AspectRatio;
84
+ designSystemId?: string | null;
85
+ }>("get-deck", { id: deckId }, { method: "GET" })
83
86
  .then((deck) => {
84
87
  const slides: Slide[] = deck.slides ?? [];
85
88
  if (slides.length === 0) {
@@ -88,6 +91,7 @@ export default function SlideRoute() {
88
91
  const idx = Math.max(0, Math.min(slideIndex, slides.length - 1));
89
92
  setSlide(slides[idx]);
90
93
  setAspectRatio(deck.aspectRatio);
94
+ setDesignSystemId(deck.designSystemId ?? null);
91
95
  setLoading(false);
92
96
  })
93
97
  .catch((err: unknown) => {
@@ -123,6 +127,7 @@ export default function SlideRoute() {
123
127
  slide={slide}
124
128
  thumbnail={false}
125
129
  aspectRatio={aspectRatio}
130
+ designSystem={designSystem}
126
131
  />
127
132
 
128
133
  {inEmbed && (
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-08-07
4
+ ---
5
+
6
+ Slide previews and Present now keep deck colors consistent
@@ -54,6 +54,7 @@ export const deckShareLinks = table("deck_share_links", {
54
54
  title: text("title").notNull(),
55
55
  slides: text("slides").notNull(), // JSON array of slide snapshots
56
56
  aspectRatio: text("aspect_ratio"),
57
+ designSystemData: text("design_system_data"), // Share-safe token snapshot
57
58
  createdAt: text("created_at").notNull().default(now()),
58
59
  });
59
60
 
@@ -1,9 +1,14 @@
1
1
  import crypto from "crypto";
2
2
 
3
3
  import { readBody } from "@agent-native/core/server";
4
- import { assertAccess, ForbiddenError } from "@agent-native/core/sharing";
4
+ import {
5
+ assertAccess,
6
+ ForbiddenError,
7
+ resolveAccess,
8
+ } from "@agent-native/core/sharing";
5
9
  import { toSharedDeckSlide } from "@shared/api";
6
10
  import type {
11
+ DesignSystemData,
7
12
  ShareDeckRequest,
8
13
  ShareDeckResponse,
9
14
  SharedDeckResponse,
@@ -19,6 +24,12 @@ import {
19
24
 
20
25
  const THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1000;
21
26
 
27
+ interface DeckShareResource {
28
+ title?: string | null;
29
+ data: string;
30
+ designSystemId?: string | null;
31
+ }
32
+
22
33
  /**
23
34
  * POST /api/share
24
35
  * Persist a deck snapshot with a random token.
@@ -53,11 +64,13 @@ async function createShareLink(event: any, deckId: string) {
53
64
  const db = getDb();
54
65
  let storedDeck: any;
55
66
  let title = "Untitled";
67
+ let deckResource: DeckShareResource;
56
68
 
57
69
  try {
58
70
  const access = await assertAccess("deck", deckId, "admin");
59
- title = access.resource.title ?? "Untitled";
60
- storedDeck = JSON.parse(access.resource.data);
71
+ deckResource = access.resource as DeckShareResource;
72
+ title = deckResource.title ?? "Untitled";
73
+ storedDeck = JSON.parse(deckResource.data);
61
74
  } catch (err) {
62
75
  if (err instanceof ForbiddenError) {
63
76
  setResponseStatus(event, err.statusCode);
@@ -73,6 +86,29 @@ async function createShareLink(event: any, deckId: string) {
73
86
 
74
87
  const token = crypto.randomBytes(12).toString("base64url");
75
88
  const now = new Date().toISOString();
89
+ const designSystemId =
90
+ deckResource.designSystemId ?? storedDeck.designSystemId;
91
+ let designSystemData: string | null = null;
92
+
93
+ if (typeof designSystemId === "string" && designSystemId.trim()) {
94
+ const designSystemAccess = await resolveAccess(
95
+ "design-system",
96
+ designSystemId,
97
+ );
98
+ const rawData = designSystemAccess?.resource?.data;
99
+ if (typeof rawData === "string") {
100
+ try {
101
+ const parsed = JSON.parse(rawData);
102
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
103
+ designSystemData = JSON.stringify(parsed as DesignSystemData);
104
+ }
105
+ // coercion-ok: malformed optional style data keeps a valid deck shareable.
106
+ } catch {
107
+ // A malformed style record should not make an otherwise valid deck
108
+ // impossible to share; the presentation will use its default tokens.
109
+ }
110
+ }
111
+ }
76
112
 
77
113
  const slides = storedDeck.slides.map((slide: unknown, index: number) =>
78
114
  toSharedDeckSlide(slide, index),
@@ -83,6 +119,7 @@ async function createShareLink(event: any, deckId: string) {
83
119
  title: title || storedDeck.title || "Untitled",
84
120
  slides: JSON.stringify(slides),
85
121
  aspectRatio: storedDeck.aspectRatio ?? null,
122
+ designSystemData,
86
123
  createdAt: now,
87
124
  });
88
125
 
@@ -136,5 +173,16 @@ export const getSharedDeck = defineEventHandler(async (event) => {
136
173
  slides: JSON.parse(shared.slides),
137
174
  aspectRatio: shared.aspectRatio as SharedDeckResponse["aspectRatio"],
138
175
  };
176
+ if (shared.designSystemData) {
177
+ try {
178
+ const parsed = JSON.parse(shared.designSystemData);
179
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
180
+ response.designSystem = parsed as DesignSystemData;
181
+ }
182
+ // coercion-ok: malformed optional snapshots remain viewable with default tokens.
183
+ } catch {
184
+ // Keep legacy or malformed snapshots viewable with default tokens.
185
+ }
186
+ }
139
187
  return response;
140
188
  });
@@ -230,6 +230,11 @@ const runSlidesMigrations = runMigrations(
230
230
  );
231
231
  CREATE INDEX IF NOT EXISTS uploaded_assets_owner_created_idx ON uploaded_assets (owner_email, created_at)`,
232
232
  },
233
+ {
234
+ version: 21,
235
+ name: "slides-share-design-system-snapshot",
236
+ sql: `ALTER TABLE deck_share_links ADD COLUMN IF NOT EXISTS design_system_data TEXT`,
237
+ },
233
238
  ],
234
239
  { table: "slides_migrations" },
235
240
  );