@agent-native/toolkit 0.211.0 → 0.212.0-nightly-20261021010049

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 (143) hide show
  1. package/agent-native.eject.json +1 -0
  2. package/dist/app/i18n/catalogs/ar-SA.d.ts.map +1 -1
  3. package/dist/app/i18n/catalogs/ar-SA.js +23 -0
  4. package/dist/app/i18n/catalogs/ar-SA.js.map +1 -1
  5. package/dist/app/i18n/catalogs/de-DE.d.ts.map +1 -1
  6. package/dist/app/i18n/catalogs/de-DE.js +23 -0
  7. package/dist/app/i18n/catalogs/de-DE.js.map +1 -1
  8. package/dist/app/i18n/catalogs/en-US.d.ts +23 -0
  9. package/dist/app/i18n/catalogs/en-US.d.ts.map +1 -1
  10. package/dist/app/i18n/catalogs/en-US.js +23 -0
  11. package/dist/app/i18n/catalogs/en-US.js.map +1 -1
  12. package/dist/app/i18n/catalogs/es-ES.d.ts.map +1 -1
  13. package/dist/app/i18n/catalogs/es-ES.js +23 -0
  14. package/dist/app/i18n/catalogs/es-ES.js.map +1 -1
  15. package/dist/app/i18n/catalogs/fr-FR.d.ts.map +1 -1
  16. package/dist/app/i18n/catalogs/fr-FR.js +23 -0
  17. package/dist/app/i18n/catalogs/fr-FR.js.map +1 -1
  18. package/dist/app/i18n/catalogs/hi-IN.d.ts.map +1 -1
  19. package/dist/app/i18n/catalogs/hi-IN.js +23 -0
  20. package/dist/app/i18n/catalogs/hi-IN.js.map +1 -1
  21. package/dist/app/i18n/catalogs/ja-JP.d.ts.map +1 -1
  22. package/dist/app/i18n/catalogs/ja-JP.js +23 -0
  23. package/dist/app/i18n/catalogs/ja-JP.js.map +1 -1
  24. package/dist/app/i18n/catalogs/ko-KR.d.ts.map +1 -1
  25. package/dist/app/i18n/catalogs/ko-KR.js +23 -0
  26. package/dist/app/i18n/catalogs/ko-KR.js.map +1 -1
  27. package/dist/app/i18n/catalogs/pt-BR.d.ts.map +1 -1
  28. package/dist/app/i18n/catalogs/pt-BR.js +23 -0
  29. package/dist/app/i18n/catalogs/pt-BR.js.map +1 -1
  30. package/dist/app/i18n/catalogs/zh-CN.d.ts.map +1 -1
  31. package/dist/app/i18n/catalogs/zh-CN.js +23 -0
  32. package/dist/app/i18n/catalogs/zh-CN.js.map +1 -1
  33. package/dist/app/i18n/catalogs/zh-TW.d.ts.map +1 -1
  34. package/dist/app/i18n/catalogs/zh-TW.js +23 -0
  35. package/dist/app/i18n/catalogs/zh-TW.js.map +1 -1
  36. package/dist/app/sharing/OwnershipFilter.d.ts +8 -0
  37. package/dist/app/sharing/OwnershipFilter.d.ts.map +1 -0
  38. package/dist/app/sharing/OwnershipFilter.js +20 -0
  39. package/dist/app/sharing/OwnershipFilter.js.map +1 -0
  40. package/dist/app/sharing/ShareButton.d.ts +43 -1
  41. package/dist/app/sharing/ShareButton.d.ts.map +1 -1
  42. package/dist/app/sharing/ShareButton.js +102 -63
  43. package/dist/app/sharing/ShareButton.js.map +1 -1
  44. package/dist/app/sharing/ShareDialog.d.ts +3 -1
  45. package/dist/app/sharing/ShareDialog.d.ts.map +1 -1
  46. package/dist/app/sharing/ShareDialog.js +18 -12
  47. package/dist/app/sharing/ShareDialog.js.map +1 -1
  48. package/dist/app/sharing/ShareVisibilityConfirmDialog.d.ts +20 -0
  49. package/dist/app/sharing/ShareVisibilityConfirmDialog.d.ts.map +1 -0
  50. package/dist/app/sharing/ShareVisibilityConfirmDialog.js +57 -0
  51. package/dist/app/sharing/ShareVisibilityConfirmDialog.js.map +1 -0
  52. package/dist/app/sharing/VisibilityBadge.d.ts +8 -1
  53. package/dist/app/sharing/VisibilityBadge.d.ts.map +1 -1
  54. package/dist/app/sharing/VisibilityBadge.js +16 -1
  55. package/dist/app/sharing/VisibilityBadge.js.map +1 -1
  56. package/dist/app/sharing/index.d.ts +2 -0
  57. package/dist/app/sharing/index.d.ts.map +1 -1
  58. package/dist/app/sharing/index.js +2 -0
  59. package/dist/app/sharing/index.js.map +1 -1
  60. package/dist/chat-history/ChatHistoryList.d.ts +19 -0
  61. package/dist/chat-history/ChatHistoryList.d.ts.map +1 -1
  62. package/dist/chat-history/ChatHistoryList.js +19 -7
  63. package/dist/chat-history/ChatHistoryList.js.map +1 -1
  64. package/dist/chat-history/ChatHistoryRail.d.ts +55 -8
  65. package/dist/chat-history/ChatHistoryRail.d.ts.map +1 -1
  66. package/dist/chat-history/ChatHistoryRail.js +33 -7
  67. package/dist/chat-history/ChatHistoryRail.js.map +1 -1
  68. package/dist/chat-history/index.d.ts +3 -3
  69. package/dist/chat-history/index.d.ts.map +1 -1
  70. package/dist/chat-history/index.js +1 -1
  71. package/dist/chat-history/index.js.map +1 -1
  72. package/dist/chat-history/useChatHistoryRailController.d.ts +44 -0
  73. package/dist/chat-history/useChatHistoryRailController.d.ts.map +1 -1
  74. package/dist/chat-history/useChatHistoryRailController.js +46 -0
  75. package/dist/chat-history/useChatHistoryRailController.js.map +1 -1
  76. package/dist/chat-history.css +29 -0
  77. package/dist/composer/TiptapComposer.d.ts +7 -0
  78. package/dist/composer/TiptapComposer.d.ts.map +1 -1
  79. package/dist/composer/TiptapComposer.js +15 -2
  80. package/dist/composer/TiptapComposer.js.map +1 -1
  81. package/dist/sharing/OwnershipFilter.d.ts +21 -0
  82. package/dist/sharing/OwnershipFilter.d.ts.map +1 -0
  83. package/dist/sharing/OwnershipFilter.js +30 -0
  84. package/dist/sharing/OwnershipFilter.js.map +1 -0
  85. package/dist/sharing/VisibilityBadge.d.ts +12 -2
  86. package/dist/sharing/VisibilityBadge.d.ts.map +1 -1
  87. package/dist/sharing/VisibilityBadge.js +13 -4
  88. package/dist/sharing/VisibilityBadge.js.map +1 -1
  89. package/dist/sharing/index.d.ts +2 -1
  90. package/dist/sharing/index.d.ts.map +1 -1
  91. package/dist/sharing/index.js +2 -1
  92. package/dist/sharing/index.js.map +1 -1
  93. package/dist/ui/alert-dialog.d.ts +2 -0
  94. package/dist/ui/alert-dialog.d.ts.map +1 -1
  95. package/dist/ui/alert-dialog.js +1 -1
  96. package/dist/ui/alert-dialog.js.map +1 -1
  97. package/dist/ui/tooltip.d.ts +6 -1
  98. package/dist/ui/tooltip.d.ts.map +1 -1
  99. package/dist/ui/tooltip.js +13 -1
  100. package/dist/ui/tooltip.js.map +1 -1
  101. package/package.json +4 -4
  102. package/src/app/i18n/catalogs/ar-SA.ts +26 -0
  103. package/src/app/i18n/catalogs/de-DE.ts +25 -0
  104. package/src/app/i18n/catalogs/en-US.ts +25 -0
  105. package/src/app/i18n/catalogs/es-ES.ts +25 -0
  106. package/src/app/i18n/catalogs/fr-FR.ts +25 -0
  107. package/src/app/i18n/catalogs/hi-IN.ts +25 -0
  108. package/src/app/i18n/catalogs/ja-JP.ts +25 -0
  109. package/src/app/i18n/catalogs/ko-KR.ts +25 -0
  110. package/src/app/i18n/catalogs/pt-BR.ts +25 -0
  111. package/src/app/i18n/catalogs/zh-CN.ts +25 -0
  112. package/src/app/i18n/catalogs/zh-TW.ts +25 -0
  113. package/src/app/sharing/OwnershipFilter.tsx +35 -0
  114. package/src/app/sharing/ShareButton.tsx +384 -259
  115. package/src/app/sharing/ShareDialog.spec.tsx +50 -0
  116. package/src/app/sharing/ShareDialog.tsx +51 -15
  117. package/src/app/sharing/ShareVisibilityConfirmDialog.spec.tsx +138 -0
  118. package/src/app/sharing/ShareVisibilityConfirmDialog.tsx +124 -0
  119. package/src/app/sharing/ShareVisibilityKeyboard.spec.tsx +244 -0
  120. package/src/app/sharing/VisibilityBadge.spec.tsx +65 -0
  121. package/src/app/sharing/VisibilityBadge.tsx +31 -3
  122. package/src/app/sharing/index.ts +2 -0
  123. package/src/app/sharing/share-visibility-mutation-lifecycle.spec.tsx +1107 -0
  124. package/src/app/sharing/useShareButtonController.spec.tsx +226 -37
  125. package/src/app/sharing/useShareDialogController.spec.tsx +357 -9
  126. package/src/chat-history/ChatHistoryList.spec.tsx +157 -0
  127. package/src/chat-history/ChatHistoryList.tsx +104 -10
  128. package/src/chat-history/ChatHistoryRail.spec.tsx +50 -1
  129. package/src/chat-history/ChatHistoryRail.tsx +177 -26
  130. package/src/chat-history/ChatHistoryRailSections.spec.tsx +410 -0
  131. package/src/chat-history/index.ts +8 -0
  132. package/src/chat-history/useChatHistoryRailController.ts +94 -0
  133. package/src/chat-history/useChatHistoryRailSectionsController.spec.tsx +133 -0
  134. package/src/chat-history.css +29 -0
  135. package/src/composer/TiptapComposer.spec.ts +19 -0
  136. package/src/composer/TiptapComposer.tsx +26 -5
  137. package/src/sharing/OwnershipFilter.spec.tsx +108 -0
  138. package/src/sharing/OwnershipFilter.tsx +103 -0
  139. package/src/sharing/VisibilityBadge.spec.ts +53 -35
  140. package/src/sharing/VisibilityBadge.tsx +23 -3
  141. package/src/sharing/index.ts +11 -0
  142. package/src/ui/alert-dialog.tsx +7 -2
  143. package/src/ui/tooltip.tsx +22 -1
