Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ ELYTerminal. A terminal on GPUI and Ely components. `PRD.md` says what it does;
- 2026-10-14: Appearance shows every built-in theme as Ely's `RadioCard` in a `SimpleGrid`, each over a small window in the theme's colors. Separate dark theme copies the theme into `dark_theme`, so turning it on changes nothing until a dark card is picked. A theme change re-dresses the chrome at once. Font family comes from Ely's `FontPicker`; size, line height and padding from its `NumberInput`, which saves a number on Enter, on leaving the field and on each step. A font saves, and a settings file starts, only when gpui loads it as the terminal draws it: gpui falls back to another font without a word and loads none without an `m`, so the terminal draws with no font but its own. The cursor preview is a prompt in the terminal's font and theme, its cursor drawn as the terminal draws it, blinking on the terminal's beat. Opacity moves by whole percents, so the saved fraction carries no float noise. A change the store refuses leaves the setting as it was, and the page says why at its top; a number field drops its draft, and the font picker shows the saved family again. The settings window opens at 900 by 640, room for three theme columns.
- 2026-10-14: Terminal shows the shell and where sessions start, which a General page held until the owner dropped it, then scrollback lines, Option as Alt, on macOS only, since elsewhere Alt is Alt, copy on select, the sessions column's drag and right press, and Ely, then every shortcut in Ely's `KeyboardShortcutsList`, read from the keymap: the bindings of each listed command that apply in the main window's terminal, matched at any depth of its context stack as gpui matches them, so the list cannot drift from the keys. The engine reads each when it needs it, so a change applies at once to every session; a smaller scrollback drops the history past it.
- 2026-10-14: the main window lets the desktop through while Background opacity is under 1, blurred or clear as Background blur says, through gpui's `Blurred` (macOS's NSVisualEffectView, Windows' acrylic) or `Transparent`; at 1 it is `Opaque`. The workspace sets it as the settings change. Ely's `ColumnShell::glass`, added to Ely for this, grounds the columns in the palette's glass, which the opacity sets, so the title bar and every column let the desktop through alike; the palette's `bg` stays solid, so a dialog, the Git diff among them, hides what lies under it. The terminal paints no ground over the glass, or the two would stack; a program's own background (OSC 11) paints over it. Ely's own themes pass their palette too, Ely's colors with the glass set. The settings window stays opaque.
- 2026-11-03: Background image paints where Picture scope says: the terminal's column or the window below the title bar, never the title bar. `src/workspace/wallpaper.rs` paints the window's ground, the theme's background at the background opacity, and the picture over it at that opacity, once behind the workspace; a canvas ruler in the terminal column's body records those bounds, since a column's header may hold it wider than its width setting, and asks for another frame when they move. While a picture is set the columns' glass is 0, so the picture keeps its colors and the columns stand transparent over the ground; a program's own background (OSC 11) still paints over it.
- 2026-10-15: on Wayland the window asks for `ext_background_effect_v1` beside gpui's `org_kde_kwin_blur`. A guest connection on gpui's own display binds it, takes the effect for the window's surface once, and blurs the window's pane: the surface less Ely's resize margin on each free edge of a client-drawn window, worked out each frame and sent only when it changes; it goes and comes back with Blurred and lands at the surface's next commit, which every frame makes. The guest connection reads the display only while the window opens, before gpui's loop runs, and is never dropped: a drop destroys its proxies through the display, which gpui may close first. The log names what the compositor offers, so which way blur goes: `ext_background_effect_v1`, gpui's `org_kde_kwin_blur`, or none, the desktop clear. Its test runs a `wayland-server` compositor on a socket pair.
- 2026-10-15: on a client-drawn window, so on Linux, Ely's `ResizeBorder` paints its pane solid and shadowed under the content, which kept the desktop from showing at all. `ResizeBorder::glass`, added to Ely for this, leaves the pane to the content while the window is see-through: no ground, nor the shadow gpui draws under the box, which would tint it. So the blur covers the pane alone and the resize margin stays clear. On X11 the window names its pane in `_KDE_NET_WM_BLUR_BEHIND_REGION` (CARDINAL, 32: x, y, width, height in the window's pixels), as Ghostty names its own, and drops the property when Blurred goes; KWin reads it, other compositors pass it by. It goes over a connection of ELYTerminal's own to `$DISPLAY`, which gpui opens too: a property belongs to the window, whoever sets it. gpui's own `org_kde_kwin_blur` on KDE's Wayland still covers the whole surface, margin included, beyond ELYTerminal's reach.
- 2026-10-15: the macOS menu bar is the PRD's: 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 (Minimize, Zoom, then the windows macOS lists). An item shows the key bound to its action, so the menus are set after the bindings; Copy, Paste and Select All go through AppKit's `copy:`, `paste:` and `selectAll:`, as gpui's `OsAction` maps them. They are Ely's text input actions, which `forms` exports for this, and the terminal handles them too: gpui sends a menu's action to what holds focus and enables the item only where a handler waits, so one action serves the terminal, find and every field. The new keys are Ghostty's: Cmd-Q, Cmd-A, Cmd-= and Cmd-+, Cmd-minus, Cmd-0, Cmd-M. Off macOS Quit and Select All take Ctrl-Shift, since Ctrl with a letter types a control byte; the font's size takes plain Ctrl with =, +, minus and 0, which type none, as in Ghostty, Alacritty and Windows Terminal; Minimize has no key there. Bigger and Smaller move every session's font a point, inside the Size range, and Reset Size goes back to the setting; nothing saves the change, as in Ghostty. Select All runs from the first character in the history to the last on the screen, the blanks around them left out, as Ghostty's, though a space under a combining mark shows a character and counts as one; copy on select takes it, and a blank grid selects nothing.
Expand Down
2 changes: 1 addition & 1 deletion PRD.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ Cmd-, opens it; on Windows and Linux, which have no menu bar, so does a gear in
- Separate dark theme: a switch; when on, a second grid for dark mode.
- Font family: a list of installed fonts. Fallbacks: installed families tried in order for a character the family lacks, each added, changed or removed in a list of pickers. CJK font: a family Chinese, Japanese and Korean characters draw in, with their marks and variation selectors, before Family, empty by default, cleared by Clear. Font size. Weight, 100 to 900, 400 by default, normal text's; bold sits 300 above it, up to 900. Line height. Ligatures. Antialiasing, on Windows and Linux: Grayscale, as Windows Terminal draws, by default, or Subpixel, which gpui draws only on an opaque window.
- Cursor: style (block, bar, underline, hollow), blink, glide, on by default, which slides the cursor to its new cell in 90 ms, with a live preview.
- Background opacity: a slider. Background image: a picture under the terminal, at that opacity, chosen as a file. Background blur: a switch. Backdrop, on Windows 11 from build 22621: Acrylic, Mica or Mica Alt.
- Background opacity: a slider. Background image: a picture under the terminal or the window, at that opacity, chosen as a file. Picture scope: the terminal's column, or the window below the title bar. Background blur: a switch. Backdrop, on Windows 11 from build 22621: Acrylic, Mica or Mica Alt.
- Padding: horizontal and vertical.
- Terminal
- Shell: a path; empty runs the login shell, on Windows `pwsh`, else `powershell`, else `cmd`. On Windows a session takes the registry's environment as it starts.
Expand Down
1 change: 1 addition & 0 deletions docs/en_us/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ English / [中文](../zh_cn/features.md)
- Interface font and size in Settings › Appearance: any installed family, with a sample line, and 12 to 16 px for every window's chrome; the terminal keeps its own font.
- On Windows, Settings › Terminal › Explorer menu adds Open in ELYTerminal to a folder's right-click menu, for you alone, and removes it; on Windows 11 it sits under Show more options. `elyterminal FOLDER` starts the first session in FOLDER on every system; cmd, which refuses a network share as its folder, says so and starts in the Windows folder. The folder may also follow `--working-directory`, `-d` or `--startingDirectory`, as xdg-terminal-exec and Windows Terminal pass it.
- Background opacity and blur: NSVisualEffectView on macOS, acrylic on Windows, or on Windows 11 from build 22621 Mica or Mica Alt, as Settings › Appearance › Backdrop says, `ext_background_effect_v1` or `org_kde_kwin_blur` on Wayland, `_KDE_NET_WM_BLUR_BEHIND_REGION` on X11, which KWin reads. Blur shows only while the opacity is under 100%. Hyprland blurs only while its own `decoration:blur:enabled` is on, and Omarchy 4.0.4 turns it off; to turn it on there, add `hl.config({ decoration = { blur = { enabled = true } } })` to `~/.config/hypr/looknfeel.lua`.
- Background image: a picture at Settings › Appearance › Background image, drawn at the background's opacity where Picture scope says, under the terminal's column or under the whole window below the title bar. A column's header can hold the terminal wider than its width setting, and the picture follows the terminal itself. The picture keeps its colors: while one is set, the columns' glass is 0, so a window-wide picture shows through them.
1 change: 1 addition & 0 deletions docs/zh_cn/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@
- 设置 › 外观 中的界面字体和字号:任一已安装字体,附示例行,12 到 16 px,作用于每个窗口的界面;终端保留自己的字体。
- Windows 上,设置 › 终端 › 资源管理器菜单 可为当前用户在文件夹的右键菜单中加入或移除“在 ELYTerminal 中打开”;Windows 11 中它位于“显示更多选项”下。`elyterminal FOLDER` 在各系统上都让第一个会话从 FOLDER 开始;cmd 不接受网络共享作为当前目录,会自行提示并进入 Windows 目录。文件夹也可以写在 `--working-directory`、`-d` 或 `--startingDirectory` 之后,与 xdg-terminal-exec 和 Windows Terminal 的写法相同。
- 背景不透明度和模糊:macOS 上是 NSVisualEffectView,Windows 上是 acrylic,Windows 11 build 22621 起可按设置 › 外观 › 背景材质选云母或云母 Alt,Wayland 上是 `ext_background_effect_v1` 或 `org_kde_kwin_blur`,X11 上是 `_KDE_NET_WM_BLUR_BEHIND_REGION`,KWin 会读它。模糊只在不透明度低于 100% 时出现。Hyprland 只在自己的 `decoration:blur:enabled` 打开时模糊,而 Omarchy 4.0.4 把它关掉了;要在那里打开,把 `hl.config({ decoration = { blur = { enabled = true } } })` 加到 `~/.config/hypr/looknfeel.lua`。
- 背景图片:在设置 › 外观 › 背景图片 选一张图,按背景不透明度显示,位置由 图片范围 决定:终端列下方,或标题栏以下的整个窗口。列头可能让终端比宽度设置更宽,图片跟着终端本身。图片保持原色:设置图片时各列的玻璃为 0,整窗图片会从列中透出。
1 change: 1 addition & 0 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ pub fn start(cx: &mut App) -> anyhow::Result<()> {
theme,
settings.custom_themes.clone(),
settings.background_opacity,
!settings.background_image.is_empty(),
)
};
let mut image = SettingsStore::get(cx).background_image.clone();
Expand Down
5 changes: 4 additions & 1 deletion src/i18n/en.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,11 @@ pub const MESSAGES: &[(&str, &str)] = &[
("et.appearance.image", "Background image"),
(
"et.appearance.image_hint",
"A picture under the terminal, at the background's opacity",
"A picture under the terminal or the window, at the background's opacity",
),
("et.appearance.scope", "Picture scope"),
("et.appearance.scope_terminal", "Terminal"),
("et.appearance.scope_window", "Window"),
("et.custom.new", "New theme"),
("et.custom.name_of", "Custom {number}"),
("et.custom.name", "Name"),
Expand Down
5 changes: 4 additions & 1 deletion src/i18n/zh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,11 @@ pub const MESSAGES: &[(&str, &str)] = &[
("et.appearance.image", "背景图片"),
(
"et.appearance.image_hint",
"终端下方的图片,按背景不透明度显示",
"终端或窗口下方的图片,按背景不透明度显示",
),
("et.appearance.scope", "图片范围"),
("et.appearance.scope_terminal", "终端"),
("et.appearance.scope_window", "窗口"),
("et.custom.new", "新建主题"),
("et.custom.name_of", "自定义 {number}"),
("et.custom.name", "名称"),
Expand Down
12 changes: 12 additions & 0 deletions src/settings/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,15 @@ pub enum PanelTab {
Files,
}

/// Where the background picture paints: the terminal's column or the window below the title bar.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum BackgroundScope {
#[default]
Terminal,
Window,
}

/// Everything the settings window edits. A field the file leaves out takes its default.
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(default, deny_unknown_fields)]
Expand All @@ -118,6 +127,8 @@ pub struct Settings {
pub custom_themes: Vec<themes::CustomTheme>,
/// A picture under the terminal; empty for none.
pub background_image: String,
/// Where the picture paints: the terminal's column or the window below the title bar.
pub background_scope: BackgroundScope,
pub font_family: String,
/// Families tried in order for a character Font family lacks.
pub font_fallbacks: Vec<String>,
Expand Down Expand Up @@ -193,6 +204,7 @@ impl Default for Settings {
dark_theme: None,
custom_themes: Vec::new(),
background_image: String::new(),
background_scope: BackgroundScope::Terminal,
font_family: "JetBrains Mono".into(),
font_fallbacks: Vec::new(),
cjk_font: String::new(),
Expand Down
11 changes: 11 additions & 0 deletions src/settings/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -361,3 +361,14 @@ fn the_interface_wears_the_system_font_at_13_px_unless_named() {
);
}
}

