labellife-design-tool 1.2.9 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -547,11 +547,8 @@ function initJsxCompat() {
547
547
  }
548
548
 
549
549
  // src/CanvasEditor.tsx
550
- import React18, { useState as useState3, useRef as useRef5, useEffect as useEffect4, useMemo, useCallback as useCallback4 } from "react";
550
+ import React17, { useRef as useRef4, useEffect as useEffect4, useMemo, useCallback as useCallback4 } from "react";
551
551
  import { Stage, Layer, Rect as Rect2 } from "react-konva";
552
- import {
553
- PlusCircle
554
- } from "lucide-react";
555
552
 
556
553
  // src/elements/EditableTextElement.tsx
557
554
  import React, { useRef, useEffect } from "react";
@@ -1361,61 +1358,6 @@ var UrlImageElement = ({ element, isSelected, onSelect, onChange }) => {
1361
1358
  }
1362
1359
  }));
1363
1360
  };
1364
- // src/constants/CanvasPresets.ts
1365
- var CANVAS_PRESETS = [
1366
- { name: "Instagram Post", width: 1080, height: 1080 },
1367
- { name: "Instagram Story", width: 1080, height: 1920 },
1368
- { name: "Facebook Post", width: 1200, height: 630 },
1369
- { name: "Twitter Post", width: 1024, height: 512 },
1370
- { name: "A4", width: 794, height: 1123 },
1371
- { name: "Letter", width: 816, height: 1056 },
1372
- { name: "YouTube Thumbnail", width: 1280, height: 720 },
1373
- { name: "Custom", width: 800, height: 600 }
1374
- ];
1375
- // src/constants/DefaultColors.ts
1376
- var DEFAULT_COLORS = [
1377
- "#000000",
1378
- "#FFFFFF",
1379
- "#FF0000",
1380
- "#00FF00",
1381
- "#0000FF",
1382
- "#FFFF00",
1383
- "#FF00FF",
1384
- "#00FFFF",
1385
- "#FFA500",
1386
- "#800080",
1387
- "#FFC0CB",
1388
- "#A52A2A",
1389
- "#808080",
1390
- "#C0C0C0",
1391
- "#FFD700"
1392
- ];
1393
- // src/constants/FontFamilies.ts
1394
- var FONT_FAMILIES = [
1395
- "Arial",
1396
- "Helvetica",
1397
- "Times New Roman",
1398
- "Georgia",
1399
- "Verdana",
1400
- "Courier New",
1401
- "Comic Sans MS",
1402
- "Impact",
1403
- "Trebuchet MS",
1404
- "Palatino",
1405
- "Garamond",
1406
- "Bookman",
1407
- "Tahoma"
1408
- ];
1409
- // src/config.ts
1410
- var _unsplashAccessKey = typeof process !== "undefined" && process.env?.UNSPLASH_ACCESS_KEY ? process.env.UNSPLASH_ACCESS_KEY : undefined;
1411
- var UNSPLASH_ACCESS_KEY = _unsplashAccessKey || "YOUR_UNSPLASH_ACCESS_KEY_HERE";
1412
- function setUnsplashAccessKey(key) {
1413
- _unsplashAccessKey = key;
1414
- }
1415
- function getUnsplashAccessKey() {
1416
- return _unsplashAccessKey;
1417
- }
1418
-
1419
1361
  // src/components/Header.tsx
1420
1362
  import React7 from "react";
1421
1363
 
@@ -1879,6 +1821,53 @@ var ElementPanel_default = ElementPanel;
1879
1821
 
1880
1822
  // src/panels/TextPanel.tsx
1881
1823
  import React10 from "react";
1824
+
1825
+ // src/constants/CanvasPresets.ts
1826
+ var CANVAS_PRESETS = [
1827
+ { name: "Instagram Post", width: 1080, height: 1080 },
1828
+ { name: "Instagram Story", width: 1080, height: 1920 },
1829
+ { name: "Facebook Post", width: 1200, height: 630 },
1830
+ { name: "Twitter Post", width: 1024, height: 512 },
1831
+ { name: "A4", width: 794, height: 1123 },
1832
+ { name: "Letter", width: 816, height: 1056 },
1833
+ { name: "YouTube Thumbnail", width: 1280, height: 720 },
1834
+ { name: "Custom", width: 800, height: 600 }
1835
+ ];
1836
+ // src/constants/DefaultColors.ts
1837
+ var DEFAULT_COLORS = [
1838
+ "#000000",
1839
+ "#FFFFFF",
1840
+ "#FF0000",
1841
+ "#00FF00",
1842
+ "#0000FF",
1843
+ "#FFFF00",
1844
+ "#FF00FF",
1845
+ "#00FFFF",
1846
+ "#FFA500",
1847
+ "#800080",
1848
+ "#FFC0CB",
1849
+ "#A52A2A",
1850
+ "#808080",
1851
+ "#C0C0C0",
1852
+ "#FFD700"
1853
+ ];
1854
+ // src/constants/FontFamilies.ts
1855
+ var FONT_FAMILIES = [
1856
+ "Arial",
1857
+ "Helvetica",
1858
+ "Times New Roman",
1859
+ "Georgia",
1860
+ "Verdana",
1861
+ "Courier New",
1862
+ "Comic Sans MS",
1863
+ "Impact",
1864
+ "Trebuchet MS",
1865
+ "Palatino",
1866
+ "Garamond",
1867
+ "Bookman",
1868
+ "Tahoma"
1869
+ ];
1870
+ // src/panels/TextPanel.tsx
1882
1871
  import {
1883
1872
  Bold,
1884
1873
  Italic,
@@ -2725,399 +2714,350 @@ var RightSidebar = ({
2725
2714
  }), /* @__PURE__ */ React16.createElement("span", null, "Delete"))));
2726
2715
  };
2727
2716
  var RightSidebar_default = RightSidebar;
