Files
paseo/packages/app/src/composer/task-list/index.tsx
T
Mohamed Boudra 25fbc939e0 feat(app): give composer track pills a segment per state
A mixed fan-out used to collapse the pill to its single most urgent
state, so a red dot beside "1 failed" over a child that was still
working read as the whole fan-out having stopped. Every state present
now gets its own mark and count, in the sidebar's status-group order.
2026-08-18 21:51:25 +02:00

56 lines
1.9 KiB
TypeScript

import { memo, useMemo } from "react";
import { View } from "react-native";
import { useTranslation } from "react-i18next";
import { StyleSheet } from "react-native-unistyles";
import { ComposerTrackPill, ComposerTrackRow } from "@/composer/tracks";
import { TaskListRow } from "@/components/task-list-row";
import type { TodoEntry } from "@/types/stream";
export const AgentTaskList = memo(function AgentTaskList({
tasks,
}: {
tasks: TodoEntry[] | undefined;
}) {
if (!tasks?.length) return null;
return <TaskListCard tasks={tasks} />;
});
const TaskListCard = memo(function TaskListCard({ tasks }: { tasks: TodoEntry[] }) {
const { t } = useTranslation();
const completed = useMemo(
() => tasks.filter((task) => task.completed || task.status === "completed").length,
[tasks],
);
// Counts only. The active task used to ride along in the header, where it was the first thing
// truncated on a phone; the panel shows it in full, in place, with the rest of the list.
const label = t("message.todo.tasksProgress", { completed, total: tasks.length });
const segments = useMemo(() => [{ bucket: null, text: label }], [label]);
return (
<ComposerTrackPill
testID="agent-task-list-header"
segments={segments}
panelTitle={t("message.todo.title")}
>
{tasks.map((task, index) => (
<ComposerTrackRow key={task.id ?? `${index}:${task.text}`}>
<View style={styles.taskRow}>
<TaskListRow task={task} />
</View>
</ComposerTrackRow>
))}
</ComposerTrackPill>
);
});
const styles = StyleSheet.create(() => ({
// The task row draws its own icon and text; this only lets it span the shared row frame.
// Basis stays `auto` so the text's width reaches the panel's measurement — see track.tsx.
taskRow: {
flexGrow: 1,
flexShrink: 1,
flexBasis: "auto",
minWidth: 0,
},
}));