@docentjs/dom 0.4.0 → 0.5.1

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connector-D6Al4Deu.cjs","names":[],"sources":["../src/connector.ts"],"sourcesContent":["/**\n * Drawn connectors between the popover and its target: the arrow styles other\n * than the default caret. Geometry is pure (tested without a browser); the\n * Connector class turns it into SVG inside the shadow root.\n */\n\nexport interface Point {\n x: number\n y: number\n}\n\nexport interface ConnectorPath {\n d: string\n /** SVG stroke-dasharray. Dashed paths fade in instead of drawing. */\n dash?: string\n width?: number\n opacity?: number\n}\n\nexport interface ConnectorShape {\n paths: ConnectorPath[]\n /** Arrowhead (open chevron) at the target end. */\n head?: string\n /** Filled dot at the target end (`pin`). */\n dot?: Point\n}\n\nimport type { ConnectorStyle } from './arrows'\n\nexport { arrowGap, CONNECTOR_STYLES, type ConnectorStyle, isConnector } from './arrows'\n\nconst r = (n: number) => Math.round(n * 10) / 10\nconst pt = (p: Point) => `${r(p.x)} ${r(p.y)}`\n\nfunction frame(a: Point, b: Point) {\n const dx = b.x - a.x\n const dy = b.y - a.y\n const length = Math.hypot(dx, dy) || 1\n const u = { x: dx / length, y: dy / length }\n const n = { x: -u.y, y: u.x }\n const at = (along: number, across: number): Point => ({\n x: a.x + u.x * along + n.x * across,\n y: a.y + u.y * along + n.y * across,\n })\n return { length, u, n, at }\n}\n\nfunction polyline(points: Point[]): string {\n return points.map((p, i) => `${i ? 'L' : 'M'}${pt(p)}`).join('')\n}\n\n/** Open chevron at `tip`, pointing along `dir` (unit vector). */\nexport function arrowHead(tip: Point, dir: Point, size = 7): string {\n const angle = 0.5\n const back = (s: number) => ({\n x: tip.x - size * (dir.x * Math.cos(angle) - s * dir.y * Math.sin(angle)),\n y: tip.y - size * (dir.y * Math.cos(angle) + s * dir.x * Math.sin(angle)),\n })\n return `M${pt(back(1))}L${pt(tip)}L${pt(back(-1))}`\n}\n\nfunction unit(from: Point, to: Point): Point {\n const l = Math.hypot(to.x - from.x, to.y - from.y) || 1\n return { x: (to.x - from.x) / l, y: (to.y - from.y) / l }\n}\n\n/** Polyline with rounded interior corners. */\nfunction rounded(points: Point[], radius: number): string {\n let d = `M${pt(points[0] as Point)}`\n for (let i = 1; i < points.length - 1; i++) {\n const p = points[i] as Point\n const prev = points[i - 1] as Point\n const next = points[i + 1] as Point\n const inLen = Math.hypot(p.x - prev.x, p.y - prev.y)\n const outLen = Math.hypot(next.x - p.x, next.y - p.y)\n const c = Math.min(radius, inLen / 2, outLen / 2)\n const a = unit(p, prev)\n const b = unit(p, next)\n d += `L${pt({ x: p.x + a.x * c, y: p.y + a.y * c })}Q${pt(p)} ${pt({ x: p.x + b.x * c, y: p.y + b.y * c })}`\n }\n return `${d}L${pt(points[points.length - 1] as Point)}`\n}\n\n/**\n * Paths for a connector from `from` (popover edge) to `to` (just outside the\n * target). `bend` flips curves to the other side (1 or -1).\n */\nexport function connectorShape(\n style: ConnectorStyle,\n from: Point,\n to: Point,\n bend = 1,\n): ConnectorShape {\n const f = frame(from, to)\n const L = f.length\n const straight = `M${pt(from)}L${pt(to)}`\n const head = (dir: Point) => arrowHead(to, dir)\n\n switch (style) {\n case 'line':\n return { paths: [{ d: straight }], head: head(f.u) }\n case 'dashed':\n return { paths: [{ d: straight, dash: '5 5' }], head: head(f.u) }\n case 'dotted':\n return { paths: [{ d: straight, dash: '0 6', width: 2.4 }], head: head(f.u) }\n case 'curve':\n case 'curve-dashed': {\n const c = f.at(L / 2, bend * L * 0.32)\n const path: ConnectorPath = { d: `M${pt(from)}Q${pt(c)} ${pt(to)}` }\n if (style === 'curve-dashed') path.dash = '5 5'\n return { paths: [path], head: head(unit(c, to)) }\n }\n case 'squiggle': {\n const steps = Math.max(24, Math.round(L / 2))\n const wave = 15\n const points: Point[] = []\n for (let i = 0; i <= steps; i++) {\n const t = i / steps\n const envelope = Math.min(1, t * 5, (1 - t) * 5)\n points.push(f.at(L * t, bend * 4.5 * envelope * Math.sin((2 * Math.PI * L * t) / wave)))\n }\n return {\n paths: [{ d: polyline(points) }],\n head: head(unit(points[points.length - 3] as Point, to)),\n }\n }\n case 'loop': {\n // A prolate cycloid: one small loop, then on to the target.\n const R = Math.min(22, Math.max(9, L / 5.5))\n const steps = 48\n const points: Point[] = []\n for (let i = 0; i <= steps; i++) {\n const t = i / steps\n points.push(\n f.at(\n L * t - R * Math.sin(2 * Math.PI * t),\n bend * 0.75 * R * (1 - Math.cos(2 * Math.PI * t)),\n ),\n )\n }\n return {\n paths: [{ d: polyline(points) }],\n head: head(unit(points[points.length - 3] as Point, to)),\n }\n }\n case 'elbow': {\n const dx = to.x - from.x\n const dy = to.y - from.y\n if (Math.abs(dx) < 6 || Math.abs(dy) < 6) return { paths: [{ d: straight }], head: head(f.u) }\n const vertical = Math.abs(dy) >= Math.abs(dx)\n const points = vertical\n ? [from, { x: from.x, y: from.y + dy / 2 }, { x: to.x, y: from.y + dy / 2 }, to]\n : [from, { x: from.x + dx / 2, y: from.y }, { x: from.x + dx / 2, y: to.y }, to]\n return { paths: [{ d: rounded(points, 10) }], head: head(unit(points[2] as Point, to)) }\n }\n case 'sketch': {\n // Two slightly different strokes read as hand-drawn; deterministic, so it never flickers.\n const c1 = f.at(L * 0.45, bend * L * 0.12)\n const c2 = f.at(L * 0.55, bend * L * 0.04 - 3)\n const start2 = f.at(2, 2)\n const end2 = { x: to.x + f.n.x * 2, y: to.y + f.n.y * 2 }\n return {\n paths: [\n { d: `M${pt(from)}Q${pt(c1)} ${pt(to)}` },\n { d: `M${pt(start2)}Q${pt(c2)} ${pt(end2)}`, opacity: 0.55, width: 1.2 },\n ],\n head: `${head(unit(c1, to))}${arrowHead(end2, unit(c2, end2), 6.5)}`,\n }\n }\n case 'pin': {\n const end = f.at(Math.max(0, L - 4), 0)\n return { paths: [{ d: `M${pt(from)}L${pt(end)}`, dash: '0 5', width: 2.2 }], dot: to }\n }\n }\n}\n\n/** Styles for the connector layer, injected into the shadow root when first used. */\nexport const CONNECTOR_STYLES_CSS = `\n/* Connectors: drawn arrows from the popover to the target. */\n.connector {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n overflow: visible;\n pointer-events: none;\n color: var(--docent-connector, oklch(98% 0.004 285 / 0.92));\n}\n:host([data-overlay=\"none\"]) .connector { color: var(--docent-connector, var(--docent-accent)); }\n.connector .stroke {\n fill: none;\n stroke: currentColor;\n stroke-width: 1.6;\n stroke-linecap: round;\n stroke-linejoin: round;\n}\n.connector .dot { fill: currentColor; }\n.connector .dot-halo { fill: currentColor; opacity: 0.2; }\n/* Drawn after the popover settles, so the line never trails it. */\n.connector.animate .draw {\n stroke-dasharray: 1;\n stroke-dashoffset: 1;\n animation: docent-draw calc(var(--docent-duration) * 1.6) var(--docent-easing) var(--docent-duration) forwards;\n}\n.connector.animate .fade,\n.connector.animate .head {\n opacity: 0;\n animation: docent-fade var(--docent-duration) ease-out calc(var(--docent-duration) * 1.6) forwards;\n}\n@keyframes docent-draw { to { stroke-dashoffset: 0; } }\n@keyframes docent-fade { to { opacity: 1; } }\n@media (prefers-reduced-motion: reduce) {\n .connector.animate .draw, .connector.animate .fade, .connector.animate .head { animation: none; stroke-dashoffset: 0; opacity: 1; }\n}\n`\n\nconst SVG_NS = 'http://www.w3.org/2000/svg'\n\n/** SVG layer in the shadow root that draws the current connector. */\nexport class Connector {\n readonly el: SVGSVGElement\n private readonly doc: Document\n\n constructor(doc: Document) {\n this.doc = doc\n this.el = doc.createElementNS(SVG_NS, 'svg')\n this.el.setAttribute('class', 'connector')\n this.el.setAttribute('part', 'connector')\n this.el.setAttribute('aria-hidden', 'true')\n }\n\n clear(): void {\n this.el.replaceChildren()\n }\n\n /** Draw a shape. `animate` plays the draw-in (after the popover settles). */\n render(shape: ConnectorShape, animate: boolean): void {\n const make = (d: string, cls: string) => {\n const path = this.doc.createElementNS(SVG_NS, 'path')\n path.setAttribute('d', d)\n path.setAttribute('class', cls)\n return path\n }\n const nodes: SVGElement[] = shape.paths.map((p) => {\n const path = make(p.d, p.dash ? 'stroke fade' : 'stroke draw')\n if (p.dash) path.setAttribute('stroke-dasharray', p.dash)\n // Solid strokes draw on by animating a normalised dash.\n else path.setAttribute('pathLength', '1')\n if (p.width) path.setAttribute('stroke-width', String(p.width))\n if (p.opacity) path.setAttribute('opacity', String(p.opacity))\n return path\n })\n if (shape.head) nodes.push(make(shape.head, 'stroke head'))\n if (shape.dot) {\n for (const [radius, cls] of [\n [8, 'dot-halo'],\n [3.5, 'dot'],\n ] as const) {\n const c = this.doc.createElementNS(SVG_NS, 'circle')\n c.setAttribute('cx', String(shape.dot.x))\n c.setAttribute('cy', String(shape.dot.y))\n c.setAttribute('r', String(radius))\n c.setAttribute('class', `${cls} head`)\n nodes.push(c)\n }\n }\n this.el.classList.toggle('animate', animate)\n this.el.replaceChildren(...nodes)\n }\n}\n"],"mappings":"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"}
package/dist/index.cjs CHANGED
@@ -1,4 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_arrows = require("./arrows-DdDu3O3y.cjs");
2
3
  let _docentjs_core = require("@docentjs/core");
3
4
  //#region src/content.ts
4
5
  const SAFE_SCHEMES = /* @__PURE__ */ new Set([
@@ -267,16 +268,41 @@ function clipToViewport(rect, viewport) {
267
268
  }
268
269
  //#endregion
269
270
  //#region src/overlay.ts
270
- /**
271
- * Full-viewport backdrop with a rounded cutout. The cutout is a `clip-path`
272
- * so pointer events pass through the hole to the page for free; a separate
273
- * blocker element covers it when the step forbids interaction.
274
- */
275
271
  function holePath(viewport, hole, radius) {
276
272
  const r = Math.max(0, Math.min(radius, hole.width / 2, hole.height / 2));
277
273
  const { x, y, width: w, height: h } = hole;
278
274
  return `path(evenodd, "${`M0 0H${viewport.width}V${viewport.height}H0Z`}${`M${x + r} ${y}H${x + w - r}A${r} ${r} 0 0 1 ${x + w} ${y + r}V${y + h - r}A${r} ${r} 0 0 1 ${x + w - r} ${y + h}H${x + r}A${r} ${r} 0 0 1 ${x} ${y + h - r}V${y + r}A${r} ${r} 0 0 1 ${x + r} ${y}Z`}")`;
279
275
  }
276
+ /** The padded cutout and its corner radius for a shape. */
277
+ function holeFor(target, padding, radius, shape = "rounded") {
278
+ if (shape === "circle") {
279
+ const cx = target.x + target.width / 2;
280
+ const cy = target.y + target.height / 2;
281
+ const rr = Math.hypot(target.width, target.height) / 2 + padding;
282
+ return {
283
+ hole: {
284
+ x: cx - rr,
285
+ y: cy - rr,
286
+ width: rr * 2,
287
+ height: rr * 2
288
+ },
289
+ radius: rr
290
+ };
291
+ }
292
+ const hole = inflate(target, padding);
293
+ if (shape === "rect") return {
294
+ hole,
295
+ radius: 0
296
+ };
297
+ if (shape === "pill") return {
298
+ hole,
299
+ radius: Math.min(hole.width, hole.height) / 2
300
+ };
301
+ return {
302
+ hole,
303
+ radius: Math.max(0, Math.min(radius, hole.width / 2, hole.height / 2))
304
+ };
305
+ }
280
306
  var Overlay = class {
281
307
  el;
282
308
  blocker;
@@ -309,13 +335,13 @@ var Overlay = class {
309
335
  get hole() {
310
336
  return this.lastHole;
311
337
  }
312
- update(viewport, { target, padding, radius }, block) {
313
- if (target) this.lastHole = inflate(target, padding);
314
- else {
338
+ update(viewport, { target, padding, radius, shape }, block) {
339
+ if (!target) {
315
340
  const c = this.lastHole;
316
341
  const cx = c ? c.x + c.width / 2 : viewport.width / 2;
317
342
  const cy = c ? c.y + c.height / 2 : viewport.height / 2;
318
343
  this.lastHole = null;
344
+ this.setCentre(cx, cy);
319
345
  this.el.style.clipPath = holePath(viewport, {
320
346
  x: cx,
321
347
  y: cy,
@@ -331,9 +357,11 @@ var Overlay = class {
331
357
  this.blocker.hidden = true;
332
358
  return;
333
359
  }
334
- const hole = this.lastHole;
335
- this.el.style.clipPath = holePath(viewport, hole, radius);
336
- this.placeRing(hole, Math.max(0, Math.min(radius, hole.width / 2, hole.height / 2)), true);
360
+ const { hole, radius: r } = holeFor(target, padding, radius, shape);
361
+ this.lastHole = hole;
362
+ this.setCentre(hole.x + hole.width / 2, hole.y + hole.height / 2);
363
+ this.el.style.clipPath = holePath(viewport, hole, r);
364
+ this.placeRing(hole, r, true);
337
365
  this.blocker.hidden = !block;
338
366
  if (block) {
339
367
  this.blocker.style.transform = `translate(${hole.x}px, ${hole.y}px)`;
@@ -341,6 +369,11 @@ var Overlay = class {
341
369
  this.blocker.style.height = `${hole.height}px`;
342
370
  }
343
371
  }
372
+ /** Exposed for the vignette style, which is centred on the cutout. */
373
+ setCentre(x, y) {
374
+ this.el.style.setProperty("--docent-hole-x", `${Math.round(x)}px`);
375
+ this.el.style.setProperty("--docent-hole-y", `${Math.round(y)}px`);
376
+ }
344
377
  };
345
378
  //#endregion
346
379
  //#region src/popover.ts
@@ -540,7 +573,7 @@ function buildHeadlessShell(doc) {
540
573
  * are ink tinted toward the Docent hue (OKLCH 285). Light is the default;
541
574
  * dark is opt-in through tokens or the `dark` preset.
542
575
  */
543
- const STYLES = `:host{--docent-bg:oklch(99.4% 0.003 285);--docent-fg:oklch(23% 0.018 285);--docent-muted:oklch(52% 0.014 285);--docent-accent:oklch(26% 0.02 285);--docent-accent-fg:oklch(98.5% 0.004 285);--docent-radius:14px;--docent-shadow:0 1px 2px oklch(23% 0.02 285 / 0.06),0 8px 24px -6px oklch(23% 0.02 285 / 0.16),0 28px 56px -16px oklch(23% 0.02 285 / 0.24);--docent-width:344px;--docent-overlay:oklch(20% 0.02 285);--docent-overlay-opacity:0.52;--docent-duration:220ms;--docent-easing:cubic-bezier(0.2,0.8,0.2,1);--_line:color-mix(in oklch,var(--docent-fg) 11%,transparent);--_soft:color-mix(in oklch,var(--docent-fg) 6%,transparent);--_body:color-mix(in oklch,var(--docent-fg) 80%,var(--docent-bg));position:fixed;inset:0;z-index:var(--docent-z,2147483000);pointer-events:none;color:var(--docent-fg);font-family:var(--docent-font);font-size:14px;font-weight:400;font-style:normal;line-height:1.55;letter-spacing:normal;text-transform:none;text-align:start;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}*{box-sizing:border-box}.overlay{position:absolute;inset:0;background:var(--docent-overlay);opacity:var(--docent-overlay-opacity);pointer-events:auto;transition:clip-path var(--docent-duration) var(--docent-easing)}.blocker{position:absolute;left:0;top:0;pointer-events:auto}.ring{position:absolute;left:0;top:0;pointer-events:none;box-shadow:0 0 0 1px oklch(98% 0.004 285 / 0.58),0 0 0 6px oklch(98% 0.004 285 / 0.08);transition:transform var(--docent-duration) var(--docent-easing),width var(--docent-duration) var(--docent-easing),height var(--docent-duration) var(--docent-easing),border-radius var(--docent-duration) var(--docent-easing),opacity var(--docent-duration) var(--docent-easing)}.popover{position:absolute;left:0;top:0;display:flex;flex-direction:column;width:var(--docent-width);max-width:calc(100vw - 32px);padding:20px 20px 16px;background:var(--docent-bg);border-radius:var(--docent-radius);box-shadow:0 0 0 1px var(--_line),var(--docent-shadow);pointer-events:auto;outline:none;transition:transform var(--docent-duration) var(--docent-easing),opacity var(--docent-duration) var(--docent-easing),scale var(--docent-duration) var(--docent-easing)}.popover[data-side="bottom"]{transform-origin:50% 0}.popover[data-side="top"]{transform-origin:50% 100%}.popover[data-side="right"]{transform-origin:0 50%}.popover[data-side="left"]{transform-origin:100% 50%}.popover[data-entering]{opacity:0;scale:0.97;transition:none}.popover[data-moving]>*{animation:docent-swap 160ms ease-out}@keyframes docent-swap{from{opacity:0}to{opacity:1}}.popover.headless{width:auto;max-width:none;padding:0;background:none;box-shadow:none;border-radius:0}.arrow{position:absolute;width:12px;height:12px;background:var(--docent-bg);transform:rotate(45deg)}.popover[data-side="bottom"] .arrow{top:-6px;border-top:1px solid var(--_line);border-left:1px solid var(--_line);border-top-left-radius:2px}.popover[data-side="top"] .arrow{bottom:-6px;border-bottom:1px solid var(--_line);border-right:1px solid var(--_line);border-bottom-right-radius:2px}.popover[data-side="right"] .arrow{left:-6px;border-bottom:1px solid var(--_line);border-left:1px solid var(--_line);border-bottom-left-radius:2px}.popover[data-side="left"] .arrow{right:-6px;border-top:1px solid var(--_line);border-right:1px solid var(--_line);border-top-right-radius:2px}.popover[data-side="center"] .arrow,.popover[data-side="sheet"] .arrow{display:none}.header{display:flex;align-items:flex-start;gap:12px}.title{flex:1;min-width:0;margin:0;font-size:18px;font-weight:600;line-height:1.3;letter-spacing:-0.012em;color:var(--docent-fg);text-wrap:balance}.close{flex:none;display:grid;place-items:center;width:28px;height:28px;margin:-3px -8px -3px 0;padding:0;border:0;border-radius:8px;background:transparent;color:var(--docent-muted);cursor:pointer;transition:background-color 120ms ease-out,color 120ms ease-out}.close svg{width:14px;height:14px}.close:hover{background:var(--_soft);color:var(--docent-fg)}.body{margin-top:6px;color:var(--_body);text-wrap:pretty}.body p{margin:0}.body p + p{margin-top:8px}.body strong{font-weight:600;color:var(--docent-fg)}.body a{color:var(--docent-fg);text-decoration:underline;text-decoration-color:color-mix(in oklch,var(--docent-fg) 32%,transparent);text-decoration-thickness:1px;text-underline-offset:3px;transition:text-decoration-color 120ms ease-out}.body a:hover{text-decoration-color:currentColor}.body code{font-family:ui-monospace,"SF Mono",SFMono-Regular,Menlo,Consolas,monospace;font-size:0.88em;padding:1px 5px;border-radius:5px;background:var(--_soft);color:var(--docent-fg)}.media{margin-top:14px}.media img,.media video{display:block;width:100%;border-radius:8px;box-shadow:0 0 0 1px var(--_line)}.footer{display:flex;align-items:center;gap:12px;margin-top:20px}.progress{flex:1;display:flex;align-items:center;gap:8px;min-width:0;color:var(--docent-muted);font-size:12px;font-variant-numeric:tabular-nums;letter-spacing:0.01em;white-space:nowrap}.meter{flex:none;width:28px;height:3px;border-radius:999px;background:linear-gradient(var(--docent-fg),var(--docent-fg)) 0 0 / calc(var(--docent-step) / var(--docent-steps) * 100%) 100% no-repeat,var(--_line)}.buttons{display:flex;align-items:center;gap:6px}.button{appearance:none;display:inline-flex;align-items:center;justify-content:center;height:32px;padding:0 12px;border:0;border-radius:8px;background:transparent;color:var(--docent-fg);font:inherit;font-size:13px;font-weight:500;line-height:1;letter-spacing:-0.003em;white-space:nowrap;cursor:pointer;transition:background-color 120ms ease-out,color 120ms ease-out,scale 80ms ease-out}.button:hover{background:var(--_soft)}.button:active{scale:0.97}[part~="button-skip"]{color:var(--docent-muted);padding:0 8px}[part~="button-skip"]:hover{color:var(--docent-fg);background:transparent}.button.primary{padding:0 14px;background:var(--docent-accent);color:var(--docent-accent-fg);font-weight:600;box-shadow:inset 0 1px 0 color-mix(in oklch,var(--docent-accent-fg) 14%,transparent)}.button.primary .icon{width:12px;height:12px;margin:0 -2px 0 6px;transition:translate 160ms var(--docent-easing)}.button.primary:hover .icon{translate:2px 0}.button.primary:hover{background:color-mix(in oklch,var(--docent-accent) 86%,var(--docent-accent-fg))}.button:focus-visible,.close:focus-visible{outline:2px solid var(--docent-accent);outline-offset:2px}.popover.sheet{max-width:none;padding:20px 20px max(16px,env(safe-area-inset-bottom));border-radius:var(--docent-radius) var(--docent-radius) 0 0;box-shadow:0 0 0 1px var(--_line),0 -12px 40px -12px oklch(23% 0.02 285 / 0.28)}@media (pointer:coarse){:host{font-size:15px}.button{min-height:44px;padding:0 16px;font-size:14px}.close{width:40px;height:40px;margin:-9px -12px -9px 0}}@media (prefers-reduced-motion:reduce){.overlay,.popover,.ring{transition:none}.popover[data-moving]>*{animation:none}}`;
576
+ const STYLES = `:host{--docent-bg:oklch(99.4% 0.003 285);--docent-fg:oklch(23% 0.018 285);--docent-muted:oklch(52% 0.014 285);--docent-accent:oklch(26% 0.02 285);--docent-accent-fg:oklch(98.5% 0.004 285);--docent-radius:14px;--docent-shadow:0 1px 2px oklch(23% 0.02 285 / 0.06),0 8px 24px -6px oklch(23% 0.02 285 / 0.16),0 28px 56px -16px oklch(23% 0.02 285 / 0.24);--docent-width:344px;--docent-overlay:oklch(20% 0.02 285);--docent-overlay-opacity:0.52;--docent-duration:220ms;--docent-easing:cubic-bezier(0.2,0.8,0.2,1);--_line:color-mix(in oklch,var(--docent-fg) 11%,transparent);--_soft:color-mix(in oklch,var(--docent-fg) 6%,transparent);--_body:color-mix(in oklch,var(--docent-fg) 80%,var(--docent-bg));position:fixed;inset:0;z-index:var(--docent-z,2147483000);pointer-events:none;color:var(--docent-fg);font-family:var(--docent-font);font-size:14px;font-weight:400;font-style:normal;line-height:1.55;letter-spacing:normal;text-transform:none;text-align:start;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}*{box-sizing:border-box}.overlay{--_scrim:color-mix(in oklch,var(--docent-overlay) calc(var(--docent-overlay-opacity) * 100%),transparent);position:absolute;inset:0;background:var(--_scrim);pointer-events:auto;transition:clip-path var(--docent-duration) var(--docent-easing)}:host([data-overlay="blur"]) .overlay{-webkit-backdrop-filter:blur(var(--docent-blur,4px));backdrop-filter:blur(var(--docent-blur,4px))}:host([data-overlay="vignette"]) .overlay{background:radial-gradient( circle at var(--docent-hole-x,50%) var(--docent-hole-y,50%),transparent 0,color-mix(in oklch,var(--docent-overlay) calc(var(--docent-overlay-opacity) * 30%),transparent) 22%,var(--_scrim) 78% )}:host([data-overlay="none"]) .overlay{background:transparent;pointer-events:none}.blocker{position:absolute;left:0;top:0;pointer-events:auto}.ring{position:absolute;left:0;top:0;pointer-events:none;color:var(--docent-ring,oklch(98% 0.004 285));box-shadow:0 0 0 1px color-mix(in oklch,currentColor 58%,transparent),0 0 0 6px color-mix(in oklch,currentColor 8%,transparent);transition:transform var(--docent-duration) var(--docent-easing),width var(--docent-duration) var(--docent-easing),height var(--docent-duration) var(--docent-easing),border-radius var(--docent-duration) var(--docent-easing),opacity var(--docent-duration) var(--docent-easing)}:host([data-overlay="none"]) .ring{color:var(--docent-ring,var(--docent-accent))}:host([data-ring="none"]) .ring{box-shadow:none}:host([data-ring="glow"]) .ring{box-shadow:0 0 0 1.5px color-mix(in oklch,currentColor 85%,transparent),0 0 20px 4px color-mix(in oklch,currentColor 42%,transparent)}:host([data-ring="solid"]) .ring{box-shadow:0 0 0 2px currentColor}:host([data-ring="dashed"]) .ring{box-shadow:none;outline:1.5px dashed color-mix(in oklch,currentColor 85%,transparent);outline-offset:3px}:host([data-ring="pulse"]) .ring::after{content:"";position:absolute;inset:0;border-radius:inherit;animation:docent-pulse 1.8s var(--docent-easing) infinite}@keyframes docent-pulse{from{box-shadow:0 0 0 0 color-mix(in oklch,currentColor 60%,transparent)}to{box-shadow:0 0 0 14px transparent}}:host(:not([data-arrow="caret"])) .arrow{display:none}:host([data-tracking]) .overlay,:host([data-tracking]) .ring,:host([data-tracking]) .popover{transition:none}.popover{position:absolute;left:0;top:0;display:flex;flex-direction:column;width:var(--docent-width);max-width:calc(100vw - 32px);padding:20px 20px 16px;background:var(--docent-bg);border-radius:var(--docent-radius);box-shadow:0 0 0 1px var(--_line),var(--docent-shadow);pointer-events:auto;outline:none;transition:transform var(--docent-duration) var(--docent-easing),opacity var(--docent-duration) var(--docent-easing),scale var(--docent-duration) var(--docent-easing)}.popover[data-side="bottom"]{transform-origin:50% 0}.popover[data-side="top"]{transform-origin:50% 100%}.popover[data-side="right"]{transform-origin:0 50%}.popover[data-side="left"]{transform-origin:100% 50%}.popover[data-entering]{opacity:0;scale:0.97;transition:none}.popover[data-moving]>*{animation:docent-swap 160ms ease-out}@keyframes docent-swap{from{opacity:0}to{opacity:1}}.popover.headless{width:auto;max-width:none;padding:0;background:none;box-shadow:none;border-radius:0}.arrow{position:absolute;width:12px;height:12px;background:var(--docent-bg);transform:rotate(45deg)}.popover[data-side="bottom"] .arrow{top:-6px;border-top:1px solid var(--_line);border-left:1px solid var(--_line);border-top-left-radius:2px}.popover[data-side="top"] .arrow{bottom:-6px;border-bottom:1px solid var(--_line);border-right:1px solid var(--_line);border-bottom-right-radius:2px}.popover[data-side="right"] .arrow{left:-6px;border-bottom:1px solid var(--_line);border-left:1px solid var(--_line);border-bottom-left-radius:2px}.popover[data-side="left"] .arrow{right:-6px;border-top:1px solid var(--_line);border-right:1px solid var(--_line);border-top-right-radius:2px}.popover[data-side="center"] .arrow,.popover[data-side="sheet"] .arrow{display:none}.header{display:flex;align-items:flex-start;gap:12px}.title{flex:1;min-width:0;margin:0;font-size:18px;font-weight:600;line-height:1.3;letter-spacing:-0.012em;color:var(--docent-fg);text-wrap:balance}.close{flex:none;display:grid;place-items:center;width:28px;height:28px;margin:-3px -8px -3px 0;padding:0;border:0;border-radius:8px;background:transparent;color:var(--docent-muted);cursor:pointer;transition:background-color 120ms ease-out,color 120ms ease-out}.close svg{width:14px;height:14px}.close:hover{background:var(--_soft);color:var(--docent-fg)}.body{margin-top:6px;color:var(--_body);text-wrap:pretty}.body p{margin:0}.body p + p{margin-top:8px}.body strong{font-weight:600;color:var(--docent-fg)}.body a{color:var(--docent-fg);text-decoration:underline;text-decoration-color:color-mix(in oklch,var(--docent-fg) 32%,transparent);text-decoration-thickness:1px;text-underline-offset:3px;transition:text-decoration-color 120ms ease-out}.body a:hover{text-decoration-color:currentColor}.body code{font-family:ui-monospace,"SF Mono",SFMono-Regular,Menlo,Consolas,monospace;font-size:0.88em;padding:1px 5px;border-radius:5px;background:var(--_soft);color:var(--docent-fg)}.media{margin-top:14px}.media img,.media video{display:block;width:100%;border-radius:8px;box-shadow:0 0 0 1px var(--_line)}.footer{display:flex;align-items:center;gap:12px;margin-top:20px}.progress{flex:1;display:flex;align-items:center;gap:8px;min-width:0;color:var(--docent-muted);font-size:12px;font-variant-numeric:tabular-nums;letter-spacing:0.01em;white-space:nowrap}.meter{flex:none;width:28px;height:3px;border-radius:999px;background:linear-gradient(var(--docent-fg),var(--docent-fg)) 0 0 / calc(var(--docent-step) / var(--docent-steps) * 100%) 100% no-repeat,var(--_line)}.buttons{display:flex;align-items:center;gap:6px}.button{appearance:none;display:inline-flex;align-items:center;justify-content:center;height:32px;padding:0 12px;border:0;border-radius:8px;background:transparent;color:var(--docent-fg);font:inherit;font-size:13px;font-weight:500;line-height:1;letter-spacing:-0.003em;white-space:nowrap;cursor:pointer;transition:background-color 120ms ease-out,color 120ms ease-out,scale 80ms ease-out}.button:hover{background:var(--_soft)}.button:active{scale:0.97}[part~="button-skip"]{color:var(--docent-muted);padding:0 8px}[part~="button-skip"]:hover{color:var(--docent-fg);background:transparent}.button.primary{padding:0 14px;background:var(--docent-accent);color:var(--docent-accent-fg);font-weight:600;box-shadow:inset 0 1px 0 color-mix(in oklch,var(--docent-accent-fg) 14%,transparent)}.button.primary .icon{width:12px;height:12px;margin:0 -2px 0 6px;transition:translate 160ms var(--docent-easing)}.button.primary:hover .icon{translate:2px 0}.button.primary:hover{background:color-mix(in oklch,var(--docent-accent) 86%,var(--docent-accent-fg))}.button:focus-visible,.close:focus-visible{outline:2px solid var(--docent-accent);outline-offset:2px}.popover.sheet{max-width:none;padding:20px 20px max(16px,env(safe-area-inset-bottom));border-radius:var(--docent-radius) var(--docent-radius) 0 0;box-shadow:0 0 0 1px var(--_line),0 -12px 40px -12px oklch(23% 0.02 285 / 0.28)}@media (pointer:coarse){:host{font-size:15px}.button{min-height:44px;padding:0 16px;font-size:14px}.close{width:40px;height:40px;margin:-9px -12px -9px 0}}@media (prefers-reduced-motion:reduce){.overlay,.popover,.ring{transition:none}.ring::after{animation:none !important}.popover[data-moving]>*{animation:none}}`;
544
577
  //#endregion
545
578
  //#region src/target.ts
546
579
  /** Attribute that `{ name }` targets resolve through. */
@@ -641,7 +674,9 @@ const THEME_VARS = {
641
674
  overlay: "overlay",
642
675
  overlayOpacity: "overlay-opacity",
643
676
  duration: "duration",
644
- zIndex: "z"
677
+ zIndex: "z",
678
+ connector: "connector",
679
+ ring: "ring"
645
680
  };
646
681
  /** Write theme tokens as inline custom properties on an element. Clears unset ones. */
647
682
  function applyTheme(el, theme) {
@@ -657,6 +692,11 @@ function mergeThemes(...themes) {
657
692
  }
658
693
  //#endregion
659
694
  //#region src/renderer.ts
695
+ const DEFAULT_LOOK = {
696
+ arrow: "caret",
697
+ spotlight: {},
698
+ overlay: {}
699
+ };
660
700
  const FOCUSABLE = "a[href], button:not([disabled]), input, select, textarea, [tabindex]:not([tabindex=\"-1\"])";
661
701
  var DomRenderer = class {
662
702
  doc;
@@ -675,6 +715,17 @@ var DomRenderer = class {
675
715
  previousFocus = null;
676
716
  /** Set once per step after the sheet has scrolled the target clear. */
677
717
  sheetAdjusted = false;
718
+ connector;
719
+ /** Loaded on first use: connector styles cost nothing for tours that never use them. */
720
+ connectorModule;
721
+ connectorLoading;
722
+ /** Arrow, spotlight and overlay settings for the current step. */
723
+ look = DEFAULT_LOOK;
724
+ /** Until then the step's own transition runs; scroll updates may animate. */
725
+ settleUntil = 0;
726
+ /** Play the connector draw-in on its next render. */
727
+ drawConnector = false;
728
+ trackingFrame;
678
729
  constructor(options = {}) {
679
730
  this.options = options;
680
731
  this.doc = options.document ?? document;
@@ -702,6 +753,9 @@ var DomRenderer = class {
702
753
  const template = this.template(ctx);
703
754
  applyTheme(host, mergeThemes(this.options.theme, template?.theme, ctx.tour.options?.theme));
704
755
  this.setTemplateCss(template?.css);
756
+ this.applyLook(host, this.resolveLook(ctx, template));
757
+ this.settleUntil = performance.now() + this.duration(host) * 1.5;
758
+ this.drawConnector = true;
705
759
  const initialFocus = this.options.headless ? this.buildHeadless(ctx, host, this.options.headless) : this.buildDefault(ctx, host, template);
706
760
  if (this.popover && from) {
707
761
  this.popover.style.transform = from;
@@ -732,31 +786,34 @@ var DomRenderer = class {
732
786
  this.host = void 0;
733
787
  this.shadow = void 0;
734
788
  this.overlay = void 0;
789
+ this.connector = void 0;
735
790
  this.templateStyle = void 0;
736
791
  }
737
792
  const prev = this.previousFocus;
738
793
  this.previousFocus = null;
739
794
  if (prev instanceof HTMLElement && prev.isConnected) prev.focus({ preventScroll: true });
740
795
  }
741
- /** Re-measure and re-position everything. Safe to call often. */
742
- update() {
796
+ /**
797
+ * Re-measure and re-position everything. Safe to call often. `tracking`
798
+ * marks updates caused by scroll or resize: once the step's own transition
799
+ * has finished, those follow the target instantly instead of trailing it.
800
+ */
801
+ update(tracking = false) {
743
802
  const ctx = this.ctx;
744
803
  const overlay = this.overlay;
745
804
  const popover = this.popover;
746
805
  const win = this.doc.defaultView;
747
806
  if (!ctx || !overlay || !popover || !win) return;
807
+ if (tracking && performance.now() > this.settleUntil) this.markTracking();
748
808
  const viewport = this.viewport();
749
809
  const overlaySize = {
750
810
  width: overlay.el.offsetWidth,
751
811
  height: overlay.el.offsetHeight
752
812
  };
753
- const spotlight = {
754
- ...this.options.spotlight,
755
- ...ctx.tour.options?.spotlight,
756
- ...ctx.step.spotlight
757
- };
813
+ const spotlight = this.look.spotlight;
758
814
  const padding = spotlight.padding ?? 8;
759
815
  const radius = spotlight.radius ?? 10;
816
+ const shape = spotlight.shape ?? "rounded";
760
817
  const external = this.headlessContainer;
761
818
  const sheet = this.isSheet(viewport);
762
819
  popover.classList.toggle("sheet", sheet);
@@ -770,8 +827,10 @@ var DomRenderer = class {
770
827
  overlay.update(overlaySize, {
771
828
  target: rect,
772
829
  padding,
773
- radius
830
+ radius,
831
+ shape
774
832
  }, this.blocksInteraction(ctx.step));
833
+ this.connector?.clear();
775
834
  const vx = viewport.x ?? 0;
776
835
  const top = (viewport.y ?? 0) + viewport.height - floating.height;
777
836
  popover.style.transform = `translate(${vx}px, ${top}px)`;
@@ -786,6 +845,7 @@ var DomRenderer = class {
786
845
  padding,
787
846
  radius
788
847
  }, false);
848
+ this.connector?.clear();
789
849
  const { x, y } = centerPosition(floating, viewport);
790
850
  popover.style.transform = `translate(${x}px, ${y}px)`;
791
851
  popover.setAttribute("data-side", "center");
@@ -796,14 +856,16 @@ var DomRenderer = class {
796
856
  overlay.update(overlaySize, {
797
857
  target: rect,
798
858
  padding,
799
- radius
859
+ radius,
860
+ shape
800
861
  }, this.blocksInteraction(ctx.step));
862
+ const hole = overlay.hole ?? rect;
801
863
  const pos = computePosition({
802
- anchor: clipToViewport(overlay.hole ?? rect, viewport),
864
+ anchor: clipToViewport(hole, viewport),
803
865
  floating,
804
866
  viewport,
805
867
  placement: ctx.step.placement ?? "auto",
806
- gap: this.options.gap ?? 12
868
+ gap: this.options.gap ?? require_arrows.arrowGap(this.look.arrow)
807
869
  });
808
870
  popover.style.transform = `translate(${pos.x}px, ${pos.y}px)`;
809
871
  popover.setAttribute("data-side", pos.side);
@@ -816,6 +878,142 @@ var DomRenderer = class {
816
878
  external.setAttribute("data-side", pos.side);
817
879
  external.style.setProperty("--docent-arrow", `${pos.arrow}px`);
818
880
  }
881
+ this.renderConnector(pos, floating, hole);
882
+ }
883
+ /** Draw the connector for connector arrow styles; clear it otherwise. */
884
+ renderConnector(pos, floating, hole) {
885
+ const style = this.look.arrow;
886
+ if (!require_arrows.isConnector(style)) {
887
+ this.connector?.clear();
888
+ return;
889
+ }
890
+ const mod = this.connectorModule;
891
+ const connector = this.connector;
892
+ if (!mod || !connector) {
893
+ this.loadConnector();
894
+ return;
895
+ }
896
+ const inset = 6;
897
+ const clampX = (x) => Math.min(Math.max(x, hole.x + 10), hole.x + hole.width - 10);
898
+ const clampY = (y) => Math.min(Math.max(y, hole.y + 10), hole.y + hole.height - 10);
899
+ const cx = hole.x + hole.width / 2;
900
+ const cy = hole.y + hole.height / 2;
901
+ const alongX = pos.x + floating.width * (cx < pos.x + floating.width / 2 ? .3 : .7);
902
+ const alongY = pos.y + floating.height * (cy < pos.y + floating.height / 2 ? .3 : .7);
903
+ let from;
904
+ let to;
905
+ switch (pos.side) {
906
+ case "bottom":
907
+ from = {
908
+ x: alongX,
909
+ y: pos.y
910
+ };
911
+ to = {
912
+ x: clampX(cx),
913
+ y: hole.y + hole.height + inset
914
+ };
915
+ break;
916
+ case "top":
917
+ from = {
918
+ x: alongX,
919
+ y: pos.y + floating.height
920
+ };
921
+ to = {
922
+ x: clampX(cx),
923
+ y: hole.y - inset
924
+ };
925
+ break;
926
+ case "right":
927
+ from = {
928
+ x: pos.x,
929
+ y: alongY
930
+ };
931
+ to = {
932
+ x: hole.x + hole.width + inset,
933
+ y: clampY(cy)
934
+ };
935
+ break;
936
+ default:
937
+ from = {
938
+ x: pos.x + floating.width,
939
+ y: alongY
940
+ };
941
+ to = {
942
+ x: hole.x - inset,
943
+ y: clampY(cy)
944
+ };
945
+ }
946
+ const bend = pos.side === "top" || pos.side === "bottom" ? to.x < from.x ? 1 : -1 : to.y < from.y ? -1 : 1;
947
+ connector.render(mod.connectorShape(style, from, to, pos.side === "top" || pos.side === "left" ? -bend : bend), this.drawConnector);
948
+ this.drawConnector = false;
949
+ }
950
+ /** Fetch the connector module once, then draw with it. */
951
+ loadConnector() {
952
+ this.connectorLoading ??= Promise.resolve().then(() => require("./connector-D6Al4Deu.cjs")).then((mod) => {
953
+ this.connectorModule = mod;
954
+ if (this.shadow) this.attachConnector(this.shadow, mod);
955
+ this.update();
956
+ });
957
+ }
958
+ /** Add the connector layer and its styles, beneath any popover. */
959
+ attachConnector(shadow, mod) {
960
+ if (this.connector) return;
961
+ const style = this.doc.createElement("style");
962
+ style.textContent = mod.CONNECTOR_STYLES_CSS;
963
+ this.connector = new mod.Connector(this.doc);
964
+ const before = this.popover ?? null;
965
+ shadow.insertBefore(style, before);
966
+ shadow.insertBefore(this.connector.el, before);
967
+ }
968
+ resolveLook(ctx, template) {
969
+ const tour = ctx.tour.options ?? {};
970
+ const step = ctx.step;
971
+ return {
972
+ arrow: step.arrow ?? tour.arrow ?? template?.arrow ?? this.options.arrow ?? "caret",
973
+ spotlight: {
974
+ ...this.options.spotlight,
975
+ ...template?.spotlight,
976
+ ...tour.spotlight,
977
+ ...step.spotlight
978
+ },
979
+ overlay: {
980
+ ...this.options.overlay,
981
+ ...template?.overlay,
982
+ ...tour.overlay,
983
+ ...step.overlay
984
+ }
985
+ };
986
+ }
987
+ /** Expose the look to the stylesheet as host attributes and variables. */
988
+ applyLook(host, look) {
989
+ this.look = look;
990
+ host.setAttribute("data-arrow", look.arrow);
991
+ host.setAttribute("data-ring", look.spotlight.ring ?? "hairline");
992
+ host.setAttribute("data-shape", look.spotlight.shape ?? "rounded");
993
+ host.setAttribute("data-overlay", look.overlay.style ?? "dim");
994
+ const { color, opacity, blur } = look.overlay;
995
+ if (color !== void 0) host.style.setProperty("--docent-overlay", color);
996
+ if (opacity !== void 0) host.style.setProperty("--docent-overlay-opacity", String(opacity));
997
+ if (blur !== void 0) host.style.setProperty("--docent-blur", `${blur}px`);
998
+ else host.style.removeProperty("--docent-blur");
999
+ }
1000
+ /** The current transition duration in ms, from the --docent-duration token. */
1001
+ duration(host) {
1002
+ const raw = this.doc.defaultView?.getComputedStyle(host).getPropertyValue("--docent-duration").trim() ?? "";
1003
+ const n = Number.parseFloat(raw);
1004
+ if (Number.isNaN(n)) return 220;
1005
+ return raw.endsWith("ms") ? n : n * 1e3;
1006
+ }
1007
+ /** Disable transitions for this frame so scroll-driven moves stay glued to the target. */
1008
+ markTracking() {
1009
+ const host = this.host;
1010
+ if (!host) return;
1011
+ host.setAttribute("data-tracking", "");
1012
+ if (this.trackingFrame !== void 0) cancelAnimationFrame(this.trackingFrame);
1013
+ this.trackingFrame = requestAnimationFrame(() => {
1014
+ this.trackingFrame = void 0;
1015
+ host.removeAttribute("data-tracking");
1016
+ });
819
1017
  }
820
1018
  /**
821
1019
  * The visible area in layout-viewport coordinates. Uses the visual viewport
@@ -954,6 +1152,7 @@ var DomRenderer = class {
954
1152
  shadow.appendChild(overlay.el);
955
1153
  shadow.appendChild(overlay.ring);
956
1154
  shadow.appendChild(overlay.blocker);
1155
+ if (this.connectorModule) this.attachConnector(shadow, this.connectorModule);
957
1156
  this.doc.body.appendChild(host);
958
1157
  this.host = host;
959
1158
  this.shadow = shadow;
@@ -967,6 +1166,7 @@ var DomRenderer = class {
967
1166
  this.frame = void 0;
968
1167
  this.popover?.remove();
969
1168
  this.popover = void 0;
1169
+ this.connector?.clear();
970
1170
  this.arrow = void 0;
971
1171
  this.ctx = void 0;
972
1172
  this.target = null;
@@ -1010,7 +1210,7 @@ var DomRenderer = class {
1010
1210
  if (this.frame !== void 0) return;
1011
1211
  this.frame = requestAnimationFrame(() => {
1012
1212
  this.frame = void 0;
1013
- this.update();
1213
+ this.update(true);
1014
1214
  });
1015
1215
  };
1016
1216
  listen() {
@@ -1281,6 +1481,7 @@ function createDocent(options = {}) {
1281
1481
  });
1282
1482
  }
1283
1483
  //#endregion
1484
+ exports.CONNECTOR_STYLES = require_arrows.CONNECTOR_STYLES;
1284
1485
  exports.DEFAULT_LABELS = DEFAULT_LABELS;
1285
1486
  exports.DomRenderer = DomRenderer;
1286
1487
  exports.DomTourController = DomTourController;
@@ -1288,6 +1489,7 @@ exports.NAME_ATTRIBUTE = NAME_ATTRIBUTE;
1288
1489
  exports.Overlay = Overlay;
1289
1490
  exports.THEME_VARS = THEME_VARS;
1290
1491
  exports.applyTheme = applyTheme;
1492
+ exports.arrowGap = require_arrows.arrowGap;
1291
1493
  exports.availableSpace = availableSpace;
1292
1494
  exports.buildHeadlessShell = buildHeadlessShell;
1293
1495
  exports.buildPopover = buildPopover;
@@ -1308,6 +1510,7 @@ exports.findOccluder = findOccluder;
1308
1510
  exports.formatProgress = formatProgress;
1309
1511
  exports.holePath = holePath;
1310
1512
  exports.inflate = inflate;
1513
+ exports.isConnector = require_arrows.isConnector;
1311
1514
  exports.isSafeUrl = isSafeUrl;
1312
1515
  exports.mergeThemes = mergeThemes;
1313
1516
  exports.parsePlacement = parsePlacement;