@agent-native/core 0.157.0 → 0.157.2

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 (114) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/server/jobs/analytics-bigquery-backfill.ts +19 -4
  3. package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +7 -2
  4. package/corpus/templates/assets/app/global.css +4 -0
  5. package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +1 -2
  6. package/corpus/templates/brain/app/routes/sources.tsx +1 -2
  7. package/corpus/templates/calendar/app/hooks/use-bookings.ts +12 -2
  8. package/corpus/templates/calendar/app/pages/BookingLinksPage.tsx +64 -2
  9. package/corpus/templates/calendar/server/handlers/bookings.ts +224 -93
  10. package/corpus/templates/calendar/server/plugins/auth.ts +0 -2
  11. package/corpus/templates/clips/actions/create-folder.ts +7 -1
  12. package/corpus/templates/clips/actions/create-meeting.ts +6 -2
  13. package/corpus/templates/clips/actions/create-recording.ts +5 -3
  14. package/corpus/templates/clips/actions/finalize-recording.ts +97 -65
  15. package/corpus/templates/clips/actions/import-loom-recording.ts +5 -3
  16. package/corpus/templates/clips/actions/request-recording-access.ts +305 -0
  17. package/corpus/templates/clips/actions/stitch-recordings.ts +5 -3
  18. package/corpus/templates/clips/app/components/meetings/share-meeting-dialog.tsx +38 -63
  19. package/corpus/templates/clips/app/components/player/share-dialog.tsx +75 -75
  20. package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +3 -21
  21. package/corpus/templates/clips/app/components/sharing/share-ui.tsx +13 -30
  22. package/corpus/templates/clips/app/i18n/en-US.ts +10 -0
  23. package/corpus/templates/clips/app/routes/_app.meetings.$meetingId.tsx +5 -5
  24. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +9 -16
  25. package/corpus/templates/clips/app/routes/share.$shareId.tsx +135 -18
  26. package/corpus/templates/clips/changelog/2026-08-14-long-resumable-uploads-now-abort-cleanly-and-show-errors.md +6 -0
  27. package/corpus/templates/clips/changelog/2026-08-14-standardized-share-popovers.md +6 -0
  28. package/corpus/templates/clips/chrome-extension/public/manifest.json +11 -3
  29. package/corpus/templates/clips/chrome-extension/src/github-preview-content.ts +37 -8
  30. package/corpus/templates/clips/chrome-extension/src/github-preview.ts +3 -3
  31. package/corpus/templates/clips/chrome-extension/src/offscreen.ts +8 -25
  32. package/corpus/templates/clips/desktop/src/app.tsx +24 -0
  33. package/corpus/templates/clips/desktop/src/lib/media-capture-constraints.ts +42 -13
  34. package/corpus/templates/clips/desktop/src/lib/recorder.ts +11 -18
  35. package/corpus/templates/clips/desktop/src/overlays/onboarding.tsx +1 -0
  36. package/corpus/templates/clips/desktop/src/shared/config.ts +1 -0
  37. package/corpus/templates/clips/desktop/src/styles.css +24 -9
  38. package/corpus/templates/clips/desktop/src-tauri/Cargo.toml +3 -0
  39. package/corpus/templates/clips/desktop/src-tauri/src/config.rs +8 -0
  40. package/corpus/templates/clips/desktop/src-tauri/src/native_screen/custom_capture.rs +154 -176
  41. package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +115 -31
  42. package/corpus/templates/clips/desktop/src-tauri/src/rewind_clip.rs +7 -0
  43. package/corpus/templates/clips/server/db/index.ts +1 -0
  44. package/corpus/templates/clips/server/db/schema.ts +1 -0
  45. package/corpus/templates/clips/server/lib/calendar-event-meetings.ts +6 -1
  46. package/corpus/templates/clips/server/lib/emails.ts +23 -0
  47. package/corpus/templates/clips/server/lib/public-agent-context.ts +55 -0
  48. package/corpus/templates/clips/server/lib/recordings.ts +2 -1
  49. package/corpus/templates/clips/server/lib/resumable-upload-cleanup.ts +49 -0
  50. package/corpus/templates/clips/server/lib/s3-upload-provider.ts +34 -7
  51. package/corpus/templates/clips/server/lib/upload-interruption.ts +15 -1
  52. package/corpus/templates/clips/server/lib/upload-lease.ts +94 -9
  53. package/corpus/templates/clips/server/routes/api/agent-context.json.get.ts +82 -7
  54. package/corpus/templates/clips/server/routes/api/public-recording.get.ts +1 -0
  55. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +71 -12
  56. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/interrupt.post.ts +4 -3
  57. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/reset-chunks.post.ts +133 -4
  58. package/corpus/templates/clips/server/routes/api/uploads/reap.post.ts +8 -1
  59. package/corpus/templates/clips/shared/recording-link.ts +4 -0
  60. package/corpus/templates/clips/shared/share-loader-response.ts +2 -1
  61. package/corpus/templates/content/app/components/editor/DocumentToolbar.tsx +7 -16
  62. package/corpus/templates/factory/AGENTS.md +5 -1
  63. package/corpus/templates/factory/README.md +6 -2
  64. package/corpus/templates/factory/actions/view-screen.ts +15 -0
  65. package/corpus/templates/factory/app/components/factory/FactoryAgentsView.tsx +58 -189
  66. package/corpus/templates/factory/app/components/layout/Header.tsx +1 -0
  67. package/corpus/templates/factory/app/components/layout/Layout.tsx +1 -1
  68. package/corpus/templates/factory/app/components/layout/Sidebar.tsx +7 -0
  69. package/corpus/templates/factory/app/hooks/use-navigation-state.ts +3 -0
  70. package/corpus/templates/factory/app/i18n/en-US.ts +3 -1
  71. package/corpus/templates/factory/app/routes/agents.tsx +9 -0
  72. package/corpus/templates/mail/server/plugins/auth.ts +0 -2
  73. package/corpus/templates/plan/app/components/plan/LocalCodebasePicker.tsx +50 -14
  74. package/corpus/templates/plan/app/pages/PlansPage.tsx +9 -19
  75. package/corpus/templates/slides/app/components/editor/HistoryPanel.tsx +7 -1
  76. package/corpus/templates/slides/app/context/DeckContext.tsx +71 -22
  77. package/corpus/templates/slides/app/lib/editor-shortcuts.ts +46 -0
  78. package/corpus/templates/slides/app/pages/DeckEditor.tsx +13 -0
  79. package/dist/client/AgentPanel.d.ts +13 -1
  80. package/dist/client/AgentPanel.js +12 -10
  81. package/dist/client/AssistantChat.js +25 -5
  82. package/dist/client/HighlightedCodeBlock.js +12 -2
  83. package/dist/client/agent-chat/index.d.ts +1 -0
  84. package/dist/client/agent-chat/index.js +1 -0
  85. package/dist/client/chat/markdown-renderer.js +16 -3
  86. package/dist/client/chat-first/apps-rail.js +1 -1
  87. package/dist/client/components/ui/message-scroller.js +1 -1
  88. package/dist/client/settings/AccountSettingsCard.js +48 -19
  89. package/dist/client/sharing/ShareButton.d.ts +1 -2
  90. package/dist/client/sharing/ShareButton.js +5 -41
  91. package/dist/client/sharing/ShareDialog.js +5 -4
  92. package/dist/client/use-chat-threads.js +10 -14
  93. package/dist/collab/awareness.d.ts +2 -2
  94. package/dist/collab/routes.d.ts +1 -1
  95. package/dist/collab/struct-routes.d.ts +1 -1
  96. package/dist/localization/default-messages.d.ts +1 -0
  97. package/dist/localization/default-messages.js +1 -0
  98. package/dist/notifications/routes.d.ts +3 -3
  99. package/dist/provider-api/actions/custom-provider-registration.d.ts +11 -11
  100. package/dist/provider-api/actions/provider-api.d.ts +15 -15
  101. package/dist/server/auth-marketing.d.ts +1 -0
  102. package/dist/server/auth-marketing.js +0 -2
  103. package/dist/server/auth.d.ts +1 -0
  104. package/dist/server/auth.js +18 -0
  105. package/dist/server/onboarding-html.d.ts +1 -0
  106. package/dist/server/onboarding-html.js +2 -88
  107. package/dist/server/realtime-token.d.ts +1 -1
  108. package/dist/shared/markdown-block-split.d.ts +16 -0
  109. package/dist/shared/markdown-block-split.js +81 -5
  110. package/dist/styles/agent-native.css +13 -0
  111. package/dist/voice/voice-cleanup-prompt.js +1 -0
  112. package/docs/content/cloneable-saas.mdx +24 -26
  113. package/package.json +3 -3
  114. package/corpus/templates/clips/shared/mic-audio-cleanup.ts +0 -350
