Skip to content

measuring stick + integer snap on release - #9

Merged
jackhenderson12 merged 1 commit into
mainfrom
measuring-stick-and-integers
May 7, 2026
Merged

measuring stick + integer snap on release#9
jackhenderson12 merged 1 commit into
mainfrom
measuring-stick-and-integers

Conversation

@jackhenderson12

Copy link
Copy Markdown
Contributor

Summary

Replaces #8's gauge (live arrow + two fading reference ticks) with a different shape: a persistent 0–10 ruler on each funnel, plus integer snapping at commit time. Net result: clean integer states, a visible scale, no decimals to parse.

The hold-to-pour gesture stays — water rises continuously and slows visibly during a hold, and that's still the load-bearing pedagogy. Only the committed value is integer.

Why this differs from #8

The live indicator from #8 duplicated the under-funnel readout, the reference ticks hid during pours (when spatial reference matters most), and decimal vote counts (5.9 votes 34.8 credits) cost more in legibility than they gained in honesty. The brief asked for a different intervention entirely.

What changed

Removed (from #8):

  • Live indicator (arrow + numeric label).
  • Reference ticks at votes 5 and 10 with the fade-during-pour cross-fade.
  • GAUGE_W = 36 reservation past the V's right edge — funnel cavity returns to pre-measuring-stick: per-funnel gauge #8 proportions.
  • isAnyPouring prop wiring from LiquidQVFunnel.
  • All decimal display. fmt(n) returns Math.round(n).toString().

Added:

  • Persistent 0–10 ruler on the outer right edge:
    • Major ticks with labels at 0/2/4/6/8/10 (1.5 px stroke, 10 px length).
    • Minor ticks at 1/3/5/7/9, no labels (1 px stroke, 5 px length).
    • Tick marks point left toward the water; labels sit just right of the axis.
    • Always visible — no fade, no state-dependent visibility. Same on every funnel.
    • Vote-axis is linear in height (votes = water height), so ticks are evenly spaced. The quadratic stays in the credits readout, not in the ruler.
  • snapVotesToInteger(live, item, votes, budget) in src/math/qv.ts:
committed = clamp(round(live), 0, ⌊√budget⌋, ⌊√(budget  Σ others²))

Round-half-up, then clamp DOWN if the rounded value would overdraw. Two edge cases pinned by tests:

  • 9.6 with b = 5 (others using 25 credits, available = 75) → round = 10 → clamps to ⌊√75⌋ = 8.
  • 9.7 with b = 4 (others using 16, available = 84) → round = 10 → clamps to ⌊√84⌋ = 9.

endPour in LiquidQV now calls snapVotesToInteger instead of clampVotesAgainstBudget. The reducer still floors any fractional input it receives as defence-in-depth.

What's preserved

  • The hold-to-pour gesture mechanics (constant volumetric rate, water rises smoothly and slows as the funnel widens).
  • The 2D triangle funnel rendering (post-Revert PR #6: 3D trough rendering #7 revert state).
  • Conservation invariant — pool + Σ votes² = budget — now expressed in integers at rest.
  • The pool reservoir, the pour stream, the intro copy, the "How it works" explainer, the footer disclaimer, the default ballot.

Verified end-to-end

  • All funnels at 0 → ruler visible (0/2/4/6/8/10 labels + minor ticks). Pool reads 100 / 100 credits. Each card reads 0 votes 0 credits.
  • Hold + on Harris ~1.5 s → on release, snaps to 3 votes 9 credits; pool drops to 91 / 100 credits. Conservation holds.
  • DOM at the post-release moment:
    harris aria-valuetext: "3 votes, 9 credits"
    harris readout:        "3 votes  9 credits"
    pool aria-valuetext:   "91 of 100 credits remaining"
    
  • 33 tests passing (was 29; added 4 new for snapVotesToInteger including both round-up clamp-down edge cases).
  • ESLint clean, tsc -b --noEmit clean, all three build targets succeed, no console warnings during interaction.

Files touched

  • src/math/qv.tsmaxVotes returns ⌊√budget⌋ again; clampVotesAgainstBudget floors fractional input; new snapVotesToInteger.
  • src/math/qv.test.ts — integer-cap behaviour reasserted; new snapVotesToInteger block (4 tests).
  • src/components/Funnel.tsx — gauge layer removed; funnel cavity restored; 0–10 ruler added in extended viewBox space past the V's right edge.
  • src/components/LiquidQV.tsxsnapVotesToInteger in endPour; fmt is now integer; isAnyPouring prop dropped.
  • src/components/CreditPool.tsx — readout uses Math.round.
  • docs/round-11/README.md — state descriptions with verified DOM snapshots.

Screenshots

GitHub doesn't accept binary uploads via gh pr create, and the preview environment couldn't reliably render clean image captures for this round (viewport / scroll quirks). docs/round-11/README.md on this branch describes each state with the verified DOM-level evidence. To see it live: npm run dev.

Test plan

  • npm install && npm run dev
  • Page at rest, all funnels at 0 → ruler visible on every funnel (labels at 0/2/4/6/8/10, minor ticks between).
  • Hold + on Kamala Harris from 0 for ~1.4 s; release → readout snaps to 3 votes 9 credits, pool to 91 / 100 credits.
  • Hold + on Harris again from 3 for another ~1.4 s; release → snaps to ~4 votes 16 credits (clearly less progress for the same hold duration — the lesson, intact).
  • Hold − to drain; release → snaps down by ~1 vote.
  • Conservation invariant holds across multiple non-zero funnels.
  • OS reduced-motion → still works (water snaps without animation; ruler always visible).
  • npm test && npm run lint && npm run typecheck — green.

Don't merge

Per the brief: hold for review.

🤖 Generated with Claude Code

Replaces PR #8's gauge (live arrow + two fading reference ticks) with
a different intervention: a persistent 0–10 ruler on each funnel and
integer snapping at commit time. Net result: clean integer states, a
visible scale, no decimals to parse.

Why
---
The live indicator from #8 duplicated the under-funnel readout, the
reference ticks hid during pours (when spatial reference matters
most), and decimal vote counts ("5.9 votes  34.8 credits") cost more
in legibility than they gained in honesty. The brief asked for a
different shape entirely.

What's added
------------
- A persistent 0–10 ruler on the outer right edge of each funnel.
  Major ticks with labels at 0/2/4/6/8/10; minor ticks (no labels)
  at 1/3/5/7/9. Always visible, no fade behaviour, no isAnyPouring
  plumbing. Tick marks point left toward the water; labels sit just
  right of the axis. Vote-axis is linear in height (votes = water
  height), so ticks are evenly spaced — the quadratic lives in the
  credits readout, not the ruler.
- snapVotesToInteger(live, item, votes, budget) in math/qv.ts:
    committed = clamp(round(live), 0, ⌊√budget⌋, ⌊√(budget − Σothers²)⌋)
  Round-half-up, then clamp DOWN if the rounded value would
  overdraw — the brief's two edge cases (release at 9.6 with others
  holding the pool, release above the cap) both fall through to the
  clamp.

What's removed
--------------
- The live indicator from PR #8 (left-pointing arrow + numeric label
  on the right edge).
