The attach-dialog entry point moved out of the task context menu in
PR #7314 and now lives in the detail panel. Update the WebDAV sync
attachment test to use openTaskDetailPanel() and click the attachment
input-item instead of right-clicking and looking for an "Attach" menu
item that no longer exists.
* fix:google calendar text exceeding background
* Chore: removed comments
* fix(issue-panel): wrap long provider names instead of clipping
Replace the clip-and-tooltip approach with white-space: normal on the
button label so long provider names (e.g. "Google Calendar (iCal)")
wrap to a second line rather than overflowing or being silently cut
off. Also reverts unrelated package-lock, source-map, and lint-comment
changes that snuck into the original PR.
Refs #7331
---------
Co-authored-by: Johannes Millan <johannes.millan@gmail.com>
The plugin-api contract for fieldMapping.toTaskValue/toIssueValue declares
ctx as { issueId: string; issueNumber?: number }, but the host adapter
was constructing ctx with issueId only at both call sites
(_extractTaskFieldsFromIssueWithSyncValues and _applyFieldMappingPull).
This caused mappings that interpolated ctx.issueNumber, like the GitHub
plugin's title mapping (`#${ctx.issueNumber} ${title}`), to render
"#undefined ..." whenever the title was pulled via the update path.
The initial add-task path was unaffected because titles came pre-formatted
from mapSearchResult.
Reads issue.number from the PluginIssue record at both sites and
includes it in ctx, falling back to undefined when absent so non-GitHub
plugins are unaffected.
Adds an advanced checkbox on the GitHub issue provider that, when enabled,
drops the hardcoded `is:issue` filter in both backlog auto-import and
in-provider search. Lets users sync PRs (assigned, authored, or
review-requested) into their backlog with a query like
`state:open is:pull-request (assignee:@me OR review-requested:@me)`.
Default is off, existing behavior is unchanged.
The JSDoc promised "first occurrence on or after startDate," but the
implementation anchored on `max(startDate, today)` — so a past startDate
was silently advanced forward. This was the root cause of #7344 and
forced the introduction of `getFirstOccurrenceAnchor` as a call-site
workaround.
- Refactor `getFirstRepeatOccurrence` to always anchor on startDate.
Drop the unused `today` parameter. Return null when startDate is
missing instead of silently falling back to 1970-01-01; existing
callers already handle null.
- Delete `getFirstOccurrenceAnchor` + spec — the workaround is obsolete
now that the util honors its contract.
- Switch `rescheduleTaskOnRepeatCfgUpdate$` to `getNextRepeatOccurrence`.
Editing an existing recurring cfg needs the next occurrence from
today, not retro-anchoring on startDate.
- Rebase deterministic spec tests whose `startDate` was past-dated but
expected today-anchored results under the old contract. Intent is
preserved; only the setup now matches the contract explicitly.
Follow-up to #7344. When a timed task with a past dueWithTime was converted
to recurring, the preserved first-occurrence date produced a past remindAt
and the reminder module flagged it as overdue, firing a "missed reminder"
popup immediately on save.
Clamp the anchor to today when cfg.startTime is set and the planned day is
past. Non-timed past-day preservation (the #7344 fix) is untouched.
The paste field sat below the "Get Auth Code" button, so on iPhone the
on-screen keyboard covered it and users could not paste the code. After
the user taps the link the field now moves to the top of the dialog and
receives focus synchronously with the click, so iOS opens the keyboard
on a visible target. Adds autocomplete/autocorrect hardening to avoid
mangling pasted tokens.
Fixes discussion #7340.
- Rename util getAnchorDateForRepeatCfgConversion → getFirstOccurrenceAnchor
to match neighboring "what it returns" naming convention
(getEffectiveRepeatStartDate, getNewestPossibleDueDate, etc.).
- Collapse the duplicated multi-line "why anchor" comments at both call
sites into single-line pointers; full rationale lives in the util JSDoc.
- JSDoc on the util now explicitly documents why rescheduleTaskOnRepeatCfgUpdate$
does NOT use it (different semantics for cfg edits vs. initial conversion).
No behavior change. Follow-up polish on the #7344 fix.
The "X starts at {{start}}" countdown banner was rendering remindAt (the
reminder fire time, e.g. 2:30 for a 30-min-before reminder) instead of
the task's actual scheduled start (dueWithTime, e.g. 3:00), confusing
users about when the task actually begins. Fall back to remindAt for
reminder-only tasks that have no dueWithTime.
Also extend the "leave banner as is" early-return guard to include
dueWithTime, otherwise rescheduling a task while keeping the same
reminder offset would leave a stale time in the banner.
Closes#7343
When an existing task with a past dueDay was converted to a recurring
task, both conversion effects called getFirstRepeatOccurrence with
`new Date()` as the anchor, which silently advanced the first occurrence
to the next future date matching the recurrence pattern — shifting the
task out of the user's planned date without consent.
Anchor on the task's existing dueDay only when it equals cfg.startDate
(the dialog default). If the user overrode startDate, fall back to
today — preserving existing behavior for all other flows.
Closes#7348.
Sizing
- Clock face scales 175/225/280/110px across breakpoints; digits scale
44/58/72/28px via --clock-time-size. Tokens hoisted to .clock-display.
- Breathing dot (flow mode) tracks the clock face at 80% of diameter.
- Duration slider fills the clock face in countdown prep and its
editable text scales with the circle (~16% of diameter).
- tabular-nums + line-height: 1 on .clock-time so countdown digits
don't shift width/metrics while ticking.
Layout
- Pomodoro settings cog moved inside the clock face, centered at
bottom; fades in on hover like the other in-circle controls.
- --control-offset derived from clock-face/clock-time/icon-button so
pause/done icons sit ~4px from the digits at every breakpoint,
clamping to --s on the smallest circle.
- Duration slider hides the underlying clock digits via an
is-duration-slider-active class bound from isShowDurationSlider().
Component API
- input-duration-slider now accepts --input-duration-slider-size and
--input-duration-slider-value-size (defaults 130px / 16px preserved
via var() fallbacks — the three existing dialog consumers are
unchanged).
- breathing-dot accepts --breathing-dot-size (default 144px preserved).
POST /tasks now accepts `parentId` and creates a subtask via
TaskService.addSubTaskTo (inheriting the parent's projectId). Invalid
parent references return 404 PARENT_NOT_FOUND; nested parents return
400 INVALID_PARENT. `subTaskIds` on POST and `parentId`/`subTaskIds`
on PATCH are now rejected with 400 UNSUPPORTED_FIELD instead of being
silently dropped.
Fixes#7346
The existing copy ("Data Damage Detected" / "Your data appears to be
damaged" / "This should not normally happen") implied a defect when the
dialog actually surfaces routine Checkpoint-D cleanup of orphan
references after Last-Write-Wins resolution of concurrent cross-device
edits — working as designed, just communicated as a bug.
Reword D_DATA_REPAIR_CONFIRM, D_DATA_REPAIRED, and the DATA_REPAIRED
toast to describe the behavior accurately while preserving the "report
if frequent" invitation. No behavioral change.
Catalogs the six post-sync corruption shapes that can trigger the
"data was automatically corrected" alert. Calls isRelatedModelDataValid
directly to bypass Typia and isolate cross-model checks.
Attach the console listener in the legacy-migration helper after
page.unroute('**/*.js') so the benign `Failed to load resource:
net::ERR_FAILED` messages from the intentional seeding-phase aborts
stop surfacing as console errors. pageerror stays attached early
since no JS runs while bundle loads are aborted.
Chromium's StatusIconLinuxDbus writes the tray PNG to TMPDIR and hands
gnome-shell the path via DBus IconThemePath. Inside a strict snap, /tmp
is bind-mounted to /tmp/snap-private-tmp/snap.<name>/tmp with mode 0700
on the parent, so gnome-shell (outside the mount namespace) cannot
stat the file and falls back to the three-dot placeholder icon.
Redirecting TMPDIR to \$XDG_RUNTIME_DIR (/run/user/\$UID) puts the
write where both sides can read it. Matches Signal Desktop's snap and
the official Electron snapcraft guide. electron-builder's SnapOptions
docstring still claims this is a default but app-builder-lib 26.8.1's
snap target no longer injects it.
Refs #7298
Template binds `countdownTime$ | async` which is `number | null`. The
function already tolerated `null` for `total`; widen `remaining` the
same way so strict template type checking passes. Also pick up the
prettier indentation fix on the progress-circle element.
The non-native describe block stubbed cfg$ as { subscribe: () => {} }
which worked while the effect only subscribed to cfg$ lazily. After
6614dfa4 introduced a _reminderCfg$ class-field initializer that pipes
on cfg$ at construction time, the stub fails with
"cfg$.pipe is not a function" for all 4 non-native tests.
Swap to NEVER — a real Observable that never emits. Matches the
stub's intent and has .pipe().
Follow-up to 6466ddc0 addressing multi-agent review feedback.
- Extract _reminderCfg$ = cfg$.pipe(map(c => c?.reminder),
distinctUntilChanged()). Both scheduling effects now subscribe to
this narrow slice, so unrelated global-config edits (theme, sync,
pomodoro, etc.) no longer trigger reschedule loops and serial
native bridge calls.
- Tighten the flip-disabled test to assert the notification IDs
passed to cancelReminder, not just the call count.
- Replace `any` in the new spec blocks with ReminderConfig,
TaskWithReminder, and a small TestCfg shape.
- Update SYNC-SAFE JSDoc to note the cfg reactivity.
Refs #6980
The "Disable all reminders" setting was only enforced at the in-app
dialog layer. On Android, native notifications scheduled via
AlarmManager kept firing regardless of the toggle, because the mobile
NgRx effects did not observe the config.
- scheduleNotifications$ now subscribes to cfg$ and, when
disableReminders is true, cancels every tracked reminder and
short-circuits further scheduling.
- scheduleDueDateNotifications$ extends its existing disabled branch
to respect the master disableReminders flag in addition to
notifyOnDueDate.
Known remaining gap: SyncReminderWorker (WorkManager) can still
schedule reminders from the server every 15 min for users with
background sync configured. Tracked as a follow-up.
Refs #6980
The sync conflict dialog title and body still hardcoded "Dropbox" in 9
locales even when users sync via WebDAV, making the prompt misleading.
English was corrected in 2020 but the translations never got updated.
Fixes#7339
* feat: add plugin data reinit api
* test(plugins): stub data init in bridge spec
* test(plugins): stub data init in counter bridge spec
---------
Co-authored-by: Aamer Akhter <aamer_akhter@users.noreply.bitbucket.org>
* fix(tasks): use attachment title as task name when pasting a bare URL
When a URL is pasted as the only input and urlBehavior is 'extract',
the URL is removed from the title and moved to an attachment, leaving
the task title blank. Now, if the title is empty after URL extraction,
the first attachment's title (derived from the URL basename) is used
as the task name.
Fixes#6444
* test(tasks): add unit test for bare URL paste using attachment title
* fix(tasks): handle duplicate URL edge case and add root URL test
Address review feedback:
- Use allUrls[0] instead of newAttachments[0] so the fallback
fires even when the pasted URL already exists as an attachment
(newAttachments is filtered by filterDuplicateUrlAttachments)
- Remove redundant .trim() — removeUrlsFromTitle already trims
- Add test for root URL (https://example.com) → domain basename 'example'
* ci(electron): verify packaged app.asar and smoke-test launch
Adds coverage for the gap that let #7320 ship: CI built electron TS
and ran electron-builder on release, but never exercised the packaged
binary. A stray relative import reaching out of electron/** was
compiled by tsc into src/app/util/*.js, fell outside the files glob
in electron-builder.yaml, and was missing from app.asar — the main
process crashed on launch for every 18.2.7 user.
Two new checks, both running on ubuntu-latest:
1. tools/verify-electron-requires.js extracts app.asar and re-resolves
every relative require() under electron/** against the packaged
tree. Runs on every PR that touches electron/**, electron-builder
config, package.json, or the workflow itself. ~10s after the build.
2. A launch-under-xvfb smoke test starts the packaged binary, waits
30s for a crash, and greps stderr for 'Cannot find module' /
'Uncaught Exception'. Runs on push to master, release tags, and
manual dispatch (not every PR — needs xvfb setup + idle wait).
Exposed as npm run electron:verify-asar for local use after dist.
* ci(electron): harden smoke workflow and verify script from review
Review feedback on the previous commit surfaced a handful of real
defects. Addressed here:
verify-electron-requires.js
- Guard resolved paths against escaping the extracted asar tree.
Without this, a relative require with enough `..` climbs above the
temp dir and Node's resolver hits the host filesystem, masking a
genuinely missing module behind a stray file on the CI runner.
- Walk .cjs and .mjs files too. electron/simple-store.test.cjs ships
via the `electron/**/*` glob and was being skipped.
- Move cleanup out of the path where `process.exit(1)` runs inside a
`try/finally` (synchronous exit does not guarantee `finally`), so
the temp dir is reliably removed.
- Code comment noting that esbuild-bundled preload.js is opaque to
this walker — that coverage comes from the launch smoke test.
electron-smoke.yml
- Broaden pull_request paths filter. The previous filter only matched
electron/**, which is exactly the wrong answer: #7320's root cause
was a file under src/app/util/ that electron/ imported. Now uses a
negative list — docs, translations, android/, ios/, other workflows.
- Explicit Xvfb screen geometry (`-screen 0 1280x720x24`) and
`--disable-dev-shm-usage` on the Electron side. Both known CI
flake preventers.
- Expanded crash-marker regex: SIGSEGV, Segmentation fault,
TypeError:, ReferenceError:, FATAL ERROR, Check failed. Also scan
the log during the liveness loop, not just at the end — catches
main-process exceptions that get logged without killing the
process.
- Move the launch log under .tmp/smoke/ so upload-artifact@v7 reliably
finds it on failure; /tmp paths are fragile across runner images.
- Sweep stray electron worker processes on teardown via pkill.
---------
Co-authored-by: Claude <noreply@anthropic.com>
* refactor(tasks): reduce detail panel and context menu overlap
Move noun-style edits (estimate dialog, deadline dialog, attachment dialog)
out of the task context menu and expose them only through the detail panel,
keeping the menu focused on state-change verbs. Add quick-schedule chips
(Today/Tomorrow/Next Week), an unschedule button, and a remove-deadline
affordance directly on the detail panel so panel users keep the same
fast paths the menu offered.
https://claude.ai/code/session_01JZT5LytmhbkXLA1PKkXnz2
* fix(tasks): revert quick-schedule chips on detail panel due-date row
The inline Today/Tomorrow/Next Week chips intercepted clicks on the
schedule row, breaking the existing "click row to open schedule dialog"
interaction (covered by the reminders-default-task-remind-option e2e test),
and the quick-schedule handlers were missing the edge-case logic from the
context menu's _schedule() (time preservation when rescheduling a
dueWithTime task, already-planned guard). Keep the × unschedule and ×
remove-deadline affordances which only render when the relevant state is
set and don't interfere with the unscheduled-task click path.
https://claude.ai/code/session_01JZT5LytmhbkXLA1PKkXnz2
---------
Co-authored-by: Claude <noreply@anthropic.com>
electron/backup.ts imported getBackupTimestamp from src/app/util/, which
tsc compiled alongside the source. electron-builder only packages
electron/** and the Angular renderer bundle, so the compiled util was
missing from app.asar and the main process crashed on launch with
"Cannot find module '../src/app/util/get-backup-timestamp'".
Moved the util to electron/shared-with-frontend/ (existing convention
for code shared between main and renderer), updated all importers.
Fixes#7270
Refs #7315, #7323, #7265, #7320
Zoneless change detection runs on TestBed.createComponent, so the
template's pipes (DateTimeFormatService → DateAdapter) and child
components (tag-list → WorkContextService → LOCAL_ACTIONS → Actions)
all get instantiated — none of which the ngOnDestroy tests need.
The previous WorkContextService stub only covered one branch.
Override the component template with an empty string before
compileComponents (same pattern as dialog-edit-task-repeat-cfg.spec)
so only the component class is exercised.
The new "destroy clears unhandled deadline reminders" describe block
creates DialogViewTaskRemindersComponent via TestBed.createComponent.
The dialog's standalone imports include TagListComponent, which injects
WorkContextService, which injects LOCAL_ACTIONS, whose factory calls
inject(Actions) — and Actions needs ScannedActionsSubject, which
provideMockStore does not provide. Result: NG0201 thrown in afterAll
once per test in the describe, aborting the suite at ~5228/8662.
Stub WorkContextService with just the activeWorkContextTypeAndId\$
stream TagListComponent reads, so the real constructor (and its
LOCAL_ACTIONS → Actions chain) never runs.
Calling TestBed.inject(MockStore) in beforeEach instantiated the test
module, so the subsequent TestBed.overrideProvider(MAT_DIALOG_DATA) in
createComponent threw "Cannot override provider when the test module
has already been instantiated" across all 7 destroy-cleanup specs.
Move the store inject and dispatch spy into createComponent, after the
override runs, so TestBed stays un-instantiated until the per-test
MAT_DIALOG_DATA value is registered.
packages/vite-plugin/package.json was bumped to vite ^6.4.2 in
4b9bd23e56 but dependent plugin lockfiles still referenced ^6.0.0.
npm install during the e2e build brought them in sync and hoisted
vitest's nested esbuild binaries, dropping ~500 redundant entries.
- Document intent of editReminder cancel path: when user cancels the
inner schedule dialog, the outer dialog closes and the destroy handler
clears deadlineRemindAt (same as ESC). Prevents the worker from
re-firing the past-due reminder.
- Replace page.waitForTimeout with expect().not.toBeVisible({timeout}) in
the deadline ESC e2e test so it fails fast if the dialog reappears and
complies with the "no waitForTimeout" rule in e2e/CLAUDE.md.
- Capture realTodayStr per test in is-deadline-approaching spec to close
the (narrow) midnight-rollover flake window.
.deadline-btn.mat-accent mat-icon needed the same \!important override
as .mat-warn mat-icon to beat Material's icon color variable. Without
it, setting color="accent" on the button left the icon in its default
neutral color.
The deadline feature spec (issue #4328) promised green/amber/red urgency
tiers, but only the overdue-red state was implemented. Preview testers
flagged the missing intermediate tier.
Adds isDeadlineApproaching(task, todayStr) returning true when an active,
non-overdue deadline is within two calendar days. Task row badge now
shows 'warn' (red) when overdue, 'accent' (amber) when approaching, and
default color otherwise.