@omerdlw/base-framework 1.0.5 → 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 (232) 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 -20019
  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 +12 -4
  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 -753
  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 -12130
  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/context.d.ts.map +1 -1
  146. package/dist/modules/dock/surface/hooks.d.ts +10 -23
  147. package/dist/modules/dock/surface/hooks.d.ts.map +1 -1
  148. package/dist/modules/dock/surface/machine.d.ts +4 -3
  149. package/dist/modules/dock/surface/machine.d.ts.map +1 -1
  150. package/dist/modules/dock/surface/stack.d.ts.map +1 -1
  151. package/dist/modules/dock/types.d.ts +158 -3
  152. package/dist/modules/dock/types.d.ts.map +1 -1
  153. package/dist/modules/index.js +16 -17873
  154. package/dist/modules/index.js.map +1 -1
  155. package/dist/modules/loading/constants.d.ts +1 -1
  156. package/dist/modules/loading/constants.d.ts.map +1 -1
  157. package/dist/modules/loading/context.d.ts +1 -1
  158. package/dist/modules/loading/hooks.d.ts +1 -1
  159. package/dist/modules/loading/index.js +5 -1153
  160. package/dist/modules/loading/index.js.map +1 -1
  161. package/dist/modules/loading/types.d.ts +2 -2
  162. package/dist/modules/media/context.d.ts +1 -1
  163. package/dist/modules/media/index.js +3 -570
  164. package/dist/modules/media/index.js.map +1 -1
  165. package/dist/modules/media/types.d.ts +2 -2
  166. package/dist/modules/modal/constants.d.ts +2 -2
  167. package/dist/modules/modal/constants.d.ts.map +1 -1
  168. package/dist/modules/modal/context.d.ts +1 -1
  169. package/dist/modules/modal/hooks.d.ts +3 -3
  170. package/dist/modules/modal/index.js +8 -2355
  171. package/dist/modules/modal/index.js.map +1 -1
  172. package/dist/modules/modal/motion.d.ts +3 -8
  173. package/dist/modules/modal/motion.d.ts.map +1 -1
  174. package/dist/modules/modal/overlay.d.ts.map +1 -1
  175. package/dist/modules/modal/types.d.ts +21 -0
  176. package/dist/modules/modal/types.d.ts.map +1 -1
  177. package/dist/modules/notification/constants.d.ts +2 -2
  178. package/dist/modules/notification/constants.d.ts.map +1 -1
  179. package/dist/modules/notification/context.d.ts +1 -1
  180. package/dist/modules/notification/hooks.d.ts +1 -1
  181. package/dist/modules/notification/index.js +4 -828
  182. package/dist/modules/notification/index.js.map +1 -1
  183. package/dist/modules/notification/motion.d.ts +1 -7
  184. package/dist/modules/notification/motion.d.ts.map +1 -1
  185. package/dist/modules/notification/overlay.d.ts.map +1 -1
  186. package/dist/modules/notification/types.d.ts +9 -1
  187. package/dist/modules/notification/types.d.ts.map +1 -1
  188. package/dist/overlay-TZ6L52A5.js +9 -0
  189. package/dist/overlay-TZ6L52A5.js.map +1 -0
  190. package/dist/provider.d.ts +1 -1
  191. package/dist/provider.d.ts.map +1 -1
  192. package/dist/provider.js +6 -1968
  193. package/dist/provider.js.map +1 -1
  194. package/dist/result.js +2 -63
  195. package/dist/result.js.map +1 -1
  196. package/dist/theme.d.ts +45 -8
  197. package/dist/theme.d.ts.map +1 -1
  198. package/dist/theme.js +3 -41
  199. package/dist/theme.js.map +1 -1
  200. package/dist/tokens/index.d.ts +1 -0
  201. package/dist/tokens/index.d.ts.map +1 -1
  202. package/dist/tokens/index.js +126 -155
  203. package/dist/tokens/index.js.map +1 -1
  204. package/dist/tokens/motion-config.d.ts +44 -0
  205. package/dist/tokens/motion-config.d.ts.map +1 -0
  206. package/dist/tokens/motion.d.ts +23 -174
  207. package/dist/tokens/motion.d.ts.map +1 -1
  208. package/dist/utils/index.js +4 -82
  209. package/dist/utils/index.js.map +1 -1
  210. package/dist/utils/report.d.ts.map +1 -1
  211. package/docs/README.md +1 -0
  212. package/docs/architecture-and-rules.md +4 -3
  213. package/docs/core-engine.md +7 -10
  214. package/docs/getting-started.md +4 -2
  215. package/docs/modules/ambient.md +194 -80
  216. package/docs/modules/background.md +3 -3
  217. package/docs/modules/context-menu.md +2 -2
  218. package/docs/modules/dock.md +8 -5
  219. package/docs/modules/modal.md +2 -2
  220. package/docs/modules/notification.md +2 -2
  221. package/docs/theming.md +81 -9
  222. package/package.json +22 -5
  223. package/templates/AGENTS.md +1 -1
  224. package/templates/motion/background.ts +17 -0
  225. package/templates/motion/context-menu.ts +63 -0
  226. package/templates/motion/dock.ts +327 -0
  227. package/templates/motion/modal.ts +79 -0
  228. package/templates/motion/notification.ts +27 -0
  229. package/templates/motion/tokens.ts +47 -0
  230. package/templates/tsconfig.json +7 -0
  231. package/dist/modules/ambient/color.d.ts +0 -16
  232. package/dist/modules/ambient/color.d.ts.map +0 -1
@@ -1,3081 +1,9 @@
1
1
  "use client";
