diff --git a/.changeset/update-tui-header-branding.md b/.changeset/update-tui-header-branding.md new file mode 100644 index 000000000..c774f2dbb --- /dev/null +++ b/.changeset/update-tui-header-branding.md @@ -0,0 +1,5 @@ +--- +"@runfusion/fusion": patch +--- + +Update dashboard TUI header branding from "fn board" to "fusion" for consistent product naming. diff --git a/packages/cli/src/commands/dashboard-tui.test.ts b/packages/cli/src/commands/dashboard-tui.test.ts index 52eba61aa..b168e02e2 100644 --- a/packages/cli/src/commands/dashboard-tui.test.ts +++ b/packages/cli/src/commands/dashboard-tui.test.ts @@ -3,6 +3,7 @@ import { LogRingBuffer, DashboardLogSink, isTTYAvailable, + renderHeaderToString, type SystemInfo, type TaskStats, type SettingsValues, @@ -148,6 +149,40 @@ describe("isTTYAvailable", () => { }); }); +// ── Header Branding Tests ─────────────────────────────────────────────── + +describe("renderHeaderToString", () => { + it("includes 'fusion' in header title", () => { + const header = renderHeaderToString(80); + expect(header).toContain("fusion"); + }); + + it("does not include old 'fn board' branding", () => { + const header = renderHeaderToString(80); + expect(header).not.toContain("fn board"); + }); + + it("renders correctly at wide terminal width (>= 70 cols)", () => { + const header = renderHeaderToString(80); + expect(header).toContain("fusion"); + expect(header).toContain("Logs"); + expect(header).toContain("System"); + }); + + it("renders correctly at medium terminal width (>= 40 cols)", () => { + const header = renderHeaderToString(50); + expect(header).toContain("fusion"); + expect(header).toContain("[1]L"); // Short label for Logs + }); + + it("renders correctly at narrow terminal width (< 40 cols)", () => { + const header = renderHeaderToString(30); + expect(header).toContain("fusion"); + expect(header).toContain("Logs"); + expect(header).toContain("[n/p]nav"); + }); +}); + // ── Type exports verification ───────────────────────────────────────────── describe("Type exports", () => { diff --git a/packages/cli/src/commands/dashboard-tui.ts b/packages/cli/src/commands/dashboard-tui.ts index 612705eee..7825153dc 100644 --- a/packages/cli/src/commands/dashboard-tui.ts +++ b/packages/cli/src/commands/dashboard-tui.ts @@ -556,7 +556,7 @@ export class DashboardTUI { private renderHeader(): void { const cols = process.stdout.columns || 80; - const title = colorize(" fn board ", "cyan"); + const title = colorize(" fusion ", "cyan"); const titleLen = visibleLength(title); process.stdout.write(title); @@ -1012,3 +1012,81 @@ function padRight(text: string, width: number): string { export function isTTYAvailable(): boolean { return Boolean(process.stdout.isTTY && process.stdin.isTTY); } + +// ── Header String Helper (for testing) ───────────────────────────────────── + +/** + * Render the header to a string using the given column width. + * Used for testing without requiring TTY mode. + */ +export function renderHeaderToString(cols: number): string { + const title = colorize(" fusion ", "cyan"); + const titleLen = visibleLength(title); + + let output = title; + + // Build tabs based on column width (same logic as renderHeader) + if (cols >= 70) { + for (let i = 0; i < SECTION_ORDER.length; i++) { + const section = SECTION_ORDER[i]; + const isActive = section === "logs"; // Default active section for test + const num = (i + 1).toString(); + const label = section.charAt(0).toUpperCase() + section.slice(1); + const tabText = `[${num}] ${label}`; + const style = isActive ? "brightBlue" : "dim"; + output += colorize(` ${tabText} `, style); + } + } else if (cols >= 40) { + const shortLabels: Record = { + logs: "L", + system: "S", + utilities: "U", + stats: "St", + settings: "Se", + }; + for (let i = 0; i < SECTION_ORDER.length; i++) { + const section = SECTION_ORDER[i]; + const isActive = section === "logs"; + const num = (i + 1).toString(); + const shortLabel = shortLabels[section]; + const tabText = `[${num}]${shortLabel}`; + const style = isActive ? "brightBlue" : "dim"; + output += colorize(` ${tabText} `, style); + } + } else { + output += colorize(" [1]Logs ", "brightBlue"); + output += colorize(" [n/p]nav ", "dim"); + } + + // Calculate tabs length for padding + const tabsLength = SECTION_ORDER.reduce((acc, s, i) => { + let label: string; + if (cols >= 70) { + label = s.charAt(0).toUpperCase() + s.slice(1); + } else if (cols >= 40) { + const shortLabels: Record = { + logs: "L", + system: "S", + utilities: "U", + stats: "St", + settings: "Se", + }; + label = shortLabels[s]; + } else { + label = s.charAt(0).toUpperCase() + s.slice(1); + } + const tabText = `[${i + 1}]${label} `; + return acc + tabText.length; + }, 0); + const headerLen = titleLen + tabsLength; + + const remaining = cols - headerLen; + if (remaining > 0) { + output += " ".repeat(remaining); + } + + output += "\n"; + output += colorize("─".repeat(Math.max(20, cols)), "dim") + "\n"; + + return output; +}