agentgui 1.0.773 → 1.0.775

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/database.js CHANGED
@@ -422,7 +422,8 @@ try {
422
422
  model: 'TEXT',
423
423
  subAgent: 'TEXT',
424
424
  pinned: 'INTEGER DEFAULT 0',
425
- tags: 'TEXT'
425
+ tags: 'TEXT',
426
+ sortOrder: 'INTEGER DEFAULT 0'
426
427
  };
427
428
 
428
429
  let addedColumns = false;
@@ -625,8 +626,8 @@ function generateId(prefix) {
625
626
  return `${prefix}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
626
627
  }
627
628
 
628
- import { createQueries } from './lib/db-queries.js';
629
-
630
- export const queries = createQueries(db, prep, generateId);
629
+ import { createQueries } from './lib/db-queries.js';
630
+
631
+ export const queries = createQueries(db, prep, generateId);
631
632
 
632
633
  export default { queries };
package/lib/db-queries.js CHANGED
@@ -36,16 +36,20 @@ export function createQueries(db, prep, generateId) {
36
36
 
37
37
  getConversationsList() {
38
38
  const stmt = prep(
39
- 'SELECT id, agentId, title, agentType, created_at, updated_at, messageCount, workingDirectory, isStreaming, model, subAgent, pinned FROM conversations WHERE status NOT IN (?, ?) ORDER BY pinned DESC, updated_at DESC'
39
+ 'SELECT id, agentId, title, agentType, created_at, updated_at, messageCount, workingDirectory, isStreaming, model, subAgent, pinned, sortOrder FROM conversations WHERE status NOT IN (?, ?) ORDER BY pinned DESC, sortOrder ASC, updated_at DESC'
40
40
  );
41
41
  return stmt.all('deleted', 'archived');
42
42
  },
43
43
 
44
44
  getConversations() {
45
- const stmt = prep('SELECT * FROM conversations WHERE status NOT IN (?, ?) ORDER BY pinned DESC, updated_at DESC');
45
+ const stmt = prep('SELECT * FROM conversations WHERE status NOT IN (?, ?) ORDER BY pinned DESC, sortOrder ASC, updated_at DESC');
46
46
  return stmt.all('deleted', 'archived');
47
47
  },
48
48
 
49
+ updateConversationSortOrder(id, sortOrder) {
50
+ prep('UPDATE conversations SET sortOrder = ? WHERE id = ?').run(sortOrder, id);
51
+ },
52
+
49
53
  getArchivedConversations() {
50
54
  const stmt = prep('SELECT id, agentId, title, agentType, created_at, updated_at, messageCount, workingDirectory, model, subAgent FROM conversations WHERE status = ? ORDER BY updated_at DESC');
51
55
  return stmt.all('archived');
@@ -125,6 +125,14 @@ export function register(router, deps) {
125
125
  return { ok: true, chunks: result.chunks, total: result.total, hasMore: result.hasMore, limit: result.limit };
126
126
  });
127
127
 
128
+ router.handle('conv.reorder', (p) => {
129
+ if (!Array.isArray(p.order)) throw Object.assign(new Error('order array required'), { code: 400 });
130
+ for (let i = 0; i < p.order.length; i++) {
131
+ queries.updateConversationSortOrder(p.order[i], i);
132
+ }
133
+ return { ok: true };
134
+ });
135
+
128
136
  router.handle('conv.export', (p) => {
129
137
  const conv = queries.getConversation(p.id);
130
138
  if (!conv) notFound();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentgui",
3
- "version": "1.0.773",
3
+ "version": "1.0.775",
4
4
  "description": "Multi-agent ACP client with real-time communication",
5
5
  "type": "module",
6
6
  "main": "electron/main.js",
@@ -275,6 +275,8 @@
275
275
  overflow: hidden;
276
276
  }
277
277
 
278
+ .conversation-item.pinned { cursor: grab; }
279
+ .conversation-item.pinned:active { cursor: grabbing; }
278
280
  .conversation-item-checkbox {
279
281
  flex-shrink: 0;
280
282
  width: 16px;
@@ -3195,6 +3197,20 @@
3195
3197
  to { transform: translateX(0); opacity: 1; }
3196
3198
  }
3197
3199
 
3200
+ .msg-edit-btn {
3201
+ background: transparent;
3202
+ border: none;
3203
+ cursor: pointer;
3204
+ color: var(--color-text-secondary);
3205
+ font-size: 0.75rem;
3206
+ padding: 0 0.25rem;
3207
+ opacity: 0;
3208
+ transition: opacity 0.15s;
3209
+ margin-left: 0.5rem;
3210
+ }
3211
+ .message-user:hover .msg-edit-btn { opacity: 0.7; }
3212
+ .msg-edit-btn:hover { opacity: 1 !important; color: var(--color-primary); }
3213
+
3198
3214
  .preset-btn {
3199
3215
  background: transparent;
3200
3216
  border: 1px solid var(--color-border);
@@ -618,7 +618,21 @@ class AgentGUIClient {
618
618
  themeToggle.addEventListener('click', () => this.toggleTheme());
619
619
  }
620
620
 
621
- // Setup scroll position tracking for current conversation
621
+ if (this.ui.outputEl) {
622
+ this.ui.outputEl.addEventListener('click', async (e) => {
623
+ const editBtn = e.target.closest('[data-edit-msg]');
624
+ if (!editBtn || !this.state.currentConversation) return;
625
+ const msgEl = editBtn.closest('.message-user');
626
+ const textEl = msgEl?.querySelector('.message-text');
627
+ if (!textEl) return;
628
+ const original = textEl.textContent || '';
629
+ const edited = await window.UIDialog?.prompt('Edit message:', original, 'Edit & Re-run');
630
+ if (!edited || edited === original) return;
631
+ this.ui.messageInput.value = edited;
632
+ this.startExecution();
633
+ });
634
+ }
635
+
622
636
  this.setupScrollTracking();
623
637
 
624
638
  window.addEventListener('create-new-conversation', (event) => {
@@ -2164,7 +2178,7 @@ class AgentGUIClient {
2164
2178
  const uDiv = document.createElement('div');
2165
2179
  uDiv.className = 'message message-user';
2166
2180
  uDiv.setAttribute('data-msg-id', m.id);
2167
- uDiv.innerHTML = `<div class="message-role">User</div>${this.renderMessageContent(m.content)}<div class="message-timestamp">${new Date(m.created_at).toLocaleString()}</div>`;
2181
+ uDiv.innerHTML = `<div class="message-role">User<button class="msg-edit-btn" data-edit-msg="${m.id}" title="Edit and re-run">&#9998;</button></div>${this.renderMessageContent(m.content)}<div class="message-timestamp">${new Date(m.created_at).toLocaleString()}</div>`;
2168
2182
  frag.appendChild(uDiv);
2169
2183
  }
