@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
@@ -8,15 +8,33 @@ import {
8
8
  IconAlertTriangle,
9
9
  IconChevronLeft,
10
10
  IconChevronRight,
11
+ IconFolderPlus,
12
+ IconLink,
13
+ IconUpload,
11
14
  } from "@tabler/icons-react";
12
- import { useCallback, useEffect, useMemo, useState } from "react";
15
+ import {
16
+ type ReactElement,
17
+ useCallback,
18
+ useEffect,
19
+ useMemo,
20
+ useState,
21
+ } from "react";
22
+ import { Link } from "react-router";
13
23
  import { toast } from "sonner";
14
24
 
15
25
  import { CreateFolderDialog } from "@/components/library/create-folder-dialog";
16
26
  import { ShareRecordingDialog } from "@/components/player/share-dialog";
17
27
  import { Button } from "@/components/ui/button";
28
+ import {
29
+ ContextMenu,
30
+ ContextMenuContent,
31
+ ContextMenuItem,
32
+ ContextMenuSeparator,
33
+ ContextMenuTrigger,
34
+ } from "@/components/ui/context-menu";
18
35
  import {
19
36
  useFolders,
37
+ useOrganizations,
20
38
  useRecordings,
21
39
  useRecordingsCount,
22
40
  useTrashRecording,
@@ -26,13 +44,21 @@ import {
26
44
  type ListRecordingsArgs,
27
45
  type RecordingSummary,
28
46
  } from "@/hooks/use-library";
47
+ import { useUploadVideoPicker } from "@/hooks/use-upload-video-picker";
48
+ import { OPEN_CREATE_FOLDER_EVENT } from "@/lib/command-events";
29
49
  import { retryRecordingUploadFromBackup } from "@/lib/recording-retry";
30
50
  import { cn } from "@/lib/utils";
31
51
 
32
52
  import { BulkActionToolbar, type BulkMoveTarget } from "./bulk-action-toolbar";
33
53
  import { EmptyState } from "./empty-state";
34
54
  import { FilterChips, type FilterChip } from "./filter-chips";
35
- import { PageBreadcrumb, PageHeader } from "./page-header";
55
+ import { FolderCard } from "./folder-card";
56
+ import { buildLibraryActionHrefs } from "./library-action-hrefs";
57
+ import {
58
+ PageBreadcrumb,
59
+ PageHeader,
60
+ type PageBreadcrumbItem,
61
+ } from "./page-header";
36
62
  import { RecordingCard } from "./recording-card";
37
63
  import { SearchBar } from "./search-bar";
38
64
  import { SortMenu, type SortKey } from "./sort-menu";
@@ -44,6 +70,7 @@ interface LibraryGridProps {
44
70
  /** What empty-state illustration to render. Defaults from `view`. */
45
71
  emptyKind?: "library" | "shared" | "folder" | "space" | "archive" | "trash";
46
72
  title?: string;
73
+ breadcrumbItems?: readonly PageBreadcrumbItem[];
47
74
  tagFilter?: string | null;
48
75
  onClearTag?: () => void;
49
76
  extraActions?: React.ReactNode;
@@ -73,6 +100,58 @@ type CreateFolderTarget =
73
100
  | { kind: "single"; recording: RecordingSummary }
74
101
  | { kind: "bulk"; recordingIds: string[] };
75
102
 
103
+ function LibraryCanvasContextMenu({
104
+ enabled,
105
+ onCreateFolder,
106
+ uploadHref,
107
+ importLoomHref,
108
+ children,
109
+ }: {
110
+ enabled: boolean;
111
+ onCreateFolder: () => void;
112
+ uploadHref: string;
113
+ importLoomHref: string;
114
+ children: ReactElement;
115
+ }) {
116
+ const t = useT();
117
+ const { input, openUploadPicker } = useUploadVideoPicker();
118
+
119
+ if (!enabled) return children;
120
+
121
+ return (
122
+ <ContextMenu>
123
+ <ContextMenuTrigger asChild>{children}</ContextMenuTrigger>
124
+ <ContextMenuContent>
125
+ <ContextMenuItem
126
+ onSelect={() => {
127
+ setTimeout(onCreateFolder, 0);
128
+ }}
129
+ >
130
+ <IconFolderPlus className="me-2 size-4" />
131
+ {t("navigation.newFolder")}
132
+ </ContextMenuItem>
133
+ <ContextMenuSeparator />
134
+ <ContextMenuItem
135
+ onSelect={(event) => {
136
+ event.preventDefault();
137
+ openUploadPicker(uploadHref);
138
+ }}
139
+ >
140
+ <IconUpload />
141
+ {t("preRecord.uploadVideo")}
142
+ </ContextMenuItem>
143
+ <ContextMenuItem asChild>
144
+ <Link to={importLoomHref}>
145
+ <IconLink />
146
+ {t("preRecord.importLoom")}
147
+ </Link>
148
+ </ContextMenuItem>
149
+ </ContextMenuContent>
150
+ {input}
151
+ </ContextMenu>
152
+ );
153
+ }
154
+
76
155
  function buildMoveTargets(
77
156
  folders: FolderTargetRow[],
78
157
  currentFolderId: string | null,
@@ -107,6 +186,7 @@ export function LibraryGrid({
107
186
  spaceId = null,
108
187
  emptyKind,
109
188
  title,
189
+ breadcrumbItems,
110
190
  tagFilter,
111
191
  onClearTag,
112
192
  extraActions,
@@ -117,11 +197,18 @@ export function LibraryGrid({
117
197
  const [lastSelectedId, setLastSelectedId] = useState<string | null>(null);
118
198
  const selectionMode = selected.size > 0;
119
199
  const [sharingRec, setSharingRec] = useState<RecordingSummary | null>(null);
200
+ const [createFolderOpen, setCreateFolderOpen] = useState(false);
120
201
  const [createFolderTarget, setCreateFolderTarget] =
121
202
  useState<CreateFolderTarget | null>(null);
122
203
  const [isBulkPending, setIsBulkPending] = useState(false);
123
204
  const [page, setPage] = useState(1);
124
205
  const selectionStateKey = useMemo(() => `selection:${getBrowserTabId()}`, []);
206
+ const pageBreadcrumbItems =
207
+ breadcrumbItems ?? (title ? [{ label: title }] : []);
208
+ const { uploadHref, importLoomHref } = buildLibraryActionHrefs({
209
+ folderId,
210
+ spaceId,
211
+ });
125
212
 
126
213
  useEffect(() => {
127
214
  if (!title) return;
@@ -139,6 +226,16 @@ export function LibraryGrid({
139
226
  setLastSelectedId(null);
140
227
  }, [view, folderId, spaceId, tagFilter, sort]);
141
228
 
229
+ useEffect(() => {
230
+ const handleOpenCreateFolder = () => setCreateFolderOpen(true);
231
+ window.addEventListener(OPEN_CREATE_FOLDER_EVENT, handleOpenCreateFolder);
232
+ return () =>
233
+ window.removeEventListener(
234
+ OPEN_CREATE_FOLDER_EVENT,
235
+ handleOpenCreateFolder,
236
+ );
237
+ }, []);
238
+
142
239
  const countArgs = useMemo(
143
240
  () => ({
144
241
  view,
@@ -179,14 +276,32 @@ export function LibraryGrid({
179
276
  const moveRecording = useMoveRecording();
180
277
  const canManageRecordings = view !== "shared";
181
278
  const canMoveSelection = view === "library" || view === "space";
182
- const { data: scopedFolders } = useFolders(
279
+ const { data: organizations } = useOrganizations({
280
+ enabled: canMoveSelection,
281
+ });
282
+ const currentOrganizationId =
283
+ organizations?.currentId ?? organizations?.organizations?.[0]?.id;
284
+ const { data: scopedFolders, isLoading: isFoldersLoading } = useFolders(
183
285
  {
286
+ organizationId: currentOrganizationId,
184
287
  spaceId: view === "space" ? (spaceId ?? null) : null,
185
288
  },
186
289
  {
187
- enabled: canMoveSelection && (view !== "space" || Boolean(spaceId)),
290
+ enabled:
291
+ canMoveSelection &&
292
+ Boolean(currentOrganizationId) &&
293
+ (view !== "space" || Boolean(spaceId)),
188
294
  },
189
295
  );
296
+ const visibleFolders = useMemo(
297
+ () =>
298
+ view === "library" || view === "space"
299
+ ? ((scopedFolders?.folders ?? []) as FolderTargetRow[]).filter(
300
+ (folder) => (folder.parentId ?? null) === (folderId ?? null),
301
+ )
302
+ : [],
303
+ [folderId, scopedFolders, view],
304
+ );
190
305
  const selectedIds = useMemo(() => Array.from(selected), [selected]);
191
306
  const moveTargets = useMemo(
192
307
  () =>
@@ -373,9 +488,12 @@ export function LibraryGrid({
373
488
  />
374
489
  )}
375
490
  <CreateFolderDialog
376
- open={Boolean(createFolderTarget)}
491
+ open={Boolean(createFolderTarget) || createFolderOpen}
377
492
  onOpenChange={(open) => {
378
- if (!open) setCreateFolderTarget(null);
493
+ if (!open) {
494
+ setCreateFolderOpen(false);
495
+ setCreateFolderTarget(null);
496
+ }
379
497
  }}
380
498
  spaceId={view === "space" ? spaceId : null}
381
499
  parentId={folderId}
@@ -393,7 +511,9 @@ export function LibraryGrid({
393
511
  <PageHeader>
394
512
  <div className="flex min-w-0 flex-1 items-center gap-3 lg:grid lg:grid-cols-[minmax(0,1fr)_20rem_minmax(0,1fr)]">
395
513
  <div className="min-w-0 flex-1 lg:flex-none">
396
- {title ? <PageBreadcrumb label={title} /> : null}
514
+ {pageBreadcrumbItems.length > 0 ? (
515
+ <PageBreadcrumb items={pageBreadcrumbItems} />
516
+ ) : null}
397
517
  </div>
398
518
  <SearchBar
399
519
  side="bottom"
@@ -421,103 +541,175 @@ export function LibraryGrid({
421
541
  )}
422
542
  aria-busy={isLoading}
423
543
  >
424
- <div className="flex min-h-full flex-col p-5">
425
- {isLoading ? (
426
- <div className="grid gap-4 [grid-template-columns:repeat(auto-fill,minmax(300px,1fr))]">
427
- {Array.from({ length: 8 }).map((_, i) => (
428
- <Skeleton key={i} />
429
- ))}
430
- </div>
431
- ) : isError && recordings.length === 0 ? (
432
- <div className="flex flex-1 flex-col items-center justify-center py-20 px-8 text-center">
433
- <div className="relative mb-6 flex h-20 w-20 items-center justify-center rounded-2xl bg-destructive/10">
434
- <IconAlertTriangle className="h-10 w-10 text-destructive" />
544
+ <LibraryCanvasContextMenu
545
+ enabled={canMoveSelection}
546
+ onCreateFolder={() => setCreateFolderOpen(true)}
547
+ uploadHref={uploadHref}
548
+ importLoomHref={importLoomHref}
549
+ >
550
+ <div className="flex min-h-full flex-col p-5">
551
+ {isLoading || (view !== "shared" && isFoldersLoading) ? (
552
+ <div className="grid gap-4 [grid-template-columns:repeat(auto-fill,minmax(300px,1fr))]">
553
+ {Array.from({ length: 8 }).map((_, i) => (
554
+ <Skeleton key={i} />
555
+ ))}
435
556
  </div>
436
- <h2 className="text-base font-semibold text-foreground mb-1">
437
- {t("libraryGrid.loadFailedTitle")}
438
- </h2>
439
- <p className="text-sm text-muted-foreground max-w-sm mb-5">
440
- {t("libraryGrid.loadFailedBody")}
441
- </p>
442
- <Button
443
- onClick={() => refetch()}
444
- disabled={isRefetching}
445
- size="sm"
446
- >
447
- {t("libraryGrid.retry")}
448
- </Button>
449
- </div>
450
- ) : recordings.length === 0 ? (
451
- <EmptyState
452
- kind={resolvedEmptyKind}
453
- spaceId={spaceId}
454
- folderId={folderId}
455
- />
456
- ) : (
457
- <div className="grid gap-4 [grid-template-columns:repeat(auto-fill,minmax(300px,1fr))]">
458
- {recordings.map((r: RecordingSummary) => (
459
- <RecordingCard
460
- key={r.id}
461
- recording={r}
462
- selected={
463
- canManageRecordings ? selected.has(r.id) : undefined
464
- }
465
- selectionMode={canManageRecordings && selectionMode}
466
- onToggleSelect={
467
- canManageRecordings ? handleToggleSelect : undefined
468
- }
469
- onShare={(rec) => setSharingRec(rec)}
470
- moveTargets={moveTargets}
471
- onMove={canMoveSelection ? moveSingle : undefined}
472
- isMovePending={moveRecording.isPending}
473
- onRetry={canManageRecordings ? handleRetry : undefined}
474
- onCreateFolder={() => {
475
- setCreateFolderTarget({ kind: "single", recording: r });
476
- }}
477
- onTrash={
478
- canManageRecordings
479
- ? (rec) => {
480
- trashRecording.mutate(
481
- { id: rec.id },
482
- {
483
- onSuccess: () =>
484
- toast.success(t("libraryGrid.movedToTrash")),
485
- },
486
- );
487
- }
488
- : undefined
489
- }
490
- onArchive={
491
- canManageRecordings
492
- ? (rec) => {
493
- if (rec.archivedAt) {
494
- restoreRecording.mutate(
495
- { id: rec.id },
496
- {
497
- onSuccess: () =>
498
- toast.success(
499
- t("libraryGrid.restoredFromArchive"),
500
- ),
501
- },
502
- );
503
- } else {
504
- archiveRecording.mutate(
505
- { id: rec.id },
506
- {
507
- onSuccess: () =>
508
- toast.success(t("libraryGrid.archived")),
509
- },
510
- );
511
- }
512
- }
513
- : undefined
514
- }
515
- readOnly={!canManageRecordings}
516
- />
517
- ))}
518
- </div>
519
- )}
520
- </div>
557
+ ) : isError && recordings.length === 0 ? (
558
+ <div className="flex flex-1 flex-col items-center justify-center py-20 px-8 text-center">
559
+ <div className="relative mb-6 flex h-20 w-20 items-center justify-center rounded-2xl bg-destructive/10">
560
+ <IconAlertTriangle className="h-10 w-10 text-destructive" />
561
+ </div>
562
+ <h2 className="text-base font-semibold text-foreground mb-1">
563
+ {t("libraryGrid.loadFailedTitle")}
564
+ </h2>
565
+ <p className="text-sm text-muted-foreground max-w-sm mb-5">
566
+ {t("libraryGrid.loadFailedBody")}
567
+ </p>
568
+ <Button
569
+ onClick={() => refetch()}
570
+ disabled={isRefetching}
571
+ size="sm"
572
+ >
573
+ {t("libraryGrid.retry")}
574
+ </Button>
575
+ </div>
576
+ ) : recordings.length === 0 && visibleFolders.length === 0 ? (
577
+ <EmptyState
578
+ kind={resolvedEmptyKind}
579
+ spaceId={spaceId}
580
+ folderId={folderId}
581
+ />
582
+ ) : (
583
+ <div className="flex flex-col gap-8">
584
+ {visibleFolders.length > 0 && (
585
+ <section aria-labelledby="library-folders-heading">
586
+ <h2
587
+ id="library-folders-heading"
588
+ className="mb-3 text-sm font-semibold text-foreground"
589
+ >
590
+ {t("navigation.folders")}
591
+ </h2>
592
+ <div className="grid gap-3 [grid-template-columns:repeat(auto-fill,minmax(min(100%,220px),1fr))]">
593
+ {visibleFolders.map((folder) => (
594
+ <div
595
+ key={folder.id}
596
+ className="min-w-0"
597
+ onContextMenu={(event) => event.stopPropagation()}
598
+ >
599
+ <FolderCard
600
+ folder={folder}
601
+ href={
602
+ view === "space"
603
+ ? `/spaces/${spaceId}/folder/${folder.id}`
604
+ : `/library/folder/${folder.id}`
605
+ }
606
+ />
607
+ </div>
608
+ ))}
609
+ </div>
610
+ </section>
611
+ )}
612
+
613
+ {recordings.length > 0 && (
614
+ <section aria-label={t("navigation.recordings")}>
615
+ {visibleFolders.length > 0 && (
616
+ <h2
617
+ id="library-recordings-heading"
618
+ className="mb-3 text-sm font-semibold text-foreground"
619
+ >
620
+ {t("navigation.recordings")}
621
+ </h2>
622
+ )}
623
+ <div className="grid gap-4 [grid-template-columns:repeat(auto-fill,minmax(300px,1fr))]">
624
+ {recordings.map((r: RecordingSummary) => (
625
+ <div
626
+ key={r.id}
627
+ className="min-w-0"
628
+ onContextMenu={(event) => event.stopPropagation()}
629
+ >
630
+ <RecordingCard
631
+ recording={r}
632
+ selected={
633
+ canManageRecordings
634
+ ? selected.has(r.id)
635
+ : undefined
636
+ }
637
+ selectionMode={
638
+ canManageRecordings && selectionMode
639
+ }
640
+ onToggleSelect={
641
+ canManageRecordings
642
+ ? handleToggleSelect
643
+ : undefined
644
+ }
645
+ onShare={(rec) => setSharingRec(rec)}
646
+ moveTargets={moveTargets}
647
+ onMove={canMoveSelection ? moveSingle : undefined}
648
+ isMovePending={moveRecording.isPending}
649
+ onRetry={
650
+ canManageRecordings ? handleRetry : undefined
651
+ }
652
+ onCreateFolder={() => {
653
+ setCreateFolderTarget({
654
+ kind: "single",
655
+ recording: r,
656
+ });
657
+ }}
658
+ onTrash={
659
+ canManageRecordings
660
+ ? (rec) => {
661
+ trashRecording.mutate(
662
+ { id: rec.id },
663
+ {
664
+ onSuccess: () =>
665
+ toast.success(
666
+ t("libraryGrid.movedToTrash"),
667
+ ),
668
+ },
669
+ );
670
+ }
671
+ : undefined
672
+ }
673
+ onArchive={
674
+ canManageRecordings
675
+ ? (rec) => {
676
+ if (rec.archivedAt) {
677
+ restoreRecording.mutate(
678
+ { id: rec.id },
679
+ {
680
+ onSuccess: () =>
681
+ toast.success(
682
+ t(
683
+ "libraryGrid.restoredFromArchive",
684
+ ),
685
+ ),
686
+ },
687
+ );
688
+ } else {
689
+ archiveRecording.mutate(
690
+ { id: rec.id },
691
+ {
692
+ onSuccess: () =>
693
+ toast.success(
694
+ t("libraryGrid.archived"),
695
+ ),
696
+ },
697
+ );
698
+ }
699
+ }
700
+ : undefined
701
+ }
702
+ readOnly={!canManageRecordings}
703
+ />
704
+ </div>
705
+ ))}
706
+ </div>
707
+ </section>
708
+ )}
709
+ </div>
710
+ )}
711
+ </div>
712
+ </LibraryCanvasContextMenu>
521
713
  </div>
522
714
 
523
715
  {!isLoading && recordings.length > 0 && totalPages > 1 && (