/** * Bridge script injected into the HTML viewer iframe. * * Handles text selection, annotation marks, theme updates, and resize * notifications. Communicates with the parent via postMessage using a * "plannotator-bridge-*" message protocol. * * This is a string constant — it gets prepended to the iframe's srcdoc. * No external dependencies. */ /** * Reads only viewer-namespaced \`--pn-*\` variables (with fallbacks): arbitrary * documents may define bare token names like \`--accent\` for themselves, and the * viewer must never depend on — or collide with — the author's namespace. */ export const ANNOTATION_HIGHLIGHT_CSS = ` /* Committed annotation visuals (highlight rectangles + numbered placed * markers) render inside a shadow-rooted fixed overlay host — see OVERLAY_CSS * in the bridge script. Nothing annotation-related is ever wrapped into or * styled onto the author's own elements. */ /* Vim pinpoint target tint. The MOUSE pinpoint path no longer mutates author * elements — it draws the dedicated overlay box below — but keyboard (vim) * navigation keeps this class-based visual. */ .plannotator-pinpoint-hover { background-color: oklch(from var(--pn-focus-highlight, #4493f8) l c h / 0.12) !important; border-radius: 3px; cursor: pointer !important; } /* SVG groups can't render a CSS background, so use a soft glow instead. */ .plannotator-pinpoint-hover:is(g, svg) { filter: drop-shadow(0 0 4px oklch(from var(--pn-focus-highlight, #4493f8) l c h / 0.55)); } /* Mouse pinpoint hover: a fixed-position outline box sized to the hovered * element's rect. Never a class/style write on the page's own elements. */ [data-plannotator-pinpoint-box] { position: fixed; z-index: 2147483643; pointer-events: none; display: none; box-sizing: border-box; border: 2px solid oklch(from var(--pn-focus-highlight, #4493f8) l c h / 0.85); border-radius: 5px; background: oklch(from var(--pn-focus-highlight, #4493f8) l c h / 0.06); } [data-plannotator-pinpoint-box].pn-pin-enter { animation: pn-pinpoint-in 0.12s ease-out; } [data-plannotator-pinpoint-box][data-pinned] { border-color: var(--pn-accent, #d97757); background: oklch(from var(--pn-accent, #d97757) l c h / 0.08); } @keyframes pn-pinpoint-in { from { opacity: 0; transform: scale(0.985); } to { opacity: 1; transform: scale(1); } } /* Pinpoint mode affordance: crosshair everywhere. Placed markers live in the * shadow overlay and keep their own pointer cursor there. */ body[data-plannotator-pinpoint-cursor], body[data-plannotator-pinpoint-cursor] * { cursor: crosshair !important; } /* A plain click on a link with a real destination follows it while armed * (Cmd/Ctrl-click pins it), so such a link keeps the pointer that says so, * until the modifier is held. A link that goes nowhere (no href, "", "#", * javascript:) is not followed and keeps the crosshair. SVG links may carry * only xlink:href, hence the any-namespace attribute. */ body[data-plannotator-pinpoint-cursor]:not([data-plannotator-pin-modifier]) :is(a, area)[href]:not([href=""]):not([href="#"]):not([href^="javascript:" i]), body[data-plannotator-pinpoint-cursor]:not([data-plannotator-pin-modifier]) :is(a, area)[href]:not([href=""]):not([href="#"]):not([href^="javascript:" i]) *, body[data-plannotator-pinpoint-cursor]:not([data-plannotator-pin-modifier]) svg a:not([href])[*|href]:not([*|href=""]):not([*|href="#"]):not([*|href^="javascript:" i]), body[data-plannotator-pinpoint-cursor]:not([data-plannotator-pin-modifier]) svg a:not([href])[*|href]:not([*|href=""]):not([*|href="#"]):not([*|href^="javascript:" i]) * { cursor: pointer !important; } /* Touch, armed: a tap on a link follows it and a press-and-hold pins it for * a comment. iOS's link callout would take that hold, so it is off on links * while armed, once a touch is seen (data-plannotator-touch) or on screens * without hover. The callout is all: link text stays selectable, so a drag * across a link (a trackpad, a touchscreen laptop) still quotes its words. * Only while a hold on a link is in progress (data-plannotator-link-hold, * from its touchstart to its release) is selection off too, so the platform's * long-press does not start a native text selection under the finger. */ body[data-plannotator-pinpoint-cursor][data-plannotator-touch] :is(a, area), body[data-plannotator-pinpoint-cursor][data-plannotator-touch] :is(a, area) *, body[data-plannotator-pinpoint-cursor][data-plannotator-touch] svg a, body[data-plannotator-pinpoint-cursor][data-plannotator-touch] svg a * { -webkit-touch-callout: none !important; } @media (hover: none) { body[data-plannotator-pinpoint-cursor] :is(a, area), body[data-plannotator-pinpoint-cursor] :is(a, area) *, body[data-plannotator-pinpoint-cursor] svg a, body[data-plannotator-pinpoint-cursor] svg a * { -webkit-touch-callout: none !important; } } body[data-plannotator-link-hold] :is(a, area), body[data-plannotator-link-hold] :is(a, area) *, body[data-plannotator-link-hold] svg a, body[data-plannotator-link-hold] svg a * { -webkit-touch-callout: none !important; -webkit-user-select: none !important; user-select: none !important; } /* Armed pinpoint over an EMBEDDED local document: the embed is one element * from the outer page's point of view, and the bridge is never injected into a * nested frame, so a click inside it would simply vanish into another document. * Making frames transparent to the pointer while armed is what lets that click * pin the