@astralyn/pi 1.0.0 → 1.0.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.
Files changed (164) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/bundle/chunks/{anthropic-messages-CG2BSDQS.js → anthropic-messages-YET4T7BO.js} +3 -3
  3. package/dist/bundle/chunks/azure-openai-responses-SBLL5YN3.js +2 -0
  4. package/dist/bundle/chunks/bedrock-converse-stream.js +2 -2
  5. package/dist/bundle/chunks/{chunk-SGLYRVQL.js → chunk-4TT2QGFL.js} +1 -1
  6. package/dist/bundle/chunks/{chunk-HESRDUKO.js → chunk-H2N2THAO.js} +72 -52
  7. package/dist/bundle/chunks/{chunk-PFZKRUMO.js → chunk-IZ3S3GDX.js} +1 -1
  8. package/dist/bundle/chunks/{chunk-6UFFVA7M.js → chunk-NY3CWK44.js} +1 -1
  9. package/dist/bundle/chunks/chunk-SVJ7OGEB.js +2 -0
  10. package/dist/bundle/chunks/chunk-XDYM2567.js +20 -0
  11. package/dist/bundle/chunks/{cloudflare-workers-ai-system-one-YB7XPPP4.js → cloudflare-workers-ai-system-one-QI46LUZ6.js} +1 -1
  12. package/dist/bundle/chunks/codemode-worker.js +24 -3
  13. package/dist/bundle/chunks/{google-generative-ai-V5ZM5MCL.js → google-generative-ai-YTZCSHQN.js} +1 -1
  14. package/dist/bundle/chunks/{google-vertex-22THOFP5.js → google-vertex-OTFZJJXQ.js} +1 -1
  15. package/dist/bundle/chunks/{mistral-conversations-WHSCJJWP.js → mistral-conversations-ELH4V4TY.js} +1 -1
  16. package/dist/bundle/chunks/node-USQQ7C6M.js +16 -0
  17. package/dist/bundle/chunks/openai-chatgpt.js +1 -1
  18. package/dist/bundle/chunks/{openai-codex-responses-N7GDPBPP.js → openai-codex-responses-OLVTU4UA.js} +1 -1
  19. package/dist/bundle/chunks/openai-completions-53M2KJ67.js +7 -0
  20. package/dist/bundle/chunks/openai-responses-4ABDEL4Z.js +3 -0
  21. package/dist/bundle/chunks/{virtual-modules-4RQ2L6W4.js → virtual-modules-YFKU7JZF.js} +1 -1
  22. package/dist/bundle/cli.js +1 -1
  23. package/dist/bundle/index.js +1 -1
  24. package/dist/bundle/rpc-entry.js +1 -1
  25. package/dist/cli/args.d.ts.map +1 -1
  26. package/dist/cli/args.js +4 -1
  27. package/dist/cli/args.js.map +1 -1
  28. package/dist/config.d.ts.map +1 -1
  29. package/dist/config.js +3 -0
  30. package/dist/config.js.map +1 -1
  31. package/dist/core/agent-session.d.ts.map +1 -1
  32. package/dist/core/agent-session.js +1 -1
  33. package/dist/core/agent-session.js.map +1 -1
  34. package/dist/core/export-html/tool-renderer.d.ts +3 -3
  35. package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
  36. package/dist/core/export-html/tool-renderer.js +3 -3
  37. package/dist/core/export-html/tool-renderer.js.map +1 -1
  38. package/dist/core/extensions/index.d.ts +1 -1
  39. package/dist/core/extensions/index.d.ts.map +1 -1
  40. package/dist/core/extensions/index.js.map +1 -1
  41. package/dist/core/extensions/loader.d.ts.map +1 -1
  42. package/dist/core/extensions/loader.js +5 -0
  43. package/dist/core/extensions/loader.js.map +1 -1
  44. package/dist/core/extensions/runner.d.ts +3 -1
  45. package/dist/core/extensions/runner.d.ts.map +1 -1
  46. package/dist/core/extensions/runner.js +6 -0
  47. package/dist/core/extensions/runner.js.map +1 -1
  48. package/dist/core/extensions/types.d.ts +9 -0
  49. package/dist/core/extensions/types.d.ts.map +1 -1
  50. package/dist/core/extensions/types.js.map +1 -1
  51. package/dist/core/mcp-servers.d.ts +6 -0
  52. package/dist/core/mcp-servers.d.ts.map +1 -1
  53. package/dist/core/mcp-servers.js +11 -0
  54. package/dist/core/mcp-servers.js.map +1 -1
  55. package/dist/core/model-config.d.ts +36 -0
  56. package/dist/core/model-config.d.ts.map +1 -1
  57. package/dist/core/model-config.js +14 -2
  58. package/dist/core/model-config.js.map +1 -1
  59. package/dist/core/model-resolver.js +1 -1
  60. package/dist/core/model-resolver.js.map +1 -1
  61. package/dist/core/provider-composer.d.ts +2 -1
  62. package/dist/core/provider-composer.d.ts.map +1 -1
  63. package/dist/core/provider-composer.js +13 -0
  64. package/dist/core/provider-composer.js.map +1 -1
  65. package/dist/core/tools/renderers/index.d.ts +2 -2
  66. package/dist/core/tools/renderers/index.d.ts.map +1 -1
  67. package/dist/core/tools/renderers/index.js.map +1 -1
  68. package/dist/extensions/mcp/cli.d.ts.map +1 -1
  69. package/dist/extensions/mcp/cli.js +3 -0
  70. package/dist/extensions/mcp/cli.js.map +1 -1
  71. package/dist/extensions/mcp/config.d.ts +15 -3
  72. package/dist/extensions/mcp/config.d.ts.map +1 -1
  73. package/dist/extensions/mcp/config.js +46 -10
  74. package/dist/extensions/mcp/config.js.map +1 -1
  75. package/dist/extensions/mcp/index.d.ts +4 -1
  76. package/dist/extensions/mcp/index.d.ts.map +1 -1
  77. package/dist/extensions/mcp/index.js +80 -45
  78. package/dist/extensions/mcp/index.js.map +1 -1
  79. package/dist/extensions/mcp/oauth.d.ts +4 -1
  80. package/dist/extensions/mcp/oauth.d.ts.map +1 -1
  81. package/dist/extensions/mcp/oauth.js +54 -10
  82. package/dist/extensions/mcp/oauth.js.map +1 -1
  83. package/dist/extensions/mcp/runtime.d.ts.map +1 -1
  84. package/dist/extensions/mcp/runtime.js +1 -0
  85. package/dist/extensions/mcp/runtime.js.map +1 -1
  86. package/dist/extensions/mcp/tools.d.ts +3 -1
  87. package/dist/extensions/mcp/tools.d.ts.map +1 -1
  88. package/dist/extensions/mcp/tools.js +19 -13
  89. package/dist/extensions/mcp/tools.js.map +1 -1
  90. package/dist/extensions/mcp/ui.d.ts.map +1 -1
  91. package/dist/extensions/mcp/ui.js +8 -4
  92. package/dist/extensions/mcp/ui.js.map +1 -1
  93. package/dist/index.d.ts +1 -1
  94. package/dist/index.d.ts.map +1 -1
  95. package/dist/index.js.map +1 -1
  96. package/dist/modes/interactive/components/armin.d.ts +3 -0
  97. package/dist/modes/interactive/components/armin.d.ts.map +1 -1
  98. package/dist/modes/interactive/components/armin.js +7 -5
  99. package/dist/modes/interactive/components/armin.js.map +1 -1
  100. package/dist/modes/interactive/components/auth-url.d.ts +14 -0
  101. package/dist/modes/interactive/components/auth-url.d.ts.map +1 -0
  102. package/dist/modes/interactive/components/auth-url.js +34 -0
  103. package/dist/modes/interactive/components/auth-url.js.map +1 -0
  104. package/dist/modes/interactive/components/easter-egg-3d.d.ts +104 -0
  105. package/dist/modes/interactive/components/easter-egg-3d.d.ts.map +1 -0
  106. package/dist/modes/interactive/components/{pi-logo-animation.js → easter-egg-3d.js} +121 -74
  107. package/dist/modes/interactive/components/easter-egg-3d.js.map +1 -0
  108. package/dist/modes/interactive/components/easter-egg-3d.lazy.d.ts +6 -0
  109. package/dist/modes/interactive/components/easter-egg-3d.lazy.d.ts.map +1 -0
  110. package/dist/modes/interactive/components/easter-egg-3d.lazy.js +24 -0
  111. package/dist/modes/interactive/components/easter-egg-3d.lazy.js.map +1 -0
  112. package/dist/modes/interactive/components/index.d.ts +0 -1
  113. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  114. package/dist/modes/interactive/components/index.js +0 -1
  115. package/dist/modes/interactive/components/index.js.map +1 -1
  116. package/dist/modes/interactive/components/login-dialog.d.ts +2 -0
  117. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  118. package/dist/modes/interactive/components/login-dialog.js +9 -5
  119. package/dist/modes/interactive/components/login-dialog.js.map +1 -1
  120. package/dist/modes/interactive/interactive-mode.d.ts +2 -2
  121. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  122. package/dist/modes/interactive/interactive-mode.js +15 -17
  123. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  124. package/dist/modes/interactive/tool-view/tool-execution.d.ts +4 -19
  125. package/dist/modes/interactive/tool-view/tool-execution.d.ts.map +1 -1
  126. package/dist/modes/interactive/tool-view/tool-execution.js +23 -67
  127. package/dist/modes/interactive/tool-view/tool-execution.js.map +1 -1
  128. package/dist/utils/image-convert.d.ts +12 -0
  129. package/dist/utils/image-convert.d.ts.map +1 -1
  130. package/dist/utils/image-convert.js +45 -6
  131. package/dist/utils/image-convert.js.map +1 -1
  132. package/docs/codemode.md +1 -1
  133. package/docs/extensions.md +4 -0
  134. package/docs/keybindings.md +1 -1
  135. package/docs/mcp.md +23 -1
  136. package/docs/models.md +30 -3
  137. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  138. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  139. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  140. package/examples/extensions/gondolin/package-lock.json +2 -2
  141. package/examples/extensions/gondolin/package.json +1 -1
  142. package/examples/extensions/sandbox/package-lock.json +2 -2
  143. package/examples/extensions/sandbox/package.json +1 -1
  144. package/examples/extensions/with-deps/package-lock.json +2 -2
  145. package/examples/extensions/with-deps/package.json +1 -1
  146. package/npm-shrinkwrap.json +58 -58
  147. package/package.json +11 -11
  148. package/dist/bundle/chunks/azure-openai-responses-PBA4QKCM.js +0 -2
  149. package/dist/bundle/chunks/chunk-PJVFYVHE.js +0 -42
  150. package/dist/bundle/chunks/chunk-Z2RKSI3O.js +0 -2
  151. package/dist/bundle/chunks/node-CXWYTK2I.js +0 -16
  152. package/dist/bundle/chunks/openai-completions-RGGQK5UW.js +0 -7
  153. package/dist/bundle/chunks/openai-responses-3ZLD5JYI.js +0 -3
  154. package/dist/modes/interactive/components/daxnuts.d.ts +0 -23
  155. package/dist/modes/interactive/components/daxnuts.d.ts.map +0 -1
  156. package/dist/modes/interactive/components/daxnuts.js +0 -138
  157. package/dist/modes/interactive/components/daxnuts.js.map +0 -1
  158. package/dist/modes/interactive/components/pi-logo-animation.d.ts +0 -68
  159. package/dist/modes/interactive/components/pi-logo-animation.d.ts.map +0 -1
  160. package/dist/modes/interactive/components/pi-logo-animation.js.map +0 -1
  161. package/dist/modes/interactive/components/pi-logo-animation.lazy.d.ts +0 -7
  162. package/dist/modes/interactive/components/pi-logo-animation.lazy.d.ts.map +0 -1
  163. package/dist/modes/interactive/components/pi-logo-animation.lazy.js +0 -12
  164. package/dist/modes/interactive/components/pi-logo-animation.lazy.js.map +0 -1
