@theme-registry/refract 0.1.4 → 0.1.6
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 +85 -9
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -9,6 +9,7 @@ var node_url = require('node:url');
|
|
|
9
9
|
var node_os = require('node:os');
|
|
10
10
|
var node_readline = require('node:readline');
|
|
11
11
|
|
|
12
|
+
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
12
13
|
/**
|
|
13
14
|
* Build-layer path + transpile helpers (Node-only).
|
|
14
15
|
*
|
|
@@ -21,8 +22,21 @@ var node_readline = require('node:readline');
|
|
|
21
22
|
* and this module resolves them against the discovered package root — working the SAME way in-repo
|
|
22
23
|
* (root = repo root) and installed (root = the installed package dir).
|
|
23
24
|
*/
|
|
25
|
+
/**
|
|
26
|
+
* This module's own directory, in **both** bundle formats.
|
|
27
|
+
*
|
|
28
|
+
* `__dirname` only exists in the CJS build. The `./build` subpath also ships an ESM bundle, and
|
|
29
|
+
* defaulting to a bare `__dirname` made every ESM consumer of this layer throw `__dirname is not
|
|
30
|
+
* defined` the moment it reached package-root discovery — `runInit`, `runCreate`, `runSkillsInstall`.
|
|
31
|
+
* It went unnoticed because the only consumer was refract's own CLI, which is bundled as CJS.
|
|
32
|
+
*
|
|
33
|
+
* `typeof` guards rather than a feature test, because referencing an undeclared `__dirname` in ESM
|
|
34
|
+
* would be a ReferenceError. Rollup rewrites `import.meta.url` for the CJS output, so the fallback
|
|
35
|
+
* expression is valid in both.
|
|
36
|
+
*/
|
|
37
|
+
const moduleDir = () => typeof __dirname !== "undefined" ? __dirname : node_path.dirname(node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli.js', document.baseURI).href))));
|
|
24
38
|
/** Walk up from `startDir` to the first ancestor that contains a `package.json`. */
|
|
25
|
-
const findPackageRoot = (startDir =
|
|
39
|
+
const findPackageRoot = (startDir = moduleDir()) => {
|
|
26
40
|
let dir = startDir;
|
|
27
41
|
for (;;) {
|
|
28
42
|
if (node_fs.existsSync(node_path.join(dir, "package.json")))
|
|
@@ -8004,7 +8018,7 @@ const RATIO_VALUES = {
|
|
|
8004
8018
|
* overlap, so a scaffolded literal and an authored `harmony:` key agree. `pentadic` is local — the
|
|
8005
8019
|
* subsystem has no five-way scheme, and the scaffolder needs one to reach five brand colours.
|
|
8006
8020
|
*/
|
|
8007
|
-
const SCHEME_ROTATIONS = {
|
|
8021
|
+
const SCHEME_ROTATIONS$1 = {
|
|
8008
8022
|
complement: [180],
|
|
8009
8023
|
analogous: [-30, 30],
|
|
8010
8024
|
"split-complement": [150, 210],
|
|
@@ -8106,7 +8120,7 @@ const defaultSchemeFor = (brandCount) => {
|
|
|
8106
8120
|
return "pentadic";
|
|
8107
8121
|
};
|
|
8108
8122
|
/** 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);
|
|
8123
|
+
const schemesFor = (brandCount) => Object.keys(SCHEME_ROTATIONS$1).filter((s) => SCHEME_ROTATIONS$1[s].length === brandCount - 1);
|
|
8110
8124
|
/**
|
|
8111
8125
|
* The contrast gate. Builds the palette set, audits every text-on-base pairing, and walks the
|
|
8112
8126
|
* lightness of each failing colour down one OKLCH point at a time until it clears the bar.
|
|
@@ -8202,7 +8216,7 @@ function scaffoldTheme(answers) {
|
|
|
8202
8216
|
}
|
|
8203
8217
|
else if (brandCount > 1) {
|
|
8204
8218
|
const scheme = (_g = answers.scheme) !== null && _g !== void 0 ? _g : defaultSchemeFor(brandCount);
|
|
8205
|
-
const rotations = SCHEME_ROTATIONS[scheme];
|
|
8219
|
+
const rotations = SCHEME_ROTATIONS$1[scheme];
|
|
8206
8220
|
if (!rotations)
|
|
8207
8221
|
throw new Error(`Unknown harmony scheme "${scheme}".`);
|
|
8208
8222
|
rotations.slice(0, brandCount - 1).forEach((deg, i) => {
|
|
@@ -8460,6 +8474,35 @@ const cursorUp = (n) => (n > 0 ? `\u001b[${n}A` : "");
|
|
|
8460
8474
|
const CLEAR_DOWN = "\u001b[0J";
|
|
8461
8475
|
const HIDE_CURSOR = "\u001b[?25l";
|
|
8462
8476
|
const SHOW_CURSOR = "\u001b[?25h";
|
|
8477
|
+
/**
|
|
8478
|
+
* Does this terminal do 24-bit colour? `COLORTERM` is the only real signal, and plenty of capable
|
|
8479
|
+
* terminals (Apple Terminal among them) never set it — so a missing value means "approximate", not
|
|
8480
|
+
* "give up". Gating swatches on truecolor alone would silently delete them for those users.
|
|
8481
|
+
*/
|
|
8482
|
+
const truecolor = () => { var _a; return /truecolor|24bit/i.test((_a = process.env.COLORTERM) !== null && _a !== void 0 ? _a : ""); };
|
|
8483
|
+
/** Nearest xterm-256 colour-cube index for an RGB triple — the fallback when 24-bit isn't offered. */
|
|
8484
|
+
const to256 = (r, g, b) => {
|
|
8485
|
+
const axis = (v) => (v < 48 ? 0 : v < 114 ? 1 : Math.min(5, Math.round((v - 35) / 40)));
|
|
8486
|
+
return 16 + 36 * axis(r) + 6 * axis(g) + axis(b);
|
|
8487
|
+
};
|
|
8488
|
+
/**
|
|
8489
|
+
* `#rrggbb` → a coloured block: exact in 24-bit terminals, approximated in 256-colour ones, and
|
|
8490
|
+
* empty when there's no colour at all (a pipe, `NO_COLOR`) so redirected output stays clean.
|
|
8491
|
+
*
|
|
8492
|
+
* Approximating is right here — the swatch is a preview, and the exact value is written to the theme
|
|
8493
|
+
* file regardless. An approximately-right hue beats a blank space.
|
|
8494
|
+
*/
|
|
8495
|
+
function swatch(hex, width = 2) {
|
|
8496
|
+
if (!useColor())
|
|
8497
|
+
return "";
|
|
8498
|
+
const m = /^#?([0-9a-f]{6})$/i.exec(hex.trim());
|
|
8499
|
+
if (!m)
|
|
8500
|
+
return "";
|
|
8501
|
+
const n = parseInt(m[1], 16);
|
|
8502
|
+
const [r, g, b] = [(n >> 16) & 255, (n >> 8) & 255, n & 255];
|
|
8503
|
+
const bg = truecolor() ? `48;2;${r};${g};${b}` : `48;5;${to256(r, g, b)}`;
|
|
8504
|
+
return `\u001b[${bg}m${" ".repeat(width)}\u001b[0m`;
|
|
8505
|
+
}
|
|
8463
8506
|
/** Decode a raw stdin chunk into a {@link ListKey}. Arrows arrive as escape sequences; j/k mirror vim. */
|
|
8464
8507
|
function decodeKey(chunk) {
|
|
8465
8508
|
switch (chunk) {
|
|
@@ -8650,13 +8693,24 @@ class Prompter {
|
|
|
8650
8693
|
};
|
|
8651
8694
|
let painted = 0;
|
|
8652
8695
|
const help = multi ? "↑↓ move · space toggle · a all · enter confirm" : "↑↓ move · enter select";
|
|
8696
|
+
// Pad labels so the hints line up into a column — a ragged right edge is most of why a long
|
|
8697
|
+
// option list reads as noise.
|
|
8698
|
+
const widest = choices.reduce((w, c) => Math.max(w, c.label.length), 0);
|
|
8653
8699
|
const frame = () => {
|
|
8654
8700
|
const rows = choices.map((c, i) => {
|
|
8701
|
+
var _a;
|
|
8655
8702
|
const here = i === state.cursor;
|
|
8656
|
-
|
|
8703
|
+
// Focus and selection are SEPARATE signals and both need to be visible: the caret says
|
|
8704
|
+
// where you are, the box says what's chosen. Showing only one (as this did) leaves a
|
|
8705
|
+
// multi-select where you can't tell what the keys will act on.
|
|
8706
|
+
const caret = here ? green("❯") : " ";
|
|
8707
|
+
const box = multi ? (state.selected.has(i) ? green("[✓]") : dim("[ ]")) : "";
|
|
8657
8708
|
const name = here ? bold(c.label) : c.label;
|
|
8658
|
-
const
|
|
8659
|
-
|
|
8709
|
+
const pad = " ".repeat(Math.max(0, widest - c.label.length));
|
|
8710
|
+
const blocks = ((_a = c.swatches) !== null && _a !== void 0 ? _a : []).map(s => swatch(s)).filter(Boolean).join(" ");
|
|
8711
|
+
const art = blocks ? ` ${blocks}` : "";
|
|
8712
|
+
const hint = c.hint ? ` ${dim(c.hint)}` : "";
|
|
8713
|
+
return ` ${caret} ${box ? `${box} ` : ""}${name}${pad}${art}${hint}`;
|
|
8660
8714
|
});
|
|
8661
8715
|
return [`${cyan("?")} ${bold(label)} ${dim(help)}`, ...rows].join("\n");
|
|
8662
8716
|
};
|
|
@@ -8762,6 +8816,19 @@ class Prompter {
|
|
|
8762
8816
|
}
|
|
8763
8817
|
}
|
|
8764
8818
|
|
|
8819
|
+
/**
|
|
8820
|
+
* The hues a scheme derives from a seed — the same rotations the generator will bake, so the
|
|
8821
|
+
* preview beside each option is the palette you'd actually get, not an illustration.
|
|
8822
|
+
*/
|
|
8823
|
+
const SCHEME_ROTATIONS = {
|
|
8824
|
+
complement: [180],
|
|
8825
|
+
analogous: [-30, 30],
|
|
8826
|
+
"split-complement": [150, 210],
|
|
8827
|
+
triadic: [120, 240],
|
|
8828
|
+
tetradic: [90, 180, 270],
|
|
8829
|
+
pentadic: [72, 144, 216, 288],
|
|
8830
|
+
};
|
|
8831
|
+
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
8832
|
/** What each harmony scheme is for, in one line — the names mean nothing to most people. */
|
|
8766
8833
|
const SCHEME_HINTS = {
|
|
8767
8834
|
complement: "one companion, 180° opposite — maximum separation",
|
|
@@ -8804,7 +8871,9 @@ async function promptCreateAnswers(p, given = {}) {
|
|
|
8804
8871
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
8805
8872
|
const seed = (_a = given.seed) !== null && _a !== void 0 ? _a : (await p.text("Primary colour", "#4c6ef5", isColor));
|
|
8806
8873
|
const lightness = Math.round(rgbToOklch(parseColor(seed).rgb).L * 10) / 10;
|
|
8807
|
-
|
|
8874
|
+
const seedHex = convertRgbToHex(parseColor(seed).rgb);
|
|
8875
|
+
const chip = swatch(seedHex, 3);
|
|
8876
|
+
p.write(` ${chip ? `${chip} ` : ""}${dim(`${seedHex} · lightness ${lightness}% — lands at ≈${nearestLadderStep(lightness)} on the ladder`)}`);
|
|
8808
8877
|
p.write();
|
|
8809
8878
|
const manual = Boolean(given.manual);
|
|
8810
8879
|
let brandCount = 2;
|
|
@@ -8827,7 +8896,14 @@ async function promptCreateAnswers(p, given = {}) {
|
|
|
8827
8896
|
const options = schemesFor(brandCount);
|
|
8828
8897
|
scheme = (_c = given.scheme) !== null && _c !== void 0 ? _c : defaultSchemeFor(brandCount);
|
|
8829
8898
|
if (!given.scheme && options.length > 1) {
|
|
8830
|
-
|
|
8899
|
+
// Show the hues each scheme actually produces. "split-complement" is a word; two blocks of
|
|
8900
|
+
// real colour beside the seed is the thing you're choosing between.
|
|
8901
|
+
scheme = await p.select("Harmony scheme", options.map(s => ({
|
|
8902
|
+
value: s,
|
|
8903
|
+
label: s,
|
|
8904
|
+
hint: SCHEME_HINTS[s],
|
|
8905
|
+
swatches: [seedHex, ...schemeHues(seedHex, s)],
|
|
8906
|
+
})), Math.max(0, options.indexOf(scheme)));
|
|
8831
8907
|
}
|
|
8832
8908
|
if (scheme && brandCount > 1) {
|
|
8833
8909
|
p.write(` ${dim(`→ ${scheme} · each member becomes its own palette with a full ladder`)}`);
|