2026-11-01 00:50:52 +01:00
# ADR 0033: Halkyon Todo, what it does and how it looks
- **Status:** decided 1 Nov 2026, by the developer (D1– D10 below, the agent's
feat(todo): recurring tasks, set through a picker (ADR 0033, D7)
A task's date may recur: every n days, weeks (on which weekdays),
months (on its day, or its nth or last weekday), years, from its date
or from when it is done, until a day or for n times. Done, it moves on
to the next day after both its date and today, its deadline as far, its
time kept, its done sub-tasks open again and when it was done kept in
its payload; undo writes all of it back. The date sheet offers the rules
that fit the task's day and a rule of the person's own; rows show a
recurring date with its own icon, the task sheet the rule in words.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-01 12:15:51 +01:00
recommendations taken as a whole). Built the same day: steps 1 to 3, Today and
feat(todo): reminders on the device, Upcoming's week strip, the task pane and search (ADR 0033, D2, D5, D8)
A task with a time takes reminders, minutes before it, in its sealed
payload; each device plans the coming ones whenever the tasks change and
schedules them with the system through flutter_local_notifications
(Android, iOS, macOS, Windows; on Linux the running app shows them), at
the minute where Android allows exact alarms. Notifying is asked for
when a task first gets a reminder; signing out or switching person
cancels them. Upcoming gets its week strip: the month opening
Material's calendar, the weeks before and after, a dot under each day
with tasks; a day picked scrolls the agenda to it, and scrolling moves
the strip along. From expanded windows on, a task opens in a pane
beside the list rather than in a dialog. Search, from every tab's app
bar, finds projects by name and tasks by every word typed, in their
title, description or labels, whatever the case and accents, on the
device alone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-01 12:16:19 +01:00
Upcoming as lists, recurrence, reminders and the week strip (step 4), the task
pane on wide windows and search (D2).
2026-11-01 00:50:52 +01:00
- **Numbering:** 0025– 0029 are held (ADR 0030's note), 0032 is Jellyfin's; this is the
next free number.
- **Builds on:** ADR 0030, D7, D12, D14, D15 and D60 (Train's sealed rows, its shell, its
frame and settings, which Todo takes); ADR 0017 (the account's keys); ADR 0031 (Todo's
icon); Train's D61 (What's new).
## Context
On 1 Nov 2026 the developer asked for work on Todo to start from 68 screenshots of
Todoist's Android app (31 Oct 2026, `~/Downloads/todoist-screenshots-mobile/` ), "following
the same foundational reasoning of Halkyon Learn and Halkyon Train": when the bar and when
the rail, a wash of strawberry, settings grouped as theirs. What the screenshots show:
- **Five tabs** (Inbox, Today, Search, Upcoming, Browse), a floating add button above the
bar, the bar's items and the button's side set by the person; Browse holds the
projects, Reporting, templates and the cog to Settings.
- **A task** has a title, a description, a **date ** (optional time and **duration ** ,
"06:30– 07:00") and, apart from it, a **deadline ** (date only, its own icon, neutral
colour; a task may have a deadline and no date), a priority (P1– P4), labels, reminders
(relative to the time, automatic 30 minutes before a timed task; by location in the paid
plans), a recurrence (completing it moves it on; its sub-tasks reset), sub-tasks
(counted "0/5" on its row), comments with attachments. Rows colour the date by meaning
(overdue red, tomorrow orange, this week purple).
- **Projects** have sections (name, description, count), seen as a list, as a **board ** (a
column a section, paged) or as a **calendar ** (a week strip opening into the month, an
agenda below). **Today ** puts Overdue first, with Reschedule for all of them;
**Upcoming ** is an agenda from today on, or a **3-day ** grid of hours where a long press
picks 30 minutes. Every view has its own **Display ** sheet: layout, completed tasks,
grouping, sorting, filters by date, deadline, priority and label.
- **The task sheet** shows a row for each field set and a chip for each one not, the
sub-tasks, and a comment field at its foot; **quick add ** is a card above the keyboard
with chips (date, deadline, priority, reminders, project), dates recognised in what is
typed.
- **Productivity**: a daily and weekly goal, streaks, karma, days off and a vacation mode;
**Reporting ** , the completed tasks by day.
- **Settings**: Account, General (home view, smart dates, reset sub-tasks, the week's
first day, how "next week" and "weekend" are read, swipe actions), Theme, Navigation
bar, Quick Add, Productivity, Reminders, Notifications (a morning overview and an evening
review), About, What's new.
## Decisions (1 Nov 2026, the developer's)
- **D1 Todo's frame is Train's (ADR 0030, D60)**: go_router with a `StatefulShellRoute` ,
every tab keeping its own stack; halkyon_ui's `NavigationFrame` , a bar floating above the
feat(learn,todo,train,meditate,ui): the navigation bar flush with the foot, a fade above it
Below medium windows the frame's bar is Material 3 Expressive's again,
flush with the window's foot (decided 7 Nov 2026 by the developer): the
theme's NavigationBar across the whole width, 64 dp in its own colour,
running on beneath the system's bar as Material's does. Right above it a
fade 32 dp high, from the background of the screen in view (the
ScreenTint's, the theme's surface before a screen claims one) made
see-through to that background, so what scrolls on beneath the bar fades
out rather than being cut off; taps pass through it. The rail of wider
windows stays as it was.
halkyon_ui's FloatingNavigationBar becomes FlushNavigationBar, its side
margin, lift and rounded card gone. Its coverOf is now the whole room
content leaves at its end, the system's bar included (the body's bottom
padding: bar, fade and system's bar inside the frame, the system's bar
alone outside it), and floating buttons rise by buttonLiftOf, the same
without the system's bar, which a Scaffold keeps them clear of itself.
Lists, Todo's hour grid and the Plan's sheet keep using coverOf; the
Profile's list no longer adds the system's bar a second time.
Whats-New: The navigation bar sits at the bottom edge again, with a soft fade above it.
Whats-New-De: Die Navigationsleiste sitzt wieder am unteren Rand, mit einem sanften Übergang darüber.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-08 00:04:18 +01:00
foot below medium windows (flush with it, a fade above it, since 7 Nov 2026: ADR 0030,
D60's amendment) and a rail with Todo's icon on top from medium on, a tick on
2026-11-01 00:50:52 +01:00
every choice; Todo's account sheet goes. Over a frame of Todo's own, which would make the
three apps behave differently on the same window.
- **D2 Four tabs: Today, Upcoming, Inbox, Browse** (recommended, over Todoist's five and over
a bar the person arranges). Today opens. Browse holds the projects, the labels and,
later, saved filters, with the cog to Settings atop it as Train's Home and Profile carry
theirs. **Search ** is an icon in every tab's app bar, not a tab, as neither Learn nor
Train gives search a tab. **Adding a task ** is a floating button above the bar
(Train's `TabScreen` places it clear of the bar), on the rail's screen from medium
windows on. From expanded windows on, a task opens in a pane beside its list, as Train's
Maps opens a route's map beside the list (Material's list– detail layout), and in a
bottom sheet below. Arranging the bar may come later; Search may become a tab if it
feat(todo): reminders on the device, Upcoming's week strip, the task pane and search (ADR 0033, D2, D5, D8)
A task with a time takes reminders, minutes before it, in its sealed
payload; each device plans the coming ones whenever the tasks change and
schedules them with the system through flutter_local_notifications
(Android, iOS, macOS, Windows; on Linux the running app shows them), at
the minute where Android allows exact alarms. Notifying is asked for
when a task first gets a reminder; signing out or switching person
cancels them. Upcoming gets its week strip: the month opening
Material's calendar, the weeks before and after, a dot under each day
with tasks; a day picked scrolls the agenda to it, and scrolling moves
the strip along. From expanded windows on, a task opens in a pane
beside the list rather than in a dialog. Search, from every tab's app
bar, finds projects by name and tasks by every word typed, in their
title, description or labels, whatever the case and accents, on the
device alone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-01 12:16:19 +01:00
proves to be used as one. As built (1 Nov): the pane is the frame's, beside whichever tab
is shown (`TaskPane` around the shell's tabs), 420 wide, the list keeping the rest; a
task tapped there opens in it, another replaces it, what was typed into the last
kept. Narrowed below expanded, the pane hides and tasks open in sheets again. Search is
a screen pushed onto the tab's own stack (back returns to the tab, a task found opens in
the pane where there is one): as typed, the projects whose name holds it and the tasks
every word is found in, in their title, description or labels' names, whatever the
case and the accents, open ones first by their date, done ones last; on the device
alone, which the screen says, as the instance reads no task (D5).
2026-11-01 00:50:52 +01:00
- **D3 Every screen washes down from the top in strawberry** (the developer's):
`TopGradient` under the app's `ScreenColor` , `PaletteColor.strawberry` , as Learn's in
cobalt and Train's in carrot; buttons and links stay cobalt.
- **D4 Settings as Learn's and Train's** (the developer's): a screen over the tabs,
grouped in `SectionCard` s. Sync's card first (as Learn's), the operator's card for
whoever operates the instance, then Todo's own groups (*Tasks*: the week's first day
from the language unless set, resetting sub-tasks; * Reminders * : the automatic reminder
before a timed task, snoozing), the account near the foot (halkyon_app's
`AccountTiles` , `SimpleLoginSection` , `AccountSecuritySection` ), What's new and About
Halkyon Todo last, the version small below them. Groups come as their settings are
2026-11-01 20:25:15 +01:00
built; none is shown empty. **Tasks and Reminders ** (as built, 1 Nov, the developer's
"as much as possible without my input"), kept in the person's sealed settings row
(`todo_settings` , D17), so on every device: * Start the week on * (the language's
unless set; the week strip, the date picker's next week, the filters' this week and
the repeat picker follow it, `firstWeekdayOf` ); * Reset sub-tasks * (on at first, as
Todoist's; off, a recurring task done leaves its sub-tasks done); * Automatic reminder *
(none at first, so that nothing reminds a person who didn't ask; at the time, or 5 to
60 minutes before): a task gaining a time, added so or given one, without reminders of
its own gets it, written into the task where the person sees and may remove it, and a
task that already had a time keeps what the person left it (`TaskRepository` , every
way of giving a time at once); * When snoozed * (15 minutes at first; 5 to 60): each
reminder's notification has a Snooze action, the reminder coming back that much later
under an ID of its own, scheduled from what the notification carries (`snooze.dart` ),
so on Android with the app closed too (`snoozeInBackground` , the plugin's
`ActionBroadcastReceiver` declared), and on Linux by the running app, as its reminders;
2026-11-01 21:51:11 +01:00
the plan of the person's reminders leaves the snoozed ones alone. On iOS, macOS and
Windows since 1 Nov 2026 (the developer asked for it): Apple's systems take a
notification's actions from categories registered as the app starts, so there is one
for each length a snooze can be (`snoozeCategory` , `snoozeCategories` ), its action
in the language the app starts in (the device's, as `resolveAppLocale` picks it; a
language changed meanwhile shows from the next start) and not to the foreground, so
that on iOS the plugin's isolate snoozes with the app closed, as on Android
(`AppDelegate` registers the plugins for it). Windows hands back only the button's
arguments, so the button carries the notification's ID and payload
(`windowsSnoozeArguments` , read by `snoozeOf` as the others' action). macOS and
Windows launch the app for an action while it is closed: the gateway snoozes what
launched it as it starts. Built and tested on Linux alone: none of the three was
tried on a device yet.
2026-11-01 00:50:52 +01:00
- **D5 Todo's rows are sealed, always, as Train's (ADR 0030, D7, D15)**, synced with
`serverpod_offline_sync` : every payload ADR 0017's sealed content under the person's
data key (`content_key` , halkyon_app's `PayloadEnvelope` ), nothing readable in a column,
not a title nor a date. Recommended over readable rows (ADR 0002's default), which would
let the server send reminders by email and a morning overview, and would make shared
projects simpler: what a person plans to do is theirs, the server needs to read none of
it to sync it, and Todo's rows can't be unsealed later without the person's devices. So
**reminders are scheduled on the device ** , as local notifications on each device that
holds the account's keys; there is no email reminder and no overview the server writes.
A project shared later is sealed under a key its members share, which is that step's to
decide.
feat(todo): reminders on the device, Upcoming's week strip, the task pane and search (ADR 0033, D2, D5, D8)
A task with a time takes reminders, minutes before it, in its sealed
payload; each device plans the coming ones whenever the tasks change and
schedules them with the system through flutter_local_notifications
(Android, iOS, macOS, Windows; on Linux the running app shows them), at
the minute where Android allows exact alarms. Notifying is asked for
when a task first gets a reminder; signing out or switching person
cancels them. Upcoming gets its week strip: the month opening
Material's calendar, the weeks before and after, a dot under each day
with tasks; a day picked scrolls the agenda to it, and scrolling moves
the strip along. From expanded windows on, a task opens in a pane
beside the list rather than in a dialog. Search, from every tab's app
bar, finds projects by name and tasks by every word typed, in their
title, description or labels, whatever the case and accents, on the
device alone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-01 12:16:19 +01:00
As built (1 Nov, the developer's choice of `flutter_local_notifications` over
`awesome_notifications` and platform code of Halkyon's own): a task's reminders are
minutes before the time on its date, in its sealed payload, so every device of the
person's reminds; a task with a day alone has none until the automatic reminder (D4)
is a setting. Each device plans its reminders whenever the tasks change, comes to the
front, or every six hours: the next 48 within 30 days (iOS keeps 64 pending), a
recurring task's next time alone, each under an ID hashed from its task and offset so
that planning again replaces it. They are scheduled with the system on Android, iOS,
macOS and Windows, so they come while the app is closed, at their instant: a device
moved to another zone has them moved once the app is opened. Linux schedules none, so
there the running app shows them. Android brings them at the minute only where the
person allows exact alarms (`SCHEDULE_EXACT_ALARM` , off by default since Android 14),
else within a few minutes; the app doesn't send the person to that setting. The
fix(todo): the permission to notify asked wherever a reminder is planned, and Settings saying while it is off (ADR 0033, D31)
On a fresh install Todo's reminders never showed: the permission to notify
(Android 13's POST_NOTIFICATIONS, iOS' and macOS' alert) was asked only
where a task's sheet chose its first reminder, so reminders typed in quick
add, added by the automatic reminder or brought by sync were scheduled and
dropped by the system without a word.
Now NotificationAccess, the device's, reads whether notifications are
allowed whenever the plan holds a reminder and asks where they aren't,
once an app start, so that a refusal isn't met with the dialog again and
again; the task sheet asks through it. While reminders are planned and
notifications are off, Settings' Reminders say so, with Turn on: asked
again, and where that doesn't allow it, the system's notification settings
of the app opened (flutter_local_notifications' own call, nothing added).
Read again whenever the app comes back.
Whats-New: Todo asks to notify as soon as there is a reminder, and Settings › Reminders shows when notifications are off, with Turn on.
Whats-New-De: Todo fragt nach Benachrichtigungen, sobald es eine Erinnerung gibt, und Einstellungen › Erinnerungen zeigt, wenn sie aus sind, mit Einschalten.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-04 22:35:06 +01:00
permission to notify is asked when a task first gets a reminder (both amended by
D31: exact alarms through `USE_EXACT_ALARM` , the permission asked wherever a reminder
is planned); a notification's
feat(todo): reminders on the device, Upcoming's week strip, the task pane and search (ADR 0033, D2, D5, D8)
A task with a time takes reminders, minutes before it, in its sealed
payload; each device plans the coming ones whenever the tasks change and
schedules them with the system through flutter_local_notifications
(Android, iOS, macOS, Windows; on Linux the running app shows them), at
the minute where Android allows exact alarms. Notifying is asked for
when a task first gets a reminder; signing out or switching person
cancels them. Upcoming gets its week strip: the month opening
Material's calendar, the weeks before and after, a dot under each day
with tasks; a day picked scrolls the agenda to it, and scrolling moves
the strip along. From expanded windows on, a task opens in a pane
beside the list rather than in a dialog. Search, from every tab's app
bar, finds projects by name and tasks by every word typed, in their
title, description or labels, whatever the case and accents, on the
device alone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-01 12:16:19 +01:00
text, the task's title, is hidden on a locked Android screen. Signing out, or
switching person, cancels the person's reminders on the device. Tapping one opens the
app, not yet its task.
2026-11-01 00:50:52 +01:00
- **D6 Four tables: `project` , `section` , `task` , `label` ** (recommended, as Train keeps a
workout and its series apart, D14): one row each, changed when edited, the last writer
winning, every payload JSON with a `schemaVersion` , UUID v7 ids. Inbox is a project the
feat(todo): the person's projects, sealed and synced, the Inbox made once (ADR 0033, D5, D6)
TodoPerson opens the person's own sync database in their folder,
halkyon_app's SyncService behind AppCompatibility, their key (TodoKeys,
Train's copied with Todo's own key IDs) and their projects, and closes
them in that order, the key last. A project is sealed for its row; a
readable, foreign or moved row shows locked, and a device without the
account's keys reads and writes nothing. The Inbox is the row under an
ID keyed by the account, so every device makes the same one and the
instance can't tell which it is; it can't be deleted. Settings shows
Sync's card first and the cog its dot while sync fails, as Learn's.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-01 01:05:31 +01:00
app makes once per person, under a row ID keyed by the account (Train's D57,
`keyedRowId` ), not marked in its payload (as built, 1 Nov): every device makes the
same row, so two making it offline write one, and the instance can tell neither
which row is the Inbox nor whose it is; a payload's mark would let a moved row
claim to be it. A **sub-task ** is a task naming its
2026-11-01 00:50:52 +01:00
parent. A task's payload: title, description, its project and section, its parent, its
place among its siblings, a **date ** (a day, or a day and a time with an optional
duration, the time as the person's wall clock with its time zone), a **deadline ** (a
day), a priority (1– 4), its labels' ids, a recurrence (D7), its reminders, when it was
completed. Completing a recurring task moves its date on and writes the occurrence done
into its payload's history, so Reporting can count it. Comments and attachments are a
later step (attachments need sealed files, which no Halkyon app has yet).
- **D7 A recurrence is a structured rule, set through a picker** (recommended, over
parsing what is typed): every n days, weeks (on which weekdays), months (on which day,
or the nth weekday), years, from the date or from completion, until a day or for n
times. Dates and tokens typed into quick add ("tomorrow 6am", "every Monday", `#project` ,
`p1` ), in English and German, are a later step, built on the same rule.
feat(todo): recurring tasks, set through a picker (ADR 0033, D7)
A task's date may recur: every n days, weeks (on which weekdays),
months (on its day, or its nth or last weekday), years, from its date
or from when it is done, until a day or for n times. Done, it moves on
to the next day after both its date and today, its deadline as far, its
time kept, its done sub-tasks open again and when it was done kept in
its payload; undo writes all of it back. The date sheet offers the rules
that fit the task's day and a rule of the person's own; rows show a
recurring date with its own icon, the task sheet the rule in words.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-01 12:15:51 +01:00
As built (1 Nov): the rule is part of the task's date (`Repeat` in `TaskDate` , its
payload's `date.repeat` ), so a task without a date doesn't recur and clearing the date
ends it; schema 1 holds it, as no task was written before. Done, a task counted from its
date moves to the rule's first day after both its date and today (an overdue one isn't
moved onto a day already past), counted from completion it moves the rule's units on
from today, on no particular weekday; its deadline moves as far, its time is kept, its
done sub-tasks open again, and when it was done is added to its `completions` . Undo
writes back every task that changed. The picker offers the rules that fit its day
(every day, every weekday, its weekday, its day or nth weekday of the month, its day of
the year) and a rule of the person's own; weeks start on Monday for every n weeks, until
the week's first day is a setting (D4).
2026-11-01 00:50:52 +01:00
- **D8 The first version** (recommended): Inbox and projects with sections, shown as a
list; sub-tasks; Today (Overdue first, rescheduling them together, then today's); Upcoming
as an agenda with the week strip opening into the month; the task sheet (rows for the
fields set, chips for the others, the sub-tasks); quick add; labels; completed tasks shown
or hidden per view; reminders on the device. **Later ** , each put to the developer when
its turn comes: the board and calendar layouts, Upcoming's 3-day grid, each view's
Display sheet (grouping, sorting, filters), saved filters, Reporting and goals (karma
2026-11-05 15:24:13 +01:00
not before the developer asks for it), location reminders (come with D32), comments and attachments,
2026-11-01 00:50:52 +01:00
typed dates, sharing.
- **D9 Colours that mean something come from halkyon_ui's palette** (recommended): a
project's colour is picked from `PaletteColor` , with its localised name, as Learn's
colours are; priorities and dates take palette colours chosen not to blur against the
strawberry wash, so overdue and P1 are **cerise ** , not strawberry, P2 carrot, P3
cobalt, P4 none; tomorrow and this week as Todoist's meanings go, in palette colours.
Each is checked against the wash in light and dark before it is kept.
- **D10 The icon stays as ADR 0031 drew it** (cobalt through pacific into fern), though
the wash is strawberry: Learn's icon ends in strawberry and washes in cobalt, so an
app's wash and its icon already differ, and Todo's and Meditate's icons are kept
related (ADR 0031, D1). The agent made no recommendation; this is the default the
decision of 1 Nov left in place, to be asked again if the screens look wrong beside it.
2026-11-02 23:09:38 +01:00
Asked again on 2 Nov: changed by ADR 0031, D5, the icon now fading from cobalt
through violet into strawberry, its backdrop.
2026-11-01 00:50:52 +01:00
feat(todo): dialogs on the desktop, the tabs reordered, a task's path and menu (ADR 0033, D11 to D14)
- halkyon_ui: showAdaptiveSheet shows a dialog on Linux, macOS and Windows
at every width (bottom sheets stay on phones and tablets below expanded
windows), so Learn and Train follow; SheetScaffold takes a heading in
the title's place and may leave out its close button.
- Todo: the tabs are Inbox, Today, Upcoming, Browse, Today still opening;
`serverpod start` launches the app on Linux, as it has no web build.
- A task's sheet shows where the task is (its project, an icon once tasks
are above it, and the tasks above it, each opening what it names) and a
⋮ menu in place of its close button: when it was added (its UUID v7),
Duplicate with its sub-tasks, hiding its done sub-tasks (synced in its
payload), Close in the pane, Delete after asking. The description's icon
stays by its first line.
- A row names its project and section at the end of its date's line; every
menu entry has its icon.
- Learn: a studyset's path starts at the Library tab's icon, named by its
tooltip.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-01 16:53:38 +01:00
## Decisions after trying it (1 Nov 2026, the developer's)
The developer used the first build beside Todoist's and asked for these; where an
alternative was put to them, the choice is theirs.
- **D11 Sheets are dialogs on the desktop, at every width** (over the window's size
alone, and over Todo alone): halkyon_ui's `showAdaptiveSheet` shows a centred dialog
(Material's term) on Linux, macOS and Windows however narrow the window, and on phones
and tablets a bottom sheet below expanded windows as before, so Learn and Train follow.
A bottom sheet is a touch surface; rising from the foot of a desktop window it reads
2026-11-01 18:20:58 +01:00
as a phone's screen. In a narrow window the dialog keeps a smaller margin. A sheet
that can't go through it asks `sheetShowsAsDialog` and follows the same rule: Learn's
conversation is a dialog as tall as the window there, nothing to drag. Train's route
import still opens its bottom sheet everywhere, until Train 0.1.1 is out.
feat(todo): dialogs on the desktop, the tabs reordered, a task's path and menu (ADR 0033, D11 to D14)
- halkyon_ui: showAdaptiveSheet shows a dialog on Linux, macOS and Windows
at every width (bottom sheets stay on phones and tablets below expanded
windows), so Learn and Train follow; SheetScaffold takes a heading in
the title's place and may leave out its close button.
- Todo: the tabs are Inbox, Today, Upcoming, Browse, Today still opening;
`serverpod start` launches the app on Linux, as it has no web build.
- A task's sheet shows where the task is (its project, an icon once tasks
are above it, and the tasks above it, each opening what it names) and a
⋮ menu in place of its close button: when it was added (its UUID v7),
Duplicate with its sub-tasks, hiding its done sub-tasks (synced in its
payload), Close in the pane, Delete after asking. The description's icon
stays by its first line.
- A row names its project and section at the end of its date's line; every
menu entry has its icon.
- Learn: a studyset's path starts at the Library tab's icon, named by its
tooltip.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-01 16:53:38 +01:00
- **D12 The tabs are Inbox, Today, Upcoming, Browse; Today still opens** (amends D2's
order, not what opens).
- **D13 A task's sheet** names where the task is on top (its project, an icon once tasks
are above it, then each task above it; the project goes to its list, a task opens in
the sheet's place), as Learn's path through a studyset, and has a ⋮ menu in place of
its close button (`SheetScaffold` 's `heading` and `closeButton` ): when it was added
(from its UUID v7, so the payload needn't say it), Duplicate (with its sub-tasks, right
after it), hiding or showing its done sub-tasks (`hideDoneSubTasks` in its payload,
so every device hides them, schema 1 as nothing is released), Close in the pane, and
Delete after asking. Copy link, View activity, Email to the task, Print and Open in a
new window wait for what they need (deep links, an activity log; mailing in is ruled
out by D5). The description's icon stays by its first line.
- **D14 Rows and menus**: a row shows its project and section ("Inbox / Errands" with the
project's icon) at the end of the line of its date, not beside the whole row; every
feat(todo): tasks chosen together, quick add reading what is typed (ADR 0033, D14, D15)
- A section's heading stays atop a project's list while its tasks
scroll beneath it (slivers, PinnedHeaderSliver).
- Upcoming's month unfolds in place beneath its strip, from its name
or its handle; a button beside the month goes back to today.
- Tasks are chosen together: a row swiped right or held, or Select
tasks in Today's, Upcoming's and a project's menu; a bar of date,
move, labels, priority and more (complete, duplicate, deadline,
delete) takes the navigation bar's place. A row swiped left asks
for its date.
- TaskRepository.move moves tasks with their sub-tasks.
- parseQuickAdd reads #project, @label (an unknown one made),
{deadline}, !!1 and p1 to p4, !30m, dates, times and recurrences
(tomorrow 9am, Mon 18:00, ev Tue 7pm, every 2 days, every weekday),
from a table of words per language: English, and German beside it.
- Each token is highlighted and shown on its chip; a tap keeps it as
text, and a chip picked takes it out of the title.
- Quick add shows its title alone at first, its chips once typing
starts, and the description and deadline behind its + (the
description also from the down arrow).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-01 16:53:59 +01:00
menu entry has its icon, a destructive one in the error colour (`MenuEntry` ). A
section's heading stays atop the list while its tasks scroll beneath it, until the
next pushes it away (`PinnedHeaderSliver` ). Upcoming's month unfolds in place beneath
its strip, from its name or its handle dragged down, the arrows then going by month,
the handle dragged up folding it; a button beside the month goes back to today once
another week shows. Tasks are chosen to act on together, as Todoist's: a row swiped
right or held, or * Select tasks * in Today's, Upcoming's and a project's ⋮, a tap then
feat(todo): tasks dragged into place or under another, the add button dropped where the task goes (ADR 0033, D16)
- A project's row, held, is dragged before or after another, or a
little to the right to become its sub-task, or onto a section's
heading; its sub-tasks go with it, and Order changed offers Undo
(TaskRepository.place). The list scrolls at its edges.
- The add button is dragged onto a project's list to add the task
between two rows, onto a day in Upcoming for that day, onto Today
for today; dropped back on its place, an ×, it gives up.
- Holding a row now drags it; a swipe to the right selects it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-01 17:28:37 +01:00
choosing or letting go (a row held is dragged since D16); the app bar counts them, with an arrow (or back) ending it,
feat(todo): tasks chosen together, quick add reading what is typed (ADR 0033, D14, D15)
- A section's heading stays atop a project's list while its tasks
scroll beneath it (slivers, PinnedHeaderSliver).
- Upcoming's month unfolds in place beneath its strip, from its name
or its handle; a button beside the month goes back to today.
- Tasks are chosen together: a row swiped right or held, or Select
tasks in Today's, Upcoming's and a project's menu; a bar of date,
move, labels, priority and more (complete, duplicate, deadline,
delete) takes the navigation bar's place. A row swiped left asks
for its date.
- TaskRepository.move moves tasks with their sub-tasks.
- parseQuickAdd reads #project, @label (an unknown one made),
{deadline}, !!1 and p1 to p4, !30m, dates, times and recurrences
(tomorrow 9am, Mon 18:00, ev Tue 7pm, every 2 days, every weekday),
from a table of words per language: English, and German beside it.
- Each token is highlighted and shown on its chip; a tap keeps it as
text, and a chip picked takes it out of the title.
- Quick add shows its title alone at first, its chips once typing
starts, and the description and deadline behind its + (the
description also from the down arrow).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-01 16:53:59 +01:00
and a bar takes the navigation bar's place: date, move, labels (those all have
ticked; a tick added to each, one unticked taken off each), priority, and ⋮ with
complete (undone together), duplicate, deadline and delete (asked first). * Assign *
waits for shared projects and * View activity * for a history, neither of which Todo
has. A row swiped left asks for its date. Neither swipe removes the row; a sheet's
sub-tasks and search's results aren't chosen (`TaskSelection` , one for the frame).
- **D15 Typed tokens in quick add** (amends D7's later step; the developer's, 1 Nov, after
feat(todo): dialogs on the desktop, the tabs reordered, a task's path and menu (ADR 0033, D11 to D14)
- halkyon_ui: showAdaptiveSheet shows a dialog on Linux, macOS and Windows
at every width (bottom sheets stay on phones and tablets below expanded
windows), so Learn and Train follow; SheetScaffold takes a heading in
the title's place and may leave out its close button.
- Todo: the tabs are Inbox, Today, Upcoming, Browse, Today still opening;
`serverpod start` launches the app on Linux, as it has no web build.
- A task's sheet shows where the task is (its project, an icon once tasks
are above it, and the tasks above it, each opening what it names) and a
⋮ menu in place of its close button: when it was added (its UUID v7),
Duplicate with its sub-tasks, hiding its done sub-tasks (synced in its
payload), Close in the pane, Delete after asking. The description's icon
stays by its first line.
- A row names its project and section at the end of its date's line; every
menu entry has its icon.
- Learn: a studyset's path starts at the Library tab's icon, named by its
tooltip.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-01 16:53:38 +01:00
asking whether the keyboard's language could choose): `#project` , `@label` ,
`{deadline}` , `!!1` and `p1` to `p4` , `!30m` , dates and times (`9am` , `Mon 18:00` ,
`ev Tue 7pm` ) and recurrences (`every day` , `every 2 days` , `every second day` ), each
highlighted as it is recognised and undone with a tap. The words are data, a table per
language (weekdays, months, * tomorrow * , * every * , * next * , times), read in the app's
language and English, so a new app language adds a table, not a parser. Not the
keyboard's language: Flutter tells nothing of it, Android doesn't reliably, and it
changes as the person switches keyboards while typing. Symbols hold in every language.
feat(todo): tasks chosen together, quick add reading what is typed (ADR 0033, D14, D15)
- A section's heading stays atop a project's list while its tasks
scroll beneath it (slivers, PinnedHeaderSliver).
- Upcoming's month unfolds in place beneath its strip, from its name
or its handle; a button beside the month goes back to today.
- Tasks are chosen together: a row swiped right or held, or Select
tasks in Today's, Upcoming's and a project's menu; a bar of date,
move, labels, priority and more (complete, duplicate, deadline,
delete) takes the navigation bar's place. A row swiped left asks
for its date.
- TaskRepository.move moves tasks with their sub-tasks.
- parseQuickAdd reads #project, @label (an unknown one made),
{deadline}, !!1 and p1 to p4, !30m, dates, times and recurrences
(tomorrow 9am, Mon 18:00, ev Tue 7pm, every 2 days, every weekday),
from a table of words per language: English, and German beside it.
- Each token is highlighted and shown on its chip; a tap keeps it as
text, and a chip picked takes it out of the title.
- Quick add shows its title alone at first, its chips once typing
starts, and the description and deadline behind its + (the
description also from the down arrow).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-01 16:53:59 +01:00
As built (`parseQuickAdd` , `QuickAddWords.en` and `.de` ): a `#` naming no project
stays text, an `@` naming no label makes one as the task is added; only the first of
feat(todo): reminders at a time of their own planned and set in a task's sheet, durations in its date sheet
planReminders plans each reminder at a time of its own of an open task,
whatever its date, under an ID of its own; the notification says
"Reminder" for a task without a time. The task's sheet always has its
reminders' row: those before its time where it has one, those at a time
of their own each with its remove button and "Remind at a time" (a day,
then a time), the permission asked as a first one is chosen. The date
sheet sets a duration once there is a time (none, 15 minutes to 4
hours, as Todoist's), a time cleared taking it with it. Recorded as
ADR 0033, D18 (the developer's, 3 Nov 2026).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-03 13:56:19 +01:00
each kind counts, the rest stays in the title (but for labels and, since D18,
reminders); a time alone is its next to come
feat(todo): tasks chosen together, quick add reading what is typed (ADR 0033, D14, D15)
- A section's heading stays atop a project's list while its tasks
scroll beneath it (slivers, PinnedHeaderSliver).
- Upcoming's month unfolds in place beneath its strip, from its name
or its handle; a button beside the month goes back to today.
- Tasks are chosen together: a row swiped right or held, or Select
tasks in Today's, Upcoming's and a project's menu; a bar of date,
move, labels, priority and more (complete, duplicate, deadline,
delete) takes the navigation bar's place. A row swiped left asks
for its date.
- TaskRepository.move moves tasks with their sub-tasks.
- parseQuickAdd reads #project, @label (an unknown one made),
{deadline}, !!1 and p1 to p4, !30m, dates, times and recurrences
(tomorrow 9am, Mon 18:00, ev Tue 7pm, every 2 days, every weekday),
from a table of words per language: English, and German beside it.
- Each token is highlighted and shown on its chip; a tap keeps it as
text, and a chip picked takes it out of the title.
- Quick add shows its title alone at first, its chips once typing
starts, and the description and deadline behind its + (the
description also from the down arrow).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-01 16:53:59 +01:00
(tomorrow once it has passed today), a weekday alone the coming one (today too),
`next` one after today; a month needs its day (`may` alone is a word), a number alone
is none; a recurrence starts on its first day from today (`every weekday` on a Sunday:
Monday). `!30m` makes a task without a time due 30 minutes from now, reminded then,
and one with a time reminded 30 minutes before it; with a day but no time it is
text. German's weekdays only by their whole names, its short forms (`so` , `do` ,
`die` , `mit` ) being everyday words. Quick add shows its title alone at first, its
chips once typing starts, a chip picked taking its token out of the title, and the
description and the deadline behind its + (the description also from the down
2026-11-01 17:32:52 +01:00
arrow). As `#` or `@` is typed, up to five projects or labels whose name, or a word
of it, starts with what follows are offered below the title, one tapped completing
it; none for a symbol inside a word (`C#` ), nor once a name is typed whole.
feat(todo): tasks dragged into place or under another, the add button dropped where the task goes (ADR 0033, D16)
- A project's row, held, is dragged before or after another, or a
little to the right to become its sub-task, or onto a section's
heading; its sub-tasks go with it, and Order changed offers Undo
(TaskRepository.place). The list scrolls at its edges.
- The add button is dragged onto a project's list to add the task
between two rows, onto a day in Upcoming for that day, onto Today
for today; dropped back on its place, an ×, it gives up.
- Holding a row now drags it; a swipe to the right selects it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-01 17:28:37 +01:00
- **D16 Dragging** (the developer's, 1 Nov, as Todoist's): a row of a project's list
(the Inbox's too), held, is dragged into another place, a line showing where: the
upper half of a row puts it before that row, the lower half after it, and the lower
half reached 48 px to the right of where the drag began makes it that row's last
sub-task; a section's heading takes it first in the section, or last in the group
before from its upper half. It goes with its sub-tasks, and * Order changed * offers
to undo it (`TaskRepository.place` , never under itself or its own sub-task). The
list scrolls at its edges. Holding a row therefore drags it everywhere it can be
dragged and no longer selects it (D14): a swipe to the right does. The add button
is dragged straight away, without a hold, its place meanwhile a larger × where
dropping it gives up: dropped between a project's rows, quick add adds there (each
next task below the one before); on a day in Upcoming, for that day; on Today, for
2026-11-01 17:58:06 +01:00
today. Upcoming's rows, overdue ones too, held and dropped on another day move there,
their time, duration and recurrence kept, * Moved to … * offering to undo it; their
order within a day stays their times' and priorities', and Today's rows aren't
feat(todo): Today laid out as a calendar, planned from its Plan by dragging, as Todoist's desktop Today (ADR 0033, D30)
The developer asked for Todoist's desktop Today planning: a day of hours
and a Plan beside it, tasks dragged from it onto an hour. Today gets a
Display, its layout alone (List or Calendar), kept in the person's sealed
settings row as Upcoming's (`TodayView`, the payload's `today`, schema
still 1). Its Calendar is one day of `HourGrid`; *Plan: n* in its app bar
shows or hides the Plan (`PlanPanel`): Overdue with Reschedule, To plan
(today's without a time) and Time-blocked (today's with one), each
folding with its count, narrowed for the session by *All projects*'
Filter by project (a search and a box a project, a sub-project with its
parents' path). A row dropped on an hour is moved to today at that time,
its duration kept; a block dropped on the Plan loses its time; both undone
from the snack bar, through `moveTask`. From a medium width of Today's own
on the Plan stands beside the day, shown or hidden as remembered; on a
phone it is a sheet over the day's foot (the developer's choice, over the
Plan on wide windows alone and over a strip above the day), part of the
screen rather than a modal route, so that the day behind it takes the
drop, and it lowers to its heading while one of its rows is held.
Whats-New: Today's Display offers a Calendar layout: today's hours, with a Plan of what is overdue, to plan and time-blocked, whose tasks you drag onto an hour.
Whats-New-De: Die Anzeige von Heute bietet das Layout Kalender: die Stunden des Tages, mit einem Plan aus Überfälligem, Zu planen und Eingeplant, dessen Aufgaben du auf eine Stunde ziehst.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-04 22:39:38 +01:00
dragged (its Plan's are since D30).
feat(todo): each project's Display: completed tasks, grouping, sorting and filters, synced (ADR 0033, D17)
- A Display button beside a project's menu (the Inbox's too), a dot
on it while the project shows otherwise than at first, opens its
sheet: completed tasks, grouping (date, deadline, added date,
priority, label), sorting (name, date, deadline, added date,
priority), filters by date, deadline, priority and label, and
Reset all.
- The choices live in the project's sealed payload (ProjectView,
written only once set), so every device shows the project alike.
- Grouped, the groups stand in the sections' place under pinned
headings; rows are dragged only while sorted by hand by sections.
- Show done tasks moves from the menu into Display.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-01 18:41:12 +01:00
- **D17 Each project's Display, synced** (the developer's, 1 Nov, as Todoist's; D8's
later step): a project's screen, the Inbox's too, has a Display button beside its ⋮, a
dot on it while anything differs from how a project shows at first. Its sheet sets
completed tasks (moved out of the ⋮), grouping (none, which keeps the sections; date,
deadline, added date, priority, label), sorting (manual, name, date, deadline, added
date, priority; a tie, or a task without what is sorted by, last and by hand), and
filters by date and deadline (all, today, this week from the language's first
weekday, next 7 days, this month, next 30 days, none) and by priority and label (any
ticked). Each change is written at once; Reset all puts the project back. Grouped,
the groups stand in the sections' place under headings that stay atop the list
(overdue first, then each day, none last; P1 first; a task under each of its labels);
rows are dragged (D16) only while sorted by hand and grouped by sections. The choices
are kept **in the project's sealed row ** , so every device of the person's shows the
project alike (`ProjectView` , the payload's `view` , written only once set; values a
version doesn't know read as at first). The schema stays 1: an older version reads
such a project, and renaming it there drops its Display, which beats a newer schema
feat(todo): Today laid out as a calendar, planned from its Plan by dragging, as Todoist's desktop Today (ADR 0033, D30)
The developer asked for Todoist's desktop Today planning: a day of hours
and a Plan beside it, tasks dragged from it onto an hour. Today gets a
Display, its layout alone (List or Calendar), kept in the person's sealed
settings row as Upcoming's (`TodayView`, the payload's `today`, schema
still 1). Its Calendar is one day of `HourGrid`; *Plan: n* in its app bar
shows or hides the Plan (`PlanPanel`): Overdue with Reschedule, To plan
(today's without a time) and Time-blocked (today's with one), each
folding with its count, narrowed for the session by *All projects*'
Filter by project (a search and a box a project, a sub-project with its
parents' path). A row dropped on an hour is moved to today at that time,
its duration kept; a block dropped on the Plan loses its time; both undone
from the snack bar, through `moveTask`. From a medium width of Today's own
on the Plan stands beside the day, shown or hidden as remembered; on a
phone it is a sheet over the day's foot (the developer's choice, over the
Plan on wide windows alone and over a strip above the day), part of the
screen rather than a modal route, so that the day behind it takes the
drop, and it lowers to its heading while one of its rows is held.
Whats-New: Today's Display offers a Calendar layout: today's hours, with a Plan of what is overdue, to plan and time-blocked, whose tasks you drag onto an hour.
Whats-New-De: Die Anzeige von Heute bietet das Layout Kalender: die Stunden des Tages, mit einem Plan aus Überfälligem, Zu planen und Eingeplant, dessen Aufgaben du auf eine Stunde ziehst.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-04 22:39:38 +01:00
it couldn't read at all. Upcoming's Display (Today had none until D30) is in
feat(todo): a project's Calendar, Upcoming's Display and its 3-day grid (ADR 0033, D17)
A project's Display gains the Calendar layout: Upcoming's week strip
and agenda, now shared as DayAgenda, over the project's tasks it keeps,
with No date beside the month and Future occurrences, each repeating
task also on the days it will come back (calendarOf on Repeat.next),
muted, neither ticked off nor dragged.
Upcoming gets a Display of its own, the same sheet over another target
(DisplayTarget): Agenda or 3-day, done tasks, future occurrences,
sorting, the filters by deadline, priority and label, and in the grid
Colouring by priority, label or project. It is kept in the person's
settings, one sealed row under an ID keyed by the account in the new
synced table todo_settings (migration settings: the table, its index
on spaceId and its foreign key to the space, cascading on delete).
The 3-day grid shows today and the two days after, stepped or swiped
three at a time: tasks without a time all day, timed ones as blocks as
long as they last, overlapping ones side by side (layOutDay). A long
press on a half hour adds a task there for half an hour; Overdue opens
the overdue tasks with Reschedule.
The board's separator builder names its BuildContext, as the root's
analyzer plugin asks (never_discard_build_context).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-01 20:01:00 +01:00
a sealed person-settings row (`todo_settings` , below), which is also to hold D4's
settings. **The Board ** (as built, 1 Nov), the Display's Layout (List,
2026-11-01 18:51:15 +01:00
Board): a column for the tasks in no section, * (No section) * , always first, then one
a section with its menu, each heading counting its open tasks, each task a card,
each column ending with Add task, a last column adding a section; grouped otherwise,
a column a group, nothing dragged. Below a medium width (the board's own, not the
window's, so the pane beside the tabs counts) one column at a time, paged, the next
peeking in, settling on the nearest when let go, dots below saying where; from it on,
columns 320 wide side by side, scrolled across. Cards are dragged as rows (D16),
within and between columns, every scrollable they are in scrolling at its edges; a
feat(todo): a project's Calendar, Upcoming's Display and its 3-day grid (ADR 0033, D17)
A project's Display gains the Calendar layout: Upcoming's week strip
and agenda, now shared as DayAgenda, over the project's tasks it keeps,
with No date beside the month and Future occurrences, each repeating
task also on the days it will come back (calendarOf on Repeat.next),
muted, neither ticked off nor dragged.
Upcoming gets a Display of its own, the same sheet over another target
(DisplayTarget): Agenda or 3-day, done tasks, future occurrences,
sorting, the filters by deadline, priority and label, and in the grid
Colouring by priority, label or project. It is kept in the person's
settings, one sealed row under an ID keyed by the account in the new
synced table todo_settings (migration settings: the table, its index
on spaceId and its foreign key to the space, cascading on delete).
The 3-day grid shows today and the two days after, stepped or swiped
three at a time: tasks without a time all day, timed ones as blocks as
long as they last, overlapping ones side by side (layOutDay). A long
press on a half hour adds a task there for half an hour; Overdue opens
the overdue tasks with Reschedule.
The board's separator builder names its BuildContext, as the root's
analyzer plugin asks (never_discard_build_context).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-01 20:01:00 +01:00
column takes a card or the add button dropped below its cards, last in it. **The
Calendar** (as built, 1 Nov, the developer's choices): Upcoming's week strip and agenda,
shared (`DayAgenda` ), over the project's tasks its Display keeps, sub-tasks too, a
day's tasks by time or, sorted otherwise, in that order; * No date * beside the month
opens those without one. Grouping is left out of its Display, and *Future
occurrences* (off at first, kept with the rest) shows each repeating task not done on
the days it will come back, the first as if done today (`Repeat.next` ), until its
recurrence ends, 400 at most: muted rows with the recurrence's mark in place of the
2026-11-01 21:33:51 +01:00
check, opening the task, neither ticked off nor dragged (`calendarOf` ). **The month **
(built 1 Nov 2026, the developer asked for it): from 720 pixels of the Calendar's own
width on (seven days of a little over a hundred each, a desktop's window beside its
rail; below it the agenda, as on a phone), the month's weeks from the week's first
day (`monthWeeks` ), the days of the months beside it muted, each day with as many of
its tasks as fit, a line each (its time, its title, its mark in its priority's
colour), and the rest counted (*3 more*), opening all of the day's in a sheet. Past
days show their tasks too, overdue or done (`monthOf` ; the agenda counts those as
overdue, a month has no such place), later times of repeating ones from today on.
The month's name, back to today, * No date * , the month before and after above it. A
task held and dropped on another day moves there as on Upcoming's (`DayDropArea` ,
`moveTask` ), a day's free space tapped opens quick add on it in the project (where
the agenda has the add button, which can still be dropped on a day), a line tapped
opens its task. The 3-day grid stays Upcoming's. **Upcoming's Display ** (as built, 1 Nov, the developer's
feat(todo): a project's Calendar, Upcoming's Display and its 3-day grid (ADR 0033, D17)
A project's Display gains the Calendar layout: Upcoming's week strip
and agenda, now shared as DayAgenda, over the project's tasks it keeps,
with No date beside the month and Future occurrences, each repeating
task also on the days it will come back (calendarOf on Repeat.next),
muted, neither ticked off nor dragged.
Upcoming gets a Display of its own, the same sheet over another target
(DisplayTarget): Agenda or 3-day, done tasks, future occurrences,
sorting, the filters by deadline, priority and label, and in the grid
Colouring by priority, label or project. It is kept in the person's
settings, one sealed row under an ID keyed by the account in the new
synced table todo_settings (migration settings: the table, its index
on spaceId and its foreign key to the space, cascading on delete).
The 3-day grid shows today and the two days after, stepped or swiped
three at a time: tasks without a time all day, timed ones as blocks as
long as they last, overlapping ones side by side (layOutDay). A long
press on a half hour adds a task there for half an hour; Overdue opens
the overdue tasks with Reschedule.
The board's separator builder names its BuildContext, as the root's
analyzer plugin asks (never_discard_build_context).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-01 20:01:00 +01:00
"as much as possible without my input"): the same sheet over another target
(`DisplayTarget` : `projectDisplay` , `upcomingDisplay` ), its layout Agenda or 3-day,
done tasks, future occurrences, sorting (Smart, by time then priority, as Todoist
calls it; name, deadline, added date, priority) and the filters by deadline, priority
and label; no grouping nor date filter, Upcoming being days already. In the 3-day
grid, Colouring: by priority, by the first label with a colour, or by project. It is
kept in * * `todo_settings` **, a synced table of the same shape as `label` (one row a
person, under an ID keyed by the account as the Inbox's, `todo/settings` , sealed for
its table and row; `SettingsRepository` ), the migration `settings` creating the table,
its index on `spaceId` and its foreign key to the space, cascading on delete, so a
person's deletion takes it with their space. None kept, or one that doesn't open, reads
as at first. **The 3-day grid ** (`ThreeDayGrid` ): today and the two days after (never
before today), stepped or swiped three days at a time; tasks without a time * All day *
above, timed ones as blocks as long as they last (half an hour at least, to midnight
at most), overlapping ones side by side in lanes (`layOutDay` ); a long press on a free
half hour marks it and opens quick add there for half an hour; a block opens its task;
2026-11-01 20:58:12 +01:00
* Overdue * opens a sheet of the overdue tasks with Reschedule. A block held and
dragged (built 1 Nov 2026) moves its task to another of the three days and another
time, its top where it is dropped snapping to a quarter hour (the long press's half
hour is too coarse to move a block a little), the time it would take shown under the
pointer, its duration, recurrence and reminders kept; dropped on a day's * All day * ,
it loses its time and duration. Either can be undone for a moment, as a row dropped
on Upcoming's day (`moveTask` , shared with `DayDropArea` ). A later time of a
repeating task doesn't move: only the task itself, at its next time, does. Dragging
2026-11-01 21:33:51 +01:00
past the three days shown waits until someone misses it.
feat(todo): reminders at a time of their own planned and set in a task's sheet, durations in its date sheet
planReminders plans each reminder at a time of its own of an open task,
whatever its date, under an ID of its own; the notification says
"Reminder" for a task without a time. The task's sheet always has its
reminders' row: those before its time where it has one, those at a time
of their own each with its remove button and "Remind at a time" (a day,
then a time), the permission asked as a first one is chosen. The date
sheet sets a duration once there is a time (none, 15 minutes to 4
hours, as Todoist's), a time cleared taking it with it. Recorded as
ADR 0033, D18 (the developer's, 3 Nov 2026).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-03 13:56:19 +01:00
- **D18 Quick add as Todoist's newsletter writes it** (the developer's, 3 Nov 2026, the
four recommended, after comparing a newsletter's examples): *Check work emails every
weekday at 9am !30m*, * Prepare document on Monday at 10am for 2h * , *Stop watching that
viral cat video !1pm !2pm !3pm !4pm*, * File my tax return every year on 1st March * .
- **`on` before a day** is taken with it (German `am` , `on` and `am` being words of the
tables, `QuickAddWords.on` ), as `at` (`um` ) before a time is; before anything that is
no day it stays text (*Work on report*, * Treffen am Bahnhof * ). `am` after a number is
still the morning (*9 am*), read first as a time.
- **Durations**: `for` (`für` ) and an amount with its unit, `for 2h` , `for 30m` , `for
1h30m`, ` for 90 min`, ` for 2 hours`, ` für 2 Std`, ` für 30 Min`, ` für 1,5 Std.`, a
decimal point or comma either way, a day at most, into `TaskDate.duration` . The
units are the tables' (`durationUnits` ), read by a pattern over the text, as a word
splits at a comma. **Only with a time ** : a duration lasts from one, so without one
(*Read for 2h*, * Read tomorrow for 2h * ) it stays text, as `!30m` on a day without a
time does (recommended, over giving the task a time to hold it, which would invent
one). The date sheet sets it once there is a time (*Duration*: none, 15, 30, 45
minutes, 1, 1½, 2, 3, 4 hours, as Todoist's time sheet offers), a time cleared taking
it with it; a row and the sheet show the span (*Tomorrow 14:00– 15:30*) and the 3-day
grid a block as long, a block dragged keeping it (D17).
- **Every `!30m` counts**, each once, sorted (`[30, 60]` ), as do the `@labels` ; the
other kinds still count once. With a time, each is so long before it; without a date,
the task is due at the latest and each reminds that long from now (*Leave !30m !1h*:
due in an hour, reminded in 30 minutes and then); on a day without a time they stay
text. The title's highlighting marks each token, whatever its kind.
- **Reminders at a time of their own**: `!1pm` , `!13:00` , `!tomorrow 9am` , `!Monday
8:30`, ` !morgen um 9 Uhr`, as many as typed, into a task's ` reminderTimes`
(`ReminderTime` : a day and minutes after midnight, the wall clock without a zone,
as `TaskDate` 's, so one comes at that hour wherever the device is). **A time without
a day** is on the task's day where it has one (a recurrence's first), else today, or
tomorrow where that time has passed, as a time alone is (D15). Each is a moment of
its own: it neither needs a date nor follows one, a recurring task's coming once,
not every time. A `!` before no time (*!tomorrow*, * !9 * ) stays text. Planned with the
rest (`planReminders` , an ID of their own each), the notification saying *Due at
14:00* for a task with a time, * Reminder * for one without; the task's sheet now
always has its reminders' row, the reminders before its time only where it has
one, and those at a time of their own each with its ✕ and * Remind at a time * (a day,
then a time), the permission asked as a first one is chosen.
- **The payload's schema stays 1** (as D17's view): `reminderTimes` , as `[{"day":
"2026-11-03", "time": 780}]`, left out where there are none, so older payloads read
with none. And from now on **a task's payload keeps what this version doesn't know **
(`TaskContent.unknown` , the fields at its top that it doesn't read, written back
beneath the ones it does, which win): Todo isn't released yet, so every version out
keeps a newer one's fields as it changes a task, where Learn's and Train's payloads
drop them. Recommended over bumping the schema for each field, which would show a
task locked on every device not yet updated. A field a newer version adds within the
date, or one whose meaning hangs on fields an older one changes, isn't kept so; such
a change still bumps the schema. No server model changes: the payload is sealed.
feat(todo): dialogs on the desktop, the tabs reordered, a task's path and menu (ADR 0033, D11 to D14)
- halkyon_ui: showAdaptiveSheet shows a dialog on Linux, macOS and Windows
at every width (bottom sheets stay on phones and tablets below expanded
windows), so Learn and Train follow; SheetScaffold takes a heading in
the title's place and may leave out its close button.
- Todo: the tabs are Inbox, Today, Upcoming, Browse, Today still opening;
`serverpod start` launches the app on Linux, as it has no web build.
- A task's sheet shows where the task is (its project, an icon once tasks
are above it, and the tasks above it, each opening what it names) and a
⋮ menu in place of its close button: when it was added (its UUID v7),
Duplicate with its sub-tasks, hiding its done sub-tasks (synced in its
payload), Close in the pane, Delete after asking. The description's icon
stays by its first line.
- A row names its project and section at the end of its date's line; every
menu entry has its icon.
- Learn: a studyset's path starts at the Library tab's icon, named by its
tooltip.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-01 16:53:38 +01:00
2026-11-03 22:03:07 +01:00
## Decisions after trying 0.1.0 on a phone (3 Nov 2026, the developer's)
The developer's review of 3 Nov 2026, with Todoist's screens beside Todo's; the project
label at a row's end the developer's answer (right-aligned, as Todoist's, over left after
the date), the rest the developer's asks, carried out as described.
- **D19 Today and its tabs' icons**: Today's tab shows today's day of the month inside a
blank calendar, as Lucide's `calendar-1` holds a 1, drawn again at midnight
(`TodayIcon` ); Browse is Lucide's `menu` , Display's button `square-menu` . **Today has at
most two groups**: what is overdue, folding under its heading (its chevron, beside
Reschedule; the fold not kept), and today's, headed by the day alone. **A day's heading **
reads as Todoist's, "Tuesday, 3 Nov" ("Dienstag, 3. Nov.", the pattern a text of each
language, `dayHeadingPattern` ); Upcoming's days keep "· Tomorrow" where it helps, Today's
has no "· Today". Upcoming's way back to today shows whenever another day than today is
picked, not only off this week.
- **D20 Rows in rounded boxes, a list's rows a group** (as Todoist's): each row its own
surface (`TaskTile` 's box, `TaskRowPosition` , `taskGroup` ), rounded at the group's ends
and slightly at the joins, so a chosen row's colour stays inside it as the list scrolls
under a pinned heading (it showed under Upcoming's week strip, as the row's tile was
painted on the screen's surface below the list). Its project and section end the line
of its date at the row's right edge.
- **D21 Lists pulled down sync now**, as Learn's: Today, Upcoming's agenda, the Inbox and
every project (`TaskRefresh` , halkyon_ui's `AppRefreshIndicator` over halkyon_app's
`syncAndWait` ), an empty list too (`PullRoom` ). A board's columns and the 3-day grid
don't, as they scroll sideways or both ways. **A project's screens wash in its own
colour**, as a studyset's in Learn; one without keeps Todo's strawberry.
- **D22 Display as Todoist's**: each layout a phone's outline with its picture, its name
below on one line, a radio button marking the one in use; the rest in rounded groups
under plain headings. "Add section" in place of "New section".
- **D23 Quick add a small bar docked above the keyboard** (amends D8, D15): the title,
then one row of boxed buttons (+ for the description and deadline, the date, the
priority, the labels, the project), each its icon alone until something is set, and a
send button, the list in view behind a lighter barrier. Its buttons show from the
start (D15 had its chips come with the first letter), as Todoist's bar has them. Where
sheets are dialogs, the same bar in a dialog.
- **D24 A task's sheet partway up, its parts in rounded boxes** (amends D13): on a phone
it opens as high as its content, at most 65 % of the window, and is dragged up to the
whole window or down to close (`DraggableScrollableSheet` ); the title, description,
date and reminders each a box, the priority, labels, deadline and project a row of
chips (named while unset), the sub-tasks a box ending in Add sub-task. No principle of
the project's argues against Todoist's layout: grouped rounded boxes are what the
review asks for across the apps. Todo keeps no comments, so there is no comment field.
- **D25 Explanations one tap away**: what a setting does stands behind halkyon_ui's
`HelpButton` beside its name (`TitleWithHelp` ), not always below it, in Todo and
Meditate; what is the person's own (a value) stays in view, as do an empty list's
message and quick add's hint while words are highlighted.
2026-11-04 20:47:59 +01:00
## Decisions after trying 0.2.0 (4 Nov 2026, the developer's)
The developer's review of 4 Nov 2026, again beside Todoist's screens.
- **D26 What the review fixed and added**: a time or duration picked in the date sheet
counts however the sheet closes (it had counted only with a day tapped), on the task's
day, or today where it had none, as Todoist's; editing a project keeps its Display.
**Today counts its tasks ** under its title, overdue and today's together ("14 tasks",
as Todoist's), not while tasks are chosen. **A row's check stands by its title's first
line**, not in the middle of the row: a `ListTile` centres its leading or tops it at
its padding, never at the title's line, so the check and the texts share its title,
the texts lowered by half of what the 48 dp tap target is taller than a line.
**A sub-task opens in its task's sheet ** , as the sheet's path did, not in a sheet over
it; the sheet keeps what it showed, the system's back returning to the last (agent's
choice: a `BackButtonListener` , over a `PopScope` , which would also have held back a
tap beside the sheet, a swipe and Escape, which close it whole; a picker over the
sheet is closed by back first).
- **D27 Listed in F-Droid as * Task * and * Calendar & Agenda * ** (the developer's, both,
over * Time * , which F-Droid's categories give to clocks and timers): no other app of
the repository used * Time * , so the repository no longer lists it.
feat(todo): a project's editor as Todoist's Add project, with sub-projects and favourites
Adding or editing a project was a dialog of a name and a colour. It is
now a screen of its own on a phone (a dialog where sheets are dialogs),
as Todoist's Add project: the name counted to 120, a description, the
colour, the parent project, a Favourite switch and the layout, Display's
pictures. All of it lives in the project's sealed payload, the schema
still 1, no server model or migration; the payload now keeps fields it
doesn't know, as a task's does.
A new project starts with the layout picked, and Display's Reset all
returns to that default rather than to List. Browse lists sub-projects
indented and folding under their parents, and the favourites in a group
above; a project is never offered as its own ancestor, and a parent's
deletion takes its sub-projects, naming them first, as Todoist's. ADR
0033, D28.
Whats-New: Add project and Edit project are a screen of their own, with a description, a parent project, Favourite and the layout.
Whats-New: Browse lists sub-projects under their parents and your favourite projects at the top.
Whats-New: Reset all in Display returns to the layout the project was made with.
Whats-New-De: „Projekt hinzufügen“ und „Projekt bearbeiten“ sind eine eigene Seite, mit Beschreibung, übergeordnetem Projekt, Favorit und Layout.
Whats-New-De: „Übersicht“ zeigt Unterprojekte unter ihren Projekten und deine Favoriten oben.
Whats-New-De: „Alles zurücksetzen“ in der Anzeige kehrt zum Layout zurück, mit dem das Projekt angelegt wurde.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-04 20:56:34 +01:00
- **D28 A project's editor as Todoist's Add project, with sub-projects and
favourites** (the developer's, over the dialog of a name and a colour): a screen of its
own on a phone (*Add project*, * Edit project * , ← and ✓ in its app bar), the same in a
dialog where sheets are dialogs; the name counted to 120, a description, then rounded
rows: the colour (its palette name, opening the picker), the parent project (*No
parent* or a project, from a sheet listing them indented; never the project itself
nor any project below it, `parentChoices` , so none becomes its own ancestor), a
Favourite switch; then the Layout, Display's pictures (`LayoutPicker` ). All of it is
**in the project's sealed payload ** (`description` , `parentId` , `favorite` ,
`defaultView` ), no server model changed, no migration, the schema still 1; the payload
now keeps the fields it doesn't know, as a task's (D18), so a later version's survive
an edit here (a 0.2.0 device editing a project still drops these). **The default
Display**: a new project starts with the layout picked, and Display's * Reset all *
returns to it (`defaultView` , the dot on Display marking a Display that differs from
it); the editor sets its layout, and a layout picked anew there shows at once.
Projects made before keep List as their default. **Browse ** lists each project's
sub-projects indented under it, folding under its chevron (the fold not kept, agent's
choice: a choice of the device, and quick to make again), and the favourite projects
in a * Favourites * group above the projects. A project whose parent is gone or doesn't
open here, or projects whose parents run in a circle (two devices each moving one
under the other), stand as projects of their own, none lost (`projectTree` ). **Deleting
a project deletes its sub-projects**, with their sections and tasks, as Todoist does,
the confirmation naming them first (`TodoPerson.deleteProject` ). * Alternatives: * the
sub-projects moved up a level (kept, but surprising after a confirmation that named
only the parent); a depth limit as Todoist's (four levels), left until deep trees turn
out to be a problem.
feat(todo): quick add keeps a word as text where it stands, and from Backspace (ADR 0033, D29)
A word tapped was kept as text by its text, so in "Watch The Daily Show
Daily" tapping the first "Daily" also stopped the second one making the
task repeat (the developer's example). Kept words are now ranges of the
title that follow its edits (`shiftKept`: ranges after the edit move by
its length, one it touches is dropped), and the parser leaves a token
overlapping one of them as text, wherever else the same word stands.
Backspace right after a highlighted word keeps it as text instead of
deleting its last character, as Todoist's; the next Backspace deletes.
It is an input formatter, so the keyboard is given the text back. The
hint under the title says so.
Whats-New: In Add task, only the word you tap stays text; the same word elsewhere still counts.
Whats-New: In Add task, Backspace right after a highlighted word keeps it as text.
Whats-New-De: In „Aufgabe hinzufügen“ bleibt nur das angetippte Wort Text; dasselbe Wort an anderer Stelle zählt weiter.
Whats-New-De: In „Aufgabe hinzufügen“ behält die Rücktaste direkt nach einem hervorgehobenen Wort es als Text.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-04 22:23:03 +01:00
- **D29 Quick add keeps a word as text where it stands, and from Backspace too**
(amends D15; the developer's, 4 Nov 2026). A word tapped had been kept as text by its
text, so in "Watch The Daily Show Daily" tapping the first * Daily * also stopped the
second one making the task repeat. A word kept is now **that occurrence ** , a range of
the title (`QuickAddRange` , `parseQuickAdd(ignored:)` ; a token overlapping one stays
text), which follows the edits: the edit found from the common start and end of the
text before and after, ranges after it moved, one it touches dropped as its word has
changed (`shiftKept` ); typing right before or after a kept word leaves it kept.
**Backspace right after a highlighted word keeps it as text ** , as Todoist's: the
character isn't deleted, the next Backspace deletes it (a quick add input formatter,
so the keyboard's own text is put back too); the hint under the title says so. And
**a word is highlighted while the keyboard still composes it ** : Android's keyboards
(Gboard with suggestions, Japanese ones) compose a word until a space, and the title
had shown the plain composing text until then; the highlight is drawn with the
composing underline over it. * Alternatives: * keeping by text but only the word
tapped and those before it (still surprising, and lost on reordering); a long press
instead of Backspace (undiscoverable, and the tap already does it).
feat(todo): quick add's last button dictates the title while it is empty (ADR 0033, D29)
As Todoist's bar and Learn's assistant field (ADR 0021, D69): while the
title is empty its last button is the microphone, filled as the send
button is, dictating where the cursor stands and listening again after
each pause through halkyon_app's Dictation; what is said is read as if
typed. It shows a stop button while listening and the send button once
there is text, or alone where nothing recognises speech (Linux). A
phone without a recogniser is told how to get one.
Todo gets the device's speech input in its dependencies, and declares
the microphone and the recognition service on Android, the usage texts
on iOS and macOS and the audio input entitlement on macOS, as Learn
does.
Whats-New: Add task's last button is a microphone while the task has no name yet: say the task, dates included.
Whats-New-De: Der letzte Knopf von „Aufgabe hinzufügen“ ist ein Mikrofon, solange die Aufgabe noch keinen Namen hat: Sprich die Aufgabe, Daten eingeschlossen.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-04 22:29:37 +01:00
**The last button dictates while the title is empty ** , as Todoist's and as Learn's
assistant field (ADR 0021, D69): the microphone, filled as the send button is,
dictating into the title where its cursor stands and listening again after each pause
(halkyon_app's `Dictation` on the device's recogniser, `TodoDependencies.speech` ), what
is said read as if typed; a stop button in the container colour while it listens;
the send button once there is text, and alone where the device recognises no speech
(Linux). A phone without a recogniser (GrapheneOS without Google's services) is told
how to get one (halkyon_app's `speechNoRecognizer` ). Android declares the microphone
and the recognition service it queries, iOS and macOS the usage texts and macOS the
audio input entitlement, as Learn's. * Alternatives: * a microphone beside the send
button always (one more button in a bar already full, and Todoist's swaps them).
feat(todo): Today laid out as a calendar, planned from its Plan by dragging, as Todoist's desktop Today (ADR 0033, D30)
The developer asked for Todoist's desktop Today planning: a day of hours
and a Plan beside it, tasks dragged from it onto an hour. Today gets a
Display, its layout alone (List or Calendar), kept in the person's sealed
settings row as Upcoming's (`TodayView`, the payload's `today`, schema
still 1). Its Calendar is one day of `HourGrid`; *Plan: n* in its app bar
shows or hides the Plan (`PlanPanel`): Overdue with Reschedule, To plan
(today's without a time) and Time-blocked (today's with one), each
folding with its count, narrowed for the session by *All projects*'
Filter by project (a search and a box a project, a sub-project with its
parents' path). A row dropped on an hour is moved to today at that time,
its duration kept; a block dropped on the Plan loses its time; both undone
from the snack bar, through `moveTask`. From a medium width of Today's own
on the Plan stands beside the day, shown or hidden as remembered; on a
phone it is a sheet over the day's foot (the developer's choice, over the
Plan on wide windows alone and over a strip above the day), part of the
screen rather than a modal route, so that the day behind it takes the
drop, and it lowers to its heading while one of its rows is held.
Whats-New: Today's Display offers a Calendar layout: today's hours, with a Plan of what is overdue, to plan and time-blocked, whose tasks you drag onto an hour.
Whats-New-De: Die Anzeige von Heute bietet das Layout Kalender: die Stunden des Tages, mit einem Plan aus Überfälligem, Zu planen und Eingeplant, dessen Aufgaben du auf eine Stunde ziehst.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-04 22:39:38 +01:00
- **D30 Today planned on a calendar, as Todoist's desktop Today** (the developer's, 4
Nov 2026, from their screenshots of Todoist's desktop app): Today gets a Display
(amends D17's "Today has none"), its **Layout ** alone, * List * or * Calendar * , with
Display's pictures (`LayoutPicker` ; no Board, a day having no sections), kept in the
person's sealed settings row as Upcoming's (`TodayView` in `todo_settings` , the
payload's `today` , schema still 1: an older version reads it, and writing its settings
there drops it, as D17 accepted for a project's view). * Calendar * is **one day of
hours**, Upcoming's 3-day grid generalised to any number of days (`HourGrid` , D17's
`ThreeDayGrid` renamed, `days: 1` ): today's tasks without a time * All day * on top,
timed ones as blocks as long as they last, overlapping ones side by side, a **line at
the time it is now** (its time in cerise in the hours' column, drawn again each
minute; Upcoming's grid has it too), a block dragged to another hour moving there
(snapping to a quarter hour), onto * All day * losing its time, a long press adding a
task, as D17. With one day there are no day names, arrows nor * Overdue * button: what
is overdue waits in the Plan. The day scrolls to the hour before now at first.
**The Plan ** (`PlanPanel` ): * Plan: 3 * in Today's app bar, in the Calendar layout
alone, shows or hides it, counting what it holds. Its groups, each folding under its
heading with its count (the fold not kept): * Overdue * with Reschedule (Today's, in
cerise as Todoist's), * To plan * (today's tasks without a time; the developer's
working name, Todoist naming no such group), * Time-blocked * (today's with a time,
Todoist's word; German * Zu planen * and * Eingeplant * ); done tasks and later times of
repeating ones are never in it, nor in the day, so neither is planned. *All
projects* opens * Filter by project * , a field searching the projects' names and a box
to tick for each (the Inbox first, a sub-project with its parents' path, "Life /
Home", D28), several at once, applied as ticked; it narrows the Plan alone, not the
day, and is kept while the app runs, not synced (agent's choice: a filter for one
planning session). A row held and dropped on an hour is moved to **today at that
time** (its top where the pointer is, snapped as a block's), its duration kept or,
without one, half an hour drawn; a block dropped on the Plan loses its time and stays
today; both through `moveTask` , * Moved to … * offering to undo it; a Plan's row dropped
back on the Plan changes nothing. **Where it stands ** : from a medium width of
Today's own on (the window-size classes over the body's width, as the board's D17,
so the task pane beside the tabs counts), a panel 340 wide beside the day, shown or
hidden as the person last left it, remembered with the settings (`TodayView.plan` ,
open at first); **on a phone, a sheet over the day's foot ** (the developer's choice,
over the Plan on wide windows alone and over a strip of tasks above the day), half
feat(learn,todo,train,meditate,ui): the navigation bar flush with the foot, a fade above it
Below medium windows the frame's bar is Material 3 Expressive's again,
flush with the window's foot (decided 7 Nov 2026 by the developer): the
theme's NavigationBar across the whole width, 64 dp in its own colour,
running on beneath the system's bar as Material's does. Right above it a
fade 32 dp high, from the background of the screen in view (the
ScreenTint's, the theme's surface before a screen claims one) made
see-through to that background, so what scrolls on beneath the bar fades
out rather than being cut off; taps pass through it. The rail of wider
windows stays as it was.
halkyon_ui's FloatingNavigationBar becomes FlushNavigationBar, its side
margin, lift and rounded card gone. Its coverOf is now the whole room
content leaves at its end, the system's bar included (the body's bottom
padding: bar, fade and system's bar inside the frame, the system's bar
alone outside it), and floating buttons rise by buttonLiftOf, the same
without the system's bar, which a Scaffold keeps them clear of itself.
Lists, Todo's hour grid and the Plan's sheet keep using coverOf; the
Profile's list no longer adds the system's bar a second time.
Whats-New: The navigation bar sits at the bottom edge again, with a soft fade above it.
Whats-New-De: Die Navigationsleiste sitzt wieder am unteren Rand, mit einem sanften Übergang darüber.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-08 00:04:18 +01:00
the body high above the navigation bar, closed until asked and while the app runs;
feat(todo): Today laid out as a calendar, planned from its Plan by dragging, as Todoist's desktop Today (ADR 0033, D30)
The developer asked for Todoist's desktop Today planning: a day of hours
and a Plan beside it, tasks dragged from it onto an hour. Today gets a
Display, its layout alone (List or Calendar), kept in the person's sealed
settings row as Upcoming's (`TodayView`, the payload's `today`, schema
still 1). Its Calendar is one day of `HourGrid`; *Plan: n* in its app bar
shows or hides the Plan (`PlanPanel`): Overdue with Reschedule, To plan
(today's without a time) and Time-blocked (today's with one), each
folding with its count, narrowed for the session by *All projects*'
Filter by project (a search and a box a project, a sub-project with its
parents' path). A row dropped on an hour is moved to today at that time,
its duration kept; a block dropped on the Plan loses its time; both undone
from the snack bar, through `moveTask`. From a medium width of Today's own
on the Plan stands beside the day, shown or hidden as remembered; on a
phone it is a sheet over the day's foot (the developer's choice, over the
Plan on wide windows alone and over a strip above the day), part of the
screen rather than a modal route, so that the day behind it takes the
drop, and it lowers to its heading while one of its rows is held.
Whats-New: Today's Display offers a Calendar layout: today's hours, with a Plan of what is overdue, to plan and time-blocked, whose tasks you drag onto an hour.
Whats-New-De: Die Anzeige von Heute bietet das Layout Kalender: die Stunden des Tages, mit einem Plan aus Überfälligem, Zu planen und Eingeplant, dessen Aufgaben du auf eine Stunde ziehst.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-04 22:39:38 +01:00
a row held there **lowers the sheet to its heading ** , the day free behind it, and it
rises again once the row is dropped. The sheet is part of Today's screen, not a
modal route (agent's choice): a modal sheet's barrier would take the drop meant for
the day behind it, and closing the route under a drag ends the row's draggable.
The add button hides while the phone's sheet is open. * Alternatives: * the Plan on
wide windows alone (a phone couldn't plan by dragging), a strip above the day
(too little room for three groups), the project filter synced (a planning
session's narrowing would follow the person to their other devices).
fix(todo): the permission to notify asked wherever a reminder is planned, and Settings saying while it is off (ADR 0033, D31)
On a fresh install Todo's reminders never showed: the permission to notify
(Android 13's POST_NOTIFICATIONS, iOS' and macOS' alert) was asked only
where a task's sheet chose its first reminder, so reminders typed in quick
add, added by the automatic reminder or brought by sync were scheduled and
dropped by the system without a word.
Now NotificationAccess, the device's, reads whether notifications are
allowed whenever the plan holds a reminder and asks where they aren't,
once an app start, so that a refusal isn't met with the dialog again and
again; the task sheet asks through it. While reminders are planned and
notifications are off, Settings' Reminders say so, with Turn on: asked
again, and where that doesn't allow it, the system's notification settings
of the app opened (flutter_local_notifications' own call, nothing added).
Read again whenever the app comes back.
Whats-New: Todo asks to notify as soon as there is a reminder, and Settings › Reminders shows when notifications are off, with Turn on.
Whats-New-De: Todo fragt nach Benachrichtigungen, sobald es eine Erinnerung gibt, und Einstellungen › Erinnerungen zeigt, wenn sie aus sind, mit Einschalten.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-04 22:35:06 +01:00
- **D31 Reminders at the minute, and the permission to notify asked wherever one is
planned** (the developer's, 4 Nov 2026, after Todo's reminders never showed on a
phone, GrapheneOS on Android 14, installed afresh from F-Droid). Two faults: the
permission to notify (Android 13's `POST_NOTIFICATIONS` , iOS' and macOS' alert) was
asked only where a task's sheet chose its first reminder, so reminders typed in quick
add (`!30m` , `!1pm` ), added by the automatic reminder (D4) or brought by sync were
planned and scheduled, and the system dropped every one without a word; and exact
alarms, `SCHEDULE_EXACT_ALARM` , are off from Android 14 on until the person allows
them in * Alarms & reminders * , so they came inexact, minutes late.
- **Asked wherever a reminder is planned**: whenever the plan of the device's
reminders holds one (`ReminderScheduler` ), `NotificationAccess` reads whether the
device may notify (`ReminderGateway.notificationsAllowed` : Android's
`areNotificationsEnabled` , Apple's `checkPermissions` ; Linux and Windows ask
nothing) and where it may not, asks, **once an app start ** , the device's and not the
person's: refused, nobody is asked again and again (Android itself stops showing
its dialog after two refusals, iOS after one). The task sheet's ask goes through the
same place, so the plan's doesn't follow it.
- **Settings › Reminders says * Notifications are off * ** while reminders are planned
and the device may not show them, with * Turn on * : asks again, and where that doesn't
allow it (refused again, or the system asks no more), opens the system's
notification settings of the app (the plugin's `openAppNotificationSettings` , no
package or platform code of Halkyon's added). Read again whenever the app comes
back, so notifications allowed there count at once. (Agent's choice: Turn on asks
first and opens the settings after a refusal, over telling the two apart with
Android's `shouldShowRequestPermissionRationale` , which would have needed a method
channel of the app's own for a case that ends in the settings either way.)
- **Exact alarms through `USE_EXACT_ALARM` ** (the developer's choice), granted with the
install from Android 13 on to an app whose purpose is reminding of what a person
planned (calendars, alarm clocks, reminders), with `SCHEDULE_EXACT_ALARM` still
declared for Android 12 and 12L, which don't know the other, up to API 32 alone
(`test/android/exact_alarms_test.dart` ). `AlarmManager.canScheduleExactAlarms` is
true with either, so the plugin's `canScheduleExactNotifications` is, and reminders
2026-11-04 22:55:38 +01:00
are `exactAllowWhileIdle` , a snoozed reminder too (the developer's, as it had been
inexact while snoozes were minutes long; an idle phone may bring an inexact one later
than the snooze lasts). * Alternatives: * leading the person to *Alarms &
fix(todo): the permission to notify asked wherever a reminder is planned, and Settings saying while it is off (ADR 0033, D31)
On a fresh install Todo's reminders never showed: the permission to notify
(Android 13's POST_NOTIFICATIONS, iOS' and macOS' alert) was asked only
where a task's sheet chose its first reminder, so reminders typed in quick
add, added by the automatic reminder or brought by sync were scheduled and
dropped by the system without a word.
Now NotificationAccess, the device's, reads whether notifications are
allowed whenever the plan holds a reminder and asks where they aren't,
once an app start, so that a refusal isn't met with the dialog again and
again; the task sheet asks through it. While reminders are planned and
notifications are off, Settings' Reminders say so, with Turn on: asked
again, and where that doesn't allow it, the system's notification settings
of the app opened (flutter_local_notifications' own call, nothing added).
Read again whenever the app comes back.
Whats-New: Todo asks to notify as soon as there is a reminder, and Settings › Reminders shows when notifications are off, with Turn on.
Whats-New-De: Todo fragt nach Benachrichtigungen, sobald es eine Erinnerung gibt, und Einstellungen › Erinnerungen zeigt, wenn sie aus sind, mit Einschalten.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-04 22:35:06 +01:00
reminders* (one more permission to ask and explain, which most would never find
again once refused), staying inexact (reminders minutes late, which a reminder at a
time can't be). Google Play reviews `USE_EXACT_ALARM` against that purpose; Todo is
one, and F-Droid asks nothing of it.
2026-11-04 20:47:59 +01:00
2026-11-05 15:24:13 +01:00
- **D32 A task's locations, with reminders on arriving or leaving** (the developer's, 5
Nov 2026): a location says where a task happens; a task may have several, which count
as alternatives, each with its own reminder: none, on arriving, or on leaving.
`TaskLocation` (name, address text, position, optional OSM type and ID, trigger) goes
in the task's sealed payload as `locations` , schema still 1 (D18's unknown fields stay);
recent places are derived from the tasks (`recentLocations` ), not kept.
- *Finding a place.* People don't know addresses, so a search starts from a city or
district and a name (`Lindau EDEKA` , `Aeschach EDEKA` ), and the map is part of
picking, not an extra (the developer's): results list name, then street, district
and city, so that two EDEKAs differ, and show as pins; a pin or a row chooses, and a
tap on the map places a pin by hand. The phone's last known position centres the map
feat(todo): a pin in the map's middle, and what is around it (ADR 0033, D32)
The location picker fills the screen with its map, the search floating
over it. A pin stays in the middle, lifting while the map moves, as
Molly's; where it lands the instance names the address and lists the
places around, as Threema's. That is `nearby` on the search endpoint,
for those signed in, the point neither kept nor logged, counted among
the searches: two lookups, as a plain one finds house numbers alone.
Fixes the map's credit, which overflowed a phone and was pale on white.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-05 23:42:06 +01:00
and biases the search, where it may be read. The map fills the screen, the search
floats over it, and a pin stays in its middle as the map moves beneath (Molly's, the
developer's, 5 Nov 2026, against a tap placing one, which a thumb covers): it lifts
while the map moves and lands where it stops. There the instance is asked what is
around (`nearby` , the point neither kept nor logged, counted among the searches): the
nearest address names the spot, and places to go to are listed below it (Threema's).
Two lookups, as a plain one finds the eight nearest house numbers alone; the second
asks for shops, amenities, tourism and leisure. Further out than zoom 13 the pin stands
feat(todo): the map keeps its size, the search a screen of its own (ADR 0033, D32)
From the developer's trial of the picker. Below a medium width a sheet
of fixed height sits under the map, whatever is around the pin, and is
pulled up over it for the whole list, as Threema's; a landscape phone or
a wider window has it as a pane beside the map. The search opens its own
screen: name, what it is, where it lies and how far. A compass under the
bar turns the map to north, and my position no longer zooms out.
A pinch only zooms unless the fingers turn first, the pin's shadow lies
on the map's middle, and a look around that failed says so, with Try again. It asks
little: after the map rested, one at a time, nothing for a spot answered
or by zooming, and nothing for 30 seconds after a failure, as komoot's
Photon throttled a person moving the map about.
Fixes the wrong map for the theme: OpenFreeMap's styles name themselves
nothing and the tile cache goes by that name, so a theme got the other's
tiles (seen on an emulator, both ways); each now has its own name.
Whats-New: The location map keeps its size, the list of places pulls up over it, and search opens its own screen with kinds and distances.
Whats-New-De: Die Ortskarte behält ihre Größe, die Ortsliste lässt sich über sie ziehen, und die Suche öffnet sich als eigene Seite mit Art und Entfernung.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-06 00:12:01 +01:00
for no spot and nothing is asked. On the developer's trial of the first build
(5 Nov 2026) the map stopped changing size with what is around it: below a medium
width a sheet of fixed height (42 % of the screen, as Threema's) sits under the map and
is pulled up over it for the whole list, the map dimming and a bar coming in above it
(back or a place chosen lowers it); from a medium width on, a landscape phone too, it
is a pane beside the map, Todo's `SidePane` . The search is a screen of its own
(Threema's), as the keyboard left the map no room: name, what it is and where it lies,
the distance on the right. What it is comes from OpenStreetMap's value, named in the
app's language where `locationKind` knows it, nothing otherwise rather than English
tags in a German list, and it is never kept. The address stays beside it, Threema
showing the kind alone but two EDEKAs needing the street. A compass under the bar turns
the map to north, and my position keeps a closer zoom than a place's 15. Two fingers do
what shows first, zooming from 0.15 of a level or a turn of 15 degrees (flutter_map's gesture
race, off by default, turned the map on every pinch; its 0.5 felt like lag), and the pin's shadow lies on the map's middle,
so that the phone's position sits under it. A look around that fails says so, the
spot staying a pinned place, as it reads otherwise as a place with nothing around,
with Try again. It stays automatic, a button being a tap more in the usual case, but
asks little (komoot's public Photon answered 503 to a person moving a map about, then
refused connections, and an instance counts each among a person's 60 searches a
minute): 800 ms after the map stopped, one question at a time and then the latest spot,
nothing for a spot within 25 m or 12 pixels of one answered, the 16 latest answers
being kept, so that zooming asks nothing, and nothing for 30 seconds after a failure. OpenFreeMap's
styles name themselves nothing and vector_map_tiles keeps the tiles it draws on the
device under that name, so each is given its own, or the dark theme got the light
theme's tiles. `PlaceSearchGateway` is the seam:
2026-11-05 14:58:38 +01:00
`TodoDependencies.places` is the instance's search, or `PublicPhotonGateway` to
komoot's public Photon, in a debug build asked for it alone
2026-11-05 15:24:13 +01:00
(`--dart-define=TODO_PUBLIC_PHOTON=true` ), with a generic User-Agent and no personal
2026-11-05 14:58:38 +01:00
data; where the instance has no Photon the picker says search isn't available. Car parks and charging
2026-11-05 15:24:13 +01:00
stations named after a place are left out (seen on `Lindau EDEKA` ). The map is Train's
(`flutter_map` , `vector_map_tiles` , OpenFreeMap's Liberty and Dark), a small copy of
`vectorTiles` , no tile layer in widget tests. Alternatives named: a street-address
form (refused: nobody knows them), the picker without a map (refused by the developer).
- *Quick add.* `+` is a keyword as `#` and `@` : recent places suggested as it is typed,
below them the way into the search with what was typed; a place named before becomes
a token (`parseQuickAdd` 's `locations` ), kept as text by a tap or Backspace as any
other (D29); an unknown `+word` , or a `+` a space follows, stays text. A + menu entry
and a pin button with the name or a count do the same by hand. The task sheet has a
Where box, each place with its ⋮; a task's row shows the name and how many more.
- *Reminders* are on the device, Android alone (`LocationReminderGateway` , a no-op
feat(todo): places are watched by a quiet service, and asked to notify (ADR 0033, D32)
The first walk showed two faults. Android gives a proximity alert of an
app in the background a fix every 30 minutes (17:04:38, then 17:34:43 on
the Pixel), so a short stop goes unseen. And the place did remind, at
17:36:43, but nothing had asked to notify, so Android showed nothing.
A foreground service of the type location now takes its own fixes while
places wait, under the quietest notification there is: as long as
reaching the nearest circle takes at 30 m/s, doubled while the phone
lies still, between 30 s and 5 min. A place with a reminder asks to
notify as any reminder does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-05 18:17:48 +01:00
elsewhere, which offers none), as the instance reads no task, with no Google
services, which GrapheneOS lacks: a foreground service of the type `location`
(`LocationWatchService` ) takes its own fixes while places wait, stopped as none
does. Planned from the open tasks as `ReminderScheduler` plans reminders
2026-11-05 15:24:13 +01:00
(`planLocationWatches` ), cancelled in `TodoPerson.dispose` , kept in the app's private
feat(todo): places are watched by a quiet service, and asked to notify (ADR 0033, D32)
The first walk showed two faults. Android gives a proximity alert of an
app in the background a fix every 30 minutes (17:04:38, then 17:34:43 on
the Pixel), so a short stop goes unseen. And the place did remind, at
17:36:43, but nothing had asked to notify, so Android showed nothing.
A foreground service of the type location now takes its own fixes while
places wait, under the quietest notification there is: as long as
reaching the nearest circle takes at 30 m/s, doubled while the phone
lies still, between 30 s and 5 min. A place with a reminder asks to
notify as any reminder does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-05 18:17:48 +01:00
preferences (titles unsealed there, as reminders' are; `allowBackup` is off), and
started anew after a restart or an update and as the app opens. Android may refuse
the start with the app out of sight, a task synced in; then it starts at the next
opening.
- *Why a service* (the developer's, 5 Nov 2026, on the first walk): the system's
`LocationManager.addProximityAlert` , built first, needs neither service nor
notification, but Android gives an app in the background a fix for it every 30
minutes (seen on the Pixel: 17:04:38, 17:34:43), so a short stop goes unseen, and
asking for updates in the background is held likewise. Keeping it was the
alternative, up to half an hour late; fused-provider geofencing was refused, it is
Google's. The price is a notification while places wait, on a channel of the least
importance (`places_watched` ), and the fixes, kept few (`LocationDwell.interval` ):
as long as reaching the nearest circle takes at 30 m/s, doubled for each fix the
2026-11-05 21:04:07 +01:00
phone lay still (moved under 50 m, and the wait asked for over, at least three
quarters of it, as asking anew brings a fix at once and an emulator's walk reached
2 min within 40 s), between 30 s and 5 min, at balanced accuracy on
fix(todo): turn to GPS where the fused provider stays silent (ADR 0033, D32)
A stock Android 17 emulator without Google's services left GPS off at
balanced accuracy and gave no fix in five minutes, while the
notification said it was watching. A Pixel's fused provider turns GPS
and the network on by itself, but nothing noticed where one doesn't.
After twice the wait asked for without a usable fix, the service asks
GPS as well, and leaves it once the fused provider gives a usable fix;
a rough one can't tell a circle, so it counts as silence.
All four walks (arrive, drive past, reboot, leave) passed on that
emulator on GPS alone, 22 fixes, no crash.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-05 21:43:17 +01:00
the fused provider of Android's own, GPS before Android 12. Where that provider
gives no usable fix for twice the wait, the service asks GPS as well, until it
does: on a Pixel it turns GPS and the network on itself, but a stock Android 17
emulator without Google's services left GPS off and gave nothing, unnoticed,
behind a notification saying it watches. Always asking high accuracy was the
alternative, simpler, with GPS busier on every phone. What it costs a day
feat(todo): places are watched by a quiet service, and asked to notify (ADR 0033, D32)
The first walk showed two faults. Android gives a proximity alert of an
app in the background a fix every 30 minutes (17:04:38, then 17:34:43 on
the Pixel), so a short stop goes unseen. And the place did remind, at
17:36:43, but nothing had asked to notify, so Android showed nothing.
A foreground service of the type location now takes its own fixes while
places wait, under the quietest notification there is: as long as
reaching the nearest circle takes at 30 m/s, doubled while the phone
lies still, between 30 s and 5 min. A place with a reminder asks to
notify as any reminder does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-05 18:17:48 +01:00
wants measuring. A fix rougher than 100 m says nothing of a circle.
2026-11-05 15:24:13 +01:00
- *Dwell*, so that driving past an EDEKA on the way to the next doesn't remind (the
feat(todo): places are watched by a quiet service, and asked to notify (ADR 0033, D32)
The first walk showed two faults. Android gives a proximity alert of an
app in the background a fix every 30 minutes (17:04:38, then 17:34:43 on
the Pixel), so a short stop goes unseen. And the place did remind, at
17:36:43, but nothing had asked to notify, so Android showed nothing.
A foreground service of the type location now takes its own fixes while
places wait, under the quietest notification there is: as long as
reaching the nearest circle takes at 30 m/s, doubled while the phone
lies still, between 30 s and 5 min. A place with a reminder asks to
notify as any reminder does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-05 18:17:48 +01:00
developer's question): crossing the circle (in at 150 m, out beyond 200 m, so a fix's error
doesn't flap) schedules a check two minutes later, a crossing back cancels it, and
the check notifies only if the phone is still on that side (`LocationDwell` , pure,
with JUnit tests CI doesn't run yet), by a fix at most 90 s old where there is one,
else by the last crossing. A red light of over
2026-11-05 15:24:13 +01:00
two minutes beside the shop still reminds; that is accepted for now.
2026-11-07 19:38:47 +01:00
- *Once a day, from the task's day on* (amended 7 Nov 2026, the developer's; before,
* once * : a task reminded once until it changed, whatever its day): a task reminds
on arriving or leaving once a day, and not before its day. A task for tomorrow stays
quiet today; an overdue one or one without a day reminds on each day the phone
arrives, as a shopping list left open does; a daily one once a day. Changing it
(title, day, places: `stamp` ) lets it remind again the same day, and an alternative
that reminded stops the task's others for the day. Todoist's reports gave the reasons
(r/todoist, read by the developer, as Reddit refuses the agent): its location
reminders ignore the due date, so a weekly task reminded on every arrival on the
other days and a daily one up to twenty times a day, as each tick made tomorrow's
task, which reminded on the next arrival; while people kept open tasks there to be
reminded on every visit. Once until it changed had the first two faults and lost
the third. The device keeps which tasks reminded today (`LocationDays` ) and an
inexact alarm at midnight lets the new day's places wait (none while every place
waits). A place waiting again on a new day reminds only after the phone crosses its
circle, so a task for today whose place is home doesn't remind just after midnight;
a task made or changed at its place still reminds there on the first fix. The
notification goes on the reminders' channel and opens the app.
2026-11-05 15:24:13 +01:00
- *Permission*: the position first, then, told why, * Allow all the time * in the
system's settings, asked as a person chooses such a reminder and never by planning
(a task may sync in); Settings' Reminders says while it is off, with Turn on, as D31.
feat(todo): places are watched by a quiet service, and asked to notify (ADR 0033, D32)
The first walk showed two faults. Android gives a proximity alert of an
app in the background a fix every 30 minutes (17:04:38, then 17:34:43 on
the Pixel), so a short stop goes unseen. And the place did remind, at
17:36:43, but nothing had asked to notify, so Android showed nothing.
A foreground service of the type location now takes its own fixes while
places wait, under the quietest notification there is: as long as
reaching the nearest circle takes at 30 m/s, doubled while the phone
lies still, between 30 s and 5 min. A place with a reminder asks to
notify as any reminder does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-05 18:17:48 +01:00
Notifications are asked for as for any reminder (D31), which the first walk found
missing: the place reminded on time and Android showed nothing.
2026-11-05 15:24:13 +01:00
- *A debug build installs beside the release* (`.debug` , * Todo Debug * ), the keys
differing; SimpleLogin's way back keeps the scheme the instance allows, so with both
installed Android asks which app answers it. Debug builds log to
`adb logcat -s HalkyonLocation` .
2026-11-05 14:58:38 +01:00
- *Found through the instance* (the agent's, the developer choosing locations): a
search goes to Todo's own server, as Train's (ADR 0030, D50), not to a public service
or Google: `SearchEndpoint` on `PlaceSearch` , on the instance's Photon (`PHOTON_URL` ,
`compose.todo-search.yaml` ), for those signed in, 60 searches a minute, neither query
nor places kept or logged. The server sees what is searched for while it is searched
for; a task's location stays in its sealed payload. Copied from Train's rather than
moved into the module, as `TodoKeys` was: the two differ, Todo's `Place` carrying the
address's parts and the object in OpenStreetMap (`osmType` , `osmId` , `osmValue` ), what
opening hours are later looked up by, and leaving out what is no place to go, Photon
asked for 12 so that 8 are still listed.
2026-11-05 15:24:13 +01:00
2026-11-06 19:44:46 +01:00
## Decisions on Todoist's Pro features (6 Nov 2026, the developer's)
**D35. Filters as Todoist's, read on the device; labels get a view.** A filter is a query in Todoist's language (`today` , `p1` , `#Work` , `##Work` with its sub-projects, `/Section` , `@label` , `overdue` , `no date` , `7 days` , `before:` , `after:` , `search:` , `&` , `|` , `!` , parentheses, `*` in names, each word in German too), kept with a name, a description, a colour and whether it is a favourite in its own sealed table, `saved_filter` , and read by `FilterQuery` on the device: the server sees no task (D5) so it could not answer one, and a query itself says what the person cares about, so it stays sealed. **Why Todoist's language, not a form: ** people who come from Todoist can bring theirs, and a form can't say `(today | overdue) & !#Work` . **Why no Filter Assist: ** it would send what someone keeps private to a model; left out for now. A query that isn't understood can't be saved, and the editor says where. **Filters & labels ** is a screen of its own under Browse, as Todoist's, which Browse's labels card moved to; a label opens its open tasks, editing and deleting moved into that screen's menu, and favourite filters and labels join Browse's Favourites (`LabelContent.favorite` , schema stays 1, older labels being none). First version in the list layout alone: boards for a filter or a label come later, since their tasks have no sections of their own.
**D36. What of Todoist's Pro stays out** (the developer's, 6 Nov 2026). Calendar sync: not yet. Themes: irrelevant to them. Backups: later. Karma: stays out (D8). What's left, decided the same day and built after D35: a table layout, on wide windows alone (columns Task, Date, Deadline, Priority, Labels), falling back to the list on a phone; comments, plain text with links, edited and deleted, no attachments or reactions yet, Markdown later without changing their data; a goal a day and a week with streaks, charts, days off and vacation mode, a quiet card once a day on reaching it, nothing to share; history as the completed tasks alone, no event log.
feat(todo): a table's columns, chosen and moved, each with its menu (ADR 0033, D36)
As Todoist's after trying it: Description and Created join the columns,
Display's Columns turns them and a task's details on and off, and a heading
filters, sorts, moves or hides its column. Sorting can run either way, and
tasks can be filtered by when they were added. Reporting's project filter
shows each project's icon.
Whats-New: Table: choose and arrange its columns, and filter, sort or hide one from its heading.
Whats-New-De: Tabelle: Wähle und ordne die Spalten und filtere, sortiere oder blende eine über ihre Überschrift aus.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-06 21:05:54 +01:00
The table's rows aren't dragged: the list is where tasks are placed by hand, and the Display's grouping, sorting and filters apply to it as to the list. After trying it (6 Nov 2026), its columns follow Todoist's: Description and Created join the five, each turned on or off under Display's Columns, as are the sub-tasks, comments and reminders beside a task's name; a heading opens a menu to filter its column (not Task or Description), sort it ascending or descending (not Labels or Description), move it and hide it (not Task). Created also joins the Display's filters. A column set too wide for the window scrolls sideways. Below 720 pixels of its own width, a phone or beside the task pane, a project kept as a table shows as a list, so the same project works on both.
2026-11-06 19:49:15 +01:00
2026-11-06 19:53:28 +01:00
Comments are kept in their own sealed table, `task_comment` , and which task one belongs to is sealed with it, so the instance can't tell how much is said about what. A task deleted takes its comments along on the device that deletes it. Links are http and https alone, opened in the browser: a comment can't start anything else on a device, whatever it says. Links show in comments, not yet in a task's description, which is an editing field.
feat(todo): Reporting, with goals, streaks and what was done (ADR 0033, D36)
A daily and a weekly goal, each with its progress, streak and chart, days
off and vacation mode, and the history of completed tasks by project, all
worked out on the device from what the tasks keep. Today shows a quiet card
once a day when the goal is reached. No karma, nothing to share.
Whats-New: Reporting: set a daily and a weekly goal, follow your streaks and see what you've done, with days off and vacation, under Browse.
Whats-New-De: Auswertung: Lege ein Tages- und ein Wochenziel fest, verfolge deine Serien und sieh, was du erledigt hast, mit freien Tagen und Urlaub, in der Übersicht.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-06 19:58:53 +01:00
Reporting is worked out on the device from what tasks keep (`completedAt` , `completions` ), so no event log exists and a task deleted leaves the history and the counts, which can shorten a streak already shown. Goals start at 5 a day and 30 a week with Saturday and Sunday off, as Todoist's, and live in the sealed settings row, synced. Days off and a vacation break no streak, and a goal reached on one still counts; vacations stay kept once over, or a streak across one would break the day it ended. Days off changed apply to the past as well, since only one set is kept. A day counts by the device's time zone at reading. The card on Today says the goal was reached once a day, marked in the settings so a second device doesn't repeat it; it plays nothing and shares nothing.
refactor(learn): the editor's general parts move to halkyon_editor (ADR 0033, D37)
Todo stores editor documents too, so the document, the editor, its view,
the format bar, links, the clipboard, scripts and EditorBlocks move, with
their strings and tests, to a shared package on the same appflowy_editor
fork. What is Learn's alone stays in Learn on top of it, and what the
editor took from it directly is inverted: EditorBlocks gains textMarks,
formatActions and onEditorClosed, RichContentEditor a wrapEditor, and an
InsertableBlock's name takes the BuildContext. Learn's screens use
LearnContentEditor and LearnContentView, which add Learn's blocks and the
grammar check. Learn behaves as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-06 21:25:44 +01:00
**D37. The editor's general parts become a shared package** (the developer's, 6 Nov 2026). Comments and descriptions in Todo take rich text, stored as the editor's document JSON, not as Markdown (task names stay plain). Two apps storing the same documents need the same editor, so `packages/halkyon_editor` is made now rather than Todo writing a second one or depending on Learn. It holds, moved with their history and their tests: `RichContent` and `content_blocks.dart` , the document's conversion, `RichContentEditor` , `RichContentView` , the format bar, `EditorBlocks` , the editor's look, the clipboard, `AtomicBlock` , the Insert menu, links, superscript and subscript, with their strings in the same words (its own gen-l10n, `HalkyonEditorLocalizations` ; only `commonCancel` , used elsewhere, stayed in Learn's too). `content_blocks.dart` names Learn's blocks too, since `RichContent.plainText` reads them and a block's type is one format for good, whichever app wrote it.
What is Learn's alone stayed in Learn, on top of the package: formula, callout, spoiler, file and YouTube blocks, ruby, Markdown, `rich_text_screen` , `save_status` , `core_blocks` , the study button, grammar. Nothing in the package may know them, so what the editor took from them directly is inverted: `EditorBlocks` gains `textMarks` (ruby and formulas drawn within lines), `formatActions` (their buttons, in the same place, after subscript) and `onEditorClosed` (a reading shown over the pointer taken away), and `RichContentEditor` a `wrapEditor` (the grammar host). An `InsertableBlock` 's name now takes the `BuildContext` , not Learn's `AppLocalizations` , and `offering` narrows what an editor offers keeping the rest (the discussion composer). Learn's screens use `LearnContentEditor` and `LearnContentView` , thin wrappers in `core/editor/learn_content.dart` passing Learn's blocks and turning `grammar` into the wrapper; a global default the app sets instead would have left every widget test without Learn's blocks, and the package's own default is `basicBlocks` , so that Todo gets a working editor with no setup. `RichContent` stays pure Dart in a Flutter package: nothing outside the apps reads it today; a server that had to would move it to a pure Dart package then.
feat(todo): comments and descriptions in the editor (ADR 0033, D38)
Comments and the descriptions of tasks, projects, sections and filters
become the editor's document on halkyon_editor; names and titles stay
text. Rows written as text read as notes and become documents when next
saved, the server seeing only sealed bytes, so there is no migration.
Sections' descriptions can now be written, and a project's shows above
its tasks. Android answers whether a link can open only for what is
declared, so Todo declares http and https.
Whats-New: Comments and descriptions take rich text: headings, lists, checklists, links and more.
Whats-New-De: Kommentare und Beschreibungen können jetzt formatiert werden: Überschriften, Listen, Checklisten, Links und mehr.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-06 21:36:05 +01:00
**D38. What is written at length is a note, in the editor** (the developer's, 6 Nov 2026, after trying D36). Comments and the descriptions of tasks, projects, sections and filters are `RichContent` from `halkyon_editor` (D37), stored in their sealed payloads as the editor's document, which supersedes the Markdown D36's comments were left for; names and titles stay text. No migration: the server sees only sealed bytes, so `readNote` reads either shape and a row written as text becomes a document when next saved. The payloads stay schema 1, since Todo isn't released: a build from before D38 can't read a row written since, as a phone with both would show, and that is accepted now and would not be once it is. A row of a newer document format is skipped rather than read half. `src/core/note/` holds it: search, filters' `search:` , the table's column, rows and one-line previews read the plain text. A note holding nothing is left out of its payload, so "no description" stays one state.
Where a note is written follows what it is: a comment, quick add, and the project's, section's and filter's dialogs write it in place (`NoteComposer` , as high as its lines, the format bar while it has the focus); a task's description and a comment being changed open the whole window (`editNote` , as Learn's `editRichText` ), and leaving it however it is left keeps what was written, as the field did when it lost the focus. Addresses written in a note become links as it is saved, http and https alone (`withLinks` ), as comments had them. The editor asks Android whether a link can open before opening it, so Todo declares both schemes under `<queries>` . Sections had a description in their payload that nothing could write, and projects one that nothing showed: the section's dialog now writes it, and a project's list shows it above its tasks.
2026-11-06 22:23:31 +01:00
A comment's handling follows Todoist's (D36), as recorded on 6 Nov 2026. A task's sheet shows nothing of comments until there is one, only the bar at its foot; then how many, under a heading that folds them, the latest in a box of its own on one line, and Show all in another. The bar opens the comments over the task, ready to write with the keyboard open; Show all or the latest comment open them to read, the field a bar until tapped. The sheet is as high as it holds; each comment is a box with the writer's picture or first letter, their name, when, a menu, the text, and reactions under the name; the task's name leads back. Who wrote a comment isn't kept: its one writer is the person, whose profile gives the name, none where they have none. Todoist's paperclip waits for attachments, and Copy link to comment for links into the app.
Reactions are a list of emoji in the comment's payload, each once; a chip's tap takes its reaction off, and with one person on a comment each counts 1. The emoji sheet is Todoist's too, written here: the search above, the recently used, then every category in one list with a bar to jump between them. `emoji_picker_flutter` (MIT) gives only the emoji, their names in each language and the recents, which stay on the device: its own picker has its search at the foot and a tab bar on the SDK's Material. Todo now has the `MaterialUiCompatibilityBridge` , as Learn does, since the editor still imports that library; without it, the picker's tab bar found no localizations.
feat(todo): comments and descriptions in the editor (ADR 0033, D38)
Comments and the descriptions of tasks, projects, sections and filters
become the editor's document on halkyon_editor; names and titles stay
text. Rows written as text read as notes and become documents when next
saved, the server seeing only sealed bytes, so there is no migration.
Sections' descriptions can now be written, and a project's shows above
its tasks. Android answers whether a link can open only for what is
declared, so Todo declares http and https.
Whats-New: Comments and descriptions take rich text: headings, lists, checklists, links and more.
Whats-New-De: Kommentare und Beschreibungen können jetzt formatiert werden: Überschriften, Listen, Checklisten, Links und mehr.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-06 21:36:05 +01:00
feat(todo): a calendar's feed read, and linked to a project in its payload (ADR 0033, D39)
The developer's decisions of 10 Nov 2026 for calendar feeds, recorded as
D39: a project links to a calendar's iCal feed, its events shown beside
tasks, read-only; each device fetches it, the address sealed in the
project's payload, the last copy kept on the device.
This lays the ground: `readIcal` reads a feed's events (title, start,
end or duration, all-day, place, cancelled, the dates a recurrence
leaves out and the occurrences changed on their own), on the device's
clock through `timezone`, and expands their recurrence with pub.dev's
`rrule` (Apache-2.0, the developer's choice); `ProjectContent` gains
`calendarUrl`, its payload staying schema 1. Nothing shows it yet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-10 11:49:34 +01:00
## Decisions of 10 Nov 2026 (the developer's)
**D39. A project can show a calendar's events, read from its feed by each device.** A project can be linked to a calendar's feed (an iCal address, as Proton Calendar, Nextcloud or a school's timetable share), whose events then show beside tasks, read-only and in the project's colour: in the project's screen (its list and its Calendar), in Today and Upcoming (their lists, agenda and hour grids, all-day events under * All day * ), and in filters and labels' results where their days include them. This takes back D36's * Calendar sync: not yet * for reading; nothing is written to a calendar.
- *Fetched by the device, its address sealed* (the agent's, kept by the developer): the address is part of the project's sealed payload (`calendarUrl` ), as a feed's address often is its only key, and each device fetches it itself over HTTPS, so the instance neither learns the address nor sees the events; a device keeps the last copy for when it is offline, not synced, as a file in the person's own folder on it (`calendars/<project>.ics` ), as their reminders keep their tasks' titles unsealed on the device, and deleted with the link or the project. **Why not through the instance ** , as Train's weather is: it would hold everyone's calendar addresses and see their events, for no gain but one fetch instead of one per device.
- *When*: as the app opens, every hour while it is open, and when a list is pulled down (`TaskRefresh` ).
- *Read by a parser of our own, recurrence by `rrule` * (the developer's choice of three): the file's events (VEVENT: title, start, end or duration, all-day, place, cancelled, the dates a recurrence leaves out and the occurrences changed) are read by `ical.dart` , short and tested here; RFC 5545's recurrence rules, the large part easy to get wrong, are expanded by pub.dev's `rrule` (Apache-2.0); an event's time zone becomes the device's through `timezone` , already in the lock. **Rejected: ** `icalendar_parser` beside `rrule` (one more dependency, less maintained, for the easy part), and recurrence of our own.
- *Shown as a task's date is*: an event becomes a day, a time and a duration on the device's clock, as `TaskDate` , so the lists, the agenda and the grids place it as they place tasks. It has no check, can't be dragged and opens nothing yet but its details.
2026-11-01 00:50:52 +01:00
## Steps
1. **The frame ** : go_router and `NavigationFrame` with the four tabs (empty screens that
say what comes), the strawberry wash, the cog, Settings as D4 with what exists today
(the account, What's new, About, the version); the account sheet removed.
2. **The first synced model ** : `content_key` and `project` (Inbox made once), its gateway
and repository on Learn's and Train's pattern, the server's access rules each with a
test asserting denial; walked through with the developer as the first of Todo's.
3. **Tasks ** : `section` , `task` , `label` ; Inbox and a project's list; the task sheet; quick
add.
4. **Today and Upcoming ** , recurrence (D7), reminders on the device.
5. Todo's first release (0.1.0), its * What's new * drafted by `tool/release.dart` .
build(todo): Todo deployable beside Learn's server, and released as an APK, on F-Droid and as a snap (ADR 0033, step 5)
deploy/compose.todo.yaml adds Todo's server at todo.halkyon.eu, a member
of Learn's register, its database halkyon_todo in the instance's
PostgreSQL, its image built as Train's. redeploy.sh and backup.sh go over
every app server .env switches on (train, todo). Each compose file names
its server to the register in a variable of its own
(INSTANCE_MEMBER_TRAIN, INSTANCE_MEMBER_TODO), which halkyon_base adds to
INSTANCE_MEMBERS, each address once, as a second compose file's default
would have replaced Train's.
apps/todo/release.dart configures the release tool for Todo's APK and
snap: a key of its own (pinned once made; until then nothing is
released), the F-Droid listing (Time, NonFreeNet), and the snap from
apps/todo/snap/, which the tool builds in a clone of the repository at the
tag with that folder in place of snap/, as snapcraft builds one recipe a
folder and the root's is Learn's. What's new for 0.1.0 in English and
German, now shown. Android's backup and transfer are off, as Train's
(D53): the reminders scheduled keep their tasks' titles unsealed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-02 15:35:40 +01:00
Decided 2 Nov 2026 (the developer's, the first two the agent's recommendations):
- **Deployed beside Learn's server, as Train is** (ADR 0030, D46):
`deploy/compose.todo.yaml` at todo.halkyon.eu, a member of Learn's register, its
database `halkyon_todo` under a role of its own, taken in by `redeploy.sh` and
`backup.sh` where `.env` switches it on, both now going over every app server so
switched on (`train` , `todo` ). Each compose file names its server to the register
in a variable of its own, `INSTANCE_MEMBER_TRAIN` , `INSTANCE_MEMBER_TODO` , which
halkyon_base adds to `INSTANCE_MEMBERS` , each address once (agent's choice): a
second compose file's default of `INSTANCE_MEMBERS` would have replaced Train's,
and Learn's register would no longer have told Train of an account gone.
* Alternatives: * the release config alone, Todo's instance later.
- **A key of its own** (as Train's D58): a PKCS #12 keystore at
`~/.local/share/halkyon-todo/android-release.p12` , alias `halkyon-todo` , in Proton
Pass as * Halkyon Todo – Android release keystore * , pinned in
2026-11-02 16:36:59 +01:00
`tool/release/todo-android-certificate.sha256` (made 2 Nov 2026, in memory alone,
`$XDG_RUNTIME_DIR` , and kept nowhere but Proton Pass). * Alternative: * Learn's key, which
build(todo): Todo deployable beside Learn's server, and released as an APK, on F-Droid and as a snap (ADR 0033, step 5)
deploy/compose.todo.yaml adds Todo's server at todo.halkyon.eu, a member
of Learn's register, its database halkyon_todo in the instance's
PostgreSQL, its image built as Train's. redeploy.sh and backup.sh go over
every app server .env switches on (train, todo). Each compose file names
its server to the register in a variable of its own
(INSTANCE_MEMBER_TRAIN, INSTANCE_MEMBER_TODO), which halkyon_base adds to
INSTANCE_MEMBERS, each address once, as a second compose file's default
would have replaced Train's.
apps/todo/release.dart configures the release tool for Todo's APK and
snap: a key of its own (pinned once made; until then nothing is
released), the F-Droid listing (Time, NonFreeNet), and the snap from
apps/todo/snap/, which the tool builds in a clone of the repository at the
tag with that folder in place of snap/, as snapcraft builds one recipe a
folder and the root's is Learn's. What's new for 0.1.0 in English and
German, now shown. Android's backup and transfer are off, as Train's
(D53): the reminders scheduled keep their tasks' titles unsealed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-02 15:35:40 +01:00
would let one app's build pass for the other's update.
- **The APK, in the F-Droid repository, and a snap** (the developer's, over the APK
2026-11-04 20:47:59 +01:00
and F-Droid alone): the listing `app.halkyon.todo.yml` (categories * Task * and
* Calendar & Agenda * since D27, `NonFreeNet` for the server's SSPL, as Learn's says), the snap `halkyon-todo` to
build(todo): Todo deployable beside Learn's server, and released as an APK, on F-Droid and as a snap (ADR 0033, step 5)
deploy/compose.todo.yaml adds Todo's server at todo.halkyon.eu, a member
of Learn's register, its database halkyon_todo in the instance's
PostgreSQL, its image built as Train's. redeploy.sh and backup.sh go over
every app server .env switches on (train, todo). Each compose file names
its server to the register in a variable of its own
(INSTANCE_MEMBER_TRAIN, INSTANCE_MEMBER_TODO), which halkyon_base adds to
INSTANCE_MEMBERS, each address once, as a second compose file's default
would have replaced Train's.
apps/todo/release.dart configures the release tool for Todo's APK and
snap: a key of its own (pinned once made; until then nothing is
released), the F-Droid listing (Time, NonFreeNet), and the snap from
apps/todo/snap/, which the tool builds in a clone of the repository at the
tag with that folder in place of snap/, as snapcraft builds one recipe a
folder and the root's is Learn's. What's new for 0.1.0 in English and
German, now shown. Android's backup and transfer are off, as Train's
(D53): the reminders scheduled keep their tasks' titles unsealed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-02 15:35:40 +01:00
the Store's edge channel, as Learn's, from `apps/todo/snap/` . snapcraft builds one
recipe a folder, `snap/snapcraft.yaml` , and the root's is Learn's, so the release
tool builds Todo's in a clone of the repository at the tag, kept in
`~/.cache/halkyon-release/snap/halkyon-todo` so that its build machine and Flutter
part stay from one release to the next, Todo's folder in place of the clone's
`snap/` but for `snap/local/` , which the recipes share (agent's choice; over a
worktree, whose `.git` file points to a folder LXD doesn't see, and over moving
Learn's recipe, which its releases and docs name). The other platforms come
later.
- Android's backup and transfer to another phone are off, as Train's D53 (agent's
choice): the reminders scheduled keep their tasks' titles unsealed on the phone.
2026-11-01 00:50:52 +01:00
2026-11-05 19:17:36 +01:00
- **D33 Side panes widened, a click adds a task** (the developer's, 5 Nov 2026, from
Todoist's desktop app; the details the agent's):
- The line beside the task pane (D2) and Today's Plan (D30) is dragged left and right
(`SidePane` ), so wherever a pane stands beside a list or a calendar. A pane stays
300 px wide at least and leaves 360 px beside it; a window too narrow for the width
chosen shows less of it, and the choice stays. Each pane keeps its own width
(`PaneWidths` , the file `panes.json` ): widths belong to a window, so they are the
device's, neither sealed nor synced, as the device's look is. One width for both was
the alternative; the Plan starts narrower than a task.
- In the hour grid (`HourGrid` , so Today's calendar and Upcoming's 3-day) a tap or a
click opens quick add at the quarter hour under it, for half an hour, on a phone
too: a list's or board's + is dragged for the place between tasks, while in a
calendar where it lands already says when. A long press stays, in quarter hours,
no more half hours. A tap that stops the day scrolling doesn't add, as a fling
keeps pointers from what it scrolls.
2026-11-05 19:37:55 +01:00
- **D34 The Plan beside Upcoming, with what has no date** (the developer's, 5 Nov 2026,
the agent's recommendation): the Plan (`PlanPanel` ) stands beside Upcoming's 3-day
grid as beside Today's calendar, through what the two now share (`PlanHost` ), a row
dropped on an hour or * All day * going to that day. It shows or hides for both calendars
(`TodayView.plan` , kept as it was: a second setting was the alternative, for a choice
people hardly make per tab); the projects picked are each tab's. It stays about today:
a block dropped on it loses its time only if it is today's. * No date * , its last
group, holds the open tasks of no day, sub-tasks staying with theirs, so that a week
is planned from the Inbox; it isn't in the button's count, which says what is due. A
routine's templates (the developer's idea) wait for what a routine is.
2026-11-01 00:50:52 +01:00
## Consequences
- No email reminder, morning overview or evening review: the server can't read what is
due. A device without the account's keys neither reads nor writes Todo's rows, as in
Train.
- Todo's server holds no logic over tasks; what Today, Upcoming and Reporting show is
worked out on the device, over every task the person has. That is fine for thousands of
tasks; a person with far more would need the device to keep an index of its own.
- Sharing a project is harder than with readable rows (a key per shared project, its
members' keys wrapping it); D5 accepts that for now.