aboutsummaryrefslogtreecommitdiffstats
path: root/src/app.js
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-09-02 20:09:46 -0700
committerAdam Soltys <[email protected]>2026-09-02 20:09:46 -0700
commit1053b92accee1d52050067d75f36c74a5fa4ca87 (patch)
treef293e481eb3606af4c407dd860d9d04d83e99f2f /src/app.js
parent35891af2bcf4f0d2e73fecf6558513bc1ecaeb2e (diff)
downloadcoinosv3-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.js18
1 files changed, 15 insertions, 3 deletions
diff --git a/src/app.js b/src/app.js
index c2b4412..f752ebc 100644
--- a/src/app.js
+++ b/src/app.js
@@ -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);
}