Two bugs reported after v2.2.0:
1. Input fields and textareas overflowed their container — typing in
Settings / SendModal / NewContactModal would push the border past
the card edge because the renderer's default box-sizing was
content-box and `width: 100%` + padding pushed widths past parents.
Added `*, *::before, *::after { box-sizing: border-box; }` to
index.html. Removes the need for per-element `boxSizing: 'border-box'`
(the existing sprinkles stay for clarity but are now redundant).
2. App went blank when opening a chat — any throw inside Conversation
propagated up through Shell and wiped the whole window, with no way
to navigate out. Added PaneBoundary, a React error boundary scoped
to one Shell pane, keyed on `${section}-(list|detail)` so it resets
when the user switches section. Now a crash shows an inline error
card with message + stack + Retry, while NavBar + StatusBar stay
usable.
Also hardened Conversation against edge cases that were candidates
for the original crash:
* `name` always falls back to shortAddr(address) if all other
branches produce an empty string.
* first letter used for the avatar is computed once, guarded
against empty input with a `?` fallback.
* Header name + short-address line get whiteSpace/overflow/ellipsis
so very long contacts no longer escape the 32px wide sub-column
the way they did for the reporter.
Fonts normalised in the global CSS too — inputs/textareas/buttons now
inherit `font-family` instead of the browser default, which was
breaking the visual rhythm in the Settings cards.
DChain Desktop
Electron shell for the DChain messenger and social feed.
Same functionality as the mobile client-app, re-imagined with a keyboard-first, 3-panel desktop layout:
┌──────────────────────────────────────────────────────────┐
│ DChain │ titlebar (drag)
├──────┬───────────────────┬────────────────────────────────┤
│ nav │ list │ detail │
│ 72px │ 340px fixed │ flex 1 │
├──────┴───────────────────┴────────────────────────────────┤
│ ● online · node.example:8080 · height 10942 │ status bar
└──────────────────────────────────────────────────────────┘
Sections (left rail): Messages · Feed · Wallet · Contacts · Settings · Profile.
Quick start
cd desktop
npm install
npm run dev # concurrently: Vite dev server + Electron
The first boot will show the Welcome screen. Pick Create to generate
fresh keys, or Import a node.json exported from the mobile client.
Build
npm run build # produces dist/ (renderer) + dist-electron/ (main) + installers
Default installers are built with electron-builder: .dmg on macOS,
NSIS .exe on Windows, AppImage + .deb on Linux. Adjust build.* in
package.json for signing / notarisation.
Layout
electron/— main + preload. TypeScript, compiled todist-electron/bytsc -p electron/tsconfig.json.src/— renderer. React + Vite.@/aliases tosrc/.src/shell/— 3-panel chrome.src/sections/— one folder per nav section, each exports{ List, Detail }.src/auth/Welcome.tsx— shown when no key is loaded.src/lib/— api, storage, store, types. Mirrors (without React-Native deps) the relevant pieces of../client-app/lib/.
Security model
Master Ed25519 priv lives in the OS keychain via Electron safeStorage
(macOS Keychain / Windows DPAPI / libsecret). A renderer compromise
cannot read or exfiltrate the key — it always travels through
window.dchain.keyfile.* IPC, which main.ts validates and mediates.
contextIsolation: true, nodeIntegration: false. CSP in index.html
pins script sources to 'self' while allowing connect-src * so the
renderer can hit any node the user configures.
Pairing (v2.2.0-alpha5+)
Desktop will reuse the same 6-digit-code + relay-envelope handshake as
the mobile client. The scaffold in src/auth/Welcome.tsx stubs the
button until the polling loop lands.
Multi-device fan-out
When the node is at v2.2.0-alpha1+, lib/api.ts:fetchDevices returns
every linked X25519 pub for a given identity; the sender then encrypts
one envelope per device. Legacy nodes return an empty array and the
client falls back to IdentityInfo.x25519_pub, preserving the
pre-multi-device behaviour.