@agent-native/toolkit 0.21.3 → 0.22.0-nightly-20260926111339

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 (166) hide show
  1. package/agent-native.eject.json +11 -20
  2. package/dist/agentkit.d.ts +5 -1
  3. package/dist/agentkit.d.ts.map +1 -1
  4. package/dist/agentkit.js +2 -0
  5. package/dist/agentkit.js.map +1 -1
  6. package/dist/app-shell/PromptHome.d.ts +26 -0
  7. package/dist/app-shell/PromptHome.d.ts.map +1 -0
  8. package/dist/app-shell/PromptHome.js +19 -0
  9. package/dist/app-shell/PromptHome.js.map +1 -0
  10. package/dist/app-shell/PromptHome.spec.d.ts +2 -0
  11. package/dist/app-shell/PromptHome.spec.d.ts.map +1 -0
  12. package/dist/app-shell/PromptHome.spec.js +206 -0
  13. package/dist/app-shell/PromptHome.spec.js.map +1 -0
  14. package/dist/app-shell/TemplateLibraryGrid.d.ts +48 -0
  15. package/dist/app-shell/TemplateLibraryGrid.d.ts.map +1 -0
  16. package/dist/app-shell/TemplateLibraryGrid.js +20 -0
  17. package/dist/app-shell/TemplateLibraryGrid.js.map +1 -0
  18. package/dist/app-shell/TemplatePreviewDialog.d.ts +41 -0
  19. package/dist/app-shell/TemplatePreviewDialog.d.ts.map +1 -0
  20. package/dist/app-shell/TemplatePreviewDialog.js +50 -0
  21. package/dist/app-shell/TemplatePreviewDialog.js.map +1 -0
  22. package/dist/app-shell/TemplatePreviewDialog.spec.d.ts +2 -0
  23. package/dist/app-shell/TemplatePreviewDialog.spec.d.ts.map +1 -0
  24. package/dist/app-shell/TemplatePreviewDialog.spec.js +258 -0
  25. package/dist/app-shell/TemplatePreviewDialog.spec.js.map +1 -0
  26. package/dist/app-shell/index.d.ts +3 -0
  27. package/dist/app-shell/index.d.ts.map +1 -1
  28. package/dist/app-shell/index.js +3 -0
  29. package/dist/app-shell/index.js.map +1 -1
  30. package/dist/app-shell/prompt-home.css +159 -0
  31. package/dist/composer/ComposerContextMenu.d.ts +48 -0
  32. package/dist/composer/ComposerContextMenu.d.ts.map +1 -0
  33. package/dist/composer/ComposerContextMenu.js +295 -0
  34. package/dist/composer/ComposerContextMenu.js.map +1 -0
  35. package/dist/composer/ComposerContextMenu.spec.d.ts +2 -0
  36. package/dist/composer/ComposerContextMenu.spec.d.ts.map +1 -0
  37. package/dist/composer/ComposerContextMenu.spec.js +270 -0
  38. package/dist/composer/ComposerContextMenu.spec.js.map +1 -0
  39. package/dist/composer/ComposerContextMenuSearch.d.ts +11 -0
  40. package/dist/composer/ComposerContextMenuSearch.d.ts.map +1 -0
  41. package/dist/composer/ComposerContextMenuSearch.js +42 -0
  42. package/dist/composer/ComposerContextMenuSearch.js.map +1 -0
  43. package/dist/composer/ComposerContextPicker.d.ts +7 -0
  44. package/dist/composer/ComposerContextPicker.d.ts.map +1 -0
  45. package/dist/composer/ComposerContextPicker.js +71 -0
  46. package/dist/composer/ComposerContextPicker.js.map +1 -0
  47. package/dist/composer/ComposerContextPicker.spec.d.ts +2 -0
  48. package/dist/composer/ComposerContextPicker.spec.d.ts.map +1 -0
  49. package/dist/composer/ComposerContextPicker.spec.js +454 -0
  50. package/dist/composer/ComposerContextPicker.spec.js.map +1 -0
  51. package/dist/composer/ComposerContextPickerDialog.d.ts +8 -0
  52. package/dist/composer/ComposerContextPickerDialog.d.ts.map +1 -0
  53. package/dist/composer/ComposerContextPickerDialog.js +101 -0
  54. package/dist/composer/ComposerContextPickerDialog.js.map +1 -0
  55. package/dist/composer/ComposerContextPickerDialog.spec.d.ts +2 -0
  56. package/dist/composer/ComposerContextPickerDialog.spec.d.ts.map +1 -0
  57. package/dist/composer/ComposerContextPickerDialog.spec.js +329 -0
  58. package/dist/composer/ComposerContextPickerDialog.spec.js.map +1 -0
  59. package/dist/composer/ComposerContextSearchInput.d.ts +7 -0
  60. package/dist/composer/ComposerContextSearchInput.d.ts.map +1 -0
  61. package/dist/composer/ComposerContextSearchInput.js +18 -0
  62. package/dist/composer/ComposerContextSearchInput.js.map +1 -0
  63. package/dist/composer/PromptComposer.context.spec.d.ts +2 -0
  64. package/dist/composer/PromptComposer.context.spec.d.ts.map +1 -0
  65. package/dist/composer/PromptComposer.context.spec.js +383 -0
  66. package/dist/composer/PromptComposer.context.spec.js.map +1 -0
  67. package/dist/composer/PromptComposer.d.ts +19 -5
  68. package/dist/composer/PromptComposer.d.ts.map +1 -1
  69. package/dist/composer/PromptComposer.js +30 -25
  70. package/dist/composer/PromptComposer.js.map +1 -1
  71. package/dist/composer/PromptComposer.spec.js +24 -1
  72. package/dist/composer/PromptComposer.spec.js.map +1 -1
  73. package/dist/composer/TiptapComposer.d.ts +13 -2
  74. package/dist/composer/TiptapComposer.d.ts.map +1 -1
  75. package/dist/composer/TiptapComposer.js +89 -32
  76. package/dist/composer/TiptapComposer.js.map +1 -1
  77. package/dist/composer/context-items.d.ts +12 -0
  78. package/dist/composer/context-items.d.ts.map +1 -0
  79. package/dist/composer/context-items.js +46 -0
  80. package/dist/composer/context-items.js.map +1 -0
  81. package/dist/composer/context-items.spec.d.ts +2 -0
  82. package/dist/composer/context-items.spec.d.ts.map +1 -0
  83. package/dist/composer/context-items.spec.js +43 -0
  84. package/dist/composer/context-items.spec.js.map +1 -0
  85. package/dist/composer/index.d.ts +4 -1
  86. package/dist/composer/index.d.ts.map +1 -1
  87. package/dist/composer/index.js +3 -1
  88. package/dist/composer/index.js.map +1 -1
  89. package/dist/composer/runtime-adapters.d.ts +6 -1
  90. package/dist/composer/runtime-adapters.d.ts.map +1 -1
  91. package/dist/composer/runtime-adapters.js.map +1 -1
  92. package/dist/composer/useComposerContextPicker.d.ts +111 -0
  93. package/dist/composer/useComposerContextPicker.d.ts.map +1 -0
  94. package/dist/composer/useComposerContextPicker.js +299 -0
  95. package/dist/composer/useComposerContextPicker.js.map +1 -0
  96. package/dist/design-system/default-adapter.d.ts.map +1 -1
  97. package/dist/design-system/default-adapter.js +13 -7
  98. package/dist/design-system/default-adapter.js.map +1 -1
  99. package/dist/design-system/default-adapter.spec.js +13 -0
  100. package/dist/design-system/default-adapter.spec.js.map +1 -1
  101. package/dist/design-system/types.d.ts +4 -1
  102. package/dist/design-system/types.d.ts.map +1 -1
  103. package/dist/index.d.ts +0 -1
  104. package/dist/index.d.ts.map +1 -1
  105. package/dist/index.js +0 -1
  106. package/dist/index.js.map +1 -1
  107. package/dist/styles.css +1 -0
  108. package/dist/ui/command.d.ts +8 -2
  109. package/dist/ui/command.d.ts.map +1 -1
  110. package/dist/ui/command.js +9 -2
  111. package/dist/ui/command.js.map +1 -1
  112. package/dist/ui/command.spec.js +20 -1
  113. package/dist/ui/command.spec.js.map +1 -1
  114. package/package.json +2 -15
  115. package/src/agentkit.ts +26 -0
  116. package/src/app-shell/PromptHome.spec.tsx +404 -0
  117. package/src/app-shell/PromptHome.tsx +111 -0
  118. package/src/app-shell/TemplateLibraryGrid.tsx +212 -0
  119. package/src/app-shell/TemplatePreviewDialog.spec.tsx +473 -0
  120. package/src/app-shell/TemplatePreviewDialog.tsx +224 -0
  121. package/src/app-shell/index.ts +3 -0
  122. package/src/app-shell/prompt-home.css +159 -0
  123. package/src/composer/ComposerContextMenu.spec.tsx +343 -0
  124. package/src/composer/ComposerContextMenu.tsx +610 -0
  125. package/src/composer/ComposerContextMenuSearch.tsx +69 -0
  126. package/src/composer/ComposerContextPicker.spec.tsx +520 -0
  127. package/src/composer/ComposerContextPicker.tsx +280 -0
  128. package/src/composer/ComposerContextPickerDialog.spec.tsx +405 -0
  129. package/src/composer/ComposerContextPickerDialog.tsx +274 -0
  130. package/src/composer/ComposerContextSearchInput.tsx +48 -0
  131. package/src/composer/PromptComposer.context.spec.tsx +577 -0
  132. package/src/composer/PromptComposer.spec.ts +24 -1
  133. package/src/composer/PromptComposer.tsx +60 -26
  134. package/src/composer/TiptapComposer.tsx +198 -48
  135. package/src/composer/context-items.spec.ts +68 -0
  136. package/src/composer/context-items.ts +72 -0
  137. package/src/composer/index.ts +27 -0
  138. package/src/composer/runtime-adapters.tsx +6 -1
  139. package/src/composer/useComposerContextPicker.ts +441 -0
  140. package/src/design-system/default-adapter.spec.tsx +23 -0
  141. package/src/design-system/default-adapter.tsx +52 -23
  142. package/src/design-system/types.ts +4 -1
  143. package/src/index.ts +0 -1
  144. package/src/styles.css +1 -0
  145. package/src/ui/command.spec.tsx +33 -1
  146. package/src/ui/command.tsx +52 -9
  147. package/dist/marketing/MarketingHome.d.ts +0 -36
  148. package/dist/marketing/MarketingHome.d.ts.map +0 -1
  149. package/dist/marketing/MarketingHome.js +0 -28
  150. package/dist/marketing/MarketingHome.js.map +0 -1
  151. package/dist/marketing/MarketingHome.spec.d.ts +0 -2
  152. package/dist/marketing/MarketingHome.spec.d.ts.map +0 -1
  153. package/dist/marketing/MarketingHome.spec.js +0 -44
  154. package/dist/marketing/MarketingHome.spec.js.map +0 -1
  155. package/dist/marketing/Starfield.d.ts +0 -8
  156. package/dist/marketing/Starfield.d.ts.map +0 -1
  157. package/dist/marketing/Starfield.js +0 -349
  158. package/dist/marketing/Starfield.js.map +0 -1
  159. package/dist/marketing/index.d.ts +0 -3
  160. package/dist/marketing/index.d.ts.map +0 -1
  161. package/dist/marketing/index.js +0 -3
  162. package/dist/marketing/index.js.map +0 -1
  163. package/src/marketing/MarketingHome.spec.tsx +0 -75
  164. package/src/marketing/MarketingHome.tsx +0 -183
  165. package/src/marketing/Starfield.tsx +0 -403
  166. package/src/marketing/index.ts +0 -6
