@theme-registry/refract 0.1.4 → 0.1.5
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/build/prompt.d.ts +15 -0
- package/dist/build.cjs.js +1 -1
- package/dist/build.cjs.js.map +1 -1
- package/dist/build.esm.js +1 -1
- package/dist/build.esm.js.map +1 -1
- package/dist/cli.js +70 -8
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -8004,7 +8004,7 @@ const RATIO_VALUES = {
|
|
|
8004
8004
|
* overlap, so a scaffolded literal and an authored `harmony:` key agree. `pentadic` is local — the
|
|
8005
8005
|
* subsystem has no five-way scheme, and the scaffolder needs one to reach five brand colours.
|
|
8006
8006
|
*/
|
|
8007
|
-
const SCHEME_ROTATIONS = {
|
|
8007
|
+
const SCHEME_ROTATIONS$1 = {
|
|
8008
8008
|
complement: [180],
|
|
8009
8009
|
analogous: [-30, 30],
|
|
8010
8010
|
"split-complement": [150, 210],
|
|
@@ -8106,7 +8106,7 @@ const defaultSchemeFor = (brandCount) => {
|
|
|
8106
8106
|
return "pentadic";
|
|
8107
8107
|
};
|
|
8108
8108
|
/** Schemes that produce exactly `brandCount − 1` members — the valid choices at that count. */
|
|
8109
|
-
const schemesFor = (brandCount) => Object.keys(SCHEME_ROTATIONS).filter((s) => SCHEME_ROTATIONS[s].length === brandCount - 1);
|
|
8109
|
+
const schemesFor = (brandCount) => Object.keys(SCHEME_ROTATIONS$1).filter((s) => SCHEME_ROTATIONS$1[s].length === brandCount - 1);
|
|
8110
8110
|
/**
|
|
8111
8111
|
* The contrast gate. Builds the palette set, audits every text-on-base pairing, and walks the
|
|
8112
8112
|
* lightness of each failing colour down one OKLCH point at a time until it clears the bar.
|
|
@@ -8202,7 +8202,7 @@ function scaffoldTheme(answers) {
|
|
|
8202
8202
|
}
|
|
8203
8203
|
else if (brandCount > 1) {
|
|
8204
8204
|
const scheme = (_g = answers.scheme) !== null && _g !== void 0 ? _g : defaultSchemeFor(brandCount);
|
|
8205
|
-
const rotations = SCHEME_ROTATIONS[scheme];
|
|
8205
|
+
const rotations = SCHEME_ROTATIONS$1[scheme];
|
|
8206
8206
|
if (!rotations)
|
|
8207
8207
|
throw new Error(`Unknown harmony scheme "${scheme}".`);
|
|
8208
8208
|
rotations.slice(0, brandCount - 1).forEach((deg, i) => {
|
|
@@ -8460,6 +8460,35 @@ const cursorUp = (n) => (n > 0 ? `\u001b[${n}A` : "");
|
|
|
8460
8460
|
const CLEAR_DOWN = "\u001b[0J";
|
|
8461
8461
|
const HIDE_CURSOR = "\u001b[?25l";
|
|
8462
8462
|
const SHOW_CURSOR = "\u001b[?25h";
|
|
8463
|
+
/**
|
|
8464
|
+
* Does this terminal do 24-bit colour? `COLORTERM` is the only real signal, and plenty of capable
|
|
8465
|
+
* terminals (Apple Terminal among them) never set it — so a missing value means "approximate", not
|
|
8466
|
+
* "give up". Gating swatches on truecolor alone would silently delete them for those users.
|
|
8467
|
+
*/
|
|
8468
|
+
const truecolor = () => { var _a; return /truecolor|24bit/i.test((_a = process.env.COLORTERM) !== null && _a !== void 0 ? _a : ""); };
|
|
8469
|
+
/** Nearest xterm-256 colour-cube index for an RGB triple — the fallback when 24-bit isn't offered. */
|
|
8470
|
+
const to256 = (r, g, b) => {
|
|
8471
|
+
const axis = (v) => (v < 48 ? 0 : v < 114 ? 1 : Math.min(5, Math.round((v - 35) / 40)));
|
|
8472
|
+
return 16 + 36 * axis(r) + 6 * axis(g) + axis(b);
|
|
8473
|
+
};
|
|
8474
|
+
/**
|
|
8475
|
+
* `#rrggbb` → a coloured block: exact in 24-bit terminals, approximated in 256-colour ones, and
|
|
8476
|
+
* empty when there's no colour at all (a pipe, `NO_COLOR`) so redirected output stays clean.
|
|
8477
|
+
*
|
|
8478
|
+
* Approximating is right here — the swatch is a preview, and the exact value is written to the theme
|
|
8479
|
+
* file regardless. An approximately-right hue beats a blank space.
|
|
8480
|
+
*/
|
|
8481
|
+
function swatch(hex, width = 2) {
|
|
8482
|
+
if (!useColor())
|
|
8483
|
+
return "";
|
|
8484
|
+
const m = /^#?([0-9a-f]{6})$/i.exec(hex.trim());
|
|
8485
|
+
if (!m)
|
|
8486
|
+
return "";
|
|
8487
|
+
const n = parseInt(m[1], 16);
|
|
8488
|
+
const [r, g, b] = [(n >> 16) & 255, (n >> 8) & 255, n & 255];
|
|
8489
|
+
const bg = truecolor() ? `48;2;${r};${g};${b}` : `48;5;${to256(r, g, b)}`;
|
|
8490
|
+
return `\u001b[${bg}m${" ".repeat(width)}\u001b[0m`;
|
|
8491
|
+
}
|
|
8463
8492
|
/** Decode a raw stdin chunk into a {@link ListKey}. Arrows arrive as escape sequences; j/k mirror vim. */
|
|
8464
8493
|
function decodeKey(chunk) {
|
|
8465
8494
|
switch (chunk) {
|
|
@@ -8650,13 +8679,24 @@ class Prompter {
|
|
|
8650
8679
|
};
|
|
8651
8680
|
let painted = 0;
|
|
8652
8681
|
const help = multi ? "↑↓ move · space toggle · a all · enter confirm" : "↑↓ move · enter select";
|
|
8682
|
+
// Pad labels so the hints line up into a column — a ragged right edge is most of why a long
|
|
8683
|
+
// option list reads as noise.
|
|
8684
|
+
const widest = choices.reduce((w, c) => Math.max(w, c.label.length), 0);
|
|
8653
8685
|
const frame = () => {
|
|
8654
8686
|
const rows = choices.map((c, i) => {
|
|
8687
|
+
var _a;
|
|
8655
8688
|
const here = i === state.cursor;
|
|
8656
|
-
|
|
8689
|
+
// Focus and selection are SEPARATE signals and both need to be visible: the caret says
|
|
8690
|
+
// where you are, the box says what's chosen. Showing only one (as this did) leaves a
|
|
8691
|
+
// multi-select where you can't tell what the keys will act on.
|
|
8692
|
+
const caret = here ? green("❯") : " ";
|
|
8693
|
+
const box = multi ? (state.selected.has(i) ? green("[✓]") : dim("[ ]")) : "";
|
|
8657
8694
|
const name = here ? bold(c.label) : c.label;
|
|
8658
|
-
const
|
|
8659
|
-
|
|
8695
|
+
const pad = " ".repeat(Math.max(0, widest - c.label.length));
|
|
8696
|
+
const blocks = ((_a = c.swatches) !== null && _a !== void 0 ? _a : []).map(s => swatch(s)).filter(Boolean).join(" ");
|
|
8697
|
+
const art = blocks ? ` ${blocks}` : "";
|
|
8698
|
+
const hint = c.hint ? ` ${dim(c.hint)}` : "";
|
|
8699
|
+
return ` ${caret} ${box ? `${box} ` : ""}${name}${pad}${art}${hint}`;
|
|
8660
8700
|
});
|
|
8661
8701
|
return [`${cyan("?")} ${bold(label)} ${dim(help)}`, ...rows].join("\n");
|
|
8662
8702
|
};
|
|
@@ -8762,6 +8802,19 @@ class Prompter {
|
|
|
8762
8802
|
}
|
|
8763
8803
|
}
|
|
8764
8804
|
|
|
8805
|
+
/**
|
|
8806
|
+
* The hues a scheme derives from a seed — the same rotations the generator will bake, so the
|
|
8807
|
+
* preview beside each option is the palette you'd actually get, not an illustration.
|
|
8808
|
+
*/
|
|
8809
|
+
const SCHEME_ROTATIONS = {
|
|
8810
|
+
complement: [180],
|
|
8811
|
+
analogous: [-30, 30],
|
|
8812
|
+
"split-complement": [150, 210],
|
|
8813
|
+
triadic: [120, 240],
|
|
8814
|
+
tetradic: [90, 180, 270],
|
|
8815
|
+
pentadic: [72, 144, 216, 288],
|
|
8816
|
+
};
|
|
8817
|
+
const schemeHues = (seedHex, scheme) => { var _a; return ((_a = SCHEME_ROTATIONS[scheme]) !== null && _a !== void 0 ? _a : []).map(deg => convertRgbToHex(parseColor(rotateHue(seedHex, deg)).rgb)); };
|
|
8765
8818
|
/** What each harmony scheme is for, in one line — the names mean nothing to most people. */
|
|
8766
8819
|
const SCHEME_HINTS = {
|
|
8767
8820
|
complement: "one companion, 180° opposite — maximum separation",
|
|
@@ -8804,7 +8857,9 @@ async function promptCreateAnswers(p, given = {}) {
|
|
|
8804
8857
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
8805
8858
|
const seed = (_a = given.seed) !== null && _a !== void 0 ? _a : (await p.text("Primary colour", "#4c6ef5", isColor));
|
|
8806
8859
|
const lightness = Math.round(rgbToOklch(parseColor(seed).rgb).L * 10) / 10;
|
|
8807
|
-
|
|
8860
|
+
const seedHex = convertRgbToHex(parseColor(seed).rgb);
|
|
8861
|
+
const chip = swatch(seedHex, 3);
|
|
8862
|
+
p.write(` ${chip ? `${chip} ` : ""}${dim(`${seedHex} · lightness ${lightness}% — lands at ≈${nearestLadderStep(lightness)} on the ladder`)}`);
|
|
8808
8863
|
p.write();
|
|
8809
8864
|
const manual = Boolean(given.manual);
|
|
8810
8865
|
let brandCount = 2;
|
|
@@ -8827,7 +8882,14 @@ async function promptCreateAnswers(p, given = {}) {
|
|
|
8827
8882
|
const options = schemesFor(brandCount);
|
|
8828
8883
|
scheme = (_c = given.scheme) !== null && _c !== void 0 ? _c : defaultSchemeFor(brandCount);
|
|
8829
8884
|
if (!given.scheme && options.length > 1) {
|
|
8830
|
-
|
|
8885
|
+
// Show the hues each scheme actually produces. "split-complement" is a word; two blocks of
|
|
8886
|
+
// real colour beside the seed is the thing you're choosing between.
|
|
8887
|
+
scheme = await p.select("Harmony scheme", options.map(s => ({
|
|
8888
|
+
value: s,
|
|
8889
|
+
label: s,
|
|
8890
|
+
hint: SCHEME_HINTS[s],
|
|
8891
|
+
swatches: [seedHex, ...schemeHues(seedHex, s)],
|
|
8892
|
+
})), Math.max(0, options.indexOf(scheme)));
|
|
8831
8893
|
}
|
|
8832
8894
|
if (scheme && brandCount > 1) {
|
|
8833
8895
|
p.write(` ${dim(`→ ${scheme} · each member becomes its own palette with a full ladder`)}`);
|