diff --git a/packages/app/src/components/message.tsx b/packages/app/src/components/message.tsx index b1aae586b..9b72a80c4 100644 --- a/packages/app/src/components/message.tsx +++ b/packages/app/src/components/message.tsx @@ -646,6 +646,9 @@ const expandableBadgeStylesheet = StyleSheet.create((theme) => ({ marginLeft: theme.spacing[1], flexShrink: 0, }, + chevronExpanded: { + transform: [{ rotate: "90deg" }], + }, detailWrapper: { borderBottomLeftRadius: theme.borderRadius.lg, borderBottomRightRadius: theme.borderRadius.lg, @@ -1289,6 +1292,8 @@ const ExpandableBadge = memo(function ExpandableBadge({ const { theme } = useUnistyles(); const resolvedDisableOuterSpacing = useDisableOuterSpacing(disableOuterSpacing); + const [isHovered, setIsHovered] = useState(false); + const [isPressed, setIsPressed] = useState(false); const isInteractive = Boolean(onToggle); const hasDetailContent = Boolean(renderDetails); const detailContent = @@ -1479,6 +1484,107 @@ const ExpandableBadge = memo(function ExpandableBadge({ } as never) : null; + const containerStyle = useMemo( + () => [ + expandableBadgeStylesheet.container, + !resolvedDisableOuterSpacing && + (isLastInSequence + ? expandableBadgeStylesheet.containerLastInSequence + : expandableBadgeStylesheet.containerSpacing), + style, + ], + [isLastInSequence, resolvedDisableOuterSpacing, style] + ); + + const pressableStyle = useMemo( + () => [ + expandableBadgeStylesheet.pressable, + isPressed && isInteractive + ? expandableBadgeStylesheet.pressablePressed + : null, + isExpanded && expandableBadgeStylesheet.pressableExpanded, + ], + [isExpanded, isInteractive, isPressed] + ); + + const accessibilityState = useMemo( + () => (isInteractive ? { expanded: isExpanded } : undefined), + [isExpanded, isInteractive] + ); + + const labelStyle = useMemo( + () => [ + expandableBadgeStylesheet.label, + isLoading && expandableBadgeStylesheet.labelLoading, + ], + [isLoading] + ); + + const shimmerLabelTextStyle = useMemo( + () => [ + expandableBadgeStylesheet.label, + isLoading && expandableBadgeStylesheet.labelLoading, + expandableBadgeStylesheet.shimmerText, + shimmerLabelStyle, + ], + [isLoading, shimmerLabelStyle] + ); + + const shimmerSecondaryTextStyle = useMemo( + () => [ + expandableBadgeStylesheet.secondaryLabel, + expandableBadgeStylesheet.shimmerText, + shimmerSecondaryStyle, + ], + [shimmerSecondaryStyle] + ); + + const nativeShimmerTrackStyle = useMemo( + () => [ + expandableBadgeStylesheet.nativeShimmerTrack, + { width: labelRowWidth, height: labelRowHeight }, + ], + [labelRowHeight, labelRowWidth] + ); + + const nativeShimmerMaskStyle = useMemo( + () => [ + expandableBadgeStylesheet.shimmerMaskRow, + { width: labelRowWidth, height: labelRowHeight }, + ], + [labelRowHeight, labelRowWidth] + ); + + const nativeLabelMaskStyle = useMemo( + () => [expandableBadgeStylesheet.label, { color: "#000000", opacity: 1 }], + [] + ); + + const nativeSecondaryMaskStyle = useMemo( + () => [ + expandableBadgeStylesheet.secondaryLabel, + { color: "#000000", opacity: 1 }, + ], + [] + ); + + const nativeShimmerPeakCombinedStyle = useMemo( + () => [ + expandableBadgeStylesheet.nativeShimmerPeak, + nativeShimmerPeakStyle, + { width: nativeShimmerPeakWidth, height: labelRowHeight }, + ], + [labelRowHeight, nativeShimmerPeakStyle, nativeShimmerPeakWidth] + ); + + const chevronStyle = useMemo( + () => [ + expandableBadgeStylesheet.chevron, + isExpanded && expandableBadgeStylesheet.chevronExpanded, + ], + [isExpanded] + ); + const IconComponent = icon; const iconColor = isError ? theme.colors.destructive @@ -1493,186 +1599,142 @@ const ExpandableBadge = memo(function ExpandableBadge({ return ( setIsHovered(true) : undefined} + onHoverOut={ + isInteractive + ? () => { + setIsHovered(false); + setIsPressed(false); + } + : undefined + } + onPressIn={isInteractive ? () => setIsPressed(true) : undefined} + onPressOut={isInteractive ? () => setIsPressed(false) : undefined} disabled={!isInteractive} accessibilityRole={isInteractive ? "button" : undefined} - accessibilityState={isInteractive ? { expanded: isExpanded } : undefined} - style={({ pressed }) => [ - expandableBadgeStylesheet.pressable, - pressed && isInteractive - ? expandableBadgeStylesheet.pressablePressed - : null, - isExpanded && expandableBadgeStylesheet.pressableExpanded, - ]} + accessibilityState={accessibilityState} + style={pressableStyle} > - {({ hovered }) => ( - <> - - {iconNode} + + {iconNode} + + + {label} + + {secondaryLabel ? ( + + {secondaryLabel} + + ) : ( + + )} + {isWebShimmer ? ( {label} {secondaryLabel ? ( {secondaryLabel} ) : ( )} - {isWebShimmer ? ( - - - {label} - - {secondaryLabel ? ( + + ) : null} + {isNativeShimmer ? ( + + - {secondaryLabel} + {label} - ) : ( - - )} - - ) : null} - {isNativeShimmer ? ( + {secondaryLabel ? ( + + {secondaryLabel} + + ) : ( + + )} + + } + > - - + + + - {label} - - {secondaryLabel ? ( - - {secondaryLabel} - - ) : ( - - )} - - } - > - - - - - - - - - - - - - - - + + + + + + + + - ) : null} + - {isInteractive && hovered ? ( - - ) : null} - - - )} + ) : null} + + {isInteractive && isHovered ? ( + + ) : null} + {detailContent ? ( ({ }, outline: { backgroundColor: "transparent", - borderColor: theme.colors.border, + borderColor: theme.colors.borderAccent, }, ghost: { backgroundColor: "transparent", @@ -59,7 +59,7 @@ const styles = StyleSheet.create((theme) => ({ text: { color: theme.colors.foreground, fontSize: theme.fontSize.sm, - fontWeight: theme.fontWeight.medium, + fontWeight: theme.fontWeight.normal, }, textDefault: { color: theme.colors.palette.white, diff --git a/packages/app/src/desktop/components/desktop-permission-row.tsx b/packages/app/src/desktop/components/desktop-permission-row.tsx index bc4240431..628dcaa25 100644 --- a/packages/app/src/desktop/components/desktop-permission-row.tsx +++ b/packages/app/src/desktop/components/desktop-permission-row.tsx @@ -41,7 +41,7 @@ export function DesktopPermissionRow({ Granted ) : ( - )} diff --git a/packages/app/src/desktop/components/desktop-permissions-section.tsx b/packages/app/src/desktop/components/desktop-permissions-section.tsx index 7391d0142..c268158e1 100644 --- a/packages/app/src/desktop/components/desktop-permissions-section.tsx +++ b/packages/app/src/desktop/components/desktop-permissions-section.tsx @@ -1,8 +1,10 @@ -import { View, Text, Pressable } from "react-native"; +import { View, Text } from "react-native"; import { StyleSheet, useUnistyles } from "react-native-unistyles"; import { RotateCw } from "lucide-react-native"; +import { Button } from "@/components/ui/button"; import { DesktopPermissionRow } from "@/desktop/components/desktop-permission-row"; import { useDesktopPermissions } from "@/desktop/permissions/use-desktop-permissions"; +import { settingsStyles } from "@/styles/settings"; export function DesktopPermissionsSection() { const { theme } = useUnistyles(); @@ -22,26 +24,23 @@ export function DesktopPermissionsSection() { const isBusy = isRefreshing || requestingPermission !== null; return ( - + - Desktop Permissions - [ - styles.permissionRefreshButton, - isBusy && styles.permissionRefreshButtonDisabled, - pressed && { opacity: 0.85 }, - ]} + Desktop Permissions + - + ({ - section: { - marginBottom: theme.spacing[6], - }, - sectionTitle: { - color: theme.colors.foregroundMuted, - fontSize: theme.fontSize.xs, - fontWeight: theme.fontWeight.normal, - marginBottom: 0, - marginLeft: theme.spacing[1], - }, permissionSectionHeader: { flexDirection: "row", alignItems: "center", @@ -82,24 +71,4 @@ const styles = StyleSheet.create((theme) => ({ gap: theme.spacing[2], marginBottom: theme.spacing[3], }, - permissionRefreshButton: { - width: 34, - height: 34, - borderRadius: theme.borderRadius.md, - borderWidth: 1, - borderColor: theme.colors.border, - backgroundColor: theme.colors.surface2, - alignItems: "center", - justifyContent: "center", - }, - permissionRefreshButtonDisabled: { - opacity: theme.opacity[50], - }, - audioCard: { - backgroundColor: theme.colors.surface2, - borderRadius: theme.borderRadius.lg, - borderWidth: 1, - borderColor: theme.colors.border, - overflow: "hidden", - }, })); diff --git a/packages/app/src/desktop/components/desktop-updates-section.tsx b/packages/app/src/desktop/components/desktop-updates-section.tsx index 68ad0b0ba..e0d77d0a7 100644 --- a/packages/app/src/desktop/components/desktop-updates-section.tsx +++ b/packages/app/src/desktop/components/desktop-updates-section.tsx @@ -3,8 +3,9 @@ import { ActivityIndicator, Alert, Image, Pressable, Text, View } from "react-na import * as Clipboard from "expo-clipboard"; import * as QRCode from "qrcode"; import { useFocusEffect } from "@react-navigation/native"; -import { StyleSheet } from "react-native-unistyles"; -import { ArrowUpRight } from "lucide-react-native"; +import { StyleSheet, useUnistyles } from "react-native-unistyles"; +import { settingsStyles } from "@/styles/settings"; +import { ArrowUpRight, Play, Pause, RotateCw, Terminal, Copy, FileText, Smartphone } from "lucide-react-native"; import { AdaptiveModalSheet } from "@/components/adaptive-modal-sheet"; import { Button } from "@/components/ui/button"; import { useAppSettings } from "@/hooks/use-settings"; @@ -35,6 +36,7 @@ export interface LocalDaemonSectionProps { } export function LocalDaemonSection({ appVersion }: LocalDaemonSectionProps) { + const { theme } = useUnistyles(); const showSection = shouldUseManagedDesktopDaemon(); const { settings, updateSettings } = useAppSettings(); const [managedStatus, setManagedStatus] = useState(null); @@ -331,9 +333,9 @@ export function LocalDaemonSection({ appVersion }: LocalDaemonSectionProps) { } return ( - + - Built-in daemon + Built-in daemon void openExternalUrl(ADVANCED_DAEMON_SETTINGS_URL)} @@ -343,7 +345,7 @@ export function LocalDaemonSection({ appVersion }: LocalDaemonSectionProps) { - + Status @@ -364,9 +366,11 @@ export function LocalDaemonSection({ appVersion }: LocalDaemonSectionProps) { ) : null} @@ -488,7 +493,7 @@ export function LocalDaemonSection({ appVersion }: LocalDaemonSectionProps) { {cliInstallInstructions?.commands ?? ""} - @@ -636,9 +641,6 @@ function PairingOfferDialogContent(input: { } const styles = StyleSheet.create((theme) => ({ - section: { - marginBottom: theme.spacing[6], - }, sectionHeader: { alignItems: "center", flexDirection: "row", @@ -646,11 +648,6 @@ const styles = StyleSheet.create((theme) => ({ marginBottom: theme.spacing[3], marginLeft: theme.spacing[1], }, - sectionTitle: { - color: theme.colors.foregroundMuted, - fontSize: theme.fontSize.xs, - fontWeight: theme.fontWeight.normal, - }, sectionLink: { alignItems: "center", flexDirection: "row", @@ -660,13 +657,6 @@ const styles = StyleSheet.create((theme) => ({ color: theme.colors.foregroundMuted, fontSize: theme.fontSize.xs, }, - card: { - backgroundColor: theme.colors.surface2, - borderRadius: theme.borderRadius.lg, - borderWidth: 1, - borderColor: theme.colors.border, - overflow: "hidden", - }, row: { flexDirection: "row", alignItems: "center", @@ -692,12 +682,6 @@ const styles = StyleSheet.create((theme) => ({ alignItems: "flex-end", gap: 2, }, - primaryActionButton: { - minWidth: 124, - }, - secondaryActionButton: { - minWidth: 112, - }, rowTitle: { color: theme.colors.foreground, fontSize: theme.fontSize.base, diff --git a/packages/app/src/screens/settings-screen.tsx b/packages/app/src/screens/settings-screen.tsx index 162a7b02d..9ee2c3ded 100644 --- a/packages/app/src/screens/settings-screen.tsx +++ b/packages/app/src/screens/settings-screen.tsx @@ -42,6 +42,7 @@ import { LocalDaemonSection } from "@/desktop/components/desktop-updates-section import { useDesktopAppUpdater } from "@/desktop/updates/use-desktop-app-updater"; import { formatVersionWithPrefix } from "@/desktop/updates/desktop-updates"; import { resolveAppVersion } from "@/utils/app-version"; +import { settingsStyles } from "@/styles/settings"; const delay = (ms: number) => new Promise((resolve) => { @@ -112,16 +113,6 @@ const styles = StyleSheet.create((theme) => ({ maxWidth: 720, alignSelf: "center", }, - section: { - marginBottom: theme.spacing[6], - }, - sectionTitle: { - color: theme.colors.foregroundMuted, - fontSize: theme.fontSize.xs, - fontWeight: theme.fontWeight.normal, - marginBottom: theme.spacing[3], - marginLeft: theme.spacing[1], - }, label: { color: theme.colors.foregroundMuted, fontSize: theme.fontSize.xs, @@ -139,10 +130,6 @@ const styles = StyleSheet.create((theme) => ({ }, // Host card styles hostCard: { - backgroundColor: theme.colors.surface2, - borderRadius: theme.borderRadius.lg, - borderWidth: 1, - borderColor: theme.colors.border, marginBottom: theme.spacing[3], overflow: "hidden", }, @@ -286,10 +273,6 @@ const styles = StyleSheet.create((theme) => ({ }, // Add/Edit form formCard: { - backgroundColor: theme.colors.surface2, - borderRadius: theme.borderRadius.lg, - borderWidth: 1, - borderColor: theme.colors.border, padding: theme.spacing[4], marginBottom: theme.spacing[3], gap: theme.spacing[4], @@ -328,10 +311,6 @@ const styles = StyleSheet.create((theme) => ({ }, // Audio settings card audioCard: { - backgroundColor: theme.colors.surface2, - borderRadius: theme.borderRadius.lg, - borderWidth: 1, - borderColor: theme.colors.border, overflow: "hidden", }, audioRow: { @@ -375,10 +354,6 @@ const styles = StyleSheet.create((theme) => ({ }, // Empty state emptyCard: { - backgroundColor: theme.colors.surface2, - borderRadius: theme.borderRadius.lg, - borderWidth: 1, - borderColor: theme.colors.border, padding: theme.spacing[4], marginBottom: theme.spacing[3], }, @@ -389,10 +364,6 @@ const styles = StyleSheet.create((theme) => ({ }, // Dev section devCard: { - backgroundColor: theme.colors.surface2, - borderRadius: theme.borderRadius.lg, - borderWidth: 1, - borderColor: theme.colors.border, overflow: "hidden", }, devButton: { @@ -501,7 +472,7 @@ function DesktopAppUpdateRow() {