diff --git a/app/(tabs)/explore.tsx b/app/(tabs)/explore.tsx index 7c26189..101d3a3 100644 --- a/app/(tabs)/explore.tsx +++ b/app/(tabs)/explore.tsx @@ -15,7 +15,7 @@ export default function SettingsScreen() { return ( - Settings + Settings Manage your preferences @@ -27,7 +27,7 @@ export default function SettingsScreen() { - + {user?.name ?.split(' ') @@ -36,11 +36,11 @@ export default function SettingsScreen() { - {user?.name} + {user?.name} {user?.email} - {user?.role} + {user?.role} @@ -53,8 +53,8 @@ export default function SettingsScreen() { - - Dark Mode + + Dark Mode {themeMode === 'system' ? 'Auto' : themeMode === 'dark' ? 'On' : 'Off'} @@ -65,45 +65,45 @@ export default function SettingsScreen() { handleThemeChange('light')} activeOpacity={0.7} > - - Light + + Light {themeMode === 'light' && ( - + )} handleThemeChange('dark')} activeOpacity={0.7} > - - Dark + + Dark {themeMode === 'dark' && ( - + )} handleThemeChange('system')} activeOpacity={0.7} > - - Auto + + Auto {themeMode === 'system' && ( - + )} @@ -117,13 +117,15 @@ export default function SettingsScreen() { - Blocking Alerts + + Blocking Alerts + @@ -131,13 +133,15 @@ export default function SettingsScreen() { - Review Requests + + Review Requests + @@ -145,13 +149,15 @@ export default function SettingsScreen() { - Session Updates + + Session Updates + @@ -159,13 +165,15 @@ export default function SettingsScreen() { - Features & News + + Features & News + @@ -178,11 +186,11 @@ export default function SettingsScreen() { ACCOUNT - Sign Out + Sign Out diff --git a/app/(tabs)/inbox.tsx b/app/(tabs)/inbox.tsx index 180506d..ef80a80 100644 --- a/app/(tabs)/inbox.tsx +++ b/app/(tabs)/inbox.tsx @@ -31,7 +31,7 @@ export default function InboxScreen() { + } showsVerticalScrollIndicator={false} > diff --git a/app/(tabs)/index.tsx b/app/(tabs)/index.tsx index 764bb49..38a8e6c 100644 --- a/app/(tabs)/index.tsx +++ b/app/(tabs)/index.tsx @@ -16,7 +16,6 @@ import { useOffline } from '@/hooks/useOffline' import { useRealtimeSession } from '@/hooks/useRealtimeSession' import { useNotifications } from '@/hooks/useNotifications' import { Header } from '@/components/layout/Header' -import { FAB } from '@/components/layout/FAB' import { SessionCard } from '@/components/session/SessionCard' import { ErrorMessage } from '@/components/ui/ErrorMessage' import { OfflineBanner } from '@/components/ui/OfflineBanner' @@ -49,7 +48,7 @@ const QuickActionButton = memo( 0 ? unreadCount : undefined, onPress: () => router.push('/notifications'), }, - { id: 'lucky', icon: 'dice.fill', text: "I'm Feeling Lucky" }, - { id: 'inspire', icon: 'lightbulb.fill', text: 'Inspire Me' }, - { id: 'invent', icon: 'sparkles', text: 'Go Invent' }, - { id: 'add', icon: 'plus.circle.fill', text: 'Add Action', disabled: true, badge: 'Soon' }, ], [runningSessions.length, unreadCount, router] ) @@ -170,7 +165,7 @@ export default function DashboardScreen() { if (authLoading) { return ( - Loading... + Loading... ) } @@ -201,7 +196,7 @@ export default function DashboardScreen() { @@ -209,12 +204,12 @@ export default function DashboardScreen() { style={[ styles.connectionDot, { - backgroundColor: isError ? colors.error : colors.warning, + backgroundColor: isError ? colors.danger : colors.warning, }, ]} /> {isError ? 'Real-time updates unavailable' : 'Connecting to updates...'} @@ -227,7 +222,7 @@ export default function DashboardScreen() { accessibilityLabel="Retry connection" accessibilityHint="Double tap to retry real-time connection" > - Retry + Retry )} @@ -235,7 +230,7 @@ export default function DashboardScreen() { {/* Quick Actions - 2 rows of 3 */} - Quick Actions + Quick Actions {quickActions.map((action) => ( @@ -249,7 +244,7 @@ export default function DashboardScreen() { {awaitingReview.length > 0 && ( - My Reviews + My Reviews - Active Sessions + + Active Sessions + {runningSessions.length > 0 && ( - View All + View All )} @@ -319,9 +316,6 @@ export default function DashboardScreen() { - - {/* Floating Action Button */} - ) } diff --git a/app/_layout.tsx b/app/_layout.tsx index 262f338..d8748ab 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -84,7 +84,7 @@ function RootLayoutNav() { headerStyle: { backgroundColor: colors.bg, }, - headerTintColor: colors.text, + headerTintColor: colors.textPrimary, headerShadowVisible: false, headerBackTitle: '', contentStyle: { diff --git a/app/announcements/index.tsx b/app/announcements/index.tsx index 0134186..3f0af43 100644 --- a/app/announcements/index.tsx +++ b/app/announcements/index.tsx @@ -90,7 +90,7 @@ export default function AnnouncementsScreen() { styles.announcementCard, { backgroundColor: colors.card, - borderColor: unread ? colors.accent : colors.border, + borderColor: unread ? colors.primary : colors.border, }, ]} onPress={() => handleAnnouncementPress(item)} @@ -98,11 +98,13 @@ export default function AnnouncementsScreen() { > - + {item.title} {unread && ( - + NEW )} @@ -124,7 +126,7 @@ export default function AnnouncementsScreen() { if (isLoading) { return ( - + ) } @@ -133,8 +135,8 @@ export default function AnnouncementsScreen() { {/* Header */} - - Announcements + + Announcements {/* Announcements List */} @@ -146,7 +148,7 @@ export default function AnnouncementsScreen() { ListEmptyComponent={ - + No announcements yet diff --git a/app/chat.tsx b/app/chat.tsx index 68916c9..68fcd03 100644 --- a/app/chat.tsx +++ b/app/chat.tsx @@ -78,13 +78,15 @@ export default function ChatModal() { {/* Loading state */} {isLoading ? ( - + ) : messages.length === 0 ? ( /* Empty state */ - Ask Claude anything + + Ask Claude anything + Your conversation will be saved locally diff --git a/app/decisions/[id].tsx b/app/decisions/[id].tsx index 69c82e1..7916ac6 100644 --- a/app/decisions/[id].tsx +++ b/app/decisions/[id].tsx @@ -26,7 +26,7 @@ export default function ReviewDecisionScreen() { if (!decision?.details) { return ( - Decision not found + Decision not found ) } @@ -79,7 +79,7 @@ export default function ReviewDecisionScreen() { = step ? colors.accent : colors.card }, + { backgroundColor: currentStep >= step ? colors.primary : colors.card }, ]} > step ? colors.accent : colors.card }, + { backgroundColor: currentStep > step ? colors.primary : colors.card }, ]} /> )} @@ -106,20 +106,26 @@ export default function ReviewDecisionScreen() { {/* Step 1: Summary */} {currentStep === 1 && ( - Question - {details.question} + Question + + {details.question} + - Context - {details.context} + Context + {details.context} - Analysis - {details.analysis} + Analysis + + {details.analysis} + - Recommendation - {details.recommendation} + Recommendation + + {details.recommendation} + setCurrentStep(2)} > Next → @@ -130,7 +136,7 @@ export default function ReviewDecisionScreen() { {/* Step 2: Key Sections */} {currentStep === 2 && ( - Key Sections + Key Sections {details.sections.map((section) => ( @@ -138,11 +144,11 @@ export default function ReviewDecisionScreen() { style={styles.accordionHeader} onPress={() => handleExpandSection(section.id)} > - + {viewedSections.has(section.id) ? '✓ ' : ''} {section.title} - + {expandedSections.has(section.id) ? '▼' : '▶'} @@ -163,7 +169,7 @@ export default function ReviewDecisionScreen() { allSectionsViewed && setCurrentStep(3)} disabled={!allSectionsViewed} @@ -183,7 +189,7 @@ export default function ReviewDecisionScreen() { {/* Step 3: Comment + Complete */} {currentStep === 3 && ( - Your Feedback + Your Feedback {['Looks good', 'Needs discussion', 'Try different approach'].map((chip) => ( @@ -192,8 +198,8 @@ export default function ReviewDecisionScreen() { style={[ styles.chip, { - backgroundColor: quickResponse === chip ? colors.accent : colors.card, - borderColor: colors.accent, + backgroundColor: quickResponse === chip ? colors.primary : colors.card, + borderColor: colors.primary, }, ]} onPress={() => handleQuickResponse(chip)} @@ -201,7 +207,7 @@ export default function ReviewDecisionScreen() { {chip} @@ -213,7 +219,7 @@ export default function ReviewDecisionScreen() { Complete Review diff --git a/app/decisions/index.tsx b/app/decisions/index.tsx index 3aed153..411ada3 100644 --- a/app/decisions/index.tsx +++ b/app/decisions/index.tsx @@ -29,7 +29,7 @@ export default function DecisionQueueScreen() { style={[styles.container, { backgroundColor: colors.bg }]} contentContainerStyle={styles.content} refreshControl={ - + } /> ) diff --git a/app/login.tsx b/app/login.tsx index c9a98f8..db370f2 100644 --- a/app/login.tsx +++ b/app/login.tsx @@ -64,7 +64,7 @@ export default function LoginScreen() { if (isLoading) { return ( - + Checking authentication... @@ -76,10 +76,10 @@ export default function LoginScreen() { {/* Logo Section */} - - + + - ACP Mobile + ACP Mobile Ambient Code Platform @@ -95,7 +95,7 @@ export default function LoginScreen() { - + {notif.title} @@ -73,7 +73,7 @@ export default function NotificationHistoryScreen() { {notif.status === 'dismissed' && ( handleRestore(notif.id)} > Restore diff --git a/app/notifications/index.tsx b/app/notifications/index.tsx index bf0c8f7..45b6dc3 100644 --- a/app/notifications/index.tsx +++ b/app/notifications/index.tsx @@ -90,7 +90,7 @@ export default function NotificationsScreen() { {/* Header with Mark All Read button */} - Notifications + Notifications {unreadCount > 0 && ( - - Mark all read + + Mark all read )} @@ -121,8 +121,8 @@ export default function NotificationsScreen() { style={[ styles.filterChip, { - backgroundColor: isActive ? colors.accent : colors.card, - borderColor: isActive ? colors.accent : colors.border, + backgroundColor: isActive ? colors.primary : colors.card, + borderColor: isActive ? colors.primary : colors.border, }, ]} onPress={() => setFilter(f.value)} @@ -132,14 +132,14 @@ export default function NotificationsScreen() { accessibilityState={{ selected: isActive }} accessibilityHint={`Double tap to show ${f.label.toLowerCase()} notifications`} > - + {f.label} {f.badge !== undefined && f.badge > 0 && ( - + {f.badge > 99 ? '99+' : f.badge} @@ -164,8 +164,8 @@ export default function NotificationsScreen() { style={[ styles.filterChip, { - backgroundColor: isActive ? colors.accent : colors.card, - borderColor: isActive ? colors.accent : colors.border, + backgroundColor: isActive ? colors.primary : colors.card, + borderColor: isActive ? colors.primary : colors.border, }, ]} onPress={() => setSourceFilter(f.value)} @@ -177,9 +177,9 @@ export default function NotificationsScreen() { - + {f.label} @@ -206,7 +206,9 @@ export default function NotificationsScreen() { ) : ( - No notifications + + No notifications + {filter === 'unread' ? 'All caught up! No unread notifications.' diff --git a/app/rfe/create.tsx b/app/rfe/create.tsx index 147928b..d137753 100644 --- a/app/rfe/create.tsx +++ b/app/rfe/create.tsx @@ -98,20 +98,24 @@ export default function CreateRFEScreen() { accessibilityRole="button" accessibilityLabel="Go back" > - + - Create RFE + Create RFE {/* Title Input */} - Title + Title - Description + Description - Sources & Context + Sources & Context Add Google Docs, URLs, or Jira tickets as context for your RFE @@ -153,7 +161,7 @@ export default function CreateRFEScreen() { style={[ styles.typeButton, { - backgroundColor: newSourceType === type ? colors.accent : colors.card, + backgroundColor: newSourceType === type ? colors.primary : colors.card, borderColor: colors.border, }, ]} @@ -164,12 +172,12 @@ export default function CreateRFEScreen() { {type.toUpperCase()} @@ -183,7 +191,11 @@ export default function CreateRFEScreen() { - + {source.type.toUpperCase()} - + {source.value} @@ -228,7 +243,7 @@ export default function CreateRFEScreen() { accessibilityRole="button" accessibilityLabel="Remove source" > - + ))} @@ -237,9 +252,9 @@ export default function CreateRFEScreen() { {/* Batch Mode Info */} - - - + + + This RFE will be created in batch mode. Attach your Google Doc design document to the session for processing. @@ -249,7 +264,7 @@ export default function CreateRFEScreen() { - + Loading session... @@ -36,7 +36,7 @@ export default function SessionDetailScreen() { if (error || !session) { return ( - + Failed to load session details @@ -58,12 +58,12 @@ export default function SessionDetailScreen() { > {/* Header with Current Task */} - {session.name} + {session.name} - - + + {getSimpleModelName(session.model)} @@ -74,7 +74,7 @@ export default function SessionDetailScreen() { Currently working on: - + {session.currentTask} @@ -83,7 +83,7 @@ export default function SessionDetailScreen() { Progress - + {session.progress}% @@ -96,11 +96,11 @@ export default function SessionDetailScreen() { - Error - + Error + {session.errorMessage} @@ -119,12 +119,12 @@ export default function SessionDetailScreen() { }, ]} > - Review Required + Review Required This session has completed and is ready for your review. router.push(`/sessions/${session.id}/review`)} activeOpacity={0.8} > @@ -141,7 +141,7 @@ export default function SessionDetailScreen() { onPress={() => setTasksExpanded(!tasksExpanded)} activeOpacity={0.7} > - + Tasks Completed ({session.tasksCompleted.length}) @@ -169,7 +169,9 @@ export default function SessionDetailScreen() { onPress={() => setDetailsExpanded(!detailsExpanded)} activeOpacity={0.7} > - Details + + Details + {detailsExpanded ? '▼' : '▶'} @@ -181,31 +183,31 @@ export default function SessionDetailScreen() { Repository: - + {session.repository.name} Branch: - + {session.repository.branch} Workflow: - + {session.workflowType} Created: - + {session.createdAt.toLocaleDateString()} at {session.createdAt.toLocaleTimeString()} Updated: - + {session.updatedAt.toLocaleDateString()} at {session.updatedAt.toLocaleTimeString()} diff --git a/app/sessions/[id]/review.tsx b/app/sessions/[id]/review.tsx index 85d2a3d..6575b17 100644 --- a/app/sessions/[id]/review.tsx +++ b/app/sessions/[id]/review.tsx @@ -106,7 +106,7 @@ export default function SessionReviewScreen() { if (isLoading || !session) { return ( - + ) } @@ -116,7 +116,7 @@ export default function SessionReviewScreen() { {/* Session Info */} - {session.name} + {session.name} Review the changes and provide feedback @@ -124,25 +124,25 @@ export default function SessionReviewScreen() { {/* Tasks Completed */} - Changes Made + Changes Made {session.tasksCompleted.map((task, index) => ( - {task} + {task} ))} {/* Comment Box */} - Your Feedback + Your Feedback Add comments to guide the next iteration. Be specific about what needs to change. ('all') // Set initial filter from URL parameter @@ -61,8 +63,8 @@ export default function SessionsListScreen() { style={[ styles.filterChip, { - backgroundColor: isActive ? colors.accent : colors.card, - borderColor: isActive ? colors.accent : colors.border, + backgroundColor: isActive ? colors.primary : colors.card, + borderColor: isActive ? colors.primary : colors.border, }, ]} onPress={() => setFilter(f.value)} @@ -72,7 +74,7 @@ export default function SessionsListScreen() { accessibilityState={{ selected: isActive }} accessibilityHint={`Double tap to show ${f.label.toLowerCase()} sessions`} > - + {f.label} @@ -97,11 +99,13 @@ export default function SessionsListScreen() { Loading sessions... ) : ( - - - No sessions found - - + router.push('/chat')} + /> ) } ListFooterComponent={} diff --git a/app/settings/appearance.tsx b/app/settings/appearance.tsx index 0f82373..bd48169 100644 --- a/app/settings/appearance.tsx +++ b/app/settings/appearance.tsx @@ -4,6 +4,7 @@ import { useTheme } from '../../hooks/useTheme' import { useOffline } from '../../hooks/useOffline' import { OfflineBanner } from '../../components/ui/OfflineBanner' import { PreferencesService } from '../../services/storage/preferences' +import { TOKENS } from '../../utils/constants' type ThemeMode = 'light' | 'dark' | 'system' @@ -84,15 +85,15 @@ function ThemeOption({ label, description, selected, onSelect }: ThemeOptionProp const styles = StyleSheet.create({ container: { flex: 1, - backgroundColor: '#f9fafb', + backgroundColor: TOKENS.bg, }, description: { fontSize: 14, - color: '#6b7280', + color: TOKENS.textSecondary, padding: 16, }, section: { - backgroundColor: '#fff', + backgroundColor: TOKENS.card, borderRadius: 12, marginHorizontal: 16, overflow: 'hidden', @@ -102,7 +103,7 @@ const styles = StyleSheet.create({ alignItems: 'center', padding: 16, borderBottomWidth: 1, - borderBottomColor: '#f3f4f6', + borderBottomColor: TOKENS.border, }, optionContent: { flex: 1, @@ -110,29 +111,29 @@ const styles = StyleSheet.create({ optionLabel: { fontSize: 16, fontWeight: '600', - color: '#1a1a1a', + color: TOKENS.textPrimary, marginBottom: 2, }, optionDescription: { fontSize: 13, - color: '#6b7280', + color: TOKENS.textSecondary, }, radio: { width: 24, height: 24, borderRadius: 12, borderWidth: 2, - borderColor: '#d1d5db', + borderColor: TOKENS.border, justifyContent: 'center', alignItems: 'center', }, radioSelected: { - borderColor: '#8b5cf6', + borderColor: TOKENS.primary, }, radioInner: { width: 12, height: 12, borderRadius: 6, - backgroundColor: '#8b5cf6', + backgroundColor: TOKENS.primary, }, }) diff --git a/app/settings/index.tsx b/app/settings/index.tsx index 3b92f4c..71f3de3 100644 --- a/app/settings/index.tsx +++ b/app/settings/index.tsx @@ -9,6 +9,7 @@ import { useOffline } from '../../hooks/useOffline' import { userApi } from '../../services/api/user' import { PreferencesService } from '../../services/storage/preferences' import type { User } from '../../types/user' +import { TOKENS } from '../../utils/constants' const FEEDBACK_FORM_URL = 'https://docs.google.com/forms/d/e/1FAIpQLScQwBV4ZH2b3Fm_D0IDzIwKyCa-B8AnKhAOXZj3_F5cN0Gm8Q/viewform' @@ -137,8 +138,6 @@ export default function SettingsScreen() { icon="git-branch-outline" onPress={() => router.push('/settings/repos')} /> - - {/* Preferences Section */} @@ -167,7 +166,7 @@ export default function SettingsScreen() { const styles = StyleSheet.create({ container: { flex: 1, - backgroundColor: '#f9fafb', + backgroundColor: TOKENS.bg, }, loading: { flex: 1, @@ -180,7 +179,7 @@ const styles = StyleSheet.create({ sectionHeader: { fontSize: 13, fontWeight: '600', - color: '#6b7280', + color: TOKENS.textMuted, textTransform: 'uppercase', letterSpacing: 0.5, paddingHorizontal: 16, diff --git a/app/settings/notifications.tsx b/app/settings/notifications.tsx index 4955f12..0382872 100644 --- a/app/settings/notifications.tsx +++ b/app/settings/notifications.tsx @@ -6,6 +6,7 @@ import { useOffline } from '../../hooks/useOffline' import { userApi } from '../../services/api/user' import { PreferencesService } from '../../services/storage/preferences' import type { NotificationPreferences } from '../../types/user' +import { TOKENS } from '../../utils/constants' export default function NotificationsSettingsScreen() { const { isOffline } = useOffline() @@ -113,7 +114,7 @@ export default function NotificationsSettingsScreen() { const styles = StyleSheet.create({ container: { flex: 1, - backgroundColor: '#f9fafb', + backgroundColor: TOKENS.bg, }, loading: { flex: 1, @@ -122,11 +123,11 @@ const styles = StyleSheet.create({ }, description: { fontSize: 14, - color: '#6b7280', + color: TOKENS.textSecondary, padding: 16, }, section: { - backgroundColor: '#fff', + backgroundColor: TOKENS.card, borderRadius: 12, marginHorizontal: 16, overflow: 'hidden', diff --git a/app/settings/privacy.tsx b/app/settings/privacy.tsx index c91a1df..85995e0 100644 --- a/app/settings/privacy.tsx +++ b/app/settings/privacy.tsx @@ -1,6 +1,7 @@ import React, { useState, useEffect } from 'react' import { View, Text, Switch, StyleSheet, ScrollView } from 'react-native' import { getTelemetryEnabled, setTelemetryEnabled } from '@/services/telemetry' +import { TOKENS } from '@/utils/constants' export default function PrivacySettingsScreen() { const [telemetryEnabled, setTelemetryEnabledState] = useState(true) @@ -51,29 +52,23 @@ export default function PrivacySettingsScreen() { value={telemetryEnabled} onValueChange={handleToggle} disabled={isLoading} - trackColor={{ false: '#d1d5db', true: '#a78bfa' }} - thumbColor={telemetryEnabled ? '#8b5cf6' : '#f4f3f4'} - ios_backgroundColor="#d1d5db" + trackColor={{ false: TOKENS.border, true: TOKENS.primary }} + thumbColor={telemetryEnabled ? '#fff' : '#f4f3f4'} + ios_backgroundColor={TOKENS.border} /> What data is collected? - When analytics are enabled, we collect: + • Anonymous usage patterns and performance metrics - • App usage patterns (screens viewed, features used) + • We never collect your code, messages, or credentials + + + • All data is anonymized and used only for improvements - • Performance metrics (app crashes, errors) - • Device information (OS version, model) - - - We never collect: - - • Your code or repository content - • Personal messages or chat conversations - • Passwords or authentication tokens @@ -87,15 +82,15 @@ export default function PrivacySettingsScreen() { const styles = StyleSheet.create({ container: { flex: 1, - backgroundColor: '#f9fafb', + backgroundColor: TOKENS.bg, }, description: { fontSize: 14, - color: '#6b7280', + color: TOKENS.textSecondary, padding: 16, }, section: { - backgroundColor: '#fff', + backgroundColor: TOKENS.card, borderRadius: 12, marginHorizontal: 16, overflow: 'hidden', @@ -113,12 +108,12 @@ const styles = StyleSheet.create({ settingLabel: { fontSize: 16, fontWeight: '600', - color: '#1a1a1a', + color: TOKENS.textPrimary, marginBottom: 4, }, settingDescription: { fontSize: 13, - color: '#6b7280', + color: TOKENS.textSecondary, lineHeight: 18, }, infoSection: { @@ -129,12 +124,12 @@ const styles = StyleSheet.create({ infoTitle: { fontSize: 16, fontWeight: '600', - color: '#1a1a1a', + color: TOKENS.textPrimary, marginBottom: 12, }, infoText: { fontSize: 14, - color: '#374151', + color: TOKENS.textSecondary, marginTop: 16, marginBottom: 8, }, @@ -143,13 +138,13 @@ const styles = StyleSheet.create({ }, bulletPoint: { fontSize: 14, - color: '#6b7280', + color: TOKENS.textSecondary, lineHeight: 22, marginBottom: 4, }, noteText: { fontSize: 13, - color: '#9ca3af', + color: TOKENS.textMuted, fontStyle: 'italic', marginTop: 16, }, diff --git a/components/ErrorBoundary.tsx b/components/ErrorBoundary.tsx index 2e9194d..8987772 100644 --- a/components/ErrorBoundary.tsx +++ b/components/ErrorBoundary.tsx @@ -74,9 +74,9 @@ function ErrorFallback({ error, onReset }: { error: Error | null; onReset: () => return ( - Something went wrong + Something went wrong - + {error?.message || 'An unexpected error occurred'} @@ -90,7 +90,7 @@ function ErrorFallback({ error, onReset }: { error: Error | null; onReset: () => )} diff --git a/components/PerformanceMonitor.tsx b/components/PerformanceMonitor.tsx index 64bb2b0..24413fe 100644 --- a/components/PerformanceMonitor.tsx +++ b/components/PerformanceMonitor.tsx @@ -65,27 +65,27 @@ export function PerformanceMonitor() { {/* Header */} - Performance Monitor + Performance Monitor setIsVisible(false)} style={styles.closeButton}> - + {/* Memory Stats */} - Memory + Memory {metrics.memory ? ( <> Used: - + {formatBytes(metrics.memory.usedJSHeapSize)} Total: - + {formatBytes(metrics.memory.totalJSHeapSize)} @@ -123,7 +123,7 @@ export function PerformanceMonitor() { {/* FPS Stats */} - Frame Rate + Frame Rate {metrics.fps ? ( <> @@ -140,19 +140,19 @@ export function PerformanceMonitor() { Average: - + {metrics.fps.average.toFixed(1)} FPS Min / Max: - + {metrics.fps.min.toFixed(0)} / {metrics.fps.max.toFixed(0)} Slow Frames: - + {metrics.fps.slowFrameCount} ( {((metrics.fps.slowFrameCount / metrics.fps.totalFrames) * 100).toFixed(1)} %) @@ -168,7 +168,9 @@ export function PerformanceMonitor() { {/* Render Stats */} - Top Rendered Components + + Top Rendered Components + {metrics.renders.length > 0 ? ( metrics.renders.map((stat, index) => ( @@ -176,7 +178,10 @@ export function PerformanceMonitor() { {index + 1}. - + {stat.componentName} @@ -196,7 +201,7 @@ export function PerformanceMonitor() { {/* Actions */} { if (typeof global !== 'undefined' && (global as any).performance?.report) { ;(global as any).performance.report() @@ -214,7 +219,7 @@ export function PerformanceMonitor() { console.log('🔄 Performance metrics reset') }} > - Reset Metrics + Reset Metrics @@ -222,7 +227,7 @@ export function PerformanceMonitor() { {/* Toggle button - always visible in dev */} {!isVisible && ( setIsVisible(true)} > 📊 @@ -246,7 +251,7 @@ export function PerformanceToggle() { {isVisible && } {!isVisible && ( setIsVisible(true)} > 📊 diff --git a/components/chat/ChatBubble.tsx b/components/chat/ChatBubble.tsx index fc53801..3a609c9 100644 --- a/components/chat/ChatBubble.tsx +++ b/components/chat/ChatBubble.tsx @@ -35,7 +35,7 @@ function ChatBubbleComponent({ message }: ChatBubbleProps) { {/* Claude avatar for assistant messages */} {!isUser && ( - + C )} @@ -45,7 +45,7 @@ function ChatBubbleComponent({ message }: ChatBubbleProps) { style={[ styles.bubble, { - backgroundColor: isUser ? colors.accent : colors.card, + backgroundColor: isUser ? colors.primary : colors.card, maxWidth: '75%', }, ]} @@ -54,7 +54,7 @@ function ChatBubbleComponent({ message }: ChatBubbleProps) { style={[ styles.content, { - color: isUser ? '#fff' : colors.text, + color: isUser ? '#fff' : colors.textPrimary, }, ]} > diff --git a/components/chat/ChatHeader.tsx b/components/chat/ChatHeader.tsx index 3485f72..1a44852 100644 --- a/components/chat/ChatHeader.tsx +++ b/components/chat/ChatHeader.tsx @@ -27,14 +27,14 @@ export function ChatHeader({ onClose }: ChatHeaderProps) { > - Claude + Claude sonnet-4.5 - + ) diff --git a/components/chat/ChatInput.tsx b/components/chat/ChatInput.tsx index ed322d0..83121a2 100644 --- a/components/chat/ChatInput.tsx +++ b/components/chat/ChatInput.tsx @@ -42,7 +42,7 @@ export function ChatInput({ onSend, disabled = false }: ChatInputProps) { style={[styles.container, { backgroundColor: colors.card, borderTopColor: colors.border }]} > - + {decision.title} @@ -27,7 +27,7 @@ export function DecisionCard({ decision, onStartReview }: DecisionCardProps) { void + disabled?: boolean + style?: StyleProp +} + +export function ControlButton({ + label, + isActive = false, + onPress, + disabled = false, + style, +}: ControlButtonProps) { + return ( + [ + styles.button, + isActive && styles.buttonActive, + disabled && styles.buttonDisabled, + { opacity: pressed ? 0.7 : 1 }, + style, + ]} + onPress={onPress} + disabled={disabled} + accessibilityRole="button" + accessibilityState={{ disabled }} + > + + {label} + + + ) +} + +const styles = StyleSheet.create({ + button: { + backgroundColor: TOKENS.card, + borderRadius: TOKENS.radius12, + paddingVertical: TOKENS.spacing12, + paddingHorizontal: TOKENS.spacing16, + minHeight: 44, // Touch target + justifyContent: 'center', + alignItems: 'center', + }, + buttonActive: { + backgroundColor: TOKENS.primary, + }, + buttonDisabled: { + opacity: 0.5, + }, + label: { + fontSize: TOKENS.labelSize, + fontWeight: TOKENS.labelWeight, + }, +}) diff --git a/components/design-system/EmptyState.tsx b/components/design-system/EmptyState.tsx new file mode 100644 index 0000000..feac787 --- /dev/null +++ b/components/design-system/EmptyState.tsx @@ -0,0 +1,54 @@ +import { View, Text, StyleSheet } from 'react-native' +import { MaterialCommunityIcons } from '@expo/vector-icons' +import { TOKENS } from '@/utils/constants' +import { ControlButton } from './ControlButton' + +interface EmptyStateProps { + icon: string + title: string + description: string + actionLabel?: string + onAction?: () => void +} + +export function EmptyState({ icon, title, description, actionLabel, onAction }: EmptyStateProps) { + return ( + + + + {title} + + {description} + + {actionLabel && onAction && ( + + )} + + ) +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + paddingHorizontal: TOKENS.spacing32, + paddingVertical: TOKENS.spacing32, + }, + title: { + fontSize: 24, + fontWeight: '700', + marginTop: TOKENS.spacing16, + textAlign: 'center', + }, + description: { + fontSize: TOKENS.bodySize, + marginTop: TOKENS.spacing8, + textAlign: 'center', + lineHeight: 24, + }, + action: { + marginTop: TOKENS.spacing24, + minWidth: 200, + }, +}) diff --git a/components/design-system/ProgressBar.tsx b/components/design-system/ProgressBar.tsx new file mode 100644 index 0000000..3636088 --- /dev/null +++ b/components/design-system/ProgressBar.tsx @@ -0,0 +1,43 @@ +import { View, StyleSheet } from 'react-native' +import { TOKENS } from '@/utils/constants' + +interface ProgressBarProps { + progress: number // 0-1 + color?: string +} + +export function ProgressBar({ progress, color = TOKENS.primary }: ProgressBarProps) { + const percentage = Math.max(0, Math.min(1, progress)) * 100 + + return ( + + + + ) +} + +const styles = StyleSheet.create({ + track: { + height: 8, + backgroundColor: TOKENS.card, + borderRadius: TOKENS.radius8, + overflow: 'hidden', + }, + fill: { + height: '100%', + borderRadius: TOKENS.radius8, + shadowRadius: 4, + shadowOpacity: 0.6, + shadowOffset: { width: 0, height: 0 }, + elevation: 2, + }, +}) diff --git a/components/design-system/ScreenLayout.tsx b/components/design-system/ScreenLayout.tsx new file mode 100644 index 0000000..6205494 --- /dev/null +++ b/components/design-system/ScreenLayout.tsx @@ -0,0 +1,51 @@ +import { ReactNode, useState } from 'react' +import { ScrollView, RefreshControl, StyleSheet } from 'react-native' +import { SafeAreaView } from 'react-native-safe-area-context' +import { TOKENS } from '@/utils/constants' + +interface ScreenLayoutProps { + children: ReactNode + onRefresh?: () => Promise +} + +export function ScreenLayout({ children, onRefresh }: ScreenLayoutProps) { + const [refreshing, setRefreshing] = useState(false) + + const handleRefresh = async () => { + if (!onRefresh) return + setRefreshing(true) + await onRefresh() + setRefreshing(false) + } + + return ( + + + ) : undefined + } + > + {children} + + + ) +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: TOKENS.bg, + }, + scrollView: { + flex: 1, + }, + content: { + paddingHorizontal: TOKENS.spacing16, + paddingTop: TOKENS.spacing24, + paddingBottom: 40, + }, +}) diff --git a/components/design-system/SectionHeader.tsx b/components/design-system/SectionHeader.tsx new file mode 100644 index 0000000..6a3107f --- /dev/null +++ b/components/design-system/SectionHeader.tsx @@ -0,0 +1,22 @@ +import { Text, StyleSheet, StyleProp, TextStyle } from 'react-native' +import { TOKENS } from '@/utils/constants' + +interface SectionHeaderProps { + title: string + style?: StyleProp +} + +export function SectionHeader({ title, style }: SectionHeaderProps) { + return {title} +} + +const styles = StyleSheet.create({ + header: { + fontSize: TOKENS.labelSize, + fontWeight: TOKENS.labelWeight, + color: TOKENS.textMuted, + textTransform: 'uppercase', + letterSpacing: 1, + marginBottom: TOKENS.spacing12, + }, +}) diff --git a/components/design-system/StatCard.tsx b/components/design-system/StatCard.tsx new file mode 100644 index 0000000..137626c --- /dev/null +++ b/components/design-system/StatCard.tsx @@ -0,0 +1,60 @@ +import { View, Text, StyleSheet, StyleProp, ViewStyle } from 'react-native' +import { MaterialCommunityIcons } from '@expo/vector-icons' +import { TOKENS } from '@/utils/constants' + +type Variant = 'default' | 'success' | 'warning' | 'danger' + +const VARIANT_COLORS = { + default: TOKENS.primary, + success: TOKENS.success, + warning: TOKENS.warning, + danger: TOKENS.danger, +} + +interface StatCardProps { + label: string + value: string + icon: string + variant?: Variant + style?: StyleProp +} + +export function StatCard({ label, value, icon, variant = 'default', style }: StatCardProps) { + const color = VARIANT_COLORS[variant] + + return ( + + + + {value} + + {label} + + ) +} + +const styles = StyleSheet.create({ + container: { + backgroundColor: TOKENS.card, + borderRadius: TOKENS.radius12, + padding: TOKENS.spacing16, + alignItems: 'center', + justifyContent: 'center', + gap: TOKENS.spacing4, + shadowColor: '#000', + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.3, + shadowRadius: 4, + elevation: 3, + }, + value: { + fontSize: 18, + fontWeight: '700', + marginTop: TOKENS.spacing4, + }, + label: { + fontSize: 11, + textTransform: 'uppercase', + letterSpacing: 0.5, + }, +}) diff --git a/components/design-system/StatusBadge.tsx b/components/design-system/StatusBadge.tsx new file mode 100644 index 0000000..38a4743 --- /dev/null +++ b/components/design-system/StatusBadge.tsx @@ -0,0 +1,96 @@ +import { View, Text, StyleSheet } from 'react-native' +import { TOKENS } from '@/utils/constants' + +type Status = 'running' | 'done' | 'error' | 'paused' | 'awaiting_review' + +const STATUS_COLORS = { + running: TOKENS.success, // #22c55e + done: TOKENS.primary, // #3b82f6 + error: TOKENS.danger, // #ef4444 + paused: TOKENS.warning, // #eab308 + awaiting_review: TOKENS.warning, +} + +interface StatusBadgeProps { + status: Status + label: string + size?: 'small' | 'large' +} + +export function StatusBadge({ status, label, size = 'small' }: StatusBadgeProps) { + const color = STATUS_COLORS[status] + const isLarge = size === 'large' + + return ( + + {/* Colored dot with glow */} + + + + {label} + + + ) +} + +const styles = StyleSheet.create({ + container: { + flexDirection: 'row', + alignItems: 'center', + paddingHorizontal: 12, + paddingVertical: 6, + borderRadius: TOKENS.radius20, + borderWidth: 1, + shadowRadius: 4, + shadowOpacity: 0.5, + shadowOffset: { width: 0, height: 2 }, + elevation: 3, // Android + }, + containerLarge: { + paddingHorizontal: 16, + paddingVertical: 10, + }, + dot: { + width: 8, + height: 8, + borderRadius: 4, + marginRight: 8, + shadowRadius: 4, + shadowOpacity: 0.8, + shadowOffset: { width: 0, height: 0 }, + elevation: 2, + }, + dotLarge: { + width: 12, + height: 12, + borderRadius: 6, + marginRight: 12, + }, + label: { + fontSize: TOKENS.labelSize, + fontWeight: TOKENS.labelWeight, + }, + labelLarge: { + fontSize: TOKENS.bodySize, + fontWeight: TOKENS.headerWeight, + }, +}) diff --git a/components/inbox/DecisionQueueCard.tsx b/components/inbox/DecisionQueueCard.tsx index 41d7578..5525766 100644 --- a/components/inbox/DecisionQueueCard.tsx +++ b/components/inbox/DecisionQueueCard.tsx @@ -24,15 +24,15 @@ export function DecisionQueueCard({ count, totalMinutes }: DecisionQueueCardProp accessibilityHint="Opens decision queue" > - Decision Queue - + Decision Queue + {count} ~{totalMinutes} min to review - + Start Reviews → diff --git a/components/inbox/ForecastCard.tsx b/components/inbox/ForecastCard.tsx index 64a7d3e..c01150d 100644 --- a/components/inbox/ForecastCard.tsx +++ b/components/inbox/ForecastCard.tsx @@ -16,25 +16,25 @@ export function ForecastCard({ forecast }: ForecastCardProps) { return ( - Forecast + Forecast Deep Work Window - + {formatTime(forecast.deepWorkWindow.start)} - {formatTime(forecast.deepWorkWindow.end)} Next Review Batch - + {formatTime(forecast.nextReviewBatch)} Agent Hours in Progress - + {forecast.agentHoursInProgress} hours diff --git a/components/inbox/InboxHeader.tsx b/components/inbox/InboxHeader.tsx index 9274364..bd09c58 100644 --- a/components/inbox/InboxHeader.tsx +++ b/components/inbox/InboxHeader.tsx @@ -20,7 +20,7 @@ export function InboxHeader({ userName, summary }: InboxHeaderProps) { return ( - + {getGreeting()}, {userName} @@ -37,8 +37,8 @@ export function InboxHeader({ userName, summary }: InboxHeaderProps) { Stuck - - + + {summary.pendingDecisions} Decisions diff --git a/components/inbox/OvernightResultsCard.tsx b/components/inbox/OvernightResultsCard.tsx index 8936d7e..1d2bc3d 100644 --- a/components/inbox/OvernightResultsCard.tsx +++ b/components/inbox/OvernightResultsCard.tsx @@ -14,12 +14,12 @@ export function OvernightResultsCard({ results }: OvernightResultsCardProps) { return ( - Overnight Results + Overnight Results {displayResults.map((result, index) => ( - + {result.task} - {agent.name} needs help + {agent.name} needs help {agent.task} diff --git a/components/layout/CreateFAB.tsx b/components/layout/CreateFAB.tsx index 252c44c..83f79b7 100644 --- a/components/layout/CreateFAB.tsx +++ b/components/layout/CreateFAB.tsx @@ -49,7 +49,7 @@ export function CreateFAB() { <> {/* FAB Button */} setModalVisible(true)} activeOpacity={0.8} accessibilityRole="button" @@ -71,7 +71,7 @@ export function CreateFAB() { {/* Header */} - Create New + Create New setModalVisible(false)} style={styles.closeButton} @@ -98,7 +98,7 @@ export function CreateFAB() { activeOpacity={0.7} disabled={option.soon} > - + diff --git a/components/layout/FAB.tsx b/components/layout/FAB.tsx index 6ce76dd..a81c976 100644 --- a/components/layout/FAB.tsx +++ b/components/layout/FAB.tsx @@ -1,5 +1,6 @@ import { TouchableOpacity, StyleSheet } from 'react-native' import { Ionicons } from '@expo/vector-icons' +import { TOKENS } from '@/utils/constants' import { router } from 'expo-router' export function FAB() { @@ -22,7 +23,7 @@ const styles = StyleSheet.create({ width: 56, height: 56, borderRadius: 28, - backgroundColor: '#6366f1', // Purple accent + backgroundColor: TOKENS.primary, justifyContent: 'center', alignItems: 'center', elevation: 4, // Android shadow diff --git a/components/layout/Header.tsx b/components/layout/Header.tsx index 68b45d7..dc67e12 100644 --- a/components/layout/Header.tsx +++ b/components/layout/Header.tsx @@ -138,7 +138,7 @@ export const Header = memo(({ isRefetching = false }: HeaderProps) => { return ( - + {greeting}, {firstName} @@ -149,10 +149,10 @@ export const Header = memo(({ isRefetching = false }: HeaderProps) => { onPress={() => router.push('/announcements')} activeOpacity={0.7} > - + {/* Unread badge */} {unreadAnnouncementsCount > 0 && ( - + {unreadAnnouncementsCount} )} @@ -164,9 +164,9 @@ export const Header = memo(({ isRefetching = false }: HeaderProps) => { onPress={openNotifications} activeOpacity={0.7} > - + {/* Unread badge */} - + 2 @@ -174,7 +174,7 @@ export const Header = memo(({ isRefetching = false }: HeaderProps) => { {/* Avatar with refresh spinner */} @@ -182,7 +182,7 @@ export const Header = memo(({ isRefetching = false }: HeaderProps) => { {isRefetching && ( - + )} diff --git a/components/modals/NotificationsModal.tsx b/components/modals/NotificationsModal.tsx index 04d6fb1..e29ecb4 100644 --- a/components/modals/NotificationsModal.tsx +++ b/components/modals/NotificationsModal.tsx @@ -101,12 +101,12 @@ export function NotificationsModal({ visible, onClose }: NotificationsModalProps const renderEmptyState = (type: 'notifications' | 'announcements') => ( - - + + "{randomQuote.quote}" - {randomQuote.author} + {randomQuote.author} {randomQuote.context} @@ -137,34 +137,34 @@ export function NotificationsModal({ visible, onClose }: NotificationsModalProps style={[ styles.notificationCard, { - backgroundColor: isUnread ? colors.accent + '10' : colors.card, - borderLeftColor: isUnread ? colors.accent : colors.border, + backgroundColor: isUnread ? colors.primary + '10' : colors.card, + borderLeftColor: isUnread ? colors.primary : colors.border, }, ]} activeOpacity={0.7} > - - + + {notification.title} - {isUnread && } + {isUnread && } {notification.message} {notification.author && ( - + {notification.author} )} @@ -188,9 +188,9 @@ export function NotificationsModal({ visible, onClose }: NotificationsModalProps {/* Header */} - Updates + Updates - Done + Done @@ -200,19 +200,19 @@ export function NotificationsModal({ visible, onClose }: NotificationsModalProps style={[ styles.tab, activeTab === 'notifications' && { - borderBottomColor: colors.accent, + borderBottomColor: colors.primary, borderBottomWidth: 2, }, ]} onPress={() => setActiveTab('notifications')} activeOpacity={0.7} > - + @@ -224,19 +224,19 @@ export function NotificationsModal({ visible, onClose }: NotificationsModalProps style={[ styles.tab, activeTab === 'announcements' && { - borderBottomColor: colors.accent, + borderBottomColor: colors.primary, borderBottomWidth: 2, }, ]} onPress={() => setActiveTab('announcements')} activeOpacity={0.7} > - + diff --git a/components/notifications/NotificationCard.tsx b/components/notifications/NotificationCard.tsx index 012ba8f..25cecf2 100644 --- a/components/notifications/NotificationCard.tsx +++ b/components/notifications/NotificationCard.tsx @@ -63,7 +63,7 @@ function NotificationCardComponent({ notification, onPress }: NotificationCardPr styles.container, { backgroundColor: colors.card, - borderLeftColor: notification.isUnread ? colors.accent : 'transparent', + borderLeftColor: notification.isUnread ? colors.primary : 'transparent', }, ]} onPress={() => onPress(notification)} @@ -74,7 +74,7 @@ function NotificationCardComponent({ notification, onPress }: NotificationCardPr > {/* Unread indicator dot */} {notification.isUnread && ( - + )} {/* Icon */} @@ -82,7 +82,7 @@ function NotificationCardComponent({ notification, onPress }: NotificationCardPr @@ -98,7 +98,7 @@ function NotificationCardComponent({ notification, onPress }: NotificationCardPr style={[ styles.title, { - color: colors.text, + color: colors.textPrimary, fontWeight: notification.isUnread ? '600' : '400', }, ]} diff --git a/components/session/ApprovalActions.tsx b/components/session/ApprovalActions.tsx index 8dc9474..2ae245c 100644 --- a/components/session/ApprovalActions.tsx +++ b/components/session/ApprovalActions.tsx @@ -114,7 +114,7 @@ export function ApprovalActions({ sessionId, sessionName, onSuccess }: ApprovalA style={[ styles.button, styles.rejectButton, - { backgroundColor: colors.error }, + { backgroundColor: colors.danger }, isButtonDisabled && styles.buttonDisabled, ]} onPress={() => setRejectModalVisible(true)} @@ -157,7 +157,7 @@ export function ApprovalActions({ sessionId, sessionName, onSuccess }: ApprovalA > - Approve Session? + Approve Session? This will mark “{sessionName}” as completed and merge the changes. @@ -168,7 +168,7 @@ export function ApprovalActions({ sessionId, sessionName, onSuccess }: ApprovalA onPress={() => setApproveModalVisible(false)} activeOpacity={0.7} > - Cancel + Cancel - Reject Session? + Reject Session? Provide feedback for Claude to make changes. Feedback is optional. @@ -204,7 +204,11 @@ export function ApprovalActions({ sessionId, sessionName, onSuccess }: ApprovalA - Cancel + Cancel diff --git a/components/session/ModelBadge.tsx b/components/session/ModelBadge.tsx index 656dc1e..12cbfa1 100644 --- a/components/session/ModelBadge.tsx +++ b/components/session/ModelBadge.tsx @@ -15,8 +15,8 @@ export function ModelBadge({ model, size = 'small' }: ModelBadgeProps) { const description = model === ModelType.SONNET_4_5 ? 'Fast & efficient' : 'Most capable' return ( - - {label} + + {label} {size === 'medium' && ( {description} )} diff --git a/components/session/SessionCard.tsx b/components/session/SessionCard.tsx index d860652..756c2c3 100644 --- a/components/session/SessionCard.tsx +++ b/components/session/SessionCard.tsx @@ -1,5 +1,5 @@ -import React, { useEffect, useRef, useCallback, memo } from 'react' -import { View, TouchableOpacity, StyleSheet, Animated } from 'react-native' +import React, { useCallback, memo } from 'react' +import { View, Text, TouchableOpacity, StyleSheet } from 'react-native' import type { Session } from '@/types/session' import { SessionProgress } from './SessionProgress' import { ModelBadge } from './ModelBadge' @@ -14,78 +14,11 @@ interface SessionCardProps { function SessionCardComponent({ session }: SessionCardProps) { const { colors } = useTheme() const router = useRouter() - const progressFlashAnim = useRef(new Animated.Value(0)).current - const statusFlashAnim = useRef(new Animated.Value(0)).current - const prevProgressRef = useRef(session.progress) - const prevStatusRef = useRef(session.status) - - // Detect progress changes and trigger flash animation - useEffect(() => { - if (prevProgressRef.current !== session.progress) { - // Progress changed - trigger flash animation - - // Flash animation: 0 -> 1 (inverted) -> 0 (normal) over 3 seconds - Animated.sequence([ - Animated.timing(progressFlashAnim, { - toValue: 1, - duration: 150, - useNativeDriver: false, - }), - Animated.timing(progressFlashAnim, { - toValue: 0, - duration: 2850, - useNativeDriver: false, - }), - ]).start() - - prevProgressRef.current = session.progress - } - }, [session.progress, progressFlashAnim, session.id]) - - // Detect status changes and trigger flash animation - useEffect(() => { - if (prevStatusRef.current !== session.status) { - // Status changed - trigger flash animation - - // Flash animation: 0 -> 1 (inverted) -> 0 (normal) over 3 seconds - Animated.sequence([ - Animated.timing(statusFlashAnim, { - toValue: 1, - duration: 150, - useNativeDriver: false, - }), - Animated.timing(statusFlashAnim, { - toValue: 0, - duration: 2850, - useNativeDriver: false, - }), - ]).start() - - prevStatusRef.current = session.status - } - }, [session.status, statusFlashAnim, session.id]) const handlePress = useCallback(() => { router.push(`/sessions/${session.id}`) }, [router, session.id]) - // Progress text color animation - const progressTextColor = progressFlashAnim.interpolate({ - inputRange: [0, 1], - outputRange: [colors.textSecondary, colors.card], - }) - - const progressBgColor = progressFlashAnim.interpolate({ - inputRange: [0, 1], - outputRange: ['transparent', colors.text], - }) - - // Status badge flash animation (will wrap StatusBadge) - const statusBgColor = statusFlashAnim.interpolate({ - inputRange: [0, 1], - outputRange: ['transparent', colors.text], - }) - return ( - + {session.name} - + - + - - - - {session.progress}% - - + + + {session.progress}% + {session.currentTask && ( - + {session.currentTask} - + )} ) @@ -174,8 +83,15 @@ const styles = StyleSheet.create({ justifyContent: 'space-between', alignItems: 'center', }, - progressContainer: { - // Container for progress percentage with flash effect + statusBadgeWrapper: { + borderRadius: 4, + paddingHorizontal: 4, + paddingVertical: 2, + }, + progressWrapper: { + borderRadius: 4, + paddingHorizontal: 6, + paddingVertical: 2, }, progress: { fontSize: 14, diff --git a/components/session/SessionProgress.tsx b/components/session/SessionProgress.tsx index 8cd1323..b3387dd 100644 --- a/components/session/SessionProgress.tsx +++ b/components/session/SessionProgress.tsx @@ -18,7 +18,7 @@ export function SessionProgress({ progress }: SessionProgressProps) { styles.fill, { width: `${clampedProgress}%`, - backgroundColor: colors.accent, + backgroundColor: colors.primary, }, ]} /> diff --git a/components/settings/ProfileCard.tsx b/components/settings/ProfileCard.tsx index 1f7a86b..745d772 100644 --- a/components/settings/ProfileCard.tsx +++ b/components/settings/ProfileCard.tsx @@ -1,6 +1,7 @@ import React from 'react' import { View, Text, Image, StyleSheet } from 'react-native' import type { User } from '../../types/user' +import { TOKENS } from '../../utils/constants' interface ProfileCardProps { user: User @@ -38,7 +39,7 @@ const styles = StyleSheet.create({ container: { alignItems: 'center', padding: 24, - backgroundColor: '#fff', + backgroundColor: TOKENS.card, borderRadius: 12, marginBottom: 16, }, @@ -51,36 +52,36 @@ const styles = StyleSheet.create({ name: { fontSize: 22, fontWeight: '700', - color: '#1a1a1a', + color: TOKENS.textPrimary, marginBottom: 8, }, badge: { paddingHorizontal: 12, paddingVertical: 4, borderRadius: 12, - backgroundColor: '#f3f4f6', + backgroundColor: TOKENS.elevated, marginBottom: 4, }, badgeText: { fontSize: 12, fontWeight: '600', - color: '#6b7280', + color: TOKENS.textSecondary, }, ssoBadge: { flexDirection: 'row', alignItems: 'center', - backgroundColor: '#d1fae5', + backgroundColor: TOKENS.success + '20', }, ssoIndicator: { width: 6, height: 6, borderRadius: 3, - backgroundColor: '#10b981', + backgroundColor: TOKENS.success, marginRight: 6, }, ssoBadgeText: { fontSize: 12, fontWeight: '600', - color: '#059669', + color: TOKENS.success, }, }) diff --git a/components/ui/ApiStatusIndicator.tsx b/components/ui/ApiStatusIndicator.tsx index ed66326..69de3cc 100644 --- a/components/ui/ApiStatusIndicator.tsx +++ b/components/ui/ApiStatusIndicator.tsx @@ -135,7 +135,7 @@ export const ApiStatusIndicator = memo( )} {error && size === 'large' && ( - + {error} )} diff --git a/components/ui/Badge.tsx b/components/ui/Badge.tsx index daff6ac..f7dc137 100644 --- a/components/ui/Badge.tsx +++ b/components/ui/Badge.tsx @@ -1,6 +1,7 @@ import React from 'react' import { View, Text, StyleSheet } from 'react-native' import { SessionStatus } from '@/types/session' +import { TOKENS } from '@/utils/constants' import { useTheme } from '@/hooks/useTheme' interface StatusBadgeProps { @@ -13,7 +14,7 @@ export function StatusBadge({ status }: StatusBadgeProps) { const getStatusColor = () => { switch (status) { case SessionStatus.RUNNING: - return colors.accent + return colors.primary case SessionStatus.PAUSED: return colors.warning case SessionStatus.DONE: @@ -21,7 +22,7 @@ export function StatusBadge({ status }: StatusBadgeProps) { case SessionStatus.AWAITING_REVIEW: return colors.warning case SessionStatus.ERROR: - return colors.error + return colors.danger default: return colors.textSecondary } @@ -60,7 +61,7 @@ const styles = StyleSheet.create({ alignItems: 'center', paddingHorizontal: 8, paddingVertical: 4, - borderRadius: 12, + borderRadius: TOKENS.radius20, gap: 4, }, dot: { diff --git a/components/ui/ErrorMessage.tsx b/components/ui/ErrorMessage.tsx index 7915fbc..f8a2973 100644 --- a/components/ui/ErrorMessage.tsx +++ b/components/ui/ErrorMessage.tsx @@ -1,6 +1,7 @@ import React from 'react' import { View, Text, TouchableOpacity, StyleSheet } from 'react-native' import { IconSymbol } from './icon-symbol' +import { TOKENS } from '@/utils/constants' import { useTheme } from '@/hooks/useTheme' interface ErrorMessageProps { @@ -19,22 +20,22 @@ export function ErrorMessage({ error, retry, showDetails = false }: ErrorMessage const { colors } = useTheme() return ( - - + + - Something went wrong + Something went wrong - + {error.message || 'An unexpected error occurred'} {showDetails && error.stack && ( - {error.stack} + {error.stack} )} {retry && ( @@ -80,7 +81,7 @@ const styles = StyleSheet.create({ gap: 8, paddingHorizontal: 24, paddingVertical: 12, - borderRadius: 8, + borderRadius: TOKENS.radius12, marginTop: 8, }, retryText: { diff --git a/components/ui/OfflineBanner.tsx b/components/ui/OfflineBanner.tsx index 2d8d68b..5070c83 100644 --- a/components/ui/OfflineBanner.tsx +++ b/components/ui/OfflineBanner.tsx @@ -1,6 +1,7 @@ import React from 'react' import { View, Text, StyleSheet } from 'react-native' import { IconSymbol } from './icon-symbol' +import { TOKENS } from '@/utils/constants' import { useTheme } from '@/hooks/useTheme' /** @@ -13,9 +14,9 @@ export function OfflineBanner() { const { colors } = useTheme() return ( - - - Offline - Using cached data + + + Offline - Using cached data ) } @@ -29,7 +30,6 @@ const styles = StyleSheet.create({ gap: 8, }, text: { - color: '#fff', fontSize: 13, fontWeight: '600', }, diff --git a/components/ui/SettingsRow.tsx b/components/ui/SettingsRow.tsx index 1a7ffeb..3d1d1ac 100644 --- a/components/ui/SettingsRow.tsx +++ b/components/ui/SettingsRow.tsx @@ -1,6 +1,7 @@ import React from 'react' import { TouchableOpacity, View, Text, StyleSheet } from 'react-native' import { Ionicons } from '@expo/vector-icons' +import { TOKENS } from '@/utils/constants' interface SettingsRowProps { label: string @@ -23,7 +24,7 @@ export function SettingsRow({ label, icon, badge, onPress, disabled = false }: S )} @@ -39,7 +40,11 @@ export function SettingsRow({ label, icon, badge, onPress, disabled = false }: S )} {/* Chevron */} - + ) } @@ -49,9 +54,9 @@ const styles = StyleSheet.create({ flexDirection: 'row', alignItems: 'center', padding: 16, - backgroundColor: '#fff', + backgroundColor: TOKENS.card, borderBottomWidth: 1, - borderBottomColor: '#f3f4f6', + borderBottomColor: TOKENS.border, }, disabled: { opacity: 0.5, @@ -63,21 +68,21 @@ const styles = StyleSheet.create({ flex: 1, fontSize: 16, fontWeight: '500', - color: '#1a1a1a', + color: TOKENS.textPrimary, }, disabledText: { - color: '#9ca3af', + color: TOKENS.textDisabled, }, badge: { paddingHorizontal: 8, paddingVertical: 2, borderRadius: 8, - backgroundColor: '#f3f4f6', + backgroundColor: TOKENS.elevated, marginRight: 8, }, badgeText: { fontSize: 11, fontWeight: '600', - color: '#6b7280', + color: TOKENS.textSecondary, }, }) diff --git a/components/ui/Toast.tsx b/components/ui/Toast.tsx index 75e6a2e..9842963 100644 --- a/components/ui/Toast.tsx +++ b/components/ui/Toast.tsx @@ -1,5 +1,6 @@ import React, { useEffect, useRef, useCallback } from 'react' import { View, Text, TouchableOpacity, StyleSheet, Animated } from 'react-native' +import { TOKENS } from '@/utils/constants' import { useTheme } from '@/hooks/useTheme' import { IconSymbol } from '@/components/ui/icon-symbol' import { useRouter } from 'expo-router' @@ -92,15 +93,15 @@ export function Toast({ notification, onDismiss }: ToastProps) { const getBackgroundColor = () => { switch (notification.type) { case 'review': - return colors.warning + 'E6' + return TOKENS.warning + 'E6' case 'session': - return colors.accent + 'E6' + return TOKENS.primary + 'E6' case 'success': - return colors.success + 'E6' + return TOKENS.success + 'E6' case 'error': - return colors.error + 'E6' + return TOKENS.danger + 'E6' default: - return colors.card + 'E6' + return TOKENS.elevated + 'E6' } } diff --git a/components/ui/Toggle.tsx b/components/ui/Toggle.tsx index cb23407..169ff9d 100644 --- a/components/ui/Toggle.tsx +++ b/components/ui/Toggle.tsx @@ -1,5 +1,6 @@ import React from 'react' import { View, Text, Switch, StyleSheet } from 'react-native' +import { TOKENS } from '@/utils/constants' interface ToggleProps { label: string @@ -27,9 +28,9 @@ export function Toggle({ value={value} onValueChange={onValueChange} disabled={disabled} - trackColor={{ false: '#d1d5db', true: '#a78bfa' }} - thumbColor={value ? '#8b5cf6' : '#f3f4f6'} - ios_backgroundColor="#d1d5db" + trackColor={{ false: TOKENS.border, true: TOKENS.primary }} + thumbColor={value ? TOKENS.primary : TOKENS.card} + ios_backgroundColor={TOKENS.border} /> ) @@ -40,9 +41,9 @@ const styles = StyleSheet.create({ flexDirection: 'row', alignItems: 'center', padding: 16, - backgroundColor: '#fff', + backgroundColor: TOKENS.card, borderBottomWidth: 1, - borderBottomColor: '#f3f4f6', + borderBottomColor: TOKENS.border, }, textContainer: { flex: 1, @@ -51,11 +52,11 @@ const styles = StyleSheet.create({ label: { fontSize: 16, fontWeight: '500', - color: '#1a1a1a', + color: TOKENS.textPrimary, marginBottom: 2, }, description: { fontSize: 13, - color: '#6b7280', + color: TOKENS.textSecondary, }, }) diff --git a/hooks/useTheme.tsx b/hooks/useTheme.tsx index 8d30e92..ab936bc 100644 --- a/hooks/useTheme.tsx +++ b/hooks/useTheme.tsx @@ -8,7 +8,7 @@ import React, { ReactNode, } from 'react' import { useColorScheme } from 'react-native' -import { COLORS } from '@/utils/constants' +import { COLORS, TOKENS } from '@/utils/constants' import { PreferencesService } from '@/services/storage/preferences' type ThemeMode = 'light' | 'dark' | 'system' @@ -16,7 +16,7 @@ type ThemeMode = 'light' | 'dark' | 'system' interface ThemeContextType { theme: 'light' | 'dark' themeMode: ThemeMode - colors: typeof COLORS.light + colors: typeof TOKENS setThemeMode: (mode: ThemeMode) => void } @@ -46,13 +46,15 @@ export function ThemeProvider({ children }: { children: ReactNode }) { [themeMode, systemColorScheme] ) - // Memoize colors object to prevent recreation - const colors = useMemo(() => COLORS[theme], [theme]) + // Force dark-first design: always return TOKENS + // Keep legacy colors for backward compatibility during migration + const colors = useMemo(() => TOKENS, []) // Memoize entire context value to prevent unnecessary re-renders + // Force theme to dark for dark-first design const contextValue = useMemo( - () => ({ theme, themeMode, colors, setThemeMode }), - [theme, themeMode, colors, setThemeMode] + () => ({ theme: 'dark' as const, themeMode, colors, setThemeMode }), + [themeMode, colors, setThemeMode] ) return {children} diff --git a/package-lock.json b/package-lock.json index e9936f9..edff26e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -119,7 +119,6 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -1501,7 +1500,6 @@ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=6.9.0" } @@ -3398,7 +3396,6 @@ "resolved": "https://registry.npmjs.org/@react-native-async-storage/async-storage/-/async-storage-2.2.0.tgz", "integrity": "sha512-gvRvjR5JAaUZF8tv2Kcq/Gbt3JHwbKFYfmb445rhOj6NUMx3qPLixmDx5pZAyb9at1bYvJ4/eTUipU5aki45xw==", "license": "MIT", - "peer": true, "dependencies": { "merge-options": "^3.0.4" }, @@ -3755,7 +3752,6 @@ "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-7.1.22.tgz", "integrity": "sha512-WuaS4iVFfuHIR6wIYcBA/ZF9/++bbtr0cEO7ohinc3PE+7PZuVJr7KgdrAFay3OI6GmqW0cmuUKZ0BPPDwQ7dw==", "license": "MIT", - "peer": true, "dependencies": { "@react-navigation/core": "^7.13.3", "escape-string-regexp": "^4.0.0", @@ -4254,7 +4250,6 @@ "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.11.tgz", "integrity": "sha512-3uyzz01D1fkTLXuxF3JfoJoHQMU2fxsfJwE+6N5hHy0dVNoZOvwKP8Z2k7k1KDeD54N20apcJnG75TBAStIrBA==", "license": "MIT", - "peer": true, "dependencies": { "@tanstack/query-core": "5.90.11" }, @@ -4290,7 +4285,6 @@ "integrity": "sha512-wIn/lB1FjV2N4Q7i9PWVRck3Ehwq5pkhAef5X5/bmQ78J/NoOsGbVY2/DG5Y9Lxw+RfE+GvSEh/fe5Tz6sKSvw==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "jest-matcher-utils": "^29.7.0", "pretty-format": "^29.7.0", @@ -4478,7 +4472,6 @@ "integrity": "sha512-Qec1E3mhALmaspIrhWt9jkQMNdw6bReVu64mjvhbhq2NFPftLPVr+l1SZgmw/66WwBNpDh7ao5AT6gF5v41PFA==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.0.2" } @@ -4564,7 +4557,6 @@ "integrity": "sha512-jCzKdm/QK0Kg4V4IK/oMlRZlY+QOcdjv89U2NgKHZk1CYTj82/RVSx1mV/0gqCVMJ/DA+Zf/S4NBWNF8GQ+eqQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.48.0", "@typescript-eslint/types": "8.48.0", @@ -5134,7 +5126,6 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -5521,7 +5512,6 @@ "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", "license": "MIT", - "peer": true, "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.4", @@ -5908,7 +5898,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", @@ -7363,7 +7352,6 @@ "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -7560,7 +7548,6 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -7863,7 +7850,6 @@ "resolved": "https://registry.npmjs.org/expo/-/expo-54.0.27.tgz", "integrity": "sha512-50BcJs8eqGwRiMUoWwphkRGYtKFS2bBnemxLzy0lrGVA1E6F4Q7L5h3WT6w1ehEZybtOVkfJu4Z6GWo2IJcpEA==", "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.20.0", "@expo/cli": "54.0.18", @@ -7958,7 +7944,6 @@ "resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-18.0.11.tgz", "integrity": "sha512-xnfrfZ7lHjb+03skhmDSYeFF7OU2K3Xn/lAeP+7RhkV2xp2f5RCKtOUYajCnYeZesvMrsUxOsbGOP2JXSOH3NA==", "license": "MIT", - "peer": true, "dependencies": { "@expo/config": "~12.0.11", "@expo/env": "~2.0.8" @@ -7985,7 +7970,6 @@ "resolved": "https://registry.npmjs.org/expo-file-system/-/expo-file-system-19.0.20.tgz", "integrity": "sha512-Jr/nNvJmUlptS3cHLKVBNyTyGMHNyxYBKRph1KRe0Nb3RzZza1gZLZXMG5Ky//sO2azTn+OaT0dv/lAyL0vJNA==", "license": "MIT", - "peer": true, "peerDependencies": { "expo": "*", "react-native": "*" @@ -7996,7 +7980,6 @@ "resolved": "https://registry.npmjs.org/expo-font/-/expo-font-14.0.10.tgz", "integrity": "sha512-UqyNaaLKRpj4pKAP4HZSLnuDQqueaO5tB1c/NWu5vh1/LF9ulItyyg2kF/IpeOp0DeOLk0GY0HrIXaKUMrwB+Q==", "license": "MIT", - "peer": true, "dependencies": { "fontfaceobserver": "^2.1.0" }, @@ -8047,7 +8030,6 @@ "resolved": "https://registry.npmjs.org/expo-linear-gradient/-/expo-linear-gradient-15.0.8.tgz", "integrity": "sha512-V2d8Wjn0VzhPHO+rrSBtcl+Fo+jUUccdlmQ6OoL9/XQB7Qk3d9lYrqKDJyccwDxmQT10JdST3Tmf2K52NLc3kw==", "license": "MIT", - "peer": true, "peerDependencies": { "expo": "*", "react": "*", @@ -8059,7 +8041,6 @@ "resolved": "https://registry.npmjs.org/expo-linking/-/expo-linking-8.0.10.tgz", "integrity": "sha512-0EKtn4Sk6OYmb/5ZqK8riO0k1Ic+wyT3xExbmDvUYhT7p/cKqlVUExMuOIAt3Cx3KUUU1WCgGmdd493W/D5XjA==", "license": "MIT", - "peer": true, "dependencies": { "expo-constants": "~18.0.11", "invariant": "^2.2.4" @@ -10155,7 +10136,6 @@ "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", @@ -13784,7 +13764,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -13825,7 +13804,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.26.0" }, @@ -13862,7 +13840,6 @@ "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.81.5.tgz", "integrity": "sha512-1w+/oSjEXZjMqsIvmkCRsOc8UBYv163bTWKTI8+1mxztvQPhCRYGTvZ/PL1w16xXHneIj/SLGfxWg2GWN2uexw==", "license": "MIT", - "peer": true, "dependencies": { "@jest/create-cache-key-function": "^29.7.0", "@react-native/assets-registry": "0.81.5", @@ -13956,7 +13933,6 @@ "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.28.0.tgz", "integrity": "sha512-0msfJ1vRxXKVgTgvL+1ZOoYw3/0z1R+Ked0+udoJhyplC2jbVKIJ8Z1bzWdpQRCV3QcQ87Op0zJVE5DhKK2A0A==", "license": "MIT", - "peer": true, "dependencies": { "@egjs/hammerjs": "^2.0.17", "hoist-non-react-statics": "^3.3.0", @@ -14046,7 +14022,6 @@ "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-5.6.2.tgz", "integrity": "sha512-4XGqMNj5qjUTYywJqpdWZ9IG8jgkS3h06sfVjfw5yZQZfWnRFXczi0GnYyFyCc2EBps/qFmoCH8fez//WumdVg==", "license": "MIT", - "peer": true, "peerDependencies": { "react": "*", "react-native": "*" @@ -14057,7 +14032,6 @@ "resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-4.16.0.tgz", "integrity": "sha512-yIAyh7F/9uWkOzCi1/2FqvNvK6Wb9Y1+Kzn16SuGfN9YFJDTbwlzGRvePCNTOX0recpLQF3kc2FmvMUhyTCH1Q==", "license": "MIT", - "peer": true, "dependencies": { "react-freeze": "^1.0.0", "react-native-is-edge-to-edge": "^1.2.1", @@ -14079,7 +14053,6 @@ "resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-15.12.1.tgz", "integrity": "sha512-vCuZJDf8a5aNC2dlMovEv4Z0jjEUET53lm/iILFnFewa15b4atjVxU6Wirm6O9y6dEsdjDZVD7Q3QM4T1wlI8g==", "license": "MIT", - "peer": true, "dependencies": { "css-select": "^5.1.0", "css-tree": "^1.1.3", @@ -14095,7 +14068,6 @@ "resolved": "https://registry.npmjs.org/react-native-web/-/react-native-web-0.21.2.tgz", "integrity": "sha512-SO2t9/17zM4iEnFvlu2DA9jqNbzNhoUP+AItkoCOyFmDMOhUnBBznBDCYN92fGdfAkfQlWzPoez6+zLxFNsZEg==", "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.18.6", "@react-native/normalize-colors": "^0.74.1", @@ -14215,7 +14187,6 @@ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -14295,7 +14266,6 @@ "integrity": "sha512-jXkSl3CpvPYEF+p/eGDLB4sPoDX8pKkYvRl9+rR8HxLY0X04vW7hCm1/0zHoUSjPZ3bDa+wXWNTDVIw/R8aDVw==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "react-is": "^19.1.0", "scheduler": "^0.26.0" @@ -16364,7 +16334,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/utils/constants.ts b/utils/constants.ts index 62b281f..ba29262 100644 --- a/utils/constants.ts +++ b/utils/constants.ts @@ -44,7 +44,7 @@ export const CACHE_TTL = { USER_PROFILE: 60 * 60 * 1000, // 1 hour } -// Theme Colors +// Theme Colors (Legacy - for backward compatibility during migration) export const COLORS = { light: { bg: '#f1f5f9', @@ -70,6 +70,56 @@ export const COLORS = { }, } +// New Design Tokens (Dark-First Design System) +export const TOKENS = { + // Backgrounds + bg: '#0a0a0a', + card: '#1a1a1a', + elevated: '#222', + + // Semantic colors + primary: '#3b82f6', + success: '#22c55e', + warning: '#eab308', + danger: '#ef4444', + + // Text (flattened, not nested) + textPrimary: '#fff', + textSecondary: '#ccc', + textMuted: '#888', + textDisabled: '#666', + + // UI elements + border: '#333', + + // Spacing (4px base) + spacing4: 4, + spacing8: 8, + spacing12: 12, + spacing16: 16, + spacing24: 24, + spacing32: 32, + + // Border radius + radius8: 8, + radius10: 10, + radius12: 12, + radius14: 14, + radius20: 20, + + // Typography + titleSize: 28, + titleWeight: '700' as const, + headerSize: 20, + headerWeight: '700' as const, + bodySize: 16, + bodyWeight: '400' as const, + labelSize: 14, + labelWeight: '600' as const, + hintSize: 12, + hintWeight: '400' as const, +} + // Workflow Types export const WORKFLOWS = [ { diff --git a/utils/mockData.ts b/utils/mockData.ts index 963d746..b24392f 100644 --- a/utils/mockData.ts +++ b/utils/mockData.ts @@ -10,10 +10,38 @@ import { GitHubNotification, NotificationType } from '@/types/notification' import { NOTIFICATION_WORKFLOW_MAP } from '@/utils/constants' import { logger } from '@/utils/logger' +/** + * Format a date as "MMM DD" (e.g., "Jan 11") + */ +function formatDateForSessionName(date: Date): string { + const months = [ + 'Jan', + 'Feb', + 'Mar', + 'Apr', + 'May', + 'Jun', + 'Jul', + 'Aug', + 'Sep', + 'Oct', + 'Nov', + 'Dec', + ] + return `${months[date.getMonth()]} ${date.getDate()}` +} + +// Generate current dates for session names +const now = new Date() +const today = formatDateForSessionName(now) +const yesterday = formatDateForSessionName(new Date(Date.now() - 86400000)) +const twoDaysAgo = formatDateForSessionName(new Date(Date.now() - 172800000)) +const threeDaysAgo = formatDateForSessionName(new Date(Date.now() - 259200000)) + export const MOCK_SESSIONS: Session[] = [ { id: '1', - name: 'platform Review - Nov 26', + name: `platform Review - ${today}`, status: SessionStatus.RUNNING, progress: 67, model: ModelType.SONNET_4_5, @@ -33,7 +61,7 @@ export const MOCK_SESSIONS: Session[] = [ }, { id: '2', - name: 'acp-mobile Bugfix - Nov 26', + name: `acp-mobile Bugfix - ${today}`, status: SessionStatus.AWAITING_REVIEW, progress: 100, model: ModelType.SONNET_4_5, @@ -59,7 +87,7 @@ export const MOCK_SESSIONS: Session[] = [ }, { id: '3', - name: 'backend-api Research - Nov 25', + name: `backend-api Research - ${yesterday}`, status: SessionStatus.RUNNING, progress: 45, model: ModelType.OPUS_4_5, @@ -79,7 +107,7 @@ export const MOCK_SESSIONS: Session[] = [ }, { id: '4', - name: 'feature-planner Plan - Nov 25', + name: `feature-planner Plan - ${yesterday}`, status: SessionStatus.ERROR, progress: 23, model: ModelType.SONNET_4_5, @@ -99,7 +127,7 @@ export const MOCK_SESSIONS: Session[] = [ }, { id: '5', - name: 'docs-site Plan a Feature - Nov 24', + name: `docs-site Plan a Feature - ${threeDaysAgo}`, status: SessionStatus.DONE, progress: 100, model: ModelType.SONNET_4_5, diff --git a/utils/mockInboxData.ts b/utils/mockInboxData.ts index be1b809..0f253f7 100644 --- a/utils/mockInboxData.ts +++ b/utils/mockInboxData.ts @@ -21,7 +21,7 @@ export const mockInboxData = { name: 'Archie', task: 'Schema design', sessionId: 'session-archie-1', - stuckSince: new Date('2025-12-07T03:42:00'), + stuckSince: new Date(Date.now() - 6 * 3600000), // 6 hours ago (stuck since 3:42 AM) }, ] as StuckAgent[], overnightResults: [ @@ -31,10 +31,10 @@ export const mockInboxData = { ] as OvernightResult[], forecast: { deepWorkWindow: { - start: new Date('2025-12-07T10:30:00'), - end: new Date('2025-12-07T13:00:00'), + start: new Date(new Date().setHours(10, 30, 0, 0)), // Today at 10:30 AM + end: new Date(new Date().setHours(13, 0, 0, 0)), // Today at 1:00 PM }, - nextReviewBatch: new Date('2025-12-07T14:15:00'), + nextReviewBatch: new Date(new Date().setHours(14, 15, 0, 0)), // Today at 2:15 PM agentHoursInProgress: 14, } as Forecast, } @@ -190,35 +190,35 @@ export const mockNotificationHistory: Notification[] = [ id: 'n1', agentName: 'Parker', title: 'RFE #67 triage complete', // Easter egg! - createdAt: new Date('2025-12-07T12:52:00'), + createdAt: new Date(Date.now() - 3600000), // 1 hour ago status: 'dismissed', }, { id: 'n2', agentName: 'Phoenix', title: 'Test suite ready for review', - createdAt: new Date('2025-12-07T11:47:00'), + createdAt: new Date(Date.now() - 2 * 3600000), // 2 hours ago status: 'reviewed', }, { id: 'n3', agentName: 'Archie', title: 'Schema retry successful', - createdAt: new Date('2025-12-07T10:15:00'), + createdAt: new Date(Date.now() - 4 * 3600000), // 4 hours ago status: 'reviewed', }, { id: 'n4', agentName: 'Taylor', title: 'PR #127 merged successfully', - createdAt: new Date('2025-12-06T16:30:00'), + createdAt: new Date(Date.now() - 18 * 3600000), // 18 hours ago (yesterday afternoon) status: 'reviewed', }, { id: 'n5', agentName: 'Morgan', title: 'Build failed - Node version mismatch', - createdAt: new Date('2025-12-06T14:22:00'), + createdAt: new Date(Date.now() - 20 * 3600000), // 20 hours ago (yesterday afternoon) status: 'dismissed', }, ]