From b6fa7ed2b9d562b6ce4a284651473f437d55e4a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Crist=C3=B3v=C3=A3o=20GUIHO?= Date: Tue, 13 Oct 2026 14:12:38 +0200 Subject: [PATCH] Left-align the welcome message and remove the organization line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Render the bare xdocs welcome flush left: logo rows, tagline and the "GUIHO · Cristóvão GUIHO" author line now start at column 0 instead of being left-padded by the center() helper. Drop the "organization GUIHO" detail line and print Capital-case Platform, Version and Author detail rows at column 0 with aligned values, separated from the author line by exactly one blank line. Paint www.guiho.co in the xdocs accent colour. Frame the welcome with exactly one blank line before and after. Plain output stays ANSI-free and the update-notice behaviour is unchanged. Remove the now-dead center() helper and innerWidth computation, add exact-layout tests for the welcome package, tighten the root command test, and update the welcome descriptor. Co-Authored-By: Claude Opus 5.5 --- cmd/root_test.go | 13 +++--- internal/welcome/welcome.go | 69 ++++++++++++------------------- internal/welcome/welcome.xdocs.md | 3 +- internal/welcome/welcome_test.go | 46 +++++++++++++++++++++ 4 files changed, 82 insertions(+), 49 deletions(-) create mode 100644 internal/welcome/welcome_test.go diff --git a/cmd/root_test.go b/cmd/root_test.go index 0280455..5ecdfc2 100644 --- a/cmd/root_test.go +++ b/cmd/root_test.go @@ -392,18 +392,21 @@ func TestNoArgumentVersionAndCatalog(t *testing.T) { for _, required := range []string{ "████╗", "Structured documentation for codebases and AI agents", "GUIHO", "Cristóvão GUIHO", - "organization", "platform", "x64", "version", "v0.8.0", + "\n\nPlatform ", "x64", "\nVersion v0.8.0\nAuthor GUIHO (www.guiho.co)\n\n", "Run xdocs --help to see available commands.", } { if !strings.Contains(out, required) { t.Fatalf("welcome output omits %q:\n%s", required, out) } } - if !strings.HasPrefix(out, "\n\n") { - t.Fatalf("welcome output does not start with two blank lines: %q", out) + if strings.Contains(out, "organization") || strings.Contains(out, "Creator") { + t.Fatalf("welcome output still shows a creator/organization detail line:\n%s", out) } - if !strings.HasSuffix(out, "\n\n") { - t.Fatalf("welcome output does not end with two blank lines: %q", out) + if !strings.HasPrefix(out, "\n██") || strings.HasPrefix(out, "\n\n") { + t.Fatalf("welcome output does not start with exactly one blank line: %q", out) + } + if !strings.HasSuffix(out, "commands.\n\n") || strings.HasSuffix(out, "\n\n\n") { + t.Fatalf("welcome output does not end with exactly one blank line: %q", out) } if strings.Contains(out, "Hello Windows") || strings.Contains(out, "\x1b[") { t.Fatalf("welcome output should be beautiful borderless without legacy text or ANSI in non-terminal mode: %q", out) diff --git a/internal/welcome/welcome.go b/internal/welcome/welcome.go index 7e7e409..e1f6a33 100644 --- a/internal/welcome/welcome.go +++ b/internal/welcome/welcome.go @@ -19,6 +19,9 @@ const ( ansiCream = "\x1b[38;2;237;224;212m" ansiSage = "\x1b[38;2;101;109;74m" ansiForest = "\x1b[38;2;65;72;51m" + + // ansiAccent is the xdocs primary accent: the warm brown logo tone. + ansiAccent = ansiBrown + ansiBold ) var platformLabels = map[string]string{ @@ -83,45 +86,39 @@ func render(platform, architecture, version string, withColor bool) string { architecture = displayValue(archLabels, architecture) version = strings.TrimPrefix(strings.TrimSpace(version), "v") - innerWidth := 56 - for _, line := range xdocsLogo { - if w := len([]rune(line)); w+4 > innerWidth { - innerWidth = w + 4 - } - } tagline := "Structured documentation for codebases and AI agents" - if len([]rune(tagline))+4 > innerWidth { - innerWidth = len([]rune(tagline)) + 4 - } var out strings.Builder - // Two blank lines before — as requested - out.WriteString("\n\n") + // One blank line before, so the welcome never touches the prompt line + out.WriteString("\n") for i, line := range xdocsLogo { color := "" if i < len(logoColors) { color = logoColors[i] } - out.WriteString(colorize(withColor, center(line, innerWidth), color)) + out.WriteString(colorize(withColor, line, color)) out.WriteByte('\n') } - out.WriteString(colorize(withColor, center(tagline, innerWidth), ansiTan+ansiDim)) + out.WriteString(colorize(withColor, tagline, ansiTan+ansiDim)) out.WriteByte('\n') - out.WriteString(colorize(withColor, center("GUIHO · Cristóvão GUIHO", innerWidth), ansiSage)) - out.WriteString("\n\n\n") + out.WriteString(colorize(withColor, "GUIHO · Cristóvão GUIHO", ansiSage)) + out.WriteString("\n\n") - // Details — organization, platform, version - writeDetail(&out, withColor, "organization", "GUIHO", ansiCream+ansiBold) - writeDetail(&out, withColor, "platform", platform+" "+architecture, ansiCream) - writeDetail(&out, withColor, "version", "v"+version, ansiBrown+ansiBold) + // Details — platform, version, author + writeDetail(&out, withColor, "Platform", colorize(withColor, platform+" "+architecture, ansiCream)) + writeDetail(&out, withColor, "Version", colorize(withColor, "v"+version, ansiAccent)) + writeDetail(&out, withColor, "Author", + colorize(withColor, "GUIHO (", ansiCream)+ + colorize(withColor, "www.guiho.co", ansiAccent)+ + colorize(withColor, ")", ansiCream)) out.WriteByte('\n') out.WriteString("Run ") out.WriteString(colorize(withColor, "xdocs --help", ansiCream+ansiBold)) out.WriteString(" to see available commands.") out.WriteString("\n") - // Two blank lines after — as requested - out.WriteString("\n\n") + // One blank line after, so the welcome never touches the next prompt + out.WriteString("\n") return out.String() } @@ -132,17 +129,6 @@ func displayValue(labels map[string]string, value string) string { return value } -func center(value string, width int) string { - w := len([]rune(value)) - if w >= width { - return value - } - pad := width - w - // left pad half, right is implicit via center visual; we only left-pad like genius - left := pad / 2 - return strings.Repeat(" ", left) + value -} - func colorize(enabled bool, value, codes string) string { if !enabled || codes == "" { return value @@ -150,15 +136,12 @@ func colorize(enabled bool, value, codes string) string { return codes + value + ansiReset } -func writeDetail(out *strings.Builder, withColor bool, label, value, valueColor string) { - const labelWidth = 12 - if withColor { - // labels in forest (subtle earth), values in cream/brown accent - out.WriteString(colorize(true, fmt.Sprintf(" %-12s", label), ansiForest)) - out.WriteString(" ") - out.WriteString(colorize(true, value, valueColor)) - out.WriteByte('\n') - return - } - fmt.Fprintf(out, " %-12s %s\n", label, value) +// detailLabelWidth fits the longest detail label ("Platform") so values align. +const detailLabelWidth = 8 + +// writeDetail writes one detail row; value is already colourized by the caller. +func writeDetail(out *strings.Builder, withColor bool, label, value string) { + // labels in forest (subtle earth), values in cream/brown accent + padded := colorize(withColor, fmt.Sprintf("%-*s", detailLabelWidth, label), ansiForest) + fmt.Fprintf(out, "%s %s\n", padded, value) } diff --git a/internal/welcome/welcome.xdocs.md b/internal/welcome/welcome.xdocs.md index 2b15395..d6e2b61 100644 --- a/internal/welcome/welcome.xdocs.md +++ b/internal/welcome/welcome.xdocs.md @@ -4,7 +4,8 @@ description: Beautiful hello window rendering for the xdocs CLI with earth-tone parent: xdocs-internal children: [] files: - welcome.go: ANSI earth-tone rendering (7F5539, A68A64, EDE0D4, 656D4A, 414833), XDOCS block logo, centered tagline, organization/platform/version details, and two blank lines before and after the hello window. + welcome.go: ANSI earth-tone rendering (7F5539, A68A64, EDE0D4, 656D4A, 414833), XDOCS block logo, left-aligned tagline and author line, column-0 Platform/Version/Author details with aligned values, and one blank line before and after the hello window. + welcome_test.go: Exact left-aligned plain layout and coloured-output alignment regression tests, including absence of the organization line and one-blank-line framing. documents: {} tags: - welcome diff --git a/internal/welcome/welcome_test.go b/internal/welcome/welcome_test.go new file mode 100644 index 0000000..51397c4 --- /dev/null +++ b/internal/welcome/welcome_test.go @@ -0,0 +1,46 @@ +package welcome + +import ( + "strings" + "testing" +) + +func TestRenderIsLeftAlignedWithoutCreatorLine(t *testing.T) { + got := Render("linux", "amd64", "0.12.0") + want := "\n" + strings.Join(xdocsLogo, "\n") + "\n" + + "Structured documentation for codebases and AI agents\n" + + "GUIHO · Cristóvão GUIHO\n" + + "\n" + + "Platform Linux x64\n" + + "Version v0.12.0\n" + + "Author GUIHO (www.guiho.co)\n" + + "\n" + + "Run xdocs --help to see available commands.\n" + + "\n" + if got != want { + t.Fatalf("unexpected welcome layout:\ngot:\n%q\nwant:\n%q", got, want) + } +} + +func TestRenderWithColorKeepsLeftAlignment(t *testing.T) { + got := RenderWithColor("windows", "arm64", "v1.2.3", true) + for _, line := range strings.Split(got, "\n") { + if strings.HasPrefix(line, " ") { + t.Fatalf("coloured welcome line is indented: %q", line) + } + } + for _, required := range []string{ + logoColors[0] + xdocsLogo[0] + ansiReset + "\n", + ansiSage + "GUIHO · Cristóvão GUIHO" + ansiReset + "\n\n" + ansiForest + "Platform" + ansiReset, + ansiForest + "Version " + ansiReset + " " + ansiBrown + ansiBold + "v1.2.3" + ansiReset + "\n" + + ansiForest + "Author " + ansiReset + " " + + ansiCream + "GUIHO (" + ansiReset + ansiAccent + "www.guiho.co" + ansiReset + ansiCream + ")" + ansiReset + "\n\nRun ", + } { + if !strings.Contains(got, required) { + t.Fatalf("coloured welcome omits %q:\n%q", required, got) + } + } + if strings.Contains(got, "organization") { + t.Fatalf("coloured welcome still shows the organization line: %q", got) + } +}