1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
|
"""Theme-studio package/app face inventory assembly helpers."""
from __future__ import annotations
import json
import os
from collections.abc import Sequence
from typing import Any
from face_data import BESPOKE_APP_SPECS, PINNED_PACKAGE_FACES
# Keys of the bespoke apps (single-sourced in face_data), excluded from the
# generic-inventory path so they aren't also emitted as plain inventory apps.
# "org" is an explicit alias of the "org-mode" bespoke app, so an inventory
# package literally named "org" never gets a duplicate generic entry.
BESPOKE_APPS = {spec[0] for spec in BESPOKE_APP_SPECS} | {"org"}
# Inventory apps (not in BESPOKE_APPS) default to the generic preview. Apps with
# a dedicated PACKAGE_PREVIEWS renderer in app.js are keyed by name here.
PREVIEW_KEYS = {
"markdown-mode": "markdown",
"company": "company",
"company-box": "companybox",
"transient": "transient",
"magit-section": "magitsection",
"rainbow-delimiters": "rainbowdelims",
"web-mode": "webmode",
}
# Custom display labels for inventory apps whose package name is an acronym
# worth spelling out (matches the bespoke EAT / LSP / SHR style: full name with
# the acronym in parentheses).
PACKAGE_LABEL_OVERRIDES = {
"emms": "emacs multimedia system (emms)",
}
def face_label(face: str, prefix: str) -> str:
label = face[len(prefix) :] if face.startswith(prefix) else face
return label.replace("-face", "").replace("-", " ")
def face_rows(names: Sequence[str], prefix: str, seed: dict[str, dict[str, Any]]) -> list[list[Any]]:
return [[face, face_label(face, prefix), seed.get(face, {})] for face in names]
def add_inventory_apps(apps: dict[str, Any], inventory_path: str) -> dict[str, Any]:
"""Add generic editable apps for installed packages not covered by bespoke previews.
PINNED_PACKAGE_FACES (the ecosystem coverage policy) is the curated record
of packages retired from this config: a pinned package is always marked
not loaded, and it survives inventory regeneration -- an uninstall must
never drop an app from the studio. Its face list stays fresh from the live
inventory when present (a still-installed dependency can grow faces); the
pin is the fallback when the inventory no longer carries it.
"""
inventory: dict[str, Any] = {}
if os.path.exists(inventory_path):
with open(inventory_path) as src:
inventory = json.load(src)
for pkg in sorted(inventory):
if pkg in BESPOKE_APPS or pkg in apps:
continue
apps[pkg] = {
"label": PACKAGE_LABEL_OVERRIDES.get(pkg, pkg),
"preview": PREVIEW_KEYS.get(pkg, "generic"),
"faces": [[face, face_label(face, pkg + "-"), {}] for face in inventory[pkg]],
}
for pkg in sorted(PINNED_PACKAGE_FACES):
if pkg in BESPOKE_APPS:
continue
faces = inventory.get(pkg, PINNED_PACKAGE_FACES[pkg])
apps[pkg] = {
"label": PACKAGE_LABEL_OVERRIDES.get(pkg, pkg) + " · not loaded",
"preview": PREVIEW_KEYS.get(pkg, "generic"),
"unloaded": True,
"hover": ("Retired from this config; its faces are pinned so ecosystem "
"themes still cover it. The live preview is the only place its "
"theming can be seen."),
"faces": [[face, face_label(face, pkg + "-"), {}] for face in faces],
}
return apps
def add_nerd_icons_app(apps: dict[str, Any], inventory_path: str, legend: Any,
gallery: Any = None) -> dict[str, Any]:
"""Register nerd-icons as a bespoke legend app from its inventory faces.
The 34 nerd-icons color faces stay editable rows; LEGEND (the validated rows
from generate.load_nerd_icons_legend) rides the app so the bespoke previews.js
renderer can draw each filetype glyph in its mapped face color. GALLERY (the
full colored catalog grouped by face, from generate.load_nerd_icons_gallery)
rides alongside when present so the same renderer can draw the gallery section;
a falsy GALLERY simply omits it (legend-only). A no-op when LEGEND is falsy or
the inventory lacks nerd-icons -- the caller guards on a valid legend, and
add_inventory_apps then creates the generic fallback app. Must run before
add_inventory_apps so the generic path skips nerd-icons.
"""
if not legend or not os.path.exists(inventory_path):
return apps
with open(inventory_path) as src:
faces = json.load(src).get("nerd-icons")
if not faces:
return apps
app = {
"label": "nerd-icons",
"preview": "nerdicons",
"faces": [[face, face_label(face, "nerd-icons-"), {}] for face in faces],
"legend": legend,
}
if gallery:
app["gallery"] = gallery
apps["nerd-icons"] = app
return apps
def apply_default_face_seeds(apps: dict[str, Any], defaults: Any) -> None:
if not defaults.available:
return
for app in apps.values():
for row in app["faces"]:
row[2] = defaults.seed(row[0], False)
def apply_package_overrides(apps: dict[str, Any], packages: dict[str, Any] | None) -> None:
if not packages:
return
for app, package_faces in packages.items():
if app not in apps:
continue
for row in apps[app]["faces"]:
if row[0] in package_faces:
row[2] = package_faces[row[0]]
|