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 +5 -4
- package/lib/db-queries.js +6 -2
- package/lib/ws-handlers-conv.js +8 -0
- package/package.json +1 -1
- package/static/css/main.css +16 -0
- package/static/js/client.js +16 -2
- package/static/js/conversations.js +31 -1
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');
|
package/lib/ws-handlers-conv.js
CHANGED
|
@@ -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
package/static/css/main.css
CHANGED
|
@@ -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);
|
package/static/js/client.js
CHANGED
|
@@ -618,7 +618,21 @@ class AgentGUIClient {
|
|
|
618
618
|
themeToggle.addEventListener('click', () => this.toggleTheme());
|
|
619
619
|
}
|
|
620
620
|
|
|
621
|
-
|
|
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
|
|
2181
|
+
uDiv.innerHTML = `<div class="message-role">User<button class="msg-edit-btn" data-edit-msg="${m.id}" title="Edit and re-run">✎</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
|
-
|
|
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 '))
|