@accelerated-agency/visual-editor 0.3.4 → 0.3.5
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.
- package/dist/vite.cjs +559 -169
- package/dist/vite.js +559 -169
- package/package.json +1 -1
package/dist/vite.js
CHANGED
|
@@ -347,21 +347,21 @@ html,body{height:100%;overflow:hidden;font-family:-apple-system,BlinkMacSystemFo
|
|
|
347
347
|
.lp-sec{border-bottom:1px solid var(--border);flex-shrink:0;padding: 24px;}
|
|
348
348
|
.lp-sec-no-border{border-bottom:none!important}
|
|
349
349
|
.lp-sec-hd{
|
|
350
|
-
|
|
350
|
+
margin-bottom: 12px;
|
|
351
|
+
font-size:14px;font-weight:600;
|
|
351
352
|
color:#404040;display:flex;align-items:center;justify-content:space-between;gap:5px
|
|
352
353
|
}
|
|
353
354
|
.lp-sec-hd-left{display:flex;align-items:center;gap:5px}
|
|
354
355
|
#active-var-label{display:none;color:var(--accent-txt);font-size:10px;font-weight:500}
|
|
355
356
|
.lp-info-icon{font-size:11px;color:var(--text-3);cursor:default;opacity:.7}
|
|
356
357
|
.lp-add-btn{
|
|
357
|
-
display:none;
|
|
358
358
|
background:none;border:none;color:var(--text);font-size:14px;font-weight:500;
|
|
359
359
|
cursor:pointer;padding:2px 5px;border-radius:4px;transition:all .12s;flex-shrink:0
|
|
360
360
|
}
|
|
361
361
|
.lp-add-btn:hover{background:var(--bg-hover);color:var(--accent-txt)}
|
|
362
362
|
|
|
363
363
|
/* \u2500\u2500 Variation tabs \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 */
|
|
364
|
-
#variation-tabs{
|
|
364
|
+
#variation-tabs{display:flex;flex-direction:column; gap:8px;}
|
|
365
365
|
.var-tab{
|
|
366
366
|
border-radius: 4px;
|
|
367
367
|
border: 1px solid #e5e7eb;
|
|
@@ -392,7 +392,7 @@ html,body{height:100%;overflow:hidden;font-family:-apple-system,BlinkMacSystemFo
|
|
|
392
392
|
#comp-search:focus{border-color:var(--accent);box-shadow:0 0 0 3px rgba(99,102,241,.12)}
|
|
393
393
|
|
|
394
394
|
/* \u2500\u2500 Left tabs \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 */
|
|
395
|
-
.lp-tabs{display:flex;border-bottom:1px solid var(--border);flex-shrink:0;background:#fff}
|
|
395
|
+
.lp-tabs, .section-components-tabs{display:flex;border-bottom:1px solid var(--border);flex-shrink:0;background:#fff}
|
|
396
396
|
.lp-tab{
|
|
397
397
|
flex:1;padding:8px 2px;text-align:center;font-size:10px;color:var(--text-3);
|
|
398
398
|
cursor:pointer;border-bottom:2px solid transparent;transition:all .15s;font-weight:600;line-height:1.15
|
|
@@ -402,10 +402,10 @@ html,body{height:100%;overflow:hidden;font-family:-apple-system,BlinkMacSystemFo
|
|
|
402
402
|
.future-hidden{display:none!important}
|
|
403
403
|
|
|
404
404
|
/* \u2500\u2500 Left panel body \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 */
|
|
405
|
-
.lp-body{flex:1;overflow-y:auto}
|
|
406
|
-
.lp-body::-webkit-scrollbar{width:3px}
|
|
407
|
-
.lp-body::-webkit-scrollbar-thumb{background:#cbd5e1;border-radius:2px}
|
|
408
|
-
.tab-pane{display:none}.tab-pane.active{display:block}
|
|
405
|
+
.lp-body, .section-components-body{flex:1;overflow-y:auto}
|
|
406
|
+
.lp-body::-webkit-scrollbar, .section-components-body::-webkit-scrollbar{width:3px}
|
|
407
|
+
.lp-body::-webkit-scrollbar-thumb, .section-components-body::-webkit-scrollbar-thumb{background:#cbd5e1;border-radius:2px}
|
|
408
|
+
.tab-pane, .section-components-tab-pane{display:none}.tab-pane.active, .section-components-tab-pane.active{display:block}
|
|
409
409
|
|
|
410
410
|
/* \u2500\u2500 Component grid \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 */
|
|
411
411
|
.cg-hdr{padding:8px 10px 4px;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;color:var(--text-3)}
|
|
@@ -617,12 +617,83 @@ select.pr-inp{cursor:pointer;background:#fff}
|
|
|
617
617
|
}
|
|
618
618
|
#states-clear:hover{border-color:#fca5a5;color:#ef4444;background:#fef2f2}
|
|
619
619
|
#history-clear{
|
|
620
|
-
display:block;width:calc(100% - 24px);margin:10px 12px;
|
|
620
|
+
display:block;width:calc(100% - 24px);margin:10px 12px 8px;
|
|
621
621
|
background:none;border:1px solid var(--border);border-radius:6px;
|
|
622
622
|
padding:6px;font-size:11px;color:var(--text-2);cursor:pointer;font-family:inherit;
|
|
623
623
|
transition:all .15s
|
|
624
624
|
}
|
|
625
625
|
#history-clear:hover{border-color:#fca5a5;color:#ef4444;background:#fef2f2}
|
|
626
|
+
.history-timeline{
|
|
627
|
+
position:relative;
|
|
628
|
+
margin:8px 0 12px;
|
|
629
|
+
padding:0 12px 0 36px;
|
|
630
|
+
}
|
|
631
|
+
.history-timeline::before{
|
|
632
|
+
content:'';
|
|
633
|
+
position:absolute;
|
|
634
|
+
left:20px;
|
|
635
|
+
top:4px;
|
|
636
|
+
bottom:4px;
|
|
637
|
+
width:2px;
|
|
638
|
+
background:#eceff3;
|
|
639
|
+
border-radius:2px;
|
|
640
|
+
}
|
|
641
|
+
.history-item{
|
|
642
|
+
position:relative;
|
|
643
|
+
display:flex;
|
|
644
|
+
align-items:flex-start;
|
|
645
|
+
gap:10px;
|
|
646
|
+
padding:10px 8px 10px 0;
|
|
647
|
+
cursor:pointer;
|
|
648
|
+
}
|
|
649
|
+
.history-dot{
|
|
650
|
+
position:absolute;
|
|
651
|
+
left:-20px;
|
|
652
|
+
top:18px;
|
|
653
|
+
width:10px;
|
|
654
|
+
height:10px;
|
|
655
|
+
border-radius:50%;
|
|
656
|
+
background:#fff;
|
|
657
|
+
border:2px solid #d7dde6;
|
|
658
|
+
}
|
|
659
|
+
.history-card{
|
|
660
|
+
flex:1;
|
|
661
|
+
min-width:0;
|
|
662
|
+
}
|
|
663
|
+
.history-title{
|
|
664
|
+
font-size:14px;
|
|
665
|
+
font-weight:600;
|
|
666
|
+
color:var(--text);
|
|
667
|
+
line-height:1.3;
|
|
668
|
+
}
|
|
669
|
+
.history-meta{
|
|
670
|
+
margin-top:4px;
|
|
671
|
+
display:flex;
|
|
672
|
+
align-items:center;
|
|
673
|
+
gap:6px;
|
|
674
|
+
color:var(--text-2);
|
|
675
|
+
font-size:11px;
|
|
676
|
+
}
|
|
677
|
+
.history-avatar{
|
|
678
|
+
width:18px;
|
|
679
|
+
height:18px;
|
|
680
|
+
border-radius:50%;
|
|
681
|
+
background:#e9e5ff;
|
|
682
|
+
color:#5b47d6;
|
|
683
|
+
display:flex;
|
|
684
|
+
align-items:center;
|
|
685
|
+
justify-content:center;
|
|
686
|
+
font-size:10px;
|
|
687
|
+
font-weight:700;
|
|
688
|
+
}
|
|
689
|
+
.history-time{
|
|
690
|
+
margin-top:3px;
|
|
691
|
+
font-size:11px;
|
|
692
|
+
color:var(--text-3);
|
|
693
|
+
}
|
|
694
|
+
.history-remove{
|
|
695
|
+
margin-top:2px;
|
|
696
|
+
}
|
|
626
697
|
</style>
|
|
627
698
|
</head>
|
|
628
699
|
<body class="mode-editor">
|
|
@@ -704,7 +775,7 @@ select.pr-inp{cursor:pointer;background:#fff}
|
|
|
704
775
|
<div class="lp-sec">
|
|
705
776
|
<div class="lp-sec-hd">
|
|
706
777
|
<span class="lp-sec-hd-left">Variations <span id="active-var-label"></span></span>
|
|
707
|
-
<button class="lp-add-btn" title="Add variation">+ Add</button>
|
|
778
|
+
<button class="lp-add-btn" style="display:none" title="Add variation">+ Add</button>
|
|
708
779
|
</div>
|
|
709
780
|
<div id="variation-tabs"></div>
|
|
710
781
|
</div>
|
|
@@ -729,27 +800,39 @@ select.pr-inp{cursor:pointer;background:#fff}
|
|
|
729
800
|
<span class="lp-sec-hd-left">Elements <i class="bi bi-info-circle lp-info-icon" title="Page elements"></i></span>
|
|
730
801
|
<button class="lp-add-btn" title="Add element">+ Add</button>
|
|
731
802
|
</div>
|
|
732
|
-
</div>
|
|
733
803
|
|
|
734
|
-
|
|
735
|
-
<div
|
|
804
|
+
<!-- Search (hidden, kept for JS) -->
|
|
805
|
+
<div>
|
|
736
806
|
<input type="search" id="comp-search" placeholder="Search layers\u2026" autocomplete="off">
|
|
737
807
|
</div>
|
|
738
808
|
|
|
809
|
+
|
|
739
810
|
<!-- Tabs (hidden, kept for JS) -->
|
|
740
|
-
<div class="lp-tabs"
|
|
741
|
-
|
|
742
|
-
<div class="lp-tab
|
|
743
|
-
|
|
811
|
+
<div class="lp-tabs" >
|
|
812
|
+
<div class="lp-tab active" onclick="switchLeftTab('elements')">Elements</div>
|
|
813
|
+
<div class="lp-tab" onclick="switchLeftTab('dom-tree')">DOM Tree</div>
|
|
814
|
+
</div>
|
|
815
|
+
|
|
744
816
|
</div>
|
|
745
817
|
|
|
746
818
|
<!-- Tab content -->
|
|
747
819
|
<div class="lp-body">
|
|
748
|
-
<div id="tab-
|
|
749
|
-
<div id="
|
|
750
|
-
|
|
820
|
+
<div id="tab-dom-tree" class="tab-pane">
|
|
821
|
+
<div id="dom-tree-root" class="dt-tree">
|
|
822
|
+
</div>
|
|
823
|
+
</div>
|
|
824
|
+
<div id="tab-elements" class="tab-pane active">
|
|
825
|
+
<div id="elements-root" class="elements-tree">
|
|
826
|
+
</div>
|
|
827
|
+
</div>
|
|
751
828
|
</div>
|
|
752
829
|
|
|
830
|
+
|
|
831
|
+
|
|
832
|
+
|
|
833
|
+
|
|
834
|
+
|
|
835
|
+
|
|
753
836
|
</div><!-- #left-panel -->
|
|
754
837
|
|
|
755
838
|
<!-- Center / iframe panel -->
|
|
@@ -757,7 +840,8 @@ select.pr-inp{cursor:pointer;background:#fff}
|
|
|
757
840
|
|
|
758
841
|
<!-- Floating toolbar for selected element (positioned over iframe) -->
|
|
759
842
|
<div id="selection-floater" aria-label="Selection actions">
|
|
760
|
-
<button type="button" class="sf-btn" id="sf-
|
|
843
|
+
<button type="button" class="sf-btn" id="sf-move-up" title="Move up"><i class="bi bi-arrow-up"></i></button>
|
|
844
|
+
<button type="button" class="sf-btn" id="sf-move-down" title="Move down"><i class="bi bi-arrow-down"></i></button>
|
|
761
845
|
<span class="sf-sep"></span>
|
|
762
846
|
<button type="button" class="sf-btn" id="sf-resize" disabled title="Resize (coming soon)"><i class="bi bi-arrows-angle-expand"></i></button>
|
|
763
847
|
<button type="button" class="sf-btn" id="sf-rotate" disabled title="Rotate (coming soon)"><i class="bi bi-arrow-repeat"></i></button>
|
|
@@ -784,13 +868,20 @@ select.pr-inp{cursor:pointer;background:#fff}
|
|
|
784
868
|
|
|
785
869
|
<!-- Right panel -->
|
|
786
870
|
<div id="right-panel">
|
|
787
|
-
|
|
871
|
+
<!-- Left-tab controls moved here -->
|
|
872
|
+
<div class="section-components-tabs">
|
|
873
|
+
<div class="lp-tab" onclick="switchSectionComponentsTab('components')">Components</div>
|
|
874
|
+
<div class="lp-tab" onclick="switchSectionComponentsTab('sections')">Sections</div>
|
|
875
|
+
</div>
|
|
876
|
+
<div class="lp-body">
|
|
877
|
+
<div id="tab-components" class="tab-pane"></div>
|
|
878
|
+
<div id="tab-sections" class="tab-pane"></div>
|
|
879
|
+
</div>
|
|
788
880
|
<!-- Element badge (hidden until selection) -->
|
|
789
881
|
<div id="el-info" style="display:none">
|
|
790
882
|
<div id="el-info-tag"></div>
|
|
791
883
|
<div id="el-info-sel"></div>
|
|
792
884
|
</div>
|
|
793
|
-
|
|
794
885
|
<!-- \u2500\u2500 3 main tabs \u2500\u2500 -->
|
|
795
886
|
<div id="main-tabs">
|
|
796
887
|
<button class="main-tab active" onclick="switchMainTab('design')">Design</button>
|
|
@@ -872,12 +963,7 @@ select.pr-inp{cursor:pointer;background:#fff}
|
|
|
872
963
|
|
|
873
964
|
<!-- \u2500\u2500 States pane \u2500\u2500 -->
|
|
874
965
|
<div id="tab-states" class="rp-pane">
|
|
875
|
-
<div id="states-list">
|
|
876
|
-
<div class="states-empty">
|
|
877
|
-
<i class="bi bi-layers"></i>
|
|
878
|
-
No changes yet \u2014 edit elements on the page to see states here
|
|
879
|
-
</div>
|
|
880
|
-
</div>
|
|
966
|
+
<div id="states-list"></div>
|
|
881
967
|
</div><!-- #tab-states -->
|
|
882
968
|
|
|
883
969
|
<!-- \u2500\u2500 History pane (saved DB changesets for active variation) \u2500\u2500 -->
|
|
@@ -1125,6 +1211,7 @@ var suppressClickUntil = 0;
|
|
|
1125
1211
|
var dragAttachDoc = null;
|
|
1126
1212
|
var currentMainTab = 'design';
|
|
1127
1213
|
var currentLeftTab = 'elements';
|
|
1214
|
+
var currentSectionComponentsTab = 'components';
|
|
1128
1215
|
var dragHandleActive = false;
|
|
1129
1216
|
var domTreeCollapsed = {};
|
|
1130
1217
|
var domTreeRefreshTimer = null;
|
|
@@ -1156,6 +1243,13 @@ var stateChangesByVarId = {};
|
|
|
1156
1243
|
var appliedChangesetSnapshots = {};
|
|
1157
1244
|
/** Canonical JSON fingerprints of persisted changesets per variation (last load / finalize) */
|
|
1158
1245
|
var baselineChangesetsByVarId = {};
|
|
1246
|
+
/** Monotonic timestamp key for ordering mixed live + saved history rows. */
|
|
1247
|
+
var vveHistorySeq = 0;
|
|
1248
|
+
|
|
1249
|
+
function nextHistoryTimestamp() {
|
|
1250
|
+
vveHistorySeq += 1;
|
|
1251
|
+
return Date.now() * 1000 + vveHistorySeq;
|
|
1252
|
+
}
|
|
1159
1253
|
|
|
1160
1254
|
// \u2500\u2500 Dirty tracking (compare DB baseline + session stateChanges vs current export) \u2500\u2500
|
|
1161
1255
|
function beginSuppressIframeMutationDirty() {
|
|
@@ -1371,27 +1465,45 @@ function setDevice(device) {
|
|
|
1371
1465
|
|
|
1372
1466
|
// \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
|
|
1373
1467
|
function switchLeftTab(tab) {
|
|
1468
|
+
if (tab !== 'elements' && tab !== 'dom-tree') return;
|
|
1374
1469
|
currentLeftTab = tab;
|
|
1375
|
-
var tabs = document.querySelectorAll('.lp-tab');
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1470
|
+
var tabs = document.querySelectorAll('.lp-tabs .lp-tab');
|
|
1471
|
+
for (var i = 0; i < tabs.length; i++) {
|
|
1472
|
+
var oc = tabs[i].getAttribute('onclick') || '';
|
|
1473
|
+
tabs[i].classList.toggle('active', oc.indexOf("switchLeftTab('" + tab + "')") >= 0);
|
|
1474
|
+
}
|
|
1475
|
+
var paneNames = ['elements', 'dom-tree'];
|
|
1476
|
+
for (var p = 0; p < paneNames.length; p++) {
|
|
1477
|
+
var pane = document.getElementById('tab-' + paneNames[p]);
|
|
1478
|
+
if (pane) pane.classList.toggle('active', paneNames[p] === tab);
|
|
1479
|
+
}
|
|
1382
1480
|
var inp = document.getElementById('comp-search');
|
|
1383
1481
|
if (tab === 'elements') {
|
|
1482
|
+
inp.placeholder = 'Search elements\u2026';
|
|
1483
|
+
renderElementsTree(inp.value);
|
|
1484
|
+
} else if (tab === 'dom-tree') {
|
|
1384
1485
|
inp.placeholder = 'Search layers\u2026';
|
|
1385
1486
|
renderDomTree(inp.value);
|
|
1386
|
-
} else if (tab === 'sections') {
|
|
1387
|
-
inp.placeholder = 'Search sections\u2026';
|
|
1388
|
-
renderSidebar(inp.value);
|
|
1389
|
-
} else {
|
|
1390
|
-
inp.placeholder = 'Search components\u2026';
|
|
1391
|
-
renderSidebar(inp.value);
|
|
1392
1487
|
}
|
|
1393
1488
|
}
|
|
1394
1489
|
|
|
1490
|
+
function switchSectionComponentsTab(tab) {
|
|
1491
|
+
if (tab !== 'components' && tab !== 'sections') return;
|
|
1492
|
+
currentSectionComponentsTab = tab;
|
|
1493
|
+
var tabs = document.querySelectorAll('.section-components-tabs .lp-tab');
|
|
1494
|
+
for (var i = 0; i < tabs.length; i++) {
|
|
1495
|
+
var oc = tabs[i].getAttribute('onclick') || '';
|
|
1496
|
+
tabs[i].classList.toggle('active', oc.indexOf("switchSectionComponentsTab('" + tab + "')") >= 0);
|
|
1497
|
+
}
|
|
1498
|
+
var compPane = document.getElementById('tab-components');
|
|
1499
|
+
var secPane = document.getElementById('tab-sections');
|
|
1500
|
+
if (compPane) compPane.classList.toggle('active', tab === 'components');
|
|
1501
|
+
if (secPane) secPane.classList.toggle('active', tab === 'sections');
|
|
1502
|
+
var inp = document.getElementById('comp-search');
|
|
1503
|
+
if (inp) inp.placeholder = tab === 'sections' ? 'Search sections\u2026' : 'Search components\u2026';
|
|
1504
|
+
renderSidebar(inp ? inp.value : '');
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1395
1507
|
// \u2500\u2500 Accordion 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
|
|
1396
1508
|
function toggleAcc(name) {
|
|
1397
1509
|
var sec = document.getElementById('acc-' + name);
|
|
@@ -1514,42 +1626,20 @@ function logChange(selector, inputId, value, targetEl, originalValue) {
|
|
|
1514
1626
|
: (originalValue != null ? originalValue : '');
|
|
1515
1627
|
var entry = {
|
|
1516
1628
|
selector: selector, inputId: inputId, label: meta.label,
|
|
1517
|
-
cssProp: meta.cssProp, value: value, targetEl: targetEl, originalValue: orig
|
|
1629
|
+
cssProp: meta.cssProp, value: value, targetEl: targetEl, originalValue: orig, vveTs: nextHistoryTimestamp()
|
|
1518
1630
|
};
|
|
1519
1631
|
if (idx >= 0) { stateChanges[idx] = entry; } else { stateChanges.push(entry); }
|
|
1520
1632
|
}
|
|
1521
1633
|
if (currentMainTab === 'states') renderStatesTab();
|
|
1634
|
+
if (currentMainTab === 'history') renderHistoryTab();
|
|
1522
1635
|
commitStateChangesForActiveVariation();
|
|
1523
1636
|
recomputeEditorDirty();
|
|
1524
1637
|
}
|
|
1525
1638
|
|
|
1526
1639
|
function renderStatesTab() {
|
|
1527
1640
|
var container = document.getElementById('states-list');
|
|
1528
|
-
if (!
|
|
1529
|
-
|
|
1530
|
-
return;
|
|
1531
|
-
}
|
|
1532
|
-
// Group by selector
|
|
1533
|
-
var groups = {};
|
|
1534
|
-
var order = [];
|
|
1535
|
-
stateChanges.forEach(function(c) {
|
|
1536
|
-
if (!groups[c.selector]) { groups[c.selector] = []; order.push(c.selector); }
|
|
1537
|
-
groups[c.selector].push(c);
|
|
1538
|
-
});
|
|
1539
|
-
var html = '<button id="states-clear" onclick="clearAllStates()"><i class="bi bi-trash3"></i> Clear all changes</button>';
|
|
1540
|
-
order.forEach(function(sel) {
|
|
1541
|
-
html += '<div class="state-group"><div class="state-group-sel">'+esc(sel)+'</div>';
|
|
1542
|
-
groups[sel].forEach(function(c) {
|
|
1543
|
-
var idx = stateChanges.indexOf(c);
|
|
1544
|
-
html += '<div class="state-item">' +
|
|
1545
|
-
'<span class="state-item-label">'+esc(c.label)+'</span>' +
|
|
1546
|
-
'<span class="state-item-val" title="'+esc(c.value)+'">'+esc(c.value)+'</span>' +
|
|
1547
|
-
'<button class="state-remove" title="Remove this change" onclick="removeStateChange('+idx+')">✕</button>' +
|
|
1548
|
-
'</div>';
|
|
1549
|
-
});
|
|
1550
|
-
html += '</div>';
|
|
1551
|
-
});
|
|
1552
|
-
container.innerHTML = html;
|
|
1641
|
+
if (!container) return;
|
|
1642
|
+
container.innerHTML = '';
|
|
1553
1643
|
}
|
|
1554
1644
|
|
|
1555
1645
|
// Resolve a live DOM element for a state-change entry.
|
|
@@ -1628,7 +1718,9 @@ function removeStateChange(idx) {
|
|
|
1628
1718
|
stateChanges.splice(idx, 1);
|
|
1629
1719
|
commitStateChangesForActiveVariation();
|
|
1630
1720
|
renderStatesTab();
|
|
1721
|
+
if (currentMainTab === 'history') renderHistoryTab();
|
|
1631
1722
|
recomputeEditorDirty();
|
|
1723
|
+
scheduleDomTreeRefresh();
|
|
1632
1724
|
}
|
|
1633
1725
|
|
|
1634
1726
|
function clearAllStates() {
|
|
@@ -1639,7 +1731,9 @@ function clearAllStates() {
|
|
|
1639
1731
|
stateChanges = [];
|
|
1640
1732
|
commitStateChangesForActiveVariation();
|
|
1641
1733
|
renderStatesTab();
|
|
1734
|
+
if (currentMainTab === 'history') renderHistoryTab();
|
|
1642
1735
|
recomputeEditorDirty();
|
|
1736
|
+
scheduleDomTreeRefresh();
|
|
1643
1737
|
}
|
|
1644
1738
|
|
|
1645
1739
|
// \u2500\u2500 History tab (saved changesets from DB for active variation) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
@@ -1812,61 +1906,169 @@ function historyEntryValuePreview(entry) {
|
|
|
1812
1906
|
return '';
|
|
1813
1907
|
}
|
|
1814
1908
|
|
|
1909
|
+
function getHistoryTimestampValue(raw, fallback) {
|
|
1910
|
+
var n = Number(raw);
|
|
1911
|
+
if (Number.isFinite(n) && n > 0) return n;
|
|
1912
|
+
return fallback;
|
|
1913
|
+
}
|
|
1914
|
+
|
|
1915
|
+
function historyTimestampForChangeset(entry, idx) {
|
|
1916
|
+
var base = idx + 1;
|
|
1917
|
+
if (!entry) return base;
|
|
1918
|
+
return getHistoryTimestampValue(
|
|
1919
|
+
entry.vveTs != null ? entry.vveTs : (entry.timestamp != null ? entry.timestamp : entry.ts),
|
|
1920
|
+
base,
|
|
1921
|
+
);
|
|
1922
|
+
}
|
|
1923
|
+
|
|
1924
|
+
function historyTimestampForStateChange(change, idx) {
|
|
1925
|
+
return getHistoryTimestampValue(change && change.vveTs, idx + 1);
|
|
1926
|
+
}
|
|
1927
|
+
|
|
1928
|
+
function formatHistoryTimestamp(ts) {
|
|
1929
|
+
if (!Number.isFinite(ts) || ts <= 0) return '';
|
|
1930
|
+
var ms = ts > 9999999999999 ? Math.floor(ts / 1000) : ts;
|
|
1931
|
+
var d = new Date(ms);
|
|
1932
|
+
if (isNaN(d.getTime())) return '';
|
|
1933
|
+
return d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' });
|
|
1934
|
+
}
|
|
1935
|
+
|
|
1936
|
+
function formatHistoryRelativeTime(ts) {
|
|
1937
|
+
if (!Number.isFinite(ts) || ts <= 0) return '';
|
|
1938
|
+
var ms = ts > 9999999999999 ? Math.floor(ts / 1000) : ts;
|
|
1939
|
+
var diff = Math.max(0, Date.now() - ms);
|
|
1940
|
+
var sec = Math.floor(diff / 1000);
|
|
1941
|
+
if (sec < 5) return 'just now';
|
|
1942
|
+
if (sec < 60) return sec + ' seconds ago';
|
|
1943
|
+
var min = Math.floor(sec / 60);
|
|
1944
|
+
if (min < 60) return min + ' minute' + (min === 1 ? '' : 's') + ' ago';
|
|
1945
|
+
var hr = Math.floor(min / 60);
|
|
1946
|
+
if (hr < 24) return hr + ' hour' + (hr === 1 ? '' : 's') + ' ago';
|
|
1947
|
+
var day = Math.floor(hr / 24);
|
|
1948
|
+
return day + ' day' + (day === 1 ? '' : 's') + ' ago';
|
|
1949
|
+
}
|
|
1950
|
+
|
|
1951
|
+
function getUnifiedHistoryItems() {
|
|
1952
|
+
var out = [];
|
|
1953
|
+
var v = getActiveVariationForHistory();
|
|
1954
|
+
var saved = v ? parseVariationChangesets(v) : [];
|
|
1955
|
+
for (var i = 0; i < saved.length; i++) {
|
|
1956
|
+
var e = saved[i];
|
|
1957
|
+
out.push({
|
|
1958
|
+
source: 'saved',
|
|
1959
|
+
idx: i,
|
|
1960
|
+
selector: (e && e.selector) || '(unknown)',
|
|
1961
|
+
label: historyEntryTypeLabel(e),
|
|
1962
|
+
value: historyEntryValuePreview(e),
|
|
1963
|
+
ts: historyTimestampForChangeset(e, i),
|
|
1964
|
+
tsLabel: formatHistoryTimestamp(historyTimestampForChangeset(e, i)),
|
|
1965
|
+
actor: 'Saved changeset',
|
|
1966
|
+
});
|
|
1967
|
+
}
|
|
1968
|
+
var live = stateChanges || [];
|
|
1969
|
+
for (var j = 0; j < live.length; j++) {
|
|
1970
|
+
var c = live[j];
|
|
1971
|
+
if (!c) continue;
|
|
1972
|
+
var ts = historyTimestampForStateChange(c, j + saved.length);
|
|
1973
|
+
out.push({
|
|
1974
|
+
source: 'live',
|
|
1975
|
+
idx: j,
|
|
1976
|
+
selector: c.selector || '(unknown)',
|
|
1977
|
+
label: c.label || 'Live change',
|
|
1978
|
+
value: c.value != null ? String(c.value).slice(0, 120) : '',
|
|
1979
|
+
ts: ts,
|
|
1980
|
+
tsLabel: formatHistoryTimestamp(ts),
|
|
1981
|
+
actor: 'You',
|
|
1982
|
+
});
|
|
1983
|
+
}
|
|
1984
|
+
out.sort(function(a, b) {
|
|
1985
|
+
if (b.ts !== a.ts) return b.ts - a.ts;
|
|
1986
|
+
if (a.source !== b.source) return a.source === 'live' ? -1 : 1;
|
|
1987
|
+
return b.idx - a.idx;
|
|
1988
|
+
});
|
|
1989
|
+
return out;
|
|
1990
|
+
}
|
|
1991
|
+
|
|
1815
1992
|
function renderHistoryTab() {
|
|
1816
1993
|
var container = document.getElementById('history-list');
|
|
1817
1994
|
if (!container) return;
|
|
1818
|
-
var
|
|
1819
|
-
|
|
1820
|
-
if (!arr.length) {
|
|
1995
|
+
var items = getUnifiedHistoryItems();
|
|
1996
|
+
if (!items.length) {
|
|
1821
1997
|
container.innerHTML =
|
|
1822
|
-
'<div class="states-empty"><i class="bi bi-clock-history"></i>No
|
|
1998
|
+
'<div class="states-empty"><i class="bi bi-clock-history"></i>No changes yet</div>';
|
|
1823
1999
|
return;
|
|
1824
2000
|
}
|
|
1825
|
-
var groups = {};
|
|
1826
|
-
var order = [];
|
|
1827
|
-
for (var gi = 0; gi < arr.length; gi++) {
|
|
1828
|
-
var entry = arr[gi];
|
|
1829
|
-
var sel = entry.selector || '(unknown)';
|
|
1830
|
-
if (!groups[sel]) {
|
|
1831
|
-
groups[sel] = [];
|
|
1832
|
-
order.push(sel);
|
|
1833
|
-
}
|
|
1834
|
-
groups[sel].push({ entry: entry, idx: gi });
|
|
1835
|
-
}
|
|
1836
2001
|
var html =
|
|
1837
|
-
'<button type="button" id="history-clear" onclick="
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
2002
|
+
'<button type="button" id="history-clear" onclick="clearAllUnifiedHistory()"><i class="bi bi-trash3"></i> Clear all changes</button>';
|
|
2003
|
+
html += '<div class="history-timeline">';
|
|
2004
|
+
for (var i = 0; i < items.length; i++) {
|
|
2005
|
+
var it = items[i];
|
|
2006
|
+
var title = 'Edit - ' + (it.label || 'Change');
|
|
2007
|
+
var avatarLabel = it.source === 'live' ? 'Y' : 'S';
|
|
2008
|
+
var timeText = formatHistoryRelativeTime(it.ts) || (it.tsLabel || '');
|
|
2009
|
+
html +=
|
|
2010
|
+
'<div class="history-item" role="button" tabindex="0" title="Jump to element in iframe" onclick="focusHistoryItem("' +
|
|
2011
|
+
esc(it.source) +
|
|
2012
|
+
'",' +
|
|
2013
|
+
it.idx +
|
|
2014
|
+
')">' +
|
|
2015
|
+
'<span class="history-dot"></span>' +
|
|
2016
|
+
'<div class="history-card">' +
|
|
2017
|
+
'<div class="history-title">' + esc(title) + '</div>' +
|
|
2018
|
+
'<div class="history-meta">' +
|
|
2019
|
+
'<span class="history-avatar">' + esc(avatarLabel) + '</span>' +
|
|
2020
|
+
'<span>' + esc(it.actor || 'Editor') + '</span>' +
|
|
2021
|
+
'</div>' +
|
|
2022
|
+
'<div class="history-time">' + esc(timeText || 'n/a') + '</div>' +
|
|
2023
|
+
'</div>' +
|
|
2024
|
+
'<button type="button" class="state-remove history-remove" title="Remove this change" onclick="removeHistoryItem("' +
|
|
2025
|
+
esc(it.source) +
|
|
2026
|
+
'",' +
|
|
2027
|
+
it.idx +
|
|
2028
|
+
', event)">✕</button>' +
|
|
2029
|
+
'</div>';
|
|
2030
|
+
}
|
|
2031
|
+
html += '</div>';
|
|
1867
2032
|
container.innerHTML = html;
|
|
1868
2033
|
}
|
|
1869
2034
|
|
|
2035
|
+
function focusHistoryItem(source, idx) {
|
|
2036
|
+
if (source === 'live') {
|
|
2037
|
+
var change = stateChanges[idx];
|
|
2038
|
+
if (!change || !change.selector) return;
|
|
2039
|
+
try {
|
|
2040
|
+
var iframe = document.getElementById('iframeId');
|
|
2041
|
+
var iframeDoc = iframe && iframe.contentDocument;
|
|
2042
|
+
if (!iframeDoc) return;
|
|
2043
|
+
var el = querySelectorResolved(iframeDoc, change.selector);
|
|
2044
|
+
if (!el) return;
|
|
2045
|
+
selectElement(el);
|
|
2046
|
+
try {
|
|
2047
|
+
el.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'nearest' });
|
|
2048
|
+
} catch(_) {
|
|
2049
|
+
el.scrollIntoView();
|
|
2050
|
+
}
|
|
2051
|
+
} catch(_) {}
|
|
2052
|
+
return;
|
|
2053
|
+
}
|
|
2054
|
+
focusHistoryChangeset(idx);
|
|
2055
|
+
}
|
|
2056
|
+
|
|
2057
|
+
function removeHistoryItem(source, idx, evt) {
|
|
2058
|
+
if (source === 'live') {
|
|
2059
|
+
if (evt && evt.stopPropagation) evt.stopPropagation();
|
|
2060
|
+
removeStateChange(idx);
|
|
2061
|
+
if (currentMainTab === 'history') renderHistoryTab();
|
|
2062
|
+
return;
|
|
2063
|
+
}
|
|
2064
|
+
removeHistoryChangeset(idx, evt);
|
|
2065
|
+
}
|
|
2066
|
+
|
|
2067
|
+
function getLatestHistoryUndoTarget() {
|
|
2068
|
+
var list = getUnifiedHistoryItems();
|
|
2069
|
+
return list.length ? list[0] : null;
|
|
2070
|
+
}
|
|
2071
|
+
|
|
1870
2072
|
function changesetListHasStructural(arr) {
|
|
1871
2073
|
if (!arr || !arr.length) return false;
|
|
1872
2074
|
for (var i = 0; i < arr.length; i++) {
|
|
@@ -1978,6 +2180,12 @@ function clearAllHistoryChangesets() {
|
|
|
1978
2180
|
softReloadEditorIframe();
|
|
1979
2181
|
}
|
|
1980
2182
|
|
|
2183
|
+
function clearAllUnifiedHistory() {
|
|
2184
|
+
clearAllStates();
|
|
2185
|
+
clearAllHistoryChangesets();
|
|
2186
|
+
if (currentMainTab === 'history') renderHistoryTab();
|
|
2187
|
+
}
|
|
2188
|
+
|
|
1981
2189
|
// \u2500\u2500 Persisted active variation (survives iframe / full page reload) \u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
1982
2190
|
/** All Visual Editor iframe keys in localStorage use this prefix \u2014 cleared on close. */
|
|
1983
2191
|
var VVE_LOCAL_STORAGE_PREFIX = 'vve:';
|
|
@@ -2331,7 +2539,7 @@ function runConsistencyReconcile() {
|
|
|
2331
2539
|
var doc = iframe && iframe.contentDocument;
|
|
2332
2540
|
if (!doc || !doc.body) return;
|
|
2333
2541
|
var variation = variations.find(function(v) { return v._id === activeVarId; });
|
|
2334
|
-
var cs =
|
|
2542
|
+
var cs = buildPersistedChainSetsForVariation(variation);
|
|
2335
2543
|
if (!cs.length || changesetsHaveBodySnapshot(cs)) return;
|
|
2336
2544
|
var granular = filterGranularChangesetEntries(cs);
|
|
2337
2545
|
var unresolved = countUnresolvedGranularSelectors(doc, granular);
|
|
@@ -2591,6 +2799,7 @@ function mergeGranularChainSets(baseList, overlayList) {
|
|
|
2591
2799
|
function appendSessionStructuralChainRow(varId, row) {
|
|
2592
2800
|
if (!varId || !row) return;
|
|
2593
2801
|
if (!sessionStructuralChainRowsByVarId[varId]) sessionStructuralChainRowsByVarId[varId] = [];
|
|
2802
|
+
if (row.vveTs == null) row.vveTs = nextHistoryTimestamp();
|
|
2594
2803
|
sessionStructuralChainRowsByVarId[varId].push(row);
|
|
2595
2804
|
}
|
|
2596
2805
|
|
|
@@ -2598,33 +2807,33 @@ function appendSessionStructuralChainRow(varId, row) {
|
|
|
2598
2807
|
function stateChangeToChainSet(c) {
|
|
2599
2808
|
if (!c || !c.selector) return null;
|
|
2600
2809
|
if (c.cssProp) {
|
|
2601
|
-
return { selector: c.selector, type: 'style', property: c.cssProp, value: c.value };
|
|
2810
|
+
return { selector: c.selector, type: 'style', property: c.cssProp, value: c.value, vveTs: c.vveTs || nextHistoryTimestamp() };
|
|
2602
2811
|
}
|
|
2603
2812
|
switch (c.inputId) {
|
|
2604
2813
|
case 'pp-text':
|
|
2605
|
-
return { selector: c.selector, type: 'content', value: c.value };
|
|
2814
|
+
return { selector: c.selector, type: 'content', value: c.value, vveTs: c.vveTs || nextHistoryTimestamp() };
|
|
2606
2815
|
case 'pp-html':
|
|
2607
|
-
return { selector: c.selector, type: 'content', html: c.value };
|
|
2816
|
+
return { selector: c.selector, type: 'content', html: c.value, vveTs: c.vveTs || nextHistoryTimestamp() };
|
|
2608
2817
|
case 'pp-cls':
|
|
2609
|
-
return { selector: c.selector, type: 'attribute', attribute: 'class', value: c.value };
|
|
2818
|
+
return { selector: c.selector, type: 'attribute', attribute: 'class', value: c.value, vveTs: c.vveTs || nextHistoryTimestamp() };
|
|
2610
2819
|
case 'pp-id':
|
|
2611
|
-
return { selector: c.selector, type: 'attribute', attribute: 'id', value: c.value };
|
|
2820
|
+
return { selector: c.selector, type: 'attribute', attribute: 'id', value: c.value, vveTs: c.vveTs || nextHistoryTimestamp() };
|
|
2612
2821
|
case 'pp-href':
|
|
2613
|
-
return { selector: c.selector, type: 'attribute', attribute: 'href', value: c.value };
|
|
2822
|
+
return { selector: c.selector, type: 'attribute', attribute: 'href', value: c.value, vveTs: c.vveTs || nextHistoryTimestamp() };
|
|
2614
2823
|
case 'pp-target':
|
|
2615
|
-
return { selector: c.selector, type: 'attribute', attribute: 'target', value: c.value };
|
|
2824
|
+
return { selector: c.selector, type: 'attribute', attribute: 'target', value: c.value, vveTs: c.vveTs || nextHistoryTimestamp() };
|
|
2616
2825
|
case 'pp-src':
|
|
2617
|
-
return { selector: c.selector, type: 'attribute', attribute: 'src', value: c.value };
|
|
2826
|
+
return { selector: c.selector, type: 'attribute', attribute: 'src', value: c.value, vveTs: c.vveTs || nextHistoryTimestamp() };
|
|
2618
2827
|
case 'pp-alt':
|
|
2619
|
-
return { selector: c.selector, type: 'attribute', attribute: 'alt', value: c.value };
|
|
2828
|
+
return { selector: c.selector, type: 'attribute', attribute: 'alt', value: c.value, vveTs: c.vveTs || nextHistoryTimestamp() };
|
|
2620
2829
|
case 'pp-ph':
|
|
2621
|
-
return { selector: c.selector, type: 'attribute', attribute: 'placeholder', value: c.value };
|
|
2830
|
+
return { selector: c.selector, type: 'attribute', attribute: 'placeholder', value: c.value, vveTs: c.vveTs || nextHistoryTimestamp() };
|
|
2622
2831
|
case 'pp-css':
|
|
2623
|
-
return { selector: c.selector, type: 'attribute', attribute: 'style', value: c.value };
|
|
2832
|
+
return { selector: c.selector, type: 'attribute', attribute: 'style', value: c.value, vveTs: c.vveTs || nextHistoryTimestamp() };
|
|
2624
2833
|
case 'pp-mob-css':
|
|
2625
|
-
return { selector: c.selector, type: 'attribute', attribute: 'data-mobile-css', value: c.value };
|
|
2834
|
+
return { selector: c.selector, type: 'attribute', attribute: 'data-mobile-css', value: c.value, vveTs: c.vveTs || nextHistoryTimestamp() };
|
|
2626
2835
|
case 'pp-tab-css':
|
|
2627
|
-
return { selector: c.selector, type: 'attribute', attribute: 'data-tablet-css', value: c.value };
|
|
2836
|
+
return { selector: c.selector, type: 'attribute', attribute: 'data-tablet-css', value: c.value, vveTs: c.vveTs || nextHistoryTimestamp() };
|
|
2628
2837
|
default:
|
|
2629
2838
|
return null;
|
|
2630
2839
|
}
|
|
@@ -2912,7 +3121,7 @@ function applyActiveVariationHtml() {
|
|
|
2912
3121
|
if (!iframeDoc || !iframeDoc.body) return;
|
|
2913
3122
|
|
|
2914
3123
|
var variation = variations.find(function(v) { return v._id === activeVarId; });
|
|
2915
|
-
var cs =
|
|
3124
|
+
var cs = buildPersistedChainSetsForVariation(variation);
|
|
2916
3125
|
refreshPersistentChangesetStyleTagForActiveVariation();
|
|
2917
3126
|
|
|
2918
3127
|
beginSuppressIframeMutationDirty();
|
|
@@ -2970,7 +3179,7 @@ function applyVariationGranularOnly(iframeDoc) {
|
|
|
2970
3179
|
if (!activeVarId || !iframeDoc || !iframeDoc.body) return;
|
|
2971
3180
|
if (varHtmlCache[activeVarId]) return;
|
|
2972
3181
|
var variation = variations.find(function(v) { return v._id === activeVarId; });
|
|
2973
|
-
var cs =
|
|
3182
|
+
var cs = buildPersistedChainSetsForVariation(variation);
|
|
2974
3183
|
if (!cs.length || changesetsHaveBodySnapshot(cs)) return;
|
|
2975
3184
|
beginSuppressIframeMutationDirty();
|
|
2976
3185
|
try {
|
|
@@ -2989,7 +3198,7 @@ function reapplyActiveVariationGranular(iframeDoc) {
|
|
|
2989
3198
|
if (!activeVarId || !iframeDoc || !iframeDoc.body) return;
|
|
2990
3199
|
if (varHtmlCache[activeVarId]) return;
|
|
2991
3200
|
var variation = variations.find(function(v) { return v._id === activeVarId; });
|
|
2992
|
-
var cs =
|
|
3201
|
+
var cs = buildPersistedChainSetsForVariation(variation);
|
|
2993
3202
|
if (!cs.length || changesetsHaveBodySnapshot(cs)) return;
|
|
2994
3203
|
beginSuppressIframeMutationDirty();
|
|
2995
3204
|
try {
|
|
@@ -3033,7 +3242,7 @@ function startIframeContentApplyWatcher(navGen, prevDocRef) {
|
|
|
3033
3242
|
|
|
3034
3243
|
if (doc.readyState === 'loading') {
|
|
3035
3244
|
var variation = variations.find(function(v) { return v._id === activeVarId; });
|
|
3036
|
-
var cs0 =
|
|
3245
|
+
var cs0 = buildPersistedChainSetsForVariation(variation);
|
|
3037
3246
|
if (!cs0.length || changesetsHaveBodySnapshot(cs0)) return;
|
|
3038
3247
|
var granular = filterGranularChangesetEntries(cs0);
|
|
3039
3248
|
if (!granular.length) return;
|
|
@@ -3079,8 +3288,9 @@ function selectElement(el) {
|
|
|
3079
3288
|
document.getElementById('no-sel').style.display = 'none';
|
|
3080
3289
|
renderRightPanel(el);
|
|
3081
3290
|
updateSelectionToolbar();
|
|
3082
|
-
if (currentLeftTab === 'elements') {
|
|
3083
|
-
var
|
|
3291
|
+
if (currentLeftTab === 'elements' || currentLeftTab === 'dom-tree') {
|
|
3292
|
+
var treeRootId = currentLeftTab === 'elements' ? 'elements-root' : 'dom-tree-root';
|
|
3293
|
+
var dr = document.getElementById(treeRootId);
|
|
3084
3294
|
if (dr && dr.querySelector('.dt-row')) syncDomTreeSelection();
|
|
3085
3295
|
else scheduleDomTreeRefresh();
|
|
3086
3296
|
}
|
|
@@ -3343,27 +3553,32 @@ function deleteSelectedEl() {
|
|
|
3343
3553
|
}
|
|
3344
3554
|
|
|
3345
3555
|
function syncDomTreeSelection() {
|
|
3346
|
-
var
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
rows
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3556
|
+
var roots = ['dom-tree-root', 'elements-root'];
|
|
3557
|
+
for (var r = 0; r < roots.length; r++) {
|
|
3558
|
+
var root = document.getElementById(roots[r]);
|
|
3559
|
+
if (!root) continue;
|
|
3560
|
+
var rows = root.querySelectorAll('.dt-row');
|
|
3561
|
+
for (var i = 0; i < rows.length; i++) {
|
|
3562
|
+
rows[i].classList.toggle('dt-selected', !!(selectedEl && rows[i]._dtEl === selectedEl));
|
|
3563
|
+
}
|
|
3564
|
+
if (!selectedEl) continue;
|
|
3565
|
+
var found = null;
|
|
3566
|
+
for (var j = 0; j < rows.length; j++) {
|
|
3567
|
+
if (rows[j]._dtEl === selectedEl) { found = rows[j]; break; }
|
|
3568
|
+
}
|
|
3569
|
+
if (found) found.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
|
|
3356
3570
|
}
|
|
3357
|
-
if (found) found.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
|
|
3358
3571
|
}
|
|
3359
3572
|
|
|
3360
3573
|
function scheduleDomTreeRefresh() {
|
|
3361
|
-
if (currentLeftTab !== 'elements') return;
|
|
3574
|
+
if (currentLeftTab !== 'elements' && currentLeftTab !== 'dom-tree') return;
|
|
3362
3575
|
if (domTreeRefreshTimer) clearTimeout(domTreeRefreshTimer);
|
|
3363
3576
|
domTreeRefreshTimer = setTimeout(function() {
|
|
3364
3577
|
domTreeRefreshTimer = null;
|
|
3365
3578
|
var inp = document.getElementById('comp-search');
|
|
3366
|
-
|
|
3579
|
+
var q = inp ? inp.value : '';
|
|
3580
|
+
if (currentLeftTab === 'elements') renderElementsTree(q);
|
|
3581
|
+
else if (currentLeftTab === 'dom-tree') renderDomTree(q);
|
|
3367
3582
|
}, 150);
|
|
3368
3583
|
}
|
|
3369
3584
|
|
|
@@ -3386,6 +3601,129 @@ function domTreePathSegment(el) {
|
|
|
3386
3601
|
return tag + '[' + idx + ']';
|
|
3387
3602
|
}
|
|
3388
3603
|
|
|
3604
|
+
function renderElementsTree(filterRaw) {
|
|
3605
|
+
var filterText = (filterRaw || '').toLowerCase().trim();
|
|
3606
|
+
var root = document.getElementById('elements-root');
|
|
3607
|
+
if (!root) return;
|
|
3608
|
+
var iframe = document.getElementById('iframeId');
|
|
3609
|
+
var doc = iframe && iframe.contentDocument;
|
|
3610
|
+
if (!isIframeDomReady(iframe, doc)) {
|
|
3611
|
+
root.innerHTML = '<div class="dt-muted">Load a page to see the elements.</div>';
|
|
3612
|
+
return;
|
|
3613
|
+
}
|
|
3614
|
+
|
|
3615
|
+
function skippable(el) {
|
|
3616
|
+
return isDomTreeSkippableTagName(el.tagName);
|
|
3617
|
+
}
|
|
3618
|
+
|
|
3619
|
+
function nodeIcon(tag) {
|
|
3620
|
+
tag = (tag || '').toLowerCase();
|
|
3621
|
+
if (/^h[1-6]$/.test(tag)) return 'bi bi-type-h1';
|
|
3622
|
+
if (tag === 'a') return 'bi bi-link-45deg';
|
|
3623
|
+
if (tag === 'img') return 'bi bi-image';
|
|
3624
|
+
if (tag === 'section' || tag === 'main' || tag === 'article' || tag === 'header' || tag === 'footer' || tag === 'nav') return 'bi bi-layout-three-columns';
|
|
3625
|
+
if (tag === 'button' || tag === 'input' || tag === 'select' || tag === 'textarea') return 'bi bi-ui-radios';
|
|
3626
|
+
if (tag === 'ul' || tag === 'ol') return 'bi bi-list-ul';
|
|
3627
|
+
if (tag === 'li') return 'bi bi-dot';
|
|
3628
|
+
if (tag === 'svg') return 'bi bi-bezier2';
|
|
3629
|
+
if (tag === 'p' || tag === 'span') return 'bi bi-text-left';
|
|
3630
|
+
return 'bi bi-square';
|
|
3631
|
+
}
|
|
3632
|
+
|
|
3633
|
+
function labelFor(el) {
|
|
3634
|
+
var tag = (el.tagName || '').toLowerCase();
|
|
3635
|
+
return tag.toUpperCase();
|
|
3636
|
+
}
|
|
3637
|
+
|
|
3638
|
+
function isListableNode(el) {
|
|
3639
|
+
if (!el || el.nodeType !== 1) return false;
|
|
3640
|
+
if (skippable(el)) return false;
|
|
3641
|
+
var tag = (el.tagName || '').toLowerCase();
|
|
3642
|
+
if (tag !== 'svg') {
|
|
3643
|
+
var p = el.parentElement;
|
|
3644
|
+
while (p) {
|
|
3645
|
+
if ((p.tagName || '').toLowerCase() === 'svg') return false;
|
|
3646
|
+
p = p.parentElement;
|
|
3647
|
+
}
|
|
3648
|
+
}
|
|
3649
|
+
return true;
|
|
3650
|
+
}
|
|
3651
|
+
|
|
3652
|
+
var nodes = [];
|
|
3653
|
+
var i, cursor;
|
|
3654
|
+
try {
|
|
3655
|
+
cursor = doc.createTreeWalker(doc.body, NodeFilter.SHOW_ELEMENT, null);
|
|
3656
|
+
} catch(_) {
|
|
3657
|
+
cursor = null;
|
|
3658
|
+
}
|
|
3659
|
+
if (cursor) {
|
|
3660
|
+
while (cursor.nextNode()) {
|
|
3661
|
+
var node = cursor.currentNode;
|
|
3662
|
+
if (isListableNode(node)) nodes.push(node);
|
|
3663
|
+
if (nodes.length > 4000) break;
|
|
3664
|
+
}
|
|
3665
|
+
} else {
|
|
3666
|
+
function collectFlat(el) {
|
|
3667
|
+
if (!el || !el.children) return;
|
|
3668
|
+
for (var j = 0; j < el.children.length; j++) {
|
|
3669
|
+
var c = el.children[j];
|
|
3670
|
+
if (!isListableNode(c)) continue;
|
|
3671
|
+
nodes.push(c);
|
|
3672
|
+
if (nodes.length > 4000) return;
|
|
3673
|
+
collectFlat(c);
|
|
3674
|
+
if (nodes.length > 4000) return;
|
|
3675
|
+
}
|
|
3676
|
+
}
|
|
3677
|
+
collectFlat(doc.body);
|
|
3678
|
+
}
|
|
3679
|
+
|
|
3680
|
+
root.innerHTML = '';
|
|
3681
|
+
for (i = 0; i < nodes.length; i++) {
|
|
3682
|
+
var el = nodes[i];
|
|
3683
|
+
var lblText = labelFor(el);
|
|
3684
|
+
if (filterText && lblText.toLowerCase().indexOf(filterText) < 0) continue;
|
|
3685
|
+
|
|
3686
|
+
var row = document.createElement('div');
|
|
3687
|
+
row.className = 'dt-row';
|
|
3688
|
+
row._dtEl = el;
|
|
3689
|
+
if (el === selectedEl) row.classList.add('dt-selected');
|
|
3690
|
+
row.style.paddingLeft = '4px';
|
|
3691
|
+
|
|
3692
|
+
var spacer = document.createElement('button');
|
|
3693
|
+
spacer.type = 'button';
|
|
3694
|
+
spacer.className = 'dt-chev dt-spacer';
|
|
3695
|
+
|
|
3696
|
+
var ico = document.createElement('div');
|
|
3697
|
+
ico.className = 'dt-ico';
|
|
3698
|
+
ico.innerHTML = '<i class="' + nodeIcon(el.tagName) + '"></i>';
|
|
3699
|
+
|
|
3700
|
+
var lbl = document.createElement('div');
|
|
3701
|
+
lbl.className = 'dt-lbl';
|
|
3702
|
+
lbl.textContent = lblText;
|
|
3703
|
+
lbl.title = buildSelector(el);
|
|
3704
|
+
|
|
3705
|
+
row.appendChild(spacer);
|
|
3706
|
+
row.appendChild(ico);
|
|
3707
|
+
row.appendChild(lbl);
|
|
3708
|
+
row.onclick = (function(targetEl) {
|
|
3709
|
+
return function() { selectElementFromTree(targetEl); };
|
|
3710
|
+
})(el);
|
|
3711
|
+
row.onmouseenter = (function(targetEl) {
|
|
3712
|
+
return function() { setTreeHoverHighlight(targetEl); };
|
|
3713
|
+
})(el);
|
|
3714
|
+
root.appendChild(row);
|
|
3715
|
+
}
|
|
3716
|
+
|
|
3717
|
+
if (!root.querySelector('.dt-row')) {
|
|
3718
|
+
root.innerHTML = filterText
|
|
3719
|
+
? '<div class="dt-muted">No elements match your search.</div>'
|
|
3720
|
+
: '<div class="dt-muted">No elements found.</div>';
|
|
3721
|
+
}
|
|
3722
|
+
root.onmouseleave = function() {
|
|
3723
|
+
clearTreeHoverHighlight();
|
|
3724
|
+
};
|
|
3725
|
+
}
|
|
3726
|
+
|
|
3389
3727
|
function renderDomTree(filterRaw) {
|
|
3390
3728
|
var filterText = (filterRaw || '').toLowerCase().trim();
|
|
3391
3729
|
var root = document.getElementById('dom-tree-root');
|
|
@@ -4124,6 +4462,20 @@ function recordReorderAfterDrag(movedEl) {
|
|
|
4124
4462
|
}
|
|
4125
4463
|
}
|
|
4126
4464
|
|
|
4465
|
+
function moveSelectedElByDirection(direction) {
|
|
4466
|
+
if (!selectedEl || !selectedEl.parentElement) return;
|
|
4467
|
+
var p = selectedEl.parentElement;
|
|
4468
|
+
var sibling = direction < 0 ? selectedEl.previousElementSibling : selectedEl.nextElementSibling;
|
|
4469
|
+
if (!sibling) return;
|
|
4470
|
+
if (direction < 0) p.insertBefore(selectedEl, sibling);
|
|
4471
|
+
else p.insertBefore(sibling, selectedEl);
|
|
4472
|
+
recordReorderAfterDrag(selectedEl);
|
|
4473
|
+
saveCurrentVariationHtml();
|
|
4474
|
+
recomputeEditorDirty();
|
|
4475
|
+
scheduleDomTreeRefresh();
|
|
4476
|
+
updateSelectionToolbar();
|
|
4477
|
+
}
|
|
4478
|
+
|
|
4127
4479
|
function attachDragReposition() {
|
|
4128
4480
|
try {
|
|
4129
4481
|
var iframe = document.getElementById('iframeId');
|
|
@@ -4310,7 +4662,9 @@ function syncIframeInteractions(reason) {
|
|
|
4310
4662
|
scheduleConsistencyReconcile();
|
|
4311
4663
|
bindSelectionToolbarScroll();
|
|
4312
4664
|
var inp = document.getElementById('comp-search');
|
|
4313
|
-
|
|
4665
|
+
var q = inp ? inp.value : '';
|
|
4666
|
+
if (currentLeftTab === 'elements') renderElementsTree(q);
|
|
4667
|
+
else if (currentLeftTab === 'dom-tree') renderDomTree(q);
|
|
4314
4668
|
updateSelectionToolbar();
|
|
4315
4669
|
recomputeEditorDirty();
|
|
4316
4670
|
} catch(_) {}
|
|
@@ -4474,8 +4828,11 @@ function renderSidebar(filter) {
|
|
|
4474
4828
|
}
|
|
4475
4829
|
|
|
4476
4830
|
document.getElementById('comp-search').addEventListener('input', function() {
|
|
4477
|
-
if (currentLeftTab === 'elements')
|
|
4478
|
-
else
|
|
4831
|
+
if (currentLeftTab === 'elements') renderElementsTree(this.value);
|
|
4832
|
+
else if (currentLeftTab === 'dom-tree') renderDomTree(this.value);
|
|
4833
|
+
if (currentSectionComponentsTab === 'components' || currentSectionComponentsTab === 'sections') {
|
|
4834
|
+
renderSidebar(this.value);
|
|
4835
|
+
}
|
|
4479
4836
|
});
|
|
4480
4837
|
|
|
4481
4838
|
// \u2500\u2500 Save / Close \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
|
|
@@ -4541,19 +4898,11 @@ document.addEventListener('keydown', function(e) {
|
|
|
4541
4898
|
var k = (e.key || '').toLowerCase();
|
|
4542
4899
|
if (meta && !e.shiftKey && k === 'z') {
|
|
4543
4900
|
e.preventDefault();
|
|
4544
|
-
|
|
4545
|
-
Vvveb.Undo.undo();
|
|
4546
|
-
saveCurrentVariationHtml();
|
|
4547
|
-
recomputeEditorDirty();
|
|
4548
|
-
}
|
|
4901
|
+
runEditorUndo();
|
|
4549
4902
|
}
|
|
4550
4903
|
if (meta && e.shiftKey && k === 'z') {
|
|
4551
4904
|
e.preventDefault();
|
|
4552
|
-
|
|
4553
|
-
Vvveb.Undo.redo();
|
|
4554
|
-
saveCurrentVariationHtml();
|
|
4555
|
-
recomputeEditorDirty();
|
|
4556
|
-
}
|
|
4905
|
+
runEditorRedo();
|
|
4557
4906
|
}
|
|
4558
4907
|
if (meta && e.key === 's') { e.preventDefault(); handleSave(); }
|
|
4559
4908
|
if (e.key === 'Escape') {
|
|
@@ -4573,8 +4922,39 @@ document.addEventListener('keydown', function(e) {
|
|
|
4573
4922
|
if (selectedEl) deselectElement();
|
|
4574
4923
|
}
|
|
4575
4924
|
});
|
|
4576
|
-
|
|
4577
|
-
|
|
4925
|
+
function runEditorUndo() {
|
|
4926
|
+
var target = getLatestHistoryUndoTarget();
|
|
4927
|
+
if (target) {
|
|
4928
|
+
removeHistoryItem(target.source, target.idx);
|
|
4929
|
+
return;
|
|
4930
|
+
}
|
|
4931
|
+
if (!(typeof Vvveb !== 'undefined' && Vvveb.Undo)) return;
|
|
4932
|
+
Vvveb.Undo.undo();
|
|
4933
|
+
saveCurrentVariationHtml();
|
|
4934
|
+
recomputeEditorDirty();
|
|
4935
|
+
scheduleDomTreeRefresh();
|
|
4936
|
+
updateSelectionToolbar();
|
|
4937
|
+
}
|
|
4938
|
+
|
|
4939
|
+
function runEditorRedo() {
|
|
4940
|
+
if (!(typeof Vvveb !== 'undefined' && Vvveb.Undo)) return;
|
|
4941
|
+
Vvveb.Undo.redo();
|
|
4942
|
+
saveCurrentVariationHtml();
|
|
4943
|
+
recomputeEditorDirty();
|
|
4944
|
+
scheduleDomTreeRefresh();
|
|
4945
|
+
updateSelectionToolbar();
|
|
4946
|
+
}
|
|
4947
|
+
|
|
4948
|
+
document.getElementById('btn-undo').addEventListener('click', function(e) {
|
|
4949
|
+
e.preventDefault();
|
|
4950
|
+
e.stopPropagation();
|
|
4951
|
+
runEditorUndo();
|
|
4952
|
+
});
|
|
4953
|
+
document.getElementById('btn-redo').addEventListener('click', function(e) {
|
|
4954
|
+
e.preventDefault();
|
|
4955
|
+
e.stopPropagation();
|
|
4956
|
+
runEditorRedo();
|
|
4957
|
+
});
|
|
4578
4958
|
|
|
4579
4959
|
function layoutLoadingTooltip(host) {
|
|
4580
4960
|
var tip = host.querySelector('.ve-pl-tooltip');
|
|
@@ -4650,8 +5030,8 @@ function registerCROSections() {
|
|
|
4650
5030
|
|
|
4651
5031
|
window.addEventListener('load', function() {
|
|
4652
5032
|
registerCROSections();
|
|
4653
|
-
|
|
4654
|
-
|
|
5033
|
+
switchSectionComponentsTab(currentSectionComponentsTab);
|
|
5034
|
+
renderElementsTree(document.getElementById('comp-search').value);
|
|
4655
5035
|
vvvebReady = true;
|
|
4656
5036
|
bindLoadingTooltipPositioning();
|
|
4657
5037
|
|
|
@@ -4704,12 +5084,22 @@ window.addEventListener('load', function() {
|
|
|
4704
5084
|
syncIframeInteractions('iframe-load');
|
|
4705
5085
|
});
|
|
4706
5086
|
|
|
4707
|
-
document.getElementById('sf-
|
|
4708
|
-
|
|
4709
|
-
|
|
4710
|
-
|
|
4711
|
-
|
|
4712
|
-
|
|
5087
|
+
var sfMoveUp = document.getElementById('sf-move-up');
|
|
5088
|
+
if (sfMoveUp) {
|
|
5089
|
+
sfMoveUp.addEventListener('click', function(e) {
|
|
5090
|
+
e.preventDefault();
|
|
5091
|
+
e.stopPropagation();
|
|
5092
|
+
moveSelectedElByDirection(-1);
|
|
5093
|
+
});
|
|
5094
|
+
}
|
|
5095
|
+
var sfMoveDown = document.getElementById('sf-move-down');
|
|
5096
|
+
if (sfMoveDown) {
|
|
5097
|
+
sfMoveDown.addEventListener('click', function(e) {
|
|
5098
|
+
e.preventDefault();
|
|
5099
|
+
e.stopPropagation();
|
|
5100
|
+
moveSelectedElByDirection(1);
|
|
5101
|
+
});
|
|
5102
|
+
}
|
|
4713
5103
|
document.getElementById('sf-dup').addEventListener('click', function(e) {
|
|
4714
5104
|
e.preventDefault();
|
|
4715
5105
|
e.stopPropagation();
|