View changes

Fixed

  • Hang on wake from display sleep — zurm could freeze permanently (requiring a force-quit) after a display slept and woke: closing the laptop lid in clamshell mode, a monitor power-cycling, or the display idle-timeout. When idle, zurm skipped the GPU present to save CPU; on macOS 14+ that stranded Ebitengine’s display-link callback thread, which then deadlocked the main thread inside QuartzCore the next time a display was reconfigured. zurm now re-presents the last frame every tick so the present loop never stalls.

  • Tick rate not restored after sleep — after a sleep/wake cycle the engine could keep running at 60 TPS instead of the configured [performance] tps value, raising idle CPU. The configured rate is now always restored on wake.

  • Crash on display sleep or disconnect — zurm could panic (ebiten: width at NewImage must be positive but 0) when a display went to sleep or an external monitor was unplugged: with no screen attached the window reports a 0×0 size, which zurm then tried to allocate render buffers for. Resizes to a zero-sized window are now ignored; layout recommits automatically once a display is back.

  • Scroll jumped on focus change — with smooth scrolling enabled, clicking a pane to focus it (or switching tabs) could snap its viewport up or down at random. The scroll animation tracked the previous pane’s position and forced it onto the newly focused pane. Each pane now keeps its own scroll position across focus and tab changes.

  • Window not resized when moved to another display — dragging zurm onto a different monitor could leave the content stranded at its old size in the top-left corner with the rest of the window black. The render surface was sized from ebiten.WindowSize(), which lags the framebuffer that actually drives rendering when the window crosses displays. Render geometry is now committed from the size ebiten renders against, so content fills the window after a move.

  • Crash on invalid font size — a font.size of zero or below in config (including via a typo during a live config edit, which hot-reloads) collapsed the glyph cell to 0×0 and panicked with an integer divide-by-zero. Font size is now clamped to a renderable range (6–72 points) on every load, and the renderer guards against a zero-sized cell as a final safeguard.

  • Crash on invalid window dimensions — a window.columns or window.rows of zero or below panicked at startup when allocating the terminal buffer. Window columns/rows are now clamped to a minimum of 1 and padding to a minimum of 0 on load, and the screen buffer floors its own dimensions as a final safeguard.

  • Click-count carried across panes — clicking to select in one pane then quickly clicking the same cell in another pane could register as a double/triple click. Click tracking now resets when focus changes panes.

  • Crash restoring a corrupt session — a session file with an out-of-range active tab index (e.g. negative, from manual editing or corruption) panicked at startup, preventing launch until the file was deleted. The restored active-tab index is now clamped to a valid range.

View changes

Fixed

  • Shift+scroll double-fires — both handleMouse and handleInput were processing the same wheel event through the shared ScrollAccum in the same frame, causing Shift+scroll to move at 2× the configured speed. All scrollback-view scroll is now consolidated in handleInput; handleMouse only forwards events to the PTY when mouse mode is active.

Added

  • Smooth scroll animation — opt-in ease-out animation for wheel scroll. When enabled, scroll input accumulates into a target and ViewOffset glides toward it each frame instead of jumping instantly. Keyboard scroll (PageUp/Down) remains instant. Enable in config:

    [scroll]
    smooth        = true   # default: false
    smooth_factor = 0.25   # fraction of remaining distance per frame; 0.1 = slow, 0.5 = snappy
    
View changes

