Add full disk access checks and settings

Add background themes and custom images

Add shell command logging toggle

Add UTF-8 locale guarantee for PTY

Add Claude hook settings injection

Add hotkey system for GUI

Add glass panel styling

Add search disabled state for agent panels

Add zoom toggle command

Add device report filtering

Add entitlements for notarization

Update version to 0.1.27
This commit is contained in:
2026-06-15 22:26:06 +07:00
parent 2ee2aaaffb
commit ee845e15b3
30 changed files with 859 additions and 123 deletions
+62 -12
View File
@@ -4,7 +4,7 @@ use alacritty_terminal::event::{Event, EventListener};
use alacritty_terminal::grid::Dimensions;
use alacritty_terminal::index::{Column, Line, Point};
use alacritty_terminal::term::{Config, Term};
use alacritty_terminal::vte::ansi::Processor;
use alacritty_terminal::vte::ansi::{NamedColor, Processor, Rgb};
/// Fixed-size terminal dimensions for the daemon-side grid.
#[derive(Clone, Copy)]
@@ -25,25 +25,53 @@ impl Dimensions for GridSize {
}
}
/// One escape sequence the terminal model wants written back to the PTY in
/// response to a query: either a ready-made byte reply (DA/DSR/etc.) or a color
/// report whose value must be resolved from the term's palette at drain time.
enum Reply {
Bytes(Vec<u8>),
Color(usize, Arc<dyn Fn(Rgb) -> String + Send + Sync>),
}
/// Collects the escape sequences the terminal model wants written back to the PTY
/// (Primary/Secondary Device Attributes, DSR cursor/status reports, etc.). Programs
/// like fish block on these replies at startup; with a void listener they hang ~2s
/// and then warn ("could not read response to Primary Device Attribute query").
/// (Primary/Secondary Device Attributes, DSR cursor/status reports, OSC color
/// queries, etc.). Programs like fish, yazi and vim block on these replies at
/// startup; with a void listener they hang ~2s and then warn ("could not read
/// response to Primary Device Attribute query") or render with the wrong theme.
///
/// The daemon is the authoritative responder for the PTY — the GUI's xterm.js is
/// display-only and must NOT echo its own replies back (its duplicate arrives an
/// IPC roundtrip late and gets typed into the shell as literal gibberish).
#[derive(Clone, Default)]
pub struct ReplyCollector {
buf: Arc<Mutex<Vec<u8>>>,
buf: Arc<Mutex<Vec<Reply>>>,
}
impl EventListener for ReplyCollector {
fn send_event(&self, event: Event) {
if let Event::PtyWrite(text) = event {
if let Ok(mut b) = self.buf.lock() {
b.extend_from_slice(text.as_bytes());
}
let reply = match event {
Event::PtyWrite(text) => Reply::Bytes(text.into_bytes()),
// OSC 10/11/12 color query — alacritty defers the value to the embedder
// (us) via a formatter; resolve it against the palette in take_replies.
Event::ColorRequest(index, fmt) => Reply::Color(index, fmt),
_ => return,
};
if let Ok(mut b) = self.buf.lock() {
b.push(reply);
}
}
}
/// Fallback palette colors when a program queries one the term has not had set
/// explicitly. Matches the GUI's default theme so OSC 11 (background) reports a
/// dark color and light/dark detection in TUIs stays correct.
fn default_color(index: usize) -> Rgb {
if index == NamedColor::Background as usize { Rgb { r: 0x0a, g: 0x0d, b: 0x12 } }
else if index == NamedColor::Foreground as usize { Rgb { r: 0xe6, g: 0xed, b: 0xf3 } }
else if index == NamedColor::Cursor as usize { Rgb { r: 0xe6, g: 0xed, b: 0xf3 } }
else { Rgb { r: 0x80, g: 0x80, b: 0x80 } }
}
/// Owns an alacritty terminal model and feeds raw PTY bytes into it.
pub struct GridSurface {
term: Term<ReplyCollector>,
@@ -68,10 +96,21 @@ impl GridSurface {
/// far. The caller must write these back to the PTY for query-driven programs
/// (fish, vim, etc.) to proceed without timing out.
pub fn take_replies(&mut self) -> Vec<u8> {
match self.replies.buf.lock() {
Ok(mut b) => std::mem::take(&mut *b),
Err(_) => Vec::new(),
let replies = {
let Ok(mut b) = self.replies.buf.lock() else { return Vec::new(); };
std::mem::take(&mut *b)
};
let mut out = Vec::new();
for reply in replies {
match reply {
Reply::Bytes(bytes) => out.extend_from_slice(&bytes),
Reply::Color(index, fmt) => {
let rgb = self.term.colors()[index].unwrap_or_else(|| default_color(index));
out.extend_from_slice(fmt(rgb).as_bytes());
}
}
}
out
}
pub fn resize(&mut self, cols: u16, rows: u16) {
@@ -141,4 +180,15 @@ mod tests {
// Replies are drained, not duplicated.
assert!(g.take_replies().is_empty());
}
#[test]
fn osc_background_color_query_gets_a_reply() {
// yazi/vim send OSC 11 ("\x1b]11;?\x07") to detect the background color and
// block on the reply; the daemon must answer it authoritatively.
let mut g = GridSurface::new(20, 5);
g.feed(b"\x1b]11;?\x07");
let reply = String::from_utf8(g.take_replies()).unwrap();
assert!(reply.starts_with("\x1b]11;rgb:"), "expected an OSC 11 reply, got {reply:?}");
assert!(g.take_replies().is_empty());
}
}