@blockrun/franklin 3.2.0 → 3.2.1
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/dist/banner.js +125 -9
- package/package.json +1 -1
package/dist/banner.js
CHANGED
|
@@ -1,5 +1,39 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
-
//
|
|
2
|
+
// ─── Ben Franklin portrait ─────────────────────────────────────────────────
|
|
3
|
+
//
|
|
4
|
+
// Generated once, at build time, from the Joseph Duplessis 1785 oil painting
|
|
5
|
+
// of Benjamin Franklin (same source as the portrait on the US $100 bill).
|
|
6
|
+
// Public domain image from Wikimedia Commons:
|
|
7
|
+
// https://commons.wikimedia.org/wiki/File:BenFranklinDuplessis.jpg
|
|
8
|
+
//
|
|
9
|
+
// Rendered via chafa with:
|
|
10
|
+
// chafa --size=20x10 --symbols=block --colors=256 ben-franklin.jpg
|
|
11
|
+
//
|
|
12
|
+
// The raw ANSI escape codes are hex-encoded below so the TS source stays
|
|
13
|
+
// readable and diff-friendly. Each string is one row of the portrait.
|
|
14
|
+
// Visible dimensions: ~17 characters wide × 10 rows tall.
|
|
15
|
+
//
|
|
16
|
+
// Rendered best in a 256-color or truecolor terminal. Degrades gracefully
|
|
17
|
+
// (shows as block-character garbage) on ancient terminals — but those
|
|
18
|
+
// are long gone and we don't support them.
|
|
19
|
+
const BEN_PORTRAIT_ROWS = [
|
|
20
|
+
'\x1b[0m\x1b[38;5;232;48;5;16m▏ \x1b[48;5;232m \x1b[48;5;16m▂\x1b[48;5;232m \x1b[38;5;233m▃▃\x1b[48;5;233m \x1b[0m',
|
|
21
|
+
'\x1b[38;5;232;48;5;16m▏ \x1b[38;5;234m▂\x1b[38;5;95;48;5;232m▄\x1b[38;5;137;48;5;233m▅\x1b[38;5;173m▅\x1b[38;5;137;48;5;234m▃\x1b[38;5;235;48;5;233m▁ \x1b[38;5;234m▄ \x1b[0m',
|
|
22
|
+
'\x1b[38;5;232;48;5;16m▏▕ \x1b[38;5;234;48;5;232m▁\x1b[38;5;235;48;5;237m▎\x1b[38;5;58;48;5;179m▌\x1b[38;5;131m▄\x1b[38;5;94m▖\x1b[38;5;58;48;5;137m▗\x1b[48;5;235m▍\x1b[38;5;235;48;5;234m▆▆▄▅ \x1b[0m',
|
|
23
|
+
'\x1b[38;5;233;48;5;232m▏\x1b[38;5;232;48;5;16m▏\x1b[38;5;16;48;5;232m▏\x1b[38;5;233m▗\x1b[38;5;236;48;5;240m▎\x1b[38;5;235;48;5;238m▄\x1b[38;5;95;48;5;173m▎\x1b[38;5;173;48;5;179m▖\x1b[38;5;137m▁\x1b[48;5;94m▍\x1b[38;5;94;48;5;233m▋\x1b[38;5;233;48;5;235m▖\x1b[38;5;236m▄ \x1b[38;5;235;48;5;234m▌ \x1b[0m',
|
|
24
|
+
'\x1b[38;5;233;48;5;232m▏ \x1b[38;5;232;48;5;234m▌\x1b[38;5;240;48;5;236m▁\x1b[38;5;95;48;5;235m▁\x1b[38;5;186;48;5;137m▗ \x1b[38;5;95;48;5;173m▃\x1b[38;5;137;48;5;94m▘\x1b[38;5;58;48;5;234m▍\x1b[38;5;234;48;5;236m▘ \x1b[38;5;236;48;5;235m▏ \x1b[38;5;232;48;5;234m▄\x1b[0m',
|
|
25
|
+
'\x1b[38;5;233;48;5;232m▏\x1b[38;5;235m▁\x1b[38;5;95;48;5;234m▄\x1b[38;5;137;48;5;236m▆\x1b[48;5;95m \x1b[38;5;101m▁\x1b[38;5;137m▔\x1b[48;5;186m▄\x1b[48;5;95m▍\x1b[48;5;236m▃\x1b[38;5;143;48;5;235m▃\x1b[38;5;236m▏\x1b[38;5;235;48;5;236m▃\x1b[48;5;235m \x1b[38;5;234m▁\x1b[38;5;235;48;5;232m▘\x1b[38;5;232;48;5;16m▔\x1b[0m',
|
|
26
|
+
'\x1b[38;5;238;48;5;233m▗\x1b[38;5;8;48;5;137m▘\x1b[38;5;138m▘ \x1b[38;5;137;48;5;95m▊\x1b[38;5;95;48;5;101m▎\x1b[38;5;137;48;5;95m▎\x1b[48;5;101m▌\x1b[48;5;95m \x1b[38;5;95;48;5;101m▏\x1b[38;5;143m▔\x1b[38;5;101;48;5;236m▅\x1b[38;5;240;48;5;234m▖\x1b[38;5;235m▞\x1b[38;5;234;48;5;232m▘\x1b[38;5;232;48;5;16m▔ \x1b[0m',
|
|
27
|
+
'\x1b[38;5;52;48;5;95m▋\x1b[48;5;137m \x1b[38;5;95;48;5;101m▕\x1b[38;5;240;48;5;137m▌\x1b[38;5;101m▂ \x1b[48;5;95m▏\x1b[38;5;239m▖▂\x1b[38;5;237m▄\x1b[38;5;101;48;5;234m▘\x1b[38;5;234;48;5;233m▝\x1b[48;5;232m▖\x1b[48;5;16m \x1b[0m',
|
|
28
|
+
'\x1b[38;5;235;48;5;95m▌\x1b[38;5;95;48;5;137m▄\x1b[38;5;101m▁ \x1b[48;5;95m▌\x1b[38;5;238m▋\x1b[38;5;240;48;5;101m▃ \x1b[38;5;95;48;5;240m▎\x1b[38;5;236;48;5;239m▝\x1b[38;5;95;48;5;235m▆\x1b[38;5;240m▆\x1b[38;5;237;48;5;233m▆\x1b[48;5;234m▃\x1b[38;5;235;48;5;233m▎\x1b[38;5;233;48;5;232m▖\x1b[48;5;16m \x1b[0m',
|
|
29
|
+
'\x1b[38;5;234;48;5;95m▌ \x1b[38;5;95;48;5;101m▃\x1b[38;5;239;48;5;95m▗\x1b[38;5;238;48;5;234m▋\x1b[38;5;236;48;5;101m▎\x1b[38;5;101;48;5;95m▋\x1b[38;5;239m▂▎ \x1b[48;5;240m▃\x1b[38;5;8;48;5;236m▍\x1b[38;5;235;48;5;233m▋\x1b[38;5;232m▅\x1b[38;5;233;48;5;232m▖\x1b[0m',
|
|
30
|
+
];
|
|
31
|
+
// ─── FRANKLIN text banner (gold → emerald gradient) ────────────────────────
|
|
32
|
+
//
|
|
33
|
+
// Kept from v3.1.0. The text is laid out as 6 block-letter rows. Each row
|
|
34
|
+
// is tinted with a color interpolated between GOLD_START and EMERALD_END,
|
|
35
|
+
// giving the smooth vertical gradient that's been Franklin's banner since
|
|
36
|
+
// v3.1.0.
|
|
3
37
|
const FRANKLIN_ART = [
|
|
4
38
|
' ███████╗██████╗ █████╗ ███╗ ██╗██╗ ██╗██╗ ██╗███╗ ██╗',
|
|
5
39
|
' ██╔════╝██╔══██╗██╔══██╗████╗ ██║██║ ██╔╝██║ ██║████╗ ██║',
|
|
@@ -8,10 +42,6 @@ const FRANKLIN_ART = [
|
|
|
8
42
|
' ██║ ██║ ██║██║ ██║██║ ╚████║██║ ██╗███████╗██║██║ ╚████║',
|
|
9
43
|
' ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝╚══════╝╚═╝╚═╝ ╚═══╝',
|
|
10
44
|
];
|
|
11
|
-
// Franklin brand gradient: gold → emerald
|
|
12
|
-
// Gold (#FFD700) is the $100 bill / Benjamins color — marketing product energy
|
|
13
|
-
// Emerald (#10B981) is the trading / money-moving color
|
|
14
|
-
// The gradient between them tells the Franklin story in one glance
|
|
15
45
|
const GOLD_START = '#FFD700';
|
|
16
46
|
const EMERALD_END = '#10B981';
|
|
17
47
|
function hexToRgb(hex) {
|
|
@@ -31,11 +61,97 @@ function interpolateHex(start, end, t) {
|
|
|
31
61
|
const [r2, g2, b2] = hexToRgb(end);
|
|
32
62
|
return rgbToHex(r1 + (r2 - r1) * t, g1 + (g2 - g1) * t, b1 + (b2 - b1) * t);
|
|
33
63
|
}
|
|
64
|
+
// ─── Banner layout ─────────────────────────────────────────────────────────
|
|
65
|
+
// Minimum terminal width to show the side-by-side portrait + text layout.
|
|
66
|
+
// The portrait is ~17 chars, the FRANKLIN text is ~65 chars, plus a 4-char
|
|
67
|
+
// gap = 86 chars. We add a small margin so ~90 cols is the threshold.
|
|
68
|
+
const MIN_WIDTH_FOR_PORTRAIT = 90;
|
|
69
|
+
/**
|
|
70
|
+
* Pad a line to an exact visual width, ignoring ANSI escape codes when
|
|
71
|
+
* measuring. Used to align the portrait's right edge before the text block.
|
|
72
|
+
*/
|
|
73
|
+
function padVisible(s, targetWidth) {
|
|
74
|
+
// Strip ANSI color codes to measure visible length
|
|
75
|
+
// eslint-disable-next-line no-control-regex
|
|
76
|
+
const visible = s.replace(/\x1b\[[0-9;]*m/g, '');
|
|
77
|
+
// Unicode block characters are width 1 (they're half-blocks, not double-width)
|
|
78
|
+
const current = [...visible].length;
|
|
79
|
+
if (current >= targetWidth)
|
|
80
|
+
return s;
|
|
81
|
+
// Append a reset + padding so background colors don't bleed into the gap
|
|
82
|
+
return s + '\x1b[0m' + ' '.repeat(targetWidth - current);
|
|
83
|
+
}
|
|
34
84
|
export function printBanner(version) {
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
85
|
+
const termWidth = process.stdout.columns ?? 80;
|
|
86
|
+
const useSideBySide = termWidth >= MIN_WIDTH_FOR_PORTRAIT;
|
|
87
|
+
if (useSideBySide) {
|
|
88
|
+
printSideBySide(version);
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
printTextOnly(version);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Full layout: Ben Franklin portrait on the left, FRANKLIN text block on the
|
|
96
|
+
* right. Portrait is 10 rows, text is 6 rows — portrait extends 4 rows below
|
|
97
|
+
* the text, so the 2-row tagline sits under the text and the last 2 rows
|
|
98
|
+
* below the portrait are just the bottom of the portrait.
|
|
99
|
+
*
|
|
100
|
+
* [portrait row 1] [empty]
|
|
101
|
+
* [portrait row 2] [empty]
|
|
102
|
+
* [portrait row 3] ███████╗██████╗ █████╗ ...
|
|
103
|
+
* [portrait row 4] ██╔════╝██╔══██╗██╔══██╗...
|
|
104
|
+
* [portrait row 5] █████╗ ██████╔╝███████║...
|
|
105
|
+
* [portrait row 6] ██╔══╝ ██╔══██╗██╔══██║...
|
|
106
|
+
* [portrait row 7] ██║ ██║ ██║██║ ██║...
|
|
107
|
+
* [portrait row 8] ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝...
|
|
108
|
+
* [portrait row 9] Franklin · The AI agent with a wallet · vX
|
|
109
|
+
* [portrait row 10] (empty)
|
|
110
|
+
*
|
|
111
|
+
* The text is vertically centered within the portrait — its top edge sits
|
|
112
|
+
* at portrait row 3 so there's a 2-row header padding above.
|
|
113
|
+
*/
|
|
114
|
+
function printSideBySide(version) {
|
|
115
|
+
const TEXT_TOP_OFFSET = 2; // rows of portrait above the text
|
|
116
|
+
const PORTRAIT_WIDTH = 18; // columns (char width) of the portrait + 1 pad
|
|
117
|
+
const GAP = ' '; // gap between portrait and text
|
|
118
|
+
const portraitRows = BEN_PORTRAIT_ROWS;
|
|
119
|
+
const textRows = FRANKLIN_ART.length;
|
|
120
|
+
const totalRows = Math.max(portraitRows.length, TEXT_TOP_OFFSET + textRows + 2);
|
|
121
|
+
for (let i = 0; i < totalRows; i++) {
|
|
122
|
+
const portraitLine = i < portraitRows.length
|
|
123
|
+
? padVisible(portraitRows[i], PORTRAIT_WIDTH)
|
|
124
|
+
: ' '.repeat(PORTRAIT_WIDTH);
|
|
125
|
+
// Text column content
|
|
126
|
+
let textCol = '';
|
|
127
|
+
const textIdx = i - TEXT_TOP_OFFSET;
|
|
128
|
+
if (textIdx >= 0 && textIdx < textRows) {
|
|
129
|
+
// FRANKLIN block letters with gradient colour
|
|
130
|
+
const t = textRows === 1 ? 0 : textIdx / (textRows - 1);
|
|
131
|
+
const color = interpolateHex(GOLD_START, EMERALD_END, t);
|
|
132
|
+
textCol = chalk.hex(color)(FRANKLIN_ART[textIdx]);
|
|
133
|
+
}
|
|
134
|
+
else if (textIdx === textRows) {
|
|
135
|
+
// Tagline row sits right under the FRANKLIN block
|
|
136
|
+
textCol =
|
|
137
|
+
chalk.bold.hex(GOLD_START)(' Franklin') +
|
|
138
|
+
chalk.dim(' · The AI agent with a wallet · v' + version);
|
|
139
|
+
}
|
|
140
|
+
// Write with a reset at the very start to prevent stray bg from the
|
|
141
|
+
// previous line bleeding into the current row's portrait column.
|
|
142
|
+
process.stdout.write('\x1b[0m' + portraitLine + GAP + textCol + '\x1b[0m\n');
|
|
143
|
+
}
|
|
144
|
+
// Trailing blank line for breathing room
|
|
145
|
+
process.stdout.write('\n');
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Compact layout for narrow terminals: just the FRANKLIN text block with
|
|
149
|
+
* its gradient, no portrait. Matches the v3.1.0 banner exactly.
|
|
150
|
+
*/
|
|
151
|
+
function printTextOnly(version) {
|
|
152
|
+
const textRows = FRANKLIN_ART.length;
|
|
153
|
+
for (let i = 0; i < textRows; i++) {
|
|
154
|
+
const t = textRows === 1 ? 0 : i / (textRows - 1);
|
|
39
155
|
const color = interpolateHex(GOLD_START, EMERALD_END, t);
|
|
40
156
|
console.log(chalk.hex(color)(FRANKLIN_ART[i]));
|
|
41
157
|
}
|
package/package.json
CHANGED