veo-sdk 0.5.5 → 0.5.10

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,5 @@
1
+ export { VEO_BLOCK_MIME, attachBlockDropTarget, attachDesignManipulator, createBuilderSession, initBuilderMode, startElementPicker, stopElementPicker } from './chunk-S2CLUAAC.mjs';
2
+ import './chunk-END325ZM.mjs';
3
+ import './chunk-PTG3BTJE.mjs';
4
+ //# sourceMappingURL=builder-2TBF3NZF.mjs.map
5
+ //# sourceMappingURL=builder-2TBF3NZF.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"builder-O2QHV2DV.mjs"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"builder-2TBF3NZF.mjs"}
package/dist/builder.cjs CHANGED
@@ -522,7 +522,8 @@ function buildButtonBlock(block, doc, callbacks) {
522
522
  btn.textContent = typeof block.text === "string" && block.text ? block.text : "OK";
523
523
  applyBlockStyle(btn, block.style, "button");
524
524
  btn.addEventListener("click", () => {
525
- const action = block.action ?? "dismiss";
525
+ const raw = block.action ?? "dismiss";
526
+ const action = raw === "next" || raw === "url" || raw === "guide" ? raw : "dismiss";
526
527
  const target = action === "url" && typeof block.url === "string" && isSafeUrl(block.url) ? block.url : action === "guide" && typeof block.targetGuideId === "string" && block.targetGuideId ? block.targetGuideId : void 0;
527
528
  const meta = {
528
529
  ...typeof block.id === "string" && block.id ? { buttonId: block.id } : {},
@@ -568,6 +569,42 @@ function applyBlockStyle(el, style, kind) {
568
569
  if (typeof s.marginBottom === "number" && Number.isFinite(s.marginBottom)) {
569
570
  el.style.marginBottom = `${clampNum(s.marginBottom, 0, 64)}px`;
570
571
  }
572
+ if (kind !== "image") {
573
+ if (typeof s.fontSize === "number" && Number.isFinite(s.fontSize)) {
574
+ el.style.fontSize = `${clampNum(s.fontSize, 8, 96)}px`;
575
+ }
576
+ if (typeof s.fontFamily === "string" && s.fontFamily && s.fontFamily !== "inherit") {
577
+ el.style.fontFamily = s.fontFamily;
578
+ }
579
+ if (typeof s.fontWeight === "string" && s.fontWeight) el.style.fontWeight = s.fontWeight;
580
+ if (typeof s.textTransform === "string" && s.textTransform && s.textTransform !== "none") {
581
+ el.style.textTransform = s.textTransform;
582
+ }
583
+ if (typeof s.lineHeight === "number" && Number.isFinite(s.lineHeight)) {
584
+ el.style.lineHeight = `${clampNum(s.lineHeight, 50, 300)}%`;
585
+ }
586
+ if (typeof s.letterSpacing === "number" && Number.isFinite(s.letterSpacing)) {
587
+ el.style.letterSpacing = `${clampNum(s.letterSpacing, -5, 20)}px`;
588
+ }
589
+ }
590
+ if (typeof s.background === "string" && s.background) el.style.background = s.background;
591
+ if (typeof s.borderWidth === "number" && s.borderWidth > 0) {
592
+ const bc = typeof s.borderColor === "string" && s.borderColor ? s.borderColor : "#bbbbbb";
593
+ el.style.border = `${clampNum(s.borderWidth, 0, 20)}px solid ${bc}`;
594
+ }
595
+ if (kind !== "image" && typeof s.radius === "number" && Number.isFinite(s.radius)) {
596
+ el.style.borderRadius = `${clampNum(s.radius, 0, 64)}px`;
597
+ }
598
+ for (const side of ["Top", "Right", "Bottom", "Left"]) {
599
+ const p = s[`padding${side}`];
600
+ if (typeof p === "number" && Number.isFinite(p)) {
601
+ el.style.setProperty(`padding-${side.toLowerCase()}`, `${clampNum(p, 0, 128)}px`);
602
+ }
603
+ const m = s[`margin${side}`];
604
+ if (typeof m === "number" && Number.isFinite(m)) {
605
+ el.style.setProperty(`margin-${side.toLowerCase()}`, `${clampNum(m, 0, 128)}px`);
606
+ }
607
+ }
571
608
  }
572
609
  function createCloseButton(doc, onClose, className = "veo-guide-close") {
573
610
  const closeBtn = doc.createElement("button");
@@ -918,6 +955,35 @@ var GUIDE_STYLES = `
918
955
  animation: veo-fade-in 160ms ease-out;
919
956
  }
920
957
 
958
+ /* \u2500\u2500 Modelo unificado (composite) \u2500\u2500 */
959
+ .veo-composite-overlay {
960
+ position: fixed; inset: 0;
961
+ z-index: ${GUIDE_Z_INDEX};
962
+ pointer-events: none;
963
+ }
964
+ /* Backdrop atenuado (solo centro con backdrop activo). */
965
+ .veo-composite-overlay--dim {
966
+ background: rgba(0, 0, 0, 0.5);
967
+ pointer-events: auto;
968
+ }
969
+ .veo-composite-anchored {
970
+ position: fixed;
971
+ z-index: ${GUIDE_Z_INDEX};
972
+ }
973
+ /* Card gen\xE9rica del modelo unificado. El dise\xF1o (bg/borde/padding/\u2026) lo aplica
974
+ el renderer como estilos inline desde step.design; ac\xE1 van los defaults. */
975
+ .veo-card {
976
+ pointer-events: auto;
977
+ background: var(--veo-bg, #ffffff);
978
+ color: var(--veo-text, #1f2937);
979
+ border-radius: var(--veo-radius, 12px);
980
+ padding: 20px;
981
+ max-width: 90vw;
982
+ box-shadow: var(--veo-shadow, 0 12px 40px rgba(0, 0, 0, 0.18));
983
+ animation: veo-fade-in 160ms ease-out;
984
+ }
985
+ .veo-composite-anchored .veo-card, .veo-composite-overlay .veo-card { position: relative; }
986
+
921
987
  .veo-guide-content {
922
988
  position: relative;
923
989
  display: flex; flex-direction: column;
@@ -1880,85 +1946,53 @@ function escapeClosing(code, tag) {
1880
1946
  return code.replace(rx, `<\\/${tag}`);
1881
1947
  }
1882
1948
 
1883
- // src/plugins/guides/renderers/custom-renderer.ts
1884
- var DEFAULT_PLACEMENT = { mode: "floating", position: "bottom-right" };
1885
- var DEFAULT_OFFSET = 16;
1886
- var DEFAULT_WIDTH = 360;
1887
- var CustomRenderer = class extends BaseRenderer {
1888
- async render(context) {
1889
- const step = context.guide.guideSteps[0];
1890
- if (!step || typeof step.html !== "string" || step.html.length === 0) return;
1891
- const placement = step.placement ?? DEFAULT_PLACEMENT;
1892
- let anchor = null;
1893
- if (placement.mode === "anchored") {
1894
- const selector = placement.selector ?? step.selector ?? context.guide.activationRules.selector;
1895
- if (typeof selector !== "string" || selector.length === 0) return;
1896
- anchor = await waitForElement(selector);
1897
- if (!anchor) return;
1898
- }
1899
- const { root } = this.createHost();
1900
- const doc = root.ownerDocument ?? document;
1949
+ // src/plugins/guides/code-block.ts
1950
+ function mountCodeBlock(block, doc, context) {
1951
+ if (block.sandboxed) {
1952
+ const step = { html: block.html ?? "", css: block.css, js: block.js };
1953
+ const { iframe, cleanup } = mountCustomFrame(doc, step, context, { width: "100%" });
1901
1954
  const wrapper = doc.createElement("div");
1902
- wrapper.className = placement.mode === "anchored" ? "veo-custom-anchored" : "veo-custom-floating";
1903
- const { iframe, cleanup } = mountCustomFrame(doc, step, context, {
1904
- width: toCssSize(placement.width ?? DEFAULT_WIDTH),
1905
- fixedHeight: placement.height ?? null
1906
- });
1907
- this.registerCleanup(cleanup);
1955
+ wrapper.className = "veo-code-sandbox";
1908
1956
  wrapper.appendChild(iframe);
1909
- root.appendChild(wrapper);
1910
- if (placement.mode === "floating") {
1911
- applyFloatingPosition(wrapper, placement);
1912
- } else if (anchor) {
1913
- this.registerCleanup(await this.setupAnchoredPosition(anchor, wrapper, placement));
1957
+ return { node: wrapper, cleanup };
1958
+ }
1959
+ const hostDoc = doc;
1960
+ const injected = [];
1961
+ if (typeof block.css === "string" && block.css.trim()) {
1962
+ const style = hostDoc.createElement("style");
1963
+ style.textContent = block.css;
1964
+ hostDoc.head.appendChild(style);
1965
+ injected.push(style);
1966
+ }
1967
+ let node = null;
1968
+ if (typeof block.html === "string" && block.html.trim()) {
1969
+ node = doc.createElement("div");
1970
+ node.className = "veo-code-html";
1971
+ node.innerHTML = block.html;
1972
+ for (const old of Array.from(node.querySelectorAll("script"))) {
1973
+ injected.push(runScript(old, hostDoc));
1974
+ old.remove();
1914
1975
  }
1915
- context.onInteraction({ guideId: context.guide.guideId, stepIndex: 0, action: "shown" });
1916
- }
1917
- async setupAnchoredPosition(anchor, wrapper, placement) {
1918
- const side = placement.side ?? "bottom";
1919
- const update = async () => {
1920
- const { x, y } = await dom.computePosition(anchor, wrapper, {
1921
- placement: side,
1922
- strategy: "fixed",
1923
- middleware: [dom.offset(placement.offsetY ?? 8), dom.flip(), dom.shift({ padding: 8 })]
1924
- });
1925
- wrapper.style.left = `${x}px`;
1926
- wrapper.style.top = `${y}px`;
1927
- };
1928
- await update();
1929
- const reposition = () => {
1930
- void update();
1931
- };
1932
- window.addEventListener("scroll", reposition, true);
1933
- window.addEventListener("resize", reposition);
1934
- return () => {
1935
- window.removeEventListener("scroll", reposition, true);
1936
- window.removeEventListener("resize", reposition);
1937
- };
1938
- }
1939
- };
1940
- function applyFloatingPosition(wrapper, placement) {
1941
- const position = placement.position ?? "bottom-right";
1942
- const ox = `${placement.offsetX ?? DEFAULT_OFFSET}px`;
1943
- const oy = `${placement.offsetY ?? DEFAULT_OFFSET}px`;
1944
- const s = wrapper.style;
1945
- if (position === "center") {
1946
- s.top = "50%";
1947
- s.left = "50%";
1948
- s.transform = "translate(-50%, -50%)";
1949
- return;
1950
1976
  }
1951
- const [vertical, horizontal] = position.split("-");
1952
- if (vertical === "top") s.top = oy;
1953
- else s.bottom = oy;
1954
- if (horizontal === "left") {
1955
- s.left = ox;
1956
- } else if (horizontal === "right") {
1957
- s.right = ox;
1958
- } else {
1959
- s.left = "50%";
1960
- s.transform = "translateX(-50%)";
1977
+ if (typeof block.js === "string" && block.js.trim()) {
1978
+ const s = hostDoc.createElement("script");
1979
+ s.textContent = block.js;
1980
+ hostDoc.body.appendChild(s);
1981
+ injected.push(s);
1961
1982
  }
1983
+ return {
1984
+ node,
1985
+ cleanup: () => {
1986
+ for (const n of injected) n.parentNode?.removeChild(n);
1987
+ }
1988
+ };
1989
+ }
1990
+ function runScript(old, hostDoc) {
1991
+ const s = hostDoc.createElement("script");
1992
+ for (const attr of Array.from(old.attributes)) s.setAttribute(attr.name, attr.value);
1993
+ if (!old.src) s.textContent = old.textContent;
1994
+ hostDoc.body.appendChild(s);
1995
+ return s;
1962
1996
  }
1963
1997
 
1964
1998
  // src/plugins/guides/form-content.ts
@@ -2226,6 +2260,430 @@ function buildScale(wrapper, doc, min, max, btnClass, symbol) {
2226
2260
  return { wrapper, read: () => selected };
2227
2261
  }
2228
2262
 
2263
+ // src/plugins/guides/renderers/composite-renderer.ts
2264
+ var CompositeRenderer = class extends BaseRenderer {
2265
+ async render(context) {
2266
+ const idx = context.nav?.stepIndex ?? 0;
2267
+ const step = context.guide.guideSteps[idx];
2268
+ if (!step) return;
2269
+ const anchorSelector = step.position === "relative-to-element" || step.presentation === "inline" ? step.anchorSelector?.trim() || void 0 : void 0;
2270
+ let anchor = null;
2271
+ if (anchorSelector) {
2272
+ anchor = await waitForElement(anchorSelector);
2273
+ if (!anchor) return;
2274
+ }
2275
+ const { host, root } = this.createHost();
2276
+ const doc = root.ownerDocument ?? document;
2277
+ const design = step.design ?? {};
2278
+ if (typeof design.zIndex === "number") host.style.zIndex = String(design.zIndex);
2279
+ const card = doc.createElement("div");
2280
+ card.className = "veo-card";
2281
+ applyCompositeDesign(card, design);
2282
+ const dismiss = (action, meta) => {
2283
+ context.onInteraction({
2284
+ guideId: context.guide.guideId,
2285
+ stepIndex: idx,
2286
+ action,
2287
+ ...{}
2288
+ });
2289
+ };
2290
+ const readers = [];
2291
+ this.buildContent(card, step.blocks ?? [], doc, context, idx, readers);
2292
+ if (design.closeButton !== false) {
2293
+ const closeBtn = createCloseButton(doc, () => {
2294
+ dismiss("dismissed");
2295
+ this.closeOrSkip(context);
2296
+ });
2297
+ const closeColor = typeof design.closeColor === "string" ? design.closeColor : null;
2298
+ const hoverColor = typeof design.closeHoverColor === "string" ? design.closeHoverColor : null;
2299
+ if (closeColor) closeBtn.style.color = closeColor;
2300
+ if (hoverColor) {
2301
+ closeBtn.addEventListener("mouseenter", () => {
2302
+ closeBtn.style.color = hoverColor;
2303
+ });
2304
+ closeBtn.addEventListener("mouseleave", () => {
2305
+ closeBtn.style.color = closeColor ?? "";
2306
+ });
2307
+ }
2308
+ card.appendChild(closeBtn);
2309
+ }
2310
+ if (step.presentation === "inline" && anchor) {
2311
+ host.style.display = "block";
2312
+ const pos = inlinePosOf(step.inlinePosition);
2313
+ root.appendChild(card);
2314
+ insertHost(anchor, host, pos);
2315
+ this.registerCleanup(keepHostAttached(host, anchorSelector, pos));
2316
+ } else if (step.position === "relative-to-element" && anchor) {
2317
+ if (design.backdrop === true) {
2318
+ const bg = doc.createElement("div");
2319
+ bg.className = "veo-composite-overlay veo-composite-overlay--dim";
2320
+ if (typeof design.backdropColor === "string") bg.style.background = design.backdropColor;
2321
+ const bz = typeof design.backdropZIndex === "number" ? design.backdropZIndex : 2e5;
2322
+ bg.style.zIndex = String(bz);
2323
+ root.appendChild(bg);
2324
+ if (design.targetAbove === true) this.registerCleanup(liftTarget(anchor, bz + 1));
2325
+ }
2326
+ const wrapper = doc.createElement("div");
2327
+ wrapper.className = "veo-composite-anchored";
2328
+ const caretEl = design.caret === false ? null : makeCaret(doc, design);
2329
+ if (caretEl) wrapper.appendChild(caretEl);
2330
+ wrapper.appendChild(card);
2331
+ root.appendChild(wrapper);
2332
+ this.registerCleanup(await anchorPosition(anchor, wrapper, caretEl));
2333
+ } else {
2334
+ const overlay = doc.createElement("div");
2335
+ overlay.className = "veo-composite-overlay";
2336
+ if (step.position === "center" && design.backdrop !== false) {
2337
+ overlay.classList.add("veo-composite-overlay--dim");
2338
+ if (typeof design.backdropColor === "string") {
2339
+ overlay.style.background = design.backdropColor;
2340
+ }
2341
+ if (typeof design.backdropZIndex === "number") {
2342
+ overlay.style.zIndex = String(design.backdropZIndex);
2343
+ }
2344
+ overlay.addEventListener("click", (e) => {
2345
+ if (e.target === overlay) {
2346
+ dismiss("dismissed");
2347
+ this.closeOrSkip(context);
2348
+ }
2349
+ });
2350
+ }
2351
+ applyViewportPosition(card, step.position, design);
2352
+ overlay.appendChild(card);
2353
+ root.appendChild(overlay);
2354
+ }
2355
+ context.onInteraction({ guideId: context.guide.guideId, stepIndex: idx, action: "shown" });
2356
+ }
2357
+ /** Recorre los bloques en orden y los pinta en la card. */
2358
+ buildContent(card, blocks, doc, context, idx, readers) {
2359
+ const emit = (block) => {
2360
+ context.onInteraction({
2361
+ guideId: context.guide.guideId,
2362
+ stepIndex: idx,
2363
+ action: "cta_clicked",
2364
+ ...block.id ? { metadata: { buttonId: block.id } } : {}
2365
+ });
2366
+ };
2367
+ let i = 0;
2368
+ while (i < blocks.length) {
2369
+ const b = blocks[i];
2370
+ if (!b) {
2371
+ i++;
2372
+ continue;
2373
+ }
2374
+ if (b.type === "button") {
2375
+ const row = doc.createElement("div");
2376
+ row.className = "veo-guide-actions";
2377
+ while (i < blocks.length && blocks[i]?.type === "button") {
2378
+ row.appendChild(
2379
+ this.buildButton(blocks[i], doc, context, idx, readers, emit)
2380
+ );
2381
+ i++;
2382
+ }
2383
+ card.appendChild(row);
2384
+ continue;
2385
+ }
2386
+ if (b.type === "code") {
2387
+ const { node: node2, cleanup } = mountCodeBlock(b, doc, context);
2388
+ if (node2) card.appendChild(node2);
2389
+ this.registerCleanup(cleanup);
2390
+ i++;
2391
+ continue;
2392
+ }
2393
+ if (b.type === "form") {
2394
+ const form = doc.createElement("div");
2395
+ form.className = "veo-form";
2396
+ for (const field of b.fields ?? []) {
2397
+ const { wrapper, read } = buildField(field, doc);
2398
+ readers.push({ field, read });
2399
+ form.appendChild(wrapper);
2400
+ }
2401
+ card.appendChild(form);
2402
+ i++;
2403
+ continue;
2404
+ }
2405
+ const node = buildContentBlock(b, doc);
2406
+ if (node) card.appendChild(node);
2407
+ i++;
2408
+ }
2409
+ }
2410
+ /** Botón con enrutado de acción (navegación / cierre / url / guía / envío de forms). */
2411
+ buildButton(block, doc, context, idx, readers, emit) {
2412
+ const btn = doc.createElement("button");
2413
+ btn.type = "button";
2414
+ btn.className = "veo-guide-cta";
2415
+ btn.textContent = block.text?.trim() || "OK";
2416
+ const nav = context.nav?.callbacks;
2417
+ btn.addEventListener("click", () => {
2418
+ switch (block.action) {
2419
+ case "next":
2420
+ emit(block);
2421
+ if (nav) nav.onNext();
2422
+ else context.onClose();
2423
+ break;
2424
+ case "prev":
2425
+ emit(block);
2426
+ nav?.onBack();
2427
+ break;
2428
+ case "url":
2429
+ emit(block);
2430
+ if (typeof block.url === "string" && isSafeUrl(block.url)) {
2431
+ window.open(block.url, "_blank", "noopener,noreferrer");
2432
+ }
2433
+ break;
2434
+ case "guide":
2435
+ emit(block);
2436
+ if (block.targetGuideId) context.onOpenGuide?.(block.targetGuideId);
2437
+ this.closeOrSkip(context);
2438
+ break;
2439
+ case "submit-forms":
2440
+ case "submit-forms-advance":
2441
+ case "submit-forms-goto":
2442
+ this.submitForms(readers, context, idx, btn, () => {
2443
+ emit(block);
2444
+ if (block.action === "submit-forms" || !nav) {
2445
+ context.onClose();
2446
+ } else if (block.action === "submit-forms-goto" && typeof block.targetStep === "number" && nav.onGoto) {
2447
+ nav.onGoto(block.targetStep);
2448
+ } else {
2449
+ nav.onNext();
2450
+ }
2451
+ });
2452
+ break;
2453
+ default:
2454
+ emit(block);
2455
+ this.closeOrSkip(context);
2456
+ }
2457
+ });
2458
+ return btn;
2459
+ }
2460
+ /** Recoge respuestas de todos los form-blocks del paso, valida y envía. */
2461
+ submitForms(readers, context, idx, btn, onDone) {
2462
+ const answers = {};
2463
+ for (const { field, read } of readers) {
2464
+ const value = read();
2465
+ const empty = value === void 0 || value === null || value === "";
2466
+ if (empty) {
2467
+ if (field.required) {
2468
+ btn.textContent = `Completa "${field.label}"`;
2469
+ return;
2470
+ }
2471
+ continue;
2472
+ }
2473
+ answers[field.key] = value;
2474
+ }
2475
+ if (readers.length > 0 && Object.keys(answers).length === 0) return;
2476
+ btn.disabled = true;
2477
+ const submitFn = context.onFormSubmit ?? (() => Promise.resolve(true));
2478
+ void submitFn(answers).then((ok) => {
2479
+ btn.disabled = false;
2480
+ if (!ok) return;
2481
+ context.onInteraction({
2482
+ guideId: context.guide.guideId,
2483
+ stepIndex: idx,
2484
+ action: "completed"
2485
+ });
2486
+ onDone();
2487
+ });
2488
+ }
2489
+ closeOrSkip(context) {
2490
+ if (context.nav?.callbacks) context.nav.callbacks.onSkip();
2491
+ else context.onClose();
2492
+ }
2493
+ };
2494
+ function inlinePosOf(p) {
2495
+ return p === "before" || p === "prepend" || p === "append" ? p : "after";
2496
+ }
2497
+ function applyViewportPosition(card, position, design) {
2498
+ const s = card.style;
2499
+ s.position = "fixed";
2500
+ const M = "16px";
2501
+ const hAlign = design.hAlign === "left" ? "left" : design.hAlign === "right" ? "right" : null;
2502
+ const v = position.includes("top") ? "top" : position.includes("bottom") ? "bottom" : "middle";
2503
+ const h = hAlign ?? (position.includes("left") ? "left" : position.includes("right") ? "right" : "middle");
2504
+ if (v === "top") s.top = M;
2505
+ else if (v === "bottom") s.bottom = M;
2506
+ else s.top = "50%";
2507
+ if (h === "left") s.left = M;
2508
+ else if (h === "right") s.right = M;
2509
+ else s.left = "50%";
2510
+ const tx = h === "middle" ? "-50%" : "0";
2511
+ const ty = v === "middle" ? "-50%" : "0";
2512
+ s.transform = tx === "0" && ty === "0" ? "" : `translate(${tx}, ${ty})`;
2513
+ }
2514
+ function makeCaret(doc, design) {
2515
+ const caret = doc.createElement("div");
2516
+ caret.className = "veo-composite-caret";
2517
+ const w = typeof design.caretWidth === "number" ? design.caretWidth : 12;
2518
+ const h = typeof design.caretHeight === "number" ? design.caretHeight : 12;
2519
+ const s = caret.style;
2520
+ s.position = "absolute";
2521
+ s.width = `${w}px`;
2522
+ s.height = `${h}px`;
2523
+ s.transform = "rotate(45deg)";
2524
+ s.background = typeof design.background === "string" ? design.background : "#ffffff";
2525
+ if (typeof design.borderWidth === "number" && design.borderWidth > 0) {
2526
+ const bc = typeof design.borderColor === "string" ? design.borderColor : "#bbbbbb";
2527
+ s.border = `${design.borderWidth}px solid ${bc}`;
2528
+ }
2529
+ return caret;
2530
+ }
2531
+ function liftTarget(anchor, zIndex) {
2532
+ const el = anchor;
2533
+ const prevPosition = el.style.position;
2534
+ const prevZ = el.style.zIndex;
2535
+ if (getComputedStyle(el).position === "static") el.style.position = "relative";
2536
+ el.style.zIndex = String(zIndex);
2537
+ return () => {
2538
+ el.style.position = prevPosition;
2539
+ el.style.zIndex = prevZ;
2540
+ };
2541
+ }
2542
+ async function anchorPosition(anchor, wrapper, caret) {
2543
+ const update = async () => {
2544
+ const { x, y, placement, middlewareData } = await dom.computePosition(anchor, wrapper, {
2545
+ placement: "bottom",
2546
+ strategy: "fixed",
2547
+ middleware: [
2548
+ dom.offset(caret ? 10 : 8),
2549
+ dom.flip(),
2550
+ dom.shift({ padding: 8 }),
2551
+ ...caret ? [dom.arrow({ element: caret })] : []
2552
+ ]
2553
+ });
2554
+ wrapper.style.left = `${x}px`;
2555
+ wrapper.style.top = `${y}px`;
2556
+ if (caret) positionArrow(caret, placement, middlewareData.arrow);
2557
+ };
2558
+ await update();
2559
+ const reposition = () => void update();
2560
+ window.addEventListener("scroll", reposition, true);
2561
+ window.addEventListener("resize", reposition);
2562
+ return () => {
2563
+ window.removeEventListener("scroll", reposition, true);
2564
+ window.removeEventListener("resize", reposition);
2565
+ };
2566
+ }
2567
+ function applyCompositeDesign(card, design) {
2568
+ const s = card.style;
2569
+ const px = (v) => typeof v === "number" && Number.isFinite(v) ? `${v}px` : null;
2570
+ const strv = (v) => typeof v === "string" && v ? v : null;
2571
+ if (strv(design.background)) s.background = design.background;
2572
+ const textColor = strv(design.color) ?? strv(design.textColor);
2573
+ if (textColor) s.color = textColor;
2574
+ if (px(design.fontSize)) s.fontSize = px(design.fontSize);
2575
+ if (strv(design.fontWeight)) s.fontWeight = design.fontWeight;
2576
+ if (strv(design.fontFamily) && design.fontFamily !== "inherit") {
2577
+ s.fontFamily = design.fontFamily;
2578
+ }
2579
+ if (strv(design.textTransform) && design.textTransform !== "none") {
2580
+ s.textTransform = design.textTransform;
2581
+ }
2582
+ if (typeof design.letterSpacing === "number") s.letterSpacing = `${design.letterSpacing}px`;
2583
+ if (strv(design.align)) s.textAlign = design.align;
2584
+ if (typeof design.lineHeight === "number") s.lineHeight = `${design.lineHeight}%`;
2585
+ if (px(design.radius)) s.borderRadius = px(design.radius);
2586
+ const bw = px(design.borderWidth);
2587
+ if (bw) s.border = `${bw} solid ${strv(design.borderColor) ?? "#e5e5e5"}`;
2588
+ if (strv(design.shadowColor) || typeof design.shadowBlur === "number") {
2589
+ const n = (k) => typeof design[k] === "number" ? design[k] : 0;
2590
+ s.boxShadow = `${n("shadowX")}px ${n("shadowY")}px ${n("shadowBlur")}px ${n("shadowSpread")}px ${strv(design.shadowColor) ?? "rgba(0,0,0,0.2)"}`;
2591
+ }
2592
+ if (design.fullWidth === true) {
2593
+ s.width = "100%";
2594
+ } else if (design.widthType === "fluid") {
2595
+ s.width = "auto";
2596
+ if (px(design.width)) s.maxWidth = px(design.width);
2597
+ } else if (px(design.width)) {
2598
+ s.width = px(design.width);
2599
+ }
2600
+ for (const side of ["Top", "Right", "Bottom", "Left"]) {
2601
+ const p = px(design[`padding${side}`]);
2602
+ if (p) s.setProperty(`padding-${side.toLowerCase()}`, p);
2603
+ const m = px(design[`margin${side}`]);
2604
+ if (m) s.setProperty(`margin-${side.toLowerCase()}`, m);
2605
+ }
2606
+ }
2607
+ var DEFAULT_PLACEMENT = { mode: "floating", position: "bottom-right" };
2608
+ var DEFAULT_OFFSET = 16;
2609
+ var DEFAULT_WIDTH = 360;
2610
+ var CustomRenderer = class extends BaseRenderer {
2611
+ async render(context) {
2612
+ const step = context.guide.guideSteps[0];
2613
+ if (!step || typeof step.html !== "string" || step.html.length === 0) return;
2614
+ const placement = step.placement ?? DEFAULT_PLACEMENT;
2615
+ let anchor = null;
2616
+ if (placement.mode === "anchored") {
2617
+ const selector = placement.selector ?? step.selector ?? context.guide.activationRules.selector;
2618
+ if (typeof selector !== "string" || selector.length === 0) return;
2619
+ anchor = await waitForElement(selector);
2620
+ if (!anchor) return;
2621
+ }
2622
+ const { root } = this.createHost();
2623
+ const doc = root.ownerDocument ?? document;
2624
+ const wrapper = doc.createElement("div");
2625
+ wrapper.className = placement.mode === "anchored" ? "veo-custom-anchored" : "veo-custom-floating";
2626
+ const { iframe, cleanup } = mountCustomFrame(doc, step, context, {
2627
+ width: toCssSize(placement.width ?? DEFAULT_WIDTH),
2628
+ fixedHeight: placement.height ?? null
2629
+ });
2630
+ this.registerCleanup(cleanup);
2631
+ wrapper.appendChild(iframe);
2632
+ root.appendChild(wrapper);
2633
+ if (placement.mode === "floating") {
2634
+ applyFloatingPosition(wrapper, placement);
2635
+ } else if (anchor) {
2636
+ this.registerCleanup(await this.setupAnchoredPosition(anchor, wrapper, placement));
2637
+ }
2638
+ context.onInteraction({ guideId: context.guide.guideId, stepIndex: 0, action: "shown" });
2639
+ }
2640
+ async setupAnchoredPosition(anchor, wrapper, placement) {
2641
+ const side = placement.side ?? "bottom";
2642
+ const update = async () => {
2643
+ const { x, y } = await dom.computePosition(anchor, wrapper, {
2644
+ placement: side,
2645
+ strategy: "fixed",
2646
+ middleware: [dom.offset(placement.offsetY ?? 8), dom.flip(), dom.shift({ padding: 8 })]
2647
+ });
2648
+ wrapper.style.left = `${x}px`;
2649
+ wrapper.style.top = `${y}px`;
2650
+ };
2651
+ await update();
2652
+ const reposition = () => {
2653
+ void update();
2654
+ };
2655
+ window.addEventListener("scroll", reposition, true);
2656
+ window.addEventListener("resize", reposition);
2657
+ return () => {
2658
+ window.removeEventListener("scroll", reposition, true);
2659
+ window.removeEventListener("resize", reposition);
2660
+ };
2661
+ }
2662
+ };
2663
+ function applyFloatingPosition(wrapper, placement) {
2664
+ const position = placement.position ?? "bottom-right";
2665
+ const ox = `${placement.offsetX ?? DEFAULT_OFFSET}px`;
2666
+ const oy = `${placement.offsetY ?? DEFAULT_OFFSET}px`;
2667
+ const s = wrapper.style;
2668
+ if (position === "center") {
2669
+ s.top = "50%";
2670
+ s.left = "50%";
2671
+ s.transform = "translate(-50%, -50%)";
2672
+ return;
2673
+ }
2674
+ const [vertical, horizontal] = position.split("-");
2675
+ if (vertical === "top") s.top = oy;
2676
+ else s.bottom = oy;
2677
+ if (horizontal === "left") {
2678
+ s.left = ox;
2679
+ } else if (horizontal === "right") {
2680
+ s.right = ox;
2681
+ } else {
2682
+ s.left = "50%";
2683
+ s.transform = "translateX(-50%)";
2684
+ }
2685
+ }
2686
+
2229
2687
  // src/plugins/guides/renderers/form-renderer.ts
2230
2688
  var FormRenderer = class extends BaseRenderer {
2231
2689
  render(context) {
@@ -2606,6 +3064,13 @@ var GuidePreviewController = class {
2606
3064
  this.input = input;
2607
3065
  const guide = normalize(input);
2608
3066
  const start = typeof input.startStepIndex === "number" ? input.startStepIndex : 0;
3067
+ if (guide.guideType === "composite") {
3068
+ const s = Math.min(Math.max(0, start), guide.guideSteps.length - 1);
3069
+ const active = guide.guideSteps[s];
3070
+ if (active) guide.guideSteps = [active];
3071
+ const ready2 = this.renderSingleStep(guide);
3072
+ return { close: () => this.close(), ready: ready2, host: () => this.activeHost() };
3073
+ }
2609
3074
  const ready = guide.guideType === "walkthrough" ? this.startWalkthrough(guide, start) : this.renderSingleStep(guide);
2610
3075
  return { close: () => this.close(), ready, host: () => this.activeHost() };
2611
3076
  }
@@ -2785,8 +3250,12 @@ function createSingleStepRenderer(type) {
2785
3250
  return new InlineFormRenderer();
2786
3251
  case "badge":
2787
3252
  return new BadgeRenderer();
3253
+ case "composite":
3254
+ return new CompositeRenderer();
2788
3255
  case "walkthrough":
2789
3256
  return null;
3257
+ default:
3258
+ return null;
2790
3259
  }
2791
3260
  }
2792
3261
  function safeDestroy(renderer) {