Commit Graph

49 Commits

Author SHA1 Message Date
bvandeusen 48c134ce6a feat(voice): pulse mic button with live amplitude
VoiceState now carries a normalized mic amplitude (0..1) updated
from the existing onAmplitudeChanged subscription, with a 0.02
change threshold so we don't spam rebuilds.

VoiceMicButton swaps the constant-rate AnimationController for an
AnimatedScale + animated glow driven by the live amplitude. 0.1
floor keeps the button breathing on silence; 120ms ease-out smooths
between 200ms samples.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-14 22:46:48 -04:00
bvandeusen fa84e40efc feat(briefing): stall watchdog + pull-to-refresh; chat attaches to in-flight streams
Briefing chat now uses the same StreamIterator + 45s per-event timeout
as the main chat provider, so a dropped SSE socket no longer leaves
isBriefingStreaming stuck true. Adds refreshMessages() that unfreezes
state when the server-side message is complete, wired to a new
pull-to-refresh gesture and the app lifecycle-resume hook.

Chat provider gains attachToGeneration() — safe to call unconditionally
on screen init, so landing on a conversation that's already mid-stream
(e.g. the /news discuss button, which now creates a conv and auto-kicks
generation server-side) picks up live tokens instead of freezing on the
placeholder.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-14 07:55:47 -04:00
bvandeusen 75b7d6d0fe feat(chat): stall watchdog, pull-to-refresh, and unfreeze on refresh
Mobile chat occasionally froze with the input disabled and nothing
progressing — the underlying cause is SSE sockets dropping silently on
mobile network handoffs / proxy idle timeouts. Dio never observes the
close, so the send loop's `await for` hangs indefinitely with
isStreaming=true.

Three changes:
- Wrap the SSE stream in a StreamIterator with a 45s per-event timeout
  as a stall watchdog. On timeout, break out and let the polling pass
  reconcile state from the server.
- Add pull-to-refresh on the chat message list and an AppBar refresh
  button with inline spinner feedback. Works on both empty and
  populated states.
- MessagesNotifier.refresh() now also clears isStreaming /
  streamingStatus when the server reports the latest assistant message
  as complete — so a stuck UI unfreezes the moment the user pulls down
  or taps refresh, even if the original SSE loop is still hanging.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 18:13:46 -04:00
bvandeusen 3e888b6458 fix: eliminate pull-to-refresh flicker by holding stale data during re-fetch 2026-04-08 14:39:50 -04:00
bvandeusen 6c29b685e8 feat(theme): shift Android palette from indigo to deep violet to match web identity 2026-04-08 13:54:39 -04:00
bvandeusen d2582f9111 feat: reduce resume cooldown to 30s; add pull-to-refresh to News, Calendar; refresh chat on resume 2026-04-07 17:43:46 -04:00
bvandeusen 946b70ecc4 fix: use dialog context in event delete confirmation
Same navigator mismatch as the chat delete bug — outer context inside
showDialog builder resolves to the wrong navigator ancestor.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 18:35:41 -04:00
bvandeusen 6ea268bf58 fix: use dialog context in delete confirmation to prevent shell nav pop
Navigator.pop(outerContext) inside a showDialog builder resolves to the
ShellRoute's nested navigator instead of the root navigator where the
dialog lives, popping the conversations route and showing a black screen.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 18:33:04 -04:00
bvandeusen 79dce1a01c feat: wire discuss button in briefing RSS cards, cap cards at 3
Adds discussArticle() to BriefingApi and wires it through to the
RSS news cards in BriefingScreen so tapping Discuss opens a chat
conversation seeded with the article. Also caps RSS cards per
message at 3 to avoid overly long briefing threads.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 17:49:16 -04:00
bvandeusen cb3a09756f fix: create conversations with empty title so server auto-names them
The app was creating conversations with title 'New conversation'.
The server only generates a title when conv_title is falsy (empty).
With a non-empty title, should_gen_title is False for the first
message (msg_count % 10 != 0), so auto-naming never fired.

