···5656- CLI loads bundled default cart or a provided `.lua` path.
5757- Audio capture foundation: `cpal` input stream with mono downmix into a lock‑free ring buffer (8192 samples); CLI flags `--list-audio`, `--audio-device`, `--audio-vu`, `--audio-disable`; simple VU feedback prints peak dBFS once per second.
5858 - FFT: 2k R2C (`realfft`) on tick thread; maintains raw/smoothed/normalized buffers with peak tracking; `--debug-fft` throttled print; Lua `fft/ffts/fftr/fftrs` wired with C-identical clamping/sum semantics; headless tests added; simple cart at `assets/fft_test.lua`.
5959+ - Screenshots: CLI supports `--screenshot <path> [--screenshot-scale N] [--screenshot-frame N]` and `--headless` offscreen capture. In windowed mode, F12 saves to `./screenshots/scr-YYYYmmdd-HHMMSS.png` without exiting.
59606061**Near-Term Backlog**
6162- FFT implementation (cpal + realfft) per `docs/specs/audio_fft_vqt.md` (see Implementation TODOs section); add headless tests and Lua `fft/ffts/fftr/fftrs`.
···99100 - Added docs index at `docs/README.md`, architecture/testing pages, and ADRs.
100101 - Added detailed testing docs (`docs/testing/strategy.md`) and a test catalog (`docs/testing/test_catalog.md`).
101102 - 2025-08-27:
103103+ - Screenshots completed:
104104+ - CLI flags implemented: `--screenshot`, `--screenshot-scale`, `--screenshot-frame`, `--headless`.
105105+ - Windowed one-off capture wired; exits after saving when path is provided.
106106+ - F12 hotkey saves to `./screenshots/scr-YYYYmmdd-HHMMSS.png` (auto-creates directory).
107107+ - Headless renderer: draws editor UI/code view or runs cart ticks offscreen, then saves.
108108+ - Tests: added `tests/screenshot_smoke.rs` to verify scaled PNG dimensions; extended util image tests.
109109+ - Hygiene verified: `cargo fmt`, `cargo clippy --all-targets --all-features -- -D warnings` clean; `cargo test` all green.
102110 - Implemented audio capture with `cpal`: device selection/listing, mono downmix, ring buffer.
103111 - Added CLI: `--list-audio`, `--audio-device`, `--audio-vu`, `--audio-disable`.
104112 - Integrated a 1s VU peak readout for manual verification.
···116124 - Memory map clarity: replaced magic numbers with named constants; documented screen nibble packing.
117125 - Bit manipulation tests: added round-trip tests for 1/2/4/8‑bit ops, VRAM screen boundary, and unaligned 4‑bit sequences (nibble order).
118126 - Lua runner quiet mode: added `--quiet` flag and a global switch; suppresses once-only init/missing TIC and TIC/BOOT error prints for headless runs.
127127+ - Editor (Phase 0 + Phase 1 partial):
128128+ - Added `--editor` flag to launch a framebuffer UI with TIC‑80‑style chrome and tabs; top‑bar buttons auto‑size and center labels.
129129+ - CODE view: rope‑backed buffer (ropey), read‑only viewport rendering, gutter with line numbers, arrow‑key caret navigation with auto‑scroll.
130130+ - Caret: TIC‑style red box with 1px drop shadow; glyph under caret drawn dark to simulate inversion; aligned to a true 6×8 cell grid (fixed‑width glyphs render only left 6 columns, advance 6 px).
131131+ - Tests: editor smoke (draw + tab switch), code viewport rendering (gutter/text pixels).
132132+ - Kept clippy/tests green.
119133 - Verified hygiene: `cargo clippy --all-targets --all-features -D warnings` is clean; `cargo test` all green; ran `cargo fmt`.
120134 - Tightened clippy setup (pedantic/nursery/cargo) with pragmatic allows:
121135 - Crate-level allows for TIC-style APIs and numeric DSP: `many_single_char_names`, `too_many_arguments`, `similar_names`, selected numeric cast lints, and multiple crate versions.
···131145- Start here: `docs/README.md`
132146- Roadmap: `docs/roadmap/overview.md`, `docs/roadmap/gui_first.md`
133147- Specs: `docs/specs/memory_map.md`, `docs/specs/lua_api_parity.md`, `docs/specs/graphics.md`, `docs/specs/audio_fft_vqt.md`
134134-- Architecture: `docs/architecture/workspace.md`, `docs/architecture/runtime.md`
148148+ - Architecture: `docs/architecture/workspace.md`, `docs/architecture/runtime.md`, `docs/architecture/screenshot_plan.md`
135149- Testing: `docs/testing/strategy.md`, `docs/testing/frame_hashes.md`
136150- ADRs: `docs/adr/0001-winit-pixels.md`, `docs/adr/0002-mlua-lua54-compat.md` (superseded), `docs/adr/0003-lua53-with-compat.md`
+16
docs/README.md
···6060- Options:
6161 - `-h, --help`: Show help and exit.
6262 - `--quiet`: Suppress once-only warnings and Lua BOOT()/TIC() error prints (useful for headless runs).
6363+ - `--editor`: Launch the livecoding editor UI (CODE/CONSOLE tabs) rendering in the 240×136 framebuffer.
6364 - `--list-audio`: List input audio devices and exit.
6465 - `--audio-device <SUBSTR>`: Select input device by substring match (case-insensitive).
6566 - `--audio-disable`: Disable audio capture and analysis.
···7475## Debug Flags (Audio Analysis)
7576- `--debug-fft`: First 16 FFT bins (smoothed normalized).
7677- `--debug-fx`: FX timings plus ring stats (dp/ovf/underrun/cons), EMA samples/tick, estimated occupancy.
7878+7979+## Editor (Livecoding)
8080+- Launch with `--editor` to open the framebuffer UI with CODE/CONSOLE tabs.
8181+- CODE view:
8282+ - Rope-backed text buffer for the loaded cart code (read-only viewport initially).
8383+ - Monospace grid 6×8 per cell (fixed-width glyphs render left 6 columns and advance 6 px).
8484+ - Gutter with 1-based line numbers; arrow keys move the caret; viewport auto-scrolls.
8585+ - Caret matches TIC‑80 style: red box slightly larger than the glyph, with 1 px drop shadow; underlying glyph drawn dark to simulate inversion.
8686+- Roadmap: see `docs/roadmap/editor_livecoding.md` for phases (editing, undo/redo, colorizer, find, console, hot reload).
8787+8888+## Screenshots
8989+- Save during run: press F12 to write to `./screenshots/scr-YYYYmmdd-HHMMSS.png` (respects `--screenshot-scale` if set).
9090+- One-off capture: `--screenshot out.png [--screenshot-scale 3]` (exits after saving).
9191+- Headless capture: `--headless --screenshot out.png [--screenshot-frame 60]`.
9292+- Plan: see `docs/architecture/screenshot_plan.md`.
+34
docs/architecture/editor.md
···11+# Editor Architecture (CODE/CONSOLE)
22+33+The editor renders a TIC‑80‑style UI inside the 240×136 framebuffer and handles input from `winit`. No external widget toolkit is used; this keeps rendering fully deterministic and testable.
44+55+## Rendering
66+- Top bar (12 px tall): tabs and buttons drawn with existing gfx primitives; button widths are computed from label length (ADV=6) and padding.
77+- Panels:
88+ - CODE: text viewport + gutter.
99+ - CONSOLE: placeholder; a ring buffer model will feed this view.
1010+- Integer scaling: `winit + pixels` provides window scaling; mouse coordinates are unprojected to 240×136 for hit testing.
1111+1212+## Text Model
1313+- Rope: `ropey` stores the cart source; enables fast inserts/deletes and stable indices.
1414+- Viewport: maps `scroll_line`/`scroll_col` to rows/cols on screen; uses a 6×8 monospace grid.
1515+ - Fixed‑width glyphs draw only the left 6 columns of the 8×8 font and advance 6 px.
1616+ - Gutter (left 24 px) shows 1‑based line numbers.
1717+1818+## Caret
1919+- TIC‑80 style: a red box slightly larger than the glyph cell with a 1px drop shadow; glyph under the caret is redrawn in dark to simulate inversion.
2020+- Position stays aligned to the 6×8 grid; auto-scrolling keeps caret visible.
2121+2222+## Input
2323+- Tabs and buttons: rectangle hit testing in framebuffer space.
2424+- Caret navigation: arrows (more keybinds to be added).
2525+- Editing (upcoming): insert/delete/backspace/newline; selection, undo/redo.
2626+2727+## Hot Reload and Cart I/O (upcoming)
2828+- CODE‑only `.tic` save; preserve other sections; hot reload swaps `LuaRunner` and gates on errors to keep last‑good.
2929+3030+## Testing Strategy
3131+- Pixel assertions for UI scaffolding and code viewport.
3232+- Buffer operation tests for text edits and viewport mapping.
3333+- Integration tests for `.tic` round‑trip and hot reload gating.
3434+
+45
docs/architecture/screenshot_plan.md
···11+# Screenshot Feature — Plan
22+33+Status
44+- Implemented in runner CLI and windowed modes; covered by unit + E2E tests.
55+66+Goals
77+- Enable saving a framebuffer screenshot during normal runs (hotkey) and when launched in headless mode.
88+- Provide deterministic, minimal-overhead capture for CI and debugging, with optional integer scaling.
99+1010+CLI
1111+- `--screenshot <path>`: capture once at first redraw (or selected frame) and exit.
1212+- `--screenshot-scale <N>`: integer scale for output PNG (default 1).
1313+- `--headless`: no window; render offscreen and save a screenshot.
1414+- `--screenshot-frame <N>` (optional): capture after N frames/ticks, then exit.
1515+- Hotkey: F12 saves to `./screenshots/scr-YYYYmmdd-HHMMSS.png` while running (does not exit).
1616+1717+Implementation Outline
1818+- Add a tiny image helper (`util::image`):
1919+ - `save_png_rgba(path, w, h, rgba)` using `png` crate.
2020+ - `scale_rgba_nn(src, w, h, scale)` (nearest-neighbor integer scale).
2121+- Windowed path:
2222+ - Track a `ScreenshotState { path, scale, frame_target, saved }` in `main.rs`.
2323+ - On `RedrawRequested`, after `fb.blit_to_rgba(frame)`, copy RGBA to Vec, optionally scale, save, and optionally exit.
2424+ - F12 hotkey builds a timestamped path and saves immediately using the same helper.
2525+- Headless path:
2626+ - Initialize framebuffer and state without `winit + pixels`.
2727+ - If `--editor`: render Editor UI + code viewport once; else load cart and run `TIC()` for `N` frames.
2828+ - Convert framebuffer to RGBA, optionally scale, and save.
2929+3030+Testing
3131+- Unit tests:
3232+ - Verify `scale_rgba_nn` expands a small RGBA image correctly.
3333+ - Encode to PNG in-memory and decode back to compare pixels.
3434+- Integration smoke:
3535+ - Headless render of a small scene (e.g., clear + draw) and save to a temp file; validate file existence and dimensions.
3636+ - E2E CLI tests invoke the binary with `--headless --screenshot` and decode the output PNG.
3737+3838+Docs
3939+- README CLI: add flags and a small “Screenshots” section with examples.
4040+- Test Catalog: reference screenshot unit tests.
4141+- Worklog (AGENTS.md): log the feature with brief UX.
4242+4343+Follow-ups
4444+- `--screenshot-delay <ticks>` convenience when not using frames.
4545+- Optionally embed metadata (frame/tick/flags) into PNG `tEXt`.
+10-9
docs/roadmap/editor_livecoding.md
···26262727Phase 0 — Shell + Layout
2828- [ ] Create `docs/` UI skin reference (palette, font, margins, tab geometry) (optional diagrams).
2929-- [ ] Add a minimal UI state model (active tab, focus, scroll positions).
3030-- [ ] Render top chrome, tabs, and placeholder panels into the framebuffer.
3131-- [ ] Wire tab switching and button hit‑testing (rect hit tests in 240×136 space).
3232-- [ ] Integrate integer scaling to window and mouse coordinate unprojection.
2929+- [x] Add a minimal UI state model (active tab, focus, scroll positions).
3030+- [x] Render top chrome, tabs, and placeholder panels into the framebuffer.
3131+- [x] Wire tab switching and button hit‑testing (rect hit tests in 240×136 space).
3232+- [x] Integrate integer scaling to window and mouse coordinate unprojection.
33333434Phase 1 — Text Engine + Editor Basics
3535-- [ ] Integrate a rope‑backed buffer (ropey) and load cart code into it.
3636-- [ ] Caret movement (arrows, home/end), insert/delete/backspace, newlines.
3535+- [x] Integrate a rope‑backed buffer (ropey) and load cart code into it.
3636+- [x] Caret movement (arrows) and auto-scroll; Home/End pending.
3737+- [ ] Insert/delete/backspace, newlines.
3738- [ ] Selection (shift+arrows), clipboard (Ctrl/Cmd+C/V/X), undo/redo (local stack for code buffer).
3838-- [ ] Horizontal/vertical scrolling; viewport mapping from text rows to framebuffer pixels.
3939-- [ ] Draw gutter (line numbers) and current line highlight.
3939+- [x] Horizontal/vertical scrolling; viewport mapping from text rows to framebuffer pixels.
4040+- [x] Draw gutter (line numbers).
40414142Phase 2 — Syntax + UX polish (minimal)
4243- [ ] Lightweight Lua colorizer (keywords, comments, strings, numbers) with palette colors.
···5758- [ ] Headless test: .tic round‑trip preserves non‑code bytes.
5859- [ ] Hot reload tests: failing edit keeps last‑good; fixing edit swaps runner.
5960- [ ] Console tests: trace() lines and error render path covered.
6060-- [ ] Add README section with usage and keybinds; link test carts and debug flags.
6161+- [x] Add README section with usage and keybinds; link test carts and debug flags.
61626263## Keybinds (initial)
6364- Save: Ctrl/Cmd+S
+12
docs/roadmap/todos_screenshots.md
···11+# TODO — Screenshot Feature
22+33+- [x] Write plan (`docs/architecture/screenshot_plan.md`).
44+- [x] Update README with CLI and usage.
55+- [x] Add `png` helper (`util::image`) with `save_png_rgba` and `scale_rgba_nn`.
66+- [x] CLI flags: `--screenshot`, `--screenshot-scale`, `--headless`, `--screenshot-frame`.
77+- [x] Windowed capture: implement one-off capture and F12 hotkey.
88+- [x] Headless capture: render offscreen and save; support `--editor` and cart run (up to frame N).
99+- [x] Unit tests: scaling + PNG encode/decode round-trip.
1010+- [x] Integration smoke: headless render + save to a temp file.
1111+- [x] Polish: create `./screenshots` dir and unique names; friendly errors.
1212+- [x] Docs: add “Screenshots” examples to README and Test Catalog.
+10-1
docs/specs/implementation_status.md
···3232- `.lua` loader: First CLI arg as a `.lua` path runs external script; fallback to bundled `assets/default.lua`.
3333- Window title: “rustic”.
3434- Audio capture scaffolding: `cpal` input stream (44.1 kHz if supported), stereo→mono downmix, lock‑free ring buffer (8192 samples); CLI flags to list/select devices and optional VU meter output.
3535- - VU behavior: Peak meter observed ~-180 dBFS at silence (BlackHole 2ch on macOS), responsive under Multi‑Output device routing.
3535+- VU behavior: Peak meter observed ~-180 dBFS at silence (BlackHole 2ch on macOS), responsive under Multi‑Output device routing.
3636+ - Screenshots:
3737+ - One‑off capture via `--screenshot <path>` with optional `--screenshot-scale <N>`; saves after frame `N` specified by `--screenshot-frame` (default first frame) and exits.
3838+ - Headless path `--headless` renders offscreen (editor UI or Lua cart ticks) and saves.
3939+ - Hotkey F12 saves to `./screenshots/scr-YYYYmmdd-HHMMSS.png` during windowed runs (no exit).
36403741Implemented (Analysis)
3842- FFT (2k):
···7175 - (none for VQT); whitening completed.
7276- Sprite flags
7377 - `fget`, `fset`.
7878+7979+Implemented (Editor — initial)
8080+- UI shell: top bar (tabs + buttons) rendered in framebuffer; integer-scaling window.
8181+- CODE view: rope‑backed buffer; read‑only viewport rendering; gutter; caret navigation (arrows) with auto‑scroll; TIC‑style caret (red box + shadow with inverted glyph); 6×8 cell grid.
8282+- CLI: `--editor` launches the editor.
74837584Test Coverage (summary)
7685- Framebuffer unit tests cover: cls/pix/line/rect/rectb/circ/circb/elli/ellib/tri/trib, clip behavior, OOB, print width/newlines, palette blit, triangle edge rules.
+2-1
docs/testing/strategy.md
···44- **Framebuffer unit tests:** Validate drawing primitives (`cls`, `pix`, `line`, `rect`, `rectb`), clipping, palette → RGBA mapping, and OOB behavior.
55- **Lua bridge tests:** Verify exposed APIs (`cls/pix/line/rect/print/rectb/clip`) and lifecycle (`BOOT`/`TIC`), and that Lua semantics (e.g., OOB `pix` read returns `nil`) are preserved.
66- **Deterministic snapshots:** Compute a simple hash of the 240×136 palette-index framebuffer (per tick) to assert determinism and enable future golden comparisons.
77+- **Headless E2E (CLI):** Invoke the binary with `--headless --screenshot` to generate PNGs and decode them to validate success and dimensions.
78- **Audio blocks (later):** Hash mixed audio blocks with tolerance windows for FP differences.
89- **Conformance carts (later):** Automate selected carts (headless) and compare traces/hashes to baselines.
910- **Fuzzing (later):** Fuzz `.tic` loader and selected APIs for robustness.
···4849**How To Run**
4950- Unit + Lua tests: `cd tic80_rust && cargo test`
5051- Clippy (treat warnings as errors): `cd tic80_rust && cargo clippy --all-targets --all-features -D warnings`
5252+- E2E screenshots only: `cd tic80_rust && cargo test -q e2e_headless_cli`
51535254**Future Additions**
5355- Expand primitives coverage (`circb/circ/elli/ellib/tri/trib`, `print` edge cases: scale>1 areas, baseline/advance, small font).
5456- Add frame-hash goldens for selected demo sequences (stable seeds and scripts).
5557- Introduce error-path tests for Lua type/arity mismatches and unknown APIs.
5658- Add input semantics tests (`key/keyp/btn/btnp/mouse`) with fixed-step repeat timing.
5757-
+11
docs/testing/test_catalog.md
···6969- Tests prefer headless framebuffer inspection over image baselines.
7070- Hashing uses FNV‑1a over VRAM palette indices for portability and stability.
7171- For manual carts, see `docs/testing/test_carts.md`.
7272+7373+## Editor Tests
7474+- `tic80_rust/tests/editor_smoke.rs`: UI shell draws and tabs switch on click; verifies top-bar pixels.
7575+- `tic80_rust/tests/editor_code_view_tests.rs`: CODE viewport renders gutter digits and text cells.
7676+7777+## Screenshot Tests
7878+- `tic80_rust/tests/screenshot_smoke.rs`
7979+ - `save_scaled_png_has_expected_dimensions`: Draw to framebuffer, encode/decode PNG in-memory; save a 3x scaled PNG to temp and verify dimensions.
8080+- `tic80_rust/tests/e2e_headless_cli.rs`
8181+ - `e2e_headless_default_cart_screenshot`: Runs the CLI binary with `--headless --screenshot` and decodes the PNG.
8282+ - `e2e_headless_editor_screenshot`: Runs with `--headless --editor --screenshot` and verifies the PNG.