Skip to content

Commit 225fd77

Browse files
Improve responsive welcome logo panel
Closes #9 Co-authored-by: devmap-agent <238585242+devmap-agent@users.noreply.github.com>
1 parent 2784f38 commit 225fd77

5 files changed

Lines changed: 104 additions & 14 deletions

File tree

docs/design.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,9 +142,13 @@ DEVMAP ANALYSIS ENGINE INITIALIZATION STARTED
142142

143143
The default `devmap` command may use the large DevMap ASCII wordmark as the primary brand signal.
144144

145-
The wordmark should use the aqua brand color.
146-
147-
Everything below the wordmark should stay concise.
145+
The wordmark should use the aqua brand color.
146+
147+
Everything below the wordmark should stay concise.
148+
149+
The welcome brand signal is framed in a single aqua panel. Wide terminals show
150+
the DevMap symbol above the large wordmark. Narrow terminals keep the symbol
151+
but use a compact `DEVMAP` title so the panel does not wrap or clip.
148152

149153
Example:
150154

docs/for-me-personal/PROGRESS.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,17 @@ Terakhir diperbarui: 2026-06-14
1919
sama-sama menghitung hasil scanner setelah ignore filtering.
2020
- Automated test saat ini berjumlah 47 dan seluruhnya lulus.
2121

22+
### Welcome Brand Panel
23+
24+
- Welcome screen sekarang menampilkan simbol logo DevMap di atas ASCII
25+
wordmark.
26+
- Logo, wordmark, dan border memakai warna aqua brand yang sama.
27+
- Terminal lebar memakai wordmark penuh dalam panel maksimal 76 kolom.
28+
- Terminal sempit memakai judul `DEVMAP` ringkas agar panel tidak wrap atau
29+
terpotong.
30+
- Renderer panel dipisahkan agar layout lebar dan sempit dapat diuji langsung.
31+
- Automated test saat ini berjumlah 49 dan seluruhnya lulus.
32+
2233
## Update 2026-06-13
2334

2435
### Packed CLI End-to-End

docs/for-me-personal/TEST.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,8 @@ pnpm --filter devmap test:types
153153
Hasil minimum saat ini:
154154

155155
```text
156-
tests 47
157-
pass 47
156+
tests 49
157+
pass 49
158158
fail 0
159159
```
160160

packages/cli/src/utils/welcome.ts

Lines changed: 55 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,36 @@ import { existsSync } from "node:fs";
22
import { getSnapshotPath } from "../cache/snapshot.js";
33
import { theme } from "./output.js";
44

5-
const LOGO = String.raw`
6-
██████╗ ███████╗██╗ ██╗███╗ ███╗ █████╗ ██████╗
7-
██╔══██╗██╔════╝██║ ██║████╗ ████║██╔══██╗██╔══██╗
8-
██║ ██║█████╗ ██║ ██║██╔████╔██║███████║██████╔╝
9-
██║ ██║██╔══╝ ╚██╗ ██╔╝██║╚██╔╝██║██╔══██║██╔═══╝
10-
██████╔╝███████╗ ╚████╔╝ ██║ ╚═╝ ██║██║ ██║██║
11-
╚═════╝ ╚══════╝ ╚═══╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝
12-
`;
5+
const SYMBOL_LOGO = [
6+
" ╷ ╱╲",
7+
" ╭──────┼──────╱ ╲",
8+
" ╭─╯ ●─────╯ ╱",
9+
" ╱ ╱│ ╱",
10+
"│ ╱ │ ╭────╯",
11+
"│ ╱ │╱╱",
12+
" ╲__╱ ╱│",
13+
" ╲____╱ │",
14+
" ╵"
15+
];
16+
17+
const WIDE_WORDMARK = [
18+
"██████╗ ███████╗██╗ ██╗███╗ ███╗ █████╗ ██████╗",
19+
"██╔══██╗██╔════╝██║ ██║████╗ ████║██╔══██╗██╔══██╗",
20+
"██║ ██║█████╗ ██║ ██║██╔████╔██║███████║██████╔╝",
21+
"██║ ██║██╔══╝ ╚██╗ ██╔╝██║╚██╔╝██║██╔══██║██╔═══╝",
22+
"██████╔╝███████╗ ╚████╔╝ ██║ ╚═╝ ██║██║ ██║██║",
23+
"╚═════╝ ╚══════╝ ╚═══╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝"
24+
];
25+
26+
const WIDE_PANEL_WIDTH = 76;
27+
const COMPACT_PANEL_WIDTH = 48;
28+
const WIDE_TERMINAL_MINIMUM = 72;
1329