Fixed

  • CMD key stuck after CMD+Tab — modifier key state is now seeded from NSEvent.modifierFlags (hardware truth) on focus gain instead of GLFW’s cached state. GLFW misses key-up events that fire while another window owns focus (e.g. CMD released in the App Switcher), leaving the key stuck as pressed indefinitely and causing every subsequent keystroke to fire as a CMD+key combo.

  • Hard hang at 3–4 GB requiring kill -9 — a soft memory ceiling (GOMEMLIMIT, default 1536 MB) tells the GC to run more aggressively as the heap approaches the limit, preventing the runaway growth that combined with Metal’s nextDrawable timeout to produce the hang. Configurable via memory_limit_mb under [performance]; set to 0 to disable.

  • Scrollback allocation pressure — the scrollback ring buffer now reuses evicted row slices in-place instead of allocating a fresh []Cell on every scrolled line. Eliminates the steady-state allocation stream for busy terminals (compiler output, streaming tools), materially reducing GC pressure on the game loop thread.

  • Display change geometry corruption — layout is now committed only after window size and DPI have been stable for 3 consecutive frames following a display-change notification, instead of immediately on the first frame. Adapts to the actual duration of EDID negotiation (HDMI docks, lid close/open, resolution changes, multi-monitor DPI transitions) rather than a fixed frame count.

  • Crash on HDMI disconnectebiten.Monitor() can return nil transiently while the display is being reconfigured; calling .DeviceScaleFactor() on a nil monitor caused a panic. All in-game-loop call sites now fall back to the last known DPI when the monitor is unavailable.

Changed

  • [performance] memory_limit_mb = 1536 — new config field for the soft memory ceiling (MB). Set to 0 for unlimited (previous behaviour).

View changes

Fixed

  • Sleep/wake hang — zurm no longer requires kill -9 after macOS sleeps. The Metal game loop is now halted via NSWorkspaceWillSleepNotification before the display powers off, preventing nextDrawable from blocking indefinitely.

  • UI corruption with external display — waking with a different HDMI configuration no longer produces a corrupted or stale layout. NSApplicationDidChangeScreenParametersNotification now triggers a full geometry and DPI recalculation.

  • Go 1.26.2 — patches four stdlib CVEs in crypto/tls and crypto/x509 (GO-2026-4870, GO-2026-4866, GO-2026-4947, GO-2026-4946).

View changes

Added

  • SSH host indicator — when the focused pane’s foreground process is ssh, the status bar shows [SSH] user@host in green on the right side. The indicator clears automatically when the SSH session exits or when the shell returns to prompt.

View changes

Added

  • Tab parking (Cmd+Shift+K) — hide a tab from the tab bar while keeping its PTY fully alive. Parked tabs are accessible via Cmd+J (tab search shows [P] badge) or by jumping to their pin slot (Cmd+G). Park as many as you want; only visible tabs count toward the limit.

  • Max open tab limit ([tabs] max_open = 10) — caps the number of visible (non-parked) tabs. Set to 0 for unlimited. When the limit is reached, Cmd+T shows a flash message.

  • Parked tab badge[N↓] count badge at the right edge of the tab bar when parked tabs exist. Badge turns accent color when any parked tab has unseen activity.

  • Session persistence for parked tabs — parked tabs are saved and restored across sessions with full layout, title, note, and pin slot fidelity.

  • Command palette entry — “Park Tab” available via Cmd+P.

  • Help overlayCmd+Shift+K documented in the Navigation category.

  • Stats overlay — tab and pane counts now include parked tabs.

Fixed

  • Pin slot evictionPinActive now evicts pin slots from both visible and parked tabs, preventing stale pin badges.

  • Dead parked tab cleanup — parked tabs whose shell exits are detected and removed every frame (no zombies).

  • PTY freeze on idle — idle suspend/unsuspend now covers parked tab PTY readers.

  • Stale layout on unpark — unparking recomputes pane rects and sends Resize so a tab parked before a window resize comes back with correct dimensions.

  • Tab search cursor clamp — cursor is clamped when the filtered list shrinks between frames (e.g. dead parked tab removed).

View changes

Fixed

  • Double-click word selection on wrapped lineswordSelection now crosses soft-wrap row boundaries so that double-clicking either visual row of a wrapped word selects the full token.

  • URL detection on wrapped linesDetectURLs merges consecutive soft-wrapped rows into a single logical line before running the URL regex. URLs that span a row break are now detected, highlighted, and Cmd+clickable. URLAt updated for correct multi-row hit-testing.

