@magicvr/schema-ui-theme 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +71 -0
- package/package.json +30 -0
- package/theme/index.d.ts +4 -0
- package/theme/theme.d.ts +64 -0
package/index.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
function resolveTheme(input) {
|
|
2
|
+
if (input.stored === "dark" || input.stored === "light") {
|
|
3
|
+
return { theme: input.stored, colorScheme: input.stored };
|
|
4
|
+
}
|
|
5
|
+
if (input.systemDefault === "dark" || input.systemDefault === "light") {
|
|
6
|
+
return { theme: input.systemDefault, colorScheme: input.systemDefault };
|
|
7
|
+
}
|
|
8
|
+
const isDark = !input.stored && input.prefersDark;
|
|
9
|
+
const theme = isDark ? "dark" : "light";
|
|
10
|
+
return { theme, colorScheme: theme };
|
|
11
|
+
}
|
|
12
|
+
function applyThemeToElement(el, output) {
|
|
13
|
+
if (output.theme === "dark") {
|
|
14
|
+
el.classList.add("dark");
|
|
15
|
+
} else {
|
|
16
|
+
el.classList.remove("dark");
|
|
17
|
+
}
|
|
18
|
+
el.style.colorScheme = output.colorScheme;
|
|
19
|
+
}
|
|
20
|
+
function initTheme() {
|
|
21
|
+
let stored = null;
|
|
22
|
+
try {
|
|
23
|
+
stored = localStorage.getItem("theme");
|
|
24
|
+
} catch {
|
|
25
|
+
}
|
|
26
|
+
let prefersDark = false;
|
|
27
|
+
try {
|
|
28
|
+
prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
29
|
+
} catch {
|
|
30
|
+
}
|
|
31
|
+
const output = resolveTheme({ stored, prefersDark });
|
|
32
|
+
applyThemeToElement(document.documentElement, output);
|
|
33
|
+
}
|
|
34
|
+
function applySystemDefaultTheme(systemDefault) {
|
|
35
|
+
let stored = null;
|
|
36
|
+
try {
|
|
37
|
+
stored = localStorage.getItem("theme");
|
|
38
|
+
} catch {
|
|
39
|
+
}
|
|
40
|
+
if (stored === "dark" || stored === "light") {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (systemDefault !== "light" && systemDefault !== "dark") {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
let prefersDark = false;
|
|
47
|
+
try {
|
|
48
|
+
prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
49
|
+
} catch {
|
|
50
|
+
}
|
|
51
|
+
const output = resolveTheme({ stored, prefersDark, systemDefault });
|
|
52
|
+
applyThemeToElement(document.documentElement, output);
|
|
53
|
+
}
|
|
54
|
+
function setTheme(theme) {
|
|
55
|
+
try {
|
|
56
|
+
if (theme === null) {
|
|
57
|
+
localStorage.removeItem("theme");
|
|
58
|
+
} else {
|
|
59
|
+
localStorage.setItem("theme", theme);
|
|
60
|
+
}
|
|
61
|
+
} catch {
|
|
62
|
+
}
|
|
63
|
+
initTheme();
|
|
64
|
+
}
|
|
65
|
+
export {
|
|
66
|
+
applySystemDefaultTheme,
|
|
67
|
+
applyThemeToElement,
|
|
68
|
+
initTheme,
|
|
69
|
+
resolveTheme,
|
|
70
|
+
setTheme
|
|
71
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@magicvr/schema-ui-theme",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "schema-ui-core 包面(VP-023 R3 六包化)",
|
|
6
|
+
"main": "index.js",
|
|
7
|
+
"types": "./theme/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./theme/index.d.ts",
|
|
11
|
+
"import": "./index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"index.js",
|
|
16
|
+
"lib",
|
|
17
|
+
"theme",
|
|
18
|
+
"components",
|
|
19
|
+
"app",
|
|
20
|
+
"host",
|
|
21
|
+
"i18n",
|
|
22
|
+
"ui",
|
|
23
|
+
"renderer",
|
|
24
|
+
"render"
|
|
25
|
+
],
|
|
26
|
+
"license": "UNLICENSED",
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public"
|
|
29
|
+
}
|
|
30
|
+
}
|
package/theme/index.d.ts
ADDED
package/theme/theme.d.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Theme pure-logic unit (S1 · C3 + VP-007 S3 system default).
|
|
3
|
+
*
|
|
4
|
+
* `applyTheme` is a side-effect-free function that computes the desired theme
|
|
5
|
+
* and then applies it to the provided document root. Keeping the decision
|
|
6
|
+
* logic in a plain function lets vitest exercise it without a browser.
|
|
7
|
+
*
|
|
8
|
+
* `initTheme` is the top-level boot call used by the synchronous external
|
|
9
|
+
* `/theme-init.js` script in index.html and by main.tsx (the module call is
|
|
10
|
+
* kept as a safety net for test/storybook entry points).
|
|
11
|
+
*
|
|
12
|
+
* VP-007 S3 priority (D-002, user-confirmed): user explicit choice →
|
|
13
|
+
* system default theme (from the public startup configuration, non-auto) →
|
|
14
|
+
* OS preference. The user's explicit choice always wins.
|
|
15
|
+
*/
|
|
16
|
+
export type Theme = "light" | "dark";
|
|
17
|
+
export interface ThemeInput {
|
|
18
|
+
/** Value of `localStorage.getItem("theme")` — null when absent. */
|
|
19
|
+
stored: string | null;
|
|
20
|
+
/** `window.matchMedia("(prefers-color-scheme: dark)").matches` */
|
|
21
|
+
prefersDark: boolean;
|
|
22
|
+
/** System default theme from /api/branding (auto/light/dark); null = auto. */
|
|
23
|
+
systemDefault?: string | null;
|
|
24
|
+
}
|
|
25
|
+
export interface ThemeOutput {
|
|
26
|
+
theme: Theme;
|
|
27
|
+
/** CSS `color-scheme` value for the root element. */
|
|
28
|
+
colorScheme: "light" | "dark";
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Pure function: decides the effective theme from storage + system default +
|
|
32
|
+
* OS preference. No DOM side-effects — safe to call in vitest.
|
|
33
|
+
*/
|
|
34
|
+
export declare function resolveTheme(input: ThemeInput): ThemeOutput;
|
|
35
|
+
/**
|
|
36
|
+
* Applies the resolved theme to a DOM element (typically
|
|
37
|
+
* `document.documentElement`). Adds or removes the `dark` class and sets
|
|
38
|
+
* the `color-scheme` style so native browser controls match.
|
|
39
|
+
*/
|
|
40
|
+
export declare function applyThemeToElement(el: HTMLElement, output: ThemeOutput): void;
|
|
41
|
+
/**
|
|
42
|
+
* End-to-end theme boot: reads real browser APIs and mutates the DOM.
|
|
43
|
+
* Called from the synchronous external /theme-init.js script in index.html and from main.tsx.
|
|
44
|
+
*
|
|
45
|
+
* W4 P2-1: localStorage may throw when site storage is disabled (privacy
|
|
46
|
+
* mode). main.tsx calls initTheme at module top level — an uncaught throw
|
|
47
|
+
* would abort module evaluation and the app would never mount (white screen,
|
|
48
|
+
* not even the failure surface). Guard reads/writes exactly like tokens.ts.
|
|
49
|
+
*/
|
|
50
|
+
export declare function initTheme(): void;
|
|
51
|
+
/**
|
|
52
|
+
* Applies the system default theme (from the public startup configuration)
|
|
53
|
+
* when the user has no explicit choice. VP-007 S3: the explicit user theme
|
|
54
|
+
* always wins; otherwise the system default (non-auto) beats the OS
|
|
55
|
+
* preference. Called after branding loads on the login page and the shell.
|
|
56
|
+
*/
|
|
57
|
+
export declare function applySystemDefaultTheme(systemDefault: string | null | undefined): void;
|
|
58
|
+
/**
|
|
59
|
+
* Sets the user's preferred theme, persists it to localStorage, and applies
|
|
60
|
+
* it immediately. Pass `null` to revert to OS preference. W4 P2-1: storage
|
|
61
|
+
* writes are best-effort — a disabled-storage environment still applies the
|
|
62
|
+
* theme for the current page.
|
|
63
|
+
*/
|
|
64
|
+
export declare function setTheme(theme: Theme | null): void;
|