termites 1.0.33 → 1.0.34

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 +38 -14
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "termites",
3
- "version": "1.0.33",
3
+ "version": "1.0.34",
4
4
  "description": "Local multi-terminal manager with web interface",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/server.js CHANGED
@@ -1095,10 +1095,10 @@ class TermitesServer {
1095
1095
  };
1096
1096
 
1097
1097
  toolbar.querySelectorAll('button').forEach(btn => {
1098
- // Use touchstart to prevent losing focus on terminal
1099
- const handler = (e) => {
1100
- e.preventDefault();
1101
- e.stopPropagation();
1098
+ let repeatInterval = null;
1099
+ const isArrowKey = ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(btn.dataset.key);
1100
+
1101
+ const sendKey = () => {
1102
1102
  const mod = btn.dataset.mod;
1103
1103
  const key = btn.dataset.key;
1104
1104
  const seq = btn.dataset.seq;
@@ -1106,7 +1106,6 @@ class TermitesServer {
1106
1106
  if (mod) {
1107
1107
  modifiers[mod] = !modifiers[mod];
1108
1108
  btn.classList.toggle('active', modifiers[mod]);
1109
- // Set timeout to auto-reset Ctrl after 10 seconds (for IME users)
1110
1109
  if (mod === 'ctrl' && modifiers.ctrl) {
1111
1110
  if (window.ctrlTimeout) clearTimeout(window.ctrlTimeout);
1112
1111
  window.ctrlTimeout = setTimeout(() => {
@@ -1114,7 +1113,6 @@ class TermitesServer {
1114
1113
  btn.classList.remove('active');
1115
1114
  }, 10000);
1116
1115
  }
1117
- // Don't focus terminal when toggling modifier, keep keyboard open
1118
1116
  return;
1119
1117
  }
1120
1118
 
@@ -1123,21 +1121,17 @@ class TermitesServer {
1123
1121
  data = seq;
1124
1122
  } else if (key) {
1125
1123
  if (modifiers.ctrl) {
1126
- // Ctrl + key
1127
1124
  if (ctrlKeyMap[key]) {
1128
1125
  data = ctrlKeyMap[key];
1129
1126
  } else if (key.length === 1) {
1130
- // Ctrl + letter (e.g., Ctrl+C = \\x03)
1131
1127
  data = String.fromCharCode(key.toUpperCase().charCodeAt(0) - 64);
1132
1128
  } else {
1133
1129
  data = keyMap[key] || '';
1134
1130
  }
1135
1131
  } else if (modifiers.alt) {
1136
- // Alt + key
1137
1132
  if (altKeyMap[key]) {
1138
1133
  data = altKeyMap[key];
1139
1134
  } else if (key.length === 1) {
1140
- // Alt + letter sends ESC + letter
1141
1135
  data = '\\x1b' + key;
1142
1136
  } else {
1143
1137
  data = keyMap[key] || '';
@@ -1149,17 +1143,47 @@ class TermitesServer {
1149
1143
 
1150
1144
  if (data && ws?.readyState === WebSocket.OPEN && selectedClientId) {
1151
1145
  ws.send(JSON.stringify({ type: 'input', clientId: selectedClientId, text: data }));
1152
- // Reset modifiers after use
1146
+ }
1147
+ };
1148
+
1149
+ const startRepeat = (e) => {
1150
+ e.preventDefault();
1151
+ e.stopPropagation();
1152
+ sendKey();
1153
+ // For arrow keys, start repeating after initial press
1154
+ if (isArrowKey && !btn.dataset.mod) {
1155
+ repeatInterval = setInterval(sendKey, 100);
1156
+ }
1157
+ // Reset modifiers after use (except for arrow keys during repeat)
1158
+ if (!isArrowKey) {
1159
+ modifiers.ctrl = false;
1160
+ modifiers.alt = false;
1161
+ if (window.ctrlTimeout) { clearTimeout(window.ctrlTimeout); window.ctrlTimeout = null; }
1162
+ toolbar.querySelectorAll('.mod-btn').forEach(b => b.classList.remove('active'));
1163
+ }
1164
+ };
1165
+
1166
+ const stopRepeat = () => {
1167
+ if (repeatInterval) {
1168
+ clearInterval(repeatInterval);
1169
+ repeatInterval = null;
1170
+ }
1171
+ // Reset modifiers after arrow key release
1172
+ if (isArrowKey) {
1153
1173
  modifiers.ctrl = false;
1154
1174
  modifiers.alt = false;
1155
1175
  if (window.ctrlTimeout) { clearTimeout(window.ctrlTimeout); window.ctrlTimeout = null; }
1156
1176
  toolbar.querySelectorAll('.mod-btn').forEach(b => b.classList.remove('active'));
1157
1177
  }
1158
- // Only focus terminal for non-modifier buttons
1159
1178
  term.focus();
1160
1179
  };
1161
- btn.addEventListener('touchstart', handler, { passive: false });
1162
- btn.addEventListener('click', handler);
1180
+
1181
+ btn.addEventListener('touchstart', startRepeat, { passive: false });
1182
+ btn.addEventListener('touchend', stopRepeat);
1183
+ btn.addEventListener('touchcancel', stopRepeat);
1184
+ btn.addEventListener('mousedown', startRepeat);
1185
+ btn.addEventListener('mouseup', stopRepeat);
1186
+ btn.addEventListener('mouseleave', stopRepeat);
1163
1187
  });
1164
1188
 
1165
1189
  // History button