Skip to content

Conversation

@saddlepaddle
Copy link
Collaborator

@saddlepaddle saddlepaddle commented Oct 7, 2025

  • WIP
  • WIP

Description

Related Issues

Type of Change

  • Bug fix
  • New feature
  • Documentation
  • Refactor
  • Other (please describe):

Testing

Screenshots (if applicable)

Additional Notes


Important

This PR adds multi-branch revert functionality, refactors versioning to be branch-based, and updates the database schema for branch-specific checkpoints.

  • New Features
    • Adds MultiBranchRevertModal in multi-branch-revert-modal.tsx for reverting multiple branches with UI support for selecting branches.
    • Updates user-message.tsx to handle legacy and multi-branch checkpoints with a dropdown menu for branch selection.
    • Implements createCheckpointsForAllBranches in utils.ts to create checkpoints for all branches.
  • Refactor
    • Removes VersionsManager and integrates versioning into GitManager in git.ts.
    • Updates engine.ts to remove versions and use branch-based versioning.
  • Database
    • Modifies message.ts to include branchId in checkpoints and updates the updateCheckpoints mutation.
  • Misc
    • Adds restoreCheckpoint function in git/utils.ts for restoring branches to specific checkpoints.
    • Updates UI components in versions.tsx and version-row.tsx to reflect new branch-based versioning.

This description was created by Ellipsis for 33cdbc8. You can customize this summary. It will automatically update as commits are pushed.


Summary by CodeRabbit

  • New Features

    • Multi-branch revert modal and per-branch restore UI in chat (dropdown + bulk restore) with success/error toasts.
    • Branch-aware Versions UI: branch selector, per-branch commits, and first-backup creation flow.
  • Refactor

    • Versioning moved from legacy manager to branch-based git manager; saved-versions views and older version manager removed.
  • Other

    • Checkpoints now include optional branchId to support multi-branch workflows.

@supabase
Copy link

supabase bot commented Oct 7, 2025

This pull request has been ignored for the connected project wowaemfasoptxrdjhilu because there are no changes detected in apps/backend/supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@vercel
Copy link

vercel bot commented Oct 7, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
web Ready Ready Preview Comment Oct 8, 2025 5:12pm
1 Skipped Deployment
Project Deployment Preview Comments Updated (UTC)
docs Skipped Skipped Oct 8, 2025 5:12pm

@coderabbitai
Copy link

coderabbitai bot commented Oct 7, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds branch-aware Git checkpoints and multi-branch restore flows: creates per-branch checkpoints from chat, persists branchId in checkpoint schema, replaces VersionsManager with a Sandbox-backed GitManager, adds a MultiBranchRevertModal UI, and refactors versions UI to operate per-branch.

Changes

Cohort / File(s) Summary of Changes
Chat multi-branch UI & modal
apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/user-message.tsx, apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/multi-branch-revert-modal.tsx
Adds MultiBranchRevertModal and multi-branch restore UI in user-message: per-branch restore handlers, dropdown + modal flows, selection controls, toasts for per-branch outcomes, and modal state wiring.
Chat hook — checkpoint creation
apps/web/client/src/app/project/[id]/_hooks/use-chat/index.tsx, apps/web/client/src/app/project/[id]/_hooks/use-chat/utils.ts
Replaces single-commit flow with createCheckpointsForAllBranches: create per-branch commits/checkpoints, aggregate checkpoints, return/persist them via API; removes attachCommitToUserMessage and adjusts hook flows to use checkpoints array.
Git manager, utils & sandbox integration
apps/web/client/src/components/store/editor/git/git.ts, apps/web/client/src/components/store/editor/git/utils.ts, apps/web/client/src/components/store/editor/git/index.ts, apps/web/client/src/components/store/editor/sandbox/index.ts
Introduces Sandbox-backed GitManager (observable commits cache, init/list/create/restore/addNote APIs), adds restoreCheckpoint utility, integrates gitManager into SandboxManager init, updates runCommand and command/parse flows.
Versions manager removal & engine change
apps/web/client/src/components/store/editor/version/index.ts, apps/web/client/src/components/store/editor/engine.ts
Removes VersionsManager module and the EditorEngine.versions field; deletes prior versioning state/logic.
Versions UI refactor
apps/web/client/src/components/ui/settings-modal/versions/versions.tsx, apps/web/client/src/components/ui/settings-modal/versions/empty-state/version.tsx, apps/web/client/src/components/ui/settings-modal/versions/index.tsx, apps/web/client/src/components/ui/settings-modal/versions/saved-version.tsx, apps/web/client/src/components/ui/settings-modal/versions/empty-state/saved.tsx, apps/web/client/src/components/ui/settings-modal/versions/version-row.tsx
Refactors Versions UI to use per-branch gitManager: adds branch selector, uses gitManager.createCommit/listCommits/restore/addNote, removes SavedVersions/NoSavedVersions components, updates rename/checkout flows, and expands VersionRowType.
Types & server schema
packages/models/src/chat/message/checkpoint.ts, apps/web/client/src/server/api/routers/chat/message.ts
Adds optional branchId?: string to GitMessageCheckpoint and extends server updateCheckpoints input schema to require branchId for each checkpoint entry.
Exports / index barrel
apps/web/client/src/components/store/editor/git/index.ts
Adds re-exports for ./git and ./utils to centralize git-related imports.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor U as User
  participant UM as UserMessage UI
  participant MR as MultiBranchRevertModal
  participant SD as SandboxManager
  participant GM as GitManager
  participant EE as EditorEngine

  U->>UM: open restore options
  UM->>UM: render single vs multi-checkpoint UI
  U->>MR: open multi-branch modal (passes checkpoints)
  MR->>SD: request branch data for each selected checkpoint
  SD->>GM: gitManager.createCommit(backup)  -- create backup per branch
  GM-->>SD: backup commit oid
  MR->>GM: restoreToCommit(checkpoint.oid)
  GM-->>MR: result (success / error)
  MR-->>U: toast summary and close modal
Loading
sequenceDiagram
  autonumber
  actor U as User
  participant CH as useChat Hook
  participant SD as SandboxManager
  participant GM as GitManager
  participant API as chat.message.updateCheckpoints

  U->>CH: send chat message
  CH->>SD: enumerate sandbox branches
  loop per branch
    SD->>GM: createCommit("New Onlook backup")
    GM-->>CH: { oid, createdAt, branchId }
  end
  CH->>API: updateCheckpoints([{ oid, branchId, createdAt }, ...])
  API-->>CH: ack
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

A hop, a nibble, across each branch I bound,
I stash small backups where new OIDs are found.
Checkpoints lined in rows, each branch a little art,
I open the modal, pick restores — then start.
Rabbit hops, restores branches — tidy heart. 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The pull request description contains only WIP markers and unfilled placeholders for all required template sections, including Description, Related Issues, Type of Change, Testing, Screenshots, and Additional Notes, and the auto-generated summary is not incorporated into the template. As a result, the author has not provided any substantive details about the changes, affected issues, or verification steps. This leaves the PR description largely incomplete relative to the repository’s required format. Please complete the PR description by replacing the WIP placeholders with a clear and concise summary of your changes, link any related issues, select the appropriate type-of-change checkboxes, describe the tests you ran, and add any relevant screenshots or additional context according to the repository’s template.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title succinctly conveys the primary feature of enabling arbitrary branch reverts and aligns with the multi-branch revert functionality introduced in the PR.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix-reverting-multiple-branches

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 and usage tips.

Comment on lines +130 to +132
onOpenChange(false);
setSelectedBranchIds([]);
}}
Copy link
Contributor

Choose a reason for hiding this comment

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

The isRestoring state should be reset when the modal is closed via the Cancel button. Consider adding setIsRestoring(false) to ensure the state is properly cleaned up, preventing potential issues if the modal is reopened later.

