@astrofoundry/pi-astro 0.14.3 → 0.14.5

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.
@@ -0,0 +1,18 @@
1
+ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
2
+ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
3
+ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
4
+ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
5
+ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
6
+ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
7
+ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
8
+ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
9
+ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
10
+ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
11
+ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
12
+ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
13
+ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
14
+ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
15
+ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
16
+ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
17
+ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
18
+ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
@@ -1,35 +1,25 @@
1
1
  import { describe, expect, it } from "vitest";
2
- import { ART_HEIGHT, ART_WIDTH, pickVariant, renderArt, VARIANTS } from "./art.ts";
2
+ import { artHeight, artWidth, renderArt } from "./art.ts";
3
3
 
4
4
  describe("art", () => {
5
- it("exposes ART_WIDTH and ART_HEIGHT consistent with the variants", () => {
6
- expect(ART_WIDTH).toBeGreaterThan(0);
7
- expect(ART_HEIGHT).toBeGreaterThan(0);
8
- for (const v of VARIANTS) {
9
- expect(v[0].length).toBe(ART_WIDTH);
10
- expect(Math.ceil(v.length / 2)).toBe(ART_HEIGHT);
11
- }
5
+ it("loads non-zero dimensions from the bundled art.ansi", () => {
6
+ expect(artWidth()).toBeGreaterThan(0);
7
+ expect(artHeight()).toBeGreaterThan(0);
12
8
  });
13
9
 
14
- it("pickVariant is deterministic for the same seed", () => {
15
- expect(pickVariant(0)).toBe(pickVariant(0));
16
- expect(pickVariant(7)).toBe(pickVariant(7));
10
+ it("renderArt returns artHeight() lines", () => {
11
+ const lines = renderArt();
12
+ expect(lines).toHaveLength(artHeight());
17
13
  });
18
14
 
19
- it("pickVariant handles negative seeds without throwing", () => {
20
- expect(() => pickVariant(-100)).not.toThrow();
21
- });
22
-
23
- it("renderArt produces ART_HEIGHT rows", () => {
24
- const lines = renderArt(VARIANTS[0]);
25
- expect(lines).toHaveLength(ART_HEIGHT);
26
- });
27
-
28
- it("renderArt embeds 24-bit ANSI fg+bg colours and the upper-half block glyph", () => {
29
- const lines = renderArt(VARIANTS[0]);
30
- const joined = lines.join("\n");
15
+ it("each line embeds 24-bit ANSI fg+bg colours and the upper-half block glyph", () => {
16
+ const joined = renderArt().join("\n");
31
17
  expect(joined).toContain("[38;2;");
32
- expect(joined).toContain(";48;2;");
18
+ expect(joined).toContain("[48;2;");
33
19
  expect(joined).toContain("▀");
34
20
  });
21
+
22
+ it("subsequent calls return the same cached array", () => {
23
+ expect(renderArt()).toBe(renderArt());
24
+ });
35
25
  });
@@ -1,125 +1,38 @@
1
- import { fgBg, PALETTE, type RGB } from "./colors.ts";
2
-
3
- const _ = "·";
4
- const S = "S";
5
- const X = "X";
6
- const A = "A";
7
- const Y = "Y";
8
- const W = "W";
9
- const M = "M";
10
- const D = "D";
11
- const G = "G";
12
- const T = "T";
13
- const E = "E";
14
- const F = "F";
15
- const O = "O";
16
- const R = "R";
17
-
18
- type PaletteKey = typeof _ | typeof S | typeof X | typeof A | typeof Y | typeof W | typeof M | typeof D | typeof G | typeof T | typeof E | typeof F | typeof O | typeof R;
19
-
20
- const PIXEL_COLOR: Record<PaletteKey, RGB | null> = {
21
- [_]: null,
22
- [S]: PALETTE.suitLight,
23
- [X]: PALETTE.suitMid,
24
- [A]: PALETTE.suitShadow,
25
- [Y]: PALETTE.suitTrim,
26
- [W]: PALETTE.visorLow,
27
- [M]: PALETTE.visorMid,
28
- [D]: PALETTE.visorTop,
29
- [G]: PALETTE.backpack,
30
- [T]: PALETTE.earthBlue,
31
- [E]: PALETTE.earthGreen,
32
- [F]: PALETTE.flameYellow,
33
- [O]: PALETTE.flameOrange,
34
- [R]: PALETTE.flameRed,
35
- };
36
-
37
- const ASTRONAUT_FLOATING: readonly string[] = [
38
- "··········SSSSSSSSSS··········",
39
- "·······SSSSXXXXXXXXSSSS·······",
40
- "·····SSXXMMMMMMMMMMMMXXSS·····",
41
- "····SXMMDDDDDDDDDDDDDDMMSS····",
42
- "···SXMDDDDWWDDDDDDWWDDDDMMS···",
43
- "···SXMDDWWWWDDDDDDWWWWDDMMS···",
44
- "···SXMDDDDDDDDDDDDDDDDDDMMS···",
45
- "···SXMDDDDDDDDWWDDDDDDDDMMS···",
46
- "····SXMMDDDDDDDDDDDDDDMMSS····",
47
- "·····SSXXMMMMMMMMMMMMXXSS·····",
48
- "·······SSXXXXXXXXXXXXSS·······",
49
- "··········SXAAAAAAXSS·········",
50
- "·······SSSSSSSSSSSSSSSS·······",
51
- "······SSSSGGGGSSSSGGGGSSS·····",
52
- "·····SSSSGGGGSSAAYAASSGGSSS···",
53
- "·····SSGGGGSSSAYYYAASSGGGGS···",
54
- "·····SSGGGSSSSAYAYASSSSGGGS···",
55
- "·····SSGGGSSSSAAAASSSSSGGGS···",
56
- "·····SSGGGSSSSSSSSSSSSGGGS····",
57
- "······SSSSSSSSSSSSSSSSSS······",
58
- "········SSAAAAAASSSSSSAAAA····",
59
- "········SSSAAAASSSSAAAAS······",
60
- "········SXXSSSSSSSXXXSS·······",
61
- "·········SSSS·····SSSS········",
62
- "··········SSS······SSS········",
63
- "··········SS········SS········",
64
- ];
65
-
66
- const ASTRONAUT_FLAG: readonly string[] = [
67
- "··········SSSSSSSSSS··········",
68
- "·······SSSSXXXXXXXXSSSS·······",
69
- "·····SSXXMMMMMMMMMMMMXXSS·····",
70
- "····SXMMDDDDDDDDDDDDDDMMSS····",
71
- "···SXMDDDDWWDDDDDDWWDDDDMMS···",
72
- "···SXMDDWWWWDDDDDDWWWWDDMMS···",
73
- "···SXMDDDDDDDDDDDDDDDDDDMMS···",
74
- "···SXMDDDDDDDDWWDDDDDDDDMMS···",
75
- "····SXMMDDDDDDDDDDDDDDMMSS····",
76
- "·····SSXXMMMMMMMMMMMMXXSS·····",
77
- "·······SSXXXXXXXXXXXXSS·······",
78
- "··········SXAAAAAAXSS·········",
79
- "······YYYYYSSSSSSSSSSSSSS·····",
80
- "······YYYYYSSGGGGSSSGGGGSSS···",
81
- "······YYYYYSSGGGGSSSSGGGSS····",
82
- "······YYYYYSSGGGSSSSSSGGGS····",
83
- "······YYYYYSSGGSSSSSSSSGGS····",
84
- "········SSSSSSSSSSSSSSSSSS····",
85
- "········SSAAAAAASSSAAAASS·····",
86
- "········SSSAAAASSSAAAAAS······",
87
- "········SXXSSSSSSSXXXSS·······",
88
- "·········SSSS·····SSSS········",
89
- "··········SSS······SSS········",
90
- "··········SS········SS········",
91
- "·························E····",
92
- "·························TT···",
93
- ];
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { visibleWidth } from "@mariozechner/pi-tui";
5
+
6
+ const extensionDir = path.dirname(fileURLToPath(import.meta.url));
7
+ const artPath = path.resolve(extensionDir, "art.ansi");
8
+
9
+ interface ArtData {
10
+ lines: string[];
11
+ width: number;
12
+ height: number;
13
+ }
94
14
 
95
- export const VARIANTS = [ASTRONAUT_FLOATING, ASTRONAUT_FLAG] as const;
15
+ let cached: ArtData | null = null;
16
+
17
+ function load(): ArtData {
18
+ if (cached !== null) return cached;
19
+ const raw = fs.readFileSync(artPath, "utf8");
20
+ const lines = raw.split("\n");
21
+ while (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
22
+ const width = lines.length > 0 ? visibleWidth(lines[0]) : 0;
23
+ const height = lines.length;
24
+ cached = { lines, width, height };
25
+ return cached;
26
+ }
96
27
 
97
- export function pickVariant(seed: number): readonly string[] {
98
- const idx = Math.abs(seed) % VARIANTS.length;
99
- return VARIANTS[idx];
28
+ export function renderArt(): string[] {
29
+ return load().lines;
100
30
  }
101
31
 
102
- export function renderArt(grid: readonly string[]): string[] {
103
- const rows: string[] = [];
104
- for (let y = 0; y < grid.length; y += 2) {
105
- const top = grid[y];
106
- const bot = grid[y + 1] ?? "·".repeat(top.length);
107
- let line = "";
108
- for (let x = 0; x < top.length; x++) {
109
- const tk = (top[x] ?? "·") as PaletteKey;
110
- const bk = (bot[x] ?? "·") as PaletteKey;
111
- const tc = PIXEL_COLOR[tk] ?? null;
112
- const bc = PIXEL_COLOR[bk] ?? null;
113
- if (!tc && !bc) {
114
- line += " ";
115
- continue;
116
- }
117
- line += fgBg(tc ?? PALETTE.space, bc ?? PALETTE.space);
118
- }
119
- rows.push(line);
120
- }
121
- return rows;
32
+ export function artWidth(): number {
33
+ return load().width;
122
34
  }
123
35
 
124
- export const ART_WIDTH = ASTRONAUT_FLOATING[0].length;
125
- export const ART_HEIGHT = Math.ceil(ASTRONAUT_FLOATING.length / 2);
36
+ export function artHeight(): number {
37
+ return load().height;
38
+ }
@@ -16,10 +16,16 @@ export const PALETTE = {
16
16
  visorTop: [40, 80, 180] as const,
17
17
  visorMid: [80, 160, 240] as const,
18
18
  visorLow: [180, 220, 255] as const,
19
- suitLight: [240, 240, 230] as const,
19
+ suitLight: [255, 255, 255] as const,
20
20
  suitMid: [200, 200, 195] as const,
21
21
  suitShadow: [120, 120, 130] as const,
22
- suitTrim: [240, 200, 60] as const,
22
+ suitTrim: [254, 209, 0] as const,
23
+ suitGoldHigh: [255, 220, 110] as const,
24
+ suitGoldShadow: [200, 150, 30] as const,
25
+ flagBlue: [0, 51, 160] as const,
26
+ voidBlack: [10, 12, 30] as const,
27
+ outline: [22, 32, 64] as const,
28
+ highlight: [255, 255, 255] as const,
23
29
  backpack: [80, 80, 90] as const,
24
30
  earthBlue: [50, 110, 200] as const,
25
31
  earthGreen: [70, 160, 100] as const,
@@ -1,6 +1,6 @@
1
1
  import { visibleWidth } from "@mariozechner/pi-tui";
2
2
  import { fg, PALETTE } from "./colors.ts";
3
- import { ART_HEIGHT, ART_WIDTH, pickVariant, renderArt } from "./art.ts";
3
+ import { artHeight, artWidth, renderArt } from "./art.ts";
4
4
  import { pickMessage } from "./messages.ts";
5
5
  import { renderStarfield, type Mask } from "./stars.ts";
6
6
 
@@ -73,12 +73,12 @@ export function composeWelcome(width: number, seed: number, stats: WelcomeStats)
73
73
  const hasVersion = !!stats.pkgVersion;
74
74
  const versionRow = 1;
75
75
  const artStartRow = hasVersion ? 2 : 1;
76
- const totalRows = artStartRow + ART_HEIGHT + 6;
76
+ const totalRows = artStartRow + artHeight() + 6;
77
77
  const safeWidth = Math.max(40, width);
78
- const artStartCol = Math.max(0, Math.floor((safeWidth - ART_WIDTH) / 2));
78
+ const artStartCol = Math.max(0, Math.floor((safeWidth - artWidth()) / 2));
79
79
 
80
80
  const mask = new CompositeMask(totalRows, safeWidth);
81
- mask.occupyArt(artStartRow, artStartCol, ART_HEIGHT, ART_WIDTH);
81
+ mask.occupyArt(artStartRow, artStartCol, artHeight(), artWidth());
82
82
 
83
83
  const titleLine = fg(PALETTE.title, TITLE);
84
84
  const titlePlaced = centerInLine(titleLine, safeWidth);
@@ -93,7 +93,7 @@ export function composeWelcome(width: number, seed: number, stats: WelcomeStats)
93
93
 
94
94
  const taglineRaw = pickMessage(seed);
95
95
  const taglineLine = fg(PALETTE.tagline, taglineRaw);
96
- const taglineRow = artStartRow + ART_HEIGHT + 1;
96
+ const taglineRow = artStartRow + artHeight() + 1;
97
97
  const taglinePlaced = centerInLine(taglineLine, safeWidth);
98
98
  mask.occupy(taglineRow, taglinePlaced.col, visibleWidth(taglineLine));
99
99
 
@@ -109,7 +109,7 @@ export function composeWelcome(width: number, seed: number, stats: WelcomeStats)
109
109
  mask.occupy(hintRow, hintPlaced.col, visibleWidth(hintColored));
110
110
 
111
111
  const stars = renderStarfield(safeWidth, totalRows, seed, mask);
112
- const artLines = renderArt(pickVariant(seed));
112
+ const artLines = renderArt();
113
113
 
114
114
  const composed: string[] = [];
115
115
  for (let r = 0; r < totalRows; r++) {
@@ -121,7 +121,7 @@ export function composeWelcome(width: number, seed: number, stats: WelcomeStats)
121
121
  if (r === statsRow) line = overlay(line, statsPlaced.line, safeWidth);
122
122
  if (r === hintRow) line = overlay(line, hintPlaced.line, safeWidth);
123
123
 
124
- if (r >= artStartRow && r < artStartRow + ART_HEIGHT) {
124
+ if (r >= artStartRow && r < artStartRow + artHeight()) {
125
125
  const artLine = artLines[r - artStartRow];
126
126
  const artLineWidth = visibleWidth(artLine);
127
127
  line = overlayAt(line, " ".repeat(artStartCol) + artLine, safeWidth, artStartCol, artLineWidth);
@@ -1,12 +1,12 @@
1
1
  import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
2
2
  import { composeWelcome, type WelcomeStats } from "./compose.ts";
3
- import { ART_HEIGHT, ART_WIDTH } from "./art.ts";
3
+ import { artHeight, artWidth } from "./art.ts";
4
4
  import { readPackageVersion } from "./version.ts";
5
5
 
6
6
  const DISMISS_AFTER_MS = 6_000;
7
7
  const MOUNT_DELAY_MS = 100;
8
- const OVERLAY_WIDTH = Math.max(ART_WIDTH + 30, 70);
9
- const OVERLAY_HEIGHT = ART_HEIGHT + 8;
8
+ const OVERLAY_HORIZONTAL_PADDING = 30;
9
+ const OVERLAY_VERTICAL_PADDING = 8;
10
10
 
11
11
  function readCavemanLevel(ctx: ExtensionContext): string | undefined {
12
12
  const entries = ctx.sessionManager.getEntries();
@@ -103,8 +103,8 @@ export default function astroWelcomeExtension(pi: ExtensionAPI): void {
103
103
  overlay: true,
104
104
  overlayOptions: {
105
105
  anchor: "center",
106
- width: OVERLAY_WIDTH,
107
- maxHeight: OVERLAY_HEIGHT,
106
+ width: Math.max(artWidth() + OVERLAY_HORIZONTAL_PADDING, 70),
107
+ maxHeight: artHeight() + OVERLAY_VERTICAL_PADDING,
108
108
  },
109
109
  },
110
110
  )
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrofoundry/pi-astro",
3
- "version": "0.14.3",
3
+ "version": "0.14.5",
4
4
  "description": "Personal pi customizations (extensions, skills, prompts, themes) for the pi coding agent.",
5
5
  "keywords": [
6
6
  "pi-package"