glad-web 1.0.45 → 2.0.1
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/README.md +4 -192
- package/THIRD_PARTY_NOTICES.md +27 -0
- package/bin/glad.cjs +56 -0
- package/package.json +19 -58
- package/README.zh-CN.md +0 -198
- package/assets/logo.svg +0 -43
- package/bin/cli.js +0 -65
- package/lib/ai-tools/demo/enhanced-demo.js +0 -625
- package/lib/ai-tools/demo/index.js +0 -24
- package/lib/ai-tools/demo/responses.js +0 -88
- package/lib/ai-tools/detector.js +0 -76
- package/lib/ai-tools/registry.js +0 -300
- package/lib/claude/cli-usage.js +0 -95
- package/lib/claude/config.js +0 -82
- package/lib/claude/structured-session.js +0 -884
- package/lib/claude/transcript-repository.js +0 -216
- package/lib/codex/image-store.js +0 -174
- package/lib/codex/structured-session.js +0 -1578
- package/lib/commands/config.js +0 -78
- package/lib/commands/tools.js +0 -128
- package/lib/commands/web.js +0 -586
- package/lib/config/constants.js +0 -17
- package/lib/config/manager.js +0 -89
- package/lib/git/service.js +0 -83
- package/lib/notifications/message-formatter.js +0 -94
- package/lib/notifications/notification-service.js +0 -143
- package/lib/notifications/serverchan-client.js +0 -58
- package/lib/notifications/serverchan-settings-store.js +0 -115
- package/lib/schedule/job-runner.js +0 -162
- package/lib/schedule/job-store.js +0 -167
- package/lib/schedule/key-sequences.js +0 -49
- package/lib/schedule/scheduler-service.js +0 -39
- package/lib/server/routes/notifications.js +0 -52
- package/lib/server/routes/providers.js +0 -114
- package/lib/server/routes/schedules.js +0 -54
- package/lib/server/routes/usage.js +0 -23
- package/lib/server/routes/workspace.js +0 -77
- package/lib/session/buffer.js +0 -102
- package/lib/session/file-attachment-store.js +0 -168
- package/lib/session/pty-manager.js +0 -255
- package/lib/session/rendered-history.js +0 -225
- package/lib/session/session-manager.js +0 -1001
- package/lib/session/text-history.js +0 -274
- package/lib/usage/ccusage-runner.js +0 -128
- package/lib/usage/source-catalog.js +0 -26
- package/lib/usage/usage-service.js +0 -226
- package/lib/utils/logger.js +0 -74
- package/lib/utils/pid.js +0 -67
- package/lib/utils/validation.js +0 -53
- package/lib/web/claude.js +0 -1129
- package/lib/web/codex.js +0 -1042
- package/lib/web/composer.js +0 -463
- package/lib/web/core.js +0 -373
- package/lib/web/git.js +0 -535
- package/lib/web/gitgraph.js +0 -293
- package/lib/web/index.html +0 -516
- package/lib/web/layout.js +0 -72
- package/lib/web/notifications.js +0 -163
- package/lib/web/schedules.js +0 -245
- package/lib/web/session.js +0 -360
- package/lib/web/shell.js +0 -59
- package/lib/web/styles.css +0 -905
- package/lib/web/terminal-scroll.js +0 -81
- package/lib/web/theme.js +0 -60
- package/lib/web/timed-inputs.js +0 -216
- package/lib/web/usage.js +0 -323
- package/lib/workspace/service.js +0 -77
- package/scripts/check-syntax.js +0 -26
package/lib/web/session.js
DELETED
|
@@ -1,360 +0,0 @@
|
|
|
1
|
-
function joinSession(id, sessionName, toolKey = null) {
|
|
2
|
-
if (typeof clearComposerAttachments === 'function' && (selectedImageAttachments.length || selectedFileAttachments.length)) {
|
|
3
|
-
void clearComposerAttachments();
|
|
4
|
-
}
|
|
5
|
-
stopTimedInputTimers();
|
|
6
|
-
activeSessionId = id;
|
|
7
|
-
window.activeSessionId = id;
|
|
8
|
-
activeToolKey = toolKey;
|
|
9
|
-
clearTimeout(sessionPollTimer);
|
|
10
|
-
sessionPollTimer = null;
|
|
11
|
-
markCompletionRead(id);
|
|
12
|
-
loadTimedInputs();
|
|
13
|
-
document.getElementById('session-title').innerText = sessionName;
|
|
14
|
-
updateToolShortcuts(activeToolKey);
|
|
15
|
-
document.querySelectorAll('.view').forEach(v => v.classList.remove('active'));
|
|
16
|
-
document.getElementById('terminal-view').classList.add('active');
|
|
17
|
-
setClaudeModeEnabled(isClaudeSession());
|
|
18
|
-
if (currentSocket) { currentSocket.close(); currentSocket = null; }
|
|
19
|
-
initTerminal(id);
|
|
20
|
-
if (typeof isSplitLayout === 'function' && isSplitLayout()) refreshSessionsNow();
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function initTerminal(sessionId) {
|
|
24
|
-
if (isClaudeSession()) {
|
|
25
|
-
initClaudeSession(sessionId);
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
if (isCodexSession()) {
|
|
29
|
-
initCodexSession(sessionId);
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
if (typeof Terminal === 'undefined') {
|
|
33
|
-
alert('Terminal library not loaded yet.');
|
|
34
|
-
showLobby();
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
function isTerminalAutoResponse(data) {
|
|
38
|
-
return /^\x1b\[\?[\d;]*c$/.test(data) || /^\x1b\[>[\d;]*c$/.test(data);
|
|
39
|
-
}
|
|
40
|
-
if (!term) {
|
|
41
|
-
term = new Terminal({ theme: typeof getGladTerminalTheme === 'function' ? getGladTerminalTheme() : { background: '#000', foreground: '#fff', cursor: '#0f0' }, cursorBlink: true, fontSize: 14, cursorStyle: 'bar', scrollback: 10000 });
|
|
42
|
-
fitAddon = new FitAddon.FitAddon();
|
|
43
|
-
term.loadAddon(fitAddon);
|
|
44
|
-
term.open(document.getElementById('terminal'));
|
|
45
|
-
term.onData(data => {
|
|
46
|
-
if (isTerminalAutoResponse(data)) return;
|
|
47
|
-
let finalData = data;
|
|
48
|
-
if (modifiers.ctrl && data.length === 1) {
|
|
49
|
-
const code = data.toLowerCase().charCodeAt(0);
|
|
50
|
-
if (code >= 97 && code <= 122) finalData = String.fromCharCode(code - 96);
|
|
51
|
-
}
|
|
52
|
-
sendWS(finalData);
|
|
53
|
-
});
|
|
54
|
-
} else { term.clear(); }
|
|
55
|
-
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
56
|
-
currentSocket = new WebSocket(protocol + '//' + window.location.host + '?sessionId=' + sessionId);
|
|
57
|
-
currentSocket.onopen = () => {
|
|
58
|
-
syncLayout();
|
|
59
|
-
setTimeout(() => logViewSnapshot('ws-open'), 300);
|
|
60
|
-
};
|
|
61
|
-
currentSocket.onmessage = (e) => {
|
|
62
|
-
const msg = JSON.parse(e.data);
|
|
63
|
-
if (msg.type === 'output') term.write(msg.data);
|
|
64
|
-
if (msg.type === 'reset') term.reset();
|
|
65
|
-
if (msg.type === 'exit') showLobby();
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
async function initClaudeSession(sessionId) {
|
|
70
|
-
claudeMessages = [];
|
|
71
|
-
claudePendingPermissions = [];
|
|
72
|
-
claudeStatus = 'idle';
|
|
73
|
-
const runtimeConfig = await refreshClaudeRuntimeConfig();
|
|
74
|
-
claudeState = {
|
|
75
|
-
permissionMode: 'default',
|
|
76
|
-
model: (runtimeConfig && runtimeConfig.defaultModel) || 'default',
|
|
77
|
-
effort: (runtimeConfig && runtimeConfig.defaultEffort) || 'medium',
|
|
78
|
-
claudeSessionId: null,
|
|
79
|
-
resumeSessionId: null,
|
|
80
|
-
canAbort: false,
|
|
81
|
-
pendingPermissionCount: 0
|
|
82
|
-
};
|
|
83
|
-
claudeResumePanelOpen = false;
|
|
84
|
-
claudeForkPanelOpen = false;
|
|
85
|
-
claudePickerOpen = null;
|
|
86
|
-
claudeUsagePending = false;
|
|
87
|
-
claudeContextPending = false;
|
|
88
|
-
claudeResumeItemsLoaded = false;
|
|
89
|
-
document.getElementById('claude-resume-panel').classList.remove('active');
|
|
90
|
-
document.getElementById('claude-resume-panel').innerHTML = '';
|
|
91
|
-
document.getElementById('claude-fork-panel').classList.remove('active');
|
|
92
|
-
document.getElementById('claude-fork-panel').innerHTML = '';
|
|
93
|
-
closeClaudePicker();
|
|
94
|
-
updateTerminalControlsHeight();
|
|
95
|
-
applyClaudeRuntimeConfig(runtimeConfig);
|
|
96
|
-
applyClaudeState(claudeState);
|
|
97
|
-
renderClaudeChat();
|
|
98
|
-
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
99
|
-
currentSocket = new WebSocket(protocol + '//' + window.location.host + '?sessionId=' + sessionId);
|
|
100
|
-
currentSocket.onopen = () => logViewSnapshot('claude-ws-open');
|
|
101
|
-
currentSocket.onmessage = (e) => {
|
|
102
|
-
const msg = JSON.parse(e.data);
|
|
103
|
-
if (msg.type === 'claude-snapshot' && msg.snapshot) {
|
|
104
|
-
claudeMessages = msg.snapshot.messages || [];
|
|
105
|
-
claudePendingPermissions = msg.snapshot.pendingPermissions || [];
|
|
106
|
-
claudeStatus = msg.snapshot.status || 'idle';
|
|
107
|
-
applyClaudeState(msg.snapshot.state || {
|
|
108
|
-
status: claudeStatus,
|
|
109
|
-
pendingPermissionCount: claudePendingPermissions.filter(item => item.status === 'pending').length,
|
|
110
|
-
canAbort: claudeStatus === 'thinking'
|
|
111
|
-
});
|
|
112
|
-
renderClaudeChat();
|
|
113
|
-
}
|
|
114
|
-
if (msg.type === 'claude-event') applyClaudeEvent(msg.event);
|
|
115
|
-
if (msg.type === 'exit') showLobby();
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
function initCodexSession(sessionId) {
|
|
120
|
-
codexMessages = [];
|
|
121
|
-
codexPendingPermissions = [];
|
|
122
|
-
codexModelPanelOpen = false;
|
|
123
|
-
codexModelCandidate = null;
|
|
124
|
-
codexResumePanelOpen = false;
|
|
125
|
-
codexForkPanelOpen = false;
|
|
126
|
-
codexPromptPanelOpen = false;
|
|
127
|
-
codexPromptItems = [];
|
|
128
|
-
codexPromptNextOffset = 0;
|
|
129
|
-
codexPromptHasMore = false;
|
|
130
|
-
codexPromptTotal = 0;
|
|
131
|
-
codexPromptLoading = false;
|
|
132
|
-
codexExpandedPrompts = new Set();
|
|
133
|
-
codexSkillPanelOpen = false;
|
|
134
|
-
codexSkillItems = [];
|
|
135
|
-
codexSkillLoading = false;
|
|
136
|
-
codexSkillQuery = '';
|
|
137
|
-
codexSkillError = '';
|
|
138
|
-
selectedCodexSkill = null;
|
|
139
|
-
codexDetailRequestSeq = 0;
|
|
140
|
-
codexDetailRequests = new Map();
|
|
141
|
-
codexDetailRevisions = new Map();
|
|
142
|
-
clearTimeout(codexDetailRefreshTimer);
|
|
143
|
-
codexDetailRefreshTimer = null;
|
|
144
|
-
codexDetailRefreshIds = new Set();
|
|
145
|
-
document.getElementById('codex-model-panel').classList.remove('active');
|
|
146
|
-
document.getElementById('codex-resume-panel').classList.remove('active');
|
|
147
|
-
document.getElementById('codex-fork-panel').classList.remove('active');
|
|
148
|
-
document.getElementById('codex-prompt-panel').classList.remove('active');
|
|
149
|
-
document.getElementById('codex-skill-panel').classList.remove('active');
|
|
150
|
-
document.getElementById('codex-control-rail').scrollLeft = 0;
|
|
151
|
-
codexState = createDefaultCodexState();
|
|
152
|
-
setClaudeModeEnabled(false);
|
|
153
|
-
applyCodexState(codexState);
|
|
154
|
-
installCodexLazyDetailHandler();
|
|
155
|
-
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
156
|
-
currentSocket = new WebSocket(protocol + '//' + window.location.host + '?sessionId=' + sessionId);
|
|
157
|
-
currentSocket.onmessage = (e) => {
|
|
158
|
-
const msg = JSON.parse(e.data);
|
|
159
|
-
if (msg.type === 'codex-snapshot' && msg.snapshot) {
|
|
160
|
-
codexMessages = msg.snapshot.messages || [];
|
|
161
|
-
codexPendingPermissions = msg.snapshot.pendingPermissions || [];
|
|
162
|
-
applyCodexState(msg.snapshot.state || {});
|
|
163
|
-
}
|
|
164
|
-
if (msg.type === 'codex-detail-response' && msg.detail) applyCodexDetailResponse(msg);
|
|
165
|
-
if (msg.type === 'codex-event') applyCodexEvent(msg.event);
|
|
166
|
-
if (msg.type === 'exit') showLobby();
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
function sendWS(data) {
|
|
171
|
-
if (currentSocket && currentSocket.readyState === 1) currentSocket.send(JSON.stringify({ type: 'input', data }));
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
let layoutTimer = null;
|
|
175
|
-
let layoutKeepAtBottom = false;
|
|
176
|
-
|
|
177
|
-
function isTerminalAtBottom() {
|
|
178
|
-
if (!term || !term.buffer || !term.buffer.active) return true;
|
|
179
|
-
const active = term.buffer.active;
|
|
180
|
-
return active.viewportY >= active.baseY - 1;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
function scrollTerminalViewportToBottom() {
|
|
184
|
-
if (!term) return;
|
|
185
|
-
term.scrollToBottom();
|
|
186
|
-
const viewport = document.querySelector('#terminal .xterm-viewport');
|
|
187
|
-
if (viewport) viewport.scrollTop = viewport.scrollHeight;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
function restoreTerminalBottomSoon() {
|
|
191
|
-
scrollTerminalViewportToBottom();
|
|
192
|
-
requestAnimationFrame(() => {
|
|
193
|
-
scrollTerminalViewportToBottom();
|
|
194
|
-
requestAnimationFrame(scrollTerminalViewportToBottom);
|
|
195
|
-
});
|
|
196
|
-
setTimeout(scrollTerminalViewportToBottom, 120);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
function syncLayout(options = {}) {
|
|
200
|
-
if (!fitAddon || !currentSocket) return;
|
|
201
|
-
const keepAtBottom = typeof options.keepAtBottom === 'boolean' ? options.keepAtBottom : isTerminalAtBottom();
|
|
202
|
-
layoutKeepAtBottom = layoutKeepAtBottom || keepAtBottom;
|
|
203
|
-
clearTimeout(layoutTimer);
|
|
204
|
-
layoutTimer = setTimeout(() => {
|
|
205
|
-
const shouldRestoreBottom = layoutKeepAtBottom;
|
|
206
|
-
layoutKeepAtBottom = false;
|
|
207
|
-
fitAddon.fit();
|
|
208
|
-
if (shouldRestoreBottom) restoreTerminalBottomSoon();
|
|
209
|
-
if (currentSocket.readyState === 1) currentSocket.send(JSON.stringify({ type: 'resize', cols: term.cols, rows: term.rows }));
|
|
210
|
-
}, 50);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
function previewText(text, maxChars = 320) {
|
|
214
|
-
if (!text) return '';
|
|
215
|
-
const normalized = String(text)
|
|
216
|
-
.replace(/\r/g, '\\r')
|
|
217
|
-
.replace(/\n/g, '\\n')
|
|
218
|
-
.replace(/\t/g, '\\t')
|
|
219
|
-
.replace(/\x1b/g, '\\x1b');
|
|
220
|
-
return normalized.length > maxChars ? normalized.slice(-maxChars) : normalized;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
async function logClientDebug(event, payload) {
|
|
224
|
-
try {
|
|
225
|
-
await fetch('/api/debug/client-log', {
|
|
226
|
-
method: 'POST',
|
|
227
|
-
headers: { 'Content-Type': 'application/json' },
|
|
228
|
-
body: JSON.stringify({ sessionId: activeSessionId, event, payload })
|
|
229
|
-
});
|
|
230
|
-
} catch (_) {}
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
function snapshotTerminalBuffer(maxLines = 12) {
|
|
234
|
-
if (!term || !term.buffer || !term.buffer.active) return null;
|
|
235
|
-
const active = term.buffer.active;
|
|
236
|
-
const totalLines = active.length || 0;
|
|
237
|
-
const start = Math.max(0, totalLines - maxLines);
|
|
238
|
-
const tailLines = [];
|
|
239
|
-
for (let i = start; i < totalLines; i++) {
|
|
240
|
-
const line = active.getLine(i);
|
|
241
|
-
if (!line) continue;
|
|
242
|
-
tailLines.push(line.translateToString(true));
|
|
243
|
-
}
|
|
244
|
-
return {
|
|
245
|
-
cols: term.cols,
|
|
246
|
-
rows: term.rows,
|
|
247
|
-
baseY: active.baseY,
|
|
248
|
-
viewportY: active.viewportY,
|
|
249
|
-
cursorX: active.cursorX,
|
|
250
|
-
cursorY: active.cursorY,
|
|
251
|
-
totalLines,
|
|
252
|
-
tailPreview: previewText(tailLines.join('\n'))
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
function logViewSnapshot(event) {
|
|
257
|
-
logClientDebug(event, {
|
|
258
|
-
sessionId: activeSessionId,
|
|
259
|
-
terminalSnapshot: snapshotTerminalBuffer()
|
|
260
|
-
});
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
// Navigation
|
|
264
|
-
function showTerminal() {
|
|
265
|
-
clearTimeout(sessionPollTimer);
|
|
266
|
-
sessionPollTimer = null;
|
|
267
|
-
document.querySelectorAll('.view').forEach(v => v.classList.remove('active'));
|
|
268
|
-
document.getElementById('terminal-view').classList.add('active');
|
|
269
|
-
logViewSnapshot('show-terminal');
|
|
270
|
-
syncLayout();
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
function showGitPreview() {
|
|
274
|
-
clearTimeout(sessionPollTimer);
|
|
275
|
-
sessionPollTimer = null;
|
|
276
|
-
document.querySelectorAll('.view').forEach(v => v.classList.remove('active'));
|
|
277
|
-
document.getElementById('git-view').classList.add('active');
|
|
278
|
-
switchGitTab('changes');
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
let historyWrapEnabled = true;
|
|
282
|
-
|
|
283
|
-
function showHistory() {
|
|
284
|
-
clearTimeout(sessionPollTimer);
|
|
285
|
-
sessionPollTimer = null;
|
|
286
|
-
document.querySelectorAll('.view').forEach(v => v.classList.remove('active'));
|
|
287
|
-
document.getElementById('history-view').classList.add('active');
|
|
288
|
-
logViewSnapshot('show-history-before-load');
|
|
289
|
-
loadHistory();
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
function toggleHistoryWrap() {
|
|
293
|
-
historyWrapEnabled = !historyWrapEnabled;
|
|
294
|
-
const contentEl = document.getElementById('history-content');
|
|
295
|
-
contentEl.classList.toggle('nowrap', !historyWrapEnabled);
|
|
296
|
-
document.getElementById('history-wrap-btn').style.color = historyWrapEnabled ? 'var(--primary)' : 'var(--text-dim)';
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
async function loadHistory() {
|
|
300
|
-
const contentEl = document.getElementById('history-content');
|
|
301
|
-
const metaEl = document.getElementById('history-meta');
|
|
302
|
-
if (!activeSessionId) {
|
|
303
|
-
metaEl.textContent = 'No active session.';
|
|
304
|
-
contentEl.textContent = '';
|
|
305
|
-
return;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
metaEl.textContent = 'Loading...';
|
|
309
|
-
try {
|
|
310
|
-
const res = await fetchWithTimeout(`/api/sessions/${activeSessionId}/history`, {}, 30000);
|
|
311
|
-
const data = await res.json();
|
|
312
|
-
if (!data.success) throw new Error(data.error || 'Failed to load history');
|
|
313
|
-
|
|
314
|
-
const updatedAt = data.updatedAt ? new Date(data.updatedAt).toLocaleTimeString() : 'unknown';
|
|
315
|
-
const size = typeof data.bytes === 'number' ? `${Math.round(data.bytes / 1024)} KB` : 'unknown size';
|
|
316
|
-
const truncated = data.truncated ? ' | truncated' : '';
|
|
317
|
-
metaEl.textContent = `${data.tool || 'Session'} | ${data.lines || 0} lines | ${size} | updated ${updatedAt}${truncated}`;
|
|
318
|
-
contentEl.textContent = data.text || 'No readable history yet.';
|
|
319
|
-
contentEl.classList.toggle('nowrap', !historyWrapEnabled);
|
|
320
|
-
document.getElementById('history-wrap-btn').style.color = historyWrapEnabled ? 'var(--primary)' : 'var(--text-dim)';
|
|
321
|
-
logClientDebug('history-loaded', {
|
|
322
|
-
meta: metaEl.textContent,
|
|
323
|
-
historyLines: data.lines || 0,
|
|
324
|
-
historyBytes: data.bytes || 0,
|
|
325
|
-
historyTail: previewText(data.text || '')
|
|
326
|
-
});
|
|
327
|
-
requestAnimationFrame(() => {
|
|
328
|
-
contentEl.scrollTop = contentEl.scrollHeight;
|
|
329
|
-
});
|
|
330
|
-
} catch (e) {
|
|
331
|
-
metaEl.textContent = 'Failed to load history.';
|
|
332
|
-
contentEl.textContent = e.message;
|
|
333
|
-
logClientDebug('history-load-failed', { message: e.message });
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
async function copyHistoryContent() {
|
|
338
|
-
const contentEl = document.getElementById('history-content');
|
|
339
|
-
const text = contentEl.textContent || '';
|
|
340
|
-
if (!text || text === 'Loading...' || text === 'No readable history yet.') {
|
|
341
|
-
alert('No history to copy.');
|
|
342
|
-
return;
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
try {
|
|
346
|
-
if (navigator.clipboard && window.isSecureContext) {
|
|
347
|
-
await navigator.clipboard.writeText(text);
|
|
348
|
-
} else {
|
|
349
|
-
const range = document.createRange();
|
|
350
|
-
range.selectNode(contentEl);
|
|
351
|
-
window.getSelection().removeAllRanges();
|
|
352
|
-
window.getSelection().addRange(range);
|
|
353
|
-
document.execCommand('copy');
|
|
354
|
-
window.getSelection().removeAllRanges();
|
|
355
|
-
}
|
|
356
|
-
alert('History copied.');
|
|
357
|
-
} catch (e) {
|
|
358
|
-
alert('Copy failed. You can select the history text manually.');
|
|
359
|
-
}
|
|
360
|
-
}
|
package/lib/web/shell.js
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
async function deleteSession(id, e) {
|
|
2
|
-
e.stopPropagation(); if (!confirm('Terminate session?')) return;
|
|
3
|
-
try { await fetchWithTimeout('/api/sessions/' + id, { method: 'DELETE' }); refreshSessionsNow(); } catch (e) { alert('Failed to delete'); }
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
function showLobby() {
|
|
7
|
-
if (currentSocket) { currentSocket.close(); currentSocket = null; }
|
|
8
|
-
closeTimedSendPanel();
|
|
9
|
-
stopTimedInputTimers();
|
|
10
|
-
activeSessionId = null;
|
|
11
|
-
window.activeSessionId = null;
|
|
12
|
-
activeToolKey = null;
|
|
13
|
-
document.querySelectorAll('.view').forEach(v => v.classList.remove('active'));
|
|
14
|
-
document.getElementById('lobby-view').classList.add('active');
|
|
15
|
-
refreshSessionsNow();
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function updateToolShortcuts(toolKey) {
|
|
19
|
-
const usesUsageCommand = ['antigravity', 'claude-code'].includes(toolKey);
|
|
20
|
-
const usageCommand = usesUsageCommand ? '/usage' : '/stat';
|
|
21
|
-
const defaultShortcuts = [
|
|
22
|
-
{ key: 'left', label: '←', special: true },
|
|
23
|
-
{ key: 'right', label: '→', special: true },
|
|
24
|
-
{ key: 'ctrl-c', label: 'Ctrl+C' },
|
|
25
|
-
{ command: '/model' },
|
|
26
|
-
{ command: '/resume' },
|
|
27
|
-
{ command: usageCommand }
|
|
28
|
-
];
|
|
29
|
-
const toolShortcuts = {
|
|
30
|
-
codex: [
|
|
31
|
-
{ key: 'ctrl-c', label: 'Ctrl+C' },
|
|
32
|
-
{ key: 'codex-perm', label: '/perm', title: 'Send /perm' },
|
|
33
|
-
{ command: '/model' },
|
|
34
|
-
{ command: '/resume' },
|
|
35
|
-
{ command: '/stat' }
|
|
36
|
-
]
|
|
37
|
-
};
|
|
38
|
-
const shortcuts = toolShortcuts[toolKey] || defaultShortcuts;
|
|
39
|
-
const shortcutGroup = document.querySelector('[data-role="tool-shortcuts"]');
|
|
40
|
-
if (!shortcutGroup) return;
|
|
41
|
-
|
|
42
|
-
shortcutGroup.style.gridTemplateColumns = `repeat(${shortcuts.length}, minmax(0, 1fr))`;
|
|
43
|
-
shortcutGroup.replaceChildren(...shortcuts.map(shortcut => {
|
|
44
|
-
const button = document.createElement(shortcut.command ? 'button' : 'div');
|
|
45
|
-
button.className = shortcut.command
|
|
46
|
-
? 'command-key'
|
|
47
|
-
: `key-btn${shortcut.special ? ' special-key' : ''}`;
|
|
48
|
-
if (shortcut.command) {
|
|
49
|
-
button.dataset.command = shortcut.command;
|
|
50
|
-
button.textContent = shortcut.command;
|
|
51
|
-
button.title = `Send ${shortcut.command}`;
|
|
52
|
-
} else {
|
|
53
|
-
button.dataset.key = shortcut.key;
|
|
54
|
-
button.textContent = shortcut.label;
|
|
55
|
-
button.title = shortcut.title || '';
|
|
56
|
-
}
|
|
57
|
-
return button;
|
|
58
|
-
}));
|
|
59
|
-
}
|