osdy-pi 1.10.0 → 1.11.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.
package/README.md CHANGED
@@ -1,23 +1,24 @@
1
1
  # Osdy Pi — a themed, responsive Pi workspace
2
2
 
3
- Osdy Pi gives [Pi](https://github.com/earendil-works/pi) a themed, responsive terminal presentation with a header, editor, working indicator, and Git view. Visit the [Osdy landing page](https://landing-osdy.vercel.app/).
3
+ Osdy Pi gives [Pi](https://github.com/earendil-works/pi) a themed, responsive terminal presentation with a header, editor, working indicator, and Git view. Visit the [Osdy landing page](https://osdy-pi.vercel.app/).
4
4
 
5
5
  <img width="1857" height="847" alt="Osdy Pi interface" src="https://github.com/user-attachments/assets/028eeb14-3f43-4f1c-9603-0c55a8d2856d" />
6
6
 
7
7
  ## Release highlights
8
8
 
9
- **1.10.0 adds four-page local usage analytics** with shared period and profile/provider/model filters, keyboard navigation, and metadata-only history. The bundle retains **20 themes**. These highlights describe the 1.10.0 release source; verify npm availability and the resolved version before installing.
9
+ **1.11.0 adds three Spider-Man-inspired dark palettes**, bringing the bundle to **23 themes**. It retains the four-page local usage analytics introduced in 1.10.0, with shared period and profile/provider/model filters, keyboard navigation, and metadata-only history. These version introductions are historical, not a fixed latest-version claim. After npm Trusted Publishing is configured, pushes to main (including direct pushes) run checks and attempt to publish an automatically allocated patch to npm. Failed checks publish nothing; authentication or registry failures can also stop publication. Verify the resolved npm version before installing. Maintainers: [automatic npm publishing setup](https://github.com/OsdyOrtiz/Osdy-Pi/blob/main/docs/npm-publishing.md).
10
10
 
11
11
  | Area | What ships |
12
12
  | --- | --- |
13
13
  | Local analytics (1.10.0) | `/osdy-usage` opens Summary, Profiles, Models, and History pages for recorded tokens and estimated cost—not billing or subscription quota. |
14
- | Themes introduced in 1.9.0 | Halloween and Halloween Killer add two dark palettes, bringing the bundle to 20 themes. |
14
+ | Themes introduced in 1.11.0 | Spider-Man Classic, Miles Morales, and Spider-Verse add three distinct dark palettes. |
15
+ | Themes introduced in 1.9.0 | Halloween and Halloween Killer add two dark palettes. |
15
16
  | Themes introduced in 1.8.0 | Gruvbox Dark, Nord, Rosé Pine base dark, and Daniela Cute. |
16
17
  | Message cards | Pi-native colored user cards without emojis and plain assistant Markdown preceded by a separate `🦝` transcript row. |
17
18
  | Header and mascot | A theme-aware `neon` header and selectable `bts` mascot, both responsive and persisted independently. |
18
19
  | Accounts | `/osdy-account` switches Codex profiles in place with atomic activation, rollback, bounded auth files, and process-safe locks. |
19
20
  | Quota | `/usage` and compact bars emphasize remaining Codex quota at warning (40% or less) and error (15% or less) thresholds. |
20
- | TODO (starting in 1.5.0) | First-party `todo` snapshots track the Pi session branch; `/todos` reads them without editing. ODD Markdown remains separate. |
21
+ | TODO (starting in 1.5.0, opt-in) | Default-off first-party `todo` snapshots track the Pi session branch; `/todos` reads them without editing. ODD Markdown remains separate. |
21
22
 
22
23
  ## Prerequisites
23
24
 
@@ -39,7 +40,7 @@ Osdy Pi gives [Pi](https://github.com/earendil-works/pi) a themed, responsive te
39
40
  pi install git:github.com/OsdyOrtiz/Osdy-Pi
40
41
  ```
41
42
 
42
- Check the resolved npm version: local analytics requires 1.10.0 or later. Once 1.10.0 is available on npm, `pi install npm:osdy-pi@1.10.0` selects it explicitly. For an unpinned installation, use `pi update`; it does not move a pinned version, so replace an older pin explicitly. First-party TODO is included starting in 1.5.0; `/osdy-pi uninstall` and explicit agent provider modes start in 1.6.0. Installing Osdy does not automatically install Joker; see [explicit agent provider modes](#explicit-agent-provider-modes-in-normal-pi).
43
+ Check the resolved npm version: the Spider-Man-inspired themes require 1.11.0 or later; local analytics requires 1.10.0 or later. To pin the historical feature release explicitly, use `pi install npm:osdy-pi@1.11.0` if that version is available on npm; automatic patches may already be newer. For an unpinned installation, use `pi update`; it does not move a pinned version, so replace an older pin explicitly. First-party TODO is included starting in 1.5.0; `/osdy-pi uninstall` and explicit agent provider modes start in 1.6.0. Installing Osdy does not automatically install Joker; see [explicit agent provider modes](#explicit-agent-provider-modes-in-normal-pi).
43
44
 
44
45
  2. Start Pi:
45
46
 
@@ -47,7 +48,9 @@ Osdy Pi gives [Pi](https://github.com/earendil-works/pi) a themed, responsive te
47
48
  pi
48
49
  ```
49
50
 
50
- Osdy Pi's own extension and themes are bundled—do not install them separately. Version **1.10.0** bundles **20 themes**: Gruvbox Dark, Nord, Rosé Pine base dark, and Daniela Cute were introduced in 1.8.0; Halloween and Halloween Killer are added in 1.9.0. Check your installed npm version to see which themes are available. Launch through `osdy-pi` (or `npm run pi:dev`) to activate a valid Osdy default account into Pi's shared agent directory before Pi starts. With no default, Pi remains unmanaged; run `/osdy-account` to create a profile and establish a default. On session start, Osdy Pi restores its persisted enabled state when a UI is available and preserves your selected Pi theme. When disabled, it leaves Gentle Shell (or Pi's native UI) untouched.
51
+ **Osdy TODO is off by default**, so installing Osdy alongside Gentle Shell does not register a competing `todo`, `/todos`, or TODO widget. To select Osdy explicitly, run `/osdy-pi todo on`, confirm the active profile target, then check `/osdy-pi todo status`. See [TODO selection](#explicit-todo-selection).
52
+
53
+ Osdy Pi's own extension and themes are bundled—do not install them separately. Starting with **1.11.0**, the package bundles **23 themes**, including Spider-Man Classic, Miles Morales, and Spider-Verse: Gruvbox Dark, Nord, Rosé Pine base dark, and Daniela Cute were introduced in 1.8.0; Halloween and Halloween Killer were added in 1.9.0. Check your installed npm version to see which themes are available. Launch through `osdy-pi` (or `npm run pi:dev`) to activate a valid Osdy default account into Pi's shared agent directory before Pi starts. With no default, Pi remains unmanaged; run `/osdy-account` to create a profile and establish a default. On session start, Osdy Pi restores its persisted enabled state when a UI is available and preserves your selected Pi theme. When disabled, it leaves Gentle Shell (or Pi's native UI) untouched.
51
54
 
52
55
  ### Optional: isolated `osdy` command
53
56
 
@@ -127,14 +130,30 @@ To load a local `gentle-pi` checkout while keeping Osdy Pi's UI authoritative, r
127
130
  osdy-pi gentle setup /absolute/path/to/gentle-pi
128
131
  ```
129
132
 
130
- The command validates that the absolute source is a readable `gentle-pi` package with Gentle's todo and agents extensions before atomically updating `$PI_CODING_AGENT_DIR/settings.json` (or `~/.pi/agent/settings.json`). It registers the local package immediately before the first configured Osdy Pi package entry, so Gentle Shell initializes first, with exclusions for Gentle's `gentle-todo.ts`, `ask-user-question.ts`, and `gentle-agents.ts`, plus `themes: []`. Restart Pi or run `/reload` after setup.
133
+ The command validates that the absolute source is a readable `gentle-pi` package with Gentle's todo and agents extensions before atomically updating `$PI_CODING_AGENT_DIR/settings.json` (or `~/.pi/agent/settings.json`). For a new registration, it places the local package before the first configured Osdy Pi entry, with exclusions for `ask-user-question.ts` and `gentle-agents.ts`, plus `themes: []`. Existing Gentle entries, deliberate filters and resource selections are preserved. Gentle TODO remains eligible by default; an existing explicit Osdy TODO opt-in retains its TODO exclusion and ownership. Restart Pi or run `/reload` after setup.
131
134
 
132
135
  Gentle Shell intentionally remains fully active underneath Osdy, including its footer and widgets. While Osdy is enabled, Osdy claims the footer and editor; disabling Osdy restores the editor Gentle Shell provided at session startup. Gentle's changes widget may coexist with Osdy's visual widgets.
133
136
 
134
- The exclusions prevent Gentle's todo, questionnaire, and agents extensions from competing with the suite. Osdy's session-branch snapshots are authoritative for its `todo` tool and read-only `/todos` command; `pi-subagents-j0k3r` is the subagent system in Joker mode (Gentle agents in Gentle mode) and `@juicesharp/rpiv-ask-user-question` remains the structured-question plugin. This setup does **not** remove an independently installed `@juicesharp/rpiv-todo` package or edit its package entry. Remove that package yourself as described below to avoid duplicate `todo` and `/todos` registrations.
137
+ The default exclusions prevent Gentle's questionnaire and agents extensions from competing with the suite, without selecting Osdy TODO. Pre-existing Gentle TODO exclusions remain unowned and are not erased or treated as opt-in. When explicitly selected, Osdy's session-branch snapshots are authoritative for its `todo` tool and read-only `/todos` command; `pi-subagents-j0k3r` is the subagent system in Joker mode (Gentle agents in Gentle mode) and `@juicesharp/rpiv-ask-user-question` remains the structured-question plugin. This setup does **not** remove an independently installed `@juicesharp/rpiv-todo` package or edit its package entry. Remove that package yourself as described below to avoid duplicate `todo` and `/todos` registrations.
138
+
139
+ ## Explicit TODO selection
140
+
141
+ ```text
142
+ /osdy-pi todo on
143
+ /osdy-pi todo status
144
+ /osdy-pi todo off
145
+ ```
146
+
147
+ `on` requires confirmation showing the active `$PI_CODING_AGENT_DIR/settings.json` (otherwise `~/.pi/agent/settings.json`). It atomically saves opt-in and only the necessary `-extensions/gentle-todo.ts` exclusions in the `osdyPiTodoProvider` namespace (`version: 1`, `enabled`, `ownedExclusions`). `off` opts out and removes only exclusions actually added by this selector; pre-existing user/legacy exclusions stay intact. Both preserve unrelated settings and task history. Successful switches notify before reloading; restart Pi if reload fails. Manual settings changes require `/reload` or restart. `status` distinguishes configured selection, actual registration in this runtime and current settings eligibility; legacy filters can leave no provider and are reported rather than repaired silently. `/osdy-pi off` remains visual-only.
148
+
149
+ Supported scope: active personal, custom or isolated profiles with npm Gentle or validated local packages (absolute, settings-relative, `~/`, `file:`); duplicate entries are checked individually. Project Gentle overrides, malformed/symlink settings, direct-extension settings, unsupported identities and uncertain allowlists refuse activation without writes. Disabled `extensions: []` is never broadened by a negative filter. Startup fails closed if an opted-on profile later gains an unfiltered Gentle source. Startup never writes defaults or infers opt-in from old filters. Only an explicit opt-in runs a bounded read-only Node cwd probe through `pi.exec`; the awaited factory checks the SDK working directory before registering TODO surfaces and fails closed if it cannot obtain a valid cwd. Reordering selector-owned package entries requires resolving ownership before switching; the selector refuses ambiguous restoration.
150
+
151
+ This is a supported-settings guarantee, not detection of arbitrary CLI/SDK-injected competing tools. SDK-only `agentDir` overrides must also set `PI_CODING_AGENT_DIR`. No project settings, installed packages, task snapshots, credentials or upstream Gentle code are changed. A new isolated setup defaults off and removes only proven selector-owned inherited TODO exclusions before remapping package sources; malformed or ambiguous ownership refuses setup before writes. User/legacy exclusions remain intact. Repeat setup preserves its existing explicit selection and resource filters.
135
152
 
136
153
  ## First-party TODO included starting in 1.5.0
137
154
 
155
+ The surfaces below register only after explicit TODO opt-in.
156
+
138
157
  The model-facing `todo` tool supports `create`, `update`, `list`, `get`, `delete`, and `clear`. Tool results carry full task snapshots; Pi's current session branch is the TODO authority. Session switches and compaction replay the latest valid branch snapshot, rather than sharing one project-wide list. `/todos` opens a read-only, grouped modal (pending, in progress, completed) in interactive Pi; scroll with arrow or Page Up/Down keys and close with Esc or q. The refreshed modal adds a completed-task progress meter, theme-aware status groups and a scroll-position footer while keeping every task reachable. It is not an interactive Markdown editor. In non-terminal modes with UI support, it retains notification output. ODD's `odd/tasks/*.md` ledger and Engram are separate orchestration records: there is no automatic sync with these Pi TODOs.
139
158
 
140
159
  In interactive Pi, the persistent widget appears above the editor while visible tasks exist. Its compact preview shows up to three task rows (excluding heading, overflow hint, and spacer), prioritizes unfinished tasks on overflow, and crosses out completed subjects. In fullscreen Pi, left-click the heading to expand or return to preview; wheel over the expanded widget to move one task at a time beneath the fixed heading. The final task can reach the first task row even in a short dock. A heading-only or hidden dock cannot display tasks; use `/todos` for the full keyboard-accessible list. Regular mode stays compact; Pi's tool-output expansion does not change this widget. `ctrl+shift+t` retains the separate fully collapsed view.
@@ -143,7 +162,7 @@ Expansion and scroll position survive same-session refresh, tree navigation, and
143
162
 
144
163
  ### Try session TODO
145
164
 
146
- Use this checkout (or a package containing the three-task preview), ask the agent to track a task with `todo`, then run `/todos`. The modal shows the entire grouped list with arrow/Page Up/Down scrolling. `maxWidgetLines: 7` permits five task rows in the expanded fullscreen widget; the preview remains capped at three. Smaller budgets also constrain the preview. Fullscreen widget mouse handling uses Pi's public 0.99.1 component APIs.
165
+ Run `/osdy-pi todo on` in this checkout (or a package containing the three-task preview), confirm and let Pi reload, then ask the agent to track a task with `todo`, then run `/todos`. The modal shows the entire grouped list with arrow/Page Up/Down scrolling. `maxWidgetLines: 7` permits five task rows in the expanded fullscreen widget; the preview remains capped at three. Smaller budgets also constrain the preview. Fullscreen widget mouse handling uses Pi's public 0.99.1 component APIs.
147
166
 
148
167
  **Verification to date (not complete):** The user confirmed fullscreen appearance and behavior via `pi:dev`. Automated PTY interaction passed 27 assertions, but fixture cleanup failed and the final task snapshot remains unverified; this was not a full end-to-end pass. Prior feature checks passed 187 extension tests, 60 script tests, typecheck, and lint. A disposable Pi TUI with seeded tasks showed the modal's progress meter, grouped rows, scroll footer, and close behavior. Authenticated RPC turns exercised all six `todo` actions, and an authenticated TUI smoke observed widget and `/todos` updates. An RPC lifecycle check observed an empty new session and the completed task on switching back. Compaction replay remains unverified because Pi returned “Nothing to compact (session too small).” Language switching in the authenticated TODO flow remains unverified (an isolated TUI with both extensions did switch English to Spanish). Visual strikethrough of completed rows in the authenticated TUI remains unverified. Launch under an installed Osdy account profile remains unverified. These checks do not establish full feature parity or validate a published npm artifact.
149
168
 
@@ -251,7 +270,7 @@ Before rename or removal, close this Pi process when it uses the target and **ma
251
270
 
252
271
  | Area | Included behavior |
253
272
  | --- | --- |
254
- | Themes | 20 built-in themes, including Osdy, Kanagawa, Dracula, Gruvbox, Nord, Rosé Pine, Daniela Cute, Halloween, Halloween Killer, Catppuccin, Matrix, and Lucent Orange palettes |
273
+ | Themes | 23 built-in themes, including Osdy, Kanagawa, Dracula, Gruvbox, Nord, Rosé Pine, Daniela Cute, Halloween, Halloween Killer, Catppuccin, Matrix, Lucent Orange, and three Spider-Man-inspired palettes |
255
274
  | Header and mascot | Independently selectable `osdy-theme`/`neon` headers and `current`/`bts` mascots |
256
275
  | Messages | Native colored user cards without emojis and plain assistant Markdown with a separate assistant `🦝` row |
257
276
  | Input | Responsive auto editor by default, with selectable simple Pi-native or extended framed modes |
@@ -266,10 +285,10 @@ Before rename or removal, close this Pi process when it uses the target and **ma
266
285
 
267
286
  ### Themes
268
287
 
269
- Version **1.10.0** contains **20 themes**. Gruvbox Dark, Nord, Rosé Pine base dark, and Daniela Cute were introduced in **1.8.0**; Halloween and Halloween Killer were added in **1.9.0**. To try the themes from a checkout:
288
+ The **1.11.0** release introduced a **23-theme** bundle, including Spider-Man Classic, Miles Morales, and Spider-Verse. Gruvbox Dark, Nord, Rosé Pine base dark, and Daniela Cute were introduced in **1.8.0**; Halloween and Halloween Killer were added in **1.9.0**. To try the themes from a checkout:
270
289
 
271
- 1. Run `npm run pi:dev` from the 1.10.0 checkout, or `/reload` if that checkout is already loaded in Pi.
272
- 2. Open `/settings` → **Theme** and select `osdy-pi-gruvbox-dark`, `osdy-pi-nord`, `osdy-pi-rose-pine`, `osdy-pi-daniela-cute`, `osdy-pi-halloween`, `osdy-pi-halloween-killer`, or another name below.
290
+ 1. Run `npm run pi:dev` from a checkout containing these themes, or `/reload` if that checkout is already loaded in Pi.
291
+ 2. Open `/settings` → **Theme** and select `osdy-pi-spider-man-classic`, `osdy-pi-miles-morales`, `osdy-pi-spider-verse`, or another name below.
273
292
 
274
293
  | Theme | Use |
275
294
  | --- | --- |
@@ -293,6 +312,9 @@ Version **1.10.0** contains **20 themes**. Gruvbox Dark, Nord, Rosé Pine base d
293
312
  | `osdy-pi-catppuccin-mocha` | Catppuccin Mocha dark palette. |
294
313
  | `osdy-pi-matrix` | OpenCode Matrix dark palette. |
295
314
  | `osdy-pi-lucent-orange` | Lucent Orange dark palette with terminal-background passthrough. |
315
+ | `osdy-pi-spider-man-classic` | Classic Peter Parker: night-blue panels, red accents, and web-white text, with blue links and distinct green success/yellow warning indicators. |
316
+ | `osdy-pi-miles-morales` | Miles Morales: black and charcoal panels with intense red accents, pale text, and distinct green success/yellow warning indicators. |
317
+ | `osdy-pi-spider-verse` | Spider-Verse: dark violet panels, red, electric-blue, and magenta highlights, with readable syntax and distinct green success/yellow warning indicators. |
296
318
 
297
319
  Osdy Pi preserves your selected Pi theme when it enables, reapplies, or disables its UI. Choose any theme in Pi:
298
320
 
@@ -350,7 +372,8 @@ The enabled state, editor mode, working-tree visibility preference, header, and
350
372
  | Main | `/osdy-pi` |
351
373
  | Main | `/osdy-pi enable\|disable\|on\|off\|status` |
352
374
  | Accounts | `/osdy-account` |
353
- | Session TODO (starting in 1.5.0) | `/todos` (read-only) |
375
+ | TODO selection | `/osdy-pi todo on\|off\|status` |
376
+ | Session TODO (opt-in, starting in 1.5.0) | `/todos` (read-only) |
354
377
  | Header | `/osdy-pi header osdy-theme\|neon\|status` |
355
378
  | Mascot | `/osdy-pi mascot current\|bts\|status` |
356
379
  | Editor | `/osdy-pi editor auto\|extended\|simple\|on\|off\|toggle\|status` |
@@ -0,0 +1,27 @@
1
+ # Assistant marker artwork
2
+
3
+ `assistant-mascot.png` is the landing's raccoon, exported as a transparent 360 × 350 RGBA PNG. It is not the unrelated package cover image.
4
+
5
+ ## Reproduce
6
+
7
+ With the existing landing dependencies and Chromium installed, run from Osdy-Pi:
8
+
9
+ ```sh
10
+ node extensions/osdy-pi/assets/assistant-mascot-export.mjs /path/to/osdy-pi-landing
11
+ ```
12
+
13
+ The exporter reads the landing without modifying it and writes only the adjacent SVG and PNG. No runtime browser or font dependency is added.
14
+
15
+ ## Fidelity and origin
16
+
17
+ - Source: `osdy-pi-landing/src/components/Mascot.tsx` and `src/data/mascot.ts`, adapted from Osdy-Pi's `HTML_MASCOT`, map, and tones. Artwork: MIT, Copyright (c) 2026 Osdy.
18
+ - Geometry: original 360 × 350 viewBox, 6px character pitch, 10px row pitch, 9px first baseline, and last-three-occupied-character edge selection.
19
+ - Font: landing's `@fontsource/jetbrains-mono` Latin normal 400 WOFF2, loaded into the export page only; Chromium waits for the font before capture. The retained SVG references the font family but does not redistribute font bytes. JetBrains Mono is licensed under SIL OFL 1.1 (see the installed font package's LICENSE).
20
+ - Palette: body `#F9F7F2`, `#F0E5D7`, `#C7B4A1`, `#7D6F67`, `#2A2321`; edge p/c/v `#22d3ee`/`#a78bfa`/`#67e8f9`. These resolve the default landing theme (`themes[0]`, Osdy Pi New). Alternate landing themes can have pink edges; this fixed asset intentionally preserves the default, not all theme variants.
21
+ - Generated artifacts: SVG source and PNG, captured headlessly with Playwright Chromium at device scale 1 and no background.
22
+
23
+ ## Terminal boundary
24
+
25
+ The assistant marker uses Pi's real `Image`, at most 3 columns × 2 rows. At default 9 × 18 pixel cells it reserves 3 × 1 on Kitty and 3 × 2 on iTerm2. iTerm2 sends `height=auto`, so width is additionally bounded using current cell geometry. Narrow widths down to 2 columns remain safe; invalid widths render an empty row.
26
+
27
+ PNG preview was inspected. Automated protocol tests are not a live Kitty/iTerm2 visual check; legibility at tiny terminal size still requires the user's trial. Emoji fallback and stored marker lifecycle are unchanged.
@@ -0,0 +1,43 @@
1
+ /* global document */
2
+ import process from 'node:process';
3
+ import { Buffer } from 'node:buffer';
4
+ import { URL } from 'node:url';
5
+ import console from 'node:console';
6
+ // Run: node extensions/osdy-pi/assets/assistant-mascot-export.mjs /path/to/osdy-pi-landing
7
+ // Uses existing landing dependencies only; writes only the adjacent SVG and PNG.
8
+ import { readFileSync, writeFileSync } from 'node:fs';
9
+ import { createRequire } from 'node:module';
10
+ import { resolve } from 'node:path';
11
+ import ts from 'typescript';
12
+
13
+ const landing = resolve(process.argv[2]);
14
+ const require = createRequire(`${landing}/package.json`);
15
+ const { chromium } = require('@playwright/test');
16
+ const source = readFileSync(`${landing}/src/data/mascot.ts`, 'utf8');
17
+ const compiled = ts.transpileModule(source, { compilerOptions: { module: ts.ModuleKind.ESNext } }).outputText;
18
+ const { mascot, toneMap, mascotColors } = await import(`data:text/javascript;base64,${Buffer.from(compiled).toString('base64')}`);
19
+ // Default landing theme: themes[0], Osdy Pi New. Resolve CSS variables explicitly.
20
+ const colors = { ...mascotColors, p: '#22d3ee', c: '#a78bfa', v: '#67e8f9' };
21
+ const font = readFileSync(`${landing}/node_modules/@fontsource/jetbrains-mono/files/jetbrains-mono-latin-400-normal.woff2`).toString('base64');
22
+ const rows = mascot.map((row, y) => {
23
+ const edges = [...row].map((ch, x) => ch === ' ' ? -1 : x).filter(x => x >= 0).reverse().slice(0, 3);
24
+ const spans = [...row].map((ch, x) => {
25
+ const edge = edges.indexOf(x);
26
+ const tone = edge >= 0 ? ['p', 'c', 'v'][edge] : toneMap[y]?.[x];
27
+ return `<tspan x="${x * 6}" fill="${colors[tone] ?? colors.m}">${ch}</tspan>`;
28
+ }).join('');
29
+ return `<text y="${y * 10 + 9}" font-family="JetBrains Mono, monospace" font-size="10" xml:space="preserve">${spans}</text>`;
30
+ });
31
+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="360" height="350" viewBox="0 0 360 350">\n${rows.join('\n')}\n</svg>\n`;
32
+ const browser = await chromium.launch({ headless: true });
33
+ try {
34
+ const page = await browser.newPage({ viewport: { width: 360, height: 350 }, deviceScaleFactor: 1 });
35
+ await page.setContent(`<style>html,body{margin:0;background:transparent}@font-face{font-family:'JetBrains Mono';src:url(data:font/woff2;base64,${font}) format('woff2');font-weight:400}</style>${svg}`);
36
+ await page.evaluate(async () => { await document.fonts.load('10px "JetBrains Mono"'); await document.fonts.ready; });
37
+ if (!await page.evaluate(() => document.fonts.check('10px "JetBrains Mono"'))) throw new Error('Font failed to load');
38
+ writeFileSync(new URL('./assistant-mascot.svg', import.meta.url), svg);
39
+ await page.locator('svg').screenshot({ path: new URL('./assistant-mascot.png', import.meta.url).pathname, omitBackground: true });
40
+ console.log('Exported transparent 360×350 PNG with loaded JetBrains Mono 400 and default landing edge colors.');
41
+ } finally {
42
+ await browser.close();
43
+ }