@agent-native/core 0.153.9 → 0.154.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 (149) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/AGENTS.md +20 -30
  3. package/corpus/templates/analytics/actions/search-dashboard-references.ts +58 -0
  4. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +1 -1
  5. package/corpus/templates/analytics/app/lib/utils.ts +1 -1
  6. package/corpus/templates/analytics/app/pages/DataSources.tsx +2 -1
  7. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/ViewSqlPopover.tsx +1 -1
  8. package/corpus/templates/analytics/app/pages/monitoring/errors/IssueList.tsx +4 -2
  9. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +4 -2
  10. package/corpus/templates/analytics/server/lib/agent-chat-plan-mode.ts +2 -0
  11. package/corpus/templates/analytics/server/lib/dashboards-store.ts +366 -3
  12. package/corpus/templates/analytics/server/lib/real-data-actions.ts +3 -1
  13. package/corpus/templates/analytics/server/plugins/agent-chat.ts +6 -2
  14. package/corpus/templates/calendar/app/components/calendar/EventDialog.tsx +1 -1
  15. package/corpus/templates/calendar/app/lib/utils.ts +1 -1
  16. package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +32 -5
  17. package/corpus/templates/clips/app/components/sharing/share-ui.tsx +15 -15
  18. package/corpus/templates/clips/app/i18n/en-US.ts +3 -3
  19. package/corpus/templates/clips/app/lib/utils.ts +5 -6
  20. package/corpus/templates/clips/app/root.tsx +4 -1
  21. package/corpus/templates/clips/app/routes/share.$shareId.tsx +2 -2
  22. package/corpus/templates/clips/chrome-extension/src/offscreen.ts +43 -8
  23. package/corpus/templates/clips/desktop/src/app.tsx +27 -13
  24. package/corpus/templates/clips/desktop/src/lib/recorder.ts +23 -9
  25. package/corpus/templates/clips/desktop/src-tauri/src/native_screen/custom_capture.rs +261 -11
  26. package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +21 -3
  27. package/corpus/templates/clips/shared/mic-audio-cleanup.ts +350 -0
  28. package/corpus/templates/content/app/i18n-data.ts +10 -10
  29. package/corpus/templates/content/scripts/check-native-deps.mjs +181 -15
  30. package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +79 -7
  31. package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +14 -2
  32. package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +2 -2
  33. package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +35 -11
  34. package/corpus/templates/design/actions/get-design-system.ts +103 -3
  35. package/corpus/templates/design/actions/import-design-tokens.ts +37 -8
  36. package/corpus/templates/design/actions/import-github.ts +32 -138
  37. package/corpus/templates/design/actions/index-design-system-with-builder.ts +49 -16
  38. package/corpus/templates/design/actions/present-design-variants.ts +93 -4
  39. package/corpus/templates/design/actions/run-design-audit.ts +229 -0
  40. package/corpus/templates/design/actions/sync-design-system-with-builder.ts +64 -0
  41. package/corpus/templates/design/app/components/design/DesignImportPanel.tsx +2 -1
  42. package/corpus/templates/design/app/components/design/TweaksPanel.tsx +2 -1
  43. package/corpus/templates/design/app/components/design/code-workbench/commands.ts +2 -2
  44. package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +17 -4
  45. package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +5 -1
  46. package/corpus/templates/design/app/components/design/inspector/ImageFillControls.tsx +5 -1
  47. package/corpus/templates/design/app/hooks/use-question-flow.ts +81 -5
  48. package/corpus/templates/design/app/i18n/en-US.ts +1 -1
  49. package/corpus/templates/design/app/i18n-data.ts +32 -1
  50. package/corpus/templates/design/app/i18n-keyboard-shortcuts.ts +10 -10
  51. package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +83 -11
  52. package/corpus/templates/design/app/pages/DesignSystems.tsx +14 -0
  53. package/corpus/templates/design/app/pages/design-editor/generation-prompt-directives.ts +22 -0
  54. package/corpus/templates/design/changelog/2026-08-13-a-screen-that-would-render-behind-an-invisible-alpine-overla.md +6 -0
  55. package/corpus/templates/design/changelog/2026-08-13-attach-a-ui-screenshot-and-the-design-is-reproduced-not-rein.md +6 -0
  56. package/corpus/templates/design/changelog/2026-08-13-linking-a-design-system-means-you-never-get-generic-directio.md +6 -0
  57. package/corpus/templates/design/changelog/2026-08-13-the-design-audit-now-reports-when-a-screen-ignores-its-linke.md +6 -0
  58. package/corpus/templates/design/changelog/2026-08-13-the-design-system-preview-now-shows-how-many-named-tokens-we.md +6 -0
  59. package/corpus/templates/design/changelog/2026-08-13-your-design-system-prompt-and-reference-screenshots-now-reac.md +6 -0
  60. package/corpus/templates/design/server/lib/builder-design-system-proxy.ts +18 -2
  61. package/corpus/templates/design/shared/design-review.ts +1 -0
  62. package/corpus/templates/design/shared/html-integrity.ts +171 -0
  63. package/corpus/templates/mail/app/lib/utils.ts +1 -1
  64. package/corpus/templates/plan/app/pages/PlansPage.tsx +5 -2
  65. package/corpus/templates/slides/.agents/skills/design-systems/SKILL.md +10 -6
  66. package/corpus/templates/slides/actions/index-design-system-with-builder.ts +41 -3
  67. package/corpus/templates/slides/actions/sync-design-system-with-builder.ts +64 -0
  68. package/corpus/templates/slides/app/components/deck/DeckCard.tsx +1 -1
  69. package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +193 -20
  70. package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +58 -33
  71. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +28 -41
  72. package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +119 -91
  73. package/corpus/templates/slides/app/components/editor/ImageOverlay.tsx +16 -0
  74. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +158 -31
  75. package/corpus/templates/slides/app/components/editor/SlideOverflowWarning.tsx +30 -14
  76. package/corpus/templates/slides/app/components/editor/SpeakerNotesPanel.tsx +2 -2
  77. package/corpus/templates/slides/app/components/editor/canvas-interactions/slides-canvas-adapter.ts +4 -1
  78. package/corpus/templates/slides/app/components/editor/rich-text-selection.ts +14 -0
  79. package/corpus/templates/slides/app/components/editor/slide-text-targets.ts +24 -0
  80. package/corpus/templates/slides/app/global.css +24 -0
  81. package/corpus/templates/slides/app/hooks/use-navigation-state.ts +5 -1
  82. package/corpus/templates/slides/app/i18n/en-US.ts +19 -10
  83. package/corpus/templates/slides/app/lib/deck-filter.ts +47 -0
  84. package/corpus/templates/slides/app/lib/export-google-slides-client.ts +15 -4
  85. package/corpus/templates/slides/app/lib/export-pptx-client.ts +637 -1
  86. package/corpus/templates/slides/app/lib/image-download.ts +84 -0
  87. package/corpus/templates/slides/app/lib/utils.ts +1 -1
  88. package/corpus/templates/slides/app/pages/Index.tsx +25 -9
  89. package/corpus/templates/slides/netlify.toml +1 -0
  90. package/corpus/templates/slides/server/lib/builder-design-system-proxy.ts +14 -2
  91. package/dist/agent/production-agent.js +1 -1
  92. package/dist/brand-kit/types.d.ts +10 -2
  93. package/dist/cli/code.js +1 -1
  94. package/dist/cli/index.js +30 -1
  95. package/dist/cli/native-dependencies.d.ts +24 -0
  96. package/dist/cli/native-dependencies.js +264 -0
  97. package/dist/cli/plan-local.js +1 -1
  98. package/dist/cli/skills-content/design-visual-edit-skill.d.ts +1 -1
  99. package/dist/cli/skills-content/design-visual-edit-skill.js +2 -2
  100. package/dist/cli/skills-content/help.d.ts +1 -1
  101. package/dist/cli/skills-content/help.js +2 -1
  102. package/dist/cli/skills.js +2 -1
  103. package/dist/cli/visualize-repo.js +1 -1
  104. package/dist/client/FeedbackButton.js +12 -12
  105. package/dist/client/NewWorkspaceAppFlow.js +4 -1
  106. package/dist/client/agent-chat-adapter.js +3 -4
  107. package/dist/client/agent-page/AgentTabsPage.d.ts +2 -2
  108. package/dist/client/agent-page/AgentTabsPage.js +12 -9
  109. package/dist/client/assistant-ui-recovery.d.ts +1 -1
  110. package/dist/client/assistant-ui-recovery.js +4 -0
  111. package/dist/client/chat/message-components.d.ts +11 -3
  112. package/dist/client/chat/message-components.js +31 -5
  113. package/dist/client/chat/tool-call-display.d.ts +2 -1
  114. package/dist/client/chat/tool-call-display.js +33 -15
  115. package/dist/client/db-admin/SqlEditor.js +1 -1
  116. package/dist/client/dev-overlay/DevOverlay.js +2 -2
  117. package/dist/client/extensions/ExtensionsSidebarSection.js +2 -1
  118. package/dist/client/extensions/portable-extension.js +4 -0
  119. package/dist/client/guided-questions.d.ts +8 -0
  120. package/dist/client/guided-questions.js +12 -3
  121. package/dist/client/onboarding/FirstRunOnboarding.js +2 -1
  122. package/dist/client/org/TeamPage.js +14 -3
  123. package/dist/client/settings/SettingsPanel.js +26 -1
  124. package/dist/client/sharing/ShareButton.js +16 -24
  125. package/dist/client/sse-event-processor.js +35 -15
  126. package/dist/client/tool-display.d.ts +9 -0
  127. package/dist/client/tool-display.js +39 -0
  128. package/dist/client/vite-dev-recovery-script.js +1 -1
  129. package/dist/extensions/html-shell.js +5 -0
  130. package/dist/integrations/catalog.js +9 -8
  131. package/dist/observability/routes.d.ts +2 -2
  132. package/dist/secrets/routes.d.ts +6 -6
  133. package/dist/server/agent-chat/prompt-resources.js +17 -16
  134. package/dist/server/agent-chat-plugin.d.ts +9 -0
  135. package/dist/server/agent-chat-plugin.js +31 -9
  136. package/dist/server/builder-design-systems.d.ts +33 -1
  137. package/dist/server/builder-design-systems.js +293 -11
  138. package/dist/server/design-token-utils.d.ts +11 -0
  139. package/dist/server/design-token-utils.js +68 -16
  140. package/dist/server/index.d.ts +1 -1
  141. package/dist/server/index.js +1 -1
  142. package/dist/server/onboarding-html.js +2 -1
  143. package/dist/shared/docs-url.d.ts +24 -0
  144. package/dist/shared/docs-url.js +39 -0
  145. package/dist/shared/index.d.ts +1 -0
  146. package/dist/shared/index.js +1 -0
  147. package/dist/templates/default/app/routes/_index.tsx +2 -1
  148. package/package.json +2 -2
  149. package/src/templates/default/app/routes/_index.tsx +2 -1