Added

  • Shift+click to extend selection — click to anchor, then Shift+click elsewhere to extend the selection to that cell. Standard terminal behaviour matching iTerm2 / Terminal.app.

  • Arrow key cursor navigation in text inputs — Left / Right / Home / End move the cursor in all text input overlays: tab rename, pane rename, tab note, search bar, command palette, tab search, keybinding overlay search, URL input, and markdown viewer search. Characters are inserted and deleted at the cursor position instead of always at the end.

View changes

Added

  • zurm-server — standalone headless PTY session daemon (cmd/zurm-server). Sessions persist independently of the zurm GUI. Communicates over a Unix socket using a length-prefixed binary protocol (zserver package). Zero Ebitengine dependency.

  • Per-pane opt-in server mode — server-backed panes are created explicitly. Regular tabs/panes always use a local PTY. No global toggle.

  • Keybindings:

  • Cmd+Shift+B — new server tab

  • Cmd+Shift+H — split horizontal (server pane)

  • Cmd+Shift+V — split vertical (server pane)

  • Command palette: “New Server Tab”, “Split Horizontal (Server)”, “Split Vertical (Server)”, “Attach to Server Session”

  • Auto-start — zurm-server spawns automatically on first server pane creation. Located next to the zurm binary or via PATH. Detached via Setsid; survives GUI close. Log: ~/.config/zurm/server.log.

  • CLI flags:

  • --list-sessions / -ls — print active server sessions and exit

  • --attach <id> / -a <id> — attach to a server session by ID or short prefix (Docker-style matching)

  • Status bar: [SERVER] indicator (cyan) when the focused pane is backed by zurm-server.

  • Output replay: 64KB ring buffer per session. On reattach, recent terminal output is replayed so you see context immediately.

  • [server] config section:

  • address — Unix socket path (default ~/.config/zurm/server.sock)

  • binary — path to zurm-server executable (default: sibling of zurm or PATH)

  • make build-server — Makefile target for the server binary.

Changed

  • Server sessions are NOT saved in session.json. Local session save/restore is completely independent of zurm-server.

  • pane.New() reverts to original 5-parameter signature. Server panes use pane.NewServer() exclusively.

  • PtyBackend interface in terminal/ abstracts PTY I/O. PTYManager (Mode A) and ServerBackend (Mode B) both implement it.

View changes

Added

  • vault.ghost_text — bool toggle to show or hide inline ghost suggestions independently of vault history. Set ghost_text = false to keep encrypted history without any visual suggestion overlay. Hot-reload supported.

  • vault.max_entries — cap on the number of commands stored in the vault. Oldest entry is evicted when the cap is reached. 0 = unlimited (default).

  • vault.sync_interval — how often (in seconds) the vault re-imports zsh history while zurm is running. 0 = import once at startup only (default).

Fixed

  • Git goroutine channel leak — rapid directory switching previously accumulated short-lived goroutines waiting on context cancellation. The git status query now uses a single persistent channel with a generation counter; stale results from cancelled goroutines are discarded without blocking.

View changes

Fixed

  • TAB key restored — vault ghost suggestions no longer intercept Tab; Tab always reaches the PTY for shell tab-completion. Right Arrow continues to accept the current suggestion.

  • Ghost persists after vault disableupdateVaultSuggestion now clears the ghost when vault == nil, so disabling vault via config (including hot-reload) immediately removes stale ghost text.

View changes

Fixed

  • Vault Tab cycle: screen was not redrawn after cycling because screenDirty was not set (dirty-render optimization)

View changes

Added

  • Vault ghost suggestions: Tab cycles through matching commands (wraps around); right arrow accepts

View changes

Added

  • Command vault: encrypted local command history with fish-style ghost text suggestions

  • Imports ~/.zsh_history on first launch (deduplicated)

  • Inline ghost text appears as you type; right arrow accepts the suggestion

  • AES-256-GCM encryption at rest with auto-generated key (~/.config/zurm/vault.key)

  • Space-prefixed commands excluded from storage (matches zsh HIST_IGNORE_SPACE)

  • Completed commands captured via OSC 133 D (shell integration)

  • New [vault] config section: enabled, history_path, vault_path, ignore_prefix, suggestion_color

  • Disabled by default; set vault.enabled = true to activate

