claude-opencode-viewer 2.6.47 → 2.6.49
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/skills/code-review-expert/README.md +74 -0
- package/.agents/skills/code-review-expert/SKILL.md +156 -0
- package/.agents/skills/code-review-expert/agents/agent.yaml +7 -0
- package/.agents/skills/code-review-expert/references/code-quality-checklist.md +130 -0
- package/.agents/skills/code-review-expert/references/removal-plan.md +52 -0
- package/.agents/skills/code-review-expert/references/security-checklist.md +118 -0
- package/.agents/skills/code-review-expert/references/solid-checklist.md +65 -0
- package/index-pc.html +135 -45
- package/index.html +88 -39
- package/package.json +1 -1
- package/server.js +98 -7
- package/skills-lock.json +10 -0
- package/test-doc.md +86 -0
package/index-pc.html
CHANGED
|
@@ -410,6 +410,25 @@
|
|
|
410
410
|
animation: loading-dots 1.2s steps(4, end) infinite;
|
|
411
411
|
}
|
|
412
412
|
#init-overlay.visible { display: flex; }
|
|
413
|
+
#reconnect-overlay {
|
|
414
|
+
display: none;
|
|
415
|
+
position: absolute;
|
|
416
|
+
top: 0; left: 0; right: 0; bottom: 0;
|
|
417
|
+
background: rgba(10, 10, 10, 0.85);
|
|
418
|
+
color: #f59e0b;
|
|
419
|
+
align-items: center;
|
|
420
|
+
justify-content: center;
|
|
421
|
+
font-size: 16px;
|
|
422
|
+
font-weight: 600;
|
|
423
|
+
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
|
|
424
|
+
letter-spacing: 1px;
|
|
425
|
+
z-index: 10;
|
|
426
|
+
}
|
|
427
|
+
#reconnect-overlay::after {
|
|
428
|
+
content: '';
|
|
429
|
+
animation: loading-dots 1.2s steps(4, end) infinite;
|
|
430
|
+
}
|
|
431
|
+
#reconnect-overlay.visible { display: flex; }
|
|
413
432
|
|
|
414
433
|
|
|
415
434
|
/* 选择模式:原位文本层 */
|
|
@@ -721,6 +740,39 @@
|
|
|
721
740
|
line-height: 1.6;
|
|
722
741
|
color: #d4d4d4;
|
|
723
742
|
}
|
|
743
|
+
.docs-md-content {
|
|
744
|
+
font-size: 13px; line-height: 1.7; color: #d4d4d4; word-break: break-word;
|
|
745
|
+
}
|
|
746
|
+
.docs-md-content h1, .docs-md-content h2, .docs-md-content h3 {
|
|
747
|
+
margin: 16px 0 8px; color: #e0e0e0; border-bottom: 1px solid #333; padding-bottom: 4px;
|
|
748
|
+
}
|
|
749
|
+
.docs-md-content h1 { font-size: 1.4em; }
|
|
750
|
+
.docs-md-content h2 { font-size: 1.2em; }
|
|
751
|
+
.docs-md-content h3 { font-size: 1.05em; }
|
|
752
|
+
.docs-md-content p { margin: 8px 0; }
|
|
753
|
+
.docs-md-content pre {
|
|
754
|
+
background: #1a1a1a; border: 1px solid #333; border-radius: 4px;
|
|
755
|
+
padding: 10px; overflow-x: auto; margin: 8px 0; white-space: pre-wrap; word-break: break-word;
|
|
756
|
+
}
|
|
757
|
+
.docs-md-content code {
|
|
758
|
+
background: #1a1a1a; padding: 1px 4px; border-radius: 3px; font-size: 12px;
|
|
759
|
+
font-family: Menlo, Monaco, monospace;
|
|
760
|
+
}
|
|
761
|
+
.docs-md-content pre code { background: none; padding: 0; }
|
|
762
|
+
.docs-md-content ul, .docs-md-content ol { margin: 8px 0; padding-left: 20px; }
|
|
763
|
+
.docs-md-content li { margin: 4px 0; }
|
|
764
|
+
.docs-md-content blockquote {
|
|
765
|
+
border-left: 3px solid #444; margin: 8px 0; padding: 4px 12px; color: #999;
|
|
766
|
+
}
|
|
767
|
+
.docs-md-content table { border-collapse: collapse; margin: 8px 0; width: 100%; }
|
|
768
|
+
.docs-md-content th, .docs-md-content td {
|
|
769
|
+
border: 1px solid #333; padding: 6px 10px; text-align: left;
|
|
770
|
+
}
|
|
771
|
+
.docs-md-content th { background: #1a1a1a; }
|
|
772
|
+
.docs-md-content a { color: #58a6ff; text-decoration: none; }
|
|
773
|
+
.docs-md-content a:hover { text-decoration: underline; }
|
|
774
|
+
.docs-md-content img { max-width: 100%; }
|
|
775
|
+
.docs-md-content hr { border: none; border-top: 1px solid #333; margin: 12px 0; }
|
|
724
776
|
.docs-placeholder {
|
|
725
777
|
flex: 1;
|
|
726
778
|
display: flex;
|
|
@@ -786,6 +838,13 @@
|
|
|
786
838
|
color: #ffa198;
|
|
787
839
|
}
|
|
788
840
|
|
|
841
|
+
.diff-sign {
|
|
842
|
+
display: inline-block;
|
|
843
|
+
width: 12px;
|
|
844
|
+
font-weight: bold;
|
|
845
|
+
user-select: none;
|
|
846
|
+
}
|
|
847
|
+
|
|
789
848
|
.diff-line-hunk {
|
|
790
849
|
background: rgba(56, 139, 253, 0.1);
|
|
791
850
|
}
|
|
@@ -1038,6 +1097,7 @@
|
|
|
1038
1097
|
<div id="terminal" style="position:relative;">
|
|
1039
1098
|
<div id="switch-overlay">正在切换</div>
|
|
1040
1099
|
<div id="init-overlay"></div>
|
|
1100
|
+
<div id="reconnect-overlay">连接断开,正在重连</div>
|
|
1041
1101
|
<div id="select-text-layer">
|
|
1042
1102
|
<div id="select-hint">长按选择文本 · 点右上角 ✕ 返回终端</div>
|
|
1043
1103
|
<pre id="select-text-pre"></pre>
|
|
@@ -1051,6 +1111,8 @@
|
|
|
1051
1111
|
<div id="copy-toast">已复制</div>
|
|
1052
1112
|
<script src="https://cdn.jsdelivr.net/npm/@xterm/xterm@5.5.0/lib/xterm.min.js"></script>
|
|
1053
1113
|
<script src="https://cdn.jsdelivr.net/npm/@xterm/addon-webgl@0.18.0/lib/addon-webgl.min.js"></script>
|
|
1114
|
+
<script src="https://cdn.jsdelivr.net/npm/marked@15.0.7/marked.min.js"></script>
|
|
1115
|
+
<script src="https://cdn.jsdelivr.net/npm/dompurify@3.2.4/dist/purify.min.js"></script>
|
|
1054
1116
|
<script>
|
|
1055
1117
|
(function() {
|
|
1056
1118
|
var isMobile = /Mobi|Android|iPhone|iPad|iPod/i.test(navigator.userAgent);
|
|
@@ -1059,6 +1121,9 @@
|
|
|
1059
1121
|
var fontSize = isMobile ? 11 : 13;
|
|
1060
1122
|
var currentMode = 'claude';
|
|
1061
1123
|
var isTransitioning = false;
|
|
1124
|
+
var transitionEndTimer = null;
|
|
1125
|
+
var waitingInitData = false;
|
|
1126
|
+
var initDataTimer = null;
|
|
1062
1127
|
var isBufferReplay = true; // 初始缓冲区回放中,不弹 toast
|
|
1063
1128
|
var startupDialogShown = false;
|
|
1064
1129
|
|
|
@@ -1640,16 +1705,16 @@
|
|
|
1640
1705
|
|
|
1641
1706
|
ws.onopen = function() {
|
|
1642
1707
|
isBufferReplay = true;
|
|
1708
|
+
document.getElementById('reconnect-overlay').classList.remove('visible');
|
|
1643
1709
|
resize();
|
|
1644
1710
|
rebindTouchScroll();
|
|
1645
|
-
setTimeout(function() { isBufferReplay = false; },
|
|
1711
|
+
setTimeout(function() { isBufferReplay = false; }, 2000);
|
|
1646
1712
|
// 不在这里初始化,等 state 消息判断是否需要弹对话框
|
|
1647
1713
|
};
|
|
1648
1714
|
|
|
1649
1715
|
ws.onclose = function() {
|
|
1650
1716
|
ws = null;
|
|
1651
|
-
|
|
1652
|
-
term.write('\r\n \x1b[33m连接断开,正在重连...\x1b[0m\r\n');
|
|
1717
|
+
document.getElementById('reconnect-overlay').classList.add('visible');
|
|
1653
1718
|
setTimeout(connect, 2000);
|
|
1654
1719
|
};
|
|
1655
1720
|
|
|
@@ -1657,7 +1722,23 @@
|
|
|
1657
1722
|
try {
|
|
1658
1723
|
var msg = JSON.parse(e.data);
|
|
1659
1724
|
if (msg.type === 'data') {
|
|
1660
|
-
if (
|
|
1725
|
+
if (isTransitioning) {
|
|
1726
|
+
// 模式切换:TUI 渲染会发送一连串 data,debounce 等稳定后移除覆盖层
|
|
1727
|
+
term.write(msg.data);
|
|
1728
|
+
clearTimeout(transitionEndTimer);
|
|
1729
|
+
transitionEndTimer = setTimeout(function() {
|
|
1730
|
+
terminalEl.classList.remove('transitioning');
|
|
1731
|
+
isTransitioning = false;
|
|
1732
|
+
}, 2000);
|
|
1733
|
+
} else if (waitingInitData) {
|
|
1734
|
+
// 启动/恢复:同样 debounce 等 TUI 渲染完再隐藏启动覆盖层
|
|
1735
|
+
throttledWrite(msg.data);
|
|
1736
|
+
clearTimeout(initDataTimer);
|
|
1737
|
+
initDataTimer = setTimeout(function() {
|
|
1738
|
+
hideInitOverlay();
|
|
1739
|
+
waitingInitData = false;
|
|
1740
|
+
}, 2000);
|
|
1741
|
+
} else if (!isCreatingNewSession) {
|
|
1661
1742
|
throttledWrite(msg.data);
|
|
1662
1743
|
}
|
|
1663
1744
|
}
|
|
@@ -1671,7 +1752,9 @@
|
|
|
1671
1752
|
if (writeTimer) { cancelAnimationFrame(writeTimer); writeTimer = null; }
|
|
1672
1753
|
writeBuffer = '';
|
|
1673
1754
|
term.reset();
|
|
1674
|
-
|
|
1755
|
+
// 更新模式但保留覆盖层,等首条 data 到达后再移除
|
|
1756
|
+
currentMode = msg.mode;
|
|
1757
|
+
modeSelect.value = msg.mode;
|
|
1675
1758
|
if (msg.buffer) {
|
|
1676
1759
|
term.write(msg.buffer);
|
|
1677
1760
|
}
|
|
@@ -1683,6 +1766,11 @@
|
|
|
1683
1766
|
term.clear();
|
|
1684
1767
|
}
|
|
1685
1768
|
else if (msg.type === 'state') {
|
|
1769
|
+
// 重连时清掉旧终端内容,防止重复叠加
|
|
1770
|
+
if (writeTimer) { cancelAnimationFrame(writeTimer); writeTimer = null; }
|
|
1771
|
+
writeBuffer = '';
|
|
1772
|
+
term.reset();
|
|
1773
|
+
term.clear();
|
|
1686
1774
|
if (msg.mode) {
|
|
1687
1775
|
currentMode = msg.mode;
|
|
1688
1776
|
modeSelect.value = msg.mode;
|
|
@@ -1698,7 +1786,8 @@
|
|
|
1698
1786
|
}
|
|
1699
1787
|
}
|
|
1700
1788
|
else if (msg.type === 'restored') {
|
|
1701
|
-
|
|
1789
|
+
// 不立即隐藏覆盖层,等 data debounce 后隐藏
|
|
1790
|
+
waitingInitData = true;
|
|
1702
1791
|
if (writeTimer) { cancelAnimationFrame(writeTimer); writeTimer = null; }
|
|
1703
1792
|
writeBuffer = '';
|
|
1704
1793
|
term.reset();
|
|
@@ -1711,12 +1800,13 @@
|
|
|
1711
1800
|
term.write('恢复失败: ' + msg.error + '\r\n');
|
|
1712
1801
|
}
|
|
1713
1802
|
else if (msg.type === 'started') {
|
|
1714
|
-
|
|
1803
|
+
// 不立即隐藏覆盖层,等 data debounce 后隐藏
|
|
1804
|
+
waitingInitData = true;
|
|
1715
1805
|
rebindTouchScroll();
|
|
1716
1806
|
preloadData();
|
|
1717
1807
|
}
|
|
1718
1808
|
else if (msg.type === 'new-session-ok') {
|
|
1719
|
-
|
|
1809
|
+
waitingInitData = true;
|
|
1720
1810
|
if (writeTimer) { cancelAnimationFrame(writeTimer); writeTimer = null; }
|
|
1721
1811
|
writeBuffer = '';
|
|
1722
1812
|
term.reset();
|
|
@@ -1739,7 +1829,7 @@
|
|
|
1739
1829
|
} else {
|
|
1740
1830
|
cacheRestored = true;
|
|
1741
1831
|
}
|
|
1742
|
-
},
|
|
1832
|
+
}, 2000);
|
|
1743
1833
|
}
|
|
1744
1834
|
|
|
1745
1835
|
window.addEventListener('resize', resize);
|
|
@@ -1908,7 +1998,7 @@
|
|
|
1908
1998
|
setTimeout(function() {
|
|
1909
1999
|
sessions = sessions.filter(function(s) { return s.id !== sessionId; });
|
|
1910
2000
|
renderSessions();
|
|
1911
|
-
},
|
|
2001
|
+
}, 2000);
|
|
1912
2002
|
} else {
|
|
1913
2003
|
itemEl.style.opacity = '';
|
|
1914
2004
|
itemEl.style.pointerEvents = '';
|
|
@@ -2108,7 +2198,7 @@
|
|
|
2108
2198
|
itemEl.style.padding = '0 12px';
|
|
2109
2199
|
itemEl.style.margin = '0';
|
|
2110
2200
|
itemEl.style.opacity = '0';
|
|
2111
|
-
setTimeout(function() { itemEl.remove(); },
|
|
2201
|
+
setTimeout(function() { itemEl.remove(); }, 2000);
|
|
2112
2202
|
} else {
|
|
2113
2203
|
itemEl.style.opacity = '';
|
|
2114
2204
|
itemEl.style.pointerEvents = '';
|
|
@@ -2389,38 +2479,29 @@
|
|
|
2389
2479
|
|
|
2390
2480
|
function loadDiffContent(file) {
|
|
2391
2481
|
var area = document.getElementById('git-diff-content-area');
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
} else if (d.unified_diff) {
|
|
2413
|
-
html += renderUnifiedDiff(d.unified_diff);
|
|
2414
|
-
} else {
|
|
2415
|
-
html += '<div class="git-diff-loading" style="color:#666;">无变更内容</div>';
|
|
2416
|
-
}
|
|
2482
|
+
var d = diffChanges.find(function(c) { return c.file === file; });
|
|
2483
|
+
if (!d) {
|
|
2484
|
+
area.innerHTML = '<div class="git-diff-error">无 diff 数据</div>';
|
|
2485
|
+
return;
|
|
2486
|
+
}
|
|
2487
|
+
var html = '<div class="git-diff-content-header">';
|
|
2488
|
+
html += '<span class="git-diff-content-path">' + escapeHtml(d.file) + '</span>';
|
|
2489
|
+
html += '<span class="git-diff-badge">' + (d.is_new ? 'NEW' : d.is_deleted ? 'DEL' : 'DIFF') + '</span>';
|
|
2490
|
+
html += '</div>';
|
|
2491
|
+
html += '<div class="git-diff-content-scroll">';
|
|
2492
|
+
|
|
2493
|
+
if (d.is_binary) {
|
|
2494
|
+
html += '<div class="git-diff-loading" style="font-style:italic;">二进制文件</div>';
|
|
2495
|
+
} else if (d.is_large) {
|
|
2496
|
+
html += '<div class="git-diff-loading" style="color:#e2c08d;">文件过大: ' + (d.size / (1024 * 1024)).toFixed(2) + ' MB</div>';
|
|
2497
|
+
} else if (d.unified_diff) {
|
|
2498
|
+
html += renderUnifiedDiff(d.unified_diff);
|
|
2499
|
+
} else {
|
|
2500
|
+
html += '<div class="git-diff-loading" style="color:#666;">无变更内容</div>';
|
|
2501
|
+
}
|
|
2417
2502
|
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
})
|
|
2421
|
-
.catch(function(err) {
|
|
2422
|
-
area.innerHTML = '<div class="git-diff-error">加载失败: ' + escapeHtml(err.message) + '</div>';
|
|
2423
|
-
});
|
|
2503
|
+
html += '</div>';
|
|
2504
|
+
area.innerHTML = html;
|
|
2424
2505
|
}
|
|
2425
2506
|
|
|
2426
2507
|
function renderUnifiedDiff(diffText) {
|
|
@@ -2453,13 +2534,13 @@
|
|
|
2453
2534
|
html += '<tr class="diff-line diff-line-add">';
|
|
2454
2535
|
html += '<td class="diff-line-num"></td>';
|
|
2455
2536
|
html += '<td class="diff-line-num">' + newLine + '</td>';
|
|
2456
|
-
html += '<td class="diff-line-content">' + escapeHtml(line.substring(1)) + '</td></tr>';
|
|
2537
|
+
html += '<td class="diff-line-content"><span class="diff-sign">+</span>' + escapeHtml(line.substring(1)) + '</td></tr>';
|
|
2457
2538
|
newLine++;
|
|
2458
2539
|
} else if (line.startsWith('-')) {
|
|
2459
2540
|
html += '<tr class="diff-line diff-line-del">';
|
|
2460
2541
|
html += '<td class="diff-line-num">' + oldLine + '</td>';
|
|
2461
2542
|
html += '<td class="diff-line-num"></td>';
|
|
2462
|
-
html += '<td class="diff-line-content">' + escapeHtml(line.substring(1)) + '</td></tr>';
|
|
2543
|
+
html += '<td class="diff-line-content"><span class="diff-sign">-</span>' + escapeHtml(line.substring(1)) + '</td></tr>';
|
|
2463
2544
|
oldLine++;
|
|
2464
2545
|
} else if (line.startsWith(' ') || (line === '' && i < lines.length - 1)) {
|
|
2465
2546
|
html += '<tr class="diff-line">';
|
|
@@ -2565,6 +2646,15 @@
|
|
|
2565
2646
|
});
|
|
2566
2647
|
}
|
|
2567
2648
|
|
|
2649
|
+
function formatDocContent(text) {
|
|
2650
|
+
if (!text) return '';
|
|
2651
|
+
try {
|
|
2652
|
+
return DOMPurify.sanitize(marked.parse(text, { breaks: true }));
|
|
2653
|
+
} catch (e) {
|
|
2654
|
+
return '<pre>' + escapeHtml(text) + '</pre>';
|
|
2655
|
+
}
|
|
2656
|
+
}
|
|
2657
|
+
|
|
2568
2658
|
function loadDocContent(file) {
|
|
2569
2659
|
var area = document.getElementById('docs-content-area');
|
|
2570
2660
|
area.innerHTML = '<div class="docs-loading">加载中...</div>';
|
|
@@ -2575,7 +2665,7 @@
|
|
|
2575
2665
|
area.innerHTML = '<div class="docs-loading" style="color:#ff6b6b;">' + escapeHtml(data.error) + '</div>';
|
|
2576
2666
|
return;
|
|
2577
2667
|
}
|
|
2578
|
-
area.innerHTML = '<
|
|
2668
|
+
area.innerHTML = '<div class="docs-md-content">' + formatDocContent(data.content) + '</div>';
|
|
2579
2669
|
})
|
|
2580
2670
|
.catch(function(err) {
|
|
2581
2671
|
area.innerHTML = '<div class="docs-loading" style="color:#ff6b6b;">加载失败</div>';
|
package/index.html
CHANGED
|
@@ -809,6 +809,39 @@
|
|
|
809
809
|
line-height: 1.6;
|
|
810
810
|
color: #d4d4d4;
|
|
811
811
|
}
|
|
812
|
+
.docs-md-content {
|
|
813
|
+
font-size: 13px; line-height: 1.7; color: #d4d4d4; word-break: break-word;
|
|
814
|
+
}
|
|
815
|
+
.docs-md-content h1, .docs-md-content h2, .docs-md-content h3 {
|
|
816
|
+
margin: 16px 0 8px; color: #e0e0e0; border-bottom: 1px solid #333; padding-bottom: 4px;
|
|
817
|
+
}
|
|
818
|
+
.docs-md-content h1 { font-size: 1.4em; }
|
|
819
|
+
.docs-md-content h2 { font-size: 1.2em; }
|
|
820
|
+
.docs-md-content h3 { font-size: 1.05em; }
|
|
821
|
+
.docs-md-content p { margin: 8px 0; }
|
|
822
|
+
.docs-md-content pre {
|
|
823
|
+
background: #1a1a1a; border: 1px solid #333; border-radius: 4px;
|
|
824
|
+
padding: 10px; overflow-x: auto; margin: 8px 0; white-space: pre-wrap; word-break: break-word;
|
|
825
|
+
}
|
|
826
|
+
.docs-md-content code {
|
|
827
|
+
background: #1a1a1a; padding: 1px 4px; border-radius: 3px; font-size: 12px;
|
|
828
|
+
font-family: Menlo, Monaco, monospace;
|
|
829
|
+
}
|
|
830
|
+
.docs-md-content pre code { background: none; padding: 0; }
|
|
831
|
+
.docs-md-content ul, .docs-md-content ol { margin: 8px 0; padding-left: 20px; }
|
|
832
|
+
.docs-md-content li { margin: 4px 0; }
|
|
833
|
+
.docs-md-content blockquote {
|
|
834
|
+
border-left: 3px solid #444; margin: 8px 0; padding: 4px 12px; color: #999;
|
|
835
|
+
}
|
|
836
|
+
.docs-md-content table { border-collapse: collapse; margin: 8px 0; width: 100%; }
|
|
837
|
+
.docs-md-content th, .docs-md-content td {
|
|
838
|
+
border: 1px solid #333; padding: 6px 10px; text-align: left;
|
|
839
|
+
}
|
|
840
|
+
.docs-md-content th { background: #1a1a1a; }
|
|
841
|
+
.docs-md-content a { color: #58a6ff; text-decoration: none; }
|
|
842
|
+
.docs-md-content a:hover { text-decoration: underline; }
|
|
843
|
+
.docs-md-content img { max-width: 100%; }
|
|
844
|
+
.docs-md-content hr { border: none; border-top: 1px solid #333; margin: 12px 0; }
|
|
812
845
|
.docs-placeholder {
|
|
813
846
|
flex: 1;
|
|
814
847
|
display: flex;
|
|
@@ -874,6 +907,13 @@
|
|
|
874
907
|
color: #ffa198;
|
|
875
908
|
}
|
|
876
909
|
|
|
910
|
+
.diff-sign {
|
|
911
|
+
display: inline-block;
|
|
912
|
+
width: 12px;
|
|
913
|
+
font-weight: bold;
|
|
914
|
+
user-select: none;
|
|
915
|
+
}
|
|
916
|
+
|
|
877
917
|
.diff-line-hunk {
|
|
878
918
|
background: rgba(56, 139, 253, 0.1);
|
|
879
919
|
}
|
|
@@ -1120,6 +1160,7 @@
|
|
|
1120
1160
|
var fontSize = isMobile ? 11 : 13;
|
|
1121
1161
|
var currentMode = 'claude';
|
|
1122
1162
|
var isTransitioning = false;
|
|
1163
|
+
var transitionEndTimer = null;
|
|
1123
1164
|
var mobileInitSent = false;
|
|
1124
1165
|
|
|
1125
1166
|
var term = new Terminal({
|
|
@@ -1681,7 +1722,14 @@
|
|
|
1681
1722
|
var msg = JSON.parse(e.data);
|
|
1682
1723
|
if (msg.type === 'data') {
|
|
1683
1724
|
hideLoading();
|
|
1684
|
-
if (
|
|
1725
|
+
if (isTransitioning) {
|
|
1726
|
+
term.write(msg.data);
|
|
1727
|
+
clearTimeout(transitionEndTimer);
|
|
1728
|
+
transitionEndTimer = setTimeout(function() {
|
|
1729
|
+
terminalEl.classList.remove('transitioning');
|
|
1730
|
+
isTransitioning = false;
|
|
1731
|
+
}, 2000);
|
|
1732
|
+
} else if (!isCreatingNewSession) {
|
|
1685
1733
|
throttledWrite(msg.data);
|
|
1686
1734
|
}
|
|
1687
1735
|
}
|
|
@@ -1737,7 +1785,8 @@
|
|
|
1737
1785
|
if (writeTimer) { cancelAnimationFrame(writeTimer); writeTimer = null; }
|
|
1738
1786
|
writeBuffer = '';
|
|
1739
1787
|
term.reset();
|
|
1740
|
-
|
|
1788
|
+
currentMode = msg.mode;
|
|
1789
|
+
modeSelect.value = msg.mode;
|
|
1741
1790
|
if (msg.buffer) {
|
|
1742
1791
|
term.write(msg.buffer);
|
|
1743
1792
|
}
|
|
@@ -1792,7 +1841,7 @@
|
|
|
1792
1841
|
} else {
|
|
1793
1842
|
cacheRestored = true;
|
|
1794
1843
|
}
|
|
1795
|
-
},
|
|
1844
|
+
}, 2000);
|
|
1796
1845
|
}
|
|
1797
1846
|
|
|
1798
1847
|
window.addEventListener('resize', resize);
|
|
@@ -2031,7 +2080,7 @@
|
|
|
2031
2080
|
setTimeout(function() {
|
|
2032
2081
|
sessions = sessions.filter(function(s) { return s.id !== sessionId; });
|
|
2033
2082
|
renderSessions();
|
|
2034
|
-
},
|
|
2083
|
+
}, 2000);
|
|
2035
2084
|
} else {
|
|
2036
2085
|
itemEl.style.opacity = '';
|
|
2037
2086
|
itemEl.style.pointerEvents = '';
|
|
@@ -2234,7 +2283,7 @@
|
|
|
2234
2283
|
itemEl.style.padding = '0 12px';
|
|
2235
2284
|
itemEl.style.margin = '0';
|
|
2236
2285
|
itemEl.style.opacity = '0';
|
|
2237
|
-
setTimeout(function() { itemEl.remove(); },
|
|
2286
|
+
setTimeout(function() { itemEl.remove(); }, 2000);
|
|
2238
2287
|
} else {
|
|
2239
2288
|
itemEl.style.opacity = '';
|
|
2240
2289
|
itemEl.style.pointerEvents = '';
|
|
@@ -2624,38 +2673,29 @@
|
|
|
2624
2673
|
|
|
2625
2674
|
function loadDiffContent(file) {
|
|
2626
2675
|
var area = document.getElementById('git-diff-content-area');
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
} else if (d.unified_diff) {
|
|
2648
|
-
html += renderUnifiedDiff(d.unified_diff);
|
|
2649
|
-
} else {
|
|
2650
|
-
html += '<div class="git-diff-loading" style="color:#666;">无变更内容</div>';
|
|
2651
|
-
}
|
|
2676
|
+
var d = diffChanges.find(function(c) { return c.file === file; });
|
|
2677
|
+
if (!d) {
|
|
2678
|
+
area.innerHTML = '<div class="git-diff-error">无 diff 数据</div>';
|
|
2679
|
+
return;
|
|
2680
|
+
}
|
|
2681
|
+
var html = '<div class="git-diff-content-header">';
|
|
2682
|
+
html += '<span class="git-diff-content-path">' + escapeHtml(d.file) + '</span>';
|
|
2683
|
+
html += '<span class="git-diff-badge">' + (d.is_new ? 'NEW' : d.is_deleted ? 'DEL' : 'DIFF') + '</span>';
|
|
2684
|
+
html += '</div>';
|
|
2685
|
+
html += '<div class="git-diff-content-scroll">';
|
|
2686
|
+
|
|
2687
|
+
if (d.is_binary) {
|
|
2688
|
+
html += '<div class="git-diff-loading" style="font-style:italic;">二进制文件</div>';
|
|
2689
|
+
} else if (d.is_large) {
|
|
2690
|
+
html += '<div class="git-diff-loading" style="color:#e2c08d;">文件过大: ' + (d.size / (1024 * 1024)).toFixed(2) + ' MB</div>';
|
|
2691
|
+
} else if (d.unified_diff) {
|
|
2692
|
+
html += renderUnifiedDiff(d.unified_diff);
|
|
2693
|
+
} else {
|
|
2694
|
+
html += '<div class="git-diff-loading" style="color:#666;">无变更内容</div>';
|
|
2695
|
+
}
|
|
2652
2696
|
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
})
|
|
2656
|
-
.catch(function(err) {
|
|
2657
|
-
area.innerHTML = '<div class="git-diff-error">加载失败: ' + escapeHtml(err.message) + '</div>';
|
|
2658
|
-
});
|
|
2697
|
+
html += '</div>';
|
|
2698
|
+
area.innerHTML = html;
|
|
2659
2699
|
}
|
|
2660
2700
|
|
|
2661
2701
|
function renderUnifiedDiff(diffText) {
|
|
@@ -2688,13 +2728,13 @@
|
|
|
2688
2728
|
html += '<tr class="diff-line diff-line-add">';
|
|
2689
2729
|
html += '<td class="diff-line-num"></td>';
|
|
2690
2730
|
html += '<td class="diff-line-num">' + newLine + '</td>';
|
|
2691
|
-
html += '<td class="diff-line-content">' + escapeHtml(line.substring(1)) + '</td></tr>';
|
|
2731
|
+
html += '<td class="diff-line-content"><span class="diff-sign">+</span>' + escapeHtml(line.substring(1)) + '</td></tr>';
|
|
2692
2732
|
newLine++;
|
|
2693
2733
|
} else if (line.startsWith('-')) {
|
|
2694
2734
|
html += '<tr class="diff-line diff-line-del">';
|
|
2695
2735
|
html += '<td class="diff-line-num">' + oldLine + '</td>';
|
|
2696
2736
|
html += '<td class="diff-line-num"></td>';
|
|
2697
|
-
html += '<td class="diff-line-content">' + escapeHtml(line.substring(1)) + '</td></tr>';
|
|
2737
|
+
html += '<td class="diff-line-content"><span class="diff-sign">-</span>' + escapeHtml(line.substring(1)) + '</td></tr>';
|
|
2698
2738
|
oldLine++;
|
|
2699
2739
|
} else if (line.startsWith(' ') || (line === '' && i < lines.length - 1)) {
|
|
2700
2740
|
html += '<tr class="diff-line">';
|
|
@@ -2802,6 +2842,15 @@
|
|
|
2802
2842
|
});
|
|
2803
2843
|
}
|
|
2804
2844
|
|
|
2845
|
+
function formatDocContent(text) {
|
|
2846
|
+
if (!text) return '';
|
|
2847
|
+
try {
|
|
2848
|
+
return DOMPurify.sanitize(marked.parse(text, { breaks: true }));
|
|
2849
|
+
} catch (e) {
|
|
2850
|
+
return '<pre>' + escapeHtml(text) + '</pre>';
|
|
2851
|
+
}
|
|
2852
|
+
}
|
|
2853
|
+
|
|
2805
2854
|
function loadDocContent(file) {
|
|
2806
2855
|
var area = document.getElementById('docs-content-area');
|
|
2807
2856
|
area.innerHTML = '<div class="docs-loading">加载中...</div>';
|
|
@@ -2812,7 +2861,7 @@
|
|
|
2812
2861
|
area.innerHTML = '<div class="docs-loading" style="color:#ff6b6b;">' + escapeHtml(data.error) + '</div>';
|
|
2813
2862
|
return;
|
|
2814
2863
|
}
|
|
2815
|
-
area.innerHTML = '<
|
|
2864
|
+
area.innerHTML = '<div class="docs-md-content">' + formatDocContent(data.content) + '</div>';
|
|
2816
2865
|
})
|
|
2817
2866
|
.catch(function() {
|
|
2818
2867
|
area.innerHTML = '<div class="docs-loading" style="color:#ff6b6b;">加载失败</div>';
|