@agent-native/core 0.178.0 → 0.178.1-nightly-20260909221943

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 (88) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/clips/.agents/skills/dictate/SKILL.md +1 -0
  3. package/corpus/templates/clips/DESIGN.md +10 -7
  4. package/corpus/templates/clips/actions/list-organization-state.ts +56 -3
  5. package/corpus/templates/clips/actions/list-recordings.ts +2 -0
  6. package/corpus/templates/clips/actions/search-dictations.ts +81 -0
  7. package/corpus/templates/clips/app/components/agent-destination-logos.tsx +50 -0
  8. package/corpus/templates/clips/app/components/bug-report/bug-report-dialog.tsx +60 -0
  9. package/corpus/templates/clips/app/components/bug-report/bug-report-form.tsx +206 -0
  10. package/corpus/templates/clips/app/components/clips-command-menu.tsx +653 -0
  11. package/corpus/templates/clips/app/components/library/empty-state.tsx +52 -17
  12. package/corpus/templates/clips/app/components/library/folder-card.tsx +34 -0
  13. package/corpus/templates/clips/app/components/library/folder-tree.tsx +109 -64
  14. package/corpus/templates/clips/app/components/library/library-action-hrefs.ts +27 -0
  15. package/corpus/templates/clips/app/components/library/library-grid.tsx +295 -103
  16. package/corpus/templates/clips/app/components/library/library-layout.tsx +381 -203
  17. package/corpus/templates/clips/app/components/library/library-primary-actions.tsx +48 -0
  18. package/corpus/templates/clips/app/components/library/page-header.tsx +48 -8
  19. package/corpus/templates/clips/app/components/library/recording-card.tsx +58 -55
  20. package/corpus/templates/clips/app/components/library/search-bar.tsx +5 -19
  21. package/corpus/templates/clips/app/components/library/sidebar-feedback-button.tsx +48 -0
  22. package/corpus/templates/clips/app/components/player/comments-panel.tsx +2 -2
  23. package/corpus/templates/clips/app/components/player/share-dialog.tsx +344 -260
  24. package/corpus/templates/clips/app/components/player/viewer-controls.tsx +2 -2
  25. package/corpus/templates/clips/app/components/recorder/camera-visualizer.tsx +0 -33
  26. package/corpus/templates/clips/app/components/sharing/share-ui.tsx +5 -1
  27. package/corpus/templates/clips/app/components/ui/tabs.tsx +1 -1
  28. package/corpus/templates/clips/app/hooks/use-command-search.ts +53 -0
  29. package/corpus/templates/clips/app/hooks/use-navigation-state.ts +32 -13
  30. package/corpus/templates/clips/app/hooks/use-upload-video-picker.tsx +1 -0
  31. package/corpus/templates/clips/app/i18n/en-US.ts +7 -0
  32. package/corpus/templates/clips/app/lib/agent-share.ts +16 -0
  33. package/corpus/templates/clips/app/lib/command-events.ts +7 -0
  34. package/corpus/templates/clips/app/root.tsx +6 -82
  35. package/corpus/templates/clips/app/routes/_app.dictate.tsx +25 -5
  36. package/corpus/templates/clips/app/routes/_app.library._index.tsx +2 -30
  37. package/corpus/templates/clips/app/routes/_app.library.folder.$folderId.tsx +13 -2
  38. package/corpus/templates/clips/app/routes/_app.meetings._index.tsx +4 -17
  39. package/corpus/templates/clips/app/routes/_app.r.$recordingId.tsx +111 -114
  40. package/corpus/templates/clips/app/routes/_app.spaces.$spaceId.folder.$folderId.tsx +25 -2
  41. package/corpus/templates/clips/app/routes/_app.spaces.$spaceId.tsx +14 -61
  42. package/corpus/templates/clips/app/routes/_app.spaces._index.tsx +34 -24
  43. package/corpus/templates/clips/app/routes/_app.trash.tsx +1 -1
  44. package/corpus/templates/clips/app/routes/_app.tsx +2 -4
  45. package/corpus/templates/clips/app/routes/bug-report.tsx +11 -189
  46. package/corpus/templates/clips/app/routes/share.$shareId.tsx +148 -123
  47. package/corpus/templates/clips/changelog/2026-09-08-comments-and-desktop-window-controls.md +6 -0
  48. package/corpus/templates/clips/changelog/2026-09-08-global-command-search.md +6 -0
  49. package/corpus/templates/clips/changelog/2026-09-09-agent-sidebar-fits-below-toolbar.md +6 -0
  50. package/corpus/templates/clips/changelog/2026-09-09-desktop-switches-stay-clear-in-dark-mode.md +6 -0
  51. package/corpus/templates/clips/changelog/2026-09-09-recording-agent-uses-global-panel.md +6 -0
  52. package/corpus/templates/clips/desktop/src/app.tsx +2 -2
  53. package/corpus/templates/clips/desktop/src/components/MediaDeviceRow.tsx +5 -3
  54. package/corpus/templates/clips/desktop/src/components/SourceRow.tsx +1 -0
  55. package/corpus/templates/clips/desktop/src/components/Switch.tsx +2 -5
  56. package/corpus/templates/clips/desktop/src/components/ui/switch.tsx +21 -3
  57. package/corpus/templates/clips/desktop/src/hooks/useMediaDevices.ts +72 -52
  58. package/corpus/templates/clips/desktop/src/styles.css +18 -19
  59. package/corpus/templates/clips/desktop/src/tailwind.css +3 -0
  60. package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +18 -10
  61. package/corpus/templates/clips/desktop/src-tauri/src/util.rs +3 -5
  62. package/corpus/templates/design/.agents/skills/export-handoff/SKILL.md +14 -3
  63. package/corpus/templates/design/AGENTS.md +2 -1
  64. package/corpus/templates/design/actions/export-png.ts +119 -0
  65. package/corpus/templates/design/actions/take-design-screenshot.ts +47 -13
  66. package/corpus/templates/design/server/plugins/agent-chat.ts +3 -1
  67. package/corpus/templates/factory/actions/dispatch-factory-item.ts +51 -22
  68. package/corpus/templates/factory/actions/list-triage-items.ts +21 -11
  69. package/corpus/templates/factory/actions/poll-slack-channel.ts +7 -2
  70. package/corpus/templates/factory/changelog/2026-09-09-slack-feedback-keeps-claimed-markers-after-poll.md +6 -0
  71. package/corpus/templates/factory/changelog/2026-09-09-slack-feedback-preserves-started-claimed-threads.md +6 -0
  72. package/corpus/templates/factory/changelog/2026-09-09-slack-feedback-skips-claimed-parents.md +6 -0
  73. package/corpus/templates/factory/server/lib/factory-automation-config.ts +1 -1
  74. package/corpus/templates/factory/server/lib/slack-feedback-prompt.ts +47 -0
  75. package/corpus/templates/factory/server/plugins/agent-chat.ts +5 -2
  76. package/corpus/templates/factory/server/plugins/factory-scheduler-job.ts +5 -4
  77. package/corpus/templates/factory/server/triage/review-skill-alignment.ts +12 -7
  78. package/corpus/templates/factory/server/triage/slack-review-window.ts +39 -0
  79. package/dist/client/CommandMenu.js +13 -1
  80. package/dist/client/analytics.js +10 -0
  81. package/dist/collab/routes.d.ts +1 -1
  82. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  83. package/dist/notifications/routes.d.ts +2 -2
  84. package/dist/observability/routes.d.ts +5 -5
  85. package/dist/vite/client.js +25 -7
  86. package/dist/vite/sentry-source-maps.d.ts +12 -0
  87. package/dist/vite/sentry-source-maps.js +105 -0
  88. package/package.json +2 -1