@@ -226,6 +226,56 @@ describe("ShareDialog primitive normalization", () => {
226
226
  expect(onClose).toHaveBeenCalledTimes(1);
227
227
  });
228
228
 
229
+ it("keeps the Enter that picks Organization from pressing Cancel on the confirmation", async () => {
230
+ await renderDialog();
231
+ const trigger = document.querySelector<HTMLButtonElement>(
232
+ '[aria-label="share.generalAccess"]',
233
+ )!;
234
+ trigger.focus();
235
+ act(() => {
236
+ trigger.dispatchEvent(
237
+ new KeyboardEvent("keydown", {
238
+ key: "ArrowDown",
239
+ bubbles: true,
240
+ cancelable: true,
241
+ }),
242
+ );
243
+ });
244
+ const organization = Array.from(
245
+ document.querySelectorAll<HTMLElement>('[role="option"]'),
246
+ ).find((option) => option.textContent === "share.organization")!;
247
+ organization.focus();
248
+
249
+ const down = new KeyboardEvent("keydown", {
250
+ key: "Enter",
251
+ bubbles: true,
252
+ cancelable: true,
253
+ });
254
+ act(() => {
255
+ organization.dispatchEvent(down);
256
+ });
257
+ // The browser's keypress goes to whatever holds focus by then, and its
258
+ // default action presses a focused button unless it was prevented.
259
+ const target = document.activeElement ?? document.body;
260
+ if (!down.defaultPrevented) {
261
+ const keypress = new KeyboardEvent("keypress", {
262
+ key: "Enter",
263
+ bubbles: true,
264
+ cancelable: true,
265
+ });
266
+ act(() => {
267
+ target.dispatchEvent(keypress);
268
+ });
269
+ if (!keypress.defaultPrevented && target instanceof HTMLButtonElement) {
270
+ act(() => target.click());
271
+ }
272
+ }
273
+
274
+ const confirmation = document.querySelector('[role="alertdialog"]');
275
+ expect(confirmation).not.toBeNull();
276
+ expect(document.activeElement?.textContent).toBe("agentChat.common.cancel");
277
+ });
278
+
229
279
  it("closes when the overlay is pressed", async () => {
230
280
  const onClose = await renderDialog();
231
281
  const overlay = document.querySelector<HTMLElement>(
@@ -27,10 +27,11 @@ import {
27
27
  IconUsersGroup,
28
28
  IconWorld,
29
29
  } from "@tabler/icons-react";
30
- import { type ReactNode } from "react";
30
+ import { useRef, type ReactNode, type RefObject } from "react";
31
31
 
32
32
  import { AccessRequestsSection } from "./AccessRequestsSection.js";
33
33
  import { AgentShareSection } from "./AgentShareSection.js";
34
+ import { ShareVisibilityConfirmDialog } from "./ShareVisibilityConfirmDialog.js";
34
35
 
35
36
  export interface ShareDialogProps {
36
37
  open: boolean;
@@ -43,6 +44,8 @@ export interface ShareDialogProps {
43
44
  embedUrl?: string;
44
45
  embedTabContent?: ReactNode;
45
46
  linkTabExtras?: ReactNode;
47
+ /** Resource-specific text added to the organization confirmation. */
48
+ visibilityConfirmDetail?: ReactNode;
46
49
  }
47
50
 
48
51
  const BUTTON_BASE =
@@ -73,7 +76,9 @@ export function ShareDialog({
73
76
  embedUrl,
74
77
  embedTabContent,
75
78
  linkTabExtras,
79
+ visibilityConfirmDetail,
76
80
  }: ShareDialogProps) {
81
+ const visibilityRef = useRef<HTMLElement>(null);
77
82
  const controller = useShareDialogController({
78
83
  open,
79
84
  onClose,
@@ -139,6 +144,7 @@ export function ShareDialog({
139
144
  <TabsContent value="link" className="m-0">
140
145
  <LinkTab
141
146
  controller={controller}
147
+ visibilityRef={visibilityRef}
142
148
  extras={linkTabExtras}
143
149
  accessRequests={accessRequests}
144
150
  />
@@ -147,6 +153,7 @@ export function ShareDialog({
147
153
  {!controller.tabsEnabled || controller.activeTab === "invite" ? (
148
154
  <InviteTab
149
155
  controller={controller}
156
+ visibilityRef={visibilityRef}
150
157
  showVisibility={!controller.tabsEnabled}
151
158
  accessRequests={accessRequests}
152
159
  />
@@ -164,16 +171,23 @@ export function ShareDialog({
164
171
  />
165
172
  </div>
166
173
  </Tabs>
174
+ <ShareVisibilityConfirmDialog
175
+ transition={controller.visibility.transition}
176
+ detail={visibilityConfirmDetail}
177
+ returnFocusRef={visibilityRef}
178
+ />
167
179
  </DesignSystemDialog>
168
180
  );
169
181
  }
170
182
 
171
183
  function LinkTab({
172
184
  controller,
185
+ visibilityRef,
173
186
  extras,
174
187
  accessRequests,
175
188
  }: {
176
189
  controller: ShareDialogController;
190
+ visibilityRef: RefObject<HTMLElement | null>;
177
191
  extras?: ReactNode;
178
192
  accessRequests: ReactNode;
179
193
  }) {
@@ -200,7 +214,10 @@ function LinkTab({
200
214
  <Icon size={16} strokeWidth={1.75} />
201
215
  </span>
202
216
  <div className="min-w-0 flex-1">
203
- <VisibilitySelect controller={controller} />
217
+ <VisibilitySelect
218
+ controller={controller}
219
+ pickerRef={visibilityRef}
220
+ />
204
221
  <div className="sr-only text-xs text-muted-foreground">
205
222
  {controller.visibility.description}
206
223
  </div>
@@ -209,6 +226,7 @@ function LinkTab({
209
226
  </div>
210
227
  <InviteTab
211
228
  controller={controller}
229
+ visibilityRef={visibilityRef}
212
230
  showVisibility={false}
213
231
  accessRequests={accessRequests}
214
232
  />
@@ -219,10 +237,12 @@ function LinkTab({
219
237
 
220
238
  function InviteTab({
221
239
  controller,
240
+ visibilityRef,
222
241
  showVisibility,
223
242
  accessRequests,
224
243
  }: {
225
244
  controller: ShareDialogController;
245
+ visibilityRef: RefObject<HTMLElement | null>;
226
246
  showVisibility: boolean;
227
247
  accessRequests: ReactNode;
228
248
  }) {
@@ -242,7 +262,10 @@ function InviteTab({
242
262
  <Icon size={16} strokeWidth={1.75} />
243
263
  </span>
244
264
  <div className="min-w-0 flex-1">
245
- <VisibilitySelect controller={controller} />
265
+ <VisibilitySelect
266
+ controller={controller}
267
+ pickerRef={visibilityRef}
268
+ />
246
269
  <div className="sr-only text-xs text-muted-foreground">
247
270
  {controller.visibility.description}
248
271
  </div>
@@ -487,22 +510,35 @@ function RoleSelect({ controller }: { controller: ShareDialogController }) {
487
510
 
488
511
  function VisibilitySelect({
489
512
  controller,
513
+ pickerRef,
490
514
  }: {
491
515
  controller: ShareDialogController;
516
+ pickerRef: RefObject<HTMLElement | null>;
492
517
  }) {
493
518
  return (
494
- <Picker
495
- mode="select"
496
- options={controller.visibility.options}
497
- value={controller.visibility.value}
498
- onChange={(value) => {
499
- if (value === "private" || value === "org" || value === "public") {
500
- controller.visibility.set(value);
501
- }
519
+ // React events from the picker's portaled list bubble to this wrapper
520
+ // after the option has selected. Enter may open a confirmation that takes
521
+ // focus; its keypress must not land on the button there.
522
+ <div
523
+ className="contents"
524
+ onKeyDown={(event) => {
525
+ if (event.key === "Enter") event.preventDefault();
502
526
  }}
503
- disabled={controller.visibility.disabled}
504
- aria-label={controller.labels.generalAccess}
505
- className="-ms-1 w-auto"
506
- />
527
+ >
528
+ <Picker
529
+ mode="select"
530
+ options={controller.visibility.options}
531
+ value={controller.visibility.value}
532
+ onChange={(value) => {
533
+ if (value === "private" || value === "org" || value === "public") {
534
+ controller.visibility.set(value);
535
+ }
536
+ }}
537
+ disabled={controller.visibility.disabled}
538
+ aria-label={controller.labels.generalAccess}
539
+ pickerRef={pickerRef}
540
+ className="-ms-1 w-auto"
541
+ />
542
+ </div>
507
543
  );
508
544
  }
@@ -0,0 +1,138 @@
1
+ // @vitest-environment happy-dom
2
+
3
+ import type { ShareVisibilityTransition } from "@agent-native/core/client/sharing/useShareVisibilityTransition";
4
+ import React, { act } from "react";
5
+ import { createRoot, type Root } from "react-dom/client";
6
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
7
+
8
+ import { ShareVisibilityConfirmDialog } from "./ShareVisibilityConfirmDialog.js";
9
+
10
+ function makeTransition(
11
+ overrides: Partial<ShareVisibilityTransition> = {},
12
+ ): ShareVisibilityTransition {
13
+ return {
14
+ pending: { from: "private", to: "org" },
15
+ organizationName: null,
16
+ submitting: false,
17
+ error: null,
18
+ request: vi.fn(),
19
+ confirm: vi.fn(async () => undefined),
20
+ cancel: vi.fn(),
21
+ ...overrides,
22
+ };
23
+ }
24
+
25
+ describe("ShareVisibilityConfirmDialog", () => {
26
+ let container: HTMLDivElement;
27
+ let root: Root;
28
+
29
+ beforeEach(() => {
30
+ vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true);
31
+ container = document.createElement("div");
32
+ document.body.appendChild(container);
33
+ root = createRoot(container);
34
+ });
35
+
36
+ afterEach(() => {
37
+ act(() => root.unmount());
38
+ container.remove();
39
+ vi.unstubAllGlobals();
40
+ });
41
+
42
+ async function render(
43
+ transition: ShareVisibilityTransition,
44
+ props: { detail?: React.ReactNode } = {},
45
+ ) {
46
+ await act(async () => {
47
+ root.render(
48
+ <ShareVisibilityConfirmDialog transition={transition} {...props} />,
49
+ );
50
+ });
51
+ }
52
+
53
+ const dialog = () =>
54
+ document.body.querySelector<HTMLElement>('[role="alertdialog"]');
55
+ const button = (label: string) =>
56
+ Array.from(
57
+ document.body.querySelectorAll<HTMLButtonElement>(
58
+ '[role="alertdialog"] button',
59
+ ),
60
+ ).find((candidate) => candidate.textContent?.trim() === label);
61
+
62
+ it("renders nothing until a broadening change is pending", async () => {
63
+ await render(makeTransition({ pending: null }));
64
+
65
+ expect(dialog()).toBeNull();
66
+ });
67
+
68
+ it("names the organization only when it is known", async () => {
69
+ await render(makeTransition());
70
+ expect(dialog()?.textContent).toContain("Share with your organization?");
71
+ expect(dialog()?.textContent).toContain("Everyone in your organization");
72
+
73
+ await render(makeTransition({ organizationName: "Acme Co" }));
74
+ expect(dialog()?.textContent).toContain("Share with Acme Co?");
75
+ expect(dialog()?.textContent).toContain("Everyone in Acme Co");
76
+ });
77
+
78
+ it("covers every kind of content and adds resource-specific detail", async () => {
79
+ await render(makeTransition(), { detail: "This also shares the chat." });
80
+
81
+ expect(dialog()?.textContent).toContain(
82
+ "including any messages, files, and other content",
83
+ );
84
+ expect(dialog()?.textContent).toContain("This also shares the chat.");
85
+ });
86
+
87
+ it("opens with Cancel focused so Enter cannot widen access by accident", async () => {
88
+ await render(makeTransition());
89
+
90
+ expect(document.activeElement).toBe(button("Cancel"));
91
+ });
92
+
93
+ it("confirms only from the confirm button and cancels from Cancel and Escape", async () => {
94
+ const transition = makeTransition();
95
+ await render(transition);
96
+
97
+ act(() => button("Cancel")?.click());
98
+ expect(transition.cancel).toHaveBeenCalledOnce();
99
+ expect(transition.confirm).not.toHaveBeenCalled();
100
+
101
+ act(() => {
102
+ dialog()?.dispatchEvent(
103
+ new KeyboardEvent("keydown", { key: "Escape", bubbles: true }),
104
+ );
105
+ });
106
+ expect(transition.cancel).toHaveBeenCalledTimes(2);
107
+
108
+ act(() => button("Share with organization")?.click());
109
+ expect(transition.confirm).toHaveBeenCalledOnce();
110
+ });
111
+
112
+ it("shows progress, keeps the confirm button focusable, and blocks cancel", async () => {
113
+ await render(makeTransition({ submitting: true }));
114
+
115
+ const confirm = button("Sharing...");
116
+ expect(confirm?.getAttribute("aria-disabled")).toBe("true");
117
+ expect(confirm?.hasAttribute("disabled")).toBe(false);
118
+ expect(
119
+ (
120
+ document.body.querySelector(
121
+ '[role="alertdialog"] button',
122
+ ) as HTMLButtonElement | null
123
+ )?.disabled,
124
+ ).toBe(true);
125
+ });
126
+
127
+ it("shows the failure in the dialog and offers a retry", async () => {
128
+ const transition = makeTransition({ error: "Could not update sharing" });
129
+ await render(transition);
130
+
131
+ expect(
132
+ document.body.querySelector('[role="alertdialog"] [role="alert"]')
133
+ ?.textContent,
134
+ ).toBe("Could not update sharing");
135
+ act(() => button("Retry")?.click());
136
+ expect(transition.confirm).toHaveBeenCalledOnce();
137
+ });
138
+ });
@@ -0,0 +1,124 @@
1
+ import { useT } from "@agent-native/core/client/i18n";
2
+ import type { ShareVisibilityTransition } from "@agent-native/core/client/sharing/useShareVisibilityTransition";
3
+ import {
4
+ AlertDialog,
5
+ AlertDialogCancel,
6
+ AlertDialogContent,
7
+ AlertDialogDescription,
8
+ AlertDialogFooter,
9
+ AlertDialogHeader,
10
+ AlertDialogTitle,
11
+ } from "@agent-native/toolkit/ui/alert-dialog";
12
+ import { Button } from "@agent-native/toolkit/ui/button";
13
+ import type { ReactNode, RefObject } from "react";
14
+
15
+ // ShareButton's nested overlays (the picker, member suggestions) sit at
16
+ // z-[100020] over the popover (z-[2000]) and ShareDialog (z-[2010]); the
17
+ // confirmation opens from the picker, so it goes just above them.
18
+ const CONFIRM_Z_INDEX = 100030;
19
+
20
+ export interface ShareVisibilityConfirmDialogProps {
21
+ transition: ShareVisibilityTransition;
22
+ /**
23
+ * What is specific to this resource, already translated by the caller, such
24
+ * as "Everyone will also see the conversation history." Shown under the
25
+ * generic text, which already covers messages, files and other content.
26
+ */
27
+ detail?: ReactNode;
28
+ /** Gets focus back when the confirmation closes (the visibility control). */
29
+ returnFocusRef?: RefObject<HTMLElement | null>;
30
+ }
31
+
32
+ /**
33
+ * Asks before a resource becomes visible to the whole organization. Pair it
34
+ * with the `transition` of a share controller; it renders nothing until a
35
+ * broadening change is waiting, and the change is saved only by Confirm.
36
+ */
37
+ export function ShareVisibilityConfirmDialog({
38
+ transition,
39
+ detail,
40
+ returnFocusRef,
41
+ }: ShareVisibilityConfirmDialogProps) {
42
+ const t = useT();
43
+ const { pending, organizationName, submitting, error } = transition;
44
+ const named = organizationName !== null;
45
+ const values = { organization: organizationName ?? "" };
46
+ const title = named
47
+ ? t("agentChat.share.confirmOrganizationTitleNamed", {
48
+ ...values,
49
+ defaultValue: "Share with {{organization}}?",
50
+ })
51
+ : t("agentChat.share.confirmOrganizationTitle", {
52
+ defaultValue: "Share with your organization?",
53
+ });
54
+ const description = named
55
+ ? t("agentChat.share.confirmOrganizationDescriptionNamed", {
56
+ ...values,
57
+ defaultValue:
58
+ "Everyone in {{organization}}, including new members, will be able to open this and see everything in it, including any messages, files, and other content. You can change this later.",
59
+ })
60
+ : t("agentChat.share.confirmOrganizationDescription", {
61
+ defaultValue:
62
+ "Everyone in your organization, including new members, will be able to open this and see everything in it, including any messages, files, and other content. You can change this later.",
63
+ });
64
+ const actionLabel = submitting
65
+ ? t("agentChat.share.confirmOrganizationSharing", {
66
+ defaultValue: "Sharing...",
67
+ })
68
+ : error
69
+ ? t("agentChat.common.retry", { defaultValue: "Retry" })
70
+ : t("agentChat.share.confirmOrganizationAction", {
71
+ defaultValue: "Share with organization",
72
+ });
73
+
74
+ return (
75
+ <AlertDialog
76
+ open={pending !== null}
77
+ onOpenChange={(open) => {
78
+ if (!open) transition.cancel();
79
+ }}
80
+ >
81
+ <AlertDialogContent
82
+ data-agent-native-share-overlay=""
83
+ style={{ zIndex: CONFIRM_Z_INDEX + 1 }}
84
+ overlayStyle={{ zIndex: CONFIRM_Z_INDEX }}
85
+ onCloseAutoFocus={(event) => {
86
+ const target = returnFocusRef?.current;
87
+ if (!target?.isConnected) return;
88
+ event.preventDefault();
89
+ target.focus();
90
+ }}
91
+ >
92
+ <AlertDialogHeader>
93
+ <AlertDialogTitle>{title}</AlertDialogTitle>
94
+ <AlertDialogDescription>{description}</AlertDialogDescription>
95
+ {detail ? (
96
+ <div className="text-sm text-muted-foreground">{detail}</div>
97
+ ) : null}
98
+ {error ? (
99
+ <div
100
+ role="alert"
101
+ className="rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-start text-xs text-destructive"
102
+ >
103
+ {error}
104
+ </div>
105
+ ) : null}
106
+ </AlertDialogHeader>
107
+ <AlertDialogFooter>
108
+ <AlertDialogCancel disabled={submitting}>
109
+ {t("agentChat.common.cancel", { defaultValue: "Cancel" })}
110
+ </AlertDialogCancel>
111
+ <Button
112
+ type="button"
113
+ aria-disabled={submitting || undefined}
114
+ onClick={() => {
115
+ void transition.confirm();
116
+ }}
117
+ >
118
+ {actionLabel}
119
+ </Button>
120
+ </AlertDialogFooter>
121
+ </AlertDialogContent>
122
+ </AlertDialog>
123
+ );
124
+ }