agentgui 1.0.546 → 1.0.548

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/static/js/client.js +118 -16
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentgui",
3
- "version": "1.0.546",
3
+ "version": "1.0.548",
4
4
  "description": "Multi-agent ACP client with real-time communication",
5
5
  "type": "module",
6
6
  "main": "server.js",
@@ -35,6 +35,16 @@ class AgentGUIClient {
35
35
  this.conversationCache = new Map();
36
36
  this.MAX_CACHE_SIZE = 10;
37
37
 
38
+ // Conversation list cache with TTL
39
+ this.conversationListCache = {
40
+ data: [],
41
+ timestamp: 0,
42
+ ttl: 30000 // 30 seconds
43
+ };
44
+
45
+ // Draft prompts per conversation
46
+ this.draftPrompts = new Map();
47
+
38
48
  // Event handlers
39
49
  this.eventHandlers = {};
40
50
 
@@ -155,12 +165,16 @@ class AgentGUIClient {
155
165
  this.updateConnectionStatus('connected');
156
166
  this._subscribeToConversationUpdates();
157
167
  this._recoverMissedChunks();
168
+ this.updateSendButtonState();
169
+ this.enablePromptArea();
158
170
  this.emit('ws:connected');
159
171
  });
160
172
 
161
173
  this.wsManager.on('disconnected', () => {
162
174
  console.log('WebSocket disconnected');
163
175
  this.updateConnectionStatus('disconnected');
176
+ this.updateSendButtonState();
177
+ this.disablePromptArea();
164
178
  this.emit('ws:disconnected');
165
179
  });
166
180
 