Now creates with empty title (matching web app behaviour). The list
still displays 'New conversation' as a UI placeholder until the
server-generated title arrives.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 17:46:23 -04:00
bvandeusen d530920284 fix: load tasks via TasksApi in Knowledge view, remove tab counts
Tasks are not part of the knowledge API (backend rejects type=task).
When the Tasks tab is selected, fetch from /api/tasks and convert to
KnowledgeItem. Also removes per-type counts from tab labels to prevent
tabs from awkwardly resizing when counts load.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 12:36:00 -04:00
bvandeusen e2a358a158 feat: show tool status notifications in chat during generation
Parse SSE `status` events alongside `chunk` events and display the
status text next to the spinner while the assistant is generating.
Previously the Android app discarded all non-chunk SSE events.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 11:46:42 -04:00
bvandeusen 5b639dbd4c feat: add CalendarScreen with month strip and agenda list 2026-04-06 10:32:13 -04:00
bvandeusen 334882520c feat: add EventFormSheet with CRUD, recurrence picker, and color chips
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 10:31:08 -04:00
bvandeusen e7d174cef7 feat: add NewsScreen with feed filter, reactions, and discuss 2026-04-06 08:29:30 -04:00
bvandeusen 95d0f529ea fix: invalidate project tasks on return from task edit screen 2026-04-06 07:15:35 -04:00
bvandeusen 1b08b2fd9e feat(voice): integrate voice mode into Briefing screen
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 14:47:38 -04:00
bvandeusen 2231c60bfb feat(voice): integrate voice mode into Chat screen
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 14:45:53 -04:00
bvandeusen d97f7b0ebd feat(knowledge): wire 4-tab shell, retire LibraryScreen, complete Knowledge View feature 2026-04-05 00:03:23 -04:00
bvandeusen 2ab24a99a8 feat(knowledge): add edit button to ProjectTasksScreen app bar 2026-04-04 23:56:40 -04:00
bvandeusen e39d31fe43 feat(knowledge): add ProjectsScreen and ProjectEditScreen, sort projects by updated_at 2026-04-04 23:56:18 -04:00
bvandeusen a50193dbc0 feat(knowledge): update Project model, ProjectsApi sort, and NoteEditScreen noteType support 2026-04-04 23:52:10 -04:00
bvandeusen f5ba2d25a3 feat(knowledge): add KnowledgeScreen with tabs, search, tag filters, and infinite scroll 2026-04-04 23:50:19 -04:00
bvandeusen 23509adfa8 feat: news story cards in Android briefing screen
Replaces bare Story-N reaction rows with full NewsCard widgets: source label,
relative timestamp, linked headline, 2-line snippet, and 👍/👎 reactions.
Reads rss_items from message metadata (requires backend ≥ this sprint).

Adds url_launcher ^6.3.1 for opening article links in the browser.
Adds https/http <queries> entries to AndroidManifest for Android 11+.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 00:08:51 -04:00
bvandeusen 7fce19a37c feat: silent background polling for briefing screen
Timer.periodic every 60s calls silentRefresh() on BriefingNotifier.
silentRefresh() patches AsyncData directly — never triggers AsyncLoading —
so existing content stays on screen while the fetch is in flight.
WidgetsBindingObserver pauses polling when app is backgrounded.
Polling is also skipped while a reply is actively streaming.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 17:43:06 -04:00
bvandeusen 46d0427901 feat: weather card and RSS reactions in briefing screen
- Parse metadata field on Message model (weather, rss_item_ids)
- Add WeatherCard widget: location, current temp, condition, today hi/lo,
  delta from yesterday, scrollable 3-day forecast strip
- BriefingScreen shows WeatherCard above the first assistant message that
  carries weather metadata (mirrors web BriefingView.vue behaviour)
- RSS reaction buttons (👍/👎) shown below assistant messages with
  rss_item_ids; optimistic toggle with rollback on failure
