- fix(ui): delay material icons until font is ready
- fix(side-nav): wire up tree readiness gate and clean up dead code
- fix(ui): hide icon ligature fallback text
- fix(side-nav): defer data-driven startup content
- fix(side-nav): stabilize magic nav initial render
isOnboardingInProgress() only covered the phase after preset selection.
Fresh users on the preset selection screen still saw the snack because
ONBOARDING_PRESET_DONE was not yet set. Extend the check to also return
true when neither preset nor skip-tour flags are set.
Google's Desktop OAuth client requires loopback redirect URIs
(http://127.0.0.1:<port>) and blocks embedded webviews. This replaces
the custom URI scheme + BrowserWindow approach with a temporary loopback
HTTP server and shell.openExternal for the system browser.
- Add PLUGIN_OAUTH_PREPARE IPC to start loopback server and return port
- Open auth URL in system browser instead of Electron BrowserWindow
- Make getRedirectUri() async to support IPC port retrieval
- Validate OAuth config before starting loopback server to prevent leaks
- Force 200 status on OPTIONS preflight responses in CORS bypass
tools/load-env.js requires dotenv directly but it was only available as
a transitive dependency of @dotenv-run/cli. This caused the Docker image
publish to fail when npm ci couldn't resolve it. Also regenerates
package-lock.json to fix stale lock file entries.
Stale references (projectId/tagId/repeatCfgId pointing to deleted
entities) in archived tasks are now treated as harmless historical
records instead of validation failures.
- Validation: logs stale archive refs as info, does not fail
- Repair: no longer mutates stale refs in archives (structural fixes
like missing projectId→INBOX and TODAY_TAG stripping are kept)
- Restore: normalizeRestoredTask() cleans stale refs when restoring
archived tasks to active state (INBOX reassignment, tag stripping,
TODAY_TAG removal, repeatCfgId clearing)
Add swipe-right-to-done gesture with animated strikethrough line that
follows the finger, checkmark scale-up on trigger, and smooth visual
bridging so the gesture visuals persist until the CSS isDone class
takes effect.
Multiple error paths in the sync pipeline silently swallowed errors,
allowing sync to report IN_SYNC while clients had diverged state.
This caused permanent data loss — tasks missing on one device but
present on another, with no indication anything was wrong.
Fixes:
1. Propagate download failure: when OperationLogDownloadService
returns success=false, throw instead of treating as "no new ops".
Prevents lastServerSeq from advancing past failed downloads.
2. Throw on LWW conflict apply failure: autoResolveConflictsLWW
now throws when applyOperations fails, matching the behavior of
applyNonConflictingOps. Prevents lastServerSeq from advancing
past failed conflict resolutions.
3. Propagate rejected ops handler errors: rethrow instead of
swallowing in the uploadPendingOps catch block, so the
sync-wrapper can set ERROR status.
4. Surface validation failure: validateAfterSync now checks the
return value from validateAndRepairCurrentState and shows an
error snackbar when validation fails.
5. Set ERROR status in sync-wrapper catch-all: the generic error
handler now calls setSyncStatus('ERROR') so the sync icon shows
the red error state.
Fixes#6571
Replace the accent-colored block + checkmark icon on swipe-right with
a dynamic strikethrough line that follows the touch position. The line
extends from left to right tracking the finger, transitions at the
trigger threshold, and snaps to full width on completion.
For undo (swiping on done tasks), the native text-decoration
line-through fades out via text-decoration-color animation, task
opacity increases from dimmed to full, and the checkmark unchecks.
Key implementation details:
- Strikethrough Y position adapts to task title center, with
even-line-count offset for multi-line titles
- Undo uses text-decoration-color animation on .display-value
- showUndoneAnimation input on done-toggle removes is-done class
- Left swipe (context menu) behavior is unchanged
- Zero performance impact on desktop (IS_TOUCH_PRIMARY guard)
Add e2e test verifying the day-change trigger correctly creates new
repeat task instances when midnight passes. Uses Playwright clock
manipulation (setFixedTime) to control Date.now() while keeping real
timers running.
Document a known intra-day gap in TaskDueEffects: once addAllDueToday()
fires for today (via distinctUntilChanged on todayDateStr$), there is no
retry mechanism within the same day. Investigation verified day-change
trigger, sync buffering, and data loading guards all work correctly in
isolation — the exact real-world trigger remains unidentified.
Add diagnostic logging in addAllDueToday() to help users reproduce and
identify the failure point (repeat config count + IDs, sync window state).
Replace the browser's native <input type="color"> with a custom color
picker component showing a 5x4 grid of preset swatches and a custom
color button that opens the native picker.
- New standalone InputColorPickerComponent with overlay panel
- 19 preset colors in rainbow hue order + 1 custom color button
- Formly ColorInputComponent wraps the new picker
- dialog-create-tag uses the new picker directly
- Escape key and backdrop click to close overlay
- Fixed positioning to avoid dialog overflow clipping
Today's tasks from the NgRx store now render immediately without waiting
for the archive IndexedDB read. Archive tasks are appended progressively
when they arrive.
Changes:
- Use combineLatest with startWith([]) on archive stream so today tasks
emit instantly while archive loads in the background
- Add isArchiveLoaded signal with subtle opacity indicator on summary
cards while archive data is loading
- Defer confetti animation until first non-empty task emission + 300ms
paint buffer, instead of a fixed setTimeout
- Show spinner only when tasks are empty and archive hasn't loaded yet
Google blocks OAuth authorization in embedded WebViews (Error 400:
invalid_request). Use @capacitor/browser to open the system browser
on mobile instead of window.open() in the WebView.
https://claude.ai/code/session_01QWSy4NhhNQNMD2GJwqoYTc
Co-authored-by: Claude <noreply@anthropic.com>
The stale client reconnection test was flaky because syncAndWait()
was called immediately after clicking done-toggle, racing with the
200ms animation delay before isDone is dispatched to the store.
Use markTaskDone helper and wait for state to persist before syncing.
Set pointer-events: none on the bounding box and overlay pane so taps
pass through to the CDK backdrop, and re-enable pointer-events on the
menu panel itself. Also remove the task title header from the top of the
context menu on touch devices.
--c-light-60 (white at 60% opacity) does not create a visible scrim on
a light background. Switch to --c-dark-60 (black at 60% opacity) to
match the Material Design M2 default.
- Add task bar, dialogs, menus, and cards use #fff in light mode instead
of off-white (#f8f8f7)
- Schedule events and repeat task cards are transparent
- Override --schedule-event-bg-in-side-panel for upcoming panel
Add a global MAT_DIALOG_DEFAULT_OPTIONS provider that sets autoFocus: false
on touch-primary devices, preventing virtual keyboard popup when dialogs open.
Remove redundant per-dialog autoFocus overrides that are now handled globally.
* insert ** and _ when pressing Ctrl+B and Ctrl+I respectively
* add test for shortcuts for bold and italic text
* change `fit` to `it`
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* support bold/italic shortcuts in macOS
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* change fit to it
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* fix unclosed curly braces
* fix redundant test
* don't set this_model directly
* place check for Ctrl+b and Ctrl+i before handleListKeydown
* add blank line between block
* fix typos in test suite
* fix typo
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
- Wrap heatmap in collapsible "Activity" section, collapsed by default
- Lazy-load heatmap only when expanded to avoid loading full archive
- Disable autoFocus at all dialog call sites to prevent keyboard popup
- Icon-only action buttons on small screens with aria-labels
- Rename "Remove for" to "Skip for" with consistent confirmation dialog
- Save button raised, Remove button warn color
The done-toggle was refactored from an inline SVG with class="done-toggle"
into a standalone Angular component <done-toggle>. Update all e2e selectors
from '.done-toggle' (class) to 'done-toggle' (element) to match.
Lock cdkDragLockAxis to Y on small screens (<600px) for task-list and
planner components, matching the breakpoint where planner switches to
single-column and bottom nav appears. Nav menu tree always locks Y.
Uses reactive LayoutService.isXs() so orientation changes work correctly.
Throttle tree-dnd onDragMoved with requestAnimationFrame to reduce
layout thrashing from getBoundingClientRect/elementFromPoint calls.
Add safeStopSelf() to TrackingForegroundService and FocusModeForegroundService
that calls startForeground() before stopSelf() in fallback branches (missing
task ID, stop when not running, null/unknown action). Android requires
startForeground() within 10s of startForegroundService().
Replace ambiguous NullPointerException in CapacitorMainActivity.onCreate with
a descriptive IllegalStateException when Capacitor bridge fails to initialize.
Add safe-call operators in onSaveInstanceState/onRestoreInstanceState.
- moveItemAfterAnchor: preserve current position when anchor is
concurrently deleted instead of appending to end (which corrupted
ordering on remote clients). For cross-list moves, append to end
as fallback to prevent data loss.
- Prevent double-write of deferred actions: track buffered actions in
a WeakSet and filter them in the effect so they are only written
once by processDeferredActions().
- Propagate skipDequeue through handleQuotaExceeded retry path to
prevent queue desync when deferred actions hit storage quota.
- Add e2e tests for concurrent delete + reorder sync scenarios.
Angular emulated ViewEncapsulation shares the same _ngcontent attribute
across all instances of the same component type. The :host:focus
.inner-wrapper > .box selector therefore matches subtask .box elements
when the parent task is focused. Override with a more specific selector
that includes .sub-tasks to reset border-color back to defaults.
Switch task-title edit-mode trigger from mousedown to click so that
mousedown propagates freely to CDK drag. CDK's built-in 5px threshold
distinguishes drag from click naturally.