termites 1.0.23 → 1.0.25
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 +101 -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,14 @@ class TermitesServer {
|
|
|
721
728
|
<button data-key="ArrowDown">↓</button>
|
|
722
729
|
<div class="sep"></div>
|
|
723
730
|
<button data-seq="/">/</button>
|
|
724
|
-
<button
|
|
725
|
-
<button
|
|
726
|
-
<button id="sel-
|
|
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 data-seq="[">[</button>
|
|
732
|
+
<button data-seq="]">]</button>
|
|
733
|
+
<button id="sel-btn" class="mod-btn">Sel</button>
|
|
730
734
|
<button id="copy-btn">Copy</button>
|
|
731
735
|
<button id="paste-btn">Paste</button>
|
|
732
736
|
</div>
|
|
737
|
+
<div id="sel-start-marker" class="sel-marker" style="display:none">▼Start</div>
|
|
738
|
+
<div id="sel-end-marker" class="sel-marker" style="display:none">▲End</div>
|
|
733
739
|
|
|
734
740
|
<script src="https://cdn.jsdelivr.net/npm/xterm@5.3.0/lib/xterm.min.js"></script>
|
|
735
741
|
<script src="https://cdn.jsdelivr.net/npm/xterm-addon-fit@0.8.0/lib/xterm-addon-fit.min.js"></script>
|
|
@@ -1065,17 +1071,34 @@ class TermitesServer {
|
|
|
1065
1071
|
pasteBtn.addEventListener('touchstart', handlePaste, { passive: false });
|
|
1066
1072
|
pasteBtn.addEventListener('click', handlePaste);
|
|
1067
1073
|
|
|
1068
|
-
// Selection mode with
|
|
1069
|
-
let selMode = false;
|
|
1074
|
+
// Selection mode with draggable markers
|
|
1075
|
+
let selMode = false;
|
|
1070
1076
|
let selStart = { col: 0, row: 0 };
|
|
1071
1077
|
let selEnd = { col: 0, row: 0 };
|
|
1072
|
-
const
|
|
1073
|
-
const
|
|
1074
|
-
const
|
|
1075
|
-
const
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1078
|
+
const selBtn = document.getElementById('sel-btn');
|
|
1079
|
+
const startMarker = document.getElementById('sel-start-marker');
|
|
1080
|
+
const endMarker = document.getElementById('sel-end-marker');
|
|
1081
|
+
const termContainer = document.getElementById('terminal-container');
|
|
1082
|
+
|
|
1083
|
+
function getCellSize() {
|
|
1084
|
+
try {
|
|
1085
|
+
return {
|
|
1086
|
+
width: term._core._renderService.dimensions.css.cell.width,
|
|
1087
|
+
height: term._core._renderService.dimensions.css.cell.height
|
|
1088
|
+
};
|
|
1089
|
+
} catch (e) {
|
|
1090
|
+
return { width: 9, height: 17 }; // fallback
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
function updateMarkerPositions() {
|
|
1095
|
+
const cell = getCellSize();
|
|
1096
|
+
const rect = termContainer.getBoundingClientRect();
|
|
1097
|
+
startMarker.style.left = (selStart.col * cell.width + 4) + 'px';
|
|
1098
|
+
startMarker.style.top = (selStart.row * cell.height) + 'px';
|
|
1099
|
+
endMarker.style.left = (selEnd.col * cell.width + 4) + 'px';
|
|
1100
|
+
endMarker.style.top = ((selEnd.row + 1) * cell.height) + 'px';
|
|
1101
|
+
}
|
|
1079
1102
|
|
|
1080
1103
|
function updateSelection() {
|
|
1081
1104
|
const startOffset = selStart.row * term.cols + selStart.col;
|
|
@@ -1084,76 +1107,80 @@ class TermitesServer {
|
|
|
1084
1107
|
if (length > 0) {
|
|
1085
1108
|
term.select(selStart.col, selStart.row, length);
|
|
1086
1109
|
}
|
|
1110
|
+
updateMarkerPositions();
|
|
1087
1111
|
}
|
|
1088
1112
|
|
|
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) => {
|
|
1113
|
+
function toggleSelMode(e) {
|
|
1097
1114
|
e.preventDefault();
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1115
|
+
selMode = !selMode;
|
|
1116
|
+
selBtn.classList.toggle('active', selMode);
|
|
1117
|
+
if (selMode) {
|
|
1118
|
+
// Enter selection mode
|
|
1101
1119
|
const lastRow = term.buffer.active.cursorY;
|
|
1102
1120
|
selStart = { col: 0, row: Math.max(0, lastRow - 2) };
|
|
1103
1121
|
selEnd = { col: term.cols - 1, row: lastRow };
|
|
1104
|
-
|
|
1122
|
+
startMarker.style.display = 'block';
|
|
1123
|
+
endMarker.style.display = 'block';
|
|
1105
1124
|
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
1125
|
} else {
|
|
1128
1126
|
// 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);
|
|
1127
|
+
startMarker.style.display = 'none';
|
|
1128
|
+
endMarker.style.display = 'none';
|
|
1129
|
+
term.clearSelection();
|
|
1144
1130
|
}
|
|
1145
|
-
|
|
1146
|
-
};
|
|
1131
|
+
}
|
|
1132
|
+
selBtn.addEventListener('touchstart', toggleSelMode, { passive: false });
|
|
1133
|
+
selBtn.addEventListener('click', toggleSelMode);
|
|
1147
1134
|
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1135
|
+
// Make markers draggable
|
|
1136
|
+
function setupDraggable(marker, isStart) {
|
|
1137
|
+
let dragging = false;
|
|
1138
|
+
let startX, startY;
|
|
1139
|
+
|
|
1140
|
+
const onStart = (e) => {
|
|
1141
|
+
e.preventDefault();
|
|
1142
|
+
dragging = true;
|
|
1143
|
+
const touch = e.touches ? e.touches[0] : e;
|
|
1144
|
+
startX = touch.clientX;
|
|
1145
|
+
startY = touch.clientY;
|
|
1146
|
+
marker.style.cursor = 'grabbing';
|
|
1147
|
+
};
|
|
1148
|
+
|
|
1149
|
+
const onMove = (e) => {
|
|
1150
|
+
if (!dragging) return;
|
|
1151
|
+
e.preventDefault();
|
|
1152
|
+
const touch = e.touches ? e.touches[0] : e;
|
|
1153
|
+
const cell = getCellSize();
|
|
1154
|
+
const rect = termContainer.getBoundingClientRect();
|
|
1155
|
+
const x = touch.clientX - rect.left - 4;
|
|
1156
|
+
const y = touch.clientY - rect.top;
|
|
1157
|
+
|
|
1158
|
+
const col = Math.max(0, Math.min(term.cols - 1, Math.floor(x / cell.width)));
|
|
1159
|
+
const row = Math.max(0, Math.floor(y / cell.height));
|
|
1160
|
+
|
|
1161
|
+
if (isStart) {
|
|
1162
|
+
selStart = { col, row };
|
|
1163
|
+
} else {
|
|
1164
|
+
selEnd = { col, row: Math.max(0, row - 1) };
|
|
1165
|
+
}
|
|
1166
|
+
updateSelection();
|
|
1167
|
+
};
|
|
1168
|
+
|
|
1169
|
+
const onEnd = () => {
|
|
1170
|
+
dragging = false;
|
|
1171
|
+
marker.style.cursor = 'grab';
|
|
1172
|
+
};
|
|
1173
|
+
|
|
1174
|
+
marker.addEventListener('touchstart', onStart, { passive: false });
|
|
1175
|
+
marker.addEventListener('touchmove', onMove, { passive: false });
|
|
1176
|
+
marker.addEventListener('touchend', onEnd);
|
|
1177
|
+
marker.addEventListener('mousedown', onStart);
|
|
1178
|
+
document.addEventListener('mousemove', onMove);
|
|
1179
|
+
document.addEventListener('mouseup', onEnd);
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
setupDraggable(startMarker, true);
|
|
1183
|
+
setupDraggable(endMarker, false);
|
|
1157
1184
|
}
|
|
1158
1185
|
|
|
1159
1186
|
function updateClientList() {
|