shadimage
extend
blockbounding-box-citations-blocksource: ok · hit

Bounding Box Citations

An extraction citation block linked to source PDF bounding boxes.

npx shadcn add "https://ui.extend.ai/r/bounding-box-citations-block.json"
free

No selection tags yet — empty means no evidence, not lack of support.

Dependencies

Bundle weight · light

Registry dependencies

@extend/bounding-box-citations@extend/pdf-block-resizable-shell@extend/pdf-viewer

Files1 · source cached 10/14/2026, 11:58:34 AM

registry/new-york-v4/blocks/bounding-box-citations-block.tsx
"use client"

import * as React from "react"

import {
  findReviewField,
  getMetadataLocation,
  getReviewFieldLocation,
  getReviewLocationKey,
  HumanReviewHighlight,
  HumanReviewPanel,
  REVIEW_FIELDS,
  type HumanReviewTheme,
  type JsonValue,
  type ReviewField,
  type ReviewLocation,
  type ReviewMetadataEntry,
} from "@/components/extend/bounding-box-citations"
import { PdfBlockResizableShell } from "@/components/extend/pdf-block-resizable-shell"
import { PDFViewer, type PDFViewerHandle } from "@/components/extend/pdf-viewer"

const DEFAULT_ZOOM = 1
export function HumanReviewBlock({
  file,
  fields = REVIEW_FIELDS,
  className,
  metadata,
  resolveArrayItemMetadataPath,
  resolveLocation,
  showExpected = true,
  theme,
}: {
  file?: string
  fields?: ReviewField[]
  className?: string
  metadata?: Record<string, ReviewMetadataEntry>
  resolveArrayItemMetadataPath?: (
    metadataPath: string,
    rowIndex: number,
    rowValue: JsonValue
  ) => string | undefined
  resolveLocation?: (metadataPath: string) => ReviewLocation | undefined
  showExpected?: boolean
  theme?: HumanReviewTheme
}) {
  const [activeFieldKey, setActiveFieldKey] = React.useState(fields[0]?.key)
  const [hoverLocation, setHoverLocation] =
    React.useState<ReviewLocation | null>(null)
  const viewerRef = React.useRef<PDFViewerHandle>(null)
  const hoverLocationKeyRef = React.useRef<string | null>(null)
  const resolveFieldLocation = React.useCallback(
    (metadataPath: string) =>
      resolveLocation?.(metadataPath) ??
      getMetadataLocation(metadata, metadataPath),
    [metadata, resolveLocation]
  )
  const activeField = findReviewField(fields, activeFieldKey) ?? fields[0]
  const activeLocation =
    hoverLocation ?? getReviewFieldLocation(activeField, resolveFieldLocation)
  if (!activeFieldKey && fields[0]) {
    setActiveFieldKey(fields[0].key)
  }
  const focusField = React.useCallback(
    (field: ReviewField) => {
      if (field.key === activeFieldKey) return
      setActiveFieldKey(field.key)
      const location = getReviewFieldLocation(field, resolveFieldLocation)
      if (location) {
        viewerRef.current?.scrollToPageArea(location.page, location.area)
      }
    },
    [activeFieldKey, resolveFieldLocation]
  )
  const handleLocationHover = React.useCallback((location?: ReviewLocation) => {
    setHoverLocation(location ?? null)
    const locationKey = getReviewLocationKey(location)
    if (!location) {
      hoverLocationKeyRef.current = null
      return
    }
    if (locationKey === hoverLocationKeyRef.current) return
    hoverLocationKeyRef.current = locationKey
    viewerRef.current?.scrollToPageArea(location.page, location.area, {
      behavior: "auto",
    })
  }, [])
  return (
    <PdfBlockResizableShell
      autoSaveId="pdf-block-bounding-box-citations"
      className={className}
      rightDefaultSize={42}
      rightMaxSize={60}
      rightMinSize={30}
      left={
        <PDFViewer
          ref={viewerRef}
          src={file}
          defaultZoom={DEFAULT_ZOOM}
          renderPageOverlay={({ pageNumber }) =>
            activeLocation?.page === pageNumber ? (
              <HumanReviewHighlight location={activeLocation} />
            ) : null
          }
        />
      }
      right={
        <aside className="min-h-0 bg-background">
          <HumanReviewPanel
            fields={fields}
            activeFieldKey={activeField?.key}
            className="h-full min-h-0"
            theme={theme}
            showExpected={showExpected}
            onFieldFocus={focusField}
            onLocationHover={handleLocationHover}
            resolveArrayItemMetadataPath={resolveArrayItemMetadataPath}
            resolveLocation={resolveFieldLocation}
          />
        </aside>
      }
    />
  )
}

More from extend

Browse all components in this library →