diff --git a/docs/features.md b/docs/features.md
index 05330455..4e42bfa0 100644
--- a/docs/features.md
+++ b/docs/features.md
@@ -19,6 +19,7 @@
- **Command palette** ⌘ P · scrollback search ⌘ F
- **⌘-click links** · desktop notifications · copy on select (opt-in, Settings → Terminal → Clipboard)
- **Eight themes, plus your own** — YAML seed themes with solid, gradient, or image backgrounds; iTerm2 `.itermcolors` import; in-app color editor with a background-image picker
+- **Sync with system** — Settings → Appearance; pick separate light and dark themes and tty7 follows the OS appearance live (`theme_follow_system`, `theme_preset_light` / `theme_preset_dark` in `config.json`)
- **Window opacity & blur** — Settings → Appearance → Window; applies to every theme, *Follow theme* returns to the theme's own `opacity` / `blur`
- **CJK / IME input**
diff --git a/docs/features.zh-CN.md b/docs/features.zh-CN.md
index bba83eae..0a7e2222 100644
--- a/docs/features.zh-CN.md
+++ b/docs/features.zh-CN.md
@@ -19,6 +19,7 @@
- **命令面板** ⌘ P · 回滚搜索 ⌘ F
- **⌘ 点击打开链接** · 桌面通知 · 划选即复制(可选,设置 → 终端 → 剪贴板)
- **8 套主题,也能自定义** — YAML 种子主题,背景支持纯色、渐变或图片;可导入 iTerm2 `.itermcolors`;应用内颜色编辑器带背景图选择
+- **跟随系统外观** — 设置 → Appearance;分别选好浅色和深色主题,tty7 随系统深浅模式实时切换(`config.json` 中的 `theme_follow_system`、`theme_preset_light` / `theme_preset_dark`)
- **窗口透明与模糊** — 设置 → Appearance → Window;对所有主题生效,*Follow theme* 恢复主题自带的 `opacity` / `blur`
- **CJK / 输入法输入**
diff --git a/src/core/config.rs b/src/core/config.rs
index 03d1b5f9..68cfc5ac 100644
--- a/src/core/config.rs
+++ b/src/core/config.rs
@@ -851,6 +851,27 @@ mod tests {
assert_eq!(back.ssh_profile_frecency.get(&id).unwrap().count, 4);
}
+ #[test]
+ fn theme_follow_system_defaults_and_round_trips() {
+ // Old configs (no follow-system keys) must land on off + the built-in
+ // light/dark pair, so nothing changes until the user opts in.
+ let cfg: Config = serde_json::from_str(r#"{"theme_preset":"dracula"}"#).unwrap();
+ assert!(!cfg.theme_follow_system);
+ assert_eq!(cfg.theme_preset_light, "light");
+ assert_eq!(cfg.theme_preset_dark, "dark");
+ assert_eq!(cfg.theme_preset, "dracula");
+
+ let mut cfg = Config::default();
+ cfg.theme_follow_system = true;
+ cfg.theme_preset_light = "one_light".to_string();
+ cfg.theme_preset_dark = "dracula".to_string();
+ let json = serde_json::to_string(&cfg).unwrap();
+ let back: Config = serde_json::from_str(&json).unwrap();
+ assert!(back.theme_follow_system);
+ assert_eq!(back.theme_preset_light, "one_light");
+ assert_eq!(back.theme_preset_dark, "dracula");
+ }
+
#[test]
fn font_features_are_optional_and_parse_as_gpui_features() {
let cfg: Config =
diff --git a/src/ui/app.rs b/src/ui/app.rs
index 88b8ae9b..a4fb4435 100644
--- a/src/ui/app.rs
+++ b/src/ui/app.rs
@@ -1137,10 +1137,11 @@ impl Tty7App {
}
/// Turn "sync with system appearance" on/off (the Appearance switch).
- /// Flipping it never visibly changes the theme by itself: turning it on
- /// seeds the slot matching the manual theme's own brightness with that
- /// theme, and turning it off adopts whatever is on screen as the manual
- /// choice.
+ /// Turning it off never visibly changes the theme: whatever is on screen
+ /// is adopted as the manual choice. Turning it on seeds the slot matching
+ /// the manual theme's own brightness with that theme — so the look only
+ /// changes when the OS is currently in the *other* mode, where switching
+ /// to that mode's slot is exactly what the feature promises.
pub(crate) fn set_theme_follow_system(
&mut self,
on: bool,
diff --git a/src/ui/settings.rs b/src/ui/settings.rs
index 9454da29..f88f1640 100644
--- a/src/ui/settings.rs
+++ b/src/ui/settings.rs
@@ -360,8 +360,8 @@ pub(crate) struct SettingsState {
/// Global window-opacity slider (Appearance's Window section). Shows the
/// effective value; dragging sets the config override.
pub(crate) window_opacity_slider: Entity,
- /// The color editor for the active editable theme, or `None` when the active
- /// theme is read-only (a built-in / import) or the system is being followed.
+ /// The color editor for the effective (on-screen) theme, or `None` when
+ /// that theme is read-only (a built-in / import).
pub(crate) theme_editor: Option,
/// Whether the theme picker panel is open beside the content pane
/// (Appearance section only). Toggled from the theme card(s).