TUI for Todoist written with React+Ink.
0

Configure Feed

Select the types of activity you want to include in your feed.

extract task list into TaskListView component

Aly Raffauf (Feb 11, 2026, 2:18 PM EST) f43e0395 b20894e2

+91 -56
+9 -56
source/app.tsx
··· 1 1 import React, {useState, useEffect, useCallback} from 'react'; 2 2 import {Box, Text, useApp} from 'ink'; 3 - import TextInput from 'ink-text-input'; 4 3 import {TodoistApi, type Task} from '@doist/todoist-api-typescript'; 5 4 import {type View, commands} from './commands.js'; 6 - import {priorityColor} from './utils.js'; 7 5 import {loadConfig, configPath} from './config.js'; 8 6 import EditView from './edit-view.js'; 7 + import TaskListView from './task-list-view.js'; 9 8 10 9 const config = loadConfig(); 11 10 const apiToken = config?.apiToken ?? process.env['TODOIST_API_TOKEN']; ··· 117 116 <Text bold color="cyan"> 118 117 {viewLabel} 119 118 </Text> 120 - {tasks.map((task, i) => ( 121 - <Text key={task.id}> 122 - <Text dimColor>{i + 1}.</Text> {task.content} 123 - {projects.get(task.projectId) ? ( 124 - <Text dimColor color="blue"> 125 - {' '} 126 - #{projects.get(task.projectId)} 127 - </Text> 128 - ) : ( 129 - '' 130 - )} 131 - {task.labels.length > 0 ? ( 132 - <Text dimColor color="yellow"> 133 - {' '} 134 - {task.labels.map(l => `@${l}`).join(' ')} 135 - </Text> 136 - ) : ( 137 - '' 138 - )} 139 - {task.due?.date ? ( 140 - <Text dimColor color="magenta"> 141 - {' '} 142 - {task.due.date} 143 - </Text> 144 - ) : ( 145 - '' 146 - )} 147 - {task.priority > 1 ? ( 148 - <Text dimColor color={priorityColor(5 - task.priority)}> 149 - {' '} 150 - p{5 - task.priority} 151 - </Text> 152 - ) : ( 153 - '' 154 - )} 155 - </Text> 156 - ))} 157 - {message && <Text color="yellow">{message}</Text>} 158 - <Box> 159 - <Text bold color="green"> 160 - {'> '} 161 - </Text> 162 - <TextInput value={input} onChange={setInput} onSubmit={handleSubmit} /> 163 - </Box> 164 - 165 - {input && 166 - commands 167 - .filter(c => c.prefix.startsWith(input)) 168 - .map(c => ( 169 - <Text key={c.prefix} dimColor> 170 - {' '} 171 - {c.hint} 172 - </Text> 173 - ))} 119 + <TaskListView 120 + tasks={tasks} 121 + projects={projects} 122 + message={message} 123 + input={input} 124 + setInput={setInput} 125 + onSubmit={handleSubmit} 126 + /> 174 127 </Box> 175 128 ); 176 129 }
+82
source/task-list-view.tsx
··· 1 + import React from 'react'; 2 + import {Box, Text} from 'ink'; 3 + import TextInput from 'ink-text-input'; 4 + import {type Task} from '@doist/todoist-api-typescript'; 5 + import {commands} from './commands.js'; 6 + import {priorityColor} from './utils.js'; 7 + 8 + type TaskListViewProps = { 9 + tasks: Task[]; 10 + projects: Map<string, string>; 11 + message: string; 12 + input: string; 13 + setInput: (value: string) => void; 14 + onSubmit: (value: string) => Promise<void>; 15 + }; 16 + 17 + export default function TaskListView({ 18 + tasks, 19 + projects, 20 + message, 21 + input, 22 + setInput, 23 + onSubmit, 24 + }: TaskListViewProps) { 25 + return ( 26 + <> 27 + {tasks.map((task, i) => ( 28 + <Text key={task.id}> 29 + <Text dimColor>{i + 1}.</Text> {task.content} 30 + {projects.get(task.projectId) ? ( 31 + <Text dimColor color="blue"> 32 + {' '} 33 + #{projects.get(task.projectId)} 34 + </Text> 35 + ) : ( 36 + '' 37 + )} 38 + {task.labels.length > 0 ? ( 39 + <Text dimColor color="yellow"> 40 + {' '} 41 + {task.labels.map(l => `@${l}`).join(' ')} 42 + </Text> 43 + ) : ( 44 + '' 45 + )} 46 + {task.due?.date ? ( 47 + <Text dimColor color="magenta"> 48 + {' '} 49 + {task.due.date} 50 + </Text> 51 + ) : ( 52 + '' 53 + )} 54 + {task.priority > 1 ? ( 55 + <Text dimColor color={priorityColor(5 - task.priority)}> 56 + {' '} 57 + p{5 - task.priority} 58 + </Text> 59 + ) : ( 60 + '' 61 + )} 62 + </Text> 63 + ))} 64 + {message && <Text color="yellow">{message}</Text>} 65 + <Box> 66 + <Text bold color="green"> 67 + {'> '} 68 + </Text> 69 + <TextInput value={input} onChange={setInput} onSubmit={onSubmit} /> 70 + </Box> 71 + {input && 72 + commands 73 + .filter(c => c.prefix.startsWith(input)) 74 + .map(c => ( 75 + <Text key={c.prefix} dimColor> 76 + {' '} 77 + {c.hint} 78 + </Text> 79 + ))} 80 + </> 81 + ); 82 + }