T01: crate, window, check script, CI
CI / platforms (windows-latest) (push) Failing after 9s
CI / macos (push) Successful in 10m19s
CI / platforms (ubuntu-24.04) (push) Canceled after 0s

This commit is contained in:
ZacharyZhang-NY committed 2026-10-14 11:09:50 -04:00
commit 4e2f3c9daa
11 files changed
+8339

No files matched your search

+33
View File
@@ -0,0 +1,33 @@
name: CI
on:
push:
branches: [main]
pull_request:
jobs:
macos:
runs-on: macos-latest
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@master
with:
toolchain: "1.95"
components: clippy, rustfmt
- uses: Swatinem/rust-cache@v2
- run: scripts/check.sh
platforms:
strategy:
fail-fast: false
matrix:
os: [ubuntu-24.04, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@master
with:
toolchain: "1.95"
- uses: Swatinem/rust-cache@v2
- run: cargo test --quiet
- run: cargo build
+3
View File
@@ -0,0 +1,3 @@
/target
/Otty.app
.DS_Store
+35
View File
@@ -0,0 +1,35 @@
# AGENTS.md
ELYTerminal. A terminal on GPUI and Ely components. `PRD.md` says what it does; `TASKS.md` says how far it is.
## Stack
- Rust 1.95, edition 2024. One binary crate, `elyterminal`.
- Ely (`ely-gpui-component`) from git.zacharyzhang.com, pinned by rev. gpui and gpui_platform from crates.io's `gpui-pre`, the exact version Ely pins, under their crate names through `package`.
- `alacritty_terminal` 0.26: grid, parser, pseudo-terminal, event loop.
- `serde`, `serde_json`: settings. `dirs`: the config folder. `log`, `env_logger`: logs to stderr.
## Commands
- `cargo run` opens the app. `RUST_LOG=elyterminal=debug cargo run` logs more.
- `scripts/check.sh`: fmt, clippy `-D warnings`, tests, the 500-line rule.
## Layout
- `src/main.rs`: start, window.
## Rules
- Files stay at or under 500 lines. Comments are one short line, and rare.
- Fail fast. No silent fallbacks. An error names its owner and what failed.
- Log every state change on a key path with `log`: spawn, exit, folder change, settings load and save, blur path.
- No dead code, no versioned names, no compatibility shims.
- Colors, sizes and radii come from Ely's theme. Gaps use gpui's rem helpers.
- No credential in any file. Pushes pass the token as an `http.extraHeader` on the command line.
## Decisions
- 2026-10-14: ELYTerminal owns its terminal engine on `alacritty_terminal`. Ely's `Terminal` lacks mouse reporting, IME, kitty keys and style settings, and a library widget should not carry an app's engine.
- 2026-10-14: the working directory comes from OSC 7, read by a tap on the pty reader, sent by injected shell integration. vte drops OSC 7.
- 2026-10-14: Git through the `git` command line, not libgit2.
- 2026-10-14: license MIT, as Ely.
Generated
+7966
View File
File diff suppressed because it is too large. Load diff
+23
View File
@@ -0,0 +1,23 @@
[package]
name = "elyterminal"
version = "0.1.0"
edition = "2024"
rust-version = "1.95"
license = "MIT"
description = "A terminal on GPUI and Ely."
repository = "https://git.zacharyzhang.com/ZacharyZhang-NY/ELYTerminal"
publish = false
[dependencies]
anyhow = "1"
ely-gpui-component = { git = "https://git.zacharyzhang.com/ZacharyZhang-NY/Ely-GPUI-Components.git", rev = "12512217bd9bc652ed6bbf10461b00cc26c01c21" }
env_logger = "0.11"
gpui = { package = "gpui-pre", version = "=0.3.8", default-features = false }
gpui_platform = { package = "gpui-pre-platform", version = "=0.3.8", features = ["font-kit"] }
log = "0.4"
[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(feature, values("cargo-clippy"))'] }
[lints.clippy]
all = { level = "warn", priority = -1 }
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 ELYTerminal contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+125
View File
@@ -0,0 +1,125 @@
# ELYTerminal PRD
A terminal for macOS, Windows and Linux. Rust, GPUI, Ely components. It draws a terminal as well as Ghostty does, shows every setting on screen, and makes no noise.
Chapters run in build order. `TASKS.md` follows them line by line.
## 1. Product
- One window. Sessions on the left, the terminal in the middle, Git or Files on the right.
- Every setting has a control in the settings window. No setting lives only in a file.
- Silent: no sound, no bell, no notifications, no update checks, no telemetry, no tips, no onboarding.
- Clean: no subtitle, no tagline, no logging options, no status bar, no badges that do not carry state.
- Built-in themes, each shown as a small preview, Catppuccin among them. A theme colors the terminal and the chrome.
- The window background blurs on macOS, Windows, Wayland and X11.
## 2. Foundation
- One binary crate, `elyterminal`, edition 2024, Rust 1.95.
- Ely from git.zacharyzhang.com by pinned rev. gpui, gpui_platform from crates.io `gpui-pre` at the exact version Ely pins.
- `alacritty_terminal` 0.26 for the grid, the parser and the pseudo-terminal, as Ely uses it.
- Logs through `log` to stderr, `RUST_LOG` sets the level. No log setting in the app.
- CI on Gitea Actions: macOS builds, tests and lints; Ubuntu and Windows build and test.
- Private repo `ELYTerminal` on git.zacharyzhang.com. No credential in any file or commit.
## 3. Settings model and storage
- One JSON file, `settings.json`, in the platform config folder (`dirs::config_dir()/ELYTerminal`).
- Every field has a default. A missing file means defaults. A missing field means its default.
- A file that does not parse, or a value out of range, stops the start with an error naming the file and the field. The app never overwrites a file it could not read.
- Writes go to a temp file in the same folder, then rename. One writer: the app's settings entity.
- Fields: see chapter 10.
## 4. Themes
- Built in: Ely Dark, Ely Light, Catppuccin Mocha, Catppuccin Macchiato, Catppuccin Frappé, Catppuccin Latte, Tokyo Night, Dracula, Nord, Gruvbox Dark, Rosé Pine, Solarized Light, One Light, Kanagawa Wave.
- A theme holds background, foreground, cursor, cursor text, selection background and foreground, and the 16 ANSI colors. Values come from the theme's upstream files (iTerm2-Color-Schemes' Ghostty export, or Ely's palette).
- A theme maps onto Ely's `Palette` through `Theme::set_palette`, so panels, inputs and lists wear the theme.
- Dark mode: one theme, or a second theme for when the system is dark.
## 5. Terminal engine
Ghostty is the bar for what the terminal does. ELYTerminal owns its engine on `alacritty_terminal`; Ely's `Terminal` is a gallery widget without mouse reporting, IME, kitty keys or style settings.
- PTY: alacritty's `tty` and event loop. The pty is wrapped so its reader passes bytes through an OSC 7 scanner. The scanner reports the working directory.
- Shell integration for zsh, bash, fish and PowerShell, injected at launch as Ghostty does: each prompt sends OSC 7 and a title. A shell without integration reports nothing, and the panels say so.
- Grid: colors (named, 256, truecolor, OSC 4/10/11/12), bold, dim, italic, underline (single, double, curly, dotted, dashed), strikethrough, inverse, hidden, wide characters, combining marks.
- Box drawing (U+2500–U+257F) and block elements (U+2580–U+259F) painted as geometry, so lines join across cells.
- Cursor: block, bar, underline, hollow block; blinking per setting and per DECSCUSR; hollow when the window is not focused.
- Keys: xterm encoding with modifiers, application cursor and keypad modes, Option as Alt on macOS, the kitty keyboard protocol as the program sets its flags.
- IME: marked text drawn at the cursor; committed text sent to the program.
- Mouse: select by character, word (double press) and line (triple press); Shift extends; mouse reporting (X10, normal, button, any; SGR and UTF-8); wheel scrolls back, or reports, or sends arrows on the alternate screen.
- Clipboard: copy, paste (bracketed when asked), copy on select per setting, OSC 52 writes.
- Links: OSC 8 and plain URLs; Cmd-press (Ctrl-press off macOS) opens them.
- Focus reporting, title (OSC 0/2), scrollback limit per setting, find in scrollback (Ely's `FindWidget`).
- Bell: no sound. The session row marks it until the session is shown.
## 6. Main window and layout
- Ely's `ColumnShell`: sessions column, terminal column, right column. The window opens with blur when the setting is on.
- The sessions column and the right column toggle and keep their state in settings.
- The title shows the active session's title. Nothing else in the title bar.
## 7. Sessions panel
- A list of sessions. Each row: title, then the working directory, home as `~`.
- New session (+ and Cmd-T), close (row's ×, Cmd-W), select (press, Cmd-1…9, Ctrl-Tab and Ctrl-Shift-Tab).
- A session whose program ends closes its row. The last row closing closes the window.
- A bell in a hidden session marks its row.
## 8. Right panel: Files
- A switch at the top: Git, Files.
- Files shows the active session's working directory as a tree, folders first, Git status colors on names.
- Double press: a folder opens, a file opens in the system's default app.
- It reloads when the session reports a new directory and at each prompt.
## 9. Right panel: Git
- Through the `git` command line. No libgit2.
- Shows the branch, staged and unstaged changes, a commit message field and Commit, and recent commits.
- Stage and unstage a file. Press a file to see its diff in Ely's `DiffViewer`.
- Outside a repository it says so. A failed git command shows its error.
- It reloads at each prompt and on directory change.
## 10. Settings window
Cmd-, opens it. Categories on the left, rows on the right, Ely's settings components. Changes apply at once to every session and save.
- General
- Shell: a path; empty runs the login shell.
- Start in: Home, or the active session's folder.
- Appearance
- Theme: a grid of previews.
- Separate dark theme: a switch; when on, a second grid for dark mode.
- Font family: a list of installed fonts. Font size. Line height. Ligatures.
- Cursor: style (block, bar, underline, hollow), blink, with a live preview.
- Background opacity: a slider. Background blur: a switch.
- Padding: horizontal and vertical.
- Terminal
- Scrollback lines.
- Option as Alt: off, left, right, both (macOS only).
- Copy on select.
## 11. Background blur
- macOS: gpui's `WindowBackgroundAppearance::Blurred` (NSVisualEffectView).
- Windows: gpui's `Blurred` (acrylic accent).
- Wayland: gpui's `Blurred` speaks `org_kde_kwin_blur`; ELYTerminal adds `ext_background_effect_v1` on the window's surface, which GNOME 51 and Plasma 6.7 support.
- X11: ELYTerminal sets `_KDE_NET_WM_BLUR_BEHIND_REGION` (CARDINAL, 32) on the window, as Ghostty does; removes it when blur turns off.
- A compositor with neither protocol shows transparency without blur. The app logs which path it took.
## 12. Menus and shortcuts
- macOS menu bar: ELYTerminal (Settings, Quit), Shell (New Session, Close Session), Edit (Copy, Paste, Select All, Find), View (Toggle Sessions, Toggle Right Panel, Bigger, Smaller, Reset Size), Window.
- The same shortcuts on Windows and Linux with Ctrl-Shift for terminal-colliding keys.
## 13. Acceptance
- Each chapter's tasks ticked in `TASKS.md` with a passing review in `tasks/reviews.md`.
- `scripts/check.sh` passes: fmt, clippy with `-D warnings`, tests, no file over 500 lines.
- CI green on macOS, Ubuntu and Windows.
## Deferred
Splits, tabs inside a session, profiles, key rebinding, kitty graphics, sixel, OSC 133 prompt marks, session restore, push and pull, discard changes, quick terminal.
+62
View File
@@ -0,0 +1,62 @@
# Tasks
Progress truth. Top to bottom, one at a time. A task is ticked only after its codex review passes; rounds go in `tasks/reviews.md`, three at most.
## 2. Foundation
- [ ] T01 Repo, crate, window: git, `.gitignore`, `Cargo.toml` with Ely pinned, `main.rs` opens one window through Ely, logging, `scripts/check.sh`, CI workflow, private Gitea repo, first push.
## 3. Settings model and storage
- [ ] T02 `Settings` with defaults and ranges; load (missing file, missing field, bad file, bad value) and atomic save; tests.
## 4. Themes
- [ ] T03 Built-in themes from upstream files; mapping onto Ely's `Palette`; dark-mode choice; tests.
## 5. Terminal engine
- [ ] T04 PTY: spawn through alacritty's event loop, OSC 7 tap on the reader, child id, events to the app; tests.
- [ ] T05 Shell integration for zsh, bash, fish, PowerShell, injected at launch; tests that zsh and bash report their folder.
- [ ] T06 Terminal view: frame from the grid, colors and styles, wide and combining cells, cursor shapes and blink, font, line height, padding, resize.
- [ ] T07 Box drawing and block elements painted as geometry; tests.
- [ ] T08 Keys: xterm encoding, modes, Option as Alt, kitty keyboard protocol, IME; tests.
- [ ] T09 Mouse: selection, mouse reporting, wheel and scrollback, clipboard, copy on select, OSC 52, links; tests.
- [ ] T10 Focus reporting, title, silent bell, scrollback limit, find.
## 6. Main window and layout
- [ ] T11 `ColumnShell` with sessions, terminal and right columns; toggles kept in settings; window title.
## 7. Sessions panel
- [ ] T12 Sessions: new, close, select, keys, titles and folders, ended programs, bell marks.
## 8. Right panel: Files
- [ ] T13 Git/Files switch; file tree of the session's folder with Git colors; open; reload on folder and prompt.
## 9. Right panel: Git
- [ ] T14 Git through the command line: status, branch, stage, unstage, commit, log, diff; tests on a temp repo.
- [ ] T15 Git panel: branch, changes, stage and unstage, commit, recent commits, diff view, errors.
## 10. Settings window
- [ ] T16 Settings window: categories, General rows, live apply and save.
- [ ] T17 Appearance: theme grids with previews, font, cursor with preview, opacity, blur, padding.
- [ ] T18 Terminal rows: scrollback, Option as Alt, copy on select.
## 11. Background blur
- [ ] T19 macOS and Windows: `Blurred` or `Transparent` or `Opaque` from settings, applied live.
- [ ] T20 Wayland: `ext_background_effect_v1` on the window's surface.
- [ ] T21 X11: `_KDE_NET_WM_BLUR_BEHIND_REGION` on the window.
## 12. Menus and shortcuts
- [ ] T22 macOS menu bar; key bindings on all platforms.
## 13. Acceptance
- [ ] T23 End-to-end checks, README, acceptance table, CI green on three platforms.
+16
View File
@@ -0,0 +1,16 @@
#!/bin/sh
# Pre-review gate: build rules plus house rules.
set -eu
cargo fmt --check
cargo clippy --all-targets -- -D warnings
cargo test --quiet
fail=0
files=$(git ls-files -co --exclude-standard '*.rs')
multi=$(awk 'FNR==1{prev=0} /^[[:space:]]*\/\// {if(prev) print FILENAME":"FNR; prev=1; next} {prev=0}' $files)
[ -z "$multi" ] || { echo "multi-line comments:"; echo "$multi"; fail=1; }
for f in $(git ls-files -co --exclude-standard); do
case "$f" in *.rs|*.md|*.sh|*.zsh|*.bash|*.fish|*.ps1|*.toml|*.yml) ;; *) continue ;; esac
n=$(wc -l < "$f")
[ "$n" -le 500 ] || { echo "$f has $n lines"; fail=1; }
done
exit $fail
+52
View File
@@ -0,0 +1,52 @@
use ely_gpui_component::{
Assets,
primitives::FocusScope,
theme::{ActiveTheme, Mode, Theme},
};
use gpui::{
App, AppContext, Bounds, Context, FocusHandle, IntoElement, Render, Styled, TitlebarOptions,
Window, WindowBounds, WindowOptions, px, size,
};
struct Workspace {
focus: FocusHandle,
}
impl Render for Workspace {
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
FocusScope::new(&self.focus)
.root()
.size_full()
.bg(cx.theme().colors.bg)
}
}
fn main() {
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("elyterminal=info"))
.init();
gpui_platform::application()
.with_assets(Assets)
.run(|cx: &mut App| {
ely_gpui_component::init(cx).expect("Ely failed to start");
Theme::set_mode_now(Mode::Dark, cx);
let bounds = Bounds::centered(None, size(px(1200.0), px(760.0)), cx);
let options = WindowOptions {
window_bounds: Some(WindowBounds::Windowed(bounds)),
titlebar: Some(TitlebarOptions {
title: Some("ELYTerminal".into()),
..Default::default()
}),
..Default::default()
};
cx.open_window(options, |window, cx| {
cx.new(|cx| {
let focus = cx.focus_handle();
window.focus(&focus, cx);
Workspace { focus }
})
})
.expect("the main window failed to open");
log::info!("app: main window open");
cx.activate(true);
});
}
+3
View File
@@ -0,0 +1,3 @@
# Reviews
Each task gets at most three codex rounds. Each round: the commit, the verdict, the findings and what was done.