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>
Red-dot overlay drawn as raw RGBA (no extra resource bundled).
Shown on Windows when unread count > 0, cleared when 0.
macOS keeps numeric Dock badge; Linux has no cross-DE badge API.
Bumps version 0.10.0 -> 0.10.1.
File variety (video/pdf/generic), user status with custom message,
auto online/offline, DND gate for ring + notifications, drag-drop +
paste upload, @mentions in groups, link previews, emoji picker,
crash recovery.
Messages:
- Reply-to: hover action, composer chip with cancel, quote bubble inside
the replying message with tap-to-jump + amber highlight ring
- Search: header search button toggles in-conversation search bar with
prev/next + match counter, auto-jump to active match
- Forward: multi-select conversation picker. Attachments are now carried
over: download + decrypt source, re-encrypt under each target conv-key,
re-upload with fresh per-attachment keys, insert new attachment rows
Conversations:
- Archive + mute per member. New migration 20260420000001 adds `archived`
+ `muted_until` on conversation_members. Shared helpers:
setConversationArchived / setConversationMutedUntil / isConversationMuted
- ChatsPage: archive toggle in header with unread badge for archived
bucket, split active/archived lists, muted indicator (BellOff icon,
dimmed unread badge)
- ConversationRowMenu via createPortal (escapes sidebar overflow clip),
forwardRef-based MenuItem so submenu positioning refs survive React 18
- ConversationsContext: suppresses notification sound + OS notif when
target conversation is muted
- Refresh on `profiles UPDATE` realtime so peer avatar / displayName
changes flow to conversation.members without manual refresh
Resilience:
- ErrorBoundary (Discord-style): centred spinner + escalating copy, no
manual reload button. Backoff retry schedule [2s, 4s, 8s, 15s, 30s].
Uses a keyed Fragment (not a div wrapper) so flex h-full chains survive
- App wrapped root + per-route RouteBoundary, conversation-level boundary
- AuthContext: flip `ready` immediately on cached session read; validate
getUser in background so a stalled/offline Supabase doesn't freeze the
app on the loading spinner
Crypto:
- Swap libsodium-wrappers -> libsodium-wrappers-sumo (compact build was
missing crypto_pwhash so Argon2id vault KDF threw, falling back to
plaintext localStorage on every launch)
- Shim d.ts for sumo types (sumo is API superset, no official types ship)
- vite optimizeDeps includes sumo with the "require" condition
- secureFileStore: exists(dir) check before mkdir; surface genuine
permission errors instead of silent catch
Tauri:
- fs scope adds `$APPLOCALDATA` direct entry (not just /**) so the app
data directory itself can be mkdir'd on first launch
Chat layout:
- Skip call_event messages when computing avatar run boundaries so a
regular bubble followed by a call event from the same sender still
shows its avatar