- Add postRssReaction / deleteRssReaction to BriefingApi

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 17:30:26 -04:00
bvandeusen 5ca5856f15 Project task view: tapping a project shows milestone-grouped task list
- ProjectLibraryCard.onTap navigates to /projects/:id/tasks
- New ProjectTasksScreen: milestone sections with coloured dot,
  done/total counter and thin progress bar; unassigned tasks at bottom
- Status cycle icon updates optimistically (pendingStatus map) so
  the list doesn't flash on every tap; syncs global tasksProvider
- New route /projects/:id/tasks registered in app.dart
- TasksApi.getByProject + TasksRepository + projectTasksProvider (family)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 21:40:14 -04:00
bvandeusen 0971433c7a refactor: remove briefing digest card, show full message list directly
The summary card duplicated the first assistant message and consumed
screen real estate without benefit on small screens. Replaced with:
- Full message list via ChatMessageBubble from the top
- SliverFillRemaining empty state ("No briefing yet today" + generate button)
  when no messages exist

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 19:08:21 -04:00
bvandeusen 844f68d376 fix: update loop, ghost queue item, briefing card scroll trap
- Update dialog no longer re-appears on every shell re-mount; check()
  is skipped if status is already available/upToDate/downloading
- Offline queue dequeue now happens before the mounted check, preventing
  ghost items when the widget disposes mid-drain
- Briefing digest card and messages now share a CustomScrollView so
  expanding the card doesn't trap the user without scroll access

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 08:23:24 -04:00
bvandeusen a687e3637f feat: upgrade Riverpod 3, go_router 17, google_fonts 8, package_info_plus 9
Migrate StateNotifierProvider/StateNotifier → NotifierProvider/Notifier,
StateProvider → NotifierProvider with simple notifier, FamilyAsyncNotifier
removed in Riverpod 3 (replaced with constructor-arg pattern), and rename
.valueOrNull → .value throughout all providers and screens.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 06:21:17 -04:00
bvandeusen def7519feb fix: resolve all flutter analyze warnings and infos
- app.dart: add braces to single-statement if body
- briefing_api.dart: escape <id> in doc comment (unintended HTML)
- notes_api.dart, tasks_api.dart, projects_api.dart: use null-aware
  map elements (?'key': value) instead of if-null guards
- task_edit_screen.dart: remove unused tasks_api.dart import
- task_edit_screen.dart, project_selector.dart: suppress
  deprecated_member_use on DropdownButtonFormField.value (value is
  the controlled-widget param; switching to initialValue would break
  current-selection display)
- project_selector.dart: use _ instead of __ for ignored error params
2026-03-12 00:36:05 -04:00
bvandeusen e86d1a59af fix: redirect to /briefing after auth (was /notes which no longer exists) 2026-03-12 00:32:38 -04:00
bvandeusen 6232c7c99a feat: app overhaul — Briefing-first navigation, Library, capture queue
Plans 1-3 implemented:

Plan 1 — Foundation
- Add google_fonts ^6.2.1
- lib/core/theme.dart: slate-indigo ColorScheme (dark/light), Fraunces
  headings, GradientButton widget
- lib/providers/capture_work_queue_provider.dart: in-memory sequential
  work queue; CaptureWorkQueueNotifier drains one item at a time;
  captureResultProvider feeds snackbars to UI
- lib/app.dart: wire fabledDarkTheme/fabledLightTheme; replace blocking
  _QuickCaptureBar with queue-based implementation (progress bar, badge)

Plan 2 — Navigation
- 3-tab shell: Briefing · Library · Chat (was Notes · Tasks · Projects · Chat)
- lib/screens/library/library_screen.dart: unified notes+tasks+projects list
  with filter pills, status sub-filter for tasks, live search, FAB
- lib/widgets/library_item_card.dart: NoteLibraryCard, TaskLibraryCard
  (status cycle), ProjectLibraryCard
