Skip to content

Commit ce90563

Browse files
Preserve diff surface when toggling right panel (pingdotgg#3083)
1 parent 31e6822 commit ce90563

3 files changed

Lines changed: 97 additions & 12 deletions

File tree

apps/web/src/components/ChatView.browser.tsx

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1546,6 +1546,18 @@ function dispatchChatNewShortcut(): void {
15461546
);
15471547
}
15481548

1549+
function dispatchConfiguredDiffToggleShortcut(): void {
1550+
window.dispatchEvent(
1551+
new KeyboardEvent("keydown", {
1552+
key: "g",
1553+
shiftKey: true,
1554+
altKey: true,
1555+
bubbles: true,
1556+
cancelable: true,
1557+
}),
1558+
);
1559+
}
1560+
15491561
function releaseModShortcut(key?: string): void {
15501562
window.dispatchEvent(
15511563
new KeyboardEvent("keyup", {
@@ -3383,6 +3395,76 @@ describe("ChatView timeline estimator parity (full app)", () => {
33833395
}
33843396
});
33853397

3398+
it("uses the configured diff toggle binding without discarding its surface", async () => {
3399+
const mounted = await mountChatView({
3400+
viewport: DEFAULT_VIEWPORT,
3401+
snapshot: createSnapshotForTargetUser({
3402+
targetMessageId: "msg-user-target-diff-hotkey" as MessageId,
3403+
targetText: "diff hotkey target",
3404+
}),
3405+
configureFixture: (nextFixture) => {
3406+
nextFixture.serverConfig = {
3407+
...nextFixture.serverConfig,
3408+
keybindings: [
3409+
{
3410+
command: "diff.toggle",
3411+
shortcut: {
3412+
key: "g",
3413+
metaKey: false,
3414+
ctrlKey: false,
3415+
shiftKey: true,
3416+
altKey: true,
3417+
modKey: false,
3418+
},
3419+
whenAst: {
3420+
type: "not",
3421+
node: { type: "identifier", name: "terminalFocus" },
3422+
},
3423+
},
3424+
],
3425+
};
3426+
},
3427+
});
3428+
3429+
try {
3430+
await waitForServerConfigToApply();
3431+
dispatchConfiguredDiffToggleShortcut();
3432+
await vi.waitFor(() => {
3433+
expect(
3434+
selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, THREAD_REF),
3435+
).toEqual({
3436+
isOpen: true,
3437+
activeSurfaceId: "diff",
3438+
surfaces: [{ id: "diff", kind: "diff" }],
3439+
});
3440+
});
3441+
3442+
dispatchConfiguredDiffToggleShortcut();
3443+
await vi.waitFor(() => {
3444+
expect(
3445+
selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, THREAD_REF),
3446+
).toEqual({
3447+
isOpen: false,
3448+
activeSurfaceId: "diff",
3449+
surfaces: [{ id: "diff", kind: "diff" }],
3450+
});
3451+
});
3452+
3453+
dispatchConfiguredDiffToggleShortcut();
3454+
await vi.waitFor(() => {
3455+
expect(
3456+
selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, THREAD_REF),
3457+
).toEqual({
3458+
isOpen: true,
3459+
activeSurfaceId: "diff",
3460+
surfaces: [{ id: "diff", kind: "diff" }],
3461+
});
3462+
});
3463+
} finally {
3464+
await mounted.cleanup();
3465+
}
3466+
});
3467+
33863468
it("focuses the composer and inserts printable text typed from the page background", async () => {
33873469
const mounted = await mountChatView({
33883470
viewport: DEFAULT_VIEWPORT,

apps/web/src/components/ChatView.tsx

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2263,9 +2263,9 @@ export default function ChatView(props: ChatViewProps) {
22632263
// openness; when both fight, `selectActiveRightPanelKindWithUrl` lets
22642264
// diff win (URL is truth).
22652265
// - The two toggles below treat the panels as mutually exclusive: opening
2266-
// one always tears down the other in BOTH the URL and the store. Without
2267-
// this, e.g. clicking the browser button while diff is URL-pinned would
2268-
// just toggle the (overridden) store value and look like a no-op.
2266+
// one updates BOTH the URL and the store. Without this, e.g. clicking the
2267+
// browser button while diff is URL-pinned would just toggle the
2268+
// (overridden) store value and look like a no-op.
22692269
const onTogglePreview = useCallback(() => {
22702270
if (!activeThreadRef) return;
22712271
if (previewPanelOpen) {
@@ -2303,11 +2303,9 @@ export default function ChatView(props: ChatViewProps) {
23032303
const diffPanelOpen = activeRightPanelKind === "diff";
23042304
if (!diffPanelOpen) {
23052305
onDiffPanelOpen?.();
2306-
if (activeThreadRef) {
2307-
useRightPanelStore.getState().open(activeThreadRef, "diff");
2308-
}
2309-
} else if (activeThreadRef) {
2310-
useRightPanelStore.getState().closeSurface(activeThreadRef, "diff");
2306+
}
2307+
if (activeThreadRef) {
2308+
useRightPanelStore.getState().toggle(activeThreadRef, "diff");
23112309
}
23122310
void navigate({
23132311
to: "/$environmentId/$threadId",

apps/web/src/rightPanelStore.test.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -111,11 +111,16 @@ describe("rightPanelStore", () => {
111111
expect(useRightPanelStore.getState().byThreadKey).toEqual({});
112112
});
113113

114-
it("toggle opens then closes the same kind", () => {
115-
useRightPanelStore.getState().toggle(refA, "preview");
116-
expect(selectActiveRightPanel(useRightPanelStore.getState().byThreadKey, refA)).toBe("preview");
117-
useRightPanelStore.getState().toggle(refA, "preview");
114+
it("toggle hides the panel without discarding the active surface", () => {
115+
useRightPanelStore.getState().toggle(refA, "diff");
116+
expect(selectActiveRightPanel(useRightPanelStore.getState().byThreadKey, refA)).toBe("diff");
117+
useRightPanelStore.getState().toggle(refA, "diff");
118118
expect(selectActiveRightPanel(useRightPanelStore.getState().byThreadKey, refA)).toBeNull();
119+
expect(selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, refA)).toEqual({
120+
isOpen: false,
121+
activeSurfaceId: "diff",
122+
surfaces: [{ id: "diff", kind: "diff" }],
123+
});
119124
});
120125

121126
it("toggle to a different kind switches active", () => {

0 commit comments

Comments
 (0)