onClick={() => {
  onOpenChange(false);
  setSelectedBranchIds([]);
  setIsRestoring(false);
}}
Suggested change
onOpenChange(false);
setSelectedBranchIds([]);
}}
onOpenChange(false);
setSelectedBranchIds([]);
setIsRestoring(false);
}}

Spotted by Diamond

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

setFinishReason(null);

const applyCommit = async () => {
console.log('[CHECKPOINT] applyCommit started');
Copy link
Contributor

Choose a reason for hiding this comment

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

There are multiple console.log statements (e.g. '[CHECKPOINT] ...') used for debugging. Consider removing or replacing them with a proper logging mechanism before production deployment.

Suggested change
console.log('[CHECKPOINT] applyCommit started');

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

♻️ Duplicate comments (2)
apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/multi-branch-revert-modal.tsx (1)

128-133: Reset isRestoring when closing the modal.

If the modal is closed (Cancel button, ESC, or backdrop) while a restore is in progress, isRestoring stays true. When reopened the primary action stays disabled and reads “Restoring…”. Reset isRestoring to false along with clearing the selection when the modal closes.

-                        onClick={() => {
-                            onOpenChange(false);
-                            setSelectedBranchIds([]);
-                        }}
+                        onClick={() => {
+                            onOpenChange(false);
+                            setSelectedBranchIds([]);
+                            setIsRestoring(false);
+                        }}
apps/web/client/src/app/project/[id]/_hooks/use-chat/index.tsx (1)

228-333: Remove the temporary checkpoint console logging

The earlier review already called out the debug console.log/console.warn statements sprinkled through the checkpoint flow. Please strip them before merge (or move to a proper logger if we need structured traces).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4639480 and 806ea44.

📒 Files selected for processing (16)
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/multi-branch-revert-modal.tsx (1 hunks)
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/user-message.tsx (6 hunks)
  • apps/web/client/src/app/project/[id]/_hooks/use-chat/index.tsx (3 hunks)
  • apps/web/client/src/app/project/[id]/_hooks/use-chat/utils.ts (1 hunks)
  • apps/web/client/src/components/store/editor/engine.ts (0 hunks)
  • apps/web/client/src/components/store/editor/sandbox/index.ts (2 hunks)
  • apps/web/client/src/components/store/editor/version/git.ts (8 hunks)
  • apps/web/client/src/components/store/editor/version/index.ts (0 hunks)
  • apps/web/client/src/components/ui/settings-modal/versions/empty-state/saved.tsx (0 hunks)
  • apps/web/client/src/components/ui/settings-modal/versions/empty-state/version.tsx (2 hunks)
  • apps/web/client/src/components/ui/settings-modal/versions/index.tsx (0 hunks)
  • apps/web/client/src/components/ui/settings-modal/versions/saved-version.tsx (0 hunks)
  • apps/web/client/src/components/ui/settings-modal/versions/version-row.tsx (7 hunks)
  • apps/web/client/src/components/ui/settings-modal/versions/versions.tsx (4 hunks)
  • apps/web/client/src/server/api/routers/chat/message.ts (1 hunks)
  • packages/models/src/chat/message/checkpoint.ts (1 hunks)
💤 Files with no reviewable changes (5)
  • apps/web/client/src/components/store/editor/engine.ts
  • apps/web/client/src/components/ui/settings-modal/versions/saved-version.tsx
  • apps/web/client/src/components/ui/settings-modal/versions/empty-state/saved.tsx
  • apps/web/client/src/components/ui/settings-modal/versions/index.tsx
  • apps/web/client/src/components/store/editor/version/index.ts
🧰 Additional context used
📓 Path-based instructions (7)
apps/web/client/src/server/api/routers/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

apps/web/client/src/server/api/routers/**/*.ts: Place tRPC routers under apps/web/client/src/server/api/routers/**
Use publicProcedure/protectedProcedure from apps/web/client/src/server/api/trpc.ts and validate inputs with Zod
Return plain objects/arrays; rely on SuperJSON for serialization in tRPC procedures

apps/web/client/src/server/api/routers/**/*.ts: Place tRPC routers under src/server/api/routers/**
Use publicProcedure/protectedProcedure from src/server/api/trpc.ts and validate inputs with Zod
Return plain objects/arrays; rely on SuperJSON for serialization

Files:

  • apps/web/client/src/server/api/routers/chat/message.ts
apps/web/client/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

apps/web/client/src/**/*.{ts,tsx}: Use path aliases @/* and ~/* for imports that map to apps/web/client/src/*
Avoid hardcoded user-facing text; use next-intl messages/hooks instead

Use path aliases @/* and ~/* for imports mapping to src/*

Files:

  • apps/web/client/src/server/api/routers/chat/message.ts
  • apps/web/client/src/app/project/[id]/_hooks/use-chat/index.tsx
  • apps/web/client/src/components/ui/settings-modal/versions/versions.tsx
  • apps/web/client/src/components/ui/settings-modal/versions/empty-state/version.tsx
  • apps/web/client/src/components/ui/settings-modal/versions/version-row.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/user-message.tsx
  • apps/web/client/src/app/project/[id]/_hooks/use-chat/utils.ts
  • apps/web/client/src/components/store/editor/sandbox/index.ts
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/multi-branch-revert-modal.tsx
  • apps/web/client/src/components/store/editor/version/git.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Do not use the any type unless necessary

Files:

  • apps/web/client/src/server/api/routers/chat/message.ts
  • packages/models/src/chat/message/checkpoint.ts
  • apps/web/client/src/app/project/[id]/_hooks/use-chat/index.tsx
  • apps/web/client/src/components/ui/settings-modal/versions/versions.tsx
  • apps/web/client/src/components/ui/settings-modal/versions/empty-state/version.tsx
  • apps/web/client/src/components/ui/settings-modal/versions/version-row.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/user-message.tsx
  • apps/web/client/src/app/project/[id]/_hooks/use-chat/utils.ts
  • apps/web/client/src/components/store/editor/sandbox/index.ts
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/multi-branch-revert-modal.tsx
  • apps/web/client/src/components/store/editor/version/git.ts
{apps,packages}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Avoid using the any type unless absolutely necessary

Files:

  • apps/web/client/src/server/api/routers/chat/message.ts
  • packages/models/src/chat/message/checkpoint.ts
  • apps/web/client/src/app/project/[id]/_hooks/use-chat/index.tsx
  • apps/web/client/src/components/ui/settings-modal/versions/versions.tsx
  • apps/web/client/src/components/ui/settings-modal/versions/empty-state/version.tsx
  • apps/web/client/src/components/ui/settings-modal/versions/version-row.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/user-message.tsx
  • apps/web/client/src/app/project/[id]/_hooks/use-chat/utils.ts
  • apps/web/client/src/components/store/editor/sandbox/index.ts
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/multi-branch-revert-modal.tsx
  • apps/web/client/src/components/store/editor/version/git.ts
apps/web/client/src/app/**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

apps/web/client/src/app/**/*.tsx: Default to Server Components; add 'use client' when using events, state/effects, browser APIs, or client‑only libraries
Do not use process.env in client code; import env from @/env instead

Avoid hardcoded user-facing text; use next-intl messages/hooks

Files:

  • apps/web/client/src/app/project/[id]/_hooks/use-chat/index.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/user-message.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/multi-branch-revert-modal.tsx
apps/web/client/src/**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

apps/web/client/src/**/*.tsx: Create MobX store instances with useState(() => new Store()) for stable references across renders
Keep the active MobX store in a useRef and perform async cleanup with setTimeout(() => storeRef.current?.clear(), 0) to avoid route-change races
Avoid useMemo for creating MobX store instances
Avoid putting the MobX store instance in effect dependency arrays if it causes loops; split concerns by domain

apps/web/client/src/**/*.tsx: Create MobX store instances with useState(() => new Store()) for stable identities across renders
Keep the active MobX store in a useRef and clean up asynchronously with setTimeout(() => storeRef.current?.clear(), 0)
Do not use useMemo to create MobX stores
Avoid placing MobX store instances in effect dependency arrays if it causes loops; split concerns instead
observer components must be client components; place a single client boundary at the feature entry; child observers need not repeat 'use client'

Files:

  • apps/web/client/src/app/project/[id]/_hooks/use-chat/index.tsx
  • apps/web/client/src/components/ui/settings-modal/versions/versions.tsx
  • apps/web/client/src/components/ui/settings-modal/versions/empty-state/version.tsx
  • apps/web/client/src/components/ui/settings-modal/versions/version-row.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/user-message.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/multi-branch-revert-modal.tsx
apps/web/client/src/app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Default to Server Components; add 'use client' only when using events, state/effects, browser APIs, or client-only libs

Files:

  • apps/web/client/src/app/project/[id]/_hooks/use-chat/index.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/user-message.tsx
  • apps/web/client/src/app/project/[id]/_hooks/use-chat/utils.ts
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/multi-branch-revert-modal.tsx
🧬 Code graph analysis (8)
apps/web/client/src/app/project/[id]/_hooks/use-chat/index.tsx (2)
packages/git/src/git.ts (2)
  • branch (116-123)
  • status (89-91)
apps/web/client/src/trpc/react.tsx (1)
  • api (23-23)
apps/web/client/src/components/ui/settings-modal/versions/versions.tsx (2)
packages/ui/src/components/select.tsx (5)
  • Select (158-158)
  • SelectTrigger (166-166)
  • SelectValue (167-167)
  • SelectContent (159-159)
  • SelectItem (161-161)
packages/git/src/git.ts (1)
  • branch (116-123)
apps/web/client/src/components/ui/settings-modal/versions/empty-state/version.tsx (2)
apps/web/client/src/components/store/editor/index.tsx (1)
  • useEditorEngine (10-14)
packages/ui/src/components/sonner.tsx (1)
  • toast (19-19)
apps/web/client/src/components/ui/settings-modal/versions/version-row.tsx (2)
packages/git/src/git.ts (1)
  • updateCommitDisplayName (159-169)
apps/web/client/src/components/store/editor/version/git.ts (1)
  • commit (169-177)
apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/user-message.tsx (2)
packages/models/src/chat/message/checkpoint.ts (1)
  • GitMessageCheckpoint (10-14)
apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/multi-branch-revert-modal.tsx (1)
  • MultiBranchRevertModal (18-150)
apps/web/client/src/components/store/editor/sandbox/index.ts (2)
apps/web/client/src/components/store/editor/sandbox/session.ts (1)
  • SessionManager (8-225)
apps/web/client/src/components/store/editor/version/git.ts (1)
  • GitManager (22-412)
apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/multi-branch-revert-modal.tsx (2)
packages/models/src/chat/message/checkpoint.ts (1)
  • GitMessageCheckpoint (10-14)
apps/web/client/src/components/store/editor/index.tsx (1)
  • useEditorEngine (10-14)
apps/web/client/src/components/store/editor/version/git.ts (3)
packages/git/src/git.ts (1)
  • GitCommit (19-25)
apps/web/client/src/components/store/editor/sandbox/index.ts (1)
  • SandboxManager (15-209)
apps/web/client/src/utils/git/index.ts (2)
  • sanitizeCommitMessage (7-47)
  • prepareCommitMessage (72-75)

@vercel vercel bot temporarily deployed to Preview – docs October 7, 2025 23:35 Inactive
disabled={isRestoring || selectedBranchIds.length === 0}
className="order-1 sm:order-2"
>
{isRestoring ? 'Restoring...' : 'Revert Selected'}
Copy link
Contributor

Choose a reason for hiding this comment

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

UI text inconsistency: Update the button label from 'Revert Selected' to 'Restore Selected' to match the modal title.

Suggested change
{isRestoring ? 'Restoring...' : 'Revert Selected'}
{isRestoring ? 'Restoring...' : 'Restore Selected'}

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 806ea44 and a829cf4.

📒 Files selected for processing (7)
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/multi-branch-revert-modal.tsx (1 hunks)
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/user-message.tsx (7 hunks)
  • apps/web/client/src/app/project/[id]/_hooks/use-chat/index.tsx (2 hunks)
  • apps/web/client/src/components/store/editor/git/git.ts (8 hunks)
  • apps/web/client/src/components/store/editor/git/index.ts (1 hunks)
  • apps/web/client/src/components/store/editor/git/utils.ts (1 hunks)
  • apps/web/client/src/components/store/editor/sandbox/index.ts (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • apps/web/client/src/components/store/editor/git/index.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/multi-branch-revert-modal.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/user-message.tsx
🧰 Additional context used
📓 Path-based instructions (6)
apps/web/client/src/app/**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

apps/web/client/src/app/**/*.tsx: Default to Server Components; add 'use client' when using events, state/effects, browser APIs, or client‑only libraries
Do not use process.env in client code; import env from @/env instead

Avoid hardcoded user-facing text; use next-intl messages/hooks

Files:

  • apps/web/client/src/app/project/[id]/_hooks/use-chat/index.tsx
apps/web/client/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

apps/web/client/src/**/*.{ts,tsx}: Use path aliases @/* and ~/* for imports that map to apps/web/client/src/*
Avoid hardcoded user-facing text; use next-intl messages/hooks instead

Use path aliases @/* and ~/* for imports mapping to src/*

Files:

  • apps/web/client/src/app/project/[id]/_hooks/use-chat/index.tsx
  • apps/web/client/src/components/store/editor/git/git.ts
  • apps/web/client/src/components/store/editor/sandbox/index.ts
  • apps/web/client/src/components/store/editor/git/utils.ts
apps/web/client/src/**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

apps/web/client/src/**/*.tsx: Create MobX store instances with useState(() => new Store()) for stable references across renders
Keep the active MobX store in a useRef and perform async cleanup with setTimeout(() => storeRef.current?.clear(), 0) to avoid route-change races
Avoid useMemo for creating MobX store instances
Avoid putting the MobX store instance in effect dependency arrays if it causes loops; split concerns by domain

apps/web/client/src/**/*.tsx: Create MobX store instances with useState(() => new Store()) for stable identities across renders
Keep the active MobX store in a useRef and clean up asynchronously with setTimeout(() => storeRef.current?.clear(), 0)
Do not use useMemo to create MobX stores
Avoid placing MobX store instances in effect dependency arrays if it causes loops; split concerns instead
observer components must be client components; place a single client boundary at the feature entry; child observers need not repeat 'use client'

Files:

  • apps/web/client/src/app/project/[id]/_hooks/use-chat/index.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Do not use the any type unless necessary

Files:

  • apps/web/client/src/app/project/[id]/_hooks/use-chat/index.tsx
  • apps/web/client/src/components/store/editor/git/git.ts
  • apps/web/client/src/components/store/editor/sandbox/index.ts
  • apps/web/client/src/components/store/editor/git/utils.ts
apps/web/client/src/app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Default to Server Components; add 'use client' only when using events, state/effects, browser APIs, or client-only libs

Files:

  • apps/web/client/src/app/project/[id]/_hooks/use-chat/index.tsx
{apps,packages}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Avoid using the any type unless absolutely necessary

Files:

  • apps/web/client/src/app/project/[id]/_hooks/use-chat/index.tsx
  • apps/web/client/src/components/store/editor/git/git.ts
  • apps/web/client/src/components/store/editor/sandbox/index.ts
  • apps/web/client/src/components/store/editor/git/utils.ts
🧬 Code graph analysis (4)
apps/web/client/src/app/project/[id]/_hooks/use-chat/index.tsx (1)
apps/web/client/src/trpc/react.tsx (1)
  • api (23-23)
apps/web/client/src/components/store/editor/git/git.ts (3)
packages/git/src/git.ts (1)
  • GitCommit (19-25)
apps/web/client/src/components/store/editor/sandbox/index.ts (1)
  • SandboxManager (15-209)
apps/web/client/src/utils/git/index.ts (2)
  • sanitizeCommitMessage (7-47)
  • prepareCommitMessage (72-75)
apps/web/client/src/components/store/editor/sandbox/index.ts (2)
apps/web/client/src/components/store/editor/sandbox/session.ts (1)
  • SessionManager (8-225)
apps/web/client/src/components/store/editor/git/git.ts (1)
  • GitManager (22-412)
apps/web/client/src/components/store/editor/git/utils.ts (2)
packages/models/src/chat/message/checkpoint.ts (1)
  • GitMessageCheckpoint (10-14)
apps/web/client/src/components/store/editor/engine.ts (1)
  • EditorEngine (33-135)

Comment on lines +38 to 43
async (provider) => {
if (provider) {
this.initializeSyncEngine(provider);
await this.initializeSyncEngine(provider);
// Initialize git after provider is ready
await this.gitManager.init();
} else if (this.sync) {
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Handle errors in the async provider reaction.

Turning the reaction effect into an async function means any rejection from initializeSyncEngine or gitManager.init() now escapes without a catch, producing unhandled promise rejections and leaving the sandbox half‑initialized. Wrap the awaited calls in a try/catch (and surface the failure through errorManager or logs) so provider transitions remain resilient.

🤖 Prompt for AI Agents
In apps/web/client/src/components/store/editor/sandbox/index.ts around lines 38
to 43, the async reaction callback can throw from initializeSyncEngine or
gitManager.init causing unhandled promise rejections and leaving the sandbox
half-initialized; wrap the awaited calls in a try/catch, log or report the error
via errorManager (or console/error logger) and ensure you set the sandbox sync
state to a safe/rolled-back value on failure (e.g., clear any partial flags or
set this.sync = false) so provider transitions remain resilient.

@saddlepaddle saddlepaddle changed the title fix reverting multiple branches Feat: Allow for reverting arbitrary branches Oct 8, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

♻️ Duplicate comments (1)
apps/web/client/src/components/ui/settings-modal/versions/version-row.tsx (1)

70-83: Guard activeBranchData before renaming.

editorEngine.branches.activeBranchData can be undefined (e.g. while branches load or after deletion). Accessing .sandbox.gitManager will throw and crash the modal. Bail out early when no active branch data (show a toast + telemetry, then return) before calling addCommitNote.

🧹 Nitpick comments (1)
apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/user-message.tsx (1)

147-147: Consider avoiding negative margin.

The negative margin mt-[-8px] can cause unexpected layout issues and makes the spacing less maintainable. Consider adjusting the parent container's padding or using a more explicit layout approach.

Apply this diff to remove the negative margin:

 <Textarea
     ref={textareaRef}
     value={editValue}
     onChange={(e) => setEditValue(e.target.value)}
-    className="text-small mt-[-8px] resize-none border-none px-0"
+    className="text-small resize-none border-none px-0"
     rows={2}

Then adjust the parent container's padding at line 224 if needed to achieve the desired spacing.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a829cf4 and 31ea4db.

📒 Files selected for processing (7)
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/multi-branch-revert-modal.tsx (1 hunks)
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/user-message.tsx (6 hunks)
  • apps/web/client/src/app/project/[id]/_hooks/use-chat/index.tsx (2 hunks)
  • apps/web/client/src/app/project/[id]/_hooks/use-chat/utils.ts (2 hunks)
  • apps/web/client/src/components/store/editor/git/utils.ts (1 hunks)
  • apps/web/client/src/components/ui/settings-modal/versions/version-row.tsx (7 hunks)
  • packages/models/src/chat/message/checkpoint.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/models/src/chat/message/checkpoint.ts
🧰 Additional context used
📓 Path-based instructions (6)
apps/web/client/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

apps/web/client/src/**/*.{ts,tsx}: Use path aliases @/* and ~/* for imports that map to apps/web/client/src/*
Avoid hardcoded user-facing text; use next-intl messages/hooks instead

Use path aliases @/* and ~/* for imports mapping to src/*

Files:

  • apps/web/client/src/app/project/[id]/_hooks/use-chat/utils.ts
  • apps/web/client/src/components/store/editor/git/utils.ts
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/user-message.tsx
  • apps/web/client/src/app/project/[id]/_hooks/use-chat/index.tsx
  • apps/web/client/src/components/ui/settings-modal/versions/version-row.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/multi-branch-revert-modal.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Do not use the any type unless necessary

Files:

  • apps/web/client/src/app/project/[id]/_hooks/use-chat/utils.ts
  • apps/web/client/src/components/store/editor/git/utils.ts
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/user-message.tsx
  • apps/web/client/src/app/project/[id]/_hooks/use-chat/index.tsx
  • apps/web/client/src/components/ui/settings-modal/versions/version-row.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/multi-branch-revert-modal.tsx
apps/web/client/src/app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Default to Server Components; add 'use client' only when using events, state/effects, browser APIs, or client-only libs

Files:

  • apps/web/client/src/app/project/[id]/_hooks/use-chat/utils.ts
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/user-message.tsx
  • apps/web/client/src/app/project/[id]/_hooks/use-chat/index.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/multi-branch-revert-modal.tsx
{apps,packages}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Avoid using the any type unless absolutely necessary

Files:

  • apps/web/client/src/app/project/[id]/_hooks/use-chat/utils.ts
  • apps/web/client/src/components/store/editor/git/utils.ts
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/user-message.tsx
  • apps/web/client/src/app/project/[id]/_hooks/use-chat/index.tsx
  • apps/web/client/src/components/ui/settings-modal/versions/version-row.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/multi-branch-revert-modal.tsx
apps/web/client/src/app/**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

apps/web/client/src/app/**/*.tsx: Default to Server Components; add 'use client' when using events, state/effects, browser APIs, or client‑only libraries
Do not use process.env in client code; import env from @/env instead

Avoid hardcoded user-facing text; use next-intl messages/hooks

Files:

  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/user-message.tsx
  • apps/web/client/src/app/project/[id]/_hooks/use-chat/index.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/multi-branch-revert-modal.tsx
apps/web/client/src/**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

apps/web/client/src/**/*.tsx: Create MobX store instances with useState(() => new Store()) for stable references across renders
Keep the active MobX store in a useRef and perform async cleanup with setTimeout(() => storeRef.current?.clear(), 0) to avoid route-change races
Avoid useMemo for creating MobX store instances
Avoid putting the MobX store instance in effect dependency arrays if it causes loops; split concerns by domain

apps/web/client/src/**/*.tsx: Create MobX store instances with useState(() => new Store()) for stable identities across renders
Keep the active MobX store in a useRef and clean up asynchronously with setTimeout(() => storeRef.current?.clear(), 0)
Do not use useMemo to create MobX stores
Avoid placing MobX store instances in effect dependency arrays if it causes loops; split concerns instead
observer components must be client components; place a single client boundary at the feature entry; child observers need not repeat 'use client'

Files:

  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/user-message.tsx
  • apps/web/client/src/app/project/[id]/_hooks/use-chat/index.tsx
  • apps/web/client/src/components/ui/settings-modal/versions/version-row.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/multi-branch-revert-modal.tsx
🧠 Learnings (3)
📚 Learning: 2025-09-14T01:44:21.209Z
Learnt from: CR
PR: onlook-dev/onlook#0
File: AGENTS.md:0-0
Timestamp: 2025-09-14T01:44:21.209Z
Learning: Applies to apps/web/client/src/**/*.{ts,tsx} : Avoid hardcoded user-facing text; use next-intl messages/hooks instead

Applied to files:

  • apps/web/client/src/components/store/editor/git/utils.ts
📚 Learning: 2025-09-16T19:22:52.461Z
Learnt from: CR
PR: onlook-dev/onlook#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-16T19:22:52.461Z
Learning: Applies to apps/web/client/src/app/**/*.tsx : Avoid hardcoded user-facing text; use next-intl messages/hooks

Applied to files:

  • apps/web/client/src/components/store/editor/git/utils.ts
📚 Learning: 2025-09-14T01:44:21.209Z
Learnt from: CR
PR: onlook-dev/onlook#0
File: AGENTS.md:0-0
Timestamp: 2025-09-14T01:44:21.209Z
Learning: Applies to apps/web/client/messages/** : Add or modify internationalized strings in apps/web/client/messages/* instead of hardcoding text

Applied to files:

  • apps/web/client/src/components/store/editor/git/utils.ts
🧬 Code graph analysis (6)
apps/web/client/src/app/project/[id]/_hooks/use-chat/utils.ts (2)
apps/web/client/src/components/store/editor/engine.ts (1)
  • EditorEngine (33-135)
packages/models/src/chat/message/checkpoint.ts (1)
  • GitMessageCheckpoint (10-14)
apps/web/client/src/components/store/editor/git/utils.ts (2)
packages/models/src/chat/message/checkpoint.ts (1)
  • GitMessageCheckpoint (10-14)
apps/web/client/src/components/store/editor/engine.ts (1)
  • EditorEngine (33-135)
apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/user-message.tsx (4)
apps/web/client/src/app/project/[id]/_hooks/use-chat/index.tsx (1)
  • EditMessage (19-23)
packages/models/src/chat/message/checkpoint.ts (1)
  • GitMessageCheckpoint (10-14)
apps/web/client/src/components/store/editor/git/utils.ts (1)
  • restoreCheckpoint (19-60)
apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/multi-branch-revert-modal.tsx (1)
  • MultiBranchRevertModal (28-176)
apps/web/client/src/app/project/[id]/_hooks/use-chat/index.tsx (3)
apps/web/client/src/app/project/[id]/_hooks/use-chat/utils.ts (1)
  • createCheckpointsForAllBranches (36-65)
packages/models/src/chat/message/checkpoint.ts (1)
  • GitMessageCheckpoint (10-14)
apps/web/client/src/trpc/react.tsx (1)
  • api (23-23)
apps/web/client/src/components/ui/settings-modal/versions/version-row.tsx (2)
packages/git/src/git.ts (1)
  • updateCommitDisplayName (159-169)
apps/web/client/src/components/store/editor/git/utils.ts (1)
  • restoreCheckpoint (19-60)
apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/multi-branch-revert-modal.tsx (3)
packages/models/src/chat/message/checkpoint.ts (1)
  • GitMessageCheckpoint (10-14)
apps/web/client/src/components/store/editor/index.tsx (1)
  • useEditorEngine (10-14)
apps/web/client/src/components/store/editor/git/utils.ts (1)
  • restoreCheckpoint (19-60)
🔇 Additional comments (5)
apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/user-message.tsx (5)

33-42: LGTM!

The refactored getUserMessageContent function is cleaner and more functional in style. The chained map/join approach is more concise than the previous implementation.


54-59: LGTM!

The checkpoint extraction and legacy detection logic is clear and correct. The approach of falling back to legacy UI when any checkpoint lacks branchId is a safe, conservative strategy that prevents confusion when dealing with mixed checkpoint types.


103-114: Verify intentional inconsistency in toast messages.

The handleRetry function only provides an error message in the toast promise (line 103-106), while sendMessage provides loading, success, and error messages (line 109-114). This inconsistency might be intentional to reduce UI noise for retries, but it's worth confirming.

Is the absence of loading/success feedback in handleRetry intentional? If so, consider adding a comment explaining the reasoning. If not, apply this diff to align the behavior:

 const handleRetry = async () => {
-    toast.promise(onEditMessage(message.id, getUserMessageContent(message), ChatType.EDIT), {
+    toast.promise(onEditMessage(message.id, getUserMessageContent(message), ChatType.EDIT), {
+        loading: 'Resubmitting message...',
+        success: 'Message resubmitted successfully',
         error: 'Failed to resubmit message',
     });
 };

116-138: LGTM!

The restoration handler functions are well-structured:

  • handleRestoreSingleBranch provides per-checkpoint restoration for the modern flow
  • handleRestoreLegacy safely handles legacy checkpoints by falling back to the first checkpoint
  • getBranchName provides appropriate fallbacks for missing or undefined branch data

State management with isRestoring is properly handled to prevent concurrent operations.


1-26: No 'use client' directive required: The parent chat-messages/index.tsx already declares 'use client'.

Comment on lines 77 to 171
toast.success(
`Successfully restored ${successCount} branch${successCount > 1 ? 'es' : ''}`,
{
description:
failCount > 0
? `${failCount} branch${failCount > 1 ? 'es' : ''} failed to restore`
: undefined,
},
);
} else if (failCount > 0) {
toast.error('Failed to restore all selected branches');
}

setIsRestoring(false);
onOpenChange(false);
setSelectedBranchIds([]);
};

return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="max-w-md">
<DialogHeader>
<DialogTitle>Restore Multiple Branches</DialogTitle>
<DialogDescription className="pt-2">
Select the branches you want to restore to their previous state.
</DialogDescription>
</DialogHeader>
<div className="flex flex-col gap-2 py-4">
<div className="mb-1 flex justify-end gap-1">
<Button
variant="outline"
size="sm"
onClick={selectAll}
disabled={isRestoring}
>
Select All
</Button>
<Button
variant="outline"
size="sm"
onClick={selectNone}
disabled={isRestoring}
>
Select None
</Button>
</div>
<div className="flex flex-col gap-2">
{checkpoints.map((checkpoint) => {
// Skip legacy checkpoints without branchId (shouldn't happen in multi-branch modal)
if (!checkpoint.branchId) return null;
const isSelected = selectedBranchIds.includes(checkpoint.branchId);
return (
<button
key={checkpoint.branchId}
onClick={() => toggleBranch(checkpoint.branchId!)}
disabled={isRestoring}
className={cn(
'flex items-center justify-between rounded-md border px-3 py-2.5 text-left transition-all',
'hover:bg-background-secondary/50',
isSelected
? 'border-primary/50 bg-primary/5'
: 'border-border/50',
isRestoring && 'cursor-not-allowed opacity-50',
)}
>
<span className="text-sm">
{editorEngine.branches.getBranchById(checkpoint.branchId)
?.name ?? checkpoint.branchId}
</span>
{isSelected && <Icons.Check className="text-primary h-4 w-4" />}
</button>
);
})}
</div>
</div>
<DialogFooter className="flex-col gap-3 sm:flex-row sm:gap-2">
<Button
variant="outline"
onClick={() => {
onOpenChange(false);
setSelectedBranchIds([]);
}}
disabled={isRestoring}
className="order-2 sm:order-1"
>
Cancel
</Button>
<Button
variant="outline"
onClick={handleRevert}
disabled={isRestoring || selectedBranchIds.length === 0}
className="order-1 sm:order-2"
>
{isRestoring ? 'Restoring...' : 'Revert Selected'}
</Button>
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Localize modal and toast copy.

Every string shown to users here (“Restore Multiple Branches”, “Please select at least one branch…”, button labels, success/failure toasts, etc.) is hardcoded. For files under apps/web/client/src/**, we must serve user text via next-intl message keys. Inject the translator and replace these literals with localized strings.
Based on learnings

Comment on lines +298 to +305
{gitCheckpoints.map((checkpoint) => (
<DropdownMenuItem
key={checkpoint.branchId}
onClick={() => handleRestoreSingleBranch(checkpoint)}
>
{getBranchName(checkpoint.branchId)}
</DropdownMenuItem>
))}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Add type guard for branch checkpoints in modern path.

Although the logic ensures all checkpoints have branchId when hasLegacyCheckpoints is false, TypeScript doesn't infer this. The key at line 300 uses checkpoint.branchId which is typed as potentially undefined, creating a type-safety concern.

Apply this diff to make the type guarantee explicit:

-                                        {gitCheckpoints.map((checkpoint) => (
+                                        {gitCheckpoints
+                                            .filter((cp): cp is GitMessageCheckpoint & { branchId: string } => !!cp.branchId)
+                                            .map((checkpoint) => (
                                             <DropdownMenuItem
                                                 key={checkpoint.branchId}
                                                 onClick={() => handleRestoreSingleBranch(checkpoint)}
                                             >
                                                 {getBranchName(checkpoint.branchId)}
                                             </DropdownMenuItem>
                                         ))}

This ensures TypeScript knows checkpoint.branchId is defined and prevents potential runtime issues if the logic ever changes.

📝 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
{gitCheckpoints.map((checkpoint) => (
<DropdownMenuItem
key={checkpoint.branchId}
onClick={() => handleRestoreSingleBranch(checkpoint)}
>
{getBranchName(checkpoint.branchId)}
</DropdownMenuItem>
))}
{gitCheckpoints
.filter(
(cp): cp is GitMessageCheckpoint & { branchId: string } =>
!!cp.branchId
)
.map((checkpoint) => (
<DropdownMenuItem
key={checkpoint.branchId}
onClick={() =>
handleRestoreSingleBranch(checkpoint)
}
>
{getBranchName(checkpoint.branchId)}
</DropdownMenuItem>
))}

Comment on lines +29 to +57
toast.error('Branch not found');
return { success: false, error: 'Branch not found' };
}

// Save current state before restoring
const saveResult = await branchData.sandbox.gitManager.createCommit(BACKUP_COMMIT_MESSAGE);
if (!saveResult.success) {
toast.warning('Failed to save before restoring backup');
}

// Restore to the specified commit
const restoreResult = await branchData.sandbox.gitManager.restoreToCommit(checkpoint.oid);

if (!restoreResult.success) {
throw new Error(restoreResult.error || 'Failed to restore commit');
}

const branchName = editorEngine.branches.getBranchById(targetBranchId)?.name || targetBranchId;
toast.success('Restored to backup!', {
description: `Branch "${branchName}" has been restored`,
});

return { success: true };
} catch (error) {
console.error('Failed to restore checkpoint:', error);
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
toast.error('Failed to restore checkpoint', {
description: errorMessage,
});
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Replace hardcoded toast text with translations.

The new restore flow surfaces several literal strings (“Branch not found”, “Failed to save before restoring backup”, “Restored to backup!”, etc.). These must come from next-intl resources rather than inline English per our client UI localization guidelines. Wire up the translation hook/scoped messages and pass the translated copy into the toasts.
Based on learnings

Comment on lines +74 to +91
if (!result.success) {
toast.error('Failed to rename backup');
editorEngine.posthog.capture('versions_rename_commit_failed', {
commit: commit.oid,
newName: name,
error: result.error,
});
return;
}

toast.success('Backup renamed successfully!', {
description: `Renamed to: "${name}"`,
});

editorEngine.posthog.capture('versions_rename_commit_success', {
commit: commit.oid,
newName: name,
});
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Localize toast copy instead of hardcoding.

All newly added toast messages (“Failed to rename backup”, “Backup renamed successfully!”, etc.) are literal strings. Per our client guidelines we must source user-visible text from next-intl/translations instead of hardcoding English. Refactor to use the appropriate t lookup with message keys.
Based on learnings

🤖 Prompt for AI Agents
In apps/web/client/src/components/ui/settings-modal/versions/version-row.tsx
around lines 74-91, the toast messages are hardcoded English strings; replace
them with localized strings via the next-intl translations (use the component's
existing t/useTranslations hook or add one if missing). Swap toast.error('Failed
to rename backup') for toast.error(t('versions.rename_failed')) and
toast.success('Backup renamed successfully!', { description: `Renamed to:
"${name}"` }) for toast.success(t('versions.rename_success_title'), {
description: t('versions.rename_success_description', { name }) }) (or use your
agreed message keys), and pass any dynamic values through the translation
interpolation; ensure message keys are added to the locale files.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
apps/web/client/src/components/ui/settings-modal/versions/versions.tsx (2)

54-77: Handle gitManager.createCommit() errors.

Line 60 awaits gitManager.createCommit() without catching rejections. If the promise throws, the handler bubbles an error, no toast displays, and the UI gives no feedback. Wrap the call in try/catch, keep the existing success/error result handling, and surface unexpected exceptions via a toast.

         const result = await gitManager.createCommit();
-        if (!result.success) {
-            toast.error('Failed to create backup', {
-                description: result.error || 'Unknown error',
-            });
-            return;
-        }
-
-        toast.success('Backup created successfully!');
-        editorEngine.posthog.capture('versions_create_commit_success');
+        try {
+            if (!result.success) {
+                toast.error('Failed to create backup', {
+                    description: result.error || 'Unknown error',
+                });
+                return;
+            }
+
+            toast.success('Backup created successfully!');
+            editorEngine.posthog.capture('versions_create_commit_success');
+        } catch (error) {
+            const description = error instanceof Error ? error.message : 'Unknown error';
+            toast.error('Failed to create backup', { description });
+        }

55-113: Replace hardcoded UI strings with translations.

Lines 56 (“Git not initialized”), 62 (“Failed to create backup”), 68 (“Backup created successfully!”), 83 (“Versions”), 92 (“Select branch”), and 111 (“New backup”) all introduce literal English text. The client guideline for apps/web/client/src/**/* requires UI copy and toasts to come from next-intl. Please wire up useTranslations, move these strings into the locale resources, and reference the keys here (including toast descriptions). As per coding guidelines.

♻️ Duplicate comments (1)
apps/web/client/src/components/ui/settings-modal/versions/version-row.tsx (1)

70-96: Localize rename toasts as previously requested.

Lines 73-95 still emit literal English (“No active branch”, “Failed to rename backup”, “Backup renamed successfully!”). This repeats the earlier review note, and the guideline for apps/web/client/src/**/* mandates next-intl messages instead of hardcoded strings. Please hook up useTranslations, move these strings into the locale files (with the renamed value passed as interpolation), and reuse them here. As per coding guidelines.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 31ea4db and 7fff805.

📒 Files selected for processing (3)
  • apps/web/client/src/components/ui/settings-modal/versions/empty-state/version.tsx (2 hunks)
  • apps/web/client/src/components/ui/settings-modal/versions/version-row.tsx (7 hunks)
  • apps/web/client/src/components/ui/settings-modal/versions/versions.tsx (4 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
apps/web/client/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

apps/web/client/src/**/*.{ts,tsx}: Use path aliases @/* and ~/* for imports that map to apps/web/client/src/*
Avoid hardcoded user-facing text; use next-intl messages/hooks instead

Use path aliases @/* and ~/* for imports mapping to src/*

Files:

  • apps/web/client/src/components/ui/settings-modal/versions/empty-state/version.tsx
  • apps/web/client/src/components/ui/settings-modal/versions/versions.tsx
  • apps/web/client/src/components/ui/settings-modal/versions/version-row.tsx
apps/web/client/src/**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

apps/web/client/src/**/*.tsx: Create MobX store instances with useState(() => new Store()) for stable references across renders
Keep the active MobX store in a useRef and perform async cleanup with setTimeout(() => storeRef.current?.clear(), 0) to avoid route-change races
Avoid useMemo for creating MobX store instances
Avoid putting the MobX store instance in effect dependency arrays if it causes loops; split concerns by domain

apps/web/client/src/**/*.tsx: Create MobX store instances with useState(() => new Store()) for stable identities across renders
Keep the active MobX store in a useRef and clean up asynchronously with setTimeout(() => storeRef.current?.clear(), 0)
Do not use useMemo to create MobX stores
Avoid placing MobX store instances in effect dependency arrays if it causes loops; split concerns instead
observer components must be client components; place a single client boundary at the feature entry; child observers need not repeat 'use client'

Files:

  • apps/web/client/src/components/ui/settings-modal/versions/empty-state/version.tsx
  • apps/web/client/src/components/ui/settings-modal/versions/versions.tsx
  • apps/web/client/src/components/ui/settings-modal/versions/version-row.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Do not use the any type unless necessary

Files:

  • apps/web/client/src/components/ui/settings-modal/versions/empty-state/version.tsx
  • apps/web/client/src/components/ui/settings-modal/versions/versions.tsx
  • apps/web/client/src/components/ui/settings-modal/versions/version-row.tsx
{apps,packages}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Avoid using the any type unless absolutely necessary

Files:

  • apps/web/client/src/components/ui/settings-modal/versions/empty-state/version.tsx
  • apps/web/client/src/components/ui/settings-modal/versions/versions.tsx
  • apps/web/client/src/components/ui/settings-modal/versions/version-row.tsx
🧠 Learnings (2)
📚 Learning: 2025-09-14T01:44:21.209Z
Learnt from: CR
PR: onlook-dev/onlook#0
File: AGENTS.md:0-0
Timestamp: 2025-09-14T01:44:21.209Z
Learning: Applies to apps/web/client/src/**/*.{ts,tsx} : Avoid hardcoded user-facing text; use next-intl messages/hooks instead

Applied to files:

  • apps/web/client/src/components/ui/settings-modal/versions/version-row.tsx
📚 Learning: 2025-09-16T19:22:52.461Z
Learnt from: CR
PR: onlook-dev/onlook#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-16T19:22:52.461Z
Learning: Applies to apps/web/client/src/app/**/*.tsx : Avoid hardcoded user-facing text; use next-intl messages/hooks

Applied to files:

  • apps/web/client/src/components/ui/settings-modal/versions/version-row.tsx
🧬 Code graph analysis (3)
apps/web/client/src/components/ui/settings-modal/versions/empty-state/version.tsx (2)
apps/web/client/src/components/store/editor/index.tsx (1)
  • useEditorEngine (10-14)
packages/ui/src/components/sonner.tsx (1)
  • toast (19-19)
apps/web/client/src/components/ui/settings-modal/versions/versions.tsx (2)
packages/ui/src/components/sonner.tsx (1)
  • toast (19-19)
packages/ui/src/components/select.tsx (5)
  • Select (158-158)
  • SelectTrigger (166-166)
  • SelectValue (167-167)
  • SelectContent (159-159)
  • SelectItem (161-161)
apps/web/client/src/components/ui/settings-modal/versions/version-row.tsx (2)
packages/git/src/git.ts (1)
  • updateCommitDisplayName (159-169)
apps/web/client/src/components/store/editor/git/utils.ts (1)
  • restoreCheckpoint (19-60)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7fff805 and 238b6e1.

📒 Files selected for processing (1)
  • apps/web/client/src/components/ui/settings-modal/versions/versions.tsx (3 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
apps/web/client/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

apps/web/client/src/**/*.{ts,tsx}: Use path aliases @/* and ~/* for imports that map to apps/web/client/src/*
Avoid hardcoded user-facing text; use next-intl messages/hooks instead

Use path aliases @/* and ~/* for imports mapping to src/*

Files:

  • apps/web/client/src/components/ui/settings-modal/versions/versions.tsx
apps/web/client/src/**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

apps/web/client/src/**/*.tsx: Create MobX store instances with useState(() => new Store()) for stable references across renders
Keep the active MobX store in a useRef and perform async cleanup with setTimeout(() => storeRef.current?.clear(), 0) to avoid route-change races
Avoid useMemo for creating MobX store instances
Avoid putting the MobX store instance in effect dependency arrays if it causes loops; split concerns by domain

apps/web/client/src/**/*.tsx: Create MobX store instances with useState(() => new Store()) for stable identities across renders
Keep the active MobX store in a useRef and clean up asynchronously with setTimeout(() => storeRef.current?.clear(), 0)
Do not use useMemo to create MobX stores
Avoid placing MobX store instances in effect dependency arrays if it causes loops; split concerns instead
observer components must be client components; place a single client boundary at the feature entry; child observers need not repeat 'use client'

Files:

  • apps/web/client/src/components/ui/settings-modal/versions/versions.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Do not use the any type unless necessary

Files:

  • apps/web/client/src/components/ui/settings-modal/versions/versions.tsx
{apps,packages}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Avoid using the any type unless absolutely necessary

Files:

  • apps/web/client/src/components/ui/settings-modal/versions/versions.tsx
🧬 Code graph analysis (1)
apps/web/client/src/components/ui/settings-modal/versions/versions.tsx (1)
packages/ui/src/components/select.tsx (5)
  • Select (158-158)
  • SelectTrigger (166-166)
  • SelectValue (167-167)
  • SelectContent (159-159)
  • SelectItem (161-161)
🔇 Additional comments (2)
apps/web/client/src/components/ui/settings-modal/versions/versions.tsx (2)

54-86: Well-structured error handling and user feedback.

The refactored handleNewBackup function demonstrates solid error handling practices:

  • Validates gitManager presence before attempting operations
  • Handles both expected failures (result.success check) and unexpected errors (catch block)
  • Provides clear user feedback via toast notifications
  • Properly manages loading state in the finally block

98-98: Verified: branch switch is synchronous and triggers re-render. switchToBranch is a MobX action that sets currentBranchId immediately, and Versions is wrapped in observer, so selectedBranchId and downstream data (commits) update on the next render.

@vercel vercel bot temporarily deployed to Preview – docs October 8, 2025 16:50 Inactive
@vercel vercel bot temporarily deployed to Preview – docs October 8, 2025 16:52 Inactive
@vercel vercel bot temporarily deployed to Preview – docs October 8, 2025 16:59 Inactive
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
apps/web/client/src/components/ui/settings-modal/versions/versions.tsx (1)

91-101: Replace hardcoded UI strings with next‑intl

Convert "Backup Versions", "Select branch", and "New backup" to translations.

-                <h2 className="text-lg">Backup Versions</h2>
+                <h2 className="text-lg">{t('title')}</h2>
...
-                        <SelectValue placeholder="Select branch" />
+                        <SelectValue placeholder={t('selectBranchPlaceholder')} />
...
-                        New backup
+                        {t('newBackup')}

Additionally (outside this hunk), ensure:

import { useTranslations } from 'next-intl';
// ...
const t = useTranslations('versions');

And add keys in apps/web/client/messages/*:

  • versions.title
  • versions.selectBranchPlaceholder
  • versions.newBackup

Based on learnings.

Also applies to: 122-122

🧹 Nitpick comments (2)
apps/web/client/src/components/ui/settings-modal/versions/versions.tsx (2)

16-22: Guard against undefined activeBranch/sandbox to avoid crashes

Accessing editorEngine.branches.activeBranch.id can throw if the branch store hasn’t initialized yet. Add a small guard to prevent a render‑time NPE.

-    const [isCreatingBackup, setIsCreatingBackup] = useState(false);
-    const selectedBranchId = editorEngine.branches.activeBranch.id;
-    const branchData = editorEngine.branches.getBranchDataById(selectedBranchId);
-    const gitManager = branchData?.sandbox.gitManager;
+    const [isCreatingBackup, setIsCreatingBackup] = useState(false);
+    const activeBranch = editorEngine.branches.activeBranch;
+    if (!activeBranch) {
+        // Optionally render a lightweight skeleton/spinner here
+        return null;
+    }
+    const selectedBranchId = activeBranch.id;
+    const branchData = editorEngine.branches.getBranchDataById(selectedBranchId);
+    const gitManager = branchData?.sandbox?.gitManager;

55-85: Internationalize toast messages and avoid racing on “latest commit” after create

  • Replace hardcoded toast strings with next‑intl messages.
  • Rely on the create API’s returned oid (if available) instead of commits?.[0], which can race with async updates. Fallback to reading commits?.[0] only if the API doesn’t return an oid.
-            if (!gitManager) {
-                toast.error('Git not initialized');
+            if (!gitManager) {
+                toast.error(t('gitNotInitialized'));
                 return;
             }
 
-            const result = await gitManager.createCommit();
+            const result = await gitManager.createCommit();
             if (!result.success) {
-                toast.error('Failed to create backup', {
-                    description: result.error || 'Unknown error',
+                toast.error(t('createBackupFailed'), {
+                    description: result.error || t('unknownError'),
                 });
                 return;
             }
 
-            toast.success('Backup created successfully!');
+            toast.success(t('backupCreated'));
             editorEngine.posthog.capture('versions_create_commit_success');
 
-            const latestCommit = gitManager.commits?.[0];
-            if (!latestCommit) {
-                console.error('No latest commit found');
-                return;
-            }
-            setCommitToRename(latestCommit.oid);
+            // Prefer returned oid if the API exposes it
+            const createdOid = (result as any)?.commit?.oid ?? (result as any)?.oid;
+            if (createdOid) {
+                setCommitToRename(createdOid);
+            } else {
+                const latestCommit = gitManager.commits?.[0];
+                if (!latestCommit) {
+                    console.error('No latest commit found');
+                    return;
+                }
+                setCommitToRename(latestCommit.oid);
+            }
         } catch (error) {
-            toast.error('Failed to create backup', {
-                description: error instanceof Error ? error.message : 'Unknown error',
+            toast.error(t('createBackupFailed'), {
+                description: error instanceof Error ? error.message : t('unknownError'),
             });

Also add (outside this hunk) at the top of the component:

import { useTranslations } from 'next-intl';
// ...
const t = useTranslations('versions');

As per coding guidelines.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 238b6e1 and 35d9e6d.

📒 Files selected for processing (2)
  • apps/web/client/src/components/ui/settings-modal/versions/empty-state/version.tsx (2 hunks)
  • apps/web/client/src/components/ui/settings-modal/versions/versions.tsx (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/client/src/components/ui/settings-modal/versions/empty-state/version.tsx
🧰 Additional context used
📓 Path-based instructions (4)
apps/web/client/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

apps/web/client/src/**/*.{ts,tsx}: Use path aliases @/* and ~/* for imports that map to apps/web/client/src/*
Avoid hardcoded user-facing text; use next-intl messages/hooks instead

Use path aliases @/* and ~/* for imports mapping to src/*

Files:

  • apps/web/client/src/components/ui/settings-modal/versions/versions.tsx
apps/web/client/src/**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

apps/web/client/src/**/*.tsx: Create MobX store instances with useState(() => new Store()) for stable references across renders
Keep the active MobX store in a useRef and perform async cleanup with setTimeout(() => storeRef.current?.clear(), 0) to avoid route-change races
Avoid useMemo for creating MobX store instances
Avoid putting the MobX store instance in effect dependency arrays if it causes loops; split concerns by domain

apps/web/client/src/**/*.tsx: Create MobX store instances with useState(() => new Store()) for stable identities across renders
Keep the active MobX store in a useRef and clean up asynchronously with setTimeout(() => storeRef.current?.clear(), 0)
Do not use useMemo to create MobX stores
Avoid placing MobX store instances in effect dependency arrays if it causes loops; split concerns instead
observer components must be client components; place a single client boundary at the feature entry; child observers need not repeat 'use client'

Files:

  • apps/web/client/src/components/ui/settings-modal/versions/versions.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Do not use the any type unless necessary

Files:

  • apps/web/client/src/components/ui/settings-modal/versions/versions.tsx
{apps,packages}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Avoid using the any type unless absolutely necessary

Files:

  • apps/web/client/src/components/ui/settings-modal/versions/versions.tsx
🧠 Learnings (3)
📚 Learning: 2025-09-14T01:44:21.209Z
Learnt from: CR
PR: onlook-dev/onlook#0
File: AGENTS.md:0-0
Timestamp: 2025-09-14T01:44:21.209Z
Learning: Applies to apps/web/client/src/**/*.{ts,tsx} : Avoid hardcoded user-facing text; use next-intl messages/hooks instead

Applied to files:

  • apps/web/client/src/components/ui/settings-modal/versions/versions.tsx
📚 Learning: 2025-09-16T19:22:52.461Z
Learnt from: CR
PR: onlook-dev/onlook#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-16T19:22:52.461Z
Learning: Applies to apps/web/client/src/app/**/*.tsx : Avoid hardcoded user-facing text; use next-intl messages/hooks

Applied to files:

  • apps/web/client/src/components/ui/settings-modal/versions/versions.tsx
📚 Learning: 2025-09-14T01:44:21.209Z
Learnt from: CR
PR: onlook-dev/onlook#0
File: AGENTS.md:0-0
Timestamp: 2025-09-14T01:44:21.209Z
Learning: Applies to apps/web/client/messages/** : Add or modify internationalized strings in apps/web/client/messages/* instead of hardcoding text

Applied to files:

  • apps/web/client/src/components/ui/settings-modal/versions/versions.tsx
🧬 Code graph analysis (1)
apps/web/client/src/components/ui/settings-modal/versions/versions.tsx (2)
packages/ui/src/components/sonner.tsx (1)
  • toast (19-19)
packages/ui/src/components/select.tsx (5)
  • Select (158-158)
  • SelectTrigger (166-166)
  • SelectValue (167-167)
  • SelectContent (159-159)
  • SelectItem (161-161)
🔇 Additional comments (1)
apps/web/client/src/components/ui/settings-modal/versions/versions.tsx (1)

97-116: Branch switching and button gating look good

  • Selector updates editorEngine.branches.switchToBranch(value) — fixes prior desync.
  • “New backup” button gated on gitManager and disabled while loading/creating — prevents duplicate requests.

@vercel vercel bot temporarily deployed to Preview – docs October 8, 2025 17:01 Inactive
@vercel vercel bot temporarily deployed to Preview – docs October 8, 2025 17:02 Inactive
@Kitenite Kitenite merged commit 15f9f2a into main Oct 8, 2025
5 of 6 checks passed
@Kitenite Kitenite deleted the fix-reverting-multiple-branches branch October 8, 2025 17:02
const handleRevert = async () => {
try {
if (selectedBranchIds.length === 0) {
toast.error('Please select at least one branch to revert');
Copy link
Contributor

Choose a reason for hiding this comment

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

Inconsistent terminology: The error message uses 'revert' while other parts of the UI (and function names) use 'restore'. Please consider using a single term for clarity.

Suggested change
toast.error('Please select at least one branch to revert');
toast.error('Please select at least one branch to restore');


const result = await restoreCheckpoint(checkpoint, editorEngine);

setIsCheckingOut(false);
Copy link
Contributor

Choose a reason for hiding this comment

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

Redundant state update: setIsCheckingOut(false) is called inside try and again in the finally block. Consider removing the inner call.

Suggested change
setIsCheckingOut(false);

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.

3 participants