From a090138d84f3e93b2d57b38159cf0b3b7330fe11 Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Sat, 13 Jun 2026 15:03:43 -0500 Subject: Update theme studio color columns and defaults --- scripts/theme-studio/generate.py | 209 ++++++++++++++++++++++++++++++--------- 1 file changed, 162 insertions(+), 47 deletions(-) (limited to 'scripts/theme-studio/generate.py') diff --git a/scripts/theme-studio/generate.py b/scripts/theme-studio/generate.py index b6e2fc73..e98d0bf3 100644 --- a/scripts/theme-studio/generate.py +++ b/scripts/theme-studio/generate.py @@ -1,4 +1,4 @@ -import json, os +import json, os, re HERE=os.path.dirname(os.path.abspath(__file__)) def strip_exports(src): @@ -38,10 +38,102 @@ src=open(os.path.join(HERE,'samples.py')).read() exec(src[:src.index('cols=')], ns) SAMPLES={"Elisp":ns['ELS'],"Go":ns['GOS'],"Python":ns['PYS'],"TypeScript":ns['TSS'],"Java":ns['JAS'],"C":ns['CS'],"C++":ns['CPS'],"Shell":ns['SHS']} COLS=ns['COLS'] -MAP={k:v[0] for k,v in COLS.items()}; BOLD={k:v[1] for k,v in COLS.items()}; MAP['str']='#5d9b86'; MAP['bg']='#000000' -PALETTE=[["#67809c","blue"],["#e8bd30","gold"],["#9b5fd0","regal"],["#2ba178","emerald"],["#5d9b86","sage"], - ["#cb6b4d","terracotta"],["#be9e74","tan"],["#ffffff","white"],["#a9b2bb","silver"],["#838d97","steel"], - ["#5e6770","pewter"],["#2f343a","gunmetal"],["#264364","navy"],["#000000","ground"],["#1a1714","bg-dim"]] +DEFAULT_FACES_PATH=os.path.join(HERE,'emacs-default-faces.json') +DEFAULT_FACES=json.load(open(DEFAULT_FACES_PATH)) if os.path.exists(DEFAULT_FACES_PATH) else None +DEFAULT_COLOR_HEX={} +if DEFAULT_FACES: + for _data in DEFAULT_FACES.get('faces',{}).values(): + for _block in ('chosenGuiLight','effectiveGuiLight'): + _d=_data.get(_block,{}) or {} + for _attr in ('foreground','background','distantForeground'): + if _d.get(_attr) and _d.get(_attr+'Hex'): + DEFAULT_COLOR_HEX[str(_d[_attr]).lower().replace(' ','')]=_d[_attr+'Hex'] +MAP={k:'' for k in COLS}; MAP['bg']='#000000'; MAP['p']='#ffffff' +BOLD={k:False for k in COLS} +ITALIC_MAP={k:False for k in COLS} +def column_id(name): + return re.sub(r'[+-]\d+$', '', name or 'color') + +def normalize_palette(palette): + return [[p[0], p[1] if len(p) > 1 else 'color', p[2] if len(p) > 2 else column_id(p[1] if len(p) > 1 else 'color')] + for p in palette] + +def default_face(face, effective=True): + if not DEFAULT_FACES: return {} + data=DEFAULT_FACES.get('faces',{}).get(face,{}) + return data.get('effectiveGuiLight' if effective else 'chosenGuiLight',{}) or {} + +def default_color(face, attr='foreground', effective=True): + d=default_face(face,effective) + return d.get(attr+'Hex') or d.get(attr) + +def emacs_box_to_theme(box): + if not box: return None + if isinstance(box,dict): return box + if not isinstance(box,list): return None + vals={} + i=0 + while i+1 seeds the tool's starting # palette / assignments / bold / italic / UI from a theme.json (path relative to # this dir), instead of the hardcoded defaults above. Unset leaves them unchanged. # Placed after every default it overrides (notably UIMAP) so the merge has targets. # Mirrors what the in-page Import does, so reseed and import agree. -LOCKS=[]; ITALIC=[] -# sterling is the default theme; THEME_STUDIO_SEED=.json overrides to view another. -_seed=os.environ.get('THEME_STUDIO_SEED') or 'sterling.json' +LOCKS=[]; ITALIC=[k for k,v in ITALIC_MAP.items() if v] +# THEME_STUDIO_SEED=.json opens an existing theme as the starting point. +# Unset starts empty: only bg/fg are in the palette. +_seed=os.environ.get('THEME_STUDIO_SEED') _d={} if _seed: _d=json.load(open(os.path.join(HERE,_seed))) @@ -90,14 +176,14 @@ if _seed: if _d.get('ui'): for _k,_v in _d['ui'].items(): UIMAP[_k]=_v if 'locks' in _d: LOCKS=_d['locks'] -# These faces carry a fixed style in Emacs's built-in definitions (verified with -# emacs -Q), independent of any theme: link / lazy-highlight / show-paren-match -# are underlined; error / warning / success are bold. Seed the defaults to match. -UIMAP["link"]["underline"]=True -for _f in ("lazy-highlight","show-paren-match"): UIMAP[_f]["underline"]=True -for _f in ("error","warning","success"): UIMAP[_f]["bold"]=True -# The mode line carries a 3D released-button box by default in Emacs. -for _f in ("mode-line","mode-line-inactive"): UIMAP[_f]["box"]={"style":"released","width":1,"color":None} +PALETTE=normalize_palette(PALETTE) +if not DEFAULT_FACES: + # These faces carry a fixed style in Emacs's built-in definitions. Fallback + # only; normal generation uses emacs-default-faces.json above. + UIMAP["link"]["underline"]=True + for _f in ("lazy-highlight","show-paren-match"): UIMAP[_f]["underline"]=True + for _f in ("error","warning","success"): UIMAP[_f]["bold"]=True + for _f in ("mode-line","mode-line-inactive"): UIMAP[_f]["box"]={"style":"released","width":1,"color":None} # Tier-3 package faces (Phase 2): complete own-defface sets for org/magit/elfeed, # built from face-name lists + a curated seed-color map. Prominent faces are # seeded; the long tail seeds to the default foreground for the user to tune. @@ -141,7 +227,11 @@ MAGIT_FACES=("magit-section-heading magit-section-secondary-heading magit-sectio "magit-reflog-merge magit-reflog-checkout magit-reflog-reset magit-reflog-rebase " "magit-reflog-cherry-pick magit-reflog-remote magit-reflog-other magit-sequence-pick " "magit-sequence-stop magit-sequence-part magit-sequence-head magit-sequence-drop magit-sequence-done " - "magit-sequence-onto magit-sequence-exec magit-left-margin").split() + "magit-sequence-onto magit-sequence-exec magit-left-margin " + "git-commit-comment-action git-commit-comment-branch-local git-commit-comment-branch-remote " + "git-commit-comment-detached git-commit-comment-file git-commit-comment-heading git-commit-keyword " + "git-commit-nonempty-second-line git-commit-overlong-summary git-commit-summary " + "git-commit-trailer-token git-commit-trailer-value").split() ELFEED_FACES=("elfeed-search-date-face elfeed-search-title-face elfeed-search-unread-title-face " "elfeed-search-feed-face elfeed-search-tag-face elfeed-search-unread-count-face " "elfeed-search-filter-face elfeed-search-last-update-face elfeed-log-date-face " @@ -426,22 +516,59 @@ if os.path.exists(_inv_path): APPS[_pkg]={"label":_pkg,"preview":"generic","faces":[ [f,(f[len(_pkg)+1:] if f.startswith(_pkg+"-") else f).replace("-face","").replace("-"," "),{}] for f in _INV[_pkg]]} -# Apply the seed theme's package overrides (sterling by default): each full per-face -# spec (color + structure) replaces the hardcoded face seed before the page renders. +if DEFAULT_FACES: + for _app in APPS.values(): + for _row in _app["faces"]: + _row[2]=face_seed(_row[0],False) +# Apply seed theme package overrides when THEME_STUDIO_SEED is set: each full +# per-face spec (color + structure) replaces the hardcoded face seed before render. if _seed and _d.get('packages'): for _app,_pkfaces in _d['packages'].items(): if _app in APPS: for _row in APPS[_app]['faces']: if _row[0] in _pkfaces: _row[2]=_pkfaces[_row[0]] + +def add_palette_color(value, label=None): + if not value: return + if any((p[0] or '').lower()==str(value).lower() for p in PALETTE): return + name=label or color_label(value,'color-'+str(len(PALETTE))) + base=name + n=2 + used={p[1].lower() for p in PALETTE} + while name.lower() in used: + name=base+'-'+str(n); n+=1 + PALETTE.append([value,name,column_id(name)]) + +if DEFAULT_FACES: + for _k,_v in MAP.items(): + add_palette_color(_v, 'bg' if _k=='bg' else 'fg' if _k=='p' else None) + for _face,_spec in UIMAP.items(): + add_palette_color(_spec.get('fg')) + add_palette_color(_spec.get('bg')) + for _app in APPS.values(): + for _face,_label,_spec in _app['faces']: + add_palette_color(_spec.get('fg')) + add_palette_color(_spec.get('bg')) + +PALETTE=normalize_palette(PALETTE) HTML = """theme-studio -

Untitled: theme

-
+
+

Untitled: theme

+
+
+ + + + + +
+ +
+

palette

-
-
@@ -468,21 +595,9 @@ STYLES_CSS
+
+
-
-

export, import, and save

-
- -
-
- - - - -
- -
-

code/color assignments

-- cgit v1.2.3