@agent-native/core 0.161.16 → 0.161.20

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 (148) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/adding-a-feature/SKILL.md +10 -0
  3. package/corpus/templates/analytics/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  4. package/corpus/templates/analytics/.agents/skills/secrets/SKILL.md +29 -2
  5. package/corpus/templates/analytics/AGENTS.md +1 -0
  6. package/corpus/templates/analytics/server/plugins/auth.ts +4 -0
  7. package/corpus/templates/assets/.agents/skills/adding-a-feature/SKILL.md +10 -0
  8. package/corpus/templates/assets/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  9. package/corpus/templates/assets/.agents/skills/secrets/SKILL.md +29 -2
  10. package/corpus/templates/assets/AGENTS.md +1 -0
  11. package/corpus/templates/brain/.agents/skills/adding-a-feature/SKILL.md +10 -0
  12. package/corpus/templates/brain/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  13. package/corpus/templates/brain/.agents/skills/secrets/SKILL.md +29 -2
  14. package/corpus/templates/brain/AGENTS.md +1 -0
  15. package/corpus/templates/calendar/.agents/skills/adding-a-feature/SKILL.md +10 -0
  16. package/corpus/templates/calendar/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  17. package/corpus/templates/calendar/.agents/skills/secrets/SKILL.md +29 -2
  18. package/corpus/templates/calendar/AGENTS.md +1 -0
  19. package/corpus/templates/chat/.agents/skills/adding-a-feature/SKILL.md +10 -0
  20. package/corpus/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  21. package/corpus/templates/chat/.agents/skills/secrets/SKILL.md +29 -2
  22. package/corpus/templates/chat/AGENTS.md +4 -0
  23. package/corpus/templates/chat/vitest.config.ts +12 -7
  24. package/corpus/templates/clips/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  25. package/corpus/templates/clips/.agents/skills/secrets/SKILL.md +29 -2
  26. package/corpus/templates/clips/AGENTS.md +1 -0
  27. package/corpus/templates/clips/app/components/player/video-player.tsx +4 -0
  28. package/corpus/templates/clips/app/hooks/use-playback-position.ts +10 -2
  29. package/corpus/templates/clips/app/routes/embed.$shareId.tsx +1 -0
  30. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +26 -25
  31. package/corpus/templates/clips/app/routes/share.$shareId.tsx +1 -0
  32. package/corpus/templates/clips/changelog/2026-08-18-clips-no-longer-opens-welcome-window-on-first-launch.md +6 -0
  33. package/corpus/templates/clips/changelog/2026-08-18-clips-now-recovers-interrupted-uploads-and-reliably-restarts.md +6 -0
  34. package/corpus/templates/clips/changelog/2026-08-18-meeting-notifications-no-longer-show-decorative-left-side-lines.md +6 -0
  35. package/corpus/templates/clips/changelog/2026-08-18-pending-recording-files-now-use-a-clips-prefixed-name.md +6 -0
  36. package/corpus/templates/clips/desktop/src/lib/updater.ts +21 -1
  37. package/corpus/templates/clips/desktop/src/main.tsx +0 -3
  38. package/corpus/templates/clips/desktop/src/overlays/meeting-notification.tsx +0 -4
  39. package/corpus/templates/clips/desktop/src/shared/config.ts +0 -1
  40. package/corpus/templates/clips/desktop/src/styles.css +0 -136
  41. package/corpus/templates/clips/desktop/src-tauri/capabilities/default.json +0 -1
  42. package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +0 -45
  43. package/corpus/templates/clips/desktop/src-tauri/src/config.rs +1 -4
  44. package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +3 -11
  45. package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +63 -8
  46. package/corpus/templates/clips/desktop/src-tauri/src/util.rs +98 -0
  47. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/reset-chunks.post.ts +54 -20
  48. package/corpus/templates/content/.agents/skills/adding-a-feature/SKILL.md +10 -0
  49. package/corpus/templates/content/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  50. package/corpus/templates/content/.agents/skills/secrets/SKILL.md +29 -2
  51. package/corpus/templates/content/AGENTS.md +1 -0
  52. package/corpus/templates/crm/.agents/skills/adding-a-feature/SKILL.md +10 -0
  53. package/corpus/templates/crm/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  54. package/corpus/templates/crm/.agents/skills/secrets/SKILL.md +29 -2
  55. package/corpus/templates/crm/AGENTS.md +1 -0
  56. package/corpus/templates/design/.agents/skills/adding-a-feature/SKILL.md +10 -0
  57. package/corpus/templates/design/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  58. package/corpus/templates/design/.agents/skills/secrets/SKILL.md +29 -2
  59. package/corpus/templates/design/AGENTS.md +1 -0
  60. package/corpus/templates/design/app/global.css +6 -0
  61. package/corpus/templates/design/app/i18n-data.ts +11 -0
  62. package/corpus/templates/design/app/lib/agent-chat.ts +9 -8
  63. package/corpus/templates/design/app/root.tsx +1 -1
  64. package/corpus/templates/design/changelog/2026-08-18-design-chat-context-stays-out-of-the-composer.md +6 -0
  65. package/corpus/templates/design/server/plugins/agent-chat.ts +2 -0
  66. package/corpus/templates/dispatch/.agents/skills/adding-a-feature/SKILL.md +10 -0
  67. package/corpus/templates/dispatch/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  68. package/corpus/templates/dispatch/.agents/skills/secrets/SKILL.md +29 -2
  69. package/corpus/templates/dispatch/AGENTS.md +5 -5
  70. package/corpus/templates/factory/.agents/skills/adding-a-feature/SKILL.md +10 -0
  71. package/corpus/templates/factory/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  72. package/corpus/templates/factory/.agents/skills/secrets/SKILL.md +29 -2
  73. package/corpus/templates/factory/AGENTS.md +1 -0
  74. package/corpus/templates/forms/.agents/skills/adding-a-feature/SKILL.md +10 -0
  75. package/corpus/templates/forms/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  76. package/corpus/templates/forms/.agents/skills/secrets/SKILL.md +29 -2
  77. package/corpus/templates/forms/AGENTS.md +1 -0
  78. package/corpus/templates/macros/.agents/skills/adding-a-feature/SKILL.md +10 -0
  79. package/corpus/templates/macros/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  80. package/corpus/templates/macros/.agents/skills/secrets/SKILL.md +29 -2
  81. package/corpus/templates/macros/AGENTS.md +1 -0
  82. package/corpus/templates/mail/.agents/skills/adding-a-feature/SKILL.md +10 -0
  83. package/corpus/templates/mail/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  84. package/corpus/templates/mail/.agents/skills/secrets/SKILL.md +29 -2
  85. package/corpus/templates/mail/AGENTS.md +1 -0
  86. package/corpus/templates/plan/.agents/skills/adding-a-feature/SKILL.md +10 -0
  87. package/corpus/templates/plan/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  88. package/corpus/templates/plan/.agents/skills/secrets/SKILL.md +29 -2
  89. package/corpus/templates/plan/AGENTS.md +1 -0
  90. package/corpus/templates/slides/.agents/skills/adding-a-feature/SKILL.md +10 -0
  91. package/corpus/templates/slides/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  92. package/corpus/templates/slides/.agents/skills/secrets/SKILL.md +29 -2
  93. package/corpus/templates/slides/AGENTS.md +2 -1
  94. package/corpus/templates/tasks/.agents/skills/adding-a-feature/SKILL.md +10 -0
  95. package/corpus/templates/tasks/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  96. package/corpus/templates/tasks/.agents/skills/secrets/SKILL.md +29 -2
  97. package/corpus/templates/tasks/AGENTS.md +1 -0
  98. package/dist/action.d.ts +1 -0
  99. package/dist/action.js +44 -2
  100. package/dist/agent/production-agent.js +28 -5
  101. package/dist/client/chat/run-recovery.js +1 -1
  102. package/dist/client/route-state.js +17 -13
  103. package/dist/collab/struct-routes.d.ts +1 -1
  104. package/dist/notifications/routes.d.ts +1 -1
  105. package/dist/observability/routes.d.ts +5 -5
  106. package/dist/secrets/routes.d.ts +3 -3
  107. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  108. package/dist/server/auth.d.ts +6 -0
  109. package/dist/server/auth.js +23 -5
  110. package/dist/server/realtime-token.d.ts +1 -1
  111. package/dist/templates/chat/.agents/skills/adding-a-feature/SKILL.md +10 -0
  112. package/dist/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  113. package/dist/templates/chat/.agents/skills/secrets/SKILL.md +29 -2
  114. package/dist/templates/chat/AGENTS.md +4 -0
  115. package/dist/templates/chat/vitest.config.ts +12 -7
  116. package/dist/templates/default/.agents/skills/adding-a-feature/SKILL.md +10 -0
  117. package/dist/templates/default/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  118. package/dist/templates/default/.agents/skills/secrets/SKILL.md +29 -2
  119. package/dist/templates/default/AGENTS.md +4 -0
  120. package/dist/templates/headless/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  121. package/dist/templates/headless/.agents/skills/secrets/SKILL.md +29 -2
  122. package/dist/templates/headless/AGENTS.md +4 -0
  123. package/dist/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +10 -0
  124. package/dist/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  125. package/dist/templates/workspace-core/.agents/skills/secrets/SKILL.md +29 -2
  126. package/dist/templates/workspace-core/AGENTS.md +4 -0
  127. package/dist/templates/workspace-root/AGENTS.md +9 -0
  128. package/docs/content/deployment.mdx +70 -0
  129. package/docs/content/faq.mdx +1 -1
  130. package/package.json +1 -1
  131. package/src/templates/chat/.agents/skills/adding-a-feature/SKILL.md +10 -0
  132. package/src/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  133. package/src/templates/chat/.agents/skills/secrets/SKILL.md +29 -2
  134. package/src/templates/chat/AGENTS.md +4 -0
  135. package/src/templates/chat/vitest.config.ts +12 -7
  136. package/src/templates/default/.agents/skills/adding-a-feature/SKILL.md +10 -0
  137. package/src/templates/default/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  138. package/src/templates/default/.agents/skills/secrets/SKILL.md +29 -2
  139. package/src/templates/default/AGENTS.md +4 -0
  140. package/src/templates/headless/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  141. package/src/templates/headless/.agents/skills/secrets/SKILL.md +29 -2
  142. package/src/templates/headless/AGENTS.md +4 -0
  143. package/src/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +10 -0
  144. package/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +24 -0
  145. package/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +29 -2
  146. package/src/templates/workspace-core/AGENTS.md +4 -0
  147. package/src/templates/workspace-root/AGENTS.md +9 -0
  148. package/corpus/templates/clips/desktop/src/overlays/onboarding.tsx +0 -314
