@scattered-light/loomiere 0.4.0 → 0.4.1

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.
Files changed (46) hide show
  1. package/figma/README.md +8 -7
  2. package/figma/library/README.md +92 -0
  3. package/figma/library/check/known-differences.md +20 -0
  4. package/figma/library/check/mojave.json +246 -0
  5. package/figma/library/kit/base.js +89 -0
  6. package/figma/library/kit/components.js +95 -0
  7. package/figma/library/kit/docs.js +37 -0
  8. package/figma/library/kit/fingerprint.js +143 -0
  9. package/figma/library/pages/02-palettes.js +40 -0
  10. package/figma/library/pages/03-color.js +126 -0
  11. package/figma/library/pages/04-type.js +56 -0
  12. package/figma/library/pages/05-scale.js +58 -0
  13. package/figma/library/pages/06-size.js +111 -0
  14. package/figma/library/pages/07-elevation-focus-motion.js +108 -0
  15. package/figma/library/pages/08-themes.js +36 -0
  16. package/figma/library/pages/09-fields/01-input.js +55 -0
  17. package/figma/library/pages/09-fields/02-checks.js +82 -0
  18. package/figma/library/pages/09-fields/03-field.js +32 -0
  19. package/figma/library/pages/09-fields/04-more-fields.js +144 -0
  20. package/figma/library/pages/09-fields/05-labels.js +14 -0
  21. package/figma/library/pages/09-fields/06-doc.js +75 -0
  22. package/figma/library/pages/10-buttons/01-buttons.js +97 -0
  23. package/figma/library/pages/10-buttons/02-doc.js +55 -0
  24. package/figma/library/pages/11-popups/01-popups.js +166 -0
  25. package/figma/library/pages/11-popups/02-more-popups.js +90 -0
  26. package/figma/library/pages/11-popups/03-labels.js +14 -0
  27. package/figma/library/pages/11-popups/04-doc.js +89 -0
  28. package/figma/library/pages/12-overlays/01-overlays.js +135 -0
  29. package/figma/library/pages/12-overlays/02-labels.js +10 -0
  30. package/figma/library/pages/12-overlays/03-doc.js +80 -0
  31. package/figma/library/pages/13-disclosure/01-disclosure.js +205 -0
  32. package/figma/library/pages/13-disclosure/02-collapsible.js +39 -0
  33. package/figma/library/pages/13-disclosure/03-labels.js +16 -0
  34. package/figma/library/pages/13-disclosure/04-doc.js +91 -0
  35. package/figma/library/pages/14-display/01-display.js +165 -0
  36. package/figma/library/pages/14-display/02-more-display.js +55 -0
  37. package/figma/library/pages/14-display/03-labels.js +14 -0
  38. package/figma/library/pages/14-display/04-doc.js +96 -0
  39. package/figma/library/pages/15-cover.js +87 -0
  40. package/figma/library/run-order.json +21 -0
  41. package/figma/library/tools/compare.mjs +11 -0
  42. package/figma/library/tools/diff-snippet.js +15 -0
  43. package/figma/library/tools/kit-script.mjs +21 -0
  44. package/figma/library/tools/run.mjs +27 -0
  45. package/figma/library/tools/runs.mjs +34 -0
  46. package/package.json +1 -1
