feat: AI commit messages, change tree, router behavesAs, sidebar times (v2.2.0)

- Changes panel: Files and Changes merged into one panel; the overview's
  groups drawn as a folder tree (compacted chains, file counts, fold,
  stage/unstage a whole folder)
- AI-suggested commit message for what is staged
  (POST /api/lanes/:id/git/commit-message, claude -p --safe-mode, haiku)
- AI Router: optional "Behaves as" Claude model id, written as
  modelPicker options into the router's --settings file (issue #56)
- Sidebar: session rows show last activity, live rows their start time;
  a closed Terminal row can be removed (DELETE /api/run/history/:id)
- Every pane's CLI starts only once a client is attached; claude panes
  no longer forced inline; tmux scrollbar one column wide
- Update notifier: "Check for updates" reopens a closed notice
This commit is contained in:
2026-10-06 12:36:06 +07:00
parent 6c605849d8
commit f0500dc592
41 changed files with 1201 additions and 249 deletions
+9 -4
View File
@@ -29,8 +29,9 @@
* ## Dismissal persistence
* Dismissals are keyed in `localStorage` by the release tag when there is one,
* else the upstream `remote_sha`, so a newer release re-opens the prompt while
* a dismissed one stays dismissed. Settings can reset dismissal via the
* `dashboard:reset-update-dismissal` window event.
* a dismissed one stays dismissed. The connection modal's "Check for updates"
* fires `dashboard:reset-update-dismissal`, which clears both the skip and the
* page-load close; `z-[110]` keeps this modal above that `z-[100]` one.
*
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
*/
@@ -184,7 +185,11 @@ export function UpdateNotifier() {
}, [syncFromPayload]);
useEffect(() => {
const handler = () => setDismissedSha(null);
// An explicit "Check for updates" undoes both kinds of close.
const handler = () => {
setDismissedSha(null);
setClosedKey(null);
};
window.addEventListener("dashboard:reset-update-dismissal", handler);
return () => window.removeEventListener("dashboard:reset-update-dismissal", handler);
}, []);
@@ -313,7 +318,7 @@ export function UpdateNotifier() {
return (
<div
className="fixed inset-0 z-[100] flex items-center justify-center p-4 bg-surface-0/70 backdrop-blur-sm animate-fade-in"
className="fixed inset-0 z-[110] flex items-center justify-center p-4 bg-surface-0/70 backdrop-blur-sm animate-fade-in"
role="dialog"
aria-modal="true"
aria-labelledby="update-notifier-title"