Skip to content

feat(ui): browser-style back/forward history for the center pane (#52) - #53

Merged
samkeen merged 1 commit into
mainfrom
claude/gh-52-fhydl1
Jul 16, 2026
Merged

feat(ui): browser-style back/forward history for the center pane (#52)#53
samkeen merged 1 commit into
mainfrom
claude/gh-52-fhydl1

Conversation

@samkeen

@samkeen samkeen commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Pure frontend, per the issue: no facade, host, or b2-core change. The
history stack tracks the pane's document — notes and resources alike,
regardless of how each was reached — as {kind, path} entries with a
cursor, held as module-locals in main.ts (the editorView pattern).

  • openNote/openResource split into a guard->push wrapper and a load
    core (loadNote/loadResource); back/forward call the core directly.
    The history commit runs at read-success with the canonical path in
    hand, so a wikilink followed by title dedupes against a tree click
    on the same note, a dead target never enters the stack, and rapid
    navigations can't interleave stack updates out of order.
  • push truncates the forward branch (browser model), suppresses
    consecutive duplicates, and caps at 100 entries; in-place content
    updates (save re-read, write report, external reconcile) never push
    because they don't go through the wrappers.
  • back/forward go through the same closeEditor() guard as any
    navigation: flush + leave edit mode, abort on a write conflict. A
    dead target toasts the generic read error and is pruned (by
    identity) so navigation isn't wedged on it.
  • vault switch clears the stack; also clears a lingering resource card
    (switchVault reset current but not currentResource).
  • chrome: Back/Forward icon-btn pair by the brand, disabled at the
    stack's ends via a targeted paintNav (the paintReindex pattern).
  • keyboard: Mod+[ / Mod+] (plus Mod+arrow aliases); suppressed while
    editing (CodeMirror owns Mod-[/] for indent) and arrows yield to
    caret movement in text fields. Mouse back/forward via auxclick.

Verified end-to-end headless (built bundle + mocked Tauri IPC): boot
states, push/truncate/dedupe, resource entries, keyboard chords and
their text-entry carve-outs, dead-target pruning, edit-mode flush and
conflict-abort — 34/34 checks green; tsc + vite build clean.

Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01EUB5nCNA67G9tMGNozezN3

Summary by CodeRabbit

  • New Features

    • Added Back and Forward buttons for navigating recently opened notes and resources.
    • Added keyboard shortcuts and mouse back/forward support.
    • Navigation history now updates automatically and resets when switching vaults.
  • Bug Fixes

    • Prevented navigation shortcuts from interfering while editing or entering text.
    • Failed navigation targets are removed so history remains usable.

Pure frontend, per the issue: no facade, host, or b2-core change. The
history stack tracks the pane's document — notes and resources alike,
regardless of how each was reached — as {kind, path} entries with a
cursor, held as module-locals in main.ts (the editorView pattern).

- openNote/openResource split into a guard->push wrapper and a load
  core (loadNote/loadResource); back/forward call the core directly.
  The history commit runs at read-success with the canonical path in
  hand, so a wikilink followed by title dedupes against a tree click
  on the same note, a dead target never enters the stack, and rapid
  navigations can't interleave stack updates out of order.
- push truncates the forward branch (browser model), suppresses
  consecutive duplicates, and caps at 100 entries; in-place content
  updates (save re-read, write report, external reconcile) never push
  because they don't go through the wrappers.
- back/forward go through the same closeEditor() guard as any
  navigation: flush + leave edit mode, abort on a write conflict. A
  dead target toasts the generic read error and is pruned (by
  identity) so navigation isn't wedged on it.
- vault switch clears the stack; also clears a lingering resource card
  (switchVault reset current but not currentResource).
- chrome: Back/Forward icon-btn pair by the brand, disabled at the
  stack's ends via a targeted paintNav (the paintReindex pattern).
- keyboard: Mod+[ / Mod+] (plus Mod+arrow aliases); suppressed while
  editing (CodeMirror owns Mod-[/] for indent) and arrows yield to
  caret movement in text fields. Mouse back/forward via auxclick.

Verified end-to-end headless (built bundle + mocked Tauri IPC): boot
states, push/truncate/dedupe, resource entries, keyboard chords and
their text-entry carve-outs, dead-target pruning, edit-mode flush and
conflict-abort — 34/34 checks green; tsc + vite build clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EUB5nCNA67G9tMGNozezN3
@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The UI adds shared note/resource loading, bounded center-pane navigation history, back/forward controls, keyboard and mouse navigation inputs, edit-mode guards, failed-target pruning, and vault-switch history clearing.

Changes

Center-pane navigation

Layer / File(s) Summary
Shared note and resource loaders
ui/src/main.ts
Note and resource loading share canonical-path commits, ancestor expansion, loading state updates, and boolean success results.
History state and vault lifecycle
ui/src/main.ts
A bounded stack tracks navigation entries, updates button state, guards editing, prunes failed targets, and clears when switching vaults.
Navigation controls and input wiring
ui/src/main.ts, ui/style.css
Back/forward buttons, styling, delegated clicks, keyboard shortcuts, and mouse auxiliary clicks invoke navigation.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related issues

  • AlteredCraft/B2 issue 52 — This PR implements browser-style center-pane back/forward history and its associated controls and guards.

Possibly related PRs

  • AlteredCraft/B2#11 — It overlaps the note-opening flow, ancestor expansion, and navigation-related shell wiring in ui/src/main.ts.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant NavigationControls
  participant navGo
  participant loadNote
  participant API
  User->>NavigationControls: click, shortcut, or mouse navigation
  NavigationControls->>navGo: navigate by delta
  navGo->>loadNote: load note history entry
  loadNote->>API: api.readNote(ref)
  API-->>loadNote: note data or error
  loadNote-->>navGo: success or failure
  navGo-->>NavigationControls: update history state
Loading

Suggested reviewers: claude

Poem

A rabbit hops back through the stack,
Then forward where the notes turn black.
Buttons gleam and shortcuts sing,
Failed paths fall off with a spring.
Vaults change; old trails disappear—
Navigation now is crisp and clear.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: browser-style back/forward history for the center pane.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/gh-52-fhydl1

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@samkeen
samkeen merged commit 8a78d1c into main Jul 16, 2026
1 check was pending
@samkeen
samkeen deleted the claude/gh-52-fhydl1 branch July 16, 2026 03:52

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@ui/src/main.ts`:
- Around line 1505-1512: Update the auxclick handler to apply the same
state.settingsOpen || state.linkTarget guard used by the keyboard navigation
handler before calling navGo. Keep preventing the back/forward event and
preserve existing navigation behavior when neither modal state is active.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: dfabc648-109b-49b5-911f-3c0db9fd3f85

📥 Commits

Reviewing files that changed from the base of the PR and between 5510f83 and 85e58b0.

📒 Files selected for processing (2)
  • ui/src/main.ts
  • ui/style.css

Comment thread ui/src/main.ts
Comment on lines +1505 to 1512
// Mouse back/forward buttons (W3C numbering: 3 back, 4 forward) walk the history
// too. `auxclick` fires only for non-primary buttons, so this never doubles the
// click delegation above.
document.addEventListener("auxclick", (e) => {
if (e.button !== 3 && e.button !== 4) return;
e.preventDefault();
void navGo(e.button === 3 ? -1 : 1);
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Mouse back/forward navigation skips the modal guards the keyboard path enforces.

The keyboard handler explicitly checks state.settingsOpen || state.linkTarget before calling navGo (Line 1495), since keyboard events bypass modal-backdrop hit-testing. auxclick has the exact same issue — it doesn't go through DOM click delegation either, so a physical mouse back/forward button press will still call navGo and swap the underlying pane while a settings or link modal is open, leaving the user looking at stale modal content over a changed document once it's closed.

🖱️ Proposed fix
   document.addEventListener("auxclick", (e) => {
     if (e.button !== 3 && e.button !== 4) return;
+    if (state.settingsOpen || state.linkTarget) return;
     e.preventDefault();
     void navGo(e.button === 3 ? -1 : 1);
   });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Mouse back/forward buttons (W3C numbering: 3 back, 4 forward) walk the history
// too. `auxclick` fires only for non-primary buttons, so this never doubles the
// click delegation above.
document.addEventListener("auxclick", (e) => {
if (e.button !== 3 && e.button !== 4) return;
e.preventDefault();
void navGo(e.button === 3 ? -1 : 1);
});
// Mouse back/forward buttons (W3C numbering: 3 back, 4 forward) walk the history
// too. `auxclick` fires only for non-primary buttons, so this never doubles the
// click delegation above.
document.addEventListener("auxclick", (e) => {
if (e.button !== 3 && e.button !== 4) return;
if (state.settingsOpen || state.linkTarget) return;
e.preventDefault();
void navGo(e.button === 3 ? -1 : 1);
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ui/src/main.ts` around lines 1505 - 1512, Update the auxclick handler to
apply the same state.settingsOpen || state.linkTarget guard used by the keyboard
navigation handler before calling navGo. Keep preventing the back/forward event
and preserve existing navigation behavior when neither modal state is active.

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