2170
2184
  const isActive = sid === activeSessionId;
@@ -116,6 +116,35 @@ class ConversationManager {
116
116
  }
117
117
 
118
118
  setupDelegatedListeners() {
119
+ let draggedId = null;
120
+ this.listEl.addEventListener('dragstart', (e) => {
121
+ const item = e.target.closest('[data-drag-conv]');
122
+ if (!item) return;
123
+ draggedId = item.dataset.dragConv;
124
+ item.style.opacity = '0.5';
125
+ e.dataTransfer.effectAllowed = 'move';
126
+ });
127
+ this.listEl.addEventListener('dragend', (e) => {
128
+ const item = e.target.closest('[data-drag-conv]');
129
+ if (item) item.style.opacity = '';
130
+ draggedId = null;
131
+ });
132
+ this.listEl.addEventListener('dragover', (e) => {
133
+ const item = e.target.closest('[data-drag-conv]');
134
+ if (item && draggedId) { e.preventDefault(); e.dataTransfer.dropEffect = 'move'; }
135
+ });
136
+ this.listEl.addEventListener('drop', (e) => {
137
+ e.preventDefault();
138
+ const target = e.target.closest('[data-drag-conv]');
139
+ if (!target || !draggedId || target.dataset.dragConv === draggedId) return;
140
+ const pinnedItems = [...this.listEl.querySelectorAll('[data-drag-conv]')];
141
+ const draggedEl = pinnedItems.find(el => el.dataset.dragConv === draggedId);
142
+ if (!draggedEl) return;
143
+ this.listEl.insertBefore(draggedEl, target);
144
+ const newOrder = [...this.listEl.querySelectorAll('[data-drag-conv]')].map(el => el.dataset.dragConv);
145
+ window.wsClient?.rpc('conv.reorder', { order: newOrder }).catch(() => {});
146
+ });
147
+
119
148
  const bulkBar = document.getElementById('bulkActionBar');
120
149
  const bulkCount = document.getElementById('bulkCount');
121
150
  const updateBulkBar = () => {
@@ -497,7 +526,8 @@ class ConversationManager {
497
526
  const badge = isStreaming
498
527
  ? h('span', { class: 'conversation-streaming-badge', title: 'Streaming in progress' }, h('span', { class: 'streaming-dot' }))
499
528
  : null;
500
- return h('li', { class: 'conversation-item' + (isActive ? ' active' : ''), 'data-conv-id': conv.id },
529
+ const dragAttrs = conv.pinned ? { draggable: 'true', 'data-drag-conv': conv.id } : {};
530
+ return h('li', { class: 'conversation-item' + (isActive ? ' active' : '') + (conv.pinned ? ' pinned' : ''), 'data-conv-id': conv.id, ...dragAttrs },
501
531
  h('div', { class: 'conversation-item-content' },
502
532
  h('div', { class: 'conversation-item-title' }, ...(badge ? [badge, title] : [title])),
503
533
  h('div', { class: 'conversation-item-meta' }, metaParts.join(' \u2022 '))