import { useState } from 'react' import { Pressable, Text, View } from 'react-native' import { ChevronRight, Circle, CircleCheck, CircleDot, ListChecks } from 'lucide-react-native' import { diffNativeChatTaskLists, NATIVE_CHAT_TASK_LIST_COPY, nativeChatTaskChangeWords, nativeChatTaskLabel, type NativeChatTask, type NativeChatTaskList as TaskList } from '../../../src/shared/native-chat-task-list' import { colors } from '../theme/mobile-theme' import { useMobileNativeChatOpenedDetail } from './mobile-native-chat-opened-detail' import { toolStyles as styles, toolType } from './mobile-native-chat-tool-styles' const STATUS_WORD: Record = { completed: NATIVE_CHAT_TASK_LIST_COPY.completed, in_progress: NATIVE_CHAT_TASK_LIST_COPY.inProgress, pending: NATIVE_CHAT_TASK_LIST_COPY.pending } /** Content plus occurrence is the only identity the providers give these entries. */ function occurrenceKeyed(items: readonly T[], content: (item: T) => string): [T, string][] { const seen = new Map() return items.map((item) => { const signature = content(item) const occurrence = seen.get(signature) ?? 0 seen.set(signature, occurrence + 1) return [item, `${signature}:${occurrence}`] }) } function TaskRow({ task, label, fontScale }: { task: NativeChatTask label?: string fontScale: number }): React.JSX.Element { const Icon = task.status === 'completed' ? CircleCheck : task.status === 'in_progress' ? CircleDot : Circle const active = task.status === 'in_progress' return ( {label ?? nativeChatTaskLabel(task)} ) } function Checklist({ list, fontScale }: { list: TaskList; fontScale: number }) { return list.tasks.length === 0 ? ( {NATIVE_CHAT_TASK_LIST_COPY.empty} ) : ( {occurrenceKeyed(list.tasks, (task) => task.content).map(([task, key]) => ( ))} ) } /** The whole list behind a disclosure, under a list of what changed. */ function FullList({ list, fontScale }: { list: TaskList; fontScale: number }) { const [open, setOpen] = useState(false) return ( setOpen((value) => !value)} accessibilityRole="button" accessibilityState={{ expanded: open }} > {NATIVE_CHAT_TASK_LIST_COPY.showAll} {open ? : null} ) } function OpenedChecklist({ list, fontScale }: { list: TaskList; fontScale: number }) { const onLayout = useMobileNativeChatOpenedDetail() return ( ) } /** Desktop parity: `NativeChatTaskList`, inline. A plan update reads as what it * changed against the previous list, or as the whole checklist when it is the first. */ export function MobileNativeChatTaskList({ list, previous, fontScale }: { list: TaskList previous?: TaskList fontScale: number }): React.JSX.Element { const completed = list.tasks.filter((task) => task.status === 'completed').length const changes = previous ? diffNativeChatTaskLists(previous, list) : null const type = toolType(fontScale) return ( {NATIVE_CHAT_TASK_LIST_COPY.title} {`${completed}/${list.tasks.length}`} {changes ? ( <> {changes.length > 0 ? ( {occurrenceKeyed(changes, (change) => `${change.kind}:${change.task.content}`).map( ([change, rowKey]) => { const { key, task } = nativeChatTaskChangeWords(change) return ( ) } )} ) : ( {NATIVE_CHAT_TASK_LIST_COPY.unchanged} )} ) : ( )} {list.explanation ? ( {list.explanation} ) : null} ) }