- lib/screens/chat/conversations_tab_screen.dart: focused replacement for
  ConversationsListScreen
- Delete 5 dead screens: notes_list, tasks_list, project_list,
  conversations_list, quick_capture

Plan 3 — Briefing
- lib/data/models/briefing_conversation.dart
- lib/data/api/briefing_api.dart: getToday, getHistory, getMessages,
  triggerSlot
- lib/providers/briefing_provider.dart: BriefingNotifier with sendReply
  (optimistic + SSE + poll, same pattern as MessagesNotifier) and refresh
- lib/widgets/chat_message_bubble.dart: extracted + redesigned shared bubble
  (ghost user bubbles, left-accent assistant bubbles)
- lib/widgets/briefing_digest_card.dart: expandable first-message card
- lib/screens/briefing/briefing_screen.dart: digest card, conversation list,
  streaming reply bar, refresh button, history overflow menu
- lib/screens/briefing/briefing_history_screen.dart: read-only past dates

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 23:17:38 -04:00
bvandeusen 3c3055d536 Improve project-task integration and sub-task support
- TasksApi: add getSubTasks(parentId) + parentId param to create()
- TasksRepository: expose new API methods
- app.dart: invalidate projectsProvider + projectMilestonesProvider
  after quick capture creates a task; taskNew route reads ?projectId=
  query param and passes as initialProjectId to TaskEditScreen
- project_list_screen.dart: _AddTaskRow passes projectId query param
  so the task editor pre-fills the project on navigation
- task_edit_screen.dart: fully rewritten with initialProjectId support,
  sub-task fetching/creation, and _SubTasksSection widget

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-02 21:59:58 -05:00
bvandeusen 868cb0e49e Add milestone data layer and redesign Projects tab
Milestone data layer:
- lib/data/models/milestone.dart: Milestone model with progress fields
  (total, completed, pct) from backend progress endpoint
- lib/data/api/milestones_api.dart: CRUD client for
  /api/projects/:id/milestones
- lib/data/repositories/milestones_repository.dart: thin repository wrapper
- lib/providers/milestones_provider.dart: FutureProvider.family keyed by
  project ID — lazily fetched per project when expanded
- api_client_provider.dart: registers milestonesApiProvider and
  milestonesRepositoryProvider

Projects tab redesign (project_list_screen.dart):
- Each project is now an ExpansionTile showing open task count in subtitle
- Expanding a project fetches its milestones and groups tasks under milestone
  headers with inline progress bar (completed/total count)
- Tasks show status icon, due date with overdue highlighting, priority dot
- 'No milestone' section at bottom for unassigned tasks
- Tapping a task navigates to the task editor
- Long-press on project title opens archive/complete/delete bottom sheet
- 'New task' button at bottom of each expanded project section

Bump version to 26.03.02+1

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-02 21:33:45 -05:00
bvandeusen fceae5529d Add Projects feature; sync Note/Task models with backend additions
## New: Projects
- Project model, API, repository, Riverpod provider
- ProjectListScreen: active/archived sections, create dialog, long-press status/delete
- ProjectSelector widget: DropdownButtonFormField for note + task editors
- Projects tab added to shell (bottom nav + navigation rail, 4th position)
- /projects route registered in GoRouter ShellRoute

## Updated: Note model
- Added tags: List<String>, projectId: int?, milestoneId: int?
- NotesApi.create/update pass tags and project_id
- NotesRepository and NotesNotifier signatures updated
- NoteEditScreen: chip-based tag input + ProjectSelector

## Updated: Task model
- Added projectId: int?, milestoneId: int?, parentId: int?
- TasksApi.create passes project_id; update payload includes project_id
- TasksRepository and TasksNotifier signatures updated
- TaskEditScreen: ProjectSelector added; project_id sent on save

