Skip to content

CMake action for github#7

Merged
5133n merged 2 commits intofrstrtr:origin/sharechain/async_threadfrom
Kaieida:origin/sharechain/async_thread
Apr 28, 2021
Merged

CMake action for github#7
5133n merged 2 commits intofrstrtr:origin/sharechain/async_threadfrom
Kaieida:origin/sharechain/async_thread

Conversation

@Kaieida
Copy link
Copy Markdown
Contributor

@Kaieida Kaieida commented Apr 26, 2021

No description provided.

@5133n 5133n merged commit b88c8a8 into frstrtr:origin/sharechain/async_thread Apr 28, 2021
frstrtr added a commit that referenced this pull request Apr 20, 2026
Three-phase animation state machine (spec §6, dashboard.html:4866-
5400). This commit ships the core — timing math, stagger schedules,
position interpolation for dying/wave/born tracks, and the
controller (start/tick/queueNext/reset). Scale, colour-lerp,
particle dissolution and card overlays land in subsequent commits;
each is a clearly isolable addition that the pure-function structure
accommodates without redesign.

Phase timing (verbatim from dashboard.html:4977-4982):

  phase1Dur  = 3000         DYING
  phase2Dur  = fast ? 2000 : 4000    WAVE  (spec §6 text has typo,
                                             code is authoritative)
  phase2Start = phase1Dur
  phase3Start = phase2Start + phase2Dur * 0.7   (overlap)
  phase3Dur  = 3000         BORN
  duration   = phase3Start + phase3Dur

Stagger schedules (all preserved verbatim from dashboard.html):

  DYING  — last dying share first, 150 ms per share. Newer end of
           the window dies visibly before older shares (mirrors the
           natural tail-eviction order).
  WAVE   — tail-first per dashboard.html:5146-5151:
               distFromTail = (N-1) - newIndex
               fraction     = distFromTail / (N-1)
               shareStart   = fraction * phase2Dur * 0.7
               shareT       = clamp01((p2elapsed - shareStart) / 600)
               ease         = 1 - (1-shareT)^3   (easeOut-cubic)
           Tail starts first, head last; each share animates over a
           600 ms window regardless of phase2Dur.
  BORN   — newest share first, 150 ms per share, full phase3Dur
           window per share.

Input / output:

  AnimationInput {
    oldShares, newShares, addedHashes, evictedHashes,
    oldLayout, newLayout, userContext, palette,
    hashOf, fast?
  }
  AnimationPlan {
    tEnd, phase1Start/Dur, phase2Start/Dur, phase3Start/Dur,
    frameAt(t): FrameSpec
  }
  FrameSpec {
    cells: CellFrame[],     // per-share {x,y,size,color,alpha,track}
    backgroundColor,
    layout                  // post-merge layout
  }

Controller:

  createAnimationController() → {
    isRunning(), start(plan, now), tick(now), queueNext(plan), reset()
  }
  - tick() returns the frame to paint, or null when idle.
  - queueNext() during running: queued plan starts automatically on
    next tick after current tEnd (§6 threshold rule #2, _animDeferred).
  - queueNext() while idle: queued plan starts on the next tick.
  - reset() drops current + queued.

Also exposed:

  SKIP_ANIMATION_NEW_COUNT_THRESHOLD = 100     (§6 rule #1, callers
                                                skip build/start when
                                                newCount >= 100)
  Helpers: clamp01, lerp, easeInOut, computePhaseTiming(fast?)
  Plugin: explorer.animator.three-phase  (provides 'animator.grid')

Tests (19): phase-timing constants for slow + fast; clamp/lerp/ease
sanity; empty input edge; fast vs slow tEnd; wave position
interpolation at t=0 and t=tEnd; wave stagger (tail moves before
head at early phase2 tick); dying staggered alpha decay; dying at
phase1 end alpha ~= 0; born spawns below grid and lands at (0,0);
born alpha fade-in over first 30%; controller idle tick; controller
start+tick+finish becomes idle; controller queueNext during running;
controller queueNext on idle starts next tick; controller reset
drops both.

Status

- 132/132 tests pass in 2.2s (113 prior + 19 new)
- Typecheck clean
- Bundles: shared-core 25.1 KB / 40, sharechain-explorer 33.9 KB /
  120 — both under budget
- Pipeline green end-to-end via npm run verify

Next commits layered onto this core:

  Phase B #5  scale effects (lift-slide-land wave scale, dying-rise
              scale, born shrink from bornScale→1x). Requires no new
              types — just richer CellFrame.size interpolation and
              paint-program extension for centred-scale fillRect.

  Phase B #6  colour lerp — dying lerps toward palette.dead across
              its stagger window; born coalesces from unverified to
              coin colour.

  Phase B #7  particle effects (ash dissolution, birth coalescence)
              and card overlays (miner addr + PPLNS % text during
              hold frames). Largest increment — may split again.

  Phase B #8  wire mergeDelta + animator + gridRenderer into a
              RealTime plugin (SSE subscription + delta application
              + animation trigger). Unlocks the demo against a live
              c2pool server.
