Skip to content

feat(gpui): History menu with labelled undo steps - #2844

Open
YoavLevavi wants to merge 4 commits into
CapSoftware:mainfrom
YoavLevavi:feat/gpui-history-panel
Open

YoavLevavi wants to merge 4 commits into
CapSoftware:mainfrom
YoavLevavi:feat/gpui-history-panel

Conversation

@YoavLevavi

@YoavLevavi YoavLevavi commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

A History button next to Undo/Redo in the editor header lists every undo step, newest first, and jumps straight to one when clicked.

  • ProjectHistory keeps a label next to each snapshot, made by diffing it with the previous one ("Added zoom", "Deleted text", "Split clip", "Changed background"...). Labels follow the same truncation and 100-entry limit as the snapshots.
  • ProjectHistory::jump_to moves the cursor directly. Steps after the cursor stay redoable and are dimmed in the menu.
  • The dropdown follows the Presets menu (backdrop click and Escape close it). Adds icons/history.svg (Lucide).

Testing: cargo test --bin cap-gpui (new history_labels_steps_and_jumps_between_them).

🤖 Generated with Claude Code

RetriggerView in GreptileConfidence Score: 5/5

The PR appears safe to merge; all previous findings are addressed.

Summary

Adds a History menu with named undo steps and direct jumps between saved states.

  • Project history labels saved edits and jumps directly to a chosen snapshot.
  • The editor’s History menu lets people pick a labelled project snapshot.

All four previous findings are addressed. No new actionable issues were found. Tests and visual checks were not run.

Reviews (3) · Last reviewed commit: "History menu: close it when the history ..." · Reviewed by Greptile

Each history entry gets a short label ("Added zoom", "Changed background")
from a diff against the previous snapshot. The header's History button lists
them newest first; clicking one walks the undo stack straight there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@YoavLevavi

Copy link
Copy Markdown
Contributor Author

@greptileai

@vercel

vercel Bot commented Oct 21, 2026

Copy link
Copy Markdown
Contributor

@YoavLevavi is attempting to deploy a commit to the Cap Software Inc Team on Vercel.

A member of the Team first needs to authorize it.

@richiemcilroy

Copy link
Copy Markdown
Member

Thanks @YoavLevavi, a jumpable history list is a nice touch, and the bookkeeping is solid: labels is truncated, drained and pushed in lockstep with entries, and jump_to keeps the redo tail. Two things before this can go in. 1) open_history_menu and render_history_menu were inserted between render_presets_menu and its doc comment (apps/desktop-gpui/src/editor_window.rs:8773), so that PresetsDropdown.tsx comment now documents open_history_menu. Please move the new functions above that comment. 2) describe_change (apps/desktop-gpui/src/editor_edits.rs:270) labels the first changed key in alphabetical order, but clip deletes ripple every other track (ripple_delete_output_tracks), and audioSegments, camera3dSegments, captionSegments and imageSegments all sort before segments. So deleting a clip with music or an image after it is labelled "Edited audio clip" or "Edited image", and a delete that removes a caption reads "Deleted caption". Could you check segments (and transitions) first, then prefer a key whose count changed over one that only shifted? A test for a clip delete that also shifts an audio segment would cover it. Optional: record now serializes two full configs to JSON on every entry, including in the screenshot editor, which never shows labels. Working out the labels when the menu opens would avoid that cost.

- Labels are worked out when the History menu opens, from consecutive
  snapshots, so record() no longer serializes two configs per entry (the
  screenshot editor never pays for them).
- describe_change looks at clips and transitions first, then prefers a
  track whose segment count changed over one that only shifted, so a clip
  delete that ripples music or drops a caption reads "Deleted clip".
- open_history_menu and render_history_menu sit above the presets menu's doc
  comment again. Merges origin/main (serializes_equal resume check).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@YoavLevavi

Copy link
Copy Markdown
Contributor Author

Thanks @richiemcilroy, both are in 427befa (also merged main), plus the optional one:

  1. open_history_menu and render_history_menu now sit above the presets menu's doc comment.
  2. describe_change checks segments and transitions first, then prefers a track whose segment count changed over one that only shifted. a_clip_delete_is_labelled_as_one_even_when_it_shifts_other_tracks covers a clip delete that shifts music and drops a caption ("Deleted clip"), and a caption delete next to a shifted audio clip ("Deleted caption").
  3. Labels are now worked out only when the menu opens, from consecutive snapshots. record no longer serializes anything, which also fits main's new serializes_equal resume check. When the limit trims the oldest entries, the first label reads "Earlier changes".

@greptileai review

