@omerdlw/base-framework 1.0.6 → 2.0.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 (234) 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-4GMQKD6F.js +353 -0
  6. package/dist/chunk-4GMQKD6F.js.map +1 -0
  7. package/dist/chunk-6SG7V77J.js +577 -0
  8. package/dist/chunk-6SG7V77J.js.map +1 -0
  9. package/dist/chunk-6ZJVLUBE.js +26 -0
  10. package/dist/chunk-6ZJVLUBE.js.map +1 -0
  11. package/dist/chunk-7HQMXTNP.js +419 -0
  12. package/dist/chunk-7HQMXTNP.js.map +1 -0
  13. package/dist/chunk-7TAVYJ3A.js +371 -0
  14. package/dist/chunk-7TAVYJ3A.js.map +1 -0
  15. package/dist/chunk-A5RE3F6O.js +9905 -0
  16. package/dist/chunk-A5RE3F6O.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-QI3ALLIB.js +38 -0
  32. package/dist/chunk-QI3ALLIB.js.map +1 -0
  33. package/dist/chunk-QXR55VVS.js +1139 -0
  34. package/dist/chunk-QXR55VVS.js.map +1 -0
  35. package/dist/chunk-R2YGFJDJ.js +408 -0
  36. package/dist/chunk-R2YGFJDJ.js.map +1 -0
  37. package/dist/chunk-RFZQWLC5.js +27 -0
  38. package/dist/chunk-RFZQWLC5.js.map +1 -0
  39. package/dist/chunk-SKBITB5X.js +888 -0
  40. package/dist/chunk-SKBITB5X.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 +711 -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/layout.d.ts.map +1 -1
  139. package/dist/modules/dock/module.d.ts +1 -1
  140. package/dist/modules/dock/motion.d.ts +44 -912
  141. package/dist/modules/dock/motion.d.ts.map +1 -1
  142. package/dist/modules/dock/overlay.d.ts +1 -1
  143. package/dist/modules/dock/overlay.d.ts.map +1 -1
  144. package/dist/modules/dock/runtime/dock.d.ts +1 -23
  145. package/dist/modules/dock/runtime/dock.d.ts.map +1 -1
  146. package/dist/modules/dock/styles.d.ts +4 -4
  147. package/dist/modules/dock/surface/hooks.d.ts +5 -20
  148. package/dist/modules/dock/surface/hooks.d.ts.map +1 -1
  149. package/dist/modules/dock/surface/machine.d.ts +4 -3
  150. package/dist/modules/dock/surface/machine.d.ts.map +1 -1
  151. package/dist/modules/dock/surface/stack.d.ts.map +1 -1
  152. package/dist/modules/dock/surface/view-model.d.ts.map +1 -1
  153. package/dist/modules/dock/types.d.ts +162 -3
  154. package/dist/modules/dock/types.d.ts.map +1 -1
  155. package/dist/modules/index.js +16 -17883
  156. package/dist/modules/index.js.map +1 -1
  157. package/dist/modules/loading/constants.d.ts +1 -1
  158. package/dist/modules/loading/constants.d.ts.map +1 -1
  159. package/dist/modules/loading/context.d.ts +1 -1
  160. package/dist/modules/loading/hooks.d.ts +1 -1
  161. package/dist/modules/loading/index.js +5 -1153
  162. package/dist/modules/loading/index.js.map +1 -1
  163. package/dist/modules/loading/types.d.ts +2 -2
  164. package/dist/modules/media/context.d.ts +1 -1
  165. package/dist/modules/media/index.js +3 -570
  166. package/dist/modules/media/index.js.map +1 -1
  167. package/dist/modules/media/types.d.ts +2 -2
  168. package/dist/modules/modal/constants.d.ts +2 -2
  169. package/dist/modules/modal/constants.d.ts.map +1 -1
  170. package/dist/modules/modal/context.d.ts +1 -1
  171. package/dist/modules/modal/hooks.d.ts +3 -3
  172. package/dist/modules/modal/index.js +8 -2355
  173. package/dist/modules/modal/index.js.map +1 -1
  174. package/dist/modules/modal/motion.d.ts +3 -8
  175. package/dist/modules/modal/motion.d.ts.map +1 -1
  176. package/dist/modules/modal/overlay.d.ts.map +1 -1
  177. package/dist/modules/modal/types.d.ts +21 -0
  178. package/dist/modules/modal/types.d.ts.map +1 -1
  179. package/dist/modules/notification/constants.d.ts +2 -2
  180. package/dist/modules/notification/constants.d.ts.map +1 -1
  181. package/dist/modules/notification/context.d.ts +1 -1
  182. package/dist/modules/notification/hooks.d.ts +1 -1
  183. package/dist/modules/notification/index.js +4 -828
  184. package/dist/modules/notification/index.js.map +1 -1
  185. package/dist/modules/notification/motion.d.ts +1 -7
  186. package/dist/modules/notification/motion.d.ts.map +1 -1
  187. package/dist/modules/notification/overlay.d.ts.map +1 -1
  188. package/dist/modules/notification/types.d.ts +9 -1
  189. package/dist/modules/notification/types.d.ts.map +1 -1
  190. package/dist/overlay-TZ6L52A5.js +9 -0
  191. package/dist/overlay-TZ6L52A5.js.map +1 -0
  192. package/dist/provider.d.ts +1 -1
  193. package/dist/provider.d.ts.map +1 -1
  194. package/dist/provider.js +6 -1968
  195. package/dist/provider.js.map +1 -1
  196. package/dist/result.js +2 -63
  197. package/dist/result.js.map +1 -1
  198. package/dist/theme.d.ts +45 -8
  199. package/dist/theme.d.ts.map +1 -1
  200. package/dist/theme.js +3 -41
  201. package/dist/theme.js.map +1 -1
  202. package/dist/tokens/index.d.ts +1 -0
  203. package/dist/tokens/index.d.ts.map +1 -1
  204. package/dist/tokens/index.js +126 -155
  205. package/dist/tokens/index.js.map +1 -1
  206. package/dist/tokens/motion-config.d.ts +44 -0
  207. package/dist/tokens/motion-config.d.ts.map +1 -0
  208. package/dist/tokens/motion.d.ts +23 -174
  209. package/dist/tokens/motion.d.ts.map +1 -1
  210. package/dist/utils/index.js +4 -82
  211. package/dist/utils/index.js.map +1 -1
  212. package/dist/utils/report.d.ts.map +1 -1
  213. package/docs/README.md +1 -0
  214. package/docs/architecture-and-rules.md +4 -3
  215. package/docs/core-engine.md +7 -10
  216. package/docs/getting-started.md +4 -2
  217. package/docs/modules/ambient.md +194 -80
  218. package/docs/modules/background.md +3 -3
  219. package/docs/modules/context-menu.md +2 -2
  220. package/docs/modules/dock.md +9 -6
  221. package/docs/modules/modal.md +2 -2
  222. package/docs/modules/notification.md +2 -2
  223. package/docs/theming.md +81 -9
  224. package/package.json +22 -5
  225. package/templates/AGENTS.md +1 -1
  226. package/templates/motion/background.ts +17 -0
  227. package/templates/motion/context-menu.ts +63 -0
  228. package/templates/motion/dock.ts +327 -0
  229. package/templates/motion/modal.ts +79 -0
  230. package/templates/motion/notification.ts +27 -0
  231. package/templates/motion/tokens.ts +47 -0
  232. package/templates/tsconfig.json +7 -0
  233. package/dist/modules/ambient/color.d.ts +0 -21
  234. package/dist/modules/ambient/color.d.ts.map +0 -1
