/**
* Session Panel — 세션 목록 UI 관리
*/
class SessionPanel {
constructor() {
this.sessions = [];
this.activeSessionId = null;
this.onSessionSelect = null; // callback(sessionId)
this.onSessionRemove = null; // callback(sessionId)
this.listEl = document.getElementById('sessionList');
}
/**
* 세션 목록 업데이트
*/
update(sessions) {
this.sessions = sessions;
this.render();
}
/**
* 활성 세션 설정
*/
setActive(sessionId) {
this.activeSessionId = sessionId;
this.render();
}
/**
* 세션 목록 렌더링
*/
render() {
if (!this.listEl) return;
if (this.sessions.length === 0) {
this.listEl.innerHTML = `
세션이 없습니다
+ 버튼으로 추가하세요
`;
return;
}
this.listEl.innerHTML = this.sessions.map(s => {
const timeStr = s.lastModified ? this._relativeTime(s.lastModified) : '';
const projectBadge = s.project
? `${this._escapeHtml(s.project)}`
: '';
const runningDot = s.isRunning ? '●' : '';
const detail = s.isBridge
? `${s.stepCount || 0} steps${timeStr ? ' · ' + timeStr : ''}`
: `${s.host}:${s.cdpPort} · ${this._statusText(s.status)}`;
return `
${runningDot}${this._escapeHtml(s.name)}
${projectBadge}${detail}
`;
}).join('');
// 이벤트 바인딩
this.listEl.querySelectorAll('.session-card').forEach(card => {
card.addEventListener('click', (e) => {
if (e.target.closest('.session-remove')) return;
const id = card.dataset.sessionId;
if (this.onSessionSelect) this.onSessionSelect(id);
});
});
this.listEl.querySelectorAll('.session-remove').forEach(btn => {
btn.addEventListener('click', (e) => {
e.stopPropagation();
const id = btn.dataset.removeId;
if (this.onSessionRemove) this.onSessionRemove(id);
});
});
}
_statusText(status) {
const map = {
connected: '연결됨',
connecting: '연결 중...',
disconnected: '연결 끊김',
running: '실행 중',
error: '오류',
};
return map[status] || status;
}
_relativeTime(iso) {
const diff = Date.now() - new Date(iso).getTime();
const mins = Math.floor(diff / 60000);
if (mins < 1) return '방금';
if (mins < 60) return `${mins}분 전`;
const hrs = Math.floor(mins / 60);
if (hrs < 24) return `${hrs}시간 전`;
const days = Math.floor(hrs / 24);
return `${days}일 전`;
}
_projectColor(name) {
const colors = {
'gravity_web': '#6366f1',
'Deriva': '#f59e0b',
'MMF': '#10b981',
'test1': '#8b5cf6',
'test2': '#ec4899',
};
return colors[name] || '#64748b';
}
_escapeHtml(str) {
const div = document.createElement('div');
div.textContent = str;
return div.innerHTML;
}
}