1430
export function printWelcome(projectRoot: string): void {
1531
const hasSnapshot = existsSync(getSnapshotPath(projectRoot));
1632
const status = hasSnapshot ? "Project snapshot found." : "No project analyzed yet.";
1733

18-
console.log(`${theme.aqua}${LOGO}${theme.reset}`);
34+
console.log(renderWelcomeBrandPanel(process.stdout.columns ?? 80));
1935
console.log(" Understand Any Codebase.");
2036
console.log(`\n ${theme.gray}──────────────────────────────────────────${theme.reset}\n`);
2137
console.log(` ${status}\n`);
@@ -31,6 +47,36 @@ export function printWelcome(projectRoot: string): void {
3147
console.log("");
3248
}
3349

50+
export function renderWelcomeBrandPanel(terminalWidth: number): string {
51+
const isWide = terminalWidth >= WIDE_TERMINAL_MINIMUM;
52+
const panelWidth = Math.max(
53+
28,
54+
Math.min(
55+
terminalWidth,
56+
isWide ? WIDE_PANEL_WIDTH : COMPACT_PANEL_WIDTH
57+
)
58+
);
59+
const contentWidth = panelWidth - 4;
60+
const wordmark = isWide ? WIDE_WORDMARK : ["DEVMAP"];
61+
const content = [
62+
...SYMBOL_LOGO,
63+
"",
64+
...wordmark
65+
];
66+
const top = `╭${"─".repeat(panelWidth - 2)}╮`;
67+
const bottom = `╰${"─".repeat(panelWidth - 2)}╯`;
68+
const rows = content.map((line) => {
69+
const visibleLine = line.slice(0, contentWidth);
70+
const leftPadding = Math.floor((contentWidth - visibleLine.length) / 2);
71+
const rightPadding = contentWidth - visibleLine.length - leftPadding;
72+
return `│ ${" ".repeat(leftPadding)}${visibleLine}${" ".repeat(rightPadding)} │`;
73+
});
74+
75+
return [top, ...rows, bottom]
76+
.map((line) => `${theme.aqua}${line}${theme.reset}`)
77+
.join("\n");
78+
}
79+
3480
function printCommand(command: string, description?: string): void {
3581
const padded = command.padEnd(22);
3682
const suffix = description ? ` ${theme.gray}${description}${theme.reset}` : "";

packages/cli/test/welcome.test.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import assert from "node:assert/strict";
2+
import test from "node:test";
3+
import { renderWelcomeBrandPanel } from "../src/utils/welcome.js";
4+
5+
test("welcome brand panel frames the logo and wordmark on wide terminals", () => {
6+
const panel = stripAnsi(renderWelcomeBrandPanel(100));
7+
const lines = panel.split("\n");
8+
9+
assert.match(panel, / /);
10+
assert.match(panel, //);
11+
assert.ok(lines.every((line) => line.length === lines[0]?.length));
12+
assert.ok((lines[0]?.length ?? 0) <= 76);
13+
assert.match(lines[0] ?? "", /^+$/);
14+
assert.match(lines.at(-1) ?? "", /^+$/);
15+
});
16+
17+
test("welcome brand panel uses a compact wordmark on narrow terminals", () => {
18+
const panel = stripAnsi(renderWelcomeBrandPanel(48));
19+
const lines = panel.split("\n");
20+
21+
assert.match(panel, /DEVMAP/);
22+
assert.ok(lines.every((line) => line.length === lines[0]?.length));
23+
assert.ok((lines[0]?.length ?? 0) <= 48);
24+
assert.doesNotMatch(panel, //);
25+
});
26+
27+
function stripAnsi(value: string): string {
28+
return value.replace(/\x1b\[[0-9;]*m/g, "");
29+
}

0 commit comments

Comments
 (0)