Update expo application
Build and Push Next App / quality (push) Successful in 1m27s
Build and Push Next App / build-next (push) Successful in 3m58s

This commit is contained in:
Gabriel Brown
2026-06-22 12:13:02 -04:00
parent ddce5efb13
commit 42f95530de
78 changed files with 5315 additions and 421 deletions
@@ -0,0 +1,46 @@
import { useState } from 'react';
import { Text, View } from 'react-native';
import { Button } from '~/components/ui/button';
export const DiffPreview = ({
content,
initialLines = 120,
}: {
content: string;
initialLines?: number;
}) => {
const [expanded, setExpanded] = useState(false);
const lines = content.split('\n');
const visibleLines = expanded ? lines : lines.slice(0, initialLines);
const hiddenCount = Math.max(lines.length - visibleLines.length, 0);
return (
<View className='gap-3 rounded-lg bg-zinc-950 p-3'>
<View>
{visibleLines.map((line, index) => {
const color = line.startsWith('+')
? 'text-emerald-300'
: line.startsWith('-')
? 'text-red-300'
: line.startsWith('@@')
? 'text-sky-300'
: 'text-zinc-100';
return (
<Text
key={`${index}-${line.slice(0, 12)}`}
className={`font-mono text-xs leading-5 ${color}`}
>
{line || ' '}
</Text>
);
})}
</View>
{hiddenCount > 0 ? (
<Button variant='outline' onPress={() => setExpanded(true)}>
Show {hiddenCount} more lines
</Button>
) : null}
</View>
);
};
@@ -0,0 +1,71 @@
import { Text, View } from 'react-native';
import * as Clipboard from 'expo-clipboard';
import { Button } from '~/components/ui/button';
import { Card } from '~/components/ui/card';
import { DiffPreview } from './diff-preview';
type Artifact = {
_id: string;
content: string;
contentType: string;
kind: string;
title: string;
};
export const WorkspaceArtifacts = ({
artifacts,
mode,
}: {
artifacts: Artifact[];
mode: 'diffs' | 'artifacts';
}) => {
const diffArtifacts = artifacts.filter(
(artifact) =>
artifact.contentType === 'text/x-diff' || artifact.kind === 'diff',
);
const visible =
mode === 'diffs'
? diffArtifacts
: artifacts.filter((artifact) => !diffArtifacts.includes(artifact));
return (
<Card className='gap-3'>
<Text className='text-foreground font-semibold'>
{mode === 'diffs' ? 'Diffs' : 'Artifacts'}
</Text>
{visible.length ? (
visible.map((artifact) => (
<View key={artifact._id} className='gap-2'>
<Text className='text-muted-foreground text-sm'>
{artifact.title}
</Text>
{mode === 'diffs' ? (
<DiffPreview content={artifact.content} />
) : (
<>
<Text className='bg-muted text-foreground rounded-md p-3 font-mono text-xs leading-5'>
{artifact.content.slice(0, 2_000)}
</Text>
<Button
variant='outline'
onPress={() =>
void Clipboard.setStringAsync(artifact.content)
}
>
Copy artifact
</Button>
</>
)}
</View>
))
) : (
<Text className='text-muted-foreground text-sm'>
{mode === 'diffs'
? 'Diff artifacts will appear here when the worker records them.'
: 'No non-diff artifacts recorded.'}
</Text>
)}
</Card>
);
};
@@ -0,0 +1,49 @@
import { useState } from 'react';
import { Text, View } from 'react-native';
import { Card } from '~/components/ui/card';
import { ChipRow } from '~/components/ui/chip-row';
import { formatDateTime, titleize } from '~/utils/format';
type Event = {
_id: string;
createdAt: number;
level: string;
message: string;
phase: string;
};
export const WorkspaceEvents = ({ events }: { events: Event[] }) => {
const [level, setLevel] = useState<'all' | 'info' | 'warn' | 'error'>('all');
const filtered =
level === 'all' ? events : events.filter((event) => event.level === level);
return (
<Card className='gap-3'>
<Text className='text-foreground font-semibold'>Events</Text>
<ChipRow
options={[
{ label: 'All', value: 'all' },
{ label: 'Info', value: 'info' },
{ label: 'Warn', value: 'warn' },
{ label: 'Error', value: 'error' },
]}
value={level}
onChange={setLevel}
/>
{filtered.length ? (
filtered.map((event) => (
<View key={event._id} className='border-border border-b pb-2'>
<Text className='text-muted-foreground text-xs'>
{formatDateTime(event.createdAt)} · {titleize(event.phase)} ·{' '}
{titleize(event.level)}
</Text>
<Text className='text-foreground mt-1'>{event.message}</Text>
</View>
))
) : (
<Text className='text-muted-foreground text-sm'>No events.</Text>
)}
</Card>
);
};
@@ -0,0 +1,26 @@
import { Text, View } from 'react-native';
import { Card } from '~/components/ui/card';
import { titleize } from '~/utils/format';
export const WorkspaceMessages = ({
messages,
}: {
messages: { _id: string; content: string; role: string; status: string }[];
}) => (
<Card className='gap-3'>
<Text className='text-foreground font-semibold'>Messages</Text>
{messages.length ? (
messages.map((message) => (
<View key={message._id} className='border-border border-b pb-2'>
<Text className='text-muted-foreground text-xs'>
{titleize(message.role)} · {titleize(message.status)}
</Text>
<Text className='text-foreground mt-1'>{message.content}</Text>
</View>
))
) : (
<Text className='text-muted-foreground text-sm'>No messages yet.</Text>
)}
</Card>
);
@@ -0,0 +1,68 @@
import { Linking, Text, View } from 'react-native';
import { Badge } from '~/components/ui/badge';
import { Button } from '~/components/ui/button';
import { Card } from '~/components/ui/card';
import { ConfirmButton } from '~/components/ui/confirm-button';
import { CopyRow } from '~/components/ui/copy-row';
import { formatDateTime, titleize } from '~/utils/format';
export const WorkspaceSummary = ({
cancelling,
job,
onCancel,
}: {
cancelling: boolean;
job: {
completedAt?: number;
model: string;
pullRequestUrl?: string;
reasoningEffort: string;
startedAt?: number;
status: string;
workBranch: string;
workspaceStatus?: string;
};
onCancel: () => void;
}) => (
<Card className='gap-3'>
<View className='flex-row flex-wrap gap-2'>
<Badge label={titleize(job.status)} tone='primary' />
<Badge label={titleize(job.workspaceStatus ?? 'not_started')} />
</View>
<Text className='text-muted-foreground text-sm'>
Branch: {job.workBranch}
</Text>
<Text className='text-muted-foreground text-sm'>Model: {job.model}</Text>
<Text className='text-muted-foreground text-sm'>
Reasoning: {titleize(job.reasoningEffort)}
</Text>
<Text className='text-muted-foreground text-sm'>
Started: {formatDateTime(job.startedAt)}
</Text>
{job.completedAt ? (
<Text className='text-muted-foreground text-sm'>
Completed: {formatDateTime(job.completedAt)}
</Text>
) : null}
<CopyRow label='Draft PR' value={job.pullRequestUrl} />
{job.pullRequestUrl ? (
<Button onPress={() => void Linking.openURL(job.pullRequestUrl ?? '')}>
Open draft PR
</Button>
) : null}
<ConfirmButton
confirmLabel='Cancel job'
destructive
disabled={
cancelling ||
['cancelled', 'draft_pr_opened', 'failed'].includes(job.status)
}
message='Cancel this workspace job? Running work will be stopped where possible.'
title='Cancel job'
onConfirm={onCancel}
>
{cancelling ? 'Cancelling...' : 'Cancel job'}
</ConfirmButton>
</Card>
);