# ASCII Art Generator Upgrade Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Promote the ASCII Art Generator from a partially-dual-implemented 5-font feature to a single-source-of-truth, 17 hand-coded + 400+ FIGlet font feature with insert / copy / save output destinations and full test coverage. **Architecture:** Extract the algorithm from `renderer.js:5942-6736` into a pure `src/main/AsciiArt.js` module that mirrors the `DocQA`/`DailyNotes` pattern. Hand-coded font tables move into `src/main/AsciiArt.fonts.js`; templates into `src/main/AsciiArt.templates.js`; `figlet` becomes a lazy-loaded `src/main/AsciiArt.figlet-adapter.js`. A new `src/renderer/ascii-controller.js` drives the existing standalone `src/ascii-generator.html` window. The in-app modal (`#ascii-art-dialog`, `showASCIIGenerator*`, `preload.js` receive channels) is deleted; the standalone window remains the only path. `electron-store` `ascii:lastFont` (via the existing `store` JSON helper in `main.js:267-289`) remembers the last-used font. **Tech Stack:** Electron 41.10.7 · electron-builder 26.15.3 · vanilla CommonJS · `figlet@^1.8.0` (pure JS) · Jest + jsdom · ESLint flat config · Prettier (2-space, single quotes, semicolons, 100-col). **Spec:** docs/superpowers/specs/2026-09-14-ascii-art-upgrade-design.md ## Global Constraints - Electron 41.10.7, electron-builder 26.15.3 - Vanilla JS, no bundler. Pure modules in `src/main/`, IPC wired in `src/main.js` - Preload allow-list at `src/preload.js` for any new IPC channels (`invoke` reuses `ALLOWED_SEND_CHANNELS`; `on` uses `ALLOWED_RECEIVE_CHANNELS`) - Tests: Jest + jsdom. Run `npm test`, `npm run lint`, `npm run format:check` - Single ASCII art path: standalone `BrowserWindow` only; in-app modal `#ascii-art-dialog` and dead channels `show-ascii-generator*` deleted - New dep: `figlet` npm package (latest 1.x, pure JS, ~400 KB unpacked) - Hand-coded font count: 17 (5 existing + 12 new) — big, small, lean, slant, isometric1-4, 3-d, 3x5, ansi-shadow, calvin-s - New settings key: `ascii:lastFont` (via the existing `store` JSON helper in `main.js:267-289`) - Output destinations: insert at cursor (existing, fenced code block wrapper) + copy to clipboard + save to file - The standalone `src/ascii-generator.html` (751 lines) keeps its layout but its controller moves to a new `src/renderer/ascii-controller.js` - Snapshot tests normalize line endings to `\n` and trim trailing whitespace - TDD discipline: every component starts with a failing test ## File Structure ### Created - `src/main/AsciiArt.fonts.js` — 17 hand-coded font tables (`HAND_CODED_FONTS`). - `src/main/AsciiArt.templates.js` — 19 named ASCII templates (`ASCII_TEMPLATES`). - `src/main/AsciiArt.figlet-adapter.js` — Lazy `require('figlet')` + `fontsSync()` cache + `textSync(text, font)` wrapper + structured error. - `src/main/AsciiArt.js` — Pure orchestrator: `generate({ text, font, options })`, `listFonts()`, `getFontMeta(id)`. - `src/renderer/ascii-controller.js` — Renderer-side controller for the standalone window. - `tests/main/ascii-art.test.js` — Unit tests for `generate` / `listFonts` / `getFontMeta` / figlet adapter. - `tests/main/ascii-art.fonts.test.js` — Snapshot tests per hand-coded font (HELLO + edge cases). - `tests/main/ascii-art.templates.test.js` — Snapshot tests per template. - `tests/main/ascii-art.figlet-adapter.test.js` — Adapter tests with `figlet` mocked. - `tests/preload-ascii.test.js` — Allow-list assertions for new `ascii:*` channels. ### Modified - `package.json` — Add `"figlet": "^1.8.0"` to `dependencies`. - `src/main.js:4005-4010` — Add `const AsciiArt = require('./main/AsciiArt');` after DocQA require. - `src/main.js` (after line 5611, near other recent IPC handler blocks) — Register `ascii:generate`, `ascii:list-fonts`, `ascii:get-font-meta`, `ascii:save`, `ascii:copy`, `ascii:last-font` handlers. - `src/preload.js:98-99,294-296` — Delete dead receive channels `show-ascii-generator` and `show-ascii-generator-window`. Add new invoke channels `ascii:generate`, `ascii:list-fonts`, `ascii:get-font-meta`, `ascii:save`, `ascii:copy`, `ascii:last-font` to `ALLOWED_SEND_CHANNELS`. Add `generators.ascii.*` namespace helpers. - `src/renderer.js:2188` — Delete `const asciiModal = new ModalManager('#ascii-art-dialog');`. - `src/renderer.js:2202` — Delete `asciiModal` entry from `window.modals`. - `src/renderer.js:5942-6736` — Delete entire in-app modal block. - `src/index.html:812-1028` — Delete entire `#ascii-art-dialog` modal markup. - `src/ascii-generator.html` — Rewrite inline ``) **Interfaces:** - Consumes: `src/renderer/ascii-controller.js` - Produces: a standalone window that loads the controller, renders all 17+ fonts, and exposes Insert / Copy / Save. - [ ] **Step 1: Replace the inline FONTS/TEMPLATES/BOX_STYLES + generation script with a controller script tag.** In `src/ascii-generator.html`, remove the entire `` (lines ~364-749). Replace with: ```html ``` - [ ] **Step 2: Replace the text-mode font-style ` ``` - [ ] **Step 3: Add a warning element above the preview.** After the `
Preview
` line, insert: ```html ``` - [ ] **Step 4: Extend the footer buttons.** Replace the footer block (lines ~359-362) with: ```html ``` - [ ] **Step 5: Verify the file still parses.** Open the file in a browser (or `cat src/ascii-generator.html | head -10` to sanity-check the head section) — must still have the `` and the closing ``. - [ ] **Step 6: Manual smoke.** Launch the app: `npm start` — Ctrl+Shift+A. Verify: (a) standalone window opens, (b) font picker lists 17+ entries, (c) preview updates on input, (d) Insert / Copy / Save each work end-to-end. - [ ] **Step 7: Commit.** `git add src/ascii-generator.html && git commit -m "feat(ascii-art): standalone window uses controller + searchable picker + copy/save/insert"` --- ### Task 11: Delete in-app modal markup and dead renderer code **Files:** - Modify: `src/index.html:812-1028` (delete the `#ascii-art-dialog` block) - Modify: `src/renderer.js:2188` (delete `const asciiModal = new ModalManager('#ascii-art-dialog');`) - Modify: `src/renderer.js:2202` (delete `asciiModal,` from `window.modals`) - Modify: `src/renderer.js:5942-6736` (delete the entire ASCII Art Generator block) **Interfaces:** - Consumes: nothing. - Produces: a `git grep` that returns zero hits for `showASCIIGenerator`, `textToASCII`, `createASCIIBox`, `getASCIITemplate`, `insertASCIIArt`, `hideASCIIGenerator`, `switchASCIIMode`, `loadASCIITemplate`, `generateASCIIPreview`, `ascii-art-dialog`, `asciiModal`, `show-ascii-generator`. - [ ] **Step 1: Delete the modal markup in `src/index.html`.** Remove lines 812-1028 inclusive (the entire `
…
` block). - [ ] **Step 2: Delete the `asciiModal` instantiation in `src/renderer.js`.** Remove line 2188 (`const asciiModal = new ModalManager('#ascii-art-dialog');`). - [ ] **Step 3: Delete the `window.modals.asciiModal` entry.** Remove line 2202 (`asciiModal,`). - [ ] **Step 4: Delete the in-app controller block.** Remove lines 5941-6736 inclusive (the entire ASCII Art Generator block, from the `// ASCII ART GENERATOR` header comment through the `ipcRenderer.on('show-ascii-generator', …)` listener and its preceding whitespace). - [ ] **Step 5: Verify zero residual references.** `git grep -nE "showASCIIGenerator|textToASCII|createASCIIBox|getASCIITemplate|insertASCIIArt|hideASCIIGenerator|switchASCIIMode|loadASCIITemplate|generateASCIIPreview|ascii-art-dialog|asciiModal|show-ascii-generator"` — must return no hits inside `src/`. - [ ] **Step 6: Verify the standalone path still works.** Run `npm start`. Ctrl+Shift+A still opens the standalone window; Tools → ASCII Art Generator (if present in menu) still works; Insert button wraps in fenced code block. - [ ] **Step 7: Commit.** `git add src/index.html src/renderer.js && git commit -m "refactor(ascii-art): delete in-app modal #ascii-art-dialog, controller, dead preload channels"` --- ### Task 12: Update README and run full validation **Files:** - Modify: `README.md:56` (ASCII Art Generator row) **Interfaces:** - Consumes: nothing. - Produces: README updated to reflect the new feature surface. - [ ] **Step 1: Update the feature row.** In `README.md`, change line 56 from: ``` - **ASCII Art Generator** - Create text banners and diagrams ``` to: ``` - **ASCII Art Generator** - 17 hand-coded fonts + 400+ FIGlet fonts; text banners, boxes, and templates; insert into editor, copy to clipboard, or save to file (Ctrl+Shift+A) ``` - [ ] **Step 2: Run the full test suite.** `npm test` — all tests pass (existing 800+ plus the ~40 new tests). - [ ] **Step 3: Run lint.** `npm run lint` — clean. - [ ] **Step 4: Run format check.** `npm run format:check` — if any files are mis-formatted, run `npm run format` and re-run the suite. - [ ] **Step 5: Run Linux build.** `npm run build:linux` — succeeds without native-binding surprises from `figlet`. (If running in CI, this is automatic.) - [ ] **Step 6: Final commit.** `git add README.md && git commit -m "docs(readme): ASCII Art Generator now 17 hand-coded + 400+ FIGlet fonts; insert/copy/save"` - [ ] **Step 7: Sweep for forbidden markers in changed files.** `git diff --name-only HEAD~12..HEAD -- 'src/main/AsciiArt*.js' 'src/renderer/ascii-controller.js' 'src/ascii-generator.html' 'src/preload.js' 'src/main.js' 'src/renderer.js' 'src/index.html' 'package.json' 'README.md' 'tests/main/ascii-art*.test.js' 'tests/main/ascii-art.fonts.test.js' 'tests/main/ascii-art.templates.test.js' 'tests/main/ascii-art.figlet-adapter.test.js' 'tests/preload-ascii.test.js' | xargs grep -nE 'TODO|FIXME|XXX|HACK|not implemented|placeholder|stub|for now|in a real app|mock data|hardcoded for demo|coming soon'` — must return zero hits (excluding pre-existing entries unrelated to this feature). --- ## Self-Review 1. **Spec coverage:** - Single implementation path — Task 11. - 17 hand-coded fonts (5 existing + 12 new) — Task 2. - figlet lazy load + cache — Task 4 (adapter) + Task 1 (dep). - Three output destinations — Task 9 (controller) + Task 10 (HTML buttons) + Task 6 (IPC handlers). - Comprehensive tests — Tasks 2, 3, 4, 5, 8 cover unit, snapshot, adapter, preload. - Pure module architecture — Tasks 2, 3, 4, 5 (all in `src/main/`, no Electron imports). - Last-used font persistence — Task 6 (`store.set('ascii:lastFont', …)`) + Task 9 (controller calls `api.lastFont`). - Error handling — Task 4 (`AsciiArtFigletError`) + Task 6 (`ascii:copy` catches, `ascii:save` returns `{ canceled }`) + Task 9 (showWarning). - Acceptance criteria — Task 12 sweep. 2. **Placeholder scan:** No `TODO` / `TBD` / `FIXME` / `placeholder` / `implement later` / `fill in` markers in the plan body. The single intentional `nope` literal in test names is a test-only sentinel for "unknown" inputs and is not a placeholder. 3. **Type consistency:** `generate`, `listFonts`, `getFontMeta`, `generateFiglet`, `listFigletFonts`, `loadFiglet`, `AsciiArtFigletError`, `HAND_CODED_FONTS`, `ASCII_TEMPLATES`, `getTemplate`, `store.get` / `store.set`, `ascii:generate` / `ascii:list-fonts` / `ascii:get-font-meta` / `ascii:save` / `ascii:copy` / `ascii:last-font`, `generators.ascii.{ listFonts, getFontMeta, generate, copy, save, lastFont }` — names match across tasks. 4. **Open gap flagged:** The integration test in spec §Testing #5 (spawn `ascii-generator.html` in headless Electron, assert no console errors) is intentionally skipped — it requires a display server and is environmental. The spec marks this test as "Skip if no display available"; in CI this would be a manual smoke check (Task 10 Step 6) rather than a Jest test.