View changes

Fixed

  • Pane grey-out after resize or sleep/wake: clear renderer pane cache on layout dirty

  • First click ignored after focus regain: reset stale prevMouseButtons on focus gain

  • Input drop during suspension recovery: reduce idle TPS to 5 (not 1) for sub-200ms input latency

  • Emergency unsuspend on mouse click for systems where IsFocused() doesn’t update after sleep/wake

  • make install now registers zurm with LaunchServices so Finder “Open With” works for folders

Added

  • auto_idle config option under [performance]: disable to keep rendering when unfocused (default: true)

View changes

Fixed

  • Cmd+V paste not working in Cmd+F search bar

  • Normalized all text input handlers (tab rename, tab note, URL input) to use inpututil.IsKeyJustPressed for paste — consistent with pane rename

View changes

Fixed

  • Light theme: all 16 ANSI colors now meet WCAG AA (7:1+ contrast against #FAFAF8) — previous palette had 11 colors below 4.5:1, worst offender bright_white at 1.2:1

View changes

Fixed

  • Block rendering Y offset: include pane header height in geometry calculations

  • Blocks only render after command completes (no janky growing border during execution)

  • Clear stale block positions on window resize; keep active block so first post-resize command works

Added

  • show_border config option for blocks: when false, hides border/bg tint but keeps badges and hover copy buttons

View changes

Fixed

  • Clipboard encoding in .app bundles: set LANG=en_US.UTF-8 at startup so pbcopy/pbpaste handle multi-byte UTF-8 correctly (em dash, CJK, etc. no longer paste as mojibake)

View changes

Fixed

  • Resize lock starvation during heavy PTY output (concurrent RLock/Lock ordering in pty.go)

  • Paste and typing dropped on focus regain (prevKeys reset on focus transition)

  • Light theme white-on-white text (bright_white adjusted to #E8E8F0)

  • Font fallback not applying on config hot-reload (gate on loaded flag)

  • Recorder nil pointer on resize before first recording

  • Missing screenDirty on overlay, menu, and confirm state changes (dirty-flag audit)

  • Guard resize resume against idle suspension

View changes

Fixed

  • C1 control byte handler (0x80-0x9F) intercepting UTF-8 continuation bytes mid-sequence, breaking all multi-byte Unicode rendering (icons, arrows, CJK, Nerd Font glyphs)

View changes

Fixed

  • CI lint: resolve staticcheck SA4004 and gosec G115/G304 failures

View changes

Fixed

  • Parser oscBuf/dcsBuf capped at 4096 bytes to prevent unbounded growth

  • CSI params clamped to 65535 (VT spec ceiling) instead of silently overflowing

  • Scroll early-return no longer drops simultaneous keystrokes

  • closeActiveTab zeroes trailing slice slot to prevent Tab/pane GC leak

  • CUU/CUD respect scroll region boundaries (clamp to ScrollTop/ScrollBottom)

  • SearchAll searches active screen (alt when active) instead of always primary

Changed

  • Deduplicated copySelection/extractSelectedText into single helper

View changes

Fixed

  • Zombie process leak: PTY reader now reaps child process on exit

  • Screenshot goroutine data race eliminated with channel pattern

  • Incorrect double-width rendering for U+2600-U+27BF symbol range removed

  • pprof server gracefully skips when port is in use

  • resizeBuf no longer falsely clears wide chars on grid grow

  • VT parser: stateIgnore byte drop, C1 control handling, stale CSI params

  • DECOM origin mode scroll region, DECRC/RCP cursor clamp, ECH wide char overlap

  • Primary scroll region preserved across alt screen toggle (no longer lost after TUI exit)

  • Alt screen wrapped slice deep-copied instead of aliased

  • Gray background on window resize (missing screenDirty flag)

  • GPU image leak on resize — old offscreen/blocksLayer/modalLayer now deallocated

  • Pane cache leak on tab close — closed pane references now released

  • Config reload rollback on failed font load prevents inconsistent state

View changes

Added

  • Multiple font fallback chain: configure fallbacks in config.toml with ordered list of fonts (CJK, emoji, Nerd Font)

  • Wide character support: CJK, fullwidth forms, and wide emoji render correctly across 2 terminal columns

  • Cell Width field (0=continuation, 1=normal, 2=wide) for precise wide character tracking

  • terminal.RuneWidth() — single source of truth for character width calculation

  • 15 table-driven tests for wide char placement, erase, resize, and width detection

Fixed

  • Erase operations (EraseInLine, EraseInDisplay, InsertChars, DeleteChars) handle wide char boundaries correctly

  • Selection copy skips continuation cells — no duplicate characters in clipboard

  • Mouse click and drag snap to parent cell when landing on wide char continuation

  • Word selection expands correctly across wide characters

  • Search highlights span the correct columns for wide characters (colMap pattern)

  • URL detection handles wide characters in surrounding text

  • Resize fixes truncated wide chars at boundaries and orphaned continuation cells

View changes

Added

  • llms.txt browser: link following hint mode (f key) — letter badges on visible links, follows llms.txt links inline or opens external URLs in system browser

  • llms.txt browser: navigation history — Backspace/H to go back, L to go forward, breadcrumb indicator in title bar

  • llms.txt browser: Cmd+Enter sends viewer content to a persistent pane running less

  • llms.txt browser: “Send Viewer to Pane” command palette entry

  • Browser category in keybindings overlay with all new shortcuts

  • Markdown viewer: improved styling for h1/h2/h3 headings, code blocks (green + border), table row borders

  • ANSI-styled output for send-to-pane (renders with less -R)

  • Precise x/y search match highlighting in markdown viewer

Fixed

  • Search highlights drawn over text making it unreadable — now uses three-layer approach (backgrounds → highlights → text)

  • Search highlight misaligned on code blocks (missing +cw offset)

  • Panic in mergePaneToTab when source is the last tab

View changes

Added

  • llms.txt browser: Cmd+L opens URL input to fetch llms.txt from any domain

  • Both /llms.txt and /llms-full.txt fetched in parallel; Tab switches between them in the markdown viewer

  • URL input overlay: Cmd+V paste, backspace key repeat, domain normalization (strips protocol and known paths)

  • Hint mode: hold Cmd to show tab number badges

  • n/N match navigation in markdown viewer normal mode (after closing search bar)

View changes

Fixed

  • Stats overlay dismiss no longer leaves a stale pane cache — call ClearPaneCache on toggle off

View changes

Added

  • Markdown viewer: Cmd+F and / to open search bar with match highlighting

  • Markdown viewer: n/N to jump between search matches, Enter/Shift+Enter in search mode

  • Markdown viewer: match count indicator and “no matches” feedback

Changed

  • Status bar CWD and foreground process polling replaced with event-driven updates — polls only fire when PTY output arrives, throttled to 2s (CWD) and 1s (foreground). Zero polling during idle.

Fixed

  • FFMPEG recorder missing last frames on stop — replaced mutex-guarded writes with buffered channel + dedicated writer goroutine that flushes before closing stdin

  • FFMPEG recording playback speed too fast — duplicate frames inserted to fill timing gaps when capture intervals exceed 33ms, keeping playback aligned with wall-clock time

  • Explicit output frame rate (-r 30) added to ffmpeg command

View changes

Changed

  • Markdown parser replaced with goldmark (GFM extension) for robust AST-based rendering

  • Table columns are now aligned with computed max-width padding

Added

  • Markdown viewer: mouse wheel scrolling

  • Markdown viewer: key repeat on j/k and arrow keys for continuous scrolling

  • Markdown viewer: vim motions — gg (top), G (bottom), Ctrl+d (half-page down), Ctrl+u (half-page up)

  • Markdown viewer: strikethrough, image, task list checkbox, and table rendering styles

Fixed

  • Markdown viewer close leaving grey screen — added ClearPaneCache on dismiss

  • Markdown viewer content capture uses full block output range instead of visible viewport

View changes

Fixed

  • Unbounded memory growth in command blocks slice — capped with copy+reslice eviction

  • Scrollback buffer O(n) shifting replaced with pre-allocated ring buffer

  • Per-frame allocations reduced — cached Leaves() on LayoutNode, ASCII rune-to-string lookup table

  • Stale git status goroutines leak on CWD change — now cancelled with context + 5s timeout

  • Upgrade Go from 1.25.8 to 1.26.1 (fixes GO-2026-4599 and GO-2026-4600 in crypto/x509)

Added

  • Optional pprof HTTP endpoint for runtime profiling (pprof.enabled = true)

  • Suspend game loop after 5s unfocused — TPS drops to 1, terminal polling paused

View changes

Fixed

  • Arrow key repeat firing inconsistently in terminal

  • Block rendering improvements for command output regions

View changes

Added

  • Text-to-speech: read selection aloud via macOS say command (Cmd+Shift+U)

  • TTS auto-speak: automatically reads command output aloud when enabled (requires OSC 133 shell hooks)

  • Bell-triggered TTS and stop-on-keypress

  • [voice] config section: enabled, voice, rate (words per minute)

  • Falls back to visible buffer text when no selection is active

  • Speech-to-text: dictation overlay via macOS SFSpeechRecognizer

  • Git branch display in status bar

Fixed

  • Default voice.enabled to false (opt-in)

  • Remove unused functions flagged by staticcheck

View changes

Added

  • Tab hover popover: minimap preview when hovering background tabs (configurable delay, size)

View changes

Added

  • Stats overlay (Cmd+I): live TPS/FPS, goroutines, heap memory, GC pauses, tab/pane count, buffer dimensions

  • “Toggle Stats Overlay” command palette entry

View changes

Added

  • Tab notes/annotations: attach a persistent text note to any tab (Cmd+Shift+N or command palette)

  • Note indicator (*) shown on tab bar for tabs with annotations

  • Active tab note displayed in status bar as [note text] segment

  • Tab notes persist across sessions via session.json

  • “Edit Tab Note” in right-click context menus (tab bar and pane)

  • File drag-and-drop: drop files from Finder to paste shell-escaped paths into the terminal

  • Detach pane to new tab (command palette or right-click menu)

  • Move pane to next/previous tab (command palette or right-click menu)

  • TCC privacy usage descriptions in Info.plist for Documents, Desktop, Downloads, removable volumes, and network volumes

Fixed

  • Paste now NFC-normalizes clipboard content (fixes accented characters from macOS NFD clipboard)

  • Paste normalizes line endings to \r (fixes multi-line paste producing weird characters)

  • Combining Unicode characters (accents, diacritics) merge with the preceding cell instead of occupying their own cell

  • Right-click tab context menu now targets the clicked tab, not the active tab

  • Scrollback scrolling blocked when alternate screen is active (fixes broken scrolling in TUI apps like Claude Code, nvim, htop)

  • Alt screen modes ?47 and ?1047 now supported alongside ?1049

  • Zoomed pane now clears HeaderH so PTY gets correct row count (fixes Helix :q hidden behind status bar)

  • Pane row calculation uses single top padding instead of double, recovering ~1 wasted row at pane bottom

  • Config reload now recomputes layout for all tabs (fixes stale status bar height after config change)

Changed

  • Go version bumped from 1.25.5 to 1.25.8 (fixes govulncheck GO-2026-4602)

View changes

Added

  • Config hot-reload (Cmd+,) — apply config changes without restarting

  • Theme system: load external TOML themes from ~/.config/zurm/themes/

  • Theme merge: user-explicit palette colors override theme; implicit colors follow theme

View changes

Added

  • Configurable separator color in [colors] (shared by tab bar and status bar)

  • Configurable separator_height_px and padding_px in [status_bar]

Changed

  • Tab bar uses darkened background with visible divider lines between tabs

  • Tab bar has 1px bottom border separating it from pane content

  • Status bar separator and padding are now configurable with sensible defaults

Fixed

  • Help button no longer covers the status bar top separator line

View changes

Fixed

  • ffmpeg not found when running as macOS .app bundle — login shell PATH is now resolved at startup

View changes

Added

  • Cmd+J tab search overlay with fuzzy filtering by tab name or CWD

  • Tab search accessible via command palette (“Tab Search”)

  • Session config template now includes auto_save option for discoverability

View changes

Added

  • Resize pane splits by dragging the divider with the mouse (4px hit zone)

  • Resize pane splits via Cmd+Option+Arrow keys (5% step per press)

  • Mouse drag to reorder tabs in the tab bar (8px threshold to distinguish from click)

  • Rename focused pane via context menu (Panes > Rename Pane) or command palette

  • Double-click on pane header to rename (mirrors tab rename UX)

  • Custom pane names persist across session save/restore

View changes

Changed

  • Info.plist version management — replaced hardcoded version with __VERSION__/__BUILD__ placeholders injected at build time

  • Distribution format — replaced ZIP with DMG via hdiutil create

  • Makefile build flags — added -trimpath -ldflags="-s -w" to match CI

Added

  • Ad-hoc code signing (codesign --sign -) in both Makefile and CI — Gatekeeper no longer blocks the app

  • make dmg target for local DMG creation

  • LSMultipleInstancesProhibited in Info.plist to allow open -n zurm.app

View changes

Fixed

  • Screen recording plays back at extreme speed — frame capture now runs before the dirty-flag gate so idle frames are duplicated at 30fps

  • Clear command (CSI 3J) leaves residual line in scrollback — mode 3 now properly calls ClearScrollback() instead of sharing the mode 2 code path

  • App appears unresponsive after macOS sleep/lock — force full redraw on focus regain across all tabs and panes

View changes

Added

  • Display app version in the status bar (right side, before the help button)

Fixed

  • Resizing the window while a pane is zoomed no longer resets it to split dimensions

  • PTY size mismatch after window resize in zoom mode

View changes

Added

  • Reorder tabs with Cmd+Shift+Left/Right

  • Focus history navigation with Cmd+; — jump back to previously viewed tab or pane (stack of 50 entries)

  • Tab activity indicator — purple dot on background tabs with unseen PTY output

  • Window close button (red X) now shows quit confirmation modal, matching Cmd+Q behavior

Fixed

  • Tab bar artifacts when reordering tabs — full clear before redraw

  • Tabs losing displayed name after reorder — stable default title assigned at creation

  • Initial tab now seeded into focus history on startup

View changes

Fixed

  • Typing after trackpad scroll not registering — sub-pixel momentum deltas blocked keyboard input

  • First keystrokes lost after Cmd+Tab — stale prevKeys from blur caused missed edge detection

  • Long line overflow overlapping at beginning of line — terminal/PTY not resized after ComputeRects in tab creation and session restore

  • Non-ASCII paste in tab rename — added UTF-8 validation for clipboard data

Changed

  • Info.plist updated with full macOS app metadata (category, dark mode, architecture, copyright)

View changes

Added

  • Alt+Backspace word deletion in all text inputs (tab rename, search, palette, file explorer)

  • Manual session save with configurable auto-save (defaults to false to prevent accidental overwrites)

  • “Save Session” command in command palette for explicit session snapshots

  • File explorer search/filter with Telescope-style filtering (press “/” to search)

  • Pane layout persistence — saves and restores complete pane tree structure with splits and ratios

  • macOS .app bundle detection — automatically uses home directory instead of bundle internals

  • Text input utilities with continuous backspace support and proper key repeat

  • File explorer . (current directory) entry — pressing Enter inserts the current path

  • File explorer .. (parent directory) entry — always visible as first item for easy navigation

Changed

  • Session auto-save now defaults to false (set session.auto_save = true in config to enable)

  • File explorer shows only matching entries when searching (like Telescope)

  • Each pane’s working directory is now saved and restored in sessions

  • File explorer search now filters current directory only (non-recursive) for better performance

  • File explorer Enter key behavior — directories navigate into them, . inserts current path

  • File explorer hint bar shows ../ for parent navigation instead of confusing h/⌫

Fixed

  • Initial directory when launching from .app bundle now correctly defaults to home

  • File explorer search navigation and scrolling when filtering

  • File explorer search rendering now shows a flat filtered list

  • Prompt arrow and UI symbols incorrectly treated as emoji characters

Removed

  • Emoji font support (saves 10MB binary size) — Ebiten doesn’t support color fonts (see docs/emoji-limitations.md)

View changes

Fixed

  • Tab stops not rebuilt on terminal resize — tabs beyond the original column count jumped to the last column, causing ls and other tab-using output to wrap incorrectly after resizing the window

View changes

Added

  • One-shot screenshot capture to PNG (Cmd+Shift+S) — saves to ~/Pictures/zurm-screenshots/

  • Screen recording via FFMPEG pipe to MP4 (Cmd+Shift+.) — saves to ~/Movies/zurm-recordings/

  • Recording status bar indicator with blinking dot, elapsed time, and output file size

  • Quit confirmation dialog on Cmd+Q

  • Makefile with build, bundle, install, and clean targets

  • macOS .app bundle packaging (Info.plist, icon, install to /Applications)

  • New tab defaults to $HOME on first launch

Initial Release

Added

  • GPU-rendered terminal using Ebitengine with offscreen compositing at native HiDPI resolution

  • Full PTY integration with xterm-256color, truecolor, and configurable 16-color ANSI palette

  • Pane splits — binary tree layout engine supporting horizontal and vertical splits at any depth

  • Each pane owns an independent PTY, shell, buffer, and cursor

  • Multi-tab workspace with OSC 0/2 title updates, user rename via double-click or Cmd+Shift+R

  • Tab pins — bind tabs to home-row slots (a–l) and jump instantly with Ctrl+Space

  • Tab switcher — fuzzy overlay to switch tabs by name (Cmd+Shift+T)

  • Text selection — click, double-click (word), triple-click (line), drag

  • Copy/paste via Cmd+C/Cmd+V with bracketed paste support

  • Configurable scrollback buffer with Shift+PgUp/Down navigation

  • In-buffer incremental search across scrollback and primary screen (Cmd+F)

  • TUI compatibility — X10 and SGR mouse reporting, alternate screen, focus events, Kitty keyboard protocol

  • Mouse drag selection in PTY mouse mode (e.g. Helix/Neovim)

  • Status bar showing live CWD, git branch, foreground process name, scroll offset, and zoom mode

  • Zoom pane — temporarily fullscreen the focused pane (Cmd+Z)

  • Command palette — searchable list of all commands and shortcuts (Cmd+P)

  • Help overlay — all keybindings grouped by category (Cmd+/)

  • Right-click context menu for copy/paste, pane management, scroll, and more

  • Session persistence — saves tab CWDs, titles, and pin slots on quit; restores on relaunch

  • Optional close confirmation dialog before closing a tab or pane

  • File explorer sidebar — tree browser with create, rename, delete, copy path, and Finder reveal (Cmd+E)

  • Command blocks — OSC 133 prompt/output tracking with hover-to-copy

  • Left Option as Meta key (configurable — right Option retains macOS character composition)

  • Word delete with Opt+Backspace

  • New tab inherits CWD of the active pane (configurable)

  • Config auto-bootstrap — writes a fully documented ~/.config/zurm/config.toml on first launch with all keys and defaults

  • Install shell hooks command in palette — writes OSC 133 zsh integration to .zshrc