coxpit 3.7.0 → 3.8.0
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/src/board.ts +38 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "coxpit",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.8.0",
|
|
4
4
|
"description": "Self-hosted cockpit for running a fleet of AI coding agents across your own machines — parallel worktree runs, live board, compare & merge, web terminal, design capture.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
package/src/board.ts
CHANGED
|
@@ -311,6 +311,16 @@ export const BOARD_HTML = /* html */ `<!doctype html>
|
|
|
311
311
|
.term-body{flex:1;min-height:0;background:#0b0d12;padding:8px 4px 4px 10px}
|
|
312
312
|
#xterm{width:100%;height:100%}
|
|
313
313
|
.term-hint{font-family:var(--mono);font-size:11px;color:var(--faint)}
|
|
314
|
+
/* 모바일 입력바 — xterm 직접 타이핑은 소프트웨어 키보드 IME 에서 자모가 낱자로
|
|
315
|
+
전송된다(조합 미지원). 일반 input 은 네이티브로 조합하므로 여기서 완성문을 보낸다. */
|
|
316
|
+
.term-ibar{display:none;gap:6px;padding:8px 10px;border-top:1px solid var(--line);
|
|
317
|
+
background:var(--surface2);align-items:center;
|
|
318
|
+
padding-bottom:calc(8px + env(safe-area-inset-bottom))}
|
|
319
|
+
.term-ibar input{flex:1;min-width:0}
|
|
320
|
+
.tkey{font-family:var(--mono);font-size:11.5px;color:var(--muted);background:var(--surface);
|
|
321
|
+
border:1px solid var(--line);border-radius:6px;padding:7px 9px;cursor:pointer;flex:0 0 auto}
|
|
322
|
+
.tkey:active{color:var(--ink);border-color:var(--brand)}
|
|
323
|
+
@media (max-width:860px),(pointer:coarse){.term-ibar{display:flex}}
|
|
314
324
|
|
|
315
325
|
/* ── mobile · the pocket pass ───────────── */
|
|
316
326
|
.menu-btn{display:none;font-size:15px;padding:5px 10px}
|
|
@@ -500,6 +510,16 @@ export const BOARD_HTML = /* html */ `<!doctype html>
|
|
|
500
510
|
<button class="x" id="termClose" aria-label="close">×</button>
|
|
501
511
|
</div>
|
|
502
512
|
<div class="term-body"><div id="xterm"></div></div>
|
|
513
|
+
<div class="term-ibar" id="termIbar">
|
|
514
|
+
<button class="tkey" data-k="esc" title="Esc">esc</button>
|
|
515
|
+
<button class="tkey" data-k="tab" title="Tab">tab</button>
|
|
516
|
+
<button class="tkey" data-k="cc" title="Ctrl-C">^C</button>
|
|
517
|
+
<button class="tkey" data-k="up" title="Up">↑</button>
|
|
518
|
+
<button class="tkey" data-k="down" title="Down">↓</button>
|
|
519
|
+
<input id="termInput" placeholder="type here — composes IME (한글 OK), Enter sends"
|
|
520
|
+
autocomplete="off" autocapitalize="off" autocorrect="off" spellcheck="false" />
|
|
521
|
+
<button class="btn sm" id="termSend">⏎</button>
|
|
522
|
+
</div>
|
|
503
523
|
</div>
|
|
504
524
|
</div>
|
|
505
525
|
|
|
@@ -1376,6 +1396,24 @@ function closeTerm(){
|
|
|
1376
1396
|
if (termWS){ try{ termWS.close(); }catch{} termWS=null; }
|
|
1377
1397
|
if (termObj){ try{ termObj.dispose(); }catch{} termObj=null; fitAddon=null; }
|
|
1378
1398
|
}
|
|
1399
|
+
/* 모바일 입력바 — 조합 완료된 텍스트를 통째로 PTY 에 (IME-안전 경로) */
|
|
1400
|
+
function termSendRaw(d){ if (termWS && termWS.readyState===1) termWS.send(JSON.stringify({t:'i', d})); }
|
|
1401
|
+
function termSendLine(){
|
|
1402
|
+
const inp = $('termInput');
|
|
1403
|
+
const v = inp.value;
|
|
1404
|
+
if (!v) { termSendRaw('\\r'); return; }
|
|
1405
|
+
termSendRaw(v + '\\r');
|
|
1406
|
+
inp.value = '';
|
|
1407
|
+
}
|
|
1408
|
+
$('termSend').addEventListener('click', termSendLine);
|
|
1409
|
+
$('termInput').addEventListener('keydown', (e)=>{
|
|
1410
|
+
if (e.isComposing) return; // 조합 중 Enter 는 IME 확정용 — 보내지 않음
|
|
1411
|
+
if (e.key === 'Enter'){ e.preventDefault(); termSendLine(); }
|
|
1412
|
+
});
|
|
1413
|
+
const TKEYS = { esc:'\\x1b', tab:'\\t', cc:'\\x03', up:'\\x1b[A', down:'\\x1b[B' };
|
|
1414
|
+
document.querySelectorAll('#termIbar .tkey').forEach(b=>{
|
|
1415
|
+
b.addEventListener('click', ()=>{ const k=TKEYS[b.dataset.k]; if(k) termSendRaw(k); });
|
|
1416
|
+
});
|
|
1379
1417
|
$('termClose').addEventListener('click', closeTerm);
|
|
1380
1418
|
$('termOverlay').addEventListener('click',(e)=>{ if(e.target===$('termOverlay')) closeTerm(); });
|
|
1381
1419
|
$('mTerm').addEventListener('click', ()=>{
|