{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "lightboard",
  "type": "registry:ui",
  "description": "Interactive lightboard component with customizable grid and lighting effects",
  "files": [
    {
      "path": "registry/default/ui/lightboard.tsx",
      "content": "\"use client\"\n\nimport React, { useCallback, useEffect, useRef, useState } from \"react\"\n\nexport type PatternCell = \"0\" | \"1\" | \"2\" | \"3\"\ntype Pattern = PatternCell[][]\n\ninterface LightBoardProps {\n  gap?: number\n  rows?: number\n  lightSize?: number\n  updateInterval?: number\n  text: string\n  font?: \"default\" | \"7segment\"\n  colors?: Partial<LightBoardColors>\n  disableDrawing?: boolean\n  controlledDrawState?: PatternCell\n  onDrawStateChange?: (newState: PatternCell) => void\n  controlledHoverState?: boolean\n  onHoverStateChange?: (isHovered: boolean) => void\n}\n\ninterface LightBoardColors {\n  drawLine: string // Color for moderately lit text\n  background: string // Color for inactive lights\n  textDim: string // Color for dimly lit text\n  textBright: string // Color for brightly lit text\n}\n\nconst defaultColors: LightBoardColors = {\n  drawLine: \"rgba(160, 160, 200, 0.7)\",\n  background: \"rgba(30, 30, 40, 0.3)\",\n  textDim: \"rgba(100, 100, 140, 0.5)\",\n  textBright: \"rgba(220, 220, 255, 0.9)\",\n}\n\n// This function takes some text and makes sure there's enough space between words\nconst normalizeText = (text: string, minSpacing: number = 3): string => {\n  const trimmed = text.trim().toUpperCase() // Remove extra spaces and make all letters big\n  const spacedText = ` ${trimmed} `.replace(/\\s+/g, \" \".repeat(minSpacing)) // Add spaces between words\n  return spacedText\n}\n\n// This function turns text into a pattern of lights\nconst textToPattern = (\n  text: string,\n  rows: number,\n  columns: number,\n  font: { [key: string]: Pattern }\n): Pattern => {\n  // First, we make the letters bigger if we have more rows\n  const letterHeight = font[\"A\"].length\n  const scale = Math.max(1, Math.floor(rows / letterHeight))\n\n  // We make each letter in the font bigger\n  const scaledFont = Object.fromEntries(\n    Object.entries(font).map(([char, pattern]) => [\n      char,\n      pattern\n        .flatMap((row) => Array(scale).fill(row))\n        .map((row) =>\n          row.flatMap((cell: PatternCell) =>\n            Array(scale).fill(cell === \"1\" ? \"1\" : \"3\")\n          )\n        ),\n    ])\n  )\n  // We add spaces to the text\n  const normalizedText = normalizeText(text)\n\n  // We turn each letter into a pattern of lights\n  const letterPatterns = normalizedText\n    .split(\"\")\n    .map((char) => scaledFont[char] || scaledFont[\" \"])\n\n  // We combine all the letter patterns into one big pattern\n  let fullPattern: Pattern = Array(scaledFont[\"A\"].length)\n    .fill([])\n    .map(() => [])\n\n  letterPatterns.forEach((letterPattern) => {\n    fullPattern = fullPattern.map((row, i) => [...row, ...letterPattern[i]])\n  })\n\n  // We add empty space above and below the pattern to center it\n  const totalRows = rows\n  const patternRows = fullPattern.length\n  const topPadding = Math.floor((totalRows - patternRows) / 2)\n  const bottomPadding = totalRows - patternRows - topPadding\n\n  const paddedPattern = [\n    ...Array(topPadding).fill(Array(fullPattern[0].length).fill(\"0\")),\n    ...fullPattern,\n    ...Array(bottomPadding).fill(Array(fullPattern[0].length).fill(\"0\")),\n  ]\n\n  // We make the pattern wider by repeating it\n  const extendedPattern = paddedPattern.map((row) => {\n    while (row.length < columns * 2) {\n      row = [...row, ...row]\n    }\n    return row\n  })\n\n  return extendedPattern\n}\n\n// This function decides what color each light should be\nfunction getLightColor(\n  state: PatternCell,\n  colors: Partial<LightBoardColors>\n): string {\n  const mergedColors = { ...defaultColors, ...colors }\n\n  switch (state) {\n    case \"1\":\n      return mergedColors.textDim\n    case \"2\":\n      return mergedColors.drawLine\n    case \"3\":\n      return mergedColors.textBright\n    default:\n      return mergedColors.background\n  }\n}\n\nconst defaultDrawState: PatternCell = \"2\"\n\nfunction LightBoard({\n  text,\n  gap = 1,\n  lightSize = 4,\n  rows = 5,\n  font = \"default\",\n  updateInterval = 10,\n  colors = {},\n  controlledDrawState,\n  disableDrawing = true,\n  controlledHoverState,\n  onHoverStateChange,\n}: LightBoardProps) {\n  // We decide how many rows and columns of lights we need\n  const containerRef = useRef<HTMLDivElement>(null)\n  const [columns, setColumns] = useState(0)\n  const mergedColors = { ...defaultColors, ...colors }\n\n  // We choose which font to use for our text\n  const selectedFont = font === \"default\" ? defaultFont : sevenSegmentFont\n  // We keep track of whether the mouse is over our board\n\n  // We keep track of whether we're drawing on the board\n  const [isDrawing, setIsDrawing] = useState(false)\n  // Use controlled state if provided, otherwise use local state\n  const [internalHoverState, setInternalHoverState] = useState(false)\n  // This is the brightness of the lights we're drawing (0 to 3)\n\n  // This is our pattern of lights that make up the text\n  const [basePattern, setBasePattern] = useState<Pattern>(() => {\n    return textToPattern(normalizeText(text), rows, columns, selectedFont)\n  })\n  // This helps us move the text across the board\n  const [offset, setOffset] = useState(0)\n  // This is how we draw on our light board (it's like a special piece of paper)\n  const canvasRef = useRef<HTMLCanvasElement>(null)\n  // This remembers where we last drew on the board\n  const lastDrawnPosition = useRef<{ x: number; y: number } | null>(null)\n  // This helps us know when to update our animation\n  const [lastUpdateTime, setLastUpdateTime] = useState(0)\n\n  const drawState =\n    controlledDrawState !== undefined ? controlledDrawState : defaultDrawState\n\n  const isHovered =\n    controlledHoverState !== undefined\n      ? controlledHoverState\n      : internalHoverState\n\n  // Calculate the number of columns based on container width\n  useEffect(() => {\n    const calculateColumns = () => {\n      if (containerRef.current) {\n        const containerWidth = containerRef.current.offsetWidth\n        const calculatedColumns = Math.floor(containerWidth / (lightSize + gap))\n        setColumns(calculatedColumns)\n      }\n    }\n\n    calculateColumns()\n    window.addEventListener(\"resize\", calculateColumns)\n    return () => window.removeEventListener(\"resize\", calculateColumns)\n  }, [lightSize, gap])\n\n  // This function draws all our lights on the board\n  const drawToCanvas = useCallback(() => {\n    const canvas = canvasRef.current\n    if (!canvas) return\n\n    const ctx = canvas.getContext(\"2d\")\n    if (!ctx) return\n\n    ctx.clearRect(0, 0, canvas.width, canvas.height)\n\n    const patternWidth = basePattern[0].length\n\n    basePattern.forEach((row, rowIndex) => {\n      for (let colIndex = 0; colIndex < columns; colIndex++) {\n        const patternColIndex = (colIndex + offset) % patternWidth\n        const state = row[patternColIndex]\n\n        ctx.fillStyle = getLightColor(state as PatternCell, mergedColors)\n        ctx.beginPath()\n        ctx.arc(\n          colIndex * (lightSize + gap) + lightSize / 2,\n          rowIndex * (lightSize + gap) + lightSize / 2,\n          lightSize / 2,\n          0,\n          2 * Math.PI\n        )\n        ctx.fill()\n      }\n    })\n  }, [basePattern, offset, columns, lightSize, gap, mergedColors])\n\n  // This makes our text move across the board\n  useEffect(() => {\n    let animationFrameId: number\n\n    const animate = () => {\n      if (!isHovered) {\n        // If the mouse isn't over the board, we move the text\n        setOffset((prevOffset) => (prevOffset + 1) % basePattern[0].length)\n      }\n      drawToCanvas()\n      animationFrameId = requestAnimationFrame(animate)\n    }\n\n    animationFrameId = requestAnimationFrame(animate)\n\n    // We clean up our animation when we're done\n    return () => cancelAnimationFrame(animationFrameId)\n  }, [basePattern, isHovered, drawToCanvas])\n\n  // This updates our light pattern when the text changes\n  useEffect(() => {\n    setBasePattern(\n      textToPattern(normalizeText(text), rows, columns, selectedFont)\n    )\n  }, [text, rows, columns, selectedFont])\n\n  // This is another way we make our text move\n  const animate = useCallback(() => {\n    const currentTime = Date.now()\n    if (currentTime - lastUpdateTime >= updateInterval && !isHovered) {\n      setOffset((prevOffset) => (prevOffset + 1) % basePattern[0].length)\n      setLastUpdateTime(currentTime)\n    }\n    drawToCanvas()\n  }, [updateInterval, isHovered, basePattern, drawToCanvas, lastUpdateTime])\n\n  // This keeps our animation going\n  useEffect(() => {\n    let animationFrameId: number\n\n    const loop = () => {\n      animate()\n      animationFrameId = requestAnimationFrame(loop)\n    }\n\n    animationFrameId = requestAnimationFrame(loop)\n\n    // We clean up our animation when we're done\n    return () => cancelAnimationFrame(animationFrameId)\n  }, [animate])\n\n  // This function helps us draw a line on our light board\n  const drawLine = useCallback(\n    (startX: number, startY: number, endX: number, endY: number) => {\n      const canvas = canvasRef.current\n      if (!canvas) return\n\n      const ctx = canvas.getContext(\"2d\")\n      if (!ctx) return\n\n      // We figure out which direction we're drawing\n      const dx = Math.abs(endX - startX)\n      const dy = Math.abs(endY - startY)\n      const sx = startX < endX ? 1 : -1\n      const sy = startY < endY ? 1 : -1\n      let err = dx - dy\n\n      // We keep drawing until we reach the end of our line\n      while (true) {\n        // We figure out which light we're on\n        const colIndex = Math.floor(startX / (lightSize + gap))\n        const rowIndex = Math.floor(startY / (lightSize + gap))\n\n        // If we're still on the board...\n        if (\n          rowIndex >= 0 &&\n          rowIndex < rows &&\n          colIndex >= 0 &&\n          colIndex < columns\n        ) {\n          // We figure out which light to change in our pattern\n          const actualColIndex = (colIndex + offset) % basePattern[0].length\n\n          // If this light isn't already the brightness we want...\n          if (basePattern[rowIndex][actualColIndex] !== drawState) {\n            // We update our pattern of lights\n            setBasePattern((prevPattern) => {\n              const newPattern = [...prevPattern]\n              newPattern[rowIndex] = [...newPattern[rowIndex]]\n              newPattern[rowIndex][actualColIndex] = drawState\n              return newPattern\n            })\n\n            // We draw the new light on our board\n            ctx.fillStyle = getLightColor(drawState, mergedColors)\n\n            ctx.beginPath()\n            ctx.arc(\n              colIndex * (lightSize + gap) + lightSize / 2,\n              rowIndex * (lightSize + gap) + lightSize / 2,\n              lightSize / 2,\n              0,\n              2 * Math.PI\n            )\n            ctx.fill()\n          }\n        }\n\n        // If we've reached the end of our line, we stop\n        if (startX === endX && startY === endY) break\n\n        // We figure out where to draw next\n        const e2 = 2 * err\n        if (e2 > -dy) {\n          err -= dy\n          startX += sx\n        }\n        if (e2 < dx) {\n          err += dx\n          startY += sy\n        }\n      }\n    },\n    [\n      basePattern,\n      columns,\n      drawState,\n      gap,\n      lightSize,\n      offset,\n      rows,\n      mergedColors,\n    ]\n  )\n\n  // _________DRAWING HANDLING_________\n\n  const handleInteractionStart = useCallback(\n    (x: number, y: number) => {\n      if (disableDrawing) return\n      setIsDrawing(true)\n      lastDrawnPosition.current = null\n      drawLine(x, y, x, y)\n    },\n    [disableDrawing, drawLine]\n  )\n\n  const handleInteractionMove = useCallback(\n    (x: number, y: number) => {\n      if (!isDrawing || disableDrawing) return\n      if (lastDrawnPosition.current) {\n        drawLine(lastDrawnPosition.current.x, lastDrawnPosition.current.y, x, y)\n      } else {\n        drawLine(x, y, x, y)\n      }\n      lastDrawnPosition.current = { x, y }\n    },\n    [isDrawing, disableDrawing, drawLine]\n  )\n\n  const handleInteractionEnd = useCallback(() => {\n    setIsDrawing(false)\n    lastDrawnPosition.current = null\n  }, [])\n\n  // This happens when we press the mouse button to start drawing\n  const handleMouseDown = useCallback(\n    (event: React.MouseEvent<HTMLCanvasElement>) => {\n      const canvas = event.currentTarget\n      const rect = canvas.getBoundingClientRect()\n      const x = event.clientX - rect.left\n      const y = event.clientY - rect.top\n      handleInteractionStart(x, y)\n    },\n    [handleInteractionStart]\n  )\n\n  const handleMouseMove = useCallback(\n    (event: React.MouseEvent<HTMLCanvasElement>) => {\n      const canvas = event.currentTarget\n      const rect = canvas.getBoundingClientRect()\n      const x = event.clientX - rect.left\n      const y = event.clientY - rect.top\n      handleInteractionMove(x, y)\n    },\n    [handleInteractionMove]\n  )\n\n  const handleMouseUp = handleInteractionEnd\n\n  const handleTouchStart = useCallback(\n    (event: React.TouchEvent<HTMLCanvasElement>) => {\n      event.preventDefault()\n      const touch = event.touches[0]\n      const canvas = event.currentTarget\n      const rect = canvas.getBoundingClientRect()\n      const x = touch.clientX - rect.left\n      const y = touch.clientY - rect.top\n      handleInteractionStart(x, y)\n    },\n    [handleInteractionStart]\n  )\n\n  const handleTouchMove = useCallback(\n    (event: React.TouchEvent<HTMLCanvasElement>) => {\n      event.preventDefault()\n      const touch = event.touches[0]\n      const canvas = event.currentTarget\n      const rect = canvas.getBoundingClientRect()\n      const x = touch.clientX - rect.left\n      const y = touch.clientY - rect.top\n      handleInteractionMove(x, y)\n    },\n    [handleInteractionMove]\n  )\n\n  const handleTouchEnd = handleInteractionEnd\n\n  // Update hover state\n  const updateHoverState = useCallback(\n    (newState: boolean) => {\n      if (controlledHoverState === undefined) {\n        setInternalHoverState(newState)\n      }\n      onHoverStateChange?.(newState)\n    },\n    [controlledHoverState, onHoverStateChange]\n  )\n\n  return (\n    <div ref={containerRef} style={{ width: \"100%\" }}>\n      {columns > 0 && (\n        <canvas\n          ref={canvasRef}\n          width={columns * (lightSize + gap)}\n          height={rows * (lightSize + gap)}\n          onMouseDown={!disableDrawing ? handleMouseDown : undefined}\n          onMouseUp={handleMouseUp}\n          onMouseMove={handleMouseMove}\n          onMouseEnter={() =>\n            controlledHoverState === undefined && updateHoverState(true)\n          }\n          onMouseLeave={() => {\n            controlledHoverState === undefined && updateHoverState(false)\n            handleInteractionEnd()\n          }}\n          onTouchStart={!disableDrawing ? handleTouchStart : undefined}\n          onTouchEnd={handleTouchEnd}\n          onTouchMove={handleTouchMove}\n          style={{\n            cursor: disableDrawing ? \"default\" : \"pointer\",\n            touchAction: \"none\",\n            userSelect: \"none\",\n          }}\n        />\n      )}\n    </div>\n  )\n}\n\nexport { LightBoard }\n\nconst sevenSegmentFont: { [key: string]: Pattern } = {\n  \"0\": [\n    [\"1\", \"1\", \"1\"],\n    [\"1\", \"0\", \"1\"],\n    [\"1\", \"0\", \"1\"],\n    [\"1\", \"0\", \"1\"],\n    [\"1\", \"1\", \"1\"],\n  ],\n  \"1\": [\n    [\"0\", \"0\", \"1\"],\n    [\"0\", \"0\", \"1\"],\n    [\"0\", \"0\", \"1\"],\n    [\"0\", \"0\", \"1\"],\n    [\"0\", \"0\", \"1\"],\n  ],\n  // Add more digits as needed\n}\n\nconst defaultFont: { [key: string]: Pattern } = {\n  \" \": [\n    [\"0\", \"0\", \"0\", \"0\"],\n    [\"0\", \"0\", \"0\", \"0\"],\n    [\"0\", \"0\", \"0\", \"0\"],\n    [\"0\", \"0\", \"0\", \"0\"],\n    [\"0\", \"0\", \"0\", \"0\"],\n  ],\n  A: [\n    [\"0\", \"1\", \"1\", \"0\"],\n    [\"1\", \"0\", \"0\", \"1\"],\n    [\"1\", \"1\", \"1\", \"1\"],\n    [\"1\", \"0\", \"0\", \"1\"],\n    [\"1\", \"0\", \"0\", \"1\"],\n  ],\n  B: [\n    [\"1\", \"1\", \"1\", \"0\"],\n    [\"1\", \"0\", \"0\", \"1\"],\n    [\"1\", \"1\", \"1\", \"0\"],\n    [\"1\", \"0\", \"0\", \"1\"],\n    [\"1\", \"1\", \"1\", \"0\"],\n  ],\n  C: [\n    [\"0\", \"1\", \"1\", \"1\"],\n    [\"1\", \"0\", \"0\", \"0\"],\n    [\"1\", \"0\", \"0\", \"0\"],\n    [\"1\", \"0\", \"0\", \"0\"],\n    [\"0\", \"1\", \"1\", \"1\"],\n  ],\n  D: [\n    [\"1\", \"1\", \"1\", \"0\"],\n    [\"1\", \"0\", \"0\", \"1\"],\n    [\"1\", \"0\", \"0\", \"1\"],\n    [\"1\", \"0\", \"0\", \"1\"],\n    [\"1\", \"1\", \"1\", \"0\"],\n  ],\n  E: [\n    [\"1\", \"1\", \"1\", \"1\"],\n    [\"1\", \"0\", \"0\", \"0\"],\n    [\"1\", \"1\", \"1\", \"0\"],\n    [\"1\", \"0\", \"0\", \"0\"],\n    [\"1\", \"1\", \"1\", \"1\"],\n  ],\n  F: [\n    [\"1\", \"1\", \"1\", \"1\"],\n    [\"1\", \"0\", \"0\", \"0\"],\n    [\"1\", \"1\", \"1\", \"0\"],\n    [\"1\", \"0\", \"0\", \"0\"],\n    [\"1\", \"0\", \"0\", \"0\"],\n  ],\n  G: [\n    [\"0\", \"1\", \"1\", \"1\"],\n    [\"1\", \"0\", \"0\", \"0\"],\n    [\"1\", \"0\", \"1\", \"1\"],\n    [\"1\", \"0\", \"0\", \"1\"],\n    [\"0\", \"1\", \"1\", \"1\"],\n  ],\n  H: [\n    [\"1\", \"0\", \"0\", \"1\"],\n    [\"1\", \"0\", \"0\", \"1\"],\n    [\"1\", \"1\", \"1\", \"1\"],\n    [\"1\", \"0\", \"0\", \"1\"],\n    [\"1\", \"0\", \"0\", \"1\"],\n  ],\n  I: [\n    [\"1\", \"1\", \"1\"],\n    [\"0\", \"1\", \"0\"],\n    [\"0\", \"1\", \"0\"],\n    [\"0\", \"1\", \"0\"],\n    [\"1\", \"1\", \"1\"],\n  ],\n  J: [\n    [\"0\", \"0\", \"1\", \"1\"],\n    [\"0\", \"0\", \"0\", \"1\"],\n    [\"0\", \"0\", \"0\", \"1\"],\n    [\"1\", \"0\", \"0\", \"1\"],\n    [\"0\", \"1\", \"1\", \"0\"],\n  ],\n  K: [\n    [\"1\", \"0\", \"0\", \"1\"],\n    [\"1\", \"0\", \"1\", \"0\"],\n    [\"1\", \"1\", \"0\", \"0\"],\n    [\"1\", \"0\", \"1\", \"0\"],\n    [\"1\", \"0\", \"0\", \"1\"],\n  ],\n  L: [\n    [\"1\", \"0\", \"0\", \"0\"],\n    [\"1\", \"0\", \"0\", \"0\"],\n    [\"1\", \"0\", \"0\", \"0\"],\n    [\"1\", \"0\", \"0\", \"0\"],\n    [\"1\", \"1\", \"1\", \"1\"],\n  ],\n  M: [\n    [\"1\", \"0\", \"0\", \"0\", \"1\"],\n    [\"1\", \"1\", \"0\", \"1\", \"1\"],\n    [\"1\", \"0\", \"1\", \"0\", \"1\"],\n    [\"1\", \"0\", \"0\", \"0\", \"1\"],\n    [\"1\", \"0\", \"0\", \"0\", \"1\"],\n  ],\n  N: [\n    [\"1\", \"0\", \"0\", \"1\"],\n    [\"1\", \"1\", \"0\", \"1\"],\n    [\"1\", \"0\", \"1\", \"1\"],\n    [\"1\", \"0\", \"0\", \"1\"],\n    [\"1\", \"0\", \"0\", \"1\"],\n  ],\n  O: [\n    [\"0\", \"1\", \"1\", \"0\"],\n    [\"1\", \"0\", \"0\", \"1\"],\n    [\"1\", \"0\", \"0\", \"1\"],\n    [\"1\", \"0\", \"0\", \"1\"],\n    [\"0\", \"1\", \"1\", \"0\"],\n  ],\n  P: [\n    [\"1\", \"1\", \"1\", \"0\"],\n    [\"1\", \"0\", \"0\", \"1\"],\n    [\"1\", \"1\", \"1\", \"0\"],\n    [\"1\", \"0\", \"0\", \"0\"],\n    [\"1\", \"0\", \"0\", \"0\"],\n  ],\n  Q: [\n    [\"0\", \"1\", \"1\", \"0\"],\n    [\"1\", \"0\", \"0\", \"1\"],\n    [\"1\", \"0\", \"0\", \"1\"],\n    [\"1\", \"0\", \"1\", \"0\"],\n    [\"0\", \"1\", \"0\", \"1\"],\n  ],\n  R: [\n    [\"1\", \"1\", \"1\", \"0\"],\n    [\"1\", \"0\", \"0\", \"1\"],\n    [\"1\", \"1\", \"1\", \"0\"],\n    [\"1\", \"0\", \"1\", \"0\"],\n    [\"1\", \"0\", \"0\", \"1\"],\n  ],\n  S: [\n    [\"0\", \"1\", \"1\", \"1\"],\n    [\"1\", \"0\", \"0\", \"0\"],\n    [\"0\", \"1\", \"1\", \"0\"],\n    [\"0\", \"0\", \"0\", \"1\"],\n    [\"1\", \"1\", \"1\", \"0\"],\n  ],\n  T: [\n    [\"1\", \"1\", \"1\", \"1\", \"1\"],\n    [\"0\", \"0\", \"1\", \"0\", \"0\"],\n    [\"0\", \"0\", \"1\", \"0\", \"0\"],\n    [\"0\", \"0\", \"1\", \"0\", \"0\"],\n    [\"0\", \"0\", \"1\", \"0\", \"0\"],\n  ],\n  U: [\n    [\"1\", \"0\", \"0\", \"1\"],\n    [\"1\", \"0\", \"0\", \"1\"],\n    [\"1\", \"0\", \"0\", \"1\"],\n    [\"1\", \"0\", \"0\", \"1\"],\n    [\"0\", \"1\", \"1\", \"0\"],\n  ],\n  V: [\n    [\"1\", \"0\", \"0\", \"0\", \"1\"],\n    [\"1\", \"0\", \"0\", \"0\", \"1\"],\n    [\"0\", \"1\", \"0\", \"1\", \"0\"],\n    [\"0\", \"1\", \"0\", \"1\", \"0\"],\n    [\"0\", \"0\", \"1\", \"0\", \"0\"],\n  ],\n  W: [\n    [\"1\", \"0\", \"0\", \"0\", \"1\"],\n    [\"1\", \"0\", \"0\", \"0\", \"1\"],\n    [\"1\", \"0\", \"1\", \"0\", \"1\"],\n    [\"1\", \"1\", \"0\", \"1\", \"1\"],\n    [\"1\", \"0\", \"0\", \"0\", \"1\"],\n  ],\n  X: [\n    [\"1\", \"0\", \"0\", \"1\"],\n    [\"0\", \"1\", \"1\", \"0\"],\n    [\"0\", \"0\", \"0\", \"0\"],\n    [\"0\", \"1\", \"1\", \"0\"],\n    [\"1\", \"0\", \"0\", \"1\"],\n  ],\n  Y: [\n    [\"1\", \"0\", \"0\", \"0\", \"1\"],\n    [\"0\", \"1\", \"0\", \"1\", \"0\"],\n    [\"0\", \"0\", \"1\", \"0\", \"0\"],\n    [\"0\", \"0\", \"1\", \"0\", \"0\"],\n    [\"0\", \"0\", \"1\", \"0\", \"0\"],\n  ],\n  Z: [\n    [\"1\", \"1\", \"1\", \"1\"],\n    [\"0\", \"0\", \"0\", \"1\"],\n    [\"0\", \"0\", \"1\", \"0\"],\n    [\"0\", \"1\", \"0\", \"0\"],\n    [\"1\", \"1\", \"1\", \"1\"],\n  ],\n}\n",
      "type": "registry:ui"
    }
  ]
}