@accelerated-agency/visual-editor 0.4.4 → 0.4.6

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.
Files changed (3) hide show
  1. package/dist/vite.cjs +491 -24
  2. package/dist/vite.js +491 -24
  3. package/package.json +1 -1
package/dist/vite.js CHANGED
@@ -138,7 +138,9 @@ async function getScraperProxyClient() {
138
138
  })();
139
139
  return scraperProxyClientPromise;
140
140
  }
141
- var iframeAlwaysShowCss = `<style id="__ce_force_show">#CybotCookiebotDialog,#CybotCookiebotDialogBodyUnderlay,#onetrust-consent-sdk,#onetrust-banner-sdk,.cc-window,.cc-banner,.cc-overlay,#cookie-notice,#cookie-banner,#cookie-consent,.cookie-notice,.cookie-banner,.cookie-consent,.cookie-popup,.cookie-bar,.cookie-message,.cookie-alert,.gdpr-banner,.gdpr-consent,.gdpr-popup,.gdpr-overlay,#gdpr-consent,#gdpr-banner,.consent-banner,.consent-popup,.consent-overlay,#consent-banner,#consent-popup,[class*="cookie-consent"],[class*="cookie-banner"],[class*="cookie-notice"],[class*="CookieConsent"],[class*="CookieBanner"],[id*="cookie-consent"],[id*="cookie-banner"],[id*="cookie-notice"],[aria-label*="cookie" i],[aria-label*="consent" i],.klaro,.klaro .cookie-modal,#usercentrics-root,.trustarc-banner,#truste-consent-track,#hs-eu-cookie-confirmation,.osano-cm-window,.osano-cm-dialog,.evidon-banner,#_evidon_banner,.js-cookie-consent,.cookie-disclaimer,.shopify-section-cookies,#shopify-section-cookies,#shopify-pc__banner,#shopify-pc__modal,.privacy-banner,.privacy-popup,[data-testid="cookie-banner"],[data-testid="consent-banner"],.amgdprcookie-bar-container,[data-amcookie-js="bar"],.amgdprjs-bar-template,.amgdprcookie-modal-container,.amgdprcookie-modal-overlay,#cmplz-cookiebanner-container,.cmplz-cookiebanner,#iubenda-cs-banner,.iubenda-cs-container,#qc-cmp2-container,.qc-cmp2-consent-info,#didomi-host,.didomi-popup-container,.didomi-notice,#termly-code-snippet-support,[class*="termly"],[class*="gdprcookie"],[class*="amgdpr"],[id*="gdpr-cookie"],[class*="cookie-modal"],[id*="cookie-modal"],[class*="cookieConsent"],[id*="cookieConsent"]{display:revert!important;visibility:visible!important;opacity:1!important;pointer-events:auto!important;height:auto!important;max-height:none!important;overflow:visible!important;}</style>`;
141
+ var iframeAlwaysShowCss = `<style id="__ce_force_show">
142
+
143
+ </style>`;
142
144
  var iframeAlwaysShowCssGuardScript = `<script id="__ce_force_show_guard">(function(){try{
143
145
  function ensureForceShowStyleLast(){
144
146
  var style=document.getElementById("__ce_force_show");
@@ -272,8 +274,97 @@ html,body{height:100%;overflow:hidden;font-family:-apple-system,BlinkMacSystemFo
272
274
  .tb-viewport #dev-label{font-size:14px;font-weight:500;color:##404040;min-width:auto;background:none;border:none;padding:0;overflow:visible;white-space:nowrap;text-overflow:clip;border-radius:0;max-width:none}
273
275
  .tb-viewport i{font-size:9px;color:##404040}
274
276
  /* Device toggle buttons */
277
+ .tb-dev-wrap{position:relative;display:flex;align-items:center}
275
278
  .tb-dev-btns{display:flex;align-items:center;gap:1px;padding:8px 4px;background:#F0F0F0;border-radius:7px; height: 30px;width: 116px;}
276
279
  .tb-dev-3btns{display:flex;align-items:center;gap:2px;padding:8px 4px;background:#F0F0F0;border-radius:7px; height: 30px;width: 100px;}
280
+ .tb-dev-menu{
281
+ position:absolute;top:calc(100% + 8px);right:0;z-index:12040;display:none;
282
+ width:264px;background:#fff;border:1px solid #e5e7eb;border-radius:8px;padding:4px 4px;
283
+ box-shadow:0 12px 28px rgba(2,6,23,.18);color:#27272a
284
+ }
285
+ .tb-dev-menu.open{display:block}
286
+ .tb-dev-menu .hd{font-size:12px;font-weight:600;color:#3f3f46;margin-bottom:10px}
287
+ .tb-dev-menu .row{display:flex;align-items:center;gap:8px;}
288
+ .tb-dev-menu .row.height-width-row .row{
289
+ flex-direction: column;
290
+ align-items: flex-start;
291
+ }
292
+ .tb-dev-menu .row.height-width-row label{
293
+ color: var(--content-subtle, #737373);
294
+ font-size: 12px;
295
+ font-style: normal;
296
+ font-weight: 500;
297
+ line-height: 14px; /* 116.667% */
298
+ }
299
+ .tb-dev-menu > .row{
300
+ padding: 4px 12px;
301
+ }
302
+ .tb-dev-menu label{ flex-shrink: 0;
303
+ overflow: hidden;
304
+ color: var(--base-surface, #646465);
305
+ font-size: var(--font-size-sm, 14px);
306
+ font-style: normal;
307
+ font-weight: 500;
308
+ line-height: var(--font-leading-4, 16px);
309
+ white-space: nowrap;
310
+ min-width: fit-content;}
311
+ .tb-dev-menu input{
312
+ box-shadow: 0 0 1px 0 rgba(0, 0, 0, 0.20), 0 1px 2px 0 rgba(0, 0, 0, 0.05), 0 1px 1px 0 rgba(0, 0, 0, 0.01);
313
+ width:100%;height:30px;border-radius:6px;
314
+ padding:0 8px;
315
+ font-size:12px;
316
+ color:#18181b;
317
+ background:#fff;
318
+ outline:none;
319
+ color:#404040;
320
+ border-color:transparent;
321
+ }
322
+ .tb-dev-menu input#dev-zoom-level{
323
+ max-width: fit-content;
324
+ margin-left: auto;
325
+ }
326
+ .tb-dev-menu input:focus{
327
+ border-color:#1A1A1A;
328
+ box-shadow:0 0 0 2px rgba(99,102,241,.14)
329
+ }
330
+ .tb-dev-menu .row-split > *{flex:1}
331
+ .tb-dev-menu .panel-toggle label{width:100%}
332
+ .tb-dev-menu .panel-toggle-label{
333
+ overflow:hidden;
334
+ color:var(--content-subtle, #737373);
335
+ text-overflow:ellipsis;
336
+ font-size:var(--font-size-sm, 14px);
337
+ font-style:normal;
338
+ font-weight:500;
339
+ line-height:var(--font-leading-4, 16px);
340
+ white-space:nowrap;
341
+ cursor:pointer;
342
+ }
343
+ .tb-dev-menu .vp-presets{
344
+ margin-top:8px;padding-top:8px;border-top:1px dashed #ececf0;
345
+ display:flex;flex-direction:column;gap:2px
346
+ }
347
+ .tb-dev-menu .vp-preset-btn{
348
+ border:none;background:transparent;text-align:left;cursor:pointer;
349
+ border-radius:6px;
350
+ padding:8px 12px;
351
+ color: var(--content-subtle, #737373);
352
+ text-overflow: ellipsis;
353
+ font-size: var(--font-size-sm, 14px);
354
+ font-style: normal;
355
+ font-weight: 500;
356
+ line-height: var(--font-leading-4, 16px);
357
+ }
358
+ .tb-dev-menu .vp-preset-btn:hover,.tb-dev-menu .vp-preset-btn.active{background:#f4f4f5}
359
+ .tb-dev-menu .ft{
360
+ margin-top:10px;padding-top:8px;border-top:1px solid #ececf0;
361
+ display:flex;justify-content:flex-end
362
+ }
363
+ .tb-dev-menu .apply-btn{
364
+ border:1px solid #d4d4d8;background:#f8fafc;color:#111827;border-radius:6px;
365
+ height:30px;padding:0 10px;font-size:12px;font-weight:600;cursor:pointer
366
+ }
367
+ .tb-dev-menu .apply-btn:hover{background:#eef2ff;border-color:#a5b4fc}
277
368
  /* Dark icon buttons */
278
369
  .tb-dk-btn{width:28px;height:28px;background:transparent;border:none;border-radius:5px;cursor:pointer;color:#71717a;display:flex;align-items:center;justify-content:center;font-size:13px;transition:all .12s;flex-shrink:0}
279
370
  .tb-dk-btn:hover{color:#e4e4e7;background:rgba(255,255,255,.07)}
@@ -408,7 +499,10 @@ html,body{height:100%;overflow:hidden;font-family:-apple-system,BlinkMacSystemFo
408
499
  .dt-ico{width:16px;flex-shrink:0;text-align:center;color:var(--text-3);font-size:12px}
409
500
  .dt-lbl{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-family:ui-monospace,SFMono-Regular,monospace;font-size:10px}
410
501
  .dt-muted{padding:16px 12px;text-align:center;color:var(--text-3);font-size:11px;line-height:1.4}
411
-
502
+ #section-components-panel{
503
+ max-height: 50%;
504
+ overflow-y: auto;
505
+ }
412
506
  /* \u2500\u2500 Right panel \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
413
507
  #right-panel{
414
508
  width:252px;background:var(--bg);border-left:1px solid var(--border);
@@ -432,7 +526,12 @@ html,body{height:100%;overflow:hidden;font-family:-apple-system,BlinkMacSystemFo
432
526
  /* \u2500\u2500 Device frame \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
433
527
  #device-frame{
434
528
  width:100%;min-height:100%;display:flex;align-items:stretch;
435
- justify-content:center;transition:max-width .3s ease
529
+ justify-content:center;transform-origin:top center;
530
+ transition:max-width .3s ease,width .2s ease,height .2s ease
531
+ }
532
+ #device-frame.desktop{
533
+ max-width:1440px;
534
+ box-shadow:0 0 0 1px var(--border),0 4px 24px rgba(0,0,0,.08)
436
535
  }
437
536
  #device-frame.tablet{
438
537
  max-width:768px;
@@ -532,6 +631,17 @@ html,body{height:100%;overflow:hidden;font-family:-apple-system,BlinkMacSystemFo
532
631
  flex:1;padding:8px 2px;text-align:center;font-size:10px;color:var(--text-3);
533
632
  cursor:pointer;border-bottom:2px solid transparent;transition:all .15s;font-weight:600;line-height:1.15
534
633
  }
634
+ .lp-tab.close-section-components-panel{
635
+ padding: 4px 10px;
636
+ background: #000;
637
+ display: flex;
638
+ max-width: fit-content;
639
+ align-items: center;
640
+ justify-content: center;
641
+ border-radius: 2px;
642
+ margin: 2px;
643
+ color: #fff;
644
+ }
535
645
  .lp-tab:hover{color:var(--text-2)}
536
646
  .lp-tab.active{color:var(--accent-txt);border-bottom-color:var(--accent)}
537
647
  .future-hidden{display:none!important}
@@ -833,10 +943,8 @@ select.pr-inp{cursor:pointer;background:#fff}
833
943
  </head>
834
944
  <body class="mode-editor">
835
945
  <div id="app">
836
-
837
946
  <!-- \u2500\u2500 Toolbar \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->
838
947
  <div id="toolbar">
839
-
840
948
  <!-- Left: Logo + Breadcrumb -->
841
949
  <div class="tb-left">
842
950
  <span class="tb-logo">\u2733</span>
@@ -854,11 +962,45 @@ select.pr-inp{cursor:pointer;background:#fff}
854
962
  <span id="dev-label">1440px</span>
855
963
  <i class="bi bi-chevron-down"></i>
856
964
  </div>
857
- <div class="tb-dev-btns">
858
- <button class="tb-dk-btn active" id="dev-desktop" onclick="setDevice('desktop')" title="Desktop \u2014 full width"><i class="bi bi-display"></i></button>
859
- <button class="tb-dk-btn" id="dev-tablet" onclick="setDevice('tablet')" title="Tablet \u2014 768px"><i class="bi bi-tablet"></i></button>
860
- <button class="tb-dk-btn" id="dev-mobile" onclick="setDevice('mobile')" title="Mobile \u2014 390px"><i class="bi bi-phone"></i></button>
861
- <button class="tb-dk-btn" title="More options"><i class="bi bi-three-dots"></i></button>
965
+ <div class="tb-dev-wrap">
966
+ <div class="tb-dev-btns">
967
+ <button class="tb-dk-btn active" id="dev-desktop" onclick="setDevice('desktop')" title="Desktop \u2014 1440x1024"><i class="bi bi-display"></i></button>
968
+ <button class="tb-dk-btn" id="dev-tablet" onclick="setDevice('tablet')" title="Tablet \u2014 768x1024"><i class="bi bi-tablet"></i></button>
969
+ <button class="tb-dk-btn" id="dev-mobile" onclick="setDevice('mobile')" title="Mobile \u2014 390x844"><i class="bi bi-phone"></i></button>
970
+ <button class="tb-dk-btn" id="dev-more-btn" title="More options"><i class="bi bi-three-dots"></i></button>
971
+ </div>
972
+ <div id="dev-more-menu" class="tb-dev-menu" aria-hidden="true">
973
+ <div class="row row-zoom">
974
+ <label for="dev-zoom-level">Zoom</label>
975
+ <input id="dev-zoom-level" type="number" min="25" max="200" step="5" value="100" />
976
+ </div>
977
+ <div class="row row-split row-width height-width-row">
978
+ <div class="row" style="margin:0">
979
+ <label for="dev-custom-width">Width</label>
980
+ <input id="dev-custom-width" type="number" min="240" max="3840" step="1" value="1440" />
981
+ </div>
982
+ <div class="row row-height" style="margin:0">
983
+ <label for="dev-custom-height">Height</label>
984
+ <input id="dev-custom-height" type="number" min="320" max="3840" step="1" value="1024" />
985
+ </div>
986
+ </div>
987
+ <div class="row panel-toggle">
988
+ <label id="left-panel-toggle-label" class="panel-toggle-label">Collapse left panel</label>
989
+ </div>
990
+
991
+ <div class="vp-presets" id="dev-preset-list" aria-label="Device presets">
992
+ <button type="button" class="vp-preset-btn" data-preset="desktop">Desktop</button>
993
+ <button type="button" class="vp-preset-btn" data-preset="mobile">iPhone 13</button>
994
+ <button type="button" class="vp-preset-btn" data-preset="galaxy-s22">Galaxy S22</button>
995
+ <button type="button" class="vp-preset-btn" data-preset="tablet">iPad</button>
996
+ <button type="button" class="vp-preset-btn" data-preset="galaxy-j1">Galaxy J1</button>
997
+ <button type="button" class="vp-preset-btn" data-preset="iphone-7">iPhone 7</button>
998
+ <button type="button" class="vp-preset-btn" data-preset="responsive">Responsive</button>
999
+ </div>
1000
+ <div class="ft">
1001
+ <button type="button" class="apply-btn" id="dev-apply-btn">Apply</button>
1002
+ </div>
1003
+ </div>
862
1004
  </div>
863
1005
  <button class="tb-dk-btn" id="btn-undo" title="Undo (\u2318Z)"><i class="bi bi-arrow-counterclockwise"></i></button>
864
1006
  <button class="tb-dk-btn" id="btn-redo" title="Redo (\u2318\u21E7Z)" style="display:none"><i class="bi bi-arrow-clockwise"></i></button>
@@ -995,7 +1137,7 @@ select.pr-inp{cursor:pointer;background:#fff}
995
1137
  </div>
996
1138
 
997
1139
  <!-- Device frame containing the editing iframe -->
998
- <div id="device-frame">
1140
+ <div id="device-frame" class="desktop">
999
1141
  <iframe id="iframeId" name="iframeId" allowfullscreen></iframe>
1000
1142
  </div>
1001
1143
 
@@ -1008,6 +1150,7 @@ select.pr-inp{cursor:pointer;background:#fff}
1008
1150
  <div class="section-components-tabs">
1009
1151
  <div class="lp-tab" onclick="switchSectionComponentsTab('components')">Components</div>
1010
1152
  <div class="lp-tab" onclick="switchSectionComponentsTab('sections')">Sections</div>
1153
+ <div class="lp-tab close-section-components-panel" onclick="toggleSectionComponentsPanel()"> Close</div>
1011
1154
  </div>
1012
1155
  <div class="lp-body">
1013
1156
  <div id="tab-components" class="tab-pane"></div>
@@ -1365,6 +1508,11 @@ var isDirty = false;
1365
1508
  var vvvebReady = false;
1366
1509
  var currentMode = 'editor';
1367
1510
  var currentDevice = 'desktop';
1511
+ var leftPanelCollapsed = false;
1512
+ var viewportPreset = 'desktop';
1513
+ var viewportWidth = 1440;
1514
+ var viewportHeight = 1024;
1515
+ var viewportZoom = 1;
1368
1516
  var selectedEl = null;
1369
1517
  /** Stable selector fingerprint for resilient selection recovery after DOM churn. */
1370
1518
  var selectedElFingerprint = '';
@@ -1617,15 +1765,202 @@ function setMode(mode) {
1617
1765
  }
1618
1766
 
1619
1767
  // \u2500\u2500 Device toggle \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
1620
- var DEVICE_LABELS = { desktop: '1440px', tablet: '768px', mobile: '390px' };
1768
+ var DEVICE_PRESETS = {
1769
+ desktop: { label: 'Desktop', width: 1440, height: 1024, device: 'desktop' },
1770
+ tablet: { label: 'iPad', width: 768, height: 1024, device: 'tablet' },
1771
+ mobile: { label: 'iPhone 13', width: 390, height: 844, device: 'mobile' },
1772
+ 'galaxy-s22': { label: 'Galaxy S22', width: 360, height: 780, device: 'mobile' },
1773
+ 'iphone-7': { label: 'iPhone 7', width: 375, height: 667, device: 'mobile' },
1774
+ 'galaxy-j1': { label: 'Galaxy J1', width: 360, height: 640, device: 'mobile' },
1775
+ responsive: { label: 'Responsive', width: 1280, height: 800, device: 'desktop' },
1776
+ };
1777
+
1778
+ function clampViewportNumber(v, fallback, min, max) {
1779
+ var n = parseInt(v, 10);
1780
+ if (!Number.isFinite(n)) return fallback;
1781
+ return Math.max(min, Math.min(max, n));
1782
+ }
1783
+
1784
+ function getViewportFitZoom() {
1785
+ var panel = document.getElementById('iframe-panel');
1786
+ var available = panel ? Math.max(260, panel.clientWidth - 24) : viewportWidth;
1787
+ if (!viewportWidth || viewportWidth <= 0) return 1;
1788
+ return Math.min(1, available / viewportWidth);
1789
+ }
1790
+
1791
+ function getAppliedViewportZoom() {
1792
+ var z = Number(viewportZoom);
1793
+ if (!Number.isFinite(z) || z <= 0) z = 1;
1794
+ z = Math.max(0.25, Math.min(2, z));
1795
+ return Math.min(z, getViewportFitZoom());
1796
+ }
1797
+
1798
+ function updateViewportLabel() {
1799
+ var lbl = document.getElementById('dev-label');
1800
+ if (!lbl) return;
1801
+ var zoomPct = Math.round(getAppliedViewportZoom() * 100);
1802
+ lbl.textContent = viewportWidth + 'x' + viewportHeight + ' \xB7 ' + zoomPct + '%';
1803
+ }
1804
+
1805
+ function syncViewportMenuControls() {
1806
+ var presetButtons = document.querySelectorAll('#dev-preset-list .vp-preset-btn');
1807
+ var widthInp = document.getElementById('dev-custom-width');
1808
+ var heightInp = document.getElementById('dev-custom-height');
1809
+ var zoomInp = document.getElementById('dev-zoom-level');
1810
+ if (presetButtons && presetButtons.length) {
1811
+ for (var i = 0; i < presetButtons.length; i++) {
1812
+ var key = presetButtons[i].getAttribute('data-preset') || '';
1813
+ presetButtons[i].classList.toggle('active', key === viewportPreset);
1814
+ }
1815
+ }
1816
+ if (widthInp) widthInp.value = String(viewportWidth);
1817
+ if (heightInp) heightInp.value = String(viewportHeight);
1818
+ if (zoomInp) zoomInp.value = String(Math.round(getAppliedViewportZoom() * 100));
1819
+ }
1820
+
1821
+ function applyViewportFrame() {
1822
+ var frame = document.getElementById('device-frame');
1823
+ var iframe = document.getElementById('iframeId');
1824
+ if (!frame) return;
1825
+ frame.className = currentDevice;
1826
+ frame.style.width = viewportWidth + 'px';
1827
+ frame.style.height = viewportHeight + 'px';
1828
+ frame.style.maxWidth = 'none';
1829
+ frame.style.zoom = String(getAppliedViewportZoom());
1830
+ if (iframe) {
1831
+ iframe.style.height = viewportHeight + 'px';
1832
+ iframe.style.minHeight = viewportHeight + 'px';
1833
+ }
1834
+ updateViewportLabel();
1835
+ syncViewportMenuControls();
1836
+ if (selectedEl && currentMode === 'editor') requestAnimationFrame(function() { positionSelectionToolbar(); });
1837
+ }
1838
+
1839
+ function setViewportPreset(presetKey) {
1840
+ var preset = DEVICE_PRESETS[presetKey];
1841
+ if (!preset) return;
1842
+ viewportPreset = presetKey;
1843
+ viewportWidth = preset.width;
1844
+ viewportHeight = preset.height;
1845
+ currentDevice = preset.device || 'desktop';
1846
+ ['desktop','tablet','mobile'].forEach(function(d) {
1847
+ document.getElementById('dev-' + d).classList.toggle('active', d === currentDevice);
1848
+ });
1849
+ applyViewportFrame();
1850
+ }
1851
+
1621
1852
  function setDevice(device) {
1853
+ viewportPreset = device;
1622
1854
  currentDevice = device;
1623
- var frame = document.getElementById('device-frame');
1624
- frame.className = device === 'desktop' ? '' : device;
1855
+ var preset = DEVICE_PRESETS[device] || DEVICE_PRESETS.desktop;
1856
+ viewportWidth = preset.width;
1857
+ viewportHeight = preset.height;
1625
1858
  ['desktop','tablet','mobile'].forEach(function(d) {
1626
1859
  document.getElementById('dev-' + d).classList.toggle('active', d === device);
1627
1860
  });
1628
- document.getElementById('dev-label').textContent = DEVICE_LABELS[device] || device;
1861
+ applyViewportFrame();
1862
+ }
1863
+
1864
+ function setViewportCustomFromInputs() {
1865
+ var widthInp = document.getElementById('dev-custom-width');
1866
+ var heightInp = document.getElementById('dev-custom-height');
1867
+ var zoomInp = document.getElementById('dev-zoom-level');
1868
+ viewportWidth = clampViewportNumber(widthInp ? widthInp.value : '', viewportWidth, 240, 3840);
1869
+ viewportHeight = clampViewportNumber(heightInp ? heightInp.value : '', viewportHeight, 320, 3840);
1870
+ var z = clampViewportNumber(zoomInp ? zoomInp.value : '', Math.round(getAppliedViewportZoom() * 100), 25, 200);
1871
+ viewportZoom = z / 100;
1872
+ viewportPreset = 'custom';
1873
+ currentDevice = viewportWidth <= 480 ? 'mobile' : (viewportWidth <= 1024 ? 'tablet' : 'desktop');
1874
+ ['desktop','tablet','mobile'].forEach(function(d) {
1875
+ document.getElementById('dev-' + d).classList.toggle('active', d === currentDevice);
1876
+ });
1877
+ applyViewportFrame();
1878
+ }
1879
+
1880
+ function closeViewportMenu() {
1881
+ var menu = document.getElementById('dev-more-menu');
1882
+ if (!menu) return;
1883
+ menu.classList.remove('open');
1884
+ menu.setAttribute('aria-hidden', 'true');
1885
+ }
1886
+
1887
+ function toggleViewportMenu() {
1888
+ var menu = document.getElementById('dev-more-menu');
1889
+ if (!menu) return;
1890
+ var shouldOpen = !menu.classList.contains('open');
1891
+ menu.classList.toggle('open', shouldOpen);
1892
+ menu.setAttribute('aria-hidden', shouldOpen ? 'false' : 'true');
1893
+ }
1894
+
1895
+ function bindViewportControls() {
1896
+ var btn = document.getElementById('dev-more-btn');
1897
+ var menu = document.getElementById('dev-more-menu');
1898
+ var leftPanelToggleLabel = document.getElementById('left-panel-toggle-label');
1899
+ var presetButtons = document.querySelectorAll('#dev-preset-list .vp-preset-btn');
1900
+ var applyBtn = document.getElementById('dev-apply-btn');
1901
+ var zoomInp = document.getElementById('dev-zoom-level');
1902
+ var viewportBtn = document.querySelector('.tb-viewport');
1903
+ function updateLeftPanelToggleLabel() {
1904
+ if (!leftPanelToggleLabel) return;
1905
+ leftPanelToggleLabel.textContent = leftPanelCollapsed ? 'Expand left panel' : 'Collapse left panel';
1906
+ }
1907
+ function setLeftPanelCollapsed(collapsed) {
1908
+ var panel = document.getElementById('left-panel');
1909
+ leftPanelCollapsed = !!collapsed;
1910
+ if (panel) panel.style.display = leftPanelCollapsed ? 'none' : '';
1911
+ updateLeftPanelToggleLabel();
1912
+ applyViewportFrame();
1913
+ }
1914
+ if (leftPanelToggleLabel) {
1915
+ leftPanelToggleLabel.addEventListener('click', function(e) {
1916
+ e.preventDefault();
1917
+ e.stopPropagation();
1918
+ setLeftPanelCollapsed(!leftPanelCollapsed);
1919
+ });
1920
+ }
1921
+ if (btn) btn.addEventListener('click', function(e) {
1922
+ e.preventDefault();
1923
+ e.stopPropagation();
1924
+ toggleViewportMenu();
1925
+ });
1926
+ if (viewportBtn) viewportBtn.addEventListener('click', function(e) {
1927
+ e.preventDefault();
1928
+ e.stopPropagation();
1929
+ toggleViewportMenu();
1930
+ });
1931
+ if (applyBtn) applyBtn.addEventListener('click', function(e) {
1932
+ e.preventDefault();
1933
+ setViewportCustomFromInputs();
1934
+ closeViewportMenu();
1935
+ });
1936
+ if (zoomInp) {
1937
+ zoomInp.addEventListener('change', function() {
1938
+ var z = clampViewportNumber(zoomInp.value, Math.round(getAppliedViewportZoom() * 100), 25, 200);
1939
+ viewportZoom = z / 100;
1940
+ applyViewportFrame();
1941
+ });
1942
+ }
1943
+ if (presetButtons && presetButtons.length) {
1944
+ for (var i = 0; i < presetButtons.length; i++) {
1945
+ presetButtons[i].addEventListener('click', function(e) {
1946
+ e.preventDefault();
1947
+ var key = this.getAttribute('data-preset');
1948
+ if (!key) return;
1949
+ setViewportPreset(key);
1950
+ });
1951
+ }
1952
+ }
1953
+ document.addEventListener('click', function(e) {
1954
+ if (!menu || !menu.classList.contains('open')) return;
1955
+ if (menu.contains(e.target) || (btn && btn.contains(e.target)) || (viewportBtn && viewportBtn.contains(e.target))) return;
1956
+ closeViewportMenu();
1957
+ });
1958
+ document.addEventListener('keydown', function(e) {
1959
+ if (e.key === 'Escape') closeViewportMenu();
1960
+ });
1961
+ window.addEventListener('resize', function() { applyViewportFrame(); });
1962
+ updateLeftPanelToggleLabel();
1963
+ applyViewportFrame();
1629
1964
  }
1630
1965
 
1631
1966
  // \u2500\u2500 Left panel tab switch \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
@@ -1787,9 +2122,60 @@ function getOriginalValue(inputId, el) {
1787
2122
  }
1788
2123
  }
1789
2124
 
2125
+ var PX_DEFAULT_INPUT_IDS = {
2126
+ 'pp-mt': true, 'pp-mr': true, 'pp-mb': true, 'pp-ml': true,
2127
+ 'pp-pt': true, 'pp-pr': true, 'pp-pb': true, 'pp-pl': true,
2128
+ };
2129
+
2130
+ var PX_DEFAULT_CSS_PROPS = {
2131
+ 'margin-top': true, 'margin-right': true, 'margin-bottom': true, 'margin-left': true,
2132
+ 'padding-top': true, 'padding-right': true, 'padding-bottom': true, 'padding-left': true,
2133
+ };
2134
+
2135
+ function normalizeCssValueForProperty(prop, value) {
2136
+ var p = prop == null ? '' : String(prop).trim();
2137
+ var pKebab = p
2138
+ .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
2139
+ .replace(/_/g, '-')
2140
+ .replace(/!important/gi, '')
2141
+ .replace(/[^a-zA-Z-]/g, '')
2142
+ .toLowerCase();
2143
+ var raw = value == null ? '' : String(value).trim();
2144
+ var rawClean = raw.replace(/[\u200B\u200C\u200D\uFEFF]/g, '').trim();
2145
+ var numericCandidate = rawClean.replace(/[, ]+/g, '');
2146
+ var isSpacingProp = /^(?:margin|padding)(?:-(?:top|right|bottom|left))?$/.test(pKebab);
2147
+ if (!pKebab || !rawClean) return rawClean;
2148
+ var numericValue = Number(numericCandidate);
2149
+ var shouldAddPx =
2150
+ isSpacingProp &&
2151
+ numericCandidate !== '' &&
2152
+ Number.isFinite(numericValue) &&
2153
+ /^[-+]?(?:[0-9]+(?:[.][0-9]+)?|[.][0-9]+)$/.test(numericCandidate);
2154
+ var out = shouldAddPx ? numericCandidate + 'px' : rawClean;
2155
+ return out;
2156
+ }
2157
+
2158
+ function normalizeChainSetRowUnits(row) {
2159
+ if (!row) return row;
2160
+ if (normalizeChangesetType(row) === 'style') {
2161
+ row.value = normalizeCssValueForProperty(row.property || row.cssProp, row.value);
2162
+ }
2163
+ return row;
2164
+ }
2165
+
2166
+ function normalizeLoggedValue(inputId, value) {
2167
+ var raw = value == null ? '' : String(value).trim();
2168
+ if (!raw) return raw;
2169
+ if (PX_DEFAULT_INPUT_IDS[inputId] && /^-?d+(?:.d+)?$/.test(raw)) {
2170
+ return raw + 'px';
2171
+ }
2172
+ return raw;
2173
+ }
2174
+
1790
2175
  function logChange(selector, inputId, value, targetEl, originalValue) {
1791
2176
  var meta = PROP_META[inputId];
1792
2177
  if (!meta) return;
2178
+ value = normalizeLoggedValue(inputId, value);
1793
2179
  var key = selector + '||' + inputId;
1794
2180
  // Skip trivially empty / reset values \u2014 remove from log if present
1795
2181
  if (value === '' || value === 'none' || value === 'auto' || value === 'normal') {
@@ -2488,9 +2874,24 @@ function pickActiveVariationIdForLoad(data, variationsArr, prevMemoryId, allowPr
2488
2874
  return fallback;
2489
2875
  }
2490
2876
 
2877
+ function updateExperimentNameLabel(data) {
2878
+ var el = document.getElementById('tb-exp-name');
2879
+ if (!el) return;
2880
+ var name = '';
2881
+ try {
2882
+ name = data && data.name != null ? String(data.name).trim() : '';
2883
+ } catch(_) {
2884
+ name = '';
2885
+ }
2886
+ if (!name) name = 'Visual Editor';
2887
+ el.textContent = name;
2888
+ el.title = name;
2889
+ }
2890
+
2491
2891
  // \u2500\u2500 Experiment loading \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
2492
2892
  function handleLoadExperiment(data) {
2493
2893
  clearPendingGranularChangesets();
2894
+ updateExperimentNameLabel(data);
2494
2895
  var prevKey = experimentData
2495
2896
  ? String(experimentData.experimentId || '') + '|' + String(experimentData.pageUrl || '')
2496
2897
  : '';
@@ -3132,7 +3533,13 @@ function removeSessionStructuralRowByTimestamp(varId, ts) {
3132
3533
  function stateChangeToChainSet(c) {
3133
3534
  if (!c || !c.selector) return null;
3134
3535
  if (c.cssProp) {
3135
- return { selector: c.selector, type: 'style', property: c.cssProp, value: c.value, vveTs: c.vveTs || nextHistoryTimestamp() };
3536
+ return {
3537
+ selector: c.selector,
3538
+ type: 'style',
3539
+ property: c.cssProp,
3540
+ value: normalizeCssValueForProperty(c.cssProp, c.value),
3541
+ vveTs: c.vveTs || nextHistoryTimestamp()
3542
+ };
3136
3543
  }
3137
3544
  switch (c.inputId) {
3138
3545
  case 'pp-text':
@@ -3304,7 +3711,7 @@ function buildPersistentStyleRulesForActiveVariation() {
3304
3711
  if (value == null || value === '') return;
3305
3712
  var sel = sanitizeSelectorForMatch(String(selector)) || String(selector);
3306
3713
  var pr = String(prop).trim();
3307
- var val = String(value).trim();
3714
+ var val = normalizeCssValueForProperty(pr, value);
3308
3715
  if (!sel || !pr || !val) return;
3309
3716
  var k = sel + '__vve_sep__' + pr;
3310
3717
  if (!map[k]) order.push(k);
@@ -3342,7 +3749,8 @@ function buildPersistentStyleRulesForActiveVariation() {
3342
3749
  var lines = [];
3343
3750
  for (var oi = 0; oi < order.length; oi++) {
3344
3751
  var row = map[order[oi]];
3345
- lines.push(row.selector + ' { ' + row.property + ': ' + row.value + ' !important; }');
3752
+ var outVal = normalizeCssValueForProperty(row.property, row.value);
3753
+ lines.push(row.selector + ' { ' + row.property + ': ' + outVal + ' !important; }');
3346
3754
  }
3347
3755
  return lines.join('\\n');
3348
3756
  }
@@ -3493,7 +3901,9 @@ function buildPersistedChainSetsForVariation(v) {
3493
3901
  var row = stateChangeToChainSet(sourceStateChanges[si]);
3494
3902
  if (row) overlay.push(row);
3495
3903
  }
3496
- return mergeGranularChainSets(mergeGranularChainSets(base, sessionExtra), overlay);
3904
+ var merged = mergeGranularChainSets(mergeGranularChainSets(base, sessionExtra), overlay);
3905
+ for (var mi = 0; mi < merged.length; mi++) normalizeChainSetRowUnits(merged[mi]);
3906
+ return merged;
3497
3907
  }
3498
3908
 
3499
3909
  /**
@@ -5395,6 +5805,7 @@ function registerCROSections() {
5395
5805
 
5396
5806
  window.addEventListener('load', function() {
5397
5807
  registerCROSections();
5808
+ bindViewportControls();
5398
5809
  switchSectionComponentsTab(currentSectionComponentsTab);
5399
5810
  renderElementsTree(document.getElementById('comp-search').value);
5400
5811
  vvvebReady = true;
@@ -5771,6 +6182,10 @@ function createVisualEditorMiddleware(options) {
5771
6182
  };
5772
6183
  const upstreamFetch = async (input, init = {}) => {
5773
6184
  if (conversionProxyBaseUrlForRequest) {
6185
+ const method2 = String(init?.method || "GET").toUpperCase();
6186
+ if (method2 !== "GET" && method2 !== "HEAD") {
6187
+ return directFetch(input, init);
6188
+ }
5774
6189
  return workerRawFetch(input, init);
5775
6190
  }
5776
6191
  const primary = await scraperFetch(input, init);
@@ -6102,8 +6517,60 @@ ${runtimePreflightScript}
6102
6517
  var TARGET_ORIGIN=${JSON.stringify(origin)};
6103
6518
  var TARGET_PAGE_URL=${JSON.stringify(targetUrl)};
6104
6519
  var EMPTY_JSON_DATA="data:application/json;charset=utf-8,%7B%7D";
6105
- function isSkippable(raw){if(!raw||typeof raw!=="string")return true;return raw.startsWith("data:")||raw.startsWith("blob:")||raw.startsWith("javascript:")||raw.startsWith("#")||raw.startsWith("http")||raw.startsWith("//");}
6106
- function toAbsoluteOriginUrl(raw){if(isSkippable(raw))return raw;try{var base=raw.startsWith("/")?TARGET_ORIGIN:TARGET_PAGE_URL;var abs=new URL(raw,base);if(abs.origin!==TARGET_ORIGIN)return raw;return abs.toString();}catch(_){return raw;}}
6520
+ var _proxyCtx=(function(){try{return new URL(window.location.href);}catch(_){return null;}})();
6521
+ var PROXY_ROOT="/api/conversion-proxy";
6522
+ var PROXY_PASSWORD=_proxyCtx?_proxyCtx.searchParams.get("password")||"":"";
6523
+ var PROXY_BASE_URL=_proxyCtx?_proxyCtx.searchParams.get("conversionProxyBaseUrl")||"":"";
6524
+ var PROXY_TRACKING_MARKERS=_proxyCtx?_proxyCtx.searchParams.get("trackingMarkers")||"":"";
6525
+ var PROXY_STRICT_FREEZE=_proxyCtx?_proxyCtx.searchParams.get("strictObserverFreeze")||"":"";
6526
+ var PROXY_UPSTREAM_MODE=_proxyCtx?_proxyCtx.searchParams.get("proxy")||"":"";
6527
+ var EDITOR_ORIGIN=(function(){try{return String(window.location.origin||"");}catch(_){return "";}})();
6528
+ var EDITOR_HOST_KEY=(function(){try{var u=new URL(EDITOR_ORIGIN);return (u.hostname||"").toLowerCase()+":"+(u.port||"");}catch(_){return "";}})();
6529
+ function isSkippable(raw){if(!raw||typeof raw!=="string")return true;return raw.startsWith("data:")||raw.startsWith("blob:")||raw.startsWith("javascript:")||raw.startsWith("#");}
6530
+ function isEditorHostUrl(u){
6531
+ try{
6532
+ if(!u) return false;
6533
+ var hostKey=(u.hostname||"").toLowerCase()+":"+(u.port||"");
6534
+ if(EDITOR_HOST_KEY&&hostKey===EDITOR_HOST_KEY) return true;
6535
+ // localhost aliases for same dev server port
6536
+ var host=(u.hostname||"").toLowerCase();
6537
+ var editorHost=EDITOR_HOST_KEY.split(":")[0]||"";
6538
+ var samePort=(u.port||"")===(EDITOR_HOST_KEY.split(":")[1]||"");
6539
+ var localAlias=(host==="localhost"||host==="127.0.0.1"||host==="[::1]");
6540
+ var editorLocalAlias=(editorHost==="localhost"||editorHost==="127.0.0.1"||editorHost==="[::1]");
6541
+ return samePort&&localAlias&&editorLocalAlias;
6542
+ }catch(_){
6543
+ return false;
6544
+ }
6545
+ }
6546
+ function toProxyNetworkUrl(raw){
6547
+ if(isSkippable(raw))return raw;
6548
+ try{
6549
+ var base=raw.startsWith("/")?TARGET_ORIGIN:TARGET_PAGE_URL;
6550
+ var abs=new URL(raw,base);
6551
+ // Some embedded scripts build absolute requests against editor origin
6552
+ // (e.g. https://localhost:4001/api/unstable/graphql.json). Remap those
6553
+ // paths to target origin first, then proxy as usual.
6554
+ if(EDITOR_ORIGIN&&isEditorHostUrl(abs)){
6555
+ var p=abs.pathname||"";
6556
+ var isRootProxyPath=p==="/api/conversion-proxy"||p.indexOf("/api/conversion-proxy/")===0;
6557
+ if(!isRootProxyPath){
6558
+ abs=new URL((abs.pathname||"/")+(abs.search||"")+(abs.hash||""),TARGET_ORIGIN);
6559
+ }
6560
+ }
6561
+ if(abs.origin!==TARGET_ORIGIN)return raw;
6562
+ var prox=new URL(PROXY_ROOT,window.location.origin);
6563
+ prox.searchParams.set("password",PROXY_PASSWORD||"");
6564
+ prox.searchParams.set("url",abs.toString());
6565
+ if(PROXY_BASE_URL)prox.searchParams.set("conversionProxyBaseUrl",PROXY_BASE_URL);
6566
+ if(PROXY_TRACKING_MARKERS)prox.searchParams.set("trackingMarkers",PROXY_TRACKING_MARKERS);
6567
+ if(PROXY_STRICT_FREEZE)prox.searchParams.set("strictObserverFreeze",PROXY_STRICT_FREEZE);
6568
+ if(PROXY_UPSTREAM_MODE)prox.searchParams.set("proxy",PROXY_UPSTREAM_MODE);
6569
+ return prox.toString();
6570
+ }catch(_){
6571
+ return raw;
6572
+ }
6573
+ }
6107
6574
  function resolveUrl(s){try{return new URL(s,window.location.href);}catch(_){return null;}}
6108
6575
  function isNestedMalformedProxy(u){if(!u)return false;var p=u.pathname||"";if(p==="/api/conversion-proxy"||p.indexOf("/api/conversion-proxy/")===0)return false;return p.indexOf("api/conversion-proxy")!==-1;}
6109
6576
  function skipNestedProxyNetwork(s){var u=typeof s==="string"?resolveUrl(s):null;return u&&isNestedMalformedProxy(u);}
@@ -6116,9 +6583,9 @@ if(window.fetch){
6116
6583
  var rawUrl=typeof input==="string"?input:(input&&input.url?String(input.url):"");
6117
6584
  if(rawUrl&&skipNestedProxyNetwork(rawUrl))return emptyJsonFetchResponse();
6118
6585
  if(typeof input==="string"){
6119
- input=toAbsoluteOriginUrl(input);
6586
+ input=toProxyNetworkUrl(input);
6120
6587
  }else if(input&&input.url){
6121
- var next=toAbsoluteOriginUrl(input.url);
6588
+ var next=toProxyNetworkUrl(input.url);
6122
6589
  if(next!==input.url){input=new Request(next,input);}
6123
6590
  }
6124
6591
  afterUrl=typeof input==="string"?input:(input&&input.url?String(input.url):"");
@@ -6152,7 +6619,7 @@ if(window.fetch){
6152
6619
  });
6153
6620
  };
6154
6621
  }
6155
- if(window.XMLHttpRequest&&window.XMLHttpRequest.prototype&&window.XMLHttpRequest.prototype.open){var _open=window.XMLHttpRequest.prototype.open;window.XMLHttpRequest.prototype.open=function(method,url){try{var u=resolveUrl(String(url));if(u&&isNestedMalformedProxy(u)){arguments[1]=EMPTY_JSON_DATA;}else{arguments[1]=toAbsoluteOriginUrl(url);}}catch(_){}return _open.apply(this,arguments);};}
6622
+ if(window.XMLHttpRequest&&window.XMLHttpRequest.prototype&&window.XMLHttpRequest.prototype.open){var _open=window.XMLHttpRequest.prototype.open;window.XMLHttpRequest.prototype.open=function(method,url){try{var u=resolveUrl(String(url));if(u&&isNestedMalformedProxy(u)){arguments[1]=EMPTY_JSON_DATA;}else{arguments[1]=toProxyNetworkUrl(url);}}catch(_){}return _open.apply(this,arguments);};}
6156
6623
  if(window.navigator&&typeof window.navigator.sendBeacon==="function"){var _beacon=window.navigator.sendBeacon.bind(window.navigator);window.navigator.sendBeacon=function(url,data){try{if(skipNestedProxyNetwork(String(url)))return true;}catch(_){}return _beacon(url,data);};}
6157
6624
  function withReloadBust(urlRaw){try{var u=resolveUrl(String(urlRaw||""));if(!u)return null;var p=u.pathname||"";var isRootProxyPath=p==="/api/conversion-proxy"||p.indexOf("/api/conversion-proxy/")===0;if(!isRootProxyPath)return null;return u.toString();}catch(_){return null;}}
6158
6625
  document.addEventListener("click",function(ev){try{var t=ev&&ev.target;if(!t||!t.closest)return;var a=t.closest("a[href]");if(a){var href=a.getAttribute("href")||a.href||"";var hasModifier=!!(ev.metaKey||ev.ctrlKey||ev.shiftKey||ev.altKey);var targetAttr=(a.getAttribute("target")||"").toLowerCase();var isNewTab=targetAttr==="_blank";var isDownload=!!a.getAttribute("download");if(hasModifier||isNewTab||isDownload)return;var prox=toProxy(href);if(!prox)return;a.setAttribute("href",prox);if(ev&&typeof ev.preventDefault==="function")ev.preventDefault();safeNavigate(href,"assign");return;}var summary=t.closest("summary[data-link]");if(summary&&summary.getAttribute){var raw=summary.getAttribute("data-link")||"";if(raw){var prox2=toProxy(raw);if(prox2){summary.setAttribute("data-link",prox2);if(ev&&typeof ev.preventDefault==="function")ev.preventDefault();safeNavigate(raw,"assign");}}}}catch(_){}},true);