agentgui 1.0.822 → 1.0.823

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,13 @@
1
+ ## [Unreleased]
2
+
3
+ ### Refactor
4
+ - 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)
5
+ - Split `websocket-manager.js` (643L) into 3 files: ws-core.js (163L), ws-latency.js (89L), websocket-manager.js (108L)
6
+ - Split `ui-components.js` (370L) into 2 files: ui-components.js (89L), ui-components-rendering.js (63L)
7
+ - Split `agent-auth.js` into agent-auth.js (147L) and agent-auth-oauth.js (160L)
8
+ - Deleted dead code: event-filter.js (zero external references)
9
+ - Updated index.html script load order for all new split files
10
+
1
11
  ## 2026-04-11
2
12
  - 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
13
  - 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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentgui",
3
- "version": "1.0.822",
3
+ "version": "1.0.823",
4
4
  "description": "Multi-agent ACP client with real-time communication",
5
5
  "type": "module",
6
6
  "main": "electron/main.js",
package/static/index.html CHANGED
@@ -298,6 +298,7 @@
298
298
  <script defer src="/gm/js/conv-sidebar-clone.js"></script>
299
299
  <script defer src="/gm/lib/msgpackr.min.js"></script>
300
300
  <script defer src="/gm/js/ws-core.js"></script>
301
+ <script defer src="/gm/js/ws-latency.js"></script>
301
302
  <script defer src="/gm/js/websocket-manager.js"></script>
302
303
  <script defer src="/gm/js/ws-client.js"></script>
303
304
  <script defer src="/gm/js/syntax-highlighter.js"></script>
@@ -313,7 +314,6 @@
313
314
  <script defer src="/gm/js/voice.js"></script>
314
315
  <script defer src="/gm/js/pm2-monitor.js"></script>
315
316
  <script defer src="/gm/js/event-filter-config.js"></script>
316
- <script defer src="/gm/js/event-filter.js"></script>
317
317
  <script defer src="/gm/js/client.js"></script>
318
318
  <script defer src="/gm/js/features.js"></script>
319
319
  <script defer src="/gm/js/agent-auth.js"></script>
