glad-web 1.0.44 → 1.0.46
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 +2 -0
- package/README.zh-CN.md +2 -0
- package/lib/claude/structured-session.js +10 -5
- package/lib/codex/image-store.js +1 -2
- package/lib/codex/structured-session.js +39 -76
- package/lib/commands/web.js +64 -9
- package/lib/config/manager.js +19 -0
- package/lib/server/routes/providers.js +0 -12
- package/lib/server/routes/skillhub.js +104 -0
- package/lib/session/file-attachment-store.js +168 -0
- package/lib/session/session-manager.js +143 -60
- package/lib/skillhub/client.js +121 -0
- package/lib/skillhub/settings-store.js +168 -0
- package/lib/skillhub/skill-installer.js +320 -0
- package/lib/web/bootstrap.js +34 -0
- package/lib/web/claude.js +44 -16
- package/lib/web/codex.js +51 -22
- package/lib/web/composer.js +293 -28
- package/lib/web/core.js +56 -38
- package/lib/web/git.js +53 -51
- package/lib/web/gitgraph.js +8 -8
- package/lib/web/index.html +61 -26
- package/lib/web/layout.js +4 -7
- package/lib/web/notifications.js +1 -0
- package/lib/web/session.js +7 -21
- package/lib/web/shell.js +17 -2
- package/lib/web/skillhub.js +197 -0
- package/lib/web/styles.css +138 -16
- package/lib/web/timed-inputs.js +6 -6
- package/package.json +6 -3
package/lib/web/git.js
CHANGED
|
@@ -51,23 +51,23 @@
|
|
|
51
51
|
let addCount = 0;
|
|
52
52
|
let subCount = 0;
|
|
53
53
|
for (const line of block.lines) {
|
|
54
|
-
let color = '
|
|
55
|
-
if (line.startsWith('+') && !line.startsWith('+++')) { color = '
|
|
56
|
-
else if (line.startsWith('-') && !line.startsWith('---')) { color = '
|
|
57
|
-
else if (line.startsWith('@@')) { color = '
|
|
58
|
-
else if (line.startsWith('diff') || line.startsWith('index') || line.startsWith('commit') || line.startsWith('Author') || line.startsWith('Date')) color = '
|
|
54
|
+
let color = 'var(--git-diff-text)', bg = 'transparent', borderLeft = '2px solid transparent';
|
|
55
|
+
if (line.startsWith('+') && !line.startsWith('+++')) { color = 'var(--git-add-text)'; bg = 'var(--git-add-bg)'; borderLeft = '2px solid var(--git-add-text)'; addCount++; }
|
|
56
|
+
else if (line.startsWith('-') && !line.startsWith('---')) { color = 'var(--git-del-text)'; bg = 'var(--git-del-bg)'; borderLeft = '2px solid var(--git-del-text)'; subCount++; }
|
|
57
|
+
else if (line.startsWith('@@')) { color = 'var(--git-hunk-text)'; bg = 'var(--git-hunk-bg)'; }
|
|
58
|
+
else if (line.startsWith('diff') || line.startsWith('index') || line.startsWith('commit') || line.startsWith('Author') || line.startsWith('Date')) color = 'var(--text)';
|
|
59
59
|
|
|
60
60
|
blockContent += `<div style="color:${color}; background:${bg}; border-left:${borderLeft}; padding:2px 8px; white-space:pre-wrap; word-break:break-all;">${line.replace(/</g, '<').replace(/>/g, '>') || ' '}</div>`;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
const isOpen = block.name === 'Commit Details';
|
|
64
|
-
const statHTML = block.name !== 'Commit Details' ? `<span style="margin-left: 12px; font-family: monospace; font-size: 12px;"><span style="color
|
|
64
|
+
const statHTML = block.name !== 'Commit Details' ? `<span style="margin-left: 12px; font-family: monospace; font-size: 12px;"><span style="color:var(--git-add-text);">+${addCount}</span> <span style="color:var(--git-del-text); margin-left:6px;">-${subCount}</span></span>` : '';
|
|
65
65
|
diffHTML += `
|
|
66
|
-
<details ${isOpen ? 'open' : ''}
|
|
67
|
-
<summary
|
|
66
|
+
<details ${isOpen ? 'open' : ''} class="git-diff-panel">
|
|
67
|
+
<summary class="git-diff-summary">
|
|
68
68
|
${block.name === 'Commit Details' ? '📝 ' : '📄 '}${block.name.replace(/</g, '<').replace(/>/g, '>')}${statHTML}
|
|
69
69
|
</summary>
|
|
70
|
-
<div
|
|
70
|
+
<div class="git-diff-body">
|
|
71
71
|
${blockContent}
|
|
72
72
|
</div>
|
|
73
73
|
</details>
|
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
function renderCommitDiffFullView() {
|
|
87
87
|
const content = document.getElementById('git-content');
|
|
88
88
|
let html = `
|
|
89
|
-
<div style="display:flex; align-items:center; background: var(--card-bg); padding: 12px 14px; border-bottom: 1px solid
|
|
89
|
+
<div style="display:flex; align-items:center; background: var(--card-bg); padding: 12px 14px; border-bottom: 1px solid var(--line); position: sticky; top: 0; z-index: 10;">
|
|
90
90
|
<button class="icon-btn" onclick="switchGitTab('graph')" style="color: var(--primary); margin-right: 12px; font-weight:600; font-size:14px; display:flex; align-items:center;">
|
|
91
91
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg> Back
|
|
92
92
|
</button>
|
|
@@ -150,11 +150,11 @@
|
|
|
150
150
|
let colorStyle = '';
|
|
151
151
|
let badgeHtml = '';
|
|
152
152
|
if (f.gitStatus) {
|
|
153
|
-
let color = '
|
|
153
|
+
let color = 'var(--text)';
|
|
154
154
|
let label = f.gitStatus.trim();
|
|
155
|
-
if (label.includes('U') || label.includes('?')) { colorStyle = 'color:
|
|
156
|
-
else if (label.includes('M')) { colorStyle = 'color:
|
|
157
|
-
else if (label.includes('D')) { colorStyle = 'color:
|
|
155
|
+
if (label.includes('U') || label.includes('?')) { colorStyle = 'color: var(--git-add-text);'; color = 'var(--git-add-text)'; if(label === '??') label = 'U'; }
|
|
156
|
+
else if (label.includes('M')) { colorStyle = 'color: var(--git-modified-text);'; color = 'var(--git-modified-text)'; }
|
|
157
|
+
else if (label.includes('D')) { colorStyle = 'color: var(--git-del-text);'; color = 'var(--git-del-text)'; }
|
|
158
158
|
badgeHtml = `<span style="color:${color}; font-weight:700; font-size:10px; border:1px solid ${color}; padding:1px 4px; border-radius:3px; opacity:0.7; margin-left: auto;">${label}</span>`;
|
|
159
159
|
}
|
|
160
160
|
|
|
@@ -232,11 +232,10 @@
|
|
|
232
232
|
files.forEach((f, idx) => {
|
|
233
233
|
const encodedPath = encodePathValue(f.path);
|
|
234
234
|
const escapedPath = escapeHtml(f.path);
|
|
235
|
-
let
|
|
235
|
+
let statusClass = 'modified';
|
|
236
236
|
let label = f.status;
|
|
237
|
-
if (label.includes('
|
|
238
|
-
else if (label.includes('
|
|
239
|
-
else if (label.includes('D')) { color = '#f87171'; }
|
|
237
|
+
if (label.includes('A') || label === '??') { statusClass = 'added'; if(label === '??') label = 'U'; }
|
|
238
|
+
else if (label.includes('D')) { statusClass = 'deleted'; }
|
|
240
239
|
const isUntracked = f.status === '??';
|
|
241
240
|
const hasStaged = !isUntracked && f.status[0] && f.status[0] !== ' ';
|
|
242
241
|
const hasUnstaged = !isUntracked && f.status[1] && f.status[1] !== ' ';
|
|
@@ -245,14 +244,14 @@
|
|
|
245
244
|
if (statsMap[f.path]) {
|
|
246
245
|
const { added, removed } = statsMap[f.path];
|
|
247
246
|
if (added > 0 || removed > 0) {
|
|
248
|
-
statHTML = `<span style="margin-left: 12px; font-family: monospace; font-size: 12px; white-space: nowrap;"><span style="color
|
|
247
|
+
statHTML = `<span style="margin-left: 12px; font-family: monospace; font-size: 12px; white-space: nowrap;"><span style="color:var(--git-add-text);">+${added}</span> <span style="color:var(--git-del-text); margin-left:6px;">-${removed}</span></span>`;
|
|
249
248
|
}
|
|
250
249
|
}
|
|
251
250
|
|
|
252
|
-
html += `<div
|
|
253
|
-
<div onclick="toggleInlineDiff('${encodedPath}', 'inline-diff-${idx}', ${!!hasStaged}, ${!!hasUnstaged}, ${isUntracked})"
|
|
254
|
-
<div
|
|
255
|
-
<span
|
|
251
|
+
html += `<div class="git-change-card">
|
|
252
|
+
<div class="git-change-header" onclick="toggleInlineDiff('${encodedPath}', 'inline-diff-${idx}', ${!!hasStaged}, ${!!hasUnstaged}, ${isUntracked})">
|
|
253
|
+
<div class="git-change-path">📄 ${escapedPath}${statHTML}</div>
|
|
254
|
+
<span class="git-status-badge ${statusClass}">${label}</span>
|
|
256
255
|
</div>
|
|
257
256
|
<div id="inline-diff-${idx}" style="display: none;" data-loaded="false"></div>
|
|
258
257
|
</div>`;
|
|
@@ -335,26 +334,26 @@
|
|
|
335
334
|
});
|
|
336
335
|
|
|
337
336
|
if (!currentFileDiff && !currentFileContent) {
|
|
338
|
-
container.innerHTML = `<div style="padding: 10px; color:
|
|
337
|
+
container.innerHTML = `<div style="padding: 10px; color: var(--git-del-text); font-size: 12px; text-align:center;">No diff available</div>`;
|
|
339
338
|
return;
|
|
340
339
|
}
|
|
341
340
|
|
|
342
341
|
let blockContent = '';
|
|
343
342
|
currentFileDiff.split('\n').forEach(line => {
|
|
344
|
-
let color = '
|
|
345
|
-
if (line.startsWith('+') && !line.startsWith('+++')) { color = '
|
|
346
|
-
else if (line.startsWith('-') && !line.startsWith('---')) { color = '
|
|
347
|
-
else if (line.startsWith('@@')) { color = '
|
|
343
|
+
let color = 'var(--git-diff-text)', bg = 'transparent', borderLeft = '2px solid transparent';
|
|
344
|
+
if (line.startsWith('+') && !line.startsWith('+++')) { color = 'var(--git-add-text)'; bg = 'var(--git-add-bg)'; borderLeft = '2px solid var(--git-add-text)'; }
|
|
345
|
+
else if (line.startsWith('-') && !line.startsWith('---')) { color = 'var(--git-del-text)'; bg = 'var(--git-del-bg)'; borderLeft = '2px solid var(--git-del-text)'; }
|
|
346
|
+
else if (line.startsWith('@@')) { color = 'var(--git-hunk-text)'; bg = 'var(--git-hunk-bg)'; }
|
|
348
347
|
|
|
349
348
|
blockContent += `<div style="color:${color}; background:${bg}; border-left:${borderLeft}; padding:2px 8px; white-space:pre-wrap; word-break:break-all;">${escapeHtml(line) || ' '}</div>`;
|
|
350
349
|
});
|
|
351
350
|
|
|
352
351
|
container.dataset.loaded = 'true';
|
|
353
352
|
container.innerHTML = `
|
|
354
|
-
<div
|
|
353
|
+
<div class="git-inline-diff git-inline-diff-body">
|
|
355
354
|
${blockContent}
|
|
356
355
|
</div>
|
|
357
|
-
<div
|
|
356
|
+
<div class="git-inline-diff-footer">
|
|
358
357
|
<button onclick="showFileDetails(decodePathValue('${encodedPath}'), true, ${!!hasStaged}, ${!!hasUnstaged}, ${!!isUntracked})" style="background: var(--primary); border: none; color: #fff; padding: 6px 12px; border-radius: 4px; font-size: 12px; cursor: pointer; display: inline-flex; align-items: center; gap: 6px;">
|
|
359
358
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path><polyline points="15 3 21 3 21 9"></polyline><line x1="10" y1="14" x2="21" y2="3"></line></svg>
|
|
360
359
|
View Full File
|
|
@@ -362,7 +361,7 @@
|
|
|
362
361
|
</div>
|
|
363
362
|
`;
|
|
364
363
|
} catch (e) {
|
|
365
|
-
container.innerHTML = `<div style="padding: 10px; color:
|
|
364
|
+
container.innerHTML = `<div style="padding: 10px; color: var(--git-del-text); font-size: 12px; text-align:center;">Error: ${e.message}</div>`;
|
|
366
365
|
}
|
|
367
366
|
};
|
|
368
367
|
|
|
@@ -404,7 +403,7 @@
|
|
|
404
403
|
function renderFileDetails() {
|
|
405
404
|
const content = document.getElementById('git-content');
|
|
406
405
|
let html = `
|
|
407
|
-
<div style="display:flex; align-items:center; background: var(--card-bg); padding: 12px 14px; border-bottom: 1px solid
|
|
406
|
+
<div style="display:flex; align-items:center; background: var(--card-bg); padding: 12px 14px; border-bottom: 1px solid var(--line); position: sticky; top: 0; z-index: 10;">
|
|
408
407
|
<button class="icon-btn" onclick="switchGitTab(currentGitTab)" style="color: var(--primary); margin-right: 12px; font-weight:600; font-size:14px; display:flex; align-items:center; flex-shrink: 0;">
|
|
409
408
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg> Back
|
|
410
409
|
</button>
|
|
@@ -415,27 +414,27 @@
|
|
|
415
414
|
if (currentFileDiff && currentFileContent) {
|
|
416
415
|
html += `
|
|
417
416
|
<div style="display: flex; gap: 6px; flex-shrink: 0; align-items: center;">
|
|
418
|
-
<div
|
|
419
|
-
<button onclick="changeFontSize(-1)"
|
|
420
|
-
<button onclick="changeFontSize(0)"
|
|
421
|
-
<button onclick="changeFontSize(1)"
|
|
417
|
+
<div class="git-file-toolbar-group">
|
|
418
|
+
<button class="git-file-toolbar-button" onclick="changeFontSize(-1)" title="Zoom Out">A-</button>
|
|
419
|
+
<button class="git-file-toolbar-button" onclick="changeFontSize(0)" title="Reset Size">${currentFontSize}</button>
|
|
420
|
+
<button class="git-file-toolbar-button" onclick="changeFontSize(1)" title="Zoom In">A+</button>
|
|
422
421
|
</div>
|
|
423
|
-
<button
|
|
422
|
+
<button class="git-file-mode-button${currentFileMode === 'diff' ? ' active' : ''}" onclick="toggleFileMode()">
|
|
424
423
|
Diff
|
|
425
424
|
</button>
|
|
426
|
-
<button
|
|
425
|
+
<button class="git-file-mode-button${currentFileWrap ? ' active' : ''}" onclick="toggleFileWrap()">
|
|
427
426
|
Wrap
|
|
428
427
|
</button>
|
|
429
428
|
</div>`;
|
|
430
429
|
} else {
|
|
431
430
|
html += `
|
|
432
431
|
<div style="display: flex; gap: 6px; flex-shrink: 0; align-items: center;">
|
|
433
|
-
<div
|
|
434
|
-
<button onclick="changeFontSize(-1)"
|
|
435
|
-
<button onclick="changeFontSize(0)"
|
|
436
|
-
<button onclick="changeFontSize(1)"
|
|
432
|
+
<div class="git-file-toolbar-group">
|
|
433
|
+
<button class="git-file-toolbar-button" onclick="changeFontSize(-1)" title="Zoom Out">A-</button>
|
|
434
|
+
<button class="git-file-toolbar-button" onclick="changeFontSize(0)" title="Reset Size">${currentFontSize}</button>
|
|
435
|
+
<button class="git-file-toolbar-button" onclick="changeFontSize(1)" title="Zoom In">A+</button>
|
|
437
436
|
</div>
|
|
438
|
-
<button
|
|
437
|
+
<button class="git-file-mode-button${currentFileWrap ? ' active' : ''}" onclick="toggleFileWrap()">
|
|
439
438
|
Wrap
|
|
440
439
|
</button>
|
|
441
440
|
</div>`;
|
|
@@ -446,7 +445,7 @@
|
|
|
446
445
|
const wrapStyle = currentFileWrap ? 'white-space: pre-wrap; word-break: break-all;' : 'white-space: pre;';
|
|
447
446
|
const innerWrapStyle = currentFileWrap ? '' : 'min-width: max-content;';
|
|
448
447
|
html += `<div style="padding: 10px;">`;
|
|
449
|
-
html += `<div
|
|
448
|
+
html += `<div class="git-file-code" style="font-size:${currentFontSize}px; line-height:1.5; -webkit-text-size-adjust:100%; text-size-adjust:100%;">`;
|
|
450
449
|
html += `<div style="${innerWrapStyle}">`;
|
|
451
450
|
|
|
452
451
|
let mergedLines = [];
|
|
@@ -505,20 +504,20 @@
|
|
|
505
504
|
}
|
|
506
505
|
|
|
507
506
|
mergedLines.forEach(item => {
|
|
508
|
-
let color = '
|
|
507
|
+
let color = 'var(--git-diff-text)', bg = 'transparent', borderLeft = '2px solid transparent';
|
|
509
508
|
let prefix = ' ';
|
|
510
|
-
if (item.type === 'added') { color = '
|
|
511
|
-
else if (item.type === 'deleted') { color = '
|
|
509
|
+
if (item.type === 'added') { color = 'var(--git-add-text)'; bg = 'var(--git-add-bg)'; borderLeft = '2px solid var(--git-add-text)'; prefix = '+'; }
|
|
510
|
+
else if (item.type === 'deleted') { color = 'var(--git-del-text)'; bg = 'var(--git-del-bg)'; borderLeft = '2px solid var(--git-del-text)'; prefix = '-'; }
|
|
512
511
|
else if (item.type === 'raw') {
|
|
513
|
-
if (item.text.startsWith('+') && !item.text.startsWith('+++')) { color = '
|
|
514
|
-
else if (item.text.startsWith('-') && !item.text.startsWith('---')) { color = '
|
|
515
|
-
else if (item.text.startsWith('@@')) { color = '
|
|
512
|
+
if (item.text.startsWith('+') && !item.text.startsWith('+++')) { color = 'var(--git-add-text)'; bg = 'var(--git-add-bg)'; borderLeft = '2px solid var(--git-add-text)'; }
|
|
513
|
+
else if (item.text.startsWith('-') && !item.text.startsWith('---')) { color = 'var(--git-del-text)'; bg = 'var(--git-del-bg)'; borderLeft = '2px solid var(--git-del-text)'; }
|
|
514
|
+
else if (item.text.startsWith('@@')) { color = 'var(--git-hunk-text)'; bg = 'var(--git-hunk-bg)'; }
|
|
516
515
|
prefix = '';
|
|
517
516
|
}
|
|
518
517
|
|
|
519
518
|
const renderedText = item.type === 'raw' ? item.text : `${prefix} ${item.text}`;
|
|
520
519
|
html += `<div style="color:${color}; background:${bg}; border-left:${borderLeft}; padding: 0 12px 0 8px; display:flex;">`;
|
|
521
|
-
html += `<div
|
|
520
|
+
html += `<div class="git-line-number">${item.lineNum || ''}</div>`;
|
|
522
521
|
html += `<div style="flex:1; min-width:0; ${wrapStyle}">${escapeHtml(renderedText) || ' '}</div>`;
|
|
523
522
|
html += `</div>`;
|
|
524
523
|
});
|
|
@@ -531,3 +530,6 @@
|
|
|
531
530
|
refreshSessionsNow();
|
|
532
531
|
document.addEventListener('visibilitychange', refreshSessionsNow);
|
|
533
532
|
});
|
|
533
|
+
window.addEventListener('glad-theme-change', () => {
|
|
534
|
+
if (document.getElementById('git-view').classList.contains('active')) switchGitTab(currentGitTab);
|
|
535
|
+
});
|
package/lib/web/gitgraph.js
CHANGED
|
@@ -74,10 +74,10 @@ class GitGraphRenderer {
|
|
|
74
74
|
rowDiv.style.display = 'flex';
|
|
75
75
|
rowDiv.style.alignItems = 'center';
|
|
76
76
|
rowDiv.style.height = this.rowHeight + 'px';
|
|
77
|
-
rowDiv.style.borderBottom = '1px solid
|
|
77
|
+
rowDiv.style.borderBottom = '1px solid var(--line)';
|
|
78
78
|
rowDiv.style.position = 'relative';
|
|
79
79
|
rowDiv.style.cursor = 'pointer';
|
|
80
|
-
rowDiv.onmouseover = () => rowDiv.style.backgroundColor = '
|
|
80
|
+
rowDiv.onmouseover = () => rowDiv.style.backgroundColor = 'var(--surface-soft)';
|
|
81
81
|
rowDiv.onmouseout = () => rowDiv.style.backgroundColor = 'transparent';
|
|
82
82
|
|
|
83
83
|
const canvas = document.createElement('canvas');
|
|
@@ -102,7 +102,7 @@ class GitGraphRenderer {
|
|
|
102
102
|
contentDiv.style.overflow = 'hidden';
|
|
103
103
|
|
|
104
104
|
const msgSpan = document.createElement('span');
|
|
105
|
-
msgSpan.style.color = '
|
|
105
|
+
msgSpan.style.color = 'var(--text)';
|
|
106
106
|
msgSpan.style.fontSize = '13px';
|
|
107
107
|
msgSpan.style.fontWeight = '500';
|
|
108
108
|
msgSpan.style.overflow = 'hidden';
|
|
@@ -160,18 +160,18 @@ class GitGraphRenderer {
|
|
|
160
160
|
el.remove();
|
|
161
161
|
});
|
|
162
162
|
|
|
163
|
-
rowDiv.style.backgroundColor = '
|
|
163
|
+
rowDiv.style.backgroundColor = 'var(--surface-soft)';
|
|
164
164
|
|
|
165
165
|
const detailsDiv = document.createElement('div');
|
|
166
166
|
detailsDiv.className = 'gitgraph-details-view';
|
|
167
|
-
detailsDiv.style.backgroundColor = '
|
|
167
|
+
detailsDiv.style.backgroundColor = 'var(--surface)';
|
|
168
168
|
detailsDiv.style.borderLeft = '2px solid ' + this.getColor(commit.col);
|
|
169
169
|
detailsDiv.style.padding = '16px';
|
|
170
170
|
detailsDiv.style.margin = '4px 0 4px ' + (canvasWidth) + 'px';
|
|
171
171
|
detailsDiv.style.borderRadius = '0 6px 6px 0';
|
|
172
172
|
detailsDiv.style.fontSize = '13px';
|
|
173
173
|
detailsDiv.style.color = 'var(--text)';
|
|
174
|
-
detailsDiv.style.boxShadow = 'inset 0 0 10px rgba(0,0,0,0.
|
|
174
|
+
detailsDiv.style.boxShadow = 'inset 0 0 10px rgba(0,0,0,0.12)';
|
|
175
175
|
|
|
176
176
|
let detailsHTML = `
|
|
177
177
|
<div style="display:flex; justify-content:space-between; margin-bottom:12px;">
|
|
@@ -189,7 +189,7 @@ class GitGraphRenderer {
|
|
|
189
189
|
|
|
190
190
|
// Add diff placeholder
|
|
191
191
|
detailsHTML += `
|
|
192
|
-
<div style="border-top:1px solid
|
|
192
|
+
<div style="border-top:1px solid var(--line); padding-top:12px; margin-top:12px;">
|
|
193
193
|
<button onclick="window.loadCommitDiff('${commit.hash}')" style="background:var(--primary); border:none; color:#fff; padding:6px 12px; border-radius:4px; font-size:12px; cursor:pointer;">View Full Diff</button>
|
|
194
194
|
</div>
|
|
195
195
|
`;
|
|
@@ -269,7 +269,7 @@ class GitGraphRenderer {
|
|
|
269
269
|
ctx.fillStyle = this.getColor(commit.col);
|
|
270
270
|
ctx.fill();
|
|
271
271
|
ctx.lineWidth = 2;
|
|
272
|
-
ctx.strokeStyle = '#121212';
|
|
272
|
+
ctx.strokeStyle = getComputedStyle(document.documentElement).getPropertyValue('--surface').trim() || '#121212';
|
|
273
273
|
ctx.stroke();
|
|
274
274
|
}
|
|
275
275
|
|
package/lib/web/index.html
CHANGED
|
@@ -6,16 +6,7 @@
|
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
|
|
7
7
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
|
8
8
|
<meta name="theme-color" content="#000000">
|
|
9
|
-
<script>
|
|
10
|
-
(() => {
|
|
11
|
-
const storedTheme = localStorage.getItem('glad-theme');
|
|
12
|
-
const theme = storedTheme === 'light' || storedTheme === 'dark'
|
|
13
|
-
? storedTheme
|
|
14
|
-
: (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
|
|
15
|
-
document.documentElement.dataset.theme = theme;
|
|
16
|
-
document.documentElement.style.backgroundColor = theme === 'dark' ? '#000000' : '#f5f6f8';
|
|
17
|
-
})();
|
|
18
|
-
</script>
|
|
9
|
+
<script src="bootstrap.js"></script>
|
|
19
10
|
<link rel="manifest" href="manifest.json">
|
|
20
11
|
<link rel="icon" type="image/svg+xml" href="logo.svg">
|
|
21
12
|
<link rel="apple-touch-icon" href="logo.svg">
|
|
@@ -39,7 +30,9 @@
|
|
|
39
30
|
<symbol id="icon-lock" viewBox="0 0 24 24"><rect x="5" y="10" width="14" height="11" rx="2"/><path d="M8 10V7a4 4 0 0 1 8 0v3"/></symbol>
|
|
40
31
|
<symbol id="icon-skills" viewBox="0 0 24 24"><path d="M9 3H4a1 1 0 0 0-1 1v5h2.5a2.5 2.5 0 1 1 0 5H3v5a1 1 0 0 0 1 1h5v-2.5a2.5 2.5 0 1 1 5 0V20h5a1 1 0 0 0 1-1v-5h-2.5a2.5 2.5 0 1 1 0-5H20V4a1 1 0 0 0-1-1h-5v2.5a2.5 2.5 0 1 1-5 0V3Z"/></symbol>
|
|
41
32
|
<symbol id="icon-image" viewBox="0 0 24 24"><rect x="3" y="5" width="18" height="14" rx="2"/><circle cx="9" cy="10" r="2"/><path d="m4 17 4.5-4 3.5 3 2.5-2 5.5 4"/></symbol>
|
|
33
|
+
<symbol id="icon-file" viewBox="0 0 24 24"><path d="M9 8.5 14.5 3a4 4 0 0 1 5.7 5.7L10.4 18.5a5.5 5.5 0 0 1-7.8-7.8l9.2-9.2"/><path d="m7 11 7-7a2 2 0 1 1 2.8 2.8l-9.1 9.1a2.5 2.5 0 0 1-3.5-3.5L12 4.6"/></symbol>
|
|
42
34
|
<symbol id="icon-schedule" viewBox="0 0 24 24"><circle cx="10.5" cy="13" r="7.5"/><path d="M10.5 9v4l2.5 1.5M18 3v6M15 6h6"/></symbol>
|
|
35
|
+
<symbol id="icon-calendar" viewBox="0 0 24 24"><rect x="3" y="5" width="18" height="16" rx="2"/><path d="M8 3v4M16 3v4M3 10h18"/></symbol>
|
|
43
36
|
</svg>
|
|
44
37
|
|
|
45
38
|
<div id="app-shell">
|
|
@@ -48,7 +41,10 @@
|
|
|
48
41
|
<div id="lobby-view" class="view active">
|
|
49
42
|
<div id="lobby">
|
|
50
43
|
<div class="header">
|
|
51
|
-
<
|
|
44
|
+
<button id="skill-hall-button" class="skill-hall-entry-btn" type="button" disabled
|
|
45
|
+
onclick="openSkillHall()" title="Skill暂不可用" aria-label="Skill暂不可用">
|
|
46
|
+
<svg class="action-icon" aria-hidden="true"><use href="#icon-skills"></use></svg><span>Skills</span>
|
|
47
|
+
</button>
|
|
52
48
|
<div class="header-actions">
|
|
53
49
|
<button class="header-action-btn" onclick="showToolModal()" title="New AI session"><span>+</span><span>Session</span></button>
|
|
54
50
|
<button id="usage-dashboard-button" class="header-action-btn icon-only" type="button"
|
|
@@ -59,9 +55,7 @@
|
|
|
59
55
|
</button>
|
|
60
56
|
<button id="schedule-create-button" class="header-action-btn icon-only" type="button"
|
|
61
57
|
onclick="showScheduleModal()" title="New scheduled task" aria-label="New scheduled task">
|
|
62
|
-
<svg width="19" height="19" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
|
|
63
|
-
<circle cx="12" cy="13" r="8"></circle><path d="M12 9v4l2.5 1.5"></path><path d="M9 2h6"></path><path d="M12 2v3"></path>
|
|
64
|
-
</svg>
|
|
58
|
+
<svg width="19" height="19" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><use href="#icon-calendar"></use></svg>
|
|
65
59
|
</button>
|
|
66
60
|
<button id="app-settings-button" class="header-action-btn icon-only" type="button"
|
|
67
61
|
onclick="openSettings()" title="Settings" aria-label="Settings">
|
|
@@ -164,7 +158,6 @@
|
|
|
164
158
|
</button>
|
|
165
159
|
<div id="session-title">Terminal</div>
|
|
166
160
|
<div style="display: flex; align-items: center; gap: 8px;">
|
|
167
|
-
<button id="codex-terminal-switch" class="icon-btn" onclick="toggleCodexPresentation()" title="Switch Codex interface" style="display:none; color: var(--primary); font-size: 12px; font-weight: 700; background: rgba(0, 122, 255, 0.1); padding: 5px 9px; border-radius: 15px; gap: 4px;">TERM</button>
|
|
168
161
|
<button class="icon-btn" onclick="showHistory()" title="History" style="color: var(--primary); font-size: 12px; font-weight: 700; background: rgba(0, 122, 255, 0.1); padding: 5px 9px; border-radius: 15px; gap: 4px;">
|
|
169
162
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12a9 9 0 1 0 3-6.7"></path><polyline points="3 3 3 9 9 9"></polyline><path d="M12 7v5l3 2"></path></svg>
|
|
170
163
|
HIS
|
|
@@ -175,18 +168,23 @@
|
|
|
175
168
|
</button>
|
|
176
169
|
</div>
|
|
177
170
|
</div>
|
|
171
|
+
<div id="session-status-strip" aria-live="polite">
|
|
172
|
+
<div id="session-attention-rail"></div>
|
|
173
|
+
</div>
|
|
178
174
|
<div id="terminal-controls">
|
|
179
175
|
<div id="timed-tag-rail"></div>
|
|
176
|
+
<div id="composer-skill-prefix"></div>
|
|
180
177
|
<div id="input-row">
|
|
181
178
|
<textarea id="cmd-input" rows="1" placeholder="Type a message..."></textarea>
|
|
182
|
-
<button id="
|
|
183
|
-
<button id="schedule-send-btn" class="composer-action-btn" type="button" title="Schedule
|
|
179
|
+
<button id="attachment-btn" class="composer-action-btn" type="button" title="Attachment" aria-label="Attachment"><svg class="action-icon" aria-hidden="true"><use href="#icon-file"></use></svg></button>
|
|
180
|
+
<button id="schedule-send-btn" class="composer-action-btn" type="button" title="Schedule" aria-label="Schedule"><svg class="action-icon" aria-hidden="true"><use href="#icon-schedule"></use></svg></button>
|
|
184
181
|
<button id="send-btn" type="button" title="Send" aria-label="Send">
|
|
185
|
-
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="22" y1="2" x2="11" y2="13"></line><polygon points="22 2 15 22 11 13 2 9 22 2"></polygon></svg>
|
|
182
|
+
<svg class="action-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="22" y1="2" x2="11" y2="13"></line><polygon points="22 2 15 22 11 13 2 9 22 2"></polygon></svg>
|
|
186
183
|
</button>
|
|
187
184
|
</div>
|
|
188
185
|
<div id="attachment-strip" aria-live="polite"></div>
|
|
189
|
-
<input id="
|
|
186
|
+
<input id="attachment-file-input" type="file" multiple hidden>
|
|
187
|
+
<div id="composer-action-tooltip" role="tooltip"></div>
|
|
190
188
|
<div id="claude-control-panel">
|
|
191
189
|
<div class="claude-control-rail">
|
|
192
190
|
<div class="claude-control-page primary">
|
|
@@ -304,13 +302,13 @@
|
|
|
304
302
|
|
|
305
303
|
<!-- Plain Text History View -->
|
|
306
304
|
<div id="history-view" class="view">
|
|
307
|
-
<div
|
|
308
|
-
<div
|
|
305
|
+
<div class="subview-nav">
|
|
306
|
+
<div class="subview-nav-row">
|
|
309
307
|
<button class="icon-btn" onclick="showTerminal()" style="color: var(--primary); font-size: 16px; font-weight: 500;">
|
|
310
308
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg>
|
|
311
309
|
Terminal
|
|
312
310
|
</button>
|
|
313
|
-
<div
|
|
311
|
+
<div class="subview-nav-title">History</div>
|
|
314
312
|
<button class="icon-btn" onclick="loadHistory()" title="Refresh" style="color: var(--primary); margin-right: 8px;">
|
|
315
313
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12a9 9 0 1 1-3-6.7"></path><polyline points="21 3 21 9 15 9"></polyline></svg>
|
|
316
314
|
</button>
|
|
@@ -328,13 +326,13 @@
|
|
|
328
326
|
|
|
329
327
|
<!-- Git View -->
|
|
330
328
|
<div id="git-view" class="view">
|
|
331
|
-
<div
|
|
332
|
-
<div
|
|
329
|
+
<div class="subview-nav">
|
|
330
|
+
<div class="subview-nav-row">
|
|
333
331
|
<button class="icon-btn" onclick="showTerminal()" style="color: var(--primary); font-size: 16px; font-weight: 500;">
|
|
334
332
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg>
|
|
335
333
|
Terminal
|
|
336
334
|
</button>
|
|
337
|
-
<div
|
|
335
|
+
<div class="subview-nav-title">Git Status</div>
|
|
338
336
|
<button class="icon-btn" onclick="loadGitStatus()" style="color: var(--primary);">
|
|
339
337
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12a9 9 0 1 1-3-6.7"></path><polyline points="21 3 21 9 15 9"></polyline></svg>
|
|
340
338
|
</button>
|
|
@@ -355,7 +353,7 @@
|
|
|
355
353
|
<!-- Tool Modal -->
|
|
356
354
|
<div id="modal-overlay" onclick="closeToolModal(event)">
|
|
357
355
|
<div id="tool-modal" onclick="event.stopPropagation()">
|
|
358
|
-
<h2
|
|
356
|
+
<div class="tool-modal-header"><div><h2 id="tool-modal-title">Create Session</h2><p id="tool-modal-skill" class="tool-modal-skill"></p></div><button class="icon-btn" type="button" onclick="closeToolModal()" aria-label="Close">×</button></div>
|
|
359
357
|
<div style="margin-bottom: 15px;">
|
|
360
358
|
<label style="display:block; margin-bottom: 8px; font-size: 14px; color: var(--text-dim);">Working Directory (Optional):</label>
|
|
361
359
|
<div style="margin-bottom: 8px; color: var(--text-dim); font-size: 12px; line-height: 1.4;">
|
|
@@ -369,6 +367,19 @@
|
|
|
369
367
|
</div>
|
|
370
368
|
</div>
|
|
371
369
|
|
|
370
|
+
<!-- Skill Hall Modal -->
|
|
371
|
+
<div id="skill-hall-overlay" onclick="closeSkillHall(event)">
|
|
372
|
+
<div id="skill-hall-modal" role="dialog" aria-modal="true" aria-labelledby="skill-hall-title" onclick="event.stopPropagation()">
|
|
373
|
+
<div class="skill-hall-header">
|
|
374
|
+
<div><h2 id="skill-hall-title">Skill Hall</h2><p>Select a private Skill to start a temporary Codex session.</p></div>
|
|
375
|
+
<button class="icon-btn" type="button" onclick="closeSkillHall()" aria-label="Close">×</button>
|
|
376
|
+
</div>
|
|
377
|
+
<input id="skill-hall-search" class="skill-hall-search" type="search" placeholder="Search skills…" aria-label="Search skills" oninput="filterSkillHall(this.value)">
|
|
378
|
+
<div id="skill-hall-status" class="skill-hall-status" aria-live="polite"></div>
|
|
379
|
+
<div id="skill-hall-grid" class="skill-hall-grid"></div>
|
|
380
|
+
</div>
|
|
381
|
+
</div>
|
|
382
|
+
|
|
372
383
|
<!-- Usage Source Modal -->
|
|
373
384
|
<div id="usage-source-overlay" onclick="closeUsageSourceModal(event)">
|
|
374
385
|
<div id="usage-source-modal" role="dialog" aria-modal="true" aria-labelledby="usage-source-modal-title" onclick="event.stopPropagation()">
|
|
@@ -436,6 +447,29 @@
|
|
|
436
447
|
<button id="serverchan-test-btn" class="small-btn" type="button" onclick="testServerChanSettings()">Send Test</button>
|
|
437
448
|
</div>
|
|
438
449
|
</section>
|
|
450
|
+
<section class="settings-section" aria-labelledby="skillhub-settings-title">
|
|
451
|
+
<div class="settings-section-label" id="skillhub-settings-title">Skills</div>
|
|
452
|
+
<div class="settings-provider-header">
|
|
453
|
+
<div><h3>SkillHub</h3><p>Connect Glad to a private Skill server.</p></div>
|
|
454
|
+
</div>
|
|
455
|
+
<div class="form-field">
|
|
456
|
+
<label for="skillhub-base-url">Server URL</label>
|
|
457
|
+
<input id="skillhub-base-url" type="url" autocomplete="off" placeholder="http://skillhub:10070">
|
|
458
|
+
<div class="serverchan-help">Remote servers must use HTTPS.</div>
|
|
459
|
+
</div>
|
|
460
|
+
<div class="form-field">
|
|
461
|
+
<label for="skillhub-token">API Token</label>
|
|
462
|
+
<input id="skillhub-token" type="password" autocomplete="off" placeholder="clh_...">
|
|
463
|
+
<div id="skillhub-token-hint" class="serverchan-help"></div>
|
|
464
|
+
</div>
|
|
465
|
+
<div id="skillhub-settings-status" class="serverchan-settings-status" aria-live="polite"></div>
|
|
466
|
+
<div class="serverchan-modal-actions">
|
|
467
|
+
<button id="skillhub-remove-btn" class="small-btn danger" type="button" onclick="removeSkillHubSettings()">Remove</button>
|
|
468
|
+
<span class="serverchan-action-spacer"></span>
|
|
469
|
+
<button id="skillhub-save-btn" class="small-btn primary" type="button" onclick="saveSkillHubSettings()">Save</button>
|
|
470
|
+
<button id="skillhub-test-btn" class="small-btn" type="button" onclick="testSkillHubSettings()">Test</button>
|
|
471
|
+
</div>
|
|
472
|
+
</section>
|
|
439
473
|
</div>
|
|
440
474
|
</div>
|
|
441
475
|
<div id="app-toast" role="status" aria-live="polite"></div>
|
|
@@ -498,6 +532,7 @@
|
|
|
498
532
|
<script src="core.js"></script>
|
|
499
533
|
<script src="layout.js"></script>
|
|
500
534
|
<script src="notifications.js"></script>
|
|
535
|
+
<script src="skillhub.js"></script>
|
|
501
536
|
<script src="claude.js"></script>
|
|
502
537
|
<script src="schedules.js"></script>
|
|
503
538
|
<script src="shell.js"></script>
|
package/lib/web/layout.js
CHANGED
|
@@ -1,19 +1,16 @@
|
|
|
1
|
-
const GLAD_SPLIT_QUERY =
|
|
2
|
-
const GLAD_SIDEBAR_KEY =
|
|
1
|
+
const GLAD_SPLIT_QUERY = window.gladLayout.splitQuery;
|
|
2
|
+
const GLAD_SIDEBAR_KEY = window.gladLayout.sidebarStorageKey;
|
|
3
3
|
|
|
4
4
|
function isSplitLayout() {
|
|
5
5
|
return window.matchMedia(GLAD_SPLIT_QUERY).matches;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
function clampSidebarWidth(value) {
|
|
9
|
-
|
|
10
|
-
return Math.min(max, Math.max(300, Number(value) || 348));
|
|
9
|
+
return window.gladLayout.clampSidebarWidth(value);
|
|
11
10
|
}
|
|
12
11
|
|
|
13
12
|
function applySidebarWidth(value) {
|
|
14
|
-
|
|
15
|
-
document.documentElement.style.setProperty('--sidebar-w', `${width}px`);
|
|
16
|
-
return width;
|
|
13
|
+
return window.gladLayout.applySidebarWidth(value);
|
|
17
14
|
}
|
|
18
15
|
|
|
19
16
|
function initializeResponsiveLayout() {
|
package/lib/web/notifications.js
CHANGED
|
@@ -50,6 +50,7 @@
|
|
|
50
50
|
event?.stopPropagation();
|
|
51
51
|
document.getElementById('settings-modal-overlay').style.display = 'flex';
|
|
52
52
|
if (typeof syncGladThemeControls === 'function') syncGladThemeControls();
|
|
53
|
+
if (typeof loadSkillHubSettings === 'function') void loadSkillHubSettings();
|
|
53
54
|
setServerChanStatus('Loading configuration…');
|
|
54
55
|
try {
|
|
55
56
|
const response = await fetchWithTimeout('/api/notifications/serverchan', {}, 10000);
|
package/lib/web/session.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
function joinSession(id, sessionName, toolKey = null) {
|
|
2
|
-
if (typeof
|
|
3
|
-
void
|
|
2
|
+
if (typeof clearComposerAttachments === 'function' && (selectedImageAttachments.length || selectedFileAttachments.length)) {
|
|
3
|
+
void clearComposerAttachments();
|
|
4
4
|
}
|
|
5
5
|
stopTimedInputTimers();
|
|
6
6
|
activeSessionId = id;
|
|
7
7
|
window.activeSessionId = id;
|
|
8
8
|
activeToolKey = toolKey;
|
|
9
|
+
if (typeof resetComposerSendState === 'function') resetComposerSendState();
|
|
9
10
|
clearTimeout(sessionPollTimer);
|
|
10
11
|
sessionPollTimer = null;
|
|
11
12
|
markCompletionRead(id);
|
|
@@ -25,7 +26,7 @@
|
|
|
25
26
|
initClaudeSession(sessionId);
|
|
26
27
|
return;
|
|
27
28
|
}
|
|
28
|
-
if (isCodexSession()
|
|
29
|
+
if (isCodexSession()) {
|
|
29
30
|
initCodexSession(sessionId);
|
|
30
31
|
return;
|
|
31
32
|
}
|
|
@@ -62,14 +63,6 @@
|
|
|
62
63
|
const msg = JSON.parse(e.data);
|
|
63
64
|
if (msg.type === 'output') term.write(msg.data);
|
|
64
65
|
if (msg.type === 'reset') term.reset();
|
|
65
|
-
if (msg.type === 'codex-event' && isCodexSession()) {
|
|
66
|
-
applyCodexEvent(msg.event);
|
|
67
|
-
if (msg.event?.type === 'presentation' && msg.event.presentation === 'structured') {
|
|
68
|
-
currentSocket.close();
|
|
69
|
-
currentSocket = null;
|
|
70
|
-
initCodexSession(sessionId);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
66
|
if (msg.type === 'exit') showLobby();
|
|
74
67
|
};
|
|
75
68
|
}
|
|
@@ -116,7 +109,7 @@
|
|
|
116
109
|
status: claudeStatus,
|
|
117
110
|
pendingPermissionCount: claudePendingPermissions.filter(item => item.status === 'pending').length,
|
|
118
111
|
canAbort: claudeStatus === 'thinking'
|
|
119
|
-
});
|
|
112
|
+
}, { providerStateReceived: true });
|
|
120
113
|
renderClaudeChat();
|
|
121
114
|
}
|
|
122
115
|
if (msg.type === 'claude-event') applyClaudeEvent(msg.event);
|
|
@@ -167,17 +160,10 @@
|
|
|
167
160
|
if (msg.type === 'codex-snapshot' && msg.snapshot) {
|
|
168
161
|
codexMessages = msg.snapshot.messages || [];
|
|
169
162
|
codexPendingPermissions = msg.snapshot.pendingPermissions || [];
|
|
170
|
-
applyCodexState(msg.snapshot.state || {});
|
|
163
|
+
applyCodexState(msg.snapshot.state || {}, { providerStateReceived: true });
|
|
171
164
|
}
|
|
172
165
|
if (msg.type === 'codex-detail-response' && msg.detail) applyCodexDetailResponse(msg);
|
|
173
|
-
if (msg.type === 'codex-event')
|
|
174
|
-
applyCodexEvent(msg.event);
|
|
175
|
-
if (msg.event?.type === 'presentation' && msg.event.presentation === 'terminal') {
|
|
176
|
-
currentSocket.close();
|
|
177
|
-
currentSocket = null;
|
|
178
|
-
initTerminal(sessionId);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
166
|
+
if (msg.type === 'codex-event') applyCodexEvent(msg.event);
|
|
181
167
|
if (msg.type === 'exit') showLobby();
|
|
182
168
|
};
|
|
183
169
|
}
|