omegon 0.7.1 → 0.7.2

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.
@@ -14,7 +14,11 @@ import type { Component, TUI } from "@styrene-lab/pi-tui";
14
14
  import { truncateToWidth } from "@styrene-lab/pi-tui";
15
15
  import {
16
16
  LOGO_LINES,
17
+ WORDMARK_LINES,
17
18
  LINE_WIDTH,
19
+ COMPACT_LOGO_LINES,
20
+ COMPACT_LINE_WIDTH,
21
+ COMPACT_MARK_ROWS,
18
22
  FRAME_INTERVAL_MS,
19
23
  TOTAL_FRAMES,
20
24
  HOLD_FRAMES,
@@ -95,8 +99,9 @@ const PENDING_GLYPH = "· ";
95
99
  // ---------------------------------------------------------------------------
96
100
  class SplashHeader implements Component {
97
101
  private tui: TUI;
102
+ private lines: string[];
98
103
  private frame = 0;
99
- private frameMap = assignUnlockFrames(LOGO_LINES, TOTAL_FRAMES, Date.now() & 0xffff);
104
+ private frameMap: ReturnType<typeof assignUnlockFrames>;
100
105
  private noiseSeed = (Date.now() * 7) & 0x7fffffff;
101
106
  private timer: ReturnType<typeof setInterval> | null = null;
102
107
  private scanFrame = 0;
@@ -107,9 +112,16 @@ class SplashHeader implements Component {
107
112
  private cachedLines: string[] | undefined;
108
113
  private cachedWidth: number | undefined;
109
114
 
110
- constructor(tui: TUI, onTransition: () => void) {
115
+ private markRows: number;
116
+ private logoWidth: number;
117
+
118
+ constructor(tui: TUI, onTransition: () => void, lines: string[], markRows: number, logoWidth: number) {
111
119
  this.tui = tui;
112
120
  this.onTransition = onTransition;
121
+ this.lines = lines;
122
+ this.markRows = markRows;
123
+ this.logoWidth = logoWidth;
124
+ this.frameMap = assignUnlockFrames(lines, TOTAL_FRAMES, Date.now() & 0xffff);
113
125
  }
114
126
 
115
127
  start(): void {
@@ -147,14 +159,14 @@ class SplashHeader implements Component {
147
159
  const lines: string[] = [];
148
160
 
149
161
  // Centre the logo horizontally
150
- const logoW = LINE_WIDTH;
162
+ const logoW = this.logoWidth;
151
163
  const pad = Math.max(0, Math.floor((width - logoW) / 2));
152
164
  const padStr = " ".repeat(pad);
153
165
 
154
166
  // Render logo frame
155
167
  const logoFrame = this.transitioned
156
- ? renderFrame(TOTAL_FRAMES + 1, LOGO_LINES, this.frameMap, this.noiseSeed)
157
- : renderFrame(Math.min(this.frame, TOTAL_FRAMES), LOGO_LINES, this.frameMap, this.noiseSeed);
168
+ ? renderFrame(TOTAL_FRAMES + 1, this.lines, this.frameMap, this.noiseSeed, this.markRows)
169
+ : renderFrame(Math.min(this.frame, TOTAL_FRAMES), this.lines, this.frameMap, this.noiseSeed, this.markRows);
158
170
 
159
171
  lines.push(""); // top spacer
160
172
  for (const row of logoFrame) {
@@ -295,15 +307,42 @@ export default function splashExtension(pi: ExtensionAPI): void {
295
307
  // after a version update), it renders as a one-liner below the splash.
296
308
  // This is acceptable — it only appears once per update.
297
309
  const termWidth = process.stdout.columns ?? 80;
298
- if (termWidth < LINE_WIDTH + 4) {
299
- // Too narrow for the ASCII art — use minimal header immediately
310
+ const termRows = process.stdout.rows ?? 24;
311
+
312
+ // Four tiers based on terminal size:
313
+ // Full (sigil + wordmark): needs ~46 rows and LINE_WIDTH+4 cols (~84 cols)
314
+ // Compact (smaller sigil + wordmark): needs ~34 rows and COMPACT_LINE_WIDTH+4 cols (~58 cols)
315
+ // Wordmark only: needs ~14 rows and LINE_WIDTH+4 cols
316
+ // Minimal (no animation): everything else
317
+ const canFitFull = termWidth >= LINE_WIDTH + 4 && termRows >= LOGO_LINES.length + 6;
318
+ const canFitCompact = termWidth >= COMPACT_LINE_WIDTH + 4 && termRows >= COMPACT_LOGO_LINES.length + 6;
319
+ const canFitWordmark = termWidth >= LINE_WIDTH + 4 && termRows >= WORDMARK_LINES.length + 6;
320
+
321
+ if (!canFitCompact && !canFitWordmark) {
322
+ // Too small for any animation — minimal branded header
300
323
  ctx.ui.setHeader(() => new BrandedHeader(version));
301
324
  } else {
325
+ let artLines: string[];
326
+ let markRows: number;
327
+ let logoWidth: number;
328
+ if (canFitFull) {
329
+ artLines = LOGO_LINES;
330
+ markRows = 31; // MARK_ROWS
331
+ logoWidth = LINE_WIDTH;
332
+ } else if (canFitCompact) {
333
+ artLines = COMPACT_LOGO_LINES;
334
+ markRows = COMPACT_MARK_ROWS;
335
+ logoWidth = COMPACT_LINE_WIDTH;
336
+ } else {
337
+ artLines = WORDMARK_LINES;
338
+ markRows = 0; // all wordmark
339
+ logoWidth = LINE_WIDTH;
340
+ }
302
341
  ctx.ui.setHeader((tui, _theme) => {
303
342
  const splash = new SplashHeader(tui, () => {
304
343
  // Transition to minimal branded header
305
344
  ctx.ui.setHeader((_, _t) => new BrandedHeader(version));
306
- });
345
+ }, artLines, markRows, logoWidth);
307
346
  splash.start();
308
347
  return splash;
309
348
  });
@@ -139,6 +139,7 @@ export function renderFrame(
139
139
  lines: string[],
140
140
  frameMap: FrameMap,
141
141
  noiseSeed: number,
142
+ markRows: number = MARK_ROWS,
142
143
  ): string[] {
143
144
  const rng = new SimpleRNG(noiseSeed + frame * 997);
144
145
  const output: string[] = [];
@@ -162,7 +163,7 @@ export function renderFrame(
162
163
  buf += " ";
163
164
  } else if (frame >= unlock) {
164
165
  // Resolved — final glyph
165
- const color = y >= MARK_ROWS + 2 ? `${BOLD}${BRIGHT}` : PRIMARY;
166
+ const color = y >= markRows + 1 ? `${BOLD}${BRIGHT}` : PRIMARY;
166
167
  if (color !== lastColor) { buf += color; lastColor = color; }
167
168
  buf += ch;
168
169
  } else {
@@ -190,5 +191,56 @@ export function renderFrame(
190
191
  // ---------------------------------------------------------------------------
191
192
  // Pre-computed data for the default logo
192
193
  // ---------------------------------------------------------------------------
193
- export { LOGO_LINES, LINE_WIDTH, MARK_ROWS };
194
+ /** Wordmark-only lines (spacer + 7 wordmark rows) for compact terminals. */
195
+ const WORDMARK_LINES: string[] = LOGO_LINES.slice(MARK_ROWS + 1); // skip sigil + first spacer, keep second spacer + wordmark
196
+ // Pad to same width
197
+ for (let i = 0; i < WORDMARK_LINES.length; i++) {
198
+ WORDMARK_LINES[i] = WORDMARK_LINES[i].padEnd(LINE_WIDTH);
199
+ }
200
+
201
+ // ---------------------------------------------------------------------------
202
+ // Compact logo — sigil + wordmark for mid-size terminals (~56 cols)
203
+ // ---------------------------------------------------------------------------
204
+ const COMPACT_MARK_ROWS = 23;
205
+
206
+ const COMPACT_LOGO_LINES: string[] = [
207
+ " * ``` #` ",
208
+ " ` ```##` ``````##` .#` ",
209
+ "````##`######### `############`##` ",
210
+ "*`*############## `##################` ",
211
+ "##:````*` `####` `#########` *#######:## ",
212
+ "` ##### ``####### ######`#` ",
213
+ " `##### #######. #########` ",
214
+ " ####`` ``*@@@@@@@@@@`* `## #### ",
215
+ " ##### `@@@@@@@@@@@@@@@@@@@ `#@ :` ",
216
+ " #####`@@@@@@@@@@@@@@@@@@@@@@@@` `#` ",
217
+ " ##*@@@@@@@@@@@@@@@@@@@@@@@@@@@` ",
218
+ " :@@@@@@@@@@@``##```@@@@@@@@@@@`` ",
219
+ " @@@@@@@@*#:` `#######`@@@@@@@@` ` ` ",
220
+ " @@@@@@@#####` `########`@@@@@@@`####`#` ",
221
+ " @@@@@@ ###### `#`#####`@@@@@@########` ",
222
+ " @@@@@ ###### `::``#*@@@@@`##` #### ",
223
+ " `@@@@@####### `@@@@@`###` `*## ",
224
+ " ``#` .@@@@`##### `@@@@@` ``###` `**",
225
+ " ``:######```@@@@@#` `.@@@@. `#.##` ",
226
+ " ######`####`* `@@@@@@ ``@@@@@` ``#####` ",
227
+ " #* .@@@@@@@@@@@@@@ :@@@@@@@@@@@@@@## ",
228
+ " ` .@@@@@@@@@@@@@@ :@@@@@@@@@@@@@@` ",
229
+ " .@ ` ` ",
230
+ // spacer
231
+ " ",
232
+ // wordmark (4 rows)
233
+ " @@@@@@@ @@@` `@@@ @@@@@@``@@@@@@ `@@@@@@@`@@@` @@ ",
234
+ " @@ @@ @@@@`@@@@ @@```` `@@` `@@ @@ @@@@ @@ ",
235
+ " @@ @@ @@ @*@`@@ @@@@` `@@`@@@ `@@ @@ @@ *@@@ ",
236
+ " @@@@@@@ @@ `@``@@ @@@@@@``@@@@@@ `@@@@@@@`@@ `@@ ",
237
+ ];
238
+
239
+ const COMPACT_LINE_WIDTH = Math.max(...COMPACT_LOGO_LINES.map(l => l.length));
240
+ for (let i = 0; i < COMPACT_LOGO_LINES.length; i++) {
241
+ COMPACT_LOGO_LINES[i] = COMPACT_LOGO_LINES[i].padEnd(COMPACT_LINE_WIDTH);
242
+ }
243
+
244
+ export { LOGO_LINES, WORDMARK_LINES, LINE_WIDTH, MARK_ROWS };
245
+ export { COMPACT_LOGO_LINES, COMPACT_LINE_WIDTH, COMPACT_MARK_ROWS };
194
246
  export { PRIMARY, PRIMARY_DIM, DIM, BRIGHT, SUCCESS, ERROR_CLR, RESET, BOLD };
@@ -471,9 +471,9 @@ function restartOmegon(): never {
471
471
  "done",
472
472
  // Extra grace period for fd/terminal release
473
473
  "sleep 0.2",
474
- // Pop kitty keyboard protocol and bracketed paste before resetting
475
- // stty sane only resets line discipline, not terminal protocol state
476
- "printf '\\033[<u\\033[>4;0m\\033[?2004l' 2>/dev/null",
474
+ // Full terminal protocol reset stty sane only resets line discipline,
475
+ // not terminal protocol state (kitty keyboard, bracketed paste, cursor, SGR)
476
+ "printf '\\033[<u\\033[>4;0m\\033[?2004l\\033[?25h\\033[0m\\033[r' 2>/dev/null",
477
477
  "stty sane 2>/dev/null",
478
478
  // Clean up this script
479
479
  `rm -f "${script}"`,
@@ -484,15 +484,20 @@ function restartOmegon(): never {
484
484
  // Reset terminal to cooked mode BEFORE exiting so the restart script
485
485
  // (and the user) aren't stuck with raw-mode terminal if something goes wrong.
486
486
  try {
487
- // Pop kitty keyboard protocol and disable bracketed paste BEFORE
488
- // releasing raw mode the terminal needs these escape sequences to
489
- // stop encoding keystrokes in CSI-u format. Without this, the restart
490
- // script (and any keystrokes during the wait) dump raw kitty sequences.
487
+ // Full terminal protocol teardown: pop kitty keyboard protocol,
488
+ // disable modifyOtherKeys, disable bracketed paste, show cursor,
489
+ // reset SGR attributes, and clear any pending scroll region.
491
490
  process.stdout.write(
492
491
  "\x1b[<u" + // Pop kitty keyboard protocol flags
493
492
  "\x1b[>4;0m" + // Disable modifyOtherKeys
494
- "\x1b[?2004l" // Disable bracketed paste
493
+ "\x1b[?2004l" + // Disable bracketed paste
494
+ "\x1b[?25h" + // Show cursor
495
+ "\x1b[0m" + // Reset all SGR attributes
496
+ "\x1b[r" // Reset scroll region to full screen
495
497
  );
498
+ // Pause stdin to prevent buffered input from being re-interpreted
499
+ // after raw mode is disabled (prevents Ctrl+D from closing parent shell).
500
+ process.stdin.pause();
496
501
  if (process.stdin.isTTY && typeof process.stdin.setRawMode === "function") {
497
502
  process.stdin.setRawMode(false);
498
503
  }
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omegon",
3
- "version": "0.7.1",
3
+ "version": "0.7.2",
4
4
  "description": "Omegon — an opinionated distribution of pi (by Mario Zechner) with extensions for lifecycle management, memory, orchestration, and visualization",
5
5
  "bin": {
6
6
  "omegon": "bin/omegon.mjs",