@preprio/toolkit 0.2.0-beta.7 → 0.2.0

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.
@@ -369,9 +369,7 @@ function decodeStega(str) {
369
369
  const match = str.match(/{"origin.*?}/);
370
370
  if (match) {
371
371
  try {
372
- const decodedMatch = (0, import_stega.vercelStegaDecode)(
373
- match[0]
374
- );
372
+ const decodedMatch = (0, import_stega.vercelStegaDecode)(match[0]);
375
373
  if (decodedMatch?.href) {
376
374
  return decodedMatch;
377
375
  }
@@ -899,8 +897,13 @@ function createStegaController(opts) {
899
897
  overlay.hide();
900
898
  }
901
899
  }, 16);
902
- document.addEventListener("mousemove", throttledMouseMove);
903
- window.addEventListener("scroll", handleScroll, { capture: true });
900
+ document.addEventListener("mousemove", throttledMouseMove, {
901
+ passive: true
902
+ });
903
+ window.addEventListener("scroll", handleScroll, {
904
+ capture: true,
905
+ passive: true
906
+ });
904
907
  initialized = true;
905
908
  debug6.log("stega controller started");
906
909
  };
@@ -1043,7 +1046,9 @@ function createChangeHandler(deps) {
1043
1046
  }
1044
1047
  setCookie(COOKIE_PREVIEW_MODE, String(after.previewMode), cookieOpts());
1045
1048
  setCookie(COOKIE_TOOLBAR_OPEN, "false", cookieOpts());
1046
- sendPreprEvent("preview_mode_toggled", { previewMode: after.previewMode });
1049
+ sendPreprEvent("preview_mode_toggled", {
1050
+ previewMode: after.previewMode
1051
+ });
1047
1052
  if (!after.isIframe) {
1048
1053
  const [path, existing] = splitPath(deps.currentPath());
1049
1054
  const params = new URLSearchParams(existing);
@@ -1350,7 +1355,7 @@ var CLOSE_ICON_SVG = `<svg width="10" height="10" viewBox="0 0 10 10" fill="none
1350
1355
  </svg>`;
1351
1356
 
1352
1357
  // src/core/ui/toolbar.generated.ts
1353
- 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 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}}";
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}}";
1354
1359
 
1355
1360
  // ../../node_modules/.pnpm/preact@10.29.7/node_modules/preact/jsx-runtime/dist/jsxRuntime.mjs
1356
1361
  var f2 = 0;
@@ -1364,7 +1369,10 @@ function u2(e2, t3, n2, o2, i2, u3) {
1364
1369
  }
1365
1370
 
1366
1371
  // src/core/ui/panel.tsx
