@agent-native/toolkit 0.22.1 → 0.22.2-nightly-20260926231845

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 (38) hide show
  1. package/dist/app-shell/PromptHome.d.ts +1 -2
  2. package/dist/app-shell/PromptHome.d.ts.map +1 -1
  3. package/dist/app-shell/PromptHome.js +4 -5
  4. package/dist/app-shell/PromptHome.js.map +1 -1
  5. package/dist/app-shell/PromptHome.spec.js +73 -9
  6. package/dist/app-shell/PromptHome.spec.js.map +1 -1
  7. package/dist/app-shell/TemplateLibraryGrid.d.ts +4 -2
  8. package/dist/app-shell/TemplateLibraryGrid.d.ts.map +1 -1
  9. package/dist/app-shell/TemplateLibraryGrid.js +6 -6
  10. package/dist/app-shell/TemplateLibraryGrid.js.map +1 -1
  11. package/dist/app-shell/index.d.ts +1 -0
  12. package/dist/app-shell/index.d.ts.map +1 -1
  13. package/dist/app-shell/index.js +1 -0
  14. package/dist/app-shell/index.js.map +1 -1
  15. package/dist/app-shell/prompt-home.css +42 -2
  16. package/dist/app-shell/use-home-search-shortcut.d.ts +2 -0
  17. package/dist/app-shell/use-home-search-shortcut.d.ts.map +1 -0
  18. package/dist/app-shell/use-home-search-shortcut.js +32 -0
  19. package/dist/app-shell/use-home-search-shortcut.js.map +1 -0
  20. package/dist/composer/ComposerContextMenu.d.ts.map +1 -1
  21. package/dist/composer/ComposerContextMenu.js +11 -36
  22. package/dist/composer/ComposerContextMenu.js.map +1 -1
  23. package/dist/composer/ComposerContextMenu.spec.js +10 -24
  24. package/dist/composer/ComposerContextMenu.spec.js.map +1 -1
  25. package/dist/composer/ComposerContextPicker.spec.js +1 -1
  26. package/dist/composer/PromptComposer.context.spec.js +1 -1
  27. package/dist/composer/PromptComposer.context.spec.js.map +1 -1
  28. package/package.json +1 -1
  29. package/src/app-shell/PromptHome.spec.tsx +84 -10
  30. package/src/app-shell/PromptHome.tsx +21 -33
  31. package/src/app-shell/TemplateLibraryGrid.tsx +17 -2
  32. package/src/app-shell/index.ts +1 -0
  33. package/src/app-shell/prompt-home.css +42 -2
  34. package/src/app-shell/use-home-search-shortcut.ts +40 -0
  35. package/src/composer/ComposerContextMenu.spec.tsx +10 -34
  36. package/src/composer/ComposerContextMenu.tsx +29 -99
  37. package/src/composer/ComposerContextPicker.spec.tsx +1 -1
  38. package/src/composer/PromptComposer.context.spec.tsx +1 -1
@@ -8,7 +8,7 @@
8
8
  min-height: clamp(36rem, 64svh, 44rem);
9
9
  flex-direction: column;
10
10
  align-items: center;
11
- padding-block-start: clamp(12rem, 33svh, 22rem);
11
+ justify-content: center;
12
12
  text-align: center;
13
13
  }
14
14
 
@@ -17,10 +17,41 @@
17
17
  max-width: 43.75rem;
18
18
  }
19
19
 
20
+ .agent-prompt-home-connection {
21
+ display: grid;
22
+ grid-template-rows: 0fr;
23
+ opacity: 0;
24
+ transition:
25
+ grid-template-rows 200ms ease,
26
+ opacity 160ms ease;
27
+ }
28
+
29
+ .agent-prompt-home-connection[data-visible] {
30
+ grid-template-rows: 1fr;
31
+ opacity: 1;
32
+ }
33
+
34
+ .agent-prompt-home-connection > * {
35
+ min-height: 0;
36
+ overflow: hidden;
37
+ }
38
+
39
+ .agent-prompt-home-connection > .agent-builder-setup-card--attached {
40
+ margin-bottom: -1.875rem;
41
+ }
42
+
20
43
  .agent-prompt-home-library {
21
44
  padding-block-start: 2rem;
22
45
  }
