termites 1.0.32 → 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.
- package/package.json +1 -1
- package/server.js +45 -15
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -619,11 +619,13 @@ class TermitesServer {
|
|
|
619
619
|
<head>
|
|
620
620
|
<meta charset="UTF-8">
|
|
621
621
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
|
622
|
+
<meta name="color-scheme" content="light dark">
|
|
622
623
|
<title>Termites</title>
|
|
623
624
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterm@5.3.0/css/xterm.css">
|
|
624
625
|
<style>
|
|
626
|
+
:root { color-scheme: light dark; }
|
|
625
627
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
626
|
-
html { height: 100%; overflow: hidden; }
|
|
628
|
+
html { height: 100%; overflow: hidden; background-color: inherit; }
|
|
627
629
|
body { height: 100vh; height: 100dvh; display: flex; flex-direction: column; transition: background 0.3s; font-family: monospace; overflow: hidden; position: fixed; width: 100%; }
|
|
628
630
|
.header {
|
|
629
631
|
padding: 10px 12px; display: flex; align-items: center; gap: 12px;
|
|
@@ -946,6 +948,10 @@ class TermitesServer {
|
|
|
946
948
|
const t = themes[themeName];
|
|
947
949
|
if (!t) return;
|
|
948
950
|
currentTheme = themeName;
|
|
951
|
+
// Set color-scheme to prevent browser auto dark mode
|
|
952
|
+
const isLightTheme = themeName === 'solarized-light';
|
|
953
|
+
document.documentElement.style.colorScheme = isLightTheme ? 'only light' : 'only dark';
|
|
954
|
+
document.documentElement.style.background = t.background;
|
|
949
955
|
document.body.style.background = t.background;
|
|
950
956
|
document.getElementById('terminal-container').style.background = t.background;
|
|
951
957
|
const header = document.querySelector('.header');
|
|
@@ -1089,10 +1095,10 @@ class TermitesServer {
|
|
|
1089
1095
|
};
|
|
1090
1096
|
|
|
1091
1097
|
toolbar.querySelectorAll('button').forEach(btn => {
|
|
1092
|
-
|
|
1093
|
-
const
|
|
1094
|
-
|
|
1095
|
-
|
|
1098
|
+
let repeatInterval = null;
|
|
1099
|
+
const isArrowKey = ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(btn.dataset.key);
|
|
1100
|
+
|
|
1101
|
+
const sendKey = () => {
|
|
1096
1102
|
const mod = btn.dataset.mod;
|
|
1097
1103
|
const key = btn.dataset.key;
|
|
1098
1104
|
const seq = btn.dataset.seq;
|
|
@@ -1100,7 +1106,6 @@ class TermitesServer {
|
|
|
1100
1106
|
if (mod) {
|
|
1101
1107
|
modifiers[mod] = !modifiers[mod];
|
|
1102
1108
|
btn.classList.toggle('active', modifiers[mod]);
|
|
1103
|
-
// Set timeout to auto-reset Ctrl after 10 seconds (for IME users)
|
|
1104
1109
|
if (mod === 'ctrl' && modifiers.ctrl) {
|
|
1105
1110
|
if (window.ctrlTimeout) clearTimeout(window.ctrlTimeout);
|
|
1106
1111
|
window.ctrlTimeout = setTimeout(() => {
|
|
@@ -1108,7 +1113,6 @@ class TermitesServer {
|
|
|
1108
1113
|
btn.classList.remove('active');
|
|
1109
1114
|
}, 10000);
|
|
1110
1115
|
}
|
|
1111
|
-
// Don't focus terminal when toggling modifier, keep keyboard open
|
|
1112
1116
|
return;
|
|
1113
1117
|
}
|
|
1114
1118
|
|
|
@@ -1117,21 +1121,17 @@ class TermitesServer {
|
|
|
1117
1121
|
data = seq;
|
|
1118
1122
|
} else if (key) {
|
|
1119
1123
|
if (modifiers.ctrl) {
|
|
1120
|
-
// Ctrl + key
|
|
1121
1124
|
if (ctrlKeyMap[key]) {
|
|
1122
1125
|
data = ctrlKeyMap[key];
|
|
1123
1126
|
} else if (key.length === 1) {
|
|
1124
|
-
// Ctrl + letter (e.g., Ctrl+C = \\x03)
|
|
1125
1127
|
data = String.fromCharCode(key.toUpperCase().charCodeAt(0) - 64);
|
|
1126
1128
|
} else {
|
|
1127
1129
|
data = keyMap[key] || '';
|
|
1128
1130
|
}
|
|
1129
1131
|
} else if (modifiers.alt) {
|
|
1130
|
-
// Alt + key
|
|
1131
1132
|
if (altKeyMap[key]) {
|
|
1132
1133
|
data = altKeyMap[key];
|
|
1133
1134
|
} else if (key.length === 1) {
|
|
1134
|
-
// Alt + letter sends ESC + letter
|
|
1135
1135
|
data = '\\x1b' + key;
|
|
1136
1136
|
} else {
|
|
1137
1137
|
data = keyMap[key] || '';
|
|
@@ -1143,17 +1143,47 @@ class TermitesServer {
|
|
|
1143
1143
|
|
|
1144
1144
|
if (data && ws?.readyState === WebSocket.OPEN && selectedClientId) {
|
|
1145
1145
|
ws.send(JSON.stringify({ type: 'input', clientId: selectedClientId, text: data }));
|
|
1146
|
-
|
|
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) {
|
|
1147
1173
|
modifiers.ctrl = false;
|
|
1148
1174
|
modifiers.alt = false;
|
|
1149
1175
|
if (window.ctrlTimeout) { clearTimeout(window.ctrlTimeout); window.ctrlTimeout = null; }
|
|
1150
1176
|
toolbar.querySelectorAll('.mod-btn').forEach(b => b.classList.remove('active'));
|
|
1151
1177
|
}
|
|
1152
|
-
// Only focus terminal for non-modifier buttons
|
|
1153
1178
|
term.focus();
|
|
1154
1179
|
};
|
|
1155
|
-
|
|
1156
|
-
btn.addEventListener('
|
|
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);
|
|
1157
1187
|
});
|
|
1158
1188
|
|
|
1159
1189
|
// History button
|