@agent-native/core-corpus 0.216.0 → 0.218.0

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 (196) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/session-replay/SKILL.md +61 -1
  3. package/corpus/templates/analytics/AGENTS.md +1 -1
  4. package/corpus/templates/analytics/CHANGELOG.md +3 -0
  5. package/corpus/templates/analytics/actions/get-session-replay-summary.ts +9 -65
  6. package/corpus/templates/analytics/actions/list-event-catalog.ts +1 -1
  7. package/corpus/templates/analytics/actions/list-session-outputs.ts +40 -0
  8. package/corpus/templates/analytics/actions/list-session-recordings.ts +17 -4
  9. package/corpus/templates/analytics/actions/view-screen.ts +29 -3
  10. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +38 -167
  11. package/corpus/templates/analytics/app/i18n/en-US.ts +27 -0
  12. package/corpus/templates/analytics/app/pages/sessions/EventCatalogPage.tsx +18 -1
  13. package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +3 -1
  14. package/corpus/templates/analytics/app/pages/sessions/SessionOutputs.tsx +197 -0
  15. package/corpus/templates/analytics/app/pages/sessions/SessionsTriagePage.tsx +114 -4
  16. package/corpus/templates/analytics/changelog/2026-10-15-sessions-can-find-people-who-made-a-slides-deck-but-didn-t-p.md +6 -0
  17. package/corpus/templates/analytics/changelog/2026-10-16-owned-and-shared-chats-appear-in-separate-history-sections.md +6 -0
  18. package/corpus/templates/analytics/changelog/2026-10-16-the-activation-funnel-counts-visitors-who-decline-analytics-.md +6 -0
  19. package/corpus/templates/analytics/server/db/schema.ts +67 -0
  20. package/corpus/templates/analytics/server/lib/canonical-first-party-dashboard-repair.ts +9 -0
  21. package/corpus/templates/analytics/server/lib/first-party-analytics.ts +28 -22
  22. package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +36 -1
  23. package/corpus/templates/analytics/server/lib/output-outcomes.ts +712 -0
  24. package/corpus/templates/analytics/server/lib/replay-origins.ts +33 -0
  25. package/corpus/templates/analytics/server/lib/session-event-index.ts +20 -0
  26. package/corpus/templates/analytics/server/lib/session-performance.ts +6 -40
  27. package/corpus/templates/analytics/server/lib/session-recording-associations.ts +30 -1
  28. package/corpus/templates/analytics/server/lib/session-replay-triage.ts +99 -0
  29. package/corpus/templates/analytics/server/lib/session-replay.ts +45 -31
  30. package/corpus/templates/analytics/server/lib/sessions-triage-lab.ts +8 -2
  31. package/corpus/templates/analytics/server/plugins/db.ts +46 -0
  32. package/corpus/templates/analytics/shared/output-use.ts +205 -0
  33. package/corpus/templates/analytics/shared/session-events.ts +6 -0
  34. package/corpus/templates/assets/CHANGELOG.md +6 -0
  35. package/corpus/templates/assets/app/components/layout/Sidebar.tsx +51 -117
  36. package/corpus/templates/assets/changelog/2026-10-16-owned-and-shared-chats-appear-in-separate-history-sections.md +6 -0
  37. package/corpus/templates/brain/app/components/layout/Sidebar.tsx +34 -101
  38. package/corpus/templates/brain/app/routes/sources.tsx +1 -1
  39. package/corpus/templates/brain/vitest.config.ts +7 -0
  40. package/corpus/templates/calendar/CHANGELOG.md +12 -0
  41. package/corpus/templates/calendar/actions/create-event.ts +2 -4
  42. package/corpus/templates/calendar/actions/update-event.ts +35 -6
  43. package/corpus/templates/calendar/app/pages/BookingLinksPage.tsx +1 -1
  44. package/corpus/templates/calendar/changelog/2026-10-16-booking-links-show-localized-visibility-badges.md +6 -0
  45. package/corpus/templates/calendar/changelog/2026-10-21-solo-events-avoid-notification-approval.md +6 -0
  46. package/corpus/templates/chat/app/components/layout/Sidebar.tsx +62 -101
  47. package/corpus/templates/clips/CHANGELOG.md +7 -20
  48. package/corpus/templates/clips/app/components/meetings/share-meeting-dialog.tsx +20 -77
  49. package/corpus/templates/clips/app/components/player/share-dialog.tsx +63 -90
  50. package/corpus/templates/clips/app/components/sharing/share-test-support.tsx +180 -0
  51. package/corpus/templates/clips/app/components/sharing/share-ui.tsx +5 -584
  52. package/corpus/templates/clips/changelog/2026-10-16-share-dialogs-make-it-easier-to-see-who-has-access-and-manag.md +6 -0
  53. package/corpus/templates/clips/changelog/2026-10-21-share-panels-describe-general-access-in-a-sentence.md +6 -0
  54. package/corpus/templates/community/qr/package.json +1 -1
  55. package/corpus/templates/content/.agents/skills/document-editing/references/databases.md +6 -3
  56. package/corpus/templates/content/CHANGELOG.md +10 -0
  57. package/corpus/templates/content/actions/_browser-document-save-attempt.ts +23 -1
  58. package/corpus/templates/content/actions/_builder-cms-blob-custody.ts +467 -0
  59. package/corpus/templates/content/actions/_builder-cms-execution-lease.ts +17 -0
  60. package/corpus/templates/content/actions/_builder-cms-intent-lookup.ts +5 -0
  61. package/corpus/templates/content/actions/_builder-cms-read-client.ts +881 -21
  62. package/corpus/templates/content/actions/_builder-cms-write-adapter.ts +28 -2
  63. package/corpus/templates/content/actions/_builder-cms-write-client.ts +109 -11
  64. package/corpus/templates/content/actions/_builder-docs-client.ts +1 -0
  65. package/corpus/templates/content/actions/_builder-private-blob.test-fixture.ts +90 -0
  66. package/corpus/templates/content/actions/_builder-source-execution-guard.ts +48 -0
  67. package/corpus/templates/content/actions/_content-save-outcomes.ts +5 -0
  68. package/corpus/templates/content/actions/_database-source-utils.ts +290 -8
  69. package/corpus/templates/content/actions/_database-utils.ts +93 -7
  70. package/corpus/templates/content/actions/_database-view-setup.ts +21 -2
  71. package/corpus/templates/content/actions/_delete-content-space.ts +8 -1
  72. package/corpus/templates/content/actions/_github-source-freshness.ts +30 -4
  73. package/corpus/templates/content/actions/add-content-database-source-field-property.ts +23 -6
  74. package/corpus/templates/content/actions/cancel-prepared-builder-source-update.ts +110 -26
  75. package/corpus/templates/content/actions/change-content-database-source-role.ts +51 -7
  76. package/corpus/templates/content/actions/delete-document.ts +41 -3
  77. package/corpus/templates/content/actions/disconnect-content-database-source.ts +68 -2
  78. package/corpus/templates/content/actions/disconnect-local-folder-source.ts +33 -2
  79. package/corpus/templates/content/actions/execute-builder-source-execution.ts +900 -140
  80. package/corpus/templates/content/actions/get-document.ts +2 -0
  81. package/corpus/templates/content/actions/permanently-delete-document.ts +6 -0
  82. package/corpus/templates/content/actions/prepare-builder-source-execution.ts +155 -13
  83. package/corpus/templates/content/actions/prepare-builder-source-review.ts +299 -73
  84. package/corpus/templates/content/actions/set-live-body-switch.ts +45 -0
  85. package/corpus/templates/content/actions/update-document.ts +139 -20
  86. package/corpus/templates/content/actions/validate-builder-source-execution.ts +135 -26
  87. package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +119 -30
  88. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +56 -13
  89. package/corpus/templates/content/app/components/editor/SuggestionText.tsx +18 -3
  90. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +89 -22
  91. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +23 -0
  92. package/corpus/templates/content/app/components/editor/database/GitHubSourceStatusLine.tsx +27 -2
  93. package/corpus/templates/content/app/components/editor/database/ListItemLayout.tsx +32 -8
  94. package/corpus/templates/content/app/components/editor/database/ListItemRow.tsx +27 -2
  95. package/corpus/templates/content/app/components/editor/database-sources/BuilderSourceReviewDialog.tsx +1 -0
  96. package/corpus/templates/content/app/components/editor/extensions/SuggestionHighlight.ts +383 -34
  97. package/corpus/templates/content/app/components/editor/suggestions/draft-session.ts +31 -0
  98. package/corpus/templates/content/app/components/editor/suggestions/presentation-rebase.ts +82 -4
  99. package/corpus/templates/content/app/global.css +31 -0
  100. package/corpus/templates/content/app/hooks/use-content-database.ts +40 -2
  101. package/corpus/templates/content/app/hooks/use-documents.ts +14 -0
  102. package/corpus/templates/content/app/i18n-data.ts +1 -0
  103. package/corpus/templates/content/changelog/2026-10-16-accepting-a-suggestion-on-a-page-that-changed-since-it-was-s.md +6 -0
  104. package/corpus/templates/content/changelog/2026-10-17-builder-content-avoids-retrying-a-write-when-builder-accepte.md +6 -0
  105. package/corpus/templates/content/changelog/2026-10-18-link-a-new-collection-to-a-github-folder-from-its-sources.md +1 -1
  106. package/corpus/templates/content/changelog/2026-10-20-builder-content-sync-keeps-read-and-write-status-accurate-wh.md +6 -0
  107. package/corpus/templates/content/changelog/2026-10-20-suggested-edits-that-add-remove-split-or-merge-paragraphs-li.md +6 -0
  108. package/corpus/templates/content/changelog/2026-10-21-list-rows-can-show-each-pages-first-lines.md +6 -0
  109. package/corpus/templates/content/changelog/2026-10-22-a-collection-linked-to-a-big-github-folder-fills-faster-and-p.md +6 -0
  110. package/corpus/templates/content/docs/product/capabilities/content.source.adapters.md +29 -0
  111. package/corpus/templates/content/parity/matrix.md +42 -42
  112. package/corpus/templates/content/parity/matrix.ts +3 -1
  113. package/corpus/templates/content/server/db/schema.ts +20 -0
  114. package/corpus/templates/content/server/lib/content-trash-purge.ts +15 -1
  115. package/corpus/templates/content/server/lib/live-body-writer.ts +138 -26
  116. package/corpus/templates/content/server/live-copy/binding.ts +31 -0
  117. package/corpus/templates/content/server/live-copy/bounded-task.d.ts +6 -0
  118. package/corpus/templates/content/server/live-copy/exec-db.ts +1 -0
  119. package/corpus/templates/content/server/live-copy/holds.ts +59 -0
  120. package/corpus/templates/content/server/live-copy/persist.ts +656 -0
  121. package/corpus/templates/content/server/live-copy/switch.ts +136 -0
  122. package/corpus/templates/content/server/live-copy/task-names.ts +4 -0
  123. package/corpus/templates/content/server/live-copy/tasks.ts +183 -0
  124. package/corpus/templates/content/server/live-copy/write-cause.ts +85 -0
  125. package/corpus/templates/content/server/live-copy/write.ts +85 -0
  126. package/corpus/templates/content/server/plugins/builder-private-payload-cleanup.ts +22 -0
  127. package/corpus/templates/content/server/plugins/collab.ts +3 -0
  128. package/corpus/templates/content/server/plugins/db.ts +28 -0
  129. package/corpus/templates/content/shared/api.ts +30 -1
  130. package/corpus/templates/content/shared/database-item-layout.ts +54 -22
  131. package/corpus/templates/content/shared/document-intent-merge.ts +16 -9
  132. package/corpus/templates/content/shared/og-description.ts +258 -36
  133. package/corpus/templates/content/shared/suggestion-diff.ts +23 -6
  134. package/corpus/templates/content/shared/suggestion-formatting.ts +44 -14
  135. package/corpus/templates/content/shared/suggestion-text.ts +20 -5
  136. package/corpus/templates/design/.agents/skills/design-templates/SKILL.md +5 -1
  137. package/corpus/templates/design/.generated/bridge/shader-runtime.generated.ts +25 -4
  138. package/corpus/templates/design/CHANGELOG.md +5 -0
  139. package/corpus/templates/design/actions/get-getting-started.ts +53 -0
  140. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +10 -0
  141. package/corpus/templates/design/app/components/design/bridge/native-shader-runtime.bridge.ts +132 -79
  142. package/corpus/templates/design/app/components/design/bridge/native-source-provider.ts +268 -130
  143. package/corpus/templates/design/app/components/design/bridge/native-worker-scene-host.ts +122 -42
  144. package/corpus/templates/design/app/components/design/bridge/shader-runtime.bridge.ts +41 -4
  145. package/corpus/templates/design/app/components/design/design-canvas/text-content-change-result.ts +46 -12
  146. package/corpus/templates/design/app/components/home/DesignHomeLibrary.tsx +15 -8
  147. package/corpus/templates/design/app/components/layout/GettingStarted.tsx +50 -0
  148. package/corpus/templates/design/app/components/layout/Sidebar.tsx +3 -0
  149. package/corpus/templates/design/app/components/templates/DesignTemplateGrid.tsx +163 -0
  150. package/corpus/templates/design/app/components/templates/DesignTemplateLibrary.tsx +65 -78
  151. package/corpus/templates/design/app/components/templates/TemplatePreview.tsx +6 -2
  152. package/corpus/templates/design/app/global.css +6 -0
  153. package/corpus/templates/design/app/i18n/en-US.ts +17 -0
  154. package/corpus/templates/design/app/lib/canvas-load-outcome.ts +147 -0
  155. package/corpus/templates/design/app/lib/canvas-outcome-tracking.ts +531 -0
  156. package/corpus/templates/design/app/lib/canvas-style-kind.ts +50 -0
  157. package/corpus/templates/design/app/pages/DesignSystems.tsx +1 -1
  158. package/corpus/templates/design/app/pages/Index.tsx +28 -67
  159. package/corpus/templates/design/app/pages/design-editor/commands/commit-visual-styles.ts +86 -29
  160. package/corpus/templates/design/app/pages/design-editor/commands/save-file-content.ts +38 -3
  161. package/corpus/templates/design/app/pages/design-editor/domains/use-editor-core.ts +9 -0
  162. package/corpus/templates/design/app/pages/design-editor/domains/use-editor-files-and-saving.ts +2 -0
  163. package/corpus/templates/design/changelog/2026-10-16-design-pages-use-localized-visibility-badges-and-a-shared-ow.md +6 -0
  164. package/corpus/templates/design/changelog/2026-10-21-a-getting-started-checklist-at-the-bottom-of-the-sidebar-tra.md +6 -0
  165. package/corpus/templates/design/changelog/2026-10-21-templates-now-open-a-read-only-preview-from-the-card-and-the.md +6 -0
  166. package/corpus/templates/design/changelog/2026-10-22-collapsed-shader-layers-no-longer-interrupt-other-shaders-on.md +6 -0
  167. package/corpus/templates/design/changelog/2026-10-22-shader-fills-keep-rendering-when-other-screen-content-cannot.md +6 -0
  168. package/corpus/templates/design/scripts/design-export-validation/export-baseline.json +24 -0
  169. package/corpus/templates/design/server/lib/getting-started.ts +267 -0
  170. package/corpus/templates/design/server/plugins/getting-started.ts +5 -0
  171. package/corpus/templates/design/shared/design-template-presets/analytics-dashboard.ts +127 -0
  172. package/corpus/templates/design/shared/design-template-presets/design-studio.ts +98 -0
  173. package/corpus/templates/design/shared/design-template-presets/messaging-app.ts +101 -0
  174. package/corpus/templates/design/shared/design-template-presets/project-tasks.ts +120 -0
  175. package/corpus/templates/design/shared/design-template-presets.ts +8 -0
  176. package/corpus/templates/dispatch/CHANGELOG.md +4 -0
  177. package/corpus/templates/dispatch/app/i18n-data.ts +75 -0
  178. package/corpus/templates/dispatch/changelog/2026-10-21-owned-and-shared-chats-appear-in-separate-history-sections.md +6 -0
  179. package/corpus/templates/factory/app/components/layout/Sidebar.tsx +34 -100
  180. package/corpus/templates/forms/app/pages/FormsListPage.tsx +1 -1
  181. package/corpus/templates/forms/server/plugins/agent-chat.ts +1 -1
  182. package/corpus/templates/plan/CHANGELOG.md +6 -0
  183. package/corpus/templates/plan/app/components/layout/Sidebar.tsx +34 -109
  184. package/corpus/templates/plan/changelog/2026-10-16-owned-and-shared-chats-appear-in-separate-history-sections.md +6 -0
  185. package/corpus/templates/slides/CHANGELOG.md +7 -0
  186. package/corpus/templates/slides/actions/delete-deck.ts +7 -0
  187. package/corpus/templates/slides/app/components/deck/DeckCard.tsx +1 -1
  188. package/corpus/templates/slides/app/components/design-system/DesignSystemCard.tsx +1 -1
  189. package/corpus/templates/slides/app/pages/Index.tsx +7 -2
  190. package/corpus/templates/slides/changelog/2026-10-16-deck-lists-use-the-shared-ownership-filter-and-visibility-ba.md +6 -0
  191. package/corpus/templates/slides/changelog/2026-10-21-connected-deck-deletion-requires-approval.md +6 -0
  192. package/corpus/templates/slides/scripts/edit-fidelity/client-action.ts +61 -0
  193. package/corpus/templates/slides/scripts/edit-fidelity/run.ts +10 -0
  194. package/package.json +1 -1
  195. package/corpus/templates/chat/app/lib/sidebar-thread-state.ts +0 -29
  196. package/corpus/templates/slides/app/components/deck/DeckFilterMenu.tsx +0 -83
