@accelerated-agency/visual-editor 0.5.9 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/vite.cjs +366 -50
  2. package/dist/vite.js +366 -50
  3. package/package.json +1 -1
package/dist/vite.cjs CHANGED
@@ -1039,24 +1039,16 @@ button.var-tab.active{
1039
1039
  .lp-body #tab-dom-tree, .lp-body #tab-elements{
1040
1040
  padding-top:4px;
1041
1041
  }
1042
- .lp-body #tab-elements .dt-lbl{
1043
- font-size: 12px;
1044
- font-style: normal;
1045
- font-weight: 500;
1046
- line-height: 14px;
1047
- letter-spacing: -0.1px;
1048
- }
1049
- .lp-body #tab-elements #elements-root > .dt-row{
1050
- padding-left: 16px!important;
1051
- }
1052
- .lp-body #dom-tree-root .dt-lbl{
1042
+ .lp-body #dom-tree-root .dt-lbl,
1043
+ .lp-body #elements-root .dt-lbl{
1053
1044
  color: var(--content-subtle, #737373);
1054
1045
  font-style: normal;
1055
1046
  font-weight: 400;
1056
- line-height: var(--font-leading-3, 12px); /* 100% */
1047
+ line-height: var(--font-leading-3, 12px);
1057
1048
  letter-spacing: -0.1px;
1058
1049
  }
1059
- .lp-body #dom-tree-root .dt-lbl .dt-tag{
1050
+ .lp-body #dom-tree-root .dt-lbl .dt-tag,
1051
+ .lp-body #elements-root .dt-lbl .dt-tag{
1060
1052
  color: var(--content-strong, #171717);
1061
1053
  font-family: "JetBrains Mono";
1062
1054
  font-size: 12px;
@@ -1070,7 +1062,6 @@ padding:3px 6px;
1070
1062
  display:inline-block;
1071
1063
  margin-right:4px;
1072
1064
  }
1073
- .lp-body #tab-elements .dt-row .dt-chev.dt-spacer{display:none;}
1074
1065
  .lp-body #tab-dom-tree .dt-row .dt-ico{display:none;}
1075
1066
  .lp-body, .section-components-body{flex:1;overflow-y:auto}
1076
1067
  .lp-body::-webkit-scrollbar, .section-components-body::-webkit-scrollbar{width:3px}
@@ -3159,6 +3150,7 @@ var PROP_META = {
3159
3150
  'pp-value': {label:'Value', cssProp:null},
3160
3151
  'pp-text': {label:'Inner text', cssProp:null},
3161
3152
  'pp-html': {label:'Inner HTML', cssProp:null},
3153
+ 'pp-select-options': {label:'Options', cssProp:null},
3162
3154
  'pp-mob-css': {label:'Mobile CSS', cssProp:null},
3163
3155
  'pp-tab-css': {label:'Tablet CSS', cssProp:null},
3164
3156
  };
