coxpit 5.13.0 → 5.14.1
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 +3 -1
- package/src/cockpit.ts +77 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "coxpit",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.14.1",
|
|
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": "SEE LICENSE IN LICENSE.md",
|
package/src/board.ts
CHANGED
|
@@ -845,7 +845,9 @@ export const BOARD_HTML = /* html */ `<!doctype html>
|
|
|
845
845
|
.fab{display:inline-flex}
|
|
846
846
|
#newBtn,.newnote{display:none}
|
|
847
847
|
header{padding:0 12px;gap:9px}
|
|
848
|
-
.brand .sub,.daemon-badge,.machines
|
|
848
|
+
.brand .sub,.daemon-badge,.machines{display:none}
|
|
849
|
+
/* cockpit(터미널) 링크는 모바일에서도 보이게 — 터미널 워크스페이스로 가는 유일한 입구 */
|
|
850
|
+
.cockpit-link{display:inline-flex;margin-left:auto}
|
|
849
851
|
main{padding:14px}
|
|
850
852
|
.grid{grid-template-columns:1fr}
|
|
851
853
|
/* 모달 = 풀블리드 */
|
package/src/cockpit.ts
CHANGED
|
@@ -49,7 +49,7 @@ export const COCKPIT_HTML = /* html */ `<!doctype html>
|
|
|
49
49
|
.toggle{font-size:12px;color:var(--muted);text-decoration:none;border:1px solid var(--line);border-radius:7px;padding:5px 11px;background:none;cursor:pointer;font-family:var(--mono)}
|
|
50
50
|
.toggle:hover{color:var(--ink);border-color:var(--line-hi)}
|
|
51
51
|
|
|
52
|
-
.layout{display:grid;grid-template-columns:270px 1fr;height:calc(
|
|
52
|
+
.layout{display:grid;grid-template-columns:270px 1fr;height:calc(100dvh - 46px)}
|
|
53
53
|
.layout > *{min-height:0;min-width:0}
|
|
54
54
|
|
|
55
55
|
/* ── workspace tree ── */
|
|
@@ -229,10 +229,43 @@ export const COCKPIT_HTML = /* html */ `<!doctype html>
|
|
|
229
229
|
.dl-file{color:var(--brand)} .dl-hunk{color:var(--open)} .dl-ctx{color:var(--muted)}
|
|
230
230
|
.dl-add{color:#7fdca0;background:rgba(88,179,104,.08)} .dl-del{color:#e58a92;background:rgba(226,91,103,.08)}
|
|
231
231
|
.rv-empty{flex:1;display:flex;align-items:center;justify-content:center;color:var(--faint);font-family:var(--mono);font-size:13px;text-align:center;padding:24px}
|
|
232
|
+
|
|
233
|
+
/* ── 모바일 대응 (드로어 트리 + 단일 터미널 + IME 입력바) ── */
|
|
234
|
+
.menu-btn{display:none;font-size:15px;color:var(--muted);background:none;border:1px solid var(--line);border-radius:7px;padding:5px 9px;cursor:pointer;font-family:var(--mono)}
|
|
235
|
+
.menu-btn:hover{color:var(--ink);border-color:var(--line-hi)}
|
|
236
|
+
.scrim{display:none;position:fixed;inset:46px 0 0 0;background:rgba(5,7,10,.5);z-index:39}
|
|
237
|
+
.scrim.on{display:block}
|
|
238
|
+
/* 모바일/터치 터미널 입력바 — 소프트키보드 IME 자모분리 방지: 조합 완료 텍스트를 통째로 PTY 로.
|
|
239
|
+
2행 구성(스크롤되는 키 줄 + 입력 줄)이라 방향키·조합키가 많아도 안 잘린다. */
|
|
240
|
+
.term-ibar{display:none;flex-direction:column;gap:6px;padding:7px 9px;border-top:1px solid var(--line);background:var(--surface2);padding-bottom:calc(7px + env(safe-area-inset-bottom))}
|
|
241
|
+
.tkeys{display:flex;gap:6px;overflow-x:auto;-webkit-overflow-scrolling:touch;padding-bottom:2px}
|
|
242
|
+
.tinput{display:flex;gap:6px;align-items:center}
|
|
243
|
+
.tinput input{flex:1;min-width:0;font-family:var(--mono);font-size:16px;color:var(--ink);background:var(--panel);border:1px solid var(--line);border-radius:8px;padding:9px 10px}
|
|
244
|
+
.tinput input:focus{outline:none;border-color:var(--brand)}
|
|
245
|
+
.tkey{font-family:var(--mono);font-size:13px;color:var(--muted);background:var(--surface);border:1px solid var(--line);border-radius:7px;padding:8px 11px;cursor:pointer;flex:0 0 auto;min-width:42px}
|
|
246
|
+
.tkey:active{color:var(--ink);border-color:var(--brand);background:var(--brand-dim)}
|
|
247
|
+
.tsend{font-family:var(--mono);font-size:13px;font-weight:600;color:var(--brand-ink);background:var(--brand);border:none;border-radius:8px;padding:9px 14px;cursor:pointer;flex:0 0 auto}
|
|
248
|
+
/* 터치 기기(아이패드 포함, 화면폭 무관): 입력바 노출 + 요청바/분할 숨김(하단 클러터·잘림 방지) */
|
|
249
|
+
body.touch .term-ibar{display:flex}
|
|
250
|
+
body.touch .reqbar{display:none}
|
|
251
|
+
body.touch #splitRow, body.touch #splitCol{display:none}
|
|
252
|
+
@media (max-width:860px){
|
|
253
|
+
header{gap:8px;padding:0 10px}
|
|
254
|
+
.brand .wm{font-size:17px}
|
|
255
|
+
.mach{display:none}
|
|
256
|
+
.vtab{padding:6px 8px;gap:5px}
|
|
257
|
+
.menu-btn{display:inline-flex}
|
|
258
|
+
.layout{grid-template-columns:1fr}
|
|
259
|
+
.rail{position:fixed;top:46px;bottom:0;left:0;width:84%;max-width:320px;z-index:40;background:var(--panel);transform:translateX(-100%);transition:transform .18s ease;border-right:1px solid var(--line-hi);box-shadow:2px 0 16px rgba(0,0,0,.4)}
|
|
260
|
+
.rail.open{transform:translateX(0)}
|
|
261
|
+
.tab{max-width:52vw}
|
|
262
|
+
.term-ibar{display:flex}
|
|
263
|
+
}
|
|
232
264
|
</style>
|
|
233
265
|
</head>
|
|
234
266
|
<body>
|
|
235
267
|
<header>
|
|
268
|
+
<button type="button" class="menu-btn" id="menuBtn" title="워크스페이스 트리">☰</button>
|
|
236
269
|
<a class="brand" href="/"><img src="/brand/mark.png" alt="" /><span class="wm">coxpit</span></a>
|
|
237
270
|
<span class="mach"><span class="dot"></span><span id="mach">local</span></span>
|
|
238
271
|
<div class="vtabs">
|
|
@@ -247,8 +280,9 @@ export const COCKPIT_HTML = /* html */ `<!doctype html>
|
|
|
247
280
|
</div>
|
|
248
281
|
</header>
|
|
249
282
|
|
|
283
|
+
<div class="scrim" id="scrim"></div>
|
|
250
284
|
<div class="layout">
|
|
251
|
-
<aside class="rail">
|
|
285
|
+
<aside class="rail" id="rail">
|
|
252
286
|
<div class="lbl"><span>Workspace</span><span id="machName" style="color:var(--faint)">local</span></div>
|
|
253
287
|
<div id="tree"></div>
|
|
254
288
|
<div class="railfoot">클릭한 run·세션은 <b>탭</b>으로 열립니다 · split 으로 페인을 나란히 배치</div>
|
|
@@ -274,6 +308,25 @@ export const COCKPIT_HTML = /* html */ `<!doctype html>
|
|
|
274
308
|
<div class="hint">세션 = <b>지정한 폴더</b>의 tmux 셸(특정 프로젝트에 소속되지 않음). 그 안에서 <code>claude</code> 를 띄워 “이 프로젝트 구현해줘” 처럼 직접 지시할 수 있습니다.</div>
|
|
275
309
|
</div>
|
|
276
310
|
</div>
|
|
311
|
+
<div class="term-ibar" id="termIbar">
|
|
312
|
+
<div class="tkeys">
|
|
313
|
+
<button type="button" class="tkey" data-k="esc" title="Esc">esc</button>
|
|
314
|
+
<button type="button" class="tkey" data-k="tab" title="Tab">tab</button>
|
|
315
|
+
<button type="button" class="tkey" data-k="enter" title="Enter">⏎</button>
|
|
316
|
+
<button type="button" class="tkey" data-k="left" title="←">←</button>
|
|
317
|
+
<button type="button" class="tkey" data-k="up" title="↑">↑</button>
|
|
318
|
+
<button type="button" class="tkey" data-k="down" title="↓">↓</button>
|
|
319
|
+
<button type="button" class="tkey" data-k="right" title="→">→</button>
|
|
320
|
+
<button type="button" class="tkey" data-k="cc" title="Ctrl-C">^C</button>
|
|
321
|
+
<button type="button" class="tkey" data-k="cd" title="Ctrl-D">^D</button>
|
|
322
|
+
<button type="button" class="tkey" data-k="cr" title="Ctrl-R (검색)">^R</button>
|
|
323
|
+
<button type="button" class="tkey" data-k="cu" title="Ctrl-U (줄 지우기)">^U</button>
|
|
324
|
+
</div>
|
|
325
|
+
<div class="tinput">
|
|
326
|
+
<input id="termInput" placeholder="입력 → 한글 OK · Enter 전송" autocomplete="off" autocapitalize="off" autocorrect="off" spellcheck="false" />
|
|
327
|
+
<button type="button" class="tsend" id="termSend">전송</button>
|
|
328
|
+
</div>
|
|
329
|
+
</div>
|
|
277
330
|
<div class="reqbar">
|
|
278
331
|
<div class="modes">
|
|
279
332
|
<button type="button" class="mode on" data-mode="new" title="새 태스크를 만들고 N개 에이전트로 팬아웃">⌗ New</button>
|
|
@@ -342,8 +395,9 @@ export const COCKPIT_HTML = /* html */ `<!doctype html>
|
|
|
342
395
|
<script src="/vendor/addon-fit.js"></script>
|
|
343
396
|
<script src="/vendor/addon-unicode11.js"></script>
|
|
344
397
|
<script>
|
|
345
|
-
//
|
|
346
|
-
|
|
398
|
+
// 모바일 = 터미널 우선을 유지하되 좁은 화면에 맞춤(드로어 트리 + 단일 터미널 + IME 입력바).
|
|
399
|
+
// (이전엔 보드로 리다이렉트했지만, 이제 cockpit 을 모바일 대응)
|
|
400
|
+
function isMobile(){ return window.matchMedia('(max-width:860px),(pointer:coarse)').matches; }
|
|
347
401
|
var esc = function(s){ return String(s==null?'':s).replace(/[&<>"]/g, function(c){ return ({'&':'&','<':'<','>':'>','"':'"'})[c]; }); };
|
|
348
402
|
var $ = function(id){ return document.getElementById(id); };
|
|
349
403
|
|
|
@@ -453,7 +507,7 @@ export const COCKPIT_HTML = /* html */ `<!doctype html>
|
|
|
453
507
|
$('tree').addEventListener('click', function(e){
|
|
454
508
|
if (e.target.closest('[data-newsession]')){ openSession(); return; }
|
|
455
509
|
var run = e.target.closest('[data-run]');
|
|
456
|
-
if (run){ openRunPane(+run.dataset.run); return; }
|
|
510
|
+
if (run){ openRunPane(+run.dataset.run); if (isMobile()) setDrawer(false); return; }
|
|
457
511
|
var fn = e.target.closest('[data-fold]');
|
|
458
512
|
if (fn){ var k = fn.dataset.fold; fold[k] = !isFold(k); renderTree(); }
|
|
459
513
|
});
|
|
@@ -614,6 +668,7 @@ export const COCKPIT_HTML = /* html */ `<!doctype html>
|
|
|
614
668
|
|
|
615
669
|
function splitFocused(dir){
|
|
616
670
|
if (!tabOrder.length) return;
|
|
671
|
+
if (isMobile()){ toast('창분할은 데스크톱 전용 — 모바일은 탭으로 전환하세요'); return; }
|
|
617
672
|
if (countLeaves()>=MAX_LEAVES){ toast('페인 최대 '+MAX_LEAVES+'개'); return; }
|
|
618
673
|
var l=findLeaf(focusLeaf) || firstLeaf(); if(!l) return;
|
|
619
674
|
var keep=l.tab; var aId=l.id, bId=newLeafId();
|
|
@@ -740,6 +795,23 @@ export const COCKPIT_HTML = /* html */ `<!doctype html>
|
|
|
740
795
|
$('splitCol').addEventListener('click', function(){ splitFocused('col'); });
|
|
741
796
|
$('closeBtn').addEventListener('click', function(){ if(focusLeaf) closeSlot(focusLeaf); });
|
|
742
797
|
|
|
798
|
+
// ── 모바일: 워크스페이스 드로어 ──
|
|
799
|
+
function setDrawer(open){ $('rail').classList.toggle('open', open); $('scrim').classList.toggle('on', open); }
|
|
800
|
+
$('menuBtn').addEventListener('click', function(){ setDrawer(!$('rail').classList.contains('open')); });
|
|
801
|
+
$('scrim').addEventListener('click', function(){ setDrawer(false); });
|
|
802
|
+
|
|
803
|
+
// ── 모바일 터미널 입력바 — 조합 완료 텍스트를 통째로 포커스 탭의 PTY 로(IME 안전) ──
|
|
804
|
+
function focusedWs(){ var rid=focusedRunId(); return (rid!=null && tabs[rid]) ? tabs[rid].ws : null; }
|
|
805
|
+
function termSendRaw(d){ var ws=focusedWs(); if(ws && ws.readyState===1) ws.send(JSON.stringify({t:'i',d:d})); }
|
|
806
|
+
function termSendLine(){ var inp=$('termInput'); var v=inp.value; if(!v){ termSendRaw('\\r'); return; } termSendRaw(v+'\\r'); inp.value=''; inp.focus(); }
|
|
807
|
+
$('termSend').addEventListener('click', termSendLine);
|
|
808
|
+
$('termInput').addEventListener('keydown', function(e){ if(e.isComposing) return; if(e.key==='Enter'){ e.preventDefault(); termSendLine(); } });
|
|
809
|
+
var TKEYS={ esc:'\\x1b', tab:'\\t', enter:'\\r', cc:'\\x03', cd:'\\x04', cr:'\\x12', cu:'\\x15',
|
|
810
|
+
up:'\\x1b[A', down:'\\x1b[B', right:'\\x1b[C', left:'\\x1b[D' };
|
|
811
|
+
Array.prototype.forEach.call(document.querySelectorAll('#termIbar .tkey'), function(b){ b.addEventListener('click', function(){ var k=TKEYS[b.dataset.k]; if(k){ termSendRaw(k); var inp=$('termInput'); if(inp) inp.focus(); } }); });
|
|
812
|
+
// 터치 기기(아이패드 포함) 판별 → body.touch (화면폭 무관하게 입력바 노출)
|
|
813
|
+
if (window.matchMedia('(pointer:coarse)').matches || (navigator.maxTouchPoints||0) > 0) document.body.classList.add('touch');
|
|
814
|
+
|
|
743
815
|
// ── 자유 세션 — 폴더를 지정해 tmux 셸(프로젝트 비소속). ──
|
|
744
816
|
var pickPathCur = '';
|
|
745
817
|
function machineSlug(){ return (fleet.machines && fleet.machines[0] && fleet.machines[0].slug) || 'local'; }
|