Shinhati2023 commited on
Commit
b5d6105
·
verified ·
1 Parent(s): 138e8b6

Create lib/store.js

Browse files
Files changed (1) hide show
  1. lib/store.js +158 -0
lib/store.js ADDED
@@ -0,0 +1,158 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* ============================================================
2
+ GLASSGRID — THEME ENGINE
3
+
4
+ Handles theme loading, switching, token overrides,
5
+ and version rollback. Zero UI modification.
6
+ ============================================================ */
7
+
8
+ 'use strict';
9
+
10
+ const STORAGE_KEY_THEME = 'gg_active_theme';
11
+ const STORAGE_KEY_HISTORY = 'gg_theme_history';
12
+ const STORAGE_KEY_OVERRIDES = 'gg_token_overrides';
13
+
14
+ class ThemeEngine {
15
+
16
+ constructor() {
17
+ this.activeTheme = 'midnight';
18
+ this.themeHistory = [];
19
+ this.overrides = {};
20
+ this._styleEl = null;
21
+ this._overrideEl = null;
22
+ }
23
+
24
+ /** Initialize theme engine — call on app start */
25
+ async init() {
26
+ this._injectOverrideStyleEl();
27
+ this.themeHistory = this._loadHistory();
28
+ this.overrides = this._loadOverrides();
29
+
30
+ const savedTheme = localStorage.getItem(STORAGE_KEY_THEME) || 'midnight';
31
+ await this.applyTheme(savedTheme, false);
32
+ this._applyTokenOverrides(this.overrides);
33
+ }
34
+
35
+ /** Switch to a named theme */
36
+ async applyTheme(themeId, pushHistory = true) {
37
+ const themes = await this._loadThemeRegistry();
38
+ const theme = themes.find(t => t.id === themeId);
39
+ if (!theme) { console.warn(`[ThemeEngine] Unknown theme: ${themeId}`); return; }
40
+
41
+ document.documentElement.dataset.theme = themeId;
42
+ this.activeTheme = themeId;
43
+ localStorage.setItem(STORAGE_KEY_THEME, themeId);
44
+
45
+ if (pushHistory) {
46
+ this.themeHistory = [themeId, ...this.themeHistory.filter(t => t !== themeId)].slice(0, 10);
47
+ this._saveHistory(this.themeHistory);
48
+ }
49
+
50
+ document.dispatchEvent(new CustomEvent('gg:theme:changed', { detail: { themeId } }));
51
+ }
52
+
53
+ /** Roll back to previous theme */
54
+ rollback() {
55
+ const [_current, previous, ...rest] = this.themeHistory;
56
+ if (!previous) { console.warn('[ThemeEngine] No previous theme to roll back to.'); return; }
57
+ this.themeHistory = [previous, _current, ...rest];
58
+ this._saveHistory(this.themeHistory);
59
+ this.applyTheme(previous, false);
60
+ document.dispatchEvent(new CustomEvent('gg:theme:rollback', { detail: { themeId: previous } }));
61
+ }
62
+
63
+ /** Override a single CSS token */
64
+ setToken(tokenName, value) {
65
+ if (!tokenName.startsWith('--')) tokenName = `--${tokenName}`;
66
+ this.overrides[tokenName] = value;
67
+ this._applyTokenOverrides(this.overrides);
68
+ this._saveOverrides(this.overrides);
69
+ }
70
+
71
+ /** Override multiple tokens at once */
72
+ setTokens(tokenMap) {
73
+ for (const [key, value] of Object.entries(tokenMap)) {
74
+ const name = key.startsWith('--') ? key : `--${key}`;
75
+ this.overrides[name] = value;
76
+ }
77
+ this._applyTokenOverrides(this.overrides);
78
+ this._saveOverrides(this.overrides);
79
+ }
80
+
81
+ /** Remove a single token override (reverts to theme default) */
82
+ removeToken(tokenName) {
83
+ if (!tokenName.startsWith('--')) tokenName = `--${tokenName}`;
84
+ delete this.overrides[tokenName];
85
+ this._applyTokenOverrides(this.overrides);
86
+ this._saveOverrides(this.overrides);
87
+ }
88
+
89
+ /** Reset all token overrides */
90
+ resetTokens() {
91
+ this.overrides = {};
92
+ this._overrideEl.textContent = '';
93
+ this._saveOverrides({});
94
+ }
95
+
96
+ /** Get all token overrides */
97
+ getOverrides() {
98
+ return { ...this.overrides };
99
+ }
100
+
101
+ /** Get theme history */
102
+ getHistory() {
103
+ return [...this.themeHistory];
104
+ }
105
+
106
+ // ── Private ──────────────────────────
107
+
108
+ _injectOverrideStyleEl() {
109
+ this._overrideEl = document.createElement('style');
110
+ this._overrideEl.id = 'gg-token-overrides';
111
+ document.head.appendChild(this._overrideEl);
112
+ }
113
+
114
+ _applyTokenOverrides(overrides) {
115
+ if (!this._overrideEl) return;
116
+ const rules = Object.entries(overrides)
117
+ .map(([k, v]) => ` ${k}: ${v};`)
118
+ .join('\n');
119
+ this._overrideEl.textContent = rules.length ? `:root {\n${rules}\n}` : '';
120
+ }
121
+
122
+ async _loadThemeRegistry() {
123
+ try {
124
+ const res = await fetch('./data/themes.json');
125
+ const data = await res.json();
126
+ return data.themes || [];
127
+ } catch {
128
+ return [
129
+ { id: 'midnight', name: 'Midnight' },
130
+ { id: 'aurora', name: 'Aurora' },
131
+ { id: 'ember', name: 'Ember' },
132
+ { id: 'ocean', name: 'Ocean' },
133
+ ];
134
+ }
135
+ }
136
+
137
+ _loadHistory() {
138
+ try {
139
+ return JSON.parse(localStorage.getItem(STORAGE_KEY_HISTORY) || '["midnight"]');
140
+ } catch { return ['midnight']; }
141
+ }
142
+
143
+ _saveHistory(history) {
144
+ localStorage.setItem(STORAGE_KEY_HISTORY, JSON.stringify(history));
145
+ }
146
+
147
+ _loadOverrides() {
148
+ try {
149
+ return JSON.parse(localStorage.getItem(STORAGE_KEY_OVERRIDES) || '{}');
150
+ } catch { return {}; }
151
+ }
152
+
153
+ _saveOverrides(overrides) {
154
+ localStorage.setItem(STORAGE_KEY_OVERRIDES, JSON.stringify(overrides));
155
+ }
156
+ }
157
+
158
+ export const themeEngine = new ThemeEngine();