termites 1.0.16 → 1.0.18

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/server.js +78 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "termites",
3
- "version": "1.0.16",
3
+ "version": "1.0.18",
4
4
  "description": "Web terminal with server-client architecture for remote shell access",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/server.js CHANGED
@@ -590,7 +590,9 @@ class TermitesServer {
590
590
  .header-title .host { color: var(--host-color, #859900); }
591
591
  .header-title .sep { color: var(--sep-color, #657b83); }
592
592
  .no-client { color: #888; font-style: italic; }
593
- #terminal-container { flex: 1; padding: 4px; transition: background 0.3s; overflow: hidden; min-height: 0; }
593
+ #terminal-container { flex: 1; padding: 4px; transition: background 0.3s; overflow: hidden; min-height: 0; position: relative; }
594
+ #terminal-container.select-mode { cursor: text; }
595
+ #terminal-container.select-mode::after { content: 'Select Mode'; position: absolute; top: 8px; right: 8px; background: rgba(0,0,0,0.7); color: #fff; padding: 4px 8px; border-radius: 4px; font-size: 11px; pointer-events: none; }
594
596
  .xterm { height: 100%; }
595
597
  .overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.4); z-index: 99; opacity: 0; visibility: hidden; transition: all 0.3s; }
596
598
  .overlay.open { opacity: 1; visibility: visible; }
@@ -725,6 +727,10 @@ class TermitesServer {
725
727
  <button data-seq="\\x04">^D</button>
726
728
  <button data-seq="\\x1a">^Z</button>
727
729
  <button data-seq="\\x0c">^L</button>
730
+ <div class="sep"></div>
731
+ <button id="copy-btn">Copy</button>
732
+ <button id="paste-btn">Paste</button>
733
+ <button id="select-btn" class="mod-btn">Select</button>
728
734
  </div>
729
735
 
730
736
  <script src="https://cdn.jsdelivr.net/npm/xterm@5.3.0/lib/xterm.min.js"></script>
@@ -1030,6 +1036,77 @@ class TermitesServer {
1030
1036
  btn.addEventListener('touchstart', handler, { passive: false });
1031
1037
  btn.addEventListener('click', handler);
1032
1038
  });
1039
+
1040
+ // Copy button - copy selection or last line
1041
+ document.getElementById('copy-btn').addEventListener('click', async () => {
1042
+ const selection = term.getSelection();
1043
+ if (selection) {
1044
+ await navigator.clipboard.writeText(selection);
1045
+ term.clearSelection();
1046
+ }
1047
+ });
1048
+
1049
+ // Paste button
1050
+ document.getElementById('paste-btn').addEventListener('click', async () => {
1051
+ try {
1052
+ const text = await navigator.clipboard.readText();
1053
+ if (text && ws?.readyState === WebSocket.OPEN && selectedClientId) {
1054
+ ws.send(JSON.stringify({ type: 'input', clientId: selectedClientId, text }));
1055
+ }
1056
+ } catch (e) {
1057
+ console.error('Paste failed:', e);
1058
+ }
1059
+ term.focus();
1060
+ });
1061
+
1062
+ // Select mode button - enables touch selection
1063
+ let selectMode = false;
1064
+ const selectBtn = document.getElementById('select-btn');
1065
+ const termContainer = document.getElementById('terminal-container');
1066
+
1067
+ selectBtn.addEventListener('click', () => {
1068
+ selectMode = !selectMode;
1069
+ selectBtn.classList.toggle('active', selectMode);
1070
+ termContainer.style.touchAction = selectMode ? 'none' : '';
1071
+ termContainer.style.userSelect = selectMode ? 'text' : '';
1072
+ if (selectMode) {
1073
+ // Enable selection overlay
1074
+ termContainer.classList.add('select-mode');
1075
+ } else {
1076
+ termContainer.classList.remove('select-mode');
1077
+ term.clearSelection();
1078
+ }
1079
+ });
1080
+
1081
+ // Handle touch selection
1082
+ let touchStartPos = null;
1083
+ termContainer.addEventListener('touchstart', (e) => {
1084
+ if (selectMode && e.touches.length === 1) {
1085
+ const touch = e.touches[0];
1086
+ const rect = termContainer.getBoundingClientRect();
1087
+ touchStartPos = { x: touch.clientX - rect.left, y: touch.clientY - rect.top };
1088
+ }
1089
+ }, { passive: true });
1090
+
1091
+ termContainer.addEventListener('touchmove', (e) => {
1092
+ if (selectMode && touchStartPos && e.touches.length === 1) {
1093
+ e.preventDefault();
1094
+ const touch = e.touches[0];
1095
+ const rect = termContainer.getBoundingClientRect();
1096
+ const currentPos = { x: touch.clientX - rect.left, y: touch.clientY - rect.top };
1097
+
1098
+ // Convert pixel positions to terminal coordinates
1099
+ const cellWidth = term._core._renderService.dimensions.css.cell.width;
1100
+ const cellHeight = term._core._renderService.dimensions.css.cell.height;
1101
+
1102
+ const startCol = Math.floor(touchStartPos.x / cellWidth);
1103
+ const startRow = Math.floor(touchStartPos.y / cellHeight);
1104
+ const endCol = Math.floor(currentPos.x / cellWidth);
1105
+ const endRow = Math.floor(currentPos.y / cellHeight);
1106
+
1107
+ term.select(startCol, startRow, (endRow - startRow) * term.cols + (endCol - startCol) + 1);
1108
+ }
1109
+ }, { passive: false });
1033
1110
  }
1034
1111
 
1035
1112
  function updateClientList() {