- The two reference ticks at votes = 5 and 10 with the
  fade-during-pour cross-fade.
- GAUGE_W (36 px) reservation past the V's right edge — the funnel
  cavity returns to its pre-#8 proportions; the ruler lives in
  extended viewBox width past the V's right edge.
- The `isAnyPouring` prop wiring from LiquidQV → Funnel. No state
  needs to follow the global pour anymore.
- All decimal display: under-funnel readout, pool readout, ARIA
  values are integer everywhere. The display formatter `fmt(n)` now
  returns `Math.round(n).toString()`. Underlying `votes` may still
  be fractional during a hold (the live derivation), but every
  visible number rounds at the boundary.

What's preserved
----------------
- The hold-to-pour gesture mechanics — constant volumetric rate,
  water rises smoothly and slows visibly as the funnel widens. This
  is still the load-bearing pedagogy: only the *committed* value is
  integer, not the in-flight motion.
- The 2D triangle funnel rendering (post-#7 revert state).
- Conservation invariant — pool + Σ votes² = budget — now expressed
  in integers at rest.
- Pool reservoir, pour stream, intro copy, on-load explainer, footer
  disclaimer, default ballot.

Verified end-to-end
-------------------
- All funnels at 0 → ruler visible (0/2/4/6/8/10 labels + minor
  ticks). Pool reads "100 / 100 credits". Each card reads
  "0 votes  0 credits".
- Hold + on Harris ~1.5 s → on release, snaps to "3 votes  9 credits";
  pool drops to "91 / 100 credits". Conservation holds.
- 33 tests passing (was 29; added 4 new for snapVotesToInteger
  including both round-up clamp-down edge cases). ESLint clean,
  typecheck clean, all three build targets succeed, no console
  warnings during interaction.

Docs
----
docs/round-11/README.md describes each state with the verified DOM
snapshots from the live preview.
@netlify

netlify Bot commented May 7, 2026

Copy link
Copy Markdown

Deploy Preview for liquid-qv ready!

Name Link
🔨 Latest commit 55258d8
🔍 Latest deploy log https://app.netlify.com/projects/liquid-qv/deploys/69fcaa4bb7cbc200087c51d6
😎 Deploy Preview https://deploy-preview-9--liquid-qv.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@jackhenderson12
jackhenderson12 merged commit f6f3248 into main May 7, 2026
5 checks passed
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.

1 participant