Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion desktop/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "codbash-desktop",
"productName": "codbash",
"version": "7.14.1",
"version": "7.14.2",
"private": true,
"description": "Desktop shell (Electron) for codbash — wraps the codbash server in a native window.",
"main": "main.js",
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "codbash-app",
"version": "7.14.1",
"version": "7.14.2",
"description": "Dashboard + CLI for AI coding agents — Claude Code, Codex, Cursor, OpenCode, Kiro. View, search, resume, convert, sync sessions.",
"bin": {
"codbash": "./bin/cli.js",
Expand Down
9 changes: 9 additions & 0 deletions src/changelog.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
'use strict';

const CHANGELOG = [
{
version: '7.14.2',
date: '2026-07-20',
title: 'Running-projects sidebar tree + safer pane labels',
changes: [
'The sidebar now shows a live tree of projects that have running terminals — click a project or terminal to jump straight to it',
'Pane title bars show the real command (e.g. "claude"), stripping leading VAR=value env prefixes so proxy credentials never appear in the header',
],
},
{
version: '7.14.1',
date: '2026-07-20',
Expand Down
3 changes: 3 additions & 0 deletions src/frontend/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,9 @@
</div>
</div>

<!-- Live tree of projects that currently have running Workspace terminals. -->
<div id="wsRunningTree" class="ws-running-tree" style="display:none"></div>

<span id="sidebarStatus" role="status" aria-live="polite" class="sr-only"></span>
<div class="sidebar-author">
Made by <a href="https://t.me/neuraldeep" target="_blank" rel="noopener noreferrer" aria-label="Valerii Kovalskii on Telegram (opens in new tab)">Valerii Kovalskii</a>
Expand Down
25 changes: 25 additions & 0 deletions src/frontend/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -2872,6 +2872,31 @@ body.sidebar-hidden .toolbar { padding-left: 52px; }
border: 1px solid var(--accent-blue); border-radius: 4px; padding: 1px 4px;
}

/* Sidebar running-terminals tree (bottom of the sidebar). */
.ws-running-tree {
margin: 8px 8px 4px; padding-top: 8px; border-top: 1px solid var(--border);
}
.ws-run-title {
font-size: 10px; font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase;
color: var(--text-muted); padding: 0 8px 4px;
}
.ws-run-proj {
display: flex; align-items: center; gap: 6px; padding: 4px 8px;
font-size: 12px; color: var(--text-primary); cursor: pointer; border-radius: 6px;
}
.ws-run-proj:hover { background: rgba(255,255,255,0.06); }
.ws-run-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--accent-green); flex-shrink: 0; }
.ws-run-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-family: Menlo, Monaco, monospace; }
.ws-run-count {
font-size: 10px; color: var(--text-muted); background: var(--bg-hover);
border-radius: 8px; padding: 0 6px; min-width: 16px; text-align: center;
}
.ws-run-term {
padding: 2px 8px 2px 22px; font-size: 11px; color: var(--text-secondary);
cursor: pointer; border-radius: 6px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ws-run-term:hover { background: rgba(255,255,255,0.05); color: var(--text-primary); }

/* In-app prompt (codbashPrompt) — replaces window.prompt (no-op in Electron). */
.cb-prompt-overlay {
position: fixed; inset: 0; z-index: 1000;
Expand Down
57 changes: 55 additions & 2 deletions src/frontend/workspace.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,11 +140,59 @@ function _wsStartStatusLoop() {
if (_wsStatusTimer) return;
_wsStatusTimer = setInterval(function () {
_wsUpdateStatusBar();
_wsRenderRunningTree();
// Keep the Overview landing's cards live too.
if (typeof _ovRefreshIfCurrent === 'function') _ovRefreshIfCurrent();
}, 1000);
}

// Group live terminals by their project folder (skips the home dir — those
// aren't "projects"). Used by the sidebar running-terminals tree.
function _wsRunningByProject() {
var groups = {}, order = [];
_wsAllPanes().forEach(function (x) {
var p = x.pane;
if (!p.sock || p.exited || p.sock.readyState !== 1) return;
var cwd = p.cwd || '';
if (!cwd || /^(\/Users\/[^/]+|\/home\/[^/]+|\/root)\/?$/.test(cwd)) return;
var key = _wsProjectBasename(cwd);
if (!groups[key]) { groups[key] = { name: key, items: [] }; order.push(key); }
groups[key].items.push(x);
});
return order.map(function (k) { return groups[k]; });
}

// Render a compact tree at the bottom of the sidebar: each project folder that
// has running terminals, with its terminals underneath — click to jump.
var _wsRunTreeSig = '';
function _wsRenderRunningTree() {
var el = document.getElementById('wsRunningTree');
if (!el) return;
var groups = _wsRunningByProject();
var sig = groups.map(function (g) {
return g.name + ':' + g.items.map(function (x) { return x.pane.id + '=' + _wsPaneLabel(x.pane); }).join(',');
}).join('|');
if (sig === _wsRunTreeSig) return; // no change → no rebuild
_wsRunTreeSig = sig;

if (!groups.length) { el.style.display = 'none'; el.innerHTML = ''; return; }
var html = '<div class="ws-run-title">Running in projects</div>';
groups.forEach(function (g) {
var first = g.items[0];
html += '<div class="ws-run-proj" title="' + escHtml(g.name) + '" ' +
'onclick="jumpToWorkspacePane(\'' + escHtml(first.tab.id) + '\',\'' + escHtml(first.pane.id) + '\')">' +
'<span class="ws-run-dot"></span><span class="ws-run-name">' + escHtml(g.name) + '</span>' +
'<span class="ws-run-count">' + g.items.length + '</span></div>';
g.items.forEach(function (x) {
html += '<div class="ws-run-term" ' +
'onclick="jumpToWorkspacePane(\'' + escHtml(x.tab.id) + '\',\'' + escHtml(x.pane.id) + '\')">' +
escHtml(_wsPaneLabel(x.pane)) + '</div>';
});
});
el.innerHTML = html;
el.style.display = '';
}

// Jump from a status chip to that pane: show Workspace, activate its tab, focus it.
function jumpToWorkspacePane(tabId, paneId) {
if (typeof setView === 'function') setView('workspace');
Expand Down Expand Up @@ -278,8 +326,13 @@ function _wsShortCwd(cwd) {
// otherwise the folder name (e.g. "CoWork"), or "~" for the home directory.
function _wsPaneLabel(pane) {
if (pane && pane.cmd) {
var w = String(pane.cmd).trim().split(/\s+/)[0];
if (w) return w;
// Strip leading `VAR=value` env assignments (value may be quoted and hold
// secrets, e.g. HTTPS_PROXY='http://user:pass@host') so the label is the
// actual command — "claude", not the proxy string.
var cmd = String(pane.cmd).trim()
.replace(/^([A-Za-z_][A-Za-z0-9_]*=(?:'[^']*'|"[^"]*"|\S+)\s+)+/, '');
var w = cmd.split(/\s+/)[0];
if (w) return (typeof _wsMaskSecrets === 'function') ? _wsMaskSecrets(w) : w;
}
var c = (pane && pane.cwd) || '';
if (!c) return 'shell';
Expand Down
Loading