@agent-native/core 0.0.0-beta-20260820190051 → 0.0.0-beta-20260820194548

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 (41) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/assets/actions/navigate.ts +2 -2
  3. package/corpus/templates/brain/actions/navigate.ts +2 -2
  4. package/corpus/templates/chat/actions/navigate.ts +2 -2
  5. package/corpus/templates/clips/CHANGELOG.md +1 -0
  6. package/corpus/templates/clips/actions/import-loom-recording.ts +16 -4
  7. package/corpus/templates/clips/actions/navigate.ts +2 -2
  8. package/corpus/templates/clips/app/components/player/clips-share-trigger.tsx +17 -0
  9. package/corpus/templates/clips/app/components/player/delete-recording-menu.tsx +3 -3
  10. package/corpus/templates/clips/app/global.css +4 -0
  11. package/corpus/templates/clips/app/lib/download-release-channel.ts +10 -0
  12. package/corpus/templates/clips/app/routes/download.tsx +67 -44
  13. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +6 -11
  14. package/corpus/templates/clips/app/routes/record.tsx +5 -5
  15. package/corpus/templates/clips/app/routes/share.$shareId.tsx +22 -25
  16. package/corpus/templates/clips/changelog/2026-08-20-clip-viewer-share-actions.md +6 -0
  17. package/corpus/templates/clips/changelog/2026-08-20-preparing-recording-countdown-overlay.md +1 -0
  18. package/corpus/templates/content/actions/navigate.ts +2 -2
  19. package/corpus/templates/design/AGENTS.md +2 -1
  20. package/corpus/templates/design/actions/generate-screens.ts +5 -2
  21. package/corpus/templates/design/actions/go-to-main-component.ts +2 -2
  22. package/corpus/templates/design/actions/navigate.ts +2 -2
  23. package/corpus/templates/design/actions/open-component-source.ts +4 -4
  24. package/corpus/templates/design/actions/present-design-variants.ts +1 -2
  25. package/corpus/templates/design/actions/show-design-questions.ts +5 -2
  26. package/corpus/templates/design/app/i18n-data.ts +122 -0
  27. package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +163 -6
  28. package/corpus/templates/factory/actions/navigate.ts +2 -2
  29. package/dist/agent/engine/registry.js +27 -14
  30. package/dist/collab/routes.d.ts +1 -1
  31. package/dist/collab/struct-routes.d.ts +1 -1
  32. package/dist/notifications/routes.d.ts +3 -3
  33. package/dist/observability/routes.d.ts +6 -6
  34. package/dist/resources/handlers.d.ts +2 -2
  35. package/dist/secrets/routes.d.ts +9 -9
  36. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  37. package/dist/server/realtime-token.d.ts +1 -1
  38. package/dist/server/transcribe-voice.d.ts +1 -1
  39. package/dist/templates/chat/actions/navigate.ts +2 -2
  40. package/package.json +1 -1
  41. package/src/templates/chat/actions/navigate.ts +2 -2
package/corpus/README.md CHANGED
@@ -31,4 +31,4 @@ rg -n "defineAction|useActionQuery" node_modules/@agent-native/core/corpus
31
31
 
32
32
  ## Generated Counts
33
33
 
34
- - template files: 8486
34
+ - template files: 8489
@@ -1,5 +1,5 @@
1
1
  import { defineAction } from "@agent-native/core";
2
- import { writeAppState } from "@agent-native/core/application-state";
2
+ import { writeAppStateForCurrentTab } from "@agent-native/core/application-state";
3
3
  import { z } from "zod";
4
4
 
