coxpit 2.3.0 → 2.4.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 +56 -1
- package/src/server.ts +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "coxpit",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.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
|
@@ -71,6 +71,26 @@ export const BOARD_HTML = /* html */ `<!doctype html>
|
|
|
71
71
|
.row .narrow{flex:0 0 64px}
|
|
72
72
|
.check{display:flex;align-items:center;gap:8px;font-size:12px;color:var(--muted);cursor:pointer;user-select:none}
|
|
73
73
|
.check input{width:auto;accent-color:var(--brand)}
|
|
74
|
+
|
|
75
|
+
/* ── custom dropdown (native select 대체) ── */
|
|
76
|
+
.dd{position:relative}
|
|
77
|
+
.dd-btn{width:100%;display:flex;align-items:center;gap:8px;background:#0e1118;color:var(--ink);
|
|
78
|
+
border:1px solid var(--line);border-radius:var(--r-ctl);padding:8px 10px;font-family:var(--mono);
|
|
79
|
+
font-size:12px;cursor:pointer;transition:border-color .15s;text-align:left}
|
|
80
|
+
.dd-btn:hover{border-color:var(--line-hi)}
|
|
81
|
+
.dd.open .dd-btn{border-color:rgba(78,201,176,.55)}
|
|
82
|
+
.dd-lbl{flex:1;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}
|
|
83
|
+
.dd-car{color:var(--faint);font-size:10px;transition:transform .15s}
|
|
84
|
+
.dd.open .dd-car{transform:rotate(180deg)}
|
|
85
|
+
.dd-panel{position:absolute;top:calc(100% + 5px);left:0;right:0;z-index:40;background:var(--surface2);
|
|
86
|
+
border:1px solid var(--line-hi);border-radius:9px;box-shadow:var(--shadow);max-height:230px;
|
|
87
|
+
overflow:auto;padding:4px;display:none}
|
|
88
|
+
.dd.open .dd-panel{display:block}
|
|
89
|
+
.dd-opt{padding:7px 10px;border-radius:6px;font-family:var(--mono);font-size:12px;color:var(--muted);
|
|
90
|
+
cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
91
|
+
.dd-opt:hover{background:var(--surface);color:var(--ink)}
|
|
92
|
+
.dd-opt.on{color:var(--brand)}
|
|
93
|
+
.dd-opt.on::before{content:'✓ ';font-size:10px}
|
|
74
94
|
.seg{display:flex;gap:3px;padding:3px;border:1px solid var(--line);border-radius:var(--r-ctl);background:#0e1118}
|
|
75
95
|
.seg-opt{flex:1;display:flex;flex-direction:column;align-items:center;gap:1px;padding:6px 8px;border:none;
|
|
76
96
|
background:transparent;color:var(--muted);font-size:12px;font-weight:600;cursor:pointer;
|
|
@@ -363,6 +383,40 @@ function toast(msg, kind){
|
|
|
363
383
|
$('toasts').appendChild(el);
|
|
364
384
|
setTimeout(()=>{ el.style.opacity='0'; el.style.transition='opacity .25s'; setTimeout(()=>el.remove(),260); }, 4200);
|
|
365
385
|
}
|
|
386
|
+
/* ── custom dropdown — 숨긴 native select 를 상태 보관용으로 감싼다 ── */
|
|
387
|
+
function dressSelect(id){
|
|
388
|
+
const sel = $(id); if(!sel || sel.dataset.dd) return;
|
|
389
|
+
sel.dataset.dd = '1';
|
|
390
|
+
const dd = document.createElement('div'); dd.className = 'dd';
|
|
391
|
+
dd.innerHTML = '<button type="button" class="dd-btn"><span class="dd-lbl"></span><span class="dd-car">▾</span></button><div class="dd-panel"></div>';
|
|
392
|
+
sel.parentNode.insertBefore(dd, sel);
|
|
393
|
+
sel.style.display = 'none';
|
|
394
|
+
dd.querySelector('.dd-btn').addEventListener('click', (e)=>{
|
|
395
|
+
e.stopPropagation();
|
|
396
|
+
const wasOpen = dd.classList.contains('open');
|
|
397
|
+
closeDropdowns();
|
|
398
|
+
if (!wasOpen) dd.classList.add('open');
|
|
399
|
+
});
|
|
400
|
+
dd.querySelector('.dd-panel').addEventListener('click', (e)=>{
|
|
401
|
+
const o = e.target.closest('.dd-opt'); if(!o) return;
|
|
402
|
+
sel.value = o.dataset.v;
|
|
403
|
+
dd.classList.remove('open');
|
|
404
|
+
syncSelect(id);
|
|
405
|
+
});
|
|
406
|
+
syncSelect(id);
|
|
407
|
+
}
|
|
408
|
+
function syncSelect(id){
|
|
409
|
+
const sel = $(id); if(!sel) return;
|
|
410
|
+
const dd = sel.previousElementSibling;
|
|
411
|
+
if (!dd || !dd.classList || !dd.classList.contains('dd')) return;
|
|
412
|
+
const cur = sel.options[sel.selectedIndex];
|
|
413
|
+
dd.querySelector('.dd-lbl').textContent = cur ? cur.textContent : '—';
|
|
414
|
+
dd.querySelector('.dd-panel').innerHTML = [...sel.options].map(o =>
|
|
415
|
+
'<div class="dd-opt'+(o.value===sel.value?' on':'')+'" data-v="'+esc(o.value)+'">'+esc(o.textContent)+'</div>').join('');
|
|
416
|
+
}
|
|
417
|
+
function closeDropdowns(){ document.querySelectorAll('.dd.open').forEach(d=>d.classList.remove('open')); }
|
|
418
|
+
document.addEventListener('click', closeDropdowns);
|
|
419
|
+
|
|
366
420
|
let cfmResolve = null;
|
|
367
421
|
function confirmUI(message, opts){
|
|
368
422
|
opts = opts || {};
|
|
@@ -511,6 +565,7 @@ function paintSidebar(){
|
|
|
511
565
|
capSel.innerHTML = '<option value="">no design capture</option>' + captures.map(c=>
|
|
512
566
|
'<option value="'+c.id+'">#'+c.id+' '+esc((c.selector||'').slice(0,40))+'</option>').join('');
|
|
513
567
|
capSel.value = cur;
|
|
568
|
+
['repoMachine','taskRepo','taskCapture'].forEach(id => { dressSelect(id); syncSelect(id); });
|
|
514
569
|
$('captures').innerHTML = captures.map(c=>
|
|
515
570
|
'<div class="repo"><span class="nm">'+esc((c.selector||'?').slice(0,46))+'</span>'
|
|
516
571
|
+ '<button class="x" data-delcap="'+c.id+'" style="float:right;background:none;border:none;color:var(--faint);cursor:pointer">×</button>'
|
|
@@ -591,7 +646,7 @@ $('grid').addEventListener('click',(e)=>{
|
|
|
591
646
|
});
|
|
592
647
|
$('mClose').addEventListener('click', closeModal);
|
|
593
648
|
$('overlay').addEventListener('click',(e)=>{ if(e.target===$('overlay')) closeModal(); });
|
|
594
|
-
document.addEventListener('keydown',(e)=>{ if(e.key==='Escape'){ cfmClose(false); closeTerm(); closeModal(); cmpTaskId=null; $('cmpOverlay').classList.remove('open'); } });
|
|
649
|
+
document.addEventListener('keydown',(e)=>{ if(e.key==='Escape'){ closeDropdowns(); cfmClose(false); closeTerm(); closeModal(); cmpTaskId=null; $('cmpOverlay').classList.remove('open'); } });
|
|
595
650
|
$('mRefreshDiff').addEventListener('click', loadDiff);
|
|
596
651
|
async function sendSteer(){
|
|
597
652
|
if (openRunId==null) return;
|
package/src/server.ts
CHANGED
|
@@ -29,7 +29,7 @@ export async function buildServer(): Promise<FastifyInstance> {
|
|
|
29
29
|
app.addHook('onRequest', authGate);
|
|
30
30
|
|
|
31
31
|
// 무인증 헬스(외부 감시용)
|
|
32
|
-
app.get('/api/health', async () => ({ ok: true, name: 'coxpit', version: '2.
|
|
32
|
+
app.get('/api/health', async () => ({ ok: true, name: 'coxpit', version: '2.4.0' }));
|
|
33
33
|
|
|
34
34
|
// 플릿 보드(단일 페이지). 인증 게이트 적용됨.
|
|
35
35
|
app.get('/', async (_req, reply) => reply.type('text/html').send(BOARD_HTML));
|
|
@@ -372,7 +372,7 @@ export async function buildServer(): Promise<FastifyInstance> {
|
|
|
372
372
|
// 라이브 스트림 좌석 — 오케스트레이터가 run/event 를 여기로 broadcast.
|
|
373
373
|
app.get('/ws', { websocket: true }, (socket) => {
|
|
374
374
|
addSink(socket);
|
|
375
|
-
socket.send(JSON.stringify({ type: 'hello', name: 'coxpit-fleet', version: '2.
|
|
375
|
+
socket.send(JSON.stringify({ type: 'hello', name: 'coxpit-fleet', version: '2.4.0' }));
|
|
376
376
|
socket.on('close', () => removeSink(socket));
|
|
377
377
|
});
|
|
378
378
|
|