@@ -1,23 +1,56 @@
1
1
  import { backgroundAnsi, colorToRgb, foregroundAnsi, getKeybindings, indexedColor, rgbColor, visibleWidth, } from "@earendil-works/pi-tui";
2
2
  import { theme } from "../theme/theme.js";
3
+ import { ARMIN_HEIGHT, ARMIN_WIDTH, isArminPixel } from "./armin.js";
3
4
  import { formatKeyText } from "./keybinding-hints.js";
4
- const CORAL = [228, 138, 122];
5
- const BLUE = [79, 142, 179];
6
- const YELLOW = [234, 182, 93];
7
5
  const DEPTH = 0.7;
8
- const CAMERA_DISTANCE = 10;
9
6
  const FLY_START = 0.1;
10
7
  const FLY_DURATION = 1.3;
11
- // The logo's pixels on a 4x4 grid. Each pixel is a block that the puzzle slides around.
12
- const LOGO_PIXELS = ["ccc.", "b.c.", "bb.y", "b..y"];
13
- const BLOCKS = LOGO_PIXELS.flatMap((line, row) => [...line].flatMap((pixel, column) => {
14
- const color = pixel === "c" ? CORAL : pixel === "b" ? BLUE : pixel === "y" ? YELLOW : undefined;
15
- return color ? [{ home: [column, row, 0], color }] : [];
16
- }));
17
- // Farthest distance of any block corner from the origin, with blocks on the outer depth layers.
18
- const LOGO_RADIUS = Math.hypot(2, 2, 1 + DEPTH / 2);
19
- // The sliding puzzle: after the logo spun for a while, blocks slide into free neighboring cells, one step at
20
- // a time. Each cycle shuffles, flies every block back home, and holds the logo briefly.
8
+ /** Braille dots per pixel when a model without an origin appears, growing from a speck at the center. */
9
+ const START_SCALE = 0.1;
10
+ function createModel(columns, rows, pixel, options) {
11
+ const blocks = [];
12
+ for (let row = 0; row < rows; row++) {
13
+ for (let column = 0; column < columns; column++) {
14
+ const color = pixel(column, row);
15
+ if (color)
16
+ blocks.push({ home: [column, row, 0], color });
17
+ }
18
+ }
19
+ return {
20
+ columns,
21
+ rows,
22
+ blocks,
23
+ cameraDistance: options.cameraDistance,
24
+ radius: Math.hypot(columns / 2, rows / 2, 1 + DEPTH / 2),
25
+ widthShare: options.widthShare,
26
+ puzzleMoves: (random) => options.puzzleMoves(random, blocks.length),
27
+ origin: options.origin,
28
+ };
29
+ }
30
+ const CORAL = [228, 138, 122];
31
+ const BLUE = [79, 142, 179];
32
+ const YELLOW = [234, 182, 93];
33
+ const PI_LOGO_PIXELS = ["ccc.", "b.c.", "bb.y", "b..y"];
34
+ const PI_LOGO_COLORS = { c: CORAL, b: BLUE, y: YELLOW };
35
+ function piLogoModel(origin) {
36
+ return createModel(4, 4, (column, row) => PI_LOGO_COLORS[PI_LOGO_PIXELS[row][column]], {
37
+ cameraDistance: 10,
38
+ widthShare: 0.35,
39
+ puzzleMoves: (random) => (random < 0.4 ? 2 : 1),
40
+ origin,
41
+ });
42
+ }
43
+ function arminModel(color) {
44
+ return createModel(ARMIN_WIDTH, ARMIN_HEIGHT, (column, row) => (isArminPixel(column, row) ? color : undefined), {
45
+ // Far enough that the perspective stays mild for a figure about 36 blocks tall.
46
+ cameraDistance: 80,
47
+ widthShare: 0.45,
48
+ puzzleMoves: (random, blockCount) => Math.round(blockCount * (0.03 + random * 0.04)),
49
+ origin: undefined,
50
+ });
51
+ }
52
+ // The sliding puzzle: after the model spun for a while, blocks slide into free neighboring cells, a few at a time.
53
+ // Each cycle shuffles, flies every block back home, and holds the model briefly.
21
54
  const PUZZLE_START = FLY_START + FLY_DURATION + 3;
