fix(ui): secondary sidebar hard-forces the app rail collapsed, preserving the user's pin (PAP-10694)

A secondary sidebar (company settings, plugin routeSidebar) now ALWAYS collapses
the app sidebar to its rail and hides its expand toggle — overriding the user's
pin while the route is active — but the force is ephemeral and never mutates the
persisted expanded/collapsed preference, so leaving the route restores exactly
what the user chose.

Previously the collapse was a soft route-request that the user pin won over, so a
user who pinned the sidebar expanded saw both sidebars (240+240) on settings/
plugin routes and could not get the intended rail.

- SidebarContext: new ephemeral `forceCollapsed` + `collapseLocked`; precedence
  becomes force > user pin > route request > default. `toggleCollapsed` is a
  no-op while forced (won't expand or write the pin).
- Layout: drives `setForceCollapsed(hasSecondarySidebar)` instead of the soft
  `setRouteRequestsCollapsed`.
- Sidebar: hides the expand/collapse affordance while `collapseLocked`.
- Plugin docs (SPEC, authoring guide, SDK README) updated to the hard-force rule.
- Tests: context forced-collapse precedence/lock/restore/mobile; Sidebar hidden
  toggle; Layout mock updated.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-06-09 15:56:14 +00:00
1 parent 9ca8cfd15d
commit 2dee4ba32e
9 files changed
+132 -31

No files matched your search

+5 -3
View File
@@ -346,9 +346,11 @@ route is active the host collapses the app `<Sidebar/>` to its 64px icon rail
Because the host drives this collapse, a plugin should **not** mount
`RequestCollapsedSidebar` or otherwise try to collapse the app sidebar itself —
doing so is redundant and fights the host. The host restores the user's previous
sidebar state when they navigate away, and an explicit user pin (expanded or
collapsed) always takes precedence over the route-driven collapse.
doing so is redundant and fights the host. While your route is active the app
rail is forced collapsed (its expand toggle is hidden), overriding any user pin
— a secondary sidebar always collapses the primary. This force never changes the
user's saved expanded/collapsed preference, so the host restores exactly what
the user chose as soon as they navigate away.
## Shared host components
+7 -3
View File
@@ -1144,9 +1144,13 @@ host's own company-settings sidebar.
The host owns the collapse. Plugins must not mount `RequestCollapsedSidebar` or
otherwise attempt to collapse the app sidebar from a `routeSidebar` — the host
applies the collapse while the route is mounted and restores the previous state
on navigation away. Collapse precedence is: explicit user pin (expanded or
collapsed) > route-requested collapse > default expanded, so a user's pin always
wins over the route-driven collapse.
on navigation away. The collapse is a **hard invariant**: while a secondary
sidebar is shown the app rail is forced collapsed and its expand/toggle
affordance is hidden, *overriding* any user pin. Crucially, this force is
ephemeral — it never mutates the user's persisted expanded/collapsed preference,
so navigating back to a normal route restores exactly what the user chose.
Precedence is therefore: secondary-sidebar force > explicit user pin >
route-requested collapse (`RequestCollapsedSidebar`) > default expanded.
## 19.6 Shared Components In `@paperclipai/plugin-sdk/ui`