╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ───────────────╮ │src/ui/widget/thread.zig│ ╰────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │478 const selected_id = if (self.selectedTab()) |tab| self.tab_ids.keys()[tab] else null; │ │479 for (self.box.children.keys(), self.box.children.values()) |id, *child| { │ │480 switch (child.widget) { │ │481 - .text_box => |*tb| tb.options.border_style = if (selected_id == id) .single else .hidden,│ │481 + .text_box => |*tb| { │ │482 + const selected = selected_id == id; │ │483 + tb.options.border_style = if (selected) .single else .hidden; │ │484 + tb.options.inverted = selected; │ │485 + }, │ │486 else => {}, │ │487 } │ │488 } │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │2296 const lb = self.listBox(i); │ │2297 for (lb.children.keys(), lb.children.values()) |id, *child| { │ │2298 switch (child.widget) { │ │2295 - .text_box => |*tb| tb.options.border_style = if (lb.getFocus().child_id == id) .single else .hidden,│ │2299 + .text_box => |*tb| { │ │2300 + const selected = lb.getFocus().child_id == id; │ │2301 + tb.options.border_style = if (selected) .single else .hidden; │ │2302 + tb.options.inverted = selected; │ │2303 + }, │ │2304 else => {}, │ │2305 } │ │2306 } │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ──╮ │src/web.zig│ ╰───────────╯ ╭─────────────────────────────────────────────╮ │2042 var cur_id: ?usize = null; │ │2043 var cur_fg: ?Grid.Color = null;│ │2044 var cur_bg: ?Grid.Color = null;│ │2045 + var cur_inverted = false; │ │2046 var open_tag: ?CellTag = null; │ │2047 var first = true; │ │2048 for (0..grid.size.width) |x| { │ ╰─────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │2062 const cell_id = if (covered_by_scroll) null else owners[y * grid.size.width + x]; │ │2063 const fg = if (covered_by_scroll) null else cell.style.fg; │ │2064 const bg = cell.style.bg; │ │2065 + const inverted = !covered_by_scroll and cell.style.inverted; │ │2066 │ │2065 - if (first or cell_id != cur_id or !colorEql(fg, cur_fg) or !colorEql(bg, cur_bg)) { │ │2067 + if (first or cell_id != cur_id or !colorEql(fg, cur_fg) or !colorEql(bg, cur_bg) or inverted != cur_inverted) {│ │2068 if (open_tag) |t| try output.appendSlice(allocator, t.closeTag()); │ │2069 │ │2070 var new_tag: ?CellTag = null; │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────╮ │2076 }; │ │2077 } │ │2078 } │ │2077 - if (new_tag == null and (fg != null or bg != null)) new_tag = .plain; │ │2079 + if (new_tag == null and (fg != null or bg != null or inverted)) new_tag = .plain;│ │2080 │ │2081 if (new_tag) |t| { │ │2082 var style_buf: [64]u8 = undefined; │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │2081 - try t.writeOpenTag(allocator, output, cell_id orelse 0, styleAttr(&style_buf, fg, bg)); │ │2083 + try t.writeOpenTag(allocator, output, cell_id orelse 0, styleAttr(&style_buf, fg, bg, inverted));│ │2084 } │ │2085 │ │2086 open_tag = new_tag; │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────╮ │2087 cur_id = cell_id; │ │2088 cur_fg = fg; │ │2089 cur_bg = bg; │ │2090 + cur_inverted = inverted;│ │2091 first = false; │ │2092 } │ │2093 │ ╰──────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │2294 return b == null; │ │2295 } │ │2296 │ │2294 -// builds an HTML ` style="color:#rrggbb;background-color:#rrggbb"` attribute │ │2295 -// from a cell's fg/bg into `buf`, omitting whichever color is unset. returns an │ │2296 -// empty slice when neither is set. │ │2297 -fn styleAttr(buf: []u8, fg: ?Grid.Color, bg: ?Grid.Color) []const u8 { │ │2298 - if (fg == null and bg == null) return buf[0..0]; │ │2297 +// the 16 ansi colors as the browser shows them (xterm's defaults) │ │2298 +const ansi_palette = [16]Grid.Color.Rgb{ │ │2299 + .{ .r = 0x00, .g = 0x00, .b = 0x00 }, .{ .r = 0xcd, .g = 0x00, .b = 0x00 }, .{ .r = 0x00, .g = 0xcd, .b = 0x00 }, .{ .r = 0xcd, .g = 0xcd, .b = 0x00 },│ │2300 + .{ .r = 0x00, .g = 0x00, .b = 0xee }, .{ .r = 0xcd, .g = 0x00, .b = 0xcd }, .{ .r = 0x00, .g = 0xcd, .b = 0xcd }, .{ .r = 0xe5, .g = 0xe5, .b = 0xe5 },│ │2301 + .{ .r = 0x7f, .g = 0x7f, .b = 0x7f }, .{ .r = 0xff, .g = 0x00, .b = 0x00 }, .{ .r = 0x00, .g = 0xff, .b = 0x00 }, .{ .r = 0xff, .g = 0xff, .b = 0x00 },│ │2302 + .{ .r = 0x5c, .g = 0x5c, .b = 0xff }, .{ .r = 0xff, .g = 0x00, .b = 0xff }, .{ .r = 0x00, .g = 0xff, .b = 0xff }, .{ .r = 0xff, .g = 0xff, .b = 0xff },│ │2303 +}; │ │2304 + │ │2305 +// resolve any color to rgb: palette colors through the ansi table and the │ │2306 +// xterm 256-color layout (16 ansi, a 6x6x6 cube, then 24 grays) │ │2307 +fn colorRgb(color: Grid.Color) Grid.Color.Rgb { │ │2308 + return switch (color) { │ │2309 + .rgb => |c| c, │ │2310 + .ansi => |a| ansi_palette[@intFromEnum(a)], │ │2311 + .indexed => |n| if (n < 16) │ │2312 + ansi_palette[n] │ │2313 + else if (n < 232) blk: { │ │2314 + const levels = [6]u8{ 0, 0x5f, 0x87, 0xaf, 0xd7, 0xff }; │ │2315 + const i = n - 16; │ │2316 + break :blk .{ .r = levels[i / 36], .g = levels[(i / 6) % 6], .b = levels[i % 6] }; │ │2317 + } else blk: { │ │2318 + const v: u8 = 8 + (n - 232) * 10; │ │2319 + break :blk .{ .r = v, .g = v, .b = v }; │ │2320 + }, │ │2321 + }; │ │2322 +} │ │2323 + │ │2324 +// builds an HTML ` style="color:…;background-color:…"` attribute from a │ │2325 +// cell's fg/bg into `buf`, omitting whichever color is unset. an inverted │ │2326 +// cell swaps the two, falling back to the page's --fg/--bg variables for │ │2327 +// whichever side is unset. returns an empty slice when nothing is set. │ │2328 +fn styleAttr(buf: []u8, fg: ?Grid.Color, bg: ?Grid.Color, inverted: bool) []const u8 { │ │2329 + if (fg == null and bg == null and !inverted) return buf[0..0]; │ │2330 const prefix = " style=\""; │ │2331 @memcpy(buf[0..prefix.len], prefix); │ │2332 var i: usize = prefix.len; │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │2302 - if (fg) |c| { │ │2303 - const s = std.fmt.bufPrint(buf[i..], "color:#{x:0>2}{x:0>2}{x:0>2};", .{ c.r, c.g, c.b }) catch return buf[0..0]; │ │2333 + const text = if (inverted) bg else fg; │ │2334 + const back = if (inverted) fg else bg; │ │2335 + if (text) |c| { │ │2336 + const v = colorRgb(c); │ │2337 + const s = std.fmt.bufPrint(buf[i..], "color:#{x:0>2}{x:0>2}{x:0>2};", .{ v.r, v.g, v.b }) catch return buf[0..0]; │ │2338 + i += s.len; │ │2339 + } else if (inverted) { │ │2340 + const s = std.fmt.bufPrint(buf[i..], "color:var(--bg);", .{}) catch return buf[0..0]; │ │2341 i += s.len; │ │2342 } │ │2306 - if (bg) |c| { │ │2307 - const s = std.fmt.bufPrint(buf[i..], "background-color:#{x:0>2}{x:0>2}{x:0>2};", .{ c.r, c.g, c.b }) catch return buf[0..0];│ │2343 + if (back) |c| { │ │2344 + const v = colorRgb(c); │ │2345 + const s = std.fmt.bufPrint(buf[i..], "background-color:#{x:0>2}{x:0>2}{x:0>2};", .{ v.r, v.g, v.b }) catch return buf[0..0];│ │2346 + i += s.len; │ │2347 + } else if (inverted) { │ │2348 + const s = std.fmt.bufPrint(buf[i..], "background-color:var(--fg);", .{}) catch return buf[0..0]; │ │2349 i += s.len; │ │2350 } │ │2351 buf[i] = '"'; │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