@agent-native/toolkit 0.198.7-nightly-20260930213124 → 0.198.7

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 (50) hide show
  1. package/dist/app/chat/AgentPanel.d.ts +4 -3
  2. package/dist/app/chat/AgentPanel.d.ts.map +1 -1
  3. package/dist/app/chat/AgentPanel.js +14 -24
  4. package/dist/app/chat/AgentPanel.js.map +1 -1
  5. package/dist/app/chat/ConnectBuilderCard.d.ts.map +1 -1
  6. package/dist/app/chat/ConnectBuilderCard.js +2 -1
  7. package/dist/app/chat/ConnectBuilderCard.js.map +1 -1
  8. package/dist/app/chat/MultiTabAssistantChat.d.ts.map +1 -1
  9. package/dist/app/chat/MultiTabAssistantChat.js +21 -2
  10. package/dist/app/chat/MultiTabAssistantChat.js.map +1 -1
  11. package/dist/composer/ComposerContextMenu.d.ts +2 -2
  12. package/dist/composer/ComposerContextMenu.d.ts.map +1 -1
  13. package/dist/composer/ComposerContextMenu.js +54 -66
  14. package/dist/composer/ComposerContextMenu.js.map +1 -1
  15. package/dist/composer/ComposerPlusMenu.d.ts +2 -3
  16. package/dist/composer/ComposerPlusMenu.d.ts.map +1 -1
  17. package/dist/composer/ComposerPlusMenu.js +13 -278
  18. package/dist/composer/ComposerPlusMenu.js.map +1 -1
  19. package/dist/composer/TiptapComposer.d.ts.map +1 -1
  20. package/dist/composer/TiptapComposer.js +3 -12
  21. package/dist/composer/TiptapComposer.js.map +1 -1
  22. package/dist/composer/use-composer-panel-placement.d.ts +0 -1
  23. package/dist/composer/use-composer-panel-placement.d.ts.map +1 -1
  24. package/dist/composer/use-composer-panel-placement.js +4 -5
  25. package/dist/composer/use-composer-panel-placement.js.map +1 -1
  26. package/package.json +4 -4
  27. package/src/app/chat/AgentPanel.header.spec.ts +25 -27
  28. package/src/app/chat/AgentPanel.overlay.spec.tsx +14 -51
  29. package/src/app/chat/AgentPanel.tsx +15 -26
  30. package/src/app/chat/ConnectBuilderCard.spec.tsx +45 -0
  31. package/src/app/chat/ConnectBuilderCard.tsx +3 -2
  32. package/src/app/chat/MultiTabAssistantChat.spec.tsx +12 -0
  33. package/src/app/chat/MultiTabAssistantChat.tsx +26 -2
  34. package/src/app/chat/composer/use-agentkit-integration-menu.spec.tsx +4 -2
  35. package/src/composer/ComposerContextMenu.spec.tsx +68 -60
  36. package/src/composer/ComposerContextMenu.tsx +93 -110
  37. package/src/composer/ComposerContextPicker.spec.tsx +16 -14
  38. package/src/composer/ComposerContextPickerDialog.spec.tsx +8 -6
  39. package/src/composer/ComposerPlusMenu.parity.spec.tsx +37 -75
  40. package/src/composer/ComposerPlusMenu.tsx +26 -450
  41. package/src/composer/PromptComposer.context.spec.tsx +112 -18
  42. package/src/composer/TiptapComposer.spec.ts +15 -0
  43. package/src/composer/TiptapComposer.tsx +2 -12
  44. package/src/composer/use-composer-panel-placement.ts +7 -5
  45. package/dist/composer/asset-picker-url.d.ts +0 -10
  46. package/dist/composer/asset-picker-url.d.ts.map +0 -1
  47. package/dist/composer/asset-picker-url.js +0 -56
  48. package/dist/composer/asset-picker-url.js.map +0 -1
  49. package/src/composer/asset-picker-url.spec.ts +0 -56
  50. package/src/composer/asset-picker-url.ts +0 -72
@@ -1,6 +1,5 @@
1
1
  import { type RefObject } from "react";
