Skip to content

fix(web): persist collapsed sidebar project state across relaunch#2259

Open
Marve10s wants to merge 4 commits intopingdotgg:mainfrom
Marve10s:fix/sidebar-project-collapse-persistence
Open

fix(web): persist collapsed sidebar project state across relaunch#2259
Marve10s wants to merge 4 commits intopingdotgg:mainfrom
Marve10s:fix/sidebar-project-collapse-persistence

Conversation

@Marve10s
Copy link
Copy Markdown
Contributor

@Marve10s Marve10s commented Apr 21, 2026

Summary

This fixes project collapse state in the threads sidebar so it survives relaunches more reliably.

What Changed

  • persist both collapsedProjectCwds and expandedProjectCwds in sidebar UI state
  • preserve the legacy upgrade behavior for older saved blobs that only stored expanded project paths
  • persist project sync, expand/collapse, and reorder changes immediately instead of waiting for the debounced subscriber
  • add regression coverage for:
    • all projects collapsed across restart
    • immediate persistence when a project is collapsed through the store actions

Root Cause

There were two separate persistence gaps:

  1. The saved shape only recorded expanded project paths. When every project was collapsed, the persisted state became indistinguishable from a fresh install, so a later sync re-expanded everything.
  2. Project visibility changes relied on the debounced persistence subscriber. If the app was closed before that debounce flushed, the latest collapse state was lost.

User Impact

Collapsed projects in the sidebar now stay collapsed after relaunch instead of reopening unexpectedly.

Validation

  • bun fmt
  • bun lint
  • bun typecheck currently fails on unrelated existing repository issues in packages/contracts
  • bun run --cwd apps/web typecheck currently fails on unrelated existing makeUnsafe and contracts typing issues elsewhere in the workspace

Note

Fix sidebar project collapsed state to persist across relaunch

Macroscope summarized 4d11318.


Note

Medium Risk
Touches sidebar UI persistence and state synchronization logic; incorrect persistence or legacy-shape handling could cause project groups to re-expand or order to regress on restart.

Overview
Fixes sidebar project expand/collapse persistence by writing both collapsedProjectCwds and expandedProjectCwds so an “all collapsed” state round-trips across relaunch, while preserving the legacy “not in expanded list = collapsed” behavior for one upgrade session.

Updates useUiStateStore actions (syncProjects, toggleProject, setProjectExpanded, reorderProjects) to persist immediately when they actually change state (in addition to the existing debounced subscriber), and adds regression tests for all-collapsed restart and immediate persistence. Also factors duplicated test localStorage stubs into a shared createLocalStorageStub helper.

Reviewed by Cursor Bugbot for commit 4d11318. Bugbot is set up for automated code reviews on this repo. Configure here.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 21, 2026

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 0acc127c-4aca-4e0f-88a3-1578dbc03c7a

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@github-actions github-actions Bot added size:M 30-99 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list. labels Apr 21, 2026
@Marve10s Marve10s marked this pull request as ready for review April 21, 2026 11:36
@Marve10s Marve10s force-pushed the fix/sidebar-project-collapse-persistence branch from 9feb55e to 620f909 Compare April 21, 2026 11:41
Copy link
Copy Markdown
Contributor

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 620f909. Configure here.

Comment thread apps/web/src/uiStateStore.ts Outdated
macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Apr 21, 2026
@macroscopeapp
Copy link
Copy Markdown
Contributor

macroscopeapp Bot commented Apr 21, 2026

Approvability

Verdict: Needs human review

This bug fix adds immediate state persistence for sidebar project collapse state. While the changes are contained and low-risk, there's an unresolved review comment questioning whether the approach is optimal, suggesting a simpler alternative may exist.

You can customize Macroscope's approvability policy. Learn more.

@macroscopeapp macroscopeapp Bot dismissed their stale review April 22, 2026 13:14

Dismissing prior approval to re-evaluate f1fcafe

macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Apr 22, 2026
Comment thread apps/web/src/uiStateStore.test.ts Outdated
useUiStateStore,
} from "./uiStateStore";

function createLocalStorageStub(): Storage {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Already have one of these.

Also i think u can just useUiStore.persist.clear() or whatever and it will use a memory fallback when LS is not available?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed this by extracting the duplicated createLocalStorageStub() into a shared test helper and reusing it from the affected tests.

I didn’t switch useUiStateStore over to useUiStateStore.persist.clearStorage() because this store doesn’t use Zustand’s persist middleware. Its persistence is manual (readPersistedState / persistState plus the module-level persisted-state caches), so the test still needs an explicit localStorage stub and explicit state reset.

Is this okay ? store architecture is smth I can edit but I think it's not the scope for this PR

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I meant this one: https://github.com/pingdotgg/t3code/blob/main/apps/web/src/lib/storage.ts

And ah, forgot this used manual storage. Not sure why 😝

Copy link
Copy Markdown
Contributor Author

@Marve10s Marve10s Apr 22, 2026

Choose a reason for hiding this comment

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

Oh, I misunderstood.

storage.ts exposes the repo’s StateStorage, not a full DOM Storage, tests are stubbing window.localStorage directly, so I'm kinda fine with the code in this PR. But if you think there is something to change - I can fix that up

@macroscopeapp macroscopeapp Bot dismissed their stale review April 22, 2026 16:57

Dismissing prior approval to re-evaluate 4d11318

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

Labels

size:M 30-99 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants