@rojaostudio/ds-core 1.1.0-next.8 → 1.1.0-next.9
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/LICENSE +11 -0
- package/dist/{chunk-6XIFYDSU.js → chunk-N3HPD3U5.js} +162 -12
- package/dist/generate.d.ts +26 -1
- package/dist/generate.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/themes.js +1 -1
- package/package.json +1 -1
package/LICENSE
CHANGED
|
@@ -19,3 +19,14 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
19
19
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
20
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
21
|
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
Arquivos gerados
|
|
26
|
+
|
|
27
|
+
Os arquivos que esta ferramenta escreve no seu projeto (o tema CSS e o bloco
|
|
28
|
+
entre os marcadores <!-- rojao-ds:start --> e <!-- rojao-ds:end --> em
|
|
29
|
+
CLAUDE.md, AGENTS.md ou .cursorrules) são de quem usa a ferramenta. Você pode
|
|
30
|
+
usá-los, alterá-los e distribuí-los como quiser, sem obrigação de manter aviso
|
|
31
|
+
de licença ou atribuição. A licença MIT acima vale para o código da ferramenta,
|
|
32
|
+
não para o que ela gera.
|
|
@@ -675,12 +675,114 @@ function resolveTheme(def, opts) {
|
|
|
675
675
|
return { tokens: isDark ? __spreadValues(__spreadValues({}, light2), dark) : light2, isDark };
|
|
676
676
|
}
|
|
677
677
|
|
|
678
|
+
// generate/validate.ts
|
|
679
|
+
var HEX = /^#[0-9a-f]{3,8}$/i;
|
|
680
|
+
var CH = String.raw`(?:[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?(?:%|deg)?|none)`;
|
|
681
|
+
var COLOR_FN = new RegExp(String.raw`^(?:rgba?|oklch)\(\s*${CH}(?:\s*[,/]?\s*${CH}){2,3}\s*\)$`, "i");
|
|
682
|
+
var VAR_NAME = /^[a-z0-9]+(?:[/-][a-z0-9]+)*$/;
|
|
683
|
+
var FONT_BAD = /["';{}\\<>\u0000-\u001f\u007f]/;
|
|
684
|
+
var isSafeColor = (v) => typeof v === "string" && (HEX.test(v) || COLOR_FN.test(v));
|
|
685
|
+
var isSafeVarName = (v) => typeof v === "string" && VAR_NAME.test(v);
|
|
686
|
+
var isSafeFont = (v) => typeof v === "string" && v.trim().length > 0 && v.length <= 100 && !FONT_BAD.test(v);
|
|
687
|
+
var isSafeCustomProperty = (k) => k.startsWith("--") && isSafeVarName(k.slice(2));
|
|
688
|
+
var FONT_ROLE_VAR = "--type-font-mono";
|
|
689
|
+
function preview(v) {
|
|
690
|
+
var _a;
|
|
691
|
+
const s = typeof v === "string" ? v : (_a = JSON.stringify(v)) != null ? _a : String(v);
|
|
692
|
+
const one = s.replace(/[\r\n\t]+/g, " ");
|
|
693
|
+
return JSON.stringify(one.length > 60 ? `${one.slice(0, 60)}\u2026` : one);
|
|
694
|
+
}
|
|
695
|
+
var RdsValidationError = class extends Error {
|
|
696
|
+
constructor(where, problems) {
|
|
697
|
+
super(`${where}: invalid input
|
|
698
|
+
${problems.join("\n ")}`);
|
|
699
|
+
this.problems = problems;
|
|
700
|
+
this.name = "RdsValidationError";
|
|
701
|
+
}
|
|
702
|
+
};
|
|
703
|
+
function collector(where) {
|
|
704
|
+
const problems = [];
|
|
705
|
+
return {
|
|
706
|
+
problems,
|
|
707
|
+
color(field, v) {
|
|
708
|
+
if (!isSafeColor(v)) problems.push(`${field}: ${preview(v)} is not a colour (hex, rgb(), rgba() or oklch())`);
|
|
709
|
+
},
|
|
710
|
+
name(field, v) {
|
|
711
|
+
if (!isSafeVarName(v)) problems.push(`${field}: ${preview(v)} is not a valid name (lowercase letters, digits, "-" and "/")`);
|
|
712
|
+
},
|
|
713
|
+
font(field, v) {
|
|
714
|
+
if (!isSafeFont(v)) problems.push(`${field}: ${preview(v)} is not a valid font family (no quotes, ";", braces, "\\" or line breaks)`);
|
|
715
|
+
},
|
|
716
|
+
done() {
|
|
717
|
+
if (problems.length) throw new RdsValidationError(where, problems);
|
|
718
|
+
}
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
function assertSafeTheme(theme, where) {
|
|
722
|
+
const c = collector(where);
|
|
723
|
+
if (!theme || typeof theme !== "object") {
|
|
724
|
+
c.problems.push("the theme is not an object");
|
|
725
|
+
c.done();
|
|
726
|
+
}
|
|
727
|
+
const check = (label, map, fontRole) => {
|
|
728
|
+
if (!map || typeof map !== "object" || Array.isArray(map)) {
|
|
729
|
+
c.problems.push(`${label}: not an object`);
|
|
730
|
+
return;
|
|
731
|
+
}
|
|
732
|
+
for (const [k, v] of Object.entries(map)) {
|
|
733
|
+
if (!isSafeCustomProperty(k)) {
|
|
734
|
+
c.problems.push(`${label}: ${preview(k)} is not a valid CSS variable name`);
|
|
735
|
+
continue;
|
|
736
|
+
}
|
|
737
|
+
if (fontRole && k === FONT_ROLE_VAR) c.font(`${label}.${k}`, v);
|
|
738
|
+
else c.color(`${label}.${k}`, v);
|
|
739
|
+
}
|
|
740
|
+
};
|
|
741
|
+
for (const mode of ["light", "dark", "brand"]) check(mode, theme[mode], true);
|
|
742
|
+
if (theme.vars !== void 0) check("vars", theme.vars, false);
|
|
743
|
+
c.done();
|
|
744
|
+
}
|
|
745
|
+
var isSafeSelectorList = (v) => (
|
|
746
|
+
// eslint-disable-next-line no-control-regex -- control characters are exactly what is refused
|
|
747
|
+
typeof v === "string" && v.trim().length > 0 && !/[{};\\<@\u0000-\u001f\u007f]|\/\*|\*\//.test(v)
|
|
748
|
+
);
|
|
749
|
+
function oneLine(v, max = 200) {
|
|
750
|
+
if (typeof v !== "string") return "";
|
|
751
|
+
let s = v.replace(/[\u0000-\u001f\u007f\u2028\u2029]+/g, " ").replace(/[<>`]/g, "").replace(/rojao-ds:(start|end)/gi, "").replace(/\s+/g, " ").trim();
|
|
752
|
+
if (s.length > max) s = `${s.slice(0, max - 1).trimEnd()}\u2026`;
|
|
753
|
+
return s;
|
|
754
|
+
}
|
|
755
|
+
|
|
678
756
|
// generate/rdsTheme.ts
|
|
679
757
|
var palettes = primitives.color;
|
|
680
758
|
var WHITE = "#ffffff";
|
|
681
759
|
var BLACK = "#000000";
|
|
682
760
|
var black = (pct) => BLACK + Math.round(pct / 100 * 255).toString(16).padStart(2, "0");
|
|
683
761
|
var white = (pct) => WHITE + Math.round(pct / 100 * 255).toString(16).padStart(2, "0");
|
|
762
|
+
var PALETTE_STEPS2 = /* @__PURE__ */ new Set([...SCALE_STEPS.map(String), "950"]);
|
|
763
|
+
function checkPalettes(custom, where) {
|
|
764
|
+
if (custom === void 0) return;
|
|
765
|
+
const c = collector(where);
|
|
766
|
+
if (!custom || typeof custom !== "object" || Array.isArray(custom)) {
|
|
767
|
+
c.problems.push("palettes: not an object");
|
|
768
|
+
c.done();
|
|
769
|
+
}
|
|
770
|
+
for (const [name, value] of Object.entries(custom)) {
|
|
771
|
+
if (!/^[a-z][a-z0-9]*$/i.test(name)) {
|
|
772
|
+
c.problems.push(`palettes: ${preview(name)} is not a valid palette name (letters and digits)`);
|
|
773
|
+
continue;
|
|
774
|
+
}
|
|
775
|
+
if (typeof value === "string") {
|
|
776
|
+
if (!isHex(value)) c.problems.push(`palettes.${name}: ${preview(value)} is not a hex colour`);
|
|
777
|
+
} else if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
778
|
+
for (const [step2, v] of Object.entries(value)) {
|
|
779
|
+
if (!PALETTE_STEPS2.has(step2)) c.problems.push(`palettes.${name}: ${preview(step2)} is not a scale step (50\u2013900)`);
|
|
780
|
+
else c.color(`palettes.${name}.${step2}`, v);
|
|
781
|
+
}
|
|
782
|
+
} else c.problems.push(`palettes.${name}: expected a hex or a scale of steps 50\u2013900`);
|
|
783
|
+
}
|
|
784
|
+
c.done();
|
|
785
|
+
}
|
|
684
786
|
function palette(name, custom) {
|
|
685
787
|
const own = custom == null ? void 0 : custom[name];
|
|
686
788
|
if (typeof own === "string") {
|
|
@@ -839,6 +941,9 @@ var roleVar = (role) => `--${role.replaceAll("/", "-")}`;
|
|
|
839
941
|
function generateRdsTheme(def, opts = {}) {
|
|
840
942
|
var _a, _b;
|
|
841
943
|
const warn = (_a = opts.warn) != null ? _a : defaultWarn;
|
|
944
|
+
const where = `generateRdsTheme(${preview(def == null ? void 0 : def.name)})`;
|
|
945
|
+
if (!(def == null ? void 0 : def.brand) || typeof def.brand !== "object") throw new Error(`${where}: brand is missing`);
|
|
946
|
+
checkPalettes(def.palettes, where);
|
|
842
947
|
const b = def.brand;
|
|
843
948
|
const own = def.palettes;
|
|
844
949
|
const P = ramp(b.primary, own);
|
|
@@ -1117,6 +1222,7 @@ function generateRdsTheme(def, opts = {}) {
|
|
|
1117
1222
|
out.brand[roleVar(role)] = pick(brandSrc, role);
|
|
1118
1223
|
}
|
|
1119
1224
|
out.brand[roleVar("border/error")] = brandError;
|
|
1225
|
+
assertSafeTheme(out, where);
|
|
1120
1226
|
return out;
|
|
1121
1227
|
}
|
|
1122
1228
|
var RDS_SCOPE_SELECTORS = [
|
|
@@ -1142,6 +1248,12 @@ var covered = (sel) => {
|
|
|
1142
1248
|
};
|
|
1143
1249
|
function emitRdsCss(theme, opts = {}) {
|
|
1144
1250
|
var _a, _b, _c;
|
|
1251
|
+
assertSafeTheme(theme, "emitRdsCss");
|
|
1252
|
+
const badSel = ["scope", "dark", "plate"].filter((k) => opts[k] !== void 0 && !isSafeSelectorList(opts[k]));
|
|
1253
|
+
if (badSel.length)
|
|
1254
|
+
throw new Error(
|
|
1255
|
+
`emitRdsCss: invalid selector in ${badSel.map((k) => `${k} ${preview(opts[k])}`).join(", ")} (no braces, ";", comments, backslash, "@", "<" or line breaks).`
|
|
1256
|
+
);
|
|
1145
1257
|
const scopes = split((_a = opts.scope) != null ? _a : ":root, .ds-scope, [data-rds-scope]");
|
|
1146
1258
|
const darks = split((_b = opts.dark) != null ? _b : '.dark, [data-rds-mode="dark"]');
|
|
1147
1259
|
const plates = split((_c = opts.plate) != null ? _c : ".ds-plate, [data-rds-plate]");
|
|
@@ -1214,6 +1326,25 @@ var ADDED_ROLES = {
|
|
|
1214
1326
|
};
|
|
1215
1327
|
function rdsThemeFromTable(table, opts = {}) {
|
|
1216
1328
|
var _a, _b, _c;
|
|
1329
|
+
const where = `rdsThemeFromTable(${preview(table == null ? void 0 : table.name)})`;
|
|
1330
|
+
if (!table || typeof table !== "object") throw new Error(`${where}: the table is not an object`);
|
|
1331
|
+
const check = collector(where);
|
|
1332
|
+
if (!table.primitives || typeof table.primitives !== "object" || Array.isArray(table.primitives))
|
|
1333
|
+
check.problems.push("primitives: not an object");
|
|
1334
|
+
else
|
|
1335
|
+
for (const [name, value] of Object.entries(table.primitives)) {
|
|
1336
|
+
check.name(`primitives ${preview(name)}`, name);
|
|
1337
|
+
check.color(`primitives.${name}`, value);
|
|
1338
|
+
}
|
|
1339
|
+
if (table.vars !== void 0) {
|
|
1340
|
+
if (!table.vars || typeof table.vars !== "object" || Array.isArray(table.vars)) check.problems.push("vars: not an object");
|
|
1341
|
+
else
|
|
1342
|
+
for (const [name, value] of Object.entries(table.vars)) {
|
|
1343
|
+
check.name(`vars ${preview(name)}`, name);
|
|
1344
|
+
if (typeof value !== "string") check.problems.push(`vars.${name}: not a string`);
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
check.done();
|
|
1217
1348
|
const out = { light: {}, dark: {}, brand: {} };
|
|
1218
1349
|
const problems = [];
|
|
1219
1350
|
const derived = /* @__PURE__ */ new Set();
|
|
@@ -1238,10 +1369,10 @@ function rdsThemeFromTable(table, opts = {}) {
|
|
|
1238
1369
|
} else out[mode][roleVar(role)] = ref2;
|
|
1239
1370
|
}
|
|
1240
1371
|
}
|
|
1241
|
-
const valueOf = (
|
|
1372
|
+
const valueOf = (field, ref2) => {
|
|
1242
1373
|
if (!isColourRef(ref2)) return ref2;
|
|
1243
1374
|
const value = table.primitives[ref2];
|
|
1244
|
-
if (value === void 0) problems.push(`${
|
|
1375
|
+
if (value === void 0) problems.push(`${field} points to unknown primitive "${ref2}"`);
|
|
1245
1376
|
return value == null ? void 0 : value.toLowerCase();
|
|
1246
1377
|
};
|
|
1247
1378
|
if (table.vars) {
|
|
@@ -1251,17 +1382,18 @@ function rdsThemeFromTable(table, opts = {}) {
|
|
|
1251
1382
|
if (value !== void 0) out.vars[roleVar(name)] = value;
|
|
1252
1383
|
}
|
|
1253
1384
|
}
|
|
1254
|
-
if (problems.length) throw new Error(
|
|
1385
|
+
if (problems.length) throw new Error(`${where}:
|
|
1255
1386
|
${problems.join("\n ")}`);
|
|
1387
|
+
assertSafeTheme(out, where);
|
|
1256
1388
|
const warn = (_c = opts.warn) != null ? _c : defaultWarn;
|
|
1257
1389
|
if (derived.size)
|
|
1258
1390
|
warn(
|
|
1259
|
-
|
|
1391
|
+
`${where}: the table was exported before ${[...derived].map((r) => `"${r}"`).join(", ")} existed in the [RDS] theme. Taken from the tokens Figma aliases it to (` + [...derived].map((r) => Object.entries(ADDED_ROLES[r]).map(([m, src]) => `${m}: ${src}`).join(", ")).join("; ") + `). Export the table again with figma/export-brand.js.`
|
|
1260
1392
|
);
|
|
1261
1393
|
const fails = rdsContrastReport(out);
|
|
1262
1394
|
if (fails.length)
|
|
1263
1395
|
warn(
|
|
1264
|
-
|
|
1396
|
+
`${where}: ${fails.length} text pair(s) below 4.5:1
|
|
1265
1397
|
` + fails.map((f) => `${f.mode}: ${f.fg} on ${f.bg} ${f.ratio}:1`).join("\n ")
|
|
1266
1398
|
);
|
|
1267
1399
|
return out;
|
|
@@ -1338,6 +1470,11 @@ ${lightBlock}${darkBlock}
|
|
|
1338
1470
|
}
|
|
1339
1471
|
|
|
1340
1472
|
// generate/emitClaudeMd.ts
|
|
1473
|
+
var MARKERS = /rojao-ds:(start|end)/i;
|
|
1474
|
+
var NAME = /^[a-z0-9][a-z0-9 _-]{0,63}$/i;
|
|
1475
|
+
var CSS_FILE = /^[a-z0-9_@./-]+\.css$/i;
|
|
1476
|
+
var CSS_URL = /^https?:\/\/[^\s"'`<>()\\]+$/i;
|
|
1477
|
+
var INSTALL = /^(?:pnpm add|yarn add|bun add|npm install|npm i) @rojaostudio\/ds(?:@[a-z0-9][a-z0-9.+-]*)?$/;
|
|
1341
1478
|
var ROLE_DOCS = [
|
|
1342
1479
|
{ role: "colors/primary/default", use: "primary action, brand fills" },
|
|
1343
1480
|
{ role: "text/on/primary", use: "text/icon on the primary fill" },
|
|
@@ -1397,8 +1534,9 @@ var CLAUDE_MD_FOUNDATION = {
|
|
|
1397
1534
|
function isTable(x) {
|
|
1398
1535
|
return typeof x === "object" && x !== null && "modes" in x && "primitives" in x;
|
|
1399
1536
|
}
|
|
1537
|
+
var TARGET_FILES = { claude: "CLAUDE.md", cursor: ".cursorrules", agents: "AGENTS.md" };
|
|
1400
1538
|
function fileLabel(target) {
|
|
1401
|
-
return target
|
|
1539
|
+
return TARGET_FILES[target];
|
|
1402
1540
|
}
|
|
1403
1541
|
function roleTable(theme) {
|
|
1404
1542
|
const rows = ROLE_DOCS.filter((r) => roleVar(r.role) in theme.light).map((r) => {
|
|
@@ -1409,19 +1547,29 @@ function roleTable(theme) {
|
|
|
1409
1547
|
return ["| Token | Use for | Light | Dark |", "|---|---|---|---|", ...rows].join("\n");
|
|
1410
1548
|
}
|
|
1411
1549
|
function emitClaudeMd(source, opts = {}) {
|
|
1412
|
-
var _a, _b, _c;
|
|
1550
|
+
var _a, _b, _c, _d;
|
|
1413
1551
|
const theme = (_a = opts.theme) != null ? _a : isTable(source) ? rdsThemeFromTable(source) : generateRdsTheme(source);
|
|
1552
|
+
assertSafeTheme(theme, "emitClaudeMd");
|
|
1414
1553
|
const name = source.name;
|
|
1554
|
+
if (name !== void 0 && name !== "" && !(typeof name === "string" && NAME.test(name)))
|
|
1555
|
+
throw new Error(`emitClaudeMd: name ${preview(name)} is not valid (letters, digits, space, "-" and "_", up to 64)`);
|
|
1415
1556
|
const Name = name ? name[0].toUpperCase() + name.slice(1) : "Brand";
|
|
1416
|
-
const
|
|
1557
|
+
const desc = !isTable(source) ? oneLine(source.description) : "";
|
|
1558
|
+
const description = desc ? ` \u2014 ${desc}` : "";
|
|
1417
1559
|
const cssFile = (_b = opts.cssFile) != null ? _b : "rds-theme.css";
|
|
1418
|
-
|
|
1560
|
+
if (!CSS_FILE.test(cssFile)) throw new Error(`emitClaudeMd: cssFile ${preview(cssFile)} is not a valid .css path`);
|
|
1561
|
+
if (opts.cssUrl !== void 0 && !CSS_URL.test(opts.cssUrl))
|
|
1562
|
+
throw new Error(`emitClaudeMd: cssUrl ${preview(opts.cssUrl)} is not a valid http(s) URL`);
|
|
1563
|
+
const install = (_c = opts.install) != null ? _c : "pnpm add @rojaostudio/ds";
|
|
1564
|
+
if (!INSTALL.test(install)) throw new Error(`emitClaudeMd: install ${preview(install)} is not an install command of @rojaostudio/ds`);
|
|
1565
|
+
const target = (_d = opts.target) != null ? _d : "claude";
|
|
1566
|
+
if (!Object.prototype.hasOwnProperty.call(TARGET_FILES, target)) throw new Error(`emitClaudeMd: unknown target ${preview(target)}`);
|
|
1419
1567
|
const f = CLAUDE_MD_FOUNDATION;
|
|
1420
1568
|
const own = theme.vars ? Object.keys(theme.vars) : [];
|
|
1421
1569
|
const themeImport = opts.cssUrl ? `@import "@rojaostudio/ds/styles/rds.css";
|
|
1422
1570
|
@import url("${opts.cssUrl}"); /* the brand theme, AFTER rds.css */` : `@import "@rojaostudio/ds/styles/rds.css";
|
|
1423
1571
|
@import "./${cssFile}"; /* the brand theme, AFTER rds.css */`;
|
|
1424
|
-
|
|
1572
|
+
const text = `# Design System \u2014 ${Name}
|
|
1425
1573
|
|
|
1426
1574
|
> This project uses **Roj\xE3o DS 2.0** with the **${Name}** brand${description}. When you build any
|
|
1427
1575
|
> UI \u2014 components, pages, screens \u2014 follow the rules below so every screen looks like the same
|
|
@@ -1429,7 +1577,7 @@ function emitClaudeMd(source, opts = {}) {
|
|
|
1429
1577
|
|
|
1430
1578
|
## Setup (once per app)
|
|
1431
1579
|
\`\`\`bash
|
|
1432
|
-
|
|
1580
|
+
${install}
|
|
1433
1581
|
\`\`\`
|
|
1434
1582
|
\`\`\`css
|
|
1435
1583
|
/* root stylesheet */
|
|
@@ -1489,6 +1637,8 @@ Brand's own variables (same value in every mode): ${own.map((v) => `\`${v}\``).j
|
|
|
1489
1637
|
|
|
1490
1638
|
<!-- Generated by Roj\xE3o DS (npx rojao-ds init) \u2014 file: ${fileLabel(target)}. Claude Code, Cursor and other agents read it from the project root. -->
|
|
1491
1639
|
`;
|
|
1640
|
+
if (MARKERS.test(text)) throw new Error("emitClaudeMd: the generated text contains a rojao-ds marker");
|
|
1641
|
+
return text;
|
|
1492
1642
|
}
|
|
1493
1643
|
|
|
1494
|
-
export { RDS_CONTRAST_PAIRS, RDS_SCOPE_SELECTORS, RDS_TOKEN_SCOPE, ROLES, SCALE_STEPS, brandTones, buildScale, contrastRatio, emitClaudeMd, emitCss, emitRdsCss, generateRdsTheme, generateTheme, hexToHsl, hslToHex, isHex, isNeutralBrand, onColor, rdsContrast, rdsContrastReport, rdsThemeFromTable, refToHex, relativeLuminance, resolveTheme, roleVar };
|
|
1644
|
+
export { RDS_CONTRAST_PAIRS, RDS_SCOPE_SELECTORS, RDS_TOKEN_SCOPE, ROLES, RdsValidationError, SCALE_STEPS, brandTones, buildScale, contrastRatio, emitClaudeMd, emitCss, emitRdsCss, generateRdsTheme, generateTheme, hexToHsl, hslToHex, isHex, isNeutralBrand, isSafeColor, isSafeFont, isSafeVarName, onColor, rdsContrast, rdsContrastReport, rdsThemeFromTable, refToHex, relativeLuminance, resolveTheme, roleVar };
|
package/dist/generate.d.ts
CHANGED
|
@@ -91,6 +91,26 @@ type RdsBrandTable = {
|
|
|
91
91
|
*/
|
|
92
92
|
declare function rdsThemeFromTable(table: RdsBrandTable, opts?: RdsThemeOptions): RdsTheme;
|
|
93
93
|
|
|
94
|
+
/**
|
|
95
|
+
* validate.ts — what the emitters accept from the outside (a recipe, a brand table, a theme object).
|
|
96
|
+
*
|
|
97
|
+
* ds-core is a public API, and its output lands in a stylesheet (`emitRdsCss`) and in the rules file an AI agent
|
|
98
|
+
* reads (`emitClaudeMd`). A value that is not a colour could close the CSS rule and open another one, pull an
|
|
99
|
+
* `url()`, or carry an instruction into CLAUDE.md. So every value is checked against an allow list before it is
|
|
100
|
+
* written, and the check runs again in each emitter (defence in depth: a theme object can be built by hand).
|
|
101
|
+
*
|
|
102
|
+
* Allow lists, never deny lists: a colour is a hex or a closed `rgb()/rgba()/oklch()`; a variable name is lowercase
|
|
103
|
+
* words joined by `-` or `/`; a font family has no quote, semicolon, brace, backslash or line break.
|
|
104
|
+
*/
|
|
105
|
+
declare const isSafeColor: (v: unknown) => v is string;
|
|
106
|
+
declare const isSafeVarName: (v: unknown) => v is string;
|
|
107
|
+
declare const isSafeFont: (v: unknown) => v is string;
|
|
108
|
+
/** Thrown when an input fails the allow list. The message lists every field that failed. */
|
|
109
|
+
declare class RdsValidationError extends Error {
|
|
110
|
+
readonly problems: string[];
|
|
111
|
+
constructor(where: string, problems: string[]);
|
|
112
|
+
}
|
|
113
|
+
|
|
94
114
|
/**
|
|
95
115
|
* emitCss.ts — formata o output do generateTheme num theme.css legível.
|
|
96
116
|
* Header + bloco light (.theme-<name>) + bloco dark (seletor duplo), agrupado
|
|
@@ -124,6 +144,11 @@ interface ClaudeMdOptions {
|
|
|
124
144
|
theme?: RdsTheme;
|
|
125
145
|
/** File the text is written to (only the footer changes). Default `claude`. */
|
|
126
146
|
target?: ClaudeMdTarget;
|
|
147
|
+
/**
|
|
148
|
+
* The install command shown in the setup, as the consumer's package manager runs it
|
|
149
|
+
* (`npm install @rojaostudio/ds@next`). Default `pnpm add @rojaostudio/ds`.
|
|
150
|
+
*/
|
|
151
|
+
install?: string;
|
|
127
152
|
}
|
|
128
153
|
declare function emitClaudeMd(source: BrandDef | RdsBrandTable, opts?: ClaudeMdOptions): string;
|
|
129
154
|
|
|
@@ -193,4 +218,4 @@ declare function isNeutralBrand(def: {
|
|
|
193
218
|
palettes?: PaletteDefs;
|
|
194
219
|
}): boolean;
|
|
195
220
|
|
|
196
|
-
export { BrandDef, type ClaudeMdOptions, type ClaudeMdTarget, RDS_CONTRAST_PAIRS, ROLES as RDS_ROLES, RDS_SCOPE_SELECTORS, RDS_TOKEN_SCOPE, type RdsBrandTable, type RdsContrastFailure, type RdsCssOptions, type RdsMode, type RdsTheme, type RdsThemeOptions, SCALE_STEPS, type Scale, type ScaleStep, brandTones, buildScale, contrastRatio, emitClaudeMd, emitCss, emitRdsCss, generateRdsTheme, hexToHsl, hslToHex, isHex, isNeutralBrand, onColor, rdsContrast, rdsContrastReport, rdsThemeFromTable, refToHex, relativeLuminance, roleVar };
|
|
221
|
+
export { BrandDef, type ClaudeMdOptions, type ClaudeMdTarget, RDS_CONTRAST_PAIRS, ROLES as RDS_ROLES, RDS_SCOPE_SELECTORS, RDS_TOKEN_SCOPE, type RdsBrandTable, type RdsContrastFailure, type RdsCssOptions, type RdsMode, type RdsTheme, type RdsThemeOptions, RdsValidationError, SCALE_STEPS, type Scale, type ScaleStep, brandTones, buildScale, contrastRatio, emitClaudeMd, emitCss, emitRdsCss, generateRdsTheme, hexToHsl, hslToHex, isHex, isNeutralBrand, isSafeColor, isSafeFont, isSafeVarName, onColor, rdsContrast, rdsContrastReport, rdsThemeFromTable, refToHex, relativeLuminance, roleVar };
|
package/dist/generate.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { RDS_CONTRAST_PAIRS, ROLES as RDS_ROLES, RDS_SCOPE_SELECTORS, RDS_TOKEN_SCOPE, SCALE_STEPS, brandTones, buildScale, contrastRatio, emitClaudeMd, emitCss, emitRdsCss, generateRdsTheme, generateTheme, hexToHsl, hslToHex, isHex, isNeutralBrand, onColor, rdsContrast, rdsContrastReport, rdsThemeFromTable, refToHex, relativeLuminance, resolveTheme, roleVar } from './chunk-
|
|
1
|
+
export { RDS_CONTRAST_PAIRS, ROLES as RDS_ROLES, RDS_SCOPE_SELECTORS, RDS_TOKEN_SCOPE, RdsValidationError, SCALE_STEPS, brandTones, buildScale, contrastRatio, emitClaudeMd, emitCss, emitRdsCss, generateRdsTheme, generateTheme, hexToHsl, hslToHex, isHex, isNeutralBrand, isSafeColor, isSafeFont, isSafeVarName, onColor, rdsContrast, rdsContrastReport, rdsThemeFromTable, refToHex, relativeLuminance, resolveTheme, roleVar } from './chunk-N3HPD3U5.js';
|
|
2
2
|
import './chunk-FRWRLCYM.js';
|
|
3
3
|
import './chunk-ORMEWXMH.js';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { G as GenResult, R as ResolvedTheme, T as TokenMap, g as generateTheme, r as resolveTheme } from './generateTheme-CUfNpsKs.js';
|
|
2
|
-
export { ClaudeMdOptions, ClaudeMdTarget, RDS_CONTRAST_PAIRS, RDS_ROLES, RDS_SCOPE_SELECTORS, RDS_TOKEN_SCOPE, RdsBrandTable, RdsContrastFailure, RdsCssOptions, RdsMode, RdsTheme, RdsThemeOptions, SCALE_STEPS, Scale, ScaleStep, brandTones, buildScale, contrastRatio, emitClaudeMd, emitCss, emitRdsCss, generateRdsTheme, hexToHsl, hslToHex, isHex, isNeutralBrand, onColor, rdsContrast, rdsContrastReport, rdsThemeFromTable, refToHex, relativeLuminance, roleVar } from './generate.js';
|
|
2
|
+
export { ClaudeMdOptions, ClaudeMdTarget, RDS_CONTRAST_PAIRS, RDS_ROLES, RDS_SCOPE_SELECTORS, RDS_TOKEN_SCOPE, RdsBrandTable, RdsContrastFailure, RdsCssOptions, RdsMode, RdsTheme, RdsThemeOptions, RdsValidationError, SCALE_STEPS, Scale, ScaleStep, brandTones, buildScale, contrastRatio, emitClaudeMd, emitCss, emitRdsCss, generateRdsTheme, hexToHsl, hslToHex, isHex, isNeutralBrand, isSafeColor, isSafeFont, isSafeVarName, onColor, rdsContrast, rdsContrastReport, rdsThemeFromTable, refToHex, relativeLuminance, roleVar } from './generate.js';
|
|
3
3
|
export { A as Archetype, B as BrandColors, a as BrandDef, C as ColorRef, D as DarkStrategy, F as FontDef, P as PaletteRef, S as ScaleToken, T as TextStyle, b as TextStyleName, c as TypeScale } from './recipe.schema-Cq7e82nx.js';
|
|
4
4
|
export { primitives, tokens } from './tokens.js';
|
|
5
5
|
export { RecipeName, recipes } from './recipes.js';
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { recipes } from './chunk-PV6GFEAM.js';
|
|
2
|
-
export { RDS_CONTRAST_PAIRS, ROLES as RDS_ROLES, RDS_SCOPE_SELECTORS, RDS_TOKEN_SCOPE, SCALE_STEPS, brandTones, buildScale, contrastRatio, emitClaudeMd, emitCss, emitRdsCss, generateRdsTheme, generateTheme, hexToHsl, hslToHex, isHex, isNeutralBrand, onColor, rdsContrast, rdsContrastReport, rdsThemeFromTable, refToHex, relativeLuminance, resolveTheme, roleVar } from './chunk-
|
|
2
|
+
export { RDS_CONTRAST_PAIRS, ROLES as RDS_ROLES, RDS_SCOPE_SELECTORS, RDS_TOKEN_SCOPE, RdsValidationError, SCALE_STEPS, brandTones, buildScale, contrastRatio, emitClaudeMd, emitCss, emitRdsCss, generateRdsTheme, generateTheme, hexToHsl, hslToHex, isHex, isNeutralBrand, isSafeColor, isSafeFont, isSafeVarName, onColor, rdsContrast, rdsContrastReport, rdsThemeFromTable, refToHex, relativeLuminance, resolveTheme, roleVar } from './chunk-N3HPD3U5.js';
|
|
3
3
|
export { primitives, tokens } from './chunk-FRWRLCYM.js';
|
|
4
4
|
import './chunk-ORMEWXMH.js';
|
package/dist/themes.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rojaostudio/ds-core",
|
|
3
|
-
"version": "1.1.0-next.
|
|
3
|
+
"version": "1.1.0-next.9",
|
|
4
4
|
"description": "O motor do Rojão DS — tokens, derivação de tema e emissores. Sem React, sem Tailwind, sem CSS.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Rojão Studio (https://rojao.studio)",
|