agentgui 1.0.822 → 1.0.824

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 CHANGED
@@ -1,3 +1,19 @@
1
+ ## 2026-04-11
2
+ - refactor: replace jsonl-watcher.js with @lanmower/cc-tail npm package (subclass CCTailWatcher, override _line() to route via JsonlParser); remove custom file-watching logic
3
+ - refactor: split syntax-highlighter.js (216L) into syntax-highlighter.js (132L) + syntax-highlighter-render.js (73L, highlight/createHighlightedElement/highlightElement via prototype extension)
4
+ - refactor: split dialogs.js (268L) into dialogs.js (54L, helpers + window._dialogCore) + dialogs-types.js (112L, window.UIDialog via window._dialogCore)
5
+ - chore: add @lanmower/cc-tail dependency to package.json
6
+
7
+ ## [Unreleased]
8
+
9
+ ### Refactor
10
+ - Split `conversations.js` (737L) into 4 files: conversations.js (117L), conv-sidebar-actions.js (185L), conv-sidebar-clone.js (92L), conv-list-renderer.js (198L)
11
+ - Split `websocket-manager.js` (643L) into 3 files: ws-core.js (163L), ws-latency.js (89L), websocket-manager.js (108L)
12
+ - Split `ui-components.js` (370L) into 2 files: ui-components.js (89L), ui-components-rendering.js (63L)
13
+ - Split `agent-auth.js` into agent-auth.js (147L) and agent-auth-oauth.js (160L)
14
+ - Deleted dead code: event-filter.js (zero external references)
15
+ - Updated index.html script load order for all new split files
16
+
1
17
  ## 2026-04-11
2
18
  - refactor: split conversations.js (742L→116L) into conv-sidebar-actions.js (prototype extension: drag/drop, bulk actions, folder browser), conv-list-renderer.js (loadConversations, render, vnode), conv-sidebar-clone.js (clone UI, delete-all); all ≤200 lines
3
19
  - refactor: split websocket-manager.js (643L) into ws-core.js (class constructor, connect, onOpen, onMessage, reconnect), websocket-manager.js (sendMessage, subscriptions, state); ws-latency/connection/heartbeat empty stubs deleted
@@ -1,36 +1,26 @@
1
1
  import fs from 'fs';
2
- import path from 'path';
3
- import os from 'os';
2
+ import { JsonlWatcher as CCTailWatcher } from '@lanmower/cc-tail';
4
3
  import { JsonlParser } from './jsonl-parser.js';
5
4
 
