diff --git a/src/cli/plugins-cli.ts b/src/cli/plugins-cli.ts index 62bc76889..303231bdb 100644 --- a/src/cli/plugins-cli.ts +++ b/src/cli/plugins-cli.ts @@ -135,19 +135,22 @@ export function registerPluginsCli(program: Command) { if (!opts.verbose) { const tableWidth = Math.max(60, (process.stdout.columns ?? 120) - 1); - const rows = list.map((plugin) => ({ - Name: plugin.name || plugin.id, - ID: plugin.name && plugin.name !== plugin.id ? plugin.id : "", - Status: - plugin.status === "loaded" - ? theme.success("loaded") - : plugin.status === "disabled" - ? theme.warn("disabled") - : theme.error("error"), - Source: plugin.source, - Version: plugin.version ?? "", - Description: plugin.description ?? "", - })); + const rows = list.map((plugin) => { + const desc = plugin.description ? theme.muted(plugin.description) : ""; + const sourceLine = desc ? `${plugin.source}\n${desc}` : plugin.source; + return { + Name: plugin.name || plugin.id, + ID: plugin.name && plugin.name !== plugin.id ? plugin.id : "", + Status: + plugin.status === "loaded" + ? theme.success("loaded") + : plugin.status === "disabled" + ? theme.warn("disabled") + : theme.error("error"), + Source: sourceLine, + Version: plugin.version ?? "", + }; + }); defaultRuntime.log( renderTable({ width: tableWidth, @@ -155,9 +158,8 @@ export function registerPluginsCli(program: Command) { { key: "Name", header: "Name", minWidth: 14, flex: true }, { key: "ID", header: "ID", minWidth: 10, flex: true }, { key: "Status", header: "Status", minWidth: 10 }, - { key: "Source", header: "Source", minWidth: 10 }, + { key: "Source", header: "Source", minWidth: 26, flex: true }, { key: "Version", header: "Version", minWidth: 8 }, - { key: "Description", header: "Description", minWidth: 18, flex: true }, ], rows, }).trimEnd(), diff --git a/src/terminal/table.test.ts b/src/terminal/table.test.ts index ee7a9fcc9..c2108accb 100644 --- a/src/terminal/table.test.ts +++ b/src/terminal/table.test.ts @@ -84,4 +84,21 @@ describe("renderTable", () => { throw new Error(`Unexpected escape sequence at index ${i}`); } }); + + it("respects explicit newlines in cell values", () => { + const out = renderTable({ + width: 48, + columns: [ + { key: "A", header: "A", minWidth: 6 }, + { key: "B", header: "B", minWidth: 10, flex: true }, + ], + rows: [{ A: "row", B: "line1\nline2" }], + }); + + const lines = out.trimEnd().split("\n"); + const line1Index = lines.findIndex((line) => line.includes("line1")); + const line2Index = lines.findIndex((line) => line.includes("line2")); + expect(line1Index).toBeGreaterThan(-1); + expect(line2Index).toBe(line1Index + 1); + }); }); diff --git a/src/terminal/table.ts b/src/terminal/table.ts index 121760957..f0ea1cb02 100644 --- a/src/terminal/table.ts +++ b/src/terminal/table.ts @@ -93,15 +93,9 @@ function wrapLine(text: string, width: number): string[] { const lines: string[] = []; const isBreakChar = (ch: string) => - ch === " " || - ch === "\t" || - ch === "\n" || - ch === "\r" || - ch === "/" || - ch === "-" || - ch === "_" || - ch === "."; + ch === " " || ch === "\t" || ch === "/" || ch === "-" || ch === "_" || ch === "."; const isSpaceChar = (ch: string) => ch === " " || ch === "\t"; + let skipNextLf = false; const buf: Token[] = []; let bufVisible = 0; @@ -149,6 +143,15 @@ function wrapLine(text: string, width: number): string[] { } const ch = token.value; + if (skipNextLf) { + skipNextLf = false; + if (ch === "\n") continue; + } + if (ch === "\n" || ch === "\r") { + flushAt(buf.length); + if (ch === "\r") skipNextLf = true; + continue; + } if (bufVisible + 1 > width && bufVisible > 0) { flushAt(lastBreakIndex); }