1367
- function RawSvg({ svg, className }) {
1372
+ function RawSvg({
1373
+ svg,
1374
+ className
1375
+ }) {
1368
1376
  return /* @__PURE__ */ u2(
1369
1377
  "span",
1370
1378
  {
@@ -1631,7 +1639,7 @@ function StatusPill({
1631
1639
  {
1632
1640
  class: "prepr-status-x",
1633
1641
  "data-prepr": "status-x",
1634
- hidden: !features.segments || !state.previewMode || state.selectedSegment === null,
1642
+ hidden: !state.previewMode || !(features.segments && state.selectedSegment !== null || features.abTesting && state.selectedVariant === "B"),
1635
1643
  children: /* @__PURE__ */ u2(RawSvg, { svg: XMARK_ICON_SVG })
1636
1644
  }
1637
1645
  )
@@ -1935,9 +1943,7 @@ var PreprToolbarElement = class extends HTMLElementBase {
1935
1943
  ) ?? null;
1936
1944
  }
1937
1945
  optionsList() {
1938
- return this.shadowRoot?.querySelector(
1939
- '[data-prepr="options"]'
1940
- ) ?? null;
1946
+ return this.shadowRoot?.querySelector('[data-prepr="options"]') ?? null;
1941
1947
  }
1942
1948
  onListboxKeydown(event) {
1943
1949
  const e2 = event;
@@ -2157,9 +2163,13 @@ function createPreprPreview(opts = {}) {
2157
2163
  const bridge = createIframeBridge(store, {
2158
2164
  allowedEditorOrigins: options?.allowedEditorOrigins
2159
2165
  });
2160
- sendPreprEvent("getScrollPosition", { value: 0 }, {
2161
- allowUntrustedTarget: true
2162
- });
2166
+ sendPreprEvent(
2167
+ "getScrollPosition",
2168
+ { value: 0 },
2169
+ {
2170
+ allowUntrustedTarget: true
2171
+ }
2172
+ );
2163
2173
  if (isIframe) {
2164
2174
  bridge.start();
2165
2175
  }
@@ -2237,13 +2247,15 @@ function PreprToolbar({
2237
2247
  }) {
2238
2248
  const router = (0, import_navigation.useRouter)();
2239
2249
  const pathname = (0, import_navigation.usePathname)();
2250
+ const pathnameRef = (0, import_react2.useRef)(pathname);
2251
+ pathnameRef.current = pathname;
2240
2252
  (0, import_react2.useEffect)(() => {
2241
2253
  const toolbar = createPreprPreview({
2242
2254
  props,
2243
2255
  options,
2244
2256
  navigation: {
2245
2257
  navigate: (url) => router.push(url),
2246
- currentPath: () => pathname + window.location.search
2258
+ currentPath: () => pathnameRef.current + window.location.search
2247
2259
  }
2248
2260
  });
2249
2261
  return () => toolbar.destroy();
@@ -2,11 +2,11 @@
2
2
  import {
3
3
  createPreprPreview,
4
4
  loadTrackingPixel
5
- } from "../chunk-P6VTLWM4.js";
5
+ } from "../chunk-4PJK6IQ4.js";
6
6
 
7
7
  // src/nextjs/components.ts
8
8
  import { usePathname, useRouter } from "next/navigation";
9
- import { useEffect as useEffect2 } from "react";
9
+ import { useEffect as useEffect2, useRef } from "react";
10
10
 
11
11
  // src/react/components.ts
12
12
  import { useEffect } from "react";
@@ -27,13 +27,15 @@ function PreprToolbar({
27
27
  }) {
28
28
  const router = useRouter();
29
29
  const pathname = usePathname();
30
+ const pathnameRef = useRef(pathname);
31
+ pathnameRef.current = pathname;
30
32
  useEffect2(() => {
31
33
  const toolbar = createPreprPreview({
32
34
  props,
33
35
  options,
34
36
  navigation: {
35
37
  navigate: (url) => router.push(url),
36
- currentPath: () => pathname + window.location.search
38
+ currentPath: () => pathnameRef.current + window.location.search
37
39
  }
38
40
  });
39
41
  return () => toolbar.destroy();
@@ -25,7 +25,10 @@ __export(image_loader_exports, {
25
25
  module.exports = __toCommonJS(image_loader_exports);
26
26
  var TRANSFORM_SEGMENT = /\/([^/]*\bw_\d+[^/]*)\//;
27
27
  var isPositiveInteger = (value) => /^\d+$/.test(value);
28
- function preprImageLoader({ src, width }) {
28
+ function preprImageLoader({
29
+ src,
30
+ width
31
+ }) {
29
32
  return src.replace(TRANSFORM_SEGMENT, (match, segment) => {
30
33
  const options = segment.split(",").map((pair) => {
31
34
  const underscore = pair.indexOf("_");
@@ -36,7 +39,9 @@ function preprImageLoader({ src, width }) {
36
39
  const emittedHeight = options.find(([key]) => key === "h");
37
40
  if (emittedHeight && isPositiveInteger(emittedHeight[1])) {
38
41
  emittedHeight[1] = String(
39
- Math.round(width * Number(emittedHeight[1]) / Number(emittedWidth[1]))
42
+ Math.round(
43
+ width * Number(emittedHeight[1]) / Number(emittedWidth[1])
44
+ )
40
45
  );
41
46
  }
42
47
  emittedWidth[1] = String(width);
@@ -9,6 +9,6 @@ import { ImageLoaderProps } from 'next/image';
9
9
  * neither of which is a ceiling on what the CDN can serve, so capping there
10
10
  * would rule out retina.
11
11
  */
12
- declare function preprImageLoader({ src, width }: ImageLoaderProps): string;
12
+ declare function preprImageLoader({ src, width, }: ImageLoaderProps): string;
13
13
 
14
14
  export { preprImageLoader as default };
@@ -9,6 +9,6 @@ import { ImageLoaderProps } from 'next/image';
9
9
  * neither of which is a ceiling on what the CDN can serve, so capping there
10
10
  * would rule out retina.
11
11
  */
12
- declare function preprImageLoader({ src, width }: ImageLoaderProps): string;
12
+ declare function preprImageLoader({ src, width, }: ImageLoaderProps): string;
13
13
 
14
14
  export { preprImageLoader as default };
@@ -1,7 +1,10 @@
1
1
  // src/nextjs/image-loader.ts
2
2
  var TRANSFORM_SEGMENT = /\/([^/]*\bw_\d+[^/]*)\//;
3
3
  var isPositiveInteger = (value) => /^\d+$/.test(value);
4
- function preprImageLoader({ src, width }) {
4
+ function preprImageLoader({
5
+ src,
6
+ width
7
+ }) {
5
8
  return src.replace(TRANSFORM_SEGMENT, (match, segment) => {
6
9
  const options = segment.split(",").map((pair) => {
7
10
  const underscore = pair.indexOf("_");
@@ -12,7 +15,9 @@ function preprImageLoader({ src, width }) {
12
15
  const emittedHeight = options.find(([key]) => key === "h");
13
16
  if (emittedHeight && isPositiveInteger(emittedHeight[1])) {
14
17
  emittedHeight[1] = String(
15
- Math.round(width * Number(emittedHeight[1]) / Number(emittedWidth[1]))
18
+ Math.round(
19
+ width * Number(emittedHeight[1]) / Number(emittedWidth[1])
20
+ )
16
21
  );
17
22
  }
18
23
  emittedWidth[1] = String(width);
@@ -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-beta.7";
41
+ var VERSION = "0.2.0";
42
42
 
43
43
  // src/core/constants.ts
44
44
  var COOKIE_PREVIEW_MODE = "Prepr-Preview-Mode";
@@ -92,7 +92,11 @@ function parseCookies(request) {
92
92
  const name = part.slice(0, eq).trim();
93
93
  const value = part.slice(eq + 1).trim();
94
94
  if (!name) continue;
95
- cookies.set(name, decodeURIComponent(value));
95
+ try {
96
+ cookies.set(name, decodeURIComponent(value));
97
+ } catch {
98
+ cookies.set(name, value);
99
+ }
96
100
  }
97
101
  return cookies;
98
102
  }
@@ -131,7 +135,10 @@ function processPreprRequest(request, opts) {
131
135
  if (userAgent) {
132
136
  requestHeaders.set("Prepr-User-Agent", sanitizeHeaderValue(userAgent));
133
137
  }
134
- requestHeaders.set("Prepr-Package", `@preprio/toolkit@${opts?.version ?? VERSION}`);
138
+ requestHeaders.set(
139
+ "Prepr-Package",
140
+ `@preprio/toolkit@${opts?.version ?? VERSION}`
141
+ );
135
142
  const ip = request.headers.get("Cf-Connecting-Ip") ?? request.headers.get("x-real-ip");
136
143
  if (ip) {
137
144
  requestHeaders.set("Prepr-Visitor-IP", sanitizeHeaderValue(ip));
@@ -140,7 +147,7 @@ function processPreprRequest(request, opts) {
140
147
  if (hutkCookie) {
141
148
  requestHeaders.set("Prepr-Hubspot-Id", sanitizeHeaderValue(hutkCookie));
142
149
  }
143
- let uid = requestCookies.get(COOKIE_UID);
150
+ let uid = sanitizeHeaderValue(requestCookies.get(COOKIE_UID) ?? "");
144
151
  if (!uid) {
145
152
  uid = crypto.randomUUID();
146
153
  responseCookies.push({
@@ -224,19 +231,30 @@ function createPreprMiddleware(request, responseOrOptions, maybeOptions) {
224
231
  } else {
225
232
  options = responseOrOptions;
226
233
  }
227
- const { requestHeaders, responseCookies } = processPreprRequest(request, options);
234
+ const { requestHeaders, responseCookies } = processPreprRequest(
235
+ request,
236
+ options
237
+ );
228
238
  const response = chainedResponse ?? import_server.NextResponse.next({ request: { headers: requestHeaders } });
229
239
  if (chainedResponse) {
240
+ const existing = response.headers.get("x-middleware-override-headers");
241
+ const names = new Set(
242
+ existing ? existing.split(",").map((name) => name.trim()).filter(Boolean) : []
243
+ );
230
244
  requestHeaders.forEach((value, key) => {
231
- response.headers.set(key, value);
245
+ names.add(key);
246
+ response.headers.set(`x-middleware-request-${key}`, value);
232
247
  });
248
+ response.headers.set("x-middleware-override-headers", [...names].join(","));
233
249
  }
234
250
  for (const cookie of responseCookies) {
235
251
  response.cookies.set(cookie.name, cookie.value, {
236
252
  maxAge: cookie.maxAge,
237
253
  path: cookie.path,
238
254
  // Lower-cased: Next's cookie API expects 'none' | 'lax' | 'strict'.
239
- ...cookie.sameSite ? { sameSite: cookie.sameSite.toLowerCase() } : {},
255
+ ...cookie.sameSite ? {
256
+ sameSite: cookie.sameSite.toLowerCase()
257
+ } : {},
240
258
  ...cookie.secure ? { secure: true } : {}
241
259
  });
242
260
  }
@@ -296,7 +314,11 @@ function getPreprHeadersFromHeaders(headers2) {
296
314
  }
297
315
  function validatePreprToken(token) {
298
316
  if (!token) {
299
- throw new PreprError("Token is required", "MISSING_TOKEN", "validatePreprToken");
317
+ throw new PreprError(
318
+ "Token is required",
319
+ "MISSING_TOKEN",
320
+ "validatePreprToken"
321
+ );
300
322
  }
301
323
  if (!token.startsWith("https://")) {
302
324
  throw new PreprError(
@@ -426,7 +448,7 @@ async function getToolbarProps(token, features) {
426
448
  }
427
449
 
428
450
  // src/nextjs/index.ts
429
- var import_components = require("./components");
451
+ var import_components = require("./components.cjs");
430
452
  // Annotate the CommonJS export names for ESM import in node:
431
453
  0 && (module.exports = {
432
454
  PreprError,
@@ -1,5 +1,11 @@
1
1
  import { NextRequest, NextResponse } from 'next/server';
2
- export { PreprToolbar, PreprTrackingPixel, PreprTrackingPixelProps } from './components';
2
+
3
+ type Dictionary = Record<string, unknown>;
4
+ declare const dictionaries: {
5
+ en: Dictionary;
6
+ nl: Dictionary;
7
+ };
8
+ type Locale = keyof typeof dictionaries;
3
9
 
4
10
  interface PreprSegment {
5
11
  readonly _id: string;
@@ -29,6 +35,36 @@ interface PreprFeatures {
29
35
  */
30
36
  editMode?: PreprFeatureConfig;
31
37
  }
38
+ interface PreprToolbarOptions {
39
+ debug?: boolean;
40
+ locale?: Locale;
41
+ /** Disable features app-wide. Pass the same object to the middleware. */
42
+ features?: PreprFeatures;
43
+ }
44
+ /**
45
+ * Options for the preview runtime. Two independent axes: `features` decides
46
+ * *what runs*, `ui` decides *whether the toolbar is visible*.
47
+ */
48
+ interface PreprPreviewOptions extends PreprToolbarOptions {
49
+ /**
50
+ * Mount the visible `<prepr-toolbar>` element. Default `true`.
51
+ *
52
+ * `false` keeps every non-visual side effect wired — click-to-edit, the
53
+ * editor bridge, scroll restore, cookies and headers — with no chrome of its
54
+ * own. That is how a site gets live editing, or editor scroll restore, while
55
+ * rendering its own UI instead of the bar.
56
+ *
57
+ * Already implied inside the editor's iframe (the CMS owns the chrome there)
58
+ * and by `?prepr_hide_bar=true`, so this option is for consumers who want a
59
+ * headless preview by their own choice.
60
+ */
61
+ ui?: boolean;
62
+ /**
63
+ * Replace the `*.prepr.io` editor-origin wildcard with an exact list.
64
+ * Intended for self-hosted editors; when set, the wildcard no longer applies.
65
+ */
66
+ allowedEditorOrigins?: string[];
67
+ }
32
68
  interface PreprToolbarProps {
33
69
  /**
34
70
  * Server-resolved active segment. Optional: omit it with `segments`
@@ -47,6 +83,13 @@ interface PreprToolbarProps {
47
83
  /** @deprecated Alias for `segments`. */
48
84
  readonly data?: readonly PreprSegment[];
49
85
  }
86
+ /**
87
+ * Prop type for the framework `<PreprToolbar>` components: the toolbar data
88
+ * plus the options bag core `createPreprPreview` accepts.
89
+ */
90
+ interface PreprToolbarComponentProps extends PreprToolbarProps {
91
+ options?: PreprPreviewOptions;
92
+ }
50
93
  type PreprVariant = 'A' | 'B';
51
94
  interface PreprHeaders {
52
95
  readonly 'prepr-customer-id'?: string;
@@ -127,4 +170,58 @@ declare function validatePreprToken(token: string): void;
127
170
  */
128
171
  declare function extractAccessToken(graphqlUrl: string): string;
129
172
 
130
- export { PreprError, type PreprMiddlewareOptions, createPreprMiddleware, extractAccessToken, getActiveSegment, getActiveVariant, getPreprHeaders, getPreprUUID, getToolbarProps, validatePreprToken };
173
+ /**
174
+ * Loader + typed facade for Prepr's CDN tracking pixel, so consumers don't have
175
+ * to paste a `<script>` tag. The pixel itself lives on the CDN; this only sets
176
+ * up the handoff.
177
+ *
178
+ * The CDN script expects a queue stub to already exist when it loads, so the
179
+ * contract must be reproduced exactly:
180
+ * - `window.prepr` pushes `arguments` onto `.queue` until the real script
181
+ * lands and replaces `.process`.
182
+ * - `.t` must be a `Date.now()` timestamp.
183
+ * - The tag is inserted async, `src` cache-busted every 24h via
184
+ * `Math.ceil(Date.now() / cacheTime) * cacheTime`.
185
+ */
186
+ /** Config accepted by the CDN pixel. */
187
+ interface PreprPixelConfig {
188
+ destinations?: {
189
+ googleTagManager?: boolean;
190
+ };
191
+ variantImpressionThreshold?: number;
192
+ }
193
+ type PreprQueueArgs = unknown[];
194
+ interface PreprQueueFn {
195
+ (...args: PreprQueueArgs): void;
196
+ queue: PreprQueueArgs[];
197
+ t: number;
198
+ process?: (...args: PreprQueueArgs) => void;
199
+ }
200
+ declare global {
201
+ interface Window {
202
+ prepr?: PreprQueueFn;
203
+ }
204
+ }
205
+
206
+ interface PreprTrackingPixelProps {
207
+ /** Prepr tracking/access token. */
208
+ id: string;
209
+ config?: PreprPixelConfig;
210
+ }
211
+ /**
212
+ * Loads Prepr's CDN tracking pixel on mount. Renders nothing.
213
+ *
214
+ * The pixel queues a single `pageload` event when it installs. Client-side
215
+ * route changes emit nothing on their own — call `trackEvent('pageload')` from
216
+ * the router to count them.
217
+ */
218
+ declare function PreprTrackingPixel({ id, config, }: PreprTrackingPixelProps): null;
219
+
220
+ /**
221
+ * Mounts the Prepr toolbar and wires it to the Next.js router so segment/variant
222
+ * switches navigate. Renders nothing — the toolbar UI is a custom element that
223
+ * `createPreprPreview` mounts imperatively.
224
+ */
225
+ declare function PreprToolbar({ options, ...props }: PreprToolbarComponentProps): null;
226
+
227
+ export { PreprError, type PreprMiddlewareOptions, PreprToolbar, PreprTrackingPixel, type PreprTrackingPixelProps, createPreprMiddleware, extractAccessToken, getActiveSegment, getActiveVariant, getPreprHeaders, getPreprUUID, getToolbarProps, validatePreprToken };
@@ -1,5 +1,11 @@
1
1
  import { NextRequest, NextResponse } from 'next/server';
2
- export { PreprToolbar, PreprTrackingPixel, PreprTrackingPixelProps } from './components';
2
+
3
+ type Dictionary = Record<string, unknown>;
4
+ declare const dictionaries: {
5
+ en: Dictionary;
6
+ nl: Dictionary;
7
+ };
8
+ type Locale = keyof typeof dictionaries;
3
9
 
4
10
  interface PreprSegment {
5
11
  readonly _id: string;
@@ -29,6 +35,36 @@ interface PreprFeatures {
29
35
  */
30
36
  editMode?: PreprFeatureConfig;
31
37
  }
38
+ interface PreprToolbarOptions {
39
+ debug?: boolean;
40
+ locale?: Locale;
41
+ /** Disable features app-wide. Pass the same object to the middleware. */
42
+ features?: PreprFeatures;
43
+ }
44
+ /**
45
+ * Options for the preview runtime. Two independent axes: `features` decides
46
+ * *what runs*, `ui` decides *whether the toolbar is visible*.
47
+ */
48
+ interface PreprPreviewOptions extends PreprToolbarOptions {
49
+ /**
50
+ * Mount the visible `<prepr-toolbar>` element. Default `true`.
51
+ *
52
+ * `false` keeps every non-visual side effect wired — click-to-edit, the
53
+ * editor bridge, scroll restore, cookies and headers — with no chrome of its
54
+ * own. That is how a site gets live editing, or editor scroll restore, while
55
+ * rendering its own UI instead of the bar.
56
+ *
57
+ * Already implied inside the editor's iframe (the CMS owns the chrome there)
58
+ * and by `?prepr_hide_bar=true`, so this option is for consumers who want a
59
+ * headless preview by their own choice.
60
+ */
61
+ ui?: boolean;
62
+ /**
63
+ * Replace the `*.prepr.io` editor-origin wildcard with an exact list.
64
+ * Intended for self-hosted editors; when set, the wildcard no longer applies.
65
+ */
66
+ allowedEditorOrigins?: string[];
67
+ }
32
68
  interface PreprToolbarProps {
33
69
  /**
34
70
  * Server-resolved active segment. Optional: omit it with `segments`
@@ -47,6 +83,13 @@ interface PreprToolbarProps {
47
83
  /** @deprecated Alias for `segments`. */
48
84
  readonly data?: readonly PreprSegment[];
49
85
  }
86
+ /**
87
+ * Prop type for the framework `<PreprToolbar>` components: the toolbar data
88
+ * plus the options bag core `createPreprPreview` accepts.
89
+ */
90
+ interface PreprToolbarComponentProps extends PreprToolbarProps {
91
+ options?: PreprPreviewOptions;
92
+ }
50
93
  type PreprVariant = 'A' | 'B';
51
94
  interface PreprHeaders {
52
95
  readonly 'prepr-customer-id'?: string;
@@ -127,4 +170,58 @@ declare function validatePreprToken(token: string): void;
127
170
  */
128
171
  declare function extractAccessToken(graphqlUrl: string): string;
129
172
 
130
- export { PreprError, type PreprMiddlewareOptions, createPreprMiddleware, extractAccessToken, getActiveSegment, getActiveVariant, getPreprHeaders, getPreprUUID, getToolbarProps, validatePreprToken };
173
+ /**
174
+ * Loader + typed facade for Prepr's CDN tracking pixel, so consumers don't have
175
+ * to paste a `<script>` tag. The pixel itself lives on the CDN; this only sets
176
+ * up the handoff.
177
+ *
178
+ * The CDN script expects a queue stub to already exist when it loads, so the
179
+ * contract must be reproduced exactly:
180
+ * - `window.prepr` pushes `arguments` onto `.queue` until the real script
181
+ * lands and replaces `.process`.
182
+ * - `.t` must be a `Date.now()` timestamp.
183
+ * - The tag is inserted async, `src` cache-busted every 24h via
184
+ * `Math.ceil(Date.now() / cacheTime) * cacheTime`.
185
+ */
186
+ /** Config accepted by the CDN pixel. */
187
+ interface PreprPixelConfig {
188
+ destinations?: {
189
+ googleTagManager?: boolean;
190
+ };
191
+ variantImpressionThreshold?: number;
192
+ }
193
+ type PreprQueueArgs = unknown[];
194
+ interface PreprQueueFn {
195
+ (...args: PreprQueueArgs): void;
196
+ queue: PreprQueueArgs[];
197
+ t: number;
198
+ process?: (...args: PreprQueueArgs) => void;
199
+ }
200
+ declare global {
201
+ interface Window {
202
+ prepr?: PreprQueueFn;
203
+ }
204
+ }
205
+
206
+ interface PreprTrackingPixelProps {
207
+ /** Prepr tracking/access token. */
208
+ id: string;
209
+ config?: PreprPixelConfig;
210
+ }
211
+ /**
212
+ * Loads Prepr's CDN tracking pixel on mount. Renders nothing.
213
+ *
214
+ * The pixel queues a single `pageload` event when it installs. Client-side
215
+ * route changes emit nothing on their own — call `trackEvent('pageload')` from
216
+ * the router to count them.
217
+ */
218
+ declare function PreprTrackingPixel({ id, config, }: PreprTrackingPixelProps): null;
219
+
220
+ /**
221
+ * Mounts the Prepr toolbar and wires it to the Next.js router so segment/variant
222
+ * switches navigate. Renders nothing — the toolbar UI is a custom element that
223
+ * `createPreprPreview` mounts imperatively.
224
+ */
225
+ declare function PreprToolbar({ options, ...props }: PreprToolbarComponentProps): null;
226
+
227
+ export { PreprError, type PreprMiddlewareOptions, PreprToolbar, PreprTrackingPixel, type PreprTrackingPixelProps, createPreprMiddleware, extractAccessToken, getActiveSegment, getActiveVariant, getPreprHeaders, getPreprUUID, getToolbarProps, validatePreprToken };