diff --git a/CHANGELOG.md b/CHANGELOG.md index 88fea4f..123f147 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to the equisdots theme-sync engine are documented here. Dates use YYYY-MM-DD. +## [2026-10-25] + +### Fixed +- VS Code target: process an installed Code / Code - Insiders variant even when + its `User/settings.json` does not exist yet (fresh profile). The file is + created with the generated theme instead of the target silently doing + nothing. This was why VSCode stayed unthemed on a fresh niri profile while + working where the file already existed. +- `scripts/check.sh`: temp-home fixture that asserts the missing settings file + is created and contains the managed theme block. + ## [2026-09-30] ### Added diff --git a/README.md b/README.md index 71eee3f..d1d10f3 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Defaults target the equisdots desktop layout: | `kitty` | `~/.config/kitty/themes/.conf` + include and active border in `kitty.conf` | | `starship` | per-palette themes + fixed `~/.config/starship.toml` + `STARSHIP_CONFIG` in `.zshrc`/`.bashrc` | | `xtop` | `~/.config/xtop/themes/.jsonc` + active theme (`xtop --ct`) | -| `vscode` | `workbench.colorTheme` / `workbench.iconTheme` for palettes bundled in the xscriptor-themes extension; any other palette (community/, user-created) gets an intelligent live tint via generated `colorCustomizations` + token/semantic colors, in Code / Code - Insiders | +| `vscode` | `workbench.colorTheme` / `workbench.iconTheme` for palettes bundled in the xscriptor-themes extension; any other palette (community/, user-created) gets an intelligent live tint via generated `colorCustomizations` + token/semantic colors, in Code / Code - Insiders. Creates `User/settings.json` when the editor is installed but the file is missing (fresh profile). | | `nvim` | `lua/themes/palettes.lua` + active-theme bootstrap | | `browsers` | Brave/Beta prefs (`color_scheme2`, accent) + Firefox `user.js` | | `opencode` | `~/.config/opencode/themes/.json` + active theme in `tui.json` | diff --git a/scripts/check.sh b/scripts/check.sh index a051d1a..b9e58b3 100755 --- a/scripts/check.sh +++ b/scripts/check.sh @@ -74,6 +74,39 @@ PY echo "vscode mapping FAIL" fail=1 fi + + # vscode fresh-profile: an installed variant with no settings.json must be + # created and themed instead of silently skipped. + tmp_home="$(mktemp -d)" + mkdir -p "$tmp_home/.config/Code - Insiders/User" + if ! PYTHONPATH="$PWD" python3 - "$PALETTES" "$tmp_home" <<'PY' +import sys +from pathlib import Path +from themesync.core import Env, load_palettes +from themesync.targets import vscode + +home = Path(sys.argv[2]) +pals = load_palettes(Path(sys.argv[1])) +pal = pals[0] +env = Env(home=home, palettes_dir=Path(sys.argv[1]), + settings_path=home / ".config/hypr/settings.json", + slug=pal.get("slug"), palettes=pals, palette=pal) +vscode.apply(env) +path = home / ".config/Code - Insiders/User/settings.json" +if not path.is_file(): + print("vscode create: settings.json missing") + sys.exit(1) +text = path.read_text() +if '"workbench.colorTheme"' not in text or "equisdots theme-sync" not in text: + print("vscode create: theme block missing") + sys.exit(1) +print("vscode create: OK") +PY + then + echo "vscode create FAIL" + fail=1 + fi + rm -rf "$tmp_home" else echo "note: no palette source found, CLI smoke test skipped" >&2 fi diff --git a/themesync/targets/vscode.py b/themesync/targets/vscode.py index 6efe122..ab23cf1 100644 --- a/themesync/targets/vscode.py +++ b/themesync/targets/vscode.py @@ -57,10 +57,22 @@ def available(env) -> bool: - # Always: if no variant is installed, apply() does nothing. + # Always: apply() decides per variant whether the editor is installed. return True +def _variant_present(env, rel: str, vendor: str) -> bool: + """Is this editor variant installed (settings file, user dir or binary)?""" + path = Path(env.home) / rel + if path.is_file() or path.parent.is_dir(): + return True + ext = Path(env.home) / EXT_DIRS.get(vendor, "") + if ext.is_dir(): + return True + binary = "code-insiders" if vendor == "Code - Insiders" else "code" + return env.binary(binary) is not None + + # ── native theme discovery (dynamic; no palette list) ───────────────────────── def _native(env, slug: str, vendor: str): @@ -172,11 +184,12 @@ def apply(env) -> list: out = [] for rel in VARIANTS: path = env.home / rel - if not path.is_file(): - continue vendor = Path(rel).parent.parent.name # "Code" / "Code - Insiders" - original = path.read_text(encoding="utf-8", errors="replace") - text = _strip_block(original) + if not _variant_present(env, rel, vendor): + continue + created = not path.is_file() + original = "" if created else path.read_text(encoding="utf-8", errors="replace") + text = "{\n}\n" if not original.strip() else _strip_block(original) display = _native(env, env.slug, vendor) icon = _icon(env, env.slug, vendor) @@ -214,7 +227,8 @@ def apply(env) -> list: "; skipped user keys: %s" % ", ".join(skipped) if skipped else "", ) - if text != original: + if created or text != original: env.write(path, text) - out.append("vscode (%s) → %s" % (vendor, note)) + suffix = " [created]" if created else "" + out.append("vscode (%s) → %s%s" % (vendor, note, suffix)) return out