WunderType
Updates

What's new.

All updates and improvements for WunderType.

What's New

v1.6.3

Calmer Progress Pill

A calmer progress indicator and a refreshed Settings window.

  • A quieter progress pill — while a correction runs, a small indicator now appears right next to your cursor, and only when there’s actually a wait. Quick corrections stay completely out of your way, and a checkmark confirms when the text is ready
  • Clear feedback when a model is loading — if an on-device model needs a moment to warm up, the indicator now tells you instead of just spinning, so a slow first correction never looks stuck
  • Refreshed Settings — every section now has a clear, labeled heading, making the General, Prompts, Personalization, and About tabs easier to scan at a glance

Replaced the ModernCorrectionHUD + ModernCorrectionHUDHost + CursorSpinnerHost trio with a single adaptive CorrectionPillHUD/CorrectionPillHost: one content-sized pill anchored just below-right of the cursor that owns the whole correction lifecycle (working spinner, escalation to a wait message, success checkmark, error text).

IMPROVE

The pill now stays hidden behind a 300ms grace period so fast corrections never flash any UI, and only escalates to a 'loading the model' explanation after 6 seconds (raised from 4) — cloud providers get a generic 'Still working…' instead.

IMPROVE

Tightened the pill to the cursor (cursorGap 12→4pt) and made it more compact (mini spinner, footnote text/icons, reduced padding) so it takes up less of the screen and reads as less of a distraction.

IMPROVE

Rewired CorrectionCoordinator and StreamingCorrector onto the new pill host lifecycle (begin/noteResponseStarted/succeed/fail/dismiss), generation-gated so a stale correction can never resolve or dismiss a successor's pill; success only shows a checkmark when the pill is already visible.

IMPROVE

Added a CorrectionPillTests Swift Testing suite covering phase→content mapping, per-provider escalation messages, grace/escalation timing invariants, and panel-placement geometry (edge clamping and above-cursor flip).

NEW

Settings visual polish: a shared SettingsSectionHeader (accent-tinted, uppercase, SF Symbol) applied consistently across the General, Prompts, Personalization, and About tabs.

IMPROVE
v1.6.2

New On-Device Models

Fresh on-device models and an easier way to manage them.

  • Two new on-device models — Qwen3 4B joins the lineup as a multilingual specialist (200+ languages), and the new Granite 4.0 Micro replaces Granite 3.3 with a small, fast model that now preserves your Markdown formatting
  • Manage your downloads — a new list in Settings shows how much space each on-device model takes and lets you delete the ones you don’t need, so retired models never linger on your disk
  • Tidier prompt editor — when a custom instruction runs long, only the text box scrolls now, keeping the Save and Shortcut controls in place

Added Qwen3 4B Instruct-2507 (mlx-community/Qwen3-4B-Instruct-2507-4bit) to the On-Device model lineup — a non-thinking, multilingual (200+ language) model that passes the full MLX smoke suite (grammar, Markdown preservation, German language retention, short-input sizing).

NEW

Replaced Granite 3.3 2B with Granite 4.0 Micro (mlx-community/granite-4.0-h-micro-4bit) in SettingsManager.availableMLXModels: a 3B hybrid model that preserves Markdown where the 2B stripped it. Users who had Granite 3.3 selected migrate to the Phi-4 Mini default via the existing availableMLXModels fallback in the mlxModel getter.

IMPROVE

New Manage Downloads UI in MLXProviderSettings: a disclosure listing every on-device model on disk (including orphans removed from the picker) with its size and a confirmation-gated Delete. Backed by new MLXService helpers downloadedModelIDs(), modelDiskSizeBytes(), deleteDownloadedModel(), and modelID(fromCacheDirectoryName:).

NEW

deleteDownloadedModel() unloads the resident model before removing its weights and refuses to delete a model that is mid-download (guards on _loadingModelID under the loader lock), preventing a corrupt half-downloaded cache.

IMPROVE

Fixed the custom prompt editor (PromptEditorSheet): a long instruction now scrolls inside a fixed-height text area instead of scrolling the whole sheet, and the sheet is taller so the Shortcut row is no longer clipped.

FIX
v1.6.1

Quick Mode Switching

Quicker mode switching and a more flexible local setup.

  • A shortcut for every mode — give Correct Grammar, Improve Writing, Make Concise, Make Formal, and Make Casual each their own keyboard shortcut, and jump straight to any of them from any app without changing your default mode first. They’re in the menu bar too, under “Correct As”
  • Custom Ollama server address — point WunderType at Ollama on a different port, through a proxy, or on another machine, instead of only the default local address
  • Ollama connection status — Settings now shows at a glance whether your Ollama server is reachable, so a wrong address is easy to spot

CorrectionCoordinator.performCorrection gained a `mode:` override that runs a one-shot correction in a specific built-in mode without persisting the user's selected default mode. Standard-correction semantics (single-line preservation, trailing-commentary stripping, silent no-changes) are preserved by keeping the gating on the `systemPrompt` parameter, so a mode-override correction never behaves like a custom prompt.

NEW

Per-mode global shortcuts — each CorrectionMode exposes a stable, no-default KeyboardShortcuts.Name (mode_*), so opt-in bindings never collide with the primary Cmd+Shift+G trigger. Handlers register once at launch (registerModeShortcuts); a new 'Correct As' menu-bar submenu and a per-mode Recorder section in the General settings tab surface them for discoverability.

NEW

The currently-selected default mode is filtered out of the Mode Shortcuts list and its dedicated binding is cleared — live on the Mode-picker onChange and defensively at launch — so it can't fire as an invisible duplicate of the global shortcut.

IMPROVE