2728
- // src/components/TemplateInputModal.tsx
2729
- import React17, { useState as useState2, useRef as useRef4 } from "react";
2730
- import { X, Upload as Upload2, Image as ImageIcon4, ChevronLeft, ChevronRight } from "lucide-react";
2731
- var TemplateInputModal = ({
2732
- inputs,
2733
- onComplete,
2734
- onCancel
2717
+
2718
+ // src/CanvasEditor.tsx
2719
+ var CanvasEditor = ({
2720
+ name,
2721
+ config,
2722
+ store
2735
2723
  }) => {
2736
- const isLightTheme = document.title.includes("Light Theme");
2737
- const modalStyle = {
2738
- backgroundColor: isLightTheme ? "#f8fafc" : "#1f2937",
2739
- color: isLightTheme ? "#0f172a" : "#ffffff",
2740
- borderColor: isLightTheme ? "#cbd5e1" : "#374151",
2741
- boxShadow: "0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1)"
2742
- };
2743
- const headerStyle = {
2744
- borderBottomColor: isLightTheme ? "#e2e8f0" : "#374151",
2745
- borderBottomWidth: "1px",
2746
- borderBottomStyle: "solid"
2747
- };
2748
- const footerStyle = {
2749
- borderTopColor: isLightTheme ? "#e2e8f0" : "#374151",
2750
- borderTopWidth: "1px",
2751
- borderTopStyle: "solid"
2752
- };
2753
- const inputStyle = {
2754
- backgroundColor: isLightTheme ? "#ffffff" : "#374151",
2755
- borderColor: isLightTheme ? "#cbd5e1" : "#4b5563",
2756
- color: isLightTheme ? "#0f172a" : "#ffffff"
2757
- };
2758
- const labelStyle = {
2759
- color: isLightTheme ? "#0f172a" : "#d1d5db",
2760
- fontWeight: 500
2761
- };
2762
- const helpTextStyle = {
2763
- color: isLightTheme ? "#64748b" : "#9ca3af"
2764
- };
2765
- const primaryButtonStyle = {
2766
- backgroundColor: "#2563eb",
2767
- color: "#ffffff"
2768
- };
2769
- const secondaryButtonStyle = {
2770
- backgroundColor: isLightTheme ? "#e2e8f0" : "#374151",
2771
- color: isLightTheme ? "#0f172a" : "#ffffff",
2772
- border: "none"
2773
- };
2774
- const [values, setValues] = useState2({});
2775
- const [errors, setErrors] = useState2({});
2776
- const [currentStep, setCurrentStep] = useState2(0);
2777
- const fileInputRefs = useRef4({});
2778
- const sortedInputs = [...inputs].sort((a, b) => (a.config.order || 0) - (b.config.order || 0));
2779
- const currentInput = sortedInputs[currentStep];
2780
- const isFirstStep = currentStep === 0;
2781
- const isLastStep = currentStep === sortedInputs.length - 1;
2782
- const handleTextChange = (id, value) => {
2783
- setValues((prev) => ({ ...prev, [id]: value }));
2784
- if (errors[id]) {
2785
- setErrors((prev) => {
2786
- const newErrors = { ...prev };
2787
- delete newErrors[id];
2788
- return newErrors;
2789
- });
2790
- }
2791
- };
2792
- const handleFileChange = (id, file) => {
2793
- if (!file) {
2794
- setValues((prev) => {
2795
- const newValues = { ...prev };
2796
- delete newValues[id];
2797
- return newValues;
2798
- });
2799
- return;
2724
+ const stageRef = useRef4(null);
2725
+ const fileInputRef = useRef4(null);
2726
+ const jsonInputRef = useRef4(null);
2727
+ const containerRef = useRef4(null);
2728
+ const currentPage = store.design.pages.find((p) => p.id === store.activePageId) || store.design.pages[0] || null;
2729
+ const selectedElement = currentPage?.elements.find((el) => el.id === store.selectedId) || null;
2730
+ useEffect4(() => {
2731
+ const handleDesignChange = (newDesign) => {};
2732
+ const handleActivePageChange = (pageId) => {};
2733
+ const handleActivePanelChange = (panelId) => {};
2734
+ const handleSelectedIdChange = (selectedId) => {};
2735
+ const handleSelectedIdsChange = (selectedIds) => {};
2736
+ const handleToolChange = (tool) => {};
2737
+ const handleZoomChange = (zoom) => {};
2738
+ const handleSelectedShapeChange = (shape) => {};
2739
+ store.on("designChanged", handleDesignChange);
2740
+ store.on("activePageChanged", handleActivePageChange);
2741
+ store.on("activePanelChanged", handleActivePanelChange);
2742
+ store.on("selectedIdChanged", handleSelectedIdChange);
2743
+ store.on("selectedIdsChanged", handleSelectedIdsChange);
2744
+ store.on("toolChanged", handleToolChange);
2745
+ store.on("zoomChanged", handleZoomChange);
2746
+ store.on("selectedShapeChanged", handleSelectedShapeChange);
2747
+ return () => {
2748
+ store.off("designChanged");
2749
+ store.off("activePageChanged");
2750
+ store.off("activePanelChanged");
2751
+ store.off("selectedIdChanged");
2752
+ store.off("selectedIdsChanged");
2753
+ store.off("toolChanged");
2754
+ store.off("zoomChanged");
2755
+ store.off("selectedShapeChanged");
2756
+ };
2757
+ }, [store]);
2758
+ const handleStageClick = useCallback4((e) => {
2759
+ const clickedOnEmpty = e.target === e.target.getStage();
2760
+ if (clickedOnEmpty) {
2761
+ store.setSelectedId(null);
2762
+ store.setSelectedIds([]);
2800
2763
  }
2801
- const config = inputs.find((input) => input.config.id === id)?.config;
2802
- if (!config)
2764
+ }, [store]);
2765
+ const addText = useCallback4(() => {
2766
+ if (!store.activePage)
2803
2767
  return;
2804
- if (config.accept && !file.type.match(config.accept.replace("*", ".*"))) {
2805
- setErrors((prev) => ({
2806
- ...prev,
2807
- [id]: `File must be of type: ${config.accept}`
2808
- }));
2768
+ store.activePage.addElement({
2769
+ type: "text",
2770
+ text: "Edit this text",
2771
+ x: store.design.width / 2 - 50,
2772
+ y: store.design.height / 2 - 15,
2773
+ width: 100,
2774
+ height: 30
2775
+ });
2776
+ }, [store]);
2777
+ const addShape = useCallback4((shapeType) => {
2778
+ if (!store.activePage)
2809
2779
  return;
2780
+ store.activePage.addElement({
2781
+ type: "shape",
2782
+ shapeType,
2783
+ x: store.design.width / 2 - 50,
2784
+ y: store.design.height / 2 - 50,
2785
+ width: 100,
2786
+ height: 100
2787
+ });
2788
+ }, [store]);
2789
+ const updateElement = useCallback4((id, attrs) => {
2790
+ store.updateElement(id, attrs);
2791
+ }, [store]);
2792
+ const deleteSelected = useCallback4(() => {
2793
+ if (store.selectedId || store.selectedIds.length > 0) {
2794
+ const idsToDelete = store.selectedIds.length > 0 ? store.selectedIds : [store.selectedId];
2795
+ idsToDelete.forEach((id) => store.deleteElement(id));
2796
+ store.setSelectedId(null);
2797
+ store.setSelectedIds([]);
2810
2798
  }
2811
- if (config.maxSize && file.size > config.maxSize) {
2812
- const maxSizeMB = (config.maxSize / 1024 / 1024).toFixed(2);
2813
- setErrors((prev) => ({
2814
- ...prev,
2815
- [id]: `File size must be less than ${maxSizeMB}MB`
2816
- }));
2817
- return;
2799
+ }, [store]);
2800
+ const duplicateSelected = useCallback4(() => {
2801
+ if (selectedElement) {
2802
+ const newElement = {
2803
+ ...selectedElement,
2804
+ id: Date.now().toString(),
2805
+ name: selectedElement.name + " copy",
2806
+ x: selectedElement.x + 20,
2807
+ y: selectedElement.y + 20
2808
+ };
2809
+ store.activePage?.addElement(newElement);
2810
+ store.setSelectedId(newElement.id);
2818
2811
  }
2819
- const reader = new FileReader;
2820
- reader.onload = (e) => {
2821
- const result = e.target?.result;
2822
- if (typeof result === "string") {
2823
- setValues((prev) => ({ ...prev, [id]: result }));
2824
- setErrors((prev) => {
2825
- const newErrors = { ...prev };
2826
- delete newErrors[id];
2827
- return newErrors;
2828
- });
2829
- }
2830
- };
2831
- reader.onerror = () => {
2832
- setErrors((prev) => ({
2833
- ...prev,
2834
- [id]: "Failed to read file"
2835
- }));
2812
+ }, [selectedElement, store]);
2813
+ const moveElementUp = useCallback4((id) => {}, [store]);
2814
+ const moveElementDown = useCallback4((id) => {}, [store]);
2815
+ const panelConfigs = useMemo(() => {
2816
+ const builtInConfigs = {
2817
+ elements: { id: "elements", title: "Elements", component: ElementPanel_default, props: { onAddShape: addShape, store } },
2818
+ text: { id: "text", title: "Text", component: TextPanel_default, props: { selectedElement, updateElement, onAddText: addText, store } },
2819
+ image: { id: "image", title: "Image", component: ImagePanel_default, props: { selectedElement, updateElement, store } },
2820
+ background: { id: "background", title: "Background", component: BackgroundPanel_default, props: { store } },
2821
+ design: { id: "design", title: "Design", component: DesignPanel_default, props: { store } },
2822
+ variables: { id: "variables", title: "Variables", component: VariablesPanel_default, props: { store } },
2823
+ export: { id: "export", title: "Export", component: ExportPanel_default, props: { store } }
2836
2824
  };
2837
- reader.readAsDataURL(file);
2838
- };
2839
- const validateCurrentStep = () => {
2840
- if (!currentInput)
2841
- return false;
2842
- const config = currentInput.config;
2843
- const value = values[config.id];
2844
- const newErrors = { ...errors };
2845
- delete newErrors[config.id];
2846
- if (config.required && !value) {
2847
- newErrors[config.id] = "This field is required";
2848
- setErrors(newErrors);
2849
- return false;
2850
- }
2851
- if (config.type === "text" && value) {
2852
- if (config.minLength && value.length < config.minLength) {
2853
- newErrors[config.id] = `Minimum length is ${config.minLength} characters`;
2854
- setErrors(newErrors);
2855
- return false;
2856
- }
2857
- if (config.maxLength && value.length > config.maxLength) {
2858
- newErrors[config.id] = `Maximum length is ${config.maxLength} characters`;
2859
- setErrors(newErrors);
2860
- return false;
2861
- }
2862
- }
2863
- setErrors(newErrors);
2864
- return true;
2865
- };
2866
- const handleNext = () => {
2867
- if (!validateCurrentStep()) {
2868
- return;
2869
- }
2870
- if (isLastStep) {
2871
- const allErrors = {};
2872
- sortedInputs.forEach((input) => {
2873
- if (input.config.required && !values[input.config.id]) {
2874
- allErrors[input.config.id] = "This field is required";
2825
+ const mergedConfigs = { ...builtInConfigs };
2826
+ if (config?.panels) {
2827
+ config.panels.forEach((panelConfig) => {
2828
+ if (typeof panelConfig === "string") {} else {
2829
+ const customPanelConfig = {
2830
+ id: panelConfig.id,
2831
+ title: panelConfig.title,
2832
+ component: panelConfig.component,
2833
+ props: panelConfig.props || {},
2834
+ ...panelConfig.icon && typeof panelConfig.icon.type === "string" ? {
2835
+ icon: panelConfig.icon.type
2836
+ } : {}
2837
+ };
2838
+ mergedConfigs[panelConfig.id] = customPanelConfig;
2875
2839
  }
2876
2840
  });
2877
- if (Object.keys(allErrors).length > 0) {
2878
- setErrors(allErrors);
2879
- const firstErrorIndex = sortedInputs.findIndex((input) => allErrors[input.config.id]);
2880
- if (firstErrorIndex !== -1) {
2881
- setCurrentStep(firstErrorIndex);
2882
- }
2883
- return;
2884
- }
2885
- onComplete(values);
2886
- } else {
2887
- setCurrentStep((prev) => prev + 1);
2888
2841
  }
2889
- };
2890
- const handlePrevious = () => {
2891
- if (!isFirstStep) {
2892
- setCurrentStep((prev) => prev - 1);
2893
- }
2894
- };
2895
- const handleSkip = () => {
2896
- if (!currentInput.config.required) {
2897
- const defaultValue = currentInput.config.defaultValue || "";
2898
- const valueToSet = defaultValue;
2899
- setValues((prev) => ({ ...prev, [currentInput.config.id]: valueToSet }));
2900
- setErrors((prev) => {
2901
- const newErrors = { ...prev };
2902
- delete newErrors[currentInput.config.id];
2903
- return newErrors;
2904
- });
2905
- const updatedValues = { ...values, [currentInput.config.id]: valueToSet };
2906
- if (isLastStep) {
2907
- const allErrors = {};
2908
- sortedInputs.forEach((input) => {
2909
- if (input.config.required && !updatedValues[input.config.id]) {
2910
- allErrors[input.config.id] = "This field is required";
2842
+ const orderedConfigs = [];
2843
+ if (config?.panels) {
2844
+ config.panels.forEach((panelConfig) => {
2845
+ if (typeof panelConfig === "string") {
2846
+ if (mergedConfigs[panelConfig]) {
2847
+ orderedConfigs.push(mergedConfigs[panelConfig]);
2911
2848
  }
2912
- });
2913
- if (Object.keys(allErrors).length > 0) {
2914
- setErrors(allErrors);
2915
- const firstErrorIndex = sortedInputs.findIndex((input) => allErrors[input.config.id]);
2916
- if (firstErrorIndex !== -1) {
2917
- setCurrentStep(firstErrorIndex);
2918
- }
2919
- return;
2849
+ } else {
2850
+ orderedConfigs.push(mergedConfigs[panelConfig.id]);
2920
2851
  }
2921
- onComplete(updatedValues);
2922
- } else {
2923
- setCurrentStep((prev) => prev + 1);
2924
- }
2852
+ });
2925
2853
  }
2926
- };
2927
- const renderInput = (input) => {
2928
- const { config } = input;
2929
- const defaultValue = config.defaultValue || "";
2930
- const value = values[config.id] !== undefined ? values[config.id] : defaultValue;
2931
- const error = errors[config.id];
2932
- switch (config.type) {
2933
- case "image": {
2934
- const isUrl = value && (value.startsWith("http://") || value.startsWith("https://") || value.startsWith("data:"));
2935
- const isDefaultValue = value === defaultValue && defaultValue && (defaultValue.startsWith("http://") || defaultValue.startsWith("https://"));
2936
- return /* @__PURE__ */ React17.createElement("div", {
2937
- className: "space-y-2"
2938
- }, /* @__PURE__ */ React17.createElement("label", {
2939
- className: "block text-sm font-medium",
2940
- style: labelStyle
2941
- }, config.label, config.required && /* @__PURE__ */ React17.createElement("span", {
2942
- className: "text-red-400 ml-1"
2943
- }, "*")), config.helpText && /* @__PURE__ */ React17.createElement("p", {
2944
- className: "text-xs",
2945
- style: helpTextStyle
2946
- }, config.helpText), isDefaultValue && /* @__PURE__ */ React17.createElement("p", {
2947
- className: "text-xs text-blue-400"
2948
- }, "Using default image"), /* @__PURE__ */ React17.createElement("div", {
2949
- className: "flex items-center space-x-4"
2950
- }, /* @__PURE__ */ React17.createElement("input", {
2951
- ref: (el) => {
2952
- fileInputRefs.current[config.id] = el;
2953
- },
2954
- type: "file",
2955
- accept: config.accept || "image/*",
2956
- onChange: (e) => handleFileChange(config.id, e.target.files?.[0] || null),
2957
- className: "hidden"
2958
- }), /* @__PURE__ */ React17.createElement("button", {
2959
- type: "button",
2960
- onClick: () => fileInputRefs.current[config.id]?.click(),
2961
- className: "flex items-center space-x-2 px-4 py-2 rounded border-2 border-dashed transition-colors",
2962
- style: {
2963
- borderColor: value ? "#10b981" : isLightTheme ? "#cbd5e1" : "#4b5563",
2964
- backgroundColor: value ? "rgba(16, 185, 129, 0.1)" : isLightTheme ? "rgba(226, 232, 240, 0.5)" : "rgba(55, 65, 81, 0.5)",
2965
- color: value ? "#10b981" : isLightTheme ? "#475569" : "#d1d5db"
2966
- }
2967
- }, /* @__PURE__ */ React17.createElement(Upload2, {
2968
- className: "w-4 h-4"
2969
- }), /* @__PURE__ */ React17.createElement("span", null, value ? "Change Image" : "Upload Image")), value && /* @__PURE__ */ React17.createElement("div", {
2970
- className: "flex items-center space-x-2"
2971
- }, /* @__PURE__ */ React17.createElement(ImageIcon4, {
2972
- className: "w-5 h-5",
2973
- style: { color: "#10b981" }
2974
- }), /* @__PURE__ */ React17.createElement("span", {
2975
- className: "text-sm",
2976
- style: { color: isLightTheme ? "#64748b" : "#9ca3af" }
2977
- }, isUrl && !isDefaultValue ? "Image URL" : isDefaultValue ? "Default image" : "Image selected"), /* @__PURE__ */ React17.createElement("button", {
2978
- type: "button",
2979
- onClick: () => {
2980
- const resetValue = defaultValue || "";
2981
- setValues((prev) => ({ ...prev, [config.id]: resetValue }));
2982
- setErrors((prev) => {
2983
- const newErrors = { ...prev };
2984
- delete newErrors[config.id];
2985
- return newErrors;
2854
+ return orderedConfigs.length > 0 ? orderedConfigs : Object.values(builtInConfigs);
2855
+ }, [addShape, addText, config, selectedElement, updateElement, store]);
2856
+ const DynamicPanelRenderer = useMemo(() => {
2857
+ const activePanel = panelConfigs.find((panel) => panel.id === store.activePanelId);
2858
+ if (!activePanel)
2859
+ return null;
2860
+ const PanelComponent = activePanel.component;
2861
+ return /* @__PURE__ */ React17.createElement(PanelComponent, {
2862
+ key: activePanel.id,
2863
+ ...activePanel.props
2864
+ });
2865
+ }, [panelConfigs, store.activePanelId]);
2866
+ const handleExportPNG = useCallback4(() => {
2867
+ exportToPNG(stageRef.current, store.design);
2868
+ }, [store.design]);
2869
+ const handleExportJPG = useCallback4(() => {
2870
+ exportToJPG(stageRef.current, store.design);
2871
+ }, [store.design]);
2872
+ const handleExportJSON = useCallback4(() => {
2873
+ exportToJSON(store.design);
2874
+ }, [store.design]);
2875
+ const handleImportJSON = useCallback4((e) => {
2876
+ const file = e.target.files?.[0];
2877
+ if (file) {
2878
+ const reader = new FileReader;
2879
+ reader.onload = (event) => {
2880
+ try {
2881
+ const jsonData = JSON.parse(event.target?.result);
2882
+ importFromJSON(jsonData, (loadedDesign) => {
2883
+ store.setDesign(loadedDesign);
2884
+ }, (errorMessage) => {
2885
+ console.error("Import error:", errorMessage);
2886
+ }, () => {
2887
+ console.log("Import requires user inputs - not implemented");
2888
+ });
2889
+ } catch (error) {
2890
+ console.error("Invalid JSON file:", error);
2891
+ }
2892
+ };
2893
+ reader.readAsText(file);
2894
+ }
2895
+ }, [store]);
2896
+ const handleImageUpload = useCallback4((e) => {
2897
+ const file = e.target.files?.[0];
2898
+ if (file) {
2899
+ const reader = new FileReader;
2900
+ reader.onload = (event) => {
2901
+ const img = new window.Image;
2902
+ img.onload = () => {
2903
+ if (store.activePage) {
2904
+ store.activePage.addElement({
2905
+ type: "image",
2906
+ src: event.target?.result,
2907
+ x: store.design.width / 2 - img.width / 2,
2908
+ y: store.design.height / 2 - img.height / 2,
2909
+ width: img.width,
2910
+ height: img.height
2986
2911
  });
2987
- },
2988
- className: "text-sm",
2989
- style: { color: "#ef4444" }
2990
- }, isDefaultValue ? "Clear" : "Remove"))), value && isUrl && /* @__PURE__ */ React17.createElement("div", {
2991
- className: "mt-2"
2992
- }, /* @__PURE__ */ React17.createElement("img", {
2993
- src: value,
2994
- alt: "Preview",
2995
- className: "max-w-xs max-h-32 object-contain rounded border",
2996
- style: { borderColor: isLightTheme ? "#cbd5e1" : "#4b5563" },
2997
- onError: (e) => {
2998
- setErrors((prev) => ({
2999
- ...prev,
3000
- [config.id]: "Failed to load image"
3001
- }));
3002
- }
3003
- })), error && /* @__PURE__ */ React17.createElement("p", {
3004
- className: "text-sm",
3005
- style: { color: "#ef4444" }
3006
- }, error));
3007
- }
3008
- case "text":
3009
- return /* @__PURE__ */ React17.createElement("div", {
3010
- className: "space-y-2"
3011
- }, /* @__PURE__ */ React17.createElement("label", {
3012
- className: "block text-sm font-medium",
3013
- style: labelStyle
3014
- }, config.label, config.required && /* @__PURE__ */ React17.createElement("span", {
3015
- className: "text-red-400 ml-1"
3016
- }, "*")), config.helpText && /* @__PURE__ */ React17.createElement("p", {
3017
- className: "text-xs",
3018
- style: helpTextStyle
3019
- }, config.helpText), /* @__PURE__ */ React17.createElement("input", {
3020
- type: "text",
3021
- value,
3022
- onChange: (e) => handleTextChange(config.id, e.target.value),
3023
- placeholder: config.placeholder || "Enter text here...",
3024
- maxLength: config.maxLength,
3025
- className: "w-full px-3 py-2 border rounded focus:outline-none focus:ring-2 focus:ring-blue-500",
3026
- style: {
3027
- backgroundColor: "#ffffff",
3028
- color: "#0f172a",
3029
- borderColor: error ? "#ef4444" : "#cbd5e1"
3030
2912
  }
3031
- }), config.maxLength && /* @__PURE__ */ React17.createElement("p", {
3032
- className: "text-xs text-right",
3033
- style: helpTextStyle
3034
- }, value.length, " / ", config.maxLength), error && /* @__PURE__ */ React17.createElement("p", {
3035
- className: "text-sm",
3036
- style: { color: "#ef4444" }
3037
- }, error));
3038
- default:
3039
- return null;
2913
+ };
2914
+ img.src = event.target?.result;
2915
+ };
2916
+ reader.readAsDataURL(file);
3040
2917
  }
3041
- };
3042
- if (!currentInput) {
3043
- return null;
3044
- }
2918
+ }, [store]);
3045
2919
  return /* @__PURE__ */ React17.createElement("div", {
3046
- className: "fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4"
3047
- }, /* @__PURE__ */ React17.createElement("div", {
3048
- className: "rounded-lg w-full max-w-2xl max-h-[90vh] overflow-hidden flex flex-col",
3049
- style: modalStyle
2920
+ className: "w-full h-full flex flex-col bg-gray-900"
2921
+ }, /* @__PURE__ */ React17.createElement(Header_default, {
2922
+ name,
2923
+ zoom: store.zoom,
2924
+ historyIndex: 0,
2925
+ historyLength: 0,
2926
+ onUndo: () => {},
2927
+ onRedo: () => {},
2928
+ onZoomIn: () => store.setZoom(Math.min(3, store.zoom + 0.1)),
2929
+ onZoomOut: () => store.setZoom(Math.max(0.1, store.zoom - 0.1)),
2930
+ onZoomReset: () => store.setZoom(1),
2931
+ onSetActivePanel: store.openSidePanel
2932
+ }), /* @__PURE__ */ React17.createElement("div", {
2933
+ className: "flex-1 flex overflow-hidden"
3050
2934
  }, /* @__PURE__ */ React17.createElement("div", {
3051
- className: "p-6 border-b flex items-center justify-between",
3052
- style: headerStyle
3053
- }, /* @__PURE__ */ React17.createElement("div", null, /* @__PURE__ */ React17.createElement("h2", {
3054
- className: "text-xl font-semibold",
3055
- style: { color: isLightTheme ? "#0f172a" : "#ffffff" }
3056
- }, inputs.some((input) => input.config.required) ? "Template Input Required" : "Template Customization"), /* @__PURE__ */ React17.createElement("p", {
3057
- className: "text-sm mt-1",
3058
- style: { color: isLightTheme ? "#64748b" : "#9ca3af" }
3059
- }, "Step ", currentStep + 1, " of ", sortedInputs.length, !inputs.some((input) => input.config.required) && /* @__PURE__ */ React17.createElement("span", {
3060
- style: { color: "#2563eb", marginLeft: "8px" }
3061
- }, "(All fields optional)"))), /* @__PURE__ */ React17.createElement("button", {
3062
- onClick: onCancel,
3063
- className: "transition-colors",
3064
- style: { color: isLightTheme ? "#64748b" : "#9ca3af" }
3065
- }, /* @__PURE__ */ React17.createElement(X, {
3066
- className: "w-5 h-5"
3067
- }))), /* @__PURE__ */ React17.createElement("div", {
3068
- className: "px-6 pt-4"
2935
+ className: "w-80 bg-gray-800 border-r border-gray-700 flex"
2936
+ }, /* @__PURE__ */ React17.createElement(LeftMenu_default, {
2937
+ tool: store.tool,
2938
+ activePanel: store.activePanelId || "elements",
2939
+ onSetTool: store.setTool,
2940
+ onSetActivePanel: store.openSidePanel,
2941
+ config
2942
+ }), /* @__PURE__ */ React17.createElement("div", {
2943
+ className: "flex-1 p-4 overflow-y-auto"
2944
+ }, /* @__PURE__ */ React17.createElement(DynamicPanelRenderer, null))), /* @__PURE__ */ React17.createElement("div", {
2945
+ className: "flex-1 bg-gray-700 overflow-hidden relative",
2946
+ ref: containerRef
2947
+ }, !currentPage ? /* @__PURE__ */ React17.createElement("div", {
2948
+ className: "absolute inset-0 flex items-center justify-center text-gray-400"
3069
2949
  }, /* @__PURE__ */ React17.createElement("div", {
3070
- className: "w-full rounded-full h-2",
3071
- style: { backgroundColor: isLightTheme ? "#e2e8f0" : "#374151" }
2950
+ className: "text-center"
2951
+ }, /* @__PURE__ */ React17.createElement("p", {
2952
+ className: "mb-4"
2953
+ }, "No pages available"), /* @__PURE__ */ React17.createElement("p", {
2954
+ className: "text-sm"
2955
+ }, "Please add pages through your store management"))) : /* @__PURE__ */ React17.createElement("div", {
2956
+ className: "absolute inset-0 flex items-center justify-center"
3072
2957
  }, /* @__PURE__ */ React17.createElement("div", {
3073
- className: "h-2 rounded-full transition-all duration-300",
2958
+ className: "bg-white shadow-2xl",
3074
2959
  style: {
3075
- backgroundColor: "#2563eb",
3076
- width: `${(currentStep + 1) / sortedInputs.length * 100}%`
2960
+ transform: `scale(${store.zoom})`,
2961
+ transformOrigin: "center center"
3077
2962
  }
3078
- }))), /* @__PURE__ */ React17.createElement("div", {
3079
- className: "flex-1 overflow-y-auto p-6"
3080
- }, /* @__PURE__ */ React17.createElement("div", {
3081
- className: "space-y-4"
3082
- }, renderInput(currentInput))), /* @__PURE__ */ React17.createElement("div", {
3083
- className: "p-6 border-t flex items-center justify-between",
3084
- style: footerStyle
3085
- }, /* @__PURE__ */ React17.createElement("button", {
3086
- onClick: onCancel,
3087
- className: "px-4 py-2 rounded transition-colors",
3088
- style: secondaryButtonStyle
3089
- }, "Cancel"), /* @__PURE__ */ React17.createElement("div", {
3090
- className: "flex items-center space-x-3"
3091
- }, !isFirstStep && /* @__PURE__ */ React17.createElement("button", {
3092
- onClick: handlePrevious,
3093
- className: "px-4 py-2 rounded transition-colors flex items-center space-x-2",
3094
- style: secondaryButtonStyle
3095
- }, /* @__PURE__ */ React17.createElement(ChevronLeft, {
3096
- className: "w-4 h-4"
3097
- }), /* @__PURE__ */ React17.createElement("span", null, "Previous")), !currentInput.config.required && /* @__PURE__ */ React17.createElement("button", {
3098
- onClick: handleSkip,
3099
- className: "px-4 py-2 rounded transition-colors",
3100
- style: { ...secondaryButtonStyle, opacity: 0.9 }
3101
- }, "Skip"), /* @__PURE__ */ React17.createElement("button", {
3102
- onClick: handleNext,
3103
- className: "px-4 py-2 rounded transition-colors flex items-center space-x-2",
3104
- style: primaryButtonStyle
3105
- }, /* @__PURE__ */ React17.createElement("span", null, isLastStep ? "Complete" : "Next"), !isLastStep && /* @__PURE__ */ React17.createElement(ChevronRight, {
3106
- className: "w-4 h-4"
3107
- }))))));
3108
- };
3109
- var TemplateInputModal_default = TemplateInputModal;
3110
-
3111
- // src/store/simpleStore.ts
3112
- class SimpleCanvasStore {
3113
- _design;
3114
- _activePageId;
3115
- _activePanelId;
3116
- _listeners = {};
3117
- constructor(initialDesign) {
3118
- this._design = {
3119
- id: initialDesign?.id || `design-${Date.now()}`,
3120
- name: initialDesign?.name || "Untitled Design",
2963
+ }, /* @__PURE__ */ React17.createElement(Stage, {
2964
+ ref: stageRef,
2965
+ width: store.design.width,
2966
+ height: store.design.height,
2967
+ onClick: handleStageClick,
2968
+ onTap: handleStageClick
2969
+ }, /* @__PURE__ */ React17.createElement(Layer, null, /* @__PURE__ */ React17.createElement(React17.Fragment, null, /* @__PURE__ */ React17.createElement(Rect2, {
2970
+ x: 0,
2971
+ y: 0,
2972
+ width: store.design.width,
2973
+ height: store.design.height,
2974
+ fill: currentPage.backgroundImageObject ? undefined : currentPage.background,
2975
+ fillPatternImage: currentPage.backgroundImageObject,
2976
+ fillPatternRepeat: "no-repeat"
2977
+ }), currentPage.elements.map((element) => {
2978
+ switch (element.type) {
2979
+ case "text":
2980
+ return /* @__PURE__ */ React17.createElement(EditableTextElement, {
2981
+ key: element.id,
2982
+ element,
2983
+ isSelected: element.id === store.selectedId,
2984
+ onSelect: () => {
2985
+ store.setSelectedId(element.id);
2986
+ store.setSelectedIds([]);
2987
+ },
2988
+ onChange: (attrs) => updateElement(element.id, attrs)
2989
+ });
2990
+ case "image":
2991
+ return /* @__PURE__ */ React17.createElement(UrlImageElement, {
2992
+ key: element.id,
2993
+ element,
2994
+ isSelected: element.id === store.selectedId,
2995
+ onSelect: () => {
2996
+ store.setSelectedId(element.id);
2997
+ store.setSelectedIds([]);
2998
+ },
2999
+ onChange: (attrs) => updateElement(element.id, attrs)
3000
+ });
3001
+ case "shape":
3002
+ return /* @__PURE__ */ React17.createElement(ShapeElement, {
3003
+ key: element.id,
3004
+ element,
3005
+ isSelected: element.id === store.selectedId,
3006
+ onSelect: () => {
3007
+ store.setSelectedId(element.id);
3008
+ store.setSelectedIds([]);
3009
+ },
3010
+ onChange: (attrs) => updateElement(element.id, attrs)
3011
+ });
3012
+ default:
3013
+ return null;
3014
+ }
3015
+ })))))), config?.multiPage && /* @__PURE__ */ React17.createElement("div", {
3016
+ className: "absolute bottom-4 left-4 flex items-center space-x-2"
3017
+ }, store.design.pages.map((page, index) => /* @__PURE__ */ React17.createElement("button", {
3018
+ key: page.id,
3019
+ onClick: () => store.setActivePage(page.id),
3020
+ className: `px-3 py-1 rounded text-sm ${page.id === store.activePageId ? "bg-blue-600 text-white" : "bg-gray-800 text-gray-300 hover:bg-gray-700"}`
3021
+ }, "Page ", index + 1)))), /* @__PURE__ */ React17.createElement(RightSidebar_default, {
3022
+ currentPageElements: currentPage?.elements || [],
3023
+ selectedElement,
3024
+ selectedId: store.selectedId,
3025
+ onSelectElement: store.setSelectedId,
3026
+ onUpdateElement: updateElement,
3027
+ onDuplicateSelected: duplicateSelected,
3028
+ onMoveElementUp: moveElementUp,
3029
+ onMoveElementDown: moveElementDown,
3030
+ onDeleteSelected: deleteSelected
3031
+ })), /* @__PURE__ */ React17.createElement("input", {
3032
+ ref: fileInputRef,
3033
+ type: "file",
3034
+ accept: "image/*",
3035
+ onChange: handleImageUpload,
3036
+ style: { display: "none" }
3037
+ }), /* @__PURE__ */ React17.createElement("input", {
3038
+ ref: jsonInputRef,
3039
+ type: "file",
3040
+ accept: ".json",
3041
+ onChange: handleImportJSON,
3042
+ style: { display: "none" }
3043
+ }));
3044
+ };
3045
+ var CanvasEditor_default = CanvasEditor;
3046
+ // src/store/simpleStore.ts
3047
+ class SimpleCanvasStore {
3048
+ _design;
3049
+ _activePageId;
3050
+ _activePanelId;
3051
+ _selectedId;
3052
+ _selectedIds;
3053
+ _tool;
3054
+ _zoom;
3055
+ _selectedShape;
3056
+ _listeners = {};
3057
+ constructor(initialDesign) {
3058
+ this._design = {
3059
+ id: initialDesign?.id || `design-${Date.now()}`,
3060
+ name: initialDesign?.name || "Untitled Design",
3121
3061
  width: initialDesign?.width || 800,
3122
3062
  height: initialDesign?.height || 600,
3123
3063
  pages: initialDesign?.pages || [],
@@ -3127,6 +3067,11 @@ class SimpleCanvasStore {
3127
3067
  };
3128
3068
  this._activePageId = initialDesign?.pages?.[0]?.id || null;
3129
3069
  this._activePanelId = null;
3070
+ this._selectedId = null;
3071
+ this._selectedIds = [];
3072
+ this._tool = "select";
3073
+ this._zoom = 1;
3074
+ this._selectedShape = "rect";
3130
3075
  }
3131
3076
  on(event, callback) {
3132
3077
  this._listeners[event] = callback;
@@ -3166,12 +3111,27 @@ class SimpleCanvasStore {
3166
3111
  get activePanelId() {
3167
3112
  return this._activePanelId;
3168
3113
  }
3114
+ get selectedId() {
3115
+ return this._selectedId;
3116
+ }
3117
+ get selectedIds() {
3118
+ return this._selectedIds;
3119
+ }
3120
+ get tool() {
3121
+ return this._tool;
3122
+ }
3123
+ get zoom() {
3124
+ return this._zoom;
3125
+ }
3126
+ get selectedShape() {
3127
+ return this._selectedShape;
3128
+ }
3169
3129
  setDesign(design) {
3170
3130
  this._design = design;
3171
3131
  if (design.pages.find((p) => p.id === this._activePageId)) {} else if (design.pages.length > 0) {
3172
3132
  this._activePageId = design.pages[0].id;
3173
3133
  }
3174
- this.emit("designChanged", design);
3134
+ this.emit("designChanged", this._design);
3175
3135
  this.emit("activePageChanged", this._activePageId);
3176
3136
  }
3177
3137
  updateDesign(updates) {
@@ -3213,14 +3173,6 @@ class SimpleCanvasStore {
3213
3173
  this._design.pages = remainingPages;
3214
3174
  this.emit("designChanged", this._design);
3215
3175
  }
3216
- setActivePage(pageId) {
3217
- const page = this._design.pages.find((p) => p.id === pageId);
3218
- if (page) {
3219
- this._activePageId = pageId;
3220
- this.emit("activePageChanged", pageId);
3221
- this.emit("designChanged", this._design);
3222
- }
3223
- }
3224
3176
  addElement(element) {
3225
3177
  const page = this.activePage;
3226
3178
  if (!page) {
@@ -3242,34 +3194,32 @@ class SimpleCanvasStore {
3242
3194
  ...element
3243
3195
  };
3244
3196
  page.elements.push(newElement);
3245
- console.log(`addElement: Added element ${newElement.id} to page. Total elements: ${page.elements.length}`);
3246
3197
  this.emit("designChanged", this._design);
3247
- console.log("addElement: Emitted designChanged event");
3248
- }
3249
- updateElement(elementId, updates) {
3250
- for (const page of this._design.pages) {
3251
- const elementIndex = page.elements.findIndex((e) => e.id === elementId);
3252
- if (elementIndex !== -1) {
3253
- page.elements[elementIndex] = { ...page.elements[elementIndex], ...updates };
3254
- this.emit("designChanged", this._design);
3255
- return;
3256
- }
3257
- }
3258
- }
3259
- deleteElement(elementId) {
3260
- for (const page of this._design.pages) {
3261
- const elementIndex = page.elements.findIndex((e) => e.id === elementId);
3262
- if (elementIndex !== -1) {
3263
- page.elements.splice(elementIndex, 1);
3264
- this.emit("designChanged", this._design);
3265
- return;
3266
- }
3267
- }
3268
3198
  }
3269
3199
  openSidePanel(panelId) {
3270
3200
  this._activePanelId = panelId;
3271
3201
  this.emit("activePanelChanged", panelId);
3272
3202
  }
3203
+ setSelectedId(selectedId) {
3204
+ this._selectedId = selectedId;
3205
+ this.emit("selectedIdChanged", selectedId);
3206
+ }
3207
+ setSelectedIds(selectedIds) {
3208
+ this._selectedIds = selectedIds;
3209
+ this.emit("selectedIdsChanged", selectedIds);
3210
+ }
3211
+ setTool(tool) {
3212
+ this._tool = tool;
3213
+ this.emit("toolChanged", tool);
3214
+ }
3215
+ setZoom(zoom) {
3216
+ this._zoom = zoom;
3217
+ this.emit("zoomChanged", zoom);
3218
+ }
3219
+ setSelectedShape(shape) {
3220
+ this._selectedShape = shape;
3221
+ this.emit("selectedShapeChanged", shape);
3222
+ }
3273
3223
  async toBlob(type = "png") {
3274
3224
  return new Promise((resolve, reject) => {
3275
3225
  try {
@@ -3299,6 +3249,34 @@ class SimpleCanvasStore {
3299
3249
  }
3300
3250
  });
3301
3251
  }
3252
+ setActivePage(pageId) {
3253
+ const page = this._design.pages.find((p) => p.id === pageId);
3254
+ if (page) {
3255
+ this._activePageId = pageId;
3256
+ this.emit("activePageChanged", pageId);
3257
+ this.emit("designChanged", this._design);
3258
+ }
3259
+ }
3260
+ updateElement(elementId, updates) {
3261
+ for (const page of this._design.pages) {
3262
+ const elementIndex = page.elements.findIndex((e) => e.id === elementId);
3263
+ if (elementIndex !== -1) {
3264
+ page.elements[elementIndex] = { ...page.elements[elementIndex], ...updates };
3265
+ this.emit("designChanged", this._design);
3266
+ return;
3267
+ }
3268
+ }
3269
+ }
3270
+ deleteElement(elementId) {
3271
+ for (const page of this._design.pages) {
3272
+ const elementIndex = page.elements.findIndex((e) => e.id === elementId);
3273
+ if (elementIndex !== -1) {
3274
+ page.elements.splice(elementIndex, 1);
3275
+ this.emit("designChanged", this._design);
3276
+ return;
3277
+ }
3278
+ }
3279
+ }
3302
3280
  async loadJSON(jsonData) {
3303
3281
  return new Promise((resolve, reject) => {
3304
3282
  try {
@@ -3321,843 +3299,6 @@ class SimpleCanvasStore {
3321
3299
  var createSimpleStore = (initialDesign) => {
3322
3300
  return new SimpleCanvasStore(initialDesign);
3323
3301
  };
3324
-
3325
- // src/CanvasEditor.tsx
3326
- var CanvasEditor = ({
3327
- name,
3328
- config,
3329
- store: externalStore
3330
- }) => {
3331
- console.log("=== CanvasEditor initializing ===", { hasExternalStore: !!externalStore });
3332
- const store = externalStore || createSimpleStore({
3333
- name,
3334
- width: 800,
3335
- height: 600
3336
- });
3337
- const [design, setDesign] = useState3(() => {
3338
- if (externalStore) {
3339
- return externalStore.design;
3340
- }
3341
- return {
3342
- id: Date.now().toString(),
3343
- name: "Untitled Design",
3344
- width: 800,
3345
- height: 600,
3346
- pages: [
3347
- {
3348
- id: "1",
3349
- name: "Page 1",
3350
- elements: [],
3351
- background: "#ffffff"
3352
- }
3353
- ],
3354
- fonts: FONT_FAMILIES,
3355
- colors: DEFAULT_COLORS
3356
- };
3357
- });
3358
- const [currentPageId, setCurrentPageId] = useState3(() => {
3359
- if (externalStore?.design.pages && externalStore.design.pages.length > 0) {
3360
- return externalStore.design.pages[0].id;
3361
- }
3362
- if (externalStore?.activePageId) {
3363
- return externalStore.activePageId;
3364
- }
3365
- return "1";
3366
- });
3367
- const [selectedId, setSelectedId] = useState3(null);
3368
- const [selectedIds, setSelectedIds] = useState3([]);
3369
- const [tool, setTool] = useState3("select");
3370
- const [activePanelId, setActivePanelId] = useState3("elements");
3371
- useEffect4(() => {
3372
- if (externalStore) {
3373
- const handleDesignChange = (newDesign) => {
3374
- console.log("CanvasEditor received designChanged:", newDesign.pages[0]?.elements.length, "elements on page 1");
3375
- setDesign(newDesign);
3376
- };
3377
- const handleActivePageChange = (pageId) => {
3378
- setCurrentPageId(pageId);
3379
- };
3380
- const handleActivePanelChange = (panelId) => {
3381
- setActivePanelId(panelId || "elements");
3382
- };
3383
- externalStore.on("designChanged", handleDesignChange);
3384
- externalStore.on("activePageChanged", handleActivePageChange);
3385
- externalStore.on("activePanelChanged", handleActivePanelChange);
3386
- return () => {
3387
- externalStore.off("designChanged");
3388
- externalStore.off("activePageChanged");
3389
- externalStore.off("activePanelChanged");
3390
- };
3391
- }
3392
- }, [externalStore]);
3393
- const [showUnsplash, setShowUnsplash] = useState3(false);
3394
- const [unsplashQuery, setUnsplashQuery] = useState3("");
3395
- const [unsplashResults, setUnsplashResults] = useState3([]);
3396
- const [unsplashMode, setUnsplashMode] = useState3("element");
3397
- const [zoom, setZoom] = useState3(1);
3398
- const [selectedShape, setSelectedShape] = useState3("rect");
3399
- const [history, setHistory] = useState3([]);
3400
- const [historyIndex, setHistoryIndex] = useState3(-1);
3401
- const [showInputModal, setShowInputModal] = useState3(false);
3402
- const [pendingInputs, setPendingInputs] = useState3([]);
3403
- const stageRef = useRef5(null);
3404
- const fileInputRef = useRef5(null);
3405
- const jsonInputRef = useRef5(null);
3406
- const containerRef = useRef5(null);
3407
- const currentPage = design.pages.find((p) => p.id === currentPageId) || design.pages[0] || null;
3408
- const selectedElement = currentPage?.elements.find((el) => el.id === selectedId) || null;
3409
- const saveToHistory = useCallback4((newDesign) => {
3410
- const newHistory = history.slice(0, historyIndex + 1);
3411
- newHistory.push(JSON.parse(JSON.stringify(newDesign)));
3412
- setHistory(newHistory);
3413
- setHistoryIndex(newHistory.length - 1);
3414
- }, [history, historyIndex]);
3415
- const undo = useCallback4(() => {
3416
- if (historyIndex > 0) {
3417
- setHistoryIndex(historyIndex - 1);
3418
- setDesign(JSON.parse(JSON.stringify(history[historyIndex - 1])));
3419
- }
3420
- }, [history, historyIndex]);
3421
- const redo = useCallback4(() => {
3422
- if (historyIndex < history.length - 1) {
3423
- setHistoryIndex(historyIndex + 1);
3424
- setDesign(JSON.parse(JSON.stringify(history[historyIndex + 1])));
3425
- }
3426
- }, [history, historyIndex]);
3427
- const addText = useCallback4(() => {
3428
- const newElement = {
3429
- id: Date.now().toString(),
3430
- type: "text",
3431
- name: "Text",
3432
- x: design.width / 2 - 50,
3433
- y: design.height / 2 - 20,
3434
- rotation: 0,
3435
- visible: true,
3436
- locked: false,
3437
- opacity: 1,
3438
- text: "Click to edit text",
3439
- fontSize: 24,
3440
- fontFamily: "Arial",
3441
- fill: "#000000",
3442
- align: "left",
3443
- lineHeight: 1.2,
3444
- letterSpacing: 0,
3445
- textCase: "none",
3446
- strikethrough: false
3447
- };
3448
- const newDesign = {
3449
- ...design,
3450
- pages: design.pages.map((page) => page.id === currentPageId ? { ...page, elements: [...page.elements, newElement] } : page)
3451
- };
3452
- setDesign(newDesign);
3453
- saveToHistory(newDesign);
3454
- if (externalStore) {
3455
- externalStore.setDesign(newDesign);
3456
- }
3457
- setSelectedId(newElement.id);
3458
- setTool("select");
3459
- }, [currentPageId, saveToHistory, design.width, design.height, externalStore]);
3460
- const addShape = useCallback4((shapeType) => {
3461
- const newElement = {
3462
- id: Date.now().toString(),
3463
- type: "shape",
3464
- name: shapeType.charAt(0).toUpperCase() + shapeType.slice(1),
3465
- x: design.width / 2 - 50,
3466
- y: design.height / 2 - 50,
3467
- width: 100,
3468
- height: 100,
3469
- rotation: 0,
3470
- visible: true,
3471
- locked: false,
3472
- opacity: 1,
3473
- fill: "#4299e1",
3474
- stroke: "#000000",
3475
- strokeWidth: 0,
3476
- shapeType,
3477
- sides: shapeType === "polygon" ? 6 : undefined,
3478
- innerRadius: shapeType === "star" ? 40 : undefined,
3479
- outerRadius: shapeType === "star" ? 70 : undefined
3480
- };
3481
- setDesign((prev) => {
3482
- const newDesign = {
3483
- ...prev,
3484
- pages: prev.pages.map((page) => page.id === currentPageId ? { ...page, elements: [...page.elements, newElement] } : page)
3485
- };
3486
- saveToHistory(newDesign);
3487
- return newDesign;
3488
- });
3489
- setSelectedId(newElement.id);
3490
- }, [currentPageId, saveToHistory, design.width, design.height]);
3491
- const handleStageClick = useCallback4((e) => {
3492
- const clickedOnEmpty = e.target === e.target.getStage();
3493
- if (clickedOnEmpty) {
3494
- setSelectedId(null);
3495
- setSelectedIds([]);
3496
- }
3497
- }, []);
3498
- const updateElement = useCallback4((id, attrs) => {
3499
- setDesign((prev) => ({
3500
- ...prev,
3501
- pages: prev.pages.map((page) => ({
3502
- ...page,
3503
- elements: page.elements.map((el) => el.id === id ? { ...el, ...attrs } : el)
3504
- }))
3505
- }));
3506
- }, []);
3507
- const deleteSelected = useCallback4(() => {
3508
- if (selectedId || selectedIds.length > 0) {
3509
- const idsToDelete = selectedIds.length > 0 ? selectedIds : [selectedId];
3510
- setDesign((prev) => ({
3511
- ...prev,
3512
- pages: prev.pages.map((page) => ({
3513
- ...page,
3514
- elements: page.elements.filter((el) => !idsToDelete.includes(el.id))
3515
- }))
3516
- }));
3517
- setSelectedId(null);
3518
- setSelectedIds([]);
3519
- saveToHistory(design);
3520
- }
3521
- }, [selectedId, selectedIds, saveToHistory, design]);
3522
- const duplicateSelected = useCallback4(() => {
3523
- if (selectedElement) {
3524
- const newElement = {
3525
- ...selectedElement,
3526
- id: Date.now().toString(),
3527
- name: selectedElement.name + " copy",
3528
- x: selectedElement.x + 20,
3529
- y: selectedElement.y + 20
3530
- };
3531
- setDesign((prev) => {
3532
- const newDesign = {
3533
- ...prev,
3534
- pages: prev.pages.map((page) => page.id === currentPageId ? { ...page, elements: [...page.elements, newElement] } : page)
3535
- };
3536
- saveToHistory(newDesign);
3537
- return newDesign;
3538
- });
3539
- setSelectedId(newElement.id);
3540
- }
3541
- }, [selectedElement, currentPageId, saveToHistory]);
3542
- const moveElementUp = useCallback4((id) => {
3543
- setDesign((prev) => ({
3544
- ...prev,
3545
- pages: prev.pages.map((page) => {
3546
- if (page.id === currentPageId) {
3547
- const index = page.elements.findIndex((el) => el.id === id);
3548
- if (index < page.elements.length - 1) {
3549
- const newElements = [...page.elements];
3550
- [newElements[index], newElements[index + 1]] = [
3551
- newElements[index + 1],
3552
- newElements[index]
3553
- ];
3554
- return { ...page, elements: newElements };
3555
- }
3556
- }
3557
- return page;
3558
- })
3559
- }));
3560
- }, [currentPageId]);
3561
- const moveElementDown = useCallback4((id) => {
3562
- setDesign((prev) => ({
3563
- ...prev,
3564
- pages: prev.pages.map((page) => {
3565
- if (page.id === currentPageId) {
3566
- const index = page.elements.findIndex((el) => el.id === id);
3567
- if (index > 0) {
3568
- const newElements = [...page.elements];
3569
- [newElements[index], newElements[index - 1]] = [
3570
- newElements[index - 1],
3571
- newElements[index]
3572
- ];
3573
- return { ...page, elements: newElements };
3574
- }
3575
- }
3576
- return page;
3577
- })
3578
- }));
3579
- }, [currentPageId]);
3580
- const addPage = useCallback4(() => {
3581
- const newPage = {
3582
- id: Date.now().toString(),
3583
- name: `Page ${design.pages.length + 1}`,
3584
- elements: [],
3585
- background: "#ffffff",
3586
- backgroundImageObject: undefined
3587
- };
3588
- setDesign((prev) => ({
3589
- ...prev,
3590
- pages: [...prev.pages, newPage]
3591
- }));
3592
- setCurrentPageId(newPage.id);
3593
- }, [design.pages.length]);
3594
- const deletePage = useCallback4((pageId) => {
3595
- if (design.pages.length > 1) {
3596
- setDesign((prev) => ({
3597
- ...prev,
3598
- pages: prev.pages.filter((p) => p.id !== pageId)
3599
- }));
3600
- if (currentPageId === pageId) {
3601
- setCurrentPageId(design.pages[0].id);
3602
- }
3603
- }
3604
- }, [design.pages, currentPageId]);
3605
- const handleImageUpload = useCallback4((e) => {
3606
- const file = e.target.files?.[0];
3607
- if (file) {
3608
- const reader = new FileReader;
3609
- reader.onload = (event) => {
3610
- const img = new window.Image;
3611
- img.src = event.target?.result;
3612
- img.onload = () => {
3613
- const newElement = {
3614
- id: Date.now().toString(),
3615
- type: "image",
3616
- name: file.name,
3617
- x: 100,
3618
- y: 100,
3619
- width: img.naturalWidth,
3620
- height: img.naturalHeight,
3621
- rotation: 0,
3622
- visible: true,
3623
- locked: false,
3624
- opacity: 1,
3625
- src: event.target?.result,
3626
- cropX: 0,
3627
- cropY: 0,
3628
- cropWidth: img.naturalWidth,
3629
- cropHeight: img.naturalHeight,
3630
- mask: undefined
3631
- };
3632
- setDesign((prev) => ({
3633
- ...prev,
3634
- pages: prev.pages.map((page) => page.id === currentPageId ? { ...page, elements: [...page.elements, newElement] } : page)
3635
- }));
3636
- saveToHistory(design);
3637
- };
3638
- };
3639
- reader.readAsDataURL(file);
3640
- }
3641
- if (fileInputRef.current) {
3642
- fileInputRef.current.value = "";
3643
- }
3644
- }, [currentPageId, saveToHistory]);
3645
- const searchUnsplash = useCallback4(async () => {
3646
- if (!unsplashQuery)
3647
- return;
3648
- if (UNSPLASH_ACCESS_KEY === "YOUR_UNSPLASH_ACCESS_KEY_HERE" || !UNSPLASH_ACCESS_KEY) {
3649
- console.warn("Unsplash API key is not configured. Please set UNSPLASH_ACCESS_KEY in src/config.ts");
3650
- const mockResults = Array(9).fill(null).map((_, i) => ({
3651
- id: `unsplash-mock-${i}`,
3652
- urls: {
3653
- regular: `https://source.unsplash.com/400x300/?${unsplashQuery}&sig=${i}`,
3654
- thumb: `https://source.unsplash.com/200x150/?${unsplashQuery}&sig=${i}`
3655
- },
3656
- user: { name: "Mock User" }
3657
- }));
3658
- setUnsplashResults(mockResults);
3659
- return;
3660
- }
3661
- try {
3662
- const response = await fetch(`https://api.unsplash.com/search/photos?query=${encodeURIComponent(unsplashQuery)}&per_page=9`, {
3663
- headers: {
3664
- Authorization: `Client-ID ${UNSPLASH_ACCESS_KEY}`
3665
- }
3666
- });
3667
- if (!response.ok) {
3668
- console.error("Failed to fetch from Unsplash:", response.statusText);
3669
- setUnsplashResults([]);
3670
- return;
3671
- }
3672
- const data = await response.json();
3673
- setUnsplashResults(data.results.map((img) => ({
3674
- id: img.id,
3675
- urls: { regular: img.urls.regular, thumb: img.urls.thumb },
3676
- user: { name: img.user.name }
3677
- })));
3678
- } catch (error) {
3679
- console.error("Error searching Unsplash:", error);
3680
- setUnsplashResults([]);
3681
- }
3682
- }, [unsplashQuery]);
3683
- const addUnsplashImage = useCallback4((imageUrl) => {
3684
- if (unsplashMode === "element") {
3685
- const img = new window.Image;
3686
- img.crossOrigin = "anonymous";
3687
- img.src = imageUrl;
3688
- img.onload = () => {
3689
- const newElement = {
3690
- id: Date.now().toString(),
3691
- type: "image",
3692
- name: "Unsplash Image",
3693
- x: 100,
3694
- y: 100,
3695
- width: img.naturalWidth,
3696
- height: img.naturalHeight,
3697
- rotation: 0,
3698
- visible: true,
3699
- locked: false,
3700
- opacity: 1,
3701
- src: imageUrl,
3702
- cropX: 0,
3703
- cropY: 0,
3704
- cropWidth: img.naturalWidth,
3705
- cropHeight: img.naturalHeight,
3706
- mask: undefined
3707
- };
3708
- setDesign((prev) => ({
3709
- ...prev,
3710
- pages: prev.pages.map((page) => page.id === currentPageId ? { ...page, elements: [...page.elements, newElement] } : page)
3711
- }));
3712
- saveToHistory(design);
3713
- };
3714
- } else if (unsplashMode === "background") {
3715
- const bgImg = new window.Image;
3716
- bgImg.crossOrigin = "anonymous";
3717
- bgImg.src = imageUrl;
3718
- bgImg.onload = () => {
3719
- setDesign((prev) => ({
3720
- ...prev,
3721
- pages: prev.pages.map((p) => p.id === currentPageId ? {
3722
- ...p,
3723
- background: `url(${imageUrl})`,
3724
- backgroundImageObject: bgImg
3725
- } : p)
3726
- }));
3727
- saveToHistory(design);
3728
- };
3729
- }
3730
- setShowUnsplash(false);
3731
- setUnsplashQuery("");
3732
- setUnsplashResults([]);
3733
- setUnsplashMode("element");
3734
- }, [currentPageId, saveToHistory, unsplashMode]);
3735
- const handleImportJSON = (event) => {
3736
- importFromJSON(event, (newDesign) => {
3737
- if (newDesign && newDesign.pages && newDesign.pages.length > 0) {
3738
- setDesign(newDesign);
3739
- setSelectedId(null);
3740
- setSelectedIds([]);
3741
- const firstPageId = newDesign.pages[0]?.id;
3742
- setCurrentPageId(firstPageId || "1");
3743
- saveToHistory(newDesign);
3744
- } else {
3745
- alert("Invalid design structure in JSON file. Could not load.");
3746
- }
3747
- }, (errorMessage) => {
3748
- alert(`Error importing JSON: ${errorMessage}`);
3749
- }, (inputs, onComplete) => {
3750
- setPendingInputs(inputs);
3751
- setShowInputModal(true);
3752
- window.__pendingImportComplete = onComplete;
3753
- });
3754
- if (jsonInputRef.current) {
3755
- jsonInputRef.current.value = "";
3756
- }
3757
- };
3758
- const handleInputModalComplete = (values) => {
3759
- setShowInputModal(false);
3760
- const onComplete = window.__pendingImportComplete;
3761
- if (onComplete) {
3762
- onComplete(values);
3763
- delete window.__pendingImportComplete;
3764
- }
3765
- setPendingInputs([]);
3766
- };
3767
- const handleInputModalCancel = () => {
3768
- setShowInputModal(false);
3769
- setPendingInputs([]);
3770
- delete window.__pendingImportComplete;
3771
- };
3772
- const panelConfigs = useMemo(() => {
3773
- const builtInConfigs = {
3774
- elements: {
3775
- id: "elements",
3776
- title: "Elements",
3777
- component: ElementPanel_default,
3778
- props: { onAddShape: addShape, store }
3779
- },
3780
- text: {
3781
- id: "text",
3782
- title: "Text",
3783
- component: TextPanel_default,
3784
- props: {
3785
- selectedElement,
3786
- updateElement,
3787
- setTool,
3788
- onAddText: addText,
3789
- store
3790
- }
3791
- },
3792
- image: {
3793
- id: "image",
3794
- title: "Images",
3795
- component: ImagePanel_default,
3796
- props: {
3797
- selectedElement,
3798
- updateElement,
3799
- onUploadClick: () => fileInputRef.current?.click(),
3800
- onUnsplashClick: () => {
3801
- setShowUnsplash(true);
3802
- setUnsplashMode("element");
3803
- },
3804
- canvasWidth: design.width,
3805
- canvasHeight: design.height,
3806
- store
3807
- }
3808
- },
3809
- design: {
3810
- id: "design",
3811
- title: "Design",
3812
- component: DesignPanel_default,
3813
- props: {
3814
- design,
3815
- currentPage,
3816
- selectedElement,
3817
- setDesign,
3818
- updateElement,
3819
- onSetUnsplashBackground: () => {
3820
- setShowUnsplash(true);
3821
- setUnsplashMode("background");
3822
- },
3823
- config,
3824
- store
3825
- }
3826
- },
3827
- background: {
3828
- id: "background",
3829
- title: "Background",
3830
- component: BackgroundPanel_default,
3831
- props: {
3832
- design,
3833
- currentPage,
3834
- setDesign,
3835
- onSetUnsplashBackground: () => {
3836
- setShowUnsplash(true);
3837
- setUnsplashMode("background");
3838
- },
3839
- store
3840
- }
3841
- },
3842
- ...config?.variables ? {
3843
- variables: {
3844
- id: "variables",
3845
- title: "Variables",
3846
- component: VariablesPanel_default,
3847
- props: {
3848
- config,
3849
- design,
3850
- setDesign,
3851
- store
3852
- }
3853
- }
3854
- } : {},
3855
- ...config?.export ? {
3856
- export: {
3857
- id: "export",
3858
- title: "Export",
3859
- component: ExportPanel_default,
3860
- props: {
3861
- onExportToPNG: config.export?.png ? () => {
3862
- if (stageRef.current)
3863
- exportToPNG(stageRef.current, design);
3864
- } : undefined,
3865
- onExportToJPG: config.export?.jpg ? () => {
3866
- if (stageRef.current)
3867
- exportToJPG(stageRef.current, design);
3868
- } : undefined,
3869
- onExportToJSON: config.export?.json ? () => exportToJSON(design) : undefined,
3870
- onImportJSON: () => jsonInputRef.current?.click(),
3871
- store
3872
- }
3873
- }
3874
- } : {}
3875
- };
3876
- if (!config?.panels || config.panels.length === 0) {
3877
- return [
3878
- "text",
3879
- "elements",
3880
- "image",
3881
- "design",
3882
- "background",
3883
- ...config?.variables ? ["variables"] : [],
3884
- ...config?.export ? ["export"] : []
3885
- ].filter((id) => builtInConfigs[id]).map((id) => builtInConfigs[id]);
3886
- }
3887
- const ordered = [];
3888
- for (const panel of config.panels) {
3889
- if (typeof panel === "string") {
3890
- const config2 = builtInConfigs[panel];
3891
- if (config2) {
3892
- ordered.push(config2);
3893
- }
3894
- } else {
3895
- ordered.push({
3896
- id: panel.id,
3897
- title: panel.title,
3898
- component: panel.component,
3899
- props: { ...panel.props, store }
3900
- });
3901
- }
3902
- }
3903
- return ordered;
3904
- }, [
3905
- addShape,
3906
- addText,
3907
- config,
3908
- currentPage,
3909
- design,
3910
- selectedElement,
3911
- setTool,
3912
- updateElement,
3913
- store
3914
- ]);
3915
- const DynamicPanelRenderer = () => {
3916
- const activePanelConfig = panelConfigs.find((p) => p.id === activePanelId);
3917
- if (!activePanelConfig) {
3918
- return /* @__PURE__ */ React18.createElement("div", {
3919
- className: "text-white"
3920
- }, "Panel not found");
3921
- }
3922
- const PanelComponent = activePanelConfig.component;
3923
- return /* @__PURE__ */ React18.createElement(PanelComponent, {
3924
- ...activePanelConfig.props
3925
- });
3926
- };
3927
- useEffect4(() => {
3928
- const handleKeyDown = (e) => {
3929
- if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement)
3930
- return;
3931
- if (e.metaKey || e.ctrlKey) {
3932
- switch (e.key) {
3933
- case "z":
3934
- if (e.shiftKey) {
3935
- redo();
3936
- } else {
3937
- undo();
3938
- }
3939
- e.preventDefault();
3940
- break;
3941
- case "c":
3942
- break;
3943
- case "v":
3944
- break;
3945
- case "d":
3946
- duplicateSelected();
3947
- e.preventDefault();
3948
- break;
3949
- case "s":
3950
- exportToJSON(design);
3951
- e.preventDefault();
3952
- break;
3953
- }
3954
- } else {
3955
- switch (e.key) {
3956
- case "Delete":
3957
- case "Backspace":
3958
- deleteSelected();
3959
- break;
3960
- case "Escape":
3961
- setSelectedId(null);
3962
- setSelectedIds([]);
3963
- break;
3964
- }
3965
- }
3966
- };
3967
- window.addEventListener("keydown", handleKeyDown);
3968
- return () => window.removeEventListener("keydown", handleKeyDown);
3969
- }, [deleteSelected, duplicateSelected, exportToJSON, undo, redo]);
3970
- useEffect4(() => {
3971
- if (history.length === 0) {
3972
- setHistory([JSON.parse(JSON.stringify(design))]);
3973
- setHistoryIndex(0);
3974
- }
3975
- }, []);
3976
- return /* @__PURE__ */ React18.createElement("div", {
3977
- className: "h-screen flex flex-col bg-gray-900"
3978
- }, /* @__PURE__ */ React18.createElement(Header_default, {
3979
- name,
3980
- zoom,
3981
- historyIndex,
3982
- historyLength: history.length,
3983
- onUndo: undo,
3984
- onRedo: redo,
3985
- onZoomIn: () => setZoom(Math.min(3, zoom + 0.1)),
3986
- onZoomOut: () => setZoom(Math.max(0.1, zoom - 0.1)),
3987
- onZoomReset: () => setZoom(1),
3988
- onSetActivePanel: setActivePanelId
3989
- }), /* @__PURE__ */ React18.createElement("div", {
3990
- className: "flex-1 flex overflow-hidden"
3991
- }, /* @__PURE__ */ React18.createElement("div", {
3992
- className: "w-80 bg-gray-800 border-r border-gray-700 flex"
3993
- }, /* @__PURE__ */ React18.createElement(LeftMenu_default, {
3994
- tool,
3995
- activePanel: activePanelId,
3996
- onSetTool: setTool,
3997
- onSetActivePanel: setActivePanelId,
3998
- config
3999
- }), /* @__PURE__ */ React18.createElement("div", {
4000
- className: "flex-1 p-4 overflow-y-auto"
4001
- }, /* @__PURE__ */ React18.createElement(DynamicPanelRenderer, null))), /* @__PURE__ */ React18.createElement("div", {
4002
- className: "flex-1 bg-gray-700 overflow-hidden relative",
4003
- ref: containerRef
4004
- }, !currentPage ? /* @__PURE__ */ React18.createElement("div", {
4005
- className: "absolute inset-0 flex items-center justify-center text-gray-400"
4006
- }, /* @__PURE__ */ React18.createElement("div", {
4007
- className: "text-center"
4008
- }, /* @__PURE__ */ React18.createElement("p", {
4009
- className: "mb-4"
4010
- }, "No pages available"), /* @__PURE__ */ React18.createElement("button", {
4011
- onClick: () => store.addPage(),
4012
- className: "px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700"
4013
- }, "Add Page"))) : /* @__PURE__ */ React18.createElement("div", {
4014
- className: "absolute inset-0 flex items-center justify-center"
4015
- }, /* @__PURE__ */ React18.createElement("div", {
4016
- className: "bg-white shadow-2xl",
4017
- style: {
4018
- transform: `scale(${zoom})`,
4019
- transformOrigin: "center center"
4020
- }
4021
- }, /* @__PURE__ */ React18.createElement(Stage, {
4022
- ref: stageRef,
4023
- width: design.width,
4024
- height: design.height,
4025
- onClick: handleStageClick,
4026
- onTap: handleStageClick
4027
- }, /* @__PURE__ */ React18.createElement(Layer, null, /* @__PURE__ */ React18.createElement(React18.Fragment, null, /* @__PURE__ */ React18.createElement(Rect2, {
4028
- x: 0,
4029
- y: 0,
4030
- width: design.width,
4031
- height: design.height,
4032
- fill: currentPage.backgroundImageObject ? undefined : currentPage.background,
4033
- fillPatternImage: currentPage.backgroundImageObject,
4034
- fillPatternRepeat: "no-repeat"
4035
- }), currentPage.elements.map((element) => {
4036
- switch (element.type) {
4037
- case "text":
4038
- return /* @__PURE__ */ React18.createElement(EditableTextElement, {
4039
- key: element.id,
4040
- element,
4041
- isSelected: element.id === selectedId,
4042
- onSelect: () => {
4043
- setSelectedId(element.id);
4044
- setSelectedIds([]);
4045
- },
4046
- onChange: (attrs) => updateElement(element.id, attrs)
4047
- });
4048
- case "image":
4049
- return /* @__PURE__ */ React18.createElement(UrlImageElement, {
4050
- key: element.id,
4051
- element,
4052
- isSelected: element.id === selectedId,
4053
- onSelect: () => {
4054
- setSelectedId(element.id);
4055
- setSelectedIds([]);
4056
- },
4057
- onChange: (attrs) => updateElement(element.id, attrs)
4058
- });
4059
- case "shape":
4060
- return /* @__PURE__ */ React18.createElement(ShapeElement, {
4061
- key: element.id,
4062
- element,
4063
- isSelected: element.id === selectedId,
4064
- onSelect: () => {
4065
- setSelectedId(element.id);
4066
- setSelectedIds([]);
4067
- },
4068
- onChange: (attrs) => updateElement(element.id, attrs)
4069
- });
4070
- default:
4071
- return null;
4072
- }
4073
- })))))), " ", config?.multiPage && /* @__PURE__ */ React18.createElement("div", {
4074
- className: "absolute bottom-4 left-4 flex items-center space-x-2"
4075
- }, design.pages.map((page, index) => /* @__PURE__ */ React18.createElement("button", {
4076
- key: page.id,
4077
- onClick: () => setCurrentPageId(page.id),
4078
- className: `px-3 py-1 rounded text-sm ${page.id === currentPageId ? "bg-blue-600 text-white" : "bg-gray-800 text-gray-300 hover:bg-gray-700"}`
4079
- }, "Page ", index + 1)), /* @__PURE__ */ React18.createElement("button", {
4080
- onClick: addPage,
4081
- className: "p-1 bg-gray-800 text-gray-300 rounded hover:bg-gray-700",
4082
- title: "Add Page"
4083
- }, /* @__PURE__ */ React18.createElement(PlusCircle, {
4084
- className: "w-4 h-4"
4085
- })))), /* @__PURE__ */ React18.createElement(RightSidebar_default, {
4086
- currentPageElements: currentPage?.elements || [],
4087
- selectedElement,
4088
- selectedId,
4089
- onSelectElement: setSelectedId,
4090
- onUpdateElement: updateElement,
4091
- onDuplicateSelected: duplicateSelected,
4092
- onMoveElementUp: moveElementUp,
4093
- onMoveElementDown: moveElementDown,
4094
- onDeleteSelected: deleteSelected
4095
- })), /* @__PURE__ */ React18.createElement("input", {
4096
- ref: fileInputRef,
4097
- type: "file",
4098
- accept: "image/*",
4099
- onChange: handleImageUpload,
4100
- className: "hidden",
4101
- multiple: true
4102
- }), /* @__PURE__ */ React18.createElement("input", {
4103
- ref: jsonInputRef,
4104
- type: "file",
4105
- accept: ".json",
4106
- onChange: handleImportJSON,
4107
- className: "hidden"
4108
- }), showInputModal && /* @__PURE__ */ React18.createElement(TemplateInputModal_default, {
4109
- inputs: pendingInputs,
4110
- onComplete: handleInputModalComplete,
4111
- onCancel: handleInputModalCancel
4112
- }), showUnsplash && /* @__PURE__ */ React18.createElement("div", {
4113
- className: "fixed inset-0 bg-black bg-opacity-75 flex items-center justify-center z-50 p-8"
4114
- }, /* @__PURE__ */ React18.createElement("div", {
4115
- className: "bg-gray-800 rounded-lg w-full max-w-4xl max-h-full overflow-hidden flex flex-col"
4116
- }, /* @__PURE__ */ React18.createElement("div", {
4117
- className: "p-6 border-b border-gray-700"
4118
- }, /* @__PURE__ */ React18.createElement("h3", {
4119
- className: "text-xl font-semibold text-white mb-4"
4120
- }, "Search Unsplash"), /* @__PURE__ */ React18.createElement("div", {
4121
- className: "flex space-x-4"
4122
- }, /* @__PURE__ */ React18.createElement("input", {
4123
- type: "text",
4124
- value: unsplashQuery,
4125
- onChange: (e) => setUnsplashQuery(e.target.value),
4126
- onKeyDown: (e) => e.key === "Enter" && searchUnsplash(),
4127
- placeholder: "Search for images...",
4128
- className: "flex-1 bg-gray-700 text-white border border-gray-600 rounded px-4 py-2 focus:border-blue-500 focus:outline-none",
4129
- autoFocus: true
4130
- }), /* @__PURE__ */ React18.createElement("button", {
4131
- onClick: searchUnsplash,
4132
- className: "px-6 py-2 bg-blue-600 text-white rounded hover:bg-blue-700"
4133
- }, "Search"), /* @__PURE__ */ React18.createElement("button", {
4134
- onClick: () => {
4135
- setShowUnsplash(false);
4136
- setUnsplashQuery("");
4137
- setUnsplashResults([]);
4138
- },
4139
- className: "px-6 py-2 bg-gray-700 text-white rounded hover:bg-gray-600"
4140
- }, "Cancel"))), /* @__PURE__ */ React18.createElement("div", {
4141
- className: "flex-1 overflow-y-auto p-6"
4142
- }, unsplashResults.length > 0 ? /* @__PURE__ */ React18.createElement("div", {
4143
- className: "grid grid-cols-3 gap-4"
4144
- }, unsplashResults.map((result) => /* @__PURE__ */ React18.createElement("button", {
4145
- key: result.id,
4146
- onClick: () => addUnsplashImage(result.urls.regular),
4147
- className: "relative group overflow-hidden rounded-lg aspect-square"
4148
- }, /* @__PURE__ */ React18.createElement("img", {
4149
- src: result.urls.thumb,
4150
- alt: "",
4151
- className: "w-full h-full object-cover group-hover:scale-110 transition-transform duration-200"
4152
- }), /* @__PURE__ */ React18.createElement("div", {
4153
- className: "absolute inset-0 bg-black bg-opacity-0 group-hover:bg-opacity-50 transition-opacity duration-200 flex items-center justify-center"
4154
- }, /* @__PURE__ */ React18.createElement(PlusCircle, {
4155
- className: "w-8 h-8 text-white opacity-0 group-hover:opacity-100 transition-opacity duration-200"
4156
- }))))) : /* @__PURE__ */ React18.createElement("div", {
4157
- className: "text-center text-gray-400 py-12"
4158
- }, "Enter a search term to find images")))));
4159
- };
4160
- var CanvasEditor_default = CanvasEditor;
4161
3302
  // src/context/CanvasStoreContext.tsx
4162
3303
  import { createContext, useContext } from "react";
4163
3304
  var CanvasStoreContext = createContext(null);
@@ -4168,6 +3309,14 @@ var useCanvasStore = () => {
4168
3309
  }
4169
3310
  return store;
4170
3311
  };
3312
+ // src/config.ts
3313
+ var _unsplashAccessKey = typeof process !== "undefined" && process.env?.UNSPLASH_ACCESS_KEY ? process.env.UNSPLASH_ACCESS_KEY : undefined;
3314
+ function setUnsplashAccessKey(key) {
3315
+ _unsplashAccessKey = key;
3316
+ }
3317
+ function getUnsplashAccessKey() {
3318
+ return _unsplashAccessKey;
3319
+ }
4171
3320
 
4172
3321
  // src/lib/index.ts
4173
3322
  initJsxCompat();