refactor(coding-agent): standardized ui dialogs to use overlay panel base
- Introduce OverlayPanel and PanelDivider classes to standardize rounded-box inline overlays and section rules. - Migrate various UI dialog, selector, and panel components to inherit from OverlayPanel instead of Container. - Remove manual DynamicBorder elements and custom title/header components across migrated components. - Update test helpers and assertions to match the new overlay panel structure and rendering methods.
This commit is contained in:
@@ -348,7 +348,7 @@ describe("HookEditorComponent prompt-style mode", () => {
|
||||
expect(onCancel).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("renders prompt-style editor with legacy ask chrome", () => {
|
||||
it("renders prompt-style editor with rounded overlay chrome", () => {
|
||||
const component = new HookEditorComponent(createTui(), "Prompt", undefined, vi.fn(), vi.fn(), {
|
||||
promptStyle: true,
|
||||
});
|
||||
@@ -356,9 +356,9 @@ describe("HookEditorComponent prompt-style mode", () => {
|
||||
const rendered = renderText(component);
|
||||
const lines = renderLines(component);
|
||||
|
||||
expect(lines[0]).toMatch(/^─+$/);
|
||||
expect(lines.at(-1)).toMatch(/^─+$/);
|
||||
expect(lines[4]?.startsWith("> ")).toBe(true);
|
||||
expect(lines[0]).toMatch(/^╭─ Prompt .*╮$/);
|
||||
expect(lines.at(-1)).toMatch(/^╰.*╯$/);
|
||||
expect(lines.some(line => line.includes("> "))).toBe(true);
|
||||
expect(rendered).toContain("enter or ctrl+q submit esc cancel");
|
||||
expect(rendered).not.toContain("shift+enter newline");
|
||||
expect(rendered).toContain("ctrl+g external editor");
|
||||
@@ -386,8 +386,9 @@ describe("HookEditorComponent prompt-style mode", () => {
|
||||
}
|
||||
|
||||
const lines = renderLines(component);
|
||||
expect(lines[4]?.startsWith("> hello")).toBe(true);
|
||||
expect(lines[4]?.startsWith("hello")).toBe(false);
|
||||
|
||||
expect(lines.some(line => line.includes("> hello"))).toBe(true);
|
||||
expect(lines.some(line => line.includes("hello") && !line.includes(">"))).toBe(false);
|
||||
});
|
||||
|
||||
it("aligns wrapped prompt-style continuation rows under the text column", () => {
|
||||
@@ -396,9 +397,11 @@ describe("HookEditorComponent prompt-style mode", () => {
|
||||
});
|
||||
|
||||
const lines = renderLines(component, 12);
|
||||
expect(lines[4]).toBe("> abcdefghij");
|
||||
expect(lines[5]?.startsWith(" klm")).toBe(true);
|
||||
expect(lines[5]?.startsWith(">")).toBe(false);
|
||||
|
||||
expect(lines.some(line => line.includes("> abcdef"))).toBe(true);
|
||||
const continuation = lines.find(line => line.includes("ghijkl"));
|
||||
expect(continuation).toBeDefined();
|
||||
expect(continuation).not.toContain(">");
|
||||
});
|
||||
|
||||
it("cancels on Escape", () => {
|
||||
@@ -432,24 +435,20 @@ describe("HookEditorComponent prompt-style mode", () => {
|
||||
expect(onSubmit).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("aligns the title and hint with the editor prompt gutter at column zero (#5313)", () => {
|
||||
it("renders the title in the border, detail lines, hint, and prompt gutter", () => {
|
||||
const title = "◆ Other (type your own)\nEnter your response:";
|
||||
const component = new HookEditorComponent(createTui(), title, "不太清楚,", vi.fn(), vi.fn(), {
|
||||
promptStyle: true,
|
||||
});
|
||||
const lines = renderLines(component);
|
||||
|
||||
const titleRow = lines.find(line => line.includes("Enter your response:"));
|
||||
const gutterRow = lines.find(line => line.startsWith("> "));
|
||||
const hintRow = lines.find(line => line.includes("esc cancel"));
|
||||
|
||||
expect(titleRow).toBeDefined();
|
||||
expect(gutterRow).toBeDefined();
|
||||
expect(hintRow).toBeDefined();
|
||||
// The borderless prompt-style editor renders `> ` starting at column 0, so
|
||||
// the surrounding title/hint chrome must not carry a leading indent.
|
||||
expect(titleRow!.startsWith("Enter your response:")).toBe(true);
|
||||
expect(hintRow!.startsWith(" ")).toBe(false);
|
||||
// First title line insets into the top border row.
|
||||
expect(lines[0]).toContain("Other (type your own)");
|
||||
// Remaining title lines, gutter, and hint are body rows.
|
||||
const content = component.renderContent(80).map(line => Bun.stripANSI(line));
|
||||
expect(content.some(line => line.startsWith("Enter your response:"))).toBe(true);
|
||||
expect(content.some(line => line.startsWith("> "))).toBe(true);
|
||||
expect(content.some(line => line.includes("esc cancel"))).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -32,6 +32,19 @@ describe("HookSelectorComponent", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("renders a multiline title in the border and body without dropping detail", () => {
|
||||
const component = new HookSelectorComponent(
|
||||
"Delete session?\nsession-2026-08-20",
|
||||
["Delete", "Cancel"],
|
||||
() => {},
|
||||
() => {},
|
||||
);
|
||||
|
||||
const lines = component.render(80).map(line => Bun.stripANSI(line));
|
||||
expect(lines[0]).toContain("Delete session?");
|
||||
expect(lines.some(line => line.includes("session-2026-08-20"))).toBe(true);
|
||||
});
|
||||
|
||||
it("wraps outlined option text without omitting the tail", () => {
|
||||
const options = [
|
||||
"Option A: Move to OMP-native only by migrating reusable shared AI instructions into .omp/AGENTS.md, .omp/rules, .omp/skills, and .omp/agents while deliberately not creating a root .github directory.",
|
||||
|
||||
@@ -32,7 +32,7 @@ describe("LoginDialogComponent", () => {
|
||||
dialog.showAuth(authorizationUrl);
|
||||
const linkTarget = `${authorizationUrl}\x07`;
|
||||
const urlRows = dialog
|
||||
.render(40)
|
||||
.renderContent(40)
|
||||
.filter(line => line.includes(linkTarget) && !Bun.stripANSI(line).includes("click to open"));
|
||||
|
||||
expect(urlRows.length).toBeGreaterThan(1);
|
||||
|
||||
@@ -34,7 +34,7 @@ function renderStripped(tree: SessionTreeNode[], leafId: string, width = 120): s
|
||||
() => {},
|
||||
() => {},
|
||||
);
|
||||
return selector.render(width).map(line => Bun.stripANSI(line));
|
||||
return selector.renderContent(width).map(line => Bun.stripANSI(line));
|
||||
}
|
||||
|
||||
describe("issue #7332: linear branch continuations stay compact", () => {
|
||||
|
||||
+1
-1
@@ -44,7 +44,7 @@ function renderStripped(tree: SessionTreeNode[], leafId: string, width = 120): s
|
||||
() => {},
|
||||
() => {},
|
||||
);
|
||||
return selector.render(width).map(line => Bun.stripANSI(line));
|
||||
return selector.renderContent(width).map(line => Bun.stripANSI(line));
|
||||
}
|
||||
|
||||
// A terminal branch whose linear chain branches again must keep every row at
|
||||
|
||||
@@ -39,7 +39,7 @@ function renderSelector(tree: SessionTreeNode, leafId: string, width: number): s
|
||||
() => {},
|
||||
() => {},
|
||||
);
|
||||
return selector.render(width).map(line => Bun.stripANSI(line));
|
||||
return selector.renderContent(width).map(line => Bun.stripANSI(line));
|
||||
}
|
||||
|
||||
describe("TreeSelectorComponent deep branching overflow", () => {
|
||||
|
||||
Reference in New Issue
Block a user