@agent-native/core-corpus 0.216.0 → 0.218.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 (196) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/session-replay/SKILL.md +61 -1
  3. package/corpus/templates/analytics/AGENTS.md +1 -1
  4. package/corpus/templates/analytics/CHANGELOG.md +3 -0
  5. package/corpus/templates/analytics/actions/get-session-replay-summary.ts +9 -65
  6. package/corpus/templates/analytics/actions/list-event-catalog.ts +1 -1
  7. package/corpus/templates/analytics/actions/list-session-outputs.ts +40 -0
  8. package/corpus/templates/analytics/actions/list-session-recordings.ts +17 -4
  9. package/corpus/templates/analytics/actions/view-screen.ts +29 -3
  10. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +38 -167
  11. package/corpus/templates/analytics/app/i18n/en-US.ts +27 -0
  12. package/corpus/templates/analytics/app/pages/sessions/EventCatalogPage.tsx +18 -1
  13. package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +3 -1
  14. package/corpus/templates/analytics/app/pages/sessions/SessionOutputs.tsx +197 -0
  15. package/corpus/templates/analytics/app/pages/sessions/SessionsTriagePage.tsx +114 -4
  16. package/corpus/templates/analytics/changelog/2026-10-15-sessions-can-find-people-who-made-a-slides-deck-but-didn-t-p.md +6 -0
  17. package/corpus/templates/analytics/changelog/2026-10-16-owned-and-shared-chats-appear-in-separate-history-sections.md +6 -0
  18. package/corpus/templates/analytics/changelog/2026-10-16-the-activation-funnel-counts-visitors-who-decline-analytics-.md +6 -0
  19. package/corpus/templates/analytics/server/db/schema.ts +67 -0
  20. package/corpus/templates/analytics/server/lib/canonical-first-party-dashboard-repair.ts +9 -0
  21. package/corpus/templates/analytics/server/lib/first-party-analytics.ts +28 -22
  22. package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +36 -1
  23. package/corpus/templates/analytics/server/lib/output-outcomes.ts +712 -0
  24. package/corpus/templates/analytics/server/lib/replay-origins.ts +33 -0
  25. package/corpus/templates/analytics/server/lib/session-event-index.ts +20 -0
  26. package/corpus/templates/analytics/server/lib/session-performance.ts +6 -40
  27. package/corpus/templates/analytics/server/lib/session-recording-associations.ts +30 -1
  28. package/corpus/templates/analytics/server/lib/session-replay-triage.ts +99 -0
  29. package/corpus/templates/analytics/server/lib/session-replay.ts +45 -31
  30. package/corpus/templates/analytics/server/lib/sessions-triage-lab.ts +8 -2
  31. package/corpus/templates/analytics/server/plugins/db.ts +46 -0
  32. package/corpus/templates/analytics/shared/output-use.ts +205 -0
  33. package/corpus/templates/analytics/shared/session-events.ts +6 -0
  34. package/corpus/templates/assets/CHANGELOG.md +6 -0
  35. package/corpus/templates/assets/app/components/layout/Sidebar.tsx +51 -117
  36. package/corpus/templates/assets/changelog/2026-10-16-owned-and-shared-chats-appear-in-separate-history-sections.md +6 -0
  37. package/corpus/templates/brain/app/components/layout/Sidebar.tsx +34 -101
  38. package/corpus/templates/brain/app/routes/sources.tsx +1 -1
  39. package/corpus/templates/brain/vitest.config.ts +7 -0
  40. package/corpus/templates/calendar/CHANGELOG.md +12 -0
  41. package/corpus/templates/calendar/actions/create-event.ts +2 -4
  42. package/corpus/templates/calendar/actions/update-event.ts +35 -6
  43. package/corpus/templates/calendar/app/pages/BookingLinksPage.tsx +1 -1
  44. package/corpus/templates/calendar/changelog/2026-10-16-booking-links-show-localized-visibility-badges.md +6 -0
  45. package/corpus/templates/calendar/changelog/2026-10-21-solo-events-avoid-notification-approval.md +6 -0
  46. package/corpus/templates/chat/app/components/layout/Sidebar.tsx +62 -101
  47. package/corpus/templates/clips/CHANGELOG.md +7 -20
  48. package/corpus/templates/clips/app/components/meetings/share-meeting-dialog.tsx +20 -77
  49. package/corpus/templates/clips/app/components/player/share-dialog.tsx +63 -90
  50. package/corpus/templates/clips/app/components/sharing/share-test-support.tsx +180 -0
  51. package/corpus/templates/clips/app/components/sharing/share-ui.tsx +5 -584
  52. package/corpus/templates/clips/changelog/2026-10-16-share-dialogs-make-it-easier-to-see-who-has-access-and-manag.md +6 -0
  53. package/corpus/templates/clips/changelog/2026-10-21-share-panels-describe-general-access-in-a-sentence.md +6 -0
  54. package/corpus/templates/community/qr/package.json +1 -1
  55. package/corpus/templates/content/.agents/skills/document-editing/references/databases.md +6 -3
  56. package/corpus/templates/content/CHANGELOG.md +10 -0
  57. package/corpus/templates/content/actions/_browser-document-save-attempt.ts +23 -1
  58. package/corpus/templates/content/actions/_builder-cms-blob-custody.ts +467 -0
  59. package/corpus/templates/content/actions/_builder-cms-execution-lease.ts +17 -0
  60. package/corpus/templates/content/actions/_builder-cms-intent-lookup.ts +5 -0
  61. package/corpus/templates/content/actions/_builder-cms-read-client.ts +881 -21
  62. package/corpus/templates/content/actions/_builder-cms-write-adapter.ts +28 -2
  63. package/corpus/templates/content/actions/_builder-cms-write-client.ts +109 -11
  64. package/corpus/templates/content/actions/_builder-docs-client.ts +1 -0
  65. package/corpus/templates/content/actions/_builder-private-blob.test-fixture.ts +90 -0
  66. package/corpus/templates/content/actions/_builder-source-execution-guard.ts +48 -0
  67. package/corpus/templates/content/actions/_content-save-outcomes.ts +5 -0
  68. package/corpus/templates/content/actions/_database-source-utils.ts +290 -8
  69. package/corpus/templates/content/actions/_database-utils.ts +93 -7
  70. package/corpus/templates/content/actions/_database-view-setup.ts +21 -2
  71. package/corpus/templates/content/actions/_delete-content-space.ts +8 -1
  72. package/corpus/templates/content/actions/_github-source-freshness.ts +30 -4
  73. package/corpus/templates/content/actions/add-content-database-source-field-property.ts +23 -6
  74. package/corpus/templates/content/actions/cancel-prepared-builder-source-update.ts +110 -26
  75. package/corpus/templates/content/actions/change-content-database-source-role.ts +51 -7
  76. package/corpus/templates/content/actions/delete-document.ts +41 -3
  77. package/corpus/templates/content/actions/disconnect-content-database-source.ts +68 -2
  78. package/corpus/templates/content/actions/disconnect-local-folder-source.ts +33 -2
  79. package/corpus/templates/content/actions/execute-builder-source-execution.ts +900 -140
  80. package/corpus/templates/content/actions/get-document.ts +2 -0
  81. package/corpus/templates/content/actions/permanently-delete-document.ts +6 -0
  82. package/corpus/templates/content/actions/prepare-builder-source-execution.ts +155 -13
  83. package/corpus/templates/content/actions/prepare-builder-source-review.ts +299 -73
  84. package/corpus/templates/content/actions/set-live-body-switch.ts +45 -0
  85. package/corpus/templates/content/actions/update-document.ts +139 -20
  86. package/corpus/templates/content/actions/validate-builder-source-execution.ts +135 -26
  87. package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +119 -30
  88. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +56 -13
  89. package/corpus/templates/content/app/components/editor/SuggestionText.tsx +18 -3
  90. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +89 -22
  91. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +23 -0
  92. package/corpus/templates/content/app/components/editor/database/GitHubSourceStatusLine.tsx +27 -2
  93. package/corpus/templates/content/app/components/editor/database/ListItemLayout.tsx +32 -8
  94. package/corpus/templates/content/app/components/editor/database/ListItemRow.tsx +27 -2
  95. package/corpus/templates/content/app/components/editor/database-sources/BuilderSourceReviewDialog.tsx +1 -0
  96. package/corpus/templates/content/app/components/editor/extensions/SuggestionHighlight.ts +383 -34
  97. package/corpus/templates/content/app/components/editor/suggestions/draft-session.ts +31 -0
  98. package/corpus/templates/content/app/components/editor/suggestions/presentation-rebase.ts +82 -4
  99. package/corpus/templates/content/app/global.css +31 -0
  100. package/corpus/templates/content/app/hooks/use-content-database.ts +40 -2
  101. package/corpus/templates/content/app/hooks/use-documents.ts +14 -0
  102. package/corpus/templates/content/app/i18n-data.ts +1 -0
  103. package/corpus/templates/content/changelog/2026-10-16-accepting-a-suggestion-on-a-page-that-changed-since-it-was-s.md +6 -0
  104. package/corpus/templates/content/changelog/2026-10-17-builder-content-avoids-retrying-a-write-when-builder-accepte.md +6 -0
  105. package/corpus/templates/content/changelog/2026-10-18-link-a-new-collection-to-a-github-folder-from-its-sources.md +1 -1
  106. package/corpus/templates/content/changelog/2026-10-20-builder-content-sync-keeps-read-and-write-status-accurate-wh.md +6 -0
  107. package/corpus/templates/content/changelog/2026-10-20-suggested-edits-that-add-remove-split-or-merge-paragraphs-li.md +6 -0
  108. package/corpus/templates/content/changelog/2026-10-21-list-rows-can-show-each-pages-first-lines.md +6 -0
  109. package/corpus/templates/content/changelog/2026-10-22-a-collection-linked-to-a-big-github-folder-fills-faster-and-p.md +6 -0
  110. package/corpus/templates/content/docs/product/capabilities/content.source.adapters.md +29 -0
  111. package/corpus/templates/content/parity/matrix.md +42 -42
  112. package/corpus/templates/content/parity/matrix.ts +3 -1
  113. package/corpus/templates/content/server/db/schema.ts +20 -0
  114. package/corpus/templates/content/server/lib/content-trash-purge.ts +15 -1
  115. package/corpus/templates/content/server/lib/live-body-writer.ts +138 -26
  116. package/corpus/templates/content/server/live-copy/binding.ts +31 -0
  117. package/corpus/templates/content/server/live-copy/bounded-task.d.ts +6 -0
  118. package/corpus/templates/content/server/live-copy/exec-db.ts +1 -0
  119. package/corpus/templates/content/server/live-copy/holds.ts +59 -0
  120. package/corpus/templates/content/server/live-copy/persist.ts +656 -0
  121. package/corpus/templates/content/server/live-copy/switch.ts +136 -0
  122. package/corpus/templates/content/server/live-copy/task-names.ts +4 -0
  123. package/corpus/templates/content/server/live-copy/tasks.ts +183 -0
  124. package/corpus/templates/content/server/live-copy/write-cause.ts +85 -0
  125. package/corpus/templates/content/server/live-copy/write.ts +85 -0
  126. package/corpus/templates/content/server/plugins/builder-private-payload-cleanup.ts +22 -0
  127. package/corpus/templates/content/server/plugins/collab.ts +3 -0
  128. package/corpus/templates/content/server/plugins/db.ts +28 -0
  129. package/corpus/templates/content/shared/api.ts +30 -1
  130. package/corpus/templates/content/shared/database-item-layout.ts +54 -22
  131. package/corpus/templates/content/shared/document-intent-merge.ts +16 -9
  132. package/corpus/templates/content/shared/og-description.ts +258 -36
  133. package/corpus/templates/content/shared/suggestion-diff.ts +23 -6
  134. package/corpus/templates/content/shared/suggestion-formatting.ts +44 -14
  135. package/corpus/templates/content/shared/suggestion-text.ts +20 -5
  136. package/corpus/templates/design/.agents/skills/design-templates/SKILL.md +5 -1
  137. package/corpus/templates/design/.generated/bridge/shader-runtime.generated.ts +25 -4
  138. package/corpus/templates/design/CHANGELOG.md +5 -0
  139. package/corpus/templates/design/actions/get-getting-started.ts +53 -0
  140. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +10 -0
  141. package/corpus/templates/design/app/components/design/bridge/native-shader-runtime.bridge.ts +132 -79
  142. package/corpus/templates/design/app/components/design/bridge/native-source-provider.ts +268 -130
  143. package/corpus/templates/design/app/components/design/bridge/native-worker-scene-host.ts +122 -42
  144. package/corpus/templates/design/app/components/design/bridge/shader-runtime.bridge.ts +41 -4
  145. package/corpus/templates/design/app/components/design/design-canvas/text-content-change-result.ts +46 -12
  146. package/corpus/templates/design/app/components/home/DesignHomeLibrary.tsx +15 -8
  147. package/corpus/templates/design/app/components/layout/GettingStarted.tsx +50 -0
  148. package/corpus/templates/design/app/components/layout/Sidebar.tsx +3 -0
  149. package/corpus/templates/design/app/components/templates/DesignTemplateGrid.tsx +163 -0
  150. package/corpus/templates/design/app/components/templates/DesignTemplateLibrary.tsx +65 -78
  151. package/corpus/templates/design/app/components/templates/TemplatePreview.tsx +6 -2
  152. package/corpus/templates/design/app/global.css +6 -0
  153. package/corpus/templates/design/app/i18n/en-US.ts +17 -0
  154. package/corpus/templates/design/app/lib/canvas-load-outcome.ts +147 -0
  155. package/corpus/templates/design/app/lib/canvas-outcome-tracking.ts +531 -0
  156. package/corpus/templates/design/app/lib/canvas-style-kind.ts +50 -0
  157. package/corpus/templates/design/app/pages/DesignSystems.tsx +1 -1
  158. package/corpus/templates/design/app/pages/Index.tsx +28 -67
  159. package/corpus/templates/design/app/pages/design-editor/commands/commit-visual-styles.ts +86 -29
  160. package/corpus/templates/design/app/pages/design-editor/commands/save-file-content.ts +38 -3
  161. package/corpus/templates/design/app/pages/design-editor/domains/use-editor-core.ts +9 -0
  162. package/corpus/templates/design/app/pages/design-editor/domains/use-editor-files-and-saving.ts +2 -0
  163. package/corpus/templates/design/changelog/2026-10-16-design-pages-use-localized-visibility-badges-and-a-shared-ow.md +6 -0
  164. package/corpus/templates/design/changelog/2026-10-21-a-getting-started-checklist-at-the-bottom-of-the-sidebar-tra.md +6 -0
  165. package/corpus/templates/design/changelog/2026-10-21-templates-now-open-a-read-only-preview-from-the-card-and-the.md +6 -0
  166. package/corpus/templates/design/changelog/2026-10-22-collapsed-shader-layers-no-longer-interrupt-other-shaders-on.md +6 -0
  167. package/corpus/templates/design/changelog/2026-10-22-shader-fills-keep-rendering-when-other-screen-content-cannot.md +6 -0
  168. package/corpus/templates/design/scripts/design-export-validation/export-baseline.json +24 -0
  169. package/corpus/templates/design/server/lib/getting-started.ts +267 -0
  170. package/corpus/templates/design/server/plugins/getting-started.ts +5 -0
  171. package/corpus/templates/design/shared/design-template-presets/analytics-dashboard.ts +127 -0
  172. package/corpus/templates/design/shared/design-template-presets/design-studio.ts +98 -0
  173. package/corpus/templates/design/shared/design-template-presets/messaging-app.ts +101 -0
  174. package/corpus/templates/design/shared/design-template-presets/project-tasks.ts +120 -0
  175. package/corpus/templates/design/shared/design-template-presets.ts +8 -0
  176. package/corpus/templates/dispatch/CHANGELOG.md +4 -0
  177. package/corpus/templates/dispatch/app/i18n-data.ts +75 -0
  178. package/corpus/templates/dispatch/changelog/2026-10-21-owned-and-shared-chats-appear-in-separate-history-sections.md +6 -0
  179. package/corpus/templates/factory/app/components/layout/Sidebar.tsx +34 -100
  180. package/corpus/templates/forms/app/pages/FormsListPage.tsx +1 -1
  181. package/corpus/templates/forms/server/plugins/agent-chat.ts +1 -1
  182. package/corpus/templates/plan/CHANGELOG.md +6 -0
  183. package/corpus/templates/plan/app/components/layout/Sidebar.tsx +34 -109
  184. package/corpus/templates/plan/changelog/2026-10-16-owned-and-shared-chats-appear-in-separate-history-sections.md +6 -0
  185. package/corpus/templates/slides/CHANGELOG.md +7 -0
  186. package/corpus/templates/slides/actions/delete-deck.ts +7 -0
  187. package/corpus/templates/slides/app/components/deck/DeckCard.tsx +1 -1
  188. package/corpus/templates/slides/app/components/design-system/DesignSystemCard.tsx +1 -1
  189. package/corpus/templates/slides/app/pages/Index.tsx +7 -2
  190. package/corpus/templates/slides/changelog/2026-10-16-deck-lists-use-the-shared-ownership-filter-and-visibility-ba.md +6 -0
  191. package/corpus/templates/slides/changelog/2026-10-21-connected-deck-deletion-requires-approval.md +6 -0
  192. package/corpus/templates/slides/scripts/edit-fidelity/client-action.ts +61 -0
  193. package/corpus/templates/slides/scripts/edit-fidelity/run.ts +10 -0
  194. package/package.json +1 -1
  195. package/corpus/templates/chat/app/lib/sidebar-thread-state.ts +0 -29
  196. package/corpus/templates/slides/app/components/deck/DeckFilterMenu.tsx +0 -83
