{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "d1-console",
  "title": "D1 console",
  "description": "A SQL console for a D1 database: the tables and their columns, an editor that runs with ⌘↵, and the result grid with its row count and timing.",
  "registryDependencies": [
    "https://sproutboat.com/ikada/r/theme.json",
    "https://sproutboat.com/ikada/r/badge.json",
    "https://sproutboat.com/ikada/r/banner.json",
    "https://sproutboat.com/ikada/r/button.json",
    "https://sproutboat.com/ikada/r/cn.json",
    "https://sproutboat.com/ikada/r/icons.json",
    "https://sproutboat.com/ikada/r/kbd.json",
    "https://sproutboat.com/ikada/r/table.json",
    "https://sproutboat.com/ikada/r/textarea.json"
  ],
  "files": [
    {
      "path": "registry/ikada/blocks/d1-console/d1-console.tsx",
      "content": "\"use client\";\n\n/**\n * A SQL console for a D1 database: the tables and their columns, an editor that runs with ⌘↵,\n * and the result grid with its row count and timing. The sample runs simple SELECTs against\n * in-memory tables; pass `run` to send queries to your own database.\n */\nimport { useId, useRef, useState } from \"react\";\nimport type { KeyboardEvent } from \"react\";\nimport { Badge } from \"@/components/ikada/badge\";\nimport { Banner } from \"@/components/ikada/banner\";\nimport { Button } from \"@/components/ikada/button\";\nimport { Kbd } from \"@/components/ikada/kbd\";\nimport { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from \"@/components/ikada/table\";\nimport { Textarea } from \"@/components/ikada/textarea\";\nimport { cn, focusRing } from \"@/components/ikada/lib/cn\";\nimport { ChevronRightIcon, DatabaseIcon, PlayIcon, TableIcon } from \"@/components/ikada/lib/icons\";\n\nexport type SqlValue = string | number | null;\nexport type ColumnType = \"INTEGER\" | \"REAL\" | \"TEXT\";\n\nexport interface SqlColumn {\n  name: string;\n  type: ColumnType;\n  primaryKey?: boolean;\n}\n\nexport interface SqlTable {\n  name: string;\n  columns: SqlColumn[];\n  rows: SqlValue[][];\n}\n\nexport interface QueryResult {\n  columns: SqlColumn[];\n  rows: SqlValue[][];\n  /** Rows the database scanned to answer. */\n  rowsRead: number;\n  durationMs: number;\n}\n\nexport interface D1ConsoleProps {\n  /** The database name shown in the header. */\n  database?: string;\n  /** The schema for the tables tree, and the data the built-in runner queries. */\n  tables?: SqlTable[];\n  /** The SQL in the editor at first; it runs once on mount. */\n  initialQuery?: string;\n  /** Runs a statement. Defaults to `runSelect` over `tables`. Throw an Error to show it as a failed query. */\n  run?: (sql: string) => QueryResult | Promise<QueryResult>;\n  className?: string;\n}\n\n/* ---------------------------------------------------------------------------\n * Sample data: a small plant shop's database\n * ------------------------------------------------------------------------- */\n\nconst PLANTS: [string, string, number, number][] = [\n  [\"Monstera deliciosa\", \"Araceae\", 34.9, 18],\n  [\"Pilea peperomioides\", \"Urticaceae\", 14.5, 0],\n  [\"Ficus lyrata\", \"Moraceae\", 48, 7],\n  [\"Calathea orbifolia\", \"Marantaceae\", 26.5, 11],\n  [\"Sansevieria trifasciata\", \"Asparagaceae\", 19.9, 32],\n  [\"Epipremnum aureum\", \"Araceae\", 12, 45],\n  [\"Zamioculcas zamiifolia\", \"Araceae\", 29.9, 14],\n  [\"Strelitzia nicolai\", \"Strelitziaceae\", 64, 4],\n  [\"Alocasia zebrina\", \"Araceae\", 39.5, 6],\n  [\"Hoya carnosa\", \"Apocynaceae\", 16.9, 21],\n  [\"Peperomia obtusifolia\", \"Piperaceae\", 11.5, 27],\n  [\"Philodendron hederaceum\", \"Araceae\", 13.9, 38],\n];\n\nconst CUSTOMERS: [string, string, string][] = [\n  [\"Mara Okafor\", \"mara@example.com\", \"PT\"],\n  [\"Theo Lindqvist\", \"theo@example.com\", \"SE\"],\n  [\"Inês Carvalho\", \"ines@example.com\", \"PT\"],\n  [\"Jonas Weber\", \"jonas@example.com\", \"DE\"],\n  [\"Aiko Tanaka\", \"aiko@example.com\", \"JP\"],\n  [\"Lucía Ortega\", \"lucia@example.com\", \"ES\"],\n  [\"Samir Haddad\", \"samir@example.com\", \"FR\"],\n  [\"Nora Kelly\", \"nora@example.com\", \"IE\"],\n];\n\nconst STATUSES = [\"delivered\", \"delivered\", \"shipped\", \"delivered\", \"paid\", \"refunded\", \"delivered\", \"shipped\"];\n\n/** Days after 2026-10-01 as an ISO date, so the sample never depends on today's clock. */\nconst day = (n: number): string => new Date(Date.UTC(2026, 9, 1 + n)).toISOString().slice(0, 10);\n\nexport const sampleTables: SqlTable[] = [\n  {\n    name: \"plants\",\n    columns: [\n      { name: \"id\", type: \"INTEGER\", primaryKey: true },\n      { name: \"name\", type: \"TEXT\" },\n      { name: \"family\", type: \"TEXT\" },\n      { name: \"price\", type: \"REAL\" },\n      { name: \"stock\", type: \"INTEGER\" },\n    ],\n    rows: PLANTS.map(([name, family, price, stock], i) => [i + 1, name, family, price, stock]),\n  },\n  {\n    name: \"customers\",\n    columns: [\n      { name: \"id\", type: \"INTEGER\", primaryKey: true },\n      { name: \"name\", type: \"TEXT\" },\n      { name: \"email\", type: \"TEXT\" },\n      { name: \"country\", type: \"TEXT\" },\n      { name: \"joined\", type: \"TEXT\" },\n    ],\n    rows: CUSTOMERS.map(([name, email, country], i) => [i + 1, name, email, country, day(i * 3 - 40)]),\n  },\n  {\n    name: \"orders\",\n    columns: [\n      { name: \"id\", type: \"INTEGER\", primaryKey: true },\n      { name: \"customer_id\", type: \"INTEGER\" },\n      { name: \"plant_id\", type: \"INTEGER\" },\n      { name: \"qty\", type: \"INTEGER\" },\n      { name: \"total\", type: \"REAL\" },\n      { name: \"status\", type: \"TEXT\" },\n      { name: \"placed\", type: \"TEXT\" },\n    ],\n    rows: Array.from({ length: 24 }, (_, i) => {\n      const plant = (i * 5) % PLANTS.length;\n      const qty = 1 + ((i * 7) % 3);\n      const price = PLANTS[plant]?.[2] ?? 0;\n      return [\n        1001 + i,\n        1 + ((i * 3) % CUSTOMERS.length),\n        plant + 1,\n        qty,\n        Math.round(price * qty * 100) / 100,\n        STATUSES[i % STATUSES.length] ?? \"paid\",\n        day(i + 6),\n      ];\n    }),\n  },\n  {\n    name: \"reviews\",\n    columns: [\n      { name: \"id\", type: \"INTEGER\", primaryKey: true },\n      { name: \"plant_id\", type: \"INTEGER\" },\n      { name: \"rating\", type: \"INTEGER\" },\n      { name: \"body\", type: \"TEXT\" },\n    ],\n    rows: [\n      [1, 1, 5, \"Arrived huge and healthy, two new leaves already.\"],\n      [2, 3, 4, \"Dropped a leaf after the move, settled in since.\"],\n      [3, 6, 5, \"Impossible to kill. Bought a second one.\"],\n      [4, 2, 3, null],\n      [5, 8, 5, \"Packed like a museum piece.\"],\n    ],\n  },\n];\n\nconst SAMPLE_QUERY = `-- Best-stocked plants first\nSELECT id, name, price, stock FROM plants ORDER BY stock DESC LIMIT 8;`;\n\n/* ---------------------------------------------------------------------------\n * The built-in runner: one SELECT shape, enough for a demo or a read-only browser\n * ------------------------------------------------------------------------- */\n\nconst SELECT_RE =\n  /^SELECT\\s+(.+?)\\s+FROM\\s+([A-Za-z_]\\w*)(?:\\s+WHERE\\s+([A-Za-z_]\\w*)\\s*=\\s*('(?:[^']|'')*'|-?\\d+(?:\\.\\d+)?|NULL))?(?:\\s+ORDER\\s+BY\\s+([A-Za-z_]\\w*)(?:\\s+(ASC|DESC))?)?(?:\\s+LIMIT\\s+(\\d+))?$/i;\n\nconst SHAPE = \"SELECT columns FROM table [WHERE column = value] [ORDER BY column [DESC]] [LIMIT n]\";\n\nfunction literal(raw: string): SqlValue {\n  if (/^NULL$/i.test(raw)) return null;\n  if (raw.startsWith(\"'\")) return raw.slice(1, -1).replaceAll(\"''\", \"'\");\n  return Number(raw);\n}\n\n/**\n * Runs `SELECT cols|* FROM t [WHERE col = value] [ORDER BY col [ASC|DESC]] [LIMIT n]` over\n * in-memory tables. Anything else throws an Error that says what it accepts.\n */\nexport function runSelect(sql: string, tables: SqlTable[]): QueryResult {\n  const started = performance.now();\n  const text = sql\n    .replace(/--[^\\n]*/g, \" \")\n    .replace(/\\s+/g, \" \")\n    .trim()\n    .replace(/;$/, \"\")\n    .trim();\n  if (!text) throw new Error(\"There is no statement to run.\");\n  if (text.includes(\";\")) throw new Error(\"Run one statement at a time.\");\n  const m = SELECT_RE.exec(text);\n  if (!m) {\n    const verb = text.split(\" \")[0]?.toUpperCase() ?? \"\";\n    throw new Error(\n      verb === \"SELECT\"\n        ? `This console understands ${SHAPE}.`\n        : `${verb} is not supported here: this console runs read-only queries of the form ${SHAPE}.`,\n    );\n  }\n  const [, cols = \"*\", tableName = \"\", whereCol, whereVal, orderCol, orderDir, limit] = m;\n  const table = tables.find((t) => t.name.toLowerCase() === tableName.toLowerCase());\n  if (!table) throw new Error(`no such table: ${tableName}`);\n  const index = (name: string): number => {\n    const i = table.columns.findIndex((c) => c.name.toLowerCase() === name.toLowerCase());\n    if (i < 0) throw new Error(`no such column: ${name}`);\n    return i;\n  };\n  const picked = cols.trim() === \"*\" ? table.columns.map((_, i) => i) : cols.split(\",\").map((c) => index(c.trim()));\n  let rows = table.rows;\n  if (whereCol && whereVal !== undefined) {\n    const i = index(whereCol);\n    const want = literal(whereVal);\n    rows = rows.filter((r) => (want === null ? r[i] === null : r[i] === want));\n  }\n  if (orderCol) {\n    const i = index(orderCol);\n    const dir = orderDir?.toUpperCase() === \"DESC\" ? -1 : 1;\n    rows = [...rows].sort((a, b) => {\n      const x = a[i] ?? null;\n      const y = b[i] ?? null;\n      if (x === y) return 0;\n      if (x === null) return -dir;\n      if (y === null) return dir;\n      return (x < y ? -1 : 1) * dir;\n    });\n  }\n  if (limit !== undefined) rows = rows.slice(0, Number(limit));\n  return {\n    columns: picked.map((i) => table.columns[i] as SqlColumn),\n    rows: rows.map((r) => picked.map((i) => r[i] ?? null)),\n    rowsRead: table.rows.length,\n    durationMs: Math.max(0.1, performance.now() - started),\n  };\n}\n\n/* ---------------------------------------------------------------------------\n * The console\n * ------------------------------------------------------------------------- */\n\ntype Outcome = { kind: \"ok\"; result: QueryResult } | { kind: \"error\"; message: string };\n\nfunction attempt(run: (sql: string) => QueryResult, sql: string): Outcome {\n  try {\n    return { kind: \"ok\", result: run(sql) };\n  } catch (error) {\n    return { kind: \"error\", message: error instanceof Error ? error.message : \"The query did not run.\" };\n  }\n}\n\nconst isMac = (): boolean => typeof navigator !== \"undefined\" && /Mac|iPhone|iPad/.test(navigator.platform);\n\n/** Tables with their columns. Up and down move between the rows of the tree; right opens, left closes. */\nfunction TablesTree({\n  tables,\n  current,\n  onQuery,\n}: {\n  tables: SqlTable[];\n  current: string | null;\n  onQuery: (table: string) => void;\n}) {\n  const [open, setOpen] = useState<Set<string>>(() => new Set(tables.slice(0, 1).map((t) => t.name)));\n  const ref = useRef<HTMLUListElement>(null);\n  const toggle = (name: string, to?: boolean) =>\n    setOpen((was) => {\n      const next = new Set(was);\n      if (to ?? !next.has(name)) next.add(name);\n      else next.delete(name);\n      return next;\n    });\n\n  function onKey(event: KeyboardEvent<HTMLButtonElement>, name: string) {\n    const items = [...(ref.current?.querySelectorAll<HTMLButtonElement>(\"[data-tree-item]\") ?? [])];\n    const at = items.indexOf(event.currentTarget);\n    const move = { ArrowDown: at + 1, ArrowUp: at - 1, Home: 0, End: items.length - 1 }[event.key];\n    if (move !== undefined) {\n      event.preventDefault();\n      items[Math.max(0, Math.min(items.length - 1, move))]?.focus();\n    } else if (event.key === \"ArrowRight\" || event.key === \"ArrowLeft\") {\n      event.preventDefault();\n      toggle(name, event.key === \"ArrowRight\");\n    }\n  }\n\n  return (\n    <ul ref={ref} className=\"flex flex-col gap-0.5 p-1.5\" aria-label=\"Tables\">\n      {tables.map((t) => {\n        const expanded = open.has(t.name);\n        return (\n          <li key={t.name} className=\"\">\n            <div className=\"flex items-center gap-0.5\">\n              <button\n                type=\"button\"\n                tabIndex={-1}\n                aria-expanded={expanded}\n                aria-label={`${expanded ? \"Hide\" : \"Show\"} columns of ${t.name}`}\n                onClick={() => toggle(t.name)}\n                className={cn(\n                  \"grid size-7 shrink-0 place-items-center rounded-md text-fg-muted hover:bg-hover hover:text-fg pointer-coarse:size-11\",\n                  focusRing,\n                )}\n              >\n                <ChevronRightIcon\n                  className={cn(\n                    \"size-3.5 transition-transform duration-(--sb-duration-hover) ease-hover\",\n                    expanded && \"rotate-90\",\n                  )}\n                />\n              </button>\n              <button\n                type=\"button\"\n                data-tree-item\n                aria-current={current === t.name ? \"true\" : undefined}\n                title={`SELECT * FROM ${t.name}`}\n                onClick={() => onQuery(t.name)}\n                onKeyDown={(event) => onKey(event, t.name)}\n                className={cn(\n                  \"flex h-8 min-w-0 flex-1 items-center gap-2 rounded-md px-2 text-start hover:bg-hover\",\n                  \"aria-[current=true]:bg-hover\",\n                  focusRing,\n                )}\n              >\n                <TableIcon className=\"text-fg-muted\" />\n                <span\n                  title={t.name}\n                  className={cn(\"min-w-0 flex-1 truncate font-mono text-sb-sm\", current === t.name && \"font-semibold\")}\n                >\n                  {t.name}\n                </span>\n                <span className=\"shrink-0 text-sb-xs text-fg-muted tabular-nums\">\n                  {t.rows.length.toLocaleString(\"en\")}\n                </span>\n              </button>\n            </div>\n            {expanded ? (\n              <ul className=\"flex flex-col gap-1 pt-1 pb-2 ps-11 pe-2\" aria-label={`Columns of ${t.name}`}>\n                {t.columns.map((c) => (\n                  <li key={c.name} className=\"flex min-w-0 items-baseline justify-between gap-2 font-mono text-sb-xs\">\n                    <span className=\"truncate\" title={c.name}>\n                      {c.name}\n                    </span>\n                    <span className=\"shrink-0 text-fg-muted\">\n                      {c.type}\n                      {c.primaryKey ? \" · PK\" : \"\"}\n                    </span>\n                  </li>\n                ))}\n              </ul>\n            ) : null}\n          </li>\n        );\n      })}\n    </ul>\n  );\n}\n\nfunction ResultGrid({ outcome, busy }: { outcome: Outcome | null; busy: boolean }) {\n  if (!outcome) {\n    return (\n      <p className=\"border-t border-hairline px-4 py-8 text-center text-sb-sm text-fg-muted\">\n        Run a query to see its rows here.\n      </p>\n    );\n  }\n  if (outcome.kind === \"error\") {\n    return (\n      <div className=\"border-t border-hairline p-3 @lg:p-4\" role=\"alert\">\n        <Banner tone=\"danger\" title=\"The query failed\">\n          <span className=\"font-mono text-sb-sm break-words\">{outcome.message}</span>\n        </Banner>\n      </div>\n    );\n  }\n  const { result } = outcome;\n  const numeric = result.columns.map((c) => c.type !== \"TEXT\");\n  const count = result.rows.length;\n  return (\n    <div className={cn(\"flex min-w-0 flex-col border-t border-hairline transition-opacity\", busy && \"opacity-60\")}>\n      <div\n        className=\"flex flex-wrap items-center gap-x-3 gap-y-1 px-3 py-2 text-sb-sm text-fg-muted @lg:px-4\"\n        role=\"status\"\n      >\n        <Badge tone=\"success\" dot>\n          {count.toLocaleString(\"en\")} {count === 1 ? \"row\" : \"rows\"}\n        </Badge>\n        <span className=\"tabular-nums\">\n          {result.durationMs < 10 ? result.durationMs.toFixed(1) : Math.round(result.durationMs)} ms ·{\" \"}\n          {result.rowsRead.toLocaleString(\"en\")} rows read\n        </span>\n      </div>\n      {count ? (\n        <Table label=\"Query result\" containerClassName=\"rounded-none shadow-none\" maxHeight=\"24rem\" stickyHeader>\n          <TableHeader>\n            <TableRow className=\"hover:bg-transparent\">\n              <TableHead className=\"w-10 bg-surface text-end\">#</TableHead>\n              {result.columns.map((c, i) => (\n                <TableHead key={c.name} className={cn(\"bg-surface whitespace-nowrap\", numeric[i] && \"text-end\")}>\n                  <span className=\"font-mono\">{c.name}</span>{\" \"}\n                  <span className=\"text-sb-xs font-normal text-fg-muted\">{c.type}</span>\n                </TableHead>\n              ))}\n            </TableRow>\n          </TableHeader>\n          <TableBody>\n            {result.rows.map((row, r) => (\n              <TableRow key={r}>\n                <TableCell className=\"py-1.5 text-end font-mono text-sb-xs text-fg-muted tabular-nums\">\n                  {r + 1}\n                </TableCell>\n                {row.map((value, c) => (\n                  <TableCell\n                    key={c}\n                    title={value === null ? \"NULL\" : String(value)}\n                    className={cn(\n                      \"max-w-72 truncate py-1.5 font-mono text-sb-sm whitespace-nowrap\",\n                      numeric[c] && \"text-end tabular-nums\",\n                    )}\n                  >\n                    {value === null ? (\n                      <span className=\"text-fg-muted\">NULL</span>\n                    ) : typeof value === \"number\" && result.columns[c]?.type === \"REAL\" ? (\n                      value.toLocaleString(\"en\", { minimumFractionDigits: 2, maximumFractionDigits: 2 })\n                    ) : (\n                      String(value)\n                    )}\n                  </TableCell>\n                ))}\n              </TableRow>\n            ))}\n          </TableBody>\n        </Table>\n      ) : (\n        <p className=\"px-4 pb-4 text-sb-sm text-fg-muted\">The query ran and matched no rows.</p>\n      )}\n    </div>\n  );\n}\n\nexport function D1Console({\n  database = \"fernleaf-shop\",\n  tables = sampleTables,\n  initialQuery = SAMPLE_QUERY,\n  run,\n  className,\n}: D1ConsoleProps) {\n  const exec = run ?? ((sql: string) => runSelect(sql, tables));\n  const [sql, setSql] = useState(initialQuery);\n  const [busy, setBusy] = useState(false);\n  const [showTables, setShowTables] = useState(false);\n  const [current, setCurrent] = useState<string | null>(null);\n  // The first query runs on mount (synchronously for the built-in runner), so the grid is never empty.\n  const [outcome, setOutcome] = useState<Outcome | null>(() =>\n    run ? null : attempt((s) => runSelect(s, tables), initialQuery),\n  );\n  const editorId = useId();\n\n  async function execute(text = sql) {\n    setBusy(true);\n    try {\n      setOutcome({ kind: \"ok\", result: await exec(text) });\n    } catch (error) {\n      setOutcome({ kind: \"error\", message: error instanceof Error ? error.message : \"The query did not run.\" });\n    } finally {\n      setBusy(false);\n    }\n  }\n\n  function queryTable(name: string) {\n    const text = `SELECT * FROM ${name} LIMIT 50;`;\n    setSql(text);\n    setCurrent(name);\n    void execute(text);\n  }\n\n  return (\n    <section\n      aria-label={`SQL console for ${database}`}\n      data-density=\"compact\"\n      className={cn(\"@container min-w-0 overflow-hidden rounded-xl bg-surface text-fg shadow-card\", className)}\n    >\n      <div className=\"grid min-w-0 @3xl:grid-cols-[15rem_minmax(0,1fr)]\">\n        <aside className=\"min-w-0 border-hairline @max-3xl:border-b @3xl:border-e\" aria-label=\"Schema\">\n          <div className=\"flex items-center gap-2 px-3 pt-3 pb-1\">\n            <DatabaseIcon className=\"text-fg-muted\" />\n            <span className=\"min-w-0 flex-1 truncate font-mono text-sb-sm font-semibold\" title={database}>\n              {database}\n            </span>\n            <button\n              type=\"button\"\n              aria-expanded={showTables}\n              onClick={() => setShowTables((v) => !v)}\n              className={cn(\n                \"inline-flex h-7 items-center gap-1 rounded-md px-2 text-sb-sm text-fg-muted hover:bg-hover hover:text-fg pointer-coarse:h-11 @3xl:hidden\",\n                focusRing,\n              )}\n            >\n              {tables.length} tables\n              <ChevronRightIcon\n                className={cn(\n                  \"size-3.5 transition-transform duration-(--sb-duration-hover) ease-hover\",\n                  showTables && \"rotate-90\",\n                )}\n              />\n            </button>\n          </div>\n          <div className={cn(!showTables && \"@max-3xl:hidden\")}>\n            <p className=\"px-3 pt-2 text-sb-xs text-fg-muted @max-3xl:hidden\">Tables · click one to query it</p>\n            <TablesTree tables={tables} current={current} onQuery={queryTable} />\n          </div>\n        </aside>\n\n        <div className=\"flex min-w-0 flex-col\">\n          <div className=\"flex items-center gap-3 border-b border-hairline px-3 py-2\">\n            <label htmlFor={editorId} className=\"text-sb-sm font-semibold\">\n              Query\n            </label>\n            <span className=\"text-sb-xs text-fg-muted @max-md:hidden\">Read-only · one statement</span>\n            <Button\n              variant=\"primary\"\n              size=\"sm\"\n              className=\"ms-auto\"\n              loading={busy}\n              onClick={() => void execute()}\n              aria-keyshortcuts=\"Meta+Enter Control+Enter\"\n            >\n              <PlayIcon />\n              Run\n              <Kbd className=\"@max-md:hidden\">{isMac() ? \"⌘↵\" : \"Ctrl ↵\"}</Kbd>\n            </Button>\n          </div>\n          <Textarea\n            id={editorId}\n            mono\n            spellCheck={false}\n            wrap=\"off\"\n            rows={5}\n            value={sql}\n            placeholder=\"SELECT * FROM plants LIMIT 50;\"\n            onChange={(event) => setSql(event.target.value)}\n            onKeyDown={(event) => {\n              if (event.key === \"Enter\" && (event.metaKey || event.ctrlKey)) {\n                event.preventDefault();\n                void execute();\n              }\n            }}\n            className=\"min-h-32 resize-y rounded-none bg-well shadow-none hover:shadow-none focus-visible:bg-well focus-visible:outline-offset-[-2px]\"\n          />\n          <ResultGrid outcome={outcome} busy={busy} />\n        </div>\n      </div>\n    </section>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/ikada/blocks/d1-console/d1-console.tsx"
    }
  ],
  "categories": [
    "dashboard"
  ],
  "type": "registry:block"
}