5
5
  export default defineAction({
@@ -43,7 +43,7 @@ export default defineAction({
43
43
  if (!args.view && !args.path) {
44
44
  throw new Error("view or path is required.");
45
45
  }
46
- await writeAppState("navigate", args);
46
+ await writeAppStateForCurrentTab("navigate", args);
47
47
  return { navigating: true, ...args };
48
48
  },
49
49
  });
@@ -1,5 +1,5 @@
1
1
  import { defineAction } from "@agent-native/core";
2
- import { writeAppState } from "@agent-native/core/application-state";
2
+ import { writeAppStateForCurrentTab } from "@agent-native/core/application-state";
3
3
  import { z } from "zod";
4
4
 
5
5
  export default defineAction({
@@ -37,7 +37,7 @@ export default defineAction({
37
37
  }),
38
38
  http: false,
39
39
  run: async (args) => {
40
- await writeAppState("navigate", { ...args, ts: Date.now() });
40
+ await writeAppStateForCurrentTab("navigate", { ...args, ts: Date.now() });
41
41
  return { navigate: args };
42
42
  },
43
43
  });
@@ -14,7 +14,7 @@
14
14
  */
15
15
 
16
16
  import { defineAction } from "@agent-native/core/action";
17
- import { writeAppState } from "@agent-native/core/application-state";
17
+ import { writeAppStateForCurrentTab } from "@agent-native/core/application-state";
18
18
  import { z } from "zod";
19
19
 
20
20
  export default defineAction({
@@ -35,7 +35,7 @@ export default defineAction({
35
35
  if (args.path) nav.path = args.path;
36
36
  if (args.threadId) nav.threadId = args.threadId;
37
37
  nav._writeId = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
38
- await writeAppState("navigate", nav);
38
+ await writeAppStateForCurrentTab("navigate", nav);
39
39
  return `Navigating to ${args.view || args.path}`;
40
40
  },
41
41
  });
@@ -7,6 +7,7 @@ from the command menu (Cmd+K → "What's new") or from Settings.
7
7
 
8
8
  ### Improved
9
9
 
10
+ - Clip viewers now make Share the primary action and keep the overflow control compact and vertical.
10
11
  - Recording controls now follow you across browser tabs and page navigations.
11
12
 
12
13
  ### Fixed
@@ -1,5 +1,8 @@
1
1
  import { defineAction } from "@agent-native/core";
2
- import { writeAppState } from "@agent-native/core/application-state";
2
+ import {
3
+ writeAppState,
4
+ writeAppStateForCurrentTab,
5
+ } from "@agent-native/core/application-state";
3
6
  import { ssrfSafeFetch } from "@agent-native/core/extensions/url-safety";
4
7
  import { uploadFile } from "@agent-native/core/file-upload";
5
8
  import { buildDeepLink } from "@agent-native/core/server";
@@ -315,7 +318,10 @@ export default defineAction({
315
318
  updatedAt: now,
316
319
  });
317
320
  await writeAppState("refresh-signal", { ts: Date.now() });
318
- await writeAppState("navigate", { view: "recording", recordingId: id });
321
+ await writeAppStateForCurrentTab("navigate", {
322
+ view: "recording",
323
+ recordingId: id,
324
+ });
319
325
 
320
326
  return {
321
327
  recordingId: id,
@@ -386,7 +392,10 @@ export default defineAction({
386
392
  updatedAt: now,
387
393
  });
388
394
  await writeAppState("refresh-signal", { ts: Date.now() });
389
- await writeAppState("navigate", { view: "recording", recordingId: id });
395
+ await writeAppStateForCurrentTab("navigate", {
396
+ view: "recording",
397
+ recordingId: id,
398
+ });
390
399
 
391
400
  try {
392
401
  console.log("[import-loom-recording] dispatching loom-import job", {
@@ -540,7 +549,10 @@ export default defineAction({
540
549
  updatedAt: now,
541
550
  });
542
551
  await writeAppState("refresh-signal", { ts: Date.now() });
543
- await writeAppState("navigate", { view: "recording", recordingId: id });
552
+ await writeAppStateForCurrentTab("navigate", {
553
+ view: "recording",
554
+ recordingId: id,
555
+ });
544
556
 
545
557
  return {
546
558
  recordingId: id,
@@ -16,7 +16,7 @@
16
16
  */
17
17
 
18
18
  import { defineAction } from "@agent-native/core";
19
- import { writeAppState } from "@agent-native/core/application-state";
19
+ import { writeAppStateForCurrentTab } from "@agent-native/core/application-state";
20
20
  import { z } from "zod";
21
21
 
22
22
  const Views = [
@@ -91,7 +91,7 @@ export default defineAction({
91
91
  if (args.shareId) nav.shareId = args.shareId;
92
92
  if (args.search) nav.search = args.search;
93
93
  if (args.path) nav.path = args.path;
94
- await writeAppState("navigate", nav);
94
+ await writeAppStateForCurrentTab("navigate", nav);
95
95
  const target =
96
96
  args.path ||
97
97
  [
@@ -0,0 +1,17 @@
1
+ import {
2
+ ShareTrigger,
3
+ type ShareTriggerProps,
4
+ } from "@agent-native/toolkit/sharing";
5
+
6
+ import { cn } from "@/lib/utils";
7
+
8
+ export function ClipsShareTrigger({ className, ...props }: ShareTriggerProps) {
9
+ return (
10
+ <ShareTrigger
11
+ {...props}
12
+ intent="primary"
13
+ emphasis="solid"
14
+ className={cn("clips-share-trigger shrink-0", className)}
15
+ />
16
+ );
17
+ }
@@ -1,6 +1,6 @@
1
1
  import { useActionMutation } from "@agent-native/core/client/hooks";
2
2
  import { useT } from "@agent-native/core/client/i18n";
3
- import { IconDots, IconDownload, IconTrash } from "@tabler/icons-react";
3
+ import { IconDotsVertical, IconDownload, IconTrash } from "@tabler/icons-react";
4
4
  import { useCallback, useRef, useState } from "react";
5
5
  import { toast } from "sonner";
6
6
 
@@ -93,10 +93,10 @@ export function RecordingOptionsMenu({
93
93
  <Button
94
94
  variant="ghost"
95
95
  size="icon"
96
- className="shrink-0"
96
+ className="-mx-1.5 h-auto w-auto shrink-0 px-0.5 py-1.5"
97
97
  aria-label={t("deleteRecordingMenu.clipOptions")}
98
98
  >
99
- <IconDots className="h-4 w-4" />
99
+ <IconDotsVertical className="h-4 w-4" />
100
100
  </Button>
101
101
  </DropdownMenuTrigger>
102
102
  <DropdownMenuContent align="end" className="w-44">
@@ -73,6 +73,10 @@
73
73
  --highlight-foreground: 0 0% 10%;
74
74
  }
75
75
 
76
+ .dark .clips-share-trigger {
77
+ --primary: 0 0% 100%;
78
+ }
79
+
76
80
  @theme inline {
77
81
  --color-highlight: hsl(var(--highlight));
78
82
  --color-highlight-foreground: hsl(var(--highlight-foreground));
@@ -0,0 +1,10 @@
1
+ export type DownloadReleaseChannel = "production" | "nightly";
2
+
3
+ export const BETA_CLIPS_HOSTNAME = "beta.clips.agent-native.com";
4
+
5
+ export function getDefaultDownloadChannel(
6
+ hostname: string | undefined,
7
+ ): DownloadReleaseChannel {
8
+ const normalized = hostname?.trim().toLowerCase().replace(/\.$/, "");
9
+ return normalized === BETA_CLIPS_HOSTNAME ? "nightly" : "production";
10
+ }
@@ -4,12 +4,20 @@ import {
4
4
  IconBrandChrome,
5
5
  IconBrandApple,
6
6
  IconBrandWindows,
7
+ IconChevronDown,
7
8
  IconDeviceDesktop,
8
9
  IconExternalLink,
9
10
  } from "@tabler/icons-react";
10
11
  import { useEffect, useState } from "react";
11
12
 
12
13
  import { Button } from "@/components/ui/button";
14
+ import {
15
+ DropdownMenu,
16
+ DropdownMenuContent,
17
+ DropdownMenuRadioGroup,
18
+ DropdownMenuRadioItem,
19
+ DropdownMenuTrigger,
20
+ } from "@/components/ui/dropdown-menu";
13
21
  import { Skeleton } from "@/components/ui/skeleton";
14
22
  import enMessages from "@/i18n/en-US";
15
23
  import {
@@ -17,6 +25,10 @@ import {
17
25
  markDesktopAppDownloaded,
18
26
  useClipsChromeExtensionEnabled,
19
27
  } from "@/lib/capture-install-options";
28
+ import {
29
+ getDefaultDownloadChannel,
30
+ type DownloadReleaseChannel,
31
+ } from "@/lib/download-release-channel";
20
32
 
21
33
  export function meta() {
22
34
  return [
@@ -29,7 +41,6 @@ export function meta() {
29
41
  }
30
42
 
31
43
  type PlatformId = "mac" | "windows" | "linux";
32
- type ReleaseChannel = "production" | "nightly";
33
44
 
34
45
  interface PlatformVariant {
35
46
  id: PlatformId;
@@ -204,7 +215,8 @@ function secondaryDownloadButton(
204
215
  export default function DownloadPage() {
205
216
  const chromeExtensionEnabled = useClipsChromeExtensionEnabled();
206
217
  const t = useT();
207
- const [channel, setChannel] = useState<ReleaseChannel>("production");
218
+ const [channel, setChannel] = useState<DownloadReleaseChannel>("production");
219
+ const [hostResolved, setHostResolved] = useState(false);
208
220
  const [manifest, setManifest] = useState<Manifest | null>(null);
209
221
  const [manifestError, setManifestError] = useState(false);
210
222
  const [detected, setDetected] = useState<PlatformId | null>(null);
@@ -215,6 +227,13 @@ export default function DownloadPage() {
215
227
  }, []);
216
228
 
217
229
  useEffect(() => {
230
+ setChannel(getDefaultDownloadChannel(window.location.hostname));
231
+ setHostResolved(true);
232
+ }, []);
233
+
234
+ useEffect(() => {
235
+ if (!hostResolved) return;
236
+
218
237
  let cancelled = false;
219
238
  setManifest(null);
220
239
  setManifestError(false);
@@ -233,13 +252,15 @@ export default function DownloadPage() {
233
252
  return () => {
234
253
  cancelled = true;
235
254
  };
236
- }, [channel, manifestRequest]);
255
+ }, [channel, hostResolved, manifestRequest]);
237
256
 
238
257
  const retryManifest = () => {
239
258
  setManifestRequest((request) => request + 1);
240
259
  };
241
260
 
242
- const handleChannelChange = (nextChannel: ReleaseChannel) => {
261
+ const handleChannelChange = (nextChannel: DownloadReleaseChannel) => {
262
+ if (nextChannel === channel) return;
263
+
243
264
  setManifest(null);
244
265
  setManifestError(false);
245
266
  setChannel(nextChannel);
@@ -335,46 +356,48 @@ export default function DownloadPage() {
335
356
  <>{t("downloadRoute.loadingRelease")}</>
336
357
  )}
337
358
  </div>
338
- <div className="mt-2 flex items-center gap-2 text-xs text-muted-foreground">
339
- <span>{t("downloadRoute.stable")}</span>
340
- <button
341
- type="button"
342
- role="switch"
343
- aria-checked={channel === "nightly"}
344
- aria-label={t(
345
- channel === "nightly"
346
- ? "downloadRoute.switchToStable"
347
- : "downloadRoute.switchToNightly",
348
- )}
349
- onClick={() =>
350
- handleChannelChange(
351
- channel === "nightly" ? "production" : "nightly",
352
- )
353
- }
354
- className={`relative inline-flex h-5 w-9 shrink-0 items-center rounded-full border border-border transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500/50 ${
355
- channel === "nightly"
356
- ? "bg-foreground"
357
- : "bg-muted-foreground/20"
358
- }`}
359
- >
360
- <span
361
- aria-hidden="true"
362
- className={`block size-3.5 rounded-full bg-primary-foreground shadow-sm transition-transform ${
363
- channel === "nightly"
364
- ? "translate-x-[18px]"
365
- : "translate-x-[2px]"
366
- }`}
367
- />
368
- </button>
369
- <span
370
- className={
371
- channel === "nightly"
372
- ? "font-medium text-foreground"
373
- : "text-muted-foreground"
374
- }
375
- >
376
- {t("downloadRoute.nightly")}
377
- </span>
359
+ <div className="mt-2 flex items-center justify-center text-xs text-muted-foreground">
360
+ <DropdownMenu>
361
+ <DropdownMenuTrigger asChild>
362
+ <Button
363
+ type="button"
364
+ variant="ghost"
365
+ size="sm"
366
+ className="h-7 gap-1.5 px-2 font-normal text-muted-foreground hover:bg-muted/50 hover:text-foreground"
367
+ data-release-channel={channel}
368
+ >
369
+ <span
370
+ className={
371
+ channel === "nightly"
372
+ ? "font-medium text-foreground"
373
+ : undefined
374
+ }
375
+ >
376
+ {channel === "nightly"
377
+ ? t("downloadRoute.nightly")
378
+ : t("downloadRoute.stable")}
379
+ </span>
380
+ <IconChevronDown className="size-3.5" aria-hidden="true" />
381
+ </Button>
382
+ </DropdownMenuTrigger>
383
+ <DropdownMenuContent align="center" className="w-44">
384
+ <DropdownMenuRadioGroup
385
+ value={channel}
386
+ onValueChange={(value) => {
387
+ if (value === "production" || value === "nightly") {
388
+ handleChannelChange(value);
389
+ }
390
+ }}
391
+ >
392
+ <DropdownMenuRadioItem value="production">
393
+ {t("downloadRoute.stable")}
394
+ </DropdownMenuRadioItem>
395
+ <DropdownMenuRadioItem value="nightly">
396
+ {t("downloadRoute.nightly")}
397
+ </DropdownMenuRadioItem>
398
+ </DropdownMenuRadioGroup>
399
+ </DropdownMenuContent>
400
+ </DropdownMenu>
378
401
  </div>
379
402
  </div>
380
403
 
@@ -16,7 +16,7 @@ import {
16
16
  isHumanReadableDocumentTitle,
17
17
  normalizeDocumentTitle,
18
18
  } from "@agent-native/core/shared";
19
- import { ShareCopyRow, ShareTrigger } from "@agent-native/toolkit/sharing";
19
+ import { ShareCopyRow } from "@agent-native/toolkit/sharing";
20
20
  import {
21
21
  BUILDER_CREDITS_UPGRADE_URL,
22
22
  type BuilderCreditsStatus,
@@ -59,6 +59,7 @@ import { toast } from "sonner";
59
59
 
60
60
  import { EditableRecordingTitle } from "@/components/editable-recording-title";
61
61
  import { EditorLayout } from "@/components/editor/editor-layout";
62
+ import { ClipsShareTrigger } from "@/components/player/clips-share-trigger";
62
63
  import { CommentsPanel } from "@/components/player/comments-panel";
63
64
  import { RecordingOptionsMenu } from "@/components/player/delete-recording-menu";
64
65
  import { InsightsPanel } from "@/components/player/insights-panel";
@@ -1051,7 +1052,7 @@ export default function RecordingPage() {
1051
1052
  if (!showRecoveryState) {
1052
1053
  return (
1053
1054
  <div className="flex min-h-screen w-full flex-col bg-background">
1054
- <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">
1055
+ <header className="flex min-w-0 shrink-0 items-center gap-2 border-b border-border px-3 py-2 sm:px-4 sm:py-3">
1055
1056
  <BackToLibraryButton />
1056
1057
  <div className="min-w-0 flex-1">
1057
1058
  <p className="truncate text-sm font-medium">{visibleTitle}</p>
@@ -1070,10 +1071,7 @@ export default function RecordingPage() {
1070
1071
  hasPassword={Boolean(recording.hasPassword)}
1071
1072
  viewerReshareOnly={viewerReshareOnly}
1072
1073
  >
1073
- <ShareTrigger
1074
- label={t("recordingPage.share")}
1075
- className="shrink-0"
1076
- />
1074
+ <ClipsShareTrigger label={t("recordingPage.share")} />
1077
1075
  </ShareRecordingPopover>
1078
1076
  </header>
1079
1077
 
@@ -1370,7 +1368,7 @@ export default function RecordingPage() {
1370
1368
  <div className="flex min-h-screen w-full max-w-full flex-col overflow-x-hidden bg-background xl:h-screen xl:flex-row xl:overflow-hidden">
1371
1369
  {/* Main video column */}
1372
1370
  <div className="flex w-full min-w-0 flex-col xl:flex-1">
1373
- <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">
1371
+ <header className="flex min-w-0 shrink-0 items-center gap-2 border-b border-border px-3 py-2 sm:px-4 sm:py-3">
1374
1372
  <BackToLibraryButton />
1375
1373
  <div className="flex-1 min-w-0">
1376
1374
  <EditableRecordingTitle
@@ -1609,10 +1607,7 @@ export default function RecordingPage() {
1609
1607
  hasPassword={Boolean(recording.hasPassword)}
1610
1608
  viewerReshareOnly={viewerReshareOnly}
1611
1609
  >
1612
- <ShareTrigger
1613
- label={t("recordingPage.share")}
1614
- className="shrink-0"
1615
- />
1610
+ <ClipsShareTrigger label={t("recordingPage.share")} />
1616
1611
  </ShareRecordingPopover>
1617
1612
  ) : null}
1618
1613
 
@@ -3,7 +3,7 @@ import {
3
3
  agentNativePath,
4
4
  appBasePath,
5
5
  } from "@agent-native/core/client/api-path";
6
- import { callAction } from "@agent-native/core/client/hooks";
6
+ import { callAction, getBrowserTabId } from "@agent-native/core/client/hooks";
7
7
  import { useT } from "@agent-native/core/client/i18n";
8
8
  import { useLiveTranscription } from "@agent-native/core/client/transcription/use-live-transcription";
9
9
  import type { BrowserDiagnosticsData } from "@shared/browser-diagnostics";
@@ -1783,7 +1783,7 @@ export default function RecordRoute() {
1783
1783
  }
1784
1784
  if (reportContext && createdId) {
1785
1785
  const path = bugReportDonePath(createdId, reportContext);
1786
- await writeAppState("navigate", {
1786
+ await writeAppState(`navigate:${getBrowserTabId()}`, {
1787
1787
  view: "bug-report-done",
1788
1788
  recordingId: createdId,
1789
1789
  path,
@@ -1792,7 +1792,7 @@ export default function RecordRoute() {
1792
1792
  navigate(path);
1793
1793
  }, 50);
1794
1794
  } else {
1795
- await writeAppState("navigate", {
1795
+ await writeAppState(`navigate:${getBrowserTabId()}`, {
1796
1796
  view: "recording",
1797
1797
  recordingId: createdId,
1798
1798
  });
@@ -1980,7 +1980,7 @@ export default function RecordRoute() {
1980
1980
 
1981
1981
  if (reportContext) {
1982
1982
  const path = bugReportDonePath(recordingId, reportContext);
1983
- await writeAppState("navigate", {
1983
+ await writeAppState(`navigate:${getBrowserTabId()}`, {
1984
1984
  view: "bug-report-done",
1985
1985
  recordingId,
1986
1986
  path,
@@ -1991,7 +1991,7 @@ export default function RecordRoute() {
1991
1991
  return;
1992
1992
  }
1993
1993
 
1994
- await writeAppState("navigate", {
1994
+ await writeAppState(`navigate:${getBrowserTabId()}`, {
1995
1995
  view: "recording",
1996
1996
  recordingId,
1997
1997
  }).catch(() => {});
@@ -13,13 +13,12 @@ import {
13
13
  import { useT } from "@agent-native/core/client/i18n";
14
14
  import { buildSignInReturnHref } from "@agent-native/core/client/ui";
15
15
  import { docsUrl } from "@agent-native/core/shared";
16
- import { ShareTrigger } from "@agent-native/toolkit/sharing";
17
16
  import {
18
17
  IconAlertTriangle,
19
18
  IconArrowLeft,
20
19
  IconDeviceDesktop,
21
20
  IconDownload,
22
- IconDots,
21
+ IconDotsVertical,
23
22
  IconLock,
24
23
  IconLogin2,
25
24
  } from "@tabler/icons-react";
@@ -51,6 +50,7 @@ import { toast } from "sonner";
51
50
 
52
51
  import { CaptureInstallButton } from "@/components/capture-install-options";
53
52
  import { AccessPasswordPrompt } from "@/components/player/access-password-prompt";
53
+ import { ClipsShareTrigger } from "@/components/player/clips-share-trigger";
54
54
  import { CommentsPanel } from "@/components/player/comments-panel";
55
55
  import { RecordingOptionsMenu } from "@/components/player/delete-recording-menu";
56
56
  import { InsightsPanel } from "@/components/player/insights-panel";
@@ -1111,7 +1111,7 @@ export default function ShareRoute() {
1111
1111
  <div className="flex min-h-screen max-w-full flex-col overflow-x-hidden bg-background text-foreground lg:h-screen lg:flex-row lg:overflow-hidden">
1112
1112
  {agentDiscovery}
1113
1113
  <div className="flex w-full min-w-0 flex-col lg:flex-1">
1114
- <header className="flex min-w-0 shrink-0 flex-wrap items-center gap-2 border-b border-border px-3 py-2 sm:gap-3 sm:px-4 sm:py-3 lg:flex-nowrap">
1114
+ <header className="flex min-w-0 shrink-0 flex-wrap items-center gap-2 border-b border-border px-3 py-2 sm:px-4 sm:py-3 lg:flex-nowrap">
1115
1115
  {session ? (
1116
1116
  <Button
1117
1117
  asChild
@@ -1147,7 +1147,7 @@ export default function ShareRoute() {
1147
1147
  )}
1148
1148
  </div>
1149
1149
 
1150
- <div className="flex w-full min-w-0 flex-wrap items-center justify-between gap-2 sm:w-auto sm:justify-end">
1150
+ <div className="flex w-full min-w-0 flex-wrap items-center justify-between gap-1 sm:w-auto sm:justify-end">
1151
1151
  <RecordingViewsBadge
1152
1152
  recordingId={recording.id}
1153
1153
  viewCount={viewCount}
@@ -1161,6 +1161,22 @@ export default function ShareRoute() {
1161
1161
  onCtaClick={fireShareCtaClick}
1162
1162
  />
1163
1163
  )}
1164
+ {viewerCanEdit || canReshareLink ? (
1165
+ <ShareRecordingPopover
1166
+ recordingId={recording.id}
1167
+ recordingTitle={recording.title}
1168
+ initialVisibility={recording.visibility}
1169
+ initialRole={viewerIsOwner ? "owner" : undefined}
1170
+ videoUrl={shareVideoUrl}
1171
+ thumbnailUrl={recording.thumbnailUrl}
1172
+ animatedThumbnailUrl={recording.animatedThumbnailUrl}
1173
+ isLoomRecording={isLoomEmbedBacked}
1174
+ hasPassword={Boolean(recording.hasPassword)}
1175
+ viewerReshareOnly={viewerReshareOnly}
1176
+ >
1177
+ <ClipsShareTrigger label={t("sharePage.share")} />
1178
+ </ShareRecordingPopover>
1179
+ ) : null}
1164
1180
  {!viewerCanEdit && canDownloadRecording ? (
1165
1181
  <DropdownMenu
1166
1182
  open={downloadMenuOpen}
@@ -1170,10 +1186,10 @@ export default function ShareRoute() {
1170
1186
  <Button
1171
1187
  variant="ghost"
1172
1188
  size="sm"
1173
- className="h-9 w-9 shrink-0 px-0"
1189
+ className="-mx-1.5 h-auto w-auto shrink-0 px-0.5 py-1.5"
1174
1190
  aria-label={t("sharePage.clipOptions")}
1175
1191
  >
1176
- <IconDots className="h-4 w-4" />
1192
+ <IconDotsVertical className="h-4 w-4" />
1177
1193
  </Button>
1178
1194
  </DropdownMenuTrigger>
1179
1195
  <DropdownMenuContent align="end" className="w-44">
@@ -1206,25 +1222,6 @@ export default function ShareRoute() {
1206
1222
  onDeleted={() => navigate("/library", { replace: true })}
1207
1223
  />
1208
1224
  ) : null}
1209
- {viewerCanEdit || canReshareLink ? (
1210
- <ShareRecordingPopover
1211
- recordingId={recording.id}
1212
- recordingTitle={recording.title}
1213
- initialVisibility={recording.visibility}
1214
- initialRole={viewerIsOwner ? "owner" : undefined}
1215
- videoUrl={shareVideoUrl}
1216
- thumbnailUrl={recording.thumbnailUrl}
1217
- animatedThumbnailUrl={recording.animatedThumbnailUrl}
1218
- isLoomRecording={isLoomEmbedBacked}
1219
- hasPassword={Boolean(recording.hasPassword)}
1220
- viewerReshareOnly={viewerReshareOnly}
1221
- >
1222
- <ShareTrigger
1223
- label={t("sharePage.share")}
1224
- className="shrink-0"
1225
- />
1226
- </ShareRecordingPopover>
1227
- ) : null}
1228
1225
  </div>
1229
1226
  </header>
1230
1227
 
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-08-20
4
+ ---
5
+
6
+ Clip viewers now make Share the primary action and keep the overflow control compact and vertical.
@@ -2,4 +2,5 @@
2
2
  type: improved
3
3
  date: 2026-08-20
4
4
  ---
5
+
5
6
  Recording preparation now uses a centered full-screen overlay that transitions smoothly into the 3-2-1 countdown.
@@ -1,5 +1,5 @@
1
1
  import { defineAction } from "@agent-native/core";
2
- import { writeAppState } from "@agent-native/core/application-state";
2
+ import { writeAppStateForCurrentTab } from "@agent-native/core/application-state";
3
3
  import { eq } from "drizzle-orm";
4
4
  import { z } from "zod";
5
5
 
@@ -57,7 +57,7 @@ export default defineAction({
57
57
  run: async (args) => {
58
58
  const path = await resolveNavigatePath(args);
59
59
 
60
- await writeAppState("navigate", { path, ts: Date.now() });
60
+ await writeAppStateForCurrentTab("navigate", { path, ts: Date.now() });
61
61
  return `Navigating to ${path}`;
62
62
  },
63
63
  });
@@ -85,7 +85,8 @@ Read the relevant skill before deeper work in that area.
85
85
  ## Application State
86
86
 
87
87
  - `navigation` — current view, design id, file id, and related UI state.
88
- - `navigate` — moves the UI; auto-deleted after the client consumes it.
88
+ - `navigate` — moves the UI in the tab that asked; auto-deleted after the
89
+ client consumes it.
89
90
  - `design-selection` — active screen, selected element, overview mode,
90
91
  inspector tab, zoom, and screen list for the current tab.
91
92
  - `design-generation-session:<designId>`, `show-questions`, `guided-questions` —
@@ -1,5 +1,8 @@
1
1
  import { defineAction, embedApp } from "@agent-native/core";
2
- import { writeAppState } from "@agent-native/core/application-state";
2
+ import {
3
+ writeAppState,
4
+ writeAppStateForCurrentTab,
5
+ } from "@agent-native/core/application-state";
3
6
  import { buildDeepLink } from "@agent-native/core/server";
4
7
  import { assertAccess } from "@agent-native/core/sharing";
5
8
  import {
@@ -430,7 +433,7 @@ export default defineAction({
430
433
  designGenerationSessionKey(designId),
431
434
  session as unknown as Record<string, unknown>,
432
435
  );
433
- await writeAppState("navigate", {
436
+ await writeAppStateForCurrentTab("navigate", {
434
437
  view: "editor",
435
438
  designId,
436
439
  editorView: "overview",
@@ -28,7 +28,7 @@
28
28
  */
29
29
 
30
30
  import { defineAction } from "@agent-native/core/action";
31
- import { writeAppState } from "@agent-native/core/application-state";
31
+ import { writeAppStateForCurrentTab } from "@agent-native/core/application-state";
32
32
  import { getText, hasCollabState } from "@agent-native/core/collab";
33
33
  import { accessFilter, resolveAccess } from "@agent-native/core/sharing";
34
34
  import { and, eq } from "drizzle-orm";
@@ -223,7 +223,7 @@ export default defineAction({
223
223
  };
224
224
  }
225
225
 
226
- await writeAppState("navigate", {
226
+ await writeAppStateForCurrentTab("navigate", {
227
227
  view: "editor",
228
228
  designId,
229
229
  editorView: "single",