/**
* 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