@@ -1,30 +1,13 @@
1
- import { appBasePath } from "@agent-native/core/client/api-path";
2
- import { useT } from "@agent-native/core/client/i18n";
3
1
  import {
4
2
  BUG_REPORT_POPUP_RESPONSE_HEADERS,
5
- bugReportContextToSearchParams,
6
3
  isBugReportSubmissionMessage,
7
4
  parseBugReportContext,
8
- type BugReportContext,
9
- type BugReportSeverity,
10
5
  } from "@shared/bug-report";
11
- import { IconBug, IconShieldCheck } from "@tabler/icons-react";
12
- import { useEffect, useMemo, useRef, useState } from "react";
6
+ import { useEffect, useMemo, useRef } from "react";
13
7
  import { useLocation, useOutlet } from "react-router";
14
8
 
15
- import { Button } from "@/components/ui/button";
16
- import { Input } from "@/components/ui/input";
17
- import { Label } from "@/components/ui/label";
18
- import {
19
- Select,
20
- SelectContent,
21
- SelectItem,
22
- SelectTrigger,
23
- SelectValue,
24
- } from "@/components/ui/select";
25
- import { Textarea } from "@/components/ui/textarea";
9
+ import { BugReportForm } from "@/components/bug-report/bug-report-form";
26
10
  import enMessages from "@/i18n/en-US";
