Commit graph

20094 commits

Author SHA1 Message Date
Corey Newton
cbc6cbaa73
Housekeeping for various docs, templates, and actions (#7451)
* chore: change Claude doc directives

Ensure docs are not only aimed at end users but also that they aren't
too myopic. DeepWiki can provide limitless detail on any topic.

* chore(ci): switch stalesweeper to a hash reference

* chore(ci): decrease frequency of discussion pruning

* chore(ci): fix "unanswered" logic for stale discussions

Close answered Q&A after 90 days as RESOLVED.
Close everything else after 180 days as OUTDATED.

* chore: clean up issue templates wrt docs

Also, invert the expected-observed order for bug report.
2026-05-02 13:58:25 +02:00
cpa-27
1d1b49d337
fix(docs): Add documentation for time estimate short syntax (#7454)
* fix(docs): Update new task placeholder to include time estimate short syntax example

* fix(docs): Update wiki entry to reference time estimate short syntax tags
- Updates new task placeholder to reflect changes made in b09aa6e
- Adds list item explaining time estimate short syntax
- Adds reference to wiki short syntax page
2026-05-02 13:57:55 +02:00
johannesjo
30d7e7d717 18.4.3 2026-05-02 00:03:46 +02:00
johannesjo
a168fb3db8 build: run snapcraft from canonical container instead of snap install
The snap-publish workflows have been failing intermittently with
"unable to contact snap store" coming from `sudo snap install snapcraft
--classic`. Both `samuelmeuli/action-snapcraft` and `canonical/action-publish`
use the same install path internally, so neither is a real alternative.

Switch to Canonical's actively-maintained snapcraft-rocks image
(ghcr.io/canonical/snapcraft:8_core24) which has snapcraft 8 pre-installed
and only needs SNAPCRAFT_STORE_CREDENTIALS — no snapd/snap-store handshake
on the runner. promote/upload subcommands and CLI flags are unchanged.

- build-publish-to-snap-on-release.yml: whole job runs in the container
- build.yml: keeps the existing ubuntu-latest job (npm/playwright/electron-builder
  need a normal Ubuntu env), but invokes snapcraft via `docker run` for the
  upload step

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 00:00:44 +02:00
johannesjo
1ab0c855a9 Merge branch 'task/add-updating-docs-as-requirement-when-1621b0' 2026-05-01 23:14:40 +02:00
johannesjo
ee71f4fe8e docs: restore recognition cues in CLAUDE.md sync rules
Add back the "why/symptom" tails that were over-trimmed: selector-based
effects fire on every store change, multi-entity meta-reducer example,
TODAY_TAG.taskIds ordering-only role, store.dispatch non-blocking
behavior, and SYNC_IMPORT "by design" framing. Rules stay one-liners
but now carry enough context for Claude to recognize when they apply.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 23:07:44 +02:00
johannesjo
1b4d0c1710 18.4.2 2026-05-01 23:07:19 +02:00
johannesjo
eddcea7070 docs: trim CLAUDE.md and split E2E reference into e2e/CLAUDE.md
Drop the architecture overview (derivable from code and already
covered in docs/sync-and-op-log/ and ARCHITECTURE-DECISIONS.md),
collapse the long sync invariants into one-liners with pointers,
and dedupe the anti-patterns table against the rule lists. Move
the SuperSync docker-compose block and E2E iteration tips into
e2e/CLAUDE.md where the rest of the E2E reference lives. Tighten
the new docs/documentation-guide.md by dropping content that
duplicates docs/wiki/0.00-Wiki-Structure-and-Organization.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 23:04:03 +02:00
johannesjo
f8efc5d9c9 docs: require wiki updates for user-facing changes in CLAUDE.md
Add a documentation-update rule to CLAUDE.md and extract the
detailed guidance into docs/documentation-guide.md, mirroring the
styling-guide pattern. Defaults wiki edits to Reference (3.XX) notes
and points to 0.00-Wiki-Structure-and-Organization.md as required
reading.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 22:50:01 +02:00
johannesjo
4d3160d2bc build: fix snap build 2026-05-01 22:48:04 +02:00
johannesjo
af7ba05845 ci(ios): bump runner to macos-26 for iOS 26 SDK
Apple now requires Xcode 26 / iOS 26 SDK for App Store uploads
(mandatory since 2026-04-28). Switch the iOS release workflow to
the macos-26 runner and explicitly select Xcode 26.2.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 22:44:57 +02:00
Johannes Millan
ff0f6d2f82 18.4.1 2026-05-01 21:59:17 +02:00
Johannes Millan
fe288fb3b5 Merge branch 'feat/ensureoverlaysclosed-1-backdrop-s-0-1f8487'
* feat/ensureoverlaysclosed-1-backdrop-s-0-1f8487:
  fix(tasks): track focus on inner elements without subtask bubbling
  fix(shortcuts): keep shortcuts working while a tooltip is open
2026-05-01 21:58:50 +02:00
Johannes Millan
2105bbc860 fix(tasks): track focus on inner elements without subtask bubbling
The 'focus' event does not bubble, so clicking a button inside a task
(title editor, hover controls, etc.) left focusedTaskId stale and
subsequent shortcuts targeted the wrong task. Switching to 'focusin'
fixes that, but its bubbling means a nested subtask's focusin would
also reach every ancestor task host and overwrite focusedTaskId with
the parent's id. Add an innermost-task guard so only the closest
<task> ancestor of the event target claims focus. Pair 'focusout' with
a relatedTarget check to avoid clearing focusedTaskId when focus moves
between elements within the same task.
2026-05-01 21:55:30 +02:00
Johannes Millan
744d3b2b28 fix(shortcuts): keep shortcuts working while a tooltip is open
A lingering Material tooltip pane (mat-mdc-tooltip-panel) leaves a CDK
overlay mounted, which made _hasOpenCdkOverlay() report a blocking
overlay and silently swallow every keyboard shortcut until the tooltip
faded out. Tooltips are non-interactive — exclude them from the check.
2026-05-01 21:55:23 +02:00
Johannes Millan
73a77e2a0f test(sync): stabilize hydration timer specs 2026-05-01 21:34:17 +02:00
Johannes Millan
4f15e66f9d 18.4.0 2026-05-01 20:58:26 +02:00
Corey Newton
cf16790b6a
docs: update README to reflect state of wiki (#7450)
The wiki is now ready for general use. I have added some notes regarding
the use of DeepWiki and how it complements the official one. The
`.devin` directory contains a file that should help steer the auto-doc
process. My two primary aims are:
1. avoid polluting the DeepWiki resource with the existing docs in case
   of error propagation and LLM context poisoning
2. Generate a differential of the observed code and the observed content
   in docs/wiki to aid in future doc updates.
2026-05-01 20:56:15 +02:00
Het Savani
601df0815a
fix(notes): prevent header stretching on expansion (#7443)
* fix(notes): prevent header stretching on expansion

* fix(panel): keep expansion panel header height consistent

* refactor(material): move expansion header height override to material overwrite styles
2026-05-01 20:54:22 +02:00
Johannes Millan
908c7e2508 Merge branch 'feat/fix-6b5ef2'
* feat/fix-6b5ef2:
  fix(focus-mode): load overlay chunk immediately to avoid blank screen
2026-05-01 20:48:47 +02:00
Johannes Millan
ec257a4c86 build: lock etc 2026-05-01 20:46:47 +02:00
Johannes Millan
bd60355942 fix(focus-mode): load overlay chunk immediately to avoid blank screen
The @defer block around <focus-mode-overlay> had no explicit trigger
and so defaulted to `on idle`. When isShowFocusOverlay() became true,
the @else branch hid the entire app container while requestIdleCallback
waited for browser idle to fetch the chunk. On a busy page (sync
flushes, store updates) this could take several seconds, leaving the
viewport blank and causing intermittent e2e timeouts on the focus-mode
specs.

Switch the trigger to `on immediate` so the chunk loads as soon as the
overlay is needed. The chunk stays in its own bundle.
2026-05-01 20:45:34 +02:00
Johannes Millan
1b616d8988 chore(sync): correct misleading LWW fallback diagnostics
The fallback branch in _convertToLWWUpdatesIfNeeded rewrote actionType to
'[TASK] LWW Update' but left the payload in its NgRx UPDATE shape. That
payload has no top-level id, so lwwUpdateMetaReducer bails at the
missing-id guard before reaching the recreate branch — meaning the
RECREATE_FALLBACK warn cannot fire from this path. The producer's error
log and the consumer's "Likely cause" message both implied otherwise.

- Drop the dead actionType rewrite; return the remote op unchanged. The
  resulting [Task Shared] updateTask action hits handleUpdateTask, which
  early-exits on missing entity (task-shared-crud.reducer.ts:588) — same
  no-op outcome as before, no cross-entity bookkeeping fires.
- Downgrade OpLog.error -> warn with text reflecting actual outcome
  (entity stays deleted, no consumer-side recovery from this path).
- Fix the consumer warn's "Likely cause" sentence: drop the
  _convertToLWWUpdatesIfNeeded reference; the actual cause is
  partial-baseEntity from a minimal DELETE op payload on the happy path.
- Update three specs in conflict-resolution.service.spec.ts that asserted
  the old actionType rewrite.

Refs: #7447 (closed)
2026-05-01 19:35:28 +02:00
Johannes Millan
6eae546d2e style(test): fix lint errors in lww-update meta-reducer spec 2026-05-01 19:35:28 +02:00
Johannes Millan
01e1776094 fix(task-repeat-cfg): re-anchor schedule when startDate moves earlier (#7423)
When editing a recurring task to move startDate earlier than the existing
lastTaskCreationDay, the live instance was rescheduled to the day after
the OLD startDate (getNextRepeatOccurrence advanced past the stale anchor)
instead of landing on the NEW startDate. Re-anchor via getFirstRepeatOccurrence
in that case. Skipped for repeatFromCompletionDate configs where startDate
is decoupled from scheduling.
2026-05-01 19:35:28 +02:00
Johannes Millan
d3ac26dd25 fix(sync): generalize LWW recreate defaults and backfill task projectId
Follow-up to 2c073067 that closes residual gaps in the LWW Update
recreate path identified in code review.

Gap 1: TASK projectId left undefined.
DEFAULT_TASK Omits projectId (it varies per task), but TaskCopy declares
it required. A partial LWW Update payload that does not carry projectId
left the recreated task with projectId undefined, still failing Typia.
Backfill with INBOX_PROJECT.id, but guard with the same fall-through
pattern as normalizeRestoredTask (task-shared-lifecycle.reducer.ts:110)
so an absent INBOX_PROJECT falls back to the first available project.
Add a matching autoFixTypiaErrors rule for any state already on disk.

Gap 2: PROJECT and TAG hit the same recreate branch unprotected.
The recreate logic is generic across entity types but the DEFAULT_*
merge was TASK-only. Replace the hardcoded TASK branch with a single
RECREATE_FALLBACK registry pairing per-type `defaults` with the
`requiredKeys` that drive the diagnostic warn — pairing makes the
lockstep invariant structural rather than tribal.

Gap 3: null and undefined slip past the partial-payload check.
Producers that emit `null` (rather than omitting a field) bypassed the
`=== undefined` warn AND had `null` overwrite the default in the spread.
Treat both as "missing": warn check uses `== null`, and null/undefined
values are stripped from the action payload before merging with
defaults so they cannot clobber a backfill.

Gap 4: warn copy implied a single root cause.
The producer emitting partial payloads is one source; another is a
local DELETE op carrying only {id} as its base entity. Reword the warn
to name both, and add `attachments` to the TASK enumeration (it was
already in DEFAULT_TASK and the auto-fix rule but missing from the
warn list).

Integration test (#7330): drive the worst-case producer-shape payload
through the meta-reducer and assert the resulting TaskState passes the
real `appDataValidators.task` Typia validator. End-to-end proof that
the user's "Repair attempted but failed" dead-end cannot fire from this
upstream path.

Verified: 85 meta-reducer specs and 12 auto-fix specs pass in
Europe/Berlin and America/Los_Angeles. conflict-resolution and
data-repair specs unaffected.
2026-05-01 19:35:28 +02:00
Johannes Millan
2c463680bb fix(main-header): show route name on Search/Upcoming/Support us/Settings
The page title was showing the last work-context name (e.g. "Today") and
the work-context three-dot menu on `/search`, `/scheduled-list`, `/donate`,
and `/config`, even though those routes are not work contexts. Settings
also rendered a redundant "Global Settings" H1 in the body.

Closes #7426
2026-05-01 19:35:28 +02:00
Johannes Millan
d1a58124bf test(e2e): add regression for recurring missed-day bug #4559
Locks down the cold-start-after-miss path: configures a monthly
"first of month" recurring task on Jun 1, jumps to Jul 3 — skipping
Jul 1 entirely — and asserts the missed Jul 1 instance is materialised
on reload.

Uses setSystemTime rather than setFixedTime so the 1s debounceTime in
TaskDueEffects.createRepeatableTasksAndAddDueToday$ keeps ticking on
the freshly-reloaded page.
2026-05-01 19:35:28 +02:00
Johannes Millan
d7ca08552e build: downgrade eslint 2026-05-01 19:35:28 +02:00
Johannes Millan
68f02d7308 fix(note): respect safe-area insets in fullscreen markdown dialog
The dialog uses position:fixed which bypasses the body's safe-area
padding, so on Android Capacitor (e.g. Pixel 9 Pro) the formatting
toolbar and first textarea line render under the status bar. Apply
safe-area padding directly on the host for body.isNativeMobile and
adjust the keyboard-height calc to keep its math correct.

Closes #7442
2026-05-01 19:35:27 +02:00
Johannes Millan
0ff06346a2 fix(security): redact user-entered content from subtask log
Commit 5772b3416c removed user-entered task data from the main-task
log call to address GHSA-jpwx-59x4-rprg, but the parallel subtask log
in PluginBridge.addTask was missed and still included the full
taskData (title, notes) in the log payload. Logs are exportable via
the bug-report flow, so this leaks the same content the GHSA fix was
meant to redact.

Match the redaction pattern of the main-task log: include only the
internal taskId.
2026-05-01 19:35:27 +02:00
Johannes Millan
8a331c05ba fix(plugins): parse short-syntax time estimate on subtasks (#7437)
The Brain Dump plugin (and any plugin using PluginAPI.addTask with a
parentId) dispatches addSubTask, which is not in the ofType filter of
ShortSyntaxEffects. Subtask titles like "subtask1 15m" were stored
verbatim and the default sub-task estimate was applied instead.

Mirror MarkdownPasteService._parseTimeProps in PluginBridge.addTask:
when shortSyntax.isEnableDue is on, run parseTimeSpentChanges on the
subtask title before createNewTaskWithDefaults. Tags/projects are still
not parsed because subtasks inherit them from the parent.
2026-05-01 19:35:27 +02:00
Johannes Millan
9715c69eac fix(sync): backfill required fields when LWW Update recreates a task
When an LWW Update arrives for a task that does not exist locally,
lwwUpdateMetaReducer recreates the entity by calling adapter.addOne with
the action payload. Some upstream producers (notably the
_convertToLWWUpdatesIfNeeded fallback in conflict-resolution.service)
emit partial payloads that only carry the changed fields. The recreated
task then has required fields (`title`, `timeSpentOnDay`, `tagIds`,
`subTaskIds`) undefined, which fails Typia validation.

dataRepair had no rule for those undefined fields, so the user got stuck
on the "Repair attempted but failed to fully fix data issues" dialog with
no recovery path (issue #7330).

This patch:
- Merges DEFAULT_TASK before addOne in the recreate branch so a TASK is
  always schema-valid, regardless of which producer fed the payload.
- Logs a warn when a TASK LWW Update arrives partial, so the upstream
  producer can be identified from logs in any future report.
- Adds defense-in-depth rules to autoFixTypiaErrors for
  task.entities[*].title, .timeSpentOnDay, .tagIds, .subTaskIds and
  .attachments so dataRepair can recover any state that was already
  corrupted on disk before this fix shipped.

Closes #7330
2026-05-01 19:35:27 +02:00
Johannes Millan
95aa94e2a0 fix(op-log): mark IndexedDBOpenError as handled and log original cause
Stops the GlobalErrorHandler from showing the "please report on GitHub"
dialog for IndexedDB open failures. The hydrator already shows a tailored
recovery dialog; runtime callers (TimeTracking, Worklog, Archive, Sync)
now fail silently with a console log entry instead of asking users to
file what is usually a known transient platform issue (e.g. WebKit bug
273827 on iPadOS).

The wrapper's message and the handled-error marker now include the
original error's name and message, and both throw sites log the wrapper
itself, so the next user report contains the underlying cause needed to
distinguish Chromium LevelDB locks, WebKit "Connection to Indexed
Database server lost", quota errors, etc.

Closes #7415
2026-05-01 19:35:27 +02:00
Johannes Millan
d9f54efbb9 chore(deps): bump stylelint 16->17 + recommended-scss + formatter-github
Paired migration:
- stylelint 16.26 -> 17.9
- stylelint-config-recommended-scss 14.1 -> 17.0
- @csstools/stylelint-formatter-github 1.0 -> 2.0 (requires stylelint 17)

Fix the 26 violations from new deprecation rules:
- 'word-wrap' replaced with 'overflow-wrap' (autofix; word-wrap is a
  legacy alias in CSS3, only matters that stylelint now flags it)
- 'word-break: break-word' (deprecated keyword) replaced with
  'overflow-wrap: anywhere' (the spec-defined equivalent), with stale
  duplicate 'overflow-wrap' declarations consolidated
2026-05-01 19:35:27 +02:00
Johannes Millan
277cf1480e chore(deps): bump cross-env, eslint, jasmine-core, typia to next major
- cross-env 7→10 (CLI-compatible, Node 20+ engine — already met)
- eslint 9→10 + @eslint/js 9→10 (all active plugins support v10;
  fix local rule require-hydration-guard to use context.sourceCode
  instead of removed context.getSourceCode())
- jasmine-core 5→6 (forbidDuplicateNames default flipped; no duplicates
  in our suite — verified by running the full test:once)
- typia 11→12 (public API surface unchanged; validation specs pass)

Lint, build and the typia/auto-fix specs verified green. The 5 pre-existing
failures in immediate-upload.service.spec.ts are unrelated — also fail on
master with the same package.json that has been shipping.

Deferred from this round: stylelint 16→17 (paired migration with
@csstools/stylelint-formatter-github 1→2, separate work),
electron-dl 3→4 (forces full electron-main CJS→ESM migration),
marked 17→18 (ngx-markdown@21.2.0 peer-locks marked at ^17).
2026-05-01 19:35:27 +02:00
Johannes Millan
cbbec922b8 chore(deps): bump non-Angular deps to latest within current major
Patch/minor updates across tooling and runtime:
electron 41.2→41.4, electron-builder 26.7→26.8, prettier 3.7→3.8,
playwright 1.57→1.59, typescript-eslint 8.52→8.59, karma 6.4.2→6.4.4,
core-js 3.47→3.49, nanoid 5.1.6→5.1.11, dotenv 17.3→17.4, glob 13.0→13.0.6,
fs-extra 11.3→11.3.4, tslib 2.7→2.8, baseline-browser-mapping 2.9→2.10,
plus type defs and native binaries (@lmdb/*, @rollup/*).
Build, lint and a sample unit test all pass.
2026-05-01 19:35:27 +02:00
Johannes Millan
6a4253bd23 chore(deps): update Angular ecosystem to latest 21.x
Bumps Angular core/CLI/Material/CDK to 21.2.11/21.2.9, @ngrx/* to 21.1.0,
ngx-markdown to 21.2.0. All within current major; build and lint pass.
2026-05-01 19:35:27 +02:00
Johannes Millan
59681d47a4 fix(tasks): render colored dots for tags in chip-list-input (#7424)
Replace plain title text with <tag> in chip rows and autocomplete
options so the recurring task config (and the two other consumers of
chip-list-input) show colored dots for both selected chips and dropdown
suggestions, matching the rest of the app.
2026-05-01 19:35:27 +02:00
Johannes Millan
2788ff1e65 feat(tasks): show project icons in move-to-project menu
Closes #7425
2026-05-01 19:35:27 +02:00
Johannes Millan
917d44c259 feat(planner): speed up auto-scroll while dragging tasks
Closes #7422
2026-05-01 19:35:27 +02:00
Johannes Millan
28cc431915 fix(planner): prevent estimate hover input from being clipped
Right-anchor the inline-input when used inside the planner time cell so
the expanding edit field grows leftward instead of bleeding past the
planner-day overflow:hidden boundary.
2026-05-01 19:35:27 +02:00
Johannes Millan
a5fb3c4a05 fix(main-header): restore play button on mobile when time tracking is on (#7404)
Reverts d74a621d68. Hiding the play button until a task is being
tracked made starting the timer a 2-step gesture (swipe + menu) on
mobile. Users who don't want the button can already disable it via
app features.
2026-05-01 19:35:27 +02:00
Dastan Medetbekov
c457c6fb36
Update CYBR theme (#7446)
* Update CYBR

Co-authored-by: Copilot <copilot@github.com>

* Changed CYBR theme metadata

---------

Co-authored-by: Copilot <copilot@github.com>
2026-05-01 19:26:02 +02:00
Johannes Millan
b9516b5ac7
Improve OAuth error handling and reporting (#7445)
* fix(plugin-oauth): surface real error and propagate state on local errors

Clicking "Connect Google Account" could show the generic
"Authentication failed!" snack with no detail. Two issues caused this:

1. The catch in connectOAuth swallowed the actual error. Log it and
   include the message in the snack so failures are diagnosable.
2. When the Electron main process emitted a local OAuth error
   (invalid_auth_url, failed_to_open_browser), the IPC payload had no
   state, and handleRedirectError silently dropped any callback whose
   state did not match. Echo the state from the auth URL on the main
   side, and treat missing-state errors as trusted local failures on
   the renderer side (they are not CSRF-relevant). This rejects the
   pending flow immediately instead of waiting for the 5-minute timeout.

* fix(plugin-oauth): apply review feedback on connect-OAuth UX

- Split the try block in connectOAuth so a failure of _loadDynamicOptions
  no longer surfaces as an "Authentication failed" snack on top of the
  success snack. The OAuth connection itself succeeded; loadOptions has
  its own per-field error reporting.
- Sanitize the surfaced error: collapse whitespace and cap to 200 chars
  so a long HttpErrorResponse message doesn't blow up the snack.
- Log the message field instead of the raw Error, per CLAUDE.md
  anti-pattern #11 (log history is exportable).
- Use undefined instead of null for the state echo in the main process
  to match the renderer/preload signatures.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-01 18:45:11 +02:00
Nicholas Dias
7a51d4b06e
feat: allow dev override for local REST API (#7440) 2026-05-01 16:54:26 +02:00
David Vornholt
f33fb79dba
fix(schedule): clamp schedule event titles (#7356) 2026-05-01 13:10:11 +02:00
Florian Bachmann
79c6d903a6
Accept config schema for uploaded plugin (#7414)
* fix(plugin-config): Adds two-way binding of plugin config dialog

* feature(plugin-config): Adds config-schema support for uploaded plugins
2026-05-01 13:07:50 +02:00
Florian Bachmann
6c1a0b54f9
Caldav subtask import (#7409)
* fix(electron): disable webSecurity in dev mode to suppress CORS preflights

* feat(CalDAV): Option to import sub-tasks along with parent

* test(CalDAV): Adds specs for option to import subtasks along with parent

* doc(CalDAV): Adds notion on automatic sub-task import

* feat(CalDAV): Handles grandchildren and ignores subtasks of archived tasks.

* fix(setup): removes policy violating code

* fix(caldav):_resolve N+1 issue

* test(caldav):_adds more tests

* fix(electron):_fix task-widget mock in electron test
2026-05-01 13:07:03 +02:00
Johannes Millan
fcb4b7265b style: fix lint errors in spec files 2026-04-29 21:23:28 +02:00