2
2
  export declare function useComposerPanelPlacement(triggerRef: RefObject<HTMLElement | null>, open: boolean): {
3
- width: number;
4
3
  maxHeight: number;
5
4
  side: "top" | "bottom";
6
5
  sideOffset: number;
@@ -1 +1 @@
1
- {"version":3,"file":"use-composer-panel-placement.d.ts","sourceRoot":"","sources":["../../src/composer/use-composer-panel-placement.ts"],"names":[],"mappings":"AAAA,OAAO,EAA6B,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAKlE,wBAAgB,yBAAyB,CACvC,UAAU,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,EACzC,IAAI,EAAE,OAAO;;;UAKI,KAAK,GAAG,QAAQ;;;eAGX,KAAK,GAAG,KAAK;EAsDpC"}
1
+ {"version":3,"file":"use-composer-panel-placement.d.ts","sourceRoot":"","sources":["../../src/composer/use-composer-panel-placement.ts"],"names":[],"mappings":"AAAA,OAAO,EAA6B,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAMlE,wBAAgB,yBAAyB,CACvC,UAAU,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,EACzC,IAAI,EAAE,OAAO;;UAII,KAAK,GAAG,QAAQ;;;eAGX,KAAK,GAAG,KAAK;EAwDpC"}
@@ -1,10 +1,10 @@
1
1
  import { useLayoutEffect, useState } from "react";
2
- // Leave room for the search field, group label, and at least one action.
2
+ // Keep the compact menu's actions reachable in short viewports.
3
3
  const MIN_USABLE_PANEL_HEIGHT = 120;
4
+ const MAX_COMPOSER_PANEL_HEIGHT = 280;
4
5
  export function useComposerPanelPlacement(triggerRef, open) {
5
6
  const [placement, setPlacement] = useState({
6
- width: 320,
7
- maxHeight: 440,
7
+ maxHeight: MAX_COMPOSER_PANEL_HEIGHT,
8
8
  side: "top",
9
9
  sideOffset: 8,
10
10
  alignOffset: 0,
@@ -30,10 +30,9 @@ export function useComposerPanelPlacement(triggerRef, open) {
30
30
  ? "bottom"
31
31
  : "top";
32
32
  setPlacement({
33
- width: bounds.width,
34
33
  direction: rtl ? "rtl" : "ltr",
35
34
  side,
36
- maxHeight: Math.min(440, side === "top" ? spaceAbove : spaceBelow),
35
+ maxHeight: Math.min(MAX_COMPOSER_PANEL_HEIGHT, side === "top" ? spaceAbove : spaceBelow),
37
36
  sideOffset: side === "top"
38
37
  ? button.top - bounds.top + 8
39
38
  : bounds.bottom - button.bottom + 8,
@@ -1 +1 @@
1
- {"version":3,"file":"use-composer-panel-placement.js","sourceRoot":"","sources":["../../src/composer/use-composer-panel-placement.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"use-composer-panel-placement.js","sourceRoot":"","sources":["../../src/composer/use-composer-panel-placement.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/toolkit",
3
- "version": "0.198.7-nightly-20260930213124",
3
+ "version": "0.198.7",
4
4
  "description": "Reusable app-building UI and helpers for Agent-Native apps.",
5
5
  "homepage": "https://github.com/BuilderIO/agent-native#readme",
6
6
  "bugs": {
@@ -665,7 +665,7 @@
665
665
  "y-protocols": "^1.0.7",
666
666
  "yjs": "^13.6.33",
667
667
  "zod": "^4.3.6",
668
- "@agent-native/agentkit": "^0.198.7-nightly-20260930213124"
668
+ "@agent-native/agentkit": "^0.198.7"
669
669
  },
670
670
  "devDependencies": {
671
671
  "@excalidraw/excalidraw": "0.18.1",
@@ -686,10 +686,10 @@
686
686
  "typescript-7": "npm:typescript@^7.0.2",
687
687
  "vite": "8.1.0",
688
688
  "vitest": "^4.1.5",
689
- "@agent-native/core": "^0.198.7-nightly-20260930213124"
689
+ "@agent-native/core": "^0.198.7"
690
690
  },
691
691
  "peerDependencies": {
692
- "@agent-native/core": "0.198.7-nightly-20260930213124",
692
+ "@agent-native/core": ">=0.196.0",
693
693
  "@excalidraw/excalidraw": ">=0.18",
694
694
  "@excalidraw/mermaid-to-excalidraw": ">=2",
695
695
  "@tanstack/react-query": "^5.99.2",
@@ -907,47 +907,45 @@ describe("AgentSidebar toggle routing", () => {
907
907
  });
908
908
 
909
909
  describe("AgentPanel header overflow actions", () => {
910
- it("closes the menu before opening a sibling overlay", () => {
911
- const requestAnimationFrame = window.requestAnimationFrame;
912
- const frames: Array<() => void> = [];
913
- window.requestAnimationFrame = ((callback: FrameRequestCallback) => {
914
- frames.push(() => callback(0));
915
- return frames.length;
916
- }) as typeof window.requestAnimationFrame;
910
+ it("opens a sibling overlay only after the menu closes", () => {
911
+ const pendingOverlayRef = { current: null as (() => void) | null };
912
+ const event = { preventDefault: vi.fn() };
913
+ const focusRestoreEvent = { preventDefault: vi.fn() };
914
+ const events: string[] = [];
917
915
 
918
- try {
919
- const event = { preventDefault: vi.fn() };
920
- const events: string[] = [];
916
+ deferAgentPanelOverlayOpen(
917
+ event,
918
+ () => events.push("menu closed"),
919
+ () => events.push("overlay opened"),
920
+ pendingOverlayRef,
921
+ );
921
922
 
922
- deferAgentPanelOverlayOpen(
923
- event,
924
- () => events.push("menu closed"),
925
- () => events.push("overlay opened"),
926
- );
923
+ expect(event.preventDefault).toHaveBeenCalledOnce();
924
+ expect(events).toEqual(["menu closed"]);
925
+ expect(pendingOverlayRef.current).toBeTypeOf("function");
927
926
 
928
- expect(event.preventDefault).toHaveBeenCalledOnce();
929
- expect(events).toEqual(["menu closed"]);
930
- expect(frames).toHaveLength(1);
927
+ consumeAgentPanelOverlayFocusRestore(pendingOverlayRef, focusRestoreEvent);
931
928
 
932
- frames[0]!();
933
- expect(events).toEqual(["menu closed", "overlay opened"]);
934
- } finally {
935
- window.requestAnimationFrame = requestAnimationFrame;
936
- }
929
+ expect(focusRestoreEvent.preventDefault).toHaveBeenCalledOnce();
930
+ expect(events).toEqual(["menu closed", "overlay opened"]);
931
+ expect(pendingOverlayRef.current).toBeNull();
937
932
  });
938
933
 
939
934
  it("consumes the pending menu focus restore for the sibling overlay", () => {
940
- const pendingOverlayRef = { current: true };
935
+ const openOverlay = vi.fn();
936
+ const pendingOverlayRef = { current: openOverlay };
941
937
  const event = { preventDefault: vi.fn() };
942
938
 
943
939
  consumeAgentPanelOverlayFocusRestore(pendingOverlayRef, event);
944
940
 
945
941
  expect(event.preventDefault).toHaveBeenCalledOnce();
946
- expect(pendingOverlayRef.current).toBe(false);
942
+ expect(openOverlay).toHaveBeenCalledOnce();
943
+ expect(pendingOverlayRef.current).toBeNull();
947
944
 
948
945
  const secondEvent = { preventDefault: vi.fn() };
949
946
  consumeAgentPanelOverlayFocusRestore(pendingOverlayRef, secondEvent);
950
947
  expect(secondEvent.preventDefault).not.toHaveBeenCalled();
948
+ expect(openOverlay).toHaveBeenCalledOnce();
951
949
  });
952
950
 
953
951
  it("keeps width and full-view actions out of the icon row", () => {
@@ -978,7 +976,7 @@ describe("AgentPanel header overflow actions", () => {
978
976
  expect(overflowMenu.match(/deferAgentPanelOverlayOpen/g)).toHaveLength(3);
979
977
  expect(source).toContain("event.preventDefault();");
980
978
  expect(overflowMenu).toContain(
981
- 'toggleHistory,\n "timeout"',
979
+ "toggleHistory,\n pendingHeaderOverlayRef",
982
980
  );
983
981
  expect(overflowMenu).toContain("onCloseAutoFocus");
984
982
  expect(
@@ -1024,7 +1022,7 @@ describe("AgentPanel header overflow actions", () => {
1024
1022
  expect(source).toContain("defaultOpen={onCollapse && shareFromMenuOpen}");
1025
1023
  expect(source).toContain("onCollapse ? setShareFromMenuOpen : undefined");
1026
1024
  expect(overflowMenu).toContain(
1027
- 'setShareFromMenuOpen(true),\n "timeout"',
1025
+ "setShareFromMenuOpen(true),\n pendingHeaderOverlayRef",
1028
1026
  );
1029
1027
  });
1030
1028
 
@@ -25,10 +25,9 @@ function OverlayHandoffHarness({
25
25
  const [feedbackOpen, setFeedbackOpen] = useState(false);
26
26
  const [historyOpen, setHistoryOpen] = useState(false);
27
27
  const [shareFromMenuOpen, setShareFromMenuOpen] = useState(false);
28
- const pendingOverlayRef = useRef(false);
28
+ const pendingOverlayRef = useRef<(() => void) | null>(null);
29
29
 
30
30
  const closeMenuForOverlay = () => {
31
- pendingOverlayRef.current = true;
32
31
  setMenuOpen(false);
33
32
  };
34
33
 
@@ -53,7 +52,7 @@ function OverlayHandoffHarness({
53
52
  event,
54
53
  closeMenuForOverlay,
55
54
  () => setHistoryOpen(true),
56
- "timeout",
55
+ pendingOverlayRef,
57
56
  )
58
57
  }
59
58
  >
@@ -62,8 +61,11 @@ function OverlayHandoffHarness({
62
61
  <DropdownMenuItem
63
62
  data-testid="feedback-item"
64
63
  onSelect={(event) =>
65
- deferAgentPanelOverlayOpen(event, closeMenuForOverlay, () =>
66
- setFeedbackOpen(true),
64
+ deferAgentPanelOverlayOpen(
65
+ event,
66
+ closeMenuForOverlay,
67
+ () => setFeedbackOpen(true),
68
+ pendingOverlayRef,
67
69
  )
68
70
  }
69
71
  >
@@ -76,7 +78,7 @@ function OverlayHandoffHarness({
76
78
  event,
77
79
  closeMenuForOverlay,
78
80
  () => setShareFromMenuOpen(true),
79
- "timeout",
81
+ pendingOverlayRef,
80
82
  )
81
83
  }
82
84
  >
@@ -139,32 +141,23 @@ function OverlayHandoffHarness({
139
141
  describe("AgentPanel sibling overlay handoff", () => {
140
142
  let container: HTMLDivElement;
141
143
  let root: Root;
142
- let frames: Array<FrameRequestCallback>;
143
- let requestAnimationFrame: typeof window.requestAnimationFrame;
144
144
 
145
145
  beforeEach(() => {
146
146
  vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true);
147
147
  container = document.createElement("div");
148
148
  document.body.appendChild(container);
149
149
  root = createRoot(container);
150
- frames = [];
151
- requestAnimationFrame = window.requestAnimationFrame;
152
- window.requestAnimationFrame = ((callback: FrameRequestCallback) => {
153
- frames.push(callback);
154
- return frames.length;
155
- }) as typeof window.requestAnimationFrame;
156
150
  });
157
151
 
158
152
  afterEach(() => {
159
153
  act(() => root.unmount());
160
154
  container.remove();
161
155
  document.body.innerHTML = "";
162
- window.requestAnimationFrame = requestAnimationFrame;
163
156
  vi.useRealTimers();
164
157
  vi.unstubAllGlobals();
165
158
  });
166
159
 
167
- it("keeps feedback open after the menu restores focus", async () => {
160
+ it("opens feedback after the menu closes", async () => {
168
161
  const focusRestorePrevented = vi.fn();
169
162
 
170
163
  await act(async () => {
@@ -204,20 +197,14 @@ describe("AgentPanel sibling overlay handoff", () => {
204
197
  });
205
198
 
206
199
  expect(focusRestorePrevented).toHaveBeenCalledWith(true);
207
- expect(frames).toHaveLength(1);
208
-
209
- await act(async () => {
210
- frames[0]!(0);
211
- });
212
200
 
213
201
  expect(
214
202
  document.body.querySelector('[data-testid="feedback-content"]'),
215
203
  ).toBeTruthy();
216
204
  });
217
205
 
218
- it("keeps All chats open after the menu restores focus", async () => {
206
+ it("keeps All chats open after the menu closes", async () => {
219
207
  const focusRestorePrevented = vi.fn();
220
- vi.useFakeTimers();
221
208
 
222
209
  await act(async () => {
223
210
  root.render(
@@ -251,19 +238,8 @@ describe("AgentPanel sibling overlay handoff", () => {
251
238
  );
252
239
  });
253
240
 
254
- expect(
255
- document.body.querySelector('[data-testid="history-content"]'),
256
- ).toBeNull();
257
-
258
- act(() => {
259
- vi.advanceTimersByTime(99);
260
- });
261
- expect(
262
- document.body.querySelector('[data-testid="history-content"]'),
263
- ).toBeNull();
264
-
265
- act(() => {
266
- vi.advanceTimersByTime(1);
241
+ await act(async () => {
242
+ await new Promise<void>((resolve) => setTimeout(resolve, 150));
267
243
  });
268
244
 
269
245
  expect(focusRestorePrevented).toHaveBeenCalledWith(true);
@@ -274,7 +250,6 @@ describe("AgentPanel sibling overlay handoff", () => {
274
250
 
275
251
  it("opens the share popover after clicking Share in the overflow menu", async () => {
276
252
  const focusRestorePrevented = vi.fn();
277
- vi.useFakeTimers();
278
253
 
279
254
  await act(async () => {
280
255
  root.render(
@@ -309,20 +284,8 @@ describe("AgentPanel sibling overlay handoff", () => {
309
284
  );
310
285
  });
311
286
 
312
- expect(frames).toHaveLength(0);
313
- expect(
314
- document.body.querySelector('[data-testid="share-content"]'),
315
- ).toBeNull();
316
-
317
- act(() => {
318
- vi.advanceTimersByTime(99);
319
- });
320
- expect(
321
- document.body.querySelector('[data-testid="share-content"]'),
322
- ).toBeNull();
323
-
324
- act(() => {
325
- vi.advanceTimersByTime(1);
287
+ await act(async () => {
288
+ await new Promise<void>((resolve) => setTimeout(resolve, 150));
326
289
  });
327
290
 
328
291
  expect(focusRestorePrevented).toHaveBeenCalledWith(true);
@@ -287,38 +287,27 @@ const AGENT_PANEL_CONTROL_STYLE = {
287
287
  lineHeight: 1,
288
288
  } satisfies React.CSSProperties;
289
289
  const ACTIVATE_KEYS = new Set(["Enter", " "]);
290
- const AGENT_PANEL_MENU_EXIT_DURATION_MS = 100;
291
- type AgentPanelOverlayOpenTiming = "animation-frame" | "timeout";
292
290
 
293
291
  export function deferAgentPanelOverlayOpen(
294
292
  event: { preventDefault: () => void },
295
293
  closeMenu: () => void,
296
294
  openOverlay: () => void,
297
- timing: AgentPanelOverlayOpenTiming = "animation-frame",
295
+ pendingOverlayRef: { current: (() => void) | null },
298
296
  ): void {
299
297
  event.preventDefault();
298
+ pendingOverlayRef.current = openOverlay;
300
299
  closeMenu();
301
- if (timing === "timeout") {
302
- setTimeout(openOverlay, AGENT_PANEL_MENU_EXIT_DURATION_MS);
303
- return;
304
- }
305
- if (
306
- typeof window !== "undefined" &&
307
- typeof window.requestAnimationFrame === "function"
308
- ) {
309
- window.requestAnimationFrame(() => openOverlay());
310
- } else {
311
- setTimeout(openOverlay, 0);
312
- }
313
300
  }
314
301
 
315
302
  export function consumeAgentPanelOverlayFocusRestore(
316
- pendingOverlayRef: { current: boolean },
303
+ pendingOverlayRef: { current: (() => void) | null },
317
304
  event: { preventDefault: () => void },
318
305
  ): void {
319
- if (!pendingOverlayRef.current) return;
320
- pendingOverlayRef.current = false;
306
+ const openOverlay = pendingOverlayRef.current;
307
+ if (!openOverlay) return;
308
+ pendingOverlayRef.current = null;
321
309
  event.preventDefault();
310
+ openOverlay();
322
311
  }
323
312
 
324
313
  interface AvailableCli {
@@ -1209,9 +1198,8 @@ function AgentPanelInner({
1209
1198
  const [headerMenuOpen, setHeaderMenuOpen] = useState(false);
1210
1199
  const [feedbackOpen, setFeedbackOpen] = useState(false);
1211
1200
  const [shareFromMenuOpen, setShareFromMenuOpen] = useState(false);
1212
- const preventHeaderMenuFocusRestoreRef = useRef(false);
1201
+ const pendingHeaderOverlayRef = useRef<(() => void) | null>(null);
1213
1202
  const closeHeaderMenuForOverlay = useCallback(() => {
1214
- preventHeaderMenuFocusRestoreRef.current = true;
1215
1203
  setHeaderMenuOpen(false);
1216
1204
  }, []);
1217
1205
 
@@ -1402,7 +1390,7 @@ function AgentPanelInner({
1402
1390
  className="max-h-[var(--radix-dropdown-menu-content-available-height)] w-48 overflow-y-auto"
1403
1391
  onCloseAutoFocus={(event) => {
1404
1392
  consumeAgentPanelOverlayFocusRestore(
1405
- preventHeaderMenuFocusRestoreRef,
1393
+ pendingHeaderOverlayRef,
1406
1394
  event,
1407
1395
  );
1408
1396
  }}
@@ -1515,7 +1503,7 @@ function AgentPanelInner({
1515
1503
  event,
1516
1504
  closeHeaderMenuForOverlay,
1517
1505
  () => setShareFromMenuOpen(true),
1518
- "timeout",
1506
+ pendingHeaderOverlayRef,
1519
1507
  )
1520
1508
  }
1521
1509
  >
@@ -1531,7 +1519,7 @@ function AgentPanelInner({
1531
1519
  event,
1532
1520
  closeHeaderMenuForOverlay,
1533
1521
  toggleHistory,
1534
- "timeout",
1522
+ pendingHeaderOverlayRef,
1535
1523
  )
1536
1524
  }
1537
1525
  >
@@ -1580,6 +1568,7 @@ function AgentPanelInner({
1580
1568
  event,
1581
1569
  closeHeaderMenuForOverlay,
1582
1570
  () => setFeedbackOpen(true),
1571
+ pendingHeaderOverlayRef,
1583
1572
  )
1584
1573
  }
1585
1574
  >
@@ -1777,7 +1766,7 @@ function AgentPanelInner({
1777
1766
  className="w-44"
1778
1767
  onCloseAutoFocus={(event) => {
1779
1768
  consumeAgentPanelOverlayFocusRestore(
1780
- preventHeaderMenuFocusRestoreRef,
1769
+ pendingHeaderOverlayRef,
1781
1770
  event,
1782
1771
  );
1783
1772
  }}
@@ -1789,7 +1778,7 @@ function AgentPanelInner({
1789
1778
  event,
1790
1779
  closeHeaderMenuForOverlay,
1791
1780
  toggleHistory,
1792
- "timeout",
1781
+ pendingHeaderOverlayRef,
1793
1782
  )
1794
1783
  }
1795
1784
  >
@@ -1854,7 +1843,7 @@ function AgentPanelInner({
1854
1843
  onPageHeaderVisibilityChange,
1855
1844
  pageHeaderLeadingSlot,
1856
1845
  pageToolbarSlot,
1857
- preventHeaderMenuFocusRestoreRef,
1846
+ pendingHeaderOverlayRef,
1858
1847
  setHeaderMenuOpen,
1859
1848
  showPageNewChatButton,
1860
1849
  t,
@@ -293,6 +293,51 @@ describe("ConnectBuilderCard", () => {
293
293
  window.removeEventListener("agent-panel:open-settings", handleOpenSettings);
294
294
  });
295
295
 
296
+ it("shows Builder.io for the legacy Fusion connection label and preserves org names", () => {
297
+ mocks.useBuilderConnectFlow.mockReturnValue({
298
+ hasFetchedStatus: true,
299
+ statusResolved: true,
300
+ configured: true,
301
+ codeChangeConfigured: false,
302
+ builderEnabled: false,
303
+ orgName: "Fusion",
304
+ envManaged: false,
305
+ connecting: false,
306
+ error: null,
307
+ start: mocks.start,
308
+ });
309
+
310
+ act(() => {
311
+ root.render(
312
+ <ConnectBuilderCard configured builderEnabled={false} connectUrl="" />,
313
+ );
314
+ });
315
+
316
+ expect(container.textContent).toContain("Connected to Builder.io.");
317
+ expect(container.textContent).not.toContain("Connected to Fusion");
318
+
319
+ mocks.useBuilderConnectFlow.mockReturnValue({
320
+ hasFetchedStatus: true,
321
+ statusResolved: true,
322
+ configured: true,
323
+ codeChangeConfigured: false,
324
+ builderEnabled: false,
325
+ orgName: "Acme workspace",
326
+ envManaged: false,
327
+ connecting: false,
328
+ error: null,
329
+ start: mocks.start,
330
+ });
331
+
332
+ act(() => {
333
+ root.render(
334
+ <ConnectBuilderCard configured builderEnabled={false} connectUrl="" />,
335
+ );
336
+ });
337
+
338
+ expect(container.textContent).toContain("Connected to Acme workspace.");
339
+ });
340
+
296
341
  it("keeps cloud code-change send when OAuth and legacy keys both exist", () => {
297
342
  mocks.useBuilderConnectFlow.mockReturnValue({
298
343
  hasFetchedStatus: true,
@@ -82,6 +82,7 @@ export function ConnectBuilderCard({
82
82
  ? flow.builderEnabled
83
83
  : initialBuilderEnabled;
84
84
  const orgName = flow.statusResolved ? flow.orgName : (initialOrgName ?? null);
85
+ const connectedToName = orgName === "Fusion" ? "Builder.io" : orgName;
85
86
  const connecting = flow.connecting;
86
87
 
87
88
  const [waitlistJoined, setWaitlistJoined] = useState(false);
@@ -303,10 +304,10 @@ export function ConnectBuilderCard({
303
304
  Managed by this deployment — every user of this app uses the same
304
305
  Builder identity. {connectedCapabilityText} {connectedCapabilityAction}
305
306
  </>
306
- ) : orgName ? (
307
+ ) : connectedToName ? (
307
308
  <>
308
309
  Connected to{" "}
309
- <span className="font-medium text-foreground">{orgName}</span>.{" "}
310
+ <span className="font-medium text-foreground">{connectedToName}</span>.{" "}
310
311
  {connectedCapabilityText} {connectedCapabilityAction}
311
312
  </>
312
313
  ) : (
@@ -2966,6 +2966,9 @@ describe("MultiTabAssistantChat tab close/open lifecycle", () => {
2966
2966
 
2967
2967
  it("replaces an active missing thread with a fresh chat", async () => {
2968
2968
  const replacementId = "thread-replacement";
2969
+ window.history.replaceState({}, "", "/?thread=missing-thread");
2970
+ threadMocks.activeThreadId = "missing-thread";
2971
+ threadMocks.threads = [makeThread("missing-thread")];
2969
2972
  threadMocks.createThread.mockImplementationOnce(async () => {
2970
2973
  threadMocks.activeThreadId = replacementId;
2971
2974
  threadMocks.threads = [makeThread(replacementId), ...threadMocks.threads];
@@ -3000,6 +3003,15 @@ describe("MultiTabAssistantChat tab close/open lifecycle", () => {
3000
3003
  });
3001
3004
 
3002
3005
  expect(headerProps?.tabs.map((tab) => tab.id)).toEqual([replacementId]);
3006
+ expect(new URL(window.location.href).searchParams.has("thread")).toBe(
3007
+ false,
3008
+ );
3009
+ expect(chatThreadHookMocks.useChatThreads).toHaveBeenLastCalledWith(
3010
+ expect.any(String),
3011
+ "missing-thread-test",
3012
+ null,
3013
+ expect.objectContaining({ routeThreadId: undefined }),
3014
+ );
3003
3015
  });
3004
3016
 
3005
3017
  it("does not replace a desktop thread before identity restore settles", async () => {
@@ -989,9 +989,32 @@ export function MultiTabAssistantChat({
989
989
 
990
990
  const writeThreadUrl = useCallback(
991
991
  (threadId: string | null, options: { replace?: boolean } = {}): void => {
992
- if (!threadUrlSyncEnabled || typeof window === "undefined") return;
992
+ if (typeof window === "undefined") return;
993
+ const normalizedThreadId = normalizeUrlThreadId(threadId);
994
+ if (!threadUrlSyncEnabled) {
995
+ if (
996
+ !activeDeepLinkedThreadId ||
997
+ normalizedThreadId === activeDeepLinkedThreadId
998
+ ) {
999
+ return;
1000
+ }
1001
+ setActiveDeepLinkedThreadId(null);
1002
+ try {
1003
+ const url = new URL(window.location.href);
1004
+ url.searchParams.delete(DEFAULT_THREAD_URL_PARAM);
1005
+ url.searchParams.delete("threadId");
1006
+ window.history.replaceState(
1007
+ window.history.state,
1008
+ "",
1009
+ `${url.pathname}${url.search}${url.hash}`,
1010
+ );
1011
+ } catch (error) {
1012
+ console.error("Could not clear the stale thread URL:", error);
1013
+ }
1014
+ window.dispatchEvent(new Event(THREAD_URL_CHANGED_EVENT));
1015
+ return;
1016
+ }
993
1017
  try {
994
- const normalizedThreadId = normalizeUrlThreadId(threadId);
995
1018
  let next: string;
996
1019
  if (getThreadPath) {
997
1020
  next = getThreadPath(normalizedThreadId);
@@ -1035,6 +1058,7 @@ export function MultiTabAssistantChat({
1035
1058
  [
1036
1059
  getThreadPath,
1037
1060
  navigateThreadUrl,
1061
+ activeDeepLinkedThreadId,
1038
1062
  threadUrlParamName,
1039
1063
  threadUrlSyncEnabled,
1040
1064
  ],
@@ -93,6 +93,7 @@ describe("shared AgentKit integration submenu", () => {
93
93
  container.querySelector('button[aria-label="Add context"]')!,
94
94
  "ArrowDown",
95
95
  );
96
+ await key(row("Add context"), "ArrowRight");
96
97
  expect(row("Integrations").getAttribute("aria-haspopup")).toBe("menu");
97
98
  await key(row("Integrations"), "ArrowRight");
98
99
  }
@@ -100,7 +101,7 @@ describe("shared AgentKit integration submenu", () => {
100
101
  await render();
101
102
  await open();
102
103
  expect(pathname).toBe("/home");
103
- expect(document.querySelectorAll('[role="menu"]')).toHaveLength(2);
104
+ expect(document.querySelectorAll('[role="menu"]')).toHaveLength(3);
104
105
  expect(document.body.textContent).toContain(
105
106
  "No integrations available for this app.",
106
107
  );
@@ -129,6 +130,7 @@ describe("shared AgentKit integration submenu", () => {
129
130
  "ArrowDown",
130
131
  );
131
132
  expect(document.body.textContent).not.toContain("GitHub");
133
+ await key(row("Add context"), "ArrowRight");
132
134
  await key(row("Integrations"), "ArrowRight");
133
135
  expect(row("GitHub").querySelector("svg")).toBeNull();
134
136
  expect(row("Slack")).toBeDefined();
@@ -172,7 +174,7 @@ describe("shared AgentKit integration submenu", () => {
172
174
  await render();
173
175
  await open();
174
176
  await key(row("Connect an integration…"), "Escape");
175
- expect(document.querySelectorAll('[role="menu"]')).toHaveLength(1);
177
+ expect(document.querySelectorAll('[role="menu"]')).toHaveLength(2);
176
178
  expect(document.activeElement).toBe(row("Integrations"));
177
179
  expect(pathname).toBe("/home");
178
180
  });