claude-opencode-viewer 2.4.2 → 2.5.0
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.html +228 -2
- package/package.json +1 -1
package/index.html
CHANGED
|
@@ -487,6 +487,93 @@
|
|
|
487
487
|
border-color: #555;
|
|
488
488
|
color: #fff;
|
|
489
489
|
}
|
|
490
|
+
|
|
491
|
+
/* 选择模式:原位文本层 */
|
|
492
|
+
#terminal.select-mode .xterm-screen {
|
|
493
|
+
visibility: hidden;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
#select-text-layer {
|
|
497
|
+
display: none;
|
|
498
|
+
position: absolute;
|
|
499
|
+
top: 0; left: 0; right: 0; bottom: 0;
|
|
500
|
+
overflow-y: auto;
|
|
501
|
+
-webkit-overflow-scrolling: touch;
|
|
502
|
+
background: #0a0a0a;
|
|
503
|
+
padding: 4px 8px;
|
|
504
|
+
touch-action: auto;
|
|
505
|
+
z-index: 10;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
#select-text-layer.visible {
|
|
509
|
+
display: block;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
#select-hint {
|
|
513
|
+
position: sticky;
|
|
514
|
+
top: 0;
|
|
515
|
+
background: rgba(30,30,30,0.95);
|
|
516
|
+
color: #888;
|
|
517
|
+
font-size: 11px;
|
|
518
|
+
text-align: center;
|
|
519
|
+
padding: 6px 0;
|
|
520
|
+
border-bottom: 1px solid #333;
|
|
521
|
+
z-index: 1;
|
|
522
|
+
-webkit-user-select: none;
|
|
523
|
+
user-select: none;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
#select-text-layer pre {
|
|
527
|
+
margin: 0;
|
|
528
|
+
color: #d4d4d4;
|
|
529
|
+
font-family: Menlo, Monaco, "Courier New", monospace;
|
|
530
|
+
font-size: 11px;
|
|
531
|
+
line-height: 1.4;
|
|
532
|
+
white-space: pre-wrap;
|
|
533
|
+
word-break: break-all;
|
|
534
|
+
-webkit-user-select: text;
|
|
535
|
+
user-select: text;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
#select-mode-close {
|
|
539
|
+
position: absolute;
|
|
540
|
+
top: 6px;
|
|
541
|
+
right: 6px;
|
|
542
|
+
z-index: 20;
|
|
543
|
+
display: none;
|
|
544
|
+
background: rgba(50,50,50,0.9);
|
|
545
|
+
border: 1px solid #555;
|
|
546
|
+
color: #ccc;
|
|
547
|
+
width: 28px;
|
|
548
|
+
height: 28px;
|
|
549
|
+
border-radius: 50%;
|
|
550
|
+
font-size: 14px;
|
|
551
|
+
line-height: 26px;
|
|
552
|
+
text-align: center;
|
|
553
|
+
cursor: pointer;
|
|
554
|
+
-webkit-user-select: none;
|
|
555
|
+
user-select: none;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
/* 复制成功提示 */
|
|
559
|
+
#copy-toast {
|
|
560
|
+
display: none;
|
|
561
|
+
position: fixed;
|
|
562
|
+
top: 50%;
|
|
563
|
+
left: 50%;
|
|
564
|
+
transform: translate(-50%, -50%);
|
|
565
|
+
background: rgba(40, 167, 69, 0.9);
|
|
566
|
+
color: #fff;
|
|
567
|
+
padding: 10px 24px;
|
|
568
|
+
border-radius: 8px;
|
|
569
|
+
font-size: 14px;
|
|
570
|
+
z-index: 9999;
|
|
571
|
+
pointer-events: none;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
#copy-toast.show {
|
|
575
|
+
display: block;
|
|
576
|
+
}
|
|
490
577
|
</style>
|
|
491
578
|
</head>
|
|
492
579
|
<body>
|
|
@@ -510,7 +597,7 @@
|
|
|
510
597
|
</button>
|
|
511
598
|
</div>
|
|
512
599
|
<div id="mode-switcher">
|
|
513
|
-
<span id="mode-label"
|
|
600
|
+
<span id="mode-label"></span>
|
|
514
601
|
<select id="mode-select">
|
|
515
602
|
<option value="opencode">OpenCode</option>
|
|
516
603
|
<option value="claude">Claude</option>
|
|
@@ -584,7 +671,13 @@
|
|
|
584
671
|
|
|
585
672
|
<div id="content">
|
|
586
673
|
<div id="terminal-container">
|
|
587
|
-
<div id="terminal"
|
|
674
|
+
<div id="terminal">
|
|
675
|
+
<div id="select-text-layer">
|
|
676
|
+
<div id="select-hint">长按选择文本 · 点右上角 ✕ 返回终端</div>
|
|
677
|
+
<pre id="select-text-pre"></pre>
|
|
678
|
+
</div>
|
|
679
|
+
<button id="select-mode-close">✕</button>
|
|
680
|
+
</div>
|
|
588
681
|
<div id="virtual-keybar">
|
|
589
682
|
<div class="virtual-key" data-key="up">↑</div>
|
|
590
683
|
<div class="virtual-key" data-key="down">↓</div>
|
|
@@ -596,11 +689,13 @@
|
|
|
596
689
|
<div class="virtual-key" data-key="ctrlc">Ctrl+C</div>
|
|
597
690
|
<div class="virtual-key scroll-key" data-scroll="-5">⇡ 滚动</div>
|
|
598
691
|
<div class="virtual-key scroll-key" data-scroll="5">⇣ 滚动</div>
|
|
692
|
+
<div class="virtual-key" id="btn-copy">复制</div>
|
|
599
693
|
</div>
|
|
600
694
|
</div>
|
|
601
695
|
</div>
|
|
602
696
|
</div>
|
|
603
697
|
|
|
698
|
+
<div id="copy-toast">已复制</div>
|
|
604
699
|
<script src="https://cdn.jsdelivr.net/npm/@xterm/xterm@5.5.0/lib/xterm.min.js"></script>
|
|
605
700
|
<script>
|
|
606
701
|
(function() {
|
|
@@ -837,17 +932,40 @@
|
|
|
837
932
|
}
|
|
838
933
|
}
|
|
839
934
|
|
|
935
|
+
// 长按检测
|
|
936
|
+
var longPressTimer = null;
|
|
937
|
+
var longPressTriggered = false;
|
|
938
|
+
var LONG_PRESS_DELAY = 500; // ms
|
|
939
|
+
|
|
940
|
+
function clearLongPress() {
|
|
941
|
+
if (longPressTimer) {
|
|
942
|
+
clearTimeout(longPressTimer);
|
|
943
|
+
longPressTimer = null;
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
|
|
840
947
|
function handleTouchStart(e) {
|
|
841
948
|
console.log('[scroll] touchstart');
|
|
842
949
|
stopMomentum();
|
|
950
|
+
longPressTriggered = false;
|
|
951
|
+
clearLongPress();
|
|
843
952
|
if (e.touches.length !== 1) return;
|
|
844
953
|
lastY = e.touches[0].clientY;
|
|
845
954
|
lastTime = performance.now();
|
|
846
955
|
velocitySamples = [];
|
|
956
|
+
|
|
957
|
+
// 启动长按计时器
|
|
958
|
+
longPressTimer = setTimeout(function() {
|
|
959
|
+
longPressTriggered = true;
|
|
960
|
+
longPressTimer = null;
|
|
961
|
+
openSelectMode();
|
|
962
|
+
}, LONG_PRESS_DELAY);
|
|
847
963
|
}
|
|
848
964
|
|
|
849
965
|
function handleTouchMove(e) {
|
|
850
966
|
if (e.touches.length !== 1) return;
|
|
967
|
+
// 有移动则取消长按
|
|
968
|
+
clearLongPress();
|
|
851
969
|
var y = e.touches[0].clientY;
|
|
852
970
|
var now = performance.now();
|
|
853
971
|
var dt = now - lastTime;
|
|
@@ -871,6 +989,16 @@
|
|
|
871
989
|
|
|
872
990
|
function handleTouchEnd() {
|
|
873
991
|
console.log('[scroll] touchend');
|
|
992
|
+
clearLongPress();
|
|
993
|
+
|
|
994
|
+
// 长按已触发,不执行滚动惯性
|
|
995
|
+
if (longPressTriggered) {
|
|
996
|
+
longPressTriggered = false;
|
|
997
|
+
pendingDy = 0;
|
|
998
|
+
pixelAccum = 0;
|
|
999
|
+
velocitySamples = [];
|
|
1000
|
+
return;
|
|
1001
|
+
}
|
|
874
1002
|
|
|
875
1003
|
if (scrollRaf) {
|
|
876
1004
|
cancelAnimationFrame(scrollRaf);
|
|
@@ -1563,6 +1691,104 @@
|
|
|
1563
1691
|
}
|
|
1564
1692
|
});
|
|
1565
1693
|
|
|
1694
|
+
// 提取终端缓冲区文本
|
|
1695
|
+
function getTerminalText() {
|
|
1696
|
+
var buf = term.buffer.active;
|
|
1697
|
+
var lines = [];
|
|
1698
|
+
for (var i = 0; i < buf.length; i++) {
|
|
1699
|
+
var line = buf.getLine(i);
|
|
1700
|
+
if (line) lines.push(line.translateToString(true));
|
|
1701
|
+
}
|
|
1702
|
+
// 去除尾部空行
|
|
1703
|
+
while (lines.length > 0 && lines[lines.length - 1].trim() === '') {
|
|
1704
|
+
lines.pop();
|
|
1705
|
+
}
|
|
1706
|
+
return lines.join('\n');
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1709
|
+
// 复制到剪贴板
|
|
1710
|
+
function copyToClipboard(text) {
|
|
1711
|
+
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
1712
|
+
navigator.clipboard.writeText(text).then(showCopyToast).catch(function() {
|
|
1713
|
+
fallbackCopy(text);
|
|
1714
|
+
});
|
|
1715
|
+
} else {
|
|
1716
|
+
fallbackCopy(text);
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1720
|
+
function fallbackCopy(text) {
|
|
1721
|
+
var ta = document.createElement('textarea');
|
|
1722
|
+
ta.value = text;
|
|
1723
|
+
ta.style.cssText = 'position:fixed;left:-9999px;top:-9999px';
|
|
1724
|
+
document.body.appendChild(ta);
|
|
1725
|
+
ta.select();
|
|
1726
|
+
document.execCommand('copy');
|
|
1727
|
+
document.body.removeChild(ta);
|
|
1728
|
+
showCopyToast();
|
|
1729
|
+
}
|
|
1730
|
+
|
|
1731
|
+
function showCopyToast() {
|
|
1732
|
+
var toast = document.getElementById('copy-toast');
|
|
1733
|
+
toast.classList.add('show');
|
|
1734
|
+
setTimeout(function() { toast.classList.remove('show'); }, 1200);
|
|
1735
|
+
}
|
|
1736
|
+
|
|
1737
|
+
// "复制" 按钮
|
|
1738
|
+
document.getElementById('btn-copy').addEventListener('touchend', function(e) {
|
|
1739
|
+
e.preventDefault();
|
|
1740
|
+
var text = getTerminalText();
|
|
1741
|
+
if (text) copyToClipboard(text);
|
|
1742
|
+
});
|
|
1743
|
+
document.getElementById('btn-copy').addEventListener('click', function(e) {
|
|
1744
|
+
e.preventDefault();
|
|
1745
|
+
var text = getTerminalText();
|
|
1746
|
+
if (text) copyToClipboard(text);
|
|
1747
|
+
});
|
|
1748
|
+
|
|
1749
|
+
// 方案2: 长按进入选择模式 — 原位显示可选纯文本
|
|
1750
|
+
var selectTextLayer = document.getElementById('select-text-layer');
|
|
1751
|
+
var selectTextPre = document.getElementById('select-text-pre');
|
|
1752
|
+
var selectModeClose = document.getElementById('select-mode-close');
|
|
1753
|
+
var inSelectMode = false;
|
|
1754
|
+
|
|
1755
|
+
function openSelectMode() {
|
|
1756
|
+
if (inSelectMode) return;
|
|
1757
|
+
inSelectMode = true;
|
|
1758
|
+
// 收起键盘
|
|
1759
|
+
var xtermTa = terminalEl.querySelector('.xterm-helper-textarea');
|
|
1760
|
+
if (xtermTa) xtermTa.blur();
|
|
1761
|
+
document.activeElement && document.activeElement.blur();
|
|
1762
|
+
var text = getTerminalText();
|
|
1763
|
+
selectTextPre.textContent = text || '(终端内容为空)';
|
|
1764
|
+
terminalEl.classList.add('select-mode');
|
|
1765
|
+
selectTextLayer.classList.add('visible');
|
|
1766
|
+
selectModeClose.style.display = 'block';
|
|
1767
|
+
unbindTouchScroll();
|
|
1768
|
+
}
|
|
1769
|
+
|
|
1770
|
+
function closeSelectMode() {
|
|
1771
|
+
if (!inSelectMode) return;
|
|
1772
|
+
inSelectMode = false;
|
|
1773
|
+
terminalEl.classList.remove('select-mode');
|
|
1774
|
+
selectTextLayer.classList.remove('visible');
|
|
1775
|
+
selectModeClose.style.display = 'none';
|
|
1776
|
+
window.getSelection().removeAllRanges();
|
|
1777
|
+
rebindTouchScroll();
|
|
1778
|
+
}
|
|
1779
|
+
|
|
1780
|
+
selectModeClose.addEventListener('click', function(e) {
|
|
1781
|
+
e.preventDefault();
|
|
1782
|
+
e.stopPropagation();
|
|
1783
|
+
closeSelectMode();
|
|
1784
|
+
});
|
|
1785
|
+
|
|
1786
|
+
selectModeClose.addEventListener('touchend', function(e) {
|
|
1787
|
+
e.preventDefault();
|
|
1788
|
+
e.stopPropagation();
|
|
1789
|
+
closeSelectMode();
|
|
1790
|
+
});
|
|
1791
|
+
|
|
1566
1792
|
// 初始化虚拟按键事件
|
|
1567
1793
|
setupVirtualKeyEvents();
|
|
1568
1794
|
|