{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "mnemonic-flow",
  "title": "Mnemonic Flow",
  "author": "Satchmo <https://bigblocks.dev>",
  "description": "Multi-mode seed phrase display and input block with create, display, import, and verify modes. Includes numbered word grid, copy-all, confirmation checkbox, and verification challenge.",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "badge",
    "button",
    "card",
    "checkbox",
    "input",
    "label"
  ],
  "files": [
    {
      "path": "registry/new-york/blocks/mnemonic-flow/index.tsx",
      "content": "\"use client\"\n\nimport {\n  useMnemonicFlow,\n  type UseMnemonicFlowOptions,\n} from \"./use-mnemonic-flow\"\nimport { MnemonicFlowUi } from \"./mnemonic-flow-ui\"\n\n// ---------------------------------------------------------------------------\n// Re-exports\n// ---------------------------------------------------------------------------\n\nexport {\n  useMnemonicFlow,\n  type UseMnemonicFlowOptions,\n  type UseMnemonicFlowReturn,\n  type MnemonicFlowMode,\n  type MnemonicWordCount,\n  type VerificationChallenge,\n} from \"./use-mnemonic-flow\"\n\nexport {\n  MnemonicFlowUi,\n  type MnemonicFlowUiProps,\n} from \"./mnemonic-flow-ui\"\n\nexport {\n  MnemonicGridUi,\n  type MnemonicGridUiProps,\n  type WordSlotProps,\n  type WordSlotMode,\n} from \"./mnemonic-grid-ui\"\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Props for the composed MnemonicFlow component */\nexport interface MnemonicFlowProps {\n  /** Operating mode: display, create, import, or verify */\n  mode: UseMnemonicFlowOptions[\"mode\"]\n  /** Pre-populated words for display/verify modes */\n  words?: string[]\n  /** Number of words (12 or 24, default: 12) */\n  wordCount?: UseMnemonicFlowOptions[\"wordCount\"]\n  /** Called when the flow completes with the final word list */\n  onComplete?: (words: string[]) => void\n  /** Called when the user cancels */\n  onCancel?: () => void\n  /** External loading state (e.g., wallet creation in progress) */\n  isLoading?: boolean\n  /** External error message */\n  error?: string | null\n  /** Additional CSS classes */\n  className?: string\n}\n\n// ---------------------------------------------------------------------------\n// Composed component\n// ---------------------------------------------------------------------------\n\n/**\n * Multi-mode seed phrase display and input block.\n *\n * Composes `useMnemonicFlow` (logic) with `MnemonicFlowUi` (presentation)\n * into a single, ready-to-use component.\n *\n * Modes:\n * - **display**: Read-only numbered grid with copy-all button\n * - **create**: Generated words + display + confirmation checkbox\n * - **import**: Editable input grid for entering words\n * - **verify**: Show 2 random positions, user must type the correct words\n *\n * @example\n * ```tsx\n * import { MnemonicFlow } from \"@/components/blocks/mnemonic-flow\"\n *\n * <MnemonicFlow\n *   mode=\"create\"\n *   words={generatedWords}\n *   onComplete={(words) => createWallet(words.join(\" \"))}\n *   onCancel={() => router.back()}\n * />\n * ```\n */\nexport function MnemonicFlow({\n  mode,\n  words: initialWords,\n  wordCount = 12,\n  onComplete,\n  onCancel,\n  isLoading = false,\n  error: externalError,\n  className,\n}: MnemonicFlowProps) {\n  const flow = useMnemonicFlow({\n    mode,\n    words: initialWords,\n    wordCount,\n    onComplete,\n    onCancel,\n  })\n\n  return (\n    <MnemonicFlowUi\n      mode={flow.mode}\n      words={flow.words}\n      wordCount={flow.wordCount}\n      confirmed={flow.confirmed}\n      onConfirmedChange={flow.setConfirmed}\n      onWordChange={flow.setWord}\n      challenge={flow.challenge}\n      onVerificationAnswer={flow.setVerificationAnswer}\n      isValid={flow.isValid}\n      canSubmit={flow.canSubmit}\n      onSubmit={flow.submit}\n      onCancel={flow.cancel}\n      onCopy={flow.copyWords}\n      copied={flow.copied}\n      isLoading={isLoading}\n      error={externalError ?? flow.error}\n      className={className}\n    />\n  )\n}\n",
      "type": "registry:block",
      "target": "~/components/blocks/mnemonic-flow/index.tsx"
    },
    {
      "path": "registry/new-york/blocks/mnemonic-flow/mnemonic-flow-ui.tsx",
      "content": "\"use client\"\n\nimport { useMemo } from \"react\"\nimport { Check, ClipboardCopy, Loader2, X } from \"lucide-react\"\nimport { Button } from \"@/components/ui/button\"\nimport {\n  Card,\n  CardContent,\n  CardDescription,\n  CardFooter,\n  CardHeader,\n  CardTitle,\n} from \"@/components/ui/card\"\nimport { Checkbox } from \"@/components/ui/checkbox\"\nimport { Label } from \"@/components/ui/label\"\nimport { Badge } from \"@/components/ui/badge\"\nimport { cn } from \"@/lib/utils\"\nimport { MnemonicGridUi } from \"./mnemonic-grid-ui\"\nimport type {\n  MnemonicFlowMode,\n  MnemonicWordCount,\n  VerificationChallenge,\n} from \"./use-mnemonic-flow\"\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Props for the mnemonic flow UI shell */\nexport interface MnemonicFlowUiProps {\n  /** Operating mode */\n  mode: MnemonicFlowMode\n  /** Word list */\n  words: string[]\n  /** Expected word count */\n  wordCount: MnemonicWordCount\n  /** Whether the confirmation checkbox is checked */\n  confirmed: boolean\n  /** Toggle confirmation */\n  onConfirmedChange: (value: boolean) => void\n  /** Update a word at index */\n  onWordChange: (index: number, value: string) => void\n  /** Verification challenge (verify mode only) */\n  challenge: VerificationChallenge | null\n  /** Update a verification answer */\n  onVerificationAnswer: (position: number, value: string) => void\n  /** Whether the state is valid */\n  isValid: boolean\n  /** Whether submit is enabled */\n  canSubmit: boolean\n  /** Handle submit */\n  onSubmit: () => void\n  /** Handle cancel */\n  onCancel: () => void\n  /** Copy all words */\n  onCopy: () => void\n  /** Whether words were recently copied */\n  copied: boolean\n  /** External loading state */\n  isLoading?: boolean\n  /** Error message */\n  error?: string | null\n  /** Additional CSS classes */\n  className?: string\n}\n\n// ---------------------------------------------------------------------------\n// Mode config\n// ---------------------------------------------------------------------------\n\ninterface ModeConfig {\n  title: string\n  description: string\n  submitLabel: string\n}\n\nfunction getModeConfig(mode: MnemonicFlowMode, wordCount: MnemonicWordCount): ModeConfig {\n  switch (mode) {\n    case \"display\":\n      return {\n        title: \"Recovery Phrase\",\n        description: `Your ${wordCount}-word recovery phrase. Store it securely.`,\n        submitLabel: \"Done\",\n      }\n    case \"create\":\n      return {\n        title: \"Create Recovery Phrase\",\n        description: `Write down these ${wordCount} words in order. You will need them to recover your wallet.`,\n        submitLabel: \"Continue\",\n      }\n    case \"import\":\n      return {\n        title: \"Import Recovery Phrase\",\n        description: `Enter your ${wordCount}-word recovery phrase to restore your wallet.`,\n        submitLabel: \"Import Wallet\",\n      }\n    case \"verify\":\n      return {\n        title: \"Verify Recovery Phrase\",\n        description:\n          \"Confirm you saved your recovery phrase by entering the words at the highlighted positions.\",\n        submitLabel: \"Verify\",\n      }\n  }\n}\n\n// ---------------------------------------------------------------------------\n// Component\n// ---------------------------------------------------------------------------\n\n/**\n * Full mnemonic flow UI with header, grid, confirmation, and action buttons.\n *\n * Pure presentation — all state and callbacks come through props. Pair with\n * `useMnemonicFlow` for the logic layer.\n */\nexport function MnemonicFlowUi({\n  mode,\n  words,\n  wordCount,\n  confirmed,\n  onConfirmedChange,\n  onWordChange,\n  challenge,\n  onVerificationAnswer,\n  isValid,\n  canSubmit,\n  onSubmit,\n  onCancel,\n  onCopy,\n  copied,\n  isLoading = false,\n  error,\n  className,\n}: MnemonicFlowUiProps) {\n  const config = getModeConfig(mode, wordCount)\n\n  // Build position sets for the grid\n  const editablePositions = useMemo(() => {\n    if (mode === \"import\") {\n      return new Set(Array.from({ length: words.length }, (_, i) => i))\n    }\n    return undefined\n  }, [mode, words.length])\n\n  const blankPositions = useMemo(() => {\n    if (mode === \"verify\" && challenge) {\n      return new Set(challenge.positions)\n    }\n    return undefined\n  }, [mode, challenge])\n\n  return (\n    <Card className={cn(\"w-full\", className)}>\n      <CardHeader>\n        <div className=\"flex items-center justify-between\">\n          <div className=\"flex flex-col gap-1.5\">\n            <CardTitle>{config.title}</CardTitle>\n            <CardDescription>{config.description}</CardDescription>\n          </div>\n          <Badge variant=\"outline\" className=\"shrink-0\">\n            {wordCount} words\n          </Badge>\n        </div>\n      </CardHeader>\n\n      <CardContent className=\"flex flex-col gap-4\">\n        {/* Word grid */}\n        <MnemonicGridUi\n          words={words}\n          columns={4}\n          editablePositions={editablePositions}\n          blankPositions={blankPositions}\n          onWordChange={onWordChange}\n          blankValues={challenge?.answers}\n          onBlankChange={onVerificationAnswer}\n        />\n\n        {/* Copy button for display and create modes */}\n        {(mode === \"display\" || mode === \"create\") && (\n          <Button\n            variant=\"outline\"\n            size=\"sm\"\n            className=\"self-end\"\n            onClick={onCopy}\n            aria-label=\"Copy recovery phrase\"\n          >\n            {copied ? (\n              <>\n                <Check data-icon=\"inline-start\" />\n                Copied\n              </>\n            ) : (\n              <>\n                <ClipboardCopy data-icon=\"inline-start\" />\n                Copy All\n              </>\n            )}\n          </Button>\n        )}\n\n        {/* Confirmation checkbox for create mode */}\n        {mode === \"create\" && (\n          <div className=\"flex items-center gap-2 rounded-md border border-border bg-muted/50 px-4 py-3\">\n            <Checkbox\n              id=\"mnemonic-confirm\"\n              checked={confirmed}\n              onCheckedChange={(checked) =>\n                onConfirmedChange(checked === true)\n              }\n            />\n            <Label\n              htmlFor=\"mnemonic-confirm\"\n              className=\"cursor-pointer select-none text-sm\"\n            >\n              I have written down my recovery phrase\n            </Label>\n          </div>\n        )}\n\n        {/* Verification hint */}\n        {mode === \"verify\" && challenge && (\n          <p className=\"text-sm text-muted-foreground\">\n            Enter word{\" \"}\n            <span className=\"font-mono font-medium text-foreground\">\n              #{challenge.positions[0] + 1}\n            </span>{\" \"}\n            and{\" \"}\n            <span className=\"font-mono font-medium text-foreground\">\n              #{challenge.positions[1] + 1}\n            </span>{\" \"}\n            from your recovery phrase.\n          </p>\n        )}\n\n        {/* Error message */}\n        {error && (\n          <div className=\"flex items-start gap-3 rounded-md border border-destructive/20 bg-destructive/5 p-4\">\n            <X className=\"mt-0.5 size-4 shrink-0 text-destructive\" />\n            <p className=\"text-sm text-destructive\">{error}</p>\n          </div>\n        )}\n      </CardContent>\n\n      {/* Footer with action buttons (hidden in pure display mode) */}\n      {mode !== \"display\" && (\n        <CardFooter className=\"flex gap-3\">\n          <Button variant=\"outline\" className=\"flex-1\" onClick={onCancel}>\n            Cancel\n          </Button>\n          <Button\n            className=\"flex-1\"\n            onClick={onSubmit}\n            disabled={!canSubmit || isLoading}\n            aria-busy={isLoading}\n          >\n            {isLoading ? (\n              <>\n                <Loader2 className=\"animate-spin\" data-icon=\"inline-start\" />\n                Processing...\n              </>\n            ) : (\n              config.submitLabel\n            )}\n          </Button>\n        </CardFooter>\n      )}\n    </Card>\n  )\n}\n",
      "type": "registry:component",
      "target": "~/components/blocks/mnemonic-flow/mnemonic-flow-ui.tsx"
    },
    {
      "path": "registry/new-york/blocks/mnemonic-flow/mnemonic-grid-ui.tsx",
      "content": "\"use client\"\n\nimport { useCallback } from \"react\"\nimport { Input } from \"@/components/ui/input\"\nimport { cn } from \"@/lib/utils\"\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** A single word slot that can be read-only, editable, or a verification blank */\nexport type WordSlotMode = \"readonly\" | \"editable\" | \"blank\"\n\n/** Props for an individual word slot in the grid */\nexport interface WordSlotProps {\n  /** 1-based position number */\n  position: number\n  /** The word to display (empty string for blanks) */\n  word: string\n  /** How the slot behaves */\n  slotMode: WordSlotMode\n  /** Called when the user changes a word (editable or blank mode) */\n  onChange?: (value: string) => void\n  /** Placeholder text for input fields */\n  placeholder?: string\n}\n\n/** Props for the full mnemonic grid */\nexport interface MnemonicGridUiProps {\n  /** The word list to render */\n  words: string[]\n  /** How many columns to show (default: 4) */\n  columns?: 3 | 4\n  /** Set of positions (0-indexed) that should be editable inputs */\n  editablePositions?: Set<number>\n  /** Set of positions (0-indexed) that are verification blanks */\n  blankPositions?: Set<number>\n  /** Called when a word changes (index, value) */\n  onWordChange?: (index: number, value: string) => void\n  /** Values for blank/verification positions keyed by index */\n  blankValues?: Record<number, string>\n  /** Called when a blank value changes (index, value) */\n  onBlankChange?: (index: number, value: string) => void\n  /** Additional CSS classes */\n  className?: string\n}\n\n// ---------------------------------------------------------------------------\n// Word Slot\n// ---------------------------------------------------------------------------\n\nfunction WordSlot({\n  position,\n  word,\n  slotMode,\n  onChange,\n  placeholder,\n}: WordSlotProps) {\n  const handleChange = useCallback(\n    (e: React.ChangeEvent<HTMLInputElement>) => {\n      onChange?.(e.target.value)\n    },\n    [onChange]\n  )\n\n  return (\n    <div\n      className={cn(\n        \"flex items-center gap-2 rounded-md border border-border bg-muted/50 px-3 py-2\",\n        slotMode === \"blank\" && \"border-primary/50 bg-primary/5\"\n      )}\n    >\n      <span className=\"shrink-0 w-6 text-right text-xs font-mono text-muted-foreground\">\n        {position}.\n      </span>\n      {slotMode === \"readonly\" ? (\n        <span className=\"text-sm font-mono text-foreground select-all truncate\">\n          {word}\n        </span>\n      ) : (\n        <Input\n          type=\"text\"\n          value={slotMode === \"blank\" ? undefined : word}\n          defaultValue={slotMode === \"blank\" ? \"\" : undefined}\n          onChange={handleChange}\n          className=\"h-auto border-0 bg-transparent p-0 text-sm font-mono shadow-none focus-visible:ring-0\"\n          placeholder={placeholder ?? `word ${position}`}\n          autoComplete=\"off\"\n          spellCheck={false}\n          autoCapitalize=\"none\"\n          aria-label={`Word ${position}`}\n        />\n      )}\n    </div>\n  )\n}\n\n// ---------------------------------------------------------------------------\n// Mnemonic Grid\n// ---------------------------------------------------------------------------\n\n/**\n * A numbered grid of mnemonic seed words.\n *\n * Supports three slot modes:\n * - **readonly**: Displays the word as text (display/create modes)\n * - **editable**: Renders an input for the user to type a word (import mode)\n * - **blank**: Shows an empty input the user must fill in (verify mode)\n *\n * Pure presentation component — receives all data and callbacks via props.\n */\nexport function MnemonicGridUi({\n  words,\n  columns = 4,\n  editablePositions,\n  blankPositions,\n  onWordChange,\n  blankValues,\n  onBlankChange,\n  className,\n}: MnemonicGridUiProps) {\n  const gridCols =\n    columns === 3\n      ? \"grid-cols-3\"\n      : \"grid-cols-2 sm:grid-cols-4\"\n\n  return (\n    <div className={cn(\"grid gap-2\", gridCols, className)}>\n      {words.map((word, index) => {\n        const isBlank = blankPositions?.has(index) ?? false\n        const isEditable = editablePositions?.has(index) ?? false\n\n        let slotMode: WordSlotMode = \"readonly\"\n        if (isBlank) slotMode = \"blank\"\n        else if (isEditable) slotMode = \"editable\"\n\n        const displayWord = isBlank\n          ? (blankValues?.[index] ?? \"\")\n          : word\n\n        const handleSlotChange = isBlank\n          ? (value: string) => onBlankChange?.(index, value)\n          : isEditable\n            ? (value: string) => onWordChange?.(index, value)\n            : undefined\n\n        return (\n          <WordSlot\n            // Position is the stable identity for mnemonic grids\n            key={index}\n            position={index + 1}\n            word={displayWord}\n            slotMode={slotMode}\n            onChange={handleSlotChange}\n            placeholder={\n              isBlank\n                ? `word ${index + 1}`\n                : isEditable\n                  ? `word ${index + 1}`\n                  : undefined\n            }\n          />\n        )\n      })}\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "~/components/blocks/mnemonic-flow/mnemonic-grid-ui.tsx"
    },
    {
      "path": "registry/new-york/blocks/mnemonic-flow/use-mnemonic-flow.ts",
      "content": "import { useCallback, useEffect, useMemo, useRef, useState } from \"react\"\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Supported operating modes for the mnemonic flow */\nexport type MnemonicFlowMode = \"display\" | \"create\" | \"import\" | \"verify\"\n\n/** Number of words in a mnemonic seed phrase */\nexport type MnemonicWordCount = 12 | 24\n\n/** A verification challenge requiring the user to confirm specific word positions */\nexport interface VerificationChallenge {\n  /** The two positions (0-indexed) the user must fill in */\n  positions: [number, number]\n  /** The user's current answers keyed by position index */\n  answers: Record<number, string>\n}\n\n/** Options for the useMnemonicFlow hook */\nexport interface UseMnemonicFlowOptions {\n  /** Operating mode (default: \"display\") */\n  mode: MnemonicFlowMode\n  /** Pre-populated words for display/verify modes */\n  words?: string[]\n  /** Number of words to generate/import (default: 12) */\n  wordCount?: MnemonicWordCount\n  /** Called when the flow completes with the final word list */\n  onComplete?: (words: string[]) => void\n  /** Called when the user cancels the flow */\n  onCancel?: () => void\n}\n\n/** Return value of the useMnemonicFlow hook */\nexport interface UseMnemonicFlowReturn {\n  /** The current word list */\n  words: string[]\n  /** The operating mode */\n  mode: MnemonicFlowMode\n  /** Expected word count */\n  wordCount: MnemonicWordCount\n  /** Whether the confirmation checkbox is checked (create mode) */\n  confirmed: boolean\n  /** Toggle the confirmation checkbox */\n  setConfirmed: (value: boolean) => void\n  /** Update a single word at a given index (import mode) */\n  setWord: (index: number, value: string) => void\n  /** Replace the full word list */\n  setWords: (words: string[]) => void\n  /** Verification challenge state (verify mode) */\n  challenge: VerificationChallenge | null\n  /** Update a verification answer */\n  setVerificationAnswer: (position: number, value: string) => void\n  /** Whether the current state is valid for submission */\n  isValid: boolean\n  /** Whether the flow can be submitted */\n  canSubmit: boolean\n  /** Submit the flow, invoking onComplete */\n  submit: () => void\n  /** Cancel the flow, invoking onCancel */\n  cancel: () => void\n  /** Copy all words to clipboard */\n  copyWords: () => Promise<void>\n  /** Whether the words were recently copied */\n  copied: boolean\n  /** Error message for the current state */\n  error: string | null\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Pick two random, distinct positions from a word list.\n * Uses Math.random which is acceptable for UI challenges (not cryptographic).\n */\nfunction pickChallengePositions(\n  wordCount: number\n): [number, number] {\n  const first = Math.floor(Math.random() * wordCount)\n  let second = Math.floor(Math.random() * (wordCount - 1))\n  if (second >= first) second += 1\n  const lo = Math.min(first, second)\n  const hi = Math.max(first, second)\n  return [lo, hi]\n}\n\n// ---------------------------------------------------------------------------\n// Hook\n// ---------------------------------------------------------------------------\n\n/**\n * Manages all state and logic for the mnemonic seed phrase flow.\n *\n * Supports four modes:\n * - **display**: Read-only grid with copy-all button\n * - **create**: Shows generated words, requires confirmation checkbox\n * - **import**: Editable grid for entering a seed phrase\n * - **verify**: Shows 2 random word positions the user must fill in correctly\n *\n * The hook owns no UI and imports nothing from the presentation layer.\n *\n * @example\n * ```ts\n * const flow = useMnemonicFlow({\n *   mode: \"create\",\n *   words: generatedWords,\n *   onComplete: (words) => createWallet(words.join(\" \")),\n * })\n * ```\n */\nexport function useMnemonicFlow({\n  mode,\n  words: initialWords,\n  wordCount = 12,\n  onComplete,\n  onCancel,\n}: UseMnemonicFlowOptions): UseMnemonicFlowReturn {\n  const [words, setWords] = useState<string[]>(\n    () => initialWords ?? Array.from<string>({ length: wordCount }).fill(\"\")\n  )\n  const [confirmed, setConfirmed] = useState(false)\n  const [copied, setCopied] = useState(false)\n  const [error, setError] = useState<string | null>(null)\n\n  // Verification challenge — generated once on mount for verify mode\n  const [challenge] = useState<VerificationChallenge | null>(() => {\n    if (mode !== \"verify\") return null\n    const count = initialWords?.length ?? wordCount\n    return {\n      positions: pickChallengePositions(count),\n      answers: {},\n    }\n  })\n\n  const [verificationAnswers, setVerificationAnswers] = useState<\n    Record<number, string>\n  >({})\n\n  // Ref for copy timeout cleanup\n  const copyTimeoutRef = useRef<ReturnType<typeof setTimeout>>(undefined)\n\n  // Clean up pending timeout on unmount\n  useEffect(() => {\n    return () => {\n      if (copyTimeoutRef.current !== undefined) {\n        clearTimeout(copyTimeoutRef.current)\n      }\n    }\n  }, [])\n\n  // Combine challenge with answers\n  const challengeWithAnswers = useMemo<VerificationChallenge | null>(() => {\n    if (!challenge) return null\n    return { ...challenge, answers: verificationAnswers }\n  }, [challenge, verificationAnswers])\n\n  const setWord = useCallback(\n    (index: number, value: string) => {\n      setWords((prev) => {\n        const next = [...prev]\n        next[index] = value.trim().toLowerCase()\n        return next\n      })\n      setError(null)\n    },\n    []\n  )\n\n  const setVerificationAnswer = useCallback(\n    (position: number, value: string) => {\n      setVerificationAnswers((prev) => ({\n        ...prev,\n        [position]: value.trim().toLowerCase(),\n      }))\n      setError(null)\n    },\n    []\n  )\n\n  // Validation\n  const isValid = useMemo(() => {\n    switch (mode) {\n      case \"display\":\n        return words.length > 0 && words.every((w) => w.length > 0)\n\n      case \"create\":\n        return (\n          confirmed &&\n          words.length > 0 &&\n          words.every((w) => w.length > 0)\n        )\n\n      case \"import\":\n        return (\n          words.length === wordCount &&\n          words.every((w) => w.length > 0)\n        )\n\n      case \"verify\": {\n        if (!challenge || !initialWords) return false\n        const [pos1, pos2] = challenge.positions\n        return (\n          verificationAnswers[pos1]?.toLowerCase() ===\n            initialWords[pos1]?.toLowerCase() &&\n          verificationAnswers[pos2]?.toLowerCase() ===\n            initialWords[pos2]?.toLowerCase()\n        )\n      }\n\n      default:\n        return false\n    }\n  }, [mode, words, confirmed, wordCount, challenge, initialWords, verificationAnswers])\n\n  const canSubmit = useMemo(() => {\n    if (mode === \"display\") return false\n    return isValid\n  }, [mode, isValid])\n\n  const submit = useCallback(() => {\n    if (!canSubmit) return\n\n    if (mode === \"verify\") {\n      if (!isValid) {\n        setError(\"Incorrect words. Please check and try again.\")\n        return\n      }\n      onComplete?.(initialWords ?? words)\n      return\n    }\n\n    onComplete?.(words)\n  }, [canSubmit, mode, isValid, words, initialWords, onComplete])\n\n  const cancel = useCallback(() => {\n    onCancel?.()\n  }, [onCancel])\n\n  const copyWords = useCallback(async () => {\n    if (typeof navigator === \"undefined\") return\n    try {\n      await navigator.clipboard.writeText(words.join(\" \"))\n      setCopied(true)\n      if (copyTimeoutRef.current !== undefined) {\n        clearTimeout(copyTimeoutRef.current)\n      }\n      copyTimeoutRef.current = setTimeout(() => setCopied(false), 2000)\n    } catch {\n      setError(\"Failed to copy to clipboard\")\n    }\n  }, [words])\n\n  return {\n    words,\n    mode,\n    wordCount,\n    confirmed,\n    setConfirmed,\n    setWord,\n    setWords,\n    challenge: challengeWithAnswers,\n    setVerificationAnswer,\n    isValid,\n    canSubmit,\n    submit,\n    cancel,\n    copyWords,\n    copied,\n    error,\n  }\n}\n",
      "type": "registry:component",
      "target": "~/components/blocks/mnemonic-flow/use-mnemonic-flow.ts"
    }
  ],
  "categories": [
    "wallet"
  ],
  "type": "registry:block"
}