agentgui 1.0.930 → 1.0.932
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/AGENTS.md +35 -12
- package/database.js +31 -2
- package/lib/http-handler.js +11 -25
- package/lib/routes-registry.js +4 -48
- package/lib/server-startup.js +3 -11
- package/lib/ws-setup.js +2 -1
- package/package.json +3 -3
- package/server.js +7 -1
- package/site/app/js/app.js +64 -68
- package/site/app/js/backend.js +1 -1
- package/static/lib/xstate.umd.min.js +1 -1
- package/lib/db-queries-chunks.js +0 -195
- package/lib/db-queries-chunks2.js +0 -82
- package/lib/db-queries-cleanup.js +0 -74
- package/lib/db-queries-del.js +0 -141
- package/lib/db-queries-events.js +0 -68
- package/lib/db-queries-import.js +0 -133
- package/lib/db-queries-messages.js +0 -102
- package/lib/db-queries-sessions.js +0 -112
- package/lib/db-queries-streams.js +0 -100
- package/lib/db-queries.js +0 -89
- package/lib/jsonl-parser.js +0 -190
- package/lib/jsonl-watcher.js +0 -64
- package/lib/routes-agent-actions.js +0 -61
- package/lib/routes-auth-config.js +0 -30
- package/lib/routes-conversations.js +0 -96
- package/lib/routes-debug.js +0 -119
- package/lib/routes-messages.js +0 -139
- package/lib/routes-runs.js +0 -156
- package/lib/routes-scripts.js +0 -135
- package/lib/routes-sessions.js +0 -144
- package/lib/routes-threads.js +0 -100
- package/lib/routes-util.js +0 -110
- package/lib/ws-handlers-conv.js +0 -138
- package/lib/ws-handlers-conv2.js +0 -169
- package/lib/ws-handlers-msg.js +0 -121
- package/lib/ws-handlers-queue.js +0 -56
- package/lib/ws-handlers-run.js +0 -182
- package/lib/ws-handlers-scripts.js +0 -66
- package/lib/ws-handlers-session.js +0 -105
- package/lib/ws-handlers-session2.js +0 -85
- package/lib/ws-legacy-handlers.js +0 -51
- package/static/app.js +0 -261
- package/static/css/app-shell.css +0 -419
- package/static/css/brand-bible.css +0 -591
- package/static/css/colors_and_type.css +0 -568
- package/static/css/gmail-skin.css +0 -663
- package/static/css/main.css +0 -4015
- package/static/css/tools-popup.css +0 -472
- package/static/index.html +0 -418
- package/static/js/agent-auth.js +0 -146
- package/static/js/app-shortcuts.js +0 -30
- package/static/js/audio-recorder-processor.js +0 -18
- package/static/js/client-agents.js +0 -155
- package/static/js/client-cache.js +0 -171
- package/static/js/client-conv.js +0 -198
- package/static/js/client-events.js +0 -164
- package/static/js/client-exec.js +0 -160
- package/static/js/client-helpers.js +0 -199
- package/static/js/client-load.js +0 -175
- package/static/js/client-render.js +0 -132
- package/static/js/client-scroll.js +0 -178
- package/static/js/client-status.js +0 -167
- package/static/js/client-streaming.js +0 -117
- package/static/js/client-streaming2.js +0 -116
- package/static/js/client-streaming3.js +0 -153
- package/static/js/client-streaming4.js +0 -194
- package/static/js/client-ui-controls.js +0 -170
- package/static/js/client-ui.js +0 -128
- package/static/js/client-ui2.js +0 -160
- package/static/js/client-url.js +0 -93
- package/static/js/client-utils.js +0 -174
- package/static/js/client-ws-msg.js +0 -88
- package/static/js/client-ws.js +0 -161
- package/static/js/client.js +0 -145
- package/static/js/codec.js +0 -4
- package/static/js/conv-list-machine.js +0 -145
- package/static/js/conv-list-renderer.js +0 -198
- package/static/js/conv-machine.js +0 -110
- package/static/js/conv-sidebar-actions.js +0 -188
- package/static/js/conv-sidebar-clone.js +0 -91
- package/static/js/conversations.js +0 -116
- package/static/js/dialogs-types.js +0 -111
- package/static/js/dialogs.js +0 -53
- package/static/js/event-filter-config.js +0 -36
- package/static/js/event-processor.js +0 -181
- package/static/js/features.js +0 -187
- package/static/js/image-loader-element.js +0 -76
- package/static/js/image-loader.js +0 -146
- package/static/js/prompt-machine.js +0 -108
- package/static/js/recording-machine.js +0 -49
- package/static/js/script-runner.js +0 -192
- package/static/js/state-barrier.js +0 -105
- package/static/js/streaming-renderer-dispatch.js +0 -144
- package/static/js/streaming-renderer-events.js +0 -163
- package/static/js/streaming-renderer-events2.js +0 -125
- package/static/js/streaming-renderer-params.js +0 -38
- package/static/js/streaming-renderer-render-misc.js +0 -107
- package/static/js/streaming-renderer-render.js +0 -181
- package/static/js/streaming-renderer-render2.js +0 -149
- package/static/js/streaming-renderer-render3.js +0 -142
- package/static/js/streaming-renderer-static.js +0 -181
- package/static/js/streaming-renderer-static2.js +0 -140
- package/static/js/streaming-renderer-stream.js +0 -170
- package/static/js/streaming-renderer-text.js +0 -185
- package/static/js/streaming-renderer-tools.js +0 -189
- package/static/js/streaming-renderer-tools2.js +0 -92
- package/static/js/streaming-renderer.js +0 -200
- package/static/js/syntax-highlighter-render.js +0 -72
- package/static/js/syntax-highlighter.js +0 -131
- package/static/js/terminal-machine.js +0 -51
- package/static/js/terminal.js +0 -178
- package/static/js/ui-components-rendering.js +0 -62
- package/static/js/ui-components.js +0 -88
- package/static/js/websocket-manager.js +0 -107
- package/static/js/ws-client.js +0 -87
- package/static/js/ws-core.js +0 -162
- package/static/js/ws-latency.js +0 -88
- package/static/js/ws-machine.js +0 -68
- package/static/lib/msgpackr.min.js +0 -2
- package/static/theme.js +0 -74
- package/static/vendor/highlight-js.css +0 -10
- package/static/vendor/highlight.min.js +0 -1244
- package/static/vendor/prism-dark.css +0 -129
- package/static/vendor/rippleui.css +0 -35
- package/static/vendor/xterm-addon-fit.min.js +0 -8
- package/static/vendor/xterm.css +0 -8
- package/static/vendor/xterm.min.js +0 -8
|
@@ -1,188 +0,0 @@
|
|
|
1
|
-
Object.assign(ConversationManager.prototype, {
|
|
2
|
-
setupDelegatedListeners() {
|
|
3
|
-
let draggedId = null;
|
|
4
|
-
this.listEl.addEventListener('dragstart', (e) => {
|
|
5
|
-
const item = e.target.closest('[data-drag-conv]');
|
|
6
|
-
if (!item) return;
|
|
7
|
-
draggedId = item.dataset.dragConv;
|
|
8
|
-
item.style.opacity = '0.5';
|
|
9
|
-
e.dataTransfer.effectAllowed = 'move';
|
|
10
|
-
});
|
|
11
|
-
this.listEl.addEventListener('dragend', (e) => {
|
|
12
|
-
const item = e.target.closest('[data-drag-conv]');
|
|
13
|
-
if (item) item.style.opacity = '';
|
|
14
|
-
draggedId = null;
|
|
15
|
-
});
|
|
16
|
-
this.listEl.addEventListener('dragover', (e) => {
|
|
17
|
-
const item = e.target.closest('[data-drag-conv]');
|
|
18
|
-
if (item && draggedId) { e.preventDefault(); e.dataTransfer.dropEffect = 'move'; }
|
|
19
|
-
});
|
|
20
|
-
this.listEl.addEventListener('drop', (e) => {
|
|
21
|
-
e.preventDefault();
|
|
22
|
-
const target = e.target.closest('[data-drag-conv]');
|
|
23
|
-
if (!target || !draggedId || target.dataset.dragConv === draggedId) return;
|
|
24
|
-
const pinnedItems = [...this.listEl.querySelectorAll('[data-drag-conv]')];
|
|
25
|
-
const draggedEl = pinnedItems.find(el => el.dataset.dragConv === draggedId);
|
|
26
|
-
if (!draggedEl) return;
|
|
27
|
-
this.listEl.insertBefore(draggedEl, target);
|
|
28
|
-
const newOrder = [...this.listEl.querySelectorAll('[data-drag-conv]')].map(el => el.dataset.dragConv);
|
|
29
|
-
window.wsClient?.rpc('conv.reorder', { order: newOrder }).catch(() => {});
|
|
30
|
-
});
|
|
31
|
-
const bulkBar = document.getElementById('bulkActionBar');
|
|
32
|
-
const bulkCount = document.getElementById('bulkCount');
|
|
33
|
-
const updateBulkBar = () => {
|
|
34
|
-
const checked = this.getSelectedIds().length;
|
|
35
|
-
if (bulkBar) bulkBar.style.display = checked > 0 ? 'flex' : 'none';
|
|
36
|
-
if (bulkCount) bulkCount.textContent = `${checked} selected`;
|
|
37
|
-
};
|
|
38
|
-
this.listEl.addEventListener('change', (e) => {
|
|
39
|
-
if (e.target.matches('.conversation-item-checkbox')) updateBulkBar();
|
|
40
|
-
});
|
|
41
|
-
document.getElementById('bulkArchiveBtn')?.addEventListener('click', () => this.bulkArchive());
|
|
42
|
-
document.getElementById('bulkDeleteBtn')?.addEventListener('click', () => this.bulkDelete());
|
|
43
|
-
this.listEl.addEventListener('click', (e) => {
|
|
44
|
-
const exportBtn = e.target.closest('[data-export-conv]');
|
|
45
|
-
if (exportBtn) { e.stopPropagation(); this.exportConversation(exportBtn.dataset.exportConv); return; }
|
|
46
|
-
const archiveBtn = e.target.closest('[data-archive-conv]');
|
|
47
|
-
if (archiveBtn) { e.stopPropagation(); this.archiveConversation(archiveBtn.dataset.archiveConv); return; }
|
|
48
|
-
const deleteBtn = e.target.closest('[data-delete-conv]');
|
|
49
|
-
if (deleteBtn) {
|
|
50
|
-
e.stopPropagation();
|
|
51
|
-
const convId = deleteBtn.dataset.deleteConv;
|
|
52
|
-
const conv = this.conversations.find(c => c.id === convId);
|
|
53
|
-
this.confirmDelete(convId, conv?.title || 'Untitled');
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
const item = e.target.closest('[data-conv-id]');
|
|
57
|
-
if (item) this.select(item.dataset.convId);
|
|
58
|
-
});
|
|
59
|
-
},
|
|
60
|
-
|
|
61
|
-
setupFolderBrowser() {
|
|
62
|
-
this.folderBrowser.modal = document.getElementById('folderBrowserModal');
|
|
63
|
-
this.folderBrowser.listEl = document.getElementById('folderList');
|
|
64
|
-
this.folderBrowser.breadcrumbEl = document.getElementById('folderBreadcrumb');
|
|
65
|
-
if (!this.folderBrowser.modal) return;
|
|
66
|
-
const closeBtn = this.folderBrowser.modal.querySelector('[data-folder-close]');
|
|
67
|
-
const cancelBtn = this.folderBrowser.modal.querySelector('[data-folder-cancel]');
|
|
68
|
-
const selectBtn = this.folderBrowser.modal.querySelector('[data-folder-select]');
|
|
69
|
-
closeBtn?.addEventListener('click', () => this.closeFolderBrowser());
|
|
70
|
-
cancelBtn?.addEventListener('click', () => this.closeFolderBrowser());
|
|
71
|
-
selectBtn?.addEventListener('click', () => this.confirmFolderSelection());
|
|
72
|
-
this.folderBrowser.modal.addEventListener('click', (e) => {
|
|
73
|
-
if (e.target === this.folderBrowser.modal) this.closeFolderBrowser();
|
|
74
|
-
});
|
|
75
|
-
this.folderBrowser.homePathReady = this.fetchHomePath();
|
|
76
|
-
},
|
|
77
|
-
|
|
78
|
-
async fetchHomePath() {
|
|
79
|
-
try {
|
|
80
|
-
const res = await fetch(`${window.__BASE_URL || '/gm'}/api/home`);
|
|
81
|
-
const data = await res.json();
|
|
82
|
-
this.folderBrowser.homePath = data.home || '~';
|
|
83
|
-
this.folderBrowser.cwdPath = data.cwd || null;
|
|
84
|
-
} catch (e) { console.error('Failed to fetch home path:', e); }
|
|
85
|
-
},
|
|
86
|
-
|
|
87
|
-
async openFolderBrowser() {
|
|
88
|
-
window.dispatchEvent(new CustomEvent('preparing-new-conversation'));
|
|
89
|
-
if (!this.folderBrowser.modal) { this.createNew(); return; }
|
|
90
|
-
if (this.folderBrowser.homePathReady) await this.folderBrowser.homePathReady;
|
|
91
|
-
const startPath = this.folderBrowser.cwdPath || '~';
|
|
92
|
-
this.folderBrowser.currentPath = startPath;
|
|
93
|
-
this.folderBrowser.modal.classList.add('visible');
|
|
94
|
-
this.folderBrowser.modal.classList.add('open');
|
|
95
|
-
this.loadFolders(startPath);
|
|
96
|
-
},
|
|
97
|
-
|
|
98
|
-
closeFolderBrowser() {
|
|
99
|
-
this.folderBrowser.modal?.classList.remove('visible');
|
|
100
|
-
this.folderBrowser.modal?.classList.remove('open');
|
|
101
|
-
},
|
|
102
|
-
|
|
103
|
-
async loadFolders(dirPath) {
|
|
104
|
-
this.folderBrowser.currentPath = dirPath;
|
|
105
|
-
this.renderBreadcrumb(dirPath);
|
|
106
|
-
if (!this.folderBrowser.listEl) return;
|
|
107
|
-
this.folderBrowser.listEl.innerHTML = '<li class="folder-list-loading">Loading...</li>';
|
|
108
|
-
try {
|
|
109
|
-
const data = await window.wsClient.rpc('folders', { path: dirPath });
|
|
110
|
-
const folders = data.folders || [];
|
|
111
|
-
this.folderBrowser.listEl.innerHTML = '';
|
|
112
|
-
const isAtRoot = dirPath === '~' || dirPath === '/' || dirPath === this.folderBrowser.homePath || /^[A-Za-z]:[\/\\]?$/.test(dirPath);
|
|
113
|
-
if (!isAtRoot) {
|
|
114
|
-
const parentPath = this.getParentPath(dirPath);
|
|
115
|
-
const upItem = document.createElement('li');
|
|
116
|
-
upItem.className = 'folder-list-item';
|
|
117
|
-
upItem.innerHTML = '<span class="folder-list-item-icon">..</span><span class="folder-list-item-name">Parent Directory</span>';
|
|
118
|
-
upItem.addEventListener('click', () => this.loadFolders(parentPath));
|
|
119
|
-
this.folderBrowser.listEl.appendChild(upItem);
|
|
120
|
-
}
|
|
121
|
-
if (folders.length === 0 && this.folderBrowser.listEl.children.length === 0) {
|
|
122
|
-
this.folderBrowser.listEl.innerHTML = '<li class="folder-list-empty">No subdirectories</li>';
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
for (const folder of folders) {
|
|
126
|
-
const li = document.createElement('li');
|
|
127
|
-
li.className = 'folder-list-item';
|
|
128
|
-
li.innerHTML = `<span class="folder-list-item-icon">📁</span><span class="folder-list-item-name">${this.escapeHtml(folder.name)}</span>`;
|
|
129
|
-
li.addEventListener('click', () => {
|
|
130
|
-
const expandedBase = dirPath === '~' ? this.folderBrowser.homePath : dirPath;
|
|
131
|
-
const separator = expandedBase.includes('\\') ? '\\' : '/';
|
|
132
|
-
const base = expandedBase.replace(/[\/\\]+$/, '');
|
|
133
|
-
this.loadFolders(base + separator + folder.name);
|
|
134
|
-
});
|
|
135
|
-
this.folderBrowser.listEl.appendChild(li);
|
|
136
|
-
}
|
|
137
|
-
} catch (err) {
|
|
138
|
-
this.folderBrowser.listEl.innerHTML = `<li class="folder-list-error">Error: ${this.escapeHtml(err.message)}</li>`;
|
|
139
|
-
}
|
|
140
|
-
},
|
|
141
|
-
|
|
142
|
-
getParentPath(dirPath) {
|
|
143
|
-
const expanded = dirPath === '~' ? this.folderBrowser.homePath : dirPath;
|
|
144
|
-
const parts = pathSplit(expanded);
|
|
145
|
-
const isWindows = expanded.includes('\\') || /^[A-Za-z]:/.test(expanded);
|
|
146
|
-
const separator = isWindows ? '\\' : '/';
|
|
147
|
-
if (parts.length <= 1) return isWindows && parts[0] && parts[0].endsWith(':') ? parts[0] + separator : separator;
|
|
148
|
-
parts.pop();
|
|
149
|
-
if (isWindows) { const joined = parts.join(separator); return parts.length === 1 && parts[0].endsWith(':') ? joined + separator : joined; }
|
|
150
|
-
return separator + parts.join(separator);
|
|
151
|
-
},
|
|
152
|
-
|
|
153
|
-
renderBreadcrumb(dirPath) {
|
|
154
|
-
if (!this.folderBrowser.breadcrumbEl) return;
|
|
155
|
-
const expanded = dirPath === '~' ? this.folderBrowser.homePath : dirPath;
|
|
156
|
-
const parts = pathSplit(expanded);
|
|
157
|
-
const isWin = expanded.includes('\\') || /^[A-Za-z]:/.test(expanded);
|
|
158
|
-
const separator = isWin ? '\\' : '/';
|
|
159
|
-
const rootPath = isWin && parts[0] && /^[A-Za-z]:$/.test(parts[0]) ? parts[0] + separator : separator;
|
|
160
|
-
let html = `<span class="folder-breadcrumb-segment" data-path="${this.escapeHtml(rootPath)}">${this.escapeHtml(rootPath)} </span>`;
|
|
161
|
-
let accumulated = '';
|
|
162
|
-
const isDriveLetter = isWin && parts[0] && /^[A-Za-z]:$/.test(parts[0]);
|
|
163
|
-
for (let i = 0; i < parts.length; i++) {
|
|
164
|
-
if (i === 0 && isDriveLetter) accumulated = parts[0];
|
|
165
|
-
else accumulated += separator + parts[i];
|
|
166
|
-
const segPath = (i === 0 && isDriveLetter) ? rootPath : accumulated;
|
|
167
|
-
html += `<span class="folder-breadcrumb-separator">${separator}</span>`;
|
|
168
|
-
html += `<span class="folder-breadcrumb-segment" data-path="${this.escapeHtml(segPath)}">${this.escapeHtml(parts[i])}</span>`;
|
|
169
|
-
}
|
|
170
|
-
this.folderBrowser.breadcrumbEl.innerHTML = html;
|
|
171
|
-
this.folderBrowser.breadcrumbEl.querySelectorAll('.folder-breadcrumb-segment').forEach(seg => {
|
|
172
|
-
seg.addEventListener('click', () => { const p = seg.dataset.path; if (p) this.loadFolders(p); });
|
|
173
|
-
});
|
|
174
|
-
},
|
|
175
|
-
|
|
176
|
-
confirmFolderSelection() {
|
|
177
|
-
const currentPath = this.folderBrowser.currentPath;
|
|
178
|
-
const expanded = currentPath === '~' ? this.folderBrowser.homePath : currentPath;
|
|
179
|
-
this.closeFolderBrowser();
|
|
180
|
-
window.dispatchEvent(new CustomEvent('create-new-conversation', { detail: { workingDirectory: expanded, title: pathBasename(expanded) || 'root' } }));
|
|
181
|
-
},
|
|
182
|
-
|
|
183
|
-
showLoading() {
|
|
184
|
-
if (!this.listEl) return;
|
|
185
|
-
this.listEl.innerHTML = '';
|
|
186
|
-
if (this.emptyEl) { this.emptyEl.textContent = 'Loading...'; this.emptyEl.style.display = 'block'; }
|
|
187
|
-
}
|
|
188
|
-
});
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
Object.assign(ConversationManager.prototype, {
|
|
2
|
-
setupDeleteAllButton() {
|
|
3
|
-
this.deleteAllBtn = document.getElementById('deleteAllConversationsBtn');
|
|
4
|
-
if (!this.deleteAllBtn) return;
|
|
5
|
-
this.deleteAllBtn.addEventListener('click', () => this.confirmDeleteAll());
|
|
6
|
-
},
|
|
7
|
-
|
|
8
|
-
async confirmDeleteAll() {
|
|
9
|
-
if (this.conversations.length === 0) { window.UIDialog.alert('No conversations to delete', 'Information'); return; }
|
|
10
|
-
const confirmed = await window.UIDialog.confirm(`Delete all ${this.conversations.length} conversation(s) and associated Claude Code artifacts?\n\nThis action cannot be undone.`, 'Delete All Conversations');
|
|
11
|
-
if (!confirmed) return;
|
|
12
|
-
try {
|
|
13
|
-
this.deleteAllBtn.disabled = true;
|
|
14
|
-
await window.wsClient.rpc('conv.del.all', {});
|
|
15
|
-
if (window.convListMachineAPI) window.convListMachineAPI.send({ type: 'CLEAR_ALL' });
|
|
16
|
-
window.ConversationState?.clear('delete_all');
|
|
17
|
-
window.dispatchEvent(new CustomEvent('conversation-deselected'));
|
|
18
|
-
this.render();
|
|
19
|
-
} catch (err) {
|
|
20
|
-
console.error('[ConversationManager] Delete all error:', err);
|
|
21
|
-
window.UIDialog.alert('Failed to delete all conversations: ' + (err.message || 'Unknown error'), 'Error');
|
|
22
|
-
} finally { this.deleteAllBtn.disabled = false; }
|
|
23
|
-
},
|
|
24
|
-
|
|
25
|
-
setupCloneUI() {
|
|
26
|
-
this.cloneBtn = document.getElementById('cloneRepoBtn');
|
|
27
|
-
this.cloneBar = document.getElementById('cloneInputBar');
|
|
28
|
-
this.cloneInput = document.getElementById('cloneRepoInput');
|
|
29
|
-
this.cloneGoBtn = document.getElementById('cloneGoBtn');
|
|
30
|
-
this.cloneCancelBtn = document.getElementById('cloneCancelBtn');
|
|
31
|
-
if (!this.cloneBtn || !this.cloneBar) return;
|
|
32
|
-
this.cloneBtn.addEventListener('click', () => this.toggleCloneBar());
|
|
33
|
-
this.cloneCancelBtn?.addEventListener('click', () => this.hideCloneBar());
|
|
34
|
-
this.cloneGoBtn?.addEventListener('click', () => this.performClone());
|
|
35
|
-
this.cloneInput?.addEventListener('keydown', (e) => { if (e.key === 'Enter') this.performClone(); if (e.key === 'Escape') this.hideCloneBar(); });
|
|
36
|
-
},
|
|
37
|
-
|
|
38
|
-
toggleCloneBar() {
|
|
39
|
-
if (!this.cloneBar) return;
|
|
40
|
-
if (this.cloneBar.style.display !== 'none') { this.hideCloneBar(); return; }
|
|
41
|
-
this.cloneBar.style.display = 'flex';
|
|
42
|
-
this.cloneInput.value = '';
|
|
43
|
-
this.cloneInput.focus();
|
|
44
|
-
this.removeCloneStatus();
|
|
45
|
-
},
|
|
46
|
-
|
|
47
|
-
hideCloneBar() {
|
|
48
|
-
if (this.cloneBar) this.cloneBar.style.display = 'none';
|
|
49
|
-
this.removeCloneStatus();
|
|
50
|
-
},
|
|
51
|
-
|
|
52
|
-
removeCloneStatus() {
|
|
53
|
-
const existing = this.sidebarEl?.querySelector('.clone-status');
|
|
54
|
-
if (existing) existing.remove();
|
|
55
|
-
},
|
|
56
|
-
|
|
57
|
-
showCloneStatus(message, type) {
|
|
58
|
-
this.removeCloneStatus();
|
|
59
|
-
const statusEl = document.createElement('div');
|
|
60
|
-
statusEl.className = `clone-status ${type}`;
|
|
61
|
-
statusEl.textContent = message;
|
|
62
|
-
if (this.cloneBar && this.cloneBar.parentNode) this.cloneBar.parentNode.insertBefore(statusEl, this.cloneBar.nextSibling);
|
|
63
|
-
if (type === 'clone-success' || type === 'clone-error') setTimeout(() => statusEl.remove(), 5000);
|
|
64
|
-
},
|
|
65
|
-
|
|
66
|
-
async performClone() {
|
|
67
|
-
const repo = (this.cloneInput?.value || '').trim();
|
|
68
|
-
if (!repo) return;
|
|
69
|
-
if (!/^[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+$/.test(repo)) { this.showCloneStatus('Invalid format. Use org/repo', 'clone-error'); return; }
|
|
70
|
-
this.cloneGoBtn.disabled = true;
|
|
71
|
-
this.cloneInput.disabled = true;
|
|
72
|
-
this.showCloneStatus(`Cloning ${repo}...`, 'cloning');
|
|
73
|
-
try {
|
|
74
|
-
const data = await window.wsClient.rpc('clone', { repo });
|
|
75
|
-
this.showCloneStatus(`Cloned ${data.name}`, 'clone-success');
|
|
76
|
-
this.hideCloneBar();
|
|
77
|
-
window.dispatchEvent(new CustomEvent('create-new-conversation', { detail: { workingDirectory: data.path, title: data.name } }));
|
|
78
|
-
} catch (err) {
|
|
79
|
-
this.showCloneStatus(err.message || 'Clone failed', 'clone-error');
|
|
80
|
-
} finally {
|
|
81
|
-
if (this.cloneGoBtn) this.cloneGoBtn.disabled = false;
|
|
82
|
-
if (this.cloneInput) this.cloneInput.disabled = false;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
if (document.readyState === 'loading') {
|
|
88
|
-
document.addEventListener('DOMContentLoaded', () => { window.conversationManager = new ConversationManager(); });
|
|
89
|
-
} else {
|
|
90
|
-
window.conversationManager = new ConversationManager();
|
|
91
|
-
}
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
function pathSplit(p) {
|
|
2
|
-
return p.split(/[\/\\]/).filter(Boolean);
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
function pathBasename(p) {
|
|
6
|
-
const parts = pathSplit(p);
|
|
7
|
-
return parts.length ? parts.pop() : '';
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
class ConversationManager {
|
|
11
|
-
constructor() {
|
|
12
|
-
this.listEl = document.querySelector('[data-conversation-list]');
|
|
13
|
-
this.emptyEl = document.querySelector('[data-conversation-empty]');
|
|
14
|
-
this.newBtn = document.querySelector('[data-new-conversation]');
|
|
15
|
-
this.sidebarEl = document.querySelector('[data-sidebar]');
|
|
16
|
-
this.agents = new Map();
|
|
17
|
-
|
|
18
|
-
this.folderBrowser = {
|
|
19
|
-
modal: null,
|
|
20
|
-
listEl: null,
|
|
21
|
-
breadcrumbEl: null,
|
|
22
|
-
currentPath: '~',
|
|
23
|
-
homePath: '~',
|
|
24
|
-
cwdPath: null,
|
|
25
|
-
homePathReady: null
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
if (!this.listEl) return;
|
|
29
|
-
|
|
30
|
-
this.init();
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
get conversations() {
|
|
34
|
-
return window.convListMachineAPI ? window.convListMachineAPI.getConversations() : [];
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
get activeId() {
|
|
38
|
-
return window.convListMachineAPI ? window.convListMachineAPI.getActiveId() : null;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
set activeId(id) {
|
|
42
|
-
if (window.convListMachineAPI) window.convListMachineAPI.send({ type: 'SELECT', id });
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
get streamingConversations() {
|
|
46
|
-
const ids = window.convListMachineAPI ? window.convListMachineAPI.getStreamingIds() : [];
|
|
47
|
-
return { has: (id) => ids.includes(id), add: (id) => window.convListMachineAPI?.send({ type: 'SET_STREAMING', id }), delete: (id) => window.convListMachineAPI?.send({ type: 'CLEAR_STREAMING', id }), clear: () => ids.forEach(id => window.convListMachineAPI?.send({ type: 'CLEAR_STREAMING', id })) };
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
async init() {
|
|
51
|
-
this.newBtn?.addEventListener('click', () => this.openFolderBrowser());
|
|
52
|
-
this.setupDelegatedListeners();
|
|
53
|
-
this.showLoading();
|
|
54
|
-
this.setupWebSocketListener();
|
|
55
|
-
this.setupFolderBrowser();
|
|
56
|
-
this.setupCloneUI();
|
|
57
|
-
this.setupDeleteAllButton();
|
|
58
|
-
|
|
59
|
-
await Promise.all([this.loadAgents(), this.loadConversations()]);
|
|
60
|
-
|
|
61
|
-
this._pollInterval = setInterval(() => this.loadConversations(), 30000);
|
|
62
|
-
|
|
63
|
-
window.addEventListener('beforeunload', () => this.destroy());
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
destroy() {
|
|
67
|
-
if (this._pollInterval) {
|
|
68
|
-
clearInterval(this._pollInterval);
|
|
69
|
-
this._pollInterval = null;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
async loadAgents() {
|
|
74
|
-
try {
|
|
75
|
-
const base = window.__BASE_URL || '/gm';
|
|
76
|
-
const res = await fetch(base + '/api/agents');
|
|
77
|
-
if (!res.ok) throw new Error('HTTP ' + res.status);
|
|
78
|
-
const data = await res.json();
|
|
79
|
-
for (const agent of data.agents || []) {
|
|
80
|
-
this.agents.set(agent.id, agent);
|
|
81
|
-
}
|
|
82
|
-
} catch (err) {
|
|
83
|
-
console.error('[ConversationManager] Error loading agents:', err);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
_updateConversations(newArray, source) {
|
|
88
|
-
if (!window.convListMachineAPI) return { version: 0, timestamp: Date.now(), oldLen: 0, newLen: 0 };
|
|
89
|
-
const oldLen = this.conversations.length;
|
|
90
|
-
const newLen = Array.isArray(newArray) ? newArray.length : 0;
|
|
91
|
-
window.convListMachineAPI.send({ type: 'LOAD_DONE', conversations: Array.isArray(newArray) ? newArray : [] });
|
|
92
|
-
const version = window.convListMachineAPI.getContext().version;
|
|
93
|
-
window._conversationCacheVersion = version;
|
|
94
|
-
return { version, timestamp: Date.now(), oldLen, newLen };
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
getConversationCacheVersion() {
|
|
98
|
-
return window.convListMachineAPI ? window.convListMachineAPI.getContext().version : 0;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
getAgentDisplayName(agentId) {
|
|
102
|
-
if (!agentId) return 'Unknown';
|
|
103
|
-
const agent = this.agents.get(agentId);
|
|
104
|
-
return agent?.name || agentId;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
formatModelLabel(model) {
|
|
108
|
-
if (!model) return '';
|
|
109
|
-
return ' (' + model.replace(/^claude-/i, '').replace(/-\d{8,}.*$/, '').replace(/-/g, ' ') + ')';
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
escapeHtml(text) {
|
|
113
|
-
return window._escHtml(text);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
(function() {
|
|
2
|
-
var c = window._dialogCore;
|
|
3
|
-
|
|
4
|
-
window.UIDialog = {
|
|
5
|
-
alert: function(message, title) {
|
|
6
|
-
return new Promise(function(resolve) {
|
|
7
|
-
var overlay = c.createOverlay();
|
|
8
|
-
var dialog = document.createElement('div');
|
|
9
|
-
dialog.className = 'dialog-container';
|
|
10
|
-
dialog.innerHTML =
|
|
11
|
-
'<div class="dialog-box"><div class="dialog-header"><h3 class="dialog-title">' + c.escapeHtml(title || 'Alert') + '</h3></div>' +
|
|
12
|
-
'<div class="dialog-body"><p class="dialog-message">' + c.escapeHtml(message) + '</p></div>' +
|
|
13
|
-
'<div class="dialog-footer"><button class="dialog-btn dialog-btn-primary" data-action="ok">OK</button></div></div>';
|
|
14
|
-
var okBtn = dialog.querySelector('[data-action="ok"]');
|
|
15
|
-
okBtn.addEventListener('click', function() { c.closeDialog(dialog, overlay); resolve(true); });
|
|
16
|
-
overlay.querySelector('.dialog-backdrop').addEventListener('click', function() { c.closeDialog(dialog, overlay); resolve(true); });
|
|
17
|
-
document.addEventListener('keydown', function handler(e) {
|
|
18
|
-
if (e.key === 'Escape' || e.key === 'Enter') { document.removeEventListener('keydown', handler); c.closeDialog(dialog, overlay); resolve(true); }
|
|
19
|
-
});
|
|
20
|
-
c.showDialog(dialog, overlay);
|
|
21
|
-
});
|
|
22
|
-
},
|
|
23
|
-
|
|
24
|
-
confirm: function(message, title) {
|
|
25
|
-
return new Promise(function(resolve) {
|
|
26
|
-
var overlay = c.createOverlay();
|
|
27
|
-
var dialog = document.createElement('div');
|
|
28
|
-
dialog.className = 'dialog-container';
|
|
29
|
-
dialog.innerHTML =
|
|
30
|
-
'<div class="dialog-box"><div class="dialog-header"><h3 class="dialog-title">' + c.escapeHtml(title || 'Confirm') + '</h3></div>' +
|
|
31
|
-
'<div class="dialog-body"><p class="dialog-message">' + c.escapeHtml(message).replace(/\n/g, '<br>') + '</p></div>' +
|
|
32
|
-
'<div class="dialog-footer"><button class="dialog-btn dialog-btn-secondary" data-action="cancel">Cancel</button>' +
|
|
33
|
-
'<button class="dialog-btn dialog-btn-primary dialog-btn-danger" data-action="confirm">Confirm</button></div></div>';
|
|
34
|
-
var cancelBtn = dialog.querySelector('[data-action="cancel"]');
|
|
35
|
-
var confirmBtn = dialog.querySelector('[data-action="confirm"]');
|
|
36
|
-
cancelBtn.addEventListener('click', function() { c.closeDialog(dialog, overlay); resolve(false); });
|
|
37
|
-
confirmBtn.addEventListener('click', function() { c.closeDialog(dialog, overlay); resolve(true); });
|
|
38
|
-
overlay.querySelector('.dialog-backdrop').addEventListener('click', function() { c.closeDialog(dialog, overlay); resolve(false); });
|
|
39
|
-
document.addEventListener('keydown', function handler(e) {
|
|
40
|
-
if (e.key === 'Escape') { document.removeEventListener('keydown', handler); c.closeDialog(dialog, overlay); resolve(false); }
|
|
41
|
-
else if (e.key === 'Enter') { document.removeEventListener('keydown', handler); c.closeDialog(dialog, overlay); resolve(true); }
|
|
42
|
-
});
|
|
43
|
-
c.showDialog(dialog, overlay);
|
|
44
|
-
});
|
|
45
|
-
},
|
|
46
|
-
|
|
47
|
-
prompt: function(message, defaultValue, title) {
|
|
48
|
-
return new Promise(function(resolve) {
|
|
49
|
-
var overlay = c.createOverlay();
|
|
50
|
-
var dialog = document.createElement('div');
|
|
51
|
-
dialog.className = 'dialog-container';
|
|
52
|
-
dialog.innerHTML =
|
|
53
|
-
'<div class="dialog-box"><div class="dialog-header"><h3 class="dialog-title">' + c.escapeHtml(title || 'Input') + '</h3></div>' +
|
|
54
|
-
'<div class="dialog-body"><label class="dialog-label">' + c.escapeHtml(message) + '</label>' +
|
|
55
|
-
'<input type="text" class="dialog-input" value="' + c.escapeHtml(defaultValue || '') + '"></div>' +
|
|
56
|
-
'<div class="dialog-footer"><button class="dialog-btn dialog-btn-secondary" data-action="cancel">Cancel</button>' +
|
|
57
|
-
'<button class="dialog-btn dialog-btn-primary" data-action="ok">OK</button></div></div>';
|
|
58
|
-
var input = dialog.querySelector('.dialog-input');
|
|
59
|
-
var cancelBtn = dialog.querySelector('[data-action="cancel"]');
|
|
60
|
-
var okBtn = dialog.querySelector('[data-action="ok"]');
|
|
61
|
-
cancelBtn.addEventListener('click', function() { c.closeDialog(dialog, overlay); resolve(null); });
|
|
62
|
-
okBtn.addEventListener('click', function() { c.closeDialog(dialog, overlay); resolve(input.value); });
|
|
63
|
-
input.addEventListener('keydown', function(e) { if (e.key === 'Enter') { c.closeDialog(dialog, overlay); resolve(input.value); } });
|
|
64
|
-
overlay.querySelector('.dialog-backdrop').addEventListener('click', function() { c.closeDialog(dialog, overlay); resolve(null); });
|
|
65
|
-
document.addEventListener('keydown', function handler(e) {
|
|
66
|
-
if (e.key === 'Escape') { document.removeEventListener('keydown', handler); c.closeDialog(dialog, overlay); resolve(null); }
|
|
67
|
-
});
|
|
68
|
-
c.showDialog(dialog, overlay);
|
|
69
|
-
});
|
|
70
|
-
},
|
|
71
|
-
|
|
72
|
-
showProgress: function(config) {
|
|
73
|
-
var overlay = c.createOverlay();
|
|
74
|
-
var dialog = document.createElement('div');
|
|
75
|
-
dialog.className = 'dialog-container';
|
|
76
|
-
dialog.innerHTML =
|
|
77
|
-
'<div class="dialog-box dialog-box-progress"><div class="dialog-header"><h3 class="dialog-title">' + c.escapeHtml(config.title || 'Please wait') + '</h3></div>' +
|
|
78
|
-
'<div class="dialog-body"><p class="dialog-message progress-message">' + c.escapeHtml(config.message || 'Loading...') + '</p>' +
|
|
79
|
-
'<div class="dialog-progress-bar"><div class="dialog-progress-fill" style="width: 0%"></div></div>' +
|
|
80
|
-
'<p class="dialog-progress-percent">0%</p></div></div>';
|
|
81
|
-
c.showDialog(dialog, overlay);
|
|
82
|
-
var progressFill = dialog.querySelector('.dialog-progress-fill');
|
|
83
|
-
var progressPercent = dialog.querySelector('.dialog-progress-percent');
|
|
84
|
-
var progressMessage = dialog.querySelector('.progress-message');
|
|
85
|
-
return {
|
|
86
|
-
update: function(percent, message) {
|
|
87
|
-
progressFill.style.width = percent + '%';
|
|
88
|
-
progressPercent.textContent = Math.round(percent) + '%';
|
|
89
|
-
if (message) progressMessage.textContent = message;
|
|
90
|
-
},
|
|
91
|
-
close: function() { c.closeDialog(dialog, overlay); }
|
|
92
|
-
};
|
|
93
|
-
},
|
|
94
|
-
|
|
95
|
-
showToast: function(message, type, duration) {
|
|
96
|
-
var existing = document.querySelector('.toast-notification');
|
|
97
|
-
if (existing) existing.remove();
|
|
98
|
-
var toast = document.createElement('div');
|
|
99
|
-
toast.className = 'toast-notification toast-' + (type || 'info');
|
|
100
|
-
toast.innerHTML = '<span class="toast-message">' + c.escapeHtml(message) + '</span>';
|
|
101
|
-
document.body.appendChild(toast);
|
|
102
|
-
requestAnimationFrame(function() { toast.classList.add('visible'); });
|
|
103
|
-
setTimeout(function() {
|
|
104
|
-
toast.classList.remove('visible');
|
|
105
|
-
setTimeout(function() { if (toast.parentNode) toast.remove(); }, 300);
|
|
106
|
-
}, duration || 3000);
|
|
107
|
-
},
|
|
108
|
-
|
|
109
|
-
closeAll: c.closeAllDialogs
|
|
110
|
-
};
|
|
111
|
-
})();
|
package/static/js/dialogs.js
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
(function() {
|
|
2
|
-
var activeDialogs = [];
|
|
3
|
-
var dialogZIndex = 10000;
|
|
4
|
-
|
|
5
|
-
function escapeHtml(text) {
|
|
6
|
-
if (typeof window._escHtml === 'function') return window._escHtml(text);
|
|
7
|
-
var d = document.createElement('div'); d.textContent = text; return d.innerHTML;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
function createOverlay() {
|
|
11
|
-
var overlay = document.createElement('div');
|
|
12
|
-
overlay.className = 'dialog-overlay';
|
|
13
|
-
overlay.innerHTML = '<div class="dialog-backdrop"></div>';
|
|
14
|
-
return overlay;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function showDialog(dialog, overlay) {
|
|
18
|
-
dialogZIndex++;
|
|
19
|
-
if (overlay) {
|
|
20
|
-
overlay.style.zIndex = dialogZIndex;
|
|
21
|
-
document.body.appendChild(overlay);
|
|
22
|
-
}
|
|
23
|
-
dialog.style.zIndex = dialogZIndex + 1;
|
|
24
|
-
document.body.appendChild(dialog);
|
|
25
|
-
activeDialogs.push({ dialog: dialog, overlay: overlay });
|
|
26
|
-
requestAnimationFrame(function() {
|
|
27
|
-
dialog.classList.add('visible');
|
|
28
|
-
if (overlay) overlay.classList.add('visible');
|
|
29
|
-
var input = dialog.querySelector('input, textarea');
|
|
30
|
-
if (input) input.focus();
|
|
31
|
-
else {
|
|
32
|
-
var btn = dialog.querySelector('.dialog-btn-primary');
|
|
33
|
-
if (btn) btn.focus();
|
|
34
|
-
}
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function closeDialog(dialog, overlay) {
|
|
39
|
-
dialog.classList.remove('visible');
|
|
40
|
-
if (overlay) overlay.classList.remove('visible');
|
|
41
|
-
setTimeout(function() {
|
|
42
|
-
if (dialog.parentNode) dialog.remove();
|
|
43
|
-
if (overlay && overlay.parentNode) overlay.remove();
|
|
44
|
-
}, 200);
|
|
45
|
-
activeDialogs = activeDialogs.filter(function(d) { return d.dialog !== dialog; });
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function closeAllDialogs() {
|
|
49
|
-
activeDialogs.forEach(function(d) { closeDialog(d.dialog, d.overlay); });
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
window._dialogCore = { escapeHtml: escapeHtml, createOverlay: createOverlay, showDialog: showDialog, closeDialog: closeDialog, closeAllDialogs: closeAllDialogs };
|
|
53
|
-
})();
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
window.EVENT_FILTER_CSV_HEADERS = ['timestamp', 'type', 'id', 'sessionId', 'message'];
|
|
3
|
-
|
|
4
|
-
window.exportEventsAsJSON = function(events) {
|
|
5
|
-
return JSON.stringify(events, null, 2);
|
|
6
|
-
};
|
|
7
|
-
|
|
8
|
-
window.exportEventsAsCSV = function(events) {
|
|
9
|
-
const headers = window.EVENT_FILTER_CSV_HEADERS;
|
|
10
|
-
const rows = [headers.join(',')];
|
|
11
|
-
for (const event of events) {
|
|
12
|
-
const row = [
|
|
13
|
-
new Date(event.timestamp || event.trackedAt).toISOString(),
|
|
14
|
-
event.type,
|
|
15
|
-
event.id || '',
|
|
16
|
-
event.sessionId || '',
|
|
17
|
-
JSON.stringify(event.message || event.content || event.text || '')
|
|
18
|
-
];
|
|
19
|
-
rows.push(row.map(v => `"${v}"`).join(','));
|
|
20
|
-
}
|
|
21
|
-
return rows.join('\n');
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
window.exportEventsAsMarkdown = function(events) {
|
|
25
|
-
const lines = ['# Event Export\n'];
|
|
26
|
-
let currentType = null;
|
|
27
|
-
for (const event of events) {
|
|
28
|
-
if (event.type !== currentType) {
|
|
29
|
-
currentType = event.type;
|
|
30
|
-
lines.push(`\n## ${currentType}\n`);
|
|
31
|
-
}
|
|
32
|
-
const time = new Date(event.timestamp || event.trackedAt).toLocaleTimeString();
|
|
33
|
-
lines.push(`- **${time}**: ${JSON.stringify(event)}`);
|
|
34
|
-
}
|
|
35
|
-
return lines.join('\n');
|
|
36
|
-
};
|