SettingsManager.ollamaBaseURL + OllamaService.normalizedBaseURL — user-configurable Ollama server address (custom port, proxy, or remote host). Normalized at use time (prepends http:// when scheme-less, strips trailing slashes, empty falls back to the default) and read dynamically per request so a change takes effect without relaunch. Works on both the sandboxed App Store build and the DMG build.

NEW

GeneralSettingsTab — the Ollama provider section gains a Connected / Not reachable status line (parity with OpenAI and OpenRouter), re-checked on provider switch and ~1s after the server address is edited; the model picker clears when the server is unreachable.

IMPROVE

On-device stack validated against mlx-swift-lm 3.31.4 (build + full unit suite including real-model MLX smoke tests all green); not pinned, as Package.resolved is intentionally gitignored and the graph floats to latest-in-constraint at build time.

IMPROVE

Tests — ModeShortcutTests (per-mode shortcut-identifier stability, uniqueness, and namespacing) and OllamaBaseURLTests (URL normalization shapes: scheme prepend, trailing-slash strip, whitespace/empty fallback).

NEW
v1.6.0

Personalization

WunderType now learns how you like your text. When you keep changing a correction back, it remembers and stops making that change — and everything it learns stays on your Mac.

  • Learns what you change back — when you repeatedly undo the same correction, WunderType notices and stops making it. Switch it on in Settings → Personalization, or when you first set the app up
  • Everything stays on your Mac — it only ever remembers short word rules, never the text you write, and nothing is ever uploaded
  • See and control everything it learns — the new Personalization tab lists every rule in plain language. Turn any one off, edit it, delete it, or clear everything with a single click
  • Add your own rules — set exact word swaps that are always applied to every correction, independent of the AI model — for example, always change “utilise” to “use”
  • Off until you turn it on — you decide whether to enable it; nothing is learned unless you opt in

PersonalizationStore — new on-device adaptive-personalization layer (@MainActor) that learns the substitutions a user keeps reverting and re-applies their preferred form to future model output. Fully deterministic string processing — no model inference, no prompt injection — so it behaves identically across model sizes and cannot regress correction behavior. Durable state is a rules-only JSON profile in Application Support (short from→to token rules + counters, never user prose); raw correction text lives exclusively in an in-memory ring buffer that is cleared on quit.

NEW

Reversion detection is MAS-safe: rather than reading the document back after paste (Accessibility, blocked by the App Store sandbox), each new correction input is LCS-diffed against WunderType's own recent outputs, so the edits a user made are recovered from text the coordinator already holds. Gated by token similarity (>=0.6) and edit-cluster count (<=3) so rewrites are not mistaken for reversions; enforcement is whole-word ICU (\b) substitution, Unicode-aware. Learned rules promote at hitCount 2; manual rules apply immediately.

NEW

PersonalizationSettingsTab — new Settings tab that doubles as a transparency inspector: every learned and manual rule is listed in plain language with its status (Learning / Ready / Active), and each is individually toggleable, editable, and deletable, with a Clear Everything action. Users can also add manual always-on word-swap rules by hand.

NEW

Consent is tri-state (undecided/enabled/disabled) so the existing-user vs deliberately-off distinction is never conflated: new users choose on the onboarding Completion step (default off), existing users get a one-time disclosure card gated on its own marker, and .undecided behaves as off. All three CorrectionCoordinator hooks (observe/enforce/recordOutput) are a strict no-op unless the user has opted in.

NEW

CorrectionCoordinator — recordOutput runs only on the confirmed-write tail so corrections aborted mid-flight (cancellation, selection-changed) never enter the reversion-detection buffer as text that was never pasted.

IMPROVE

Settings — grouped Forms drop their gray canvas (scrollContentBackground hidden) and the window uses a full-size, transparent, title-less titlebar for a cleaner multi-tab surface; the Prompts tab clarifies that Writing Context is soft, model-interpreted guidance versus Personalization's exact, always-applied word swaps.

IMPROVE
v1.5.3

Stability & Performance

A stability and performance release — lighter on battery and memory, quicker to respond, and more careful with your data. No new features; everything you already use just runs better.

  • Lighter on battery — cloud providers (OpenAI and OpenRouter) now check their connection far less often in the background, with an instant status refresh the moment you open the menu
  • Lower memory use — On-Device AI now releases its model from memory automatically when your Mac runs low, then reloads it on your next correction
  • Your clipboard stays private — while a correction runs, the temporary clipboard contents are marked private so clipboard-manager apps don’t record your text
  • More reliable API keys — saving an API key is now safer, with no moment where it could be lost, and WunderType tells you if a save ever fails
  • Snappier menu — the menu bar menu and your custom prompts open faster
  • Cancellable downloads — you can now stop an On-Device model download that’s in progress

MLXService — critical memory-pressure monitor (DispatchSource .critical) unloads the resident On-Device model when macOS reports memory pressure and calls Memory.clearCache() so freed Metal buffers actually return to the OS; the model reloads from the disk cache on the next correction. Warning-level pressure is intentionally ignored (transient), and the prefix cache is not repopulated if the model was evicted mid-generation.

IMPROVE

AppDelegate health polling — cloud providers (OpenAI/OpenRouter) now background-poll every 5 min instead of 30s, sleeping in 30s slices so a provider switch is picked up promptly; polling pauses while a correction is in flight, and menu-open triggers a throttled one-shot refresh so the status line stays current. Fast 10→60s backoff retained when unhealthy.

IMPROVE

KeychainHelper — API-key saves use update-then-add (no delete-then-add window where a key could be lost), return a Bool, and surface failures via NotificationService; the legacy UserDefaults→Keychain migration deletes the plaintext copy only after a confirmed Keychain write.

FIX

ClipboardService.writeString — correction text written to the pasteboard is now marked org.nspasteboard.TransientType + ConcealedType so clipboard-manager apps don't record it during the copy → AI → paste cycle; target apps still read the plain string.

IMPROVE

AppDelegate menu + SettingsManager — the status menu is built once and only its dynamic items (connection status, Launch-at-Login) refresh per open, with the custom-prompt section rebuilt only on change notification; the decoded customPrompts array is cached behind an NSLock and invalidated by the setter, removing per-open JSON decode churn.

IMPROVE

MLXService — inference is now cancellable: the token stream checks Task.isCancelled and stops the GPU loop promptly instead of running to maxTokens, freeing the serialized container for the next correction; On-Device model downloads in Settings are cancellable too.

IMPROVE

OpenAICompatibleProvider — OpenAI and OpenRouter unified behind a shared provider protocol over OpenAIChatClient, collapsing duplicated REST/error-mapping code; ResponseArtifacts centralizes the stop-marker / preamble / filler-prefix lists shared by the MLX atomic cleaner and the streaming cleaner.

IMPROVE

AppDelegate Ollama warm-up loop — re-checks the keepOllamaLoaded toggle every 30s (warm-up cadence still 600s) so enabling it takes effect within ~30s instead of up to 10 minutes.

IMPROVE

MLXProviderSettings — model unload is gated on app state rather than a view's .onDisappear, so switching Settings tabs no longer evicts the loaded On-Device model; MLXModelLoadController extracts the load/cancel/progress state machine from the view.

FIX

Tests — added PromptPersistenceTests (legacy decode, round-trip, bundled starter-pack) and ProviderResponseParsingTests (ChatResponse/ErrorResponse decoding, extractResult rules, full mapError matrix for OpenAI + OpenRouter); seedDefaultPromptsIfNeeded split into a pure, tested seedingDecision plus an I/O wrapper.

NEW
v1.5.2

Download Progress & Cleanup

Downloading an On-Device model now shows real progress — and WunderType tidies up after itself.

  • Accurate download progress — when you download an On-Device model, the progress bar now climbs with a live percentage as the model downloads, instead of a spinner that looked stuck
  • Automatic disk cleanup — interrupted or cancelled model downloads no longer leave large temporary files behind; WunderType reclaims that space automatically

MLXDownloadProgressMonitor — new disk-based progress source for On-Device model downloads: sums the HuggingFace blobs directory plus the in-flight CFNetworkDownload temp file against each model's approximate size to drive a determinate progress bar. Works around swift-huggingface #48, where the library's own progress callback never reports incremental bytes for the large safetensors download on the LFS transport (didWriteData does not fire for the redirected CDN transfer), leaving the old bar stuck near 1% then jumping to 100%.

NEW

MLXService — uncached model downloads now report accurate progress via MLXDownloadProgressMonitor instead of the unreliable library callback; added modelCacheDirectory(_:) and downloadSizeBytes(for:) helpers, and a per-tick progress fraction capped at 99% until the model finishes loading.

IMPROVE

MLXService.cleanUpOrphanedDownloadTempFiles() — sweeps orphaned CFNetworkDownload_*.tmp files (multi-GB leftovers from cancelled/failed downloads) older than 10 minutes; runs at app launch (off-main) and before each new download.

FIX

SettingsManager — added approxDownloadBytes to each MLXModelInfo entry, used as the denominator for the download progress bar.

IMPROVE

MLXDownloadProgressMonitorTests — new Swift Testing suite covering the fraction cap math and the on-disk byte accounting (baseline orphan exclusion, temp-file matching, missing directories).

NEW
v1.5.1

Streaming Corrections

Watch your corrections appear. A new experimental option types the corrected text into place as the On-Device model writes it, instead of replacing your selection all at once.

  • Streaming corrections (experimental) — switch it on in Settings → General → Experimental, and On-Device corrections are typed into place live, as the model generates them
  • On-Device only — streaming uses the local MLX model for instant output; OpenAI, OpenRouter, and Ollama keep the standard one-shot replacement
  • Works in more apps than before — because the text is typed in like a keyboard, the streaming effect reaches apps where live editing previously wasn’t possible
  • Keep the target app in front while text streams in — switching away stops the correction, and ⌘Z undoes any partial text
  • Accessibility and polish — clearer VoiceOver descriptions throughout onboarding and provider setup, plus a lighter, smoother progress cursor while corrections run

StreamingCorrector — new experimental streaming correction engine: types the On-Device model's output into the focused control as it generates, paced at ~280 chars/sec, with prefix-hold (identical output never touches the document), focus-change abort, and clipboard-paste paragraph separators (typed newlines would send messages in chat apps)

NEW

TypingService — posts synthetic Unicode keystrokes (CGEvent keyboardSetUnicodeString) for sandbox-compatible insertion that also reaches Chromium/Electron apps which swallow Accessibility writes; waits for the correction shortcut's modifiers to be released before typing so keystrokes don't register as shortcuts

NEW

MLXService.streamCorrectText — ChatSession-based streaming returning an AsyncThrowingStream<String, Error>, with cancellation wired through the stream's onTermination; added readyContainerSnapshot and effectiveSystemPrompt helpers now shared with the atomic correctText path

NEW

StreamingResponseCleaner — incremental counterpart to MLXService.cleanResponse; filters thinking blocks, preamble lines, code fences, leaked EOS markers, and "Okay, here's the corrected…" filler prefixes before each chunk is emitted, since typed text cannot be un-typed

NEW

ResponseCommentary — shared detection of trailing model commentary ("(Note: I corrected…)"), now multilingual across the languages WunderType supports, used by both the atomic and streaming correction paths

NEW

Settings — "Stream corrections into the document" toggle (default off) under a new Experimental section in General, gated to Apple Silicon, with inline known-limitations guidance when enabled

NEW

CorrectionCoordinator — routes On-Device corrections through StreamingCorrector when streaming is enabled, with full outcome handling (completed, no-changes, cancelled, failed, aborted-mid-stream); the standard atomic path now also strips trailing model commentary for parity

IMPROVE

Tests — added ResponseCommentaryTests and StreamingResponseCleanerTests (21 cases) covering multilingual commentary detection, legitimate-parenthetical preservation, and incremental artifact/filler filtering across chunk boundaries

NEW

StreamingCorrector / TypingService — cancellation now propagates into the detached typing task (withTaskCancellationHandler + Task.isCancelled between keystroke chunks), so a focus change or user cancel aborts at the next keystroke boundary instead of finishing the in-flight slice into whatever app gained focus

IMPROVE

MLXPromptPrefixCache — extracted the prompt-prefix match into a pure reusablePrefixLength(promptTokens:cachedTokens:cachedOffset:) function, documented the precondition that take/store must run inside ModelContainer.perform, and added MLXPromptPrefixCacheTests (7 cases) pinning the reuse boundaries

IMPROVE

CursorSpinnerHost — coalesced the global mouse-move handler to reposition the progress spinner at most once per frame (~60 Hz) instead of a window-server setFrameOrigin round trip on every event for the full correction duration

IMPROVE

Onboarding — replaced the Int-keyed step state machine with a type-checked OnboardingStep enum (centralized progress mapping, no dead default branch); extracted shared APIKeyField (debounced OpenAI/OpenRouter key validation), CopyCommandRow, and AboutRow components; made the cloud provider cards data-driven

IMPROVE

Accessibility — added VoiceOver labels for the API-key validation status icons and the onboarding progress indicator; moved PromptEditorSheet's generation-error alert from a derived Binding(get:set:) to a Bool state flag

IMPROVE
v1.5.0

On-Device AI

On-Device AI is here. Correct text locally on your Mac — no Ollama, no API key, no internet after setup.

  • New \“On-Device\” AI provider runs models directly on your Mac’s GPU using Apple’s MLX framework
  • Download a model once, then correct text entirely offline — your text never leaves your Mac
  • Four models to choose from: Gemma 3 1B (fastest, ~0.7 GB), Granite 2B (fast, ~1.3 GB), Phi-4 Mini (balanced, ~2.0 GB), or Gemma 3 4B (best quality, ~3.0 GB)
  • Built for speed — the model warms up at launch and reuses its prepared instructions between corrections, so corrections start instantly
  • Zero setup — no external apps, no API keys. Select On-Device during onboarding and go
  • Requires Apple Silicon (M1 or later)

MLXService — new AI provider using Apple's mlx-swift-lm framework for in-process Metal GPU inference. Supports model download from HuggingFace, automatic caching, EOS token handling, and response cleaning (thinking blocks, code fences, preambles, leaked special tokens)

NEW

AIProvider — added .mlx ("On-Device") case wired through UnifiedAIService checkHealth, correctText, generateSystemPrompt, and showError

NEW

MLXSetupStep — new onboarding step with model picker, download with indeterminate progress spinner, model descriptions, error display, and download cancellation on Back

NEW

GeneralSettingsTab — On-Device provider section with model picker, descriptions, download/load/unload buttons, error display, and model-change state reset

NEW

ProviderSelectionStep — added On-Device provider card with cpu icon and Recommended badge, reorganized from 3-column to a 2x2 grid with compacted card spacing to fit the onboarding window; Intel Macs keep the original 3-card row with Ollama recommended

IMPROVE

AppDelegate — MLX model pre-loading at launch when On-Device is the configured provider, with stored task handle for cancellation on termination

NEW

MLXService.cleanResponse — shared post-processing strips thinking blocks, EOS tokens (Gemma, Phi, Llama, Qwen), code fences, and preamble lines from all model output

NEW

Smart prompt routing — each entry in the model list declares whether it needs a compact prompt; Gemma 3 1B receives a compact grammar-focused prompt, while 2B+ models receive the full commonRules system prompt (matching Ollama behavior)

NEW

Race-safe model loading — concurrent calls wait for in-flight loads instead of duplicating downloads, with error and cancellation propagation to waiters; waiters time out after 120 seconds for cached loads or 30 minutes for active downloads

IMPROVE

Apple Silicon gating — MLXService.isDeviceSupported hides the On-Device provider on Intel Macs (onboarding card and Settings picker), coerces a stored On-Device provider setting back to Ollama, and loadModel throws unsupportedDevice as a final guard; the app binary remains universal

NEW

MLX memory management — switching the AI provider away from On-Device (in Settings or onboarding) now frees the model weights, and loading a different model releases the previous one first instead of transiently holding two models in unified memory

FIX

Correction shortcut never triggers a silent multi-GB download — if the selected on-device model isn't cached (e.g. macOS purged the cache), the correction fails fast with a "Model not loaded" HUD instead of blocking for minutes while downloading

FIX

Corrections always run on the model selected in Settings — correctText reloads when the in-memory model diverges from the persisted selection, instead of silently serving from the previously loaded model

FIX

Model cache detection resolves the HuggingFace cache directory through the library's own CacheLocationProvider (sandbox- and env-var-aware) and requires config.json alongside the weights, so partial or interrupted downloads aren't mistaken for cached models

FIX

GeneralSettingsTab — model picker disabled while a load is in flight, loaded state verified against the service after each load, Reload Model actually unloads first (was a no-op), Unload guarded during loads, and cache checks moved off the SwiftUI render path

FIX

MLXSetupStep — restores the previously chosen model and loaded state when re-entering the step, no longer shows a download error when the user cancels via Back, and progress copy shows the model size and distinguishes downloading from loading

IMPROVE

SettingsManager — MLX model list converted from a tuple to a typed MLXModelInfo struct; a stored model ID that's no longer in the list falls back to the default instead of blanking the picker and dropping its EOS token configuration

IMPROVE

Download progress handler removed for uncached models — workaround for swift-huggingface issue #48 where @MainActor progress callbacks stall large downloads

FIX

CompletionStep — model summary row now shows the display name for all four providers including On-Device

IMPROVE

AboutSettingsTab — model name switch handles .mlx case, displaying the friendly model name from the available models list

IMPROVE

CorrectionCoordinator — hudMessage(for:) handles MLXServiceError with user-facing messages for model-not-loaded, download-failed, and correction-failed states

IMPROVE

OnboardingView — default provider changed from .ollama to .mlx for new users, step 9 added for MLX setup with correct progress bar mapping

IMPROVE

MLXPromptPrefixCache — KV-cache prefix reuse for on-device corrections: the templated system prompt (~150–430 tokens) is prefilled once and reused across corrections via token-level prefix matching, so each correction only prefills the selected text (~0.3–0.5s saved per correction). Mode switches, custom prompts, and model changes invalidate naturally; the stored cache is bounded to 512 tokens to keep idle memory predictable

NEW

MLXService — generation moved from ChatSession to a TokenIterator-based loop to enable prefix caching, with per-correction timing instrumentation (prefill/generation token counts, tok/s, and cached-token reuse) logged via os.Logger

IMPROVE

MLX warm-up — a tiny throwaway generation after model load (launch pre-load, Settings load button, onboarding download) compiles the Metal kernels and pre-builds the system-prompt prefix cache, so the user's first correction no longer pays that cost

IMPROVE

Gemma 3 1B no longer flips messy English input into German — the compact system prompt named languages ("German stays German, French stays French"), which primed the 1B model to translate. Reworded to name no languages; validated 0/4 German after vs 6/6 before, with German input still staying German

FIX

MLXService.cleanResponse — strips filler-prefixed preambles ("Okay, here's the corrected text:") that previously leaked past the preamble check into the corrected output

FIX

MLXCleanResponseTests — 27 unit tests covering EOS stripping (5 token formats), thinking block removal, code fence unwrapping, preamble stripping, and combined scenarios

NEW

MLXSmokeTests — parameterized integration tests running real inference across all shipped models, checking grammar correction, markdown preservation, German language preservation, and response sizing; plus a prefix-cache reuse test (asserts cached-token reuse with clean output, including identical resubmission) and a Gemma 1B language-stability regression test

NEW

SwiftPM dependencies added: mlx-swift-lm (3.0.0+), swift-huggingface (0.9.0+), swift-transformers (1.3.0+) with MLXLLM, MLXLMCommon, MLXHuggingFace, HuggingFace, and Tokenizers product dependencies

NEW
v1.4.3

Onboarding Improvements

Onboarding improvements based on user feedback.

  • You can now choose your keyboard shortcut during onboarding — no need to dig into Settings if the default conflicts with your apps
  • Ollama detection is faster and clearer — a live indicator shows it’s still checking when Ollama isn’t running
  • The completion screen no longer cuts off the button on smaller windows
  • Fixed the onboarding progress bar jumping when using the OpenAI provider path

ShortcutStep — new onboarding step between Accessibility and Completion that presents a KeyboardShortcuts.Recorder, letting users customize their global shortcut before finishing setup

NEW

OnboardingView — added progressStep computed property that maps all internal step numbers to correct logical positions; previously the OpenAI setup path (step 3) showed progress position 3 instead of 2, causing the progress bar to jump

FIX

OllamaSetupStep — added a visible ProgressView spinner and "Launch Ollama and it will be detected automatically" hint in the not-running state, so users know polling is active without navigating away

IMPROVE

OllamaSetupStep — first 3 health checks now run at 500ms intervals before settling to the 2s polling cadence, improving detection speed when Ollama is already running but slow to respond

IMPROVE

OllamaSetupStep — running-state transition now animates smoothly with withAnimation instead of snapping

IMPROVE

CompletionStep — removed the large green checkmark icon that was pushing the "Start Using WunderType" button off-screen on the 600×500 onboarding window

FIX

CompletionStep — summary section now shows the configured keyboard shortcut, and the Quick Start instructions display the actual shortcut (e.g. "Press ⌘⇧G") instead of generic "Press your keyboard shortcut"

IMPROVE
v1.4.2

Setup & Custom Prompt Fixes

A small reliability release for setup and custom prompts.

  • Your \“Install starter prompts\” choice during setup is now respected — opting out no longer installs them anyway
  • Custom prompt shortcuts you add or change now work immediately, without restarting the app
  • Smart Selection now puts your cursor back where it was when a correction makes no changes, instead of leaving the whole paragraph selected (requires non-sandboxed build)

AppDelegate — default-prompt seeding moved from applicationDidFinishLaunching into post-onboarding setup. Previously seedDefaultPromptsIfNeeded() ran before the Completion step wrote the installDefaultPrompts toggle, so the seed was version-stamped first and a user who opted out still received all 8 starter prompts. The opt-out is now honored

FIX

AppDelegate / PromptsSettingsTab — adding, editing, or deleting a custom prompt now posts a customPromptsDidChange notification; AppDelegate re-registers the onKeyUp shortcut handlers and rebuilds the menu. Newly added or re-bound shortcuts now fire without relaunching the app. v1.4.1 fixed edits to existing prompts (trigger-time lookup); this fixes brand-new shortcuts, which had a key binding but no action handler until restart

FIX

SettingsManager — the customPrompts getter now logs decode failures via os.Logger instead of silently returning an empty array. A silent decode failure is the mechanism behind the v1.4.1 data-loss bug (an empty load gets overwritten on the next save); logging makes any future recurrence observable

IMPROVE

CorrectionCoordinator — the direct-write (Accessibility) path re-reads the selection immediately before writing and aborts with a "Selection changed" HUD if it moved during the AI call, instead of overwriting the wrong text. A pre-write cancellation check was also added. The write now returns a three-state outcome (written / needs-fallback / selection-changed) so a moved selection aborts cleanly rather than pasting into the wrong place. Affects non-sandboxed builds (the AX path)

FIX

CorrectionCoordinator / AccessibilityService — Smart Selection now restores the original caret position when no replacement happens (the AI returns identical text, errors, or is cancelled), so the user isn't left with the whole paragraph selected. New getSelectedRange / setSelectedRange helpers capture and restore the kAXSelectedTextRange. Affects non-sandboxed builds (the AX path)

FIX
v1.4.1

Custom Prompt Fixes

A quick fix release for custom prompt users.

  • Fixed a bug where updating from v1.3.x could cause existing custom prompts to disappear
  • Custom prompt keyboard shortcuts now pick up edits immediately — no restart needed
  • Custom prompts now show a \“No changes made\” HUD when the AI returns your text unchanged
  • Brief custom prompt instructions are now automatically framed for better AI compliance
  • Improved VoiceOver labels for prompt editing buttons

CustomPrompt — added custom init(from:) decoder with decodeIfPresent for isDefault field, fixing a crash when decoding v1.3.x prompts that lack the field. Previously the decode failed silently, returning an empty array, which caused seedDefaultPromptsIfNeeded to overwrite existing user prompts with defaults

FIX

Custom prompt keyboard shortcuts now read the prompt from SettingsManager at trigger time instead of capturing the systemPrompt string at registration. Edits to prompt text take effect immediately without restarting the app

FIX

CorrectionCoordinator — when a custom prompt returns text identical to the input, a "No changes made" HUD is now shown instead of silently skipping. Built-in correction modes still skip silently (unchanged text means already correct)

FIX

PromptEditorSheet — on save, bare instructions that don't start with "You are" are automatically framed with "You are an in-place text editor" prefix and "Return ONLY the result" suffix for more reliable AI compliance

IMPROVE

PromptsSettingsTab — writingContext switched from @AppStorage to @State with explicit SettingsManager sync, ensuring the 500-character clamp is always enforced

FIX

PromptsSettingsTab — Edit and Delete buttons now have per-prompt VoiceOver accessibility labels

FIX

PromptEditorSheet — keyboard shortcut recorder now has a VoiceOver accessibility label

FIX
v1.4.0

Writing Context, Smart Selection & Built-in Prompts

The biggest update yet — WunderType learns your preferences, ships with ready-to-use prompts, and keeps Ollama fast.

  • Writing Context — teach WunderType your preferred names, terms, and style notes, applied to every correction
  • 8 built-in prompts: Email Polish, Slack Reply, Translate to English, TL;DR, Bullet Points, Prose from Bullets, LinkedIn Post, Commit Message
  • Ollama keep-alive toggle keeps your model loaded in memory between corrections
  • Redesigned tabbed Settings with dedicated Prompts tab for inline editing
  • Slimmer menu bar dropdown

Writing Context — new persistent text field in Settings (Prompts tab) appended to every AI system prompt. 500-character cap enforced at both UI and storage level

NEW

8 curated default prompts bundled as default-prompts.json, seeded on first launch with stable UUIDs for deduplication. Users can edit or delete defaults

NEW

Ollama keep-alive — pass keep_alive: -1 on API requests when enabled, plus periodic 10-minute warm-up ping to prevent model unloading. New toggle in Settings under Ollama provider

NEW

Tabbed Settings — SettingsView refactored into GeneralSettingsTab, PromptsSettingsTab, and AboutSettingsTab with TabView container. Custom prompt CRUD moved from NSAlert dialogs to SwiftUI sheet (PromptEditorSheet)

IMPROVE

Menu bar dropdown slimmed — removed Add/Edit/Remove prompt items; prompt management now in Settings. Dropdown shows only executable prompts

IMPROVE

NSApp.activate(ignoringOtherApps:) replaced with NSApp.activate() across 3 call sites (deprecated since macOS 14)

FIX

CorrectionCoordinator.normalizeUnicode — replaced Foundation replacingOccurrences(of:with:) with Swift-native replacing(_:with:)

FIX

OllamaService.GenerateRequest — custom encode(to:) with encodeIfPresent for keep_alive to avoid sending null (key omission instead)

FIX

Smart selection AX calls collapsed from 3 sequential Task.detached round-trips into a single detached block with 50ms focus-settle delay for macOS 15+ compatibility

FIX

SettingsManager.seedDefaultPromptsIfNeeded — decode errors now logged via os.Logger instead of silently swallowed

FIX
v1.3.7

Code Quality & Accessibility

A code quality and accessibility pass across the app.

  • Fixed a potential crash in the correction HUD when displaying error messages
  • Copy buttons in Ollama setup are now accessible to VoiceOver
  • HUD text now respects your Dynamic Type size preference
  • Ollama setup polling uses modern lifecycle management for better reliability
  • Consistent font styling across all onboarding screens

ModernCorrectionHUD — replaced force unwrap on optional message with safe optional chaining to prevent potential crash

FIX

OllamaSetupStep — copy-to-clipboard buttons now have VoiceOver-accessible text labels with .labelStyle(.iconOnly)

FIX

ModernCorrectionHUD — replaced hardcoded font size with semantic .caption for Dynamic Type support

FIX

OllamaSetupStep — replaced onAppear/onDisappear polling with .task {} for automatic cancellation on view disappear

IMPROVE

Onboarding screens — replaced fontWeight(.bold) with bold() across 8 files for correct system weight selection

IMPROVE
v1.3.6

OpenRouter Onboarding & Brand Refresh

We’re constantly working to make WunderType better for you.

  • OpenRouter is now available as a provider during onboarding — pick from hundreds of models on first launch
  • Updated brand colors across the app for a warmer, more polished look
  • Small bug fixes and improvements

OpenRouterSetupStep — new onboarding step with API key validation and model ID input, matching the existing OpenAI setup flow

NEW

ProviderSelectionStep — added OpenRouter as a third provider card alongside Ollama and OpenAI

IMPROVE

OnboardingView — routing updated to handle Ollama (step 2), OpenAI (step 3), and OpenRouter (step 7) setup paths with correct progress bar normalization

IMPROVE

AccentColor asset added — app-wide accent color changed from system blue to a warm brown matching the WunderType brand

IMPROVE

ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME build setting added for both Debug and Release configurations

FIX

App Store metadata updated — revised keywords, promotional text, and description to better showcase custom prompts and improve discoverability

IMPROVE
v1.3.5

Clearer Error Messages

A focused fix release — error popups now tell you what went wrong instead of showing a silent warning icon.

  • Error HUD now displays a clear message (e.g. \“No text selected\”, \“API key not configured\”, \“Ollama is not running\”) instead of just a warning icon
  • Error HUD stays visible longer (2.5s) so you have time to read the message
  • Previously, error details were only shown via macOS notifications — if notification permissions weren’t granted, you’d see a blank warning triangle with no explanation

ModernCorrectionHUD — added optional message parameter; HUD widens from 64pt to 180pt when a message is present and displays it below the icon

IMPROVE

ModernCorrectionHUDHost — show() and update() now accept an optional message string passed through to the HUD view

IMPROVE

CorrectionCoordinator — new hudMessage(for:) helper maps errors to short user-facing strings ("No text selected", "API key not configured", "Invalid API key", "Ollama is not running", "Model not found", "Insufficient credits", "Correction failed")

NEW

CorrectionCoordinator — error HUD display time increased from 1.5s to 2.5s to give users time to read the message

IMPROVE

CorrectionCoordinator — "No text selected" now shows the error HUD with message in addition to the system notification

FIX
v1.3.4

Onboarding, Accessibility & Safety

A polish release that rounds out the v1.3 series with better onboarding, smarter corrections, and accessibility improvements.

  • Onboarding now asks for notification permission before accessibility — so you’ll see error messages from day one
  • If the AI returns your text unchanged, WunderType skips the replacement entirely — no more cursor flicker on already-perfect text
  • The HUD and cursor spinner now respect Reduce Motion for users with vestibular sensitivity
  • Unusually long AI responses are now truncated automatically to prevent runaway pastes
  • Updated app icons and logo with refreshed color scheme

NotificationStep — new onboarding step that requests notification permission before the Accessibility step, with inline grant/deny handling and skip option

NEW

Onboarding flow expanded from 5 to 6 steps: Welcome → Provider → Provider Setup → Notifications → Accessibility → Completion

IMPROVE

CorrectionCoordinator — skip AX/clipboard replacement when the AI returns text identical to the input, avoiding unnecessary cursor flicker on already-perfect text

IMPROVE

CorrectionCoordinator — defensive output truncation caps AI responses at max(2× input length, 50K characters) with a user notification when triggered

NEW

ModernCorrectionHUD — reads accessibilityReduceMotion environment and disables the .symbolEffect(.bounce) animation when Reduce Motion is enabled

FIX

CursorSpinnerHost — checks NSWorkspace.shared.accessibilityDisplayShouldReduceMotion and shows a static indicator instead of the spinning progress indicator

FIX

NotificationStep icon uses @ScaledMetric for Dynamic Type support, consistent with other onboarding steps

FIX

Updated app icons and logo images with refreshed color scheme for improved visual consistency

IMPROVE
v1.3.3

Smarter Prompts & Clearer Settings

A quality release focused on smarter corrections. The AI follows your instructions more reliably, and the Settings screen is clearer about what each mode actually does.

  • Tightened correction prompts: markdown formatting, code blocks, brand names, and the language you wrote in are now reliably preserved across every mode
  • Make Concise reliably trims at least 25% — no more \“shortened\” results that came back the same length
  • Settings → Mode picker now shows what each mode does in plain language, instead of leaking the internal AI prompt
  • Improved reliability of the direct text-replacement path in native Mac apps

Anti-asterisk rule in commonRules — small models (gemma3:4b, gemma3:1b) had a tendency to wrap changed words in *…* to highlight edits. Explicit rule prevents this

FIX

CorrectionMode prompts — refactored into shared commonRules block applied to every built-in mode. Enforces same-language responses, markdown preservation (in both directions — never strip, never add), opaque code blocks, proper-noun preservation, paragraph structure, and voice/rhythm preservation

IMPROVE

Make Concise prompt — strengthened from "aim to reduce by 25–40%" to imperative "MUST be at least 25% shorter; count words and verify before responding". Validated across 4 Ollama models

IMPROVE

CorrectionMode — new user-facing description property; Settings Mode picker now shows plain-language descriptions instead of the raw multi-paragraph system prompt

IMPROVE

AccessibilityService — added per-PID focused-element fallback when AXUIElementCreateSystemWide() returns -25204. Recurring macOS 15 quirk when AX is queried from a global-shortcut handler; the per-PID query is more reliable for non-sandboxed builds

IMPROVE

AccessibilityService — new frontmostApplicationPID() helper to safely fetch the PID on MainActor (NSWorkspace is MainActor-isolated) before dispatching AX work to Task.detached

IMPROVE

docs/BUILD_DMG.md — documented the leftover sandbox container gotcha. Flipping ENABLE_APP_SANDBOX from YES to NO leaves ~/Library/Containers/com.wunderpen.app behind, and macOS keeps routing preference I/O through the container until it's manually deleted

IMPROVE

docs/STREAMING_RESEARCH.md — captured the streaming-insertion spike as research. Validated the technical approach but not shipped due to App Store sandbox restrictions on cross-process Accessibility writes. Code removed from the binary; design and findings preserved for revival if Apple ever permits sandboxed AX text manipulation

IMPROVE
v1.3.2

Accessibility & Polish

An accessibility and code-quality update.

  • Provider cards in onboarding are now proper buttons, so VoiceOver announces them correctly and reads the selected state
  • Onboarding screens now respect your text-size preference — icons and pickers scale with Dynamic Type
  • Modernized internal notification and concurrency code for better reliability under macOS 15
  • Refactored the onboarding and HUD source files into smaller, single-responsibility modules

ProviderCard — replaced onTapGesture with Button so VoiceOver recognizes provider selection cards and announces the chosen provider via accessibilityAddTraits(.isSelected)

FIX

Dynamic Type — onboarding icons (lock, completion checkmark, provider-card glyph) now use @ScaledMetric so they grow with the user's text-size preference

FIX

Onboarding pickers — Ollama and OpenAI model pickers switched from fixed frame(width:) to frame(maxWidth:) so long model IDs aren't truncated at larger Dynamic Type sizes

FIX

OnboardingView — split the 707-line file into 10 single-responsibility files under WunderType/Onboarding/ for clearer ownership and easier navigation

IMPROVE

ModernCorrectionHUD — split into three files under WunderType/HUD/ and converted the static-only host types (ModernCorrectionHUDHost, CursorSpinnerHost) from final class to enum namespaces

IMPROVE

NotificationService — replaced closure-based UNUserNotificationCenter.requestAuthorization and add(request:) with async/await variants for better Swift 6 strict-concurrency safety

IMPROVE

Concurrency cleanup — removed redundant MainActor.run wrappers from AppDelegate health polling, system-prompt generation, and onboarding poll tasks (these already inherit MainActor isolation)

IMPROVE

ProviderCard — collapsed the background + overlay shape pair into a single chained RoundedRectangle.fill().stroke() (modern iOS 17+ Shape API)

IMPROVE
v1.3.1

Quality & Polish

A quality and polish update that makes WunderType leaner and more reliable.

  • Refreshed Settings design with a cleaner, more spacious layout
  • Fixed an issue where correcting with no text selected could process unrelated clipboard content
  • Internal code improvements for better maintainability

OpenAIChatClient — shared chat completions client for OpenAI-compatible providers, eliminating ~200 lines of duplicated code between OpenAIService and OpenRouterService

IMPROVE

AccessibilityService — extracted focusedElement() helper to replace 5x duplicated AX boilerplate

IMPROVE

CorrectionCoordinator — consolidated clipboard restore logic into a single helper

IMPROVE

Cached menu bar icon instead of recreating it on every health poll tick

IMPROVE

Refreshed Settings window with larger layout (580x580) and cleaner footer with version and links

IMPROVE

Removed last-resort clipboard fallback that could silently correct stale clipboard content when no text was selected

FIX

Removed unused AIProvider.isAvailable property

FIX

Removed dead getSelectedTextBounds() and getSelectedTextEndPosition() methods from AccessibilityService

FIX
v1.3.0

OpenRouter Integration

WunderType now supports OpenRouter, giving you access to hundreds of AI models through a single API key. Use Claude, Gemini, Llama, Mistral, DeepSeek, and many more — all from within WunderType.

  • New AI provider: OpenRouter — access hundreds of models with one API key
  • Choose any model by entering its ID in Settings
  • Secure API key storage in macOS Keychain

OpenRouterService — new AI provider using OpenAI-compatible chat completions API at openrouter.ai

NEW

OpenRouter API key storage in macOS Keychain via KeychainHelper

NEW

OpenRouter provider section in Settings with API key, model ID text field, and connection status

NEW

OpenRouter error handling with user-facing notifications (rate limits, insufficient credits, invalid key)

NEW

UnifiedAIService routing extended to support three providers (Ollama, OpenAI, OpenRouter)

IMPROVE

Default OpenRouter model set to mistralai/mistral-small-3.2-24b-instruct

IMPROVE
v1.2.2

Security & Connectivity

A small but meaningful update that strengthens WunderType’s security posture and makes it easier to stay connected.

  • Added quick links to changelog, website, and contact in Settings
  • Under-the-hood security improvements

Implemented cooldown mechanism to prevent re-triggers from synthetic keystrokes

FIX

Added character limit validation for text correction requests

FIX

Added unit test coverage (entitlements, input validation, keychain)

NEW

Improved OpenAI API key handling in settings

IMPROVE

Updated sandbox entitlements for network access

IMPROVE
v1.2.1

Reliability & Security Improvements

We’re constantly working to make WunderType better for you. This update focuses on reliability and security, making WunderType more robust for everyday use.

  • Fixed an issue where the correction shortcut could trigger twice
  • Improved security for API key storage
  • Crisp, high-resolution logo on Retina displays
  • General stability and reliability improvements

Correction shortcut could fire twice due to synthetic keystroke re-triggers — added cooldown mechanism

FIX

Character limit enforcement on text correction requests to prevent oversized API calls

FIX

More secure OpenAI API key handling

IMPROVE

Better task cancellation and isProcessing state management in CorrectionCoordinator

IMPROVE

AI response character count now logged for diagnostics

NEW

Unit testing target and scheme configuration

NEW
v1.2.0

Rebranded to WunderType

This update includes performance improvements, visual enhancements, and bug fixes to ensure the best possible experience.

  • Improved performance and stability during text corrections
  • Sharper, high-resolution logo on Retina displays
  • Smoother onboarding experience with automatic accessibility permission handling
  • Updated app icon and menu bar icon
  • General bug fixes and improvements

Renamed from WunderPen to WunderType across the entire project

NEW

Replaced all Task.sleep(nanoseconds:) calls with Duration-based API for clarity

IMPROVE

High-resolution logo assets (1x + 2x) for crisp rendering on Retina displays

IMPROVE

Onboarding now automatically requests Accessibility permission on appear

IMPROVE

Cleaned up unused duplicate service files from old WunderPen structure

FIX
v1.1.0

Live Cursor Indicator

A big step forward for the correction experience — WunderType now shows a native spinning cursor while your text is being processed, so you always know it’s working.

  • Live spinning cursor indicator during text corrections
  • Refreshed app icon and menu bar icon
  • Streamlined AI model selection
  • Smoother settings experience with smarter API key input

CursorSpinnerHost — native spinning cursor indicator shown during text correction

NEW

AccessibilityService methods to retrieve selected text and its screen bounds

NEW

Debounce logic for OpenAI API key input in Settings

NEW

Simplified OpenAI model list to gpt-4o-mini only (removed deprecated models)

IMPROVE

Refactored AppDelegate to use dedicated MenuBarIcon for the status item

IMPROVE

CorrectionCoordinator cancels ongoing tasks more efficiently

IMPROVE

CursorSpinnerHost safety timeout set to 150 seconds

IMPROVE
v1.0.0

Welcome to WunderType!

Welcome to WunderType! Correct and improve your text anywhere on your Mac with a single keyboard shortcut.

  • System-wide text correction powered by AI
  • Works with OpenAI or local Ollama models
  • Guided onboarding to get you set up in minutes
  • Secure API key storage in macOS Keychain
  • Lives in your menu bar — lightweight and always ready

System-wide text correction via keyboard shortcut

NEW

OpenAI and Ollama AI provider support

NEW

5-step onboarding flow

NEW

macOS Keychain integration for API key storage

NEW

Accessibility API text replacement with clipboard fallback

NEW

Menu bar agent (no Dock icon)

NEW

App Sandbox with network client entitlement

NEW