frstrtr added a commit that referenced this pull request Apr 20, 2026
Adds scale effects, colour lerp, and alpha handling to the animator
so dying shares fade + lerp to red, born shares coalesce from the
unverified grey to the final coin colour, and wave shares lift-
slide-land with a 1.35x pop — all verbatim from dashboard.html's
paint body (lines 5155-5172). Alpha rides as the 4th channel of an
rgba(...) colour string so the existing PaintCommand set doesn't
need a global-alpha extension.

src/explorer/color-utils.ts (~95 LOC)

- parseHexColor(hex)           #rrggbb / #rgb / #rrggbbaa → {r,g,b}.
                               Alpha in 8-digit hex is ignored; use
                               applyAlpha() to set it explicitly.
- lerpColor(from, to, t)       sRGB-space midpoint, t clamped to
                               [0,1], `rgb(r,g,b)` output. Falls
                               back to `from` on parse failure.
- applyAlpha(color, a)         Accepts hex / rgb() / rgba(), returns
                               `rgba(r,g,b,a)` with a clamped to
                               [0,1]. Idempotent (rgba→rgba swaps
                               just the alpha channel).
- lerpColorWithAlpha           Composition convenience.
- ColorUtilsPlugin             id 'explorer.color-utils', provides
                               capability 'color-utils', registered
                               via registerExplorerBaseline.

src/explorer/animator.ts (in-place enrichment)

- WAVE cells                   Lift-slide-land scale per
                               dashboard.html:5155-5167 verbatim:
                                 ease < 0.2  → liftA = ease/0.2
                                 0.2-0.8     → lift held, slide begins
                                 ≥ 0.8       → slide done, landA ramp
                                 pop = liftA * (1 - landA)
                                 scale = 1 + 0.35 * pop
                               Cell drawn centred on sliding centre
                               (x = centre - size/2).
- DYING cells                  colour lerps toward palette.dead over
                               first 60% of stagger; alpha decays
                               linearly 1→0; rise scale 1→1.10 over
                               first 30%. Cell centred.
- BORN cells                   colour coalesces from palette.unverified
                               to share's final getColor() over first
                               70% of stagger; alpha fades in over
                               first 30%; scale shrinks from
                               BORN_INITIAL_SCALE (3x, pending text-
                               width-derived bornScale with cards in
                               Phase B #7) to 1x over last 20%.
                               Cell centred.
- New tunables                 WAVE_PEAK_SCALE = 1.35 (matches 0.35
                               bump in dashboard:5164),
                               DYING_RISE_SCALE = 1.10,
                               BORN_INITIAL_SCALE = 3,
                               WAVE_PER_SHARE_MS = 600 (per-share
                               slice dur, already present).

src/explorer/grid-paint.ts

- buildAnimatedPaintProgram(frame, dpr)
                               New function: consumes an animator
                               FrameSpec, emits setTransform + bg +
                               per-cell fillCell using the CellFrame's
                               own `size` (so scale effects carry
                               through). Cells with alpha ≤ 0 are
                               skipped entirely.

Tests added

- color-utils.test.ts (14)     parseHexColor hex6/hex3/hex8/rejects;
                               lerpColor endpoints/midpoint/clamp/
                               fallback; applyAlpha hex/rgb/rgba
                               paths + clamping; lerpColorWithAlpha
                               composition.
- animator-visuals.test.ts (8) wave scale hits 1.35 peak and returns
                               to 1 across its slice; scale never
                               exceeds peak; dying colour alpha
                               fades; dying rise scale in bounds;
                               born colour coalesces + scale shrinks
                               from 3x to 1x; buildAnimatedPaintProgram
                               skips alpha-0 cells, threads dpr.
- animator.test.ts (1 update)  wave stagger test now compares cell
                               centres (x + size/2) instead of top-
                               lefts, since scaling shifts the top-
                               left inward.

Status

- 154/154 tests pass in 2.2 s (132 prior + 22 new)
- Typecheck clean under strict + exactOptionalPropertyTypes
- Bundles: shared-core 25.1 KB / 40, sharechain-explorer 36.0 KB /
  120 — both under budget
- Pipeline green via npm run verify

Next: Phase B #7 particles + card overlays (largest remaining
piece), or Phase B #8 realtime plugin (SSE + delta + animator
wired), or Qt refactor step 1 (CMake deps + option flags).
frstrtr added a commit that referenced this pull request Apr 20, 2026
Wires Transport subscribeStream + fetchDelta + mergeDelta + Animator
+ renderer into a single live-updates runtime. Operational parity
with dashboard.html's RealTime-mode pipeline.