@@ -650,12 +650,21 @@ function resolveVerbatimRun(
650
650
  export type SuggestionFormattingSlicePart =
651
651
  | { type: "text"; text: string; marks: NonNullable<PMNode["marks"]> }
652
652
  | { type: "break"; text: string }
653
- | { type: "indent"; text: string };
653
+ | { type: "indent"; text: string }
654
+ | { type: "block"; text: string };
654
655
 
656
+ const EMPTY_BLOCK = "<empty-block/>";
657
+ // The syntax that opens a line's block: a heading, list item, task, or quote.
658
+ const BLOCK_MARKER = /^(?:#{1,6} |(?:> )+|[-*+] (?:\[[ xX]\] )?|\d+[.)] )/;
659
+
660
+ // `blocks` admits block syntax and empty paragraphs. Without it a gap holds
661
+ // only line breaks and indentation, which is what places a zero-width
662
+ // boundary on one side of a gap or the other.
655
663
  function structuralGapParts(
656
664
  source: string,
657
665
  from: number,
658
666
  to: number,
667
+ blocks = true,
659
668
  ): SuggestionFormattingSlicePart[] | null {
660
669
  const parts: SuggestionFormattingSlicePart[] = [];
661
670
  let offset = from;
@@ -678,15 +687,36 @@ function structuralGapParts(
678
687
  source.lastIndexOf("\r", offset - 1),
679
688
  ) + 1;
680
689
  const atLineStart = /^\t*$/.test(source.slice(lineStart, offset));
681
- if (!atLineStart || source[offset] !== "\t") return null;
682
- const indentFrom = offset;
683
- while (offset < to && source[offset] === "\t") offset += 1;
684
- parts.push({
685
- type: "indent",
686
- text: "⇥".repeat(offset - indentFrom),
687
- });
688
- const heading = /^(?:#{1,6}) /.exec(source.slice(offset, to));
689
- if (heading) offset += heading[0].length;
690
+ if (!atLineStart) return null;
691
+ if (source[offset] === "\t") {
692
+ const indentFrom = offset;
693
+ while (offset < to && source[offset] === "\t") offset += 1;
694
+ parts.push({
695
+ type: "indent",
696
+ text: "⇥".repeat(offset - indentFrom),
697
+ });
698
+ const heading = /^(?:#{1,6}) /.exec(source.slice(offset, to));
699
+ if (heading) offset += heading[0].length;
700
+ continue;
701
+ }
702
+ if (!blocks) return null;
703
+ // An empty paragraph or a block's opening syntax has no text of its own,
704
+ // so it is shown as written: a pilcrow for the paragraph, the marker
705
+ // otherwise. A marker is only whole when the slice holds all of it.
706
+ const lineEnd = offset + EMPTY_BLOCK.length;
707
+ if (
708
+ source.startsWith(EMPTY_BLOCK, offset) &&
709
+ lineEnd <= to &&
710
+ (lineEnd === source.length || /[\r\n]/.test(source[lineEnd]!))
711
+ ) {
712
+ parts.push({ type: "block", text: "¶" });
713
+ offset = lineEnd;
714
+ continue;
715
+ }
716
+ const marker = BLOCK_MARKER.exec(source.slice(offset, to));
717
+ if (!marker) return null;
718
+ parts.push({ type: "block", text: marker[0] });
719
+ offset += marker[0].length;
690
720
  }
691
721
  return parts;
692
722
  }
@@ -894,7 +924,7 @@ export function suggestionFormattingSourceRange(
894
924
  sourceTo: run.sourceFrom,
895
925
  offset: run.from,
896
926
  structural: Boolean(
897
- structuralGapParts(source, previousSourceTo, run.sourceFrom),
927
+ structuralGapParts(source, previousSourceTo, run.sourceFrom, false),
898
928
  ),
899
929
  afterText: index > 0,
900
930
  beforeText: true,
@@ -910,7 +940,7 @@ export function suggestionFormattingSourceRange(
910
940
  sourceTo: source.length,
911
941
  offset: previousTextTo,
912
942
  structural: Boolean(
913
- structuralGapParts(source, previousSourceTo, source.length),
943
+ structuralGapParts(source, previousSourceTo, source.length, false),
914
944
  ),
915
945
  afterText: mapped.runs.length > 0,
916
946
  beforeText: false,
@@ -983,13 +1013,13 @@ export function suggestionFormattingSourceRange(
983
1013
  if (
984
1014
  gap.afterText &&
985
1015
  offset > gap.sourceFrom &&
986
- structuralGapParts(source, gap.sourceFrom, offset)
1016
+ structuralGapParts(source, gap.sourceFrom, offset, false)
987
1017
  )
988
1018
  candidates.push({ offset: gap.offset, affinity: "left" });
989
1019
  if (
990
1020
  gap.beforeText &&
991
1021
  offset < gap.sourceTo &&
992
- (structuralGapParts(source, offset, gap.sourceTo) ||
1022
+ (structuralGapParts(source, offset, gap.sourceTo, false) ||
993
1023
  !/[\r\n]/.test(opening) ||
994
1024
  (gap.beforeCode && /^[^\r\n]*\r?\n$/.test(opening)))
995
1025
  )
@@ -11,6 +11,8 @@ export type SuggestionPresentationContext = {
11
11
  export type SuggestionPresentationNode =
12
12
  | { type: "text"; value: string }
13
13
  | { type: "indent"; value: string }
14
+ | { type: "block"; value: string }
15
+ | { type: "lineBreak" }
14
16
  | {
15
17
  type: "strong" | "emphasis" | "strike" | "code" | "underline";
16
18
  children: SuggestionPresentationNode[];
@@ -120,7 +122,12 @@ function stripInlineContext(
120
122
  const value = node.value.slice(context.length);
121
123
  return value ? { type: "text", value } : null;
122
124
  }
123
- if (node.type === "indent") return node;
125
+ if (
126
+ node.type === "indent" ||
127
+ node.type === "block" ||
128
+ node.type === "lineBreak"
129
+ )
130
+ return node;
124
131
  const children = node.children.flatMap((child) => {
125
132
  const strippedChild = stripNode(child);
126
133
  return strippedChild ? [strippedChild] : [];
@@ -246,11 +253,19 @@ function computeSuggestionTextPresentationForSource(
246
253
  ? text.replace(/ /g, "·").replace(/\t/g, "⇥")
247
254
  : text
248
255
  ).replace(/\n/g, "↵");
249
- return parts.flatMap((part) =>
250
- part.type === "indent"
251
- ? [{ type: "indent" as const, value: part.text }]
256
+ // A line break beside text breaks the line; on its own it stays a visible ↵.
257
+ const breaksLines = parts.some(
258
+ (part) => part.type === "text" && /\S/.test(part.text),
259
+ );
260
+ return parts.flatMap((part): SuggestionPresentationNode[] =>
261
+ part.type === "indent" || part.type === "block"
262
+ ? [{ type: part.type, value: part.text }]
252
263
  : part.type === "break"
253
- ? [{ type: "text" as const, value: part.text }]
264
+ ? [
265
+ breaksLines
266
+ ? { type: "lineBreak" }
267
+ : { type: "text", value: part.text },
268
+ ]
254
269
  : presentationNode(
255
270
  { type: "text", text: visibleText(part.text), marks: part.marks },
256
271
  (text) => text,
@@ -20,7 +20,11 @@ description: >-
20
20
  1. **Resolve the starting point.** When the user mentions a template, past
21
21
  design, or prior work, call both `list-design-templates` and `list-designs`
22
22
  before generating. Match the requested name or id; ask only when multiple
23
- plausible results remain.
23
+ plausible results remain. To look through a template before using it, call
24
+ `get-design-template --templateId="<id>"` (read-only); in the UI a template
25
+ card opens the same read-only preview, and its "Use template" button is the
26
+ copy step. Built-in starters include app screens (analytics dashboard,
27
+ messaging app, project tasks, design studio) beside ads, posters and pages.
24
28
  2. **Save a reusable template.** Call `save-design-as-template` with the source
25
29
  `designId`. The action snapshots inline screens, canvas dimensions,
26
30
  defaults, linked design system, and locked layers.
@@ -26,8 +26,10 @@ export const shaderRuntimeBridgeScript: string = `"use strict";
26
26
  const registry = {};
27
27
  const mounts = [];
28
28
  let rafId = null;
29
+ let lastFrameTs = null;
29
30
  let needsRender = false;
30
31
  let scanTimer = null;
32
+ let viewScale = 1;
31
33
  function nowMs() {
32
34
  return W.performance && W.performance.now ? W.performance.now() : Date.now();
33
35
  }
@@ -285,9 +287,10 @@ export const shaderRuntimeBridgeScript: string = `"use strict";
285
287
  const rect = el.getBoundingClientRect();
286
288
  const wCss = Math.max(1, Math.round(rect.width));
287
289
  const hCss = Math.max(1, Math.round(rect.height));
288
- const dpr = Math.min(2, W.devicePixelRatio || 1);
289
- const wPx = Math.max(1, Math.round(wCss * dpr));
290
- const hPx = Math.max(1, Math.round(hCss * dpr));
290
+ const deviceRatio = Math.min(2, W.devicePixelRatio || 1);
291
+ const ratio = Math.max(Math.min(1, deviceRatio), deviceRatio * viewScale);
292
+ const wPx = Math.max(1, Math.round(wCss * ratio));
293
+ const hPx = Math.max(1, Math.round(hCss * ratio));
291
294
  if (canvas.width !== wPx || canvas.height !== hPx) {
292
295
  canvas.width = wPx;
293
296
  canvas.height = hPx;
@@ -319,8 +322,15 @@ export const shaderRuntimeBridgeScript: string = `"use strict";
319
322
  gl.clear(gl.COLOR_BUFFER_BIT);
320
323
  gl.drawArrays(gl.TRIANGLES, 0, 3);
321
324
  }
322
- function frame() {
325
+ function frame(ts) {
323
326
  rafId = null;
327
+ if (typeof ts === "number") {
328
+ if (ts === lastFrameTs) {
329
+ startLoop();
330
+ return;
331
+ }
332
+ lastFrameTs = ts;
333
+ }
324
334
  const t = (nowMs() - t0) / 1e3;
325
335
  const animate = !reducedMotion && !document.hidden;
326
336
  for (let i = mounts.length - 1; i >= 0; i--) {
@@ -414,6 +424,17 @@ export const shaderRuntimeBridgeScript: string = `"use strict";
414
424
  document.addEventListener("visibilitychange", function() {
415
425
  if (!document.hidden) requestRender();
416
426
  });
427
+ W.addEventListener("message", function(event) {
428
+ if (W.parent === W || event.source !== W.parent) return;
429
+ const data = event.data;
430
+ if (!data || data.type !== "set-editor-chrome-scale") return;
431
+ const scale = Math.max(Number(data.scaleX) || 0, Number(data.scaleY) || 0);
432
+ if (!(scale > 0)) return;
433
+ const next = Math.min(1, Math.ceil(scale * 8) / 8);
434
+ if (next === viewScale) return;
435
+ viewScale = next;
436
+ requestRender();
437
+ });
417
438
  try {
418
439
  new MutationObserver(function() {
419
440
  scheduleScan();
@@ -7,16 +7,20 @@ from the command menu (Cmd+K → "What's new") or from Settings.
7
7
 
8
8
  ### Fixed
9
9
 
10
+ - Collapsed shader layers no longer interrupt other shaders on the canvas
11
+ - Shader fills keep rendering when other screen content cannot be captured
10
12
  - Shader fills and effects render correctly on canvas elements created or updated in the editor.
11
13
 
12
14
  ## 2026-10-21
13
15
 
14
16
  ### Added
15
17
 
18
+ - A Getting started checklist at the bottom of the sidebar tracks your first design, brand, sharing, teammates, and templates, and checks steps off as you finish them
16
19
  - Pulsing Border adds moving color highlights along an element’s outline, with concentrated bright cores, colored falloff, and controls for motion and shape.
17
20
 
18
21
  ### Improved
19
22
 
23
+ - Templates now open a read-only preview from the card, and the home library shows more starters, including dashboard, messaging, task board and design studio screens.
20
24
  - Effect thumbnails now show how shaders treat the Agent-Native mark against a quiet tonal background, making their differences easier to judge.
21
25
  - Native shader previews use less GPU memory when many mesh effects are visible.
22
26
  - Pulsing Border uses the standard Linear and Angular gradient picker for its moving border colors, with up to eight editable stops and a separate Background color. Thickness sets the bright edge, Softness feathers it, Bloom adjusts glow brightness, and Glow spread sets the halo radius. Smoke, margins, offsets, and layout settings stay grouped in collapsible sections.
@@ -126,6 +130,7 @@ from the command menu (Cmd+K → "What's new") or from Settings.
126
130
 
127
131
  ### Improved
128
132
 
133
+ - Design pages use localized visibility badges and a shared ownership filter.
129
134
  - Animated shader fills render together more efficiently and pause work outside the visible canvas.
130
135
  - Shader property sliders preview changes while dragging
131
136
  - Canvas selection, layer controls, text insertion, grouping, and auto-layout behave more predictably.
@@ -0,0 +1,53 @@
1
+ import { defineAction } from "@agent-native/core/action";
2
+ import {
3
+ getOnboardingStepStatuses,
4
+ readOnboardingDismissed,
5
+ } from "@agent-native/core/onboarding/status";
6
+ import {
7
+ getRequestOrgId,
8
+ getRequestUserEmail,
9
+ } from "@agent-native/core/server/request-context";
10
+ import { z } from "zod";
11
+
12
+ import {
13
+ GETTING_STARTED_CHECKLIST,
14
+ registerGettingStartedSteps,
15
+ } from "../server/lib/getting-started.js";
16
+
17
+ export default defineAction({
18
+ description:
19
+ "Read the user's Getting started checklist: each step with whether the user has completed it and the in-app path that completes it. Use it to tell the user what is left or what to do next.",
20
+ schema: z.object({}),
21
+ http: { method: "GET" },
22
+ readOnly: true,
23
+ run: async () => {
24
+ const userEmail = getRequestUserEmail();
25
+ if (!userEmail) {
26
+ throw Object.assign(new Error("Not authenticated"), { statusCode: 401 });
27
+ }
28
+ registerGettingStartedSteps();
29
+ const context = {
30
+ sessionId: userEmail,
31
+ userEmail,
32
+ orgId: getRequestOrgId() ?? null,
33
+ };
34
+ const [steps, dismissed] = await Promise.all([
35
+ getOnboardingStepStatuses(context, {
36
+ checklist: GETTING_STARTED_CHECKLIST,
37
+ }),
38
+ readOnboardingDismissed(userEmail, GETTING_STARTED_CHECKLIST),
39
+ ]);
40
+ return {
41
+ dismissed,
42
+ completeCount: steps.filter((step) => step.complete).length,
43
+ totalCount: steps.length,
44
+ steps: steps.map(({ id, title, description, complete, href }) => ({
45
+ id,
46
+ title,
47
+ description,
48
+ complete,
49
+ href,
50
+ })),
51
+ };
52
+ },
53
+ });
@@ -79,6 +79,7 @@ import {
79
79
  type ReviewFocusRequest,
80
80
  } from "@/components/visual-editor";
81
81
  import { sendToDesignAgentChatAndConfirm } from "@/lib/agent-chat";
82
+ import { useCanvasLoadOutcome } from "@/lib/canvas-load-outcome";
82
83
  import {
83
84
  resolveDesktopDesignSnapshotLayer,
84
85
  useDesktopDesignNativePreview,
@@ -2336,6 +2337,12 @@ export function DesignCanvas({
2336
2337
  const liveEditRestartAttemptRef = useRef(0);
2337
2338
  const liveEditSameInstanceElapsedMsRef = useRef(0);
2338
2339
  const liveEditSameInstanceDelayRef = useRef(LIVE_EDIT_READY_TIMEOUT_MS);
2340
+ // Snapshots and localhost or Fusion previews may never report ready; timing
2341
+ // them would count a missing bridge as a stalled load.
2342
+ const markCanvasLoadReady = useCanvasLoadOutcome({
2343
+ designId,
2344
+ measure: !snapshotOnly && (sourceType ?? "inline") === "inline",
2345
+ });
2339
2346
  const liveEditSameInstanceRearmTimerRef = useRef<number | undefined>(
2340
2347
  undefined,
2341
2348
  );
@@ -4614,6 +4621,7 @@ export function DesignCanvas({
4614
4621
  if (trustedCurrentFrame && !bridgeReadyRef.current) {
4615
4622
  bridgeReadyRef.current = true;
4616
4623
  onBridgeReady?.();
4624
+ markCanvasLoadReady();
4617
4625
  setReadyIframeDocumentIdentity(readyDocumentIdentity);
4618
4626
  flushPendingOneShotMessages();
4619
4627
  }
@@ -4741,6 +4749,7 @@ export function DesignCanvas({
4741
4749
  bridgeReadyRef.current = true;
4742
4750
  editorChromeReadyRef.current = true;
4743
4751
  onBridgeReady?.();
4752
+ markCanvasLoadReady();
4744
4753
  setReadyIframeDocumentIdentity(readyDocumentIdentity);
4745
4754
  if (shouldRefreshRuntimeLayerSnapshot) {
4746
4755
  refreshRuntimeLayerSnapshotAfterReady();
@@ -6225,6 +6234,7 @@ export function DesignCanvas({
6225
6234
  bridgeReadyRef.current = true;
6226
6235
  editorChromeReadyRef.current = true;
6227
6236
  onBridgeReady?.();
6237
+ markCanvasLoadReady();
6228
6238
  refreshRuntimeLayerSnapshotAfterReady();
6229
6239
  flushPendingOneShotMessages();
6230
6240
  }
@@ -367,6 +367,7 @@ import {
367
367
  import { NativeWorkerHostError } from "./native-worker-host";
368
368
  import {
369
369
  NativeWorkerSceneHost,
370
+ NativeWorkerSceneSourceError,
370
371
  type NativeWorkerSceneEntry,
371
372
  } from "./native-worker-scene-host";
372
373
 
@@ -1204,6 +1205,8 @@ class NativeShaderRuntime {
1204
1205
  private workerPreviewBase: EffectInstance | null = null;
1205
1206
  private legacyDraftSession = false;
1206
1207
  private workerCapabilityBlocked = false;
1208
+ private workerRootSourceBlocked = false;
1209
+ private workerSourceRetryTimer = 0;
1207
1210
  private workerRecoveryAttempts = 0;
1208
1211
  private workerRecoveryQueued = false;
1209
1212
  private cameraReportedState = "idle";
@@ -5201,6 +5204,88 @@ class NativeShaderRuntime {
5201
5204
  );
5202
5205
  }
5203
5206
 
5207
+ private hasWorkerPresentationToRetire(): boolean {
5208
+ return Boolean(
5209
+ this.retiringWorkerScene || this.mounts.size || this.scenePresentation,
5210
+ );
5211
+ }
5212
+
5213
+ private prepareWorkerSceneCommit(): void {
5214
+ if (this.retiringWorkerScene) {
5215
+ this.retiringWorkerScene.dispose({ preservePresentationState: true });
5216
+ this.retiringWorkerScene = null;
5217
+ }
5218
+ for (const [id, mount] of this.mounts) {
5219
+ this.unmount(mount);
5220
+ this.mounts.delete(id);
5221
+ }
5222
+ if (this.scenePresentation) this.releaseScenePresentation();
5223
+ }
5224
+
5225
+ private onWorkerSceneFailure = (error: Error): void => {
5226
+ const code =
5227
+ error instanceof NativeWorkerSceneSourceError
5228
+ ? error.captureFailure.code
5229
+ : error instanceof NativeSourceError ||
5230
+ error instanceof NativeWorkerHostError
5231
+ ? error.code
5232
+ : "worker-scene-failed";
5233
+ if (
5234
+ error instanceof NativeWorkerSceneSourceError ||
5235
+ [
5236
+ "worker-continuous-source-unsupported",
5237
+ "worker-unavailable",
5238
+ "gpu-unavailable",
5239
+ "canvas-unavailable",
5240
+ ].includes(code)
5241
+ ) {
5242
+ if (error instanceof NativeWorkerSceneSourceError) {
5243
+ this.workerRootSourceBlocked = true;
5244
+ clearTimeout(this.workerSourceRetryTimer);
5245
+ this.workerSourceRetryTimer = 0;
5246
+ } else this.workerCapabilityBlocked = true;
5247
+ queueMicrotask(() => this.requestScan());
5248
+ }
5249
+ if (
5250
+ [
5251
+ "gpu-device-lost",
5252
+ "gpu-uncaptured-error",
5253
+ "worker-failed",
5254
+ "worker-init-timeout",
5255
+ ].includes(code) &&
5256
+ this.workerRecoveryAttempts < 2 &&
5257
+ !this.workerRecoveryQueued
5258
+ ) {
5259
+ this.workerRecoveryAttempts += 1;
5260
+ this.workerRecoveryQueued = true;
5261
+ queueMicrotask(() => {
5262
+ const failed = this.workerScene;
5263
+ if (failed?.committedScene()) {
5264
+ this.retiringWorkerScene?.dispose();
5265
+ this.retiringWorkerScene = failed;
5266
+ failed.holdLastFrame();
5267
+ } else failed?.dispose();
5268
+ if (this.workerScene === failed) this.workerScene = null;
5269
+ this.workerRecoveryQueued = false;
5270
+ window.setTimeout(
5271
+ () => this.requestScan(),
5272
+ 100 * this.workerRecoveryAttempts,
5273
+ );
5274
+ });
5275
+ }
5276
+ this.issue(code, error.message);
5277
+ for (const entry of this.workerEntries) {
5278
+ const status = this.workerScene?.committedScene() ? "last-good" : "error";
5279
+ entry.target.setAttribute("data-an-native-status", status);
5280
+ entry.target.setAttribute("data-an-native-error", code);
5281
+ this.postStatus(entry.instance.id, entry.instance.nodeId, status, {
5282
+ code,
5283
+ message: error.message,
5284
+ instanceId: entry.instance.id,
5285
+ });
5286
+ }
5287
+ };
5288
+
5204
5289
  private scanOnce = async (): Promise<void> => {
5205
5290
  if (this.disposed) return;
5206
5291
  if (this.running) await this.running;
@@ -5549,6 +5634,7 @@ class NativeShaderRuntime {
5549
5634
  this.dynamicRangeRequested === "sdr" &&
5550
5635
  !this.legacyDraftSession &&
5551
5636
  !this.workerCapabilityBlocked &&
5637
+ !this.workerRootSourceBlocked &&
5552
5638
  !window.frameElement?.hasAttribute(
5553
5639
  "data-native-catalog-preview-renderer",
5554
5640
  ) &&
@@ -5597,20 +5683,6 @@ class NativeShaderRuntime {
5597
5683
  },
5598
5684
  (_publication, message) => {
5599
5685
  this.workerRecoveryAttempts = 0;
5600
- if (this.retiringWorkerScene) {
5601
- this.retiringWorkerScene.dispose({
5602
- preservePresentationState: true,
5603
- });
5604
- this.retiringWorkerScene = null;
5605
- }
5606
- if (this.mounts.size || this.scenePresentation) {
5607
- for (const [id, mount] of this.mounts) {
5608
- this.unmount(mount);
5609
- this.mounts.delete(id);
5610
- }
5611
- if (this.scenePresentation) this.releaseScenePresentation();
5612
- this.workerScene?.setEntries(this.workerEntries);
5613
- }
5614
5686
  if (message.contentChanged)
5615
5687
  for (const committed of message.committedInstances) {
5616
5688
  const entry = this.workerEntries.find(
@@ -5624,69 +5696,7 @@ class NativeShaderRuntime {
5624
5696
  );
5625
5697
  }
5626
5698
  },
5627
- (error) => {
5628
- const code =
5629
- error instanceof NativeSourceError ||
5630
- error instanceof NativeWorkerHostError
5631
- ? error.code
5632
- : "worker-scene-failed";
5633
- if (
5634
- [
5635
- "worker-continuous-source-unsupported",
5636
- "worker-unavailable",
5637
- "gpu-unavailable",
5638
- "canvas-unavailable",
5639
- ].includes(code)
5640
- ) {
5641
- this.workerCapabilityBlocked = true;
5642
- queueMicrotask(() => this.requestScan());
5643
- }
5644
- if (
5645
- [
5646
- "gpu-device-lost",
5647
- "gpu-uncaptured-error",
5648
- "worker-failed",
5649
- "worker-init-timeout",
5650
- ].includes(code) &&
5651
- this.workerRecoveryAttempts < 2 &&
5652
- !this.workerRecoveryQueued
5653
- ) {
5654
- this.workerRecoveryAttempts += 1;
5655
- this.workerRecoveryQueued = true;
5656
- queueMicrotask(() => {
5657
- const failed = this.workerScene;
5658
- if (failed?.committedScene()) {
5659
- this.retiringWorkerScene?.dispose();
5660
- this.retiringWorkerScene = failed;
5661
- failed.holdLastFrame();
5662
- } else failed?.dispose();
5663
- if (this.workerScene === failed) this.workerScene = null;
5664
- this.workerRecoveryQueued = false;
5665
- window.setTimeout(
5666
- () => this.requestScan(),
5667
- 100 * this.workerRecoveryAttempts,
5668
- );
5669
- });
5670
- }
5671
- this.issue(code, error.message);
5672
- for (const entry of this.workerEntries) {
5673
- const status = this.workerScene?.committedScene()
5674
- ? "last-good"
5675
- : "error";
5676
- entry.target.setAttribute("data-an-native-status", status);
5677
- entry.target.setAttribute("data-an-native-error", code);
5678
- this.postStatus(
5679
- entry.instance.id,
5680
- entry.instance.nodeId,
5681
- status,
5682
- {
5683
- code,
5684
- message: error.message,
5685
- instanceId: entry.instance.id,
5686
- },
5687
- );
5688
- }
5689
- },
5699
+ this.onWorkerSceneFailure,
5690
5700
  nativePresentationTargetLayout,
5691
5701
  () => {
5692
5702
  this.refreshPreviewViewport();
@@ -5706,6 +5716,8 @@ class NativeShaderRuntime {
5706
5716
  compact: this.previewPolicy.isOverviewCompact(),
5707
5717
  }),
5708
5718
  () => this.clockOrigin,
5719
+ () => this.hasWorkerPresentationToRetire(),
5720
+ () => this.prepareWorkerSceneCommit(),
5709
5721
  );
5710
5722
  this.workerScene.setVisible(
5711
5723
  document.visibilityState !== "hidden" &&
@@ -20540,6 +20552,30 @@ class NativeShaderRuntime {
20540
20552
  "composition-instance-stale",
20541
20553
  "The requested native scene differs from the worker preview.",
20542
20554
  );
20555
+ const committedScene = workerScene.committedScene();
20556
+ const presentedInstances = new Map(
20557
+ committedScene?.scene.instances.map((instance) => [
20558
+ instance.id,
20559
+ instance,
20560
+ ]),
20561
+ );
20562
+ if (
20563
+ !committedScene ||
20564
+ presentedInstances.size !== committedScene.scene.instances.length ||
20565
+ committedScene.scene.instances.some((instance) => {
20566
+ const current = this.workerEntries.find(
20567
+ (entry) => entry.instance.id === instance.id,
20568
+ );
20569
+ return (
20570
+ !current ||
20571
+ JSON.stringify(current.instance) !== JSON.stringify(instance)
20572
+ );
20573
+ })
20574
+ )
20575
+ throw new NativeSourceError(
20576
+ "composition-instance-stale",
20577
+ "The committed worker scene differs from its authored effects.",
20578
+ );
20543
20579
  const timeSeconds = this.currentTime();
20544
20580
  const exported = await workerScene.captureExportScene({
20545
20581
  cropRect: region.region,
@@ -20565,7 +20601,14 @@ class NativeShaderRuntime {
20565
20601
  ))) ||
20566
20602
  exported.width !== region.width ||
20567
20603
  exported.height !== region.height ||
20568
- exported.rgba.byteLength !== region.width * region.height * 4
20604
+ exported.rgba.byteLength !== region.width * region.height * 4 ||
20605
+ exported.committedInstances.length !== presentedInstances.size ||
20606
+ new Set(
20607
+ exported.committedInstances.map((instance) => instance.instanceId),
20608
+ ).size !== exported.committedInstances.length ||
20609
+ exported.committedInstances.some(
20610
+ (instance) => !presentedInstances.has(instance.instanceId),
20611
+ )
20569
20612
  )
20570
20613
  throw new NativeSourceError(
20571
20614
  "composition-preview-stale",
@@ -20604,7 +20647,7 @@ class NativeShaderRuntime {
20604
20647
  };
20605
20648
  }),
20606
20649
  );
20607
- if (committedInstances.length !== this.workerEntries.length)
20650
+ if (committedInstances.length !== presentedInstances.size)
20608
20651
  throw new NativeSourceError(
20609
20652
  "composition-instance-stale",
20610
20653
  "The worker still omitted a native effect.",
@@ -22674,6 +22717,15 @@ class NativeShaderRuntime {
22674
22717
  record.target !== this.runtimeScript &&
22675
22718
  !isNativeSourceOmittedMutation(record),
22676
22719
  );
22720
+ if (this.workerRootSourceBlocked && authoredRecords.length) {
22721
+ clearTimeout(this.workerSourceRetryTimer);
22722
+ this.workerSourceRetryTimer = window.setTimeout(() => {
22723
+ this.workerSourceRetryTimer = 0;
22724
+ if (this.disposed || !this.workerRootSourceBlocked) return;
22725
+ this.workerRootSourceBlocked = false;
22726
+ this.requestScan();
22727
+ }, 100);
22728
+ }
22677
22729
  if (this.compositionAbort && authoredRecords.length)
22678
22730
  this.compositionAbort.abort(
22679
22731
  new NativeSourceError(
@@ -22902,6 +22954,7 @@ class NativeShaderRuntime {
22902
22954
  this.previewHostParent = null;
22903
22955
  window.removeEventListener("message", this.onMessage);
22904
22956
  clearTimeout(this.rescanTimer);
22957
+ clearTimeout(this.workerSourceRetryTimer);
22905
22958
  clearTimeout(this.deviceRetryTimer);
22906
22959
  if (
22907
22960
  this.scenePresentation &&