@graview/core 0.1.0

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 (327) hide show
  1. package/LICENSE +96 -0
  2. package/README.md +35 -0
  3. package/dist/app.d.ts +294 -0
  4. package/dist/app.d.ts.map +1 -0
  5. package/dist/app.js +49 -0
  6. package/dist/app.js.map +1 -0
  7. package/dist/arrange.d.ts +185 -0
  8. package/dist/arrange.d.ts.map +1 -0
  9. package/dist/arrange.js +456 -0
  10. package/dist/arrange.js.map +1 -0
  11. package/dist/beginning.d.ts +65 -0
  12. package/dist/beginning.d.ts.map +1 -0
  13. package/dist/beginning.js +94 -0
  14. package/dist/beginning.js.map +1 -0
  15. package/dist/bind.d.ts +42 -0
  16. package/dist/bind.d.ts.map +1 -0
  17. package/dist/bind.js +26 -0
  18. package/dist/bind.js.map +1 -0
  19. package/dist/city.d.ts +93 -0
  20. package/dist/city.d.ts.map +1 -0
  21. package/dist/city.js +160 -0
  22. package/dist/city.js.map +1 -0
  23. package/dist/cli/check/arrangement.d.ts +13 -0
  24. package/dist/cli/check/arrangement.d.ts.map +1 -0
  25. package/dist/cli/check/arrangement.js +63 -0
  26. package/dist/cli/check/arrangement.js.map +1 -0
  27. package/dist/cli/check/beginnings.d.ts +16 -0
  28. package/dist/cli/check/beginnings.d.ts.map +1 -0
  29. package/dist/cli/check/beginnings.js +53 -0
  30. package/dist/cli/check/beginnings.js.map +1 -0
  31. package/dist/cli/check/brand.d.ts +17 -0
  32. package/dist/cli/check/brand.d.ts.map +1 -0
  33. package/dist/cli/check/brand.js +153 -0
  34. package/dist/cli/check/brand.js.map +1 -0
  35. package/dist/cli/check/city.d.ts +17 -0
  36. package/dist/cli/check/city.d.ts.map +1 -0
  37. package/dist/cli/check/city.js +58 -0
  38. package/dist/cli/check/city.js.map +1 -0
  39. package/dist/cli/check/context.d.ts +50 -0
  40. package/dist/cli/check/context.d.ts.map +1 -0
  41. package/dist/cli/check/context.js +37 -0
  42. package/dist/cli/check/context.js.map +1 -0
  43. package/dist/cli/check/fields.d.ts +16 -0
  44. package/dist/cli/check/fields.d.ts.map +1 -0
  45. package/dist/cli/check/fields.js +237 -0
  46. package/dist/cli/check/fields.js.map +1 -0
  47. package/dist/cli/check/index.d.ts +14 -0
  48. package/dist/cli/check/index.d.ts.map +1 -0
  49. package/dist/cli/check/index.js +14 -0
  50. package/dist/cli/check/index.js.map +1 -0
  51. package/dist/cli/check/intelligence.d.ts +17 -0
  52. package/dist/cli/check/intelligence.d.ts.map +1 -0
  53. package/dist/cli/check/intelligence.js +136 -0
  54. package/dist/cli/check/intelligence.js.map +1 -0
  55. package/dist/cli/check/lenses.d.ts +17 -0
  56. package/dist/cli/check/lenses.d.ts.map +1 -0
  57. package/dist/cli/check/lenses.js +239 -0
  58. package/dist/cli/check/lenses.js.map +1 -0
  59. package/dist/cli/check/migrations.d.ts +16 -0
  60. package/dist/cli/check/migrations.d.ts.map +1 -0
  61. package/dist/cli/check/migrations.js +43 -0
  62. package/dist/cli/check/migrations.js.map +1 -0
  63. package/dist/cli/check/modules.d.ts +16 -0
  64. package/dist/cli/check/modules.d.ts.map +1 -0
  65. package/dist/cli/check/modules.js +123 -0
  66. package/dist/cli/check/modules.js.map +1 -0
  67. package/dist/cli/check/policy.d.ts +16 -0
  68. package/dist/cli/check/policy.d.ts.map +1 -0
  69. package/dist/cli/check/policy.js +90 -0
  70. package/dist/cli/check/policy.js.map +1 -0
  71. package/dist/cli/check/relations.d.ts +29 -0
  72. package/dist/cli/check/relations.d.ts.map +1 -0
  73. package/dist/cli/check/relations.js +279 -0
  74. package/dist/cli/check/relations.js.map +1 -0
  75. package/dist/cli/check/routes.d.ts +16 -0
  76. package/dist/cli/check/routes.d.ts.map +1 -0
  77. package/dist/cli/check/routes.js +31 -0
  78. package/dist/cli/check/routes.js.map +1 -0
  79. package/dist/cli/check/settings.d.ts +16 -0
  80. package/dist/cli/check/settings.d.ts.map +1 -0
  81. package/dist/cli/check/settings.js +81 -0
  82. package/dist/cli/check/settings.js.map +1 -0
  83. package/dist/cli/check.d.ts +36 -0
  84. package/dist/cli/check.d.ts.map +1 -0
  85. package/dist/cli/check.js +76 -0
  86. package/dist/cli/check.js.map +1 -0
  87. package/dist/cli/create.d.ts +20 -0
  88. package/dist/cli/create.d.ts.map +1 -0
  89. package/dist/cli/create.js +251 -0
  90. package/dist/cli/create.js.map +1 -0
  91. package/dist/cli/describe.d.ts +35 -0
  92. package/dist/cli/describe.d.ts.map +1 -0
  93. package/dist/cli/describe.js +230 -0
  94. package/dist/cli/describe.js.map +1 -0
  95. package/dist/cli/docs.d.ts +11 -0
  96. package/dist/cli/docs.d.ts.map +1 -0
  97. package/dist/cli/docs.js +152 -0
  98. package/dist/cli/docs.js.map +1 -0
  99. package/dist/cli/index.d.ts +18 -0
  100. package/dist/cli/index.d.ts.map +1 -0
  101. package/dist/cli/index.js +281 -0
  102. package/dist/cli/index.js.map +1 -0
  103. package/dist/extend.d.ts +23 -0
  104. package/dist/extend.d.ts.map +1 -0
  105. package/dist/extend.js +53 -0
  106. package/dist/extend.js.map +1 -0
  107. package/dist/graph/diff.d.ts +31 -0
  108. package/dist/graph/diff.d.ts.map +1 -0
  109. package/dist/graph/diff.js +71 -0
  110. package/dist/graph/diff.js.map +1 -0
  111. package/dist/graph/graph.d.ts +69 -0
  112. package/dist/graph/graph.d.ts.map +1 -0
  113. package/dist/graph/graph.js +284 -0
  114. package/dist/graph/graph.js.map +1 -0
  115. package/dist/graph/primitives.d.ts +52 -0
  116. package/dist/graph/primitives.d.ts.map +1 -0
  117. package/dist/graph/primitives.js +62 -0
  118. package/dist/graph/primitives.js.map +1 -0
  119. package/dist/graph/tracked.d.ts +31 -0
  120. package/dist/graph/tracked.d.ts.map +1 -0
  121. package/dist/graph/tracked.js +69 -0
  122. package/dist/graph/tracked.js.map +1 -0
  123. package/dist/graph/types.d.ts +34 -0
  124. package/dist/graph/types.d.ts.map +1 -0
  125. package/dist/graph/types.js +0 -0
  126. package/dist/graph/types.js.map +1 -0
  127. package/dist/index.d.ts +100 -0
  128. package/dist/index.d.ts.map +1 -0
  129. package/dist/index.js +90 -0
  130. package/dist/index.js.map +1 -0
  131. package/dist/installation.d.ts +112 -0
  132. package/dist/installation.d.ts.map +1 -0
  133. package/dist/installation.js +193 -0
  134. package/dist/installation.js.map +1 -0
  135. package/dist/intelligence-bridge.d.ts +73 -0
  136. package/dist/intelligence-bridge.d.ts.map +1 -0
  137. package/dist/intelligence-bridge.js +31 -0
  138. package/dist/intelligence-bridge.js.map +1 -0
  139. package/dist/invariants/engine.d.ts +36 -0
  140. package/dist/invariants/engine.d.ts.map +1 -0
  141. package/dist/invariants/engine.js +88 -0
  142. package/dist/invariants/engine.js.map +1 -0
  143. package/dist/invariants/types.d.ts +75 -0
  144. package/dist/invariants/types.d.ts.map +1 -0
  145. package/dist/invariants/types.js +2 -0
  146. package/dist/invariants/types.js.map +1 -0
  147. package/dist/modules.d.ts +75 -0
  148. package/dist/modules.d.ts.map +1 -0
  149. package/dist/modules.js +87 -0
  150. package/dist/modules.js.map +1 -0
  151. package/dist/mutations/decidable.d.ts +26 -0
  152. package/dist/mutations/decidable.d.ts.map +1 -0
  153. package/dist/mutations/decidable.js +39 -0
  154. package/dist/mutations/decidable.js.map +1 -0
  155. package/dist/mutations/define-mutation.d.ts +32 -0
  156. package/dist/mutations/define-mutation.d.ts.map +1 -0
  157. package/dist/mutations/define-mutation.js +181 -0
  158. package/dist/mutations/define-mutation.js.map +1 -0
  159. package/dist/mutations/derive-edits.d.ts +75 -0
  160. package/dist/mutations/derive-edits.d.ts.map +1 -0
  161. package/dist/mutations/derive-edits.js +240 -0
  162. package/dist/mutations/derive-edits.js.map +1 -0
  163. package/dist/mutations/form.d.ts +63 -0
  164. package/dist/mutations/form.d.ts.map +1 -0
  165. package/dist/mutations/form.js +113 -0
  166. package/dist/mutations/form.js.map +1 -0
  167. package/dist/mutations/node-ref.d.ts +83 -0
  168. package/dist/mutations/node-ref.d.ts.map +1 -0
  169. package/dist/mutations/node-ref.js +113 -0
  170. package/dist/mutations/node-ref.js.map +1 -0
  171. package/dist/mutations/types.d.ts +136 -0
  172. package/dist/mutations/types.d.ts.map +1 -0
  173. package/dist/mutations/types.js +2 -0
  174. package/dist/mutations/types.js.map +1 -0
  175. package/dist/ops/log.d.ts +35 -0
  176. package/dist/ops/log.d.ts.map +1 -0
  177. package/dist/ops/log.js +103 -0
  178. package/dist/ops/log.js.map +1 -0
  179. package/dist/ops/types.d.ts +52 -0
  180. package/dist/ops/types.d.ts.map +1 -0
  181. package/dist/ops/types.js +2 -0
  182. package/dist/ops/types.js.map +1 -0
  183. package/dist/ops/undo.d.ts +33 -0
  184. package/dist/ops/undo.d.ts.map +1 -0
  185. package/dist/ops/undo.js +65 -0
  186. package/dist/ops/undo.js.map +1 -0
  187. package/dist/permissions/policy.d.ts +68 -0
  188. package/dist/permissions/policy.d.ts.map +1 -0
  189. package/dist/permissions/policy.js +191 -0
  190. package/dist/permissions/policy.js.map +1 -0
  191. package/dist/permissions/types.d.ts +78 -0
  192. package/dist/permissions/types.d.ts.map +1 -0
  193. package/dist/permissions/types.js +17 -0
  194. package/dist/permissions/types.js.map +1 -0
  195. package/dist/persistence/memory.d.ts +6 -0
  196. package/dist/persistence/memory.d.ts.map +1 -0
  197. package/dist/persistence/memory.js +31 -0
  198. package/dist/persistence/memory.js.map +1 -0
  199. package/dist/persistence/sqlite.d.ts +59 -0
  200. package/dist/persistence/sqlite.d.ts.map +1 -0
  201. package/dist/persistence/sqlite.js +131 -0
  202. package/dist/persistence/sqlite.js.map +1 -0
  203. package/dist/persistence/types.d.ts +22 -0
  204. package/dist/persistence/types.d.ts.map +1 -0
  205. package/dist/persistence/types.js +2 -0
  206. package/dist/persistence/types.js.map +1 -0
  207. package/dist/presence.d.ts +38 -0
  208. package/dist/presence.d.ts.map +1 -0
  209. package/dist/presence.js +50 -0
  210. package/dist/presence.js.map +1 -0
  211. package/dist/scaffold/domain.d.ts +8 -0
  212. package/dist/scaffold/domain.d.ts.map +1 -0
  213. package/dist/scaffold/domain.js +331 -0
  214. package/dist/scaffold/domain.js.map +1 -0
  215. package/dist/scaffold/index.d.ts +66 -0
  216. package/dist/scaffold/index.d.ts.map +1 -0
  217. package/dist/scaffold/index.js +142 -0
  218. package/dist/scaffold/index.js.map +1 -0
  219. package/dist/scaffold/lens.d.ts +31 -0
  220. package/dist/scaffold/lens.d.ts.map +1 -0
  221. package/dist/scaffold/lens.js +176 -0
  222. package/dist/scaffold/lens.js.map +1 -0
  223. package/dist/scaffold/names.d.ts +44 -0
  224. package/dist/scaffold/names.d.ts.map +1 -0
  225. package/dist/scaffold/names.js +69 -0
  226. package/dist/scaffold/names.js.map +1 -0
  227. package/dist/scaffold/project.d.ts +41 -0
  228. package/dist/scaffold/project.d.ts.map +1 -0
  229. package/dist/scaffold/project.js +604 -0
  230. package/dist/scaffold/project.js.map +1 -0
  231. package/dist/scaffold/ui.d.ts +15 -0
  232. package/dist/scaffold/ui.d.ts.map +1 -0
  233. package/dist/scaffold/ui.js +536 -0
  234. package/dist/scaffold/ui.js.map +1 -0
  235. package/dist/schema/define-node.d.ts +132 -0
  236. package/dist/schema/define-node.d.ts.map +1 -0
  237. package/dist/schema/define-node.js +254 -0
  238. package/dist/schema/define-node.js.map +1 -0
  239. package/dist/schema/figures.d.ts +42 -0
  240. package/dist/schema/figures.d.ts.map +1 -0
  241. package/dist/schema/figures.js +220 -0
  242. package/dist/schema/figures.js.map +1 -0
  243. package/dist/schema/json-schema.d.ts +39 -0
  244. package/dist/schema/json-schema.d.ts.map +1 -0
  245. package/dist/schema/json-schema.js +74 -0
  246. package/dist/schema/json-schema.js.map +1 -0
  247. package/dist/schema/schema.d.ts +43 -0
  248. package/dist/schema/schema.d.ts.map +1 -0
  249. package/dist/schema/schema.js +102 -0
  250. package/dist/schema/schema.js.map +1 -0
  251. package/dist/schema/types.d.ts +201 -0
  252. package/dist/schema/types.d.ts.map +1 -0
  253. package/dist/schema/types.js +2 -0
  254. package/dist/schema/types.js.map +1 -0
  255. package/dist/search.d.ts +215 -0
  256. package/dist/search.d.ts.map +1 -0
  257. package/dist/search.js +449 -0
  258. package/dist/search.js.map +1 -0
  259. package/dist/settings.d.ts +30 -0
  260. package/dist/settings.d.ts.map +1 -0
  261. package/dist/settings.js +74 -0
  262. package/dist/settings.js.map +1 -0
  263. package/dist/store.d.ts +284 -0
  264. package/dist/store.d.ts.map +1 -0
  265. package/dist/store.js +660 -0
  266. package/dist/store.js.map +1 -0
  267. package/dist/studio-door.d.ts +187 -0
  268. package/dist/studio-door.d.ts.map +1 -0
  269. package/dist/studio-door.js +20 -0
  270. package/dist/studio-door.js.map +1 -0
  271. package/dist/sync/conflict.d.ts +37 -0
  272. package/dist/sync/conflict.d.ts.map +1 -0
  273. package/dist/sync/conflict.js +56 -0
  274. package/dist/sync/conflict.js.map +1 -0
  275. package/dist/sync/engine.d.ts +118 -0
  276. package/dist/sync/engine.d.ts.map +1 -0
  277. package/dist/sync/engine.js +341 -0
  278. package/dist/sync/engine.js.map +1 -0
  279. package/dist/sync/google-calendar.d.ts +54 -0
  280. package/dist/sync/google-calendar.d.ts.map +1 -0
  281. package/dist/sync/google-calendar.js +167 -0
  282. package/dist/sync/google-calendar.js.map +1 -0
  283. package/dist/sync/types.d.ts +125 -0
  284. package/dist/sync/types.d.ts.map +1 -0
  285. package/dist/sync/types.js +6 -0
  286. package/dist/sync/types.js.map +1 -0
  287. package/dist/temporal/effectivity.d.ts +75 -0
  288. package/dist/temporal/effectivity.d.ts.map +1 -0
  289. package/dist/temporal/effectivity.js +89 -0
  290. package/dist/temporal/effectivity.js.map +1 -0
  291. package/dist/testing.d.ts +63 -0
  292. package/dist/testing.d.ts.map +1 -0
  293. package/dist/testing.js +194 -0
  294. package/dist/testing.js.map +1 -0
  295. package/dist/theme/contrast.d.ts +47 -0
  296. package/dist/theme/contrast.d.ts.map +1 -0
  297. package/dist/theme/contrast.js +172 -0
  298. package/dist/theme/contrast.js.map +1 -0
  299. package/dist/theme/derive.d.ts +81 -0
  300. package/dist/theme/derive.d.ts.map +1 -0
  301. package/dist/theme/derive.js +171 -0
  302. package/dist/theme/derive.js.map +1 -0
  303. package/dist/theme/kit.d.ts +106 -0
  304. package/dist/theme/kit.d.ts.map +1 -0
  305. package/dist/theme/kit.js +84 -0
  306. package/dist/theme/kit.js.map +1 -0
  307. package/dist/theme/palettes.d.ts +32 -0
  308. package/dist/theme/palettes.d.ts.map +1 -0
  309. package/dist/theme/palettes.js +96 -0
  310. package/dist/theme/palettes.js.map +1 -0
  311. package/dist/theme/types.d.ts +131 -0
  312. package/dist/theme/types.d.ts.map +1 -0
  313. package/dist/theme/types.js +23 -0
  314. package/dist/theme/types.js.map +1 -0
  315. package/dist/views/types.d.ts +89 -0
  316. package/dist/views/types.d.ts.map +1 -0
  317. package/dist/views/types.js +88 -0
  318. package/dist/views/types.js.map +1 -0
  319. package/dist/watched.d.ts +33 -0
  320. package/dist/watched.d.ts.map +1 -0
  321. package/dist/watched.js +50 -0
  322. package/dist/watched.js.map +1 -0
  323. package/dist/who.d.ts +26 -0
  324. package/dist/who.d.ts.map +1 -0
  325. package/dist/who.js +20 -0
  326. package/dist/who.js.map +1 -0
  327. package/package.json +77 -0
