Commit Graph
37 Commits
Author SHA1 Message Date
l0ng-ai b21e299fba fix(windows): close a pane when its shell exits on its own (#30)
Windows: a shell that exits on its own (exit / Ctrl-D / crash) left the pane
wedged open because ConPTY's output pipe never EOFs on a natural child exit.
Add a Windows-only exit monitor that waits on the child handle directly and
reports through a shared, run-once DeathReporter latch — the same Exited /
on_dead path the reader's EOF drives on Unix. Unix behavior is unchanged.

Claude-Session: https://claude.ai/code/session_01ABey161AUxhgmJC3PRoYtF
2026-07-08 22:31:57 +08:00
l0ng-aiandl0ng-ai 59a4c9d859 fix(terminal): stop severing wide Nerd Font prompt glyphs (#17) (#31)
* fix(terminal): give solo prompt glyphs a two-cell paint window

Nerd Font prompt icons and the multiline `➜`/`❯` rendered sliced off on
the right (#17). tty7 paints any single-column glyph clipped to exactly
one cell, but a symbol face routinely draws ink past that cell: a
non-Mono Nerd Font sets a one-cell *advance* on its icons yet fills up to
~1.9 cells (measured across Hasklug / Meslo / JetBrainsMono NF), and the
OS cascade serves a proportional `➜` the same way when nothing in the
font list covers it. The per-cell content mask then severed the overflow
— the incomplete icons and cut-off arrow in the report.

Advance is no signal here (it reads one cell for those overflowing
icons), so widen every solo glyph's clip to two cells. A glyph that
already fits is untouched — it has no ink to spill — while a symbol that
overflows renders whole, bleeding into a trailing blank the way iTerm2
and Terminal.app do with non-Mono faces; the two-cell bound keeps a
pathological face from smearing a lone glyph across the row. Batched
Run/Wide segments keep their exact column-span clip, and Mono Nerd Fonts
are pixel-identical. Pairs with the native powerline separators from #19.

Verified in-app on Apple Silicon with a non-Mono "Hasklug Nerd Font"
primary: icons and a powerline prompt that were severed now render whole,
with no change to ASCII, CJK, or the Mono-font path.

* test(terminal): cover both clip-width branches, not just the solo constant

Fold the solo/batched clip-width decision into one pure `seg_clip_width`
so the branch itself is testable (per PR review): the test now asserts a
batched Run/Wide segment clips to its exact column span while a solo
glyph gets the two-cell window, instead of only checking the standalone
constant. No behavior change.

---------

Co-authored-by: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com>
2026-07-08 22:10:49 +08:00
l0ng-aiandl0ng-ai c6e071362a fix(menu): soften context-menu and dropdown row hover to match the palette (#24)
gpui-component's MenuItemElement highlights the hovered/selected row from
`tokens.accent` + `accent_foreground`. tty7's theme never overrode those, so
menu rows fell back to the stock saturated accent — a hard highlight out of
step with the app's soft mix-based palette.

Point the accent tokens (and the plain `accent`/`accent_foreground` fields the
input completion / code-action popovers read) at `list_active` (mix 0.17), the
same soft fill the command palette uses for its selected row, and keep the
hover text at `foreground` so it stays legible on the low-contrast fill. Right-
click menu, "+" dropdown and command palette now share one hover language.

Claude-Session: https://claude.ai/code/session_01ABey161AUxhgmJC3PRoYtF

Co-authored-by: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com>
2026-07-08 18:58:57 +08:00
l0ng-aiandl0ng-ai 86857f8cb2 fix(shell): keep zsh plugin managers out of the throwaway ZDOTDIR (#29)
Opening a new tab or split stalled for seconds while a zsh plugin manager
reinstalled itself from scratch. tty7 launches zsh through a per-pane throwaway
ZDOTDIR so it can layer its OSC 133 shell integration on top of the user's
config, but it left ZDOTDIR aimed at that empty temp dir for the whole shell
lifetime. Tools that resolve their own state via ${ZDOTDIR:-$HOME} — Zim
(.zimrc/.zim), oh-my-zsh, compinit's .zcompdump — therefore looked inside the
empty throwaway dir and rebuilt on every pane: Zim redownloads/recompiles every
module (the ~3s stall and "modules/…: Installed" spam of #15), compinit rewrites
its dump into the temp dir, etc.

Each redirector now stashes our dir, points ZDOTDIR at the user's real config
dir while their startup file runs, then restores ours so zsh still reaches the
next redirector; the integration body restores the real dir for the live
session via a one-shot precmd hook. When the user never set a ZDOTDIR we unset
it (rather than force $HOME) while sourcing, so their file sees exactly what a
real launch gives it and the `: ${ZDOTDIR:=~/.config/zsh}` relocate idiom still
fires. .zshenv recaptures a user-relocated ZDOTDIR so the classic tiny-~/.zshenv
layout keeps both its config and tty7's integration (previously it silently lost
the integration).

Verified end-to-end against real zsh for three layouts (config in $HOME,
unconditional relocate, and `-z`-guarded relocate): a second pane now hits the
plugin manager's cache instead of reinstalling, and runtime ZDOTDIR resolves to
the real dir. Adds three unit tests covering the redirector ordering, the
.zshenv recapture, and the runtime restore hook.

Fixes #15

Co-authored-by: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com>
2026-07-08 18:52:35 +08:00
l0ng-ai 0ee67092d5 chore(release): v0.6.1
Claude-Session: https://claude.ai/code/session_01ABey161AUxhgmJC3PRoYtF
v0.6.1
2026-07-08 17:43:37 +08:00
l0ng-ai c66a7fa014 fix(daemon): keep the local line editor out of remote ssh sessions (#28)
A remote shell that emits its own OSC 133 prompt marks — fish 4.x over
ssh, most visibly, which ships them on by default — made the daemon
report at_prompt=true, so tty7 engaged its local line editor and Tab ran
completion against the local filesystem instead of reaching the remote
shell.

The shell tty7 spawned only emits its OSC 133 marks while it is itself
the PTY's foreground process group (idle at its prompt). Reject a prompt
mark whenever a foreground command (ssh, a TUI, a nested shell) owns the
PTY — comparing the PTY's foreground pgid against the shell pid — so
keystrokes pass raw to whatever is really reading them. The normal local
case (shell idle ⇒ it is the foreground group) is untouched.

Fixes #26.

Claude-Session: https://claude.ai/code/session_01ABey161AUxhgmJC3PRoYtF
2026-07-08 17:41:22 +08:00
l0ng-ai bc3c1d599c chore(release): v0.6.0
Promote the [Unreleased] changelog to [0.6.0]: the "+" shell picker and the
Windows PowerShell 7 default, plus this cycle's fixes — native powerline
separators + bundled Hack fallback (#17), URL detection stopping at full-width
brackets (#20), PATH-probed shell detection for brew/nix installs (#18), and
stale-daemon restart-and-retry on upgrade (#22). Bump the crate version
0.5.0 → 0.6.0 so the built binary reports its real version to the update check.
v0.6.0
2026-07-08 13:36:56 +08:00
yanyaoerandyanyao 1c5a38652d fix(daemon): recover from stale shell daemon (#22)
Co-authored-by: yanyao <yanyao@xiaomi.com>
2026-07-08 13:30:47 +08:00
l0ng-aiandl0ng-ai f9b8f3b175 fix(shell): detect PATH-installed shells missing from /etc/shells (#23)
The Unix shell picker only enumerated $SHELL and /etc/shells, but
Homebrew and nix don't register what they install there (Homebrew only
suggests adding fish to /etc/shells, and few users do). A brew-installed
fish therefore never appeared in the "+" dropdown, even though the app
can spawn and shell-integrate it fine once selected.

Probe a curated set of well-known shells (fish, nu, pwsh, elvish, xonsh)
on PATH as a catch-all, after the /etc/shells entries. Registered shells
keep their /etc/shells paths via the existing basename dedupe; only the
unregistered leftovers are picked up from PATH. Uses the login-shell-
enriched PATH so Dock launches see Homebrew's prefix too.

Closes #18

Co-authored-by: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com>
2026-07-08 13:28:13 +08:00
l0ng-aiandl0ng-ai 6e4492b983 fix(terminal): stop bare-URL detection at full-width brackets glued after the link (#20)
A URL followed by a full-width parenthetical with no ASCII space, e.g.
`…/pull/343(fix/… → dev)`, leaked the bracket text into the link: the
whitespace split ran past the URL and trailing-punctuation trimming only
touches the token's end. URL characters are all ASCII (RFC 3986), so after
locating the scheme, truncate at the first non-URL char — a CJK character,
full-width bracket, arrow or emoji — which marks where the link ends. Interior
ASCII parens (Wikipedia-style URLs) are preserved. Adds regression tests.

Co-authored-by: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com>
2026-07-08 13:28:08 +08:00
l0ng-aiandl0ng-ai d39199987f fix(terminal): draw powerline separators natively, anchor bundled Hack in font fallbacks (#19)
Prompt themes (powerlevel10k, oh-my-posh, oh-my-zsh) render their
segment separators and status glyphs through whatever face the font
fallback chain resolves, and two failure modes followed (#17):

- Powerline separators (U+E0B0-E0BE) resolved from a fallback face
  render at *that face's* advance, not the cell width — narrow wedges
  that no longer meet their segment backgrounds, or tofu with no Nerd
  Font installed at all.
- A glyph nothing in the configured chain covers falls through to the
  OS cascade, which can serve a proportional glyph wider than the
  cell; paint_glyphs' per-cell clip then truncates it — the severed
  U+279C arrow in the report (pixel forensics: the cut is a zero-
  antialiasing vertical edge exactly one cell after the glyph origin).

Fix, the approach Warp (stretchable SVGs) and kitty (programmatic
glyphs) settled on:

- The eight solid powerline separators — sharp triangles E0B0/E0B2,
  round caps E0B4/E0B6, slants E0B8/BA/BC/BE — are now drawn as gpui
  fill paths sized to the exact cell: pixel-perfect for every
  font/size/line-height combination, no font dependency at all. The
  thin/outline variants stay on the font path (hairline strokes can't
  be a fill, and the bundled Hack covers the common ones).
- fallback_chain() appends the bundled "Hack" to every font fallback
  list, so prompt symbols (U+279C, U+276F, box drawing, the sharp
  wedges) resolve against a monospace-fitting face before ever
  reaching the OS cascade lottery.

Claude-Session: https://claude.ai/code/session_01ABey161AUxhgmJC3PRoYtF

Co-authored-by: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com>
2026-07-08 13:27:59 +08:00
l0ng-ai bf9410d903 Merge pull request #14 from l0ng-ai/feat/macos-option-as-alt
feat(input): treat Option as Alt/Meta on macOS behind a setting
2026-07-08 10:52:50 +08:00
l0ng-ai 20b590310a Merge pull request #16 from l0ng-ai/feat/shell-picker
feat(shell): detect installed shells, pick one from the "+" dropdown
2026-07-08 10:52:24 +08:00
l0ng-ai 92ce3ca3c1 fix(shell): notify after the shell probe lands so the dropdown refreshes
The background detect_shells() update set detected_shells without
cx.notify(), so an idle window kept rendering the pre-probe (empty)
list until something else forced a redraw. Flagged by CodeRabbit.

Claude-Session: https://claude.ai/code/session_01ABey161AUxhgmJC3PRoYtF
2026-07-07 23:43:34 +08:00
l0ng-ai cdb383d6ce feat(shell): detect installed shells, pick one from the "+" dropdown
The "+" button now opens a shell picker listing every shell found on
this machine — the login shell plus /etc/shells on Unix; PowerShell 7,
Windows PowerShell, cmd, Git Bash and WSL distributions on Windows — so
opening a tab in a different shell no longer requires hand-editing
config.json. The default entry leads the menu (Cmd+T still opens a
default tab in one keystroke), and splits inherit the pane's shell.

The Windows default shell now prefers PowerShell 7 when installed,
probed the way Warp does it (Program Files x64/x86/ARM, the Microsoft
Store shim, scoop, dotnet tools, then PATH), falling back to the
Windows PowerShell that ships with the OS.

Spawn precedence in the daemon is now: explicit per-spawn pick >
config.json `shell` > platform default. Wire compat is preserved across
GUI/daemon version skew: a default spawn keeps the legacy SPAWN frame
byte-for-byte, so an old daemon still serves a new GUI; only an
explicit pick uses the new SPAWN_SHELL kind, locked by a round-trip +
legacy-frame test.

Claude-Session: https://claude.ai/code/session_01ABey161AUxhgmJC3PRoYtF
2026-07-07 23:31:54 +08:00
l0ng-ai 60db1cee58 docs: update README and README.zh-CN to refine terminal description 2026-07-07 19:58:41 +08:00
l0ng-ai 0cdaf3a79a docs: add Discord community link, drop SECURITY.md
Add a Discord badge to the README badge row and a chat invite in the
Contributing section (both English and Simplified Chinese). Remove
SECURITY.md and its references; private vulnerability reporting stays
available through GitHub's repository setting.

Claude-Session: https://claude.ai/code/session_01ABey161AUxhgmJC3PRoYtF
2026-07-07 19:44:32 +08:00
l0ng-ai 6789f944ae feat(input): treat Option as Alt/Meta on macOS behind a setting
macOS gives the Option key two jobs a terminal can't serve at once: the
OS composes a special character (Option+B types ∫), while Meta bindings
need an ESC-prefixed chord (Option+B → ESC b, readline's backward-word).
A new `macos_option_as_alt` config (Settings → Terminal → Keyboard, off
by default) picks which one wins, matching Ghostty's `macos-option-as-alt`
and iTerm2's "Option as Meta".

The keystroke is reshaped once at the top of `on_key_down` — on: the
composed `key_char` is replaced with the base key so the encoder emits
`ESC` + the plain character (uppercased under Shift, like xterm's
metaSendsEscape); off: the alt bit is dropped so the composed character
goes out bare. Every consumer downstream (the ⌘ dispatcher, the prompt
editor, the PTY encoder, the Kitty encoder) then sees one consistent
story.

This also fixes two existing defects, both settings:

* The raw path used to emit `ESC ∫` for Option+B — an ESC prefix bolted
  onto the composed character, wrong under either reading of Option.
* The prompt editor swallowed Option+letter chords entirely: no composed
  character, no motion. Off now inserts the composed character; on maps
  M-b / M-f / M-d to the readline word motions (which also lights them
  up on Linux, where Alt is natively Meta but the chords were no-ops).

Known limitation: with the setting on and a non-ASCII IME input source
active (e.g. Pinyin), gpui routes printable keys — Option chords
included — to the IME first, which commits the composed character before
the key event reaches the view; Meta chords need an ASCII-capable source
(e.g. ABC). No regression: that state behaved identically before.

Closes #13
2026-07-07 18:42:47 +08:00
l0ng-ai 5ae867297f chore(release): v0.5.0
Promote the [Unreleased] changelog to [0.5.0] (Windows installer, startup
update check, ⌘K clear, ⌘⏎ fullscreen, right-click menu shortcut hints) and
add this release's fixes (right-click Select All matching ⌘A, Ctrl+R ASCII
keystrokes). Bump the crate version 0.3.0 → 0.5.0 so the built binary reports
its real version to the update check, and backfill the missing [0.3.0]/[0.4.0]
changelog compare links.
v0.5.0
2026-07-07 17:34:12 +08:00
l0ng-ai 930b2e1787 Merge pull request #11 from l0ng-ai/feat/menu-hints-reverse-search
Right-click menu shortcut hints + Ctrl+R ASCII keystroke fix
2026-07-07 17:32:01 +08:00
l0ng-ai 03fda9c33d feat(menu): show shortcut hints in the right-click menu
Copy / Paste / Select All / Find are dispatched inline in
`handle_cmd_shortcut` with no registered `KeyBinding`, so gpui-component's
PopupMenu couldn't auto-derive their hints the way it does for the
keybinding-backed items (Clear, Split, Maximize, New Tab, Close Pane) — the
menu showed shortcuts on some rows and nothing on others. Render the hints
ourselves via `menu_row_with_hint`, styled to match the auto-rendered ones,
instead of registering real bindings (which would risk the Ctrl+C SIGINT
fall-through on Windows/Linux). ⌘A / ⌘F are macOS-only, so those hints are
gated behind `mac_only`.

Also fix the "Select All" menu item diverging from ⌘A: both now go through
`select_all_contextual` (select the edited command line at the prompt,
otherwise the whole terminal buffer) so click and shortcut stay in sync.
2026-07-07 16:55:21 +08:00
l0ng-ai 4e9604ffdc fix(terminal): accept ASCII keystrokes in the Ctrl+R reverse-search prompt
The reverse-search query only took text from the IME commit path
(input_text -> push_query), so a CJK input source could type into it but a
plain ASCII source — and Linux, where prefers_ime_for_printable_keys is
false — delivered printable keys via on_key_down, where handle_key ignored
them. Ctrl+R opened, but the search box swallowed every keystroke.

Forward printable key_char events from handle_reverse_search_key into
push_query, mirroring the editor's existing key_char path; control/Cmd/Alt
chords and non-printable keys still fall through to the control-key handling.
Fixes ASCII typing on macOS and all typing on Linux. Reported on V2EX.

Claude-Session: https://claude.ai/code/session_01ABey161AUxhgmJC3PRoYtF
2026-07-07 16:54:29 +08:00
l0ng-ai 0770b55e60 Merge pull request #9 from l0ng-ai/feat/startup-update-check
feat: startup update check + Windows installer & console-window fix
2026-07-07 16:21:41 +08:00
l0ng-ai 47e47897ed style: cargo fmt 2026-07-07 16:16:35 +08:00
l0ng-ai 3900160b27 fix(update): address PR #9 review — tighten version parse + explicit timeout
Two CodeRabbit findings on core::update:

- parse_version now rejects malformed cores instead of truncating them:
  a doubled prefix (`vv0.3.1`) and extra components (`0.3.1.1`) no longer
  collapse to a valid triple, so an unrecognizable tag can't surface a
  bogus "update available". Uses strip_prefix('v') (not trim_start_matches)
  and rejects a 4th dot component. Added tests for both cases.
- The check now races an explicit 15s timeout (ReqwestClient only sets a
  connect timeout), so a connected-but-stalled response fails soft
  deterministically instead of sitting pending for the session.
2026-07-07 16:15:38 +08:00
l0ng-ai d0cf53522f feat(release): ship a Windows Inno Setup installer alongside the portable zip
bundle-windows.ps1 now compiles windows-installer.iss (ISCC is preinstalled
on windows-latest) from the same staged payload as the zip, producing
tty7-<version>-windows-x86_64-setup.exe: per-user install by default with an
all-users option, Start Menu shortcut, Apps uninstall entry, optional desktop
icon. Release workflow uploads the new artifact; READMEs and CHANGELOG updated.
2026-07-07 16:14:44 +08:00
l0ng-ai 12b515a659 fix(windows): hide console window in release builds
Set `windows_subsystem = "windows"` on release builds so launching the
packaged .exe doesn't flash a console window, while keeping it in debug
builds so println!/eprintln! stay visible during development.
2026-07-07 16:09:19 +08:00
l0ng-ai 8726f5a6dc feat: one-time "update available" modal, once per version
Build on the notify-only check: when a newer release is found, pop a
native modal dialog ("Update available — Download / Later") in addition
to the passive Settings -> About prompt. Download opens the Releases
page; tty7 still never self-updates.

- Pops exactly once per version: the shown version is recorded in a small
  update.json (mirroring session.json's load/save) and re-checked on the
  next launch, so we never nag twice for the same release.
- wait_for_window handles the startup race where the network check
  finishes before the window opens — poll briefly, then host the modal.
- open_releases_page moves to core::update as a shared free fn; the
  Settings -> About button now delegates to it (one opener, two callers).
- Fails soft as before: no window / disabled check / offline all skip the
  modal silently; the About prompt still appears whenever we're outdated.
2026-07-07 16:09:12 +08:00
l0ng-ai 296f71e18d feat: notify-only startup update check
Ask the GitHub releases API once, in the background, on GUI startup
whether a newer version has shipped and — if so — surface a "Download"
prompt in Settings → About that links to the Releases page. tty7 never
downloads or self-updates; the user installs by hand, exactly as the
README's Install section describes.

- core::update: UpdateStatus global, version parse/compare, and the
  GitHub fetch via reqwest_client. That reqwest+rustls stack is already
  compiled into the tree through gpui-component-assets, so this pins no
  new native code — it only exposes what we already build.
- config: check_for_updates toggle (default true) to skip the network
  call entirely for offline / privacy.
- Settings -> About: an "Updates" section with the version prompt, a
  Download button (default style, matching the sibling actions), and the
  toggle.
- Fails soft: no network, a rate-limit, a private/renamed repo, or an
  unparseable tag all collapse to "no prompt", logged at debug and never
  surfaced. The terminal opens the same whether or not GitHub is reachable.
2026-07-07 15:01:16 +08:00
l0ng-ai 5eb455f9d8 Refactor fullscreen toggle action for improved readability. Simplified the listener closure in the Tty7App implementation to enhance code clarity while maintaining functionality. v0.4.0 2026-07-07 12:05:42 +08:00
l0ng-ai c2472c3ab3 Enhance terminal functionality with new keyboard shortcuts and actions. Added ⌘K (Ctrl+K) for clearing the screen and scrollback, and ⌘⏎ for toggling fullscreen, while adjusting the maximize pane shortcut to ⌘⇧⏎. Updated keybindings and command palette to reflect these changes, ensuring a more intuitive user experience. 2026-07-07 12:01:18 +08:00
l0ng-ai b9d7a4e8f7 Update README and README.zh-CN to refine terminal description and features. Simplified language for clarity, emphasizing GPU rendering and performance metrics. Adjusted prompt feature descriptions for consistency and improved readability. 2026-07-07 11:57:59 +08:00
l0ng-ai e6efc457ab chore(release): v0.3.0 v0.3.0 2026-07-07 10:49:25 +08:00
l0ng-ai 5574b5662a Merge pull request #8 from l0ng-ai/fix/stale-tui-modes-and-exit-close
Fix stale TUI residue after ssh drops and auto-close exited panes
2026-07-07 10:47:12 +08:00
l0ng-ai 3cc95f5915 Close the pane when its shell exits
Typing `exit` (or Ctrl-D) left a dead "process exited" pane behind: the
view marked itself exited, but nothing ever closed the pane or tab.

The reader thread now records whether the pane ended with a *genuine*
child exit (`DaemonMsg::Exited`) as opposed to a daemon disconnect, and
the view surfaces that as a `ChildExited` gpui event. The app subscribes
at the single place all panes are built and closes the emitting leaf:
collapsing its split, or closing the tab when it was the only pane (the
last tab falls back to the home page). The daemon pane is killed so the
session drops it; panes that died while detached clean themselves up on
the next attach, when the daemon replays their exit.

A disconnect deliberately keeps the dead pane visible -- auto-closing
would silently discard (and kill) sessions that may still be alive
daemon-side.
2026-07-07 10:39:45 +08:00
l0ng-ai 2a74e5381b Scrub stale TUI modes when the shell returns to its prompt
An ssh session dropping mid-TUI (htop, vim, ...) leaves the pane wedged:
the remote app hid the cursor (DECTCEM), entered the alt screen and
enabled mouse reporting, and the restore sequences can never arrive. The
host shell's next prompt then renders with no cursor anywhere, mouse
clicks echo `0;19;42M`-style junk, and scrollback stays broken.

The client's reader thread now scrubs this residue when the daemon
reports a fresh prompt (OSC 133): at that position in the byte stream no
full-screen program owns the pane, so a stranded alt screen, hidden
cursor, mouse/focus reporting, or kitty keyboard flags are provably
stale. Each reset is emitted only when its mode is actually set
(`?1049l` performs a cursor restore when idle, so no blanket resets),
and bracketed paste / application cursor keys are left to the line
editor, which re-arms them on every read. Reattach self-heals the same
way -- the daemon replays the prompt state after the ring.
2026-07-07 10:35:56 +08:00
l0ng-ai 22e1ab1694 tty7: a GPU-rendered, daemon-backed terminal in pure Rust
tty7 is split into two Rust processes: a persistent daemon that owns the
shells and a GPU-rendered client that talks to it over a local socket.
Because the shells live in the daemon, quitting and reopening the app
leaves the session intact — detach and reattach, no tmux required.

- Persistent sessions — the daemon holds the PTYs and child processes, so
  closing a window or swapping in a new build never takes a shell down.
- Performance — an 11 MB `cat` completes in 95 ms and DOOM-fire renders at
  888 fps; the daemon drains the PTY at device speed off the render path.
- Shell-aware — new tabs and splits open in the current working directory;
  zsh, bash, fish, and PowerShell are set up automatically.
- Enhanced prompt — inline completion, syntax highlighting, history, and
  in-terminal search, with rich flag/subcommand signatures for common tools.
- Tabs, resizable splits, a command palette, click-to-open links, desktop
  notifications, eight themes, and CJK/IME input.

Native builds for macOS, Windows, and Linux.
Built on Zed's gpui and Alacritty's VT core.
2026-07-06 21:54:27 +08:00