@@ -194,10 +208,16 @@ class AgentGUIClient {
194
208
  // Switch to idle view when selecting non-streaming conversation
195
209
  window.addEventListener('conversation-selected', (e) => {
196
210
  const convId = e.detail.conversationId;
211
+ // Save draft from previous conversation before switching
212
+ this.saveDraftPrompt();
213
+
197
214
  const isStreaming = convId && this.state.streamingConversations.has(convId);
198
215
  if (!isStreaming && window.switchView) {
199
216
  window.switchView('chat');
200
217
  }
218
+
219
+ // Restore draft for new conversation after a tick
220
+ setTimeout(() => this.restoreDraftPrompt(convId), 0);
201
221
  });
202
222
 
203
223
  // Preserve controls state across tab switches
@@ -392,6 +412,19 @@ class AgentGUIClient {
392
412
  this.ui.agentSelector = document.querySelector('[data-agent-selector]');
393
413
  this.ui.modelSelector = document.querySelector('[data-model-selector]');
394
414
 
415
+ // Auto-save drafts on input
416
+ if (this.ui.messageInput) {
417
+ this.ui.messageInput.addEventListener('input', () => {
418
+ this.saveDraftPrompt();
419
+ });
420
+
421
+ // Restore draft when conversation loads
422
+ const currentConvId = this.state.currentConversation?.id;
423
+ if (currentConvId) {
424
+ this.restoreDraftPrompt(currentConvId);
425
+ }
426
+ }
427
+
395
428
  if (this.ui.cliSelector) {
396
429
  this.ui.cliSelector.addEventListener('change', () => {
397
430
  if (!this._agentLocked) {
@@ -2140,10 +2173,21 @@ class AgentGUIClient {
2140
2173
  * Load conversations
2141
2174
  */
2142
2175
  async loadConversations() {
2176
+ // Return cached conversations if still fresh
2177
+ const now = Date.now();
2178
+ if (this.conversationListCache.data.length > 0 &&
2179
+ (now - this.conversationListCache.timestamp) < this.conversationListCache.ttl) {
2180
+ this.state.conversations = this.conversationListCache.data;
2181
+ return this.conversationListCache.data;
2182
+ }
2183
+
2143
2184
  return this._dedupedFetch('loadConversations', async () => {
2144
2185
  try {
2145
2186
  const { conversations } = await window.wsClient.rpc('conv.ls');
2146
2187
  this.state.conversations = conversations;
2188
+ // Update cache
2189
+ this.conversationListCache.data = conversations;
2190
+ this.conversationListCache.timestamp = Date.now();
2147
2191
  return conversations;
2148
2192
  } catch (error) {
2149
2193
  console.error('Failed to load conversations:', error);
@@ -2284,33 +2328,23 @@ class AgentGUIClient {
2284
2328
 
2285
2329
  /**
2286
2330
  * Disable UI controls during streaming
2331
+ * NOTE: Only disables stop button visibility. Prompt, input, and inject remain enabled.
2287
2332
  */
2288
2333
  disableControls() {
2289
- if (this.ui.messageInput) {
2290
- this.ui.messageInput.disabled = true;
2291
- }
2292
- if (this.ui.sendButton) {
2293
- this.ui.sendButton.disabled = true;
2294
- }
2295
- const injectBtn = document.getElementById('injectBtn');
2334
+ // Prompt area and inject button are NEVER disabled during streaming
2335
+ // Only stop button behavior changes
2296
2336
  const stopBtn = document.getElementById('stopBtn');
2297
- if (injectBtn) injectBtn.disabled = true;
2298
2337
  if (stopBtn) stopBtn.disabled = false;
2299
2338
  }
2300
2339
 
2301
2340
  /**
2302
2341
  * Enable UI controls
2342
+ * NOTE: Restores stop button visibility. Prompt area always stays enabled.
2303
2343
  */
2304
2344
  enableControls() {
2305
- if (this.ui.messageInput) {
2306
- this.ui.messageInput.disabled = false;
2307
- }
2308
- if (this.ui.sendButton) {
2309
- this.ui.sendButton.disabled = false;
2310
- }
2311
- const injectBtn = document.getElementById('injectBtn');
2345
+ // Prompt area and inject button are always enabled unless disconnected
2346
+ // Only stop button behavior changes
2312
2347
  const stopBtn = document.getElementById('stopBtn');
2313
- if (injectBtn) injectBtn.disabled = false;
2314
2348
  if (stopBtn) stopBtn.disabled = true;
2315
2349
  }
2316
2350
 
@@ -2874,6 +2908,74 @@ class AgentGUIClient {
2874
2908
  };
2875
2909
  }
2876
2910
 
2911
+ /**
2912
+ * Save draft prompt for current conversation
2913
+ */
2914
+ saveDraftPrompt() {
2915
+ const convId = this.state.currentConversation?.id;
2916
+ if (convId && this.ui.messageInput) {
2917
+ const draft = this.ui.messageInput.value;
2918
+ this.draftPrompts.set(convId, draft);
2919
+ if (draft) {
2920
+ localStorage.setItem(`draft-${convId}`, draft);
2921
+ }
2922
+ }
2923
+ }
2924
+
2925
+ /**
2926
+ * Restore draft prompt for conversation
2927
+ */
2928
+ restoreDraftPrompt(conversationId) {
2929
+ if (!this.ui.messageInput) return;
2930
+
2931
+ let draft = this.draftPrompts.get(conversationId) || '';
2932
+ if (!draft) {
2933
+ draft = localStorage.getItem(`draft-${conversationId}`) || '';
2934
+ if (draft) this.draftPrompts.set(conversationId, draft);
2935
+ }
2936
+
2937
+ this.ui.messageInput.value = draft;
2938
+ }
2939
+
2940
+ /**
2941
+ * Clear draft for conversation
2942
+ */
2943
+ clearDraft(conversationId) {
2944
+ this.draftPrompts.delete(conversationId);
2945
+ localStorage.removeItem(`draft-${conversationId}`);
2946
+ }
2947
+
2948
+ /**
2949
+ * Update send button state based on WebSocket connection
2950
+ */
2951
+ updateSendButtonState() {
2952
+ if (this.ui.sendButton) {
2953
+ this.ui.sendButton.disabled = !this.wsManager.isConnected;
2954
+ }
2955
+ }
2956
+
2957
+ /**
2958
+ * Disable prompt area (input and inject button) only on disconnect
2959
+ */
2960
+ disablePromptArea() {
2961
+ if (this.ui.messageInput) {
2962
+ this.ui.messageInput.disabled = true;
2963
+ }
2964
+ const injectBtn = document.getElementById('injectBtn');
2965
+ if (injectBtn) injectBtn.disabled = true;
2966
+ }
2967
+
2968
+ /**
2969
+ * Enable prompt area (input and inject button) on connect
2970
+ */
2971
+ enablePromptArea() {
2972
+ if (this.ui.messageInput) {
2973
+ this.ui.messageInput.disabled = false;
2974
+ }
2975
+ const injectBtn = document.getElementById('injectBtn');
2976
+ if (injectBtn) injectBtn.disabled = false;
2977
+ }
2978
+
2877
2979
  /**
2878
2980
  * Cleanup resources
2879
2981
  */