From 481d4d2fa88ea2487f5b45c8d2b82143c9c07f9b Mon Sep 17 00:00:00 2001 From: vsecoder Date: Wed, 22 Apr 2026 18:53:37 +0300 Subject: [PATCH] fix(desktop): global box-sizing + per-pane error boundary + Conversation defensives MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- desktop/index.html | 12 ++++ .../src/sections/messages/Conversation.tsx | 19 ++++-- desktop/src/shell/PaneBoundary.tsx | 62 +++++++++++++++++++ desktop/src/shell/Shell.tsx | 9 ++- 4 files changed, 94 insertions(+), 8 deletions(-) create mode 100644 desktop/src/shell/PaneBoundary.tsx diff --git a/desktop/index.html b/desktop/index.html index 5309a15..bffc3a7 100644 --- a/desktop/index.html +++ b/desktop/index.html @@ -9,6 +9,13 @@ dev vs. production rules cleanly. --> DChain diff --git a/desktop/src/sections/messages/Conversation.tsx b/desktop/src/sections/messages/Conversation.tsx index a354191..bf1c8c5 100644 --- a/desktop/src/sections/messages/Conversation.tsx +++ b/desktop/src/sections/messages/Conversation.tsx @@ -98,12 +98,13 @@ export function Conversation({ address }: { address: string }): React.ReactEleme } }; - const name = contact?.username ? `@${contact.username}` + const name = (contact?.username ? `@${contact.username}` : contact?.alias ? contact.alias : isSelf ? 'Saved Messages' - : shortAddr(address, 8); + : shortAddr(address || '', 8)) || shortAddr(address || '', 8); + const firstLetter = (name || '?').replace(/^@/, '').charAt(0).toUpperCase() || '?'; return (
@@ -118,12 +119,18 @@ export function Conversation({ address }: { address: string }): React.ReactEleme color: '#fff', fontWeight: 700, fontSize: 14, display: 'flex', alignItems: 'center', justifyContent: 'center', }}> - {isSelf ? '★' : name.replace(/^@/, '').charAt(0).toUpperCase()} + {isSelf ? '★' : firstLetter}
-
{name}
-
- {shortAddr(address, 6)} +
{name}
+
+ {shortAddr(address || '', 6)}
diff --git a/desktop/src/shell/PaneBoundary.tsx b/desktop/src/shell/PaneBoundary.tsx new file mode 100644 index 0000000..a83c237 --- /dev/null +++ b/desktop/src/shell/PaneBoundary.tsx @@ -0,0 +1,62 @@ +// PaneBoundary — ErrorBoundary scoped to one Shell pane. A crash in +// the Conversation component shouldn't black-out the whole window; it +// should leave NavBar + List + StatusBar usable so the operator can +// switch sections and report the bug. Resets when the keyed section +// changes. + +import React from 'react'; + +interface Props { + /** Used as React key at the callsite; also shown in the panic copy. */ + sectionName: string; + children: React.ReactNode; +} + +interface State { + error: Error | null; +} + +export class PaneBoundary extends React.Component { + state: State = { error: null }; + + static getDerivedStateFromError(error: Error): State { + return { error }; + } + + componentDidCatch(error: Error, info: React.ErrorInfo): void { + console.error(`[PaneBoundary:${this.props.sectionName}]`, error, info); + } + + render(): React.ReactNode { + if (!this.state.error) return this.props.children; + return ( +
+
+ {this.props.sectionName} crashed +
+
+ {this.state.error.message} +
+
+          {this.state.error.stack}
+        
+ +
+ ); + } +} diff --git a/desktop/src/shell/Shell.tsx b/desktop/src/shell/Shell.tsx index 4d22e0f..29a161e 100644 --- a/desktop/src/shell/Shell.tsx +++ b/desktop/src/shell/Shell.tsx @@ -24,6 +24,7 @@ import { TitleBar } from './TitleBar'; import { NavBar } from './NavBar'; import { StatusBar } from './StatusBar'; import { UpdateBanner } from './UpdateBanner'; +import { PaneBoundary } from './PaneBoundary'; import { MessagesList, MessagesDetail } from '@/sections/messages'; import { FeedList, FeedDetail } from '@/sections/feed'; import { WalletList, WalletDetail } from '@/sections/wallet'; @@ -51,10 +52,14 @@ export function Shell(): React.ReactElement { borderRight: '1px solid #1f1f1f', overflowY: 'auto', }}> - + + +
- + + +