Files
tty7/Cargo.toml
T
l0ng-ai ceb303aa6c fix(terminal): prefer the OS tokenizer over jieba for CJK selection
jieba's dictionary costs ~55 MB resident and ~130 ms to build, and it was
built eagerly on every terminal-view creation regardless of the
`smart_select` setting or whether the user ever selects CJK text.

macOS already ships a Chinese lexicon in CFStringTokenizer that matches
jieba on most prose, is locale-independent (identical output for current /
NULL / zh_CN / en_US), and segments Japanese and Korean properly where
jieba shreds them into single characters. Make the OS tokenizer the
primary path and keep jieba only as the fallback for platforms with no
such API:

- gate the dependency behind `cfg(not(target_os = "macos"))` so the
  embedded dictionary isn't even linked into the macOS build
- never warm eagerly; the first CJK double-click kicks the build off in
  the background and settles for the unsegmented run, so the UI thread
  never blocks on it
- skip jieba for runs containing kana or hangul, where selecting the
  whole run beats per-character tokens

Also honor `Config::smart_select` in the prompt's command editor, which
did bracket pairing, CJK segmentation and mixed-script narrowing even
with the Settings toggle off.
2026-07-18 21:41:58 +08:00

288 lines
14 KiB
TOML

[package]
name = "tty7"
version = "26.7.1"
edition = "2024"
description = "A terminal workbench: shells, persistent sessions, SSH, coding agents — GPU-rendered on Zed's gpui, pure Rust"
repository = "https://github.com/l0ng-ai/tty7"
license = "Apache-2.0"
readme = "README.md"
publish = false
default-run = "tty7"
[[bin]]
name = "tty7"
path = "src/main.rs"
[dependencies]
gpui = { workspace = true }
gpui_platform = { workspace = true }
gpui-component = { workspace = true }
gpui-component-assets = { workspace = true }
anyhow.workspace = true
log.workspace = true
# Smart double-click selection patterns (URL/email/path). Already in the tree
# transitively, so pinning it here adds no new native code.
regex = "1"
smol.workspace = true
smallvec.workspace = true
serde = { workspace = true }
serde_json.workspace = true
# SSH connection-manager data layer (`core::ssh_profile` / `core::keychain`).
# `uuid` mints stable profile ids (v4) and serde-serializes them as strings;
# `sha2` hashes private-key file contents to the sha512-hex account key that
# passphrases are stored under in the OS keychain (per PRD §7.2). Both are already
# in the tree transitively, so pinning them here adds no new native code. `keyring`
# 4.x is the OS credential-vault backend: its default `v1` feature auto-selects the
# platform store (macOS Keychain / Windows Credential Manager / Linux Secret
# Service), so no per-platform feature guards are needed.
uuid = { version = "1", features = ["v4", "serde"] }
sha2 = "0.10"
keyring = "4"
# Theme files. tty7 themes are authored as YAML (`~/.config/tty7/themes/*.yaml`,
# our own schema); `plist` parses imported iTerm2 `.itermcolors` schemes (XML
# plist) so the large iTerm color-scheme ecosystem drops straight in. serde_yaml
# is already in the tree transitively, so it pins no new code; plist is pure Rust.
serde_yaml = "0.9"
plist = "1"
# Clipboard-image paste (`terminal::view`). When the clipboard holds a screenshot
# and a coding-agent TUI (Claude Code &co.) is in the pane, off macOS we stage the
# image to a temp file and paste its path — agents attach an image path the same way
# they do a drag-drop. Windows screenshots arrive as BMP (`CF_DIB`), which agent
# vision won't accept, so we transcode to PNG. `image` is already in the tree via
# gpui's own clipboard code, so pinning it here adds no new native code.
image = "0.25"
# HTTP client for the startup update check (`core::update`): one GET to the
# GitHub releases API to see if a newer version has shipped. `reqwest_client`
# wraps Zed's `zed-reqwest` fork behind gpui's `http_client` trait (re-exported
# as `gpui::http_client`) and manages its own tokio runtime. That reqwest+rustls
# stack is *already* compiled into the tree via `gpui-component-assets`, so this
# pins no new native code — it only exposes what we're already building. Pinned
# to gpui's rev so the shared `http_client`/`zed-reqwest` versions stay aligned.
reqwest_client = { git = "https://github.com/zed-industries/zed", rev = "1d217ee39d381ac101b7cf49d3d22451ac1093fe" }
# Zed's fork of alacritty_terminal — same rev Zed pins. Used by the *client*
# (`terminal::remote`) for the VT parser + grid (`Term`/`ansi::Processor`) that
# renders the mirror. The daemon's PTY itself is driven by `portable-pty` below.
alacritty_terminal = { git = "https://github.com/zed-industries/alacritty", rev = "fcf32feacb367b75ec84dd40f041e4fd411d3cc1" }
# Desktop notifications driven by OSC 9 / OSC 777 escape sequences. Cross-platform;
# the macOS backend uses the deprecated NSUserNotification (weak — a completion
# toast is fine, revisit mac-notification-sys if it proves unusable).
notify-rust = "4"
# Filesystem watcher for live config reload: watches `config.json` and reloads
# `Config` + re-applies the theme without a restart.
notify = "8"
# Cross-platform PTY for the daemon: a Unix pty on Unix, ConPTY on Windows, behind
# one blocking `Read`/`Write`/`resize` API. This is what lets `daemon::pane` share
# a single code path across platforms instead of hand-rolling fd/ioctl/signal code.
portable-pty = "0.8"
# Native (pure-Rust) SSH client for the daemon's russh session engine
# (`daemon::ssh`). Replaces shelling out to the system `ssh` binary for managed
# connections: it gives us the protocol stack that GUI-hosted auth, host-key
# prompts, in-memory connection reuse, and (later) SFTP / native port-forwarding
# all need. 0.62 folded the old `russh-keys` crate in as `russh::keys` (key
# parsing + ssh-agent client). The async russh session lives on a small tokio
# runtime owned by `daemon::ssh`; the rest of the daemon stays std-threads and
# bridges to it through blocking `Read`/`Write` adapters.
russh = "0.62"
# SFTP client for the native SSH engine (`daemon::ssh::sftp`, Workstream 5).
# Not part of russh proper: `russh-sftp` drives the SFTP subsystem over any
# AsyncRead+AsyncWrite stream, which a russh session channel provides via
# `Channel::into_stream()`. Version-independent of russh (it only needs the
# channel byte stream), so it rides the same tokio runtime `daemon::ssh` owns.
russh-sftp = "2"
# tokio powers only the russh session engine — a single runtime `daemon::ssh`
# owns. The daemon's PTY/reader/writer threads remain std threads and never touch
# it; they cross into async through bounded/unbounded channels (the blocking
# `Read`/`Write` adapters in `daemon::ssh::session`).
tokio = { version = "1", features = [
"rt-multi-thread",
"net",
"io-util",
"sync",
"time",
"macros",
"process",
# `fs` powers the local side of SFTP transfers (`daemon::ssh::sftp`): async
# file/dir IO on the daemon process's own filesystem during upload/download.
"fs",
] }
# SIMD byte search for the OSC tokenizer's Ground/Ignore fast paths — the
# sniffers sit on the full-throughput output stream (100+ MB/s at full drain),
# where a per-byte state machine costs a measurable slice of the reader loop.
# Already in the tree transitively (vte et al.), so this pins no new code.
memchr = "2"
# System tray / menu bar status item (`ui::tray`). Rasterizes the bundled SVG
# logo into the tray bitmap at runtime — gpui's own SVG path only yields a
# tinted alpha mask, not raw RGBA. Pinned to the exact version already in the
# tree via gpui (0.45.x), so this adds no new native code; default features off
# drops the text/font machinery our icon SVGs don't use.
resvg = { version = "0.45", default-features = false }
# The tray icon itself, macOS + Windows: tauri's `tray-icon` (NSStatusItem /
# Shell_NotifyIcon via objc2 / windows-sys, both already in the tree). Linux is
# deliberately NOT on this crate — its Linux backend needs GTK + libappindicator,
# which tty7's AppImage doesn't bundle and the x11/wayland gpui backends don't
# pull. Linux instead uses `ksni` below.
[target.'cfg(any(target_os = "macos", target_os = "windows"))'.dependencies]
tray-icon = "0.24"
# `setsid` (daemon detach) and the macOS foreground-process proc queries are the
# only libc users left, both Unix-only — so the dep is Unix-only too.
[target.'cfg(unix)'.dependencies]
libc = "0.2"
libgssapi = "0.11"
# The Windows GUI⇄daemon transport is loopback TCP, which (unlike a Unix socket)
# any local process can connect to — so the daemon authenticates each connection
# against a random token it writes into the user-private port file. `getrandom`
# is the OS CSPRNG that mints that token; already in the tree transitively, so
# this pins no new code. Windows-only, matching the transport it guards.
[target.'cfg(windows)'.dependencies]
getrandom = "0.3"
# Toolhelp process enumeration + `TerminateProcess`, used by `daemon::winproc` to
# title a pane by its foreground command and to tear down a shell's descendant
# tree on hangup (ConPTY's `kill` only reaches the shell itself). Already in the
# tree via gpui's Windows backend, so this pins no new code. Windows-only.
windows-sys = { version = "0.59", features = [
"Win32_Foundation",
"Win32_System_Console",
"Win32_System_Diagnostics_ToolHelp",
"Win32_System_Threading",
] }
# Embeds `assets/favicon.ico` into the `.exe` so Windows shows the tty7 logo in
# the taskbar / window / Explorer (macOS gets its icon from the `.app` bundle via
# bundle.sh instead). Only needed at build time on Windows — see build.rs.
[target.'cfg(windows)'.build-dependencies]
winresource = "0.1"
# gpui only *reads* the macOS window appearance; it never sets it. We force the
# app appearance to match the active theme (see `ui::theme::sync_native_appearance`)
# so the native traffic-light buttons render in the right light/dark style.
[target.'cfg(target_os = "macos")'.dependencies]
# CFStringTokenizer FFI for dictionary-based CJK word segmentation on
# double-click (`terminal::smart_select`). Already in the tree transitively,
# and it makes jieba unnecessary here — see the non-macos section below.
core-foundation = "0.10"
objc2 = "0.6"
objc2-app-kit = { version = "0.3", features = ["NSApplication", "NSResponder", "NSAppearance", "NSGraphics", "NSImage"] }
# NSData feeds the runtime Dock icon for bare (non-bundled) binaries — see
# `set_dock_icon_for_bare_binary` in main.rs.
objc2-foundation = { version = "0.3", features = ["NSData"] }
# Dictionary-based Chinese word segmentation for double-click selection
# (`terminal::smart_select`), as a *fallback* where the OS has no tokenizer of
# its own. macOS is excluded on purpose: CFStringTokenizer segments Chinese
# about as well (and Japanese/Korean far better) at zero cost, while jieba's
# table costs ~55 MB resident once built and ~2 MB of binary for the embedded
# dictionary. Keeping the dep off macOS means that dictionary isn't even
# linked into the build that can't use it.
[target.'cfg(not(target_os = "macos"))'.dependencies]
jieba-rs = "0.10"
# x11/wayland are the Linux windowing backends; only pull them on Linux. The
# Windows backend (`gpui_windows`) and macOS backend are selected by gpui_platform
# itself via `cfg`, so no feature is needed for them.
[target.'cfg(target_os = "linux")'.dependencies]
gpui_platform = { workspace = true, features = ["x11", "wayland"] }
# Linux tray (`ui::tray`): pure-Rust StatusNotifierItem over DBus (zbus — already
# in the tree). `blocking` + `async-io` gives a synchronous handle without
# requiring the app to own a tokio runtime; ksni runs its own service thread. On
# desktops without an SNI host (bare GNOME without the AppIndicator extension)
# spawning fails and the tray is silently absent — the app is unaffected.
ksni = { version = "0.3.6", default-features = false, features = ["blocking", "async-io"] }
# gpui's `test-support` unlocks `#[gpui::test]` + `TestAppContext`, the headless
# App/Window harness the view/event tests run on. Dev-only: the feature merges
# into test builds and never reaches a release binary.
[dev-dependencies]
gpui = { workspace = true, features = ["test-support"] }
# `test-util` unlocks tokio's paused clock (`start_paused`) so the ssh prompt
# broker's timeout/retry tests run instantly instead of in real time. Dev-only.
tokio = { version = "1", features = ["test-util", "macros", "rt"] }
[lints]
workspace = true
# ---- Standalone workspace mirroring gpui-component's pins so the git/source
# ---- caches are shared and versions stay aligned. ----
[workspace]
members = ["."]
[workspace.package]
edition = "2024"
[workspace.dependencies]
# Our fork's `tty7` branch carries the local customizations tty7 relies on:
# `PopupMenu::with_size` plus the 1px hairline menu separator. The exact commit
# is still pinned by Cargo.lock. For co-developing the UI crate, point these
# back at a sibling checkout: `path = "../gpui-component/crates/{ui,assets}"`.
gpui-component = { git = "https://github.com/l0ng-ai/gpui-component", branch = "tty7", version = "0.5.2" }
gpui-component-assets = { git = "https://github.com/l0ng-ai/gpui-component", branch = "tty7", version = "0.5.1" }
gpui = { git = "https://github.com/zed-industries/zed", rev = "1d217ee39d381ac101b7cf49d3d22451ac1093fe" }
# Base features are cross-platform (`font-kit`, `runtime_shaders` only map to the
# macOS backend; they're no-ops elsewhere). The Linux-only `x11`/`wayland`
# backends are added by the `cfg(target_os = "linux")` dependency entry in the
# package manifest, so they never reach the Windows/macOS builds.
gpui_platform = { git = "https://github.com/zed-industries/zed", rev = "1d217ee39d381ac101b7cf49d3d22451ac1093fe", features = ["font-kit", "runtime_shaders"] }
anyhow = "1"
log = "0.4"
serde = { version = "1.0.219", features = ["derive"] }
serde_json = "1"
smallvec = "1"
smol = "2"
[patch.crates-io]
# Temporary until upstream russh releases gssapi-with-mic client auth support
# (https://github.com/Eugeny/russh/pull/737) — remove this patch and take the
# crates.io release once it lands. Pinned to an exact rev (never a branch):
# russh is the SSH protocol layer handling user credentials, and a moving
# branch on a third-party fork could change what `cargo update` builds.
russh = { git = "https://github.com/ayamir/russh", rev = "0d1d073350ed823069252075cbf3db9672d5b490" }
[workspace.lints.clippy]
dbg_macro = "deny"
todo = "deny"
type_complexity = "allow"
[profile.dev]
codegen-units = 16
debug = "limited"
split-debuginfo = "unpacked"
[profile.dev.package]
resvg = { opt-level = 3 }
rustybuzz = { opt-level = 3 }
taffy = { opt-level = 3 }
ttf-parser = { opt-level = 3 }
smol = { opt-level = 3 }
gpui = { opt-level = 3 }
gpui_platform = { opt-level = 3 }
gpui_macros = { opt-level = 3 }
# The VT parser + grid run on every PTY byte; at opt-level 0 a `cat bigfile`
# crawls under `cargo dev`.
alacritty_terminal = { opt-level = 3 }
# The render/parse hot path crosses the tty7 ↔ alacritty_terminal ↔ gpui crate
# boundaries, so cross-crate inlining (thin LTO, like Zed ships) buys real
# throughput there; single codegen unit for the same reason.
[profile.release]
lto = "thin"
codegen-units = 1