design: align visual style with bincio_autarchive

- Add src/theme.ts with centralised color palette
- Backgrounds: #111 → #09090b, surfaces #1e1e1e → #18181b
- All cards get 1px #27272a borders (matches autarchive cards)
- Text: #fff/#888/#555 → #f4f4f5/#a1a1aa/#71717a
- Accent: #3b82f6 → #60a5fa (autarchive default palette)
- Tab icons: colored emoji → monochromatic Unicode (◉ ☰ ⚙)
- Action buttons use muted palette (#16a34a / #d97706 / #dc2626)
- Keep-awake toggle uses ◑/◌ symbols, border highlights accent on active
- Connect/Scan buttons match autarchive surface+border style
This commit is contained in:
Davide Scaini
2026-06-03 09:50:05 +02:00
parent 6e47ced264
commit ea938e5644
7 changed files with 165 additions and 137 deletions
+23 -25
View File
@@ -10,36 +10,34 @@ import { SensorPairingScreen } from '../screens/SensorPairingScreen';
import { SavedRecordingsScreen } from '../screens/SavedRecordingsScreen';
import { SettingsScreen } from '../screens/SettingsScreen';
import { RootStackParamList, TabParamList } from '../types';
import { colors } from '../theme';
const Stack = createNativeStackNavigator<RootStackParamList>();
const Tab = createBottomTabNavigator<TabParamList>();
const TAB_ICONS: Record<string, string> = {
Recording: '◉',
Saved: '☰',
Settings: '⚙',
};
function Tabs() {
return (
<Tab.Navigator
screenOptions={{
headerStyle: { backgroundColor: '#111' },
headerTintColor: '#fff',
tabBarStyle: { backgroundColor: '#111', borderTopColor: '#222' },
tabBarActiveTintColor: '#3b82f6',
tabBarInactiveTintColor: '#555',
}}
screenOptions={({ route }) => ({
headerStyle: { backgroundColor: colors.bg },
headerTintColor: colors.text,
tabBarStyle: { backgroundColor: colors.surface, borderTopColor: colors.border },
tabBarActiveTintColor: colors.accent,
tabBarInactiveTintColor: colors.textMuted,
tabBarIcon: ({ color }) => (
<Text style={{ color, fontSize: 18 }}>{TAB_ICONS[route.name]}</Text>
),
})}
>
<Tab.Screen
name="Recording"
component={RecordingScreen}
options={{ tabBarIcon: ({ color }) => <Text style={{ color, fontSize: 20 }}></Text> }}
/>
<Tab.Screen
name="Saved"
component={SavedRecordingsScreen}
options={{ tabBarIcon: ({ color }) => <Text style={{ color, fontSize: 20 }}>📋</Text> }}
/>
<Tab.Screen
name="Settings"
component={SettingsScreen}
options={{ tabBarIcon: ({ color }) => <Text style={{ color, fontSize: 20 }}></Text> }}
/>
<Tab.Screen name="Recording" component={RecordingScreen} />
<Tab.Screen name="Saved" component={SavedRecordingsScreen} />
<Tab.Screen name="Settings" component={SettingsScreen} />
</Tab.Navigator>
);
}
@@ -49,9 +47,9 @@ export function AppNavigator() {
<NavigationContainer>
<Stack.Navigator
screenOptions={{
headerStyle: { backgroundColor: '#111' },
headerTintColor: '#fff',
contentStyle: { backgroundColor: '#111' },
headerStyle: { backgroundColor: colors.bg },
headerTintColor: colors.text,
contentStyle: { backgroundColor: colors.bg },
}}
>
<Stack.Screen name="Tabs" component={Tabs} options={{ headerShown: false }} />
+15 -14
View File
@@ -6,6 +6,7 @@ import { saveGpx } from '../services/gpx';
import { insertRecording } from '../services/db';
import { SavedRecording } from '../types';
import { randomUUID } from 'expo-crypto';
import { colors } from '../theme';
export function PostRecordingScreen() {
const nav = useNavigation();
@@ -65,7 +66,7 @@ export function PostRecordingScreen() {
<TextInput
style={styles.input}
placeholder="Activity title"
placeholderTextColor="#555"
placeholderTextColor={colors.placeholder}
value={title}
onChangeText={setTitle}
autoFocus
@@ -75,7 +76,7 @@ export function PostRecordingScreen() {
<Text style={styles.btnText}>{saving ? 'Saving…' : 'Save'}</Text>
</TouchableOpacity>
<TouchableOpacity style={[styles.btn, styles.btnDiscard]} onPress={handleDiscard}>
<Text style={[styles.btnText, { color: '#ef4444' }]}>Discard</Text>
<Text style={[styles.btnText, { color: colors.error }]}>Discard</Text>
</TouchableOpacity>
</ScrollView>
);
@@ -91,16 +92,16 @@ function Stat({ label, value }: { label: string; value: string }) {
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: '#111' },
content: { padding: 24, gap: 16 },
heading: { color: '#fff', fontSize: 24, fontWeight: '700', marginBottom: 8 },
statsRow: { flexDirection: 'row', flexWrap: 'wrap', gap: 12, marginBottom: 8 },
stat: { flex: 1, minWidth: '40%', backgroundColor: '#1e1e1e', borderRadius: 12, padding: 16 },
statLabel: { color: '#888', fontSize: 12, textTransform: 'uppercase' },
statValue: { color: '#fff', fontSize: 22, fontWeight: '600', marginTop: 4 },
input: { backgroundColor: '#1e1e1e', color: '#fff', borderRadius: 12, padding: 16, fontSize: 18 },
btn: { borderRadius: 12, padding: 16, alignItems: 'center' },
btnSave: { backgroundColor: '#22c55e' },
btnDiscard: { backgroundColor: '#1e1e1e' },
btnText: { fontSize: 16, fontWeight: '700', color: '#fff' },
container: { flex: 1, backgroundColor: colors.bg },
content: { padding: 24, gap: 12 },
heading: { color: colors.text, fontSize: 22, fontWeight: '700', marginBottom: 4 },
statsRow: { flexDirection: 'row', flexWrap: 'wrap', gap: 8, marginBottom: 4 },
stat: { flex: 1, minWidth: '40%', backgroundColor: colors.surface, borderWidth: 1, borderColor: colors.border, borderRadius: 10, padding: 14 },
statLabel: { color: colors.textMuted, fontSize: 11, textTransform: 'uppercase', letterSpacing: 0.5 },
statValue: { color: colors.text, fontSize: 22, fontWeight: '700', marginTop: 4 },
input: { backgroundColor: colors.surface, borderWidth: 1, borderColor: colors.border, color: colors.text, borderRadius: 10, padding: 14, fontSize: 16 },
btn: { borderRadius: 10, padding: 16, alignItems: 'center' },
btnSave: { backgroundColor: colors.btnStart },
btnDiscard:{ backgroundColor: colors.surface, borderWidth: 1, borderColor: colors.border },
btnText: { fontSize: 16, fontWeight: '600', color: colors.text },
});
+20 -22
View File
@@ -7,12 +7,13 @@ import { Map, Camera, GeoJSONSource, Layer, UserLocation } from '@maplibre/mapli
import type { LineLayerStyle } from '@maplibre/maplibre-react-native';
import { useRecordingStore } from '../store/recording';
import { startGpsRecording, stopGpsRecording, requestLocationPermissions } from '../services/gps';
import { RootStackParamList, TrackPoint } from '../types';
import { RootStackParamList } from '../types';
import { colors } from '../theme';
const MAP_STYLE = 'https://tiles.openfreemap.org/styles/liberty';
const trackLineStyle: LineLayerStyle = {
lineColor: '#3b82f6',
lineColor: colors.accent,
lineWidth: 3,
lineJoin: 'round',
lineCap: 'round',
@@ -41,10 +42,7 @@ export function RecordingScreen() {
const trackGeoJSON = useMemo<GeoJSON.Feature<GeoJSON.LineString>>(() => ({
type: 'Feature',
geometry: {
type: 'LineString',
coordinates: trackPoints.map((p) => [p.lon, p.lat]),
},
geometry: { type: 'LineString', coordinates: trackPoints.map((p) => [p.lon, p.lat]) },
properties: {},
}), [trackPoints]);
@@ -118,7 +116,7 @@ export function RecordingScreen() {
style={[styles.awakeBtn, keepAwake && styles.awakeBtnOn]}
onPress={() => setKeepAwake(!keepAwake)}
>
<Text style={styles.awakeBtnText}>{keepAwake ? '☀️ Awake' : '💤 Sleep'}</Text>
<Text style={styles.awakeBtnText}>{keepAwake ? ' Awake' : ' Sleep'}</Text>
</TouchableOpacity>
{status === 'idle' && (
@@ -161,21 +159,21 @@ function StatBox({ label, value }: { label: string; value: string }) {
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: '#111' },
statsGrid: { flexDirection: 'row', flexWrap: 'wrap', padding: 8 },
container: { flex: 1, backgroundColor: colors.bg },
statsGrid: { flexDirection: 'row', flexWrap: 'wrap', padding: 8, borderBottomWidth: 1, borderBottomColor: colors.border },
statBox: { width: '25%', padding: 8, alignItems: 'center' },
statLabel: { color: '#888', fontSize: 11, textTransform: 'uppercase' },
statValue: { color: '#fff', fontSize: 18, fontWeight: '600', marginTop: 2 },
statLabel: { color: colors.textMuted, fontSize: 10, textTransform: 'uppercase', letterSpacing: 0.5 },
statValue: { color: colors.text, fontSize: 17, fontWeight: '600', marginTop: 2 },
mapArea: { flex: 1, overflow: 'hidden' },
sensorBtn: { position: 'absolute', top: 10, right: 10, backgroundColor: 'rgba(17,17,17,0.85)', borderRadius: 8, paddingVertical: 6, paddingHorizontal: 12 },
sensorBtnText: { color: '#3b82f6', fontSize: 13, fontWeight: '600' },
controls: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: 12, padding: 20 },
awakeBtn: { backgroundColor: '#1e1e1e', borderRadius: 20, paddingVertical: 8, paddingHorizontal: 14 },
awakeBtnOn: { backgroundColor: '#2a2a1a' },
awakeBtnText: { color: '#aaa', fontSize: 13 },
btn: { paddingVertical: 16, paddingHorizontal: 32, borderRadius: 50 },
btnStart: { backgroundColor: '#22c55e' },
btnPause: { backgroundColor: '#f59e0b' },
btnStop: { backgroundColor: '#ef4444' },
btnText: { color: '#fff', fontSize: 18, fontWeight: '700' },
sensorBtn: { position: 'absolute', top: 10, right: 10, backgroundColor: 'rgba(9,9,11,0.8)', borderRadius: 8, borderWidth: 1, borderColor: colors.border, paddingVertical: 6, paddingHorizontal: 12 },
sensorBtnText: { color: colors.accent, fontSize: 13, fontWeight: '600' },
controls: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: 12, padding: 20, borderTopWidth: 1, borderTopColor: colors.border },
awakeBtn: { backgroundColor: colors.surface, borderRadius: 20, borderWidth: 1, borderColor: colors.border, paddingVertical: 8, paddingHorizontal: 14 },
awakeBtnOn: { borderColor: colors.accent },
awakeBtnText: { color: colors.textSub, fontSize: 13 },
btn: { paddingVertical: 15, paddingHorizontal: 30, borderRadius: 50 },
btnStart: { backgroundColor: colors.btnStart },
btnPause: { backgroundColor: colors.btnPause },
btnStop: { backgroundColor: colors.btnStop },
btnText: { color: '#fff', fontSize: 17, fontWeight: '700' },
});
+12 -11
View File
@@ -6,6 +6,7 @@ import { listRecordings, deleteRecording } from '../services/db';
import { uploadGpx } from '../services/upload';
import { SavedRecording } from '../types';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { colors } from '../theme';
export function SavedRecordingsScreen() {
const [recordings, setRecordings] = useState<SavedRecording[]>([]);
@@ -26,7 +27,7 @@ export function SavedRecordingsScreen() {
const instanceUrl = await AsyncStorage.getItem('instanceUrl');
const apiToken = await AsyncStorage.getItem('apiToken');
if (!instanceUrl || !apiToken) {
Alert.alert('Not configured', 'Set your bincio instance URL and API token in Settings.');
Alert.alert('Not connected', 'Log in to your bincio instance in Settings first.');
return;
}
setUploading(rec.id);
@@ -56,7 +57,7 @@ export function SavedRecordingsScreen() {
return h > 0 ? `${h}h ${m}m` : `${m}m`;
};
if (loading) return <View style={styles.center}><ActivityIndicator color="#fff" /></View>;
if (loading) return <View style={styles.center}><ActivityIndicator color={colors.accent} /></View>;
return (
<View style={styles.container}>
@@ -80,7 +81,7 @@ export function SavedRecordingsScreen() {
<Text style={styles.action}>{uploading === item.id ? '…' : 'Upload'}</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => handleDelete(item)}>
<Text style={[styles.action, { color: '#ef4444' }]}>Delete</Text>
<Text style={[styles.action, { color: colors.error }]}>Delete</Text>
</TouchableOpacity>
</View>
</View>
@@ -92,14 +93,14 @@ export function SavedRecordingsScreen() {
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: '#111' },
center: { flex: 1, backgroundColor: '#111', alignItems: 'center', justifyContent: 'center' },
list: { padding: 16, gap: 12 },
card: { backgroundColor: '#1e1e1e', borderRadius: 12, padding: 16 },
container: { flex: 1, backgroundColor: colors.bg },
center: { flex: 1, backgroundColor: colors.bg, alignItems: 'center', justifyContent: 'center' },
list: { padding: 16, gap: 10 },
card: { backgroundColor: colors.surface, borderWidth: 1, borderColor: colors.border, borderRadius: 12, padding: 16 },
cardMeta: { marginBottom: 12 },
cardTitle: { color: '#fff', fontSize: 17, fontWeight: '600' },
cardSub: { color: '#888', fontSize: 13, marginTop: 4 },
cardTitle: { color: colors.text, fontSize: 15, fontWeight: '600' },
cardSub: { color: colors.textMuted, fontSize: 12, marginTop: 4 },
cardActions:{ flexDirection: 'row', gap: 20 },
action: { color: '#3b82f6', fontWeight: '600', fontSize: 15 },
empty: { color: '#555', textAlign: 'center', marginTop: 60 },
action: { color: colors.accent, fontWeight: '600', fontSize: 14 },
empty: { color: colors.textMuted, textAlign: 'center', marginTop: 60 },
});
+25 -25
View File
@@ -8,6 +8,7 @@ import {
subscribeForDevice, savePairedDevice, loadPairedDevices, removePairedDevice,
} from '../services/ble';
import { BleDevice } from '../types';
import { colors } from '../theme';
const TYPE_LABEL: Record<BleDevice['type'], string> = {
hr: 'Heart Rate',
@@ -29,7 +30,6 @@ export function SensorPairingScreen() {
const [connecting, setConnecting] = useState<Record<string, boolean>>({});
const stopScanRef = useRef<(() => void) | null>(null);
// Load saved devices and attempt reconnect on mount
useEffect(() => {
(async () => {
const devices = await loadPairedDevices();
@@ -89,14 +89,14 @@ export function SensorPairingScreen() {
setSaved((prev) => prev.filter((e) => e.device.id !== device.id));
}
const savedIds = new Set(saved.map((e) => e.device.id));
const newFound = found.filter((d) => !savedIds.has(d.id));
type ListItem =
| { kind: 'header'; label: string }
| { kind: 'saved'; entry: SavedEntry }
| { kind: 'found'; device: BleDevice };
const savedIds = new Set(saved.map((e) => e.device.id));
const newFound = found.filter((d) => !savedIds.has(d.id));
const listData: ListItem[] = [
...(saved.length > 0 ? [{ kind: 'header' as const, label: 'Saved sensors' }, ...saved.map((e) => ({ kind: 'saved' as const, entry: e }))] : []),
...(newFound.length > 0 ? [{ kind: 'header' as const, label: 'Found nearby' }, ...newFound.map((d) => ({ kind: 'found' as const, device: d }))] : []),
@@ -104,11 +104,10 @@ export function SensorPairingScreen() {
return (
<View style={styles.container}>
<Text style={styles.heading}>Sensors</Text>
<Text style={styles.sub}>Pair your HR monitor, power meter, or cadence sensor.</Text>
<TouchableOpacity style={styles.scanBtn} onPress={handleScan} disabled={scanning}>
{scanning ? <ActivityIndicator color="#fff" /> : <Text style={styles.scanBtnText}>Scan for sensors</Text>}
{scanning ? <ActivityIndicator color={colors.text} /> : <Text style={styles.scanBtnText}>Scan for sensors</Text>}
</TouchableOpacity>
<FlatList
@@ -128,7 +127,7 @@ export function SensorPairingScreen() {
<Text style={styles.deviceType}>{TYPE_LABEL[entry.device.type]}</Text>
</View>
<View style={styles.deviceActions}>
{entry.status === 'connecting' && <ActivityIndicator color="#3b82f6" />}
{entry.status === 'connecting' && <ActivityIndicator color={colors.accent} />}
{entry.status === 'connected' && <Text style={styles.connectedLabel}>Connected</Text>}
{(entry.status === 'saved' || entry.status === 'error') && (
<TouchableOpacity style={styles.reconnectBtn} onPress={() => reconnect(entry.device, true)}>
@@ -155,7 +154,9 @@ export function SensorPairingScreen() {
onPress={() => handleConnect(device)}
disabled={isConnecting}
>
{isConnecting ? <ActivityIndicator color="#fff" /> : <Text style={styles.connectBtnText}>Connect</Text>}
{isConnecting
? <ActivityIndicator color={colors.text} />
: <Text style={styles.connectBtnText}>Connect</Text>}
</TouchableOpacity>
</View>
);
@@ -169,23 +170,22 @@ export function SensorPairingScreen() {
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: '#111', padding: 24 },
heading: { color: '#fff', fontSize: 24, fontWeight: '700' },
sub: { color: '#888', marginTop: 4, marginBottom: 20 },
scanBtn: { backgroundColor: '#3b82f6', borderRadius: 12, padding: 14, alignItems: 'center', marginBottom: 8 },
scanBtnText: { color: '#fff', fontSize: 16, fontWeight: '700' },
list: { flex: 1, marginTop: 8 },
sectionHeader: { color: '#555', fontSize: 12, textTransform: 'uppercase', letterSpacing: 0.8, marginTop: 16, marginBottom: 8 },
deviceRow: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', backgroundColor: '#1e1e1e', borderRadius: 12, padding: 16, marginBottom: 8 },
container: { flex: 1, backgroundColor: colors.bg, padding: 16 },
sub: { color: colors.textMuted, fontSize: 13, marginBottom: 16 },
scanBtn: { backgroundColor: colors.surface, borderWidth: 1, borderColor: colors.border, borderRadius: 10, padding: 14, alignItems: 'center', marginBottom: 4 },
scanBtnText: { color: colors.text, fontSize: 15, fontWeight: '600' },
list: { flex: 1, marginTop: 4 },
sectionHeader: { color: colors.textMuted, fontSize: 11, textTransform: 'uppercase', letterSpacing: 0.8, marginTop: 20, marginBottom: 8 },
deviceRow: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', backgroundColor: colors.surface, borderWidth: 1, borderColor: colors.border, borderRadius: 10, padding: 14, marginBottom: 8 },
deviceInfo: { flex: 1, marginRight: 12 },
deviceName: { color: '#fff', fontSize: 16, fontWeight: '600' },
deviceType: { color: '#888', fontSize: 13, marginTop: 2 },
deviceName: { color: colors.text, fontSize: 15, fontWeight: '600' },
deviceType: { color: colors.textMuted, fontSize: 12, marginTop: 2 },
deviceActions: { flexDirection: 'row', alignItems: 'center', gap: 12 },
connectedLabel: { color: '#22c55e', fontWeight: '600', fontSize: 14 },
reconnectBtn: { backgroundColor: '#1e3a5f', borderRadius: 8, paddingVertical: 7, paddingHorizontal: 12 },
reconnectBtnText: { color: '#3b82f6', fontWeight: '600', fontSize: 14 },
forgetLabel: { color: '#ef4444', fontSize: 14 },
connectBtn: { backgroundColor: '#3b82f6', borderRadius: 8, paddingVertical: 8, paddingHorizontal: 16, minWidth: 80, alignItems: 'center' },
connectBtnText: { color: '#fff', fontWeight: '600' },
empty: { color: '#555', textAlign: 'center', marginTop: 40 },
connectedLabel: { color: colors.success, fontWeight: '600', fontSize: 13 },
reconnectBtn: { backgroundColor: colors.accentDim, borderRadius: 8, paddingVertical: 6, paddingHorizontal: 12 },
reconnectBtnText:{ color: colors.accent, fontWeight: '600', fontSize: 13 },
forgetLabel: { color: colors.error, fontSize: 13 },
connectBtn: { backgroundColor: colors.accentDim, borderRadius: 8, paddingVertical: 7, paddingHorizontal: 14, minWidth: 80, alignItems: 'center' },
connectBtnText: { color: colors.accent, fontWeight: '600' },
empty: { color: colors.textMuted, textAlign: 'center', marginTop: 40 },
});
+27 -21
View File
@@ -5,6 +5,7 @@ import {
} from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { login, logout, loadAuthState } from '../services/auth';
import { colors } from '../theme';
export function SettingsScreen() {
const [instanceUrl, setInstanceUrl] = useState('');
@@ -77,7 +78,7 @@ export function SettingsScreen() {
value={instanceUrl}
onChangeText={setInstanceUrl}
placeholder="https://bincio.example.com"
placeholderTextColor="#555"
placeholderTextColor={colors.placeholder}
autoCapitalize="none"
keyboardType="url"
editable={!connectedAs}
@@ -101,7 +102,7 @@ export function SettingsScreen() {
value={handle}
onChangeText={setHandle}
placeholder="your-handle"
placeholderTextColor="#555"
placeholderTextColor={colors.placeholder}
autoCapitalize="none"
autoCorrect={false}
/>
@@ -112,7 +113,7 @@ export function SettingsScreen() {
value={password}
onChangeText={setPassword}
placeholder="••••••••"
placeholderTextColor="#555"
placeholderTextColor={colors.placeholder}
secureTextEntry
/>
@@ -126,7 +127,7 @@ export function SettingsScreen() {
disabled={connecting}
>
{connecting
? <ActivityIndicator color="#fff" />
? <ActivityIndicator color={colors.text} />
: <Text style={styles.connectBtnText}>Connect</Text>}
</TouchableOpacity>
</>
@@ -136,27 +137,32 @@ export function SettingsScreen() {
<View style={styles.row}>
<Text style={styles.rowLabel}>Kilometre alerts</Text>
<Switch value={kmNotifications} onValueChange={handleKmToggle} trackColor={{ true: '#3b82f6' }} />
<Switch
value={kmNotifications}
onValueChange={handleKmToggle}
trackColor={{ true: colors.accent }}
thumbColor={colors.text}
/>
</View>
</ScrollView>
);
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: '#111' },
content: { padding: 24, gap: 12 },
sectionTitle: { color: '#888', fontSize: 13, textTransform: 'uppercase', letterSpacing: 0.8, marginTop: 16 },
label: { color: '#aaa', fontSize: 14, marginBottom: 4 },
input: { backgroundColor: '#1e1e1e', color: '#fff', borderRadius: 10, padding: 14, fontSize: 16 },
hint: { color: '#555', fontSize: 13, lineHeight: 18 },
connectedBox: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', backgroundColor: '#1e1e1e', borderRadius: 10, padding: 14 },
connectedLabel: { color: '#22c55e', fontSize: 12, textTransform: 'uppercase', letterSpacing: 0.6 },
connectedName: { color: '#fff', fontSize: 16, fontWeight: '600', marginTop: 2 },
disconnectBtn: { paddingVertical: 6, paddingHorizontal: 12, borderRadius: 8, borderWidth: 1, borderColor: '#ef4444' },
disconnectBtnText: { color: '#ef4444', fontWeight: '600' },
connectBtn: { backgroundColor: '#3b82f6', borderRadius: 12, padding: 16, alignItems: 'center', marginTop: 4 },
connectBtnDisabled: { opacity: 0.6 },
connectBtnText: { color: '#fff', fontSize: 16, fontWeight: '700' },
row: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', backgroundColor: '#1e1e1e', borderRadius: 10, padding: 14 },
rowLabel: { color: '#fff', fontSize: 16 },
container: { flex: 1, backgroundColor: colors.bg },
content: { padding: 16, gap: 10 },
sectionTitle: { color: colors.textMuted, fontSize: 11, textTransform: 'uppercase', letterSpacing: 0.8, marginTop: 16 },
label: { color: colors.textSub, fontSize: 13, marginBottom: 2 },
input: { backgroundColor: colors.surface, borderWidth: 1, borderColor: colors.border, color: colors.text, borderRadius: 8, padding: 12, fontSize: 15 },
hint: { color: colors.textMuted, fontSize: 12, lineHeight: 18 },
connectedBox: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', backgroundColor: colors.surface, borderWidth: 1, borderColor: colors.border, borderRadius: 10, padding: 14 },
connectedLabel: { color: colors.success, fontSize: 11, textTransform: 'uppercase', letterSpacing: 0.6 },
connectedName: { color: colors.text, fontSize: 15, fontWeight: '600', marginTop: 2 },
disconnectBtn: { paddingVertical: 6, paddingHorizontal: 12, borderRadius: 8, borderWidth: 1, borderColor: colors.errorBg },
disconnectBtnText: { color: colors.error, fontWeight: '600', fontSize: 13 },
connectBtn: { backgroundColor: colors.surface, borderWidth: 1, borderColor: colors.border, borderRadius: 10, padding: 14, alignItems: 'center', marginTop: 4 },
connectBtnDisabled: { opacity: 0.5 },
connectBtnText: { color: colors.text, fontSize: 15, fontWeight: '600' },
row: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', backgroundColor: colors.surface, borderWidth: 1, borderColor: colors.border, borderRadius: 10, padding: 14 },
rowLabel: { color: colors.text, fontSize: 15 },
});
+24
View File
@@ -0,0 +1,24 @@
export const colors = {
bg: '#09090b',
surface: '#18181b',
border: '#27272a',
borderStrong: '#3f3f46',
text: '#f4f4f5',
textSub: '#a1a1aa',
textMuted: '#71717a',
placeholder: '#52525b',
accent: '#60a5fa',
accentDim: 'rgba(96,165,250,0.15)',
success: '#86efac',
successBg: '#14532d',
error: '#fca5a5',
errorBg: '#7f1d1d',
// recording action buttons
btnStart: '#16a34a',
btnPause: '#d97706',
btnStop: '#dc2626',
} as const;