feat: match preview styling to xterm.js — palette, background, font, line-height

ANSI_COLORS: replaced with xterm.js default GTK/Tango palette.
Background: #000000 on tile-body, sidebar-item-body, preview-popover.
Font: 'SF Mono', 'Fira Code', Consolas, monospace (same as terminal).
Line-height: 1.0 across all preview surfaces (xterm.js default).
Gradient overlays: fade to #000 instead of theme bg.
Result: previews are visually indistinguishable from the terminal.
This commit is contained in:
Brian Krabach
2026-03-29 19:19:05 -07:00
parent 85298abdcc
commit 583de6ec2d
4 changed files with 122 additions and 16 deletions
+2 -2
View File
@@ -269,8 +269,8 @@ function escapeHtml(str) {
// Converts terminal color sequences to <span> tags with inline styles.
// ---------------------------------------------------------------------------
var ANSI_COLORS = [
'#000','#c23621','#25bc26','#adad27','#492ee1','#d338d3','#33bbc8','#cbcccd',
'#818383','#fc391f','#31e722','#eaec23','#5833ff','#f935f8','#14f0f0','#e9ebeb'
'#2e3436','#cc0000','#4e9a06','#c4a000','#3465a4','#75507b','#06989a','#d3d7cf',
'#555753','#ef2929','#8ae234','#fce94f','#729fcf','#ad7fa8','#34e2e2','#eeeeec'
];
function ansiToHtml(raw) {
+13 -10
View File
@@ -221,6 +221,7 @@ body {
flex: 1;
overflow: hidden;
position: relative;
background: #000000;
}
.tile-body pre {
@@ -229,8 +230,9 @@ body {
left: 0;
right: 0;
padding: 6px 8px;
font-family: var(--font-mono);
font-family: 'SF Mono', 'Fira Code', Consolas, monospace;
font-size: 11px;
line-height: 1.0;
color: #c9d1d9; /* match xterm.js foreground */
white-space: pre;
overflow: hidden;
@@ -257,7 +259,7 @@ body {
left: 0;
right: 0;
height: 40px;
background: linear-gradient(to bottom, transparent, var(--bg-tile));
background: linear-gradient(to bottom, transparent, #000000);
pointer-events: none;
z-index: 1;
}
@@ -518,6 +520,7 @@ body {
flex: 1;
position: relative;
overflow: hidden;
background: #000000;
}
.sidebar-item-body::before {
@@ -527,13 +530,13 @@ body {
left: 0;
right: 0;
height: 32px;
background: linear-gradient(to bottom, transparent, var(--bg-secondary));
background: linear-gradient(to bottom, transparent, #000000);
pointer-events: none;
z-index: 1;
}
.sidebar-item--active .sidebar-item-body::before {
background: linear-gradient(to bottom, transparent, var(--bg-surface));
background: linear-gradient(to bottom, transparent, #000000);
}
.sidebar-item-body pre {
@@ -541,9 +544,9 @@ body {
bottom: 0;
left: 0;
right: 0;
font-family: var(--font-mono);
font-family: 'SF Mono', 'Fira Code', Consolas, monospace;
font-size: 10px;
line-height: 1.4;
line-height: 1.0;
color: #c9d1d9; /* match xterm.js foreground */
white-space: pre;
padding: 0 8px 6px;
@@ -966,7 +969,7 @@ body {
.preview-popover {
position: fixed;
z-index: 500;
background: var(--bg-secondary);
background: #000000;
border: 2px solid var(--accent);
border-radius: 8px;
padding: 16px 20px;
@@ -982,9 +985,9 @@ body {
.preview-popover pre {
margin: 0;
font-family: var(--font-mono);
font-size: 12px;
line-height: 1.5;
font-family: 'SF Mono', 'Fira Code', Consolas, monospace;
font-size: 14px;
line-height: 1.0;
color: #c9d1d9; /* match xterm.js foreground exactly */
white-space: pre-wrap; /* WRAP text instead of horizontal overflow */
word-break: break-all; /* break long lines at viewport edge */
+13
View File
@@ -2114,4 +2114,17 @@ test('ansiToHtml converts SGR codes to styled spans', () => {
assert.ok(source.includes('ansiToHtml(session.snapshot)'), 'overlay must use ansiToHtml');
});
test('ANSI_COLORS uses xterm.js default GTK/Tango palette', () => {
// xterm.js default palette (GTK/Tango-derived) — must match exactly
// so previews render colors identically to the interactive terminal
assert.strictEqual(app.ansi256Color(0), '#2e3436', 'color 0 must be xterm.js default: #2e3436');
assert.strictEqual(app.ansi256Color(1), '#cc0000', 'color 1 must be xterm.js default: #cc0000');
assert.strictEqual(app.ansi256Color(2), '#4e9a06', 'color 2 must be xterm.js default: #4e9a06');
assert.strictEqual(app.ansi256Color(3), '#c4a000', 'color 3 must be xterm.js default: #c4a000');
assert.strictEqual(app.ansi256Color(7), '#d3d7cf', 'color 7 must be xterm.js default: #d3d7cf');
assert.strictEqual(app.ansi256Color(8), '#555753', 'color 8 must be xterm.js default: #555753');
assert.strictEqual(app.ansi256Color(10), '#8ae234', 'color 10 must be xterm.js default: #8ae234');
assert.strictEqual(app.ansi256Color(14), '#34e2e2', 'color 14 must be xterm.js default: #34e2e2');
});
+94 -4
View File
@@ -403,7 +403,7 @@ def test_css_sidebar_item_body():
def test_css_sidebar_item_body_pre():
""".sidebar-item-body pre must be anchored to bottom with 10px monospace font using --font-mono token."""
""".sidebar-item-body pre must be anchored to bottom with 10px monospace font matching xterm.js."""
css = read_css()
assert ".sidebar-item-body pre" in css
block = _extract_rule_block(css, ".sidebar-item-body pre {")
@@ -412,12 +412,12 @@ def test_css_sidebar_item_body_pre():
assert "left: 0" in block
assert "right: 0" in block
assert "font-size: 10px" in block
assert "line-height: 1.4" in block
assert "line-height: 1.0" in block, "sidebar-item-body pre must use line-height: 1.0 (xterm.js default)"
assert "#c9d1d9" in block, "sidebar-item-body pre must match xterm.js foreground"
assert "white-space: pre" in block
assert "padding: 0 8px 6px" in block
# Monospace font family via design token
assert "font-family: var(--font-mono)" in block
# Explicit xterm.js font family (not design token variable)
assert "'SF Mono'" in block or "SF Mono" in block, "sidebar-item-body pre must use explicit xterm.js font family"
def test_css_sidebar_empty():
@@ -640,3 +640,93 @@ def test_preview_popover_has_accent_border():
end = css.index("}", start)
block = css[start:end]
assert "var(--accent)" in block, ".preview-popover must use var(--accent) border"
def test_preview_popover_has_black_background():
""".preview-popover must use #000000 background to match xterm.js terminal."""
css = read_css()
start = css.index(".preview-popover {")
end = css.index("}", start)
block = css[start:end]
assert "#000000" in block or "background: #000" in block, (
".preview-popover must use #000000 background (not var(--bg-secondary))"
)
def test_preview_popover_pre_matches_xterm_typography():
""".preview-popover pre must match xterm.js terminal: 14px, line-height 1.0, explicit font stack."""
css = read_css()
block = _extract_rule_block(css, ".preview-popover pre {")
assert "font-size: 14px" in block, ".preview-popover pre must use 14px (xterm.js default)"
assert "line-height: 1.0" in block, ".preview-popover pre must use line-height: 1.0 (xterm.js default)"
assert "'SF Mono'" in block or "SF Mono" in block, (
".preview-popover pre must use explicit xterm.js font family"
)
def test_tile_body_has_black_background():
""".tile-body must use #000000 background to match xterm.js terminal."""
css = read_css()
block = _extract_rule_block(css, ".tile-body {")
assert "#000000" in block or "background: #000" in block, (
".tile-body must use #000000 background to match xterm.js"
)
def test_tile_body_pre_has_xterm_line_height():
""".tile-body pre must use line-height: 1.0 to match xterm.js terminal."""
css = read_css()
block = _extract_rule_block(css, ".tile-body pre {")
assert "line-height: 1.0" in block, ".tile-body pre must use line-height: 1.0 (xterm.js default)"
def test_tile_body_pre_has_explicit_font_family():
""".tile-body pre must use explicit xterm.js font family, not CSS variable."""
css = read_css()
block = _extract_rule_block(css, ".tile-body pre {")
assert "'SF Mono'" in block or "SF Mono" in block, (
".tile-body pre must use explicit xterm.js font family"
)
def test_sidebar_item_body_has_black_background():
""".sidebar-item-body must use #000000 background to match xterm.js terminal."""
css = read_css()
block = _extract_rule_block(css, ".sidebar-item-body {")
assert "#000000" in block or "background: #000" in block, (
".sidebar-item-body must use #000000 background to match xterm.js"
)
def test_sidebar_item_body_pre_has_xterm_line_height():
""".sidebar-item-body pre must use line-height: 1.0 to match xterm.js terminal."""
css = read_css()
block = _extract_rule_block(css, ".sidebar-item-body pre {")
assert "line-height: 1.0" in block, ".sidebar-item-body pre must use line-height: 1.0 (xterm.js default)"
def test_sidebar_item_body_pre_has_explicit_font_family():
""".sidebar-item-body pre must use explicit xterm.js font family, not CSS variable."""
css = read_css()
block = _extract_rule_block(css, ".sidebar-item-body pre {")
assert "'SF Mono'" in block or "SF Mono" in block, (
".sidebar-item-body pre must use explicit xterm.js font family"
)
def test_sidebar_item_body_gradient_fades_to_black():
""".sidebar-item-body::before gradient must fade to #000000 (not theme background)."""
css = read_css()
block = _extract_rule_block(css, ".sidebar-item-body::before {")
assert "#000000" in block or "transparent, #000" in block, (
".sidebar-item-body::before gradient must fade to #000000 to match black terminal bg"
)
def test_tile_body_gradient_fades_to_black():
""".tile-body::before gradient must fade to #000000 (not theme background)."""
css = read_css()
block = _extract_rule_block(css, ".tile-body::before {")
assert "#000000" in block or "transparent, #000" in block, (
".tile-body::before gradient must fade to #000000 to match black terminal bg"
)