@agent-native/core 0.149.5 → 0.151.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 (197) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  3. package/corpus/templates/analytics/.agents/skills/frontend-design/SKILL.md +5 -0
  4. package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +83 -45
  5. package/corpus/templates/analytics/changelog/2026-08-11-analytics-dashboards-keep-the-selected-tab-when-reopened.md +6 -0
  6. package/corpus/templates/assets/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  7. package/corpus/templates/assets/.agents/skills/frontend-design/SKILL.md +5 -0
  8. package/corpus/templates/brain/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  9. package/corpus/templates/brain/.agents/skills/frontend-design/SKILL.md +5 -0
  10. package/corpus/templates/calendar/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  11. package/corpus/templates/calendar/.agents/skills/frontend-design/SKILL.md +5 -0
  12. package/corpus/templates/chat/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  13. package/corpus/templates/chat/.agents/skills/frontend-design/SKILL.md +5 -0
  14. package/corpus/templates/clips/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  15. package/corpus/templates/clips/.agents/skills/frontend-design/SKILL.md +5 -0
  16. package/corpus/templates/clips/changelog/2026-08-11-clips-recovers-from-transient-updates-and-avoids-macos-priva.md +6 -0
  17. package/corpus/templates/clips/desktop/src/app.tsx +32 -176
  18. package/corpus/templates/clips/desktop/src/lib/updater.ts +52 -37
  19. package/corpus/templates/clips/desktop/src/styles.css +1 -166
  20. package/corpus/templates/clips/desktop/src-tauri/Cargo.toml +2 -0
  21. package/corpus/templates/clips/desktop/src-tauri/Info.plist +4 -0
  22. package/corpus/templates/clips/desktop/src-tauri/src/eventkit.rs +28 -1
  23. package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +2 -0
  24. package/corpus/templates/clips/desktop/src-tauri/src/native_speech.rs +48 -1
  25. package/corpus/templates/clips/desktop/src-tauri/src/permission_status.rs +3 -0
  26. package/corpus/templates/clips/desktop/src-tauri/src/whisper_speech.rs +5 -0
  27. package/corpus/templates/clips/server/plugins/auth.ts +3 -0
  28. package/corpus/templates/content/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  29. package/corpus/templates/content/.agents/skills/frontend-design/SKILL.md +5 -0
  30. package/corpus/templates/content/actions/_document-discovery-query.ts +127 -0
  31. package/corpus/templates/content/actions/list-documents.ts +189 -198
  32. package/corpus/templates/content/actions/search-documents.ts +95 -24
  33. package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +2 -1
  34. package/corpus/templates/content/app/hooks/use-documents.ts +84 -6
  35. package/corpus/templates/content/shared/api.ts +10 -0
  36. package/corpus/templates/design/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  37. package/corpus/templates/design/.agents/skills/frontend-design/SKILL.md +5 -0
  38. package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +5 -1
  39. package/corpus/templates/design/changelog/2026-08-11-design-agent-prompts-copy-successfully-in-the-desktop-app.md +6 -0
  40. package/corpus/templates/dispatch/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  41. package/corpus/templates/dispatch/.agents/skills/frontend-design/SKILL.md +5 -0
  42. package/corpus/templates/dispatch/app/global.css +2 -2
  43. package/corpus/templates/dispatch/changelog/2026-08-10-desktop-workspace-sign-in.md +6 -0
  44. package/corpus/templates/dispatch/changelog/2026-08-11-dispatch-no-longer-shows-the-generic-chat-starter-among-defa.md +6 -0
  45. package/corpus/templates/dispatch/changelog/2026-08-11-integrations-and-scheduled-work-open-as-first-class-electron-pages.md +6 -0
  46. package/corpus/templates/dispatch/changelog/2026-08-11-sidebar-text-is-slightly-easier-to-read-in-light-and-dark-mo.md +6 -0
  47. package/corpus/templates/dispatch/server/lib/identity-sso.ts +57 -31
  48. package/corpus/templates/dispatch/server/plugins/feature-flags.ts +5 -0
  49. package/corpus/templates/dispatch/server/plugins/identity-sso.ts +82 -33
  50. package/corpus/templates/dispatch/server/plugins/org-apps-directory.ts +7 -15
  51. package/corpus/templates/dispatch/server/plugins/setup-dispatch.ts +9 -1
  52. package/corpus/templates/dispatch/shared/feature-flags.ts +15 -0
  53. package/corpus/templates/factory/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  54. package/corpus/templates/factory/.agents/skills/frontend-design/SKILL.md +5 -0
  55. package/corpus/templates/forms/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  56. package/corpus/templates/forms/.agents/skills/form-responses/SKILL.md +8 -7
  57. package/corpus/templates/forms/.agents/skills/frontend-design/SKILL.md +5 -0
  58. package/corpus/templates/forms/app/hooks/use-forms.ts +8 -1
  59. package/corpus/templates/forms/changelog/2026-08-11-fixed-public-forms-so-conditional-questions-stay-hidden-unti.md +6 -0
  60. package/corpus/templates/forms/server/handlers/submissions.ts +2 -1
  61. package/corpus/templates/forms/server/lib/public-form-ssr.ts +37 -2
  62. package/corpus/templates/forms/shared/page-url.ts +54 -0
  63. package/corpus/templates/forms/shared/types.ts +4 -4
  64. package/corpus/templates/macros/.agents/skills/frontend-design/SKILL.md +5 -0
  65. package/corpus/templates/mail/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  66. package/corpus/templates/mail/.agents/skills/frontend-design/SKILL.md +5 -0
  67. package/corpus/templates/plan/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  68. package/corpus/templates/plan/.agents/skills/frontend-design/SKILL.md +5 -0
  69. package/corpus/templates/slides/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  70. package/corpus/templates/slides/.agents/skills/frontend-design/SKILL.md +5 -0
  71. package/corpus/templates/slides/actions/add-slide.ts +0 -27
  72. package/corpus/templates/slides/actions/import-file.ts +28 -9
  73. package/corpus/templates/slides/actions/view-screen.ts +4 -4
  74. package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +8 -1
  75. package/corpus/templates/slides/app/components/editor/AiEditingMarker.tsx +25 -0
  76. package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +43 -48
  77. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +9 -6
  78. package/corpus/templates/slides/app/components/editor/GeneratingSlidePreview.tsx +1 -9
  79. package/corpus/templates/slides/app/components/editor/ImageDropPromptPopover.tsx +2 -0
  80. package/corpus/templates/slides/app/components/editor/ImageOverlay.tsx +0 -26
  81. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +904 -217
  82. package/corpus/templates/slides/app/components/editor/canvas-interactions/index.ts +1 -0
  83. package/corpus/templates/slides/app/components/editor/canvas-interactions/slides-canvas-adapter.ts +11 -0
  84. package/corpus/templates/slides/app/components/editor/inline-edit-session.ts +16 -0
  85. package/corpus/templates/slides/app/components/editor/slide-object-interactions.ts +22 -6
  86. package/corpus/templates/slides/app/components/editor/style-clipboard.ts +61 -0
  87. package/corpus/templates/slides/app/components/layout/Layout.tsx +26 -2
  88. package/corpus/templates/slides/app/components/ui/context-menu.tsx +1 -0
  89. package/corpus/templates/slides/app/context/DeckContext.tsx +75 -25
  90. package/corpus/templates/slides/app/global.css +1 -1
  91. package/corpus/templates/slides/app/i18n/en-US.ts +6 -1
  92. package/corpus/templates/slides/app/lib/create-deck-generation.ts +1 -1
  93. package/corpus/templates/slides/app/lib/image-drop-to-agent.ts +12 -12
  94. package/corpus/templates/slides/app/lib/slide-agent-context.ts +52 -0
  95. package/corpus/templates/slides/app/lib/slide-image-replacement.ts +61 -0
  96. package/corpus/templates/slides/app/pages/DeckEditor.tsx +55 -116
  97. package/corpus/templates/slides/app/pages/Index.tsx +28 -3
  98. package/corpus/templates/slides/changelog/2026-08-11-agent-chat-now-follows-the-current-slide-and-selection.md +6 -0
  99. package/corpus/templates/slides/changelog/2026-08-11-deck-titles-now-use-slide-content-instead-of-placeholder-filenames.md +6 -0
  100. package/corpus/templates/slides/changelog/2026-08-11-history-now-opens-from-the-deck-overflow-menu.md +6 -0
  101. package/corpus/templates/slides/changelog/2026-08-11-images-can-be-dropped-directly-onto-slides-and-resized-or-re.md +6 -0
  102. package/corpus/templates/slides/changelog/2026-08-11-new-decks-now-transition-directly-to-a-full-page-editor-load.md +6 -0
  103. package/corpus/templates/slides/changelog/2026-08-11-selected-slide-layers-can-be-nudged-precisely-with-arrow-key.md +6 -0
  104. package/corpus/templates/slides/changelog/2026-08-11-slides-can-copy-and-paste-element-styles-with-cmd-option-c-v.md +6 -0
  105. package/corpus/templates/slides/changelog/2026-08-11-slides-shows-one-compact-ai-marker-and-keeps-your-current-sl.md +6 -0
  106. package/corpus/templates/slides/changelog/2026-08-11-text-edits-preserve-slide-layout-when-no-changes-are-made.md +6 -0
  107. package/corpus/templates/slides/changelog/2026-08-11-the-style-toolbar-now-uses-the-full-available-width.md +6 -0
  108. package/corpus/templates/slides/changelog/2026-08-11-undo-and-redo-now-move-one-visible-slide-state-at-a-time.md +6 -0
  109. package/corpus/templates/slides/server/lib/chat-attachments.ts +6 -2
  110. package/corpus/templates/slides/shared/deck-title.ts +56 -0
  111. package/corpus/templates/tasks/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  112. package/corpus/templates/tasks/.agents/skills/frontend-design/SKILL.md +5 -0
  113. package/dist/agent/run-store.js +6 -1
  114. package/dist/agent/thread-data-builder.js +19 -44
  115. package/dist/agent/types.d.ts +13 -0
  116. package/dist/catalog.json +1 -0
  117. package/dist/cli/create.js +1 -1
  118. package/dist/client/AssistantChat.js +58 -10
  119. package/dist/client/FileStorageSetupCard.d.ts +6 -0
  120. package/dist/client/FileStorageSetupCard.js +18 -0
  121. package/dist/client/NewWorkspaceAppFlow.js +10 -0
  122. package/dist/client/agent-chat-adapter.js +87 -11
  123. package/dist/client/agent-chat.d.ts +1 -1
  124. package/dist/client/agent-chat.js +8 -3
  125. package/dist/client/chat/attachment-adapters.js +10 -0
  126. package/dist/client/chat/message-components.d.ts +4 -1
  127. package/dist/client/chat/message-components.js +37 -13
  128. package/dist/client/chat/tool-call-display.js +14 -0
  129. package/dist/client/chat-first/apps-rail.d.ts +1 -1
  130. package/dist/client/chat-first/apps-rail.js +6 -6
  131. package/dist/client/chat-first/copy.js +1 -0
  132. package/dist/client/chat-first/types.d.ts +1 -0
  133. package/dist/client/extensions/EmbeddedExtension.js +1 -1
  134. package/dist/client/extensions/ExtensionViewer.js +1 -1
  135. package/dist/client/extensions/InlineExtensionFrame.js +1 -1
  136. package/dist/client/onboarding/FirstRunOnboarding.js +17 -2
  137. package/dist/client/settings/FileStorageSettingsForm.d.ts +2 -0
  138. package/dist/client/settings/FileStorageSettingsForm.js +113 -0
  139. package/dist/client/settings/SettingsPanel.js +2 -1
  140. package/dist/client/sse-event-processor.js +21 -0
  141. package/dist/extensions/actions.js +2 -2
  142. package/dist/extensions/html-shell.js +3 -1
  143. package/dist/file-upload/pre-upload-attachments.d.ts +15 -10
  144. package/dist/file-upload/pre-upload-attachments.js +68 -24
  145. package/dist/file-upload/registry.d.ts +4 -5
  146. package/dist/file-upload/registry.js +7 -14
  147. package/dist/file-upload/s3.d.ts +9 -0
  148. package/dist/file-upload/s3.js +168 -0
  149. package/dist/file-upload/types.d.ts +3 -3
  150. package/dist/file-upload/types.js +2 -2
  151. package/dist/onboarding/app-profile.js +23 -1
  152. package/dist/onboarding/default-steps.js +69 -0
  153. package/dist/onboarding/types.d.ts +2 -0
  154. package/dist/progress/routes.d.ts +1 -1
  155. package/dist/provider-api/actions/custom-provider-registration.d.ts +11 -11
  156. package/dist/provider-api/actions/provider-api.d.ts +8 -8
  157. package/dist/resources/handlers.d.ts +1 -1
  158. package/dist/secrets/routes.d.ts +6 -6
  159. package/dist/server/agent-chat/browser-team-tools.js +20 -0
  160. package/dist/server/agent-chat-plugin.js +12 -3
  161. package/dist/server/auth.js +45 -21
  162. package/dist/server/better-auth-instance.d.ts +2 -0
  163. package/dist/server/better-auth-instance.js +2 -2
  164. package/dist/server/core-routes-plugin.d.ts +1 -0
  165. package/dist/server/core-routes-plugin.js +26 -16
  166. package/dist/server/identity-sso-store.d.ts +3 -0
  167. package/dist/server/identity-sso-store.js +45 -0
  168. package/dist/server/identity-sso.d.ts +20 -10
  169. package/dist/server/identity-sso.js +79 -31
  170. package/dist/server/index.d.ts +1 -1
  171. package/dist/server/index.js +1 -1
  172. package/dist/templates/chat/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  173. package/dist/templates/chat/.agents/skills/frontend-design/SKILL.md +5 -0
  174. package/dist/templates/default/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  175. package/dist/templates/default/.agents/skills/frontend-design/SKILL.md +5 -0
  176. package/dist/templates/workspace-core/.agents/skills/authentication/SKILL.md +24 -1
  177. package/dist/templates/workspace-core/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  178. package/dist/templates/workspace-core/.agents/skills/extensions/SKILL.md +6 -2
  179. package/dist/templates/workspace-core/.agents/skills/extensions/references/api.md +1 -1
  180. package/dist/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +5 -0
  181. package/dist/templates/workspace-core/.agents/skills/generative-ui/SKILL.md +3 -1
  182. package/docs/content/authentication.mdx +27 -0
  183. package/docs/content/cross-app-sso.mdx +38 -8
  184. package/docs/content/extensions.mdx +15 -15
  185. package/docs/content/file-uploads.mdx +10 -12
  186. package/docs/content/generative-ui.mdx +6 -1
  187. package/package.json +2 -2
  188. package/src/templates/chat/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  189. package/src/templates/chat/.agents/skills/frontend-design/SKILL.md +5 -0
  190. package/src/templates/default/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  191. package/src/templates/default/.agents/skills/frontend-design/SKILL.md +5 -0
  192. package/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +24 -1
  193. package/src/templates/workspace-core/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  194. package/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +6 -2
  195. package/src/templates/workspace-core/.agents/skills/extensions/references/api.md +1 -1
  196. package/src/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +5 -0
  197. package/src/templates/workspace-core/.agents/skills/generative-ui/SKILL.md +3 -1
