).observation;
+ if (obs.command === "plan") {
+ return obs.task_list.map((t, i) => ({
+ id: String(i + 1),
+ title: t.title,
+ status: t.status,
+ notes: t.notes || undefined,
+ }));
+ }
+ }
+
+ return null;
+}
+
+export function useTaskList() {
+ const events = useEventStore((state) => state.events);
+
+ return useMemo(() => {
+ // Iterate in reverse to find the latest TaskTrackingObservation with command="plan"
+ for (let i = events.length - 1; i >= 0; i -= 1) {
+ const taskList = getTaskListFromEvent(events[i]);
+ if (taskList) {
+ return { taskList, hasTaskList: taskList.length > 0 };
+ }
+ }
+
+ return { taskList: [] as TaskListItem[], hasTaskList: false };
+ }, [events]);
+}
diff --git a/frontend/src/i18n/declaration.ts b/frontend/src/i18n/declaration.ts
index 44ff4dcc38..b968be1ec9 100644
--- a/frontend/src/i18n/declaration.ts
+++ b/frontend/src/i18n/declaration.ts
@@ -993,6 +993,8 @@ export enum I18nKey {
COMMON$MORE_OPTIONS = "COMMON$MORE_OPTIONS",
COMMON$CREATE_A_PLAN = "COMMON$CREATE_A_PLAN",
COMMON$TASKS = "COMMON$TASKS",
+ COMMON$TASK_LIST = "COMMON$TASK_LIST",
+ COMMON$NO_TASKS = "COMMON$NO_TASKS",
COMMON$PLAN_MD = "COMMON$PLAN_MD",
COMMON$READ_MORE = "COMMON$READ_MORE",
COMMON$BUILD = "COMMON$BUILD",
diff --git a/frontend/src/i18n/translation.json b/frontend/src/i18n/translation.json
index d84602daab..1817da31f7 100644
--- a/frontend/src/i18n/translation.json
+++ b/frontend/src/i18n/translation.json
@@ -15891,6 +15891,38 @@
"de": "Aufgaben",
"uk": "Завдання"
},
+ "COMMON$TASK_LIST": {
+ "en": "Task List",
+ "ja": "タスクリスト",
+ "zh-CN": "任务列表",
+ "zh-TW": "任務列表",
+ "ko-KR": "작업 목록",
+ "no": "Oppgaveliste",
+ "it": "Elenco attività",
+ "pt": "Lista de tarefas",
+ "es": "Lista de tareas",
+ "ar": "قائمة المهام",
+ "fr": "Liste des tâches",
+ "tr": "Görev listesi",
+ "de": "Aufgabenliste",
+ "uk": "Список завдань"
+ },
+ "COMMON$NO_TASKS": {
+ "en": "No tasks yet",
+ "ja": "タスクはまだありません",
+ "zh-CN": "暂无任务",
+ "zh-TW": "尚無任務",
+ "ko-KR": "아직 작업이 없습니다",
+ "no": "Ingen oppgaver ennå",
+ "it": "Nessuna attività",
+ "pt": "Nenhuma tarefa ainda",
+ "es": "Sin tareas aún",
+ "ar": "لا توجد مهام بعد",
+ "fr": "Aucune tâche pour le moment",
+ "tr": "Henüz görev yok",
+ "de": "Noch keine Aufgaben",
+ "uk": "Завдань поки немає"
+ },
"COMMON$PLAN_MD": {
"en": "Plan.md",
"ja": "Plan.md",
diff --git a/frontend/src/icons/double-check.svg b/frontend/src/icons/double-check.svg
new file mode 100644
index 0000000000..6205a25f89
--- /dev/null
+++ b/frontend/src/icons/double-check.svg
@@ -0,0 +1,4 @@
+
diff --git a/frontend/src/routes/task-list-tab.tsx b/frontend/src/routes/task-list-tab.tsx
new file mode 100644
index 0000000000..43280e3abb
--- /dev/null
+++ b/frontend/src/routes/task-list-tab.tsx
@@ -0,0 +1,41 @@
+import { useTranslation } from "react-i18next";
+import { I18nKey } from "#/i18n/declaration";
+import CheckCircleIcon from "#/icons/u-check-circle.svg?react";
+import { TaskItem } from "#/components/features/chat/task-tracking/task-item";
+import { useTaskList } from "#/hooks/use-task-list";
+import { Text } from "#/ui/typography";
+import { cn } from "#/utils/utils";
+
+function TaskListTab() {
+ const { t } = useTranslation();
+ const { taskList } = useTaskList();
+
+ if (taskList.length === 0) {
+ return (
+
+
+
+ {t(I18nKey.COMMON$NO_TASKS)}
+
+
+ );
+ }
+
+ return (
+
+ {taskList.map((task) => (
+
+
+
+ ))}
+
+ );
+}
+
+export default TaskListTab;
diff --git a/frontend/src/stores/conversation-store.ts b/frontend/src/stores/conversation-store.ts
index 94103d23c4..3d864fe14e 100644
--- a/frontend/src/stores/conversation-store.ts
+++ b/frontend/src/stores/conversation-store.ts
@@ -11,7 +11,8 @@ export type ConversationTab =
| "served"
| "vscode"
| "terminal"
- | "planner";
+ | "planner"
+ | "tasklist";
export type ConversationMode = "code" | "plan";