claude-opencode-viewer 2.6.45 → 2.6.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/index-pc.html +2 -0
- package/index.html +142 -163
- package/package.json +1 -1
package/index-pc.html
CHANGED
|
@@ -1591,6 +1591,8 @@
|
|
|
1591
1591
|
currentMode = mode;
|
|
1592
1592
|
modeSelect.value = mode;
|
|
1593
1593
|
document.getElementById('mode-label').textContent = '';
|
|
1594
|
+
var label = mode === 'claude' ? 'Claude' : 'OpenCode';
|
|
1595
|
+
term.write('\r\n 正在启动 ' + label + (sessionId ? '(恢复会话)' : '') + '...\r\n');
|
|
1594
1596
|
var msg = { type: 'init', mode: mode };
|
|
1595
1597
|
if (sessionId) msg.sessionId = sessionId;
|
|
1596
1598
|
ws.send(JSON.stringify(msg));
|
package/index.html
CHANGED
|
@@ -30,11 +30,14 @@
|
|
|
30
30
|
}
|
|
31
31
|
#loading-overlay.hidden { display: none !important; }
|
|
32
32
|
</style>
|
|
33
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@xterm/xterm@
|
|
33
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@xterm/xterm@6.0.0/css/xterm.min.css" media="print" onload="this.media='all'">
|
|
34
34
|
<style>
|
|
35
35
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
36
36
|
html, body { margin: 0; padding: 0; overflow: hidden; }
|
|
37
37
|
|
|
38
|
+
/* 隐藏 xterm textarea 的闪烁光标 (cc-viewer TerminalPanel.module.css) */
|
|
39
|
+
.xterm-helper-textarea { caret-color: transparent !important; }
|
|
40
|
+
|
|
38
41
|
/* 参考 cc-viewer 的 App.jsx 行 1319: 移动端容器使用 100vw/100vh */
|
|
39
42
|
#layout {
|
|
40
43
|
position: fixed;
|
|
@@ -426,6 +429,7 @@
|
|
|
426
429
|
display: flex;
|
|
427
430
|
gap: 6px;
|
|
428
431
|
padding: 8px 10px;
|
|
432
|
+
padding-bottom: calc(8px + env(safe-area-inset-bottom, 0px));
|
|
429
433
|
background: #111;
|
|
430
434
|
border-top: 1px solid #222;
|
|
431
435
|
overflow-x: auto;
|
|
@@ -444,22 +448,19 @@
|
|
|
444
448
|
font-family: Menlo, Monaco, monospace;
|
|
445
449
|
cursor: pointer;
|
|
446
450
|
user-select: none;
|
|
451
|
+
-webkit-user-select: none;
|
|
447
452
|
-webkit-tap-highlight-color: transparent;
|
|
453
|
+
-webkit-touch-callout: none;
|
|
448
454
|
touch-action: pan-x;
|
|
449
455
|
min-width: 44px;
|
|
450
456
|
min-height: 44px;
|
|
451
457
|
display: flex;
|
|
452
|
-
align-items:
|
|
453
|
-
padding-top: 40px;
|
|
458
|
+
align-items: center;
|
|
454
459
|
justify-content: center;
|
|
455
|
-
border: none;
|
|
456
460
|
outline: none;
|
|
457
|
-
-webkit-user-select: none;
|
|
458
|
-
-moz-user-select: none;
|
|
459
|
-
-ms-user-select: none;
|
|
460
461
|
}
|
|
461
462
|
|
|
462
|
-
.virtual-key
|
|
463
|
+
.virtual-key-pressed {
|
|
463
464
|
background: #333;
|
|
464
465
|
border-color: #555;
|
|
465
466
|
color: #fff;
|
|
@@ -468,11 +469,12 @@
|
|
|
468
469
|
/* 消息查看器 */
|
|
469
470
|
#message-viewer {
|
|
470
471
|
display: none;
|
|
471
|
-
position:
|
|
472
|
-
|
|
472
|
+
position: absolute;
|
|
473
|
+
inset: 0;
|
|
473
474
|
background: #0a0a0a;
|
|
474
475
|
z-index: 1000;
|
|
475
476
|
flex-direction: column;
|
|
477
|
+
overflow: hidden;
|
|
476
478
|
}
|
|
477
479
|
#message-viewer.visible {
|
|
478
480
|
display: flex;
|
|
@@ -506,61 +508,68 @@
|
|
|
506
508
|
}
|
|
507
509
|
#msg-viewer-content {
|
|
508
510
|
flex: 1;
|
|
509
|
-
overflow
|
|
511
|
+
overflow: auto;
|
|
510
512
|
-webkit-overflow-scrolling: touch;
|
|
513
|
+
overscroll-behavior: contain;
|
|
511
514
|
padding: 12px;
|
|
512
515
|
}
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
border:
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
}
|
|
523
|
-
.msg-
|
|
524
|
-
|
|
525
|
-
border-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
text-transform: uppercase;
|
|
532
|
-
margin-bottom: 6px;
|
|
533
|
-
}
|
|
534
|
-
.msg-text {
|
|
535
|
-
color: #ddd;
|
|
536
|
-
font-size: 13px;
|
|
537
|
-
line-height: 1.6;
|
|
516
|
+
/* 聊天气泡布局(参考 cc-viewer ChatMessage) */
|
|
517
|
+
.msg-row { display: flex; gap: 8px; padding: 6px 12px; align-items: flex-start; }
|
|
518
|
+
.msg-row-end { display: flex; gap: 8px; padding: 6px 12px; justify-content: flex-end; align-items: flex-start; }
|
|
519
|
+
.msg-avatar {
|
|
520
|
+
width: 28px; height: 28px; border-radius: 50%; flex-shrink: 0;
|
|
521
|
+
display: flex; align-items: center; justify-content: center;
|
|
522
|
+
font-size: 12px; color: #fff; font-weight: 600;
|
|
523
|
+
}
|
|
524
|
+
.msg-content-col { min-width: 0; max-width: 85%; }
|
|
525
|
+
.msg-label { font-size: 10px; color: #888; margin-bottom: 2px; }
|
|
526
|
+
.msg-label-right { text-align: right; }
|
|
527
|
+
.msg-bubble {
|
|
528
|
+
border-radius: 8px; border: 1px solid #333; padding: 8px 12px;
|
|
529
|
+
font-size: 13px; line-height: 1.6; word-break: break-word;
|
|
530
|
+
-webkit-user-select: text; user-select: text;
|
|
531
|
+
}
|
|
532
|
+
.msg-bubble-user {
|
|
533
|
+
background: #1668dc; color: #fff; border-color: #4a9eff;
|
|
538
534
|
white-space: pre-wrap;
|
|
539
|
-
word-break: break-word;
|
|
540
|
-
-webkit-user-select: text;
|
|
541
|
-
user-select: text;
|
|
542
|
-
}
|
|
543
|
-
.msg-text pre {
|
|
544
|
-
background: #0d0d0d;
|
|
545
|
-
border: 1px solid #333;
|
|
546
|
-
border-radius: 4px;
|
|
547
|
-
padding: 8px;
|
|
548
|
-
overflow-x: auto;
|
|
549
|
-
font-family: 'Cascadia Code', 'Fira Code', 'Consolas', monospace;
|
|
550
|
-
font-size: 12px;
|
|
551
|
-
line-height: 1.4;
|
|
552
|
-
white-space: pre;
|
|
553
|
-
-webkit-overflow-scrolling: touch;
|
|
554
|
-
}
|
|
555
|
-
.msg-tool {
|
|
556
|
-
margin-top: 6px;
|
|
557
|
-
padding: 6px 8px;
|
|
558
|
-
background: #1a1a0a;
|
|
559
|
-
border: 1px solid #333;
|
|
560
|
-
border-radius: 4px;
|
|
561
|
-
font-size: 11px;
|
|
562
|
-
color: #f0ad4e;
|
|
563
535
|
}
|
|
536
|
+
.msg-bubble-assistant {
|
|
537
|
+
background: #111; color: #ddd; border-color: #2a2a2a;
|
|
538
|
+
}
|
|
539
|
+
/* 工具标签 */
|
|
540
|
+
.msg-tools { display: flex; flex-wrap: wrap; gap: 4px; padding: 2px 0 4px; }
|
|
541
|
+
.msg-tool-tag {
|
|
542
|
+
display: inline-block; padding: 2px 8px; border-radius: 4px;
|
|
543
|
+
background: #1a1a2a; border: 1px solid #333; color: #aaa;
|
|
544
|
+
font-size: 11px; white-space: nowrap;
|
|
545
|
+
}
|
|
546
|
+
/* Markdown 富文本(助手气泡内) */
|
|
547
|
+
.msg-bubble-assistant pre {
|
|
548
|
+
background: #0d1117; border: 1px solid #2a2a2a; border-radius: 6px;
|
|
549
|
+
padding: 12px; overflow-x: auto; font-size: 13px; line-height: 1.5;
|
|
550
|
+
}
|
|
551
|
+
.msg-bubble-assistant code {
|
|
552
|
+
background: #14141F; padding: 2px 6px; border-radius: 4px;
|
|
553
|
+
font-size: 13px; color: #aeafff;
|
|
554
|
+
}
|
|
555
|
+
.msg-bubble-assistant pre code { background: none; padding: 0; color: inherit; }
|
|
556
|
+
.msg-bubble-assistant p { margin: 6px 0; }
|
|
557
|
+
.msg-bubble-assistant ul, .msg-bubble-assistant ol { padding-left: 20px; margin: 6px 0; }
|
|
558
|
+
.msg-bubble-assistant li { margin: 2px 0; }
|
|
559
|
+
.msg-bubble-assistant h1, .msg-bubble-assistant h2, .msg-bubble-assistant h3 { margin: 12px 0 6px 0; color: #fff; }
|
|
560
|
+
.msg-bubble-assistant h1 { font-size: 1.3em; }
|
|
561
|
+
.msg-bubble-assistant h2 { font-size: 1.15em; }
|
|
562
|
+
.msg-bubble-assistant h3 { font-size: 1.05em; }
|
|
563
|
+
.msg-bubble-assistant blockquote {
|
|
564
|
+
border-left: 3px solid #3b82f6; margin: 8px 0; padding: 4px 12px; color: #888;
|
|
565
|
+
}
|
|
566
|
+
.msg-bubble-assistant table { border-collapse: collapse; margin: 8px 0; font-size: 13px; }
|
|
567
|
+
.msg-bubble-assistant th, .msg-bubble-assistant td { border: 1px solid #6b7280; padding: 6px 10px; }
|
|
568
|
+
.msg-bubble-assistant th { background: #1e1e1e; color: #fff; }
|
|
569
|
+
.msg-bubble-assistant a { color: #60a5fa; }
|
|
570
|
+
.msg-bubble-assistant img { max-width: 100%; height: auto; border-radius: 6px; }
|
|
571
|
+
.msg-bubble-assistant hr { border: none; border-top: 1px solid #2a2a2a; margin: 12px 0; }
|
|
572
|
+
.msg-bubble-assistant strong { color: #e5e5e5; }
|
|
564
573
|
.msg-empty {
|
|
565
574
|
text-align: center;
|
|
566
575
|
padding: 40px 20px;
|
|
@@ -1102,21 +1111,24 @@
|
|
|
1102
1111
|
</div>
|
|
1103
1112
|
</div>
|
|
1104
1113
|
</div>
|
|
1105
|
-
</div>
|
|
1106
1114
|
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1115
|
+
<div id="message-viewer">
|
|
1116
|
+
<div id="msg-viewer-header">
|
|
1117
|
+
<span>会话消息</span>
|
|
1118
|
+
<button id="msg-viewer-close">✕</button>
|
|
1119
|
+
</div>
|
|
1120
|
+
<div id="msg-viewer-content">
|
|
1121
|
+
<div class="msg-empty">加载中...</div>
|
|
1122
|
+
</div>
|
|
1114
1123
|
</div>
|
|
1115
1124
|
</div>
|
|
1116
1125
|
|
|
1117
1126
|
<div id="copy-toast">已复制</div>
|
|
1118
|
-
<script src="https://cdn.jsdelivr.net/npm/@xterm/xterm@
|
|
1119
|
-
<script src="https://cdn.jsdelivr.net/npm/@xterm/addon-webgl@0.
|
|
1127
|
+
<script src="https://cdn.jsdelivr.net/npm/@xterm/xterm@6.0.0/lib/xterm.min.js"></script>
|
|
1128
|
+
<script src="https://cdn.jsdelivr.net/npm/@xterm/addon-webgl@0.19.0/lib/addon-webgl.min.js"></script>
|
|
1129
|
+
<script src="https://cdn.jsdelivr.net/npm/@xterm/addon-unicode11@0.9.0/lib/addon-unicode11.min.js"></script>
|
|
1130
|
+
<script src="https://cdn.jsdelivr.net/npm/marked@15.0.7/marked.min.js"></script>
|
|
1131
|
+
<script src="https://cdn.jsdelivr.net/npm/dompurify@3.2.4/dist/purify.min.js"></script>
|
|
1120
1132
|
<script>
|
|
1121
1133
|
(function() {
|
|
1122
1134
|
var isMobile = /Mobi|Android|iPhone|iPad|iPod/i.test(navigator.userAgent);
|
|
@@ -1129,6 +1141,9 @@
|
|
|
1129
1141
|
|
|
1130
1142
|
var term = new Terminal({
|
|
1131
1143
|
cursorBlink: !isMobile,
|
|
1144
|
+
cursorStyle: 'bar',
|
|
1145
|
+
cursorWidth: 1,
|
|
1146
|
+
cursorInactiveStyle: 'none',
|
|
1132
1147
|
fontSize: fontSize,
|
|
1133
1148
|
fontFamily: 'Menlo, Monaco, "Courier New", monospace',
|
|
1134
1149
|
theme: {
|
|
@@ -1145,6 +1160,15 @@
|
|
|
1145
1160
|
|
|
1146
1161
|
term.open(document.getElementById('terminal'));
|
|
1147
1162
|
|
|
1163
|
+
// Unicode 11 宽字符支持:box-drawing、CJK、emoji 等字符宽度精确计算
|
|
1164
|
+
if (window.Unicode11Addon) {
|
|
1165
|
+
try {
|
|
1166
|
+
var unicode11 = new Unicode11Addon.Unicode11Addon();
|
|
1167
|
+
term.loadAddon(unicode11);
|
|
1168
|
+
term.unicode.activeVersion = '11';
|
|
1169
|
+
} catch(e) {}
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1148
1172
|
// WebGL 渲染器:GPU 加速绘制,非 iOS 设备启用(iOS WebGL 性能差)
|
|
1149
1173
|
if (!isIOS && window.WebglAddon) {
|
|
1150
1174
|
try {
|
|
@@ -1834,6 +1858,11 @@
|
|
|
1834
1858
|
if (seq && ws && ws.readyState === 1 && !isTransitioning) {
|
|
1835
1859
|
ws.send(JSON.stringify({ type: 'input', data: seq }));
|
|
1836
1860
|
}
|
|
1861
|
+
// 手机端主动 blur xterm textarea,防止系统键盘弹出
|
|
1862
|
+
if (isMobile) {
|
|
1863
|
+
var xtermTa = terminalEl.querySelector('.xterm-helper-textarea');
|
|
1864
|
+
if (xtermTa) xtermTa.blur();
|
|
1865
|
+
}
|
|
1837
1866
|
}
|
|
1838
1867
|
|
|
1839
1868
|
function scrollTerminal(lines) {
|
|
@@ -1841,111 +1870,62 @@
|
|
|
1841
1870
|
doScroll(lines);
|
|
1842
1871
|
}
|
|
1843
1872
|
|
|
1844
|
-
//
|
|
1845
|
-
// 每个按键独立绑定事件,不在容器上绑定
|
|
1873
|
+
// 虚拟按键触摸处理
|
|
1846
1874
|
var vkStartX = 0, vkStartY = 0, vkMoved = false, vkTarget = null;
|
|
1847
|
-
var scrollInterval = null; // 长按滚动定时器
|
|
1848
1875
|
|
|
1849
1876
|
function setupVirtualKeyEvents() {
|
|
1850
1877
|
var keys = document.querySelectorAll('.virtual-key');
|
|
1851
1878
|
keys.forEach(function(key) {
|
|
1852
|
-
// 防止元素获得焦点
|
|
1853
1879
|
key.setAttribute('tabindex', '-1');
|
|
1854
1880
|
|
|
1855
|
-
// 移动端触摸事件
|
|
1856
1881
|
key.addEventListener('touchstart', function(e) {
|
|
1857
1882
|
var touch = e.touches[0];
|
|
1858
1883
|
vkStartX = touch.clientX;
|
|
1859
1884
|
vkStartY = touch.clientY;
|
|
1860
1885
|
vkMoved = false;
|
|
1861
1886
|
vkTarget = e.currentTarget;
|
|
1862
|
-
vkTarget.
|
|
1863
|
-
|
|
1864
|
-
// 如果是滚动按钮,阻止默认行为(防止键盘弹出)并启动持续滚动
|
|
1865
|
-
var scrollLines = e.currentTarget.getAttribute('data-scroll');
|
|
1866
|
-
if (scrollLines) {
|
|
1867
|
-
e.preventDefault(); // 关键:阻止默认行为,防止键盘弹出
|
|
1868
|
-
scrollTerminal(parseInt(scrollLines, 10));
|
|
1869
|
-
scrollInterval = setInterval(function() {
|
|
1870
|
-
scrollTerminal(parseInt(scrollLines, 10));
|
|
1871
|
-
}, 100);
|
|
1872
|
-
}
|
|
1873
|
-
}, { passive: false }); // 必须是 false 才能调用 preventDefault()
|
|
1887
|
+
vkTarget.classList.add('virtual-key-pressed');
|
|
1888
|
+
}, { passive: true });
|
|
1874
1889
|
|
|
1875
1890
|
key.addEventListener('touchmove', function(e) {
|
|
1876
1891
|
if (vkMoved) return;
|
|
1877
1892
|
var touch = e.touches[0];
|
|
1878
1893
|
var dx = touch.clientX - vkStartX;
|
|
1879
1894
|
var dy = touch.clientY - vkStartY;
|
|
1880
|
-
if (dx * dx + dy * dy > 64) {
|
|
1895
|
+
if (dx * dx + dy * dy > 64) {
|
|
1881
1896
|
vkMoved = true;
|
|
1882
|
-
if (vkTarget) {
|
|
1883
|
-
vkTarget.style.background = '';
|
|
1884
|
-
}
|
|
1885
1897
|
}
|
|
1886
1898
|
}, { passive: true });
|
|
1887
1899
|
|
|
1888
1900
|
key.addEventListener('touchend', function(e) {
|
|
1889
|
-
|
|
1890
|
-
if (scrollInterval) {
|
|
1891
|
-
clearInterval(scrollInterval);
|
|
1892
|
-
scrollInterval = null;
|
|
1893
|
-
}
|
|
1894
|
-
|
|
1901
|
+
e.preventDefault();
|
|
1895
1902
|
if (vkTarget) {
|
|
1896
|
-
vkTarget.
|
|
1903
|
+
vkTarget.classList.remove('virtual-key-pressed');
|
|
1897
1904
|
vkTarget = null;
|
|
1898
1905
|
}
|
|
1899
|
-
|
|
1900
|
-
// 如果没有移动,触发按键功能并阻止默认行为
|
|
1901
1906
|
if (!vkMoved) {
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1907
|
+
var xtermTa = terminalEl.querySelector('.xterm-helper-textarea');
|
|
1908
|
+
if (xtermTa) xtermTa.blur();
|
|
1909
|
+
var scrollAttr = e.currentTarget.getAttribute('data-scroll');
|
|
1910
|
+
if (scrollAttr) {
|
|
1911
|
+
scrollTerminal(parseInt(scrollAttr, 10));
|
|
1912
|
+
} else {
|
|
1906
1913
|
var keyName = e.currentTarget.getAttribute('data-key');
|
|
1907
1914
|
sendKey(keyName);
|
|
1908
1915
|
}
|
|
1909
1916
|
}
|
|
1910
1917
|
}, { passive: false });
|
|
1911
1918
|
|
|
1912
|
-
// PC端点击支持
|
|
1913
1919
|
key.addEventListener('click', function(e) {
|
|
1914
1920
|
e.preventDefault();
|
|
1915
|
-
var
|
|
1916
|
-
if (
|
|
1917
|
-
scrollTerminal(parseInt(
|
|
1921
|
+
var scrollAttr = e.currentTarget.getAttribute('data-scroll');
|
|
1922
|
+
if (scrollAttr) {
|
|
1923
|
+
scrollTerminal(parseInt(scrollAttr, 10));
|
|
1918
1924
|
} else {
|
|
1919
1925
|
var keyName = e.currentTarget.getAttribute('data-key');
|
|
1920
1926
|
sendKey(keyName);
|
|
1921
1927
|
}
|
|
1922
1928
|
});
|
|
1923
|
-
|
|
1924
|
-
// PC端鼠标按下支持(长按滚动)
|
|
1925
|
-
key.addEventListener('mousedown', function(e) {
|
|
1926
|
-
e.preventDefault();
|
|
1927
|
-
var scrollLines = e.currentTarget.getAttribute('data-scroll');
|
|
1928
|
-
if (scrollLines) {
|
|
1929
|
-
scrollTerminal(parseInt(scrollLines, 10));
|
|
1930
|
-
scrollInterval = setInterval(function() {
|
|
1931
|
-
scrollTerminal(parseInt(scrollLines, 10));
|
|
1932
|
-
}, 100);
|
|
1933
|
-
}
|
|
1934
|
-
});
|
|
1935
|
-
|
|
1936
|
-
key.addEventListener('mouseup', function(e) {
|
|
1937
|
-
if (scrollInterval) {
|
|
1938
|
-
clearInterval(scrollInterval);
|
|
1939
|
-
scrollInterval = null;
|
|
1940
|
-
}
|
|
1941
|
-
});
|
|
1942
|
-
|
|
1943
|
-
key.addEventListener('mouseleave', function(e) {
|
|
1944
|
-
if (scrollInterval) {
|
|
1945
|
-
clearInterval(scrollInterval);
|
|
1946
|
-
scrollInterval = null;
|
|
1947
|
-
}
|
|
1948
|
-
});
|
|
1949
1929
|
});
|
|
1950
1930
|
}
|
|
1951
1931
|
|
|
@@ -2404,7 +2384,6 @@
|
|
|
2404
2384
|
|
|
2405
2385
|
messageViewer.classList.add('visible');
|
|
2406
2386
|
msgViewerContent.innerHTML = '<div class="msg-empty">加载中...</div>';
|
|
2407
|
-
unbindTouchScroll();
|
|
2408
2387
|
|
|
2409
2388
|
if (currentMode === 'claude') {
|
|
2410
2389
|
// Claude 模式:从 JSONL 文件读取消息
|
|
@@ -2465,22 +2444,12 @@
|
|
|
2465
2444
|
}
|
|
2466
2445
|
|
|
2467
2446
|
function formatMsgText(text) {
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
if (p.startsWith('```') && p.endsWith('```')) {
|
|
2474
|
-
// 去掉首尾 ```(可能带语言标记)
|
|
2475
|
-
var inner = p.slice(3, -3);
|
|
2476
|
-
var nlIdx = inner.indexOf('\n');
|
|
2477
|
-
if (nlIdx !== -1) inner = inner.slice(nlIdx + 1);
|
|
2478
|
-
result += '<pre>' + escapeHtml(inner) + '</pre>';
|
|
2479
|
-
} else {
|
|
2480
|
-
result += escapeHtml(p);
|
|
2481
|
-
}
|
|
2447
|
+
if (!text) return '';
|
|
2448
|
+
try {
|
|
2449
|
+
return DOMPurify.sanitize(marked.parse(text, { breaks: true }));
|
|
2450
|
+
} catch (e) {
|
|
2451
|
+
return escapeHtml(text);
|
|
2482
2452
|
}
|
|
2483
|
-
return result;
|
|
2484
2453
|
}
|
|
2485
2454
|
|
|
2486
2455
|
function renderMessages(messages) {
|
|
@@ -2491,19 +2460,30 @@
|
|
|
2491
2460
|
var html = '';
|
|
2492
2461
|
messages.forEach(function(msg) {
|
|
2493
2462
|
var role = msg.role || 'unknown';
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
html += '
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2463
|
+
if (role === 'user') {
|
|
2464
|
+
html += '<div class="msg-row-end">';
|
|
2465
|
+
html += '<div class="msg-content-col">';
|
|
2466
|
+
html += '<div class="msg-label msg-label-right">用户</div>';
|
|
2467
|
+
html += '<div class="msg-bubble msg-bubble-user">' + escapeHtml(msg.text || '') + '</div>';
|
|
2468
|
+
html += '</div>';
|
|
2469
|
+
html += '<div class="msg-avatar" style="background:#1e40af">U</div>';
|
|
2470
|
+
html += '</div>';
|
|
2471
|
+
} else if (role === 'assistant') {
|
|
2472
|
+
html += '<div class="msg-row">';
|
|
2473
|
+
html += '<div class="msg-avatar" style="background:#000;border:1px solid #333">A</div>';
|
|
2474
|
+
html += '<div class="msg-content-col">';
|
|
2475
|
+
html += '<div class="msg-label">助手</div>';
|
|
2476
|
+
html += '<div class="msg-bubble msg-bubble-assistant">' + formatMsgText(msg.text || '') + '</div>';
|
|
2477
|
+
if (msg.toolCalls && msg.toolCalls.length > 0) {
|
|
2478
|
+
html += '<div class="msg-tools">';
|
|
2479
|
+
msg.toolCalls.forEach(function(tc) {
|
|
2480
|
+
html += '<span class="msg-tool-tag">🔧 ' + escapeHtml(tc.name || 'tool') + '</span>';
|
|
2481
|
+
});
|
|
2482
|
+
html += '</div>';
|
|
2483
|
+
}
|
|
2484
|
+
html += '</div>';
|
|
2485
|
+
html += '</div>';
|
|
2505
2486
|
}
|
|
2506
|
-
html += '</div>';
|
|
2507
2487
|
});
|
|
2508
2488
|
msgViewerContent.innerHTML = html;
|
|
2509
2489
|
msgViewerContent.scrollTop = msgViewerContent.scrollHeight;
|
|
@@ -2515,7 +2495,6 @@
|
|
|
2515
2495
|
|
|
2516
2496
|
function closeMessageViewer() {
|
|
2517
2497
|
messageViewer.classList.remove('visible');
|
|
2518
|
-
rebindTouchScroll();
|
|
2519
2498
|
}
|
|
2520
2499
|
|
|
2521
2500
|
msgViewerClose.addEventListener('click', function(e) {
|