* Refactor getPickleKey so that it can throw
* Review feedback
* Clarification on why errors are just logged
* Tests for pickling utils
* Revert Service Worker behaviour for pickle key not set
* Try and simplify tryDecryptToken() some more
Make it the caller's responsibility to handle edge cases sensibly.
* Rest of merge
* Additional test coverage
* Remove the no-op enable-usermedia-screen-capturing switch
Only Chrome's own capture permission handler reads it, which Electron does not include.
* Remove the obsolete WebRTCPipeWireCapturer feature flag
Chromium removed the feature in M133 and now always allows PipeWire capture on Wayland.
On Wayland, compositors match windows to installed applications via the
xdg-shell app_id, which Chromium derives from the desktop file name set
through app.setDesktopName(). Element never sets it, so Electron falls
back to the app name ("Element"), which matches neither the desktop file
electron-builder generates (element-desktop.desktop, named after the
executable) nor its StartupWMClass. As a result, e.g. GNOME cannot
associate Element windows with the application and shows a generic
executable icon in the dock/task switcher instead of the Element logo.
Use electron-builder's window-association support: inject desktopName
into package.json via extraMetadata (Electron reads it at startup and
uses it as the app_id) and enable linux.syncDesktopName so the generated
desktop file and its StartupWMClass are named to match. Since desktopName
is derived from the variant name, which is also linux.executableName, the
generated desktop file names are unchanged for both the stable and
nightly variants.
https://www.electron.build/linux#window-association-desktopname--syncdesktopname
Assisted-by: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018VvCBTF5CusjukYB74DDk2
Co-authored-by: R Midhun Suresh <hi@midhun.dev>
* Update playwright to v1.62.0
* Make tsc happy
* Tweak pw config
* Update test snapshots
* Upgrade to playwright 1.62.1
* Add workaround
* Fix duration seconds wrongly rounding down to 0 which makes no sense for a non-zero playback
* Use round instead of ceil
* Tweak empty message length
---------
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
* feat: Add tokenizer mode support for message search
Add user-facing setting to choose tokenizer mode (language-based vs N-gram)
for local message search. N-gram tokenization supports CJK languages and
mixed-language content. Includes confirmation dialog for mode changes
(requires reindex) and automatic checkpoint recovery.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: Add tokenizer mode support for event indexing (desktop side)
Add N-gram tokenizer support to Seshat search index initialization.
Refactors initEventIndex into a testable module with dependency injection,
supporting both language-based and N-gram tokenization modes. Includes
automatic database recreation fallback and unit tests.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: propagate non-reindex errors instead of deleting search index
Previously, any Seshat open failure that wasn't a ReindexError would
silently delete the EventStore and recreate it, losing the user's search
index on transient errors like filesystem locks or passphrase issues.
Restore the original behavior: only handle ReindexError with recovery
logic, and propagate all other errors to the caller (which shows an
error to the user via sendError).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: update lockfile for tokenizer mode branch
* Fix tokenizer mode CI failures
* Address tokenizer mode review comments
* chore: update tokenizer mode copyright headers
* fix: address tokenizer mode lint errors
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Will Hunt <2072976+Half-Shot@users.noreply.github.com>
* Hide the macOS app on ⌘W/close instead of leaving it in a windowless limbo
With titleBarStyle: "hidden" and a single window, the previous close handler hid
only the BrowserWindow on macOS. That leaves Element frontmost with an empty menu
bar and no window — a "limbo state" the user has to ⌘-Tab out of (#32267).
Treat ⌘W / the red traffic-light the same as ⌘H: hide the whole application via
app.hide(), so another app becomes active and Element is brought back from the
dock. Elsewhere (with a tray) closing still hides the window to minimise to tray,
and without a tray the window is allowed to close and quit the single-window app.
A real quit already in progress always wins.
The decision is extracted into a pure resolveWindowCloseBehavior() helper so the
quit / hide-app / hide-window matrix is unit-tested. The second-instance handler
calls app.show() first on macOS, because app.hide() does not clear
BrowserWindow.isVisible() and the existing isVisible() guard would otherwise miss it.
* Test the hide and reveal behaviour, not just the decision that picks it
The close handler and the second-instance re-show still lived inline in
electron-main.ts, a module no test imports, so the only thing under test was the
pure helper that chooses between quitting, hiding the app and hiding the window.
Everything the fix actually does — preventing the default close, leaving
fullscreen first, calling app.hide() instead of window.hide(), and calling
app.show() again when a second instance arrives — was asserted nowhere, and diff
coverage came out at 45%.
Both sides now live in window-close.ts as handleWindowClose and
revealMainWindow, which is what the tests drive. resolveWindowCloseBehavior
stops being exported: with the effects testable there is no reason to assert the
intermediate string a caller never sees, and the tests read as the outcome the
user gets rather than the mechanism chosen to get there.
The two new files also carry my own copyright line rather than New Vector's,
which is what the reviewers have asked for on the sibling desktop PRs.
Tests: the platform x tray x quitting x fullscreen matrix for the close path,
including the window having already gone away, and the hidden, minimised and
non-macOS cases for the reveal path.
* Type the captured leave-fullscreen listener so the test can call it
`BrowserWindow.once` carries dozens of overloads, and `vi.mocked()` collapses them
to a single one — the `will-resize` signature, whose listener takes three required
arguments. Reading the registered listener back out of the spy therefore came back
typed as a three-argument function, so invoking it with none failed
`tsc --noEmit -p tsconfig.node.json` with TS2554 while the test itself passed.
Rather than widen the call at each site, the read now goes through one helper that
views the spy as the only signature the production code ever registers,
`(event: string, listener: () => void)`. That confines the cast to a single place
and leaves both fullscreen tests reading as the thing they simulate: macOS
finishing the leave-fullscreen animation before the hide runs.