Types
All types are exported from penciled, and the engine’s types from penciled/headless too.
import type { Annotation, AnnotationStorage, AnnotateOptions, Annotator } from 'penciled';Annotation
Section titled “Annotation”A single highlight, optionally with a note. This is what getAll() returns, what events receive and what storage adapters save.
interface Annotation { version: 1; id: string; quote: string; prefix: string; suffix: string; start: number; end: number; color: string; note?: string; editable?: boolean; meta?: Record<string, unknown>; createdAt: string; updatedAt: string;}| Field | Description |
|---|---|
version |
The shape’s version. Always 1 for now; older shapes will be migrated when it changes. |
id |
A UUID. |
quote |
The annotated text, with whitespace runs collapsed to one space. |
prefix, suffix |
Up to 32 characters of text before and after the quote, used to find it again. |
start, end |
The quote’s position in the container’s whitespace-collapsed text, in UTF-16 code units. |
color |
A color name, such as 'yellow'. Unknown names are painted with defaultColor. |
note |
Plain text. Always displayed as text, never as HTML. |
editable |
false stops readers from changing or removing it. Defaults to true. |
meta |
Your own data, for example a user id. Stored and returned untouched. |
createdAt, updatedAt |
ISO 8601 timestamps. |
The shape is flat on purpose, so it’s easy to store in a database table or a JSON column. To exchange annotations with other tools, convert them to W3C Web Annotations.
AnnotationInit
Section titled “AnnotationInit”The fields you can set with create().
interface AnnotationInit { color?: string; note?: string; editable?: boolean; meta?: Record<string, unknown>;}AnnotationPatch
Section titled “AnnotationPatch”The fields you can change with update(). A note of null or '' removes the note.
interface AnnotationPatch { color?: string; note?: string | null; editable?: boolean; meta?: Record<string, unknown>;}AnnotationStorage
Section titled “AnnotationStorage”A persistence adapter. Pass one as the storage option.
interface AnnotationStorage { load(documentId: string): Promise<Annotation[]>; save(documentId: string, annotations: Annotation[]): Promise<void>; // Optional. When present, used instead of `save` for single changes. create?(documentId: string, annotation: Annotation): Promise<void>; update?(documentId: string, annotation: Annotation): Promise<void>; delete?(documentId: string, id: string): Promise<void>;}loadis called once, when the instance is created. Return[]when there’s nothing stored.savereceives every annotation in the document. It is enough on its own.create,updateanddeletelet you send only what changed, which suits a REST API. Each one you implement replacessavefor that kind of change. Changes to the same annotation withinsaveDelayare combined: creating and then editing one annotation sends a singlecreate.- Nothing is saved until
loadhas finished, so a slowloadcan’t be overwritten. - A rejected promise goes to
onError. penciled doesn’t retry.
See Custom storage for a complete example.
ExportData
Section titled “ExportData”What export() returns and import() accepts.
interface ExportData { version: 1; documentId: string; annotations: Annotation[];}SelectContext
Section titled “SelectContext”What the select event and custom actions receive.
interface SelectContext { text: string; range: Range; annotation?: Annotation; editable: boolean;}A highlight color for the colors option.
interface Color { name: string; light?: string; // without it, the value comes from your CSS (--penciled-<name>) dark?: string; // defaults to `light` label?: string; // accessible name, defaults to `name`}Action
Section titled “Action”A custom toolbar action for the actions option. The toolbar closes after onSelect runs.
interface Action { id: string; label: string; icon?: string | Element; // SVG markup or an element; without one, the label is shown onSelect(ctx: SelectContext): void;}label is used as the button’s accessible name and tooltip. id is set as data-penciled-action on the button, for styling. Custom actions are shown even in read-only mode, and ctx.editable says whether the reader may change the annotation.
BuiltInAction
Section titled “BuiltInAction”type BuiltInAction = 'highlight' | 'note' | 'copy' | 'remove';Placement
Section titled “Placement”type Placement = 'top' | 'bottom' | 'top-start' | 'top-end' | 'bottom-start' | 'bottom-end';A side of the text, optionally aligned with the start or end of the nearest line.
Labels
Section titled “Labels”Every string in the UI, for the labels option. {name} placeholders are filled in.
| Key | Default | Used for |
|---|---|---|
toolbar |
Annotation |
The toolbar’s accessible name |
highlight |
Highlight {color} |
Color buttons. {color} is the color’s label |
note |
Add note |
Note button |
editNote |
Edit note |
Note button, when there is a note |
copy |
Copy |
Copy button |
copied |
Copied |
Announced after copying |
remove |
Remove |
Remove button |
noteField |
Note |
The note field’s accessible name |
notePlaceholder |
Add a note… |
The note field’s placeholder |
save |
Save |
Save button |
cancel |
Cancel |
Cancel button |
position |
Annotation {index} of {total}: {text} |
Announced when moving between annotations with the keyboard |
empty |
No annotations |
Announced when there are none to move to |
type Theme = 'light' | 'dark' | 'auto';