agentgui 1.0.772 → 1.0.773

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentgui",
3
- "version": "1.0.772",
3
+ "version": "1.0.773",
4
4
  "description": "Multi-agent ACP client with real-time communication",
5
5
  "type": "module",
6
6
  "main": "electron/main.js",
package/static/app.js CHANGED
@@ -903,4 +903,66 @@ document.addEventListener('keydown', (e) => {
903
903
  });
904
904
  })();
905
905
 
906
+ (function initPresets() {
907
+ const STORAGE_KEY = 'agentgui-presets';
908
+ const saveBtn = document.getElementById('savePresetBtn');
909
+ const selector = document.getElementById('presetSelector');
910
+ if (!saveBtn || !selector) return;
911
+
912
+ function getPresets() {
913
+ try { return JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]'); } catch { return []; }
914
+ }
915
+ function savePresets(p) { localStorage.setItem(STORAGE_KEY, JSON.stringify(p)); }
916
+
917
+ function renderSelector() {
918
+ const presets = getPresets();
919
+ if (presets.length === 0) { selector.style.display = 'none'; return; }
920
+ selector.style.display = 'inline-block';
921
+ selector.innerHTML = '<option value="">Presets</option>' +
922
+ presets.map((p, i) => `<option value="${i}">${window._escHtml(p.name)}</option>`).join('') +
923
+ '<option value="__clear">Clear all</option>';
924
+ }
925
+
926
+ saveBtn.addEventListener('click', () => {
927
+ const cli = document.querySelector('[data-cli-selector]');
928
+ const agent = document.querySelector('[data-agent-selector]');
929
+ const model = document.querySelector('[data-model-selector]');
930
+ const cliVal = cli?.value || '';
931
+ const agentVal = agent?.value || '';
932
+ const modelVal = model?.value || '';
933
+ if (!cliVal) return;
934
+ const parts = [cliVal.split('-').pop()];
935
+ if (agentVal) parts.push(agentVal.split('/').pop());
936
+ if (modelVal) parts.push(modelVal.split('/').pop().split('-')[0]);
937
+ const name = prompt('Preset name:', parts.join(' / '));
938
+ if (!name) return;
939
+ const presets = getPresets();
940
+ presets.push({ name, cli: cliVal, agent: agentVal, model: modelVal });
941
+ savePresets(presets);
942
+ renderSelector();
943
+ });
944
+
945
+ selector.addEventListener('change', () => {
946
+ const val = selector.value;
947
+ if (val === '__clear') { savePresets([]); renderSelector(); return; }
948
+ if (!val) return;
949
+ const presets = getPresets();
950
+ const preset = presets[parseInt(val)];
951
+ if (!preset) return;
952
+ const cli = document.querySelector('[data-cli-selector]');
953
+ const agent = document.querySelector('[data-agent-selector]');
954
+ const model = document.querySelector('[data-model-selector]');
955
+ if (cli && preset.cli) { cli.value = preset.cli; cli.dispatchEvent(new Event('change')); }
956
+ setTimeout(() => {
957
+ if (agent && preset.agent) { agent.value = preset.agent; agent.dispatchEvent(new Event('change')); }
958
+ setTimeout(() => {
959
+ if (model && preset.model) { model.value = preset.model; model.dispatchEvent(new Event('change')); }
960
+ }, 500);
961
+ }, 500);
962
+ selector.value = '';
963
+ });
964
+
965
+ renderSelector();
966
+ })();
967
+
906
968
  window.addEventListener('load', initializeApp);
@@ -275,6 +275,18 @@
275
275
  overflow: hidden;
276
276
  }
277
277
 
278
+ .conversation-item-checkbox {
279
+ flex-shrink: 0;
280
+ width: 16px;
281
+ height: 16px;
282
+ opacity: 0;
283
+ cursor: pointer;
284
+ margin: 0;
285
+ }
286
+ .conversation-item:hover .conversation-item-checkbox,
287
+ .conversation-item-checkbox:checked {
288
+ opacity: 1;
289
+ }
278
290
  .conversation-item-delete,
279
291
  .conversation-item-archive,
280
292
  .conversation-item-export {
@@ -3182,4 +3194,25 @@
3182
3194
  from { transform: translateX(100%); opacity: 0; }
3183
3195
  to { transform: translateX(0); opacity: 1; }
3184
3196
  }
