stoop 0.4.0 → 0.5.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.
- package/README.md +9 -4
- package/dist/api/core-api.d.ts +1 -0
- package/dist/api/styled.d.ts +2 -8
- package/dist/core/cache.d.ts +0 -1
- package/dist/core/stringify.d.ts +19 -0
- package/dist/core/theme-manager.d.ts +3 -11
- package/dist/create-stoop-internal.d.ts +3 -5
- package/dist/create-stoop-ssr.d.ts +0 -2
- package/dist/create-stoop-ssr.js +16 -26
- package/dist/create-stoop.d.ts +3 -7
- package/dist/create-stoop.js +16 -156
- package/dist/inject.d.ts +2 -4
- package/dist/types/index.d.ts +20 -9
- package/dist/utils/helpers.d.ts +0 -3
- package/dist/utils/storage.d.ts +2 -95
- package/dist/utils/theme-utils.d.ts +0 -13
- package/dist/utils/theme.d.ts +1 -3
- package/package.json +11 -13
- package/dist/api/core-api.js +0 -135
- package/dist/api/global-css.d.ts +0 -7
- package/dist/api/global-css.js +0 -42
- package/dist/api/styled.js +0 -419
- package/dist/api/theme-provider.js +0 -223
- package/dist/constants.js +0 -154
- package/dist/core/cache.js +0 -68
- package/dist/core/compiler.js +0 -206
- package/dist/core/theme-manager.js +0 -107
- package/dist/core/variants.d.ts +0 -15
- package/dist/core/variants.js +0 -38
- package/dist/create-stoop-internal.js +0 -123
- package/dist/inject.js +0 -308
- package/dist/types/index.js +0 -5
- package/dist/utils/auto-preload.d.ts +0 -45
- package/dist/utils/auto-preload.js +0 -167
- package/dist/utils/helpers.js +0 -150
- package/dist/utils/storage.js +0 -396
- package/dist/utils/theme-utils.js +0 -353
- package/dist/utils/theme.js +0 -304
package/dist/utils/helpers.js
DELETED
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Helper utilities for Stoop.
|
|
3
|
-
* Consolidates environment detection, type guards, theme validation, and utility function application.
|
|
4
|
-
*/
|
|
5
|
-
import { APPROVED_THEME_SCALES } from "../constants";
|
|
6
|
-
// ============================================================================
|
|
7
|
-
// Environment Detection
|
|
8
|
-
// ============================================================================
|
|
9
|
-
/**
|
|
10
|
-
* Checks if code is running in a browser environment.
|
|
11
|
-
*
|
|
12
|
-
* @returns True if running in browser, false if in SSR/Node environment
|
|
13
|
-
*/
|
|
14
|
-
export function isBrowser() {
|
|
15
|
-
return (typeof window !== "undefined" &&
|
|
16
|
-
typeof document !== "undefined" &&
|
|
17
|
-
typeof window.document === "object" &&
|
|
18
|
-
// Use document.createElement instead of requestAnimationFrame for better compatibility
|
|
19
|
-
// requestAnimationFrame may not exist in test environments (jsdom)
|
|
20
|
-
typeof document.createElement === "function");
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Checks if running in production mode.
|
|
24
|
-
* Extracted to helper function for consistency.
|
|
25
|
-
*
|
|
26
|
-
* @returns True if running in production mode
|
|
27
|
-
*/
|
|
28
|
-
export function isProduction() {
|
|
29
|
-
return typeof process !== "undefined" && process.env?.NODE_ENV === "production";
|
|
30
|
-
}
|
|
31
|
-
// ============================================================================
|
|
32
|
-
// Type Guards
|
|
33
|
-
// ============================================================================
|
|
34
|
-
/**
|
|
35
|
-
* Type guard for CSS objects.
|
|
36
|
-
*
|
|
37
|
-
* @param value - Value to check
|
|
38
|
-
* @returns True if value is a CSS object
|
|
39
|
-
*/
|
|
40
|
-
export function isCSSObject(value) {
|
|
41
|
-
return typeof value === "object" && value !== null;
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Checks if a value is a styled component reference.
|
|
45
|
-
* Consolidated function used across the codebase.
|
|
46
|
-
*
|
|
47
|
-
* @param value - Value to check
|
|
48
|
-
* @returns True if value is a styled component reference
|
|
49
|
-
*/
|
|
50
|
-
export function isStyledComponentRef(value) {
|
|
51
|
-
return (typeof value === "object" &&
|
|
52
|
-
value !== null &&
|
|
53
|
-
"__isStoopStyled" in value &&
|
|
54
|
-
"__stoopClassName" in value &&
|
|
55
|
-
value.__isStoopStyled === true);
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Type guard for valid CSS objects (excludes styled component references).
|
|
59
|
-
*
|
|
60
|
-
* @param value - Value to check
|
|
61
|
-
* @returns True if value is a valid CSS object
|
|
62
|
-
*/
|
|
63
|
-
export function isValidCSSObject(value) {
|
|
64
|
-
return isCSSObject(value) && !isStyledComponentRef(value);
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Type guard for theme objects.
|
|
68
|
-
*
|
|
69
|
-
* @param value - Value to check
|
|
70
|
-
* @returns True if value is a theme object
|
|
71
|
-
*/
|
|
72
|
-
export function isThemeObject(value) {
|
|
73
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
74
|
-
}
|
|
75
|
-
// ============================================================================
|
|
76
|
-
// Theme Validation
|
|
77
|
-
// ============================================================================
|
|
78
|
-
/**
|
|
79
|
-
* Validates that a theme object only contains approved scales.
|
|
80
|
-
* In production, skips all validation for performance.
|
|
81
|
-
*
|
|
82
|
-
* @param theme - Theme object to validate
|
|
83
|
-
* @returns Validated theme as DefaultTheme
|
|
84
|
-
* @throws Error if theme contains invalid scales (development only)
|
|
85
|
-
*/
|
|
86
|
-
export function validateTheme(theme) {
|
|
87
|
-
if (!theme || typeof theme !== "object" || Array.isArray(theme)) {
|
|
88
|
-
throw new Error("[Stoop] Theme must be a non-null object");
|
|
89
|
-
}
|
|
90
|
-
// Skip all validation in production for performance
|
|
91
|
-
if (isProduction()) {
|
|
92
|
-
return theme;
|
|
93
|
-
}
|
|
94
|
-
const themeObj = theme;
|
|
95
|
-
const invalidScales = [];
|
|
96
|
-
for (const key in themeObj) {
|
|
97
|
-
if (key === "media") {
|
|
98
|
-
continue;
|
|
99
|
-
}
|
|
100
|
-
if (!APPROVED_THEME_SCALES.includes(key)) {
|
|
101
|
-
invalidScales.push(key);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
if (invalidScales.length > 0) {
|
|
105
|
-
const errorMessage = `[Stoop] Theme contains invalid scales: ${invalidScales.join(", ")}. ` +
|
|
106
|
-
`Only these scales are allowed: ${APPROVED_THEME_SCALES.join(", ")}`;
|
|
107
|
-
throw new Error(errorMessage);
|
|
108
|
-
}
|
|
109
|
-
return theme;
|
|
110
|
-
}
|
|
111
|
-
// ============================================================================
|
|
112
|
-
// Utility Function Application
|
|
113
|
-
// ============================================================================
|
|
114
|
-
/**
|
|
115
|
-
* Applies utility functions to transform shorthand properties into CSS.
|
|
116
|
-
*
|
|
117
|
-
* @param styles - CSS object to process
|
|
118
|
-
* @param utils - Optional utility functions object
|
|
119
|
-
* @returns CSS object with utilities applied
|
|
120
|
-
*/
|
|
121
|
-
export function applyUtilities(styles, utils) {
|
|
122
|
-
if (!utils || !styles || typeof styles !== "object") {
|
|
123
|
-
return styles;
|
|
124
|
-
}
|
|
125
|
-
const result = {};
|
|
126
|
-
const utilityKeys = Object.keys(utils);
|
|
127
|
-
for (const key in styles) {
|
|
128
|
-
const value = styles[key];
|
|
129
|
-
if (utilityKeys.includes(key) && utils[key]) {
|
|
130
|
-
try {
|
|
131
|
-
const utilityResult = utils[key](value);
|
|
132
|
-
if (utilityResult && typeof utilityResult === "object") {
|
|
133
|
-
for (const utilKey in utilityResult) {
|
|
134
|
-
result[utilKey] = utilityResult[utilKey];
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
catch {
|
|
139
|
-
result[key] = value;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
else if (isCSSObject(value)) {
|
|
143
|
-
result[key] = applyUtilities(value, utils);
|
|
144
|
-
}
|
|
145
|
-
else {
|
|
146
|
-
result[key] = value;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
return result;
|
|
150
|
-
}
|
package/dist/utils/storage.js
DELETED
|
@@ -1,396 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Storage and theme detection utilities.
|
|
3
|
-
* Provides simplified localStorage and cookie management, plus automatic theme selection.
|
|
4
|
-
* Supports SSR compatibility and error handling.
|
|
5
|
-
*/
|
|
6
|
-
import { DEFAULT_COOKIE_MAX_AGE, DEFAULT_COOKIE_PATH } from "../constants";
|
|
7
|
-
import { isBrowser } from "./helpers";
|
|
8
|
-
// ============================================================================
|
|
9
|
-
// Storage Utilities
|
|
10
|
-
// ============================================================================
|
|
11
|
-
/**
|
|
12
|
-
* Parses a JSON value safely.
|
|
13
|
-
*
|
|
14
|
-
* @param value - String value to parse
|
|
15
|
-
* @param fallback - Fallback value if parsing fails
|
|
16
|
-
* @returns Parsed value or fallback
|
|
17
|
-
*/
|
|
18
|
-
function safeJsonParse(value, fallback) {
|
|
19
|
-
try {
|
|
20
|
-
return JSON.parse(value);
|
|
21
|
-
}
|
|
22
|
-
catch {
|
|
23
|
-
return fallback;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Safely gets a value from localStorage.
|
|
28
|
-
*
|
|
29
|
-
* @param key - Storage key
|
|
30
|
-
* @returns Storage result
|
|
31
|
-
*/
|
|
32
|
-
export function getFromStorage(key) {
|
|
33
|
-
if (!isBrowser()) {
|
|
34
|
-
return { error: "Not in browser environment", success: false, value: null };
|
|
35
|
-
}
|
|
36
|
-
try {
|
|
37
|
-
const value = localStorage.getItem(key);
|
|
38
|
-
return { source: "localStorage", success: true, value };
|
|
39
|
-
}
|
|
40
|
-
catch (error) {
|
|
41
|
-
return {
|
|
42
|
-
error: error instanceof Error ? error.message : "localStorage access failed",
|
|
43
|
-
success: false,
|
|
44
|
-
value: null,
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* Safely sets a value in localStorage.
|
|
50
|
-
*
|
|
51
|
-
* @param key - Storage key
|
|
52
|
-
* @param value - Value to store
|
|
53
|
-
* @returns Storage result
|
|
54
|
-
*/
|
|
55
|
-
export function setInStorage(key, value) {
|
|
56
|
-
if (!isBrowser()) {
|
|
57
|
-
return { error: "Not in browser environment", success: false, value: undefined };
|
|
58
|
-
}
|
|
59
|
-
try {
|
|
60
|
-
localStorage.setItem(key, value);
|
|
61
|
-
return { success: true, value: undefined };
|
|
62
|
-
}
|
|
63
|
-
catch (error) {
|
|
64
|
-
return {
|
|
65
|
-
error: error instanceof Error ? error.message : "localStorage write failed",
|
|
66
|
-
success: false,
|
|
67
|
-
value: undefined,
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
/**
|
|
72
|
-
* Safely removes a value from localStorage.
|
|
73
|
-
*
|
|
74
|
-
* @param key - Storage key
|
|
75
|
-
* @returns Storage result
|
|
76
|
-
*/
|
|
77
|
-
export function removeFromStorage(key) {
|
|
78
|
-
if (!isBrowser()) {
|
|
79
|
-
return { error: "Not in browser environment", success: false, value: undefined };
|
|
80
|
-
}
|
|
81
|
-
try {
|
|
82
|
-
localStorage.removeItem(key);
|
|
83
|
-
return { success: true, value: undefined };
|
|
84
|
-
}
|
|
85
|
-
catch (error) {
|
|
86
|
-
return {
|
|
87
|
-
error: error instanceof Error ? error.message : "localStorage remove failed",
|
|
88
|
-
success: false,
|
|
89
|
-
value: undefined,
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
/**
|
|
94
|
-
* Gets a cookie value.
|
|
95
|
-
*
|
|
96
|
-
* @param name - Cookie name
|
|
97
|
-
* @returns Cookie value or null if not found
|
|
98
|
-
*/
|
|
99
|
-
export function getCookie(name) {
|
|
100
|
-
if (!isBrowser())
|
|
101
|
-
return null;
|
|
102
|
-
const value = `; ${document.cookie}`;
|
|
103
|
-
const parts = value.split(`; ${name}=`);
|
|
104
|
-
if (parts.length === 2) {
|
|
105
|
-
return parts.pop()?.split(";").shift() || null;
|
|
106
|
-
}
|
|
107
|
-
return null;
|
|
108
|
-
}
|
|
109
|
-
/**
|
|
110
|
-
* Sets a cookie value.
|
|
111
|
-
*
|
|
112
|
-
* @param name - Cookie name
|
|
113
|
-
* @param value - Cookie value
|
|
114
|
-
* @param options - Cookie options
|
|
115
|
-
* @returns Success status
|
|
116
|
-
*/
|
|
117
|
-
export function setCookie(name, value, options = {}) {
|
|
118
|
-
if (!isBrowser())
|
|
119
|
-
return false;
|
|
120
|
-
const { maxAge = DEFAULT_COOKIE_MAX_AGE, path = DEFAULT_COOKIE_PATH, secure = false } = options;
|
|
121
|
-
try {
|
|
122
|
-
document.cookie = `${name}=${value}; path=${path}; max-age=${maxAge}${secure ? "; secure" : ""}`;
|
|
123
|
-
return true;
|
|
124
|
-
}
|
|
125
|
-
catch {
|
|
126
|
-
return false;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
/**
|
|
130
|
-
* Removes a cookie by setting it to expire.
|
|
131
|
-
*
|
|
132
|
-
* @param name - Cookie name
|
|
133
|
-
* @param path - Cookie path
|
|
134
|
-
* @returns Success status
|
|
135
|
-
*/
|
|
136
|
-
export function removeCookie(name, path = "/") {
|
|
137
|
-
if (!isBrowser())
|
|
138
|
-
return false;
|
|
139
|
-
try {
|
|
140
|
-
document.cookie = `${name}=; path=${path}; max-age=0`;
|
|
141
|
-
return true;
|
|
142
|
-
}
|
|
143
|
-
catch {
|
|
144
|
-
return false;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
/**
|
|
148
|
-
* Unified storage API that works with both localStorage and cookies.
|
|
149
|
-
*
|
|
150
|
-
* @param key - Storage key
|
|
151
|
-
* @param options - Storage options
|
|
152
|
-
* @returns Storage result
|
|
153
|
-
*/
|
|
154
|
-
export function getStorage(key, options = {}) {
|
|
155
|
-
const { type = "localStorage" } = options;
|
|
156
|
-
if (type === "cookie") {
|
|
157
|
-
const value = getCookie(key);
|
|
158
|
-
return {
|
|
159
|
-
source: "cookie",
|
|
160
|
-
success: value !== null,
|
|
161
|
-
value,
|
|
162
|
-
};
|
|
163
|
-
}
|
|
164
|
-
return getFromStorage(key);
|
|
165
|
-
}
|
|
166
|
-
/**
|
|
167
|
-
* Unified storage API that works with both localStorage and cookies.
|
|
168
|
-
*
|
|
169
|
-
* @param key - Storage key
|
|
170
|
-
* @param value - Value to store
|
|
171
|
-
* @param options - Storage options
|
|
172
|
-
* @returns Storage result
|
|
173
|
-
*/
|
|
174
|
-
export function setStorage(key, value, options = {}) {
|
|
175
|
-
const { type = "localStorage" } = options;
|
|
176
|
-
if (type === "cookie") {
|
|
177
|
-
const success = setCookie(key, value, options);
|
|
178
|
-
return {
|
|
179
|
-
error: success ? undefined : "Cookie write failed",
|
|
180
|
-
success,
|
|
181
|
-
value: undefined,
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
return setInStorage(key, value);
|
|
185
|
-
}
|
|
186
|
-
/**
|
|
187
|
-
* Unified storage API that works with both localStorage and cookies.
|
|
188
|
-
*
|
|
189
|
-
* @param key - Storage key
|
|
190
|
-
* @param options - Storage options
|
|
191
|
-
* @returns Storage result
|
|
192
|
-
*/
|
|
193
|
-
export function removeStorage(key, options = {}) {
|
|
194
|
-
const { type = "localStorage" } = options;
|
|
195
|
-
if (type === "cookie") {
|
|
196
|
-
const success = removeCookie(key, options.path);
|
|
197
|
-
return {
|
|
198
|
-
error: success ? undefined : "Cookie remove failed",
|
|
199
|
-
success,
|
|
200
|
-
value: undefined,
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
|
-
return removeFromStorage(key);
|
|
204
|
-
}
|
|
205
|
-
/**
|
|
206
|
-
* Gets a JSON value from storage with automatic parsing.
|
|
207
|
-
*
|
|
208
|
-
* @param key - Storage key
|
|
209
|
-
* @param fallback - Fallback value if parsing fails or key not found
|
|
210
|
-
* @param options - Storage options
|
|
211
|
-
* @returns Parsed JSON value or fallback
|
|
212
|
-
*/
|
|
213
|
-
export function getJsonFromStorage(key, fallback, options = {}) {
|
|
214
|
-
const result = getStorage(key, options);
|
|
215
|
-
if (!result.success || result.value === null) {
|
|
216
|
-
return { ...result, value: fallback };
|
|
217
|
-
}
|
|
218
|
-
const parsed = safeJsonParse(result.value, fallback);
|
|
219
|
-
return { ...result, value: parsed };
|
|
220
|
-
}
|
|
221
|
-
/**
|
|
222
|
-
* Sets a JSON value in storage with automatic serialization.
|
|
223
|
-
*
|
|
224
|
-
* @param key - Storage key
|
|
225
|
-
* @param value - Value to serialize and store
|
|
226
|
-
* @param options - Storage options
|
|
227
|
-
* @returns Storage result
|
|
228
|
-
*/
|
|
229
|
-
export function setJsonInStorage(key, value, options = {}) {
|
|
230
|
-
try {
|
|
231
|
-
const serialized = JSON.stringify(value);
|
|
232
|
-
return setStorage(key, serialized, options);
|
|
233
|
-
}
|
|
234
|
-
catch (error) {
|
|
235
|
-
return {
|
|
236
|
-
error: error instanceof Error ? error.message : "JSON serialization failed",
|
|
237
|
-
success: false,
|
|
238
|
-
value: undefined,
|
|
239
|
-
};
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
/**
|
|
243
|
-
* Creates a typed storage interface for a specific key.
|
|
244
|
-
*
|
|
245
|
-
* @param key - Storage key
|
|
246
|
-
* @param options - Storage options
|
|
247
|
-
* @returns Typed storage interface
|
|
248
|
-
*/
|
|
249
|
-
export function createStorage(key, options = {}) {
|
|
250
|
-
return {
|
|
251
|
-
get: () => getStorage(key, options),
|
|
252
|
-
getJson: (fallback) => getJsonFromStorage(key, fallback, options),
|
|
253
|
-
remove: () => removeStorage(key, options),
|
|
254
|
-
set: (value) => setStorage(key, value, options),
|
|
255
|
-
setJson: (value) => setJsonInStorage(key, value, options),
|
|
256
|
-
};
|
|
257
|
-
}
|
|
258
|
-
// ============================================================================
|
|
259
|
-
// Theme Detection Utilities
|
|
260
|
-
// ============================================================================
|
|
261
|
-
/**
|
|
262
|
-
* Gets localStorage value safely (internal helper for theme detection).
|
|
263
|
-
* Uses getFromStorage for consistency.
|
|
264
|
-
*
|
|
265
|
-
* @param key - Storage key
|
|
266
|
-
* @returns Stored value or null if not found or access failed
|
|
267
|
-
*/
|
|
268
|
-
function getLocalStorage(key) {
|
|
269
|
-
const result = getFromStorage(key);
|
|
270
|
-
return result.success ? result.value : null;
|
|
271
|
-
}
|
|
272
|
-
/**
|
|
273
|
-
* Detects system color scheme preference.
|
|
274
|
-
*
|
|
275
|
-
* @returns 'dark' or 'light' based on system preference
|
|
276
|
-
*/
|
|
277
|
-
function getSystemPreference() {
|
|
278
|
-
if (!isBrowser())
|
|
279
|
-
return null;
|
|
280
|
-
try {
|
|
281
|
-
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
282
|
-
}
|
|
283
|
-
catch {
|
|
284
|
-
return null;
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
/**
|
|
288
|
-
* Validates if a theme name exists in available themes.
|
|
289
|
-
*
|
|
290
|
-
* @param themeName - Theme name to validate
|
|
291
|
-
* @param themes - Available themes map
|
|
292
|
-
* @returns True if theme is valid
|
|
293
|
-
*/
|
|
294
|
-
function isValidTheme(themeName, themes) {
|
|
295
|
-
return !themes || !!themes[themeName];
|
|
296
|
-
}
|
|
297
|
-
/**
|
|
298
|
-
* Detects the best theme to use based on multiple sources with priority.
|
|
299
|
-
*
|
|
300
|
-
* Priority order (highest to lowest):
|
|
301
|
-
* 1. Explicit localStorage preference
|
|
302
|
-
* 2. Cookie preference (for SSR compatibility)
|
|
303
|
-
* 3. System color scheme preference
|
|
304
|
-
* 4. Default theme
|
|
305
|
-
*
|
|
306
|
-
* @param options - Theme detection options
|
|
307
|
-
* @returns Theme detection result
|
|
308
|
-
*/
|
|
309
|
-
export function detectTheme(options = {}) {
|
|
310
|
-
const { cookie: cookieKey, default: defaultTheme = "light", localStorage: storageKey, systemPreference = true, themes, } = options;
|
|
311
|
-
// 1. Check localStorage (highest priority - explicit user choice)
|
|
312
|
-
if (storageKey) {
|
|
313
|
-
const stored = getLocalStorage(storageKey);
|
|
314
|
-
if (stored && isValidTheme(stored, themes)) {
|
|
315
|
-
return {
|
|
316
|
-
confidence: 0.9, // High confidence - explicit user choice
|
|
317
|
-
source: "localStorage",
|
|
318
|
-
theme: stored,
|
|
319
|
-
};
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
// 2. Check cookie (for SSR compatibility)
|
|
323
|
-
if (cookieKey) {
|
|
324
|
-
const cookieValue = getCookie(cookieKey);
|
|
325
|
-
if (cookieValue && isValidTheme(cookieValue, themes)) {
|
|
326
|
-
return {
|
|
327
|
-
confidence: 0.8, // High confidence - persisted preference
|
|
328
|
-
source: "cookie",
|
|
329
|
-
theme: cookieValue,
|
|
330
|
-
};
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
// 3. Check system preference
|
|
334
|
-
if (systemPreference) {
|
|
335
|
-
const system = getSystemPreference();
|
|
336
|
-
if (system && isValidTheme(system, themes)) {
|
|
337
|
-
return {
|
|
338
|
-
confidence: 0.6, // Medium confidence - system default
|
|
339
|
-
source: "system",
|
|
340
|
-
theme: system,
|
|
341
|
-
};
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
// 4. Fall back to default
|
|
345
|
-
return {
|
|
346
|
-
confidence: 0.3, // Low confidence - fallback only
|
|
347
|
-
source: "default",
|
|
348
|
-
theme: defaultTheme,
|
|
349
|
-
};
|
|
350
|
-
}
|
|
351
|
-
/**
|
|
352
|
-
* Creates a theme detector function with pre-configured options.
|
|
353
|
-
*
|
|
354
|
-
* @param options - Theme detection options
|
|
355
|
-
* @returns Theme detection function
|
|
356
|
-
*/
|
|
357
|
-
export function createThemeDetector(options) {
|
|
358
|
-
return () => detectTheme(options);
|
|
359
|
-
}
|
|
360
|
-
/**
|
|
361
|
-
* Auto-detects theme for SSR contexts (server-side or during hydration).
|
|
362
|
-
* Uses only cookie and default sources since localStorage isn't available.
|
|
363
|
-
*
|
|
364
|
-
* @param options - Theme detection options
|
|
365
|
-
* @returns Theme name
|
|
366
|
-
*/
|
|
367
|
-
export function detectThemeForSSR(options = {}) {
|
|
368
|
-
const { cookie: cookieKey, default: defaultTheme = "light", themes } = options;
|
|
369
|
-
// Only check cookie in SSR context
|
|
370
|
-
if (cookieKey) {
|
|
371
|
-
const cookieValue = getCookie(cookieKey);
|
|
372
|
-
if (cookieValue && isValidTheme(cookieValue, themes)) {
|
|
373
|
-
return cookieValue;
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
return defaultTheme;
|
|
377
|
-
}
|
|
378
|
-
/**
|
|
379
|
-
* Listens for system theme changes and calls callback when changed.
|
|
380
|
-
*
|
|
381
|
-
* @param callback - Function to call when system theme changes
|
|
382
|
-
* @returns Cleanup function
|
|
383
|
-
*/
|
|
384
|
-
export function onSystemThemeChange(callback) {
|
|
385
|
-
if (!isBrowser()) {
|
|
386
|
-
return () => { }; // No-op in SSR
|
|
387
|
-
}
|
|
388
|
-
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
389
|
-
const handleChange = (e) => {
|
|
390
|
-
callback(e.matches ? "dark" : "light");
|
|
391
|
-
};
|
|
392
|
-
mediaQuery.addEventListener("change", handleChange);
|
|
393
|
-
return () => {
|
|
394
|
-
mediaQuery.removeEventListener("change", handleChange);
|
|
395
|
-
};
|
|
396
|
-
}
|