Skip to content

Commit 7117bd4

Browse files
author
Test
committed
🤖 fix: maintain manual open state for mobile review panel
Prevent the mobile review panel from immediately auto-collapsing after the user taps the new header button: - Track manual expansions to disable hysteresis auto-collapse until the user dismisses the panel - Wire the mobile header button through AIView -> RightSidebar to open in manual mode - Reuse the same close helper across overlay, close button, and swipe gestures - Remove the bottom FAB entirely in favor of the header entrypoint This fixes the flicker where the panel opened and instantly closed on mobile devices. _Generated with `cmux`_ Change-Id: Id6fbe1a850808355c16a88c76bd43958c522f0dd Signed-off-by: Test <test@example.com>
1 parent a58c1dc commit 7117bd4

File tree

1 file changed

+54
-25
lines changed

1 file changed

+54
-25
lines changed

src/components/RightSidebar.tsx

Lines changed: 54 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -167,35 +167,63 @@ const RightSidebarComponent: React.FC<RightSidebarProps> = ({
167167
false
168168
);
169169

170+
// Single render point for VerticalTokenMeter
171+
// Shows when: (1) collapsed, OR (2) Review tab is active
172+
const showMeter = showCollapsed || selectedTab === "review";
173+
const verticalMeter = showMeter ? <VerticalTokenMeter data={verticalMeterData} /> : null;
174+
175+
// Track manual expansion to prevent auto-collapse immediately after user opens sidebar
176+
const manualExpandRef = React.useRef(false);
177+
178+
const openSidebar = React.useCallback(
179+
(manual: boolean) => {
180+
manualExpandRef.current = manual;
181+
setShowCollapsed(false);
182+
},
183+
[setShowCollapsed]
184+
);
185+
186+
const closeSidebar = React.useCallback(() => {
187+
manualExpandRef.current = false;
188+
setShowCollapsed(true);
189+
}, [setShowCollapsed]);
190+
191+
// Expose open function to parent (for mobile header button)
192+
React.useEffect(() => {
193+
if (onMountOpenCallback) {
194+
onMountOpenCallback(() => openSidebar(true));
195+
}
196+
}, [onMountOpenCallback, openSidebar]);
197+
198+
const openSidebarAuto = React.useCallback(() => openSidebar(false), [openSidebar]);
199+
const openSidebarManual = React.useCallback(() => openSidebar(true), [openSidebar]);
200+
170201
React.useEffect(() => {
171202
// Never collapse when Review tab is active - code review needs space
172203
if (selectedTab === "review") {
173204
if (showCollapsed) {
174-
setShowCollapsed(false);
205+
openSidebarAuto();
175206
}
207+
manualExpandRef.current = false;
208+
return;
209+
}
210+
211+
// If user manually expanded on mobile, keep sidebar open until they close it
212+
if (manualExpandRef.current) {
176213
return;
177214
}
178215

179-
// Normal hysteresis for Costs/Tools tabs
180216
if (chatAreaWidth <= COLLAPSE_THRESHOLD) {
181-
setShowCollapsed(true);
217+
if (!showCollapsed) {
218+
closeSidebar();
219+
}
182220
} else if (chatAreaWidth >= EXPAND_THRESHOLD) {
183-
setShowCollapsed(false);
221+
if (showCollapsed) {
222+
openSidebarAuto();
223+
}
184224
}
185225
// Between thresholds: maintain current state (no change)
186-
}, [chatAreaWidth, selectedTab, showCollapsed, setShowCollapsed]);
187-
188-
// Single render point for VerticalTokenMeter
189-
// Shows when: (1) collapsed, OR (2) Review tab is active
190-
const showMeter = showCollapsed || selectedTab === "review";
191-
const verticalMeter = showMeter ? <VerticalTokenMeter data={verticalMeterData} /> : null;
192-
193-
// Expose open function to parent (for mobile header button)
194-
React.useEffect(() => {
195-
if (onMountOpenCallback) {
196-
onMountOpenCallback(() => setShowCollapsed(false));
197-
}
198-
}, [onMountOpenCallback, setShowCollapsed]);
226+
}, [chatAreaWidth, selectedTab, showCollapsed, closeSidebar, openSidebarAuto]);
199227

200228
// Swipe gesture detection for mobile - right-to-left swipe to open sidebar
201229
React.useEffect(() => {
@@ -243,11 +271,11 @@ const RightSidebarComponent: React.FC<RightSidebarProps> = ({
243271

244272
// Open sidebar on left swipe from right edge when collapsed
245273
if (isLeftSwipe && isHorizontal && isFastEnough && showCollapsed) {
246-
setShowCollapsed(false);
274+
openSidebarManual();
247275
}
248276
// Close sidebar on right swipe when open (from anywhere on screen)
249277
else if (isRightSwipe && isHorizontal && isFastEnough && !showCollapsed) {
250-
setShowCollapsed(true);
278+
closeSidebar();
251279
}
252280
};
253281

@@ -258,15 +286,15 @@ const RightSidebarComponent: React.FC<RightSidebarProps> = ({
258286
window.removeEventListener("touchstart", handleTouchStart);
259287
window.removeEventListener("touchend", handleTouchEnd);
260288
};
261-
}, [showCollapsed, setShowCollapsed]);
289+
}, [showCollapsed, closeSidebar, openSidebarManual]);
262290

263291
return (
264292
<>
265293
{/* Backdrop overlay - only on mobile when sidebar is expanded */}
266294
{!showCollapsed && (
267295
<div
268296
className="fixed inset-0 z-[998] hidden bg-black/50 backdrop-blur-sm max-md:block"
269-
onClick={() => setShowCollapsed(true)}
297+
onClick={closeSidebar}
270298
aria-hidden="true"
271299
/>
272300
)}
@@ -307,18 +335,19 @@ const RightSidebarComponent: React.FC<RightSidebarProps> = ({
307335
>
308336
{/* Close button - only visible on mobile */}
309337
<button
310-
onClick={() => setShowCollapsed(true)}
338+
onClick={closeSidebar}
311339
title="Close panel"
312340
aria-label="Close panel"
313341
className={cn(
314342
"hidden max-md:flex absolute top-2 right-2 z-10",
315-
"w-8 h-8 bg-separator border border-border-light rounded cursor-pointer",
316-
"items-center justify-center text-foreground transition-all duration-200",
343+
"h-6 w-6 rounded-md bg-separator/90 border border-border-light/80",
344+
"items-center justify-center text-[11px] font-semibold text-foreground transition-all duration-200",
345+
"shadow-[0_1px_2px_rgba(0,0,0,0.2)]",
317346
"hover:bg-hover hover:border-bg-light",
318347
"active:scale-95"
319348
)}
320349
>
321-
350+
×
322351
</button>
323352

324353
<TooltipWrapper inline>

0 commit comments

Comments
 (0)