@agent-native/toolkit 0.198.6 → 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.
- package/dist/app/chat/AgentPanel.d.ts +4 -3
- package/dist/app/chat/AgentPanel.d.ts.map +1 -1
- package/dist/app/chat/AgentPanel.js +14 -24
- package/dist/app/chat/AgentPanel.js.map +1 -1
- package/dist/app/chat/ConnectBuilderCard.d.ts.map +1 -1
- package/dist/app/chat/ConnectBuilderCard.js +2 -1
- package/dist/app/chat/ConnectBuilderCard.js.map +1 -1
- package/dist/app/chat/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/app/chat/MultiTabAssistantChat.js +21 -2
- package/dist/app/chat/MultiTabAssistantChat.js.map +1 -1
- package/dist/composer/ComposerContextMenu.d.ts +2 -2
- package/dist/composer/ComposerContextMenu.d.ts.map +1 -1
- package/dist/composer/ComposerContextMenu.js +54 -66
- package/dist/composer/ComposerContextMenu.js.map +1 -1
- package/dist/composer/ComposerPlusMenu.d.ts +2 -3
- package/dist/composer/ComposerPlusMenu.d.ts.map +1 -1
- package/dist/composer/ComposerPlusMenu.js +13 -278
- package/dist/composer/ComposerPlusMenu.js.map +1 -1
- package/dist/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/composer/TiptapComposer.js +3 -12
- package/dist/composer/TiptapComposer.js.map +1 -1
- package/dist/composer/use-composer-panel-placement.d.ts +0 -1
- package/dist/composer/use-composer-panel-placement.d.ts.map +1 -1
- package/dist/composer/use-composer-panel-placement.js +4 -5
- package/dist/composer/use-composer-panel-placement.js.map +1 -1
- package/package.json +3 -3
- package/src/app/chat/AgentPanel.header.spec.ts +25 -27
- package/src/app/chat/AgentPanel.overlay.spec.tsx +14 -51
- package/src/app/chat/AgentPanel.tsx +15 -26
- package/src/app/chat/ConnectBuilderCard.spec.tsx +45 -0
- package/src/app/chat/ConnectBuilderCard.tsx +3 -2
- package/src/app/chat/MultiTabAssistantChat.spec.tsx +12 -0
- package/src/app/chat/MultiTabAssistantChat.tsx +26 -2
- package/src/app/chat/composer/use-agentkit-integration-menu.spec.tsx +4 -2
- package/src/composer/ComposerContextMenu.spec.tsx +68 -60
- package/src/composer/ComposerContextMenu.tsx +93 -110
- package/src/composer/ComposerContextPicker.spec.tsx +16 -14
- package/src/composer/ComposerContextPickerDialog.spec.tsx +8 -6
- package/src/composer/ComposerPlusMenu.parity.spec.tsx +37 -75
- package/src/composer/ComposerPlusMenu.tsx +26 -450
- package/src/composer/PromptComposer.context.spec.tsx +112 -18
- package/src/composer/TiptapComposer.spec.ts +15 -0
- package/src/composer/TiptapComposer.tsx +2 -12
- package/src/composer/use-composer-panel-placement.ts +7 -5
- package/dist/composer/asset-picker-url.d.ts +0 -10
- package/dist/composer/asset-picker-url.d.ts.map +0 -1
- package/dist/composer/asset-picker-url.js +0 -56
- package/dist/composer/asset-picker-url.js.map +0 -1
- package/src/composer/asset-picker-url.spec.ts +0 -56
- package/src/composer/asset-picker-url.ts +0 -72
|
@@ -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;
|
|
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
|
-
//
|
|
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
|
-
|
|
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(
|
|
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":"AAAA,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAkB,MAAM,OAAO,CAAC;AAElE,
|
|
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.
|
|
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.
|
|
668
|
+
"@agent-native/agentkit": "^0.198.7"
|
|
669
669
|
},
|
|
670
670
|
"devDependencies": {
|
|
671
671
|
"@excalidraw/excalidraw": "0.18.1",
|
|
@@ -686,7 +686,7 @@
|
|
|
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.
|
|
689
|
+
"@agent-native/core": "^0.198.7"
|
|
690
690
|
},
|
|
691
691
|
"peerDependencies": {
|
|
692
692
|
"@agent-native/core": ">=0.196.0",
|
|
@@ -907,47 +907,45 @@ describe("AgentSidebar toggle routing", () => {
|
|
|
907
907
|
});
|
|
908
908
|
|
|
909
909
|
describe("AgentPanel header overflow actions", () => {
|
|
910
|
-
it("
|
|
911
|
-
const
|
|
912
|
-
const
|
|
913
|
-
|
|
914
|
-
|
|
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
|
-
|
|
919
|
-
|
|
920
|
-
|
|
916
|
+
deferAgentPanelOverlayOpen(
|
|
917
|
+
event,
|
|
918
|
+
() => events.push("menu closed"),
|
|
919
|
+
() => events.push("overlay opened"),
|
|
920
|
+
pendingOverlayRef,
|
|
921
|
+
);
|
|
921
922
|
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
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
|
-
|
|
929
|
-
expect(events).toEqual(["menu closed"]);
|
|
930
|
-
expect(frames).toHaveLength(1);
|
|
927
|
+
consumeAgentPanelOverlayFocusRestore(pendingOverlayRef, focusRestoreEvent);
|
|
931
928
|
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
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
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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(
|
|
66
|
-
|
|
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
|
-
|
|
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("
|
|
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
|
|
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
|
-
|
|
255
|
-
|
|
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
|
-
|
|
313
|
-
|
|
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
|
-
|
|
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:
|
|
303
|
+
pendingOverlayRef: { current: (() => void) | null },
|
|
317
304
|
event: { preventDefault: () => void },
|
|
318
305
|
): void {
|
|
319
|
-
|
|
320
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
1506
|
+
pendingHeaderOverlayRef,
|
|
1519
1507
|
)
|
|
1520
1508
|
}
|
|
1521
1509
|
>
|
|
@@ -1531,7 +1519,7 @@ function AgentPanelInner({
|
|
|
1531
1519
|
event,
|
|
1532
1520
|
closeHeaderMenuForOverlay,
|
|
1533
1521
|
toggleHistory,
|
|
1534
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1781
|
+
pendingHeaderOverlayRef,
|
|
1793
1782
|
)
|
|
1794
1783
|
}
|
|
1795
1784
|
>
|
|
@@ -1854,7 +1843,7 @@ function AgentPanelInner({
|
|
|
1854
1843
|
onPageHeaderVisibilityChange,
|
|
1855
1844
|
pageHeaderLeadingSlot,
|
|
1856
1845
|
pageToolbarSlot,
|
|
1857
|
-
|
|
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
|
-
) :
|
|
307
|
+
) : connectedToName ? (
|
|
307
308
|
<>
|
|
308
309
|
Connected to{" "}
|
|
309
|
-
<span className="font-medium text-foreground">{
|
|
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 (
|
|
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(
|
|
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(
|
|
177
|
+
expect(document.querySelectorAll('[role="menu"]')).toHaveLength(2);
|
|
176
178
|
expect(document.activeElement).toBe(row("Integrations"));
|
|
177
179
|
expect(pathname).toBe("/home");
|
|
178
180
|
});
|