@omerdlw/base-framework 1.0.6 → 2.0.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 (231) hide show
  1. package/dist/atoms/index.js +1 -103
  2. package/dist/atoms/index.js.map +1 -1
  3. package/dist/chunk-3BUP64Q6.js +105 -0
  4. package/dist/chunk-3BUP64Q6.js.map +1 -0
  5. package/dist/chunk-3MSK4XGK.js +9888 -0
  6. package/dist/chunk-3MSK4XGK.js.map +1 -0
  7. package/dist/chunk-4GMQKD6F.js +353 -0
  8. package/dist/chunk-4GMQKD6F.js.map +1 -0
  9. package/dist/chunk-65ZB4QTQ.js +888 -0
  10. package/dist/chunk-65ZB4QTQ.js.map +1 -0
  11. package/dist/chunk-6SG7V77J.js +577 -0
  12. package/dist/chunk-6SG7V77J.js.map +1 -0
  13. package/dist/chunk-7HQMXTNP.js +419 -0
  14. package/dist/chunk-7HQMXTNP.js.map +1 -0
  15. package/dist/chunk-7TAVYJ3A.js +371 -0
  16. package/dist/chunk-7TAVYJ3A.js.map +1 -0
  17. package/dist/chunk-AQ4BNXA2.js +349 -0
  18. package/dist/chunk-AQ4BNXA2.js.map +1 -0
  19. package/dist/chunk-CGHZSSFR.js +2166 -0
  20. package/dist/chunk-CGHZSSFR.js.map +1 -0
  21. package/dist/chunk-DGCQWQLZ.js +61 -0
  22. package/dist/chunk-DGCQWQLZ.js.map +1 -0
  23. package/dist/chunk-FIMPHODX.js +67 -0
  24. package/dist/chunk-FIMPHODX.js.map +1 -0
  25. package/dist/chunk-GF7TUX5I.js +1889 -0
  26. package/dist/chunk-GF7TUX5I.js.map +1 -0
  27. package/dist/chunk-JELG4JLT.js +454 -0
  28. package/dist/chunk-JELG4JLT.js.map +1 -0
  29. package/dist/chunk-K676GL7Z.js +51 -0
  30. package/dist/chunk-K676GL7Z.js.map +1 -0
  31. package/dist/chunk-KKQT46TP.js +26 -0
  32. package/dist/chunk-KKQT46TP.js.map +1 -0
  33. package/dist/chunk-QI3ALLIB.js +38 -0
  34. package/dist/chunk-QI3ALLIB.js.map +1 -0
  35. package/dist/chunk-QXR55VVS.js +1139 -0
  36. package/dist/chunk-QXR55VVS.js.map +1 -0
  37. package/dist/chunk-R2YGFJDJ.js +408 -0
  38. package/dist/chunk-R2YGFJDJ.js.map +1 -0
  39. package/dist/chunk-RFZQWLC5.js +27 -0
  40. package/dist/chunk-RFZQWLC5.js.map +1 -0
  41. package/dist/chunk-SYAVGTOX.js +521 -0
  42. package/dist/chunk-SYAVGTOX.js.map +1 -0
  43. package/dist/chunk-TV3WEF5G.js +784 -0
  44. package/dist/chunk-TV3WEF5G.js.map +1 -0
  45. package/dist/chunk-XASQT4CQ.js +280 -0
  46. package/dist/chunk-XASQT4CQ.js.map +1 -0
  47. package/dist/error/index.js +5 -682
  48. package/dist/error/index.js.map +1 -1
  49. package/dist/error/reporter.d.ts.map +1 -1
  50. package/dist/error/theme.d.ts +1 -1
  51. package/dist/error/theme.d.ts.map +1 -1
  52. package/dist/events.js +1 -7
  53. package/dist/events.js.map +1 -1
  54. package/dist/hooks/index.js +1 -358
  55. package/dist/hooks/index.js.map +1 -1
  56. package/dist/hooks/use-global-event.d.ts +1 -1
  57. package/dist/index.js +18 -20029
  58. package/dist/index.js.map +1 -1
  59. package/dist/kernel/index.js +2 -2035
  60. package/dist/kernel/index.js.map +1 -1
  61. package/dist/kernel/types.d.ts +1 -1
  62. package/dist/modules/ambient/analyze.d.ts +46 -0
  63. package/dist/modules/ambient/analyze.d.ts.map +1 -0
  64. package/dist/modules/ambient/constants.d.ts +1 -5
  65. package/dist/modules/ambient/constants.d.ts.map +1 -1
  66. package/dist/modules/ambient/context.d.ts.map +1 -1
  67. package/dist/modules/ambient/dom.d.ts +11 -12
  68. package/dist/modules/ambient/dom.d.ts.map +1 -1
  69. package/dist/modules/ambient/extractor.d.ts +10 -10
  70. package/dist/modules/ambient/extractor.d.ts.map +1 -1
  71. package/dist/modules/ambient/hooks.d.ts +1 -1
  72. package/dist/modules/ambient/hooks.d.ts.map +1 -1
  73. package/dist/modules/ambient/index.d.ts +6 -4
  74. package/dist/modules/ambient/index.d.ts.map +1 -1
  75. package/dist/modules/ambient/index.js +7 -762
  76. package/dist/modules/ambient/index.js.map +1 -1
  77. package/dist/modules/ambient/module.d.ts +1 -1
  78. package/dist/modules/ambient/module.d.ts.map +1 -1
  79. package/dist/modules/ambient/oklab.d.ts +36 -0
  80. package/dist/modules/ambient/oklab.d.ts.map +1 -0
  81. package/dist/modules/ambient/pipeline.d.ts +28 -0
  82. package/dist/modules/ambient/pipeline.d.ts.map +1 -0
  83. package/dist/modules/ambient/quantize.d.ts +22 -0
  84. package/dist/modules/ambient/quantize.d.ts.map +1 -0
  85. package/dist/modules/ambient/server/address.d.ts +8 -0
  86. package/dist/modules/ambient/server/address.d.ts.map +1 -0
  87. package/dist/modules/ambient/server/index.d.ts +53 -0
  88. package/dist/modules/ambient/server/index.d.ts.map +1 -0
  89. package/dist/modules/ambient/server/index.js +221 -0
  90. package/dist/modules/ambient/server/index.js.map +1 -0
  91. package/dist/modules/ambient/settings.d.ts +19 -0
  92. package/dist/modules/ambient/settings.d.ts.map +1 -0
  93. package/dist/modules/ambient/types.d.ts +105 -9
  94. package/dist/modules/ambient/types.d.ts.map +1 -1
  95. package/dist/modules/ambient/worker/index.d.ts +15 -0
  96. package/dist/modules/ambient/worker/index.d.ts.map +1 -0
  97. package/dist/modules/ambient/worker/index.js +114 -0
  98. package/dist/modules/ambient/worker/index.js.map +1 -0
  99. package/dist/modules/background/constants.d.ts +2 -2
  100. package/dist/modules/background/constants.d.ts.map +1 -1
  101. package/dist/modules/background/hooks.d.ts +1 -1
  102. package/dist/modules/background/index.js +6 -3078
  103. package/dist/modules/background/index.js.map +1 -1
  104. package/dist/modules/background/motion.d.ts +9 -7
  105. package/dist/modules/background/motion.d.ts.map +1 -1
  106. package/dist/modules/background/overlay.d.ts +3 -8
  107. package/dist/modules/background/overlay.d.ts.map +1 -1
  108. package/dist/modules/background/types.d.ts +16 -2
  109. package/dist/modules/background/types.d.ts.map +1 -1
  110. package/dist/modules/context-menu/constants.d.ts +2 -2
  111. package/dist/modules/context-menu/constants.d.ts.map +1 -1
  112. package/dist/modules/context-menu/hooks.d.ts +1 -1
  113. package/dist/modules/context-menu/index.js +10 -1896
  114. package/dist/modules/context-menu/index.js.map +1 -1
  115. package/dist/modules/context-menu/motion.d.ts +1 -8
  116. package/dist/modules/context-menu/motion.d.ts.map +1 -1
  117. package/dist/modules/context-menu/overlay.d.ts.map +1 -1
  118. package/dist/modules/context-menu/types.d.ts +16 -2
  119. package/dist/modules/context-menu/types.d.ts.map +1 -1
  120. package/dist/modules/controls/constants.d.ts +1 -1
  121. package/dist/modules/controls/constants.d.ts.map +1 -1
  122. package/dist/modules/controls/entries.d.ts +1 -1
  123. package/dist/modules/controls/hooks.d.ts +1 -1
  124. package/dist/modules/controls/index.js +4 -1110
  125. package/dist/modules/controls/index.js.map +1 -1
  126. package/dist/modules/controls/overlay.d.ts +1 -1
  127. package/dist/modules/controls/types.d.ts +2 -2
  128. package/dist/modules/dock/cards.d.ts +708 -24
  129. package/dist/modules/dock/cards.d.ts.map +1 -1
  130. package/dist/modules/dock/constants.d.ts +2 -2
  131. package/dist/modules/dock/constants.d.ts.map +1 -1
  132. package/dist/modules/dock/helpers.d.ts +2 -2
  133. package/dist/modules/dock/hooks.d.ts +2 -2
  134. package/dist/modules/dock/index.d.ts +1 -1
  135. package/dist/modules/dock/index.d.ts.map +1 -1
  136. package/dist/modules/dock/index.js +9 -12132
  137. package/dist/modules/dock/index.js.map +1 -1
  138. package/dist/modules/dock/module.d.ts +1 -1
  139. package/dist/modules/dock/motion.d.ts +41 -915
  140. package/dist/modules/dock/motion.d.ts.map +1 -1
  141. package/dist/modules/dock/overlay.d.ts.map +1 -1
  142. package/dist/modules/dock/runtime/dock.d.ts +1 -23
  143. package/dist/modules/dock/runtime/dock.d.ts.map +1 -1
  144. package/dist/modules/dock/styles.d.ts +4 -4
  145. package/dist/modules/dock/surface/hooks.d.ts +5 -20
  146. package/dist/modules/dock/surface/hooks.d.ts.map +1 -1
  147. package/dist/modules/dock/surface/machine.d.ts +4 -3
  148. package/dist/modules/dock/surface/machine.d.ts.map +1 -1
  149. package/dist/modules/dock/surface/stack.d.ts.map +1 -1
  150. package/dist/modules/dock/types.d.ts +158 -3
  151. package/dist/modules/dock/types.d.ts.map +1 -1
  152. package/dist/modules/index.js +16 -17883
  153. package/dist/modules/index.js.map +1 -1
  154. package/dist/modules/loading/constants.d.ts +1 -1
  155. package/dist/modules/loading/constants.d.ts.map +1 -1
  156. package/dist/modules/loading/context.d.ts +1 -1
  157. package/dist/modules/loading/hooks.d.ts +1 -1
  158. package/dist/modules/loading/index.js +5 -1153
  159. package/dist/modules/loading/index.js.map +1 -1
  160. package/dist/modules/loading/types.d.ts +2 -2
  161. package/dist/modules/media/context.d.ts +1 -1
  162. package/dist/modules/media/index.js +3 -570
  163. package/dist/modules/media/index.js.map +1 -1
  164. package/dist/modules/media/types.d.ts +2 -2
  165. package/dist/modules/modal/constants.d.ts +2 -2
  166. package/dist/modules/modal/constants.d.ts.map +1 -1
  167. package/dist/modules/modal/context.d.ts +1 -1
  168. package/dist/modules/modal/hooks.d.ts +3 -3
  169. package/dist/modules/modal/index.js +8 -2355
  170. package/dist/modules/modal/index.js.map +1 -1
  171. package/dist/modules/modal/motion.d.ts +3 -8
  172. package/dist/modules/modal/motion.d.ts.map +1 -1
  173. package/dist/modules/modal/overlay.d.ts.map +1 -1
  174. package/dist/modules/modal/types.d.ts +21 -0
  175. package/dist/modules/modal/types.d.ts.map +1 -1
  176. package/dist/modules/notification/constants.d.ts +2 -2
  177. package/dist/modules/notification/constants.d.ts.map +1 -1
  178. package/dist/modules/notification/context.d.ts +1 -1
  179. package/dist/modules/notification/hooks.d.ts +1 -1
  180. package/dist/modules/notification/index.js +4 -828
  181. package/dist/modules/notification/index.js.map +1 -1
  182. package/dist/modules/notification/motion.d.ts +1 -7
  183. package/dist/modules/notification/motion.d.ts.map +1 -1
  184. package/dist/modules/notification/overlay.d.ts.map +1 -1
  185. package/dist/modules/notification/types.d.ts +9 -1
  186. package/dist/modules/notification/types.d.ts.map +1 -1
  187. package/dist/overlay-TZ6L52A5.js +9 -0
  188. package/dist/overlay-TZ6L52A5.js.map +1 -0
  189. package/dist/provider.d.ts +1 -1
  190. package/dist/provider.d.ts.map +1 -1
  191. package/dist/provider.js +6 -1968
  192. package/dist/provider.js.map +1 -1
  193. package/dist/result.js +2 -63
  194. package/dist/result.js.map +1 -1
  195. package/dist/theme.d.ts +45 -8
  196. package/dist/theme.d.ts.map +1 -1
  197. package/dist/theme.js +3 -41
  198. package/dist/theme.js.map +1 -1
  199. package/dist/tokens/index.d.ts +1 -0
  200. package/dist/tokens/index.d.ts.map +1 -1
  201. package/dist/tokens/index.js +126 -155
  202. package/dist/tokens/index.js.map +1 -1
  203. package/dist/tokens/motion-config.d.ts +44 -0
  204. package/dist/tokens/motion-config.d.ts.map +1 -0
  205. package/dist/tokens/motion.d.ts +23 -174
  206. package/dist/tokens/motion.d.ts.map +1 -1
  207. package/dist/utils/index.js +4 -82
  208. package/dist/utils/index.js.map +1 -1
  209. package/dist/utils/report.d.ts.map +1 -1
  210. package/docs/README.md +1 -0
  211. package/docs/architecture-and-rules.md +4 -3
  212. package/docs/core-engine.md +7 -10
  213. package/docs/getting-started.md +4 -2
  214. package/docs/modules/ambient.md +194 -80
  215. package/docs/modules/background.md +3 -3
  216. package/docs/modules/context-menu.md +2 -2
  217. package/docs/modules/dock.md +8 -5
  218. package/docs/modules/modal.md +2 -2
  219. package/docs/modules/notification.md +2 -2
  220. package/docs/theming.md +81 -9
  221. package/package.json +22 -5
  222. package/templates/AGENTS.md +1 -1
  223. package/templates/motion/background.ts +17 -0
  224. package/templates/motion/context-menu.ts +63 -0
  225. package/templates/motion/dock.ts +327 -0
  226. package/templates/motion/modal.ts +79 -0
  227. package/templates/motion/notification.ts +27 -0
  228. package/templates/motion/tokens.ts +47 -0
  229. package/templates/tsconfig.json +7 -0
  230. package/dist/modules/ambient/color.d.ts +0 -21
  231. package/dist/modules/ambient/color.d.ts.map +0 -1