## Provider fix
- ProjectsNotifier.update renamed to updateProject to avoid conflict
  with AsyncNotifier.update(FutureOr<State> Function(State)) base method

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-02 20:52:59 -05:00
bvandeusen abf91874c3 Show specific error reason on setup screen connection failure
Instead of a generic "Could not reach server" for all failures, show
the actual cause: SSL cert error, timeout, or the raw connection error
message. This makes it possible to diagnose setup issues on-device
without needing a debugger.

Also extend connect timeout from 5s to 10s.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-01 14:48:49 -05:00
bvandeusen ccaec61de2 Fix setup screen always rejecting valid servers
Dio throws DioException for 4xx responses by default, so a server
returning 401 on /api/auth/status (correct behaviour for an
unauthenticated request) was caught and shown as "Could not reach
server", preventing the URL from ever being saved.

Set validateStatus: (_) => true so any HTTP response (200, 401, 404…)
counts as "reachable". Only actual network failures (no response) now
trigger the error message.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-01 14:38:01 -05:00
bvandeusen 467fa6a553 Migrate flutter_markdown → flutter_markdown_plus; fix app label
flutter_markdown was discontinued; flutter_markdown_plus is the
maintained drop-in replacement (same API, updated imports only).

Also set android:label to "Fabled" so the app appears correctly
on the home screen and in the app drawer instead of "fabled_app".

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-01 14:26:01 -05:00
bvandeusen ad3b317115 Add self-hosted update system via Forgejo releases
The app now checks for new APK releases on a Forgejo instance and can
download and install them without leaving the app.

