termites 1.0.23 → 1.0.24
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/package.json +1 -1
- package/server.js +99 -74
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -645,6 +645,13 @@ class TermitesServer {
|
|
|
645
645
|
.mobile-toolbar button:active { opacity: 0.6; transform: scale(0.95); }
|
|
646
646
|
.mobile-toolbar button.active { background: var(--btn-active, rgba(255,255,255,0.15)); }
|
|
647
647
|
.mobile-toolbar .sep { width: 1px; background: currentColor; opacity: 0.2; margin: 0 4px; }
|
|
648
|
+
.sel-marker {
|
|
649
|
+
position: absolute; z-index: 50; padding: 4px 8px; border-radius: 4px;
|
|
650
|
+
font-size: 11px; font-weight: bold; cursor: grab; touch-action: none;
|
|
651
|
+
user-select: none; -webkit-user-select: none;
|
|
652
|
+
}
|
|
653
|
+
#sel-start-marker { background: #22c55e; color: #fff; }
|
|
654
|
+
#sel-end-marker { background: #ef4444; color: #fff; }
|
|
648
655
|
</style>
|
|
649
656
|
</head>
|
|
650
657
|
<body>
|
|
@@ -721,15 +728,12 @@ class TermitesServer {
|
|
|
721
728
|
<button data-key="ArrowDown">↓</button>
|
|
722
729
|
<div class="sep"></div>
|
|
723
730
|
<button data-seq="/">/</button>
|
|
724
|
-
<button id="sel-
|
|
725
|
-
<button id="sel-left-btn" class="sel-ctrl" style="display:none">◀</button>
|
|
726
|
-
<button id="sel-right-btn" class="sel-ctrl" style="display:none">▶</button>
|
|
727
|
-
<button id="sel-up-btn" class="sel-ctrl" style="display:none">▲</button>
|
|
728
|
-
<button id="sel-down-btn" class="sel-ctrl" style="display:none">▼</button>
|
|
729
|
-
<button id="sel-end-btn" class="sel-ctrl" style="display:none">End</button>
|
|
731
|
+
<button id="sel-btn" class="mod-btn">Sel</button>
|
|
730
732
|
<button id="copy-btn">Copy</button>
|
|
731
733
|
<button id="paste-btn">Paste</button>
|
|
732
734
|
</div>
|
|
735
|
+
<div id="sel-start-marker" class="sel-marker" style="display:none">▼Start</div>
|
|
736
|
+
<div id="sel-end-marker" class="sel-marker" style="display:none">▲End</div>
|
|
733
737
|
|
|
734
738
|
<script src="https://cdn.jsdelivr.net/npm/xterm@5.3.0/lib/xterm.min.js"></script>
|
|
735
739
|
<script src="https://cdn.jsdelivr.net/npm/xterm-addon-fit@0.8.0/lib/xterm-addon-fit.min.js"></script>
|
|
@@ -1065,17 +1069,34 @@ class TermitesServer {
|
|
|
1065
1069
|
pasteBtn.addEventListener('touchstart', handlePaste, { passive: false });
|
|
1066
1070
|
pasteBtn.addEventListener('click', handlePaste);
|
|
1067
1071
|
|
|
1068
|
-
// Selection mode with
|
|
1069
|
-
let selMode = false;
|
|
1072
|
+
// Selection mode with draggable markers
|
|
1073
|
+
let selMode = false;
|
|
1070
1074
|
let selStart = { col: 0, row: 0 };
|
|
1071
1075
|
let selEnd = { col: 0, row: 0 };
|
|
1072
|
-
const
|
|
1073
|
-
const
|
|
1074
|
-
const
|
|
1075
|
-
const
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1076
|
+
const selBtn = document.getElementById('sel-btn');
|
|
1077
|
+
const startMarker = document.getElementById('sel-start-marker');
|
|
1078
|
+
const endMarker = document.getElementById('sel-end-marker');
|
|
1079
|
+
const termContainer = document.getElementById('terminal-container');
|
|
1080
|
+
|
|
1081
|
+
function getCellSize() {
|
|
1082
|
+
try {
|
|
1083
|
+
return {
|
|
1084
|
+
width: term._core._renderService.dimensions.css.cell.width,
|
|
1085
|
+
height: term._core._renderService.dimensions.css.cell.height
|
|
1086
|
+
};
|
|
1087
|
+
} catch (e) {
|
|
1088
|
+
return { width: 9, height: 17 }; // fallback
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
function updateMarkerPositions() {
|
|
1093
|
+
const cell = getCellSize();
|
|
1094
|
+
const rect = termContainer.getBoundingClientRect();
|
|
1095
|
+
startMarker.style.left = (selStart.col * cell.width + 4) + 'px';
|
|
1096
|
+
startMarker.style.top = (selStart.row * cell.height) + 'px';
|
|
1097
|
+
endMarker.style.left = (selEnd.col * cell.width + 4) + 'px';
|
|
1098
|
+
endMarker.style.top = ((selEnd.row + 1) * cell.height) + 'px';
|
|
1099
|
+
}
|
|
1079
1100
|
|
|
1080
1101
|
function updateSelection() {
|
|
1081
1102
|
const startOffset = selStart.row * term.cols + selStart.col;
|
|
@@ -1084,76 +1105,80 @@ class TermitesServer {
|
|
|
1084
1105
|
if (length > 0) {
|
|
1085
1106
|
term.select(selStart.col, selStart.row, length);
|
|
1086
1107
|
}
|
|
1108
|
+
updateMarkerPositions();
|
|
1087
1109
|
}
|
|
1088
1110
|
|
|
1089
|
-
function
|
|
1090
|
-
selCtrls.forEach(el => el.style.display = show ? '' : 'none');
|
|
1091
|
-
selStartBtn.textContent = show ? 'Start' : 'Sel▶';
|
|
1092
|
-
selStartBtn.classList.toggle('active', selMode === 'start');
|
|
1093
|
-
selEndBtn.classList.toggle('active', selMode === 'end');
|
|
1094
|
-
}
|
|
1095
|
-
|
|
1096
|
-
const handleSelStart = (e) => {
|
|
1111
|
+
function toggleSelMode(e) {
|
|
1097
1112
|
e.preventDefault();
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1113
|
+
selMode = !selMode;
|
|
1114
|
+
selBtn.classList.toggle('active', selMode);
|
|
1115
|
+
if (selMode) {
|
|
1116
|
+
// Enter selection mode
|
|
1101
1117
|
const lastRow = term.buffer.active.cursorY;
|
|
1102
1118
|
selStart = { col: 0, row: Math.max(0, lastRow - 2) };
|
|
1103
1119
|
selEnd = { col: term.cols - 1, row: lastRow };
|
|
1104
|
-
|
|
1120
|
+
startMarker.style.display = 'block';
|
|
1121
|
+
endMarker.style.display = 'block';
|
|
1105
1122
|
updateSelection();
|
|
1106
|
-
} else if (selMode === 'end') {
|
|
1107
|
-
// Switch to adjusting start
|
|
1108
|
-
selMode = 'start';
|
|
1109
|
-
selStartBtn.classList.add('active');
|
|
1110
|
-
selEndBtn.classList.remove('active');
|
|
1111
|
-
} else {
|
|
1112
|
-
// Already adjusting start, switch to end
|
|
1113
|
-
selMode = 'end';
|
|
1114
|
-
selStartBtn.classList.remove('active');
|
|
1115
|
-
selEndBtn.classList.add('active');
|
|
1116
|
-
}
|
|
1117
|
-
};
|
|
1118
|
-
selStartBtn.addEventListener('touchstart', handleSelStart, { passive: false });
|
|
1119
|
-
selStartBtn.addEventListener('click', handleSelStart);
|
|
1120
|
-
|
|
1121
|
-
const handleSelEnd = (e) => {
|
|
1122
|
-
e.preventDefault();
|
|
1123
|
-
if (selMode === 'start') {
|
|
1124
|
-
selMode = 'end';
|
|
1125
|
-
selStartBtn.classList.remove('active');
|
|
1126
|
-
selEndBtn.classList.add('active');
|
|
1127
1123
|
} else {
|
|
1128
1124
|
// Exit selection mode
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
};
|
|
1133
|
-
selEndBtn.addEventListener('touchstart', handleSelEnd, { passive: false });
|
|
1134
|
-
selEndBtn.addEventListener('click', handleSelEnd);
|
|
1135
|
-
|
|
1136
|
-
// Arrow controls for selection
|
|
1137
|
-
const moveSelection = (dCol, dRow) => {
|
|
1138
|
-
if (selMode === 'start') {
|
|
1139
|
-
selStart.col = Math.max(0, Math.min(term.cols - 1, selStart.col + dCol));
|
|
1140
|
-
selStart.row = Math.max(0, selStart.row + dRow);
|
|
1141
|
-
} else if (selMode === 'end') {
|
|
1142
|
-
selEnd.col = Math.max(0, Math.min(term.cols - 1, selEnd.col + dCol));
|
|
1143
|
-
selEnd.row = Math.max(0, selEnd.row + dRow);
|
|
1125
|
+
startMarker.style.display = 'none';
|
|
1126
|
+
endMarker.style.display = 'none';
|
|
1127
|
+
term.clearSelection();
|
|
1144
1128
|
}
|
|
1145
|
-
|
|
1146
|
-
};
|
|
1129
|
+
}
|
|
1130
|
+
selBtn.addEventListener('touchstart', toggleSelMode, { passive: false });
|
|
1131
|
+
selBtn.addEventListener('click', toggleSelMode);
|
|
1147
1132
|
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1133
|
+
// Make markers draggable
|
|
1134
|
+
function setupDraggable(marker, isStart) {
|
|
1135
|
+
let dragging = false;
|
|
1136
|
+
let startX, startY;
|
|
1137
|
+
|
|
1138
|
+
const onStart = (e) => {
|
|
1139
|
+
e.preventDefault();
|
|
1140
|
+
dragging = true;
|
|
1141
|
+
const touch = e.touches ? e.touches[0] : e;
|
|
1142
|
+
startX = touch.clientX;
|
|
1143
|
+
startY = touch.clientY;
|
|
1144
|
+
marker.style.cursor = 'grabbing';
|
|
1145
|
+
};
|
|
1146
|
+
|
|
1147
|
+
const onMove = (e) => {
|
|
1148
|
+
if (!dragging) return;
|
|
1149
|
+
e.preventDefault();
|
|
1150
|
+
const touch = e.touches ? e.touches[0] : e;
|
|
1151
|
+
const cell = getCellSize();
|
|
1152
|
+
const rect = termContainer.getBoundingClientRect();
|
|
1153
|
+
const x = touch.clientX - rect.left - 4;
|
|
1154
|
+
const y = touch.clientY - rect.top;
|
|
1155
|
+
|
|
1156
|
+
const col = Math.max(0, Math.min(term.cols - 1, Math.floor(x / cell.width)));
|
|
1157
|
+
const row = Math.max(0, Math.floor(y / cell.height));
|
|
1158
|
+
|
|
1159
|
+
if (isStart) {
|
|
1160
|
+
selStart = { col, row };
|
|
1161
|
+
} else {
|
|
1162
|
+
selEnd = { col, row: Math.max(0, row - 1) };
|
|
1163
|
+
}
|
|
1164
|
+
updateSelection();
|
|
1165
|
+
};
|
|
1166
|
+
|
|
1167
|
+
const onEnd = () => {
|
|
1168
|
+
dragging = false;
|
|
1169
|
+
marker.style.cursor = 'grab';
|
|
1170
|
+
};
|
|
1171
|
+
|
|
1172
|
+
marker.addEventListener('touchstart', onStart, { passive: false });
|
|
1173
|
+
marker.addEventListener('touchmove', onMove, { passive: false });
|
|
1174
|
+
marker.addEventListener('touchend', onEnd);
|
|
1175
|
+
marker.addEventListener('mousedown', onStart);
|
|
1176
|
+
document.addEventListener('mousemove', onMove);
|
|
1177
|
+
document.addEventListener('mouseup', onEnd);
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
setupDraggable(startMarker, true);
|
|
1181
|
+
setupDraggable(endMarker, false);
|
|
1157
1182
|
}
|
|
1158
1183
|
|
|
1159
1184
|
function updateClientList() {
|