27
- import { cn } from "@/lib/utils";
28
11
 
29
12
  export function meta() {
30
13
  return [{ title: enMessages.bugReportRoute.pageTitle }];
@@ -34,20 +17,6 @@ export function headers() {
34
17
  return BUG_REPORT_POPUP_RESPONSE_HEADERS;
35
18
  }
36
19
 
37
- function openRecorder(url: string): Window | null {
38
- const opened = window.open(
39
- url,
40
- "agent-native-clips-bug-report",
41
- "popup,width=1120,height=820",
42
- );
43
- if (!opened) {
44
- window.location.href = url;
45
- return null;
46
- }
47
- opened.focus();
48
- return opened;
49
- }
50
-
51
20
  function originFor(value: string | null): string | null {
52
21
  if (!value) return null;
53
22
  try {
@@ -58,7 +27,6 @@ function originFor(value: string | null): string | null {
58
27
  }
59
28
 
60
29
  export default function BugReportRoute() {
61
- const t = useT();
62
30
  const location = useLocation();
63
31
  const outlet = useOutlet();
64
32
  const initialContext = useMemo(() => {
@@ -66,28 +34,10 @@ export default function BugReportRoute() {
66
34
  return parseBugReportContext(params, { allowLoose: true });
67
35
  }, [location.search]);
68
36
 
69
- const [title, setTitle] = useState(initialContext?.title ?? "");
70
- const [description, setDescription] = useState(
71
- initialContext?.description ?? "",
72
- );
73
- const [reporterEmail, setReporterEmail] = useState(
74
- initialContext?.reporterEmail ?? "",
75
- );
76
- const [severity, setSeverity] = useState<BugReportSeverity>(
77
- initialContext?.severity ?? "normal",
78
- );
79
- const [referrer, setReferrer] = useState<string | null>(null);
80
37
  const recorderWindowRef = useRef<Window | null>(null);
81
-
82
- useEffect(() => {
83
- setReferrer(document.referrer || null);
84
- }, []);
85
-
86
- const sourceUrl = initialContext?.sourceUrl ?? referrer;
87
- const pageTitle = initialContext?.pageTitle ?? null;
88
- const sourceLabel =
89
- pageTitle || sourceUrl || t("bugReportRoute.sourceUnknown");
90
- const hostOrigin = originFor(initialContext?.returnUrl ?? sourceUrl);
38
+ const hostOrigin = originFor(
39
+ initialContext?.returnUrl ?? initialContext?.sourceUrl ?? null,
40
+ );
91
41
 
92
42
  useEffect(() => {
93
43
  if (!hostOrigin) return;
@@ -110,144 +60,16 @@ export default function BugReportRoute() {
110
60
 
111
61
  if (outlet) return outlet;
112
62
 
113
- const startRecording = () => {
114
- const context: BugReportContext = {
115
- projectId: initialContext?.projectId ?? null,
116
- title: title.trim() || initialContext?.title || null,
117
- description: description.trim() || initialContext?.description || null,
118
- severity,
119
- sourceUrl,
120
- pageTitle,
121
- appVersion: initialContext?.appVersion ?? null,
122
- environment: initialContext?.environment ?? null,
123
- reporterEmail:
124
- reporterEmail.trim() || initialContext?.reporterEmail || null,
125
- reporterName: initialContext?.reporterName ?? null,
126
- reporterId: initialContext?.reporterId ?? null,
127
- metadata: initialContext?.metadata ?? null,
128
- returnUrl: initialContext?.returnUrl ?? sourceUrl,
129
- };
130
- const params = bugReportContextToSearchParams(context);
131
- params.set("intent", "bug-report");
132
- params.set("mode", "screen");
133
- params.set("surface", "browser");
134
- recorderWindowRef.current = openRecorder(
135
- `${appBasePath()}/record?${params.toString()}`,
136
- );
137
- };
138
-
139
63
  return (
140
64
  <main className="min-h-screen bg-background text-foreground">
141
65
  <div className="mx-auto flex min-h-screen w-full max-w-xl flex-col justify-center px-4 py-5 sm:px-6">
142
66
  <section className="rounded-lg border bg-card p-4 shadow-sm sm:p-5">
143
- <div className="mb-5 flex items-start gap-3">
144
- <div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-md bg-primary text-primary-foreground">
145
- <IconBug size={20} />
146
- </div>
147
- <div className="min-w-0">
148
- <p className="text-xs font-medium uppercase tracking-[0.12em] text-muted-foreground">
149
- {t("bugReportRoute.eyebrow")}
150
- </p>
151
- <h1 className="mt-1 text-xl font-semibold leading-tight">
152
- {t("bugReportRoute.title")}
153
- </h1>
154
- <p className="mt-2 text-sm leading-6 text-muted-foreground">
155
- {t("bugReportRoute.description")}
156
- </p>
157
- </div>
158
- </div>
159
-
160
- <div className="space-y-4">
161
- <div className="space-y-2">
162
- <Label htmlFor="bug-title">
163
- {t("bugReportRoute.issueTitleLabel")}
164
- </Label>
165
- <Input
166
- id="bug-title"
167
- value={title}
168
- onChange={(event) => setTitle(event.target.value)}
169
- placeholder={t("bugReportRoute.issueTitlePlaceholder")}
170
- />
171
- </div>
172
-
173
- <div className="space-y-2">
174
- <Label htmlFor="bug-details">
175
- {t("bugReportRoute.detailsLabel")}
176
- </Label>
177
- <Textarea
178
- id="bug-details"
179
- value={description}
180
- onChange={(event) => setDescription(event.target.value)}
181
- placeholder={t("bugReportRoute.detailsPlaceholder")}
182
- className="min-h-24 resize-none"
183
- />
184
- </div>
185
-
186
- <div className="grid gap-4 sm:grid-cols-[1fr_180px]">
187
- <div className="space-y-2">
188
- <Label htmlFor="bug-email">
189
- {t("bugReportRoute.emailLabel")}
190
- </Label>
191
- <Input
192
- id="bug-email"
193
- type="email"
194
- value={reporterEmail}
195
- onChange={(event) => setReporterEmail(event.target.value)}
196
- placeholder={t("bugReportRoute.emailPlaceholder")}
197
- />
198
- </div>
199
- <div className="space-y-2">
200
- <Label>{t("bugReportRoute.severityLabel")}</Label>
201
- <Select
202
- value={severity}
203
- onValueChange={(value) =>
204
- setSeverity(value as BugReportSeverity)
205
- }
206
- >
207
- <SelectTrigger>
208
- <SelectValue />
209
- </SelectTrigger>
210
- <SelectContent>
211
- <SelectItem value="low">
212
- {t("bugReportRoute.severityLow")}
213
- </SelectItem>
214
- <SelectItem value="normal">
215
- {t("bugReportRoute.severityNormal")}
216
- </SelectItem>
217
- <SelectItem value="high">
218
- {t("bugReportRoute.severityHigh")}
219
- </SelectItem>
220
- <SelectItem value="urgent">
221
- {t("bugReportRoute.severityUrgent")}
222
- </SelectItem>
223
- </SelectContent>
224
- </Select>
225
- </div>
226
- </div>
227
-
228
- <div className="rounded-md border bg-muted/40 px-3 py-2">
229
- <p className="text-xs font-medium text-muted-foreground">
230
- {t("bugReportRoute.sourceLabel")}
231
- </p>
232
- <p
233
- className={cn(
234
- "mt-1 truncate text-sm",
235
- !sourceUrl && "text-muted-foreground",
236
- )}
237
- >
238
- {sourceLabel}
239
- </p>
240
- </div>
241
-
242
- <Button className="h-11 w-full" onClick={startRecording}>
243
- {t("bugReportRoute.startRecording")}
244
- </Button>
245
- </div>
246
-
247
- <div className="mt-5 flex items-start gap-2 rounded-md bg-muted/50 p-3 text-xs leading-5 text-muted-foreground">
248
- <IconShieldCheck size={16} className="mt-0.5 shrink-0" />
249
- <p>{t("bugReportRoute.privacyNote")}</p>
250
- </div>
67
+ <BugReportForm
68
+ initialContext={initialContext}
69
+ onRecorderOpened={(recorderWindow) => {
70
+ recorderWindowRef.current = recorderWindow;
71
+ }}
72
+ />
251
73
  </section>
252
74
  </div>
253
75
  </main>
@@ -167,7 +167,7 @@ type SharePageLoaderData = {
167
167
  accessRequestToken?: string;
168
168
  };
169
169
 
170
- type SharePanel = "transcript" | "agent";
170
+ type SharePanel = "comments" | "transcript" | "agent";
171
171
 
172
172
  type PendingAccountAction =
173
173
  | { intent: "comment"; atMs: number }
@@ -515,6 +515,15 @@ export default function ShareRoute() {
515
515
  const [panel, setPanel] = useState<SharePanel>("transcript");
516
516
  const [descriptionExpanded, setDescriptionExpanded] = useState(false);
517
517
  const commentsSectionRef = useRef<HTMLElement | null>(null);
518
+ const selectCommentsPanel = useCallback(() => {
519
+ setPanel("comments");
520
+ requestAnimationFrame(() => {
521
+ commentsSectionRef.current?.scrollIntoView({
522
+ behavior: "smooth",
523
+ block: "start",
524
+ });
525
+ });
526
+ }, []);
518
527
  const [downloading, setDownloading] = useState(false);
519
528
  const [accessRequestSent, setAccessRequestSent] = useState(false);
520
529
  const [accessRequestError, setAccessRequestError] = useState<string | null>(
@@ -1411,104 +1420,110 @@ export default function ShareRoute() {
1411
1420
  </header>
1412
1421
 
1413
1422
  <div className="flex w-full min-w-0 flex-none flex-col overflow-visible lg:col-start-1 lg:row-start-2 lg:min-h-0 lg:flex-1 lg:overflow-y-hidden">
1414
- <main className="overflow-visible lg:min-h-0 lg:flex-1 lg:overflow-y-auto">
1415
- <div className="mx-auto flex w-full flex-col gap-5 pb-10 sm:px-4 lg:pt-4">
1416
- <div className="relative aspect-video w-full">
1417
- <VideoPlayer
1418
- ref={playerRef}
1419
- onVideoElementChange={setTrackedVideoEl}
1420
- recordingId={recording.id}
1421
- videoUrl={recording.videoUrl}
1422
- mediaVersion={
1423
- recording.mediaUpdatedAt ?? recording.videoSizeBytes ?? null
1424
- }
1425
- videoFormat={recording.videoFormat}
1426
- embedProvider={isLoomEmbedBacked ? "loom" : null}
1427
- durationMs={recording.durationMs}
1428
- startMs={resolveStartMs(startMs, recording.durationMs)}
1429
- persistPlaybackPosition={Boolean(session)}
1430
- editsJson={recording.editsJson}
1431
- thumbnailUrl={recording.thumbnailUrl}
1432
- role={viewerRole ?? (viewerCanEdit ? "owner" : "viewer")}
1433
- defaultSpeed={parsePlaybackSpeed(recording.defaultSpeed) ?? 1.2}
1434
- comments={comments}
1435
- chapters={chapters}
1436
- reactions={reactions}
1437
- transcriptSegments={transcriptSegments}
1438
- cta={firstCta}
1439
- onCtaClick={() => tracking.reportCtaClick()}
1440
- onTimeUpdate={(ms) => setCurrentMs(ms)}
1441
- onCommentClick={
1442
- viewerCanUseFullscreenInteractions
1443
- ? () => {
1444
- requestAnimationFrame(() =>
1445
- commentsSectionRef.current?.scrollIntoView({
1446
- behavior: "smooth",
1447
- block: "start",
1448
- }),
1449
- );
1450
- }
1451
- : undefined
1452
- }
1453
- onFullscreenChange={setIsPlayerFullscreen}
1454
- enableComments={
1455
- recording.enableComments && viewerCanUseFullscreenInteractions
1456
- }
1457
- onAddComment={
1458
- viewerCanUseFullscreenInteractions
1459
- ? () => {
1460
- if (!session) {
1461
- requireSignIn("comment");
1462
- return;
1463
- }
1464
- setCommentAtMs(resolvePlaybackMs());
1465
- setCommentOpen(true);
1466
- }
1467
- : undefined
1468
- }
1469
- enableReactions={
1470
- recording.enableReactions &&
1471
- viewerCanUseFullscreenInteractions
1472
- }
1473
- onReact={
1474
- viewerCanUseFullscreenInteractions
1475
- ? reactToRecording
1476
- : undefined
1477
- }
1478
- className="h-full w-full rounded-none sm:rounded-xl"
1479
- />
1480
- {commentOpen && viewerCanComment
1481
- ? (() => {
1482
- const composer = (
1483
- <TimestampedCommentBar
1484
- recordingId={recording.id}
1485
- atMs={commentAtMs}
1486
- draft={commentDraft}
1487
- onDraftChange={setCommentDraft}
1488
- onClose={() => setCommentOpen(false)}
1489
- onAdded={() => {
1490
- void dataQ.refetch();
1491
- if (resumedAccountActionRef.current === "comment") {
1492
- resumedAccountActionRef.current = null;
1493
- trackEvent("share_account_action_completed", {
1494
- surface: "public_share",
1495
- recording_id: recording.id,
1496
- intent: "comment",
1497
- });
1423
+ <main className="overflow-visible lg:min-h-0 lg:flex-1 lg:overflow-hidden">
1424
+ {/* Match the authenticated viewer's YouTube-like desktop measure so
1425
+ the discussion stays close to the video on wide displays. */}
1426
+ <div className="mx-auto flex w-full flex-col gap-5 pb-10 sm:px-4 lg:h-full lg:min-h-0 lg:max-w-[min(100%,1600px,calc(177.778dvh-35.556rem))] lg:pt-4">
1427
+ <div className="flex w-full shrink-0 justify-center">
1428
+ <div className="relative aspect-video w-full">
1429
+ <VideoPlayer
1430
+ ref={playerRef}
1431
+ onVideoElementChange={setTrackedVideoEl}
1432
+ recordingId={recording.id}
1433
+ videoUrl={recording.videoUrl}
1434
+ mediaVersion={
1435
+ recording.mediaUpdatedAt ?? recording.videoSizeBytes ?? null
1436
+ }
1437
+ videoFormat={recording.videoFormat}
1438
+ embedProvider={isLoomEmbedBacked ? "loom" : null}
1439
+ durationMs={recording.durationMs}
1440
+ startMs={resolveStartMs(startMs, recording.durationMs)}
1441
+ persistPlaybackPosition={Boolean(session)}
1442
+ editsJson={recording.editsJson}
1443
+ thumbnailUrl={recording.thumbnailUrl}
1444
+ role={viewerRole ?? (viewerCanEdit ? "owner" : "viewer")}
1445
+ defaultSpeed={
1446
+ parsePlaybackSpeed(recording.defaultSpeed) ?? 1.2
1447
+ }
1448
+ comments={comments}
1449
+ chapters={chapters}
1450
+ reactions={reactions}
1451
+ transcriptSegments={transcriptSegments}
1452
+ cta={firstCta}
1453
+ onCtaClick={() => tracking.reportCtaClick()}
1454
+ onTimeUpdate={(ms) => setCurrentMs(ms)}
1455
+ onCommentClick={
1456
+ viewerCanUseFullscreenInteractions
1457
+ ? selectCommentsPanel
1458
+ : undefined
1459
+ }
1460
+ onFullscreenChange={setIsPlayerFullscreen}
1461
+ enableComments={
1462
+ recording.enableComments &&
1463
+ viewerCanUseFullscreenInteractions
1464
+ }
1465
+ onAddComment={
1466
+ viewerCanUseFullscreenInteractions
1467
+ ? () => {
1468
+ if (!session) {
1469
+ requireSignIn("comment");
1470
+ return;
1498
1471
  }
1499
- }}
1500
- />
1501
- );
1502
- // The Fullscreen API only paints the player's own element,
1503
- // so portal the composer there instead of exiting
1504
- // fullscreen when it's open.
1505
- const fullscreenContainer =
1506
- isPlayerFullscreen && playerRef.current?.container;
1507
- return fullscreenContainer
1508
- ? createPortal(composer, fullscreenContainer)
1509
- : composer;
1510
- })()
1511
- : null}
1472
+ const liveMs = resolvePlaybackMs();
1473
+ setCurrentMs(liveMs);
1474
+ if (!isPlayerFullscreen) {
1475
+ selectCommentsPanel();
1476
+ return;
1477
+ }
1478
+ setCommentAtMs(liveMs);
1479
+ setCommentOpen(true);
1480
+ }
1481
+ : undefined
1482
+ }
1483
+ enableReactions={
1484
+ recording.enableReactions &&
1485
+ viewerCanUseFullscreenInteractions
1486
+ }
1487
+ onReact={
1488
+ viewerCanUseFullscreenInteractions
1489
+ ? reactToRecording
1490
+ : undefined
1491
+ }
1492
+ className="h-full w-full rounded-none sm:rounded-xl"
1493
+ />
1494
+ {commentOpen && viewerCanComment
1495
+ ? (() => {
1496
+ const composer = (
1497
+ <TimestampedCommentBar
1498
+ recordingId={recording.id}
1499
+ atMs={commentAtMs}
1500
+ draft={commentDraft}
1501
+ onDraftChange={setCommentDraft}
1502
+ onClose={() => setCommentOpen(false)}
1503
+ onAdded={() => {
1504
+ void dataQ.refetch();
1505
+ if (resumedAccountActionRef.current === "comment") {
1506
+ resumedAccountActionRef.current = null;
1507
+ trackEvent("share_account_action_completed", {
1508
+ surface: "public_share",
1509
+ recording_id: recording.id,
1510
+ intent: "comment",
1511
+ });
1512
+ }
1513
+ }}
1514
+ />
1515
+ );
1516
+ // The Fullscreen API only paints the player's own element,
1517
+ // so portal the composer there instead of exiting
1518
+ // fullscreen when it's open.
1519
+ const fullscreenContainer =
1520
+ isPlayerFullscreen && playerRef.current?.container;
1521
+ return fullscreenContainer
1522
+ ? createPortal(composer, fullscreenContainer)
1523
+ : composer;
1524
+ })()
1525
+ : null}
1526
+ </div>
1512
1527
  </div>
1513
1528
 
1514
1529
  <section className="flex shrink-0 flex-col gap-3 px-4 pt-1 sm:px-0">
@@ -1619,11 +1634,42 @@ export default function ShareRoute() {
1619
1634
  </div>
1620
1635
  ) : null}
1621
1636
  </section>
1637
+ </div>
1638
+ </main>
1639
+ </div>
1622
1640
 
1623
- {recording.enableComments ? (
1641
+ <Tabs
1642
+ value={panel}
1643
+ onValueChange={(value) => setPanel(value as SharePanel)}
1644
+ className="contents"
1645
+ >
1646
+ <RecordingSidePanel
1647
+ className="lg:col-start-2 lg:row-start-2"
1648
+ tabs={
1649
+ <ViewerTabsList>
1650
+ {recording.enableComments ? (
1651
+ <ViewerTabsTrigger value="comments">
1652
+ {t("sharePage.comments")}
1653
+ </ViewerTabsTrigger>
1654
+ ) : null}
1655
+ <ViewerTabsTrigger value="transcript">
1656
+ {t("sharePage.transcript")}
1657
+ </ViewerTabsTrigger>
1658
+ <ViewerTabsTrigger value="agent">
1659
+ {t("sharePage.agent")}
1660
+ </ViewerTabsTrigger>
1661
+ </ViewerTabsList>
1662
+ }
1663
+ >
1664
+ {recording.enableComments ? (
1665
+ <TabsContent
1666
+ forceMount
1667
+ value="comments"
1668
+ className="mt-0 flex min-h-0 flex-1 flex-col overflow-hidden data-[state=inactive]:hidden"
1669
+ >
1624
1670
  <section
1625
1671
  ref={commentsSectionRef}
1626
- className="scroll-mt-14 flex min-h-0 flex-1 flex-col px-1 pb-5 pt-4"
1672
+ className="flex min-h-0 flex-1 flex-col overflow-hidden px-3 pb-3 pt-3"
1627
1673
  >
1628
1674
  <h2 className="mb-3 shrink-0 text-sm font-semibold">
1629
1675
  {t("sharePage.comments")}
@@ -1655,29 +1701,8 @@ export default function ShareRoute() {
1655
1701
  presentation="inline"
1656
1702
  />
1657
1703
  </section>
1658
- ) : null}
1659
- </div>
1660
- </main>
1661
- </div>
1662
-
1663
- <Tabs
1664
- value={panel}
1665
- onValueChange={(value) => setPanel(value as SharePanel)}
1666
- className="contents"
1667
- >
1668
- <RecordingSidePanel
1669
- className="lg:col-start-2 lg:row-start-2"
1670
- tabs={
1671
- <ViewerTabsList>
1672
- <ViewerTabsTrigger value="transcript">
1673
- {t("sharePage.transcript")}
1674
- </ViewerTabsTrigger>
1675
- <ViewerTabsTrigger value="agent">
1676
- {t("sharePage.agent")}
1677
- </ViewerTabsTrigger>
1678
- </ViewerTabsList>
1679
- }
1680
- >
1704
+ </TabsContent>
1705
+ ) : null}
1681
1706
  <TabsContent
1682
1707
  value="agent"
1683
1708
  className="mt-0 flex min-h-0 flex-1 flex-col overflow-y-auto"
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-09-08
4
+ ---
5
+
6
+ Fixed desktop comment threads by keeping them in a full-height viewer tab, polished the side-panel tabs and spacing, added move, minimize, and close controls to desktop Clips windows, corrected the shared New recording action’s split-button corners, made library sub-item selection clear in the sidebar, and added visible folder tiles plus scoped recording actions to library and space views.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-09-08
4
+ ---
5
+
6
+ The Cmd+K command menu now searches and navigates across recordings, meetings, dictations, folders, and spaces.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-09-09
4
+ ---
5
+
6
+ The Agent sidebar now fits below the app toolbar without clipping its composer.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-09-09
4
+ ---
5
+
6
+ Desktop recorder switches now keep their checked state clear and legible in dark mode.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-09-09
4
+ ---
5
+
6
+ Recording pages now open the same contextual Agent panel used throughout the app.
@@ -898,7 +898,7 @@ function SettingsSwitch({
898
898
  onCheckedChange={onCheckedChange}
899
899
  disabled={disabled}
900
900
  aria-label={label}
901
- className="data-[state=checked]:border-success data-[state=checked]:bg-success data-[state=unchecked]:border-border"
901
+ tone="success"
902
902
  />
903
903
  );
904
904
  }
@@ -1351,7 +1351,7 @@ export function App({
1351
1351
  loadDevices,
1352
1352
  requestDeviceAccess,
1353
1353
  } = useMediaDevices({
1354
- bubbleActiveRef,
1354
+ microphoneEnabled: micOn,
1355
1355
  popoverVisible,
1356
1356
  setCameraError,
1357
1357
  setRecError,
@@ -131,7 +131,7 @@ export function MediaDeviceRow({
131
131
  </DropdownMenuTrigger>
132
132
  ) : (
133
133
  <span
134
- className="row-button row-button-placeholder row-label-muted"
134
+ className="row-button row-button-placeholder"
135
135
  aria-disabled="true"
136
136
  >
137
137
  {label}
@@ -191,8 +191,10 @@ export function MediaDeviceRow({
191
191
  </div>
192
192
  </div>
193
193
  {kind === "mic" && onSystemAudioToggle ? (
194
- <div className="system-audio-row">
195
- <span className="row-icon" aria-hidden>
194
+ <div
195
+ className={`row ${systemAudio ? "row-on" : "row-off"} system-audio-row`}
196
+ >
197
+ <span className="row-icon system-audio-icon" aria-hidden>
196
198
  <IconVolume2 size={20} stroke={1.75} />
197
199
  </span>
198
200
  <span className="system-audio-label">Record system audio</span>
@@ -115,6 +115,7 @@ export function SourceRow({
115
115
  </DropdownMenuRadioGroup>
116
116
  <DropdownMenuSeparator />
117
117
  <DropdownMenuItem
118
+ inset
118
119
  onSelect={() => {
119
120
  onChange("window");
120
121
  onChooseWindow?.();
@@ -1,6 +1,5 @@
1
1
  import * as React from "react";
2
2
 
3
- import { cn } from "../lib/utils";
4
3
  import { Switch as UiSwitch } from "./ui/switch";
5
4
 
6
5
  interface SwitchProps extends Omit<
@@ -31,10 +30,8 @@ export const Switch = React.forwardRef<
31
30
  aria-label={label}
32
31
  disabled={disabled}
33
32
  data-tw-surface
34
- className={cn(
35
- "data-[state=checked]:border-success data-[state=checked]:bg-success data-[state=unchecked]:border-border",
36
- className,
37
- )}
33
+ tone="success"
34
+ className={className}
38
35
  />
39
36
  );
40
37
  });