23
46
 
47
+ .agent-prompt-home-tabs > div:first-child {
48
+ position: sticky;
49
+ top: 0;
50
+ z-index: 10;
51
+ padding-block: 0.5rem;
52
+ background: hsl(var(--background));
53
+ }
54
+
24
55
  .agent-template-library-grid {
25
56
  display: grid;
26
57
  grid-template-columns: minmax(0, 1fr);
@@ -28,6 +59,7 @@
28
59
  }
29
60
 
30
61
  .agent-template-library-preview {
62
+ position: relative;
31
63
  aspect-ratio: 16 / 9;
32
64
  overflow: hidden;
33
65
  border-radius: inherit;
@@ -41,11 +73,13 @@
41
73
 
42
74
  .agent-template-library-primary {
43
75
  grid-area: 1 / 1 / 3 / 3;
76
+ position: relative;
44
77
  min-width: 0;
45
78
  }
46
79
 
47
80
  .agent-template-library-caption {
48
- height: 5rem;
81
+ min-height: 5rem;
82
+ padding: 1rem;
49
83
  padding-inline-end: 3rem;
50
84
  }
51
85
 
@@ -77,6 +111,12 @@
77
111
  height: min(85dvh, 60rem);
78
112
  }
79
113
 
114
+ @media (prefers-reduced-motion: reduce) {
115
+ .agent-prompt-home-connection {
116
+ transition: none;
117
+ }
118
+ }
119
+
80
120
  .agent-template-preview-viewer {
81
121
  display: grid;
82
122
  min-height: 0;
@@ -0,0 +1,40 @@
1
+ import { useEffect } from "react";
2
+
3
+ export function useHomeSearchShortcut(enabled: boolean): void {
4
+ useEffect(() => {
5
+ if (!enabled) return;
6
+ const onKeyDown = (event: KeyboardEvent) => {
7
+ if (
8
+ event.key !== "/" ||
9
+ event.metaKey ||
10
+ event.ctrlKey ||
11
+ event.altKey ||
12
+ event.isComposing
13
+ ) {
14
+ return;
15
+ }
16
+ const target = event.target instanceof HTMLElement ? event.target : null;
17
+ if (
18
+ target?.isContentEditable ||
19
+ target?.closest("input, textarea, select, [role='textbox']")
20
+ ) {
21
+ return;
22
+ }
23
+ if (
24
+ document.querySelector(
25
+ '[role="menu"][data-state="open"], [role="dialog"][data-state="open"], [role="alertdialog"][data-state="open"], dialog[open]',
26
+ )
27
+ ) {
28
+ return;
29
+ }
30
+ const search = Array.from(
31
+ document.querySelectorAll<HTMLInputElement>("[data-home-search]"),
32
+ ).find((input) => input.getClientRects().length > 0);
33
+ if (!search || search.disabled) return;
34
+ event.preventDefault();
35
+ search.focus();
36
+ };
37
+ document.addEventListener("keydown", onKeyDown);
38
+ return () => document.removeEventListener("keydown", onKeyDown);
39
+ }, [enabled]);
40
+ }
@@ -136,24 +136,10 @@ describe("connected composer menus", () => {
136
136
  async function click(label: string) {
137
137
  await act(async () => row(label).click());
138
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 () => {
139
+ it("shows complete options in each anchored menu with w64", async () => {
152
140
  await render(items, { addAttachment: vi.fn() });
153
141
  await open();
154
- expect(
155
- document.querySelector('input[placeholder="Search…"]'),
156
- ).not.toBeNull();
142
+ expect(document.querySelectorAll('[role="searchbox"]')).toHaveLength(0);
157
143
  expect(row("Upload File").querySelector("svg")?.getAttribute("width")).toBe(
158
144
  "16",
159
145
  );
@@ -162,14 +148,12 @@ describe("connected composer menus", () => {
162
148
  await click("Archive");
163
149
  expect(menus()).toHaveLength(4);
164
150
  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();
151
+ expect(document.querySelectorAll('[role="searchbox"]')).toHaveLength(0);
169
152
  expect(row("Documents").getAttribute("aria-expanded")).toBe("true");
170
153
  expect(row("Meeting notes").getAttribute("role")).toBe("menuitem");
154
+ expect(row("Project brief")).toBeDefined();
171
155
  });
172
- it("filters root and category search without flattening the hierarchy", async () => {
156
+ it("keeps the menu hierarchy while listing every option", async () => {
173
157
  const select = vi.fn();
174
158
  await render(
175
159
  [
@@ -187,27 +171,19 @@ describe("connected composer menus", () => {
187
171
  { addAttachment: vi.fn() },
188
172
  );
189
173
  await open();
190
- await search("Search…", "brief");
191
- expect(menus()[0].textContent).not.toContain("Upload File");
174
+ expect(row("Upload File")).toBeDefined();
192
175
  await click("Add context");
193
- await search("Search context…", "documents");
194
- expect(menus()[1].textContent).not.toContain("Library");
176
+ expect(row("Library")).toBeDefined();
195
177
  await click("Documents");
196
- await search("Search documents…", "brief");
197
- expect(menus()[2].textContent).not.toContain("Notes");
178
+ expect(row("Notes")).toBeDefined();
179
+ expect(document.querySelectorAll('[role="searchbox"]')).toHaveLength(0);
198
180
  await click("Project brief");
199
181
  expect(select).toHaveBeenCalledOnce();
200
182
  expect(menus()).toHaveLength(0);
201
183
  });
202
- it("keeps typing out of typeahead and supports native ArrowRight, Left and Escape", async () => {
184
+ it("supports native ArrowRight, Left and Escape navigation", async () => {
203
185
  await render();
204
186
  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
187
  await key(row("Add context"), "ArrowRight");
212
188
  await key(row("Documents"), "ArrowRight");
213
189
  expect(menus()).toHaveLength(3);
@@ -20,7 +20,6 @@ import {
20
20
  } from "../ui/dropdown-menu.js";
21
21
  import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip.js";
22
22
  import { formatAttachmentError } from "./attachment-accept.js";
23
- import { ComposerContextMenuSearch } from "./ComposerContextMenuSearch.js";
24
23
  import {
25
24
  ComposerContextPicker,
26
25
  type ComposerContextPickerConfig,
@@ -189,37 +188,6 @@ function ContextSubmenu({
189
188
  );
190
189
  }
191
190
 
192
- function ContextMenuPanel({
193
- placeholder,
194
- children,
195
- }: {
196
- placeholder: string;
197
- children: (query: string) => ReactNode;
198
- }) {
199
- const [query, setQuery] = useState("");
200
- return (
201
- <>
202
- <ComposerContextMenuSearch
203
- placeholder={placeholder}
204
- value={query}
205
- onValueChange={setQuery}
206
- />
207
- {children(query)}
208
- </>
209
- );
210
- }
211
-
212
- function matchesEntry(entry: ComposerContextMenuItem, query: string): boolean {
213
- const terms = query.trim().toLocaleLowerCase().split(/\s+/).filter(Boolean);
214
- const text = [entry.label, ...(entry.keywords ?? [])]
215
- .join(" ")
216
- .toLocaleLowerCase();
217
- return (
218
- terms.every((term) => text.includes(term)) ||
219
- Boolean(entry.children?.some((child) => matchesEntry(child, query)))
220
- );
221
- }
222
-
223
191
  function LegacyContextPage({ children }: { children: ReactNode }) {
224
192
  const element = useRef<HTMLDivElement>(null);
225
193
  useEffect(() => {
@@ -274,9 +242,6 @@ export function ComposerContextMenu({
274
242
  const label = t("agentChat.composer.addContext", {
275
243
  defaultValue: "Add context",
276
244
  });
277
- const searchContext = t("agentChat.composer.searchContext", {
278
- defaultValue: "Search context…",
279
- });
280
245
  const uploadLabel = t("agentChat.composer.menu.uploadFile", {
281
246
  defaultValue: "Upload File",
282
247
  });
@@ -399,18 +364,7 @@ export function ComposerContextMenu({
399
364
  }}
400
365
  >
401
366
  {entry.children ? (
402
- <ContextMenuPanel
403
- placeholder={entry.searchPlaceholder ?? searchContext}
404
- >
405
- {(query) =>
406
- renderEntries(
407
- entry.children.filter((child) =>
408
- matchesEntry(child, query),
409
- ),
410
- branch,
411
- )
412
- }
413
- </ContextMenuPanel>
367
+ renderEntries(entry.children, branch)
414
368
  ) : entry.picker ? (
415
369
  <ComposerContextPicker
416
370
  key={JSON.stringify([entry.id, entry.picker.scopeKey])}
@@ -522,59 +476,35 @@ export function ComposerContextMenu({
522
476
  restoreFocusOnClose.current = true;
523
477
  }}
524
478
  >
525
- <ContextMenuPanel
526
- placeholder={t("agentChat.composer.menu.search", {
527
- defaultValue: "Search…",
528
- })}
529
- >
530
- {(query) => (
531
- <DropdownMenuGroup>
532
- {addAttachment &&
533
- uploadLabel
534
- .toLocaleLowerCase()
535
- .includes(query.trim().toLocaleLowerCase()) && (
536
- <DropdownMenuItem
537
- onSelect={() => {
538
- changeOpen(false);
539
- inputRef.current?.click();
540
- }}
541
- >
542
- <IconFile size={16} />
543
- {uploadLabel}
544
- </DropdownMenuItem>
545
- )}
546
- {items.length > 0 &&
547
- (label
548
- .toLocaleLowerCase()
549
- .includes(query.trim().toLocaleLowerCase()) ||
550
- items.some((item) => matchesEntry(item, query))) && (
551
- <ContextSubmenu
552
- label={label}
553
- icon={<IconTextRecognition size={16} />}
554
- open={contextOpen}
555
- onOpenChange={(next) => {
556
- setContextOpen(next);
557
- if (!next) {
558
- dismissPage();
559
- updatePath([]);
560
- }
561
- }}
562
- >
563
- <ContextMenuPanel placeholder={searchContext}>
564
- {(contextQuery) =>
565
- renderEntries(
566
- items.filter((item) =>
567
- matchesEntry(item, contextQuery),
568
- ),
569
- [],
570
- )
571
- }
572
- </ContextMenuPanel>
573
- </ContextSubmenu>
574
- )}
575
- </DropdownMenuGroup>
479
+ <DropdownMenuGroup>
480
+ {addAttachment && (
481
+ <DropdownMenuItem
482
+ onSelect={() => {
483
+ changeOpen(false);
484
+ inputRef.current?.click();
485
+ }}
486
+ >
487
+ <IconFile size={16} />
488
+ {uploadLabel}
489
+ </DropdownMenuItem>
576
490
  )}
577
- </ContextMenuPanel>
491
+ {items.length > 0 ? (
492
+ <ContextSubmenu
493
+ label={label}
494
+ icon={<IconTextRecognition size={16} />}
495
+ open={contextOpen}
496
+ onOpenChange={(next) => {
497
+ setContextOpen(next);
498
+ if (!next) {
499
+ dismissPage();
500
+ updatePath([]);
501
+ }
502
+ }}
503
+ >
504
+ {renderEntries(items, [])}
505
+ </ContextSubmenu>
506
+ ) : null}
507
+ </DropdownMenuGroup>
578
508
  </DropdownMenuContent>
579
509
  </DropdownMenu>
580
510
  {dialog &&
@@ -242,7 +242,7 @@ describe("declarative context picker", () => {
242
242
  expect(menus()).toHaveLength(4);
243
243
  expect(menus().every((menu) => menu.classList.contains("w-64"))).toBe(true);
244
244
  const searches = document.querySelectorAll('input[role="searchbox"]');
245
- expect(searches).toHaveLength(4);
245
+ expect(searches).toHaveLength(1);
246
246
  expect(document.querySelector('[role="combobox"]')).toBeNull();
247
247
  for (const input of searches)
248
248
  expect(input.hasAttribute("aria-controls")).toBe(false);
@@ -44,7 +44,7 @@ describe("controlled composer context", () => {
44
44
  ),
45
45
  );
46
46
  const menu = document.querySelector('[role="menu"]')!;
47
- expect(menu.querySelector('[role="searchbox"]')).not.toBeNull();
47
+ expect(menu.querySelector('[role="searchbox"]')).toBeNull();
48
48
  expect(menu.textContent).toBe("Upload File");
49
49
  await act(async () =>
50
50
  document.dispatchEvent(