Skip to content

feat: use clientLoader + localStorage for resizable panel persistence#3387

Draft
devin-ai-integration[bot] wants to merge 3 commits intomainfrom
devin/1776266661-clientloader-panel-persistence
Draft

feat: use clientLoader + localStorage for resizable panel persistence#3387
devin-ai-integration[bot] wants to merge 3 commits intomainfrom
devin/1776266661-clientloader-panel-persistence

Conversation

@devin-ai-integration
Copy link
Copy Markdown
Contributor

Summary

Replaces server-side cookie reading (getResizableSnapshot) with client-side localStorage for persisting resizable panel sizes on two routes. Cookies were hitting the ~4KB size limit; localStorage supports 5–10MB.

Two routes modified, two approaches used:

  1. Run detail route (runs.$runParam/route.tsx): Adds a Remix clientLoader with hydrate = true that reads panel snapshots from localStorage before first render via a getLocalStorageSnapshot helper that validates the stored JSON shape.

  2. Prompts route (prompts.$promptSlug/route.tsx): Simply removes the server-side cookie reads and returns undefined for snapshots. The react-window-splitter library already defaults to autosaveStrategy="localStorage" and reads from it when no snapshot prop is provided. A clientLoader was not used here because this route uses remix-typedjson (typedjson/useTypedLoaderData), which doesn't compose cleanly with clientLoader.

In both cases the library continues to automatically write to localStorage on resize (via autosaveId), so no save-side changes were needed. The resizablePanel.server module is left intact as it may be used by other routes.

Review & Testing Checklist for Human

  • Prompts route panel restore: Resize the outer/vertical/generations panels on the prompts page, reload, and verify sizes are restored. Since there's no clientLoader here, the library's built-in localStorage read is doing the work — confirm it actually kicks in when snapshot is undefined (keys: prompt-detail, prompt-vertical, prompt-generations)
  • Run detail route panel restore: Resize parent/tree panels, reload, verify sizes restore from localStorage (keys: panel-run-parent-v2, panel-run-tree)
  • No hydration errors in browser console on initial load of both routes — the clientLoader.hydrate = true pattern on the run detail route is the highest-risk area for hydration mismatches

Notes

  • Old cookie data from the previous approach remains in users' browsers but is harmless — it simply won't be read anymore.
  • The getLocalStorageSnapshot helper checks for a "status" property to validate the stored shape. If the library ever changes its serialization format, this silently falls back to default panel sizes rather than crashing.
  • The two different approaches (explicit clientLoader vs. library-native) are intentional due to the remix-typedjson constraint but could be unified later if remix-typedjson is removed from the prompts route.

Link to Devin session: https://app.devin.ai/sessions/2e69f1933945442e9daba486837a6f5b
Requested by: @ericallam

Replace server-side cookie reading (getResizableSnapshot) with client-side
localStorage for persisting resizable panel sizes. Cookies were hitting the
~4KB size limit; localStorage supports 5-10MB.

- Run detail route: uses Remix clientLoader with hydrate=true to read panel
  snapshots from localStorage before first render
- Prompts route: returns undefined for snapshots, letting the
  react-window-splitter library read from localStorage natively via
  autosaveStrategy="localStorage"

Co-Authored-By: Eric Allam <eallam@icloud.com>
@devin-ai-integration
Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Apr 15, 2026

⚠️ No Changeset found

Latest commit: 4d79a9e

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@devin-ai-integration
Copy link
Copy Markdown
Contributor Author

Testing Results — clientLoader + localStorage Panel Persistence

Ran the webapp locally against a fresh database with a completed task run. Tested the panel resize → localStorage persistence → reload restoration flow on the run detail route.

Test Results
Test Result
localStorage keys written on resize PASSED
Panel sizes restore after reload PASSED
No critical console errors PASSED (with minor caveat)
Evidence

After resizing tree panel (arrow keys moved divider left)

Panel after resize

localStorage values confirmed via DevTools console

localStorage verification

After page reload — panels restored to same position

Panels restored after reload

Hydration Warning (expected)
Warning: Prop `aria-valuemin` did not match. Server: "0" Client: "0.03644314868804665"
    at PanelResizerVisible2 → ResizableHandle → ResizablePanelGroup → TasksTreeView

This is inherent to the clientLoader pattern: the server has no access to localStorage, so it renders panels at default sizes. The clientLoader provides the saved sizes, causing React to detect a brief prop difference during hydration. The library self-corrects immediately and the UI displays correctly.

Not Tested
  • Prompts route: No AI prompt data in local DB. Code review confirms it correctly passes undefined for all three snapshot fields, relying on the library's native autosaveStrategy="localStorage".

Devin session

devin-ai-integration bot and others added 2 commits April 15, 2026 16:42
…ize handles

Co-Authored-By: Eric Allam <eallam@icloud.com>
Co-Authored-By: Eric Allam <eallam@icloud.com>
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.

1 participant