slopie 1.8.5 → 1.8.6

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.
@@ -1,12 +1,12 @@
1
1
  // Instant first frame for `slopie audit`.
2
2
  //
3
3
  // Loading the CLI bundle takes ~300 ms. For the interactive audit we paint the
4
- // loading story's first frame (Slopie, the brand, the privacy promise) before
5
- // the bundle loads, so the screen responds at once; the real loading story
6
- // takes over the alternate screen in exactly the same place and calls
7
- // `globalThis.__slopieSplash.handOff()`.
4
+ // loading screen's first frame (the header, the section label, the first
5
+ // caption and the privacy promise) before the bundle loads, so the screen
6
+ // responds at once; the real loading screen takes over the alternate screen
7
+ // in exactly the same place and calls `globalThis.__slopieSplash.handOff()`.
8
8
  //
9
- // The pixel art and geometry mirror packages/cli/src/audit-ui/{mascot,loading}.ts;
9
+ // The palette, text and geometry mirror packages/cli/src/audit-ui/{gfx/color,loading}.ts;
10
10
  // tests/audit-ui/splash.test.ts keeps them identical.
11
11
  //
12
12
  // If anything else ends up writing (an error, --help), the alternate screen is
@@ -15,68 +15,46 @@
15
15
 
16
16
  const PLAIN_FLAGS = new Set(['--json', '--plain', '--export', '--legacy', '--sweep', '--verify', '--help', '-h', '--version', '-V']);
17
17
 
