Files
dchain/desktop/src/main.tsx
vsecoder 3641cb113d fix(desktop): CSP via webRequest + boot error visibility
Two problems from the first alpha4 run reported as "blank window + CSP
warning in devtools":

1. CSP was set via <meta> in index.html with a strict policy (script-src
   'self'). Vite's dev server uses eval() for HMR, which the strict CSP
   blocked at module-load time, so the renderer never ran. The meta CSP
   also conflicted with Electron's own security heuristics (hence the
   warning even though *we* had a policy — Electron was looking for it
   on the HTTP response).

   Moved the CSP to electron/main.ts via session.webRequest
   .onHeadersReceived. Dev profile enables 'unsafe-eval' + ws:/wss: for
   HMR; production profile stays strict (no eval, no remote scripts,
   connect-src still wide because the user picks arbitrary node URLs).

2. When window.dchain isn't available (preload failed to load, dev
   misconfig, etc.), loadKeyFile() throws inside a useEffect. React
   swallows async-effect throws, so the app renders blank forever.

   Added:
     - requireDchain() guard in storage.ts with an explicit error.
     - App.tsx catches boot-effect errors and renders them inline.
     - ErrorBoundary.tsx for render-time throws.
     - window.addEventListener('error') in main.tsx as a last-resort
       paint for throws that escape React entirely.

Also: npm script electron:dev now rebuilds main.ts before spawning
Electron (was a silent concurrency bug — TypeScript errors in main.ts
would produce stale dist-electron/).
2026-04-22 17:13:26 +03:00

25 lines
905 B
TypeScript

import React from 'react';
import ReactDOM from 'react-dom/client';
import { App } from './App';
import { ErrorBoundary } from './ErrorBoundary';
// Last-resort fallback: if even rendering ErrorBoundary+App fails (say, a
// syntax error in some lazy import), paint a visible message into #root
// so the window isn't just black. window.onerror catches async errors
// that escape React's boundaries.
window.addEventListener('error', (e) => {
const root = document.getElementById('root');
if (root && !root.firstChild) {
root.innerHTML = `<pre style="color:#ff6b6b;background:#000;padding:20px;font-family:monospace;white-space:pre-wrap;">` +
`Fatal: ${String(e.error ?? e.message)}\n\n${e.error?.stack ?? ''}</pre>`;
}
});
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<ErrorBoundary>
<App />
</ErrorBoundary>
</React.StrictMode>,
);