{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "detail-panel",
  "title": "Detail Panel",
  "author": "ilinxa",
  "description": "Selection-aware detail container with read and edit modes, lifecycle states, sticky header and footer actions, and a slot-based body.",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "button",
    "skeleton"
  ],
  "files": [
    {
      "path": "src/registry/components/feedback/detail-panel/detail-panel.tsx",
      "content": "\"use client\";\n\nimport {\n  useCallback,\n  useEffect,\n  useImperativeHandle,\n  useMemo,\n  useRef,\n} from \"react\";\nimport type {\n  DetailPanelContextValue,\n  DetailPanelHandle,\n  DetailPanelProps,\n} from \"./types\";\nimport { DEFAULT_DETAIL_PANEL_LABELS } from \"./types\";\nimport { DetailPanelContext } from \"./parts/detail-panel-context\";\nimport { DetailPanelHeader } from \"./parts/detail-panel-header\";\nimport { DetailPanelBody } from \"./parts/detail-panel-body\";\nimport { DetailPanelActions } from \"./parts/detail-panel-actions\";\nimport { DetailPanelEmptyState } from \"./parts/detail-panel-empty-state\";\nimport { DetailPanelSkeleton } from \"./parts/detail-panel-skeleton\";\nimport { DetailPanelError } from \"./parts/detail-panel-error\";\nimport { SelectionAnnouncer } from \"./parts/selection-announcer\";\nimport { useDetailPanelMode } from \"./hooks/use-detail-panel-mode\";\nimport { useFocusRestore } from \"./hooks/use-focus-restore\";\nimport { keyForSelection } from \"./lib/selection-key\";\nimport { cn } from \"@/lib/utils\";\n\nconst FOCUSABLE =\n  'input, textarea, select, button, [tabindex]:not([tabindex=\"-1\"]), [contenteditable=\"true\"]';\n\nfunction DetailPanelImpl({\n  selection,\n  mode: controlledMode,\n  onModeChange,\n  canEdit = true,\n  loading = false,\n  error = null,\n  emptyState,\n  children,\n  ariaLabel,\n  labels,\n  className,\n  ref,\n}: DetailPanelProps) {\n  const resolvedAriaLabel =\n    ariaLabel ?? labels?.region ?? DEFAULT_DETAIL_PANEL_LABELS.region;\n  const rootRef = useRef<HTMLDivElement>(null);\n  const bodyRef = useRef<HTMLDivElement>(null);\n\n  const selectionKey = useMemo(() => keyForSelection(selection), [selection]);\n\n  const { mode, setMode } = useDetailPanelMode({\n    controlledMode,\n    onModeChange,\n    selectionKey,\n  });\n\n  const { captureTrigger } = useFocusRestore({ mode, rootRef, bodyRef });\n\n  const wrappedSetMode = useCallback(\n    (next: typeof mode) => {\n      captureTrigger();\n      setMode(next);\n    },\n    [captureTrigger, setMode],\n  );\n\n  const hasError = !!error;\n  const hasSelection = selection !== null;\n\n  const lastSelectionKeyRef = useRef(selectionKey);\n  useEffect(() => {\n    if (lastSelectionKeyRef.current === selectionKey) return;\n    lastSelectionKeyRef.current = selectionKey;\n    rootRef.current?.focus({ preventScroll: true });\n  }, [selectionKey]);\n\n  const focusBody = useCallback(() => {\n    const body = bodyRef.current;\n    if (!body) return;\n    const focusable = body.querySelector<HTMLElement>(FOCUSABLE);\n    (focusable ?? body).focus();\n  }, []);\n\n  const resetMode = useCallback(() => {\n    setMode(\"read\");\n  }, [setMode]);\n\n  useImperativeHandle(\n    ref,\n    (): DetailPanelHandle => ({\n      focusBody,\n      resetMode,\n    }),\n    [focusBody, resetMode],\n  );\n\n  const ctx: DetailPanelContextValue = useMemo(\n    () => ({\n      selection,\n      mode,\n      setMode: wrappedSetMode,\n      canEdit,\n      loading,\n      hasError,\n      selectionKey,\n      bodyRef,\n    }),\n    [\n      selection,\n      mode,\n      wrappedSetMode,\n      canEdit,\n      loading,\n      hasError,\n      selectionKey,\n    ],\n  );\n\n  let body: React.ReactNode;\n  if (hasError && error) {\n    body = <DetailPanelError error={error} />;\n  } else if (loading) {\n    body = <DetailPanelSkeleton />;\n  } else if (!hasSelection) {\n    body = emptyState ?? <DetailPanelEmptyState />;\n  } else {\n    body = (\n      <div key={selectionKey} className=\"contents\">\n        {children}\n      </div>\n    );\n  }\n\n  return (\n    <DetailPanelContext.Provider value={ctx}>\n      <div\n        ref={rootRef}\n        role=\"region\"\n        aria-label={resolvedAriaLabel}\n        aria-busy={loading || undefined}\n        tabIndex={-1}\n        className={cn(\n          \"relative flex h-full flex-col overflow-hidden rounded-md border border-border bg-card text-card-foreground outline-none focus-visible:ring-2 focus-visible:ring-ring\",\n          className,\n        )}\n      >\n        <SelectionAnnouncer selectionKey={selectionKey} />\n        {body}\n      </div>\n    </DetailPanelContext.Provider>\n  );\n}\n\ntype DetailPanelComponent = ((props: DetailPanelProps) => React.JSX.Element) & {\n  Header: typeof DetailPanelHeader;\n  Body: typeof DetailPanelBody;\n  Actions: typeof DetailPanelActions;\n};\n\nexport const DetailPanel = DetailPanelImpl as unknown as DetailPanelComponent;\nDetailPanel.Header = DetailPanelHeader;\nDetailPanel.Body = DetailPanelBody;\nDetailPanel.Actions = DetailPanelActions;\n\nexport { DetailPanelEmptyState };\n",
      "type": "registry:component",
      "target": "components/detail-panel/detail-panel.tsx"
    },
    {
      "path": "src/registry/components/feedback/detail-panel/index.ts",
      "content": "export { DetailPanel, DetailPanelEmptyState } from \"./detail-panel\";\nexport { DEFAULT_DETAIL_PANEL_LABELS } from \"./types\";\nexport type {\n  DetailPanelActionsContext,\n  DetailPanelActionsProps,\n  DetailPanelActionsRenderFn,\n  DetailPanelBodyProps,\n  DetailPanelError,\n  DetailPanelHandle,\n  DetailPanelHeaderProps,\n  DetailPanelLabels,\n  DetailPanelMode,\n  DetailPanelProps,\n  DetailPanelSelection,\n} from \"./types\";\n",
      "type": "registry:component",
      "target": "components/detail-panel/index.ts"
    },
    {
      "path": "src/registry/components/feedback/detail-panel/types.ts",
      "content": "import type { ReactNode, Ref, RefObject } from \"react\";\n\nexport type DetailPanelMode = \"read\" | \"edit\";\n\nexport interface DetailPanelSelection {\n  type: string;\n  id: string;\n}\n\nexport interface DetailPanelError {\n  message: string;\n  retry?: () => void;\n}\n\nexport interface DetailPanelActionsContext {\n  mode: DetailPanelMode;\n  setMode: (next: DetailPanelMode) => void;\n  canEdit: boolean;\n}\n\nexport type DetailPanelActionsRenderFn = (\n  ctx: DetailPanelActionsContext,\n) => ReactNode;\n\nexport interface DetailPanelLabels {\n  /**\n   * Accessible name for the `role=\"region\"` landmark when no per-render\n   * `ariaLabel` is supplied. Default: 'Detail panel'.\n   */\n  region?: string;\n}\n\n/** Default English labels — exported for consumer composition. */\nexport const DEFAULT_DETAIL_PANEL_LABELS: Required<DetailPanelLabels> = {\n  region: \"Detail panel\",\n};\n\nexport interface DetailPanelProps {\n  selection: DetailPanelSelection | null;\n\n  mode?: DetailPanelMode;\n  onModeChange?: (mode: DetailPanelMode) => void;\n\n  canEdit?: boolean;\n\n  loading?: boolean;\n  error?: DetailPanelError | null;\n\n  emptyState?: ReactNode;\n\n  children: ReactNode;\n\n  /**\n   * Per-render accessible name override (e.g., the currently-selected\n   * entity's label). Wins over `labels.region`. When BOTH are absent, the\n   * region falls back to `DEFAULT_DETAIL_PANEL_LABELS.region` (\"Detail panel\")\n   * so the `role=\"region\"` landmark always has an accessible name.\n   */\n  ariaLabel?: string;\n  /**\n   * Localized labels. Currently a single key — `region` (the static landmark\n   * name). `ariaLabel` overrides on a per-render basis.\n   */\n  labels?: DetailPanelLabels;\n  className?: string;\n\n  ref?: Ref<DetailPanelHandle>;\n}\n\nexport interface DetailPanelHeaderProps {\n  children: ReactNode;\n  sticky?: boolean;\n  className?: string;\n}\n\nexport interface DetailPanelBodyProps {\n  children: ReactNode;\n  className?: string;\n}\n\nexport interface DetailPanelActionsProps {\n  children: ReactNode | DetailPanelActionsRenderFn;\n  position?: \"footer\" | \"header\";\n  className?: string;\n}\n\nexport interface DetailPanelHandle {\n  focusBody(): void;\n  resetMode(): void;\n}\n\nexport interface DetailPanelContextValue {\n  selection: DetailPanelSelection | null;\n  mode: DetailPanelMode;\n  setMode: (next: DetailPanelMode) => void;\n  canEdit: boolean;\n  loading: boolean;\n  hasError: boolean;\n  selectionKey: string;\n  bodyRef: RefObject<HTMLDivElement | null>;\n}\n",
      "type": "registry:component",
      "target": "components/detail-panel/types.ts"
    },
    {
      "path": "src/registry/components/feedback/detail-panel/hooks/use-detail-panel-mode.ts",
      "content": "import { useCallback, useEffect, useRef, useState } from \"react\";\nimport type { DetailPanelMode } from \"../types\";\n\ninterface UseModeArgs {\n  controlledMode: DetailPanelMode | undefined;\n  onModeChange: ((mode: DetailPanelMode) => void) | undefined;\n  selectionKey: string;\n}\n\ninterface UseModeResult {\n  mode: DetailPanelMode;\n  setMode: (next: DetailPanelMode) => void;\n  isLocked: boolean;\n}\n\ninterface ModeSnapshot {\n  mode: DetailPanelMode;\n  key: string;\n}\n\nexport function useDetailPanelMode({\n  controlledMode,\n  onModeChange,\n  selectionKey,\n}: UseModeArgs): UseModeResult {\n  const isControlled = controlledMode !== undefined;\n  const isLocked = isControlled && !onModeChange;\n\n  // Uncontrolled-only state. Derived `mode` returns \"read\" when the snapshot's\n  // key doesn't match the current selectionKey — this is how the auto-reset\n  // contract is honored without a setState-in-effect.\n  const [snapshot, setSnapshot] = useState<ModeSnapshot>({\n    mode: \"read\",\n    key: selectionKey,\n  });\n\n  const warnedLockedMountRef = useRef(false);\n  useEffect(() => {\n    if (!isLocked) return;\n    if (warnedLockedMountRef.current) return;\n    warnedLockedMountRef.current = true;\n    if (process.env.NODE_ENV !== \"production\") {\n      console.warn(\n        \"[detail-panel] `mode` is supplied without `onModeChange`. \" +\n          \"The panel cannot honor the auto-reset contract for selection changes. \" +\n          \"Pass `onModeChange` to enable the controlled configuration, or omit `mode` to use uncontrolled.\",\n      );\n    }\n  }, [isLocked]);\n\n  // Controlled-mode auto-reset: notify host of the mode change to \"read\" on\n  // selection change (host owns state — this is a callback, not a setState).\n  const lastReportedKeyRef = useRef(selectionKey);\n  useEffect(() => {\n    if (!isControlled || isLocked) return;\n    if (lastReportedKeyRef.current === selectionKey) return;\n    lastReportedKeyRef.current = selectionKey;\n    if (controlledMode !== \"read\" && onModeChange) {\n      onModeChange(\"read\");\n    }\n  }, [selectionKey, isControlled, isLocked, controlledMode, onModeChange]);\n\n  const lastWarnedLockedKeyRef = useRef(selectionKey);\n  useEffect(() => {\n    if (!isLocked) return;\n    if (lastWarnedLockedKeyRef.current === selectionKey) return;\n    lastWarnedLockedKeyRef.current = selectionKey;\n    if (process.env.NODE_ENV !== \"production\") {\n      console.warn(\n        \"[detail-panel] selection changed but the panel is in locked mode \" +\n          \"(no `onModeChange`); auto-reset to `\\\"read\\\"` cannot fire.\",\n      );\n    }\n  }, [selectionKey, isLocked]);\n\n  const setMode = useCallback(\n    (next: DetailPanelMode) => {\n      if (isLocked) {\n        if (process.env.NODE_ENV !== \"production\") {\n          console.warn(\n            \"[detail-panel] setMode called in locked mode; pass `onModeChange` to enable mode toggling.\",\n          );\n        }\n        return;\n      }\n      if (isControlled) {\n        onModeChange?.(next);\n        return;\n      }\n      // Snapshot the current selectionKey alongside the mode so the\n      // derived-mode logic resets cleanly on subsequent selection changes.\n      setSnapshot({ mode: next, key: selectionKey });\n    },\n    [isLocked, isControlled, onModeChange, selectionKey],\n  );\n\n  let mode: DetailPanelMode;\n  if (isControlled) {\n    mode = controlledMode as DetailPanelMode;\n  } else if (snapshot.key === selectionKey) {\n    mode = snapshot.mode;\n  } else {\n    mode = \"read\";\n  }\n\n  return { mode, setMode, isLocked };\n}\n",
      "type": "registry:component",
      "target": "components/detail-panel/hooks/use-detail-panel-mode.ts"
    },
    {
      "path": "src/registry/components/feedback/detail-panel/hooks/use-focus-restore.ts",
      "content": "\"use client\";\n\nimport { useEffect, useRef } from \"react\";\nimport type { DetailPanelMode } from \"../types\";\n\ninterface UseFocusRestoreArgs {\n  mode: DetailPanelMode;\n  rootRef: React.RefObject<HTMLDivElement | null>;\n  bodyRef: React.RefObject<HTMLDivElement | null>;\n}\n\nconst FOCUSABLE =\n  'input, textarea, select, button, [tabindex]:not([tabindex=\"-1\"]), [contenteditable=\"true\"]';\n\nexport function useFocusRestore({\n  mode,\n  rootRef,\n  bodyRef,\n}: UseFocusRestoreArgs): {\n  captureTrigger: () => void;\n} {\n  const triggerIdRef = useRef<string | null>(null);\n  const lastModeRef = useRef<DetailPanelMode>(mode);\n\n  const captureTrigger = () => {\n    if (typeof document === \"undefined\") return;\n    const active = document.activeElement;\n    if (active instanceof HTMLElement && active.id) {\n      triggerIdRef.current = active.id;\n    } else {\n      triggerIdRef.current = null;\n    }\n  };\n\n  useEffect(() => {\n    const previous = lastModeRef.current;\n    lastModeRef.current = mode;\n    if (previous === mode) return;\n\n    if (mode === \"edit\") {\n      const body = bodyRef.current;\n      if (!body) return;\n      const focusable = body.querySelector<HTMLElement>(FOCUSABLE);\n      (focusable ?? body).focus();\n      return;\n    }\n    if (mode === \"read\" && previous === \"edit\") {\n      const id = triggerIdRef.current;\n      if (id && typeof document !== \"undefined\") {\n        const target = document.getElementById(id);\n        if (target instanceof HTMLElement) {\n          target.focus();\n          triggerIdRef.current = null;\n          return;\n        }\n      }\n      const root = rootRef.current;\n      root?.focus();\n    }\n  }, [mode, bodyRef, rootRef]);\n\n  return { captureTrigger };\n}\n",
      "type": "registry:component",
      "target": "components/detail-panel/hooks/use-focus-restore.ts"
    },
    {
      "path": "src/registry/components/feedback/detail-panel/lib/selection-key.ts",
      "content": "import type { DetailPanelSelection } from \"../types\";\n\nexport const EMPTY_SELECTION_KEY = \"__empty__\";\n\nexport function keyForSelection(\n  selection: DetailPanelSelection | null,\n): string {\n  if (!selection) return EMPTY_SELECTION_KEY;\n  return `${selection.type}:${selection.id}`;\n}\n",
      "type": "registry:component",
      "target": "components/detail-panel/lib/selection-key.ts"
    },
    {
      "path": "src/registry/components/feedback/detail-panel/parts/detail-panel-actions.tsx",
      "content": "\"use client\";\n\nimport type {\n  DetailPanelActionsProps,\n  DetailPanelActionsRenderFn,\n} from \"../types\";\nimport { useDetailPanel } from \"./detail-panel-context\";\nimport { cn } from \"@/lib/utils\";\n\nfunction isRenderFn(\n  children: DetailPanelActionsProps[\"children\"],\n): children is DetailPanelActionsRenderFn {\n  return typeof children === \"function\";\n}\n\nexport function DetailPanelActions({\n  children,\n  position = \"footer\",\n  className,\n}: DetailPanelActionsProps) {\n  const { mode, setMode, canEdit } = useDetailPanel();\n  const content = isRenderFn(children)\n    ? children({ mode, setMode, canEdit })\n    : children;\n\n  const isFooter = position === \"footer\";\n\n  return (\n    <div\n      role=\"toolbar\"\n      className={cn(\n        \"z-10 flex items-center justify-end gap-2 bg-background\",\n        isFooter\n          ? \"sticky bottom-0 border-t border-border px-4 py-3\"\n          : \"ml-auto\",\n        className,\n      )}\n    >\n      {content}\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/detail-panel/parts/detail-panel-actions.tsx"
    },
    {
      "path": "src/registry/components/feedback/detail-panel/parts/detail-panel-body.tsx",
      "content": "\"use client\";\n\nimport type { DetailPanelBodyProps } from \"../types\";\nimport { useDetailPanel } from \"./detail-panel-context\";\nimport { cn } from \"@/lib/utils\";\n\nexport function DetailPanelBody({\n  children,\n  className,\n}: DetailPanelBodyProps) {\n  const { bodyRef } = useDetailPanel();\n  return (\n    <div\n      ref={bodyRef}\n      tabIndex={-1}\n      className={cn(\n        \"flex-1 overflow-y-auto px-4 py-4 outline-none\",\n        className,\n      )}\n    >\n      {children}\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/detail-panel/parts/detail-panel-body.tsx"
    },
    {
      "path": "src/registry/components/feedback/detail-panel/parts/detail-panel-context.tsx",
      "content": "\"use client\";\n\nimport { createContext, useContext } from \"react\";\nimport type { DetailPanelContextValue } from \"../types\";\n\nexport const DetailPanelContext =\n  createContext<DetailPanelContextValue | null>(null);\n\nexport function useDetailPanel(): DetailPanelContextValue {\n  const ctx = useContext(DetailPanelContext);\n  if (!ctx) {\n    throw new Error(\n      \"DetailPanel.Header / .Body / .Actions must be used inside <DetailPanel>.\",\n    );\n  }\n  return ctx;\n}\n",
      "type": "registry:component",
      "target": "components/detail-panel/parts/detail-panel-context.tsx"
    },
    {
      "path": "src/registry/components/feedback/detail-panel/parts/detail-panel-empty-state.tsx",
      "content": "import { Info } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\n\ninterface DetailPanelEmptyStateProps {\n  title?: string;\n  description?: string;\n  className?: string;\n}\n\nexport function DetailPanelEmptyState({\n  title = \"Nothing selected\",\n  description = \"Select an item to view details.\",\n  className,\n}: DetailPanelEmptyStateProps) {\n  return (\n    <div\n      role=\"status\"\n      className={cn(\n        \"flex h-full flex-col items-center justify-center gap-2 p-6 text-center\",\n        className,\n      )}\n    >\n      <Info aria-hidden=\"true\" className=\"h-8 w-8 text-muted-foreground\" />\n      <p className=\"text-sm font-medium text-foreground\">{title}</p>\n      <p className=\"text-xs text-muted-foreground\">{description}</p>\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/detail-panel/parts/detail-panel-empty-state.tsx"
    },
    {
      "path": "src/registry/components/feedback/detail-panel/parts/detail-panel-error.tsx",
      "content": "import { AlertCircle } from \"lucide-react\";\nimport { Button } from \"@/components/ui/button\";\nimport type { DetailPanelError as DetailPanelErrorShape } from \"../types\";\n\ninterface DetailPanelErrorProps {\n  error: DetailPanelErrorShape;\n}\n\nexport function DetailPanelError({ error }: DetailPanelErrorProps) {\n  return (\n    <div\n      role=\"alert\"\n      className=\"flex h-full flex-col items-center justify-center gap-3 p-6 text-center\"\n    >\n      <AlertCircle aria-hidden=\"true\" className=\"h-8 w-8 text-destructive\" />\n      <p className=\"text-sm font-medium text-foreground\">{error.message}</p>\n      {error.retry ? (\n        <Button variant=\"outline\" size=\"sm\" onClick={error.retry}>\n          Try again\n        </Button>\n      ) : null}\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/detail-panel/parts/detail-panel-error.tsx"
    },
    {
      "path": "src/registry/components/feedback/detail-panel/parts/detail-panel-header.tsx",
      "content": "\"use client\";\n\nimport type { DetailPanelHeaderProps } from \"../types\";\nimport { useDetailPanel } from \"./detail-panel-context\";\nimport { cn } from \"@/lib/utils\";\n\nexport function DetailPanelHeader({\n  children,\n  sticky = true,\n  className,\n}: DetailPanelHeaderProps) {\n  // Verify Provider context exists; throws standard \"must be inside <DetailPanel>\" if not.\n  useDetailPanel();\n  return (\n    <div\n      className={cn(\n        \"z-10 flex items-center justify-between gap-3 border-b border-border bg-background px-4 py-3\",\n        sticky ? \"sticky top-0\" : undefined,\n        className,\n      )}\n    >\n      {children}\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/detail-panel/parts/detail-panel-header.tsx"
    },
    {
      "path": "src/registry/components/feedback/detail-panel/parts/detail-panel-skeleton.tsx",
      "content": "import { Skeleton } from \"@/components/ui/skeleton\";\n\nexport function DetailPanelSkeleton() {\n  return (\n    <div aria-hidden=\"true\" className=\"flex h-full flex-col\">\n      <div className=\"border-b border-border p-4\">\n        <Skeleton className=\"h-6 w-3/5\" />\n        <Skeleton className=\"mt-2 h-4 w-2/5\" />\n      </div>\n      <div className=\"flex-1 space-y-4 p-4\">\n        <Skeleton className=\"h-20 w-full\" />\n        <Skeleton className=\"h-20 w-full\" />\n        <Skeleton className=\"h-20 w-full\" />\n      </div>\n      <div className=\"flex justify-end gap-2 border-t border-border p-4\">\n        <Skeleton className=\"h-9 w-20\" />\n        <Skeleton className=\"h-9 w-20\" />\n      </div>\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/detail-panel/parts/detail-panel-skeleton.tsx"
    },
    {
      "path": "src/registry/components/feedback/detail-panel/parts/selection-announcer.tsx",
      "content": "import { EMPTY_SELECTION_KEY } from \"../lib/selection-key\";\n\ninterface SelectionAnnouncerProps {\n  selectionKey: string;\n}\n\nexport function SelectionAnnouncer({ selectionKey }: SelectionAnnouncerProps) {\n  if (selectionKey === EMPTY_SELECTION_KEY) {\n    return (\n      <div\n        role=\"status\"\n        aria-live=\"polite\"\n        aria-atomic=\"true\"\n        className=\"sr-only\"\n      />\n    );\n  }\n  return (\n    <div\n      key={selectionKey}\n      role=\"status\"\n      aria-live=\"polite\"\n      aria-atomic=\"true\"\n      className=\"sr-only\"\n    >\n      Selection changed\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/detail-panel/parts/selection-announcer.tsx"
    }
  ],
  "categories": [
    "feedback",
    "graph-system"
  ],
  "type": "registry:block"
}