跳到内容

v0.10.0返回更新日志

One terminal engine everywhere

发布于

One terminal engine, on every platform

Paneflow 0.10.0 makes the statically linked Ghostty engine the only terminal engine on every shipping target, macOS Apple Silicon included. The Alacritty backend and the terminal.backend setting are gone: there is no second parser and no runtime fallback left to select. With the engine settled, the release wires up everything libghostty already knew how to do and Paneflow was never asking for: Kitty graphics, desktop notifications, OSC 9;4 progress, engine-resolved selection, and a scrollback that finally honors scrollback_lines.

Two other things move. A tab now names itself after the work going on inside it instead of sitting in the sidebar as a fourth "Claude Code" row, and the Shortcuts page becomes grouped, searchable and fast.

Sessions and layouts written by 0.9.x load unchanged. The only configuration key that changes meaning is terminal.backend, which is now ignored.

Breaking changes

The Alacritty backend and terminal.backend are removed

The terminal.backend key in paneflow.json no longer selects anything. A file that still carries it loads fine and the key is ignored.

// paneflow.json, before
{ "terminal": { "backend": "alacritty" } }   // or "ghostty", or "auto"

// paneflow.json, after: delete the key
{ "terminal": {} }

The symptom of doing nothing is none at runtime, since the key is simply ignored. What does change is the failure mode: a Ghostty startup failure used to be able to fall back to Alacritty once before child spawn, and is now reported in the pane instead.

macOS moves onto the same engine as Linux and Windows. macOS panes were parsed by Alacritty through 0.9.x, so this is the first release where a macOS pane and a Linux pane are driven by the same code.

Two build targets can no longer be built

The --no-default-features recovery build went with the Alacritty backend. x86_64-apple-darwin (Intel Macs) and aarch64-pc-windows-msvc (Windows on ARM) have no pinned Ghostty archive in native/libghostty/manifest.toml, and src-app/build.rs now fails the build outright rather than producing a binary with no terminal. Neither target was in the release matrix, so no shipped artifact disappears. Building from source for either target is what stops working.

Quit takes Ctrl+Q away from every pane

Quit now has a default binding on every platform: Ctrl+Q on Linux and Windows, Cmd+Q on macOS. It was macOS-only before, so there was no keyboard route out of Paneflow elsewhere.

Bare Ctrl+Q is XON flow control in a POSIX terminal, the key that resumes output after Ctrl+S, and readline's quoted-insert for anyone running stty -ixon. GPUI stops dispatching a keystroke once a binding handles it, so the PTY never sees the chord again. If you rely on it, rebind or unbind quit in Settings > Shortcuts.

The Attention Queue moves to Ctrl+Shift+A

open_attention_queue moves from secondary-shift-k to secondary-shift-a (Cmd+Shift+A on macOS). Ctrl+Shift+K is what kitty and Ghostty use to clear the scrollback, and that action now claims it. The queue keeps its place in the UI, so the chord it gave up is the one that was harder to discover. A custom binding you wrote yourself is untouched.

Workspaces can no longer be renamed

A workspace is named after the folder it holds. A title free to drift from that folder left the sidebar claiming a name nothing on disk answered to, with no way back. Double-clicking a folder row now selects it. Tab rename is unaffected, and a name stored by an older build is dropped on load.

The close_window action is removed

Paneflow only ever opens one window, and close_window ran exactly the same code as quit. No default bound it and no menu offered it. A close_window entry left in your keybindings is ignored; bind quit instead. The window's own close button and the window manager still close it.

Markdown no longer drags from the Files sidebar into a pane

Clicking a .md row in the Files sidebar opens it in the diff dock's editor as source, like every other file, rather than spawning a rendered markdown pane. The dock's highlighter already carries the block and inline markdown grammars, so the file reads with its own coloring. The drag gesture that dropped a markdown file into a pane split is removed with it; a pane accepts a session drag and a pane drag only. Rendered markdown panes still open from a terminal OSC path click and still restore from a session.

