{
  "name": "inscriptions-components-inscriptionbutton",
  "type": "registry:component",
  "dependencies": [
    "@radix-ui/react-icons",
    "js-1sat-ord"
  ],
  "devDependencies": [],
  "registryDependencies": [],
  "files": [
    {
      "path": "components/inscriptions/components/InscriptionButton.tsx",
      "type": "registry:component",
      "content": "/**\n * InscriptionButton Component\n *\n * Base component for all inscription types\n * Provides consistent UI/UX and shared functionality\n */\n\nimport { CheckCircledIcon, InfoCircledIcon } from \"@radix-ui/react-icons\";\nimport type { PreMAP } from \"js-1sat-ord\";\nimport type React from \"react\";\nimport { useCallback, useMemo, useState } from \"react\";\nimport { ErrorDisplay } from \"../../ui-components/ErrorDisplay.js\";\nimport { LoadingButton } from \"../../ui-components/LoadingButton.js\";\nimport {\n\tTransactionProgress,\n\tTransactionStatus,\n} from \"../../ui-components/TransactionProgress.js\";\nimport { Badge } from \"../../ui/badge.js\";\nimport { Button } from \"../../ui/button.js\";\nimport {\n\tDialog,\n\tDialogContent,\n\tDialogHeader,\n\tDialogTitle,\n} from \"../../ui/dialog.js\";\nimport { useInscription } from \"../hooks/useInscription.js\";\n\nexport interface InscriptionButtonProps {\n\t/** Button text */\n\tbuttonText?: string;\n\t/** Dialog title */\n\tdialogTitle?: string;\n\t/** Dialog description */\n\tdialogDescription?: string;\n\t/** Button variant */\n\tvariant?: \"solid\" | \"soft\" | \"outline\" | \"ghost\";\n\t/** Button size */\n\tsize?: \"1\" | \"2\" | \"3\" | \"4\";\n\t/** Button color */\n\tcolor?: \"blue\" | \"green\" | \"red\" | \"gray\";\n\t/** Additional CSS classes */\n\tclassName?: string;\n\t/** Whether button is disabled */\n\tdisabled?: boolean;\n\t/** Loading state override */\n\tloading?: boolean;\n\t/** Show fee estimate */\n\tshowFeeEstimate?: boolean;\n\t/** Show transaction details after success */\n\tshowTransactionDetails?: boolean;\n\t/** Custom icon */\n\ticon?: React.ReactNode;\n\t/** Success callback */\n\tonSuccess?: (txid: string) => void;\n\t/** Error callback */\n\tonError?: (error: Error) => void;\n\t/** Close callback */\n\tonClose?: () => void;\n\t/** Content to inscribe (for simple inscriptions) */\n\tcontent?: string | ArrayBuffer;\n\t/** Content type (for simple inscriptions) */\n\tcontentType?: string;\n\t/** Metadata to attach */\n\tmetadata?: PreMAP;\n\t/** Number of iterations (for bulk inscriptions) */\n\titerations?: number;\n\t/** Identity key for signing */\n\tidentityKey?: string;\n\t/** Children (for custom dialog content) */\n\tchildren?: React.ReactNode;\n}\n\nexport function InscriptionButton({\n\tbuttonText = \"Create Inscription\",\n\tdialogTitle = \"Create Inscription\",\n\tdialogDescription,\n\tvariant = \"solid\",\n\tsize = \"2\",\n\tcolor = \"blue\",\n\tclassName = \"\",\n\tdisabled = false,\n\tloading = false,\n\tshowFeeEstimate = true,\n\tshowTransactionDetails = true,\n\ticon,\n\tonSuccess,\n\tonError,\n\tonClose,\n\tcontent,\n\tcontentType,\n\tmetadata,\n\titerations = 1,\n\tidentityKey,\n\tchildren,\n}: InscriptionButtonProps) {\n\tconst [open, setOpen] = useState(false);\n\tconst [step, setStep] = useState<\n\t\t\"configure\" | \"confirm\" | \"processing\" | \"success\" | \"error\"\n\t>(\"configure\");\n\n\tconst {\n\t\tinscribe,\n\t\testimateFee,\n\t\tloading: inscribing,\n\t\terror,\n\t\tresult,\n\t\tfeeEstimate,\n\t\treset,\n\t} = useInscription({\n\t\tonSuccess: (result) => {\n\t\t\tsetStep(\"success\");\n\t\t\tonSuccess?.(result.txid);\n\t\t},\n\t\tonError: (err) => {\n\t\t\tsetStep(\"error\");\n\t\t\tonError?.(err);\n\t\t},\n\t\tonProgress: (_status) => {\n\t\t\t// Could show progress updates\n\t\t},\n\t});\n\n\t// Handle dialog open\n\tconst handleOpen = useCallback(async () => {\n\t\tsetOpen(true);\n\t\treset();\n\t\tsetStep(\"configure\");\n\n\t\t// Estimate fee if content is provided\n\t\tif (content && contentType && showFeeEstimate) {\n\t\t\ttry {\n\t\t\t\tawait estimateFee(content, contentType, iterations);\n\t\t\t} catch (err) {\n\t\t\t\t// Fee estimation error is not critical\n\t\t\t\tconsole.error(\"Fee estimation failed:\", err);\n\t\t\t}\n\t\t}\n\t}, [content, contentType, iterations, showFeeEstimate, estimateFee, reset]);\n\n\t// Handle inscription\n\tconst handleInscribe = useCallback(async () => {\n\t\tif (!content || !contentType) {\n\t\t\tconsole.error(\"Content and contentType are required\");\n\t\t\treturn;\n\t\t}\n\n\t\tsetStep(\"processing\");\n\n\t\ttry {\n\t\t\tawait inscribe({\n\t\t\t\tcontent,\n\t\t\t\tcontentType,\n\t\t\t\tmetadata,\n\t\t\t\titerations,\n\t\t\t\tidentityKey,\n\t\t\t});\n\t\t} catch (_err) {\n\t\t\t// Error is handled by the hook\n\t\t}\n\t}, [content, contentType, metadata, iterations, identityKey, inscribe]);\n\n\t// Handle close\n\tconst handleClose = useCallback(() => {\n\t\tsetOpen(false);\n\t\tonClose?.();\n\t\t// Reset after animation\n\t\tsetTimeout(() => {\n\t\t\treset();\n\t\t\tsetStep(\"configure\");\n\t\t}, 200);\n\t}, [onClose, reset]);\n\n\t// Format fee for display\n\tconst formattedFee = useMemo(() => {\n\t\tif (!feeEstimate) return null;\n\t\treturn (feeEstimate / 100_000_000).toFixed(8);\n\t}, [feeEstimate]);\n\n\t// Render dialog content based on step\n\tconst renderContent = () => {\n\t\tswitch (step) {\n\t\t\tcase \"configure\":\n\t\t\t\treturn (\n\t\t\t\t\t<>\n\t\t\t\t\t\t{children || (\n\t\t\t\t\t\t\t<div className=\"flex flex-col gap-3\">\n\t\t\t\t\t\t\t\t{dialogDescription && (\n\t\t\t\t\t\t\t\t\t<p className=\"text-sm text-muted-foreground\">\n\t\t\t\t\t\t\t\t\t\t{dialogDescription}\n\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t\t\t{content && contentType && (\n\t\t\t\t\t\t\t\t\t<div className=\"flex flex-col gap-2\">\n\t\t\t\t\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\t\t\t\t<span className=\"text-sm font-medium\">Content Type:</span>\n\t\t\t\t\t\t\t\t\t\t\t<Badge>{contentType}</Badge>\n\t\t\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t\t\t{iterations > 1 && (\n\t\t\t\t\t\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"text-sm font-medium\">Iterations:</span>\n\t\t\t\t\t\t\t\t\t\t\t\t<Badge>{iterations}</Badge>\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t\t\t\t\t{showFeeEstimate && feeEstimate && (\n\t\t\t\t\t\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"text-sm font-medium\">\n\t\t\t\t\t\t\t\t\t\t\t\t\tEstimated Fee:\n\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t<Badge color=\"purple\">{formattedFee} BSV</Badge>\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</>\n\t\t\t\t);\n\n\t\t\tcase \"confirm\":\n\t\t\t\treturn (\n\t\t\t\t\t<div className=\"flex flex-col gap-3\">\n\t\t\t\t\t\t<span className=\"text-sm text-muted-foreground\">\n\t\t\t\t\t\t\tPlease confirm the inscription details:\n\t\t\t\t\t\t</span>\n\n\t\t\t\t\t\t<div className=\"flex flex-col gap-2\">\n\t\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\t<span className=\"text-sm font-medium\">Content Type:</span>\n\t\t\t\t\t\t\t\t<Badge>{contentType}</Badge>\n\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t{iterations > 1 && (\n\t\t\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\t\t<span className=\"text-sm font-medium\">Iterations:</span>\n\t\t\t\t\t\t\t\t\t<Badge>{iterations}</Badge>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t\t{feeEstimate && (\n\t\t\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\t\t<span className=\"text-sm font-medium\">Estimated Fee:</span>\n\t\t\t\t\t\t\t\t\t<Badge color=\"purple\">{formattedFee} BSV</Badge>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<div className=\"flex items-center gap-2 mt-2\">\n\t\t\t\t\t\t\t<InfoCircledIcon />\n\t\t\t\t\t\t\t<span className=\"text-xs text-muted-foreground\">\n\t\t\t\t\t\t\t\tThis will create a permanent inscription on the blockchain\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t);\n\n\t\t\tcase \"processing\":\n\t\t\t\treturn (\n\t\t\t\t\t<div className=\"flex flex-col gap-3 items-center py-4\">\n\t\t\t\t\t\t<div className=\"animate-spin h-8 w-8 border-4 border-primary border-t-transparent rounded-full\" />\n\t\t\t\t\t\t<span className=\"text-sm text-muted-foreground\">\n\t\t\t\t\t\t\tCreating inscription...\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t);\n\n\t\t\tcase \"success\":\n\t\t\t\treturn (\n\t\t\t\t\t<div className=\"flex flex-col gap-3\">\n\t\t\t\t\t\t<div className=\"flex items-center gap-2 text-bigblocks-success\">\n\t\t\t\t\t\t\t<CheckCircledIcon width=\"20\" height=\"20\" />\n\t\t\t\t\t\t\t<span className=\"text-base font-medium\">\n\t\t\t\t\t\t\t\tInscription Created Successfully!\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t{result && showTransactionDetails && (\n\t\t\t\t\t\t\t<TransactionProgress\n\t\t\t\t\t\t\t\tstatus={TransactionStatus.Confirmed}\n\t\t\t\t\t\t\t\ttxid={result.txid}\n\t\t\t\t\t\t\t\tshowDetails={true}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t);\n\n\t\t\tcase \"error\":\n\t\t\t\treturn (\n\t\t\t\t\t<ErrorDisplay\n\t\t\t\t\t\terror={error?.message || \"Unknown error\"}\n\t\t\t\t\t\tsize=\"default\"\n\t\t\t\t\t/>\n\t\t\t\t);\n\n\t\t\tdefault:\n\t\t\t\treturn null;\n\t\t}\n\t};\n\n\t// Determine if action button should be shown\n\tconst showActionButton = step === \"configure\" || step === \"confirm\";\n\tconst actionButtonText =\n\t\tstep === \"confirm\" ? \"Confirm Inscription\" : \"Continue\";\n\tconst isProcessing = step === \"processing\" || inscribing;\n\n\treturn (\n\t\t<>\n\t\t\t<Button\n\t\t\t\tvariant={\n\t\t\t\t\tvariant === \"solid\"\n\t\t\t\t\t\t? \"default\"\n\t\t\t\t\t\t: variant === \"soft\"\n\t\t\t\t\t\t\t? \"secondary\"\n\t\t\t\t\t\t\t: variant\n\t\t\t\t}\n\t\t\t\tsize={size === \"1\" ? \"sm\" : size === \"2\" ? \"default\" : \"lg\"}\n\t\t\t\tclassName={className}\n\t\t\t\tdisabled={disabled || loading || inscribing}\n\t\t\t\tonClick={handleOpen}\n\t\t\t>\n\t\t\t\t{loading || inscribing ? (\n\t\t\t\t\t<div className=\"animate-spin h-4 w-4 border-2 border-current border-t-transparent rounded-full\" />\n\t\t\t\t) : (\n\t\t\t\t\ticon\n\t\t\t\t)}\n\t\t\t\t{buttonText}\n\t\t\t</Button>\n\n\t\t\t<Dialog open={open} onOpenChange={setOpen}>\n\t\t\t\t<DialogContent className=\"max-w-[500px]\">\n\t\t\t\t\t<DialogHeader>\n\t\t\t\t\t\t<DialogTitle>{dialogTitle}</DialogTitle>\n\t\t\t\t\t</DialogHeader>\n\n\t\t\t\t\t{renderContent()}\n\n\t\t\t\t\t<div className=\"flex gap-3 mt-4 justify-end\">\n\t\t\t\t\t\t{step !== \"processing\" && (\n\t\t\t\t\t\t\t<Button variant=\"outline\" onClick={handleClose}>\n\t\t\t\t\t\t\t\t{step === \"success\" ? \"Close\" : \"Cancel\"}\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{showActionButton && (\n\t\t\t\t\t\t\t<LoadingButton\n\t\t\t\t\t\t\t\tvariant=\"default\"\n\t\t\t\t\t\t\t\tloading={isProcessing}\n\t\t\t\t\t\t\t\tdisabled={!content || !contentType}\n\t\t\t\t\t\t\t\tonClick={\n\t\t\t\t\t\t\t\t\tstep === \"configure\"\n\t\t\t\t\t\t\t\t\t\t? () => setStep(\"confirm\")\n\t\t\t\t\t\t\t\t\t\t: handleInscribe\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{actionButtonText}\n\t\t\t\t\t\t\t</LoadingButton>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t</DialogContent>\n\t\t\t</Dialog>\n\t\t</>\n\t);\n}\n",
      "target": "<%- config.aliases.components %>/inscriptionbutton.tsx"
    }
  ]
}