claude-opencode-viewer 2.6.44 → 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 +30 -4
- package/index.html +192 -180
- package/package.json +1 -1
package/index-pc.html
CHANGED
|
@@ -365,9 +365,32 @@
|
|
|
365
365
|
overscroll-behavior: contain;
|
|
366
366
|
}
|
|
367
367
|
|
|
368
|
-
#terminal.transitioning {
|
|
369
|
-
|
|
370
|
-
|
|
368
|
+
#terminal.transitioning .xterm { visibility: hidden; }
|
|
369
|
+
@keyframes loading-dots {
|
|
370
|
+
0% { content: ''; }
|
|
371
|
+
25% { content: '.'; }
|
|
372
|
+
50% { content: '..'; }
|
|
373
|
+
75% { content: '...'; }
|
|
374
|
+
}
|
|
375
|
+
#switch-overlay {
|
|
376
|
+
display: none;
|
|
377
|
+
position: absolute;
|
|
378
|
+
top: 0; left: 0; right: 0; bottom: 0;
|
|
379
|
+
background: #0a0a0a;
|
|
380
|
+
color: #ccc;
|
|
381
|
+
align-items: flex-start;
|
|
382
|
+
padding-top: 40px;
|
|
383
|
+
justify-content: center;
|
|
384
|
+
font-size: 15px;
|
|
385
|
+
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
|
|
386
|
+
letter-spacing: 1px;
|
|
387
|
+
z-index: 10;
|
|
388
|
+
}
|
|
389
|
+
#switch-overlay::after {
|
|
390
|
+
content: '';
|
|
391
|
+
animation: loading-dots 1.2s steps(4, end) infinite;
|
|
392
|
+
}
|
|
393
|
+
#terminal.transitioning #switch-overlay { display: flex;
|
|
371
394
|
}
|
|
372
395
|
|
|
373
396
|
|
|
@@ -994,7 +1017,8 @@
|
|
|
994
1017
|
|
|
995
1018
|
<div id="content">
|
|
996
1019
|
<div id="terminal-container">
|
|
997
|
-
<div id="terminal">
|
|
1020
|
+
<div id="terminal" style="position:relative;">
|
|
1021
|
+
<div id="switch-overlay">正在切换</div>
|
|
998
1022
|
<div id="select-text-layer">
|
|
999
1023
|
<div id="select-hint">长按选择文本 · 点右上角 ✕ 返回终端</div>
|
|
1000
1024
|
<pre id="select-text-pre"></pre>
|
|
@@ -1567,6 +1591,8 @@
|
|
|
1567
1591
|
currentMode = mode;
|
|
1568
1592
|
modeSelect.value = mode;
|
|
1569
1593
|
document.getElementById('mode-label').textContent = '';
|
|
1594
|
+
var label = mode === 'claude' ? 'Claude' : 'OpenCode';
|
|
1595
|
+
term.write('\r\n 正在启动 ' + label + (sessionId ? '(恢复会话)' : '') + '...\r\n');
|
|
1570
1596
|
var msg = { type: 'init', mode: mode };
|
|
1571
1597
|
if (sessionId) msg.sessionId = sessionId;
|
|
1572
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;
|
|
@@ -54,7 +57,8 @@
|
|
|
54
57
|
background: #111;
|
|
55
58
|
border-bottom: 1px solid #222;
|
|
56
59
|
display: flex;
|
|
57
|
-
align-items:
|
|
60
|
+
align-items: flex-start;
|
|
61
|
+
padding-top: 40px;
|
|
58
62
|
justify-content: space-between;
|
|
59
63
|
flex-shrink: 0;
|
|
60
64
|
height: 40px;
|
|
@@ -90,7 +94,8 @@
|
|
|
90
94
|
|
|
91
95
|
#session-history-header {
|
|
92
96
|
display: flex;
|
|
93
|
-
align-items:
|
|
97
|
+
align-items: flex-start;
|
|
98
|
+
padding-top: 40px;
|
|
94
99
|
justify-content: space-between;
|
|
95
100
|
padding: 12px 16px;
|
|
96
101
|
background: #111;
|
|
@@ -124,7 +129,8 @@
|
|
|
124
129
|
|
|
125
130
|
.session-item {
|
|
126
131
|
display: flex;
|
|
127
|
-
align-items:
|
|
132
|
+
align-items: flex-start;
|
|
133
|
+
padding-top: 40px;
|
|
128
134
|
gap: 8px;
|
|
129
135
|
padding: 8px 12px;
|
|
130
136
|
background: #1a1a1a;
|
|
@@ -208,7 +214,8 @@
|
|
|
208
214
|
cursor: pointer;
|
|
209
215
|
border-radius: 4px;
|
|
210
216
|
display: flex;
|
|
211
|
-
align-items:
|
|
217
|
+
align-items: flex-start;
|
|
218
|
+
padding-top: 40px;
|
|
212
219
|
gap: 3px;
|
|
213
220
|
flex-shrink: 0;
|
|
214
221
|
}
|
|
@@ -249,7 +256,8 @@
|
|
|
249
256
|
cursor: pointer;
|
|
250
257
|
border-radius: 50%;
|
|
251
258
|
display: flex;
|
|
252
|
-
align-items:
|
|
259
|
+
align-items: flex-start;
|
|
260
|
+
padding-top: 40px;
|
|
253
261
|
justify-content: center;
|
|
254
262
|
transition: all 0.15s;
|
|
255
263
|
-webkit-tap-highlight-color: transparent;
|
|
@@ -294,7 +302,8 @@
|
|
|
294
302
|
|
|
295
303
|
#claude-detail-header {
|
|
296
304
|
display: flex;
|
|
297
|
-
align-items:
|
|
305
|
+
align-items: flex-start;
|
|
306
|
+
padding-top: 40px;
|
|
298
307
|
padding: 10px 12px;
|
|
299
308
|
background: #111;
|
|
300
309
|
border-bottom: 1px solid #222;
|
|
@@ -347,7 +356,8 @@
|
|
|
347
356
|
#mode-switcher {
|
|
348
357
|
display: flex;
|
|
349
358
|
gap: 4px;
|
|
350
|
-
align-items:
|
|
359
|
+
align-items: flex-start;
|
|
360
|
+
padding-top: 40px;
|
|
351
361
|
}
|
|
352
362
|
|
|
353
363
|
#mode-label {
|
|
@@ -393,16 +403,33 @@
|
|
|
393
403
|
overscroll-behavior: contain;
|
|
394
404
|
}
|
|
395
405
|
|
|
396
|
-
#terminal.transitioning {
|
|
397
|
-
|
|
398
|
-
|
|
406
|
+
#terminal.transitioning .xterm { visibility: hidden; }
|
|
407
|
+
#switch-overlay {
|
|
408
|
+
display: none;
|
|
409
|
+
position: absolute;
|
|
410
|
+
top: 0; left: 0; right: 0; bottom: 0;
|
|
411
|
+
background: #0a0a0a;
|
|
412
|
+
color: #ccc;
|
|
413
|
+
align-items: flex-start;
|
|
414
|
+
padding-top: 40px;
|
|
415
|
+
justify-content: center;
|
|
416
|
+
font-size: 16px;
|
|
417
|
+
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
|
|
418
|
+
letter-spacing: 1px;
|
|
419
|
+
z-index: 10;
|
|
420
|
+
}
|
|
421
|
+
#switch-overlay::after {
|
|
422
|
+
content: '';
|
|
423
|
+
animation: loading-dots 1.2s steps(4, end) infinite;
|
|
399
424
|
}
|
|
425
|
+
#terminal.transitioning #switch-overlay { display: flex; }
|
|
400
426
|
|
|
401
427
|
/* 参考 cc-viewer 的 TerminalPanel.module.css: 虚拟键盘栏 */
|
|
402
428
|
#virtual-keybar {
|
|
403
429
|
display: flex;
|
|
404
430
|
gap: 6px;
|
|
405
431
|
padding: 8px 10px;
|
|
432
|
+
padding-bottom: calc(8px + env(safe-area-inset-bottom, 0px));
|
|
406
433
|
background: #111;
|
|
407
434
|
border-top: 1px solid #222;
|
|
408
435
|
overflow-x: auto;
|
|
@@ -421,21 +448,19 @@
|
|
|
421
448
|
font-family: Menlo, Monaco, monospace;
|
|
422
449
|
cursor: pointer;
|
|
423
450
|
user-select: none;
|
|
451
|
+
-webkit-user-select: none;
|
|
424
452
|
-webkit-tap-highlight-color: transparent;
|
|
453
|
+
-webkit-touch-callout: none;
|
|
425
454
|
touch-action: pan-x;
|
|
426
455
|
min-width: 44px;
|
|
427
456
|
min-height: 44px;
|
|
428
457
|
display: flex;
|
|
429
458
|
align-items: center;
|
|
430
459
|
justify-content: center;
|
|
431
|
-
border: none;
|
|
432
460
|
outline: none;
|
|
433
|
-
-webkit-user-select: none;
|
|
434
|
-
-moz-user-select: none;
|
|
435
|
-
-ms-user-select: none;
|
|
436
461
|
}
|
|
437
462
|
|
|
438
|
-
.virtual-key
|
|
463
|
+
.virtual-key-pressed {
|
|
439
464
|
background: #333;
|
|
440
465
|
border-color: #555;
|
|
441
466
|
color: #fff;
|
|
@@ -444,18 +469,20 @@
|
|
|
444
469
|
/* 消息查看器 */
|
|
445
470
|
#message-viewer {
|
|
446
471
|
display: none;
|
|
447
|
-
position:
|
|
448
|
-
|
|
472
|
+
position: absolute;
|
|
473
|
+
inset: 0;
|
|
449
474
|
background: #0a0a0a;
|
|
450
475
|
z-index: 1000;
|
|
451
476
|
flex-direction: column;
|
|
477
|
+
overflow: hidden;
|
|
452
478
|
}
|
|
453
479
|
#message-viewer.visible {
|
|
454
480
|
display: flex;
|
|
455
481
|
}
|
|
456
482
|
#msg-viewer-header {
|
|
457
483
|
display: flex;
|
|
458
|
-
align-items:
|
|
484
|
+
align-items: flex-start;
|
|
485
|
+
padding-top: 40px;
|
|
459
486
|
justify-content: space-between;
|
|
460
487
|
padding: 10px 14px;
|
|
461
488
|
background: #111;
|
|
@@ -481,61 +508,68 @@
|
|
|
481
508
|
}
|
|
482
509
|
#msg-viewer-content {
|
|
483
510
|
flex: 1;
|
|
484
|
-
overflow
|
|
511
|
+
overflow: auto;
|
|
485
512
|
-webkit-overflow-scrolling: touch;
|
|
513
|
+
overscroll-behavior: contain;
|
|
486
514
|
padding: 12px;
|
|
487
515
|
}
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
border:
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
}
|
|
498
|
-
.msg-
|
|
499
|
-
|
|
500
|
-
border-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
text-transform: uppercase;
|
|
507
|
-
margin-bottom: 6px;
|
|
508
|
-
}
|
|
509
|
-
.msg-text {
|
|
510
|
-
color: #ddd;
|
|
511
|
-
font-size: 13px;
|
|
512
|
-
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;
|
|
513
534
|
white-space: pre-wrap;
|
|
514
|
-
word-break: break-word;
|
|
515
|
-
-webkit-user-select: text;
|
|
516
|
-
user-select: text;
|
|
517
|
-
}
|
|
518
|
-
.msg-text pre {
|
|
519
|
-
background: #0d0d0d;
|
|
520
|
-
border: 1px solid #333;
|
|
521
|
-
border-radius: 4px;
|
|
522
|
-
padding: 8px;
|
|
523
|
-
overflow-x: auto;
|
|
524
|
-
font-family: 'Cascadia Code', 'Fira Code', 'Consolas', monospace;
|
|
525
|
-
font-size: 12px;
|
|
526
|
-
line-height: 1.4;
|
|
527
|
-
white-space: pre;
|
|
528
|
-
-webkit-overflow-scrolling: touch;
|
|
529
|
-
}
|
|
530
|
-
.msg-tool {
|
|
531
|
-
margin-top: 6px;
|
|
532
|
-
padding: 6px 8px;
|
|
533
|
-
background: #1a1a0a;
|
|
534
|
-
border: 1px solid #333;
|
|
535
|
-
border-radius: 4px;
|
|
536
|
-
font-size: 11px;
|
|
537
|
-
color: #f0ad4e;
|
|
538
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; }
|
|
539
573
|
.msg-empty {
|
|
540
574
|
text-align: center;
|
|
541
575
|
padding: 40px 20px;
|
|
@@ -582,7 +616,8 @@
|
|
|
582
616
|
|
|
583
617
|
#git-diff-header {
|
|
584
618
|
display: flex;
|
|
585
|
-
align-items:
|
|
619
|
+
align-items: flex-start;
|
|
620
|
+
padding-top: 40px;
|
|
586
621
|
justify-content: space-between;
|
|
587
622
|
padding: 12px 16px;
|
|
588
623
|
background: #111;
|
|
@@ -606,7 +641,8 @@
|
|
|
606
641
|
|
|
607
642
|
.git-diff-file-item {
|
|
608
643
|
display: flex;
|
|
609
|
-
align-items:
|
|
644
|
+
align-items: flex-start;
|
|
645
|
+
padding-top: 40px;
|
|
610
646
|
padding: 6px 12px;
|
|
611
647
|
cursor: pointer;
|
|
612
648
|
color: #ccc;
|
|
@@ -650,7 +686,8 @@
|
|
|
650
686
|
|
|
651
687
|
.git-diff-content-header {
|
|
652
688
|
display: flex;
|
|
653
|
-
align-items:
|
|
689
|
+
align-items: flex-start;
|
|
690
|
+
padding-top: 40px;
|
|
654
691
|
gap: 10px;
|
|
655
692
|
padding: 8px 12px;
|
|
656
693
|
border-bottom: 1px solid #2a2a2a;
|
|
@@ -690,7 +727,8 @@
|
|
|
690
727
|
flex: 1;
|
|
691
728
|
display: flex;
|
|
692
729
|
flex-direction: column;
|
|
693
|
-
align-items:
|
|
730
|
+
align-items: flex-start;
|
|
731
|
+
padding-top: 40px;
|
|
694
732
|
justify-content: center;
|
|
695
733
|
gap: 12px;
|
|
696
734
|
color: #333;
|
|
@@ -723,7 +761,8 @@
|
|
|
723
761
|
#docs-bar.visible { display: flex; }
|
|
724
762
|
#docs-header {
|
|
725
763
|
display: flex;
|
|
726
|
-
align-items:
|
|
764
|
+
align-items: flex-start;
|
|
765
|
+
padding-top: 40px;
|
|
727
766
|
justify-content: space-between;
|
|
728
767
|
padding: 12px 16px;
|
|
729
768
|
background: #111;
|
|
@@ -743,7 +782,8 @@
|
|
|
743
782
|
}
|
|
744
783
|
.docs-file-item {
|
|
745
784
|
display: flex;
|
|
746
|
-
align-items:
|
|
785
|
+
align-items: flex-start;
|
|
786
|
+
padding-top: 40px;
|
|
747
787
|
padding: 8px 12px;
|
|
748
788
|
cursor: pointer;
|
|
749
789
|
color: #ccc;
|
|
@@ -789,7 +829,8 @@
|
|
|
789
829
|
flex: 1;
|
|
790
830
|
display: flex;
|
|
791
831
|
flex-direction: column;
|
|
792
|
-
align-items:
|
|
832
|
+
align-items: flex-start;
|
|
833
|
+
padding-top: 40px;
|
|
793
834
|
justify-content: center;
|
|
794
835
|
gap: 12px;
|
|
795
836
|
color: #333;
|
|
@@ -1054,7 +1095,8 @@
|
|
|
1054
1095
|
|
|
1055
1096
|
<div id="content">
|
|
1056
1097
|
<div id="terminal-container">
|
|
1057
|
-
<div id="terminal">
|
|
1098
|
+
<div id="terminal" style="position:relative;">
|
|
1099
|
+
<div id="switch-overlay">正在切换</div>
|
|
1058
1100
|
</div>
|
|
1059
1101
|
<div id="virtual-keybar">
|
|
1060
1102
|
<div class="virtual-key" data-key="up">↑</div>
|
|
@@ -1069,21 +1111,24 @@
|
|
|
1069
1111
|
</div>
|
|
1070
1112
|
</div>
|
|
1071
1113
|
</div>
|
|
1072
|
-
</div>
|
|
1073
1114
|
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
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>
|
|
1081
1123
|
</div>
|
|
1082
1124
|
</div>
|
|
1083
1125
|
|
|
1084
1126
|
<div id="copy-toast">已复制</div>
|
|
1085
|
-
<script src="https://cdn.jsdelivr.net/npm/@xterm/xterm@
|
|
1086
|
-
<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>
|
|
1087
1132
|
<script>
|
|
1088
1133
|
(function() {
|
|
1089
1134
|
var isMobile = /Mobi|Android|iPhone|iPad|iPod/i.test(navigator.userAgent);
|
|
@@ -1096,6 +1141,9 @@
|
|
|
1096
1141
|
|
|
1097
1142
|
var term = new Terminal({
|
|
1098
1143
|
cursorBlink: !isMobile,
|
|
1144
|
+
cursorStyle: 'bar',
|
|
1145
|
+
cursorWidth: 1,
|
|
1146
|
+
cursorInactiveStyle: 'none',
|
|
1099
1147
|
fontSize: fontSize,
|
|
1100
1148
|
fontFamily: 'Menlo, Monaco, "Courier New", monospace',
|
|
1101
1149
|
theme: {
|
|
@@ -1112,6 +1160,15 @@
|
|
|
1112
1160
|
|
|
1113
1161
|
term.open(document.getElementById('terminal'));
|
|
1114
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
|
+
|
|
1115
1172
|
// WebGL 渲染器:GPU 加速绘制,非 iOS 设备启用(iOS WebGL 性能差)
|
|
1116
1173
|
if (!isIOS && window.WebglAddon) {
|
|
1117
1174
|
try {
|
|
@@ -1801,6 +1858,11 @@
|
|
|
1801
1858
|
if (seq && ws && ws.readyState === 1 && !isTransitioning) {
|
|
1802
1859
|
ws.send(JSON.stringify({ type: 'input', data: seq }));
|
|
1803
1860
|
}
|
|
1861
|
+
// 手机端主动 blur xterm textarea,防止系统键盘弹出
|
|
1862
|
+
if (isMobile) {
|
|
1863
|
+
var xtermTa = terminalEl.querySelector('.xterm-helper-textarea');
|
|
1864
|
+
if (xtermTa) xtermTa.blur();
|
|
1865
|
+
}
|
|
1804
1866
|
}
|
|
1805
1867
|
|
|
1806
1868
|
function scrollTerminal(lines) {
|
|
@@ -1808,111 +1870,62 @@
|
|
|
1808
1870
|
doScroll(lines);
|
|
1809
1871
|
}
|
|
1810
1872
|
|
|
1811
|
-
//
|
|
1812
|
-
// 每个按键独立绑定事件,不在容器上绑定
|
|
1873
|
+
// 虚拟按键触摸处理
|
|
1813
1874
|
var vkStartX = 0, vkStartY = 0, vkMoved = false, vkTarget = null;
|
|
1814
|
-
var scrollInterval = null; // 长按滚动定时器
|
|
1815
1875
|
|
|
1816
1876
|
function setupVirtualKeyEvents() {
|
|
1817
1877
|
var keys = document.querySelectorAll('.virtual-key');
|
|
1818
1878
|
keys.forEach(function(key) {
|
|
1819
|
-
// 防止元素获得焦点
|
|
1820
1879
|
key.setAttribute('tabindex', '-1');
|
|
1821
1880
|
|
|
1822
|
-
// 移动端触摸事件
|
|
1823
1881
|
key.addEventListener('touchstart', function(e) {
|
|
1824
1882
|
var touch = e.touches[0];
|
|
1825
1883
|
vkStartX = touch.clientX;
|
|
1826
1884
|
vkStartY = touch.clientY;
|
|
1827
1885
|
vkMoved = false;
|
|
1828
1886
|
vkTarget = e.currentTarget;
|
|
1829
|
-
vkTarget.
|
|
1830
|
-
|
|
1831
|
-
// 如果是滚动按钮,阻止默认行为(防止键盘弹出)并启动持续滚动
|
|
1832
|
-
var scrollLines = e.currentTarget.getAttribute('data-scroll');
|
|
1833
|
-
if (scrollLines) {
|
|
1834
|
-
e.preventDefault(); // 关键:阻止默认行为,防止键盘弹出
|
|
1835
|
-
scrollTerminal(parseInt(scrollLines, 10));
|
|
1836
|
-
scrollInterval = setInterval(function() {
|
|
1837
|
-
scrollTerminal(parseInt(scrollLines, 10));
|
|
1838
|
-
}, 100);
|
|
1839
|
-
}
|
|
1840
|
-
}, { passive: false }); // 必须是 false 才能调用 preventDefault()
|
|
1887
|
+
vkTarget.classList.add('virtual-key-pressed');
|
|
1888
|
+
}, { passive: true });
|
|
1841
1889
|
|
|
1842
1890
|
key.addEventListener('touchmove', function(e) {
|
|
1843
1891
|
if (vkMoved) return;
|
|
1844
1892
|
var touch = e.touches[0];
|
|
1845
1893
|
var dx = touch.clientX - vkStartX;
|
|
1846
1894
|
var dy = touch.clientY - vkStartY;
|
|
1847
|
-
if (dx * dx + dy * dy > 64) {
|
|
1895
|
+
if (dx * dx + dy * dy > 64) {
|
|
1848
1896
|
vkMoved = true;
|
|
1849
|
-
if (vkTarget) {
|
|
1850
|
-
vkTarget.style.background = '';
|
|
1851
|
-
}
|
|
1852
1897
|
}
|
|
1853
1898
|
}, { passive: true });
|
|
1854
1899
|
|
|
1855
1900
|
key.addEventListener('touchend', function(e) {
|
|
1856
|
-
|
|
1857
|
-
if (scrollInterval) {
|
|
1858
|
-
clearInterval(scrollInterval);
|
|
1859
|
-
scrollInterval = null;
|
|
1860
|
-
}
|
|
1861
|
-
|
|
1901
|
+
e.preventDefault();
|
|
1862
1902
|
if (vkTarget) {
|
|
1863
|
-
vkTarget.
|
|
1903
|
+
vkTarget.classList.remove('virtual-key-pressed');
|
|
1864
1904
|
vkTarget = null;
|
|
1865
1905
|
}
|
|
1866
|
-
|
|
1867
|
-
// 如果没有移动,触发按键功能并阻止默认行为
|
|
1868
1906
|
if (!vkMoved) {
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
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 {
|
|
1873
1913
|
var keyName = e.currentTarget.getAttribute('data-key');
|
|
1874
1914
|
sendKey(keyName);
|
|
1875
1915
|
}
|
|
1876
1916
|
}
|
|
1877
1917
|
}, { passive: false });
|
|
1878
1918
|
|
|
1879
|
-
// PC端点击支持
|
|
1880
1919
|
key.addEventListener('click', function(e) {
|
|
1881
1920
|
e.preventDefault();
|
|
1882
|
-
var
|
|
1883
|
-
if (
|
|
1884
|
-
scrollTerminal(parseInt(
|
|
1921
|
+
var scrollAttr = e.currentTarget.getAttribute('data-scroll');
|
|
1922
|
+
if (scrollAttr) {
|
|
1923
|
+
scrollTerminal(parseInt(scrollAttr, 10));
|
|
1885
1924
|
} else {
|
|
1886
1925
|
var keyName = e.currentTarget.getAttribute('data-key');
|
|
1887
1926
|
sendKey(keyName);
|
|
1888
1927
|
}
|
|
1889
1928
|
});
|
|
1890
|
-
|
|
1891
|
-
// PC端鼠标按下支持(长按滚动)
|
|
1892
|
-
key.addEventListener('mousedown', function(e) {
|
|
1893
|
-
e.preventDefault();
|
|
1894
|
-
var scrollLines = e.currentTarget.getAttribute('data-scroll');
|
|
1895
|
-
if (scrollLines) {
|
|
1896
|
-
scrollTerminal(parseInt(scrollLines, 10));
|
|
1897
|
-
scrollInterval = setInterval(function() {
|
|
1898
|
-
scrollTerminal(parseInt(scrollLines, 10));
|
|
1899
|
-
}, 100);
|
|
1900
|
-
}
|
|
1901
|
-
});
|
|
1902
|
-
|
|
1903
|
-
key.addEventListener('mouseup', function(e) {
|
|
1904
|
-
if (scrollInterval) {
|
|
1905
|
-
clearInterval(scrollInterval);
|
|
1906
|
-
scrollInterval = null;
|
|
1907
|
-
}
|
|
1908
|
-
});
|
|
1909
|
-
|
|
1910
|
-
key.addEventListener('mouseleave', function(e) {
|
|
1911
|
-
if (scrollInterval) {
|
|
1912
|
-
clearInterval(scrollInterval);
|
|
1913
|
-
scrollInterval = null;
|
|
1914
|
-
}
|
|
1915
|
-
});
|
|
1916
1929
|
});
|
|
1917
1930
|
}
|
|
1918
1931
|
|
|
@@ -2371,7 +2384,6 @@
|
|
|
2371
2384
|
|
|
2372
2385
|
messageViewer.classList.add('visible');
|
|
2373
2386
|
msgViewerContent.innerHTML = '<div class="msg-empty">加载中...</div>';
|
|
2374
|
-
unbindTouchScroll();
|
|
2375
2387
|
|
|
2376
2388
|
if (currentMode === 'claude') {
|
|
2377
2389
|
// Claude 模式:从 JSONL 文件读取消息
|
|
@@ -2432,22 +2444,12 @@
|
|
|
2432
2444
|
}
|
|
2433
2445
|
|
|
2434
2446
|
function formatMsgText(text) {
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
if (p.startsWith('```') && p.endsWith('```')) {
|
|
2441
|
-
// 去掉首尾 ```(可能带语言标记)
|
|
2442
|
-
var inner = p.slice(3, -3);
|
|
2443
|
-
var nlIdx = inner.indexOf('\n');
|
|
2444
|
-
if (nlIdx !== -1) inner = inner.slice(nlIdx + 1);
|
|
2445
|
-
result += '<pre>' + escapeHtml(inner) + '</pre>';
|
|
2446
|
-
} else {
|
|
2447
|
-
result += escapeHtml(p);
|
|
2448
|
-
}
|
|
2447
|
+
if (!text) return '';
|
|
2448
|
+
try {
|
|
2449
|
+
return DOMPurify.sanitize(marked.parse(text, { breaks: true }));
|
|
2450
|
+
} catch (e) {
|
|
2451
|
+
return escapeHtml(text);
|
|
2449
2452
|
}
|
|
2450
|
-
return result;
|
|
2451
2453
|
}
|
|
2452
2454
|
|
|
2453
2455
|
function renderMessages(messages) {
|
|
@@ -2458,19 +2460,30 @@
|
|
|
2458
2460
|
var html = '';
|
|
2459
2461
|
messages.forEach(function(msg) {
|
|
2460
2462
|
var role = msg.role || 'unknown';
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
html += '
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
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>';
|
|
2472
2486
|
}
|
|
2473
|
-
html += '</div>';
|
|
2474
2487
|
});
|
|
2475
2488
|
msgViewerContent.innerHTML = html;
|
|
2476
2489
|
msgViewerContent.scrollTop = msgViewerContent.scrollHeight;
|
|
@@ -2482,7 +2495,6 @@
|
|
|
2482
2495
|
|
|
2483
2496
|
function closeMessageViewer() {
|
|
2484
2497
|
messageViewer.classList.remove('visible');
|
|
2485
|
-
rebindTouchScroll();
|
|
2486
2498
|
}
|
|
2487
2499
|
|
|
2488
2500
|
msgViewerClose.addEventListener('click', function(e) {
|