@@ -28,7 +28,10 @@ import {
28
28
  DEFAULT_ASPECT_RATIO,
29
29
  type AspectRatio,
30
30
  } from "../shared/aspect-ratios.js";
31
- import { isOpaqueDeckTitle } from "../shared/deck-title.js";
31
+ import {
32
+ isOpaqueDeckTitle,
33
+ resolveImportedDeckTitle,
34
+ } from "../shared/deck-title.js";
32
35
  import { readUserUploadedFile } from "./_uploaded-files.js";
33
36
  import { withDeckLock } from "./patch-deck.js";
34
37
 
@@ -182,17 +185,18 @@ export default defineAction({
182
185
  presentation.slides[0].heightEmu,
183
186
  )
184
187
  : undefined;
185
- await appendDeckSlides(
188
+ const importedTitle = await appendDeckSlides(
186
189
  deckId,
187
190
  title,
188
191
  slides,
189
192
  "import-file:pptx",
190
193
  aspectRatio,
191
194
  sourceImport,
195
+ pptxResults[0]?.sourceText,
192
196
  );
193
197
  return {
194
198
  format: "pptx",
195
- title,
199
+ title: importedTitle,
196
200
  slideCount: slides.length,
197
201
  theme: presentation.theme,
198
202
  deckId,
@@ -241,10 +245,18 @@ export default defineAction({
241
245
  layout: "content",
242
246
  notes: "",
243
247
  }));
244
- await appendDeckSlides(deckId, title, slides, "import-file:docx");
248
+ const importedTitle = await appendDeckSlides(
249
+ deckId,
250
+ title,
251
+ slides,
252
+ "import-file:docx",
253
+ undefined,
254
+ undefined,
255
+ doc.text,
256
+ );
245
257
  return {
246
258
  format: "docx",
247
- title,
259
+ title: importedTitle,
248
260
  sectionCount: doc.sections.length,
249
261
  slideCount: slides.length,
250
262
  textLength: doc.text.length,
@@ -520,18 +532,19 @@ async function importPdfPagesWithFidelity(args: {
520
532
  slides: imported.map((entry) => entry.snapshot),
521
533
  });
522
534
 
523
- await appendDeckSlides(
535
+ const importedTitle = await appendDeckSlides(
524
536
  deckId,
525
537
  title,
526
538
  slides,
527
539
  "import-file:pdf",
528
540
  aspectRatio,
529
541
  sourceImport,
542
+ imported[0]?.snapshot.text,
530
543
  );
531
544
 
532
545
  return {
533
546
  format: "pdf",
534
- title,
547
+ title: importedTitle,
535
548
  pageCount: slides.length,
536
549
  slideCount: slides.length,
537
550
  aspectRatio,
@@ -674,7 +687,8 @@ async function appendDeckSlides(
674
687
  source: string,
675
688
  aspectRatio?: AspectRatio,
676
689
  sourceImport?: ReturnType<typeof buildSourceImportMetadata>,
677
- ) {
690
+ titleSource?: unknown,
691
+ ): Promise<string> {
678
692
  await assertAccess("deck", deckId, "editor");
679
693
 
680
694
  // Read-modify-write under the shared per-deck lock used by patch-deck /
@@ -682,6 +696,7 @@ async function appendDeckSlides(
682
696
  // with another import or an editor/agent slide mutation on the same deck
683
697
  // could read stale data and clobber the other write when both save the
684
698
  // whole decks.data blob back.
699
+ let resolvedTitle = title;
685
700
  const now = await withDeckLock(deckId, async () => {
686
701
  const db = getDb();
687
702
  const existing = await db
@@ -706,7 +721,10 @@ async function appendDeckSlides(
706
721
  // had no slides yet, otherwise resizing the canvas mid-deck would distort
707
722
  // every slide already on it.
708
723
  const hadExistingSlides = previousSlides.length > 0;
709
- const nextTitle = hadExistingSlides ? (existing[0].title ?? title) : title;
724
+ const nextTitle = hadExistingSlides
725
+ ? (existing[0].title ?? title)
726
+ : resolveImportedDeckTitle(title, titleSource ?? slides[0]?.content);
727
+ resolvedTitle = nextTitle;
710
728
  const nextSourceImport = sourceImport
711
729
  ? mergeSourceImportMetadata(
712
730
  sourceImportForDeck(previousData.sourceImport),
@@ -736,6 +754,7 @@ async function appendDeckSlides(
736
754
 
737
755
  notifyClients(deckId);
738
756
  await writeAppState("refresh-signal", { ts: now, source });
757
+ return resolvedTitle;
739
758
  }
740
759
 
741
760
  function safeParseDeckData(raw: string): Record<string, unknown> {
@@ -162,12 +162,12 @@ export default defineAction({
162
162
  style?: Record<string, unknown>;
163
163
  }>;
164
164
  } | null;
165
- if (
166
- selection &&
167
- (!selection.slideId || selection.slideId === currentSlide?.id)
168
- ) {
165
+ if (selection && currentSlide && selection.slideId === currentSlide.id) {
169
166
  lines.push(``);
170
167
  lines.push(`### Current visual selection`);
168
+ lines.push(
169
+ `selectionSlideId: ${selection.slideId} (matches currentSlideId)`,
170
+ );
171
171
  lines.push(`mode: ${selection.mode ?? "unknown"}`);
172
172
  lines.push(`activeTool: ${selection.activeTool ?? "select"}`);
173
173
  if (Array.isArray(selection.items) && selection.items.length > 0) {
@@ -581,8 +581,15 @@ export function DesignSystemSetup({
581
581
  }. Present a summary for review.`,
582
582
  );
583
583
 
584
+ const message =
585
+ parts[0] ?? "Set up a design system from the selected sources.";
586
+ const contextParts = parts.slice(1);
584
587
  openAgentSidebar();
585
- sendToAgentChat({ message: parts.join("\n"), submit: true });
588
+ sendToAgentChat({
589
+ message,
590
+ context: contextParts.join("\n"),
591
+ submit: true,
592
+ });
586
593
  toast(t("designSystemSetup.generationStarted"), {
587
594
  description: t("designSystemSetup.generationStartedDescription"),
588
595
  });
@@ -0,0 +1,25 @@
1
+ import { useT } from "@agent-native/core/client/i18n";
2
+ import { DEFAULT_AGENT_IDENTITY } from "@agent-native/toolkit/collab-ui";
3
+
4
+ import { cn } from "@/lib/utils";
5
+
6
+ /** The single compact marker used for every AI-editing state in Slides. */
7
+ export function AiEditingMarker({ className }: { className?: string }) {
8
+ const t = useT();
9
+ const label = t("raw.aiEditing");
10
+
11
+ return (
12
+ <span
13
+ aria-label={label}
14
+ className={cn(
15
+ "inline-flex size-5 shrink-0 items-center justify-center rounded-full text-[9px] font-semibold leading-none text-primary-foreground",
16
+ className,
17
+ )}
18
+ role="img"
19
+ style={{ backgroundColor: DEFAULT_AGENT_IDENTITY.color }}
20
+ title={label}
21
+ >
22
+ AI
23
+ </span>
24
+ );
25
+ }
@@ -2,13 +2,13 @@ import {
2
2
  agentNativePath,
3
3
  appBasePath,
4
4
  } from "@agent-native/core/client/api-path";
5
- import {
6
- type AttributedRecentEdit,
7
- type CollabUser,
5
+ import type {
6
+ AttributedRecentEdit,
7
+ CollabUser,
8
8
  } from "@agent-native/core/client/collab";
9
9
  import { useAvatarUrl } from "@agent-native/core/client/hooks";
10
10
  import { useT } from "@agent-native/core/client/i18n";
11
- import { RecentEditHighlights } from "@agent-native/toolkit/collab-ui";
11
+ import { DEFAULT_AGENT_IDENTITY } from "@agent-native/toolkit/collab-ui";
12
12
  import {
13
13
  useSortable,
14
14
  SortableContext,
@@ -22,6 +22,7 @@ import { useCallback } from "react";
22
22
 
23
23
  import SlideRenderer from "@/components/deck/SlideRenderer";
24
24
  import type { SlideOverflowInfo } from "@/components/deck/SlideRenderer";
25
+ import { AiEditingMarker } from "@/components/editor/AiEditingMarker";
25
26
  import GeneratingSlidePreview from "@/components/editor/GeneratingSlidePreview";
26
27
  import {
27
28
  Tooltip,
@@ -43,14 +44,14 @@ interface EditorSidebarProps {
43
44
  readOnly?: boolean;
44
45
  /** Presence map: slideId → list of users currently viewing that slide */
45
46
  slidePresence?: Map<string, CollabUser[]>;
46
- /** Lingering recent edits (e.g. agent edits) to highlight over thumbnails. */
47
+ /** Lingering recent edits used to show the compact AI marker. */
47
48
  recentEdits?: AttributedRecentEdit[];
48
49
  /** Deck aspect ratio (defaults to 16:9 when omitted) */
49
50
  aspectRatio?: AspectRatio;
50
51
  /** Active deck design system used by slide content tokens. */
51
52
  designSystem?: DesignSystemData;
52
53
  /** The next slide while the agent is preparing its HTML. */
53
- generatingSlide?: { index: number; content?: string | null };
54
+ generatingSlide?: { index: number };
54
55
  generatingSlideSelected?: boolean;
55
56
  onSelectGeneratingSlide?: () => void;
56
57
  }
@@ -72,6 +73,13 @@ function slideIdFromEdit(edit: AttributedRecentEdit): string | null {
72
73
  return null;
73
74
  }
74
75
 
76
+ function isAgentPresenceUser(user: CollabUser): boolean {
77
+ return (
78
+ user.email.trim().toLowerCase() ===
79
+ DEFAULT_AGENT_IDENTITY.email.trim().toLowerCase()
80
+ );
81
+ }
82
+
75
83
  /** Small presence avatar circle with hover card showing name + email */
76
84
  function PresenceAvatarTip({
77
85
  user,
@@ -143,6 +151,7 @@ function SortableSlideThumb({
143
151
  designSystem,
144
152
  onOverflowChange,
145
153
  readOnly = false,
154
+ aiEditing = false,
146
155
  }: {
147
156
  slide: Slide;
148
157
  index: number;
@@ -154,6 +163,7 @@ function SortableSlideThumb({
154
163
  aspectRatio?: AspectRatio;
155
164
  designSystem?: DesignSystemData;
156
165
  onOverflowChange: (info: SlideOverflowInfo) => void;
166
+ aiEditing?: boolean;
157
167
  }) {
158
168
  const t = useT();
159
169
  const {
@@ -175,6 +185,11 @@ function SortableSlideThumb({
175
185
  };
176
186
 
177
187
  const thumbDims = getAspectRatioDims(aspectRatio);
188
+ const agentPresent = presenceUsers.some(isAgentPresenceUser);
189
+ const humanPresenceUsers = presenceUsers.filter(
190
+ (user) => !isAgentPresenceUser(user),
191
+ );
192
+ const showAiMarker = aiEditing || agentPresent;
178
193
 
179
194
  return (
180
195
  <div ref={setNodeRef} style={style}>
@@ -189,7 +204,7 @@ function SortableSlideThumb({
189
204
  aria-current={isActive ? "true" : undefined}
190
205
  data-slide-thumbnail-id={slide.id}
191
206
  className={`w-full text-left flex items-start gap-1.5 p-1.5 rounded-lg transition-[background-color,box-shadow] duration-150 ${
192
- isActive ? "bg-accent ring-1 ring-primary/50" : ""
207
+ isActive ? "bg-accent" : ""
193
208
  } ${
194
209
  readOnly ? "" : "cursor-grab active:cursor-grabbing"
195
210
  } focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1`}
@@ -199,14 +214,17 @@ function SortableSlideThumb({
199
214
  <span className="block text-center text-[10px] font-medium leading-5 text-muted-foreground/70">
200
215
  {index + 1}
201
216
  </span>
202
- {presenceUsers.length > 0 && (
217
+ {(showAiMarker || humanPresenceUsers.length > 0) && (
203
218
  <div className="absolute left-1/2 top-5 z-10 flex -translate-x-1/2 flex-col items-center gap-1">
204
- {presenceUsers.slice(0, 4).map((u, i) => (
219
+ {showAiMarker && (
220
+ <AiEditingMarker className="size-4 text-[8px]" />
221
+ )}
222
+ {humanPresenceUsers.slice(0, 4).map((u, i) => (
205
223
  <PresenceAvatarTip key={i} user={u} size={14} />
206
224
  ))}
207
- {presenceUsers.length > 4 && (
225
+ {humanPresenceUsers.length > 4 && (
208
226
  <span className="flex h-4 min-w-4 items-center justify-center rounded-full bg-muted px-1 text-[8px] font-medium leading-none text-muted-foreground ring-1 ring-black/40">
209
- +{presenceUsers.length - 4}
227
+ +{humanPresenceUsers.length - 4}
210
228
  </span>
211
229
  )}
212
230
  </div>
@@ -224,8 +242,8 @@ function SortableSlideThumb({
224
242
  className="w-full overflow-hidden rounded border"
225
243
  style={{
226
244
  borderColor:
227
- presenceUsers.length > 0
228
- ? presenceUsers[0].color + "66"
245
+ humanPresenceUsers.length > 0
246
+ ? humanPresenceUsers[0].color + "66"
229
247
  : "rgba(255,255,255,0.06)",
230
248
  aspectRatio: `${thumbDims.width} / ${thumbDims.height}`,
231
249
  contentVisibility: "auto",
@@ -248,14 +266,12 @@ function GeneratingSlideSkeleton({
248
266
  index,
249
267
  aspectRatio,
250
268
  designSystem,
251
- content,
252
269
  selected,
253
270
  onSelect,
254
271
  }: {
255
272
  index: number;
256
273
  aspectRatio?: AspectRatio;
257
274
  designSystem?: DesignSystemData;
258
- content?: string | null;
259
275
  selected?: boolean;
260
276
  onSelect?: () => void;
261
277
  }) {
@@ -263,20 +279,20 @@ function GeneratingSlideSkeleton({
263
279
  return (
264
280
  <button
265
281
  type="button"
266
- className={`relative block w-full rounded-lg text-left transition-[box-shadow] ${
267
- selected ? "ring-1 ring-primary" : ""
282
+ className={`relative block w-full rounded-lg text-left transition-colors ${
283
+ selected ? "bg-accent" : ""
268
284
  }`}
269
285
  aria-label={t("editorSidebar.generatingSlide")}
270
286
  aria-current={selected ? "true" : undefined}
271
287
  onClick={onSelect}
272
288
  >
273
289
  <div className="flex w-full items-start gap-1.5 rounded-lg p-1.5">
274
- <span className="flex-shrink-0 w-4 text-center text-[10px] font-medium leading-5 text-muted-foreground/70">
290
+ <span className="flex flex-shrink-0 flex-col items-center gap-1 text-center text-[10px] font-medium leading-5 text-muted-foreground/70">
275
291
  {index + 1}
292
+ <AiEditingMarker className="size-4 text-[8px]" />
276
293
  </span>
277
294
  <div className="flex-1 min-w-0">
278
295
  <GeneratingSlidePreview
279
- content={content}
280
296
  aspectRatio={aspectRatio}
281
297
  designSystem={designSystem}
282
298
  thumbnail
@@ -302,7 +318,6 @@ export default function EditorSidebar({
302
318
  onSelectGeneratingSlide,
303
319
  }: EditorSidebarProps) {
304
320
  const slideButtonRefs = useRef(new Map<string, HTMLButtonElement>());
305
- const thumbScrollRef = useRef<HTMLDivElement>(null);
306
321
  const measurementsRef = useRef(
307
322
  new Map<
308
323
  string,
@@ -310,6 +325,12 @@ export default function EditorSidebar({
310
325
  >(),
311
326
  );
312
327
  const writeTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
328
+ const aiEditedSlideIds = new Set(
329
+ (recentEdits ?? [])
330
+ .filter((edit) => edit.isAgent)
331
+ .map(slideIdFromEdit)
332
+ .filter((slideId): slideId is string => Boolean(slideId)),
333
+ );
313
334
 
314
335
  const writeDeckFitState = useCallback(() => {
315
336
  const currentSlideIds = new Set(slides.map((slide) => slide.id));
@@ -383,19 +404,6 @@ export default function EditorSidebar({
383
404
  };
384
405
  }, []);
385
406
 
386
- // Resolve a recent-edit descriptor (`slides.<id>`) to the on-screen rect of
387
- // that slide's thumbnail button, relative to the scroll container, so the
388
- // shared RecentEditHighlights overlay can draw a fading "AI edited" ring.
389
- const resolveThumbRect = useCallback(
390
- (edit: AttributedRecentEdit): DOMRect | null => {
391
- const slideId = slideIdFromEdit(edit);
392
- if (!slideId) return null;
393
- const node = slideButtonRefs.current.get(slideId);
394
- if (!node) return null;
395
- return node.getBoundingClientRect();
396
- },
397
- [],
398
- );
399
407
  const registerSlideButton = useCallback(
400
408
  (slideId: string, node: HTMLButtonElement | null) => {
401
409
  if (node) {
@@ -446,10 +454,7 @@ export default function EditorSidebar({
446
454
 
447
455
  return (
448
456
  <div className="flex h-full min-h-0 w-48 flex-shrink-0 flex-col bg-background sm:w-52">
449
- <div
450
- ref={thumbScrollRef}
451
- className="relative min-h-0 flex-1 space-y-1 overflow-y-auto overscroll-contain p-2"
452
- >
457
+ <div className="relative min-h-0 flex-1 space-y-1 overflow-y-auto overscroll-contain p-2">
453
458
  <SortableContext
454
459
  items={slides.map((s) => s.id)}
455
460
  strategy={verticalListSortingStrategy}
@@ -466,6 +471,7 @@ export default function EditorSidebar({
466
471
  presenceUsers={slidePresence?.get(slide.id) ?? []}
467
472
  aspectRatio={aspectRatio}
468
473
  designSystem={designSystem}
474
+ aiEditing={aiEditedSlideIds.has(slide.id)}
469
475
  onOverflowChange={(info) =>
470
476
  handleSlideOverflowChange(slide, info)
471
477
  }
@@ -477,21 +483,10 @@ export default function EditorSidebar({
477
483
  index={generatingSlide.index}
478
484
  aspectRatio={aspectRatio}
479
485
  designSystem={designSystem}
480
- content={generatingSlide.content}
481
486
  selected={generatingSlideSelected}
482
487
  onSelect={onSelectGeneratingSlide}
483
488
  />
484
489
  )}
485
- {/* Fading "AI edited" highlights over the thumbnails of just-edited
486
- slides (the component handles the fade + name/color tag). */}
487
- {recentEdits && recentEdits.length > 0 && (
488
- <RecentEditHighlights
489
- edits={recentEdits}
490
- resolveRect={resolveThumbRect}
491
- containerRef={thumbScrollRef}
492
- outlineOnly
493
- />
494
- )}
495
490
  </div>
496
491
  </div>
497
492
  );
@@ -590,12 +590,12 @@ export default function EditorToolbar({
590
590
  value={deckTitle}
591
591
  onChange={(e) => onTitleChange(e.target.value)}
592
592
  style={{ width: `${titleInputWidth}px` }}
593
- className="min-w-0 max-w-[500px] flex-shrink bg-transparent text-sm font-medium text-foreground/90 outline-none focus:text-foreground"
593
+ className="min-w-0 max-w-[500px] shrink-0 bg-transparent text-sm font-medium text-foreground/90 outline-none focus:text-foreground"
594
594
  spellCheck={false}
595
595
  />
596
596
 
597
597
  {/* Spacer */}
598
- <div className="flex-1 min-w-2" />
598
+ <div className="w-2 shrink-0" />
599
599
 
600
600
  <div
601
601
  ref={onWideContextToolbarSlotChange}
@@ -769,6 +769,13 @@ export default function EditorToolbar({
769
769
  </>
770
770
  )}
771
771
 
772
+ <DropdownMenuSeparator />
773
+ <DropdownMenuGroup>
774
+ <DropdownMenuItem onSelect={onShowHistory}>
775
+ <IconHistory className="size-4" />
776
+ {t("editorToolbar.savedVersions")}
777
+ </DropdownMenuItem>
778
+ </DropdownMenuGroup>
772
779
  <DropdownMenuSeparator />
773
780
  <ExportMenu
774
781
  ref={exportMenuRef}
@@ -794,10 +801,6 @@ export default function EditorToolbar({
794
801
  ? t("editorToolbar.importing")
795
802
  : t("editorToolbar.importFile")}
796
803
  </DropdownMenuItem>
797
- <DropdownMenuItem onSelect={onShowHistory}>
798
- <IconHistory className="size-4" />
799
- {t("editorToolbar.savedVersions")}
800
- </DropdownMenuItem>
801
804
  <DropdownMenuSeparator />
802
805
  <DropdownMenuItem
803
806
  onSelect={() => setTheme(isDark ? "light" : "dark")}
@@ -1,5 +1,4 @@
1
1
  import { useT } from "@agent-native/core/client/i18n";
2
- import { IconLoader2 } from "@tabler/icons-react";
3
2
 
4
3
  import SlideRenderer from "@/components/deck/SlideRenderer";
5
4
  import { Skeleton } from "@/components/ui/skeleton";
@@ -47,7 +46,7 @@ export default function GeneratingSlidePreview({
47
46
  return (
48
47
  <div
49
48
  className={cn(
50
- "relative w-full overflow-hidden rounded-lg border border-primary/70",
49
+ "relative w-full overflow-hidden rounded-lg border border-border bg-muted/20",
51
50
  className,
52
51
  )}
53
52
  style={{ aspectRatio: cssRatio }}
@@ -65,13 +64,6 @@ export default function GeneratingSlidePreview({
65
64
  ) : (
66
65
  <SlideLoadingArtwork />
67
66
  )}
68
- <div className="pointer-events-none absolute left-2 top-2 inline-flex items-center gap-1.5 rounded-full border border-primary/70 px-2 py-1 text-[11px] font-medium text-primary">
69
- <span className="size-1.5 rounded-full bg-primary" aria-hidden="true" />
70
- {t("raw.aiEditing")}
71
- </div>
72
- <div className="absolute right-2 top-2 rounded-full bg-background/80 p-1.5 text-muted-foreground shadow-sm">
73
- <IconLoader2 className="size-3.5 animate-spin" aria-hidden="true" />
74
- </div>
75
67
  </div>
76
68
  );
77
69
  }
@@ -159,12 +159,14 @@ export default function ImageDropPromptPopover({
159
159
  if (payload.kind === "hosted") {
160
160
  sendToAgentChat({
161
161
  message: payload.message,
162
+ context: payload.context,
162
163
  submit: true,
163
164
  referenceImagePaths: payload.referenceImagePaths,
164
165
  });
165
166
  } else {
166
167
  sendToAgentChat({
167
168
  message: payload.message,
169
+ context: payload.context,
168
170
  submit: true,
169
171
  images: payload.images,
170
172
  });
@@ -3,10 +3,8 @@ import {
3
3
  IconPhoto,
4
4
  IconFolderOpen,
5
5
  IconUpload,
6
- IconSearch,
7
6
  IconMaximize,
8
7
  IconMinimize,
9
- IconGlobe,
10
8
  } from "@tabler/icons-react";
11
9
  import { useEffect, useRef } from "react";
12
10
  import { createPortal } from "react-dom";
@@ -17,8 +15,6 @@ interface ImageOverlayProps {
17
15
  onGenerate: () => void;
18
16
  onLibrary: () => void;
19
17
  onUpload: () => void;
20
- onSearch: () => void;
21
- onLogo: () => void;
22
18
  onToggleObjectFit: () => void;
23
19
  onClose: () => void;
24
20
  }
@@ -29,8 +25,6 @@ export default function ImageOverlay({
29
25
  onGenerate,
30
26
  onLibrary,
31
27
  onUpload,
32
- onSearch,
33
- onLogo,
34
28
  onToggleObjectFit,
35
29
  onClose,
36
30
  }: ImageOverlayProps) {
@@ -106,26 +100,6 @@ export default function ImageOverlay({
106
100
  <IconUpload className="w-3.5 h-3.5 text-muted-foreground" />
107
101
  Upload
108
102
  </button>
109
- <button
110
- onClick={() => {
111
- onSearch();
112
- onClose();
113
- }}
114
- className="image-overlay-btn"
115
- >
116
- <IconSearch className="w-3.5 h-3.5 text-muted-foreground" />
117
- Search
118
- </button>
119
- <button
120
- onClick={() => {
121
- onLogo();
122
- onClose();
123
- }}
124
- className="image-overlay-btn"
125
- >
126
- <IconGlobe className="w-3.5 h-3.5 text-muted-foreground" />
127
- Logo
128
- </button>
129
103
  <div className="mx-1.5 border-t border-border" />
130
104
  <button onClick={onToggleObjectFit} className="image-overlay-btn">
131
105
  {objectFit === "cover" ? (