diff --git a/public/index.html b/public/index.html index 9d36cd1..b74737d 100644 --- a/public/index.html +++ b/public/index.html @@ -500,6 +500,69 @@ font-size: 11px; } + .git-line { + font-family: monospace; + } + + .git-icon { + margin-right: 4px; + opacity: 0.7; + } + + .git-additions { + color: #4CAF50; + } + + .git-deletions { + color: #f44336; + } + + .git-file { + font-family: monospace; + font-size: 0.85em; + } + + .git-file-modified { + color: #ff9800; + } + + .git-file-added { + color: #4CAF50; + } + + .git-file-deleted { + color: #f44336; + } + + .git-file-untracked { + color: #9e9e9e; + } + + .git-files { + margin-top: 8px; + } + + .git-files summary { + cursor: pointer; + user-select: none; + padding: 4px 0; + } + + .git-files summary:hover { + opacity: 0.8; + } + + .git-file-list { + margin-top: 4px; + padding-left: 16px; + } + + .git-file-more { + color: #666; + font-style: italic; + margin-top: 4px; + } + .empty-state { padding: 32px 16px; text-align: center; @@ -696,6 +759,8 @@ mode: 'normal', model: null, idle_since: null, + git_branch: null, + git_files_json: null, }); renderSessions(); }); @@ -758,6 +823,16 @@ } }); + es.addEventListener('git', (e) => { + const data = JSON.parse(e.data); + const session = state.sessions.get(data.session_id); + if (session) { + session.git_branch = data.branch; + session.git_files_json = data.files_json; + updateStatsWidget(data.session_id); + } + }); + state.eventSource = es; } @@ -790,6 +865,14 @@ return `${Math.floor(seconds / 3600)}h`; } + function getFileClass(status) { + if (status === 'M') return 'git-file-modified'; + if (status === 'A') return 'git-file-added'; + if (status === 'D') return 'git-file-deleted'; + if (status === '??') return 'git-file-untracked'; + return ''; + } + function renderStatsWidget(session) { const lines = []; @@ -831,6 +914,74 @@ lines.push(`
${badges.join(' ')}
`); } + // Git info + if (session.git_branch || session.git_files_json) { + const gitParts = []; + let files = []; + let totalAdditions = 0; + let totalDeletions = 0; + + // Parse files if available + if (session.git_files_json) { + try { + files = JSON.parse(session.git_files_json); + files.forEach(f => { + totalAdditions += f.additions || 0; + totalDeletions += f.deletions || 0; + }); + } catch (e) { + console.error('Failed to parse git files JSON:', e); + } + } + + // Build git summary line + if (session.git_branch) { + gitParts.push(``); + gitParts.push(`${escapeHtml(session.git_branch)}`); + } + + if (files.length > 0) { + gitParts.push(`| ${files.length} file${files.length === 1 ? '' : 's'}`); + } + + if (totalAdditions > 0) { + gitParts.push(`| +${totalAdditions}`); + } + + if (totalDeletions > 0) { + gitParts.push(`| -${totalDeletions}`); + } + + if (gitParts.length > 0) { + let gitLine = `
${gitParts.join(' ')}
`; + + // Add collapsible file list if there are files + if (files.length > 0) { + const fileListItems = files.slice(0, 10).map(f => { + const filePath = escapeHtml(f.path); + const fileClass = getFileClass(f.status); + const additions = f.additions ? `+${f.additions}` : ''; + const deletions = f.deletions ? `-${f.deletions}` : ''; + return `
${escapeHtml(f.status)} ${filePath} ${additions} ${deletions}
`; + }).join(''); + + const moreText = files.length > 10 ? `
and ${files.length - 10} more...
` : ''; + + gitLine += ` +
+ ${files.length} changed file${files.length === 1 ? '' : 's'} +
+ ${fileListItems} + ${moreText} +
+
+ `; + } + + lines.push(gitLine); + } + } + if (lines.length === 0) { return ''; }