summaryrefslogtreecommitdiff
path: root/android/app/src
AgeCommit message (Collapse)Author
2026-08-14Fix tomorrow's all-day events using the wrong text colorPeter Stone
TomorrowSection de-emphasizes every other row to palette.textSecondary, but AllDayRow had no titleColor parameter and always rendered at palette.textPrimary -- so all-day/multi-day items (like a birthday) stayed bright while every timed event and task around them dimmed. AllDayRow now takes a titleColor (defaulting to textPrimary, which keeps today's section unchanged), and TomorrowSection passes textSecondary like it already does for TimeLabeledEventRow/TaskRow.
2026-08-12Add task title editing/deletion, timeline click-to-open, widget app launchPeter Stone
Task-detail modal was description-only with no delete affordance; HandleUpdateTask now saves the title too and a Delete button hits a new DELETE /tasks/{id} route backed by store.DeleteNativeTask, which repairs chain_position/unlocks the successor when the deleted task belongs to a chain. Timeline tab task/card/gtask rows now open the same detail modal as the Tasks tab. Android widget's "TODAY" header is now a tap target that launches DashboardActivity, since nothing previously opened the full app from the widget.
2026-08-12Fix tomorrow's task rows: color and alignment vs tomorrow's eventsPeter Stone
TaskRow always used textPrimary for its title, while tomorrow's timed events (TimeLabeledEventRow) are deliberately de-emphasized with textSecondary -- tomorrow's tasks now match. TaskRow also had no leading gutter of its own outside the hourly grid (where HourRow's hour-label column supplies it externally), so in TomorrowSection its title landed at a variable offset depending on checkbox/dot size and hideCheckboxes, instead of the fixed 32dp gutter AllDayRow and TimeLabeledEventRow use there. Added optional titleColor/gutterWidth params to TaskRow (both default to prior grid behavior) and set them from TomorrowSection. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017GMEkHeqKz6FLkmizowBTK
2026-08-09Widget: Quick Add dismisses instantly instead of blocking on networkPeter Stone
Tapping "Add" ran addTask() plus up to five sequential follow-up calls (description, due date, project, labels, recurrence) and a widget refresh, all inside the sheet's own lifecycleScope, before finish() -- the sheet sat on screen doing nothing visible for however long that chain took, reported as "sits black for a couple seconds." Moved the actual submission into a new AddTaskWorker, following the same pattern already established by CompleteWorker/DeferWorker: a CoroutineWorker enqueued fire-and-forget, so it survives the activity finishing (lifecycleScope wouldn't -- it's cancelled the moment the activity is destroyed). onAdd now does a local-only DataStore config check (fast, no network, so a missing server URL/token still doesn't silently eat what was typed), then confirms via toast and calls finish() immediately, mirroring the optimistic-dismiss pattern TaskDetailActivity.onComplete already uses. The worker preserves the original partial-failure reporting (a toast listing what didn't stick) for the rare case something after addTask itself fails, now delivered asynchronously via Toast.makeText posted to the main thread rather than blocking the sheet on it. Verification: this exact activity hit the same headless-emulator input limitation noted earlier this session (2026-08-06, QuickAdd keyboard focus) -- confirmed it's an environment constraint, not a regression, by trying both `input text` and raw `input keyevent` injection (which also failed) against a field that visibly has focus, on the same emulator where the same commands work fine for an equivalent OutlinedTextField in SettingsActivity. Verified instead by: go build/ test equivalent (./gradlew testDebugUnitTest, all passing -- the individual WidgetRepository calls AddTaskWorker orchestrates already have unit coverage in WidgetRepositoryTest.kt), a clean assembleDebug, and a crash-sanity launch on emulator-5556 with no FATAL in logcat. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ7ikw2ukGJFTHE3bJS7zL
2026-08-06Widget: real launcher icon, fix double title bar, rename app to "doot"Peter Stone
Icon: adaptive-icon vector launcher icon reusing web/static/favicon.svg's brand mark (indigo->purple gradient square, white checkmark) so the app and web dashboard visually match. minSdk 36 (per build.gradle.kts) means mipmap-anydpi-v26 alone is sufficient, no legacy PNG fallback needed. Double title bar: DashboardActivity inherited the app-wide Theme.DeviceDefault.DayNight, which has a native ActionBar, stacked on top of the new Compose TopAppBar from the last commit -- visibly two bars, showing "Doot" (static ActionBar label) above "Personal Dashboard" (the Compose bar tracking the web page's own <title>). Added Theme.Dashboard (NoActionBar) for the activity, and stopped tracking the WebView's document.title for the Compose bar's title text -- the dashboard is a single HTMX-swapped page (see DashboardActivity's class doc), so the title never meaningfully changes, and it was just producing a second, differently-branded piece of text. Renamed the app from "Doot Widget" to "doot" throughout (application label, widget-picker description, Settings screen heading) to match the project's actual branding. Also: the web Settings page's "Back to Dashboard" link was a plain <a href="/">, which in the WebView pushes a NEW history entry for "/" instead of reusing the one already on the stack -- so Settings <-> Home round trips kept growing the WebView back-stack ("zigzag"), and the in-app back arrow/hardware back key never actually unwound it. Now it calls history.back() when there's stack to pop, falling back to a plain navigation only if there isn't (e.g. Settings opened directly). Verified on emulator-5556: rebuilt and reinstalled, confirmed a single title bar (no native ActionBar behind the Compose one), confirmed the launcher icon renders (checked via Settings > App info, since this AVD's launcher doesn't expose an app drawer over adb), no crashes. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ7ikw2ukGJFTHE3bJS7zL
2026-08-06Widget: give Dashboard its own launcher icon, add toolbar navPeter Stone
DashboardActivity is now the app's single home-screen launcher entry (MAIN/LAUNCHER intent-filter), replacing SettingsActivity in that role. SettingsActivity keeps only APPWIDGET_CONFIGURE (still exported=true, since the launcher/home-screen process starts it directly during widget placement) and is now reached via a gear button in Dashboard's toolbar. DashboardActivity itself is rewritten from a bare setContentView(webView) to Compose Scaffold/TopAppBar wrapping the WebView via AndroidView, adding: - a back arrow (shown only when the WebView has history) instead of relying solely on the hardware back key - a settings gear action launching SettingsActivity - the page title tracked from the loaded page Also fixes a race in the original version: server URL was loaded via a lifecycleScope coroutine racing the WebView's own initialization order. Now it's plain Compose state (LaunchedEffect + AndroidView's update callback), so the WebView never loads before the URL is known. No native tab bar: web/templates/index.html's tabs are HTMX partials (hx-get targeting #tab-content), not separate pages, so loading "/" in the WebView already gets full in-app navigation for free. Verified on emulator-5556 (doot_test_api36): pm resolve-activity confirms DashboardActivity is the launcher default; launched it, confirmed no crash and topResumedActivity is Dashboard; configured a dummy server URL via Settings and confirmed the toolbar renders (title, gear icon) and the gear button correctly navigates to SettingsActivity (topResumedActivity becomes SettingsActivity) with no crash. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ7ikw2ukGJFTHE3bJS7zL
2026-08-06Widget: fix QuickAdd keyboard-focus race, force widget re-render on savePeter Stone
QuickAddActivity's autofocus used delay(150) then requestFocus() + keyboard.show() -- a fixed delay racing against the window actually gaining focus. This is a translucent, separate-taskAffinity popup activity, so window focus transfer is variable/device-dependent; a show() call made before the window is focused is silently dropped by the IME service, no error, no retry -- the classic cause of "keyboard doesn't appear until I tap out and back in" (reported 2026-08-06, "more consistent here" than the same general Android quirk elsewhere). Fixed to react to LocalWindowInfo.isWindowFocused instead of guessing a timing window, plus windowSoftInputMode="adjustResize|stateVisible" as an OS-level second line of defense. Tried to verify live and hit a real limitation worth recording: this box's AVDs run -no-window (headless), and in that mode mInputShown never reports true via dumpsys input_method even for a manual, deliberate tap on the field -- confirmed by testing a plain tap directly, independent of any app code. The harness can't observe IME visibility here, so this fix is verified by code-level reasoning (LocalWindowInfo-driven focus is the standard, documented fix for this exact bug class) and confirmed window-focus DOES transfers correctly (mServedView moves to the bottom sheet's window), not by watching the keyboard actually appear. Real confirmation has to happen on-device. Also: SettingsActivity.saveAndFinish() only ever called DootWidget().updateAll() indirectly, as a side effect of RefreshWorker succeeding its network fetch -- so a slow or failing request could delay or block the widget from reflecting a setting the user just saved, even though every setting saved there (theme, text size, background, checkboxes) is already fully local and needs no network round trip to take effect. Now calls updateAll() directly and immediately after writing prefs; RefreshWorker still runs afterward to separately pull fresh server data. Verified installable and crash-free on a real API 36 emulator. Deployed as doot-widget.apk.
2026-08-06Widget: ship Material You theming, past-events restructuring, legibility ↵Peter Stone
fixes, new settings This lands the color-theming work that had sat uncommitted since a prior session (2026-07-28) -- every build published in between stripped it out deliberately to avoid shipping unreviewed work -- plus a full round of fixes and new features layered on top since it finally shipped: Theming (WidgetPalette.kt, new): - 5 themes now: NEUTRAL/ACCENT/TONAL (wallpaper-derived via Material You), VIVID (new -- all three text roles pull from a different accent slot instead of anchoring primary to neutral, for real hue variety), CLASSIC (fixed, wallpaper-independent). - Settings picker redesigned to match Android's native wallpaper "Basic colors" circular swatches (bottom half + two top quadrants, filled with each theme's actual buildWidgetPalette() output, not an approximation). - Per-source accent colors (colored checkboxes/bars) fully removed from the grid. Past events (DootWidget.kt, WidgetRows.kt): - Already-ended-today events pulled out of the hourly grid, shown as list rows above it instead (matching how past tasks already float) -- fixes the grid's start hour getting stretched backward by stale events. Legibility (WidgetRows.kt): - ShadowedText upgraded from a single-corner drop shadow to a 4-corner halo/outline (protects all sides of a glyph, not just one). - Halo color now tracks each palette's text luminance (dark halo for light text, light halo for dark text) -- a hardcoded black halo behind already-dark light-mode text was doing essentially nothing. Bumped opacity 0.45/0.55 -> 0.65/0.7 as the cheap, low-risk strength dial. New settings (SettingsActivity.kt, DataStore.kt): - Background transparency slider (0-85%, default 0% unchanged) -- exposed for testing per explicit request, not a default change. - Hide-checkboxes toggle: drops the leading checkbox/dot element entirely (not just hides the icon) so task titles land flush with event titles; tap-to-complete-from-widget trades off for TaskDetailActivity's Complete button. Also: today's moon phase in the TODAY header (moonPhaseEmoji, pure date computation, no network) -- verified against direct calculation before writing test assertions, not hand-computed. Removed the unused glance-material3 dependency (verified zero usages before removing; turned out to save ~2KB, not the ~280KB expected, since material3 itself already pulls the same transitive deps -- noted honestly rather than oversold). Every new pure-logic piece has unit tests (isPastEvent, moonPhaseEmoji, theme construction) -- 59 total, all green. Verified installable and crash-free via a real API 36 emulator launch before each publish, not assumed. Deployed as doot-widget.apk.
2026-08-06Widget: wrap the web dashboard in a WebView (DashboardActivity)Peter Stone
First cut per the 2026-08-06 feasibility check (verdict: easy, no architecture blockers): a plain WebView pointed at the configured server URL, cookie jar enabled for the existing session-cookie login (internal/auth/middleware.go's RequireAuth) -- no separate auth bridge needed, the widget's own bearer token is a completely different scheme and doesn't need to touch this at all. External links (e.g. a calendar event's source URL) escape to the user's real browser instead of getting stuck in the WebView. No deep-linking to specific tabs, no native-rendered chrome -- this is the minimal first step to validate the wrapped experience is worth building further, not the final shape.
2026-08-06Widget: add postpone (tomorrow/next week/next month) to task detail popupPeter Stone
Alongside Complete/Edit, doot-native tasks now get a Postpone button with a dropdown (Tomorrow / Next week / Next month), reusing the existing reschedule wiring (WidgetRepository.reschedule) the due-date picker already uses. Caught and documented a real divergence while writing the test: Java's LocalDate.plusMonths CLAMPS to the target month's last valid day (Jan 31 -> Feb 28), while the Go server's ComputeNextOccurrence (recurrence math) OVERFLOWS instead (Jan 31 + 1 month -> Mar 3) via time.AddDate. Verified by actually running both, not assumed -- a first draft of this test asserted the wrong (Go-style) behavior before checking. Not reconciled here, just accurately documented as a known inconsistency between this client-side helper and the server's date math. 4 new tests, all passing.
2026-08-05Widget: fix "complete works once, then stops" race between overlapping ↵Peter Stone
completions Root cause traced from server logs, not guessed: every completion request was succeeding server-side (100% 200s, including a 5-tap burst spanning different tasks), and each successful completion's response payload was correctly shrinking. So the failure wasn't dispatch or network -- it was that a successful completion could still get silently undone client-side. fetchAndPersist does an unconditional full overwrite of the cached item list on every successful GET. CompleteWorker/DeferWorker run one instance per task id with no ordering guarantee between different ids' workers (different unique work names, no KEEP protection across them -- that protection only ever covered same-task double-taps). So: tapping complete on task A starts a GET that's still in flight; tapping complete on task B before A's GET returns optimistically removes B locally; A's slower GET response, captured before B's completion landed, then overwrites the cache and silently resurrects B. Fix: track locally-optimistic removals with a timestamp (PendingRemovals.kt) and filter them out of every fetchAndPersist write for a bounded TTL (2 min), regardless of which worker's fetch is doing the writing. The TTL means a completion that never actually confirms (permanent network failure) still self-heals via the next periodic refresh, matching an existing self-healing property already relied on elsewhere in this codebase, instead of hiding the task forever. Added PendingRemovalsTest.kt (pure-function unit tests, no Android runtime needed) covering the exact race scenario plus TTL expiry and edge cases. Verified the tests actually catch a regression by deliberately reverting the fix to a no-op against a real backup, confirming 3 tests failed with the exact expected assertion, then restoring and confirming green again. Built, tested, and published as doot-widget.apk.
2026-08-04Widget quick-add: surface errors instead of failing silentlyPeter Stone
addTask()'s failure path had no .onFailure handler, so a failed POST (network hiccup, server down) left the Add sheet just sitting there with zero feedback -- looked like the button did nothing. Also: the five calls chained after a successful addTask (updateTask, reschedule, setTaskProject, setTaskLabels, setTaskRecurrence) were still unchecked, so a task could get created with silently-dropped metadata even with the addTask fix in place. Both paths now collect and toast what failed; the task itself still gets created and the sheet still closes, since aborting would leave a task that already exists server-side with no way to tell the user without a duplicate.
2026-07-18fix(widget): wrap TomorrowSection in a Column to stop rows overlappingPeter Stone
TomorrowSection emitted its divider, header, and every event/task row as top-level siblings with no shared layout container. Since the whole function is composed inside a single LazyColumn item{} slot (one RemoteViews node), nothing told Glance to stack them vertically -- they all rendered on top of each other instead of flowing top-to-bottom. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ7ikw2ukGJFTHE3bJS7zL
2026-07-17Implement linear task chains and recurring maintenance bucketsPeter Stone
Backend, web timeline, and Android widget wiring for the last two unimplemented items from doot-future-task-scheduling-ideas. Chains: task_chains table + chain_id/chain_position/chain_unlocked on native_tasks (migration 026), WIP-limit-1 advancement hooked into CompleteNativeTask, locked tasks excluded from all date-based queries, 5 new /api/widget/chains* endpoints, an N/M position badge on web and Android widget rows. Buckets: maintenance_buckets table + bucket_id/bucket_state/ bucket_last_active_at on native_tasks (migration 027), staleness-then-priority selection scoring, a new RunBucketCycleCheck scheduler loop, 5 new endpoints including the distinct Defer action, a Defer button on web and Android widget rows. Also corrected stale "not yet approved" status headers on the two already-shipped specs this work depended on (labels/projects, budgets/ availability) -- their headers were never updated after implementation. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ7ikw2ukGJFTHE3bJS7zL
2026-07-17fix(widget): autofocus quick-add title field, redesign as blank edit formPeter Stone
Quick Add's text field never requested focus or triggered the keyboard, so tapping it opened a sheet with no visible way to type. Added a FocusRequester + delayed requestFocus()/keyboard show (ModalBottomSheet needs a beat to finish its enter animation before focus requests land). While in there, rebuilt the quick-add sheet to match the task edit popup instead of being a bare title field: due date, recurrence, project, and label chips, plus a description field, reusing the same dialogs the edit popup already uses. POST /api/widget/add now returns the created task's id so the client can chain the same project/labels/recurrence/due-date setter calls edit already relies on.
2026-07-16Show scheduled/available badge on widget TODAY headerPeter Stone
Threads the /api/widget budget_status field through the Android widget's DataStore-backed cache (a new BUDGET_STATUS_JSON pref, since the widget decomposes WidgetResponse into individual prefs rather than caching it whole) and renders a small "Nm/Nm" badge next to TODAY when there's a nonzero tracked load.
2026-07-16feat(widget): show project color as a small accent on task rowsPeter Stone
2026-07-16feat(widget): add project picker and label editor to task detail popupPeter Stone
2026-07-16feat(widget): add Android data layer for projects and label colorsPeter Stone
2026-07-16refactor(widget): split DootWidget.kt by responsibilityPeter Stone
DootWidget.kt (582 lines) mixed the GlanceAppWidget entry point/root composable, all the row-rendering composables, and multi-day-event detection logic in one file. Split into: - MultiDayEvents.kt: MultiDayVariant, effectiveEndDay, isMultiDayEvent, multiDayVariant, multiDayLabel, timeSuffix. - WidgetRows.kt: sourceColor, calendarViewIntent, and all row/section composables (AllDayRow, RefreshButton, QuickAddButton, HourRow, EventBlock, TaskFragmentBlock, TaskRow, TomorrowSection, TomorrowEventRow), plus hourLabel/calcGridStart/calcGridEnd. - DootWidget.kt: just the GlanceAppWidget class and WidgetRoot, now 145 lines. All same package (org.terst.doot.widget.ui), so no import changes needed for cross-file references. Pure move, no behavior change -- full unit test suite (including DootWidgetGridTest/ DootWidgetMultiDayTest, which call the internal functions directly) passes unchanged. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VTUSAEKfsPc6WGDq45yPHD
2026-07-16fix(widget): redesign recurrence dialog layoutPeter Stone
The 4 frequency chips and 7 weekday chips were each laid out in a single non-wrapping Row -- on a real phone width these overflow/get cut off rather than wrapping. The interval field was a full-width OutlinedTextField with the unit baked into its label ("Every N dailys"/"weeklys" -- ungrammatical for anything but weekly), and the whole dialog had no visual grouping. Redesigned: FlowRow (wraps instead of overflowing) for both chip rows; single-letter weekday chips (S M T W T F S) to stay compact; a narrow fixed-width (72dp) interval field paired with a correctly-pluralized unit label rendered separately from the field; muted section labels (REPEATS / EVERY / ON THESE DAYS) for structure. No callback/API changes -- purely a layout rewrite of the same onSave/onClear/onDismiss contract. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VTUSAEKfsPc6WGDq45yPHD
2026-07-16fix(widget): match task font color to events; complete instantlyPeter Stone
TaskRow used a flat hardcoded gray (0xFFDDDDDD) while EventBlock used Color.White (dimmed only when past) at the same size/weight -- the color mismatch read as a font difference. Both now use the same Color.White base. CompleteTaskAction previously did no local update at all: the row stayed visible until CompleteWorker's full complete() -> fetchAndPersist() -> updateAll() round trip finished (two sequential network calls). It now optimistically removes the completed item from the cached list and re-renders immediately, matching RefreshTaskAction's existing synchronous-flag-then-updateAll pattern -- the background worker's own fetchAndPersist still replaces this with the authoritative server state (including a newly-created recurring successor, if any) moments later. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VTUSAEKfsPc6WGDq45yPHD
2026-07-16fix(widget): re-arm periodic refresh on every onUpdate, not just onEnabledPeter Stone
onEnabled only fires when the first widget instance is added to a home screen, so if the periodic RefreshWorker job is ever silently dropped (an app reinstall can clear WorkManager's persisted schedule without the widget itself being removed/re-added -- exactly what happens when sideloading a new APK build, as opposed to a Play Store update), there was no way for it to come back except manually removing and re-adding the widget. onUpdate fires far more often (reboot, periodic OS ticks) and now also re-arms the schedule (a no-op via KEEP if already running). Root-caused via manual refresh restoring all missing content instantly (rules out a data/rendering bug) plus the widget only showing one stale morning event beforehand (consistent with the periodic job having stopped ticking hours earlier, right around today's APK reinstalls). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VTUSAEKfsPc6WGDq45yPHD
2026-07-16fix(widget): remove red text color for overdue tasksPeter Stone
Overdue and non-overdue tasks now render with the same plain title color; no more red highlighting for overdue items. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VTUSAEKfsPc6WGDq45yPHD
2026-07-16feat(widget): redesign task detail popup with editing and recurrencePeter Stone
Editable title/description (Edit -> Cancel/Save toggle), linkified description, and independently-tappable date/recurrence/next-date chips. Non-doot sources (Trello, Google Tasks) are unaffected -- same title + Complete button as before, no live fetch, no edit UI.
2026-07-16feat(widget): add description linkification for URLs and phone numbersPeter Stone
2026-07-16feat(widget): add WidgetRepository methods for task detail/recurrencePeter Stone
2026-07-16fix(widget): fix all-day alignment, drop duplicate TODAY, boost label ↵Peter Stone
legibility, soften overdue red - AllDayRow now has the same 32dp leading gutter as HourRow's hour-label column, so its color bar lines up with EventBlock's bar in the grid below instead of sitting flush left; also dropped the title's defaultWeight() so the multi-day label sits right after the title instead of being pushed to the far-right edge. - TaskFragmentBlock no longer renders its own "TODAY" sub-header -- it's always inside the already-labeled TODAY section, and Tomorrow's floating tasks never had this redundant label to begin with. - Section headers and hour/time labels bumped from ~30-40% to ~50-60% opacity -- against a home-screen wallpaper the previous values were barely legible. - Overdue task text alpha reduced to 0.75 -- full-opacity bright red read as too alarming.
2026-07-16fix(widget): scale content font sizes down 5% across all text-size tiersPeter Stone
2026-07-16fix(widget): render multi-day starts/ends label at full opacityPeter Stone
The label was concatenated into the title string, inheriting its 0.9 alpha. Split into its own Text at full opacity, matching EventBlock's treatment of upcoming (non-past) events.
2026-07-16feat(widget): show multi-day calendar events on every day they spanPeter Stone
Multi-day events (Start and End on different calendar days) are pulled out of the normal grid/all-day pipeline and rendered as an all-day-style row on every day they touch (Today and/or Tomorrow), labeled starts/ends/plain per the day being rendered. Previously such an event either only appeared in the single hourly grid slot matching its start time (never again on later days) or, if genuinely flagged all-day, never had its End forwarded at all.
2026-07-16feat(widget): tap event to open its source directly, drop detail popupPeter Stone
Tapping a calendar event (or any event-type item) now opens the event's URL (Google Calendar, Plan to Eat, etc.) directly instead of showing an intermediate popup with an "Open in Calendar" button. Removes EventDetailActivity and the recurrence-schedule lookup it was the only consumer of: WidgetRepository.getRecurrence, the Go /api/widget/recurrence endpoint, HandleWidgetRecurrence, GoogleCalendarAPI.GetRecurrenceRule, and formatRecurrence, plus their tests. RecurringEventID itself stays -- it's general calendar-sync metadata used elsewhere in the timeline pipeline, not exclusive to the removed popup.
2026-07-16fix(widget): give Text Size label and chip labels explicit colorsPeter Stone
These were the only labels in the settings screen without an explicit color, unlike the rest of the screen's text elements.
2026-07-16fix(widget): floor header text size/weight at NORMAL even when SMALLPeter Stone
Headers (hour labels, section titles) shrinking along with content at the SMALL setting made them too small relative to content. Content still shrinks at SMALL; headers now stay at NORMAL's scale/weight regardless of the selected tier.
2026-07-16fix(widget): give popup activities distinct taskAffinityPeter Stone
TaskDetailActivity, QuickAddActivity, and EventDetailActivity shared the app's default task affinity with SettingsActivity. FLAG_ACTIVITY_NEW_TASK reuses an existing task with matching affinity rather than creating a fresh one, so if SettingsActivity's task was still alive in recents, these translucent popups rendered on top of it instead of the home screen behind them, as the theme's transparency was designed to show.
2026-07-16chore(widget): remove dead sp importPeter Stone
Unused after threading WidgetTextSize's scaling functions through every Text() call site that previously used a literal .sp value.
2026-07-16fix(widget): restore trailing-slash trim on Test button's URLPeter Stone
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VTUSAEKfsPc6WGDq45yPHD
2026-07-16feat(widget): add Small/Normal/Large text size picker to widget settingsPeter Stone
2026-07-16feat(widget): thread WidgetTextSize through all widget text renderingPeter Stone
2026-07-16feat(widget): add WidgetTextSize enum with independent header/content scalingPeter Stone
2026-07-16test(widget): pin JVM default timezone in DootWidgetGridTest for determinismPeter Stone
2026-07-16fix(widget): stop tomorrow's events from stretching today's grid boundsPeter Stone
calcGridStart/calcGridEnd only looked at hour-of-day, so a tomorrow event's early or late hour could inflate today's grid range with empty rows, pushing the non-scrolling TomorrowSection below the widget's visible area. Filter to today-only events before computing bounds.
2026-07-13Merge github/master: reconcile with parallel widget workPeter Stone
Another session pushed 27 commits in parallel covering quick-add, event detail popups, recurrence display, overdue badges, a manual refresh button, and its own fix for the same overdue-tasks bug (via a separate GetOverdueNativeTasks fetch folded into BuildTimeline, rather than widening GetNativeTasksByDateRange's bound directly). Reconciled rather than blindly taking one side: - Reverted GetNativeTasksByDateRange to its original bounded query and kept upstream's GetOverdueNativeTasks + BuildTimeline fold-in as the sole overdue mechanism for native tasks, to avoid double-counting overdue items (my widened query + their separate fetch would have both returned them). Re-pointed the regression test at the now-correct contract and added a store-level test for GetOverdueNativeTasks directly. - Kept my GetGoogleTasksByDateRange fix as-is (single unbounded query) -- upstream never touched Google Tasks overdue handling, so there's no duplication risk there. - Rewove WidgetRoot's LazyColumn structure (added for scrolling) around upstream's new header buttons, pinned all-day event rows, and the enhanced TomorrowSection, none of which were written LazyColumn-aware since that work landed on this side only. - Combined both sides' additions to TaskDetailActivity/TaskDetailSheet (description-edit detail popup + due-date reschedule label) and WidgetRepository/Actions (optimistic local removal + refresh button wiring) -- these were independent, non-overlapping features that both needed to survive. - Renumbered the migration collision: both sides independently added a migration numbered 022. Card-description was already applied to the live production DB under that filename earlier this session (migrations are tracked by filename), so it keeps 022; the recurring-event-id migration, never deployed under any name here, moves to 023. Verified: go build clean, full test suite passes (only the two pre-existing agent-handler failures and the pre-existing models package build error remain, both confirmed unrelated via git stash before this session began), and a dry run against a copy of the live production database applies both migrations cleanly with no re-run conflicts. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ7ikw2ukGJFTHE3bJS7zL
2026-07-13feat(widget): editable task details, scrollable list, and overdue-task fixesPeter Stone
- Add description editing to the widget's task detail popup for doot/gtasks/trello, backed by new GET /api/widget/detail and POST /api/widget/update endpoints - Make Google Tasks and Trello cards completable via the widget (Trello completion archives the card); fix Trello description never being fetched, which meant saving could silently wipe a card's real desc - Fix google_tasks.due_date/updated_at (TEXT columns) never round-tripping through sql.NullTime, which broke cached Google Tasks reads whenever the cache was valid - Fix native-task and Google-Task date-range queries excluding anything due before the window start, which dropped incomplete tasks off the widget the moment their due day passed (the "overdue tasks disappeared" bug) - Fix native task description edits blanking the task's title - Make the widget's day list scroll (LazyColumn) instead of clipping - Optimistically remove a task from the widget immediately on completion, ahead of the authoritative background refresh Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ7ikw2ukGJFTHE3bJS7zL
2026-07-12feat(widget): add event detail popup showing recurrence schedulePeter Stone
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VTUSAEKfsPc6WGDq45yPHD
2026-07-12fix(widget): add IME handling to quick-add's keyboard-covering input fieldPeter Stone
2026-07-12feat(widget): add quick-add button and entry sheetPeter Stone
2026-07-12feat(widget): make the due-date display the reschedule tap targetPeter Stone
2026-07-12feat(widget): color overdue task titles distinctlyPeter Stone
2026-07-12feat(widget): add manual refresh button with loading-state iconPeter Stone