Image source adapter
retab-ui/image-sourceFreeComponent
Bridges the document-source model to the image viewer: imageAnchorToTarget, rotateImageArea, and renderImageSourceOverlay for image_bbox anchors.
Install it
npx shadcn@latest add https://ui.retab.com/r/image-source.jsonSource
1"use client";23import * as React from "react";45import type { Source, SourceAnchor, SourceArea } from "@/lib/document-source";6import { normalizeRotation, rotateNormalizedBox } from "@/lib/image-geometry";7import {8 type ImageFrameOverlayProps,9 type ImageViewerHandle,10} from "@/components/ui/image-viewer-types";1112const HIGHLIGHT_CLASS =13 "pointer-events-none absolute z-10 rounded-[2px] border border-primary/70 bg-primary/12 shadow-[0_4px_16px_rgb(0_0_0_/_8%)]";1415export interface SourceTarget {16 scrollTo?: (source: Source, options: { behavior: ScrollBehavior }) => void;17}1819export interface ImageSourceTarget {20 frame: number;21 area: SourceArea;22}2324/** A normalized image/pdf bbox anchor → the frame and percentage box to target. */25export function imageAnchorToTarget(26 anchor: SourceAnchor,27): ImageSourceTarget | undefined {28 if (anchor.kind === "image_bbox" || anchor.kind === "pdf_bbox") {29 const frame = imageAnchorFrame(anchor);30 if (!isValidNormalizedBox(anchor)) return undefined;31 if (frame === undefined) return undefined;32 return {33 frame,34 area: {35 left: anchor.left * 100,36 top: anchor.top * 100,37 width: anchor.width * 100,38 height: anchor.height * 100,39 },40 };41 }42 return undefined;43}4445/**46 * The 1-based frame a bbox anchor lives on, or undefined if it isn't a raster47 * bbox. `image_bbox` defaults to frame 1 (single image); a multi-frame TIFF (or48 * a rasterized slide deck) sets `page` explicitly.49 */50function imageAnchorFrame(anchor: SourceAnchor): number | undefined {51 if (anchor.kind === "image_bbox") {52 const frame = anchor.page ?? 1;53 return isPositiveInteger(frame) ? frame : undefined;54 }55 if (anchor.kind === "pdf_bbox") {56 return isPositiveInteger(anchor.page) ? anchor.page : undefined;57 }58 return undefined;59}6061function isPositiveInteger(value: number): boolean {62 return Number.isInteger(value) && value >= 1;63}6465function isValidNormalizedBox({66 left,67 top,68 width,69 height,70}: {71 left: number;72 top: number;73 width: number;74 height: number;75}): boolean {76 return (77 Number.isFinite(left) &&78 Number.isFinite(top) &&79 Number.isFinite(width) &&80 Number.isFinite(height) &&81 left >= 0 &&82 top >= 0 &&83 width > 0 &&84 height > 0 &&85 left + width <= 1 &&86 top + height <= 187 );88}8990export function rotateImageArea(91 area: SourceArea,92 rotation: number,93): SourceArea {94 const rotated = rotateNormalizedBox(95 {96// … truncated
What it pulls in
Other registry items
Files it writes
More from retab-ui
All 130 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Alertalert | retab-ui | Components | Free | 1 dep | |
| Alert Dialogalert-dialog | retab-ui | Components | Free | 1 dep | |
| Aspect Ratioaspect-ratio | retab-ui | Components | Free | no deps | |
| Attachment Sidebarattachment-sidebar | retab-ui | Components | Free | 1 dep | |
| Autocompleteautocomplete | retab-ui | Components | Free | 2 deps | |
| Calendarcalendar | retab-ui | Components | Free | 2 deps | |
| Code Viewercode-viewer | retab-ui | Components | Free | 2 deps · 32 files | |
| Commandcommand | retab-ui | Components | Free | 2 deps |
