Annotation Dialog
The annotation dialog lets users place interactive pins on any image inside the Uhuu Editor — without touching the source image. It opens a full-screen annotation editor where users can add markers, text labels, and callout cards at precise positions.
Why SVG Overlay Instead of Merging Into the Image
Most annotation tools burn annotations directly into a rasterized copy of the image. Uhuu takes a different approach: annotations are rendered as an SVG layer on top of the base image, and saved as structured data.
This matters for three reasons:
Non-destructive editing. Because annotations are a separate data record, not pixels baked into the image, users can always reposition, relabel, recolor, or delete individual pins. Changing the underlying photo does not erase the annotation layout — the pins simply reappear on the new image.
Crisp print output. Uhuu templates produce PDFs. Rasterized annotations degrade at high DPI because they were created at screen resolution. SVG annotations are mathematical shapes — circles, lines, text — that the PDF renderer reproduces at full vector quality at any print size, with no blurring or pixelation.
Annotations are data. The saved payload is a structured JSON record (schema: "uhuu.annotation.v1"), not an image blob. This means annotations can be read, transformed, re-rendered, or exported as a standalone SVG overlay by the template — for instance to composite the base photo and the annotation layer separately during PDF generation.
Annotation Types
The editor has two annotation tools:
Callout
A rich card connected to an anchor point by a line. The most expressive type.
- Modes:
minimal(compact badge) ordetailed(full card with title, description, optional icon/image, and stat rows) - Connector: a line from the card to the anchor point; can be toggled per callout
- Visual: attach an icon from the gallery to the card
Badge
A floating text label attached to a point.
- Variants:
pill,rounded,rectangle - Sizes:
sm,md,lg - Modes:
filloroutline
Parameters
$uhuu.editDialog({
path: 'page.heroAnnotation',
type: 'annotation',
image: 'https://example.com/photo-1200.jpg',
config: {
visualGallery: [
'https://cdn.example.com/icons/hospital.svg',
'https://cdn.example.com/icons/school.svg',
]
},
value: payload.page?.heroAnnotation
})| Parameter | Type | Description |
|---|---|---|
type | string | 'annotation' |
path | string | Payload path where annotation data is saved |
image | string | URL of the background image shown in the editor. Use a reasonably sized version (e.g. 1200 × 1200px) — not a full-resolution original |
config.visualGallery | string[] | Optional array of SVG icon URLs shown as selectable tiles in the visual picker. Defaults to a set of Phosphor icons. Pass an empty array to disable |
value | object | Current annotation data. If omitted the editor starts empty |
Saved Data Format
{
"schema": "uhuu.annotation.v1",
"annotations": [
{
"id": "abc123",
"type": "callout",
"anchorX": 42.5,
"anchorY": 31.0,
"cardX": 55.0,
"cardY": 20.0,
"mode": "detailed",
"variant": "rounded",
"color": "#3b82f6",
"scale": 1,
"content": {
"title": "Main entrance",
"description": "Access from north side",
"visual": "https://cdn.example.com/icons/building.svg"
},
"connector": { "enabled": true }
},
{
"id": "def456",
"type": "label",
"x": 68.0,
"y": 55.5,
"text": "Parking",
"variant": "pill",
"size": "sm",
"mode": "fill",
"color": "#0f172a"
}
],
"annotationSvg": "<svg>…</svg>"
}All positions (x, y, anchorX/Y, cardX/Y) are percentages (0–100) relative to the image dimensions. annotationSvg is a pre-rendered SVG overlay — use it directly in templates without re-rendering.
Using with ImageBlock
import { ImageBlock } from 'uhuu-components'
<ImageBlock
src={imageUrl}
dialog={buildPageDialog(dataBinding, 'heroImage', { type: 'image' }, heroImage)}
annotation={buildPageDialog(
dataBinding,
'heroImageAnnotation',
{
type: 'annotation',
image: resolveImageSrc(heroImage),
visualGallery: Object.values(mapIcons)
},
page.heroImageAnnotation
)}
/>The user sees two separate edit controls on the image block: one for changing the photo, one for editing annotations. Both store at independent payload paths.
Rendering Annotations in Templates
{page.heroAnnotation?.annotationSvg && (
<div className="relative">
<img src={imageUrl} className="w-full h-full object-cover" />
<div
className="absolute inset-0"
dangerouslySetInnerHTML={{ __html: page.heroAnnotation.annotationSvg }}
/>
</div>
)}The SVG overlay renders at full vector quality in PDFs — shapes, text, and connector lines stay sharp at any print size.
Notes
annotationSvgis only present when at least one annotation exists. Always guard against its absence before rendering.- The
imageURL in the dialog config is the editor background only. The image displayed in the template is managed separately. - Positions are percentages, so annotations stay correctly placed if the image block changes size across breakpoints.
visualGalleryicons must be publicly accessible SVG URLs, not local paths.