Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,31 +25,70 @@ const withGap = (gap: number) => async (ctx: PlayContext) => {
const target = Math.max(0, row.getBoundingClientRect().left - frame.left - rail.getBoundingClientRect().width - gap);
rail.style.left = `${target}px`;
};
const gapPlay = withGap(4);

/// Gallery-only inset comparison. The production placement remains untouched; these plays are
/// exploratory edge positions for the pending railPlacement/useRailPlacement seam. The exact
/// choice is made from rendered gallery captures, not inferred from private-image scale.
const withInset = (inset: number) => async (ctx: PlayContext) => {
await ctx.waitFor(() => ctx.document.querySelector(".acpmux-minimap__rail:not(.acpmux-scrollmap)"));
const rail = ctx.document.querySelector<HTMLElement>(".acpmux-minimap__rail:not(.acpmux-scrollmap)");
if (!rail) return;
rail.style.left = `${inset}px`;
};

export const railInsetVariants = {
"inset-current": {
note: "Current product placement with no gallery override; this is the baseline for every inset comparison.",
snapshot: chat(rows, { title: "Thread minimap, current placement" }),
},
"inset-4-lawrence-candidate": {
note: "Exploratory 4 px pane-edge candidate informed by the supplied Lawrence crop; confirm the visual match in the rendered gallery.",
snapshot: chat(rows, { title: "Thread minimap, 4 px inset candidate" }),
play: withInset(4),
},
"inset-8-reference-candidate": {
note: "Exploratory 8 px pane-edge candidate informed by the four rail references; confirm the visual match in the rendered gallery before selecting a production token.",
snapshot: chat(rows, { title: "Thread minimap, 8 px inset candidate" }),
play: withInset(8),
},
"inset-12-alternative": {
note: "Exploratory 12 px pane-edge alternative, retained to show a looser edge step in the same harness.",
snapshot: chat(rows, { title: "Thread minimap, 12 px inset alternative" }),
play: withInset(12),
},
"inset-16-alternative": {
note: "Exploratory 16 px pane-edge alternative, the widest comparison before the rail starts consuming the chat gutter.",
snapshot: chat(rows, { title: "Thread minimap, 16 px inset alternative" }),
play: withInset(16),
},
} as const;

export const threadMinimapGalleryVariants = {
"gap-4-chosen": {
note: "Chosen. The rail keeps its full 36 px look and sits 4 px from the transcript at rest; it sheds its spare gutter first as Session Info narrows the chat.",
snapshot: chat(rows, { title: "Thread minimap, 4 px chosen gutter" }),
play: withGap(4),
},
"gap-8": {
note: "Alternative: an 8 px rail-to-text gutter, compared at wide, Session Info and narrow widths.",
snapshot: chat(rows, { title: "Thread minimap, 8 px gutter alternative" }),
play: withGap(8),
},
"gap-12": {
note: "Alternative: a 12 px rail-to-text gutter, kept here to show the extra padding Leo rejected.",
snapshot: chat(rows, { title: "Thread minimap, 12 px gutter alternative" }),
play: withGap(12),
},
...railInsetVariants,
} as const;

export default agentPaneEntry({
id: "agent-pane.thread-minimap-narrow",
title: "Thread minimap gutter variants",
title: "Thread minimap gutter and pane-edge inset variants",
area: "Agent pane",
height: 640,
widths: { narrow: 780, normal: 1040, wide: 1600 },
anchors: [{ selector: ".acpmux-scroll" }],
covers: ["agent-session/acpmux/threadMinimap/ThreadMinimap.tsx#ThreadMinimap"],
variants: {
"gap-4-chosen": {
note: "Chosen. The rail keeps its full 36 px look and sits 4 px from the transcript at rest; it sheds its spare gutter first as Session Info narrows the chat.",
snapshot: chat(rows, { title: "Thread minimap, 4 px chosen gutter" }),
play: gapPlay,
},
"gap-8": {
note: "Alternative: an 8 px rail-to-text gutter, compared at wide, Session Info and narrow widths.",
snapshot: chat(rows, { title: "Thread minimap, 8 px gutter alternative" }),
play: withGap(8),
},
"gap-12": {
note: "Alternative: a 12 px rail-to-text gutter, kept here to show the extra padding Leo rejected.",
snapshot: chat(rows, { title: "Thread minimap, 12 px gutter alternative" }),
play: withGap(12),
},
},
variants: threadMinimapGalleryVariants,
});
50 changes: 50 additions & 0 deletions webviews/test/thread-minimap-inset-variants.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { expect, test } from "bun:test";
import { JSDOM } from "jsdom";
import gallery from "../src/agent-session/acpmux/threadMinimap/threadMinimapNarrow.gallery";
import type { PlayContext } from "../src/gallery/play";

test.each([
["inset-current", 29],
["inset-4-lawrence-candidate", 4],
["inset-8-reference-candidate", 8],
["inset-12-alternative", 12],
["inset-16-alternative", 16],
["gap-4-chosen", 40],
["gap-8", 36],
["gap-12", 32],
] as const)("%s places the prompt rail at %i px", async (variantName, expectedLeft) => {
const dom = new JSDOM(`<!doctype html><div id="frame">
<nav class="acpmux-minimap__rail acpmux-scrollmap" style="left: 73px"></nav>
<nav id="prompt-rail" class="acpmux-minimap__rail" style="left: 29px"></nav>
<div class="acpmux-row"></div>
</div>`);
try {
const document = dom.window.document;
const rail = document.querySelector<HTMLElement>("#prompt-rail")!;
const scrollMap = document.querySelector<HTMLElement>(".acpmux-scrollmap")!;
const frame = document.querySelector<HTMLElement>("#frame")!;
const row = document.querySelector<HTMLElement>(".acpmux-row")!;
// jsdom has no layout. Keep a known transcript gutter for the pre-existing gap plays.
Object.defineProperty(rail, "offsetParent", { value: frame });
frame.getBoundingClientRect = () => new dom.window.DOMRect(20, 0, 780, 640);
row.getBoundingClientRect = () => new dom.window.DOMRect(100, 0, 600, 100);
rail.getBoundingClientRect = () => new dom.window.DOMRect(49, 0, 36, 180);
let waits = 0;
const ctx = {
document,
waitFor: async (condition: Parameters<PlayContext["waitFor"]>[0]) => {
waits += 1;
expect(condition()).toBe(rail);
},
} as PlayContext;
const variant = gallery.variants[variantName];
expect(variant).toBeDefined();
await variant!.play?.(ctx);

expect(rail.style.left).toBe(`${expectedLeft}px`);
expect(scrollMap.style.left).toBe("73px");
expect(waits).toBe(variantName === "inset-current" ? 0 : 1);
} finally {
dom.window.close();
}
});
Loading