6
- const PROJECTS_DIR = path.join(os.homedir(), '.claude', 'projects');
7
- const DEBOUNCE_MS = 16;
8
-
9
- export class JsonlWatcher {
5
+ export class JsonlWatcher extends CCTailWatcher {
10
6
  constructor({ broadcastSync, queries, ownedSessionIds }) {
7
+ super();
11
8
  this._parser = new JsonlParser({ broadcastSync, queries, ownedSessionIds });
12
- this._tails = new Map();
13
- this._timers = new Map();
14
- this._watcher = null;
15
9
  }
16
10
 
17
- start() {
18
- if (!fs.existsSync(PROJECTS_DIR)) return;
19
- this._scanDir(PROJECTS_DIR, 0);
20
- for (const [fp, t] of this._timers.entries()) { clearTimeout(t); this._timers.delete(fp); this._read(fp); }
21
- this._parser.endAllStreaming();
22
- try {
23
- this._watcher = fs.watch(PROJECTS_DIR, { recursive: true }, (_, f) => {
24
- if (f && f.endsWith('.jsonl')) this._debounce(path.join(PROJECTS_DIR, f));
25
- });
26
- this._watcher.on('error', (e) => console.error('[JsonlWatcher]', e.message));
27
- } catch (e) { console.error('[JsonlWatcher] start failed:', e.message); }
11
+ _line(line) {
12
+ line = line.trim();
13
+ if (!line) return;
14
+ let e;
15
+ try { e = JSON.parse(line); } catch (_) { return; }
16
+ if (!e || !e.sessionId) return;
17
+ const cid = this._parser._conv(e.sessionId, e);
18
+ if (cid) this._parser._route(cid, e.sessionId, e);
28
19
  }
29
20
 
30
21
  stop() {
31
- if (this._watcher) try { this._watcher.close(); } catch (_) {}
32
- for (const s of this._tails.values()) if (s.fd !== null) try { fs.closeSync(s.fd); } catch (_) {}
33
- for (const t of this._timers.values()) clearTimeout(t);
22
+ super.stop();
23
+ this._parser.endAllStreaming();
34
24
  }
35
25
 
36
26
  removeConversation(conversationId) {
@@ -54,45 +44,4 @@ export class JsonlWatcher {
54
44
  this._timers.clear();
55
45
  this._parser.clear();
56
46
  }
57
-
58
- _scanDir(dir, depth) {
59
- if (depth > 4) return;
60
- try {
61
- for (const d of fs.readdirSync(dir, { withFileTypes: true })) {
62
- const fp = path.join(dir, d.name);
63
- if (d.isFile() && d.name.endsWith('.jsonl')) this._debounce(fp);
64
- else if (d.isDirectory()) this._scanDir(fp, depth + 1);
65
- }
66
- } catch (_) {}
67
- }
68
-
69
- _debounce(fp) {
70
- const t = this._timers.get(fp);
71
- if (t) clearTimeout(t);
72
- this._timers.set(fp, setTimeout(() => { this._timers.delete(fp); this._read(fp); }, DEBOUNCE_MS));
73
- }
74
-
75
- _read(fp) {
76
- let s = this._tails.get(fp);
77
- if (!s) { s = { fd: null, offset: 0, partial: '' }; this._tails.set(fp, s); }
78
- try {
79
- if (s.fd === null) s.fd = fs.openSync(fp, 'r');
80
- const stat = fs.fstatSync(s.fd);
81
- if (stat.size <= s.offset) return;
82
- const buf = Buffer.allocUnsafe(stat.size - s.offset);
83
- const n = fs.readSync(s.fd, buf, 0, buf.length, s.offset);
84
- s.offset += n;
85
- const text = s.partial + buf.toString('utf8', 0, n);
86
- const t0 = process.hrtime.bigint();
87
- const lines = []; let start = 0, idx;
88
- while ((idx = text.indexOf('\n', start)) !== -1) { lines.push(text.slice(start, idx)); start = idx + 1; }
89
- s.partial = text.slice(start);
90
- const ms = Number(process.hrtime.bigint() - t0) / 1e6;
91
- if (ms > 5) console.warn(`[JsonlWatcher] hot path ${ms.toFixed(1)}ms (${lines.length} lines)`);
92
- for (const l of lines) this._parser._line(fp, l);
93
- } catch (e) {
94
- if (e.code !== 'ENOENT') console.error('[JsonlWatcher] read error:', e.message);
95
- if (s.fd !== null) { try { fs.closeSync(s.fd); } catch (_) {} s.fd = null; }
96
- }
97
- }
98
47
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentgui",
3
- "version": "1.0.822",
3
+ "version": "1.0.824",
4
4
  "description": "Multi-agent ACP client with real-time communication",
5
5
  "type": "module",
6
6
  "main": "electron/main.js",
@@ -28,6 +28,7 @@
28
28
  "@google/gemini-cli": "latest",
29
29
  "@huggingface/transformers": "^3.8.1",
30
30
  "@kilocode/cli": "latest",
31
+ "@lanmower/cc-tail": "^1.0.2",
31
32
  "audio-decode": "^2.2.3",
32
33
  "better-sqlite3": "^12.6.2",
33
34
  "busboy": "^1.6.0",
package/static/index.html CHANGED
@@ -282,6 +282,7 @@
282
282
  <script defer src="/gm/js/event-processor.js"></script>
283
283
  <script defer src="/gm/js/streaming-renderer.js"></script>
284
284
  <script defer src="/gm/js/image-loader.js"></script>
285
+ <script defer src="/gm/js/image-loader-element.js"></script>
285
286
  <script defer src="/gm/lib/webjsx.js"></script>
286
287
  <script defer src="/gm/lib/xstate.umd.min.js"></script>
287
288
  <script defer src="/gm/js/ws-machine.js"></script>
@@ -298,10 +299,13 @@
298
299
  <script defer src="/gm/js/conv-sidebar-clone.js"></script>
299
300
  <script defer src="/gm/lib/msgpackr.min.js"></script>
300
301
  <script defer src="/gm/js/ws-core.js"></script>
302
+ <script defer src="/gm/js/ws-latency.js"></script>
301
303
  <script defer src="/gm/js/websocket-manager.js"></script>
302
304
  <script defer src="/gm/js/ws-client.js"></script>
303
305
  <script defer src="/gm/js/syntax-highlighter.js"></script>
306
+ <script defer src="/gm/js/syntax-highlighter-render.js"></script>
304
307
  <script defer src="/gm/js/dialogs.js"></script>
308
+ <script defer src="/gm/js/dialogs-types.js"></script>
305
309
  <script defer src="/gm/js/ui-components.js"></script>
306
310
  <script defer src="/gm/js/ui-components-rendering.js"></script>
307
311
  <script defer src="/gm/js/state-barrier.js"></script>
@@ -313,7 +317,6 @@
313
317
  <script defer src="/gm/js/voice.js"></script>
314
318
  <script defer src="/gm/js/pm2-monitor.js"></script>
315
319
  <script defer src="/gm/js/event-filter-config.js"></script>
316
- <script defer src="/gm/js/event-filter.js"></script>
317
320
  <script defer src="/gm/js/client.js"></script>
318
321
  <script defer src="/gm/js/features.js"></script>
319
322
  <script defer src="/gm/js/agent-auth.js"></script>
@@ -0,0 +1,111 @@
1
+ (function() {
2
+ var c = window._dialogCore;
3
+
4
+ window.UIDialog = {
5
+ alert: function(message, title) {
6
+ return new Promise(function(resolve) {
7
+ var overlay = c.createOverlay();
8
+ var dialog = document.createElement('div');
9
+ dialog.className = 'dialog-container';
10
+ dialog.innerHTML =
11
+ '<div class="dialog-box"><div class="dialog-header"><h3 class="dialog-title">' + c.escapeHtml(title || 'Alert') + '</h3></div>' +
12
+ '<div class="dialog-body"><p class="dialog-message">' + c.escapeHtml(message) + '</p></div>' +
13
+ '<div class="dialog-footer"><button class="dialog-btn dialog-btn-primary" data-action="ok">OK</button></div></div>';
14
+ var okBtn = dialog.querySelector('[data-action="ok"]');
15
+ okBtn.addEventListener('click', function() { c.closeDialog(dialog, overlay); resolve(true); });
16
+ overlay.querySelector('.dialog-backdrop').addEventListener('click', function() { c.closeDialog(dialog, overlay); resolve(true); });
17
+ document.addEventListener('keydown', function handler(e) {
18
+ if (e.key === 'Escape' || e.key === 'Enter') { document.removeEventListener('keydown', handler); c.closeDialog(dialog, overlay); resolve(true); }
19
+ });
20
+ c.showDialog(dialog, overlay);
21
+ });
22
+ },
23
+
24
+ confirm: function(message, title) {
25
+ return new Promise(function(resolve) {
26
+ var overlay = c.createOverlay();
27
+ var dialog = document.createElement('div');
28
+ dialog.className = 'dialog-container';
29
+ dialog.innerHTML =
30
+ '<div class="dialog-box"><div class="dialog-header"><h3 class="dialog-title">' + c.escapeHtml(title || 'Confirm') + '</h3></div>' +
31
+ '<div class="dialog-body"><p class="dialog-message">' + c.escapeHtml(message).replace(/\n/g, '<br>') + '</p></div>' +
32
+ '<div class="dialog-footer"><button class="dialog-btn dialog-btn-secondary" data-action="cancel">Cancel</button>' +
33
+ '<button class="dialog-btn dialog-btn-primary dialog-btn-danger" data-action="confirm">Confirm</button></div></div>';
34
+ var cancelBtn = dialog.querySelector('[data-action="cancel"]');
35
+ var confirmBtn = dialog.querySelector('[data-action="confirm"]');
36
+ cancelBtn.addEventListener('click', function() { c.closeDialog(dialog, overlay); resolve(false); });
37
+ confirmBtn.addEventListener('click', function() { c.closeDialog(dialog, overlay); resolve(true); });
38
+ overlay.querySelector('.dialog-backdrop').addEventListener('click', function() { c.closeDialog(dialog, overlay); resolve(false); });
39
+ document.addEventListener('keydown', function handler(e) {
40
+ if (e.key === 'Escape') { document.removeEventListener('keydown', handler); c.closeDialog(dialog, overlay); resolve(false); }
41
+ else if (e.key === 'Enter') { document.removeEventListener('keydown', handler); c.closeDialog(dialog, overlay); resolve(true); }
42
+ });
43
+ c.showDialog(dialog, overlay);
44
+ });
45
+ },
46
+
47
+ prompt: function(message, defaultValue, title) {
48
+ return new Promise(function(resolve) {
49
+ var overlay = c.createOverlay();
50
+ var dialog = document.createElement('div');
51
+ dialog.className = 'dialog-container';
52
+ dialog.innerHTML =
53
+ '<div class="dialog-box"><div class="dialog-header"><h3 class="dialog-title">' + c.escapeHtml(title || 'Input') + '</h3></div>' +
54
+ '<div class="dialog-body"><label class="dialog-label">' + c.escapeHtml(message) + '</label>' +
55
+ '<input type="text" class="dialog-input" value="' + c.escapeHtml(defaultValue || '') + '"></div>' +
56
+ '<div class="dialog-footer"><button class="dialog-btn dialog-btn-secondary" data-action="cancel">Cancel</button>' +
57
+ '<button class="dialog-btn dialog-btn-primary" data-action="ok">OK</button></div></div>';
58
+ var input = dialog.querySelector('.dialog-input');
59
+ var cancelBtn = dialog.querySelector('[data-action="cancel"]');
60
+ var okBtn = dialog.querySelector('[data-action="ok"]');
61
+ cancelBtn.addEventListener('click', function() { c.closeDialog(dialog, overlay); resolve(null); });
62
+ okBtn.addEventListener('click', function() { c.closeDialog(dialog, overlay); resolve(input.value); });
63
+ input.addEventListener('keydown', function(e) { if (e.key === 'Enter') { c.closeDialog(dialog, overlay); resolve(input.value); } });
64
+ overlay.querySelector('.dialog-backdrop').addEventListener('click', function() { c.closeDialog(dialog, overlay); resolve(null); });
65
+ document.addEventListener('keydown', function handler(e) {
66
+ if (e.key === 'Escape') { document.removeEventListener('keydown', handler); c.closeDialog(dialog, overlay); resolve(null); }
67
+ });
68
+ c.showDialog(dialog, overlay);
69
+ });
70
+ },
71
+
72
+ showProgress: function(config) {
73
+ var overlay = c.createOverlay();
74
+ var dialog = document.createElement('div');
75
+ dialog.className = 'dialog-container';
76
+ dialog.innerHTML =
77
+ '<div class="dialog-box dialog-box-progress"><div class="dialog-header"><h3 class="dialog-title">' + c.escapeHtml(config.title || 'Please wait') + '</h3></div>' +
78
+ '<div class="dialog-body"><p class="dialog-message progress-message">' + c.escapeHtml(config.message || 'Loading...') + '</p>' +
79
+ '<div class="dialog-progress-bar"><div class="dialog-progress-fill" style="width: 0%"></div></div>' +
80
+ '<p class="dialog-progress-percent">0%</p></div></div>';
81
+ c.showDialog(dialog, overlay);
82
+ var progressFill = dialog.querySelector('.dialog-progress-fill');
83
+ var progressPercent = dialog.querySelector('.dialog-progress-percent');
84
+ var progressMessage = dialog.querySelector('.progress-message');
85
+ return {
86
+ update: function(percent, message) {
87
+ progressFill.style.width = percent + '%';
88
+ progressPercent.textContent = Math.round(percent) + '%';
89
+ if (message) progressMessage.textContent = message;
90
+ },
91
+ close: function() { c.closeDialog(dialog, overlay); }
92
+ };
93
+ },
94
+
95
+ showToast: function(message, type, duration) {
96
+ var existing = document.querySelector('.toast-notification');
97
+ if (existing) existing.remove();
98
+ var toast = document.createElement('div');
99
+ toast.className = 'toast-notification toast-' + (type || 'info');
100
+ toast.innerHTML = '<span class="toast-message">' + c.escapeHtml(message) + '</span>';
101
+ document.body.appendChild(toast);
102
+ requestAnimationFrame(function() { toast.classList.add('visible'); });
103
+ setTimeout(function() {
104
+ toast.classList.remove('visible');
105
+ setTimeout(function() { if (toast.parentNode) toast.remove(); }, 300);
106
+ }, duration || 3000);
107
+ },
108
+
109
+ closeAll: c.closeAllDialogs
110
+ };
111
+ })();
@@ -1,267 +1,53 @@
1
- (function() {
2
- var activeDialogs = [];
3
- var dialogZIndex = 10000;
4
-
5
- function escapeHtml(text) {
6
- if (typeof window._escHtml === 'function') return window._escHtml(text);
7
- var d = document.createElement('div'); d.textContent = text; return d.innerHTML;
8
- }
9
-
10
- function createOverlay() {
11
- var overlay = document.createElement('div');
12
- overlay.className = 'dialog-overlay';
13
- overlay.innerHTML = '<div class="dialog-backdrop"></div>';
14
- return overlay;
15
- }
16
-
17
- function showDialog(dialog, overlay) {
18
- dialogZIndex++;
19
- if (overlay) {
20
- overlay.style.zIndex = dialogZIndex;
21
- document.body.appendChild(overlay);
22
- }
23
- dialog.style.zIndex = dialogZIndex + 1;
24
- document.body.appendChild(dialog);
25
- activeDialogs.push({ dialog: dialog, overlay: overlay });
26
-
27
- requestAnimationFrame(function() {
28
- dialog.classList.add('visible');
29
- if (overlay) overlay.classList.add('visible');
30
- var input = dialog.querySelector('input, textarea');
31
- if (input) input.focus();
32
- else {
33
- var btn = dialog.querySelector('.dialog-btn-primary');
34
- if (btn) btn.focus();
35
- }
36
- });
37
- }
38
-
39
- function closeDialog(dialog, overlay) {
40
- dialog.classList.remove('visible');
41
- if (overlay) overlay.classList.remove('visible');
42
- setTimeout(function() {
43
- if (dialog.parentNode) dialog.remove();
44
- if (overlay && overlay.parentNode) overlay.remove();
45
- }, 200);
46
- activeDialogs = activeDialogs.filter(function(d) {
47
- return d.dialog !== dialog;
48
- });
49
- }
50
-
51
- function closeAllDialogs() {
52
- activeDialogs.forEach(function(d) {
53
- closeDialog(d.dialog, d.overlay);
54
- });
55
- }
56
-
57
- window.UIDialog = {
58
- alert: function(message, title) {
59
- return new Promise(function(resolve) {
60
- var overlay = createOverlay();
61
- var dialog = document.createElement('div');
62
- dialog.className = 'dialog-container';
63
- dialog.innerHTML =
64
- '<div class="dialog-box">' +
65
- '<div class="dialog-header">' +
66
- '<h3 class="dialog-title">' + escapeHtml(title || 'Alert') + '</h3>' +
67
- '</div>' +
68
- '<div class="dialog-body">' +
69
- '<p class="dialog-message">' + escapeHtml(message) + '</p>' +
70
- '</div>' +
71
- '<div class="dialog-footer">' +
72
- '<button class="dialog-btn dialog-btn-primary" data-action="ok">OK</button>' +
73
- '</div>' +
74
- '</div>';
75
-
76
- var okBtn = dialog.querySelector('[data-action="ok"]');
77
- okBtn.addEventListener('click', function() {
78
- closeDialog(dialog, overlay);
79
- resolve(true);
80
- });
81
-
82
- overlay.querySelector('.dialog-backdrop').addEventListener('click', function() {
83
- closeDialog(dialog, overlay);
84
- resolve(true);
85
- });
86
-
87
- document.addEventListener('keydown', function handler(e) {
88
- if (e.key === 'Escape' || e.key === 'Enter') {
89
- document.removeEventListener('keydown', handler);
90
- closeDialog(dialog, overlay);
91
- resolve(true);
92
- }
93
- });
94
-
95
- showDialog(dialog, overlay);
96
- });
97
- },
98
-
99
- confirm: function(message, title) {
100
- return new Promise(function(resolve) {
101
- var overlay = createOverlay();
102
- var dialog = document.createElement('div');
103
- dialog.className = 'dialog-container';
104
- dialog.innerHTML =
105
- '<div class="dialog-box">' +
106
- '<div class="dialog-header">' +
107
- '<h3 class="dialog-title">' + escapeHtml(title || 'Confirm') + '</h3>' +
108
- '</div>' +
109
- '<div class="dialog-body">' +
110
- '<p class="dialog-message">' + escapeHtml(message).replace(/\n/g, '<br>') + '</p>' +
111
- '</div>' +
112
- '<div class="dialog-footer">' +
113
- '<button class="dialog-btn dialog-btn-secondary" data-action="cancel">Cancel</button>' +
114
- '<button class="dialog-btn dialog-btn-primary dialog-btn-danger" data-action="confirm">Confirm</button>' +
115
- '</div>' +
116
- '</div>';
117
-
118
- var cancelBtn = dialog.querySelector('[data-action="cancel"]');
119
- var confirmBtn = dialog.querySelector('[data-action="confirm"]');
120
-
121
- cancelBtn.addEventListener('click', function() {
122
- closeDialog(dialog, overlay);
123
- resolve(false);
124
- });
125
-
126
- confirmBtn.addEventListener('click', function() {
127
- closeDialog(dialog, overlay);
128
- resolve(true);
129
- });
130
-
131
- overlay.querySelector('.dialog-backdrop').addEventListener('click', function() {
132
- closeDialog(dialog, overlay);
133
- resolve(false);
134
- });
135
-
136
- document.addEventListener('keydown', function handler(e) {
137
- if (e.key === 'Escape') {
138
- document.removeEventListener('keydown', handler);
139
- closeDialog(dialog, overlay);
140
- resolve(false);
141
- } else if (e.key === 'Enter') {
142
- document.removeEventListener('keydown', handler);
143
- closeDialog(dialog, overlay);
144
- resolve(true);
145
- }
146
- });
147
-
148
- showDialog(dialog, overlay);
149
- });
150
- },
151
-
152
- prompt: function(message, defaultValue, title) {
153
- return new Promise(function(resolve) {
154
- var overlay = createOverlay();
155
- var dialog = document.createElement('div');
156
- dialog.className = 'dialog-container';
157
- dialog.innerHTML =
158
- '<div class="dialog-box">' +
159
- '<div class="dialog-header">' +
160
- '<h3 class="dialog-title">' + escapeHtml(title || 'Input') + '</h3>' +
161
- '</div>' +
162
- '<div class="dialog-body">' +
163
- '<label class="dialog-label">' + escapeHtml(message) + '</label>' +
164
- '<input type="text" class="dialog-input" value="' + escapeHtml(defaultValue || '') + '">' +
165
- '</div>' +
166
- '<div class="dialog-footer">' +
167
- '<button class="dialog-btn dialog-btn-secondary" data-action="cancel">Cancel</button>' +
168
- '<button class="dialog-btn dialog-btn-primary" data-action="ok">OK</button>' +
169
- '</div>' +
170
- '</div>';
171
-
172
- var input = dialog.querySelector('.dialog-input');
173
- var cancelBtn = dialog.querySelector('[data-action="cancel"]');
174
- var okBtn = dialog.querySelector('[data-action="ok"]');
175
-
176
- cancelBtn.addEventListener('click', function() {
177
- closeDialog(dialog, overlay);
178
- resolve(null);
179
- });
180
-
181
- okBtn.addEventListener('click', function() {
182
- closeDialog(dialog, overlay);
183
- resolve(input.value);
184
- });
185
-
186
- input.addEventListener('keydown', function(e) {
187
- if (e.key === 'Enter') {
188
- closeDialog(dialog, overlay);
189
- resolve(input.value);
190
- }
191
- });
192
-
193
- overlay.querySelector('.dialog-backdrop').addEventListener('click', function() {
194
- closeDialog(dialog, overlay);
195
- resolve(null);
196
- });
197
-
198
- document.addEventListener('keydown', function handler(e) {
199
- if (e.key === 'Escape') {
200
- document.removeEventListener('keydown', handler);
201
- closeDialog(dialog, overlay);
202
- resolve(null);
203
- }
204
- });
205
-
206
- showDialog(dialog, overlay);
207
- });
208
- },
209
-
210
- showProgress: function(config) {
211
- var overlay = createOverlay();
212
- var dialog = document.createElement('div');
213
- dialog.className = 'dialog-container';
214
- dialog.innerHTML =
215
- '<div class="dialog-box dialog-box-progress">' +
216
- '<div class="dialog-header">' +
217
- '<h3 class="dialog-title">' + escapeHtml(config.title || 'Please wait') + '</h3>' +
218
- '</div>' +
219
- '<div class="dialog-body">' +
220
- '<p class="dialog-message progress-message">' + escapeHtml(config.message || 'Loading...') + '</p>' +
221
- '<div class="dialog-progress-bar">' +
222
- '<div class="dialog-progress-fill" style="width: 0%"></div>' +
223
- '</div>' +
224
- '<p class="dialog-progress-percent">0%</p>' +
225
- '</div>' +
226
- '</div>';
227
-
228
- showDialog(dialog, overlay);
229
-
230
- var progressFill = dialog.querySelector('.dialog-progress-fill');
231
- var progressPercent = dialog.querySelector('.dialog-progress-percent');
232
- var progressMessage = dialog.querySelector('.progress-message');
233
-
234
- return {
235
- update: function(percent, message) {
236
- progressFill.style.width = percent + '%';
237
- progressPercent.textContent = Math.round(percent) + '%';
238
- if (message) progressMessage.textContent = message;
239
- },
240
- close: function() {
241
- closeDialog(dialog, overlay);
242
- }
243
- };
244
- },
245
-
246
- showToast: function(message, type, duration) {
247
- var existing = document.querySelector('.toast-notification');
248
- if (existing) existing.remove();
249
-
250
- var toast = document.createElement('div');
251
- toast.className = 'toast-notification toast-' + (type || 'info');
252
- toast.innerHTML = '<span class="toast-message">' + escapeHtml(message) + '</span>';
253
- document.body.appendChild(toast);
254
-
255
- requestAnimationFrame(function() {
256
- toast.classList.add('visible');
257
- });
258
-
259
- setTimeout(function() {
260
- toast.classList.remove('visible');
261
- setTimeout(function() { if (toast.parentNode) toast.remove(); }, 300);
262
- }, duration || 3000);
263
- },
264
-
265
- closeAll: closeAllDialogs
266
- };
267
- })();
1
+ (function() {
2
+ var activeDialogs = [];
3
+ var dialogZIndex = 10000;
4
+
5
+ function escapeHtml(text) {
6
+ if (typeof window._escHtml === 'function') return window._escHtml(text);
7
+ var d = document.createElement('div'); d.textContent = text; return d.innerHTML;
8
+ }
9
+
10
+ function createOverlay() {
11
+ var overlay = document.createElement('div');
12
+ overlay.className = 'dialog-overlay';
13
+ overlay.innerHTML = '<div class="dialog-backdrop"></div>';
14
+ return overlay;
15
+ }
16
+
17
+ function showDialog(dialog, overlay) {
18
+ dialogZIndex++;
19
+ if (overlay) {
20
+ overlay.style.zIndex = dialogZIndex;
21
+ document.body.appendChild(overlay);
22
+ }
23
+ dialog.style.zIndex = dialogZIndex + 1;
24
+ document.body.appendChild(dialog);
25
+ activeDialogs.push({ dialog: dialog, overlay: overlay });
26
+ requestAnimationFrame(function() {
27
+ dialog.classList.add('visible');
28
+ if (overlay) overlay.classList.add('visible');
29
+ var input = dialog.querySelector('input, textarea');
30
+ if (input) input.focus();
31
+ else {
32
+ var btn = dialog.querySelector('.dialog-btn-primary');
33
+ if (btn) btn.focus();
34
+ }
35
+ });
36
+ }
37
+
38
+ function closeDialog(dialog, overlay) {
39
+ dialog.classList.remove('visible');
40
+ if (overlay) overlay.classList.remove('visible');
41
+ setTimeout(function() {
42
+ if (dialog.parentNode) dialog.remove();
43
+ if (overlay && overlay.parentNode) overlay.remove();
44
+ }, 200);
45
+ activeDialogs = activeDialogs.filter(function(d) { return d.dialog !== dialog; });
46
+ }
47
+
48
+ function closeAllDialogs() {
49
+ activeDialogs.forEach(function(d) { closeDialog(d.dialog, d.overlay); });
50
+ }
51
+
52
+ window._dialogCore = { escapeHtml: escapeHtml, createOverlay: createOverlay, showDialog: showDialog, closeDialog: closeDialog, closeAllDialogs: closeAllDialogs };
53
+ })();