Git 与 ripgrep 为可选依赖,不装则源码控制和工作区搜索不可用;文件类型图标需终端安装 Nerd Font 且默认关闭;从源码构建会编译开发分支代码,可能不如正式版稳定。
ttt
eugenioenkoMIT活跃维护在终端里跑的代码编辑器,用 TUI 做出接近图形界面的操作手感,单二进制零配置,内置 LSP、语法高亮和 Git 集成,适合不想离开命令行的开发者。
TTT Editor (Terminal Text Tool): A real alternative to VS Code, Zed, and Sublime that runs in your terminal. A TUI that feels like GUI. Single binary, zero config.
brew tap eugenioenko/ttt && brew trust eugenioenko/ttt && brew install ttt
curl -sSfL https://raw.githubusercontent.com/eugenioenko/ttt/main/install.sh | shREADME 未说明硬件要求;提供 Linux、macOS、FreeBSD、Android Termux、Windows 的预编译二进制,终端中运行。
终端里跑的类 GUI 代码编辑器,单二进制零配置,可替代 VS Code 等重型 IDE
在终端里跑的代码编辑器,用 TUI 做出接近图形界面的操作手感,单二进制零配置,内置 LSP、语法高亮和 Git 集成,适合不想离开命令行的开发者。
核心能力
- 用纯 Go 移植的 TextMate 引擎做 124 种语言语法高亮
- 支持多光标编辑、代码折叠、查找替换与 LSP 补全诊断
- 内置多根工作区、文件树与 ripgrep 驱动的全局搜索
- 侧边栏集成 Git 暂存、提交、拉取推送与 diff 视图
- 可直接用 ttt 命令打开 GitHub PR 进行评审
容易踩的坑
- v1.11.0最新正式版2026-11-06 · 2026-11-06 04:52 UTC
v1.11.0
Another Thursday, a new ttt release. Thank you all 🙏
v1.11.0 is here, with 49 pull requests and patches from 12 community contributors. Thank you to everyone who tried ttt, reported issues, reviewed changes, and sent patches. This release makes searching smoother, adds more editing controls, and brings clearer mouse feedback throughout the editor.
Highlights
- Smoother find and replace: Start file or workspace searches from selected text, switch between Find and Replace without losing your input, and keep search highlights in sync as you edit. Paste and replace-bar mouse controls work more reliably too.
- More editing controls: Choose syntax highlighting with Change Language Mode or by clicking the language name in the status bar. Add cursors above or below with Alt+Shift+Up/Down for quick edits across several lines.
- Clickable links: Hold Ctrl to highlight web URLs and local file references, then Ctrl+click to open them. File references can take you straight to a line and column, including from diff views.
- Better mouse interactions: Context-aware pointer shapes show clickable controls, text, resize dividers, and dragging in supported terminals. Scroll horizontally with Alt+wheel, and drag to select text in input fields. Pointer shapes can be disabled in settings.
- Keep working in one editor: With
reuseWindowenabled, command-line folder arguments now add folders to the running editor's workspace while preserving open tabs and unsaved edits. Commands launched from the integrated terminal prefer their parent editor. - More community plugins: The registry now includes OpenTDF, PostgreSQL, SQLite, PR Review, Catppuccin Mocha, a Linux desktop launcher, C/C++ header highlighting, and the TypeScript 7+ native language server.
- Reliability fixes: LSP snippet placeholders expand correctly, language-server positions handle UTF-16 offsets, undo history preserves save points, and ttt warns when an open file is deleted externally.
Building from source: Go 1.25 or newer is now required.
More
What's Changed
- feat(plugins): list Catppuccin Mocha theme plugin by @salmonumbrella in https://github.com/eugenioenko/ttt/pull/1240
- feat(plugins): list the Linux desktop launcher plugin by @SimonOcampo1 in https://github.com/eugenioenko/ttt/pull/1267
- chore(plugins): list OpenTDF, PostgreSQL, SQLite and PR Review by @eugenioenko in https://github.com/eugenioenko/ttt/pull/1287
- fix(terminal): add symmetric configurable padding by @Lzcwbcgs in https://github.com/eugenioenko/ttt/pull/1290
- fix(lsp): expand snippet placeholders instead of inserting them literally by @eduplessis in https://github.com/eugenioenko/ttt/pull/1298
- fix(plugin): clean up debug plugin replacements by @Aaqibhafeezkhan in https://github.com/eugenioenko/ttt/pull/1288
- fix(explorer): open the clicked compact-folder segment by @huskyhao in https://github.com/eugenioenko/ttt/pull/1284
- fix(find): make the replace bar's previous, next and close buttons clickable by @jayhemnani9910 in https://github.com/eugenioenko/ttt/pull/1299
- fix(search): route paste to the focused replace bar input by @jayhemnani9910 in https://github.com/eugenioenko/ttt/pull/1300
- Feat(sidebar): Add icon display options for sidebar tabs by @Nandaleio in https://github.com/eugenioenko/ttt/pull/1307
- fix(cli): prefer parent editor and report forwarding destination by @Lzcwbcgs in https://github.com/eugenioenko/ttt/pull/1301
- fix(lsp): convert UTF-16 position offsets at editor boundaries by @huskyhao in https://github.com/eugenioenko/ttt/pull/1257
- fix(undo): support balanced nested transactions by @SatriaBagusNurhadianto in https://github.com/eugenioenko/ttt/pull/1256
- fix(plugin): deliver sys.spawn callbacks in order, on_exit last by @SimonOcampo1 in https://github.com/eugenioenko/ttt/pull/1270
- docs: clarify
- v1.10.02026-10-30 · 2026-10-30 01:14 UTC
查看更新说明
v1.10.0
New Thursday, new ttt release. Thank you all 🙏
v1.10.0 is here, with 81 pull requests and patches from 21 community contributors, including 13 first-time contributors. Thank you to everyone who tried ttt, reported issues, reviewed changes, and sent patches. This release brings more editing controls, easier file browsing, and new tools for the plugins you're building.
Highlights
- Editor improvements: Opt-in bracket and quote auto-closing, multi-cursor copy, cut, and paste, and an option to invert selection colors. Ctrl+D now brings each newly selected occurrence into view.
- Better word wrap: Set a maximum wrap column, with separate settings for each language. Up and Down now move by visible screen row when wrapping is enabled.
- Reuse a running editor: Enable
reuseWindowto open command-line files in the newest running editor using the same config directory. It is off by default and requires a restart;--new-windowopens a separate editor. - Markdown improvements: Expand and collapse
<details>sections, with fixes for nested lists and quotes, list numbering, wrapped prefixes, wide tables, and hard line breaks. - Richer plugin interfaces: Tabs and pager widgets, bordered and password inputs, table row actions and horizontal scrolling, and text selection and copying in plugin panels. Plugins can also read and navigate diff tabs.
More
What's Changed
- feat(plugins): add ttt-mcp to the featured community registry by @eugenioenko in https://github.com/eugenioenko/ttt/pull/1117
- fix(widgets): measure title text and badge by display width by @somaz94 in https://github.com/eugenioenko/ttt/pull/1116
- fix(editor): bring newly added Ctrl+D selection into view by @Hrushikesh-ramilla in https://github.com/eugenioenko/ttt/pull/1122
- fix(terminal): close the pty exactly once by @ManManavadaria in https://github.com/eugenioenko/ttt/pull/1121
- fix(plugins): fall back to root README when plugin documentation is missing by @iamcipherdev in https://github.com/eugenioenko/ttt/pull/1119
- fix(plugins): keep button padding and refresh reused stack layout by @eugenioenko in https://github.com/eugenioenko/ttt/pull/1137
- fix(widgets): scroll views fit stack content to their width by @eugenioenko in https://github.com/eugenioenko/ttt/pull/1138
- feat(plugins): crypto.base64_encode and base64_decode by @eugenioenko in https://github.com/eugenioenko/ttt/pull/1139
- feat(plugins): ttt.debounce by @eugenioenko in https://github.com/eugenioenko/ttt/pull/1140
- feat(plugins): tabs widget by @eugenioenko in https://github.com/eugenioenko/ttt/pull/1143
- feat(plugins): pager widget by @eugenioenko in https://github.com/eugenioenko/ttt/pull/1142
- feat(ui): add option to invert colors for selection (#633) by @Hrushikesh-ramilla in https://github.com/eugenioenko/ttt/pull/1131
- fix(buffer): keep an unsaved line ending change dirty after undo by @jayhemnani9910 in https://github.com/eugenioenko/ttt/pull/1124
- feat(plugins): open_tab id and on_close by @eugenioenko in https://github.com/eugenioenko/ttt/pull/1141
- feat(plugins): table on_activate, selected, row_styles, loading, empty, inset by @eugenioenko in https://github.com/eugenioenko/ttt/pull/1144
- feat(plugins): tree and list badge_right and inset by @eugenioenko in https://github.com/eugenioenko/ttt/pull/1145
- fix(plugins): remove the duplicate Inset field by @eugenioenko in https://github.com/eugenioenko/ttt/pull/1169
- feat(plugins): hstack align by @eugenioenko in https://github.com/eugenioenko/ttt/pull/1146
- feat(plugins): bordered, titled and password inputs by @eugenioenko in https://github.com/eugenioenko/ttt/pull/1148
- feat(plugins): box title and width by @eugenioenko in https://github.com/eugenioenko/ttt/pull/1147
- feat(plugins): button disabled by @eugenioenko in https://github.com/eugenioenko/ttt/pull/1149
- feat(plu
- v1.9.02026-10-23 · 2026-10-23 06:40 UTC
查看更新说明
v1.9.0
Thank you 🙏
This is our biggest release so far, with patches from 30 community contributors. Thank you to everyone who tried ttt, reported issues, reviewed changes, and sent patches. Your feedback and the plugins you're building continue to shape the editor.
Also, big thanks to @aahoughton for identifying and fixing security issues in the plugin sandbox
Highlights
- Two-way plugin RPC: We saw people building ttt plugins that needed fast communication with other binaries, so we added two-way RPC to let plugins and external tools exchange requests and responses.
- Richer plugins: Custom dialogs, multiline inputs, editor decorations, terminal control, regex, and native multi-file diff views.
- Plugin discovery and permissions: A Featured section, new community plugins, and tighter filesystem and network permission handling.
- Editor improvements: Open piped stdin in a tab, opt-in Save All and Quit, customizable icons, and better controls for large files.
- Git improvements: Create branches from Changes, navigate detail controls by keyboard, and view commit messages as Markdown.
- Reliability fixes: Search and replace, Save As, undo cursor restoration, LSP formatting, and terminal shutdown.
- Platform support: Android ARM64 and Windows ARM64 builds, FreeBSD installer support, and downloadable checksums.
More
What's Changed
- feat(plugins): add pdf-viewer to the community registry by @sercanatalik in https://github.com/eugenioenko/ttt/pull/836
- docs: document Scoop installation on Windows by @premiuslol in https://github.com/eugenioenko/ttt/pull/911
- fix(editor): ask before Save As replaces an existing file by @jayhemnani9910 in https://github.com/eugenioenko/ttt/pull/910
- feat(plugin): add ttt.clipboard_read behind a clipboard.read permission by @peterheo in https://github.com/eugenioenko/ttt/pull/912
- fix(editor): apply final newline and trim settings when saving a new file by @jayhemnani9910 in https://github.com/eugenioenko/ttt/pull/909
- refactor(app): remove unused working tree wrapper by @dannyward630 in https://github.com/eugenioenko/ttt/pull/922
- Update windows install order README.md by @eugenioenko in https://github.com/eugenioenko/ttt/pull/923
- feat(plugin): scope fs.read and fs.write to manifest paths by @eugenioenko in https://github.com/eugenioenko/ttt/pull/908
- fix(lsp): build valid file URIs for Windows paths by @eugenioenko in https://github.com/eugenioenko/ttt/pull/935
- docs(plugins): list missing permissions by @danielmrdev in https://github.com/eugenioenko/ttt/pull/928
- fix: send Ctrl+K and chord keys to focused terminal by @ulrichheringer in https://github.com/eugenioenko/ttt/pull/924
- fix: recompute find matches after buffer edits by @ulrichheringer in https://github.com/eugenioenko/ttt/pull/925
- fix(editor): ask before Save As replaces a dangling symlink or hard link by @adamsiwiec1 in https://github.com/eugenioenko/ttt/pull/926
- fix(undo): restore the cursor when undoing line commands by @mangeshraut712 in https://github.com/eugenioenko/ttt/pull/927
- feat(plugins): add omarchy-theme to the community registry by @danielmrdev in https://github.com/eugenioenko/ttt/pull/929
- feat(plugins): show file types on plugin details by @danielmrdev in https://github.com/eugenioenko/ttt/pull/930
- fix(search): scope find results to active tab by @danielmrdev in https://github.com/eugenioenko/ttt/pull/931
- feat(git): add New Branch action to Changes panel by @danielmrdev in https://github.com/eugenioenko/ttt/pull/932
- feat(ui): show file icons across editor surfaces by @danielmrdev in https://github.com/eugenioenko/ttt/pull/934
- chore: remove audit docs by @eugenioenko in https://github.com/eugenioenko/ttt/pull/941
- fix(search): show no matches on image and binary tabs by @mangeshraut712 in https://github.com/eugenioenko/ttt/pull/94
- v1.8.02026-10-15 · 2026-10-15 04:03 UTC
查看更新说明
v1.8.0
Thank you 🙏
v1.8.0 is here, with patches from 17 community contributors. ttt grows thanks to the support of its community, and every report, review and patch is truly appreciated.
This release brings editable diffs, pull diagnostics for language servers that need them, image support for plugin system, and much faster editing of very large files.
I aim to release every two weeks, but with this many contributions coming in, holding them that long stopped making sense, so this release ships a week early. Thank you all for the support that made that happen.
Highlights
Edit your changes right in the diff
"Open Changes" opens the file itself in diff mode: edit, save and undo as in any tab while the diff follows each keystroke. Every diff view is now a real editor pane, staged and unstaged changes open separately like in VS Code, and git changes are marked on the scrollbar.
Language servers
ttt pulls diagnostics from servers that support it, so TypeScript 7's
tsc --lspnow shows errors. Completion, hover and protocol handling got a round of fixes.Performance
Redraws allocate up to 80% less, jumping to the end of a long file no longer stalls on highlighting, and very large files open, wrap and take typing much faster with less memory. A 256 MiB log opens in 127 ms instead of 201 ms, and word wrap on it takes 1.3 s instead of 3.1 s.
Plugins can open binary files and draw images
Plugins can register handlers for binary file types and draw images on terminals with the Kitty graphics protocol. ttt-pdf uses both to show PDF pages. See the plugin authoring guide.
Also in this release
- Extensionless scripts get highlighting from their shebang line.
editor.diffColorGutterNumbersis off by default, so line numbers on changed rows are now plain.- Replace in Files no longer overwrites unsaved edits.
- Many git fixes: permalinks, blame of unsaved edits, special-character paths, and gutter marks in CRLF and gitignored files.
- Windows install with Scoop.
More
- feat(tabs): add Reveal in Explorer to the tab context menu by @omar3903 in https://github.com/eugenioenko/ttt/pull/747
- ci: fail when Go code isn't gofmt-formatted by @eugenioenko in https://github.com/eugenioenko/ttt/pull/746
- test(plugin): run the SSRF protection test without DNS lookups by @eugenioenko in https://github.com/eugenioenko/ttt/pull/753
- chore(plugins): remove cheat sheet from community registry by @eugenioenko in https://github.com/eugenioenko/ttt/pull/754
- fix(ui): use ✕ for the editor tab close button by @Detoy in https://github.com/eugenioenko/ttt/pull/752
- plugin: no-op Replace on identical text; reject invalid ranges by @eugenioenko in https://github.com/eugenioenko/ttt/pull/538
- ci(bench): benchmark diff views alongside the editor by @eugenioenko in https://github.com/eugenioenko/ttt/pull/764
- test(e2e): time open changes without git's process time by @eugenioenko in https://github.com/eugenioenko/ttt/pull/765
- refactor(buffer): record line changes from every write by @eugenioenko in https://github.com/eugenioenko/ttt/pull/771
- perf: cut per-frame allocations in editor rendering by @eugenioenko in https://github.com/eugenioenko/ttt/pull/773
- fix(diff): drop the stray trailing row and cut diff allocations by @eugenioenko in https://github.com/eugenioenko/ttt/pull/776
- feat(app): open staged and unstaged changes like VS Code by @eugenioenko in https://github.com/eugenioenko/ttt/pull/777
- docs(readme): replace contributor lists with avatars by @eugenioenko in https://github.com/eugenioenko/ttt/pull/780
- refactor(ui): one incremental row layout for wrap and folds by @eugenioenko in https://github.com/eugenioenko/ttt/pull/781
- feat(editor): ctrl+home/ctrl+end go to the start/end of t
- v1.7.12026-10-09 · 2026-10-09 04:10 UTC
查看更新说明
What's Changed
- feat(explorer): reveal the active file in the explorer by @mshafir in https://github.com/eugenioenko/ttt/pull/743
- feat(lsp): add LSP: Show Server Log command by @Detoy in https://github.com/eugenioenko/ttt/pull/741
- feat(editor): add Trim Trailing Whitespace command by @Detoy in https://github.com/eugenioenko/ttt/pull/740
- Show the Nerd Font chevron for menu items with submenus by @LetMeSleep8h in https://github.com/eugenioenko/ttt/pull/728
- fix(plugin): disable git credential prompts in tests for plugin install and update by @eugenioenko in https://github.com/eugenioenko/ttt/pull/748
New Contributors
- @mshafir made their first contribution in https://github.com/eugenioenko/ttt/pull/743
- @Detoy made their first contribution in https://github.com/eugenioenko/ttt/pull/741
Contributors & Acknowledgments
Thank you everyone who contributed for this release, ttt is better because of your work :pray:
Also special thanks to reddit user
Adn0nnymouswho reported an issue with AUR build pipeline where during testing a git clone was performed and git asked for github credentials to clone a repo that doesn't exist and intentionally fail. All Go tests have been verified to not make any network calls. Follow up here https://github.com/eugenioenko/ttt/issues/749Next Release: October 22
I've been trying to release every two weeks on Thursdays. This was an exceptional fast follow-up because I did not want awkward AUR build behavior
Full Changelog: https://github.com/eugenioenko/ttt/compare/v1.7.0...v1.7.1
用户点评
登录用户的真实使用记录,可按版本和硬件筛选还没有人点评。第一个说出真实体验的人最有价值。
TTT Editor: Terminal Text Tool
The IDE that lives in your terminal. A TUI that feels like GUI. A real alternative to VS Code, Zed, and Sublime that happens to run in your terminal. Single Go binary, zero config.

Full documentation lives in docs-web/src/content/docs/ (published at tttedit.dev).
Installation
Optional dependencies
Neither tool is required to install or use ttt for basic editing. They enable additional functionality:
Quick Install MacOS (brew)
brew tap eugenioenko/ttt
brew trust eugenioenko/ttt
brew install ttt
Quick Install Linux, macOS, FreeBSD, & Android (Termux)
curl -sSfL https://raw.githubusercontent.com/eugenioenko/ttt/main/install.sh | sh
The script downloads the latest release binary for your OS/architecture and installs it to ~/.local/bin on Linux, /usr/local/bin on macOS and FreeBSD, or $PREFIX/bin in Android Termux (arm64). To install elsewhere:
curl -sSfL https://raw.githubusercontent.com/eugenioenko/ttt/main/install.sh | INSTALL_DIR="$HOME/bin" sh
Set INSTALL_DIR on sh so the installer receives it.
Arch Linux (AUR)
yay -S ttt
NixOS
Available in nixpkgs:
environment.systemPackages = [ pkgs.ttt ];
Or install directly from the repo flake (tracks the development branch):
nix run github:eugenioenko/ttt
Quick Install Windows (scoop)
scoop install ttt
Go Install
Requires Go 1.25 or newer:
go install github.com/eugenioenko/ttt/cmd/ttt@latest
This installs the ttt binary to your $GOPATH/bin (or $HOME/go/bin by default). Make sure that directory is in your PATH.
Download Binary
Pre-built binaries for Linux, macOS, FreeBSD, Android (arm64), and Windows are available on the Releases page. Windows downloads include ttt-windows-amd64.exe for Intel/AMD PCs and ttt-windows-arm64.exe for ARM PCs. Download the one for your platform, make it executable, and put it in your PATH.
From Source
Note: Building from source compiles the latest development code, which may include work-in-progress features and could be less stable than official releases.
git clone https://github.com/eugenioenko/ttt.git
cd ttt
make build
This produces an optimized binary at bin/ttt. Add it to your PATH or copy it somewhere convenient:
cp bin/ttt ~/.local/bin/
Herdr
TTT is available as a Herdr plugin, so you can open it as a pane inside your Herdr terminal workspace.
herdr plugin install eugenioenko/ttt/herdr-plugin
Once installed, bind it to a key (e.g. Ctrl+b e) to launch TTT in the active worktree with a single keystroke. See the Herdr plugin README for setup and configuration details.
Features
Editor
- Syntax highlighting powered by textmate-go, our pure Go port of VS Code's TextMate engine: 124 languages, with multi-line constructs handled correctly
- Bracket matching with highlighted pairs
- Find and Replace — inline find bar (Ctrl+F) with match navigation, replace bar (Ctrl+R) with replace-one and replace-all
- Go to Line (Ctrl+G)
- Selection, copy/cut/paste (Ctrl+C/X/V) with system clipboard support
- Undo/redo (Ctrl+Z/Y) via a command-pattern undo stack
- Code folding — toggle folds (Ctrl+K [), fold/unfold all, fold-aware search and navigation
- Line operations — toggle line comment, move/duplicate/delete line, join lines, sort lines, split selection into lines
- Multi-cursor editing — Alt+Shift+Up/Down to add a cursor above/below, Ctrl+D to select next occurrence, Ctrl+K L to select all occurrences, Alt+Click to add cursors; typing, backspace, delete, and enter work at all positions simultaneously
.editorconfigsupport and indent detection — indentation is picked up automatically per file; manual override via the status bar indent picker- Mouse support — click to position cursor, click tabs, drag sidebar/panel dividers, right-click context menus
- Auto-completion — LSP-powered completions with live filtering, debounce, and auto-import support
- Signature help — parameter hints shown automatically on
(and, - Diagnostics — inline curly underline squiggles, problems panel, hover popup, and status bar counts
- Document formatting — format document, format selection, and format-on-save via LSP or external formatters
- Git blame — inline blame info for the current line shown in the status bar (author, relative time, summary)
- Line numbers with current-line highlighting
- Diff-based renderer for efficient terminal updates (double-buffered cell grid)
Multi-Folder Workspaces
Open multiple project directories in a single session. Each root appears as a collapsible group in the explorer, search, and changes panels.
ttt # opens the current directory
ttt . # also opens the current directory
ttt /path/to/dir # opens that directory as the workspace
ttt /path/to/file.go # opens just the file — no workspace folder
ttt /path/to/file.go:42:5 # opens the file at line 42, column 5
ttt /path/to/repo/subdir # opens that folder; git features (changes,
# branch) use the enclosing git repo root
ttt dir1 dir2 # opens multiple folders as a multi-root workspace
ttt --workspace project.ttt # loads a saved workspace file
ttt https://github.com/owner/repo/pull/123 # opens pull request review
git diff | ttt # opens piped output in an unsaved tab (or: ttt -)
Set "reuseWindow": true in settings.json (off by default; restart required) to open command-line files and add folders in a running editor using the same config directory. The integrated terminal sets TTT_FORWARD_SOCKET to prefer its parent editor; otherwise, or if that socket is unavailable, the newest reachable editor receives the files and folders. If none is reachable, ttt starts normally. Once connected, open errors or timeouts exit with status 1 without trying another editor. Successful forwarding prints opened in running editor (pid N) to stderr (without the PID when unavailable). Use ttt --new-window file to start a separate editor. With reuse enabled, EDITOR=ttt returns immediately; use EDITOR="ttt --new-window" for commands that wait for editing.
Folder arguments add workspace roots without closing existing tabs; folders already in the workspace are ignored using the same rules as Add Folder to Workspace. For example, ttt . adds the current directory, and ttt dirA file.go adds the folder and opens the file. Bare ttt and ttt --workspace project.ttt still start a new editor. To forward folders after upgrading, restart the running editor with the updated binary, or use --new-window to start a separate editor.
Workspace files use the .ttt extension and store a list of folders as relative paths:
{
"folders": [
{ "path": "." },
{ "path": "../other-project" }
]
}
- Save Workspace As... from the File menu to create a workspace file
- Add Folder to Workspace and Remove Folder from Workspace via the command palette
- The git branch in the status bar switches automatically based on which workspace folder the active file belongs to
See File Explorer & Workspaces.
File Explorer
Multi-root file tree in the sidebar (Ctrl+K E). When multiple folders are open, each root is shown as a collapsible group.
- Directories sorted before files, both alphabetically
- Expand/collapse with Enter or arrow keys
- Right-click context menu: New File, New Folder, Rename, Delete
- Deleted files and folders go to system Trash by default. Turn off
explorer.useTrashin Settings > Explorer > Files for permanent deletion - Sidebar actions button for Refresh and New File
- Explorer: Reveal Active File expands the tree to the active file and selects it; set
explorer.autoRevealto do it on every tab switch - File-type icons, colored by the theme (glyph mappings from nvim-web-devicons). They need a Nerd Font in your terminal, so they default off; turn them on with
"appearance": { "icons": "nerd-font" }or Options > Appearance > Font Icons
Search
Sidebar search panel (Ctrl+K F) powered by ripgrep. Results are grouped by file with match counts.
- Smart-case matching by default
- Include/Exclude glob filters — click the toggle arrow to reveal filter inputs (e.g.
*.go,vendor/**) - Tab between search, include, and exclude inputs
- Searches across all workspace folders simultaneously
- Click a result to jump to the file and line
See Search.
Git Integration
Changes panel in the sidebar (Ctrl+K C) with full staging workflow.
Working-tree files and files under expanded commits can be shown as a compact directory Tree or a full-path List (the default). The choice persists in git.fileView. File-type icons sit between the status letter and the file name; like the Explorer's, they follow the shared appearance.icons setting, need a Nerd Font, and default off — Options > Appearance > Font Icons toggles them. Changes, commit details, and Explorer expose safe Expand All and Collapse All actions in their relevant menus.
Staging:
- Spacebar — toggle stage/unstage on the selected file
a— stage all unstaged filesu— unstage all staged files+/-buttons on the section headers — stage/unstage all files in that section
Committing:
- Inline commit message input below the file list (Tab from the tree to focus it)
- Type a message and press Enter to commit all staged files
- Commit History starts with the 10 most recent commits; activate Load older commits… to append bounded pages from the same HEAD snapshot
Remote operations:
- Pull, Push, Sync (pull then push) from the sidebar actions button
- Per-repo actions via the group header menu button in multi-root workspaces
Diff view:
- Select a changed file to open a split or unified diff with syntax highlighting layered on diff backgrounds
- Working-tree changes open in the file's own editor tab in diff mode, so you can edit and save there; the diff updates as you type. Git: Toggle Inline Diff turns diff mode on or off for any tracked file
- Set the global view mode, context, wrapping, and high-contrast presentation under Options; the Changes panel menu provides the same contextual controls
- Changes-only views show quiet collapsed-context rows that can be expanded in place; full-file context remains available globally or per diff
- Untracked files open directly in the editor
Multi-root:
- Changes are grouped by repository, each with its own collapsible Staged/Changes sections and commit input
- File status badges: M (modified), A (added), D (deleted), R (renamed), U (untracked)
GitHub pull request review:
ttt https://github.com/owner/repo/pull/123 # review a PR
ttt . https://github.com/owner/repo/pull/123 # review a PR with the repo tree open
Opens the PR as a review surface — commit-by-commit history detail, a live view of the current changes, and the same unified presentation controls as local diffs.
See Git Integration.
Command Palette
- Ctrl+P — opens the command palette with all available commands
- Ctrl+K P — opens quick file open (searches all files across workspace folders)
- Type
>in quick-open mode to switch to command mode; delete the>to switch back - Type
?to browse searchable help for panels, navigation, and key chords - Menu shortcuts resolve dynamically from your keybindings
Integrated Terminal
Built-in terminal emulator. Press Ctrl+T to toggle the terminal panel, or Alt+T for fullscreen.
- Ctrl+K T to spawn a new terminal tab; multiple tabs with a vertical inner tab bar on the left edge
- Full VT escape sequence support via
eugenioenko/xterm-go(a maintained fork ofgitpod-io/xterm-go) and PTY management viaaymanbagabas/go-pty - True color (24-bit) and 256-color rendering
- When the terminal is focused, all keys go to the PTY except force keys (Ctrl+T, Alt+T, Ctrl+Q, Ctrl+P, Ctrl+K P, Ctrl+B, F6)
- Terminal shell and scrollback are configurable in
settings.json; ANSI colors are theme-configurable via theterminalfield intheme.json
See Integrated Terminal.
LSP (Language Server Protocol)
TTT has built-in LSP support for language-aware editing: autocomplete, signature help, go to definition/implementation/type definition, find references, rename symbol, hover, document/selection formatting, format-on-save, and diagnostics.
Language servers are configured via plugins — install the LSP plugin for your language and the corresponding server binary from the Plugins panel or command palette.
You can also add custom servers manually in ~/.config/ttt/settings.json. To disable LSP entirely: "lsp": { "enabled": false }.
The language segment in the status bar shows server state for the current file: ◉ connected, ◌ starting, ⚠ failed. Click it to open the OUTPUT panel, where each server logs its startup command and stderr under lsp:<server>.
See the LSP guide and LSP feature reference for keybindings, configuration, and per-feature detail.
Theming
TTT supports fully customizable themes via JSON files. Every color is themeable — syntax highlighting, diff backgrounds, sidebar, tabs, status bar, terminal ANSI colors, borders, and semantic colors (success, danger, warning).
Built-in themes ship with the binary, and extra themes are available for download from the repo.
Switch themes via Options > Theme (or the command palette) for a live preview. To create a custom theme, copy a built-in file to ~/.config/ttt/themes/ and set "theme" in settings.json. To use your terminal's native colors, set foreground/background to empty strings in your theme file.
See Themes.
Plugins
TTT supports Lua plugins that add sidebar panels, bottom panel tabs, commands, and keybindings. Plugins run in a sandboxed Lua VM with a permission system — users approve each plugin's capabilities on first load.
Editor emulation — prefer modal or Emacs-style editing? Install Vim Mode or Emacs Mode from the Plugins sidebar tab. Both require ttt 1.1.0 or newer.
Installing — open the Plugins sidebar tab to browse and install from the community registry, or use Plugins: Install from URL to install from any git repository. Browse the available utilities, formatters, and language servers at ttt-plugins.
Managing — click an installed plugin to enable/disable it; ↑ to update, × to uninstall, Plugins: Reload for live reload during development. To disable the plugin system entirely: "plugins": { "enabled": false }.
Creating — plugins are Lua scripts with a plugin.ttt.json manifest. See the Plugin Authoring Guide.
See Plugins.
Configuration
Config files are loaded from <exe-dir>/config/ (bundled defaults) or ~/.config/ttt/ (user overrides):
| File | Purpose |
|---|---|
settings.json | Editor settings (tabSize, wordWrap, theme, lsp, autocomplete, etc.) |
keybindings.json | Custom keybindings (VS Code key format) |
themes/*.json | Custom color themes |
Most settings can be edited from a form: View → Settings, Ctrl+K ,, or Settings: Open Editor Settings from the command palette. Changes are held until you press Apply; Cancel drops them. Settings marked (restart) take effect on next launch. LSP settings and external formatters are JSON-only — use Settings: Open settings.json.
See Settings reference for every key, its type, default, and description.
Keybindings
All keybindings are customizable via keybindings.json. Chord sequences (e.g. ctrl+k e) are supported. Open your keybindings file with Ctrl+K Y, or press Ctrl+P and type ? to browse shortcuts in-app.
The full default keybinding list is in docs-web/src/content/docs/reference/keybindings.md.
Testing
TTT uses deterministic tests at several boundaries, from core data structures to real-binary and live-PTY workflows.
Unit Tests (Go)
Core algorithms such as buffer operations, cursor math, and undo/redo have focused Go tests. Core packages are presentation-independent; syntax highlighting lives in the presentation-owned internal/highlight package.
make test # run all unit tests
go test ./internal/core/buffer/ # test a single package
E2E Tests (Go + SimScreen)
These wire the complete App to an in-memory term.SimScreen and exercise editor state, commands, event dispatch, and rendered output without starting a real terminal.
go test ./tests/e2e/
Functional Tests (vitest + --exec)
These launch the real ttt binary via the built-in --exec debug harness, run scripted commands in batch, and assert on screenshots and file contents. No external dependencies beyond vitest. Coverage includes:
- File operations — open, edit, save, Save As, new file, dirty indicator
- Editing — undo/redo, select all + overwrite, line delete/move/duplicate, word delete, sort, case transform
- Unicode — accented characters, CJK, emoji, stress tests
- Find & Replace — search, match navigation, single/all replace, save verification
- Navigation — go to line, tab switching, code folding, matching brackets
- UI panels — sidebar toggle, terminal toggle, command palette, word wrap
- Tab management — multi-tab state isolation, close, unsaved changes dialog
- Multi-cursor — add cursor, select occurrences, type with multiple cursors
cd tests/functional
pnpm install
pnpm test # run all functional tests
The harness acknowledges scripted input and commands after main-thread handling and redraw. Tests should wait only for genuine asynchronous work, using a unique visible post-transition state; raw elapsed waits belong only in tests whose invariant is timing or delayed lifecycle behavior.
Integration Tests (vitest + tui-use)
Tests that require live PTY interaction or a real external process boundary. Built with vitest and tui-use, they cover real language servers, external file changes, settings roundtrips, and bracketed paste.
cd tests/integration
pnpm install
pnpm test
To run the pinned LSP tests independently in GitHub Actions, open Actions → LSP Tests → Run workflow, select a branch, and choose all, c, go, or typescript. The same workflow runs in CI. With the GitHub CLI:
gh workflow run lsp-tests.yml --ref main -f language=go
The LSP Latest Canary workflow can also be triggered manually to test the broader set of servers at their latest versions.
Use the smallest deterministic layer that proves an invariant. Functional tests are a compact real-binary contract, not a mandatory copy of lower-layer coverage. Add a broader test only when it establishes behavior unique to that boundary. CI runs the broad regression lanes on pull requests.
Chaos Monkey (Fuzz Testing)
A randomized fuzz tester that hammers the editor with thousands of random events — keypresses, mouse clicks, resizes, clipboard operations, and command palette commands — to find panics and crashes that normal testing misses. It runs against an in-memory simulated screen (term.SimScreen) so no real terminal is needed.
All chaos targets run in Docker: the random command stream can write files, persist settings, and open browser tabs, so the harness refuses to run unsandboxed (set CHAOS_ALLOW_HOST=1 to force a host run, e.g. under a debugger).
# Quick run (50 iterations x 500 events)
make chaos
# Run continuously (crash logs saved to chaos-output/)
make chaos-docker
# Reproduce a specific crash deterministically
CHAOS_REPLAY=chaos-output/crash-<seed>-<iter>.json make chaos-replay
Each crash is saved as a JSON report with the random seed and full event log, so any panic can be replayed and debugged deterministically.
Debug & Testing CLI Flags
TTT includes a built-in scripted interaction system designed for AI agent interactivity and automated debugging. Think of --exec as a fast Playwright for the terminal — full click, keyboard, and command simulation with screenshot and state dump capture, all without the overhead of a terminal emulation layer.
| Flag | Description |
|---|---|
--exec "commands" | Execute semicolon-separated commands after startup |
--listen | Start an HTTP command server on 127.0.0.1:4242 (POST /exec accepts the same script format as --exec, against an already-running editor) |
--plugin FILE | Load a Lua plugin file on startup without approval, using the name and permissions of a plugin.ttt.json next to it (without one: every permission except system.exec) |
--size WxH | Force screen dimensions (e.g. 120x40) for deterministic layout |
--debug | Enable debug mode regardless of config |
The TTT_CONFIG_DIR environment variable overrides the config directory (~/.config/ttt) entirely — settings, keybindings, themes, and plugins are read from and written to that directory instead. Use it to run scripted sessions isolated from your real configuration. Headless --exec sessions also use a process-local clipboard so concurrent automation cannot overwrite the desktop clipboard; interactive sessions, including --listen, keep the system clipboard.
--exec Commands
The --exec flag accepts a semicolon-separated string of commands that run sequentially after the editor starts. AI agents (like Claude Code) can use this to interact with the editor, inspect UI state, and verify behavior programmatically — no manual interaction needed:
| Command | Description |
|---|---|
click X Y | Simulate a mouse click at screen coordinates |
key COMBO | Simulate a key press (e.g. key ctrl+p, key enter) |
type TEXT | Type a string of text character by character |
exec "Command Name" | Run a command palette command by title |
screenshot PATH | Save the current screen text to a file |
debug PATH | Save the editor's debug state as JSON to a file |
wait MS | Wait for the given number of milliseconds |
wait-for TEXT [timeout=MS] | Wait until text appears on the visible screen (default timeout: 5000ms) |
quit / shutdown | Exit the editor |
Quote wait-for text when it contains leading/trailing whitespace or escapes, for example wait-for "Indexing complete" timeout=10000. Scripted input and commands are acknowledged only after the main event loop handles and redraws them, so a following wait-for, screenshot, or debug observes their completed visible state. Invalid actions, missing commands/panels, capture failures, and wait timeouts stop the script: CLI --exec writes the error to stderr and exits nonzero; POST /exec returns a non-2xx response with the same detail.
Example — capture a screenshot and debug state, then quit:
ttt --size 120x40 --exec "wait-for Explore; screenshot /tmp/screen.txt; debug /tmp/state.json; quit"
Example — drive an already-running editor over --listen instead of scripting in advance:
ttt --listen &
curl -X POST --data "type hi; wait-for hi; screenshot /tmp/screen.txt" http://127.0.0.1:4242/exec
curl -X POST --data "shutdown" http://127.0.0.1:4242/exec
Architecture
See ARCHITECTURE.md for the package structure, dependency boundaries, and architecture convergence plan.
Contributing
Bug reports, feature requests, and pull requests are all welcome — open an issue or a PR. See CONTRIBUTING.md before opening one.
Contributors & Acknowledgments
TTT is better because of the people who took the time to try it, report bugs, request features, and contribute code. Thank you. This project grows because of you. 🙏
Want to help? Bug reports, feature requests, and pull requests are all welcome — open an issue or a PR.
License
MIT