Create LICENSE.md#9
Merged
frstrtr merged 1 commit intoorigin/sharechain/async_threadfrom Aug 20, 2021
Merged
Conversation
frstrtr
added a commit
that referenced
this pull request
Apr 20, 2026
Introduces a parallel test surface — dashboard-bundled.html — that wires the extracted Realtime orchestrator against a live c2pool server via HttpTransport. Operators run it alongside the untouched production dashboard.html in a second tab to visually A/B the bundled pipeline against the inline renderer. Production dashboard.html stays unchanged until parity is verified — spec §11 / plugin-arch §16 migration discipline. Deliberately *not* modifying dashboard.html this commit. Swapping the inline renderer with a <script type="module"> bundle import is a later commit gated by a query-string feature flag for safe rollout. dashboard-bundled.html (~75 LOC) - Minimal CSP-compliant shell (same policy as the demo page). - Header inputs: base URL (defaults http://127.0.0.1:8080) and "my address" (optional, for mine-variant colouring). - Connect / disconnect / refresh buttons. - Live status pill + stats line (share count, tip-short, animation state, queue state, fetch state). - Rolling 8 KB log pane for structured ExplorerError / info events. - Canvas filling the grid wrap; RAF-driven paint via createRealtime. dashboard-bundled.mjs (~100 LOC) - Imports createHttpTransport + createRealtime from dist/. - URL params: ?base= / ?addr= / ?autoconnect=0 / ?fast=1. - Persists {base, myAddress} in localStorage under 'c2p-bundled-prefs' so reconnect-on-reload is typing-free. - onError handler maps ExplorerError.{type,status,message} into the log pane without leaking internal context. - 250 ms stats-loop updates the header stats from RealtimeController.getState() — gives operators live visibility into the state machine. - Exposes window.__explorer = RealtimeController for devtools poking (getState, refresh, etc.). - beforeunload hooks stop() so SSE subscriptions + RAF are cleanly torn down when the tab closes. README.md (~95 LOC) First top-level README for the module. Documents: - Directory layout + npm scripts - Three surfaces: demo.html (synthetic) / dashboard-bundled.html (live A/B) / dashboard.html (untouched production). - Pointer to the eight-doc design baseline in frstrtr/the. - Bundle-size numbers + current Phase A/B status. - Instructions for live A/B testing (serve with python -m http.server or c2pool's HTTP server, open both pages in parallel tabs). Status - No source/test changes — verify remains 166/166 green. - Bundles unchanged: shared-core 25.1 KB / 40, sharechain-explorer 42.0 KB / 120. - dashboard-bundled.html now loads the bundle via <script type="module" src="./dashboard-bundled.mjs"> and Just Works against any c2pool instance exposing the spec §5 endpoints. Next: either Phase B #9 — wire the bundle into dashboard.html behind a ?new-explorer=1 feature flag (final M2 step; removes the inline defrag once pixel-diff confirms parity), or Phase B #10 particles + card overlays, or Qt refactor step 1 (CMake deps).
frstrtr
added a commit
that referenced
this pull request
Apr 20, 2026
… flag
Wires the extracted @c2pool/sharechain-explorer bundle into the
production dashboard, OFF by default. Operators opt in per-tab with
?new-explorer=1 (or persistently via localStorage.c2p_new_explorer=1).
Zero byte / behaviour change for default users — the bundle script
is dynamically imported only inside the flag-on branch, so the CSP
and network footprint of the inline path are unchanged.
Closes M2 mechanical extraction per the Explorer spec §11 / plugin-
arch doc §16.2 migration flag. Pixel-diff CI against an
`explorer-baseline-v0` tag becomes meaningful after this commit
lands: operators can toggle the flag inside the same browser
(identical DPR / fonts / CSS) and compare the two paths rigorously.
Two surgical edits to dashboard.html (75 insertions, no deletions):
1) Flag check + defrag disarm (just after the inline `defrag`
object definition, before defrag.init()):
- Resolves the flag from URLSearchParams then localStorage with
a narrow try/catch per source so older browsers without
URLSearchParams gracefully fall through.
- When ON: walks defrag's own keys, replaces every function-
valued entry with a no-op. This disarms init(), render(),
load(), _animate3D(), toggleRealTime(), showZoom(), and any
other method the RealTime toggle / auto-refresh timer /
inline handlers might call — so the bundled renderer owns
the canvas unambiguously.
- Logs a clear `console.info` so devtools shows which path
is active.
2) Bundled bootstrap script (just before </body>):
- `<script type="module">` with `import('./sharechain-explorer/
dist/sharechain-explorer.js')`. Dynamic import so the bundle
is fetched only when the flag is set.
- createHttpTransport({ baseUrl: '..' }) matches dashboard.html's
existing relative-path convention (d3.json('../current_merged_
payouts', ...) at :2517 / :4064).
- createRealtime wires canvas + containerWidth against the
existing #defrag-canvas + its wrap. userContext pulls my-
address from window.currency_info where available.
- onError callback routes structured ExplorerError to console.
- Hides #defrag-loading when mount succeeds.
- Exposes window.__explorerBundled = RealtimeController for
devtools poking (.getState(), .refresh(), etc.).
Known limitations in flag-on mode (documented in the inline comment
near the flag block and in README.md):
- Right-hand stats panel (#defrag-total, #defrag-chain-len, etc.)
stays at "-". Bundled Realtime does not yet update these
elements — Phase B #10 wires stat emission.
- RealTime toggle button does nothing (bundled is always live).
The inline toggle calls a disarmed no-op.
- Hover-zoom + per-share PPLNS treemap not yet ported.
None of these affect the default path — operators without the flag
see the existing dashboard verbatim.
Safety posture:
- dashboard.html on branch explorer-module, not master. No
production pool is affected until the branch is merged.
- Flag is default OFF; even after merge, zero behaviour change
unless an operator deliberately opts in.
- Can be reverted to the pre-flag state with one `git revert` if
the bundled path exhibits regressions in the wild.
Status
- 166/166 tests pass in 2.2s — no test-surface changes.
- Bundles unchanged: shared-core 25.1 KB / 40 KB, sharechain-
explorer 42.0 KB / 120 KB.
- curl smoke test confirms dashboard.html still serves (200 OK,
411 KB) and the bundle is reachable from the relative path.
Next: Phase B #10 particles + card overlays to close cosmetic
parity, or wire stat-panel emission into Realtime so flag-on mode
matches the inline stats output, or Qt refactor step 1 (CMake
deps + option flags).
frstrtr
added a commit
that referenced
this pull request
Apr 20, 2026
Closes the most visible limitation of ?new-explorer=1 (Phase B #9): operators enabling the flag no longer see dashes across the stats panel. Parity with the inline renderer's stat counters achieved except for minor #defrag-blocks formatting (coin-name still hardcoded "LTC / DOGE"; generic merged-chain labelling arrives with CoinDescriptor consumption per M1 D5). src/explorer/delta.ts - WindowSnapshot<S> gains three optional metadata fields: chainLength, primaryBlocks, dogeBlocks All typed explicitly as `T | undefined` so exactOptionalPropertyTypes stays satisfied. src/explorer/realtime.ts - New type RealtimeStats: shares, chainLength, verified, mine, stale, dead, fee, v36native, v36signaling, primaryBlocks, dogeBlocks. - RealtimeState.stats surfaced through getState(), lazy-computed by currentStats() and cached per lastAppliedTip so repeated polls on the same tip are O(1). Cache invalidated on rebuild + on applyDelta. - extractMeta() helper pulls chain_length + blocks + doge_blocks from raw /sharechain/window and /sharechain/delta payloads (spec §5.1 + §5.3). Only-defined-fields return keeps exactOptionalPropertyTypes happy across the spread patterns. - rebuildWindow + applyDelta populate the new meta fields from the raw response; applyDelta preserves previous meta fields across deltas that don't carry them. - Classification thresholds use ctx.shareVersion so a Dash V16 host reports v36native / v36signaling counts against 16 — the field names are legacy from dashboard.html; semantics are coin-agnostic. src/explorer/index.ts - Re-exports RealtimeStats. tests/unit/realtime-stats.test.ts (10 tests) - Empty window → all-zero stats. - Priority order: dead(2) / stale(1) / unverified / fee / verified. - Mine counting against myAddress; empty myAddress always 0. - v36native vs v36signaling branches including boundary V===36 + V===37 native counting. - Dash V16 threshold demonstrates coin-agnostic behaviour. - chain_length + blocks + doge_blocks passthrough from payload. - Cache identity: same tip → same object reference (===). - Cache invalidation: delta merge produces distinct stats object. web-static/dashboard.html (flag-on bootstrap) - Builds an element lookup once (#defrag-total, -chain-len, -verified, -mine, -stale, -dead, -fee, -v36native, -v36sig, -blocks). - 250 ms setInterval calls writeStats() which short-circuits when lastAppliedTip hasn't changed — matches the orchestrator's per-tip caching so DOM writes are minimal. - #defrag-blocks formatted as "N LTC / M DOGE" — identical to the inline path at :5909. Coin-aware label comes with CoinBridge. - beforeunload clears the interval. web-static/sharechain-explorer/dashboard-bundled.mjs - Header stats line now surfaces chain length, v36/v36sig, mine, stale/dead, primary/doge blocks counts alongside the existing tip + connection state. Dropped fields that are zero to keep the line compact. Status - 176/176 tests pass (166 prior + 10 new). - Typecheck clean. - Bundles: shared-core 25.1 KB / 40, sharechain-explorer 43.5 KB / 120 — both under budget. (+1.5 KB from ~2 KB of new stats code.) - dashboard.html default path (flag off) touches zero new code at runtime — the stat polling is gated inside the flag-on bootstrap. Next: Phase B #11 particles + card overlays (largest remaining visual piece), or Phase B #12 hover-zoom + per-share PPLNS treemap (dashboard.html:5686-5800 port), or Qt refactor step 1.
frstrtr
added a commit
that referenced
this pull request
Apr 20, 2026
Closes the second-most-visible flag-on gap identified in Phase B #9: hovering a share cell now pops the 240x240 squarified PPLNS panel that the inline renderer showed via dashboard.html:5686-5782. Both dashboard.html (?new-explorer=1) and dashboard-bundled.html get the feature in this commit. Three new primitives, all verbatim-ported from dashboard.html so M2 pixel-diff holds. src/explorer/pplns.ts (~45 LOC) - interface PPLNSEntry = { addr, amt, pct } - parsePPLNS(raw): handles both flat {addr: amt} and merged {addr: {amount, merged:[]}} payload shapes, filters zero/ negative/NaN amounts, sorts desc by amt, normalises pct to the remaining total. Byte-identical behaviour to dashboard.html's _parsePPLNS() at :5631-5643. - PPLNSPlugin: id 'explorer.pplns.parser', provides 'pplns.parser'. src/explorer/hover-zoom.ts (~210 LOC) - buildHoverZoomProgram(opts): PaintCommand[] — pure builder. Uses SharedCore's squarify + addrHue. Cell colours preserved verbatim (dashboard.html:5719-5725): hovered miner → hsl(hue, 85%, 55%) brightest my address → hsl(hue, 60%, 42%) mid everyone else → hsl(hue, 35%, 30%) muted White highlight ring on hovered miner (line 2.5). Label emission thresholds preserved: % when cell > 30x16, address when > 44x28 with font-size scaling exactly matching dashboard.html:5746-5761. - createHoverZoomPanel(opts): HoverZoomPanel — thin DOM adapter. position: fixed 240x240 canvas + label row + border/padding. show() clamps to viewport edges (flip left past a configurable tooltip-width offset, matches dashboard.html:5775-5781). destroy() removes from parent. Does not capture pointer events (pointer-events: none) so hover-through still works. - HoverZoomPlugin: id 'explorer.hover-zoom.canvas', provides 'renderer.hover-zoom', fills slot 'explorer.main.overlay'. src/explorer/delta.ts + realtime.ts - WindowSnapshot<S> gains: pplnsCurrent?: readonly PPLNSEntry[] pplnsByShare?: ReadonlyMap<string, readonly PPLNSEntry[]> - RealtimeOrchestrator.extractMeta() parses pplns_current + pplns from /sharechain/window and /sharechain/delta payloads (spec §5.1, §5.3). Every parse uses parsePPLNS() so the same shape handling applies everywhere. - applyDelta merges pplnsByShare additively across deltas; delta entries win over prior (mirrors dashboard.html:8079-8080). Growth bounded at 2 x windowSize per architecture doc §2 ("up to 10000 entries") / delta v1 §C.2 LRU. - New method: orchestrator.getPPLNSForShare(shortHash). Implements the three-step fallback from dashboard.html:5647-5663: exact cache hit → walk backward (toward newer) to nearest cached → pplns_current. - RealtimeController surfaces getPPLNSForShare passthrough for DOM consumers. web-static/dashboard.html (flag-on path) - After rt.start(), startHoverZoom() creates a HoverZoomPanel and attaches mousemove + mouseleave to #defrag-canvas. - mousemove: computes layout from current containerWidth, hit-tests via cellAtPoint, looks up share via getState().window.shares[idx], fetches PPLNS via controller.getPPLNSForShare(share.h), calls panel.show() with cursor coords. Cell-index memoised to avoid re-rendering when hover stays inside the same cell. - mouseleave: hides panel. web-static/sharechain-explorer/dashboard-bundled.mjs - Parallel wiring so the A/B surface also shows hover-zoom. - disconnect() / connect() manage hoverPanel lifecycle. - onMouseMove reuses controller.getPPLNSForShare. myAddress is read from the header input so A/B-testers can exercise the is-me colour. tests/unit/pplns-hoverzoom.test.ts (15 tests) - parsePPLNS: empty / flat / merged / zero-filter / missing-amount - getPPLNSForShare: exact / walk-backward / fallback to pplns_current / additive-across-deltas - buildHoverZoomProgram: empty → bg only; correct fill count; hovered miner gets 2 strokes including white highlight; large cell gets % + address labels; tiny cells skip labels. Status - 191/191 tests pass (176 prior + 15 new). - Typecheck clean. - Bundles: shared-core 25.1 KB / 40, sharechain-explorer 48.3 KB / 120 — both under budget. (+4.8 KB from the new modules.) - dashboard.html default path (flag off) unchanged: bundle isn't loaded, no new DOM, no new event listeners. Flag-on parity with inline now covers: canvas rendering + all stat panel counters + hover-zoom PPLNS treemap. Remaining cosmetic gaps from Phase B #9: RealTime toggle button (inert; bundled is always live) and particles + dissolve/birth card overlays. Next: Phase B #11 particles + cards (final cosmetic parity), or Phase B #13 RealTime-toggle plumbing (small, wires the button to rt.start/rt.stop), or Qt refactor step 1 (CMake deps).
frstrtr
added a commit
that referenced
this pull request
Apr 20, 2026
Closes the "RealTime toggle button inert" limitation documented in Phase B #9's commit message. Clicking the existing #realtime-btn in ?new-explorer=1 mode now stops + restarts the bundled orchestrator instead of a no-op. src/explorer/realtime.ts - RealtimeOrchestrator.stop() now clears _started so a subsequent start() works. Previously stop was one-way: _stopped=true blocked restart with an error. The toggle workflow (OFF → ON → OFF → ON over one session) needs restartability. - start() picks up the pattern: it now resets _stopped=false so the full init sequence (rebuildWindow + subscribeStream) runs again. - No caller-visible semantic change when .stop() is terminal (which it was in every pre-B#13 call site). Existing 12 realtime.test.ts tests still pass verbatim. - New test: 'restart: start/stop/start cycle works' exercises the second start() — window re-fetched, subscription re-registered, subsequent tips processed. web-static/dashboard.html (flag-on path) - Overrides window.toggleRealTime() — the handler the existing `<a href="javascript:toggleRealTime()" id="realtime-btn">` at line 2139 invokes. In bundled mode the inline implementation (lines 7939-7997: manages its own EventSource + polling + #realtime-btn styling) never runs because defrag methods are disarmed; our override takes its place. - Two helpers (setRtButtonOn / setRtButtonOff) replicate the inline path's button styling exactly: ON → "RealTime: SSE", bg #28a745, white text OFF → "RealTime: OFF", bg var(--card-bg), text-muted - Initial mount: bundled Realtime auto-starts, so the button is painted green before any user interaction (consistent with how the inline path looks after its first click). - Click flow: reads rt.getState().started and calls rt.stop() or rt.start() accordingly. Thenable chain updates the button only after the orchestrator transitions, so a fast double-click doesn't desync button text from underlying state. Status - 192/192 tests pass (191 prior + 1 new restart test) - Typecheck clean - Bundles unchanged at 48.3 KB (behaviour-only change) - Flag-off (default) path untouched — inline toggleRealTime() still owns the button as before. Gap list at end of Phase B #13: - Particles + cards (dying-share ash dissolution, birth coalescence, hold-frame miner/PPLNS-% cards during phases 1 and 3). Purely cosmetic; not on the critical path to functional parity. After this commit flag-on mode is functionally complete modulo those animations: canvas render + all stat panel counters + hover- zoom PPLNS treemap + RealTime toggle control. Pixel-diff against the inline path can now be run in A/B tabs with a reasonable expectation of exact-matching frame content (animations aside).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.