Terminal

  • Images render in the grid. A program transmitting through the Kitty graphics protocol gets its placements painted, cropped and scaled as it asked, under or over the text according to their z-index. Image storage is capped at 32 MiB per pane.
  • Desktop notifications from a program. OSC 9 and OSC 777 raise a desktop notification, suppressed while the window has focus. The title and body go through the same bidi and zero-width strip an agent question does.
  • Progress in the pane header. A program reporting OSC 9;4 gets a chip showing a percentage, working, paused, or error. The chip clears when the program removes the indicator or the child exits.
  • Selection is resolved by the engine. Double-click and triple-click selection, and the cell a drag lands on, now come from libghostty's gesture API: a drag ending past the middle of a cell includes it, as it does in every other terminal. A drag held past the edge of a pane scrolls the viewport and keeps extending, instead of stopping at the last visible row. Hold Alt for a rectangular block selection.
  • A reopened pane keeps its styling. Restoring a closed pane replays its scrollback with colors, styling and hyperlinks intact instead of as plain text, through libghostty's snapshot codec.
  • scrollback_lines is honored. The line budget reached the engine but its byte budget did not, so an 80-column pane pruned at roughly a thousand rows whatever the setting said.
  • OSC 4 color queries answer with the active theme. The renderer painted the theme while the engine answered from its own built-in palette, so a program asking what color 1 is got an answer the screen contradicted. Indexed colors written by a program resolve against the theme too, and follow a theme change.
  • XTGETTCAP for the terminal name answers xterm-256color, which is what the PTY exports as TERM, instead of failing.
  • CSI 0 q resets the cursor to the configured shape and blink rather than the engine's built-in default.
  • Mouse motion reporting no longer clears its deduplication state on every event, so a program in mouse-tracking mode stops receiving redundant motion reports.
  • Reads from the engine are batched and the render cycle follows libghostty's own dirty tracking.
  • The pinned libghostty-vt archive moves to Ghostty f2d5758f built with Zig 0.16.0 on all three platforms. OSC 7 and the clipboard protocols are decoded by libghostty itself instead of a Paneflow-side router; clipboard writes keep the same 100 KiB budget.

Tabs name themselves

A tab opened from the preset picker used to sit in the sidebar as a fourth "Claude Code" row. It now names itself after the work going on inside it, so a rail of agents says what each one is doing.

The opening words of the first prompt land immediately as a placeholder, and the title the agent's own CLI generates for its resume picker replaces it once the CLI has written one. Claude Code writes one today; Codex and Pi generate none and keep the placeholder. No model of our own is ever called.

Renaming a tab yourself turns naming off for that tab for good, since a name you typed is never overwritten. "Reset name" in the tab's context menu hands it back. A tab split between several agents is left alone, because no single one speaks for it.

Agent status without hooks

An organization can disable Claude Code's hooks wholesale from managed settings, with disableAllHooks or allowManagedHooksOnly. The sidebar then knew an agent was running and nothing else, which reads as a broken feature rather than as a blocked one.

Two sources were already reaching Paneflow with nobody listening, and both are now read: the pane itself, since Paneflow presents as Ghostty and Claude Code writes OSC 9;4 progress and OSC 777 notifications into the grid, and Claude Code's own session registry at <CLAUDE_CONFIG_DIR|~/.claude>/sessions/<pid>.json, which carries the full turn state including why a session is blocked. Both go through the same entry point as a hook frame, so they inherit the same surface binding, attention sync and auto-clear.

Three writers on one map need an order. AgentStateSource ranks them Terminal < SessionRegistry < Hook, enforced at the single existing write choke point: a weaker source never talks over a live stronger one, and takes over only after 20 seconds of its silence. Nothing changes on a machine where hooks work.

Settings, Shortcuts

  • The page is nine collapsible groups (Panes, Workspaces, Tabs, Terminal, Search, Diff, Markdown, Agents, Application) declared by the action registry rather than implied by table order, with an expand and collapse all control.
  • A text filter matches the action description and the keystroke. Since chords render as Apple HIG glyphs on macOS, every entry also carries an ASCII spelling covering both readings of GPUI's secondary, so cmd shift k and ctrl shift k both find the same row.
  • A key-capture toggle turns the next pressed chord into the filter, which answers "what already owns this chord?". Capture reads the keystroke through App::intercept_keystrokes, before GPUI matches it against the keymap, so pressing Cmd+Q to find Quit does not quit.
  • "Reset to defaults" asks before rewriting every binding.
  • Rebinding was broken on every platform and is fixed. A recorded chord was persisted through Keystroke::to_string(), the Display impl, rather than the --separated syntax Keystroke::parse reads back. A rebind therefore stored a chord no keypress could ever match, while displacing the default it replaced. If you rebound anything in 0.9.x or earlier and it silently stopped working, that binding is still broken in your keybindings file: rebind it once in 0.10.0 to store a parseable chord.
  • The page no longer lags with every section open. It laid out all ~90 rows on every repaint, offscreen ones included, and since each row highlights on hover, moving the pointer across the list redrew the whole thing. It now renders only the rows the viewport shows, and filters when the query or the fold state changes rather than once per frame. A repaint of the fully expanded page went from 6.2 ms to 0.3 ms on the development machine.

Added

  • Help > System Info shows a bug report's environment section and copies it with one button: Paneflow version and install format, OS, display server, CPU, GPU and driver, renderer, and libghostty version. It says outright when the GPU is a software rasterizer, which is the answer to most "why is it slow" reports. The panel shows the block before you copy it, because it goes into a public issue: no project path and no environment dump. The bug templates now have a slot for it.
  • Default shortcuts for two actions that worked but nothing bound and no menu offered, so they were unreachable unless you bound them by hand. Clearing the scrollback is secondary-shift-k (Ctrl+Shift+K on Linux and Windows, Cmd+Shift+K on macOS, with a plain Cmd+K alias), matching kitty, Ghostty, iTerm2 and Terminal.app. reset_terminal, which is what recovers a pane wrecked by dumping a binary to it, is secondary-shift-r.

