@8ux-co/eelzap 0.10.1 → 0.10.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/refs.ts","../src/preview/protocol.ts","../src/stega.ts","../src/preview/anchor.ts","../src/preview/overlay.ts","../src/preview/tags.ts","../src/preview/site/shortcut.ts"],"sourcesContent":["/**\n * The record and field grammar of tags (`.docs/proposals/zap-cms-v2.md` §2.4),\n * in a module of its own so the light entries (`./fields`, `./react`, the\n * boot) can name records without pulling in the preview protocol.\n * No imports, no side effects.\n */\n\n/**\n * A record reference in its canonical string form: `collectionKey/slug` for an\n * entry, `doc:documentKey` for a document (the tag grammar, §2.4).\n */\nexport type RecordRef = string\n\n/** Zap's field key pattern. */\nexport const FIELD_KEY_RE = /^[a-z][a-z0-9_]{0,127}$/\n/** Entry and document references; keys and slugs are `[a-z0-9]+(?:-[a-z0-9]+)*`. */\nconst ENTRY_REF_RE = /^([a-z0-9]+(?:-[a-z0-9]+)*)\\/([a-z0-9]+(?:-[a-z0-9]+)*)$/\nconst DOC_REF_RE = /^doc:([a-z0-9]+(?:-[a-z0-9]+)*)$/\nconst MAX_REF_LENGTH = 300\n\nexport type ParsedRecordRef =\n | { kind: 'entry'; collection: string; slug: string }\n | { kind: 'document'; key: string }\n\nexport function parseRecordRef(value: unknown): ParsedRecordRef | null {\n if (typeof value !== 'string' || value.length > MAX_REF_LENGTH) return null\n const doc = DOC_REF_RE.exec(value)\n if (doc) return { kind: 'document', key: doc[1]! }\n const entry = ENTRY_REF_RE.exec(value)\n if (entry) return { kind: 'entry', collection: entry[1]!, slug: entry[2]! }\n return null\n}\n\nexport function isRecordRef(value: unknown): value is RecordRef {\n return parseRecordRef(value) !== null\n}\n\n/** `zpt_` + 32 bytes in base64url, as Zap mints it (zap-cms-v2 §3.5). */\nexport const PREVIEW_TOKEN_RE = /^zpt_[A-Za-z0-9_-]{43}$/\n\n/** A preview token in the shape Zap mints; says nothing about whether it is live. */\nexport function isPreviewTokenShape(value: unknown): value is string {\n return typeof value === 'string' && PREVIEW_TOKEN_RE.test(value)\n}\n\nexport function isFieldKey(value: unknown): value is string {\n return typeof value === 'string' && FIELD_KEY_RE.test(value)\n}\n\n/**\n * What `zapAttrs` and `onValues` accept: a record reference string\n * (`blog/hola`, `doc:home`) or a delivery API entry (an item carries\n * `collection.key` and `slug`, a document `key`).\n */\nexport type EntryLike =\n | RecordRef\n | {\n slug?: string | null\n key?: string | null\n collection?: { key?: string | null } | null\n localizedSlugs?: Record<string, string | null | undefined> | null\n }\n\n/** Every reference an entry answers to (default slug first, then localized ones). */\nexport function recordRefsOf(entry: EntryLike | null | undefined): RecordRef[] {\n if (!entry) return []\n if (typeof entry === 'string') return isRecordRef(entry) ? [entry] : []\n const refs: RecordRef[] = []\n const add = (ref: string) => {\n if (isRecordRef(ref) && !refs.includes(ref)) refs.push(ref)\n }\n const collection = entry.collection?.key\n if (collection) {\n if (entry.slug) add(`${collection}/${entry.slug}`)\n for (const slug of Object.values(entry.localizedSlugs ?? {})) {\n if (slug) add(`${collection}/${slug}`)\n }\n } else if (entry.key) {\n add(`doc:${entry.key}`)\n }\n return refs\n}\n\n/**\n * The tag for one field, as attributes to spread on the element that renders\n * it: `{ 'data-zap': 'blog/hola#title' }`. Empty when the entry or the field\n * key is not one Zap can name, so a bad call never breaks the page.\n */\nexport function zapAttrs(entry: EntryLike, fieldKey: string): { 'data-zap'?: string } {\n const [ref] = recordRefsOf(entry)\n if (!ref || !isFieldKey(fieldKey)) return {}\n return { 'data-zap': `${ref}#${fieldKey}` }\n}\n","import { isFieldKey, isPreviewTokenShape, isRecordRef, type RecordRef } from '../refs'\n\n/**\n * The bridge protocol, version 1 (`.docs/proposals/zap-cms-v2.md` §2.6).\n *\n * The editor frames the customer's own site by its real URL, and the site's\n * Zap client (`live.ts`) answers. Every message is the envelope `{ source: 'eel-zap', v: 1, session, type, payload }`;\n * anything else is ignored.\n *\n * ## Hand-written validators, not zod\n *\n * The spec asks for \"a zod schema on both sides\". The browser core has a\n * 12 KB gzip budget and zero dependencies (§2.7, §7.4), and zod alone is\n * several times that, so every payload is checked here by small explicit\n * predicates instead. The EDITOR side imports these same validators\n * (`./editor`), so the two ends cannot disagree about what a valid message\n * is — one source of truth instead of a zod copy that drifts.\n *\n * Every validator is strict: wrong types, unknown enum values, strings over\n * their cap and arrays over their cap fail the WHOLE message (it is dropped\n * and counted), never coerced. Extra keys are ignored.\n *\n * ## Additions to the §2.6 table\n *\n * - `zap:zoom` (editor → page) `{ zoom }`: the panel's scale changes when the\n * panel is resized or the device switches, and the overlay divides its 2px\n * outline and 12px chip by it (ui-patterns/preview-panel.md rule 5). The page\n * cannot read its parent's CSS transform, so the editor tells it.\n * - `zap:highlight` (editor → page) `{ anchors }`: selecting a change request\n * highlights its anchors in the preview (§3.3 \"Where they show\"). The\n * anchors' selectors come from stored, user-influenced rows, so the overlay\n * re-finds them defensively (`findAnchorElement`).\n * - `zap:hello` also carries an optional `zoom` so the first paint is right.\n * - `zap:hello` carries an optional `previewToken` in Live mode (P7): the\n * `zpt_` token the editor minted for the draft-mode URL, so the client can\n * keep it in `sessionStorage` for the cookieless fallback (§2.5 \"Cookies in\n * the frame\"). The editor sends it only to the exact page origin that\n * answered `zap:ready` from the site's preview origins, the same site the\n * draft-mode URL already handed it to.\n * - Comments (§3.3 \"Anchors\", spot pins), only to a page whose `zap:ready`\n * advertised the `pins` capability (older CDN clients never see them):\n * mode `spot` (a click anywhere becomes `zap:spot` with the point as\n * fractions of the whole document), `zap:pins` (editor → page, the numbered\n * pins to draw, ≤ 50) and `zap:pin` (page → editor, a pin was clicked).\n * - Links, only to a page whose `zap:ready` advertised the `links` capability:\n * a URL field's value is `{ url }` and an EMAIL field's `{ email }`, so the\n * page writes a link's `href` (`mailto:` for an email) and leaves its label\n * alone (`values.ts`). Older clients get the bare string, as before.\n * - `zap:paused` (page → editor) `{ reason, count }`: the runaway guard\n * (`tagGuard` in `tags.ts`) stopped following the page: `cap` past 5,000\n * tagged elements, `runaway` when the count kept growing with nothing on\n * the page explaining it. The page stops writing values; the editor offers\n * a reload. Sent again after a hello, which a pause before it never reached.\n */\n\nexport const PROTOCOL_SOURCE = 'eel-zap'\n/**\n * `zap:error` code the page sends from `pagehide`: this document is going away\n * (a link, a refresh, a site script setting `location`). The editor holds\n * everything it would post until the next document says `zap:ready`, and\n * answers that one with the hello again.\n */\nexport const PAGE_UNLOAD_ERROR = 'PAGE_UNLOAD'\nexport const PROTOCOL_VERSION = 1\n/** Hard cap per message, in UTF-8 bytes of its JSON form (§2.6, §7.4). */\nexport const MAX_MESSAGE_BYTES = 256 * 1024\n/** §3.3 limits. */\nexport const MAX_ANCHORS = 20\nexport const MAX_QUOTE_EXACT = 500\nexport const MAX_QUOTE_CONTEXT = 32\nexport const MAX_SELECTOR_BYTES = 1024\nexport const MAX_URL_LENGTH = 2048\n/** Bounds on report sizes so a hostile or huge page cannot flood the editor. */\nexport const MAX_TAG_REPORT = 2000\nexport const MAX_LABELS = 500\n/** Pins drawn at once (`zap:pins`). */\nexport const MAX_PINS = 50\n\nexport type OverlayMode = 'inspect' | 'select' | 'spot' | 'off'\nexport const OVERLAY_MODES: readonly OverlayMode[] = ['inspect', 'select', 'spot', 'off']\n\nexport type Capability = 'overlay' | 'values' | 'stega' | 'pins' | 'links' | 'refresh'\n/** Every capability; a client built from this source advertises all of them. */\nexport const CAPABILITIES: readonly Capability[] = [\n 'overlay',\n 'values',\n 'stega',\n 'pins',\n 'links',\n 'refresh',\n]\n\nexport {\n FIELD_KEY_RE,\n isFieldKey,\n isPreviewTokenShape,\n PREVIEW_TOKEN_RE,\n isRecordRef,\n parseRecordRef,\n type ParsedRecordRef,\n type RecordRef,\n} from '../refs'\n\n/**\n * A value the editor sends for one field, ALREADY formatted for display.\n *\n * The browser core never formats: the editor owns the suite's formatters\n * (dates, numbers, enum labels; CLAUDE.md \"Money/dates/plurals go through\n * shared formatters\"), knows each field's type, and sends the text the page\n * should show. A bare string, number or boolean is shorthand for `{ text }`.\n * `{ url }` and `{ email }` (a URL and an EMAIL field, `links` capability) go\n * to a link's `href` rather than its text.\n */\nexport type PreviewValue =\n | string\n | number\n | boolean\n | null\n | { text: string }\n | { html: string }\n | { url: string }\n | { email: string }\n | { image: { src?: string | null; srcset?: string | null; alt?: string | null } }\n\n/** Longest EMAIL value accepted (RFC 5321's path limit). */\nexport const MAX_EMAIL_LENGTH = 320\n\n/** How the page found an element: a stega marker in its text, or a `data-zap` attribute. */\nexport type TagSource = 'stega' | 'attr'\n\nexport interface TagSummary {\n /** The canonical tag, `recordRef#fieldKey`. */\n tag: string\n recordRef: RecordRef\n fieldKey: string\n count: number\n /**\n * `attr` when at least one element carries a manual tag (a manual tag wins),\n * else `stega`. Optional on the wire: clients released before stega omit it.\n */\n source?: TagSource\n}\n\nexport interface TextQuote {\n exact: string\n prefix: string\n suffix: string\n}\n\nexport interface NormalizedRect {\n x: number\n y: number\n w: number\n h: number\n}\n\n/** The `dom` layer of an anchor (§3.3), captured by the overlay. */\nexport interface DomAnchor {\n tag: string | null\n selector: string | null\n textQuote: TextQuote | null\n rect: NormalizedRect\n viewport: { w: number; h: number }\n}\n\n/**\n * An anchor as the PAGE reports it. The editor adds the field's `id` (the page\n * knows keys, never ids) before storing it as a change-request anchor.\n */\nexport interface PageAnchor {\n field: { recordRef: RecordRef; key: string; locale: string | null } | null\n dom: DomAnchor\n pageUrl: string\n}\n\nexport interface ReadyPayload {\n version: string\n capabilities: Capability[]\n pageUrl: string\n /**\n * The site's draft-mode route as a PATH (`/api/zap-preview`), or null when\n * the site has none (§2.5, §7.5). Never an origin: the editor joins it to\n * the frame's verified origin (`draftModeUrl` in `./editor`), so a hostile\n * page cannot send a preview token to another host. Optional on the wire:\n * older clients omit it, which reads as null.\n */\n draftRoute?: string | null\n}\n\nexport interface HelloPayload {\n session: string\n siteKey: string\n locale: string\n labels: Record<string, string>\n recordRef: RecordRef\n mode: OverlayMode\n theme?: OverlayTheme\n zoom?: number\n /** Live only: the draft-mode token for the cookieless fallback. */\n previewToken?: string\n /**\n * Fields on the page that belong to ANOTHER record than `recordRef` (a\n * site-wide document in the header, a related entry): the full tag\n * (`TagSummary.tag`, `recordRef#fieldKey`) to `[label, suffix]`, the suffix\n * already localized («en Configuración»). The chip draws the label, then\n * the suffix muted. At most `MAX_LABELS`; optional, older clients ignore it.\n */\n foreign?: Record<string, [label: string, suffix: string]>\n}\n\n/** Colours the editor may override; `#rgb` / `#rrggbb` only. */\nexport interface OverlayTheme {\n outline?: string\n chip?: string\n chipText?: string\n}\n\nexport interface ValuesPayload {\n recordRef: RecordRef\n locale: string\n patch: Record<string, PreviewValue>\n}\n\nexport interface FieldRefPayload {\n recordRef: RecordRef\n fieldKey: string\n}\n\n/**\n * `zap:click`: the field, and optionally the clicked element's box in the\n * frame's viewport, CSS px before the editor's zoom (older clients omit it).\n */\nexport interface ClickPayload extends FieldRefPayload {\n rect?: { x: number; y: number; w: number; h: number }\n}\n\n/** A point as fractions (0..1) of the whole document, not the viewport (§3.3). */\nexport interface SpotPoint {\n x: number\n y: number\n}\n\n/** A spot anchor (§3.3): the point, and the document's scroll size when it was taken. */\nexport interface SpotAnchor {\n spot: SpotPoint\n viewport: { w: number; h: number }\n}\n\n/** `zap:spot`: the user clicked the page in `spot` mode. */\nexport interface SpotPayload extends SpotAnchor {\n pageUrl: string\n}\n\n/**\n * A numbered comment pin (`zap:pins`). Exactly one of `spot` (a point of the\n * document) or `dom` (an element, re-found like a highlight). `n` is the\n * number drawn on it.\n */\nexport type Pin = { id: string; n: number } & (\n | { spot: SpotPoint; dom?: undefined }\n | { dom: DomAnchor; spot?: undefined }\n)\n\n/** `zap:paused`: why the page stopped following itself, and the tagged count then. */\nexport interface PausedPayload {\n reason: 'runaway' | 'cap'\n count: number\n}\n\nexport interface PageMessages {\n 'zap:ready': ReadyPayload\n 'zap:tags': TagSummary[]\n 'zap:click': ClickPayload\n 'zap:select': { anchors: PageAnchor[] }\n 'zap:navigate': { url: string }\n 'zap:error': { code: string; detail?: string }\n 'zap:spot': SpotPayload\n 'zap:pin': { id: string }\n 'zap:paused': PausedPayload\n}\n\nexport interface EditorMessages {\n 'zap:hello': HelloPayload\n 'zap:values': ValuesPayload\n 'zap:focus-field': FieldRefPayload\n 'zap:mode': OverlayMode\n 'zap:zoom': { zoom: number }\n /**\n * Reload the page in place (`refresh` capability): for values the overlay\n * cannot place, once the draft is saved. The browser keeps the scroll and\n * the draft-mode cookie keeps the draft; the new document says ready.\n */\n 'zap:refresh': Record<string, never>\n 'zap:highlight': { anchors: DomAnchor[] }\n 'zap:pins': { pins: Pin[] }\n}\n\nexport type PageMessageType = keyof PageMessages\nexport type EditorMessageType = keyof EditorMessages\n\nexport interface Envelope<T extends string = string, P = unknown> {\n source: typeof PROTOCOL_SOURCE\n v: typeof PROTOCOL_VERSION\n session: string\n type: T\n payload: P\n}\n\nexport type PageMessage = {\n [K in PageMessageType]: Envelope<K, PageMessages[K]>\n}[PageMessageType]\n\nexport type EditorMessage = {\n [K in EditorMessageType]: Envelope<K, EditorMessages[K]>\n}[EditorMessageType]\n\n/** Why an incoming message was dropped. Every drop is counted. */\nexport type DropReason =\n | 'not-envelope'\n | 'oversize'\n | 'unknown-type'\n | 'invalid-payload'\n | 'wrong-source'\n | 'wrong-origin'\n | 'wrong-session'\n\nexport type DropStats = Record<DropReason, number>\n\nexport function emptyDropStats(): DropStats {\n return {\n 'not-envelope': 0,\n oversize: 0,\n 'unknown-type': 0,\n 'invalid-payload': 0,\n 'wrong-source': 0,\n 'wrong-origin': 0,\n 'wrong-session': 0,\n }\n}\n\n// ── Grammar ─────────────────────────────────────────────────────────────────\n\nconst LOCALE_RE = /^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8}){0,3}$/\nconst SESSION_RE = /^[0-9a-f]{32}$/\nconst COLOR_RE = /^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/\nconst ERROR_CODE_RE = /^[A-Z][A-Z0-9_]{0,63}$/\nconst PIN_ID_RE = /^[A-Za-z0-9_-]{1,64}$/\n\nexport function isLocale(value: unknown): value is string {\n return typeof value === 'string' && LOCALE_RE.test(value)\n}\n\n/** A session id as `createSession()` makes it: 128 bits, lowercase hex. */\nexport function isSession(value: unknown): value is string {\n return typeof value === 'string' && SESSION_RE.test(value)\n}\n\n// ── Small predicates ────────────────────────────────────────────────────────\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction isString(value: unknown, max: number): value is string {\n return typeof value === 'string' && value.length <= max\n}\n\nfunction isFiniteNumber(value: unknown, min: number, max: number): value is number {\n return typeof value === 'number' && Number.isFinite(value) && value >= min && value <= max\n}\n\nfunction isOptional<T>(value: unknown, check: (v: unknown) => v is T): boolean {\n return value === undefined || check(value)\n}\n\nfunction isNullableString(value: unknown, max: number): boolean {\n return value === null || value === undefined || isString(value, max)\n}\n\nfunction isHttpUrl(value: unknown): value is string {\n if (!isString(value, MAX_URL_LENGTH)) return false\n try {\n const url = new URL(value)\n return url.protocol === 'https:' || url.protocol === 'http:'\n } catch {\n return false\n }\n}\n\n/** Longest draft-mode route path accepted. */\nexport const MAX_DRAFT_ROUTE_LENGTH = 512\nconst DRAFT_ROUTE_RE = /^\\/[A-Za-z0-9\\-._~!$&'()*+,;=:@%/]*$/\n\n/**\n * A draft-mode route path: starts with exactly one `/`, then only URL path\n * characters (no `\\\\`, no whitespace or control character, no `?` or `#`), no\n * `//` anywhere and no dot segment, so that joined to any origin it stays on\n * that origin and names the same path. Anything that could carry or imply a\n * host (`//evil.example`, `/\\\\evil.example`, `https://…`, `/./`, `/..`) fails.\n */\nexport function isDraftRoutePath(value: unknown): value is string {\n if (typeof value !== 'string' || value.length > MAX_DRAFT_ROUTE_LENGTH) return false\n if (!DRAFT_ROUTE_RE.test(value) || value.includes('//')) return false\n return !value.split('/').some((segment) => /^(?:\\.|%2e){1,2}$/i.test(segment))\n}\n\nfunction isMode(value: unknown): value is OverlayMode {\n return typeof value === 'string' && (OVERLAY_MODES as readonly string[]).includes(value)\n}\n\nfunction isTheme(value: unknown): value is OverlayTheme {\n if (!isObject(value)) return false\n for (const key of ['outline', 'chip', 'chipText'] as const) {\n const v = value[key]\n if (v !== undefined && !(typeof v === 'string' && COLOR_RE.test(v))) return false\n }\n return true\n}\n\nfunction isZoom(value: unknown): value is number {\n return isFiniteNumber(value, 0.05, 4)\n}\n\nexport function isPinId(value: unknown): value is string {\n return typeof value === 'string' && PIN_ID_RE.test(value)\n}\n\nfunction isSpotPoint(value: unknown): value is SpotPoint {\n return isObject(value) && isFiniteNumber(value.x, 0, 1) && isFiniteNumber(value.y, 0, 1)\n}\n\n/** A pin: id grammar, integer `n` 1..999, and exactly one of `spot` / `dom`. */\nexport function isPin(value: unknown): value is Pin {\n return (\n isObject(value) &&\n isPinId(value.id) &&\n Number.isInteger(value.n) &&\n isFiniteNumber(value.n, 1, 999) &&\n (value.spot === undefined) !== (value.dom === undefined) &&\n (value.spot === undefined ? isDomAnchor(value.dom) : isSpotPoint(value.spot))\n )\n}\n\nfunction isFieldRef(value: unknown): value is FieldRefPayload & Record<string, unknown> {\n return isObject(value) && isRecordRef(value.recordRef) && isFieldKey(value.fieldKey)\n}\n\nexport function isPreviewValue(value: unknown): value is PreviewValue {\n if (value === null || typeof value === 'boolean') return true\n if (typeof value === 'string') return true\n if (typeof value === 'number') return Number.isFinite(value)\n if (!isObject(value)) return false\n const keys = Object.keys(value)\n if (keys.length !== 1) return false\n if ('text' in value) return typeof value.text === 'string'\n if ('html' in value) return typeof value.html === 'string'\n if ('url' in value) return isString(value.url, MAX_URL_LENGTH)\n if ('email' in value) return isString(value.email, MAX_EMAIL_LENGTH)\n if ('image' in value) {\n const image = value.image\n return (\n isObject(image) &&\n isNullableString(image.src, MAX_URL_LENGTH) &&\n isNullableString(image.srcset, MAX_URL_LENGTH * 4) &&\n isNullableString(image.alt, 2000)\n )\n }\n return false\n}\n\nfunction isTextQuote(value: unknown): value is TextQuote {\n return (\n isObject(value) &&\n isString(value.exact, MAX_QUOTE_EXACT) &&\n isString(value.prefix, MAX_QUOTE_CONTEXT) &&\n isString(value.suffix, MAX_QUOTE_CONTEXT)\n )\n}\n\nfunction isRect(value: unknown): value is NormalizedRect {\n return (\n isObject(value) &&\n isFiniteNumber(value.x, -1, 2) &&\n isFiniteNumber(value.y, -1, 2) &&\n isFiniteNumber(value.w, 0, 2) &&\n isFiniteNumber(value.h, 0, 2)\n )\n}\n\n/** The selector cap is in BYTES (§3.3), so a multi-byte selector counts right. */\nexport function utf8Length(value: string): number {\n return new TextEncoder().encode(value).length\n}\n\nexport function isDomAnchor(value: unknown): value is DomAnchor {\n if (!isObject(value)) return false\n const tagOk = value.tag === null || (isString(value.tag, 512) && parseTag(value.tag) !== null)\n const selectorOk =\n value.selector === null ||\n (typeof value.selector === 'string' &&\n value.selector.length > 0 &&\n utf8Length(value.selector) <= MAX_SELECTOR_BYTES)\n const viewport = value.viewport\n return (\n tagOk &&\n selectorOk &&\n (value.textQuote === null || isTextQuote(value.textQuote)) &&\n isRect(value.rect) &&\n isObject(viewport) &&\n isFiniteNumber(viewport.w, 0, 100_000) &&\n isFiniteNumber(viewport.h, 0, 10_000_000)\n )\n}\n\nexport function isPageAnchor(value: unknown): value is PageAnchor {\n if (!isObject(value)) return false\n const field = value.field\n const fieldOk =\n field === null ||\n (isObject(field) &&\n isRecordRef(field.recordRef) &&\n isFieldKey(field.key) &&\n (field.locale === null || isLocale(field.locale)))\n return fieldOk && isDomAnchor(value.dom) && isHttpUrl(value.pageUrl)\n}\n\nfunction isAnchorList<T>(value: unknown, check: (v: unknown) => v is T): value is T[] {\n return Array.isArray(value) && value.length <= MAX_ANCHORS && value.every(check)\n}\n\n/**\n * Parse `recordRef#fieldKey` (the `data-zap` grammar). Exported here because\n * `isDomAnchor` checks a reported tag with it; `tags.ts` re-exports it.\n */\nexport function parseTag(value: string): { recordRef: RecordRef; fieldKey: string } | null {\n const trimmed = value.trim()\n const hash = trimmed.lastIndexOf('#')\n if (hash <= 0) return null\n const recordRef = trimmed.slice(0, hash)\n const fieldKey = trimmed.slice(hash + 1)\n if (!isRecordRef(recordRef) || !isFieldKey(fieldKey)) return null\n return { recordRef, fieldKey }\n}\n\n// ── Payload validators per type ─────────────────────────────────────────────\n\nconst PAGE_VALIDATORS: { [K in PageMessageType]: (p: unknown) => boolean } = {\n 'zap:ready': (p) =>\n isObject(p) &&\n isString(p.version, 32) &&\n Array.isArray(p.capabilities) &&\n p.capabilities.length <= CAPABILITIES.length &&\n p.capabilities.every((c) => (CAPABILITIES as readonly unknown[]).includes(c)) &&\n isHttpUrl(p.pageUrl) &&\n (p.draftRoute === undefined || p.draftRoute === null || isDraftRoutePath(p.draftRoute)),\n 'zap:tags': (p) =>\n Array.isArray(p) &&\n p.length <= MAX_TAG_REPORT &&\n p.every(\n (t) =>\n isObject(t) &&\n typeof t.tag === 'string' &&\n isRecordRef(t.recordRef) &&\n isFieldKey(t.fieldKey) &&\n t.tag === `${t.recordRef}#${t.fieldKey}` &&\n Number.isInteger(t.count) &&\n (t.count as number) > 0 &&\n (t.source === undefined || t.source === 'stega' || t.source === 'attr'),\n ),\n 'zap:click': (p) =>\n isFieldRef(p) &&\n (p.rect === undefined ||\n (isObject(p.rect) &&\n isFiniteNumber(p.rect.x, -1e7, 1e7) &&\n isFiniteNumber(p.rect.y, -1e7, 1e7) &&\n isFiniteNumber(p.rect.w, 0, 1e7) &&\n isFiniteNumber(p.rect.h, 0, 1e7))),\n 'zap:select': (p) => isObject(p) && isAnchorList(p.anchors, isPageAnchor),\n 'zap:navigate': (p) => isObject(p) && isHttpUrl(p.url),\n 'zap:error': (p) =>\n isObject(p) &&\n typeof p.code === 'string' &&\n ERROR_CODE_RE.test(p.code) &&\n isOptional(p.detail, (d): d is string => isString(d, 500)),\n 'zap:spot': (p) =>\n isObject(p) &&\n isSpotPoint(p.spot) &&\n isObject(p.viewport) &&\n isFiniteNumber(p.viewport.w, 1, 100_000) &&\n isFiniteNumber(p.viewport.h, 1, 10_000_000) &&\n isHttpUrl(p.pageUrl),\n 'zap:pin': (p) => isObject(p) && isPinId(p.id),\n 'zap:paused': (p) =>\n isObject(p) &&\n (p.reason === 'runaway' || p.reason === 'cap') &&\n Number.isInteger(p.count) &&\n (p.count as number) >= 0,\n}\n\nconst EDITOR_VALIDATORS: { [K in EditorMessageType]: (p: unknown) => boolean } = {\n 'zap:hello': (p) => {\n if (!isObject(p) || !isSession(p.session) || !isString(p.siteKey, 128)) return false\n if (!isLocale(p.locale) || !isRecordRef(p.recordRef) || !isMode(p.mode)) return false\n if (p.theme !== undefined && !isTheme(p.theme)) return false\n if (p.zoom !== undefined && !isZoom(p.zoom)) return false\n if (p.previewToken !== undefined && !isPreviewTokenShape(p.previewToken)) return false\n // Both maps capped and checked entry by entry: labels by field key,\n // `foreign` by full tag to `[label, suffix]`.\n const within = (map: unknown, ok: (key: string, value: unknown) => boolean) =>\n isObject(map) &&\n Object.keys(map).length <= MAX_LABELS &&\n Object.entries(map).every(([key, value]) => ok(key, value))\n return (\n within(p.labels, (key, label) => isFieldKey(key) && isString(label, 200)) &&\n within(\n p.foreign ?? {},\n (tag, pair) =>\n !!parseTag(tag) &&\n Array.isArray(pair) &&\n pair.length === 2 &&\n pair.every((part) => isString(part, 200)),\n )\n )\n },\n 'zap:values': (p) => {\n if (!isObject(p) || !isRecordRef(p.recordRef) || !isLocale(p.locale)) return false\n const patch = p.patch\n if (!isObject(patch)) return false\n return Object.entries(patch).every(([key, value]) => isFieldKey(key) && isPreviewValue(value))\n },\n 'zap:focus-field': isFieldRef,\n 'zap:mode': isMode,\n 'zap:zoom': (p) => isObject(p) && isZoom(p.zoom),\n 'zap:refresh': isObject,\n 'zap:highlight': (p) => isObject(p) && isAnchorList(p.anchors, isDomAnchor),\n 'zap:pins': (p) =>\n isObject(p) && Array.isArray(p.pins) && p.pins.length <= MAX_PINS && p.pins.every(isPin),\n}\n\nexport const PAGE_MESSAGE_TYPES = /* @__PURE__ */ Object.keys(PAGE_VALIDATORS) as PageMessageType[]\nexport const EDITOR_MESSAGE_TYPES = /* @__PURE__ */ Object.keys(\n EDITOR_VALIDATORS,\n) as EditorMessageType[]\n\n// ── Envelope ────────────────────────────────────────────────────────────────\n\nexport type ParseResult<M> = { ok: true; message: M } | { ok: false; reason: DropReason }\n\n/** UTF-8 size of the message's JSON form, or `Infinity` when it has none. */\nexport function messageBytes(data: unknown): number {\n try {\n const json = JSON.stringify(data)\n return json === undefined ? Infinity : utf8Length(json)\n } catch {\n return Infinity\n }\n}\n\nfunction parseWith<M>(\n data: unknown,\n validators: Record<string, (p: unknown) => boolean>,\n): ParseResult<M> {\n if (\n !isObject(data) ||\n data.source !== PROTOCOL_SOURCE ||\n data.v !== PROTOCOL_VERSION ||\n typeof data.session !== 'string' ||\n data.session.length > 64 ||\n typeof data.type !== 'string'\n ) {\n return { ok: false, reason: 'not-envelope' }\n }\n // The cap comes before any deep validation: a 50 MB payload is refused on\n // its size, not walked.\n if (messageBytes(data) > MAX_MESSAGE_BYTES) return { ok: false, reason: 'oversize' }\n const validate = Object.prototype.hasOwnProperty.call(validators, data.type)\n ? validators[data.type]\n : undefined\n if (!validate) return { ok: false, reason: 'unknown-type' }\n if (!validate(data.payload)) return { ok: false, reason: 'invalid-payload' }\n return {\n ok: true,\n message: {\n source: PROTOCOL_SOURCE,\n v: PROTOCOL_VERSION,\n session: data.session,\n type: data.type,\n payload: data.payload,\n } as M,\n }\n}\n\n/** Parse what the PAGE received: only editor → page types are known. */\nexport function parseEditorMessage(data: unknown): ParseResult<EditorMessage> {\n return parseWith<EditorMessage>(data, EDITOR_VALIDATORS)\n}\n\n/** Parse what the EDITOR received: only page → editor types are known. */\nexport function parsePageMessage(data: unknown): ParseResult<PageMessage> {\n return parseWith<PageMessage>(data, PAGE_VALIDATORS)\n}\n\nexport function envelope<T extends string, P>(\n type: T,\n session: string,\n payload: P,\n): Envelope<T, P> {\n return { source: PROTOCOL_SOURCE, v: PROTOCOL_VERSION, session, type, payload }\n}\n","/**\n * Stega: invisible field markers on prose values read in preview\n * (`.docs/proposals/zap-cms-v2.md` §2.4, §7.5).\n *\n * Zap appends a marker to TEXT and LONG_TEXT values, and to the last text\n * node of each RICH_TEXT block, ONLY in `preview=1` delivery responses. The\n * preview overlay finds the marker in the page's text nodes and so knows\n * which element renders which field, with no manual tag. Published responses\n * never carry one.\n *\n * ## Format (eelzap stega v1)\n *\n * A marker is the tag the overlay would read from a `data-zap` attribute,\n * `recordRef#fieldKey`, plus `@locale` when the value belongs to a locale:\n *\n * blog-posts/mi-primer-post#title@es\n * doc:home#hero_title@es\n *\n * written as zero-width characters:\n *\n * - four digits, base 4: U+200B (0), U+200C (1), U+200D (2), U+2060 (3);\n * - a prefix of 8 digits, the bytes 0xE5 0xA7 (`3211 2213`);\n * - then every byte of the ASCII payload as 4 digits, most significant first.\n *\n * The payload is printable ASCII (the tag grammar allows nothing else), so\n * every payload byte starts with digit 0 or 1 and the prefix, which starts\n * with 3, can never be read inside a payload. A decoder reads groups of four\n * after the prefix until a group is not a payload byte, then validates the\n * result against the tag grammar; anything that does not parse is ignored. A\n * value never carries two markers (`stegaEncode` refuses to add a second).\n *\n * Why not `@vercel/stega`'s format: it encodes a JSON object (a URL and\n * origin in Sanity's and Vercel's use) and uses U+FEFF, which some tools strip\n * as a byte order mark. Here the decoded marker IS the tag grammar\n * (`data-zap`), so one parser serves both, and U+FEFF is avoided. No\n * dependency either way.\n *\n * Zero dependencies, no side effects: `.` re-exports `cleanStega` and\n * `hasStega`; the encoder is internal (Zap's delivery imports it); the\n * decoder serves `./preview`.\n */\n\nconst DIGITS = ['​', '‌', '‍', '⁠'] as const\nconst PREFIX = '⁠‍‌‌‍‍‌⁠'\n/** Longest payload a marker may carry, in bytes (ref 300 + field 128 + locale). */\nconst MAX_PAYLOAD = 512\n\n/** `recordRef#fieldKey` with an optional `@locale`: the tag grammar (`refs.ts`). */\nconst PAYLOAD_RE =\n /^(doc:[a-z0-9]+(?:-[a-z0-9]+)*|[a-z0-9]+(?:-[a-z0-9]+)*\\/[a-z0-9]+(?:-[a-z0-9]+)*)#([a-z][a-z0-9_]{0,127})(?:@([A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8}){0,3}))?$/\n/** The prefix, then whole payload bytes (first digit 0 or 1). */\nconst MARKER_RE = /⁠‍‌‌‍‍‌⁠(?:[​‌][​‌‍⁠]{3})*/g\n\n/** What one marker says: the record, the field, and the locale when known. */\nexport interface StegaInfo {\n /** `collectionKey/slug` or `doc:documentKey`. */\n recordRef: string\n fieldKey: string\n locale?: string | null\n}\n\nexport interface DecodedStega {\n recordRef: string\n fieldKey: string\n locale: string | null\n /** `recordRef#fieldKey`, the `data-zap` tag. */\n tag: string\n}\n\n/** The marker for one field, or `''` when the info does not fit the tag grammar. */\nexport function stegaMarker(info: StegaInfo): string {\n const payload = `${info.recordRef}#${info.fieldKey}${info.locale ? `@${info.locale}` : ''}`\n if (payload.length > MAX_PAYLOAD || !PAYLOAD_RE.test(payload)) return ''\n let out = PREFIX\n for (let i = 0; i < payload.length; i++) {\n const byte = payload.charCodeAt(i)\n out +=\n DIGITS[(byte >> 6) & 3]! +\n DIGITS[(byte >> 4) & 3]! +\n DIGITS[(byte >> 2) & 3]! +\n DIGITS[byte & 3]!\n }\n return out\n}\n\n/**\n * `text` with the field's marker appended. A string that already carries a\n * marker, or info outside the grammar, comes back unchanged.\n */\nexport function stegaEncode(text: string, info: StegaInfo): string {\n if (hasStega(text)) return text\n return text + stegaMarker(info)\n}\n\n/** True when `value` is a string carrying at least one marker. */\nexport function hasStega(value: unknown): boolean {\n return typeof value === 'string' && value.includes(PREFIX)\n}\n\n/** Every valid marker in `text`, in order. Invalid ones are skipped. */\nexport function stegaDecode(text: string): DecodedStega[] {\n if (!text.includes(PREFIX)) return []\n const found: DecodedStega[] = []\n for (const match of text.match(MARKER_RE) ?? []) {\n let payload = ''\n for (let i = PREFIX.length; i + 4 <= match.length; i += 4) {\n let byte = 0\n for (let j = 0; j < 4; j++) byte = byte * 4 + DIGITS.indexOf(match[i + j] as never)\n payload += String.fromCharCode(byte)\n }\n const parsed = PAYLOAD_RE.exec(payload)\n if (!parsed) continue\n found.push({\n recordRef: parsed[1]!,\n fieldKey: parsed[2]!,\n locale: parsed[3] ?? null,\n tag: `${parsed[1]}#${parsed[2]}`,\n })\n }\n return found\n}\n\n/** `text` split into its visible part and the markers it carries, in order. */\nexport function splitStega(text: string): { text: string; markers: string } {\n if (!text.includes(PREFIX)) return { text, markers: '' }\n return { text: text.replace(MARKER_RE, ''), markers: (text.match(MARKER_RE) ?? []).join('') }\n}\n\n/** `text` without any marker. */\nfunction cleanString(text: string): string {\n return text.includes(PREFIX) ? text.replace(MARKER_RE, '') : text\n}\n\n/**\n * `value` with every stega marker removed: a string is cleaned, an array or a\n * plain object is cleaned deeply (into new objects), anything else is\n * returned as is. Use it where a preview value is compared, parsed or put in\n * an attribute, a URL or `<head>`; outside preview it changes nothing.\n */\nexport function cleanStega<T>(value: T): T {\n if (typeof value === 'string') return cleanString(value) as T\n if (Array.isArray(value)) return value.map((item) => cleanStega(item)) as T\n if (value && typeof value === 'object' && Object.getPrototypeOf(value) === Object.prototype) {\n const out: Record<string, unknown> = {}\n for (const [key, item] of Object.entries(value)) out[key] = cleanStega(item)\n return out as T\n }\n return value\n}\n","import { cleanStega } from '../stega'\nimport {\n MAX_QUOTE_CONTEXT,\n MAX_QUOTE_EXACT,\n MAX_SELECTOR_BYTES,\n utf8Length,\n type DomAnchor,\n type PageAnchor,\n type TextQuote,\n} from './protocol'\nimport type { TagIndex, TaggedElement } from './tags'\n\n/**\n * Anchors for change requests (§3.3 \"Anchors\"): the `dom` layer is captured\n * here, in the page, never by the server. Re-finding goes tag → selector →\n * text quote → rectangle.\n */\n\n/** `CSS.escape`, with a fallback for environments without it. */\nexport function cssEscape(value: string): string {\n const native = (globalThis as { CSS?: { escape?: (v: string) => string } }).CSS?.escape\n if (native) return native(value)\n // The CSSOM `serialize an identifier` algorithm.\n let out = ''\n for (let i = 0; i < value.length; i++) {\n const ch = value.charAt(i)\n const code = value.charCodeAt(i)\n const digit = code >= 48 && code <= 57\n if (code === 0) out += '�'\n else if (\n (code >= 1 && code <= 31) ||\n code === 127 ||\n (i === 0 && digit) ||\n (i === 1 && digit && value.charCodeAt(0) === 45)\n ) {\n out += `\\\\${code.toString(16)} `\n } else if (i === 0 && value.length === 1 && code === 45) out += `\\\\${ch}`\n else if (code >= 128 || code === 45 || code === 95 || digit || /[A-Za-z]/.test(ch)) out += ch\n else out += `\\\\${ch}`\n }\n return out\n}\n\n/**\n * `querySelectorAll` that never throws: a selector that did not come from\n * `buildSelector` in THIS page (a stored anchor, a message) may be malformed\n * or hostile, and a SyntaxError must not take the overlay down (§7 audit).\n */\nexport function safeQueryAll(root: ParentNode, selector: string): Element[] {\n try {\n return Array.from(root.querySelectorAll(selector))\n } catch {\n return []\n }\n}\n\n/** Stable-looking ids only: framework-generated ids (`:r1:`, `radix-…7`) change per render. */\nfunction usableId(element: Element): string | null {\n const id = element.getAttribute('id')\n if (!id || id.length > 64 || /^[:\\d]|:|\\d{3,}/.test(id)) return null\n return id\n}\n\nfunction segment(element: Element): string {\n const tag = element.localName\n const parent = element.parentElement\n if (!parent) return tag\n let index = 0\n let count = 0\n for (const sibling of Array.from(parent.children)) {\n if (sibling.localName !== tag) continue\n count++\n if (sibling === element) index = count\n }\n return count > 1 ? `${tag}:nth-of-type(${index})` : tag\n}\n\nfunction isUnique(doc: Document, selector: string, element: Element): boolean {\n const found = safeQueryAll(doc, selector)\n return found.length === 1 && found[0] === element\n}\n\n/**\n * A short CSS selector that matches exactly `element` today, or null when no\n * selector under 1 KB does.\n *\n * Walks up from the element, one `tag:nth-of-type(n)` segment per level, and\n * stops at the first ancestor with a usable id or a `data-zap*` attribute\n * (both are things the site chose to name, so they outlive a redesign better\n * than a position does), or as soon as the path is unique. Classes are left\n * out on purpose: utility and hashed class names change on every deploy.\n */\nexport function buildSelector(element: Element, doc: Document): string | null {\n const ownId = usableId(element)\n if (ownId) {\n const selector = `#${cssEscape(ownId)}`\n if (isUnique(doc, selector, element)) return selector\n }\n const parts: string[] = []\n for (let node: Element | null = element; node; node = node.parentElement) {\n if (node.localName === 'html') break\n if (node !== element) {\n const id = usableId(node)\n if (id) {\n const candidate = [`#${cssEscape(id)}`, ...parts].join(' > ')\n if (isUnique(doc, candidate, element)) return fit(candidate)\n }\n const zap = node.getAttribute('data-zap') ?? node.getAttribute('data-zap-entry')\n const zapAttr = node.hasAttribute('data-zap') ? 'data-zap' : 'data-zap-entry'\n if (zap) {\n const candidate = [`[${zapAttr}=\"${cssEscape(zap)}\"]`, ...parts].join(' > ')\n if (isUnique(doc, candidate, element)) return fit(candidate)\n }\n }\n parts.unshift(segment(node))\n const candidate = parts.join(' > ')\n if (isUnique(doc, candidate, element)) return fit(candidate)\n }\n const full = parts.join(' > ')\n return full && isUnique(doc, full, element) ? fit(full) : null\n}\n\nfunction fit(selector: string): string | null {\n return utf8Length(selector) <= MAX_SELECTOR_BYTES ? selector : null\n}\n\n/**\n * Collapse whitespace the way a reader sees text, without stega markers: an\n * element's text in preview carries them, and they are never content (a\n * quote, a «before» or a typed value read from the page must not store one).\n */\nexport function normalizeText(value: string): string {\n return cleanStega(value).replace(/\\s+/g, ' ').trim()\n}\n\n/** W3C TextQuoteSelector-shaped quote: the element's text plus 32 chars around it. */\nexport function textQuoteFor(element: Element, doc: Document): TextQuote | null {\n const exact = normalizeText(element.textContent ?? '').slice(0, MAX_QUOTE_EXACT)\n if (!exact) return null\n let prefix = ''\n let suffix = ''\n const body = doc.body\n if (body && body.contains(element) && typeof doc.createRange === 'function') {\n try {\n const before = doc.createRange()\n before.setStart(body, 0)\n before.setEndBefore(element)\n prefix = normalizeText(before.toString()).slice(-MAX_QUOTE_CONTEXT)\n const after = doc.createRange()\n after.setStartAfter(element)\n after.setEnd(body, body.childNodes.length)\n suffix = normalizeText(after.toString()).slice(0, MAX_QUOTE_CONTEXT)\n } catch {\n prefix = ''\n suffix = ''\n }\n }\n return { exact, prefix, suffix }\n}\n\nfunction round(value: number): number {\n return Math.round(value * 10_000) / 10_000\n}\n\nfunction pageSize(doc: Document): { w: number; h: number } {\n const root = doc.documentElement\n return {\n w: Math.max(root.scrollWidth, root.clientWidth, 1),\n h: Math.max(root.scrollHeight, root.clientHeight, 1),\n }\n}\n\n/** The element's box as fractions of the whole page (not the viewport). */\nexport function normalizedRect(element: Element, doc: Document, win: Window) {\n const box = element.getBoundingClientRect()\n const size = pageSize(doc)\n const clamp = (v: number, lo: number, hi: number) => Math.min(hi, Math.max(lo, v))\n return {\n rect: {\n x: round(clamp((box.left + win.scrollX) / size.w, -1, 2)),\n y: round(clamp((box.top + win.scrollY) / size.h, -1, 2)),\n w: round(clamp(box.width / size.w, 0, 2)),\n h: round(clamp(box.height / size.h, 0, 2)),\n },\n viewport: size,\n }\n}\n\nexport function buildDomAnchor(\n element: Element,\n tagged: TaggedElement | null,\n doc: Document,\n win: Window,\n): DomAnchor {\n return {\n tag: tagged?.tag ?? null,\n selector: buildSelector(element, doc),\n textQuote: textQuoteFor(element, doc),\n ...normalizedRect(element, doc, win),\n }\n}\n\n/**\n * The anchor for a selected element: a `field` layer when the element is (or\n * sits inside) a tagged field, and always a `dom` layer.\n */\nexport function buildAnchor(\n element: Element,\n index: TagIndex,\n pageUrl: string,\n doc: Document,\n win: Window,\n): PageAnchor {\n const tagged = index.get(element)\n return {\n field: tagged\n ? { recordRef: tagged.recordRef, key: tagged.fieldKey, locale: tagged.locale }\n : null,\n dom: buildDomAnchor(element, tagged, doc, win),\n pageUrl,\n }\n}\n\n/**\n * Re-find an anchor's element: the tag first, then the selector, then the\n * text quote, then the rectangle. Never throws.\n */\nexport function findAnchorElement(\n anchor: DomAnchor,\n index: TagIndex,\n doc: Document,\n win: Window,\n): Element | null {\n if (anchor.tag) {\n const hash = anchor.tag.lastIndexOf('#')\n const tagged = index.byField(anchor.tag.slice(0, hash), anchor.tag.slice(hash + 1))\n if (tagged.length === 1) return tagged[0]!.element\n if (tagged.length > 1 && anchor.textQuote) {\n const exact = anchor.textQuote.exact\n const match = tagged.find((t) => normalizeText(t.element.textContent ?? '') === exact)\n if (match) return match.element\n }\n if (tagged.length > 0) return tagged[0]!.element\n }\n\n if (anchor.selector) {\n const found = safeQueryAll(doc, anchor.selector)\n if (found.length === 1) return found[0]!\n }\n\n if (anchor.textQuote && anchor.textQuote.exact.length >= 3 && doc.body) {\n const exact = anchor.textQuote.exact\n let best: Element | null = null\n let bestLength = Infinity\n for (const element of Array.from(doc.body.getElementsByTagName('*'))) {\n const text = normalizeText(element.textContent ?? '')\n if (text.length < bestLength && text.includes(exact)) {\n best = element\n bestLength = text.length\n }\n }\n if (best) return best\n }\n\n const fromPoint = (doc as { elementFromPoint?: Document['elementFromPoint'] }).elementFromPoint\n if (typeof fromPoint === 'function') {\n const size = pageSize(doc)\n const x = (anchor.rect.x + anchor.rect.w / 2) * size.w - win.scrollX\n const y = (anchor.rect.y + anchor.rect.h / 2) * size.h - win.scrollY\n if (x >= 0 && y >= 0 && x <= win.innerWidth && y <= win.innerHeight) {\n try {\n return fromPoint.call(doc, x, y)\n } catch {\n return null\n }\n }\n }\n return null\n}\n","import {\n MAX_ANCHORS,\n MAX_PINS,\n type OverlayMode,\n type OverlayTheme,\n type SpotAnchor,\n type SpotPoint,\n} from './protocol'\nimport type { TagIndex, TaggedElement } from './tags'\n\n/**\n * The in-page overlay (§2.4 \"Finding the element for a field\", §3.3\n * \"Selecting in the preview\", ui-patterns/preview-panel.md rule 5).\n *\n * - Everything is drawn in a CLOSED Shadow DOM on a custom element appended\n * to `<html>`, so the site's CSS cannot restyle it and it cannot restyle\n * the site, and site scripts cannot reach into it through `shadowRoot`.\n * - Outlines are 2px gold with a field chip at the element's top right, both\n * DIVIDED BY THE ZOOM so they stay 2px and 12px on screen when the panel\n * scales the page down (Designer's boards PreviewEtiquetada /\n * SeleccionarElementos: outline #CF8700, chip #9A6200 with white text,\n * selected elements tinted rgba(207,135,0,0.10) and the chip gains a check).\n * - Modes: `inspect` (hover outlines tagged elements; click focuses the field\n * in the form), `select` (hover outlines any element; click selects, Shift\n * or Cmd/Ctrl click adds, Esc clears; with `spotOnLarge`, a plain click on\n * the page itself or on an element covering half the viewport or more\n * places a spot instead, as the site's Comentar does: one tool comments on\n * elements and on points), `off` (nothing drawn, nothing\n * intercepted), `spot` (comments: a transparent capture layer in the shadow\n * root covers the page, so a click never reaches the site; the click becomes\n * a point of the whole document and a pending pin is drawn there; Esc clears\n * it; no hover outlines).\n * - Numbered comment pins (`setPins`) are drawn in every mode but `off` (in\n * `off` too with `pinsWhenOff`, the live site's «Navegar»), and are the only\n * things here that take the pointer besides the capture layer.\n * A click on one is consumed and reported (`onPin`), never also a spot.\n * - Boxes are `position: fixed` from `getBoundingClientRect()` and redrawn on\n * scroll and resize in one animation frame, so nothing in the site's layout\n * moves.\n */\n\nexport const OVERLAY_HOST_TAG = 'eel-zap-overlay'\n\nconst DEFAULT_THEME: Required<OverlayTheme> = {\n outline: '#CF8700',\n chip: '#9A6200',\n chipText: '#FFFFFF',\n}\n\nconst SELECTED_TINT = 'rgba(207,135,0,0.10)'\n\nconst CHECK_SVG =\n '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.4\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M20 6 9 17l-5-5\"/></svg>'\n\nexport interface OverlayCallbacks {\n /** Inspect mode: a tagged element was clicked. */\n onInspect?(tagged: TaggedElement): void\n /** Select mode: the selection changed (≤ 20 elements). */\n onSelect?(elements: Element[]): void\n /** Spot mode: the page was clicked at this point of the document. */\n onSpot?(anchor: SpotAnchor): void\n /** A pin was clicked. */\n onPin?(pin: { id: string }): void\n}\n\n/** A pin to draw: at a point of the document, or at an element's top-left corner. */\nexport interface OverlayPin {\n id: string\n n: number\n spot?: SpotPoint\n element?: Element\n}\n\nexport interface OverlayOptions extends OverlayCallbacks {\n doc: Document\n win: Window\n index: TagIndex\n /** Events on these targets are not the overlay's (the suggestion UI's own host). */\n ignore?(target: EventTarget): boolean\n /** Select mode: every click adds or removes, Shift or not (the site toolbar's Seleccionar). */\n additive?(): boolean\n /** Draw the pins in `off` mode too, intercepting nothing else (the live site's «Navegar»). */\n pinsWhenOff?: boolean\n /**\n * Select mode: a plain click on `<html>`/`<body>` or on an element taking\n * at least half the viewport is a spot (`onSpot`), not a selection. Zap's\n * preview has ONE comment toggle (board Main, «Comentar sobre la página»)\n * and this is how it reaches both anchors.\n */\n spotOnLarge?: boolean\n}\n\ntype BoxKind = 'hover' | 'focus' | 'selected' | 'highlight'\n\ninterface Box {\n element: Element\n kind: BoxKind\n label: string | null\n /** A field of another record: «en Configuración», drawn muted after the label. */\n suffix?: string\n}\n\nexport class Overlay {\n private host: HTMLElement | null = null\n private layer: HTMLElement | null = null\n private mode: OverlayMode = 'off'\n private zoom = 1\n private theme: Required<OverlayTheme> = { ...DEFAULT_THEME }\n private labels: Record<string, string> = {}\n private foreign: Record<string, [string, string]> = {}\n private hover: Element | null = null\n private focused: Element[] = []\n private highlighted: Element[] = []\n private selected: Element[] = []\n private pins: OverlayPin[] = []\n private pending: SpotPoint | null = null\n private capture: HTMLElement | null = null\n private frame: number | null = null\n private readonly listeners: Array<[EventTarget, string, EventListener, boolean]> = []\n\n constructor(private readonly options: OverlayOptions) {}\n\n mount(): void {\n if (this.host) return\n const { doc } = this.options\n const host = doc.createElement(OVERLAY_HOST_TAG)\n // Inline !important so a site rule like `* { display: none }` cannot hide it.\n const hostStyle: Record<string, string> = {\n all: 'initial',\n position: 'fixed',\n inset: '0',\n 'pointer-events': 'none',\n 'z-index': '2147483647',\n display: 'block',\n }\n for (const [name, value] of Object.entries(hostStyle)) {\n host.style.setProperty(name, value, 'important')\n }\n const shadow = host.attachShadow({ mode: 'closed' })\n const style = doc.createElement('style')\n style.textContent = STYLES\n const layer = doc.createElement('div')\n layer.className = 'layer'\n // Spot mode's capture layer, under the boxes and pins; shown in spot mode only.\n const capture = doc.createElement('div')\n capture.className = 'capture'\n capture.onclick = (event) => {\n this.consume(event)\n this.spotAt(event)\n }\n shadow.append(style, capture, layer)\n this.capture = capture\n doc.documentElement.appendChild(host)\n this.host = host\n this.layer = layer\n this.install()\n this.render()\n }\n\n /**\n * Register the event listeners. Called by `mount()`, and EARLIER by the\n * runtime — at script start, before any site script has run — so our\n * capture listeners on `window` are first in line, ahead of anything the\n * site registers.\n */\n install(): void {\n if (this.listeners.length > 0) return\n const { win } = this.options\n this.listen(win, 'mouseover', (event) => this.onPointer(event as MouseEvent))\n // Leaving the document (relatedTarget null) clears the hover outline.\n this.listen(win, 'mouseout', (event) => {\n if (!(event as MouseEvent).relatedTarget) this.setHover(null)\n })\n this.listen(win, 'click', (event) => this.onClick(event as MouseEvent))\n // While the overlay is on, the page never navigates away (Zap's preview\n // frame; the live site's Editar and Comentar): no form is submitted.\n this.listen(win, 'submit', (event) => this.mode !== 'off' && this.consume(event))\n this.listen(win, 'keydown', (event) => this.onKey(event as KeyboardEvent))\n this.listen(win, 'scroll', () => this.schedule())\n this.listen(win, 'resize', () => this.schedule())\n }\n\n destroy(): void {\n for (const [target, type, listener, capture] of this.listeners) {\n target.removeEventListener(type, listener, capture)\n }\n this.listeners.length = 0\n this.host?.remove()\n this.host = null\n this.layer = null\n this.capture = null\n }\n\n /** The host element, so the tag observer can ignore our own mutations. */\n get hostElement(): Element | null {\n return this.host\n }\n\n getMode(): OverlayMode {\n return this.mode\n }\n\n setMode(mode: OverlayMode): void {\n if (mode === this.mode) return\n const hadSelection = this.selected.length > 0\n this.mode = mode\n this.hover = null\n this.pending = null\n if (mode !== 'select' && hadSelection) {\n this.selected = []\n this.options.onSelect?.([])\n }\n if (mode === 'off') {\n this.focused = []\n this.highlighted = []\n }\n this.schedule()\n }\n\n setZoom(zoom: number): void {\n if (!(zoom > 0)) return\n this.zoom = zoom\n this.schedule()\n }\n\n setTheme(theme: OverlayTheme | undefined): void {\n this.theme = { ...DEFAULT_THEME, ...(theme ?? {}) }\n this.schedule()\n }\n\n /** Labels by field key; `foreign`, by full tag, the fields of other records (`HelloPayload.foreign`). */\n setLabels(labels: Record<string, string>, foreign: Record<string, [string, string]> = {}): void {\n this.labels = { ...labels }\n this.foreign = { ...foreign }\n this.schedule()\n }\n\n /** Outline every element of a field and scroll the first into view. */\n focusField(elements: Element[]): number {\n this.focused = elements.slice()\n const first = elements[0]\n if (first) reveal(this.options.win, first)\n this.schedule()\n return elements.length\n }\n\n /** Outline change-request anchors and scroll to the first. */\n highlight(elements: Element[]): void {\n this.highlighted = elements.slice(0, MAX_ANCHORS)\n const first = this.highlighted[0]\n if (first) reveal(this.options.win, first)\n this.schedule()\n }\n\n /** Draw these pins (≤ 50); drops the pending spot pin. */\n setPins(pins: OverlayPin[]): void {\n this.pins = pins.slice(0, MAX_PINS)\n this.clearPending()\n }\n\n /** Drop the pending spot pin. */\n clearPending(): void {\n this.pending = null\n this.schedule()\n }\n\n clearSelection(): void {\n if (this.selected.length === 0) return\n this.selected = []\n this.options.onSelect?.([])\n this.schedule()\n }\n\n get selection(): readonly Element[] {\n return this.selected\n }\n\n /** For specs: what the closed shadow root currently draws. */\n inspect(): {\n boxes: Array<{ kind: BoxKind; label: string | null }>\n pins: HTMLElement[]\n capture: HTMLElement | null\n zoom: number\n } {\n const nodes = Array.from(this.layer?.children ?? []) as HTMLElement[]\n const boxes = nodes\n .filter((node) => node.className === 'box')\n .map((node) => ({\n kind: node.dataset.kind as BoxKind,\n label: node.querySelector('.chip')?.textContent ?? null,\n }))\n const capture = this.capture?.style.display === 'block' ? this.capture : null\n return { boxes, pins: nodes.filter((n) => n.className === 'pin'), capture, zoom: this.zoom }\n }\n\n /** For specs: the style of the n-th box. */\n boxStyle(n: number): CSSStyleDeclaration | null {\n const node = this.layer?.children[n] as HTMLElement | undefined\n return node?.style ?? null\n }\n\n // ── Events ────────────────────────────────────────────────────────────────\n\n private listen(target: EventTarget, type: string, listener: EventListener): void {\n // Capture on window: we see the event before any site listener, so a\n // site's own click handler (a router, a lightbox) never runs for a click\n // that the overlay consumed.\n target.addEventListener(type, listener, true)\n this.listeners.push([target, type, listener, true])\n }\n\n private isOwn(node: EventTarget | null): boolean {\n return (!!this.host && node === this.host) || (!!node && !!this.options.ignore?.(node))\n }\n\n private targetFor(event: Event): Element | null {\n const target = event.target as Element | null\n if (!target || this.isOwn(target) || typeof target.closest !== 'function') return null\n if (this.mode === 'inspect') return this.options.index.closest(target)?.element ?? null\n if (this.mode === 'select') {\n const tagged = this.options.index.closest(target)\n const element = tagged?.element ?? target\n const name = element.localName\n return name === 'html' || name === 'body' ? null : element\n }\n return null\n }\n\n private onPointer(event: MouseEvent): void {\n if (this.mode === 'off') return\n this.setHover(this.targetFor(event))\n }\n\n private setHover(element: Element | null): void {\n if (element === this.hover) return\n this.hover = element\n this.schedule()\n }\n\n private onClick(event: MouseEvent): void {\n if (this.mode === 'off' || this.isOwn(event.target)) return\n // Nor does a link navigate; a tagged one still selects its field below.\n if ((event.target as Element).closest?.('a[href],area[href]')) this.consume(event)\n if (this.mode === 'spot') {\n // The capture layer normally takes the click; anything that still lands\n // on the site (a synthetic click, a gap) is consumed the same way.\n this.consume(event)\n return this.spotAt(event)\n }\n const element = this.targetFor(event)\n if (this.mode === 'inspect') {\n if (!element) return\n const tagged = this.options.index.get(element)\n if (!tagged) return\n event.preventDefault()\n event.stopImmediatePropagation()\n this.options.onInspect?.(tagged)\n return\n }\n // Select mode consumes every click, so nothing on the page reacts.\n this.consume(event)\n const additive = event.shiftKey || event.metaKey || event.ctrlKey || !!this.options.additive?.()\n if (this.options.spotOnLarge && !additive && (!element || this.isLarge(element))) {\n // A wrapper, not the thing the person pointed at: comment on the point.\n this.clearSelection()\n return this.spotAt(event)\n }\n if (!element) return\n if (this.pending) this.clearPending()\n let next: Element[]\n if (additive) {\n next = this.selected.includes(element)\n ? this.selected.filter((e) => e !== element)\n : this.selected.length < MAX_ANCHORS\n ? [...this.selected, element]\n : this.selected\n } else {\n next = [element]\n }\n if (next === this.selected) return\n this.selected = next\n this.options.onSelect?.(next.slice())\n this.schedule()\n }\n\n /** Most of the viewport: a wrapper, not the thing the person pointed at. */\n private isLarge(element: Element): boolean {\n const { win } = this.options\n const rect = element.getBoundingClientRect()\n const area = Math.max(1, win.innerWidth * win.innerHeight)\n return (rect.width * rect.height) / area >= LARGE_SHARE\n }\n\n private consume(event: Event): void {\n event.preventDefault()\n event.stopImmediatePropagation()\n }\n\n /** The click as a point of the whole document (§3.3 spot anchor). */\n private spotAt(event: MouseEvent): void {\n const { doc, win } = this.options\n const root = doc.documentElement\n const w = Math.max(root.scrollWidth, 1)\n const h = Math.max(root.scrollHeight, 1)\n const at = (v: number, size: number) => Math.min(1, Math.max(0, v / size))\n const spot = { x: at(event.clientX + win.scrollX, w), y: at(event.clientY + win.scrollY, h) }\n this.pending = spot\n this.schedule()\n this.options.onSpot?.({ spot, viewport: { w, h } })\n }\n\n private onKey(event: KeyboardEvent): void {\n if (event.key !== 'Escape') return\n if ((this.mode === 'spot' || this.mode === 'select') && this.pending) {\n this.consume(event)\n return this.clearPending()\n }\n if (this.mode !== 'select' || this.selected.length === 0) return\n this.consume(event)\n this.clearSelection()\n }\n\n // ── Drawing ───────────────────────────────────────────────────────────────\n\n private schedule(): void {\n if (!this.layer || this.frame !== null) return\n const win = this.options.win\n const raf =\n typeof win.requestAnimationFrame === 'function'\n ? (fn: () => void) => win.requestAnimationFrame(fn)\n : (fn: () => void) => win.setTimeout(fn, 16)\n this.frame = raf(() => {\n this.frame = null\n this.render()\n }) as number\n }\n\n /** Draw now (also used by specs, which have no animation frames). */\n render(): void {\n const layer = this.layer\n if (!layer) return\n const boxes: Box[] = []\n const seen = new Set<Element>()\n const add = (element: Element, kind: BoxKind) => {\n if (seen.has(element) || !element.isConnected) return\n seen.add(element)\n const tagged = this.options.index.get(element)\n const [label, suffix] = tagged ? this.labelFor(tagged) : [null]\n boxes.push({ element, kind, label, suffix })\n }\n if (this.mode !== 'off') {\n for (const element of this.selected) add(element, 'selected')\n for (const element of this.focused) add(element, 'focus')\n for (const element of this.highlighted) add(element, 'highlight')\n if (this.hover) add(this.hover, 'hover')\n }\n const nodes = boxes.map((box) => this.drawBox(box))\n if (this.capture) this.capture.style.display = this.mode === 'spot' ? 'block' : 'none'\n if (this.mode !== 'off' || this.options.pinsWhenOff) {\n const { doc, win } = this.options\n const root = doc.documentElement\n const toView = (p: SpotPoint): [number, number] => [\n p.x * root.scrollWidth - win.scrollX,\n p.y * root.scrollHeight - win.scrollY,\n ]\n for (const pin of this.pins) {\n const element = pin.element\n if (element) {\n if (!element.isConnected) continue\n const rect = element.getBoundingClientRect()\n nodes.push(this.drawPin([rect.left, rect.top], pin))\n } else if (pin.spot) nodes.push(this.drawPin(toView(pin.spot), pin))\n }\n if (this.pending && (this.mode === 'spot' || this.mode === 'select')) {\n nodes.push(this.drawPin(toView(this.pending)))\n }\n }\n layer.replaceChildren(...nodes)\n // Chips stay inside the viewport: measured once drawn, shifted in.\n const inset = EDGE_PX / this.zoom\n for (const chip of layer.querySelectorAll<HTMLElement>('.chip')) {\n const { left, right } = chip.getBoundingClientRect()\n const dx = Math.min(Math.max(0, inset - left), this.options.win.innerWidth - inset - right)\n chip.style.transform = `translateX(${dx}px)`\n }\n }\n\n /** A gold teardrop whose point (bottom-left) sits on `[x, y]`; without a pin, the pending one. */\n private drawPin([x, y]: [number, number], pin?: OverlayPin): HTMLElement {\n const node = this.options.doc.createElement('span')\n node.className = 'pin'\n const z = this.zoom\n const size = PIN_PX / z\n const vw = this.options.win.innerWidth\n // No room above a point on screen: the pin hangs below it, its tip up;\n // near the right edge it shifts in.\n const below = y >= 0 && y - size < EDGE_PX / z\n if (below) node.style.borderRadius = '0 999px 999px'\n Object.assign(node.style, {\n left: `${x <= vw ? Math.min(x, vw - size - EDGE_PX / z) : x}px`,\n top: `${below ? y : y - size}px`,\n width: `${size}px`,\n height: `${size}px`,\n fontSize: `${11 / z}px`,\n lineHeight: `${size}px`,\n boxShadow: `0 0 0 ${2 / z}px #fff,0 ${2 / z}px ${6 / z}px rgba(0,0,0,.25)`,\n })\n if (pin) {\n // The number as text, never markup; the id never reaches the DOM.\n node.textContent = String(pin.n)\n node.onclick = (event) => {\n this.consume(event)\n this.options.onPin?.({ id: pin.id })\n }\n } else node.dataset.pending = ''\n return node\n }\n\n private labelFor(tagged: TaggedElement): [string, string?] {\n const [label, suffix] = this.foreign[tagged.tag] ?? [this.labels[tagged.fieldKey]]\n return [typeof label === 'string' && label.trim() ? label : tagged.fieldKey, suffix]\n }\n\n private drawBox(box: Box): HTMLElement {\n const doc = this.options.doc\n const z = this.zoom\n const px = (n: number) => `${Math.round((n / z) * 100) / 100}px`\n const rect = box.element.getBoundingClientRect()\n const node = doc.createElement('div')\n node.className = 'box'\n node.dataset.kind = box.kind\n const offset = 3 / z\n Object.assign(node.style, {\n left: `${rect.left - offset}px`,\n top: `${rect.top - offset}px`,\n width: `${rect.width + offset * 2}px`,\n height: `${rect.height + offset * 2}px`,\n outline: `${px(2)} solid ${this.theme.outline}`,\n borderRadius: px(3),\n background: box.kind === 'selected' ? SELECTED_TINT : 'transparent',\n })\n // A chip names the field on hover, on selection and where the editor\n // located it (board CampoLocalizar: the located «Título» carries its\n // chip); a highlight outline is a thread pointing at its anchor. A\n // selected element smaller on screen than the chip itself (at low zoom,\n // most lines of text) keeps its outline and tint but shows its chip only\n // while hovered: a row of such chips buried the very elements being\n // picked (acceptance F6). The hovered one always names itself.\n const tooSmall =\n box.kind === 'selected' &&\n box.element !== this.hover &&\n Math.min(rect.width, rect.height) * z < CHIP_HEIGHT_PX\n if (box.label && box.kind !== 'highlight' && !tooSmall) {\n const chip = doc.createElement('span')\n chip.className = 'chip'\n // No room above the element: the chip goes below it (flip); the\n // horizontal shift waits until it is drawn and measurable (render).\n const lift = 27 / z\n const above =\n rect.top - offset - lift >= EDGE_PX / z || rect.bottom + lift > this.options.win.innerHeight\n Object.assign(chip.style, {\n right: px(-5),\n [above ? 'top' : 'bottom']: px(-27),\n height: px(CHIP_HEIGHT_PX),\n padding: `0 ${px(7)}`,\n gap: px(4),\n borderRadius: px(6),\n fontSize: px(12),\n background: this.theme.chip,\n color: this.theme.chipText,\n })\n if (box.kind === 'selected') {\n const icon = doc.createElement('span')\n icon.className = 'icon'\n icon.style.width = px(12)\n icon.style.height = px(12)\n // A constant SVG string of ours, never network data (§7.3).\n icon.innerHTML = CHECK_SVG\n chip.appendChild(icon)\n }\n // Label text is set with textContent: labels come from the editor and\n // are never parsed as markup.\n chip.appendChild(doc.createTextNode(box.label))\n if (box.suffix) {\n const muted = doc.createElement('span')\n muted.className = 'of'\n muted.textContent = box.suffix\n chip.appendChild(muted)\n }\n node.appendChild(chip)\n }\n return node\n }\n}\n\n/** `spotOnLarge`: an element taking this share of the viewport is commented as a point (site Comentar's rule). */\nconst LARGE_SHARE = 0.5\n\n/**\n * Scroll THIS window only, centring `element`; nothing when it is fully in\n * view. Never `scrollIntoView`, which also scrolls every scrollable ancestor\n * across the frame: Zap's editor around its preview frame.\n */\nexport function reveal(win: Window, element: Element): void {\n const { top, bottom, height } = element.getBoundingClientRect()\n const h = win.innerHeight\n if (top < 0 || bottom > h)\n win.scrollTo?.({ top: win.scrollY + top - Math.max(0, (h - height) / 2), behavior: 'smooth' })\n}\n\n/** The chip's on-screen height; a selected element smaller than this shows its chip on hover only. */\nconst CHIP_HEIGHT_PX = 20\n/** How far chips and pins keep from the viewport's edges, on screen. */\nconst EDGE_PX = 4\n/** A pin's on-screen size (SeleccionarElementos board, «Comentar»). */\nconst PIN_PX = 22\n\nconst STYLES = `\n.layer { position: fixed; inset: 0; pointer-events: none; }\n.box { position: fixed; box-sizing: border-box; pointer-events: none; }\n.chip {\n position: absolute; display: inline-flex; align-items: center; white-space: nowrap;\n font-family: Poppins, ui-sans-serif, system-ui, sans-serif; font-weight: 600; line-height: 1;\n letter-spacing: 0; box-sizing: border-box;\n}\n.of { font-weight: 400; opacity: .72; }\n.capture { position: fixed; inset: 0; pointer-events: auto; cursor: crosshair; display: none; }\n.pin {\n position: fixed; box-sizing: border-box; pointer-events: auto; cursor: pointer; text-align: center;\n border-radius: 999px 999px 999px 0; background: #CF8700; color: #fff;\n font-family: Poppins, ui-sans-serif, system-ui, sans-serif; font-weight: 700;\n}\n.icon { display: inline-flex; flex-shrink: 0; }\n.icon svg { width: 100%; height: 100%; display: block; }\n`\n","import { hasStega, stegaDecode } from '../stega'\nimport {\n isLocale,\n parseRecordRef,\n parseTag,\n type PausedPayload,\n type RecordRef,\n type TagSource,\n type TagSummary,\n} from './protocol'\n\n/**\n * The tag grammar and the tag index (`.docs/proposals/zap-cms-v2.md` §2.4).\n *\n * ```html\n * <h1 data-zap=\"blog-posts/mi-primer-post#title\">…</h1>\n * <p data-zap=\"doc:home#hero_subtitle\">…</p>\n * <article data-zap-entry=\"blog-posts/mi-primer-post\">\n * <h1 data-zap-field=\"title\">…</h1>\n * <div data-zap-field=\"body\" data-zap-html>…</div>\n * </article>\n * ```\n *\n * - `collectionKey/slug` for entries (default-locale or localized slug; the\n * EDITOR resolves which record that is — the page only reports strings),\n * `doc:documentKey` for documents, `#fieldKey` for the field. Keys, slugs\n * and field keys follow Zap's own patterns (lowercase, hyphens; field keys\n * `[a-z][a-z0-9_]*`), so a tag that could never match a record is reported\n * as invalid instead of silently never lighting up.\n * - `data-zap` wins over `data-zap-field` on the same element.\n * - `data-zap-field` takes its record from the nearest `data-zap-entry`\n * ancestor (or the element itself).\n * - `data-zap-locale` on the element or any ancestor pins the locale; a value\n * for another locale is not substituted there.\n * - `data-zap-html` opts the element into sanitised innerHTML substitution.\n *\n * ## Stega (§2.4)\n *\n * Text fields read in preview carry an invisible marker (`../stega.ts`). The\n * scan also walks the page's text nodes, decodes every marker and indexes the\n * text node's parent element under that field, with `source: 'stega'`. A\n * manual tag on the same element wins: the element keeps its attribute tag\n * and the marker is ignored for it. Text in `<head>`, `<script>`, `<style>`,\n * `<template>` and `<noscript>` is never indexed.\n */\n\nexport { parseRecordRef, parseTag }\n\nexport const TAG_SELECTOR = '[data-zap],[data-zap-field]'\n\nexport interface TaggedElement {\n element: Element\n recordRef: RecordRef\n fieldKey: string\n /** Canonical `recordRef#fieldKey`. */\n tag: string\n locale: string | null\n html: boolean\n /** `attr` for a manual tag, `stega` for a marker found in a text node. */\n source: TagSource\n /** Stega only: the text node that carries the marker (values are written to it). */\n textNode?: Text\n}\n\nexport interface TagProblem {\n attribute: 'data-zap' | 'data-zap-field' | 'data-zap-entry'\n value: string\n}\n\nexport type ReadTagResult =\n | { ok: true; tagged: TaggedElement }\n | { ok: false; problem: TagProblem }\n | null\n\n/** Read one element's tag. `null` when it carries no tag attribute. */\nexport function readElementTag(element: Element): ReadTagResult {\n const locale = readLocale(element)\n const html = element.hasAttribute('data-zap-html')\n\n const direct = element.getAttribute('data-zap')\n if (direct !== null) {\n const parsed = parseTag(direct)\n if (!parsed) return { ok: false, problem: { attribute: 'data-zap', value: clip(direct) } }\n return { ok: true, tagged: build(element, parsed.recordRef, parsed.fieldKey, locale, html) }\n }\n\n const field = element.getAttribute('data-zap-field')\n if (field === null) return null\n const fieldKey = field.trim()\n if (!parseTag(`doc:x#${fieldKey}`)) {\n return { ok: false, problem: { attribute: 'data-zap-field', value: clip(field) } }\n }\n const container = element.closest('[data-zap-entry]')\n const entry = container?.getAttribute('data-zap-entry')?.trim()\n if (!entry || !parseRecordRef(entry)) {\n return { ok: false, problem: { attribute: 'data-zap-entry', value: clip(entry ?? '') } }\n }\n return { ok: true, tagged: build(element, entry, fieldKey, locale, html) }\n}\n\nfunction build(\n element: Element,\n recordRef: string,\n fieldKey: string,\n locale: string | null,\n html: boolean,\n): TaggedElement {\n return {\n element,\n recordRef,\n fieldKey,\n tag: `${recordRef}#${fieldKey}`,\n locale,\n html,\n source: 'attr',\n }\n}\n\n/** Elements whose text is never a rendered field. */\nconst NON_RENDERED = /^(?:head|title|script|style|template|noscript|textarea)$/\n\nfunction readLocale(element: Element): string | null {\n const value = element.closest('[data-zap-locale]')?.getAttribute('data-zap-locale')?.trim()\n return value && isLocale(value) ? value : null\n}\n\nfunction clip(value: string): string {\n return value.length > 200 ? value.slice(0, 200) : value\n}\n\n/**\n * Every tagged element in a document, in document order, with lookups by\n * field and by element. Rebuilt wholesale by `scan()`: a full scan of a large\n * page is one `querySelectorAll`, cheaper and far simpler than patching the\n * index per mutation record.\n */\nexport class TagIndex {\n private list: TaggedElement[] = []\n private byElement = new WeakMap<Element, TaggedElement>()\n private byTag = new Map<string, TaggedElement[]>()\n problems: TagProblem[] = []\n\n constructor(private readonly root: ParentNode) {}\n\n scan(): void {\n const list: TaggedElement[] = []\n const problems: TagProblem[] = []\n const byElement = new WeakMap<Element, TaggedElement>()\n const byTag = new Map<string, TaggedElement[]>()\n const add = (element: Element, tagged: TaggedElement) => {\n list.push(tagged)\n if (!byElement.has(element)) byElement.set(element, tagged)\n const bucket = byTag.get(tagged.tag)\n if (bucket) bucket.push(tagged)\n else byTag.set(tagged.tag, [tagged])\n }\n for (const element of Array.from(this.root.querySelectorAll(TAG_SELECTOR))) {\n const result = readElementTag(element)\n if (!result) continue\n if (!result.ok) {\n problems.push(result.problem)\n continue\n }\n add(element, result.tagged)\n }\n const doc = (this.root as Node).ownerDocument ?? (this.root as Document)\n const walker = doc.createTreeWalker(this.root as Node, 4 /* NodeFilter.SHOW_TEXT */)\n for (let node: Node | null; (node = walker.nextNode()); ) {\n const text = node as Text\n const element = text.parentElement\n if (!element || !hasStega(text.data)) continue\n // Never a rendered field; and a manual tag on the element wins over its markers.\n if (NON_RENDERED.test(element.localName) || element.closest('head')) continue\n if (byElement.get(element)?.source === 'attr') continue\n for (const found of stegaDecode(text.data)) {\n add(element, {\n element,\n recordRef: found.recordRef,\n fieldKey: found.fieldKey,\n tag: found.tag,\n locale: found.locale ?? readLocale(element),\n html: false,\n source: 'stega',\n textNode: text,\n })\n }\n }\n // Document order across both kinds (same element: insertion order).\n list.sort((a, b) =>\n a.element.compareDocumentPosition(b.element) & 4 /* FOLLOWING */\n ? -1\n : a.element === b.element\n ? 0\n : 1,\n )\n this.list = list\n this.byElement = byElement\n this.byTag = byTag\n this.problems = problems\n }\n\n get elements(): readonly TaggedElement[] {\n return this.list\n }\n\n byField(recordRef: RecordRef, fieldKey: string): TaggedElement[] {\n return this.byTag.get(`${recordRef}#${fieldKey}`) ?? []\n }\n\n get(element: Element): TaggedElement | null {\n return this.byElement.get(element) ?? null\n }\n\n /** The nearest tagged element at or above `target`. */\n closest(target: Element | null): TaggedElement | null {\n for (let node: Element | null = target; node; node = node.parentElement) {\n const tagged = this.byElement.get(node)\n if (tagged) return tagged\n }\n return null\n }\n\n /** The `zap:tags` report: one row per tag with its element count, sorted. */\n summary(): TagSummary[] {\n return Array.from(this.byTag.entries())\n .map(([tag, items]) => ({\n tag,\n recordRef: items[0]!.recordRef,\n fieldKey: items[0]!.fieldKey,\n count: items.length,\n source: items.some((item) => item.source === 'attr')\n ? ('attr' as const)\n : ('stega' as const),\n }))\n .sort((a, b) => (a.tag < b.tag ? -1 : a.tag > b.tag ? 1 : 0))\n }\n}\n\n/** Tagged elements past which the preview stops following the page. */\nexport const MAX_TAGGED = 5000\n/** Passes in a row the tagged count may grow with nothing on the page explaining it. */\nexport const RUNAWAY_PASSES = 3\n\n/** Why the guard stopped following the page (`zap:paused`). */\nexport type PauseReason = PausedPayload['reason']\n\n/**\n * The runaway guard (defence in depth for the 2026-10 crash, where values\n * carrying stega markers made every pass add markers, and the tagged count\n * doubled until the tab ran out of memory). Call the returned check once per\n * pass, right after `scan()`, with the tagged count and `observeTags`'s\n * `page` flag. It answers why to stop following the page, or null:\n *\n * - `cap`: more than `MAX_TAGGED` tagged elements;\n * - `runaway`: the count grew on `RUNAWAY_PASSES` passes in a row that no\n * page mutation explains (a values patch's own writes are made outside a\n * pass, so they count as the page's).\n *\n * Once it answers, the caller stops observing and stops writing.\n */\nexport function tagGuard(): (count: number, page: boolean) => PauseReason | null {\n let last = 0\n let grew = 0\n return (count, page) => {\n grew = count > last && !page ? grew + 1 : 0\n last = count\n return count > MAX_TAGGED ? 'cap' : grew >= RUNAWAY_PASSES ? 'runaway' : null\n }\n}\n\nexport interface ObserveOptions {\n /** Minimum gap between two callbacks. Default 250 ms. */\n throttleMs?: number\n /** Mutations entirely inside these nodes are ignored (the overlay's host). */\n ignore?: (node: Node) => boolean\n setTimeout?: (fn: () => void, ms: number) => unknown\n clearTimeout?: (handle: unknown) => void\n}\n\n/**\n * Call `onChange` after DOM mutations, throttled (leading call deferred,\n * trailing call guaranteed): a hydrating site or an infinite list can fire\n * thousands of records a second, and the index needs one rescan per burst.\n * Mutations made by the overlay's own host are ignored, so drawing outlines\n * never triggers a rescan.\n *\n * `page` says whether the page changed since the last call. Mutations made\n * DURING `onChange` (our own writes: `reapply()`) still lead to one more\n * call, so they settle as before, but with `page` false: the runaway guard\n * (`tagGuard`) tells a site that keeps rendering from a pass feeding itself.\n */\nexport function observeTags(\n target: Node,\n onChange: (page: boolean) => void,\n options: ObserveOptions = {},\n): () => void {\n const throttleMs = options.throttleMs ?? 250\n const set = options.setTimeout ?? ((fn, ms) => setTimeout(fn, ms))\n const clear = options.clearTimeout ?? ((h) => clearTimeout(h as ReturnType<typeof setTimeout>))\n const view = (target.ownerDocument ?? (target as Document)).defaultView\n const Observer = view?.MutationObserver ?? globalThis.MutationObserver\n if (!Observer) return () => {}\n\n let timer: unknown = null\n let last = -Infinity\n let page = false\n const queue = (records: MutationRecord[], fromPage: boolean) => {\n if (options.ignore && records.every((record) => options.ignore!(record.target))) return\n page ||= fromPage\n if (timer !== null) return\n const wait = Math.max(0, last + throttleMs - Date.now())\n timer = set(fire, Math.max(wait, 16))\n }\n const fire = () => {\n timer = null\n last = Date.now()\n const changed = page\n page = false\n onChange(changed)\n // What onChange wrote is ours, not the page's.\n const own = observer.takeRecords()\n if (own.length) queue(own, false)\n }\n const observer = new Observer((records) => queue(records, true))\n observer.observe(target, {\n subtree: true,\n childList: true,\n attributes: true,\n attributeFilter: ['data-zap', 'data-zap-field', 'data-zap-entry', 'data-zap-locale'],\n characterData: true,\n })\n return () => {\n observer.disconnect()\n if (timer !== null) clear(timer)\n }\n}\n","/**\n * The keyboard shortcut that opens «Editar» and «Comentar» on the live site\n * (zap-cms-v2 §3.4, owner 2026-10-05): Shift plus one letter, `Z` by default.\n * It replaced «Shift twice», which fired while people typed capitals.\n *\n * Shared by the boot every visitor runs (`src/boot/boot.ts`, under 1 KB) and\n * the overlay core (`./boot.ts`), so the two can never disagree about what\n * counts. No directive, no imports.\n *\n * It never fires while the person is typing: focus in an input, a textarea, a\n * select or anything editable, or a key that is part of an IME composition\n * (`isComposing`, or the legacy `keyCode` 229 some browsers send instead).\n */\n\n/** The letter pressed with Shift when a site sets none. */\nexport const DEFAULT_SHORTCUT = 'Z'\n\n/**\n * Where typing happens: the target itself or anything it sits in. A\n * `contenteditable=\"false\"` region is not typing, so the shortcut still works\n * there; a field being edited on the live site is, so Shift+Z types a capital.\n */\nconst TYPING = 'input,textarea,select,[contenteditable]:not([contenteditable=\"false\"])'\n\nexport function isShortcut(event: KeyboardEvent, key: string): boolean {\n // The real target, inside an open shadow root too (a site's own components).\n const target = (event.composedPath?.()[0] ?? event.target) as Element | null\n return (\n event.shiftKey &&\n !(event.ctrlKey || event.metaKey || event.altKey || event.repeat || event.isComposing) &&\n // Some browsers report a key inside an IME composition only as keyCode 229.\n event.keyCode !== 229 &&\n event.key?.toLowerCase() === key.toLowerCase() &&\n !target?.closest?.(TYPING)\n )\n}\n"],"mappings":"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","names":["segment","candidate"]}
1
+ {"version":3,"sources":["../src/refs.ts","../src/preview/protocol.ts","../src/stega.ts","../src/preview/anchor.ts","../src/preview/overlay.ts","../src/preview/tags.ts","../src/preview/site/shortcut.ts"],"sourcesContent":["/**\n * The record and field grammar of tags (`.docs/proposals/zap-cms-v2.md` §2.4),\n * in a module of its own so the light entries (`./fields`, `./react`, the\n * boot) can name records without pulling in the preview protocol.\n * No imports, no side effects.\n */\n\n/**\n * A record reference in its canonical string form: `collectionKey/slug` for an\n * entry, `doc:documentKey` for a document (the tag grammar, §2.4).\n */\nexport type RecordRef = string\n\n/** Zap's field key pattern. */\nexport const FIELD_KEY_RE = /^[a-z][a-z0-9_]{0,127}$/\n/** Entry and document references; keys and slugs are `[a-z0-9]+(?:-[a-z0-9]+)*`. */\nconst ENTRY_REF_RE = /^([a-z0-9]+(?:-[a-z0-9]+)*)\\/([a-z0-9]+(?:-[a-z0-9]+)*)$/\nconst DOC_REF_RE = /^doc:([a-z0-9]+(?:-[a-z0-9]+)*)$/\nconst MAX_REF_LENGTH = 300\n\nexport type ParsedRecordRef =\n | { kind: 'entry'; collection: string; slug: string }\n | { kind: 'document'; key: string }\n\nexport function parseRecordRef(value: unknown): ParsedRecordRef | null {\n if (typeof value !== 'string' || value.length > MAX_REF_LENGTH) return null\n const doc = DOC_REF_RE.exec(value)\n if (doc) return { kind: 'document', key: doc[1]! }\n const entry = ENTRY_REF_RE.exec(value)\n if (entry) return { kind: 'entry', collection: entry[1]!, slug: entry[2]! }\n return null\n}\n\nexport function isRecordRef(value: unknown): value is RecordRef {\n return parseRecordRef(value) !== null\n}\n\n/** `zpt_` + 32 bytes in base64url, as Zap mints it (zap-cms-v2 §3.5). */\nexport const PREVIEW_TOKEN_RE = /^zpt_[A-Za-z0-9_-]{43}$/\n\n/** A preview token in the shape Zap mints; says nothing about whether it is live. */\nexport function isPreviewTokenShape(value: unknown): value is string {\n return typeof value === 'string' && PREVIEW_TOKEN_RE.test(value)\n}\n\nexport function isFieldKey(value: unknown): value is string {\n return typeof value === 'string' && FIELD_KEY_RE.test(value)\n}\n\n/**\n * What `zapAttrs` and `onValues` accept: a record reference string\n * (`blog/hola`, `doc:home`) or a delivery API entry (an item carries\n * `collection.key` and `slug`, a document `key`).\n */\nexport type EntryLike =\n | RecordRef\n | {\n slug?: string | null\n key?: string | null\n collection?: { key?: string | null } | null\n localizedSlugs?: Record<string, string | null | undefined> | null\n }\n\n/** Every reference an entry answers to (default slug first, then localized ones). */\nexport function recordRefsOf(entry: EntryLike | null | undefined): RecordRef[] {\n if (!entry) return []\n if (typeof entry === 'string') return isRecordRef(entry) ? [entry] : []\n const refs: RecordRef[] = []\n const add = (ref: string) => {\n if (isRecordRef(ref) && !refs.includes(ref)) refs.push(ref)\n }\n const collection = entry.collection?.key\n if (collection) {\n if (entry.slug) add(`${collection}/${entry.slug}`)\n for (const slug of Object.values(entry.localizedSlugs ?? {})) {\n if (slug) add(`${collection}/${slug}`)\n }\n } else if (entry.key) {\n add(`doc:${entry.key}`)\n }\n return refs\n}\n\n/**\n * The tag for one field, as attributes to spread on the element that renders\n * it: `{ 'data-zap': 'blog/hola#title' }`. Empty when the entry or the field\n * key is not one Zap can name, so a bad call never breaks the page.\n */\nexport function zapAttrs(entry: EntryLike, fieldKey: string): { 'data-zap'?: string } {\n const [ref] = recordRefsOf(entry)\n if (!ref || !isFieldKey(fieldKey)) return {}\n return { 'data-zap': `${ref}#${fieldKey}` }\n}\n","import { isFieldKey, isPreviewTokenShape, isRecordRef, type RecordRef } from '../refs'\n\n/**\n * The bridge protocol, version 1 (`.docs/proposals/zap-cms-v2.md` §2.6).\n *\n * The editor frames the customer's own site by its real URL, and the site's\n * Zap client (`live.ts`) answers. Every message is the envelope `{ source: 'eel-zap', v: 1, session, type, payload }`;\n * anything else is ignored.\n *\n * ## Hand-written validators, not zod\n *\n * The spec asks for \"a zod schema on both sides\". The browser core has a\n * 12 KB gzip budget and zero dependencies (§2.7, §7.4), and zod alone is\n * several times that, so every payload is checked here by small explicit\n * predicates instead. The EDITOR side imports these same validators\n * (`./editor`), so the two ends cannot disagree about what a valid message\n * is — one source of truth instead of a zod copy that drifts.\n *\n * Every validator is strict: wrong types, unknown enum values, strings over\n * their cap and arrays over their cap fail the WHOLE message (it is dropped\n * and counted), never coerced. Extra keys are ignored.\n *\n * ## Additions to the §2.6 table\n *\n * - `zap:zoom` (editor → page) `{ zoom }`: the panel's scale changes when the\n * panel is resized or the device switches, and the overlay divides its 2px\n * outline and 12px chip by it (ui-patterns/preview-panel.md rule 5). The page\n * cannot read its parent's CSS transform, so the editor tells it.\n * - `zap:highlight` (editor → page) `{ anchors }`: selecting a change request\n * highlights its anchors in the preview (§3.3 \"Where they show\"). The\n * anchors' selectors come from stored, user-influenced rows, so the overlay\n * re-finds them defensively (`findAnchorElement`).\n * - `zap:hello` also carries an optional `zoom` so the first paint is right.\n * - `zap:hello` carries an optional `previewToken` in Live mode (P7): the\n * `zpt_` token the editor minted for the draft-mode URL, so the client can\n * keep it in `sessionStorage` for the cookieless fallback (§2.5 \"Cookies in\n * the frame\"). The editor sends it only to the exact page origin that\n * answered `zap:ready` from the site's preview origins, the same site the\n * draft-mode URL already handed it to.\n * - Comments (§3.3 \"Anchors\", spot pins), only to a page whose `zap:ready`\n * advertised the `pins` capability (older CDN clients never see them):\n * mode `spot` (a click anywhere becomes `zap:spot` with the point as\n * fractions of the whole document), `zap:pins` (editor → page, the numbered\n * pins to draw, ≤ 50) and `zap:pin` (page → editor, a pin was clicked).\n * - Links, only to a page whose `zap:ready` advertised the `links` capability:\n * a URL field's value is `{ url }` and an EMAIL field's `{ email }`, so the\n * page writes a link's `href` (`mailto:` for an email) and leaves its label\n * alone (`values.ts`). Older clients get the bare string, as before.\n * - `zap:paused` (page → editor) `{ reason, count }`: the runaway guard\n * (`tagGuard` in `tags.ts`) stopped following the page: `cap` past 5,000\n * tagged elements, `runaway` when the count kept growing with nothing on\n * the page explaining it. The page stops writing values; the editor offers\n * a reload. Sent again after a hello, which a pause before it never reached.\n */\n\nexport const PROTOCOL_SOURCE = 'eel-zap'\n/**\n * `zap:error` code the page sends from `pagehide`: this document is going away\n * (a link, a refresh, a site script setting `location`). The editor holds\n * everything it would post until the next document says `zap:ready`, and\n * answers that one with the hello again.\n */\nexport const PAGE_UNLOAD_ERROR = 'PAGE_UNLOAD'\nexport const PROTOCOL_VERSION = 1\n/** Hard cap per message, in UTF-8 bytes of its JSON form (§2.6, §7.4). */\nexport const MAX_MESSAGE_BYTES = 256 * 1024\n/** §3.3 limits. */\nexport const MAX_ANCHORS = 20\nexport const MAX_QUOTE_EXACT = 500\nexport const MAX_QUOTE_CONTEXT = 32\nexport const MAX_SELECTOR_BYTES = 1024\nexport const MAX_URL_LENGTH = 2048\n/** Bounds on report sizes so a hostile or huge page cannot flood the editor. */\nexport const MAX_TAG_REPORT = 2000\nexport const MAX_LABELS = 500\n/** Pins drawn at once (`zap:pins`). */\nexport const MAX_PINS = 50\n\nexport type OverlayMode = 'inspect' | 'select' | 'spot' | 'off'\nexport const OVERLAY_MODES: readonly OverlayMode[] = ['inspect', 'select', 'spot', 'off']\n\nexport type Capability = 'overlay' | 'values' | 'stega' | 'pins' | 'links' | 'refresh'\n/** Every capability; a client built from this source advertises all of them. */\nexport const CAPABILITIES: readonly Capability[] = [\n 'overlay',\n 'values',\n 'stega',\n 'pins',\n 'links',\n 'refresh',\n]\n\nexport {\n FIELD_KEY_RE,\n isFieldKey,\n isPreviewTokenShape,\n PREVIEW_TOKEN_RE,\n isRecordRef,\n parseRecordRef,\n type ParsedRecordRef,\n type RecordRef,\n} from '../refs'\n\n/**\n * A value the editor sends for one field, ALREADY formatted for display.\n *\n * The browser core never formats: the editor owns the suite's formatters\n * (dates, numbers, enum labels; CLAUDE.md \"Money/dates/plurals go through\n * shared formatters\"), knows each field's type, and sends the text the page\n * should show. A bare string, number or boolean is shorthand for `{ text }`.\n * `{ url }` and `{ email }` (a URL and an EMAIL field, `links` capability) go\n * to a link's `href` rather than its text.\n */\nexport type PreviewValue =\n | string\n | number\n | boolean\n | null\n | { text: string }\n | { html: string }\n | { url: string }\n | { email: string }\n | { image: { src?: string | null; srcset?: string | null; alt?: string | null } }\n\n/** Longest EMAIL value accepted (RFC 5321's path limit). */\nexport const MAX_EMAIL_LENGTH = 320\n\n/** How the page found an element: a stega marker in its text, or a `data-zap` attribute. */\nexport type TagSource = 'stega' | 'attr'\n\nexport interface TagSummary {\n /** The canonical tag, `recordRef#fieldKey`. */\n tag: string\n recordRef: RecordRef\n fieldKey: string\n count: number\n /**\n * `attr` when at least one element carries a manual tag (a manual tag wins),\n * else `stega`. Optional on the wire: clients released before stega omit it.\n */\n source?: TagSource\n}\n\nexport interface TextQuote {\n exact: string\n prefix: string\n suffix: string\n}\n\nexport interface NormalizedRect {\n x: number\n y: number\n w: number\n h: number\n}\n\n/** The `dom` layer of an anchor (§3.3), captured by the overlay. */\nexport interface DomAnchor {\n tag: string | null\n selector: string | null\n textQuote: TextQuote | null\n rect: NormalizedRect\n viewport: { w: number; h: number }\n}\n\n/**\n * An anchor as the PAGE reports it. The editor adds the field's `id` (the page\n * knows keys, never ids) before storing it as a change-request anchor.\n */\nexport interface PageAnchor {\n field: { recordRef: RecordRef; key: string; locale: string | null } | null\n dom: DomAnchor\n pageUrl: string\n}\n\nexport interface ReadyPayload {\n version: string\n capabilities: Capability[]\n pageUrl: string\n /**\n * The site's draft-mode route as a PATH (`/api/zap-preview`), or null when\n * the site has none (§2.5, §7.5). Never an origin: the editor joins it to\n * the frame's verified origin (`draftModeUrl` in `./editor`), so a hostile\n * page cannot send a preview token to another host. Optional on the wire:\n * older clients omit it, which reads as null.\n */\n draftRoute?: string | null\n}\n\nexport interface HelloPayload {\n session: string\n siteKey: string\n locale: string\n labels: Record<string, string>\n recordRef: RecordRef\n mode: OverlayMode\n theme?: OverlayTheme\n zoom?: number\n /** Live only: the draft-mode token for the cookieless fallback. */\n previewToken?: string\n /**\n * Fields on the page that belong to ANOTHER record than `recordRef` (a\n * site-wide document in the header, a related entry): the full tag\n * (`TagSummary.tag`, `recordRef#fieldKey`) to `[label, suffix]`, the suffix\n * already localized («en Configuración»). The chip draws the label, then\n * the suffix muted. At most `MAX_LABELS`; optional, older clients ignore it.\n */\n foreign?: Record<string, [label: string, suffix: string]>\n}\n\n/** Colours the editor may override; `#rgb` / `#rrggbb` only. */\nexport interface OverlayTheme {\n outline?: string\n chip?: string\n chipText?: string\n}\n\nexport interface ValuesPayload {\n recordRef: RecordRef\n locale: string\n patch: Record<string, PreviewValue>\n}\n\nexport interface FieldRefPayload {\n recordRef: RecordRef\n fieldKey: string\n}\n\n/**\n * `zap:click`: the field, and optionally the clicked element's box in the\n * frame's viewport, CSS px before the editor's zoom (older clients omit it).\n */\nexport interface ClickPayload extends FieldRefPayload {\n rect?: { x: number; y: number; w: number; h: number }\n}\n\n/** A point as fractions (0..1) of the whole document, not the viewport (§3.3). */\nexport interface SpotPoint {\n x: number\n y: number\n}\n\n/** A spot anchor (§3.3): the point, and the document's scroll size when it was taken. */\nexport interface SpotAnchor {\n spot: SpotPoint\n viewport: { w: number; h: number }\n}\n\n/** `zap:spot`: the user clicked the page in `spot` mode. */\nexport interface SpotPayload extends SpotAnchor {\n pageUrl: string\n}\n\n/**\n * A numbered comment pin (`zap:pins`). Exactly one of `spot` (a point of the\n * document) or `dom` (an element, re-found like a highlight). `n` is the\n * number drawn on it.\n */\nexport type Pin = { id: string; n: number } & (\n | { spot: SpotPoint; dom?: undefined }\n | { dom: DomAnchor; spot?: undefined }\n)\n\n/** `zap:paused`: why the page stopped following itself, and the tagged count then. */\nexport interface PausedPayload {\n reason: 'runaway' | 'cap'\n count: number\n}\n\nexport interface PageMessages {\n 'zap:ready': ReadyPayload\n 'zap:tags': TagSummary[]\n 'zap:click': ClickPayload\n 'zap:select': { anchors: PageAnchor[] }\n 'zap:navigate': { url: string }\n 'zap:error': { code: string; detail?: string }\n 'zap:spot': SpotPayload\n 'zap:pin': { id: string }\n 'zap:paused': PausedPayload\n}\n\nexport interface EditorMessages {\n 'zap:hello': HelloPayload\n 'zap:values': ValuesPayload\n 'zap:focus-field': FieldRefPayload\n 'zap:mode': OverlayMode\n 'zap:zoom': { zoom: number }\n /**\n * Reload the page in place (`refresh` capability): for values the overlay\n * cannot place, once the draft is saved. The browser keeps the scroll and\n * the draft-mode cookie keeps the draft; the new document says ready.\n */\n 'zap:refresh': Record<string, never>\n 'zap:highlight': { anchors: DomAnchor[] }\n 'zap:pins': { pins: Pin[] }\n}\n\nexport type PageMessageType = keyof PageMessages\nexport type EditorMessageType = keyof EditorMessages\n\nexport interface Envelope<T extends string = string, P = unknown> {\n source: typeof PROTOCOL_SOURCE\n v: typeof PROTOCOL_VERSION\n session: string\n type: T\n payload: P\n}\n\nexport type PageMessage = {\n [K in PageMessageType]: Envelope<K, PageMessages[K]>\n}[PageMessageType]\n\nexport type EditorMessage = {\n [K in EditorMessageType]: Envelope<K, EditorMessages[K]>\n}[EditorMessageType]\n\n/** Why an incoming message was dropped. Every drop is counted. */\nexport type DropReason =\n | 'not-envelope'\n | 'oversize'\n | 'unknown-type'\n | 'invalid-payload'\n | 'wrong-source'\n | 'wrong-origin'\n | 'wrong-session'\n\nexport type DropStats = Record<DropReason, number>\n\nexport function emptyDropStats(): DropStats {\n return {\n 'not-envelope': 0,\n oversize: 0,\n 'unknown-type': 0,\n 'invalid-payload': 0,\n 'wrong-source': 0,\n 'wrong-origin': 0,\n 'wrong-session': 0,\n }\n}\n\n// ── Grammar ─────────────────────────────────────────────────────────────────\n\nconst LOCALE_RE = /^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8}){0,3}$/\nconst SESSION_RE = /^[0-9a-f]{32}$/\nconst COLOR_RE = /^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/\nconst ERROR_CODE_RE = /^[A-Z][A-Z0-9_]{0,63}$/\nconst PIN_ID_RE = /^[A-Za-z0-9_-]{1,64}$/\n\nexport function isLocale(value: unknown): value is string {\n return typeof value === 'string' && LOCALE_RE.test(value)\n}\n\n/** A session id as `createSession()` makes it: 128 bits, lowercase hex. */\nexport function isSession(value: unknown): value is string {\n return typeof value === 'string' && SESSION_RE.test(value)\n}\n\n// ── Small predicates ────────────────────────────────────────────────────────\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction isString(value: unknown, max: number): value is string {\n return typeof value === 'string' && value.length <= max\n}\n\nfunction isFiniteNumber(value: unknown, min: number, max: number): value is number {\n return typeof value === 'number' && Number.isFinite(value) && value >= min && value <= max\n}\n\nfunction isOptional<T>(value: unknown, check: (v: unknown) => v is T): boolean {\n return value === undefined || check(value)\n}\n\nfunction isNullableString(value: unknown, max: number): boolean {\n return value === null || value === undefined || isString(value, max)\n}\n\nfunction isHttpUrl(value: unknown): value is string {\n if (!isString(value, MAX_URL_LENGTH)) return false\n try {\n const url = new URL(value)\n return url.protocol === 'https:' || url.protocol === 'http:'\n } catch {\n return false\n }\n}\n\n/** Longest draft-mode route path accepted. */\nexport const MAX_DRAFT_ROUTE_LENGTH = 512\nconst DRAFT_ROUTE_RE = /^\\/[A-Za-z0-9\\-._~!$&'()*+,;=:@%/]*$/\n\n/**\n * A draft-mode route path: starts with exactly one `/`, then only URL path\n * characters (no `\\\\`, no whitespace or control character, no `?` or `#`), no\n * `//` anywhere and no dot segment, so that joined to any origin it stays on\n * that origin and names the same path. Anything that could carry or imply a\n * host (`//evil.example`, `/\\\\evil.example`, `https://…`, `/./`, `/..`) fails.\n */\nexport function isDraftRoutePath(value: unknown): value is string {\n if (typeof value !== 'string' || value.length > MAX_DRAFT_ROUTE_LENGTH) return false\n if (!DRAFT_ROUTE_RE.test(value) || value.includes('//')) return false\n return !value.split('/').some((segment) => /^(?:\\.|%2e){1,2}$/i.test(segment))\n}\n\nfunction isMode(value: unknown): value is OverlayMode {\n return typeof value === 'string' && (OVERLAY_MODES as readonly string[]).includes(value)\n}\n\nfunction isTheme(value: unknown): value is OverlayTheme {\n if (!isObject(value)) return false\n for (const key of ['outline', 'chip', 'chipText'] as const) {\n const v = value[key]\n if (v !== undefined && !(typeof v === 'string' && COLOR_RE.test(v))) return false\n }\n return true\n}\n\nfunction isZoom(value: unknown): value is number {\n return isFiniteNumber(value, 0.05, 4)\n}\n\nexport function isPinId(value: unknown): value is string {\n return typeof value === 'string' && PIN_ID_RE.test(value)\n}\n\nfunction isSpotPoint(value: unknown): value is SpotPoint {\n return isObject(value) && isFiniteNumber(value.x, 0, 1) && isFiniteNumber(value.y, 0, 1)\n}\n\n/** A pin: id grammar, integer `n` 1..999, and exactly one of `spot` / `dom`. */\nexport function isPin(value: unknown): value is Pin {\n return (\n isObject(value) &&\n isPinId(value.id) &&\n Number.isInteger(value.n) &&\n isFiniteNumber(value.n, 1, 999) &&\n (value.spot === undefined) !== (value.dom === undefined) &&\n (value.spot === undefined ? isDomAnchor(value.dom) : isSpotPoint(value.spot))\n )\n}\n\nfunction isFieldRef(value: unknown): value is FieldRefPayload & Record<string, unknown> {\n return isObject(value) && isRecordRef(value.recordRef) && isFieldKey(value.fieldKey)\n}\n\nexport function isPreviewValue(value: unknown): value is PreviewValue {\n if (value === null || typeof value === 'boolean') return true\n if (typeof value === 'string') return true\n if (typeof value === 'number') return Number.isFinite(value)\n if (!isObject(value)) return false\n const keys = Object.keys(value)\n if (keys.length !== 1) return false\n if ('text' in value) return typeof value.text === 'string'\n if ('html' in value) return typeof value.html === 'string'\n if ('url' in value) return isString(value.url, MAX_URL_LENGTH)\n if ('email' in value) return isString(value.email, MAX_EMAIL_LENGTH)\n if ('image' in value) {\n const image = value.image\n return (\n isObject(image) &&\n isNullableString(image.src, MAX_URL_LENGTH) &&\n isNullableString(image.srcset, MAX_URL_LENGTH * 4) &&\n isNullableString(image.alt, 2000)\n )\n }\n return false\n}\n\nfunction isTextQuote(value: unknown): value is TextQuote {\n return (\n isObject(value) &&\n isString(value.exact, MAX_QUOTE_EXACT) &&\n isString(value.prefix, MAX_QUOTE_CONTEXT) &&\n isString(value.suffix, MAX_QUOTE_CONTEXT)\n )\n}\n\nfunction isRect(value: unknown): value is NormalizedRect {\n return (\n isObject(value) &&\n isFiniteNumber(value.x, -1, 2) &&\n isFiniteNumber(value.y, -1, 2) &&\n isFiniteNumber(value.w, 0, 2) &&\n isFiniteNumber(value.h, 0, 2)\n )\n}\n\n/** The selector cap is in BYTES (§3.3), so a multi-byte selector counts right. */\nexport function utf8Length(value: string): number {\n return new TextEncoder().encode(value).length\n}\n\nexport function isDomAnchor(value: unknown): value is DomAnchor {\n if (!isObject(value)) return false\n const tagOk = value.tag === null || (isString(value.tag, 512) && parseTag(value.tag) !== null)\n const selectorOk =\n value.selector === null ||\n (typeof value.selector === 'string' &&\n value.selector.length > 0 &&\n utf8Length(value.selector) <= MAX_SELECTOR_BYTES)\n const viewport = value.viewport\n return (\n tagOk &&\n selectorOk &&\n (value.textQuote === null || isTextQuote(value.textQuote)) &&\n isRect(value.rect) &&\n isObject(viewport) &&\n isFiniteNumber(viewport.w, 0, 100_000) &&\n isFiniteNumber(viewport.h, 0, 10_000_000)\n )\n}\n\nexport function isPageAnchor(value: unknown): value is PageAnchor {\n if (!isObject(value)) return false\n const field = value.field\n const fieldOk =\n field === null ||\n (isObject(field) &&\n isRecordRef(field.recordRef) &&\n isFieldKey(field.key) &&\n (field.locale === null || isLocale(field.locale)))\n return fieldOk && isDomAnchor(value.dom) && isHttpUrl(value.pageUrl)\n}\n\nfunction isAnchorList<T>(value: unknown, check: (v: unknown) => v is T): value is T[] {\n return Array.isArray(value) && value.length <= MAX_ANCHORS && value.every(check)\n}\n\n/**\n * Parse `recordRef#fieldKey` (the `data-zap` grammar). Exported here because\n * `isDomAnchor` checks a reported tag with it; `tags.ts` re-exports it.\n */\nexport function parseTag(value: string): { recordRef: RecordRef; fieldKey: string } | null {\n const trimmed = value.trim()\n const hash = trimmed.lastIndexOf('#')\n if (hash <= 0) return null\n const recordRef = trimmed.slice(0, hash)\n const fieldKey = trimmed.slice(hash + 1)\n if (!isRecordRef(recordRef) || !isFieldKey(fieldKey)) return null\n return { recordRef, fieldKey }\n}\n\n// ── Payload validators per type ─────────────────────────────────────────────\n\nconst PAGE_VALIDATORS: { [K in PageMessageType]: (p: unknown) => boolean } = {\n 'zap:ready': (p) =>\n isObject(p) &&\n isString(p.version, 32) &&\n Array.isArray(p.capabilities) &&\n p.capabilities.length <= CAPABILITIES.length &&\n p.capabilities.every((c) => (CAPABILITIES as readonly unknown[]).includes(c)) &&\n isHttpUrl(p.pageUrl) &&\n (p.draftRoute === undefined || p.draftRoute === null || isDraftRoutePath(p.draftRoute)),\n 'zap:tags': (p) =>\n Array.isArray(p) &&\n p.length <= MAX_TAG_REPORT &&\n p.every(\n (t) =>\n isObject(t) &&\n typeof t.tag === 'string' &&\n isRecordRef(t.recordRef) &&\n isFieldKey(t.fieldKey) &&\n t.tag === `${t.recordRef}#${t.fieldKey}` &&\n Number.isInteger(t.count) &&\n (t.count as number) > 0 &&\n (t.source === undefined || t.source === 'stega' || t.source === 'attr'),\n ),\n 'zap:click': (p) =>\n isFieldRef(p) &&\n (p.rect === undefined ||\n (isObject(p.rect) &&\n isFiniteNumber(p.rect.x, -1e7, 1e7) &&\n isFiniteNumber(p.rect.y, -1e7, 1e7) &&\n isFiniteNumber(p.rect.w, 0, 1e7) &&\n isFiniteNumber(p.rect.h, 0, 1e7))),\n 'zap:select': (p) => isObject(p) && isAnchorList(p.anchors, isPageAnchor),\n 'zap:navigate': (p) => isObject(p) && isHttpUrl(p.url),\n 'zap:error': (p) =>\n isObject(p) &&\n typeof p.code === 'string' &&\n ERROR_CODE_RE.test(p.code) &&\n isOptional(p.detail, (d): d is string => isString(d, 500)),\n 'zap:spot': (p) =>\n isObject(p) &&\n isSpotPoint(p.spot) &&\n isObject(p.viewport) &&\n isFiniteNumber(p.viewport.w, 1, 100_000) &&\n isFiniteNumber(p.viewport.h, 1, 10_000_000) &&\n isHttpUrl(p.pageUrl),\n 'zap:pin': (p) => isObject(p) && isPinId(p.id),\n 'zap:paused': (p) =>\n isObject(p) &&\n (p.reason === 'runaway' || p.reason === 'cap') &&\n Number.isInteger(p.count) &&\n (p.count as number) >= 0,\n}\n\nconst EDITOR_VALIDATORS: { [K in EditorMessageType]: (p: unknown) => boolean } = {\n 'zap:hello': (p) => {\n if (!isObject(p) || !isSession(p.session) || !isString(p.siteKey, 128)) return false\n if (!isLocale(p.locale) || !isRecordRef(p.recordRef) || !isMode(p.mode)) return false\n if (p.theme !== undefined && !isTheme(p.theme)) return false\n if (p.zoom !== undefined && !isZoom(p.zoom)) return false\n if (p.previewToken !== undefined && !isPreviewTokenShape(p.previewToken)) return false\n // Both maps capped and checked entry by entry: labels by field key,\n // `foreign` by full tag to `[label, suffix]`.\n const within = (map: unknown, ok: (key: string, value: unknown) => boolean) =>\n isObject(map) &&\n Object.keys(map).length <= MAX_LABELS &&\n Object.entries(map).every(([key, value]) => ok(key, value))\n return (\n within(p.labels, (key, label) => isFieldKey(key) && isString(label, 200)) &&\n within(\n p.foreign ?? {},\n (tag, pair) =>\n !!parseTag(tag) &&\n Array.isArray(pair) &&\n pair.length === 2 &&\n pair.every((part) => isString(part, 200)),\n )\n )\n },\n 'zap:values': (p) => {\n if (!isObject(p) || !isRecordRef(p.recordRef) || !isLocale(p.locale)) return false\n const patch = p.patch\n if (!isObject(patch)) return false\n return Object.entries(patch).every(([key, value]) => isFieldKey(key) && isPreviewValue(value))\n },\n 'zap:focus-field': isFieldRef,\n 'zap:mode': isMode,\n 'zap:zoom': (p) => isObject(p) && isZoom(p.zoom),\n 'zap:refresh': isObject,\n 'zap:highlight': (p) => isObject(p) && isAnchorList(p.anchors, isDomAnchor),\n 'zap:pins': (p) =>\n isObject(p) && Array.isArray(p.pins) && p.pins.length <= MAX_PINS && p.pins.every(isPin),\n}\n\nexport const PAGE_MESSAGE_TYPES = /* @__PURE__ */ Object.keys(PAGE_VALIDATORS) as PageMessageType[]\nexport const EDITOR_MESSAGE_TYPES = /* @__PURE__ */ Object.keys(\n EDITOR_VALIDATORS,\n) as EditorMessageType[]\n\n// ── Envelope ────────────────────────────────────────────────────────────────\n\nexport type ParseResult<M> = { ok: true; message: M } | { ok: false; reason: DropReason }\n\n/** UTF-8 size of the message's JSON form, or `Infinity` when it has none. */\nexport function messageBytes(data: unknown): number {\n try {\n const json = JSON.stringify(data)\n return json === undefined ? Infinity : utf8Length(json)\n } catch {\n return Infinity\n }\n}\n\nfunction parseWith<M>(\n data: unknown,\n validators: Record<string, (p: unknown) => boolean>,\n): ParseResult<M> {\n if (\n !isObject(data) ||\n data.source !== PROTOCOL_SOURCE ||\n data.v !== PROTOCOL_VERSION ||\n typeof data.session !== 'string' ||\n data.session.length > 64 ||\n typeof data.type !== 'string'\n ) {\n return { ok: false, reason: 'not-envelope' }\n }\n // The cap comes before any deep validation: a 50 MB payload is refused on\n // its size, not walked.\n if (messageBytes(data) > MAX_MESSAGE_BYTES) return { ok: false, reason: 'oversize' }\n const validate = Object.prototype.hasOwnProperty.call(validators, data.type)\n ? validators[data.type]\n : undefined\n if (!validate) return { ok: false, reason: 'unknown-type' }\n if (!validate(data.payload)) return { ok: false, reason: 'invalid-payload' }\n return {\n ok: true,\n message: {\n source: PROTOCOL_SOURCE,\n v: PROTOCOL_VERSION,\n session: data.session,\n type: data.type,\n payload: data.payload,\n } as M,\n }\n}\n\n/** Parse what the PAGE received: only editor → page types are known. */\nexport function parseEditorMessage(data: unknown): ParseResult<EditorMessage> {\n return parseWith<EditorMessage>(data, EDITOR_VALIDATORS)\n}\n\n/** Parse what the EDITOR received: only page → editor types are known. */\nexport function parsePageMessage(data: unknown): ParseResult<PageMessage> {\n return parseWith<PageMessage>(data, PAGE_VALIDATORS)\n}\n\nexport function envelope<T extends string, P>(\n type: T,\n session: string,\n payload: P,\n): Envelope<T, P> {\n return { source: PROTOCOL_SOURCE, v: PROTOCOL_VERSION, session, type, payload }\n}\n","/**\n * Stega: invisible field markers on prose values read in preview\n * (`.docs/proposals/zap-cms-v2.md` §2.4, §7.5).\n *\n * Zap appends a marker to TEXT and LONG_TEXT values, and to the last text\n * node of each RICH_TEXT block, ONLY in `preview=1` delivery responses. The\n * preview overlay finds the marker in the page's text nodes and so knows\n * which element renders which field, with no manual tag. Published responses\n * never carry one.\n *\n * ## Format (eelzap stega v1)\n *\n * A marker is the tag the overlay would read from a `data-zap` attribute,\n * `recordRef#fieldKey`, plus `@locale` when the value belongs to a locale:\n *\n * blog-posts/mi-primer-post#title@es\n * doc:home#hero_title@es\n *\n * written as zero-width characters:\n *\n * - four digits, base 4: U+200B (0), U+200C (1), U+200D (2), U+2060 (3);\n * - a prefix of 8 digits, the bytes 0xE5 0xA7 (`3211 2213`);\n * - then every byte of the ASCII payload as 4 digits, most significant first.\n *\n * The payload is printable ASCII (the tag grammar allows nothing else), so\n * every payload byte starts with digit 0 or 1 and the prefix, which starts\n * with 3, can never be read inside a payload. A decoder reads groups of four\n * after the prefix until a group is not a payload byte, then validates the\n * result against the tag grammar; anything that does not parse is ignored. A\n * value never carries two markers (`stegaEncode` refuses to add a second).\n *\n * Why not `@vercel/stega`'s format: it encodes a JSON object (a URL and\n * origin in Sanity's and Vercel's use) and uses U+FEFF, which some tools strip\n * as a byte order mark. Here the decoded marker IS the tag grammar\n * (`data-zap`), so one parser serves both, and U+FEFF is avoided. No\n * dependency either way.\n *\n * Zero dependencies, no side effects: `.` re-exports `cleanStega` and\n * `hasStega`; the encoder is internal (Zap's delivery imports it); the\n * decoder serves `./preview`.\n */\n\nconst DIGITS = ['​', '‌', '‍', '⁠'] as const\nconst PREFIX = '⁠‍‌‌‍‍‌⁠'\n/** Longest payload a marker may carry, in bytes (ref 300 + field 128 + locale). */\nconst MAX_PAYLOAD = 512\n\n/** `recordRef#fieldKey` with an optional `@locale`: the tag grammar (`refs.ts`). */\nconst PAYLOAD_RE =\n /^(doc:[a-z0-9]+(?:-[a-z0-9]+)*|[a-z0-9]+(?:-[a-z0-9]+)*\\/[a-z0-9]+(?:-[a-z0-9]+)*)#([a-z][a-z0-9_]{0,127})(?:@([A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8}){0,3}))?$/\n/** The prefix, then whole payload bytes (first digit 0 or 1). */\nconst MARKER_RE = /⁠‍‌‌‍‍‌⁠(?:[​‌][​‌‍⁠]{3})*/g\n\n/** What one marker says: the record, the field, and the locale when known. */\nexport interface StegaInfo {\n /** `collectionKey/slug` or `doc:documentKey`. */\n recordRef: string\n fieldKey: string\n locale?: string | null\n}\n\nexport interface DecodedStega {\n recordRef: string\n fieldKey: string\n locale: string | null\n /** `recordRef#fieldKey`, the `data-zap` tag. */\n tag: string\n}\n\n/** The marker for one field, or `''` when the info does not fit the tag grammar. */\nexport function stegaMarker(info: StegaInfo): string {\n const payload = `${info.recordRef}#${info.fieldKey}${info.locale ? `@${info.locale}` : ''}`\n if (payload.length > MAX_PAYLOAD || !PAYLOAD_RE.test(payload)) return ''\n let out = PREFIX\n for (let i = 0; i < payload.length; i++) {\n const byte = payload.charCodeAt(i)\n out +=\n DIGITS[(byte >> 6) & 3]! +\n DIGITS[(byte >> 4) & 3]! +\n DIGITS[(byte >> 2) & 3]! +\n DIGITS[byte & 3]!\n }\n return out\n}\n\n/**\n * `text` with the field's marker appended. A string that already carries a\n * marker, or info outside the grammar, comes back unchanged.\n */\nexport function stegaEncode(text: string, info: StegaInfo): string {\n if (hasStega(text)) return text\n return text + stegaMarker(info)\n}\n\n/** True when `value` is a string carrying at least one marker. */\nexport function hasStega(value: unknown): boolean {\n return typeof value === 'string' && value.includes(PREFIX)\n}\n\n/** Every valid marker in `text`, in order. Invalid ones are skipped. */\nexport function stegaDecode(text: string): DecodedStega[] {\n if (!text.includes(PREFIX)) return []\n const found: DecodedStega[] = []\n for (const match of text.match(MARKER_RE) ?? []) {\n let payload = ''\n for (let i = PREFIX.length; i + 4 <= match.length; i += 4) {\n let byte = 0\n for (let j = 0; j < 4; j++) byte = byte * 4 + DIGITS.indexOf(match[i + j] as never)\n payload += String.fromCharCode(byte)\n }\n const parsed = PAYLOAD_RE.exec(payload)\n if (!parsed) continue\n found.push({\n recordRef: parsed[1]!,\n fieldKey: parsed[2]!,\n locale: parsed[3] ?? null,\n tag: `${parsed[1]}#${parsed[2]}`,\n })\n }\n return found\n}\n\n/** `text` split into its visible part and the markers it carries, in order. */\nexport function splitStega(text: string): { text: string; markers: string } {\n if (!text.includes(PREFIX)) return { text, markers: '' }\n return { text: text.replace(MARKER_RE, ''), markers: (text.match(MARKER_RE) ?? []).join('') }\n}\n\n/** `text` without any marker. */\nfunction cleanString(text: string): string {\n return text.includes(PREFIX) ? text.replace(MARKER_RE, '') : text\n}\n\n/**\n * `value` with every stega marker removed: a string is cleaned, an array or a\n * plain object is cleaned deeply (into new objects), anything else is\n * returned as is. Use it where a preview value is compared, parsed or put in\n * an attribute, a URL or `<head>`; outside preview it changes nothing.\n */\nexport function cleanStega<T>(value: T): T {\n if (typeof value === 'string') return cleanString(value) as T\n if (Array.isArray(value)) return value.map((item) => cleanStega(item)) as T\n if (value && typeof value === 'object' && Object.getPrototypeOf(value) === Object.prototype) {\n const out: Record<string, unknown> = {}\n for (const [key, item] of Object.entries(value)) out[key] = cleanStega(item)\n return out as T\n }\n return value\n}\n","import { cleanStega } from '../stega'\nimport {\n MAX_QUOTE_CONTEXT,\n MAX_QUOTE_EXACT,\n MAX_SELECTOR_BYTES,\n utf8Length,\n type DomAnchor,\n type PageAnchor,\n type TextQuote,\n} from './protocol'\nimport type { TagIndex, TaggedElement } from './tags'\n\n/**\n * Anchors for change requests (§3.3 \"Anchors\"): the `dom` layer is captured\n * here, in the page, never by the server. Re-finding goes tag → selector →\n * text quote → rectangle.\n */\n\n/** `CSS.escape`, with a fallback for environments without it. */\nexport function cssEscape(value: string): string {\n const native = (globalThis as { CSS?: { escape?: (v: string) => string } }).CSS?.escape\n if (native) return native(value)\n // The CSSOM `serialize an identifier` algorithm.\n let out = ''\n for (let i = 0; i < value.length; i++) {\n const ch = value.charAt(i)\n const code = value.charCodeAt(i)\n const digit = code >= 48 && code <= 57\n if (code === 0) out += '�'\n else if (\n (code >= 1 && code <= 31) ||\n code === 127 ||\n (i === 0 && digit) ||\n (i === 1 && digit && value.charCodeAt(0) === 45)\n ) {\n out += `\\\\${code.toString(16)} `\n } else if (i === 0 && value.length === 1 && code === 45) out += `\\\\${ch}`\n else if (code >= 128 || code === 45 || code === 95 || digit || /[A-Za-z]/.test(ch)) out += ch\n else out += `\\\\${ch}`\n }\n return out\n}\n\n/**\n * `querySelectorAll` that never throws: a selector that did not come from\n * `buildSelector` in THIS page (a stored anchor, a message) may be malformed\n * or hostile, and a SyntaxError must not take the overlay down (§7 audit).\n */\nexport function safeQueryAll(root: ParentNode, selector: string): Element[] {\n try {\n return Array.from(root.querySelectorAll(selector))\n } catch {\n return []\n }\n}\n\n/** Stable-looking ids only: framework-generated ids (`:r1:`, `radix-…7`) change per render. */\nfunction usableId(element: Element): string | null {\n const id = element.getAttribute('id')\n if (!id || id.length > 64 || /^[:\\d]|:|\\d{3,}/.test(id)) return null\n return id\n}\n\nfunction segment(element: Element): string {\n const tag = element.localName\n const parent = element.parentElement\n if (!parent) return tag\n let index = 0\n let count = 0\n for (const sibling of Array.from(parent.children)) {\n if (sibling.localName !== tag) continue\n count++\n if (sibling === element) index = count\n }\n return count > 1 ? `${tag}:nth-of-type(${index})` : tag\n}\n\nfunction isUnique(doc: Document, selector: string, element: Element): boolean {\n const found = safeQueryAll(doc, selector)\n return found.length === 1 && found[0] === element\n}\n\n/**\n * A short CSS selector that matches exactly `element` today, or null when no\n * selector under 1 KB does.\n *\n * Walks up from the element, one `tag:nth-of-type(n)` segment per level, and\n * stops at the first ancestor with a usable id or a `data-zap*` attribute\n * (both are things the site chose to name, so they outlive a redesign better\n * than a position does), or as soon as the path is unique. Classes are left\n * out on purpose: utility and hashed class names change on every deploy.\n */\nexport function buildSelector(element: Element, doc: Document): string | null {\n const ownId = usableId(element)\n if (ownId) {\n const selector = `#${cssEscape(ownId)}`\n if (isUnique(doc, selector, element)) return selector\n }\n const parts: string[] = []\n for (let node: Element | null = element; node; node = node.parentElement) {\n if (node.localName === 'html') break\n if (node !== element) {\n const id = usableId(node)\n if (id) {\n const candidate = [`#${cssEscape(id)}`, ...parts].join(' > ')\n if (isUnique(doc, candidate, element)) return fit(candidate)\n }\n const zap = node.getAttribute('data-zap') ?? node.getAttribute('data-zap-entry')\n const zapAttr = node.hasAttribute('data-zap') ? 'data-zap' : 'data-zap-entry'\n if (zap) {\n const candidate = [`[${zapAttr}=\"${cssEscape(zap)}\"]`, ...parts].join(' > ')\n if (isUnique(doc, candidate, element)) return fit(candidate)\n }\n }\n parts.unshift(segment(node))\n const candidate = parts.join(' > ')\n if (isUnique(doc, candidate, element)) return fit(candidate)\n }\n const full = parts.join(' > ')\n return full && isUnique(doc, full, element) ? fit(full) : null\n}\n\nfunction fit(selector: string): string | null {\n return utf8Length(selector) <= MAX_SELECTOR_BYTES ? selector : null\n}\n\n/**\n * Collapse whitespace the way a reader sees text, without stega markers: an\n * element's text in preview carries them, and they are never content (a\n * quote, a «before» or a typed value read from the page must not store one).\n */\nexport function normalizeText(value: string): string {\n return cleanStega(value).replace(/\\s+/g, ' ').trim()\n}\n\n/** W3C TextQuoteSelector-shaped quote: the element's text plus 32 chars around it. */\nexport function textQuoteFor(element: Element, doc: Document): TextQuote | null {\n const exact = normalizeText(element.textContent ?? '').slice(0, MAX_QUOTE_EXACT)\n if (!exact) return null\n let prefix = ''\n let suffix = ''\n const body = doc.body\n if (body && body.contains(element) && typeof doc.createRange === 'function') {\n try {\n const before = doc.createRange()\n before.setStart(body, 0)\n before.setEndBefore(element)\n prefix = normalizeText(before.toString()).slice(-MAX_QUOTE_CONTEXT)\n const after = doc.createRange()\n after.setStartAfter(element)\n after.setEnd(body, body.childNodes.length)\n suffix = normalizeText(after.toString()).slice(0, MAX_QUOTE_CONTEXT)\n } catch {\n prefix = ''\n suffix = ''\n }\n }\n return { exact, prefix, suffix }\n}\n\nfunction round(value: number): number {\n return Math.round(value * 10_000) / 10_000\n}\n\nfunction pageSize(doc: Document): { w: number; h: number } {\n const root = doc.documentElement\n return {\n w: Math.max(root.scrollWidth, root.clientWidth, 1),\n h: Math.max(root.scrollHeight, root.clientHeight, 1),\n }\n}\n\n/** The element's box as fractions of the whole page (not the viewport). */\nexport function normalizedRect(element: Element, doc: Document, win: Window) {\n const box = element.getBoundingClientRect()\n const size = pageSize(doc)\n const clamp = (v: number, lo: number, hi: number) => Math.min(hi, Math.max(lo, v))\n return {\n rect: {\n x: round(clamp((box.left + win.scrollX) / size.w, -1, 2)),\n y: round(clamp((box.top + win.scrollY) / size.h, -1, 2)),\n w: round(clamp(box.width / size.w, 0, 2)),\n h: round(clamp(box.height / size.h, 0, 2)),\n },\n viewport: size,\n }\n}\n\nexport function buildDomAnchor(\n element: Element,\n tagged: TaggedElement | null,\n doc: Document,\n win: Window,\n): DomAnchor {\n return {\n tag: tagged?.tag ?? null,\n selector: buildSelector(element, doc),\n textQuote: textQuoteFor(element, doc),\n ...normalizedRect(element, doc, win),\n }\n}\n\n/**\n * The anchor for a selected element: a `field` layer when the element is (or\n * sits inside) a tagged field, and always a `dom` layer.\n */\nexport function buildAnchor(\n element: Element,\n index: TagIndex,\n pageUrl: string,\n doc: Document,\n win: Window,\n): PageAnchor {\n const tagged = index.get(element)\n return {\n field: tagged\n ? { recordRef: tagged.recordRef, key: tagged.fieldKey, locale: tagged.locale }\n : null,\n dom: buildDomAnchor(element, tagged, doc, win),\n pageUrl,\n }\n}\n\n/**\n * Re-find an anchor's element: the tag first, then the selector, then the\n * text quote, then the rectangle. Never throws.\n */\nexport function findAnchorElement(\n anchor: DomAnchor,\n index: TagIndex,\n doc: Document,\n win: Window,\n): Element | null {\n if (anchor.tag) {\n const hash = anchor.tag.lastIndexOf('#')\n const tagged = index.byField(anchor.tag.slice(0, hash), anchor.tag.slice(hash + 1))\n if (tagged.length === 1) return tagged[0]!.element\n if (tagged.length > 1 && anchor.textQuote) {\n const exact = anchor.textQuote.exact\n const match = tagged.find((t) => normalizeText(t.element.textContent ?? '') === exact)\n if (match) return match.element\n }\n if (tagged.length > 0) return tagged[0]!.element\n }\n\n if (anchor.selector) {\n const found = safeQueryAll(doc, anchor.selector)\n if (found.length === 1) return found[0]!\n }\n\n if (anchor.textQuote && anchor.textQuote.exact.length >= 3 && doc.body) {\n const exact = anchor.textQuote.exact\n let best: Element | null = null\n let bestLength = Infinity\n for (const element of Array.from(doc.body.getElementsByTagName('*'))) {\n const text = normalizeText(element.textContent ?? '')\n if (text.length < bestLength && text.includes(exact)) {\n best = element\n bestLength = text.length\n }\n }\n if (best) return best\n }\n\n const fromPoint = (doc as { elementFromPoint?: Document['elementFromPoint'] }).elementFromPoint\n if (typeof fromPoint === 'function') {\n const size = pageSize(doc)\n const x = (anchor.rect.x + anchor.rect.w / 2) * size.w - win.scrollX\n const y = (anchor.rect.y + anchor.rect.h / 2) * size.h - win.scrollY\n if (x >= 0 && y >= 0 && x <= win.innerWidth && y <= win.innerHeight) {\n try {\n return fromPoint.call(doc, x, y)\n } catch {\n return null\n }\n }\n }\n return null\n}\n","import {\n MAX_ANCHORS,\n MAX_PINS,\n type OverlayMode,\n type OverlayTheme,\n type SpotAnchor,\n type SpotPoint,\n} from './protocol'\nimport type { TagIndex, TaggedElement } from './tags'\n\n/**\n * The in-page overlay (§2.4 \"Finding the element for a field\", §3.3\n * \"Selecting in the preview\", ui-patterns/preview-panel.md rule 5).\n *\n * - Everything is drawn in a CLOSED Shadow DOM on a custom element appended\n * to `<html>`, so the site's CSS cannot restyle it and it cannot restyle\n * the site, and site scripts cannot reach into it through `shadowRoot`.\n * - Outlines are 2px gold with a field chip at the element's top right, both\n * DIVIDED BY THE ZOOM so they stay 2px and 12px on screen when the panel\n * scales the page down (Designer's boards PreviewEtiquetada /\n * SeleccionarElementos: outline #CF8700, chip #9A6200 with white text,\n * selected elements tinted rgba(207,135,0,0.10) and the chip gains a check).\n * - Modes: `inspect` (hover outlines tagged elements; click focuses the field\n * in the form), `select` (hover outlines any element; click selects, Shift\n * or Cmd/Ctrl click adds, Esc clears; with `spotOnLarge`, a plain click on\n * the page itself or on an element covering half the viewport or more\n * places a spot instead, as the site's Comentar does: one tool comments on\n * elements and on points), `off` (nothing drawn, nothing\n * intercepted), `spot` (comments: a transparent capture layer in the shadow\n * root covers the page, so a click never reaches the site; the click becomes\n * a point of the whole document and a pending pin is drawn there; Esc clears\n * it; no hover outlines).\n * - Numbered comment pins (`setPins`) are drawn in every mode but `off` (in\n * `off` too with `pinsWhenOff`, the live site's «Navegar»), and are the only\n * things here that take the pointer besides the capture layer.\n * A click on one is consumed and reported (`onPin`), never also a spot.\n * - Boxes are `position: fixed` from `getBoundingClientRect()` and redrawn on\n * scroll and resize in one animation frame, so nothing in the site's layout\n * moves.\n */\n\nexport const OVERLAY_HOST_TAG = 'eel-zap-overlay'\n\nconst DEFAULT_THEME: Required<OverlayTheme> = {\n outline: '#CF8700',\n chip: '#9A6200',\n chipText: '#FFFFFF',\n}\n\nconst SELECTED_TINT = 'rgba(207,135,0,0.10)'\n\nconst CHECK_SVG =\n '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.4\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M20 6 9 17l-5-5\"/></svg>'\n\nexport interface OverlayCallbacks {\n /** Inspect mode: a tagged element was clicked. */\n onInspect?(tagged: TaggedElement): void\n /** Select mode: the selection changed (≤ 20 elements). */\n onSelect?(elements: Element[]): void\n /** Spot mode: the page was clicked at this point of the document. */\n onSpot?(anchor: SpotAnchor): void\n /** A pin was clicked. */\n onPin?(pin: { id: string }): void\n}\n\n/** A pin to draw: at a point of the document, or at an element's top-left corner. */\nexport interface OverlayPin {\n id: string\n n: number\n spot?: SpotPoint\n element?: Element\n}\n\nexport interface OverlayOptions extends OverlayCallbacks {\n doc: Document\n win: Window\n index: TagIndex\n /** Events on these targets are not the overlay's (the suggestion UI's own host). */\n ignore?(target: EventTarget): boolean\n /** Select mode: every click adds or removes, Shift or not (the site toolbar's Seleccionar). */\n additive?(): boolean\n /** Draw the pins in `off` mode too, intercepting nothing else (the live site's «Navegar»). */\n pinsWhenOff?: boolean\n /**\n * Select mode: a plain click on `<html>`/`<body>` or on an element taking\n * at least half the viewport is a spot (`onSpot`), not a selection. Zap's\n * preview has ONE comment toggle (board Main, «Comentar sobre la página»)\n * and this is how it reaches both anchors.\n */\n spotOnLarge?: boolean\n}\n\ntype BoxKind = 'hover' | 'focus' | 'selected' | 'highlight'\n\ninterface Box {\n element: Element\n kind: BoxKind\n label: string | null\n /** A field of another record: «en Configuración», drawn muted after the label. */\n suffix?: string\n}\n\nexport class Overlay {\n private host: HTMLElement | null = null\n private layer: HTMLElement | null = null\n private mode: OverlayMode = 'off'\n private zoom = 1\n private theme: Required<OverlayTheme> = { ...DEFAULT_THEME }\n private labels: Record<string, string> = {}\n private foreign: Record<string, [string, string]> = {}\n private hover: Element | null = null\n private focused: Element[] = []\n private highlighted: Element[] = []\n private selected: Element[] = []\n private pins: OverlayPin[] = []\n private pending: SpotPoint | null = null\n private capture: HTMLElement | null = null\n private frame: number | null = null\n private readonly listeners: Array<[EventTarget, string, EventListener, boolean]> = []\n\n constructor(private readonly options: OverlayOptions) {}\n\n mount(): void {\n if (this.host) return\n const { doc } = this.options\n const host = doc.createElement(OVERLAY_HOST_TAG)\n // Inline !important so a site rule like `* { display: none }` cannot hide it.\n const hostStyle: Record<string, string> = {\n all: 'initial',\n position: 'fixed',\n inset: '0',\n 'pointer-events': 'none',\n 'z-index': '2147483647',\n display: 'block',\n }\n for (const [name, value] of Object.entries(hostStyle)) {\n host.style.setProperty(name, value, 'important')\n }\n const shadow = host.attachShadow({ mode: 'closed' })\n const style = doc.createElement('style')\n style.textContent = STYLES\n const layer = doc.createElement('div')\n layer.className = 'layer'\n // Spot mode's capture layer, under the boxes and pins; shown in spot mode only.\n const capture = doc.createElement('div')\n capture.className = 'capture'\n capture.onclick = (event) => {\n this.consume(event)\n this.spotAt(event)\n }\n shadow.append(style, capture, layer)\n this.capture = capture\n doc.documentElement.appendChild(host)\n this.host = host\n this.layer = layer\n this.install()\n this.render()\n }\n\n /**\n * Register the event listeners. Called by `mount()`, and EARLIER by the\n * runtime — at script start, before any site script has run — so our\n * capture listeners on `window` are first in line, ahead of anything the\n * site registers.\n */\n install(): void {\n if (this.listeners.length > 0) return\n const { win } = this.options\n this.listen(win, 'mouseover', (event) => this.onPointer(event as MouseEvent))\n // Leaving the document (relatedTarget null) clears the hover outline.\n this.listen(win, 'mouseout', (event) => {\n if (!(event as MouseEvent).relatedTarget) this.setHover(null)\n })\n this.listen(win, 'click', (event) => this.onClick(event as MouseEvent))\n // While the overlay is on, the page never navigates away (Zap's preview\n // frame; the live site's Editar and Comentar): no form is submitted.\n this.listen(win, 'submit', (event) => this.mode !== 'off' && this.consume(event))\n this.listen(win, 'keydown', (event) => this.onKey(event as KeyboardEvent))\n this.listen(win, 'scroll', () => this.schedule())\n this.listen(win, 'resize', () => this.schedule())\n }\n\n destroy(): void {\n for (const [target, type, listener, capture] of this.listeners) {\n target.removeEventListener(type, listener, capture)\n }\n this.listeners.length = 0\n this.host?.remove()\n this.host = null\n this.layer = null\n this.capture = null\n }\n\n /** The host element, so the tag observer can ignore our own mutations. */\n get hostElement(): Element | null {\n return this.host\n }\n\n getMode(): OverlayMode {\n return this.mode\n }\n\n setMode(mode: OverlayMode): void {\n if (mode === this.mode) return\n const hadSelection = this.selected.length > 0\n this.mode = mode\n this.hover = null\n this.pending = null\n if (mode !== 'select' && hadSelection) {\n this.selected = []\n this.options.onSelect?.([])\n }\n if (mode === 'off') {\n this.focused = []\n this.highlighted = []\n }\n this.schedule()\n }\n\n setZoom(zoom: number): void {\n if (!(zoom > 0)) return\n this.zoom = zoom\n this.schedule()\n }\n\n setTheme(theme: OverlayTheme | undefined): void {\n this.theme = { ...DEFAULT_THEME, ...(theme ?? {}) }\n this.schedule()\n }\n\n /** Labels by field key; `foreign`, by full tag, the fields of other records (`HelloPayload.foreign`). */\n setLabels(labels: Record<string, string>, foreign: Record<string, [string, string]> = {}): void {\n this.labels = { ...labels }\n this.foreign = { ...foreign }\n this.schedule()\n }\n\n /** Outline every element of a field and scroll the first into view. */\n focusField(elements: Element[]): number {\n this.focused = elements.slice()\n const first = elements[0]\n if (first) reveal(this.options.win, first)\n this.schedule()\n return elements.length\n }\n\n /** Outline change-request anchors and scroll to the first. */\n highlight(elements: Element[]): void {\n this.highlighted = elements.slice(0, MAX_ANCHORS)\n const first = this.highlighted[0]\n if (first) reveal(this.options.win, first)\n this.schedule()\n }\n\n /** Draw these pins (≤ 50); drops the pending spot pin. */\n setPins(pins: OverlayPin[]): void {\n this.pins = pins.slice(0, MAX_PINS)\n this.clearPending()\n }\n\n /** Drop the pending spot pin. */\n clearPending(): void {\n this.pending = null\n this.schedule()\n }\n\n clearSelection(): void {\n if (this.selected.length === 0) return\n this.selected = []\n this.options.onSelect?.([])\n this.schedule()\n }\n\n get selection(): readonly Element[] {\n return this.selected\n }\n\n /** For specs: what the closed shadow root currently draws. */\n inspect(): {\n boxes: Array<{ kind: BoxKind; label: string | null }>\n pins: HTMLElement[]\n capture: HTMLElement | null\n zoom: number\n } {\n const nodes = Array.from(this.layer?.children ?? []) as HTMLElement[]\n const boxes = nodes\n .filter((node) => node.className === 'box')\n .map((node) => ({\n kind: node.dataset.kind as BoxKind,\n label: node.querySelector('.chip')?.textContent ?? null,\n }))\n const capture = this.capture?.style.display === 'block' ? this.capture : null\n return { boxes, pins: nodes.filter((n) => n.className === 'pin'), capture, zoom: this.zoom }\n }\n\n /** For specs: the style of the n-th box. */\n boxStyle(n: number): CSSStyleDeclaration | null {\n const node = this.layer?.children[n] as HTMLElement | undefined\n return node?.style ?? null\n }\n\n // ── Events ────────────────────────────────────────────────────────────────\n\n private listen(target: EventTarget, type: string, listener: EventListener): void {\n // Capture on window: we see the event before any site listener, so a\n // site's own click handler (a router, a lightbox) never runs for a click\n // that the overlay consumed.\n target.addEventListener(type, listener, true)\n this.listeners.push([target, type, listener, true])\n }\n\n private isOwn(node: EventTarget | null): boolean {\n return (!!this.host && node === this.host) || (!!node && !!this.options.ignore?.(node))\n }\n\n private targetFor(event: Event): Element | null {\n const target = event.target as Element | null\n if (!target || this.isOwn(target) || typeof target.closest !== 'function') return null\n if (this.mode === 'inspect') return this.options.index.closest(target)?.element ?? null\n if (this.mode === 'select') {\n const tagged = this.options.index.closest(target)\n const element = tagged?.element ?? target\n const name = element.localName\n return name === 'html' || name === 'body' ? null : element\n }\n return null\n }\n\n private onPointer(event: MouseEvent): void {\n if (this.mode === 'off') return\n this.setHover(this.targetFor(event))\n }\n\n private setHover(element: Element | null): void {\n if (element === this.hover) return\n this.hover = element\n this.schedule()\n }\n\n private onClick(event: MouseEvent): void {\n if (this.mode === 'off' || this.isOwn(event.target)) return\n // Nor does a link navigate; a tagged one still selects its field below.\n if ((event.target as Element).closest?.('a[href],area[href]')) this.consume(event)\n if (this.mode === 'spot') {\n // The capture layer normally takes the click; anything that still lands\n // on the site (a synthetic click, a gap) is consumed the same way.\n this.consume(event)\n return this.spotAt(event)\n }\n const element = this.targetFor(event)\n if (this.mode === 'inspect') {\n if (!element) return\n const tagged = this.options.index.get(element)\n if (!tagged) return\n event.preventDefault()\n event.stopImmediatePropagation()\n this.options.onInspect?.(tagged)\n return\n }\n // Select mode consumes every click, so nothing on the page reacts.\n this.consume(event)\n const additive = event.shiftKey || event.metaKey || event.ctrlKey || !!this.options.additive?.()\n if (this.options.spotOnLarge && !additive && (!element || this.isLarge(element))) {\n // A wrapper, not the thing the person pointed at: comment on the point.\n this.clearSelection()\n return this.spotAt(event)\n }\n if (!element) return\n if (this.pending) this.clearPending()\n let next: Element[]\n if (additive) {\n next = this.selected.includes(element)\n ? this.selected.filter((e) => e !== element)\n : this.selected.length < MAX_ANCHORS\n ? [...this.selected, element]\n : this.selected\n } else {\n next = [element]\n }\n if (next === this.selected) return\n this.selected = next\n this.options.onSelect?.(next.slice())\n this.schedule()\n }\n\n /** Most of the viewport: a wrapper, not the thing the person pointed at. */\n private isLarge(element: Element): boolean {\n const { win } = this.options\n const rect = element.getBoundingClientRect()\n const area = Math.max(1, win.innerWidth * win.innerHeight)\n return (rect.width * rect.height) / area >= LARGE_SHARE\n }\n\n private consume(event: Event): void {\n event.preventDefault()\n event.stopImmediatePropagation()\n }\n\n /** The click as a point of the whole document (§3.3 spot anchor). */\n private spotAt(event: MouseEvent): void {\n const { doc, win } = this.options\n const root = doc.documentElement\n const w = Math.max(root.scrollWidth, 1)\n const h = Math.max(root.scrollHeight, 1)\n const at = (v: number, size: number) => Math.min(1, Math.max(0, v / size))\n const spot = { x: at(event.clientX + win.scrollX, w), y: at(event.clientY + win.scrollY, h) }\n this.pending = spot\n this.schedule()\n this.options.onSpot?.({ spot, viewport: { w, h } })\n }\n\n private onKey(event: KeyboardEvent): void {\n if (event.key !== 'Escape') return\n if ((this.mode === 'spot' || this.mode === 'select') && this.pending) {\n this.consume(event)\n return this.clearPending()\n }\n if (this.mode !== 'select' || this.selected.length === 0) return\n this.consume(event)\n this.clearSelection()\n }\n\n // ── Drawing ───────────────────────────────────────────────────────────────\n\n private schedule(): void {\n if (!this.layer || this.frame !== null) return\n const win = this.options.win\n const raf =\n typeof win.requestAnimationFrame === 'function'\n ? (fn: () => void) => win.requestAnimationFrame(fn)\n : (fn: () => void) => win.setTimeout(fn, 16)\n this.frame = raf(() => {\n this.frame = null\n this.render()\n }) as number\n }\n\n /** Draw now (also used by specs, which have no animation frames). */\n render(): void {\n const layer = this.layer\n if (!layer) return\n const boxes: Box[] = []\n const seen = new Set<Element>()\n const add = (element: Element, kind: BoxKind) => {\n if (seen.has(element) || !element.isConnected) return\n seen.add(element)\n const tagged = this.options.index.get(element)\n const [label, suffix] = tagged ? this.labelFor(tagged) : [null]\n boxes.push({ element, kind, label, suffix })\n }\n if (this.mode !== 'off') {\n for (const element of this.selected) add(element, 'selected')\n for (const element of this.focused) add(element, 'focus')\n for (const element of this.highlighted) add(element, 'highlight')\n if (this.hover) add(this.hover, 'hover')\n }\n const nodes = boxes.map((box) => this.drawBox(box))\n if (this.capture) this.capture.style.display = this.mode === 'spot' ? 'block' : 'none'\n if (this.mode !== 'off' || this.options.pinsWhenOff) {\n const { doc, win } = this.options\n const root = doc.documentElement\n const toView = (p: SpotPoint): [number, number] => [\n p.x * root.scrollWidth - win.scrollX,\n p.y * root.scrollHeight - win.scrollY,\n ]\n for (const pin of this.pins) {\n const element = pin.element\n if (element) {\n if (!element.isConnected) continue\n const rect = element.getBoundingClientRect()\n nodes.push(this.drawPin([rect.left, rect.top], pin))\n } else if (pin.spot) nodes.push(this.drawPin(toView(pin.spot), pin))\n }\n if (this.pending && (this.mode === 'spot' || this.mode === 'select')) {\n nodes.push(this.drawPin(toView(this.pending)))\n }\n }\n layer.replaceChildren(...nodes)\n // Chips stay inside the viewport: measured once drawn, shifted in.\n const inset = EDGE_PX / this.zoom\n for (const chip of layer.querySelectorAll<HTMLElement>('.chip')) {\n const { left, right } = chip.getBoundingClientRect()\n const dx = Math.min(Math.max(0, inset - left), this.options.win.innerWidth - inset - right)\n chip.style.transform = `translateX(${dx}px)`\n }\n }\n\n /** A gold teardrop whose point (bottom-left) sits on `[x, y]`; without a pin, the pending one. */\n private drawPin([x, y]: [number, number], pin?: OverlayPin): HTMLElement {\n const node = this.options.doc.createElement('span')\n node.className = 'pin'\n const z = this.zoom\n const size = PIN_PX / z\n const vw = this.options.win.innerWidth\n // No room above a point on screen: the pin hangs below it, its tip up;\n // near the right edge it shifts in.\n const below = y >= 0 && y - size < EDGE_PX / z\n if (below) node.style.borderRadius = '0 999px 999px'\n Object.assign(node.style, {\n left: `${x <= vw ? Math.min(x, vw - size - EDGE_PX / z) : x}px`,\n top: `${below ? y : y - size}px`,\n width: `${size}px`,\n height: `${size}px`,\n fontSize: `${11 / z}px`,\n lineHeight: `${size}px`,\n boxShadow: `0 0 0 ${2 / z}px #fff,0 ${2 / z}px ${6 / z}px rgba(0,0,0,.25)`,\n })\n if (pin) {\n // The number as text, never markup; the id never reaches the DOM.\n node.textContent = String(pin.n)\n node.onclick = (event) => {\n this.consume(event)\n this.options.onPin?.({ id: pin.id })\n }\n } else node.dataset.pending = ''\n return node\n }\n\n private labelFor(tagged: TaggedElement): [string, string?] {\n const [label, suffix] = this.foreign[tagged.tag] ?? [this.labels[tagged.fieldKey]]\n return [typeof label === 'string' && label.trim() ? label : tagged.fieldKey, suffix]\n }\n\n private drawBox(box: Box): HTMLElement {\n const doc = this.options.doc\n const z = this.zoom\n const px = (n: number) => `${Math.round((n / z) * 100) / 100}px`\n const rect = box.element.getBoundingClientRect()\n const node = doc.createElement('div')\n node.className = 'box'\n node.dataset.kind = box.kind\n const offset = 3 / z\n Object.assign(node.style, {\n left: `${rect.left - offset}px`,\n top: `${rect.top - offset}px`,\n width: `${rect.width + offset * 2}px`,\n height: `${rect.height + offset * 2}px`,\n outline: `${px(2)} solid ${this.theme.outline}`,\n borderRadius: px(3),\n background: box.kind === 'selected' ? SELECTED_TINT : 'transparent',\n })\n // A chip names the field on hover, on selection and where the editor\n // located it (board CampoLocalizar: the located «Título» carries its\n // chip); a highlight outline is a thread pointing at its anchor. A\n // selected element smaller on screen than the chip itself (at low zoom,\n // most lines of text) keeps its outline and tint but shows its chip only\n // while hovered: a row of such chips buried the very elements being\n // picked (acceptance F6). The hovered one always names itself.\n const tooSmall =\n box.kind === 'selected' &&\n box.element !== this.hover &&\n Math.min(rect.width, rect.height) * z < CHIP_HEIGHT_PX\n if (box.label && box.kind !== 'highlight' && !tooSmall) {\n const chip = doc.createElement('span')\n chip.className = 'chip'\n // No room above the element: the chip goes below it (flip); the\n // horizontal shift waits until it is drawn and measurable (render).\n const lift = 27 / z\n const above =\n rect.top - offset - lift >= EDGE_PX / z || rect.bottom + lift > this.options.win.innerHeight\n Object.assign(chip.style, {\n right: px(-5),\n [above ? 'top' : 'bottom']: px(-27),\n height: px(CHIP_HEIGHT_PX),\n padding: `0 ${px(7)}`,\n gap: px(4),\n borderRadius: px(6),\n fontSize: px(12),\n background: this.theme.chip,\n color: this.theme.chipText,\n })\n if (box.kind === 'selected') {\n const icon = doc.createElement('span')\n icon.className = 'icon'\n icon.style.width = px(12)\n icon.style.height = px(12)\n // A constant SVG string of ours, never network data (§7.3).\n icon.innerHTML = CHECK_SVG\n chip.appendChild(icon)\n }\n // Label text is set with textContent: labels come from the editor and\n // are never parsed as markup.\n chip.appendChild(doc.createTextNode(box.label))\n if (box.suffix) {\n const muted = doc.createElement('span')\n muted.className = 'of'\n muted.textContent = box.suffix\n chip.appendChild(muted)\n }\n node.appendChild(chip)\n }\n return node\n }\n}\n\n/** `spotOnLarge`: an element taking this share of the viewport is commented as a point (site Comentar's rule). */\nconst LARGE_SHARE = 0.5\n\n/**\n * Scroll THIS window only, centring `element`; nothing when it is fully in\n * view. Never `scrollIntoView`, which also scrolls every scrollable ancestor\n * across the frame: Zap's editor around its preview frame.\n */\nexport function reveal(win: Window, element: Element): void {\n const { top, bottom, height } = element.getBoundingClientRect()\n const h = win.innerHeight\n if (top < 0 || bottom > h)\n win.scrollTo?.({ top: win.scrollY + top - Math.max(0, (h - height) / 2), behavior: 'smooth' })\n}\n\n/** The chip's on-screen height; a selected element smaller than this shows its chip on hover only. */\nconst CHIP_HEIGHT_PX = 20\n/** How far chips and pins keep from the viewport's edges, on screen. */\nconst EDGE_PX = 4\n/** A pin's on-screen size (SeleccionarElementos board, «Comentar»). */\nconst PIN_PX = 22\n\nconst STYLES = `\n.layer { position: fixed; inset: 0; pointer-events: none; }\n.box { position: fixed; box-sizing: border-box; pointer-events: none; }\n.chip {\n position: absolute; display: inline-flex; align-items: center; white-space: nowrap;\n font-family: Poppins, ui-sans-serif, system-ui, sans-serif; font-weight: 600; line-height: 1;\n letter-spacing: 0; text-transform: none; font-style: normal; box-sizing: border-box;\n}\n.of { font-weight: 400; opacity: .72; }\n.capture { position: fixed; inset: 0; pointer-events: auto; cursor: crosshair; display: none; }\n.pin {\n position: fixed; box-sizing: border-box; pointer-events: auto; cursor: pointer; text-align: center;\n border-radius: 999px 999px 999px 0; background: #CF8700; color: #fff;\n font-family: Poppins, ui-sans-serif, system-ui, sans-serif; font-weight: 700;\n}\n.icon { display: inline-flex; flex-shrink: 0; }\n.icon svg { width: 100%; height: 100%; display: block; }\n`\n","import { hasStega, stegaDecode } from '../stega'\nimport {\n isLocale,\n parseRecordRef,\n parseTag,\n type PausedPayload,\n type RecordRef,\n type TagSource,\n type TagSummary,\n} from './protocol'\n\n/**\n * The tag grammar and the tag index (`.docs/proposals/zap-cms-v2.md` §2.4).\n *\n * ```html\n * <h1 data-zap=\"blog-posts/mi-primer-post#title\">…</h1>\n * <p data-zap=\"doc:home#hero_subtitle\">…</p>\n * <article data-zap-entry=\"blog-posts/mi-primer-post\">\n * <h1 data-zap-field=\"title\">…</h1>\n * <div data-zap-field=\"body\" data-zap-html>…</div>\n * </article>\n * ```\n *\n * - `collectionKey/slug` for entries (default-locale or localized slug; the\n * EDITOR resolves which record that is — the page only reports strings),\n * `doc:documentKey` for documents, `#fieldKey` for the field. Keys, slugs\n * and field keys follow Zap's own patterns (lowercase, hyphens; field keys\n * `[a-z][a-z0-9_]*`), so a tag that could never match a record is reported\n * as invalid instead of silently never lighting up.\n * - `data-zap` wins over `data-zap-field` on the same element.\n * - `data-zap-field` takes its record from the nearest `data-zap-entry`\n * ancestor (or the element itself).\n * - `data-zap-locale` on the element or any ancestor pins the locale; a value\n * for another locale is not substituted there.\n * - `data-zap-html` opts the element into sanitised innerHTML substitution.\n *\n * ## Stega (§2.4)\n *\n * Text fields read in preview carry an invisible marker (`../stega.ts`). The\n * scan also walks the page's text nodes, decodes every marker and indexes the\n * text node's parent element under that field, with `source: 'stega'`. A\n * manual tag on the same element wins: the element keeps its attribute tag\n * and the marker is ignored for it. Text in `<head>`, `<script>`, `<style>`,\n * `<template>` and `<noscript>` is never indexed.\n */\n\nexport { parseRecordRef, parseTag }\n\nexport const TAG_SELECTOR = '[data-zap],[data-zap-field]'\n\nexport interface TaggedElement {\n element: Element\n recordRef: RecordRef\n fieldKey: string\n /** Canonical `recordRef#fieldKey`. */\n tag: string\n locale: string | null\n html: boolean\n /** `attr` for a manual tag, `stega` for a marker found in a text node. */\n source: TagSource\n /** Stega only: the text node that carries the marker (values are written to it). */\n textNode?: Text\n}\n\nexport interface TagProblem {\n attribute: 'data-zap' | 'data-zap-field' | 'data-zap-entry'\n value: string\n}\n\nexport type ReadTagResult =\n | { ok: true; tagged: TaggedElement }\n | { ok: false; problem: TagProblem }\n | null\n\n/** Read one element's tag. `null` when it carries no tag attribute. */\nexport function readElementTag(element: Element): ReadTagResult {\n const locale = readLocale(element)\n const html = element.hasAttribute('data-zap-html')\n\n const direct = element.getAttribute('data-zap')\n if (direct !== null) {\n const parsed = parseTag(direct)\n if (!parsed) return { ok: false, problem: { attribute: 'data-zap', value: clip(direct) } }\n return { ok: true, tagged: build(element, parsed.recordRef, parsed.fieldKey, locale, html) }\n }\n\n const field = element.getAttribute('data-zap-field')\n if (field === null) return null\n const fieldKey = field.trim()\n if (!parseTag(`doc:x#${fieldKey}`)) {\n return { ok: false, problem: { attribute: 'data-zap-field', value: clip(field) } }\n }\n const container = element.closest('[data-zap-entry]')\n const entry = container?.getAttribute('data-zap-entry')?.trim()\n if (!entry || !parseRecordRef(entry)) {\n return { ok: false, problem: { attribute: 'data-zap-entry', value: clip(entry ?? '') } }\n }\n return { ok: true, tagged: build(element, entry, fieldKey, locale, html) }\n}\n\nfunction build(\n element: Element,\n recordRef: string,\n fieldKey: string,\n locale: string | null,\n html: boolean,\n): TaggedElement {\n return {\n element,\n recordRef,\n fieldKey,\n tag: `${recordRef}#${fieldKey}`,\n locale,\n html,\n source: 'attr',\n }\n}\n\n/** Elements whose text is never a rendered field. */\nconst NON_RENDERED = /^(?:head|title|script|style|template|noscript|textarea)$/\n\nfunction readLocale(element: Element): string | null {\n const value = element.closest('[data-zap-locale]')?.getAttribute('data-zap-locale')?.trim()\n return value && isLocale(value) ? value : null\n}\n\nfunction clip(value: string): string {\n return value.length > 200 ? value.slice(0, 200) : value\n}\n\n/**\n * Every tagged element in a document, in document order, with lookups by\n * field and by element. Rebuilt wholesale by `scan()`: a full scan of a large\n * page is one `querySelectorAll`, cheaper and far simpler than patching the\n * index per mutation record.\n */\nexport class TagIndex {\n private list: TaggedElement[] = []\n private byElement = new WeakMap<Element, TaggedElement>()\n private byTag = new Map<string, TaggedElement[]>()\n problems: TagProblem[] = []\n\n constructor(private readonly root: ParentNode) {}\n\n scan(): void {\n const list: TaggedElement[] = []\n const problems: TagProblem[] = []\n const byElement = new WeakMap<Element, TaggedElement>()\n const byTag = new Map<string, TaggedElement[]>()\n const add = (element: Element, tagged: TaggedElement) => {\n list.push(tagged)\n if (!byElement.has(element)) byElement.set(element, tagged)\n const bucket = byTag.get(tagged.tag)\n if (bucket) bucket.push(tagged)\n else byTag.set(tagged.tag, [tagged])\n }\n for (const element of Array.from(this.root.querySelectorAll(TAG_SELECTOR))) {\n const result = readElementTag(element)\n if (!result) continue\n if (!result.ok) {\n problems.push(result.problem)\n continue\n }\n add(element, result.tagged)\n }\n const doc = (this.root as Node).ownerDocument ?? (this.root as Document)\n const walker = doc.createTreeWalker(this.root as Node, 4 /* NodeFilter.SHOW_TEXT */)\n for (let node: Node | null; (node = walker.nextNode()); ) {\n const text = node as Text\n const element = text.parentElement\n if (!element || !hasStega(text.data)) continue\n // Never a rendered field; and a manual tag on the element wins over its markers.\n if (NON_RENDERED.test(element.localName) || element.closest('head')) continue\n if (byElement.get(element)?.source === 'attr') continue\n for (const found of stegaDecode(text.data)) {\n add(element, {\n element,\n recordRef: found.recordRef,\n fieldKey: found.fieldKey,\n tag: found.tag,\n locale: found.locale ?? readLocale(element),\n html: false,\n source: 'stega',\n textNode: text,\n })\n }\n }\n // Document order across both kinds (same element: insertion order).\n list.sort((a, b) =>\n a.element.compareDocumentPosition(b.element) & 4 /* FOLLOWING */\n ? -1\n : a.element === b.element\n ? 0\n : 1,\n )\n this.list = list\n this.byElement = byElement\n this.byTag = byTag\n this.problems = problems\n }\n\n get elements(): readonly TaggedElement[] {\n return this.list\n }\n\n byField(recordRef: RecordRef, fieldKey: string): TaggedElement[] {\n return this.byTag.get(`${recordRef}#${fieldKey}`) ?? []\n }\n\n get(element: Element): TaggedElement | null {\n return this.byElement.get(element) ?? null\n }\n\n /** The nearest tagged element at or above `target`. */\n closest(target: Element | null): TaggedElement | null {\n for (let node: Element | null = target; node; node = node.parentElement) {\n const tagged = this.byElement.get(node)\n if (tagged) return tagged\n }\n return null\n }\n\n /** The `zap:tags` report: one row per tag with its element count, sorted. */\n summary(): TagSummary[] {\n return Array.from(this.byTag.entries())\n .map(([tag, items]) => ({\n tag,\n recordRef: items[0]!.recordRef,\n fieldKey: items[0]!.fieldKey,\n count: items.length,\n source: items.some((item) => item.source === 'attr')\n ? ('attr' as const)\n : ('stega' as const),\n }))\n .sort((a, b) => (a.tag < b.tag ? -1 : a.tag > b.tag ? 1 : 0))\n }\n}\n\n/** Tagged elements past which the preview stops following the page. */\nexport const MAX_TAGGED = 5000\n/** Passes in a row the tagged count may grow with nothing on the page explaining it. */\nexport const RUNAWAY_PASSES = 3\n\n/** Why the guard stopped following the page (`zap:paused`). */\nexport type PauseReason = PausedPayload['reason']\n\n/**\n * The runaway guard (defence in depth for the 2026-10 crash, where values\n * carrying stega markers made every pass add markers, and the tagged count\n * doubled until the tab ran out of memory). Call the returned check once per\n * pass, right after `scan()`, with the tagged count and `observeTags`'s\n * `page` flag. It answers why to stop following the page, or null:\n *\n * - `cap`: more than `MAX_TAGGED` tagged elements;\n * - `runaway`: the count grew on `RUNAWAY_PASSES` passes in a row that no\n * page mutation explains (a values patch's own writes are made outside a\n * pass, so they count as the page's).\n *\n * Once it answers, the caller stops observing and stops writing.\n */\nexport function tagGuard(): (count: number, page: boolean) => PauseReason | null {\n let last = 0\n let grew = 0\n return (count, page) => {\n grew = count > last && !page ? grew + 1 : 0\n last = count\n return count > MAX_TAGGED ? 'cap' : grew >= RUNAWAY_PASSES ? 'runaway' : null\n }\n}\n\nexport interface ObserveOptions {\n /** Minimum gap between two callbacks. Default 250 ms. */\n throttleMs?: number\n /** Mutations entirely inside these nodes are ignored (the overlay's host). */\n ignore?: (node: Node) => boolean\n setTimeout?: (fn: () => void, ms: number) => unknown\n clearTimeout?: (handle: unknown) => void\n}\n\n/**\n * Call `onChange` after DOM mutations, throttled (leading call deferred,\n * trailing call guaranteed): a hydrating site or an infinite list can fire\n * thousands of records a second, and the index needs one rescan per burst.\n * Mutations made by the overlay's own host are ignored, so drawing outlines\n * never triggers a rescan.\n *\n * `page` says whether the page changed since the last call. Mutations made\n * DURING `onChange` (our own writes: `reapply()`) still lead to one more\n * call, so they settle as before, but with `page` false: the runaway guard\n * (`tagGuard`) tells a site that keeps rendering from a pass feeding itself.\n */\nexport function observeTags(\n target: Node,\n onChange: (page: boolean) => void,\n options: ObserveOptions = {},\n): () => void {\n const throttleMs = options.throttleMs ?? 250\n const set = options.setTimeout ?? ((fn, ms) => setTimeout(fn, ms))\n const clear = options.clearTimeout ?? ((h) => clearTimeout(h as ReturnType<typeof setTimeout>))\n const view = (target.ownerDocument ?? (target as Document)).defaultView\n const Observer = view?.MutationObserver ?? globalThis.MutationObserver\n if (!Observer) return () => {}\n\n let timer: unknown = null\n let last = -Infinity\n let page = false\n const queue = (records: MutationRecord[], fromPage: boolean) => {\n if (options.ignore && records.every((record) => options.ignore!(record.target))) return\n page ||= fromPage\n if (timer !== null) return\n const wait = Math.max(0, last + throttleMs - Date.now())\n timer = set(fire, Math.max(wait, 16))\n }\n const fire = () => {\n timer = null\n last = Date.now()\n const changed = page\n page = false\n onChange(changed)\n // What onChange wrote is ours, not the page's.\n const own = observer.takeRecords()\n if (own.length) queue(own, false)\n }\n const observer = new Observer((records) => queue(records, true))\n observer.observe(target, {\n subtree: true,\n childList: true,\n attributes: true,\n attributeFilter: ['data-zap', 'data-zap-field', 'data-zap-entry', 'data-zap-locale'],\n characterData: true,\n })\n return () => {\n observer.disconnect()\n if (timer !== null) clear(timer)\n }\n}\n","/**\n * The keyboard shortcut that opens «Editar» and «Comentar» on the live site\n * (zap-cms-v2 §3.4, owner 2026-10-05): Shift plus one letter, `Z` by default.\n * It replaced «Shift twice», which fired while people typed capitals.\n *\n * Shared by the boot every visitor runs (`src/boot/boot.ts`, under 1 KB) and\n * the overlay core (`./boot.ts`), so the two can never disagree about what\n * counts. No directive, no imports.\n *\n * It never fires while the person is typing: focus in an input, a textarea, a\n * select or anything editable, or a key that is part of an IME composition\n * (`isComposing`, or the legacy `keyCode` 229 some browsers send instead).\n */\n\n/** The letter pressed with Shift when a site sets none. */\nexport const DEFAULT_SHORTCUT = 'Z'\n\n/**\n * Where typing happens: the target itself or anything it sits in. A\n * `contenteditable=\"false\"` region is not typing, so the shortcut still works\n * there; a field being edited on the live site is, so Shift+Z types a capital.\n */\nconst TYPING = 'input,textarea,select,[contenteditable]:not([contenteditable=\"false\"])'\n\nexport function isShortcut(event: KeyboardEvent, key: string): boolean {\n // The real target, inside an open shadow root too (a site's own components).\n const target = (event.composedPath?.()[0] ?? event.target) as Element | null\n return (\n event.shiftKey &&\n !(event.ctrlKey || event.metaKey || event.altKey || event.repeat || event.isComposing) &&\n // Some browsers report a key inside an IME composition only as keyCode 229.\n event.keyCode !== 229 &&\n event.key?.toLowerCase() === key.toLowerCase() &&\n !target?.closest?.(TYPING)\n )\n}\n"],"mappings":"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","names":["segment","candidate"]}