agentgui 1.0.815 → 1.0.816
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/CHANGELOG.md +4 -0
- package/package.json +1 -1
- package/static/js/client.js +0 -272
- package/static/js/conv-machine.js +0 -3
- package/static/js/conversations.js +0 -5
- package/static/js/event-filter.js +0 -59
- package/static/js/image-loader.js +0 -35
- package/static/js/script-runner.js +0 -3
- package/static/js/state-barrier.js +0 -4
- package/static/js/streaming-renderer.js +0 -273
- package/static/js/syntax-highlighter.js +0 -55
- package/static/js/ui-components.js +0 -51
- package/static/js/websocket-manager.js +0 -7
- package/static/js/ws-client.js +0 -4
- package/static/js/ws-machine.js +0 -2
- package/static/theme.js +0 -8
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Syntax Highlighter Integration
|
|
3
|
-
* Handles lazy-loading and caching of Prism.js for code highlighting
|
|
4
|
-
*/
|
|
5
1
|
|
|
6
2
|
class SyntaxHighlighter {
|
|
7
3
|
constructor(config = {}) {
|
|
@@ -24,21 +20,15 @@ class SyntaxHighlighter {
|
|
|
24
20
|
this.loadPromise = null;
|
|
25
21
|
}
|
|
26
22
|
|
|
27
|
-
/**
|
|
28
|
-
* Ensure Prism is loaded
|
|
29
|
-
*/
|
|
30
23
|
async ensureLoaded() {
|
|
31
|
-
// Already loaded
|
|
32
24
|
if (typeof Prism !== 'undefined' && this.isLoaded) {
|
|
33
25
|
return true;
|
|
34
26
|
}
|
|
35
27
|
|
|
36
|
-
// Currently loading
|
|
37
28
|
if (this.isLoading && this.loadPromise) {
|
|
38
29
|
return this.loadPromise;
|
|
39
30
|
}
|
|
40
31
|
|
|
41
|
-
// Start loading
|
|
42
32
|
this.isLoading = true;
|
|
43
33
|
this.loadPromise = this.loadPrism();
|
|
44
34
|
|
|
@@ -54,19 +44,14 @@ class SyntaxHighlighter {
|
|
|
54
44
|
}
|
|
55
45
|
}
|
|
56
46
|
|
|
57
|
-
/**
|
|
58
|
-
* Load Prism library
|
|
59
|
-
*/
|
|
60
47
|
async loadPrism() {
|
|
61
48
|
return new Promise((resolve, reject) => {
|
|
62
49
|
try {
|
|
63
|
-
// Load main Prism JS
|
|
64
50
|
const script = document.createElement('script');
|
|
65
51
|
script.src = `${this.config.cdnUrl}/prism.js`;
|
|
66
52
|
script.async = true;
|
|
67
53
|
|
|
68
54
|
script.onload = () => {
|
|
69
|
-
// Load common language files
|
|
70
55
|
this.loadLanguages();
|
|
71
56
|
this.isLoading = false;
|
|
72
57
|
resolve(true);
|
|
@@ -85,9 +70,6 @@ class SyntaxHighlighter {
|
|
|
85
70
|
});
|
|
86
71
|
}
|
|
87
72
|
|
|
88
|
-
/**
|
|
89
|
-
* Load language files
|
|
90
|
-
*/
|
|
91
73
|
loadLanguages() {
|
|
92
74
|
const languages = ['javascript', 'python', 'sql', 'bash', 'json'];
|
|
93
75
|
|
|
@@ -99,13 +81,9 @@ class SyntaxHighlighter {
|
|
|
99
81
|
}
|
|
100
82
|
}
|
|
101
83
|
|
|
102
|
-
/**
|
|
103
|
-
* Highlight code
|
|
104
|
-
*/
|
|
105
84
|
async highlight(code, language = 'plaintext') {
|
|
106
85
|
if (!code) return '';
|
|
107
86
|
|
|
108
|
-
// Ensure Prism is loaded
|
|
109
87
|
if (this.config.lazyLoad) {
|
|
110
88
|
try {
|
|
111
89
|
await this.ensureLoaded();
|
|
@@ -115,19 +93,16 @@ class SyntaxHighlighter {
|
|
|
115
93
|
}
|
|
116
94
|
}
|
|
117
95
|
|
|
118
|
-
// Check cache
|
|
119
96
|
const cacheKey = `${language}:${code}`;
|
|
120
97
|
if (this.config.enableCache && this.highlightCache.has(cacheKey)) {
|
|
121
98
|
return this.highlightCache.get(cacheKey);
|
|
122
99
|
}
|
|
123
100
|
|
|
124
|
-
// Highlight code
|
|
125
101
|
let highlighted;
|
|
126
102
|
try {
|
|
127
103
|
if (typeof Prism !== 'undefined' && Prism.languages[language]) {
|
|
128
104
|
highlighted = Prism.highlight(code, Prism.languages[language], language);
|
|
129
105
|
} else {
|
|
130
|
-
// Fallback to escaped HTML if language not supported
|
|
131
106
|
highlighted = this.escapeHtml(code);
|
|
132
107
|
}
|
|
133
108
|
} catch (error) {
|
|
@@ -135,11 +110,9 @@ class SyntaxHighlighter {
|
|
|
135
110
|
highlighted = this.escapeHtml(code);
|
|
136
111
|
}
|
|
137
112
|
|
|
138
|
-
// Cache result
|
|
139
113
|
if (this.config.enableCache) {
|
|
140
114
|
this.highlightCache.set(cacheKey, highlighted);
|
|
141
115
|
|
|
142
|
-
// Trim cache if too large
|
|
143
116
|
if (this.highlightCache.size > this.config.maxCacheSize) {
|
|
144
117
|
const firstKey = this.highlightCache.keys().next().value;
|
|
145
118
|
this.highlightCache.delete(firstKey);
|
|
@@ -149,14 +122,10 @@ class SyntaxHighlighter {
|
|
|
149
122
|
return highlighted;
|
|
150
123
|
}
|
|
151
124
|
|
|
152
|
-
/**
|
|
153
|
-
* Create highlighted code element
|
|
154
|
-
*/
|
|
155
125
|
async createHighlightedElement(code, language = 'plaintext') {
|
|
156
126
|
const pre = document.createElement('pre');
|
|
157
127
|
const code_el = document.createElement('code');
|
|
158
128
|
|
|
159
|
-
// Set language class
|
|
160
129
|
code_el.className = `language-${language}`;
|
|
161
130
|
|
|
162
131
|
if (this.config.lazyLoad) {
|
|
@@ -175,9 +144,6 @@ class SyntaxHighlighter {
|
|
|
175
144
|
return pre;
|
|
176
145
|
}
|
|
177
146
|
|
|
178
|
-
/**
|
|
179
|
-
* Highlight DOM element
|
|
180
|
-
*/
|
|
181
147
|
async highlightElement(element) {
|
|
182
148
|
if (!element || !element.querySelector('code')) return;
|
|
183
149
|
|
|
@@ -199,13 +165,9 @@ class SyntaxHighlighter {
|
|
|
199
165
|
}
|
|
200
166
|
}
|
|
201
167
|
|
|
202
|
-
/**
|
|
203
|
-
* Detect language from code content
|
|
204
|
-
*/
|
|
205
168
|
detectLanguage(code) {
|
|
206
169
|
if (!code) return 'plaintext';
|
|
207
170
|
|
|
208
|
-
// Shebang detection
|
|
209
171
|
if (code.startsWith('#!/')) {
|
|
210
172
|
if (code.includes('python')) return 'python';
|
|
211
173
|
if (code.includes('node') || code.includes('node.js')) return 'javascript';
|
|
@@ -213,7 +175,6 @@ class SyntaxHighlighter {
|
|
|
213
175
|
if (code.includes('ruby')) return 'ruby';
|
|
214
176
|
}
|
|
215
177
|
|
|
216
|
-
// Pattern detection
|
|
217
178
|
if (code.includes('def ') && code.includes(':')) return 'python';
|
|
218
179
|
if (code.includes('function') || code.includes('=>')) return 'javascript';
|
|
219
180
|
if (code.includes('fn ') && code.includes('->')) return 'rust';
|
|
@@ -225,30 +186,18 @@ class SyntaxHighlighter {
|
|
|
225
186
|
return 'plaintext';
|
|
226
187
|
}
|
|
227
188
|
|
|
228
|
-
/**
|
|
229
|
-
* Get supported languages
|
|
230
|
-
*/
|
|
231
189
|
getSupportedLanguages() {
|
|
232
190
|
return [...this.config.supportedLanguages];
|
|
233
191
|
}
|
|
234
192
|
|
|
235
|
-
/**
|
|
236
|
-
* Check if language is supported
|
|
237
|
-
*/
|
|
238
193
|
isSupportedLanguage(language) {
|
|
239
194
|
return this.config.supportedLanguages.includes(language);
|
|
240
195
|
}
|
|
241
196
|
|
|
242
|
-
/**
|
|
243
|
-
* Clear cache
|
|
244
|
-
*/
|
|
245
197
|
clearCache() {
|
|
246
198
|
this.highlightCache.clear();
|
|
247
199
|
}
|
|
248
200
|
|
|
249
|
-
/**
|
|
250
|
-
* Get cache stats
|
|
251
|
-
*/
|
|
252
201
|
getCacheStats() {
|
|
253
202
|
return {
|
|
254
203
|
size: this.highlightCache.size,
|
|
@@ -256,15 +205,11 @@ class SyntaxHighlighter {
|
|
|
256
205
|
};
|
|
257
206
|
}
|
|
258
207
|
|
|
259
|
-
/**
|
|
260
|
-
* HTML escape utility
|
|
261
|
-
*/
|
|
262
208
|
escapeHtml(text) {
|
|
263
209
|
return window._escHtml(text);
|
|
264
210
|
}
|
|
265
211
|
}
|
|
266
212
|
|
|
267
|
-
// Export for use in browser
|
|
268
213
|
if (typeof module !== 'undefined' && module.exports) {
|
|
269
214
|
module.exports = SyntaxHighlighter;
|
|
270
215
|
}
|
|
@@ -1,12 +1,5 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* UI Components
|
|
3
|
-
* Reusable UI building blocks for modals, tabs, buttons, and more
|
|
4
|
-
*/
|
|
5
1
|
|
|
6
2
|
class UIComponents {
|
|
7
|
-
/**
|
|
8
|
-
* Create a modal dialog
|
|
9
|
-
*/
|
|
10
3
|
static createModal(config = {}) {
|
|
11
4
|
const {
|
|
12
5
|
title = 'Dialog',
|
|
@@ -45,14 +38,12 @@ class UIComponents {
|
|
|
45
38
|
</div>
|
|
46
39
|
`;
|
|
47
40
|
|
|
48
|
-
// Add close handler
|
|
49
41
|
const closeBtn = modal.querySelector('.modal-close');
|
|
50
42
|
closeBtn.addEventListener('click', () => {
|
|
51
43
|
modal.remove();
|
|
52
44
|
if (onClose) onClose();
|
|
53
45
|
});
|
|
54
46
|
|
|
55
|
-
// Add button handlers
|
|
56
47
|
modal.querySelectorAll('[data-action]').forEach(btn => {
|
|
57
48
|
btn.addEventListener('click', (e) => {
|
|
58
49
|
const action = e.target.dataset.action;
|
|
@@ -63,7 +54,6 @@ class UIComponents {
|
|
|
63
54
|
});
|
|
64
55
|
});
|
|
65
56
|
|
|
66
|
-
// Close on background click
|
|
67
57
|
modal.addEventListener('click', (e) => {
|
|
68
58
|
if (e.target === modal) {
|
|
69
59
|
modal.remove();
|
|
@@ -74,9 +64,6 @@ class UIComponents {
|
|
|
74
64
|
return modal;
|
|
75
65
|
}
|
|
76
66
|
|
|
77
|
-
/**
|
|
78
|
-
* Create a tabbed interface
|
|
79
|
-
*/
|
|
80
67
|
static createTabs(config = {}) {
|
|
81
68
|
const {
|
|
82
69
|
tabs = [],
|
|
@@ -87,7 +74,6 @@ class UIComponents {
|
|
|
87
74
|
const container = document.createElement('div');
|
|
88
75
|
container.className = 'tabs';
|
|
89
76
|
|
|
90
|
-
// Tab buttons
|
|
91
77
|
const tabButtons = document.createElement('div');
|
|
92
78
|
tabButtons.className = 'tab-buttons flex border-b';
|
|
93
79
|
|
|
@@ -98,7 +84,6 @@ class UIComponents {
|
|
|
98
84
|
btn.dataset.tabIndex = index;
|
|
99
85
|
|
|
100
86
|
btn.addEventListener('click', () => {
|
|
101
|
-
// Update active button
|
|
102
87
|
tabButtons.querySelectorAll('.tab-button').forEach((b, i) => {
|
|
103
88
|
if (i === index) {
|
|
104
89
|
b.classList.add('tab-active');
|
|
@@ -107,7 +92,6 @@ class UIComponents {
|
|
|
107
92
|
}
|
|
108
93
|
});
|
|
109
94
|
|
|
110
|
-
// Update tab content
|
|
111
95
|
tabContent.querySelectorAll('.tab-pane').forEach((pane, i) => {
|
|
112
96
|
pane.style.display = i === index ? 'block' : 'none';
|
|
113
97
|
});
|
|
@@ -120,7 +104,6 @@ class UIComponents {
|
|
|
120
104
|
|
|
121
105
|
container.appendChild(tabButtons);
|
|
122
106
|
|
|
123
|
-
// Tab content
|
|
124
107
|
const tabContent = document.createElement('div');
|
|
125
108
|
tabContent.className = 'tab-content mt-4';
|
|
126
109
|
|
|
@@ -136,9 +119,6 @@ class UIComponents {
|
|
|
136
119
|
return container;
|
|
137
120
|
}
|
|
138
121
|
|
|
139
|
-
/**
|
|
140
|
-
* Create an alert/notification
|
|
141
|
-
*/
|
|
142
122
|
static createAlert(config = {}) {
|
|
143
123
|
const {
|
|
144
124
|
message = '',
|
|
@@ -175,9 +155,6 @@ class UIComponents {
|
|
|
175
155
|
return alert;
|
|
176
156
|
}
|
|
177
157
|
|
|
178
|
-
/**
|
|
179
|
-
* Create a loading spinner
|
|
180
|
-
*/
|
|
181
158
|
static createSpinner(config = {}) {
|
|
182
159
|
const {
|
|
183
160
|
size = 'medium', // small, medium, large
|
|
@@ -199,9 +176,6 @@ class UIComponents {
|
|
|
199
176
|
return container;
|
|
200
177
|
}
|
|
201
178
|
|
|
202
|
-
/**
|
|
203
|
-
* Create a progress bar
|
|
204
|
-
*/
|
|
205
179
|
static createProgressBar(config = {}) {
|
|
206
180
|
const {
|
|
207
181
|
percentage = 0,
|
|
@@ -225,9 +199,6 @@ class UIComponents {
|
|
|
225
199
|
return container;
|
|
226
200
|
}
|
|
227
201
|
|
|
228
|
-
/**
|
|
229
|
-
* Create a collapsible section
|
|
230
|
-
*/
|
|
231
202
|
static createCollapsible(config = {}) {
|
|
232
203
|
const {
|
|
233
204
|
title = 'Details',
|
|
@@ -252,9 +223,6 @@ class UIComponents {
|
|
|
252
223
|
return container;
|
|
253
224
|
}
|
|
254
225
|
|
|
255
|
-
/**
|
|
256
|
-
* Create a form input
|
|
257
|
-
*/
|
|
258
226
|
static createInput(config = {}) {
|
|
259
227
|
const {
|
|
260
228
|
type = 'text',
|
|
@@ -288,9 +256,6 @@ class UIComponents {
|
|
|
288
256
|
return container;
|
|
289
257
|
}
|
|
290
258
|
|
|
291
|
-
/**
|
|
292
|
-
* Create a select dropdown
|
|
293
|
-
*/
|
|
294
259
|
static createSelect(config = {}) {
|
|
295
260
|
const {
|
|
296
261
|
name = '',
|
|
@@ -326,9 +291,6 @@ class UIComponents {
|
|
|
326
291
|
return container;
|
|
327
292
|
}
|
|
328
293
|
|
|
329
|
-
/**
|
|
330
|
-
* Create a button group
|
|
331
|
-
*/
|
|
332
294
|
static createButtonGroup(config = {}) {
|
|
333
295
|
const {
|
|
334
296
|
buttons = [],
|
|
@@ -351,9 +313,6 @@ class UIComponents {
|
|
|
351
313
|
return container;
|
|
352
314
|
}
|
|
353
315
|
|
|
354
|
-
/**
|
|
355
|
-
* Create a badge/tag
|
|
356
|
-
*/
|
|
357
316
|
static createBadge(config = {}) {
|
|
358
317
|
const {
|
|
359
318
|
label = '',
|
|
@@ -381,16 +340,10 @@ class UIComponents {
|
|
|
381
340
|
return badge;
|
|
382
341
|
}
|
|
383
342
|
|
|
384
|
-
/**
|
|
385
|
-
* HTML escape utility
|
|
386
|
-
*/
|
|
387
343
|
static escapeHtml(text) {
|
|
388
344
|
return window._escHtml(text);
|
|
389
345
|
}
|
|
390
346
|
|
|
391
|
-
/**
|
|
392
|
-
* Copy text to clipboard
|
|
393
|
-
*/
|
|
394
347
|
static copyToClipboard(text) {
|
|
395
348
|
return navigator.clipboard.writeText(text).catch(err => {
|
|
396
349
|
console.error('Failed to copy:', err);
|
|
@@ -398,9 +351,6 @@ class UIComponents {
|
|
|
398
351
|
});
|
|
399
352
|
}
|
|
400
353
|
|
|
401
|
-
/**
|
|
402
|
-
* Download data as file
|
|
403
|
-
*/
|
|
404
354
|
static downloadFile(data, filename, mimeType = 'text/plain') {
|
|
405
355
|
const blob = new Blob([data], { type: mimeType });
|
|
406
356
|
const url = URL.createObjectURL(blob);
|
|
@@ -414,7 +364,6 @@ class UIComponents {
|
|
|
414
364
|
}
|
|
415
365
|
}
|
|
416
366
|
|
|
417
|
-
// Export for use in browser
|
|
418
367
|
if (typeof module !== 'undefined' && module.exports) {
|
|
419
368
|
module.exports = UIComponents;
|
|
420
369
|
}
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
// codec is loaded as ES module and exposed globally by ws-client.js
|
|
2
|
-
// or inline: import('./codec.js').then(m => window._codec = m)
|
|
3
1
|
|
|
4
2
|
class WebSocketManager {
|
|
5
3
|
constructor(config = {}) {
|
|
@@ -94,7 +92,6 @@ class WebSocketManager {
|
|
|
94
92
|
}
|
|
95
93
|
set connectionState(v) { this._connectionState = v; }
|
|
96
94
|
|
|
97
|
-
// Machine context is authoritative for these flags
|
|
98
95
|
get isManuallyDisconnected() {
|
|
99
96
|
if (this._wsActor) return !!this._wsActor.getSnapshot().context.manualDisconnect;
|
|
100
97
|
return this._isManuallyDisconnected;
|
|
@@ -201,7 +198,6 @@ class WebSocketManager {
|
|
|
201
198
|
);
|
|
202
199
|
}
|
|
203
200
|
|
|
204
|
-
// RPC reply envelopes — emit for WsClient to intercept, then skip broadcast
|
|
205
201
|
if (data.r !== undefined && !data.type) {
|
|
206
202
|
this.emit('message', data);
|
|
207
203
|
continue;
|
|
@@ -233,7 +229,6 @@ class WebSocketManager {
|
|
|
233
229
|
|
|
234
230
|
this.latency.current = rtt;
|
|
235
231
|
|
|
236
|
-
// EMA smoothing (α=0.2 → slow adaptation, less noise)
|
|
237
232
|
const alpha = 0.2;
|
|
238
233
|
this._latencyEma = this._latencyEma === null ? rtt : alpha * rtt + (1 - alpha) * this._latencyEma;
|
|
239
234
|
this.latency.avg = this._latencyEma;
|
|
@@ -535,8 +530,6 @@ class WebSocketManager {
|
|
|
535
530
|
this.stats.totalMessagesSent++;
|
|
536
531
|
} catch (_) {}
|
|
537
532
|
}
|
|
538
|
-
// Server automatically sends streaming_start{resumed:true} on subscribe
|
|
539
|
-
// when an active execution exists — no need to query conv.get here.
|
|
540
533
|
}
|
|
541
534
|
|
|
542
535
|
unsubscribeFromSession(sessionId) {
|
package/static/js/ws-client.js
CHANGED
|
@@ -10,7 +10,6 @@ class WsClient {
|
|
|
10
10
|
_install() {
|
|
11
11
|
if (this._installed) return;
|
|
12
12
|
this._installed = true;
|
|
13
|
-
// Listen on decoded message objects — websocket-manager emits 'message' with decoded obj
|
|
14
13
|
this._ws.on('message', (data) => {
|
|
15
14
|
if (data.r && this._pending.has(data.r)) {
|
|
16
15
|
const p = this._pending.get(data.r);
|
|
@@ -23,7 +22,6 @@ class WsClient {
|
|
|
23
22
|
}
|
|
24
23
|
return; // consumed — don't re-emit
|
|
25
24
|
}
|
|
26
|
-
// Non-RPC messages are already emitted by websocket-manager; nothing to do
|
|
27
25
|
});
|
|
28
26
|
this._ws.on('disconnected', () => this.cancelAll());
|
|
29
27
|
}
|
|
@@ -78,8 +76,6 @@ class WsClient {
|
|
|
78
76
|
|
|
79
77
|
window.WsClient = WsClient;
|
|
80
78
|
|
|
81
|
-
// Bootstrap: create wsManager and wsClient synchronously so other modules can use them immediately.
|
|
82
|
-
// Codec is loaded async and upgrades encoding once ready; websocket-manager falls back to msgpackr until then.
|
|
83
79
|
window.wsManager = new WebSocketManager();
|
|
84
80
|
window.wsClient = new WsClient(window.wsManager);
|
|
85
81
|
window.wsManager.connect().catch(function() {});
|
package/static/js/ws-machine.js
CHANGED
package/static/theme.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// Theme management for dark/light mode
|
|
2
1
|
class ThemeManager {
|
|
3
2
|
constructor() {
|
|
4
3
|
this.THEME_KEY = 'gmgui-theme';
|
|
@@ -7,27 +6,22 @@ class ThemeManager {
|
|
|
7
6
|
}
|
|
8
7
|
|
|
9
8
|
init() {
|
|
10
|
-
// Load saved theme or use system preference
|
|
11
9
|
const savedTheme = localStorage.getItem(this.THEME_KEY);
|
|
12
10
|
const prefersDark = this.SYSTEM_DARK_MODE.matches;
|
|
13
11
|
|
|
14
12
|
if (savedTheme) {
|
|
15
13
|
this.setTheme(savedTheme);
|
|
16
14
|
} else {
|
|
17
|
-
// Use system preference
|
|
18
15
|
this.setTheme(prefersDark ? 'dark' : 'light');
|
|
19
16
|
}
|
|
20
17
|
|
|
21
|
-
// Listen for system theme changes
|
|
22
18
|
this.SYSTEM_DARK_MODE.addEventListener('change', (e) => {
|
|
23
19
|
const savedTheme = localStorage.getItem(this.THEME_KEY);
|
|
24
|
-
// Only auto-switch if user hasn't manually set a preference
|
|
25
20
|
if (!savedTheme) {
|
|
26
21
|
this.setTheme(e.matches ? 'dark' : 'light');
|
|
27
22
|
}
|
|
28
23
|
});
|
|
29
24
|
|
|
30
|
-
// Setup theme toggle button
|
|
31
25
|
const themeToggle = document.getElementById('themeToggle');
|
|
32
26
|
if (themeToggle) {
|
|
33
27
|
themeToggle.addEventListener('click', () => this.toggleTheme());
|
|
@@ -45,7 +39,6 @@ class ThemeManager {
|
|
|
45
39
|
localStorage.setItem(this.THEME_KEY, theme);
|
|
46
40
|
this.updateThemeIcon(theme);
|
|
47
41
|
|
|
48
|
-
// Notify embedded iframes (storage events don't fire in same window)
|
|
49
42
|
const msg = { type: 'theme-change', theme };
|
|
50
43
|
document.querySelectorAll('iframe').forEach(iframe => {
|
|
51
44
|
try { iframe.contentWindow.postMessage(msg, '*'); } catch (_) {}
|
|
@@ -73,7 +66,6 @@ class ThemeManager {
|
|
|
73
66
|
}
|
|
74
67
|
}
|
|
75
68
|
|
|
76
|
-
// Initialize theme manager when DOM is ready
|
|
77
69
|
if (document.readyState === 'loading') {
|
|
78
70
|
document.addEventListener('DOMContentLoaded', () => {
|
|
79
71
|
window.themeManager = new ThemeManager();
|