@@ -0,0 +1,15 @@
1
+ // Diffs units on a real page against the same units on its check page. tools/run.mjs --diff
2
+ // sets PAGE, UNITS (['doc:Color/Text', 'set:Input', 'wrap:Input', ...]) and MAX.
3
+ var F = await new (Object.getPrototypeOf(async function () {}).constructor)('figma', figma.root.getSharedPluginData('loomiere', 'fingerprint'))(figma);
4
+ var PREFIX = figma.root.getSharedPluginData('loomiere', 'pagePrefix');
5
+ async function unitNode(name, key) {
6
+ var pg = figma.root.children.filter(function (p) { return p.name === name; })[0]; await pg.loadAsync();
7
+ var kind = key.slice(0, key.indexOf(':')), rest = key.slice(key.indexOf(':') + 1).replace(/#\d+$/, '');
8
+ if (kind === 'set') return pg.findAll(function (n) { return (n.type === 'COMPONENT_SET' || (n.type === 'COMPONENT' && n.parent.type !== 'COMPONENT_SET')) && n.name === rest; })[0];
9
+ if (kind === 'doc') { var d = rest.split('/'), top = pg.children.filter(function (n) { return n.name === d[0].replace('(frame)', ''); })[0]; return d.length > 1 ? top.children.filter(function (n) { return n.name === d.slice(1).join('/'); })[0] : top; }
10
+ if (kind === 'wrap') return pg.children.filter(function (n) { return n.name === 'Components'; })[0].children.filter(function (n) { return n.name === rest; })[0];
11
+ return pg.children.filter(function (n) { return n.name === rest.replace('(shell)', ''); })[0];
12
+ }
13
+ var out = {};
14
+ for (var i = 0; i < UNITS.length; i++) out[UNITS[i]] = await F.diff(await unitNode(PAGE, UNITS[i]), await unitNode(PREFIX + PAGE, UNITS[i]), MAX || 12);
15
+ return out;
@@ -0,0 +1,21 @@
1
+ // Prints the use_figma script that stores the library kit in a file's shared plugin data, and
2
+ // returns each stored part's length and hash, to compare with `node tools/kit-script.mjs --hashes`.
3
+ import fs from 'node:fs';
4
+ const kit = new URL('../kit/', import.meta.url);
5
+ export const KEYS = { 'base.js': 'docHelpers', 'components.js': 'compHelpers', 'docs.js': 'docKit', 'fingerprint.js': 'fingerprint' };
6
+ const h32 = (s) => { let h = 0x811c9dc5; for (let i = 0; i < s.length; i++) { h ^= s.charCodeAt(i); h = Math.imul(h, 0x01000193) >>> 0; } return h.toString(16).padStart(8, '0'); };
7
+ const read = (file) => fs.readFileSync(new URL(file, kit), 'utf8');
8
+ if (process.argv.includes('--hashes')) {
9
+ console.log(JSON.stringify(Object.fromEntries(Object.entries(KEYS).map(([f, k]) => [k, `${read(f).length} ${h32(read(f))}`]))));
10
+ } else {
11
+ // --only docHelpers,docKit stores just those parts.
12
+ const only = process.argv.includes('--only') ? process.argv[process.argv.indexOf('--only') + 1].split(',') : null;
13
+ let out = '';
14
+ for (const [file, key] of Object.entries(KEYS)) {
15
+ if (only && !only.includes(key)) continue;
16
+ out += `figma.root.setSharedPluginData('loomiere', '${key}', ${JSON.stringify(read(file))});\n`;
17
+ }
18
+ out += `function h32(s) { var h = 0x811c9dc5; for (var i = 0; i < s.length; i++) { h ^= s.charCodeAt(i); h = Math.imul(h, 0x01000193) >>> 0; } return ('0000000' + h.toString(16)).slice(-8); }\n`;
19
+ out += `var out = {}; ${JSON.stringify(Object.values(KEYS))}.forEach(function (k) { var t = figma.root.getSharedPluginData('loomiere', k); out[k] = t.length + ' ' + h32(t); }); return out;\n`;
20
+ process.stdout.write(out);
21
+ }
@@ -0,0 +1,27 @@
1
+ // Bundles page scripts into one use_figma script, under its 50,000-character limit.
2
+ // node tools/run.mjs [--store all|docHelpers,...] [--check|--real] [--diff Page=unit,unit] [--units Page] pages/...js ...
3
+ // --store writes the kit first (needed once per file, and after a kit change).
4
+ // --check builds on "✱ check / " pages beside the real ones; --real builds the real pages.
5
+ // --diff compares units on the real page with the check page; --units returns a page's fingerprints
6
+ // for tools/compare.mjs.
7
+ import fs from 'node:fs';
8
+ import { execFileSync } from 'node:child_process';
9
+ const lib = new URL('../', import.meta.url);
10
+ const args = process.argv.slice(2), files = [], opt = {};
11
+ for (let i = 0; i < args.length; i++) {
12
+ if (!args[i].startsWith('--')) files.push(args[i]);
13
+ else opt[args[i].slice(2)] = ['check', 'real'].includes(args[i].slice(2)) ? true : args[++i];
14
+ }
15
+ let out = 'var __out = {};\n';
16
+ if (opt.store) out += execFileSync('node', [new URL('tools/kit-script.mjs', lib).pathname, ...(opt.store === 'all' ? [] : ['--only', opt.store])], { encoding: 'utf8' }).replace(/^var out = .*$/m, '') + '\n';
17
+ if (opt.check) out += "figma.root.setSharedPluginData('loomiere', 'pagePrefix', '✱ check / ');\n";
18
+ if (opt.real) out += "figma.root.setSharedPluginData('loomiere', 'pagePrefix', '');\n";
19
+ for (const f of files) out += `__out[${JSON.stringify(f)}] = await (async function () {\n${fs.readFileSync(new URL(f, lib), 'utf8')}\n})();\n`;
20
+ if (opt.diff) {
21
+ const [page, units] = opt.diff.split('=');
22
+ out += `__out.diff = await (async function (PAGE, UNITS, MAX) {\n${fs.readFileSync(new URL('tools/diff-snippet.js', lib), 'utf8')}\n})(${JSON.stringify(page)}, ${JSON.stringify(units.split(','))}, ${Number(opt.max) || 12});\n`;
23
+ }
24
+ if (opt.units) out += `__out.units = await (async function () { var F = await new (Object.getPrototypeOf(async function () {}).constructor)('figma', figma.root.getSharedPluginData('loomiere', 'fingerprint'))(figma); var pg = figma.root.children.filter(function (p) { return p.name === (figma.root.getSharedPluginData('loomiere', 'pagePrefix') || '') + ${JSON.stringify(opt.units)}; })[0]; await figma.setCurrentPageAsync(pg); return await F.units(); })();\n`;
25
+ out += 'return __out;\n';
26
+ if (out.length > 50000) throw new Error(`Bundle is ${out.length} characters; use_figma takes 50,000. Split it.`);
27
+ process.stdout.write(out);
@@ -0,0 +1,34 @@
1
+ // Packs run-order.json into as few use_figma scripts as fit under 50,000 characters each, and
2
+ // writes them to a folder, numbered in the order to run them.
3
+ // node tools/runs.mjs [--real|--check] [--store] [--pages Fields,Buttons] [--out dir]
4
+ // --real builds the real pages (the default); --check builds "✱ check / " pages to compare.
5
+ // --store puts the kit store first (a new file, or after a kit change).
6
+ // --pages keeps only the steps for those pages, in run order, for patching a few pages.
7
+ import fs from 'node:fs';
8
+ import { execFileSync } from 'node:child_process';
9
+ const lib = new URL('../', import.meta.url);
10
+ const args = process.argv.slice(2), opt = {};
11
+ for (let i = 0; i < args.length; i++) opt[args[i].slice(2)] = ['real', 'check', 'store'].includes(args[i].slice(2)) ? true : args[++i];
12
+ const { steps } = JSON.parse(fs.readFileSync(new URL('run-order.json', lib), 'utf8'));
13
+ const want = opt.pages ? opt.pages.split(',') : null;
14
+ const files = steps.filter((s) => !want || want.includes(s.page)).flatMap((s) => s.files);
15
+ const run = (list, extra) => execFileSync('node', [new URL('tools/run.mjs', lib).pathname, opt.check ? '--check' : '--real', ...extra, ...list], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
16
+ const out = new URL((opt.out || 'runs').replace(/\/?$/, '/'), lib);
17
+ fs.rmSync(out, { recursive: true, force: true }); fs.mkdirSync(out, { recursive: true });
18
+ const bundles = [];
19
+ if (opt.store) bundles.push({ files: ['kit'], text: run([], ['--store', 'all']) });
20
+ let group = [];
21
+ for (const f of files) {
22
+ const next = [...group, f];
23
+ let ok = true;
24
+ try { run(next, []); } catch { ok = false; }
25
+ if (ok) { group = next; continue; }
26
+ if (!group.length) throw new Error(`${f} alone is over the limit; split it.`);
27
+ bundles.push({ files: group, text: run(group, []) }); group = [f];
28
+ }
29
+ if (group.length) bundles.push({ files: group, text: run(group, []) });
30
+ bundles.forEach((b, i) => {
31
+ const name = `run-${String(i + 1).padStart(2, '0')}.js`;
32
+ fs.writeFileSync(new URL(name, out), b.text);
33
+ console.log(`${name} ${String(b.text.length).padStart(6)} ${b.files.join(' ')}`);
34
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scattered-light/loomiere",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Turns a small brand file into a full design token theme: OKLCH color ramps, a type scale, light, dark and high-contrast modes, plus Tailwind, MUI, Chakra, AG Grid and Figma exports.",
5
5
  "keywords": ["design-tokens", "theme", "style-dictionary", "dtcg", "oklch", "base-ui", "ag-grid", "tailwind"],
6
6
  "repository": {