File size: 2,671 Bytes
28ad89a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | import type { CompactIdentity } from './identity'
/**
* Numbered pin stack for comment mode. Saving a pin only appends — it never
* sends a turn. Numbers are assigned 1..N in add order and stay put if a pin
* is removed, so Comment 3 in the composer still matches marker 3 on the page.
*/
export type AnnotatePinKind = 'area' | 'element'
export interface AnnotateRect {
height: number
width: number
x: number
y: number
}
/** What a pin knows about its element. One shape, owned by `identity`. */
export type AnnotateIdentity = CompactIdentity
export interface AnnotatePin {
id: string
identity?: AnnotateIdentity
imageDataUrl: string
kind: AnnotatePinKind
note: string
number: number
pageTitle: string
pageUrl: string
rect: AnnotateRect
}
export interface AnnotateStack {
nextNumber: number
pins: AnnotatePin[]
}
export type AnnotatePinDraft = Omit<AnnotatePin, 'id' | 'number'> & { id?: string }
export function emptyAnnotateStack(): AnnotateStack {
return { nextNumber: 1, pins: [] }
}
export function addAnnotatePin(stack: AnnotateStack, draft: AnnotatePinDraft): AnnotateStack {
const pin: AnnotatePin = {
...draft,
id: draft.id || `annotate-${stack.nextNumber}`,
number: stack.nextNumber
}
return {
nextNumber: stack.nextNumber + 1,
pins: [...stack.pins, pin]
}
}
export function updateAnnotatePinNote(stack: AnnotateStack, id: string, note: string): AnnotateStack {
return {
...stack,
pins: stack.pins.map(pin => (pin.id === id ? { ...pin, note } : pin))
}
}
export function removeAnnotatePin(stack: AnnotateStack, id: string): AnnotateStack {
return {
...stack,
pins: stack.pins.filter(pin => pin.id !== id)
}
}
export function clearAnnotateStack(stack: AnnotateStack): AnnotateStack {
void stack
return emptyAnnotateStack()
}
/** Clear a flushed batch while keeping comment numbers unique in this page session. */
export function clearAnnotatePins(stack: AnnotateStack): AnnotateStack {
return { ...stack, pins: [] }
}
export interface AnnotateSession {
/** In-progress pick that has a crop but no saved note yet. */
draft: AnnotatePinDraft | null
mode: boolean
overlayLive: boolean
stack: AnnotateStack
}
export function emptyAnnotateSession(): AnnotateSession {
return { draft: null, mode: false, overlayLive: false, stack: emptyAnnotateStack() }
}
export function beginAnnotateMode(session: AnnotateSession): AnnotateSession {
return { ...session, mode: true, overlayLive: true }
}
export function endAnnotateMode(session: AnnotateSession): AnnotateSession {
return { ...session, draft: null, mode: false, overlayLive: false }
}
|