src/explorer/realtime.ts (~370 LOC)

- RealtimeOrchestrator — pure state machine, no DOM, no RAF.
    constructor(RealtimeConfig)
    start()               fetches window, subscribes to stream
    stop()                unsubscribe + AbortController.abort()
    refresh()             forces full window rebuild
    getState()            { window, animating, hasQueued, started,
                             shareCount, lastAppliedTip, deltaInFlight }
    currentFrame(now)     FrameSpec | null (idle returns static frame)
    buildStaticFrame()    no-animation snapshot for initial paint +
                           post-animation idle frames + resize events

  Contract (spec §5 + §6):
    1. start() → fetchWindow → subscribeStream.
    2. onTip({hash}) → dedup against lastAppliedTip + pendingTip.
    3. Only one delta request in flight at a time; if a newer tip
       arrives during a fetch, drain it right after the current one
       settles (coalesce).
    4. mergeDelta → if fork_switch, trigger full rebuild.
    5. If added.length >= skipAnimationThreshold (default 100 per
       spec §6 rule #1), skip animation and reset the animator.
    6. Otherwise buildAnimationPlan + either start (idle animator)
       or queueNext (running animator per rule #2, _animDeferred).
    7. onReconnect → fetchTip and apply if changed (delta v1 §A.3
       catch-up semantics).
    8. All Transport calls thread AbortSignal from the orchestrator's
       internal AbortController so stop() cancels in-flight work.

- createRealtime(RealtimeDOMOptions) → RealtimeController
    DOM adapter: owns the canvas + requestAnimationFrame loop.
    Sizes canvas for devicePixelRatio each frame; paints via
    buildAnimatedPaintProgram + executePaintProgram. stop() cancels
    the RAF, calls orchestrator.stop(), and destroys the renderer.

- RealtimePlugin — id 'explorer.realtime.default', provides
  'realtime.orchestrator', fills slot 'explorer.data.realtime'.
  Capabilities expose { RealtimeOrchestrator, createRealtime } for
  plugin consumers. Registered via registerExplorerBaseline.

Type tightening

- ShareForClassify gains `h: string` (spec §5.1 — every share has
  one; omitting it was an oversight). Now satisfies DeltaShare
  directly.
- DeltaShare relaxed: dropped the `[key: string]: unknown` index
  signature — only requires `{ h: string }`. Strict interfaces
  (ShareForClassify) now satisfy it without casts.
- realtime.ts extracts `h` from the provided hashOf (default:
  (s) => s.h) rather than dictating the shape.

Tests (tests/unit/realtime.test.ts — 12 tests)

- start: fetches window + sets tip; empty window valid
- tip triggers delta fetch and appends; delta.since = current.tip
- tip dedup on same hash
- tip coalescing: max 1 delta in flight under rapid-fire tips
- fork_switch triggers second fetchWindow
- skipAnimationThreshold: bulk updates skip animation path
- below threshold: animation runs and completes via currentFrame(t)
- stop unsubscribes + halts tip processing after stop
- reconnect: fetches tip, applies delta if changed
- fetchWindow error surfaces via onError as structured ExplorerError
- currentFrame static snapshot reflects live window state

Status

- 166/166 tests pass in ~2.3s (154 prior + 12 new)
- Typecheck clean
- Bundles: shared-core 25.1 KB / 40 (63%), sharechain-explorer 42.0
  KB / 120 (35%) — both under budget; 78 KB headroom for
  particles + cards + any future wiring.

Next: Phase B #8 particles + card overlays (largest remaining
visual piece; ~300-400 LOC), or Qt refactor step 1 (CMake deps,
~30 LOC, parallel track). After particles + cards, Phase B is
feature-complete and M2 pixel-diff work begins.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants