@preprio/toolkit 0.2.0 → 0.3.4

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.
@@ -396,6 +396,20 @@ function walkTextNodes(root, visit) {
396
396
  visit(node);
397
397
  }
398
398
  }
399
+ function resolveEditTarget(node) {
400
+ const parent = node.parentElement;
401
+ if (!parent) return null;
402
+ const editTarget = parent.closest("[data-prepr-edit-target]");
403
+ if (editTarget) return editTarget;
404
+ return isRenderedElement(parent) ? parent : null;
405
+ }
406
+ function isRenderedElement(element) {
407
+ if (element.hidden) return false;
408
+ if (element.getClientRects().length === 0) return false;
409
+ const style = element.ownerDocument.defaultView?.getComputedStyle(element);
410
+ if (!style) return true;
411
+ return style.visibility !== "hidden" && style.display !== "none";
412
+ }
399
413
  function tagEncodedElement(element, decoded) {
400
414
  element.setAttribute("data-prepr-encoded", "");
401
415
  element.setAttribute("data-prepr-href", decoded.href);
@@ -404,32 +418,45 @@ function tagEncodedElement(element, decoded) {
404
418
 
405
419
  // src/core/stega/auto-clean.ts
406
420
  var debug2 = createScopedLogger("stega:auto-clean");
407
- function createStegaAutoClean() {
421
+ function createStegaAutoClean(options) {
422
+ const stripText = options?.enabled ?? true;
408
423
  let observer = null;
409
424
  let processing = false;
410
425
  let cleaned = /* @__PURE__ */ new WeakMap();
426
+ let pendingHidden = /* @__PURE__ */ new Set();
427
+ let pendingDirty = /* @__PURE__ */ new Set();
411
428
  let debounceTimeout = null;
412
429
  function cleanTextNode(textNode) {
413
430
  if (isIgnoredTextNode(textNode)) return false;
414
431
  const textContent = textNode.textContent;
415
432
  const info = cleaned.get(textNode);
416
- if (info && textContent === info.cleaned) return false;
433
+ const settled = stripText ? info?.cleaned : info?.original;
434
+ if (info && textContent === settled) {
435
+ const shown = resolveEditTarget(textNode);
436
+ if (!shown) return false;
437
+ pendingHidden.delete(textNode);
438
+ if (shown.hasAttribute("data-prepr-encoded")) return false;
439
+ tagEncodedElement(shown, info.decoded);
440
+ return true;
441
+ }
417
442
  try {
418
443
  const { cleaned: stripped, encoded } = (0, import_stega2.vercelStegaSplit)(textContent);
419
444
  if (!encoded || stripped === textContent) return false;
420
445
  const decoded = decodeStega(textContent);
421
446
  if (!decoded?.href) return false;
422
- cleaned.set(textNode, { original: textContent, cleaned: stripped });
423
- textNode.textContent = stripped;
424
- let target = textNode.parentElement;
425
- const editTargetParent = textNode.parentElement?.closest(
426
- "[data-prepr-edit-target]"
427
- );
428
- if (editTargetParent) target = editTargetParent;
447
+ cleaned.set(textNode, {
448
+ original: textContent,
449
+ cleaned: stripped,
450
+ decoded
451
+ });
452
+ if (stripText) textNode.textContent = stripped;
453
+ const target = resolveEditTarget(textNode);
429
454
  if (target) {
455
+ pendingHidden.delete(textNode);
430
456
  tagEncodedElement(target, decoded);
431
457
  return true;
432
458
  }
459
+ pendingHidden.add(textNode);
433
460
  } catch (error) {
434
461
  debug2.log("error cleaning text node", error);
435
462
  }
@@ -438,8 +465,10 @@ function createStegaAutoClean() {
438
465
  function scanAndClean() {
439
466
  processing = true;
440
467
  walkTextNodes(document.body, cleanTextNode);
441
- const title = (0, import_stega2.vercelStegaSplit)(document.title);
442
- if (title.encoded) document.title = title.cleaned;
468
+ if (stripText) {
469
+ const title = (0, import_stega2.vercelStegaSplit)(document.title);
470
+ if (title.encoded) document.title = title.cleaned;
471
+ }
443
472
  processing = false;
444
473
  }
445
474
  function collectAffected(mutations) {
@@ -464,15 +493,37 @@ function createStegaAutoClean() {
464
493
  });
465
494
  return affected;
466
495
  }
496
+ function retryPendingHidden() {
497
+ if (pendingHidden.size === 0) return;
498
+ processing = true;
499
+ [...pendingHidden].forEach((textNode) => {
500
+ if (!textNode.isConnected) {
501
+ pendingHidden.delete(textNode);
502
+ return;
503
+ }
504
+ cleanTextNode(textNode);
505
+ });
506
+ processing = false;
507
+ }
467
508
  function handleMutations(mutations) {
468
509
  if (processing) return;
469
510
  const affected = collectAffected(mutations);
470
- if (affected.size === 0) return;
511
+ if (affected.size === 0) {
512
+ retryPendingHidden();
513
+ return;
514
+ }
515
+ affected.forEach((textNode) => pendingDirty.add(textNode));
471
516
  if (debounceTimeout) clearTimeout(debounceTimeout);
472
517
  debounceTimeout = setTimeout(() => {
518
+ debounceTimeout = null;
473
519
  processing = true;
474
- affected.forEach((textNode) => cleanTextNode(textNode));
520
+ const batch = [...pendingDirty];
521
+ pendingDirty.clear();
522
+ batch.forEach((textNode) => {
523
+ if (textNode.isConnected) cleanTextNode(textNode);
524
+ });
475
525
  processing = false;
526
+ retryPendingHidden();
476
527
  }, 50);
477
528
  }
478
529
  function setupObserver() {
@@ -480,7 +531,11 @@ function createStegaAutoClean() {
480
531
  observer.observe(document.body, {
481
532
  childList: true,
482
533
  subtree: true,
483
- characterData: true
534
+ characterData: true,
535
+ // A dropdown opening only toggles a class or inline style; without these
536
+ // the nodes it reveals are never revisited and stay untagged.
537
+ attributes: true,
538
+ attributeFilter: ["class", "style", "hidden", "open", "aria-expanded"]
484
539
  });
485
540
  }
486
541
  function start() {
@@ -499,6 +554,8 @@ function createStegaAutoClean() {
499
554
  observer = null;
500
555
  }
501
556
  cleaned = /* @__PURE__ */ new WeakMap();
557
+ pendingHidden = /* @__PURE__ */ new Set();
558
+ pendingDirty = /* @__PURE__ */ new Set();
502
559
  debug2.log("stega auto-clean stopped");
503
560
  }
504
561
  return { start, stop };
@@ -521,7 +578,7 @@ var StegaElements = class {
521
578
  tagFromTextNode(node) {
522
579
  const decoded = this.decode(node.textContent);
523
580
  if (!decoded?.href) return false;
524
- const target = node.parentElement;
581
+ const target = resolveEditTarget(node);
525
582
  if (!target || target.hasAttribute("data-prepr-encoded")) return false;
526
583
  tagEncodedElement(target, decoded);
527
584
  return true;
@@ -567,8 +624,13 @@ var StegaElements = class {
567
624
  if (mutation.type === "characterData") {
568
625
  addedNodes.add(mutation.target);
569
626
  }
627
+ if (mutation.type === "attributes") {
628
+ addedNodes.add(mutation.target);
629
+ }
630
+ });
631
+ addedNodes.forEach((node) => {
632
+ if (node.isConnected) this.scanNode(node);
570
633
  });
571
- addedNodes.forEach((node) => this.scanNode(node));
572
634
  pending = [];
573
635
  this.elements = document.querySelectorAll("[data-prepr-encoded]");
574
636
  onUpdate?.();
@@ -581,7 +643,11 @@ var StegaElements = class {
581
643
  this.observer.observe(document.body, {
582
644
  childList: true,
583
645
  subtree: true,
584
- characterData: true
646
+ characterData: true,
647
+ // Reveal-on-class/style (dropdowns, accordions) changes no text and adds
648
+ // no nodes, but does change what is hoverable and needs a rescan.
649
+ attributes: true,
650
+ attributeFilter: ["class", "style", "hidden", "open", "aria-expanded"]
585
651
  });
586
652
  }
587
653
  /** Drops the active class but keeps the data attributes, unlike cleanup(). */
@@ -670,11 +736,13 @@ var StegaOverlay = class {
670
736
  const maxTop = window.scrollY + window.innerHeight - tooltip.clientHeight - 4;
671
737
  const minLeft = window.scrollX + 4;
672
738
  const maxLeft = window.scrollX + window.innerWidth - tooltip.clientWidth - 4;
673
- if (top < minTop) {
739
+ const below = top < minTop;
740
+ if (below) {
674
741
  top = rect.bottom + window.scrollY + 2;
675
742
  }
676
743
  top = Math.max(minTop, Math.min(top, maxTop));
677
744
  left = Math.max(minLeft, Math.min(left, maxLeft));
745
+ tooltip.dataset.preprPlacement = below ? "below" : "above";
678
746
  tooltip.style.top = `${top}px`;
679
747
  tooltip.style.left = `${left}px`;
680
748
  });
@@ -806,7 +874,7 @@ var StegaProximity = class {
806
874
  };
807
875
 
808
876
  // src/core/stega/stega.generated.ts
809
- var STEGA_STYLES = '[data-prepr-encoded]{position:relative}.prepr-overlay{pointer-events:none;z-index:10000;border:2px solid #4f46e5;border-radius:4px 0 4px 4px;transition:all .2s ease-in-out;position:absolute}.prepr-tooltip{color:#fff;white-space:nowrap;z-index:10001;text-align:center;pointer-events:auto;cursor:pointer;background-color:#4f46e5;border-radius:4px 4px 0 0;min-width:80px;padding:4px 8px;font-size:12px;line-height:1;transition:opacity .2s ease-in-out;position:absolute}.prepr-tooltip:hover{background-color:#4338ca}.prepr-proximity-highlight{z-index:1;position:relative}.prepr-proximity-highlight,.prepr-proximity-highlight *{overflow:visible!important}.prepr-proximity-highlight:before{content:"";pointer-events:none;background:radial-gradient(circle var(--gradient-size) at var(--cursor-x) var(--cursor-y), #4f46e526 0%, #2563eb00 70%);z-index:9999;border:2px solid #4f46e526;border-radius:4px;transition:all .2s;position:absolute;top:-2px;bottom:-2px;left:-4px;right:-4px;overflow:visible}';
877
+ var STEGA_STYLES = '[data-prepr-encoded]{position:relative}.prepr-overlay{pointer-events:none;z-index:10000;border:2px solid #4f46e5;border-radius:4px 0 4px 4px;transition:all .2s ease-in-out;position:absolute}.prepr-tooltip{color:#fff;white-space:nowrap;z-index:10001;text-align:center;pointer-events:auto;cursor:pointer;background-color:#4f46e5;border-radius:4px 4px 0 0;min-width:80px;padding:4px 8px;font-size:12px;line-height:1;transition:opacity .2s ease-in-out;position:absolute}.prepr-tooltip[data-prepr-placement=below]{border-radius:0 0 4px 4px}.prepr-tooltip:hover{background-color:#4338ca}.prepr-proximity-highlight{z-index:1;position:relative}.prepr-proximity-highlight,.prepr-proximity-highlight *{overflow:visible!important}.prepr-proximity-highlight:before{content:"";pointer-events:none;background:radial-gradient(circle var(--gradient-size) at var(--cursor-x) var(--cursor-y), #4f46e526 0%, #2563eb00 70%);z-index:9999;border:2px solid #4f46e526;border-radius:4px;transition:all .2s;position:absolute;top:-2px;bottom:-2px;left:-4px;right:-4px;overflow:visible}';
810
878
 
811
879
  // src/core/stega/styles.ts
812
880
  var STEGA_STYLE_MARKER = "data-prepr-stega";
@@ -1355,7 +1423,7 @@ var CLOSE_ICON_SVG = `<svg width="10" height="10" viewBox="0 0 10 10" fill="none
1355
1423
  </svg>`;
1356
1424
 
1357
1425
  // src/core/ui/toolbar.generated.ts
1358
- var TOOLBAR_CSS = ":host{--prepr-primary:#4338ca;--prepr-bg:#eef2ff;--prepr-text:#1f2937;--prepr-radius:8px;--prepr-shadow:0px 0px 40px 0px #1f29373d;--prepr-z-index:10000;--prepr-primary-50:#eef2ff;--prepr-primary-100:#e0e7ff;--prepr-primary-800:#3730a3;--prepr-indigo-600:#4f46e5;--prepr-indigo-50:#eef2ff;--prepr-indigo-700:#4338ca;--prepr-secondary-400:#fb923c;--prepr-secondary-500:#f97316;--prepr-gray-100:#f3f4f6;--prepr-gray-200:#e5e7eb;--prepr-gray-300:#d1d5db;--prepr-gray-400:#9ca3af;--prepr-gray-500:#6b7280;--prepr-gray-700:#374151;--prepr-gray-800:#1f2937;--prepr-gray-900:#111827;--prepr-shadow-sm:0px 0px 12px 0px #c3c3c3cc;color:var(--prepr-text);box-sizing:border-box;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif;font-size:14px;line-height:1.5}:host *,:host :before,:host :after,*,:before,:after{box-sizing:border-box}button{font-family:inherit;font-size:inherit;margin:0}.prepr-regular-text{font-size:.875rem;font-weight:500;line-height:1.5}.prepr-backdrop{z-index:calc(var(--prepr-z-index) - 1);opacity:0;pointer-events:none;background:#0000001a;transition:opacity .2s ease-in-out;position:fixed;top:0;bottom:0;left:0;right:0}.prepr-backdrop[data-open=true]{opacity:1;pointer-events:auto;animation:.2s ease-in-out prepr-fade-in}.prepr-container{z-index:var(--prepr-z-index);justify-content:flex-end;align-items:center;display:flex;position:fixed;top:calc(50% - 64px);right:0}.prepr-toggle-holder{padding:.5rem}.prepr-toggle{cursor:pointer;background:var(--prepr-primary);color:#fff;border:none;border-radius:9999px;justify-content:center;align-items:center;width:2.25rem;height:2.25rem;display:flex}.prepr-toggle>span{display:flex}.prepr-toggle svg{color:#fff}.prepr-panel{z-index:var(--prepr-z-index);opacity:0;pointer-events:none;justify-content:flex-end;padding:.75rem;transition:opacity .2s ease-in-out;display:flex;position:fixed;top:50%;right:0;transform:translateY(-50%)}.prepr-panel[data-open=true]{opacity:1;pointer-events:auto;animation:.2s ease-in-out prepr-fade-in}.prepr-content{box-shadow:var(--prepr-shadow);border-radius:var(--prepr-radius);background:var(--prepr-bg);flex-direction:column;gap:2.5rem;width:100%;padding:1.5rem;display:flex}@media (min-width:640px){.prepr-panel{margin-right:4rem;padding:0}.prepr-content{width:502px;padding:2.5rem}}.prepr-header{justify-content:space-between;align-items:center;gap:.5rem;display:flex}.prepr-header-left{align-items:flex-start;gap:.75rem;display:flex}.prepr-badge{background:var(--prepr-primary-100);height:1.5rem;color:var(--prepr-primary);border-radius:4px;align-items:center;padding:.25rem .75rem;font-size:.875rem;display:flex}.prepr-header-close{cursor:pointer;background:var(--prepr-primary-100);width:2rem;height:2rem;color:var(--prepr-gray-700);border:none;border-radius:6px;justify-content:center;align-items:center;display:flex}.prepr-section{flex-direction:column;gap:.5rem;display:flex}.prepr-section-label{color:var(--prepr-gray-400);font-size:.875rem}.prepr-row{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:.5rem 1.5rem;display:flex}.prepr-row-title{color:var(--prepr-gray-800);margin:0;font-size:1rem;font-weight:600}.prepr-radiogroup{border-radius:var(--prepr-radius);border:1px solid var(--prepr-gray-300);background:#fff;align-items:center;gap:.25rem;height:2.5rem;padding:.25rem;display:flex}.prepr-radiogroup[data-disabled=true]{cursor:not-allowed;opacity:.5}.prepr-radiogroup[data-disabled=true] .prepr-radio{pointer-events:none}.prepr-radio{cursor:pointer;white-space:nowrap;text-align:center;height:2rem;color:var(--prepr-gray-900);background:0 0;border:none;border-radius:6px;justify-content:center;align-items:center;padding:.5rem 1.125rem;font-size:.875rem;font-weight:500;transition:all .2s ease-in-out;display:flex}.prepr-radio[data-checked=true]{background:var(--prepr-indigo-600);color:#fff;box-shadow:var(--prepr-shadow-sm)}.prepr-radio[data-checked=true][data-off=true]{background:var(--prepr-gray-800);color:#fff}.prepr-radio-variant{width:82px}.prepr-listbox{position:relative}.prepr-segment-button{border-radius:var(--prepr-radius);border:1px solid var(--prepr-gray-300);width:240px;height:2.5rem;color:var(--prepr-gray-500);cursor:pointer;background:#fff;flex-shrink:0;align-items:center;gap:.5rem;padding:0 .5rem;font-size:.875rem;font-weight:500;display:flex;overflow:hidden}@media (min-width:768px){.prepr-segment-button{padding:0 1rem}}.prepr-segment-button:disabled{cursor:not-allowed;background:var(--prepr-gray-200);color:var(--prepr-gray-400)}.prepr-segment-button[aria-expanded=true]{border-bottom-color:#fff;border-bottom-right-radius:0;border-bottom-left-radius:0}.prepr-segment-label{white-space:nowrap;text-overflow:ellipsis;text-align:left;width:100%;margin-right:auto;overflow:hidden}.prepr-segment-caret{color:var(--prepr-gray-800);display:flex}.prepr-options{z-index:calc(var(--prepr-z-index) + 1);border-left:1px solid var(--prepr-gray-300);border-right:1px solid var(--prepr-gray-300);border-bottom:1px solid var(--prepr-gray-300);-ms-overflow-style:none;scrollbar-width:none;background:#fff;border-radius:0 0 6px 6px;width:240px;max-height:300px;margin:0;padding:0;list-style:none;position:absolute;top:100%;left:0;overflow-y:auto;box-shadow:0 20px 25px -5px #0000001a,0 8px 10px -6px #0000001a}.prepr-options::-webkit-scrollbar{display:none}.prepr-options[hidden]{display:none}.prepr-option-search{background:#fff;padding:.5rem;position:-webkit-sticky;position:sticky;top:0}.prepr-segment-search{border:1px solid var(--prepr-gray-300);border-radius:var(--prepr-radius);width:100%;height:2rem;font:inherit;color:var(--prepr-gray-900);box-sizing:border-box;background:#fff;padding:0 .5rem;font-size:.875rem}.prepr-segment-search::placeholder{color:var(--prepr-gray-400)}.prepr-segment-search:focus{border-color:var(--prepr-gray-400);outline:none}.prepr-option{cursor:pointer;width:100%;color:var(--prepr-gray-900);background:#fff;align-items:center;padding:.5rem 1rem;font-size:.875rem;font-weight:500;display:flex}.prepr-option:hover{background:var(--prepr-gray-100)}.prepr-option[aria-selected=true]{background:var(--prepr-indigo-50);color:var(--prepr-indigo-700)}.prepr-option-label{white-space:nowrap;text-overflow:ellipsis;text-align:left;width:100%;margin-right:auto;overflow:hidden}.prepr-reset{background:var(--prepr-secondary-400);color:#fff;cursor:pointer;border:none;border-radius:6px;justify-content:center;align-items:center;gap:.5rem;width:100%;height:2.5rem;padding:.5rem .75rem;font-size:.875rem;font-weight:500;display:flex}.prepr-reset:hover:not(:disabled){background:var(--prepr-secondary-500)}.prepr-reset:disabled{background:var(--prepr-gray-400);color:var(--prepr-gray-500);cursor:not-allowed}@media (min-width:768px){.prepr-reset{width:108px}}.prepr-indicators{z-index:var(--prepr-z-index);gap:.75rem;display:flex;position:fixed;bottom:1.5rem;right:1.5rem}.prepr-status-pill{background:var(--prepr-primary);color:#fff;cursor:pointer;border:none;border-radius:9999px;align-items:center;gap:.5rem;padding:.5rem 1rem;font-size:.75rem;font-weight:500;transition:background-color .15s;display:flex;box-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a}.prepr-status-pill:hover{background:var(--prepr-primary-800)}.prepr-status-pill[hidden]{display:none}.prepr-status-viewing{color:#fff9;font-size:10px}.prepr-status-segment{text-overflow:ellipsis;white-space:nowrap;max-width:120px;display:inline-block;overflow:hidden}.prepr-status-variant{text-overflow:ellipsis;white-space:nowrap;background:#fff3;border-radius:4px;max-width:80px;padding:0 .5rem;display:inline-block;overflow:hidden}.prepr-status-x{color:#fff;width:.75rem;height:.75rem}.prepr-close-edit-pill{background:var(--prepr-primary-50);color:var(--prepr-gray-800);cursor:pointer;border:none;border-radius:9999px;align-items:center;gap:.5rem;padding:.5rem 1rem;font-size:.75rem;font-weight:500;transition:background-color .15s;display:flex;box-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a}.prepr-close-edit-pill:hover{background:var(--prepr-primary-100)}.prepr-close-edit-pill[hidden]{display:none}.prepr-close-edit-x{width:1rem;height:1rem;color:var(--prepr-gray-500);transition:color .15s}.prepr-close-edit-pill:hover .prepr-close-edit-x{color:var(--prepr-gray-700)}.prepr-tip{z-index:calc(var(--prepr-z-index) + 2);overflow-wrap:break-word;background:var(--prepr-gray-900);color:#fff;pointer-events:none;border-radius:6px;width:auto;max-width:min(420px,100vw - 16px);padding:.5rem .75rem;font-size:.75rem;font-weight:500;position:fixed;box-shadow:0 20px 25px -5px #0000001a,0 8px 10px -6px #0000001a}.prepr-tip-arrow{border-left:6px solid #0000;border-right:6px solid #0000;border-top:6px solid var(--prepr-gray-900);width:0;height:0;position:absolute;top:100%;transform:translate(-50%)}[hidden]{display:none!important}@keyframes prepr-fade-in{0%{opacity:0}to{opacity:1}}";
1426
+ var TOOLBAR_CSS = ":host{--prepr-primary:#4338ca;--prepr-bg:#eef2ff;--prepr-text:#1f2937;--prepr-radius:8px;--prepr-shadow:0px 0px 40px 0px #1f29373d;--prepr-z-index:10000;--prepr-primary-50:#eef2ff;--prepr-primary-100:#e0e7ff;--prepr-primary-800:#3730a3;--prepr-indigo-600:#4f46e5;--prepr-indigo-50:#eef2ff;--prepr-indigo-700:#4338ca;--prepr-secondary-400:#fb923c;--prepr-secondary-500:#f97316;--prepr-gray-100:#f3f4f6;--prepr-gray-200:#e5e7eb;--prepr-gray-300:#d1d5db;--prepr-gray-400:#9ca3af;--prepr-gray-500:#6b7280;--prepr-gray-700:#374151;--prepr-gray-800:#1f2937;--prepr-gray-900:#111827;--prepr-shadow-sm:0px 0px 12px 0px #c3c3c3cc;color:var(--prepr-text);box-sizing:border-box;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif;font-size:14px;line-height:1.5}:host *,:host :before,:host :after,*,:before,:after{box-sizing:border-box}button{font-family:inherit;font-size:inherit;margin:0}.prepr-regular-text{font-size:.875rem;font-weight:500;line-height:1.5}.prepr-backdrop{z-index:calc(var(--prepr-z-index) - 1);opacity:0;pointer-events:none;background:#0000001a;transition:opacity .2s ease-in-out;position:fixed;top:0;bottom:0;left:0;right:0}.prepr-backdrop[data-open=true]{opacity:1;pointer-events:auto;animation:.2s ease-in-out prepr-fade-in}.prepr-container{z-index:var(--prepr-z-index);justify-content:flex-end;align-items:center;display:flex;position:fixed;top:calc(50% - 64px);right:0}.prepr-toggle-holder{padding:.5rem}.prepr-toggle{cursor:pointer;background:var(--prepr-primary);color:#fff;border:none;border-radius:9999px;justify-content:center;align-items:center;width:2.25rem;height:2.25rem;display:flex}.prepr-toggle>span{display:flex}.prepr-toggle svg{color:#fff}.prepr-panel{z-index:var(--prepr-z-index);opacity:0;pointer-events:none;justify-content:flex-end;padding:.75rem;transition:opacity .2s ease-in-out;display:flex;position:fixed;top:50%;right:0;transform:translateY(-50%)}.prepr-panel[data-open=true]{opacity:1;pointer-events:auto;animation:.2s ease-in-out prepr-fade-in}.prepr-content{box-shadow:var(--prepr-shadow);border-radius:var(--prepr-radius);background:var(--prepr-bg);flex-direction:column;gap:2.5rem;width:100%;padding:1.5rem;display:flex}@media (min-width:640px){.prepr-panel{margin-right:4rem;padding:0}.prepr-content{width:502px;padding:2.5rem}}.prepr-header{justify-content:space-between;align-items:center;gap:.5rem;display:flex}.prepr-header-left{align-items:flex-start;gap:.75rem;display:flex}.prepr-badge{background:var(--prepr-primary-100);height:1.5rem;color:var(--prepr-primary);border-radius:4px;align-items:center;padding:.25rem .75rem;font-size:.875rem;display:flex}.prepr-header-close{cursor:pointer;background:var(--prepr-primary-100);width:2rem;height:2rem;color:var(--prepr-gray-700);border:none;border-radius:6px;justify-content:center;align-items:center;display:flex}.prepr-section{flex-direction:column;gap:.5rem;display:flex}.prepr-section-label{color:var(--prepr-gray-400);font-size:.875rem}.prepr-row{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:.5rem 1.5rem;display:flex}.prepr-row-title{color:var(--prepr-gray-800);margin:0;font-size:1rem;font-weight:600}.prepr-radiogroup{border-radius:var(--prepr-radius);border:1px solid var(--prepr-gray-300);background:#fff;align-items:center;gap:.25rem;height:2.5rem;padding:.25rem;display:flex}.prepr-radiogroup[data-disabled=true]{cursor:not-allowed;opacity:.5}.prepr-radiogroup[data-disabled=true] .prepr-radio{pointer-events:none}.prepr-radio{cursor:pointer;white-space:nowrap;text-align:center;height:2rem;color:var(--prepr-gray-900);background:0 0;border:none;border-radius:6px;justify-content:center;align-items:center;padding:.5rem 1.125rem;font-size:.875rem;font-weight:500;transition:all .2s ease-in-out;display:flex}.prepr-radio[data-checked=true]{background:var(--prepr-indigo-600);color:#fff;box-shadow:var(--prepr-shadow-sm)}.prepr-radio[data-checked=true][data-off=true]{background:var(--prepr-gray-800);color:#fff}.prepr-radio-variant{width:82px}.prepr-listbox{position:relative}.prepr-segment-button{border-radius:var(--prepr-radius);border:1px solid var(--prepr-gray-300);width:240px;height:2.5rem;color:var(--prepr-gray-500);cursor:pointer;background:#fff;flex-shrink:0;align-items:center;gap:.5rem;padding:0 .5rem;font-size:.875rem;font-weight:500;display:flex;overflow:hidden}@media (min-width:768px){.prepr-segment-button{padding:0 1rem}}.prepr-segment-button:disabled{cursor:not-allowed;background:var(--prepr-gray-200);color:var(--prepr-gray-400)}.prepr-segment-button[aria-expanded=true]{border-bottom-color:#fff;border-bottom-right-radius:0;border-bottom-left-radius:0}.prepr-segment-label{white-space:nowrap;text-overflow:ellipsis;text-align:left;width:100%;margin-right:auto;overflow:hidden}.prepr-segment-caret{color:var(--prepr-gray-800);display:flex}.prepr-options{z-index:calc(var(--prepr-z-index) + 1);border-left:1px solid var(--prepr-gray-300);border-right:1px solid var(--prepr-gray-300);border-bottom:1px solid var(--prepr-gray-300);-ms-overflow-style:none;scrollbar-width:none;background:#fff;border-radius:0 0 6px 6px;width:240px;max-height:300px;margin:0;padding:0;list-style:none;position:absolute;top:100%;left:0;overflow-y:auto;box-shadow:0 20px 25px -5px #0000001a,0 8px 10px -6px #0000001a}.prepr-options::-webkit-scrollbar{display:none}.prepr-options[hidden]{display:none}.prepr-option-search{background:#fff;padding:.5rem;position:-webkit-sticky;position:sticky;top:0}.prepr-segment-search{border:1px solid var(--prepr-gray-300);border-radius:var(--prepr-radius);width:100%;height:2rem;font:inherit;color:var(--prepr-gray-900);box-sizing:border-box;background:#fff;padding:0 .5rem;font-size:.875rem}.prepr-segment-search::placeholder{color:var(--prepr-gray-400)}.prepr-segment-search:focus{border-color:var(--prepr-gray-400);outline:none}.prepr-option{cursor:pointer;width:100%;color:var(--prepr-gray-900);background:#fff;align-items:center;padding:.5rem 1rem;font-size:.875rem;font-weight:500;display:flex}.prepr-option:hover{background:var(--prepr-gray-100)}.prepr-option[aria-selected=true]{background:var(--prepr-indigo-50);color:var(--prepr-indigo-700)}.prepr-option-label{white-space:nowrap;text-overflow:ellipsis;text-align:left;width:100%;margin-right:auto;overflow:hidden}.prepr-reset{background:var(--prepr-secondary-400);color:#fff;cursor:pointer;border:none;border-radius:6px;justify-content:center;align-items:center;gap:.5rem;width:100%;height:2.5rem;padding:.5rem .75rem;font-size:.875rem;font-weight:500;display:flex}.prepr-reset:hover:not(:disabled){background:var(--prepr-secondary-500)}.prepr-reset:disabled{background:var(--prepr-gray-400);color:var(--prepr-gray-500);cursor:not-allowed}@media (min-width:768px){.prepr-reset{width:108px}}.prepr-indicators{z-index:var(--prepr-z-index);gap:.75rem;display:flex;position:fixed;bottom:1.5rem;right:1.5rem}.prepr-status-pill{background:var(--prepr-primary);color:#fff;cursor:pointer;border:none;border-radius:9999px;align-items:center;gap:.5rem;padding:.5rem 1rem;font-size:.75rem;font-weight:500;transition:background-color .15s;display:flex;box-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a}.prepr-status-pill[data-interactive=false]{cursor:default;opacity:1;color:#fff}.prepr-status-pill[data-interactive=true]:hover{background:var(--prepr-primary-800)}.prepr-status-pill[hidden]{display:none}.prepr-status-viewing{color:#fff9;font-size:10px}.prepr-status-segment{text-overflow:ellipsis;white-space:nowrap;max-width:120px;display:inline-block;overflow:hidden}.prepr-status-variant{text-overflow:ellipsis;white-space:nowrap;background:#fff3;border-radius:4px;max-width:80px;padding:0 .5rem;display:inline-block;overflow:hidden}.prepr-status-x{color:#fff;width:.75rem;height:.75rem}.prepr-close-edit-pill{background:var(--prepr-primary-50);color:var(--prepr-gray-800);cursor:pointer;border:none;border-radius:9999px;align-items:center;gap:.5rem;padding:.5rem 1rem;font-size:.75rem;font-weight:500;transition:background-color .15s;display:flex;box-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a}.prepr-close-edit-pill:hover{background:var(--prepr-primary-100)}.prepr-close-edit-pill[hidden]{display:none}.prepr-close-edit-x{width:1rem;height:1rem;color:var(--prepr-gray-500);transition:color .15s}.prepr-close-edit-pill:hover .prepr-close-edit-x{color:var(--prepr-gray-700)}.prepr-tip{z-index:calc(var(--prepr-z-index) + 2);overflow-wrap:break-word;background:var(--prepr-gray-900);color:#fff;pointer-events:none;border-radius:6px;width:auto;max-width:min(420px,100vw - 16px);padding:.5rem .75rem;font-size:.75rem;font-weight:500;position:fixed;box-shadow:0 20px 25px -5px #0000001a,0 8px 10px -6px #0000001a}.prepr-tip-arrow{border-left:6px solid #0000;border-right:6px solid #0000;border-top:6px solid var(--prepr-gray-900);width:0;height:0;position:absolute;top:100%;transform:translate(-50%)}[hidden]{display:none!important}@keyframes prepr-fade-in{0%{opacity:0}to{opacity:1}}";
1359
1427
 
1360
1428
  // ../../node_modules/.pnpm/preact@10.29.7/node_modules/preact/jsx-runtime/dist/jsxRuntime.mjs
1361
1429
  var f2 = 0;
@@ -1614,13 +1682,17 @@ function StatusPill({
1614
1682
  } else {
1615
1683
  segmentLabel = defaultSegmentName;
1616
1684
  }
1685
+ const dirty = features.segments && state.selectedSegment !== null || features.abTesting && state.selectedVariant === "B";
1686
+ const interactive = !state.previewMode || dirty;
1617
1687
  return /* @__PURE__ */ u2(
1618
1688
  "button",
1619
1689
  {
1620
1690
  type: "button",
1621
1691
  class: "prepr-status-pill",
1622
1692
  "data-prepr": "status-pill",
1693
+ "data-interactive": String(interactive),
1623
1694
  hidden: state.isIframe,
1695
+ disabled: !interactive,
1624
1696
  onClick: () => handlers.onStatusPill(),
1625
1697
  children: [
1626
1698
  /* @__PURE__ */ u2("span", { class: "prepr-status-viewing", "data-prepr": "status-viewing", children: t3("common.viewingAs") }),
@@ -1639,7 +1711,7 @@ function StatusPill({
1639
1711
  {
1640
1712
  class: "prepr-status-x",
1641
1713
  "data-prepr": "status-x",
1642
- hidden: !state.previewMode || !(features.segments && state.selectedSegment !== null || features.abTesting && state.selectedVariant === "B"),
1714
+ hidden: !state.previewMode || !dirty,
1643
1715
  children: /* @__PURE__ */ u2(RawSvg, { svg: XMARK_ICON_SVG })
1644
1716
  }
1645
1717
  )
@@ -2143,7 +2215,9 @@ function createPreprPreview(opts = {}) {
2143
2215
  }
2144
2216
  }
2145
2217
  });
2146
- const autoClean = createStegaAutoClean();
2218
+ const autoClean = createStegaAutoClean({
2219
+ enabled: options?.autoClean
2220
+ });
2147
2221
  autoClean.start();
2148
2222
  const handleChange = createChangeHandler({
2149
2223
  store,
@@ -2192,7 +2266,7 @@ function createPreprPreview(opts = {}) {
2192
2266
  var import_react = require("react");
2193
2267
 
2194
2268
  // src/core/pixel.ts
2195
- var PIXEL_SCRIPT_URL = "https://cdn.tracking.prepr.io/js/prepr-v2.min.js";
2269
+ var PIXEL_SCRIPT_URL = "https://cdn.tracking.prepr.io/js/prepr.min.js";
2196
2270
  var CACHE_BUST_INTERVAL_MS = 24 * 60 * 60 * 1e3;
2197
2271
  function loadTrackingPixel(trackingId, config) {
2198
2272
  if (typeof window === "undefined" || typeof document === "undefined") {
@@ -1,4 +1,4 @@
1
- import { k as PreprPixelConfig, l as PreprToolbarComponentProps } from '../pixel-Dd72wuJp.cjs';
1
+ import { k as PreprPixelConfig, l as PreprToolbarComponentProps } from '../pixel-B6FtY-11.cjs';
2
2
 
3
3
  interface PreprTrackingPixelProps {
4
4
  /** Prepr tracking/access token. */
@@ -1,4 +1,4 @@
1
- import { k as PreprPixelConfig, l as PreprToolbarComponentProps } from '../pixel-Dd72wuJp.js';
1
+ import { k as PreprPixelConfig, l as PreprToolbarComponentProps } from '../pixel-B6FtY-11.js';
2
2
 
3
3
  interface PreprTrackingPixelProps {
4
4
  /** Prepr tracking/access token. */
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  createPreprPreview,
4
4
  loadTrackingPixel
5
- } from "../chunk-4PJK6IQ4.js";
5
+ } from "../chunk-HXQ6JUZM.js";
6
6
 
7
7
  // src/nextjs/components.ts
8
8
  import { usePathname, useRouter } from "next/navigation";
@@ -38,7 +38,7 @@ module.exports = __toCommonJS(nextjs_exports);
38
38
  var import_server = require("next/server");
39
39
 
40
40
  // src/version.ts
41
- var VERSION = "0.2.0";
41
+ var VERSION = "0.3.4";
42
42
 
43
43
  // src/core/constants.ts
44
44
  var COOKIE_PREVIEW_MODE = "Prepr-Preview-Mode";
@@ -64,6 +64,20 @@ interface PreprPreviewOptions extends PreprToolbarOptions {
64
64
  * Intended for self-hosted editors; when set, the wildcard no longer applies.
65
65
  */
66
66
  allowedEditorOrigins?: string[];
67
+ /**
68
+ * Strip the invisible stega characters out of visible text at runtime.
69
+ * Default `true`.
70
+ *
71
+ * Preview content carries an encoded payload inside the text itself. Left in
72
+ * place it is invisible but real: it inflates `String.length`, breaks text
73
+ * measurement and truncation, and is announced by screen readers.
74
+ *
75
+ * Set `false` only when the site already strips the characters itself — for
76
+ * example by passing every field through `stegaClean` as it renders.
77
+ * Click-to-edit is unaffected either way: elements are still tagged from the
78
+ * payload, so the overlay keeps working.
79
+ */
80
+ autoClean?: boolean;
67
81
  }
68
82
  interface PreprToolbarProps {
69
83
  /**
@@ -64,6 +64,20 @@ interface PreprPreviewOptions extends PreprToolbarOptions {
64
64
  * Intended for self-hosted editors; when set, the wildcard no longer applies.
65
65
  */
66
66
  allowedEditorOrigins?: string[];
67
+ /**
68
+ * Strip the invisible stega characters out of visible text at runtime.
69
+ * Default `true`.
70
+ *
71
+ * Preview content carries an encoded payload inside the text itself. Left in
72
+ * place it is invisible but real: it inflates `String.length`, breaks text
73
+ * measurement and truncation, and is announced by screen readers.
74
+ *
75
+ * Set `false` only when the site already strips the characters itself — for
76
+ * example by passing every field through `stegaClean` as it renders.
77
+ * Click-to-edit is unaffected either way: elements are still tagged from the
78
+ * payload, so the overlay keeps working.
79
+ */
80
+ autoClean?: boolean;
67
81
  }
68
82
  interface PreprToolbarProps {
69
83
  /**
@@ -2,7 +2,7 @@
2
2
  import { NextResponse } from "next/server";
3
3
 
4
4
  // src/version.ts
5
- var VERSION = "0.2.0";
5
+ var VERSION = "0.3.4";
6
6
 
7
7
  // src/core/constants.ts
8
8
  var COOKIE_PREVIEW_MODE = "Prepr-Preview-Mode";
@@ -35,7 +35,7 @@ __export(nuxt_exports, {
35
35
  module.exports = __toCommonJS(nuxt_exports);
36
36
 
37
37
  // src/version.ts
38
- var VERSION = "0.2.0";
38
+ var VERSION = "0.3.4";
39
39
 
40
40
  // src/core/constants.ts
41
41
  var COOKIE_PREVIEW_MODE = "Prepr-Preview-Mode";
@@ -1,5 +1,5 @@
1
1
  // src/version.ts
2
- var VERSION = "0.2.0";
2
+ var VERSION = "0.3.4";
3
3
 
4
4
  // src/core/constants.ts
5
5
  var COOKIE_PREVIEW_MODE = "Prepr-Preview-Mode";
@@ -68,6 +68,20 @@ interface PreprPreviewOptions extends PreprToolbarOptions {
68
68
  * Intended for self-hosted editors; when set, the wildcard no longer applies.
69
69
  */
70
70
  allowedEditorOrigins?: string[];
71
+ /**
72
+ * Strip the invisible stega characters out of visible text at runtime.
73
+ * Default `true`.
74
+ *
75
+ * Preview content carries an encoded payload inside the text itself. Left in
76
+ * place it is invisible but real: it inflates `String.length`, breaks text
77
+ * measurement and truncation, and is announced by screen readers.
78
+ *
79
+ * Set `false` only when the site already strips the characters itself — for
80
+ * example by passing every field through `stegaClean` as it renders.
81
+ * Click-to-edit is unaffected either way: elements are still tagged from the
82
+ * payload, so the overlay keeps working.
83
+ */
84
+ autoClean?: boolean;
71
85
  }
72
86
  interface PreprToolbarProps {
73
87
  /**
@@ -68,6 +68,20 @@ interface PreprPreviewOptions extends PreprToolbarOptions {
68
68
  * Intended for self-hosted editors; when set, the wildcard no longer applies.
69
69
  */
70
70
  allowedEditorOrigins?: string[];
71
+ /**
72
+ * Strip the invisible stega characters out of visible text at runtime.
73
+ * Default `true`.
74
+ *
75
+ * Preview content carries an encoded payload inside the text itself. Left in
76
+ * place it is invisible but real: it inflates `String.length`, breaks text
77
+ * measurement and truncation, and is announced by screen readers.
78
+ *
79
+ * Set `false` only when the site already strips the characters itself — for
80
+ * example by passing every field through `stegaClean` as it renders.
81
+ * Click-to-edit is unaffected either way: elements are still tagged from the
82
+ * payload, so the overlay keeps working.
83
+ */
84
+ autoClean?: boolean;
71
85
  }
72
86
  interface PreprToolbarProps {
73
87
  /**