From 00aa0d03f86023a666dd7ad25b00eccfcee4f3ac Mon Sep 17 00:00:00 2001 From: Aly Raffauf Date: Wed, 11 Feb 2026 14:53:52 -0500 Subject: [PATCH] make ui layout and rendering pretty --- source/app.tsx | 84 +++++++++++++++++++------- source/commands.ts | 1 - source/edit-view.tsx | 9 +-- source/task-list-view.tsx | 124 +++++++++++++++++++------------------- 4 files changed, 130 insertions(+), 88 deletions(-) diff --git a/source/app.tsx b/source/app.tsx index c2cc47c..33be1d4 100644 --- a/source/app.tsx +++ b/source/app.tsx @@ -1,5 +1,6 @@ import React, {useState, useEffect, useCallback} from 'react'; import {Box, Text, useApp} from 'ink'; +import TextInput from 'ink-text-input'; import {TodoistApi, type Task} from '@doist/todoist-api-typescript'; import {type View, commands} from './commands.js'; import {loadConfig, configPath} from './config.js'; @@ -63,6 +64,10 @@ export default function App() { const trimmed = value.trim(); setInput(''); + if (!trimmed || trimmed === '?') { + return; + } + const ctx = { api, tasks, @@ -91,39 +96,76 @@ export default function App() { const viewLabel = view.type === 'edit' ? `dewy ∙ edit: ${view.task.content}` - : `dewy ∙ ${view.query}`; + : `dewy ∙ ${view.query === homeFilter ? 'home' : view.query}`; if (view.type === 'edit') { return ( - + {viewLabel} - { - setView({type: 'filter', query: homeFilter}); - refresh(); - }} - /> + + { + setView({type: 'filter', query: homeFilter}); + refresh(); + }} + /> + ); } return ( - - {viewLabel} - - + + + {message && {message}} + + + + + {'> '} + + + + + {input === '?' ? ( + commands.map(c => ( + + {' '} + {c.hint} + + )) + ) : input ? ( + commands + .filter(c => c.prefix.startsWith(input)) + .map(c => ( + + {' '} + {c.hint} + + )) + ) : ( + {' type ? for help'} + )} ); } diff --git a/source/commands.ts b/source/commands.ts index 02c8278..c6cfbbd 100644 --- a/source/commands.ts +++ b/source/commands.ts @@ -8,7 +8,6 @@ export type CommandContext = { projects: Map; homeFilter: string; setMessage: (msg: string) => void; - refresh: () => Promise; setView: (view: View) => void; exit: () => void; diff --git a/source/edit-view.tsx b/source/edit-view.tsx index d7fd280..01aac8e 100644 --- a/source/edit-view.tsx +++ b/source/edit-view.tsx @@ -1,5 +1,5 @@ import React, {useState} from 'react'; -import {Box, Text, useInput} from 'ink'; +import {Text, useInput} from 'ink'; import TextInput from 'ink-text-input'; import {type TodoistApi, type Task} from '@doist/todoist-api-typescript'; import {priorityColor} from './utils.js'; @@ -98,7 +98,7 @@ export default function EditView({task, api, onBack}: EditViewProps) { if (key.return && !editing) { setEditing(true); - setEditValue(getFieldValue(task, fields[cursor]!.key)); + setEditValue(fieldValues[fields[cursor]!.key] ?? ''); } }, {isActive: !editing}, @@ -114,7 +114,7 @@ export default function EditView({task, api, onBack}: EditViewProps) { ); return ( - + <> {fields.map((f, i) => ( {cursor === i ? '> ' : ' '} @@ -132,11 +132,12 @@ export default function EditView({task, api, onBack}: EditViewProps) { )} ))} + {''} {editing ? ' Enter to save ∙ Escape to cancel' : ' Enter to edit ∙ Escape to go back'} - + ); } diff --git a/source/task-list-view.tsx b/source/task-list-view.tsx index 1f92689..be56b5e 100644 --- a/source/task-list-view.tsx +++ b/source/task-list-view.tsx @@ -1,82 +1,82 @@ import React from 'react'; import {Box, Text} from 'ink'; -import TextInput from 'ink-text-input'; import {type Task} from '@doist/todoist-api-typescript'; -import {commands} from './commands.js'; import {priorityColor} from './utils.js'; type TaskListViewProps = { tasks: Task[]; projects: Map; - message: string; - input: string; - setInput: (value: string) => void; - onSubmit: (value: string) => Promise; + viewLabel: string; }; +function ProjectLabel({name}: {name: string | undefined}) { + if (!name) return null; + return ( + + {' '} + #{name} + + ); +} + +function Labels({labels}: {labels: string[]}) { + if (labels.length === 0) return null; + return ( + + {' '} + {labels.map(l => `@${l}`).join(' ')} + + ); +} + +function DueDate({date}: {date: string | undefined}) { + if (!date) return null; + return ( + + {' '} + {date} + + ); +} + +function Priority({priority}: {priority: number}) { + const display = 5 - priority; + if (display >= 4) return null; + return ( + + {' '} + p{display} + + ); +} + export default function TaskListView({ tasks, projects, - message, - input, - setInput, - onSubmit, + viewLabel, }: TaskListViewProps) { return ( <> - {tasks.map((task, i) => ( - - {i + 1}. {task.content} - {projects.get(task.projectId) ? ( - - {' '} - #{projects.get(task.projectId)} - - ) : ( - '' - )} - {task.labels.length > 0 ? ( - - {' '} - {task.labels.map(l => `@${l}`).join(' ')} - - ) : ( - '' - )} - {task.due?.date ? ( - - {' '} - {task.due.date} - - ) : ( - '' - )} - {task.priority > 1 ? ( - - {' '} - p{5 - task.priority} - - ) : ( - '' - )} - - ))} - {message && {message}} - - - {'> '} - - + + {viewLabel} + + + {tasks.length === 0 && No tasks} + {tasks.map((task, i) => ( + + {i + 1}. {task.content} + + + + + + ))} - {input && - commands - .filter(c => c.prefix.startsWith(input)) - .map(c => ( - - {' '} - {c.hint} - - ))} ); } -- 2.51.2