Changed

  • read_pane over the MCP bridge sees the screen, not just the scrollback. surface.read returned the retained history, which stops at the viewport by design. An alternate-screen program has no scrollback at all, so reading a pane running a full-screen TUI returned nothing, and that is where every agent CLI lives. The response is now the retained history followed by the screen the program is currently painting, read through libghostty's formatter so soft-wrapped lines rejoin. The two halves do not overlap.
  • Building from source needs Rust 1.98.0. rust-toolchain.toml pins it, so rustup picks it up automatically. Shipped binaries are unaffected.
  • The Files sidebar rail is scoped to the tab that opened it. It was mounted outside any mode branch, so it survived into the Review surface and into Settings where its rows open nothing, and it was a single app-level flag, so opening the tree in one workspace tab put it in front of every sibling tab.
  • macOS: the native window material is dropped in fullscreen. Native fullscreen moves the window onto its own Space with a black backdrop, so AppKit's behind-window material has nothing left to sample and the blur collapses into a flat, dead tint. Fullscreen falls back to the opaque theme chrome. Tiled and maximized windows stay in the desktop Space and keep a live blur, verified on macOS 15.
  • macOS: gpui_macos's text system is pinned to error in the default log filter. It emits per-glyph fallback warnings that flooded the default warn filter during normal rendering. RUST_LOG=info still overrides it.

Fixed

  • A pane no longer inherits the host terminal's identity. CommandBuilder seeds a pane's environment from Paneflow's own, and removing a key from the assembled map cannot unset an inherited name, so only an env_remove at the spawn boundary fixes it. An inherited WT_SESSION makes Claude Code disable OSC 9;4 outright, and an inherited TMUX makes it wrap notifications in multiplexer passthrough that libghostty does not unwrap. Launching Paneflow from Windows Terminal or from inside tmux silently lost both channels in every pane. The launching agent session's CLAUDE_CODE_SESSION_ID leaked the same way and is stripped too.
  • The detached diff dock is keyed on the tab, not the workspace. Two tabs of the same folder shared one dock, so opening it in one tab opened it in its sibling, with that sibling's tabs and last diff snapshot. Closing a background tab also leaked the parked dock and the terminals it held.
  • The diff dock clamps its width to the room the main panel has. Opening a right rail narrowed the main panel under a dock sized for the wide one, and the dock pushed its right edge past the panel's clip. The stored width is now a preference fitted into the live width, and nothing is written back, so the dock returns to full width when the rail closes.
  • The sidebar inline rename accepts keystrokes again, on every platform. Both the workspace row and the tab row host a real text field with a caret, selection, IME and clipboard, focused when the rename opens. It commits on Enter or when focus leaves, cancels on Escape, and hands focus back to the active pane. Refs #32.

Upgrade notes

Install over 0.9.x and launch. No manual step is required.

  • Delete terminal.backend from paneflow.json if you set it. Leaving it in place is harmless; it is ignored.
  • If you rebound a shortcut in 0.9.x or earlier and it stopped working, rebind it once in 0.10.0. The old value was stored in an unparseable form.
  • If you use bare Ctrl+Q for XON flow control or quoted-insert inside a pane, unbind quit in Settings > Shortcuts before you need it.
  • The session format, the packaging contract, and the IPC and MCP method names are unchanged. read_pane returns strictly more than it did.
  • Downgrading to 0.9.x works. The session schema stays at v2; the one field 0.10.0 adds (title_source, which records who named a tab) is optional and an older build ignores it, losing only the "a name you typed is never overwritten" memory for that tab.

Install and validation

Download the signed artifacts from this release or use the installation instructions.

Every primary artifact carries a .sha256 sidecar and a minisign .minisig; each AppImage also ships an .AppImage.zsync for delta updates. 35 assets in total.

Pipeline: run 33375013157.

Legs that passed:

  • Build: x86_64-unknown-linux-gnu, aarch64-unknown-linux-gnu, aarch64-apple-darwin, x86_64-pc-windows-msvc
  • Release test gate on Linux x86_64: cargo fmt --check, cargo clippy --workspace --locked -- -D warnings, cargo test --workspace --locked
  • Package smoke tests: Debian 12 and Ubuntu 22.04 (.deb postinst), Fedora 40 and openSUSE Tumbleweed (.rpm postinst), Arch Linux (tar.gz binary), Windows MSI install and relay
  • Auto-update end to end on Linux x86_64

Full Changelog: v0.9.0...v0.10.0