@fecommunity/reactpress 4.0.0-beta.1 → 4.0.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/out/bin/reactpress.js +2 -2
- package/out/bin/reactpress.js.map +1 -1
- package/out/lib/cli-events.d.ts +12 -0
- package/out/lib/cli-events.d.ts.map +1 -0
- package/out/lib/cli-events.js +54 -0
- package/out/lib/cli-events.js.map +1 -0
- package/out/lib/command-palette.d.ts +6 -0
- package/out/lib/command-palette.d.ts.map +1 -0
- package/out/lib/command-palette.js +53 -0
- package/out/lib/command-palette.js.map +1 -0
- package/out/lib/context-status.d.ts +2 -0
- package/out/lib/context-status.d.ts.map +1 -0
- package/out/lib/context-status.js +148 -0
- package/out/lib/context-status.js.map +1 -0
- package/out/lib/design-tokens.d.ts +38 -0
- package/out/lib/design-tokens.d.ts.map +1 -0
- package/out/lib/design-tokens.js +55 -0
- package/out/lib/design-tokens.js.map +1 -0
- package/out/lib/dev-banner.js +10 -5
- package/out/lib/dev-banner.js.map +1 -1
- package/out/lib/dev.js +20 -1
- package/out/lib/dev.js.map +1 -1
- package/out/lib/i18n/index.js +5 -1
- package/out/lib/i18n/index.js.map +1 -1
- package/out/lib/i18n/strings.js +99 -41
- package/out/lib/i18n/strings.js.map +1 -1
- package/out/lib/menu-actions.d.ts +2 -0
- package/out/lib/menu-actions.d.ts.map +1 -0
- package/out/lib/menu-actions.js +418 -0
- package/out/lib/menu-actions.js.map +1 -0
- package/out/lib/nginx.js +5 -2
- package/out/lib/nginx.js.map +1 -1
- package/out/lib/paths.js +11 -0
- package/out/lib/paths.js.map +1 -1
- package/out/lib/remote-dev.js +5 -1
- package/out/lib/remote-dev.js.map +1 -1
- package/out/lib/shell-mode.d.ts +5 -0
- package/out/lib/shell-mode.d.ts.map +1 -0
- package/out/lib/shell-mode.js +23 -0
- package/out/lib/shell-mode.js.map +1 -0
- package/out/lib/theme-warmup.js +1 -1
- package/out/lib/theme-warmup.js.map +1 -1
- package/out/shell/app.d.ts +12 -0
- package/out/shell/app.d.ts.map +1 -0
- package/out/shell/app.js +384 -0
- package/out/shell/app.js.map +1 -0
- package/out/shell/components/brand-banner.d.ts +20 -0
- package/out/shell/components/brand-banner.d.ts.map +1 -0
- package/out/shell/components/brand-banner.js +93 -0
- package/out/shell/components/brand-banner.js.map +1 -0
- package/out/shell/components/command-palette.d.ts +10 -0
- package/out/shell/components/command-palette.d.ts.map +1 -0
- package/out/shell/components/command-palette.js +77 -0
- package/out/shell/components/command-palette.js.map +1 -0
- package/out/shell/components/dev-stepper.d.ts +8 -0
- package/out/shell/components/dev-stepper.d.ts.map +1 -0
- package/out/shell/components/dev-stepper.js +54 -0
- package/out/shell/components/dev-stepper.js.map +1 -0
- package/out/shell/components/ink-theme.d.ts +34 -0
- package/out/shell/components/ink-theme.d.ts.map +1 -0
- package/out/shell/components/ink-theme.js +86 -0
- package/out/shell/components/ink-theme.js.map +1 -0
- package/out/shell/components/menu-list.d.ts +7 -0
- package/out/shell/components/menu-list.d.ts.map +1 -0
- package/out/shell/components/menu-list.js +49 -0
- package/out/shell/components/menu-list.js.map +1 -0
- package/out/shell/components/pulse-bar.d.ts +6 -0
- package/out/shell/components/pulse-bar.d.ts.map +1 -0
- package/out/shell/components/pulse-bar.js +55 -0
- package/out/shell/components/pulse-bar.js.map +1 -0
- package/out/shell/components/status-rack.d.ts +16 -0
- package/out/shell/components/status-rack.d.ts.map +1 -0
- package/out/shell/components/status-rack.js +47 -0
- package/out/shell/components/status-rack.js.map +1 -0
- package/out/shell/hooks/use-terminal-size.d.ts +2 -0
- package/out/shell/hooks/use-terminal-size.d.ts.map +1 -0
- package/out/shell/hooks/use-terminal-size.js +27 -0
- package/out/shell/hooks/use-terminal-size.js.map +1 -0
- package/out/shell/ink-shell.mjs +12056 -0
- package/out/shell/lib/shell-layout.d.ts +27 -0
- package/out/shell/lib/shell-layout.d.ts.map +1 -0
- package/out/shell/lib/shell-layout.js +60 -0
- package/out/shell/lib/shell-layout.js.map +1 -0
- package/out/shell/runtime/render-ink.d.ts +2 -0
- package/out/shell/runtime/render-ink.d.ts.map +1 -0
- package/out/shell/runtime/render-ink.js +89 -0
- package/out/shell/runtime/render-ink.js.map +1 -0
- package/out/ui/banner-startup.d.ts +2 -0
- package/out/ui/banner-startup.d.ts.map +1 -0
- package/out/ui/banner-startup.js +125 -0
- package/out/ui/banner-startup.js.map +1 -0
- package/out/ui/banner.js +405 -275
- package/out/ui/banner.js.map +1 -1
- package/out/ui/interactive.js +149 -125
- package/out/ui/interactive.js.map +1 -1
- package/out/ui/startup-splash.d.ts +2 -0
- package/out/ui/startup-splash.d.ts.map +1 -0
- package/out/ui/startup-splash.js +185 -0
- package/out/ui/startup-splash.js.map +1 -0
- package/out/ui/theme.js +63 -10
- package/out/ui/theme.js.map +1 -1
- package/package.json +1 -1
package/out/ui/banner.js
CHANGED
|
@@ -2,15 +2,11 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
// @ts-nocheck
|
|
4
4
|
const os = require('os');
|
|
5
|
-
const path = require('path');
|
|
6
5
|
const chalk = require('chalk');
|
|
7
|
-
const { brand, icon, palette, visibleLength, padRight,
|
|
6
|
+
const { brand, icon, palette, visibleLength, padRight, gradientText, macTrafficLights, systemStatusBadge, statusPill, pulseBar, } = require('./theme');
|
|
8
7
|
const { t } = require('../lib/i18n');
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
* Each row is exactly 81 single-cell columns, so we can size the surrounding
|
|
12
|
-
* cyber-card deterministically without measuring per-glyph widths.
|
|
13
|
-
*/
|
|
8
|
+
const { getCliVersion } = require('../lib/paths');
|
|
9
|
+
/** Full ANSI Shadow logo — 81 cells wide. */
|
|
14
10
|
const TECH_LOGO = [
|
|
15
11
|
'██████╗ ███████╗ █████╗ ██████╗████████╗██████╗ ██████╗ ███████╗███████╗███████╗',
|
|
16
12
|
'██╔══██╗██╔════╝██╔══██╗██╔════╝╚══██╔══╝██╔══██╗██╔══██╗██╔════╝██╔════╝██╔════╝',
|
|
@@ -19,7 +15,7 @@ const TECH_LOGO = [
|
|
|
19
15
|
'██║ ██║███████╗██║ ██║╚██████╗ ██║ ██║ ██║ ██║███████╗███████║███████║',
|
|
20
16
|
'╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚══════╝',
|
|
21
17
|
];
|
|
22
|
-
const LOGO_WIDTH =
|
|
18
|
+
const LOGO_WIDTH = TECH_LOGO[0].length;
|
|
23
19
|
const LOGO_GRADIENTS = [
|
|
24
20
|
[palette.pink, palette.primary],
|
|
25
21
|
[palette.pink, palette.primary],
|
|
@@ -29,19 +25,9 @@ const LOGO_GRADIENTS = [
|
|
|
29
25
|
[palette.accent, palette.primary],
|
|
30
26
|
];
|
|
31
27
|
const REPO_URL = 'https://github.com/fecommunity/reactpress';
|
|
32
|
-
/**
|
|
33
|
-
* Shorter, human-friendly form of REPO_URL shown beneath the title bar.
|
|
34
|
-
* The clickable hyperlink still resolves to the full https:// URL via
|
|
35
|
-
* `hyperlink()`, so users can `cmd+click` from any modern terminal.
|
|
36
|
-
*/
|
|
37
28
|
const REPO_DISPLAY = 'github.com/fecommunity/reactpress';
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
* Warp, WezTerm, modern macOS Terminal, VS Code, GNOME Terminal, Kitty, …)
|
|
41
|
-
* render the label as a clickable link. We only emit the escape sequence when
|
|
42
|
-
* stdout is a real TTY — otherwise (CI logs, file redirects, dumb terminals)
|
|
43
|
-
* we fall back to the plain styled label so users never see the raw `]8;;`.
|
|
44
|
-
*/
|
|
29
|
+
const CARD_MIN = 68;
|
|
30
|
+
const ASCII_CARD_PAD = 8;
|
|
45
31
|
function hyperlink(url, label) {
|
|
46
32
|
if (!process.stdout.isTTY)
|
|
47
33
|
return label;
|
|
@@ -49,310 +35,454 @@ function hyperlink(url, label) {
|
|
|
49
35
|
return label;
|
|
50
36
|
return `\u001B]8;;${url}\u0007${label}\u001B]8;;\u0007`;
|
|
51
37
|
}
|
|
52
|
-
function safeReadCliVersion() {
|
|
53
|
-
try {
|
|
54
|
-
return require(path.join(__dirname, '..', 'package.json')).version;
|
|
55
|
-
}
|
|
56
|
-
catch {
|
|
57
|
-
return 'dev';
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
38
|
function homify(p) {
|
|
61
39
|
if (!p)
|
|
62
40
|
return p;
|
|
63
41
|
const home = os.homedir();
|
|
64
|
-
if (home && p.startsWith(home))
|
|
42
|
+
if (home && p.startsWith(home))
|
|
65
43
|
return '~' + p.slice(home.length);
|
|
66
|
-
}
|
|
67
44
|
return p;
|
|
68
45
|
}
|
|
46
|
+
function truncateText(text, maxLen) {
|
|
47
|
+
const raw = String(text);
|
|
48
|
+
if (visibleLength(raw) <= maxLen)
|
|
49
|
+
return raw;
|
|
50
|
+
if (maxLen <= 3)
|
|
51
|
+
return '…';
|
|
52
|
+
let out = '…';
|
|
53
|
+
for (let i = raw.length - 1; i >= 0; i -= 1) {
|
|
54
|
+
const candidate = '…' + raw.slice(i);
|
|
55
|
+
if (visibleLength(candidate) <= maxLen) {
|
|
56
|
+
out = candidate;
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return out;
|
|
61
|
+
}
|
|
62
|
+
function bannerColumns() {
|
|
63
|
+
const raw = Number(process.stdout.columns);
|
|
64
|
+
return raw >= 48 ? raw : 80;
|
|
65
|
+
}
|
|
66
|
+
/** Core dev stack — Docker/Nginx are shown but do not affect the SYSTEM badge. */
|
|
67
|
+
const CORE_SYSTEM_SERVICES = new Set(['sqlite', 'mysql', 'server', 'web']);
|
|
68
|
+
function deriveSystemState(project, status) {
|
|
69
|
+
const type = project && project.type;
|
|
70
|
+
if (type !== 'monorepo' && type !== 'standalone') {
|
|
71
|
+
return 'pending';
|
|
72
|
+
}
|
|
73
|
+
if (!status || !Array.isArray(status.components) || status.components.length === 0) {
|
|
74
|
+
return 'pending';
|
|
75
|
+
}
|
|
76
|
+
const core = status.components.filter((c) => CORE_SYSTEM_SERVICES.has(c.id));
|
|
77
|
+
if (core.length === 0)
|
|
78
|
+
return 'pending';
|
|
79
|
+
const okCount = core.filter((c) => c.ok).length;
|
|
80
|
+
if (okCount === core.length)
|
|
81
|
+
return 'online';
|
|
82
|
+
if (okCount === 0)
|
|
83
|
+
return 'error';
|
|
84
|
+
return 'partial';
|
|
85
|
+
}
|
|
86
|
+
function resolveSystemState(options) {
|
|
87
|
+
if (options && options.systemState)
|
|
88
|
+
return options.systemState;
|
|
89
|
+
if (options && options.status) {
|
|
90
|
+
return deriveSystemState(options.project, options.status);
|
|
91
|
+
}
|
|
92
|
+
const type = options && options.project && options.project.type;
|
|
93
|
+
if (type === 'monorepo' || type === 'standalone')
|
|
94
|
+
return 'pending';
|
|
95
|
+
return 'pending';
|
|
96
|
+
}
|
|
97
|
+
function componentLabel(component) {
|
|
98
|
+
return t(`banner.service.${component.id}`).trim();
|
|
99
|
+
}
|
|
100
|
+
function computeLabelWidth(components) {
|
|
101
|
+
const labels = [
|
|
102
|
+
t('banner.label.mode').trim(),
|
|
103
|
+
t('banner.label.path').trim(),
|
|
104
|
+
...(components || []).map((c) => componentLabel(c)),
|
|
105
|
+
];
|
|
106
|
+
return Math.max(...labels.map((label) => visibleLength(label)), 4);
|
|
107
|
+
}
|
|
108
|
+
function componentStatusPill(component) {
|
|
109
|
+
if (component.ok === 'pending') {
|
|
110
|
+
return statusPill('pending', { pending: t('banner.pulsePending') });
|
|
111
|
+
}
|
|
112
|
+
if (component.id === 'sqlite' || component.id === 'mysql') {
|
|
113
|
+
return statusPill(component.ok, {
|
|
114
|
+
on: t('menu.statusReady'),
|
|
115
|
+
off: t('menu.statusNotReady'),
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
if (component.id === 'docker') {
|
|
119
|
+
return statusPill(component.ok, {
|
|
120
|
+
on: t('menu.statusYes'),
|
|
121
|
+
off: t('menu.statusNo'),
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
if (component.id === 'nginx') {
|
|
125
|
+
return statusPill(component.ok, {
|
|
126
|
+
on: t('banner.nginxRunning'),
|
|
127
|
+
off: t('banner.nginxStopped'),
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
return statusPill(component.ok, {
|
|
131
|
+
on: t('menu.statusOn'),
|
|
132
|
+
off: t('menu.statusOff'),
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
function appendComponentStatusRows(lines, innerWidth, status, lw) {
|
|
136
|
+
if (!status || !Array.isArray(status.components))
|
|
137
|
+
return;
|
|
138
|
+
for (const component of status.components) {
|
|
139
|
+
lines.push(cardRow(infoRow(componentLabel(component), componentStatusPill(component), lw), innerWidth));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function resolveLayout(cols) {
|
|
143
|
+
const showAscii = cols >= LOGO_WIDTH + ASCII_CARD_PAD;
|
|
144
|
+
const width = showAscii
|
|
145
|
+
? Math.min(cols - 2, Math.max(LOGO_WIDTH + ASCII_CARD_PAD, 88))
|
|
146
|
+
: Math.min(Math.max(CARD_MIN, cols - 4), 88);
|
|
147
|
+
return { showAscii, width };
|
|
148
|
+
}
|
|
149
|
+
function centerLine(content, innerWidth) {
|
|
150
|
+
const pad = Math.max(0, Math.floor((innerWidth - visibleLength(content)) / 2));
|
|
151
|
+
return ' '.repeat(pad) + content;
|
|
152
|
+
}
|
|
69
153
|
function renderLogoLines() {
|
|
70
154
|
return TECH_LOGO.map((line, i) => gradientText(line, LOGO_GRADIENTS[i]));
|
|
71
155
|
}
|
|
156
|
+
function mixHexLocal(a, b, t) {
|
|
157
|
+
const parse = (h) => {
|
|
158
|
+
const s = String(h || '#000000').replace('#', '');
|
|
159
|
+
return {
|
|
160
|
+
r: parseInt(s.substring(0, 2), 16),
|
|
161
|
+
g: parseInt(s.substring(2, 4), 16),
|
|
162
|
+
b: parseInt(s.substring(4, 6), 16),
|
|
163
|
+
};
|
|
164
|
+
};
|
|
165
|
+
const pa = parse(a);
|
|
166
|
+
const pb = parse(b);
|
|
167
|
+
const r = Math.round(pa.r + (pb.r - pa.r) * t);
|
|
168
|
+
const g = Math.round(pa.g + (pb.g - pa.g) * t);
|
|
169
|
+
const bl = Math.round(pa.b + (pb.b - pa.b) * t);
|
|
170
|
+
const pad = (n) => n.toString(16).padStart(2, '0');
|
|
171
|
+
return `#${pad(r)}${pad(g)}${pad(bl)}`;
|
|
172
|
+
}
|
|
173
|
+
/** Animated logo — horizontal light streak sweeps across ASCII art. */
|
|
174
|
+
function renderAnimatedLogoLines(frame = 0, revealRatio = 1) {
|
|
175
|
+
const beamSpeed = 3.6;
|
|
176
|
+
const beamGlow = 16;
|
|
177
|
+
const cycleLen = LOGO_WIDTH + beamGlow * 2;
|
|
178
|
+
const head = (frame * beamSpeed) % cycleLen;
|
|
179
|
+
const trail = (head - beamGlow * 0.75 + cycleLen) % cycleLen;
|
|
180
|
+
const streamHot = '#FFFFFF';
|
|
181
|
+
const streamCore = palette.accent;
|
|
182
|
+
const streamTrail = palette.pink;
|
|
183
|
+
function beamIntensity(col, rowIndex, beamHead) {
|
|
184
|
+
const pos = col + rowIndex * 2.2;
|
|
185
|
+
const raw = Math.abs(pos - beamHead);
|
|
186
|
+
const wrapped = Math.min(raw, cycleLen - raw);
|
|
187
|
+
if (wrapped >= beamGlow)
|
|
188
|
+
return 0;
|
|
189
|
+
const t = 1 - wrapped / beamGlow;
|
|
190
|
+
return t * t;
|
|
191
|
+
}
|
|
192
|
+
function paintChar(ch, ci, rowIndex, colors, lineLen) {
|
|
193
|
+
if (ch === ' ')
|
|
194
|
+
return ch;
|
|
195
|
+
const n = Math.max(lineLen - 1, 1);
|
|
196
|
+
const ratio = ci / n;
|
|
197
|
+
const base = mixHexLocal(colors[0], colors[1] || colors[0], ratio);
|
|
198
|
+
const dimBase = mixHexLocal(base, palette.surface, 0.85);
|
|
199
|
+
const primary = beamIntensity(ci, rowIndex, head);
|
|
200
|
+
const secondary = beamIntensity(ci, rowIndex, trail) * 0.55;
|
|
201
|
+
const glow = Math.min(1, primary + secondary);
|
|
202
|
+
if (glow <= 0.03) {
|
|
203
|
+
return chalk.hex(dimBase)(ch);
|
|
204
|
+
}
|
|
205
|
+
let lit = dimBase;
|
|
206
|
+
if (primary > 0.65) {
|
|
207
|
+
lit = mixHexLocal(streamHot, streamCore, 1 - primary);
|
|
208
|
+
}
|
|
209
|
+
else if (glow > 0.3) {
|
|
210
|
+
lit = mixHexLocal(mixHexLocal(dimBase, streamTrail, 0.15), streamCore, glow);
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
lit = mixHexLocal(dimBase, streamTrail, glow * 0.9);
|
|
214
|
+
}
|
|
215
|
+
const c = chalk.hex(lit);
|
|
216
|
+
return glow > 0.45 ? c.bold(ch) : c(ch);
|
|
217
|
+
}
|
|
218
|
+
return TECH_LOGO.map((line, rowIndex) => {
|
|
219
|
+
const rowThreshold = (rowIndex + 1) / TECH_LOGO.length;
|
|
220
|
+
if (revealRatio < rowThreshold - 0.06) {
|
|
221
|
+
const ghost = mixHexLocal(palette.border, palette.surface, 0.5);
|
|
222
|
+
return chalk.hex(ghost)([...line]
|
|
223
|
+
.map((ch) => (ch === ' ' ? ch : '░'))
|
|
224
|
+
.join(''));
|
|
225
|
+
}
|
|
226
|
+
const colors = LOGO_GRADIENTS[rowIndex];
|
|
227
|
+
return [...line]
|
|
228
|
+
.map((ch, ci) => paintChar(ch, ci, rowIndex, colors, line.length))
|
|
229
|
+
.join('');
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
function startupPulseRow(innerWidth, { completed = 0, total = 0, ready = false, frame = 0 } = {}) {
|
|
233
|
+
const barWidth = Math.min(24, Math.max(8, innerWidth - 22));
|
|
234
|
+
let filled = 0;
|
|
235
|
+
if (total > 0) {
|
|
236
|
+
filled = Math.round((completed / total) * barWidth);
|
|
237
|
+
if (!ready)
|
|
238
|
+
filled = Math.min(barWidth, filled + (frame % 2));
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
filled = 4 + (frame % Math.max(1, barWidth - 4));
|
|
242
|
+
}
|
|
243
|
+
const label = brand.muted(padRight(t('banner.pulseLabel').trim(), 6));
|
|
244
|
+
const bar = pulseBar(barWidth, filled);
|
|
245
|
+
const status = ready
|
|
246
|
+
? brand.success(t('banner.pulseReady'))
|
|
247
|
+
: brand.warn(t('banner.pulsePending'));
|
|
248
|
+
return centerLine(`${label}${bar} ${status}`, innerWidth);
|
|
249
|
+
}
|
|
250
|
+
function cardTop(width) {
|
|
251
|
+
return brand.border(` ╭${'─'.repeat(width - 2)}╮`);
|
|
252
|
+
}
|
|
253
|
+
function cardBottom(width) {
|
|
254
|
+
return brand.border(` ╰${'─'.repeat(width - 2)}╯`);
|
|
255
|
+
}
|
|
256
|
+
function cardRow(content, innerWidth) {
|
|
257
|
+
const padded = padRight(content, innerWidth);
|
|
258
|
+
return brand.border(' │ ') + padded + brand.border(' │');
|
|
259
|
+
}
|
|
260
|
+
function cardGap(innerWidth) {
|
|
261
|
+
return cardRow('', innerWidth);
|
|
262
|
+
}
|
|
263
|
+
function titleBarRow(innerWidth, version, systemState) {
|
|
264
|
+
const lights = macTrafficLights() + brand.dim(' ');
|
|
265
|
+
const lightsW = visibleLength(lights);
|
|
266
|
+
const title = brand.dim(`reactpress · v${version}`);
|
|
267
|
+
const titleW = visibleLength(title);
|
|
268
|
+
const status = systemStatusBadge(systemState);
|
|
269
|
+
const statusW = visibleLength(status);
|
|
270
|
+
const titleStart = Math.max(lightsW + 1, Math.floor((innerWidth - titleW) / 2));
|
|
271
|
+
const padBeforeTitle = Math.max(0, titleStart - lightsW);
|
|
272
|
+
let line = lights + ' '.repeat(padBeforeTitle) + title;
|
|
273
|
+
const gap = innerWidth - visibleLength(line) - statusW;
|
|
274
|
+
if (gap >= 2) {
|
|
275
|
+
line += ' '.repeat(gap) + status;
|
|
276
|
+
}
|
|
277
|
+
else if (gap >= 0) {
|
|
278
|
+
line += ' '.repeat(gap);
|
|
279
|
+
}
|
|
280
|
+
return padRight(line, innerWidth);
|
|
281
|
+
}
|
|
282
|
+
function repoRow(innerWidth) {
|
|
283
|
+
const link = brand.dim(icon.link + ' ') +
|
|
284
|
+
hyperlink(REPO_URL, brand.dim.underline(REPO_DISPLAY));
|
|
285
|
+
return centerLine(link, innerWidth);
|
|
286
|
+
}
|
|
287
|
+
function headerRows(innerWidth, version, systemState) {
|
|
288
|
+
return [titleBarRow(innerWidth, version, systemState), repoRow(innerWidth)];
|
|
289
|
+
}
|
|
290
|
+
function wordmarkHero() {
|
|
291
|
+
const bars = brand.border('═══');
|
|
292
|
+
const word = gradientText('REACTPRESS', [palette.pink, palette.primary, palette.accent], {
|
|
293
|
+
bold: true,
|
|
294
|
+
});
|
|
295
|
+
return `${bars} ${word} ${bars}`;
|
|
296
|
+
}
|
|
297
|
+
/** Compact wordmark with the same streaming-light sweep. */
|
|
298
|
+
function renderAnimatedWordmark(frame = 0) {
|
|
299
|
+
const text = 'REACTPRESS';
|
|
300
|
+
const beamSpeed = 3.2;
|
|
301
|
+
const beamGlow = 6;
|
|
302
|
+
const cycleLen = text.length + beamGlow * 2;
|
|
303
|
+
const head = (frame * beamSpeed) % cycleLen;
|
|
304
|
+
const streamHot = '#F0FDFF';
|
|
305
|
+
const bars = brand.border('═══');
|
|
306
|
+
const painted = [...text]
|
|
307
|
+
.map((ch, ci) => {
|
|
308
|
+
const raw = Math.abs(ci + 0.5 - head);
|
|
309
|
+
const wrapped = Math.min(raw, cycleLen - raw);
|
|
310
|
+
const glow = wrapped >= beamGlow ? 0 : Math.pow(1 - wrapped / beamGlow, 2);
|
|
311
|
+
const base = mixHexLocal(palette.pink, palette.accent, ci / Math.max(text.length - 1, 1));
|
|
312
|
+
const dimBase = mixHexLocal(base, palette.surface, 0.65);
|
|
313
|
+
if (glow <= 0.05)
|
|
314
|
+
return chalk.hex(dimBase).bold(ch);
|
|
315
|
+
const lit = glow > 0.75
|
|
316
|
+
? mixHexLocal(streamHot, palette.accent, 1 - glow)
|
|
317
|
+
: mixHexLocal(dimBase, palette.accent, glow);
|
|
318
|
+
const c = chalk.hex(lit);
|
|
319
|
+
return glow > 0.5 ? c.bold(ch) : c(ch);
|
|
320
|
+
})
|
|
321
|
+
.join('');
|
|
322
|
+
return `${bars} ${painted} ${bars}`;
|
|
323
|
+
}
|
|
324
|
+
function taglineRow() {
|
|
325
|
+
return `${brand.accent('◇')} ${brand.accent(t('banner.tagline').trim())}`;
|
|
326
|
+
}
|
|
327
|
+
function softRule(innerWidth) {
|
|
328
|
+
const w = Math.min(innerWidth - 4, Math.max(28, innerWidth - 16));
|
|
329
|
+
return centerLine(brand.border('─'.repeat(w)), innerWidth);
|
|
330
|
+
}
|
|
72
331
|
function modeChip(type) {
|
|
73
332
|
if (type === 'monorepo') {
|
|
74
|
-
return chalk
|
|
75
|
-
.bgHex(palette.primary)
|
|
76
|
-
.hex('#0B1220')
|
|
77
|
-
.bold(` ${t('banner.mode.monorepo')} `);
|
|
333
|
+
return chalk.bgHex(palette.primary).hex('#0B1220').bold(` ${t('banner.mode.monorepo').trim()} `);
|
|
78
334
|
}
|
|
79
335
|
if (type === 'standalone') {
|
|
80
|
-
return chalk
|
|
81
|
-
.bgHex(palette.accent)
|
|
82
|
-
.hex('#0B1220')
|
|
83
|
-
.bold(` ${t('banner.mode.standalone')} `);
|
|
84
|
-
}
|
|
85
|
-
return chalk
|
|
86
|
-
.bgHex(palette.gray)
|
|
87
|
-
.hex('#0B1220')
|
|
88
|
-
.bold(` ${t('banner.mode.uninitialized')} `);
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* Decide how "ready" the welcome banner should look. When a fully
|
|
92
|
-
* initialized project is detected we render the pulse bar at 100% and
|
|
93
|
-
* report `ONLINE` status, instead of the static 70% placeholder that used
|
|
94
|
-
* to make `doctor` runs look incomplete even when everything passed.
|
|
95
|
-
*/
|
|
96
|
-
function bannerReadyState(options) {
|
|
97
|
-
const type = options && options.project && options.project.type;
|
|
98
|
-
if (type === 'monorepo' || type === 'standalone') {
|
|
99
|
-
return { ratio: 1, ready: true };
|
|
336
|
+
return chalk.bgHex(palette.accent).hex('#0B1220').bold(` ${t('banner.mode.standalone').trim()} `);
|
|
100
337
|
}
|
|
101
|
-
return {
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
*/
|
|
107
|
-
function brandedTopBorder(version, width) {
|
|
108
|
-
const titleBlock = brand.primary('[') +
|
|
109
|
-
' ' +
|
|
110
|
-
gradientText('REACTPRESS', [palette.primary, palette.accent], { bold: true }) +
|
|
111
|
-
' ' +
|
|
112
|
-
brand.muted('·') +
|
|
113
|
-
' ' +
|
|
114
|
-
brand.accent(`v${version}`) +
|
|
115
|
-
' ' +
|
|
116
|
-
brand.primary(']');
|
|
117
|
-
const dashTotal = Math.max(0, width - 2 - visibleLength(titleBlock));
|
|
118
|
-
const left = Math.floor(dashTotal / 2);
|
|
119
|
-
const right = dashTotal - left;
|
|
120
|
-
return (brand.primary('╔' + '═'.repeat(left)) +
|
|
121
|
-
titleBlock +
|
|
122
|
-
brand.primary('═'.repeat(right) + '╗'));
|
|
123
|
-
}
|
|
124
|
-
function bottomBorder(width) {
|
|
125
|
-
return brand.primary('╚' + '═'.repeat(width - 2) + '╝');
|
|
126
|
-
}
|
|
127
|
-
function bodyLine(content, innerWidth) {
|
|
128
|
-
const padded = padRight(content, innerWidth);
|
|
129
|
-
return brand.primary('║ ') + padded + brand.primary(' ║');
|
|
130
|
-
}
|
|
131
|
-
function emptyBodyLine(innerWidth) {
|
|
132
|
-
return bodyLine('', innerWidth);
|
|
133
|
-
}
|
|
134
|
-
/**
|
|
135
|
-
* A subtle "CRT scan-line" rendered just under the logo.
|
|
136
|
-
* ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
|
|
137
|
-
*/
|
|
138
|
-
function scanline(width) {
|
|
139
|
-
return brand.muted('▔'.repeat(width));
|
|
140
|
-
}
|
|
141
|
-
/**
|
|
142
|
-
* Width of the left-side banner label column.
|
|
143
|
-
*
|
|
144
|
-
* Sized to fit our longest English label (`MODE` / `PATH` → 4 cells)
|
|
145
|
-
* plus a 2-cell trailing gap, which also accommodates the Chinese
|
|
146
|
-
* translations `模式` / `路径` (4 East-Asian cells each).
|
|
147
|
-
*/
|
|
148
|
-
const LABEL_WIDTH = 6;
|
|
149
|
-
/**
|
|
150
|
-
* Centered, dim repo subtitle that sits directly under the top border.
|
|
151
|
-
* Replaces the previous in-body `◇ REPO ↗ …` row, which competed visually
|
|
152
|
-
* with the operational fields (MODE / PATH / pulse) further down.
|
|
153
|
-
*/
|
|
154
|
-
function repoSubline(innerWidth) {
|
|
155
|
-
const link = brand.muted('↗ ') + hyperlink(REPO_URL, brand.accent.underline(REPO_DISPLAY));
|
|
156
|
-
const pad = Math.max(0, Math.floor((innerWidth - visibleLength(link)) / 2));
|
|
157
|
-
return ' '.repeat(pad) + link;
|
|
158
|
-
}
|
|
159
|
-
/**
|
|
160
|
-
* Single-cell-wide chip label, e.g. `◇ MODE ▸ monorepo`.
|
|
161
|
-
*/
|
|
162
|
-
function infoRow(label, value) {
|
|
163
|
-
return (brand.accent('◇ ') +
|
|
164
|
-
brand.muted(padRight(label, LABEL_WIDTH)) +
|
|
165
|
-
' ' +
|
|
166
|
-
brand.primary('▸ ') +
|
|
167
|
-
brand.dim(value));
|
|
168
|
-
}
|
|
169
|
-
/**
|
|
170
|
-
* Render the "command rail" navigation footer:
|
|
171
|
-
* ⟫ init ⟫ dev ⟫ build ⟫ deploy ⟫ publish
|
|
172
|
-
*/
|
|
338
|
+
return chalk.bgHex(palette.gray).hex('#0B1220').bold(` ${t('banner.mode.uninitialized').trim()} `);
|
|
339
|
+
}
|
|
340
|
+
function infoRow(label, value, lw) {
|
|
341
|
+
return brand.muted(padRight(label, lw)) + brand.border('│ ') + value;
|
|
342
|
+
}
|
|
173
343
|
function commandRail() {
|
|
174
344
|
const items = ['init', 'dev', 'build', 'deploy', 'publish'];
|
|
175
345
|
return items
|
|
176
|
-
.map((name) => brand.primary('
|
|
177
|
-
.join(brand.
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
* Wide, full-fat cyber banner: ASCII logo + scan-line + bordered card.
|
|
181
|
-
*/
|
|
182
|
-
function printWideBanner(version, options) {
|
|
183
|
-
const cols = terminalWidth();
|
|
184
|
-
const cardWidth = Math.min(Math.max(LOGO_WIDTH + 8, 88), cols - 2);
|
|
185
|
-
const innerWidth = cardWidth - 4;
|
|
186
|
-
const lines = [];
|
|
187
|
-
lines.push('');
|
|
188
|
-
lines.push(' ' + brandedTopBorder(version, cardWidth));
|
|
189
|
-
lines.push(' ' + bodyLine(repoSubline(innerWidth), innerWidth));
|
|
190
|
-
lines.push(' ' + emptyBodyLine(innerWidth));
|
|
191
|
-
const logoIndent = Math.max(0, Math.floor((innerWidth - LOGO_WIDTH) / 2));
|
|
192
|
-
const indent = ' '.repeat(logoIndent);
|
|
193
|
-
for (const logoLine of renderLogoLines()) {
|
|
194
|
-
lines.push(' ' + bodyLine(indent + logoLine, innerWidth));
|
|
195
|
-
}
|
|
196
|
-
const scanWidth = Math.min(innerWidth - 2, LOGO_WIDTH);
|
|
197
|
-
const scanIndent = ' '.repeat(Math.max(0, Math.floor((innerWidth - scanWidth) / 2)));
|
|
198
|
-
lines.push(' ' + bodyLine(scanIndent + scanline(scanWidth), innerWidth));
|
|
199
|
-
lines.push(' ' + emptyBodyLine(innerWidth));
|
|
200
|
-
const ready = bannerReadyState(options);
|
|
201
|
-
const subtitle = chalk.bold(brand.accent('◆ ')) +
|
|
202
|
-
gradientText(t('banner.subtitle').trim(), [palette.accent, palette.primary, palette.pink], {
|
|
203
|
-
bold: true,
|
|
204
|
-
});
|
|
205
|
-
const stateLabel = ready.ready
|
|
206
|
-
? brand.success(t('banner.systemOnline').trim())
|
|
207
|
-
: brand.warn(t('banner.systemPending').trim());
|
|
208
|
-
const right = statusLights(ready.ready ? 'online' : 'pending') +
|
|
209
|
-
' ' +
|
|
210
|
-
brand.dim(t('banner.systemLabel').trim() + ' ') +
|
|
211
|
-
stateLabel;
|
|
212
|
-
lines.push(' ' + bodyLine(subtitle + spacer(subtitle, right, innerWidth) + right, innerWidth));
|
|
213
|
-
lines.push(' ' + emptyBodyLine(innerWidth));
|
|
346
|
+
.map((name) => brand.primary('》 ') + gradientText(name, [palette.primary, palette.accent]))
|
|
347
|
+
.join(brand.dim(' '));
|
|
348
|
+
}
|
|
349
|
+
function appendInfoRows(lines, innerWidth, options, lw) {
|
|
214
350
|
if (options.project) {
|
|
215
|
-
lines.push('
|
|
216
|
-
bodyLine(brand.accent('◇ ') +
|
|
217
|
-
brand.muted(padRight(t('banner.label.mode').trim(), LABEL_WIDTH)) +
|
|
218
|
-
' ' +
|
|
219
|
-
modeChip(options.project.type), innerWidth));
|
|
351
|
+
lines.push(cardRow(infoRow(t('banner.label.mode').trim(), modeChip(options.project.type), lw), innerWidth));
|
|
220
352
|
}
|
|
221
353
|
if (options.projectRoot) {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
if (pulseWidth > 8) {
|
|
227
|
-
const filled = Math.max(1, Math.min(pulseWidth, Math.round(pulseWidth * ready.ratio)));
|
|
228
|
-
const pulse = pulseBar(pulseWidth, filled);
|
|
229
|
-
const pulseStatus = ready.ready
|
|
230
|
-
? t('banner.pulseReady').trim()
|
|
231
|
-
: t('banner.pulsePending').trim();
|
|
232
|
-
const pulseLine = brand.accent('◇ ') +
|
|
233
|
-
brand.muted(padRight(t('banner.pulseLabel').trim(), LABEL_WIDTH)) +
|
|
234
|
-
' ' +
|
|
235
|
-
pulse +
|
|
236
|
-
' ' +
|
|
237
|
-
(ready.ready ? brand.success(pulseStatus) : brand.warn(pulseStatus));
|
|
238
|
-
lines.push(' ' + bodyLine(pulseLine, innerWidth));
|
|
354
|
+
const pathValue = brand.primary('▶') +
|
|
355
|
+
brand.dim(' ') +
|
|
356
|
+
brand.dim(truncateText(homify(options.projectRoot), innerWidth - lw - 8));
|
|
357
|
+
lines.push(cardRow(infoRow(t('banner.label.path').trim(), pathValue, lw), innerWidth));
|
|
239
358
|
}
|
|
240
|
-
lines.
|
|
241
|
-
lines.push(' ' + bottomBorder(cardWidth));
|
|
242
|
-
lines.push(' ' + commandRail());
|
|
243
|
-
lines.push('');
|
|
244
|
-
for (const line of lines)
|
|
245
|
-
console.log(line);
|
|
359
|
+
appendComponentStatusRows(lines, innerWidth, options.status, lw);
|
|
246
360
|
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
const used = visibleLength(left) + visibleLength(right);
|
|
253
|
-
const gap = Math.max(2, innerWidth - used);
|
|
254
|
-
return ' '.repeat(gap);
|
|
255
|
-
}
|
|
256
|
-
/**
|
|
257
|
-
* Compact cyber banner for terminals that cannot host the full ASCII logo.
|
|
258
|
-
*/
|
|
259
|
-
function printCompactBanner(version, options) {
|
|
260
|
-
const cols = terminalWidth();
|
|
261
|
-
const cardWidth = Math.min(cols - 2, 76);
|
|
262
|
-
const innerWidth = cardWidth - 4;
|
|
361
|
+
function composeBannerLines(version, options, { showAscii, width, startup } = {}) {
|
|
362
|
+
const innerWidth = width - 4;
|
|
363
|
+
const systemState = startup && !startup.ready ? 'pending' : resolveSystemState(options);
|
|
364
|
+
const components = (options.status && options.status.components) || [];
|
|
365
|
+
const lw = computeLabelWidth(components);
|
|
263
366
|
const lines = [];
|
|
264
367
|
lines.push('');
|
|
265
|
-
lines.push(
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
const ready = bannerReadyState(options);
|
|
269
|
-
const wordmark = brand.primary('▌▍▎ ') +
|
|
270
|
-
gradientText('REACTPRESS', [palette.pink, palette.primary, palette.accent], {
|
|
271
|
-
bold: true,
|
|
272
|
-
}) +
|
|
273
|
-
brand.primary(' ▎▍▌');
|
|
274
|
-
const lights = statusLights(ready.ready ? 'online' : 'pending');
|
|
275
|
-
lines.push(' ' + bodyLine(wordmark + spacer(wordmark, lights, innerWidth) + lights, innerWidth));
|
|
276
|
-
const subtitle = chalk.bold(brand.accent('◆ ')) + brand.dim(t('banner.subtitle').trim());
|
|
277
|
-
lines.push(' ' + bodyLine(subtitle, innerWidth));
|
|
278
|
-
lines.push(' ' + emptyBodyLine(innerWidth));
|
|
279
|
-
if (options.project) {
|
|
280
|
-
lines.push(' ' +
|
|
281
|
-
bodyLine(brand.accent('◇ ') +
|
|
282
|
-
brand.muted(padRight(t('banner.label.mode').trim(), LABEL_WIDTH)) +
|
|
283
|
-
' ' +
|
|
284
|
-
modeChip(options.project.type), innerWidth));
|
|
368
|
+
lines.push(cardTop(width));
|
|
369
|
+
for (const row of headerRows(innerWidth, version, systemState)) {
|
|
370
|
+
lines.push(cardRow(row, innerWidth));
|
|
285
371
|
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
372
|
+
lines.push(cardGap(innerWidth));
|
|
373
|
+
if (showAscii) {
|
|
374
|
+
const logoLines = startup
|
|
375
|
+
? renderAnimatedLogoLines(startup.frame || 0, 1)
|
|
376
|
+
: renderLogoLines();
|
|
377
|
+
for (const logoLine of logoLines) {
|
|
378
|
+
lines.push(cardRow(centerLine(logoLine, innerWidth), innerWidth));
|
|
379
|
+
}
|
|
380
|
+
lines.push(cardGap(innerWidth));
|
|
381
|
+
lines.push(cardRow(centerLine(taglineRow(), innerWidth), innerWidth));
|
|
382
|
+
if (startup && !startup.ready) {
|
|
383
|
+
lines.push(cardRow(startupPulseRow(innerWidth, {
|
|
384
|
+
completed: startup.completed,
|
|
385
|
+
total: startup.total,
|
|
386
|
+
ready: startup.ready,
|
|
387
|
+
frame: startup.frame || 0,
|
|
388
|
+
}), innerWidth));
|
|
389
|
+
}
|
|
289
390
|
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
391
|
+
else {
|
|
392
|
+
const wordmark = startup ? renderAnimatedWordmark(startup.frame || 0) : wordmarkHero();
|
|
393
|
+
lines.push(cardRow(centerLine(wordmark, innerWidth), innerWidth));
|
|
394
|
+
lines.push(cardRow(centerLine(taglineRow(), innerWidth), innerWidth));
|
|
395
|
+
if (startup && !startup.ready) {
|
|
396
|
+
lines.push(cardRow(startupPulseRow(innerWidth, {
|
|
397
|
+
completed: startup.completed,
|
|
398
|
+
total: startup.total,
|
|
399
|
+
ready: startup.ready,
|
|
400
|
+
frame: startup.frame || 0,
|
|
401
|
+
}), innerWidth));
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
lines.push(cardRow(softRule(innerWidth), innerWidth));
|
|
405
|
+
appendInfoRows(lines, innerWidth, options, lw);
|
|
406
|
+
lines.push(cardBottom(width));
|
|
407
|
+
lines.push(` ${commandRail()}`);
|
|
293
408
|
lines.push('');
|
|
409
|
+
return lines;
|
|
410
|
+
}
|
|
411
|
+
function printCardBanner(version, options, { showAscii, width, startup } = {}) {
|
|
412
|
+
const lines = composeBannerLines(version, options, { showAscii, width, startup });
|
|
294
413
|
for (const line of lines)
|
|
295
414
|
console.log(line);
|
|
296
415
|
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
const
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
416
|
+
function printCompactServiceStatus(status) {
|
|
417
|
+
if (!status || !Array.isArray(status.components))
|
|
418
|
+
return;
|
|
419
|
+
const lw = computeLabelWidth(status.components);
|
|
420
|
+
for (const component of status.components) {
|
|
421
|
+
console.log(` ${brand.muted(padRight(componentLabel(component), lw))}${brand.border('│ ')}${componentStatusPill(component)}`);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
function printCompactBanner(version, options) {
|
|
425
|
+
const systemState = resolveSystemState(options);
|
|
305
426
|
console.log('');
|
|
306
|
-
console.log(` ${
|
|
307
|
-
console.log(` ${brand.
|
|
308
|
-
|
|
309
|
-
|
|
427
|
+
console.log(` ${macTrafficLights()} ${brand.dim(`reactpress · v${version}`)} ${systemStatusBadge(systemState)}`);
|
|
428
|
+
console.log(` ${brand.muted(icon.link)} ${hyperlink(REPO_URL, brand.dim.underline(REPO_DISPLAY))}`);
|
|
429
|
+
console.log('');
|
|
430
|
+
for (const logoLine of renderLogoLines()) {
|
|
431
|
+
console.log(` ${logoLine}`);
|
|
310
432
|
}
|
|
433
|
+
console.log(` ${centerLine(taglineRow(), bannerColumns() - 4)}`);
|
|
434
|
+
if (options.project)
|
|
435
|
+
console.log(` ${modeChip(options.project.type)}`);
|
|
311
436
|
if (options.projectRoot) {
|
|
312
|
-
console.log(` ${
|
|
437
|
+
console.log(` ${brand.primary('▶')} ${brand.dim(homify(options.projectRoot))}`);
|
|
313
438
|
}
|
|
314
|
-
|
|
439
|
+
printCompactServiceStatus(options.status);
|
|
440
|
+
console.log(` ${commandRail()}`);
|
|
315
441
|
console.log('');
|
|
316
442
|
}
|
|
317
|
-
/**
|
|
318
|
-
* Print the top-of-screen banner. Adaptive to terminal width: collapses to a
|
|
319
|
-
* single-line greeting on very narrow terminals, otherwise renders a bordered
|
|
320
|
-
* cyber-card with the full ANSI Shadow logo when there is room.
|
|
321
|
-
*
|
|
322
|
-
* @param {{
|
|
323
|
-
* projectRoot?: string,
|
|
324
|
-
* project?: { type: string, hasClient: boolean, hasServerSource: boolean }
|
|
325
|
-
* }} [options]
|
|
326
|
-
*/
|
|
327
443
|
function printBanner(options = {}) {
|
|
328
|
-
const version =
|
|
329
|
-
const cols =
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
return;
|
|
333
|
-
}
|
|
334
|
-
if (cols < LOGO_WIDTH + 10) {
|
|
444
|
+
const version = getCliVersion();
|
|
445
|
+
const cols = bannerColumns();
|
|
446
|
+
const { showAscii, width } = resolveLayout(cols);
|
|
447
|
+
if (cols < CARD_MIN) {
|
|
335
448
|
printCompactBanner(version, options);
|
|
336
449
|
return;
|
|
337
450
|
}
|
|
338
|
-
|
|
451
|
+
printCardBanner(version, options, { showAscii, width });
|
|
339
452
|
}
|
|
340
|
-
/**
|
|
341
|
-
* Box helper retained for backwards compatibility: a few callers still
|
|
342
|
-
* import `box` from this module to wrap arbitrary multi-line content.
|
|
343
|
-
*/
|
|
344
453
|
function box(lines, { width } = {}) {
|
|
345
454
|
const innerWidth = width
|
|
346
455
|
? width - 4
|
|
347
456
|
: lines.reduce((max, line) => Math.max(max, visibleLength(line)), 0);
|
|
348
|
-
const horizontal = '
|
|
349
|
-
const top = brand.
|
|
350
|
-
const bottom = brand.
|
|
457
|
+
const horizontal = '─'.repeat(innerWidth + 2);
|
|
458
|
+
const top = brand.border(` ╭${horizontal}╮`);
|
|
459
|
+
const bottom = brand.border(` ╰${horizontal}╯`);
|
|
351
460
|
const body = lines.map((line) => {
|
|
352
461
|
const padded = padRight(line, innerWidth);
|
|
353
|
-
return brand.
|
|
462
|
+
return brand.border(' │ ') + padded + brand.border(' │');
|
|
354
463
|
});
|
|
355
464
|
return [top, ...body, bottom];
|
|
356
465
|
}
|
|
357
|
-
module.exports = {
|
|
466
|
+
module.exports = {
|
|
467
|
+
printBanner,
|
|
468
|
+
visibleLength,
|
|
469
|
+
padRight,
|
|
470
|
+
TECH_LOGO,
|
|
471
|
+
LOGO_GRADIENTS,
|
|
472
|
+
LOGO_WIDTH,
|
|
473
|
+
box,
|
|
474
|
+
commandRail,
|
|
475
|
+
renderLogoLines,
|
|
476
|
+
renderAnimatedLogoLines,
|
|
477
|
+
renderAnimatedWordmark,
|
|
478
|
+
composeBannerLines,
|
|
479
|
+
startupPulseRow,
|
|
480
|
+
resolveLayout,
|
|
481
|
+
bannerColumns,
|
|
482
|
+
titleBarRow,
|
|
483
|
+
macTrafficLights,
|
|
484
|
+
deriveSystemState,
|
|
485
|
+
resolveSystemState,
|
|
486
|
+
CORE_SYSTEM_SERVICES,
|
|
487
|
+
};
|
|
358
488
|
//# sourceMappingURL=banner.js.map
|