Settings:
- New "Update repository URL" field (e.g. https://git.example.com/user/fabled_app)
- "App version" tile with a manual "Check" button and live status display
- Download progress bar and "Install" button appear when update is found

Startup:
- Shell runs a silent background check on first load when a repo URL is set
- A dialog appears automatically if a newer release is found

Provider (update_provider.dart):
- UpdateNotifier: idle → checking → available → downloading → available
- Hits Forgejo /api/v1/repos/{owner}/{repo}/releases/latest
- Parses semver tag, finds .apk asset, downloads with progress via Dio
- Opens the APK with open_file to trigger the system package installer

Android:
- REQUEST_INSTALL_PACKAGES permission
- FileProvider configured for open_file (shares APK URI with installer)
- res/xml/file_paths.xml covers external-files and cache directories

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-01 14:08:17 -05:00
bvandeusen 6e996d9d2e Fix black screen and failed delete when confirming note deletion
The dialog builder was discarding its own context (_) and calling
Navigator.pop(context, ...) with the outer screen context instead.
This popped NoteDetailScreen off the navigator instead of closing the
dialog, causing a black screen and returning null to showDialog so the
delete operation never ran.

Fix: use dialogContext in the builder and pop that instead.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-01 13:39:06 -05:00
bvandeusen d01978ab46 Fix FAB overlapping send button in wide/tablet master-detail layout
In wide mode, the floating action button defaulted to bottom-right of
the screen, sitting on top of the chat send button and note detail pane.

Replace the FAB with a pinned ListTile at the bottom of the list pane
in wide mode (New conversation / New note). The FAB is still used on
narrow/phone screens where there is no overlap.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-01 13:30:43 -05:00
bvandeusen 3c5f932327 Render and navigate [[wikilink]] syntax in notes
- Add WikilinkSyntax (InlineSyntax) that parses [[Note Title]] into
  anchor elements with a wikilink:// href
- wikilinkExtensionSet extends GFM with this syntax; shared by detail
  view and edit preview so rendering is consistent
- NoteDetailScreen handles onTapLink: wikilink:// hrefs look up the
  target note by title (case-insensitive) and push its detail route;
  unresolved links show a "Note not found" snackbar
- Add markdown as an explicit dependency (was previously transitive)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-01 12:55:06 -05:00
bvandeusen 45768e9bb8 Add master-detail layout for Notes and Chat on wide screens
On screens ≥600dp, Notes and Chat show a two-pane layout:
- Left pane (300dp): scrollable list with selection highlight
- Right pane: inline detail (NoteDetailScreen / ChatScreen)

Tapping a list item selects it in wide mode instead of pushing a route.
Creating a new conversation on wide screen auto-selects it in the pane.
Deleting a selected note/conversation clears the right pane.
NoteDetailScreen gains an optional onDeleted callback used by the
embedded pane to clear selection rather than call context.pop().
Tasks keep push-based navigation (edit form has save/pop semantics
that require a full-screen context).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-01 12:48:45 -05:00
bvandeusen 2d4c9a9f70 Adapt layout for wide screens and tablets
- Switch navigation breakpoint from orientation to width (≥600dp):
  NavigationRail shown on tablets in portrait and phones in landscape
- Chat bubble max-width capped at 480dp (prevents 640dp+ bubbles on tablets)
- Chat input maxLines reduced to 2 when width ≥600dp
- Task edit form centered and constrained to 600dp max-width on tablets

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-01 12:27:36 -05:00
bvandeusen eef101bef3 Fix bugs found in full code audit
- Edit screens (note, task): cache _initFuture in initState instead of
  calling _loadExisting() from FutureBuilder on every rebuild. Prevents
  race condition where multiple concurrent loads could overwrite edits.
  Also removes unnecessary _loaded flag and simplifies the pattern.

- Note editor: remove onChanged: (_) => setState((){}) on the body
  TextField — triggered a full rebuild on every keystroke for no reason.

- SSE streaming: use utf8.decode(chunk, allowMalformed: true) so a
  malformed byte sequence from the server skips rather than throwing an
  unhandled FormatException that escaped all catch blocks.

- Chat provider: guard msgs.isEmpty before accessing msgs.last during
  SSE chunk processing to prevent a potential index error on empty state.

- Offline queue drain: check mounted before and after each async gap in
  _drainQueue so ref is never accessed on a disposed widget.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-01 11:56:26 -05:00
bvandeusen 647b36837e Remove AppBar gap from notes and tasks list screens
Notes: no AppBar at all; a floating search icon (top-right corner)
triggers an inline search field above the list rather than an AppBar.

Tasks: no AppBar; TabBar sits directly at the top of the body, flush
with the quick-capture bar. The search icon lives to the right of the
tabs and expands a search field above them when tapped.

Both changes eliminate the ~56dp toolbar gap that was visible between
the quick-capture bar and the list content.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-01 11:49:46 -05:00
bvandeusen 2a35fe5532 Add search, offline queue, app icon, and UI polish
- Collapsible search in notes and tasks: magnifying glass in AppBar
  expands to a text field inline; close button resets the filter
- Offline capture queue: failed quick-captures (NetworkException) are
  persisted to SharedPreferences and retried automatically on next
  successful submit or app start; badge shows pending count
- App icon: book-with-sparkle logo from FabledAssistant SVG rendered
  at all Android densities with adaptive icon (indigo #6366f1 bg)
- Dark mode subtitle fix: use colorScheme.onSurfaceVariant for note
  preview and conversation timestamp text
- Remove swipe-to-delete (accidental deletions); long-press remains
- Chat: SSE streaming reliability, polling fallback, title patching
- Settings: theme toggle (system/light/dark) persisted to prefs
- Notes: delete button in edit screen; body preview in list
- Tasks: fix delete dialog context; description search support

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-28 23:15:06 -05:00
bvandeusen 4da36aa31d Initial commit: Fabled Android app
Flutter Android client for FabledAssistant with:
- Session-cookie auth via persistent cookie jar (Dio + cookie_jar)
- OAuth/SSO login via in-app WebView (flutter_inappwebview)
- Notes: list, detail (markdown render), create/edit
- Tasks: list with status tabs, create/edit with priority
- Chat: SSE streaming bubbles, conversation management
- Quick Capture FAB for rapid note/task creation
- Settings screen (change server URL, logout)
- Android home screen widget → opens chat
- Riverpod state management, go_router navigation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-28 21:28:53 -05:00