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',
},
]