Skip to content

Commit 3d8fc6c

Browse files
refactor(ui): update layout and styling for navigation and footer components
- Adjusted the left navigation strip scrollbar gap to overlay the divider for a cleaner look. - Simplified the bottom menu rule logic to always show the bottom hairline. - Updated the FloatingShield component to conditionally render based on layout width. - Enhanced the MessagesColumnFooter to integrate settings and shield buttons for wide layouts. - Removed deprecated components from the TelegramConnectFooterStrip for clarity. - Refactored SmartPurposeMenuWithDivider to improve scrollbar behavior and visual consistency. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 43da122 commit 3d8fc6c

7 files changed

Lines changed: 98 additions & 88 deletions

ui/components/AuthenticatedHomeLeftNavStrip.tsx

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -177,9 +177,8 @@ export function AuthenticatedHomeLeftNavStrip({
177177
: layoutIsWide
178178
? 0
179179
: AH.leftNavStripMarginTopPx;
180-
/** Bottom hairline: only after `firstBreakpoint`, and only when the split is actually multi-column. */
181-
const showBottomMenuRule =
182-
layoutIsWide && (chromeFromSplit ? splitMetrics.columnCount >= 2 : true);
180+
/** Bottom hairline under Feed/Messages/… — full strip width in every layout (music-bar track). */
181+
const showBottomMenuRule = true;
183182

184183
const fadeGradientIdRight = useId().replace(/[^a-zA-Z0-9_-]/g, "_");
185184
const fadeGradientIdLeft = useId().replace(/[^a-zA-Z0-9_-]/g, "_");
@@ -203,10 +202,8 @@ export function AuthenticatedHomeLeftNavStrip({
203202
const lineT = menuStripRuleThickness();
204203
/** Edge fades; matches `contentSideInsetPx` (15px) in theme. */
205204
const fadeW = AH.leftNavStripRightFadeWidthPx;
206-
const scrollbarGapAboveBorder = AH.leftNavStripScrollbarAboveBorderPx;
207-
const thumbBottomSnapped = snapToPixelGrid(
208-
(showBottomMenuRule ? lineT : 0) + scrollbarGapAboveBorder,
209-
);
205+
/** Thumb sits on the divider (music seek bar), not above it. */
206+
const thumbBottomSnapped = 0;
210207

211208
const onOuterLayout = useCallback((e: LayoutChangeEvent) => {
212209
setOuterW(Math.round(e.nativeEvent.layout.width));
@@ -450,10 +447,10 @@ export function AuthenticatedHomeLeftNavStrip({
450447
return {
451448
width: thumbSnapW,
452449
height: lineT,
453-
backgroundColor: colors.accent,
450+
backgroundColor: colors.primary,
454451
...(Platform.OS === "web" ? ({ willChange: "transform" } as ViewStyle) : null),
455452
};
456-
}, [showScrollbar, thumbW, thumbSnapW, colors.accent, lineT]);
453+
}, [showScrollbar, thumbW, thumbSnapW, colors.primary, lineT]);
457454

458455
const labelStyle = (active: boolean) => ({
459456
fontFamily: Platform.OS === "web" ? WEB_UI_SANS_STACK : FONT_UI_SANS_REGULAR,
@@ -635,7 +632,7 @@ export function AuthenticatedHomeLeftNavStrip({
635632
bottomRuleColorRole: "colors.highlight",
636633
scrollbarTrackVisible: showScrollbar,
637634
scrollbarThumbBottomOffsetPx: thumbBottomSnapped,
638-
scrollbarGapAboveBorderThemePx: scrollbarGapAboveBorder,
635+
scrollbarGapAboveBorderThemePx: AH.leftNavStripScrollbarAboveBorderPx,
639636
platform: Platform.OS,
640637
devicePixelRatio: dpr,
641638
alignmentChange: alignmentEvent,
@@ -659,7 +656,6 @@ export function AuthenticatedHomeLeftNavStrip({
659656
showScrollbar,
660657
lineT,
661658
thumbBottomSnapped,
662-
scrollbarGapAboveBorder,
663659
chromeFromSplit,
664660
splitMetrics,
665661
]);

ui/components/FloatingShield.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useEffect, useRef } from "react";
22
import { Animated, Pressable, StyleSheet, Text, useWindowDimensions, View } from "react-native";
3-
import { authenticatedHomeBottomBarDock, layout, useColors } from "../theme";
3+
import { layout, useColors } from "../theme";
44
import { useBottomBarLayout } from "./BottomBarLayoutContext";
55
import { useAppStrings } from "../../locales/AppStringsContext";
66
import { LiquidGlassShaderUndercover } from "./LiquidGlassShaderUndercover";
@@ -68,7 +68,6 @@ export function FloatingShield() {
6868
const { width: windowWidth } = useWindowDimensions();
6969
const pathname = useResolvedPathname();
7070
const { isAuthenticated } = useAuth();
71-
const bottomBarDock = authenticatedHomeBottomBarDock(pathname, windowWidth, isAuthenticated);
7271
/** Match authenticated home: narrow / welcome-style width keeps the stack on the right; wide moves it to the left. */
7372
const shieldOnRight = windowWidth <= AH.firstBreakpoint;
7473
const { barHeight: bottomBarHeight, footerDockedToScreenEdge } = useBottomBarLayout();
@@ -77,8 +76,8 @@ export function FloatingShield() {
7776

7877
const isAuthenticatedHome =
7978
isAuthenticated && (pathname === "/" || pathname === "" || pathname == null);
80-
const showTelegramConnectStrip =
81-
isAuthenticatedHome && bottomBarDock === "screenFooter" && shieldOnRight;
79+
/** Wide authenticated home: Settings + Shield live in the messages column footer (right of search). */
80+
const iconsInMessagesColumnFooter = isAuthenticatedHome && !shieldOnRight;
8281
const { openSettingsSheet } = useSettingsSheet();
8382
const chatListSearchActive = useMessagesChatListSearchActiveOptional();
8483

@@ -87,7 +86,7 @@ export function FloatingShield() {
8786
return null;
8887
}
8988

90-
if (showTelegramConnectStrip) {
89+
if (iconsInMessagesColumnFooter) {
9190
return null;
9291
}
9392

ui/components/MessagesColumnFooter.tsx

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,8 @@ function ConnectTelegramFooterButton({
201201
}
202202

203203
/**
204-
* Left-column footer: menu + search (wide), or liquid-glass Menu → Settings → Shield → Search (narrow).
204+
* Left-column footer: menu + search; wide also places Settings + Shield after search.
205+
* Narrow uses FloatingShield for Settings/Shield so the search field keeps width.
205206
* When Telegram is disconnected, shows Connect Telegram instead.
206207
*/
207208
export function MessagesColumnFooter({ showSearch = true }: Props) {
@@ -271,43 +272,47 @@ export function MessagesColumnFooter({ showSearch = true }: Props) {
271272
</Pressable>
272273
);
273274

274-
const settingsButton = (
275+
const settingsButtonWide = (
275276
<Pressable
276277
accessibilityRole="button"
277278
accessibilityLabel={t("settings.sheetTitle")}
278279
onPress={openSettingsSheet}
279-
style={styles.liquidGlassChipPressable}
280+
style={({ pressed }) => ({
281+
width: MENU_BTN_PX,
282+
height: MENU_BTN_PX,
283+
backgroundColor: colors.undercover,
284+
alignItems: "center",
285+
justifyContent: "center",
286+
opacity: pressed ? 0.75 : 1,
287+
flexShrink: 0,
288+
})}
280289
>
281-
<LiquidGlassShaderUndercover
282-
size={LIQUID_GLASS_CHIP_PX}
283-
phaseOffset={0.08}
284-
isLightTheme={isLightTheme}
285-
capturePointerEvents={false}
286-
>
287-
<SettingsIcon color={colors.primary} size={SETTINGS_ICON_SIZE_PX} />
288-
</LiquidGlassShaderUndercover>
290+
<SettingsIcon color={colors.primary} size={SETTINGS_ICON_SIZE_PX} />
289291
</Pressable>
290292
);
291293

292-
const shieldChip = (
293-
<View style={styles.liquidGlassChipPressable} pointerEvents="none">
294-
<LiquidGlassShaderUndercover
295-
size={LIQUID_GLASS_CHIP_PX}
296-
phaseOffset={0.41}
297-
isLightTheme={isLightTheme}
298-
capturePointerEvents={false}
299-
>
300-
<ShieldIcon
301-
powerColor={powerColor}
302-
width={SHIELD_ICON_WIDTH_PX}
303-
height={SHIELD_ICON_HEIGHT_PX}
304-
/>
305-
</LiquidGlassShaderUndercover>
294+
const shieldChipWide = (
295+
<View
296+
style={{
297+
width: MENU_BTN_PX,
298+
height: MENU_BTN_PX,
299+
backgroundColor: colors.undercover,
300+
alignItems: "center",
301+
justifyContent: "center",
302+
flexShrink: 0,
303+
}}
304+
pointerEvents="none"
305+
>
306+
<ShieldIcon
307+
powerColor={powerColor}
308+
width={SHIELD_ICON_WIDTH_PX}
309+
height={SHIELD_ICON_HEIGHT_PX}
310+
/>
306311
</View>
307312
);
308313

309314
const searchField = showSearch ? (
310-
<View style={{ flex: 1, minWidth: 0 }}>
315+
<View style={{ flex: 1, minWidth: isNarrow ? 96 : 0 }}>
311316
<MessageChatListSearchField
312317
value={chatListSearchQuery}
313318
onChangeText={setChatListSearchQuery}
@@ -336,15 +341,15 @@ export function MessagesColumnFooter({ showSearch = true }: Props) {
336341
) : (
337342
<>
338343
{menuButton}
339-
{settingsButton}
340-
{shieldChip}
341344
{searchField}
342345
</>
343346
)
344347
) : (
345348
<>
346349
{menuButton}
347350
{searchField}
351+
{settingsButtonWide}
352+
{shieldChipWide}
348353
</>
349354
)}
350355
</View>

ui/components/TelegramConnectFooterStrip.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ type Props = {
55

66
/**
77
* Formerly floated shield/settings above the narrow messages footer.
8-
* Those chips now live inside {@link MessagesColumnFooter} — keep this null.
8+
* Narrow home uses {@link FloatingShield}; wide home places them in {@link MessagesColumnFooter}.
99
*/
1010
export function TelegramConnectFooterStrip(_props: Props) {
1111
return null;

ui/components/smart/SmartPurposeMenuWithDivider.tsx

Lines changed: 43 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@ export function SmartPurposeMenuWithDivider({
9090
}: Props) {
9191
const colors = useColors();
9292
const lineT = ruleThicknessPx();
93-
const scrollGapAboveLinePx = layout.authenticatedHome.leftNavStripScrollbarAboveBorderPx;
9493
const contentInset = layout.contentSideInsetPx;
9594
const menuPaddingPx = MENU_SCROLL_PADDING_PX;
9695
const menuPaddingTotalPx = menuPaddingPx * 2;
@@ -161,7 +160,8 @@ export function SmartPurposeMenuWithDivider({
161160
);
162161
const thumbSnapLeft = snapScrollIndicatorCoordPx(thumbLeft);
163162
const thumbSnapW = Math.max(1, snapScrollIndicatorCoordPx(thumbW));
164-
const thumbBottomPx = snapScrollIndicatorCoordPx(lineT + scrollGapAboveLinePx);
163+
/** Overlay the divider track (music progress style), not a gap above it. */
164+
const thumbBottomPx = 0;
165165

166166
const scrollContentContainerStyle = useMemo(
167167
(): ViewStyle => ({
@@ -398,51 +398,53 @@ export function SmartPurposeMenuWithDivider({
398398
<View style={{ position: "relative", width: "100%", alignSelf: "stretch" }}>
399399
<View style={{ height: gapAboveDividerPx }} />
400400

401-
{showScrollbar ? (
402-
<View
403-
style={{
404-
position: "absolute",
405-
left: -contentInset,
406-
right: -contentInset,
407-
bottom: thumbBottomPx,
408-
paddingHorizontal: overflowLinePaddingPx,
409-
zIndex: 1,
410-
pointerEvents: "box-none",
411-
}}
412-
>
401+
<View style={{ position: "relative", width: "100%", alignSelf: "stretch" }}>
402+
<SmartGradientDivider
403+
variant={fits ? "gradient" : "solid"}
404+
horizontalPaddingPx={fits ? 0 : overflowLinePaddingPx}
405+
/>
406+
407+
{showScrollbar ? (
413408
<View
414-
onLayout={onTrackLayout}
415409
style={{
416-
height: lineT,
417-
overflow: "visible",
410+
position: "absolute",
411+
left: -contentInset,
412+
right: -contentInset,
413+
bottom: thumbBottomPx,
414+
paddingHorizontal: overflowLinePaddingPx,
415+
zIndex: 1,
416+
pointerEvents: "box-none",
418417
}}
419418
>
420-
<ScrollIndicatorDragHandle
421-
axis="horizontal"
422-
trackSpan={scrollTrackWidth}
423-
thumbSpan={thumbSnapW}
424-
thumbOffset={thumbSnapLeft}
425-
scrollRange={scrollRange}
426-
onScrollTo={scrollToX}
427-
crossAxisVisualSpan={lineT}
419+
<View
420+
onLayout={onTrackLayout}
421+
style={{
422+
height: lineT,
423+
overflow: "visible",
424+
}}
428425
>
429-
<View
430-
style={{
431-
width: thumbSnapW,
432-
height: lineT,
433-
backgroundColor: colors.accent,
434-
...(Platform.OS === "web" ? ({ willChange: "transform" } as ViewStyle) : null),
435-
}}
436-
/>
437-
</ScrollIndicatorDragHandle>
426+
<ScrollIndicatorDragHandle
427+
axis="horizontal"
428+
trackSpan={scrollTrackWidth}
429+
thumbSpan={thumbSnapW}
430+
thumbOffset={thumbSnapLeft}
431+
scrollRange={scrollRange}
432+
onScrollTo={scrollToX}
433+
crossAxisVisualSpan={lineT}
434+
>
435+
<View
436+
style={{
437+
width: thumbSnapW,
438+
height: lineT,
439+
backgroundColor: colors.primary,
440+
...(Platform.OS === "web" ? ({ willChange: "transform" } as ViewStyle) : null),
441+
}}
442+
/>
443+
</ScrollIndicatorDragHandle>
444+
</View>
438445
</View>
439-
</View>
440-
) : null}
441-
442-
<SmartGradientDivider
443-
variant={fits ? "gradient" : "solid"}
444-
horizontalPaddingPx={fits ? 0 : overflowLinePaddingPx}
445-
/>
446+
) : null}
447+
</View>
446448
</View>
447449
</>
448450
);

ui/screens/HomeAuthenticatedScreen.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1659,7 +1659,11 @@ function HomeAuthenticatedScreenMain() {
16591659
const homeLeftColumn = (
16601660
<>
16611661
{isWideHome ? homeLeftNavStrip : null}
1662-
{!isWideHome ? homeHeaderRow : null}
1662+
{!isWideHome ? (
1663+
<View style={{ paddingTop: layout.authenticatedHome.contentInsetTop, width: "100%" }}>
1664+
{homeHeaderRow}
1665+
</View>
1666+
) : null}
16631667
{!isWideHome ? homeLeftNavStrip : null}
16641668
{homeLeftScrollShell(
16651669
<HspScrollColumn

ui/theme.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,11 @@ export const layout = {
292292
/**
293293
* Left column top nav strip: gap (px) between the horizontal scroll thumb and the bottom rule (strip width).
294294
*/
295-
leftNavStripScrollbarAboveBorderPx: 3,
295+
/**
296+
* Gap (px) between the Feed/Messages/… horizontal scroll thumb and the strip bottom hairline.
297+
* `0` = thumb overlays the divider (music progress-bar style).
298+
*/
299+
leftNavStripScrollbarAboveBorderPx: 0,
296300
/** Left column nav strip: width (px) of each horizontal edge fade (15px; mirrors `contentSideInsetPx`). */
297301
leftNavStripRightFadeWidthPx: contentSideInsetPx,
298302
/** Vertical scroll thumb layer inside {@link HspScrollColumn}; above {@link splitPaneDividerOverlayZIndex} at column seams. */

0 commit comments

Comments
 (0)