Files
dchain/desktop
vsecoder 481d4d2fa8 fix(desktop): global box-sizing + per-pane error boundary + Conversation defensives
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.
2026-04-22 18:53:37 +03:00
..

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 to dist-electron/ by tsc -p electron/tsconfig.json.
  • src/ — renderer. React + Vite. @/ aliases to src/.
  • 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.