feat(ui): git status indicator in the bottom bar, with a panel of uncommitted and unpushed work

Optional and per-device (showGitStatus, default off). GET /api/sessions/:id/git-status is
read-only and offline (no fetch, --no-optional-locks), skips remote and Docker sessions, caps its
lists, and single-flights concurrent polls. The toolbar indicator shows uncommitted files,
commits not pushed, or a check; clicking opens a draggable panel in the style of the Files window.

Which repositories: the enclosing one when there is one; otherwise every repository up to two
levels below the working directory (capped, skipping dot-folders and node_modules, never
following symlinks), each in a collapsible section, with the indicator summing them. A repository
that merely sits above the workspace and is the home folder or higher (a dotfiles repo) is
ignored. Git-supplied text is only ever written with textContent.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JrzFKEdBLwVfu6ev2ZscJS
This commit is contained in:
Devvyn
2026-10-05 07:19:11 +08:00
co-authored by Claude Sonnet 5.5
parent ffaa5ee80c
commit bfc164a262
14 changed files with 2528 additions and 1 deletions
+34
View File
@@ -567,6 +567,19 @@
<div class="file-browser-status" id="fileBrowserStatus"></div>
</div>
<!-- Git status panel (git-status-ui.js): what the active session's repo has not committed or pushed. -->
<div class="git-status-panel" id="gitStatusPanel" role="dialog" aria-label="Git status">
<div class="git-status-header">
<span class="git-status-title">Git <span class="git-status-branch" id="gitStatusBranch" data-i18n-skip></span></span>
<div class="git-status-actions">
<button class="btn-icon-sm" onclick="app.refreshGitStatusNow()" title="Refresh" aria-label="Refresh git status">&#x21BB;</button>
<button class="btn-icon-sm" onclick="app.closeGitStatusPanel()" title="Close" aria-label="Close git status">&times;</button>
</div>
</div>
<div class="git-status-body" id="gitStatusBody" data-i18n-skip></div>
<div class="git-status-footer" id="gitStatusFooter" data-i18n-skip></div>
</div>
<!-- File Preview Overlay -->
<div class="file-preview-overlay" id="filePreviewOverlay">
<div class="file-preview-window">
@@ -771,6 +784,13 @@
<!-- Orchestrator button hidden until feature is ready -->
<!-- <button class="btn-toolbar btn-sm" onclick="app.toggleOrchestratorPanel()" title="Orchestrator Loop">&#x2699; Orchestrator</button> -->
<button class="btn-toolbar btn-sm btn-cron btn-cron--hidden" onclick="app.openCron()" title="Cron Jobs">&#x23F0; Cron</button>
<!-- Git status of the active session's repository (git-status-ui.js). Optional and per-device
(App Settings → Header & Panels → Bottom bar, default OFF), so it is hidden until that
setting is on AND the session is a local git repository. -->
<button type="button" class="btn-toolbar btn-sm btn-git-status" id="gitStatusBtn" hidden aria-expanded="false" aria-controls="gitStatusPanel" onclick="app.toggleGitStatusPanel()">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="6" cy="6" r="2.5"/><circle cx="6" cy="18" r="2.5"/><circle cx="18" cy="8" r="2.5"/><path d="M6 8.5v7"/><path d="M18 10.5c0 4-6 3-11 6"/></svg>
<span class="git-status-label" data-i18n-skip></span>
</button>
<span class="version-display" id="versionDisplay" title="Codeman version">v0.0.0</span>
</div>
</footer>
@@ -1935,6 +1955,19 @@
</div>
</div>
<div class="set-group">
<div class="set-group-head"><h4>Bottom bar</h4></div>
<div class="set-group-body">
<div class="set-row" data-search="git status uncommitted unpushed commit push indicator bottom bar toolbar">
<div class="set-row-text">
<span class="set-row-label">Git status <span class="set-scope">device</span></span>
<span class="set-row-desc">Shows, at the right of the bottom bar, when the active session's repository (or each repository inside its folder, up to two levels down) has uncommitted files or commits that are not pushed. Click it for the list. Read-only: Codeman never fetches or changes the repository. Not shown for Docker or remote sessions. Off by default.</span>
</div>
<label class="switch switch-sm"><input type="checkbox" id="appSettingsShowGitStatus"><span class="slider"></span></label>
</div>
</div>
</div>
<div class="set-group">
<div class="set-group-head"><h4>Subagent windows</h4></div>
<div class="set-group-body">
@@ -3870,6 +3903,7 @@
<script defer src="webview-tabs.js"></script>
<script defer src="mobile-overview.js"></script>
<script defer src="home-sessions.js"></script>
<script defer src="git-status-ui.js"></script>
<script defer src="entrance-animations.js"></script>
<script defer src="ralph-wizard.js"></script>
<script defer src="api-client.js"></script>