Bug Hunt Shell
The visual wrapper for a bug-hunt puzzle. Runs the trace / find / fix / done state machine, hosts a BugHuntCode panel by default, and publishes the live state to any descendant via BugHuntProvider. Drop BugHuntFeedback, BugHuntFixPhase, or any custom widget into the children / footer slots and they can read useBugHunt() without prop drilling.
Preview
Find the bug1 remaining
Customize
Options
Installation
npx shadcn@latest add https://craftbits.dev/r/bug-hunt-shell.jsonUsage
import { BugHuntShell } from "@craft-bits/core";
<BugHuntShell
codeLines={code}
buggyLines={[4]}
fixOptions={fixOptions}
lang="typescript"
onComplete={(correct) => console.log("done", correct)}
/>Add bespoke chrome around the embedded panel by composing children and footer slots:
<BugHuntShell codeLines={code} buggyLines={[4]} fixOptions={fixes}>
<p className="text-cb-fg-muted">Trace the loop guard, then tap the bug.</p>
{/* the embedded BugHuntCode renders here */}
</BugHuntShell>Wire descendants to the shared state with useBugHunt:
function Hint() {
const { state } = useBugHunt();
if (state.wrongLineCount === 0) return null;
return <p>Look at the loop guard.</p>;
}Anatomy
- Provider —
BugHuntProviderwraps the entire tree so any descendant can calluseBugHunt(). The provider carriesstate,actions,codeLines,buggyLines,fixOptions,lang, andtrackHex. - Layout — a
<div>styledflex flex-col gap-4carriesdata-cb-edu="bug-hunt-shell"anddata-phasereflecting the current state machine phase. - Children slot — rendered above the embedded code panel; ideal for narration, a trace stepper, or any context the learner needs before tapping.
- Embedded
BugHuntCode— rendered with the shell's state. Suppress it withhideCodePanelwhen you want a fully bespoke layout but still want the shared state machine. - Footer slot — rendered below the embedded code panel; ideal for
BugHuntFeedback,BugHuntFixPhase, or a custom result card.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
codeLines | readonly string[] | — | Lines of code rendered in the panel. |
buggyLines | readonly number[] | — | 1-indexed lines that contain a bug. |
lang | string | "typescript" | Shiki language id forwarded to the panel. |
traceLength | number | 0 | Number of trace steps before the find phase. |
fixOptions | readonly BugHuntFixOptionDef[] | [] | Candidate fixes shown after the last bug is found. |
fixRetryable | boolean | false | When true, a wrong fix resets so the learner can try again. |
onComplete | (correct: boolean) => void | — | Fires once the puzzle resolves. |
trackHex | string | var(--cb-accent) | Accent color forwarded into the context. |
codeHeaderText | ReactNode | "Find the bug" | Header text on the embedded panel. |
hideCodeHeader | boolean | false | Suppress the panel's header strip. |
showLineNumbers | boolean | true | Show gutter line numbers in the panel. |
codeMaxHeight | string | — | max-height on the panel's scroll container. |
children | ReactNode | — | Rendered above the embedded panel. |
footer | ReactNode | — | Rendered below the embedded panel. |
hideCodePanel | boolean | false | Suppress the embedded panel entirely. |
onCorrectTap | () => void | — | Fires on a confirmed bug tap or a correct fix pick. |
onWrongTap | () => void | — | Fires on a wrong-line tap or a wrong fix pick. |
className | string | — | Merged onto the layout wrapper via cn(). |
Accessibility
- The shell exposes
data-cb-edu="bug-hunt-shell"anddata-phaseon its layout wrapper — useful both as a styling hook and as a landmark for screen-reader users navigating phase changes. - The embedded
BugHuntCodekeeps its ownrole="group"plus per-linearia-label/aria-pressed; the shell does not override them. - Motion comes from the embedded
BugHuntCode; the shell adds none of its own.prefers-reduced-motionflows through to every descendant. - Sound is opt-in via
onCorrectTap/onWrongTap. Consumers that wire audio should also pair it with a visual equivalent (the panel's tap feedback already covers this for hit/miss).
Credits
- Extracted from:
AlgoFlashcards(src/lessons/primitives/BugHunt/BugHuntShell.tsx). Folded the project'suseBugHuntreducer into a private state-machine hook, stripped theuseLessonContextSafefallback (onScreenComplete/trackInfo.hexdefaults), replacedplaySound("correct" / "wrong")withonCorrectTap/onWrongTapcallbacks, swapped the always-presentBugHuntCodechild for a controllablehideCodePanelslot, and added afooterslot so feedback and fix-phase panels sit naturally below the code without ad-hoc composition.