Improve ask option rendering

This commit is contained in:
rimless-casualty
2026-06-01 17:37:26 +08:00
parent 8b619a2f40
commit 3c7d50d292
12 changed files with 431 additions and 73 deletions
@@ -31,4 +31,94 @@ describe("HookSelectorComponent", () => {
expect(visibleWidth(Bun.stripANSI(line))).toBeLessThanOrEqual(width);
}
});
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.",
"Option B: Keep dual support by migrating canonical instructions into .omp while also maintaining a root .github/copilot-instructions.md compatibility bridge for editors that do not understand OMP resources yet.",
];
const component = new HookSelectorComponent(
"Which migration stance should be used?",
options,
() => {},
() => {},
{ outline: true, initialIndex: 0 },
);
const width = 72;
const lines = component.render(width);
const plain = lines.map(line => Bun.stripANSI(line)).join("\n");
const normalizedPlain = plain.replace(/[\u2500-\u257f]/g, " ").replace(/\s+/g, " ");
expect(normalizedPlain).toContain("not creating a root .github directory");
expect(normalizedPlain).toContain("do not understand OMP resources yet");
for (const line of lines) {
expect(visibleWidth(Bun.stripANSI(line))).toBeLessThanOrEqual(width);
}
});
it("renders option descriptions as separate wrapped rows", () => {
const options = [
{
label: "Use existing local credentials",
description:
"Authenticate via the provider keys and OAuth state already configured under ~/.omp without opening a new browser-based setup flow.",
},
{
label: "Set up Oh My Pi in terminal",
description:
"Launch the local terminal UI to add provider keys, select models, and keep the current editor session waiting for the configured credentials.",
},
];
const component = new HookSelectorComponent(
"How should authentication continue?",
options,
() => {},
() => {},
{ outline: true, initialIndex: 0 },
);
const width = 76;
const lines = component.render(width);
const plainLines = lines.map(line => Bun.stripANSI(line));
const normalizedPlain = plainLines
.join("\n")
.replace(/[\u2500-\u257f]/g, " ")
.replace(/\s+/g, " ");
const labelLineIndex = plainLines.findIndex(line => line.includes("Use existing local credentials"));
const descriptionLineIndex = plainLines.findIndex(line => line.includes("Authenticate via the provider keys"));
expect(labelLineIndex).toBeGreaterThanOrEqual(0);
expect(descriptionLineIndex).toBeGreaterThan(labelLineIndex);
expect(normalizedPlain).toContain("without opening a new browser-based setup flow");
expect(normalizedPlain).toContain("keep the current editor session waiting for the configured credentials");
for (const line of lines) {
expect(visibleWidth(Bun.stripANSI(line))).toBeLessThanOrEqual(width);
}
});
it("filters options by description text", () => {
const component = new HookSelectorComponent(
"Which setup path should be used?",
[
{ label: "Path A", description: "Reuse the credentials already available in the environment." },
{ label: "Path B", description: "Launch a browser flow to authorize a new provider account." },
{ label: "Path C", description: "Open the local settings file and edit provider keys manually." },
{ label: "Path D", description: "Skip provider setup and continue with offline-only tools." },
],
() => {},
() => {},
{ outline: true, maxVisible: 3 },
);
for (const key of "browser") {
component.handleInput(key);
}
const plain = component
.render(76)
.map(line => Bun.stripANSI(line))
.join("\n");
expect(plain).toContain("Path B");
expect(plain).toContain("Launch a browser flow");
expect(plain).not.toContain("Path A");
});
});