schemes: Theme File Format Adapters#
Each adapter understands one foreign (or native) theme file format – Sublime .sublime-color-scheme (JSONC), VSCode theme JSON, and the kimi-code 19-token TUI theme JSON – and exposes a uniform surface:
detect(path, text)– extension + content sniffing.usages(text)– every color usage in the file: a JSON-path-ish locator, the canonical role key fromirix.semantics.Semantics(orNone), and the original hex literal.main_background(text)– the scheme’s principal background hex, for mode auto-detection.
Rewriting is textual and structure-preserving: hex literals are replaced in the raw text, never by re-serializing parsed JSON, so comments and formatting in JSONC files survive intact.
Adapter.rewrite applies a global hex mapping; Adapter.rewrite_decisions goes further and rewrites per site (unique keys by anchored match, token-rule lists by channel-sequential cursor), so two usages that share one literal but decide differently – the norm in real flat-JSON themes like Dracula’s, which reuses #6272A4 for twenty distinct roles – each get their own decision.
For Sublime schemes, var(...) references are resolved through the scheme’s own variables: dict for classification; the replacement lands at the variable’s definition site, inheriting the highest-fidelity decision among the variable and its referrers.
Theme File Format Adapters.
Each adapter understands one foreign (or native) theme file format – Sublime
.sublime-color-scheme (JSONC), VSCode theme JSON, and the kimi-code 19-token TUI theme
JSON – and exposes a uniform surface:
detect(path, text)– extension + content sniffing.usages(text)– every color usage in the file: a JSON-path-ish locator, the canonical role key fromirix.semantics.Semantics(orNone), and the original hex literal.main_background(text)– the scheme’s principal background hex, for mode auto-detection.
Rewriting is textual and structure-preserving: hex literals are replaced in the raw
text, never by re-serializing parsed JSON, so comments and formatting in JSONC files
survive intact. Adapter.rewrite applies a global hex mapping; Adapter.rewrite_decisions
goes further and rewrites per site (unique keys by anchored match, token-rule lists by
channel-sequential cursor), so two usages that share one literal but decide differently –
the norm in real flat-JSON themes like Dracula’s, which reuses #6272A4 for twenty
distinct roles – each get their own decision. For Sublime schemes, var(...) references
are resolved through the scheme’s own variables: dict for classification; the
replacement lands at the variable’s definition site, inheriting the highest-fidelity
decision among the variable and its referrers.
- irix.schemes.HEX_RE = re.compile('#(?:[0-9a-fA-F]{8}|[0-9a-fA-F]{6}|[0-9a-fA-F]{3})(?![0-9a-fA-F])')#
Matches hex color literals – #RGB, #RRGGBB, or #RRGGBBAA (case-insensitive), refusing partial matches of longer hex runs.
- irix.schemes.KIMI_TOKENS: dict[str, str] = {'accent': 'ui.accent', 'border': 'ui.border', 'borderFocus': 'ui.border-focus', 'diffAdded': 'diff.added', 'diffAddedStrong': 'diff.added-strong', 'diffGutter': 'diff.gutter', 'diffMeta': 'diff.meta', 'diffRemoved': 'diff.removed', 'diffRemovedStrong': 'diff.removed-strong', 'error': 'ui.error', 'primary': 'ui.primary', 'roleUser': 'role.user', 'shellMode': 'mode.shell', 'success': 'ui.success', 'text': 'ui.text', 'textDim': 'ui.text-dim', 'textMuted': 'ui.text-muted', 'textStrong': 'ui.text-strong', 'warning': 'ui.warning'}#
The kimi-code theme’s fixed 19 tokens -> canonical roles.
- class irix.schemes.Usage(locator: str, role: str | None, hex: str, kind: str = '')#
Bases:
NamedTupleOne color usage in a theme file.
locatoris a JSON-path-ish human-readable address (e.g.rules[12].foreground,colors.editor.background,variables.sand-1);roleis the canonical role key fromSemanticsclassification orNone;hexis the original literal as written;kindis the usage’s rung class –'foreground','surface','border', or''when unknown (e.g. a Sublimevariables:definition) – and guards the semantic decision rung against cross-class mappings (seeirix.themecode.slide_rung).- locator: str#
Alias for field number 0
- role: str | None#
Alias for field number 1
- hex: str#
Alias for field number 2
- kind: str#
Alias for field number 3
- class irix.schemes.Replacement(locator: str, old: str, new: str, verdict: str)#
Bases:
NamedTupleOne decided rewrite: which usage, from which literal, to which new hex.
oldis the literal exactly as written at the usage’s site (case and alpha preserved);newis the normalized#RRGGBBdecision;verdictlets per-site rewriters resolve definition-site conflicts by ladder fidelity.- locator: str#
Alias for field number 0
- old: str#
Alias for field number 1
- new: str#
Alias for field number 2
- verdict: str#
Alias for field number 3
- irix.schemes.normalize_hex(value: str) str#
Normalize a hex literal to uppercase 6-digit
#RRGGBBform (alpha dropped).- Parameters:
value – A
#RGB,#RRGGBB, or#RRGGBBAAliteral, any case.
- irix.schemes.is_hex(value: str) bool#
Return
Trueif value is exactly a hex color literal (3/6/8 digits).
- irix.schemes.kind_of_key(key: str) str#
Infer a color usage’s rung class (
kind) from its workbench/global key name.Keys mentioning
foregroundare foregrounds,backgroundkeys are surfaces, and keys mentioning border/outline are borders; anything else is unknown (''). Substring (not just suffix) matching, so numbered keys likeeditorBracketHighlight.foreground1still classify.- Parameters:
key – A workbench key (
editor.background,sideBar.border, …).
- irix.schemes.strip_jsonc(text: str) str#
Strip
//and/* */comments plus trailing commas from JSONC text.String-aware: comment markers inside string literals (e.g. URLs) are preserved. For analysis only – rewrites never use the stripped form.
- Parameters:
text – Raw JSONC file content.
- Returns:
Text parseable by
json.loads.
- class irix.schemes.Adapter#
Bases:
objectBase class for theme format adapters; holds the shared textual rewrite.
- classmethod detect(path: Path, text: str) bool#
Return
Trueif this adapter claims the file at path with content text.
- classmethod usages(text: str, sem: Semantics) list[Usage]#
Return every color usage in text, classified through sem.
- classmethod main_background(text: str) str | None#
Return the scheme’s principal background hex (for mode auto-detection).
- classmethod rewrite(text: str, mapping: dict[str, str]) str#
Replace hex literals in text per mapping, preserving all other bytes.
- Parameters:
text – The raw file content.
mapping – Normalized old hex (
#RRGGBBupper) -> new#RRGGBBhex. 8-digit literals keep their original alpha suffix; 3-digit literals are expanded to the 6-digit replacement.
- classmethod rewrite_decisions(text: str, replacements: list[Replacement]) str#
Apply per-usage decisions to text, preserving all other bytes.
The base implementation degrades to the global hex mapping (first decision per hex wins); format adapters override it with per-site rewrites so that two usages sharing one literal but deciding differently each get their own value.
- class irix.schemes.SublimeAdapter#
Bases:
AdapterAdapter for Sublime Text
.sublime-color-schemefiles (JSONC).- classmethod detect(path: Path, text: str) bool#
Claim
.sublime-color-schemefiles, or JSONC with arulesarray.
- classmethod usages(text: str, sem: Semantics) list[Usage]#
Yield usages from variables, globals, and rules (var-resolved).
- classmethod main_background(text: str) str | None#
Return the
backgroundglobal (falling back to abackgroundvariable).
- classmethod rewrite_decisions(text: str, replacements: list[Replacement]) str#
Apply decisions per site: variable definitions, literal globals, inline rules.
var(...)-referencing rules and globals carry no literal of their own, so their decisions are inherited by the variable’s definition site: each variable rewrites to the highest-fidelity decision among itself and everything that references it (its own decision wins ties). Rules with inline hex literals are rewritten channel-sequentially within therulesarray, so two rules sharing one literal still get their own values. Comments and formatting are untouched.
- class irix.schemes.VscodeAdapter#
Bases:
AdapterAdapter for VSCode theme JSON (
colors+tokenColors+semanticTokenColors).- classmethod detect(path: Path, text: str) bool#
Claim JSON files with a
tokenColorsarray or aneditor.backgroundcolor.
- classmethod usages(text: str, sem: Semantics) list[Usage]#
Yield usages from
colors,tokenColors, andsemanticTokenColors.
- classmethod main_background(text: str) str | None#
Return
colors['editor.background'], else the themetypehint is unused.
- classmethod rewrite_decisions(text: str, replacements: list[Replacement]) str#
Apply decisions per site: unique
colorskeys, then token rules in order.Flat workbench keys are unique, so each rewrites by anchored key match.
tokenColorsrules repeat literals across rules, so foreground/background channels rewrite sequentially (rule i’s decision lands on rule i) within thetokenColorssection only, never bleeding intosemanticTokenColors.
- class irix.schemes.KimiAdapter#
Bases:
AdapterAdapter for the kimi-code 19-token flat
colorstheme JSON.- classmethod detect(path: Path, text: str) bool#
Claim JSON files whose
colorsdict carries the kimi token signature.
- classmethod usages(text: str, sem: Semantics) list[Usage]#
Yield one usage per kimi token (roles come from the fixed
KIMI_TOKENSmap).
- classmethod main_background(text: str) str | None#
Return
None– kimi themes carry no background; mode detection uses text.
- classmethod text_probe(text: str) str | None#
Return the
texttoken hex, the best luminance probe for a kimi theme.
- classmethod rewrite_decisions(text: str, replacements: list[Replacement]) str#
Apply decisions per token key (the 19 kimi tokens are unique).