Skip to content

feat(#44): show a reduction % on every folded block and group#48

Draft
a-Fig wants to merge 7 commits into
devmainfrom
feat/reduction-pct
Draft

feat(#44): show a reduction % on every folded block and group#48
a-Fig wants to merge 7 commits into
devmainfrom
feat/reduction-pct

Conversation

@a-Fig

@a-Fig a-Fig commented Jul 1, 2026

Copy link
Copy Markdown
Owner

Closes #44.

What

Adds a small, on-brand reduction-percentage tag to every folded block and folded (collapsed) group so users can read how aggressive each fold was — light or heavy. Shows % of tokens REMOVED (saved / full × 100, rounded to a whole percent), consistent with the existing Map-header "saves X tok" copy.

Brand & taste (read brand/accordion-brand-kit/brand.md first)

  • Smoke mono only--muted (#9A9A9A, the brand "labels, metadata, fold states" color) on IBM Plex Mono. Never a spectrum hue, never blue (blue is reserved for user blocks), never on a live tile. The tag keeps the folded tile's recessed, drained calm (the ci: cross-platform release workflow (Tauri installers) #1 brand signal) — information, not decoration.
  • Folded = recessed; the % is faint and inset.
  • No hype words ("powerful," "seamless," …).

Three surfaces

surface what
Pure helper engine/tokens.ts reductionPct(full, live) — divide-by-zero → 0; unit-tested
Map canvas ui/map/tileDraw.ts reductionPct? on TileSpec; cached offscreen sprite per rounded-5% value (≤21) blitted with drawImage — no per-tile fillText, no filter/gradient (CLAUDE.md perf rule). Drawn last, inset at tile bottom
Map specs ui/map/ContextMap.svelte compute reductionPct in buildTilesSpecs + protSpecs (folded blocks + collapsed groups)
Sliver mode ContextMap.svelte .cell-pct Smoke-mono badge on summary tiles (ungrouped fold + group cocoa + peeked open-band parent)
Transcript ContextMap.svelte −N% (.tr-reduction) on folded cards
Tooltips ContextMap.svelte tip/groupTip/foldTip include −N%
Inspector Inspector.svelte −N% in the token data row for folded blocks and folded groups (.tok-reduction)

Data (pure reads, no wire change)

  • Folded block: reductionPct(b.tokens, store.effTokens(b)).
  • Folded group: reductionPct(store.groupFullTokens(g), store.groupLiveTokens(g)). A drop group → live 0 → 100%, signalling "the agent does not see this block".
  • Zero-token block → 0 → no tag rendered.

Design calls left to the owner (made restrained defaults; flagged in case the screenshot loop wants to change)

  • The dense Map canvas tile shows a bare number (e.g. 85), not −85% — a ~20px tile can't legibly fit 4 chars. The Transcript/Inspector/tooltips carry the full −N% form, which establishes the number's meaning. A different glyph is a one-line sprite change.
  • Map-canvas precision is rounded to nearest 5% (sprite-cache bound); Inspector/Transcript show the exact whole-percent.

Verification

  • cd app && npm run check → 0 errors / 0 warnings
  • cd app && npm run test → 41 files, 809 tests passed (incl. new tokens.test.ts + 2 tileDraw.test.ts TileSpec cases)
  • Did not run npm run dev (port 1420 may be busy; UI is for the owner's screenshot review).

smash added 2 commits July 1, 2026 08:50
Add a small reduction-percentage tag (whole-% of tokens removed) so users
can read how aggressive each fold was, across all three fold surfaces.

- engine/tokens.ts: pure `reductionPct(full, live)` helper (saved/full*100,
  divide-by-zero -> 0); unit-tested in tokens.test.ts.
- Map canvas (tileDraw.ts): a `reductionPct?` field on TileSpec + a cached
  offscreen-sprite badge (rounded to nearest 5% -> <=21 sprites, blitted
  like the dice/hatch sprites - no per-tile fillText/filter/gradient).
  Drawn inset at the tile bottom, Smoke mono, only on folded blocks and
  collapsed-group tiles.
- ContextMap.svelte: compute reductionPct in buildTilesSpecs/protSpecs;
  add a Smoke-mono `.cell-pct` badge to sliver-mode summary tiles and the
  open-group parent (peeked = folded); add a `−N%` badge to transcript
  folded cards; surface the % in the tile/group/fold tooltips.
- Inspector.svelte: `−N%` in the token data row for folded blocks and groups.

Brand: Smoke (#9A9A9A / --muted) mono only - never a spectrum hue, never on a
live tile, faint so the folded tile stays recessed and calm. Defaults change
nothing for users who never fold.

Closes #44.
…an't render negative

reductionPct(full, live) documented and displayed as "percent of tokens
removed" did not clamp. A conductor substitution LARGER than the original
block returned a negative value (reductionPct(100, 150) = -50), which
rendered as a stray minus sign on every fold surface (tooltip, foldTip,
sliver badge, transcript badge, Inspector token row) since those sites
emit the value unconditionally for folded blocks.

Clamp at the helper so all surfaces are correct by construction, and make
the defensive test assert exact bounds (0 for oversized, 100 for fully
removed / negative live).
@a-Fig

a-Fig commented Jul 1, 2026

Copy link
Copy Markdown
Owner Author

Thanks for the review — finding addressed in 071d672.

You're right: reductionPct(100, 150) returned -50, and the fold surfaces that emit the value unconditionally (tooltip, foldTip, sliver badge, transcript badge, Inspector token row) would render −-50%. Reached because conductor replace substitutions are arbitrary content and their length is counted as the effective token cost without a shrinkage check.

Fixed at the helper (one place, so every surface is correct by construction):

export function reductionPct(full: live: number): number {
  if (full <= 0) return 0;
  const pct = Math.round(((full - live) / full) * 100);
  return Math.max(0, Math.min(100, pct));  // clamp to documented [0, 100]
}

The defensive test now asserts exact bounds — reductionPct(100, 150) === 0 and reductionPct(100, 0) === 100 (covers drop groups), plus a negative-live case reductionPct(100, -5) === 100. This also aligns the helper with the TileSpec.reductionPct "0-100" doc.

I deliberately did not add reductionPct > 0 guards at each render surface in addition to the clamp — the clamp is the single source of truth, and redundant guards at call sites would invite drift (exactly the bug that just happened: the test's comment claimed "the caller renders no tag for ≤ 0" while several callers didn't). The Map canvas's existing spec.reductionPct > 0 guard stays (it's a draw-skip optimization, not a correctness gate).

Re-verified: npm run check 0/0, tokens.test.ts 11/11, full suite 810/810 pass.

smash and others added 5 commits July 1, 2026 18:40
Tile badges (Map canvas sprite + sliver/open-group DOM spans) now show a
single 0-9 digit — drop the ones place, keep the tens digit of the
reduction %, clamped to 9 for a fully-removed (100%) fold. Tooltips, the
Transcript row, and the Inspector panel keep the precise "-X%" text.
Every number added in this PR now reads as "how much of the original
content is still on the wire" instead of "how much was removed":
tokens.ts's reductionPct/reductionDigit become remainingPct/remainingDigit
(live/full x 100, clamped [0,100]; a zero-token block reads as 100% -
fully intact, matching the "nothing to remove" case). The tile badge
digit, tooltips, Transcript row (renamed .tr-remaining), and Inspector
panel (renamed .tok-remaining) all switch accordingly, dropping the
leading minus sign in favor of "X% remains" copy since the number is no
longer a delta.
Both render paths (canvas sprite in tileDraw.ts, DOM .cell-pct span in
ContextMap.svelte) shift from bottom-centered to top-right, inset 1-2px.
…on border

The digit badge collides with dice pips at every corner once a tile reaches
face 4+, so drop it in favor of a border that lives at the tile's true edge
(the one region the pips never touch). remainingBand buckets remaining-token
pct into 6 stepped sizes (full >=90, 90-75, 75-50, 50-25, 25-10, empty <10);
both the canvas (tileDraw.ts, cached sprite per band) and DOM/sliver
(ContextMap.svelte, inline SVG) renderers share the same erosionArmLength
geometry so the two paths stay pixel-consistent. Applied to individually
folded blocks and group summary tiles only. The border is drawn before the
pinned/selected/inrange rings so those rarer, more important states always
paint on top when they coincide.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
… % remaining

Live blocks/groups keep their existing richer tooltip (tool name, action
hint). Anything already degraded — a folded block, a folded or drop group,
a peeked group — now shows only the 3 fields that matter once something's
gone: kind, original token count, precise % remaining. Backed by a single
shared minimalTip() helper so all 4 call sites (tip, foldTip, groupTip, the
open-group peek title) stay consistent.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@a-Fig a-Fig marked this pull request as draft July 3, 2026 03:05
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