self.history_menu = Some(HistoryMenu {
origin: point(px(x), px(f32::from(anchor.y) + 8.)),
scroll: gpui::ScrollHandle::new(),
labels: self.history.labels(),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 History rows target wrong steps

The menu keeps its original labels and row indices even when keyboard edits change the history. For example, open History, undo, then press C inside a clip. The split replaces the redo branch, but the menu still shows that branch's old labels. Clicking one can restore a different state or do nothing. Close or refresh the menu whenever the history changes.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop-gpui/src/editor_window.rs
Line: 9151

Comment:
**History rows target wrong steps**

The menu keeps its original labels and row indices even when keyboard edits change the history. For example, open History, undo, then press `C` inside a clip. The split replaces the redo branch, but the menu still shows that branch's old labels. Clicking one can restore a different state or do nothing. Close or refresh the menu whenever the history changes.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Comment thread apps/desktop-gpui/src/editor_edits.rs Outdated
Comment on lines +295 to +299
let timeline = |map: &serde_json::Map<String, serde_json::Value>| match map.get("timeline") {
Some(serde_json::Value::Object(timeline)) => timeline.clone(),
_ => empty.clone(),
};
let (before_timeline, after_timeline) = (timeline(&before), timeline(&after));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 History copies timelines unnecessarily

describe_change deep-copies both timeline maps even though it only reads them. Opening History repeats this for up to 99 snapshot pairs, after already turning both projects into JSON trees. Caption-heavy projects therefore copy all timeline captions and words again just to open the menu. Borrow the maps instead.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop-gpui/src/editor_edits.rs
Line: 295-299

Comment:
**History copies timelines unnecessarily**

`describe_change` deep-copies both timeline maps even though it only reads them. Opening History repeats this for up to 99 snapshot pairs, after already turning both projects into JSON trees. Caption-heavy projects therefore copy all timeline captions and words again just to open the menu. Borrow the maps instead.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Comment thread apps/desktop-gpui/src/editor_window.rs Outdated
}
}

/// The History dropdown's width.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Added comments repeat the code

This comment only restates HISTORY_MENU_WIDTH. AGENTS.md explicitly bans comments that narrate code and requires non-obvious context instead. Remove this narration, along with the similar comments on history_menu and jump_to_history. This repository requirement must be satisfied before merging.

Context Used: AGENTS.md (source)

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop-gpui/src/editor_window.rs
Line: 206

Comment:
**Added comments repeat the code**

This comment only restates `HISTORY_MENU_WIDTH`. `AGENTS.md` explicitly bans comments that narrate code and requires non-obvious context instead. Remove this narration, along with the similar comments on `history_menu` and `jump_to_history`. This repository requirement must be satisfied before merging.

**Context Used:** AGENTS.md ([source](https://github.com/capsoftware/cap/blob/main/AGENTS.md))

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment thread apps/desktop-gpui/src/editor_window.rs Outdated
Comment on lines +9145 to +9147
let x = f32::from(anchor.x)
.min(f32::from(viewport.width) - HISTORY_MENU_WIDTH - 12.)
.max(12.);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Bounds use the prohibited form

The new position calculation uses .min(...).max(12.), which AGENTS.md explicitly forbids in favor of clamp. Use clamp and ensure its upper bound is at least 12. so very narrow windows remain safe. This repository requirement must be satisfied before merging.

Suggested change
let x = f32::from(anchor.x)
.min(f32::from(viewport.width) - HISTORY_MENU_WIDTH - 12.)
.max(12.);
let x = f32::from(anchor.x).clamp(
12.,
(f32::from(viewport.width) - HISTORY_MENU_WIDTH - 12.).max(12.),
);

Context Used: AGENTS.md (source)

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop-gpui/src/editor_window.rs
Line: 9145-9147

Comment:
**Bounds use the prohibited form**

The new position calculation uses `.min(...).max(12.)`, which `AGENTS.md` explicitly forbids in favor of `clamp`. Use `clamp` and ensure its upper bound is at least `12.` so very narrow windows remain safe. This repository requirement must be satisfied before merging.

```suggestion
        let x = f32::from(anchor.x).clamp(
            12.,
            (f32::from(viewport.width) - HISTORY_MENU_WIDTH - 12.).max(12.),
        );
```

**Context Used:** AGENTS.md ([source](https://github.com/capsoftware/cap/blob/main/AGENTS.md))

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@YoavLevavi

Copy link
Copy Markdown
Contributor Author

Done in dc784ac: removed the narrating comments on HISTORY_MENU_WIDTH, history_menu, jump_to_history, the History menu render and the ProjectHistory cursor/jump methods. The comments that stay explain why (on-demand labels, trimmed history, label priority).

@greptileai review

@richiemcilroy

Copy link
Copy Markdown
Member

Thanks! Can you get Greptile to 5/5? Then we'll take a look.

The open menu kept its labels and row indices after a keyboard edit
replaced the redo branch, so a row could restore a different state. The
history now carries a revision bumped whenever its entries change; a
stale menu closes on the next render and its rows refuse to jump.
describe_change borrows the timeline maps instead of copying them, and
the menu position uses clamp.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@YoavLevavi

Copy link
Copy Markdown
Contributor Author

@greptile-apps review

@richiemcilroy

Copy link
Copy Markdown
Member

Thanks! Can you rebase on main to clear the conflicts and resolve the open review threads? Then we'll take a look.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants