@threadlabs/looma-docs 0.0.0-stage → 0.52.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/LICENSE +21 -0
  2. package/README.md +11 -2
  3. package/bin/looma-docs.mjs +3 -0
  4. package/docs/README.md +89 -0
  5. package/docs/card.html +79 -0
  6. package/docs/card.js +18 -0
  7. package/docs/catalog-view.js +24 -0
  8. package/docs/catalog-view.mjs +13 -0
  9. package/docs/catalog.mjs +135 -0
  10. package/docs/cli.mjs +26 -0
  11. package/docs/commands.mjs +94 -0
  12. package/docs/customizer-render.mjs +157 -0
  13. package/docs/customizer.js +494 -0
  14. package/docs/documentation.css +42 -0
  15. package/docs/example-invocations.mjs +63 -0
  16. package/docs/fonts.mjs +73 -0
  17. package/docs/framework-examples.d.mts +9 -0
  18. package/docs/framework-examples.mjs +225 -0
  19. package/docs/guides/01.theme-your-system.md +64 -0
  20. package/docs/guides/02.add-your-components.md +96 -0
  21. package/docs/guides/03.publish-your-system.md +71 -0
  22. package/docs/guides/04.theme-decisions.md +141 -0
  23. package/docs/guides/05.(Theme reference)/01.theme-colors.md +95 -0
  24. package/docs/guides/05.(Theme reference)/02.theme-typography.md +73 -0
  25. package/docs/guides/05.(Theme reference)/03.theme-spacing.md +103 -0
  26. package/docs/guides/05.(Theme reference)/04.theme-material.md +90 -0
  27. package/docs/guides/05.(Theme reference)/05.theme-motion.md +57 -0
  28. package/docs/guides/assets/fellowship-dark.png +0 -0
  29. package/docs/guides/assets/fellowship-light.png +0 -0
  30. package/docs/index.d.ts +59 -0
  31. package/docs/index.mjs +4 -0
  32. package/docs/local-server.mjs +65 -0
  33. package/docs/navigation.mjs +40 -0
  34. package/docs/palette.mjs +68 -0
  35. package/docs/pastoral.svg +54 -0
  36. package/docs/portrait.svg +15 -0
  37. package/docs/reference.html +1 -0
  38. package/docs/reference.js +36 -0
  39. package/docs/render.mjs +97 -0
  40. package/docs/site.mjs +244 -0
  41. package/docs/store.mjs +92 -0
  42. package/docs/theme-session.mjs +72 -0
  43. package/docs/theme.mjs +669 -0
  44. package/docs/value-editors.mjs +37 -0
  45. package/docs/workspace.mjs +32 -0
  46. package/package.json +53 -3