22
55
  const PUZZLE_STEP = 0.3;
23
56
  const PUZZLE_STEPS = 12;
@@ -37,18 +70,19 @@ const PUZZLE_MOVES = [
37
70
  [0, 0, -1],
38
71
  ];
39
72
  /** Block positions after each step of one shuffle cycle, starting at home. Deterministic per cycle. */
40
- function shuffleSteps(cycle) {
41
- let positions = BLOCKS.map((block) => block.home);
73
+ function shuffleSteps(model, cycle) {
74
+ const { columns, rows } = model;
75
+ let positions = model.blocks.map((block) => block.home);
42
76
  const steps = [positions];
43
77
  const lastMoved = new Set();
44
78
  let random = cycle * 7_919 + 1;
45
79
  const next = () => hash(random++);
46
- const key = ([x, y, z]) => (z + 1) * 16 + y * 4 + x;
80
+ const key = ([x, y, z]) => ((z + 1) * rows + y) * columns + x;
47
81
  for (let step = 0; step < PUZZLE_STEPS; step++) {
48
82
  const occupied = new Set(positions.map(key));
49
83
  const nextPositions = [...positions];
50
84
  const moved = new Set();
51
- const moveCount = next() < 0.4 ? 2 : 1;
85
+ const moveCount = model.puzzleMoves(next());
52
86
  for (let move = 0; move < moveCount; move++) {
53
87
  const candidates = [];
54
88
  for (let block = 0; block < positions.length; block++) {
@@ -57,7 +91,7 @@ function shuffleSteps(cycle) {
57
91
  const [x, y, z] = positions[block];
58
92
  for (const [dx, dy, dz] of PUZZLE_MOVES) {
59
93
  const target = [x + dx, y + dy, z + dz];
60
- if (target[0] < 0 || target[0] > 3 || target[1] < 0 || target[1] > 3)
94
+ if (target[0] < 0 || target[0] >= columns || target[1] < 0 || target[1] >= rows)
61
95
  continue;
62
96
  if (target[2] < -1 || target[2] > 1 || occupied.has(key(target)))
63
97
  continue;
@@ -335,12 +369,12 @@ function spinAngle(time) {
335
369
  return phase - SPIN_LINGER * Math.sin(phase);
336
370
  }
337
371
  /**
338
- * Renders the logo blocks into braille cells. Only faces that point at the camera and are not covered by a
372
+ * Renders the model's blocks into braille cells. Only faces that point at the camera and are not covered by a
339
373
  * touching block are drawn. Each face is rasterized over its projected bounds by intersecting each dot's ray
340
374
  * with the face's plane, with a depth buffer resolving overlaps. Buffers are reused between frames.
341
375
  */
342
- class LogoRaster {
343
- constructor() {
376
+ class BlockRaster {
377
+ constructor(cameraDistance) {
344
378
  /** Braille dot bits per cell. */
345
379
  this.bits = new Uint8Array(0);
346
380
  /** Lit dots per cell. */
@@ -356,7 +390,8 @@ class LogoRaster {
356
390
  /** Ray parameter of the nearest hit per dot; smaller is nearer. */
357
391
  this.depth = new Float32Array(0);
358
392
  /** Face index + 1 of the nearest hit per dot, 0 for none. */
359
- this.faceIds = new Uint8Array(0);
393
+ this.faceIds = new Uint16Array(0);
394
+ this.cameraDistance = cameraDistance;
360
395
  }
361
396
  render(width, height, pose, boxes, background, haloStrength) {
362
397
  const dotWidth = width * 2;
@@ -371,7 +406,7 @@ class LogoRaster {
371
406
  this.haloRgb = new Float32Array(width * height * 3);
372
407
  this.haloDirty = undefined;
373
408
  this.depth = new Float32Array(dotWidth * dotHeight).fill(Infinity);
374
- this.faceIds = new Uint8Array(dotWidth * dotHeight);
409
+ this.faceIds = new Uint16Array(dotWidth * dotHeight);
375
410
  this.dirty = undefined;
376
411
  }
377
412
  else if (this.dirty) {
@@ -393,8 +428,9 @@ class LogoRaster {
393
428
  }
394
429
  const m = rotation(pose.yaw, pose.pitch, pose.roll);
395
430
  const { centerX, centerY, scale } = pose;
396
- // The camera sits at (0, 0, CAMERA_DISTANCE) in camera space; object space is the transpose rotation.
397
- const origin = [m[6] * CAMERA_DISTANCE, m[7] * CAMERA_DISTANCE, m[8] * CAMERA_DISTANCE];
431
+ const cameraDistance = this.cameraDistance;
432
+ // The camera sits at (0, 0, cameraDistance) in camera space; object space is the transpose rotation.
433
+ const origin = [m[6] * cameraDistance, m[7] * cameraDistance, m[8] * cameraDistance];
398
434
  const light = isLight(background);
399
435
  const faces = this.visibleFaces(m, origin, pose, boxes, dotWidth, dotHeight, light);
400
436
  if (faces.length === 0)
@@ -428,9 +464,9 @@ class LogoRaster {
428
464
  const sx = (face.minX + 0.5 - centerX) / scale;
429
465
  for (let dotY = face.minY; dotY <= face.maxY; dotY++) {
430
466
  const sy = (dotY + 0.5 - centerY) / scale;
431
- let directionA = m[a] * sx + m[3 + a] * sy - m[6 + a] * CAMERA_DISTANCE;
432
- let directionU = m[u] * sx + m[3 + u] * sy - m[6 + u] * CAMERA_DISTANCE;
433
- let directionV = m[v] * sx + m[3 + v] * sy - m[6 + v] * CAMERA_DISTANCE;
467
+ let directionA = m[a] * sx + m[3 + a] * sy - m[6 + a] * cameraDistance;
468
+ let directionU = m[u] * sx + m[3 + u] * sy - m[6 + u] * cameraDistance;
469
+ let directionV = m[v] * sx + m[3 + v] * sy - m[6 + v] * cameraDistance;
434
470
  let index = dotY * dotWidth + face.minX;
435
471
  for (let dotX = face.minX; dotX <= face.maxX; dotX++, index++) {
436
472
  const t = (face.plane - originA) / directionA;
@@ -464,7 +500,7 @@ class LogoRaster {
464
500
  continue;
465
501
  const face = faces[id - 1];
466
502
  // Points farther from the camera fade slightly toward the background for depth.
467
- const fog = clamp01(0.15 - CAMERA_DISTANCE * (1 - depth[index]) * 0.12) * (light ? 0.5 : 1);
503
+ const fog = clamp01(0.15 - cameraDistance * (1 - depth[index]) * 0.12) * (light ? 0.5 : 1);
468
504
  faceIds[index] = 0;
469
505
  depth[index] = Infinity;
470
506
  const cellX = dotX >> 1;
@@ -584,6 +620,7 @@ class LogoRaster {
584
620
  }
585
621
  visibleFaces(m, origin, pose, boxes, dotWidth, dotHeight, lightBackground) {
586
622
  const faces = [];
623
+ const cameraDistance = this.cameraDistance;
587
624
  for (const box of boxes) {
588
625
  for (let a = 0; a < 3; a++) {
589
626
  const u = (a + 1) % 3;
@@ -615,7 +652,7 @@ class LogoRaster {
615
652
  const x = m[0] * corner[0] + m[1] * corner[1] + m[2] * corner[2];
616
653
  const y = m[3] * corner[0] + m[4] * corner[1] + m[5] * corner[2];
617
654
  const z = m[6] * corner[0] + m[7] * corner[1] + m[8] * corner[2];
618
- const perspective = (pose.scale * CAMERA_DISTANCE) / (CAMERA_DISTANCE - z);
655
+ const perspective = (pose.scale * cameraDistance) / (cameraDistance - z);
619
656
  const screenX = pose.centerX + x * perspective;
620
657
  const screenY = pose.centerY + y * perspective;
621
658
  minX = Math.min(minX, screenX);
@@ -670,7 +707,7 @@ let playing = false;
670
707
  * Show the animation as a fullscreen overlay until it is dismissed. The overlay takes focus and mouse input and
671
708
  * returns focus when hidden, so the rest of the UI keeps running underneath untouched.
672
709
  */
673
- export async function playPiLogoAnimation(tui, options) {
710
+ export async function playEasterEgg3d(tui, screen, egg) {
674
711
  if (playing)
675
712
  return;
676
713
  playing = true;
@@ -686,14 +723,15 @@ export async function playPiLogoAnimation(tui, options) {
686
723
  playing = false;
687
724
  return;
688
725
  }
689
- const animation = new PiLogoAnimation(tui, options, colors, () => {
726
+ const model = egg.kind === "armin" ? arminModel(toRgb(theme.colors.accent)) : piLogoModel({ column: egg.column, row: egg.row });
727
+ const animation = new EasterEgg3dAnimation(tui, screen, model, colors, () => {
690
728
  playing = false;
691
729
  overlay.hide();
692
730
  });
693
731
  const overlay = tui.showOverlay(animation, { anchor: "top-left", width: "100%", maxHeight: "100%" });
694
732
  }
695
- export class PiLogoAnimation {
696
- constructor(tui, options, colors, onDone) {
733
+ export class EasterEgg3dAnimation {
734
+ constructor(tui, screen, model, colors, onDone) {
697
735
  this.startTime = performance.now();
698
736
  this.lastRender = performance.now();
699
737
  this.screenWidth = -1;
@@ -702,9 +740,10 @@ export class PiLogoAnimation {
702
740
  this.stars = [];
703
741
  this.cells = [];
704
742
  this.ansiCache = new Map();
705
- this.logo = new LogoRaster();
706
743
  this.tui = tui;
707
- this.options = options;
744
+ this.screen = screen;
745
+ this.model = model;
746
+ this.raster = new BlockRaster(model.cameraDistance);
708
747
  this.foreground = colors.foreground;
709
748
  this.background = colors.background;
710
749
  this.onDone = onDone;
@@ -789,19 +828,20 @@ export class PiLogoAnimation {
789
828
  starAlpha = this.starAlpha(time);
790
829
  offsets = this.blockOffsets(time);
791
830
  }
792
- const boxes = BLOCKS.map((block, index) => {
831
+ const { columns, rows } = this.model;
832
+ const boxes = this.model.blocks.map((block, index) => {
793
833
  const [dx, dy, dz] = offsets[index];
794
- const x = block.home[0] - 2 + dx;
795
- const y = block.home[1] - 2 + dy;
834
+ const x = block.home[0] - columns / 2 + dx;
835
+ const y = block.home[1] - rows / 2 + dy;
796
836
  return { min: [x, y, dz - DEPTH / 2], max: [x + 1, y + 1, dz + DEPTH / 2], color: block.color };
797
837
  });
798
- const logo = this.logo;
799
- logo.render(width, height, pose, boxes, background, isLight(background) ? LIGHT_HALO_STRENGTH : 0);
838
+ const raster = this.raster;
839
+ raster.render(width, height, pose, boxes, background, isLight(background) ? LIGHT_HALO_STRENGTH : 0);
800
840
  const halo = (index, base) => {
801
- const amount = logo.haloAmount[index];
841
+ const amount = raster.haloAmount[index];
802
842
  if (amount <= 0)
803
843
  return base;
804
- const color = logo.haloRgb;
844
+ const color = raster.haloRgb;
805
845
  return mix(base ?? background, [color[index * 3], color[index * 3 + 1], color[index * 3 + 2]], amount);
806
846
  };
807
847
  const backgroundFade = smooth(dissolveTime / BACKGROUND_FADE);
@@ -855,11 +895,11 @@ export class PiLogoAnimation {
855
895
  emit(hint.text[offset], offset < hint.keyLength ? hint.keyColor : hint.color, cellBg);
856
896
  continue;
857
897
  }
858
- const dots = logo.bits[index];
898
+ const dots = raster.bits[index];
859
899
  if (dots) {
860
900
  // Quantized so neighboring cells on one face usually share a color and its escape sequence.
861
- const step = LOGO_COLOR_STEP * logo.counts[index];
862
- const rgb = logo.rgb;
901
+ const step = LOGO_COLOR_STEP * raster.counts[index];
902
+ const rgb = raster.rgb;
863
903
  const color = [
864
904
  Math.round(rgb[index * 3] / step) * LOGO_COLOR_STEP,
865
905
  Math.round(rgb[index * 3 + 1] / step) * LOGO_COLOR_STEP,
@@ -874,7 +914,7 @@ export class PiLogoAnimation {
874
914
  continue;
875
915
  }
876
916
  const dust = (dissolveTime - cell.delay) / DUST_DURATION;
877
- if (dust < 0 && cell.width === 2 && !logo.bits[index + 1]) {
917
+ if (dust < 0 && cell.width === 2 && !raster.bits[index + 1]) {
878
918
  emit(cell.text, mix(cell.fg, background, textFade), cellBg);
879
919
  column++;
880
920
  continue;
@@ -919,18 +959,19 @@ export class PiLogoAnimation {
919
959
  this.timer = undefined;
920
960
  this.onDone();
921
961
  }
922
- /** Each block's displacement from its place in the logo at `time`, in grid units. */
962
+ /** Each block's displacement from its place in the model at `time`, in grid units. */
923
963
  blockOffsets(time) {
924
- const home = () => BLOCKS.map(() => [0, 0, 0]);
964
+ const blocks = this.model.blocks;
965
+ const home = () => blocks.map(() => [0, 0, 0]);
925
966
  if (time < PUZZLE_START)
926
967
  return home();
927
968
  const cycle = Math.floor((time - PUZZLE_START) / PUZZLE_CYCLE);
928
969
  const local = time - PUZZLE_START - cycle * PUZZLE_CYCLE;
929
970
  if (this.shuffle?.cycle !== cycle)
930
- this.shuffle = { cycle, steps: shuffleSteps(cycle) };
971
+ this.shuffle = { cycle, steps: shuffleSteps(this.model, cycle) };
931
972
  const steps = this.shuffle.steps;
932
973
  const offset = (position, index) => {
933
- const block = BLOCKS[index].home;
974
+ const block = blocks[index].home;
934
975
  return [position[0] - block[0], position[1] - block[1], position[2] - block[2]];
935
976
  };
936
977
  const shuffleEnd = PUZZLE_STEP * PUZZLE_STEPS;
@@ -965,14 +1006,16 @@ export class PiLogoAnimation {
965
1006
  return smooth((time - FLY_START - FLY_DURATION) / HINT_FADE);
966
1007
  }
967
1008
  pose(width, height, progress, time) {
968
- // At the start the front face must cover exactly the header logo's 8x8 dots despite the perspective.
969
- const startScale = (2 * (CAMERA_DISTANCE - DEPTH / 2)) / CAMERA_DISTANCE;
970
- const reach = LOGO_RADIUS * (CAMERA_DISTANCE / (CAMERA_DISTANCE - LOGO_RADIUS));
971
- const endScale = Math.max(startScale, Math.min(width * 2 * 0.35, (height * 4 - 8) * 0.48) / reach);
972
- const startX = this.options.logoColumn * 2 + 4;
973
- const startY = this.options.logoRow * 4 + 4;
1009
+ const { columns, rows, cameraDistance, radius, widthShare, origin } = this.model;
1010
+ const reach = radius * (cameraDistance / (cameraDistance - radius));
1011
+ // Lifting off, the front face must cover exactly the half-block cells (2x2 dots per pixel) despite the
1012
+ // perspective.
1013
+ const startScale = origin ? (2 * (cameraDistance - DEPTH / 2)) / cameraDistance : START_SCALE;
1014
+ const endScale = Math.max(startScale, Math.min(width * 2 * widthShare, (height * 4 - 8) * 0.48) / reach);
974
1015
  const endX = width;
975
1016
  const endY = height * 2 - 2;
1017
+ const startX = origin ? origin.column * 2 + columns : endX;
1018
+ const startY = origin ? origin.row * 4 + rows : endY;
976
1019
  return {
977
1020
  centerX: startX + (endX - startX) * progress,
978
1021
  centerY: startY + (endY - startY) * progress,
@@ -1018,10 +1061,12 @@ export class PiLogoAnimation {
1018
1061
  return stars;
1019
1062
  }
1020
1063
  prepareCells(width, foreground) {
1021
- const cells = parseScreen(this.options.screen, width, foreground, this.background);
1022
- const centerX = this.options.logoColumn + 2;
1023
- const centerY = this.options.logoRow + 1;
1064
+ const cells = parseScreen(this.screen, width, foreground, this.background);
1024
1065
  const height = Math.max(1, this.tui.terminal.rows);
1066
+ const { columns, rows, origin } = this.model;
1067
+ // The dust spreads out from where the model starts.
1068
+ const centerX = origin ? origin.column + columns / 2 : width / 2;
1069
+ const centerY = origin ? origin.row + rows / 4 : height / 2;
1025
1070
  const farthest = Math.hypot(Math.max(centerX, width - centerX), Math.max(centerY, height - centerY) * 2);
1026
1071
  for (let row = 0; row < cells.length; row++) {
1027
1072
  const line = cells[row];
@@ -1041,18 +1086,20 @@ export class PiLogoAnimation {
1041
1086
  cell.ink = glyphInk(owner.text, seed);
1042
1087
  }
1043
1088
  }
1044
- // The 3D logo replaces the header logo.
1045
- for (let row = this.options.logoRow; row < this.options.logoRow + 2; row++) {
1046
- const line = cells[row];
1047
- if (!line)
1048
- continue;
1049
- for (let column = this.options.logoColumn; column < this.options.logoColumn + 4; column++) {
1050
- const cell = line[column];
1051
- if (cell) {
1052
- cell.text = " ";
1053
- cell.width = 1;
1054
- cell.ink = 0;
1055
- cell.bg = undefined;
1089
+ // The 3D model replaces its half-block rendering on screen.
1090
+ if (origin) {
1091
+ for (let row = origin.row; row < origin.row + Math.ceil(rows / 2); row++) {
1092
+ const line = cells[row];
1093
+ if (!line)
1094
+ continue;
1095
+ for (let column = origin.column; column < origin.column + columns; column++) {
1096
+ const cell = line[column];
1097
+ if (cell) {
1098
+ cell.text = " ";
1099
+ cell.width = 1;
1100
+ cell.ink = 0;
1101
+ cell.bg = undefined;
1102
+ }
1056
1103
  }
1057
1104
  }
1058
1105
  }
@@ -1073,4 +1120,4 @@ export class PiLogoAnimation {
1073
1120
  return value;
1074
1121
  }
1075
1122
  }
1076
- //# sourceMappingURL=pi-logo-animation.js.map
1123
+ //# sourceMappingURL=easter-egg-3d.js.map