@@ -0,0 +1,577 @@
1
+ // src/modules/ambient/oklab.ts
2
+ var DEG = Math.PI / 180;
3
+ var LINEAR_LUT = new Float32Array(256);
4
+ for (let i = 0; i < 256; i++) {
5
+ const v = i / 255;
6
+ LINEAR_LUT[i] = v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
7
+ }
8
+ function clamp(value, min, max) {
9
+ return Math.max(min, Math.min(max, value));
10
+ }
11
+ function smoothstep(edge0, edge1, value) {
12
+ const t = clamp((value - edge0) / (edge1 - edge0), 0, 1);
13
+ return t * t * (3 - 2 * t);
14
+ }
15
+ function srgbToOklab(r, g, b) {
16
+ const lr = LINEAR_LUT[r & 255];
17
+ const lg = LINEAR_LUT[g & 255];
18
+ const lb = LINEAR_LUT[b & 255];
19
+ const l = Math.cbrt(0.4122214708 * lr + 0.5363325363 * lg + 0.0514459929 * lb);
20
+ const m = Math.cbrt(0.2119034982 * lr + 0.6806995451 * lg + 0.1073969566 * lb);
21
+ const s = Math.cbrt(0.0883024619 * lr + 0.2817188376 * lg + 0.6299787005 * lb);
22
+ return [
23
+ 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s,
24
+ 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s,
25
+ 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s
26
+ ];
27
+ }
28
+ function oklchToLinearSrgb(l, c, h) {
29
+ const a = c * Math.cos(h * DEG);
30
+ const b = c * Math.sin(h * DEG);
31
+ const l_ = (l + 0.3963377774 * a + 0.2158037573 * b) ** 3;
32
+ const m_ = (l - 0.1055613458 * a - 0.0638541728 * b) ** 3;
33
+ const s_ = (l - 0.0894841775 * a - 1.291485548 * b) ** 3;
34
+ return [
35
+ 4.0767416621 * l_ - 3.3077115913 * m_ + 0.2309699292 * s_,
36
+ -1.2684380046 * l_ + 2.6097574011 * m_ - 0.3413193965 * s_,
37
+ -0.0041960863 * l_ - 0.7034186147 * m_ + 1.707614701 * s_
38
+ ];
39
+ }
40
+ function labToLch([l, a, b]) {
41
+ const c = Math.sqrt(a * a + b * b);
42
+ const h = c < 1e-6 ? 0 : (Math.atan2(b, a) / DEG + 360) % 360;
43
+ return { c, h, l };
44
+ }
45
+ var inGamut = (rgb) => rgb.every((v) => v >= -1e-5 && v <= 1 + 1e-5);
46
+ function maxChroma(l, h) {
47
+ if (l <= 0 || l >= 1) return 0;
48
+ let low = 0;
49
+ let high = 0.4;
50
+ for (let step = 0; step < 18; step++) {
51
+ const middle = (low + high) / 2;
52
+ if (inGamut(oklchToLinearSrgb(l, middle, h))) low = middle;
53
+ else high = middle;
54
+ }
55
+ return low;
56
+ }
57
+ function hueDistance(a, b) {
58
+ const d = Math.abs(a - b) % 360;
59
+ return d > 180 ? 360 - d : d;
60
+ }
61
+ function luminance({ c, h, l }) {
62
+ const [r, g, b] = oklchToLinearSrgb(l, c, h).map((v) => clamp(v, 0, 1));
63
+ return 0.2126 * r + 0.7152 * g + 0.0722 * b;
64
+ }
65
+ function contrastRatio(a, b) {
66
+ const la = luminance(a);
67
+ const lb = luminance(b);
68
+ return (Math.max(la, lb) + 0.05) / (Math.min(la, lb) + 0.05);
69
+ }
70
+ var round = (value, digits) => {
71
+ const factor = 10 ** digits;
72
+ return Math.round(value * factor) / factor;
73
+ };
74
+ function formatOklch({ c, h, l }) {
75
+ const chroma = c < 5e-4 ? 0 : c;
76
+ return `oklch(${round(clamp(l, 0, 1), 3).toFixed(3)} ${round(chroma, 3).toFixed(3)} ${round(chroma === 0 ? 0 : h, 1).toFixed(1)})`;
77
+ }
78
+ function toneOf(h, l, rel, cap = 0.3) {
79
+ const room = maxChroma(l, h) * 0.96;
80
+ return { c: Math.min(cap, clamp(rel, 0, 1) * room), h, l };
81
+ }
82
+ function onColorFor(background, minContrast = 4.5) {
83
+ const light = (l) => toneOf(background.h, l, 0.12, 0.012);
84
+ const dark = (l) => toneOf(background.h, l, 0.35, 0.03);
85
+ const towardLight = contrastRatio(light(0.985), background);
86
+ const towardDark = contrastRatio(dark(0.18), background);
87
+ const useLight = towardLight >= minContrast || towardLight >= towardDark;
88
+ for (let step = 0; step <= 12; step++) {
89
+ const candidate = useLight ? light(Math.min(1, 0.985 + step * 25e-4)) : dark(Math.max(0, 0.18 - step * 0.015));
90
+ if (contrastRatio(candidate, background) >= minContrast) return candidate;
91
+ }
92
+ return useLight ? { c: 0, h: 0, l: 1 } : { c: 0, h: 0, l: 0 };
93
+ }
94
+
95
+ // src/modules/ambient/settings.ts
96
+ var AMBIENT_TONES = Object.freeze([
97
+ 10,
98
+ 15,
99
+ 20,
100
+ 30,
101
+ 40,
102
+ 50,
103
+ 60,
104
+ 70,
105
+ 80,
106
+ 90,
107
+ 95
108
+ ]);
109
+ var AMBIENT_REGIONS = Object.freeze([
110
+ "top",
111
+ "bottom",
112
+ "left",
113
+ "right",
114
+ "center"
115
+ ]);
116
+ var DEFAULT_ANALYSIS = Object.freeze({
117
+ centerWeight: 0.5,
118
+ clusters: 7,
119
+ letterbox: true,
120
+ minContrast: 4.5,
121
+ minHueDistance: 40,
122
+ sampleSize: 128,
123
+ shadowLightness: 0.16
124
+ });
125
+ var DEFAULT_CSS_VAR_NAMES = Object.freeze({
126
+ on: "--ambient-on-{role}",
127
+ region: "--ambient-{region}",
128
+ role: "--ambient-{role}",
129
+ shadow: "--ambient-shadow",
130
+ tone: "--ambient-{role}-{tone}"
131
+ });
132
+ var DEFAULT_PROXY = "/api/ambient/proxy?url=";
133
+ var createDefaults = () => ({
134
+ analysis: { ...DEFAULT_ANALYSIS },
135
+ cacheLimit: 100,
136
+ cssVars: { ...DEFAULT_CSS_VAR_NAMES, tones: AMBIENT_TONES },
137
+ proxy: DEFAULT_PROXY,
138
+ strategy: "auto",
139
+ worker: null
140
+ });
141
+ var SETTINGS_KEY = /* @__PURE__ */ Symbol.for("@omerdlw/base-framework/ambient/settings");
142
+ var scope = globalThis;
143
+ function getAmbientSettings() {
144
+ return scope[SETTINGS_KEY] ??= createDefaults();
145
+ }
146
+ function mergeAmbientSettings(base, input) {
147
+ return {
148
+ analysis: { ...base.analysis, ...input.analysis },
149
+ cacheLimit: input.cacheLimit ?? base.cacheLimit,
150
+ cssVars: { ...base.cssVars, ...input.cssVars },
151
+ proxy: input.proxy === void 0 ? base.proxy : input.proxy,
152
+ strategy: input.strategy ?? base.strategy,
153
+ worker: input.worker === void 0 ? base.worker : input.worker
154
+ };
155
+ }
156
+ function configureAmbient(input) {
157
+ const previous = getAmbientSettings();
158
+ const next = mergeAmbientSettings(previous, input);
159
+ scope[SETTINGS_KEY] = next;
160
+ return () => {
161
+ if (scope[SETTINGS_KEY] === next) scope[SETTINGS_KEY] = previous;
162
+ };
163
+ }
164
+ function resolveAnalysis(overrides) {
165
+ return { ...getAmbientSettings().analysis, ...overrides };
166
+ }
167
+
168
+ // src/modules/ambient/quantize.ts
169
+ function kmeans(points, weights, requested, { iterations = 16, lightnessWeight = 0.45 } = {}) {
170
+ const n = weights.length;
171
+ const assignment = new Int32Array(n);
172
+ const k = Math.max(0, Math.min(requested, n));
173
+ const centroids = new Float64Array(k * 3);
174
+ if (k === 0) return { assignment, centroids, k };
175
+ const distance = (i, j) => {
176
+ const dl = points[i * 3] - centroids[j * 3];
177
+ const da = points[i * 3 + 1] - centroids[j * 3 + 1];
178
+ const db = points[i * 3 + 2] - centroids[j * 3 + 2];
179
+ return lightnessWeight * dl * dl + da * da + db * db;
180
+ };
181
+ let first = 0;
182
+ for (let i = 1; i < n; i++) if (weights[i] > weights[first]) first = i;
183
+ centroids.set(points.subarray(first * 3, first * 3 + 3), 0);
184
+ const nearest = new Float64Array(n).fill(Infinity);
185
+ let found = 1;
186
+ for (; found < k; found++) {
187
+ let best = -1;
188
+ let bestScore = 0;
189
+ for (let i = 0; i < n; i++) {
190
+ const d = distance(i, found - 1);
191
+ if (d < nearest[i]) nearest[i] = d;
192
+ const score = weights[i] * nearest[i];
193
+ if (score > bestScore) {
194
+ bestScore = score;
195
+ best = i;
196
+ }
197
+ }
198
+ if (best < 0) break;
199
+ centroids.set(points.subarray(best * 3, best * 3 + 3), found * 3);
200
+ }
201
+ const sums = new Float64Array(found * 4);
202
+ for (let round2 = 0; round2 < iterations; round2++) {
203
+ let moved = round2 === 0;
204
+ for (let i = 0; i < n; i++) {
205
+ let best = 0;
206
+ let bestDistance = Infinity;
207
+ for (let j = 0; j < found; j++) {
208
+ const d = distance(i, j);
209
+ if (d < bestDistance) {
210
+ bestDistance = d;
211
+ best = j;
212
+ }
213
+ }
214
+ if (assignment[i] !== best) {
215
+ assignment[i] = best;
216
+ moved = true;
217
+ }
218
+ }
219
+ if (!moved) break;
220
+ sums.fill(0);
221
+ for (let i = 0; i < n; i++) {
222
+ const j = assignment[i] * 4;
223
+ const w = weights[i];
224
+ sums[j] += points[i * 3] * w;
225
+ sums[j + 1] += points[i * 3 + 1] * w;
226
+ sums[j + 2] += points[i * 3 + 2] * w;
227
+ sums[j + 3] += w;
228
+ }
229
+ for (let j = 0; j < found; j++) {
230
+ const w = sums[j * 4 + 3];
231
+ if (w <= 0) continue;
232
+ centroids[j * 3] = sums[j * 4] / w;
233
+ centroids[j * 3 + 1] = sums[j * 4 + 1] / w;
234
+ centroids[j * 3 + 2] = sums[j * 4 + 2] / w;
235
+ }
236
+ }
237
+ return { assignment, centroids: centroids.slice(0, found * 3), k: found };
238
+ }
239
+
240
+ // src/modules/ambient/analyze.ts
241
+ var MIN_CHROMA = 0.022;
242
+ var MIN_PRESENCE = 0.03;
243
+ var MIN_POPULATION = 4e-3;
244
+ var REGION_BITS = {
245
+ top: 1,
246
+ bottom: 2,
247
+ left: 4,
248
+ right: 8,
249
+ center: 16
250
+ };
251
+ var lightnessAt = (data, index) => srgbToOklab(data[index], data[index + 1], data[index + 2])[0];
252
+ function findContentBounds({ data, height, width }) {
253
+ const isBar = (count, at) => {
254
+ let sum = 0;
255
+ let max = 0;
256
+ let seen = 0;
257
+ for (let step = 0; step < count; step++) {
258
+ const index = at(step);
259
+ if (data[index + 3] < 128) continue;
260
+ const l = lightnessAt(data, index);
261
+ sum += l;
262
+ if (l > max) max = l;
263
+ seen++;
264
+ }
265
+ return seen > 0 && sum / seen < 0.12 && max < 0.2;
266
+ };
267
+ const row = (y) => isBar(width, (x) => (y * width + x) * 4);
268
+ const maxRows = Math.floor(height * 0.22);
269
+ const maxColumns = Math.floor(width * 0.22);
270
+ let top = 0;
271
+ while (top < maxRows && row(top)) top++;
272
+ let bottom = height;
273
+ while (height - bottom < maxRows && bottom - 1 > top && row(bottom - 1)) bottom--;
274
+ const column = (x) => isBar(bottom - top, (step) => ((top + step) * width + x) * 4);
275
+ let left = 0;
276
+ while (left < maxColumns && column(left)) left++;
277
+ let right = width;
278
+ while (width - right < maxColumns && right - 1 > left && column(right - 1)) right--;
279
+ return { bottom, left, right, top };
280
+ }
281
+ var reliability = (l) => smoothstep(0.1, 0.28, l) * (1 - smoothstep(0.9, 0.98, l));
282
+ var colorfulness = (c) => smoothstep(MIN_CHROMA, 0.06, c);
283
+ var isChromatic = (cluster) => cluster.c >= MIN_CHROMA && cluster.rely > 0;
284
+ function pickBest(items, score) {
285
+ let best = null;
286
+ let bestScore = 0;
287
+ for (const item of items) {
288
+ const value = score(item);
289
+ if (value > bestScore) {
290
+ bestScore = value;
291
+ best = item;
292
+ }
293
+ }
294
+ return best;
295
+ }
296
+ var round3 = (value) => Math.round(value * 1e3) / 1e3;
297
+ function buildSwatch(source, { cap, max, min, rel }, fallback, fromImage, minContrast) {
298
+ const h = source?.h ?? fallback.h;
299
+ const l = clamp(source?.l ?? fallback.l, min, max);
300
+ const tone = toneOf(h, l, rel, cap);
301
+ const ramp = {};
302
+ for (const step of AMBIENT_TONES) {
303
+ ramp[step] = formatOklch(toneOf(h, step / 100, rel, cap));
304
+ }
305
+ return {
306
+ c: round3(tone.c),
307
+ color: formatOklch(tone),
308
+ fromImage,
309
+ h: Math.round(h * 10) / 10,
310
+ l: round3(tone.l),
311
+ on: formatOklch(onColorFor(tone, minContrast)),
312
+ population: round3(source?.pop ?? 0),
313
+ ramp
314
+ };
315
+ }
316
+ var lift = (rel, base, gain, ceiling) => clamp(base + gain * rel, 0, ceiling);
317
+ function clusterAmbientPixels(pixels, options) {
318
+ const { data, width, height } = pixels;
319
+ const bounds = options.letterbox ? findContentBounds(pixels) : { bottom: height, left: 0, right: width, top: 0 };
320
+ const boxWidth = Math.max(1, bounds.right - bounds.left);
321
+ const boxHeight = Math.max(1, bounds.bottom - bounds.top);
322
+ const binOf = new Int32Array(32768).fill(-1);
323
+ const binSums = [];
324
+ const pixelBins = new Int32Array(boxWidth * boxHeight).fill(-1);
325
+ const pixelRegions = new Uint8Array(boxWidth * boxHeight);
326
+ const sigma2 = 2 * 0.35 * 0.35;
327
+ let total = 0;
328
+ let sumL = 0;
329
+ for (let y = bounds.top; y < bounds.bottom; y++) {
330
+ const v = (y - bounds.top + 0.5) / boxHeight;
331
+ for (let x = bounds.left; x < bounds.right; x++) {
332
+ const index = (y * width + x) * 4;
333
+ if (data[index + 3] < 128) continue;
334
+ const r = data[index];
335
+ const g = data[index + 1];
336
+ const b = data[index + 2];
337
+ const [l, a, bb] = srgbToOklab(r, g, b);
338
+ const u = (x - bounds.left + 0.5) / boxWidth;
339
+ const du = (u - 0.5) * 2;
340
+ const dv = (v - 0.5) * 2;
341
+ const weight = 1 - options.centerWeight + 2 * options.centerWeight * Math.exp(-(du * du + dv * dv) / sigma2);
342
+ const key = r >> 3 << 10 | g >> 3 << 5 | b >> 3;
343
+ let bin = binOf[key];
344
+ if (bin < 0) {
345
+ bin = binSums.length / 4;
346
+ binOf[key] = bin;
347
+ binSums.push(0, 0, 0, 0);
348
+ }
349
+ binSums[bin * 4] += l * weight;
350
+ binSums[bin * 4 + 1] += a * weight;
351
+ binSums[bin * 4 + 2] += bb * weight;
352
+ binSums[bin * 4 + 3] += weight;
353
+ total += weight;
354
+ sumL += l * weight;
355
+ const local = (y - bounds.top) * boxWidth + (x - bounds.left);
356
+ pixelBins[local] = bin;
357
+ pixelRegions[local] = (v < 1 / 3 ? REGION_BITS.top : 0) | (v > 2 / 3 ? REGION_BITS.bottom : 0) | (u < 1 / 3 ? REGION_BITS.left : 0) | (u > 2 / 3 ? REGION_BITS.right : 0) | (Math.abs(u - 0.5) < 0.25 && Math.abs(v - 0.5) < 0.25 ? REGION_BITS.center : 0);
358
+ }
359
+ }
360
+ const meanL = total > 0 ? sumL / total : 0.5;
361
+ const bins = binSums.length / 4;
362
+ const groups = [[], []];
363
+ for (let i = 0; i < bins; i++) {
364
+ const w = binSums[i * 4 + 3];
365
+ const a = binSums[i * 4 + 1] / w;
366
+ const b = binSums[i * 4 + 2] / w;
367
+ const coloured = Math.sqrt(a * a + b * b) >= MIN_CHROMA && binSums[i * 4] / w >= 0.06;
368
+ groups[coloured ? 0 : 1].push(i);
369
+ }
370
+ const requested = clamp(Math.round(options.clusters), 2, 16);
371
+ const greyClusters = groups[0].length === 0 ? requested : groups[1].length === 0 ? 0 : requested >= 8 ? 3 : 2;
372
+ const budgets = [requested - greyClusters, greyClusters];
373
+ const clusters = [];
374
+ const clusterOfBin = new Int32Array(bins).fill(-1);
375
+ groups.forEach((members, group) => {
376
+ if (members.length === 0 || budgets[group] <= 0) return;
377
+ const points = new Float64Array(members.length * 3);
378
+ const weights = new Float64Array(members.length);
379
+ members.forEach((bin, i) => {
380
+ const w = binSums[bin * 4 + 3];
381
+ const a = binSums[bin * 4 + 1] / w;
382
+ const b = binSums[bin * 4 + 2] / w;
383
+ points[i * 3] = binSums[bin * 4] / w;
384
+ points[i * 3 + 1] = a;
385
+ points[i * 3 + 2] = b;
386
+ weights[i] = w * (1 + 5 * Math.sqrt(a * a + b * b));
387
+ });
388
+ const { assignment, centroids, k } = kmeans(points, weights, budgets[group]);
389
+ const pop = new Float64Array(k);
390
+ members.forEach((bin, i) => pop[assignment[i]] += binSums[bin * 4 + 3]);
391
+ const index = new Int32Array(k).fill(-1);
392
+ for (let j = 0; j < k; j++) {
393
+ if (pop[j] <= 0) continue;
394
+ const lch = labToLch([centroids[j * 3], centroids[j * 3 + 1], centroids[j * 3 + 2]]);
395
+ const room = maxChroma(lch.l, lch.h);
396
+ index[j] = clusters.length;
397
+ clusters.push({
398
+ ...lch,
399
+ pop: pop[j] / total,
400
+ rel: room > 0 ? clamp(lch.c / room, 0, 1) : 0,
401
+ rely: reliability(lch.l)
402
+ });
403
+ }
404
+ members.forEach((bin, i) => clusterOfBin[bin] = index[assignment[i]]);
405
+ });
406
+ const regionWeights = AMBIENT_REGIONS.map(() => new Float64Array(clusters.length));
407
+ for (let i = 0; i < pixelBins.length; i++) {
408
+ const bin = pixelBins[i];
409
+ if (bin < 0) continue;
410
+ const cluster = clusterOfBin[bin];
411
+ const bits = pixelRegions[i];
412
+ AMBIENT_REGIONS.forEach((region, r) => {
413
+ if (bits & REGION_BITS[region]) regionWeights[r][cluster] += 1;
414
+ });
415
+ }
416
+ return { clusters, meanL, regionWeights };
417
+ }
418
+ function analyzeAmbientPixels(pixels, overrides = {}) {
419
+ const options = { ...DEFAULT_ANALYSIS, ...overrides };
420
+ const { clusters, meanL, regionWeights } = clusterAmbientPixels(pixels, options);
421
+ const chromatic = clusters.filter(isChromatic);
422
+ const presence = chromatic.reduce(
423
+ (sum, cluster) => sum + cluster.pop * colorfulness(cluster.c) * cluster.rely,
424
+ 0
425
+ );
426
+ const neutral = presence < MIN_PRESENCE;
427
+ const dilution = 0.55 + 0.45 * Math.min(1, presence / 0.25);
428
+ const { minContrast, minHueDistance } = options;
429
+ const grey = { h: 0, l: clamp(meanL, 0.3, 0.8) };
430
+ const family = /* @__PURE__ */ new Map();
431
+ for (const cluster of chromatic) {
432
+ let share2 = 0;
433
+ for (const other of chromatic) {
434
+ if (hueDistance(cluster.h, other.h) <= 20) share2 += other.pop;
435
+ }
436
+ family.set(cluster, share2);
437
+ }
438
+ const share = (cluster) => family.get(cluster) ?? cluster.pop;
439
+ const withShare = (cluster) => cluster ? { ...cluster, pop: share(cluster) } : null;
440
+ const dominantPick = neutral ? null : pickBest(chromatic, (cluster) => share(cluster) * cluster.c * cluster.rely);
441
+ const vividPick = neutral ? null : pickBest(
442
+ chromatic.filter((cluster) => share(cluster) >= MIN_POPULATION),
443
+ (cluster) => cluster.c * cluster.c * Math.pow(share(cluster), 0.3) * cluster.rely
444
+ );
445
+ const accentPick = pickBest(
446
+ chromatic.filter(
447
+ (cluster) => neutral ? share(cluster) >= 6e-3 && cluster.c >= 0.06 : share(cluster) >= MIN_POPULATION && cluster.c >= 0.035 && (!dominantPick || hueDistance(cluster.h, dominantPick.h) >= minHueDistance)
448
+ ),
449
+ (cluster) => Math.pow(share(cluster), 0.6) * cluster.c * cluster.rely
450
+ );
451
+ const mutedPick = neutral ? null : pickBest(
452
+ chromatic.filter((cluster) => cluster.c <= 0.09),
453
+ (cluster) => share(cluster) * cluster.rely * (1 - cluster.c / 0.12)
454
+ );
455
+ const dominantHue = dominantPick?.h ?? vividPick?.h ?? 0;
456
+ const roles = {
457
+ dominant: buildSwatch(
458
+ withShare(dominantPick),
459
+ {
460
+ cap: 0.22,
461
+ max: 0.74,
462
+ min: 0.42,
463
+ rel: dominantPick ? lift(dominantPick.rel, 0.25, 0.85, 0.9) : 0
464
+ },
465
+ grey,
466
+ true,
467
+ minContrast
468
+ ),
469
+ vibrant: buildSwatch(
470
+ withShare(vividPick),
471
+ {
472
+ cap: 0.24,
473
+ max: 0.84,
474
+ min: 0.62,
475
+ rel: vividPick ? lift(vividPick.rel, 0.4, 0.75, 0.95) * dilution : 0
476
+ },
477
+ { h: dominantHue, l: 0.78 },
478
+ true,
479
+ minContrast
480
+ ),
481
+ muted: buildSwatch(
482
+ withShare(mutedPick),
483
+ {
484
+ cap: 0.08,
485
+ max: 0.66,
486
+ min: 0.38,
487
+ rel: mutedPick ? clamp(mutedPick.rel, 0.1, 0.3) : dominantPick ? 0.2 : 0
488
+ },
489
+ { h: dominantHue, l: clamp(meanL, 0.38, 0.66) },
490
+ Boolean(mutedPick) || neutral,
491
+ minContrast
492
+ ),
493
+ accent: buildSwatch(
494
+ withShare(accentPick ?? vividPick),
495
+ {
496
+ cap: 0.24,
497
+ max: 0.8,
498
+ min: 0.55,
499
+ rel: accentPick ?? vividPick ? lift((accentPick ?? vividPick).rel, 0.35, 0.75, 0.92) * dilution : 0
500
+ },
501
+ { h: dominantHue, l: 0.72 },
502
+ Boolean(accentPick),
503
+ minContrast
504
+ )
505
+ };
506
+ const shadowPick = neutral ? null : pickBest(
507
+ clusters.filter(
508
+ (cluster) => cluster.l <= 0.5 && cluster.c >= 0.02 && cluster.pop >= 0.02
509
+ ),
510
+ (cluster) => cluster.pop * cluster.rel * smoothstep(0.05, 0.14, cluster.l)
511
+ );
512
+ const deepSource = shadowPick ?? dominantPick;
513
+ const shadowL = clamp(
514
+ options.shadowLightness + (meanL - 0.45) * 0.06,
515
+ options.shadowLightness - 0.02,
516
+ options.shadowLightness + 0.02
517
+ );
518
+ const shadow = deepSource ? toneOf(deepSource.h, shadowL, lift(deepSource.rel, 0.3, 0.7, 0.85), 0.1) : { c: 0, h: 0, l: shadowL };
519
+ const regions = {};
520
+ AMBIENT_REGIONS.forEach((region, r) => {
521
+ const weights = regionWeights[r];
522
+ let regionTotal = 0;
523
+ let regionL = 0;
524
+ clusters.forEach((cluster, j) => {
525
+ regionTotal += weights[j];
526
+ regionL += cluster.l * weights[j];
527
+ });
528
+ const share2 = (j) => regionTotal > 0 ? weights[j] / regionTotal : 0;
529
+ let strength = 0;
530
+ let pick = null;
531
+ let pickScore = 0;
532
+ clusters.forEach((cluster, j) => {
533
+ if (!isChromatic(cluster)) return;
534
+ strength += share2(j) * colorfulness(cluster.c) * cluster.rely;
535
+ const score = share2(j) * cluster.c * cluster.rely;
536
+ if (score > pickScore) {
537
+ pickScore = score;
538
+ pick = cluster;
539
+ }
540
+ });
541
+ const chosen = neutral || strength < MIN_PRESENCE ? null : pick;
542
+ const tone = chosen ? toneOf(
543
+ chosen.h,
544
+ clamp(chosen.l, 0.42, 0.72),
545
+ lift(chosen.rel, 0.25, 0.85, 0.9),
546
+ 0.22
547
+ ) : {
548
+ c: 0,
549
+ h: 0,
550
+ l: clamp(regionTotal > 0 ? regionL / regionTotal : meanL, 0.3, 0.7)
551
+ };
552
+ regions[region] = {
553
+ c: round3(tone.c),
554
+ color: formatOklch(tone),
555
+ h: Math.round(tone.h * 10) / 10,
556
+ l: round3(tone.l),
557
+ strength: round3(chosen ? clamp(strength, 0, 1) : 0)
558
+ };
559
+ });
560
+ return {
561
+ neutral,
562
+ regions,
563
+ roles,
564
+ shadow: formatOklch(shadow)
565
+ };
566
+ }
567
+ function fitSample(width, height, size) {
568
+ const scale = Math.min(1, size / Math.max(width, height, 1));
569
+ return {
570
+ height: Math.max(1, Math.round(height * scale)),
571
+ width: Math.max(1, Math.round(width * scale))
572
+ };
573
+ }
574
+
575
+ export { analyzeAmbientPixels, configureAmbient, contrastRatio, findContentBounds, fitSample, formatOklch, getAmbientSettings, maxChroma, onColorFor, resolveAnalysis, srgbToOklab, toneOf };
576
+ //# sourceMappingURL=chunk-6SG7V77J.js.map
577
+ //# sourceMappingURL=chunk-6SG7V77J.js.map