2
- import { memo, createContext, useState, useRef, useEffect, useMemo, useCallback, useContext, useSyncExternalStore, useLayoutEffect, use, useId, isValidElement, cloneElement, Children } from 'react';
3
- import { clsx } from 'clsx';
4
- import { twMerge } from 'tailwind-merge';
5
- import { AnimatePresence, motion } from 'motion/react';
6
- import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
7
- import { usePathname } from 'next/navigation';
8
- import { Icon as Icon$1 } from '@iconify-icon/react';
9
-
10
- function cn(...inputs) {
11
- return twMerge(clsx(inputs));
12
- }
13
-
14
- // src/utils/string.ts
15
- function trimToNull(value) {
16
- if (value === null || value === void 0) return null;
17
- const trimmed = String(value).trim();
18
- return trimmed.length > 0 ? trimmed : null;
19
- }
20
-
21
- // src/utils/dom.ts
22
- var isBrowser = typeof window !== "undefined";
23
-
24
- // src/utils/object.ts
25
- function isObject(value) {
26
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
27
- }
28
- function report(scope, error, level = "error") {
29
- if (process.env.NODE_ENV === "production" && typeof window !== "undefined") {
30
- return;
31
- }
32
- console[level](`[${scope}]`, error);
33
- }
34
-
35
- // src/utils/store.ts
36
- function freezeStoreSnapshot(state) {
37
- if (process.env.NODE_ENV === "production" || !state || typeof state !== "object") {
38
- return state;
39
- }
40
- const seen = /* @__PURE__ */ new WeakSet();
41
- const freeze = (value) => {
42
- if (seen.has(value) || Object.isFrozen(value)) return;
43
- seen.add(value);
44
- const prototype = Object.getPrototypeOf(value);
45
- if (Array.isArray(value) || prototype === Object.prototype || prototype === null) {
46
- for (const key of Reflect.ownKeys(value)) {
47
- const descriptor = Object.getOwnPropertyDescriptor(value, key);
48
- if (descriptor && "value" in descriptor && descriptor.value !== null) {
49
- if (typeof descriptor.value === "object") freeze(descriptor.value);
50
- }
51
- }
52
- }
53
- Object.freeze(value);
54
- };
55
- freeze(state);
56
- return state;
57
- }
58
- function createStore(initialState, { freezeSnapshots = true } = {}) {
59
- const listeners = /* @__PURE__ */ new Set();
60
- const prepare = freezeSnapshots ? freezeStoreSnapshot : (state) => state;
61
- let snapshot = prepare(initialState);
62
- const publish = (nextStateOrUpdater) => {
63
- const nextState = typeof nextStateOrUpdater === "function" ? nextStateOrUpdater(snapshot) : nextStateOrUpdater;
64
- if (Object.is(snapshot, nextState)) return false;
65
- snapshot = prepare(nextState);
66
- for (const listener of listeners) {
67
- try {
68
- listener();
69
- } catch (error) {
70
- report("CoreStore subscriber", error, "warn");
71
- }
72
- }
73
- return true;
74
- };
75
- const setState = (updater) => {
76
- if (typeof updater === "function") {
77
- return publish(updater);
78
- }
79
- if (snapshot && typeof snapshot === "object" && updater && typeof updater === "object") {
80
- return publish({ ...snapshot, ...updater });
81
- }
82
- return publish(updater);
83
- };
84
- return Object.freeze({
85
- getSnapshot() {
86
- return snapshot;
87
- },
88
- publish,
89
- setState,
90
- subscribe(listener) {
91
- if (typeof listener !== "function") return () => {
92
- };
93
- listeners.add(listener);
94
- return () => listeners.delete(listener);
95
- }
96
- });
97
- }
98
-
99
- // src/tokens/motion.ts
100
- var DURATION_TOKENS = Object.freeze({
101
- INSTANT: 0.08,
102
- MICRO: 0.18,
103
- FAST: 0.28,
104
- BASE: 0.36,
105
- MODERATE: 0.44,
106
- SLOW: 0.58,
107
- CINEMATIC: 0.86
108
- });
109
- var EASING_CURVES = Object.freeze({
110
- OUT_EXPO: [0.16, 1, 0.3, 1],
111
- OUT_QUART: [0.25, 1, 0.5, 1],
112
- OUT_QUINT: [0.22, 1, 0.36, 1],
113
- IN_OUT_CUBIC: [0.65, 0, 0.35, 1],
114
- IN_CUBIC: [0.5, 0, 1, 1],
115
- OUT_BACK: [0.34, 1.56, 0.64, 1]
116
- });
117
- var MOTION_EASINGS = Object.freeze({
118
- ENTER: EASING_CURVES.OUT_QUART,
119
- ENTER_EMPHASIZED: EASING_CURVES.OUT_EXPO,
120
- EXIT: EASING_CURVES.IN_CUBIC
121
- });
122
- Object.freeze({
123
- MICRO: Object.freeze({
124
- distance: 4,
125
- duration: DURATION_TOKENS.MICRO,
126
- ease: MOTION_EASINGS.ENTER_EMPHASIZED,
127
- scaleDelta: 8e-3
128
- }),
129
- FAST: Object.freeze({
130
- distance: 9,
131
- duration: DURATION_TOKENS.FAST,
132
- ease: MOTION_EASINGS.ENTER_EMPHASIZED,
133
- scaleDelta: 0.012
134
- }),
135
- STANDARD: Object.freeze({
136
- distance: 18,
137
- duration: DURATION_TOKENS.MODERATE,
138
- ease: MOTION_EASINGS.ENTER,
139
- scaleDelta: 0.018
140
- }),
141
- SURFACE: Object.freeze({
142
- distance: 28,
143
- duration: DURATION_TOKENS.SLOW,
144
- ease: MOTION_EASINGS.ENTER_EMPHASIZED,
145
- scaleDelta: 0.024
146
- })
147
- });
148
- Object.freeze({
149
- duration: DURATION_TOKENS.INSTANT,
150
- ease: "linear"
151
- });
152
-
153
- // src/kernel/constants.ts
154
- var DEFAULT_SOURCE = "dynamic";
155
- var REGISTRY_SOURCES = Object.freeze({
156
- STATIC: "static",
157
- DYNAMIC: "dynamic",
158
- USER: "user"
159
- });
160
- var REGISTRY_LIFECYCLES = Object.freeze({
161
- IMMEDIATE: "immediate",
162
- GRACEFUL: "graceful",
163
- PERSISTENT: "persistent",
164
- ROUTE: "route"
165
- });
166
- var REGISTRY_VALIDATION_MODES = Object.freeze({
167
- WARN: "warn",
168
- STRICT: "strict"
169
- });
170
- var REGISTRY_METADATA_KEYS = Object.freeze([
171
- "cleanup",
172
- "cleanupDelayMs",
173
- "instanceId",
174
- "lifecycle",
175
- "priority",
176
- "source",
177
- "scope",
178
- "validation"
179
- ]);
180
- Object.freeze({
181
- [REGISTRY_SOURCES.STATIC]: 100,
182
- [REGISTRY_SOURCES.DYNAMIC]: 200,
183
- [REGISTRY_SOURCES.USER]: 300
184
- });
185
- Object.freeze({
186
- [REGISTRY_SOURCES.STATIC]: 10,
187
- [REGISTRY_SOURCES.DYNAMIC]: 20,
188
- [REGISTRY_SOURCES.USER]: 30
189
- });
190
- var REGISTRY_LIFECYCLE_VALUES = new Set(
191
- Object.values(REGISTRY_LIFECYCLES)
192
- );
193
- var REGISTRY_METADATA_KEY_SET = new Set(
194
- REGISTRY_METADATA_KEYS
195
- );
196
- var REGISTRY_SCOPE_KINDS = Object.freeze({
197
- APP: "app",
198
- SESSION: "session",
199
- ROUTE: "route",
200
- INSTANCE: "instance",
201
- WORKSPACE: "workspace"
202
- });
203
- REGISTRY_SCOPE_KINDS.APP;
204
- function getOrCreateGlobalContext(name, defaultValue) {
205
- const sym = /* @__PURE__ */ Symbol.for(`@omerdlw/base-framework/context/${name}`);
206
- const store = globalThis;
207
- if (!store[sym]) {
208
- store[sym] = createContext(defaultValue);
209
- }
210
- return store[sym];
211
- }
212
-
213
- // src/kernel/utils.ts
214
- function parseFiniteNumber(value) {
215
- const parsed = Number(value);
216
- return Number.isFinite(parsed) ? parsed : null;
217
- }
218
- function getValidString(value, fallback = null) {
219
- return trimToNull(value) ?? fallback;
220
- }
221
-
222
- // src/kernel/schema.ts
223
- function resolveLifecycle(metadata, fallback) {
224
- const lifecycle = metadata?.lifecycle ?? metadata?.cleanup;
225
- return lifecycle && REGISTRY_LIFECYCLE_VALUES.has(lifecycle) ? lifecycle : fallback;
226
- }
227
- function validateRegistryMetadata(metadata) {
228
- if (metadata === void 0 || metadata === null)
229
- return { issues: [], valid: true };
230
- if (!isObject(metadata))
231
- return { issues: ["registry metadata must be an object"], valid: false };
232
- const issues = [];
233
- Object.keys(metadata).forEach((key) => {
234
- if (!REGISTRY_METADATA_KEY_SET.has(key))
235
- issues.push(`unknown registry metadata field: ${key}`);
236
- });
237
- if (metadata.source !== void 0 && !getValidString(metadata.source))
238
- issues.push("source must be a non-empty string");
239
- if (metadata.instanceId !== void 0 && !getValidString(metadata.instanceId))
240
- issues.push("instanceId must be a non-empty string");
241
- if (metadata.scope !== void 0 && !getValidString(metadata.scope))
242
- issues.push("scope must be a non-empty string");
243
- if (metadata.priority !== void 0 && parseFiniteNumber(metadata.priority) === null)
244
- issues.push("priority must be a finite number");
245
- if (metadata.cleanupDelayMs !== void 0) {
246
- const delay = parseFiniteNumber(metadata.cleanupDelayMs);
247
- if (delay === null || delay < 0)
248
- issues.push("cleanupDelayMs must be a non-negative finite number");
249
- }
250
- const lifecycle = metadata.lifecycle ?? metadata.cleanup;
251
- if (lifecycle !== void 0 && !REGISTRY_LIFECYCLE_VALUES.has(String(lifecycle))) {
252
- issues.push(
253
- `lifecycle must be one of: ${[...REGISTRY_LIFECYCLE_VALUES].join(", ")}`
254
- );
255
- }
256
- if (metadata.validation !== void 0 && !Object.values(REGISTRY_VALIDATION_MODES).includes(
257
- metadata.validation
258
- )) {
259
- issues.push(
260
- `validation must be one of: ${Object.values(REGISTRY_VALIDATION_MODES).join(", ")}`
261
- );
262
- }
263
- return { issues, valid: issues.length === 0 };
264
- }
265
- function normalizeRegistryMetadata(metadata, {
266
- defaultCleanupDelayMs = null,
267
- defaultLifecycle = null,
268
- defaultSource = DEFAULT_SOURCE
269
- } = {}) {
270
- const registryMeta = isObject(metadata) ? metadata : {};
271
- const normalizedDefaultDelay = parseFiniteNumber(defaultCleanupDelayMs);
272
- const fallbackCleanupDelayMs = normalizedDefaultDelay !== null && normalizedDefaultDelay >= 0 ? normalizedDefaultDelay : null;
273
- const fallbackLifecycle = defaultLifecycle && REGISTRY_LIFECYCLE_VALUES.has(defaultLifecycle) ? defaultLifecycle : null;
274
- const source = getValidString(registryMeta.source) || getValidString(defaultSource, DEFAULT_SOURCE) || DEFAULT_SOURCE;
275
- const scope = getValidString(registryMeta.scope);
276
- const priority = parseFiniteNumber(registryMeta.priority);
277
- const lifecycle = resolveLifecycle(registryMeta, fallbackLifecycle);
278
- const validation = isValidationMode(registryMeta.validation) ? registryMeta.validation : null;
279
- const cleanupDelayCandidate = parseFiniteNumber(registryMeta.cleanupDelayMs);
280
- let cleanupDelayMs = cleanupDelayCandidate !== null && cleanupDelayCandidate >= 0 ? cleanupDelayCandidate : fallbackCleanupDelayMs;
281
- if (lifecycle === REGISTRY_LIFECYCLES.IMMEDIATE) {
282
- cleanupDelayMs = 0;
283
- } else if ((lifecycle === REGISTRY_LIFECYCLES.GRACEFUL || lifecycle === REGISTRY_LIFECYCLES.ROUTE) && cleanupDelayMs === null) {
284
- cleanupDelayMs = fallbackCleanupDelayMs;
285
- }
286
- return {
287
- cleanupDelayMs,
288
- lifecycle,
289
- priority,
290
- registerOptions: {
291
- ...priority !== null && { priority },
292
- ...validation === REGISTRY_VALIDATION_MODES.STRICT && { validation },
293
- ...scope && { scope }
294
- },
295
- source,
296
- ...scope && { scope },
297
- ...validation && { validation }
298
- };
299
- }
300
- function isValidationMode(value) {
301
- return Object.values(REGISTRY_VALIDATION_MODES).includes(value);
302
- }
303
- function pickRegistryMetadata(options) {
304
- if (!isObject(options)) return {};
305
- return Object.fromEntries(
306
- REGISTRY_METADATA_KEYS.filter((key) => options[key] !== void 0).map(
307
- (key) => [key, options[key]]
308
- )
309
- );
310
- }
311
- function useRequiredContext(context, hookName, providerName) {
312
- const value = use(context);
313
- if (value === null) {
314
- throw new Error(`${hookName} must be used within ${providerName}`);
315
- }
316
- return value;
317
- }
318
- function useStore(store, selector = (state) => state, isEqual = Object.is) {
319
- const cacheRef = useRef({
320
- isEqual: null,
321
- selected: void 0,
322
- selector: null,
323
- snapshot: null
324
- });
325
- const getSelectedSnapshot = useCallback(() => {
326
- const snapshot = store.getSnapshot();
327
- const cache = cacheRef.current;
328
- if (cache.snapshot === snapshot && cache.selector === selector && cache.isEqual === isEqual) {
329
- return cache.selected;
330
- }
331
- const selected = selector(snapshot);
332
- if (cache.snapshot !== null && cache.isEqual === isEqual && isEqual(cache.selected, selected)) {
333
- cache.snapshot = snapshot;
334
- cache.selector = selector;
335
- return cache.selected;
336
- }
337
- cacheRef.current = {
338
- isEqual,
339
- selected,
340
- selector,
341
- snapshot
342
- };
343
- return selected;
344
- }, [isEqual, selector, store]);
345
- return useSyncExternalStore(
346
- store.subscribe,
347
- getSelectedSnapshot,
348
- getSelectedSnapshot
349
- );
350
- }
351
- var useIsomorphicLayoutEffect = isBrowser ? useLayoutEffect : useEffect;
352
- var RegistryContext = getOrCreateGlobalContext(
353
- "RegistryContext",
354
- null
355
- );
356
- function useRegistryActions() {
357
- return useRequiredContext(
358
- RegistryContext,
359
- "useRegistryActions",
360
- "RegistryProvider"
361
- ).actions;
362
- }
363
- function useRegistrySubscription() {
364
- return useRequiredContext(
365
- RegistryContext,
366
- "useRegistrySubscription",
367
- "RegistryProvider"
368
- ).subscription;
369
- }
370
- function useRegistryValue(type, key) {
371
- const { getSnapshot, subscribe } = useRegistrySubscription();
372
- const subscribeToKey = useCallback(
373
- (listener) => subscribe(type, key, listener),
374
- [key, subscribe, type]
375
- );
376
- const getValue = useCallback(
377
- () => getSnapshot(type, key),
378
- [getSnapshot, key, type]
379
- );
380
- return useSyncExternalStore(subscribeToKey, getValue, getValue);
381
- }
382
- var EMPTY_MODULES = Object.freeze([]);
383
- var ModuleHostContext = getOrCreateGlobalContext("ModuleHostContext", null);
384
- function selectModuleSlice(coreModule, config) {
385
- const slice = coreModule.page?.select ? coreModule.page.select(config) : config[coreModule.id];
386
- return slice ?? null;
387
- }
388
- function createModulePayload(modules, config, metadata = {}) {
389
- const payload = {};
390
- let hasSlices = false;
391
- for (const coreModule of modules) {
392
- if (!coreModule.page?.entries) continue;
393
- const slice = selectModuleSlice(coreModule, config);
394
- if (slice === null) continue;
395
- payload[coreModule.id] = slice;
396
- hasSlices = true;
397
- }
398
- if (!hasSlices) return null;
399
- return {
400
- ...payload,
401
- registry: {
402
- ...metadata,
403
- ...isObject(config.registry) ? config.registry : {}
404
- }
405
- };
406
- }
407
- function useInstalledModules() {
408
- return use(ModuleHostContext)?.modules ?? EMPTY_MODULES;
409
- }
410
-
411
- // src/kernel/handlers.ts
412
- function isPersistentLifecycle(lifecycle) {
413
- return lifecycle === REGISTRY_LIFECYCLES.PERSISTENT;
414
- }
415
- function runRegistrationBatch(context, executor) {
416
- return typeof context.batch === "function" ? context.batch(executor) : executor(context);
417
- }
418
- function createEntryCleanupKey(type, key, source, instanceId, scope) {
419
- return JSON.stringify([
420
- "entry",
421
- type,
422
- key,
423
- scope ?? null,
424
- source,
425
- instanceId
426
- ]);
427
- }
428
- function getCleanupScope(context) {
429
- return context?.cleanupScope || /* @__PURE__ */ new Map();
430
- }
431
- function clearCleanupTimer(scope, cleanupKey) {
432
- const lifecycle = scope.get(cleanupKey);
433
- if (!lifecycle) return;
434
- lifecycle.cancelled = true;
435
- if (lifecycle.timerId !== null) clearTimeout(lifecycle.timerId);
436
- scope.delete(cleanupKey);
437
- }
438
- function scheduleCleanup(scope, cleanupKey, callback, delayMs) {
439
- clearCleanupTimer(scope, cleanupKey);
440
- const lifecycle = {
441
- cancelled: false,
442
- timerId: null
443
- };
444
- lifecycle.timerId = setTimeout(() => {
445
- if (lifecycle.cancelled || scope.get(cleanupKey) !== lifecycle) return;
446
- scope.delete(cleanupKey);
447
- callback();
448
- }, delayMs);
449
- scope.set(cleanupKey, lifecycle);
450
- }
451
- function collectModuleRegistrations(coreModule, slice, pageMetadata, context) {
452
- const definition = coreModule.registry;
453
- const toEntries = coreModule.page?.entries;
454
- if (!definition || !toEntries || slice == null || slice === false) return [];
455
- const type = coreModule.id;
456
- const items = Array.isArray(slice) ? slice : [slice];
457
- const registrations = [];
458
- for (const item of items) {
459
- if (item == null || item === false) continue;
460
- const itemMetadata = isObject(item) && isObject(item.registry) ? item.registry : {};
461
- const metadata = { ...pageMetadata, ...itemMetadata };
462
- if (metadata.validation === "strict" && !validateRegistryMetadata(metadata).valid) {
463
- continue;
464
- }
465
- const payload = isObject(item) ? Object.fromEntries(
466
- Object.entries(item).filter(([key]) => key !== "registry")
467
- ) : item;
468
- const { cleanupDelayMs, lifecycle, registerOptions, scope, source } = normalizeRegistryMetadata(metadata, {
469
- defaultCleanupDelayMs: definition.cleanupDelayMs ?? null,
470
- defaultLifecycle: definition.lifecycle,
471
- defaultSource: DEFAULT_SOURCE
472
- });
473
- for (const { key, value } of toEntries(payload, context) ?? []) {
474
- const validation = definition.validate?.(value);
475
- if (validation && !validation.valid) {
476
- report(`Registry ${type} config is invalid`, validation.issues, "warn");
477
- continue;
478
- }
479
- registrations.push({
480
- cleanupDelayMs,
481
- cleanupKey: createEntryCleanupKey(
482
- type,
483
- key,
484
- source,
485
- context.instanceId,
486
- scope
487
- ),
488
- key,
489
- lifecycle,
490
- registerOptions,
491
- scope,
492
- source,
493
- type,
494
- value
495
- });
496
- }
497
- }
498
- return registrations;
499
- }
500
- var NO_CLEANUP = () => {
501
- };
502
- function applyRegistryConfig(config, context) {
503
- const modules = context.modules ?? [];
504
- if (!config || modules.length === 0) return NO_CLEANUP;
505
- const pageMetadata = isObject(config.registry) ? config.registry : {};
506
- const slices = config;
507
- const registrations = modules.flatMap((coreModule) => {
508
- try {
509
- return collectModuleRegistrations(
510
- coreModule,
511
- slices[coreModule.id],
512
- pageMetadata,
513
- context
514
- );
515
- } catch (error) {
516
- report(`Registry apply (${coreModule.id})`, error);
517
- return [];
518
- }
519
- });
520
- if (registrations.length === 0) return NO_CLEANUP;
521
- const cleanupScope = getCleanupScope(context);
522
- const owner = (registration) => ({
523
- instanceId: context.instanceId,
524
- scope: registration.scope,
525
- source: registration.source
526
- });
527
- runRegistrationBatch(context, (queue) => {
528
- registrations.forEach(
529
- ({ cleanupKey, key, registerOptions, source, type, value }) => {
530
- clearCleanupTimer(cleanupScope, cleanupKey);
531
- queue.register(type, key, value, source, registerOptions);
532
- }
533
- );
534
- });
535
- return () => {
536
- const immediate = [];
537
- registrations.forEach((registration) => {
538
- if (isPersistentLifecycle(registration.lifecycle)) return;
539
- const { cleanupDelayMs, cleanupKey, key, type } = registration;
540
- if (typeof cleanupDelayMs === "number" && cleanupDelayMs > 0) {
541
- scheduleCleanup(
542
- cleanupScope,
543
- cleanupKey,
544
- () => context.unregister(type, key, owner(registration)),
545
- cleanupDelayMs
546
- );
547
- } else {
548
- immediate.push(registration);
549
- }
550
- });
551
- if (immediate.length === 0) return;
552
- runRegistrationBatch(context, (queue) => {
553
- immediate.forEach(
554
- (registration) => queue.unregister(
555
- registration.type,
556
- registration.key,
557
- owner(registration)
558
- )
559
- );
560
- });
561
- };
562
- }
563
-
564
- // src/kernel/hooks.ts
565
- function withInstanceId(instanceId, sourceOrOptions, optionsArg) {
566
- if (typeof sourceOrOptions === "string") {
567
- return {
568
- optionsArg: { ...isObject(optionsArg) ? optionsArg : {}, instanceId },
569
- sourceOrOptions
570
- };
571
- }
572
- if (isObject(sourceOrOptions)) {
573
- return {
574
- optionsArg,
575
- sourceOrOptions: { ...sourceOrOptions, instanceId }
576
- };
577
- }
578
- return {
579
- optionsArg: void 0,
580
- sourceOrOptions: {
581
- ...isObject(optionsArg) ? optionsArg : {},
582
- instanceId
583
- }
584
- };
585
- }
586
- function withInstanceIdForUnregister(instanceId, sourceOrOptions) {
587
- if (typeof sourceOrOptions === "string") {
588
- return { instanceId, source: sourceOrOptions };
589
- }
590
- if (isObject(sourceOrOptions)) {
591
- return { ...sourceOrOptions, instanceId };
592
- }
593
- return { instanceId };
594
- }
595
- function resolveRegisterArgsWithInstance(instanceId, sourceOrOptions, optionsArg) {
596
- const input = withInstanceId(instanceId, sourceOrOptions, optionsArg);
597
- return [input.sourceOrOptions, input.optionsArg];
598
- }
599
- var MAX_STABILIZATION_DEPTH = 64;
600
- function createStableFunctionEntry(fn) {
601
- const entry = {
602
- current: fn,
603
- stable(...args) {
604
- return entry.current?.apply(this, args);
605
- }
606
- };
607
- return entry;
608
- }
609
- function isDirectRegistryComponentPath(path, componentSlices) {
610
- const match = /^config\.([^.[]+)\.[^.[]+$/.exec(path);
611
- return Boolean(match && componentSlices.has(match[1]));
612
- }
613
- function isReactNodeLike(value) {
614
- return value === null || value === void 0 || typeof value === "boolean" || typeof value === "string" || typeof value === "number" || isValidElement(value);
615
- }
616
- function isStabilizableObject(value) {
617
- return typeof value === "object" && value !== null && !Array.isArray(value) && !isValidElement(value);
618
- }
619
- function hasSameObjectKeys(previousValue, nextValue) {
620
- if (!isStabilizableObject(previousValue) || !isStabilizableObject(nextValue)) {
621
- return false;
622
- }
623
- const previousKeys = Object.keys(previousValue);
624
- const nextKeys = Object.keys(nextValue);
625
- if (previousKeys.length !== nextKeys.length) return false;
626
- return nextKeys.every(
627
- (key) => Object.prototype.hasOwnProperty.call(previousValue, key)
628
- );
629
- }
630
- function stabilizeRegistryValue(value, path, scope, previousValue, seen = /* @__PURE__ */ new WeakSet(), depth = 0) {
631
- if (depth > MAX_STABILIZATION_DEPTH) return value;
632
- if (typeof value === "function") {
633
- const isComponent = value.name && /^[A-Z]/.test(value.name);
634
- if (isComponent || isDirectRegistryComponentPath(path, scope.componentSlices)) {
635
- return value;
636
- }
637
- scope.usedPaths.add(path);
638
- let entry = scope.functionEntries.get(path);
639
- if (!entry) {
640
- entry = createStableFunctionEntry(value);
641
- scope.functionEntries.set(path, entry);
642
- } else {
643
- entry.current = value;
644
- }
645
- return entry.stable;
646
- }
647
- if (value && typeof value === "object") {
648
- if (seen.has(value)) return value;
649
- seen.add(value);
650
- }
651
- if (isValidElement(value)) {
652
- const nextProps = stabilizeRegistryValue(
653
- value.props,
654
- `${path}.props`,
655
- scope,
656
- isValidElement(previousValue) ? previousValue.props : void 0,
657
- seen,
658
- depth + 1
659
- );
660
- if (isValidElement(previousValue) && previousValue.type === value.type && previousValue.key === value.key && previousValue.props === nextProps) {
661
- return previousValue;
662
- }
663
- return cloneElement(value, nextProps);
664
- }
665
- if (Array.isArray(value)) {
666
- const nextValue = value.every(isReactNodeLike) ? Children.toArray(value) : value;
667
- const previousArray = Array.isArray(previousValue) ? previousValue : null;
668
- let hasChanged2 = !previousArray || previousArray.length !== nextValue.length;
669
- const stabilizedValue2 = nextValue.map((item, index) => {
670
- const nextItem = stabilizeRegistryValue(
671
- item,
672
- `${path}[${index}]`,
673
- scope,
674
- previousArray?.[index],
675
- seen,
676
- depth + 1
677
- );
678
- if (!previousArray || nextItem !== previousArray[index]) {
679
- hasChanged2 = true;
680
- }
681
- return nextItem;
682
- });
683
- return !hasChanged2 ? previousArray : stabilizedValue2;
684
- }
685
- if (typeof value !== "object" || value === null) {
686
- return value;
687
- }
688
- const source = value;
689
- const previousRecord = previousValue;
690
- const stabilizedValue = {};
691
- const canReusePrevious = hasSameObjectKeys(previousValue, value);
692
- let hasChanged = !canReusePrevious;
693
- Object.keys(source).forEach((key) => {
694
- const nextValue = stabilizeRegistryValue(
695
- source[key],
696
- `${path}.${key}`,
697
- scope,
698
- previousRecord?.[key],
699
- seen,
700
- depth + 1
701
- );
702
- stabilizedValue[key] = nextValue;
703
- if (!canReusePrevious || nextValue !== previousRecord?.[key]) {
704
- hasChanged = true;
705
- }
706
- });
707
- return !hasChanged ? previousValue : stabilizedValue;
708
- }
709
- function useAppliedRegistryConfig(payload, context, componentSlices) {
710
- const functionEntriesRef = useRef(/* @__PURE__ */ new Map());
711
- const appliedRef = useRef(null);
712
- useIsomorphicLayoutEffect(() => {
713
- const previous = appliedRef.current;
714
- if (previous && previous.payload === payload && previous.context === context) {
715
- return;
716
- }
717
- const functionEntries = functionEntriesRef.current;
718
- const usedPaths = /* @__PURE__ */ new Set();
719
- const config = stabilizeRegistryValue(
720
- payload,
721
- "config",
722
- { componentSlices, functionEntries, usedPaths },
723
- previous?.config
724
- );
725
- functionEntries.forEach((_entry, path) => {
726
- if (!usedPaths.has(path)) functionEntries.delete(path);
727
- });
728
- if (previous && previous.config === config && previous.context === context) {
729
- previous.payload = payload;
730
- return;
731
- }
732
- previous?.cleanup();
733
- appliedRef.current = {
734
- cleanup: applyRegistryConfig(config, context),
735
- config,
736
- context,
737
- payload
738
- };
739
- });
740
- useIsomorphicLayoutEffect(
741
- () => () => {
742
- appliedRef.current?.cleanup();
743
- appliedRef.current = null;
744
- },
745
- []
746
- );
747
- }
748
- function createRegistryApplyContext(actions, {
749
- cleanupScope = /* @__PURE__ */ new Map(),
750
- instanceId,
751
- modules = [],
752
- pathname
753
- }) {
754
- const stamp = (queue) => ({
755
- register: (type, key, item, sourceOrOptions, optionsArg) => queue.register(
756
- type,
757
- key,
758
- item,
759
- ...resolveRegisterArgsWithInstance(
760
- instanceId,
761
- sourceOrOptions,
762
- optionsArg
763
- )
764
- ),
765
- unregister: (type, key, sourceOrOptions) => queue.unregister(
766
- type,
767
- key,
768
- withInstanceIdForUnregister(instanceId, sourceOrOptions)
769
- )
770
- });
771
- return {
772
- ...stamp(actions),
773
- batch: (executor) => typeof executor === "function" ? actions.batch((queue) => executor(stamp(queue))) : 0,
774
- cleanupScope,
775
- instanceId,
776
- modules,
777
- pathname
778
- };
779
- }
780
- function useRegistry(config, metadata) {
781
- const { batch, register, unregister } = useRegistryActions();
782
- const pathname = usePathname();
783
- const modules = useInstalledModules();
784
- const instanceId = `registry-instance-${useId()}`;
785
- const [cleanupScope] = useState(
786
- () => /* @__PURE__ */ new Map()
787
- );
788
- const context = useMemo(
789
- () => createRegistryApplyContext(
790
- { batch, register, unregister },
791
- {
792
- cleanupScope,
793
- instanceId,
794
- modules,
795
- pathname
796
- }
797
- ),
798
- [batch, cleanupScope, instanceId, modules, pathname, register, unregister]
799
- );
800
- const payload = useMemo(
801
- () => config ? createModulePayload(modules, config, metadata) : null,
802
- [config, metadata, modules]
803
- );
804
- const componentSlices = useMemo(
805
- () => new Set(
806
- modules.filter(
807
- (coreModule) => coreModule.registry?.valueKind === "component"
808
- ).map((coreModule) => coreModule.id)
809
- ),
810
- [modules]
811
- );
812
- useAppliedRegistryConfig(payload, context, componentSlices);
813
- }
814
- function defineModule(coreModule) {
815
- return Object.freeze({ ...coreModule });
816
- }
817
- createStore(void 0);
818
-
819
- // src/kernel/adapters.ts
820
- function useModuleRegistration(id, value, options) {
821
- const coreModule = useInstalledModules().find(
822
- (candidate) => candidate.id === id
823
- );
824
- const config = coreModule && options?.enabled !== false && value != null ? { [id]: value } : null;
825
- useRegistry(config, pickRegistryMetadata(options));
826
- }
827
-
828
- // src/theme.tsx
829
- function defineThemeSpec(id) {
830
- return Object.freeze({ id });
831
- }
832
- var EMPTY = /* @__PURE__ */ new Map();
833
- var ThemeContext = getOrCreateGlobalContext("ThemeContext", EMPTY);
834
- function useTheme(spec) {
835
- const config = useContext(ThemeContext).get(spec.id);
836
- if (!config) {
837
- throw new Error(
838
- `Theme "${spec.id}" is missing. Ensure its theme configuration is provided to <ThemeProvider themes={[...]}> (e.g. in src/config/ or your theme setup).`
839
- );
840
- }
841
- return {
842
- slots: config.slots,
843
- styles: config.styles ?? {}
844
- };
845
- }
846
-
847
- // src/modules/background/constants.ts
848
- var backgroundTheme = defineThemeSpec("background");
849
- var BACKGROUND_REGISTRY_KEY = "page-background";
850
- var BACKGROUND_MEDIA_ID = "background";
851
-
852
- // src/modules/background/visual.ts
853
- var objectFit = (value) => ({
854
- objectFit: value
855
- });
856
- var objectPosition = (value) => ({
857
- objectPosition: value
858
- });
859
- var BG_TOKEN_TO_OBJECT_STYLE = Object.freeze({
860
- "bg-bottom": objectPosition("bottom"),
861
- "bg-center": objectPosition("center"),
862
- "bg-contain": objectFit("contain"),
863
- "bg-cover": objectFit("cover"),
864
- "bg-fill": objectFit("fill"),
865
- "bg-left": objectPosition("left"),
866
- "bg-left-bottom": objectPosition("left bottom"),
867
- "bg-left-top": objectPosition("left top"),
868
- "bg-none": objectFit("none"),
869
- "bg-right": objectPosition("right"),
870
- "bg-right-bottom": objectPosition("right bottom"),
871
- "bg-right-top": objectPosition("right top"),
872
- "bg-scale-down": objectFit("scale-down"),
873
- "bg-top": objectPosition("top")
874
- });
875
- var OBJECT_FITS = Object.freeze(["contain", "cover", "fill", "none", "scale-down"]);
876
- var DEFAULT_COLOR = "var(--black, #000)";
877
- var WIDTH_CLASS = /^(w-|max-w-|min-w-)/;
878
- function resolveVideoClassNames(...inputs) {
879
- const tokens = inputs.flatMap((input) => typeof input === "string" ? input.replace(/,/g, " ").split(/\s+/) : []).filter(Boolean);
880
- return {
881
- objectStyle: Object.assign({}, ...tokens.map((token) => BG_TOKEN_TO_OBJECT_STYLE[token])),
882
- other: tokens.filter((token) => !WIDTH_CLASS.test(token)).join(" "),
883
- width: tokens.filter((token) => WIDTH_CLASS.test(token)).join(" ")
884
- };
885
- }
886
- function resolveVideoOptions(options = {}) {
887
- return {
888
- codec: options.codec || "auto",
889
- corp: options.corp ?? 0,
890
- forceIframe: Boolean(options.forceIframe),
891
- isLoop: options.loop ?? false,
892
- isMuted: options.muted ?? true,
893
- playbackRate: options.playbackRate ?? 1,
894
- quality: options.quality || "1080p",
895
- shouldAutoPlay: options.autoplay ?? true,
896
- showPoster: Boolean(options.showPoster),
897
- showSpinner: options.showSpinner !== false
898
- };
899
- }
900
- var generateBaseGradient = (direction, color) => {
901
- const to = direction === "left" ? "to right" : "to left";
902
- return `linear-gradient(${to}, ${color} 0%, color-mix(in srgb, ${color} 85%, transparent) 15%, color-mix(in srgb, ${color} 50%, transparent) 45%, color-mix(in srgb, ${color} 18%, transparent) 75%, transparent 100%)`;
903
- };
904
- var generateEdgeGradient = (direction, color) => {
905
- const to = direction === "left" ? "to right" : "to left";
906
- return `linear-gradient(${to}, color-mix(in srgb, ${color} 95%, transparent) 0%, color-mix(in srgb, ${color} 70%, transparent) 25%, color-mix(in srgb, ${color} 35%, transparent) 55%, color-mix(in srgb, ${color} 10%, transparent) 80%, transparent 100%)`;
907
- };
908
- function getVisualStyle(currentStyle = {}) {
909
- const { className: _className, leftGradient = 0, rightGradient = 0, ...baseStyle } = currentStyle;
910
- return {
911
- baseStyle,
912
- leftGradient: Number(leftGradient) || 0,
913
- rightGradient: Number(rightGradient) || 0
914
- };
915
- }
916
- function resolveGradientSettings({
917
- fadeEdges = null,
918
- hasWidth = false,
919
- leftGradient = 0,
920
- rightGradient = 0
921
- }) {
922
- let leftPercent = 0;
923
- let rightPercent = 0;
924
- if (leftGradient > 0) leftPercent = Math.min(48, Math.max(12, leftGradient * 7.5));
925
- else if (hasWidth && fadeEdges !== false) leftPercent = 20;
926
- if (rightGradient > 0) rightPercent = Math.min(48, Math.max(12, rightGradient * 7.5));
927
- else if (hasWidth && fadeEdges !== false) rightPercent = 20;
928
- if (typeof fadeEdges === "number") {
929
- leftPercent = fadeEdges;
930
- rightPercent = fadeEdges;
931
- } else if (typeof fadeEdges === "string") {
932
- const parsed = parseFloat(fadeEdges);
933
- if (!Number.isNaN(parsed)) {
934
- leftPercent = parsed;
935
- rightPercent = parsed;
936
- }
937
- } else if (typeof fadeEdges === "object" && fadeEdges !== null) {
938
- const edgeObj = fadeEdges;
939
- if (edgeObj.left !== void 0) leftPercent = parseFloat(String(edgeObj.left)) || 0;
940
- if (edgeObj.right !== void 0) rightPercent = parseFloat(String(edgeObj.right)) || 0;
941
- } else if (fadeEdges === false) {
942
- leftPercent = 0;
943
- rightPercent = 0;
944
- }
945
- const leftOpacity = leftGradient > 0 ? Math.min(1, leftGradient * 0.22) : 0;
946
- const rightOpacity = rightGradient > 0 ? Math.min(1, rightGradient * 0.22) : 0;
947
- return {
948
- enabled: leftPercent > 0 || rightPercent > 0,
949
- leftOpacity,
950
- leftPercent,
951
- rightOpacity,
952
- rightPercent
953
- };
954
- }
955
- function generateSmoothstepStops(percent, direction = "in", color = "var(--black, #000)") {
956
- const steps = [
957
- { s: 0, t: 0 },
958
- { s: 0.06, t: 0.15 },
959
- { s: 0.22, t: 0.35 },
960
- { s: 0.47, t: 0.55 },
961
- { s: 0.76, t: 0.75 },
962
- { s: 0.93, t: 0.9 },
963
- { s: 1, t: 1 }
964
- ];
965
- if (direction === "in") {
966
- return steps.map(
967
- ({ s, t }) => s === 0 ? `transparent ${(t * percent).toFixed(1)}%` : s === 1 ? `${color} ${(t * percent).toFixed(1)}%` : `color-mix(in srgb, ${color} ${Math.round(s * 100)}%, transparent) ${(t * percent).toFixed(1)}%`
968
- );
969
- }
970
- return steps.map(({ s, t }) => {
971
- const alpha = 1 - s;
972
- return alpha === 0 ? `transparent ${(100 - percent + t * percent).toFixed(1)}%` : alpha === 1 ? `${color} ${(100 - percent + t * percent).toFixed(1)}%` : `color-mix(in srgb, ${color} ${Math.round(alpha * 100)}%, transparent) ${(100 - percent + t * percent).toFixed(1)}%`;
973
- });
974
- }
975
- function getEdgeFadeMask({
976
- color = "var(--black, #000)",
977
- leftPercent = 0,
978
- rightPercent = 0
979
- }) {
980
- if (leftPercent <= 0 && rightPercent <= 0) return void 0;
981
- const leftStops = leftPercent > 0 ? generateSmoothstepStops(leftPercent, "in", color) : [`${color} 0%`];
982
- const rightStops = rightPercent > 0 ? generateSmoothstepStops(rightPercent, "out", color) : [`${color} 100%`];
983
- return `linear-gradient(to right, ${leftStops.join(", ")}, ${rightStops.join(", ")})`;
984
- }
985
-
986
- // src/modules/background/playback.ts
987
- function applyVideoPlaybackState({
988
- isPlaying,
989
- playbackRate,
990
- setVideoPlaying,
991
- videoElement
992
- }) {
993
- if (!videoElement) return;
994
- const numericPlaybackRate = Number(playbackRate);
995
- const resolvedPlaybackRate = Number.isFinite(numericPlaybackRate) && numericPlaybackRate > 0 ? numericPlaybackRate : 1;
996
- try {
997
- if (videoElement.playbackRate !== resolvedPlaybackRate) {
998
- videoElement.playbackRate = resolvedPlaybackRate;
999
- }
1000
- } catch {
1001
- return;
1002
- }
1003
- if (!isPlaying) {
1004
- if (!videoElement.paused) videoElement.pause();
1005
- return;
1006
- }
1007
- if (videoElement.readyState === 0) {
1008
- return;
1009
- }
1010
- const playPromise = videoElement.play();
1011
- if (!playPromise || typeof playPromise.then !== "function") return;
1012
- playPromise.then(() => setVideoPlaying(true)).catch((error) => {
1013
- if (error?.name === "AbortError" || videoElement.readyState < 2) {
1014
- return;
1015
- }
1016
- if (error?.name === "NotAllowedError") {
1017
- videoElement.muted = true;
1018
- videoElement.play().then(() => setVideoPlaying(true)).catch(() => {
1019
- });
1020
- return;
1021
- }
1022
- setVideoPlaying(false);
1023
- });
1024
- }
1025
- function triggerVideoPlaybackDirect(videoElement, nextPlaying, configuredMuted) {
1026
- if (!videoElement) return;
1027
- if (nextPlaying) {
1028
- videoElement.muted = configuredMuted;
1029
- if (!configuredMuted && videoElement.volume === 0) {
1030
- videoElement.volume = 0.8;
1031
- }
1032
- videoElement.play().catch((err) => {
1033
- if (err?.name === "NotAllowedError") {
1034
- videoElement.muted = true;
1035
- videoElement.play().catch(() => {
1036
- });
1037
- }
1038
- });
1039
- } else {
1040
- videoElement.pause();
1041
- }
1042
- }
1043
- function syncVideoMutedDirect(videoElement, nextMuted) {
1044
- if (!videoElement) return;
1045
- videoElement.muted = nextMuted;
1046
- if (!nextMuted && videoElement.volume === 0) {
1047
- videoElement.volume = 0.8;
1048
- }
1049
- }
1050
- function syncVideoLoopDirect(videoElement, nextLoop) {
1051
- if (!videoElement) return;
1052
- videoElement.loop = nextLoop;
1053
- }
1054
-
1055
- // src/modules/background/motion.ts
1056
- var DEFAULT_MOTION = Object.freeze({
1057
- animate: Object.freeze({
1058
- opacity: 1
1059
- }),
1060
- exit: Object.freeze({
1061
- opacity: 0,
1062
- transition: Object.freeze({
1063
- duration: DURATION_TOKENS.SLOW,
1064
- ease: EASING_CURVES.IN_CUBIC
1065
- })
1066
- }),
1067
- initial: Object.freeze({
1068
- opacity: 0
1069
- }),
1070
- transition: Object.freeze({
1071
- delay: 0,
1072
- duration: DURATION_TOKENS.SLOW,
1073
- ease: EASING_CURVES.OUT_QUART
1074
- })
1075
- });
1076
- function getBackgroundMotionConfig(animation) {
1077
- return {
1078
- animate: animation?.animate ?? DEFAULT_MOTION.animate,
1079
- exit: animation?.exit ?? DEFAULT_MOTION.exit,
1080
- exitDurationFactor: Number(animation?.exitDurationFactor),
1081
- initial: animation?.initial ?? DEFAULT_MOTION.initial,
1082
- transition: animation?.transition ?? DEFAULT_MOTION.transition
1083
- };
1084
- }
1085
- function toCssDuration(seconds) {
1086
- const value = Number(seconds);
1087
- return `${Math.max(0, Number.isFinite(value) ? value : DURATION_TOKENS.SLOW) * 1e3}ms`;
1088
- }
1089
- function toCssDelay(seconds) {
1090
- const value = Number(seconds);
1091
- return `${Math.max(0, Number.isFinite(value) ? value : 0) * 1e3}ms`;
1092
- }
1093
- function toCssEasing(easing) {
1094
- if (Array.isArray(easing)) {
1095
- return `cubic-bezier(${easing.join(", ")})`;
1096
- }
1097
- if (typeof easing === "string" && easing.trim()) {
1098
- return easing;
1099
- }
1100
- return "ease";
1101
- }
1102
-
1103
- // src/modules/media/constants.ts
1104
- var DEFAULT_MEDIA_STATE = Object.freeze({
1105
- audibleElement: null,
1106
- element: null,
1107
- hasMedia: false,
1108
- isPlaying: false,
1109
- kind: null,
1110
- loop: false,
1111
- sourceId: null
1112
- });
1113
- var INERT_MEDIA_ACTIONS = Object.freeze({
1114
- removeSource: () => {
1115
- },
1116
- setMuted: () => {
1117
- },
1118
- toggle: () => {
1119
- },
1120
- toggleLoop: () => {
1121
- },
1122
- upsertSource: () => {
1123
- }
1124
- });
1125
- var MediaContext = getOrCreateGlobalContext(
1126
- "MediaContext",
1127
- null
1128
- );
1129
- createStore(DEFAULT_MEDIA_STATE);
1130
- function useOptionalMediaActions() {
1131
- const ctx = useContext(MediaContext);
1132
- return ctx?.actions ?? INERT_MEDIA_ACTIONS;
1133
- }
1134
-
1135
- // src/modules/background/youtube/parse.ts
1136
- var YOUTUBE_ID_PATTERN = /^[a-zA-Z0-9_-]{11}$/;
1137
- function parseYouTubeTimeParam(value) {
1138
- if (value === void 0 || value === null || value === "") return 0;
1139
- if (typeof value === "number") {
1140
- return Number.isFinite(value) && value >= 0 ? value : 0;
1141
- }
1142
- const trimmed = String(value).trim().toLowerCase();
1143
- if (!trimmed) return 0;
1144
- if (/^\d+(\.\d+)?$/.test(trimmed)) {
1145
- return Math.max(0, parseFloat(trimmed));
1146
- }
1147
- const hmsMatch = trimmed.match(
1148
- /^(?:(\d+)h)?(?:(\d+)m)?(?:(\d+(?:\.\d+)?)s?)?$/
1149
- );
1150
- if (hmsMatch && (hmsMatch[1] || hmsMatch[2] || hmsMatch[3])) {
1151
- const hours = parseInt(hmsMatch[1] || "0", 10);
1152
- const minutes = parseInt(hmsMatch[2] || "0", 10);
1153
- const seconds = parseFloat(hmsMatch[3] || "0");
1154
- return Math.max(0, hours * 3600 + minutes * 60 + seconds);
1155
- }
1156
- return 0;
1157
- }
1158
- var YOUTUBE_HOST_PATH = /^(?:www\.|m\.|music\.)?(?:youtube\.com|youtu\.be|youtube-nocookie\.com)\//i;
1159
- function toUrl(input, { youtubeOnly = false } = {}) {
1160
- let candidate = null;
1161
- if (/^https?:\/\//.test(input)) candidate = input;
1162
- else if (input.startsWith("//")) candidate = `https:${input}`;
1163
- else if (!youtubeOnly || YOUTUBE_HOST_PATH.test(input)) {
1164
- candidate = `https://${input}`;
1165
- }
1166
- try {
1167
- return candidate ? new URL(candidate) : null;
1168
- } catch {
1169
- return null;
1170
- }
1171
- }
1172
- function getIdFromUrl(url) {
1173
- const host = url.hostname.replace(/^(www\.|m\.|music\.)/i, "").toLowerCase();
1174
- const segments = url.pathname.split("/").filter(Boolean);
1175
- if (host === "youtu.be") {
1176
- return YOUTUBE_ID_PATTERN.test(segments[0] ?? "") ? segments[0] : null;
1177
- }
1178
- if (host !== "youtube.com" && host !== "youtube-nocookie.com" && !host.endsWith(".youtube.com")) {
1179
- return null;
1180
- }
1181
- const fromQuery = url.searchParams.get("v") || url.searchParams.get("vi");
1182
- if (fromQuery && YOUTUBE_ID_PATTERN.test(fromQuery)) return fromQuery;
1183
- const isVideoPath = segments.length >= 2 && ["embed", "shorts", "live", "v", "e", "watch"].includes(
1184
- segments[0].toLowerCase()
1185
- );
1186
- return isVideoPath && YOUTUBE_ID_PATTERN.test(segments[1]) ? segments[1] : null;
1187
- }
1188
- function extractYouTubeVideoId(input, options = {}) {
1189
- const trimmed = typeof input === "string" ? input.trim() : "";
1190
- if (!trimmed) return null;
1191
- if (options.allowBareId && YOUTUBE_ID_PATTERN.test(trimmed)) return trimmed;
1192
- const prefixed = trimmed.match(/^(?:youtube|yt):([a-zA-Z0-9_-]{11})$/i);
1193
- if (prefixed) return prefixed[1];
1194
- const url = toUrl(trimmed, { youtubeOnly: true });
1195
- return url ? getIdFromUrl(url) : null;
1196
- }
1197
- function isYouTubeUrl(input) {
1198
- return Boolean(extractYouTubeVideoId(input));
1199
- }
1200
- function isDirectVideoUrl(input) {
1201
- if (!input || typeof input !== "string") return false;
1202
- const trimmed = input.trim();
1203
- if (!trimmed) return false;
1204
- if (isYouTubeUrl(trimmed)) return true;
1205
- if (trimmed.startsWith("/api/background/youtube") || trimmed.startsWith("blob:")) {
1206
- return true;
1207
- }
1208
- const cleanPath = trimmed.split("?")[0].split("#")[0].toLowerCase();
1209
- return /\.(mp4|webm|mov|m4v|ogg|ogv|m3u8)$/i.test(cleanPath);
1210
- }
1211
- function parseYouTubeUrlConfig(input, options = {}) {
1212
- const videoId = extractYouTubeVideoId(input, options);
1213
- if (!videoId || !input) return null;
1214
- const params = toUrl(input.trim())?.searchParams;
1215
- return {
1216
- endTime: parseYouTubeTimeParam(params?.get("end")),
1217
- listId: params?.get("list") || null,
1218
- rawUrl: input,
1219
- startTime: parseYouTubeTimeParam(params?.get("t") || params?.get("start")),
1220
- videoId
1221
- };
1222
- }
1223
- function getYouTubeStreamUrl(videoId, options = {}) {
1224
- const params = new URLSearchParams();
1225
- params.set("id", videoId);
1226
- params.set("stream", options.stream || "video");
1227
- if (options.quality) params.set("quality", options.quality);
1228
- if (options.codec) params.set("codec", options.codec);
1229
- return `/api/background/youtube?${params.toString()}`;
1230
- }
1231
- function getYouTubeThumbnailUrl(videoId, proxied = true) {
1232
- if (proxied) {
1233
- return `/api/background/youtube?id=${encodeURIComponent(videoId)}&stream=thumbnail`;
1234
- }
1235
- return `https://i.ytimg.com/vi/${encodeURIComponent(videoId)}/maxresdefault.jpg`;
1236
- }
1237
- function createYouTubeVideoElementProxy(controller) {
1238
- const proxyVideo = document.createElement("video");
1239
- Object.defineProperties(proxyVideo, {
1240
- currentTime: {
1241
- configurable: true,
1242
- get: () => controller.getCurrentTime(),
1243
- set: (value) => {
1244
- controller.seekTo(Number(value) || 0);
1245
- proxyVideo.dispatchEvent(new Event("timeupdate"));
1246
- }
1247
- },
1248
- duration: {
1249
- configurable: true,
1250
- get: () => controller.getDuration()
1251
- },
1252
- loop: {
1253
- configurable: true,
1254
- get: () => controller.getLoop(),
1255
- set: (value) => {
1256
- controller.setLoop(Boolean(value));
1257
- }
1258
- },
1259
- muted: {
1260
- configurable: true,
1261
- get: () => controller.getMuted(),
1262
- set: (value) => {
1263
- controller.setMuted(Boolean(value));
1264
- proxyVideo.dispatchEvent(new Event("volumechange"));
1265
- }
1266
- },
1267
- pause: {
1268
- configurable: true,
1269
- value: () => {
1270
- controller.pause();
1271
- proxyVideo.dispatchEvent(new Event("pause"));
1272
- }
1273
- },
1274
- paused: {
1275
- configurable: true,
1276
- get: () => controller.getPaused()
1277
- },
1278
- play: {
1279
- configurable: true,
1280
- value: () => controller.play().then(() => {
1281
- proxyVideo.dispatchEvent(new Event("play"));
1282
- proxyVideo.dispatchEvent(new Event("playing"));
1283
- })
1284
- },
1285
- playbackRate: {
1286
- configurable: true,
1287
- get: () => controller.getPlaybackRate(),
1288
- set: (value) => {
1289
- controller.setPlaybackRate(Number(value) || 1);
1290
- proxyVideo.dispatchEvent(new Event("ratechange"));
1291
- }
1292
- },
1293
- volume: {
1294
- configurable: true,
1295
- get: () => controller.getVolume(),
1296
- set: (value) => {
1297
- const clamped = Math.max(0, Math.min(1, Number(value) || 0));
1298
- controller.setVolume(clamped);
1299
- proxyVideo.dispatchEvent(new Event("volumechange"));
1300
- }
1301
- }
1302
- });
1303
- return proxyVideo;
1304
- }
1305
-
1306
- // src/modules/background/state.ts
1307
- var DEFAULT_BACKGROUND = Object.freeze({
1308
- animation: null,
1309
- className: "",
1310
- fadeEdges: null,
1311
- fit: null,
1312
- image: null,
1313
- imageStyle: {},
1314
- isPlaying: false,
1315
- noiseStyle: {},
1316
- overlay: false,
1317
- overlayColor: "var(--black)",
1318
- overlayOpacity: 0,
1319
- position: "center",
1320
- video: null,
1321
- videoClassName: "",
1322
- videoElement: null,
1323
- videoOptions: {
1324
- autoplay: true,
1325
- className: "",
1326
- corp: 0,
1327
- loop: false,
1328
- muted: true,
1329
- playbackRate: 1,
1330
- width: null
1331
- },
1332
- videoStyle: {},
1333
- width: null
1334
- });
1335
- var DEFAULT_BACKGROUND_COMPUTED = Object.freeze({
1336
- ...DEFAULT_BACKGROUND,
1337
- hasBackground: false,
1338
- isVideo: false,
1339
- isYouTube: false,
1340
- posterUrl: null,
1341
- youtubeVideoId: null
1342
- });
1343
- var INERT_BACKGROUND_ACTIONS = Object.freeze({
1344
- resetBackground: () => {
1345
- },
1346
- setBackground: () => {
1347
- },
1348
- setVideoElement: () => {
1349
- },
1350
- setVideoMuted: () => {
1351
- },
1352
- setVideoPlaying: () => {
1353
- },
1354
- toggleLoop: () => {
1355
- },
1356
- toggleMute: () => {
1357
- },
1358
- toggleVideo: () => {
1359
- }
1360
- });
1361
- function normalizeBackgroundInput(patch = {}) {
1362
- if (!patch) return {};
1363
- if (typeof patch === "string") {
1364
- const trimmed = patch.trim();
1365
- if (isYouTubeUrl(trimmed) || isDirectVideoUrl(trimmed)) {
1366
- return { image: null, video: trimmed };
1367
- }
1368
- return { image: trimmed };
1369
- }
1370
- const nextPatch = { ...patch };
1371
- if (typeof nextPatch.image === "string" && isYouTubeUrl(nextPatch.image) && !nextPatch.video) {
1372
- nextPatch.video = nextPatch.image;
1373
- nextPatch.image = null;
1374
- }
1375
- return nextPatch;
1376
- }
1377
- function mergeBackgroundState(baseState, patch = {}) {
1378
- const resolvedPatch = normalizeBackgroundInput(patch);
1379
- const nextVideoOptions = {
1380
- ...baseState.videoOptions,
1381
- ...resolvedPatch.videoOptions || {}
1382
- };
1383
- const nextVideo = resolvedPatch.video !== void 0 ? resolvedPatch.video : baseState.video;
1384
- const shouldAutoPlay = nextVideoOptions.autoplay !== false;
1385
- const resolvedIsPlaying = resolvedPatch.isPlaying !== void 0 ? resolvedPatch.isPlaying : resolvedPatch.video !== void 0 && Boolean(nextVideo) ? shouldAutoPlay : baseState.isPlaying;
1386
- return {
1387
- ...baseState,
1388
- ...resolvedPatch,
1389
- isPlaying: resolvedIsPlaying,
1390
- animation: resolvedPatch.animation !== void 0 ? resolvedPatch.animation ? {
1391
- ...baseState.animation || {},
1392
- ...resolvedPatch.animation
1393
- } : resolvedPatch.animation : baseState.animation,
1394
- imageStyle: {
1395
- ...baseState.imageStyle,
1396
- ...resolvedPatch.imageStyle || {}
1397
- },
1398
- noiseStyle: {
1399
- ...baseState.noiseStyle,
1400
- ...resolvedPatch.noiseStyle || {}
1401
- },
1402
- videoOptions: nextVideoOptions,
1403
- videoStyle: {
1404
- ...baseState.videoStyle,
1405
- ...resolvedPatch.videoStyle || {}
1406
- }
1407
- };
1408
- }
1409
- function resolveBackgroundState(registryBackground, override, pathname) {
1410
- const base = registryBackground ? mergeBackgroundState(DEFAULT_BACKGROUND, registryBackground) : DEFAULT_BACKGROUND;
1411
- const patch = override.pathname === pathname ? override.patch : null;
1412
- return patch ? mergeBackgroundState(base, patch) : base;
1413
- }
1414
- function computeBackgroundState(background) {
1415
- const youtubeVideoId = extractYouTubeVideoId(background.video);
1416
- const isYouTube = Boolean(youtubeVideoId);
1417
- const isVideo = Boolean(background.video || isYouTube);
1418
- return {
1419
- ...background,
1420
- hasBackground: Boolean(
1421
- background.image || isVideo || background.color || background.overlay || background.noiseStyle && (background.noiseStyle.opacity === void 0 || (background.noiseStyle.opacity ?? 0) > 0)
1422
- ),
1423
- isVideo,
1424
- isYouTube,
1425
- posterUrl: youtubeVideoId ? getYouTubeThumbnailUrl(youtubeVideoId) : null,
1426
- youtubeVideoId
1427
- };
1428
- }
1429
- var BackgroundContext = getOrCreateGlobalContext(
1430
- "BackgroundContext",
1431
- null
1432
- );
1433
- function useSafePathname() {
1434
- try {
1435
- return usePathname();
1436
- } catch {
1437
- return null;
1438
- }
1439
- }
1440
- function BackgroundProvider({ children }) {
1441
- const pathname = useSafePathname();
1442
- const registryBackground = useRegistryValue(
1443
- "background",
1444
- BACKGROUND_REGISTRY_KEY
1445
- ) ?? null;
1446
- const [overrideStore] = useState(
1447
- () => createStore(
1448
- { patch: null, pathname },
1449
- { freezeSnapshots: false }
1450
- )
1451
- );
1452
- const scopedOverride = useStore(overrideStore);
1453
- const inputsRef = useRef({ pathname, registryBackground });
1454
- useIsomorphicLayoutEffect(() => {
1455
- inputsRef.current = { pathname, registryBackground };
1456
- }, [pathname, registryBackground]);
1457
- const videoElementRef = useRef(null);
1458
- const state = useMemo(
1459
- () => computeBackgroundState(
1460
- resolveBackgroundState(registryBackground, scopedOverride, pathname)
1461
- ),
1462
- [pathname, registryBackground, scopedOverride]
1463
- );
1464
- const [stateStore] = useState(
1465
- () => createStore(state, { freezeSnapshots: false })
1466
- );
1467
- useIsomorphicLayoutEffect(() => {
1468
- stateStore.publish(state);
1469
- }, [state, stateStore]);
1470
- const actions = useMemo(() => {
1471
- const readCurrent = () => {
1472
- const { pathname: currentPath, registryBackground: registry } = inputsRef.current;
1473
- return resolveBackgroundState(
1474
- registry,
1475
- overrideStore.getSnapshot(),
1476
- currentPath
1477
- );
1478
- };
1479
- const updatePatch = (updater) => {
1480
- overrideStore.publish((prev) => {
1481
- const { pathname: currentPath, registryBackground: registry } = inputsRef.current;
1482
- const currentPatch = prev.pathname === currentPath ? prev.patch : null;
1483
- const rawNext = updater(
1484
- resolveBackgroundState(registry, prev, currentPath)
1485
- );
1486
- if (!rawNext || Object.keys(rawNext).length === 0) {
1487
- return prev;
1488
- }
1489
- const normalizedNext = normalizeBackgroundInput(rawNext);
1490
- const mergedPatch = {
1491
- ...currentPatch || {},
1492
- ...normalizedNext,
1493
- ...currentPatch?.videoOptions || normalizedNext.videoOptions ? {
1494
- videoOptions: {
1495
- ...currentPatch?.videoOptions || {},
1496
- ...normalizedNext.videoOptions || {}
1497
- }
1498
- } : {}
1499
- };
1500
- return {
1501
- patch: mergedPatch,
1502
- pathname: currentPath
1503
- };
1504
- });
1505
- };
1506
- const currentVideoElement = (current) => videoElementRef.current || current.videoElement;
1507
- return {
1508
- resetBackground: () => {
1509
- overrideStore.publish({
1510
- patch: null,
1511
- pathname: inputsRef.current.pathname
1512
- });
1513
- },
1514
- setBackground: (nextBackground) => {
1515
- updatePatch(() => normalizeBackgroundInput(nextBackground));
1516
- },
1517
- setVideoElement: (videoElement2) => {
1518
- videoElementRef.current = videoElement2;
1519
- updatePatch(
1520
- (cur) => cur.videoElement === videoElement2 ? {} : { videoElement: videoElement2 }
1521
- );
1522
- },
1523
- setVideoMuted: (muted) => {
1524
- const nextMuted = Boolean(muted);
1525
- syncVideoMutedDirect(currentVideoElement(readCurrent()), nextMuted);
1526
- updatePatch((cur) => {
1527
- if (cur.videoOptions?.muted === nextMuted) return {};
1528
- return {
1529
- videoOptions: { ...cur.videoOptions, muted: nextMuted }
1530
- };
1531
- });
1532
- },
1533
- setVideoPlaying: (isPlaying2) => {
1534
- updatePatch(
1535
- (cur) => cur.isPlaying === isPlaying2 ? {} : { isPlaying: isPlaying2 }
1536
- );
1537
- },
1538
- toggleLoop: () => {
1539
- const current = readCurrent();
1540
- const nextLoop = !current.videoOptions?.loop;
1541
- syncVideoLoopDirect(currentVideoElement(current), nextLoop);
1542
- updatePatch((cur) => ({
1543
- videoOptions: { ...cur.videoOptions, loop: nextLoop }
1544
- }));
1545
- },
1546
- toggleMute: () => {
1547
- const current = readCurrent();
1548
- const nextMuted = !current.videoOptions?.muted;
1549
- syncVideoMutedDirect(currentVideoElement(current), nextMuted);
1550
- updatePatch((cur) => ({
1551
- isPlaying: nextMuted ? cur.isPlaying : true,
1552
- videoOptions: { ...cur.videoOptions, muted: nextMuted }
1553
- }));
1554
- },
1555
- toggleVideo: () => {
1556
- const current = readCurrent();
1557
- const videoEl = currentVideoElement(current);
1558
- const domIsActuallyPlaying = videoEl ? !videoEl.paused && !videoEl.ended : Boolean(current.isPlaying);
1559
- const nextPlaying = !domIsActuallyPlaying;
1560
- const configuredMuted = Boolean(current.videoOptions?.muted);
1561
- triggerVideoPlaybackDirect(videoEl, nextPlaying, configuredMuted);
1562
- updatePatch(() => ({ isPlaying: nextPlaying }));
1563
- }
1564
- };
1565
- }, [overrideStore]);
1566
- const media = useOptionalMediaActions();
1567
- const { isPlaying, isVideo, videoElement } = state;
1568
- const isLoop = Boolean(state.videoOptions?.loop);
1569
- useEffect(() => {
1570
- if (!isVideo) {
1571
- media.removeSource(BACKGROUND_MEDIA_ID);
1572
- return;
1573
- }
1574
- media.upsertSource({
1575
- controls: {
1576
- setMuted: actions.setVideoMuted,
1577
- toggle: actions.toggleVideo,
1578
- toggleLoop: actions.toggleLoop
1579
- },
1580
- element: videoElement ?? null,
1581
- id: BACKGROUND_MEDIA_ID,
1582
- isPlaying,
1583
- kind: "video",
1584
- loop: isLoop
1585
- });
1586
- }, [actions, isLoop, isPlaying, isVideo, media, videoElement]);
1587
- useEffect(() => () => media.removeSource(BACKGROUND_MEDIA_ID), [media]);
1588
- const value = useMemo(
1589
- () => ({ actions, store: stateStore }),
1590
- [actions, stateStore]
1591
- );
1592
- return /* @__PURE__ */ jsx(BackgroundContext, { value, children });
1593
- }
1594
-
1595
- // src/modules/background/hooks.ts
1596
- var NOOP_BACKGROUND_STORE = createStore(
1597
- DEFAULT_BACKGROUND_COMPUTED
1598
- );
1599
- function useBackgroundRegistration(config, options) {
1600
- useModuleRegistration("background", config, options);
1601
- }
1602
- function useBackgroundState() {
1603
- const { store } = useRequiredContext(
1604
- BackgroundContext,
1605
- "useBackgroundState",
1606
- "BackgroundProvider"
1607
- );
1608
- return useStore(store);
1609
- }
1610
- function useBackgroundActions() {
1611
- return useRequiredContext(
1612
- BackgroundContext,
1613
- "useBackgroundActions",
1614
- "BackgroundProvider"
1615
- ).actions;
1616
- }
1617
- function useBackground(config, options) {
1618
- useBackgroundRegistration(config, options);
1619
- const actions = useBackgroundActions();
1620
- const state = useBackgroundState();
1621
- return useMemo(() => ({ ...state, ...actions }), [actions, state]);
1622
- }
1623
- function useOptionalBackgroundState(selector, isEqual) {
1624
- const ctx = useContext(BackgroundContext);
1625
- return useStore(
1626
- ctx?.store ?? NOOP_BACKGROUND_STORE,
1627
- selector,
1628
- isEqual
1629
- );
1630
- }
1631
- function useOptionalBackgroundActions() {
1632
- const ctx = useContext(BackgroundContext);
1633
- return ctx?.actions ?? INERT_BACKGROUND_ACTIONS;
1634
- }
1635
- function Icon({
1636
- className = "center",
1637
- color,
1638
- icon,
1639
- onClick,
1640
- size = 20,
1641
- style,
1642
- ...props
1643
- }) {
1644
- return /* @__PURE__ */ jsx(
1645
- Icon$1,
1646
- {
1647
- className,
1648
- height: size,
1649
- icon,
1650
- onClick,
1651
- style: { color, ...style },
1652
- width: size,
1653
- ...props
1654
- }
1655
- );
1656
- }
1657
- Icon.displayName = "Icon";
1658
- function Spinner({ className = "", size = 15 }) {
1659
- return /* @__PURE__ */ jsx(
1660
- "div",
1661
- {
1662
- className,
1663
- "aria-label": "Loading",
1664
- role: "status",
1665
- children: /* @__PURE__ */ jsx(Icon, { icon: "mingcute:loading-3-fill", size })
1666
- }
1667
- );
1668
- }
1669
- Spinner.displayName = "Spinner";
1670
- var PLAYER_STATE = { ENDED: 0, PLAYING: 1, PAUSED: 2, BUFFERING: 3 };
1671
- var apiPromise = null;
1672
- function loadYouTubeIframeApi() {
1673
- if (typeof window === "undefined") return Promise.resolve(null);
1674
- if (window.YT?.Player) return Promise.resolve(window.YT);
1675
- if (apiPromise) return apiPromise;
1676
- apiPromise = new Promise((resolve) => {
1677
- const previousCallback = window.onYouTubeIframeAPIReady;
1678
- window.onYouTubeIframeAPIReady = () => {
1679
- previousCallback?.();
1680
- resolve(window.YT);
1681
- };
1682
- if (!document.querySelector(
1683
- 'script[src="https://www.youtube.com/iframe_api"]'
1684
- )) {
1685
- const script = document.createElement("script");
1686
- script.src = "https://www.youtube.com/iframe_api";
1687
- script.async = true;
1688
- document.head.appendChild(script);
1689
- }
1690
- });
1691
- return apiPromise;
1692
- }
1693
- function setHighQuality(player) {
1694
- player?.setPlaybackQualityRange?.("hd1080", "hd1080");
1695
- player?.setPlaybackQuality?.("hd1080");
1696
- }
1697
- function useYouTubeIframePlayer({
1698
- enabled,
1699
- hostRef,
1700
- isLoop,
1701
- isMuted,
1702
- isPlaying,
1703
- live,
1704
- playbackRate,
1705
- setIsBuffering,
1706
- setIsFrameReady,
1707
- setVideoElement,
1708
- setVideoPlaying,
1709
- shouldAutoPlay,
1710
- startTime,
1711
- videoId
1712
- }) {
1713
- const playerRef = useRef(null);
1714
- useEffect(() => {
1715
- if (!enabled) return void 0;
1716
- let isDisposed = false;
1717
- let progressInterval = null;
1718
- let proxyElement = null;
1719
- const state = {
1720
- currentTime: startTime || 0,
1721
- duration: 0,
1722
- loop: Boolean(isLoop),
1723
- muted: Boolean(isMuted),
1724
- paused: !shouldAutoPlay,
1725
- playbackRate: Number(playbackRate) || 1,
1726
- volume: isMuted ? 0 : 0.8
1727
- };
1728
- const player = () => playerRef.current;
1729
- const restart = (target) => {
1730
- const restartAt = Math.max(0, live.current.startTime || 0);
1731
- target?.seekTo?.(restartAt, true);
1732
- target?.playVideo?.();
1733
- return restartAt;
1734
- };
1735
- const emit = (type) => proxyElement?.dispatchEvent(new Event(type));
1736
- const trackProgress = () => {
1737
- const current = player();
1738
- if (!current || typeof current.getCurrentTime !== "function") return;
1739
- const time = Number(current.getCurrentTime()) || 0;
1740
- const duration = Number(current.getDuration?.()) || state.duration;
1741
- state.currentTime = time;
1742
- if (duration > 0 && duration !== state.duration) {
1743
- state.duration = duration;
1744
- emit("durationchange");
1745
- }
1746
- const { corp, endTime, isLoop: loops } = live.current;
1747
- const safetyEnd = endTime > 0 ? endTime : duration > 0 ? duration - Math.max(corp, 0.4) : 0;
1748
- if (safetyEnd > 0 && time >= safetyEnd) {
1749
- if (state.loop || loops) {
1750
- state.currentTime = restart(current);
1751
- } else {
1752
- current.pauseVideo?.();
1753
- state.paused = true;
1754
- setVideoPlaying(false);
1755
- }
1756
- }
1757
- emit("timeupdate");
1758
- };
1759
- const handleReady = (event) => {
1760
- if (isDisposed) return;
1761
- const target = event.target;
1762
- playerRef.current = target ?? null;
1763
- state.duration = Number(target?.getDuration?.()) || 0;
1764
- const { isMuted: muted, isPlaying: isPlaying2, playbackRate: rate } = live.current;
1765
- if (rate !== 1) target?.setPlaybackRate?.(rate);
1766
- if (muted) {
1767
- target?.mute?.();
1768
- } else {
1769
- target?.unMute?.();
1770
- target?.setVolume?.(80);
1771
- }
1772
- if (live.current.shouldAutoPlay && isPlaying2) {
1773
- setHighQuality(target);
1774
- target?.playVideo?.();
1775
- state.paused = false;
1776
- setVideoPlaying(true);
1777
- }
1778
- if (proxyElement) {
1779
- setVideoElement(proxyElement);
1780
- emit("loadedmetadata");
1781
- emit("durationchange");
1782
- }
1783
- progressInterval = window.setInterval(trackProgress, 200);
1784
- };
1785
- const handleStateChange = (event) => {
1786
- if (isDisposed) return;
1787
- switch (event.data) {
1788
- case PLAYER_STATE.PLAYING:
1789
- setHighQuality(event.target);
1790
- state.paused = false;
1791
- setIsFrameReady(true);
1792
- setIsBuffering(false);
1793
- setVideoPlaying(true);
1794
- emit("play");
1795
- emit("playing");
1796
- break;
1797
- case PLAYER_STATE.BUFFERING:
1798
- setIsBuffering(true);
1799
- break;
1800
- case PLAYER_STATE.PAUSED:
1801
- state.paused = true;
1802
- setIsBuffering(false);
1803
- setVideoPlaying(false);
1804
- emit("pause");
1805
- break;
1806
- case PLAYER_STATE.ENDED:
1807
- if (state.loop || live.current.isLoop) {
1808
- restart(player());
1809
- } else {
1810
- state.paused = true;
1811
- setVideoPlaying(false);
1812
- emit("ended");
1813
- }
1814
- break;
1815
- }
1816
- };
1817
- loadYouTubeIframeApi().then((YT) => {
1818
- const host = hostRef.current;
1819
- if (isDisposed || !YT?.Player || !host) return;
1820
- host.innerHTML = "";
1821
- const mountTarget = document.createElement("div");
1822
- host.appendChild(mountTarget);
1823
- proxyElement = createYouTubeVideoElementProxy({
1824
- getCurrentTime: () => state.currentTime,
1825
- getDuration: () => state.duration,
1826
- getLoop: () => state.loop,
1827
- getMuted: () => state.muted,
1828
- getPaused: () => state.paused,
1829
- getPlaybackRate: () => state.playbackRate,
1830
- getVolume: () => state.volume,
1831
- pause: () => {
1832
- state.paused = true;
1833
- player()?.pauseVideo?.();
1834
- setVideoPlaying(false);
1835
- },
1836
- play: async () => {
1837
- state.paused = false;
1838
- player()?.playVideo?.();
1839
- setVideoPlaying(true);
1840
- },
1841
- seekTo: (seconds) => {
1842
- state.currentTime = seconds;
1843
- player()?.seekTo?.(seconds, true);
1844
- if (!state.paused) player()?.playVideo?.();
1845
- },
1846
- setLoop: (loop) => {
1847
- state.loop = loop;
1848
- },
1849
- setMuted: (muted) => {
1850
- state.muted = muted;
1851
- if (muted) {
1852
- player()?.mute?.();
1853
- return;
1854
- }
1855
- player()?.unMute?.();
1856
- if (state.volume === 0) {
1857
- state.volume = 0.7;
1858
- player()?.setVolume?.(70);
1859
- }
1860
- },
1861
- setPlaybackRate: (rate) => {
1862
- state.playbackRate = rate;
1863
- player()?.setPlaybackRate?.(rate);
1864
- },
1865
- setVolume: (volume) => {
1866
- state.volume = volume;
1867
- player()?.setVolume?.(Math.round(volume * 100));
1868
- if (volume > 0 && state.muted) {
1869
- state.muted = false;
1870
- player()?.unMute?.();
1871
- } else if (volume === 0 && !state.muted) {
1872
- state.muted = true;
1873
- player()?.mute?.();
1874
- }
1875
- }
1876
- });
1877
- const initial = live.current;
1878
- playerRef.current = new YT.Player(mountTarget, {
1879
- events: { onReady: handleReady, onStateChange: handleStateChange },
1880
- host: "https://www.youtube-nocookie.com",
1881
- playerVars: {
1882
- autoplay: initial.shouldAutoPlay ? 1 : 0,
1883
- controls: 0,
1884
- disablekb: 1,
1885
- enablejsapi: 1,
1886
- fs: 0,
1887
- iv_load_policy: 3,
1888
- loop: initial.isLoop ? 1 : 0,
1889
- modestbranding: 1,
1890
- mute: initial.isMuted ? 1 : 0,
1891
- origin: window.location.origin,
1892
- playlist: videoId,
1893
- playsinline: 1,
1894
- rel: 0,
1895
- start: Math.floor(initial.startTime || 0)
1896
- },
1897
- videoId
1898
- });
1899
- });
1900
- return () => {
1901
- isDisposed = true;
1902
- if (progressInterval !== null) window.clearInterval(progressInterval);
1903
- try {
1904
- playerRef.current?.destroy?.();
1905
- } catch {
1906
- }
1907
- playerRef.current = null;
1908
- setVideoElement(null);
1909
- };
1910
- }, [
1911
- enabled,
1912
- hostRef,
1913
- isLoop,
1914
- isMuted,
1915
- live,
1916
- playbackRate,
1917
- setIsBuffering,
1918
- setIsFrameReady,
1919
- setVideoElement,
1920
- setVideoPlaying,
1921
- shouldAutoPlay,
1922
- startTime,
1923
- videoId
1924
- ]);
1925
- useEffect(() => {
1926
- const current = playerRef.current;
1927
- if (!enabled || !current) return;
1928
- try {
1929
- if (isPlaying) current.playVideo?.();
1930
- else current.pauseVideo?.();
1931
- if (isMuted) current.mute?.();
1932
- else current.unMute?.();
1933
- if (playbackRate) current.setPlaybackRate?.(playbackRate);
1934
- } catch {
1935
- }
1936
- }, [enabled, isMuted, isPlaying, playbackRate]);
1937
- }
1938
- var AUDIO_SYNC_EVENTS = [
1939
- "play",
1940
- "playing",
1941
- "pause",
1942
- "waiting",
1943
- "volumechange",
1944
- "ratechange"
1945
- ];
1946
- function setElementMuted(video, muted) {
1947
- video.muted = muted;
1948
- if (!muted && video.volume === 0) video.volume = 0.8;
1949
- }
1950
- function unlockAudioOnGesture(unlock) {
1951
- const handler = () => {
1952
- unlock();
1953
- window.removeEventListener("pointerdown", handler);
1954
- window.removeEventListener("keydown", handler);
1955
- };
1956
- window.addEventListener("pointerdown", handler, { once: true });
1957
- window.addEventListener("keydown", handler, { once: true });
1958
- }
1959
- function useYouTubeBackgroundPlayerModel({
1960
- codec = "auto",
1961
- corp = 0,
1962
- endTime = 0,
1963
- forceIframe = false,
1964
- isLoop = false,
1965
- isMuted = true,
1966
- isPlaying = true,
1967
- playbackRate = 1,
1968
- posterUrl = null,
1969
- quality = "1080p",
1970
- setVideoElement,
1971
- setVideoPlaying,
1972
- shouldAutoPlay = true,
1973
- showPoster = false,
1974
- showSpinner = true,
1975
- startTime = 0,
1976
- videoClasses: _videoClasses,
1977
- videoId,
1978
- videoStyle: _videoStyle
1979
- }) {
1980
- const [useIframeFallback, setUseIframeFallback] = useState(
1981
- Boolean(forceIframe)
1982
- );
1983
- const [isFrameReady, setIsFrameReady] = useState(false);
1984
- const [isBuffering, setIsBuffering] = useState(true);
1985
- const videoRef = useRef(null);
1986
- const audioRef = useRef(null);
1987
- const iframeHostRef = useRef(null);
1988
- const hasLoadedVideoRef = useRef(false);
1989
- const errorRetryCountRef = useRef(0);
1990
- const audioErrorRetryCountRef = useRef(0);
1991
- const retryTimerIdsRef = useRef(/* @__PURE__ */ new Set());
1992
- const lastAudioSeekAtRef = useRef(0);
1993
- const live = useRef({
1994
- corp,
1995
- endTime,
1996
- isLoop,
1997
- isMuted,
1998
- isPlaying,
1999
- playbackRate,
2000
- shouldAutoPlay,
2001
- startTime
2002
- });
2003
- live.current = {
2004
- corp,
2005
- endTime,
2006
- isLoop,
2007
- isMuted,
2008
- isPlaying,
2009
- playbackRate,
2010
- shouldAutoPlay,
2011
- startTime
2012
- };
2013
- useEffect(() => {
2014
- setUseIframeFallback(Boolean(forceIframe));
2015
- setIsFrameReady(false);
2016
- setIsBuffering(true);
2017
- hasLoadedVideoRef.current = false;
2018
- errorRetryCountRef.current = 0;
2019
- audioErrorRetryCountRef.current = 0;
2020
- }, [forceIframe, videoId]);
2021
- const videoStreamUrl = useMemo(
2022
- () => getYouTubeStreamUrl(videoId, { codec, quality, stream: "video" }),
2023
- [codec, quality, videoId]
2024
- );
2025
- const audioStreamUrl = useMemo(
2026
- () => getYouTubeStreamUrl(videoId, { stream: "audio" }),
2027
- [videoId]
2028
- );
2029
- const resolvedPoster = showPoster ? posterUrl || getYouTubeThumbnailUrl(videoId, true) : null;
2030
- const syncCompanionAudio = useCallback((forceSeek = false) => {
2031
- const video = videoRef.current;
2032
- const audio = audioRef.current;
2033
- if (!video || !audio) return;
2034
- try {
2035
- const targetRate = Number(video.playbackRate) || 1;
2036
- if (audio.playbackRate !== targetRate) audio.playbackRate = targetRate;
2037
- if (audio.readyState < 2 || !Number.isFinite(audio.duration) || audio.duration <= 0) {
2038
- return;
2039
- }
2040
- const volume = Math.max(0, Math.min(1, Number(video.volume) ?? 1));
2041
- if (Math.abs(audio.volume - volume) > 5e-3) audio.volume = volume;
2042
- const isSilent = Boolean(video.muted) || volume === 0;
2043
- if (audio.muted !== isSilent) audio.muted = isSilent;
2044
- if (video.paused || video.ended || video.seeking) {
2045
- if (!audio.paused) audio.pause();
2046
- return;
2047
- }
2048
- const now = performance.now();
2049
- const drift = Math.abs(audio.currentTime - video.currentTime);
2050
- const maxDrift = isSilent ? 0.5 : 0.35;
2051
- if (forceSeek || drift > maxDrift && now - lastAudioSeekAtRef.current > 1500) {
2052
- lastAudioSeekAtRef.current = now;
2053
- audio.currentTime = video.currentTime;
2054
- }
2055
- if (audio.paused) audio.play().catch(() => {
2056
- });
2057
- } catch {
2058
- }
2059
- }, []);
2060
- const playWithFallback = useCallback(
2061
- (video, forceSeek, unlockAudio) => {
2062
- const onPlayed = () => {
2063
- setVideoPlaying(true);
2064
- syncCompanionAudio(forceSeek);
2065
- };
2066
- video.play().then(onPlayed).catch((error) => {
2067
- if (error?.name !== "NotAllowedError") return;
2068
- video.muted = true;
2069
- video.play().then(() => {
2070
- onPlayed();
2071
- if (!unlockAudio) return;
2072
- unlockAudioOnGesture(() => {
2073
- const current = videoRef.current;
2074
- if (!live.current.isMuted && current) {
2075
- setElementMuted(current, false);
2076
- }
2077
- });
2078
- }).catch(() => {
2079
- });
2080
- });
2081
- },
2082
- [setVideoPlaying, syncCompanionAudio]
2083
- );
2084
- const scheduleRetry = (callback, delay) => {
2085
- const timerId = window.setTimeout(() => {
2086
- retryTimerIdsRef.current.delete(timerId);
2087
- try {
2088
- callback();
2089
- } catch {
2090
- }
2091
- }, delay);
2092
- retryTimerIdsRef.current.add(timerId);
2093
- };
2094
- const handleEnded = useCallback(() => {
2095
- const video = videoRef.current;
2096
- const audio = audioRef.current;
2097
- if (!video) return;
2098
- if (video.loop || live.current.isLoop) {
2099
- const resetTime = Math.max(0, live.current.startTime || 0);
2100
- video.currentTime = resetTime;
2101
- if (audio && audio.readyState >= 2) audio.currentTime = resetTime;
2102
- video.play().then(() => {
2103
- setVideoPlaying(true);
2104
- syncCompanionAudio(true);
2105
- }).catch(() => {
2106
- });
2107
- return;
2108
- }
2109
- video.pause();
2110
- if (audio && !audio.paused) audio.pause();
2111
- setVideoPlaying(false);
2112
- }, [setVideoPlaying, syncCompanionAudio]);
2113
- const handleTimeUpdate = () => {
2114
- const video = videoRef.current;
2115
- if (!video) return;
2116
- if (video.currentTime > 0 && video.readyState >= 2) {
2117
- setIsFrameReady(true);
2118
- setIsBuffering(false);
2119
- }
2120
- const duration = Number(video.duration) || 0;
2121
- const { corp: activeCorp, endTime: activeEndTime } = live.current;
2122
- const effectiveEnd = activeEndTime > 0 ? activeEndTime : activeCorp > 0 && duration > 0 ? duration - activeCorp : 0;
2123
- if (effectiveEnd > 0 && video.currentTime >= effectiveEnd) handleEnded();
2124
- };
2125
- const handlePlay = () => {
2126
- const video = videoRef.current;
2127
- hasLoadedVideoRef.current = true;
2128
- if (video && !live.current.isMuted && video.muted) {
2129
- setElementMuted(video, false);
2130
- }
2131
- if (!video || video.readyState < 2) setIsBuffering(true);
2132
- setVideoPlaying(true);
2133
- syncCompanionAudio(false);
2134
- };
2135
- const handlePlaying = () => {
2136
- hasLoadedVideoRef.current = true;
2137
- setIsFrameReady(true);
2138
- setIsBuffering(false);
2139
- setVideoPlaying(true);
2140
- syncCompanionAudio(false);
2141
- };
2142
- const handlePause = () => {
2143
- const video = videoRef.current;
2144
- syncCompanionAudio(false);
2145
- setIsBuffering(false);
2146
- if (video && !video.seeking && !video.ended && !live.current.isPlaying) {
2147
- setVideoPlaying(false);
2148
- }
2149
- };
2150
- const handleSeeked = () => {
2151
- const video = videoRef.current;
2152
- if (!video) return;
2153
- setIsFrameReady(true);
2154
- if (video.readyState >= 2 && (!live.current.isPlaying || !video.paused)) {
2155
- setIsBuffering(false);
2156
- }
2157
- syncCompanionAudio(true);
2158
- if (live.current.isPlaying && video.paused) {
2159
- video.play().then(() => {
2160
- setVideoPlaying(true);
2161
- syncCompanionAudio(true);
2162
- }).catch(() => {
2163
- });
2164
- }
2165
- };
2166
- const handleCanPlay = () => {
2167
- const video = videoRef.current;
2168
- if (!video) return;
2169
- hasLoadedVideoRef.current = true;
2170
- if (!live.current.isPlaying) {
2171
- setIsFrameReady(true);
2172
- setIsBuffering(false);
2173
- } else if (video.paused) {
2174
- setElementMuted(video, Boolean(live.current.isMuted));
2175
- playWithFallback(video, false, true);
2176
- }
2177
- };
2178
- const handleLoadedMetadata = () => {
2179
- const video = videoRef.current;
2180
- if (!video) return;
2181
- hasLoadedVideoRef.current = true;
2182
- if (startTime > 0 && video.currentTime < startTime) {
2183
- video.currentTime = startTime;
2184
- if (audioRef.current) audioRef.current.currentTime = startTime;
2185
- }
2186
- };
2187
- const handleLoadedData = () => {
2188
- const video = videoRef.current;
2189
- if (!video) return;
2190
- hasLoadedVideoRef.current = true;
2191
- video.playbackRate = playbackRate;
2192
- setElementMuted(video, Boolean(live.current.isMuted));
2193
- if (startTime > 0 && video.currentTime < startTime) {
2194
- video.currentTime = startTime;
2195
- }
2196
- if (!live.current.isPlaying) {
2197
- setIsFrameReady(true);
2198
- setIsBuffering(false);
2199
- } else if (video.paused) {
2200
- playWithFallback(video, true, false);
2201
- }
2202
- };
2203
- const handleError = () => {
2204
- const video = videoRef.current;
2205
- if (video && hasLoadedVideoRef.current) {
2206
- const resumeTime = video.currentTime || 0;
2207
- scheduleRetry(() => {
2208
- video.load();
2209
- video.currentTime = resumeTime;
2210
- if (live.current.isPlaying) video.play().catch(() => {
2211
- });
2212
- }, 150);
2213
- return;
2214
- }
2215
- errorRetryCountRef.current += 1;
2216
- if (video && errorRetryCountRef.current <= 1) {
2217
- scheduleRetry(() => video.load(), 200);
2218
- return;
2219
- }
2220
- setUseIframeFallback(true);
2221
- };
2222
- const handleAudioError = useCallback(() => {
2223
- const audio = audioRef.current;
2224
- if (!audio) return;
2225
- audioErrorRetryCountRef.current += 1;
2226
- if (audioErrorRetryCountRef.current <= 1) {
2227
- scheduleRetry(() => {
2228
- audio.load();
2229
- if (videoRef.current) {
2230
- audio.currentTime = videoRef.current.currentTime || 0;
2231
- }
2232
- }, 200);
2233
- return;
2234
- }
2235
- if (!live.current.isMuted) {
2236
- setUseIframeFallback(true);
2237
- }
2238
- }, []);
2239
- useEffect(() => {
2240
- if (useIframeFallback) return void 0;
2241
- const video = videoRef.current;
2242
- const audio = audioRef.current;
2243
- const retryTimerIds = retryTimerIdsRef.current;
2244
- if (!video) {
2245
- setVideoElement(null);
2246
- return void 0;
2247
- }
2248
- setVideoElement(video);
2249
- const onSync = () => syncCompanionAudio(false);
2250
- const onSeeked = () => syncCompanionAudio(true);
2251
- for (const event of AUDIO_SYNC_EVENTS) {
2252
- video.addEventListener(event, onSync);
2253
- }
2254
- video.addEventListener("seeked", onSeeked);
2255
- const syncTimer = window.setInterval(onSync, 1e3);
2256
- return () => {
2257
- window.clearInterval(syncTimer);
2258
- retryTimerIds.forEach((timerId) => window.clearTimeout(timerId));
2259
- retryTimerIds.clear();
2260
- for (const event of AUDIO_SYNC_EVENTS) {
2261
- video.removeEventListener(event, onSync);
2262
- }
2263
- video.removeEventListener("seeked", onSeeked);
2264
- try {
2265
- video.pause();
2266
- audio?.pause();
2267
- } catch {
2268
- }
2269
- };
2270
- }, [setVideoElement, syncCompanionAudio, useIframeFallback, videoStreamUrl]);
2271
- useEffect(() => {
2272
- const video = videoRef.current;
2273
- if (useIframeFallback || !video) return;
2274
- applyVideoPlaybackState({
2275
- isPlaying,
2276
- playbackRate,
2277
- setVideoPlaying,
2278
- videoElement: video
2279
- });
2280
- syncCompanionAudio();
2281
- }, [
2282
- isPlaying,
2283
- playbackRate,
2284
- setVideoPlaying,
2285
- syncCompanionAudio,
2286
- useIframeFallback
2287
- ]);
2288
- useEffect(() => {
2289
- const video = videoRef.current;
2290
- if (useIframeFallback || !video) return;
2291
- if (video.muted !== isMuted) setElementMuted(video, isMuted);
2292
- syncCompanionAudio(true);
2293
- }, [isMuted, syncCompanionAudio, useIframeFallback]);
2294
- useYouTubeIframePlayer({
2295
- enabled: useIframeFallback,
2296
- hostRef: iframeHostRef,
2297
- isLoop,
2298
- isMuted,
2299
- isPlaying,
2300
- live,
2301
- playbackRate,
2302
- setIsBuffering,
2303
- setIsFrameReady,
2304
- setVideoElement,
2305
- setVideoPlaying,
2306
- shouldAutoPlay,
2307
- startTime,
2308
- videoId
2309
- });
2310
- const isCovered = !isFrameReady || useIframeFallback && !isPlaying;
2311
- const showSpinnerOverlay = showSpinner && (!isFrameReady || isBuffering);
2312
- return {
2313
- useIframeFallback,
2314
- isFrameReady,
2315
- setIsBuffering,
2316
- videoRef,
2317
- audioRef,
2318
- iframeHostRef,
2319
- videoStreamUrl,
2320
- audioStreamUrl,
2321
- resolvedPoster,
2322
- syncCompanionAudio,
2323
- handleEnded,
2324
- handleTimeUpdate,
2325
- handlePlay,
2326
- handlePlaying,
2327
- handlePause,
2328
- handleSeeked,
2329
- handleCanPlay,
2330
- handleLoadedMetadata,
2331
- handleLoadedData,
2332
- handleError,
2333
- handleAudioError,
2334
- isCovered,
2335
- showSpinnerOverlay
2336
- };
2337
- }
2338
- var YouTubeBackgroundPlayer = memo(function YouTubeBackgroundPlayer2({
2339
- codec = "auto",
2340
- corp = 0,
2341
- endTime = 0,
2342
- forceIframe = false,
2343
- isLoop = false,
2344
- isMuted = true,
2345
- isPlaying = true,
2346
- playbackRate = 1,
2347
- posterUrl = null,
2348
- quality = "1080p",
2349
- setVideoElement,
2350
- setVideoPlaying,
2351
- shouldAutoPlay = true,
2352
- showPoster = false,
2353
- showSpinner = true,
2354
- startTime = 0,
2355
- theme,
2356
- videoClasses,
2357
- videoId,
2358
- videoStyle
2359
- }) {
2360
- const {
2361
- useIframeFallback,
2362
- isFrameReady,
2363
- setIsBuffering: _setIsBuffering,
2364
- videoRef,
2365
- audioRef,
2366
- iframeHostRef,
2367
- videoStreamUrl,
2368
- audioStreamUrl,
2369
- resolvedPoster,
2370
- syncCompanionAudio: _syncCompanionAudio,
2371
- handleEnded,
2372
- handleTimeUpdate,
2373
- handlePlay,
2374
- handlePlaying,
2375
- handlePause,
2376
- handleSeeked,
2377
- handleCanPlay,
2378
- handleLoadedMetadata,
2379
- handleLoadedData,
2380
- handleError,
2381
- handleAudioError,
2382
- isCovered,
2383
- showSpinnerOverlay
2384
- } = useYouTubeBackgroundPlayerModel({
2385
- codec,
2386
- corp,
2387
- endTime,
2388
- forceIframe,
2389
- isLoop,
2390
- isMuted,
2391
- isPlaying,
2392
- playbackRate,
2393
- posterUrl,
2394
- quality,
2395
- setVideoElement,
2396
- setVideoPlaying,
2397
- shouldAutoPlay,
2398
- showPoster,
2399
- showSpinner,
2400
- startTime,
2401
- videoClasses,
2402
- videoId,
2403
- videoStyle
2404
- });
2405
- return /* @__PURE__ */ jsxs("div", { className: theme.slots.youtube, children: [
2406
- /* @__PURE__ */ jsx("div", { "data-visible": isCovered, className: theme.slots.youtubeCover }),
2407
- /* @__PURE__ */ jsx(
2408
- "div",
2409
- {
2410
- "data-visible": showSpinnerOverlay,
2411
- className: theme.slots.youtubeSpinner,
2412
- children: showSpinner ? /* @__PURE__ */ jsx(Spinner, { size: 30, className: theme.slots.youtubeSpinnerIcon }) : null
2413
- }
2414
- ),
2415
- resolvedPoster ? /* @__PURE__ */ jsx(
2416
- "div",
2417
- {
2418
- "data-visible": isCovered,
2419
- className: theme.slots.youtubePoster,
2420
- style: {
2421
- backgroundImage: `url(${resolvedPoster})`,
2422
- ...videoStyle
2423
- }
2424
- }
2425
- ) : null,
2426
- useIframeFallback && !isPlaying ? /* @__PURE__ */ jsx("div", { className: theme.slots.youtubeBlackout }) : null,
2427
- useIframeFallback ? /* @__PURE__ */ jsx(
2428
- "div",
2429
- {
2430
- "data-visible": isFrameReady,
2431
- className: theme.slots.youtubeFrame,
2432
- style: videoStyle,
2433
- children: /* @__PURE__ */ jsx("div", { ref: iframeHostRef, className: theme.slots.youtubeHost })
2434
- }
2435
- ) : /* @__PURE__ */ jsxs(Fragment, { children: [
2436
- /* @__PURE__ */ jsx(
2437
- "video",
2438
- {
2439
- ref: videoRef,
2440
- src: videoStreamUrl,
2441
- "data-visible": isFrameReady,
2442
- className: cn(videoClasses, theme.slots.youtubeVideo),
2443
- preload: "auto",
2444
- muted: isMuted,
2445
- loop: isLoop,
2446
- playsInline: true,
2447
- style: videoStyle,
2448
- onTimeUpdate: handleTimeUpdate,
2449
- onEnded: handleEnded,
2450
- onPlay: handlePlay,
2451
- onWaiting: () => {
2452
- _setIsBuffering(true);
2453
- _syncCompanionAudio(false);
2454
- },
2455
- onPlaying: handlePlaying,
2456
- onPause: handlePause,
2457
- onSeeking: () => _setIsBuffering(true),
2458
- onSeeked: handleSeeked,
2459
- onCanPlay: handleCanPlay,
2460
- onLoadedMetadata: handleLoadedMetadata,
2461
- onLoadedData: handleLoadedData,
2462
- onError: handleError
2463
- }
2464
- ),
2465
- /* @__PURE__ */ jsx(
2466
- "audio",
2467
- {
2468
- ref: audioRef,
2469
- src: audioStreamUrl,
2470
- preload: "auto",
2471
- onCanPlay: () => _syncCompanionAudio(true),
2472
- onError: handleAudioError
2473
- }
2474
- )
2475
- ] })
2476
- ] });
2477
- });
2478
- function useNativeVideoModel({
2479
- corp,
2480
- isLoop,
2481
- isMuted,
2482
- isPlaying,
2483
- playbackRate,
2484
- setVideoElement,
2485
- setVideoPlaying,
2486
- shouldAutoPlay,
2487
- src,
2488
- style: _style,
2489
- className: _className
2490
- }) {
2491
- const videoRef = useRef(null);
2492
- const handleEnded = useCallback(() => {
2493
- const video = videoRef.current;
2494
- if (!video) return;
2495
- if (video.loop) {
2496
- video.currentTime = 0;
2497
- video.play().catch((error) => report("Background loop play", error, "warn"));
2498
- return;
2499
- }
2500
- video.pause();
2501
- setVideoPlaying(false);
2502
- }, [setVideoPlaying]);
2503
- const handleTimeUpdate = useCallback(() => {
2504
- const video = videoRef.current;
2505
- if (video?.duration && corp > 0 && video.currentTime >= video.duration - corp) {
2506
- handleEnded();
2507
- }
2508
- }, [corp, handleEnded]);
2509
- const handleLoadedData = () => {
2510
- const video = videoRef.current;
2511
- if (!video) return;
2512
- video.playbackRate = playbackRate;
2513
- if (!shouldAutoPlay) return;
2514
- if (isMuted) video.muted = true;
2515
- video.play().then(() => setVideoPlaying(true)).catch((error) => {
2516
- report("Background autoplay", error, "warn");
2517
- setVideoPlaying(false);
2518
- });
2519
- };
2520
- useEffect(() => {
2521
- const video = videoRef.current;
2522
- setVideoElement(video);
2523
- return () => {
2524
- try {
2525
- video?.pause();
2526
- } catch {
2527
- }
2528
- };
2529
- }, [src, setVideoElement]);
2530
- useEffect(() => () => setVideoElement(null), [setVideoElement]);
2531
- useEffect(() => {
2532
- applyVideoPlaybackState({
2533
- isPlaying,
2534
- playbackRate,
2535
- setVideoPlaying,
2536
- videoElement: videoRef.current
2537
- });
2538
- }, [isPlaying, playbackRate, setVideoPlaying]);
2539
- return { videoRef, handleEnded, handleTimeUpdate, handleLoadedData };
2540
- }
2541
- function NativeVideo({
2542
- corp,
2543
- isLoop,
2544
- isMuted,
2545
- isPlaying,
2546
- playbackRate,
2547
- setVideoElement,
2548
- setVideoPlaying,
2549
- shouldAutoPlay,
2550
- src,
2551
- style,
2552
- className
2553
- }) {
2554
- const { videoRef, handleEnded, handleTimeUpdate, handleLoadedData } = useNativeVideoModel({
2555
- corp,
2556
- isLoop,
2557
- isMuted,
2558
- isPlaying,
2559
- playbackRate,
2560
- setVideoElement,
2561
- setVideoPlaying,
2562
- shouldAutoPlay,
2563
- src,
2564
- style,
2565
- className
2566
- });
2567
- return /* @__PURE__ */ jsx(
2568
- "video",
2569
- {
2570
- ref: videoRef,
2571
- src,
2572
- className,
2573
- preload: "auto",
2574
- muted: isMuted,
2575
- loop: isLoop,
2576
- playsInline: true,
2577
- style,
2578
- onTimeUpdate: handleTimeUpdate,
2579
- onEnded: handleEnded,
2580
- onLoadedData: handleLoadedData
2581
- }
2582
- );
2583
- }
2584
- var BackgroundGradients = memo(function BackgroundGradients2({
2585
- count = 0,
2586
- direction,
2587
- color = DEFAULT_COLOR,
2588
- theme
2589
- }) {
2590
- if (!count || count <= 0) return null;
2591
- const opacity = Math.min(1, Math.max(0.2, count * 0.25));
2592
- return /* @__PURE__ */ jsx(
2593
- "div",
2594
- {
2595
- "data-side": direction,
2596
- className: theme.slots.gradient,
2597
- style: {
2598
- ...theme.styles.gradient,
2599
- opacity,
2600
- background: generateBaseGradient(direction, color)
2601
- }
2602
- }
2603
- );
2604
- });
2605
- var EdgeGradient = memo(function EdgeGradient2({
2606
- direction,
2607
- percent = 0,
2608
- opacity = 0,
2609
- color,
2610
- theme
2611
- }) {
2612
- if (opacity <= 0) return null;
2613
- return /* @__PURE__ */ jsx(
2614
- "div",
2615
- {
2616
- "data-side": direction,
2617
- className: theme.slots.edge,
2618
- style: {
2619
- width: `${Math.max(30, percent * 1.15)}%`,
2620
- opacity,
2621
- background: generateEdgeGradient(direction, color || DEFAULT_COLOR)
2622
- }
2623
- }
2624
- );
2625
- });
2626
- var NoiseOverlay = memo(function NoiseOverlay2({
2627
- opacity,
2628
- blendMode,
2629
- inlineStyle,
2630
- maskImage,
2631
- isFixed,
2632
- theme
2633
- }) {
2634
- return /* @__PURE__ */ jsx(
2635
- "div",
2636
- {
2637
- "data-position": isFixed ? "fixed" : "absolute",
2638
- className: theme.slots.noise,
2639
- style: {
2640
- ...theme.styles.noise,
2641
- ...typeof opacity === "number" ? { opacity } : {},
2642
- ...typeof blendMode === "string" && blendMode.trim() ? { mixBlendMode: blendMode } : {},
2643
- ...maskImage ? { maskImage, WebkitMaskImage: maskImage } : {},
2644
- ...inlineStyle
2645
- }
2646
- }
2647
- );
2648
- });
2649
- var SolidOverlay = memo(function SolidOverlay2({
2650
- opacity = 0,
2651
- color,
2652
- transitionStyle,
2653
- maskImage,
2654
- theme
2655
- }) {
2656
- return /* @__PURE__ */ jsx(
2657
- "div",
2658
- {
2659
- className: theme.slots.solid,
2660
- style: {
2661
- opacity,
2662
- backgroundColor: color,
2663
- ...maskImage ? { maskImage, WebkitMaskImage: maskImage } : {},
2664
- ...transitionStyle
2665
- }
2666
- }
2667
- );
2668
- });
2669
- function useBackgroundOverlayModel() {
2670
- const {
2671
- animation,
2672
- className,
2673
- fadeEdges,
2674
- fit,
2675
- hasBackground,
2676
- image,
2677
- imageStyle,
2678
- isPlaying,
2679
- isVideo,
2680
- isYouTube,
2681
- leftGradient: configuredLeftGradient,
2682
- noiseStyle,
2683
- overlay,
2684
- overlayColor,
2685
- overlayOpacity,
2686
- position,
2687
- rightGradient: configuredRightGradient,
2688
- video,
2689
- videoClassName,
2690
- videoOptions,
2691
- videoStyle,
2692
- width
2693
- } = useBackgroundState();
2694
- const { setVideoElement, setVideoPlaying } = useBackgroundActions();
2695
- const options = resolveVideoOptions(videoOptions);
2696
- const { corp, isMuted, playbackRate } = options;
2697
- const youtubeConfig = useMemo(
2698
- () => isYouTube ? parseYouTubeUrlConfig(video) : null,
2699
- [isYouTube, video]
2700
- );
2701
- const startTime = videoOptions?.startTime ?? youtubeConfig?.startTime ?? 0;
2702
- const endTime = videoOptions?.endTime ?? youtubeConfig?.endTime ?? 0;
2703
- const backgroundKey = isVideo ? youtubeConfig ? `youtube:${youtubeConfig.videoId}` : video : image;
2704
- const motionConfig = useMemo(
2705
- () => getBackgroundMotionConfig(animation),
2706
- [animation]
2707
- );
2708
- const {
2709
- baseStyle,
2710
- leftGradient: styleLeft,
2711
- rightGradient: styleRight
2712
- } = useMemo(
2713
- () => getVisualStyle((isVideo ? videoStyle : imageStyle) || {}),
2714
- [imageStyle, isVideo, videoStyle]
2715
- );
2716
- const leftGradient = configuredLeftGradient ?? styleLeft;
2717
- const rightGradient = configuredRightGradient ?? styleRight;
2718
- const {
2719
- opacity: noiseOpacity,
2720
- mixBlendMode: noiseBlendMode,
2721
- ...noiseInlineStyle
2722
- } = noiseStyle || {};
2723
- const overlayTransitionStyle = useMemo(
2724
- () => ({
2725
- transitionDelay: toCssDelay(motionConfig.transition.delay),
2726
- transitionDuration: toCssDuration(motionConfig.transition.duration),
2727
- transitionProperty: "opacity",
2728
- transitionTimingFunction: toCssEasing(motionConfig.transition.ease)
2729
- }),
2730
- [motionConfig.transition]
2731
- );
2732
- const rawWidth = width ?? videoOptions?.width ?? videoStyle?.width;
2733
- const resolvedWidth = rawWidth === void 0 || rawWidth === null || rawWidth === "" ? void 0 : typeof rawWidth === "number" ? `${rawWidth}px` : String(rawWidth);
2734
- const rawFit = fit ?? videoOptions?.fit ?? videoOptions?.objectFit;
2735
- const theme = useTheme(backgroundTheme);
2736
- const fitStyle = useMemo(
2737
- () => rawFit && OBJECT_FITS.includes(rawFit) ? { objectFit: rawFit } : {},
2738
- [rawFit]
2739
- );
2740
- const classNames = resolveVideoClassNames(
2741
- videoClassName,
2742
- className,
2743
- videoOptions?.videoClassName,
2744
- videoOptions?.className,
2745
- videoStyle?.className
2746
- );
2747
- const hasCustomWidth = Boolean(resolvedWidth || classNames.width);
2748
- const gradientSettings = useMemo(
2749
- () => resolveGradientSettings({
2750
- fadeEdges,
2751
- hasWidth: hasCustomWidth,
2752
- leftGradient,
2753
- rightGradient
2754
- }),
2755
- [fadeEdges, hasCustomWidth, leftGradient, rightGradient]
2756
- );
2757
- const maskImage = useMemo(
2758
- () => gradientSettings.enabled ? getEdgeFadeMask({
2759
- color: overlayColor || DEFAULT_COLOR,
2760
- leftPercent: gradientSettings.leftPercent,
2761
- rightPercent: gradientSettings.rightPercent
2762
- }) : void 0,
2763
- [gradientSettings, overlayColor]
2764
- );
2765
- const framePosition = position === "left" || position === "right" ? position : "center";
2766
- const videoClasses = cn(theme.slots.video, classNames.other);
2767
- const objectPosition2 = baseStyle.objectPosition || position || void 0;
2768
- const sharedVideoStyle = useMemo(
2769
- () => ({
2770
- ...classNames.objectStyle,
2771
- ...fitStyle,
2772
- ...objectPosition2 ? { objectPosition: objectPosition2 } : {},
2773
- ...maskImage ? { WebkitMaskImage: maskImage, maskImage } : {},
2774
- ...baseStyle
2775
- }),
2776
- [baseStyle, classNames.objectStyle, fitStyle, maskImage, objectPosition2]
2777
- );
2778
- const exitDurationFactor = Number.isFinite(motionConfig.exitDurationFactor) ? Math.max(0, motionConfig.exitDurationFactor) : 0.6;
2779
- const exitTransition = motionConfig.exit.transition;
2780
- return {
2781
- className,
2782
- hasBackground,
2783
- image,
2784
- isPlaying,
2785
- isVideo,
2786
- overlay,
2787
- overlayColor,
2788
- overlayOpacity,
2789
- position,
2790
- video,
2791
- videoStyle,
2792
- width,
2793
- setVideoElement,
2794
- setVideoPlaying,
2795
- options,
2796
- corp,
2797
- isMuted,
2798
- playbackRate,
2799
- youtubeConfig,
2800
- startTime,
2801
- endTime,
2802
- backgroundKey,
2803
- motionConfig,
2804
- baseStyle,
2805
- leftGradient,
2806
- rightGradient,
2807
- noiseOpacity,
2808
- noiseBlendMode,
2809
- noiseInlineStyle,
2810
- overlayTransitionStyle,
2811
- resolvedWidth,
2812
- classNames,
2813
- hasCustomWidth,
2814
- gradientSettings,
2815
- maskImage,
2816
- framePosition,
2817
- videoClasses,
2818
- sharedVideoStyle,
2819
- exitDurationFactor,
2820
- exitTransition,
2821
- theme
2822
- };
2823
- }
2824
- function BackgroundOverlay() {
2825
- const {
2826
- hasBackground,
2827
- image,
2828
- isPlaying,
2829
- isVideo,
2830
- overlay,
2831
- overlayColor,
2832
- overlayOpacity,
2833
- position,
2834
- video,
2835
- setVideoElement,
2836
- setVideoPlaying,
2837
- options,
2838
- corp,
2839
- isMuted,
2840
- playbackRate,
2841
- youtubeConfig,
2842
- startTime,
2843
- endTime,
2844
- backgroundKey,
2845
- motionConfig,
2846
- baseStyle,
2847
- leftGradient,
2848
- rightGradient,
2849
- noiseOpacity,
2850
- noiseBlendMode,
2851
- noiseInlineStyle,
2852
- overlayTransitionStyle,
2853
- resolvedWidth,
2854
- classNames,
2855
- hasCustomWidth,
2856
- gradientSettings,
2857
- maskImage,
2858
- framePosition,
2859
- videoClasses,
2860
- sharedVideoStyle,
2861
- exitDurationFactor,
2862
- exitTransition,
2863
- theme
2864
- } = useBackgroundOverlayModel();
2865
- return /* @__PURE__ */ jsx(AnimatePresence, { mode: "sync", children: hasBackground && /* @__PURE__ */ jsxs(
2866
- motion.div,
2867
- {
2868
- initial: motionConfig.initial,
2869
- animate: motionConfig.animate,
2870
- transition: motionConfig.transition,
2871
- exit: {
2872
- ...motionConfig.exit,
2873
- transition: {
2874
- ...motionConfig.transition,
2875
- delay: 0,
2876
- duration: exitTransition?.duration ?? (motionConfig.transition.duration ?? 0.6) * exitDurationFactor,
2877
- ease: exitTransition?.ease ?? EASING_CURVES.IN_CUBIC
2878
- }
2879
- },
2880
- className: theme.slots.root,
2881
- style: {
2882
- willChange: "transform, opacity, filter",
2883
- ...theme.styles.root
2884
- },
2885
- children: [
2886
- isVideo ? /* @__PURE__ */ jsxs(
2887
- "div",
2888
- {
2889
- "data-position": framePosition,
2890
- "data-width": classNames.width || resolvedWidth ? void 0 : "full",
2891
- className: cn(theme.slots.videoFrame, classNames.width),
2892
- style: {
2893
- ...theme.styles.videoFrame,
2894
- ...resolvedWidth ? { width: resolvedWidth } : {}
2895
- },
2896
- children: [
2897
- youtubeConfig ? /* @__PURE__ */ jsx(
2898
- YouTubeBackgroundPlayer,
2899
- {
2900
- videoId: youtubeConfig.videoId,
2901
- startTime,
2902
- endTime,
2903
- corp,
2904
- isPlaying,
2905
- isMuted,
2906
- isLoop: options.isLoop,
2907
- shouldAutoPlay: options.shouldAutoPlay,
2908
- showPoster: options.showPoster,
2909
- showSpinner: options.showSpinner,
2910
- playbackRate,
2911
- quality: options.quality,
2912
- codec: options.codec,
2913
- forceIframe: options.forceIframe,
2914
- posterUrl: options.showPoster ? image || null : null,
2915
- theme,
2916
- videoClasses,
2917
- videoStyle: sharedVideoStyle,
2918
- setVideoElement,
2919
- setVideoPlaying
2920
- }
2921
- ) : /* @__PURE__ */ jsx(
2922
- NativeVideo,
2923
- {
2924
- src: video || void 0,
2925
- className: videoClasses,
2926
- style: sharedVideoStyle,
2927
- corp,
2928
- isLoop: options.isLoop,
2929
- isMuted,
2930
- isPlaying,
2931
- playbackRate,
2932
- shouldAutoPlay: options.shouldAutoPlay,
2933
- setVideoElement,
2934
- setVideoPlaying
2935
- }
2936
- ),
2937
- /* @__PURE__ */ jsx(
2938
- EdgeGradient,
2939
- {
2940
- theme,
2941
- direction: "left",
2942
- percent: gradientSettings.leftPercent,
2943
- opacity: gradientSettings.leftOpacity,
2944
- color: overlayColor
2945
- }
2946
- ),
2947
- /* @__PURE__ */ jsx(
2948
- EdgeGradient,
2949
- {
2950
- theme,
2951
- direction: "right",
2952
- percent: gradientSettings.rightPercent,
2953
- opacity: gradientSettings.rightOpacity,
2954
- color: overlayColor
2955
- }
2956
- ),
2957
- hasCustomWidth && /* @__PURE__ */ jsx(
2958
- NoiseOverlay,
2959
- {
2960
- theme,
2961
- opacity: noiseOpacity,
2962
- blendMode: noiseBlendMode,
2963
- inlineStyle: noiseInlineStyle,
2964
- maskImage
2965
- }
2966
- ),
2967
- hasCustomWidth && overlay && /* @__PURE__ */ jsx(
2968
- SolidOverlay,
2969
- {
2970
- theme,
2971
- opacity: overlayOpacity,
2972
- color: overlayColor,
2973
- transitionStyle: overlayTransitionStyle,
2974
- maskImage
2975
- }
2976
- )
2977
- ]
2978
- }
2979
- ) : /* @__PURE__ */ jsx(
2980
- "div",
2981
- {
2982
- className: theme.slots.image,
2983
- style: {
2984
- backgroundImage: image ? `url(${image})` : void 0,
2985
- backgroundPosition: position,
2986
- ...baseStyle
2987
- }
2988
- }
2989
- ),
2990
- (!isVideo || !hasCustomWidth) && /* @__PURE__ */ jsxs(Fragment, { children: [
2991
- /* @__PURE__ */ jsx(
2992
- BackgroundGradients,
2993
- {
2994
- theme,
2995
- count: leftGradient,
2996
- direction: "left",
2997
- color: overlayColor
2998
- }
2999
- ),
3000
- /* @__PURE__ */ jsx(
3001
- BackgroundGradients,
3002
- {
3003
- theme,
3004
- count: rightGradient,
3005
- direction: "right",
3006
- color: overlayColor
3007
- }
3008
- ),
3009
- /* @__PURE__ */ jsx(
3010
- NoiseOverlay,
3011
- {
3012
- theme,
3013
- isFixed: true,
3014
- opacity: noiseOpacity,
3015
- blendMode: noiseBlendMode,
3016
- inlineStyle: noiseInlineStyle
3017
- }
3018
- ),
3019
- overlay && /* @__PURE__ */ jsx(
3020
- SolidOverlay,
3021
- {
3022
- theme,
3023
- opacity: overlayOpacity,
3024
- color: overlayColor,
3025
- transitionStyle: overlayTransitionStyle
3026
- }
3027
- )
3028
- ] })
3029
- ]
3030
- },
3031
- backgroundKey
3032
- ) });
3033
- }
3034
- function selectPageBackground(background) {
3035
- return background ? normalizeBackgroundInput(background) : null;
3036
- }
3037
- function useBackgroundPage(_slice, page) {
3038
- const actions = useBackgroundActions();
3039
- return useMemo(
3040
- () => ({
3041
- ...actions,
3042
- set: (background) => page.set({ background })
3043
- }),
3044
- [actions, page]
3045
- );
3046
- }
3047
- var backgroundModule = defineModule({
3048
- id: "background",
3049
- context: BackgroundContext,
3050
- Provider: BackgroundProvider,
3051
- Backdrop: BackgroundOverlay,
3052
- uses: ["media"],
3053
- registry: {
3054
- cleanupDelayMs: 600,
3055
- keyPolicy: "singleton",
3056
- lifecycle: "immediate",
3057
- singletonKey: BACKGROUND_REGISTRY_KEY
3058
- },
3059
- page: {
3060
- select: (config) => selectPageBackground(config.background),
3061
- entries: (slice) => [
3062
- { key: BACKGROUND_REGISTRY_KEY, value: slice }
3063
- ],
3064
- use: useBackgroundPage
3065
- }
3066
- });
3067
- function defineBackground(definition = {}) {
3068
- const { id = "background", ...config } = typeof definition === "string" ? { image: definition } : definition;
3069
- return Object.freeze({
3070
- config,
3071
- id,
3072
- use: function useDefinedBackground(overrides = {}, options = {}) {
3073
- const patch = typeof overrides === "string" ? { image: overrides } : overrides;
3074
- return useBackground({ ...config, ...patch }, options);
3075
- }
3076
- });
3077
- }
3078
-
3079
- export { BACKGROUND_MEDIA_ID, BACKGROUND_REGISTRY_KEY, BG_TOKEN_TO_OBJECT_STYLE, BackgroundContext, BackgroundOverlay, BackgroundProvider, DEFAULT_BACKGROUND, DEFAULT_BACKGROUND_COMPUTED, DEFAULT_COLOR, INERT_BACKGROUND_ACTIONS, OBJECT_FITS, YouTubeBackgroundPlayer, applyVideoPlaybackState, backgroundModule, backgroundTheme, computeBackgroundState, defineBackground, extractYouTubeVideoId, generateBaseGradient, generateEdgeGradient, getEdgeFadeMask, getYouTubeStreamUrl, getYouTubeThumbnailUrl, isDirectVideoUrl, isYouTubeUrl, mergeBackgroundState, normalizeBackgroundInput, parseYouTubeUrlConfig, resolveBackgroundState, resolveVideoClassNames, resolveVideoOptions, selectPageBackground, syncVideoLoopDirect, syncVideoMutedDirect, triggerVideoPlaybackDirect, useBackground, useBackgroundActions, useBackgroundRegistration, useBackgroundState, useOptionalBackgroundActions, useOptionalBackgroundState };
2
+ export { BACKGROUND_MEDIA_ID, BACKGROUND_REGISTRY_KEY, BG_TOKEN_TO_OBJECT_STYLE, BackgroundContext, BackgroundOverlay, BackgroundProvider, DEFAULT_BACKGROUND, DEFAULT_BACKGROUND_COMPUTED, DEFAULT_COLOR, INERT_BACKGROUND_ACTIONS, OBJECT_FITS, YouTubeBackgroundPlayer, applyVideoPlaybackState, backgroundModule, backgroundTheme, computeBackgroundState, defineBackground, extractYouTubeVideoId, generateBaseGradient, generateEdgeGradient, getEdgeFadeMask, getYouTubeStreamUrl, getYouTubeThumbnailUrl, isDirectVideoUrl, isYouTubeUrl, mergeBackgroundState, normalizeBackgroundInput, parseYouTubeUrlConfig, resolveBackgroundState, resolveVideoClassNames, resolveVideoOptions, selectPageBackground, syncVideoLoopDirect, syncVideoMutedDirect, triggerVideoPlaybackDirect, useBackground, useBackgroundActions, useBackgroundRegistration, useBackgroundState, useOptionalBackgroundActions, useOptionalBackgroundState } from '../../chunk-CGHZSSFR.js';
3
+ import '../../chunk-R2YGFJDJ.js';
4
+ import '../../chunk-7TAVYJ3A.js';
5
+ import '../../chunk-GF7TUX5I.js';
6
+ import '../../chunk-TV3WEF5G.js';
7
+ import '../../chunk-3BUP64Q6.js';
3080
8
  //# sourceMappingURL=index.js.map
3081
9
  //# sourceMappingURL=index.js.map