free-coding-models 0.3.17 → 0.3.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/README.md +11 -1
- package/package.json +1 -1
- package/src/app.js +106 -3
- package/src/command-palette.js +170 -0
- package/src/config.js +3 -3
- package/src/key-handler.js +492 -142
- package/src/openclaw.js +39 -5
- package/src/opencode.js +2 -1
- package/src/overlays.js +426 -208
- package/src/render-helpers.js +1 -1
- package/src/render-table.js +141 -177
- package/src/theme.js +294 -43
- package/src/tier-colors.js +15 -17
- package/src/tool-bootstrap.js +310 -0
- package/src/tool-launchers.js +12 -7
- package/src/ui-config.js +24 -31
package/src/theme.js
CHANGED
|
@@ -1,67 +1,318 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file theme.js
|
|
3
|
-
* @description
|
|
3
|
+
* @description Semantic light/dark palette, auto theme detection, and shared TUI colour helpers.
|
|
4
|
+
*
|
|
5
|
+
* @functions
|
|
6
|
+
* → `detectActiveTheme` — resolve the live theme from user preference or terminal/OS signals
|
|
7
|
+
* → `getTheme` — return the currently active resolved theme (`dark` or `light`)
|
|
8
|
+
* → `cycleThemeSetting` — rotate persisted theme preference (`auto` → `dark` → `light`)
|
|
9
|
+
* → `getThemeStatusLabel` — format the settings/help label for the current theme mode
|
|
10
|
+
* → `getProviderRgb` — return the provider accent colour for the active theme
|
|
11
|
+
* → `getTierRgb` — return the tier accent colour for the active theme
|
|
12
|
+
*
|
|
13
|
+
* @exports { THEME_OPTIONS, detectActiveTheme, getTheme, cycleThemeSetting, getThemeStatusLabel, getReadableTextRgb, getProviderRgb, getTierRgb, themeColors }
|
|
14
|
+
*
|
|
15
|
+
* @see src/render-table.js
|
|
16
|
+
* @see src/overlays.js
|
|
17
|
+
* @see src/tier-colors.js
|
|
4
18
|
*/
|
|
5
19
|
|
|
6
20
|
import chalk from 'chalk'
|
|
7
21
|
import { execSync } from 'child_process'
|
|
8
22
|
|
|
23
|
+
export const THEME_OPTIONS = ['auto', 'dark', 'light']
|
|
24
|
+
|
|
9
25
|
let activeTheme = 'dark'
|
|
10
26
|
|
|
11
|
-
|
|
27
|
+
const PALETTES = {
|
|
28
|
+
dark: {
|
|
29
|
+
text: [234, 239, 248],
|
|
30
|
+
textStrong: [255, 255, 255],
|
|
31
|
+
muted: [149, 160, 182],
|
|
32
|
+
soft: [178, 190, 210],
|
|
33
|
+
accent: [110, 214, 255],
|
|
34
|
+
accentStrong: [72, 198, 255],
|
|
35
|
+
info: [129, 210, 255],
|
|
36
|
+
success: [112, 231, 181],
|
|
37
|
+
successStrong: [139, 239, 176],
|
|
38
|
+
warning: [255, 208, 102],
|
|
39
|
+
warningStrong: [255, 221, 140],
|
|
40
|
+
danger: [255, 129, 129],
|
|
41
|
+
dangerStrong: [255, 166, 166],
|
|
42
|
+
hotkey: [255, 214, 102],
|
|
43
|
+
link: [149, 205, 255],
|
|
44
|
+
border: [111, 125, 149],
|
|
45
|
+
footerLove: [255, 168, 209],
|
|
46
|
+
footerCoffee: [255, 209, 134],
|
|
47
|
+
footerDiscord: [207, 179, 255],
|
|
48
|
+
overlayFg: [234, 239, 248],
|
|
49
|
+
overlayBg: {
|
|
50
|
+
settings: [7, 13, 24],
|
|
51
|
+
help: [9, 18, 31],
|
|
52
|
+
recommend: [8, 21, 20],
|
|
53
|
+
feedback: [31, 13, 20],
|
|
54
|
+
changelog: [12, 24, 44],
|
|
55
|
+
commandPalette: [14, 20, 36],
|
|
56
|
+
},
|
|
57
|
+
cursor: {
|
|
58
|
+
defaultBg: [39, 55, 90],
|
|
59
|
+
defaultFg: [255, 255, 255],
|
|
60
|
+
installBg: [22, 60, 69],
|
|
61
|
+
installFg: [255, 255, 255],
|
|
62
|
+
settingsBg: [26, 54, 34],
|
|
63
|
+
settingsFg: [255, 255, 255],
|
|
64
|
+
legacyBg: [67, 31, 69],
|
|
65
|
+
legacyFg: [255, 255, 255],
|
|
66
|
+
modelBg: [62, 73, 115],
|
|
67
|
+
modelFg: [255, 255, 255],
|
|
68
|
+
recommendedBg: [20, 51, 33],
|
|
69
|
+
recommendedFg: [234, 239, 248],
|
|
70
|
+
favoriteBg: [76, 55, 17],
|
|
71
|
+
favoriteFg: [255, 244, 220],
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
light: {
|
|
75
|
+
text: [28, 36, 51],
|
|
76
|
+
textStrong: [8, 12, 20],
|
|
77
|
+
muted: [95, 109, 129],
|
|
78
|
+
soft: [76, 89, 109],
|
|
79
|
+
accent: [0, 120, 186],
|
|
80
|
+
accentStrong: [0, 99, 163],
|
|
81
|
+
info: [0, 109, 168],
|
|
82
|
+
success: [0, 118, 68],
|
|
83
|
+
successStrong: [0, 96, 56],
|
|
84
|
+
warning: [146, 90, 0],
|
|
85
|
+
warningStrong: [171, 102, 0],
|
|
86
|
+
danger: [177, 53, 53],
|
|
87
|
+
dangerStrong: [147, 35, 48],
|
|
88
|
+
hotkey: [171, 98, 0],
|
|
89
|
+
link: [0, 94, 170],
|
|
90
|
+
border: [151, 166, 188],
|
|
91
|
+
footerLove: [176, 79, 128],
|
|
92
|
+
footerCoffee: [170, 102, 0],
|
|
93
|
+
footerDiscord: [104, 83, 190],
|
|
94
|
+
overlayFg: [28, 36, 51],
|
|
95
|
+
overlayBg: {
|
|
96
|
+
settings: [248, 250, 255],
|
|
97
|
+
help: [246, 250, 255],
|
|
98
|
+
recommend: [246, 252, 248],
|
|
99
|
+
feedback: [255, 247, 248],
|
|
100
|
+
changelog: [244, 248, 255],
|
|
101
|
+
commandPalette: [242, 247, 255],
|
|
102
|
+
},
|
|
103
|
+
cursor: {
|
|
104
|
+
defaultBg: [217, 231, 255],
|
|
105
|
+
defaultFg: [9, 18, 35],
|
|
106
|
+
installBg: [218, 242, 236],
|
|
107
|
+
installFg: [12, 33, 26],
|
|
108
|
+
settingsBg: [225, 244, 229],
|
|
109
|
+
settingsFg: [14, 43, 27],
|
|
110
|
+
legacyBg: [248, 228, 244],
|
|
111
|
+
legacyFg: [76, 28, 73],
|
|
112
|
+
modelBg: [209, 223, 255],
|
|
113
|
+
modelFg: [9, 18, 35],
|
|
114
|
+
recommendedBg: [221, 245, 229],
|
|
115
|
+
recommendedFg: [17, 47, 28],
|
|
116
|
+
favoriteBg: [255, 241, 208],
|
|
117
|
+
favoriteFg: [79, 53, 0],
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const PROVIDER_PALETTES = {
|
|
123
|
+
dark: {
|
|
124
|
+
nvidia: [132, 235, 168],
|
|
125
|
+
groq: [255, 191, 144],
|
|
126
|
+
cerebras: [153, 215, 255],
|
|
127
|
+
sambanova: [255, 215, 142],
|
|
128
|
+
openrouter: [228, 191, 239],
|
|
129
|
+
huggingface: [255, 235, 122],
|
|
130
|
+
replicate: [166, 212, 255],
|
|
131
|
+
deepinfra: [146, 222, 213],
|
|
132
|
+
fireworks: [255, 184, 194],
|
|
133
|
+
codestral: [245, 175, 212],
|
|
134
|
+
hyperbolic: [255, 160, 127],
|
|
135
|
+
scaleway: [115, 209, 255],
|
|
136
|
+
googleai: [166, 210, 255],
|
|
137
|
+
siliconflow: [145, 232, 243],
|
|
138
|
+
together: [255, 232, 98],
|
|
139
|
+
cloudflare: [255, 191, 118],
|
|
140
|
+
perplexity: [243, 157, 195],
|
|
141
|
+
qwen: [255, 213, 128],
|
|
142
|
+
zai: [150, 208, 255],
|
|
143
|
+
iflow: [211, 229, 101],
|
|
144
|
+
},
|
|
145
|
+
light: {
|
|
146
|
+
nvidia: [0, 126, 73],
|
|
147
|
+
groq: [171, 86, 22],
|
|
148
|
+
cerebras: [0, 102, 177],
|
|
149
|
+
sambanova: [165, 94, 0],
|
|
150
|
+
openrouter: [122, 65, 156],
|
|
151
|
+
huggingface: [135, 104, 0],
|
|
152
|
+
replicate: [0, 94, 163],
|
|
153
|
+
deepinfra: [0, 122, 117],
|
|
154
|
+
fireworks: [183, 55, 72],
|
|
155
|
+
codestral: [157, 61, 110],
|
|
156
|
+
hyperbolic: [178, 68, 27],
|
|
157
|
+
scaleway: [0, 113, 189],
|
|
158
|
+
googleai: [0, 111, 168],
|
|
159
|
+
siliconflow: [0, 115, 138],
|
|
160
|
+
together: [122, 101, 0],
|
|
161
|
+
cloudflare: [176, 92, 0],
|
|
162
|
+
perplexity: [171, 62, 121],
|
|
163
|
+
qwen: [132, 89, 0],
|
|
164
|
+
zai: [0, 104, 171],
|
|
165
|
+
iflow: [107, 130, 0],
|
|
166
|
+
},
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const TIER_PALETTES = {
|
|
170
|
+
dark: {
|
|
171
|
+
'S+': [111, 255, 164],
|
|
172
|
+
'S': [147, 241, 101],
|
|
173
|
+
'A+': [201, 233, 104],
|
|
174
|
+
'A': [255, 211, 101],
|
|
175
|
+
'A-': [255, 178, 100],
|
|
176
|
+
'B+': [255, 145, 112],
|
|
177
|
+
'B': [255, 113, 113],
|
|
178
|
+
'C': [255, 139, 164],
|
|
179
|
+
},
|
|
180
|
+
light: {
|
|
181
|
+
'S+': [0, 122, 58],
|
|
182
|
+
'S': [54, 122, 0],
|
|
183
|
+
'A+': [95, 113, 0],
|
|
184
|
+
'A': [128, 92, 0],
|
|
185
|
+
'A-': [156, 80, 0],
|
|
186
|
+
'B+': [171, 69, 0],
|
|
187
|
+
'B': [168, 44, 44],
|
|
188
|
+
'C': [123, 35, 75],
|
|
189
|
+
},
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function currentPalette() {
|
|
193
|
+
return PALETTES[activeTheme] ?? PALETTES.dark
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function themeLabel(theme) {
|
|
197
|
+
return theme.charAt(0).toUpperCase() + theme.slice(1)
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function buildStyle({ fgRgb = null, bgRgb = null, bold = false, italic = false } = {}) {
|
|
201
|
+
let style = chalk
|
|
202
|
+
if (bgRgb) style = style.bgRgb(...bgRgb)
|
|
203
|
+
if (fgRgb) style = style.rgb(...fgRgb)
|
|
204
|
+
if (bold) style = style.bold
|
|
205
|
+
if (italic) style = style.italic
|
|
206
|
+
return style
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export function getReadableTextRgb(bgRgb) {
|
|
210
|
+
const [r, g, b] = bgRgb
|
|
211
|
+
const yiq = (r * 299 + g * 587 + b * 114) / 1000
|
|
212
|
+
return yiq >= 150 ? [10, 16, 28] : [248, 251, 255]
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export function detectActiveTheme(configTheme = 'auto') {
|
|
12
216
|
if (configTheme === 'dark' || configTheme === 'light') {
|
|
13
|
-
activeTheme = configTheme
|
|
14
|
-
return activeTheme
|
|
217
|
+
activeTheme = configTheme
|
|
218
|
+
return activeTheme
|
|
15
219
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
activeTheme
|
|
21
|
-
return activeTheme;
|
|
22
|
-
} else if (fgbg) {
|
|
23
|
-
activeTheme = 'dark';
|
|
24
|
-
return activeTheme;
|
|
220
|
+
|
|
221
|
+
const fgbg = process.env.COLORFGBG || ''
|
|
222
|
+
if (fgbg.includes(';15') || fgbg.includes(';7') || fgbg.includes(';base03')) {
|
|
223
|
+
activeTheme = 'light'
|
|
224
|
+
return activeTheme
|
|
25
225
|
}
|
|
26
|
-
|
|
226
|
+
if (fgbg) {
|
|
227
|
+
activeTheme = 'dark'
|
|
228
|
+
return activeTheme
|
|
229
|
+
}
|
|
230
|
+
|
|
27
231
|
if (process.platform === 'darwin') {
|
|
28
232
|
try {
|
|
29
|
-
const style = execSync('defaults read -g AppleInterfaceStyle 2>/dev/null', { timeout: 100 }).toString().trim()
|
|
30
|
-
activeTheme = style === 'Dark' ? 'dark' : 'light'
|
|
233
|
+
const style = execSync('defaults read -g AppleInterfaceStyle 2>/dev/null', { timeout: 100 }).toString().trim()
|
|
234
|
+
activeTheme = style === 'Dark' ? 'dark' : 'light'
|
|
31
235
|
} catch {
|
|
32
|
-
activeTheme = 'light'
|
|
236
|
+
activeTheme = 'light'
|
|
33
237
|
}
|
|
34
|
-
|
|
35
|
-
activeTheme = 'dark';
|
|
238
|
+
return activeTheme
|
|
36
239
|
}
|
|
37
|
-
|
|
38
|
-
|
|
240
|
+
|
|
241
|
+
activeTheme = 'dark'
|
|
242
|
+
return activeTheme
|
|
39
243
|
}
|
|
40
244
|
|
|
41
245
|
export function getTheme() {
|
|
42
|
-
return activeTheme
|
|
246
|
+
return activeTheme
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export function cycleThemeSetting(currentTheme = 'auto') {
|
|
250
|
+
const currentIdx = THEME_OPTIONS.indexOf(currentTheme)
|
|
251
|
+
const nextIdx = currentIdx === -1 ? 0 : (currentIdx + 1) % THEME_OPTIONS.length
|
|
252
|
+
return THEME_OPTIONS[nextIdx]
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export function getThemeStatusLabel(setting = 'auto') {
|
|
256
|
+
if (setting === 'auto') return `Auto → ${themeLabel(activeTheme)}`
|
|
257
|
+
return themeLabel(setting)
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
export function getProviderRgb(providerKey) {
|
|
261
|
+
const palette = PROVIDER_PALETTES[activeTheme] ?? PROVIDER_PALETTES.dark
|
|
262
|
+
return palette[providerKey] ?? currentPalette().accent
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export function getTierRgb(tier) {
|
|
266
|
+
const palette = TIER_PALETTES[activeTheme] ?? TIER_PALETTES.dark
|
|
267
|
+
return palette[tier] ?? currentPalette().textStrong
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function paintRgb(rgb, text, options = {}) {
|
|
271
|
+
return buildStyle({ fgRgb: rgb, ...options })(text)
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function paintBg(bgRgb, text, fgRgb = null, options = {}) {
|
|
275
|
+
return buildStyle({ bgRgb, fgRgb: fgRgb ?? getReadableTextRgb(bgRgb), ...options })(text)
|
|
43
276
|
}
|
|
44
277
|
|
|
45
|
-
// Semantic colors
|
|
46
278
|
export const themeColors = {
|
|
47
|
-
text: (
|
|
48
|
-
textBold: (
|
|
49
|
-
dim: (
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
279
|
+
text: (text) => paintRgb(currentPalette().text, text),
|
|
280
|
+
textBold: (text) => paintRgb(currentPalette().textStrong, text, { bold: true }),
|
|
281
|
+
dim: (text) => paintRgb(currentPalette().muted, text),
|
|
282
|
+
soft: (text) => paintRgb(currentPalette().soft, text),
|
|
283
|
+
accent: (text) => paintRgb(currentPalette().accent, text),
|
|
284
|
+
accentBold: (text) => paintRgb(currentPalette().accentStrong, text, { bold: true }),
|
|
285
|
+
info: (text) => paintRgb(currentPalette().info, text),
|
|
286
|
+
success: (text) => paintRgb(currentPalette().success, text),
|
|
287
|
+
successBold: (text) => paintRgb(currentPalette().successStrong, text, { bold: true }),
|
|
288
|
+
warning: (text) => paintRgb(currentPalette().warning, text),
|
|
289
|
+
warningBold: (text) => paintRgb(currentPalette().warningStrong, text, { bold: true }),
|
|
290
|
+
error: (text) => paintRgb(currentPalette().danger, text),
|
|
291
|
+
errorBold: (text) => paintRgb(currentPalette().dangerStrong, text, { bold: true }),
|
|
292
|
+
hotkey: (text) => paintRgb(currentPalette().hotkey, text, { bold: true }),
|
|
293
|
+
link: (text) => paintRgb(currentPalette().link, text),
|
|
294
|
+
border: (text) => paintRgb(currentPalette().border, text),
|
|
295
|
+
footerLove: (text) => paintRgb(currentPalette().footerLove, text),
|
|
296
|
+
footerCoffee: (text) => paintRgb(currentPalette().footerCoffee, text),
|
|
297
|
+
footerDiscord: (text) => paintRgb(currentPalette().footerDiscord, text),
|
|
298
|
+
metricGood: (text) => paintRgb(currentPalette().successStrong, text),
|
|
299
|
+
metricOk: (text) => paintRgb(currentPalette().info, text),
|
|
300
|
+
metricWarn: (text) => paintRgb(currentPalette().warning, text),
|
|
301
|
+
metricBad: (text) => paintRgb(currentPalette().danger, text),
|
|
302
|
+
provider: (providerKey, text, { bold = false } = {}) => paintRgb(getProviderRgb(providerKey), text, { bold }),
|
|
303
|
+
tier: (tier, text, { bold = true } = {}) => paintRgb(getTierRgb(tier), text, { bold }),
|
|
304
|
+
badge: (text, bgRgb, fgRgb = null) => paintBg(bgRgb, ` ${text} `, fgRgb, { bold: true }),
|
|
305
|
+
bgCursor: (text) => paintBg(currentPalette().cursor.defaultBg, text, currentPalette().cursor.defaultFg),
|
|
306
|
+
bgCursorInstall: (text) => paintBg(currentPalette().cursor.installBg, text, currentPalette().cursor.installFg),
|
|
307
|
+
bgCursorSettingsList: (text) => paintBg(currentPalette().cursor.settingsBg, text, currentPalette().cursor.settingsFg),
|
|
308
|
+
bgCursorLegacy: (text) => paintBg(currentPalette().cursor.legacyBg, text, currentPalette().cursor.legacyFg),
|
|
309
|
+
bgModelCursor: (text) => paintBg(currentPalette().cursor.modelBg, text, currentPalette().cursor.modelFg),
|
|
310
|
+
bgModelRecommended: (text) => paintBg(currentPalette().cursor.recommendedBg, text, currentPalette().cursor.recommendedFg),
|
|
311
|
+
bgModelFavorite: (text) => paintBg(currentPalette().cursor.favoriteBg, text, currentPalette().cursor.favoriteFg),
|
|
312
|
+
overlayBgSettings: (text) => paintBg(currentPalette().overlayBg.settings, text, currentPalette().overlayFg),
|
|
313
|
+
overlayBgHelp: (text) => paintBg(currentPalette().overlayBg.help, text, currentPalette().overlayFg),
|
|
314
|
+
overlayBgRecommend: (text) => paintBg(currentPalette().overlayBg.recommend, text, currentPalette().overlayFg),
|
|
315
|
+
overlayBgFeedback: (text) => paintBg(currentPalette().overlayBg.feedback, text, currentPalette().overlayFg),
|
|
316
|
+
overlayBgChangelog: (text) => paintBg(currentPalette().overlayBg.changelog, text, currentPalette().overlayFg),
|
|
317
|
+
overlayBgCommandPalette: (text) => paintBg(currentPalette().overlayBg.commandPalette, text, currentPalette().overlayFg),
|
|
67
318
|
}
|
package/src/tier-colors.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file tier-colors.js
|
|
3
|
-
* @description Chalk colour functions for each tier level
|
|
3
|
+
* @description Theme-aware Chalk colour functions for each tier level.
|
|
4
4
|
*
|
|
5
5
|
* @details
|
|
6
6
|
* The tier system maps model quality tiers (S+, S, A+, A, A-, B+, B, C) to a
|
|
@@ -9,9 +9,10 @@
|
|
|
9
9
|
* single, consistent visual language without depending on the whole TUI entry point.
|
|
10
10
|
*
|
|
11
11
|
* The gradient is deliberately designed so that the higher the tier the more
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
12
|
+
* The previous palette used very dark reds and bright yellows directly, which
|
|
13
|
+
* became muddy on dark terminals and nearly invisible on light ones. This
|
|
14
|
+
* module now delegates to the semantic theme palette so tier colours stay
|
|
15
|
+
* readable in both modes while keeping the same best→worst ordering.
|
|
15
16
|
*
|
|
16
17
|
* @exports
|
|
17
18
|
* TIER_COLOR — object mapping tier string → chalk colouring function
|
|
@@ -21,17 +22,14 @@
|
|
|
21
22
|
*/
|
|
22
23
|
|
|
23
24
|
import chalk from 'chalk'
|
|
25
|
+
import { getTierRgb } from './theme.js'
|
|
24
26
|
|
|
25
|
-
// 📖
|
|
26
|
-
// 📖
|
|
27
|
-
// 📖
|
|
28
|
-
export const TIER_COLOR = {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
'B+': t => chalk.bold.rgb(255, 70, 0)(t), // 🟠 orange-red — average
|
|
35
|
-
'B': t => chalk.bold.rgb(210, 20, 0)(t), // 🔴 red — below avg
|
|
36
|
-
'C': t => chalk.bold.rgb(140, 0, 0)(t), // 🔴 dark red — lightweight
|
|
37
|
-
}
|
|
27
|
+
// 📖 TIER_COLOR remains object-like for existing call sites, but every access is
|
|
28
|
+
// 📖 resolved lazily from the live theme so `G`/Settings theme switches repaint
|
|
29
|
+
// 📖 the whole TUI without rebuilding import-time constants.
|
|
30
|
+
export const TIER_COLOR = new Proxy({}, {
|
|
31
|
+
get(_target, tier) {
|
|
32
|
+
if (typeof tier !== 'string') return undefined
|
|
33
|
+
return (text) => chalk.bold.rgb(...getTierRgb(tier))(text)
|
|
34
|
+
},
|
|
35
|
+
})
|