@@ -1,11 +1,16 @@
1
1
  import { trackEvent } from "@agent-native/core/client/analytics";
2
2
  import { appPath } from "@agent-native/core/client/api-path";
3
- import {
4
- useActionMutation,
5
- useActionQuery,
6
- useSession,
7
- } from "@agent-native/core/client/hooks";
3
+ import { useActionMutation, useSession } from "@agent-native/core/client/hooks";
8
4
  import { useT } from "@agent-native/core/client/i18n";
5
+ import {
6
+ useShareButtonController,
7
+ type ShareButtonController,
8
+ } from "@agent-native/core/client/sharing/useShareButtonController";
9
+ import {
10
+ ShareGeneralAccessRow,
11
+ SharePeopleInvite,
12
+ SharePeopleList,
13
+ } from "@agent-native/toolkit/app/sharing";
9
14
  import { writeClipboardText } from "@agent-native/toolkit/clipboard";
10
15
  import {
11
16
  AgentDestinationActions,
@@ -41,14 +46,8 @@ import {
41
46
  } from "@/components/agent-destination-logos";
42
47
  import {
43
48
  CopyButton,
44
- GeneralAccessSelect,
45
- InvitePeopleField,
46
- PeopleAccessSection,
47
49
  ShareSectionLabel,
48
50
  nestedLayerDismissGuards,
49
- useResourceVisibilityMutation,
50
- type SharesQuery,
51
- type SharesResponse,
52
51
  type Visibility,
53
52
  } from "@/components/sharing/share-ui";
54
53
  import { Button } from "@/components/ui/button";
@@ -326,19 +325,22 @@ function ShareRecordingContent({
326
325
  setShareMode("people");
327
326
  }, [recordingId]);
328
327
 
329
- const sharesQuery = useActionQuery<SharesResponse>(
330
- "list-resource-shares",
331
- { resourceType: "recording", resourceId: recordingId },
332
- { enabled: !viewerReshareOnly },
333
- );
334
-
335
- const data = viewerReshareOnly ? undefined : sharesQuery.data;
328
+ const controller = useShareButtonController({
329
+ resourceType: "recording",
330
+ resourceId: recordingId,
331
+ defaultOpen: !viewerReshareOnly,
332
+ shareUrl: absoluteAppUrl(`/r/${recordingId}`),
333
+ });
334
+ const sharesQuery = controller.sharesQuery;
335
+ const data = viewerReshareOnly ? undefined : controller.data;
336
336
  const role = data?.role ?? initialRole;
337
- const canManage = role === "owner" || role === "admin";
337
+ const canManage = !viewerReshareOnly && controller.canManage;
338
338
  const canViewShares =
339
339
  role === "owner" || role === "admin" || role === "editor";
340
340
  const visibility =
341
- (data?.visibility as Visibility | null | undefined) ??
341
+ (viewerReshareOnly
342
+ ? null
343
+ : (controller.visibilityOverride ?? data?.visibility)) ??
342
344
  initialVisibility ??
343
345
  null;
344
346
  const canEmbed = canViewShares || visibility === "public";
@@ -355,9 +357,8 @@ function ShareRecordingContent({
355
357
  ownerViaId,
356
358
  );
357
359
 
358
- const { setResourceVisibility, isPending: visibilityMutationPending } =
359
- useResourceVisibilityMutation("recording", recordingId, sharesQuery);
360
- const visibilityPending = visibilityMutationPending || sharesQuery.isLoading;
360
+ const visibilityPending =
361
+ controller.visibilityTransition.submitting || sharesQuery.isLoading;
361
362
  const sharesLoaded = visibility !== null;
362
363
  const viewTitle =
363
364
  view === "social"
@@ -368,11 +369,8 @@ function ShareRecordingContent({
368
369
  const peopleTab = (
369
370
  <PeopleTab
370
371
  recordingId={recordingId}
371
- sharesQuery={sharesQuery}
372
+ controller={controller}
372
373
  visibility={visibility}
373
- visibilityPending={visibilityPending}
374
- onVisibilityChange={setResourceVisibility}
375
- canManage={canManage}
376
374
  hasPassword={passwordProtected}
377
375
  expiresAt={currentExpiry}
378
376
  onPasswordChange={setPasswordProtected}
@@ -462,7 +460,7 @@ function ShareRecordingContent({
462
460
  ) : (
463
461
  <ClipsEmbedConfigurator
464
462
  recordingId={recordingId}
465
- sharesQuery={sharesQuery}
463
+ controller={controller}
466
464
  visibility={visibility}
467
465
  canManage={canManage}
468
466
  ownerViaId={ownerViaId}
@@ -498,11 +496,8 @@ function ShareOptionRow({
498
496
 
499
497
  function PeopleTab({
500
498
  recordingId,
501
- sharesQuery,
499
+ controller,
502
500
  visibility,
503
- visibilityPending,
504
- onVisibilityChange,
505
- canManage,
506
501
  hasPassword,
507
502
  expiresAt,
508
503
  onPasswordChange,
@@ -514,14 +509,8 @@ function PeopleTab({
514
509
  onOpenEmbed,
515
510
  }: {
516
511
  recordingId: string;
517
- sharesQuery: SharesQuery;
512
+ controller: ShareButtonController;
518
513
  visibility: Visibility | null;
519
- visibilityPending: boolean;
520
- onVisibilityChange: (
521
- next: Visibility,
522
- options?: { onSuccess?: () => void },
523
- ) => void;
524
- canManage: boolean;
525
514
  hasPassword?: boolean;
526
515
  expiresAt: string | null;
527
516
  onPasswordChange: (hasPassword: boolean) => void;
@@ -556,55 +545,37 @@ function PeopleTab({
556
545
  return (
557
546
  <div className="flex flex-col gap-3">
558
547
  <div className="flex flex-col gap-3">
559
- {/* `share-resource` requires owner/admin, so anyone else would only
560
- get a rejected submission. */}
561
- {canManage ? (
562
- <InvitePeopleField
563
- resourceType="recording"
564
- resourceId={recordingId}
565
- resourceUrl={absoluteAppUrl(`/r/${recordingId}`)}
566
- sharesQuery={sharesQuery}
567
- onError={(err) =>
568
- toast.error(
569
- err instanceof Error
570
- ? err.message
571
- : t("clipsFinalRaw.inviteFailed"),
572
- )
573
- }
574
- />
575
- ) : null}
548
+ <SharePeopleInvite
549
+ controller={controller}
550
+ roleCopy={{
551
+ commenter: {
552
+ label: t("shareUi.recordingCommenter.label"),
553
+ description: t("shareUi.recordingCommenter.description"),
554
+ },
555
+ }}
556
+ />
576
557
 
577
558
  {canViewShares ? (
578
559
  visibility ? (
579
560
  <div className="flex flex-col gap-1.5">
580
561
  <ShareSectionLabel>{t("shareUi.whoHasAccess")}</ShareSectionLabel>
581
562
  <div className="flex flex-col gap-0.5">
582
- <PeopleAccessSection
583
- resourceType="recording"
584
- resourceId={recordingId}
585
- sharesQuery={sharesQuery}
586
- canManage={canManage}
587
- roleCopy={{
588
- commenter: {
589
- label: t("shareUi.recordingCommenter.label"),
590
- description: t("shareUi.recordingCommenter.description"),
591
- },
592
- }}
593
- onError={(err, action) =>
594
- toast.error(
595
- err instanceof Error
596
- ? err.message
597
- : action === "permission"
598
- ? t("clipsFinalRaw.permissionUpdateFailed")
599
- : t("clipsFinalRaw.removePersonFailed"),
600
- )
601
- }
602
- />
603
- <GeneralAccessSelect
604
- visibility={visibility}
605
- canManage={canManage}
606
- isPending={visibilityPending}
607
- onChange={onVisibilityChange}
563
+ <div className="max-h-72 overflow-y-auto">
564
+ <SharePeopleList
565
+ controller={controller}
566
+ roleCopy={{
567
+ commenter: {
568
+ label: t("shareUi.recordingCommenter.label"),
569
+ description: t(
570
+ "shareUi.recordingCommenter.description",
571
+ ),
572
+ },
573
+ }}
574
+ />
575
+ </div>
576
+ <ShareGeneralAccessRow
577
+ controller={{ ...controller, visibility }}
578
+ sentenceTrigger
608
579
  />
609
580
  <RecordingAccessControls
610
581
  recordingId={recordingId}
@@ -1133,13 +1104,13 @@ function SocialTab({
1133
1104
 
1134
1105
  function ClipsEmbedConfigurator({
1135
1106
  recordingId,
1136
- sharesQuery,
1107
+ controller,
1137
1108
  visibility,
1138
1109
  canManage,
1139
1110
  ownerViaId,
1140
1111
  }: {
1141
1112
  recordingId: string;
1142
- sharesQuery: SharesQuery;
1113
+ controller: ShareButtonController;
1143
1114
  visibility: Visibility | null;
1144
1115
  canManage: boolean;
1145
1116
  ownerViaId?: string;
@@ -1156,12 +1127,8 @@ function ClipsEmbedConfigurator({
1156
1127
  const visibilityLabel = visibility
1157
1128
  ? t(`shareUi.visibility.${visibility}.label`)
1158
1129
  : "";
1159
- const { setResourceVisibility, isPending } = useResourceVisibilityMutation(
1160
- "recording",
1161
- recordingId,
1162
- sharesQuery,
1163
- );
1164
- const makePublic = () => setResourceVisibility("public");
1130
+ const isPending = controller.visibilityTransition.submitting;
1131
+ const makePublic = () => controller.handleVisibility("public");
1165
1132
 
1166
1133
  const src = useMemo(() => {
1167
1134
  const params: string[] = [];
@@ -1219,6 +1186,12 @@ function ClipsEmbedConfigurator({
1219
1186
  </div>
1220
1187
  ) : null}
1221
1188
 
1189
+ {controller.shareError ? (
1190
+ <div role="alert" className="text-xs text-destructive">
1191
+ {controller.shareError}
1192
+ </div>
1193
+ ) : null}
1194
+
1222
1195
  <CopyButton value={code} className="w-full" variant="default">
1223
1196
  {t("shareDialog.copyEmbedCode")}
1224
1197
  </CopyButton>
@@ -0,0 +1,180 @@
1
+ import type { ShareButtonSharesResponse } from "@agent-native/core/client/sharing/useShareButtonController";
2
+ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
3
+ import React, { act, type ReactNode } from "react";
4
+ import { createRoot } from "react-dom/client";
5
+ import { afterEach, beforeEach, expect, vi } from "vitest";
6
+
7
+ import { TooltipProvider } from "@/components/ui/tooltip";
8
+
9
+ const mocks = vi.hoisted(() => ({
10
+ data: undefined as ShareButtonSharesResponse | undefined,
11
+ query: vi.fn(),
12
+ refetch: vi.fn(async () => undefined),
13
+ visibility: vi.fn(),
14
+ share: vi.fn(),
15
+ unshare: vi.fn(),
16
+ update: vi.fn(),
17
+ createLink: vi.fn(async () => ({
18
+ contextUrl: "https://clips.example.test/scoped-agent",
19
+ })),
20
+ clipboard: vi.fn(async (_value: string) => true),
21
+ track: vi.fn(),
22
+ }));
23
+ function mutation(name: string) {
24
+ if (name === "set-resource-visibility")
25
+ return { mutateAsync: mocks.visibility };
26
+ if (name === "share-resource") return { mutate: mocks.share };
27
+ if (name === "unshare-resource") return { mutate: mocks.unshare };
28
+ if (name.includes("agent-link") || name === "create-agent-resource-link")
29
+ return { mutateAsync: mocks.createLink };
30
+ return { mutate: mocks.update };
31
+ }
32
+ vi.mock("@agent-native/core/client/use-action", () => ({
33
+ useActionQuery: (...args: unknown[]) => {
34
+ mocks.query(...args);
35
+ return { data: mocks.data, refetch: mocks.refetch, isLoading: false };
36
+ },
37
+ useActionMutation: (name: string) => mutation(name),
38
+ }));
39
+ vi.mock("@agent-native/core/client/hooks", () => ({
40
+ useActionMutation: (name: string) => mutation(name),
41
+ useSession: () => ({ session: { userId: "test-owner" } }),
42
+ }));
43
+ vi.mock("@agent-native/core/client/org/hooks", () => ({
44
+ useOrg: () => ({ data: null }),
45
+ }));
46
+ vi.mock("@agent-native/core/client/analytics", () => ({
47
+ trackEvent: mocks.track,
48
+ }));
49
+ vi.mock("@agent-native/core/client/i18n", () => ({
50
+ useT: () => (key: string, options?: { defaultValue?: string }) =>
51
+ options?.defaultValue ?? key,
52
+ }));
53
+ vi.mock("@agent-native/toolkit/clipboard", () => ({
54
+ writeClipboardText: mocks.clipboard,
55
+ }));
56
+ vi.mock("@/components/ui/dialog", () => ({
57
+ Dialog: ({ open, children }: { open: boolean; children: ReactNode }) =>
58
+ open ? children : null,
59
+ DialogContent: ({ children }: { children: ReactNode }) => (
60
+ <div>{children}</div>
61
+ ),
62
+ DialogTitle: ({ children }: { children: ReactNode }) => <h2>{children}</h2>,
63
+ }));
64
+ vi.mock("@/components/ui/popover", () => ({
65
+ Popover: ({ children }: { children: ReactNode }) => children,
66
+ PopoverContent: ({ children }: { children: ReactNode }) => (
67
+ <div>{children}</div>
68
+ ),
69
+ PopoverTrigger: ({ children }: { children: ReactNode }) => children,
70
+ PopoverAnchor: ({ children }: { children: ReactNode }) => children,
71
+ }));
72
+
73
+ export function shareTestHarness() {
74
+ let root: ReturnType<typeof createRoot>;
75
+ let container: HTMLDivElement;
76
+ let client: QueryClient;
77
+ beforeEach(() => {
78
+ vi.clearAllMocks();
79
+ vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true);
80
+ vi.stubGlobal(
81
+ "fetch",
82
+ vi.fn(async () => ({ ok: true, json: async () => ({ members: [] }) })),
83
+ );
84
+ Element.prototype.scrollIntoView = vi.fn();
85
+ Element.prototype.hasPointerCapture = vi.fn(() => false);
86
+ Element.prototype.setPointerCapture = vi.fn();
87
+ Element.prototype.releasePointerCapture = vi.fn();
88
+ mocks.data = {
89
+ ownerEmail: "owner@example.test",
90
+ orgId: "org-test",
91
+ role: "owner",
92
+ visibility: "private",
93
+ shares: [
94
+ {
95
+ id: "share-test",
96
+ principalType: "user",
97
+ principalId: "viewer@example.test",
98
+ role: "viewer",
99
+ },
100
+ ],
101
+ };
102
+ mocks.visibility
103
+ .mockReset()
104
+ .mockImplementation(async ({ visibility }: { visibility: string }) => ({
105
+ visibility,
106
+ }));
107
+ mocks.clipboard.mockResolvedValue(true);
108
+ client = new QueryClient({
109
+ defaultOptions: {
110
+ queries: { retry: false },
111
+ mutations: { retry: false },
112
+ },
113
+ });
114
+ container = document.createElement("div");
115
+ document.body.appendChild(container);
116
+ root = createRoot(container);
117
+ });
118
+ afterEach(() => {
119
+ act(() => root.unmount());
120
+ client.clear();
121
+ container.remove();
122
+ vi.unstubAllGlobals();
123
+ });
124
+ return {
125
+ async render(children: ReactNode) {
126
+ await act(async () =>
127
+ root.render(
128
+ <QueryClientProvider client={client}>
129
+ <TooltipProvider>{children}</TooltipProvider>
130
+ </QueryClientProvider>,
131
+ ),
132
+ );
133
+ },
134
+ get client() {
135
+ return client;
136
+ },
137
+ };
138
+ }
139
+ export function button(label: string, scope: ParentNode = document.body) {
140
+ const result = Array.from(
141
+ scope.querySelectorAll<HTMLButtonElement>("button"),
142
+ ).find(
143
+ (el) =>
144
+ el.textContent?.trim() === label ||
145
+ el.getAttribute("aria-label") === label,
146
+ );
147
+ expect(result, `button ${label}`).toBeDefined();
148
+ return result!;
149
+ }
150
+ export async function click(element: HTMLElement) {
151
+ await act(async () => {
152
+ element.dispatchEvent(
153
+ new MouseEvent("mousedown", { bubbles: true, button: 0 }),
154
+ );
155
+ element.click();
156
+ });
157
+ }
158
+ export async function input(element: HTMLInputElement, value: string) {
159
+ await act(async () => {
160
+ Object.getOwnPropertyDescriptor(
161
+ HTMLInputElement.prototype,
162
+ "value",
163
+ )!.set!.call(element, value);
164
+ element.dispatchEvent(new Event("input", { bubbles: true }));
165
+ });
166
+ }
167
+ export async function select(trigger: HTMLElement, label: string) {
168
+ await act(async () =>
169
+ trigger.dispatchEvent(
170
+ new KeyboardEvent("keydown", { key: "ArrowDown", bubbles: true }),
171
+ ),
172
+ );
173
+ const option = Array.from(
174
+ document.querySelectorAll<HTMLElement>('[role="option"]'),
175
+ ).find((el) => el.textContent?.includes(label));
176
+ expect(option, `option ${label}`).toBeDefined();
177
+ await click(option!);
178
+ }
179
+
180
+ export { mocks };