@@ -3,8 +3,8 @@ name: secrets
3
3
  description: >-
4
4
  Declaratively register API keys and service credentials a template needs so
5
5
  they appear in the agent sidebar settings UI and the onboarding checklist.
6
- Use for any third-party API key (OpenAI, Stripe, Twilio, etc.) and for
7
- surfacing OAuth connections in the unified settings UI.
6
+ Use before adding any third-party credential or setup UI so API keys, OAuth
7
+ connections, and scoped configuration use the correct shared primitive.
8
8
  scope: dev
9
9
  metadata:
10
10
  internal: true
@@ -24,6 +24,33 @@ encrypted `app_secrets` vault, `saveCredential` / `resolveCredential`, OAuth, or
24
24
  `${keys.NAME}` substitution. Examples must use obvious placeholders such as
25
25
  `<OPENAI_API_KEY>` or `${keys.SLACK_WEBHOOK}`, not real-looking copied values.
26
26
 
27
+ ## Credential Modeling Preflight
28
+
29
+ Before registering a provider's fields, inspect the workspace/provider connection
30
+ catalog first. If a reusable connection exists, use its app grant and scoped
31
+ `resolveWorkspaceConnectionCredential(s)ForApp` path instead of registering a
32
+ parallel secret. Only classify fields for app-local setup when no reusable
33
+ connection exists:
34
+
35
+ - **API or service key** - register it as `kind: "api-key"` with the narrowest
36
+ correct `scope`, a human label, a description, a docs link, and a validator.
37
+ - **OAuth authorization or refresh token** - use the OAuth token store and
38
+ register a `kind: "oauth"` entry so the shared UI renders Connect and the
39
+ runtime owns status, refresh, and reauthorization.
40
+ - **Deploy- or app-level configuration** - use deployment/runtime
41
+ configuration, not a per-user secret row.
42
+ - **Account, customer, manager, or other non-secret identifier** - store it as
43
+ scoped connection metadata or app data, not as a masked secret field.
44
+
45
+ `required: true` is for a logical setup requirement. If a provider needs
46
+ several values, do not automatically create one required checklist item per
47
+ field; use one composite onboarding step or a registered connection readiness
48
+ check.
49
+
50
+ Custom setup UI is allowed for provider-specific prerequisites, ordering, or
51
+ health checks, but it must delegate credential storage and connection state to
52
+ the shared vault/OAuth/settings surfaces.
53
+
27
54
  ## When to use
