omegon 0.7.2 → 0.7.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.
@@ -279,6 +279,112 @@ class BrandedHeader implements Component {
279
279
  }
280
280
  }
281
281
 
282
+ // ---------------------------------------------------------------------------
283
+ // Extension entry point
284
+ // ---------------------------------------------------------------------------
285
+ // ---------------------------------------------------------------------------
286
+ // Fullscreen splash replay component (easter egg)
287
+ // ---------------------------------------------------------------------------
288
+ class SplashReplay implements Component {
289
+ private tui: TUI;
290
+ private lines: string[];
291
+ private frame = 0;
292
+ private frameMap: ReturnType<typeof assignUnlockFrames>;
293
+ private noiseSeed = (Date.now() * 7) & 0x7fffffff;
294
+ private timer: ReturnType<typeof setInterval> | null = null;
295
+ private holdCount = 0;
296
+ private done: () => void;
297
+ private markRows: number;
298
+ private logoWidth: number;
299
+ private cachedLines: string[] | undefined;
300
+ private cachedWidth: number | undefined;
301
+
302
+ constructor(tui: TUI, done: () => void, lines: string[], markRows: number, logoWidth: number) {
303
+ this.tui = tui;
304
+ this.done = done;
305
+ this.lines = lines;
306
+ this.markRows = markRows;
307
+ this.logoWidth = logoWidth;
308
+ this.frameMap = assignUnlockFrames(lines, TOTAL_FRAMES, Date.now() & 0xffff);
309
+ }
310
+
311
+ start(): void {
312
+ this.timer = setInterval(() => this.tick(), FRAME_INTERVAL_MS);
313
+ }
314
+
315
+ private tick(): void {
316
+ this.frame++;
317
+ this.cachedLines = undefined;
318
+
319
+ if (this.frame >= TOTAL_FRAMES) {
320
+ this.holdCount++;
321
+ if (this.holdCount >= HOLD_FRAMES + 12) {
322
+ this.dispose();
323
+ this.done();
324
+ return;
325
+ }
326
+ }
327
+
328
+ this.tui.requestRender();
329
+ }
330
+
331
+ render(width: number): string[] {
332
+ if (this.cachedLines && this.cachedWidth === width) return this.cachedLines;
333
+
334
+ const height = process.stdout.rows ?? 24;
335
+ const lines: string[] = [];
336
+
337
+ const logoFrame = renderFrame(
338
+ Math.min(this.frame, TOTAL_FRAMES),
339
+ this.lines,
340
+ this.frameMap,
341
+ this.noiseSeed,
342
+ this.markRows,
343
+ );
344
+
345
+ // Vertically centre
346
+ const topPad = Math.max(0, Math.floor((height - logoFrame.length) / 2));
347
+ for (let i = 0; i < topPad; i++) lines.push("");
348
+
349
+ // Horizontally centre
350
+ const pad = Math.max(0, Math.floor((width - this.logoWidth) / 2));
351
+ const padStr = " ".repeat(pad);
352
+ for (const row of logoFrame) {
353
+ lines.push(truncateToWidth(padStr + row, width));
354
+ }
355
+
356
+ // Fill remaining
357
+ const remaining = height - lines.length;
358
+ for (let i = 0; i < remaining; i++) lines.push("");
359
+
360
+ this.cachedLines = lines;
361
+ this.cachedWidth = width;
362
+ return lines;
363
+ }
364
+
365
+ handleInput(input: string): boolean {
366
+ // Any key dismisses early
367
+ if (input) {
368
+ this.dispose();
369
+ this.done();
370
+ return true;
371
+ }
372
+ return false;
373
+ }
374
+
375
+ invalidate(): void {
376
+ this.cachedLines = undefined;
377
+ this.cachedWidth = undefined;
378
+ }
379
+
380
+ dispose(): void {
381
+ if (this.timer) {
382
+ clearInterval(this.timer);
383
+ this.timer = null;
384
+ }
385
+ }
386
+ }
387
+
282
388
  // ---------------------------------------------------------------------------
283
389
  // Extension entry point
284
390
  // ---------------------------------------------------------------------------
@@ -286,6 +392,42 @@ export default function splashExtension(pi: ExtensionAPI): void {
286
392
  // Initialise shared state immediately so other extensions can write to it
287
393
  getSharedState();
288
394
 
395
+ // Easter egg: /splash replays the animation fullscreen
396
+ pi.registerCommand("splash", {
397
+ description: "Replay the Omegon splash animation",
398
+ handler: async (_args, ctx) => {
399
+ if (!ctx.hasUI) return;
400
+ const termWidth = process.stdout.columns ?? 80;
401
+ const termRows = process.stdout.rows ?? 24;
402
+
403
+ // Pick the best art that fits
404
+ let artLines: string[];
405
+ let markRows: number;
406
+ let logoWidth: number;
407
+ const canFitFull = termWidth >= LINE_WIDTH + 4 && termRows >= LOGO_LINES.length + 4;
408
+ const canFitCompact = termWidth >= COMPACT_LINE_WIDTH + 4 && termRows >= COMPACT_LOGO_LINES.length + 4;
409
+ if (canFitFull) {
410
+ artLines = LOGO_LINES;
411
+ markRows = 31;
412
+ logoWidth = LINE_WIDTH;
413
+ } else if (canFitCompact) {
414
+ artLines = COMPACT_LOGO_LINES;
415
+ markRows = COMPACT_MARK_ROWS;
416
+ logoWidth = COMPACT_LINE_WIDTH;
417
+ } else {
418
+ artLines = WORDMARK_LINES;
419
+ markRows = 0;
420
+ logoWidth = LINE_WIDTH;
421
+ }
422
+
423
+ await ctx.ui.custom<void>((tui, _theme, _kb, done) => {
424
+ const replay = new SplashReplay(tui, () => done(undefined), artLines, markRows, logoWidth);
425
+ replay.start();
426
+ return replay;
427
+ });
428
+ },
429
+ });
430
+
289
431
  let version = "0.0.0";
290
432
 
291
433
  pi.on("session_start", async (_event, ctx) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omegon",
3
- "version": "0.7.2",
3
+ "version": "0.7.3",
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",