A React Native app for the ultimate thinking partner.

feat(ui): improve sidebar UX with borderless design and persistent state

- Remove all internal borders for cleaner, modern appearance
- Add delayed fade-in animation for sidebar content during expansion
- Keep sidebar open when navigating between sections on desktop
- Maintain auto-close behavior on mobile overlay for better UX
- Simplify styling and eliminate border alignment issues

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

+41 -35
+41 -35
src/components/AppSidebar.tsx
··· 103 103 style={[ 104 104 isOverlay ? styles.sidebarOverlay : styles.sidebarContainer, 105 105 { 106 - paddingTop: insets.top, 107 106 backgroundColor: theme.colors.background.secondary, 108 107 borderRightColor: theme.colors.border.primary, 109 108 ...(isOverlay ··· 125 124 outputRange: [0, 280], 126 125 }), 127 126 }), 128 - opacity: animationValue.interpolate({ 129 - inputRange: [0, 0.3, 1], 130 - outputRange: [0, 0.8, 1], 131 - }), 127 + opacity: isOverlay 128 + ? animationValue.interpolate({ 129 + inputRange: [0, 0.3, 1], 130 + outputRange: [0, 0.8, 1], 131 + }) 132 + : 1, // Don't animate overall opacity in push mode 132 133 }, 133 134 ]} 134 135 > 135 - <View 136 + {/* Content wrapper with delayed fade-in for push mode */} 137 + <Animated.View 136 138 style={[ 137 - styles.sidebarHeader, 138 - { borderBottomColor: theme.colors.border.primary }, 139 + styles.contentWrapper, 140 + { 141 + opacity: isOverlay 142 + ? 1 // Overlay mode: content is always visible once sidebar slides in 143 + : animationValue.interpolate({ 144 + inputRange: [0, 0.6, 1], 145 + outputRange: [0, 0, 1], // Stay hidden until 60% then fade in 146 + }), 147 + }, 139 148 ]} 140 149 > 141 - <Text style={[styles.sidebarTitle, { color: theme.colors.text.primary }]}> 142 - Menu 143 - </Text> 144 - <TouchableOpacity onPress={onClose} style={styles.closeSidebar}> 145 - <Ionicons name="close" size={24} color={theme.colors.text.primary} /> 146 - </TouchableOpacity> 147 - </View> 150 + <View 151 + style={[ 152 + styles.sidebarHeader, 153 + { 154 + paddingTop: insets.top + 16, 155 + paddingBottom: 16, 156 + }, 157 + ]} 158 + > 159 + <Text style={[styles.sidebarTitle, { color: theme.colors.text.primary }]}> 160 + Menu 161 + </Text> 162 + <TouchableOpacity onPress={onClose} style={styles.closeSidebar}> 163 + <Ionicons name="close" size={24} color={theme.colors.text.primary} /> 164 + </TouchableOpacity> 165 + </View> 148 166 149 - <FlatList 150 - style={{ flex: 1 }} 151 - contentContainerStyle={{ flexGrow: 1 }} 152 - ListHeaderComponent={ 167 + <FlatList 168 + style={{ flex: 1 }} 169 + contentContainerStyle={{ flexGrow: 1 }} 170 + ListHeaderComponent={ 153 171 <View style={styles.menuItems}> 154 172 {/* You */} 155 173 <TouchableOpacity 156 174 style={[ 157 175 styles.menuItem, 158 - { borderBottomColor: theme.colors.border.primary }, 159 176 currentView === 'you' && { backgroundColor: theme.colors.background.tertiary }, 160 177 ]} 161 178 onPress={() => { 162 - onClose(); 163 179 onYouPress(); 164 180 }} 165 181 > ··· 177 193 <TouchableOpacity 178 194 style={[ 179 195 styles.menuItem, 180 - { borderBottomColor: theme.colors.border.primary }, 181 196 currentView === 'chat' && { backgroundColor: theme.colors.background.tertiary }, 182 197 ]} 183 198 onPress={() => { 184 - onClose(); 185 199 onChatPress(); 186 200 }} 187 201 > ··· 199 213 <TouchableOpacity 200 214 style={[ 201 215 styles.menuItem, 202 - { borderBottomColor: theme.colors.border.primary }, 203 216 currentView === 'knowledge' && { backgroundColor: theme.colors.background.tertiary }, 204 217 ]} 205 218 onPress={() => { 206 - onClose(); 207 219 onKnowledgePress(); 208 220 }} 209 221 > ··· 221 233 <TouchableOpacity 222 234 style={[ 223 235 styles.menuItem, 224 - { borderBottomColor: theme.colors.border.primary }, 225 236 currentView === 'settings' && { backgroundColor: theme.colors.background.tertiary }, 226 237 ]} 227 238 onPress={() => { 228 - onClose(); 229 239 onSettingsPress(); 230 240 }} 231 241 > ··· 243 253 <TouchableOpacity 244 254 style={[ 245 255 styles.menuItem, 246 - { borderBottomColor: theme.colors.border.primary }, 247 256 ]} 248 257 onPress={onThemeToggle} 249 258 > ··· 261 270 <TouchableOpacity 262 271 style={[ 263 272 styles.menuItem, 264 - { borderBottomColor: theme.colors.border.primary }, 265 273 ]} 266 274 onPress={handleOpenInBrowser} 267 275 disabled={!agentId} ··· 281 289 <TouchableOpacity 282 290 style={[ 283 291 styles.menuItem, 284 - { borderBottomColor: theme.colors.border.primary }, 285 292 ]} 286 293 onPress={handleRefreshAgent} 287 294 > ··· 305 312 <TouchableOpacity 306 313 style={[ 307 314 styles.menuItem, 308 - { borderBottomColor: theme.colors.border.primary }, 309 315 ]} 310 316 onPress={() => { 311 - onClose(); 312 317 onLogout(); 313 318 }} 314 319 > ··· 326 331 data={[]} 327 332 renderItem={() => null} 328 333 /> 334 + </Animated.View> 329 335 </Animated.View> 330 336 ); 331 337 } ··· 346 352 borderRightWidth: 1, 347 353 overflow: 'hidden', 348 354 }, 355 + contentWrapper: { 356 + flex: 1, 357 + }, 349 358 sidebarHeader: { 350 359 flexDirection: 'row', 351 360 justifyContent: 'space-between', 352 361 alignItems: 'center', 353 362 paddingHorizontal: 16, 354 - paddingVertical: 16, 355 - borderBottomWidth: 1, 356 363 }, 357 364 closeSidebar: { 358 365 padding: 8, ··· 369 376 alignItems: 'center', 370 377 paddingHorizontal: 20, 371 378 paddingVertical: 16, 372 - borderBottomWidth: StyleSheet.hairlineWidth, 373 379 }, 374 380 menuItemText: { 375 381 fontSize: 16,