28
55
 
29
56
  Use this for any external credential your template needs: API keys, service
@@ -29,6 +29,7 @@ video sharing app. The agent and the UI share the same SQL data and actions.
29
29
  - Never hardcode API keys, tokens, webhook URLs, signing secrets, private
30
30
  Builder/internal data, customer data, or credential-looking literals. Use
31
31
  secrets/OAuth/runtime config and obvious placeholders.
32
+ - For external integrations, inspect the workspace/provider connection catalog first; reuse its scoped resolver.
32
33
  - Use actions for recording metadata, transcripts, cleanup, summaries, chapters,
33
34
  comments, spaces/folders, meetings, and sharing. Never bypass access helpers.
34
35
  - Comment text accepts inline Markdown for emphasis, inline code, links, and
@@ -183,6 +183,8 @@ export interface VideoPlayerProps {
183
183
  defaultSpeed?: number;
184
184
  /** Autoplay on mount. */
185
185
  autoPlay?: boolean;
186
+ /** Persist resume position for an authenticated viewer. */
187
+ persistPlaybackPosition?: boolean;
186
188
  /** Start time in ms. */
187
189
  startMs?: number;
188
190
  /** Comment + chapter overlays for the scrubber. */
@@ -247,6 +249,7 @@ export const VideoPlayer = forwardRef<VideoPlayerHandle, VideoPlayerProps>(
247
249
  thumbnailUrl,
248
250
  defaultSpeed = 1.2,
249
251
  autoPlay,
252
+ persistPlaybackPosition = true,
250
253
  startMs,
251
254
  editsJson,
252
255
  comments,
@@ -520,6 +523,7 @@ export const VideoPlayer = forwardRef<VideoPlayerHandle, VideoPlayerProps>(
520
523
  recordingId,
521
524
  videoEl: playbackVideoEl,
522
525
  durationMs,
526
+ enabled: persistPlaybackPosition,
523
527
  explicitStartMs: startMs,
524
528
  allowRestoreWhilePlaying: autoPlay,
525
529
  onRestore: restorePlaybackPosition,
@@ -13,6 +13,7 @@ export interface UsePlaybackPositionOptions {
13
13
  recordingId: string;
14
14
  videoEl: HTMLVideoElement | null;
15
15
  durationMs: number;
16
+ enabled?: boolean;
16
17
  explicitStartMs?: number;
17
18
  allowRestoreWhilePlaying?: boolean;
18
19
  onRestore?: (positionMs: number) => void;
@@ -27,6 +28,7 @@ export function usePlaybackPosition({
27
28
  recordingId,
28
29
  videoEl,
29
30
  durationMs,
31
+ enabled = true,
30
32
  explicitStartMs,
31
33
  allowRestoreWhilePlaying = false,
32
34
  onRestore,
@@ -38,7 +40,7 @@ export function usePlaybackPosition({
38
40
  onRestoreRef.current = onRestore;
39
41
 
40
42
  useEffect(() => {
41
- if (!videoEl || !recordingId) return;
43
+ if (!enabled || !videoEl || !recordingId) return;
42
44
 
43
45
  const controller = new AbortController();
44
46
  let cancelled = false;
@@ -184,5 +186,11 @@ export function usePlaybackPosition({
184
186
  window.removeEventListener("pagehide", onPageHide);
185
187
  document.removeEventListener("visibilitychange", onVisibilityChange);
186
188
  };
187
- }, [allowRestoreWhilePlaying, explicitStartMs, recordingId, videoEl]);
189
+ }, [
190
+ allowRestoreWhilePlaying,
191
+ enabled,
192
+ explicitStartMs,
193
+ recordingId,
194
+ videoEl,
195
+ ]);
188
196
  }
@@ -236,6 +236,7 @@ export default function EmbedRoute() {
236
236
  videoFormat={recording.videoFormat}
237
237
  embedProvider={isLoomEmbedBacked ? "loom" : null}
238
238
  durationMs={recording.durationMs}
239
+ persistPlaybackPosition={false}
239
240
  editsJson={recording.editsJson}
240
241
  thumbnailUrl={recording.thumbnailUrl}
241
242
  defaultSpeed={parsePlaybackSpeed(recording.defaultSpeed) ?? 1.2}
@@ -187,6 +187,30 @@ function nativeSaveFailureMessage(reason: string | null | undefined): string {
187
187
  return "The desktop recorder finished and saved a local copy, but Clips could not upload it. You can retry from the Clips menu without recording again.";
188
188
  }
189
189
 
190
+ export function BackToLibraryButton() {
191
+ const t = useT();
192
+ const navigate = useNavigate();
193
+
194
+ return (
195
+ <Tooltip>
196
+ <TooltipTrigger asChild>
197
+ <Button
198
+ variant="ghost"
199
+ size="icon"
200
+ className="shrink-0"
201
+ onClick={() => navigate("/library", { replace: true })}
202
+ aria-label={t("recordingPage.backToLibrary")}
203
+ >
204
+ <IconArrowLeft className="h-4 w-4 rtl:-scale-x-100" />
205
+ </Button>
206
+ </TooltipTrigger>
207
+ <TooltipContent side="bottom" align="start">
208
+ {t("recordingPage.backToLibrary")}
209
+ </TooltipContent>
210
+ </Tooltip>
211
+ );
212
+ }
213
+
190
214
  function parseTimeParam(raw: string | null): number {
191
215
  if (!raw) return 0;
192
216
  const value = raw.trim();
@@ -965,17 +989,7 @@ export default function RecordingPage() {
965
989
  return (
966
990
  <div className="flex min-h-screen w-full flex-col bg-background">
967
991
  <header className="flex min-w-0 shrink-0 items-center gap-3 border-b border-border px-3 py-2 sm:px-4 sm:py-3">
968
- <Button
969
- asChild
970
- variant="ghost"
971
- size="icon"
972
- className="shrink-0"
973
- aria-label={t("recordingPage.backToLibrary")}
974
- >
975
- <Link to="/library" replace>
976
- <IconArrowLeft className="h-4 w-4 rtl:-scale-x-100" />
977
- </Link>
978
- </Button>
992
+ <BackToLibraryButton />
979
993
  <div className="min-w-0 flex-1">
980
994
  <p className="truncate text-sm font-medium">{visibleTitle}</p>
981
995
  <p className="truncate text-xs text-muted-foreground">
@@ -1294,20 +1308,7 @@ export default function RecordingPage() {
1294
1308
  {/* Main video column */}
1295
1309
  <div className="flex w-full min-w-0 flex-col xl:flex-1">
1296
1310
  <header className="flex min-w-0 shrink-0 items-center gap-2 border-b border-border px-3 py-2 sm:gap-3 sm:px-4 sm:py-3">
1297
- <Button
1298
- asChild
1299
- variant="ghost"
1300
- size="icon"
1301
- className="shrink-0 sm:w-auto sm:px-2"
1302
- aria-label={t("recordingPage.backToLibrary")}
1303
- >
1304
- <Link to="/library" replace>
1305
- <IconArrowLeft className="h-4 w-4 rtl:-scale-x-100" />
1306
- <span className="hidden sm:inline">
1307
- {t("recordingPage.backToLibrary")}
1308
- </span>
1309
- </Link>
1310
- </Button>
1311
+ <BackToLibraryButton />
1311
1312
  <div className="flex-1 min-w-0">
1312
1313
  <EditableRecordingTitle
1313
1314
  recordingId={recording.id}
@@ -1226,6 +1226,7 @@ export default function ShareRoute() {
1226
1226
  videoFormat={recording.videoFormat}
1227
1227
  embedProvider={isLoomEmbedBacked ? "loom" : null}
1228
1228
  durationMs={recording.durationMs}
1229
+ persistPlaybackPosition={Boolean(session)}
1229
1230
  editsJson={recording.editsJson}
1230
1231
  thumbnailUrl={recording.thumbnailUrl}
1231
1232
  role={viewerRole ?? (viewerCanEdit ? "owner" : "viewer")}
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: removed
3
+ date: 2026-08-18
4
+ ---
5
+
6
+ Clips no longer opens a welcome window on first launch.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-08-18
4
+ ---
5
+
6
+ Clips now recovers interrupted uploads and reliably restarts after installing desktop updates.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-08-18
4
+ ---
5
+
6
+ Meeting notifications no longer show decorative left-side lines.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-08-18
4
+ ---
5
+
6
+ Pending recording files now use a Clips-prefixed name instead of a feature-ambiguous stem.
@@ -1,7 +1,10 @@
1
- import { relaunch } from "@tauri-apps/plugin-process";
1
+ import { invoke } from "@tauri-apps/api/core";
2
+ import { exit, relaunch } from "@tauri-apps/plugin-process";
2
3
  import { check, Update } from "@tauri-apps/plugin-updater";
3
4
  import { useEffect, useState } from "react";
4
5
 
6
+ import { isMacPlatform } from "./platform";
7
+
5
8
  declare const __CLIPS_DESKTOP_LOCAL_BUILD__: boolean;
6
9
 
7
10
  export type UpdateStatus =
@@ -166,9 +169,26 @@ export async function installAndRestart(): Promise<void> {
166
169
  // bundle no longer matches the running process as short as possible — the
167
170
  // process is torn down by `relaunch()` right after, so capture never runs
168
171
  // against a swapped-out bundle.
172
+ const restartBundlePath = await invoke<string>("restart_bundle_path").catch(
173
+ (err) => {
174
+ console.error("[clips-updater] restart target lookup failed:", err);
175
+ return null;
176
+ },
177
+ );
169
178
  if (pendingUpdate) {
170
179
  await pendingUpdate.install();
171
180
  }
181
+ if (isMacPlatform() && restartBundlePath) {
182
+ try {
183
+ await invoke("schedule_restart_after_exit", {
184
+ bundlePath: restartBundlePath,
185
+ });
186
+ await exit(0);
187
+ return;
188
+ } catch (err) {
189
+ console.error("[clips-updater] macOS restart handoff failed:", err);
190
+ }
191
+ }
172
192
  await relaunch();
173
193
  }
174
194
 
@@ -11,7 +11,6 @@ import { FlowBar } from "./overlays/flow-bar";
11
11
  import { MeetingNotification } from "./overlays/meeting-notification";
12
12
  import { MeetingNub } from "./overlays/meeting-nub";
13
13
  import { MonitorPicker } from "./overlays/monitor-picker";
14
- import { Onboarding } from "./overlays/onboarding";
15
14
  import { Preparing } from "./overlays/preparing";
16
15
  import { RecordingPill } from "./overlays/recording-pill";
17
16
  import { RegionGuideEditor, RegionGuides } from "./overlays/region-guides";
@@ -47,8 +46,6 @@ function pickRoute(route: string): React.ReactElement {
47
46
  return <Finalizing />;
48
47
  case "preparing":
49
48
  return <Preparing />;
50
- case "onboarding":
51
- return <Onboarding />;
52
49
  case "meeting-notif":
53
50
  return <MeetingNotification />;
54
51
  case "meeting-nub":
@@ -386,7 +386,6 @@ export function MeetingNotification() {
386
386
  return <div className="meeting-notification-root" />;
387
387
  }
388
388
 
389
- const isCalendar = data.type === "calendar";
390
389
  const hasJoin = Boolean(data.joinUrl);
391
390
  const provider = detectMeetingJoinProvider(data.joinUrl, data.platform);
392
391
  const providerName = joinProviderLabel(provider);
@@ -404,9 +403,6 @@ export function MeetingNotification() {
404
403
  onMouseEnter={() => setDomHovered(true)}
405
404
  onMouseLeave={() => setDomHovered(false)}
406
405
  >
407
- <div
408
- className={`meeting-notification-bar ${isCalendar ? "meeting-notification-bar-calendar" : "meeting-notification-bar-adhoc"}`}
409
- />
410
406
  <div className="meeting-notification-content">
411
407
  <div className="meeting-notification-title">{data.title}</div>
412
408
  <div className="meeting-notification-subtitle">{data.subtitle}</div>
@@ -129,7 +129,6 @@ export interface FeatureConfig {
129
129
  showInScreenCapture: boolean;
130
130
  regionGuides: RegionGuidesConfig;
131
131
  screenMemory: ScreenMemoryConfig;
132
- onboardingComplete: boolean;
133
132
  whisperModelEnabled: boolean;
134
133
  whisperModelId: string;
135
134
  }
@@ -5263,124 +5263,6 @@ body[data-clips-route="recording-pill"] #root {
5263
5263
  }
5264
5264
  }
5265
5265
 
5266
- /* ------------------------------------------------------------------------- */
5267
- /* Overlay: onboarding */
5268
- /* ------------------------------------------------------------------------- */
5269
-
5270
- .onboarding-root {
5271
- position: fixed;
5272
- inset: 0;
5273
- display: flex;
5274
- align-items: center;
5275
- justify-content: center;
5276
- background: #212121;
5277
- color: #f5f5f5;
5278
- }
5279
-
5280
- .onboarding-card {
5281
- display: flex;
5282
- flex-direction: column;
5283
- align-items: center;
5284
- gap: 20px;
5285
- padding: 40px 36px;
5286
- max-width: 380px;
5287
- width: 100%;
5288
- }
5289
-
5290
- .onboarding-title {
5291
- font-size: 22px;
5292
- font-weight: 700;
5293
- color: white;
5294
- margin: 0;
5295
- letter-spacing: -0.3px;
5296
- }
5297
-
5298
- .onboarding-subtitle {
5299
- font-size: 14px;
5300
- color: #a3a3a3;
5301
- margin: -8px 0 0;
5302
- }
5303
-
5304
- .onboarding-features {
5305
- display: flex;
5306
- flex-direction: column;
5307
- gap: 10px;
5308
- width: 100%;
5309
- }
5310
-
5311
- .onboarding-feature {
5312
- display: flex;
5313
- align-items: flex-start;
5314
- gap: 12px;
5315
- padding: 14px 16px;
5316
- background: rgba(255, 255, 255, 0.04);
5317
- border: 1px solid rgba(255, 255, 255, 0.08);
5318
- border-radius: 12px;
5319
- cursor: pointer;
5320
- user-select: none;
5321
- }
5322
-
5323
- .onboarding-feature:hover {
5324
- background: rgba(255, 255, 255, 0.06);
5325
- }
5326
-
5327
- .onboarding-checkbox {
5328
- width: 18px;
5329
- height: 18px;
5330
- margin: 1px 0 0;
5331
- accent-color: #16a34a;
5332
- cursor: pointer;
5333
- flex-shrink: 0;
5334
- }
5335
-
5336
- .onboarding-feature-text {
5337
- display: flex;
5338
- flex-direction: column;
5339
- gap: 3px;
5340
- }
5341
-
5342
- .onboarding-feature-name {
5343
- font-size: 14px;
5344
- font-weight: 600;
5345
- color: white;
5346
- }
5347
-
5348
- .onboarding-feature-desc {
5349
- font-size: 12px;
5350
- color: #a3a3a3;
5351
- line-height: 1.4;
5352
- }
5353
-
5354
- .onboarding-cta {
5355
- width: 100%;
5356
- border: none;
5357
- border-radius: 12px;
5358
- padding: 12px 16px;
5359
- background: #f5f5f5;
5360
- color: #212121;
5361
- font-size: 14px;
5362
- font-weight: 600;
5363
- cursor: pointer;
5364
- height: 44px;
5365
- display: inline-flex;
5366
- align-items: center;
5367
- justify-content: center;
5368
- margin-top: 4px;
5369
- }
5370
-
5371
- .onboarding-cta:hover {
5372
- background: #e5e5e5;
5373
- }
5374
-
5375
- .onboarding-cta:active {
5376
- transform: translateY(1px);
5377
- }
5378
-
5379
- .onboarding-cta:disabled {
5380
- opacity: 0.6;
5381
- cursor: not-allowed;
5382
- }
5383
-
5384
5266
  /* ------------------------------------------------------------------------- */
5385
5267
  /* Overlay: meeting notification */
5386
5268
  /* ------------------------------------------------------------------------- */
@@ -5414,24 +5296,6 @@ body[data-clips-route="recording-pill"] #root {
5414
5296
  max-width: 440px;
5415
5297
  }
5416
5298
 
5417
- .meeting-notification-bar {
5418
- width: 3px;
5419
- height: 36px;
5420
- border-radius: 999px;
5421
- flex-shrink: 0;
5422
- }
5423
-
5424
- .meeting-notification-bar-calendar {
5425
- background: #7dd3fc;
5426
- }
5427
-
5428
- .meeting-notification-bar-adhoc {
5429
- background: transparent;
5430
- border-left: 3px dashed #a3a3a3;
5431
- width: 3px;
5432
- border-radius: 0;
5433
- }
5434
-
5435
5299
  .meeting-notification-content {
5436
5300
  flex: 1;
5437
5301
  min-width: 0;
@@ -9,7 +9,6 @@
9
9
  "bubble",
10
10
  "signin",
11
11
  "finalizing",
12
- "onboarding",
13
12
  "meeting-notif",
14
13
  "meeting-nub",
15
14
  "flow-bar",
@@ -48,7 +48,6 @@ const REGION_GUIDES_LABEL: &str = "region-guides";
48
48
  const REGION_GUIDE_EDITOR_LABEL: &str = "region-guide-editor";
49
49
  const REGION_RECORD_BORDER_LABEL: &str = "region-record-border";
50
50
  const MONITOR_PICKER_LABEL_PREFIX: &str = "monitor-picker-";
51
- const ONBOARDING_LABEL: &str = "onboarding";
52
51
  const OVERLAY_LABELS: &[&str] = &[
53
52
  COUNTDOWN_LABEL,
54
53
  TOOLBAR_LABEL,
@@ -59,8 +58,6 @@ const OVERLAY_LABELS: &[&str] = &[
59
58
  REGION_GUIDES_LABEL,
60
59
  REGION_RECORD_BORDER_LABEL,
61
60
  ];
62
- const ONBOARDING_WIDTH_LOGICAL: f64 = 560.0;
63
- const ONBOARDING_HEIGHT_LOGICAL: f64 = 640.0;
64
61
 
65
62
  /// Physical-pixel bubble sizes. Logical px on retina = physical / 2, so these
66
63
  /// map to ~96 (small) and ~180 (medium) logical px — matching Loom's camera
@@ -599,48 +596,6 @@ pub async fn hide_finalizing(app: AppHandle) -> Result<(), String> {
599
596
  Ok(())
600
597
  }
601
598
 
602
- /// First-run onboarding window (ONBOARD-WINDOW). Unlike the transparent,
603
- /// click-through overlays above, this is a normal decorated, focused window
604
- /// with its own solid dark background (`.onboarding-root` in styles.css) —
605
- /// centered on the primary display so it reads as a real app window, not a
606
- /// HUD. Called once from `lib.rs`'s `setup()` when `onboarding_complete` is
607
- /// false. Reuses an existing window if one is somehow already open (e.g. a
608
- /// hot-reload re-triggering setup in dev) instead of building a second one.
609
- pub fn show_onboarding_window(app: &AppHandle) {
610
- if let Some(existing) = app.get_webview_window(ONBOARDING_LABEL) {
611
- let _ = existing.show();
612
- let _ = existing.set_focus();
613
- return;
614
- }
615
- let win =
616
- match WebviewWindowBuilder::new(app, ONBOARDING_LABEL, build_overlay_url("onboarding"))
617
- .title("Welcome to Clips")
618
- .inner_size(ONBOARDING_WIDTH_LOGICAL, ONBOARDING_HEIGHT_LOGICAL)
619
- .resizable(false)
620
- .center()
621
- .focused(true)
622
- .build()
623
- {
624
- Ok(w) => w,
625
- Err(e) => {
626
- eprintln!("[clips-tray] onboarding window build failed: {}", e);
627
- return;
628
- }
629
- };
630
- let _ = win.show();
631
- }
632
-
633
- /// Close the first-run onboarding window once the overlay's finish handler
634
- /// has saved feature config + opened the popover. Idempotent — closing an
635
- /// already-closed/never-built window is a no-op.
636
- #[tauri::command]
637
- pub async fn hide_onboarding_window(app: AppHandle) -> Result<(), String> {
638
- if let Some(w) = app.get_webview_window(ONBOARDING_LABEL) {
639
- let _ = w.close();
640
- }
641
- Ok(())
642
- }
643
-
644
599
  /// Full-screen, click-through recording guides. The React view renders the
645
600
  /// saved translucent rectangles, while AppKit marks the whole overlay as
646
601
  /// non-shareable so the guides stay private to the recorder.
@@ -134,7 +134,6 @@ pub struct FeatureConfig {
134
134
  pub region_guides: RegionGuidesConfig,
135
135
  #[serde(default)]
136
136
  pub screen_memory: ScreenMemoryConfig,
137
- pub onboarding_complete: bool,
138
137
  #[serde(default = "default_whisper_model_enabled")]
139
138
  pub whisper_model_enabled: bool,
140
139
  #[serde(default = "default_whisper_model_id")]
@@ -238,7 +237,6 @@ impl Default for FeatureConfig {
238
237
  show_in_screen_capture: false,
239
238
  region_guides: RegionGuidesConfig::default(),
240
239
  screen_memory: ScreenMemoryConfig::default(),
241
- onboarding_complete: false,
242
240
  whisper_model_enabled: default_whisper_model_enabled(),
243
241
  whisper_model_id: default_whisper_model_id(),
244
242
  }
@@ -437,8 +435,7 @@ mod tests {
437
435
  let config: FeatureConfig = serde_json::from_value(serde_json::json!({
438
436
  "clipsEnabled": true,
439
437
  "meetingsEnabled": true,
440
- "voiceEnabled": true,
441
- "onboardingComplete": true
438
+ "voiceEnabled": true
442
439
  }))
443
440
  .unwrap();
444
441
 
@@ -52,7 +52,7 @@ use state::{
52
52
  };
53
53
  use util::{
54
54
  configure_overlay_behavior, is_recording_active, present_interactive_window,
55
- set_capture_excluded,
55
+ restart_bundle_path, schedule_restart_after_exit, set_capture_excluded,
56
56
  };
57
57
 
58
58
  // Embedded fallback icon — a tiny 16x16 solid purple PNG so the binary always
@@ -116,7 +116,6 @@ pub fn run() {
116
116
  clips::close_signin,
117
117
  clips::show_flow_bar,
118
118
  clips::hide_flow_bar,
119
- clips::hide_onboarding_window,
120
119
  clips::complete_voice_dictation,
121
120
  clips::paste_last_dictation,
122
121
  clips::set_recording_state,
@@ -248,6 +247,8 @@ pub fn run() {
248
247
  // persistent log file (production debugging)
249
248
  logfile::frontend_log,
250
249
  logfile::open_logs,
250
+ restart_bundle_path,
251
+ schedule_restart_after_exit,
251
252
  ])
252
253
  .plugin(tauri_plugin_shell::init())
253
254
  .plugin(tauri_plugin_deep_link::init())
@@ -341,15 +342,6 @@ pub fn run() {
341
342
  tray::build_tray(app)?;
342
343
  config::sync_launch_at_login(app.handle());
343
344
  let feature_config = config::feature_config(app.handle());
344
- // ONBOARD-WINDOW: first-run onboarding never had a Rust-side
345
- // window to appear in — the `#onboarding` hash route and overlay
346
- // component existed, but nothing ever called
347
- // WebviewWindowBuilder for it. Build it here, after the popover
348
- // exists, gated on the persisted `onboarding_complete` flag so
349
- // returning users never see it again.
350
- if !feature_config.onboarding_complete {
351
- clips::show_onboarding_window(app.handle());
352
- }
353
345
  screen_memory::sync_from_config(app.handle(), &feature_config);
354
346
  // Re-show always-on region guides after relaunch/reboot when the
355
347
  // setting is on (no-op if a recording owns the window or the