WindowBudget

A sliding-window viewer fused with a budget meter. The caller supplies an array of numbers, an inclusive window interval start, end, a budget cap (total ops allowed), and the current ops consumed so far. The component renders the array as a row of cells with the active window highlighted, and a token-stack meter underneath that drains as current grows. As the remaining budget crosses 30 percent the meter flips to warning styling; below 15 percent it pulses critical.

Pure visualization primitive — no protocol logic, no narration, no scoring. Drop into any sliding-window narrative (brute force versus incremental, fixed versus variable window, monotonic deque) and layer narration, predictions, or audio around it. The callers reducer drives window and current; the component owns the layout, the meter, and the motion.

Sliding window: window [0, 3] of length 4. Budget 56 of 60 ops remaining (4 used).
Sliding window of length 4 over a 15-cell array (60 op budget)
2
7
1
8
2
8
1
4
5
9
0
3
6
2
7
budget:56 / 60(3 ops/dot)

Installation

npx shadcn@latest add https://craftbits.dev/r/window-budget.json

Usage

import { WindowBudget } from "@craft-bits/core";
 
<WindowBudget
  array={[2, 7, 1, 8, 2, 8, 1, 4, 5, 9]}
  window={{ start: 0, end: 3 }}
  budget={40}
  current={4}
/>

Cells hidden, meter only — just the draining token stack for a header chip:

<WindowBudget
  array={values}
  window={{ start: 0, end: 3 }}
  budget={40}
  current={10}
  hideCells
/>

Custom meter caption — replace the default chip with a caller-formatted node:

<WindowBudget
  array={values}
  window={{ start: 0, end: 3 }}
  budget={40}
  current={10}
  formatMeter={({ remaining, total }) => "ops left: " + remaining + " / " + total}
/>

Understanding the component

  1. Inclusive window. window={ start, end } is closed on both ends — start === end is a single-cell window. Out-of-range values clamp to the array bounds.
  2. Budget clamps for display. current is clamped to the range zero through budget before render. Pass a value above budget and the meter shows zero remaining — it never overflows.
  3. Token-stack meter. Each token represents one op when budget <= 24. Above that, ops group into chunks of ceil(budget / 24) so the row never exceeds 24 dots; the chunk size is announced in the caption.
  4. Three urgency tiers. Above 30 percent remaining the meter sits in the accent tone. Below 30 percent it shifts to warning. Below 15 percent it pulses with a 0.5s critical loop.
  5. Exhausted state. When current >= budget the meter renders zero alive tokens and adds an inline "exhausted" tag in the error tone.
  6. Reduced motion. When prefers-reduced-motion: reduce is set the cell scale, the meter pulses, and the dot stagger all collapse to instant.

Props

PropTypeDefaultDescription
arrayreadonly number[]requiredSource array. Caller owns the values.
window{ start: number; end: number }requiredActive inclusive window interval. Clamped to the array bounds.
budgetnumberrequiredTotal operations the search is allowed to consume.
currentnumberrequiredOperations consumed so far. Clamped to [0, budget].
tone"default" | "accent" | "success" | "warning" | "error""accent"Semantic tone for the window highlight and meter accent.
titleReactNodeOptional title rendered above the cells.
hideCellsbooleanfalseHide the array cell row.
hideMeterbooleanfalseHide the token-stack budget meter.
formatMeter(state) => ReactNodeOverride the meter caption with a caller-formatted node.
classNamestringMerged onto the outer <div> via cn().

Accessibility

  • The root carries role="group" with aria-roledescription="window budget" and an aria-label summarising the active window and remaining budget on every render.
  • An off-screen aria-live="polite" summary mirrors the visible state so screen-reader users hear the window and meter advance.
  • Each cell has an aria-label naming its index, value, and whether it sits inside the active window.
  • The meter is role="meter" with aria-valuenow, aria-valuemin, and aria-valuemax matching the remaining budget — assistive tech reports it as a real progress signal.
  • Urgency is conveyed by colour, scale pulse, and an explicit "exhausted" tag — never colour alone.
  • Motion respects prefers-reduced-motion: reduce — cell scale, meter pulse, and dot stagger collapse to instant.

Credits

  • Extracted from: algoflashcards (src/lessons/primitives/observation/WindowBudget.tsx). The source was a 2300+ line four-phase sliding-window lesson bundling a brute-force tap-to-count Phase 1 with a hard budget cap, a Discovery moment where the student taps the overlap between two windows, a Phase 2 incremental update with directional and consequence prediction gates, a Phase 3 code bridge driven by MagicMoveBlock that morphs the nested loop into a compact single-loop slide, a PhaseSummary 4-beat cinematic with arrow-and-savings-badge reveal, audio cues, scoring, per-distractor PredictionGate feedback, mood-driven container palettes, and Done-screen sparkle motion. The library extract strips every lesson concern and keeps only the playback primitive: the highlighted window row, the token-stack budget meter with its three urgency tiers, and the caller-controlled window and current props.