@@ -32,6 +32,30 @@ const codeFileSchema = z.object({
32
32
  ),
33
33
  });
34
34
 
35
+ const githubSourceSchema = z.object({
36
+ repoUrl: z.string().trim().min(1).describe("GitHub repository URL"),
37
+ ref: z
38
+ .string()
39
+ .trim()
40
+ .min(1)
41
+ .optional()
42
+ .describe("Optional branch, tag, or commit"),
43
+ include: z
44
+ .array(z.string().trim().min(1))
45
+ .optional()
46
+ .describe("Optional repository-relative files or folders to include"),
47
+ exclude: z
48
+ .array(z.string().trim().min(1))
49
+ .optional()
50
+ .describe("Optional repository-relative files or folders to exclude"),
51
+ instructions: z
52
+ .string()
53
+ .trim()
54
+ .min(1)
55
+ .optional()
56
+ .describe("Optional indexing guidance for this repository"),
57
+ });
58
+
35
59
  export default defineAction({
36
60
  description:
37
61
  "Start Builder DSI design-system indexing from connected code, a GitHub repository, code/design files, and optional design.md guidance. " +
@@ -49,7 +73,15 @@ export default defineAction({
49
73
  githubRepoUrl: z
50
74
  .string()
51
75
  .optional()
52
- .describe("GitHub repository URL to index with Builder"),
76
+ .describe("Legacy single GitHub repository URL to index with Builder"),
77
+ githubSources: z
78
+ .array(githubSourceSchema)
79
+ .min(1)
80
+ .max(20)
81
+ .optional()
82
+ .describe(
83
+ "GitHub repositories to index in one design system. Each source may specify a branch/tag/commit and repository-relative files or folders.",
84
+ ),
53
85
  connectedProjectId: z
54
86
  .string()
55
87
  .optional()
@@ -69,6 +101,7 @@ export default defineAction({
69
101
  projectName,
70
102
  description,
71
103
  githubRepoUrl,
104
+ githubSources,
72
105
  connectedProjectId,
73
106
  codeFiles,
74
107
  designMd,
@@ -81,6 +114,7 @@ export default defineAction({
81
114
  projectName,
82
115
  description,
83
116
  githubRepoUrl,
117
+ githubRepos: githubSources,
84
118
  connectedProjectId,
85
119
  files,
86
120
  });
@@ -93,10 +127,13 @@ export default defineAction({
93
127
  orgId: getRequestOrgId(),
94
128
  projectName,
95
129
  description,
130
+ githubSources:
131
+ githubSources ?? (githubRepoUrl ? [{ repoUrl: githubRepoUrl }] : []),
96
132
  sourceKind:
97
- githubRepoUrl && (codeFiles?.length || designMd)
133
+ (githubSources?.length || githubRepoUrl) &&
134
+ (codeFiles?.length || designMd)
98
135
  ? "mixed"
99
- : githubRepoUrl
136
+ : githubSources?.length || githubRepoUrl
100
137
  ? "github"
101
138
  : codeFiles?.length || designMd
102
139
  ? "code"
@@ -107,6 +144,7 @@ export default defineAction({
107
144
  ...result,
108
145
  ...proxy,
109
146
  uploadedFileCount: files.length,
147
+ githubSourceCount: githubSources?.length ?? (githubRepoUrl ? 1 : 0),
110
148
  };
111
149
  },
112
150
  });
@@ -0,0 +1,64 @@
1
+ import { defineAction } from "@agent-native/core";
2
+ import {
3
+ parseBuilderDesignSystemProxyReference,
4
+ startBuilderDesignSystemIndex,
5
+ } from "@agent-native/core/server";
6
+ import { getRequestUserEmail } from "@agent-native/core/server/request-context";
7
+ import { assertAccess, resolveAccess } from "@agent-native/core/sharing";
8
+ import { z } from "zod";
9
+
10
+ import "../server/db/index.js";
11
+ import { upsertBuilderProxyDesignSystem } from "../server/lib/builder-design-system-proxy.js";
12
+
13
+ export default defineAction({
14
+ description:
15
+ "Re-index a Builder-backed GitHub design system using its persisted repository, ref, and file/folder scope. Requires editor access.",
16
+ schema: z.object({
17
+ id: z.string().min(1).describe("Local design system id"),
18
+ }),
19
+ run: async ({ id }) => {
20
+ await assertAccess("design-system", id, "editor");
21
+ const access = await resolveAccess("design-system", id);
22
+ if (!access) throw new Error("Design system not found");
23
+
24
+ const reference = parseBuilderDesignSystemProxyReference(
25
+ access.resource.data,
26
+ );
27
+ if (!reference?.githubSources?.length) {
28
+ throw new Error(
29
+ "This design system has no persisted GitHub source scope to sync.",
30
+ );
31
+ }
32
+ if (reference.sourceKind === "mixed") {
33
+ throw new Error(
34
+ "This design system combines GitHub with non-replayable sources. Re-import all sources before syncing it.",
35
+ );
36
+ }
37
+
38
+ const result = await startBuilderDesignSystemIndex({
39
+ projectName: access.resource.title,
40
+ description: access.resource.description ?? undefined,
41
+ githubRepos: reference.githubSources,
42
+ });
43
+ const ownerEmail = getRequestUserEmail();
44
+ if (!ownerEmail) throw new Error("no authenticated user");
45
+
46
+ const proxy = await upsertBuilderProxyDesignSystem({
47
+ result,
48
+ ownerEmail: access.resource.ownerEmail,
49
+ orgId: access.resource.orgId,
50
+ projectName: access.resource.title,
51
+ description: access.resource.description ?? undefined,
52
+ sourceKind: reference.sourceKind,
53
+ githubSources: reference.githubSources,
54
+ localDesignSystemId: id,
55
+ });
56
+
57
+ return {
58
+ ...result,
59
+ ...proxy,
60
+ synced: true,
61
+ githubSourceCount: reference.githubSources.length,
62
+ };
63
+ },
64
+ });
@@ -189,7 +189,7 @@ export default function DeckCard({
189
189
  </DropdownMenuTrigger>
190
190
  <DropdownMenuContent
191
191
  align="end"
192
- className="w-40"
192
+ className="w-56"
193
193
  onCloseAutoFocus={(e) => {
194
194
  if (pendingRenameRef.current) {
195
195
  e.preventDefault();
@@ -19,6 +19,7 @@ import {
19
19
  IconCheck,
20
20
  IconExternalLink,
21
21
  IconChevronDown,
22
+ IconRefresh,
22
23
  } from "@tabler/icons-react";
23
24
  import { useState, useCallback, useEffect, useRef, useMemo } from "react";
24
25
  import { toast } from "sonner";
@@ -59,6 +60,8 @@ interface DesignSystemSetupProps {
59
60
  interface GitHubLink {
60
61
  id: string;
61
62
  url: string;
63
+ ref?: string;
64
+ include?: string[];
62
65
  }
63
66
 
64
67
  interface UploadedFile {
@@ -81,6 +84,13 @@ interface BuilderSourceDetails {
81
84
  tokenValues?: Record<string, string>;
82
85
  docCount?: number;
83
86
  warning?: string;
87
+ githubSources?: Array<{
88
+ repoUrl: string;
89
+ ref?: string;
90
+ include?: string[];
91
+ exclude?: string[];
92
+ }>;
93
+ syncedAt?: string;
84
94
  }
85
95
 
86
96
  interface ExistingDesignSystem {
@@ -91,6 +101,25 @@ interface ExistingDesignSystem {
91
101
  builder?: BuilderSourceDetails | null;
92
102
  }
93
103
 
104
+ interface BuilderIndexInput {
105
+ projectName?: string;
106
+ description?: string;
107
+ githubSources?: Array<{
108
+ repoUrl: string;
109
+ ref?: string;
110
+ include?: string[];
111
+ exclude?: string[];
112
+ }>;
113
+ githubRepoUrl?: string;
114
+ codeFiles?: Array<{
115
+ filename: string;
116
+ content: string;
117
+ mimeType?: string;
118
+ encoding?: "utf8" | "base64";
119
+ }>;
120
+ designMd?: string;
121
+ }
122
+
94
123
  type OtherSource = "brand" | "code" | "files" | "existing" | "context";
95
124
 
96
125
  function normalizeWebsiteUrlInput(input: string): string | null {
@@ -134,6 +163,8 @@ export function DesignSystemSetup({
134
163
  const [websiteUrl, setWebsiteUrl] = useState("");
135
164
  const [websiteUrls, setWebsiteUrls] = useState<string[]>([]);
136
165
  const [githubUrl, setGithubUrl] = useState("");
166
+ const [githubRef, setGithubRef] = useState("");
167
+ const [githubPaths, setGithubPaths] = useState("");
137
168
  const [githubLinks, setGithubLinks] = useState<GitHubLink[]>([]);
138
169
  const [codeFiles, setCodeFiles] = useState<UploadedFile[]>([]);
139
170
  const [docFiles, setDocFiles] = useState<UploadedFile[]>([]);
@@ -211,6 +242,13 @@ export function DesignSystemSetup({
211
242
  const imageInputRef = useRef<HTMLInputElement>(null);
212
243
  const figInputRef = useRef<HTMLInputElement>(null);
213
244
  const updateSystemMutation = useActionMutation("update-design-system");
245
+ const syncSystemMutation = useActionMutation(
246
+ "sync-design-system-with-builder",
247
+ );
248
+ const indexSystemMutation = useActionMutation<
249
+ BuilderIndexResult,
250
+ BuilderIndexInput
251
+ >("index-design-system-with-builder");
214
252
 
215
253
  const {
216
254
  data: existingDs,
@@ -267,6 +305,8 @@ export function DesignSystemSetup({
267
305
  setWebsiteUrl("");
268
306
  setWebsiteUrls([]);
269
307
  setGithubUrl("");
308
+ setGithubRef("");
309
+ setGithubPaths("");
270
310
  setGithubLinks([]);
271
311
  setCodeFiles([]);
272
312
  setDocFiles([]);
@@ -323,9 +363,23 @@ export function DesignSystemSetup({
323
363
  const addGithubLink = useCallback(() => {
324
364
  const url = githubUrl.trim();
325
365
  if (!url) return;
326
- setGithubLinks((prev) => [...prev, { id: crypto.randomUUID(), url }]);
366
+ const include = githubPaths
367
+ .split(/[\n,]/)
368
+ .map((path) => path.trim().replace(/^\/+|\/+$/g, ""))
369
+ .filter(Boolean);
370
+ setGithubLinks((prev) => [
371
+ ...prev,
372
+ {
373
+ id: crypto.randomUUID(),
374
+ url,
375
+ ...(githubRef.trim() ? { ref: githubRef.trim() } : {}),
376
+ ...(include.length > 0 ? { include: [...new Set(include)] } : {}),
377
+ },
378
+ ]);
327
379
  setGithubUrl("");
328
- }, [githubUrl]);
380
+ setGithubRef("");
381
+ setGithubPaths("");
382
+ }, [githubPaths, githubRef, githubUrl]);
329
383
 
330
384
  const readTextFiles = useCallback(
331
385
  (
@@ -432,12 +486,66 @@ export function DesignSystemSetup({
432
486
  }
433
487
  };
434
488
 
489
+ const handleSync = useCallback(async () => {
490
+ if (!editingId || !existingDs?.builder?.githubSources?.length) return;
491
+ setGenerating(true);
492
+ try {
493
+ await syncSystemMutation.mutateAsync({ id: editingId });
494
+ toast.success(t("designSystemSetup.syncStarted"));
495
+ } catch (error) {
496
+ toast.error(t("designSystemSetup.syncFailed"), {
497
+ description:
498
+ error instanceof Error
499
+ ? error.message
500
+ : t("designSystemSetup.syncFailed"),
501
+ });
502
+ } finally {
503
+ setGenerating(false);
504
+ }
505
+ }, [editingId, existingDs, syncSystemMutation, t]);
506
+
435
507
  const handleGenerate = useCallback(async () => {
436
508
  if (editingId) {
437
509
  await handleEditSave();
438
510
  return;
439
511
  }
440
512
 
513
+ const isGithubOnlySource =
514
+ githubLinks.length > 0 &&
515
+ websiteUrls.length === 0 &&
516
+ codeFiles.length === 0 &&
517
+ !builderIndexResult &&
518
+ docFiles.length === 0 &&
519
+ imageFiles.length === 0 &&
520
+ !selectedSystemId &&
521
+ !brandNotes.trim() &&
522
+ !customInstructions.trim();
523
+ if (isGithubOnlySource) {
524
+ setGenerating(true);
525
+ try {
526
+ await indexSystemMutation.mutateAsync({
527
+ projectName: companyName.trim() || undefined,
528
+ githubSources: githubLinks.map((link) => ({
529
+ repoUrl: link.url,
530
+ ...(link.ref ? { ref: link.ref } : {}),
531
+ ...(link.include?.length ? { include: link.include } : {}),
532
+ })),
533
+ });
534
+ toast.success(t("designSystemSetup.generationStarted"));
535
+ onComplete();
536
+ } catch (error) {
537
+ toast.error(t("designSystemSetup.updateFailed"), {
538
+ description:
539
+ error instanceof Error
540
+ ? error.message
541
+ : t("designSystemSetup.updateFailed"),
542
+ });
543
+ } finally {
544
+ setGenerating(false);
545
+ }
546
+ return;
547
+ }
548
+
441
549
  const requestedTitle = companyName.trim();
442
550
  const localDesignSystemId = builderIndexResult?.localDesignSystemId;
443
551
  if (requestedTitle && localDesignSystemId) {
@@ -485,7 +593,15 @@ export function DesignSystemSetup({
485
593
 
486
594
  if (githubLinks.length > 0) {
487
595
  parts.push(
488
- `\n## Connect Code: GitHub Repositories\nStart Builder DSI indexing for each repository with \`index-design-system-with-builder\`:\n${githubLinks.map((l) => `- ${l.url}`).join("\n")}\n\nBuilder is the source of truth for repo/code design-system indexing. The action also creates a local selectable proxy design system for Slides flows. If Builder is not connected, stop and tell me to connect Builder (free tier available) from Settings.`,
596
+ `\n## Connect Code: GitHub Repositories\nMake one call to \`index-design-system-with-builder\` with \`githubSources\` set to this JSON array:\n\n\`\`\`json\n${JSON.stringify(
597
+ githubLinks.map((link) => ({
598
+ repoUrl: link.url,
599
+ ...(link.ref ? { ref: link.ref } : {}),
600
+ ...(link.include?.length ? { include: link.include } : {}),
601
+ })),
602
+ null,
603
+ 2,
604
+ )}\n\`\`\`\n\nBuilder is the source of truth for repo/code design-system indexing. The action creates one local selectable proxy design system for Slides flows. If Builder is not connected, stop and tell me to connect Builder (free tier available) from Settings.`,
489
605
  );
490
606
  }
491
607
 
@@ -610,6 +726,7 @@ export function DesignSystemSetup({
610
726
  onComplete,
611
727
  t,
612
728
  updateSystemMutation,
729
+ indexSystemMutation,
613
730
  existingDs,
614
731
  ]);
615
732
 
@@ -651,7 +768,16 @@ export function DesignSystemSetup({
651
768
  </div>
652
769
 
653
770
  {editingId && existingDs?.builder ? (
654
- <BuilderSourceStatus builder={existingDs.builder} />
771
+ <BuilderSourceStatus
772
+ builder={existingDs.builder}
773
+ onSync={
774
+ existingDs.builder.githubSources?.length &&
775
+ existingDs.builder.sourceKind !== "mixed"
776
+ ? handleSync
777
+ : undefined
778
+ }
779
+ syncing={syncSystemMutation.isPending}
780
+ />
655
781
  ) : null}
656
782
 
657
783
  {!editingId && (
@@ -895,8 +1021,28 @@ export function DesignSystemSetup({
895
1021
  {t("designSystemSetup.add")}
896
1022
  </Button>
897
1023
  </div>
1024
+ <div className="grid gap-2 sm:grid-cols-2">
1025
+ <Input
1026
+ value={githubRef}
1027
+ onChange={(e) => setGithubRef(e.target.value)}
1028
+ placeholder={t("designSystemSetup.githubRef")}
1029
+ aria-label={t("designSystemSetup.githubRef")}
1030
+ className="bg-accent border-border text-foreground placeholder:text-muted-foreground"
1031
+ />
1032
+ <Input
1033
+ value={githubPaths}
1034
+ onChange={(e) => setGithubPaths(e.target.value)}
1035
+ placeholder={t("designSystemSetup.githubPaths")}
1036
+ aria-label={t("designSystemSetup.githubPaths")}
1037
+ className="bg-accent border-border text-foreground placeholder:text-muted-foreground"
1038
+ />
1039
+ </div>
898
1040
  <TagList
899
- items={githubLinks.map((l) => l.url)}
1041
+ items={githubLinks.map((l) =>
1042
+ [l.url, l.ref, l.include?.join(", ")]
1043
+ .filter(Boolean)
1044
+ .join(" · "),
1045
+ )}
900
1046
  onRemove={(i) =>
901
1047
  setGithubLinks((p) => p.filter((_, j) => j !== i))
902
1048
  }
@@ -1253,7 +1399,15 @@ function SourceAccordionRow({
1253
1399
  );
1254
1400
  }
1255
1401
 
1256
- function BuilderSourceStatus({ builder }: { builder: BuilderSourceDetails }) {
1402
+ function BuilderSourceStatus({
1403
+ builder,
1404
+ onSync,
1405
+ syncing = false,
1406
+ }: {
1407
+ builder: BuilderSourceDetails;
1408
+ onSync?: () => void;
1409
+ syncing?: boolean;
1410
+ }) {
1257
1411
  const t = useT();
1258
1412
  const docs = builder.docCount ?? builder.docs?.length ?? 0;
1259
1413
  const tokens = Object.keys(builder.tokenValues ?? {}).length;
@@ -1357,20 +1511,39 @@ function BuilderSourceStatus({ builder }: { builder: BuilderSourceDetails }) {
1357
1511
  {statusTitle}
1358
1512
  </div>
1359
1513
  </div>
1360
- {builder.builderUrl ? (
1361
- <div className="mt-3 border-t border-border pt-3">
1362
- <a
1363
- href={withBuilderUtmTrackingParams(builder.builderUrl, {
1364
- campaign: "product",
1365
- content: "design_system_intelligence",
1366
- })}
1367
- target="_blank"
1368
- rel="noreferrer"
1369
- className="inline-flex items-center gap-1.5 text-xs font-medium text-foreground underline-offset-4 hover:underline"
1370
- >
1371
- {t("designSystemSetup.sourceOpenInBuilder")}
1372
- <IconExternalLink className="size-3.5" />
1373
- </a>
1514
+ {builder.builderUrl || onSync ? (
1515
+ <div className="mt-3 flex flex-wrap items-center gap-3 border-t border-border pt-3">
1516
+ {builder.builderUrl ? (
1517
+ <a
1518
+ href={withBuilderUtmTrackingParams(builder.builderUrl, {
1519
+ campaign: "product",
1520
+ content: "design_system_intelligence",
1521
+ })}
1522
+ target="_blank"
1523
+ rel="noreferrer"
1524
+ className="inline-flex items-center gap-1.5 text-xs font-medium text-foreground underline-offset-4 hover:underline"
1525
+ >
1526
+ {t("designSystemSetup.sourceOpenInBuilder")}
1527
+ <IconExternalLink className="size-3.5" />
1528
+ </a>
1529
+ ) : null}
1530
+ {onSync ? (
1531
+ <Button
1532
+ type="button"
1533
+ variant="ghost"
1534
+ size="sm"
1535
+ onClick={onSync}
1536
+ disabled={syncing}
1537
+ className="h-7 gap-1.5 px-2 text-xs"
1538
+ >
1539
+ <IconRefresh
1540
+ className={cn("size-3.5", syncing && "animate-spin")}
1541
+ />
1542
+ {syncing
1543
+ ? t("designSystemSetup.syncingSource")
1544
+ : t("designSystemSetup.syncSource")}
1545
+ </Button>
1546
+ ) : null}
1374
1547
  </div>
1375
1548
  ) : null}
1376
1549
  </section>
@@ -17,8 +17,7 @@ import {
17
17
  import { CSS } from "@dnd-kit/utilities";
18
18
  import { appStateKeyForBrowserTab } from "@shared/app-state-tabs";
19
19
  import { hashSlideContent, type DeckFitState } from "@shared/slide-fit";
20
- import { useRef, useEffect } from "react";
21
- import { useCallback } from "react";
20
+ import { useCallback, useEffect, useRef, useState } from "react";
22
21
 
23
22
  import SlideRenderer from "@/components/deck/SlideRenderer";
24
23
  import type { SlideOverflowInfo } from "@/components/deck/SlideRenderer";
@@ -34,6 +33,7 @@ import { getAspectRatioDims, type AspectRatio } from "@/lib/aspect-ratios";
34
33
  import { TAB_ID } from "@/lib/tab-id";
35
34
 
36
35
  import type { DesignSystemData } from "../../../shared/api";
36
+ import { isSlideTextEditingTarget } from "./slide-text-targets";
37
37
 
38
38
  interface EditorSidebarProps {
39
39
  slides: Slide[];
@@ -317,6 +317,7 @@ export default function EditorSidebar({
317
317
  generatingSlideSelected = false,
318
318
  onSelectGeneratingSlide,
319
319
  }: EditorSidebarProps) {
320
+ const [thumbnailListScrolled, setThumbnailListScrolled] = useState(false);
320
321
  const slideButtonRefs = useRef(new Map<string, HTMLButtonElement>());
321
322
  const measurementsRef = useRef(
322
323
  new Map<
@@ -391,6 +392,10 @@ export default function EditorSidebar({
391
392
  measurementsRef.current.clear();
392
393
  }, [deckId, aspectRatio]);
393
394
 
395
+ useEffect(() => {
396
+ setThumbnailListScrolled(false);
397
+ }, [deckId]);
398
+
394
399
  useEffect(() => {
395
400
  return () => {
396
401
  if (writeTimerRef.current) clearTimeout(writeTimerRef.current);
@@ -419,12 +424,20 @@ export default function EditorSidebar({
419
424
  useEffect(() => {
420
425
  const handleKeyDown = (e: KeyboardEvent) => {
421
426
  if (e.key !== "ArrowUp" && e.key !== "ArrowDown") return;
422
- // Don't intercept if user is typing in an input/textarea or contenteditable
427
+ // Text editing and selected canvas objects own arrow keys. The marker
428
+ // query also covers a transient focus loss after a native selection.
423
429
  const tag = (e.target as HTMLElement)?.tagName;
424
430
  if (
425
431
  tag === "INPUT" ||
426
432
  tag === "TEXTAREA" ||
427
- (e.target as HTMLElement)?.isContentEditable
433
+ isSlideTextEditingTarget(
434
+ e.target,
435
+ document.activeElement,
436
+ document.querySelector(
437
+ '[contenteditable="true"], [data-editing-block="true"]',
438
+ ),
439
+ ) ||
440
+ document.querySelector('[data-slide-element-selected="true"]')
428
441
  )
429
442
  return;
430
443
 
@@ -454,39 +467,51 @@ export default function EditorSidebar({
454
467
 
455
468
  return (
456
469
  <div className="flex h-full min-h-0 w-48 flex-shrink-0 flex-col bg-background sm:w-52">
457
- <div className="relative min-h-0 flex-1 space-y-1 overflow-y-auto overscroll-contain p-2">
458
- <SortableContext
459
- items={slides.map((s) => s.id)}
460
- strategy={verticalListSortingStrategy}
470
+ <div
471
+ className="relative min-h-0 flex-1"
472
+ data-slides-thumbnail-scroll={
473
+ thumbnailListScrolled ? "scrolled" : "top"
474
+ }
475
+ >
476
+ <div
477
+ className="h-full min-h-0 space-y-1 overflow-y-auto overscroll-contain p-2"
478
+ onScroll={(event) => {
479
+ setThumbnailListScrolled(event.currentTarget.scrollTop > 1);
480
+ }}
461
481
  >
462
- {slides.map((slide, index) => (
463
- <SortableSlideThumb
464
- key={slide.id}
465
- slide={slide}
466
- index={index}
467
- isActive={slide.id === activeSlideId}
468
- onSelect={() => onSelectSlide(slide.id)}
469
- readOnly={readOnly}
470
- registerButtonRef={registerSlideButton}
471
- presenceUsers={slidePresence?.get(slide.id) ?? []}
482
+ <SortableContext
483
+ items={slides.map((s) => s.id)}
484
+ strategy={verticalListSortingStrategy}
485
+ >
486
+ {slides.map((slide, index) => (
487
+ <SortableSlideThumb
488
+ key={slide.id}
489
+ slide={slide}
490
+ index={index}
491
+ isActive={slide.id === activeSlideId}
492
+ onSelect={() => onSelectSlide(slide.id)}
493
+ readOnly={readOnly}
494
+ registerButtonRef={registerSlideButton}
495
+ presenceUsers={slidePresence?.get(slide.id) ?? []}
496
+ aspectRatio={aspectRatio}
497
+ designSystem={designSystem}
498
+ aiEditing={aiEditedSlideIds.has(slide.id)}
499
+ onOverflowChange={(info) =>
500
+ handleSlideOverflowChange(slide, info)
501
+ }
502
+ />
503
+ ))}
504
+ </SortableContext>
505
+ {generatingSlide && (
506
+ <GeneratingSlideSkeleton
507
+ index={generatingSlide.index}
472
508
  aspectRatio={aspectRatio}
473
509
  designSystem={designSystem}
474
- aiEditing={aiEditedSlideIds.has(slide.id)}
475
- onOverflowChange={(info) =>
476
- handleSlideOverflowChange(slide, info)
477
- }
510
+ selected={generatingSlideSelected}
511
+ onSelect={onSelectGeneratingSlide}
478
512
  />
479
- ))}
480
- </SortableContext>
481
- {generatingSlide && (
482
- <GeneratingSlideSkeleton
483
- index={generatingSlide.index}
484
- aspectRatio={aspectRatio}
485
- designSystem={designSystem}
486
- selected={generatingSlideSelected}
487
- onSelect={onSelectGeneratingSlide}
488
- />
489
- )}
513
+ )}
514
+ </div>
490
515
  </div>
491
516
  </div>
492
517
  );