Commit Graph

9 Commits

Author SHA1 Message Date
cbe3d9be59 One open note per repo, merge tool
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-11-02 14:11:21 +01:00
9f2ffdb8a9 Attach any file to a note, of any size, not only images
Some checks failed
dependency freshness / dependencies on newest stable (push) Has been cancelled
file-length / file length <= 250 lines (push) Has been cancelled
md-naming / markdown naming convention (push) Has been cancelled
release-apk / release (APK) (push) Has been cancelled
📎 now picks any file (Android: Camera / Gallery / File; desktop: an
unfiltered dialog) and the desktop page accepts any dropped file. Photo and
screenshot formats up to 64 MB are still re-encoded and shown inline;
everything else is stored byte for byte on dufs beside the images and linked
as [name](url), shown as a row with its size that opens in the platform's
app (Android) or a new browser tab (desktop). BACKLOG.md rewrites file links
to ~/data/cloud/todo-images/ like image links.

There is no size cap, so no layer holds a file in memory: uploads and
downloads stream through DufsImageClient, writes land in a .tmp renamed when
whole (an interrupted download no longer leaves a truncated file that looks
done), the desktop page POSTs the browser's own Blob with fetch, and Android's
"File" uses file_picker, because file_selector's Android side reads the whole
file into memory on the UI thread. Verified with a 1 GB file end to end on
desktop (peak RSS +36 MB) and on an API 36 emulator.

The wrapper serves attachments from the origin that holds the GitHub token
and the notes, so only script-free types open inline (nosniff), the rest
download; writes carrying another site's Origin are refused (this also
closes cross-site overwrites of the backup files); and attachment routes run
concurrently so a long upload or a playing video cannot stall the page.

Also: cupertino_icons 2.0.0 (unused, but the freshness gate blocks the
pubspec otherwise), open_file 4.0.0 and file_picker 13.1.0 pinned.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 12:17:06 +02:00
0798f7b1d0 Attach images to notes, stored on dufs and kept offline
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-25 14:11:15 +02:00
e6e1052c59 Adopt the shared design_system package, dropping theme.dart and confirm.dart
Some checks are pending
file-length / file length <= 250 lines (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
Both files were donors for the new package rather than casualties of it:
theme.dart was byte-identical to untools' copy (md5 6cf8e82d), and
confirm.dart's confirmDestructive() was the ready-made pattern for the
~35 AlertDialogs inlined across the fleet. Keeping local copies after
promoting them upstream is exactly how the two would drift apart again,
so they are deleted and imported from design_system-v0.1.0 instead.

test/confirm_test.dart moves upstream with its subject -- the package
covers that behaviour (Escape and the barrier both refuse, Cancel is
autofocused so a reflexive Return cannot delete) as part of its own
100% gate. test/theme_test.dart stays as a consumer-side smoke check
that the dependency actually resolves to the frozen palette.

Verified: flutter analyze clean (bar one pre-existing bin/ style info),
308 tests pass at 100% coverage (1962/1962 -- the drop from 2005 is
exactly the promoted lines), and the release APK is installed and
running on the Pixel 6a with all 108 notes intact, the gold #B8862E
focus ring rendering from the package's inputDecorationTheme, and no
crashes in logcat.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XS1m7b1piBtAL22USLW6jL
2026-08-14 22:59:59 +02:00
85a45daaee Share one note form between capture and the detail screen
Some checks are pending
file-length / file length <= 250 lines (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
Editing an existing note looked nothing like capturing a new one: it
always showed the template dropdown, the View/Guided/Raw pills and the
priority/status row. The cause was one missing argument -- both screens
already embedded the same NoteEditor, but NoteDetailScreen never passed
advancedMode, which defaults to true in the widget while the app-wide
AppSettings.advancedMode defaults to false.

Extract the shared form as three single-purpose components rather than
one widget owning several jobs:

- note_meta_row.dart renders the priority/status row (was
  capture_meta_row.dart; drops the private _MetaDropdown duplicate the
  detail screen carried, which used DropdownButtonFormField where the
  shared one uses DropdownButton).
- note_form_chrome_gate.dart owns chrome visibility and nothing else,
  replacing the _chromeVisible flag both screens copy-pasted.
- note_form.dart composes those two with NoteEditor.

appSettings is now a required parameter through NotesListScreen and
NoteDetailScreen. Required, not defaulted: a silent default is exactly
what caused this, so it must not be reintroducible.

Also in the capture app bar: drop the decorative Icons.list_alt_outlined
beside the note count, since the adjacent Icons.list button already names
what is counted, and move the count to the right of that button. The
finalize-and-clear action becomes Icons.check instead of Icons.add -- it
never saved anything (every keystroke is already persisted), so a plus
sign misdescribed it.

314 tests, 100% line coverage. Verified in a running web build that edit
and capture render identically with advanced mode both off and on.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-14 17:48:39 +02:00
e103261bc8 Confirm before deleting a note
Both delete paths -- the per-note actions sheet and the detail screen -- deleted
immediately, with no confirmation and no undo. With a pointer that is merely
risky: you have to travel to the control and press it. With a keyboard it is
one Tab away from a Return you were about to press anyway, and the note is gone
with nothing to recover it.

The confirmation is built so the SAFE option is what a reflexive keypress hits:
Cancel is autofocused, Escape dismisses as "no", and the destructive button is
error-coloured but deliberately not the default action. Escape is bound
explicitly rather than relying on the barrier, because the desktop surface here
is a Chrome --app window where that cannot be assumed -- and a confirmation the
keyboard cannot back out of is worse than no confirmation at all.

The actions sheet also gains isScrollControlled. Default bottom sheets cap at
9/16 of the viewport, i.e. ~432px on a 768px screen, and this sheet's height
grows with the note title and its chip rows -- so Delete could be clipped out of
reach on the very screen this app is used on.

The two existing delete tests correctly failed once confirmation landed; both
now go through the dialog, and each gains a cancel-keeps-the-note counterpart so
the negative path is covered too. 204 tests, 100% line coverage held.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 16:31:15 +02:00
f463e50d67 Switch to very_good_analysis, matching the rest of the Flutter ecosystem
Standardizes on the same stricter lint set + shared analysis_options.yaml
used by diet-guard/app and the new habit_stack app. Fixes all 127 issues
surfaced: dart fix --apply for imports/cascades/sorting, unawaited() on
fire-and-forget Futures, bare catch(e) narrowed to on Exception catch(e)
(matching diet_guard's existing pattern), one documented ignore for a
deliberate TypeError catch around untrusted peer JSON, and missing
public-member doc comments throughout. No behavior change: 174/174 tests
pass, 100% line coverage maintained.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-20 11:05:13 +02:00
29f94e76a5 Add priority/template wizard and raw-as-default for guided mode
Entering Guided on an empty draft now runs a two-step wizard (priority,
then template) before showing the stepper. CaptureScreen defaults to Raw
mode so a quick capture stays in the single-field flow; switching to
Guided opens the wizard. Chrome (template/mode bar and priority/status
row) is hidden while Guided or the wizard is active.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017Cb7oE5Xsc8zSBHHtT6qwa
2026-06-27 12:10:21 +02:00
abd4ba3bd7 Add full note view/editor with templates and Markdown render
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>
2026-06-15 21:59:31 +02:00