From 4416663ccc2f5a88673282aa8bc794b291cfc155 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 20 Oct 2026 14:25:42 +0700 Subject: [PATCH 1/3] T27 OS window: matte glass for every surface in a window, opaque black in full screen; the terminal ends at an open panel; the KEYS foot has a row per line surface_alpha(kind, full) decides the fill of the sidebar, content, terminal and panel surfaces: a darker tint over the behind-window glass in a window (0.45, 0.35, 0.15 over the content, 0.58), opaque #000000 in full screen. The terminal's default background is the terminal tint, not opaque black; the honeycomb cards are not drawn under a terminal pane. The Queen, DOCTOR and KEYS panels share one ground (wiped to the glass and tinted), the button plates are the same glass, and the sidebar foot is the sidebar's glass again. The terminal grid is laid out to the open panel's left edge (content_w), so the pty gets the narrower size through the existing resize path and nothing of it is under the panel. The KEYS foot is the status row, the OPEN IN row and the chips in rows of three, each in its own band computed from the chip count (editors.t27 foot_*); chip rows are 14 pt so six chips fit under nine providers at 960 x 540. Refs #10782 Co-Authored-By: Claude Sonnet 5.5 --- specs/t27os/editors.t27 | 142 ++++++++++++++++- specs/t27os/glass_window.t27 | 286 +++++++++++++++++++++++++++-------- 2 files changed, 367 insertions(+), 61 deletions(-) diff --git a/specs/t27os/editors.t27 b/specs/t27os/editors.t27 index 9df66e5928..4370eb1b84 100644 --- a/specs/t27os/editors.t27 +++ b/specs/t27os/editors.t27 @@ -426,10 +426,11 @@ test only_an_absolute_clean_directory_is_handed_over { // ---- the OPEN IN strip at the foot of the KEYS panel: the installed editors as a grid of text chips ---- /// Three chips of 88 pt fill the 268 pt of the Queen/KEYS panel (QP_W 300 less two QP_PAD): "Antigravity IDE" -/// and "VS Code Insiders" fit one chip at 9 pt, where four 66 pt chips overlapped. +/// and "VS Code Insiders" fit one chip at 9 pt, where four 66 pt chips overlapped. A chip row is 14 pt (it was +/// 16): two rows, the status row and the OPEN IN row then fit under the nine provider rows of a 540 pt window. pub const ED_COLS : u32 = 3; pub const ED_SLOT_W : f64 = 88.0; -pub const ED_SLOT_H : f64 = 16.0; +pub const ED_SLOT_H : f64 = 14.0; pub const ED_NONE : u32 = 255; /// The editor (index into the table) that is the k-th installed one in `mask`; ED_NONE when there are fewer. @@ -486,6 +487,143 @@ pub fn ed_hit(x: f64, y: f64, l: f64, pad: f64, top: f64, count: u32) -> u32 { return ED_NONE; } +// ---- the foot of the KEYS panel: the status row, the OPEN IN row, then the chips, one band each ---- +// Owner, 2026-10-20: the claude status line was drawn on top of the second row of chips. The foot starts at +// `top`, the bottom edge of the last provider row, and stacks rows that never share a y band: +// the status row FT_LINE_H tall, its baseline FT_BASE below the band's top; +// the OPEN IN row FT_LINE_H tall, same baseline; +// the chips ED_COLS to a row, ED_SLOT_H tall, baseline ED_BASE below the slot's top. +// How many chip rows there are comes from the chip count. A row that does not fit above the bottom edge `sh` +// is not drawn, and its chips cannot be clicked (foot_chips): at 960 x 540 six chips are two rows. + +pub const FT_LINE_H : f64 = 12.0; +pub const FT_BASE : f64 = 9.0; +pub const ED_BASE : f64 = 10.0; +/// The ink of a 9 to 9.5 pt line reaches at most FT_ASC above its baseline and FT_DESC below it. +pub const FT_ASC : f64 = 7.0; +pub const FT_DESC : f64 = 2.5; + +/// Chip rows that `count` chips make, ED_COLS to a row. +pub fn strip_rows(count: u32) -> u32 { + return (count + ED_COLS - 1) / ED_COLS; +} + +pub fn foot_status_top(top: f64) -> f64 { + return top; +} + +pub fn foot_head_top(top: f64) -> f64 { + return top + FT_LINE_H; +} + +/// The strip's top edge: the first chip slot (ed_slot_y, ed_hit take this as `top`). +pub fn foot_chips_top(top: f64) -> f64 { + return top + 2.0 * FT_LINE_H; +} + +/// The status row's band lies inside the panel. +pub fn foot_status_fits(top: f64, sh: f64) -> bool { + return top + FT_LINE_H <= sh; +} + +/// Whole chip rows between the strip's top edge and the bottom edge `sh`. +pub fn foot_rows_fit(top: f64, sh: f64) -> u32 { + const room : f64 = sh - foot_chips_top(top); + if (room < ED_SLOT_H) { return 0; } + return @as(u32, @intFromFloat(room / ED_SLOT_H)); +} + +/// Chip rows drawn: all of them when they fit, else as many as fit. +pub fn foot_rows(top: f64, sh: f64, count: u32) -> u32 { + const want : u32 = strip_rows(count); + const fit : u32 = foot_rows_fit(top, sh); + if (want < fit) { return want; } + return fit; +} + +/// Chips drawn, and the only ones a click can land on. +pub fn foot_chips(top: f64, sh: f64, count: u32) -> u32 { + const shown : u32 = foot_rows(top, sh, count) * ED_COLS; + if (count < shown) { return count; } + return shown; +} + +/// The bottom edge of the last chip row drawn (the strip's top when there is none). +pub fn foot_bottom(top: f64, sh: f64, count: u32) -> f64 { + return foot_chips_top(top) + @as(f64, @floatFromInt(foot_rows(top, sh, count))) * ED_SLOT_H; +} + +test the_foot_rows_come_from_the_chip_count { + assert strip_rows(0) == 0; + assert strip_rows(1) == 1; + assert strip_rows(ED_COLS) == 1; + assert strip_rows(ED_COLS + 1) == 2; + assert strip_rows(6) == 2; + assert strip_rows(EDITOR_COUNT) == 9; + // with room, every chip is drawn: the rows are the count over ED_COLS, rounded up + assert foot_rows(487.0, 1440.0, 6) == 2; + assert foot_rows(487.0, 1440.0, 7) == 3; + assert foot_chips(487.0, 1440.0, 7) == 7; + assert foot_chips(487.0, 1440.0, EDITOR_COUNT) == EDITOR_COUNT; + assert foot_rows(487.0, 1440.0, 0) == 0; +} + +test no_two_text_rows_of_the_foot_share_a_y_band { + const top : f64 = 487.0; + // a line's ink band is [baseline - FT_ASC, baseline + FT_DESC]; each band ends before the next begins + assert foot_status_top(top) + FT_BASE - FT_ASC >= top; + assert foot_status_top(top) + FT_BASE + FT_DESC <= foot_head_top(top) + FT_BASE - FT_ASC; + assert foot_head_top(top) + FT_BASE + FT_DESC <= foot_chips_top(top) + ED_BASE - FT_ASC; + // and two chip rows in a row: the next slot's ink starts after this one's ends + assert ED_BASE + FT_DESC <= ED_SLOT_H + ED_BASE - FT_ASC; + assert ED_SLOT_H >= FT_ASC + FT_DESC; + // a chip's baseline is inside its slot, and the slots do not overlap + assert ED_BASE + FT_DESC <= ED_SLOT_H; + assert ed_slot_y(foot_chips_top(top), ED_COLS) == ed_slot_y(foot_chips_top(top), 0) + ED_SLOT_H; + // the foot's three kinds of row are in the order status, OPEN IN, chips + assert foot_status_top(top) < foot_head_top(top); + assert foot_head_top(top) < foot_chips_top(top); +} + +test the_foot_never_draws_below_the_panel_and_hides_whole_rows_only { + const top : f64 = 487.0; + var n : u32 = 0; + while (n <= EDITOR_COUNT) { + // 540 is the window's own height, 720 a taller window, 1440 a full screen + const h0 : f64 = 540.0; + const h1 : f64 = 720.0; + const h2 : f64 = 1440.0; + assert foot_bottom(top, h0, n) <= h0; + assert foot_bottom(top, h1, n) <= h1; + assert foot_bottom(top, h2, n) <= h2; + // a chip that is drawn belongs to a whole row, and a row that is hidden hides all its chips + assert foot_chips(top, h0, n) <= n; + assert foot_chips(top, h0, n) <= foot_rows(top, h0, n) * ED_COLS; + // more room never shows fewer chips + assert foot_chips(top, h1, n) >= foot_chips(top, h0, n); + assert foot_chips(top, h2, n) >= foot_chips(top, h1, n); + // in full screen every one of the 27 editors fits + assert foot_chips(top, h2, n) == n; + n = n + 1; + } +} + +test a_chip_in_a_row_that_is_not_drawn_cannot_be_clicked { + const top : f64 = 487.0; + const l : f64 = 660.0; + const ct : f64 = foot_chips_top(top); + // 540 high: nine chips would be three rows, two fit, so six are drawn and clickable + assert foot_chips(top, 540.0, 9) == 6; + assert ed_hit(l + 16.0 + 3.0, ct + 3.0, l, 16.0, ct, foot_chips(top, 540.0, 9)) == 0; + assert ed_hit(l + 16.0 + 3.0, ct + ED_SLOT_H + 3.0, l, 16.0, ct, foot_chips(top, 540.0, 9)) == ED_COLS; + assert ed_hit(l + 16.0 + 3.0, ct + 2.0 * ED_SLOT_H + 3.0, l, 16.0, ct, foot_chips(top, 540.0, 9)) == ED_NONE; + // in full screen the same click is the seventh chip + assert ed_hit(l + 16.0 + 3.0, ct + 2.0 * ED_SLOT_H + 3.0, l, 16.0, ct, foot_chips(top, 1440.0, 9)) == 2 * ED_COLS; + // a click on the status row or on the OPEN IN row is on no chip + assert ed_hit(l + 16.0 + 3.0, foot_status_top(top) + 3.0, l, 16.0, ct, 6) == ED_NONE; + assert ed_hit(l + 16.0 + 3.0, foot_head_top(top) + 3.0, l, 16.0, ct, 6) == ED_NONE; +} + test the_kth_installed_editor_skips_the_missing_ones { assert nth_installed(0, 0) == ED_NONE; assert nth_installed(1 + 4 + 16, 0) == 0; diff --git a/specs/t27os/glass_window.t27 b/specs/t27os/glass_window.t27 index 2f26aa3a22..96b5ac1e79 100644 --- a/specs/t27os/glass_window.t27 +++ b/specs/t27os/glass_window.t27 @@ -141,6 +141,15 @@ use t27os::editors::esc_twice; use t27os::editors::ed_slot_x; use t27os::editors::ed_slot_y; use t27os::editors::ed_hit; +use t27os::editors::ED_BASE; +use t27os::editors::FT_BASE; +use t27os::editors::FT_ASC; +use t27os::editors::FT_DESC; +use t27os::editors::foot_status_top; +use t27os::editors::foot_head_top; +use t27os::editors::foot_chips_top; +use t27os::editors::foot_status_fits; +use t27os::editors::foot_chips; use t27os::editors::open_word; use t27os::editors::OPEN_NONE; use t27os::editors::OPEN_OK; @@ -1049,6 +1058,21 @@ pub fn content_x(ws: u32, collapsed: bool) -> f64 { return side_w(ws, collapsed) + 1.0; } +/// P. The right edge of the content area: the window's edge, or the left edge of the right-hand panel (Queen, +/// DOCTOR or KEYS) while one is open, so the terminal's grid ends where the panel starts and nothing of it +/// sits under the panel (owner, 2026-10-20: the terminal's text showed through the KEYS rows). +pub fn content_right(sw: f64, panel: bool) -> f64 { + if (panel) { return qp_left(sw); } + return sw; +} + +/// P. The width the terminal's grid is laid out in: from the content's left edge to content_right, never negative. +pub fn content_w(sw: f64, ws: u32, collapsed: bool, panel: bool) -> f64 { + const w : f64 = content_right(sw, panel) - content_x(ws, collapsed); + if (w < 0.0) { return 0.0; } + return w; +} + /// W. True when x is over the sidebar's rows (expanded workspace, sidebar shown). pub fn in_side(x: f64, ws: u32, collapsed: bool) -> bool { return ws == WS_EXPANDED and collapsed == false and x >= 12.0 and x <= SIDE_W - 12.0; @@ -1570,14 +1594,14 @@ fn side_row(c: u64, y: f64, h: f64, selected: bool, hover: bool, a: f64) -> void /// C. Chat workspace: sidebar (unless collapsed), divider, content with header, divider and cards. /// K. Compact (ws 1): no sidebar, the OPEN DASHBOARD bar instead of the header. Forced dashboard (ws 2): /// the expanded layout with a zero-width sidebar and the Close Dashboard button in the header. -fn draw_chat(c: u64, kit: [48]u64, shown: u32, task: u32, space: u32, sp: u32, ss: u64, cur: u32, a: f64, collapsed: bool, mx: f64, my: f64, sw: f64, sh: f64, ws: u32, dlabel: u64, dlit: bool) -> void { +fn draw_chat(c: u64, kit: [48]u64, shown: u32, task: u32, space: u32, sp: u32, ss: u64, cur: u32, a: f64, collapsed: bool, mx: f64, my: f64, sw: f64, sh: f64, ws: u32, dlabel: u64, dlit: bool, full: bool, tup: bool) -> void { const cx : f64 = content_x(ws, collapsed); if (ws == WS_DASHBOARD and collapsed == false) { cg_fill_rgba(c, 1.0, 1.0, 1.0, LINE_WHITE * a); cg_fill_rect(c, 0.0, WS_TOP, HAIR, sh - WS_TOP); } if (ws == WS_EXPANDED and collapsed == false) { - cg_fill_rgba(c, 0.0, 0.0, 0.0, STRICT_SIDE * a); + cg_fill_rgba(c, 0.0, 0.0, 0.0, surface_alpha(SURF_SIDEBAR, full) * a); cg_fill_rect(c, 0.0, WS_TOP, SIDE_W, sh - WS_TOP); cg_fill_rgba(c, 1.0, 1.0, 1.0, LINE_WHITE * a); cg_fill_rect(c, SIDE_W, WS_TOP, HAIR, sh - WS_TOP); @@ -1674,9 +1698,10 @@ fn draw_chat(c: u64, kit: [48]u64, shown: u32, task: u32, space: u32, sp: u32, s } r = r + 1; } - // W. the foot: a hairline, then OPEN SOURCE T27, which opens the repository (windows.t27 REPO_URL) - cg_fill_rgba(c, 0.0, 0.0, 0.0, a); - cg_fill_rect(c, 0.0, sh - FOOT_H, SIDE_W, FOOT_H); + // W. the foot: a hairline, then OPEN SOURCE T27, which opens the repository (windows.t27 REPO_URL). + // Its ground hides a long list of chats under it; in a window it is the sidebar's glass again, not + // an opaque black strip + draw_ground(c, 0.0, sh - FOOT_H, SIDE_W, FOOT_H, SURF_SIDEBAR, full, a); cg_fill_rgba(c, 1.0, 1.0, 1.0, LINE_WHITE * a); cg_fill_rect(c, 12.0, sh - FOOT_H, SIDE_W - 24.0, HAIR); var la : f64 = LABEL_WHITE; @@ -1692,14 +1717,16 @@ fn draw_chat(c: u64, kit: [48]u64, shown: u32, task: u32, space: u32, sp: u32, s cg_fill_rgba(c, 1.0, 1.0, 1.0, da * a); draw_text(c, kit, dlabel, SIDE_W - 14.0 - DOC_LINK_W + 22.0, sh - 13.0, 9.5, WEIGHT_SEMIBOLD, false); } - cg_fill_rgba(c, 0.0, 0.0, 0.0, STRICT_CONTENT * a); + cg_fill_rgba(c, 0.0, 0.0, 0.0, surface_alpha(SURF_CONTENT, full) * a); cg_fill_rect(c, cx, WS_TOP, sw - cx, sh - WS_TOP); if (ws == WS_COMPACT) { draw_dash_bar(c, kit, a, sw, on_open_dash(mx, my, sw, ws)); } else { draw_header(c, kit, shown, a, collapsed, mx, my, sw, ws); } - draw_cards(c, kit, task, a, collapsed, mx, my, sw, sh, ws); + // the cards are the screen of a chat with no pane; with a terminal pane up they would show through its + // glass, so they are not drawn + if (tup == false) { draw_cards(c, kit, task, a, collapsed, mx, my, sw, sh, ws); } } /// C. The 44 pt header: sidebar.left toggle, Close Dashboard when forced open, the title, the divider. @@ -2004,6 +2031,65 @@ pub fn tint_swap(full: bool, was_full: bool) -> bool { return full != was_full; } +/// F. Surfaces (owner, 2026-10-20): in a window every surface is a darker tint over the same matte black glass +/// -- the behind-window blur under the layer tint -- and none is opaque; in full screen every surface is +/// opaque black #000000, as the frame is (backdrop_alpha). One fn decides for all of them. +pub const SURF_SIDEBAR : u32 = 0; +pub const SURF_CONTENT : u32 = 1; +pub const SURF_TERMINAL : u32 = 2; +pub const SURF_PANEL : u32 = 3; +/// F. The terminal is drawn over the content's STRICT_CONTENT (0.35): 0.15 more brings the two to 0.4475, +/// the sidebar's 0.45 (the same look as the sidebar, which the owner called glass). +pub const TERMINAL_TINT : f64 = 0.15; +/// F. The right-hand panels (Queen, DOCTOR, KEYS) are the elevated surface of trios (0.58): their small +/// type needs more ground than the sidebar's. +pub const PANEL_BLACK : f64 = 0.58; + +/// F. The alpha of a surface's black fill: its glass tint in a window, 1.0 (opaque #000000) in full screen. +pub fn surface_alpha(kind: u32, full: bool) -> f64 { + if (full) { return 1.0; } + if (kind == SURF_SIDEBAR) { return STRICT_SIDE; } + if (kind == SURF_CONTENT) { return STRICT_CONTENT; } + if (kind == SURF_TERMINAL) { return TERMINAL_TINT; } + return PANEL_BLACK; +} + +/// F. A surface that is not opaque must first wipe what was drawn under it, as the backdrop does, or the +/// cards and the last frame show through it. +pub fn surface_clears(kind: u32, full: bool) -> bool { + return surface_alpha(kind, full) < 1.0; +} + +/// F. A black surface over a rectangle: wiped first when it is glass (and fully shown: during the 0.25 s +/// cross-fade `a` is below 1 and the wipe waits, so nothing pops), then the tint times `a`. +fn draw_ground(c: u64, x: f64, y: f64, w: f64, h: f64, kind: u32, full: bool, a: f64) -> void { + if (surface_clears(kind, full) and a >= 1.0) { + cg_blend(c, BLEND_CLEAR); + cg_fill_rect(c, x, y, w, h); + cg_blend(c, BLEND_NORMAL); + } + const s : f64 = surface_alpha(kind, full) * a; + if (s > 0.0) { + cg_fill_rgba(c, 0.0, 0.0, 0.0, s); + cg_fill_rect(c, x, y, w, h); + } +} + +/// F. The ground under a right-hand panel, drawn once before it: the whole column from the top of the +/// workspace to the bottom edge, wiped to the glass and tinted (opaque black in full screen). The wipe reaches +/// BACK_BLEED past the right and bottom edges, where a fractional pixel would keep part of the last frame. +fn draw_panel_ground(c: u64, sw: f64, sh: f64, full: bool) -> void { + const l : f64 = qp_left(sw); + draw_ground(c, l, WS_TOP, sw - l + BACK_BLEED, sh - WS_TOP + BACK_BLEED, SURF_PANEL, full, 1.0); +} + +/// F. A plate under a panel's button, so a long line of the row under it does not show through the button: the +/// panel's own glass wiped and tinted again, not an opaque black box. In full screen the wiped pixel shows +/// the layer's opaque black (tint_alpha(true) == 1.0), so the plate is #000000 there as well. +fn draw_plate(c: u64, x: f64, y: f64, w: f64, h: f64) -> void { + draw_ground(c, x, y, w, h, SURF_PANEL, false, 1.0); +} + /// F. The background: clear (BLEND_CLEAR) in a window, one opaque black fill over the whole frame in full screen. fn draw_backdrop(c: u64, sw: f64, sh: f64, full: bool) -> void { if (backdrop_clears(full)) { @@ -2026,8 +2112,7 @@ fn draw_queen_panel(c: u64, kit: [48]u64, sw: f64, sh: f64, mx: f64, my: f64, vi const l : f64 = qp_left(sw); const t : f64 = WS_TOP; const x : f64 = l + QP_PAD; - cg_fill_rgba(c, 0.0, 0.0, 0.0, 0.94); - cg_fill_rect(c, l, t, QP_W, sh - t); + // the ground is draw_panel_ground's, drawn once before the panel cg_fill_rgba(c, 1.0, 1.0, 1.0, LINE_WHITE); cg_fill_rect(c, l, t, HAIR, sh - t); // the crown and QUEEN @@ -2261,13 +2346,16 @@ fn term_banner(st: []u32, cells: []u32, cwd: u64) -> bool { return true; } -/// The pane: black content area under the header, one monospace line per grid row, a white -/// block cursor with its character redrawn in black. -fn draw_term(c: u64, kit: [48]u64, st: []const u32, cells: []const u32, curon: bool, a: f64, collapsed: bool, sw: f64, sh: f64, ws: u32) -> void { +/// The pane: the content area under the header, one monospace line per grid row, a white block cursor with +/// its character redrawn in black. Its default background is a surface (surface_alpha SURF_TERMINAL): glass in +/// a window, opaque #000000 in full screen. The program's colours are dropped by vt.t27 (SGR is read and +/// discarded), so no cell has a background of its own: the one filled cell is the cursor. The area ends at +/// the left edge of an open right-hand panel (content_right), where the grid also ends. +fn draw_term(c: u64, kit: [48]u64, st: []const u32, cells: []const u32, curon: bool, a: f64, collapsed: bool, sw: f64, sh: f64, ws: u32, full: bool, panel: bool) -> void { const ox : f64 = content_x(ws, collapsed); const oy : f64 = WS_TOP + HEADER_H; - cg_fill_rgba(c, 0.0, 0.0, 0.0, a); - cg_fill_rect(c, ox, oy + HAIR, sw - ox, sh - oy - HAIR); + cg_fill_rgba(c, 0.0, 0.0, 0.0, surface_alpha(SURF_TERMINAL, full) * a); + cg_fill_rect(c, ox, oy + HAIR, content_w(sw, ws, collapsed, panel), sh - oy - HAIR); const cols : u32 = st[ST_COLS]; const rows : u32 = st[ST_ROWS]; var lb : [1600]u8 = undefined; @@ -2388,13 +2476,12 @@ fn status_dot(st: u32) -> u32 { return DOT_ALERT; } -/// One of the three buttons of a row: a black plate, a rounded outline, the word centred; `on` draws it -/// filled (the armed DELETE), `live` false draws it dim (nothing to delete or check). +/// One of the three buttons of a row: a plate of the panel's glass, a rounded outline, the word centred; `on` +/// draws it filled (the armed DELETE), `live` false draws it dim (nothing to delete or check). fn draw_key_btn(c: u64, kit: [48]u64, bx: f64, by: f64, word: u32, on: bool, live: bool, over: bool) -> void { var w : [24]u8 = undefined; const n : usize = csv_field(KEYS_WORDS, word, w[0..24]); - cg_fill_rgba(c, 0.0, 0.0, 0.0, 1.0); - cg_fill_rect(c, bx - 2.0, by - 2.0, KY_BTN_W + 4.0, KY_BTN_H + 4.0); + draw_plate(c, bx - 2.0, by - 2.0, KY_BTN_W + 4.0, KY_BTN_H + 4.0); round_rect(c, bx, by, KY_BTN_W, KY_BTN_H, 5.0); var ink : f64 = TEXT_WHITE; if (on) { @@ -2418,8 +2505,7 @@ fn draw_keys_panel(c: u64, kit: [48]u64, sw: f64, sh: f64, mx: f64, my: f64, sto const t : f64 = WS_TOP; const x : f64 = l + QP_PAD; var w : [48]u8 = undefined; - cg_fill_rgba(c, 0.0, 0.0, 0.0, 0.94); - cg_fill_rect(c, l, t, QP_W, sh - t); + // the ground is draw_panel_ground's, drawn once before the panel cg_fill_rgba(c, 1.0, 1.0, 1.0, LINE_WHITE); cg_fill_rect(c, l, t, HAIR, sh - t); cg_fill_rgba(c, 1.0, 1.0, 1.0, TEXT_WHITE); @@ -2489,34 +2575,36 @@ fn draw_keys_panel(c: u64, kit: [48]u64, sw: f64, sh: f64, mx: f64, my: f64, sto var wy : [96]u8 = undefined; n = profile_why(sm, bad_mask(kst[0..9]), wy[0..96]); cg_fill_rgba(c, 1.0, 1.0, 1.0, LABEL_WHITE); - draw_bytes(c, kit, wy[0..96], n, x, sh - 14.0, 9.5, 0.0, false); - // the OPEN IN strip: the editors found on this Mac (editors.t27), one chip each, below the rows - const ec : u32 = strip_count(kemask, ktpick); - if (ec > 0) { - const top : f64 = ky_row_top(t, PROVIDER_COUNT) + 4.0; - if (top + 14.0 + ED_SLOT_H <= sh) { - n = csv_field(KEYS_WORDS, 10, w[0..48]); - cg_fill_rgba(c, 1.0, 1.0, 1.0, LABEL_WHITE); - const hw : f64 = draw_bytes(c, kit, w[0..48], n, x, top + 8.0, 9.5, 0.0, false); - // why the last click did not open an editor (editors.t27 open_word); nothing after an open that started - if (kewhy != OPEN_NONE and kewhy != OPEN_OK) { - n = open_word(kewhy, w[0..48]); - cg_fill_rgba(c, 1.0, 1.0, 1.0, TEXT_WHITE); - draw_bytes(c, kit, w[0..48], n, x + hw + 8.0, top + 8.0, 9.5, 0.0, false); - } - const hv : u32 = ed_hit(mx, my, l, QP_PAD, top + 14.0, ec); - var k : u32 = 0; - while (k < ec) { - n = strip_label(strip_chip(kemask, ktpick, k), w[0..48]); - var ea : f64 = MUTED_WHITE; - if (hv == k) { ea = TEXT_WHITE; } - cg_fill_rgba(c, 1.0, 1.0, 1.0, ea); - const cx : f64 = ed_slot_x(l, QP_PAD, k); - const cy : f64 = ed_slot_y(top + 14.0, k); - const cw : f64 = draw_bytes(c, kit, w[0..48], n, cx, cy + 11.0, 9.0, 0.0, false); - if (hv == k) { cg_fill_rect(c, cx, cy + 13.0, cw, HAIR); } - k = k + 1; - } + // the foot (editors.t27 foot_*): the status row, then OPEN IN, then the chips, each in a band of its own + // below the provider rows, so no two text rows share a y band (the status line sat on the chips) + const ft : f64 = ky_row_top(t, PROVIDER_COUNT); + if (foot_status_fits(ft, sh)) { + draw_bytes(c, kit, wy[0..96], n, x, foot_status_top(ft) + FT_BASE, 9.5, 0.0, false); + } + // the OPEN IN strip: the editors found on this Mac (editors.t27), one chip each; only whole rows that fit + const nc : u32 = foot_chips(ft, sh, strip_count(kemask, ktpick)); + if (nc > 0) { + n = csv_field(KEYS_WORDS, 10, w[0..48]); + cg_fill_rgba(c, 1.0, 1.0, 1.0, LABEL_WHITE); + const hw : f64 = draw_bytes(c, kit, w[0..48], n, x, foot_head_top(ft) + FT_BASE, 9.5, 0.0, false); + // why the last click did not open an editor (editors.t27 open_word); nothing after an open that started + if (kewhy != OPEN_NONE and kewhy != OPEN_OK) { + n = open_word(kewhy, w[0..48]); + cg_fill_rgba(c, 1.0, 1.0, 1.0, TEXT_WHITE); + draw_bytes(c, kit, w[0..48], n, x + hw + 8.0, foot_head_top(ft) + FT_BASE, 9.5, 0.0, false); + } + const hv : u32 = ed_hit(mx, my, l, QP_PAD, foot_chips_top(ft), nc); + var k : u32 = 0; + while (k < nc) { + n = strip_label(strip_chip(kemask, ktpick, k), w[0..48]); + var ea : f64 = MUTED_WHITE; + if (hv == k) { ea = TEXT_WHITE; } + cg_fill_rgba(c, 1.0, 1.0, 1.0, ea); + const cx : f64 = ed_slot_x(l, QP_PAD, k); + const cy : f64 = ed_slot_y(foot_chips_top(ft), k); + const cw : f64 = draw_bytes(c, kit, w[0..48], n, cx, cy + ED_BASE, 9.0, 0.0, false); + if (hv == k) { cg_fill_rect(c, cx, cy + ED_BASE + 2.0, cw, HAIR); } + k = k + 1; } } } @@ -2534,9 +2622,11 @@ fn draw(c: u64, kit: [48]u64, frame: u32, state: u32, hov: u32, ma: f64, shown: if (hov != PETAL_NONE) { draw_tooltip(c, kit, hov, ha, z, sw, sh); } } if (ma > 0.0) { - draw_chat(c, kit, shown, task, space, sp, ss, cur, ma, collapsed, mx, my, sw, sh, ws, dlabel, dp); - if (topen != PANE_NONE) { draw_term(c, kit, tst, tcells, tcur, ma, collapsed, sw, sh, ws); } + draw_chat(c, kit, shown, task, space, sp, ss, cur, ma, collapsed, mx, my, sw, sh, ws, dlabel, dp, full, topen != PANE_NONE); + if (topen != PANE_NONE) { draw_term(c, kit, tst, tcells, tcur, ma, collapsed, sw, sh, ws, full, qp or dp or kyp); } } + // exactly one right-hand panel is drawn (the Queen's, else DOCTOR's, else KEYS'): its ground first + if (qp or dp or kyp) { draw_panel_ground(c, sw, sh, full); } if (qp) { draw_queen_panel(c, kit, sw, sh, mx, my, view_of(menu_open(state), shown), space, ss, cur, ring, n, now, sug, god); } if (dp and qp == false) { draw_doctor_panel(c, kit, dk, sw, sh, mx, my, darmed, doff, dgo, dlive); } if (kyp and qp == false and dp == false) { draw_keys_panel(c, kit, sw, sh, mx, my, kstored, kmask, kmlen, kst, kfail, kopen, now, kedit, kbuf, klen, karm, kbusy, kemask, kewhy, ktpick); } @@ -3119,8 +3209,7 @@ fn draw_doctor_panel(c: u64, kit: [48]u64, dk: [128]u64, sw: f64, sh: f64, mx: f const l : f64 = qp_left(sw); const t : f64 = WS_TOP; const x : f64 = l + QP_PAD; - cg_fill_rgba(c, 0.0, 0.0, 0.0, 0.94); - cg_fill_rect(c, l, t, QP_W, sh - t); + // the ground is draw_panel_ground's, drawn once before the panel cg_fill_rgba(c, 1.0, 1.0, 1.0, LINE_WHITE); cg_fill_rect(c, l, t, HAIR, sh - t); cg_fill_rgba(c, 1.0, 1.0, 1.0, TEXT_WHITE); @@ -3180,9 +3269,8 @@ fn draw_doctor_panel(c: u64, kit: [48]u64, dk: [128]u64, sw: f64, sh: f64, mx: f const over : bool = on_doc_btn(mx, my, l, QP_W, QP_PAD, t, r); var word : u32 = DL_REMOVE; if (armed == @as(u32, @intCast(ri))) { word = DL_CONFIRM; } - // a black plate first, so a long line under the button does not show through it - cg_fill_rgba(c, 0.0, 0.0, 0.0, 1.0); - cg_fill_rect(c, bx - 6.0, by - 2.0, DOC_BTN_W + 6.0, DOC_BTN_H + 4.0); + // a plate of the panel's glass first, so a long line under the button does not show through it + draw_plate(c, bx - 6.0, by - 2.0, DOC_BTN_W + 6.0, DOC_BTN_H + 4.0); round_rect(c, bx, by, DOC_BTN_W, DOC_BTN_H, 5.0); if (word == DL_CONFIRM) { cg_fill_rgba(c, 1.0, 1.0, 1.0, TEXT_WHITE); @@ -3822,8 +3910,8 @@ pub fn main() -> void { } } else { karm = KY_NONE; - const ktop : f64 = ky_row_top(WS_TOP, PROVIDER_COUNT) + 4.0 + 14.0; - const keh : u32 = ed_hit(mx, my, kl, QP_PAD, ktop, strip_count(kemask, ktpick)); + const kft : f64 = ky_row_top(WS_TOP, PROVIDER_COUNT); + const keh : u32 = ed_hit(mx, my, kl, QP_PAD, foot_chips_top(kft), foot_chips(kft, sh, strip_count(kemask, ktpick))); if (keh != ED_NONE) { // the editor opens the folder of the space on screen (home when it is unbound) var kdirs : u64 = dk_at(dk, dk[DK_SLOTS], @as(u64, space)); @@ -4277,9 +4365,10 @@ pub fn main() -> void { if (editor_view_stays(eview, screen, cur, ecur, space, esp) == false) { eview = false; } const want : u32 = view_key(eview, cur != SESSION_NONE and opens_pane(session_agent(ss, cur)), cur); if (screen and want != PANE_NONE) { - const tox : f64 = content_x(ws, collapsed); const toy : f64 = WS_TOP + HEADER_H; - const tw : f64 = sw - tox; + // the grid ends at the left edge of an open right-hand panel: the pty gets the narrower size + // through the resize below, and nothing of the terminal is under the panel + const tw : f64 = content_w(sw, ws, collapsed, qp or dp or kyp); const th : f64 = sh - toy; // a drag changes the size every frame: the pane takes it once it has held TP_SETTLE frames, so // the grid and the ring replay run once per resize instead of once per frame @@ -4934,3 +5023,82 @@ test the_editor_view_shares_the_terminal_areas_key_space_with_the_chat_panes { assert view_key(true, true, 2) == VIEW_EDITOR; assert view_key(true, false, SESSION_NONE) == VIEW_EDITOR; } + +test every_surface_is_glass_in_a_window_and_opaque_black_in_full_screen { + // owner, 2026-10-20: a window is matte black glass all over -- each surface a darker tint over the blur, + // none opaque -- and full screen is opaque #000000 all over + assert surface_alpha(SURF_SIDEBAR, false) == 0.45; + assert surface_alpha(SURF_CONTENT, false) == 0.35; + assert surface_alpha(SURF_TERMINAL, false) == 0.15; + assert surface_alpha(SURF_PANEL, false) == 0.58; + assert surface_alpha(SURF_SIDEBAR, true) == 1.0; + assert surface_alpha(SURF_CONTENT, true) == 1.0; + assert surface_alpha(SURF_TERMINAL, true) == 1.0; + assert surface_alpha(SURF_PANEL, true) == 1.0; + var k : u32 = SURF_SIDEBAR; + while (k <= SURF_PANEL) { + // never opaque in a window, always opaque in full screen, and then the same as the frame + assert surface_alpha(k, false) < 1.0; + assert surface_alpha(k, false) > 0.0; + assert surface_clears(k, false); + assert surface_alpha(k, true) == backdrop_alpha(true); + assert surface_clears(k, true) == false; + k = k + 1; + } + // the terminal is drawn over the content: together they are the sidebar's tone (0.4475 against 0.45) + const both : f64 = 1.0 - (1.0 - surface_alpha(SURF_CONTENT, false)) * (1.0 - surface_alpha(SURF_TERMINAL, false)); + assert @abs(both - surface_alpha(SURF_SIDEBAR, false)) < 0.01; + // a right-hand panel is the darkest surface, and still glass + assert surface_alpha(SURF_PANEL, false) > surface_alpha(SURF_SIDEBAR, false); + assert surface_alpha(SURF_PANEL, false) < 1.0; + // a plate wiped inside a panel in full screen shows the layer, which is opaque black there + assert tint_alpha(true) == 1.0; +} + +test an_open_panel_takes_its_width_from_the_terminal { + const sw : f64 = 960.0; + // no panel: the content runs to the window's edge, as it always did + assert content_right(sw, false) == sw; + assert content_w(sw, WS_EXPANDED, false, false) == sw - content_x(WS_EXPANDED, false); + // a panel (Queen, DOCTOR or KEYS, all QP_W wide): the content ends at the panel's left edge + assert content_right(sw, true) == qp_left(sw); + assert content_right(sw, true) == sw - QP_W; + assert content_w(sw, WS_EXPANDED, false, true) == content_w(sw, WS_EXPANDED, false, false) - QP_W; + assert content_w(sw, WS_EXPANDED, false, true) == 387.0; + assert content_w(sw, WS_EXPANDED, true, true) == 660.0; + // the grid the pty is resized to is narrower, and its last cell ends before the panel begins + assert tp_cols(content_w(sw, WS_EXPANDED, false, true)) < tp_cols(content_w(sw, WS_EXPANDED, false, false)); + assert tp_cols(content_w(sw, WS_EXPANDED, false, true)) == 49; + assert tp_cell_x(content_x(WS_EXPANDED, false), tp_cols(content_w(sw, WS_EXPANDED, false, true))) <= qp_left(sw); + // full screen: wider window, same rule + assert content_w(1920.0, WS_EXPANDED, false, true) == 1920.0 - QP_W - content_x(WS_EXPANDED, false); + // a window too narrow for the panel leaves no negative width + assert content_w(200.0, WS_COMPACT, false, true) == 0.0; +} + +test the_keys_foot_gives_the_status_the_open_in_row_and_each_chip_row_a_band_at_960_by_540 { + const ft : f64 = ky_row_top(WS_TOP, PROVIDER_COUNT); + const last : f64 = ky_row_top(WS_TOP, PROVIDER_COUNT - 1); + // the foot starts below the last provider row's buttons and plates + assert last + KY_BTN_DY + KY_BTN_H + 2.0 <= foot_status_top(ft); + // the bench Mac's six chips (T27 OS, Cursor, Xcode, Antigravity, Antigravity IDE, ZCode) are two rows + assert foot_status_fits(ft, H_PT); + assert foot_chips(ft, H_PT, 6) == 6; + assert foot_chips_top(ft) + 2.0 * ED_SLOT_H <= H_PT; + // the second row of chips (the screenshot's Antigravity row) is below the status line's ink, not on it + const status_base : f64 = foot_status_top(ft) + FT_BASE; + const row1_base : f64 = ed_slot_y(foot_chips_top(ft), ED_COLS) + ED_BASE; + assert row1_base - FT_ASC > status_base + FT_DESC; + // status, OPEN IN, chip row 0, chip row 1: each baseline is a full ink height below the one before + const head_base : f64 = foot_head_top(ft) + FT_BASE; + const row0_base : f64 = ed_slot_y(foot_chips_top(ft), 0) + ED_BASE; + assert head_base - FT_ASC > status_base + FT_DESC; + assert row0_base - FT_ASC > head_base + FT_DESC; + assert row1_base - FT_ASC > row0_base + FT_DESC; + // a taller window or a full screen shows every chip of a full table, and still nothing under the bottom edge + assert foot_chips(ft, 900.0, EDITOR_COUNT) == EDITOR_COUNT; + assert foot_chips(ft, 1440.0, EDITOR_COUNT) == EDITOR_COUNT; + assert foot_bottom(ft, 900.0, EDITOR_COUNT) <= 900.0; + // the window's own height holds six chips of a longer table: whole rows only, never a partial one + assert foot_chips(ft, H_PT, EDITOR_COUNT) == 6; +} From ef053f4b53bfd2742c1ba2a3e2d9638c9d074b56 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 20 Oct 2026 14:29:33 +0700 Subject: [PATCH 2/3] T27 OS window: a click on a terminal pane is the pane's; no card, petal or control under it answers While a Claude Code pane or the editor view covers the content area, the click handler still asked card_at, so a click on the terminal's input row landed on a hidden honeycomb cell and opened or switched an agent chat. term_up / on_term / term_click decide that a click inside the terminal's rectangle belongs to the pane (its cursor goes solid) and the chain below never sees it; card_live answers no card while a pane is up, in the click and under the cursor. Every other control of the chain (title bar, header toggle, Close Dashboard, "+", the compact dashboard bar, the sidebar rows and foot links, a right-hand panel) lies outside the rectangle: controls_under_term samples it in six layouts and counts none. Refs #10782 Co-Authored-By: Claude Sonnet 5.5 --- specs/t27os/glass_window.t27 | 108 ++++++++++++++++++++++++++++++++++- 1 file changed, 105 insertions(+), 3 deletions(-) diff --git a/specs/t27os/glass_window.t27 b/specs/t27os/glass_window.t27 index 96b5ac1e79..30bed515db 100644 --- a/specs/t27os/glass_window.t27 +++ b/specs/t27os/glass_window.t27 @@ -1210,6 +1210,34 @@ pub fn card_at(x: f64, y: f64, sw: f64, sh: f64, ws: u32, collapsed: bool) -> u3 return CARD_SLOTS; } +/// T. A terminal pane covers the content area: a chat whose agent opens one, or the editor view, on the chat +/// screen (`topen` is the pane the window shows, PANE_NONE when none). +pub fn term_up(screen: bool, topen: u32) -> bool { + return screen and topen != PANE_NONE; +} + +/// T. The terminal's rectangle, as draw_term lays it out: from the content's left edge to content_right, +/// from under the header to the bottom edge (half open: the header's hairline and the window's edge are out). +pub fn on_term(x: f64, y: f64, sw: f64, sh: f64, ws: u32, collapsed: bool, panel: bool) -> bool { + const l : f64 = content_x(ws, collapsed); + const t : f64 = WS_TOP + HEADER_H; + return x >= l and x < l + content_w(sw, ws, collapsed, panel) and y >= t and y < sh; +} + +/// T. A click that belongs to the terminal: it lands on the rectangle of a pane that is up. The pane takes +/// it (the cursor goes solid) and nothing under it reacts -- the honeycomb cell there used to open an agent +/// chooser when the input row was clicked (owner, 2026-10-20). +pub fn term_click(up: bool, x: f64, y: f64, sw: f64, sh: f64, ws: u32, collapsed: bool, panel: bool) -> bool { + return up and on_term(x, y, sw, sh, ws, collapsed, panel); +} + +/// C. The card slot a click or the cursor sees: none while a terminal pane is up (the cards are not drawn then, +/// the pane covers them), else the slot under (x, y). +pub fn card_live(up: bool, x: f64, y: f64, sw: f64, sh: f64, ws: u32, collapsed: bool) -> u32 { + if (up) { return CARD_SLOTS; } + return card_at(x, y, sw, sh, ws, collapsed); +} + /// C. Cards arrive one after another: slot k shows CASCADE of the fade later than slot k - 1. pub fn card_alpha(ma: f64, k: u32) -> f64 { return clamp01((ma - CASCADE * @as(f64, @floatFromInt(k))) / (1.0 - CASCADE * 8.0)); @@ -3948,6 +3976,11 @@ pub fn main() -> void { darmed = ARMED_NONE; } } + } else if (term_click(term_up(screen, topen), mx, my, sw, sh, ws, collapsed, qp or dp or kyp)) { + // T. a click on the terminal pane is the pane's: its cursor goes solid and nothing under it + // reacts (no card, no petal, no bar); every control the chain below answers lies outside + // on_term (the title bar, the header, the dashboard bar, the sidebar), or is a card + tsince = 0; } else if (on_home_button(mx, my, sw)) { ctl = C_HOME; state = on_back(state); @@ -3983,9 +4016,9 @@ pub fn main() -> void { if (session_count(ss) > before) { cur = before; task = nk; } ctl = C_NEW_CHAT; carg = nk; - } else if (screen and card_at(mx, my, sw, sh, ws, collapsed) != CARD_SLOTS) { + } else if (screen and card_live(term_up(screen, topen), mx, my, sw, sh, ws, collapsed) != CARD_SLOTS) { // A. a tap on a cell opens that agent's chat here, or a first one if there is none - task = card_at(mx, my, sw, sh, ws, collapsed); + task = card_live(term_up(screen, topen), mx, my, sw, sh, ws, collapsed); ctl = C_CELL; carg = task; cur = SESSION_NONE; @@ -4504,7 +4537,7 @@ pub fn main() -> void { var on_control : bool = on_home_button(mx, my, sw) or on_full_button(mx, my, sw) or on_dock_button(mx, my, sw); if (screen and (on_nav_back(mx, my, full) or on_side_toggle(mx, my, ws, collapsed))) { on_control = true; } if (screen and (on_open_dash(mx, my, sw, ws) or on_close_dash(mx, my, ws, collapsed))) { on_control = true; } - if (screen and ma >= 1.0 and card_at(mx, my, sw, sh, ws, collapsed) != CARD_SLOTS) { on_control = true; } + if (screen and ma >= 1.0 and card_live(term_up(screen, topen), mx, my, sw, sh, ws, collapsed) != CARD_SLOTS) { on_control = true; } if (screen and ma >= 1.0 and in_side(mx, ws, collapsed) and (row_at(WS_TOP, GROUP_QUEEN, spaces_shown(sp), 1, my) == 0 or row_at(WS_TOP, GROUP_SPACES, spaces_shown(sp), spaces_shown(sp), my) != ROW_NONE or row_at(WS_TOP, GROUP_AGENTS, spaces_shown(sp), sessions_in(ss, space), my) != ROW_NONE)) { on_control = true; } if (screen and on_new_chat(mx, my, sw, ws)) { on_control = true; } if (screen and (on_repo_link(mx, my, sh, ws, collapsed) or (in_side(mx, ws, collapsed) and on_space_plus(mx, my) and new_space(sp) != SPACE_NONE))) { on_control = true; } @@ -5102,3 +5135,72 @@ test the_keys_foot_gives_the_status_the_open_in_row_and_each_chip_row_a_band_at_ // the window's own height holds six chips of a longer table: whole rows only, never a partial one assert foot_chips(ft, H_PT, EDITOR_COUNT) == 6; } + +/// Test helper: how many sampled points of the terminal's rectangle (every 12 pt) a window control answers at. +/// The title bar, the header (toggle, Close Dashboard, "+"), the compact dashboard bar, the sidebar and a +/// right-hand panel all lie outside that rectangle, so this is 0 in every layout. The home logo's petals need +/// the home screen, where no pane is up. +fn controls_under_term(sw: f64, sh: f64, ws: u32, collapsed: bool, panel: bool) -> u32 { + var hits : u32 = 0; + const l : f64 = content_x(ws, collapsed); + const r : f64 = l + content_w(sw, ws, collapsed, panel); + var y : f64 = WS_TOP + HEADER_H; + while (y < sh) { + var x : f64 = l; + while (x < r) { + if (on_home_button(x, y, sw) or on_full_button(x, y, sw) or on_dock_button(x, y, sw)) { hits = hits + 1; } + if (on_nav_back(x, y, false) or on_nav_back(x, y, true)) { hits = hits + 1; } + if (on_open_dash(x, y, sw, ws) or on_close_dash(x, y, ws, collapsed)) { hits = hits + 1; } + if (on_side_toggle(x, y, ws, collapsed) or on_new_chat(x, y, sw, ws)) { hits = hits + 1; } + // the sidebar's rows and its "+" answer only where in_side says the sidebar is + if (in_side(x, ws, collapsed)) { hits = hits + 1; } + if (on_repo_link(x, y, sh, ws, collapsed) or on_doctor_link(x, y, sh, ws, collapsed) or on_keys_foot(x, y, sh, ws, collapsed)) { hits = hits + 1; } + if (panel and on_panel(x, y, sw, WS_TOP)) { hits = hits + 1; } + x = x + 12.0; + } + y = y + 12.0; + } + return hits; +} + +test a_click_on_a_terminal_pane_is_the_panes_and_nothing_under_it_answers { + // the bug: the input row of a Claude Code pane lies over a honeycomb cell, and the click opened an agent chat + const sw : f64 = W_PT; + const sh : f64 = H_PT; + const bl : f64 = body_left(sw, WS_EXPANDED, false); + const bt : f64 = body_top(WS_EXPANDED); + const bw : f64 = body_w(sw, WS_EXPANDED, false); + const bh : f64 = body_h(sw, sh, WS_EXPANDED, false); + const cx : f64 = hex_c(4, 0, bl, bt, bw, bh); + const cy : f64 = hex_c(4, 1, bl, bt, bw, bh); + assert card_at(cx, cy, sw, sh, WS_EXPANDED, false) == 4; + assert on_term(cx, cy, sw, sh, WS_EXPANDED, false, false); + // with a pane up the click is the pane's and no card answers it, in the click and under the cursor + assert term_click(term_up(true, 2), cx, cy, sw, sh, WS_EXPANDED, false, false); + assert card_live(term_up(true, 2), cx, cy, sw, sh, WS_EXPANDED, false) == CARD_SLOTS; + // the editor view is a pane too + assert term_up(true, VIEW_EDITOR); + assert term_click(term_up(true, VIEW_EDITOR), cx, cy, sw, sh, WS_EXPANDED, false, false); + // with no pane the card answers as before, and the pane takes nothing + assert term_up(true, PANE_NONE) == false; + assert term_up(false, 2) == false; + assert term_click(term_up(true, PANE_NONE), cx, cy, sw, sh, WS_EXPANDED, false, false) == false; + assert card_live(term_up(true, PANE_NONE), cx, cy, sw, sh, WS_EXPANDED, false) == 4; + // the rectangle is the terminal's: right of the sidebar, under the header, short of an open panel + assert on_term(content_x(WS_EXPANDED, false) + 1.0, WS_TOP + HEADER_H + 1.0, sw, sh, WS_EXPANDED, false, false); + assert on_term(content_x(WS_EXPANDED, false) - 2.0, WS_TOP + HEADER_H + 1.0, sw, sh, WS_EXPANDED, false, false) == false; + assert on_term(500.0, WS_TOP + HEADER_H - 1.0, sw, sh, WS_EXPANDED, false, false) == false; + assert on_term(500.0, sh, sw, sh, WS_EXPANDED, false, false) == false; + assert on_term(sw - 1.0, 300.0, sw, sh, WS_EXPANDED, false, false); + assert on_term(sw - 1.0, 300.0, sw, sh, WS_EXPANDED, false, true) == false; + assert on_term(qp_left(sw) - 1.0, 300.0, sw, sh, WS_EXPANDED, false, true); + // the sidebar's rows, the header's buttons, the title bar, the dashboard bar: none is inside the rectangle + assert controls_under_term(sw, sh, WS_EXPANDED, false, false) == 0; + assert controls_under_term(sw, sh, WS_EXPANDED, false, true) == 0; + assert controls_under_term(sw, sh, WS_EXPANDED, true, false) == 0; + assert controls_under_term(1728.0, 1117.0, WS_EXPANDED, false, false) == 0; + assert controls_under_term(1728.0, 1117.0, WS_EXPANDED, false, true) == 0; + assert controls_under_term(700.0, 900.0, WS_DASHBOARD, false, false) == 0; + assert controls_under_term(PANEL_W, 900.0, WS_COMPACT, false, false) == 0; + assert controls_under_term(PANEL_W, 900.0, WS_COMPACT, false, true) == 0; +} From 2aafb88cea0f9d40f017470cdd6fe3fc9f95e8ba Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 20 Oct 2026 14:42:31 +0700 Subject: [PATCH 3/3] T27 OS: matte glass everywhere in a window, black in full screen, panels narrow the terminal, KEYS foot rows, terminal clicks stay in the terminal (Closes #11262) Merge of master plus sealing, gates run on the merged tree with t27c from master: tests clean with vacuous passes 0, seal --verify MATCH, tri types ratchet CLEAN, dupe_scan failure set a subset of master's. Co-Authored-By: Claude Sonnet 5.5 --- .trinity/seals/t27os_T27osEditors.json | 33 ++++++++++++++++++++++ .trinity/seals/t27os_T27osTermPane.json | 37 +++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 .trinity/seals/t27os_T27osEditors.json create mode 100644 .trinity/seals/t27os_T27osTermPane.json diff --git a/.trinity/seals/t27os_T27osEditors.json b/.trinity/seals/t27os_T27osEditors.json new file mode 100644 index 0000000000..1792d3ce5e --- /dev/null +++ b/.trinity/seals/t27os_T27osEditors.json @@ -0,0 +1,33 @@ +{ + "built_by": "t27c-bootstrap@0.5.2+339a2d31b", + "closure": [ + { + "spec": "specs/t27os/session_store.t27", + "spec_hash": "sha256:7d05d7dda14587458a77291c3d587e3f08516d8e678f0a4bd06dcaab7f6d1f2f" + } + ], + "config": "gen=c,rust,verilog,zig;tests=zig", + "gen_hash_c": "sha256:c884fccdcb816ab5c48fd289bd04515452873c300889d29149cd7481abcf8939", + "gen_hash_rust": "sha256:cac70330d6d304ab3800f870ca5114f10bb2f5d1e029c4de28d2f7232f5a92ca", + "gen_hash_verilog": "sha256:82a39ca4aa16b7d3c37d5fa37f7ac3aeecfde29d0d31030fa23726ef1235eacd", + "gen_hash_zig": "sha256:4dd5942ad00da4c9bc011cb373421e8da83eb6cb98a8ff1bdb1caafb5e98fe78", + "module": "T27osEditors", + "ring": 12, + "seal_schema": 2, + "sealed_at": "2026-10-20T07:42:07Z", + "sealed_by": "t27c-bootstrap@0.5.2", + "spec_hash": "sha256:fc3608c5131f7fae21d6084102a019fc0425117ecf7544ec1eebfa8155d74f72", + "spec_path": "specs/t27os/editors.t27", + "tests": { + "failed": 0, + "failing": [], + "forced": false, + "passed": 30, + "total": 30, + "vacuous": 0 + }, + "toolchain": { + "test_runner": "sha256:8512b80da533b79289110fca4a65f3336abd7a059ca4b72ea595d5e33ff8ad10", + "zig": "0.16.0" + } +} \ No newline at end of file diff --git a/.trinity/seals/t27os_T27osTermPane.json b/.trinity/seals/t27os_T27osTermPane.json new file mode 100644 index 0000000000..75b2650dd8 --- /dev/null +++ b/.trinity/seals/t27os_T27osTermPane.json @@ -0,0 +1,37 @@ +{ + "built_by": "t27c-bootstrap@0.5.2+339a2d31b", + "closure": [ + { + "spec": "specs/t27os/pty_proto.t27", + "spec_hash": "sha256:fe9dee96f99eccaa6273124bb9022a66ca916bc9a0f6c3f415c2f4b6ab964ab1" + }, + { + "spec": "specs/t27os/windows.t27", + "spec_hash": "sha256:eddbb0bab395f5d23c89d2e43ee081a2e0c0281ff112151aa61c7fab0ee875af" + } + ], + "config": "gen=c,rust,verilog,zig;tests=zig", + "gen_hash_c": "sha256:97ceec65b02689bcecdc7a3c8e9cdd359f34d6d8cd073c7d846ea7134205b604", + "gen_hash_rust": "sha256:30d38401423690741c966e8f2067738639bc403e5d32df756d4a0fe16e35ce24", + "gen_hash_verilog": "sha256:a53251a619ef71e8be2dd0de46530dcabd56f70af9701b8c3a81649725a66a10", + "gen_hash_zig": "sha256:0edf236094c91e001fc45a83c938e113451211b31853e0c1d366134693ad4116", + "module": "T27osTermPane", + "ring": 12, + "seal_schema": 2, + "sealed_at": "2026-10-20T07:42:12Z", + "sealed_by": "t27c-bootstrap@0.5.2", + "spec_hash": "sha256:50a9ff94e1c09c8e4d3ac9e2199c871ee00d821cd3e5d9c5b12fc5b994f5ba4e", + "spec_path": "specs/t27os/term_pane.t27", + "tests": { + "failed": 0, + "failing": [], + "forced": false, + "passed": 10, + "total": 10, + "vacuous": 0 + }, + "toolchain": { + "test_runner": "sha256:8512b80da533b79289110fca4a65f3336abd7a059ca4b72ea595d5e33ff8ad10", + "zig": "0.16.0" + } +} \ No newline at end of file