claude-opencode-viewer 2.6.45 → 2.6.47
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 +33 -2
- package/index.html +161 -197
- package/package.json +1 -1
package/index-pc.html
CHANGED
|
@@ -390,8 +390,26 @@
|
|
|
390
390
|
content: '';
|
|
391
391
|
animation: loading-dots 1.2s steps(4, end) infinite;
|
|
392
392
|
}
|
|
393
|
-
#terminal.transitioning #switch-overlay { display: flex;
|
|
393
|
+
#terminal.transitioning #switch-overlay { display: flex; }
|
|
394
|
+
#init-overlay {
|
|
395
|
+
display: none;
|
|
396
|
+
position: absolute;
|
|
397
|
+
top: 0; left: 0; right: 0; bottom: 0;
|
|
398
|
+
background: #0a0a0a;
|
|
399
|
+
color: #ccc;
|
|
400
|
+
align-items: center;
|
|
401
|
+
justify-content: center;
|
|
402
|
+
font-size: 18px;
|
|
403
|
+
font-weight: 600;
|
|
404
|
+
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
|
|
405
|
+
letter-spacing: 1px;
|
|
406
|
+
z-index: 10;
|
|
407
|
+
}
|
|
408
|
+
#init-overlay::after {
|
|
409
|
+
content: '';
|
|
410
|
+
animation: loading-dots 1.2s steps(4, end) infinite;
|
|
394
411
|
}
|
|
412
|
+
#init-overlay.visible { display: flex; }
|
|
395
413
|
|
|
396
414
|
|
|
397
415
|
/* 选择模式:原位文本层 */
|
|
@@ -1019,6 +1037,7 @@
|
|
|
1019
1037
|
<div id="terminal-container">
|
|
1020
1038
|
<div id="terminal" style="position:relative;">
|
|
1021
1039
|
<div id="switch-overlay">正在切换</div>
|
|
1040
|
+
<div id="init-overlay"></div>
|
|
1022
1041
|
<div id="select-text-layer">
|
|
1023
1042
|
<div id="select-hint">长按选择文本 · 点右上角 ✕ 返回终端</div>
|
|
1024
1043
|
<pre id="select-text-pre"></pre>
|
|
@@ -1054,7 +1073,7 @@
|
|
|
1054
1073
|
selectionBackground: '#264f78',
|
|
1055
1074
|
},
|
|
1056
1075
|
allowProposedApi: true,
|
|
1057
|
-
scrollback: isIOS ?
|
|
1076
|
+
scrollback: isIOS ? 2000 : isMobile ? 5000 : 50000,
|
|
1058
1077
|
smoothScrollDuration: 0,
|
|
1059
1078
|
scrollOnUserInput: true,
|
|
1060
1079
|
});
|
|
@@ -1591,11 +1610,19 @@
|
|
|
1591
1610
|
currentMode = mode;
|
|
1592
1611
|
modeSelect.value = mode;
|
|
1593
1612
|
document.getElementById('mode-label').textContent = '';
|
|
1613
|
+
var label = mode === 'claude' ? 'Claude' : 'OpenCode';
|
|
1614
|
+
var initOv = document.getElementById('init-overlay');
|
|
1615
|
+
initOv.textContent = '正在启动 ' + label + (sessionId ? '(恢复会话)' : '');
|
|
1616
|
+
initOv.classList.add('visible');
|
|
1594
1617
|
var msg = { type: 'init', mode: mode };
|
|
1595
1618
|
if (sessionId) msg.sessionId = sessionId;
|
|
1596
1619
|
ws.send(JSON.stringify(msg));
|
|
1597
1620
|
}
|
|
1598
1621
|
|
|
1622
|
+
function hideInitOverlay() {
|
|
1623
|
+
document.getElementById('init-overlay').classList.remove('visible');
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1599
1626
|
function getTimeAgo(ts) {
|
|
1600
1627
|
var diff = Date.now() - ts;
|
|
1601
1628
|
var min = Math.floor(diff / 60000);
|
|
@@ -1671,6 +1698,7 @@
|
|
|
1671
1698
|
}
|
|
1672
1699
|
}
|
|
1673
1700
|
else if (msg.type === 'restored') {
|
|
1701
|
+
hideInitOverlay();
|
|
1674
1702
|
if (writeTimer) { cancelAnimationFrame(writeTimer); writeTimer = null; }
|
|
1675
1703
|
writeBuffer = '';
|
|
1676
1704
|
term.reset();
|
|
@@ -1679,13 +1707,16 @@
|
|
|
1679
1707
|
}
|
|
1680
1708
|
}
|
|
1681
1709
|
else if (msg.type === 'restore-error') {
|
|
1710
|
+
hideInitOverlay();
|
|
1682
1711
|
term.write('恢复失败: ' + msg.error + '\r\n');
|
|
1683
1712
|
}
|
|
1684
1713
|
else if (msg.type === 'started') {
|
|
1714
|
+
hideInitOverlay();
|
|
1685
1715
|
rebindTouchScroll();
|
|
1686
1716
|
preloadData();
|
|
1687
1717
|
}
|
|
1688
1718
|
else if (msg.type === 'new-session-ok') {
|
|
1719
|
+
hideInitOverlay();
|
|
1689
1720
|
if (writeTimer) { cancelAnimationFrame(writeTimer); writeTimer = null; }
|
|
1690
1721
|
writeBuffer = '';
|
|
1691
1722
|
term.reset();
|
package/index.html
CHANGED
|
@@ -30,12 +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
|
-
/*
|
|
38
|
+
/* 隐藏 xterm textarea 的闪烁光标 (cc-viewer TerminalPanel.module.css) */
|
|
39
|
+
.xterm-helper-textarea { caret-color: transparent !important; }
|
|
40
|
+
|
|
39
41
|
#layout {
|
|
40
42
|
position: fixed;
|
|
41
43
|
top: 0;
|
|
@@ -54,8 +56,7 @@
|
|
|
54
56
|
background: #111;
|
|
55
57
|
border-bottom: 1px solid #222;
|
|
56
58
|
display: flex;
|
|
57
|
-
align-items:
|
|
58
|
-
padding-top: 40px;
|
|
59
|
+
align-items: center;
|
|
59
60
|
justify-content: space-between;
|
|
60
61
|
flex-shrink: 0;
|
|
61
62
|
height: 40px;
|
|
@@ -91,8 +92,7 @@
|
|
|
91
92
|
|
|
92
93
|
#session-history-header {
|
|
93
94
|
display: flex;
|
|
94
|
-
align-items:
|
|
95
|
-
padding-top: 40px;
|
|
95
|
+
align-items: center;
|
|
96
96
|
justify-content: space-between;
|
|
97
97
|
padding: 12px 16px;
|
|
98
98
|
background: #111;
|
|
@@ -126,8 +126,7 @@
|
|
|
126
126
|
|
|
127
127
|
.session-item {
|
|
128
128
|
display: flex;
|
|
129
|
-
align-items:
|
|
130
|
-
padding-top: 40px;
|
|
129
|
+
align-items: center;
|
|
131
130
|
gap: 8px;
|
|
132
131
|
padding: 8px 12px;
|
|
133
132
|
background: #1a1a1a;
|
|
@@ -211,8 +210,7 @@
|
|
|
211
210
|
cursor: pointer;
|
|
212
211
|
border-radius: 4px;
|
|
213
212
|
display: flex;
|
|
214
|
-
align-items:
|
|
215
|
-
padding-top: 40px;
|
|
213
|
+
align-items: center;
|
|
216
214
|
gap: 3px;
|
|
217
215
|
flex-shrink: 0;
|
|
218
216
|
}
|
|
@@ -253,8 +251,7 @@
|
|
|
253
251
|
cursor: pointer;
|
|
254
252
|
border-radius: 50%;
|
|
255
253
|
display: flex;
|
|
256
|
-
align-items:
|
|
257
|
-
padding-top: 40px;
|
|
254
|
+
align-items: center;
|
|
258
255
|
justify-content: center;
|
|
259
256
|
transition: all 0.15s;
|
|
260
257
|
-webkit-tap-highlight-color: transparent;
|
|
@@ -299,8 +296,7 @@
|
|
|
299
296
|
|
|
300
297
|
#claude-detail-header {
|
|
301
298
|
display: flex;
|
|
302
|
-
align-items:
|
|
303
|
-
padding-top: 40px;
|
|
299
|
+
align-items: center;
|
|
304
300
|
padding: 10px 12px;
|
|
305
301
|
background: #111;
|
|
306
302
|
border-bottom: 1px solid #222;
|
|
@@ -353,8 +349,7 @@
|
|
|
353
349
|
#mode-switcher {
|
|
354
350
|
display: flex;
|
|
355
351
|
gap: 4px;
|
|
356
|
-
align-items:
|
|
357
|
-
padding-top: 40px;
|
|
352
|
+
align-items: center;
|
|
358
353
|
}
|
|
359
354
|
|
|
360
355
|
#mode-label {
|
|
@@ -407,8 +402,7 @@
|
|
|
407
402
|
top: 0; left: 0; right: 0; bottom: 0;
|
|
408
403
|
background: #0a0a0a;
|
|
409
404
|
color: #ccc;
|
|
410
|
-
align-items:
|
|
411
|
-
padding-top: 40px;
|
|
405
|
+
align-items: center;
|
|
412
406
|
justify-content: center;
|
|
413
407
|
font-size: 16px;
|
|
414
408
|
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
|
|
@@ -426,6 +420,7 @@
|
|
|
426
420
|
display: flex;
|
|
427
421
|
gap: 6px;
|
|
428
422
|
padding: 8px 10px;
|
|
423
|
+
padding-bottom: calc(8px + env(safe-area-inset-bottom, 0px));
|
|
429
424
|
background: #111;
|
|
430
425
|
border-top: 1px solid #222;
|
|
431
426
|
overflow-x: auto;
|
|
@@ -444,22 +439,19 @@
|
|
|
444
439
|
font-family: Menlo, Monaco, monospace;
|
|
445
440
|
cursor: pointer;
|
|
446
441
|
user-select: none;
|
|
442
|
+
-webkit-user-select: none;
|
|
447
443
|
-webkit-tap-highlight-color: transparent;
|
|
444
|
+
-webkit-touch-callout: none;
|
|
448
445
|
touch-action: pan-x;
|
|
449
446
|
min-width: 44px;
|
|
450
447
|
min-height: 44px;
|
|
451
448
|
display: flex;
|
|
452
|
-
align-items:
|
|
453
|
-
padding-top: 40px;
|
|
449
|
+
align-items: center;
|
|
454
450
|
justify-content: center;
|
|
455
|
-
border: none;
|
|
456
451
|
outline: none;
|
|
457
|
-
-webkit-user-select: none;
|
|
458
|
-
-moz-user-select: none;
|
|
459
|
-
-ms-user-select: none;
|
|
460
452
|
}
|
|
461
453
|
|
|
462
|
-
.virtual-key
|
|
454
|
+
.virtual-key-pressed {
|
|
463
455
|
background: #333;
|
|
464
456
|
border-color: #555;
|
|
465
457
|
color: #fff;
|
|
@@ -468,19 +460,19 @@
|
|
|
468
460
|
/* 消息查看器 */
|
|
469
461
|
#message-viewer {
|
|
470
462
|
display: none;
|
|
471
|
-
position:
|
|
472
|
-
|
|
463
|
+
position: absolute;
|
|
464
|
+
inset: 0;
|
|
473
465
|
background: #0a0a0a;
|
|
474
466
|
z-index: 1000;
|
|
475
467
|
flex-direction: column;
|
|
468
|
+
overflow: hidden;
|
|
476
469
|
}
|
|
477
470
|
#message-viewer.visible {
|
|
478
471
|
display: flex;
|
|
479
472
|
}
|
|
480
473
|
#msg-viewer-header {
|
|
481
474
|
display: flex;
|
|
482
|
-
align-items:
|
|
483
|
-
padding-top: 40px;
|
|
475
|
+
align-items: center;
|
|
484
476
|
justify-content: space-between;
|
|
485
477
|
padding: 10px 14px;
|
|
486
478
|
background: #111;
|
|
@@ -506,61 +498,68 @@
|
|
|
506
498
|
}
|
|
507
499
|
#msg-viewer-content {
|
|
508
500
|
flex: 1;
|
|
509
|
-
overflow
|
|
501
|
+
overflow: auto;
|
|
510
502
|
-webkit-overflow-scrolling: touch;
|
|
503
|
+
overscroll-behavior: contain;
|
|
511
504
|
padding: 12px;
|
|
512
505
|
}
|
|
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;
|
|
506
|
+
/* 聊天气泡布局(参考 cc-viewer ChatMessage) */
|
|
507
|
+
.msg-row { display: flex; gap: 8px; padding: 6px 12px; align-items: flex-start; }
|
|
508
|
+
.msg-row-end { display: flex; gap: 8px; padding: 6px 12px; justify-content: flex-end; align-items: flex-start; }
|
|
509
|
+
.msg-avatar {
|
|
510
|
+
width: 28px; height: 28px; border-radius: 50%; flex-shrink: 0;
|
|
511
|
+
display: flex; align-items: center; justify-content: center;
|
|
512
|
+
font-size: 12px; color: #fff; font-weight: 600;
|
|
513
|
+
}
|
|
514
|
+
.msg-content-col { min-width: 0; max-width: 85%; }
|
|
515
|
+
.msg-label { font-size: 10px; color: #888; margin-bottom: 2px; }
|
|
516
|
+
.msg-label-right { text-align: right; }
|
|
517
|
+
.msg-bubble {
|
|
518
|
+
border-radius: 8px; border: 1px solid #333; padding: 8px 12px;
|
|
519
|
+
font-size: 13px; line-height: 1.6; word-break: break-word;
|
|
520
|
+
-webkit-user-select: text; user-select: text;
|
|
521
|
+
}
|
|
522
|
+
.msg-bubble-user {
|
|
523
|
+
background: #1668dc; color: #fff; border-color: #4a9eff;
|
|
538
524
|
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
525
|
}
|
|
526
|
+
.msg-bubble-assistant {
|
|
527
|
+
background: #111; color: #ddd; border-color: #2a2a2a;
|
|
528
|
+
}
|
|
529
|
+
/* 工具标签 */
|
|
530
|
+
.msg-tools { display: flex; flex-wrap: wrap; gap: 4px; padding: 2px 0 4px; }
|
|
531
|
+
.msg-tool-tag {
|
|
532
|
+
display: inline-block; padding: 2px 8px; border-radius: 4px;
|
|
533
|
+
background: #1a1a2a; border: 1px solid #333; color: #aaa;
|
|
534
|
+
font-size: 11px; white-space: nowrap;
|
|
535
|
+
}
|
|
536
|
+
/* Markdown 富文本(助手气泡内) */
|
|
537
|
+
.msg-bubble-assistant pre {
|
|
538
|
+
background: #0d1117; border: 1px solid #2a2a2a; border-radius: 6px;
|
|
539
|
+
padding: 12px; overflow-x: auto; font-size: 13px; line-height: 1.5;
|
|
540
|
+
}
|
|
541
|
+
.msg-bubble-assistant code {
|
|
542
|
+
background: #14141F; padding: 2px 6px; border-radius: 4px;
|
|
543
|
+
font-size: 13px; color: #aeafff;
|
|
544
|
+
}
|
|
545
|
+
.msg-bubble-assistant pre code { background: none; padding: 0; color: inherit; }
|
|
546
|
+
.msg-bubble-assistant p { margin: 6px 0; }
|
|
547
|
+
.msg-bubble-assistant ul, .msg-bubble-assistant ol { padding-left: 20px; margin: 6px 0; }
|
|
548
|
+
.msg-bubble-assistant li { margin: 2px 0; }
|
|
549
|
+
.msg-bubble-assistant h1, .msg-bubble-assistant h2, .msg-bubble-assistant h3 { margin: 12px 0 6px 0; color: #fff; }
|
|
550
|
+
.msg-bubble-assistant h1 { font-size: 1.3em; }
|
|
551
|
+
.msg-bubble-assistant h2 { font-size: 1.15em; }
|
|
552
|
+
.msg-bubble-assistant h3 { font-size: 1.05em; }
|
|
553
|
+
.msg-bubble-assistant blockquote {
|
|
554
|
+
border-left: 3px solid #3b82f6; margin: 8px 0; padding: 4px 12px; color: #888;
|
|
555
|
+
}
|
|
556
|
+
.msg-bubble-assistant table { border-collapse: collapse; margin: 8px 0; font-size: 13px; }
|
|
557
|
+
.msg-bubble-assistant th, .msg-bubble-assistant td { border: 1px solid #6b7280; padding: 6px 10px; }
|
|
558
|
+
.msg-bubble-assistant th { background: #1e1e1e; color: #fff; }
|
|
559
|
+
.msg-bubble-assistant a { color: #60a5fa; }
|
|
560
|
+
.msg-bubble-assistant img { max-width: 100%; height: auto; border-radius: 6px; }
|
|
561
|
+
.msg-bubble-assistant hr { border: none; border-top: 1px solid #2a2a2a; margin: 12px 0; }
|
|
562
|
+
.msg-bubble-assistant strong { color: #e5e5e5; }
|
|
564
563
|
.msg-empty {
|
|
565
564
|
text-align: center;
|
|
566
565
|
padding: 40px 20px;
|
|
@@ -607,8 +606,7 @@
|
|
|
607
606
|
|
|
608
607
|
#git-diff-header {
|
|
609
608
|
display: flex;
|
|
610
|
-
align-items:
|
|
611
|
-
padding-top: 40px;
|
|
609
|
+
align-items: center;
|
|
612
610
|
justify-content: space-between;
|
|
613
611
|
padding: 12px 16px;
|
|
614
612
|
background: #111;
|
|
@@ -632,8 +630,7 @@
|
|
|
632
630
|
|
|
633
631
|
.git-diff-file-item {
|
|
634
632
|
display: flex;
|
|
635
|
-
align-items:
|
|
636
|
-
padding-top: 40px;
|
|
633
|
+
align-items: center;
|
|
637
634
|
padding: 6px 12px;
|
|
638
635
|
cursor: pointer;
|
|
639
636
|
color: #ccc;
|
|
@@ -677,8 +674,7 @@
|
|
|
677
674
|
|
|
678
675
|
.git-diff-content-header {
|
|
679
676
|
display: flex;
|
|
680
|
-
align-items:
|
|
681
|
-
padding-top: 40px;
|
|
677
|
+
align-items: center;
|
|
682
678
|
gap: 10px;
|
|
683
679
|
padding: 8px 12px;
|
|
684
680
|
border-bottom: 1px solid #2a2a2a;
|
|
@@ -718,8 +714,7 @@
|
|
|
718
714
|
flex: 1;
|
|
719
715
|
display: flex;
|
|
720
716
|
flex-direction: column;
|
|
721
|
-
align-items:
|
|
722
|
-
padding-top: 40px;
|
|
717
|
+
align-items: center;
|
|
723
718
|
justify-content: center;
|
|
724
719
|
gap: 12px;
|
|
725
720
|
color: #333;
|
|
@@ -752,8 +747,7 @@
|
|
|
752
747
|
#docs-bar.visible { display: flex; }
|
|
753
748
|
#docs-header {
|
|
754
749
|
display: flex;
|
|
755
|
-
align-items:
|
|
756
|
-
padding-top: 40px;
|
|
750
|
+
align-items: center;
|
|
757
751
|
justify-content: space-between;
|
|
758
752
|
padding: 12px 16px;
|
|
759
753
|
background: #111;
|
|
@@ -773,8 +767,7 @@
|
|
|
773
767
|
}
|
|
774
768
|
.docs-file-item {
|
|
775
769
|
display: flex;
|
|
776
|
-
align-items:
|
|
777
|
-
padding-top: 40px;
|
|
770
|
+
align-items: center;
|
|
778
771
|
padding: 8px 12px;
|
|
779
772
|
cursor: pointer;
|
|
780
773
|
color: #ccc;
|
|
@@ -820,8 +813,7 @@
|
|
|
820
813
|
flex: 1;
|
|
821
814
|
display: flex;
|
|
822
815
|
flex-direction: column;
|
|
823
|
-
align-items:
|
|
824
|
-
padding-top: 40px;
|
|
816
|
+
align-items: center;
|
|
825
817
|
justify-content: center;
|
|
826
818
|
gap: 12px;
|
|
827
819
|
color: #333;
|
|
@@ -1102,21 +1094,24 @@
|
|
|
1102
1094
|
</div>
|
|
1103
1095
|
</div>
|
|
1104
1096
|
</div>
|
|
1105
|
-
</div>
|
|
1106
1097
|
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1098
|
+
<div id="message-viewer">
|
|
1099
|
+
<div id="msg-viewer-header">
|
|
1100
|
+
<span>会话消息</span>
|
|
1101
|
+
<button id="msg-viewer-close">✕</button>
|
|
1102
|
+
</div>
|
|
1103
|
+
<div id="msg-viewer-content">
|
|
1104
|
+
<div class="msg-empty">加载中...</div>
|
|
1105
|
+
</div>
|
|
1114
1106
|
</div>
|
|
1115
1107
|
</div>
|
|
1116
1108
|
|
|
1117
1109
|
<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.
|
|
1110
|
+
<script src="https://cdn.jsdelivr.net/npm/@xterm/xterm@6.0.0/lib/xterm.min.js"></script>
|
|
1111
|
+
<script src="https://cdn.jsdelivr.net/npm/@xterm/addon-webgl@0.19.0/lib/addon-webgl.min.js"></script>
|
|
1112
|
+
<script src="https://cdn.jsdelivr.net/npm/@xterm/addon-unicode11@0.9.0/lib/addon-unicode11.min.js"></script>
|
|
1113
|
+
<script src="https://cdn.jsdelivr.net/npm/marked@15.0.7/marked.min.js"></script>
|
|
1114
|
+
<script src="https://cdn.jsdelivr.net/npm/dompurify@3.2.4/dist/purify.min.js"></script>
|
|
1120
1115
|
<script>
|
|
1121
1116
|
(function() {
|
|
1122
1117
|
var isMobile = /Mobi|Android|iPhone|iPad|iPod/i.test(navigator.userAgent);
|
|
@@ -1129,6 +1124,9 @@
|
|
|
1129
1124
|
|
|
1130
1125
|
var term = new Terminal({
|
|
1131
1126
|
cursorBlink: !isMobile,
|
|
1127
|
+
cursorStyle: 'bar',
|
|
1128
|
+
cursorWidth: 1,
|
|
1129
|
+
cursorInactiveStyle: 'none',
|
|
1132
1130
|
fontSize: fontSize,
|
|
1133
1131
|
fontFamily: 'Menlo, Monaco, "Courier New", monospace',
|
|
1134
1132
|
theme: {
|
|
@@ -1138,13 +1136,23 @@
|
|
|
1138
1136
|
selectionBackground: '#264f78',
|
|
1139
1137
|
},
|
|
1140
1138
|
allowProposedApi: true,
|
|
1141
|
-
scrollback: isIOS ?
|
|
1139
|
+
scrollback: isIOS ? 2000 : isMobile ? 5000 : 50000,
|
|
1142
1140
|
smoothScrollDuration: 0,
|
|
1143
1141
|
scrollOnUserInput: true,
|
|
1144
1142
|
});
|
|
1145
1143
|
|
|
1146
1144
|
term.open(document.getElementById('terminal'));
|
|
1147
1145
|
|
|
1146
|
+
|
|
1147
|
+
// Unicode 11 宽字符支持:box-drawing、CJK、emoji 等字符宽度精确计算
|
|
1148
|
+
if (window.Unicode11Addon) {
|
|
1149
|
+
try {
|
|
1150
|
+
var unicode11 = new Unicode11Addon.Unicode11Addon();
|
|
1151
|
+
term.loadAddon(unicode11);
|
|
1152
|
+
term.unicode.activeVersion = '11';
|
|
1153
|
+
} catch(e) {}
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1148
1156
|
// WebGL 渲染器:GPU 加速绘制,非 iOS 设备启用(iOS WebGL 性能差)
|
|
1149
1157
|
if (!isIOS && window.WebglAddon) {
|
|
1150
1158
|
try {
|
|
@@ -1302,6 +1310,7 @@
|
|
|
1302
1310
|
var lineHeight = (newCellDims && newCellDims.height) || cellDims.height;
|
|
1303
1311
|
var rows = Math.max(5, Math.min(Math.floor(availH / lineHeight), 100));
|
|
1304
1312
|
term.resize(MOBILE_COLS, rows);
|
|
1313
|
+
term.scrollToBottom();
|
|
1305
1314
|
if (ws && ws.readyState === 1 && !isTransitioning) {
|
|
1306
1315
|
ws.send(JSON.stringify({ type: 'resize', cols: MOBILE_COLS, rows: rows, mobile: true }));
|
|
1307
1316
|
}
|
|
@@ -1834,6 +1843,11 @@
|
|
|
1834
1843
|
if (seq && ws && ws.readyState === 1 && !isTransitioning) {
|
|
1835
1844
|
ws.send(JSON.stringify({ type: 'input', data: seq }));
|
|
1836
1845
|
}
|
|
1846
|
+
// 手机端主动 blur xterm textarea,防止系统键盘弹出
|
|
1847
|
+
if (isMobile) {
|
|
1848
|
+
var xtermTa = terminalEl.querySelector('.xterm-helper-textarea');
|
|
1849
|
+
if (xtermTa) xtermTa.blur();
|
|
1850
|
+
}
|
|
1837
1851
|
}
|
|
1838
1852
|
|
|
1839
1853
|
function scrollTerminal(lines) {
|
|
@@ -1841,111 +1855,62 @@
|
|
|
1841
1855
|
doScroll(lines);
|
|
1842
1856
|
}
|
|
1843
1857
|
|
|
1844
|
-
//
|
|
1845
|
-
// 每个按键独立绑定事件,不在容器上绑定
|
|
1858
|
+
// 虚拟按键触摸处理
|
|
1846
1859
|
var vkStartX = 0, vkStartY = 0, vkMoved = false, vkTarget = null;
|
|
1847
|
-
var scrollInterval = null; // 长按滚动定时器
|
|
1848
1860
|
|
|
1849
1861
|
function setupVirtualKeyEvents() {
|
|
1850
1862
|
var keys = document.querySelectorAll('.virtual-key');
|
|
1851
1863
|
keys.forEach(function(key) {
|
|
1852
|
-
// 防止元素获得焦点
|
|
1853
1864
|
key.setAttribute('tabindex', '-1');
|
|
1854
1865
|
|
|
1855
|
-
// 移动端触摸事件
|
|
1856
1866
|
key.addEventListener('touchstart', function(e) {
|
|
1857
1867
|
var touch = e.touches[0];
|
|
1858
1868
|
vkStartX = touch.clientX;
|
|
1859
1869
|
vkStartY = touch.clientY;
|
|
1860
1870
|
vkMoved = false;
|
|
1861
1871
|
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()
|
|
1872
|
+
vkTarget.classList.add('virtual-key-pressed');
|
|
1873
|
+
}, { passive: true });
|
|
1874
1874
|
|
|
1875
1875
|
key.addEventListener('touchmove', function(e) {
|
|
1876
1876
|
if (vkMoved) return;
|
|
1877
1877
|
var touch = e.touches[0];
|
|
1878
1878
|
var dx = touch.clientX - vkStartX;
|
|
1879
1879
|
var dy = touch.clientY - vkStartY;
|
|
1880
|
-
if (dx * dx + dy * dy > 64) {
|
|
1880
|
+
if (dx * dx + dy * dy > 64) {
|
|
1881
1881
|
vkMoved = true;
|
|
1882
|
-
if (vkTarget) {
|
|
1883
|
-
vkTarget.style.background = '';
|
|
1884
|
-
}
|
|
1885
1882
|
}
|
|
1886
1883
|
}, { passive: true });
|
|
1887
1884
|
|
|
1888
1885
|
key.addEventListener('touchend', function(e) {
|
|
1889
|
-
|
|
1890
|
-
if (scrollInterval) {
|
|
1891
|
-
clearInterval(scrollInterval);
|
|
1892
|
-
scrollInterval = null;
|
|
1893
|
-
}
|
|
1894
|
-
|
|
1886
|
+
e.preventDefault();
|
|
1895
1887
|
if (vkTarget) {
|
|
1896
|
-
vkTarget.
|
|
1888
|
+
vkTarget.classList.remove('virtual-key-pressed');
|
|
1897
1889
|
vkTarget = null;
|
|
1898
1890
|
}
|
|
1899
|
-
|
|
1900
|
-
// 如果没有移动,触发按键功能并阻止默认行为
|
|
1901
1891
|
if (!vkMoved) {
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1892
|
+
var xtermTa = terminalEl.querySelector('.xterm-helper-textarea');
|
|
1893
|
+
if (xtermTa) xtermTa.blur();
|
|
1894
|
+
var scrollAttr = e.currentTarget.getAttribute('data-scroll');
|
|
1895
|
+
if (scrollAttr) {
|
|
1896
|
+
scrollTerminal(parseInt(scrollAttr, 10));
|
|
1897
|
+
} else {
|
|
1906
1898
|
var keyName = e.currentTarget.getAttribute('data-key');
|
|
1907
1899
|
sendKey(keyName);
|
|
1908
1900
|
}
|
|
1909
1901
|
}
|
|
1910
1902
|
}, { passive: false });
|
|
1911
1903
|
|
|
1912
|
-
// PC端点击支持
|
|
1913
1904
|
key.addEventListener('click', function(e) {
|
|
1914
1905
|
e.preventDefault();
|
|
1915
|
-
var
|
|
1916
|
-
if (
|
|
1917
|
-
scrollTerminal(parseInt(
|
|
1906
|
+
var scrollAttr = e.currentTarget.getAttribute('data-scroll');
|
|
1907
|
+
if (scrollAttr) {
|
|
1908
|
+
scrollTerminal(parseInt(scrollAttr, 10));
|
|
1918
1909
|
} else {
|
|
1919
1910
|
var keyName = e.currentTarget.getAttribute('data-key');
|
|
1920
1911
|
sendKey(keyName);
|
|
1921
1912
|
}
|
|
1922
1913
|
});
|
|
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
1914
|
});
|
|
1950
1915
|
}
|
|
1951
1916
|
|
|
@@ -2404,7 +2369,6 @@
|
|
|
2404
2369
|
|
|
2405
2370
|
messageViewer.classList.add('visible');
|
|
2406
2371
|
msgViewerContent.innerHTML = '<div class="msg-empty">加载中...</div>';
|
|
2407
|
-
unbindTouchScroll();
|
|
2408
2372
|
|
|
2409
2373
|
if (currentMode === 'claude') {
|
|
2410
2374
|
// Claude 模式:从 JSONL 文件读取消息
|
|
@@ -2465,22 +2429,12 @@
|
|
|
2465
2429
|
}
|
|
2466
2430
|
|
|
2467
2431
|
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
|
-
}
|
|
2432
|
+
if (!text) return '';
|
|
2433
|
+
try {
|
|
2434
|
+
return DOMPurify.sanitize(marked.parse(text, { breaks: true }));
|
|
2435
|
+
} catch (e) {
|
|
2436
|
+
return escapeHtml(text);
|
|
2482
2437
|
}
|
|
2483
|
-
return result;
|
|
2484
2438
|
}
|
|
2485
2439
|
|
|
2486
2440
|
function renderMessages(messages) {
|
|
@@ -2491,19 +2445,30 @@
|
|
|
2491
2445
|
var html = '';
|
|
2492
2446
|
messages.forEach(function(msg) {
|
|
2493
2447
|
var role = msg.role || 'unknown';
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
html += '
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2448
|
+
if (role === 'user') {
|
|
2449
|
+
html += '<div class="msg-row-end">';
|
|
2450
|
+
html += '<div class="msg-content-col">';
|
|
2451
|
+
html += '<div class="msg-label msg-label-right">用户</div>';
|
|
2452
|
+
html += '<div class="msg-bubble msg-bubble-user">' + escapeHtml(msg.text || '') + '</div>';
|
|
2453
|
+
html += '</div>';
|
|
2454
|
+
html += '<div class="msg-avatar" style="background:#1e40af">U</div>';
|
|
2455
|
+
html += '</div>';
|
|
2456
|
+
} else if (role === 'assistant') {
|
|
2457
|
+
html += '<div class="msg-row">';
|
|
2458
|
+
html += '<div class="msg-avatar" style="background:#000;border:1px solid #333">A</div>';
|
|
2459
|
+
html += '<div class="msg-content-col">';
|
|
2460
|
+
html += '<div class="msg-label">助手</div>';
|
|
2461
|
+
html += '<div class="msg-bubble msg-bubble-assistant">' + formatMsgText(msg.text || '') + '</div>';
|
|
2462
|
+
if (msg.toolCalls && msg.toolCalls.length > 0) {
|
|
2463
|
+
html += '<div class="msg-tools">';
|
|
2464
|
+
msg.toolCalls.forEach(function(tc) {
|
|
2465
|
+
html += '<span class="msg-tool-tag">🔧 ' + escapeHtml(tc.name || 'tool') + '</span>';
|
|
2466
|
+
});
|
|
2467
|
+
html += '</div>';
|
|
2468
|
+
}
|
|
2469
|
+
html += '</div>';
|
|
2470
|
+
html += '</div>';
|
|
2505
2471
|
}
|
|
2506
|
-
html += '</div>';
|
|
2507
2472
|
});
|
|
2508
2473
|
msgViewerContent.innerHTML = html;
|
|
2509
2474
|
msgViewerContent.scrollTop = msgViewerContent.scrollHeight;
|
|
@@ -2515,7 +2480,6 @@
|
|
|
2515
2480
|
|
|
2516
2481
|
function closeMessageViewer() {
|
|
2517
2482
|
messageViewer.classList.remove('visible');
|
|
2518
|
-
rebindTouchScroll();
|
|
2519
2483
|
}
|
|
2520
2484
|
|
|
2521
2485
|
msgViewerClose.addEventListener('click', function(e) {
|