3197
+
3198
+ .preset-btn {
3199
+ background: transparent;
3200
+ border: 1px solid var(--color-border);
3201
+ border-radius: 0.25rem;
3202
+ cursor: pointer;
3203
+ color: var(--color-text-secondary);
3204
+ font-size: 0.875rem;
3205
+ padding: 0.25rem 0.5rem;
3206
+ line-height: 1;
3207
+ }
3208
+ .preset-btn:hover { color: #f59e0b; }
3209
+ .preset-selector {
3210
+ font-size: 0.75rem;
3211
+ padding: 0.25rem;
3212
+ border: 1px solid var(--color-border);
3213
+ border-radius: 0.25rem;
3214
+ background: var(--color-bg);
3215
+ color: var(--color-text);
3216
+ max-width: 120px;
3217
+ }
3185
3218
 
package/static/index.html CHANGED
@@ -49,6 +49,11 @@
49
49
  <button class="clone-go-btn" id="cloneGoBtn" title="Clone">Go</button>
50
50
  <button class="clone-cancel-btn" id="cloneCancelBtn" title="Cancel">&times;</button>
51
51
  </div>
52
+ <div class="bulk-action-bar" id="bulkActionBar" style="display:none;padding:0.375rem 0.75rem;background:var(--color-primary);display:none;gap:0.5rem;align-items:center;">
53
+ <span id="bulkCount" style="color:white;font-size:0.75rem;flex:1;">0 selected</span>
54
+ <button id="bulkArchiveBtn" style="background:rgba(255,255,255,0.2);color:white;border:none;border-radius:0.25rem;padding:0.25rem 0.5rem;font-size:0.75rem;cursor:pointer;">Archive</button>
55
+ <button id="bulkDeleteBtn" style="background:rgba(255,255,255,0.2);color:white;border:none;border-radius:0.25rem;padding:0.25rem 0.5rem;font-size:0.75rem;cursor:pointer;">Delete</button>
56
+ </div>
52
57
  <ul class="sidebar-list" data-conversation-list role="listbox" aria-label="Conversations">
53
58
  <li class="sidebar-empty" data-conversation-empty>Loading...</li>
54
59
  </ul>
@@ -183,6 +188,10 @@
183
188
  <select class="agent-selector cli-selector" data-cli-selector title="Select CLI tool"></select>
184
189
  <select class="agent-selector sub-agent-selector" data-agent-selector title="Select sub-agent" style="display:none"></select>
185
190
  <select class="model-selector" data-model-selector title="Select model" data-empty="true"></select>
191
+ <div class="preset-controls" style="display:inline-flex;gap:0.25rem;margin-left:0.25rem;">
192
+ <select id="presetSelector" class="preset-selector" title="Load preset" style="display:none;"></select>
193
+ <button id="savePresetBtn" class="preset-btn" title="Save current agent/model as preset">&#9733;</button>
194
+ </div>
186
195
  <div class="message-input-container">
187
196
  <textarea
188
197
  class="message-textarea"
@@ -116,6 +116,19 @@ class ConversationManager {
116
116
  }
117
117
 
118
118
  setupDelegatedListeners() {
119
+ const bulkBar = document.getElementById('bulkActionBar');
120
+ const bulkCount = document.getElementById('bulkCount');
121
+ const updateBulkBar = () => {
122
+ const checked = this.getSelectedIds().length;
123
+ if (bulkBar) bulkBar.style.display = checked > 0 ? 'flex' : 'none';
124
+ if (bulkCount) bulkCount.textContent = `${checked} selected`;
125
+ };
126
+ this.listEl.addEventListener('change', (e) => {
127
+ if (e.target.matches('.conversation-item-checkbox')) updateBulkBar();
128
+ });
129
+ document.getElementById('bulkArchiveBtn')?.addEventListener('click', () => this.bulkArchive());
130
+ document.getElementById('bulkDeleteBtn')?.addEventListener('click', () => this.bulkDelete());
131
+
119
132
  this.listEl.addEventListener('click', (e) => {
120
133
  const exportBtn = e.target.closest('[data-export-conv]');
121
134
  if (exportBtn) {
@@ -489,6 +502,7 @@ class ConversationManager {
489
502
  h('div', { class: 'conversation-item-title' }, ...(badge ? [badge, title] : [title])),
490
503
  h('div', { class: 'conversation-item-meta' }, metaParts.join(' \u2022 '))
491
504
  ),
505
+ h('input', { type: 'checkbox', class: 'conversation-item-checkbox', 'data-bulk-check': conv.id, title: 'Select for bulk action', onclick: 'event.stopPropagation()' }),
492
506
  h('button', { class: 'conversation-item-export', title: 'Export as markdown', 'data-export-conv': conv.id },
493
507
  h('svg', { width: '14', height: '14', viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', 'stroke-width': '2' },
494
508
  h('path', { d: 'M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4' }),
@@ -610,6 +624,30 @@ class ConversationManager {
610
624
  this.render();
611
625
  }
612
626
 
627
+ getSelectedIds() {
628
+ return [...this.listEl.querySelectorAll('.conversation-item-checkbox:checked')].map(cb => cb.dataset.bulkCheck);
629
+ }
630
+
631
+ async bulkArchive() {
632
+ const ids = this.getSelectedIds();
633
+ if (ids.length === 0) return;
634
+ if (!await window.UIDialog?.confirm(`Archive ${ids.length} conversation(s)?`, 'Bulk Archive')) return;
635
+ const base = window.__BASE_URL || '';
636
+ for (const id of ids) {
637
+ try { await fetch(`${base}/api/conversations/${id}/archive`, { method: 'POST' }); } catch (_) {}
638
+ this.deleteConversation(id);
639
+ }
640
+ }
641
+
642
+ async bulkDelete() {
643
+ const ids = this.getSelectedIds();
644
+ if (ids.length === 0) return;
645
+ if (!await window.UIDialog?.confirm(`Permanently delete ${ids.length} conversation(s)?`, 'Bulk Delete')) return;
646
+ for (const id of ids) {
647
+ this.confirmDelete(id, '');
648
+ }
649
+ }
650
+
613
651
  async exportConversation(convId) {
614
652
  try {
615
653
  const result = await window.wsClient.rpc('conv.export', { id: convId, format: 'markdown' });