@@ -0,0 +1,1139 @@
1
+ import { useOptionalBackgroundState } from './chunk-CGHZSSFR.js';
2
+ import { defineThemeSpec, useTheme } from './chunk-7TAVYJ3A.js';
3
+ import { getOrCreateGlobalContext, defineModule } from './chunk-GF7TUX5I.js';
4
+ import { createStore, shallowEqual, useRequiredContext, useStore } from './chunk-TV3WEF5G.js';
5
+ import { useMemo, useState, use, useEffect, useRef } from 'react';
6
+ import { jsx } from 'react/jsx-runtime';
7
+
8
+ var AmbientContext = getOrCreateGlobalContext("AmbientContext", null);
9
+ function AmbientProvider({
10
+ children,
11
+ initialPalette = null
12
+ }) {
13
+ const [store] = useState(
14
+ () => createStore(
15
+ { isExtracting: false, palette: initialPalette },
16
+ { freezeSnapshots: false }
17
+ )
18
+ );
19
+ const value = useMemo(
20
+ () => ({
21
+ actions: {
22
+ setIsExtracting: (next) => store.publish((state) => {
23
+ const isExtracting = typeof next === "function" ? next(state.isExtracting) : next;
24
+ return isExtracting === state.isExtracting ? state : { ...state, isExtracting };
25
+ }),
26
+ setPalette: (next) => store.publish((state) => {
27
+ const palette = typeof next === "function" ? next(state.palette) : next;
28
+ return palette === state.palette ? state : { ...state, palette };
29
+ })
30
+ },
31
+ store
32
+ }),
33
+ [store]
34
+ );
35
+ return /* @__PURE__ */ jsx(AmbientContext, { value, children });
36
+ }
37
+
38
+ // src/modules/ambient/constants.ts
39
+ var ambientTheme = defineThemeSpec("ambient");
40
+
41
+ // src/modules/ambient/oklab.ts
42
+ var DEG = Math.PI / 180;
43
+ var LINEAR_LUT = new Float32Array(256);
44
+ for (let i = 0; i < 256; i++) {
45
+ const v = i / 255;
46
+ LINEAR_LUT[i] = v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
47
+ }
48
+ function clamp(value, min, max) {
49
+ return Math.max(min, Math.min(max, value));
50
+ }
51
+ function smoothstep(edge0, edge1, value) {
52
+ const t = clamp((value - edge0) / (edge1 - edge0), 0, 1);
53
+ return t * t * (3 - 2 * t);
54
+ }
55
+ function srgbToOklab(r, g, b) {
56
+ const lr = LINEAR_LUT[r & 255];
57
+ const lg = LINEAR_LUT[g & 255];
58
+ const lb = LINEAR_LUT[b & 255];
59
+ const l = Math.cbrt(0.4122214708 * lr + 0.5363325363 * lg + 0.0514459929 * lb);
60
+ const m = Math.cbrt(0.2119034982 * lr + 0.6806995451 * lg + 0.1073969566 * lb);
61
+ const s = Math.cbrt(0.0883024619 * lr + 0.2817188376 * lg + 0.6299787005 * lb);
62
+ return [
63
+ 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s,
64
+ 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s,
65
+ 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s
66
+ ];
67
+ }
68
+ function oklchToLinearSrgb(l, c, h) {
69
+ const a = c * Math.cos(h * DEG);
70
+ const b = c * Math.sin(h * DEG);
71
+ const l_ = (l + 0.3963377774 * a + 0.2158037573 * b) ** 3;
72
+ const m_ = (l - 0.1055613458 * a - 0.0638541728 * b) ** 3;
73
+ const s_ = (l - 0.0894841775 * a - 1.291485548 * b) ** 3;
74
+ return [
75
+ 4.0767416621 * l_ - 3.3077115913 * m_ + 0.2309699292 * s_,
76
+ -1.2684380046 * l_ + 2.6097574011 * m_ - 0.3413193965 * s_,
77
+ -0.0041960863 * l_ - 0.7034186147 * m_ + 1.707614701 * s_
78
+ ];
79
+ }
80
+ function labToLch([l, a, b]) {
81
+ const c = Math.sqrt(a * a + b * b);
82
+ const h = c < 1e-6 ? 0 : (Math.atan2(b, a) / DEG + 360) % 360;
83
+ return { c, h, l };
84
+ }
85
+ var inGamut = (rgb) => rgb.every((v) => v >= -1e-5 && v <= 1 + 1e-5);
86
+ function maxChroma(l, h) {
87
+ if (l <= 0 || l >= 1) return 0;
88
+ let low = 0;
89
+ let high = 0.4;
90
+ for (let step = 0; step < 18; step++) {
91
+ const middle = (low + high) / 2;
92
+ if (inGamut(oklchToLinearSrgb(l, middle, h))) low = middle;
93
+ else high = middle;
94
+ }
95
+ return low;
96
+ }
97
+ function hueDistance(a, b) {
98
+ const d = Math.abs(a - b) % 360;
99
+ return d > 180 ? 360 - d : d;
100
+ }
101
+ function luminance({ c, h, l }) {
102
+ const [r, g, b] = oklchToLinearSrgb(l, c, h).map((v) => clamp(v, 0, 1));
103
+ return 0.2126 * r + 0.7152 * g + 0.0722 * b;
104
+ }
105
+ function contrastRatio(a, b) {
106
+ const la = luminance(a);
107
+ const lb = luminance(b);
108
+ return (Math.max(la, lb) + 0.05) / (Math.min(la, lb) + 0.05);
109
+ }
110
+ var round = (value, digits) => {
111
+ const factor = 10 ** digits;
112
+ return Math.round(value * factor) / factor;
113
+ };
114
+ function formatOklch({ c, h, l }) {
115
+ const chroma = c < 5e-4 ? 0 : c;
116
+ return `oklch(${round(clamp(l, 0, 1), 3).toFixed(3)} ${round(chroma, 3).toFixed(3)} ${round(chroma === 0 ? 0 : h, 1).toFixed(1)})`;
117
+ }
118
+ function toneOf(h, l, rel, cap = 0.3) {
119
+ const room = maxChroma(l, h) * 0.96;
120
+ return { c: Math.min(cap, clamp(rel, 0, 1) * room), h, l };
121
+ }
122
+ function onColorFor(background, minContrast = 4.5) {
123
+ const light = (l) => toneOf(background.h, l, 0.12, 0.012);
124
+ const dark = (l) => toneOf(background.h, l, 0.35, 0.03);
125
+ const towardLight = contrastRatio(light(0.985), background);
126
+ const towardDark = contrastRatio(dark(0.18), background);
127
+ const useLight = towardLight >= minContrast || towardLight >= towardDark;
128
+ for (let step = 0; step <= 12; step++) {
129
+ const candidate = useLight ? light(Math.min(1, 0.985 + step * 25e-4)) : dark(Math.max(0, 0.18 - step * 0.015));
130
+ if (contrastRatio(candidate, background) >= minContrast) return candidate;
131
+ }
132
+ return useLight ? { c: 0, h: 0, l: 1 } : { c: 0, h: 0, l: 0 };
133
+ }
134
+
135
+ // src/modules/ambient/settings.ts
136
+ var AMBIENT_TONES = Object.freeze([
137
+ 10,
138
+ 15,
139
+ 20,
140
+ 30,
141
+ 40,
142
+ 50,
143
+ 60,
144
+ 70,
145
+ 80,
146
+ 90,
147
+ 95
148
+ ]);
149
+ var AMBIENT_ROLES = Object.freeze([
150
+ "dominant",
151
+ "vibrant",
152
+ "muted",
153
+ "accent"
154
+ ]);
155
+ var AMBIENT_REGIONS = Object.freeze([
156
+ "top",
157
+ "bottom",
158
+ "left",
159
+ "right",
160
+ "center"
161
+ ]);
162
+ var DEFAULT_ANALYSIS = Object.freeze({
163
+ centerWeight: 0.5,
164
+ clusters: 7,
165
+ letterbox: true,
166
+ minContrast: 4.5,
167
+ minHueDistance: 40,
168
+ sampleSize: 128,
169
+ shadowLightness: 0.16
170
+ });
171
+ var DEFAULT_CSS_VAR_NAMES = Object.freeze({
172
+ on: "--ambient-on-{role}",
173
+ region: "--ambient-{region}",
174
+ role: "--ambient-{role}",
175
+ shadow: "--ambient-shadow",
176
+ tone: "--ambient-{role}-{tone}"
177
+ });
178
+ var DEFAULT_PROXY = "/api/ambient/proxy?url=";
179
+ var createDefaults = () => ({
180
+ analysis: { ...DEFAULT_ANALYSIS },
181
+ cacheLimit: 100,
182
+ cssVars: { ...DEFAULT_CSS_VAR_NAMES, tones: AMBIENT_TONES },
183
+ proxy: DEFAULT_PROXY,
184
+ strategy: "auto",
185
+ worker: null
186
+ });
187
+ var SETTINGS_KEY = /* @__PURE__ */ Symbol.for("@omerdlw/base-framework/ambient/settings");
188
+ var scope = globalThis;
189
+ function getAmbientSettings() {
190
+ return scope[SETTINGS_KEY] ??= createDefaults();
191
+ }
192
+ function mergeAmbientSettings(base, input) {
193
+ return {
194
+ analysis: { ...base.analysis, ...input.analysis },
195
+ cacheLimit: input.cacheLimit ?? base.cacheLimit,
196
+ cssVars: { ...base.cssVars, ...input.cssVars },
197
+ proxy: input.proxy === void 0 ? base.proxy : input.proxy,
198
+ strategy: input.strategy ?? base.strategy,
199
+ worker: input.worker === void 0 ? base.worker : input.worker
200
+ };
201
+ }
202
+ function configureAmbient(input) {
203
+ const previous = getAmbientSettings();
204
+ const next = mergeAmbientSettings(previous, input);
205
+ scope[SETTINGS_KEY] = next;
206
+ return () => {
207
+ if (scope[SETTINGS_KEY] === next) scope[SETTINGS_KEY] = previous;
208
+ };
209
+ }
210
+ function resolveAnalysis(overrides) {
211
+ return { ...getAmbientSettings().analysis, ...overrides };
212
+ }
213
+ function resolveProxyUrl(proxy, src) {
214
+ if (!proxy || !/^https?:\/\//i.test(src)) return null;
215
+ if (typeof proxy === "function") return proxy(src);
216
+ const encoded = encodeURIComponent(src);
217
+ return proxy.includes("{url}") ? proxy.replace("{url}", encoded) : `${proxy}${encoded}`;
218
+ }
219
+
220
+ // src/modules/ambient/quantize.ts
221
+ function kmeans(points, weights, requested, { iterations = 16, lightnessWeight = 0.45 } = {}) {
222
+ const n = weights.length;
223
+ const assignment = new Int32Array(n);
224
+ const k = Math.max(0, Math.min(requested, n));
225
+ const centroids = new Float64Array(k * 3);
226
+ if (k === 0) return { assignment, centroids, k };
227
+ const distance = (i, j) => {
228
+ const dl = points[i * 3] - centroids[j * 3];
229
+ const da = points[i * 3 + 1] - centroids[j * 3 + 1];
230
+ const db = points[i * 3 + 2] - centroids[j * 3 + 2];
231
+ return lightnessWeight * dl * dl + da * da + db * db;
232
+ };
233
+ let first = 0;
234
+ for (let i = 1; i < n; i++) if (weights[i] > weights[first]) first = i;
235
+ centroids.set(points.subarray(first * 3, first * 3 + 3), 0);
236
+ const nearest = new Float64Array(n).fill(Infinity);
237
+ let found = 1;
238
+ for (; found < k; found++) {
239
+ let best = -1;
240
+ let bestScore = 0;
241
+ for (let i = 0; i < n; i++) {
242
+ const d = distance(i, found - 1);
243
+ if (d < nearest[i]) nearest[i] = d;
244
+ const score = weights[i] * nearest[i];
245
+ if (score > bestScore) {
246
+ bestScore = score;
247
+ best = i;
248
+ }
249
+ }
250
+ if (best < 0) break;
251
+ centroids.set(points.subarray(best * 3, best * 3 + 3), found * 3);
252
+ }
253
+ const sums = new Float64Array(found * 4);
254
+ for (let round2 = 0; round2 < iterations; round2++) {
255
+ let moved = round2 === 0;
256
+ for (let i = 0; i < n; i++) {
257
+ let best = 0;
258
+ let bestDistance = Infinity;
259
+ for (let j = 0; j < found; j++) {
260
+ const d = distance(i, j);
261
+ if (d < bestDistance) {
262
+ bestDistance = d;
263
+ best = j;
264
+ }
265
+ }
266
+ if (assignment[i] !== best) {
267
+ assignment[i] = best;
268
+ moved = true;
269
+ }
270
+ }
271
+ if (!moved) break;
272
+ sums.fill(0);
273
+ for (let i = 0; i < n; i++) {
274
+ const j = assignment[i] * 4;
275
+ const w = weights[i];
276
+ sums[j] += points[i * 3] * w;
277
+ sums[j + 1] += points[i * 3 + 1] * w;
278
+ sums[j + 2] += points[i * 3 + 2] * w;
279
+ sums[j + 3] += w;
280
+ }
281
+ for (let j = 0; j < found; j++) {
282
+ const w = sums[j * 4 + 3];
283
+ if (w <= 0) continue;
284
+ centroids[j * 3] = sums[j * 4] / w;
285
+ centroids[j * 3 + 1] = sums[j * 4 + 1] / w;
286
+ centroids[j * 3 + 2] = sums[j * 4 + 2] / w;
287
+ }
288
+ }
289
+ return { assignment, centroids: centroids.slice(0, found * 3), k: found };
290
+ }
291
+
292
+ // src/modules/ambient/analyze.ts
293
+ var MIN_CHROMA = 0.022;
294
+ var MIN_PRESENCE = 0.03;
295
+ var MIN_POPULATION = 4e-3;
296
+ var REGION_BITS = {
297
+ top: 1,
298
+ bottom: 2,
299
+ left: 4,
300
+ right: 8,
301
+ center: 16
302
+ };
303
+ var lightnessAt = (data, index) => srgbToOklab(data[index], data[index + 1], data[index + 2])[0];
304
+ function findContentBounds({ data, height, width }) {
305
+ const isBar = (count, at) => {
306
+ let sum = 0;
307
+ let max = 0;
308
+ let seen = 0;
309
+ for (let step = 0; step < count; step++) {
310
+ const index = at(step);
311
+ if (data[index + 3] < 128) continue;
312
+ const l = lightnessAt(data, index);
313
+ sum += l;
314
+ if (l > max) max = l;
315
+ seen++;
316
+ }
317
+ return seen > 0 && sum / seen < 0.12 && max < 0.2;
318
+ };
319
+ const row = (y) => isBar(width, (x) => (y * width + x) * 4);
320
+ const maxRows = Math.floor(height * 0.22);
321
+ const maxColumns = Math.floor(width * 0.22);
322
+ let top = 0;
323
+ while (top < maxRows && row(top)) top++;
324
+ let bottom = height;
325
+ while (height - bottom < maxRows && bottom - 1 > top && row(bottom - 1)) bottom--;
326
+ const column = (x) => isBar(bottom - top, (step) => ((top + step) * width + x) * 4);
327
+ let left = 0;
328
+ while (left < maxColumns && column(left)) left++;
329
+ let right = width;
330
+ while (width - right < maxColumns && right - 1 > left && column(right - 1)) right--;
331
+ return { bottom, left, right, top };
332
+ }
333
+ var reliability = (l) => smoothstep(0.1, 0.28, l) * (1 - smoothstep(0.9, 0.98, l));
334
+ var colorfulness = (c) => smoothstep(MIN_CHROMA, 0.06, c);
335
+ var isChromatic = (cluster) => cluster.c >= MIN_CHROMA && cluster.rely > 0;
336
+ function pickBest(items, score) {
337
+ let best = null;
338
+ let bestScore = 0;
339
+ for (const item of items) {
340
+ const value = score(item);
341
+ if (value > bestScore) {
342
+ bestScore = value;
343
+ best = item;
344
+ }
345
+ }
346
+ return best;
347
+ }
348
+ var round3 = (value) => Math.round(value * 1e3) / 1e3;
349
+ function buildSwatch(source, { cap, max, min, rel }, fallback, fromImage, minContrast) {
350
+ const h = source?.h ?? fallback.h;
351
+ const l = clamp(source?.l ?? fallback.l, min, max);
352
+ const tone = toneOf(h, l, rel, cap);
353
+ const ramp = {};
354
+ for (const step of AMBIENT_TONES) {
355
+ ramp[step] = formatOklch(toneOf(h, step / 100, rel, cap));
356
+ }
357
+ return {
358
+ c: round3(tone.c),
359
+ color: formatOklch(tone),
360
+ fromImage,
361
+ h: Math.round(h * 10) / 10,
362
+ l: round3(tone.l),
363
+ on: formatOklch(onColorFor(tone, minContrast)),
364
+ population: round3(source?.pop ?? 0),
365
+ ramp
366
+ };
367
+ }
368
+ var lift = (rel, base, gain, ceiling) => clamp(base + gain * rel, 0, ceiling);
369
+ function clusterAmbientPixels(pixels, options) {
370
+ const { data, width, height } = pixels;
371
+ const bounds = options.letterbox ? findContentBounds(pixels) : { bottom: height, left: 0, right: width, top: 0 };
372
+ const boxWidth = Math.max(1, bounds.right - bounds.left);
373
+ const boxHeight = Math.max(1, bounds.bottom - bounds.top);
374
+ const binOf = new Int32Array(32768).fill(-1);
375
+ const binSums = [];
376
+ const pixelBins = new Int32Array(boxWidth * boxHeight).fill(-1);
377
+ const pixelRegions = new Uint8Array(boxWidth * boxHeight);
378
+ const sigma2 = 2 * 0.35 * 0.35;
379
+ let total = 0;
380
+ let sumL = 0;
381
+ for (let y = bounds.top; y < bounds.bottom; y++) {
382
+ const v = (y - bounds.top + 0.5) / boxHeight;
383
+ for (let x = bounds.left; x < bounds.right; x++) {
384
+ const index = (y * width + x) * 4;
385
+ if (data[index + 3] < 128) continue;
386
+ const r = data[index];
387
+ const g = data[index + 1];
388
+ const b = data[index + 2];
389
+ const [l, a, bb] = srgbToOklab(r, g, b);
390
+ const u = (x - bounds.left + 0.5) / boxWidth;
391
+ const du = (u - 0.5) * 2;
392
+ const dv = (v - 0.5) * 2;
393
+ const weight = 1 - options.centerWeight + 2 * options.centerWeight * Math.exp(-(du * du + dv * dv) / sigma2);
394
+ const key = r >> 3 << 10 | g >> 3 << 5 | b >> 3;
395
+ let bin = binOf[key];
396
+ if (bin < 0) {
397
+ bin = binSums.length / 4;
398
+ binOf[key] = bin;
399
+ binSums.push(0, 0, 0, 0);
400
+ }
401
+ binSums[bin * 4] += l * weight;
402
+ binSums[bin * 4 + 1] += a * weight;
403
+ binSums[bin * 4 + 2] += bb * weight;
404
+ binSums[bin * 4 + 3] += weight;
405
+ total += weight;
406
+ sumL += l * weight;
407
+ const local = (y - bounds.top) * boxWidth + (x - bounds.left);
408
+ pixelBins[local] = bin;
409
+ 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);
410
+ }
411
+ }
412
+ const meanL = total > 0 ? sumL / total : 0.5;
413
+ const bins = binSums.length / 4;
414
+ const groups = [[], []];
415
+ for (let i = 0; i < bins; i++) {
416
+ const w = binSums[i * 4 + 3];
417
+ const a = binSums[i * 4 + 1] / w;
418
+ const b = binSums[i * 4 + 2] / w;
419
+ const coloured = Math.sqrt(a * a + b * b) >= MIN_CHROMA && binSums[i * 4] / w >= 0.06;
420
+ groups[coloured ? 0 : 1].push(i);
421
+ }
422
+ const requested = clamp(Math.round(options.clusters), 2, 16);
423
+ const greyClusters = groups[0].length === 0 ? requested : groups[1].length === 0 ? 0 : requested >= 8 ? 3 : 2;
424
+ const budgets = [requested - greyClusters, greyClusters];
425
+ const clusters = [];
426
+ const clusterOfBin = new Int32Array(bins).fill(-1);
427
+ groups.forEach((members, group) => {
428
+ if (members.length === 0 || budgets[group] <= 0) return;
429
+ const points = new Float64Array(members.length * 3);
430
+ const weights = new Float64Array(members.length);
431
+ members.forEach((bin, i) => {
432
+ const w = binSums[bin * 4 + 3];
433
+ const a = binSums[bin * 4 + 1] / w;
434
+ const b = binSums[bin * 4 + 2] / w;
435
+ points[i * 3] = binSums[bin * 4] / w;
436
+ points[i * 3 + 1] = a;
437
+ points[i * 3 + 2] = b;
438
+ weights[i] = w * (1 + 5 * Math.sqrt(a * a + b * b));
439
+ });
440
+ const { assignment, centroids, k } = kmeans(points, weights, budgets[group]);
441
+ const pop = new Float64Array(k);
442
+ members.forEach((bin, i) => pop[assignment[i]] += binSums[bin * 4 + 3]);
443
+ const index = new Int32Array(k).fill(-1);
444
+ for (let j = 0; j < k; j++) {
445
+ if (pop[j] <= 0) continue;
446
+ const lch = labToLch([centroids[j * 3], centroids[j * 3 + 1], centroids[j * 3 + 2]]);
447
+ const room = maxChroma(lch.l, lch.h);
448
+ index[j] = clusters.length;
449
+ clusters.push({
450
+ ...lch,
451
+ pop: pop[j] / total,
452
+ rel: room > 0 ? clamp(lch.c / room, 0, 1) : 0,
453
+ rely: reliability(lch.l)
454
+ });
455
+ }
456
+ members.forEach((bin, i) => clusterOfBin[bin] = index[assignment[i]]);
457
+ });
458
+ const regionWeights = AMBIENT_REGIONS.map(() => new Float64Array(clusters.length));
459
+ for (let i = 0; i < pixelBins.length; i++) {
460
+ const bin = pixelBins[i];
461
+ if (bin < 0) continue;
462
+ const cluster = clusterOfBin[bin];
463
+ const bits = pixelRegions[i];
464
+ AMBIENT_REGIONS.forEach((region, r) => {
465
+ if (bits & REGION_BITS[region]) regionWeights[r][cluster] += 1;
466
+ });
467
+ }
468
+ return { clusters, meanL, regionWeights };
469
+ }
470
+ function analyzeAmbientPixels(pixels, overrides = {}) {
471
+ const options = { ...DEFAULT_ANALYSIS, ...overrides };
472
+ const { clusters, meanL, regionWeights } = clusterAmbientPixels(pixels, options);
473
+ const chromatic = clusters.filter(isChromatic);
474
+ const presence = chromatic.reduce(
475
+ (sum, cluster) => sum + cluster.pop * colorfulness(cluster.c) * cluster.rely,
476
+ 0
477
+ );
478
+ const neutral = presence < MIN_PRESENCE;
479
+ const dilution = 0.55 + 0.45 * Math.min(1, presence / 0.25);
480
+ const { minContrast, minHueDistance } = options;
481
+ const grey = { h: 0, l: clamp(meanL, 0.3, 0.8) };
482
+ const family = /* @__PURE__ */ new Map();
483
+ for (const cluster of chromatic) {
484
+ let share2 = 0;
485
+ for (const other of chromatic) {
486
+ if (hueDistance(cluster.h, other.h) <= 20) share2 += other.pop;
487
+ }
488
+ family.set(cluster, share2);
489
+ }
490
+ const share = (cluster) => family.get(cluster) ?? cluster.pop;
491
+ const withShare = (cluster) => cluster ? { ...cluster, pop: share(cluster) } : null;
492
+ const dominantPick = neutral ? null : pickBest(chromatic, (cluster) => share(cluster) * cluster.c * cluster.rely);
493
+ const vividPick = neutral ? null : pickBest(
494
+ chromatic.filter((cluster) => share(cluster) >= MIN_POPULATION),
495
+ (cluster) => cluster.c * cluster.c * Math.pow(share(cluster), 0.3) * cluster.rely
496
+ );
497
+ const accentPick = pickBest(
498
+ chromatic.filter(
499
+ (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)
500
+ ),
501
+ (cluster) => Math.pow(share(cluster), 0.6) * cluster.c * cluster.rely
502
+ );
503
+ const mutedPick = neutral ? null : pickBest(
504
+ chromatic.filter((cluster) => cluster.c <= 0.09),
505
+ (cluster) => share(cluster) * cluster.rely * (1 - cluster.c / 0.12)
506
+ );
507
+ const dominantHue = dominantPick?.h ?? vividPick?.h ?? 0;
508
+ const roles = {
509
+ dominant: buildSwatch(
510
+ withShare(dominantPick),
511
+ {
512
+ cap: 0.22,
513
+ max: 0.74,
514
+ min: 0.42,
515
+ rel: dominantPick ? lift(dominantPick.rel, 0.25, 0.85, 0.9) : 0
516
+ },
517
+ grey,
518
+ true,
519
+ minContrast
520
+ ),
521
+ vibrant: buildSwatch(
522
+ withShare(vividPick),
523
+ {
524
+ cap: 0.24,
525
+ max: 0.84,
526
+ min: 0.62,
527
+ rel: vividPick ? lift(vividPick.rel, 0.4, 0.75, 0.95) * dilution : 0
528
+ },
529
+ { h: dominantHue, l: 0.78 },
530
+ true,
531
+ minContrast
532
+ ),
533
+ muted: buildSwatch(
534
+ withShare(mutedPick),
535
+ {
536
+ cap: 0.08,
537
+ max: 0.66,
538
+ min: 0.38,
539
+ rel: mutedPick ? clamp(mutedPick.rel, 0.1, 0.3) : dominantPick ? 0.2 : 0
540
+ },
541
+ { h: dominantHue, l: clamp(meanL, 0.38, 0.66) },
542
+ Boolean(mutedPick) || neutral,
543
+ minContrast
544
+ ),
545
+ accent: buildSwatch(
546
+ withShare(accentPick ?? vividPick),
547
+ {
548
+ cap: 0.24,
549
+ max: 0.8,
550
+ min: 0.55,
551
+ rel: accentPick ?? vividPick ? lift((accentPick ?? vividPick).rel, 0.35, 0.75, 0.92) * dilution : 0
552
+ },
553
+ { h: dominantHue, l: 0.72 },
554
+ Boolean(accentPick),
555
+ minContrast
556
+ )
557
+ };
558
+ const shadowPick = neutral ? null : pickBest(
559
+ clusters.filter(
560
+ (cluster) => cluster.l <= 0.5 && cluster.c >= 0.02 && cluster.pop >= 0.02
561
+ ),
562
+ (cluster) => cluster.pop * cluster.rel * smoothstep(0.05, 0.14, cluster.l)
563
+ );
564
+ const deepSource = shadowPick ?? dominantPick;
565
+ const shadowL = clamp(
566
+ options.shadowLightness + (meanL - 0.45) * 0.06,
567
+ options.shadowLightness - 0.02,
568
+ options.shadowLightness + 0.02
569
+ );
570
+ const shadow = deepSource ? toneOf(deepSource.h, shadowL, lift(deepSource.rel, 0.3, 0.7, 0.85), 0.1) : { c: 0, h: 0, l: shadowL };
571
+ const regions = {};
572
+ AMBIENT_REGIONS.forEach((region, r) => {
573
+ const weights = regionWeights[r];
574
+ let regionTotal = 0;
575
+ let regionL = 0;
576
+ clusters.forEach((cluster, j) => {
577
+ regionTotal += weights[j];
578
+ regionL += cluster.l * weights[j];
579
+ });
580
+ const share2 = (j) => regionTotal > 0 ? weights[j] / regionTotal : 0;
581
+ let strength = 0;
582
+ let pick = null;
583
+ let pickScore = 0;
584
+ clusters.forEach((cluster, j) => {
585
+ if (!isChromatic(cluster)) return;
586
+ strength += share2(j) * colorfulness(cluster.c) * cluster.rely;
587
+ const score = share2(j) * cluster.c * cluster.rely;
588
+ if (score > pickScore) {
589
+ pickScore = score;
590
+ pick = cluster;
591
+ }
592
+ });
593
+ const chosen = neutral || strength < MIN_PRESENCE ? null : pick;
594
+ const tone = chosen ? toneOf(
595
+ chosen.h,
596
+ clamp(chosen.l, 0.42, 0.72),
597
+ lift(chosen.rel, 0.25, 0.85, 0.9),
598
+ 0.22
599
+ ) : {
600
+ c: 0,
601
+ h: 0,
602
+ l: clamp(regionTotal > 0 ? regionL / regionTotal : meanL, 0.3, 0.7)
603
+ };
604
+ regions[region] = {
605
+ c: round3(tone.c),
606
+ color: formatOklch(tone),
607
+ h: Math.round(tone.h * 10) / 10,
608
+ l: round3(tone.l),
609
+ strength: round3(chosen ? clamp(strength, 0, 1) : 0)
610
+ };
611
+ });
612
+ return {
613
+ neutral,
614
+ regions,
615
+ roles,
616
+ shadow: formatOklch(shadow)
617
+ };
618
+ }
619
+ function fitSample(width, height, size) {
620
+ const scale = Math.min(1, size / Math.max(width, height, 1));
621
+ return {
622
+ height: Math.max(1, Math.round(height * scale)),
623
+ width: Math.max(1, Math.round(width * scale))
624
+ };
625
+ }
626
+
627
+ // src/modules/ambient/pipeline.ts
628
+ async function fetchImageBlob(src, proxyUrl) {
629
+ if (typeof fetch !== "function") return null;
630
+ const read = async (url, init) => {
631
+ const response = await fetch(url, { ...init, mode: "cors" });
632
+ if (!response.ok) return null;
633
+ const blob = await response.blob();
634
+ return blob.type.startsWith("text/") ? null : blob;
635
+ };
636
+ try {
637
+ const blob = await read(src);
638
+ if (blob) return blob;
639
+ } catch {
640
+ if (isCrossOrigin(src)) {
641
+ try {
642
+ const blob = await read(src, { cache: "reload" });
643
+ if (blob) return blob;
644
+ } catch {
645
+ }
646
+ }
647
+ }
648
+ if (proxyUrl) {
649
+ try {
650
+ return await read(proxyUrl);
651
+ } catch {
652
+ }
653
+ }
654
+ return null;
655
+ }
656
+ function isCrossOrigin(url) {
657
+ try {
658
+ return new URL(url).origin !== globalThis.location?.origin;
659
+ } catch {
660
+ return false;
661
+ }
662
+ }
663
+ function createContext2D(width, height) {
664
+ if (typeof OffscreenCanvas === "function") {
665
+ return new OffscreenCanvas(width, height).getContext("2d", {
666
+ willReadFrequently: true
667
+ });
668
+ }
669
+ if (typeof document !== "undefined") {
670
+ const canvas = document.createElement("canvas");
671
+ canvas.width = width;
672
+ canvas.height = height;
673
+ return canvas.getContext("2d", { willReadFrequently: true });
674
+ }
675
+ return null;
676
+ }
677
+ function readPixels(image, sourceWidth, sourceHeight, size) {
678
+ const { height, width } = fitSample(sourceWidth, sourceHeight, size);
679
+ const context = createContext2D(width, height);
680
+ if (!context) return null;
681
+ context.drawImage(image, 0, 0, width, height);
682
+ return { data: context.getImageData(0, 0, width, height).data, height, width };
683
+ }
684
+ async function decodeBlob(blob, size) {
685
+ if (typeof createImageBitmap !== "function") return null;
686
+ let bitmap;
687
+ try {
688
+ bitmap = await createImageBitmap(blob, {
689
+ resizeQuality: "medium",
690
+ resizeWidth: size
691
+ });
692
+ } catch {
693
+ bitmap = await createImageBitmap(blob);
694
+ }
695
+ try {
696
+ return readPixels(bitmap, bitmap.width, bitmap.height, size);
697
+ } finally {
698
+ bitmap.close();
699
+ }
700
+ }
701
+ var canDecodeHere = () => typeof createImageBitmap === "function" && (typeof OffscreenCanvas === "function" || typeof document !== "undefined");
702
+ async function extractHere(request) {
703
+ if (!canDecodeHere()) return { supported: false };
704
+ const blob = await fetchImageBlob(request.src, request.proxyUrl);
705
+ if (!blob) return { palette: null, supported: true };
706
+ try {
707
+ const pixels = await decodeBlob(blob, request.analysis.sampleSize);
708
+ return {
709
+ palette: pixels ? analyzeAmbientPixels(pixels, request.analysis) : null,
710
+ supported: true
711
+ };
712
+ } catch {
713
+ return { palette: null, supported: true };
714
+ }
715
+ }
716
+
717
+ // src/modules/ambient/worker/index.ts
718
+ var AMBIENT_WORKER_REQUEST = "ambient:extract";
719
+ var AMBIENT_WORKER_RESPONSE = "ambient:palette";
720
+
721
+ // src/modules/ambient/extractor.ts
722
+ var WORKER_PATIENCE = 15e3;
723
+ var paletteCache = /* @__PURE__ */ new Map();
724
+ var inFlight = /* @__PURE__ */ new Map();
725
+ function cachePalette(key, palette) {
726
+ paletteCache.delete(key);
727
+ const limit = Math.max(1, getAmbientSettings().cacheLimit);
728
+ while (paletteCache.size >= limit) {
729
+ const oldest = paletteCache.keys().next().value;
730
+ if (oldest === void 0) break;
731
+ paletteCache.delete(oldest);
732
+ }
733
+ paletteCache.set(key, palette);
734
+ }
735
+ function primeAmbientPalette(src, palette) {
736
+ if (src && palette) cachePalette(src, { ...palette, source: src });
737
+ }
738
+ function getCachedAmbientPalette(src) {
739
+ return paletteCache.get(src) ?? null;
740
+ }
741
+ var slot = null;
742
+ var brokenFactories = /* @__PURE__ */ new WeakSet();
743
+ function dropWorker(target) {
744
+ if (slot === target) slot = null;
745
+ target.worker.terminate();
746
+ for (const resolve of target.pending.values()) resolve(null);
747
+ target.pending.clear();
748
+ }
749
+ function workerSlot(factory) {
750
+ if (slot?.factory === factory) return slot;
751
+ if (slot) dropWorker(slot);
752
+ if (brokenFactories.has(factory)) return null;
753
+ let worker;
754
+ try {
755
+ worker = factory();
756
+ } catch {
757
+ brokenFactories.add(factory);
758
+ return null;
759
+ }
760
+ const created = { factory, next: 1, pending: /* @__PURE__ */ new Map(), worker };
761
+ worker.addEventListener("message", (event) => {
762
+ const message = event.data;
763
+ if (message?.type !== AMBIENT_WORKER_RESPONSE) return;
764
+ created.pending.get(message.id)?.(message);
765
+ created.pending.delete(message.id);
766
+ });
767
+ worker.addEventListener("error", () => {
768
+ brokenFactories.add(factory);
769
+ dropWorker(created);
770
+ });
771
+ slot = created;
772
+ return created;
773
+ }
774
+ function extractInWorker(factory, request) {
775
+ const target = workerSlot(factory);
776
+ if (!target) return Promise.resolve(null);
777
+ const id = target.next++;
778
+ return new Promise((resolve) => {
779
+ const timer = setTimeout(() => {
780
+ target.pending.delete(id);
781
+ resolve(null);
782
+ }, WORKER_PATIENCE);
783
+ target.pending.set(id, (result) => {
784
+ clearTimeout(timer);
785
+ resolve(result);
786
+ });
787
+ target.worker.postMessage({ ...request, id, type: AMBIENT_WORKER_REQUEST });
788
+ });
789
+ }
790
+ function loadImageElement(url) {
791
+ return new Promise((resolve) => {
792
+ const image = new Image();
793
+ image.onload = () => resolve(image);
794
+ image.onerror = () => resolve(null);
795
+ image.src = url;
796
+ });
797
+ }
798
+ async function extractOnMainThread(request) {
799
+ const result = await extractHere(request);
800
+ if (result.supported) return result.palette;
801
+ const blob = await fetchImageBlob(request.src, request.proxyUrl);
802
+ if (!blob || typeof URL.createObjectURL !== "function") return null;
803
+ const objectUrl = URL.createObjectURL(blob);
804
+ try {
805
+ const image = await loadImageElement(objectUrl);
806
+ const pixels = image && readPixels(
807
+ image,
808
+ image.naturalWidth,
809
+ image.naturalHeight,
810
+ request.analysis.sampleSize
811
+ );
812
+ return pixels ? analyzeAmbientPixels(pixels, request.analysis) : null;
813
+ } catch {
814
+ return null;
815
+ } finally {
816
+ URL.revokeObjectURL(objectUrl);
817
+ }
818
+ }
819
+ function absoluteUrl(src) {
820
+ try {
821
+ return new URL(src, globalThis.location?.href).href;
822
+ } catch {
823
+ return src;
824
+ }
825
+ }
826
+ var isCrossOrigin2 = (url) => {
827
+ try {
828
+ return new URL(url).origin !== globalThis.location?.origin;
829
+ } catch {
830
+ return false;
831
+ }
832
+ };
833
+ async function extractPalette(src, options) {
834
+ const settings = getAmbientSettings();
835
+ const strategy = options.strategy ?? settings.strategy;
836
+ const absolute = absoluteUrl(src);
837
+ const proxy = options.proxy === void 0 ? settings.proxy : options.proxy;
838
+ const request = {
839
+ analysis: resolveAnalysis(options.analysis),
840
+ proxyUrl: isCrossOrigin2(absolute) ? (() => {
841
+ const proxied = resolveProxyUrl(proxy, absolute);
842
+ return proxied ? absoluteUrl(proxied) : null;
843
+ })() : null,
844
+ src: absolute
845
+ };
846
+ if (strategy !== "main" && settings.worker && typeof Worker === "function") {
847
+ const result = await extractInWorker(settings.worker, request);
848
+ if (result?.supported) return result.palette;
849
+ }
850
+ if (strategy === "worker") return null;
851
+ return extractOnMainThread(request);
852
+ }
853
+ async function extractPaletteFromImage(imageSource, options = {}) {
854
+ const src = typeof imageSource === "string" ? imageSource : imageSource?.src;
855
+ if (!src || typeof window === "undefined") return null;
856
+ const strategy = options.strategy ?? getAmbientSettings().strategy;
857
+ if (strategy === "none") return getCachedAmbientPalette(src);
858
+ const key = options.analysis ? `${src}#${JSON.stringify(options.analysis)}` : src;
859
+ const cached = paletteCache.get(key);
860
+ if (cached) return cached;
861
+ let pending = inFlight.get(key);
862
+ if (!pending) {
863
+ pending = extractPalette(src, options).catch(() => null).then((palette) => {
864
+ if (!palette) return null;
865
+ const sourced = { ...palette, source: src };
866
+ cachePalette(key, sourced);
867
+ return sourced;
868
+ }).finally(() => inFlight.delete(key));
869
+ inFlight.set(key, pending);
870
+ }
871
+ return pending;
872
+ }
873
+
874
+ // src/modules/ambient/dom.ts
875
+ function resolveCssVarNames(overrides) {
876
+ const base = getAmbientSettings().cssVars;
877
+ return overrides ? { ...base, ...overrides } : base;
878
+ }
879
+ var fill = (template, values) => template.replace(
880
+ /\{(\w+)\}/g,
881
+ (match, key) => key in values ? String(values[key]) : match
882
+ );
883
+ function resolveTargetElement(target) {
884
+ if (typeof document === "undefined") return null;
885
+ if (!target) return document.documentElement;
886
+ if (typeof target === "string") {
887
+ return document.querySelector(target);
888
+ }
889
+ return "current" in target ? target.current : target;
890
+ }
891
+ function normalizeColorMap(colors) {
892
+ const result = {};
893
+ for (const [key, value] of Object.entries(colors)) {
894
+ if (!value) continue;
895
+ result[key.startsWith("--") ? key : `--color-${key}`] = value;
896
+ }
897
+ return result;
898
+ }
899
+ function applyScopedCssVariables(element, varMap) {
900
+ const previous = /* @__PURE__ */ new Map();
901
+ for (const [name, value] of Object.entries(varMap)) {
902
+ if (!name || !value) continue;
903
+ previous.set(name, {
904
+ priority: element.style.getPropertyPriority(name),
905
+ value: element.style.getPropertyValue(name)
906
+ });
907
+ element.style.setProperty(name, value);
908
+ }
909
+ return () => {
910
+ for (const [name, { priority, value }] of previous) {
911
+ if (value) element.style.setProperty(name, value, priority);
912
+ else element.style.removeProperty(name);
913
+ }
914
+ };
915
+ }
916
+ function paletteVarMap(palette, names = resolveCssVarNames()) {
917
+ const varMap = {};
918
+ if (!palette) return varMap;
919
+ if (names.shadow) varMap[names.shadow] = palette.shadow;
920
+ for (const role of AMBIENT_ROLES) {
921
+ const swatch = palette.roles[role];
922
+ if (names.role) varMap[fill(names.role, { role })] = swatch.color;
923
+ if (names.on) varMap[fill(names.on, { role })] = swatch.on;
924
+ if (names.tone && names.tones) {
925
+ for (const tone of names.tones) {
926
+ varMap[fill(names.tone, { role, tone })] = swatch.ramp[tone];
927
+ }
928
+ }
929
+ }
930
+ if (names.region) {
931
+ for (const region of AMBIENT_REGIONS) {
932
+ varMap[fill(names.region, { region })] = palette.regions[region].color;
933
+ }
934
+ }
935
+ return varMap;
936
+ }
937
+ function resolveAmbientVarMap({
938
+ colors = null,
939
+ cssVars = null,
940
+ image = null,
941
+ palette = null
942
+ }) {
943
+ const varMap = image ? paletteVarMap(palette, resolveCssVarNames(cssVars)) : {};
944
+ if (colors) Object.assign(varMap, normalizeColorMap(colors));
945
+ return varMap;
946
+ }
947
+ function useStableObject(value) {
948
+ const ref = useRef(value);
949
+ if (!shallowEqual(ref.current, value)) {
950
+ ref.current = value;
951
+ }
952
+ return ref.current;
953
+ }
954
+ function useAmbientColor(imageSource, options = null) {
955
+ const stableOptions = useStableObject(options);
956
+ const initialPalette = useStableObject(options?.initialPalette ?? null);
957
+ const srcKey = typeof imageSource === "string" ? imageSource : imageSource?.src;
958
+ const seeded = srcKey && initialPalette?.source === srcKey ? initialPalette : null;
959
+ const [result, setResult] = useState(null);
960
+ const settled = result?.source === srcKey ? result : null;
961
+ const isExtracting = Boolean(imageSource && !seeded && !settled);
962
+ useEffect(() => {
963
+ if (seeded && srcKey) primeAmbientPalette(srcKey, seeded);
964
+ }, [seeded, srcKey]);
965
+ useEffect(() => {
966
+ if (!imageSource || !srcKey || seeded) return;
967
+ let active = true;
968
+ void extractPaletteFromImage(imageSource, stableOptions ?? {}).catch(() => null).then((palette2) => {
969
+ if (active) setResult({ palette: palette2, source: srcKey });
970
+ });
971
+ return () => {
972
+ active = false;
973
+ };
974
+ }, [imageSource, seeded, srcKey, stableOptions]);
975
+ const palette = imageSource ? seeded ?? settled?.palette ?? initialPalette : initialPalette;
976
+ return { isExtracting, palette };
977
+ }
978
+ function useAmbientTheme(config = null) {
979
+ const ambientActions = use(AmbientContext)?.actions ?? null;
980
+ const normalizedConfig = typeof config === "string" ? { image: config } : config || {};
981
+ const {
982
+ colors = null,
983
+ cssVars = null,
984
+ image = null,
985
+ initialPalette = null,
986
+ options = null,
987
+ scope: scope2 = null,
988
+ transition = true
989
+ } = normalizedConfig;
990
+ const stableInitialPalette = useStableObject(initialPalette);
991
+ const stableOptions = useStableObject(options);
992
+ const stableColors = useStableObject(colors);
993
+ const stableCssVars = useStableObject(cssVars);
994
+ const resolvedOptions = useMemo(
995
+ () => ({
996
+ ...stableOptions || {},
997
+ ...stableInitialPalette ? { initialPalette: stableInitialPalette } : {}
998
+ }),
999
+ [stableInitialPalette, stableOptions]
1000
+ );
1001
+ const { palette: extractedPalette, isExtracting } = useAmbientColor(
1002
+ image,
1003
+ resolvedOptions
1004
+ );
1005
+ const hasActiveConfig = Boolean(image || stableColors);
1006
+ useEffect(() => {
1007
+ if (!hasActiveConfig || !ambientActions) return;
1008
+ ambientActions.setIsExtracting(isExtracting);
1009
+ ambientActions.setPalette(
1010
+ (prev) => shallowEqual(prev, extractedPalette) ? prev : extractedPalette
1011
+ );
1012
+ }, [ambientActions, extractedPalette, hasActiveConfig, isExtracting]);
1013
+ const transitionClasses = useTheme(ambientTheme).slots.transition.split(/\s+/).filter(Boolean);
1014
+ const transitionKey = transitionClasses.join(" ");
1015
+ const resolvedVarMap = useMemo(
1016
+ () => resolveAmbientVarMap({
1017
+ colors: stableColors,
1018
+ cssVars: stableCssVars,
1019
+ image,
1020
+ palette: extractedPalette
1021
+ }),
1022
+ [extractedPalette, image, stableColors, stableCssVars]
1023
+ );
1024
+ useEffect(() => {
1025
+ if (Object.keys(resolvedVarMap).length === 0) return;
1026
+ const targetEl = resolveTargetElement(scope2);
1027
+ if (!targetEl) return;
1028
+ if (transition) {
1029
+ targetEl.classList.add(...transitionKey.split(" ").filter(Boolean));
1030
+ }
1031
+ const restoreVariables = applyScopedCssVariables(targetEl, resolvedVarMap);
1032
+ return () => {
1033
+ restoreVariables();
1034
+ if (transition && scope2) {
1035
+ targetEl.classList.remove(...transitionKey.split(" ").filter(Boolean));
1036
+ }
1037
+ };
1038
+ }, [resolvedVarMap, scope2, transition, transitionKey]);
1039
+ return { isExtracting, palette: extractedPalette };
1040
+ }
1041
+ function useAmbient(configOrImage) {
1042
+ const { actions, store } = useRequiredContext(
1043
+ AmbientContext,
1044
+ "useAmbient",
1045
+ "AmbientProvider"
1046
+ );
1047
+ const shared = useStore(store);
1048
+ const ctx = useMemo(
1049
+ () => ({ ...shared, ...actions }),
1050
+ [actions, shared]
1051
+ );
1052
+ const { isExtracting, palette } = useAmbientTheme(configOrImage ?? null);
1053
+ const themeState = useMemo(
1054
+ () => ({ isExtracting, palette }),
1055
+ [isExtracting, palette]
1056
+ );
1057
+ return useMemo(
1058
+ () => configOrImage ? { ...ctx, ...themeState } : ctx,
1059
+ [configOrImage, ctx, themeState]
1060
+ );
1061
+ }
1062
+ var firstString = (...values) => values.find((value) => typeof value === "string" && value) ?? null;
1063
+ function resolvePageAmbientTheme(slice, posterUrl) {
1064
+ const ambient = slice?.ambient;
1065
+ const banner = slice?.banner ?? null;
1066
+ const defaultImage = banner ?? posterUrl;
1067
+ if (ambient === false) return null;
1068
+ if (ambient === true || typeof ambient === "string") {
1069
+ return { image: typeof ambient === "string" ? ambient : defaultImage };
1070
+ }
1071
+ if (ambient && typeof ambient === "object") {
1072
+ return { image: ambient.image ?? defaultImage, ...ambient };
1073
+ }
1074
+ if (banner && !posterUrl) return { image: banner };
1075
+ return null;
1076
+ }
1077
+ function selectPageAmbient(config) {
1078
+ return {
1079
+ ambient: config.ambient,
1080
+ banner: firstString(config.dock?.banner, config.dock?.bannerUrl)
1081
+ };
1082
+ }
1083
+ function useAmbientPage(slice, page) {
1084
+ const posterUrl = useOptionalBackgroundState(
1085
+ (background) => background.posterUrl
1086
+ );
1087
+ const theme = useMemo(
1088
+ () => resolvePageAmbientTheme(slice, posterUrl),
1089
+ [posterUrl, slice]
1090
+ );
1091
+ useAmbientTheme(theme);
1092
+ return useMemo(
1093
+ () => ({ set: (ambient) => page.set({ ambient }) }),
1094
+ [page]
1095
+ );
1096
+ }
1097
+ var ambientModule = defineModule({
1098
+ id: "ambient",
1099
+ context: AmbientContext,
1100
+ Provider: AmbientProvider,
1101
+ uses: ["background"],
1102
+ page: {
1103
+ select: selectPageAmbient,
1104
+ use: useAmbientPage
1105
+ }
1106
+ });
1107
+ function resolveValue(value, props) {
1108
+ return typeof value === "function" ? value(props) : value;
1109
+ }
1110
+ function defineAmbient(definition = {}) {
1111
+ const { defaultProps, id = "ambient-theme", ...config } = definition;
1112
+ const create = (props = {}, { id: overrideId, ...overrides } = {}) => {
1113
+ const mergedProps = { ...defaultProps, ...props };
1114
+ const merged = { ...config, ...overrides };
1115
+ return {
1116
+ ...merged,
1117
+ colors: resolveValue(merged.colors ?? null, mergedProps) ?? null,
1118
+ id: overrideId ?? id,
1119
+ image: resolveValue(merged.image ?? null, mergedProps),
1120
+ initialPalette: merged.initialPalette ?? null,
1121
+ options: { ...config.options, ...overrides.options },
1122
+ props: mergedProps,
1123
+ scope: merged.scope ?? null,
1124
+ transition: merged.transition ?? true
1125
+ };
1126
+ };
1127
+ return Object.freeze({
1128
+ config: definition,
1129
+ create,
1130
+ id,
1131
+ use: function useDefinedAmbient(props, overrides) {
1132
+ return useAmbientTheme(create(props, overrides));
1133
+ }
1134
+ });
1135
+ }
1136
+
1137
+ export { AMBIENT_REGIONS, AMBIENT_ROLES, AMBIENT_TONES, AmbientContext, AmbientProvider, DEFAULT_ANALYSIS, DEFAULT_CSS_VAR_NAMES, ambientModule, ambientTheme, analyzeAmbientPixels, applyScopedCssVariables, configureAmbient, contrastRatio, defineAmbient, extractPaletteFromImage, findContentBounds, formatOklch, getAmbientSettings, getCachedAmbientPalette, maxChroma, normalizeColorMap, onColorFor, paletteVarMap, primeAmbientPalette, resolveAmbientVarMap, resolveCssVarNames, resolvePageAmbientTheme, resolveTargetElement, srgbToOklab, toneOf, useAmbient, useAmbientColor, useAmbientTheme };
1138
+ //# sourceMappingURL=chunk-QXR55VVS.js.map
1139
+ //# sourceMappingURL=chunk-QXR55VVS.js.map