@@ -1,183 +1,62 @@
1
-
2
- UIComponents.copyToClipboard = function(text) {
3
- return navigator.clipboard.writeText(text).catch(err => {
4
- console.error('Failed to copy:', err);
5
- return false;
6
- });
7
- };
8
-
9
- UIComponents.downloadFile = function(data, filename, mimeType = 'text/plain') {
10
- const blob = new Blob([data], { type: mimeType });
11
- const url = URL.createObjectURL(blob);
12
- const link = document.createElement('a');
13
- link.href = url;
14
- link.download = filename;
15
- document.body.appendChild(link);
16
- link.click();
17
- document.body.removeChild(link);
18
- URL.revokeObjectURL(url);
19
- };
20
-
21
- UIComponents.createProgressBar = function(config = {}) {
22
- const {
23
- percentage = 0,
24
- label = '',
25
- showLabel = true
26
- } = config;
27
-
28
- const container = document.createElement('div');
29
- container.className = 'progress-container';
30
-
31
- let html = '';
32
- if (label && showLabel) {
33
- html += `<div class="flex justify-between mb-2 text-sm"><span>${UIComponents.escapeHtml(label)}</span><span>${Math.round(percentage)}%</span></div>`;
34
- }
35
-
36
- html += `
37
- <progress class="progress progress-primary progress-xs w-full" value="${Math.min(100, Math.max(0, percentage))}" max="100"></progress>
38
- `;
39
-
40
- container.innerHTML = html;
41
- return container;
42
- };
43
-
44
- UIComponents.createCollapsible = function(config = {}) {
45
- const {
46
- title = 'Details',
47
- content = '',
48
- isOpen = false
49
- } = config;
50
-
51
- const container = document.createElement('div');
52
- container.className = 'collapsible';
53
-
54
- container.innerHTML = `
55
- <details ${isOpen ? 'open' : ''}>
56
- <summary class="cursor-pointer font-semibold hover:bg-base-200 px-2 py-1 rounded transition-colors">
57
- ${UIComponents.escapeHtml(title)}
58
- </summary>
59
- <div class="content mt-2 ml-4">
60
- ${typeof content === 'string' ? content : ''}
61
- </div>
62
- </details>
63
- `;
64
-
65
- return container;
66
- };
67
-
68
- UIComponents.createInput = function(config = {}) {
69
- const {
70
- type = 'text',
71
- name = '',
72
- label = '',
73
- placeholder = '',
74
- value = '',
75
- required = false
76
- } = config;
77
-
78
- const container = document.createElement('div');
79
- container.className = 'form-group mb-4';
80
-
81
- let html = '';
82
- if (label) {
83
- html += `<label class="block text-sm font-medium mb-2">${UIComponents.escapeHtml(label)}</label>`;
1
+ Object.assign(UIComponents, {
2
+ escapeHtml(text) { return window._escHtml ? window._escHtml(String(text)) : String(text).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;'); },
3
+
4
+ copyToClipboard(text) {
5
+ return navigator.clipboard.writeText(text).catch(() => false);
6
+ },
7
+
8
+ downloadFile(data, filename, mimeType = 'text/plain') {
9
+ const blob = new Blob([data], { type: mimeType });
10
+ const url = URL.createObjectURL(blob);
11
+ const link = document.createElement('a');
12
+ link.href = url; link.download = filename;
13
+ document.body.appendChild(link); link.click(); document.body.removeChild(link);
14
+ URL.revokeObjectURL(url);
15
+ },
16
+
17
+ createInput(config = {}) {
18
+ const { type = 'text', name = '', label = '', placeholder = '', value = '', required = false } = config;
19
+ const container = document.createElement('div');
20
+ container.className = 'form-group mb-4';
21
+ let html = '';
22
+ if (label) html += `<label class="block text-sm font-medium mb-2">${UIComponents.escapeHtml(label)}</label>`;
23
+ html += `<input type="${type}" name="${name}" placeholder="${UIComponents.escapeHtml(placeholder)}" value="${UIComponents.escapeHtml(value)}" ${required ? 'required' : ''} class="input input-block input-solid" />`;
24
+ container.innerHTML = html;
25
+ return container;
26
+ },
27
+
28
+ createSelect(config = {}) {
29
+ const { name = '', label = '', options = [], value = '', required = false } = config;
30
+ const container = document.createElement('div');
31
+ container.className = 'form-group mb-4';
32
+ let html = '';
33
+ if (label) html += `<label class="block text-sm font-medium mb-2">${UIComponents.escapeHtml(label)}</label>`;
34
+ html += `<select name="${name}" ${required ? 'required' : ''} class="select select-block select-solid">${options.map(opt => `<option value="${opt.value}" ${opt.value === value ? 'selected' : ''}>${UIComponents.escapeHtml(opt.label)}</option>`).join('')}</select>`;
35
+ container.innerHTML = html;
36
+ return container;
37
+ },
38
+
39
+ createButtonGroup(config = {}) {
40
+ const { buttons = [], vertical = false } = config;
41
+ const container = document.createElement('div');
42
+ container.className = `button-group flex gap-2 ${vertical ? 'flex-col' : 'flex-row'}`;
43
+ buttons.forEach(btn => {
44
+ const button = document.createElement('button');
45
+ button.className = `btn btn-${btn.variant || 'secondary'} flex-${vertical ? '1' : 'none'}`;
46
+ button.textContent = btn.label;
47
+ if (btn.onClick) button.addEventListener('click', btn.onClick);
48
+ container.appendChild(button);
49
+ });
50
+ return container;
51
+ },
52
+
53
+ createBadge(config = {}) {
54
+ const { label = '', variant = 'default', size = 'medium' } = config;
55
+ const sizeClasses = { small: 'badge-sm', medium: 'badge-md', large: 'badge-lg' };
56
+ const variantClasses = { default: 'badge-flat', primary: 'badge-flat-primary', success: 'badge-flat-success', warning: 'badge-flat-warning', error: 'badge-flat-error' };
57
+ const badge = document.createElement('span');
58
+ badge.className = `badge ${sizeClasses[size] || sizeClasses.medium} ${variantClasses[variant] || variantClasses.default}`;
59
+ badge.textContent = label;
60
+ return badge;
84
61
  }
85
-
86
- html += `
87
- <input
88
- type="${type}"
89
- name="${name}"
90
- placeholder="${UIComponents.escapeHtml(placeholder)}"
91
- value="${UIComponents.escapeHtml(value)}"
92
- ${required ? 'required' : ''}
93
- class="input input-block input-solid"
94
- />
95
- `;
96
-
97
- container.innerHTML = html;
98
- return container;
99
- };
100
-
101
- UIComponents.createSelect = function(config = {}) {
102
- const {
103
- name = '',
104
- label = '',
105
- options = [],
106
- value = '',
107
- required = false
108
- } = config;
109
-
110
- const container = document.createElement('div');
111
- container.className = 'form-group mb-4';
112
-
113
- let html = '';
114
- if (label) {
115
- html += `<label class="block text-sm font-medium mb-2">${UIComponents.escapeHtml(label)}</label>`;
116
- }
117
-
118
- html += `
119
- <select
120
- name="${name}"
121
- ${required ? 'required' : ''}
122
- class="select select-block select-solid"
123
- >
124
- ${options.map(opt => `
125
- <option value="${opt.value}" ${opt.value === value ? 'selected' : ''}>
126
- ${UIComponents.escapeHtml(opt.label)}
127
- </option>
128
- `).join('')}
129
- </select>
130
- `;
131
-
132
- container.innerHTML = html;
133
- return container;
134
- };
135
-
136
- UIComponents.createButtonGroup = function(config = {}) {
137
- const {
138
- buttons = [],
139
- vertical = false
140
- } = config;
141
-
142
- const container = document.createElement('div');
143
- container.className = `button-group flex gap-2 ${vertical ? 'flex-col' : 'flex-row'}`;
144
-
145
- buttons.forEach(btn => {
146
- const button = document.createElement('button');
147
- button.className = `btn btn-${btn.variant || 'secondary'} flex-${vertical ? '1' : 'none'}`;
148
- button.textContent = btn.label;
149
- if (btn.onClick) {
150
- button.addEventListener('click', btn.onClick);
151
- }
152
- container.appendChild(button);
153
- });
154
-
155
- return container;
156
- };
157
-
158
- UIComponents.createBadge = function(config = {}) {
159
- const {
160
- label = '',
161
- variant = 'default',
162
- size = 'medium'
163
- } = config;
164
-
165
- const sizeClasses = {
166
- 'small': 'badge-sm',
167
- 'medium': 'badge-md',
168
- 'large': 'badge-lg'
169
- };
170
-
171
- const variantClasses = {
172
- 'default': 'badge-flat',
173
- 'primary': 'badge-flat-primary',
174
- 'success': 'badge-flat-success',
175
- 'warning': 'badge-flat-warning',
176
- 'error': 'badge-flat-error'
177
- };
178
-
179
- const badge = document.createElement('span');
180
- badge.className = `badge ${sizeClasses[size] || sizeClasses['medium']} ${variantClasses[variant] || variantClasses['default']}`;
181
- badge.textContent = label;
182
- return badge;
183
- };
62
+ });
@@ -0,0 +1,88 @@
1
+ Object.assign(WebSocketManager.prototype, {
2
+ startHeartbeat() {
3
+ this.stopHeartbeat();
4
+ this.heartbeatTimer = setInterval(() => { this.ping(); }, this.config.heartbeatInterval);
5
+ },
6
+
7
+ stopHeartbeat() {
8
+ if (this.heartbeatTimer) { clearInterval(this.heartbeatTimer); this.heartbeatTimer = null; }
9
+ },
10
+
11
+ _reportLatency() {
12
+ this.emit('latency', { current: this.latency.current, avg: this.latency.avg, jitter: this.latency.jitter, quality: this.latency.quality, trend: this.latency.trend });
13
+ },
14
+
15
+ _handleVisibilityChange() {
16
+ if (document.visibilityState === 'visible' && !this.isConnected && !this.isManuallyDisconnected) this.connect().catch(() => {});
17
+ },
18
+
19
+ _handleOnline() {
20
+ if (!this.isConnected && !this.isManuallyDisconnected) this.connect().catch(() => {});
21
+ },
22
+
23
+ ping() {
24
+ if (!this.isConnected) return;
25
+ this.latency.pingCounter++;
26
+ const id = this.latency.pingCounter;
27
+ const pongTimer = setTimeout(() => {
28
+ this.latency.missedPongs++;
29
+ this.emit('latency', { current: null, avg: this.latency.avg, jitter: this.latency.jitter, quality: 'poor', missed: true });
30
+ if (this.latency.missedPongs >= 3) { this.onClose(); }
31
+ }, this.config.pongTimeout);
32
+ this.requestMap.set('ping:' + id, { type: 'ping', sentAt: Date.now(), pongTimer });
33
+ try {
34
+ this.ws.send(window._codec ? window._codec.encode({ type: 'ping', id }) : msgpackr.pack({ type: 'ping', id }));
35
+ } catch (_) { clearTimeout(pongTimer); this.requestMap.delete('ping:' + id); }
36
+ },
37
+
38
+ _handlePong(data) {
39
+ const key = 'ping:' + data.id;
40
+ const pending = this.requestMap.get(key);
41
+ if (!pending) return;
42
+ clearTimeout(pending.pongTimer);
43
+ this.requestMap.delete(key);
44
+ this.latency.missedPongs = 0;
45
+ const rtt = Date.now() - pending.sentAt;
46
+ this._recordLatency(rtt);
47
+ this._checkDegradation();
48
+ this._reportLatency();
49
+ },
50
+
51
+ _recordLatency(rtt) {
52
+ const samples = this.latency.samples;
53
+ samples.push(rtt);
54
+ if (samples.length > this.config.latencyWindowSize) samples.shift();
55
+ this.latency.current = rtt;
56
+ this.latency.avg = Math.round(samples.reduce((a, b) => a + b, 0) / samples.length);
57
+ const jitterSamples = samples.slice(1).map((v, i) => Math.abs(v - samples[i]));
58
+ this.latency.jitter = jitterSamples.length ? Math.round(jitterSamples.reduce((a, b) => a + b, 0) / jitterSamples.length) : 0;
59
+ this.latency.quality = this._qualityTier(this.latency.avg);
60
+ if (this._latencyEma === null) this._latencyEma = rtt;
61
+ else this._latencyEma = 0.3 * rtt + 0.7 * this._latencyEma;
62
+ this.latency.predicted = Math.round(this._latencyEma);
63
+ this.stats.avgLatency = this.latency.avg;
64
+ this._trendHistory.push(rtt);
65
+ if (this._trendHistory.length > 5) this._trendHistory.shift();
66
+ if (this._trendHistory.length >= 3) {
67
+ const h = this._trendHistory;
68
+ const slope = (h[h.length - 1] - h[0]) / h.length;
69
+ this.latency.trend = slope > 5 ? 'rising' : slope < -5 ? 'falling' : 'stable';
70
+ }
71
+ },
72
+
73
+ _qualityTier(avg) {
74
+ if (avg < 50) return 'excellent';
75
+ if (avg < 150) return 'good';
76
+ if (avg < 300) return 'fair';
77
+ return 'poor';
78
+ },
79
+
80
+ _checkDegradation() {
81
+ const tier = this.latency.quality;
82
+ const prev = this._lastQualityTier;
83
+ if (tier !== prev) {
84
+ this._lastQualityTier = tier;
85
+ this.emit('quality_change', { quality: tier, latency: this.latency.avg });
86
+ }
87
+ }
88
+ });