A personal media tracker built on the AT Protocol opnshelf.xyz
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: integrate Tailwind CSS and Uniwind for styling, update Home and Search screens with new styles

+313 -220
+1
apps/mobile/App.tsx
··· 1 + import './src/global.css'; 1 2 import { StatusBar } from 'expo-status-bar'; 2 3 import { NavigationContainer } from '@react-navigation/native'; 3 4 import { createNativeStackNavigator } from '@react-navigation/native-stack';
+14
apps/mobile/metro.config.js
··· 1 + // Learn more https://docs.expo.io/guides/customizing-metro 2 + const { getDefaultConfig } = require('expo/metro-config'); 3 + const { withUniwindConfig } = require('uniwind/metro'); 4 + 5 + /** @type {import('expo/metro-config').MetroConfig} */ 6 + const config = getDefaultConfig(__dirname); 7 + 8 + module.exports = withUniwindConfig(config, { 9 + // relative path to your global.css file (from previous step) 10 + cssEntryFile: './src/global.css', 11 + // (optional) path where we gonna auto-generate typings 12 + // defaults to project's root 13 + dtsFile: './src/uniwind-types.d.ts' 14 + });
+3 -1
apps/mobile/package.json
··· 20 20 "react": "19.1.0", 21 21 "react-native": "0.81.5", 22 22 "react-native-safe-area-context": "~5.4.0", 23 - "react-native-screens": "~4.10.0" 23 + "react-native-screens": "~4.10.0", 24 + "tailwindcss": "^4.1.18", 25 + "uniwind": "^1.2.7" 24 26 }, 25 27 "devDependencies": { 26 28 "@types/react": "~19.1.0",
+2
apps/mobile/src/global.css
··· 1 + @import 'tailwindcss'; 2 + @import 'uniwind';
+27 -82
apps/mobile/src/screens/HomeScreen.tsx
··· 1 1 import type { NativeStackScreenProps } from '@react-navigation/native-stack'; 2 2 import { Ionicons } from '@expo/vector-icons'; 3 - import { StyleSheet, Text, TouchableOpacity, View } from 'react-native'; 3 + import { Text, TouchableOpacity, View } from 'react-native'; 4 4 import type { RootStackParamList } from '../navigation'; 5 5 6 6 type Props = NativeStackScreenProps<RootStackParamList, 'Home'>; 7 7 8 8 export function HomeScreen({ navigation }: Props) { 9 9 return ( 10 - <View style={styles.container}> 11 - <View style={styles.hero}> 12 - <View style={styles.iconWrap}> 10 + <View className="flex-1 bg-gray-950 px-4 pt-12 pb-6"> 11 + <View className="items-center mb-12"> 12 + <View className="mb-6"> 13 13 <Ionicons name="film" size={48} color="#a855f7" /> 14 14 </View> 15 - <Text style={styles.title}>OpnShelf</Text> 16 - <Text style={styles.subtitle}> 15 + <Text className="text-4xl font-bold text-gray-50 mb-4">OpnShelf</Text> 16 + <Text className="text-lg text-gray-400 text-center mb-8"> 17 17 Your personal media tracker powered by AT Protocol 18 18 </Text> 19 19 <TouchableOpacity 20 - style={styles.cta} 21 - onPress={() => navigation.navigate('Search')} 20 + className="flex-row items-center gap-2 bg-violet-600 py-3 px-6 rounded-lg" 21 + onPress={() => navigation.navigate('Search', {})} 22 22 activeOpacity={0.8} 23 23 > 24 24 <Ionicons name="search" size={20} color="#fff" /> 25 - <Text style={styles.ctaText}>Search Movies</Text> 25 + <Text className="text-white text-base font-semibold"> 26 + Search Movies 27 + </Text> 26 28 </TouchableOpacity> 27 29 </View> 28 30 29 - <View style={styles.cards}> 30 - <View style={styles.card}> 31 - <Text style={styles.cardTitle}>Track Your Media</Text> 32 - <Text style={styles.cardText}> 31 + <View className="gap-6"> 32 + <View className="bg-gray-900 p-6 rounded-lg border border-gray-800"> 33 + <Text className="text-lg font-semibold text-gray-50 mb-2"> 34 + Track Your Media 35 + </Text> 36 + <Text className="text-sm text-gray-400"> 33 37 Keep track of movies, shows, and games you've watched and played 34 38 </Text> 35 39 </View> 36 - <View style={styles.card}> 37 - <Text style={styles.cardTitle}>Own Your Data</Text> 38 - <Text style={styles.cardText}> 40 + <View className="bg-gray-900 p-6 rounded-lg border border-gray-800"> 41 + <Text className="text-lg font-semibold text-gray-50 mb-2"> 42 + Own Your Data 43 + </Text> 44 + <Text className="text-sm text-gray-400"> 39 45 Built on AT Protocol - your data belongs to you 40 46 </Text> 41 47 </View> 42 - <View style={styles.card}> 43 - <Text style={styles.cardTitle}>Discover & Share</Text> 44 - <Text style={styles.cardText}> 48 + <View className="bg-gray-900 p-6 rounded-lg border border-gray-800"> 49 + <Text className="text-lg font-semibold text-gray-50 mb-2"> 50 + Discover & Share 51 + </Text> 52 + <Text className="text-sm text-gray-400"> 45 53 See what others are watching and share your favorites 46 54 </Text> 47 55 </View> ··· 49 57 </View> 50 58 ); 51 59 } 52 - 53 - const styles = StyleSheet.create({ 54 - container: { 55 - flex: 1, 56 - backgroundColor: '#030712', 57 - paddingHorizontal: 16, 58 - paddingTop: 48, 59 - paddingBottom: 24, 60 - }, 61 - hero: { 62 - alignItems: 'center', 63 - marginBottom: 48, 64 - }, 65 - iconWrap: { 66 - marginBottom: 24, 67 - }, 68 - title: { 69 - fontSize: 40, 70 - fontWeight: '700', 71 - color: '#f9fafb', 72 - marginBottom: 16, 73 - }, 74 - subtitle: { 75 - fontSize: 18, 76 - color: '#9ca3af', 77 - marginBottom: 32, 78 - textAlign: 'center', 79 - }, 80 - cta: { 81 - flexDirection: 'row', 82 - alignItems: 'center', 83 - gap: 8, 84 - backgroundColor: '#9333ea', 85 - paddingVertical: 12, 86 - paddingHorizontal: 24, 87 - borderRadius: 8, 88 - }, 89 - ctaText: { 90 - color: '#fff', 91 - fontSize: 16, 92 - fontWeight: '600', 93 - }, 94 - cards: { 95 - gap: 24, 96 - }, 97 - card: { 98 - backgroundColor: '#111827', 99 - padding: 24, 100 - borderRadius: 8, 101 - borderWidth: 1, 102 - borderColor: '#1f2937', 103 - }, 104 - cardTitle: { 105 - fontSize: 18, 106 - fontWeight: '600', 107 - color: '#f9fafb', 108 - marginBottom: 8, 109 - }, 110 - cardText: { 111 - fontSize: 14, 112 - color: '#9ca3af', 113 - }, 114 - });
+36 -137
apps/mobile/src/screens/SearchScreen.tsx
··· 6 6 ActivityIndicator, 7 7 FlatList, 8 8 Image, 9 - StyleSheet, 10 9 Text, 11 10 TextInput, 12 11 View, 13 12 } from 'react-native'; 14 13 import { useCallback, useEffect, useRef, useState } from 'react'; 15 14 import type { RootStackParamList } from '../navigation'; 15 + 16 16 type Props = NativeStackScreenProps<RootStackParamList, 'Search'>; 17 17 18 18 const DEBOUNCE_MS = 300; ··· 27 27 28 28 function MovieCard({ movie }: { movie: MovieItem }) { 29 29 return ( 30 - <View style={styles.movieCard}> 31 - <View style={styles.posterWrap}> 30 + <View className="flex-1 min-w-0"> 31 + <View className="aspect-2/3 bg-gray-900 rounded-lg overflow-hidden mb-2"> 32 32 {movie.poster_path ? ( 33 33 <Image 34 34 source={{ uri: `${POSTER_BASE}${movie.poster_path}` }} 35 - style={styles.poster} 35 + className="w-full h-full" 36 36 resizeMode="cover" 37 37 /> 38 38 ) : ( 39 - <View style={styles.posterPlaceholder}> 40 - <Text style={styles.posterPlaceholderText}>No poster</Text> 39 + <View className="flex-1 justify-center items-center"> 40 + <Text className="text-gray-500 text-xs">No poster</Text> 41 41 </View> 42 42 )} 43 43 </View> 44 - <Text style={styles.movieTitle} numberOfLines={2}> 44 + <Text className="text-sm font-semibold text-gray-50 mb-1" numberOfLines={2}> 45 45 {movie.title} 46 46 </Text> 47 47 {movie.release_date ? ( 48 - <Text style={styles.movieYear}>{movie.release_date.split('-')[0]}</Text> 48 + <Text className="text-xs text-gray-500"> 49 + {movie.release_date.split('-')[0]} 50 + </Text> 49 51 ) : null} 50 52 </View> 51 53 ); ··· 61 63 if (debounceRef.current) clearTimeout(debounceRef.current); 62 64 const trimmed = query.trim(); 63 65 if (trimmed !== searchQuery) { 64 - debounceRef.current = setTimeout(() => setSearchQuery(trimmed), DEBOUNCE_MS); 66 + debounceRef.current = setTimeout( 67 + () => setSearchQuery(trimmed), 68 + DEBOUNCE_MS, 69 + ); 65 70 } 66 71 return () => { 67 72 if (debounceRef.current) clearTimeout(debounceRef.current); ··· 79 84 80 85 const renderItem = useCallback( 81 86 ({ item }: { item: MovieItem }) => <MovieCard movie={item} />, 82 - [] 87 + [], 83 88 ); 84 89 85 90 const keyExtractor = useCallback((item: MovieItem) => String(item.id), []); 86 91 87 92 return ( 88 - <View style={styles.container}> 89 - <Text style={styles.title}>Search Movies</Text> 93 + <View className="flex-1 bg-gray-950 px-4 pt-12 pb-6"> 94 + <Text className="text-3xl font-bold text-gray-50 mb-6"> 95 + Search Movies 96 + </Text> 90 97 91 - <View style={styles.inputWrap}> 92 - <Ionicons 93 - name="search" 94 - size={20} 95 - color="#9ca3af" 96 - style={styles.inputIcon} 97 - /> 98 + <View className="flex-row items-center bg-gray-900 border border-gray-800 rounded-lg mb-6"> 99 + <View className="ml-4"> 100 + <Ionicons name="search" size={20} color="#9ca3af" /> 101 + </View> 98 102 <TextInput 99 - style={styles.input} 103 + className="flex-1 py-3 px-3 pl-2 text-base text-gray-50" 100 104 value={query} 101 105 onChangeText={setQuery} 102 106 placeholder="Search for a movie..." 103 - placeholderTextColor="#6b7280" 107 + placeholderTextColorClassName="accent-gray-500" 104 108 autoCapitalize="none" 105 109 autoCorrect={false} 106 110 /> 107 111 </View> 108 112 109 113 {isLoading && ( 110 - <View style={styles.loading}> 111 - <ActivityIndicator size="large" color="#a855f7" /> 114 + <View className="flex-1 justify-center py-12"> 115 + <ActivityIndicator size="large" colorClassName="accent-violet-500" /> 112 116 </View> 113 117 )} 114 118 115 119 {error && ( 116 - <View style={styles.error}> 117 - <Text style={styles.errorText}>Error: {(error as Error).message}</Text> 120 + <View className="bg-red-950/20 border border-red-900/50 rounded-lg p-3 mb-6"> 121 + <Text className="text-red-400"> 122 + Error: {(error as Error).message} 123 + </Text> 118 124 </View> 119 125 )} 120 126 121 127 {data && results.length > 0 && ( 122 128 <> 123 - <Text style={styles.resultCount}> 129 + <Text className="text-gray-400 mb-4"> 124 130 Found {totalResults.toLocaleString()} results 125 131 </Text> 126 132 <FlatList ··· 128 134 renderItem={renderItem} 129 135 keyExtractor={keyExtractor} 130 136 numColumns={2} 131 - columnWrapperStyle={styles.row} 132 - contentContainerStyle={styles.listContent} 137 + columnWrapperClassName="gap-4 mb-4" 138 + contentContainerClassName="pb-6" 133 139 key="grid" 134 140 /> 135 141 </> 136 142 )} 137 143 138 144 {data && results.length === 0 && searchQuery.length > 0 && ( 139 - <View style={styles.empty}> 140 - <Text style={styles.emptyText}> 145 + <View className="flex-1 justify-center py-12"> 146 + <Text className="text-gray-400 text-lg text-center"> 141 147 No results found for "{searchQuery}" 142 148 </Text> 143 149 </View> ··· 145 151 </View> 146 152 ); 147 153 } 148 - 149 - const styles = StyleSheet.create({ 150 - container: { 151 - flex: 1, 152 - backgroundColor: '#030712', 153 - paddingHorizontal: 16, 154 - paddingTop: 48, 155 - paddingBottom: 24, 156 - }, 157 - title: { 158 - fontSize: 32, 159 - fontWeight: '700', 160 - color: '#f9fafb', 161 - marginBottom: 24, 162 - }, 163 - inputWrap: { 164 - flexDirection: 'row', 165 - alignItems: 'center', 166 - backgroundColor: '#111827', 167 - borderWidth: 1, 168 - borderColor: '#1f2937', 169 - borderRadius: 8, 170 - marginBottom: 24, 171 - }, 172 - inputIcon: { 173 - marginLeft: 16, 174 - }, 175 - input: { 176 - flex: 1, 177 - paddingVertical: 12, 178 - paddingHorizontal: 12, 179 - paddingLeft: 8, 180 - fontSize: 16, 181 - color: '#f9fafb', 182 - }, 183 - loading: { 184 - flex: 1, 185 - justifyContent: 'center', 186 - paddingVertical: 48, 187 - }, 188 - error: { 189 - backgroundColor: 'rgba(127, 29, 29, 0.2)', 190 - borderWidth: 1, 191 - borderColor: 'rgba(127, 29, 29, 0.5)', 192 - borderRadius: 8, 193 - padding: 12, 194 - marginBottom: 24, 195 - }, 196 - errorText: { 197 - color: '#f87171', 198 - }, 199 - resultCount: { 200 - color: '#9ca3af', 201 - marginBottom: 16, 202 - }, 203 - listContent: { 204 - paddingBottom: 24, 205 - }, 206 - row: { 207 - gap: 16, 208 - marginBottom: 16, 209 - }, 210 - movieCard: { 211 - flex: 1, 212 - maxWidth: '50%', 213 - }, 214 - posterWrap: { 215 - aspectRatio: 2 / 3, 216 - backgroundColor: '#111827', 217 - borderRadius: 8, 218 - overflow: 'hidden', 219 - marginBottom: 8, 220 - }, 221 - poster: { 222 - width: '100%', 223 - height: '100%', 224 - }, 225 - posterPlaceholder: { 226 - flex: 1, 227 - justifyContent: 'center', 228 - alignItems: 'center', 229 - }, 230 - posterPlaceholderText: { 231 - color: '#4b5563', 232 - fontSize: 12, 233 - }, 234 - movieTitle: { 235 - fontSize: 14, 236 - fontWeight: '600', 237 - color: '#f9fafb', 238 - marginBottom: 4, 239 - }, 240 - movieYear: { 241 - fontSize: 12, 242 - color: '#6b7280', 243 - }, 244 - empty: { 245 - flex: 1, 246 - justifyContent: 'center', 247 - paddingVertical: 48, 248 - }, 249 - emptyText: { 250 - color: '#9ca3af', 251 - fontSize: 18, 252 - textAlign: 'center', 253 - }, 254 - });
+10
apps/mobile/src/uniwind-types.d.ts
··· 1 + // NOTE: This file is generated by uniwind and it should not be edited manually. 2 + /// <reference types="uniwind/types" /> 3 + 4 + declare module 'uniwind' { 5 + export interface UniwindConfig { 6 + themes: readonly ['light', 'dark'] 7 + } 8 + } 9 + 10 + export {}
+1
apps/mobile/src/uniwind.d.ts
··· 1 + /// <reference types="uniwind/types" />
+184
pnpm-lock.yaml
··· 46 46 react-native-screens: 47 47 specifier: ~4.10.0 48 48 version: 4.10.0(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) 49 + tailwindcss: 50 + specifier: ^4.1.18 51 + version: 4.1.18 52 + uniwind: 53 + specifier: ^1.2.7 54 + version: 1.2.7(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)(tailwindcss@4.1.18) 49 55 devDependencies: 50 56 '@types/react': 51 57 specifier: ~19.1.0 ··· 2476 2482 zod: 2477 2483 optional: true 2478 2484 2485 + '@tailwindcss/node@4.1.17': 2486 + resolution: {integrity: sha512-csIkHIgLb3JisEFQ0vxr2Y57GUNYh447C8xzwj89U/8fdW8LhProdxvnVH6U8M2Y73QKiTIH+LWbK3V2BBZsAg==} 2487 + 2479 2488 '@tailwindcss/node@4.1.18': 2480 2489 resolution: {integrity: sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ==} 2481 2490 2491 + '@tailwindcss/oxide-android-arm64@4.1.17': 2492 + resolution: {integrity: sha512-BMqpkJHgOZ5z78qqiGE6ZIRExyaHyuxjgrJ6eBO5+hfrfGkuya0lYfw8fRHG77gdTjWkNWEEm+qeG2cDMxArLQ==} 2493 + engines: {node: '>= 10'} 2494 + cpu: [arm64] 2495 + os: [android] 2496 + 2482 2497 '@tailwindcss/oxide-android-arm64@4.1.18': 2483 2498 resolution: {integrity: sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q==} 2484 2499 engines: {node: '>= 10'} 2485 2500 cpu: [arm64] 2486 2501 os: [android] 2487 2502 2503 + '@tailwindcss/oxide-darwin-arm64@4.1.17': 2504 + resolution: {integrity: sha512-EquyumkQweUBNk1zGEU/wfZo2qkp/nQKRZM8bUYO0J+Lums5+wl2CcG1f9BgAjn/u9pJzdYddHWBiFXJTcxmOg==} 2505 + engines: {node: '>= 10'} 2506 + cpu: [arm64] 2507 + os: [darwin] 2508 + 2488 2509 '@tailwindcss/oxide-darwin-arm64@4.1.18': 2489 2510 resolution: {integrity: sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A==} 2490 2511 engines: {node: '>= 10'} 2491 2512 cpu: [arm64] 2492 2513 os: [darwin] 2493 2514 2515 + '@tailwindcss/oxide-darwin-x64@4.1.17': 2516 + resolution: {integrity: sha512-gdhEPLzke2Pog8s12oADwYu0IAw04Y2tlmgVzIN0+046ytcgx8uZmCzEg4VcQh+AHKiS7xaL8kGo/QTiNEGRog==} 2517 + engines: {node: '>= 10'} 2518 + cpu: [x64] 2519 + os: [darwin] 2520 + 2494 2521 '@tailwindcss/oxide-darwin-x64@4.1.18': 2495 2522 resolution: {integrity: sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw==} 2496 2523 engines: {node: '>= 10'} 2497 2524 cpu: [x64] 2498 2525 os: [darwin] 2499 2526 2527 + '@tailwindcss/oxide-freebsd-x64@4.1.17': 2528 + resolution: {integrity: sha512-hxGS81KskMxML9DXsaXT1H0DyA+ZBIbyG/sSAjWNe2EDl7TkPOBI42GBV3u38itzGUOmFfCzk1iAjDXds8Oh0g==} 2529 + engines: {node: '>= 10'} 2530 + cpu: [x64] 2531 + os: [freebsd] 2532 + 2500 2533 '@tailwindcss/oxide-freebsd-x64@4.1.18': 2501 2534 resolution: {integrity: sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA==} 2502 2535 engines: {node: '>= 10'} 2503 2536 cpu: [x64] 2504 2537 os: [freebsd] 2538 + 2539 + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.17': 2540 + resolution: {integrity: sha512-k7jWk5E3ldAdw0cNglhjSgv501u7yrMf8oeZ0cElhxU6Y2o7f8yqelOp3fhf7evjIS6ujTI3U8pKUXV2I4iXHQ==} 2541 + engines: {node: '>= 10'} 2542 + cpu: [arm] 2543 + os: [linux] 2505 2544 2506 2545 '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18': 2507 2546 resolution: {integrity: sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA==} ··· 2509 2548 cpu: [arm] 2510 2549 os: [linux] 2511 2550 2551 + '@tailwindcss/oxide-linux-arm64-gnu@4.1.17': 2552 + resolution: {integrity: sha512-HVDOm/mxK6+TbARwdW17WrgDYEGzmoYayrCgmLEw7FxTPLcp/glBisuyWkFz/jb7ZfiAXAXUACfyItn+nTgsdQ==} 2553 + engines: {node: '>= 10'} 2554 + cpu: [arm64] 2555 + os: [linux] 2556 + libc: [glibc] 2557 + 2512 2558 '@tailwindcss/oxide-linux-arm64-gnu@4.1.18': 2513 2559 resolution: {integrity: sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw==} 2514 2560 engines: {node: '>= 10'} ··· 2516 2562 os: [linux] 2517 2563 libc: [glibc] 2518 2564 2565 + '@tailwindcss/oxide-linux-arm64-musl@4.1.17': 2566 + resolution: {integrity: sha512-HvZLfGr42i5anKtIeQzxdkw/wPqIbpeZqe7vd3V9vI3RQxe3xU1fLjss0TjyhxWcBaipk7NYwSrwTwK1hJARMg==} 2567 + engines: {node: '>= 10'} 2568 + cpu: [arm64] 2569 + os: [linux] 2570 + libc: [musl] 2571 + 2519 2572 '@tailwindcss/oxide-linux-arm64-musl@4.1.18': 2520 2573 resolution: {integrity: sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==} 2521 2574 engines: {node: '>= 10'} 2522 2575 cpu: [arm64] 2523 2576 os: [linux] 2524 2577 libc: [musl] 2578 + 2579 + '@tailwindcss/oxide-linux-x64-gnu@4.1.17': 2580 + resolution: {integrity: sha512-M3XZuORCGB7VPOEDH+nzpJ21XPvK5PyjlkSFkFziNHGLc5d6g3di2McAAblmaSUNl8IOmzYwLx9NsE7bplNkwQ==} 2581 + engines: {node: '>= 10'} 2582 + cpu: [x64] 2583 + os: [linux] 2584 + libc: [glibc] 2525 2585 2526 2586 '@tailwindcss/oxide-linux-x64-gnu@4.1.18': 2527 2587 resolution: {integrity: sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==} ··· 2530 2590 os: [linux] 2531 2591 libc: [glibc] 2532 2592 2593 + '@tailwindcss/oxide-linux-x64-musl@4.1.17': 2594 + resolution: {integrity: sha512-k7f+pf9eXLEey4pBlw+8dgfJHY4PZ5qOUFDyNf7SI6lHjQ9Zt7+NcscjpwdCEbYi6FI5c2KDTDWyf2iHcCSyyQ==} 2595 + engines: {node: '>= 10'} 2596 + cpu: [x64] 2597 + os: [linux] 2598 + libc: [musl] 2599 + 2533 2600 '@tailwindcss/oxide-linux-x64-musl@4.1.18': 2534 2601 resolution: {integrity: sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==} 2535 2602 engines: {node: '>= 10'} ··· 2537 2604 os: [linux] 2538 2605 libc: [musl] 2539 2606 2607 + '@tailwindcss/oxide-wasm32-wasi@4.1.17': 2608 + resolution: {integrity: sha512-cEytGqSSoy7zK4JRWiTCx43FsKP/zGr0CsuMawhH67ONlH+T79VteQeJQRO/X7L0juEUA8ZyuYikcRBf0vsxhg==} 2609 + engines: {node: '>=14.0.0'} 2610 + cpu: [wasm32] 2611 + bundledDependencies: 2612 + - '@napi-rs/wasm-runtime' 2613 + - '@emnapi/core' 2614 + - '@emnapi/runtime' 2615 + - '@tybys/wasm-util' 2616 + - '@emnapi/wasi-threads' 2617 + - tslib 2618 + 2540 2619 '@tailwindcss/oxide-wasm32-wasi@4.1.18': 2541 2620 resolution: {integrity: sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==} 2542 2621 engines: {node: '>=14.0.0'} ··· 2549 2628 - '@emnapi/wasi-threads' 2550 2629 - tslib 2551 2630 2631 + '@tailwindcss/oxide-win32-arm64-msvc@4.1.17': 2632 + resolution: {integrity: sha512-JU5AHr7gKbZlOGvMdb4722/0aYbU+tN6lv1kONx0JK2cGsh7g148zVWLM0IKR3NeKLv+L90chBVYcJ8uJWbC9A==} 2633 + engines: {node: '>= 10'} 2634 + cpu: [arm64] 2635 + os: [win32] 2636 + 2552 2637 '@tailwindcss/oxide-win32-arm64-msvc@4.1.18': 2553 2638 resolution: {integrity: sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA==} 2554 2639 engines: {node: '>= 10'} 2555 2640 cpu: [arm64] 2556 2641 os: [win32] 2557 2642 2643 + '@tailwindcss/oxide-win32-x64-msvc@4.1.17': 2644 + resolution: {integrity: sha512-SKWM4waLuqx0IH+FMDUw6R66Hu4OuTALFgnleKbqhgGU30DY20NORZMZUKgLRjQXNN2TLzKvh48QXTig4h4bGw==} 2645 + engines: {node: '>= 10'} 2646 + cpu: [x64] 2647 + os: [win32] 2648 + 2558 2649 '@tailwindcss/oxide-win32-x64-msvc@4.1.18': 2559 2650 resolution: {integrity: sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q==} 2560 2651 engines: {node: '>= 10'} 2561 2652 cpu: [x64] 2562 2653 os: [win32] 2563 2654 2655 + '@tailwindcss/oxide@4.1.17': 2656 + resolution: {integrity: sha512-F0F7d01fmkQhsTjXezGBLdrl1KresJTcI3DB8EkScCldyKp3Msz4hub4uyYaVnk88BAS1g5DQjjF6F5qczheLA==} 2657 + engines: {node: '>= 10'} 2658 + 2564 2659 '@tailwindcss/oxide@4.1.18': 2565 2660 resolution: {integrity: sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A==} 2566 2661 engines: {node: '>= 10'} ··· 3870 3965 3871 3966 csstype@3.2.3: 3872 3967 resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} 3968 + 3969 + culori@4.0.2: 3970 + resolution: {integrity: sha512-1+BhOB8ahCn4O0cep0Sh2l9KCOfOdY+BXJnKMHFFzDEouSr/el18QwXEMRlOj9UY5nCeA8UN3a/82rUWRBeyBw==} 3971 + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} 3873 3972 3874 3973 data-urls@6.0.1: 3875 3974 resolution: {integrity: sha512-euIQENZg6x8mj3fO6o9+fOW8MimUI4PpD/fZBhJfeioZVy9TUpM4UY7KjQNVZFlqwJ0UdzRDzkycB997HEq1BQ==} ··· 6621 6720 tailwind-merge@3.4.0: 6622 6721 resolution: {integrity: sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==} 6623 6722 6723 + tailwindcss@4.1.17: 6724 + resolution: {integrity: sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q==} 6725 + 6624 6726 tailwindcss@4.1.18: 6625 6727 resolution: {integrity: sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==} 6626 6728 ··· 6933 7035 resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} 6934 7036 engines: {node: '>= 10.0.0'} 6935 7037 7038 + uniwind@1.2.7: 7039 + resolution: {integrity: sha512-4aclu1rpqh9XSSyw8/3buNud7BAw7sPadX70WYXYp+9csgTGTqt9o/QaPPfpliULUSaG8ONDY1+gC4ylz+coyg==} 7040 + peerDependencies: 7041 + react: '>=19.0.0' 7042 + react-native: '>=0.81.0' 7043 + tailwindcss: '>=4' 7044 + 6936 7045 unpipe@1.0.0: 6937 7046 resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} 6938 7047 engines: {node: '>= 0.8'} ··· 9907 10016 valibot: 1.2.0(typescript@5.9.3) 9908 10017 zod: 4.3.6 9909 10018 10019 + '@tailwindcss/node@4.1.17': 10020 + dependencies: 10021 + '@jridgewell/remapping': 2.3.5 10022 + enhanced-resolve: 5.18.4 10023 + jiti: 2.6.1 10024 + lightningcss: 1.30.2 10025 + magic-string: 0.30.21 10026 + source-map-js: 1.2.1 10027 + tailwindcss: 4.1.17 10028 + 9910 10029 '@tailwindcss/node@4.1.18': 9911 10030 dependencies: 9912 10031 '@jridgewell/remapping': 2.3.5 ··· 9917 10036 source-map-js: 1.2.1 9918 10037 tailwindcss: 4.1.18 9919 10038 10039 + '@tailwindcss/oxide-android-arm64@4.1.17': 10040 + optional: true 10041 + 9920 10042 '@tailwindcss/oxide-android-arm64@4.1.18': 9921 10043 optional: true 9922 10044 10045 + '@tailwindcss/oxide-darwin-arm64@4.1.17': 10046 + optional: true 10047 + 9923 10048 '@tailwindcss/oxide-darwin-arm64@4.1.18': 9924 10049 optional: true 9925 10050 10051 + '@tailwindcss/oxide-darwin-x64@4.1.17': 10052 + optional: true 10053 + 9926 10054 '@tailwindcss/oxide-darwin-x64@4.1.18': 9927 10055 optional: true 9928 10056 10057 + '@tailwindcss/oxide-freebsd-x64@4.1.17': 10058 + optional: true 10059 + 9929 10060 '@tailwindcss/oxide-freebsd-x64@4.1.18': 9930 10061 optional: true 9931 10062 10063 + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.17': 10064 + optional: true 10065 + 9932 10066 '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18': 9933 10067 optional: true 9934 10068 10069 + '@tailwindcss/oxide-linux-arm64-gnu@4.1.17': 10070 + optional: true 10071 + 9935 10072 '@tailwindcss/oxide-linux-arm64-gnu@4.1.18': 9936 10073 optional: true 9937 10074 10075 + '@tailwindcss/oxide-linux-arm64-musl@4.1.17': 10076 + optional: true 10077 + 9938 10078 '@tailwindcss/oxide-linux-arm64-musl@4.1.18': 9939 10079 optional: true 9940 10080 10081 + '@tailwindcss/oxide-linux-x64-gnu@4.1.17': 10082 + optional: true 10083 + 9941 10084 '@tailwindcss/oxide-linux-x64-gnu@4.1.18': 10085 + optional: true 10086 + 10087 + '@tailwindcss/oxide-linux-x64-musl@4.1.17': 9942 10088 optional: true 9943 10089 9944 10090 '@tailwindcss/oxide-linux-x64-musl@4.1.18': 9945 10091 optional: true 9946 10092 10093 + '@tailwindcss/oxide-wasm32-wasi@4.1.17': 10094 + optional: true 10095 + 9947 10096 '@tailwindcss/oxide-wasm32-wasi@4.1.18': 9948 10097 optional: true 9949 10098 10099 + '@tailwindcss/oxide-win32-arm64-msvc@4.1.17': 10100 + optional: true 10101 + 9950 10102 '@tailwindcss/oxide-win32-arm64-msvc@4.1.18': 9951 10103 optional: true 9952 10104 10105 + '@tailwindcss/oxide-win32-x64-msvc@4.1.17': 10106 + optional: true 10107 + 9953 10108 '@tailwindcss/oxide-win32-x64-msvc@4.1.18': 9954 10109 optional: true 10110 + 10111 + '@tailwindcss/oxide@4.1.17': 10112 + optionalDependencies: 10113 + '@tailwindcss/oxide-android-arm64': 4.1.17 10114 + '@tailwindcss/oxide-darwin-arm64': 4.1.17 10115 + '@tailwindcss/oxide-darwin-x64': 4.1.17 10116 + '@tailwindcss/oxide-freebsd-x64': 4.1.17 10117 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.17 10118 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.17 10119 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.17 10120 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.17 10121 + '@tailwindcss/oxide-linux-x64-musl': 4.1.17 10122 + '@tailwindcss/oxide-wasm32-wasi': 4.1.17 10123 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.17 10124 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.17 9955 10125 9956 10126 '@tailwindcss/oxide@4.1.18': 9957 10127 optionalDependencies: ··· 11546 11716 lru-cache: 11.2.5 11547 11717 11548 11718 csstype@3.2.3: {} 11719 + 11720 + culori@4.0.2: {} 11549 11721 11550 11722 data-urls@6.0.1: 11551 11723 dependencies: ··· 14717 14889 14718 14890 tailwind-merge@3.4.0: {} 14719 14891 14892 + tailwindcss@4.1.17: {} 14893 + 14720 14894 tailwindcss@4.1.18: {} 14721 14895 14722 14896 tapable@2.3.0: {} ··· 14986 15160 crypto-random-string: 2.0.0 14987 15161 14988 15162 universalify@2.0.1: {} 15163 + 15164 + uniwind@1.2.7(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)(tailwindcss@4.1.18): 15165 + dependencies: 15166 + '@tailwindcss/node': 4.1.17 15167 + '@tailwindcss/oxide': 4.1.17 15168 + culori: 4.0.2 15169 + lightningcss: 1.30.2 15170 + react: 19.1.0 15171 + react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0) 15172 + tailwindcss: 4.1.18 14989 15173 14990 15174 unpipe@1.0.0: {} 14991 15175
+35
settings.json
··· 1 + { 2 + "tailwindCSS.classAttributes": [ 3 + "class", 4 + "className", 5 + "headerClassName", 6 + "contentContainerClassName", 7 + "columnWrapperClassName", 8 + "endFillColorClassName", 9 + "imageClassName", 10 + "tintColorClassName", 11 + "ios_backgroundColorClassName", 12 + "thumbColorClassName", 13 + "trackColorOnClassName", 14 + "trackColorOffClassName", 15 + "selectionColorClassName", 16 + "cursorColorClassName", 17 + "underlineColorAndroidClassName", 18 + "placeholderTextColorClassName", 19 + "selectionHandleColorClassName", 20 + "colorsClassName", 21 + "progressBackgroundColorClassName", 22 + "titleColorClassName", 23 + "underlayColorClassName", 24 + "colorClassName", 25 + "drawerBackgroundColorClassName", 26 + "statusBarBackgroundColorClassName", 27 + "backdropColorClassName", 28 + "backgroundColorClassName", 29 + "ListFooterComponentClassName", 30 + "ListHeaderComponentClassName" 31 + ], 32 + "tailwindCSS.classFunctions": [ 33 + "useResolveClassNames" 34 + ] 35 + }