Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

Sidebar collapse does not survive reload — sidebar_state cookie is written but never read back (SidebarProvider always starts from defaultOpen = true) #4234

Closed

Description

@huangyiirene

Symptom

Collapsing the console sidebar does not survive a page reload.

  • The cookie is written: sidebar_state=false, 7-day max-age — and it is still present after the reload.
  • Yet the sidebar renders expanded: width 255px, data-state=expanded.
  • Measured at +2s / +4s / +8s after load specifically to exclude a settling race — expanded at every sample.
  • Reproduced 3×.
  • Expected: with sidebar_state=false present, the sidebar renders collapsed on first paint.

Root cause

packages/components/src/ui/sidebar.tsx only ever writes the cookie and never reads it back.

  • SIDEBAR_COOKIE_NAME = "sidebar_state", SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7 (~lines 36-37) — matching the observed 7-day max-age.
  • setOpen writes it on every toggle: document.cookie = ... (~line 101). That is the only occurrence of the cookie name in the whole package.
  • SidebarProvider initialises from the prop default: defaultOpen = true → const [_open, _setOpen] = React.useState(defaultOpen). There is no cookie read, no lazy initialiser, no hydration effect.
  • No SPA caller passes a cookie-derived defaultOpen. The only defaultOpen={false} call sites on main are marketing/demo components in apps/site; the console shell passes nothing, so it gets true every load.

The upstream shadcn original resolves this by reading the cookie in a server component and handing the value down as defaultOpen. A pure SPA has no equivalent of that step — so the fix needs an SPA-side read (a lazy useState initialiser or equivalent that parses document.cookie on mount), not merely "pass the prop". Passing a prop from the console shell alone still leaves every other SPA consumer broken.

Verified still present on objectui origin/main @ bb68488 (current at filing time), not just on the vendored console 09987b68 the run used.

Honest contrast — the rest of shell-nav-personalization is healthy

This is a narrow defect, not a broken persistence layer:

  • Pins, favorites and recents all persist correctly, backed by real sys_user_preference rows.
  • Reorder is blocked — not failing, untestable: no drag affordance is rendered at all.
  • Recents capture record visits but not object-list visits.

Reproduction

Framework a86db175, vendored console 09987b68, showcase app, authenticated session.

  1. Collapse the sidebar.
  2. Confirm the cookie: sidebar_state=false, max-age 7 days.
  3. Reload the page.
  4. Confirm the cookie is still sidebar_state=false.
  5. Measure the sidebar at +2s, +4s and +8s after load.

Observed: 255px and data-state=expanded at all three sample points. Reproduced 3×.

Dedup

Searched objectstack-ai/objectui for sidebar_state, SidebarProvider, defaultOpen, and sidebar-collapse-not-persisted symptoms — no open or closed duplicate found.

Source

Extracted from the QA run objectstack-ai/objectstack#7514 (framework a86db175, vendored console 09987b6). Root-cause re-verification is against objectui origin/main @ bb68488.

Activity

  1. self-assigned this
    on Aug 11, 2026
  2. yinlianghui commented on Aug 11, 2026

    @yinlianghui
    Contributor

    CLAIM + 代决 — session session_017Qqyix2QcnpUC9XeYVDzx3(objectui whole-repo PM seat;否决窗口开放)。Branch: claude/issue-4234-sidebar-cookie-read。

    按卡片结论执行(SPA 侧读取,不是只传 prop——单改 console shell 会留下其余 SPA 消费者继续坏),附两条裁决:

    1. 优先级序,须在 PR 钉住:受控 open prop > cookie(用户已持久化的选择)> defaultOpen prop > true。cookie 覆盖的是"默认值",不覆盖受控用法;显式 defaultOpen={false} 的营销 demo 在 cookie 缺席时行为不变(对照 pin)。
    2. shadcn-sync 分歧先测后动:sidebar.tsx 是 sync 面(shadcn-sync 的 registry 请求没有超时:连接被黑洞时 pnpm shadcn:check 会一直挂着(46 次串行请求) #4031 语境)。dev 先实测 shadcn:check 如何对待本地分歧(逐字比对?分歧登记?),照仓库既有的"有意分歧"模式落地;若该文件须保持 pristine,则把读取做进 components 包内所有 SPA 消费者共用的薄封装——但绝不允许"只修 console 一个调用点"的形状。

    核心 pin(红先):cookie sidebar_state=false 在场 ⇒ 首帧 collapsed(QA 的 3× 复现变红先用例);cookie 缺席 ⇒ defaultOpen 语义逐字不变(对照);写侧(每次 toggle 写 cookie)不回归。

    Surface:packages/components/src/ui/sidebar.tsx + 测试;components 包当前无在飞持有者(#4040 批次2 在 plugin-grid/plugin-form/i18n,#4292/#4240/#4265 均不相交)。


    Generated by Claude Code

  3. yinlianghui commented on Aug 11, 2026

    @yinlianghui
    Contributor

    ACCEPT — PM review of record (session session_017Qqyix2QcnpUC9XeYVDzx3)。

    PR #4312 验收要点:

    • shadcn-sync 分歧的处理是教科书级:实测出仓库的真实模式(shadcn-local-patches.mjs 声明式补丁注册表 + payload 必须出 src/ui/ + 离线执法测试 + 上游失配自动开单),并完整照办——解析逻辑全在 sync 永不再生的 lib/sidebar-cookie.ts,sidebar.tsx 只加两行锚定补丁并入注册表,pnpm shadcn:update 会重放而非静默回滚。补丁注册表门自己也进了红先验证(revert 后该门变红),修复受保护这件事本身有证据。
    • 裁决两条全落:优先级序四态钉住(受控 > cookie > defaultOpen > true);cookie 名保持唯一拼写(传入 reader 而非引常量,避免环形依赖)。lazy initialiser 满足首帧判据(QA 的 +2s/+4s/+8s 采样)。
    • apps/site SSR 水合注记处理正确:按裁决实现(cookie 压 defaultOpen),把 Next SSR 下"访客切换过 demo 侧栏后刷新会有一次 React 自纠水合差异"的后果如实写进 PR——两个替代方案分别违反首帧判据与裁决本身,不擅自偏离而是标记,正是想要的形状。console(本卡报告面)是纯 SPA 无此窗口。
    • 顺手修复的 close-label 家族断言(第二个补丁家族出现即必红)范围克制、属同面。
    • 诚实记账:"cookie=expanded ⇒ expanded"自认不具判别力、记为方向性对照不计 pin。红先 4 红 25 绿与预言精确;CI 20 项收敛零失败。

    Flipping ready + auto-merge。


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions