`appVersion: process.env.npm_package_version ?? '0.0.0'` only worked in
dev — pnpm sets that env var while running its lifecycle scripts. In
the packaged Electron build npm_package_version is unset, so every
installed user saw `v0.0.0` on the Changelog page and the version badge
permanently flagged them as "Update verfügbar" against their own
actually-current version.
Replace with a static `import pkg from '../package.json'` so Vite
inlines the version string into the preload bundle at build time. The
release script bumps package.json before electron-builder runs, so the
inlined value always matches the freshly-released version.
resolveJsonModule + esModuleInterop are already on in tsconfig.node.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Critical hotfix for the 0.17.0 regression: users upgrading from 0.16.x
were logged out, and their next login wrote a fresh empty secure-store
on top of the original ciphertext — destroying device keys irrecoverably.
Why it happened: loadState used a blanket `catch {}` that conflated
"file doesn't exist (genuine new user)" with "file exists but can't be
decrypted (DPAPI / OSCrypt quirk after the install rename)". Both paths
returned an empty Map; the next scheduledSave then overwrote the
original .bin file with a fresh blob.
Fix:
* Separate ENOENT from decrypt/parse failures. ENOENT → empty Map. Any
other read error → log, empty Map (no quarantine, matches old
behaviour for transient lock issues).
* When decrypt/parse fails the original file is renamed to
<file>.broken-<iso-ts> BEFORE returning empty Map. The next save
writes to a fresh file; the original ciphertext is preserved on disk
so a future build (or manual recovery) can still get at the bytes.
* Loud console.error around the failure so future regressions surface
in main-process logs.
main.ts: move setPath('userData', appData/ChatApp) BEFORE setName so
any productName-derived path caching inside setName can't beat us to
it. Add a startup log of the resolved paths so future debugging has
hard evidence instead of guessing.
Affected users on 0.17.0 should still recover via Settings → Backup
Wiederherstellen (account-level keys are unchanged); this fix prevents
the data destruction for anyone who hasn't upgraded yet.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ScreenSharePickerModal now exposes Auflösung (Auto · 720p · 1080p · 1440p
· 4K) and FPS (30 · 60) as separate pill rows instead of bundled quality
presets — users can pick "1440p · 30 fps" or "4K · 30 fps" which the old
preset list didn't surface. The underlying screenShareSettings framerateOverride
slot already existed; the modal just stopped resetting it to null on every
start and now plumbs the chosen FPS through to startScreenShare.
Cinema-mode fullscreen on Windows had two defects:
1. Maximized → fullscreen left the taskbar drawn on top of the window
because DWM kept the maximized work-area constraints. We now unmaximize
first so DWM recomposes cleanly and setFullScreen actually covers the
whole monitor including the taskbar strip.
2. Esc out of cinema came back as a small floating window even when the
user had been maximized before clicking the Vollbild button — the
unmaximize from (1) was never undone. We now memo the pre-fullscreen
maximized flag per window-id and call win.maximize() once the
leave-full-screen event has fired.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
App productName becomes Netralax (driving exe name and window title);
existing installs keep their %APPDATA%\ChatApp profile via an explicit
app.setPath('userData', appData/ChatApp) so no Login/Sounds/Secret store
data is lost.
The Windows taskbar overlay now renders a red bubble with the actual
unread count (Discord parity) instead of just a static red dot. Renderer
paints a 64×64 PNG via canvas — full-bleed red circle, white bold count
with a "99+" cap, no outer ring — and passes the data URL through the
existing setTrayUnread IPC. Main decodes via nativeImage and applies it
as the BrowserWindow overlay icon. Falls back to the static dot if the
renderer canvas pipeline is unavailable.
Also: app.setName('Netralax') + setAppUserModelId('cloud.netralax.desktop')
for Windows taskbar grouping and notification source attribution, and
release.mjs now reads productName dynamically from package.json so the
artifact lookup stays correct after the rename.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Disable the previous auto-share of conversation keys to newly-registered
devices: a stolen password / new device registered by an attacker no
longer automatically grants history access. Backup-Restore (which
restores the old device-id) still opens existing wraps as before.
Phase 1 of the approval replacement:
- New `lib/deviceApproval.ts`: realtime listener for `devices` INSERT,
surfaces a pending list, persists approve/deny decisions in
`chatapp.approvedDeviceIds` / `chatapp.dismissedDeviceIds`. Filters the
initial fetch by created_at > own-device's created_at so a freshly
installed client doesn't try to "approve" pre-existing devices.
- New `components/DeviceApprovalBanner.tsx`: bottom-right Discord-style
banner per pending request with Genehmigen / Ablehnen actions; reuses
`wrapForOneDevice` from conversationKeySync to fan out conv-keys.
- AppShell mounts both the listener and the banner.
Plus dev userData isolation in main.ts: when running unpackaged, append
`-Dev` to the userData path so `pnpm dev` runs side-by-side with the
installed packaged build instead of colliding on the single-instance
lock. Window title also distinguished as "ChatApp (Dev)".
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>