@@ -6,8 +6,8 @@ import {
6
6
  useSession,
7
7
  } from "@agent-native/core/client/hooks";
8
8
  import { useT } from "@agent-native/core/client/i18n";
9
+ import { ShareAgentsSection } from "@agent-native/toolkit/sharing";
9
10
  import {
10
- IconChevronDown,
11
11
  IconCode,
12
12
  IconExternalLink,
13
13
  IconLink,
@@ -35,11 +35,6 @@ import {
35
35
  type Visibility,
36
36
  } from "@/components/sharing/share-ui";
37
37
  import { Button } from "@/components/ui/button";
38
- import {
39
- Collapsible,
40
- CollapsibleContent,
41
- CollapsibleTrigger,
42
- } from "@/components/ui/collapsible";
43
38
  import { Dialog, DialogContent, DialogTitle } from "@/components/ui/dialog";
44
39
  import { Input } from "@/components/ui/input";
45
40
  import { Label } from "@/components/ui/label";
@@ -52,6 +47,7 @@ import { Switch } from "@/components/ui/switch";
52
47
  import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs";
53
48
  import { cn } from "@/lib/utils";
54
49
 
50
+ import { buildAgentApiUrls } from "../../../shared/agent-context";
55
51
  import { buildEmailPreviewMarkup } from "../../../shared/email-preview";
56
52
  import { isLoomEmbedUrl } from "../../../shared/loom";
57
53
  import { withShareAttribution } from "../../../shared/share-attribution";
@@ -62,6 +58,14 @@ function absoluteAppUrl(path: string): string {
62
58
  return new URL(appPath(path), window.location.origin).toString();
63
59
  }
64
60
 
61
+ function absolutePublicAgentContextUrl(recordingId: string): string {
62
+ if (typeof window === "undefined") return "";
63
+ return buildAgentApiUrls(recordingId, {
64
+ origin: window.location.origin,
65
+ basePath: appPath("/").replace(/\/$/, ""),
66
+ }).contextUrl;
67
+ }
68
+
65
69
  export interface ShareRecordingPopoverProps {
66
70
  recordingId: string;
67
71
  recordingTitle?: string;
@@ -375,6 +379,14 @@ function LinkTab({
375
379
  const sharesLoaded = visibility !== null;
376
380
  const visibilityPending = isPending || sharesQuery.isLoading;
377
381
  const isLoomRecording = isLoomRecordingProp || isLoomEmbedUrl(videoUrl);
382
+ const needsScopedAgentContext = !isPublic || hasPassword !== false;
383
+ const publicAgentContextUrl = useMemo(
384
+ () =>
385
+ isPublic && hasPassword === false
386
+ ? absolutePublicAgentContextUrl(recordingId)
387
+ : "",
388
+ [hasPassword, isPublic, recordingId],
389
+ );
378
390
  const emailPreviewThumbnailUrl = useMemo(() => {
379
391
  if (!isPublic || hasPassword !== false || !sharesLoaded) return null;
380
392
  const variant = preferredThumbnailVariant({
@@ -438,10 +450,10 @@ function LinkTab({
438
450
  try {
439
451
  const result = (await createAgentLinkAsyncRef.current({
440
452
  recordingId,
441
- })) as { url?: string };
453
+ })) as { contextUrl?: string };
442
454
  if (agentLinkRequestIdRef.current !== requestId) return;
443
- if (result?.url) {
444
- setAgentContextUrl(result.url);
455
+ if (result?.contextUrl) {
456
+ setAgentContextUrl(result.contextUrl);
445
457
  } else {
446
458
  setAgentLinkError(true);
447
459
  }
@@ -457,20 +469,30 @@ function LinkTab({
457
469
  setAgentLinkError(false);
458
470
  if (!sharesLoaded) return;
459
471
 
460
- if (!isPublic) {
472
+ if (needsScopedAgentContext) {
461
473
  void loadAgentContextUrl();
462
474
  }
463
475
 
464
476
  return () => {
465
477
  agentLinkRequestIdRef.current += 1;
466
478
  };
467
- }, [isPublic, loadAgentContextUrl, recordingId, sharesLoaded, visibility]);
479
+ }, [
480
+ loadAgentContextUrl,
481
+ needsScopedAgentContext,
482
+ recordingId,
483
+ sharesLoaded,
484
+ visibility,
485
+ ]);
468
486
 
469
- const agentLink = isPublic ? shareUrl : agentContextUrl;
487
+ const agentLink = isPublic
488
+ ? publicAgentContextUrl || agentContextUrl
489
+ : agentContextUrl;
470
490
  const agentShareDisabled =
471
491
  visibilityPending ||
472
492
  !sharesLoaded ||
473
- (!isPublic && (isPending || createAgentLink.isPending || !agentContextUrl));
493
+ !agentLink ||
494
+ (needsScopedAgentContext &&
495
+ (isPending || createAgentLink.isPending || !agentContextUrl));
474
496
  const agentPrompt = agentLink
475
497
  ? t("shareDialog.agentPrompt", { agentContextUrl: agentLink })
476
498
  : "";
@@ -529,71 +551,49 @@ function LinkTab({
529
551
  disabled={visibilityPending || !sharesLoaded}
530
552
  />
531
553
 
532
- {!isPublic ? (
533
- <Collapsible
534
- open={agentDetailsOpen}
535
- onOpenChange={setAgentDetailsOpen}
536
- className="overflow-hidden rounded-md border border-border"
537
- >
538
- <CollapsibleTrigger asChild>
539
- <button
540
- type="button"
541
- className="flex min-h-10 w-full items-center justify-between gap-3 px-3 py-2 text-left text-sm font-medium transition-colors hover:bg-muted/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset"
542
- >
543
- <span className="flex min-w-0 items-center gap-2 truncate">
544
- <IconLink
545
- aria-hidden="true"
546
- className="size-3.5 shrink-0 text-muted-foreground"
547
- strokeWidth={1.8}
548
- />
549
- {t("shareDialog.shareWithAgents")}
550
- </span>
551
- <IconChevronDown
552
- aria-hidden="true"
553
- className={`size-4 shrink-0 text-muted-foreground transition-transform ${agentDetailsOpen ? "rotate-180" : ""}`}
554
- />
555
- </button>
556
- </CollapsibleTrigger>
557
- <CollapsibleContent className="clips-collapsible-content border-t border-border px-3 py-3">
558
- <div className="space-y-2">
559
- <CopyField
560
- label={t("shareDialog.shareWithAgents")}
561
- value={agentLink}
562
- disabled={agentShareDisabled}
563
- />
564
- {sharesLoaded ? (
565
- <>
554
+ <ShareAgentsSection
555
+ label={t("shareDialog.shareWithAgents")}
556
+ open={agentDetailsOpen}
557
+ onOpenChange={setAgentDetailsOpen}
558
+ contentClassName="clips-collapsible-content"
559
+ >
560
+ <div className="space-y-2">
561
+ <CopyField
562
+ label={t("shareDialog.shareLink")}
563
+ value={agentLink}
564
+ disabled={agentShareDisabled}
565
+ />
566
+ {sharesLoaded ? (
567
+ <>
568
+ <p className="text-xs text-muted-foreground">
569
+ {t("shareDialog.agentTokenDescription")}
570
+ </p>
571
+ {agentLinkError ? (
572
+ <div className="flex items-center justify-between gap-2 rounded-md border border-border bg-muted/30 px-3 py-2">
566
573
  <p className="text-xs text-muted-foreground">
567
- {t("shareDialog.agentTokenDescription")}
574
+ {t("shareDialog.agentLinkUnavailable")}
568
575
  </p>
569
- {agentLinkError ? (
570
- <div className="flex items-center justify-between gap-2 rounded-md border border-border bg-muted/30 px-3 py-2">
571
- <p className="text-xs text-muted-foreground">
572
- {t("shareDialog.agentLinkUnavailable")}
573
- </p>
574
- <Button
575
- type="button"
576
- variant="ghost"
577
- size="sm"
578
- className="h-7"
579
- onClick={() => void loadAgentContextUrl()}
580
- disabled={createAgentLink.isPending}
581
- >
582
- {t("shareDialog.retryAgentLink")}
583
- </Button>
584
- </div>
585
- ) : null}
586
- <CopyField
587
- label={t("shareDialog.copyAgentPrompt")}
588
- value={agentPrompt}
589
- disabled={agentShareDisabled}
590
- />
591
- </>
576
+ <Button
577
+ type="button"
578
+ variant="ghost"
579
+ size="sm"
580
+ className="h-7"
581
+ onClick={() => void loadAgentContextUrl()}
582
+ disabled={createAgentLink.isPending}
583
+ >
584
+ {t("shareDialog.retryAgentLink")}
585
+ </Button>
586
+ </div>
592
587
  ) : null}
593
- </div>
594
- </CollapsibleContent>
595
- </Collapsible>
596
- ) : null}
588
+ <CopyField
589
+ label={t("shareDialog.copyAgentPrompt")}
590
+ value={agentPrompt}
591
+ disabled={agentShareDisabled}
592
+ />
593
+ </>
594
+ ) : null}
595
+ </div>
596
+ </ShareAgentsSection>
597
597
 
598
598
  {showMakePublic ? (
599
599
  <MakePublicCard
@@ -8,10 +8,6 @@ import {
8
8
  isLikelyPhoneMicLabel,
9
9
  type AudioInputFallback,
10
10
  } from "@shared/media-device-selection";
11
- import {
12
- createMicAudioCleanup,
13
- type MicAudioCleanupHandle,
14
- } from "@shared/mic-audio-cleanup";
15
11
  import {
16
12
  SCREEN_CAPTURE_FRAME_RATE,
17
13
  screenCaptureDisplayOptions,
@@ -509,7 +505,6 @@ export class RecorderEngine {
509
505
  private cameraComposite: CameraCompositeHandle | null = null;
510
506
  private audioMixCtx: AudioContext | null = null;
511
507
  private audioMixSources: MediaStreamAudioSourceNode[] = [];
512
- private micAudioCleanup: MicAudioCleanupHandle[] = [];
513
508
  private recorder: MediaRecorder | null = null;
514
509
  private mimeType: string = "video/webm";
515
510
 
@@ -1360,7 +1355,6 @@ export class RecorderEngine {
1360
1355
  // Full stream teardown happens in the finally block below.
1361
1356
  this.cameraLive = false;
1362
1357
  this.audioMixSources = [];
1363
- this.stopMicAudioCleanup();
1364
1358
  this.audioMixCtx?.close().catch(() => {});
1365
1359
  this.audioMixCtx = null;
1366
1360
  for (const s of [this.cameraStream, this.rawCameraStream, this.micStream]) {
@@ -1828,26 +1822,15 @@ export class RecorderEngine {
1828
1822
  this.audioMixSources = [];
1829
1823
  const dest = ctx.createMediaStreamDestination();
1830
1824
  for (const input of audioInputs) {
1831
- let track = input.track;
1832
- if (input.isMicrophone) {
1833
- const cleanup = createMicAudioCleanup(new MediaStream([track]), {
1834
- audioContext: ctx,
1835
- });
1836
- this.micAudioCleanup.push(cleanup);
1837
- track = cleanup.stream.getAudioTracks()[0] ?? track;
1838
- }
1839
- const source = ctx.createMediaStreamSource(new MediaStream([track]));
1825
+ const source = ctx.createMediaStreamSource(
1826
+ new MediaStream([input.track]),
1827
+ );
1840
1828
  source.connect(dest);
1841
1829
  this.audioMixSources.push(source);
1842
1830
  }
1843
1831
  return dest.stream.getAudioTracks()[0];
1844
1832
  }
1845
1833
 
1846
- private stopMicAudioCleanup(): void {
1847
- for (const cleanup of this.micAudioCleanup) cleanup.stop();
1848
- this.micAudioCleanup = [];
1849
- }
1850
-
1851
1834
  private buildCombinedStream(): MediaStream {
1852
1835
  if (this.opts.mode === "screen") {
1853
1836
  return this.buildDisplayRecordingStream();
@@ -2382,7 +2365,6 @@ export class RecorderEngine {
2382
2365
  // aren't mistaken for a disconnect.
2383
2366
  this.cameraLive = false;
2384
2367
  this.audioMixSources = [];
2385
- this.stopMicAudioCleanup();
2386
2368
  this.audioMixCtx?.close().catch(() => {});
2387
2369
  this.audioMixCtx = null;
2388
2370
  this.cameraComposite?.cleanup();
@@ -4,9 +4,8 @@ import {
4
4
  useActionQuery,
5
5
  } from "@agent-native/core/client/hooks";
6
6
  import { useT } from "@agent-native/core/client/i18n";
7
+ import { ShareCopyRow } from "@agent-native/toolkit/sharing";
7
8
  import {
8
- IconCheck,
9
- IconLink,
10
9
  IconLock,
11
10
  IconSend2,
12
11
  IconTrash,
@@ -281,35 +280,19 @@ export function CopyField({
281
280
  description?: string;
282
281
  }) {
283
282
  const t = useT();
284
- const [copied, setCopied] = useState(false);
285
- const copy = () => {
286
- if (disabled) return;
287
- copyToClipboard(value);
288
- setCopied(true);
289
- setTimeout(() => setCopied(false), 1400);
290
- };
291
283
  return (
292
- <div className="flex items-center justify-between gap-3">
293
- <div className="min-w-0">
294
- {label ? <div className="text-sm font-medium">{label}</div> : null}
295
- {description ? (
296
- <div className="text-xs text-muted-foreground">{description}</div>
297
- ) : null}
298
- </div>
299
- <div aria-live="polite" className="shrink-0">
300
- <Button
301
- type="button"
302
- variant="outline"
303
- size="sm"
304
- onClick={copy}
305
- disabled={disabled}
306
- className="h-9"
307
- >
308
- {copied ? <IconCheck size={14} /> : <IconLink size={14} />}
309
- {copied ? t("recordRoute.linkCopied") : t("shareUi.copy")}
310
- </Button>
311
- </div>
312
- </div>
284
+ <ShareCopyRow
285
+ label={label}
286
+ value={value}
287
+ description={description}
288
+ disabled={disabled}
289
+ copyLabel={t("shareUi.copy")}
290
+ copiedLabel={t("recordRoute.linkCopied")}
291
+ onCopy={(nextValue) => {
292
+ copyToClipboard(nextValue);
293
+ return true;
294
+ }}
295
+ />
313
296
  );
314
297
  }
315
298
 
@@ -256,6 +256,16 @@ const messages = {
256
256
  passwordProtected: "This clip is password-protected",
257
257
  linkExpired: "Link expired",
258
258
  linkExpiredMessage: "The creator set an expiry on this share link.",
259
+ privateClip: "Private clip",
260
+ privateClipMessage:
261
+ "This clip is private. Request access and the owner will be notified.",
262
+ privateClipSignedOutMessage:
263
+ "This clip is private. Sign in to request access from the owner.",
264
+ requestAccess: "Request access",
265
+ requestingAccess: "Requesting access...",
266
+ accessRequested: "Access requested",
267
+ accessRequestSent: "The clip owner was notified.",
268
+ accessRequestFailed: "Couldn't request access. Try again.",
259
269
  clipUnavailable: "Clip unavailable",
260
270
  clipUnavailableMessage:
261
271
  "This recording isn't public, or the link is invalid. If it's your clip, sign in to check access.",
@@ -5,6 +5,7 @@ import {
5
5
  useActionQuery,
6
6
  } from "@agent-native/core/client/hooks";
7
7
  import { useT } from "@agent-native/core/client/i18n";
8
+ import { ShareTrigger } from "@agent-native/toolkit/sharing";
8
9
  import {
9
10
  IconArrowLeft,
10
11
  IconCheck,
@@ -18,7 +19,6 @@ import {
18
19
  IconNotes,
19
20
  IconPlayerStop,
20
21
  IconRefresh,
21
- IconShare3,
22
22
  IconTrash,
23
23
  IconUsers,
24
24
  } from "@tabler/icons-react";
@@ -711,10 +711,10 @@ export default function MeetingDetailRoute() {
711
711
  Boolean(data?.transcript?.fullText?.trim()))
712
712
  }
713
713
  >
714
- <Button size="sm" className="shrink-0 gap-1.5">
715
- <IconShare3 className="h-4 w-4" />
716
- {t("meetingDetail.share")}
717
- </Button>
714
+ <ShareTrigger
715
+ label={t("meetingDetail.share")}
716
+ className="shrink-0"
717
+ />
718
718
  </ShareMeetingPopover>
719
719
  {canEdit && (
720
720
  <AlertDialog open={deleteOpen} onOpenChange={setDeleteOpen}>
@@ -16,6 +16,7 @@ import {
16
16
  isHumanReadableDocumentTitle,
17
17
  normalizeDocumentTitle,
18
18
  } from "@agent-native/core/shared";
19
+ import { ShareTrigger } from "@agent-native/toolkit/sharing";
19
20
  import {
20
21
  BUILDER_CREDITS_UPGRADE_URL,
21
22
  type BuilderCreditsStatus,
@@ -33,7 +34,6 @@ import {
33
34
  } from "@shared/share-attribution";
34
35
  import type { WorkflowKind } from "@shared/workflow";
35
36
  import {
36
- IconShare3,
37
37
  IconArrowLeft,
38
38
  IconChevronDown,
39
39
  IconCalendar,
@@ -1002,12 +1002,10 @@ export default function RecordingPage() {
1002
1002
  hasPassword={Boolean(recording.hasPassword)}
1003
1003
  viewerReshareOnly={viewerReshareOnly}
1004
1004
  >
1005
- <Button className="shrink-0 gap-1.5" size="sm">
1006
- {recording.visibility !== "public" ? (
1007
- <IconShare3 className="h-4 w-4" />
1008
- ) : null}
1009
- {t("recordingPage.share")}
1010
- </Button>
1005
+ <ShareTrigger
1006
+ label={t("recordingPage.share")}
1007
+ className="shrink-0"
1008
+ />
1011
1009
  </ShareRecordingPopover>
1012
1010
  </header>
1013
1011
 
@@ -1570,15 +1568,10 @@ export default function RecordingPage() {
1570
1568
  hasPassword={Boolean(recording.hasPassword)}
1571
1569
  viewerReshareOnly={viewerReshareOnly}
1572
1570
  >
1573
- <Button
1574
- className="shrink-0 gap-1.5 bg-primary text-primary-foreground hover:bg-primary/90"
1575
- size="sm"
1576
- >
1577
- {recording.visibility !== "public" ? (
1578
- <IconShare3 className="h-4 w-4" />
1579
- ) : null}
1580
- {t("recordingPage.share")}
1581
- </Button>
1571
+ <ShareTrigger
1572
+ label={t("recordingPage.share")}
1573
+ className="shrink-0"
1574
+ />
1582
1575
  </ShareRecordingPopover>
1583
1576
  ) : null}
1584
1577