diff options
| author | Adam Soltys <[email protected]> | 2026-09-02 20:09:46 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-09-02 20:09:46 -0700 |
| commit | 1053b92accee1d52050067d75f36c74a5fa4ca87 (patch) | |
| tree | f293e481eb3606af4c407dd860d9d04d83e99f2f /src/app.js | |
| parent | 35891af2bcf4f0d2e73fecf6558513bc1ecaeb2e (diff) | |
| download | coinosv3-1053b92accee1d52050067d75f36c74a5fa4ca87.tar.gz coinosv3-1053b92accee1d52050067d75f36c74a5fa4ca87.zip | |
The single-file wallet returns as a first-class artifact: every build now emits standalone.html (~950KB, everything inlined, boots from a bare file:// with no server), How it works grows a 'Keep a copy' card linking the download, and the tab strip's phantom double-tap dies — the pointerdown's paired click was arriving after the render had already swapped in fresh handlers, and with toggling tabs that ghost click un-did the tap it belonged to. The timestamp guard now lives on the DOM node, which survives the morph
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
Diffstat (limited to 'src/app.js')
| -rw-r--r-- | src/app.js | 18 |
1 files changed, 15 insertions, 3 deletions
@@ -783,6 +783,15 @@ function howItWorksScreen() { h('div', { class: 'card col', style: 'gap:14px' }, h('h3', {}, '🎁 ' + t('hiwGiftsTitle')), para('hiwGifts1')), + // The standalone file: the whole app inlined into one HTML — the + // save-and-open-offline story the split build gave up. Only offered when + // there's a server to download it from (on file:// you already have it). + location.protocol.startsWith('http') + ? h('div', { class: 'card col', style: 'gap:14px' }, + h('h3', {}, '💾 ' + t('hiwStandaloneTitle')), + para('hiwStandalone1'), + h('a', { href: '/standalone.html', download: 'coinos.html' }, t('hiwStandaloneLink'))) + : null, h('button', { class: 'btn-block', onClick: back }, t('back')) ); } @@ -821,7 +830,10 @@ function tabBtn(label, active, onClick) { // state waited for the whole pane to build. Flip the strip's classes with // a direct DOM write (paints this frame), then run the real render after // that paint — double-rAF is the "after the next paint" idiom. - let downAt = 0; + // The down-timestamp lives ON THE NODE, not in this closure: the + // pointerdown's own render replaces these handlers (fresh closure, zero + // timestamp) before the browser delivers the paired click, and with tabs + // that TOGGLE, letting that click through un-did the tap it belonged to. const select = (btn) => { if (btn && btn.parentElement) { for (const b of btn.parentElement.children) b.classList.toggle('active', b === btn); @@ -832,10 +844,10 @@ function tabBtn(label, active, onClick) { class: active ? 'active' : '', onPointerdown: (e) => { if (e.button !== 0) return; - downAt = performance.now(); + e.currentTarget._downAt = performance.now(); select(e.currentTarget); }, - onClick: (e) => { if (performance.now() - downAt > 500) select(e.currentTarget); }, + onClick: (e) => { if (performance.now() - (e.currentTarget._downAt || 0) > 500) select(e.currentTarget); }, }, label); } |
