@nurix/etna 0.20.17 → 0.20.18
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/data/harness-index.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"harness": "code",
|
|
3
3
|
"name": "code",
|
|
4
|
-
"version": "3.16.
|
|
4
|
+
"version": "3.16.6",
|
|
5
5
|
"files": [
|
|
6
6
|
".prettierignore",
|
|
7
7
|
".prettierrc.json",
|
|
@@ -25,5 +25,5 @@
|
|
|
25
25
|
}
|
|
26
26
|
],
|
|
27
27
|
"docsRoot": "docs",
|
|
28
|
-
"digest": "
|
|
28
|
+
"digest": "79daa8edeb53"
|
|
29
29
|
}
|
|
@@ -36,6 +36,8 @@ personality:
|
|
|
36
36
|
Each prevents a known silent-failure class:
|
|
37
37
|
|
|
38
38
|
- **Specificity armor.** Theme blocks are `:root:not(.dark)` and `:root.dark` — both specificity (0,2,0) — keyed on the `.dark` class the library compiles into its own stylesheet. A `data-theme` attribute keys none of the library's `dark:` variants, its theme toggle, or its toaster: an app on the attribute inverts its tokens and renders mixed library chrome.
|
|
39
|
+
- **Theme-invariant tokens sit in a bare `:root`.** Only the colour roles are themed. Font stacks, the type ramp, radii, shadows, motion and layout emit into a bare `:root` block ahead of the two theme blocks. Behind `:root:not(.dark)` they match nothing once `.dark` is on the root, and every `font-size: var(--text-…)` collapses to an inherited 16px in dark mode while the colours still look right.
|
|
40
|
+
- **The ramp emits Tailwind's composite sub-keys.** `--text-<step>` with `--line-height`, `--font-weight` and `--letter-spacing` sub-keys are exactly what the `text-<step>` utility applies. A `-size`/`-line`/`-weight` suffix form generates junk utilities and leaves `text-<step>` undefined. The `--font-feature-settings` and `--font-family` sub-keys are inert for the utility and are read through `var()`. The dense operator tier's steps are optional: absent steps emit nothing and warn once per package (`dmw-08`).
|
|
39
41
|
- **The tone trio.** `success`, `warning`, and `info` reach the library as `--tone-success|warning|info` — what a status dot (`<Chip prefix="dot" color="var(--tone-success)">`) and the copy button's check read, falling back to the library's own hue when a role is absent. Declare all three, in both themes — a missing `info` (`dmw-06`) puts the library's blue beside the package's green and amber.
|
|
40
42
|
- **Font binding by derivation.** Font stacks bind to installed packages under **registered family names** (`"Inter Variable"`, not `"Inter"`), each with its declared fallback stack — otherwise the flagship font silently never renders. Install the font packages the design.md declares (e.g. Fontsource) and verify the name matches what the package registers. The library's `font-sans` / `font-mono` / `font-display` utilities compile to `inherit` on purpose (a ramp, never a face): the app declares its own theme keys in a plain `@theme` pointing at the emitted slots — `--font-body: var(--font-sans)`, `--font-heading: var(--font-display)`, `--font-code: var(--font-mono)` — and sets the ambient face on `body` in `@layer base`.
|
|
41
43
|
- **Every authored token group reaches a named consumer.** An authored ramp with no emitter vanishes without a trace — wire each group (colors → theme classes/utilities, ramp → text styles, elevation → radius/shadow utilities, motion → duration/easing vars) and let the ship gate's reach check prove it. The personality families reach the library by construction; the app's `--duration-*` / `--ease-*` reach app-owned animation code, or they are unused.
|
|
@@ -105,14 +105,15 @@
|
|
|
105
105
|
// background (checked only where chroma ≥ 0.01 — hue is noise near gray).
|
|
106
106
|
// dmw-07 accent chroma delta between themes > 0.06 (identity likely not held across themes).
|
|
107
107
|
//
|
|
108
|
-
//
|
|
109
|
-
//
|
|
110
|
-
//
|
|
111
|
-
//
|
|
112
|
-
//
|
|
113
|
-
// -
|
|
114
|
-
//
|
|
115
|
-
//
|
|
108
|
+
// dmw-08 the dense operator tier is unadopted: typography.ramp omits optional steps (one warn per package).
|
|
109
|
+
//
|
|
110
|
+
// Emitted artifact shape (deterministic, byte-stable): THREE blocks. A bare `:root` carries every
|
|
111
|
+
// theme-invariant group — --font-display/sans/mono (registered names quoted, fallback stacks appended),
|
|
112
|
+
// the ramp as Tailwind composite keys --text-<step> + --line-height/--font-weight/--letter-spacing
|
|
113
|
+
// sub-keys, --radius-*, --shadow-*, --duration-*, --ease-*, and the layout vars (unmapped keys emit as
|
|
114
|
+
// --layout-<key>). Then `:root:not(.dark)` and `:root.dark` (both 0,2,0) carry the colour roles.
|
|
115
|
+
// Theme-invariant tokens must never move behind `:root:not(.dark)`: it matches nothing once `.dark` is on
|
|
116
|
+
// the root, and the whole ramp collapses to inherited 16px in dark mode (references/emission.md).
|
|
116
117
|
|
|
117
118
|
import { readFileSync, writeFileSync, existsSync, statSync, readdirSync, realpathSync } from "node:fs";
|
|
118
119
|
import { join, resolve, basename, dirname, relative, sep } from "node:path";
|
|
@@ -140,7 +141,11 @@ const SECTIONS = [
|
|
|
140
141
|
"11 Responsive", "12 Agent Prompt Guide",
|
|
141
142
|
];
|
|
142
143
|
|
|
143
|
-
|
|
144
|
+
// The dense operator tier is optional: every catalog package authors only the seven required steps, and
|
|
145
|
+
// each new value would be a taste decision per identity. Absent optional steps warn once (dmw-08) and emit nothing.
|
|
146
|
+
const RAMP_STEPS_REQUIRED = ["display", "h1", "h2", "body", "small", "caption", "data"];
|
|
147
|
+
const RAMP_STEPS_OPTIONAL = ["title", "h3", "prose", "control-lg", "dense", "control", "meta", "micro", "code", "ident"];
|
|
148
|
+
const RAMP_STEPS = [...RAMP_STEPS_REQUIRED, ...RAMP_STEPS_OPTIONAL];
|
|
144
149
|
const FONT_SLOTS = ["display", "sans", "mono"];
|
|
145
150
|
// Optional color roles: emitted only when declared (both themes or neither). `info` completes the
|
|
146
151
|
// success/warning/info trio @nurix/components' status dots read through --tone-*.
|
|
@@ -688,9 +693,17 @@ function checkCoverage(values, report) {
|
|
|
688
693
|
for (const slot of FONT_SLOTS) if (!isObj(typo[slot])) missing(`typography.${slot}`);
|
|
689
694
|
if (!isObj(typo.ramp)) missing("typography.ramp");
|
|
690
695
|
else {
|
|
696
|
+
// One warn per package, not one per step, so the taste warns stay readable across the catalog.
|
|
697
|
+
const unadopted = RAMP_STEPS_OPTIONAL.filter((s) => !isObj(typo.ramp[s]));
|
|
698
|
+
if (unadopted.length) {
|
|
699
|
+
warn(report, "dmw-08", `dense operator tier unadopted — typography.ramp missing ${unadopted.length}/${RAMP_STEPS_OPTIONAL.length}: ${unadopted.join(", ")}`);
|
|
700
|
+
}
|
|
691
701
|
for (const step of RAMP_STEPS) {
|
|
692
702
|
const r = typo.ramp[step];
|
|
693
|
-
if (!isObj(r)) {
|
|
703
|
+
if (!isObj(r)) {
|
|
704
|
+
if (!RAMP_STEPS_OPTIONAL.includes(step)) missing(`typography.ramp.${step}`);
|
|
705
|
+
continue;
|
|
706
|
+
}
|
|
694
707
|
for (const k of ["size", "line", "weight"]) if (r[k] == null) missing(`typography.ramp.${step}.${k}`);
|
|
695
708
|
if (r.family == null) missing(`typography.ramp.${step}.family`);
|
|
696
709
|
else if (!FONT_SLOTS.includes(r.family)) {
|
|
@@ -997,30 +1010,27 @@ export function emitTokens(front, values) {
|
|
|
997
1010
|
const out = [];
|
|
998
1011
|
out.push(`/* design.tokens.css — compiled from ${id} (design-md/v2) by validate-design-md.mjs — derived artifact: edit the design.md, never this file */`);
|
|
999
1012
|
out.push("");
|
|
1000
|
-
|
|
1001
|
-
out.push("
|
|
1002
|
-
for (const role of COLOR_ROLES) out.push(` --${role}: ${cssValue(req(values, ["color", "light", role]))};`);
|
|
1003
|
-
for (const role of OPTIONAL_COLOR_ROLES) {
|
|
1004
|
-
const v = values.color.light[role];
|
|
1005
|
-
if (v != null) out.push(` --${role}: ${cssValue(v)};`);
|
|
1006
|
-
}
|
|
1007
|
-
out.push("");
|
|
1013
|
+
// Theme-invariant tokens sit in a bare `:root`; behind `:root:not(.dark)` they would not exist in dark mode.
|
|
1014
|
+
out.push(":root {");
|
|
1008
1015
|
out.push(" /* typography */");
|
|
1009
1016
|
for (const slot of FONT_SLOTS) {
|
|
1010
1017
|
const f = req(values, ["typography", slot]);
|
|
1011
1018
|
if (f.family == null || f.fallback == null) throw new Error(`cannot emit: typography.${slot} family/fallback missing`);
|
|
1012
1019
|
out.push(` --font-${slot}: "${f.family}", ${f.fallback};`);
|
|
1013
1020
|
}
|
|
1021
|
+
const ramp = req(values, ["typography", "ramp"]);
|
|
1014
1022
|
for (const step of RAMP_STEPS) {
|
|
1023
|
+
if (ramp[step] == null && RAMP_STEPS_OPTIONAL.includes(step)) continue;
|
|
1015
1024
|
const r = req(values, ["typography", "ramp", step]);
|
|
1016
|
-
|
|
1017
|
-
out.push(` --text-${step}
|
|
1018
|
-
out.push(` --text-${step}-
|
|
1019
|
-
|
|
1020
|
-
if (r.tracking != null) out.push(` --text-${step}-
|
|
1025
|
+
// Tailwind's `text-*` utility reads exactly --line-height, --letter-spacing and --font-weight sub-keys.
|
|
1026
|
+
out.push(` --text-${step}: ${cssValue(req(r, ["size"]))};`);
|
|
1027
|
+
out.push(` --text-${step}--line-height: ${cssValue(req(r, ["line"]))};`);
|
|
1028
|
+
out.push(` --text-${step}--font-weight: ${cssValue(req(r, ["weight"]))};`);
|
|
1029
|
+
if (r.tracking != null) out.push(` --text-${step}--letter-spacing: ${cssValue(r.tracking)};`);
|
|
1021
1030
|
if (Array.isArray(r.features) && r.features.length) {
|
|
1022
|
-
out.push(` --text-${step}-
|
|
1031
|
+
out.push(` --text-${step}--font-feature-settings: ${r.features.map((x) => `"${x}"`).join(", ")}; /* inert for text-*; read via var() */`);
|
|
1023
1032
|
}
|
|
1033
|
+
if (r.family != null) out.push(` --text-${step}--font-family: var(--font-${r.family}); /* inert for text-*; read via var() */`);
|
|
1024
1034
|
}
|
|
1025
1035
|
out.push("");
|
|
1026
1036
|
out.push(" /* elevation */");
|
|
@@ -1043,15 +1053,18 @@ export function emitTokens(front, values) {
|
|
|
1043
1053
|
}
|
|
1044
1054
|
}
|
|
1045
1055
|
out.push("}");
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
const
|
|
1052
|
-
|
|
1056
|
+
// Both colour blocks reach (0,2,0), one notch above a library's unlayered `:root`/`.dark`, whatever the import order.
|
|
1057
|
+
for (const [selector, theme] of [[":root:not(.dark)", "light"], [":root.dark", "dark"]]) {
|
|
1058
|
+
out.push("");
|
|
1059
|
+
out.push(`${selector} {`);
|
|
1060
|
+
out.push(` /* color roles — ${theme} */`);
|
|
1061
|
+
for (const role of COLOR_ROLES) out.push(` --${role}: ${cssValue(req(values, ["color", theme, role]))};`);
|
|
1062
|
+
for (const role of OPTIONAL_COLOR_ROLES) {
|
|
1063
|
+
const v = values.color[theme][role];
|
|
1064
|
+
if (v != null) out.push(` --${role}: ${cssValue(v)};`);
|
|
1065
|
+
}
|
|
1066
|
+
out.push("}");
|
|
1053
1067
|
}
|
|
1054
|
-
out.push("}");
|
|
1055
1068
|
return out.join("\n") + "\n";
|
|
1056
1069
|
}
|
|
1057
1070
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nurix/etna",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.18",
|
|
4
4
|
"description": "Bootstrap a project with a Nurix harness — copies the harness's CLAUDE.md, skills, agents, rules, and hooks directly into the project from a bundled payload (no marketplace fetch, no network).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -42,5 +42,5 @@
|
|
|
42
42
|
"agent",
|
|
43
43
|
"plugin"
|
|
44
44
|
],
|
|
45
|
-
"etnaFingerprint": "
|
|
45
|
+
"etnaFingerprint": "993a4863fa1f"
|
|
46
46
|
}
|