Commit graph

19903 commits

Author SHA1 Message Date
Johannes Millan
e9ee2c4e45 fix(task-view-customizer): scope Group By to current work context (#7279)
Group By: Tag / Group By: Project pulled tasks from every project when
used inside a specific project view, because de1b5d485c (fix for #7050)
deliberately switched the source to all undone tasks for these two
options. That closed #7050 but broke the more common case reported in
#7279 (and its duplicate #7290).

- TaskViewCustomizerService: revert customizeUndoneTasks() to the work
  context's undoneTasks$, drop _allUndoneTasksWithSubTasks$ and the
  cross-store selector/switchMap.
- Hide Group By: Project in a PROJECT context (it would collapse into
  one group) via a new availableGroupOptions computed signal backed by
  isActiveWorkContextProject$.
- Sanitize any stored group=project state inside the service's
  context-load subscription, not from a lazy panel effect — the panel
  is only instantiated when the mobile bottom-sheet customizer opens.
- Drop the now-redundant cross-context selection guard in
  WorkViewComponent and retire its #7269 test block: after this fix
  customizedUndoneTasks.list is always a subset of undoneTasks, so the
  scenario those tests constructed can no longer occur.
- Stub isActiveWorkContextProject$ in all WorkContextService test
  doubles so the service's toSignal() call has something to subscribe
  to.

Closes #7290.
2026-04-20 15:48:40 +02:00
Johannes Millan
ecba6c4240 docs(research): add §16 field data from #7270 follow-up
First post-release report on PR #7266 (v18.2.4): X11 widening fires
but doesn't rescue user on Ubuntu 24.04 + Intel Arrow Lake-P. Mesa
DRI still fails, GPU process segfaults in a loop. Lowers the 95%
estimate, promotes --disable-software-rasterizer to must-ship, and
upgrades core24/gpu-2404 migration urgency to 18.3.
2026-04-20 15:48:40 +02:00
Johannes Millan
2cefde61bf fix(electron): keep window reachable when show/hide shortcut has no tray
The globalShowHide shortcut called mainWin.hide() unconditionally. On
Windows/Linux hide() removes the taskbar entry, so with no tray icon
the window became unreachable.

- macOS: use hide() (dock icon always remains).
- Windows/Linux: hide to tray only when minimize-to-tray is on AND the
  tray was created; otherwise minimize() to keep a taskbar handle.
- Log whether Windows tray creation used the GUID path, to help diagnose
  cases where the icon lands in the hidden overflow area.

Refs #7282
2026-04-20 15:48:40 +02:00
Johannes Millan
007c7024c7 chore(deps): bump @fastify/static to 9.1.1 in super-sync-server
Patches CVE-2026-6410 and CVE-2026-6414. Regenerates the root
package-lock.json so workspace resolution matches the bumped range.
2026-04-20 15:48:40 +02:00
Johannes Millan
4f0239f878 perf(focus-mode): throttle tray IPC to 1/sec during active focus sessions
addTimeSpent and the newly-added tick action both fire every 1s from
the shared globalInterval, so taskChangeElectron$ was dispatching
updateCurrentTask twice per second during active-task focus sessions.
Collapse to a single IPC/sec with throttleTime(500, leading+trailing);
matches the sibling setTaskBarProgress$ pattern in focus-mode.effects.
Leading keeps user-triggered actions (setCurrentTask, startFocusSession)
immediate; trailing ensures the final tick in a burst still lands.
2026-04-20 15:48:40 +02:00
Rayan Salhab
ca2165a7d7
fix(nav-item): prevent project counters from overlapping menu icon (#7142)
* fix(nav-item): prevent project counters from overlapping menu icon #7132

The task-count badge was positioned at right: 16px which caused it to
overlap with the additional-btn (three-dot menu, 40px wide) when the
sidebar was narrow.

Changed the default position to right: 48px (40px button + 8px padding)
to ensure the counter is always visible and doesn't overlap the menu icon.

Fixes #7132

* fix(nav-item): use inline layout on touch devices for counter (#7132)

Instead of absolute-positioning the task counter to the left of the
more-vert button, let it flow inline when .isTouchPrimary is active.
This matches the detection mechanism used by multi-btn-wrapper for
.additional-btn visibility, avoiding overlap on hybrid devices where
the @media (hover) query and the JS touch-class disagree. Desktop
behavior (absolute counter that hides on hover) is unchanged.

Co-authored-by: Rayan Salhab <rayansalhab@hotmail.com>

---------

Co-authored-by: Johannes Millan <johannes.millan@gmail.com>
2026-04-20 14:55:21 +02:00
dependabot[bot]
1b8616ca89
chore(deps)(deps): bump the github-actions-minor group across 1 directory with 8 updates (#7289)
Bumps the github-actions-minor group with 8 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [step-security/harden-runner](https://github.com/step-security/harden-runner) | `2.17.0` | `2.19.0` |
| [actions/setup-node](https://github.com/actions/setup-node) | `6.3.0` | `6.4.0` |
| [actions/cache](https://github.com/actions/cache) | `5.0.4` | `5.0.5` |
| [KSXGitHub/github-actions-deploy-aur](https://github.com/ksxgithub/github-actions-deploy-aur) | `4.1.2` | `4.1.3` |
| [signpath/github-action-submit-signing-request](https://github.com/signpath/github-action-submit-signing-request) | `2.1` | `2.2` |
| [anthropics/claude-code-action](https://github.com/anthropics/claude-code-action) | `1.0.93` | `1.0.101` |
| [github/codeql-action](https://github.com/github/codeql-action) | `4.35.1` | `4.35.2` |
| [cloudflare/wrangler-action](https://github.com/cloudflare/wrangler-action) | `3.14.1` | `3.15.0` |



Updates `step-security/harden-runner` from 2.17.0 to 2.19.0
- [Release notes](https://github.com/step-security/harden-runner/releases)
- [Commits](f808768d15...8d3c67de8e)

Updates `actions/setup-node` from 6.3.0 to 6.4.0
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](53b83947a5...48b55a011b)

Updates `actions/cache` from 5.0.4 to 5.0.5
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](668228422a...27d5ce7f10)

Updates `KSXGitHub/github-actions-deploy-aur` from 4.1.2 to 4.1.3
- [Release notes](https://github.com/ksxgithub/github-actions-deploy-aur/releases)
- [Commits](abe8ac26b5...da03e16036)

Updates `signpath/github-action-submit-signing-request` from 2.1 to 2.2
- [Release notes](https://github.com/signpath/github-action-submit-signing-request/releases)
- [Commits](bc66d86b01...b9d91eadd3)

Updates `anthropics/claude-code-action` from 1.0.93 to 1.0.101
- [Release notes](https://github.com/anthropics/claude-code-action/releases)
- [Commits](b47fd721da...38ec876110)

Updates `github/codeql-action` from 4.35.1 to 4.35.2
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](c10b8064de...95e58e9a2c)

Updates `cloudflare/wrangler-action` from 3.14.1 to 3.15.0
- [Release notes](https://github.com/cloudflare/wrangler-action/releases)
- [Changelog](https://github.com/cloudflare/wrangler-action/blob/main/CHANGELOG.md)
- [Commits](da0e0dfe58...9acf94ace1)

---
updated-dependencies:
- dependency-name: step-security/harden-runner
  dependency-version: 2.19.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions-minor
- dependency-name: actions/setup-node
  dependency-version: 6.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions-minor
- dependency-name: actions/cache
  dependency-version: 5.0.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions-minor
- dependency-name: KSXGitHub/github-actions-deploy-aur
  dependency-version: 4.1.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions-minor
- dependency-name: signpath/github-action-submit-signing-request
  dependency-version: '2.2'
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions-minor
- dependency-name: anthropics/claude-code-action
  dependency-version: 1.0.101
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions-minor
- dependency-name: github/codeql-action
  dependency-version: 4.35.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions-minor
- dependency-name: cloudflare/wrangler-action
  dependency-version: 3.15.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-20 13:41:16 +02:00
Johannes Millan
01e30b9c7e
Feat/to me it looks like there are lots of 60dd04 (#7280)
* fix(issue): prevent crash from orphan issueProviderId (#7135)

The Jira image-headers effect in task-detail-panel subscribed to
selectIssueProviderById without an error handler, so a task with an
issueProviderId pointing at a deleted provider (e.g. after sync
convergence where taskIdsToUnlink didn't cover all local tasks)
propagated the selector throw to Zone.js as a crash dialog. Wrap the
inner selector observable in catchError that logs and falls back to
of(null); the downstream jiraCfg?.isEnabled guard handles the fallback.

Also drop IssueLog.log(issueProviderKey, issueProvider) from the
throwing variant of the selector: providers may carry credentials
(host, token, apiKey) and IssueLog history is exportable.

* fix(focus-mode): sync tray countdown with in-app timer during breaks

Tray title was rebuilt from a cached currentFocusSessionTime that only
refreshed when CURRENT_TASK_UPDATED fired. addTimeSpent is gated on an
active current task, so during focus-mode breaks or task-less focus
sessions the cache froze while the in-app timer kept ticking.

Add the tick action to taskChangeElectron$ so the cache refreshes every
second whenever the focus timer is running.

Fixes #7278

* fix(ci): restore GitHub Actions SHA pins undone by 0e9218bd68

Commit 0e9218bd68 silently reverted PR #7212 (github-actions-minor group
bump) along with its stated sync/client-id work. This restores the 15
workflow files to their pre-revert state.

Actions restored to newer pinned SHAs:
- actions/upload-artifact v7.0.0 -> v7.0.1
- step-security/harden-runner v2.16.1 -> v2.17.0
- softprops/action-gh-release v2.6.1 -> v3.0.0
- signpath/github-action-submit-signing-request v2.0 -> v2.1
- anthropics/claude-code-action v1.0.89 -> v1.0.93
- docker/build-push-action v7.0.0 -> v7.1.0
- easingthemes/ssh-deploy v5.1.1 -> v6.0.3

* fix: restore i18n, UI, and docs work undone by 0e9218bd68

Commit 0e9218bd68 silently reverted the following work alongside its stated
sync/client-id changes. Files where later master commits (fec7b25f23, etc.)
already re-applied the reverted work are intentionally left untouched.

Restored:
- #7232 docs/long-term-plans/location-based-reminders.md (513 lines)
- #7199 Romanian i18n phase 3 (ro.json + ro-md.json, ~1168 lines)
- #7049 Polish translation improvements
- #7143 planner component styling (4 scss files)
- #7211 add-task-bar preserve time estimate when typing title
- #7208 task.reducer roll-up estimates for added subtasks
- #6767 focus-mode pomodoro reset button
- #7205 plugin-dev github-issue-provider TOKEN description
- #7231 mobile-bottom-nav FAB fix
- a4fe03272 iOS keyboard accessory bar (global-theme + dialog-fullscreen-markdown)
- 309670db3 ShortSyntaxEffects undefined guard
- 667a7986f Dropbox PKCE auth comment/behavior

* fix(electron): restore electron + e2e work undone by 0e9218bd68

Commit 0e9218bd68 silently reverted the following electron/e2e work.
Files already re-fixed by later master commits are left as-is:
- e2e/tests/sync/supersync-archive-conflict.spec.ts (de33234976 + 191d129ff3)

Restored:
- e8a3e156eb fix(electron): Linux autostart IDB backing-store recovery
  (re-adds electron/clear-stale-idb-locks.ts + start-app.ts wiring)
- 5ce78a5b63 fix(electron): macOS Cmd+Q / Dock > Quit hang
  (setIsQuiting + before-quit delegate to close-handler)
- 46e0fa2d01 fix(sync): FILE_SYNC_LIST_FILES IPC contract
  (electronAPI.d.ts + local-file-sync + preload + ipc-events)
- ea1ef16307 fix(android): session-only SAF permissions on OEM devices
- 8865dc0a50 test(e2e): supersync parallel-worker stampede guard
  (SUPERSYNC_SERVER_HEALTHY env-var fallback + goto retry loop)
- af7c7687e2 test(e2e): block WS-triggered downloads in non-WS specs
- 265b44db5d test(e2e): premature waitForURL on daily-summary
  (this is literally the fix the bad commit's message claimed to add)

Conflict resolutions:
- e2e/utils/supersync-helpers.ts: kept the refined getDoneTaskElement
  checks from d64014d086 (later than c558bcab5e) while restoring the
  goto retry loop from 8865dc0a50.
- electron/start-app.ts: unioned imports (setIsQuiting +
  clearStaleLevelDbLocks from theirs, fs from ours).

* fix(sync): restore sync-core work undone by 0e9218bd68

Commit 0e9218bd68 silently reverted parts of several sync fixes. Most
sync-core reverts have already been re-addressed differently on master
by later commits (1f5184f6e7, 05cd875dd6, 09f5ced2c9, 7df43358ab,
d9158d6adb, 32dbc95ed9, 8c3b08e016, f89fe1ebc3) — those files are
intentionally left untouched to avoid reverting master's newer work.

This PR restores only the pieces that are genuinely still missing:

- e8a3e156eb fix(electron): IDB backing-store autoreload (in-app piece)
  operation-log-hydrator.service.ts + .spec.ts (the electron/clear-
  stale-idb-locks.ts piece was restored in the prior commit)

Plus three low-risk documentation/cleanup restorations:
- operation-sync.util.ts — add "Nextcloud" to isFileBasedProvider JSDoc
- dropbox.ts — restore improved _getRedirectUri JSDoc (667a7986fb)
- dialog-get-and-enter-auth-code.component.ts — restore isNativePlatform
  comment explaining why manual code entry flow is used (667a7986fb)
- file-adapter.interface.ts — remove stray "// NEW" comment (46e0fa2d01)

Intentionally NOT restored (master's newer work covers or supersedes):
- sync-trigger.service.ts / sync.effects.ts (05cd875dd6)
- sync-wrapper.service.ts (1f5184f6e7)
- sync-errors.ts (1f5184f6e7 re-added LegacySyncFormatDetectedError)
- file-based-sync-adapter.service.ts + spec (1f5184f6e7 + d9158d6adb)
- file-based-sync.types.ts (1f5184f6e7)
- operation-log.const.ts (32dbc95ed9 bumped IDB_OPEN_RETRIES to 5)
- dialog-sync-initial-cfg.component.ts (f89fe1ebc3)
2026-04-20 12:04:38 +02:00
Johannes Millan
1d98d356be
style: replace hardcoded styles with design tokens (#7281)
Mechanical sweep replacing hardcoded values with documented design tokens
per docs/styling-guide.md. No visual regressions intended; transition
durations align to nearest token bucket (0.1s -> --transition-fast 90ms,
0.3s/200ms -> --transition-duration-m 225ms).

- Transitions/animations: done-toggle, heatmap, progress-circle, task,
  add-task-bar, main-header
- Colors: #fff -> --card-bg in task time-badge, #333 -> --text-color in
  work-view no-tasks-label
- Spacing: 4px/16px -> --s-half/--s2 in help-box, add-task-bar; -12px ->
  calc(var(--s) * -1.5) in dialog-deadline

Note: lint not run locally (node_modules unavailable in env); changes are
1:1 token substitutions matching existing indentation.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-20 01:26:52 +02:00
Johannes Millan
e113cd8d21 18.2.4 2026-04-19 19:21:50 +02:00
Johannes Millan
de33234976 test(e2e): harden LWW archive and encryption password-change tests
- supersync-archive-conflict: add closing B sync to the convergence
  rounds (A→B→A→B) so B is not one op behind A's final conflict-
  resolution state; bump active-list assertion timeout to 15s to
  cover the async reducer pipeline + sync-replay event-loop yield.
- supersync.page: fix race in changeEncryptionPassword where a stale
  check icon from the previous syncAndWait() caused the method to
  return before the server wipe + re-upload finished, leaving the
  next client to race against partially-uploaded server state.
  Now captures the check-icon state at entry and waits for it to
  clear (same pattern syncAndWait uses).
2026-04-19 19:20:34 +02:00
John Costa
32dbc95ed9
fix(persistence): increase IndexedDB retry window for session-restart locks (#7220)
* fix(persistence): increase IndexedDB retry window for session-restart locks

On Linux desktop environments (especially Flatpak with autostart),
logging out and back in can leave stale LevelDB locks for 5-15+
seconds. The previous retry window (~3.5s) was too short to outlast
this, causing "Database Error - Cannot Load Data" on every
logout/login cycle.

Increase IDB_OPEN_RETRIES from 3 to 5 and IDB_OPEN_RETRY_BASE_DELAY_MS
from 500ms to 1000ms, giving a total retry window of ~31 seconds.

Fixes #7191

* fix: update stale retry timing comments to match new constants

* fix(persistence): gate long IDB retry window on lock-related errors

_ensureInit() is awaited by every op-log read/write, and the hydrator
auto-reloads on IndexedDB open errors. Applying the ~31s retry window
to every error class could create a reload -> wait -> reload loop that
feels like a hang. Classify the error on first failure and fall back
to a short retry budget (IDB_OPEN_RETRIES_NON_LOCK=2, ~3s window) for
errors that don't look lock-related. InvalidStateError and messages
containing "backing store" still get the full window used for stale
LevelDB locks on Linux desktop session restart. See #7191.

The spec is hardened to assert concrete constant floors rather than
recomputing the backoff formula (which only proved the formula matched
itself). Also adds unit tests for the classification helper.

* fix(persistence): address review feedback on IDB retry split

- Correct stale "auto-reload" rationale in op-log-errors.const,
  operation-log.const, operation-log-store.service, and archive-store.service:
  the hydrator shows a blocking alert dialog on IndexedDBOpenError, it does
  not auto-reload. The fail-fast argument now reflects the real reason —
  every op-log read/write awaits _ensureInit(), so a 31s wait on a non-lock
  error blocks the subsystem for 31s before the alert dialog surfaces.
- isLockRelatedIdbOpenError now checks `err instanceof DOMException || err
  instanceof Error` before reading .name/.message, mirroring the pattern in
  the sibling isConnectionClosingError. In some Electron / older runtimes
  DOMException does not satisfy instanceof Error.
- Added behavioral tests for _openDbWithRetry that spy on a new
  _openDbOnce testing seam: generic errors take exactly
  1 + IDB_OPEN_RETRIES_NON_LOCK attempts, InvalidStateError takes up to
  1 + IDB_OPEN_RETRIES, and a lock-error followed by success resolves.
- Bumped IDB_OPEN_RETRIES_NON_LOCK from 2 to 3 to match master's pre-PR
  retry count (1s + 2s + 4s = ~7s ceiling). Framing this as "keep master's
  existing retry count for non-lock errors, add long retry only for lock
  errors" is a lower-risk scope. Adjusted the const spec's upper-bound
  assertion accordingly.
- Replaced the "retries 1-5" hardcoded schedule comment in
  operation-log-store.service and archive-store.service with a formula-based
  comment so the two retry budgets can't drift out of sync with reality.
- Added two DOMException-shaped test cases to
  operation-log.const.spec.ts covering the new predicate branch.

* docs(persistence): clean up rot-prone worked examples in retry docstrings
2026-04-19 19:20:23 +02:00
Johannes Millan
bbd4b41b12
test: clear selectOverdue/LaterToday selector overrides across specs (#7274)
The new work-view.component.spec.ts uses provideMockStore with
overrideSelector for selectOverdueTasksWithSubTasks (and
selectLaterTodayTasksWithSubTasks). overrideSelector calls setResult(),
which persists across spec files and is only cleared by clearResult().

When task.selectors.spec.ts runs after work-view.component.spec.ts, the
persisted override ([] in most cases) caused four selectOverdueTasksWithSubTasks
assertions to fail with "Expected 0 to be 1".

Extend the defensive cleanup in the selectors spec's beforeEach to also
clear and release these two selectors, matching the existing pattern
documented in the same block.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-19 19:00:17 +02:00
Johannes Millan
e738bd2823
docs(research): extend Snap+Wayland GPU research with PR #7273 follow-up (#7275)
Follow-up to issue #7270 and the now-closed PR #7273. Extends the
existing research doc with four new sections based on multi-agent
research, verification passes, and empirical reporter confirmation.

- §12: PR #7273 GPU startup guard framing and mechanism analysis.
- §13: Deepened research on Chromium/Electron/Mesa correctness, prior
  art for crash-loop startup markers (Firefox recent_crashes, Chromium
  in-process GpuMode stack, BugSnag/Sentry patterns), testing strategy
  with concrete spec proposal, edge cases, and long-term strategy.
- §14: Verification pass findings (four parallel agents) — corrections
  applied to §12 (v18.2.3 timeline, --disable-gpu wording), rejected
  claims with evidence, outstanding attribution fixes.
- §15: Final PR re-evaluation — including why PR #7273 was closed in
  favor of #7266 after the reporter empirically confirmed that
  --ozone-platform=x11 resolves their launch failure on Ubuntu 22.04.

Keeps the research available for the next time this class of failure
surfaces (future Chromium bumps, Mesa ABI drift on core24/gpu-2404,
Flatpak packaging, etc.).
2026-04-19 18:50:38 +02:00
Johannes Millan
c9fdf42929
fix(work-view): keep task selected when group/filter shows cross-context tasks (#7272)
* fix(work-view): keep task selected when group/filter shows cross-context tasks

When Group by project/tag or cross-context filters pulled tasks from other work
contexts into the view, tapping one of those tasks dispatched setSelectedId(id)
but an effect in WorkViewComponent immediately reset it to null because the
task was not in the current context's undone/done/later/overdue/backlog lists.
On mobile this opened the bottom sheet with no task-detail-panel content,
appearing as a blank screen.

Also consult customizedUndoneTasks().list so tasks surfaced via the customizer
keep the selection alive and the detail panel can render.

Fixes #7269

https://claude.ai/code/session_019AwFGKFEEEcBqjp7HdhU17

* test(work-view): cover selected-task retention when customizer shows cross-context tasks

Add unit tests that mirror the constructor effect in WorkViewComponent
responsible for deselecting the currently selected task. The tests pin
the fix for #7269: when the task-view customizer surfaces tasks from
other work contexts, the selection must be retained so the detail panel
can render.

Covers:
- fix: task only present in customizedUndoneTasks.list must NOT trigger
  setSelectedId(null), including the subtask-traversal case
- regression: task missing from every list (including customizer) must
  still deselect; customizer list being undefined/empty must not mask
  deselection
- existing behaviour: null selectedTaskId is a no-op; tasks in undone /
  done / laterToday / backlog / overdue (on TODAY_TAG) keep selection;
  overdue tasks on non-TODAY contexts still deselect

The component has a large dependency surface and instantiates several
signals from observables in its constructor, so the test mirrors the
effect body verbatim and drives it with plain inputs rather than
standing up the full TestBed graph.

https://claude.ai/code/session_019AwFGKFEEEcBqjp7HdhU17

* test(work-view): upgrade retention spec to drive the real component via TestBed

Replace the logic-mirror helper with a TestBed-based harness that
instantiates the real WorkViewComponent with an overridden template and
stub providers, then drives its constructor effect through the signal
inputs. Any future change to the effect body now fails the test rather
than the mirror needing to be kept in sync by hand.

https://claude.ai/code/session_019AwFGKFEEEcBqjp7HdhU17

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-19 16:46:20 +02:00
adnoh
941e95bc12
Implement task parsing with sub-tasks (#7184)
* Implement task parsing with sub-tasks

Added a new function to parse tasks with sub-tasks from a text input, improving task management capabilities. Updated the task addition logic to utilize this new parsing method.
Should solve #7183

* Enhance task parsing and user information

    Key Changes Summary:
     Critical Fix #1 - 4-space indentation: Detects minimum indent and normalizes relative to it
     Critical Fix #2 - Mixed input: Plain-text lines no longer silently dropped; warning shown
     Critical Fix #3 - Deeply-nested items: Now flattened to sub-task level with user notification
     Minor - Sub-task dueDay: Documented that it inherits from parent (not passed independently)
     Suggestion - UI/UX: Updated placeholder text to show expected format with examples

* fix indentation calculation and missing isBullet: true

* fix(brain-dump-plugin): keep sub-tasks when plain-text lines interrupt

Previously, a plain-text line between a bullet and its indented sub-task
caused the sub-task to be silently dropped: the outer loop advanced past
the plain-text entry but the inner look-ahead had already exited at
indent 0, so the following indented bullet was never attached to the
parent and was rejected by the outer loop's else branch.

The sub-task scan now skips over plain-text lines while still breaking
at the next top-level bullet, so indented bullets attach to the previous
parent regardless of interleaved plain text. The plain-text line itself
is still emitted as its own top-level task by the outer loop.

Also drops dead code:
- findMinimumIndent() was declared but never called.
- dueDay on sub-task payload was forwarded but silently discarded by the
  plugin bridge (sub-tasks inherit the parent's date).

And de-duplicates the "Note: ... Note: ..." prefix in the deeply-nested
warning snack.

---------

Co-authored-by: Adnoh <git@rotzefull.de>
Co-authored-by: Johannes Millan <johannes.millan@gmail.com>
2026-04-19 16:44:19 +02:00
Johannes Millan
0dd97e122d feat(misc): extend default start page to projects, planner, schedule, boards
Widens MiscConfig.defaultStartPage from number to number | string so users
can pick any project as their startup screen in addition to Today (0),
legacy Inbox (1), Planner (2), Schedule (3), or Boards (4). Builds on
#6354.

- New StartPageSelectComponent (Formly type start-page-select) lists
  built-in pages plus all non-archived, non-hidden projects. A legacy
  Inbox option is rendered only while the stored value is still 1 so
  existing users see their selection without any silent config write.
- Feature-gated options (Planner/Schedule/Boards) are hidden from the
  dropdown when the corresponding appFeatures flag is off.
- DefaultStartPageGuard validates the selection against current state:
  project must exist and not be archived or hidden; feature pages only
  route when enabled; empty strings, missing projects, and disabled
  features all fall back to Today instead of stranding the user.
- Deleting a project clears misc.defaultStartPage alongside the existing
  tasks.defaultProjectId cleanup so a stale pointer can't linger.
- Unit tests for DefaultStartPageGuard cover every branch (including
  empty-string and archived/hidden regressions) and the new effect
  cleanup paths.
2026-04-18 23:24:19 +02:00
Johannes Millan
3ed287516a fix(archive): skip updateTasks dispatch for empty updates (#7268)
Editing a recurring task schedule when no archive instances exist
dispatched TaskSharedActions.updateTasks with tasks: [], tripping the
operation-log validator with "invalid entityId/entityIds (undefined)".
Guard the batch dispatch on updates.length > 0 so the empty-array path
is a no-op for all three callers that route through updateArchiveTasks.
2026-04-18 23:24:19 +02:00
Johannes Millan
404c68127a 18.2.3 2026-04-18 23:24:19 +02:00
Johannes Millan
a1e3d9cc55 fix(build): pin app-builder-lib minimatch back to 10.1.1 2026-04-18 23:24:19 +02:00
Johannes Millan
191d129ff3 test(e2e): restore title tolerance in LWW archive resurrection assertion 2026-04-18 23:24:19 +02:00
aakhter
baa97ec4b4
fix: harden archive payload repair and validation (#7248)
* fix: harden archive payload repair and validation

* refactor: address PR review feedback on archive payload repair

- Sync subTaskIds with surviving subtasks in sanitizeTasksForArchiving so
  the archived parent cannot carry dangling references when subtasks are
  dropped during sanitization.
- Extract triple-duplicated valid-id predicate into shared
  op-log/validation/is-valid-entity-id.ts and reuse from
  validate-operation-payload, data-repair, and archive.service.
- Consolidate task sanitization into archive.service as the single source
  of truth (it also covers writeTasksToArchiveForRemoteSync). Removes the
  inline sanitizer block from task.service.moveToArchive.
- Replace console.warn / console.error in task.service.moveToArchive with
  TaskLog.warn / TaskLog.err so records flow through the app log.
- Add spec covering the subTaskIds sync behavior; update task.service
  spec to reflect archive.service-owned filtering.

All 8413 tests pass; lint clean.

* refactor(archive): avoid logging full task and tighten sanitizer spec

- task.service.moveToArchive now logs only the task id when it has to
  wrap a single task into an array. TaskLog output is exportable, so
  dumping the full task object risked leaking user-authored titles/notes.
- archive.service.spec asserts the archived parent's subTaskIds is empty
  after malformed subtasks are dropped, pinning down the sanitizer's
  subTaskIds sync behavior.

---------

Co-authored-by: Aamer Akhter <aamer_akhter@users.noreply.bitbucket.org>
Co-authored-by: Johannes Millan <johannes.millan@gmail.com>
2026-04-18 23:18:20 +02:00
Milo Ivir
7c53431945
Update Croatian translation (#7148)
* Update Croatian translation

This update corrects the current Croatian translation (see #4964).
The update is based on the English version of the 2nd April 2026.

Please update as soon as possible, because of the above metioned issue!
Thank you.

* Update Croatian

Corrected translation errors found by @johannesjo. See issue #7148.
I hope got them all!

* Update Croatian translations in hr.json

Corrected SyntaxError (missing comma) and updated translation string

* fix(i18n): apply review fixes to Croatian translation

- Restore <strong> and unsynced-changes warning in SUPER_SYNC FORCE_OVERWRITE_CONFIRM
- Add 26 keys added to en.json since PR base (BOARDS sort/tag-match, SYNC errors, FOCUS_MODE sounds, PLUGINS size limits, COPY_URL)
- Remove renamed GCF.FOCUS_MODE.L_IS_PLAY_TICK (superseded by L_FOCUS_MODE_SOUND)

Co-authored-by: Claude <noreply@anthropic.com>

---------

Co-authored-by: Johannes Millan <johannes.millan@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
2026-04-18 23:17:22 +02:00
Symon Baikov
3c75b527cd
fix(electron): recreate tray indicator before minimize to tray (#7072)
* fix(electron): recreate tray indicator before minimize to tray

* fix(electron): handle missing tray indicator gracefully
2026-04-18 22:05:16 +02:00
Alan Płócieniak
626db33e3c
feature(schedule): persist selected time view (#6473) 2026-04-18 21:04:18 +02:00
Johannes Millan
ac7cf7b853
Claude/snap wayland gpu fix co dc5 (#7266)
* docs(research): add Snap + Wayland GPU init failure research

Synthesizes root cause (Mesa ABI drift in gnome-42-2204), scope,
peer-app consensus, and ranked options. Recommends widening the
existing Snap-gated --ozone-platform=x11 guard in start-app.ts to
cover Snap + Wayland sessions, with core24 + gpu-2404 migration
as the long-term fix.

https://claude.ai/code/session_01Hu6EP7Xux9JRvGGGKpBfwm

* docs(research): correct electron timing and soften unverified peer claims

* fix(electron): force X11 on Snap Wayland to avoid Mesa ABI drift

The existing Snap-only X11 fallback triggered only when $SNAP/gnome-platform
was empty. In practice gnome-platform is populated but its Mesa can drift
out of ABI sync with Electron's bundled libgbm, producing
"Failed to get system egl display" and a GPU process respawn loop on
Wayland sessions. Widen the guard to also fire when the session is
Wayland (XDG_SESSION_TYPE=wayland or WAYLAND_DISPLAY set). X11/GLX
avoids the failing Wayland EGL init path entirely while preserving
hardware acceleration.

Refs: electron-builder#9452, super-productivity#5672,
forum.snapcraft.io #40975, #49173

https://claude.ai/code/session_01JNxazJmDhpMp9UYV6SqnBG

* refactor(electron): address review feedback on Snap X11 force

- Handle space-separated `--ozone-platform wayland` override in addition
  to the `=` form; tighten the argv scan to reject accidental substring
  matches (e.g., arg values that happen to contain `--ozone-platform=`).
- Enrich the log line with raw XDG_SESSION_TYPE and WAYLAND_DISPLAY
  values to make user-reported triage faster.
- Tighten comment: correct "X11/GLX" to "X11 ozone backend" (modern
  Chromium uses EGL on X11), clarify the root cause as Mesa version
  drift rather than bundled-library ABI drift, broaden the loss list
  beyond fractional scaling, and move issue references to the commit
  body per CLAUDE.md guidance.

Follow-ups flagged but not addressed here: possible interaction with
`allowNativeWayland: true` in electron-builder.yaml, GPU cache keyed
by effective ozone platform, and the longer-term core24 migration
already in docs/long-term-plans/electron-upgrade-to-v40.md.

Refs: electron-builder#9452, super-productivity#5672,
forum.snapcraft.io #40975, #49173

https://claude.ai/code/session_01JNxazJmDhpMp9UYV6SqnBG

* docs(research): verify peer-app claims, correct SP electron timing

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-18 20:57:00 +02:00
dependabot[bot]
4b9bd23e56
chore(deps): bump the npm_and_yarn group across 1 directory with 9 updates (#7259)
Bumps the npm_and_yarn group with 9 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [fastify](https://github.com/fastify/fastify) | `5.8.4` | `5.8.5` |
| [nodemailer](https://github.com/nodemailer/nodemailer) | `8.0.4` | `8.0.5` |
| [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) | `6.4.1` | `6.4.2` |
| [@hono/node-server](https://github.com/honojs/node-server) | `1.19.11` | `1.19.14` |
| [axios](https://github.com/axios/axios) | `1.13.6` | `1.15.0` |
| [dompurify](https://github.com/cure53/DOMPurify) | `3.3.3` | `3.4.0` |
| [follow-redirects](https://github.com/follow-redirects/follow-redirects) | `1.15.11` | `1.16.0` |
| [hono](https://github.com/honojs/hono) | `4.12.9` | `4.12.14` |
| [lodash-es](https://github.com/lodash/lodash) | `4.17.23` | `4.18.1` |



Updates `fastify` from 5.8.4 to 5.8.5
- [Release notes](https://github.com/fastify/fastify/releases)
- [Commits](https://github.com/fastify/fastify/compare/v5.8.4...v5.8.5)

Updates `nodemailer` from 8.0.4 to 8.0.5
- [Release notes](https://github.com/nodemailer/nodemailer/releases)
- [Changelog](https://github.com/nodemailer/nodemailer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodemailer/nodemailer/compare/v8.0.4...v8.0.5)

Updates `vite` from 6.4.1 to 6.4.2
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v6.4.2/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v6.4.2/packages/vite)

Updates `@hono/node-server` from 1.19.11 to 1.19.14
- [Release notes](https://github.com/honojs/node-server/releases)
- [Commits](https://github.com/honojs/node-server/compare/v1.19.11...v1.19.14)

Updates `axios` from 1.13.6 to 1.15.0
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.13.6...v1.15.0)

Updates `dompurify` from 3.3.3 to 3.4.0
- [Release notes](https://github.com/cure53/DOMPurify/releases)
- [Commits](https://github.com/cure53/DOMPurify/compare/3.3.3...3.4.0)

Updates `follow-redirects` from 1.15.11 to 1.16.0
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.11...v1.16.0)

Updates `hono` from 4.12.9 to 4.12.14
- [Release notes](https://github.com/honojs/hono/releases)
- [Commits](https://github.com/honojs/hono/compare/v4.12.9...v4.12.14)

Updates `lodash-es` from 4.17.23 to 4.18.1
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.23...4.18.1)

---
updated-dependencies:
- dependency-name: fastify
  dependency-version: 5.8.5
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: nodemailer
  dependency-version: 8.0.5
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: vite
  dependency-version: 6.4.2
  dependency-type: direct:development
  dependency-group: npm_and_yarn
- dependency-name: "@hono/node-server"
  dependency-version: 1.19.14
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: axios
  dependency-version: 1.15.0
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: dompurify
  dependency-version: 3.4.0
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: follow-redirects
  dependency-version: 1.16.0
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: hono
  dependency-version: 4.12.14
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: lodash-es
  dependency-version: 4.18.1
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-18 20:35:50 +02:00
Johannes Millan
0065d291cc
fix(task-view-customizer): reset sort/group/filter on work context change (#7263)
* fix(task-view-customizer): reset sort/group/filter on work context change

The TaskViewCustomizerService persisted its sort/group/filter options in
localStorage and never reset them when the active work context changed,
causing customizations to leak across projects, tags and the Today view.
Users reported that switching to a project or tag still applied the
previous filter, forcing a manual "Reset all" click.

Subscribe to activeWorkContextTypeAndId$ (skipping the initial emission so
persisted state survives across reloads in the same context) and call
resetAll() whenever the context changes.

Fixes #7262

* refactor(task-view-customizer): persist sort/group/filter per work context

Replace the three global localStorage keys with a single map keyed by
work-context (`${type}:${id}`). Each context now has its own independent
customization state — filters set in Project A stay with Project A, and
returning to it restores them. This replaces the previous blanket
"reset on context change" fix, which neutralized persistence entirely
after the first context switch.

Orphaned LS keys (TASK_VIEW_CUSTOMIZER_SORT/GROUP/FILTER) are dropped;
users will lose any previously-persisted state once, which was already
unreliable in 18.2.0+ due to #7262.

Refs #7262

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-18 18:55:12 +02:00
Johannes Millan
47c52dfa87 18.2.2 2026-04-17 22:51:53 +02:00
Johannes Millan
3edbcdb357 style(boards): remove unused eslint-disable directives in specs 2026-04-17 22:34:37 +02:00
Johannes Millan
b9d6dc6d86
test(e2e): update Show/hide task panel button label (#7260)
* test(e2e): update Show/hide task panel button label

The TOGGLE_DETAIL_PANEL translation was renamed from "Show/Hide
additional info" to "Show/hide task panel" in dd789d2, but e2e
selectors still referenced the old string, causing every test that
opens the task detail panel to time out.

https://claude.ai/code/session_011mX4Pr24S42svmA5j7fRux

* 18.2.1

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-17 22:19:22 +02:00
Johannes Millan
9b7d34100e 18.2.0 2026-04-17 19:48:55 +02:00
Johannes Millan
d9158d6adb fix(sync): treat unreadable legacy __meta_ as legacy detected
The legacy pfapi probe (1f5184f6e7) only mapped
RemoteFileNotFoundAPIError to LegacySyncFormatDetectedError. WebDAV
surfaces a corrupt or empty __meta_ body as InvalidDataSPError, so
those users bypassed the friendly force-overwrite snack and saw an
unhandled data error instead. Presence — even unreadable — still
proves a v16.x client touched the target, so set legacyFileFound on
InvalidDataSPError and let the existing warning path take over.

Also widen the Dropbox and LocalFile getFileRev signatures (and the
Dropbox getMetaData call under it) to accept `string | null`, matching
the interface and the `null` argument the legacy probe already passes.
No runtime behaviour change — Dropbox never serializes the revision
for metadata lookups and LocalFile only logs it.
2026-04-17 19:45:41 +02:00
Johannes Millan
fddeb581dd refactor(boards): extract rewriteTagIdsForPanel helper
Move the tag-rewrite logic out of BoardPanelComponent.drop() into a
pure rewriteTagIdsForPanel() in boards.util.ts. The component drops
from 421 to 396 lines and the tricky 'any' vs 'all' include/exclude
branches become directly unit-testable.

Cover the extracted helper with cases for each match-mode permutation
plus a frozen-input no-mutation guard, and add the first drop() specs
for board-panel.component covering sorted-mode early-return,
AND-excluded cross-panel drop, and OR-include add.
2026-04-17 19:45:28 +02:00
Johannes Millan
6171a98646 refactor(android): route specialUse FGS through ServiceCompat helper
Both FocusModeForegroundService and TrackingForegroundService declare
android:foregroundServiceType="specialUse" in the manifest. The 2-arg
startForeground() overload already falls back to the manifest type on
Android 14+, but passing the type explicitly via ServiceCompat is
Google's documented best practice and removes OEM-dependent fallback
ambiguity.

Extract the Build-gated call into a Service.startForegroundSpecialUse
extension in ForegroundServiceExt.kt so a future third FGS picks up
the API-version gating automatically. Callers shrink to one-liners.
2026-04-17 19:45:20 +02:00
Johannes Millan
f81858b488 fix(android): guard broken WebView package probe
WebView.getCurrentWebViewPackage() is documented to return null on
devices without an installed provider, but in practice it can throw
AndroidRuntimeException, MissingWebViewPackageException, or NPE on
devices with a corrupt or updating provider. An unhandled throw there
short-circuits WebViewCompatibilityChecker.evaluate() before
FullscreenActivity's recovery path (added in 5a5c3bca89) has a chance
to run, re-crashing the app on launch.

Wrap the call in a narrow catch for RuntimeException so the documented
throwables fall through to the PackageManager lookup while Error
subclasses still propagate. Log at debug level so broken-provider
telemetry survives.
2026-04-17 19:45:10 +02:00
Johannes Millan
710ed9b5e4 fix(keyboard): navigate Shift+T to Today tag directly
goToTimeline was navigating to /timeline, a route not declared in
app.routes.ts. It fell through to the wildcard and DefaultStartPageGuard,
which lands on Today only when the user's default start page is TODAY.
For INBOX-default users the shortcut jumped to INBOX, contradicting the
"Go to Today" label shipped in #7101.

Navigate to /tag/TODAY/tasks directly so the behaviour matches the label
regardless of the configured default start page.
2026-04-17 19:45:02 +02:00
Johannes Millan
5c2645667f fix(config): restore plural for collapse/expand sub-task labels
Revert the singular COLLAPSE_SUB_TASKS / EXPAND_SUB_TASKS wording back
to plural. The shortcut handler toggles the parent task's
_hideSubTasksMode, which affects every subtask of the selection — so
the plural form accurately describes the action.
2026-04-17 19:44:56 +02:00
Johannes Millan
a2f45b0d69 refactor(android): narrow broad catch clauses so unknown failures surface in Play Console
The recent WebView, FGS, and JS-dialog guards each caught Exception,
which hid every thrown type behind a log line. Play Console only
surfaces uncaught throws, so a future regression inside any of these
paths would be silently swallowed.

Narrow to the known recoverable types:
- FullscreenActivity.initWebView: AndroidRuntimeException (incl.
  MissingWebViewPackageException) + IllegalStateException.
- FullscreenActivity.onJsAlert/onJsConfirm: WindowManager.BadTokenException
  + IllegalStateException; JsResult.cancel() still fires via the
  handled flag so the WebView JS thread never deadlocks.
- TrackingForegroundService / FocusModeForegroundService
  ensureForegroundNotification: IllegalArgumentException (Samsung One UI
  / Android 14 blank-title case) + SecurityException. Anything else,
  including ForegroundServiceStartNotAllowedException, now propagates.
2026-04-17 18:13:49 +02:00
Johannes Millan
87f66fea9c refactor(date): centralize startOfNextDay offset behind DateService
Replaces ad-hoc `Date.now() - _dateService.startOfNextDayDiff` arithmetic
scattered across services with a small set of offset-aware helpers on
DateService, and makes the raw field private so TypeScript prevents
future regressions.

Public surface added to DateService:
  - getLogicalTodayDate(): Date — offset-aware "today"
  - getLogicalTomorrowMs(): number — logical today + 1 calendar day,
    DST-safe via setDate (naive +24h stayed on the same local date
    during fall-back transitions)
  - getStartOfNextDayDiffMs(): number — read-only accessor for the
    service-boundary case where pure utilities need the raw offset

The raw `startOfNextDayDiff` field is now `private`; TypeScript
catches both member access and destructuring from outside.

Migrated call sites (all semantic-identical except the DST fix):
  - planner.service.ts: tomorrow$ and ensureDayLoaded
  - add-tasks-for-tomorrow.service.ts: addAllDueToday/Tomorrow
    (+ offset-aware spec coverage)
  - daily-summary.component.ts: isIncludeYesterday
  - global-config.effects.ts + app-state.effects.ts: setTodayString
    payloads now read via the accessor

Preserved for sync-replay determinism:
  - isTodayWithOffset pure util in src/app/util/is-today.util.ts
  - reducers/selectors continue to take startOfNextDayDiffMs as an
    explicit argument from action payloads

Deferred with an in-file TODO: task-due.effects.ts:170 — the effect
is selector-driven (replay-sensitive per CLAUDE.md #8) and its spec is
xdescribe'd, so verification is impossible in this pass.

Documented in CLAUDE.md (Important Development Notes #14 + an
anti-patterns row).

A broader audit surfaced 10 pre-existing sites elsewhere in the
codebase that bypass the logical clock (planner daysToShow$,
argument-less getDbDateStr in pipes, simple-counter streak, plugin
counter). Those are tracked separately and not part of this refactor.
2026-04-17 18:13:02 +02:00
Johannes Millan
dd789d2520 fix(config): clarify keyboard shortcut labels (#7101)
Apply UI copy fixes for keyboard shortcut labels in en.json:
- Update wording for project backlog vs task backlog
- Swap GO_TO_SCHEDULE/GO_TO_SCHEDULED_VIEW labels so they match the
  routes they actually navigate to (Today vs Schedule)
- Replace "Toggle" with "Start/stop" for time tracking shortcuts
- Tidy task panel and task completion wording

Plus follow-ups from issue #7074:
- Make TOGGLE_DETAIL_PANEL tooltip match the new "task panel" wording
- Use singular form for COLLAPSE/EXPAND_SUB_TASKS
- Drop misleading "Press Enter to save" from KEYBOARD.HELP

Closes #7074
Co-authored-by: Symon Baikov <symonbaikov@users.noreply.github.com>
2026-04-17 18:13:02 +02:00
Johannes Millan
5e0c60e977 feat(boards): add tag AND/OR match mode and generalized sort options
Addresses #4688, #4822, #4132 (sort) and #5175, #5810 (tag match) from the
Kanban feature collection in #7245.

- Panel filter: includedTagsMatch and excludedTagsMatch (all/any) with
  current-behavior defaults (all / any) — no migration needed.
- Generalized sortBy (dueDate, created, title, timeEstimate) + sortDir;
  replaces the scheduled-only sortByDue toggle. Legacy sortByDue is
  migrated on load and scrubbed on save by sanitizePanelCfg in the
  boards reducer. Unknown sortBy values are dropped defensively.
- buildComparator uses timezone-safe dueDate logic matching task.selectors.
- Manual drag-reorder disabled (cdkDropListSortingDisabled) when auto-sort
  is active; cross-panel drag still works.
- Cross-panel drop and inline-create write paths honor match mode:
  OR-included adds only one tag when none present; AND-excluded doesn't
  mutate (add-task-bar applies tagsToRemove blindly, so stripping would
  wrongly remove user input).
2026-04-17 18:13:02 +02:00
Johannes Millan
5a5c3bca89 fix(android): recover from WebView init crash via WebViewBlockActivity
Observed on Play Console: AndroidRuntimeException thrown from
WebViewFactory.getProvider during FullscreenActivity.initWebView on
devices where the system WebView package itself is broken (seen with a
WebView Beta build throwing "Already registered a list of actions in
this process" from WebViewChromiumFactoryProvider.<init>). The activity
had no recovery - the constructor threw and the app died at launch.

Mirror the existing CapacitorMainActivity pattern: catch around the
WebHelper().instanceView(this) call, and on failure present the existing
WebViewBlockActivity using compatibility.copy(status = BLOCK) so the
block screen keeps the detected provider/version, then finish().

Also harden lifecycle callbacks that dereference the lateinit webView /
javaScriptInterface fields so the new failure path can't trigger an
UninitializedPropertyAccessException via OEM-specific lifecycle quirks:
::isInitialized guards in onSaveInstanceState, onRestoreInstanceState,
onBackPressed, and callJSInterfaceFunctionIfExists (the last matching
the existing guard in CapacitorMainActivity).
2026-04-17 18:13:02 +02:00
Johannes Millan
b9f91a5327 fix(android): guard FGS contract unconditionally in onStartCommand
Every Context.startForegroundService() call creates a per-call FGS token
that must be answered with startForeground() within the system timeout,
even if the service is already a foreground service. Earlier fixes closed
specific branches one at a time and left gaps — most notably ACTION_START
with null taskId while already tracking, which returned without calling
startForeground().

Satisfy the contract once at the top of onStartCommand in both
TrackingForegroundService and FocusModeForegroundService, so every branch
below (known and future) already has the token answered. Add
stopForegroundAndSelf() on the previously silent no-op branches
(ACTION_UPDATE / ACTION_STOP when idle) so the guard does not leak the
service as foreground with a placeholder notification.

The placeholder notification carries a content title — Samsung One UI can
render title-less notifications as blank and a few Android 14 builds throw
IllegalArgumentException from startForeground() without one, which would
be swallowed by the guard's try/catch and re-trigger the very timeout we
are preventing. Matches the crash's heavy Samsung/Lenovo-tablet bias.
2026-04-17 18:13:02 +02:00
Johannes Millan
83766b3af9 chore(android): untrack .idea/gradle.xml
File paths in this file depend on the checkout location (e.g., main vs.
git worktree), causing it to churn in unrelated commits. Already matched
by the pre-existing android/.idea gitignore rule; just needed removing
from the index.
2026-04-17 18:13:02 +02:00
Johannes Millan
367342edd6 fix(android): guard webchrome dialogs against activity teardown
Prevents WindowManager$BadTokenException in FullscreenActivity.onJsConfirm
and onJsAlert when JS fires alert()/confirm() after the activity starts
finishing. Also ensures JsResult is always released so the WebView JS
thread never deadlocks on lifecycle-driven dismissal.
2026-04-17 18:13:02 +02:00
Johannes Millan
1f5184f6e7 fix(sync): detect legacy v16.x pfapi format and warn user instead of diverging silently
When sync-data.json is missing, check for a legacy __meta_ file before
treating the provider as a fresh start. If found, throw
LegacySyncFormatDetectedError and show an error snack with a force-overwrite
action instead of silently diverging from a v16.x device still writing the
old format.

This restores the behavior added in 791fa533e7, which was unintentionally
removed in 0e9218bd68 during a client-ID refactor. The snack copy clarifies
that force-overwrite only unblocks this device and does not clean up the
legacy files on the remote — if a v16 device syncs again, divergence resumes.

Related: #5964, #6174
2026-04-17 18:13:02 +02:00
Johannes Millan
d64014d086 test(e2e): wait for .isDone in supersync mark-done helpers
TaskService.toggleDoneWithAnimation schedules the setDone dispatch via
setTimeout(200ms) to play the mark-done animation. markTaskDone and
markSubtaskDone returned immediately after clicking done-toggle, so a
following syncAndWait() could start before the late updateTask op was
captured. The op then landed during the sync's upload batch, leaving
hasPendingOps=true after sync completed and the .sync-state-ico check
mark never appeared — supersync.page.ts:1762 timed out.

Fixes flaky "4.1 Complex chain of actions syncs correctly" and
"Time tracking data persists after archive".
2026-04-17 18:13:02 +02:00
Leandro Marques
179700ccda
chore: update electron to v41.x (#7097)
* chore: update electron to v41.1.1

* feat(start-app): clear GPU cache on Electron version change for Linux

* fix(window-decorators): disable custom window title bar for GNOME

This fixes some issues when moving and resizing the window

* refactor: Migrate url.format() (DEP0116) to file:// path approach

* refactor(start-app): remove deprecated protocol.registerFileProtocol in start-app.ts

* feat(electron-builder): update gnome content snap to gnome-42-2204 for improved compatibility and update flatpak permissions

* fix(window-decorators): improve handling of custom window title bar for GNOME

* feat(start-app): implement fallback to X11 in Snap if gnome-42-2204 runtime is unavailable

* chore: update electron to v41.1.1

* chore(package-lock): remove unused dependencies from package-lock.json

* fix(electron): move snap ozone-platform switch before app ready event

app.commandLine.appendSwitch() must be called before Chromium
initializes — after the ready event fires the GPU backend is already
running and the switch is a no-op. Move the Snap X11 fallback (defense-
in-depth for missing gnome-42-2204 runtime) to run synchronously at
startup, alongside the existing gtk-version and speech-dispatcher
switches.

* fix(electron): align IS_GNOME_DESKTOP detection with preload.ts logic

The original implementation only matched Ubuntu's GNOME session
(XDG_CURRENT_DESKTOP contains both 'gnome' AND 'ubuntu'), missing plain
GNOME on Fedora, Arch, etc. The preload.ts isGnomeDesktop() already
used the correct approach: check four environment variables with OR
logic. Align common.const.ts to match, preventing a split-brain where
the main process and renderer disagree on whether the user is on GNOME
— which would produce no title bar at all on non-Ubuntu GNOME desktops.

* fix(electron): restore v41 bump lost in merge and gate title-bar toggle

- Re-apply electron 41.2.0 + minimatch 10.2.5 override (master's 0e9218bd
  reverted the dependabot bump back to 37.10.3 while this branch's
  merge-base still contained 41.2.0, so the pre-merge diff was empty).
- Regenerate root package-lock.json accordingly.
- Drop unrelated esbuild additions from plugin-dev sub-lockfiles.
- misc-settings-form: gate isUseCustomWindowTitleBar on IS_ELECTRON &&
  !IS_GNOME_DESKTOP so the toggle does not appear in the web/PWA build.

---------

Co-authored-by: Johannes Millan <johannes.millan@gmail.com>
2026-04-17 18:12:43 +02:00
Johannes Millan
5787437a9a
fix(reminder): restore Android alarms after app update (#7254)
Android clears all AlarmManager alarms (and may cancel WorkManager jobs)
whenever the app's APK is replaced — i.e. on every Play Store auto-update
or manual reinstall. BootReceiver already knows how to re-register
everything from ReminderAlarmStore and re-enqueue SyncReminderWorker, but
its intent-filter only listened for BOOT_COMPLETED, so after an update
reminders stayed silently unregistered until the user next opened the app.

Add MY_PACKAGE_REPLACED to the manifest and broaden the receiver's action
guard so alarm restoration runs on both events. No new permissions or
helpers required — MY_PACKAGE_REPLACED is a protected broadcast delivered
only by the system to the owning app.

Refs #6832

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-17 17:39:59 +02:00