fix(ask): used full dialog width for wrapped labels
This commit is contained in:
@@ -317,14 +317,12 @@ function renderRowLabel(
|
||||
const cursor = selected ? theme.fg("accent", `${theme.nav.cursor} `) : " ";
|
||||
const label = renderInlineMarkdown(rowItem.label, mdTheme, t => theme.fg(color, t));
|
||||
const noteMarker = state.note && state.noteRowKey === rowItem.key ? theme.fg("success", " ✎ note") : "";
|
||||
// Wrap the label onto continuation lines indented under the marker so the
|
||||
// cursor stays visually anchored and the full label stays readable (the
|
||||
// body ScrollView carries vertical overflow). The note marker is reserved
|
||||
// on the first line; the label wraps to the remaining width. The row()
|
||||
// chrome (borders + insets) consumes 4 columns, so the label must fit
|
||||
// `width - 4` or the outer fit() re-truncates it with an ellipsis.
|
||||
// `width` is already the inner content width consumed by row(); when a
|
||||
// scrollbar is needed, renderRows() calls this again with one less column.
|
||||
// Keep the cursor, option marker, first wrapped label line, and optional
|
||||
// note marker within that budget so the outer fit() never truncates them.
|
||||
const noteWidth = noteMarker ? visibleWidth(noteMarker) : 0;
|
||||
const labelWidth = Math.max(1, width - 4 - visibleWidth(cursor) - visibleWidth(marker) - noteWidth);
|
||||
const labelWidth = Math.max(1, width - visibleWidth(cursor) - visibleWidth(marker) - noteWidth);
|
||||
const wrappedLabel = wrapTextWithAnsi(label, labelWidth);
|
||||
const indent = padding(visibleWidth(cursor) + visibleWidth(marker));
|
||||
const lines = [`${cursor}${marker}${wrappedLabel[0] ?? ""}${noteMarker}`];
|
||||
|
||||
@@ -1252,7 +1252,8 @@ describe("AskDialogComponent", () => {
|
||||
|
||||
it("wraps long option labels onto indented continuation lines instead of truncating", () => {
|
||||
const onSubmit = vi.fn();
|
||||
const longLabel = "This is a deliberately long option label ".repeat(4).trim();
|
||||
const tail = "UNIQUE_TAIL_MARKER_8654";
|
||||
const longLabel = `${"This is a deliberately long option label ".repeat(4)}${tail}`;
|
||||
const questions: ExtensionAskDialogQuestion[] = [
|
||||
{
|
||||
id: "q1",
|
||||
@@ -1268,8 +1269,8 @@ describe("AskDialogComponent", () => {
|
||||
});
|
||||
|
||||
const output = render(component);
|
||||
// The label tail must be present — no ellipsis truncation.
|
||||
expect(output).toContain("deliberately long option label");
|
||||
// The unique label tail must be present — no ellipsis truncation.
|
||||
expect(output).toContain(tail);
|
||||
expect(output).not.toContain("…");
|
||||
// The first line carries the cursor glyph; continuation lines are
|
||||
// indented under the marker so the cursor stays visually anchored.
|
||||
@@ -1280,6 +1281,16 @@ describe("AskDialogComponent", () => {
|
||||
expect(continuation).toMatch(/│ {3}/);
|
||||
});
|
||||
|
||||
it("does not wrap an option label that fits the dialog content width", () => {
|
||||
const component = new AskDialogComponent(
|
||||
[{ id: "q1", question: "Pick one?", options: [{ label: "x".repeat(70) }] }],
|
||||
{ onSubmit: vi.fn(), onCancel: vi.fn(), onPrompt: vi.fn() },
|
||||
);
|
||||
|
||||
const output = render(component);
|
||||
expect(output.split("\n").filter(line => line.includes("x"))).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("Other editor cancel returns to the option list without submitting", async () => {
|
||||
const onPrompt = vi.fn().mockReturnValue(Promise.resolve(undefined));
|
||||
const onSubmit = vi.fn();
|
||||
|
||||
Reference in New Issue
Block a user