18
- /** Slopie, idle. Rows of palette letters; '.' is transparent. First row is pixel row -2 (the tip). */
19
- export const SPLASH_ART = {
20
- big: {
21
- top: -2,
22
- palette: ['#ff7a1a', '#e16011', '#c94a0a', '#ffb070', '#fff4e6', '#ffffff', '#1c0f08', '#8a2e05', '#ff7027'],
23
- rows: ['........ab....', '.......aa.....', '.....aaaa.....', '...aaddaaaa...', '..adedaaaaaa..', '.aadaaaaaaaab.', '.adaaaaaaaabb.', 'aaaaaaaaaaaabb', 'aaafgaaaafgabb', 'aaaggaaaaggabb', 'aaaaaaaaaaaabb', 'aiiaahaahaaiib', 'caaaaahhaaaabc', 'ccaaaaaaaaaacc', '.cccccccccccc.', '..cccccccccc..', '...c.......c..', '...........b..'],
24
- },
25
- compact: {
26
- top: 0,
27
- palette: ['#ff7a1a', '#e16011', '#c94a0a', '#ffb070', '#fff4e6', '#1c0f08', '#8a2e05'],
28
- rows: ['..aaaa..', '.adaaaa.', 'aeaaaaab', 'aafaafab', 'aafaafab', 'aagaagab', 'caaggaac', '.cccccc.'],
29
- },
18
+ /** The audit palette (mirrors C in audit-ui/gfx/color.ts), with its tuned 256-colour indices. */
19
+ export const SPLASH_PALETTE = {
20
+ bg: ['#08090a', 232], text: ['#f7f8f8', 255], text2: ['#8a8f98', 245], text3: ['#5d6168', 241], rule: ['#1f2125', 234], accent: ['#7c7cff', 105],
30
21
  };
22
+ export const PRIVACY_LINE = 'all local · nothing leaves this machine';
23
+ export const FIRST_CAPTION = 'finding your agents';
31
24
 
32
25
  /** Mirrors loadingGeometry() in audit-ui/loading.ts. */
33
26
  export function splashGeometry(w, h) {
34
27
  const L = w >= 100 && h >= 30;
35
28
  const S = w < 76 || h < 23;
36
- const mx = L ? 4 : S ? 2 : 3;
37
- const scale = L ? 2 : 1;
38
- const mascotW = S ? 10 : 18 * scale;
39
- const mascotH = S ? 4 : 8 * scale;
40
- const panelX = S ? mx : Math.max(mx + mascotW + 6, Math.floor(w * (L ? 0.46 : 0.42)));
41
- const sx = S ? Math.floor((w - 10) / 2) : Math.max(mx, Math.floor((panelX - mascotW) / 2) - 1);
42
- const sy = S ? 2 : Math.max(2, Math.floor((h - mascotH) / 2) - 2);
43
- const rowsY = S ? sy + mascotH + 2 : Math.max(3, Math.floor(h / 2) - (L ? 6 : 5));
44
- return { L, S, mx, scale, mascotW, mascotH, panelX, sx, sy, rowsY };
29
+ const mx = L ? 6 : 3;
30
+ const top = L ? 1 : 0;
31
+ const footY = L ? h - 2 : h - 1;
32
+ const rowStep = L ? 2 : 1;
33
+ const numH = L ? 5 : 3;
34
+ const blockH = 2 + 4 * rowStep + 1 + numH + 2 + 2 + 1;
35
+ const labelY = Math.max(top + 3, top + 2 + Math.floor((footY - top - 2 - blockH) / 2));
36
+ const rowsY = labelY + 2;
37
+ const numY = rowsY + 4 * rowStep + 1;
38
+ const barY = numY + numH + 2;
39
+ const captionY = barY + 2;
40
+ return { L, S, mx, top, footY, rowStep, numH, labelY, rowsY, numY, barY, captionY };
45
41
  }
46
42
 
47
- const INK = [13, 11, 10];
48
- const TUNED_256 = { '#ff7a1a': 208, '#ffb070': 215, '#fff4e6': 230, '#ffffff': 231, '#c94a0a': 166, '#8a2e05': 94, '#1c0f08': 232 };
49
- const hexRgb = hex => [1, 3, 5].map(i => parseInt(hex.slice(i, i + 2), 16));
50
- function to256(hex) {
51
- if (TUNED_256[hex]) return TUNED_256[hex];
52
- const cube = [0, 95, 135, 175, 215, 255];
53
- const near = v => cube.reduce((best, c, i) => (Math.abs(c - v) < Math.abs(cube[best] - v) ? i : best), 0);
54
- const [r, g, b] = hexRgb(hex);
55
- return 16 + 36 * near(r) + 6 * near(g) + near(b);
56
- }
57
-
58
- /** Cells for the art: Map "x,y" → [topHex|null, bottomHex|null], in cell coordinates. */
59
- export function splashCells(size, x, y, scale) {
60
- const art = SPLASH_ART[size];
61
- const offset = size === 'big' ? 2 : 0;
62
- const top = size === 'big' ? 2 : 0;
63
- const cells = new Map();
64
- art.rows.forEach((row, ry) => {
65
- for (let rx = 0; rx < row.length; rx++) {
66
- if (row[rx] === '.') continue;
67
- const color = art.palette['abcdefghij'.indexOf(row[rx])];
68
- const px = rx, py = ry + art.top;
69
- for (let sy = 0; sy < scale; sy++) for (let sx = 0; sx < scale; sx++) {
70
- const cx = x + (px + offset) * scale + sx;
71
- const pyy = y * 2 + (py + top) * scale + sy;
72
- const key = `${cx},${pyy >> 1}`;
73
- const cell = cells.get(key) ?? [null, null];
74
- cell[pyy & 1] = color;
75
- cells.set(key, cell);
76
- }
77
- }
78
- });
79
- return cells;
43
+ /** The splash as text runs: { x, y, text, tone, bold }. */
44
+ export function splashRuns(w, h, unicode = true) {
45
+ const g = splashGeometry(w, h);
46
+ const iw = w - g.mx * 2;
47
+ const runs = [
48
+ { x: g.mx, y: g.top, text: 'slopie', tone: 'text', bold: true },
49
+ { x: g.mx + 7, y: g.top, text: 'audit', tone: 'text3' },
50
+ { x: g.mx, y: g.top + 1, text: (unicode ? '─' : '-').repeat(iw), tone: 'rule' },
51
+ { x: g.mx, y: g.labelY, text: 'YOUR AGENTS', tone: 'text3' },
52
+ { x: g.mx, y: g.barY, text: (unicode ? '─' : '-').repeat(Math.min(64, iw)), tone: 'rule' },
53
+ { x: g.mx, y: g.captionY, text: FIRST_CAPTION, tone: 'text2' },
54
+ { x: g.mx, y: g.footY, text: unicode ? '●' : '*', tone: 'accent' },
55
+ { x: g.mx + 2, y: g.footY, text: PRIVACY_LINE, tone: 'text3' },
56
+ ];
57
+ return runs;
80
58
  }
81
59
 
82
60
  export function auditSplash(argv = process.argv.slice(2)) {
@@ -90,32 +68,14 @@ export function auditSplash(argv = process.argv.slice(2)) {
90
68
  const color = !('NO_COLOR' in process.env) && !argv.includes('--no-color') && process.env.TERM !== 'dumb';
91
69
  const truecolor = /^(truecolor|24bit)$/i.test(process.env.COLORTERM ?? '') || /^(iTerm\.app|WezTerm|ghostty|vscode)$/i.test(process.env.TERM_PROGRAM ?? '');
92
70
  const deep = color && (truecolor || /256/.test(process.env.TERM ?? '') || process.env.TERM_PROGRAM === 'Apple_Terminal');
93
- const fg = hex => (!color ? '' : truecolor ? `\x1b[38;2;${hexRgb(hex).join(';')}m` : `\x1b[38;5;${to256(hex)}m`);
94
- const bgOf = hex => (!deep ? '' : truecolor ? `\x1b[48;2;${hexRgb(hex).join(';')}m` : `\x1b[48;5;${to256(hex)}m`);
95
- const ink = !deep ? '' : truecolor ? `\x1b[48;2;${INK.join(';')}m` : '\x1b[48;5;233m';
71
+ const rgb = hex => [1, 3, 5].map(i => parseInt(hex.slice(i, i + 2), 16)).join(';');
72
+ const fg = tone => (!deep ? '' : truecolor ? `\x1b[38;2;${rgb(SPLASH_PALETTE[tone][0])}m` : `\x1b[38;5;${SPLASH_PALETTE[tone][1]}m`);
73
+ const ink = !deep ? '' : truecolor ? `\x1b[48;2;${rgb(SPLASH_PALETTE.bg[0])}m` : `\x1b[48;5;${SPLASH_PALETTE.bg[1]}m`;
96
74
  const reset = `\x1b[0m${ink}`;
75
+ const unicode = !argv.includes('--ascii');
97
76
 
98
- const g = splashGeometry(w, h);
99
77
  let out = `\x1b[?1049h\x1b[?25l${ink}\x1b[H\x1b[2J`;
100
- const at = (x, y, text) => { out += `\x1b[${y + 1};${x + 1}H${text}${reset}`; };
101
- at(g.mx, g.L ? 1 : 0, `\x1b[1m${fg('#ff7a1a')}slopie${reset} ${fg('#5c5c5c')}audit`);
102
- if (deep) {
103
- const cells = splashCells(g.S ? 'compact' : 'big', g.sx, g.sy, g.S ? 1 : g.scale);
104
- for (const [key, [top, bottom]] of cells) {
105
- const [x, y] = key.split(',').map(Number);
106
- if (top && bottom) at(x, y, top === bottom ? `${fg(top)}█` : `${fg(top)}${bgOf(bottom)}▀`);
107
- else if (top) at(x, y, `${fg(top)}▀`);
108
- else if (bottom) at(x, y, `${fg(bottom)}▄`);
109
- }
110
- } else {
111
- at(g.sx + 4, g.sy + Math.floor(g.mascotH / 2), `${fg('#ff7a1a')}(•ᴗ•)`);
112
- }
113
- const privacy = 'all local · nothing leaves this machine';
114
- const py = h - (g.L ? 2 : 1);
115
- const px = Math.round(w / 2 - (privacy.length + 2) / 2);
116
- at(px, py, `${fg('#ff7a1a')}●${reset} ${fg('#5c5c5c')}${privacy}`);
117
- const cy = g.rowsY + (g.L ? 13 : 9);
118
- at(g.panelX, cy, `${fg('#ffb070')}finding your agents`);
78
+ for (const run of splashRuns(w, h, unicode)) out += `\x1b[${run.y + 1};${run.x + 1}H${run.bold ? '\x1b[1m' : ''}${fg(run.tone)}${run.text}${reset}`;
119
79
  stdout.write(out + '\x1b[0m');
120
80
  try { stdin.setRawMode(true); } catch { /* not a raw-capable tty */ }
121
81