← Back to Whittl
WHITTL v2.3.0 RELEASE NOTES
April 2026
Drop a screenshot of any UI into chat and a vision-capable model rebuilds it as a native Python app. A Figma mock, a web app you want to run locally, a competitor's login screen, your own old tool you're tired of configuring. Every other AI coding tool outputs more web. Whittl uniquely converts design references into runnable software you own.
What you can drop in
- Any vision-capable model on OpenRouter. Claude (Opus, Sonnet, Haiku), GPT-4o / 4o-mini, Gemini 2.5 Pro / Flash / Flash-Lite, Llama 3.2 Vision, Pixtral, Qwen-VL, Gemma 3 27B. Uses standard OpenAI multimodal format with
image_url content parts.
- Claude direct backend via Anthropic's native image content blocks.
- Gemini direct backend via Google's multimodal parts format.
- Images flow through tool-use rounds, not just the first chat turn. The AI sees your image while it builds, not just when you send it.
- Preview before send. Attached images show inline with filename and size, removable, no silent bytes leaving your machine.
Flip the toggle in Preferences → AI Generation → Agent Mode and capable backends unlock a different runtime: the AI classifies intent inside the tool loop (no separate planner call), runs an unbounded loop up to 50 rounds for tier-S models, carries session memory across prompts, and gets a real bash tool for shell commands inside the project directory.
- Tier-S models unlock it fully. Claude Opus/Sonnet, GPT-4o, Gemini 2.5 Pro, Qwen3.5-Plus, DeepSeek V3.2. Tier-A gets 20 rounds. Weaker models default to capped behavior automatically.
- Session memory across prompts. Follow-ups like "now make that also work on startup" land on an agent that knows what "that" is. Resets on project switch.
- Hard safeguards underneath. Loop detection, read-only bailout, oscillation guard, and a 5-round hard cap still fire early on most real sessions. Agent Mode raises the ceiling; it doesn't remove the floor.
v2.2's iterating autofix was a power tool without a safety net. v2.3 adds four of them.
- Oscillation guard. Tracks an error fingerprint over a sliding 6-entry window. If the same error pattern trips 3+ times, autofix stops cleanly with a message. No more A→B→A loops that burn 12 rounds of tokens.
- Hard round cap. 5 rounds, full stop. A competent fix usually lands in 1-3 rounds. More than 5 means the model is flailing. The cap produces a clear message pointing the user toward a stronger model or a manual edit.
- Stop button that actually stops. The button now persists across rounds during an active cycle and genuinely cancels queued rounds. Previously it flipped to Generate between rounds, making it impossible to abort a runaway loop without force-quitting Whittl.
- Live transparency. Status bar cycles through descriptive captions ("Editing main.py...", "Checking syntax...", "Round 3/5...") instead of freezing on "Ready." Chat shows System banners when autofix starts and finishes so you know what's happening.
The OpenRouter Models dialog used to show opaque S/A/B tier letters based on made-up grading. v2.3 replaces them with concrete capability chips you can actually verify.
- Four chips per model:
[Tools], [Thinks], [Long], [Vision]. Each chip either lights up or it doesn't based on OpenRouter's own supported_parameters metadata.
- New filter dropdown: All / ★ Favorites / Supports Tools / Vision / Long context / Cheap / Premium.
- New sort order: favorites first, then by capability count, then alphabetical. The best models you care about are always at the top.
- Free-tier model enrichment. Context windows and capability chips now populate correctly for
openrouter/auto and the curated free models, not just paid ones.
- Character-by-character chat. Chunk batcher dropped from 80ms to 30ms. Chat feels typewriter-smooth instead of chunky.
- Sub-line code updates. Editor delta thresholds tightened from +20/+50 chars to +5/+15. Code streams in without 5-line jumps.
- Live multi-file. When the AI emits
### FILE: name.py markers mid-stream, Whittl now routes each file's content to its own tab as it's being written. First file appearance auto-focuses that tab, then releases focus so you can click around freely.
- Duplicate marker handling. grok-4.1-fast and similar models sometimes emit a stray second
### FILE: marker for a file they already finished. The parser now keeps the larger valid block instead of last-write-wins clobbering. Fixes a class of IndentationErrors where a 125-char orphan fragment overwrote a 5,900-char real file.
v2.2's architecture was solid. v2.2's interface was scattered Segoe UI and emoji. v2.3 makes Whittl look like a single designed product.
- Typography. Cormorant Garamond italic for section headers, dialog titles, and chat speaker labels. Inter for body and code. Registered via
QFontDatabase.addApplicationFont so it works without system-install.
- Palette. Navy / cream / tan / copper. Single source of truth in
core/brand_colors.py.
- Dark theme restraint. Neutral navy-slate base (#1A2028 to #3A4756). Warm tan accent reserved for active and primary states only. Cursor / VS Code level of visual calm for long sessions.
- Light theme four-tone. Four distinct cream tones (#FFFCF3, #FAF4E5, #F0E6D2, #E8DCC0) so the chat, editor, and sidebar read as separate regions. Copper as the single primary accent.
- Phosphor Duotone icons everywhere. 125+ SVGs covering buttons, menus, file types, project sidebar, and tabs. No more emoji.
- Real avatars in chat. Whittl knife-and-wood mark for AI messages, Phosphor silhouette for user messages. Avatar rows replace text-drawn dividers between messages.
- Flat file tabs with line counts.
main.py (147). Dirty-state dot. Close X appears on hover, not always.
- Persistent bottom status bar. Generation time, round count, token total, and cost live where you expect them instead of being shuffled into chat messages.
- Per-project icons. 125+ Phosphor Duotone icons auto-matched from project name keywords (weather, music, photo, game, puzzle, chat, book, database, fitness, recipe, and 20+ more). Right-click any project to override with a curated picker.
A startup migration in previous Whittl builds auto-appended :free to any saved OpenRouter model ID whose free variant was in the curated free list. So a paid user who picked a paid model (say, a 1M-context Qwen) would find their setting silently rewritten to the rate-limited free tier on the next launch.
The migration was trying to help users who wanted the free tier but didn't know the :free convention. Problem: same bare ID, two different intents, no way to tell them apart. Removed the migration entirely. Paid bare IDs now round-trip unchanged. Three regression tests guard it.
Heavy focus on safeguards that matter.
- Oscillation guard, hard round cap, stop-button persistence
- Theme-flip regression harness (walks widget tree post-flip checking for opposite-theme colors leaking through)
- Streaming parser edge cases including the duplicate
### FILE: marker regression
- OpenRouter favorites race between the background update-checker and the favorites writer
- Project icon availability (every keyword rule cross-checked against shipped SVGs at CI time)
- Test suite isolation (tests that write settings no longer corrupt your real
~/.whittl/ files)
- Chat avatar persistence across new-project creation and theme flips
- Vision pipeline multimodal format across OpenRouter, Claude direct, Gemini direct
- Just launch v2.3. No data migration needed. Projects, settings, skills, and OpenRouter favorites all carry forward unchanged.
- The :free downgrade migration is gone. If your OpenRouter model ID was silently rewritten to
:free by a previous build, you'll need to pick your paid model again. From v2.3 onward the setting sticks.
- Skills you've taught Whittl carry forward. The skills system now reads from
~/.whittl/skills/ the same way it did. v2.4 will add Claude-compatible paths alongside.
- Itch.io users: v2.3.0 is the final version on itch.io. Future updates ship through the in-app updater. Click Help → Check for Updates after upgrading to subscribe to future releases directly.