diff --git a/README.md b/README.md index 7afe853..b8465b0 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ See what needs attention, resume the right conversation, and work on a paired PC -> **1.47.1** makes closing a Claude Code background session from the sidebar work when Claude Code's background service is not running — DevDeck starts it as Claude Code does and tries again, and a failure names the PC the command has to run on ([release notes](docs/releases/1.47.1.md)). 1.47.0 moves to Electron 44 (Chromium 152, Node.js 24); macOS 13 or later is now required, and screenshot paste / image copy use Electron's new clipboard API ([release notes](docs/releases/1.47.0.md)). 1.46.0 lets you close Claude Code's background sessions from the sidebar — the row's ⋯ menu or the group's bulk close runs `claude stop` and `claude rm` for you, so a finished or failed session no longer sits in the Background group forever; its conversation is kept ([release notes](docs/releases/1.46.0.md)). 1.45.2 stops an update from doubling the sessions on a paired PC — the relaunch no longer restores remote tiles itself, it adopts them from that PC, and no restore starts a second conversation for one that is already open here ([release notes](docs/releases/1.45.2.md)). 1.45.1 stops offering Claude background sessions whose folder was deleted, and says so plainly when a session's folder is gone ([release notes](docs/releases/1.45.1.md)). 1.45.0 sends any file to a session on another PC — any kind, up to 4 GB, in pieces with its progress shown ([release notes](docs/releases/1.45.0.md)). 1.44.1 fixes the "Assertion failed: remove_pty_baton" dialog (and the occasional hang or crash) when quitting or running Update all with several sessions open ([release notes](docs/releases/1.44.1.md)). 1.44.0 lists Claude Code's own background sessions (`claude --bg`, the agent view) in the sidebar, marks the ones waiting for you, and opens one in a tile by attaching to it ([release notes](docs/releases/1.44.0.md)). 1.43.0 runs parallel sessions in separate workspaces — each in its own git worktree and branch, so two agents never edit the same files — with its changes in the header and a clean-up that warns before unmerged work is lost ([release notes](docs/releases/1.43.0.md)). 1.42.0 shows what changed — a What's new window after each update, with Show me, and NEW dots on new buttons until you try them — and, as an experimental setting on Windows, lets Claude Code report its own state (working, needs approval, asking, done) instead of DevDeck reading the screen ([release notes](docs/releases/1.42.0.md)). 1.41.0 adds quick replies (one click or Ctrl+Shift+1…9 for the phrases you keep typing), a session timeline of your prompts with their times, replies and images, red boxes and arrows drawn on a screenshot and sent straight back to the session, and text, log and PDF attachments for sessions on another PC ([release notes](docs/releases/1.41.0.md)). 1.40.0 ties each screenshot to the terminal row it appeared on — hover a highlighted `[Image]` for a thumbnail, click for the full view, and jump from the viewer back to the row — says what led to each image, previews new screenshots, copies images, and adds Ctrl+↑/↓ between your prompts ([release notes](docs/releases/1.40.0.md)). 1.39.0 shows the screenshots and images a Claude session worked with — Claude Desktop shows them inline; DevDeck puts them in a strip beside the terminal, also for sessions on another PC — adds 📎 / drag-and-drop attachments (images travel to a remote PC), **Update all** for Claude Code on Windows, staggered session restores that no longer stall a PC, and ends the daily false "Login expired" ([release notes](docs/releases/1.39.0.md)). 1.38.0 made closing a session reliable across paired PCs ([notes](docs/releases/1.38.0.md)). See [the maintenance review](docs/quality-review-2026-09-08.md) and [session synchronization review](docs/session-sync-review-2026-09-08.md). +> **1.48.0** makes DevDeck lighter at rest — a quiet terminal is no longer re-scanned every second, the process listing that detects a tile's agent runs only after that tile printed something, git is re-read every two minutes for tiles you are not looking at, and the background-session listing slows to a minute when the group is empty and two when the window is not focused ([release notes](docs/releases/1.48.0.md)). 1.47.1 makes closing a Claude Code background session from the sidebar work when Claude Code's background service is not running — DevDeck starts it as Claude Code does and tries again, and a failure names the PC the command has to run on ([release notes](docs/releases/1.47.1.md)). 1.47.0 moves to Electron 44 (Chromium 152, Node.js 24); macOS 13 or later is now required, and screenshot paste / image copy use Electron's new clipboard API ([release notes](docs/releases/1.47.0.md)). 1.46.0 lets you close Claude Code's background sessions from the sidebar — the row's ⋯ menu or the group's bulk close runs `claude stop` and `claude rm` for you, so a finished or failed session no longer sits in the Background group forever; its conversation is kept ([release notes](docs/releases/1.46.0.md)). 1.45.2 stops an update from doubling the sessions on a paired PC — the relaunch no longer restores remote tiles itself, it adopts them from that PC, and no restore starts a second conversation for one that is already open here ([release notes](docs/releases/1.45.2.md)). 1.45.1 stops offering Claude background sessions whose folder was deleted, and says so plainly when a session's folder is gone ([release notes](docs/releases/1.45.1.md)). 1.45.0 sends any file to a session on another PC — any kind, up to 4 GB, in pieces with its progress shown ([release notes](docs/releases/1.45.0.md)). 1.44.1 fixes the "Assertion failed: remove_pty_baton" dialog (and the occasional hang or crash) when quitting or running Update all with several sessions open ([release notes](docs/releases/1.44.1.md)). 1.44.0 lists Claude Code's own background sessions (`claude --bg`, the agent view) in the sidebar, marks the ones waiting for you, and opens one in a tile by attaching to it ([release notes](docs/releases/1.44.0.md)). 1.43.0 runs parallel sessions in separate workspaces — each in its own git worktree and branch, so two agents never edit the same files — with its changes in the header and a clean-up that warns before unmerged work is lost ([release notes](docs/releases/1.43.0.md)). 1.42.0 shows what changed — a What's new window after each update, with Show me, and NEW dots on new buttons until you try them — and, as an experimental setting on Windows, lets Claude Code report its own state (working, needs approval, asking, done) instead of DevDeck reading the screen ([release notes](docs/releases/1.42.0.md)). 1.41.0 adds quick replies (one click or Ctrl+Shift+1…9 for the phrases you keep typing), a session timeline of your prompts with their times, replies and images, red boxes and arrows drawn on a screenshot and sent straight back to the session, and text, log and PDF attachments for sessions on another PC ([release notes](docs/releases/1.41.0.md)). 1.40.0 ties each screenshot to the terminal row it appeared on — hover a highlighted `[Image]` for a thumbnail, click for the full view, and jump from the viewer back to the row — says what led to each image, previews new screenshots, copies images, and adds Ctrl+↑/↓ between your prompts ([release notes](docs/releases/1.40.0.md)). 1.39.0 shows the screenshots and images a Claude session worked with — Claude Desktop shows them inline; DevDeck puts them in a strip beside the terminal, also for sessions on another PC — adds 📎 / drag-and-drop attachments (images travel to a remote PC), **Update all** for Claude Code on Windows, staggered session restores that no longer stall a PC, and ends the daily false "Login expired" ([release notes](docs/releases/1.39.0.md)). 1.38.0 made closing a session reliable across paired PCs ([notes](docs/releases/1.38.0.md)). See [the maintenance review](docs/quality-review-2026-09-08.md) and [session synchronization review](docs/session-sync-review-2026-09-08.md).
diff --git a/docs/releases/1.48.0.md b/docs/releases/1.48.0.md
new file mode 100644
index 0000000..cf847a4
--- /dev/null
+++ b/docs/releases/1.48.0.md
@@ -0,0 +1,20 @@
+# DevDeck 1.48.0
+
+## Lighter at rest
+
+Measured on Windows 11 with two sessions open and nothing happening (DevDeck 1.47.0, 15 minutes): about 5% of one core — main 1.7%, renderer 2.3%, GPU 1% — and some 590 MB of working set across the five processes. Most of the CPU went to periodic work that re-read what the window was already showing. This release keeps each of those for the moments it can matter; nothing shown changes.
+
+- **A quiet terminal is not re-scanned every second.** The activity tick reads the last 12 rows of every live terminal's buffer once a second to tell working from waiting. A terminal that was not written to since the last tick still shows what it showed then, so the scan is skipped — it runs after each write, and for two more seconds while xterm catches up with it.
+- **The process listing that detects a tile's agent runs only after that tile printed something.** Every 30 s each tile asked main which agent runs under its shell; main answered off a listing of every process on the machine — a PowerShell process on Windows, about 500 ms and 80 MB each time, cached 15 s. A tile that printed nothing cannot have changed agent. The listing still runs on selecting a tile, on the update restart, and whenever a tile has output.
+- **Git is re-read every two minutes for tiles you are not looking at.** Every 30 s each tile ran `git rev-parse` and `git status` (and more for a workspace) for the branch and dirty count in its row. The selected tile keeps its 30 s; the other rows refresh every fourth tick, and nothing is re-read while the window is hidden. Selecting a tile refreshes it at once, as before.
+- **The background-session listing slows down when there is nothing to watch.** `claude agents --json --all` starts a Claude Code process (~340 ms) every 30 s, on this PC and on every connected one. It stays at 30 s while the window is focused and the group has rows; a minute when the group is empty everywhere (a new `claude --bg` still shows within a minute); two minutes while the window is not focused.
+
+With two idle sessions this removes two git processes and one PowerShell process per 30 s, two Claude Code processes per minute, and about two terminal-buffer scans per second — the scheduled process starts at rest drop from roughly 9 per minute to 1.
+
+## What was looked at and left alone
+
+- The 250 ms spinner tick already pauses when the window is unfocused or hidden, and does nothing when no row is working.
+- The 5 s image poll reads only the selected Claude session, and costs one `stat` while it is idle.
+- The 15 s projects re-scan runs only while the projects view is active and the window focused.
+- The session-meta read (model, active time, context) folds only the lines appended since the last read; the usage footer polls every 5 minutes; the link heartbeat is one small frame per 15 s per machine.
+- Memory: the renderer's share is mostly the terminals' 10,000-line scrollbacks and xterm's DOM rows; the GPU process (~135 MB) is Chromium's compositor and does not grow with sessions.
diff --git a/package-lock.json b/package-lock.json
index a33d9d2..bb72fe1 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "devdeck",
- "version": "1.47.1",
+ "version": "1.48.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "devdeck",
- "version": "1.47.1",
+ "version": "1.48.0",
"license": "MIT",
"dependencies": {
"@fontsource-variable/geist": "5.3.0",
diff --git a/package.json b/package.json
index 6d33778..20eebe5 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "devdeck",
- "version": "1.47.1",
+ "version": "1.48.0",
"description": "Project command deck — at-a-glance state + Claude Code, Codex, and Antigravity resume",
"main": "dist/main/main.js",
"type": "commonjs",
diff --git a/src/renderer/cockpitView.ts b/src/renderer/cockpitView.ts
index df6d983..e972e81 100644
--- a/src/renderer/cockpitView.ts
+++ b/src/renderer/cockpitView.ts
@@ -22,6 +22,7 @@ import { setActiveUsageProvider } from './usageBar';
import { reportShutdownActivity } from './shutdown';
import { createIcon, type IconName } from './icons';
import { deckFor, machineName, machineState, onMachineConnected, onMachinesChanged, LOCAL_MACHINE_ID, HOST_TOO_OLD } from './machineDeck';
+import { agentProbeDue, backgroundPollDue, gitRefreshDue, screenScanDue } from '../shared/idleCadence';
import { parseRemoteId } from '../shared/link/machine';
import { ptyCompatFor } from './ptyCompat';
import { followPtySize, type TerminalDims } from '../shared/terminalSize';
@@ -52,6 +53,12 @@ interface Live {
*/
native: { status: ProgramStatus; since: number } | null;
openedSessionId: string | null; openedAt: number; idCheckAt: number;
+ /** When the tile's agent was last probed off the process tree — a quiet tile is not probed again. */
+ providerCheckAt: number;
+ /** When the terminal was last written to (output, a replayed screen, a repaint) — unlike lastDataAt, never withheld. */
+ screenDirtyAt: number;
+ /** The screen tail the activity tick last read, and when — reused while nothing is written. */
+ screenText: string; screenScanAt: number;
labelVersion: SessionLabelVersion | null;
customLabel: string | null; meta: SessionMetaView | null; pinned: boolean; lastSelectedAt: number;
/**
@@ -181,7 +188,14 @@ export function mountCockpit(): void {
initTimeline(termsEl, () => { if (!editingId) renderHeader(); });
// Not at start-up: restoring sessions comes first, and the list is not urgent.
setTimeout(() => void refreshBackgroundAgents(), BACKGROUND_FIRST_POLL_MS);
- setInterval(() => { if (!document.hidden) void refreshBackgroundAgents(); }, BACKGROUND_POLL_MS);
+ // Each listing starts a claude process (~340 ms) here and on every connected machine — so 30 s only
+ // while someone is looking at a group that has rows; a minute when it is empty, two unfocused.
+ setInterval(() => {
+ if (document.hidden) return;
+ // A background session opened in a tile is still in the listing, so the listing alone tells.
+ const hasRows = [...backgroundAgents.values()].some((list) => list.length > 0);
+ if (backgroundPollDue({ now: Date.now(), lastPollMs: backgroundPolledAt, windowFocused: document.hasFocus(), hasRows })) void refreshBackgroundAgents();
+ }, BACKGROUND_TICK_MS);
// Mid-turn, new screenshots show up as they are taken rather than at the end of the turn; between
// turns a slow re-read still catches images written some other way (another process, a resume).
// Only the selected session is ever read, and an unchanged transcript costs one stat.
@@ -223,6 +237,7 @@ export function mountCockpit(): void {
// Held back only while a repaint is in flight (see Live.replayPending) so the screen underneath
// cannot land on top of newer output. Activity tracking is not gated: the session IS producing.
if (l.replayPending) holdForReplay(l, chunk); else l.term.write(chunk);
+ l.screenDirtyAt = Date.now(); // the screen changes either way — a repaint too, which is not counted as output below
// ConPTY answers every size change with a full repaint of the screen. Counted as output, one window
// resize (or the usage bar appearing) turned every idle session "working" and reshuffled the
// recency order; and the repainted screen's OLD text could read as a fresh prompt.
@@ -615,7 +630,9 @@ export function cockpitNavigationItems(): ShellSessionInput[] {
// ---- Claude Code's own background sessions ----
/** Per machine, the background sessions its `claude agents` lists (see main/backgroundAgents). */
const backgroundAgents = new Map