Persist the work drawer state; shimmer washes toward background

Drawer: the open/closed preference reset on every chat switch, so the
panel re-animated open each time a PR chat was selected. The toggle is
now one persisted preference — switching chats renders the drawer in
its remembered state with no animation; auto-open-on-PR remains only
until the user toggles once.

Shimmer: the band color moves close to the background (a whisper of
primary keeps the hue) so glyphs visibly wash out as it passes — the
strongest contrast against full-color text — and the highlight wash
drops to a soft glow instead of a gray slab.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
klopez4212
2026-07-05 09:05:22 +01:00
parent c9236b3910
commit 680bb16e97
2 changed files with 27 additions and 15 deletions

View File

@@ -55,6 +55,8 @@ import { normalizePubkey } from "@/shared/lib/pubkey";
import { getMentionTagPubkey } from "@/shared/lib/resolveMentionNames";
import { Button } from "@/shared/ui/button";
const WORK_PANEL_OPEN_KEY = "buzz:chats:work-panel-open:v1";
type ChatsScreenProps = {
initialProjectId?: string | null;
selectedChatId?: string | null;
@@ -335,16 +337,27 @@ export function ChatsScreen({
}
return null;
}, [messages]);
// null = auto: open once the agent has produced a PR, closed otherwise.
// A click stores an explicit preference for the current chat.
// The drawer's open state is a single persisted preference — switching
// chats must NOT reset it (the panel used to re-animate open on every
// switch). null = never toggled: auto-open when the chat has a PR.
const [workPanelPreference, setWorkPanelPreference] = React.useState<
boolean | null
>(null);
const workPanelChatRef = React.useRef(selectedChatId);
if (workPanelChatRef.current !== selectedChatId) {
workPanelChatRef.current = selectedChatId;
setWorkPanelPreference(null);
}
>(() => {
try {
const raw = window.localStorage.getItem(WORK_PANEL_OPEN_KEY);
return raw === null ? null : raw === "true";
} catch {
return null;
}
});
const handleWorkPanelPreference = React.useCallback((next: boolean) => {
setWorkPanelPreference(next);
try {
window.localStorage.setItem(WORK_PANEL_OPEN_KEY, String(next));
} catch {
// Best-effort persistence.
}
}, []);
const isWorkPanelOpen = workPanelPreference ?? agentPullRequestHref !== null;
const startManagedAgentMutation = useStartManagedAgentMutation();
const [isEnsuringDefaultAgent, setIsEnsuringDefaultAgent] =
@@ -638,7 +651,7 @@ export function ChatsScreen({
}
aria-pressed={isWorkPanelOpen}
data-testid="toggle-work-panel"
onClick={() => setWorkPanelPreference(!isWorkPanelOpen)}
onClick={() => handleWorkPanelPreference(!isWorkPanelOpen)}
size="icon"
type="button"
variant="ghost"

View File

@@ -233,19 +233,18 @@
saturated instead of graying toward the background) — a rich mid-purple
band over near-white text. */
.buzz-shimmer-accent {
/* The band hugs the BACKGROUND color (a whisper of primary keeps the
hue): glyphs visibly wash out toward the background as it passes,
which is the strongest possible contrast against full-color text. */
--buzz-shimmer-highlight: color-mix(
in oklab,
hsl(var(--primary)) 40%,
hsl(var(--primary)) 18%,
hsl(var(--background))
);
--buzz-shimmer-band: 300%;
color: inherit;
}
.dark .buzz-shimmer-accent {
--buzz-shimmer-highlight: color-mix(in oklch, hsl(var(--primary)) 60%, black);
}
/* Glyph recoloring alone is too subtle at sidebar sizes — sweep a
translucent highlight WASH behind the text as well (same keyframes, so
band and wash move together). The wash is a soft primary tint the text
@@ -256,7 +255,7 @@
90deg,
transparent 0%,
transparent 40%,
hsl(var(--primary) / 0.28) 50%,
hsl(var(--primary) / 0.16) 50%,
transparent 60%,
transparent 100%
);