@bitmagic/cli 0.1.59-dev.1 → 0.1.59-dev.3
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/dist/cli.d.ts +1 -0
- package/dist/cli.js +2 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/theme.d.ts +3 -0
- package/dist/commands/theme.js +201 -0
- package/dist/commands/theme.js.map +1 -0
- package/dist/scaffold/project-files.d.ts +1 -0
- package/dist/scaffold/project-files.js +47 -1
- package/dist/scaffold/project-files.js.map +1 -1
- package/dist/scaffold/project.js +2 -1
- package/dist/scaffold/project.js.map +1 -1
- package/dist/theme/hud-theme-catalog.d.ts +236 -0
- package/dist/theme/hud-theme-catalog.js +386 -0
- package/dist/theme/hud-theme-catalog.js.map +1 -0
- package/dist/theme/theme-color-math.d.ts +21 -0
- package/dist/theme/theme-color-math.js +99 -0
- package/dist/theme/theme-color-math.js.map +1 -0
- package/dist/theme/theme-io.d.ts +49 -0
- package/dist/theme/theme-io.js +190 -0
- package/dist/theme/theme-io.js.map +1 -0
- package/dist/theme/theme-merge.d.ts +36 -0
- package/dist/theme/theme-merge.js +81 -0
- package/dist/theme/theme-merge.js.map +1 -0
- package/dist/theme/theme-report.d.ts +28 -0
- package/dist/theme/theme-report.js +142 -0
- package/dist/theme/theme-report.js.map +1 -0
- package/dist/theme/theme-stored.d.ts +21 -0
- package/dist/theme/theme-stored.js +50 -0
- package/dist/theme/theme-stored.js.map +1 -0
- package/dist/theme/theme-validate.d.ts +41 -0
- package/dist/theme/theme-validate.js +406 -0
- package/dist/theme/theme-validate.js.map +1 -0
- package/dist/verify/classify.js +19 -0
- package/dist/verify/classify.js.map +1 -1
- package/dist/verify/hud-theme.d.ts +55 -0
- package/dist/verify/hud-theme.js +70 -0
- package/dist/verify/hud-theme.js.map +1 -0
- package/package.json +4 -4
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The text report that rides every successful theme read/write — the model's
|
|
3
|
+
* eyes. An LLM cannot see the rendered HUD, so the one moment it can learn
|
|
4
|
+
* "your literal value will not render" or "your edit also changed X" is inside
|
|
5
|
+
* the tool response, in text, immediately.
|
|
6
|
+
*
|
|
7
|
+
* Two sections:
|
|
8
|
+
* changed: leaf-level diff of what the write actually did — the confirmation
|
|
9
|
+
* of the intended edit AND of anything a full replace changed
|
|
10
|
+
* without meaning to.
|
|
11
|
+
* contrast: the derived-ink pairings computed with EXACTLY the engine's
|
|
12
|
+
* arithmetic (theme-color-math mirrors engine colorMath, gate-
|
|
13
|
+
* checked), so "SUBSTITUTED -> renders #X" is a promise about the
|
|
14
|
+
* pixels, not an estimate.
|
|
15
|
+
*
|
|
16
|
+
* Deliberately report-only: the engine self-heals every pairing marked
|
|
17
|
+
* SUBSTITUTED, so nothing here blocks a write. The report exists so the agent
|
|
18
|
+
* can decide whether the substitute serves the user's ask — a lime accent that
|
|
19
|
+
* renders as forest green might be exactly right, or reason to pick a colour
|
|
20
|
+
* the ground can carry.
|
|
21
|
+
*/
|
|
22
|
+
import { accentInkOn, contrastRatio, inkOnSurface, mutedInkOnSurface, readableTextColor, } from './theme-color-math.js';
|
|
23
|
+
import { diffThemes } from './theme-merge.js';
|
|
24
|
+
const HEX = /^#[0-9a-fA-F]{6}$/;
|
|
25
|
+
const MAX_DIFF_LINES = 12;
|
|
26
|
+
function hex(value) {
|
|
27
|
+
return typeof value === 'string' && HEX.test(value) ? value : null;
|
|
28
|
+
}
|
|
29
|
+
function ratio(a, b) {
|
|
30
|
+
return `${(Math.round(contrastRatio(a, b) * 10) / 10).toFixed(1)}:1`;
|
|
31
|
+
}
|
|
32
|
+
function formatValue(v) {
|
|
33
|
+
if (typeof v === 'string')
|
|
34
|
+
return v;
|
|
35
|
+
if (typeof v === 'number' || typeof v === 'boolean')
|
|
36
|
+
return String(v);
|
|
37
|
+
if (v === undefined)
|
|
38
|
+
return '(absent)';
|
|
39
|
+
const json = JSON.stringify(v) ?? '(absent)';
|
|
40
|
+
return json.length > 40 ? `${json.slice(0, 37)}…` : json;
|
|
41
|
+
}
|
|
42
|
+
function formatDiff(entries) {
|
|
43
|
+
if (entries.length === 0)
|
|
44
|
+
return 'changed: nothing — the write produced the same theme';
|
|
45
|
+
const shown = entries.slice(0, MAX_DIFF_LINES)
|
|
46
|
+
.map(e => `${e.path} ${formatValue(e.from)}→${formatValue(e.to)}`);
|
|
47
|
+
const more = entries.length > MAX_DIFF_LINES ? `; +${entries.length - MAX_DIFF_LINES} more` : '';
|
|
48
|
+
return `changed: ${shown.join('; ')}${more}`;
|
|
49
|
+
}
|
|
50
|
+
function pair(label, ink, ground, substituted) {
|
|
51
|
+
const shown = substituted ?? ink;
|
|
52
|
+
const verdict = substituted === null ? 'OK' : `SUBSTITUTED -> renders ${substituted}`;
|
|
53
|
+
return { label, detail: `${ratio(shown, ground)} ${verdict}` };
|
|
54
|
+
}
|
|
55
|
+
function contrastSection(theme) {
|
|
56
|
+
const c = theme.colors ?? {};
|
|
57
|
+
const background = hex(c.background);
|
|
58
|
+
const surface = hex(c.surface);
|
|
59
|
+
const text = hex(c.text);
|
|
60
|
+
const textMuted = hex(c.textMuted);
|
|
61
|
+
const primary = hex(c.primary);
|
|
62
|
+
const danger = hex(c.danger);
|
|
63
|
+
const pairings = [];
|
|
64
|
+
if (text !== null && surface !== null) {
|
|
65
|
+
const derived = inkOnSurface(surface, text);
|
|
66
|
+
pairings.push(pair('text on elements', text, surface, derived === text ? null : derived));
|
|
67
|
+
}
|
|
68
|
+
if (textMuted !== null && surface !== null) {
|
|
69
|
+
const derived = mutedInkOnSurface(surface, textMuted);
|
|
70
|
+
pairings.push(pair('muted text on elements', textMuted, surface, derived === textMuted ? null : derived));
|
|
71
|
+
}
|
|
72
|
+
if (text !== null && background !== null) {
|
|
73
|
+
// The ground pairing has no engine substitute — `text` IS the ground ink —
|
|
74
|
+
// so a low value here is the one the agent must fix by hand.
|
|
75
|
+
const r = contrastRatio(text, background);
|
|
76
|
+
pairings.push({
|
|
77
|
+
label: 'text on panels',
|
|
78
|
+
detail: `${ratio(text, background)} ${r >= 4.5 ? 'OK' : 'LOW — no auto-fix for this pairing; adjust colors.text or colors.background'}`,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
if (primary !== null) {
|
|
82
|
+
// The engine picks the label by luminance threshold, not by best
|
|
83
|
+
// contrast, so mid-luminance fills (a strong orange, a mid blue) can
|
|
84
|
+
// land the label on the LOW side. That is what will render — surface
|
|
85
|
+
// it instead of blessing it. 3:1 is the large-text floor; button
|
|
86
|
+
// labels are display-scale.
|
|
87
|
+
const label = readableTextColor(primary);
|
|
88
|
+
const r = contrastRatio(label, primary);
|
|
89
|
+
pairings.push({
|
|
90
|
+
label: `button label (auto ${label} on primary)`,
|
|
91
|
+
detail: `${ratio(label, primary)} ${r >= 3 ? 'OK' : 'LOW — the auto label lands on the low-contrast side of this fill; nudge colors.primary lighter or darker so the derived label clears'}`,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
if (primary !== null && background !== null) {
|
|
95
|
+
const derived = accentInkOn(background, primary);
|
|
96
|
+
pairings.push(pair('primary as heading/type', primary, background, derived));
|
|
97
|
+
}
|
|
98
|
+
if (danger !== null && background !== null) {
|
|
99
|
+
const derived = accentInkOn(background, danger);
|
|
100
|
+
pairings.push(pair('danger as error text', danger, background, derived));
|
|
101
|
+
}
|
|
102
|
+
if (primary !== null) {
|
|
103
|
+
// Bar fill against its trough. Nothing engine-side heals this pairing, so
|
|
104
|
+
// it is advisory: below 2:1 a fill cannot be told from its own channel.
|
|
105
|
+
const trough = hex(theme.elements?.progressBar?.colors?.background) ?? background;
|
|
106
|
+
if (trough !== null) {
|
|
107
|
+
const r = contrastRatio(primary, trough);
|
|
108
|
+
pairings.push({
|
|
109
|
+
label: 'bar fill vs its track',
|
|
110
|
+
detail: `${ratio(primary, trough)} ${r >= 2 ? 'OK' : 'LOW — the fill will blend into its track; darken the track via elements.progressBar.colors.background'}`,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const healthTrough = hex(theme.elements?.healthBar?.colors?.background);
|
|
115
|
+
const healthDanger = hex(theme.elements?.healthBar?.colors?.danger) ?? danger;
|
|
116
|
+
if (healthTrough !== null && healthDanger !== null) {
|
|
117
|
+
const r = contrastRatio(healthDanger, healthTrough);
|
|
118
|
+
pairings.push({
|
|
119
|
+
label: 'healthBar critical fill vs its track',
|
|
120
|
+
detail: `${ratio(healthDanger, healthTrough)} ${r >= 2 ? 'OK' : 'LOW'}`,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
if (pairings.length === 0)
|
|
124
|
+
return [];
|
|
125
|
+
const width = Math.max(...pairings.map(p => p.label.length));
|
|
126
|
+
return ['contrast:', ...pairings.map(p => ` ${p.label.padEnd(width)} ${p.detail}`)];
|
|
127
|
+
}
|
|
128
|
+
export function buildThemeReport(after, options) {
|
|
129
|
+
const theme = (typeof after === 'object' && after !== null ? after : {});
|
|
130
|
+
const name = typeof theme.name === 'string' ? theme.name : '(unnamed)';
|
|
131
|
+
const lines = [];
|
|
132
|
+
const diff = options.before === undefined ? null : diffThemes(options.before, after);
|
|
133
|
+
lines.push(`theme: "${name}" (${options.baseLabel}${diff ? `, ${diff.length} change${diff.length === 1 ? '' : 's'}` : ''})`);
|
|
134
|
+
if (diff)
|
|
135
|
+
lines.push(formatDiff(diff));
|
|
136
|
+
lines.push(...contrastSection(theme));
|
|
137
|
+
if (lines.some(l => l.includes('SUBSTITUTED'))) {
|
|
138
|
+
lines.push('SUBSTITUTED = the engine will not render your literal value there; it derives the shown replacement. Patch the colour only if the substitute is wrong for the ask.');
|
|
139
|
+
}
|
|
140
|
+
return lines.join('\n');
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=theme-report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"theme-report.js","sourceRoot":"","sources":["../../src/theme/theme-report.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One resolver for "what does this stored hud.theme value render as" — the
|
|
3
|
+
* logic both lanes need before a patch (the base), a report (the `before`
|
|
4
|
+
* side), or a verdict (`show`/`check`) can exist.
|
|
5
|
+
*
|
|
6
|
+
* It lived twice: the write tool's currentEffectiveTheme and the CLI's
|
|
7
|
+
* readStoredTheme each hand-rolled the stored-value→tokens mapping, and they
|
|
8
|
+
* had already diverged — the CLI copy named an unknown preset truthfully while
|
|
9
|
+
* the agent copy silently folded it into "the engine default". A new stored
|
|
10
|
+
* form (or a label fix) had to land twice with nothing watching. This file is
|
|
11
|
+
* part of the byte-identical kit mirror, so the gate now watches.
|
|
12
|
+
*/
|
|
13
|
+
export interface ResolvedStoredTheme {
|
|
14
|
+
kind: 'preset' | 'custom' | 'none' | 'unknown-preset';
|
|
15
|
+
/** The tokens the engine renders for this stored value. */
|
|
16
|
+
tokens: Record<string, unknown>;
|
|
17
|
+
/** Human-readable description of where `tokens` came from. */
|
|
18
|
+
label: string;
|
|
19
|
+
}
|
|
20
|
+
/** Resolve a raw `worldProfileData.hud.theme` value to what the engine renders. */
|
|
21
|
+
export declare function resolveStoredTheme(stored: unknown): ResolvedStoredTheme;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One resolver for "what does this stored hud.theme value render as" — the
|
|
3
|
+
* logic both lanes need before a patch (the base), a report (the `before`
|
|
4
|
+
* side), or a verdict (`show`/`check`) can exist.
|
|
5
|
+
*
|
|
6
|
+
* It lived twice: the write tool's currentEffectiveTheme and the CLI's
|
|
7
|
+
* readStoredTheme each hand-rolled the stored-value→tokens mapping, and they
|
|
8
|
+
* had already diverged — the CLI copy named an unknown preset truthfully while
|
|
9
|
+
* the agent copy silently folded it into "the engine default". A new stored
|
|
10
|
+
* form (or a label fix) had to land twice with nothing watching. This file is
|
|
11
|
+
* part of the byte-identical kit mirror, so the gate now watches.
|
|
12
|
+
*/
|
|
13
|
+
import { DEFAULT_THEME_TOKENS, HUD_PRESETS, presetTheme } from './hud-theme-catalog.js';
|
|
14
|
+
/** Resolve a raw `worldProfileData.hud.theme` value to what the engine renders. */
|
|
15
|
+
export function resolveStoredTheme(stored) {
|
|
16
|
+
if (stored === undefined || stored === null) {
|
|
17
|
+
return {
|
|
18
|
+
kind: 'none',
|
|
19
|
+
tokens: structuredClone(DEFAULT_THEME_TOKENS),
|
|
20
|
+
label: 'the engine default (no theme set)',
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
if (typeof stored === 'string') {
|
|
24
|
+
if (HUD_PRESETS.includes(stored)) {
|
|
25
|
+
return {
|
|
26
|
+
kind: 'preset',
|
|
27
|
+
tokens: presetTheme(stored),
|
|
28
|
+
label: `preset ${stored}`,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
kind: 'unknown-preset',
|
|
33
|
+
tokens: structuredClone(DEFAULT_THEME_TOKENS),
|
|
34
|
+
label: `the engine default (unknown preset "${stored}")`,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
if (typeof stored === 'object' && !Array.isArray(stored)) {
|
|
38
|
+
return {
|
|
39
|
+
kind: 'custom',
|
|
40
|
+
tokens: stored,
|
|
41
|
+
label: 'the current custom theme',
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
kind: 'unknown-preset',
|
|
46
|
+
tokens: structuredClone(DEFAULT_THEME_TOKENS),
|
|
47
|
+
label: `the engine default (hud.theme has unexpected type ${typeof stored})`,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=theme-stored.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"theme-stored.js","sourceRoot":"","sources":["../../src/theme/theme-stored.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,oBAAoB,EAAE,WAAW,EAAE,WAAW,EAAsB,MAAM,wBAAwB,CAAC;AAU5G,mFAAmF;AACnF,MAAM,UAAU,kBAAkB,CAAC,MAAe;IAC9C,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QAC1C,OAAO;YACH,IAAI,EAAE,MAAM;YACZ,MAAM,EAAE,eAAe,CAAC,oBAAoB,CAA4B;YACxE,KAAK,EAAE,mCAAmC;SAC7C,CAAC;IACN,CAAC;IACD,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;QAC7B,IAAK,WAAqC,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YAC1D,OAAO;gBACH,IAAI,EAAE,QAAQ;gBACd,MAAM,EAAE,WAAW,CAAC,MAAuB,CAAC;gBAC5C,KAAK,EAAE,UAAU,MAAM,EAAE;aAC5B,CAAC;QACN,CAAC;QACD,OAAO;YACH,IAAI,EAAE,gBAAgB;YACtB,MAAM,EAAE,eAAe,CAAC,oBAAoB,CAA4B;YACxE,KAAK,EAAE,uCAAuC,MAAM,IAAI;SAC3D,CAAC;IACN,CAAC;IACD,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACvD,OAAO;YACH,IAAI,EAAE,QAAQ;YACd,MAAM,EAAE,MAAiC;YACzC,KAAK,EAAE,0BAA0B;SACpC,CAAC;IACN,CAAC;IACD,OAAO;QACH,IAAI,EAAE,gBAAgB;QACtB,MAAM,EAAE,eAAe,CAAC,oBAAoB,CAA4B;QACxE,KAAK,EAAE,qDAAqD,OAAO,MAAM,GAAG;KAC/E,CAAC;AACN,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The strict write-time HUD theme validator.
|
|
3
|
+
*
|
|
4
|
+
* STRICTER than the engine's load-time validator on purpose, and that asymmetry
|
|
5
|
+
* is the design: at WRITE time a bad per-element leaf is an error the agent can
|
|
6
|
+
* fix in the same turn, while at LOAD time the engine must keep rendering
|
|
7
|
+
* already-published themes, so it warns and drops the leaf instead. Everything
|
|
8
|
+
* this validator accepts, the engine accepts; the reverse is not required.
|
|
9
|
+
*
|
|
10
|
+
* This replaced `lightValidateTheme`, which skipped glow keys, every numeric
|
|
11
|
+
* range, the whole `elements` block, decoration slot suitability and decoration
|
|
12
|
+
* size/position/repeat — six ways a theme could pass the write tool and then
|
|
13
|
+
* silently render as the DEFAULT look at engine load, which reads as "theming
|
|
14
|
+
* is broken" rather than "the value was wrong". Parity with the engine is
|
|
15
|
+
* enforced by scripts/check-hud-catalog.ts, which runs both validators over a
|
|
16
|
+
* fixture corpus under tsx and compares verdicts.
|
|
17
|
+
*
|
|
18
|
+
* Validation rules mirror game/src/engine/hud/validateTheme.ts; vocabulary
|
|
19
|
+
* comes from the drift-checked catalog next door.
|
|
20
|
+
*/
|
|
21
|
+
import { type CatalogValidationIssue } from './hud-theme-catalog.js';
|
|
22
|
+
export interface StrictValidationResult {
|
|
23
|
+
ok: boolean;
|
|
24
|
+
errors: CatalogValidationIssue[];
|
|
25
|
+
warnings: CatalogValidationIssue[];
|
|
26
|
+
}
|
|
27
|
+
export declare function suggestClosest(value: string, candidates: ReadonlyArray<string>): string | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* Whether an issue would make the ENGINE reject the whole theme, or only drop a
|
|
30
|
+
* leaf while the theme keeps rendering.
|
|
31
|
+
*
|
|
32
|
+
* This validator is deliberately stricter than the engine at two spots, and the
|
|
33
|
+
* difference matters when DESCRIBING a stored theme: per-element
|
|
34
|
+
* font/colors/shape leaves are warn-and-dropped at engine load (the theme still
|
|
35
|
+
* renders minus the leaf), and unknown top-level color tokens are ignored
|
|
36
|
+
* outright. Telling a user "the engine is falling back to the default look"
|
|
37
|
+
* over one droppable leaf describes a fallback that is not happening — and an
|
|
38
|
+
* agent acting on that "repairs" a theme the user is actually seeing.
|
|
39
|
+
*/
|
|
40
|
+
export declare function isEngineFatalIssue(issue: CatalogValidationIssue): boolean;
|
|
41
|
+
export declare function validateThemeStrict(theme: unknown): StrictValidationResult;
|