v1.0.209
✨ New: Save-or-stay on file switch
Switching files mid-edit no longer silently drops unsaved changes. With a dirty editor, Cockpit prompts Save and switch vs Cancel — pick Cancel and you stay on the current file with the edit intact. Clean editors switch instantly as before.
Bonus: vi-mode mutations (yyp, dd, x, …) in normal mode now auto-promote the viewer into edit mode and mark the file dirty, so Save, Cmd+S, and the new switch prompt all treat them as real unsaved edits. The caret lands near the vi cursor on entry.
✨ New: Chat tool list keeps its header reachable
Single-tool calls in chat used to render inline and hide the tool-list header — the bar that holds AskUserQuestion, FileDiff, and the other interactive entry points. The list now always collapses, and the single-tool case is expanded by default, so you keep the content visible and the header always sits where you expect it.
🌐 Site: positioned for any agent, not just Claude
cocking.cc and the README have been rewritten around "Claude by default — bring any agent you want." A new Engines section on the landing page covers Claude, OpenAI Codex, DeepSeek, Kimi, and Ollama side by side; the hero badge reads Claude · Codex · DeepSeek · Kimi · Ollama; the README pain table, comparison table, and use-cases pick up the same framing (cheap second opinion, Ollama air-gapped, …). The npm package description and keywords broaden too, so @surething/cockpit now turns up in npm search for more than just "Claude."
UI labels normalize DeepSeek (a few places still spelled it "Deepseek"); code identifiers are intentionally left as-is to keep diffs small.
🐛 Fix: no more [object Object] on error screens
The recent Effect-runtime overhaul changed every API route's error shape to a structured object, but every client error path was still doing new Error(data.error || …) — which String()-ifies an object into the literal [object Object]. That's what users were seeing on the Postgres bubble's "connection failed" screen, on SQL execution failures, and on roughly every other route's error display.
Server-side, errorToResponse now serializes errors as { error: <string>, tag: <_tag> }, extracting a real message from e.message, cause.message, or the error's typed fields in that order. Client-side, a defensive helper handles the old object shape too, so a future regression can never reintroduce [object Object] in the UI. Real database / filesystem / agent error messages everywhere again.
🐛 Fix: DiffView no longer garbles rows after panel switch
Diff rows could collapse into each other vertically after the sequence open a diff → swipe to Chat → have the agent edit the file a few times → swipe back — the row-overlap / "garbled diff" symptom. Root cause was the virtualizer keying its measurement cache by array index, so a freshly rebuilt diff could reuse a sibling row's stale ~0 height; the misaligned render frame between plain-text and tokenized highlight then pinned the bad measurement.
Three layered fixes land together:
useLineHighlightnow derives the plain-text fallback synchronously from the current input viauseMemo, removing the render frame where state could disagree with input.- The virtualizer gets
getItemKeyso rows are keyed by identity (gap id, or L/R line number) instead of array position — cross-build cache reuse can't pick up a sibling's measurement. - The cache reset on file change moves from
useEffecttouseLayoutEffect, closing the timing window where ResizeObserver could write back into the just-cleared cache.
The original bug-report reproduction no longer shows overlapping rows.