{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"code","title":"Code","description":"Inline and block code with optional reveal and clipboard actions.","dependencies":["class-variance-authority@0.7.1"],"registryDependencies":["@siteplane/base","@siteplane/action-icons","@siteplane/button","@siteplane/copy-button"],"files":[{"path":"packages/ui/src/components/code/code.tsx","content":"\"use client\";\n\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { useState, type ComponentProps } from \"react\";\n\nimport { cn } from \"../../system/utils/cn\";\nimport { slotClass } from \"../../system/utils/slot-class\";\nimport { RevealIcon } from \"../action-icons\";\nimport { Button } from \"../button\";\nimport { CopyButton } from \"../copy-button\";\n\n// Inline code wraps a long command or an unbroken value between any two\n// characters with the text around it, so it never widens its line (rulebook\n// K5).\nconst codeInlineClassName =\n  \"rounded-(--code-inline-radius) bg-muted px-1.5 py-0.5 font-mono [font-size:var(--code-inline-font-size)] text-foreground wrap-anywhere\";\nconst codeBlockBaseClassName =\n  \"relative w-full overflow-x-auto rounded-lg bg-muted/50 font-mono text-foreground leading-relaxed\";\n// The md size names its line height with its font size: a bare text-xs\n// replaced the block's leading-relaxed, so sm had taller lines than md.\nconst codeBlockSizeClassNames = {\n  md: \"px-3.5 py-3 text-xs/relaxed\",\n  sm: \"px-3 py-2 [font-size:var(--code-block-sm-font-size)]\",\n} as const;\n\nexport function Code({ className, ...props }: ComponentProps<\"code\">) {\n  return (\n    <code\n      className={cn(slotClass(\"code\", props), codeInlineClassName, className)}\n      data-slot=\"code\"\n      {...props}\n    />\n  );\n}\n\nconst codeBlockVariants = cva(codeBlockBaseClassName, {\n  defaultVariants: { size: \"md\" },\n  variants: { size: codeBlockSizeClassNames },\n});\n\n/** The words a CodeBlock speaks to assistive technology. */\nexport type CodeBlockLabels = {\n  /** Read in place of a masked value. */\n  hidden: string;\n  /** The reveal button while the value is masked. */\n  reveal: string;\n  /** The same button while the value shows. */\n  hide: string;\n  /** The copy button. */\n  copy: string;\n  /** The copy button while its check confirms a copy. */\n  copied: string;\n  /** The copy button and its announcement when the clipboard refuses. */\n  copyFailed: string;\n};\n\nconst defaultCodeBlockLabels: CodeBlockLabels = {\n  hidden: \"Hidden value\",\n  reveal: \"Reveal value\",\n  hide: \"Hide value\",\n  copy: \"Copy to clipboard\",\n  copied: \"Copied to clipboard\",\n  copyFailed: \"Copy unavailable\",\n};\n\nexport interface CodeBlockProps\n  extends\n    Omit<ComponentProps<\"pre\">, \"children\">,\n    VariantProps<typeof codeBlockVariants> {\n  value: string;\n  copyable?: boolean;\n  /** Replaces the English names of the hidden value and the actions. */\n  labels?: Partial<CodeBlockLabels> | undefined;\n  mask?: boolean;\n}\n\nexport function CodeBlock({\n  value,\n  copyable = false,\n  labels,\n  mask = false,\n  size,\n  className,\n  ...props\n}: CodeBlockProps) {\n  const text = { ...defaultCodeBlockLabels, ...labels };\n  // The person's reveal, which a change of `mask` takes back: a block that is\n  // masked after it loaded is hidden again, not left in clear text.\n  const [revealedByPerson, setRevealedByPerson] = useState(false);\n  const [maskedBefore, setMaskedBefore] = useState(mask);\n  if (maskedBefore !== mask) {\n    setMaskedBefore(mask);\n    setRevealedByPerson(false);\n  }\n  const revealed = !mask || revealedByPerson;\n  const masked = mask && !revealed;\n  const hasActions = copyable || mask;\n  // The 24px field actions keep their glyph as far from the top edge as from\n  // the end edge; the code text stops just before them.\n  const isSmall = size === \"sm\";\n  const actionClassName = cn(\n    \"absolute flex gap-1\",\n    isSmall ? \"end-1\" : \"end-2\",\n    mask ? \"top-1/2 -translate-y-1/2\" : isSmall ? \"top-1\" : \"top-2\",\n  );\n  return (\n    <pre\n      className={cn(\n        slotClass(\"code-block\", props),\n        codeBlockVariants({ size }),\n        hasActions && (copyable && mask ? \"pe-18\" : \"pe-11\"),\n        className,\n      )}\n      data-slot=\"code-block\"\n      {...props}\n    >\n      {/* anywhere, not break-word: a long value also stops counting as one\n          unbreakable width, so it never widens the block's grid or flex\n          parent (rulebook K5). */}\n      <code className=\"block whitespace-pre-wrap wrap-anywhere\">\n        {masked ? (\n          <>\n            {/* The bullets only show that a value is hidden; a screen reader\n                hears one short text instead of dozens of \"bullet\". */}\n            <span aria-hidden=\"true\">\n              {\"•\".repeat(Math.min(value.length, 44))}\n            </span>\n            <span className=\"sr-only\">{text.hidden}</span>\n          </>\n        ) : (\n          value\n        )}\n      </code>\n      {hasActions ? (\n        <span className={actionClassName}>\n          {mask ? (\n            <Button\n              aria-label={revealed ? text.hide : text.reveal}\n              onClick={() => setRevealedByPerson((current) => !current)}\n              profile=\"field-action\"\n              shape=\"square\"\n              type=\"button\"\n            >\n              <RevealIcon revealed={revealed} />\n            </Button>\n          ) : null}\n          {/* The kit's copy action: its check after a copy, a cross for a\n              moment when the clipboard is missing or refuses, its tooltip\n              and the announcement of either. */}\n          {copyable ? (\n            <CopyButton\n              labels={{\n                copied: text.copied,\n                copy: text.copy,\n                copyFailed: text.copyFailed,\n              }}\n              profile=\"field-action\"\n              shape=\"square\"\n              value={value}\n            />\n          ) : null}\n        </span>\n      ) : null}\n    </pre>\n  );\n}\n","type":"registry:ui","target":"~/src/siteplane/components/code/code.tsx"},{"path":"packages/ui/src/components/code/index.ts","content":"export * from \"./code\";\n","type":"registry:ui","target":"~/src/siteplane/components/code/index.ts"}],"type":"registry:ui","docs":"https://ui.siteplane.io/docs-markdown/components/code.md","categories":["Layout & utilities"],"meta":{"avoidWhen":["Avoid for syntax-highlighted listings; Code does not highlight.","Avoid masking as protection for a secret: a masked value still reaches the browser."],"description":"Inline and block code with optional reveal and clipboard actions.","knownLimitations":["Copying depends on browser permissions and a secure context; a failed copy is shown on the button.","A reveal by the person survives a new value and resets when mask changes."],"providerNotes":[],"releaseStatus":"stable","searchTerms":["code"],"useWhen":["Use to show inline code, or a block of text that people can copy and that can stay masked until revealed."]}}