diff --git a/CHANGELOG.md b/CHANGELOG.md
index 40b96f92..4507f822 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,12 @@
### Fixed
+## [v0.50.242] — 2026-04-30
+
+### Reverted
+- **Assistant message serif font (Georgia)** — Reverted the global `.assistant-turn .msg-body { font-family: var(--font-assistant) }` rule introduced in v0.50.240 (PR #1282). Assistant responses now render in the same system sans-serif stack as the rest of the UI, matching pre-v0.50.240 behavior. The `--font-assistant` CSS token has been removed. (`static/style.css`)
+- **Calm Console theme** — Removed the `data-theme="calm"` palette and its associated picker entry, theme-apply branch, and server-side enum value. The theme was the only consumer of the assistant serif rule and was not pulling its weight as a third theme option. Users who selected `calm` will fall back to the default theme on next page load (the server settings validator now rejects `calm` and resets to `dark`). (`static/style.css`, `static/boot.js`, `static/index.html`, `api/config.py`, `tests/test_ui_tool_call_cleanup.py`)
+
## [v0.50.241] — 2026-04-30
### Added
diff --git a/api/config.py b/api/config.py
index eb6a850e..21dde7d6 100644
--- a/api/config.py
+++ b/api/config.py
@@ -2179,7 +2179,7 @@ _SETTINGS_DEFAULTS = {
"show_cli_sessions": False, # merge CLI sessions from state.db into the sidebar
"sync_to_insights": False, # mirror WebUI token usage to state.db for /insights
"check_for_updates": True, # check if webui/agent repos are behind upstream
- "theme": "dark", # light | dark | system | calm
+ "theme": "dark", # light | dark | system
"skin": "default", # accent color skin: default | ares | mono | slate | poseidon | sisyphus | charizard
"font_size": "default", # small | default | large
"language": "en", # UI locale code; must match a key in static/i18n.js LOCALES
@@ -2196,7 +2196,7 @@ _SETTINGS_DEFAULTS = {
"password_hash": None, # PBKDF2-HMAC-SHA256 hash; None = auth disabled
}
_SETTINGS_LEGACY_DROP_KEYS = {"assistant_language", "bubble_layout", "default_model"}
-_SETTINGS_THEME_VALUES = {"light", "dark", "system", "calm"}
+_SETTINGS_THEME_VALUES = {"light", "dark", "system"}
_SETTINGS_SKIN_VALUES = {
"default",
"ares",
diff --git a/static/boot.js b/static/boot.js
index e5402c71..e6a1d267 100644
--- a/static/boot.js
+++ b/static/boot.js
@@ -630,7 +630,6 @@ const _THEMES=[
{name:'Light', value:'light', colors:['#FEFCF7','#FAF7F0','#B8860B']},
{name:'Dark', value:'dark', colors:['#0D0D1A','#141425','#FFD700']},
{name:'System', value:'system', colors:['#FEFCF7','#0D0D1A','#B8860B']},
- {name:'Calm', value:'calm', colors:['#C6AC8F','#EAE0D5','#22333B']},
];
const _SKINS=[
{name:'Default', colors:['#FFD700','#FFBF00','#CD7F32']},
@@ -689,11 +688,6 @@ function _applyTheme(name){
_systemThemeMq.addEventListener('change',_onSystemThemeChange);
return;
}
- if(normalized.theme==='calm'){
- document.documentElement.dataset.theme='calm';
- _setResolvedTheme(true);
- return;
- }
_setResolvedTheme(normalized.theme==='dark');
}
diff --git a/static/index.html b/static/index.html
index 3a1c3ef5..34dd1515 100644
--- a/static/index.html
+++ b/static/index.html
@@ -612,12 +612,6 @@
System
-
diff --git a/static/style.css b/static/style.css
index d82a303a..0cadcf49 100644
--- a/static/style.css
+++ b/static/style.css
@@ -13,7 +13,6 @@
--space-1:4px;--space-2:8px;--space-3:12px;--space-4:16px;
--font-size-xs:11px;--font-size-sm:12px;--font-size-md:14px;
--font-ui:-apple-system,BlinkMacSystemFont,"Segoe UI",Inter,system-ui,sans-serif;
- --font-assistant:Georgia,"Times New Roman",serif;
--surface-subtle:rgba(0,0,0,.025);--surface-subtle-hover:rgba(0,0,0,.045);
--border-subtle:rgba(0,0,0,.08);--border-muted:rgba(0,0,0,.12);
font-family:var(--font-ui);font-size:14px;line-height:1.6;
@@ -79,31 +78,6 @@
--surface-subtle:rgba(255,255,255,.025);--surface-subtle-hover:rgba(255,255,255,.045);
--border-subtle:rgba(255,255,255,.075);--border-muted:rgba(255,255,255,.12);
}
- /* ── Custom Theme: Calm Console (Coolors earth/slate palette) ── */
- :root[data-theme="calm"] {
- --bg:#EAE0D5;--sidebar:#F4EEE7;--border:#C6AC8F;--border2:rgba(10,9,8,0.13);
- --text:#0A0908;--muted:#5B4B3B;--accent:#22333B;--blue:#22333B;--gold:#C6AC8F;--code-bg:#D8C8B7;
- --surface:#F4EEE7;--topbar-bg:rgba(244,238,231,.96);--main-bg:rgba(234,224,213,0.72);
- --focus-ring:rgba(34,51,59,.28);--focus-glow:rgba(34,51,59,.08);
- --input-bg:rgba(10,9,8,.035);--hover-bg:rgba(10,9,8,.055);
- --strong:#0A0908;--em:#3B3028;--code-text:#22333B;--code-inline-bg:rgba(10,9,8,.06);--pre-text:#0A0908;
- --accent-hover:#0A0908;--accent-bg:rgba(34,51,59,0.08);--accent-bg-strong:rgba(34,51,59,0.15);--accent-text:#22333B;
- --error:#B42318;--success:#2F6B3F;--warning:#8A5A18;--info:#22333B;
- --surface-subtle:rgba(10,9,8,.025);--surface-subtle-hover:rgba(10,9,8,.045);
- --border-subtle:rgba(10,9,8,.08);--border-muted:rgba(10,9,8,.12);
- }
- :root.dark[data-theme="calm"] {
- --bg:#0A0908;--sidebar:#22333B;--border:#3B4A50;--border2:rgba(234,224,213,0.16);
- --text:#EAE0D5;--muted:#C6AC8F;--accent:#C6AC8F;--blue:#C6AC8F;--gold:#C6AC8F;--code-bg:#11100E;
- --surface:#22333B;--topbar-bg:rgba(34,51,59,.96);--main-bg:rgba(10,9,8,0.72);
- --focus-ring:rgba(198,172,143,.28);--focus-glow:rgba(198,172,143,.08);
- --input-bg:rgba(234,224,213,.035);--hover-bg:rgba(234,224,213,.055);
- --strong:#F7F0E8;--em:#D8C6B2;--code-text:#C6AC8F;--code-inline-bg:rgba(234,224,213,.07);--pre-text:#EAE0D5;
- --accent-hover:#EAE0D5;--accent-bg:rgba(198,172,143,0.08);--accent-bg-strong:rgba(198,172,143,0.14);--accent-text:#C6AC8F;
- --error:#F87171;--success:#86C08B;--warning:#E0B15D;--info:#C6AC8F;
- --surface-subtle:rgba(234,224,213,.025);--surface-subtle-hover:rgba(234,224,213,.045);
- --border-subtle:rgba(234,224,213,.075);--border-muted:rgba(234,224,213,.12);
- }
/* ── Skin: Default (gold — matches base) ── */
/* No overrides needed — :root and .dark already use gold accent */
/* ── Skin: Ares (red) ── */
@@ -2264,11 +2238,9 @@ main.main.showing-profiles > #mainProfiles{display:flex;}
/* ── Unified indent rail — every child of a turn lines up on --msg-rail ── */
.msg-row { padding: 12px 0; }
.msg-body { padding-left: var(--msg-rail); padding-top: 8px; max-width: var(--msg-max); }
-.assistant-turn .msg-body { font-family: var(--font-assistant); font-size: 15px; line-height: 1.68; letter-spacing: .003em; }
-.assistant-turn .msg-body code,
-.assistant-turn .msg-body pre,
-.assistant-turn .msg-body table { font-family: 'SF Mono', ui-monospace, monospace; }
-.msg-row[data-role="user"] .msg-body { font-family: var(--font-ui); }
+.msg-body code,
+.msg-body pre,
+.msg-body table { font-family: 'SF Mono', ui-monospace, monospace; }
.msg-body:empty { display: none; }
.assistant-turn { width: 100%; }
.assistant-turn-blocks { display: flex; flex-direction: column; }
diff --git a/tests/test_ui_tool_call_cleanup.py b/tests/test_ui_tool_call_cleanup.py
index 567fc6b8..d79d3f6d 100644
--- a/tests/test_ui_tool_call_cleanup.py
+++ b/tests/test_ui_tool_call_cleanup.py
@@ -211,43 +211,11 @@ class TestToolCardDesignTokens:
for token in expected_tokens:
assert token in css_min, f"Base light palette token missing: {token}"
- def test_calm_console_palette_is_gated_as_custom_theme_not_base(self):
- css_min = re.sub(r"\s+", "", CSS)
- assert ':root.dark[data-theme="calm"]' in css_min, (
- "Coolors calm palette should be gated behind the custom calm theme."
- )
- for token in (
- "--bg:#0A0908",
- "--sidebar:#22333B",
- "--text:#EAE0D5",
- "--muted:#C6AC8F",
- "--accent:#C6AC8F",
- ):
- assert token in css_min, f"Calm custom theme token missing: {token}"
-
- def test_default_skin_preview_stays_upstream_and_calm_theme_preview_is_separate(self):
+ def test_default_skin_preview_stays_upstream(self):
boot_min = re.sub(r"\s+", "", BOOT_JS)
assert "{name:'Default',colors:['#FFD700','#FFBF00','#CD7F32']}" in boot_min, (
"The Default skin swatch should stay aligned with the upstream gold base."
)
- assert "{name:'Calm'," in boot_min and "colors:['#C6AC8F','#EAE0D5','#22333B']" in boot_min, (
- "The Coolors palette should be exposed as a separate custom Calm theme preview."
- )
-
- def test_claude_like_message_typography_splits_user_and_assistant_fonts(self):
- css_min = re.sub(r"\s+", "", CSS)
- assert "--font-ui:" in css_min and "--font-assistant:" in css_min, (
- "Typography should define separate UI/user and assistant font tokens."
- )
- assert ".assistant-turn.msg-body{font-family:var(--font-assistant)" in css_min or ".assistant-turn.msg-body" in css_min.replace(" .", "."), (
- "Assistant prose should use the Claude-like editorial serif stack."
- )
- assert ".msg-row[data-role=\"user\"].msg-body{font-family:var(--font-ui)" in css_min, (
- "User bubbles should keep the sans/UI stack, matching Claude's split typography."
- )
- assert "Georgia" in CSS and "system-ui" in CSS, (
- "Claude-like fallback stacks should include Georgia for assistant prose and system-ui for UI/user text."
- )
def test_tool_card_css_uses_design_tokens_for_chrome(self):
css_min = re.sub(r"\s+", "", CSS)