claude-opencode-viewer 2.1.6 → 2.1.8
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/index.html +15 -8
- package/package.json +1 -1
- package/server.js +38 -12
package/index.html
CHANGED
|
@@ -504,11 +504,12 @@
|
|
|
504
504
|
var ARROW_KEYS = ['up', 'down', 'left', 'right'];
|
|
505
505
|
|
|
506
506
|
function sendKey(keyName) {
|
|
507
|
-
|
|
507
|
+
// 重新获取焦点状态(确保使用最新状态)
|
|
508
|
+
var isInputFocused = (inputField && document.activeElement === inputField);
|
|
508
509
|
|
|
509
510
|
// 方向键特殊处理:根据焦点位置决定行为
|
|
510
|
-
if (
|
|
511
|
-
if (
|
|
511
|
+
if (ARROW_KEYS.indexOf(keyName) !== -1) {
|
|
512
|
+
if (isInputFocused) {
|
|
512
513
|
// 焦点在输入框:模拟方向键操作输入框光标
|
|
513
514
|
var cursorPos = inputField.selectionStart;
|
|
514
515
|
var textLen = inputField.value.length;
|
|
@@ -522,28 +523,27 @@
|
|
|
522
523
|
return;
|
|
523
524
|
} else {
|
|
524
525
|
// 焦点在终端:发送方向键到终端,并收起键盘
|
|
525
|
-
if (
|
|
526
|
+
if (isMobile) {
|
|
526
527
|
inputField.blur();
|
|
527
528
|
}
|
|
528
529
|
}
|
|
529
530
|
} else {
|
|
530
531
|
// 非方向键:点击时收起键盘
|
|
531
|
-
if (
|
|
532
|
+
if (isMobile) {
|
|
532
533
|
inputField.blur();
|
|
533
534
|
}
|
|
534
535
|
}
|
|
535
536
|
|
|
536
537
|
// Enter 键特殊处理:根据焦点位置决定行为
|
|
537
538
|
if (keyName === 'enter') {
|
|
538
|
-
if (
|
|
539
|
+
if (isInputFocused && inputField && inputField.value) {
|
|
539
540
|
// 焦点在输入框且有内容:只发送输入框内容,不额外发送 \r
|
|
540
541
|
var value = inputField.value;
|
|
541
542
|
console.log('[enter] from input:', value);
|
|
542
543
|
ws.send(JSON.stringify({ type: 'input', data: value + '\r' }));
|
|
543
544
|
inputField.value = '';
|
|
544
|
-
// 让输入框失去焦点,防止终端 textarea 同时收到输入
|
|
545
545
|
inputField.blur();
|
|
546
|
-
} else if (
|
|
546
|
+
} else if (isInputFocused && inputField) {
|
|
547
547
|
// 焦点在输入框但内容为空:不发送任何内容,避免误触
|
|
548
548
|
console.log('[enter] input focused but empty, ignored');
|
|
549
549
|
} else {
|
|
@@ -554,6 +554,13 @@
|
|
|
554
554
|
return;
|
|
555
555
|
}
|
|
556
556
|
|
|
557
|
+
// Esc 键:中断终端进程(发送 ESC 字符)
|
|
558
|
+
if (keyName === 'esc') {
|
|
559
|
+
console.log('[esc] sending escape to terminal');
|
|
560
|
+
ws.send(JSON.stringify({ type: 'input', data: '\x1b' }));
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
|
|
557
564
|
// 其他按键直接发送到终端
|
|
558
565
|
var key = KEY_MAP[keyName];
|
|
559
566
|
if (key && ws && ws.readyState === 1 && !isTransitioning) {
|
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -133,18 +133,25 @@ async function spawnProcess(mode) {
|
|
|
133
133
|
}
|
|
134
134
|
});
|
|
135
135
|
|
|
136
|
+
// 只在初始化时杀死旧进程,switchMode 已经处理了切换时的进程清理
|
|
136
137
|
if (mode === 'claude') {
|
|
137
|
-
if (claudeProcess
|
|
138
|
-
try {
|
|
138
|
+
if (claudeProcess && claudeProcess !== proc && claudeProcess.pid) {
|
|
139
|
+
try {
|
|
140
|
+
console.log(`[spawnProcess] 清理旧 claude 进程 PID: ${claudeProcess.pid}`);
|
|
141
|
+
claudeProcess.kill();
|
|
142
|
+
} catch {}
|
|
139
143
|
}
|
|
140
144
|
claudeProcess = proc;
|
|
141
145
|
} else {
|
|
142
|
-
if (opencodeProcess
|
|
143
|
-
try {
|
|
146
|
+
if (opencodeProcess && opencodeProcess !== proc && opencodeProcess.pid) {
|
|
147
|
+
try {
|
|
148
|
+
console.log(`[spawnProcess] 清理旧 opencode 进程 PID: ${opencodeProcess.pid}`);
|
|
149
|
+
opencodeProcess.kill();
|
|
150
|
+
} catch {}
|
|
144
151
|
}
|
|
145
152
|
opencodeProcess = proc;
|
|
146
153
|
}
|
|
147
|
-
|
|
154
|
+
|
|
148
155
|
currentProcess = proc;
|
|
149
156
|
console.log(`[claude-opencode-viewer] ${mode === 'claude' ? 'Claude Code' : 'OpenCode'} 已启动 (PID: ${proc.pid})`);
|
|
150
157
|
return proc;
|
|
@@ -152,24 +159,43 @@ async function spawnProcess(mode) {
|
|
|
152
159
|
|
|
153
160
|
async function switchMode(newMode) {
|
|
154
161
|
if (newMode === currentMode) return;
|
|
155
|
-
|
|
162
|
+
|
|
163
|
+
console.log(`[switchMode] 从 ${currentMode} 切换到 ${newMode}`);
|
|
164
|
+
|
|
156
165
|
// 清空所有历史输出
|
|
157
166
|
outputBuffer = '';
|
|
158
|
-
|
|
167
|
+
|
|
159
168
|
// 杀死旧进程
|
|
160
169
|
if (currentMode === 'claude' && claudeProcess) {
|
|
161
|
-
try {
|
|
170
|
+
try {
|
|
171
|
+
console.log(`[switchMode] 杀死 claude 进程 PID: ${claudeProcess.pid}`);
|
|
172
|
+
claudeProcess.kill();
|
|
173
|
+
} catch (e) {
|
|
174
|
+
console.log('[switchMode] 杀死 claude 进程失败:', e.message);
|
|
175
|
+
}
|
|
162
176
|
claudeProcess = null;
|
|
163
177
|
} else if (currentMode === 'opencode' && opencodeProcess) {
|
|
164
|
-
try {
|
|
178
|
+
try {
|
|
179
|
+
console.log(`[switchMode] 杀死 opencode 进程 PID: ${opencodeProcess.pid}`);
|
|
180
|
+
opencodeProcess.kill();
|
|
181
|
+
} catch (e) {
|
|
182
|
+
console.log('[switchMode] 杀死 opencode 进程失败:', e.message);
|
|
183
|
+
}
|
|
165
184
|
opencodeProcess = null;
|
|
166
185
|
}
|
|
167
186
|
currentProcess = null;
|
|
168
|
-
|
|
187
|
+
|
|
188
|
+
// 等待一小段时间确保进程完全退出
|
|
189
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
190
|
+
|
|
169
191
|
// 切换到新模式
|
|
170
192
|
currentMode = newMode;
|
|
171
|
-
|
|
172
|
-
|
|
193
|
+
try {
|
|
194
|
+
await spawnProcess(newMode);
|
|
195
|
+
console.log(`[switchMode] 切换到 ${newMode} 成功`);
|
|
196
|
+
} catch (e) {
|
|
197
|
+
console.error('[switchMode] 启动新进程失败:', e.message);
|
|
198
|
+
}
|
|
173
199
|
}
|
|
174
200
|
|
|
175
201
|
function writeToPty(data) {
|