From 6f1a88949e127645ec57abb58fc830b3c1c7357d Mon Sep 17 00:00:00 2001 From: Angelo Ashmore Date: Fri, 10 Apr 2026 12:53:16 -1000 Subject: [PATCH 1/4] feat: add a consistent table formatter for tabular output Co-Authored-By: Claude Opus 4.6 (1M context) --- src/commands/docs-list.ts | 12 +++++------- src/commands/locale-list.ts | 8 +++++--- src/commands/preview-list.ts | 6 +++--- src/commands/repo-list.ts | 9 +++++---- src/commands/slice-list.ts | 6 +++--- src/commands/slice-view.ts | 8 +++++--- src/commands/token-list.ts | 15 ++++++++------- src/commands/type-list.ts | 8 +++++--- src/commands/type-view.ts | 8 +++++--- src/commands/webhook-list.ts | 8 +++++--- src/lib/command.ts | 27 ++++++++------------------- src/lib/string.ts | 17 +++++++++++++++++ test/slice-list.test.ts | 2 +- test/slice-view.test.ts | 7 +++---- test/type-list.test.ts | 4 ++-- test/type-view.test.ts | 7 +++---- 16 files changed, 83 insertions(+), 69 deletions(-) diff --git a/src/commands/docs-list.ts b/src/commands/docs-list.ts index d968ffa..82e32c0 100644 --- a/src/commands/docs-list.ts +++ b/src/commands/docs-list.ts @@ -2,6 +2,7 @@ import { getDocsIndex, getDocsPageIndex } from "../clients/docs"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; import { stringify } from "../lib/json"; import { NotFoundRequestError, UnknownRequestError } from "../lib/request"; +import { formatTable } from "../lib/string"; const config = { name: "prismic docs list", @@ -52,9 +53,8 @@ export default createCommand(config, async ({ positionals, values }) => { return; } - for (const anchor of entry.anchors) { - console.info(`${path}#${anchor.slug}: ${anchor.excerpt}`); - } + const rows = entry.anchors.map((anchor) => [`${path}#${anchor.slug}`, anchor.excerpt]); + console.info(formatTable(rows)); } else { let pages; try { @@ -79,9 +79,7 @@ export default createCommand(config, async ({ positionals, values }) => { return; } - for (const page of pages) { - const description = page.description ? ` — ${page.description}` : ""; - console.info(`${page.path}: ${page.title}${description}`); - } + const rows = pages.map((page) => [page.path, page.title, page.description ?? ""]); + console.info(formatTable(rows)); } }); diff --git a/src/commands/locale-list.ts b/src/commands/locale-list.ts index 64b63c8..d1a7907 100644 --- a/src/commands/locale-list.ts +++ b/src/commands/locale-list.ts @@ -2,6 +2,7 @@ import { getHost, getToken } from "../auth"; import { getLocales } from "../clients/locale"; import { createCommand, type CommandConfig } from "../lib/command"; import { stringify } from "../lib/json"; +import { formatTable } from "../lib/string"; import { getRepositoryName } from "../project"; const config = { @@ -36,8 +37,9 @@ export default createCommand(config, async ({ values }) => { return; } - for (const locale of locales) { + const rows = locales.map((locale) => { const masterLabel = locale.isMaster ? " (master)" : ""; - console.info(`${locale.id} ${locale.label}${masterLabel}`); - } + return [locale.id, `${locale.label}${masterLabel}`]; + }); + console.info(formatTable(rows)); }); diff --git a/src/commands/preview-list.ts b/src/commands/preview-list.ts index 7744489..0666fbc 100644 --- a/src/commands/preview-list.ts +++ b/src/commands/preview-list.ts @@ -2,6 +2,7 @@ import { getHost, getToken } from "../auth"; import { getPreviews, getSimulatorUrl } from "../clients/core"; import { createCommand, type CommandConfig } from "../lib/command"; import { stringify } from "../lib/json"; +import { formatTable } from "../lib/string"; import { getRepositoryName } from "../project"; const config = { @@ -44,9 +45,8 @@ export default createCommand(config, async ({ values }) => { return; } - for (const preview of previews) { - console.info(`${preview.url} ${preview.label}`); - } + const rows = previews.map((preview) => [preview.url, preview.label]); + console.info(formatTable(rows)); if (simulatorUrl) { console.info(`\nSimulator: ${simulatorUrl}`); diff --git a/src/commands/repo-list.ts b/src/commands/repo-list.ts index db67818..135f73d 100644 --- a/src/commands/repo-list.ts +++ b/src/commands/repo-list.ts @@ -3,6 +3,7 @@ import { getProfile } from "../clients/user"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; import { stringify } from "../lib/json"; import { UnknownRequestError } from "../lib/request"; +import { formatTable } from "../lib/string"; const config = { name: "prismic repo list", @@ -50,9 +51,9 @@ export default createCommand(config, async ({ values }) => { return; } - for (const repo of repos) { + const rows = repos.map((repo) => { const name = repo.name || "(no name)"; - const role = repo.role ? ` ${repo.role}` : ""; - console.info(`${repo.domain} ${name}${role}`); - } + return [repo.domain, name, repo.role ?? ""]; + }); + console.info(formatTable(rows)); }); diff --git a/src/commands/slice-list.ts b/src/commands/slice-list.ts index 55f4736..20c6694 100644 --- a/src/commands/slice-list.ts +++ b/src/commands/slice-list.ts @@ -2,6 +2,7 @@ import { getHost, getToken } from "../auth"; import { getSlices } from "../clients/custom-types"; import { createCommand, type CommandConfig } from "../lib/command"; import { stringify } from "../lib/json"; +import { formatTable } from "../lib/string"; import { getRepositoryName } from "../project"; const config = { @@ -30,7 +31,6 @@ export default createCommand(config, async ({ values }) => { return; } - for (const slice of slices) { - console.info(`${slice.name} (id: ${slice.id})`); - } + const rows = slices.map((slice) => [slice.name, slice.id]); + console.info(formatTable(rows)); }); diff --git a/src/commands/slice-view.ts b/src/commands/slice-view.ts index 5063e1b..7b7ff4f 100644 --- a/src/commands/slice-view.ts +++ b/src/commands/slice-view.ts @@ -2,6 +2,7 @@ import { getHost, getToken } from "../auth"; import { getSlices } from "../clients/custom-types"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; import { stringify } from "../lib/json"; +import { formatTable } from "../lib/string"; import { getRepositoryName } from "../project"; const config = { @@ -44,12 +45,13 @@ export default createCommand(config, async ({ positionals, values }) => { if (entries.length === 0) { console.info(" (no fields)"); } else { - for (const [id, field] of entries) { + const rows = entries.map(([id, field]) => { const config = field.config as Record | undefined; const label = (config?.label as string) || ""; const placeholder = config?.placeholder ? `"${config.placeholder}"` : ""; - console.info(` ${[id, field.type, label, placeholder].filter(Boolean).join(" ")}`); - } + return [` ${id}`, field.type, label, placeholder]; + }); + console.info(formatTable(rows)); } } }); diff --git a/src/commands/token-list.ts b/src/commands/token-list.ts index 102dd11..41386c2 100644 --- a/src/commands/token-list.ts +++ b/src/commands/token-list.ts @@ -2,6 +2,7 @@ import { getHost, getToken } from "../auth"; import { getOAuthApps, getWriteTokens } from "../clients/wroom"; import { createCommand, type CommandConfig } from "../lib/command"; import { stringify } from "../lib/json"; +import { formatTable } from "../lib/string"; import { getRepositoryName } from "../project"; const config = { @@ -46,9 +47,8 @@ export default createCommand(config, async ({ values }) => { if (accessTokens.length > 0) { console.info("ACCESS TOKENS"); - for (const accessToken of accessTokens) { - console.info(` ${accessToken.name} ${accessToken.scope} ${accessToken.token} ${accessToken.createdAt}`); - } + const rows = accessTokens.map((t) => [` ${t.name}`, t.scope, t.token, t.createdAt]); + console.info(formatTable(rows)); } else { console.info("ACCESS TOKENS (none)"); } @@ -57,10 +57,11 @@ export default createCommand(config, async ({ values }) => { if (writeTokens.length > 0) { console.info("WRITE TOKENS"); - for (const writeToken of writeTokens) { - const date = new Date(writeToken.timestamp * 1000).toISOString().split("T")[0]; - console.info(` ${writeToken.app_name} ${writeToken.token} ${date}`); - } + const rows = writeTokens.map((t) => { + const date = new Date(t.timestamp * 1000).toISOString().split("T")[0]; + return [` ${t.app_name}`, t.token, date]; + }); + console.info(formatTable(rows)); } else { console.info("WRITE TOKENS (none)"); } diff --git a/src/commands/type-list.ts b/src/commands/type-list.ts index 59b5137..4db3adf 100644 --- a/src/commands/type-list.ts +++ b/src/commands/type-list.ts @@ -2,6 +2,7 @@ import { getHost, getToken } from "../auth"; import { getCustomTypes } from "../clients/custom-types"; import { createCommand, type CommandConfig } from "../lib/command"; import { stringify } from "../lib/json"; +import { formatTable } from "../lib/string"; import { getRepositoryName } from "../project"; const config = { @@ -31,8 +32,9 @@ export default createCommand(config, async ({ values }) => { return; } - for (const type of types) { + const rows = types.map((type) => { const label = type.label || "(no name)"; - console.info(`${label} (id: ${type.id}, format: ${type.format})`); - } + return [label, type.id, type.format ?? ""]; + }); + console.info(formatTable(rows)); }); diff --git a/src/commands/type-view.ts b/src/commands/type-view.ts index a8a5183..fbdc1ef 100644 --- a/src/commands/type-view.ts +++ b/src/commands/type-view.ts @@ -2,6 +2,7 @@ import { getHost, getToken } from "../auth"; import { getCustomTypes } from "../clients/custom-types"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; import { stringify } from "../lib/json"; +import { formatTable } from "../lib/string"; import { getRepositoryName } from "../project"; const config = { @@ -46,12 +47,13 @@ export default createCommand(config, async ({ positionals, values }) => { if (entries.length === 0) { console.info(" (no fields)"); } else { - for (const [id, field] of entries) { + const rows = entries.map(([id, field]) => { const config = field.config as Record | undefined; const label = (config?.label as string) || ""; const placeholder = config?.placeholder ? `"${config.placeholder}"` : ""; - console.info(` ${[id, field.type, label, placeholder].filter(Boolean).join(" ")}`); - } + return [` ${id}`, field.type, label, placeholder]; + }); + console.info(formatTable(rows)); } } }); diff --git a/src/commands/webhook-list.ts b/src/commands/webhook-list.ts index 7f6266e..a726b47 100644 --- a/src/commands/webhook-list.ts +++ b/src/commands/webhook-list.ts @@ -2,6 +2,7 @@ import { getHost, getToken } from "../auth"; import { getWebhooks } from "../clients/wroom"; import { createCommand, type CommandConfig } from "../lib/command"; import { stringify } from "../lib/json"; +import { formatTable } from "../lib/string"; import { getRepositoryName } from "../project"; const config = { @@ -35,9 +36,10 @@ export default createCommand(config, async ({ values }) => { return; } - for (const webhook of webhooks) { + const rows = webhooks.map((webhook) => { const status = webhook.config.active ? "enabled" : "disabled"; const name = webhook.config.name ? ` (${webhook.config.name})` : ""; - console.info(`${webhook.config.url}${name} [${status}]`); - } + return [`${webhook.config.url}${name}`, `[${status}]`]; + }); + console.info(formatTable(rows)); }); diff --git a/src/lib/command.ts b/src/lib/command.ts index 6c9de2c..666c494 100644 --- a/src/lib/command.ts +++ b/src/lib/command.ts @@ -2,7 +2,7 @@ import type { ParseArgsOptionDescriptor } from "node:util"; import { parseArgs } from "node:util"; -import { dedent } from "./string"; +import { dedent, formatTable } from "./string"; export type CommandConfig = { name: string; @@ -89,16 +89,13 @@ function buildCommandHelp(config: CommandConfig): string { if (positionalNames.length > 0) { lines.push(""); lines.push("ARGUMENTS"); - const maxNameLength = Math.max( - ...positionalNames.map((positionalName) => `<${positionalName}>`.length), - ); + const rows: string[][] = []; for (const positionalName in positionals) { - const formattedName = `<${positionalName}>`; - const paddedName = formattedName.padEnd(maxNameLength); const positional = positionals[positionalName]; const description = positional.description + (positional.required ? " (required)" : ""); - lines.push(` ${paddedName} ${description}`); + rows.push([` <${positionalName}>`, description]); } + lines.push(formatTable(rows)); } lines.push(""); @@ -116,11 +113,8 @@ function buildCommandHelp(config: CommandConfig): string { } } optionEntries.push({ left: "-h, --help", description: "Show help for command" }); - const maxOptionLength = Math.max(...optionEntries.map((optionEntry) => optionEntry.left.length)); - for (const optionEntry of optionEntries) { - const paddedLeft = optionEntry.left.padEnd(maxOptionLength); - lines.push(` ${paddedLeft} ${optionEntry.description}`); - } + const optionRows = optionEntries.map((entry) => [` ${entry.left}`, entry.description]); + lines.push(formatTable(optionRows)); if (sections) { for (const sectionName in sections) { @@ -190,13 +184,8 @@ function buildRouterHelp(config: CreateCommandRouterConfig): string { lines.push(""); lines.push("COMMANDS"); - const commandNames = Object.keys(commands); - const maxNameLength = Math.max(...commandNames.map((commandName) => commandName.length)); - for (const commandName of commandNames) { - const paddedName = commandName.padEnd(maxNameLength); - const description = commands[commandName].description; - lines.push(` ${paddedName} ${description}`); - } + const commandRows = Object.entries(commands).map(([name, cmd]) => [` ${name}`, cmd.description]); + lines.push(formatTable(commandRows)); lines.push(""); lines.push("OPTIONS"); diff --git a/src/lib/string.ts b/src/lib/string.ts index d030e24..00bd83a 100644 --- a/src/lib/string.ts +++ b/src/lib/string.ts @@ -1,3 +1,20 @@ import baseDedent from "dedent"; export const dedent = baseDedent.withOptions({ alignValues: true }); + +export function formatTable(rows: string[][], separator = " "): string { + const columnWidths: number[] = []; + for (const row of rows) { + for (let i = 0; i < row.length; i++) { + columnWidths[i] = Math.max(columnWidths[i] ?? 0, row[i].length); + } + } + return rows + .map((row) => + row + .map((cell, i) => (i < row.length - 1 ? cell.padEnd(columnWidths[i]) : cell)) + .join(separator) + .trimEnd(), + ) + .join("\n"); +} diff --git a/test/slice-list.test.ts b/test/slice-list.test.ts index b06dda2..ba5899b 100644 --- a/test/slice-list.test.ts +++ b/test/slice-list.test.ts @@ -13,7 +13,7 @@ it("lists slices", async ({ expect, prismic, repo, token, host }) => { const { stdout, exitCode } = await prismic("slice", ["list"]); expect(exitCode).toBe(0); - expect(stdout).toContain(`${slice.name} (id: ${slice.id})`); + expect(stdout).toMatch(new RegExp(`${slice.name}\\s+${slice.id}`)); }); it("lists slices as JSON", async ({ expect, prismic, repo, token, host }) => { diff --git a/test/slice-view.test.ts b/test/slice-view.test.ts index c159e0c..fd27e26 100644 --- a/test/slice-view.test.ts +++ b/test/slice-view.test.ts @@ -51,11 +51,10 @@ it("shows fields per variation", async ({ expect, prismic, repo, token, host }) const { stdout, exitCode } = await prismic("slice", ["view", slice.name]); expect(exitCode).toBe(0); expect(stdout).toContain("default:"); - expect(stdout).toContain("title StructuredText Title"); - expect(stdout).toContain('"Enter title"'); - expect(stdout).toContain("is_active Boolean Is Active"); + expect(stdout).toMatch(/title\s+StructuredText\s+Title\s+"Enter title"/); + expect(stdout).toMatch(/is_active\s+Boolean\s+Is Active/); expect(stdout).toContain("withImage:"); - expect(stdout).toContain("image Image Image"); + expect(stdout).toMatch(/image\s+Image\s+Image/); }); it("views a slice as JSON", async ({ expect, prismic, repo, token, host }) => { diff --git a/test/type-list.test.ts b/test/type-list.test.ts index 543300c..f6271dc 100644 --- a/test/type-list.test.ts +++ b/test/type-list.test.ts @@ -15,8 +15,8 @@ it("lists all types", async ({ expect, prismic, repo, token, host }) => { const { stdout, exitCode } = await prismic("type", ["list"]); expect(exitCode).toBe(0); - expect(stdout).toContain(`${customType.label} (id: ${customType.id}, format: custom)`); - expect(stdout).toContain(`${pageType.label} (id: ${pageType.id}, format: page)`); + expect(stdout).toMatch(new RegExp(`${customType.label}\\s+${customType.id}\\s+custom`)); + expect(stdout).toMatch(new RegExp(`${pageType.label}\\s+${pageType.id}\\s+page`)); }); it("lists types as JSON", async ({ expect, prismic, repo, token, host }) => { diff --git a/test/type-view.test.ts b/test/type-view.test.ts index f0a843b..60ba122 100644 --- a/test/type-view.test.ts +++ b/test/type-view.test.ts @@ -37,11 +37,10 @@ it("shows fields per tab", async ({ expect, prismic, repo, token, host }) => { const { stdout, exitCode } = await prismic("type", ["view", customType.label!]); expect(exitCode).toBe(0); expect(stdout).toContain("Main:"); - expect(stdout).toContain("title StructuredText Title"); - expect(stdout).toContain('"Enter title"'); - expect(stdout).toContain("is_active Boolean Is Active"); + expect(stdout).toMatch(/title\s+StructuredText\s+Title\s+"Enter title"/); + expect(stdout).toMatch(/is_active\s+Boolean\s+Is Active/); expect(stdout).toContain("SEO:"); - expect(stdout).toContain("meta_title Text Meta Title"); + expect(stdout).toMatch(/meta_title\s+Text\s+Meta Title/); }); it("views a type as JSON", async ({ expect, prismic, repo, token, host }) => { From 577ade6e9ed4b322e83a531c1ec48f5c9a932aa7 Mon Sep 17 00:00:00 2001 From: Angelo Ashmore Date: Fri, 10 Apr 2026 13:15:15 -1000 Subject: [PATCH 2/4] feat: add header support to formatTable and add headers to list commands Co-Authored-By: Claude Opus 4.6 (1M context) --- src/commands/docs-list.ts | 4 ++-- src/commands/locale-list.ts | 2 +- src/commands/preview-list.ts | 2 +- src/commands/repo-list.ts | 2 +- src/commands/slice-list.ts | 2 +- src/commands/type-list.ts | 2 +- src/commands/webhook-list.ts | 2 +- src/lib/string.ts | 20 +++++++++++++------- 8 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/commands/docs-list.ts b/src/commands/docs-list.ts index 82e32c0..9e3b54b 100644 --- a/src/commands/docs-list.ts +++ b/src/commands/docs-list.ts @@ -54,7 +54,7 @@ export default createCommand(config, async ({ positionals, values }) => { } const rows = entry.anchors.map((anchor) => [`${path}#${anchor.slug}`, anchor.excerpt]); - console.info(formatTable(rows)); + console.info(formatTable(rows, { headers: ["PATH", "EXCERPT"] })); } else { let pages; try { @@ -80,6 +80,6 @@ export default createCommand(config, async ({ positionals, values }) => { } const rows = pages.map((page) => [page.path, page.title, page.description ?? ""]); - console.info(formatTable(rows)); + console.info(formatTable(rows, { headers: ["PATH", "TITLE", "DESCRIPTION"] })); } }); diff --git a/src/commands/locale-list.ts b/src/commands/locale-list.ts index d1a7907..c235cef 100644 --- a/src/commands/locale-list.ts +++ b/src/commands/locale-list.ts @@ -41,5 +41,5 @@ export default createCommand(config, async ({ values }) => { const masterLabel = locale.isMaster ? " (master)" : ""; return [locale.id, `${locale.label}${masterLabel}`]; }); - console.info(formatTable(rows)); + console.info(formatTable(rows, { headers: ["ID", "LABEL"] })); }); diff --git a/src/commands/preview-list.ts b/src/commands/preview-list.ts index 0666fbc..0f70606 100644 --- a/src/commands/preview-list.ts +++ b/src/commands/preview-list.ts @@ -46,7 +46,7 @@ export default createCommand(config, async ({ values }) => { } const rows = previews.map((preview) => [preview.url, preview.label]); - console.info(formatTable(rows)); + console.info(formatTable(rows, { headers: ["URL", "LABEL"] })); if (simulatorUrl) { console.info(`\nSimulator: ${simulatorUrl}`); diff --git a/src/commands/repo-list.ts b/src/commands/repo-list.ts index 135f73d..cd402f5 100644 --- a/src/commands/repo-list.ts +++ b/src/commands/repo-list.ts @@ -55,5 +55,5 @@ export default createCommand(config, async ({ values }) => { const name = repo.name || "(no name)"; return [repo.domain, name, repo.role ?? ""]; }); - console.info(formatTable(rows)); + console.info(formatTable(rows, { headers: ["DOMAIN", "NAME", "ROLE"] })); }); diff --git a/src/commands/slice-list.ts b/src/commands/slice-list.ts index 20c6694..17e75d6 100644 --- a/src/commands/slice-list.ts +++ b/src/commands/slice-list.ts @@ -32,5 +32,5 @@ export default createCommand(config, async ({ values }) => { } const rows = slices.map((slice) => [slice.name, slice.id]); - console.info(formatTable(rows)); + console.info(formatTable(rows, { headers: ["NAME", "ID"] })); }); diff --git a/src/commands/type-list.ts b/src/commands/type-list.ts index 4db3adf..a64bf8d 100644 --- a/src/commands/type-list.ts +++ b/src/commands/type-list.ts @@ -36,5 +36,5 @@ export default createCommand(config, async ({ values }) => { const label = type.label || "(no name)"; return [label, type.id, type.format ?? ""]; }); - console.info(formatTable(rows)); + console.info(formatTable(rows, { headers: ["NAME", "ID", "FORMAT"] })); }); diff --git a/src/commands/webhook-list.ts b/src/commands/webhook-list.ts index a726b47..2557d7a 100644 --- a/src/commands/webhook-list.ts +++ b/src/commands/webhook-list.ts @@ -41,5 +41,5 @@ export default createCommand(config, async ({ values }) => { const name = webhook.config.name ? ` (${webhook.config.name})` : ""; return [`${webhook.config.url}${name}`, `[${status}]`]; }); - console.info(formatTable(rows)); + console.info(formatTable(rows, { headers: ["URL", "STATUS"] })); }); diff --git a/src/lib/string.ts b/src/lib/string.ts index 00bd83a..bd8890d 100644 --- a/src/lib/string.ts +++ b/src/lib/string.ts @@ -2,19 +2,25 @@ import baseDedent from "dedent"; export const dedent = baseDedent.withOptions({ alignValues: true }); -export function formatTable(rows: string[][], separator = " "): string { +export function formatTable( + rows: string[][], + config?: { headers?: string[]; separator?: string }, +): string { + const separator = config?.separator ?? " "; + const allRows = config?.headers ? [config.headers, ...rows] : rows; const columnWidths: number[] = []; - for (const row of rows) { + for (const row of allRows) { for (let i = 0; i < row.length; i++) { columnWidths[i] = Math.max(columnWidths[i] ?? 0, row[i].length); } } - return rows - .map((row) => - row + return allRows + .map((row) => { + const line = row .map((cell, i) => (i < row.length - 1 ? cell.padEnd(columnWidths[i]) : cell)) .join(separator) - .trimEnd(), - ) + .trimEnd(); + return line; + }) .join("\n"); } From 7cd41b4f356117adfd0a1ac54244af498145a323 Mon Sep 17 00:00:00 2001 From: Angelo Ashmore Date: Fri, 10 Apr 2026 13:17:48 -1000 Subject: [PATCH 3/4] fix: avoid blank line in `preview list` when only simulator URL exists Co-Authored-By: Claude Opus 4.6 (1M context) --- src/commands/preview-list.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/commands/preview-list.ts b/src/commands/preview-list.ts index 0f70606..da8fb14 100644 --- a/src/commands/preview-list.ts +++ b/src/commands/preview-list.ts @@ -45,10 +45,15 @@ export default createCommand(config, async ({ values }) => { return; } - const rows = previews.map((preview) => [preview.url, preview.label]); - console.info(formatTable(rows, { headers: ["URL", "LABEL"] })); + if (previews.length > 0) { + const rows = previews.map((preview) => [preview.url, preview.label]); + console.info(formatTable(rows, { headers: ["URL", "LABEL"] })); + } if (simulatorUrl) { - console.info(`\nSimulator: ${simulatorUrl}`); + if (previews.length > 0) { + console.info(""); + } + console.info(`Simulator: ${simulatorUrl}`); } }); From 729b9ba963b44b1281b97669c84a745b63d7a4bc Mon Sep 17 00:00:00 2001 From: Angelo Ashmore Date: Fri, 10 Apr 2026 13:18:14 -1000 Subject: [PATCH 4/4] fix: rename preview list header from LABEL to NAME Co-Authored-By: Claude Opus 4.6 (1M context) --- src/commands/preview-list.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/preview-list.ts b/src/commands/preview-list.ts index da8fb14..bc6da3f 100644 --- a/src/commands/preview-list.ts +++ b/src/commands/preview-list.ts @@ -47,7 +47,7 @@ export default createCommand(config, async ({ values }) => { if (previews.length > 0) { const rows = previews.map((preview) => [preview.url, preview.label]); - console.info(formatTable(rows, { headers: ["URL", "LABEL"] })); + console.info(formatTable(rows, { headers: ["URL", "NAME"] })); } if (simulatorUrl) {