| … | … | @@ -14,6 +14,31 @@ |
| 14 | 14 | <div class="boot-name">p2pbuilders</div> |
| 15 | 15 | <div class="boot-sub">connecting to peers…</div> |
| 16 | 16 | </div> |
| 17 | <script> | |
| 18 | // Boot watchdog (classic script: runs even when module loading is blocked). | |
| 19 | // Browsers refuse ES modules on file:// — the app never starts and the | |
| 20 | // splash would sit there forever. Explain instead of hanging. | |
| 21 | (function () { | |
| 22 | var explain = function (html) { | |
| 23 | var sub = document.querySelector('.boot .boot-sub') | |
| 24 | if (sub) sub.innerHTML = html | |
| 25 | } | |
| 26 | if (location.protocol === 'file:') { | |
| 27 | setTimeout(function () { | |
| 28 | if (!document.querySelector('.boot')) return | |
| 29 | explain('this app can't run from a file:// URL (browsers block module scripts).<br>' + | |
| 30 | 'serve it instead:<br><code>cd web && node dev-server.mjs</code><br>' + | |
| 31 | 'then open <code>http://localhost:8778</code> — or open the hyper:// link in PearBrowser.') | |
| 32 | }, 1500) | |
| 33 | } else { | |
| 34 | setTimeout(function () { | |
| 35 | if (!document.querySelector('.boot')) return | |
| 36 | explain('still starting… this usually means a script failed to load.<br>' + | |
| 37 | 'check the browser console (F12) for errors, then hard-refresh.') | |
| 38 | }, 10000) | |
| 39 | } | |
| 40 | })() | |
| 41 | </script> | |
| 17 | 42 | <script type="module" src="js/app.js"></script> |
| 18 | 43 | </body> |
| 19 | 44 | </html> |
| … | … | @@ -457,6 +457,15 @@ async function seedDemo () { |
| 457 | 457 | toast('demo ready'); location.hash = '#/'; route() |
| 458 | 458 | } |
| 459 | 459 | |
| 460 | // A boot failure must never strand the splash screen silently. | |
| 461 | function bootSafe () { | |
| 462 | boot().catch((err) => { | |
| 463 | console.error('boot failed:', err) | |
| 464 | const sub = document.querySelector('.boot-sub') | |
| 465 | if (sub) sub.innerHTML = `failed to start: ${esc(err && err.message ? err.message : String(err))}<br>see the browser console for details.` | |
| 466 | }) | |
| 467 | } | |
| 468 | ||
| 460 | 469 | if (typeof window !== 'undefined') window.__p2pb = { get data () { return data }, get sync () { return sync }, route } |
| 461 | if (typeof document !== 'undefined') { if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', boot); else boot() } | |
| 470 | if (typeof document !== 'undefined') { if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', bootSafe); else bootSafe() } | |
| 462 | 471 | export { boot } |
| … | … | @@ -35,8 +35,10 @@ async function detect () { |
| 35 | 35 | if (okk) { backend = 'subtle'; return } |
| 36 | 36 | } |
| 37 | 37 | } catch {} |
| 38 | // Fall back to node:crypto. | |
| 38 | // Fall back to node:crypto — never in a browser, where the import can't | |
| 39 | // succeed and only produces a scary CORS/404 console error. | |
| 39 | 40 | try { |
| 41 | if (typeof window !== 'undefined') { backend = 'none'; return } | |
| 40 | 42 | const mod = await import('node:crypto') |
| 41 | 43 | nodeCrypto = mod.default || mod |
| 42 | 44 | const { privateKey, publicKey } = nodeCrypto.generateKeyPairSync('ed25519') |