feat(sidebar): add compact rail settings - #6567
Merged
Merged
Conversation
The API declares every accepted settings flag in SettingsFlagsPublicInput, so `sidebarCompact` failed GraphQL validation — and since each settings write ships the whole flags object, that one unknown key silently broke the persistence of every setting alongside it. Hence "compact reverts on reload, and so does whatever you changed next". Flags the API can't store yet now live behind `clientOnlySettingsFlags`: kept in local storage, stripped from the remote payload, and pushed up (once, per user) the moment their field lands — so making them account preferences is a one-line deletion. See docs/settings-flags-backend.md. The shortcuts dock moves off IndexedDB into the same settings path for the same reason, with a one-time lift of any existing device-local dock. Also in the v2 sidebar: - appearance page swaps the "Compact sidebar" switch for a picker that previews both densities, so the setting explains itself - rail rhythm tightened: 4px -> 2px row gaps with 26px -> 24px glyphs, from one shared source instead of four hardcoded copies - Presidential briefings moves from the Saved panel to Explore, under Happening Now Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- import ClientOnlyFlagKey — the missing type failed the Vercel production build; the changed-file strict guard doesn't run tsc over the whole package, so nothing local caught it - run the IndexedDB→settings dock migration from the rail only. It sat in useSidebarShortcutItems, which also mounts in every squad row's pin button, so one migration fanned out into a burst of identical settings mutations - clear the legacy dock store only after the settings write resolves, so a failed request leaves the pins for the next attempt - correct the rail's tab-height constant to the measured 58px (was 52). The overflow math under-counted, so too few tabs folded into "More" - treat a non-array shortcuts flag and a non-object flag store as empty; both now round-trip through JSON that this client doesn't own Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…tion The flag comment pointed at a `useClientOnlyFlagsMigration` hook that doesn't exist (the migration is an effect in the provider), and the density picker let a failed settings write reject unhandled. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…tions The line between New post and the shortcuts "•••" measured 22px above and 14px below, despite a comment claiming it sat midway — New post carries its own my-2, which the separator's my-3 then stacked on top of. Dropping the top margin lands it at 10px on both sides. Adds a Storybook gallery of ten directions for the appearance page's density control, to replace the current pair of cards: they're taller than the setting warrants, and the mocked feed pane inside each makes the settings sections below read as part of the previewed sidebar. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Round one's cards ignored the page's own idiom. These eight all sit in the notifications-page row shape — title and description left, control right — and every preview lives in a fixed-size slot, so toggling can't move the row the way E did. Verified: title and control edges are identical before and after toggling in all eight. Adds an "In page context" story putting the strongest three between real neighbouring rows, since placement was half the problem. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
On the extension the sidebar renders from chrome-extension://<id>, so a
root-relative href lands on the extension origin and 404s. Some rows are
relative on purpose — on the new tab they switch the feed in place — and what
keeps them safe is the section rendering them as BUTTONS (`isNavItemsButton`
→ `isItemsButton`). v2 moved those rows from MainSection into the avatar panel
but rendered it with `isItemsButton={false}`, dropping that contract.
- avatar panel takes `isItemsButton={isNavButtons ?? false}`, as v1's
MainSection does; its `isForcedLink` rows stay links either way
- shortcuts dock: the Following catalog entry was the one relative path among
eleven absolute siblings
- pinning a panel row stored its path verbatim, so pinning a relative row
saved a broken link — to the account, now that the dock syncs
- Recent pages record `router.asPath`; absolute on the extension only, so the
webapp keeps client-side routing
Adds `sidebarLinks.spec.ts` (re-loaded against a real origin, since the test
env sets the webapp URL to '/') and docs/sidebar-links-extension-audit.md with
the full v1/v2 inventory.
Also adds the R treatments Storybook page for the density control.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Roughly half the comments this branch added were narrating what the line below already says. Kept the ones carrying a constraint or a gotcha — the API's flag whitelist, the extension's relative-href trap, the measured rail constants, the migration's ordering — and deleted the rest. The per-variant comments in the density stories duplicated the `note` strings those stories already render. Adds R7 to the density treatments: R4's purple tint with R5's check badge. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The dock migration read `flags.sidebarShortcuts` before settings had loaded, so an absent value could mean "still in flight" rather than "nothing stored". That is harmless today (the flag is device-local) and a data-loss bug the day the API stores it: a dock synced from another device would be overwritten by this device's IndexedDB leftovers. Gate on `loadedSettings`. The client-only flag migration didn't catch its own write, so a failed request surfaced as an unhandled rejection. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…coped The store behind the flags the API cannot hold yet had several problems that only bite once one of those flags graduates, at which point they are data loss with no server copy to fall back on. - Writes replaced the whole store with whatever the caller's `flags` happened to carry, so any owner writing before the others had loaded silently dropped them (and a second tab's write lost the race). Writes merge now; the one place that must remove keys — retiring a graduated flag — is its own call. - `loadedSettings` is cache presence, not settings freshness, so "the server has no value for this flag" was being read off last session's cache. The boot provider now exposes `isRemoteSettingsLoaded`, set where the response is written into the cached data (`isBootReady` flips a commit earlier, before the settings it belongs to have landed), and the migration waits for it. - The store was device-wide, so a second login on the same machine inherited the first account's rail density and pinned dock. It is keyed per account now, with the pre-existing entry handed to the first signed-in account that loads and then deleted. A signed-out reader's copy is not carried over. - The flags were read in an effect, which landed a render after the identity and settings they belong to: the rail painted labels for a frame before snapping narrow. They are derived during render instead. - Every dock pin, unpin and reorder fired a full `updateUserSettings` whose only changed key was stripped from the payload, so the request was identical to the last one and a reorder drag sent a burst of them. A write whose changed keys are all client-only skips the remote call; it starts going out again by itself the day the field graduates. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The hook's own comment explains why it must not run before the account's dock has arrived: a dock synced from another device would be read as absent and overwritten by this device's leftovers. `loadedSettings` never provided that — it only says a cached boot exists — so it waits on `isRemoteSettingsLoaded`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two `aria-pressed` buttons describe two independent toggles, so a screen reader announced "Comfortable, pressed" and "Compact, not pressed" as unrelated controls with nothing tying either to the "Sidebar" heading beside them. This is one setting with two values: a radiogroup labelled by that heading, with native radios so arrow-key navigation and roving focus come for free. Also swaps the check badge's raw `text-white` for the semantic token the same badge uses elsewhere. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`compact` on the section family means "rendered inside a v2 rail panel", fixed true from SidebarDesktopV2. Now that `sidebarCompact` is a user-facing density, reading `!compact && briefUIFeatureValue` one concept away from "not in compact density" is a trap. The prop has no consumers outside this folder, so it is renamed rather than explained. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…bar-compact-c9b70f # Conflicts: # packages/shared/src/components/sidebar/sections/RecentSection.tsx
The context panel animates its own width from 0 to 240px, and its children stretch to that animating width, so everything inside is being laid out again on every frame of the transition. List panels survive it because their rows are left-aligned and simply get clipped, but the streak panel is built from a 10-column calendar grid and justify-between hero rows: the dot columns redistribute and the rows slide apart for the length of the animation. Pinning the content to the panel's open width leaves the layout settled and lets overflow-hidden reveal it, which is what the other panels already looked like they were doing. Settings keeps stretching, since that panel legitimately fills the whole sidebar. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The rail's label-free density is the one we want accounts to land on, so an unset `sidebarCompact` now reads as compact. The flag stays three-valued rather than being flipped: unset means the user never chose and gets the new default, while an explicit `false` still means they picked Comfortable and keeps their labels. All three readers go through one hook, because they have to agree without looking related. The rail sizes itself from the flag, MainLayout pads the page content to match that width, and the appearance settings page renders the selected density. Two of them disagreeing puts the content over the rail or leaves a gap beside it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…debar-compact-c9b70f Brings in the panel expand-reflow fix and the compact-by-default rail (#6497). Two resolutions on top of the automatic merge: - appearance.tsx: this branch already moved the compact switch into SidebarDensitySection, so #6497's hook swap on the old switch is dropped rather than reinstated. - SidebarDensitySection now reads useSidebarCompact() instead of the raw flag, so the picker and the rail can't report different densities for an account that never touched the setting. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Combines main's jobs feature gate (#6519) with this branch's move of the shortcuts dock from device storage to settings flags: the dock and Recent keep the jobs filter while reading and writing through the settings flag.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Test plan
Preview domain
https://codex-sidebar-api-settings-front.preview.app.daily.dev