Provides a one-command setup for Arch Linux (builds flutter-bin from
the AUR via makepkg) and Ubuntu/Debian (apt if a flutter package
exists, otherwise the upstream SDK clone), plus the Linux desktop
build toolchain, then runs `flutter run -d linux`.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Scrolling on the next post-frame callback raced the Stepper's own
200ms expand/collapse transition, so ensureVisible measured a layout
that hadn't settled yet — the tapped step ended up hidden under the
fixed Priority/Status/Template bar. Delaying past kThemeAnimationDuration
before scrolling fixes it; verified on-device via ADB screenshots.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds `tech` (stack + versions) and `ask` (decisions needing human
approval) sections; renames `out` → `never` to match the three-tier
always/ask/never boundary system from Addy Osmani's good-spec article.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The token moved from plaintext SharedPreferences into flutter_secure_storage
(Android Keystore / libsecret). Only the non-secret owner/repo/clientId stay in
prefs. Migration is confirm-before-delete: load() reads the keystore first and
falls back to the legacy plaintext token, migrating it only once a secure write
succeeds; save() likewise keeps writing plaintext if no secret service is
available, so we degrade to — never below — the old behaviour.
157 tests, 100% line coverage, analyze clean. Verified on-device: Settings
stays connected and sync succeeds after the one-time migration.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Connecting via the device flow saved the token to prefs but had no effect:
the capture screen cached its settings from launch and only re-adopted them
when Settings was popped *with a result*, which the connect flow never did.
So auto-sync kept using stale (token-less) settings (notes never downloaded)
and reopening Settings re-seeded the fields from the stale settings (empty
token → Test connection failed, Connect restarted every time).
- settings: on a successful connect, save then run a sync right away and
report the result ("Connected and synced …") so notes download and the
user gets real confirmation, instead of the inert "Token saved on Save".
- capture: always reload settings from storage after returning from Settings,
so a device-flow connect (which saves without popping a result) is picked up.
- tool/device_flow_check.dart: standalone end-to-end device-flow probe used to
confirm the OAuth App + token + repo-access chain is healthy (it is); the bug
was purely app-side token application.
152 tests, 100% line coverage.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Previously "Connect GitHub" (OAuth device flow) still required entering an
OAuth App client id and owner/repo — friction that returned on every
reinstall once shared_prefs were wiped.
- Bake the app's own device-flow OAuth App client id in as
SyncSettings.defaultClientId and default to it in load() (alongside the
existing kuhyx/todo-sync repo default). A device-flow client id is a
public identifier, not a secret, so it is safe to commit.
- Settings now leads with a single "Connect GitHub" button; the manual
client-id / token fields and Test connection move under an "Advanced"
expander. Result: fresh install (or post-reinstall) is one tap →
authorize the code in the browser → synced. No tokens, no setup.
Note: an OAuth App authorizes with the classic `repo` scope (all repos),
broader than the prior fine-grained PAT — the trade-off for one-tap
device-flow convenience. 151 tests, 100% line coverage.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Third durability layer beside GitHub auto-sync and Android Auto Backup:
a plain, human/LLM-readable Markdown file kept current on local disk.
- LocalBackup (lib/sync): pure, injectable file IO. scheduleExport()
debounces writes (a burst of keystrokes → one export); recover() parses
the file back into notes. Reused NotesMarkdown serializer.
- CaptureScreen wires it: on launch, recover into an *empty* DB only (so a
stale backup never clobbers existing notes), then keep the backup current
as notes change. Platform path = ~/todo/BACKLOG.md on desktop (the path
the user's workflow already reads) or the app documents dir on mobile
(covered by Android Auto Backup). File IO is injected in tests.
- Added fake_async dev dep to unit-test the debounce with a virtual clock.
151 tests, 100% line coverage.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
OS-level safety net so a reinstall can restore data through Android's
backup/restore flow. Sets allowBackup=true and points fullBackupContent
(pre-31) and dataExtractionRules (API 31+) at empty rule sets, which back
up the default locations: the app files dir (local CRDT DB todo.db),
databases, and shared_prefs (GitHub sync settings + token).
Backing up shared_prefs is intentional — it restores the sync config on
reinstall so recovery needs no re-auth (zero-touch). The token is a
narrowly-scoped contents-only PAT for the single private sync repo, kept
in the user's own Google backup.
Caveats: Auto Backup is scheduled (~daily, Wi-Fi+charging) so very recent
changes may not be captured, and restore fires through the Play Store /
device-setup flow, not an `adb install -r`. Auto-sync remains the primary
durability path; this is the secondary net. Verified the attributes merge
into the app manifest via a debug build.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Manual sync left a large window in which local edits had not reached
the remote, and a reinstalled device only recovered after a manual
sync. Make sync automatic so the GitHub repo (the durable store) stays
near-current and recovery is near-automatic.
- Pull on launch (once settings load) so a reinstalled device pulls its
old changeset back without user action.
- Push when the app is backgrounded (AppLifecycleState.paused) so edits
leave the device promptly. Lifecycle-based, not per-keystroke, to keep
CPU/battery and GitHub API usage low.
- Best-effort: silent, skips when unconfigured, single-flight guard so a
launch sync and a background sync never overlap; failures are swallowed
(the manual Sync button still surfaces errors).
100% line coverage maintained.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Notes were previously only openable via a quick-actions sheet; you
could not read or edit a note in full. Add a shared NoteEditor used by
both the capture and detail screens, plus selectable templates and a
rendered Markdown view.
- note_template.dart: pure assemble/parse layer over a Markdown subset
(# title, ## sections + italic guidance, dropping empty sections).
assemble(parse(text)) is idempotent for conforming text; non-conforming
/ legacy / freeform text is reported so the UI falls back to raw,
untouched. Two templates: llm-design-spec (default) and blank.
- note_editor.dart: View / Guided / Raw modes. Guided is an inline
Stepper (one step per section with its guidance); View renders the
note via MarkdownView; Raw is the verbatim text. Guided is offered
only for structured templates; switching to it is blocked when the
raw text no longer conforms.
- markdown_view.dart: lean read-only renderer for the note subset,
wrapped in a SelectionArea for copy-out.
- note_detail_screen.dart: full-screen note; opens in View, edits
persist immediately, priority/status dropdowns, delete.
- capture_screen / notes_list_screen wired to the new editor and detail
screen (tap a note opens it; quick actions move to the overflow button).
The editor is a view over plain text, so CRDT storage and Markdown
export/sync are unaffected. 138 tests, 100% line coverage.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Initialised from this session: documents the data/sync/ui layers, the CRDT
sync model, the structured note template, build/test commands, the repo-specific
git rule (always push to main, never open PRs), and the widget-testing patterns
that keep the suite fast and at 100% coverage.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Inject an optional http.Client into CaptureScreen (mirroring SettingsScreen)
so the configured sync path runs against a MockClient instead of the network;
capture_screen.dart now 100%.
- Mock the file_selector and url_launcher platform interfaces and the clipboard
channel so the import flow, _openPage launch, and the device-code dialog's
error/Cancel/Open paths are exercised deterministically (no hangs, no timers).
- Add unit tests for the remaining fallbacks/defaults: copyWith no-arg paths,
GitHubApiException.toString, default-constructed clients, empty NoteFilter,
the v1->v2 status-column migration, and the export/import error branches.
- coverage:ignore the private static-only NotesMarkdown ctor.
101 tests, all green in ~5.5s. Line coverage 96.2% -> 100%.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Notes list & filtering:
- Text-search filter plus independent date-range filters for both created
and last-updated (AND-combined), a priority filter, and a new status
filter. Default view hides Done/Abandoned and renders as "unfiltered"
(no badge for the default state); fixed badge clipping.
- NoteSort options wired into the list UI; watchCount() for the "N saved".
Status & priority:
- New Status enum (toDo/inProgress/Done/Abandoned) as a settable + filterable
attribute on every note, with capture-screen dropdown.
- Removed "None" priority: every note is Low/Medium/High, default Medium.
Schema migration v2->v3 rewrites legacy priority 0 -> Medium.
Export / import:
- NotesMarkdown round-trippable single-file format with HTML-comment markers.
- Settings "Export notes" (mobile share sheet / desktop writes ~/todo/BACKLOG.md)
and "Import notes" (file picker + safe newer-wins merge by id).
Structured template:
- Every new note pre-fills the richer what/where/must/nice/out/done/depends/
estimate/refs scaffold.
Tests:
- New fast (~5s), deterministic suite via FakeNoteRepository (no DB timers) and
injected http/file-selector/url-launcher fakes. 86 tests, 96.2% line coverage
(note.dart & sync_service.dart at 100%, settings 98.7%). Mobile-only share
branch excluded via coverage:ignore (unreachable on the Linux test host).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Flutter app for Android + Linux desktop. Captures ideas with per-keystroke local autosave to a CRDT-backed SQLite store (sqlite_crdt), and syncs through a private GitHub repo using per-device changeset files (conflict-free last-writer-wins merge). Includes GitHub OAuth device-flow sign-in with PAT fallback, a barebones notes list, and sync settings.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>