From 53ed4a7ca5f155c54547819b0f974f2b9e6054ee Mon Sep 17 00:00:00 2001 From: baizhi958216 <1475289190@qq.com> Date: Tue, 30 Jun 2026 13:57:39 +0800 Subject: [PATCH] refactor: migrate app screens to nativewind classes --- src/app/create.tsx | 436 ++++++++--------------- src/app/index.tsx | 324 ++++++----------- src/app/profile.tsx | 837 +++++++++++++++++--------------------------- 3 files changed, 581 insertions(+), 1016 deletions(-) diff --git a/src/app/create.tsx b/src/app/create.tsx index 7a468b3..1f3f95e 100644 --- a/src/app/create.tsx +++ b/src/app/create.tsx @@ -1,17 +1,10 @@ -import { creationOptions, defaultStoryDraft } from '@infiplot/core'; -import { SymbolView } from 'expo-symbols'; -import { useState } from 'react'; -import { - Pressable, - ScrollView, - StyleSheet, - Text, - TextInput, - View, -} from 'react-native'; -import { SafeAreaView } from 'react-native-safe-area-context'; +import { creationOptions, defaultStoryDraft } from "@infiplot/core"; +import { SymbolView } from "expo-symbols"; +import { useState } from "react"; +import { Pressable, ScrollView, Text, TextInput, View } from "react-native"; +import { SafeAreaView } from "react-native-safe-area-context"; -import { BottomTabInset } from '@/constants/theme'; +import { BottomTabInset } from "@/constants/theme"; export default function CreateScreen() { const [world, setWorld] = useState(defaultStoryDraft.worldSetting); @@ -23,92 +16,157 @@ export default function CreateScreen() { const ready = world.trim().length > 10 && style.trim().length > 5; return ( - + - - - AI 剧情创作 - 写下两段文字,生成可刷的短剧。 + contentContainerStyle={{ paddingBottom: BottomTabInset + 112 }} + > + + + + + AI 剧情创作 + + + 写下两段文字,生成可刷的短剧。 + + + + + - - - - - - 题材模板 - - - - - - 世界设定 - {world.length} + + + 题材模板 + + - - - - - 视觉风格 - {style.length} + + + + 世界设定 + + + {world.length} + + + - - - - - 画面比例 - + + + + 视觉风格 + + + {style.length} + + + - - 叙事节奏 - - - - - 生成流程 - - {['故事圣经', '分镜脚本', '角色视觉', '首集预览'].map((step, index) => ( - - {index + 1} - {step} - - ))} + + + + 画面比例 + + + + + + 叙事节奏 + + + + + + + 生成流程 + + {["故事圣经", "分镜脚本", "角色视觉", "首集预览"].map( + (step, index) => ( + + + {index + 1} + + + {step} + + + ), + )} + - + [ - styles.generateButton, - !ready && styles.disabledButton, - pressed && ready && styles.pressed, - ]}> - - {ready ? '生成第一集' : '继续补充设定'} + className={`h-[52px] flex-row items-center justify-center gap-2 rounded-full bg-white ${!ready ? "opacity-40" : ""}`} + style={({ pressed }) => ({ + opacity: pressed && ready ? 0.76 : undefined, + })} + > + + + {ready ? "生成第一集" : "继续补充设定"} + @@ -127,216 +185,26 @@ function ChipRow({ compact?: boolean; }) { return ( - + {options.map((option) => { const selected = option === value; return ( onChange(option)} - style={({ pressed }) => [ - styles.chip, - compact && styles.compactChip, - selected && styles.selectedChip, - pressed && styles.pressed, - ]}> - {option} + className={`items-center justify-center rounded-full border ${ + compact ? "min-h-8 px-2.5" : "min-h-[38px] px-3.5" + } ${selected ? "border-white bg-white" : "border-white/10 bg-white/10"}`} + style={({ pressed }) => ({ opacity: pressed ? 0.76 : undefined })} + > + + {option} + ); })} ); } - -const styles = StyleSheet.create({ - safe: { - flex: 1, - backgroundColor: '#09090b', - }, - scroll: { - flex: 1, - }, - content: { - padding: 18, - paddingBottom: BottomTabInset + 112, - gap: 18, - }, - header: { - flexDirection: 'row', - alignItems: 'flex-start', - justifyContent: 'space-between', - gap: 18, - paddingTop: 8, - }, - eyebrow: { - color: '#ff4d6d', - fontSize: 12, - fontWeight: '900', - marginBottom: 8, - }, - title: { - color: '#fff', - fontSize: 30, - lineHeight: 36, - fontWeight: '900', - maxWidth: 300, - }, - headerIcon: { - width: 46, - height: 46, - borderRadius: 23, - alignItems: 'center', - justifyContent: 'center', - backgroundColor: 'rgba(255,255,255,0.12)', - }, - section: { - gap: 10, - }, - label: { - color: 'rgba(255,255,255,0.7)', - fontSize: 13, - fontWeight: '900', - }, - chips: { - flexDirection: 'row', - flexWrap: 'wrap', - gap: 10, - }, - compactChips: { - gap: 8, - }, - chip: { - minHeight: 38, - borderRadius: 19, - paddingHorizontal: 14, - alignItems: 'center', - justifyContent: 'center', - backgroundColor: 'rgba(255,255,255,0.09)', - borderWidth: 1, - borderColor: 'rgba(255,255,255,0.08)', - }, - compactChip: { - minHeight: 32, - paddingHorizontal: 10, - }, - selectedChip: { - backgroundColor: '#fff', - borderColor: '#fff', - }, - chipText: { - color: '#fff', - fontSize: 13, - fontWeight: '800', - }, - selectedChipText: { - color: '#09090b', - }, - fieldBlock: { - gap: 10, - }, - fieldHeader: { - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', - }, - counter: { - color: 'rgba(255,255,255,0.42)', - fontSize: 12, - fontWeight: '800', - }, - textArea: { - minHeight: 158, - borderRadius: 8, - padding: 14, - color: '#fff', - fontSize: 16, - lineHeight: 24, - fontWeight: '600', - backgroundColor: 'rgba(255,255,255,0.08)', - borderWidth: 1, - borderColor: 'rgba(255,255,255,0.1)', - }, - shortArea: { - minHeight: 116, - }, - twoColumns: { - flexDirection: 'row', - gap: 12, - }, - optionCard: { - flex: 1, - gap: 12, - borderRadius: 8, - padding: 12, - backgroundColor: 'rgba(255,255,255,0.07)', - }, - previewCard: { - borderRadius: 8, - padding: 14, - gap: 12, - backgroundColor: 'rgba(255,255,255,0.07)', - }, - previewTitle: { - color: '#fff', - fontSize: 15, - fontWeight: '900', - }, - steps: { - flexDirection: 'row', - flexWrap: 'wrap', - gap: 10, - }, - step: { - flexDirection: 'row', - alignItems: 'center', - gap: 8, - minWidth: '46%', - }, - stepIndex: { - width: 22, - height: 22, - borderRadius: 11, - overflow: 'hidden', - textAlign: 'center', - lineHeight: 22, - color: '#09090b', - backgroundColor: '#b8ff5d', - fontSize: 12, - fontWeight: '900', - }, - stepText: { - color: 'rgba(255,255,255,0.82)', - fontSize: 13, - fontWeight: '800', - }, - footer: { - position: 'absolute', - left: 0, - right: 0, - bottom: 0, - paddingHorizontal: 18, - paddingTop: 12, - paddingBottom: BottomTabInset + 14, - backgroundColor: 'rgba(9,9,11,0.96)', - }, - generateButton: { - height: 52, - borderRadius: 26, - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'center', - gap: 8, - backgroundColor: '#fff', - }, - disabledButton: { - opacity: 0.42, - }, - generateText: { - color: '#050505', - fontSize: 16, - fontWeight: '900', - }, - pressed: { - opacity: 0.76, - }, -}); diff --git a/src/app/index.tsx b/src/app/index.tsx index 4bef814..c3fdc6d 100644 --- a/src/app/index.tsx +++ b/src/app/index.tsx @@ -1,26 +1,28 @@ -import { mobileDramaFeed } from '@infiplot/core'; -import type { MobileDrama } from '@infiplot/types'; -import { SymbolView } from 'expo-symbols'; -import type { ComponentProps } from 'react'; -import { useMemo, useRef, useState } from 'react'; +import { mobileDramaFeed } from "@infiplot/core"; +import type { MobileDrama } from "@infiplot/types"; +import { SymbolView } from "expo-symbols"; +import type { ComponentProps } from "react"; +import { useMemo, useRef, useState } from "react"; import { Dimensions, FlatList, Pressable, - StyleSheet, Text, View, type NativeScrollEvent, type NativeSyntheticEvent, -} from 'react-native'; -import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context'; +} from "react-native"; +import { + SafeAreaView, + useSafeAreaInsets, +} from "react-native-safe-area-context"; -import { BottomTabInset } from '@/constants/theme'; +import { BottomTabInset } from "@/constants/theme"; -type SymbolName = ComponentProps['name']; +type SymbolName = ComponentProps["name"]; export default function FeedScreen() { - const { height } = Dimensions.get('window'); + const { height } = Dimensions.get("window"); const insets = useSafeAreaInsets(); const pageHeight = height; const [activeIndex, setActiveIndex] = useState(0); @@ -32,12 +34,16 @@ export default function FeedScreen() { ); function handleScrollEnd(event: NativeSyntheticEvent) { - const nextIndex = Math.round(event.nativeEvent.contentOffset.y / pageHeight); - setActiveIndex(Math.max(0, Math.min(mobileDramaFeed.length - 1, nextIndex))); + const nextIndex = Math.round( + event.nativeEvent.contentOffset.y / pageHeight, + ); + setActiveIndex( + Math.max(0, Math.min(mobileDramaFeed.length - 1, nextIndex)), + ); } return ( - + - - - {drama.title} - {drama.episode} + + + + + {drama.title} + + + {drama.episode} + - - - InfiPlot - - - 刷剧 + + + InfiPlot + + + 刷剧 - - - - + + + + - - @{drama.creator} - {drama.title} - {drama.hook} - + + + @{drama.creator} + + + {drama.title} + + + {drama.hook} + + {drama.tags.map((tag) => ( - + #{tag} ))} - - [styles.primaryButton, pressed && styles.pressed]}> - - 继续看 + + ({ opacity: pressed ? 0.75 : undefined })} + > + + 继续看 - {drama.episode} + + {drama.episode} + @@ -120,176 +190,14 @@ function DramaPage({ function ActionButton({ name, label }: { name: SymbolName; label: string }) { return ( - [styles.actionButton, pressed && styles.pressed]}> - + ({ opacity: pressed ? 0.75 : undefined })} + > + - {label} + {label} ); } - -const styles = StyleSheet.create({ - root: { - flex: 1, - backgroundColor: '#000', - }, - page: { - overflow: 'hidden', - }, - posterGlow: { - position: 'absolute', - width: 520, - height: 520, - borderRadius: 260, - top: '14%', - left: -120, - opacity: 0.5, - transform: [{ rotate: '-18deg' }], - }, - posterOrb: { - position: 'absolute', - top: '18%', - alignSelf: 'center', - width: '76%', - aspectRatio: 0.72, - borderWidth: 1, - borderRadius: 28, - justifyContent: 'center', - alignItems: 'center', - padding: 24, - backgroundColor: 'rgba(255,255,255,0.08)', - }, - posterTitle: { - fontSize: 42, - lineHeight: 48, - fontWeight: '800', - textAlign: 'center', - }, - posterSubtitle: { - color: 'rgba(255,255,255,0.72)', - marginTop: 14, - fontSize: 13, - fontWeight: '700', - }, - safe: { - flex: 1, - }, - topBar: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - paddingHorizontal: 18, - paddingTop: 6, - }, - brand: { - color: '#fff', - fontSize: 20, - fontWeight: '900', - }, - livePill: { - flexDirection: 'row', - alignItems: 'center', - gap: 6, - paddingHorizontal: 12, - paddingVertical: 7, - borderRadius: 99, - backgroundColor: 'rgba(0,0,0,0.32)', - }, - liveDot: { - width: 7, - height: 7, - borderRadius: 7, - }, - liveText: { - color: '#fff', - fontSize: 12, - fontWeight: '800', - }, - sideRail: { - position: 'absolute', - right: 14, - gap: 18, - alignItems: 'center', - }, - actionButton: { - alignItems: 'center', - gap: 6, - }, - actionIcon: { - width: 48, - height: 48, - borderRadius: 24, - alignItems: 'center', - justifyContent: 'center', - backgroundColor: 'rgba(255,255,255,0.16)', - }, - actionLabel: { - color: '#fff', - fontSize: 11, - fontWeight: '800', - }, - infoPanel: { - marginTop: 'auto', - paddingLeft: 18, - paddingRight: 82, - gap: 10, - }, - creator: { - color: '#fff', - fontSize: 15, - fontWeight: '900', - }, - title: { - color: '#fff', - fontSize: 30, - lineHeight: 36, - fontWeight: '900', - }, - hook: { - color: 'rgba(255,255,255,0.88)', - fontSize: 15, - lineHeight: 22, - fontWeight: '600', - }, - tags: { - flexDirection: 'row', - flexWrap: 'wrap', - gap: 8, - }, - tag: { - color: '#fff', - fontSize: 13, - fontWeight: '800', - }, - playRow: { - flexDirection: 'row', - alignItems: 'center', - gap: 12, - marginTop: 4, - }, - primaryButton: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'center', - gap: 6, - paddingHorizontal: 18, - height: 42, - borderRadius: 21, - backgroundColor: '#fff', - }, - primaryButtonText: { - color: '#101010', - fontSize: 14, - fontWeight: '900', - }, - episode: { - color: 'rgba(255,255,255,0.78)', - fontSize: 12, - fontWeight: '700', - flexShrink: 1, - }, - pressed: { - opacity: 0.75, - }, -}); diff --git a/src/app/profile.tsx b/src/app/profile.tsx index bb8006d..76d939c 100644 --- a/src/app/profile.tsx +++ b/src/app/profile.tsx @@ -1,27 +1,26 @@ -import type { ModelConfig, ModelConfigMap, ModelRole } from '@infiplot/types'; -import { SymbolView } from 'expo-symbols'; -import type { ComponentProps } from 'react'; -import { useEffect, useMemo, useState } from 'react'; +import type { ModelConfig, ModelConfigMap, ModelRole } from "@infiplot/types"; +import { SymbolView } from "expo-symbols"; +import type { ComponentProps } from "react"; +import { useEffect, useMemo, useState } from "react"; import { Pressable, ScrollView, - StyleSheet, Switch, Text, TextInput, View, -} from 'react-native'; -import { SafeAreaView } from 'react-native-safe-area-context'; +} from "react-native"; +import { SafeAreaView } from "react-native-safe-area-context"; -import { BottomTabInset } from '@/constants/theme'; +import { BottomTabInset } from "@/constants/theme"; import { clearModelConfig, defaultModelConfig, readModelConfig, writeModelConfig, -} from '@/lib/model-config'; +} from "@/lib/model-config"; -type SymbolName = ComponentProps['name']; +type SymbolName = ComponentProps["name"]; const modelGroups: { id: ModelRole; @@ -29,28 +28,46 @@ const modelGroups: { hint: string; accent: string; }[] = [ - { id: 'text', title: '文本模型', hint: '剧情、分镜、选择分支', accent: '#ff4d6d' }, - { id: 'image', title: '图像模型', hint: '角色、场景、封面生成', accent: '#b8ff5d' }, - { id: 'vision', title: '视觉理解', hint: '风格图解析、画面标注', accent: '#78d7ff' }, - { id: 'tts', title: '语音模型', hint: '对白配音、旁白', accent: '#ffd166' }, + { + id: "text", + title: "文本模型", + hint: "剧情、分镜、选择分支", + accent: "#ff4d6d", + }, + { + id: "image", + title: "图像模型", + hint: "角色、场景、封面生成", + accent: "#b8ff5d", + }, + { + id: "vision", + title: "视觉理解", + hint: "风格图解析、画面标注", + accent: "#78d7ff", + }, + { id: "tts", title: "语音模型", hint: "对白配音、旁白", accent: "#ffd166" }, ]; const roleLabels: Record = { - text: '文本', - image: '图像', - vision: '视觉', - tts: '语音', + text: "文本", + image: "图像", + vision: "视觉", + tts: "语音", }; export default function ProfileScreen() { const [syncEnabled, setSyncEnabled] = useState(true); const [showKeys, setShowKeys] = useState(false); const [signedIn, setSignedIn] = useState(false); - const [email, setEmail] = useState(''); - const [selectedRole, setSelectedRole] = useState('text'); + const [email, setEmail] = useState(""); + const [selectedRole, setSelectedRole] = useState("text"); const [configs, setConfigs] = useState(defaultModelConfig); - const [savedConfigs, setSavedConfigs] = useState(defaultModelConfig); - const [status, setStatus] = useState<'loading' | 'saved' | 'dirty'>('loading'); + const [savedConfigs, setSavedConfigs] = + useState(defaultModelConfig); + const [status, setStatus] = useState<"loading" | "saved" | "dirty">( + "loading", + ); useEffect(() => { let mounted = true; @@ -59,7 +76,7 @@ export default function ProfileScreen() { if (!mounted) return; setConfigs(stored); setSavedConfigs(stored); - setStatus('saved'); + setStatus("saved"); }); return () => { @@ -67,11 +84,14 @@ export default function ProfileScreen() { }; }, []); - const selectedGroup = modelGroups.find((group) => group.id === selectedRole) ?? modelGroups[0]; + const selectedGroup = + modelGroups.find((group) => group.id === selectedRole) ?? modelGroups[0]; const selectedConfig = configs[selectedRole]; const configuredCount = useMemo( - () => Object.values(configs).filter((config) => config.apiKey.trim().length > 0).length, + () => + Object.values(configs).filter((config) => config.apiKey.trim().length > 0) + .length, [configs], ); @@ -88,28 +108,28 @@ export default function ProfileScreen() { [field]: value, }, })); - setStatus('dirty'); + setStatus("dirty"); } async function handleSave() { const saved = await writeModelConfig(configs); setConfigs(saved); setSavedConfigs(saved); - setStatus('saved'); + setStatus("saved"); } async function handleReset() { const reset = await clearModelConfig(); setConfigs(reset); setSavedConfigs(reset); - setSelectedRole('text'); - setStatus('saved'); + setSelectedRole("text"); + setStatus("saved"); } function handleAuthAction() { if (signedIn) { setSignedIn(false); - setEmail(''); + setEmail(""); return; } @@ -119,179 +139,263 @@ export default function ProfileScreen() { } return ( - - - - - IP - - - {signedIn ? 'InfiPlot 创作者' : '未登录创作者'} - - {signedIn ? email.trim() : '登录后同步我的剧情、草稿和模型配置。'} - - - [styles.loginButton, pressed && styles.pressed]}> - {signedIn ? '退出' : '登录'} - - - - {!signedIn && ( - - - [ - styles.loginSubmit, - email.trim().length <= 3 && styles.disabledButton, - pressed && email.trim().length > 3 && styles.pressed, - ]}> - 继续 - - - )} - - - - - - - - - - 模型 API - 对应 web 端文字、图片、视觉和 TTS 配置。 - - setShowKeys((value) => !value)} - style={({ pressed }) => [styles.iconButton, pressed && styles.pressed]}> - - - - - - {modelGroups.map((group) => { - const selected = group.id === selectedRole; - const configured = configs[group.id].apiKey.trim().length > 0; - - return ( - setSelectedRole(group.id)} - style={({ pressed }) => [ - styles.roleTab, - selected && styles.selectedRoleTab, - pressed && styles.pressed, - ]}> - - - {roleLabels[group.id]} - - - ); - })} - - - - - - {selectedGroup.title} - {selectedGroup.hint} + + + + + + IP - - - {status === 'loading' ? '读取中' : dirty ? '未保存' : '已保存'} + + + {signedIn ? "InfiPlot 创作者" : "未登录创作者"} + + + {signedIn + ? email.trim() + : "登录后同步我的剧情、草稿和模型配置。"} + ({ opacity: pressed ? 0.76 : undefined })} + > + + {signedIn ? "退出" : "登录"} + + - updateConfig('provider', value)} - placeholder="openai / claude / gemini / stepfun" - /> - updateConfig('baseUrl', value)} - placeholder="https://api.example.com/v1" - /> - updateConfig('apiKey', value)} - secureTextEntry={!showKeys} - placeholder="sk-..." - /> - updateConfig('model', value)} - placeholder="model name" - /> - - - [styles.secondaryButton, pressed && styles.pressed]}> - 重置 - - [ - styles.saveButton, - !dirty && styles.disabledButton, - pressed && dirty && styles.pressed, - ]}> - + + ({ + opacity: + pressed && email.trim().length > 3 ? 0.76 : undefined, + })} + > + + 继续 + + + + )} + + + + + + + + + + + 模型 API + + + 对应 web 端文字、图片、视觉和 TTS 配置。 + + + setShowKeys((value) => !value)} + className="h-[42px] w-[42px] items-center justify-center rounded-full bg-white/10" + style={({ pressed }) => ({ opacity: pressed ? 0.76 : undefined })} + > + - 保存配置 - - - - 云端同步 - 登录后自动同步我的剧情和配置。 + + {modelGroups.map((group) => { + const selected = group.id === selectedRole; + const configured = configs[group.id].apiKey.trim().length > 0; + + return ( + setSelectedRole(group.id)} + className={`min-h-[42px] flex-1 flex-row items-center justify-center gap-1.5 rounded-lg border ${ + selected + ? "border-white/20 bg-white/[0.14]" + : "border-white/10 bg-white/[0.07]" + }`} + style={({ pressed }) => ({ + opacity: pressed ? 0.76 : undefined, + })} + > + + + {roleLabels[group.id]} + + + ); + })} - - - - - - + + + + + {selectedGroup.title} + + + {selectedGroup.hint} + + + + + {status === "loading" + ? "读取中" + : dirty + ? "未保存" + : "已保存"} + + + + + updateConfig("provider", value)} + placeholder="openai / claude / gemini / stepfun" + /> + updateConfig("baseUrl", value)} + placeholder="https://api.example.com/v1" + /> + updateConfig("apiKey", value)} + secureTextEntry={!showKeys} + placeholder="sk-..." + /> + updateConfig("model", value)} + placeholder="model name" + /> + + + ({ + opacity: pressed ? 0.76 : undefined, + })} + > + 重置 + + ({ + opacity: pressed && dirty ? 0.76 : undefined, + })} + > + + + 保存配置 + + + + + + + + + 云端同步 + + + 登录后自动同步我的剧情和配置。 + + + + + + + + + + @@ -300,9 +404,9 @@ export default function ProfileScreen() { function Stat({ value, label }: { value: string; label: string }) { return ( - - {value} - {label} + + {value} + {label} ); } @@ -321,15 +425,15 @@ function InputLine({ placeholder: string; }) { return ( - - {label} + + {label} @@ -339,338 +443,23 @@ function InputLine({ function MenuItem({ name, label }: { name: SymbolName; label: string }) { return ( - [styles.menuItem, pressed && styles.pressed]}> - + ({ opacity: pressed ? 0.76 : undefined })} + > + - {label} + {label} ); } - -const styles = StyleSheet.create({ - safe: { - flex: 1, - backgroundColor: '#09090b', - }, - content: { - padding: 18, - paddingBottom: BottomTabInset + 34, - gap: 18, - }, - profileCard: { - flexDirection: 'row', - alignItems: 'center', - gap: 12, - borderRadius: 8, - padding: 14, - backgroundColor: 'rgba(255,255,255,0.08)', - }, - avatar: { - width: 54, - height: 54, - borderRadius: 27, - alignItems: 'center', - justifyContent: 'center', - backgroundColor: '#ff4d6d', - }, - avatarText: { - color: '#fff', - fontSize: 18, - fontWeight: '900', - }, - profileCopy: { - flex: 1, - gap: 3, - }, - name: { - color: '#fff', - fontSize: 18, - fontWeight: '900', - }, - subline: { - color: 'rgba(255,255,255,0.58)', - fontSize: 12, - lineHeight: 17, - fontWeight: '700', - }, - loginButton: { - height: 36, - paddingHorizontal: 16, - borderRadius: 18, - justifyContent: 'center', - backgroundColor: '#fff', - }, - loginText: { - color: '#09090b', - fontSize: 13, - fontWeight: '900', - }, - loginPanel: { - flexDirection: 'row', - gap: 10, - borderRadius: 8, - padding: 12, - backgroundColor: 'rgba(255,255,255,0.06)', - }, - loginInput: { - flex: 1, - minHeight: 42, - borderRadius: 8, - paddingHorizontal: 12, - color: '#fff', - fontSize: 13, - fontWeight: '700', - backgroundColor: 'rgba(0,0,0,0.22)', - borderWidth: 1, - borderColor: 'rgba(255,255,255,0.08)', - }, - loginSubmit: { - minWidth: 70, - height: 42, - borderRadius: 21, - alignItems: 'center', - justifyContent: 'center', - backgroundColor: '#fff', - }, - loginSubmitText: { - color: '#09090b', - fontSize: 13, - fontWeight: '900', - }, - stats: { - flexDirection: 'row', - borderRadius: 8, - paddingVertical: 14, - backgroundColor: 'rgba(255,255,255,0.06)', - }, - stat: { - flex: 1, - alignItems: 'center', - gap: 4, - }, - statValue: { - color: '#fff', - fontSize: 20, - fontWeight: '900', - }, - statLabel: { - color: 'rgba(255,255,255,0.54)', - fontSize: 12, - fontWeight: '800', - }, - sectionHeader: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - gap: 16, - }, - sectionTitle: { - color: '#fff', - fontSize: 23, - fontWeight: '900', - }, - sectionHint: { - color: 'rgba(255,255,255,0.56)', - fontSize: 12, - lineHeight: 18, - fontWeight: '700', - marginTop: 4, - }, - iconButton: { - width: 42, - height: 42, - borderRadius: 21, - alignItems: 'center', - justifyContent: 'center', - backgroundColor: 'rgba(255,255,255,0.1)', - }, - roleTabs: { - flexDirection: 'row', - gap: 8, - }, - roleTab: { - flex: 1, - minHeight: 42, - borderRadius: 8, - alignItems: 'center', - justifyContent: 'center', - flexDirection: 'row', - gap: 6, - backgroundColor: 'rgba(255,255,255,0.07)', - borderWidth: 1, - borderColor: 'rgba(255,255,255,0.08)', - }, - selectedRoleTab: { - backgroundColor: 'rgba(255,255,255,0.14)', - borderColor: 'rgba(255,255,255,0.2)', - }, - roleDot: { - width: 7, - height: 7, - borderRadius: 7, - }, - roleText: { - color: 'rgba(255,255,255,0.58)', - fontSize: 12, - fontWeight: '900', - }, - selectedRoleText: { - color: '#fff', - }, - modelEditor: { - gap: 12, - borderRadius: 8, - padding: 14, - backgroundColor: 'rgba(255,255,255,0.075)', - borderWidth: 1, - borderColor: 'rgba(255,255,255,0.08)', - }, - modelTop: { - flexDirection: 'row', - justifyContent: 'space-between', - gap: 12, - }, - modelTitle: { - color: '#fff', - fontSize: 17, - fontWeight: '900', - }, - modelHint: { - color: 'rgba(255,255,255,0.52)', - fontSize: 12, - fontWeight: '700', - marginTop: 3, - }, - statusPill: { - height: 26, - justifyContent: 'center', - borderRadius: 13, - paddingHorizontal: 10, - backgroundColor: 'rgba(184,255,93,0.16)', - }, - dirtyPill: { - backgroundColor: 'rgba(255,209,102,0.16)', - }, - statusText: { - color: '#b8ff5d', - fontSize: 11, - fontWeight: '900', - }, - dirtyText: { - color: '#ffd166', - }, - inputLine: { - gap: 6, - }, - inputLabel: { - color: 'rgba(255,255,255,0.5)', - fontSize: 11, - fontWeight: '900', - }, - input: { - minHeight: 42, - borderRadius: 8, - paddingHorizontal: 12, - color: '#fff', - fontSize: 13, - fontWeight: '700', - backgroundColor: 'rgba(0,0,0,0.22)', - borderWidth: 1, - borderColor: 'rgba(255,255,255,0.08)', - }, - actionRow: { - flexDirection: 'row', - gap: 10, - marginTop: 2, - }, - secondaryButton: { - height: 44, - borderRadius: 22, - paddingHorizontal: 18, - justifyContent: 'center', - backgroundColor: 'rgba(255,255,255,0.1)', - }, - secondaryText: { - color: '#fff', - fontSize: 14, - fontWeight: '900', - }, - saveButton: { - flex: 1, - height: 44, - borderRadius: 22, - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'center', - gap: 7, - backgroundColor: '#fff', - }, - disabledButton: { - opacity: 0.42, - }, - saveText: { - color: '#09090b', - fontSize: 14, - fontWeight: '900', - }, - settingRow: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - gap: 18, - borderRadius: 8, - padding: 14, - backgroundColor: 'rgba(255,255,255,0.07)', - }, - settingTextBlock: { - flex: 1, - gap: 3, - }, - settingTitle: { - color: '#fff', - fontSize: 15, - fontWeight: '900', - }, - settingHint: { - color: 'rgba(255,255,255,0.52)', - fontSize: 12, - fontWeight: '700', - }, - menuList: { - borderRadius: 8, - overflow: 'hidden', - backgroundColor: 'rgba(255,255,255,0.07)', - }, - menuItem: { - minHeight: 56, - flexDirection: 'row', - alignItems: 'center', - gap: 12, - paddingHorizontal: 14, - borderBottomWidth: StyleSheet.hairlineWidth, - borderBottomColor: 'rgba(255,255,255,0.08)', - }, - menuIcon: { - width: 34, - height: 34, - borderRadius: 17, - alignItems: 'center', - justifyContent: 'center', - backgroundColor: 'rgba(255,255,255,0.1)', - }, - menuLabel: { - flex: 1, - color: '#fff', - fontSize: 14, - fontWeight: '800', - }, - pressed: { - opacity: 0.76, - }, -});