* fix(macOS): gracefully quit app
When macOS App is closed via menu item "Quit", make sure that IPC flow completes first.
- close main window instead of directly invoking `quitApp()`
* fix(macOS): use quit-intent flag
- do not touch "minimizeToTray" setting
- instead set "quitRequested" flag to indicate quit intent
- set timeout to cancel quit request, if app fails to respond within 5 seconds
* refactor(macOS): split quit-intent state and add timeout rationale
- Separate the dual-purpose timer handle into an explicit boolean +
named timer to make the semantics obvious.
- Extract the 5s window as a named constant with a comment on why this
particular trade-off (long enough for sync/finish-day, short enough
to not block a subsequent tray-hide click after cancel).
- Fix the misleading "set flag" comment in the no-window branch
(no flag is set there; none is needed).
- Note that the `closed` reset clears the pending timer so it doesn't
keep the event loop alive.
Pure cleanup, no behavior change.
---------
Co-authored-by: Johannes Millan <johannes.millan@gmail.com>
* feat(layout): experimental vertical action strip on right edge
Teleports main-header's action-nav-right to document.body on desktop so
it escapes any ancestor containing-block, then pins it as a vertical strip
at the viewport's right edge via fixed positioning. Reserves the column
with padding-right on .app-container so the right-panel ends to its left.
* feat(layout): right-panel spans full viewport height
Restructure app shell so right-panel is a direct flex child of
.app-container (wrapping main-content via ng-content), letting its
.side extend over the full height of the viewport — including the
header area — rather than starting below the header.
* style(layout): give vertical action strip an elevated surface bg
* style(layout): match vertical action strip styling to left side nav
* fix(layout): stack play/focus buttons vertically in action strip
* style(layout): use solid surface bg for vertical action strip
* fix(layout): keep current-task title visible beside the action strip
In the teleported vertical action strip the current-task title pill was
positioned right:100% of the play button and clipped by the strip's
overflow, and the 48px mini-fab overflowed the 48px rail and got
side-clipped.
Render the title as a position:fixed flyout to the left of the strip
(the strip has no transform/filter so it is not a containing block for
fixed descendants), aligned to the play button across the web /
mac-titlebar / obsidian-header / RTL variants, and drop the play button
wrapper's horizontal margin so it fits the rail.
* fix(layout): stop tooltip overlay from blocking action strip clicks
A tooltip shown 'below' a button in the vertical action strip lands
directly over the next button. The tooltip's cdk-overlay-pane wrapper
(unlike the inner mat-tooltip-component) had no pointer-events:none, so
it intercepted clicks for real users and for Playwright actionability —
breaking ~20 focus-mode/break e2e tests.
* test(focus-mode): use teleport-robust focus-button locator
The action nav is teleported out of <main-header> into a body-level
strip, so 'main-header focus-button button' no longer matches. Drop the
main-header ancestor; 'focus-button button' is unique either way.
* fix(layout): polish vertical action strip (flyout, spacing, bg)
- Current-task title is now a hover-reveal flyout: hidden until the play
button (or the flyout itself) is hovered. Mobile (< 1080px) unchanged
(component still display:none's it there).
- Drop the position:fixed + magic top calc; keep the component's own
absolute + translateY(-50%) so the flyout is pixel-perfectly centred
on the play button regardless of theme/Material density.
- Strip now overflow: visible so the flyout can extend past the 48px
rail (overflow-x can't be visible while overflow-y is auto).
- Re-declare --header-nav-button-gap on the strip: it is scoped to
<main-header>'s :host but the strip is teleported to <body>, so the
var was undefined => every strip button had gap: 0 (cramped).
- Extra margin between the play button and the focus button.
- Strip background now matches the left sidenav (--sidenav-bg).
* fix(layout): uniform vertical spacing between strip buttons
Drop the extra play-button and counters-group bottom margins; one
--header-nav-button-gap (var(--s)) now drives the spacing between every
button in the strip. Measured: all consecutive gaps = 8px.
* feat(layout): make vertical action bar a configurable opt-in
Adds misc.isVerticalActionBar (default off). The right-edge vertical
action strip was previously an unconditional experiment; it is now an
opt-in toggle in Settings > Misc that switches the layout live without
reload:
- app.component: @if branches the DOM between classic (horizontal
header) and vertical (right-panel hoisted to full viewport height);
.app-container gets .has-vertical-action-bar to gate CSS.
- main-header: one-shot ngAfterViewInit teleport replaced with an
effect() that teleports/restores the action nav reactively to the
config flag and the desktop/mobile breakpoint.
- app/right-panel SCSS: strip padding and full-height right-panel are
now scoped to .has-vertical-action-bar; classic layout restored as
the default.
* fix(layout): even vertical-strip spacing for panel-button wrappers
plugin-header-btns, plugin-side-panel-btns, desktop-panel-buttons and
user-profile-button are zero-height wrapper custom-elements. As direct
flex children of the vertical action strip the column gap landed on the
collapsed wrapper instead of the button(s) inside, so their icons were
unevenly spaced next to real buttons like the add button. Make each
wrapper a centered column flex item that stacks its button(s) with the
same gap, and hide truly-empty wrappers so they don't reserve a phantom
gap slot. Verified: all 7 visible strip buttons now 8px apart.
* fix(right-panel): clip transient content overflow during slide animation
.side animates width 0<->* while .side-inner keeps its min-width, so
content briefly spills past the (intentionally overflow:visible) .side
during open/close. Add an isPanelAnimating host class driven by the
@slideRightPanel start/done callbacks and clip :host for that window
only (overflow: clip — no scroll container). Mirrors the existing
resizing/windowResizing transient-state pattern. Verified: class +
clip present only during the ~200ms animation, visible when idle.
* refactor(layout): drop dual DOM for vertical action bar
Single classic layout for both modes; strip is teleported and offset by
--bar-height so the horizontal header keeps owning the title-bar zone
(native drag region + WCO + Mac traffic lights). Drops the 40px
title-bar-collision padding and the right-panel full-height override.
* feat(layout): right-panel side spans full viewport height
Move the header into right-panel's projected .content slot and make
right-panel host full-height. The panel column now starts at viewport
top with the header band only spanning the .content width, similar to
Obsidian / Linear / VS Code.
* fix(layout): only offset vertical strip below WCO band on Win/Linux
Default top inset is now 0 (web, Mac hiddenInset, native-frame Electron
all let the strip start at viewport top — none of them have a window-
control overlay clashing with the right edge). Push the strip down by
--bar-height only on Win/Linux Electron with custom title bar, where
the WCO buttons would otherwise sit on top of the strip's first row.
* style(electron): use compact 32px WCO band on Win/Linux
Shrinks the Windows Controls Overlay height from 44px to 32px so the
native min/max/close buttons sit in a slimmer band — matches VS Code /
Edge slim title bar conventions and frees more of the header zone for
app content. Width stays OS-controlled (~138px); only height is
configurable.
* style(layout): tighten WCO band to 24px and pull strip up to match
Drops WCO_HEIGHT to 24px on Win/Linux and introduces a --wco-height CSS
var mirroring it. The vertical action strip now clears the WCO band by
exactly --wco-height instead of --bar-height (48px), reclaiming the
newly-freed top zone on the right edge.
* style(layout): add breathing room above strip below WCO band
Strip top inset moves from --wco-height to --wco-height + --s so the
first action button isn't flush against the bottom of the window
controls overlay on Win/Linux.
* fix(styles): scope vertical-action-bar tooltip rule to the experiment
The pane-level `pointer-events: none` was needed only because tooltips
in the teleported vertical strip can land over the next button. Apply
it via a new `body.isVerticalActionBar` body class (toggled from
`misc.isVerticalActionBar` in GlobalThemeService, alongside the existing
`isObsidianStyleHeader` effect) so unrelated app tooltips keep their
default pane behaviour.
* refactor(styles): extract vertical-action-bar CSS to its own partial
Move the 130-line experimental strip block out of `src/styles.scss` and
into `src/styles/components/_vertical-action-bar.scss`. Wired via
`_components.scss`. No CSS rules change — pure code organisation.
* merge(master): resolve _components.scss conflict in right action bar branch
Agent-Logs-Url: https://github.com/super-productivity/super-productivity/sessions/1941dd45-72e1-4a3e-92da-912fcfcb9bed
Co-authored-by: johannesjo <1456265+johannesjo@users.noreply.github.com>
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: johannesjo <1456265+johannesjo@users.noreply.github.com>
Round of robustness fixes for the store-screenshot pipeline so the
Liquid Glass captures actually ship MAS-compliant PNGs:
Capture & dimensions:
- Capture by CGWindowID on macOS (`screencapture -l <id>` resolved via
desktopCapturer.getSources matched on window title) instead of by
screen rect. Survives small displays clipping the window and grabs
the full hiddenInset titlebar including traffic-lights regardless of
off-screen position.
- `enableLargerThanScreen: true` on the BrowserWindow when launched in
screenshot mode (gated on a new SP_SCREENSHOT_MODE=1 env var). Keeps
the configured 1280×800 outer from being clamped down to fit the area
below the menu bar / dock — without this, captures came out 20pt
short and Mac App Store rejected the dimensions.
- Drop `--no-sandbox` / `--disable-dev-shm-usage` from the Electron
launch on macOS. They're Linux/CI helpers; on Mac `--no-sandbox`
empirically suppresses the hiddenInset traffic-lights even though the
same SP build launched via `npm start` shows them.
Configurability:
- `SCREENSHOT_CUSTOM_THEME` env var feeds the fixture's `customTheme`
option, so a one-off pipeline run under e.g. liquid-glass / dracula
doesn't require editing spec files.
- New `scrollScheduleUp` helper called from every schedule capture
(desktop slots 03 + 06, mobile 05, tablet 03) — nudges the schedule
scroll-wrapper up ~80px so the captured frame shows context before
work-start instead of being flush against it.
- New slot-02 light variants for desktop (eisenhower) and mobile
(planner-expanded) so both light and dark land at slot 02.
Diagnostics:
- Surface execFile stderr/stdout in the capture-failed Error so
permission failures name the actual cause (`could not create image
from rect`, `not authorized`, etc.) instead of just "Command failed".
- Loud banner on first OS-capture failure plus an end-of-run summary
(via the existing globalTeardown) re-surfacing the warning so it
isn't lost in long log scrollback. Marker file in MASTER_DIR
bridges fixture → globalTeardown.
- Per-capture `[screenshot] <name> → <bin> <args>` log line, plus an
`setBounds requested=… achieved outer=…; content=…` line right after
the resize, so a "missing chrome" run is debuggable from the test
output alone.
- README: macOS Screen Recording permission requirement documented.
Round of refinements on top of the Liquid Glass theme to take it from
"works" to "ships":
- Default to Liquid Glass on Apple Silicon Macs. Web / Intel / Linux /
Windows still default to the system theme. Adds an `isAppleSilicon()`
preload bridge + `IS_APPLE_SILICON` constant, and a CustomTheme
default selector that picks Liquid Glass when both Electron and arm64
are detected.
- macOS title-bar inset for the floating side-nav (titleBarStyle
switched to 'hiddenInset' so AppKit positions traffic-lights at the
standard inset other native apps use).
- Wallpaper handling: blur `.bg-image` directly so the whole page sees
one out-of-focus image (no seam at the side-nav edge), drop
`.main-content` / side-nav backdrop-filters under `body.hasBgImage`
so the source-blur isn't re-blurred at different strengths, and hide
the white/black `.bg-overlay` scrim that was washing out the glass.
- Surface opacity: bump every Tier-A fill (tasks, sub-tasks,
schedule-events, planner-tasks, cards, banner, right-panel, notes,
task-detail, attachments, sidenav) to 0.78–0.95 alpha so text reads
cleanly whether the background is the soft primary-radial gradient
or a user-selected photo. Light + dark aligned to the same alpha.
- Dark-mode further: tasks / sub-tasks / schedule-events / task-detail
go fully opaque (deep RGB stepped ~10 units toward black) so content
surfaces stay legible against the wallpaper while chrome / containers
keep the soft glass material.
- Schedule weekday / week-column headers: bind to a solid --surface-2
fill instead of the translucent --bg-lighter / transparent --bg the
defaults used.
- Task time-badge / repeat-date-badge: solid surface (white in light,
surface-4 in dark, slightly brighter than the task fill so the badge
reads as a raised label rather than a recessed hole). Defaults bound
it to translucent tokens, and the icon underneath bled through.
- Two stylelint suppressions for the new layered-token sections in
_css-variables.scss (Cat B follows Layer 1 primitives on the same
body / body.isDarkTheme selector — intentional duplicate).
* 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
The "Get Authorization Code" button silently failed on Flatpak because
shell.openExternal rejects without renderer feedback when the
org.freedesktop.portal.Desktop talk-name isn't granted. After the user
gave up and reopened the dialog, the second attempt failed again with
`invalid_grant: invalid code verifier` because each call to
Dropbox.getAuthHelper() generated a fresh PKCE verifier that no longer
matched the originally-shown URL's challenge.
Three changes:
- Cache the in-flight PKCE Promise on the Dropbox provider so concurrent
callers and consecutive dialog opens share one verifier+URL pair.
Cleared on successful exchange, on clearAuthCredentials(), and on a
rejected generation (so a one-time crypto failure doesn't poison the
session). Five regression tests cover reuse, success-clear, explicit
clear, concurrent calls, and rejection-recovery.
- Render the auth URL as user-selectable text under a <details>
disclosure. Escape hatch when both shell.openExternal and the
clipboard portal are denied — the user can triple-click to select and
Ctrl+C the URL into a manually-opened browser. Adds
D_AUTH_CODE.MANUAL_URL_HINT translation key.
- Pipe shell.openExternal rejections through
errorHandlerWithFrontendInform so the existing IPC.ERROR snack
channel surfaces a "Could not open the link in your browser" message
instead of swallowing the failure to electron-log. Wrapped in a
try/catch since errorHandlerWithFrontendInform throws synchronously
if the renderer isn't ready.
The Flathub manifest also needs --talk-name=org.freedesktop.portal.Desktop
and --socket=wayland to fully fix the user-reported issue, but that
change lives in the flathub repo.
electron-window-state reads isMaximized() on an already-hidden window in
its `closed` handler, which returns false (electron#27838). Patch the
persisted state file in will-quit using our own wasMaximizedBeforeHide
flag, which is kept accurate by the maximize/unmaximize event listeners.
Defense-in-depth against GPU init failures on Snap/Flatpak Linux where
the main process stays alive but the GPU process crashes at init and
the window never renders. Field data in #7270 (two post-v18.2.4
reports) shows this happens on Ubuntu 24.04+/25.10 regardless of GPU
vendor — the driver is core22 Mesa/libgbm drifted from the host Mesa.
See §12–§17 in docs/research/snap-wayland-gpu-fix-research.md.
Mechanism (electron/gpu-startup-guard.ts):
- Content-based crash marker in userData with {ts, electronVersion}.
Written before app.whenReady() on confined Linux; cleared via
IPC.APP_READY after Angular boot — not ready-to-show, which fires
on blank/broken renderers too.
- Previous-crash detection: marker present AND recent (<5 min) AND
matching Electron version. Staleness bound + version gating drop
systemd-SIGKILL-mid-boot and post-upgrade-residue false-negatives.
- Env overrides SP_DISABLE_GPU=1 / SP_ENABLE_GPU=1 work on all
platforms; auto-detection is Linux+Snap/Flatpak-only.
- Non-ENOENT fs errors logged at warn — a swallowed write-fail
previously meant the guard could re-enter the loop with no
diagnostic trail; a swallowed unlink-fail meant a successful boot
could get permanently stuck in crash-recovery.
Fallback flag bundle (start-app.ts):
--disable-gpu
--disable-software-rasterizer
--ozone-platform=x11
The pair matches Chromium's GPU integration tests' "no GPU process"
invariant; DisplayCompositor handles 2D in the browser process
without spawning a GPU child. app.disableHardwareAcceleration()
alone does NOT — verified against electron/electron#17180/#20702.
The extra --ozone-platform=x11 closes the Chromium 140+
browser-side Wayland/libgbm-dlopen gap on Flatpak (redundant with
the existing Snap X11 widening branch; last flag wins).
Novelty: R3 survey of VS Code, Slack, Insomnia, LosslessCut,
Obsidian flatpak, Firefox snap, and Canonical's gpu-2404-wrapper
found no peer Electron-snap implementing an equivalent reactive
crash-detection + auto-fallback. Prevailing patterns are manual
--ozone-platform=x11, proactive env-sniff, or do-nothing.
Review: §14–§15 multi-agent verification on the original PR #7273;
re-reviewed via 7-agent multi-review (6 Claude focus-agents + Codex
CLI) + 5 research agents here (§17). Live-tested with
SP_DISABLE_GPU=1 on a KDE/X11 dev host — window rendered normally
via DisplayCompositor.
Refs: #7270
* 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)
* 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>
The "Get Authorization Code" button silently fails on some Linux
packagings (Flatpak, AUR) because shell.openExternal() can reject
without visible feedback — the wasOpened boolean check in
openUrlInBrowser has been dead code since the call was migrated from
shell.openPath to shell.openExternal (which returns Promise<void>).
- Add a "Copy URL" button to the auth dialog as a reliable fallback
users can paste into a manually-opened browser.
- Replace the dead wasOpened check with a .catch() that logs the
rejection reason, so future portal failures surface in electron logs.
- Add regression tests documenting the PKCE verifier/challenge
invariants that make stale auth codes fail with "invalid code
verifier" when the dialog is reopened.
- Separate JsonParseError and SyncDataCorruptedError handlers in sync wrapper
- Handle corrupted remote data gracefully instead of throwing
- Add getOrGenerateClientId() as unified entry point, eliminating dual injection
of ClientIdService + CLIENT_ID_PROVIDER in snapshot-upload, file-based-encryption,
and sync-hydration services
- Use crypto.getRandomValues() instead of Math.random() for client ID generation
- Warn user when stored client ID is invalid and must be regenerated
- Fix flaky e2e supersync tests (premature waitForURL match on daily-summary URL)
On macOS, native quit fires before-quit BEFORE window close events. The
before-quit handler was calling ipcMain.removeAllListeners(), which destroyed
the BEFORE_CLOSE_DONE IPC listener. The close handler then sent NOTIFY_ON_CLOSE
to the renderer (for sync/finish-day callbacks), the renderer responded with
BEFORE_CLOSE_DONE, but the listener was gone — causing a permanent hang.
Fix: intercept before-quit when isQuiting=false and delegate to win.close(),
which re-enters the existing close handler and runs before-close callbacks
normally. Move ipcMain.removeAllListeners() to will-quit, which fires after
all windows are closed and all IPC flows are guaranteed complete.
Also improves close event log to include pending before-close handler IDs.
Rename the "overlay indicator" feature to "task widget" across the
entire codebase for clearer naming. Includes file renames, config key
migration, IPC event rename, and translation key updates.
Migration handles old persisted data from both the `overlayIndicator`
config key and the deprecated `misc.isOverlayIndicatorEnabled` field.
The `taskWidget` type is made optional in GlobalConfigState to prevent
Typia auto-fix from overwriting migrated values during upgrade.
Google's Desktop OAuth client requires loopback redirect URIs
(http://127.0.0.1:<port>) and blocks embedded webviews. This replaces
the custom URI scheme + BrowserWindow approach with a temporary loopback
HTTP server and shell.openExternal for the system browser.
- Add PLUGIN_OAUTH_PREPARE IPC to start loopback server and return port
- Open auth URL in system browser instead of Electron BrowserWindow
- Make getRedirectUri() async to support IPC port retrieval
- Validate OAuth config before starting loopback server to prevent leaks
- Force 200 status on OPTIONS preflight responses in CORS bypass
* fix: address review findings from daily changes
- Add setPermissionCheckHandler alongside setPermissionRequestHandler
for defense-in-depth in Electron (permission queries now also denied)
- Fix tagIds merge ordering in issue service: provider adapter tags are
now merged with default tags instead of being silently overwritten
- Execute deleteTask action last in automations to prevent subsequent
actions from failing on a deleted task
- Add unit tests for getTaskDefaults logic (TODAY_TAG filtering,
defaultNote, tagIds merging, deduplication, context tags)
- Add test for deleteTask execution ordering in ActionExecutor
https://claude.ai/code/session_01UhoR5g7RQgm4E6bZCvU9PS
* refactor: revert YAGNI changes from review fixes
- Revert tagIds merge in issue.service.ts: no provider currently sets
tagIds in getAddTaskData, so merging solves a hypothetical problem
- Revert deleteTask sort in action-executor: the automations feature is
new and nobody has hit this edge case yet
- Remove tests for reverted behavior, keep tests for existing features
(TODAY_TAG filtering, defaultNote, note override prevention)
https://claude.ai/code/session_01UhoR5g7RQgm4E6bZCvU9PS
---------
Co-authored-by: Claude <noreply@anthropic.com>
Enable webSecurity (same-origin policy) since CORS is already handled at
the session level via onBeforeSendHeaders and onHeadersReceived. Add
setPermissionRequestHandler to deny unnecessary permissions (webcam,
microphone, geolocation). Improve certificate-error logging with warn
level and URL context.
Bundle preload.ts into a single file so all local imports (e.g.
ipc-events.const) are inlined at build time. This allows removing
sandbox: false since the bundled preload only requires the built-in
electron module, which is allowed in sandboxed preloads.
The preload script uses require() for local modules which is not
supported in Electron's sandboxed preload environment (default since
Electron 20). This only affects Electron's preload API restrictions,
not Chromium's OS-level process sandbox which remains active.
- Make overlay window resizable with persisted bounds via simpleStore
- Add new OverlayIndicatorConfig section (isEnabled, isAlwaysShow, opacity)
- Move setting from misc.isOverlayIndicatorEnabled to overlayIndicator
with migration in reducer
- Add responsive CSS with scale variable (full/tiny modes)
- Add always-show mode that keeps overlay visible when main window is open
- Add opacity slider (10-100%) applied as CSS variable
- Consolidate overlay init into single updateOverlayEnabled path
- Remove dead code: updateOverlayTheme, setIgnoreMouseEvents, unused
shortcut param
Previously the 1px border only appeared with the custom title bar
enabled. Now it shows for all Electron windows and is hidden during
fullscreen via IPC-driven body class toggling.
- Gate blur() to Windows only (not supported on Wayland, limited on macOS)
- Add setTimeout delay in ready-to-show to match showOrFocus pattern
- Add webContents.focus() in ready-to-show for renderer-level focus
- Add isDestroyed() guards in setTimeout callbacks
- Only restore focus on resume/unlock if window was visible before
suspend/lock to avoid surfacing hidden/minimized windows
On Windows, BrowserWindow.show() can silently fail to acquire keyboard
focus after a system reboot (electron#20464). This leaves the window in
a "phantom focus" state where clicks work but typing does not.
- Add blur()+focus() cycle after show() in ready-to-show handler
- Add webContents.focus() in showOrFocus() delayed callback
- Add showOrFocus() call in resume/unlock-screen power monitor handlers
Fixes#6663
- Fix isLoading staying true after search results arrive in issue panel
- Use getPayloadKey() for correct entity key derivation in conflict resolution
- Update archive-wins comments/logs to reflect all op types, not just updates
- Add archive-wins unit tests for local/remote archive vs UPDATE/DELETE
- Tighten WebDAV archive E2E assertion to deterministic archive-wins check
- Add @deprecated JSDoc to legacy pfapi meta-model-ctrl.js
- Refactor wasMaximizedBeforeHide from exported let to getter/setter
* fix(window): restore maximized window state from startup/minimized/tray
Changes:
Call `win.restore()` before `win.show()` to ensure the window returns to its previous state when restored from startup/minimized/tray.
Fixes#5466
* fix(window): add manual tracking of maximized window state before hide
Changes:
add wasMaximizedBeforeHide flag to manually track and handle maximized window state reliably across all platforms
* fix(window): add manual tracking of maximized window state before hide
Changes:
add wasMaximizedBeforeHide flag to manually track and handle maximized window state reliably across all platforms
- Replace deprecated `selector:` properties with proper Electron `role:` in macOS menu
- Add standard macOS menu items (hide, hideOthers, unhide)
- Ensure before-close handlers always call setDone() to prevent app hanging
- Change sync error dialog from confirm() to alert() since result was ignored
The `in` operator checks for array indices, not values, so the condition
was always false. This prevented User-Agent header removal for GitHub,
Office365, and Outlook requests.
- Use CSS env(titlebar-area-width) for dynamic DPI-aware spacing
- Increase fallback width from 96px to 140px to prevent button overlap
- Add theme-aware semi-transparent background to window controls
- Remove duplicate MentionItem interface definition from mention-config.ts
- Fix TemplateRef type to match Angular's expected template context structure
- Update type casting for TagCopy and ProjectCopy arrays in add-task-bar component
- Remove unused CustomEvent interface from mention directive
- Fix syntax error in electron main-window.ts
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
# Conflicts:
# electron/main-window.ts