@@ -3175,6 +3167,7 @@ function getOriginalValue(inputId, el) {
3175
3167
  switch (inputId) {
3176
3168
  case 'pp-text': return el.innerText || '';
3177
3169
  case 'pp-html': return el.innerHTML || '';
3170
+ case 'pp-select-options': return getSelectOptionsText(el);
3178
3171
  case 'pp-cls': return el.className || '';
3179
3172
  case 'pp-id': return el.id || '';
3180
3173
  case 'pp-href': return el.getAttribute('href') || '';
@@ -3324,6 +3317,7 @@ function revertChangeOnDom(change) {
3324
3317
  switch (change.inputId) {
3325
3318
  case 'pp-text': el.innerText = orig; break;
3326
3319
  case 'pp-html': el.innerHTML = orig; break;
3320
+ case 'pp-select-options': applySelectOptionsFromText(el, orig); break;
3327
3321
  case 'pp-cls': el.className = orig; break;
3328
3322
  case 'pp-id': el.id = orig; break;
3329
3323
  case 'pp-css': orig ? el.setAttribute('style', orig) : el.removeAttribute('style'); break;
@@ -4982,6 +4976,8 @@ function stateChangeToChainSet(c) {
4982
4976
  return { selector: c.selector, type: 'content', value: c.value, vveTs: c.vveTs || nextHistoryTimestamp() };
4983
4977
  case 'pp-html':
4984
4978
  return { selector: c.selector, type: 'content', html: c.value, vveTs: c.vveTs || nextHistoryTimestamp() };
4979
+ case 'pp-select-options':
4980
+ return { selector: c.selector, type: 'content', html: selectOptionsTextToHtml(c.value), vveTs: c.vveTs || nextHistoryTimestamp() };
4985
4981
  case 'pp-cls':
4986
4982
  return { selector: c.selector, type: 'attribute', attribute: 'class', value: c.value, vveTs: c.vveTs || nextHistoryTimestamp() };
4987
4983
  case 'pp-id':
@@ -5595,6 +5591,10 @@ function setTreeHoverHighlight(el) {
5595
5591
  }
5596
5592
 
5597
5593
  function isTreeHoverOnlyClassMutation(mutation) {
5594
+ return isEditorChromeOnlyClassMutation(mutation);
5595
+ }
5596
+
5597
+ function isEditorChromeOnlyClassMutation(mutation) {
5598
5598
  if (!mutation || mutation.type !== 'attributes' || mutation.attributeName !== 'class') return false;
5599
5599
  var oldClass = String(mutation.oldValue || '');
5600
5600
  var target = mutation.target;
@@ -5602,7 +5602,12 @@ function isTreeHoverOnlyClassMutation(mutation) {
5602
5602
  try {
5603
5603
  nextClass = target && typeof target.className === 'string' ? target.className : '';
5604
5604
  } catch(_) {}
5605
- return oldClass.indexOf('vve-tree-hover') >= 0 || String(nextClass).indexOf('vve-tree-hover') >= 0;
5605
+ var combined = oldClass + ' ' + nextClass;
5606
+ return (
5607
+ combined.indexOf('vve-tree-hover') >= 0 ||
5608
+ combined.indexOf('vve-selected') >= 0 ||
5609
+ combined.indexOf('vve-dragging') >= 0
5610
+ );
5606
5611
  }
5607
5612
 
5608
5613
  function setDragHandleActive(on) {
@@ -5652,7 +5657,13 @@ function positionSelectionToolbar() {
5652
5657
  if (!bar || !liveSelected || !iframe || !iframe.contentWindow || !panel) return;
5653
5658
  if (selectedEl !== liveSelected) {
5654
5659
  selectedEl = liveSelected;
5655
- renderRightPanel(liveSelected);
5660
+ if (!document.activeElement || (
5661
+ document.activeElement.id !== 'pp-html' &&
5662
+ document.activeElement.id !== 'pp-text' &&
5663
+ document.activeElement.id !== 'pp-select-options'
5664
+ )) {
5665
+ renderRightPanel(liveSelected);
5666
+ }
5656
5667
  syncDomTreeSelection();
5657
5668
  }
5658
5669
  var elR = getIframeElementVisualRect(selectedEl);
@@ -6186,31 +6197,156 @@ function renderElementsTree(filterRaw) {
6186
6197
  return;
6187
6198
  }
6188
6199
 
6189
- function nodeIcon(tag) {
6190
- tag = (tag || '').toLowerCase();
6191
- if (/^h[1-6]$/.test(tag)) return 'bi bi-type-h1';
6192
- if (tag === 'a') return 'bi bi-link-45deg';
6193
- if (tag === 'img') return 'bi bi-image';
6194
- if (tag === 'section' || tag === 'main' || tag === 'article' || tag === 'header' || tag === 'footer' || tag === 'nav') return 'bi bi-layout-three-columns';
6195
- if (tag === 'button' || tag === 'input' || tag === 'select' || tag === 'textarea') return 'bi bi-ui-radios';
6196
- if (tag === 'ul' || tag === 'ol') return 'bi bi-list-ul';
6197
- if (tag === 'li') return 'bi bi-dot';
6198
- if (tag === 'svg') return 'bi bi-bezier2';
6199
- if (tag === 'p' || tag === 'span') return 'bi bi-text-left';
6200
- return 'bi bi-square';
6201
- }
6200
+ function nodeIcon(tag) {
6201
+ tag = (tag || '').toLowerCase();
6202
+
6203
+ // Headings
6204
+ if (/^h[1-6]$/.test(tag)) return 'bi bi-type-h1';
6205
+
6206
+ // Document & metadata
6207
+ if (tag === 'html') return 'bi bi-filetype-html';
6208
+ if (tag === 'head') return 'bi bi-file-earmark-code';
6209
+ if (tag === 'body') return 'bi bi-file-earmark-richtext';
6210
+ if (tag === 'title') return 'bi bi-card-heading';
6211
+ if (tag === 'meta') return 'bi bi-info-circle';
6212
+ if (tag === 'link') return 'bi bi-link';
6213
+ if (tag === 'style') return 'bi bi-filetype-css';
6214
+ if (tag === 'script') return 'bi bi-filetype-js';
6215
+ if (tag === 'noscript') return 'bi bi-slash-circle';
6216
+ if (tag === 'base') return 'bi bi-house-gear';
6217
+
6218
+ // Sectioning & layout
6219
+ if (tag === 'section' || tag === 'main' || tag === 'article' ||
6220
+ tag === 'header' || tag === 'footer' || tag === 'nav' || tag === 'aside')
6221
+ return 'bi bi-layout-three-columns';
6222
+ if (tag === 'div') return 'bi bi-square';
6223
+ if (tag === 'address') return 'bi bi-geo-alt';
6224
+
6225
+ // Text content
6226
+ if (tag === 'p' || tag === 'span') return 'bi bi-text-left';
6227
+ if (tag === 'blockquote' || tag === 'q') return 'bi bi-blockquote-left';
6228
+ if (tag === 'pre') return 'bi bi-code-square';
6229
+ if (tag === 'code') return 'bi bi-code-slash';
6230
+ if (tag === 'kbd') return 'bi bi-keyboard';
6231
+ if (tag === 'samp') return 'bi bi-terminal';
6232
+ if (tag === 'var') return 'bi bi-braces';
6233
+ if (tag === 'cite') return 'bi bi-quote';
6234
+ if (tag === 'abbr') return 'bi bi-fonts';
6235
+ if (tag === 'time') return 'bi bi-clock';
6236
+ if (tag === 'mark') return 'bi bi-highlighter';
6237
+ if (tag === 'small') return 'bi bi-type';
6238
+ if (tag === 'sub') return 'bi bi-subscript';
6239
+ if (tag === 'sup') return 'bi bi-superscript';
6240
+ if (tag === 'br') return 'bi bi-arrow-return-left';
6241
+ if (tag === 'wbr') return 'bi bi-distribute-horizontal';
6242
+
6243
+ // Inline formatting
6244
+ if (tag === 'strong' || tag === 'b') return 'bi bi-type-bold';
6245
+ if (tag === 'em' || tag === 'i') return 'bi bi-type-italic';
6246
+ if (tag === 'u' || tag === 'ins') return 'bi bi-type-underline';
6247
+ if (tag === 's' || tag === 'del' || tag === 'strike') return 'bi bi-type-strikethrough';
6248
+
6249
+ // Lists
6250
+ if (tag === 'ul') return 'bi bi-list-ul';
6251
+ if (tag === 'ol') return 'bi bi-list-ol';
6252
+ if (tag === 'li') return 'bi bi-dot';
6253
+ if (tag === 'dl') return 'bi bi-card-list';
6254
+ if (tag === 'dt') return 'bi bi-tag';
6255
+ if (tag === 'dd') return 'bi bi-text-indent-left';
6256
+
6257
+ // Links & navigation
6258
+ if (tag === 'a') return 'bi bi-link-45deg';
6259
+
6260
+ // Media
6261
+ if (tag === 'img') return 'bi bi-image';
6262
+ if (tag === 'picture') return 'bi bi-images';
6263
+ if (tag === 'figure') return 'bi bi-card-image';
6264
+ if (tag === 'figcaption') return 'bi bi-card-text';
6265
+ if (tag === 'video') return 'bi bi-camera-video';
6266
+ if (tag === 'audio') return 'bi bi-music-note-beamed';
6267
+ if (tag === 'source') return 'bi bi-cloud-arrow-down';
6268
+ if (tag === 'track') return 'bi bi-badge-cc';
6269
+ if (tag === 'iframe') return 'bi bi-window';
6270
+ if (tag === 'embed' || tag === 'object') return 'bi bi-box-arrow-in-down';
6271
+ if (tag === 'param') return 'bi bi-sliders';
6272
+ if (tag === 'canvas') return 'bi bi-easel';
6273
+ if (tag === 'map') return 'bi bi-map';
6274
+ if (tag === 'area') return 'bi bi-bounding-box';
6275
+
6276
+ // Vector / math
6277
+ if (tag === 'svg') return 'bi bi-bezier2';
6278
+ if (tag === 'path') return 'bi bi-bezier';
6279
+ if (tag === 'circle') return 'bi bi-circle';
6280
+ if (tag === 'rect') return 'bi bi-square';
6281
+ if (tag === 'line') return 'bi bi-slash-lg';
6282
+ if (tag === 'polygon') return 'bi bi-pentagon';
6283
+ if (tag === 'polyline') return 'bi bi-share';
6284
+ if (tag === 'ellipse') return 'bi bi-circle-half';
6285
+ if (tag === 'g') return 'bi bi-collection';
6286
+ if (tag === 'use') return 'bi bi-arrow-repeat';
6287
+ if (tag === 'defs') return 'bi bi-bookmark';
6288
+ if (tag === 'symbol') return 'bi bi-star';
6289
+ if (tag === 'text') return 'bi bi-fonts';
6290
+ if (tag === 'math') return 'bi bi-calculator';
6291
+
6292
+ // Forms
6293
+ if (tag === 'form') return 'bi bi-file-earmark-check';
6294
+ if (tag === 'fieldset') return 'bi bi-bounding-box-circles';
6295
+ if (tag === 'legend') return 'bi bi-tag-fill';
6296
+ if (tag === 'label') return 'bi bi-tag';
6297
+ if (tag === 'input') return 'bi bi-input-cursor-text';
6298
+ if (tag === 'button') return 'bi bi-ui-radios';
6299
+ if (tag === 'select') return 'bi bi-menu-button';
6300
+ if (tag === 'option') return 'bi bi-check2-square';
6301
+ if (tag === 'optgroup') return 'bi bi-list-nested';
6302
+ if (tag === 'textarea') return 'bi bi-textarea-resize';
6303
+ if (tag === 'datalist') return 'bi bi-list-columns';
6304
+ if (tag === 'output') return 'bi bi-box-arrow-right';
6305
+ if (tag === 'progress') return 'bi bi-bar-chart-line';
6306
+ if (tag === 'meter') return 'bi bi-speedometer2';
6307
+
6308
+ // Tables
6309
+ if (tag === 'table') return 'bi bi-table';
6310
+ if (tag === 'caption') return 'bi bi-card-heading';
6311
+ if (tag === 'thead') return 'bi bi-layout-text-window';
6312
+ if (tag === 'tbody') return 'bi bi-layout-text-sidebar';
6313
+ if (tag === 'tfoot') return 'bi bi-layout-text-window-reverse';
6314
+ if (tag === 'tr') return 'bi bi-grip-horizontal';
6315
+ if (tag === 'th') return 'bi bi-grid-3x3-gap-fill';
6316
+ if (tag === 'td') return 'bi bi-grid-3x3-gap';
6317
+ if (tag === 'col') return 'bi bi-layout-three-columns';
6318
+ if (tag === 'colgroup') return 'bi bi-columns-gap';
6319
+
6320
+ // Interactive / disclosure
6321
+ if (tag === 'details') return 'bi bi-caret-down-square';
6322
+ if (tag === 'summary') return 'bi bi-card-text';
6323
+ if (tag === 'dialog') return 'bi bi-chat-square-text';
6324
+ if (tag === 'menu') return 'bi bi-list';
6325
+
6326
+ // Web components / templating
6327
+ if (tag === 'template') return 'bi bi-file-earmark-code';
6328
+ if (tag === 'slot') return 'bi bi-box-seam';
6329
+
6330
+ // Ruby annotation
6331
+ if (tag === 'ruby' || tag === 'rt' || tag === 'rp' || tag === 'rb')
6332
+ return 'bi bi-translate';
6333
+ if (tag === 'bdi' || tag === 'bdo') return 'bi bi-arrow-left-right';
6334
+
6335
+ // Misc
6336
+ if (tag === 'hr') return 'bi bi-hr';
6337
+ if (tag === '#text' || tag === 'text-node') return 'bi bi-cursor-text';
6338
+ if (tag === '#comment') return 'bi bi-chat-left-text';
6339
+
6340
+ return 'bi bi-square';
6341
+ }
6202
6342
 
6203
- function labelFor(el) {
6204
- var tag = (el.tagName || '').toLowerCase();
6205
- return tag.toUpperCase();
6206
- }
6207
6343
 
6208
6344
  var nodes = collectEditorInsertedElements(doc);
6209
6345
 
6210
6346
  root.innerHTML = '';
6211
6347
  for (var i = 0; i < nodes.length; i++) {
6212
6348
  var el = nodes[i];
6213
- var lblText = labelFor(el);
6349
+ var lblText = domTreeLabelFor(el);
6214
6350
  if (filterText && lblText.toLowerCase().indexOf(filterText) < 0) continue;
6215
6351
 
6216
6352
  var row = document.createElement('div');
@@ -6229,7 +6365,7 @@ function renderElementsTree(filterRaw) {
6229
6365
 
6230
6366
  var lbl = document.createElement('div');
6231
6367
  lbl.className = 'dt-lbl';
6232
- lbl.textContent = lblText;
6368
+ setDomTreeLabelContent(lbl, el);
6233
6369
  lbl.title = buildSelector(el);
6234
6370
 
6235
6371
  row.appendChild(spacer);
@@ -6254,11 +6390,35 @@ function renderElementsTree(filterRaw) {
6254
6390
  };
6255
6391
  }
6256
6392
 
6393
+ function domTreeLabelClassExcluded(className) {
6394
+ var c = String(className || '');
6395
+ return !c || c === 'vve-selected' || c === 'vve-tree-hover' || c.indexOf('vve-') === 0;
6396
+ }
6397
+
6398
+ function domTreeLabelClassString(el) {
6399
+ if (!el) return '';
6400
+ try {
6401
+ if (el.classList && el.classList.length) {
6402
+ var out = [];
6403
+ for (var i = 0; i < el.classList.length; i++) {
6404
+ var cls = el.classList[i];
6405
+ if (!domTreeLabelClassExcluded(cls)) out.push(cls);
6406
+ }
6407
+ return out.join(' ');
6408
+ }
6409
+ } catch(_) {}
6410
+ var cn = el.className;
6411
+ if (cn && typeof cn === 'object' && cn.baseVal != null) cn = String(cn.baseVal);
6412
+ return typeof cn === 'string' ? cn : '';
6413
+ }
6414
+
6257
6415
  function domTreeLabelSuffix(el) {
6258
6416
  if (el.id != null && el.id !== '') return '#' + String(el.id).slice(0, 40);
6259
- var cn = el.className && typeof el.className === 'string' ? el.className.trim() : '';
6417
+ var cn = domTreeLabelClassString(el).trim();
6260
6418
  if (cn) {
6261
- var parts = cn.split(/s+/).filter(function(x) { return x.indexOf('vve-') !== 0; }).slice(0, 2).join('.');
6419
+ var parts = cn.split(/s+/).filter(function(x) {
6420
+ return x && !domTreeLabelClassExcluded(x);
6421
+ }).slice(0, 2).join('.');
6262
6422
  if (parts) return '.' + parts.slice(0, 56);
6263
6423
  }
6264
6424
  return '';
@@ -6446,11 +6606,64 @@ function isFormControlElement(el) {
6446
6606
  function shouldShowInnerContentFields(el) {
6447
6607
  if (!el || el.nodeType !== 1) return false;
6448
6608
  var tag = (el.tagName || '').toLowerCase();
6449
- if (tag === 'input' || tag === 'textarea' || tag === 'video') return false;
6609
+ if (tag === 'input' || tag === 'textarea' || tag === 'select' || tag === 'video') return false;
6450
6610
  if (isEmbeddedVideoIframe(el)) return false;
6451
6611
  return true;
6452
6612
  }
6453
6613
 
6614
+ function getSelectOptionsText(el) {
6615
+ if (!el || el.nodeType !== 1) return '';
6616
+ try {
6617
+ var opts = el.querySelectorAll('option');
6618
+ var lines = [];
6619
+ for (var i = 0; i < opts.length; i++) {
6620
+ lines.push(opts[i].textContent || '');
6621
+ }
6622
+ return lines.join('\\n');
6623
+ } catch(_) {
6624
+ return '';
6625
+ }
6626
+ }
6627
+
6628
+ function selectOptionsTextToHtml(text) {
6629
+ var lines = String(text == null ? '' : text).split('\\n');
6630
+ var parts = [];
6631
+ for (var i = 0; i < lines.length; i++) {
6632
+ var label = lines[i];
6633
+ if (!label && i === lines.length - 1 && lines.length > 1) continue;
6634
+ parts.push('<option>' + esc(label) + '</option>');
6635
+ }
6636
+ if (!parts.length) parts.push('<option>Option 1</option>');
6637
+ return parts.join('');
6638
+ }
6639
+
6640
+ function applySelectOptionsFromText(el, text) {
6641
+ if (!el || el.nodeType !== 1) return;
6642
+ beginSuppressIframeMutationDirty();
6643
+ try {
6644
+ var prevValue = '';
6645
+ try { prevValue = el.value || ''; } catch(_) {}
6646
+ var doc = el.ownerDocument;
6647
+ while (el.firstChild) el.removeChild(el.firstChild);
6648
+ var lines = String(text == null ? '' : text).split('\\n');
6649
+ if (!lines.length || (lines.length === 1 && !String(lines[0] || '').trim())) {
6650
+ lines = ['Option 1', 'Option 2'];
6651
+ }
6652
+ for (var i = 0; i < lines.length; i++) {
6653
+ var label = lines[i];
6654
+ if (!label && i === lines.length - 1) continue;
6655
+ var opt = doc.createElement('option');
6656
+ opt.textContent = label;
6657
+ el.appendChild(opt);
6658
+ }
6659
+ if (prevValue) {
6660
+ try { el.value = prevValue; } catch(_) {}
6661
+ }
6662
+ } finally {
6663
+ endSuppressIframeMutationDirty();
6664
+ }
6665
+ }
6666
+
6454
6667
  function elementHasHtmlChildren(el) {
6455
6668
  if (!el || el.nodeType !== 1) return false;
6456
6669
  try {
@@ -7109,6 +7322,12 @@ function renderRightPanel(el, options) {
7109
7322
  subLbl('Placeholder') +
7110
7323
  '<input class="pr-inp" id="pp-ph" type="text" value="'+esc(el.getAttribute('placeholder')||'')+'" style="width:100%;margin-bottom:8px">';
7111
7324
  }
7325
+ if (tag === 'select') {
7326
+ contentHtml +=
7327
+ subLbl('Options') +
7328
+ '<div style="font-size:11px;color:var(--text-3);margin:-4px 0 8px">One option label per line</div>' +
7329
+ '<textarea class="pr-inp" id="pp-select-options" style="width:100%;min-height:80px;font-family:var(--font-mono);font-size:11px">'+esc(getSelectOptionsText(el))+'</textarea>';
7330
+ }
7112
7331
  if (shouldShowInnerContentFields(el)) {
7113
7332
  if (elementHasHtmlChildren(el)) {
7114
7333
  contentHtml +=
@@ -7216,18 +7435,49 @@ function renderRightPanel(el, options) {
7216
7435
  function wireContentFieldSync(el, sel) {
7217
7436
  var textInp = document.getElementById('pp-text');
7218
7437
  var htmlInp = document.getElementById('pp-html');
7438
+ var selectOptsInp = document.getElementById('pp-select-options');
7219
7439
  var syncing = false;
7220
- function applyContentChange(changedId) {
7440
+ var htmlApplyTimer = null;
7441
+ var selectApplyTimer = null;
7442
+
7443
+ function applyHtmlToElement(htmlValue) {
7444
+ beginSuppressIframeMutationDirty();
7445
+ try {
7446
+ el.innerHTML = htmlValue;
7447
+ } finally {
7448
+ endSuppressIframeMutationDirty();
7449
+ }
7450
+ }
7451
+
7452
+ function applyContentChange(changedId, immediate) {
7221
7453
  if (syncing) return;
7454
+ if (changedId === 'pp-html' && !immediate) {
7455
+ if (htmlApplyTimer) clearTimeout(htmlApplyTimer);
7456
+ htmlApplyTimer = setTimeout(function() { applyContentChange('pp-html', true); }, 250);
7457
+ return;
7458
+ }
7459
+ if (changedId === 'pp-select-options' && !immediate) {
7460
+ if (selectApplyTimer) clearTimeout(selectApplyTimer);
7461
+ selectApplyTimer = setTimeout(function() { applyContentChange('pp-select-options', true); }, 250);
7462
+ return;
7463
+ }
7222
7464
  syncing = true;
7223
7465
  try {
7224
7466
  var orig = getOriginalValue(changedId, el);
7225
7467
  if (changedId === 'pp-text') {
7226
- el.innerText = textInp.value;
7468
+ beginSuppressIframeMutationDirty();
7469
+ try {
7470
+ el.innerText = textInp.value;
7471
+ } finally {
7472
+ endSuppressIframeMutationDirty();
7473
+ }
7227
7474
  if (htmlInp) htmlInp.value = el.innerHTML;
7228
7475
  logChange(sel, 'pp-text', textInp.value, el, orig);
7476
+ } else if (changedId === 'pp-select-options') {
7477
+ applySelectOptionsFromText(el, selectOptsInp.value);
7478
+ logChange(sel, 'pp-select-options', selectOptsInp.value, el, orig);
7229
7479
  } else {
7230
- el.innerHTML = htmlInp.value;
7480
+ applyHtmlToElement(htmlInp.value);
7231
7481
  if (textInp) textInp.value = el.innerText;
7232
7482
  logChange(sel, 'pp-html', htmlInp.value, el, orig);
7233
7483
  }
@@ -7240,8 +7490,12 @@ function wireContentFieldSync(el, sel) {
7240
7490
  textInp.addEventListener('change', function() { applyContentChange('pp-text'); });
7241
7491
  }
7242
7492
  if (htmlInp) {
7243
- htmlInp.addEventListener('input', function() { applyContentChange('pp-html'); });
7244
- htmlInp.addEventListener('change', function() { applyContentChange('pp-html'); });
7493
+ htmlInp.addEventListener('input', function() { applyContentChange('pp-html', false); });
7494
+ htmlInp.addEventListener('change', function() { applyContentChange('pp-html', true); });
7495
+ }
7496
+ if (selectOptsInp) {
7497
+ selectOptsInp.addEventListener('input', function() { applyContentChange('pp-select-options', false); });
7498
+ selectOptsInp.addEventListener('change', function() { applyContentChange('pp-select-options', true); });
7245
7499
  }
7246
7500
  }
7247
7501
 
@@ -7732,14 +7986,21 @@ function attachChangeObserver() {
7732
7986
  changeObserverDoc = null;
7733
7987
  }
7734
7988
  changeObserver = new MutationObserver(function(mutations) {
7989
+ if (suppressIframeMutationDirty > 0) return;
7735
7990
  var hasMeaningfulMutation = false;
7736
7991
  for (var mi = 0; mi < mutations.length; mi++) {
7737
- if (!isTreeHoverOnlyClassMutation(mutations[mi])) {
7992
+ if (!isEditorChromeOnlyClassMutation(mutations[mi])) {
7738
7993
  hasMeaningfulMutation = true;
7739
7994
  break;
7740
7995
  }
7741
7996
  }
7742
7997
  if (!hasMeaningfulMutation) return;
7998
+ var activeId = '';
7999
+ try { activeId = document.activeElement && document.activeElement.id ? String(document.activeElement.id) : ''; } catch(_) {}
8000
+ if (activeId === 'pp-html' || activeId === 'pp-text' || activeId === 'pp-select-options') {
8001
+ scheduleDomTreeRefresh();
8002
+ return;
8003
+ }
7743
8004
  // Dirty state is derived from changesets baseline + stateChanges (not raw DOM mutations).
7744
8005
  // Host scripts can replace selected nodes every few frames (e.g. A/B tool observers).
7745
8006
  // Keep selection sticky by re-resolving from fingerprint.
@@ -8235,7 +8496,9 @@ window.addEventListener('load', function() {
8235
8496
  return;
8236
8497
  }
8237
8498
  hideIframeLoadError();
8238
- emitEditorUrlChanged(iframe.src || docUrl);
8499
+ var iframeLiveUrl = '';
8500
+ try { iframeLiveUrl = String(iframe.contentWindow.location.href || ''); } catch(_) {}
8501
+ emitEditorUrlChanged(iframeLiveUrl || docUrl || iframe.src || '');
8239
8502
  // Stale events: src may already be the proxy URL while the document is still
8240
8503
  // about:blank (e.g. src cleared then reset to force reload). Ask sync path to retry.
8241
8504
  if (docUrl === 'about:blank') {
@@ -8620,6 +8883,11 @@ function createVisualEditorMiddleware(options) {
8620
8883
  }
8621
8884
  if (chunks.length > 0) requestBody = Buffer.concat(chunks);
8622
8885
  }
8886
+ const secFetchMode = (req.headers?.["sec-fetch-mode"] || "").toLowerCase();
8887
+ const secFetchDest = (req.headers?.["sec-fetch-dest"] || "").toLowerCase();
8888
+ const isLikelyDocumentNavigation = secFetchMode === "navigate" || secFetchDest === "iframe" || secFetchDest === "document" || secFetchDest === "nested-document" || secFetchDest === "frame";
8889
+ const isLikelyFetchOrXHR = secFetchDest === "empty" && (secFetchMode === "cors" || secFetchMode === "same-origin" || secFetchMode === "no-cors");
8890
+ const passthroughUpstreamRedirects = (method === "GET" || method === "HEAD") && (isLikelyDocumentNavigation || !isLikelyFetchOrXHR);
8623
8891
  const upstreamTimeoutMs = 12e4;
8624
8892
  const ac = new AbortController();
8625
8893
  const timeoutId = setTimeout(() => ac.abort(), upstreamTimeoutMs);
@@ -8629,7 +8897,7 @@ function createVisualEditorMiddleware(options) {
8629
8897
  method,
8630
8898
  headers: fetchHeaders,
8631
8899
  body: requestBody ? Buffer.from(requestBody) : null,
8632
- redirect: "follow",
8900
+ redirect: passthroughUpstreamRedirects ? "manual" : "follow",
8633
8901
  signal: ac.signal
8634
8902
  });
8635
8903
  } catch (fetchErr) {
@@ -8653,12 +8921,43 @@ function createVisualEditorMiddleware(options) {
8653
8921
  return;
8654
8922
  }
8655
8923
  clearTimeout(timeoutId);
8924
+ if (passthroughUpstreamRedirects && upstream.status >= 300 && upstream.status < 400) {
8925
+ const locationHeader = upstream.headers.get("location") || upstream.headers.get("Location");
8926
+ if (locationHeader) {
8927
+ try {
8928
+ const redirectTarget = new URL(locationHeader, targetUrl);
8929
+ if (redirectTarget.origin === origin) {
8930
+ const proxyRedirect = new URL(proxyRootForRequest, "http://localhost");
8931
+ proxyRedirect.searchParams.set("password", password);
8932
+ proxyRedirect.searchParams.set("url", redirectTarget.toString());
8933
+ url.searchParams.forEach((value, key) => {
8934
+ if (key === "url" || key === "password") return;
8935
+ proxyRedirect.searchParams.set(key, value);
8936
+ });
8937
+ res.statusCode = upstream.status === 303 ? 303 : 302;
8938
+ res.setHeader("Location", `${proxyRedirect.pathname}${proxyRedirect.search}`);
8939
+ res.setHeader("Cache-Control", "no-store");
8940
+ res.setHeader("Access-Control-Allow-Origin", "*");
8941
+ setFrameHeaders(req, res);
8942
+ res.end();
8943
+ return;
8944
+ }
8945
+ res.statusCode = 502;
8946
+ res.setHeader("Content-Type", "application/json");
8947
+ res.end(
8948
+ JSON.stringify({
8949
+ error: "Cross-origin redirect blocked in editor preview",
8950
+ location: redirectTarget.toString(),
8951
+ from: targetUrl
8952
+ })
8953
+ );
8954
+ return;
8955
+ } catch (_) {
8956
+ }
8957
+ }
8958
+ }
8656
8959
  const responseContentType = upstream.headers.get("content-type") || "";
8657
8960
  const isHtmlResponse = responseContentType.includes("text/html");
8658
- const secFetchMode = (req.headers?.["sec-fetch-mode"] || "").toLowerCase();
8659
- const secFetchDest = (req.headers?.["sec-fetch-dest"] || "").toLowerCase();
8660
- const isLikelyDocumentNavigation = secFetchMode === "navigate" || secFetchDest === "iframe" || secFetchDest === "document" || secFetchDest === "nested-document" || secFetchDest === "frame";
8661
- const isLikelyFetchOrXHR = secFetchDest === "empty" && (secFetchMode === "cors" || secFetchMode === "same-origin" || secFetchMode === "no-cors");
8662
8961
  const shouldInjectHtmlBridge = isHtmlResponse && (isLikelyDocumentNavigation || !isLikelyFetchOrXHR);
8663
8962
  if (!isHtmlResponse || !shouldInjectHtmlBridge) {
8664
8963
  const binary = Buffer.from(await upstream.arrayBuffer());
@@ -8712,6 +9011,18 @@ ${iframeAlwaysShowCssGuardScript}
8712
9011
  /<meta[^>]+name=["']?\s*content-security-policy\s*["']?[^>]*>/gi,
8713
9012
  ""
8714
9013
  );
9014
+ html = html.replace(
9015
+ /(<meta[^>]+http-equiv=["']?refresh["']?[^>]*content=["'][^"']*url=)([^"';]+)(["'][^>]*>)/gi,
9016
+ (match, prefix, urlPart, suffix) => {
9017
+ try {
9018
+ const abs = new URL(String(urlPart).trim(), origin).toString();
9019
+ if (new URL(abs).origin !== origin) return match;
9020
+ return `${prefix}${proxyBase}${encodeURIComponent(abs)}${suffix}`;
9021
+ } catch {
9022
+ return match;
9023
+ }
9024
+ }
9025
+ );
8715
9026
  const runtimePreflightScript = `<script>(function(){try{
8716
9027
  var TARGET_ORIGIN=${JSON.stringify(origin)};
8717
9028
  var TARGET_PAGE_URL=${JSON.stringify(targetUrl)};
@@ -8918,6 +9229,8 @@ try{if(window.history&&typeof window.history.pushState==="function"){var nativeP
8918
9229
  try{if(window.history&&typeof window.history.replaceState==="function"){var nativeReplaceState=window.history.replaceState;window.history.replaceState=function(){var ret=nativeReplaceState.apply(window.history,arguments);setTimeout(notifyEditorUrlChanged,0);return ret;};}}catch(_){}
8919
9230
  try{window.addEventListener("popstate",notifyEditorUrlChanged,true);}catch(_){}
8920
9231
  try{window.addEventListener("hashchange",notifyEditorUrlChanged,true);}catch(_){}
9232
+ try{window.addEventListener("pageshow",notifyEditorUrlChanged,true);}catch(_){}
9233
+ try{window.addEventListener("load",function(){setTimeout(notifyEditorUrlChanged,0);},true);}catch(_){}
8921
9234
  function isSkippable(raw){if(!raw||typeof raw!=="string")return true;return raw.startsWith("data:")||raw.startsWith("blob:")||raw.startsWith("javascript:")||raw.startsWith("#");}
8922
9235
  function toAbsolute(raw){if(isSkippable(raw))return raw;try{var base=raw.startsWith("/")||raw.startsWith("//")?TARGET_ORIGIN:TARGET_PAGE_URL;return new URL(raw,base).toString();}catch(_){return raw;}}
8923
9236
  function toProxy(raw){
@@ -8946,6 +9259,9 @@ function toProxy(raw){
8946
9259
  var nativeAssign=window.location.assign?window.location.assign.bind(window.location):null;
8947
9260
  var nativeReplace=window.location.replace?window.location.replace.bind(window.location):null;
8948
9261
  function safeNavigate(raw,mode){var abs=toAbsolute(raw);var prox=toProxy(raw);if(!prox){try{console.warn("[conversion-proxy] redirect blocked",{mode:mode,requested:raw,resolved:abs,origin:TARGET_ORIGIN});}catch(_){}return false;}try{console.info("[conversion-proxy] redirect intercepted",{mode:mode,requested:raw,resolved:abs,proxied:prox});if(mode==="replace"&&nativeReplace){nativeReplace(prox);return true;}if(nativeAssign){nativeAssign(prox);return true;}window.location.href=prox;return true;}catch(err){try{console.warn("[conversion-proxy] redirect interception failed",{mode:mode,requested:raw,resolved:abs,proxied:prox,error:err&&err.message?err.message:String(err)});}catch(_){}return false;}}
9262
+ function interceptMetaRefresh(){try{var metas=document.querySelectorAll('meta[http-equiv="refresh" i],meta[http-equiv="Refresh"]');for(var i=0;i<metas.length;i++){var content=metas[i].getAttribute("content")||"";var m=content.match(/url=(.+)$/i);if(!m)continue;var raw=m[1].trim().replace(/^['"]|['"]$/g,"");if(safeNavigate(raw,"replace"))metas[i].parentNode&&metas[i].parentNode.removeChild(metas[i]);}}catch(_){}}
9263
+ try{interceptMetaRefresh();}catch(_){}
9264
+ try{document.addEventListener("DOMContentLoaded",interceptMetaRefresh,true);}catch(_){}
8949
9265
  try{if(nativeAssign){window.location.assign=function(url){return safeNavigate(url,"assign");};}}catch(_){}
8950
9266
  try{if(nativeReplace){window.location.replace=function(url){return safeNavigate(url,"replace");};}}catch(_){}
8951
9267
  try{var hrefDesc=Object.getOwnPropertyDescriptor(Location.prototype,"href");if(hrefDesc&&hrefDesc.configurable&&hrefDesc.get&&hrefDesc.set){Object.defineProperty(Location.prototype,"href",{configurable:true,enumerable:hrefDesc.enumerable,get:function(){return hrefDesc.get.call(this);},set:function(v){safeNavigate(v,"assign");}});}}catch(_){}
package/dist/vite.js CHANGED
@@ -1031,24 +1031,16 @@ button.var-tab.active{
1031
1031
  .lp-body #tab-dom-tree, .lp-body #tab-elements{
1032
1032
  padding-top:4px;
1033
1033
  }
1034
- .lp-body #tab-elements .dt-lbl{
1035
- font-size: 12px;
1036
- font-style: normal;
1037
- font-weight: 500;
1038
- line-height: 14px;
1039
- letter-spacing: -0.1px;
1040
- }
1041
- .lp-body #tab-elements #elements-root > .dt-row{
1042
- padding-left: 16px!important;
1043
- }
1044
- .lp-body #dom-tree-root .dt-lbl{
1034
+ .lp-body #dom-tree-root .dt-lbl,
1035
+ .lp-body #elements-root .dt-lbl{
1045
1036
  color: var(--content-subtle, #737373);
1046
1037
  font-style: normal;
1047
1038
  font-weight: 400;
1048
- line-height: var(--font-leading-3, 12px); /* 100% */
1039
+ line-height: var(--font-leading-3, 12px);
1049
1040
  letter-spacing: -0.1px;
1050
1041
  }
1051
- .lp-body #dom-tree-root .dt-lbl .dt-tag{
1042
+ .lp-body #dom-tree-root .dt-lbl .dt-tag,
1043
+ .lp-body #elements-root .dt-lbl .dt-tag{
1052
1044
  color: var(--content-strong, #171717);
1053
1045
  font-family: "JetBrains Mono";
1054
1046
  font-size: 12px;
@@ -1062,7 +1054,6 @@ padding:3px 6px;
1062
1054
  display:inline-block;
1063
1055
  margin-right:4px;
1064
1056
  }
1065
- .lp-body #tab-elements .dt-row .dt-chev.dt-spacer{display:none;}
1066
1057
  .lp-body #tab-dom-tree .dt-row .dt-ico{display:none;}
1067
1058
  .lp-body, .section-components-body{flex:1;overflow-y:auto}
1068
1059
  .lp-body::-webkit-scrollbar, .section-components-body::-webkit-scrollbar{width:3px}
@@ -3151,6 +3142,7 @@ var PROP_META = {
3151
3142
  'pp-value': {label:'Value', cssProp:null},
3152
3143
  'pp-text': {label:'Inner text', cssProp:null},
3153
3144
  'pp-html': {label:'Inner HTML', cssProp:null},
3145
+ 'pp-select-options': {label:'Options', cssProp:null},
3154
3146
  'pp-mob-css': {label:'Mobile CSS', cssProp:null},
3155
3147
  'pp-tab-css': {label:'Tablet CSS', cssProp:null},
3156
3148
  };
@@ -3167,6 +3159,7 @@ function getOriginalValue(inputId, el) {
3167
3159
  switch (inputId) {
3168
3160
  case 'pp-text': return el.innerText || '';
3169
3161
  case 'pp-html': return el.innerHTML || '';
3162
+ case 'pp-select-options': return getSelectOptionsText(el);
3170
3163
  case 'pp-cls': return el.className || '';
3171
3164
  case 'pp-id': return el.id || '';
3172
3165
  case 'pp-href': return el.getAttribute('href') || '';
@@ -3316,6 +3309,7 @@ function revertChangeOnDom(change) {
3316
3309
  switch (change.inputId) {
3317
3310
  case 'pp-text': el.innerText = orig; break;
3318
3311
  case 'pp-html': el.innerHTML = orig; break;
3312
+ case 'pp-select-options': applySelectOptionsFromText(el, orig); break;
3319
3313
  case 'pp-cls': el.className = orig; break;
3320
3314
  case 'pp-id': el.id = orig; break;
3321
3315
  case 'pp-css': orig ? el.setAttribute('style', orig) : el.removeAttribute('style'); break;
@@ -4974,6 +4968,8 @@ function stateChangeToChainSet(c) {
4974
4968
  return { selector: c.selector, type: 'content', value: c.value, vveTs: c.vveTs || nextHistoryTimestamp() };
4975
4969
  case 'pp-html':
4976
4970
  return { selector: c.selector, type: 'content', html: c.value, vveTs: c.vveTs || nextHistoryTimestamp() };
4971
+ case 'pp-select-options':
4972
+ return { selector: c.selector, type: 'content', html: selectOptionsTextToHtml(c.value), vveTs: c.vveTs || nextHistoryTimestamp() };
4977
4973
  case 'pp-cls':
4978
4974
  return { selector: c.selector, type: 'attribute', attribute: 'class', value: c.value, vveTs: c.vveTs || nextHistoryTimestamp() };
4979
4975
  case 'pp-id':
@@ -5587,6 +5583,10 @@ function setTreeHoverHighlight(el) {
5587
5583
  }
5588
5584
 
5589
5585
  function isTreeHoverOnlyClassMutation(mutation) {
5586
+ return isEditorChromeOnlyClassMutation(mutation);
5587
+ }
5588
+
5589
+ function isEditorChromeOnlyClassMutation(mutation) {
5590
5590
  if (!mutation || mutation.type !== 'attributes' || mutation.attributeName !== 'class') return false;
5591
5591
  var oldClass = String(mutation.oldValue || '');
5592
5592
  var target = mutation.target;
@@ -5594,7 +5594,12 @@ function isTreeHoverOnlyClassMutation(mutation) {
5594
5594
  try {
5595
5595
  nextClass = target && typeof target.className === 'string' ? target.className : '';
5596
5596
  } catch(_) {}
5597
- return oldClass.indexOf('vve-tree-hover') >= 0 || String(nextClass).indexOf('vve-tree-hover') >= 0;
5597
+ var combined = oldClass + ' ' + nextClass;
5598
+ return (
5599
+ combined.indexOf('vve-tree-hover') >= 0 ||
5600
+ combined.indexOf('vve-selected') >= 0 ||
5601
+ combined.indexOf('vve-dragging') >= 0
5602
+ );
5598
5603
  }
5599
5604
 
5600
5605
  function setDragHandleActive(on) {
@@ -5644,7 +5649,13 @@ function positionSelectionToolbar() {
5644
5649
  if (!bar || !liveSelected || !iframe || !iframe.contentWindow || !panel) return;
5645
5650
  if (selectedEl !== liveSelected) {
5646
5651
  selectedEl = liveSelected;
5647
- renderRightPanel(liveSelected);
5652
+ if (!document.activeElement || (
5653
+ document.activeElement.id !== 'pp-html' &&
5654
+ document.activeElement.id !== 'pp-text' &&
5655
+ document.activeElement.id !== 'pp-select-options'
5656
+ )) {
5657
+ renderRightPanel(liveSelected);
5658
+ }
5648
5659
  syncDomTreeSelection();
5649
5660
  }
5650
5661
  var elR = getIframeElementVisualRect(selectedEl);
@@ -6178,31 +6189,156 @@ function renderElementsTree(filterRaw) {
6178
6189
  return;
6179
6190
  }
6180
6191
 
6181
- function nodeIcon(tag) {
6182
- tag = (tag || '').toLowerCase();
6183
- if (/^h[1-6]$/.test(tag)) return 'bi bi-type-h1';
6184
- if (tag === 'a') return 'bi bi-link-45deg';
6185
- if (tag === 'img') return 'bi bi-image';
6186
- if (tag === 'section' || tag === 'main' || tag === 'article' || tag === 'header' || tag === 'footer' || tag === 'nav') return 'bi bi-layout-three-columns';
6187
- if (tag === 'button' || tag === 'input' || tag === 'select' || tag === 'textarea') return 'bi bi-ui-radios';
6188
- if (tag === 'ul' || tag === 'ol') return 'bi bi-list-ul';
6189
- if (tag === 'li') return 'bi bi-dot';
6190
- if (tag === 'svg') return 'bi bi-bezier2';
6191
- if (tag === 'p' || tag === 'span') return 'bi bi-text-left';
6192
- return 'bi bi-square';
6193
- }
6192
+ function nodeIcon(tag) {
6193
+ tag = (tag || '').toLowerCase();
6194
+
6195
+ // Headings
6196
+ if (/^h[1-6]$/.test(tag)) return 'bi bi-type-h1';
6197
+
6198
+ // Document & metadata
6199
+ if (tag === 'html') return 'bi bi-filetype-html';
6200
+ if (tag === 'head') return 'bi bi-file-earmark-code';
6201
+ if (tag === 'body') return 'bi bi-file-earmark-richtext';
6202
+ if (tag === 'title') return 'bi bi-card-heading';
6203
+ if (tag === 'meta') return 'bi bi-info-circle';
6204
+ if (tag === 'link') return 'bi bi-link';
6205
+ if (tag === 'style') return 'bi bi-filetype-css';
6206
+ if (tag === 'script') return 'bi bi-filetype-js';
6207
+ if (tag === 'noscript') return 'bi bi-slash-circle';
6208
+ if (tag === 'base') return 'bi bi-house-gear';
6209
+
6210
+ // Sectioning & layout
6211
+ if (tag === 'section' || tag === 'main' || tag === 'article' ||
6212
+ tag === 'header' || tag === 'footer' || tag === 'nav' || tag === 'aside')
6213
+ return 'bi bi-layout-three-columns';
6214
+ if (tag === 'div') return 'bi bi-square';
6215
+ if (tag === 'address') return 'bi bi-geo-alt';
6216
+
6217
+ // Text content
6218
+ if (tag === 'p' || tag === 'span') return 'bi bi-text-left';
6219
+ if (tag === 'blockquote' || tag === 'q') return 'bi bi-blockquote-left';
6220
+ if (tag === 'pre') return 'bi bi-code-square';
6221
+ if (tag === 'code') return 'bi bi-code-slash';
6222
+ if (tag === 'kbd') return 'bi bi-keyboard';
6223
+ if (tag === 'samp') return 'bi bi-terminal';
6224
+ if (tag === 'var') return 'bi bi-braces';
6225
+ if (tag === 'cite') return 'bi bi-quote';
6226
+ if (tag === 'abbr') return 'bi bi-fonts';
6227
+ if (tag === 'time') return 'bi bi-clock';
6228
+ if (tag === 'mark') return 'bi bi-highlighter';
6229
+ if (tag === 'small') return 'bi bi-type';
6230
+ if (tag === 'sub') return 'bi bi-subscript';
6231
+ if (tag === 'sup') return 'bi bi-superscript';
6232
+ if (tag === 'br') return 'bi bi-arrow-return-left';
6233
+ if (tag === 'wbr') return 'bi bi-distribute-horizontal';
6234
+
6235
+ // Inline formatting
6236
+ if (tag === 'strong' || tag === 'b') return 'bi bi-type-bold';
6237
+ if (tag === 'em' || tag === 'i') return 'bi bi-type-italic';
6238
+ if (tag === 'u' || tag === 'ins') return 'bi bi-type-underline';
6239
+ if (tag === 's' || tag === 'del' || tag === 'strike') return 'bi bi-type-strikethrough';
6240
+
6241
+ // Lists
6242
+ if (tag === 'ul') return 'bi bi-list-ul';
6243
+ if (tag === 'ol') return 'bi bi-list-ol';
6244
+ if (tag === 'li') return 'bi bi-dot';
6245
+ if (tag === 'dl') return 'bi bi-card-list';
6246
+ if (tag === 'dt') return 'bi bi-tag';
6247
+ if (tag === 'dd') return 'bi bi-text-indent-left';
6248
+
6249
+ // Links & navigation
6250
+ if (tag === 'a') return 'bi bi-link-45deg';
6251
+
6252
+ // Media
6253
+ if (tag === 'img') return 'bi bi-image';
6254
+ if (tag === 'picture') return 'bi bi-images';
6255
+ if (tag === 'figure') return 'bi bi-card-image';
6256
+ if (tag === 'figcaption') return 'bi bi-card-text';
6257
+ if (tag === 'video') return 'bi bi-camera-video';
6258
+ if (tag === 'audio') return 'bi bi-music-note-beamed';
6259
+ if (tag === 'source') return 'bi bi-cloud-arrow-down';
6260
+ if (tag === 'track') return 'bi bi-badge-cc';
6261
+ if (tag === 'iframe') return 'bi bi-window';
6262
+ if (tag === 'embed' || tag === 'object') return 'bi bi-box-arrow-in-down';
6263
+ if (tag === 'param') return 'bi bi-sliders';
6264
+ if (tag === 'canvas') return 'bi bi-easel';
6265
+ if (tag === 'map') return 'bi bi-map';
6266
+ if (tag === 'area') return 'bi bi-bounding-box';
6267
+
6268
+ // Vector / math
6269
+ if (tag === 'svg') return 'bi bi-bezier2';
6270
+ if (tag === 'path') return 'bi bi-bezier';
6271
+ if (tag === 'circle') return 'bi bi-circle';
6272
+ if (tag === 'rect') return 'bi bi-square';
6273
+ if (tag === 'line') return 'bi bi-slash-lg';
6274
+ if (tag === 'polygon') return 'bi bi-pentagon';
6275
+ if (tag === 'polyline') return 'bi bi-share';
6276
+ if (tag === 'ellipse') return 'bi bi-circle-half';
6277
+ if (tag === 'g') return 'bi bi-collection';
6278
+ if (tag === 'use') return 'bi bi-arrow-repeat';
6279
+ if (tag === 'defs') return 'bi bi-bookmark';
6280
+ if (tag === 'symbol') return 'bi bi-star';
6281
+ if (tag === 'text') return 'bi bi-fonts';
6282
+ if (tag === 'math') return 'bi bi-calculator';
6283
+
6284
+ // Forms
6285
+ if (tag === 'form') return 'bi bi-file-earmark-check';
6286
+ if (tag === 'fieldset') return 'bi bi-bounding-box-circles';
6287
+ if (tag === 'legend') return 'bi bi-tag-fill';
6288
+ if (tag === 'label') return 'bi bi-tag';
6289
+ if (tag === 'input') return 'bi bi-input-cursor-text';
6290
+ if (tag === 'button') return 'bi bi-ui-radios';
6291
+ if (tag === 'select') return 'bi bi-menu-button';
6292
+ if (tag === 'option') return 'bi bi-check2-square';
6293
+ if (tag === 'optgroup') return 'bi bi-list-nested';
6294
+ if (tag === 'textarea') return 'bi bi-textarea-resize';
6295
+ if (tag === 'datalist') return 'bi bi-list-columns';
6296
+ if (tag === 'output') return 'bi bi-box-arrow-right';
6297
+ if (tag === 'progress') return 'bi bi-bar-chart-line';
6298
+ if (tag === 'meter') return 'bi bi-speedometer2';
6299
+
6300
+ // Tables
6301
+ if (tag === 'table') return 'bi bi-table';
6302
+ if (tag === 'caption') return 'bi bi-card-heading';
6303
+ if (tag === 'thead') return 'bi bi-layout-text-window';
6304
+ if (tag === 'tbody') return 'bi bi-layout-text-sidebar';
6305
+ if (tag === 'tfoot') return 'bi bi-layout-text-window-reverse';
6306
+ if (tag === 'tr') return 'bi bi-grip-horizontal';
6307
+ if (tag === 'th') return 'bi bi-grid-3x3-gap-fill';
6308
+ if (tag === 'td') return 'bi bi-grid-3x3-gap';
6309
+ if (tag === 'col') return 'bi bi-layout-three-columns';
6310
+ if (tag === 'colgroup') return 'bi bi-columns-gap';
6311
+
6312
+ // Interactive / disclosure
6313
+ if (tag === 'details') return 'bi bi-caret-down-square';
6314
+ if (tag === 'summary') return 'bi bi-card-text';
6315
+ if (tag === 'dialog') return 'bi bi-chat-square-text';
6316
+ if (tag === 'menu') return 'bi bi-list';
6317
+
6318
+ // Web components / templating
6319
+ if (tag === 'template') return 'bi bi-file-earmark-code';
6320
+ if (tag === 'slot') return 'bi bi-box-seam';
6321
+
6322
+ // Ruby annotation
6323
+ if (tag === 'ruby' || tag === 'rt' || tag === 'rp' || tag === 'rb')
6324
+ return 'bi bi-translate';
6325
+ if (tag === 'bdi' || tag === 'bdo') return 'bi bi-arrow-left-right';
6326
+
6327
+ // Misc
6328
+ if (tag === 'hr') return 'bi bi-hr';
6329
+ if (tag === '#text' || tag === 'text-node') return 'bi bi-cursor-text';
6330
+ if (tag === '#comment') return 'bi bi-chat-left-text';
6331
+
6332
+ return 'bi bi-square';
6333
+ }
6194
6334
 
6195
- function labelFor(el) {
6196
- var tag = (el.tagName || '').toLowerCase();
6197
- return tag.toUpperCase();
6198
- }
6199
6335
 
6200
6336
  var nodes = collectEditorInsertedElements(doc);
6201
6337
 
6202
6338
  root.innerHTML = '';
6203
6339
  for (var i = 0; i < nodes.length; i++) {
6204
6340
  var el = nodes[i];
6205
- var lblText = labelFor(el);
6341
+ var lblText = domTreeLabelFor(el);
6206
6342
  if (filterText && lblText.toLowerCase().indexOf(filterText) < 0) continue;
6207
6343
 
6208
6344
  var row = document.createElement('div');
@@ -6221,7 +6357,7 @@ function renderElementsTree(filterRaw) {
6221
6357
 
6222
6358
  var lbl = document.createElement('div');
6223
6359
  lbl.className = 'dt-lbl';
6224
- lbl.textContent = lblText;
6360
+ setDomTreeLabelContent(lbl, el);
6225
6361
  lbl.title = buildSelector(el);
6226
6362
 
6227
6363
  row.appendChild(spacer);
@@ -6246,11 +6382,35 @@ function renderElementsTree(filterRaw) {
6246
6382
  };
6247
6383
  }
6248
6384
 
6385
+ function domTreeLabelClassExcluded(className) {
6386
+ var c = String(className || '');
6387
+ return !c || c === 'vve-selected' || c === 'vve-tree-hover' || c.indexOf('vve-') === 0;
6388
+ }
6389
+
6390
+ function domTreeLabelClassString(el) {
6391
+ if (!el) return '';
6392
+ try {
6393
+ if (el.classList && el.classList.length) {
6394
+ var out = [];
6395
+ for (var i = 0; i < el.classList.length; i++) {
6396
+ var cls = el.classList[i];
6397
+ if (!domTreeLabelClassExcluded(cls)) out.push(cls);
6398
+ }
6399
+ return out.join(' ');
6400
+ }
6401
+ } catch(_) {}
6402
+ var cn = el.className;
6403
+ if (cn && typeof cn === 'object' && cn.baseVal != null) cn = String(cn.baseVal);
6404
+ return typeof cn === 'string' ? cn : '';
6405
+ }
6406
+
6249
6407
  function domTreeLabelSuffix(el) {
6250
6408
  if (el.id != null && el.id !== '') return '#' + String(el.id).slice(0, 40);
6251
- var cn = el.className && typeof el.className === 'string' ? el.className.trim() : '';
6409
+ var cn = domTreeLabelClassString(el).trim();
6252
6410
  if (cn) {
6253
- var parts = cn.split(/s+/).filter(function(x) { return x.indexOf('vve-') !== 0; }).slice(0, 2).join('.');
6411
+ var parts = cn.split(/s+/).filter(function(x) {
6412
+ return x && !domTreeLabelClassExcluded(x);
6413
+ }).slice(0, 2).join('.');
6254
6414
  if (parts) return '.' + parts.slice(0, 56);
6255
6415
  }
6256
6416
  return '';
@@ -6438,11 +6598,64 @@ function isFormControlElement(el) {
6438
6598
  function shouldShowInnerContentFields(el) {
6439
6599
  if (!el || el.nodeType !== 1) return false;
6440
6600
  var tag = (el.tagName || '').toLowerCase();
6441
- if (tag === 'input' || tag === 'textarea' || tag === 'video') return false;
6601
+ if (tag === 'input' || tag === 'textarea' || tag === 'select' || tag === 'video') return false;
6442
6602
  if (isEmbeddedVideoIframe(el)) return false;
6443
6603
  return true;
6444
6604
  }
6445
6605
 
6606
+ function getSelectOptionsText(el) {
6607
+ if (!el || el.nodeType !== 1) return '';
6608
+ try {
6609
+ var opts = el.querySelectorAll('option');
6610
+ var lines = [];
6611
+ for (var i = 0; i < opts.length; i++) {
6612
+ lines.push(opts[i].textContent || '');
6613
+ }
6614
+ return lines.join('\\n');
6615
+ } catch(_) {
6616
+ return '';
6617
+ }
6618
+ }
6619
+
6620
+ function selectOptionsTextToHtml(text) {
6621
+ var lines = String(text == null ? '' : text).split('\\n');
6622
+ var parts = [];
6623
+ for (var i = 0; i < lines.length; i++) {
6624
+ var label = lines[i];
6625
+ if (!label && i === lines.length - 1 && lines.length > 1) continue;
6626
+ parts.push('<option>' + esc(label) + '</option>');
6627
+ }
6628
+ if (!parts.length) parts.push('<option>Option 1</option>');
6629
+ return parts.join('');
6630
+ }
6631
+
6632
+ function applySelectOptionsFromText(el, text) {
6633
+ if (!el || el.nodeType !== 1) return;
6634
+ beginSuppressIframeMutationDirty();
6635
+ try {
6636
+ var prevValue = '';
6637
+ try { prevValue = el.value || ''; } catch(_) {}
6638
+ var doc = el.ownerDocument;
6639
+ while (el.firstChild) el.removeChild(el.firstChild);
6640
+ var lines = String(text == null ? '' : text).split('\\n');
6641
+ if (!lines.length || (lines.length === 1 && !String(lines[0] || '').trim())) {
6642
+ lines = ['Option 1', 'Option 2'];
6643
+ }
6644
+ for (var i = 0; i < lines.length; i++) {
6645
+ var label = lines[i];
6646
+ if (!label && i === lines.length - 1) continue;
6647
+ var opt = doc.createElement('option');
6648
+ opt.textContent = label;
6649
+ el.appendChild(opt);
6650
+ }
6651
+ if (prevValue) {
6652
+ try { el.value = prevValue; } catch(_) {}
6653
+ }
6654
+ } finally {
6655
+ endSuppressIframeMutationDirty();
6656
+ }
6657
+ }
6658
+
6446
6659
  function elementHasHtmlChildren(el) {
6447
6660
  if (!el || el.nodeType !== 1) return false;
6448
6661
  try {
@@ -7101,6 +7314,12 @@ function renderRightPanel(el, options) {
7101
7314
  subLbl('Placeholder') +
7102
7315
  '<input class="pr-inp" id="pp-ph" type="text" value="'+esc(el.getAttribute('placeholder')||'')+'" style="width:100%;margin-bottom:8px">';
7103
7316
  }
7317
+ if (tag === 'select') {
7318
+ contentHtml +=
7319
+ subLbl('Options') +
7320
+ '<div style="font-size:11px;color:var(--text-3);margin:-4px 0 8px">One option label per line</div>' +
7321
+ '<textarea class="pr-inp" id="pp-select-options" style="width:100%;min-height:80px;font-family:var(--font-mono);font-size:11px">'+esc(getSelectOptionsText(el))+'</textarea>';
7322
+ }
7104
7323
  if (shouldShowInnerContentFields(el)) {
7105
7324
  if (elementHasHtmlChildren(el)) {
7106
7325
  contentHtml +=
@@ -7208,18 +7427,49 @@ function renderRightPanel(el, options) {
7208
7427
  function wireContentFieldSync(el, sel) {
7209
7428
  var textInp = document.getElementById('pp-text');
7210
7429
  var htmlInp = document.getElementById('pp-html');
7430
+ var selectOptsInp = document.getElementById('pp-select-options');
7211
7431
  var syncing = false;
7212
- function applyContentChange(changedId) {
7432
+ var htmlApplyTimer = null;
7433
+ var selectApplyTimer = null;
7434
+
7435
+ function applyHtmlToElement(htmlValue) {
7436
+ beginSuppressIframeMutationDirty();
7437
+ try {
7438
+ el.innerHTML = htmlValue;
7439
+ } finally {
7440
+ endSuppressIframeMutationDirty();
7441
+ }
7442
+ }
7443
+
7444
+ function applyContentChange(changedId, immediate) {
7213
7445
  if (syncing) return;
7446
+ if (changedId === 'pp-html' && !immediate) {
7447
+ if (htmlApplyTimer) clearTimeout(htmlApplyTimer);
7448
+ htmlApplyTimer = setTimeout(function() { applyContentChange('pp-html', true); }, 250);
7449
+ return;
7450
+ }
7451
+ if (changedId === 'pp-select-options' && !immediate) {
7452
+ if (selectApplyTimer) clearTimeout(selectApplyTimer);
7453
+ selectApplyTimer = setTimeout(function() { applyContentChange('pp-select-options', true); }, 250);
7454
+ return;
7455
+ }
7214
7456
  syncing = true;
7215
7457
  try {
7216
7458
  var orig = getOriginalValue(changedId, el);
7217
7459
  if (changedId === 'pp-text') {
7218
- el.innerText = textInp.value;
7460
+ beginSuppressIframeMutationDirty();
7461
+ try {
7462
+ el.innerText = textInp.value;
7463
+ } finally {
7464
+ endSuppressIframeMutationDirty();
7465
+ }
7219
7466
  if (htmlInp) htmlInp.value = el.innerHTML;
7220
7467
  logChange(sel, 'pp-text', textInp.value, el, orig);
7468
+ } else if (changedId === 'pp-select-options') {
7469
+ applySelectOptionsFromText(el, selectOptsInp.value);
7470
+ logChange(sel, 'pp-select-options', selectOptsInp.value, el, orig);
7221
7471
  } else {
7222
- el.innerHTML = htmlInp.value;
7472
+ applyHtmlToElement(htmlInp.value);
7223
7473
  if (textInp) textInp.value = el.innerText;
7224
7474
  logChange(sel, 'pp-html', htmlInp.value, el, orig);
7225
7475
  }
@@ -7232,8 +7482,12 @@ function wireContentFieldSync(el, sel) {
7232
7482
  textInp.addEventListener('change', function() { applyContentChange('pp-text'); });
7233
7483
  }
7234
7484
  if (htmlInp) {
7235
- htmlInp.addEventListener('input', function() { applyContentChange('pp-html'); });
7236
- htmlInp.addEventListener('change', function() { applyContentChange('pp-html'); });
7485
+ htmlInp.addEventListener('input', function() { applyContentChange('pp-html', false); });
7486
+ htmlInp.addEventListener('change', function() { applyContentChange('pp-html', true); });
7487
+ }
7488
+ if (selectOptsInp) {
7489
+ selectOptsInp.addEventListener('input', function() { applyContentChange('pp-select-options', false); });
7490
+ selectOptsInp.addEventListener('change', function() { applyContentChange('pp-select-options', true); });
7237
7491
  }
7238
7492
  }
7239
7493
 
@@ -7724,14 +7978,21 @@ function attachChangeObserver() {
7724
7978
  changeObserverDoc = null;
7725
7979
  }
7726
7980
  changeObserver = new MutationObserver(function(mutations) {
7981
+ if (suppressIframeMutationDirty > 0) return;
7727
7982
  var hasMeaningfulMutation = false;
7728
7983
  for (var mi = 0; mi < mutations.length; mi++) {
7729
- if (!isTreeHoverOnlyClassMutation(mutations[mi])) {
7984
+ if (!isEditorChromeOnlyClassMutation(mutations[mi])) {
7730
7985
  hasMeaningfulMutation = true;
7731
7986
  break;
7732
7987
  }
7733
7988
  }
7734
7989
  if (!hasMeaningfulMutation) return;
7990
+ var activeId = '';
7991
+ try { activeId = document.activeElement && document.activeElement.id ? String(document.activeElement.id) : ''; } catch(_) {}
7992
+ if (activeId === 'pp-html' || activeId === 'pp-text' || activeId === 'pp-select-options') {
7993
+ scheduleDomTreeRefresh();
7994
+ return;
7995
+ }
7735
7996
  // Dirty state is derived from changesets baseline + stateChanges (not raw DOM mutations).
7736
7997
  // Host scripts can replace selected nodes every few frames (e.g. A/B tool observers).
7737
7998
  // Keep selection sticky by re-resolving from fingerprint.
@@ -8227,7 +8488,9 @@ window.addEventListener('load', function() {
8227
8488
  return;
8228
8489
  }
8229
8490
  hideIframeLoadError();
8230
- emitEditorUrlChanged(iframe.src || docUrl);
8491
+ var iframeLiveUrl = '';
8492
+ try { iframeLiveUrl = String(iframe.contentWindow.location.href || ''); } catch(_) {}
8493
+ emitEditorUrlChanged(iframeLiveUrl || docUrl || iframe.src || '');
8231
8494
  // Stale events: src may already be the proxy URL while the document is still
8232
8495
  // about:blank (e.g. src cleared then reset to force reload). Ask sync path to retry.
8233
8496
  if (docUrl === 'about:blank') {
@@ -8612,6 +8875,11 @@ function createVisualEditorMiddleware(options) {
8612
8875
  }
8613
8876
  if (chunks.length > 0) requestBody = Buffer.concat(chunks);
8614
8877
  }
8878
+ const secFetchMode = (req.headers?.["sec-fetch-mode"] || "").toLowerCase();
8879
+ const secFetchDest = (req.headers?.["sec-fetch-dest"] || "").toLowerCase();
8880
+ const isLikelyDocumentNavigation = secFetchMode === "navigate" || secFetchDest === "iframe" || secFetchDest === "document" || secFetchDest === "nested-document" || secFetchDest === "frame";
8881
+ const isLikelyFetchOrXHR = secFetchDest === "empty" && (secFetchMode === "cors" || secFetchMode === "same-origin" || secFetchMode === "no-cors");
8882
+ const passthroughUpstreamRedirects = (method === "GET" || method === "HEAD") && (isLikelyDocumentNavigation || !isLikelyFetchOrXHR);
8615
8883
  const upstreamTimeoutMs = 12e4;
8616
8884
  const ac = new AbortController();
8617
8885
  const timeoutId = setTimeout(() => ac.abort(), upstreamTimeoutMs);
@@ -8621,7 +8889,7 @@ function createVisualEditorMiddleware(options) {
8621
8889
  method,
8622
8890
  headers: fetchHeaders,
8623
8891
  body: requestBody ? Buffer.from(requestBody) : null,
8624
- redirect: "follow",
8892
+ redirect: passthroughUpstreamRedirects ? "manual" : "follow",
8625
8893
  signal: ac.signal
8626
8894
  });
8627
8895
  } catch (fetchErr) {
@@ -8645,12 +8913,43 @@ function createVisualEditorMiddleware(options) {
8645
8913
  return;
8646
8914
  }
8647
8915
  clearTimeout(timeoutId);
8916
+ if (passthroughUpstreamRedirects && upstream.status >= 300 && upstream.status < 400) {
8917
+ const locationHeader = upstream.headers.get("location") || upstream.headers.get("Location");
8918
+ if (locationHeader) {
8919
+ try {
8920
+ const redirectTarget = new URL(locationHeader, targetUrl);
8921
+ if (redirectTarget.origin === origin) {
8922
+ const proxyRedirect = new URL(proxyRootForRequest, "http://localhost");
8923
+ proxyRedirect.searchParams.set("password", password);
8924
+ proxyRedirect.searchParams.set("url", redirectTarget.toString());
8925
+ url.searchParams.forEach((value, key) => {
8926
+ if (key === "url" || key === "password") return;
8927
+ proxyRedirect.searchParams.set(key, value);
8928
+ });
8929
+ res.statusCode = upstream.status === 303 ? 303 : 302;
8930
+ res.setHeader("Location", `${proxyRedirect.pathname}${proxyRedirect.search}`);
8931
+ res.setHeader("Cache-Control", "no-store");
8932
+ res.setHeader("Access-Control-Allow-Origin", "*");
8933
+ setFrameHeaders(req, res);
8934
+ res.end();
8935
+ return;
8936
+ }
8937
+ res.statusCode = 502;
8938
+ res.setHeader("Content-Type", "application/json");
8939
+ res.end(
8940
+ JSON.stringify({
8941
+ error: "Cross-origin redirect blocked in editor preview",
8942
+ location: redirectTarget.toString(),
8943
+ from: targetUrl
8944
+ })
8945
+ );
8946
+ return;
8947
+ } catch (_) {
8948
+ }
8949
+ }
8950
+ }
8648
8951
  const responseContentType = upstream.headers.get("content-type") || "";
8649
8952
  const isHtmlResponse = responseContentType.includes("text/html");
8650
- const secFetchMode = (req.headers?.["sec-fetch-mode"] || "").toLowerCase();
8651
- const secFetchDest = (req.headers?.["sec-fetch-dest"] || "").toLowerCase();
8652
- const isLikelyDocumentNavigation = secFetchMode === "navigate" || secFetchDest === "iframe" || secFetchDest === "document" || secFetchDest === "nested-document" || secFetchDest === "frame";
8653
- const isLikelyFetchOrXHR = secFetchDest === "empty" && (secFetchMode === "cors" || secFetchMode === "same-origin" || secFetchMode === "no-cors");
8654
8953
  const shouldInjectHtmlBridge = isHtmlResponse && (isLikelyDocumentNavigation || !isLikelyFetchOrXHR);
8655
8954
  if (!isHtmlResponse || !shouldInjectHtmlBridge) {
8656
8955
  const binary = Buffer.from(await upstream.arrayBuffer());
@@ -8704,6 +9003,18 @@ ${iframeAlwaysShowCssGuardScript}
8704
9003
  /<meta[^>]+name=["']?\s*content-security-policy\s*["']?[^>]*>/gi,
8705
9004
  ""
8706
9005
  );
9006
+ html = html.replace(
9007
+ /(<meta[^>]+http-equiv=["']?refresh["']?[^>]*content=["'][^"']*url=)([^"';]+)(["'][^>]*>)/gi,
9008
+ (match, prefix, urlPart, suffix) => {
9009
+ try {
9010
+ const abs = new URL(String(urlPart).trim(), origin).toString();
9011
+ if (new URL(abs).origin !== origin) return match;
9012
+ return `${prefix}${proxyBase}${encodeURIComponent(abs)}${suffix}`;
9013
+ } catch {
9014
+ return match;
9015
+ }
9016
+ }
9017
+ );
8707
9018
  const runtimePreflightScript = `<script>(function(){try{
8708
9019
  var TARGET_ORIGIN=${JSON.stringify(origin)};
8709
9020
  var TARGET_PAGE_URL=${JSON.stringify(targetUrl)};
@@ -8910,6 +9221,8 @@ try{if(window.history&&typeof window.history.pushState==="function"){var nativeP
8910
9221
  try{if(window.history&&typeof window.history.replaceState==="function"){var nativeReplaceState=window.history.replaceState;window.history.replaceState=function(){var ret=nativeReplaceState.apply(window.history,arguments);setTimeout(notifyEditorUrlChanged,0);return ret;};}}catch(_){}
8911
9222
  try{window.addEventListener("popstate",notifyEditorUrlChanged,true);}catch(_){}
8912
9223
  try{window.addEventListener("hashchange",notifyEditorUrlChanged,true);}catch(_){}
9224
+ try{window.addEventListener("pageshow",notifyEditorUrlChanged,true);}catch(_){}
9225
+ try{window.addEventListener("load",function(){setTimeout(notifyEditorUrlChanged,0);},true);}catch(_){}
8913
9226
  function isSkippable(raw){if(!raw||typeof raw!=="string")return true;return raw.startsWith("data:")||raw.startsWith("blob:")||raw.startsWith("javascript:")||raw.startsWith("#");}
8914
9227
  function toAbsolute(raw){if(isSkippable(raw))return raw;try{var base=raw.startsWith("/")||raw.startsWith("//")?TARGET_ORIGIN:TARGET_PAGE_URL;return new URL(raw,base).toString();}catch(_){return raw;}}
8915
9228
  function toProxy(raw){
@@ -8938,6 +9251,9 @@ function toProxy(raw){
8938
9251
  var nativeAssign=window.location.assign?window.location.assign.bind(window.location):null;
8939
9252
  var nativeReplace=window.location.replace?window.location.replace.bind(window.location):null;
8940
9253
  function safeNavigate(raw,mode){var abs=toAbsolute(raw);var prox=toProxy(raw);if(!prox){try{console.warn("[conversion-proxy] redirect blocked",{mode:mode,requested:raw,resolved:abs,origin:TARGET_ORIGIN});}catch(_){}return false;}try{console.info("[conversion-proxy] redirect intercepted",{mode:mode,requested:raw,resolved:abs,proxied:prox});if(mode==="replace"&&nativeReplace){nativeReplace(prox);return true;}if(nativeAssign){nativeAssign(prox);return true;}window.location.href=prox;return true;}catch(err){try{console.warn("[conversion-proxy] redirect interception failed",{mode:mode,requested:raw,resolved:abs,proxied:prox,error:err&&err.message?err.message:String(err)});}catch(_){}return false;}}
9254
+ function interceptMetaRefresh(){try{var metas=document.querySelectorAll('meta[http-equiv="refresh" i],meta[http-equiv="Refresh"]');for(var i=0;i<metas.length;i++){var content=metas[i].getAttribute("content")||"";var m=content.match(/url=(.+)$/i);if(!m)continue;var raw=m[1].trim().replace(/^['"]|['"]$/g,"");if(safeNavigate(raw,"replace"))metas[i].parentNode&&metas[i].parentNode.removeChild(metas[i]);}}catch(_){}}
9255
+ try{interceptMetaRefresh();}catch(_){}
9256
+ try{document.addEventListener("DOMContentLoaded",interceptMetaRefresh,true);}catch(_){}
8941
9257
  try{if(nativeAssign){window.location.assign=function(url){return safeNavigate(url,"assign");};}}catch(_){}
8942
9258
  try{if(nativeReplace){window.location.replace=function(url){return safeNavigate(url,"replace");};}}catch(_){}
8943
9259
  try{var hrefDesc=Object.getOwnPropertyDescriptor(Location.prototype,"href");if(hrefDesc&&hrefDesc.configurable&&hrefDesc.get&&hrefDesc.set){Object.defineProperty(Location.prototype,"href",{configurable:true,enumerable:hrefDesc.enumerable,get:function(){return hrefDesc.get.call(this);},set:function(v){safeNavigate(v,"assign");}});}}catch(_){}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@accelerated-agency/visual-editor",
3
- "version": "0.5.9",
3
+ "version": "0.6.1",
4
4
  "private": false,
5
5
  "description": "Conversion visual editor as a reusable React package",
6
6
  "type": "module",