Bug Hunt Feedback

The response panel that pairs with a BugHunt code panel. Three slots stack vertically — a wrong-line hint while the learner is still hunting, a celebration line once every bug has been flagged, and a long-form explanation card once the surrounding flow is done.

Purely presentational: the surrounding shell owns the state machine and code panel; this primitive consumes a thin set of status props and animates the three slots in and out.

Preview
Line 3 looks fine — the assignment is correct. Try a line that touches the loop guard.
Customize
Phase
Options

Installation

npx shadcn@latest add https://craftbits.dev/r/bug-hunt-feedback.json

Usage

import { BugHuntFeedback } from "@craft-bits/core";
 
<BugHuntFeedback
  phase="find"
  selectedLine={3}
  wrongLineCount={1}
  remainingBugs={1}
  wrongLineHint="Try a line that touches the loop guard."
/>

Once the hunt is fully done, drop into the explanation phase:

<BugHuntFeedback
  phase="done"
  remainingBugs={0}
  foundCount={2}
  explanation="The loop guard reads one past the end — change <= to <."
/>

wrongLineHint accepts three shapes — a static string, an array indexed by wrongLineCount, or a function called with the tapped line:

// Array — escalates per miss; clamps to the last entry
<BugHuntFeedback
  phase="find"
  wrongLineCount={2}
  wrongLineHint={["Look at the loop.", "It is the comparison operator."]}
/>
 
// Function — receives the tapped line
<BugHuntFeedback
  phase="find"
  selectedLine={4}
  wrongLineHint={(line) => "Line " + line + " is not the bug — check earlier."}
/>

Anatomy

  • Wrong-line hint — small warning-tinted text. Only renders while phase === "find", after at least one miss, and on a line that is not already in foundLines.
  • Celebration — checkmark plus a headline ("Bug found!" / "All bugs found!"), shown once remainingBugs is 0 and the phase has advanced to complete or done.
  • Explanation card — accent-railed prose. Renders only on phase === "done" and when explanation is non-empty; pass suppressExplanation to hide it even on done.

Props

PropTypeDefaultDescription
phase'find' | 'complete' | 'done'Drives which slots render.
wrongLineHintstring | string[] | (line) => stringHint shown after a miss.
selectedLinenumber | nullnull1-indexed line most recently selected.
wrongLineCountnumber0Total wrong-line taps so far. Indexes into wrongLineHint.
remainingBugsnumber0Bugs still to find. 0 reveals the celebration.
foundCountnumber0Bugs found so far. Tunes the headline (singular vs plural).
foundLinesReadonlySet<number>Lines already confirmed as bugs. Suppresses the hint when selectedLine matches.
suppressExplanationbooleanfalseForce the explanation hidden even on done.
explanationReactNodeLong-form explanation revealed on done.
accentColorstringvar(--cb-accent)Left-rail color for the explanation card.
celebrationLabelReactNodetone-derivedOverride the celebration headline.
classNamestringMerged onto the root via cn().

Accessibility

  • Each of the three slots renders inside a role="status" region with aria-live="polite" so screen-reader users hear new feedback as it appears without being interrupted.
  • All animation is opacity + small translate only; reduced-motion users see the same content without the spring.
  • Color is never the only channel — every slot has its own text label.

Credits

  • Extracted from: AlgoFlashcards (src/lessons/primitives/BugHunt/BugHuntFeedback.tsx). Stripped the useBugHuntContext coupling and the source's mixed-responsibility branching (the same component decided celebration vs explanation by reading fixOptions.length). Re-shaped around an explicit phase plus thin status props so the panel works outside the original BugHunt shell. Inline SPRING.snappy / SPRING.bouncy / SPRING.smooth and hardcoded text-warning / text-success / text-muted-foreground classes were swapped for SPRINGS.* and the cb-* semantic tokens.