@@ -0,0 +1,343 @@
1
+ // @vitest-environment happy-dom
2
+
3
+ import { act } from "react";
4
+ import { createRoot } from "react-dom/client";
5
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
6
+
7
+ import { TooltipProvider } from "../ui/tooltip.js";
8
+ import {
9
+ ComposerContextMenu,
10
+ getComposerContextMenuEntries,
11
+ type ComposerContextMenuItem,
12
+ type ComposerContextPageControls,
13
+ } from "./ComposerContextMenu.js";
14
+ import { snapshotComposerContextItems } from "./context-items.js";
15
+
16
+ const items: ComposerContextMenuItem[] = [
17
+ {
18
+ id: "documents",
19
+ label: "Documents",
20
+ searchPlaceholder: "Search documents…",
21
+ children: [
22
+ {
23
+ id: "brief",
24
+ label: "Project brief",
25
+ keywords: ["launch"],
26
+ onSelect() {},
27
+ },
28
+ {
29
+ id: "archive",
30
+ label: "Archive",
31
+ children: [{ id: "notes", label: "Meeting notes", onSelect() {} }],
32
+ },
33
+ ],
34
+ },
35
+ {
36
+ id: "library",
37
+ label: "Library",
38
+ children: [{ id: "reference", label: "Launch reference", onSelect() {} }],
39
+ },
40
+ ];
41
+
42
+ describe("composer context contracts", () => {
43
+ it("retains scoped public search and inherited disabled state", () => {
44
+ expect(
45
+ getComposerContextMenuEntries(items, [], "").map((item) => item.id),
46
+ ).toEqual(["documents", "library"]);
47
+ expect(
48
+ getComposerContextMenuEntries(items, [], "launch").map((item) => item.id),
49
+ ).toEqual(["brief", "reference"]);
50
+ expect(
51
+ getComposerContextMenuEntries(items, ["documents"], "launch").map(
52
+ (item) => item.id,
53
+ ),
54
+ ).toEqual(["brief"]);
55
+ expect(
56
+ getComposerContextMenuEntries(
57
+ [{ ...items[0], disabled: true }],
58
+ [],
59
+ "brief",
60
+ )[0].disabled,
61
+ ).toBe(true);
62
+ expect(getComposerContextMenuEntries(items, ["missing"], "")).toEqual([]);
63
+ });
64
+ it("copies and freezes selected context and rejects unready items", () => {
65
+ const source = [{ key: "brief", title: "Brief", context: "original" }];
66
+ const snapshot = snapshotComposerContextItems(source);
67
+ source[0].context = "changed";
68
+ expect(snapshot[0].context).toBe("original");
69
+ expect(Object.isFrozen(snapshot)).toBe(true);
70
+ expect(Object.isFrozen(snapshot[0])).toBe(true);
71
+ expect(snapshotComposerContextItems(undefined)).toBeUndefined();
72
+ expect(snapshotComposerContextItems([])).toEqual([]);
73
+ for (const status of ["pending", "error"] as const)
74
+ expect(() =>
75
+ snapshotComposerContextItems([
76
+ { key: "bad", title: "Bad", context: "", status },
77
+ ]),
78
+ ).toThrow("not ready");
79
+ });
80
+ });
81
+
82
+ describe("connected composer menus", () => {
83
+ let container: HTMLDivElement;
84
+ let root: ReturnType<typeof createRoot>;
85
+ beforeEach(() => {
86
+ vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true);
87
+ vi.spyOn(HTMLElement.prototype, "scrollIntoView").mockImplementation(
88
+ () => {},
89
+ );
90
+ container = document.createElement("div");
91
+ document.body.append(container);
92
+ root = createRoot(container);
93
+ });
94
+ afterEach(async () => {
95
+ await act(async () => root.unmount());
96
+ container.remove();
97
+ vi.restoreAllMocks();
98
+ vi.unstubAllGlobals();
99
+ });
100
+ async function render(
101
+ entries = items,
102
+ props: Partial<React.ComponentProps<typeof ComposerContextMenu>> = {},
103
+ ) {
104
+ await act(async () =>
105
+ root.render(
106
+ <TooltipProvider>
107
+ <input aria-label="Prompt draft" defaultValue="Keep this draft" />
108
+ <ComposerContextMenu items={entries} {...props} />
109
+ </TooltipProvider>,
110
+ ),
111
+ );
112
+ }
113
+ const menus = () =>
114
+ Array.from(document.querySelectorAll<HTMLElement>('[role="menu"]'));
115
+ const row = (label: string) => {
116
+ const found = Array.from(
117
+ document.querySelectorAll<HTMLElement>('[role^="menuitem"]'),
118
+ ).find((element) => element.textContent === label);
119
+ expect(found, label).toBeDefined();
120
+ return found!;
121
+ };
122
+ async function key(target: HTMLElement, key: string) {
123
+ await act(async () => {
124
+ target.focus();
125
+ target.dispatchEvent(
126
+ new KeyboardEvent("keydown", { key, bubbles: true, cancelable: true }),
127
+ );
128
+ await new Promise((resolve) => setTimeout(resolve, 0));
129
+ });
130
+ }
131
+ const open = () =>
132
+ key(
133
+ container.querySelector('button[aria-label="Add context"]')!,
134
+ "ArrowDown",
135
+ );
136
+ async function click(label: string) {
137
+ await act(async () => row(label).click());
138
+ }
139
+ async function search(placeholder: string, value: string) {
140
+ const input = document.querySelector<HTMLInputElement>(
141
+ `input[placeholder="${placeholder}"]`,
142
+ )!;
143
+ await act(async () => {
144
+ Object.getOwnPropertyDescriptor(
145
+ HTMLInputElement.prototype,
146
+ "value",
147
+ )!.set!.call(input, value);
148
+ input.dispatchEvent(new Event("input", { bubbles: true }));
149
+ });
150
+ }
151
+ it("keeps all anchored panels visible with category search overrides and w64", async () => {
152
+ await render(items, { addAttachment: vi.fn() });
153
+ await open();
154
+ expect(
155
+ document.querySelector('input[placeholder="Search…"]'),
156
+ ).not.toBeNull();
157
+ expect(row("Upload File").querySelector("svg")?.getAttribute("width")).toBe(
158
+ "16",
159
+ );
160
+ await click("Add context");
161
+ await click("Documents");
162
+ await click("Archive");
163
+ expect(menus()).toHaveLength(4);
164
+ expect(menus().every((menu) => menu.classList.contains("w-64"))).toBe(true);
165
+ expect(document.querySelectorAll('[role="searchbox"]')).toHaveLength(4);
166
+ expect(
167
+ document.querySelector('input[placeholder="Search documents…"]'),
168
+ ).not.toBeNull();
169
+ expect(row("Documents").getAttribute("aria-expanded")).toBe("true");
170
+ expect(row("Meeting notes").getAttribute("role")).toBe("menuitem");
171
+ });
172
+ it("filters root and category search without flattening the hierarchy", async () => {
173
+ const select = vi.fn();
174
+ await render(
175
+ [
176
+ {
177
+ id: "documents",
178
+ label: "Documents",
179
+ searchPlaceholder: "Search documents…",
180
+ children: [
181
+ { id: "brief", label: "Project brief", onSelect: select },
182
+ { id: "notes", label: "Notes", onSelect() {} },
183
+ ],
184
+ },
185
+ items[1],
186
+ ],
187
+ { addAttachment: vi.fn() },
188
+ );
189
+ await open();
190
+ await search("Search…", "brief");
191
+ expect(menus()[0].textContent).not.toContain("Upload File");
192
+ await click("Add context");
193
+ await search("Search context…", "documents");
194
+ expect(menus()[1].textContent).not.toContain("Library");
195
+ await click("Documents");
196
+ await search("Search documents…", "brief");
197
+ expect(menus()[2].textContent).not.toContain("Notes");
198
+ await click("Project brief");
199
+ expect(select).toHaveBeenCalledOnce();
200
+ expect(menus()).toHaveLength(0);
201
+ });
202
+ it("keeps typing out of typeahead and supports native ArrowRight, Left and Escape", async () => {
203
+ await render();
204
+ await open();
205
+ const input =
206
+ document.querySelector<HTMLInputElement>('[role="searchbox"]')!;
207
+ await key(input, "d");
208
+ expect(document.activeElement).toBe(input);
209
+ await key(input, "ArrowDown");
210
+ expect(document.activeElement).toBe(row("Add context"));
211
+ await key(row("Add context"), "ArrowRight");
212
+ await key(row("Documents"), "ArrowRight");
213
+ expect(menus()).toHaveLength(3);
214
+ await key(row("Project brief"), "ArrowDown");
215
+ expect(document.activeElement).toBe(row("Archive"));
216
+ await key(row("Archive"), "ArrowLeft");
217
+ expect(menus()).toHaveLength(2);
218
+ expect(document.activeElement).toBe(row("Documents"));
219
+ await key(row("Documents"), "Escape");
220
+ expect(menus()).toHaveLength(1);
221
+ expect(document.activeElement).toBe(row("Add context"));
222
+ });
223
+ it("preserves native upload accepts, cancellation, multiple files and errors", async () => {
224
+ const attach = vi
225
+ .fn()
226
+ .mockRejectedValueOnce(new Error("Upload unavailable"))
227
+ .mockResolvedValue(undefined);
228
+ const error = vi.fn();
229
+ await render([], {
230
+ addAttachment: attach,
231
+ attachmentAccept: "text/plain",
232
+ onAttachmentError: error,
233
+ });
234
+ const input =
235
+ container.querySelector<HTMLInputElement>('input[type="file"]')!;
236
+ const picker = vi.spyOn(input, "click");
237
+ expect(input.multiple).toBe(true);
238
+ expect(input.accept).toBe("text/plain");
239
+ await open();
240
+ await click("Upload File");
241
+ expect(picker).toHaveBeenCalledOnce();
242
+ await act(async () =>
243
+ input.dispatchEvent(new Event("change", { bubbles: true })),
244
+ );
245
+ expect(attach).not.toHaveBeenCalled();
246
+ const files = [new File(["one"], "one.txt"), new File(["two"], "two.txt")];
247
+ Object.defineProperty(input, "files", { value: files, configurable: true });
248
+ await act(async () =>
249
+ input.dispatchEvent(new Event("change", { bubbles: true })),
250
+ );
251
+ expect(attach.mock.calls.map(([file]) => file)).toEqual(files);
252
+ expect(error).toHaveBeenCalledWith("Upload unavailable");
253
+ });
254
+ it("preserves legacy render, latest updates, dismissal and resume without reselecting", async () => {
255
+ let controls!: ComposerContextPageControls;
256
+ const select = vi.fn();
257
+ const dismiss = vi.fn();
258
+ const legacy = (text: string): ComposerContextMenuItem[] => [
259
+ {
260
+ id: "legacy",
261
+ label: "Reference",
262
+ onSelect: select,
263
+ onDismiss: dismiss,
264
+ render(next) {
265
+ controls = next;
266
+ return <input aria-label="Reference URL" defaultValue={text} />;
267
+ },
268
+ },
269
+ ];
270
+ await render(legacy("first"));
271
+ const draft = container.querySelector('[aria-label="Prompt draft"]');
272
+ await open();
273
+ await click("Add context");
274
+ await click("Reference");
275
+ expect(select).toHaveBeenCalledOnce();
276
+ const resume = controls.onResume;
277
+ await act(async () => controls.onClose({ restoreFocus: false }));
278
+ expect(dismiss).toHaveBeenCalledOnce();
279
+ await render(legacy("updated"));
280
+ await act(async () => resume());
281
+ expect(
282
+ document.querySelector<HTMLInputElement>('[aria-label="Reference URL"]')
283
+ ?.value,
284
+ ).toBe("updated");
285
+ expect(select).toHaveBeenCalledOnce();
286
+ expect(container.querySelector('[aria-label="Prompt draft"]')).toBe(draft);
287
+ await act(async () => controls.onBack());
288
+ expect(menus()).toHaveLength(2);
289
+ expect(dismiss).toHaveBeenCalledTimes(2);
290
+ });
291
+ it("ignores stale legacy close and reports resume of a removed source", async () => {
292
+ let controls!: ComposerContextPageControls;
293
+ const dismiss = vi.fn();
294
+ await render([
295
+ {
296
+ id: "legacy",
297
+ label: "Reference",
298
+ onSelect() {},
299
+ onDismiss: dismiss,
300
+ render(next) {
301
+ controls = next;
302
+ return <input />;
303
+ },
304
+ },
305
+ ]);
306
+ await open();
307
+ await click("Add context");
308
+ await click("Reference");
309
+ const old = controls;
310
+ await act(async () => controls.onBack());
311
+ await act(async () => old.onClose());
312
+ expect(menus()).toHaveLength(2);
313
+ await render([]);
314
+ await act(async () => old.onResume());
315
+ expect(document.querySelector('[role="alert"]')?.textContent).toBe(
316
+ "Could not add context.",
317
+ );
318
+ expect(dismiss).toHaveBeenCalledOnce();
319
+ });
320
+ it.each(["sync", "async"])(
321
+ "reports %s legacy errors without losing the picker",
322
+ async (kind) => {
323
+ await render([
324
+ {
325
+ id: "error",
326
+ label: "Reference",
327
+ render: () => <input />,
328
+ onSelect() {
329
+ if (kind === "sync") throw new Error("Reference unavailable");
330
+ return Promise.reject(new Error("Reference unavailable"));
331
+ },
332
+ },
333
+ ]);
334
+ await open();
335
+ await click("Add context");
336
+ await click("Reference");
337
+ expect(menus()).toHaveLength(3);
338
+ expect(document.querySelector('[role="alert"]')?.textContent).toBe(
339
+ "Reference unavailable",
340
+ );
341
+ },
342
+ );
343
+ });