@@ -0,0 +1,37 @@
1
+ /** Translate canonical recipe values for the reusable value editors; never duplicate design bounds here. */
2
+ const lengthUnits = { px: 1, rem: 16, em: 16, cm: 96 / 2.54, mm: 96 / 25.4, Q: 96 / 101.6, in: 96, pt: 96 / 72, pc: 16 };
3
+ const timeUnits = { ms: 1, s: 1000 };
4
+ const rounded = value => String(Number(Number(value).toFixed(6)));
5
+
6
+ export function valueEditor(field, value, { remPixels = 16 } = {}) {
7
+ if (field.type === 'inherited-length') return { kind: 'inherited-size', value };
8
+ if (field.id === 'text-size') return { kind: 'number', value, unit: 'px', min: field.min, max: field.max, step: 1 };
9
+ if (field.type === 'choice') return { kind: 'choice', value };
10
+ if (field.type === 'easing') return { kind: /^step-/.test(value) ? 'text' : 'curve', value };
11
+ if (['colour', 'optional-colour', 'font'].includes(field.type)) return { kind: 'text', value };
12
+ if (field.type.includes('length') || field.type === 'time') {
13
+ const unit = value.match(/[a-z]+$/i)?.[0] ?? 'px';
14
+ // Convert authoring limits against the same root as the displayed quantity.
15
+ const units = field.type === 'time' ? timeUnits : { ...lengthUnits, rem: remPixels };
16
+ const factor = units[unit];
17
+ return { kind: 'number', value: value === '0' ? '0px' : value, unit,
18
+ min: factor ? field.min / factor : .001, max: factor ? field.max / factor : 1000000,
19
+ step: (field.type === 'time' ? 10 : field.id === 'line-weight' ? .25 : 1) / (factor ?? 1),
20
+ units: field.type === 'time' ? ['ms', 's'] : ['px', 'rem'], convertible: Boolean(factor) };
21
+ }
22
+ const scale = field.type === 'fraction' || field.id === 'spacing' ? 100 : 1;
23
+ return { kind: field.type === 'weight' ? 'number' : 'slider', value: field.type === 'weight' ? value : Number(value) * scale,
24
+ min: field.min * scale, max: field.max * scale, step: field.type === 'weight' ? 50 : scale === 100 ? 1 : .01,
25
+ unit: scale === 100 ? '%' : '', scale };
26
+ }
27
+
28
+ export function editorValue(field, value) {
29
+ return field.type === 'fraction' || field.id === 'spacing' ? rounded(Number(value) / 100) : String(value);
30
+ }
31
+
32
+ export function convertValueUnit(value, nextUnit, { remPixels = 16 } = {}) {
33
+ const unit = value.match(/[a-z]+$/i)?.[0] ?? 'px';
34
+ const units = Object.hasOwn(timeUnits, unit) ? timeUnits : { ...lengthUnits, rem: remPixels, em: remPixels };
35
+ if (!Object.hasOwn(units, unit) || !Object.hasOwn(units, nextUnit)) throw new Error('Choose compatible units.');
36
+ return rounded(parseFloat(value) * units[unit] / units[nextUnit]) + nextUnit;
37
+ }
@@ -0,0 +1,32 @@
1
+ import { lstat, mkdir, open, realpath, unlink } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+
4
+ export async function rejectSymlink(file) {
5
+ try { if ((await lstat(file)).isSymbolicLink()) throw new Error(`Looma's generated path cannot be a symlink: ${file}.`); }
6
+ catch (error) { if (error.code !== 'ENOENT') throw error; }
7
+ }
8
+
9
+ /** Generated sources and tooling wrappers stay in owned cache directories. */
10
+ export async function cacheDirectory(root, name = '') {
11
+ root = await realpath(path.resolve(root));
12
+ if (name && !/^[a-z][a-z-]*$/.test(name)) throw new Error('Invalid Looma cache directory.');
13
+ const cache = path.join(root, '.looma');
14
+ await rejectSymlink(cache); await mkdir(cache, { recursive: true });
15
+ const directory = path.join(cache, name);
16
+ await rejectSymlink(directory); await mkdir(directory, { recursive: true });
17
+ return directory;
18
+ }
19
+
20
+ /** One complete preparation/build owns the generated tree, including across CLI processes. */
21
+ export async function withBuildLock(root, action) {
22
+ const lockFile = path.join(await cacheDirectory(root), 'docs-build.lock');
23
+ await rejectSymlink(lockFile);
24
+ let lock;
25
+ try { lock = await open(lockFile, 'wx', 0o600); }
26
+ catch (error) {
27
+ if (error.code === 'EEXIST') throw new Error('Another documentation build holds .looma/docs-build.lock. Retry after it finishes. If the old process stopped, close it before removing that lock.');
28
+ throw error;
29
+ }
30
+ try { await lock.writeFile(JSON.stringify({ pid: process.pid })); return await action(); }
31
+ finally { try { await lock.close(); } finally { await unlink(lockFile); } }
32
+ }
package/package.json CHANGED
@@ -1,6 +1,56 @@
1
1
  {
2
+ "version": "0.52.1",
3
+ "license": "MIT",
4
+ "repository": {
5
+ "type": "git",
6
+ "url": "git+https://github.com/threadlabs-studio/looma.git",
7
+ "directory": "packages/looma-docs"
8
+ },
9
+ "bugs": {
10
+ "url": "https://github.com/threadlabs-studio/looma/issues"
11
+ },
12
+ "homepage": "https://github.com/threadlabs-studio/looma#readme",
13
+ "publishConfig": {
14
+ "access": "public",
15
+ "provenance": true
16
+ },
17
+ "engines": {
18
+ "node": ">=22.22.2 <23 || >=24.15 <25"
19
+ },
2
20
  "name": "@threadlabs/looma-docs",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
21
+ "description": "Local design-system documentation and theme customization for Looma.",
22
+ "type": "module",
23
+ "bin": {
24
+ "looma-docs": "./bin/looma-docs.mjs"
25
+ },
26
+ "exports": {
27
+ ".": {
28
+ "types": "./docs/index.d.ts",
29
+ "import": "./docs/index.mjs"
30
+ }
31
+ },
32
+ "files": [
33
+ "bin/",
34
+ "docs/",
35
+ "README.md",
36
+ "LICENSE"
37
+ ],
38
+ "dependencies": {
39
+ "@threadlabs/looma": ">=0.52.0",
40
+ "@nextwebwg/html-next": ">=1.0.0-alpha.84",
41
+ "@threadlabs/markupkit": ">=0.6.1",
42
+ "markupress": ">=0.18.4",
43
+ "vite": ">=8.3.0",
44
+ "@tiptap/core": "^2.11.5",
45
+ "@tiptap/pm": "^2.11.5",
46
+ "expressmark": ">=0.6.0",
47
+ "parse5": "^8.0.1"
48
+ },
49
+ "devDependencies": {
50
+ "jsdom": "^27.0.0"
51
+ },
52
+ "scripts": {
53
+ "build": "node build.mjs",
54
+ "test": "node --test tooling/docs/*.test.mjs"
55
+ }
6
56
  }