@@ -0,0 +1,172 @@
1
+ import { TEXT_PAIRS } from "./types.js";
2
+ const HEX = /^#([0-9a-f]{3,8})$/i;
3
+ const FUNC = /(rgba?|hsla?)\(([^)]+)\)/gi;
4
+ function fromHex(hex) {
5
+ const match = HEX.exec(hex.trim());
6
+ if (!match)
7
+ return null;
8
+ let body = match[1];
9
+ if (body.length === 3 || body.length === 4) {
10
+ body = [...body].map((c) => c + c).join("");
11
+ }
12
+ if (body.length !== 6 && body.length !== 8)
13
+ return null;
14
+ const int = Number.parseInt(body.slice(0, 6), 16);
15
+ const alpha = body.length === 8 ? Number.parseInt(body.slice(6, 8), 16) / 255 : 1;
16
+ return { r: (int >> 16) & 255, g: (int >> 8) & 255, b: int & 255, a: alpha };
17
+ }
18
+ function fromFunction(text) {
19
+ const name = text.slice(0, text.indexOf("(")).toLowerCase();
20
+ const parts = text
21
+ .slice(text.indexOf("(") + 1, text.lastIndexOf(")"))
22
+ .split(/[,\s/]+/)
23
+ .filter(Boolean);
24
+ const numbers = parts.map((part) => part.endsWith("%") ? Number.parseFloat(part) / 100 : Number.parseFloat(part));
25
+ if (numbers.some(Number.isNaN))
26
+ return null;
27
+ if (name.startsWith("rgb")) {
28
+ const [r, g, b, a] = numbers;
29
+ if (r === undefined || g === undefined || b === undefined)
30
+ return null;
31
+ return { r, g, b, a: a ?? 1 };
32
+ }
33
+ const [h, s, l, a] = numbers;
34
+ if (h === undefined || s === undefined || l === undefined)
35
+ return null;
36
+ return { ...hslToRgb(h, s > 1 ? s / 100 : s, l > 1 ? l / 100 : l), a: a ?? 1 };
37
+ }
38
+ /** sRGB back to HSL, so a colour can be moved in lightness and stay itself. */
39
+ export function rgbToHsl(colour) {
40
+ const r = colour.r / 255;
41
+ const g = colour.g / 255;
42
+ const b = colour.b / 255;
43
+ const max = Math.max(r, g, b);
44
+ const min = Math.min(r, g, b);
45
+ const l = (max + min) / 2;
46
+ if (max === min)
47
+ return { h: 0, s: 0, l };
48
+ const d = max - min;
49
+ const s = d / (1 - Math.abs(2 * l - 1));
50
+ const h = max === r
51
+ ? 60 * (((g - b) / d) % 6)
52
+ : max === g
53
+ ? 60 * ((b - r) / d + 2)
54
+ : 60 * ((r - g) / d + 4);
55
+ return { h: (h + 360) % 360, s, l };
56
+ }
57
+ export function hsl(h, s, l) {
58
+ return { ...hslToRgb(h, s, l), a: 1 };
59
+ }
60
+ function hslToRgb(h, s, l) {
61
+ const c = (1 - Math.abs(2 * l - 1)) * s;
62
+ const hp = (((h % 360) + 360) % 360) / 60;
63
+ const x = c * (1 - Math.abs((hp % 2) - 1));
64
+ const [r, g, b] = hp < 1
65
+ ? [c, x, 0]
66
+ : hp < 2
67
+ ? [x, c, 0]
68
+ : hp < 3
69
+ ? [0, c, x]
70
+ : hp < 4
71
+ ? [0, x, c]
72
+ : hp < 5
73
+ ? [x, 0, c]
74
+ : [c, 0, x];
75
+ const m = l - c / 2;
76
+ return {
77
+ r: Math.round((r + m) * 255),
78
+ g: Math.round((g + m) * 255),
79
+ b: Math.round((b + m) * 255),
80
+ };
81
+ }
82
+ /**
83
+ * Every colour a token value mentions.
84
+ *
85
+ * A gradient is not one colour and pretending otherwise is how a check passes
86
+ * on a value it never looked at. `bar` and `float` are gradients in both
87
+ * built-in schemes; a pair drawn on one owes its contrast against the WORST
88
+ * stop, because that is where the text will be at some point along it.
89
+ */
90
+ export function coloursIn(value) {
91
+ const found = [];
92
+ for (const token of value.matchAll(FUNC)) {
93
+ const parsed = fromFunction(token[0]);
94
+ if (parsed)
95
+ found.push(parsed);
96
+ }
97
+ for (const token of value.matchAll(/#[0-9a-fA-F]{3,8}\b/g)) {
98
+ const parsed = fromHex(token[0]);
99
+ if (parsed)
100
+ found.push(parsed);
101
+ }
102
+ return found;
103
+ }
104
+ /** One colour laid over another, straight alpha. */
105
+ export function composite(over, under) {
106
+ const a = over.a + under.a * (1 - over.a);
107
+ if (a === 0)
108
+ return { r: 0, g: 0, b: 0, a: 0 };
109
+ const mix = (x, y) => (x * over.a + y * under.a * (1 - over.a)) / a;
110
+ return { r: mix(over.r, under.r), g: mix(over.g, under.g), b: mix(over.b, under.b), a };
111
+ }
112
+ function channel(value) {
113
+ const c = value / 255;
114
+ return c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
115
+ }
116
+ export function luminance(colour) {
117
+ return (0.2126 * channel(colour.r) + 0.7152 * channel(colour.g) + 0.0722 * channel(colour.b));
118
+ }
119
+ /** WCAG 2.1 contrast ratio, 1 to 21. */
120
+ export function contrast(a, b) {
121
+ const [x, y] = [luminance(a), luminance(b)].sort((m, n) => n - m);
122
+ return (x + 0.05) / (y + 0.05);
123
+ }
124
+ /**
125
+ * Every text pair in a palette, measured.
126
+ *
127
+ * Returns the failures AND the unreadable values. A token this cannot parse
128
+ * is reported rather than skipped: silently passing a value nobody looked at
129
+ * is exactly the failure mode a contrast check exists to prevent.
130
+ */
131
+ export function checkContrast(tokens) {
132
+ const findings = [];
133
+ for (const pair of TEXT_PAIRS) {
134
+ const inkValue = String(tokens[pair.ink]);
135
+ const onValue = String(tokens[pair.on]);
136
+ const inks = coloursIn(inkValue);
137
+ const grounds = coloursIn(onValue);
138
+ if (inks.length === 0 || grounds.length === 0) {
139
+ findings.push({
140
+ ink: pair.ink,
141
+ on: pair.on,
142
+ where: pair.where,
143
+ ratio: 0,
144
+ requires: pair.requires,
145
+ unreadable: inks.length === 0 ? inkValue : onValue,
146
+ });
147
+ continue;
148
+ }
149
+ const backdrop = pair.over ? coloursIn(String(tokens[pair.over]))[0] : undefined;
150
+ const opaque = backdrop ?? { r: 255, g: 255, b: 255, a: 1 };
151
+ // The worst stop of a gradient and the worst ink of a multi-stop value:
152
+ // text has to be legible everywhere it lands, not on average.
153
+ let worst = Number.POSITIVE_INFINITY;
154
+ for (const ground of grounds) {
155
+ const solidGround = composite(ground, opaque);
156
+ for (const ink of inks) {
157
+ worst = Math.min(worst, contrast(composite(ink, solidGround), solidGround));
158
+ }
159
+ }
160
+ if (worst + 0.005 < pair.requires) {
161
+ findings.push({
162
+ ink: pair.ink,
163
+ on: pair.on,
164
+ where: pair.where,
165
+ ratio: Math.round(worst * 100) / 100,
166
+ requires: pair.requires,
167
+ });
168
+ }
169
+ }
170
+ return findings;
171
+ }
172
+ //# sourceMappingURL=contrast.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contrast.js","sourceRoot":"","sources":["../../src/theme/contrast.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,81 @@
1
+ import { checkContrast } from "./contrast.js";
2
+ import type { Scheme, ThemeTokens } from "./types.js";
3
+ /**
4
+ * A brand hands over one accent colour. What can be made of it, and what
5
+ * cannot.
6
+ *
7
+ * The two schemes here are not inversions of each other — dark loses
8
+ * luminance, light loses contrast and gains haze — so "derive the other one"
9
+ * is not a colour operation. What an accent CAN determine is everything
10
+ * keyed to it: its dim companion, its glow, the lit edge, the wash. What it
11
+ * cannot determine is the ground family (warm paper or cool slate is a
12
+ * decision, not a consequence), the ink that has to clear AA against that
13
+ * ground, and the warning colour, which must stay distinguishable from the
14
+ * accent rather than derived from it.
15
+ *
16
+ * So this derives what it can from a neutral base and REFUSES, naming what
17
+ * else is needed, when the accent cannot carry a role it is being asked to.
18
+ * A brand that gets a coherent pair of schemes and one that gets a clear
19
+ * refusal are both better served than one that gets a quietly unreadable
20
+ * interface.
21
+ */
22
+ export interface AccentBrandOptions {
23
+ /** The one colour a brand always has. Hex, rgb() or hsl(). */
24
+ readonly accent: string;
25
+ /**
26
+ * Text drawn ON the accent — a filled button. Derived as black or white
27
+ * when absent, which works for most accents and is checked either way.
28
+ */
29
+ readonly accentInk?: {
30
+ readonly dark?: string;
31
+ readonly light?: string;
32
+ };
33
+ /** The neutral pair to build on. Supply the framework's own when absent. */
34
+ readonly base: Readonly<Record<Scheme, ThemeTokens>>;
35
+ /** Kept away from the accent on purpose; derived only if it stays distinct. */
36
+ readonly warn?: {
37
+ readonly dark?: string;
38
+ readonly light?: string;
39
+ };
40
+ }
41
+ export interface DerivedBrand {
42
+ readonly ok: true;
43
+ readonly schemes: Readonly<Record<Scheme, ThemeTokens>>;
44
+ }
45
+ export interface RefusedBrand {
46
+ readonly ok: false;
47
+ /** What the brand has to supply, in the words of the thing that needs it. */
48
+ readonly missing: readonly string[];
49
+ readonly why: string;
50
+ }
51
+ /**
52
+ * Builds a coherent pair of schemes around one accent, or says what is
53
+ * missing.
54
+ */
55
+ export declare function brandFromAccent(options: AccentBrandOptions): DerivedBrand | RefusedBrand;
56
+ /** Every contrast failure in a pair of schemes, named by scheme. */
57
+ export declare function checkBrandContrast(schemes: Readonly<Record<Scheme, ThemeTokens>>): readonly (ReturnType<typeof checkContrast>[number] & {
58
+ scheme: Scheme;
59
+ })[];
60
+ /**
61
+ * Stable hue per kind — THE thread of kind identity across every surface:
62
+ * chip dots, legend swatches, district roofs, the focus tag, a page's
63
+ * kind mark. A brand that declares a kind's hue (`accents`) wins over the
64
+ * hash. Lives beside the brand because the brand is what overrides it.
65
+ *
66
+ * IN DEGREES, 0–360, which is what a hue is in CSS, in every design tool,
67
+ * and in `brand.accents` itself — the checker already validates an accent as
68
+ * "a number 0–360". It used to divide by 360 on the way out, because every
69
+ * consumer in this repository wanted a fraction, and the name said nothing
70
+ * about that. So an app author read the name, wrote
71
+ * `hsl(${hueFor(kind)} 58% 50%)`, which is valid CSS that renders, and got a
72
+ * hue between 0 and 1 for every kind — which is red. A map with four
73
+ * surfaces came out four shades of the same pink. Nothing failed: not tsc,
74
+ * not graview check, not a test. It was only wrong to look at, and only if
75
+ * you knew what it should have looked like.
76
+ *
77
+ * The consumers divide now, where dividing is a local detail rather than a
78
+ * surprise in a public name.
79
+ */
80
+ export declare function hueFor(kind: string, accents?: Readonly<Record<string, number>>): number;
81
+ //# sourceMappingURL=derive.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"derive.d.ts","sourceRoot":"","sources":["../../src/theme/derive.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EAOd,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEtD;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,kBAAkB;IACjC,8DAA8D;IAC9D,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE;QAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACzE,4EAA4E;IAC5E,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;IACrD,+EAA+E;IAC/E,QAAQ,CAAC,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACrE;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAClB,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;CACzD;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IACnB,6EAA6E;IAC7E,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB;AAQD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,kBAAkB,GAAG,YAAY,GAAG,YAAY,CAmGxF;AAqDD,oEAAoE;AACpE,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,GAC7C,SAAS,CAAC,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,GAAG;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,EAAE,CAI5E;AAGD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,MAAM,CASvF"}
@@ -0,0 +1,171 @@
1
+ import { checkContrast, coloursIn, composite, contrast, hsl, rgbToHsl, } from "./contrast.js";
2
+ const rgba = (colour, alpha) => `rgba(${Math.round(colour.r)}, ${Math.round(colour.g)}, ${Math.round(colour.b)}, ${alpha})`;
3
+ const BLACK = { r: 0, g: 0, b: 0, a: 1 };
4
+ const WHITE = { r: 255, g: 255, b: 255, a: 1 };
5
+ /**
6
+ * Builds a coherent pair of schemes around one accent, or says what is
7
+ * missing.
8
+ */
9
+ export function brandFromAccent(options) {
10
+ const accent = coloursIn(options.accent)[0];
11
+ if (!accent) {
12
+ return {
13
+ ok: false,
14
+ missing: ["accent"],
15
+ why: `"${options.accent}" is not a colour this can read. Use a hex, rgb() or hsl() value.`,
16
+ };
17
+ }
18
+ const schemes = {};
19
+ const missing = new Set();
20
+ const reasons = [];
21
+ const base = rgbToHsl(accent);
22
+ for (const scheme of ["dark", "light"]) {
23
+ const neutral = options.base[scheme];
24
+ const ground = coloursIn(neutral.ground)[0] ?? (scheme === "dark" ? BLACK : WHITE);
25
+ const panel = composite(coloursIn(neutral.panel)[0] ?? WHITE, ground);
26
+ /*
27
+ * The accent is used as TEXT, and no single colour can be text in both
28
+ * schemes.
29
+ *
30
+ * 4.5:1 on white needs a lightness under about 0.18; 4.5:1 on a dark
31
+ * panel needs one over about 0.24. Those do not overlap, which is why
32
+ * "the brand's accent" is one hue with two lightnesses rather than one
33
+ * colour — and why a framework that simply took the hex would have
34
+ * shipped an unreadable label in one scheme or the other.
35
+ *
36
+ * So the hue and the saturation are the brand's; the lightness is moved
37
+ * as little as it takes. If it has to move further than this, the colour
38
+ * has stopped being recognisably theirs and the honest answer is to ask.
39
+ */
40
+ const readable = nearestReadable(base, panel, scheme, 4.5);
41
+ if (!readable) {
42
+ missing.add(`accent (${scheme})`);
43
+ reasons.push(`no lightness of this hue clears 4.5:1 as text on the ${scheme} panel, so ${scheme} needs an accent of its own`);
44
+ }
45
+ else if (Math.abs(readable.l - base.l) > MAX_SHIFT) {
46
+ missing.add(`accent (${scheme})`);
47
+ reasons.push(`reaching 4.5:1 on the ${scheme} panel would move the accent's lightness by ${Math.round(Math.abs(readable.l - base.l) * 100)} points, which is far enough that it stops being the same colour — supply one for ${scheme}`);
48
+ }
49
+ const tone = readable ?? base;
50
+ const shown = hsl(tone.h, tone.s, tone.l);
51
+ const chosenInk = options.accentInk?.[scheme] ??
52
+ (contrast(WHITE, shown) >= contrast(BLACK, shown) ? "#ffffff" : "#000000");
53
+ const inkOnAccent = contrast(coloursIn(chosenInk)[0] ?? WHITE, shown);
54
+ if (inkOnAccent + 0.005 < 4.5) {
55
+ missing.add(`accentInk (${scheme})`);
56
+ reasons.push(`neither black nor white clears 4.5:1 on the accent (best ${inkOnAccent.toFixed(2)}:1), so text on a filled button has to be given`);
57
+ }
58
+ schemes[scheme] = {
59
+ ...neutral,
60
+ accent: rgba(shown, 1),
61
+ accentDim: rgba(shown, scheme === "dark" ? 0.35 : 0.22),
62
+ accentInk: chosenInk,
63
+ glow: rgba(shown, scheme === "dark" ? 0.28 : 0.1),
64
+ /*
65
+ * A lit edge is not text, and it still owes 3:1: a panel outline nobody
66
+ * can see is a panel with no edge. The aesthetic alpha is a preference,
67
+ * so it gives way to the alpha that can actually be seen.
68
+ */
69
+ edgeBright: rgba(shown, visibleAlpha(shown, panel, scheme === "dark" ? 0.55 : 0.42, 3)),
70
+ wash: scheme === "dark"
71
+ ? `radial-gradient(120% 80% at 50% -10%, var(--graview-glow) 0%, transparent 60%), radial-gradient(90% 60% at 12% 108%, ${rgba(shown, 0.1)} 0%, transparent 62%)`
72
+ : `radial-gradient(120% 80% at 50% -20%, rgba(255,255,255,0.9) 0%, transparent 58%), radial-gradient(80% 60% at 92% 104%, ${rgba(shown, 0.06)} 0%, transparent 60%)`,
73
+ ...(options.warn?.[scheme] ? { warn: options.warn[scheme] } : {}),
74
+ };
75
+ /*
76
+ * A warning colour must not be mistakable for the accent. Two roles that
77
+ * look alike is a worse failure than an ugly pair, because one of them
78
+ * means "something is broken".
79
+ */
80
+ const warn = coloursIn(schemes[scheme].warn)[0];
81
+ if (warn && hueGap(rgbToHsl(warn).h, tone.h) < 25) {
82
+ missing.add(`warn (${scheme})`);
83
+ reasons.push(`the warning colour shares the accent's hue in ${scheme}, so "something is broken" looks like "this is selected"`);
84
+ }
85
+ }
86
+ if (missing.size > 0) {
87
+ return { ok: false, missing: [...missing].sort(), why: reasons.join("; ") };
88
+ }
89
+ return { ok: true, schemes };
90
+ }
91
+ /**
92
+ * The smallest alpha at which a colour laid on a ground clears a ratio, or
93
+ * the preferred one if that is already enough.
94
+ *
95
+ * Searched rather than solved because compositing then luminance is not
96
+ * invertible in closed form, and a hundred steps is cheap for something that
97
+ * runs once per theme.
98
+ */
99
+ function visibleAlpha(colour, ground, preferred, ratio) {
100
+ for (let step = Math.round(preferred * 100); step <= 100; step++) {
101
+ const alpha = step / 100;
102
+ if (contrast(composite({ ...colour, a: alpha }, ground), ground) >= ratio)
103
+ return alpha;
104
+ }
105
+ return 1;
106
+ }
107
+ /** How far apart two hues are on the wheel, 0–180. */
108
+ function hueGap(a, b) {
109
+ const gap = Math.abs(a - b) % 360;
110
+ return gap > 180 ? 360 - gap : gap;
111
+ }
112
+ /** As much lightness as a brand's colour may be moved and still be theirs. */
113
+ const MAX_SHIFT = 0.3;
114
+ /**
115
+ * The lightness nearest the brand's own that clears the ratio on this ground.
116
+ *
117
+ * Searched from the original outwards rather than from an end, so a colour
118
+ * that already works is left exactly alone and one that does not is moved the
119
+ * smallest distance that helps. Dark schemes are searched upward first and
120
+ * light ones downward, since that is the direction that will win.
121
+ */
122
+ function nearestReadable(base, ground, scheme, ratio) {
123
+ const up = scheme === "dark";
124
+ for (let step = 0; step <= 100; step++) {
125
+ const first = up ? base.l + step / 100 : base.l - step / 100;
126
+ const second = up ? base.l - step / 100 : base.l + step / 100;
127
+ for (const l of [first, second]) {
128
+ if (l < 0 || l > 1)
129
+ continue;
130
+ if (contrast(hsl(base.h, base.s, l), ground) >= ratio)
131
+ return { ...base, l };
132
+ }
133
+ }
134
+ return null;
135
+ }
136
+ /** Every contrast failure in a pair of schemes, named by scheme. */
137
+ export function checkBrandContrast(schemes) {
138
+ return ["dark", "light"].flatMap((scheme) => checkContrast(schemes[scheme]).map((finding) => ({ ...finding, scheme })));
139
+ }
140
+ /**
141
+ * Stable hue per kind — THE thread of kind identity across every surface:
142
+ * chip dots, legend swatches, district roofs, the focus tag, a page's
143
+ * kind mark. A brand that declares a kind's hue (`accents`) wins over the
144
+ * hash. Lives beside the brand because the brand is what overrides it.
145
+ *
146
+ * IN DEGREES, 0–360, which is what a hue is in CSS, in every design tool,
147
+ * and in `brand.accents` itself — the checker already validates an accent as
148
+ * "a number 0–360". It used to divide by 360 on the way out, because every
149
+ * consumer in this repository wanted a fraction, and the name said nothing
150
+ * about that. So an app author read the name, wrote
151
+ * `hsl(${hueFor(kind)} 58% 50%)`, which is valid CSS that renders, and got a
152
+ * hue between 0 and 1 for every kind — which is red. A map with four
153
+ * surfaces came out four shades of the same pink. Nothing failed: not tsc,
154
+ * not graview check, not a test. It was only wrong to look at, and only if
155
+ * you knew what it should have looked like.
156
+ *
157
+ * The consumers divide now, where dividing is a local detail rather than a
158
+ * surprise in a public name.
159
+ */
160
+ export function hueFor(kind, accents) {
161
+ const declared = accents?.[kind];
162
+ if (declared !== undefined)
163
+ return ((declared % 360) + 360) % 360;
164
+ let h = 2166136261;
165
+ for (let i = 0; i < kind.length; i++) {
166
+ h ^= kind.charCodeAt(i);
167
+ h = Math.imul(h, 16777619);
168
+ }
169
+ return (h >>> 0) % 360;
170
+ }
171
+ //# sourceMappingURL=derive.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"derive.js","sourceRoot":"","sources":["../../src/theme/derive.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,SAAS,EACT,SAAS,EACT,QAAQ,EACR,GAAG,EACH,QAAQ,GAET,MAAM,eAAe,CAAC;AAgDvB,MAAM,IAAI,GAAG,CAAC,MAAY,EAAE,KAAa,EAAE,EAAE,CAC3C,QAAQ,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,KAAK,GAAG,CAAC;AAE9F,MAAM,KAAK,GAAS,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;AAC/C,MAAM,KAAK,GAAS,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;AAErD;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,OAA2B;IACzD,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5C,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO;YACL,EAAE,EAAE,KAAK;YACT,OAAO,EAAE,CAAC,QAAQ,CAAC;YACnB,GAAG,EAAE,IAAI,OAAO,CAAC,MAAM,mEAAmE;SAC3F,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GAAG,EAAiC,CAAC;IAClD,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;IAE9B,KAAK,MAAM,MAAM,IAAI,CAAC,MAAM,EAAE,OAAO,CAAU,EAAE,CAAC;QAChD,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACrC,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QACnF,MAAM,KAAK,GAAG,SAAS,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,EAAE,MAAM,CAAC,CAAC;QAEtE;;;;;;;;;;;;;WAaG;QACH,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC;QAC3D,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,OAAO,CAAC,GAAG,CAAC,WAAW,MAAM,GAAG,CAAC,CAAC;YAClC,OAAO,CAAC,IAAI,CACV,wDAAwD,MAAM,cAAc,MAAM,6BAA6B,CAChH,CAAC;QACJ,CAAC;aAAM,IAAI,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,SAAS,EAAE,CAAC;YACrD,OAAO,CAAC,GAAG,CAAC,WAAW,MAAM,GAAG,CAAC,CAAC;YAClC,OAAO,CAAC,IAAI,CACV,yBAAyB,MAAM,+CAA+C,IAAI,CAAC,KAAK,CACtF,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,GAAG,CACpC,qFAAqF,MAAM,EAAE,CAC/F,CAAC;QACJ,CAAC;QACD,MAAM,IAAI,GAAG,QAAQ,IAAI,IAAI,CAAC;QAC9B,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;QAE1C,MAAM,SAAS,GACb,OAAO,CAAC,SAAS,EAAE,CAAC,MAAM,CAAC;YAC3B,CAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QAC7E,MAAM,WAAW,GAAG,QAAQ,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,EAAE,KAAK,CAAC,CAAC;QACtE,IAAI,WAAW,GAAG,KAAK,GAAG,GAAG,EAAE,CAAC;YAC9B,OAAO,CAAC,GAAG,CAAC,cAAc,MAAM,GAAG,CAAC,CAAC;YACrC,OAAO,CAAC,IAAI,CACV,4DAA4D,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,iDAAiD,CACpI,CAAC;QACJ,CAAC;QAED,OAAO,CAAC,MAAM,CAAC,GAAG;YAChB,GAAG,OAAO;YACV,MAAM,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;YACtB,SAAS,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;YACvD,SAAS,EAAE,SAAS;YACpB,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC;YACjD;;;;eAIG;YACH,UAAU,EAAE,IAAI,CAAC,KAAK,EAAE,YAAY,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;YACvF,IAAI,EACF,MAAM,KAAK,MAAM;gBACf,CAAC,CAAC,wHAAwH,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,uBAAuB;gBACjK,CAAC,CAAC,0HAA0H,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,uBAAuB;YACxK,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,MAAM,CAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACnE,CAAC;QAEF;;;;WAIG;QACH,MAAM,IAAI,GAAG,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QAChD,IAAI,IAAI,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC;YAClD,OAAO,CAAC,GAAG,CAAC,SAAS,MAAM,GAAG,CAAC,CAAC;YAChC,OAAO,CAAC,IAAI,CACV,iDAAiD,MAAM,0DAA0D,CAClH,CAAC;QACJ,CAAC;IACH,CAAC;IAED,IAAI,OAAO,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;QACrB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,GAAG,EAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;IAC9E,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AAC/B,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,YAAY,CAAC,MAAY,EAAE,MAAY,EAAE,SAAiB,EAAE,KAAa;IAChF,KAAK,IAAI,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,GAAG,CAAC,EAAE,IAAI,IAAI,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC;QACjE,MAAM,KAAK,GAAG,IAAI,GAAG,GAAG,CAAC;QACzB,IAAI,QAAQ,CAAC,SAAS,CAAC,EAAE,GAAG,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,IAAI,KAAK;YAAE,OAAO,KAAK,CAAC;IAC1F,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,sDAAsD;AACtD,SAAS,MAAM,CAAC,CAAS,EAAE,CAAS;IAClC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC;IAClC,OAAO,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;AACrC,CAAC;AAED,8EAA8E;AAC9E,MAAM,SAAS,GAAG,GAAG,CAAC;AAEtB;;;;;;;GAOG;AACH,SAAS,eAAe,CACtB,IAAyC,EACzC,MAAY,EACZ,MAAc,EACd,KAAa;IAEb,MAAM,EAAE,GAAG,MAAM,KAAK,MAAM,CAAC;IAC7B,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,IAAI,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC;QACvC,MAAM,KAAK,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,GAAG,GAAG,CAAC;QAC7D,MAAM,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,GAAG,GAAG,CAAC;QAC9D,KAAK,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,CAAC;YAChC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC;gBAAE,SAAS;YAC7B,IAAI,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,IAAI,KAAK;gBAAE,OAAO,EAAE,GAAG,IAAI,EAAE,CAAC,EAAE,CAAC;QAC/E,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,kBAAkB,CAChC,OAA8C;IAE9C,OAAQ,CAAC,MAAM,EAAE,OAAO,CAAW,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE,CACrD,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,CAC1E,CAAC;AACJ,CAAC;AAGD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,MAAM,CAAC,IAAY,EAAE,OAA0C;IAC7E,MAAM,QAAQ,GAAG,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC;IACjC,IAAI,QAAQ,KAAK,SAAS;QAAE,OAAO,CAAC,CAAC,QAAQ,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC;IAClE,IAAI,CAAC,GAAG,UAAU,CAAC;IACnB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QACxB,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;IAC7B,CAAC;IACD,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,CAAC;AACzB,CAAC"}
@@ -0,0 +1,106 @@
1
+ import { type Rgba } from "./contrast.js";
2
+ import type { ThemeTokens } from "./types.js";
3
+ /** How a connector travels between its two ends. */
4
+ export type ConnectorRoute = "curve" | "straight" | "orthogonal";
5
+ export type KitStrokePattern = "solid" | "dashed" | "dotted" | "double" | "tapered";
6
+ export type KitEndCap = "none" | "arrow" | "dot" | "bar";
7
+ export interface ConnectorKit {
8
+ /** The path's shape. One strategy per name; adding one is one case. */
9
+ readonly route: ConnectorRoute;
10
+ /** Drawn at all. An edge kind a brand keeps quiet is still selectable from the inspector. */
11
+ readonly visible: boolean;
12
+ /** An explicit CSS colour; absent, the kind's own hue. */
13
+ readonly colour?: string;
14
+ readonly pattern?: KitStrokePattern;
15
+ readonly width?: number;
16
+ readonly cap?: KitEndCap;
17
+ readonly opacity?: number;
18
+ }
19
+ export interface Kit {
20
+ readonly connectors: {
21
+ /** Every edge kind, unless `byEdge` says otherwise. */
22
+ readonly all: ConnectorKit;
23
+ /** Per edge kind: colour, visibility, pattern — whatever this kind wants differently. */
24
+ readonly byEdge: Readonly<Record<string, Partial<ConnectorKit>>>;
25
+ };
26
+ /** The words over a neighbourhood: the edge's own reading. */
27
+ readonly captions: {
28
+ readonly visible: boolean;
29
+ };
30
+ /** The fine square measure under the scene on the ground. */
31
+ readonly grid: {
32
+ readonly visible: boolean;
33
+ readonly size: number;
34
+ };
35
+ /** The isometric lattice the ground becomes from altitude. */
36
+ readonly lattice: {
37
+ readonly visible: boolean;
38
+ readonly size: number;
39
+ };
40
+ /** The kind's name astride a focused card. */
41
+ readonly tags: {
42
+ readonly visible: boolean;
43
+ };
44
+ /**
45
+ * How loud a connector is when it is not the one being looked at.
46
+ *
47
+ * `rest` is what an unlit line keeps while NOTHING is lit — the quiet
48
+ * weight every relation in the picture carries at once. `dim` is what it
49
+ * keeps while something else IS lit: 0 vanishes, 1 does not recede. They
50
+ * are two different questions, and one number could not answer both —
51
+ * a resting opacity low enough to be calm made a selection's own lines
52
+ * barely brighter than the crowd, and one high enough to show the crowd
53
+ * left twenty lines shouting over the two a person had just chosen.
54
+ */
55
+ readonly emphasis: {
56
+ readonly rest: number;
57
+ readonly dim: number;
58
+ };
59
+ /** The glyph a broken rule leaves on what it names. */
60
+ readonly marks: {
61
+ readonly flag: string;
62
+ };
63
+ }
64
+ /** A brand's say: any part of the kit, the rest as shipped. */
65
+ export interface KitOverrides {
66
+ readonly connectors?: {
67
+ readonly all?: Partial<ConnectorKit>;
68
+ readonly byEdge?: Readonly<Record<string, Partial<ConnectorKit>>>;
69
+ };
70
+ readonly captions?: Partial<Kit["captions"]>;
71
+ readonly grid?: Partial<Kit["grid"]>;
72
+ readonly lattice?: Partial<Kit["lattice"]>;
73
+ readonly tags?: Partial<Kit["tags"]>;
74
+ readonly emphasis?: Partial<Kit["emphasis"]>;
75
+ readonly marks?: Partial<Kit["marks"]>;
76
+ }
77
+ export declare const DEFAULT_KIT: Kit;
78
+ /** The kit as this brand wants it, every entry present. */
79
+ export declare function resolveKit(overrides?: KitOverrides): Kit;
80
+ /** One edge kind's connector, `all` under `byEdge`. */
81
+ export declare function connectorKitFor(kit: Kit, edgeKind: string): ConnectorKit;
82
+ /**
83
+ * The kit as custom properties, so the theme's rules read it and a host can
84
+ * override one entry without forking a component. Booleans become the CSS
85
+ * that carries them — a display, a multiplier — so no rule needs a script.
86
+ */
87
+ export declare function kitVariables(kit: Kit): string;
88
+ export interface KitContrastFinding {
89
+ readonly edgeKind: string | "*";
90
+ readonly colour: string;
91
+ readonly ratio: number;
92
+ readonly requires: number;
93
+ readonly unreadable?: string;
94
+ /** Which ground it failed on: the scene's own, or the deeper one under a district. */
95
+ readonly ground?: "ground" | "deep ground";
96
+ }
97
+ /**
98
+ * A connector's colour must be told from the ground it crosses: 3:1, the
99
+ * floor for graphics, on both the ground and the deeper ground the
100
+ * altitude view uses. Only explicit colours are judged — a kind's own hue
101
+ * is the theme's to keep readable.
102
+ */
103
+ export declare function checkKitContrast(kit: Kit, tokens: ThemeTokens): readonly KitContrastFinding[];
104
+ /** The kind's own hue (degrees) as the theme paints a connector, for callers that want the same colour elsewhere. */
105
+ export declare function connectorHueColour(hue: number): Rgba;
106
+ //# sourceMappingURL=kit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kit.d.ts","sourceRoot":"","sources":["../../src/theme/kit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,84 @@
1
+ import { coloursIn, contrast, hsl } from "./contrast.js";
2
+ export const DEFAULT_KIT = {
3
+ connectors: { all: { route: "curve", visible: true }, byEdge: {} },
4
+ captions: { visible: true },
5
+ grid: { visible: true, size: 64 },
6
+ lattice: { visible: true, size: 46 },
7
+ tags: { visible: true },
8
+ emphasis: { rest: 0.34, dim: 0.12 },
9
+ marks: { flag: "⚠" },
10
+ };
11
+ /** The kit as this brand wants it, every entry present. */
12
+ export function resolveKit(overrides) {
13
+ if (!overrides)
14
+ return DEFAULT_KIT;
15
+ return {
16
+ connectors: {
17
+ all: { ...DEFAULT_KIT.connectors.all, ...(overrides.connectors?.all ?? {}) },
18
+ byEdge: { ...DEFAULT_KIT.connectors.byEdge, ...(overrides.connectors?.byEdge ?? {}) },
19
+ },
20
+ captions: { ...DEFAULT_KIT.captions, ...(overrides.captions ?? {}) },
21
+ grid: { ...DEFAULT_KIT.grid, ...(overrides.grid ?? {}) },
22
+ lattice: { ...DEFAULT_KIT.lattice, ...(overrides.lattice ?? {}) },
23
+ tags: { ...DEFAULT_KIT.tags, ...(overrides.tags ?? {}) },
24
+ emphasis: { ...DEFAULT_KIT.emphasis, ...(overrides.emphasis ?? {}) },
25
+ marks: { ...DEFAULT_KIT.marks, ...(overrides.marks ?? {}) },
26
+ };
27
+ }
28
+ /** One edge kind's connector, `all` under `byEdge`. */
29
+ export function connectorKitFor(kit, edgeKind) {
30
+ return { ...kit.connectors.all, ...(kit.connectors.byEdge[edgeKind] ?? {}) };
31
+ }
32
+ /**
33
+ * The kit as custom properties, so the theme's rules read it and a host can
34
+ * override one entry without forking a component. Booleans become the CSS
35
+ * that carries them — a display, a multiplier — so no rule needs a script.
36
+ */
37
+ export function kitVariables(kit) {
38
+ return [
39
+ ` --graview-kit-grid: ${kit.grid.visible ? 1 : 0};`,
40
+ ` --graview-kit-grid-size: ${kit.grid.size}px;`,
41
+ ` --graview-kit-lattice: ${kit.lattice.visible ? 1 : 0};`,
42
+ ` --graview-kit-lattice-size: ${kit.lattice.size}px;`,
43
+ ` --graview-kit-tags: ${kit.tags.visible ? "inline-flex" : "none"};`,
44
+ ` --graview-kit-captions: ${kit.captions.visible ? "block" : "none"};`,
45
+ ` --graview-kit-rest: ${kit.emphasis.rest};`,
46
+ ` --graview-kit-dim: ${kit.emphasis.dim};`,
47
+ ` --graview-kit-flag: "${kit.marks.flag.replace(/"/g, "'")}";`,
48
+ ].join("\n");
49
+ }
50
+ /**
51
+ * A connector's colour must be told from the ground it crosses: 3:1, the
52
+ * floor for graphics, on both the ground and the deeper ground the
53
+ * altitude view uses. Only explicit colours are judged — a kind's own hue
54
+ * is the theme's to keep readable.
55
+ */
56
+ export function checkKitContrast(kit, tokens) {
57
+ const findings = [];
58
+ const grounds = [["ground", tokens.ground], ["deep ground", tokens.groundDeep]]
59
+ .map(([name, value]) => ({ name, colour: coloursIn(value)[0] }))
60
+ .filter((one) => one.colour !== undefined);
61
+ const judge = (edgeKind, colour) => {
62
+ if (!colour)
63
+ return;
64
+ const ink = coloursIn(colour)[0];
65
+ if (!ink) {
66
+ findings.push({ edgeKind, colour, ratio: 0, requires: 3, unreadable: colour });
67
+ return;
68
+ }
69
+ for (const ground of grounds) {
70
+ const ratio = Math.round(contrast(ink, ground.colour) * 100) / 100;
71
+ if (ratio < 3)
72
+ findings.push({ edgeKind, colour, ratio, requires: 3, ground: ground.name });
73
+ }
74
+ };
75
+ judge("*", kit.connectors.all.colour);
76
+ for (const [edgeKind, connector] of Object.entries(kit.connectors.byEdge))
77
+ judge(edgeKind, connector.colour);
78
+ return findings;
79
+ }
80
+ /** The kind's own hue (degrees) as the theme paints a connector, for callers that want the same colour elsewhere. */
81
+ export function connectorHueColour(hue) {
82
+ return hsl(hue, 55, 62);
83
+ }
84
+ //# sourceMappingURL=kit.js.map