Skip to content
Sign in
Image studioText in image editMenu

Text in image editPro

Edit the text rendered inside an image: every line a detector found gets a box on the image and a field in a list. Type over a line in place, at its size and angle; the result fades in only inside that line while the others stay editable.

ProA generated poster whose four lines are found one by one. Click a line or press Enter on it to type over it in place; the model letters it again and only that line fades in.

Installation

$ pnpm dlx shadcn@latest add @chatcn-pro/text-in-image-edit

Usage

tsx
import { TextInImageEdit } from "@/components/ui/chatcn/pro/text-in-image-edit"

<TextInImageEdit
  src={image.url}
  alt={image.alt}
  width={image.width}
  height={image.height}
  regions={detected.lines}
  detecting={detected.pending}
  onEdit={async (line, text) => {
    const next = await editText(image.id, line.id, text)
    return { src: next.url }
  }}
/>

Each region is one line of lettering: its text, a box in fractions of the image, an optional angle for tilted lines and the cap height, so the inline field opens at the size of the lettering it replaces. onEdit sends one line at a time; resolve with the new image and the component fades it in inside that line's box first, then swaps the full picture with no visible jump. Throw an Error whose message says how to fix the problem, and the line shows it with Retry. Lines can arrive one by one while detecting is on, and status, error and pendingText on a region reflect edits you run yourself.

Examples

States

ProFinding text, no text found, a failed edit on a lit sign with Retry, one line applying, disabled and a broken image.

Props

PropTypeNotes
src, altstringThe image with the lettering
width, height, aspectRationumberFrame shape before load
regions{ id, text, box, angle?, fontSize?, label?, dir?, status?, error?, pendingText? }[]Every line found. box is { x, y, width, height } in fractions of the image
detecting, detectingLabelboolean, stringA "Finding text" chip after 300 ms; boxes fade in as lines arrive
onEdit(region, text) => Promise<{ src? } | undefined>Re-render the image with one line changed. Throw to show a failure
onRegionChange({ id, text, original }) => voidAfter a line's applied text changes
showBoxes, defaultShowBoxes, onShowBoxesChangebooleanOutline every line instead of only on hover and focus. Touch screens always outline them
listbooleanThe list of lines beside or below the image. Default true
disabledbooleanNothing can be edited; boxes lose their outlines
emptyTitle, emptyDescription, footerstring, ReactNodeThe no text state and a slot below

Keyboard

KeyAction
TabMoves through the lines on the image, then the fields in the list
Enter on a lineOpens a field over it with the text selected
Enter in a fieldApplies the new text. Ignored while an input method is composing
EscCancels: the field closes and focus returns to the line

Accessibility

  • Each box is a button named by the text it holds, "After dark, edit headline", so the name starts with what is visible in the image.
  • The list is the full path without a pointer: a labeled field per line with Apply, Retry and Revert buttons whose names start with their visible words.
  • Applying, failed and edited are shown with a spinner, an icon and a word, never color alone. Errors are tied to their field and say how to fix the problem.
  • Results are announced politely once per line, not while the model works.
  • Fields take the line's writing direction, so right to left lettering edits naturally.

Motion

  • Boxes fade in over 150 ms as detection finds them. The inline field opens at once: it is triggered by a click or Enter.
  • A finished edit fades in over 220 ms inside the line's rotated box only, then the full image swaps underneath with no visible change.
  • Nothing moves, so reduced motion keeps the same short fades.