Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions docs/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,9 +142,13 @@ DEVMAP ANALYSIS ENGINE INITIALIZATION STARTED

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

The wordmark should use the aqua brand color.

Everything below the wordmark should stay concise.
The wordmark should use the aqua brand color.

Everything below the wordmark should stay concise.

The welcome brand signal is framed in a single aqua panel. Wide terminals show
the DevMap symbol above the large wordmark. Narrow terminals keep the symbol
but use a compact `DEVMAP` title so the panel does not wrap or clip.

Example:

Expand Down
11 changes: 11 additions & 0 deletions docs/for-me-personal/PROGRESS.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,17 @@ Terakhir diperbarui: 2026-06-14
sama-sama menghitung hasil scanner setelah ignore filtering.
- Automated test saat ini berjumlah 47 dan seluruhnya lulus.

### Welcome Brand Panel

- Welcome screen sekarang menampilkan simbol logo DevMap di atas ASCII
wordmark.
- Logo, wordmark, dan border memakai warna aqua brand yang sama.
- Terminal lebar memakai wordmark penuh dalam panel maksimal 76 kolom.
- Terminal sempit memakai judul `DEVMAP` ringkas agar panel tidak wrap atau
terpotong.
- Renderer panel dipisahkan agar layout lebar dan sempit dapat diuji langsung.
- Automated test saat ini berjumlah 49 dan seluruhnya lulus.

## Update 2026-06-13

### Packed CLI End-to-End
Expand Down
4 changes: 2 additions & 2 deletions docs/for-me-personal/TEST.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,8 @@ pnpm --filter devmap test:types
Hasil minimum saat ini:

```text
tests 47
pass 47
tests 49
pass 49
fail 0
```

Expand Down
64 changes: 55 additions & 9 deletions packages/cli/src/utils/welcome.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,36 @@ import { existsSync } from "node:fs";
import { getSnapshotPath } from "../cache/snapshot.js";
import { theme } from "./output.js";

const LOGO = String.raw`
██████╗ ███████╗██╗ ██╗███╗ ███╗ █████╗ ██████╗
██╔══██╗██╔════╝██║ ██║████╗ ████║██╔══██╗██╔══██╗
██║ ██║█████╗ ██║ ██║██╔████╔██║███████║██████╔╝
██║ ██║██╔══╝ ╚██╗ ██╔╝██║╚██╔╝██║██╔══██║██╔═══╝
██████╔╝███████╗ ╚████╔╝ ██║ ╚═╝ ██║██║ ██║██║
╚═════╝ ╚══════╝ ╚═══╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝
`;
const SYMBOL_LOGO = [
" ╷ ╱╲",
" ╭──────┼──────╱ ╲",
" ╭─╯ ●─────╯ ╱",
" ╱ ╱│ ╱",
"│ ╱ │ ╭────╯",
"│ ╱ │╱╱",
" ╲__╱ ╱│",
" ╲____╱ │",
" ╵"
];

const WIDE_WORDMARK = [
"██████╗ ███████╗██╗ ██╗███╗ ███╗ █████╗ ██████╗",
"██╔══██╗██╔════╝██║ ██║████╗ ████║██╔══██╗██╔══██╗",
"██║ ██║█████╗ ██║ ██║██╔████╔██║███████║██████╔╝",
"██║ ██║██╔══╝ ╚██╗ ██╔╝██║╚██╔╝██║██╔══██║██╔═══╝",
"██████╔╝███████╗ ╚████╔╝ ██║ ╚═╝ ██║██║ ██║██║",
"╚═════╝ ╚══════╝ ╚═══╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝"
];

const WIDE_PANEL_WIDTH = 76;
const COMPACT_PANEL_WIDTH = 48;
const WIDE_TERMINAL_MINIMUM = 72;

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

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

export function renderWelcomeBrandPanel(terminalWidth: number): string {
const isWide = terminalWidth >= WIDE_TERMINAL_MINIMUM;
const panelWidth = Math.max(
28,
Math.min(
terminalWidth,
isWide ? WIDE_PANEL_WIDTH : COMPACT_PANEL_WIDTH
)
);
const contentWidth = panelWidth - 4;
const wordmark = isWide ? WIDE_WORDMARK : ["DEVMAP"];
const content = [
...SYMBOL_LOGO,
"",
...wordmark
];
const top = `╭${"─".repeat(panelWidth - 2)}╮`;
const bottom = `╰${"─".repeat(panelWidth - 2)}╯`;
const rows = content.map((line) => {
const visibleLine = line.slice(0, contentWidth);
const leftPadding = Math.floor((contentWidth - visibleLine.length) / 2);
const rightPadding = contentWidth - visibleLine.length - leftPadding;
return `│ ${" ".repeat(leftPadding)}${visibleLine}${" ".repeat(rightPadding)} │`;
});

return [top, ...rows, bottom]
.map((line) => `${theme.aqua}${line}${theme.reset}`)
.join("\n");
}

function printCommand(command: string, description?: string): void {
const padded = command.padEnd(22);
const suffix = description ? ` ${theme.gray}${description}${theme.reset}` : "";
Expand Down
29 changes: 29 additions & 0 deletions packages/cli/test/welcome.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import assert from "node:assert/strict";
import test from "node:test";
import { renderWelcomeBrandPanel } from "../src/utils/welcome.js";

test("welcome brand panel frames the logo and wordmark on wide terminals", () => {
const panel = stripAnsi(renderWelcomeBrandPanel(100));
const lines = panel.split("\n");

assert.match(panel, /██████╗ ███████╗/);
assert.match(panel, /╱╲/);
assert.ok(lines.every((line) => line.length === lines[0]?.length));
assert.ok((lines[0]?.length ?? 0) <= 76);
assert.match(lines[0] ?? "", /^╭─+╮$/);
assert.match(lines.at(-1) ?? "", /^╰─+╯$/);
});

test("welcome brand panel uses a compact wordmark on narrow terminals", () => {
const panel = stripAnsi(renderWelcomeBrandPanel(48));
const lines = panel.split("\n");

assert.match(panel, /DEVMAP/);
assert.ok(lines.every((line) => line.length === lines[0]?.length));
assert.ok((lines[0]?.length ?? 0) <= 48);
assert.doesNotMatch(panel, /██████/);
});

function stripAnsi(value: string): string {
return value.replace(/\x1b\[[0-9;]*m/g, "");
}
Loading