@accelerated-agency/visual-editor 0.1.6 → 0.1.7
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/bridge-channel-ISTPKGMY.js +1 -10
- package/dist/chunk-WCSTG2IY.js +1 -5
- package/dist/index.js +651 -743
- package/dist/vite.cjs +2067 -0
- package/dist/vite.js +25 -11
- package/package.json +5 -4
package/dist/index.js
CHANGED
|
@@ -1,18 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
} from
|
|
1
|
+
import { sendToBridge, setIframeRef, capturePageSnapshot } from './chunk-WCSTG2IY.js';
|
|
2
|
+
import { memo, createContext, useContext, useState, useCallback, useEffect, useRef, useMemo, useLayoutEffect } from 'react';
|
|
3
|
+
import { create } from 'zustand';
|
|
4
|
+
import { persist } from 'zustand/middleware';
|
|
5
|
+
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
6
|
+
import { HexColorPicker } from 'react-colorful';
|
|
6
7
|
|
|
7
|
-
// src/components/EditorShell.tsx
|
|
8
|
-
import { useCallback as useCallback13, useEffect as useEffect11, useRef as useRef9, useState as useState12 } from "react";
|
|
9
|
-
|
|
10
|
-
// src/components/TopBar.tsx
|
|
11
|
-
import { useState, useEffect, useRef, useCallback } from "react";
|
|
12
|
-
|
|
13
|
-
// src/store/variations.ts
|
|
14
|
-
import { create } from "zustand";
|
|
15
|
-
import { persist } from "zustand/middleware";
|
|
16
8
|
var VARIATION_COLORS = [
|
|
17
9
|
"#0084D1",
|
|
18
10
|
"#F54A00",
|
|
@@ -165,9 +157,6 @@ var useVariationsStore = create()(
|
|
|
165
157
|
}
|
|
166
158
|
)
|
|
167
159
|
);
|
|
168
|
-
|
|
169
|
-
// src/components/TopBar.tsx
|
|
170
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
171
160
|
function TopBar({
|
|
172
161
|
connectionStatus,
|
|
173
162
|
onLoadUrl,
|
|
@@ -478,15 +467,8 @@ function TopBar({
|
|
|
478
467
|
}
|
|
479
468
|
);
|
|
480
469
|
}
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
import { useState as useState3, useCallback as useCallback3, useEffect as useEffect3, useRef as useRef3, useMemo as useMemo2 } from "react";
|
|
484
|
-
|
|
485
|
-
// src/store/mutations.ts
|
|
486
|
-
import { create as create2 } from "zustand";
|
|
487
|
-
import { persist as persist2 } from "zustand/middleware";
|
|
488
|
-
var useMutationsStore = create2()(
|
|
489
|
-
persist2(
|
|
470
|
+
var useMutationsStore = create()(
|
|
471
|
+
persist(
|
|
490
472
|
(set) => ({
|
|
491
473
|
mutations: [],
|
|
492
474
|
selectedElement: null,
|
|
@@ -517,9 +499,6 @@ var useMutationsStore = create2()(
|
|
|
517
499
|
}
|
|
518
500
|
)
|
|
519
501
|
);
|
|
520
|
-
|
|
521
|
-
// src/store/sections.ts
|
|
522
|
-
import { create as create3 } from "zustand";
|
|
523
502
|
var STORAGE_KEY = "conversion-section-library";
|
|
524
503
|
var DEFAULT_CATEGORIES = [
|
|
525
504
|
{ id: "hero", name: "Hero", icon: "\u{1F3E0}" },
|
|
@@ -545,7 +524,7 @@ var EMPTY_LIBRARY = {
|
|
|
545
524
|
categories: DEFAULT_CATEGORIES,
|
|
546
525
|
lastSynced: ""
|
|
547
526
|
};
|
|
548
|
-
var useSectionsStore =
|
|
527
|
+
var useSectionsStore = create((set, get) => ({
|
|
549
528
|
library: EMPTY_LIBRARY,
|
|
550
529
|
searchQuery: "",
|
|
551
530
|
activeCategory: "all",
|
|
@@ -701,43 +680,39 @@ async function refreshLibrary(themeRepo = "Shopify/dawn") {
|
|
|
701
680
|
}
|
|
702
681
|
return added;
|
|
703
682
|
}
|
|
704
|
-
|
|
705
|
-
// src/components/LayerPanel.tsx
|
|
706
|
-
import { useState as useState2, useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, memo, useMemo } from "react";
|
|
707
|
-
import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
708
683
|
function TypeIcon({ type }) {
|
|
709
684
|
const color = "#666666";
|
|
710
685
|
const size = 14;
|
|
711
686
|
switch (type) {
|
|
712
687
|
case "section":
|
|
713
|
-
return /* @__PURE__ */
|
|
714
|
-
/* @__PURE__ */
|
|
715
|
-
/* @__PURE__ */
|
|
688
|
+
return /* @__PURE__ */ jsxs("svg", { width: size, height: size, viewBox: "0 0 14 14", fill: "none", children: [
|
|
689
|
+
/* @__PURE__ */ jsx("rect", { x: "1.5", y: "1.5", width: "11", height: "4", rx: "1", stroke: color, strokeWidth: "1.2" }),
|
|
690
|
+
/* @__PURE__ */ jsx("rect", { x: "1.5", y: "8.5", width: "11", height: "4", rx: "1", stroke: color, strokeWidth: "1.2" })
|
|
716
691
|
] });
|
|
717
692
|
case "image":
|
|
718
|
-
return /* @__PURE__ */
|
|
719
|
-
/* @__PURE__ */
|
|
720
|
-
/* @__PURE__ */
|
|
721
|
-
/* @__PURE__ */
|
|
693
|
+
return /* @__PURE__ */ jsxs("svg", { width: size, height: size, viewBox: "0 0 14 14", fill: "none", children: [
|
|
694
|
+
/* @__PURE__ */ jsx("rect", { x: "1.5", y: "1.5", width: "11", height: "11", rx: "1", stroke: color, strokeWidth: "1.2" }),
|
|
695
|
+
/* @__PURE__ */ jsx("circle", { cx: "5", cy: "5.5", r: "1.2", fill: color }),
|
|
696
|
+
/* @__PURE__ */ jsx("path", { d: "M1.5 11l3-3 2 2 2-2 4 4", stroke: color, strokeWidth: "1.2", strokeLinejoin: "round" })
|
|
722
697
|
] });
|
|
723
698
|
case "text":
|
|
724
|
-
return /* @__PURE__ */
|
|
699
|
+
return /* @__PURE__ */ jsx("svg", { width: size, height: size, viewBox: "0 0 14 14", fill: "none", children: /* @__PURE__ */ jsx("path", { d: "M3 4h8M3 7h5M3 10h7", stroke: color, strokeWidth: "1.2", strokeLinecap: "round" }) });
|
|
725
700
|
case "link":
|
|
726
|
-
return /* @__PURE__ */
|
|
701
|
+
return /* @__PURE__ */ jsx("svg", { width: size, height: size, viewBox: "0 0 14 14", fill: "none", children: /* @__PURE__ */ jsx("path", { d: "M6 8l2-2M5 9.5L3.5 8a2 2 0 010-2.83l1-1a2 2 0 012.83 0M9 4.5l1.5 1.5a2 2 0 010 2.83l-1 1a2 2 0 01-2.83 0", stroke: color, strokeWidth: "1.2", strokeLinecap: "round" }) });
|
|
727
702
|
case "button":
|
|
728
|
-
return /* @__PURE__ */
|
|
729
|
-
/* @__PURE__ */
|
|
730
|
-
/* @__PURE__ */
|
|
703
|
+
return /* @__PURE__ */ jsxs("svg", { width: size, height: size, viewBox: "0 0 14 14", fill: "none", children: [
|
|
704
|
+
/* @__PURE__ */ jsx("rect", { x: "1.5", y: "3.5", width: "11", height: "7", rx: "2", stroke: color, strokeWidth: "1.2" }),
|
|
705
|
+
/* @__PURE__ */ jsx("path", { d: "M5 7h4", stroke: color, strokeWidth: "1.2", strokeLinecap: "round" })
|
|
731
706
|
] });
|
|
732
707
|
case "form":
|
|
733
|
-
return /* @__PURE__ */
|
|
734
|
-
/* @__PURE__ */
|
|
735
|
-
/* @__PURE__ */
|
|
708
|
+
return /* @__PURE__ */ jsxs("svg", { width: size, height: size, viewBox: "0 0 14 14", fill: "none", children: [
|
|
709
|
+
/* @__PURE__ */ jsx("rect", { x: "1.5", y: "1.5", width: "11", height: "11", rx: "1", stroke: color, strokeWidth: "1.2" }),
|
|
710
|
+
/* @__PURE__ */ jsx("path", { d: "M4 5h6M4 7.5h4M4 10h5", stroke: color, strokeWidth: "1.2", strokeLinecap: "round" })
|
|
736
711
|
] });
|
|
737
712
|
case "container":
|
|
738
|
-
return /* @__PURE__ */
|
|
713
|
+
return /* @__PURE__ */ jsx("svg", { width: size, height: size, viewBox: "0 0 14 14", fill: "none", children: /* @__PURE__ */ jsx("rect", { x: "1.5", y: "1.5", width: "11", height: "11", rx: "1.5", stroke: color, strokeWidth: "1.2", strokeDasharray: "2 2" }) });
|
|
739
714
|
default:
|
|
740
|
-
return /* @__PURE__ */
|
|
715
|
+
return /* @__PURE__ */ jsx("svg", { width: size, height: size, viewBox: "0 0 14 14", fill: "none", children: /* @__PURE__ */ jsx("rect", { x: "2", y: "2", width: "10", height: "10", rx: "1.5", stroke: color, strokeWidth: "1.2" }) });
|
|
741
716
|
}
|
|
742
717
|
}
|
|
743
718
|
var TreeNodeRow = memo(function TreeNodeRow2({
|
|
@@ -766,8 +741,8 @@ var TreeNodeRow = memo(function TreeNodeRow2({
|
|
|
766
741
|
});
|
|
767
742
|
if (!matches && !childrenMatch) return null;
|
|
768
743
|
}
|
|
769
|
-
return /* @__PURE__ */
|
|
770
|
-
/* @__PURE__ */
|
|
744
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
745
|
+
/* @__PURE__ */ jsxs(
|
|
771
746
|
"div",
|
|
772
747
|
{
|
|
773
748
|
className: "flex items-center gap-1 cursor-pointer transition-colors",
|
|
@@ -784,7 +759,7 @@ var TreeNodeRow = memo(function TreeNodeRow2({
|
|
|
784
759
|
onContextMenu: (e) => onContextMenu(e, node),
|
|
785
760
|
title: node.selector,
|
|
786
761
|
children: [
|
|
787
|
-
/* @__PURE__ */
|
|
762
|
+
/* @__PURE__ */ jsx(
|
|
788
763
|
"button",
|
|
789
764
|
{
|
|
790
765
|
onClick: (e) => {
|
|
@@ -793,7 +768,7 @@ var TreeNodeRow = memo(function TreeNodeRow2({
|
|
|
793
768
|
},
|
|
794
769
|
className: "w-4 h-4 flex items-center justify-center shrink-0",
|
|
795
770
|
style: { opacity: hasChildren ? 1 : 0, cursor: hasChildren ? "pointer" : "default" },
|
|
796
|
-
children: /* @__PURE__ */
|
|
771
|
+
children: /* @__PURE__ */ jsx(
|
|
797
772
|
"svg",
|
|
798
773
|
{
|
|
799
774
|
width: "8",
|
|
@@ -804,13 +779,13 @@ var TreeNodeRow = memo(function TreeNodeRow2({
|
|
|
804
779
|
transform: isExpanded ? "rotate(90deg)" : "rotate(0deg)",
|
|
805
780
|
transition: "transform 0.15s ease"
|
|
806
781
|
},
|
|
807
|
-
children: /* @__PURE__ */
|
|
782
|
+
children: /* @__PURE__ */ jsx("path", { d: "M2 1l4 3-4 3V1z" })
|
|
808
783
|
}
|
|
809
784
|
)
|
|
810
785
|
}
|
|
811
786
|
),
|
|
812
|
-
/* @__PURE__ */
|
|
813
|
-
/* @__PURE__ */
|
|
787
|
+
/* @__PURE__ */ jsx("span", { className: "shrink-0 flex items-center justify-center", children: /* @__PURE__ */ jsx(TypeIcon, { type: node.type }) }),
|
|
788
|
+
/* @__PURE__ */ jsx(
|
|
814
789
|
"span",
|
|
815
790
|
{
|
|
816
791
|
className: "text-[12px] truncate flex-1",
|
|
@@ -822,7 +797,7 @@ var TreeNodeRow = memo(function TreeNodeRow2({
|
|
|
822
797
|
children: node.label
|
|
823
798
|
}
|
|
824
799
|
),
|
|
825
|
-
hasMutation && /* @__PURE__ */
|
|
800
|
+
hasMutation && /* @__PURE__ */ jsx(
|
|
826
801
|
"span",
|
|
827
802
|
{
|
|
828
803
|
className: "shrink-0 w-[6px] h-[6px] rounded-full",
|
|
@@ -832,7 +807,7 @@ var TreeNodeRow = memo(function TreeNodeRow2({
|
|
|
832
807
|
]
|
|
833
808
|
}
|
|
834
809
|
),
|
|
835
|
-
isExpanded && hasChildren && node.children.map((child) => /* @__PURE__ */
|
|
810
|
+
isExpanded && hasChildren && node.children.map((child) => /* @__PURE__ */ jsx(
|
|
836
811
|
TreeNodeRow2,
|
|
837
812
|
{
|
|
838
813
|
node: child,
|
|
@@ -851,8 +826,8 @@ var TreeNodeRow = memo(function TreeNodeRow2({
|
|
|
851
826
|
] });
|
|
852
827
|
});
|
|
853
828
|
function ContextMenu({ x, y, node, onClose, onSelect, onHide, onRemoveChanges, onCopySelector }) {
|
|
854
|
-
const menuRef =
|
|
855
|
-
|
|
829
|
+
const menuRef = useRef(null);
|
|
830
|
+
useEffect(() => {
|
|
856
831
|
function handleClick(e) {
|
|
857
832
|
if (menuRef.current && !menuRef.current.contains(e.target)) {
|
|
858
833
|
onClose();
|
|
@@ -861,14 +836,14 @@ function ContextMenu({ x, y, node, onClose, onSelect, onHide, onRemoveChanges, o
|
|
|
861
836
|
document.addEventListener("mousedown", handleClick);
|
|
862
837
|
return () => document.removeEventListener("mousedown", handleClick);
|
|
863
838
|
}, [onClose]);
|
|
864
|
-
return /* @__PURE__ */
|
|
839
|
+
return /* @__PURE__ */ jsxs(
|
|
865
840
|
"div",
|
|
866
841
|
{
|
|
867
842
|
ref: menuRef,
|
|
868
843
|
className: "fixed z-50 bg-white rounded-lg shadow-lg py-1",
|
|
869
844
|
style: { left: x, top: y, border: "1px solid #E5E5E5", minWidth: 160 },
|
|
870
845
|
children: [
|
|
871
|
-
/* @__PURE__ */
|
|
846
|
+
/* @__PURE__ */ jsx(
|
|
872
847
|
"button",
|
|
873
848
|
{
|
|
874
849
|
onClick: () => {
|
|
@@ -880,7 +855,7 @@ function ContextMenu({ x, y, node, onClose, onSelect, onHide, onRemoveChanges, o
|
|
|
880
855
|
children: "Select"
|
|
881
856
|
}
|
|
882
857
|
),
|
|
883
|
-
/* @__PURE__ */
|
|
858
|
+
/* @__PURE__ */ jsx(
|
|
884
859
|
"button",
|
|
885
860
|
{
|
|
886
861
|
onClick: () => {
|
|
@@ -892,7 +867,7 @@ function ContextMenu({ x, y, node, onClose, onSelect, onHide, onRemoveChanges, o
|
|
|
892
867
|
children: "Hide element"
|
|
893
868
|
}
|
|
894
869
|
),
|
|
895
|
-
/* @__PURE__ */
|
|
870
|
+
/* @__PURE__ */ jsx(
|
|
896
871
|
"button",
|
|
897
872
|
{
|
|
898
873
|
onClick: () => {
|
|
@@ -904,7 +879,7 @@ function ContextMenu({ x, y, node, onClose, onSelect, onHide, onRemoveChanges, o
|
|
|
904
879
|
children: "Remove changes"
|
|
905
880
|
}
|
|
906
881
|
),
|
|
907
|
-
/* @__PURE__ */
|
|
882
|
+
/* @__PURE__ */ jsx(
|
|
908
883
|
"button",
|
|
909
884
|
{
|
|
910
885
|
onClick: () => {
|
|
@@ -921,14 +896,14 @@ function ContextMenu({ x, y, node, onClose, onSelect, onHide, onRemoveChanges, o
|
|
|
921
896
|
);
|
|
922
897
|
}
|
|
923
898
|
function LayerPanel() {
|
|
924
|
-
const [tree, setTree] =
|
|
925
|
-
const [expandedIds, setExpandedIds] =
|
|
926
|
-
const [selectedNodeId, setSelectedNodeId] =
|
|
927
|
-
const [hoveredNodeId, setHoveredNodeId] =
|
|
928
|
-
const [searchQuery, setSearchQuery] =
|
|
929
|
-
const [contextMenu, setContextMenu] =
|
|
930
|
-
const treeContainerRef =
|
|
931
|
-
const reCaptureTimerRef =
|
|
899
|
+
const [tree, setTree] = useState([]);
|
|
900
|
+
const [expandedIds, setExpandedIds] = useState(/* @__PURE__ */ new Set());
|
|
901
|
+
const [selectedNodeId, setSelectedNodeId] = useState(null);
|
|
902
|
+
const [hoveredNodeId, setHoveredNodeId] = useState(null);
|
|
903
|
+
const [searchQuery, setSearchQuery] = useState("");
|
|
904
|
+
const [contextMenu, setContextMenu] = useState(null);
|
|
905
|
+
const treeContainerRef = useRef(null);
|
|
906
|
+
const reCaptureTimerRef = useRef(null);
|
|
932
907
|
const selectedElement = useMutationsStore((s) => s.selectedElement);
|
|
933
908
|
const activeVariationId = useVariationsStore((s) => s.activeVariationId);
|
|
934
909
|
const variations = useVariationsStore((s) => s.variations);
|
|
@@ -939,7 +914,7 @@ function LayerPanel() {
|
|
|
939
914
|
const mutatedSelectors = useMemo(() => {
|
|
940
915
|
return new Set(mutations.map((m) => m.selector));
|
|
941
916
|
}, [mutations]);
|
|
942
|
-
const findNodeBySelector =
|
|
917
|
+
const findNodeBySelector = useCallback((nodes, selector) => {
|
|
943
918
|
for (const n of nodes) {
|
|
944
919
|
if (n.selector === selector) return n;
|
|
945
920
|
const found = findNodeBySelector(n.children, selector);
|
|
@@ -947,7 +922,7 @@ function LayerPanel() {
|
|
|
947
922
|
}
|
|
948
923
|
return null;
|
|
949
924
|
}, []);
|
|
950
|
-
const findPathToNode =
|
|
925
|
+
const findPathToNode = useCallback((nodes, targetId, path = []) => {
|
|
951
926
|
for (const n of nodes) {
|
|
952
927
|
if (n.id === targetId) return [...path, n.id];
|
|
953
928
|
const found = findPathToNode(n.children, targetId, [...path, n.id]);
|
|
@@ -955,7 +930,7 @@ function LayerPanel() {
|
|
|
955
930
|
}
|
|
956
931
|
return null;
|
|
957
932
|
}, []);
|
|
958
|
-
const expandPathToNode =
|
|
933
|
+
const expandPathToNode = useCallback((nodeId) => {
|
|
959
934
|
const path = findPathToNode(tree, nodeId);
|
|
960
935
|
if (path) {
|
|
961
936
|
setExpandedIds((prev) => {
|
|
@@ -965,7 +940,7 @@ function LayerPanel() {
|
|
|
965
940
|
});
|
|
966
941
|
}
|
|
967
942
|
}, [tree, findPathToNode]);
|
|
968
|
-
|
|
943
|
+
useEffect(() => {
|
|
969
944
|
const handleMessage = (e) => {
|
|
970
945
|
const msg = e.data;
|
|
971
946
|
if (!msg || msg.channel !== "conversion-editor") return;
|
|
@@ -988,7 +963,7 @@ function LayerPanel() {
|
|
|
988
963
|
window.addEventListener("message", handleMessage);
|
|
989
964
|
return () => window.removeEventListener("message", handleMessage);
|
|
990
965
|
}, []);
|
|
991
|
-
|
|
966
|
+
useEffect(() => {
|
|
992
967
|
const handleBridgeReady = (e) => {
|
|
993
968
|
const msg = e.data;
|
|
994
969
|
if (!msg || msg.channel !== "conversion-editor") return;
|
|
@@ -1000,7 +975,7 @@ function LayerPanel() {
|
|
|
1000
975
|
sendToBridge({ type: "capturePageTree" });
|
|
1001
976
|
return () => window.removeEventListener("message", handleBridgeReady);
|
|
1002
977
|
}, []);
|
|
1003
|
-
|
|
978
|
+
useEffect(() => {
|
|
1004
979
|
if (reCaptureTimerRef.current) clearTimeout(reCaptureTimerRef.current);
|
|
1005
980
|
reCaptureTimerRef.current = setTimeout(() => {
|
|
1006
981
|
sendToBridge({ type: "capturePageTree" });
|
|
@@ -1009,7 +984,7 @@ function LayerPanel() {
|
|
|
1009
984
|
if (reCaptureTimerRef.current) clearTimeout(reCaptureTimerRef.current);
|
|
1010
985
|
};
|
|
1011
986
|
}, [mutations.length]);
|
|
1012
|
-
|
|
987
|
+
useEffect(() => {
|
|
1013
988
|
if (selectedElement) {
|
|
1014
989
|
const found = findNodeBySelector(tree, selectedElement.selector);
|
|
1015
990
|
if (found) {
|
|
@@ -1020,7 +995,7 @@ function LayerPanel() {
|
|
|
1020
995
|
setSelectedNodeId(null);
|
|
1021
996
|
}
|
|
1022
997
|
}, [selectedElement, tree, findNodeBySelector, expandPathToNode]);
|
|
1023
|
-
const handleToggle =
|
|
998
|
+
const handleToggle = useCallback((id) => {
|
|
1024
999
|
setExpandedIds((prev) => {
|
|
1025
1000
|
const next = new Set(prev);
|
|
1026
1001
|
if (next.has(id)) next.delete(id);
|
|
@@ -1028,22 +1003,22 @@ function LayerPanel() {
|
|
|
1028
1003
|
return next;
|
|
1029
1004
|
});
|
|
1030
1005
|
}, []);
|
|
1031
|
-
const handleSelect =
|
|
1006
|
+
const handleSelect = useCallback((node) => {
|
|
1032
1007
|
setSelectedNodeId(node.id);
|
|
1033
1008
|
sendToBridge({ type: "selectElement", selector: node.selector });
|
|
1034
1009
|
sendToBridge({ type: "scrollToElement", selector: node.selector });
|
|
1035
1010
|
}, []);
|
|
1036
|
-
const handleHover =
|
|
1011
|
+
const handleHover = useCallback((node) => {
|
|
1037
1012
|
setHoveredNodeId(node?.id || null);
|
|
1038
1013
|
if (node) {
|
|
1039
1014
|
sendToBridge({ type: "hoverElement", selector: node.selector });
|
|
1040
1015
|
}
|
|
1041
1016
|
}, []);
|
|
1042
|
-
const handleContextMenu =
|
|
1017
|
+
const handleContextMenu = useCallback((e, node) => {
|
|
1043
1018
|
e.preventDefault();
|
|
1044
1019
|
setContextMenu({ x: e.clientX, y: e.clientY, node });
|
|
1045
1020
|
}, []);
|
|
1046
|
-
const handleHideElement =
|
|
1021
|
+
const handleHideElement = useCallback((node) => {
|
|
1047
1022
|
addMutationToActive({
|
|
1048
1023
|
id: `m_${Date.now()}_hide`,
|
|
1049
1024
|
selector: node.selector,
|
|
@@ -1062,11 +1037,11 @@ function LayerPanel() {
|
|
|
1062
1037
|
}
|
|
1063
1038
|
});
|
|
1064
1039
|
}, [addMutationToActive]);
|
|
1065
|
-
const handleRemoveChanges =
|
|
1040
|
+
const handleRemoveChanges = useCallback((node) => {
|
|
1066
1041
|
removeMutationsForSelector(node.selector);
|
|
1067
1042
|
sendToBridge({ type: "clearAllMutations" });
|
|
1068
1043
|
}, [removeMutationsForSelector]);
|
|
1069
|
-
const handleCopySelector =
|
|
1044
|
+
const handleCopySelector = useCallback((node) => {
|
|
1070
1045
|
navigator.clipboard.writeText(node.selector).catch(() => {
|
|
1071
1046
|
const ta = document.createElement("textarea");
|
|
1072
1047
|
ta.value = node.selector;
|
|
@@ -1076,13 +1051,13 @@ function LayerPanel() {
|
|
|
1076
1051
|
document.body.removeChild(ta);
|
|
1077
1052
|
});
|
|
1078
1053
|
}, []);
|
|
1079
|
-
return /* @__PURE__ */
|
|
1080
|
-
/* @__PURE__ */
|
|
1081
|
-
/* @__PURE__ */
|
|
1082
|
-
/* @__PURE__ */
|
|
1083
|
-
/* @__PURE__ */
|
|
1054
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col h-full", children: [
|
|
1055
|
+
/* @__PURE__ */ jsxs("div", { className: "relative mb-2", children: [
|
|
1056
|
+
/* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", className: "absolute left-2.5 top-1/2 -translate-y-1/2", style: { color: "#999999" }, children: [
|
|
1057
|
+
/* @__PURE__ */ jsx("circle", { cx: "7", cy: "7", r: "4.5", stroke: "currentColor", strokeWidth: "1.3" }),
|
|
1058
|
+
/* @__PURE__ */ jsx("path", { d: "M10.5 10.5l3 3", stroke: "currentColor", strokeWidth: "1.3", strokeLinecap: "round" })
|
|
1084
1059
|
] }),
|
|
1085
|
-
/* @__PURE__ */
|
|
1060
|
+
/* @__PURE__ */ jsx(
|
|
1086
1061
|
"input",
|
|
1087
1062
|
{
|
|
1088
1063
|
type: "text",
|
|
@@ -1094,7 +1069,7 @@ function LayerPanel() {
|
|
|
1094
1069
|
}
|
|
1095
1070
|
)
|
|
1096
1071
|
] }),
|
|
1097
|
-
/* @__PURE__ */
|
|
1072
|
+
/* @__PURE__ */ jsx("div", { ref: treeContainerRef, className: "flex-1 overflow-y-auto", style: { scrollbarWidth: "thin" }, children: tree.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-[12px] px-1 py-4", style: { color: "#999999" }, children: "Load a page to see the layer tree" }) : tree.map((node) => /* @__PURE__ */ jsx(
|
|
1098
1073
|
TreeNodeRow,
|
|
1099
1074
|
{
|
|
1100
1075
|
node,
|
|
@@ -1110,7 +1085,7 @@ function LayerPanel() {
|
|
|
1110
1085
|
},
|
|
1111
1086
|
node.id
|
|
1112
1087
|
)) }),
|
|
1113
|
-
contextMenu && /* @__PURE__ */
|
|
1088
|
+
contextMenu && /* @__PURE__ */ jsx(
|
|
1114
1089
|
ContextMenu,
|
|
1115
1090
|
{
|
|
1116
1091
|
x: contextMenu.x,
|
|
@@ -1125,9 +1100,6 @@ function LayerPanel() {
|
|
|
1125
1100
|
)
|
|
1126
1101
|
] });
|
|
1127
1102
|
}
|
|
1128
|
-
|
|
1129
|
-
// src/components/LeftPanel.tsx
|
|
1130
|
-
import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1131
1103
|
var CATEGORY_PILLS = [
|
|
1132
1104
|
{ id: "all", label: "All" },
|
|
1133
1105
|
{ id: "social-proof", label: "Social Proof" },
|
|
@@ -1152,20 +1124,20 @@ var SOURCE_COLORS = {
|
|
|
1152
1124
|
github: { bg: "#F3F4F6", color: "#374151" }
|
|
1153
1125
|
};
|
|
1154
1126
|
function LeftPanel() {
|
|
1155
|
-
const [bottomTab, setBottomTab] =
|
|
1156
|
-
const [elementsView, setElementsView] =
|
|
1157
|
-
const [searchQuery, setSearchQuery] =
|
|
1158
|
-
const [menuOpenId, setMenuOpenId] =
|
|
1159
|
-
const [renamingId, setRenamingId] =
|
|
1160
|
-
const [renameValue, setRenameValue] =
|
|
1161
|
-
const [addMenuOpen, setAddMenuOpen] =
|
|
1162
|
-
const [elementMenuSelector, setElementMenuSelector] =
|
|
1163
|
-
const [generateModalOpen, setGenerateModalOpen] =
|
|
1164
|
-
const [generatePrompt, setGeneratePrompt] =
|
|
1165
|
-
const [generating, setGenerating] =
|
|
1166
|
-
const [generateError, setGenerateError] =
|
|
1167
|
-
const [refreshing, setRefreshing] =
|
|
1168
|
-
const seeded =
|
|
1127
|
+
const [bottomTab, setBottomTab] = useState("elements");
|
|
1128
|
+
const [elementsView, setElementsView] = useState("tree");
|
|
1129
|
+
const [searchQuery, setSearchQuery] = useState("");
|
|
1130
|
+
const [menuOpenId, setMenuOpenId] = useState(null);
|
|
1131
|
+
const [renamingId, setRenamingId] = useState(null);
|
|
1132
|
+
const [renameValue, setRenameValue] = useState("");
|
|
1133
|
+
const [addMenuOpen, setAddMenuOpen] = useState(false);
|
|
1134
|
+
const [elementMenuSelector, setElementMenuSelector] = useState(null);
|
|
1135
|
+
const [generateModalOpen, setGenerateModalOpen] = useState(false);
|
|
1136
|
+
const [generatePrompt, setGeneratePrompt] = useState("");
|
|
1137
|
+
const [generating, setGenerating] = useState(false);
|
|
1138
|
+
const [generateError, setGenerateError] = useState("");
|
|
1139
|
+
const [refreshing, setRefreshing] = useState(false);
|
|
1140
|
+
const seeded = useRef(false);
|
|
1169
1141
|
const selectedElement = useMutationsStore((s) => s.selectedElement);
|
|
1170
1142
|
const setSelectedElement = useMutationsStore((s) => s.setSelectedElement);
|
|
1171
1143
|
const variations = useVariationsStore((s) => s.variations);
|
|
@@ -1188,25 +1160,25 @@ function LeftPanel() {
|
|
|
1188
1160
|
const setDraggedSection = useSectionsStore((s) => s.setDraggedSection);
|
|
1189
1161
|
const getFilteredSections = useSectionsStore((s) => s.getFilteredSections);
|
|
1190
1162
|
const activeVariation = variations.find((v) => v.id === activeVariationId);
|
|
1191
|
-
const mutations =
|
|
1163
|
+
const mutations = useMemo(() => activeVariation?.mutations || [], [activeVariation?.mutations]);
|
|
1192
1164
|
const modifiedSelectors = Array.from(new Set(mutations.map((m) => m.selector)));
|
|
1193
1165
|
const filteredSelectors = searchQuery && bottomTab === "elements" ? modifiedSelectors.filter((s) => s.toLowerCase().includes(searchQuery.toLowerCase())) : modifiedSelectors;
|
|
1194
|
-
|
|
1166
|
+
useEffect(() => {
|
|
1195
1167
|
loadLibrary();
|
|
1196
1168
|
}, [loadLibrary]);
|
|
1197
|
-
|
|
1169
|
+
useEffect(() => {
|
|
1198
1170
|
if (bottomTab === "library" && library.sections.length === 0 && !isLoading && !seeded.current) {
|
|
1199
1171
|
seeded.current = true;
|
|
1200
1172
|
seedDefaultLibrary().catch(console.error);
|
|
1201
1173
|
}
|
|
1202
1174
|
}, [bottomTab, library.sections.length, isLoading]);
|
|
1203
|
-
|
|
1175
|
+
useEffect(() => {
|
|
1204
1176
|
if (bottomTab === "library") {
|
|
1205
1177
|
searchSections(searchQuery);
|
|
1206
1178
|
}
|
|
1207
1179
|
}, [searchQuery, bottomTab, searchSections]);
|
|
1208
1180
|
const filteredSections = bottomTab === "library" ? getFilteredSections() : [];
|
|
1209
|
-
const handleSwitchVariation =
|
|
1181
|
+
const handleSwitchVariation = useCallback((id) => {
|
|
1210
1182
|
setActiveVariation(id);
|
|
1211
1183
|
sendToBridge({ type: "clearAllMutations" });
|
|
1212
1184
|
const v = variations.find((v2) => v2.id === id);
|
|
@@ -1214,18 +1186,18 @@ function LeftPanel() {
|
|
|
1214
1186
|
sendToBridge({ type: "applyMutationBatch", mutations: v.mutations });
|
|
1215
1187
|
}
|
|
1216
1188
|
}, [setActiveVariation, variations]);
|
|
1217
|
-
const handleStartRename =
|
|
1189
|
+
const handleStartRename = useCallback((id, currentName) => {
|
|
1218
1190
|
setRenamingId(id);
|
|
1219
1191
|
setRenameValue(currentName);
|
|
1220
1192
|
setMenuOpenId(null);
|
|
1221
1193
|
}, []);
|
|
1222
|
-
const handleFinishRename =
|
|
1194
|
+
const handleFinishRename = useCallback(() => {
|
|
1223
1195
|
if (renamingId && renameValue.trim()) {
|
|
1224
1196
|
renameVariation(renamingId, renameValue.trim());
|
|
1225
1197
|
}
|
|
1226
1198
|
setRenamingId(null);
|
|
1227
1199
|
}, [renamingId, renameValue, renameVariation]);
|
|
1228
|
-
const handleSelectElement =
|
|
1200
|
+
const handleSelectElement = useCallback((selector) => {
|
|
1229
1201
|
const mutationsForEl = mutations.filter((m) => m.selector === selector);
|
|
1230
1202
|
if (mutationsForEl.length > 0) {
|
|
1231
1203
|
setSelectedElement({
|
|
@@ -1237,12 +1209,12 @@ function LeftPanel() {
|
|
|
1237
1209
|
});
|
|
1238
1210
|
}
|
|
1239
1211
|
}, [mutations, setSelectedElement]);
|
|
1240
|
-
const handleRemoveChanges =
|
|
1212
|
+
const handleRemoveChanges = useCallback((selector) => {
|
|
1241
1213
|
removeMutationsForSelector(selector);
|
|
1242
1214
|
sendToBridge({ type: "clearAllMutations" });
|
|
1243
1215
|
setElementMenuSelector(null);
|
|
1244
1216
|
}, [removeMutationsForSelector]);
|
|
1245
|
-
const handleHideElement =
|
|
1217
|
+
const handleHideElement = useCallback((selector) => {
|
|
1246
1218
|
const mutation = {
|
|
1247
1219
|
id: `m_${Date.now()}_hide`,
|
|
1248
1220
|
selector,
|
|
@@ -1271,7 +1243,7 @@ function LeftPanel() {
|
|
|
1271
1243
|
sendToBridge({ type: "applyMutation", mutation });
|
|
1272
1244
|
addMutationToActive(mutation);
|
|
1273
1245
|
}
|
|
1274
|
-
const handleGenerateSection =
|
|
1246
|
+
const handleGenerateSection = useCallback(async () => {
|
|
1275
1247
|
if (!generatePrompt.trim() || generating) return;
|
|
1276
1248
|
setGenerating(true);
|
|
1277
1249
|
setGenerateError("");
|
|
@@ -1288,7 +1260,7 @@ function LeftPanel() {
|
|
|
1288
1260
|
setGenerating(false);
|
|
1289
1261
|
}
|
|
1290
1262
|
}, [generatePrompt, generating]);
|
|
1291
|
-
const handleRefreshLibrary =
|
|
1263
|
+
const handleRefreshLibrary = useCallback(async () => {
|
|
1292
1264
|
if (refreshing) return;
|
|
1293
1265
|
setRefreshing(true);
|
|
1294
1266
|
try {
|
|
@@ -1299,13 +1271,13 @@ function LeftPanel() {
|
|
|
1299
1271
|
setRefreshing(false);
|
|
1300
1272
|
}
|
|
1301
1273
|
}, [refreshing]);
|
|
1302
|
-
const handleDragStart =
|
|
1274
|
+
const handleDragStart = useCallback((section) => {
|
|
1303
1275
|
setDraggedSection(section);
|
|
1304
1276
|
}, [setDraggedSection]);
|
|
1305
|
-
const handleDragEnd =
|
|
1277
|
+
const handleDragEnd = useCallback(() => {
|
|
1306
1278
|
setDraggedSection(null);
|
|
1307
1279
|
}, [setDraggedSection]);
|
|
1308
|
-
const handleAddToPage =
|
|
1280
|
+
const handleAddToPage = useCallback((section) => {
|
|
1309
1281
|
const mutation = {
|
|
1310
1282
|
id: `sec_${section.id}_${Date.now()}`,
|
|
1311
1283
|
selector: "main",
|
|
@@ -1321,24 +1293,24 @@ function LeftPanel() {
|
|
|
1321
1293
|
sendToBridge({ type: "applyMutation", mutation: { ...mutation, action: "insertHTML" } });
|
|
1322
1294
|
setExpandedSectionId(null);
|
|
1323
1295
|
}, [addMutationToActive, setExpandedSectionId]);
|
|
1324
|
-
return /* @__PURE__ */
|
|
1325
|
-
/* @__PURE__ */
|
|
1326
|
-
/* @__PURE__ */
|
|
1327
|
-
/* @__PURE__ */
|
|
1328
|
-
/* @__PURE__ */
|
|
1296
|
+
return /* @__PURE__ */ jsxs("div", { className: "bg-white flex flex-col h-full overflow-hidden shrink-0", style: { width: 288, borderRight: "1px solid var(--color-border)" }, children: [
|
|
1297
|
+
/* @__PURE__ */ jsxs("div", { className: "p-3", children: [
|
|
1298
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-2", style: { height: 38 }, children: [
|
|
1299
|
+
/* @__PURE__ */ jsx("span", { className: "text-[12px] font-semibold uppercase", style: { color: "#999999", letterSpacing: "0.5px" }, children: "Variations" }),
|
|
1300
|
+
/* @__PURE__ */ jsxs(
|
|
1329
1301
|
"button",
|
|
1330
1302
|
{
|
|
1331
1303
|
onClick: addVariation,
|
|
1332
1304
|
className: "h-[26px] px-2.5 rounded-md flex items-center gap-1 text-[12px] font-medium transition-colors hover:bg-[#F9FAFB]",
|
|
1333
1305
|
style: { border: "1px solid #E5E5E5", color: "#374151" },
|
|
1334
1306
|
children: [
|
|
1335
|
-
/* @__PURE__ */
|
|
1307
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm leading-none", children: "+" }),
|
|
1336
1308
|
" Add Variation"
|
|
1337
1309
|
]
|
|
1338
1310
|
}
|
|
1339
1311
|
)
|
|
1340
1312
|
] }),
|
|
1341
|
-
/* @__PURE__ */
|
|
1313
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-0", children: variations.map((v) => /* @__PURE__ */ jsxs(
|
|
1342
1314
|
"div",
|
|
1343
1315
|
{
|
|
1344
1316
|
onClick: () => handleSwitchVariation(v.id),
|
|
@@ -1354,8 +1326,8 @@ function LeftPanel() {
|
|
|
1354
1326
|
if (activeVariationId !== v.id) e.currentTarget.style.backgroundColor = "transparent";
|
|
1355
1327
|
},
|
|
1356
1328
|
children: [
|
|
1357
|
-
/* @__PURE__ */
|
|
1358
|
-
renamingId === v.id ? /* @__PURE__ */
|
|
1329
|
+
/* @__PURE__ */ jsx("div", { className: "w-[14px] h-[14px] flex items-center justify-center shrink-0", children: /* @__PURE__ */ jsx("div", { className: "w-[10.5px] h-[10.5px] rounded-[3px]", style: { backgroundColor: v.color } }) }),
|
|
1330
|
+
renamingId === v.id ? /* @__PURE__ */ jsx(
|
|
1359
1331
|
"input",
|
|
1360
1332
|
{
|
|
1361
1333
|
autoFocus: true,
|
|
@@ -1370,10 +1342,10 @@ function LeftPanel() {
|
|
|
1370
1342
|
style: { color: "#1A1A1A" },
|
|
1371
1343
|
onClick: (e) => e.stopPropagation()
|
|
1372
1344
|
}
|
|
1373
|
-
) : /* @__PURE__ */
|
|
1374
|
-
v.mutations.length > 0 && /* @__PURE__ */
|
|
1375
|
-
!v.isControl && /* @__PURE__ */
|
|
1376
|
-
/* @__PURE__ */
|
|
1345
|
+
) : /* @__PURE__ */ jsx("span", { className: `text-[14px] flex-1 truncate ${activeVariationId === v.id ? "font-medium" : "font-normal"}`, style: { color: "#1A1A1A" }, children: v.name }),
|
|
1346
|
+
v.mutations.length > 0 && /* @__PURE__ */ jsx("span", { className: "shrink-0 min-w-[18px] h-[18px] rounded-full flex items-center justify-center text-[10px] font-medium", style: { backgroundColor: "#F3F4F6", color: "#666666" }, children: v.mutations.length }),
|
|
1347
|
+
!v.isControl && /* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
1348
|
+
/* @__PURE__ */ jsx(
|
|
1377
1349
|
"button",
|
|
1378
1350
|
{
|
|
1379
1351
|
onClick: (e) => {
|
|
@@ -1382,15 +1354,15 @@ function LeftPanel() {
|
|
|
1382
1354
|
},
|
|
1383
1355
|
className: "opacity-0 group-hover:opacity-100 w-6 h-6 flex items-center justify-center rounded hover:bg-white/50 transition-all",
|
|
1384
1356
|
style: { color: "#999999" },
|
|
1385
|
-
children: /* @__PURE__ */
|
|
1386
|
-
/* @__PURE__ */
|
|
1387
|
-
/* @__PURE__ */
|
|
1388
|
-
/* @__PURE__ */
|
|
1357
|
+
children: /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "currentColor", children: [
|
|
1358
|
+
/* @__PURE__ */ jsx("circle", { cx: "8", cy: "4", r: "1.2" }),
|
|
1359
|
+
/* @__PURE__ */ jsx("circle", { cx: "8", cy: "8", r: "1.2" }),
|
|
1360
|
+
/* @__PURE__ */ jsx("circle", { cx: "8", cy: "12", r: "1.2" })
|
|
1389
1361
|
] })
|
|
1390
1362
|
}
|
|
1391
1363
|
),
|
|
1392
|
-
menuOpenId === v.id && /* @__PURE__ */
|
|
1393
|
-
/* @__PURE__ */
|
|
1364
|
+
menuOpenId === v.id && /* @__PURE__ */ jsxs("div", { className: "absolute right-0 top-full mt-1 w-36 bg-white rounded-lg shadow-lg py-1 z-50", style: { border: "1px solid #E5E5E5" }, children: [
|
|
1365
|
+
/* @__PURE__ */ jsx(
|
|
1394
1366
|
"button",
|
|
1395
1367
|
{
|
|
1396
1368
|
onClick: (e) => {
|
|
@@ -1403,7 +1375,7 @@ function LeftPanel() {
|
|
|
1403
1375
|
children: "Duplicate"
|
|
1404
1376
|
}
|
|
1405
1377
|
),
|
|
1406
|
-
/* @__PURE__ */
|
|
1378
|
+
/* @__PURE__ */ jsx(
|
|
1407
1379
|
"button",
|
|
1408
1380
|
{
|
|
1409
1381
|
onClick: (e) => {
|
|
@@ -1415,7 +1387,7 @@ function LeftPanel() {
|
|
|
1415
1387
|
children: "Rename"
|
|
1416
1388
|
}
|
|
1417
1389
|
),
|
|
1418
|
-
/* @__PURE__ */
|
|
1390
|
+
/* @__PURE__ */ jsx(
|
|
1419
1391
|
"button",
|
|
1420
1392
|
{
|
|
1421
1393
|
onClick: (e) => {
|
|
@@ -1435,9 +1407,9 @@ function LeftPanel() {
|
|
|
1435
1407
|
v.id
|
|
1436
1408
|
)) })
|
|
1437
1409
|
] }),
|
|
1438
|
-
/* @__PURE__ */
|
|
1439
|
-
/* @__PURE__ */
|
|
1440
|
-
/* @__PURE__ */
|
|
1410
|
+
/* @__PURE__ */ jsx("div", { className: "mx-3", style: { height: 1, backgroundColor: "#E5E5E5" } }),
|
|
1411
|
+
/* @__PURE__ */ jsxs("div", { className: "flex mx-3 mt-3 mb-2 rounded-lg overflow-hidden", style: { border: "1px solid #E5E5E5" }, children: [
|
|
1412
|
+
/* @__PURE__ */ jsx(
|
|
1441
1413
|
"button",
|
|
1442
1414
|
{
|
|
1443
1415
|
onClick: () => setBottomTab("elements"),
|
|
@@ -1449,7 +1421,7 @@ function LeftPanel() {
|
|
|
1449
1421
|
children: "Elements"
|
|
1450
1422
|
}
|
|
1451
1423
|
),
|
|
1452
|
-
/* @__PURE__ */
|
|
1424
|
+
/* @__PURE__ */ jsx(
|
|
1453
1425
|
"button",
|
|
1454
1426
|
{
|
|
1455
1427
|
onClick: () => setBottomTab("library"),
|
|
@@ -1462,14 +1434,14 @@ function LeftPanel() {
|
|
|
1462
1434
|
}
|
|
1463
1435
|
)
|
|
1464
1436
|
] }),
|
|
1465
|
-
/* @__PURE__ */
|
|
1437
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 flex flex-col overflow-hidden px-3 pb-3", children: bottomTab === "elements" ? (
|
|
1466
1438
|
/* ═══ ELEMENTS VIEW ═══ */
|
|
1467
|
-
/* @__PURE__ */
|
|
1468
|
-
/* @__PURE__ */
|
|
1469
|
-
/* @__PURE__ */
|
|
1470
|
-
/* @__PURE__ */
|
|
1471
|
-
/* @__PURE__ */
|
|
1472
|
-
/* @__PURE__ */
|
|
1439
|
+
/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1440
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-2", style: { height: 38 }, children: [
|
|
1441
|
+
/* @__PURE__ */ jsx("span", { className: "text-[12px] font-semibold uppercase", style: { color: "#999999", letterSpacing: "0.5px" }, children: "Elements" }),
|
|
1442
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
1443
|
+
/* @__PURE__ */ jsxs("div", { className: "flex rounded-md overflow-hidden", style: { border: "1px solid #E5E5E5" }, children: [
|
|
1444
|
+
/* @__PURE__ */ jsx(
|
|
1473
1445
|
"button",
|
|
1474
1446
|
{
|
|
1475
1447
|
onClick: () => setElementsView("tree"),
|
|
@@ -1479,15 +1451,15 @@ function LeftPanel() {
|
|
|
1479
1451
|
color: elementsView === "tree" ? "#FFFFFF" : "#999999"
|
|
1480
1452
|
},
|
|
1481
1453
|
title: "Tree view",
|
|
1482
|
-
children: /* @__PURE__ */
|
|
1483
|
-
/* @__PURE__ */
|
|
1484
|
-
/* @__PURE__ */
|
|
1485
|
-
/* @__PURE__ */
|
|
1486
|
-
/* @__PURE__ */
|
|
1454
|
+
children: /* @__PURE__ */ jsxs("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", children: [
|
|
1455
|
+
/* @__PURE__ */ jsx("rect", { x: "1.5", y: "1.5", width: "4", height: "2", rx: "0.5", stroke: "currentColor", strokeWidth: "1" }),
|
|
1456
|
+
/* @__PURE__ */ jsx("rect", { x: "5.5", y: "5.5", width: "4", height: "2", rx: "0.5", stroke: "currentColor", strokeWidth: "1" }),
|
|
1457
|
+
/* @__PURE__ */ jsx("rect", { x: "5.5", y: "9.5", width: "4", height: "2", rx: "0.5", stroke: "currentColor", strokeWidth: "1" }),
|
|
1458
|
+
/* @__PURE__ */ jsx("path", { d: "M3.5 3.5V6.5H5.5M3.5 6.5V10.5H5.5", stroke: "currentColor", strokeWidth: "1" })
|
|
1487
1459
|
] })
|
|
1488
1460
|
}
|
|
1489
1461
|
),
|
|
1490
|
-
/* @__PURE__ */
|
|
1462
|
+
/* @__PURE__ */ jsx(
|
|
1491
1463
|
"button",
|
|
1492
1464
|
{
|
|
1493
1465
|
onClick: () => setElementsView("flat"),
|
|
@@ -1497,53 +1469,53 @@ function LeftPanel() {
|
|
|
1497
1469
|
color: elementsView === "flat" ? "#FFFFFF" : "#999999"
|
|
1498
1470
|
},
|
|
1499
1471
|
title: "Flat view",
|
|
1500
|
-
children: /* @__PURE__ */
|
|
1472
|
+
children: /* @__PURE__ */ jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", children: /* @__PURE__ */ jsx("path", { d: "M3 3.5h8M3 7h8M3 10.5h8", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round" }) })
|
|
1501
1473
|
}
|
|
1502
1474
|
)
|
|
1503
1475
|
] }),
|
|
1504
|
-
/* @__PURE__ */
|
|
1505
|
-
/* @__PURE__ */
|
|
1476
|
+
/* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
1477
|
+
/* @__PURE__ */ jsxs(
|
|
1506
1478
|
"button",
|
|
1507
1479
|
{
|
|
1508
1480
|
onClick: () => setAddMenuOpen(!addMenuOpen),
|
|
1509
1481
|
className: "h-[26px] px-2.5 rounded-md flex items-center gap-1 text-[12px] font-medium transition-colors hover:bg-[#F9FAFB]",
|
|
1510
1482
|
style: { border: "1px solid #E5E5E5", color: "#374151" },
|
|
1511
1483
|
children: [
|
|
1512
|
-
/* @__PURE__ */
|
|
1484
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm leading-none", children: "+" }),
|
|
1513
1485
|
" Add"
|
|
1514
1486
|
]
|
|
1515
1487
|
}
|
|
1516
1488
|
),
|
|
1517
|
-
addMenuOpen && /* @__PURE__ */
|
|
1518
|
-
/* @__PURE__ */
|
|
1519
|
-
/* @__PURE__ */
|
|
1489
|
+
addMenuOpen && /* @__PURE__ */ jsxs("div", { className: "absolute right-0 top-full mt-1 w-44 bg-white rounded-lg shadow-lg py-1 z-50", style: { border: "1px solid #E5E5E5" }, children: [
|
|
1490
|
+
/* @__PURE__ */ jsxs("button", { onClick: () => setAddMenuOpen(false), className: "w-full text-left px-3 py-1.5 text-[12px] hover:bg-[#F9FAFB] flex items-center gap-2", style: { color: "#666666" }, children: [
|
|
1491
|
+
/* @__PURE__ */ jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", children: /* @__PURE__ */ jsx("path", { d: "M2 3l3 8L7 7l4-2L2 3z", stroke: "currentColor", strokeWidth: "1.2", strokeLinejoin: "round" }) }),
|
|
1520
1492
|
"Select from page"
|
|
1521
1493
|
] }),
|
|
1522
|
-
/* @__PURE__ */
|
|
1494
|
+
/* @__PURE__ */ jsxs("button", { onClick: () => {
|
|
1523
1495
|
handleInsertElement("text");
|
|
1524
1496
|
setAddMenuOpen(false);
|
|
1525
1497
|
}, className: "w-full text-left px-3 py-1.5 text-[12px] hover:bg-[#F9FAFB] flex items-center gap-2", style: { color: "#666666" }, children: [
|
|
1526
|
-
/* @__PURE__ */
|
|
1498
|
+
/* @__PURE__ */ jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", children: /* @__PURE__ */ jsx("path", { d: "M3 4h8M3 7h5M3 10h7", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round" }) }),
|
|
1527
1499
|
"Add text block"
|
|
1528
1500
|
] }),
|
|
1529
|
-
/* @__PURE__ */
|
|
1501
|
+
/* @__PURE__ */ jsxs("button", { onClick: () => {
|
|
1530
1502
|
handleInsertElement("image");
|
|
1531
1503
|
setAddMenuOpen(false);
|
|
1532
1504
|
}, className: "w-full text-left px-3 py-1.5 text-[12px] hover:bg-[#F9FAFB] flex items-center gap-2", style: { color: "#666666" }, children: [
|
|
1533
|
-
/* @__PURE__ */
|
|
1534
|
-
/* @__PURE__ */
|
|
1535
|
-
/* @__PURE__ */
|
|
1536
|
-
/* @__PURE__ */
|
|
1505
|
+
/* @__PURE__ */ jsxs("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", children: [
|
|
1506
|
+
/* @__PURE__ */ jsx("rect", { x: "2", y: "2", width: "10", height: "10", rx: "1", stroke: "currentColor", strokeWidth: "1.2" }),
|
|
1507
|
+
/* @__PURE__ */ jsx("circle", { cx: "5", cy: "5.5", r: "1", fill: "currentColor" }),
|
|
1508
|
+
/* @__PURE__ */ jsx("path", { d: "M2 10l3-3 2 2 2-2 3 3", stroke: "currentColor", strokeWidth: "1.2", strokeLinejoin: "round" })
|
|
1537
1509
|
] }),
|
|
1538
1510
|
"Add image"
|
|
1539
1511
|
] }),
|
|
1540
|
-
/* @__PURE__ */
|
|
1512
|
+
/* @__PURE__ */ jsxs("button", { onClick: () => {
|
|
1541
1513
|
handleInsertElement("button");
|
|
1542
1514
|
setAddMenuOpen(false);
|
|
1543
1515
|
}, className: "w-full text-left px-3 py-1.5 text-[12px] hover:bg-[#F9FAFB] flex items-center gap-2", style: { color: "#666666" }, children: [
|
|
1544
|
-
/* @__PURE__ */
|
|
1545
|
-
/* @__PURE__ */
|
|
1546
|
-
/* @__PURE__ */
|
|
1516
|
+
/* @__PURE__ */ jsxs("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", children: [
|
|
1517
|
+
/* @__PURE__ */ jsx("rect", { x: "2", y: "4", width: "10", height: "6", rx: "2", stroke: "currentColor", strokeWidth: "1.2" }),
|
|
1518
|
+
/* @__PURE__ */ jsx("path", { d: "M5 7h4", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round" })
|
|
1547
1519
|
] }),
|
|
1548
1520
|
"Add button"
|
|
1549
1521
|
] })
|
|
@@ -1553,16 +1525,16 @@ function LeftPanel() {
|
|
|
1553
1525
|
] }),
|
|
1554
1526
|
elementsView === "tree" ? (
|
|
1555
1527
|
/* ─── TREE VIEW ─── */
|
|
1556
|
-
/* @__PURE__ */
|
|
1528
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 min-h-0 overflow-hidden", children: /* @__PURE__ */ jsx(LayerPanel, {}) })
|
|
1557
1529
|
) : (
|
|
1558
1530
|
/* ─── FLAT VIEW ─── */
|
|
1559
|
-
/* @__PURE__ */
|
|
1560
|
-
/* @__PURE__ */
|
|
1561
|
-
/* @__PURE__ */
|
|
1562
|
-
/* @__PURE__ */
|
|
1563
|
-
/* @__PURE__ */
|
|
1531
|
+
/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1532
|
+
/* @__PURE__ */ jsxs("div", { className: "relative mb-2", children: [
|
|
1533
|
+
/* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", className: "absolute left-2.5 top-1/2 -translate-y-1/2", style: { color: "#999999" }, children: [
|
|
1534
|
+
/* @__PURE__ */ jsx("circle", { cx: "7", cy: "7", r: "4.5", stroke: "currentColor", strokeWidth: "1.3" }),
|
|
1535
|
+
/* @__PURE__ */ jsx("path", { d: "M10.5 10.5l3 3", stroke: "currentColor", strokeWidth: "1.3", strokeLinecap: "round" })
|
|
1564
1536
|
] }),
|
|
1565
|
-
/* @__PURE__ */
|
|
1537
|
+
/* @__PURE__ */ jsx(
|
|
1566
1538
|
"input",
|
|
1567
1539
|
{
|
|
1568
1540
|
type: "text",
|
|
@@ -1574,14 +1546,14 @@ function LeftPanel() {
|
|
|
1574
1546
|
}
|
|
1575
1547
|
)
|
|
1576
1548
|
] }),
|
|
1577
|
-
/* @__PURE__ */
|
|
1578
|
-
/* @__PURE__ */
|
|
1549
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-1 mb-2", children: /* @__PURE__ */ jsx("span", { className: "text-[12px] font-medium", style: { color: "#999999" }, children: "Modified elements" }) }),
|
|
1550
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto", children: filteredSelectors.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-[12px] leading-relaxed", style: { color: "#999999" }, children: activeVariation?.isControl ? "Control variation shows the original page without changes" : "No elements have been added or modified yet" }) : /* @__PURE__ */ jsx("div", { className: "space-y-0", children: filteredSelectors.map((selector, idx) => {
|
|
1579
1551
|
const isActive = selectedElement?.selector === selector;
|
|
1580
1552
|
const elLabel = selector.split(">").pop()?.trim() || selector;
|
|
1581
1553
|
const tag = getTagFromSelector(selector);
|
|
1582
1554
|
const mutCount = mutations.filter((m) => m.selector === selector).length;
|
|
1583
1555
|
const isSection = mutations.some((m) => m.selector === selector && m.action === "insertSection");
|
|
1584
|
-
return /* @__PURE__ */
|
|
1556
|
+
return /* @__PURE__ */ jsxs(
|
|
1585
1557
|
"div",
|
|
1586
1558
|
{
|
|
1587
1559
|
draggable: isSection,
|
|
@@ -1611,18 +1583,18 @@ function LeftPanel() {
|
|
|
1611
1583
|
style: { height: 36, backgroundColor: isActive ? "#F0F4FF" : "transparent" },
|
|
1612
1584
|
title: selector,
|
|
1613
1585
|
children: [
|
|
1614
|
-
isSection && /* @__PURE__ */
|
|
1615
|
-
/* @__PURE__ */
|
|
1616
|
-
/* @__PURE__ */
|
|
1617
|
-
/* @__PURE__ */
|
|
1618
|
-
/* @__PURE__ */
|
|
1619
|
-
/* @__PURE__ */
|
|
1620
|
-
/* @__PURE__ */
|
|
1586
|
+
isSection && /* @__PURE__ */ jsx("span", { className: "shrink-0 cursor-grab opacity-0 group-hover:opacity-100 transition-opacity", style: { color: "#999999" }, children: /* @__PURE__ */ jsxs("svg", { width: "10", height: "14", viewBox: "0 0 10 14", fill: "currentColor", children: [
|
|
1587
|
+
/* @__PURE__ */ jsx("circle", { cx: "3", cy: "2", r: "1.2" }),
|
|
1588
|
+
/* @__PURE__ */ jsx("circle", { cx: "7", cy: "2", r: "1.2" }),
|
|
1589
|
+
/* @__PURE__ */ jsx("circle", { cx: "3", cy: "7", r: "1.2" }),
|
|
1590
|
+
/* @__PURE__ */ jsx("circle", { cx: "7", cy: "7", r: "1.2" }),
|
|
1591
|
+
/* @__PURE__ */ jsx("circle", { cx: "3", cy: "12", r: "1.2" }),
|
|
1592
|
+
/* @__PURE__ */ jsx("circle", { cx: "7", cy: "12", r: "1.2" })
|
|
1621
1593
|
] }) }),
|
|
1622
|
-
/* @__PURE__ */
|
|
1623
|
-
/* @__PURE__ */
|
|
1624
|
-
mutCount > 0 && /* @__PURE__ */
|
|
1625
|
-
/* @__PURE__ */
|
|
1594
|
+
/* @__PURE__ */ jsx(ElementIcon, { tag }),
|
|
1595
|
+
/* @__PURE__ */ jsx("p", { className: "text-[12px] font-mono truncate flex-1", style: { color: "#1A1A1A" }, children: elLabel.length > 30 ? elLabel.slice(0, 30) + "..." : elLabel }),
|
|
1596
|
+
mutCount > 0 && /* @__PURE__ */ jsx("span", { className: "shrink-0 min-w-[18px] h-[18px] rounded-full flex items-center justify-center text-[10px] font-medium", style: { backgroundColor: "#F3F4F6", color: "#666666" }, children: mutCount }),
|
|
1597
|
+
/* @__PURE__ */ jsx(
|
|
1626
1598
|
"button",
|
|
1627
1599
|
{
|
|
1628
1600
|
onClick: (e) => {
|
|
@@ -1631,24 +1603,24 @@ function LeftPanel() {
|
|
|
1631
1603
|
},
|
|
1632
1604
|
className: "opacity-0 group-hover:opacity-100 w-5 h-5 flex items-center justify-center rounded hover:bg-white/50 transition-all shrink-0",
|
|
1633
1605
|
style: { color: "#999999" },
|
|
1634
|
-
children: /* @__PURE__ */
|
|
1635
|
-
/* @__PURE__ */
|
|
1636
|
-
/* @__PURE__ */
|
|
1637
|
-
/* @__PURE__ */
|
|
1606
|
+
children: /* @__PURE__ */ jsxs("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "currentColor", children: [
|
|
1607
|
+
/* @__PURE__ */ jsx("circle", { cx: "6", cy: "3", r: "1" }),
|
|
1608
|
+
/* @__PURE__ */ jsx("circle", { cx: "6", cy: "6", r: "1" }),
|
|
1609
|
+
/* @__PURE__ */ jsx("circle", { cx: "6", cy: "9", r: "1" })
|
|
1638
1610
|
] })
|
|
1639
1611
|
}
|
|
1640
1612
|
),
|
|
1641
|
-
elementMenuSelector === selector && /* @__PURE__ */
|
|
1642
|
-
/* @__PURE__ */
|
|
1613
|
+
elementMenuSelector === selector && /* @__PURE__ */ jsxs("div", { className: "absolute right-0 top-full mt-1 w-40 bg-white rounded-lg shadow-lg py-1 z-50", style: { border: "1px solid #E5E5E5" }, children: [
|
|
1614
|
+
/* @__PURE__ */ jsx("button", { onClick: (e) => {
|
|
1643
1615
|
e.stopPropagation();
|
|
1644
1616
|
handleSelectElement(selector);
|
|
1645
1617
|
setElementMenuSelector(null);
|
|
1646
1618
|
}, className: "w-full text-left px-3 py-1.5 text-[12px] hover:bg-[#F9FAFB]", style: { color: "#666666" }, children: "Select" }),
|
|
1647
|
-
/* @__PURE__ */
|
|
1619
|
+
/* @__PURE__ */ jsx("button", { onClick: (e) => {
|
|
1648
1620
|
e.stopPropagation();
|
|
1649
1621
|
handleRemoveChanges(selector);
|
|
1650
1622
|
}, className: "w-full text-left px-3 py-1.5 text-[12px] hover:bg-[#F9FAFB]", style: { color: "#666666" }, children: "Remove changes" }),
|
|
1651
|
-
/* @__PURE__ */
|
|
1623
|
+
/* @__PURE__ */ jsx("button", { onClick: (e) => {
|
|
1652
1624
|
e.stopPropagation();
|
|
1653
1625
|
handleHideElement(selector);
|
|
1654
1626
|
}, className: "w-full text-left px-3 py-1.5 text-[12px] hover:bg-[#F9FAFB]", style: { color: "#666666" }, children: "Hide element" })
|
|
@@ -1663,13 +1635,13 @@ function LeftPanel() {
|
|
|
1663
1635
|
] })
|
|
1664
1636
|
) : (
|
|
1665
1637
|
/* ═══ LIBRARY VIEW ═══ */
|
|
1666
|
-
/* @__PURE__ */
|
|
1667
|
-
/* @__PURE__ */
|
|
1668
|
-
/* @__PURE__ */
|
|
1669
|
-
/* @__PURE__ */
|
|
1670
|
-
/* @__PURE__ */
|
|
1638
|
+
/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1639
|
+
/* @__PURE__ */ jsxs("div", { className: "relative mb-2", children: [
|
|
1640
|
+
/* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", className: "absolute left-2.5 top-1/2 -translate-y-1/2", style: { color: "#999999" }, children: [
|
|
1641
|
+
/* @__PURE__ */ jsx("circle", { cx: "7", cy: "7", r: "4.5", stroke: "currentColor", strokeWidth: "1.3" }),
|
|
1642
|
+
/* @__PURE__ */ jsx("path", { d: "M10.5 10.5l3 3", stroke: "currentColor", strokeWidth: "1.3", strokeLinecap: "round" })
|
|
1671
1643
|
] }),
|
|
1672
|
-
/* @__PURE__ */
|
|
1644
|
+
/* @__PURE__ */ jsx(
|
|
1673
1645
|
"input",
|
|
1674
1646
|
{
|
|
1675
1647
|
type: "text",
|
|
@@ -1681,7 +1653,7 @@ function LeftPanel() {
|
|
|
1681
1653
|
}
|
|
1682
1654
|
)
|
|
1683
1655
|
] }),
|
|
1684
|
-
/* @__PURE__ */
|
|
1656
|
+
/* @__PURE__ */ jsx("div", { className: "flex gap-1.5 overflow-x-auto pb-2 mb-2", style: { scrollbarWidth: "none" }, children: CATEGORY_PILLS.map((cat) => /* @__PURE__ */ jsx(
|
|
1685
1657
|
"button",
|
|
1686
1658
|
{
|
|
1687
1659
|
onClick: () => filterByCategory(cat.id),
|
|
@@ -1694,23 +1666,23 @@ function LeftPanel() {
|
|
|
1694
1666
|
},
|
|
1695
1667
|
cat.id
|
|
1696
1668
|
)) }),
|
|
1697
|
-
/* @__PURE__ */
|
|
1698
|
-
/* @__PURE__ */
|
|
1699
|
-
/* @__PURE__ */
|
|
1700
|
-
/* @__PURE__ */
|
|
1701
|
-
/* @__PURE__ */
|
|
1669
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto", children: isLoading ? /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-2", children: [1, 2, 3, 4].map((i) => /* @__PURE__ */ jsxs("div", { className: "rounded-lg overflow-hidden animate-pulse", style: { border: "1px solid #E5E5E5" }, children: [
|
|
1670
|
+
/* @__PURE__ */ jsx("div", { className: "h-20", style: { backgroundColor: "#F3F4F6" } }),
|
|
1671
|
+
/* @__PURE__ */ jsxs("div", { className: "p-2", children: [
|
|
1672
|
+
/* @__PURE__ */ jsx("div", { className: "h-3 rounded", style: { backgroundColor: "#F3F4F6", width: "70%" } }),
|
|
1673
|
+
/* @__PURE__ */ jsx("div", { className: "h-2.5 rounded mt-1.5", style: { backgroundColor: "#F3F4F6", width: "50%" } })
|
|
1702
1674
|
] })
|
|
1703
|
-
] }, i)) }) : library.sections.length === 0 && !searchQuery ? /* @__PURE__ */
|
|
1704
|
-
/* @__PURE__ */
|
|
1705
|
-
/* @__PURE__ */
|
|
1706
|
-
/* @__PURE__ */
|
|
1675
|
+
] }, i)) }) : library.sections.length === 0 && !searchQuery ? /* @__PURE__ */ jsxs("div", { className: "text-center py-8", children: [
|
|
1676
|
+
/* @__PURE__ */ jsx("div", { className: "w-10 h-10 rounded-full flex items-center justify-center mx-auto mb-3", style: { backgroundColor: "#F3F4F6" }, children: /* @__PURE__ */ jsxs("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", children: [
|
|
1677
|
+
/* @__PURE__ */ jsx("path", { d: "M4 4h12v12H4V4z", stroke: "#999999", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }),
|
|
1678
|
+
/* @__PURE__ */ jsx("path", { d: "M10 7v6M7 10h6", stroke: "#999999", strokeWidth: "1.5", strokeLinecap: "round" })
|
|
1707
1679
|
] }) }),
|
|
1708
|
-
/* @__PURE__ */
|
|
1709
|
-
/* @__PURE__ */
|
|
1710
|
-
] }) : filteredSections.length === 0 ? /* @__PURE__ */
|
|
1711
|
-
/* @__PURE__ */
|
|
1712
|
-
/* @__PURE__ */
|
|
1713
|
-
/* @__PURE__ */
|
|
1680
|
+
/* @__PURE__ */ jsx("p", { className: "text-[12px] mb-1", style: { color: "#666666" }, children: "Loading section library..." }),
|
|
1681
|
+
/* @__PURE__ */ jsx("p", { className: "text-[11px]", style: { color: "#999999" }, children: "Discovering CRO sections from Shopify themes" })
|
|
1682
|
+
] }) : filteredSections.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "text-center py-6", children: [
|
|
1683
|
+
/* @__PURE__ */ jsx("p", { className: "text-[12px] mb-2", style: { color: "#666666" }, children: "No sections found." }),
|
|
1684
|
+
/* @__PURE__ */ jsx("p", { className: "text-[11px] mb-3", style: { color: "#999999" }, children: "Try a different search or generate one with AI." }),
|
|
1685
|
+
/* @__PURE__ */ jsx(
|
|
1714
1686
|
"button",
|
|
1715
1687
|
{
|
|
1716
1688
|
onClick: () => setGenerateModalOpen(true),
|
|
@@ -1719,10 +1691,10 @@ function LeftPanel() {
|
|
|
1719
1691
|
children: "Generate with AI"
|
|
1720
1692
|
}
|
|
1721
1693
|
)
|
|
1722
|
-
] }) : /* @__PURE__ */
|
|
1694
|
+
] }) : /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-2", children: filteredSections.map((section) => {
|
|
1723
1695
|
const isExpanded = expandedSectionId === section.id;
|
|
1724
1696
|
const sourceStyle = SOURCE_COLORS[section.source.type] || SOURCE_COLORS.custom;
|
|
1725
|
-
return /* @__PURE__ */
|
|
1697
|
+
return /* @__PURE__ */ jsxs(
|
|
1726
1698
|
"div",
|
|
1727
1699
|
{
|
|
1728
1700
|
className: `rounded-lg overflow-hidden cursor-pointer transition-all ${isExpanded ? "col-span-2" : ""}`,
|
|
@@ -1732,23 +1704,23 @@ function LeftPanel() {
|
|
|
1732
1704
|
onDragStart: () => handleDragStart(section),
|
|
1733
1705
|
onDragEnd: handleDragEnd,
|
|
1734
1706
|
children: [
|
|
1735
|
-
/* @__PURE__ */
|
|
1707
|
+
/* @__PURE__ */ jsx(
|
|
1736
1708
|
"div",
|
|
1737
1709
|
{
|
|
1738
1710
|
className: "relative overflow-hidden",
|
|
1739
1711
|
style: { height: isExpanded ? 100 : 72, background: "linear-gradient(135deg, #F9FAFB 0%, #EFF6FF 100%)" },
|
|
1740
|
-
children: /* @__PURE__ */
|
|
1712
|
+
children: /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center text-[10px] font-medium", style: { color: "#999999", padding: 4, textAlign: "center" }, children: section.name })
|
|
1741
1713
|
}
|
|
1742
1714
|
),
|
|
1743
|
-
/* @__PURE__ */
|
|
1744
|
-
/* @__PURE__ */
|
|
1745
|
-
/* @__PURE__ */
|
|
1746
|
-
/* @__PURE__ */
|
|
1747
|
-
/* @__PURE__ */
|
|
1715
|
+
/* @__PURE__ */ jsxs("div", { className: "p-2", children: [
|
|
1716
|
+
/* @__PURE__ */ jsx("p", { className: "text-[12px] font-medium truncate", style: { color: "#1A1A1A" }, children: section.name }),
|
|
1717
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 mt-1", children: [
|
|
1718
|
+
/* @__PURE__ */ jsx("span", { className: "text-[9px] font-medium px-1.5 py-0.5 rounded-full", style: { backgroundColor: sourceStyle.bg, color: sourceStyle.color }, children: section.source.type === "ai-generated" ? "AI" : section.source.type.charAt(0).toUpperCase() + section.source.type.slice(1) }),
|
|
1719
|
+
/* @__PURE__ */ jsx("span", { className: "text-[9px] px-1.5 py-0.5 rounded-full", style: { backgroundColor: "#F3F4F6", color: "#666666" }, children: section.category })
|
|
1748
1720
|
] }),
|
|
1749
|
-
isExpanded && /* @__PURE__ */
|
|
1750
|
-
/* @__PURE__ */
|
|
1751
|
-
/* @__PURE__ */
|
|
1721
|
+
isExpanded && /* @__PURE__ */ jsxs("div", { className: "mt-2 pt-2", style: { borderTop: "1px solid #E5E5E5" }, children: [
|
|
1722
|
+
/* @__PURE__ */ jsx("p", { className: "text-[11px] leading-relaxed mb-2", style: { color: "#666666" }, children: section.description }),
|
|
1723
|
+
/* @__PURE__ */ jsx(
|
|
1752
1724
|
"button",
|
|
1753
1725
|
{
|
|
1754
1726
|
onClick: (e) => {
|
|
@@ -1767,8 +1739,8 @@ function LeftPanel() {
|
|
|
1767
1739
|
section.id
|
|
1768
1740
|
);
|
|
1769
1741
|
}) }) }),
|
|
1770
|
-
/* @__PURE__ */
|
|
1771
|
-
/* @__PURE__ */
|
|
1742
|
+
/* @__PURE__ */ jsxs("div", { className: "pt-2 mt-2 flex gap-2", style: { borderTop: "1px solid #E5E5E5" }, children: [
|
|
1743
|
+
/* @__PURE__ */ jsx(
|
|
1772
1744
|
"button",
|
|
1773
1745
|
{
|
|
1774
1746
|
onClick: () => setGenerateModalOpen(true),
|
|
@@ -1777,7 +1749,7 @@ function LeftPanel() {
|
|
|
1777
1749
|
children: generating ? "Generating..." : "Generate Custom"
|
|
1778
1750
|
}
|
|
1779
1751
|
),
|
|
1780
|
-
/* @__PURE__ */
|
|
1752
|
+
/* @__PURE__ */ jsx(
|
|
1781
1753
|
"button",
|
|
1782
1754
|
{
|
|
1783
1755
|
onClick: handleRefreshLibrary,
|
|
@@ -1788,9 +1760,9 @@ function LeftPanel() {
|
|
|
1788
1760
|
}
|
|
1789
1761
|
)
|
|
1790
1762
|
] }),
|
|
1791
|
-
generateModalOpen && /* @__PURE__ */
|
|
1792
|
-
/* @__PURE__ */
|
|
1793
|
-
/* @__PURE__ */
|
|
1763
|
+
generateModalOpen && /* @__PURE__ */ jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/30", onClick: () => setGenerateModalOpen(false), children: /* @__PURE__ */ jsxs("div", { className: "w-[380px] bg-white rounded-lg shadow-2xl p-5", style: { border: "1px solid #E5E5E5" }, onClick: (e) => e.stopPropagation(), children: [
|
|
1764
|
+
/* @__PURE__ */ jsx("h3", { className: "text-[14px] font-semibold mb-3", style: { color: "#1A1A1A" }, children: "Generate Custom Section" }),
|
|
1765
|
+
/* @__PURE__ */ jsx(
|
|
1794
1766
|
"textarea",
|
|
1795
1767
|
{
|
|
1796
1768
|
value: generatePrompt,
|
|
@@ -1800,9 +1772,9 @@ function LeftPanel() {
|
|
|
1800
1772
|
style: { border: "1px solid #E5E5E5", color: "#1A1A1A" }
|
|
1801
1773
|
}
|
|
1802
1774
|
),
|
|
1803
|
-
generateError && /* @__PURE__ */
|
|
1804
|
-
/* @__PURE__ */
|
|
1805
|
-
/* @__PURE__ */
|
|
1775
|
+
generateError && /* @__PURE__ */ jsx("p", { className: "text-[11px] mt-2 px-2 py-1.5 rounded", style: { backgroundColor: "#FEF2F2", color: "#B91C1C" }, children: generateError }),
|
|
1776
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2 mt-3", children: [
|
|
1777
|
+
/* @__PURE__ */ jsx(
|
|
1806
1778
|
"button",
|
|
1807
1779
|
{
|
|
1808
1780
|
onClick: () => {
|
|
@@ -1814,7 +1786,7 @@ function LeftPanel() {
|
|
|
1814
1786
|
children: "Cancel"
|
|
1815
1787
|
}
|
|
1816
1788
|
),
|
|
1817
|
-
/* @__PURE__ */
|
|
1789
|
+
/* @__PURE__ */ jsx(
|
|
1818
1790
|
"button",
|
|
1819
1791
|
{
|
|
1820
1792
|
onClick: handleGenerateSection,
|
|
@@ -1839,44 +1811,37 @@ function ElementIcon({ tag }) {
|
|
|
1839
1811
|
const iconColor = "#666666";
|
|
1840
1812
|
const bgColor = "#F3F4F6";
|
|
1841
1813
|
if (tag === "img" || tag === "picture" || tag === "svg") {
|
|
1842
|
-
return /* @__PURE__ */
|
|
1843
|
-
/* @__PURE__ */
|
|
1844
|
-
/* @__PURE__ */
|
|
1845
|
-
/* @__PURE__ */
|
|
1814
|
+
return /* @__PURE__ */ jsx("span", { className: "shrink-0 w-5 h-5 rounded flex items-center justify-center", style: { backgroundColor: bgColor }, children: /* @__PURE__ */ jsxs("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", children: [
|
|
1815
|
+
/* @__PURE__ */ jsx("rect", { x: "1", y: "1", width: "10", height: "10", rx: "1", stroke: iconColor, strokeWidth: "1.2" }),
|
|
1816
|
+
/* @__PURE__ */ jsx("circle", { cx: "4", cy: "4.5", r: "1", fill: iconColor }),
|
|
1817
|
+
/* @__PURE__ */ jsx("path", { d: "M1 9l3-3 2 2 1.5-1.5L11 9", stroke: iconColor, strokeWidth: "1.2", strokeLinejoin: "round" })
|
|
1846
1818
|
] }) });
|
|
1847
1819
|
}
|
|
1848
1820
|
if (tag === "a") {
|
|
1849
|
-
return /* @__PURE__ */
|
|
1821
|
+
return /* @__PURE__ */ jsx("span", { className: "shrink-0 w-5 h-5 rounded flex items-center justify-center", style: { backgroundColor: bgColor }, children: /* @__PURE__ */ jsx("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", children: /* @__PURE__ */ jsx("path", { d: "M5 7l2-2M4 8.5L2.5 7a2 2 0 010-2.83l1-1a2 2 0 012.83 0M8 3.5l1.5 1.5a2 2 0 010 2.83l-1 1a2 2 0 01-2.83 0", stroke: iconColor, strokeWidth: "1.2", strokeLinecap: "round" }) }) });
|
|
1850
1822
|
}
|
|
1851
1823
|
if (["h1", "h2", "h3", "h4", "h5", "h6", "p", "span", "label"].includes(tag)) {
|
|
1852
|
-
return /* @__PURE__ */
|
|
1824
|
+
return /* @__PURE__ */ jsx("span", { className: "shrink-0 w-5 h-5 rounded flex items-center justify-center", style: { backgroundColor: bgColor }, children: /* @__PURE__ */ jsx("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", children: /* @__PURE__ */ jsx("path", { d: "M2 3h8M2 6h5M2 9h7", stroke: iconColor, strokeWidth: "1.2", strokeLinecap: "round" }) }) });
|
|
1853
1825
|
}
|
|
1854
1826
|
if (tag === "button" || tag === "input") {
|
|
1855
|
-
return /* @__PURE__ */
|
|
1856
|
-
/* @__PURE__ */
|
|
1857
|
-
/* @__PURE__ */
|
|
1827
|
+
return /* @__PURE__ */ jsx("span", { className: "shrink-0 w-5 h-5 rounded flex items-center justify-center", style: { backgroundColor: bgColor }, children: /* @__PURE__ */ jsxs("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", children: [
|
|
1828
|
+
/* @__PURE__ */ jsx("rect", { x: "1", y: "3", width: "10", height: "6", rx: "2", stroke: iconColor, strokeWidth: "1.2" }),
|
|
1829
|
+
/* @__PURE__ */ jsx("path", { d: "M4 6h4", stroke: iconColor, strokeWidth: "1.2", strokeLinecap: "round" })
|
|
1858
1830
|
] }) });
|
|
1859
1831
|
}
|
|
1860
|
-
return /* @__PURE__ */
|
|
1832
|
+
return /* @__PURE__ */ jsx("span", { className: "shrink-0 w-5 h-5 rounded flex items-center justify-center", style: { backgroundColor: bgColor }, children: /* @__PURE__ */ jsx("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", children: /* @__PURE__ */ jsx("rect", { x: "1.5", y: "1.5", width: "9", height: "9", rx: "1.5", stroke: iconColor, strokeWidth: "1.2" }) }) });
|
|
1861
1833
|
}
|
|
1862
|
-
|
|
1863
|
-
// src/components/CanvasArea.tsx
|
|
1864
|
-
import { useState as useState6, useCallback as useCallback6, useEffect as useEffect6, useRef as useRef6 } from "react";
|
|
1865
|
-
|
|
1866
|
-
// src/components/IframeCanvas.tsx
|
|
1867
|
-
import { useCallback as useCallback4, useEffect as useEffect4, useRef as useRef4, useState as useState4 } from "react";
|
|
1868
|
-
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1869
1834
|
var CHANNEL = "conversion-editor";
|
|
1870
1835
|
function IframeCanvas({ url, password, proxyBaseUrl = "", onBridgeReady, onPong }) {
|
|
1871
|
-
const iframeElRef =
|
|
1836
|
+
const iframeElRef = useRef(null);
|
|
1872
1837
|
const setSelectedElement = useMutationsStore((s) => s.setSelectedElement);
|
|
1873
1838
|
const addMutationToActive = useVariationsStore((s) => s.addMutationToActive);
|
|
1874
|
-
const [loading, setLoading] =
|
|
1875
|
-
|
|
1839
|
+
const [loading, setLoading] = useState(false);
|
|
1840
|
+
useEffect(() => {
|
|
1876
1841
|
setIframeRef(iframeElRef.current);
|
|
1877
1842
|
return () => setIframeRef(null);
|
|
1878
1843
|
});
|
|
1879
|
-
const handleMessage =
|
|
1844
|
+
const handleMessage = useCallback(
|
|
1880
1845
|
(e) => {
|
|
1881
1846
|
const msg = e.data;
|
|
1882
1847
|
if (!msg || msg.channel !== CHANNEL) return;
|
|
@@ -1906,11 +1871,11 @@ function IframeCanvas({ url, password, proxyBaseUrl = "", onBridgeReady, onPong
|
|
|
1906
1871
|
},
|
|
1907
1872
|
[onBridgeReady, onPong, setSelectedElement, addMutationToActive]
|
|
1908
1873
|
);
|
|
1909
|
-
|
|
1874
|
+
useEffect(() => {
|
|
1910
1875
|
window.addEventListener("message", handleMessage);
|
|
1911
1876
|
return () => window.removeEventListener("message", handleMessage);
|
|
1912
1877
|
}, [handleMessage]);
|
|
1913
|
-
|
|
1878
|
+
useEffect(() => {
|
|
1914
1879
|
if (url) setLoading(true);
|
|
1915
1880
|
}, [url]);
|
|
1916
1881
|
let resolvedUrl;
|
|
@@ -1922,21 +1887,21 @@ function IframeCanvas({ url, password, proxyBaseUrl = "", onBridgeReady, onPong
|
|
|
1922
1887
|
resolvedUrl = url;
|
|
1923
1888
|
}
|
|
1924
1889
|
if (!url) {
|
|
1925
|
-
return /* @__PURE__ */
|
|
1926
|
-
/* @__PURE__ */
|
|
1927
|
-
/* @__PURE__ */
|
|
1890
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center h-full text-center px-8", children: [
|
|
1891
|
+
/* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold text-gray-400 mb-2", children: "Canvas" }),
|
|
1892
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-gray-400", children: 'Load a page from the Page dropdown in the top bar, or type "test" for the local test page' })
|
|
1928
1893
|
] });
|
|
1929
1894
|
}
|
|
1930
|
-
return /* @__PURE__ */
|
|
1931
|
-
loading && /* @__PURE__ */
|
|
1932
|
-
/* @__PURE__ */
|
|
1933
|
-
/* @__PURE__ */
|
|
1934
|
-
/* @__PURE__ */
|
|
1935
|
-
/* @__PURE__ */
|
|
1936
|
-
/* @__PURE__ */
|
|
1937
|
-
/* @__PURE__ */
|
|
1895
|
+
return /* @__PURE__ */ jsxs("div", { className: "relative w-full h-full", children: [
|
|
1896
|
+
loading && /* @__PURE__ */ jsxs("div", { className: "absolute inset-0 z-10 flex flex-col gap-4 p-8", style: { backgroundColor: "#F9FAFB" }, children: [
|
|
1897
|
+
/* @__PURE__ */ jsx("div", { className: "h-8 w-3/4 rounded-md animate-pulse", style: { backgroundColor: "#E5E5E5" } }),
|
|
1898
|
+
/* @__PURE__ */ jsx("div", { className: "h-4 w-1/2 rounded-md animate-pulse", style: { backgroundColor: "#E5E5E5" } }),
|
|
1899
|
+
/* @__PURE__ */ jsx("div", { className: "h-48 w-full rounded-md animate-pulse", style: { backgroundColor: "#E5E5E5" } }),
|
|
1900
|
+
/* @__PURE__ */ jsx("div", { className: "h-4 w-2/3 rounded-md animate-pulse", style: { backgroundColor: "#E5E5E5" } }),
|
|
1901
|
+
/* @__PURE__ */ jsx("div", { className: "h-4 w-1/3 rounded-md animate-pulse", style: { backgroundColor: "#E5E5E5" } }),
|
|
1902
|
+
/* @__PURE__ */ jsx("div", { className: "h-32 w-full rounded-md animate-pulse", style: { backgroundColor: "#E5E5E5" } })
|
|
1938
1903
|
] }),
|
|
1939
|
-
/* @__PURE__ */
|
|
1904
|
+
/* @__PURE__ */ jsx(
|
|
1940
1905
|
"iframe",
|
|
1941
1906
|
{
|
|
1942
1907
|
ref: iframeElRef,
|
|
@@ -1948,20 +1913,16 @@ function IframeCanvas({ url, password, proxyBaseUrl = "", onBridgeReady, onPong
|
|
|
1948
1913
|
)
|
|
1949
1914
|
] });
|
|
1950
1915
|
}
|
|
1951
|
-
|
|
1952
|
-
// src/components/ElementOverlayToolbar.tsx
|
|
1953
|
-
import { useEffect as useEffect5, useState as useState5, useCallback as useCallback5, useRef as useRef5 } from "react";
|
|
1954
|
-
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1955
1916
|
function ElementOverlayToolbar() {
|
|
1956
1917
|
const selectedElement = useMutationsStore((s) => s.selectedElement);
|
|
1957
1918
|
const setSelectedElement = useMutationsStore((s) => s.setSelectedElement);
|
|
1958
1919
|
const removeMutationsForSelector = useVariationsStore((s) => s.removeMutationsForSelector);
|
|
1959
1920
|
const addMutationToActive = useVariationsStore((s) => s.addMutationToActive);
|
|
1960
|
-
const [position, setPosition] =
|
|
1961
|
-
const [moveActive, setMoveActive] =
|
|
1962
|
-
const [resizeActive, setResizeActive] =
|
|
1963
|
-
const toolbarRef =
|
|
1964
|
-
|
|
1921
|
+
const [position, setPosition] = useState(null);
|
|
1922
|
+
const [moveActive, setMoveActive] = useState(false);
|
|
1923
|
+
const [resizeActive, setResizeActive] = useState(false);
|
|
1924
|
+
const toolbarRef = useRef(null);
|
|
1925
|
+
useEffect(() => {
|
|
1965
1926
|
if (!selectedElement) {
|
|
1966
1927
|
setPosition(null);
|
|
1967
1928
|
return;
|
|
@@ -1987,7 +1948,7 @@ function ElementOverlayToolbar() {
|
|
|
1987
1948
|
window.addEventListener("resize", updatePosition);
|
|
1988
1949
|
return () => window.removeEventListener("resize", updatePosition);
|
|
1989
1950
|
}, [selectedElement]);
|
|
1990
|
-
|
|
1951
|
+
useEffect(() => {
|
|
1991
1952
|
const onKey = (e) => {
|
|
1992
1953
|
if (e.key === "Escape") {
|
|
1993
1954
|
setSelectedElement(null);
|
|
@@ -1996,7 +1957,7 @@ function ElementOverlayToolbar() {
|
|
|
1996
1957
|
window.addEventListener("keydown", onKey);
|
|
1997
1958
|
return () => window.removeEventListener("keydown", onKey);
|
|
1998
1959
|
}, [setSelectedElement]);
|
|
1999
|
-
|
|
1960
|
+
useEffect(() => {
|
|
2000
1961
|
const onMsg = (e) => {
|
|
2001
1962
|
if (!e.data || e.data.channel !== "conversion-editor") return;
|
|
2002
1963
|
const payload = e.data.payload;
|
|
@@ -2053,11 +2014,11 @@ function ElementOverlayToolbar() {
|
|
|
2053
2014
|
window.addEventListener("message", onMsg);
|
|
2054
2015
|
return () => window.removeEventListener("message", onMsg);
|
|
2055
2016
|
}, [selectedElement, addMutationToActive, removeMutationsForSelector]);
|
|
2056
|
-
|
|
2017
|
+
useEffect(() => {
|
|
2057
2018
|
setMoveActive(false);
|
|
2058
2019
|
setResizeActive(false);
|
|
2059
2020
|
}, [selectedElement?.selector]);
|
|
2060
|
-
const handleMove =
|
|
2021
|
+
const handleMove = useCallback(() => {
|
|
2061
2022
|
if (!selectedElement) return;
|
|
2062
2023
|
if (moveActive) {
|
|
2063
2024
|
sendToBridge({ type: "exitMoveMode" });
|
|
@@ -2071,7 +2032,7 @@ function ElementOverlayToolbar() {
|
|
|
2071
2032
|
setMoveActive(true);
|
|
2072
2033
|
}
|
|
2073
2034
|
}, [selectedElement, moveActive, resizeActive]);
|
|
2074
|
-
const handleResize =
|
|
2035
|
+
const handleResize = useCallback(() => {
|
|
2075
2036
|
if (!selectedElement) return;
|
|
2076
2037
|
if (resizeActive) {
|
|
2077
2038
|
sendToBridge({ type: "exitResizeMode" });
|
|
@@ -2085,15 +2046,15 @@ function ElementOverlayToolbar() {
|
|
|
2085
2046
|
setResizeActive(true);
|
|
2086
2047
|
}
|
|
2087
2048
|
}, [selectedElement, resizeActive, moveActive]);
|
|
2088
|
-
const handleResetPosition =
|
|
2049
|
+
const handleResetPosition = useCallback(() => {
|
|
2089
2050
|
if (!selectedElement) return;
|
|
2090
2051
|
sendToBridge({ type: "resetMovePosition" });
|
|
2091
2052
|
}, [selectedElement]);
|
|
2092
|
-
const handleDuplicate =
|
|
2053
|
+
const handleDuplicate = useCallback(() => {
|
|
2093
2054
|
if (!selectedElement) return;
|
|
2094
2055
|
sendToBridge({ type: "duplicateElement", selector: selectedElement.selector });
|
|
2095
2056
|
}, [selectedElement]);
|
|
2096
|
-
const handleHide =
|
|
2057
|
+
const handleHide = useCallback(() => {
|
|
2097
2058
|
if (!selectedElement) return;
|
|
2098
2059
|
const mutation = {
|
|
2099
2060
|
id: `m_${Date.now()}_hide`,
|
|
@@ -2105,16 +2066,16 @@ function ElementOverlayToolbar() {
|
|
|
2105
2066
|
sendToBridge({ type: "applyMutation", mutation });
|
|
2106
2067
|
addMutationToActive(mutation);
|
|
2107
2068
|
}, [selectedElement, addMutationToActive]);
|
|
2108
|
-
const handleDeleteChanges =
|
|
2069
|
+
const handleDeleteChanges = useCallback(() => {
|
|
2109
2070
|
if (!selectedElement) return;
|
|
2110
2071
|
removeMutationsForSelector(selectedElement.selector);
|
|
2111
2072
|
sendToBridge({ type: "clearAllMutations" });
|
|
2112
2073
|
}, [selectedElement, removeMutationsForSelector]);
|
|
2113
|
-
const handleDeselect =
|
|
2074
|
+
const handleDeselect = useCallback(() => {
|
|
2114
2075
|
setSelectedElement(null);
|
|
2115
2076
|
}, [setSelectedElement]);
|
|
2116
2077
|
if (!selectedElement || !position) return null;
|
|
2117
|
-
return /* @__PURE__ */
|
|
2078
|
+
return /* @__PURE__ */ jsxs(
|
|
2118
2079
|
"div",
|
|
2119
2080
|
{
|
|
2120
2081
|
ref: toolbarRef,
|
|
@@ -2127,28 +2088,28 @@ function ElementOverlayToolbar() {
|
|
|
2127
2088
|
boxShadow: "0 2px 12px rgba(0,0,0,0.12), 0 0 0 1px rgba(0,0,0,0.04)"
|
|
2128
2089
|
},
|
|
2129
2090
|
children: [
|
|
2130
|
-
/* @__PURE__ */
|
|
2131
|
-
/* @__PURE__ */
|
|
2132
|
-
/* @__PURE__ */
|
|
2133
|
-
/* @__PURE__ */
|
|
2091
|
+
/* @__PURE__ */ jsx(OverlayButton, { title: "Move", onClick: handleMove, active: moveActive, children: /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: /* @__PURE__ */ jsx("path", { d: "M8 2v12M2 8h12M8 2l-2 2M8 2l2 2M8 14l-2-2M8 14l2-2M2 8l2-2M2 8l2 2M14 8l-2-2M14 8l-2 2", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round" }) }) }),
|
|
2092
|
+
/* @__PURE__ */ jsx(OverlayButton, { title: "Resize", onClick: handleResize, active: resizeActive, children: /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: [
|
|
2093
|
+
/* @__PURE__ */ jsx("path", { d: "M14 2L6 10M14 2v5M14 2H9", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round", strokeLinejoin: "round" }),
|
|
2094
|
+
/* @__PURE__ */ jsx("path", { d: "M2 14l8-8M2 14V9M2 14h5", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round", strokeLinejoin: "round" })
|
|
2134
2095
|
] }) }),
|
|
2135
|
-
/* @__PURE__ */
|
|
2136
|
-
/* @__PURE__ */
|
|
2137
|
-
/* @__PURE__ */
|
|
2096
|
+
/* @__PURE__ */ jsx(OverlayButton, { title: "Reset position", onClick: handleResetPosition, children: /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: [
|
|
2097
|
+
/* @__PURE__ */ jsx("path", { d: "M2 8a6 6 0 1111.5-2.5", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round" }),
|
|
2098
|
+
/* @__PURE__ */ jsx("path", { d: "M14 3v3h-3", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round", strokeLinejoin: "round" })
|
|
2138
2099
|
] }) }),
|
|
2139
|
-
/* @__PURE__ */
|
|
2140
|
-
/* @__PURE__ */
|
|
2141
|
-
/* @__PURE__ */
|
|
2142
|
-
/* @__PURE__ */
|
|
2100
|
+
/* @__PURE__ */ jsx("div", { className: "w-px h-5 mx-0.5", style: { backgroundColor: "#E5E5E5" } }),
|
|
2101
|
+
/* @__PURE__ */ jsx(OverlayButton, { title: "Duplicate", onClick: handleDuplicate, children: /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: [
|
|
2102
|
+
/* @__PURE__ */ jsx("rect", { x: "5", y: "5", width: "8", height: "8", rx: "1", stroke: "currentColor", strokeWidth: "1.2" }),
|
|
2103
|
+
/* @__PURE__ */ jsx("path", { d: "M11 3H4a1 1 0 00-1 1v7", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round" })
|
|
2143
2104
|
] }) }),
|
|
2144
|
-
/* @__PURE__ */
|
|
2145
|
-
/* @__PURE__ */
|
|
2146
|
-
/* @__PURE__ */
|
|
2147
|
-
/* @__PURE__ */
|
|
2105
|
+
/* @__PURE__ */ jsx(OverlayButton, { title: "Hide", onClick: handleHide, children: /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: [
|
|
2106
|
+
/* @__PURE__ */ jsx("path", { d: "M2 8s2.5-4 6-4 6 4 6 4-2.5 4-6 4-6-4-6-4z", stroke: "currentColor", strokeWidth: "1.2" }),
|
|
2107
|
+
/* @__PURE__ */ jsx("circle", { cx: "8", cy: "8", r: "2", stroke: "currentColor", strokeWidth: "1.2" }),
|
|
2108
|
+
/* @__PURE__ */ jsx("path", { d: "M3 13L13 3", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round" })
|
|
2148
2109
|
] }) }),
|
|
2149
|
-
/* @__PURE__ */
|
|
2150
|
-
/* @__PURE__ */
|
|
2151
|
-
/* @__PURE__ */
|
|
2110
|
+
/* @__PURE__ */ jsx(OverlayButton, { title: "Delete changes", onClick: handleDeleteChanges, children: /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: /* @__PURE__ */ jsx("path", { d: "M4 5h8M6 5V4a1 1 0 011-1h2a1 1 0 011 1v1M5 5v7a1 1 0 001 1h4a1 1 0 001-1V5", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round", strokeLinejoin: "round" }) }) }),
|
|
2111
|
+
/* @__PURE__ */ jsx("div", { className: "w-px h-5 mx-0.5", style: { backgroundColor: "#E5E5E5" } }),
|
|
2112
|
+
/* @__PURE__ */ jsx(OverlayButton, { title: "Deselect", onClick: handleDeselect, children: /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: /* @__PURE__ */ jsx("path", { d: "M4 4l8 8M12 4l-8 8", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }) }) })
|
|
2152
2113
|
]
|
|
2153
2114
|
}
|
|
2154
2115
|
);
|
|
@@ -2160,7 +2121,7 @@ function OverlayButton({
|
|
|
2160
2121
|
active,
|
|
2161
2122
|
children
|
|
2162
2123
|
}) {
|
|
2163
|
-
return /* @__PURE__ */
|
|
2124
|
+
return /* @__PURE__ */ jsx(
|
|
2164
2125
|
"button",
|
|
2165
2126
|
{
|
|
2166
2127
|
onClick,
|
|
@@ -2172,9 +2133,6 @@ function OverlayButton({
|
|
|
2172
2133
|
}
|
|
2173
2134
|
);
|
|
2174
2135
|
}
|
|
2175
|
-
|
|
2176
|
-
// src/components/CanvasArea.tsx
|
|
2177
|
-
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
2178
2136
|
var VIEWPORT_WIDTHS = {
|
|
2179
2137
|
desktop: "100%",
|
|
2180
2138
|
tablet: "768px",
|
|
@@ -2188,12 +2146,12 @@ var VIEWPORT_LABELS = {
|
|
|
2188
2146
|
function CanvasArea({ url, password, viewport, proxyBaseUrl, onBridgeReady, onPong }) {
|
|
2189
2147
|
const iframeWidth = VIEWPORT_WIDTHS[viewport];
|
|
2190
2148
|
const isConstrained = viewport !== "desktop";
|
|
2191
|
-
const [dragOver, setDragOver] =
|
|
2192
|
-
const dropTargetRef =
|
|
2149
|
+
const [dragOver, setDragOver] = useState(false);
|
|
2150
|
+
const dropTargetRef = useRef(null);
|
|
2193
2151
|
const draggedSection = useSectionsStore((s) => s.draggedSection);
|
|
2194
2152
|
const setDraggedSection = useSectionsStore((s) => s.setDraggedSection);
|
|
2195
2153
|
const addMutationToActive = useVariationsStore((s) => s.addMutationToActive);
|
|
2196
|
-
|
|
2154
|
+
useEffect(() => {
|
|
2197
2155
|
function onMessage(e) {
|
|
2198
2156
|
const msg = e.data;
|
|
2199
2157
|
if (!msg || msg.channel !== "conversion-editor") return;
|
|
@@ -2209,7 +2167,7 @@ function CanvasArea({ url, password, viewport, proxyBaseUrl, onBridgeReady, onPo
|
|
|
2209
2167
|
window.addEventListener("message", onMessage);
|
|
2210
2168
|
return () => window.removeEventListener("message", onMessage);
|
|
2211
2169
|
}, []);
|
|
2212
|
-
|
|
2170
|
+
useEffect(() => {
|
|
2213
2171
|
if (draggedSection) {
|
|
2214
2172
|
sendToBridge({ type: "enterDragMode" });
|
|
2215
2173
|
} else {
|
|
@@ -2217,15 +2175,15 @@ function CanvasArea({ url, password, viewport, proxyBaseUrl, onBridgeReady, onPo
|
|
|
2217
2175
|
dropTargetRef.current = null;
|
|
2218
2176
|
}
|
|
2219
2177
|
}, [draggedSection]);
|
|
2220
|
-
const handleDragOver =
|
|
2178
|
+
const handleDragOver = useCallback((e) => {
|
|
2221
2179
|
e.preventDefault();
|
|
2222
2180
|
e.dataTransfer.dropEffect = "copy";
|
|
2223
2181
|
setDragOver(true);
|
|
2224
2182
|
}, []);
|
|
2225
|
-
const handleDragLeave =
|
|
2183
|
+
const handleDragLeave = useCallback(() => {
|
|
2226
2184
|
setDragOver(false);
|
|
2227
2185
|
}, []);
|
|
2228
|
-
const handleDrop =
|
|
2186
|
+
const handleDrop = useCallback((e) => {
|
|
2229
2187
|
e.preventDefault();
|
|
2230
2188
|
setDragOver(false);
|
|
2231
2189
|
if (!draggedSection) return;
|
|
@@ -2270,7 +2228,7 @@ function CanvasArea({ url, password, viewport, proxyBaseUrl, onBridgeReady, onPo
|
|
|
2270
2228
|
}
|
|
2271
2229
|
setDraggedSection(null);
|
|
2272
2230
|
}, [draggedSection, addMutationToActive, setDraggedSection]);
|
|
2273
|
-
return /* @__PURE__ */
|
|
2231
|
+
return /* @__PURE__ */ jsxs(
|
|
2274
2232
|
"div",
|
|
2275
2233
|
{
|
|
2276
2234
|
className: "flex-1 relative flex flex-col items-center overflow-hidden",
|
|
@@ -2279,8 +2237,8 @@ function CanvasArea({ url, password, viewport, proxyBaseUrl, onBridgeReady, onPo
|
|
|
2279
2237
|
onDragLeave: handleDragLeave,
|
|
2280
2238
|
onDrop: handleDrop,
|
|
2281
2239
|
children: [
|
|
2282
|
-
isConstrained && /* @__PURE__ */
|
|
2283
|
-
/* @__PURE__ */
|
|
2240
|
+
isConstrained && /* @__PURE__ */ jsx("div", { className: "pt-2 pb-1", children: /* @__PURE__ */ jsx("span", { className: "text-[11px] font-medium", style: { color: "#999999" }, children: VIEWPORT_LABELS[viewport] }) }),
|
|
2241
|
+
/* @__PURE__ */ jsx("div", { className: `flex-1 w-full flex justify-center ${isConstrained ? "pb-4" : ""}`, children: /* @__PURE__ */ jsx(
|
|
2284
2242
|
"div",
|
|
2285
2243
|
{
|
|
2286
2244
|
className: `h-full transition-all duration-300 ${isConstrained ? "rounded-lg overflow-hidden bg-white" : ""}`,
|
|
@@ -2289,32 +2247,22 @@ function CanvasArea({ url, password, viewport, proxyBaseUrl, onBridgeReady, onPo
|
|
|
2289
2247
|
maxWidth: "100%",
|
|
2290
2248
|
boxShadow: isConstrained ? "0 4px 24px rgba(0,0,0,0.1)" : "none"
|
|
2291
2249
|
},
|
|
2292
|
-
children: /* @__PURE__ */
|
|
2250
|
+
children: /* @__PURE__ */ jsx(IframeCanvas, { url, password, proxyBaseUrl, onBridgeReady, onPong })
|
|
2293
2251
|
}
|
|
2294
2252
|
) }),
|
|
2295
|
-
dragOver && draggedSection && /* @__PURE__ */
|
|
2253
|
+
dragOver && draggedSection && /* @__PURE__ */ jsx(
|
|
2296
2254
|
"div",
|
|
2297
2255
|
{
|
|
2298
2256
|
className: "absolute inset-0 z-20 flex items-center justify-center pointer-events-none",
|
|
2299
2257
|
style: { backgroundColor: "rgba(59,130,246,0.06)", border: "2px dashed #3B82F6", borderRadius: 8 },
|
|
2300
|
-
children: /* @__PURE__ */
|
|
2258
|
+
children: /* @__PURE__ */ jsx("div", { className: "px-4 py-2 rounded-lg text-[13px] font-medium", style: { backgroundColor: "rgba(59,130,246,0.9)", color: "#FFFFFF" }, children: "Drop section here" })
|
|
2301
2259
|
}
|
|
2302
2260
|
),
|
|
2303
|
-
/* @__PURE__ */
|
|
2261
|
+
/* @__PURE__ */ jsx(ElementOverlayToolbar, {})
|
|
2304
2262
|
]
|
|
2305
2263
|
}
|
|
2306
2264
|
);
|
|
2307
2265
|
}
|
|
2308
|
-
|
|
2309
|
-
// src/components/RightPanel.tsx
|
|
2310
|
-
import { useState as useState10, useRef as useRef8, useEffect as useEffect9, useCallback as useCallback11 } from "react";
|
|
2311
|
-
|
|
2312
|
-
// src/components/EditPanel.tsx
|
|
2313
|
-
import { useState as useState7, useCallback as useCallback8 } from "react";
|
|
2314
|
-
import { HexColorPicker } from "react-colorful";
|
|
2315
|
-
|
|
2316
|
-
// src/lib/use-bridge-messenger.ts
|
|
2317
|
-
import { useCallback as useCallback7 } from "react";
|
|
2318
2266
|
var counter = 0;
|
|
2319
2267
|
function generateId() {
|
|
2320
2268
|
return `m_${Date.now()}_${++counter}`;
|
|
@@ -2324,7 +2272,7 @@ function useBridgeMessenger() {
|
|
|
2324
2272
|
const selectedElement = useMutationsStore((s) => s.selectedElement);
|
|
2325
2273
|
const updateSelectedText = useMutationsStore((s) => s.updateSelectedText);
|
|
2326
2274
|
const updateSelectedStyle = useMutationsStore((s) => s.updateSelectedStyle);
|
|
2327
|
-
const sendMutation =
|
|
2275
|
+
const sendMutation = useCallback(
|
|
2328
2276
|
(opts) => {
|
|
2329
2277
|
if (!selectedElement) return;
|
|
2330
2278
|
const mutation = {
|
|
@@ -2353,9 +2301,6 @@ function useBridgeMessenger() {
|
|
|
2353
2301
|
);
|
|
2354
2302
|
return { sendMutation };
|
|
2355
2303
|
}
|
|
2356
|
-
|
|
2357
|
-
// src/components/EditPanel.tsx
|
|
2358
|
-
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
2359
2304
|
function rgbToHex(rgb) {
|
|
2360
2305
|
const match = rgb.match(/\d+/g);
|
|
2361
2306
|
if (!match || match.length < 3) return "#000000";
|
|
@@ -2365,50 +2310,50 @@ function parsePxValue(val) {
|
|
|
2365
2310
|
return val.replace(/px/g, "").trim().split(" ")[0] || "0";
|
|
2366
2311
|
}
|
|
2367
2312
|
function Section({ title, defaultOpen = false, children }) {
|
|
2368
|
-
return /* @__PURE__ */
|
|
2369
|
-
/* @__PURE__ */
|
|
2313
|
+
return /* @__PURE__ */ jsxs("details", { open: defaultOpen, className: "group", style: { borderBottom: "1px solid #E5E5E5" }, children: [
|
|
2314
|
+
/* @__PURE__ */ jsxs(
|
|
2370
2315
|
"summary",
|
|
2371
2316
|
{
|
|
2372
2317
|
className: "flex items-center justify-between cursor-pointer select-none hover:bg-[#F9FAFB] transition-colors",
|
|
2373
2318
|
style: { padding: "10px 12px", fontSize: 13, fontWeight: 500, color: "#1A1A1A" },
|
|
2374
2319
|
children: [
|
|
2375
2320
|
title,
|
|
2376
|
-
/* @__PURE__ */
|
|
2321
|
+
/* @__PURE__ */ jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", className: "group-open:rotate-180 transition-transform", style: { color: "#999999" }, children: /* @__PURE__ */ jsx("path", { d: "M4 5.5l3 3 3-3", stroke: "currentColor", strokeWidth: "1.3", strokeLinecap: "round", strokeLinejoin: "round" }) })
|
|
2377
2322
|
]
|
|
2378
2323
|
}
|
|
2379
2324
|
),
|
|
2380
|
-
/* @__PURE__ */
|
|
2325
|
+
/* @__PURE__ */ jsx("div", { style: { padding: "0 12px 12px" }, className: "space-y-2", children })
|
|
2381
2326
|
] });
|
|
2382
2327
|
}
|
|
2383
2328
|
function FieldLabel({ children }) {
|
|
2384
|
-
return /* @__PURE__ */
|
|
2329
|
+
return /* @__PURE__ */ jsx("label", { className: "block text-[11px] mb-1", style: { color: "#666666" }, children });
|
|
2385
2330
|
}
|
|
2386
2331
|
function ColorField({ label, value, property, previous, sendStyle }) {
|
|
2387
|
-
const [open, setOpen] =
|
|
2332
|
+
const [open, setOpen] = useState(false);
|
|
2388
2333
|
const hex = rgbToHex(value);
|
|
2389
|
-
return /* @__PURE__ */
|
|
2390
|
-
/* @__PURE__ */
|
|
2391
|
-
/* @__PURE__ */
|
|
2334
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
2335
|
+
/* @__PURE__ */ jsx(FieldLabel, { children: label }),
|
|
2336
|
+
/* @__PURE__ */ jsxs(
|
|
2392
2337
|
"button",
|
|
2393
2338
|
{
|
|
2394
2339
|
onClick: () => setOpen(!open),
|
|
2395
2340
|
className: "flex items-center gap-2 w-full rounded-md text-[13px] hover:border-gray-300 transition-colors",
|
|
2396
2341
|
style: { height: 32, padding: "0 8px", border: "1px solid #E5E5E5", color: "#1A1A1A" },
|
|
2397
2342
|
children: [
|
|
2398
|
-
/* @__PURE__ */
|
|
2399
|
-
/* @__PURE__ */
|
|
2343
|
+
/* @__PURE__ */ jsx("div", { className: "w-6 h-6 rounded shrink-0", style: { backgroundColor: hex, border: "1px solid #E5E5E5" } }),
|
|
2344
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono truncate", children: hex })
|
|
2400
2345
|
]
|
|
2401
2346
|
}
|
|
2402
2347
|
),
|
|
2403
|
-
open && /* @__PURE__ */
|
|
2348
|
+
open && /* @__PURE__ */ jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsx(HexColorPicker, { color: hex, onChange: (newHex) => sendStyle(property, newHex, previous), style: { width: "100%", height: 140 } }) })
|
|
2404
2349
|
] });
|
|
2405
2350
|
}
|
|
2406
2351
|
function NumberField({ label, value, property, previous, suffix = "px", sendStyle }) {
|
|
2407
2352
|
const numVal = parsePxValue(value);
|
|
2408
|
-
return /* @__PURE__ */
|
|
2409
|
-
/* @__PURE__ */
|
|
2410
|
-
/* @__PURE__ */
|
|
2411
|
-
/* @__PURE__ */
|
|
2353
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
2354
|
+
/* @__PURE__ */ jsx(FieldLabel, { children: label }),
|
|
2355
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
|
|
2356
|
+
/* @__PURE__ */ jsx(
|
|
2412
2357
|
"input",
|
|
2413
2358
|
{
|
|
2414
2359
|
type: "number",
|
|
@@ -2418,7 +2363,7 @@ function NumberField({ label, value, property, previous, suffix = "px", sendStyl
|
|
|
2418
2363
|
style: { height: 32, padding: "0 8px", border: "1px solid #E5E5E5", fontSize: 13, color: "#1A1A1A" }
|
|
2419
2364
|
}
|
|
2420
2365
|
),
|
|
2421
|
-
suffix && /* @__PURE__ */
|
|
2366
|
+
suffix && /* @__PURE__ */ jsx(
|
|
2422
2367
|
"span",
|
|
2423
2368
|
{
|
|
2424
2369
|
className: "flex items-center rounded-r-md text-[12px]",
|
|
@@ -2430,24 +2375,24 @@ function NumberField({ label, value, property, previous, suffix = "px", sendStyl
|
|
|
2430
2375
|
] });
|
|
2431
2376
|
}
|
|
2432
2377
|
function SelectField({ label, value, property, previous, options, sendStyle }) {
|
|
2433
|
-
return /* @__PURE__ */
|
|
2434
|
-
/* @__PURE__ */
|
|
2435
|
-
/* @__PURE__ */
|
|
2378
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
2379
|
+
/* @__PURE__ */ jsx(FieldLabel, { children: label }),
|
|
2380
|
+
/* @__PURE__ */ jsx(
|
|
2436
2381
|
"select",
|
|
2437
2382
|
{
|
|
2438
2383
|
value,
|
|
2439
2384
|
onChange: (e) => sendStyle(property, e.target.value, previous),
|
|
2440
2385
|
className: "w-full rounded-md outline-none focus:border-blue-500 bg-white",
|
|
2441
2386
|
style: { height: 32, padding: "0 8px", border: "1px solid #E5E5E5", fontSize: 13, color: "#1A1A1A" },
|
|
2442
|
-
children: options.map((o) => /* @__PURE__ */
|
|
2387
|
+
children: options.map((o) => /* @__PURE__ */ jsx("option", { value: o.value, children: o.label }, o.value))
|
|
2443
2388
|
}
|
|
2444
2389
|
)
|
|
2445
2390
|
] });
|
|
2446
2391
|
}
|
|
2447
2392
|
function ToggleButtons({ label, value, property, previous, options, sendStyle }) {
|
|
2448
|
-
return /* @__PURE__ */
|
|
2449
|
-
/* @__PURE__ */
|
|
2450
|
-
/* @__PURE__ */
|
|
2393
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
2394
|
+
/* @__PURE__ */ jsx(FieldLabel, { children: label }),
|
|
2395
|
+
/* @__PURE__ */ jsx("div", { className: "flex rounded-md overflow-hidden", style: { border: "1px solid #E5E5E5" }, children: options.map((o) => /* @__PURE__ */ jsx(
|
|
2451
2396
|
"button",
|
|
2452
2397
|
{
|
|
2453
2398
|
onClick: () => sendStyle(property, o.value, previous),
|
|
@@ -2465,10 +2410,10 @@ function ToggleButtons({ label, value, property, previous, options, sendStyle })
|
|
|
2465
2410
|
}
|
|
2466
2411
|
function SpacingField({ label, value, property, previous, sendStyle }) {
|
|
2467
2412
|
const numVal = parsePxValue(value);
|
|
2468
|
-
return /* @__PURE__ */
|
|
2469
|
-
/* @__PURE__ */
|
|
2470
|
-
/* @__PURE__ */
|
|
2471
|
-
/* @__PURE__ */
|
|
2413
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
2414
|
+
/* @__PURE__ */ jsx(FieldLabel, { children: label }),
|
|
2415
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
|
|
2416
|
+
/* @__PURE__ */ jsx(
|
|
2472
2417
|
"input",
|
|
2473
2418
|
{
|
|
2474
2419
|
type: "number",
|
|
@@ -2479,7 +2424,7 @@ function SpacingField({ label, value, property, previous, sendStyle }) {
|
|
|
2479
2424
|
style: { height: 32, padding: "0 8px", border: "1px solid #E5E5E5", fontSize: 13, color: "#1A1A1A" }
|
|
2480
2425
|
}
|
|
2481
2426
|
),
|
|
2482
|
-
/* @__PURE__ */
|
|
2427
|
+
/* @__PURE__ */ jsx(
|
|
2483
2428
|
"span",
|
|
2484
2429
|
{
|
|
2485
2430
|
className: "flex items-center rounded-r-md text-[12px]",
|
|
@@ -2519,20 +2464,20 @@ function EditPanel() {
|
|
|
2519
2464
|
const selectedElement = useMutationsStore((s) => s.selectedElement);
|
|
2520
2465
|
const setSelectedElement = useMutationsStore((s) => s.setSelectedElement);
|
|
2521
2466
|
const { sendMutation } = useBridgeMessenger();
|
|
2522
|
-
const [customCSS, setCustomCSS] =
|
|
2523
|
-
const sendStyle =
|
|
2467
|
+
const [customCSS, setCustomCSS] = useState("");
|
|
2468
|
+
const sendStyle = useCallback((property, value, previous) => {
|
|
2524
2469
|
sendMutation({ action: "setStyle", property, value, previous });
|
|
2525
2470
|
}, [sendMutation]);
|
|
2526
|
-
const handleTextChange =
|
|
2471
|
+
const handleTextChange = useCallback((newText) => {
|
|
2527
2472
|
if (!selectedElement) return;
|
|
2528
2473
|
sendMutation({ action: "setText", value: newText, previous: selectedElement.textContent });
|
|
2529
2474
|
}, [sendMutation, selectedElement]);
|
|
2530
|
-
const handleVisibilityToggle =
|
|
2475
|
+
const handleVisibilityToggle = useCallback(() => {
|
|
2531
2476
|
if (!selectedElement) return;
|
|
2532
2477
|
const isHidden = selectedElement.computedStyles.display === "none";
|
|
2533
2478
|
sendMutation({ action: isHidden ? "show" : "hide", value: isHidden ? "block" : "none", previous: selectedElement.computedStyles.display });
|
|
2534
2479
|
}, [sendMutation, selectedElement]);
|
|
2535
|
-
const handleApplyCustomCSS =
|
|
2480
|
+
const handleApplyCustomCSS = useCallback(() => {
|
|
2536
2481
|
const lines = customCSS.split("\n").filter((l) => l.trim());
|
|
2537
2482
|
for (const line of lines) {
|
|
2538
2483
|
const colonIdx = line.indexOf(":");
|
|
@@ -2549,15 +2494,15 @@ function EditPanel() {
|
|
|
2549
2494
|
const tag = selectedElement.tagName.toLowerCase();
|
|
2550
2495
|
const isImage = tag === "img";
|
|
2551
2496
|
const isLink = tag === "a";
|
|
2552
|
-
return /* @__PURE__ */
|
|
2553
|
-
/* @__PURE__ */
|
|
2554
|
-
/* @__PURE__ */
|
|
2555
|
-
/* @__PURE__ */
|
|
2556
|
-
/* @__PURE__ */
|
|
2497
|
+
return /* @__PURE__ */ jsxs("div", { className: "overflow-y-auto h-full", children: [
|
|
2498
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-2", style: { padding: "10px 12px", borderBottom: "1px solid #E5E5E5", backgroundColor: "#F9FAFB" }, children: [
|
|
2499
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
2500
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono text-[13px] font-semibold", style: { color: "#3B82F6" }, children: selectedElement.tagName }),
|
|
2501
|
+
/* @__PURE__ */ jsx("p", { className: "text-[10px] font-mono mt-0.5 break-all leading-tight", style: { color: "#999999" }, children: selectedElement.selector.length > 50 ? selectedElement.selector.slice(0, 50) + "..." : selectedElement.selector })
|
|
2557
2502
|
] }),
|
|
2558
|
-
/* @__PURE__ */
|
|
2503
|
+
/* @__PURE__ */ jsx("button", { onClick: () => setSelectedElement(null), className: "text-[11px] shrink-0 mt-0.5 transition-colors hover:opacity-70", style: { color: "#999999" }, children: "Deselect" })
|
|
2559
2504
|
] }),
|
|
2560
|
-
!isImage && /* @__PURE__ */
|
|
2505
|
+
!isImage && /* @__PURE__ */ jsx(Section, { title: "Content", defaultOpen: !isImage, children: /* @__PURE__ */ jsx(
|
|
2561
2506
|
"textarea",
|
|
2562
2507
|
{
|
|
2563
2508
|
value: selectedElement.textContent,
|
|
@@ -2567,9 +2512,9 @@ function EditPanel() {
|
|
|
2567
2512
|
style: { padding: 8, border: "1px solid #E5E5E5", fontSize: 13, color: "#1A1A1A" }
|
|
2568
2513
|
}
|
|
2569
2514
|
) }),
|
|
2570
|
-
isImage && /* @__PURE__ */
|
|
2571
|
-
/* @__PURE__ */
|
|
2572
|
-
/* @__PURE__ */
|
|
2515
|
+
isImage && /* @__PURE__ */ jsxs(Section, { title: "Source", defaultOpen: true, children: [
|
|
2516
|
+
/* @__PURE__ */ jsx(FieldLabel, { children: "Image URL" }),
|
|
2517
|
+
/* @__PURE__ */ jsx(
|
|
2573
2518
|
"input",
|
|
2574
2519
|
{
|
|
2575
2520
|
type: "text",
|
|
@@ -2580,60 +2525,60 @@ function EditPanel() {
|
|
|
2580
2525
|
}
|
|
2581
2526
|
)
|
|
2582
2527
|
] }),
|
|
2583
|
-
isImage && /* @__PURE__ */
|
|
2528
|
+
isImage && /* @__PURE__ */ jsx(Section, { title: "Image", children: /* @__PURE__ */ jsx(SelectField, { label: "Object Fit", value: styles.objectFit || "cover", property: "objectFit", previous: styles.objectFit || "", options: [
|
|
2584
2529
|
{ label: "Cover", value: "cover" },
|
|
2585
2530
|
{ label: "Contain", value: "contain" },
|
|
2586
2531
|
{ label: "Fill", value: "fill" },
|
|
2587
2532
|
{ label: "None", value: "none" }
|
|
2588
2533
|
], sendStyle }) }),
|
|
2589
|
-
isLink && /* @__PURE__ */
|
|
2590
|
-
/* @__PURE__ */
|
|
2591
|
-
/* @__PURE__ */
|
|
2534
|
+
isLink && /* @__PURE__ */ jsxs(Section, { title: "Link", children: [
|
|
2535
|
+
/* @__PURE__ */ jsx(FieldLabel, { children: "Target" }),
|
|
2536
|
+
/* @__PURE__ */ jsxs(
|
|
2592
2537
|
"select",
|
|
2593
2538
|
{
|
|
2594
2539
|
className: "w-full rounded-md outline-none focus:border-blue-500 bg-white",
|
|
2595
2540
|
style: { height: 32, padding: "0 8px", border: "1px solid #E5E5E5", fontSize: 13, color: "#1A1A1A" },
|
|
2596
2541
|
children: [
|
|
2597
|
-
/* @__PURE__ */
|
|
2598
|
-
/* @__PURE__ */
|
|
2542
|
+
/* @__PURE__ */ jsx("option", { value: "_self", children: "Same window (_self)" }),
|
|
2543
|
+
/* @__PURE__ */ jsx("option", { value: "_blank", children: "New tab (_blank)" })
|
|
2599
2544
|
]
|
|
2600
2545
|
}
|
|
2601
2546
|
)
|
|
2602
2547
|
] }),
|
|
2603
|
-
/* @__PURE__ */
|
|
2604
|
-
/* @__PURE__ */
|
|
2605
|
-
/* @__PURE__ */
|
|
2548
|
+
/* @__PURE__ */ jsx(Section, { title: "Size & Position", children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
2549
|
+
/* @__PURE__ */ jsx(NumberField, { label: "Width", value: styles.width || "auto", property: "width", previous: styles.width || "", sendStyle }),
|
|
2550
|
+
/* @__PURE__ */ jsx(NumberField, { label: "Height", value: styles.height || "auto", property: "height", previous: styles.height || "", sendStyle })
|
|
2606
2551
|
] }) }),
|
|
2607
|
-
/* @__PURE__ */
|
|
2608
|
-
/* @__PURE__ */
|
|
2609
|
-
/* @__PURE__ */
|
|
2552
|
+
/* @__PURE__ */ jsxs(Section, { title: "Background", children: [
|
|
2553
|
+
/* @__PURE__ */ jsx(ColorField, { label: "Background Color", value: styles.backgroundColor || "", property: "backgroundColor", previous: styles.backgroundColor || "", sendStyle }),
|
|
2554
|
+
/* @__PURE__ */ jsx(NumberField, { label: "Opacity", value: styles.opacity || "1", property: "opacity", previous: styles.opacity || "", suffix: "", sendStyle })
|
|
2610
2555
|
] }),
|
|
2611
|
-
/* @__PURE__ */
|
|
2612
|
-
/* @__PURE__ */
|
|
2613
|
-
/* @__PURE__ */
|
|
2614
|
-
/* @__PURE__ */
|
|
2556
|
+
/* @__PURE__ */ jsxs(Section, { title: "Border", children: [
|
|
2557
|
+
/* @__PURE__ */ jsx(NumberField, { label: "Border Width", value: styles.borderWidth || "0px", property: "borderWidth", previous: styles.borderWidth || "", sendStyle }),
|
|
2558
|
+
/* @__PURE__ */ jsx(ColorField, { label: "Border Color", value: styles.borderColor || "", property: "borderColor", previous: styles.borderColor || "", sendStyle }),
|
|
2559
|
+
/* @__PURE__ */ jsx(SelectField, { label: "Border Style", value: styles.borderStyle || "solid", property: "borderStyle", previous: styles.borderStyle || "", options: [
|
|
2615
2560
|
{ label: "Solid", value: "solid" },
|
|
2616
2561
|
{ label: "Dashed", value: "dashed" },
|
|
2617
2562
|
{ label: "Dotted", value: "dotted" },
|
|
2618
2563
|
{ label: "None", value: "none" }
|
|
2619
2564
|
], sendStyle }),
|
|
2620
|
-
/* @__PURE__ */
|
|
2565
|
+
/* @__PURE__ */ jsx(NumberField, { label: "Border Radius", value: styles.borderRadius || "0px", property: "borderRadius", previous: styles.borderRadius || "", sendStyle })
|
|
2621
2566
|
] }),
|
|
2622
|
-
/* @__PURE__ */
|
|
2623
|
-
/* @__PURE__ */
|
|
2624
|
-
/* @__PURE__ */
|
|
2625
|
-
/* @__PURE__ */
|
|
2626
|
-
/* @__PURE__ */
|
|
2627
|
-
/* @__PURE__ */
|
|
2628
|
-
/* @__PURE__ */
|
|
2629
|
-
/* @__PURE__ */
|
|
2630
|
-
/* @__PURE__ */
|
|
2631
|
-
/* @__PURE__ */
|
|
2632
|
-
/* @__PURE__ */
|
|
2633
|
-
/* @__PURE__ */
|
|
2567
|
+
/* @__PURE__ */ jsx(Section, { title: "Shadow", children: /* @__PURE__ */ jsx("p", { className: "text-[12px]", style: { color: "#999999" }, children: "Box shadow controls \u2014 coming soon" }) }),
|
|
2568
|
+
/* @__PURE__ */ jsx(Section, { title: "Padding", children: /* @__PURE__ */ jsx(SpacingField, { label: "All sides", value: styles.padding || "0px", property: "padding", previous: styles.padding || "", sendStyle }) }),
|
|
2569
|
+
/* @__PURE__ */ jsx(Section, { title: "Margin", children: /* @__PURE__ */ jsx(SpacingField, { label: "All sides", value: styles.margin || "0px", property: "margin", previous: styles.margin || "", sendStyle }) }),
|
|
2570
|
+
/* @__PURE__ */ jsxs(Section, { title: "Typography", children: [
|
|
2571
|
+
/* @__PURE__ */ jsx(ColorField, { label: "Text Color", value: styles.color || "", property: "color", previous: styles.color || "", sendStyle }),
|
|
2572
|
+
/* @__PURE__ */ jsx(NumberField, { label: "Font Size", value: styles.fontSize || "16px", property: "fontSize", previous: styles.fontSize || "", sendStyle }),
|
|
2573
|
+
/* @__PURE__ */ jsx(SelectField, { label: "Font Weight", value: styles.fontWeight || "400", property: "fontWeight", previous: styles.fontWeight || "", options: FONT_WEIGHT_OPTIONS, sendStyle }),
|
|
2574
|
+
/* @__PURE__ */ jsx(NumberField, { label: "Line Height", value: styles.lineHeight || "normal", property: "lineHeight", previous: styles.lineHeight || "", sendStyle }),
|
|
2575
|
+
/* @__PURE__ */ jsx(NumberField, { label: "Letter Spacing", value: styles.letterSpacing || "0px", property: "letterSpacing", previous: styles.letterSpacing || "", sendStyle }),
|
|
2576
|
+
/* @__PURE__ */ jsx(ToggleButtons, { label: "Text Align", value: styles.textAlign || "left", property: "textAlign", previous: styles.textAlign || "", options: TEXT_ALIGN_OPTIONS, sendStyle }),
|
|
2577
|
+
/* @__PURE__ */ jsx(ToggleButtons, { label: "Text Decoration", value: styles.textDecoration?.split(" ")[0] || "none", property: "textDecoration", previous: styles.textDecoration || "", options: TEXT_DECORATION_OPTIONS, sendStyle }),
|
|
2578
|
+
/* @__PURE__ */ jsx(ToggleButtons, { label: "Text Transform", value: styles.textTransform || "none", property: "textTransform", previous: styles.textTransform || "", options: TEXT_TRANSFORM_OPTIONS, sendStyle })
|
|
2634
2579
|
] }),
|
|
2635
|
-
/* @__PURE__ */
|
|
2636
|
-
/* @__PURE__ */
|
|
2580
|
+
/* @__PURE__ */ jsxs(Section, { title: "CSS and Classes", children: [
|
|
2581
|
+
/* @__PURE__ */ jsx(
|
|
2637
2582
|
"textarea",
|
|
2638
2583
|
{
|
|
2639
2584
|
value: customCSS,
|
|
@@ -2644,7 +2589,7 @@ function EditPanel() {
|
|
|
2644
2589
|
style: { padding: 8, border: "1px solid #E5E5E5", fontSize: 13, color: "#1A1A1A" }
|
|
2645
2590
|
}
|
|
2646
2591
|
),
|
|
2647
|
-
/* @__PURE__ */
|
|
2592
|
+
/* @__PURE__ */ jsx(
|
|
2648
2593
|
"button",
|
|
2649
2594
|
{
|
|
2650
2595
|
onClick: handleApplyCustomCSS,
|
|
@@ -2654,7 +2599,7 @@ function EditPanel() {
|
|
|
2654
2599
|
}
|
|
2655
2600
|
)
|
|
2656
2601
|
] }),
|
|
2657
|
-
/* @__PURE__ */
|
|
2602
|
+
/* @__PURE__ */ jsx(Section, { title: "Visibility", children: /* @__PURE__ */ jsx(
|
|
2658
2603
|
"button",
|
|
2659
2604
|
{
|
|
2660
2605
|
onClick: handleVisibilityToggle,
|
|
@@ -2671,9 +2616,6 @@ function EditPanel() {
|
|
|
2671
2616
|
] });
|
|
2672
2617
|
}
|
|
2673
2618
|
|
|
2674
|
-
// src/components/CodeEditorPanel.tsx
|
|
2675
|
-
import { useState as useState8, useEffect as useEffect7, useMemo as useMemo3, useCallback as useCallback9 } from "react";
|
|
2676
|
-
|
|
2677
2619
|
// src/lib/exportGenerator.ts
|
|
2678
2620
|
function escapeJS(str) {
|
|
2679
2621
|
return str.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/\n/g, "\\n").replace(/\r/g, "\\r");
|
|
@@ -2957,8 +2899,6 @@ function convertChainSetsToMutations(chainSets) {
|
|
|
2957
2899
|
case "remove":
|
|
2958
2900
|
mutations.push({ id, selector: cs.selector, action: "hide", value: "none", timestamp });
|
|
2959
2901
|
break;
|
|
2960
|
-
case "class":
|
|
2961
|
-
break;
|
|
2962
2902
|
}
|
|
2963
2903
|
}
|
|
2964
2904
|
return mutations;
|
|
@@ -3023,9 +2963,6 @@ function generateJSFromChainSets(chainSets) {
|
|
|
3023
2963
|
lines.push("})();");
|
|
3024
2964
|
return lines.join("\n");
|
|
3025
2965
|
}
|
|
3026
|
-
|
|
3027
|
-
// src/components/CodeEditorPanel.tsx
|
|
3028
|
-
import { Fragment as Fragment3, jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
3029
2966
|
function highlightCSS(code) {
|
|
3030
2967
|
return code.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/^([^{/\n][^{\n]*)\{/gm, '<span style="color:#3B82F6">$1</span>{').replace(/([\w-]+)\s*:/g, '<span style="color:#9333EA">$1</span>:').replace(/:\s*([^;!]+)/g, ': <span style="color:#059669">$1</span>').replace(/!important/g, '<span style="color:#EF4444">!important</span>').replace(/(\/\*[\s\S]*?\*\/)/g, '<span style="color:#999999">$1</span>');
|
|
3031
2968
|
}
|
|
@@ -3033,34 +2970,34 @@ function highlightJS(code) {
|
|
|
3033
2970
|
return code.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/\b(var|let|const|function|if|else|return|try|catch|document|window)\b/g, '<span style="color:#9333EA">$1</span>').replace(/('[^&#]*'|'[^']*')/g, '<span style="color:#059669">$1</span>').replace(/(\/\/[^\n]*)/g, '<span style="color:#999999">$1</span>').replace(/(\/\*[\s\S]*?\*\/)/g, '<span style="color:#999999">$1</span>');
|
|
3034
2971
|
}
|
|
3035
2972
|
function CodeEditorPanel() {
|
|
3036
|
-
const [tab, setTab] =
|
|
3037
|
-
const [cssEdited, setCssEdited] =
|
|
3038
|
-
const [jsEdited, setJsEdited] =
|
|
3039
|
-
const [copied, setCopied] =
|
|
2973
|
+
const [tab, setTab] = useState("css");
|
|
2974
|
+
const [cssEdited, setCssEdited] = useState(null);
|
|
2975
|
+
const [jsEdited, setJsEdited] = useState(null);
|
|
2976
|
+
const [copied, setCopied] = useState(false);
|
|
3040
2977
|
const variations = useVariationsStore((s) => s.variations);
|
|
3041
2978
|
const activeVariationId = useVariationsStore((s) => s.activeVariationId);
|
|
3042
2979
|
const activeVariation = variations.find((v) => v.id === activeVariationId);
|
|
3043
|
-
const mutations =
|
|
3044
|
-
const generatedCSS =
|
|
2980
|
+
const mutations = useMemo(() => activeVariation?.mutations || [], [activeVariation?.mutations]);
|
|
2981
|
+
const generatedCSS = useMemo(() => {
|
|
3045
2982
|
const result = exportCSS(mutations);
|
|
3046
2983
|
return result.css;
|
|
3047
2984
|
}, [mutations]);
|
|
3048
|
-
const generatedJS =
|
|
2985
|
+
const generatedJS = useMemo(() => {
|
|
3049
2986
|
return exportJS(mutations);
|
|
3050
2987
|
}, [mutations]);
|
|
3051
|
-
|
|
2988
|
+
useEffect(() => {
|
|
3052
2989
|
setCssEdited(null);
|
|
3053
2990
|
setJsEdited(null);
|
|
3054
2991
|
}, [mutations.length]);
|
|
3055
2992
|
const displayCSS = cssEdited ?? generatedCSS;
|
|
3056
2993
|
const displayJS = jsEdited ?? generatedJS;
|
|
3057
|
-
const handleCSSChange =
|
|
2994
|
+
const handleCSSChange = useCallback((value) => {
|
|
3058
2995
|
setCssEdited(value);
|
|
3059
2996
|
}, []);
|
|
3060
|
-
const handleJSChange =
|
|
2997
|
+
const handleJSChange = useCallback((value) => {
|
|
3061
2998
|
setJsEdited(value);
|
|
3062
2999
|
}, []);
|
|
3063
|
-
const handleCopy =
|
|
3000
|
+
const handleCopy = useCallback(async () => {
|
|
3064
3001
|
const text = tab === "css" ? displayCSS : displayJS;
|
|
3065
3002
|
try {
|
|
3066
3003
|
await navigator.clipboard.writeText(text);
|
|
@@ -3081,11 +3018,11 @@ function CodeEditorPanel() {
|
|
|
3081
3018
|
{ id: "css", label: "CSS" },
|
|
3082
3019
|
{ id: "js", label: "JavaScript" }
|
|
3083
3020
|
];
|
|
3084
|
-
return /* @__PURE__ */
|
|
3085
|
-
/* @__PURE__ */
|
|
3086
|
-
/* @__PURE__ */
|
|
3087
|
-
/* @__PURE__ */
|
|
3088
|
-
tabs.map((t) => /* @__PURE__ */
|
|
3021
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col h-full", style: { borderTop: "1px solid #E5E5E5" }, children: [
|
|
3022
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between shrink-0 px-3", style: { height: 36, borderBottom: "1px solid #E5E5E5" }, children: [
|
|
3023
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5", children: [
|
|
3024
|
+
/* @__PURE__ */ jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", style: { color: "#999999", marginRight: 4 }, children: /* @__PURE__ */ jsx("path", { d: "M5 3l-3 4 3 4M9 3l3 4-3 4", stroke: "currentColor", strokeWidth: "1.3", strokeLinecap: "round", strokeLinejoin: "round" }) }),
|
|
3025
|
+
tabs.map((t) => /* @__PURE__ */ jsx(
|
|
3089
3026
|
"button",
|
|
3090
3027
|
{
|
|
3091
3028
|
onClick: () => setTab(t.id),
|
|
@@ -3099,26 +3036,26 @@ function CodeEditorPanel() {
|
|
|
3099
3036
|
t.id
|
|
3100
3037
|
))
|
|
3101
3038
|
] }),
|
|
3102
|
-
/* @__PURE__ */
|
|
3103
|
-
mutations.length > 0 && /* @__PURE__ */
|
|
3039
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
3040
|
+
mutations.length > 0 && /* @__PURE__ */ jsxs("span", { className: "text-[10px]", style: { color: "#999999" }, children: [
|
|
3104
3041
|
mutations.length,
|
|
3105
3042
|
" mutation",
|
|
3106
3043
|
mutations.length !== 1 ? "s" : ""
|
|
3107
3044
|
] }),
|
|
3108
|
-
/* @__PURE__ */
|
|
3045
|
+
/* @__PURE__ */ jsx(
|
|
3109
3046
|
"button",
|
|
3110
3047
|
{
|
|
3111
3048
|
onClick: handleCopy,
|
|
3112
3049
|
className: "flex items-center gap-1 px-2 py-1 rounded text-[11px] font-medium transition-colors hover:bg-[#F3F4F6]",
|
|
3113
3050
|
style: { color: copied ? "#22C55E" : "#666666" },
|
|
3114
3051
|
title: "Copy to clipboard",
|
|
3115
|
-
children: copied ? /* @__PURE__ */
|
|
3116
|
-
/* @__PURE__ */
|
|
3052
|
+
children: copied ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3053
|
+
/* @__PURE__ */ jsx("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", children: /* @__PURE__ */ jsx("path", { d: "M2 6l3 3 5-5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }),
|
|
3117
3054
|
"Copied"
|
|
3118
|
-
] }) : /* @__PURE__ */
|
|
3119
|
-
/* @__PURE__ */
|
|
3120
|
-
/* @__PURE__ */
|
|
3121
|
-
/* @__PURE__ */
|
|
3055
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3056
|
+
/* @__PURE__ */ jsxs("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", children: [
|
|
3057
|
+
/* @__PURE__ */ jsx("rect", { x: "4", y: "4", width: "6", height: "6", rx: "1", stroke: "currentColor", strokeWidth: "1.2" }),
|
|
3058
|
+
/* @__PURE__ */ jsx("path", { d: "M8 2H3a1 1 0 00-1 1v5", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round" })
|
|
3122
3059
|
] }),
|
|
3123
3060
|
"Copy"
|
|
3124
3061
|
] })
|
|
@@ -3126,7 +3063,7 @@ function CodeEditorPanel() {
|
|
|
3126
3063
|
)
|
|
3127
3064
|
] })
|
|
3128
3065
|
] }),
|
|
3129
|
-
/* @__PURE__ */
|
|
3066
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 min-h-0 overflow-auto", children: tab === "css" ? /* @__PURE__ */ jsx(
|
|
3130
3067
|
CodeArea,
|
|
3131
3068
|
{
|
|
3132
3069
|
value: displayCSS,
|
|
@@ -3134,7 +3071,7 @@ function CodeEditorPanel() {
|
|
|
3134
3071
|
highlight: highlightCSS,
|
|
3135
3072
|
placeholder: "/* No style mutations yet */\n/* Edit elements in the canvas or add CSS here */\n\n.selector {\n property: value !important;\n}"
|
|
3136
3073
|
}
|
|
3137
|
-
) : /* @__PURE__ */
|
|
3074
|
+
) : /* @__PURE__ */ jsx(
|
|
3138
3075
|
CodeArea,
|
|
3139
3076
|
{
|
|
3140
3077
|
value: displayJS,
|
|
@@ -3152,14 +3089,14 @@ function CodeArea({
|
|
|
3152
3089
|
placeholder
|
|
3153
3090
|
}) {
|
|
3154
3091
|
const isEmpty = !value.trim();
|
|
3155
|
-
return /* @__PURE__ */
|
|
3156
|
-
/* @__PURE__ */
|
|
3092
|
+
return /* @__PURE__ */ jsxs("div", { className: "relative", style: { minHeight: "100%" }, children: [
|
|
3093
|
+
/* @__PURE__ */ jsx(
|
|
3157
3094
|
"pre",
|
|
3158
3095
|
{
|
|
3159
3096
|
className: "px-3 py-2 text-[12px] leading-relaxed pointer-events-none",
|
|
3160
3097
|
style: { fontFamily: "'SF Mono', 'Fira Code', 'Cascadia Code', monospace", color: "#1A1A1A", margin: 0, whiteSpace: "pre-wrap", wordBreak: "break-all" },
|
|
3161
3098
|
"aria-hidden": true,
|
|
3162
|
-
children: /* @__PURE__ */
|
|
3099
|
+
children: /* @__PURE__ */ jsx(
|
|
3163
3100
|
"code",
|
|
3164
3101
|
{
|
|
3165
3102
|
dangerouslySetInnerHTML: {
|
|
@@ -3169,7 +3106,7 @@ function CodeArea({
|
|
|
3169
3106
|
)
|
|
3170
3107
|
}
|
|
3171
3108
|
),
|
|
3172
|
-
/* @__PURE__ */
|
|
3109
|
+
/* @__PURE__ */ jsx(
|
|
3173
3110
|
"textarea",
|
|
3174
3111
|
{
|
|
3175
3112
|
value,
|
|
@@ -3187,10 +3124,6 @@ function CodeArea({
|
|
|
3187
3124
|
)
|
|
3188
3125
|
] });
|
|
3189
3126
|
}
|
|
3190
|
-
|
|
3191
|
-
// src/components/AIChatPanel.tsx
|
|
3192
|
-
import { useState as useState9, useCallback as useCallback10, useEffect as useEffect8, useRef as useRef7 } from "react";
|
|
3193
|
-
import { Fragment as Fragment4, jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
3194
3127
|
var QUICK_ACTIONS = [
|
|
3195
3128
|
{ icon: "\u2726", label: "Improve this element", requiresSelection: true, group: "suggestions" },
|
|
3196
3129
|
{ icon: "\u2726", label: "Suggest headline alternatives", group: "suggestions" },
|
|
@@ -3242,25 +3175,25 @@ function SuggestionCard({
|
|
|
3242
3175
|
applied,
|
|
3243
3176
|
onApply
|
|
3244
3177
|
}) {
|
|
3245
|
-
return /* @__PURE__ */
|
|
3178
|
+
return /* @__PURE__ */ jsxs(
|
|
3246
3179
|
"div",
|
|
3247
3180
|
{
|
|
3248
3181
|
className: "rounded-lg p-2.5 my-2",
|
|
3249
3182
|
style: { backgroundColor: "#F8F7FF", border: "1px solid #E9E5FF" },
|
|
3250
3183
|
children: [
|
|
3251
|
-
/* @__PURE__ */
|
|
3252
|
-
/* @__PURE__ */
|
|
3253
|
-
/* @__PURE__ */
|
|
3184
|
+
/* @__PURE__ */ jsx("p", { className: "text-[11px] font-medium leading-relaxed", style: { color: "#1A1A1A" }, children: suggestion.description }),
|
|
3185
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 mt-1.5", children: [
|
|
3186
|
+
/* @__PURE__ */ jsxs("span", { className: "text-[9px] font-mono px-1.5 py-0.5 rounded", style: { backgroundColor: "#F3F4F6", color: "#666" }, children: [
|
|
3254
3187
|
suggestion.mutations.length,
|
|
3255
3188
|
" change",
|
|
3256
3189
|
suggestion.mutations.length !== 1 ? "s" : ""
|
|
3257
3190
|
] }),
|
|
3258
|
-
suggestion.mutations.map((m, i) => /* @__PURE__ */
|
|
3191
|
+
suggestion.mutations.map((m, i) => /* @__PURE__ */ jsx("span", { className: "text-[9px] font-mono px-1 py-0.5 rounded", style: { backgroundColor: "#EFF6FF", color: "#3B82F6" }, children: m.action }, i))
|
|
3259
3192
|
] }),
|
|
3260
|
-
/* @__PURE__ */
|
|
3261
|
-
/* @__PURE__ */
|
|
3262
|
-
/* @__PURE__ */
|
|
3263
|
-
] }) : /* @__PURE__ */
|
|
3193
|
+
/* @__PURE__ */ jsx("div", { className: "flex gap-1.5 mt-2", children: applied ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
3194
|
+
/* @__PURE__ */ jsx("svg", { width: "12", height: "12", viewBox: "0 0 14 14", fill: "none", children: /* @__PURE__ */ jsx("path", { d: "M3 7l3 3 5-5", stroke: "#22C55E", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }),
|
|
3195
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] font-medium", style: { color: "#22C55E" }, children: "Applied" })
|
|
3196
|
+
] }) : /* @__PURE__ */ jsx(
|
|
3264
3197
|
"button",
|
|
3265
3198
|
{
|
|
3266
3199
|
onClick: onApply,
|
|
@@ -3277,69 +3210,69 @@ function ResponseActions({
|
|
|
3277
3210
|
content,
|
|
3278
3211
|
onRetry
|
|
3279
3212
|
}) {
|
|
3280
|
-
const [copied, setCopied] =
|
|
3281
|
-
const [liked, setLiked] =
|
|
3282
|
-
const handleCopy =
|
|
3213
|
+
const [copied, setCopied] = useState(false);
|
|
3214
|
+
const [liked, setLiked] = useState(null);
|
|
3215
|
+
const handleCopy = useCallback(() => {
|
|
3283
3216
|
navigator.clipboard.writeText(content).then(() => {
|
|
3284
3217
|
setCopied(true);
|
|
3285
3218
|
setTimeout(() => setCopied(false), 2e3);
|
|
3286
3219
|
});
|
|
3287
3220
|
}, [content]);
|
|
3288
|
-
return /* @__PURE__ */
|
|
3289
|
-
/* @__PURE__ */
|
|
3221
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5 mt-1.5", children: [
|
|
3222
|
+
/* @__PURE__ */ jsx(
|
|
3290
3223
|
"button",
|
|
3291
3224
|
{
|
|
3292
3225
|
onClick: handleCopy,
|
|
3293
3226
|
className: "w-6 h-6 rounded flex items-center justify-center transition-colors hover:bg-[#F3F4F6]",
|
|
3294
3227
|
title: copied ? "Copied!" : "Copy response",
|
|
3295
|
-
children: copied ? /* @__PURE__ */
|
|
3296
|
-
/* @__PURE__ */
|
|
3297
|
-
/* @__PURE__ */
|
|
3228
|
+
children: copied ? /* @__PURE__ */ jsx("svg", { width: "12", height: "12", viewBox: "0 0 14 14", fill: "none", children: /* @__PURE__ */ jsx("path", { d: "M3 7l3 3 5-5", stroke: "#22C55E", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }) : /* @__PURE__ */ jsxs("svg", { width: "12", height: "12", viewBox: "0 0 16 16", fill: "none", children: [
|
|
3229
|
+
/* @__PURE__ */ jsx("rect", { x: "5", y: "5", width: "8", height: "8", rx: "1.5", stroke: "#999", strokeWidth: "1.2" }),
|
|
3230
|
+
/* @__PURE__ */ jsx("path", { d: "M3 11V3a1.5 1.5 0 011.5-1.5H11", stroke: "#999", strokeWidth: "1.2", strokeLinecap: "round" })
|
|
3298
3231
|
] })
|
|
3299
3232
|
}
|
|
3300
3233
|
),
|
|
3301
|
-
/* @__PURE__ */
|
|
3234
|
+
/* @__PURE__ */ jsx(
|
|
3302
3235
|
"button",
|
|
3303
3236
|
{
|
|
3304
3237
|
onClick: () => setLiked(liked === "up" ? null : "up"),
|
|
3305
3238
|
className: "w-6 h-6 rounded flex items-center justify-center transition-colors hover:bg-[#F3F4F6]",
|
|
3306
3239
|
title: "Helpful",
|
|
3307
|
-
children: /* @__PURE__ */
|
|
3240
|
+
children: /* @__PURE__ */ jsx("svg", { width: "12", height: "12", viewBox: "0 0 16 16", fill: liked === "up" ? "#22C55E" : "none", children: /* @__PURE__ */ jsx("path", { d: "M4 7h2l2-5 1.5 1v4H13l-1 7H4V7z", stroke: liked === "up" ? "#22C55E" : "#999", strokeWidth: "1.2", strokeLinejoin: "round" }) })
|
|
3308
3241
|
}
|
|
3309
3242
|
),
|
|
3310
|
-
/* @__PURE__ */
|
|
3243
|
+
/* @__PURE__ */ jsx(
|
|
3311
3244
|
"button",
|
|
3312
3245
|
{
|
|
3313
3246
|
onClick: () => setLiked(liked === "down" ? null : "down"),
|
|
3314
3247
|
className: "w-6 h-6 rounded flex items-center justify-center transition-colors hover:bg-[#F3F4F6]",
|
|
3315
3248
|
title: "Not helpful",
|
|
3316
|
-
children: /* @__PURE__ */
|
|
3249
|
+
children: /* @__PURE__ */ jsx("svg", { width: "12", height: "12", viewBox: "0 0 16 16", fill: liked === "down" ? "#EF4444" : "none", style: { transform: "scaleY(-1)" }, children: /* @__PURE__ */ jsx("path", { d: "M4 7h2l2-5 1.5 1v4H13l-1 7H4V7z", stroke: liked === "down" ? "#EF4444" : "#999", strokeWidth: "1.2", strokeLinejoin: "round" }) })
|
|
3317
3250
|
}
|
|
3318
3251
|
),
|
|
3319
|
-
/* @__PURE__ */
|
|
3252
|
+
/* @__PURE__ */ jsx(
|
|
3320
3253
|
"button",
|
|
3321
3254
|
{
|
|
3322
3255
|
onClick: onRetry,
|
|
3323
3256
|
className: "w-6 h-6 rounded flex items-center justify-center transition-colors hover:bg-[#F3F4F6]",
|
|
3324
3257
|
title: "Retry",
|
|
3325
|
-
children: /* @__PURE__ */
|
|
3326
|
-
/* @__PURE__ */
|
|
3327
|
-
/* @__PURE__ */
|
|
3258
|
+
children: /* @__PURE__ */ jsxs("svg", { width: "12", height: "12", viewBox: "0 0 16 16", fill: "none", children: [
|
|
3259
|
+
/* @__PURE__ */ jsx("path", { d: "M2 8a6 6 0 0110.5-4M14 8a6 6 0 01-10.5 4", stroke: "#999", strokeWidth: "1.2", strokeLinecap: "round" }),
|
|
3260
|
+
/* @__PURE__ */ jsx("path", { d: "M12 1v3h-3M4 15v-3h3", stroke: "#999", strokeWidth: "1.2", strokeLinecap: "round", strokeLinejoin: "round" })
|
|
3328
3261
|
] })
|
|
3329
3262
|
}
|
|
3330
3263
|
)
|
|
3331
3264
|
] });
|
|
3332
3265
|
}
|
|
3333
3266
|
function ThinkingIndicator({ startTime }) {
|
|
3334
|
-
const [elapsed, setElapsed] =
|
|
3335
|
-
const [visibleSteps, setVisibleSteps] =
|
|
3336
|
-
|
|
3267
|
+
const [elapsed, setElapsed] = useState(0);
|
|
3268
|
+
const [visibleSteps, setVisibleSteps] = useState(1);
|
|
3269
|
+
useEffect(() => {
|
|
3337
3270
|
const timer = setInterval(() => {
|
|
3338
3271
|
setElapsed(Math.floor((Date.now() - startTime) / 1e3));
|
|
3339
3272
|
}, 1e3);
|
|
3340
3273
|
return () => clearInterval(timer);
|
|
3341
3274
|
}, [startTime]);
|
|
3342
|
-
|
|
3275
|
+
useEffect(() => {
|
|
3343
3276
|
if (visibleSteps < LOADING_STEPS.length) {
|
|
3344
3277
|
const delay = setTimeout(() => {
|
|
3345
3278
|
setVisibleSteps((prev) => Math.min(prev + 1, LOADING_STEPS.length));
|
|
@@ -3347,16 +3280,16 @@ function ThinkingIndicator({ startTime }) {
|
|
|
3347
3280
|
return () => clearTimeout(delay);
|
|
3348
3281
|
}
|
|
3349
3282
|
}, [visibleSteps]);
|
|
3350
|
-
return /* @__PURE__ */
|
|
3351
|
-
/* @__PURE__ */
|
|
3352
|
-
/* @__PURE__ */
|
|
3353
|
-
/* @__PURE__ */
|
|
3354
|
-
/* @__PURE__ */
|
|
3283
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-2 py-2", children: [
|
|
3284
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
3285
|
+
/* @__PURE__ */ jsx("svg", { width: "14", height: "14", viewBox: "0 0 16 16", fill: "none", className: "animate-pulse", children: /* @__PURE__ */ jsx("path", { d: "M8 1l1.5 3.5L13 6l-3.5 1.5L8 11 6.5 7.5 3 6l3.5-1.5L8 1z", fill: "#8B5CF6", stroke: "#8B5CF6", strokeWidth: "0.5" }) }),
|
|
3286
|
+
/* @__PURE__ */ jsx("span", { className: "text-[11px] font-medium", style: { color: "#8B5CF6" }, children: "Thinking..." }),
|
|
3287
|
+
/* @__PURE__ */ jsxs("span", { className: "text-[10px]", style: { color: "#999" }, children: [
|
|
3355
3288
|
elapsed,
|
|
3356
3289
|
"s"
|
|
3357
3290
|
] })
|
|
3358
3291
|
] }),
|
|
3359
|
-
/* @__PURE__ */
|
|
3292
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-1 ml-5", children: LOADING_STEPS.slice(0, visibleSteps).map((step, i) => /* @__PURE__ */ jsx(
|
|
3360
3293
|
"div",
|
|
3361
3294
|
{
|
|
3362
3295
|
className: "text-[10px] transition-opacity duration-500",
|
|
@@ -3371,29 +3304,29 @@ function ThinkingIndicator({ startTime }) {
|
|
|
3371
3304
|
] });
|
|
3372
3305
|
}
|
|
3373
3306
|
function AIChatPanel() {
|
|
3374
|
-
const [messages, setMessages] =
|
|
3375
|
-
const [input, setInput] =
|
|
3376
|
-
const [isLoading, setIsLoading] =
|
|
3377
|
-
const [loadingStartTime, setLoadingStartTime] =
|
|
3378
|
-
const scrollRef =
|
|
3379
|
-
const inputRef =
|
|
3307
|
+
const [messages, setMessages] = useState([]);
|
|
3308
|
+
const [input, setInput] = useState("");
|
|
3309
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
3310
|
+
const [loadingStartTime, setLoadingStartTime] = useState(0);
|
|
3311
|
+
const scrollRef = useRef(null);
|
|
3312
|
+
const inputRef = useRef(null);
|
|
3380
3313
|
const variations = useVariationsStore((s) => s.variations);
|
|
3381
3314
|
const activeVariationId = useVariationsStore((s) => s.activeVariationId);
|
|
3382
3315
|
const addMutationToActive = useVariationsStore((s) => s.addMutationToActive);
|
|
3383
3316
|
const selectedElement = useMutationsStore((s) => s.selectedElement);
|
|
3384
3317
|
const activeVariation = variations.find((v) => v.id === activeVariationId);
|
|
3385
3318
|
const hasConversation = messages.length > 0;
|
|
3386
|
-
|
|
3319
|
+
useEffect(() => {
|
|
3387
3320
|
if (scrollRef.current) {
|
|
3388
3321
|
scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
|
|
3389
3322
|
}
|
|
3390
3323
|
}, [messages, isLoading]);
|
|
3391
|
-
|
|
3324
|
+
useEffect(() => {
|
|
3392
3325
|
if (inputRef.current) {
|
|
3393
3326
|
setTimeout(() => inputRef.current?.focus(), 100);
|
|
3394
3327
|
}
|
|
3395
3328
|
}, []);
|
|
3396
|
-
const buildContext =
|
|
3329
|
+
const buildContext = useCallback(() => {
|
|
3397
3330
|
const parts = [];
|
|
3398
3331
|
const expData = window.__CONVERSION_EXPERIMENT_DATA__;
|
|
3399
3332
|
if (expData?.name) parts.push(`Experiment: ${expData.name}`);
|
|
@@ -3435,7 +3368,7 @@ function AIChatPanel() {
|
|
|
3435
3368
|
}
|
|
3436
3369
|
return parts.join("\n");
|
|
3437
3370
|
}, [activeVariation, selectedElement]);
|
|
3438
|
-
const handleSend =
|
|
3371
|
+
const handleSend = useCallback(
|
|
3439
3372
|
async (text) => {
|
|
3440
3373
|
const prompt = text || input.trim();
|
|
3441
3374
|
if (!prompt || isLoading) return;
|
|
@@ -3548,7 +3481,7 @@ ${contextStr}` : prompt,
|
|
|
3548
3481
|
}
|
|
3549
3482
|
return parts.join("\n");
|
|
3550
3483
|
};
|
|
3551
|
-
const handleApplySuggestion =
|
|
3484
|
+
const handleApplySuggestion = useCallback(
|
|
3552
3485
|
(msgId, suggestionIndex, suggestion) => {
|
|
3553
3486
|
for (const mut of suggestion.mutations) {
|
|
3554
3487
|
const mutation = {
|
|
@@ -3573,7 +3506,7 @@ ${contextStr}` : prompt,
|
|
|
3573
3506
|
},
|
|
3574
3507
|
[addMutationToActive]
|
|
3575
3508
|
);
|
|
3576
|
-
const handleRetry =
|
|
3509
|
+
const handleRetry = useCallback(
|
|
3577
3510
|
(msgId) => {
|
|
3578
3511
|
const msgIndex = messages.findIndex((m) => m.id === msgId);
|
|
3579
3512
|
if (msgIndex <= 0) return;
|
|
@@ -3585,7 +3518,7 @@ ${contextStr}` : prompt,
|
|
|
3585
3518
|
},
|
|
3586
3519
|
[messages, handleSend]
|
|
3587
3520
|
);
|
|
3588
|
-
const buildQuickActionPrompt =
|
|
3521
|
+
const buildQuickActionPrompt = useCallback(
|
|
3589
3522
|
(action) => {
|
|
3590
3523
|
const el = selectedElement;
|
|
3591
3524
|
switch (action.label) {
|
|
@@ -3621,12 +3554,12 @@ ${contextStr}` : prompt,
|
|
|
3621
3554
|
};
|
|
3622
3555
|
const suggestionsGroup = QUICK_ACTIONS.filter((a) => a.group === "suggestions");
|
|
3623
3556
|
const analysisGroup = QUICK_ACTIONS.filter((a) => a.group === "analysis");
|
|
3624
|
-
return /* @__PURE__ */
|
|
3625
|
-
/* @__PURE__ */
|
|
3557
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col h-full", children: [
|
|
3558
|
+
/* @__PURE__ */ jsx("div", { ref: scrollRef, className: "flex-1 overflow-y-auto px-3 py-3", children: !hasConversation ? (
|
|
3626
3559
|
/* ── Initial State ── */
|
|
3627
|
-
/* @__PURE__ */
|
|
3628
|
-
/* @__PURE__ */
|
|
3629
|
-
/* @__PURE__ */
|
|
3560
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
3561
|
+
/* @__PURE__ */ jsxs("div", { className: "text-center pt-4 pb-2", children: [
|
|
3562
|
+
/* @__PURE__ */ jsx("svg", { width: "24", height: "24", viewBox: "0 0 16 16", fill: "none", className: "mx-auto mb-2", children: /* @__PURE__ */ jsx(
|
|
3630
3563
|
"path",
|
|
3631
3564
|
{
|
|
3632
3565
|
d: "M8 1l1.5 3.5L13 6l-3.5 1.5L8 11 6.5 7.5 3 6l3.5-1.5L8 1z",
|
|
@@ -3635,14 +3568,14 @@ ${contextStr}` : prompt,
|
|
|
3635
3568
|
strokeWidth: "0.5"
|
|
3636
3569
|
}
|
|
3637
3570
|
) }),
|
|
3638
|
-
/* @__PURE__ */
|
|
3639
|
-
/* @__PURE__ */
|
|
3571
|
+
/* @__PURE__ */ jsx("p", { className: "text-[13px] font-semibold", style: { color: "#1A1A1A" }, children: "How can I help you today?" }),
|
|
3572
|
+
/* @__PURE__ */ jsx("p", { className: "text-[11px] mt-0.5", style: { color: "#999" }, children: "Ask me about your A/B test." })
|
|
3640
3573
|
] }),
|
|
3641
|
-
/* @__PURE__ */
|
|
3642
|
-
/* @__PURE__ */
|
|
3643
|
-
/* @__PURE__ */
|
|
3574
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
3575
|
+
/* @__PURE__ */ jsx("p", { className: "text-[10px] font-medium uppercase tracking-wider mb-1.5", style: { color: "#999" }, children: "Quick Suggestions" }),
|
|
3576
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-1", children: suggestionsGroup.map((action) => {
|
|
3644
3577
|
const disabled = action.requiresSelection && !selectedElement;
|
|
3645
|
-
return /* @__PURE__ */
|
|
3578
|
+
return /* @__PURE__ */ jsxs(
|
|
3646
3579
|
"button",
|
|
3647
3580
|
{
|
|
3648
3581
|
onClick: () => !disabled && handleSend(buildQuickActionPrompt(action)),
|
|
@@ -3661,18 +3594,18 @@ ${contextStr}` : prompt,
|
|
|
3661
3594
|
e.currentTarget.style.backgroundColor = "";
|
|
3662
3595
|
},
|
|
3663
3596
|
children: [
|
|
3664
|
-
/* @__PURE__ */
|
|
3665
|
-
/* @__PURE__ */
|
|
3666
|
-
disabled && /* @__PURE__ */
|
|
3597
|
+
/* @__PURE__ */ jsx("span", { className: "text-[12px] shrink-0", style: { color: disabled ? "#CCC" : "#8B5CF6" }, children: action.icon }),
|
|
3598
|
+
/* @__PURE__ */ jsx("span", { children: action.label }),
|
|
3599
|
+
disabled && /* @__PURE__ */ jsx("span", { className: "ml-auto text-[9px]", style: { color: "#CCC" }, children: "Select element" })
|
|
3667
3600
|
]
|
|
3668
3601
|
},
|
|
3669
3602
|
action.label
|
|
3670
3603
|
);
|
|
3671
3604
|
}) })
|
|
3672
3605
|
] }),
|
|
3673
|
-
/* @__PURE__ */
|
|
3674
|
-
/* @__PURE__ */
|
|
3675
|
-
/* @__PURE__ */
|
|
3606
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
3607
|
+
/* @__PURE__ */ jsx("p", { className: "text-[10px] font-medium uppercase tracking-wider mb-1.5", style: { color: "#999" }, children: "Deep Analysis" }),
|
|
3608
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-1", children: analysisGroup.map((action) => /* @__PURE__ */ jsxs(
|
|
3676
3609
|
"button",
|
|
3677
3610
|
{
|
|
3678
3611
|
onClick: () => handleSend(buildQuickActionPrompt(action)),
|
|
@@ -3685,8 +3618,8 @@ ${contextStr}` : prompt,
|
|
|
3685
3618
|
e.currentTarget.style.backgroundColor = "";
|
|
3686
3619
|
},
|
|
3687
3620
|
children: [
|
|
3688
|
-
/* @__PURE__ */
|
|
3689
|
-
/* @__PURE__ */
|
|
3621
|
+
/* @__PURE__ */ jsx("span", { className: "text-[12px] shrink-0", children: action.icon }),
|
|
3622
|
+
/* @__PURE__ */ jsx("span", { children: action.label })
|
|
3690
3623
|
]
|
|
3691
3624
|
},
|
|
3692
3625
|
action.label
|
|
@@ -3695,9 +3628,9 @@ ${contextStr}` : prompt,
|
|
|
3695
3628
|
] })
|
|
3696
3629
|
) : (
|
|
3697
3630
|
/* ── Conversation ── */
|
|
3698
|
-
/* @__PURE__ */
|
|
3631
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-3", children: messages.map((msg) => /* @__PURE__ */ jsx("div", { children: msg.role === "user" ? (
|
|
3699
3632
|
/* User message */
|
|
3700
|
-
/* @__PURE__ */
|
|
3633
|
+
/* @__PURE__ */ jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsx(
|
|
3701
3634
|
"div",
|
|
3702
3635
|
{
|
|
3703
3636
|
className: "rounded-lg px-3 py-2 max-w-[90%] text-[11px] leading-relaxed",
|
|
@@ -3707,17 +3640,17 @@ ${contextStr}` : prompt,
|
|
|
3707
3640
|
) })
|
|
3708
3641
|
) : (
|
|
3709
3642
|
/* Assistant message */
|
|
3710
|
-
/* @__PURE__ */
|
|
3711
|
-
msg.isLoading && /* @__PURE__ */
|
|
3712
|
-
msg.error && /* @__PURE__ */
|
|
3713
|
-
/* @__PURE__ */
|
|
3714
|
-
/* @__PURE__ */
|
|
3643
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
3644
|
+
msg.isLoading && /* @__PURE__ */ jsx(ThinkingIndicator, { startTime: loadingStartTime }),
|
|
3645
|
+
msg.error && /* @__PURE__ */ jsxs("div", { className: "rounded-lg p-2.5", style: { backgroundColor: "#FEF2F2", border: "1px solid #FECACA" }, children: [
|
|
3646
|
+
/* @__PURE__ */ jsx("p", { className: "text-[11px] font-medium", style: { color: "#B91C1C" }, children: "Something went wrong" }),
|
|
3647
|
+
/* @__PURE__ */ jsx("p", { className: "text-[10px] mt-0.5", style: { color: "#EF4444" }, children: msg.error })
|
|
3715
3648
|
] }),
|
|
3716
|
-
!msg.isLoading && !msg.error && msg.content && /* @__PURE__ */
|
|
3649
|
+
!msg.isLoading && !msg.error && msg.content && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3717
3650
|
parseResponseContent(msg.content).map((part, partIdx) => {
|
|
3718
3651
|
if (part.type === "suggestion" && part.suggestion) {
|
|
3719
3652
|
const isApplied = msg.appliedSuggestions?.has(partIdx) || false;
|
|
3720
|
-
return /* @__PURE__ */
|
|
3653
|
+
return /* @__PURE__ */ jsx(
|
|
3721
3654
|
SuggestionCard,
|
|
3722
3655
|
{
|
|
3723
3656
|
suggestion: part.suggestion,
|
|
@@ -3728,7 +3661,7 @@ ${contextStr}` : prompt,
|
|
|
3728
3661
|
partIdx
|
|
3729
3662
|
);
|
|
3730
3663
|
}
|
|
3731
|
-
return /* @__PURE__ */
|
|
3664
|
+
return /* @__PURE__ */ jsx(
|
|
3732
3665
|
"div",
|
|
3733
3666
|
{
|
|
3734
3667
|
className: "text-[11px] leading-relaxed",
|
|
@@ -3738,7 +3671,7 @@ ${contextStr}` : prompt,
|
|
|
3738
3671
|
partIdx
|
|
3739
3672
|
);
|
|
3740
3673
|
}),
|
|
3741
|
-
/* @__PURE__ */
|
|
3674
|
+
/* @__PURE__ */ jsx(
|
|
3742
3675
|
ResponseActions,
|
|
3743
3676
|
{
|
|
3744
3677
|
content: msg.content,
|
|
@@ -3749,18 +3682,18 @@ ${contextStr}` : prompt,
|
|
|
3749
3682
|
] })
|
|
3750
3683
|
) }, msg.id)) })
|
|
3751
3684
|
) }),
|
|
3752
|
-
/* @__PURE__ */
|
|
3753
|
-
/* @__PURE__ */
|
|
3685
|
+
/* @__PURE__ */ jsx("div", { className: "px-3 pb-3 pt-2 shrink-0", style: { borderTop: "1px solid #E5E5E5" }, children: /* @__PURE__ */ jsxs("div", { className: "flex items-end gap-1.5", children: [
|
|
3686
|
+
/* @__PURE__ */ jsx(
|
|
3754
3687
|
"button",
|
|
3755
3688
|
{
|
|
3756
3689
|
disabled: true,
|
|
3757
3690
|
className: "w-7 h-7 shrink-0 rounded flex items-center justify-center transition-colors",
|
|
3758
3691
|
style: { color: "#CCC" },
|
|
3759
3692
|
title: "Attachments coming soon",
|
|
3760
|
-
children: /* @__PURE__ */
|
|
3693
|
+
children: /* @__PURE__ */ jsx("svg", { width: "14", height: "14", viewBox: "0 0 16 16", fill: "none", children: /* @__PURE__ */ jsx("path", { d: "M8 3v10M3 8h10", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }) })
|
|
3761
3694
|
}
|
|
3762
3695
|
),
|
|
3763
|
-
/* @__PURE__ */
|
|
3696
|
+
/* @__PURE__ */ jsx(
|
|
3764
3697
|
"textarea",
|
|
3765
3698
|
{
|
|
3766
3699
|
ref: inputRef,
|
|
@@ -3779,24 +3712,21 @@ ${contextStr}` : prompt,
|
|
|
3779
3712
|
}
|
|
3780
3713
|
}
|
|
3781
3714
|
),
|
|
3782
|
-
/* @__PURE__ */
|
|
3715
|
+
/* @__PURE__ */ jsx(
|
|
3783
3716
|
"button",
|
|
3784
3717
|
{
|
|
3785
3718
|
onClick: () => handleSend(),
|
|
3786
3719
|
disabled: isLoading || !input.trim(),
|
|
3787
3720
|
className: "w-7 h-7 shrink-0 rounded-lg flex items-center justify-center text-white transition-colors disabled:opacity-30",
|
|
3788
3721
|
style: { backgroundColor: "#8B5CF6" },
|
|
3789
|
-
children: /* @__PURE__ */
|
|
3722
|
+
children: /* @__PURE__ */ jsx("svg", { width: "14", height: "14", viewBox: "0 0 16 16", fill: "none", children: /* @__PURE__ */ jsx("path", { d: "M3 13l10-5L3 3v4l6 1-6 1v4z", fill: "currentColor" }) })
|
|
3790
3723
|
}
|
|
3791
3724
|
)
|
|
3792
3725
|
] }) })
|
|
3793
3726
|
] });
|
|
3794
3727
|
}
|
|
3795
|
-
|
|
3796
|
-
// src/components/RightPanel.tsx
|
|
3797
|
-
import { Fragment as Fragment5, jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
3798
3728
|
function RightPanel({ codeEditorOpen, experimentId }) {
|
|
3799
|
-
const [tab, setTab] =
|
|
3729
|
+
const [tab, setTab] = useState("design");
|
|
3800
3730
|
const selectedElement = useMutationsStore((s) => s.selectedElement);
|
|
3801
3731
|
const variations = useVariationsStore((s) => s.variations);
|
|
3802
3732
|
const activeVariationId = useVariationsStore((s) => s.activeVariationId);
|
|
@@ -3804,8 +3734,8 @@ function RightPanel({ codeEditorOpen, experimentId }) {
|
|
|
3804
3734
|
const clearActiveMutations = useVariationsStore((s) => s.clearActiveMutations);
|
|
3805
3735
|
const activeVariation = variations.find((v) => v.id === activeVariationId);
|
|
3806
3736
|
const mutations = activeVariation?.mutations || [];
|
|
3807
|
-
const historyRef =
|
|
3808
|
-
const handleUndo =
|
|
3737
|
+
const historyRef = useRef(null);
|
|
3738
|
+
const handleUndo = useCallback(() => {
|
|
3809
3739
|
removeLastMutationFromActive();
|
|
3810
3740
|
sendToBridge({ type: "clearAllMutations" });
|
|
3811
3741
|
const remaining = useVariationsStore.getState().getActiveMutations();
|
|
@@ -3813,7 +3743,7 @@ function RightPanel({ codeEditorOpen, experimentId }) {
|
|
|
3813
3743
|
sendToBridge({ type: "applyMutationBatch", mutations: remaining });
|
|
3814
3744
|
}
|
|
3815
3745
|
}, [removeLastMutationFromActive]);
|
|
3816
|
-
const handleClearAll =
|
|
3746
|
+
const handleClearAll = useCallback(() => {
|
|
3817
3747
|
clearActiveMutations();
|
|
3818
3748
|
sendToBridge({ type: "clearAllMutations" });
|
|
3819
3749
|
}, [clearActiveMutations]);
|
|
@@ -3822,13 +3752,13 @@ function RightPanel({ codeEditorOpen, experimentId }) {
|
|
|
3822
3752
|
{ id: "history", label: "History" },
|
|
3823
3753
|
{ id: "ai", label: "AI" }
|
|
3824
3754
|
];
|
|
3825
|
-
|
|
3755
|
+
useEffect(() => {
|
|
3826
3756
|
if (tab === "history" && historyRef.current) {
|
|
3827
3757
|
historyRef.current.scrollTop = 0;
|
|
3828
3758
|
}
|
|
3829
3759
|
}, [mutations.length, tab]);
|
|
3830
|
-
return /* @__PURE__ */
|
|
3831
|
-
/* @__PURE__ */
|
|
3760
|
+
return /* @__PURE__ */ jsxs("div", { className: "bg-white flex flex-col h-full overflow-hidden shrink-0", style: { width: 288, borderLeft: "1px solid var(--color-border)" }, children: [
|
|
3761
|
+
/* @__PURE__ */ jsx("div", { className: "flex shrink-0", style: { height: 40, borderBottom: "1px solid #E5E5E5" }, children: tabs.map((t) => /* @__PURE__ */ jsxs(
|
|
3832
3762
|
"button",
|
|
3833
3763
|
{
|
|
3834
3764
|
onClick: () => setTab(t.id),
|
|
@@ -3838,8 +3768,8 @@ function RightPanel({ codeEditorOpen, experimentId }) {
|
|
|
3838
3768
|
color: tab === t.id ? "#3B82F6" : "#666666"
|
|
3839
3769
|
},
|
|
3840
3770
|
children: [
|
|
3841
|
-
t.id === "ai" ? /* @__PURE__ */
|
|
3842
|
-
/* @__PURE__ */
|
|
3771
|
+
t.id === "ai" ? /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1", children: [
|
|
3772
|
+
/* @__PURE__ */ jsx("svg", { width: "11", height: "11", viewBox: "0 0 16 16", fill: "none", children: /* @__PURE__ */ jsx(
|
|
3843
3773
|
"path",
|
|
3844
3774
|
{
|
|
3845
3775
|
d: "M8 1l1.5 3.5L13 6l-3.5 1.5L8 11 6.5 7.5 3 6l3.5-1.5L8 1z",
|
|
@@ -3850,15 +3780,15 @@ function RightPanel({ codeEditorOpen, experimentId }) {
|
|
|
3850
3780
|
) }),
|
|
3851
3781
|
t.label
|
|
3852
3782
|
] }) : t.label,
|
|
3853
|
-
tab === t.id && /* @__PURE__ */
|
|
3783
|
+
tab === t.id && /* @__PURE__ */ jsx("div", { className: "absolute bottom-0 left-0 right-0 h-0.5", style: { backgroundColor: "#3B82F6" } })
|
|
3854
3784
|
]
|
|
3855
3785
|
},
|
|
3856
3786
|
t.id
|
|
3857
3787
|
)) }),
|
|
3858
|
-
/* @__PURE__ */
|
|
3859
|
-
/* @__PURE__ */
|
|
3860
|
-
tab === "design" && (selectedElement ? /* @__PURE__ */
|
|
3861
|
-
tab === "history" && /* @__PURE__ */
|
|
3788
|
+
/* @__PURE__ */ jsxs("div", { className: `flex-1 min-h-0 flex flex-col ${codeEditorOpen ? "" : "overflow-y-auto"}`, children: [
|
|
3789
|
+
/* @__PURE__ */ jsxs("div", { className: codeEditorOpen ? "flex-1 min-h-0 overflow-y-auto" : "flex-1 min-h-0 flex flex-col", children: [
|
|
3790
|
+
tab === "design" && (selectedElement ? /* @__PURE__ */ jsx(EditPanel, {}) : /* @__PURE__ */ jsx(EmptyDesignState, {})),
|
|
3791
|
+
tab === "history" && /* @__PURE__ */ jsx(
|
|
3862
3792
|
HistoryTab,
|
|
3863
3793
|
{
|
|
3864
3794
|
mutations,
|
|
@@ -3868,30 +3798,30 @@ function RightPanel({ codeEditorOpen, experimentId }) {
|
|
|
3868
3798
|
experimentId
|
|
3869
3799
|
}
|
|
3870
3800
|
),
|
|
3871
|
-
tab === "ai" && /* @__PURE__ */
|
|
3801
|
+
tab === "ai" && /* @__PURE__ */ jsx(AIChatPanel, {})
|
|
3872
3802
|
] }),
|
|
3873
|
-
codeEditorOpen && /* @__PURE__ */
|
|
3803
|
+
codeEditorOpen && /* @__PURE__ */ jsx("div", { className: "flex-1 min-h-0", children: /* @__PURE__ */ jsx(CodeEditorPanel, {}) })
|
|
3874
3804
|
] })
|
|
3875
3805
|
] });
|
|
3876
3806
|
}
|
|
3877
3807
|
function EmptyDesignState() {
|
|
3878
|
-
return /* @__PURE__ */
|
|
3879
|
-
/* @__PURE__ */
|
|
3808
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center h-full px-6 text-center", children: [
|
|
3809
|
+
/* @__PURE__ */ jsx(
|
|
3880
3810
|
"div",
|
|
3881
3811
|
{
|
|
3882
3812
|
className: "w-14 h-14 rounded-full flex items-center justify-center mb-4",
|
|
3883
3813
|
style: { border: "2px dashed var(--color-accent-blue-dashed)", color: "var(--color-accent-blue-hover)" },
|
|
3884
|
-
children: /* @__PURE__ */
|
|
3885
|
-
/* @__PURE__ */
|
|
3886
|
-
/* @__PURE__ */
|
|
3814
|
+
children: /* @__PURE__ */ jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", children: [
|
|
3815
|
+
/* @__PURE__ */ jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2", stroke: "currentColor", strokeWidth: "1.5" }),
|
|
3816
|
+
/* @__PURE__ */ jsx("path", { d: "M3 9h18M9 9v12", stroke: "currentColor", strokeWidth: "1.5" })
|
|
3887
3817
|
] })
|
|
3888
3818
|
}
|
|
3889
3819
|
),
|
|
3890
|
-
/* @__PURE__ */
|
|
3891
|
-
/* @__PURE__ */
|
|
3820
|
+
/* @__PURE__ */ jsx("h3", { className: "text-[14px] font-semibold mb-2", style: { color: "var(--color-text-primary)" }, children: "Start Creating" }),
|
|
3821
|
+
/* @__PURE__ */ jsxs("p", { className: "text-[12px] leading-relaxed", style: { color: "var(--color-text-muted)" }, children: [
|
|
3892
3822
|
"Click any element on the site to edit, or click",
|
|
3893
3823
|
" ",
|
|
3894
|
-
/* @__PURE__ */
|
|
3824
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium", style: { color: "var(--color-text-secondary)" }, children: '"Add Element"' }),
|
|
3895
3825
|
" ",
|
|
3896
3826
|
"in the left panel."
|
|
3897
3827
|
] })
|
|
@@ -3904,8 +3834,8 @@ function HistoryTab({
|
|
|
3904
3834
|
onClearAll,
|
|
3905
3835
|
experimentId
|
|
3906
3836
|
}) {
|
|
3907
|
-
const [savedVersions, setSavedVersions] =
|
|
3908
|
-
const loadSavedVersions =
|
|
3837
|
+
const [savedVersions, setSavedVersions] = useState([]);
|
|
3838
|
+
const loadSavedVersions = useCallback(() => {
|
|
3909
3839
|
if (!experimentId) return;
|
|
3910
3840
|
try {
|
|
3911
3841
|
const raw = localStorage.getItem("conversion-version-history-" + experimentId);
|
|
@@ -3914,7 +3844,7 @@ function HistoryTab({
|
|
|
3914
3844
|
setSavedVersions([]);
|
|
3915
3845
|
}
|
|
3916
3846
|
}, [experimentId]);
|
|
3917
|
-
|
|
3847
|
+
useEffect(() => {
|
|
3918
3848
|
loadSavedVersions();
|
|
3919
3849
|
const handler = () => loadSavedVersions();
|
|
3920
3850
|
window.addEventListener("version-history-updated", handler);
|
|
@@ -3981,20 +3911,20 @@ function HistoryTab({
|
|
|
3981
3911
|
const d = new Date(ts);
|
|
3982
3912
|
return d.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", second: "2-digit" });
|
|
3983
3913
|
}
|
|
3984
|
-
return /* @__PURE__ */
|
|
3985
|
-
/* @__PURE__ */
|
|
3986
|
-
mutations.length > 0 && /* @__PURE__ */
|
|
3987
|
-
/* @__PURE__ */
|
|
3988
|
-
/* @__PURE__ */
|
|
3914
|
+
return /* @__PURE__ */ jsxs("div", { ref: historyRef, className: "flex flex-col h-full", children: [
|
|
3915
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto p-3", children: [
|
|
3916
|
+
mutations.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3917
|
+
/* @__PURE__ */ jsx("p", { className: "text-[11px] font-medium uppercase tracking-wide mb-2", style: { color: "#999999" }, children: "Current Session" }),
|
|
3918
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-1 mb-4", children: [...mutations].reverse().map((mut, i) => /* @__PURE__ */ jsxs(
|
|
3989
3919
|
"div",
|
|
3990
3920
|
{
|
|
3991
3921
|
className: "flex items-start gap-2 px-2.5 py-2 rounded-md hover:bg-[#F9FAFB] transition-colors group",
|
|
3992
3922
|
children: [
|
|
3993
|
-
/* @__PURE__ */
|
|
3994
|
-
/* @__PURE__ */
|
|
3995
|
-
/* @__PURE__ */
|
|
3923
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
3924
|
+
/* @__PURE__ */ jsx("p", { className: "text-[12px] leading-snug", style: { color: "#1A1A1A" }, children: describeAction(mut) }),
|
|
3925
|
+
/* @__PURE__ */ jsx("p", { className: "text-[11px] mt-0.5", style: { color: "#999999" }, children: formatTime(mut.timestamp) })
|
|
3996
3926
|
] }),
|
|
3997
|
-
i === 0 && /* @__PURE__ */
|
|
3927
|
+
i === 0 && /* @__PURE__ */ jsx(
|
|
3998
3928
|
"button",
|
|
3999
3929
|
{
|
|
4000
3930
|
onClick: onUndo,
|
|
@@ -4008,23 +3938,23 @@ function HistoryTab({
|
|
|
4008
3938
|
`${mut.id}-${i}`
|
|
4009
3939
|
)) })
|
|
4010
3940
|
] }),
|
|
4011
|
-
experimentId && /* @__PURE__ */
|
|
4012
|
-
/* @__PURE__ */
|
|
4013
|
-
savedVersions.length === 0 ? /* @__PURE__ */
|
|
4014
|
-
savedVersions.map((version) => /* @__PURE__ */
|
|
3941
|
+
experimentId && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3942
|
+
/* @__PURE__ */ jsx("p", { className: "text-[11px] font-medium uppercase tracking-wide mb-2", style: { color: "#999999" }, children: "Saved Versions" }),
|
|
3943
|
+
savedVersions.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-[12px] text-center py-3", style: { color: "#999999" }, children: "No versions saved yet. Versions are created automatically when you save." }) : /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
3944
|
+
savedVersions.map((version) => /* @__PURE__ */ jsxs(
|
|
4015
3945
|
"div",
|
|
4016
3946
|
{
|
|
4017
3947
|
className: "flex items-start gap-2 px-2.5 py-2 rounded-md hover:bg-[#F9FAFB] transition-colors group",
|
|
4018
3948
|
style: { border: "1px solid #F0F0F0" },
|
|
4019
3949
|
children: [
|
|
4020
|
-
/* @__PURE__ */
|
|
4021
|
-
/* @__PURE__ */
|
|
4022
|
-
/* @__PURE__ */
|
|
3950
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
3951
|
+
/* @__PURE__ */ jsx("p", { className: "text-[12px] font-medium", style: { color: "#1A1A1A" }, children: formatVersionTime(version.timestamp) }),
|
|
3952
|
+
/* @__PURE__ */ jsxs("p", { className: "text-[11px] mt-0.5", style: { color: "#999999" }, children: [
|
|
4023
3953
|
sourceLabel(version.source),
|
|
4024
3954
|
version.variations?.length > 0 && ` \xB7 ${version.variations.filter((v) => v.csscode || v.jscode || v.changesets && v.changesets !== "[]").length} variation(s)`
|
|
4025
3955
|
] })
|
|
4026
3956
|
] }),
|
|
4027
|
-
/* @__PURE__ */
|
|
3957
|
+
/* @__PURE__ */ jsx(
|
|
4028
3958
|
"button",
|
|
4029
3959
|
{
|
|
4030
3960
|
onClick: () => handleRestoreVersion(version),
|
|
@@ -4038,7 +3968,7 @@ function HistoryTab({
|
|
|
4038
3968
|
},
|
|
4039
3969
|
version.id
|
|
4040
3970
|
)),
|
|
4041
|
-
/* @__PURE__ */
|
|
3971
|
+
/* @__PURE__ */ jsx(
|
|
4042
3972
|
"button",
|
|
4043
3973
|
{
|
|
4044
3974
|
onClick: handleClearVersions,
|
|
@@ -4049,9 +3979,9 @@ function HistoryTab({
|
|
|
4049
3979
|
)
|
|
4050
3980
|
] })
|
|
4051
3981
|
] }),
|
|
4052
|
-
mutations.length === 0 && savedVersions.length === 0 && /* @__PURE__ */
|
|
3982
|
+
mutations.length === 0 && savedVersions.length === 0 && /* @__PURE__ */ jsx("div", { className: "text-center pt-8", children: /* @__PURE__ */ jsx("p", { className: "text-[12px]", style: { color: "#999999" }, children: "No changes recorded yet. Select and edit elements to see your history here." }) })
|
|
4053
3983
|
] }),
|
|
4054
|
-
mutations.length > 0 && /* @__PURE__ */
|
|
3984
|
+
mutations.length > 0 && /* @__PURE__ */ jsx("div", { className: "p-3 shrink-0", style: { borderTop: "1px solid #E5E5E5" }, children: /* @__PURE__ */ jsx(
|
|
4055
3985
|
"button",
|
|
4056
3986
|
{
|
|
4057
3987
|
onClick: onClearAll,
|
|
@@ -4062,32 +3992,28 @@ function HistoryTab({
|
|
|
4062
3992
|
) })
|
|
4063
3993
|
] });
|
|
4064
3994
|
}
|
|
4065
|
-
|
|
4066
|
-
// src/components/Toast.tsx
|
|
4067
|
-
import { useState as useState11, useCallback as useCallback12, useEffect as useEffect10, createContext, useContext } from "react";
|
|
4068
|
-
import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
4069
3995
|
var ToastContext = createContext({ toast: () => {
|
|
4070
3996
|
} });
|
|
4071
3997
|
function useToast() {
|
|
4072
3998
|
return useContext(ToastContext);
|
|
4073
3999
|
}
|
|
4074
4000
|
function ToastProvider({ children }) {
|
|
4075
|
-
const [messages, setMessages] =
|
|
4076
|
-
const toast =
|
|
4001
|
+
const [messages, setMessages] = useState([]);
|
|
4002
|
+
const toast = useCallback((text, type = "info") => {
|
|
4077
4003
|
const id = `toast_${Date.now()}`;
|
|
4078
4004
|
setMessages((prev) => [...prev, { id, text, type }]);
|
|
4079
4005
|
setTimeout(() => {
|
|
4080
4006
|
setMessages((prev) => prev.filter((m) => m.id !== id));
|
|
4081
4007
|
}, 3e3);
|
|
4082
4008
|
}, []);
|
|
4083
|
-
return /* @__PURE__ */
|
|
4009
|
+
return /* @__PURE__ */ jsxs(ToastContext.Provider, { value: { toast }, children: [
|
|
4084
4010
|
children,
|
|
4085
|
-
messages.length > 0 && /* @__PURE__ */
|
|
4011
|
+
messages.length > 0 && /* @__PURE__ */ jsx("div", { className: "fixed bottom-20 right-4 z-[60] flex flex-col gap-2", children: messages.map((msg) => /* @__PURE__ */ jsx(ToastItem, { message: msg, onDismiss: () => setMessages((prev) => prev.filter((m) => m.id !== msg.id)) }, msg.id)) })
|
|
4086
4012
|
] });
|
|
4087
4013
|
}
|
|
4088
4014
|
function ToastItem({ message, onDismiss }) {
|
|
4089
|
-
const [visible, setVisible] =
|
|
4090
|
-
|
|
4015
|
+
const [visible, setVisible] = useState(false);
|
|
4016
|
+
useEffect(() => {
|
|
4091
4017
|
requestAnimationFrame(() => setVisible(true));
|
|
4092
4018
|
const timer = setTimeout(() => setVisible(false), 2700);
|
|
4093
4019
|
return () => clearTimeout(timer);
|
|
@@ -4098,7 +4024,7 @@ function ToastItem({ message, onDismiss }) {
|
|
|
4098
4024
|
warning: { bg: "#92400E", text: "#FFFFFF" }
|
|
4099
4025
|
};
|
|
4100
4026
|
const c = colors[message.type];
|
|
4101
|
-
return /* @__PURE__ */
|
|
4027
|
+
return /* @__PURE__ */ jsx(
|
|
4102
4028
|
"div",
|
|
4103
4029
|
{
|
|
4104
4030
|
onClick: onDismiss,
|
|
@@ -4118,9 +4044,6 @@ function ToastItem({ message, onDismiss }) {
|
|
|
4118
4044
|
}
|
|
4119
4045
|
);
|
|
4120
4046
|
}
|
|
4121
|
-
|
|
4122
|
-
// src/components/EditorShell.tsx
|
|
4123
|
-
import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
4124
4047
|
var PING_INTERVAL = 3e3;
|
|
4125
4048
|
var PONG_TIMEOUT = 1500;
|
|
4126
4049
|
var PLATFORM_CHANNEL = "conversion-platform";
|
|
@@ -4137,22 +4060,22 @@ function sendToPlatform(type, payload) {
|
|
|
4137
4060
|
}));
|
|
4138
4061
|
}
|
|
4139
4062
|
function EditorShell({ initialExperiment, embeddedMode, proxyBaseUrl }) {
|
|
4140
|
-
const [url, setUrl] =
|
|
4141
|
-
const [password, setPassword] =
|
|
4142
|
-
const [connectionStatus, setConnectionStatus] =
|
|
4143
|
-
const [viewport, setViewport] =
|
|
4144
|
-
const [codeEditorOpen, setCodeEditorOpen] =
|
|
4145
|
-
const [interactionMode, setInteractionMode] =
|
|
4146
|
-
const [embedded, setEmbedded] =
|
|
4063
|
+
const [url, setUrl] = useState("");
|
|
4064
|
+
const [password, setPassword] = useState("");
|
|
4065
|
+
const [connectionStatus, setConnectionStatus] = useState("disconnected");
|
|
4066
|
+
const [viewport, setViewport] = useState("desktop");
|
|
4067
|
+
const [codeEditorOpen, setCodeEditorOpen] = useState(false);
|
|
4068
|
+
const [interactionMode, setInteractionMode] = useState("edit");
|
|
4069
|
+
const [embedded, setEmbedded] = useState(
|
|
4147
4070
|
typeof embeddedMode === "boolean" ? embeddedMode : isEmbeddedMode
|
|
4148
4071
|
);
|
|
4149
|
-
const [experimentData, setExperimentData] =
|
|
4150
|
-
const experimentDataRef =
|
|
4151
|
-
const lastInitialExperimentKeyRef =
|
|
4152
|
-
|
|
4072
|
+
const [experimentData, setExperimentData] = useState(null);
|
|
4073
|
+
const experimentDataRef = useRef(null);
|
|
4074
|
+
const lastInitialExperimentKeyRef = useRef("");
|
|
4075
|
+
useEffect(() => {
|
|
4153
4076
|
experimentDataRef.current = experimentData;
|
|
4154
4077
|
}, [experimentData]);
|
|
4155
|
-
|
|
4078
|
+
useEffect(() => {
|
|
4156
4079
|
window.__CONVERSION_EXPERIMENT_DATA__ = experimentData ? {
|
|
4157
4080
|
name: experimentData.name,
|
|
4158
4081
|
pageUrl: experimentData.pageUrl || url,
|
|
@@ -4173,14 +4096,14 @@ function EditorShell({ initialExperiment, embeddedMode, proxyBaseUrl }) {
|
|
|
4173
4096
|
const setActiveVariation = useVariationsStore((s) => s.setActiveVariation);
|
|
4174
4097
|
const loadExperimentVariations = useVariationsStore((s) => s.loadExperimentVariations);
|
|
4175
4098
|
const { toast } = useToast();
|
|
4176
|
-
|
|
4099
|
+
useEffect(() => {
|
|
4177
4100
|
if (typeof embeddedMode === "boolean") {
|
|
4178
4101
|
setEmbedded(embeddedMode);
|
|
4179
4102
|
return;
|
|
4180
4103
|
}
|
|
4181
4104
|
setEmbedded(isEmbeddedMode());
|
|
4182
4105
|
}, [embeddedMode]);
|
|
4183
|
-
const buildPlatformVariations =
|
|
4106
|
+
const buildPlatformVariations = useCallback(() => {
|
|
4184
4107
|
const currentVariations = useVariationsStore.getState().variations;
|
|
4185
4108
|
return (experimentData?.variations || []).map((pv) => {
|
|
4186
4109
|
const editorVar = currentVariations.find(
|
|
@@ -4198,7 +4121,7 @@ function EditorShell({ initialExperiment, embeddedMode, proxyBaseUrl }) {
|
|
|
4198
4121
|
};
|
|
4199
4122
|
});
|
|
4200
4123
|
}, [experimentData]);
|
|
4201
|
-
const handleSave =
|
|
4124
|
+
const handleSave = useCallback(() => {
|
|
4202
4125
|
if (!embedded || !experimentData) return;
|
|
4203
4126
|
sendToPlatform("save-experiment", {
|
|
4204
4127
|
experimentId: experimentData?.experimentId,
|
|
@@ -4206,7 +4129,7 @@ function EditorShell({ initialExperiment, embeddedMode, proxyBaseUrl }) {
|
|
|
4206
4129
|
});
|
|
4207
4130
|
toast("Saving...", "info");
|
|
4208
4131
|
}, [embedded, experimentData, toast, buildPlatformVariations]);
|
|
4209
|
-
const handleFinalize =
|
|
4132
|
+
const handleFinalize = useCallback(() => {
|
|
4210
4133
|
if (!embedded || !experimentData) return;
|
|
4211
4134
|
sendToPlatform("save-and-navigate", {
|
|
4212
4135
|
experimentId: experimentData?.experimentId,
|
|
@@ -4215,10 +4138,10 @@ function EditorShell({ initialExperiment, embeddedMode, proxyBaseUrl }) {
|
|
|
4215
4138
|
});
|
|
4216
4139
|
toast("Saving and continuing...", "info");
|
|
4217
4140
|
}, [embedded, experimentData, toast, buildPlatformVariations]);
|
|
4218
|
-
const pingIntervalRef =
|
|
4219
|
-
const pongTimeoutRef =
|
|
4220
|
-
const syncDebounceRef =
|
|
4221
|
-
const stopHeartbeat =
|
|
4141
|
+
const pingIntervalRef = useRef(null);
|
|
4142
|
+
const pongTimeoutRef = useRef(null);
|
|
4143
|
+
const syncDebounceRef = useRef(null);
|
|
4144
|
+
const stopHeartbeat = useCallback(() => {
|
|
4222
4145
|
if (pingIntervalRef.current) {
|
|
4223
4146
|
clearInterval(pingIntervalRef.current);
|
|
4224
4147
|
pingIntervalRef.current = null;
|
|
@@ -4228,10 +4151,10 @@ function EditorShell({ initialExperiment, embeddedMode, proxyBaseUrl }) {
|
|
|
4228
4151
|
pongTimeoutRef.current = null;
|
|
4229
4152
|
}
|
|
4230
4153
|
}, []);
|
|
4231
|
-
const startHeartbeat =
|
|
4154
|
+
const startHeartbeat = useCallback(() => {
|
|
4232
4155
|
stopHeartbeat();
|
|
4233
4156
|
const sendPing = () => {
|
|
4234
|
-
import(
|
|
4157
|
+
import('./bridge-channel-ISTPKGMY.js').then(({ sendToBridge: sendToBridge2 }) => {
|
|
4235
4158
|
sendToBridge2({ type: "ping" });
|
|
4236
4159
|
});
|
|
4237
4160
|
pongTimeoutRef.current = setTimeout(() => {
|
|
@@ -4240,7 +4163,7 @@ function EditorShell({ initialExperiment, embeddedMode, proxyBaseUrl }) {
|
|
|
4240
4163
|
};
|
|
4241
4164
|
pingIntervalRef.current = setInterval(sendPing, PING_INTERVAL);
|
|
4242
4165
|
}, [stopHeartbeat]);
|
|
4243
|
-
const handleLoadUrl =
|
|
4166
|
+
const handleLoadUrl = useCallback(
|
|
4244
4167
|
(newUrl, newPassword) => {
|
|
4245
4168
|
stopHeartbeat();
|
|
4246
4169
|
clearAll();
|
|
@@ -4251,7 +4174,7 @@ function EditorShell({ initialExperiment, embeddedMode, proxyBaseUrl }) {
|
|
|
4251
4174
|
},
|
|
4252
4175
|
[stopHeartbeat, clearAll, setSelectedElement]
|
|
4253
4176
|
);
|
|
4254
|
-
|
|
4177
|
+
useEffect(() => {
|
|
4255
4178
|
if (!initialExperiment) return;
|
|
4256
4179
|
const key = JSON.stringify(initialExperiment);
|
|
4257
4180
|
if (lastInitialExperimentKeyRef.current === key) return;
|
|
@@ -4281,7 +4204,7 @@ function EditorShell({ initialExperiment, embeddedMode, proxyBaseUrl }) {
|
|
|
4281
4204
|
});
|
|
4282
4205
|
loadExperimentVariations(editorVariations);
|
|
4283
4206
|
}, [initialExperiment, handleLoadUrl, loadExperimentVariations]);
|
|
4284
|
-
const handleBridgeReady =
|
|
4207
|
+
const handleBridgeReady = useCallback(() => {
|
|
4285
4208
|
setConnectionStatus("connected");
|
|
4286
4209
|
startHeartbeat();
|
|
4287
4210
|
const { variations: currentVariations, activeVariationId: currentActiveId } = useVariationsStore.getState();
|
|
@@ -4290,18 +4213,18 @@ function EditorShell({ initialExperiment, embeddedMode, proxyBaseUrl }) {
|
|
|
4290
4213
|
sendToBridge({ type: "applyMutationBatch", mutations: active.mutations });
|
|
4291
4214
|
}
|
|
4292
4215
|
}, [startHeartbeat]);
|
|
4293
|
-
const handlePong =
|
|
4216
|
+
const handlePong = useCallback(() => {
|
|
4294
4217
|
if (pongTimeoutRef.current) {
|
|
4295
4218
|
clearTimeout(pongTimeoutRef.current);
|
|
4296
4219
|
pongTimeoutRef.current = null;
|
|
4297
4220
|
}
|
|
4298
4221
|
setConnectionStatus((prev) => prev === "lost" ? "connected" : prev);
|
|
4299
4222
|
}, []);
|
|
4300
|
-
|
|
4223
|
+
useEffect(() => {
|
|
4301
4224
|
if (!embedded) return;
|
|
4302
4225
|
sendToPlatform("editor-ready", {});
|
|
4303
4226
|
}, [embedded]);
|
|
4304
|
-
|
|
4227
|
+
useEffect(() => {
|
|
4305
4228
|
if (!embedded) return;
|
|
4306
4229
|
const handlePlatformMessage = (e) => {
|
|
4307
4230
|
const msg = e.data;
|
|
@@ -4402,14 +4325,14 @@ function EditorShell({ initialExperiment, embeddedMode, proxyBaseUrl }) {
|
|
|
4402
4325
|
window.addEventListener("message", handlePlatformMessage);
|
|
4403
4326
|
return () => window.removeEventListener("message", handlePlatformMessage);
|
|
4404
4327
|
}, [embedded, handleLoadUrl, toast, loadExperimentVariations]);
|
|
4405
|
-
|
|
4328
|
+
useEffect(() => {
|
|
4406
4329
|
if (!embedded) return;
|
|
4407
4330
|
sendToPlatform("mutations-changed", {});
|
|
4408
4331
|
}, [embedded, variations]);
|
|
4409
|
-
|
|
4332
|
+
useEffect(() => {
|
|
4410
4333
|
sendToBridge({ type: "setMode", mode: interactionMode });
|
|
4411
4334
|
}, [interactionMode]);
|
|
4412
|
-
|
|
4335
|
+
useEffect(() => {
|
|
4413
4336
|
if (syncDebounceRef.current) {
|
|
4414
4337
|
clearTimeout(syncDebounceRef.current);
|
|
4415
4338
|
syncDebounceRef.current = null;
|
|
@@ -4430,7 +4353,7 @@ function EditorShell({ initialExperiment, embeddedMode, proxyBaseUrl }) {
|
|
|
4430
4353
|
}
|
|
4431
4354
|
};
|
|
4432
4355
|
}, [connectionStatus, activeVariationId, experimentData?.experimentId]);
|
|
4433
|
-
|
|
4356
|
+
useEffect(() => {
|
|
4434
4357
|
const handler = (e) => {
|
|
4435
4358
|
const meta = e.metaKey || e.ctrlKey;
|
|
4436
4359
|
const target = e.target;
|
|
@@ -4561,11 +4484,11 @@ function EditorShell({ initialExperiment, embeddedMode, proxyBaseUrl }) {
|
|
|
4561
4484
|
interactionMode,
|
|
4562
4485
|
setInteractionMode
|
|
4563
4486
|
]);
|
|
4564
|
-
|
|
4487
|
+
useEffect(() => {
|
|
4565
4488
|
return () => stopHeartbeat();
|
|
4566
4489
|
}, [stopHeartbeat]);
|
|
4567
|
-
return /* @__PURE__ */
|
|
4568
|
-
/* @__PURE__ */
|
|
4490
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col h-full", style: { backgroundColor: "#E8EBF0" }, children: [
|
|
4491
|
+
/* @__PURE__ */ jsx(
|
|
4569
4492
|
TopBar,
|
|
4570
4493
|
{
|
|
4571
4494
|
connectionStatus,
|
|
@@ -4582,9 +4505,9 @@ function EditorShell({ initialExperiment, embeddedMode, proxyBaseUrl }) {
|
|
|
4582
4505
|
onSave: handleSave
|
|
4583
4506
|
}
|
|
4584
4507
|
),
|
|
4585
|
-
/* @__PURE__ */
|
|
4586
|
-
interactionMode !== "preview" && /* @__PURE__ */
|
|
4587
|
-
/* @__PURE__ */
|
|
4508
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-1 min-h-0", children: [
|
|
4509
|
+
interactionMode !== "preview" && /* @__PURE__ */ jsx(LeftPanel, {}),
|
|
4510
|
+
/* @__PURE__ */ jsx(
|
|
4588
4511
|
CanvasArea,
|
|
4589
4512
|
{
|
|
4590
4513
|
url,
|
|
@@ -4595,14 +4518,10 @@ function EditorShell({ initialExperiment, embeddedMode, proxyBaseUrl }) {
|
|
|
4595
4518
|
onPong: handlePong
|
|
4596
4519
|
}
|
|
4597
4520
|
),
|
|
4598
|
-
interactionMode !== "preview" && /* @__PURE__ */
|
|
4521
|
+
interactionMode !== "preview" && /* @__PURE__ */ jsx(RightPanel, { codeEditorOpen, experimentId: experimentData?.experimentId })
|
|
4599
4522
|
] })
|
|
4600
4523
|
] });
|
|
4601
4524
|
}
|
|
4602
|
-
|
|
4603
|
-
// src/PlatformVisualEditor.tsx
|
|
4604
|
-
import { useCallback as useCallback14, useEffect as useEffect12, useLayoutEffect, useMemo as useMemo4, useRef as useRef10, useState as useState13 } from "react";
|
|
4605
|
-
import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
4606
4525
|
function PlatformVisualEditor({
|
|
4607
4526
|
channel = "conversion-platform",
|
|
4608
4527
|
embeddedGlobalKey = "__CONVERSION_EMBEDDED__",
|
|
@@ -4637,15 +4556,15 @@ function PlatformVisualEditor({
|
|
|
4637
4556
|
renderError
|
|
4638
4557
|
}) {
|
|
4639
4558
|
console.log(experiment);
|
|
4640
|
-
const [editorReady, setEditorReady] =
|
|
4641
|
-
const [dirty, setDirty] =
|
|
4642
|
-
const dirtyRef =
|
|
4643
|
-
const mutationChangeCountRef =
|
|
4644
|
-
const lastLoadedPayloadRef =
|
|
4645
|
-
|
|
4559
|
+
const [editorReady, setEditorReady] = useState(false);
|
|
4560
|
+
const [dirty, setDirty] = useState(false);
|
|
4561
|
+
const dirtyRef = useRef(false);
|
|
4562
|
+
const mutationChangeCountRef = useRef(0);
|
|
4563
|
+
const lastLoadedPayloadRef = useRef("");
|
|
4564
|
+
useEffect(() => {
|
|
4646
4565
|
dirtyRef.current = dirty;
|
|
4647
4566
|
}, [dirty]);
|
|
4648
|
-
const sendToEditor =
|
|
4567
|
+
const sendToEditor = useCallback(
|
|
4649
4568
|
(type, payload) => {
|
|
4650
4569
|
window.dispatchEvent(
|
|
4651
4570
|
new MessageEvent("message", {
|
|
@@ -4655,16 +4574,16 @@ function PlatformVisualEditor({
|
|
|
4655
4574
|
},
|
|
4656
4575
|
[channel]
|
|
4657
4576
|
);
|
|
4658
|
-
|
|
4577
|
+
useEffect(() => {
|
|
4659
4578
|
window[embeddedGlobalKey] = true;
|
|
4660
4579
|
return () => {
|
|
4661
4580
|
delete window[embeddedGlobalKey];
|
|
4662
4581
|
};
|
|
4663
4582
|
}, [embeddedGlobalKey]);
|
|
4664
|
-
|
|
4583
|
+
useEffect(() => {
|
|
4665
4584
|
onDirtyChange?.(dirty);
|
|
4666
4585
|
}, [dirty, onDirtyChange]);
|
|
4667
|
-
const loadPayload =
|
|
4586
|
+
const loadPayload = useMemo(
|
|
4668
4587
|
() => ({
|
|
4669
4588
|
experimentId: experiment?.experimentId,
|
|
4670
4589
|
name: experiment?.name,
|
|
@@ -4675,7 +4594,7 @@ function PlatformVisualEditor({
|
|
|
4675
4594
|
}),
|
|
4676
4595
|
[experiment]
|
|
4677
4596
|
);
|
|
4678
|
-
|
|
4597
|
+
useEffect(() => {
|
|
4679
4598
|
if (!editorReady) return;
|
|
4680
4599
|
const payloadKey = JSON.stringify(loadPayload);
|
|
4681
4600
|
if (lastLoadedPayloadRef.current === payloadKey) return;
|
|
@@ -4683,7 +4602,7 @@ function PlatformVisualEditor({
|
|
|
4683
4602
|
mutationChangeCountRef.current = 0;
|
|
4684
4603
|
sendToEditor("load-experiment", loadPayload);
|
|
4685
4604
|
}, [editorReady, loadPayload, sendToEditor]);
|
|
4686
|
-
const handleMessage =
|
|
4605
|
+
const handleMessage = useCallback(
|
|
4687
4606
|
async (e) => {
|
|
4688
4607
|
const msg = e.data;
|
|
4689
4608
|
if (!msg || msg.channel !== channel) return;
|
|
@@ -4744,8 +4663,6 @@ function PlatformVisualEditor({
|
|
|
4744
4663
|
}
|
|
4745
4664
|
if (await onDiscardDirty()) onClose?.();
|
|
4746
4665
|
break;
|
|
4747
|
-
default:
|
|
4748
|
-
break;
|
|
4749
4666
|
}
|
|
4750
4667
|
},
|
|
4751
4668
|
[
|
|
@@ -4774,16 +4691,16 @@ function PlatformVisualEditor({
|
|
|
4774
4691
|
window.removeEventListener("message", listener);
|
|
4775
4692
|
};
|
|
4776
4693
|
}, [handleMessage]);
|
|
4777
|
-
const onTabClick =
|
|
4694
|
+
const onTabClick = useCallback((tab) => onTabChange?.(tab), [onTabChange]);
|
|
4778
4695
|
if (loading) {
|
|
4779
4696
|
if (renderLoading) return renderLoading();
|
|
4780
|
-
return /* @__PURE__ */
|
|
4697
|
+
return /* @__PURE__ */ jsx("div", { className: "fixed inset-0 z-[9999] flex items-center justify-center bg-white", children: /* @__PURE__ */ jsx("div", { className: "text-sm text-gray-500", children: loadingText }) });
|
|
4781
4698
|
}
|
|
4782
4699
|
if (error) {
|
|
4783
4700
|
if (renderError) return renderError(error);
|
|
4784
|
-
return /* @__PURE__ */
|
|
4701
|
+
return /* @__PURE__ */ jsx("div", { className: "fixed inset-0 z-[9999] flex items-center justify-center bg-white", children: /* @__PURE__ */ jsx("div", { className: "text-sm text-red-500", children: error }) });
|
|
4785
4702
|
}
|
|
4786
|
-
return /* @__PURE__ */
|
|
4703
|
+
return /* @__PURE__ */ jsxs("div", { className, children: [
|
|
4787
4704
|
showHeader ? renderHeader?.({
|
|
4788
4705
|
title: title ?? experiment?.name,
|
|
4789
4706
|
status: status ?? experiment?.status,
|
|
@@ -4791,13 +4708,13 @@ function PlatformVisualEditor({
|
|
|
4791
4708
|
activeTab,
|
|
4792
4709
|
onTabClick,
|
|
4793
4710
|
onClose
|
|
4794
|
-
}) ?? /* @__PURE__ */
|
|
4795
|
-
/* @__PURE__ */
|
|
4796
|
-
/* @__PURE__ */
|
|
4797
|
-
status ? /* @__PURE__ */
|
|
4711
|
+
}) ?? /* @__PURE__ */ jsxs("div", { className: "h-14 border-b bg-white flex items-center justify-between px-4", children: [
|
|
4712
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 min-w-0", children: [
|
|
4713
|
+
/* @__PURE__ */ jsx("div", { className: "font-semibold truncate", children: title ?? experiment?.name ?? "Visual Editor" }),
|
|
4714
|
+
status ? /* @__PURE__ */ jsx("span", { className: "text-xs px-2 py-0.5 border rounded", children: status }) : null
|
|
4798
4715
|
] }),
|
|
4799
|
-
/* @__PURE__ */
|
|
4800
|
-
/* @__PURE__ */
|
|
4716
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
4717
|
+
/* @__PURE__ */ jsx("div", { className: "hidden md:flex items-center gap-1", children: tabs.map((tab) => /* @__PURE__ */ jsx(
|
|
4801
4718
|
"button",
|
|
4802
4719
|
{
|
|
4803
4720
|
type: "button",
|
|
@@ -4807,7 +4724,7 @@ function PlatformVisualEditor({
|
|
|
4807
4724
|
},
|
|
4808
4725
|
tab.hash
|
|
4809
4726
|
)) }),
|
|
4810
|
-
showCloseButton ? /* @__PURE__ */
|
|
4727
|
+
showCloseButton ? /* @__PURE__ */ jsx(
|
|
4811
4728
|
"button",
|
|
4812
4729
|
{
|
|
4813
4730
|
type: "button",
|
|
@@ -4818,13 +4735,9 @@ function PlatformVisualEditor({
|
|
|
4818
4735
|
) : null
|
|
4819
4736
|
] })
|
|
4820
4737
|
] }) : null,
|
|
4821
|
-
/* @__PURE__ */
|
|
4738
|
+
/* @__PURE__ */ jsx("div", { className: editorClassName, children: /* @__PURE__ */ jsx(ToastProvider, { children: /* @__PURE__ */ jsx(EditorShell, { initialExperiment: experiment, embeddedMode: true, proxyBaseUrl }) }) })
|
|
4822
4739
|
] });
|
|
4823
4740
|
}
|
|
4824
|
-
|
|
4825
|
-
// src/PlatformVisualEditorV2.tsx
|
|
4826
|
-
import { useCallback as useCallback15, useEffect as useEffect13, useLayoutEffect as useLayoutEffect2, useMemo as useMemo5, useRef as useRef11, useState as useState14 } from "react";
|
|
4827
|
-
import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
4828
4741
|
var VVVEB_CHANNEL = "vvveb-bridge";
|
|
4829
4742
|
function PlatformVisualEditorV2({
|
|
4830
4743
|
// channel kept for API compatibility; VvvebJs uses its own internal channel
|
|
@@ -4857,31 +4770,31 @@ function PlatformVisualEditorV2({
|
|
|
4857
4770
|
renderError
|
|
4858
4771
|
}) {
|
|
4859
4772
|
console.log(experiment);
|
|
4860
|
-
const iframeRef =
|
|
4861
|
-
const [editorReady, setEditorReady] =
|
|
4862
|
-
const [dirty, setDirty] =
|
|
4863
|
-
const dirtyRef =
|
|
4864
|
-
const mutationSkipCountRef =
|
|
4865
|
-
const lastLoadedRef =
|
|
4866
|
-
|
|
4773
|
+
const iframeRef = useRef(null);
|
|
4774
|
+
const [editorReady, setEditorReady] = useState(false);
|
|
4775
|
+
const [dirty, setDirty] = useState(false);
|
|
4776
|
+
const dirtyRef = useRef(false);
|
|
4777
|
+
const mutationSkipCountRef = useRef(0);
|
|
4778
|
+
const lastLoadedRef = useRef("");
|
|
4779
|
+
useEffect(() => {
|
|
4867
4780
|
dirtyRef.current = dirty;
|
|
4868
4781
|
}, [dirty]);
|
|
4869
|
-
|
|
4782
|
+
useEffect(() => {
|
|
4870
4783
|
window[embeddedGlobalKey] = true;
|
|
4871
4784
|
return () => {
|
|
4872
4785
|
delete window[embeddedGlobalKey];
|
|
4873
4786
|
};
|
|
4874
4787
|
}, [embeddedGlobalKey]);
|
|
4875
|
-
|
|
4788
|
+
useEffect(() => {
|
|
4876
4789
|
onDirtyChange?.(dirty);
|
|
4877
4790
|
}, [dirty, onDirtyChange]);
|
|
4878
|
-
const sendToVvveb =
|
|
4791
|
+
const sendToVvveb = useCallback((type, payload) => {
|
|
4879
4792
|
iframeRef.current?.contentWindow?.postMessage(
|
|
4880
4793
|
{ channel: VVVEB_CHANNEL, type, payload },
|
|
4881
4794
|
"*"
|
|
4882
4795
|
);
|
|
4883
4796
|
}, []);
|
|
4884
|
-
const loadPayload =
|
|
4797
|
+
const loadPayload = useMemo(
|
|
4885
4798
|
() => ({
|
|
4886
4799
|
experimentId: experiment?.experimentId,
|
|
4887
4800
|
name: experiment?.name,
|
|
@@ -4892,7 +4805,7 @@ function PlatformVisualEditorV2({
|
|
|
4892
4805
|
}),
|
|
4893
4806
|
[experiment]
|
|
4894
4807
|
);
|
|
4895
|
-
|
|
4808
|
+
useEffect(() => {
|
|
4896
4809
|
if (!editorReady) return;
|
|
4897
4810
|
const key = JSON.stringify(loadPayload);
|
|
4898
4811
|
if (lastLoadedRef.current === key) return;
|
|
@@ -4900,7 +4813,7 @@ function PlatformVisualEditorV2({
|
|
|
4900
4813
|
mutationSkipCountRef.current = 0;
|
|
4901
4814
|
sendToVvveb("load-experiment", loadPayload);
|
|
4902
4815
|
}, [editorReady, loadPayload, sendToVvveb]);
|
|
4903
|
-
const handleMessage =
|
|
4816
|
+
const handleMessage = useCallback(
|
|
4904
4817
|
async (e) => {
|
|
4905
4818
|
if (!e.data || e.data.channel !== VVVEB_CHANNEL) return;
|
|
4906
4819
|
const { type, payload } = e.data;
|
|
@@ -4967,31 +4880,31 @@ function PlatformVisualEditorV2({
|
|
|
4967
4880
|
sendToVvveb
|
|
4968
4881
|
]
|
|
4969
4882
|
);
|
|
4970
|
-
|
|
4883
|
+
useLayoutEffect(() => {
|
|
4971
4884
|
const listener = (e) => void handleMessage(e);
|
|
4972
4885
|
window.addEventListener("message", listener);
|
|
4973
4886
|
return () => window.removeEventListener("message", listener);
|
|
4974
4887
|
}, [handleMessage]);
|
|
4975
|
-
const onTabClick =
|
|
4888
|
+
const onTabClick = useCallback(
|
|
4976
4889
|
(tab) => onTabChange?.(tab),
|
|
4977
4890
|
[onTabChange]
|
|
4978
4891
|
);
|
|
4979
4892
|
if (loading) {
|
|
4980
4893
|
if (renderLoading) return renderLoading();
|
|
4981
|
-
return /* @__PURE__ */
|
|
4982
|
-
/* @__PURE__ */
|
|
4983
|
-
/* @__PURE__ */
|
|
4894
|
+
return /* @__PURE__ */ jsx("div", { className: "fixed inset-0 z-[9999] flex items-center justify-center bg-slate-50", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-3", children: [
|
|
4895
|
+
/* @__PURE__ */ jsx("div", { className: "w-8 h-8 border-2 border-slate-200 border-t-indigo-500 rounded-full animate-spin" }),
|
|
4896
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-slate-400", children: loadingText })
|
|
4984
4897
|
] }) });
|
|
4985
4898
|
}
|
|
4986
4899
|
if (error) {
|
|
4987
4900
|
if (renderError) return renderError(error);
|
|
4988
|
-
return /* @__PURE__ */
|
|
4989
|
-
/* @__PURE__ */
|
|
4990
|
-
/* @__PURE__ */
|
|
4991
|
-
/* @__PURE__ */
|
|
4901
|
+
return /* @__PURE__ */ jsx("div", { className: "fixed inset-0 z-[9999] flex items-center justify-center bg-slate-50", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-2 text-center px-8", children: [
|
|
4902
|
+
/* @__PURE__ */ jsx("div", { className: "text-2xl text-red-300", children: "\u26A0" }),
|
|
4903
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-slate-700", children: "Something went wrong" }),
|
|
4904
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-red-400", children: error })
|
|
4992
4905
|
] }) });
|
|
4993
4906
|
}
|
|
4994
|
-
return /* @__PURE__ */
|
|
4907
|
+
return /* @__PURE__ */ jsxs("div", { className, children: [
|
|
4995
4908
|
showHeader ? renderHeader?.({
|
|
4996
4909
|
title: title ?? experiment?.name,
|
|
4997
4910
|
status: status ?? experiment?.status,
|
|
@@ -5000,12 +4913,12 @@ function PlatformVisualEditorV2({
|
|
|
5000
4913
|
onTabClick,
|
|
5001
4914
|
onClose
|
|
5002
4915
|
}) ?? /* ── Figma-style top bar ── */
|
|
5003
|
-
/* @__PURE__ */
|
|
5004
|
-
/* @__PURE__ */
|
|
5005
|
-
/* @__PURE__ */
|
|
5006
|
-
/* @__PURE__ */
|
|
5007
|
-
status ? /* @__PURE__ */
|
|
5008
|
-
dirty ? /* @__PURE__ */
|
|
4916
|
+
/* @__PURE__ */ jsxs("div", { className: "h-12 border-b border-slate-200 bg-white flex items-center justify-between px-4 shadow-[0_1px_3px_rgba(0,0,0,0.04)] shrink-0", children: [
|
|
4917
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5 min-w-0", children: [
|
|
4918
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] font-bold tracking-wide px-1.5 py-0.5 rounded bg-indigo-50 text-indigo-600 border border-indigo-100 shrink-0", children: "V2" }),
|
|
4919
|
+
/* @__PURE__ */ jsx("span", { className: "font-semibold text-sm text-slate-800 truncate", children: title ?? experiment?.name ?? "Visual Editor" }),
|
|
4920
|
+
status ? /* @__PURE__ */ jsx("span", { className: "hidden sm:inline-flex text-[10px] px-2 py-0.5 rounded-full border border-slate-200 text-slate-500 font-medium bg-slate-50 capitalize", children: status }) : null,
|
|
4921
|
+
dirty ? /* @__PURE__ */ jsx(
|
|
5009
4922
|
"span",
|
|
5010
4923
|
{
|
|
5011
4924
|
className: "w-1.5 h-1.5 rounded-full bg-amber-400 shrink-0",
|
|
@@ -5013,7 +4926,7 @@ function PlatformVisualEditorV2({
|
|
|
5013
4926
|
}
|
|
5014
4927
|
) : null
|
|
5015
4928
|
] }),
|
|
5016
|
-
tabs.length > 0 ? /* @__PURE__ */
|
|
4929
|
+
tabs.length > 0 ? /* @__PURE__ */ jsx("div", { className: "hidden md:flex items-center gap-1 absolute left-1/2 -translate-x-1/2", children: tabs.map((tab) => /* @__PURE__ */ jsx(
|
|
5017
4930
|
"button",
|
|
5018
4931
|
{
|
|
5019
4932
|
type: "button",
|
|
@@ -5023,7 +4936,7 @@ function PlatformVisualEditorV2({
|
|
|
5023
4936
|
},
|
|
5024
4937
|
tab.hash
|
|
5025
4938
|
)) }) : null,
|
|
5026
|
-
/* @__PURE__ */
|
|
4939
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: showCloseButton ? /* @__PURE__ */ jsx(
|
|
5027
4940
|
"button",
|
|
5028
4941
|
{
|
|
5029
4942
|
type: "button",
|
|
@@ -5033,7 +4946,7 @@ function PlatformVisualEditorV2({
|
|
|
5033
4946
|
}
|
|
5034
4947
|
) : null })
|
|
5035
4948
|
] }) : null,
|
|
5036
|
-
/* @__PURE__ */
|
|
4949
|
+
/* @__PURE__ */ jsx("div", { className: editorClassName, children: /* @__PURE__ */ jsx(
|
|
5037
4950
|
"iframe",
|
|
5038
4951
|
{
|
|
5039
4952
|
ref: iframeRef,
|
|
@@ -5045,10 +4958,5 @@ function PlatformVisualEditorV2({
|
|
|
5045
4958
|
) })
|
|
5046
4959
|
] });
|
|
5047
4960
|
}
|
|
5048
|
-
|
|
5049
|
-
|
|
5050
|
-
PlatformVisualEditor,
|
|
5051
|
-
PlatformVisualEditorV2,
|
|
5052
|
-
ToastProvider,
|
|
5053
|
-
useToast
|
|
5054
|
-
};
|
|
4961
|
+
|
|
4962
|
+
export { EditorShell, PlatformVisualEditor, PlatformVisualEditorV2, ToastProvider, useToast };
|