A mouse-aware terminal todo app with natural-language dates
Project description
soonish
A small mouse-aware terminal todo list, written in plain Python with no framework — raw-mode input decoding, ANSI rendering, and persistence are all implemented in-tree. The name is how it treats time: entry dates display with just enough precision to answer "what's happening soonish?".
You type entries into a prompt at the bottom of the screen; they appear above it, split into undated todos and date-scheduled tasks. Clicking an entry completes it.
Installation and usage
pip install soonish # or: pip install soonish[watch]
soonish [--debug-events | --no-debug-events] [--discard-unknown-fields]
python -m soonish works too. From a source checkout, pip install -e .
gives the same entry points.
Requires Python 3.10+ (uses match statements);
dateparser is installed as a
dependency, and the [watch] extra adds
inotify for live reload
when another instance or editor changes the data file.
Must be run in a real terminal; mouse support
needs a terminal that speaks SGR mouse reporting (any modern one does).
Requires a terminal of at least 5 columns × 3 rows; below that the app displays
a frowning face (☹) until the window is resized.
Options:
--debug-events/--no-debug-events— show / silently ignore unhandled input events on the status line (useful for development); when neither is given, thedebug_eventssetting (F4; default on) decides--discard-unknown-fields— load a data file written by a newer version of the software, dropping the fields this version does not understand (by default such a file is refused, so no data is silently lost)
Adding entries
Type at the prompt and press Enter:
| Input | Effect |
|---|---|
todo: buy milk |
Add an undated todo (if the line starts with todo:, the rest is the text) |
tomorrow 9am: standup |
Add a task due at that time (the date is everything before the last ": " in the line, falling back to the last : if there is no colon-space — so times like 17:30: dentist work) |
next friday: review |
next <weekday>/last <weekday> resolve to the nearest such day strictly in the future/past (never today, at most a week away); next week/next month mean the same day next week/month. last ... and other past dates are allowed — the task just shows as already due |
todo: [high] pay rent |
Tags: a todo starting with [high] sorts above untagged todos, [low] below (tags are case-insensitive, shown as typed; only leading [...] words count) |
/query |
Live search: filters the current view as you type. A date or range (/friday, /june, /next week, /2026-06-12) filters tasks by due time — the range is as wide as what you typed (a day, a week, a month, a year); plain text filters both panes case-insensitively; /friday: dentist applies both; /todo: milk searches only todos. Matching tasks show full-precision dates. ESC cancels; Enter ends the search (and records it in input history) |
done: milk |
(ACTIVE view only; prefix case-insensitive) live search like /, but when Enter is hit with exactly one entry matching, that entry is marked complete — the keyboard alternative to a left click |
Dates are understood in English plus your locale's language (from
$LANGUAGE/$LANG — under LANG=fr_FR.UTF-8, demain: x works).
Letting dateparser guess the language of every input instead is far
too slow for the live search, which parses the query on each keystroke;
the dateparser_languages setting (F4) adds languages your locale
doesn't mention, or selects them all (leave it empty), at that price.
Keys and mouse
- F1 / F2 / F3 — switch between ACTIVE, COMPLETED, and CALENDAR views; clicking a mode label in the heading does the same. The current view's label is highlighted bright+bold (or
[bracketed]on colourless terminals) - F4 — the settings view: every tunable (date language list, layout thresholds, ASCII markers, linger time, colour off, …) as a row. Click a tickbox (or Enter on the Up/Down-selected row) to toggle a boolean; click any other row to edit its value on the prompt, Enter to apply. Changes apply immediately and persist to a
settings.tsvnext to the data file. F4 or ESC leaves - CALENDAR view — as many month grids as fit the window; dates colour-coded (red = overdue tasks, green = upcoming, grey = nothing scheduled, plain = all done; today bold); PgUp/PgDn page months; clicking a date jumps to ACTIVE/COMPLETED with a
/search for that date applied - Left click an entry — mark complete (it lingers ~15 s, then leaves the ACTIVE view); the keyboard alternative is a
done:search (see above) - Right click an entry — un-complete it (in the COMPLETED view it lingers ~15 s before leaving, for the same mid-glance reasons)
- Middle click an entry — edit it: the prompt is pre-filled with its
date: textform, the entry mirrors your typing live (in bold), and Enter replaces it (creation time is kept). ESC or an empty Enter cancels. Ending a/search with Enter when exactly one entry matches also opens it for editing. Committing an edit of a completed entry un-completes it — the keyboard alternative to a right click (find it with/, Enter to edit, Enter to commit) - Up/Down arrows or scroll wheel — input history
- Shift+Left/Right — horizontally scroll truncated entry text (half a screen per press; markers and dates stay put; resets on mode switch, Enter or ESC)
- PgUp/PgDn — scroll the todo/task panes by half a pane per press; click the whitespace after an entry's text to make that pane active (rendered bold) and scroll it alone — click again or press ESC to deactivate
- Enter on an empty prompt — clears the status line
- Usual line-editing keys at the prompt: Home/End, Ctrl+Left/Right (by word), Insert (overwrite mode), ESC (clear line)
- Ctrl-C — quit
On terminals that render unicode (probed at startup) and are at least
80×25, the whole UI is framed with box-drawing borders — mode labels and
clock embedded in the top border, … indicators where a pane has more
entries than fit. Smaller or glyph-less terminals keep the plain layout.
At 160 columns or wider, todos and tasks display side by side (todos
left, 50/50 split) instead of stacked.
On colour-capable terminals (set NO_COLOR to opt out), the ACTIVE view
colours tasks green when due within 30 minutes and red when overdue;
just-completed entries linger in green; a leading [high] tag is shown in
high intensity.
Storage
Entries are kept in a single TSV file at $XDG_DATA_HOME/soonish/soonish.tsv
(default ~/.local/share/soonish/soonish.tsv). The app was previously named
todo; an existing ~/.local/share/todo/todo.tsv is adopted (moved over)
automatically on first start, unless a soonish data file already exists.
Writes are atomic (temp file + rename),
fsync'd to protect against power loss, and preserve the file's permissions.
The file is human-readable: a name:type header row describing the columns,
then one line per entry:
created:t due:t complete:t uid:s modified:t text:s
<created>\t<due|None>\t<complete|None>\t<uid>\t<modified>\t<text>
The header makes the format forward-compatible: old headerless files are
read and silently upgraded on the next change (a .bak copy is kept), and
fields a future version adds will default sensibly when missing.
Two running instances (e.g. two users with write access to the same file)
can share a task list: writes take a lock and merge concurrent changes by
entry uid (the newer change wins), and a running instance notices external
changes to the file via inotify and folds them in within half a second.
See doc/database.md for the details.
Code layout
The package lives in src/soonish/:
| Module | Role |
|---|---|
main.py |
cli() entry point, event loop, command dispatch |
events.py |
Raw terminal input → typed events; TextBuffer line editor and its History |
rendering.py |
View: screen layout, modes, status line |
calendar_view.py |
The CALENDAR mode's month grids and paging |
database.py |
Entry records and the TSV-backed Database |
search.py |
Date parsing (parse_when) and live / search queries |
tags.py |
[tag] semantics ([high]/[low] priority bands) |
caps.py |
Terminal capability detection (colour, cursor control, glyph width) |
ansi.py |
ANSI escape-sequence constants/builders |
config.py |
The defaults for every user-tunable value |
settings.py |
Settings schema and settings.tsv persistence (the F4 view's backend) |
Per-module documentation lives in doc/. Tests (tests/, pytest)
run against the source tree directly: python3 -m pytest -q.
License
GPLv3 (see LICENSE).
Status
Working but in progress; pending small features are marked with todo:
comments in the source.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file soonish-0.1.1.tar.gz.
File metadata
- Download URL: soonish-0.1.1.tar.gz
- Upload date:
- Size: 108.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
288d830543e80bf883c47741f3b6293655373f9c6bb97e4c0eab89cc3512ab28
|
|
| MD5 |
d046f310fe87ca7e5ff735c723e1b8c8
|
|
| BLAKE2b-256 |
09b8a2a8b177aff135a8b848ca7ef918811f47d0441d7d8ce128441908d99eaa
|
File details
Details for the file soonish-0.1.1-py3-none-any.whl.
File metadata
- Download URL: soonish-0.1.1-py3-none-any.whl
- Upload date:
- Size: 60.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9c34f4c4ef166cac30499ec246e0b2bfd7212705f64c728b464fecce3d2b08e3
|
|
| MD5 |
2061dfb6d53bfadbb3540c52e5165b17
|
|
| BLAKE2b-256 |
a5a5bdf8dbc1bac9281d5424106bdfd9e7555956311f5b5626756a1d615d3e56
|