#[test]
fn a_picture_lies_under_the_terminal_unless_the_window_is_chosen() {
let settings: Settings = serde_json::from_str("{}").expect("every field has a default");
assert_eq!(settings.background_scope, BackgroundScope::Terminal);
let window: Settings =
serde_json::from_str(r#"{"background_scope":"window"}"#).expect("a scope");
assert_eq!(window.background_scope, BackgroundScope::Window);
let json = serde_json::to_string(&window).expect("the settings write");
assert!(json.contains(r#""background_scope":"window""#), "{json}");
}
28 changes: 26 additions & 2 deletions src/settings/window/appearance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ use super::{
use crate::{
blur,
settings::{
Backdrop, FONT_SIZE, FONT_WEIGHT, LINE_HEIGHT, Language, OPACITY, PADDING, Settings,
SettingsStore,
Backdrop, BackgroundScope, FONT_SIZE, FONT_WEIGHT, LINE_HEIGHT, Language, OPACITY, PADDING,
Settings, SettingsStore,
},
};

Expand All @@ -44,6 +44,20 @@ const LANGUAGES: [(Language, &str, &str); 2] = [
(Language::SimplifiedChinese, "zh-CN", "et.language.chinese"),
];

/// Each picture scope, its key and its name's key.
const SCOPES: [(BackgroundScope, &str, &str); 2] = [
(
BackgroundScope::Terminal,
"terminal",
"et.appearance.scope_terminal",
),
(
BackgroundScope::Window,
"window",
"et.appearance.scope_window",
),
];

impl SettingsWindow {
/// The language, theme, font, cursor and window rows.
pub(super) fn appearance(&self, window: &Window, cx: &Context<Self>) -> Div {
Expand Down Expand Up @@ -194,6 +208,16 @@ impl SettingsWindow {
.description(word("et.appearance.image_hint"))
.control(image),
)
.row(
SettingsRow::new(word("et.appearance.scope")).control(choice(
&this,
"background_scope",
&SCOPES,
settings.background_scope,
|settings, scope| settings.background_scope = scope,
cx,
)),
)
.row(SettingsRow::new(word("et.appearance.blur")).control(switch(
&this,
"background_blur",
Expand Down
48 changes: 0 additions & 48 deletions src/term/backdrop.rs

This file was deleted.

1 change: 0 additions & 1 deletion src/term/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
mod backdrop;
mod boxes;
mod click;
mod clipboard;
Expand Down
5 changes: 0 additions & 5 deletions src/term/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,6 @@ pub struct TerminalView {
pub(super) pointer: Pointer,
/// The mouse mode, as the tap follows it.
pub(super) mouse: MouseMode,
/// The background picture that last failed, logged once.
pub(super) backdrop_failed: Option<std::path::PathBuf>,
/// Kitty graphics' textures.
pictures: Pictures,
/// Keys go as Windows' records, as ConPTY asks.
Expand Down Expand Up @@ -169,7 +167,6 @@ impl TerminalView {
target: None,
pointer: Pointer::default(),
mouse: MouseMode::default(),
backdrop_failed: None,
pictures: Pictures::default(),
#[cfg(windows)]
win32: false,
Expand Down Expand Up @@ -452,11 +449,9 @@ impl Render for TerminalView {
root.text_color(ink.fg).child(format!("{error:#}"))
}
};
let backdrop = self.backdrop(&settings, window, cx);
div()
.relative()
.size_full()
.children(backdrop)
.child(root)
.children(scrollbar::bar(span, window, cx))
.children(
Expand Down
7 changes: 6 additions & 1 deletion src/themes/chrome.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,12 @@ pub fn palette(theme: &TerminalTheme, opacity: f32) -> Palette {
pub fn apply(cx: &mut App) {
let settings = SettingsStore::get(cx);
let theme = active(settings, cx.window_appearance(), cx);
let opacity = settings.background_opacity;
// A picture grounds the window; the columns leave it as it is.
let opacity = if settings.background_image.is_empty() {
settings.background_opacity
} else {
0.0
};
let mode = if theme.is_dark() {
Mode::Dark
} else {
Expand Down
Loading