labellife-design-tool 1.2.8 → 1.3.0

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,7 +547,7 @@ 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
552
  import {
553
553
  PlusCircle
@@ -1361,61 +1361,6 @@ var UrlImageElement = ({ element, isSelected, onSelect, onChange }) => {
1361
1361
  }
1362
1362
  }));
1363
1363
  };
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
1364
  // src/components/Header.tsx
1420
1365
  import React7 from "react";
1421
1366
 
@@ -1879,6 +1824,53 @@ var ElementPanel_default = ElementPanel;
1879
1824
 
1880
1825
  // src/panels/TextPanel.tsx
1881
1826
  import React10 from "react";
1827
+
1828
+ // src/constants/CanvasPresets.ts
1829
+ var CANVAS_PRESETS = [
1830
+ { name: "Instagram Post", width: 1080, height: 1080 },
1831
+ { name: "Instagram Story", width: 1080, height: 1920 },
1832
+ { name: "Facebook Post", width: 1200, height: 630 },
1833
+ { name: "Twitter Post", width: 1024, height: 512 },
1834
+ { name: "A4", width: 794, height: 1123 },
1835
+ { name: "Letter", width: 816, height: 1056 },
1836
+ { name: "YouTube Thumbnail", width: 1280, height: 720 },
1837
+ { name: "Custom", width: 800, height: 600 }
1838
+ ];
1839
+ // src/constants/DefaultColors.ts
1840
+ var DEFAULT_COLORS = [
1841
+ "#000000",
1842
+ "#FFFFFF",
1843
+ "#FF0000",
1844
+ "#00FF00",
1845
+ "#0000FF",
1846
+ "#FFFF00",
1847
+ "#FF00FF",
1848
+ "#00FFFF",
1849
+ "#FFA500",
1850
+ "#800080",
1851
+ "#FFC0CB",
1852
+ "#A52A2A",
1853
+ "#808080",
1854
+ "#C0C0C0",
1855
+ "#FFD700"
1856
+ ];
1857
+ // src/constants/FontFamilies.ts
1858
+ var FONT_FAMILIES = [
1859
+ "Arial",
1860
+ "Helvetica",
1861
+ "Times New Roman",
1862
+ "Georgia",
1863
+ "Verdana",
1864
+ "Courier New",
1865
+ "Comic Sans MS",
1866
+ "Impact",
1867
+ "Trebuchet MS",
1868
+ "Palatino",
1869
+ "Garamond",
1870
+ "Bookman",
1871
+ "Tahoma"
1872
+ ];
1873
+ // src/panels/TextPanel.tsx
1882
1874
  import {
1883
1875
  Bold,
1884
1876
  Italic,
@@ -2725,394 +2717,16 @@ var RightSidebar = ({
2725
2717
  }), /* @__PURE__ */ React16.createElement("span", null, "Delete"))));
2726
2718
  };
2727
2719
  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
2735
- }) => {
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;
2800
- }
2801
- const config = inputs.find((input) => input.config.id === id)?.config;
2802
- if (!config)
2803
- 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
- }));
2809
- return;
2810
- }
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;
2818
- }
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
- }));
2836
- };
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";
2875
- }
2876
- });
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
- }
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";
2911
- }
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;
2920
- }
2921
- onComplete(updatedValues);
2922
- } else {
2923
- setCurrentStep((prev) => prev + 1);
2924
- }
2925
- }
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;
2986
- });
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
- }
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;
3040
- }
3041
- };
3042
- if (!currentInput) {
3043
- return null;
3044
- }
3045
- 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
3050
- }, /* @__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"
3069
- }, /* @__PURE__ */ React17.createElement("div", {
3070
- className: "w-full rounded-full h-2",
3071
- style: { backgroundColor: isLightTheme ? "#e2e8f0" : "#374151" }
3072
- }, /* @__PURE__ */ React17.createElement("div", {
3073
- className: "h-2 rounded-full transition-all duration-300",
3074
- style: {
3075
- backgroundColor: "#2563eb",
3076
- width: `${(currentStep + 1) / sortedInputs.length * 100}%`
3077
- }
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
2720
  // src/store/simpleStore.ts
3112
2721
  class SimpleCanvasStore {
3113
2722
  _design;
3114
2723
  _activePageId;
3115
2724
  _activePanelId;
2725
+ _selectedId;
2726
+ _selectedIds;
2727
+ _tool;
2728
+ _zoom;
2729
+ _selectedShape;
3116
2730
  _listeners = {};
3117
2731
  constructor(initialDesign) {
3118
2732
  this._design = {
@@ -3127,6 +2741,11 @@ class SimpleCanvasStore {
3127
2741
  };
3128
2742
  this._activePageId = initialDesign?.pages?.[0]?.id || null;
3129
2743
  this._activePanelId = null;
2744
+ this._selectedId = null;
2745
+ this._selectedIds = [];
2746
+ this._tool = "select";
2747
+ this._zoom = 1;
2748
+ this._selectedShape = "rect";
3130
2749
  }
3131
2750
  on(event, callback) {
3132
2751
  this._listeners[event] = callback;
@@ -3166,12 +2785,27 @@ class SimpleCanvasStore {
3166
2785
  get activePanelId() {
3167
2786
  return this._activePanelId;
3168
2787
  }
2788
+ get selectedId() {
2789
+ return this._selectedId;
2790
+ }
2791
+ get selectedIds() {
2792
+ return this._selectedIds;
2793
+ }
2794
+ get tool() {
2795
+ return this._tool;
2796
+ }
2797
+ get zoom() {
2798
+ return this._zoom;
2799
+ }
2800
+ get selectedShape() {
2801
+ return this._selectedShape;
2802
+ }
3169
2803
  setDesign(design) {
3170
2804
  this._design = design;
3171
2805
  if (design.pages.find((p) => p.id === this._activePageId)) {} else if (design.pages.length > 0) {
3172
2806
  this._activePageId = design.pages[0].id;
3173
2807
  }
3174
- this.emit("designChanged", design);
2808
+ this.emit("designChanged", this._design);
3175
2809
  this.emit("activePageChanged", this._activePageId);
3176
2810
  }
3177
2811
  updateDesign(updates) {
@@ -3213,18 +2847,12 @@ class SimpleCanvasStore {
3213
2847
  this._design.pages = remainingPages;
3214
2848
  this.emit("designChanged", this._design);
3215
2849
  }
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
2850
  addElement(element) {
3225
2851
  const page = this.activePage;
3226
- if (!page)
2852
+ if (!page) {
2853
+ console.warn("addElement: No active page!");
3227
2854
  return;
2855
+ }
3228
2856
  const newElement = {
3229
2857
  id: element.id || `element-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
3230
2858
  type: element.type,
@@ -3242,30 +2870,30 @@ class SimpleCanvasStore {
3242
2870
  page.elements.push(newElement);
3243
2871
  this.emit("designChanged", this._design);
3244
2872
  }
3245
- updateElement(elementId, updates) {
3246
- for (const page of this._design.pages) {
3247
- const elementIndex = page.elements.findIndex((e) => e.id === elementId);
3248
- if (elementIndex !== -1) {
3249
- page.elements[elementIndex] = { ...page.elements[elementIndex], ...updates };
3250
- this.emit("designChanged", this._design);
3251
- return;
3252
- }
3253
- }
3254
- }
3255
- deleteElement(elementId) {
3256
- for (const page of this._design.pages) {
3257
- const elementIndex = page.elements.findIndex((e) => e.id === elementId);
3258
- if (elementIndex !== -1) {
3259
- page.elements.splice(elementIndex, 1);
3260
- this.emit("designChanged", this._design);
3261
- return;
3262
- }
3263
- }
3264
- }
3265
2873
  openSidePanel(panelId) {
3266
2874
  this._activePanelId = panelId;
3267
2875
  this.emit("activePanelChanged", panelId);
3268
2876
  }
2877
+ setSelectedId(selectedId) {
2878
+ this._selectedId = selectedId;
2879
+ this.emit("selectedIdChanged", selectedId);
2880
+ }
2881
+ setSelectedIds(selectedIds) {
2882
+ this._selectedIds = selectedIds;
2883
+ this.emit("selectedIdsChanged", selectedIds);
2884
+ }
2885
+ setTool(tool) {
2886
+ this._tool = tool;
2887
+ this.emit("toolChanged", tool);
2888
+ }
2889
+ setZoom(zoom) {
2890
+ this._zoom = zoom;
2891
+ this.emit("zoomChanged", zoom);
2892
+ }
2893
+ setSelectedShape(shape) {
2894
+ this._selectedShape = shape;
2895
+ this.emit("selectedShapeChanged", shape);
2896
+ }
3269
2897
  async toBlob(type = "png") {
3270
2898
  return new Promise((resolve, reject) => {
3271
2899
  try {
@@ -3295,6 +2923,34 @@ class SimpleCanvasStore {
3295
2923
  }
3296
2924
  });
3297
2925
  }
2926
+ setActivePage(pageId) {
2927
+ const page = this._design.pages.find((p) => p.id === pageId);
2928
+ if (page) {
2929
+ this._activePageId = pageId;
2930
+ this.emit("activePageChanged", pageId);
2931
+ this.emit("designChanged", this._design);
2932
+ }
2933
+ }
2934
+ updateElement(elementId, updates) {
2935
+ for (const page of this._design.pages) {
2936
+ const elementIndex = page.elements.findIndex((e) => e.id === elementId);
2937
+ if (elementIndex !== -1) {
2938
+ page.elements[elementIndex] = { ...page.elements[elementIndex], ...updates };
2939
+ this.emit("designChanged", this._design);
2940
+ return;
2941
+ }
2942
+ }
2943
+ }
2944
+ deleteElement(elementId) {
2945
+ for (const page of this._design.pages) {
2946
+ const elementIndex = page.elements.findIndex((e) => e.id === elementId);
2947
+ if (elementIndex !== -1) {
2948
+ page.elements.splice(elementIndex, 1);
2949
+ this.emit("designChanged", this._design);
2950
+ return;
2951
+ }
2952
+ }
2953
+ }
3298
2954
  async loadJSON(jsonData) {
3299
2955
  return new Promise((resolve, reject) => {
3300
2956
  try {
@@ -3329,190 +2985,84 @@ var CanvasEditor = ({
3329
2985
  width: 800,
3330
2986
  height: 600
3331
2987
  });
3332
- const [design, setDesign] = useState3(() => {
3333
- if (externalStore) {
3334
- return externalStore.design;
3335
- }
3336
- return {
3337
- id: Date.now().toString(),
3338
- name: "Untitled Design",
3339
- width: 800,
3340
- height: 600,
3341
- pages: [
3342
- {
3343
- id: "1",
3344
- name: "Page 1",
3345
- elements: [],
3346
- background: "#ffffff"
3347
- }
3348
- ],
3349
- fonts: FONT_FAMILIES,
3350
- colors: DEFAULT_COLORS
3351
- };
3352
- });
3353
- const [currentPageId, setCurrentPageId] = useState3(() => {
3354
- if (externalStore?.design.pages && externalStore.design.pages.length > 0) {
3355
- return externalStore.design.pages[0].id;
3356
- }
3357
- if (externalStore?.activePageId) {
3358
- return externalStore.activePageId;
3359
- }
3360
- return "1";
3361
- });
3362
- const [selectedId, setSelectedId] = useState3(null);
3363
- const [selectedIds, setSelectedIds] = useState3([]);
3364
- const [tool, setTool] = useState3("select");
3365
- const [activePanelId, setActivePanelId] = useState3("elements");
2988
+ const stageRef = useRef4(null);
2989
+ const fileInputRef = useRef4(null);
2990
+ const jsonInputRef = useRef4(null);
2991
+ const containerRef = useRef4(null);
2992
+ const currentPage = store.design.pages.find((p) => p.id === store.activePageId) || store.design.pages[0] || null;
2993
+ const selectedElement = currentPage?.elements.find((el) => el.id === store.selectedId) || null;
3366
2994
  useEffect4(() => {
3367
2995
  if (externalStore) {
3368
- const handleDesignChange = (newDesign) => {
3369
- setDesign(newDesign);
3370
- };
3371
- const handleActivePageChange = (pageId) => {
3372
- setCurrentPageId(pageId);
3373
- };
3374
- const handleActivePanelChange = (panelId) => {
3375
- setActivePanelId(panelId || "elements");
3376
- };
2996
+ const handleDesignChange = (newDesign) => {};
2997
+ const handleActivePageChange = (pageId) => {};
2998
+ const handleActivePanelChange = (panelId) => {};
2999
+ const handleSelectedIdChange = (selectedId) => {};
3000
+ const handleSelectedIdsChange = (selectedIds) => {};
3001
+ const handleToolChange = (tool) => {};
3002
+ const handleZoomChange = (zoom) => {};
3003
+ const handleSelectedShapeChange = (shape) => {};
3377
3004
  externalStore.on("designChanged", handleDesignChange);
3378
3005
  externalStore.on("activePageChanged", handleActivePageChange);
3379
3006
  externalStore.on("activePanelChanged", handleActivePanelChange);
3007
+ externalStore.on("selectedIdChanged", handleSelectedIdChange);
3008
+ externalStore.on("selectedIdsChanged", handleSelectedIdsChange);
3009
+ externalStore.on("toolChanged", handleToolChange);
3010
+ externalStore.on("zoomChanged", handleZoomChange);
3011
+ externalStore.on("selectedShapeChanged", handleSelectedShapeChange);
3380
3012
  return () => {
3381
3013
  externalStore.off("designChanged");
3382
3014
  externalStore.off("activePageChanged");
3383
3015
  externalStore.off("activePanelChanged");
3016
+ externalStore.off("selectedIdChanged");
3017
+ externalStore.off("selectedIdsChanged");
3018
+ externalStore.off("toolChanged");
3019
+ externalStore.off("zoomChanged");
3020
+ externalStore.off("selectedShapeChanged");
3384
3021
  };
3385
3022
  }
3386
3023
  }, [externalStore]);
3387
- const [showUnsplash, setShowUnsplash] = useState3(false);
3388
- const [unsplashQuery, setUnsplashQuery] = useState3("");
3389
- const [unsplashResults, setUnsplashResults] = useState3([]);
3390
- const [unsplashMode, setUnsplashMode] = useState3("element");
3391
- const [zoom, setZoom] = useState3(1);
3392
- const [selectedShape, setSelectedShape] = useState3("rect");
3393
- const [history, setHistory] = useState3([]);
3394
- const [historyIndex, setHistoryIndex] = useState3(-1);
3395
- const [showInputModal, setShowInputModal] = useState3(false);
3396
- const [pendingInputs, setPendingInputs] = useState3([]);
3397
- const stageRef = useRef5(null);
3398
- const fileInputRef = useRef5(null);
3399
- const jsonInputRef = useRef5(null);
3400
- const containerRef = useRef5(null);
3401
- const currentPage = design.pages.find((p) => p.id === currentPageId) || design.pages[0] || null;
3402
- const selectedElement = currentPage?.elements.find((el) => el.id === selectedId) || null;
3403
- const saveToHistory = useCallback4((newDesign) => {
3404
- const newHistory = history.slice(0, historyIndex + 1);
3405
- newHistory.push(JSON.parse(JSON.stringify(newDesign)));
3406
- setHistory(newHistory);
3407
- setHistoryIndex(newHistory.length - 1);
3408
- }, [history, historyIndex]);
3409
- const undo = useCallback4(() => {
3410
- if (historyIndex > 0) {
3411
- setHistoryIndex(historyIndex - 1);
3412
- setDesign(JSON.parse(JSON.stringify(history[historyIndex - 1])));
3413
- }
3414
- }, [history, historyIndex]);
3415
- const redo = useCallback4(() => {
3416
- if (historyIndex < history.length - 1) {
3417
- setHistoryIndex(historyIndex + 1);
3418
- setDesign(JSON.parse(JSON.stringify(history[historyIndex + 1])));
3024
+ const handleStageClick = useCallback4((e) => {
3025
+ const clickedOnEmpty = e.target === e.target.getStage();
3026
+ if (clickedOnEmpty) {
3027
+ store.setSelectedId(null);
3028
+ store.setSelectedIds([]);
3419
3029
  }
3420
- }, [history, historyIndex]);
3030
+ }, [store]);
3421
3031
  const addText = useCallback4(() => {
3422
- const newElement = {
3423
- id: Date.now().toString(),
3032
+ if (!store.activePage)
3033
+ return;
3034
+ store.activePage.addElement({
3424
3035
  type: "text",
3425
- name: "Text",
3426
- x: design.width / 2 - 50,
3427
- y: design.height / 2 - 20,
3428
- rotation: 0,
3429
- visible: true,
3430
- locked: false,
3431
- opacity: 1,
3432
- text: "Click to edit text",
3433
- fontSize: 24,
3434
- fontFamily: "Arial",
3435
- fill: "#000000",
3436
- align: "left",
3437
- lineHeight: 1.2,
3438
- letterSpacing: 0,
3439
- textCase: "none",
3440
- strikethrough: false
3441
- };
3442
- const newDesign = {
3443
- ...design,
3444
- pages: design.pages.map((page) => page.id === currentPageId ? { ...page, elements: [...page.elements, newElement] } : page)
3445
- };
3446
- setDesign(newDesign);
3447
- saveToHistory(newDesign);
3448
- if (externalStore) {
3449
- externalStore.setDesign(newDesign);
3450
- }
3451
- setSelectedId(newElement.id);
3452
- setTool("select");
3453
- }, [currentPageId, saveToHistory, design.width, design.height, externalStore]);
3036
+ text: "Edit this text",
3037
+ x: store.design.width / 2 - 50,
3038
+ y: store.design.height / 2 - 15,
3039
+ width: 100,
3040
+ height: 30
3041
+ });
3042
+ }, [store]);
3454
3043
  const addShape = useCallback4((shapeType) => {
3455
- const newElement = {
3456
- id: Date.now().toString(),
3044
+ if (!store.activePage)
3045
+ return;
3046
+ store.activePage.addElement({
3457
3047
  type: "shape",
3458
- name: shapeType.charAt(0).toUpperCase() + shapeType.slice(1),
3459
- x: design.width / 2 - 50,
3460
- y: design.height / 2 - 50,
3461
- width: 100,
3462
- height: 100,
3463
- rotation: 0,
3464
- visible: true,
3465
- locked: false,
3466
- opacity: 1,
3467
- fill: "#4299e1",
3468
- stroke: "#000000",
3469
- strokeWidth: 0,
3470
3048
  shapeType,
3471
- sides: shapeType === "polygon" ? 6 : undefined,
3472
- innerRadius: shapeType === "star" ? 40 : undefined,
3473
- outerRadius: shapeType === "star" ? 70 : undefined
3474
- };
3475
- setDesign((prev) => {
3476
- const newDesign = {
3477
- ...prev,
3478
- pages: prev.pages.map((page) => page.id === currentPageId ? { ...page, elements: [...page.elements, newElement] } : page)
3479
- };
3480
- saveToHistory(newDesign);
3481
- return newDesign;
3049
+ x: store.design.width / 2 - 50,
3050
+ y: store.design.height / 2 - 50,
3051
+ width: 100,
3052
+ height: 100
3482
3053
  });
3483
- setSelectedId(newElement.id);
3484
- }, [currentPageId, saveToHistory, design.width, design.height]);
3485
- const handleStageClick = useCallback4((e) => {
3486
- const clickedOnEmpty = e.target === e.target.getStage();
3487
- if (clickedOnEmpty) {
3488
- setSelectedId(null);
3489
- setSelectedIds([]);
3490
- }
3491
- }, []);
3054
+ }, [store]);
3492
3055
  const updateElement = useCallback4((id, attrs) => {
3493
- setDesign((prev) => ({
3494
- ...prev,
3495
- pages: prev.pages.map((page) => ({
3496
- ...page,
3497
- elements: page.elements.map((el) => el.id === id ? { ...el, ...attrs } : el)
3498
- }))
3499
- }));
3500
- }, []);
3056
+ store.updateElement(id, attrs);
3057
+ }, [store]);
3501
3058
  const deleteSelected = useCallback4(() => {
3502
- if (selectedId || selectedIds.length > 0) {
3503
- const idsToDelete = selectedIds.length > 0 ? selectedIds : [selectedId];
3504
- setDesign((prev) => ({
3505
- ...prev,
3506
- pages: prev.pages.map((page) => ({
3507
- ...page,
3508
- elements: page.elements.filter((el) => !idsToDelete.includes(el.id))
3509
- }))
3510
- }));
3511
- setSelectedId(null);
3512
- setSelectedIds([]);
3513
- saveToHistory(design);
3059
+ if (store.selectedId || store.selectedIds.length > 0) {
3060
+ const idsToDelete = store.selectedIds.length > 0 ? store.selectedIds : [store.selectedId];
3061
+ idsToDelete.forEach((id) => store.deleteElement(id));
3062
+ store.setSelectedId(null);
3063
+ store.setSelectedIds([]);
3514
3064
  }
3515
- }, [selectedId, selectedIds, saveToHistory, design]);
3065
+ }, [store]);
3516
3066
  const duplicateSelected = useCallback4(() => {
3517
3067
  if (selectedElement) {
3518
3068
  const newElement = {
@@ -3522,603 +3072,254 @@ var CanvasEditor = ({
3522
3072
  x: selectedElement.x + 20,
3523
3073
  y: selectedElement.y + 20
3524
3074
  };
3525
- setDesign((prev) => {
3526
- const newDesign = {
3527
- ...prev,
3528
- pages: prev.pages.map((page) => page.id === currentPageId ? { ...page, elements: [...page.elements, newElement] } : page)
3529
- };
3530
- saveToHistory(newDesign);
3531
- return newDesign;
3075
+ store.activePage?.addElement(newElement);
3076
+ store.setSelectedId(newElement.id);
3077
+ }
3078
+ }, [selectedElement, store]);
3079
+ const moveElementUp = useCallback4((id) => {}, [store]);
3080
+ const moveElementDown = useCallback4((id) => {}, [store]);
3081
+ const addPage = useCallback4(() => {
3082
+ store.addPage();
3083
+ }, [store]);
3084
+ const deletePage = useCallback4((pageId) => {
3085
+ store.deletePages([pageId]);
3086
+ }, [store]);
3087
+ const panelConfigs = useMemo(() => {
3088
+ const builtInConfigs = {
3089
+ elements: { id: "elements", title: "Elements", component: ElementPanel_default, props: { onAddShape: addShape, store } },
3090
+ text: { id: "text", title: "Text", component: TextPanel_default, props: { selectedElement, updateElement, onAddText: addText, store } },
3091
+ image: { id: "image", title: "Image", component: ImagePanel_default, props: { selectedElement, updateElement, store } },
3092
+ background: { id: "background", title: "Background", component: BackgroundPanel_default, props: { store } },
3093
+ design: { id: "design", title: "Design", component: DesignPanel_default, props: { store } },
3094
+ variables: { id: "variables", title: "Variables", component: VariablesPanel_default, props: { store } },
3095
+ export: { id: "export", title: "Export", component: ExportPanel_default, props: { store } }
3096
+ };
3097
+ const mergedConfigs = { ...builtInConfigs };
3098
+ if (config?.panels) {
3099
+ config.panels.forEach((panelConfig) => {
3100
+ if (typeof panelConfig === "string") {} else {
3101
+ const customPanelConfig = {
3102
+ id: panelConfig.id,
3103
+ title: panelConfig.title,
3104
+ component: panelConfig.component,
3105
+ props: panelConfig.props || {},
3106
+ ...panelConfig.icon && typeof panelConfig.icon.type === "string" ? {
3107
+ icon: panelConfig.icon.type
3108
+ } : {}
3109
+ };
3110
+ mergedConfigs[panelConfig.id] = customPanelConfig;
3111
+ }
3532
3112
  });
3533
- setSelectedId(newElement.id);
3534
3113
  }
3535
- }, [selectedElement, currentPageId, saveToHistory]);
3536
- const moveElementUp = useCallback4((id) => {
3537
- setDesign((prev) => ({
3538
- ...prev,
3539
- pages: prev.pages.map((page) => {
3540
- if (page.id === currentPageId) {
3541
- const index = page.elements.findIndex((el) => el.id === id);
3542
- if (index < page.elements.length - 1) {
3543
- const newElements = [...page.elements];
3544
- [newElements[index], newElements[index + 1]] = [
3545
- newElements[index + 1],
3546
- newElements[index]
3547
- ];
3548
- return { ...page, elements: newElements };
3114
+ const orderedConfigs = [];
3115
+ if (config?.panels) {
3116
+ config.panels.forEach((panelConfig) => {
3117
+ if (typeof panelConfig === "string") {
3118
+ if (mergedConfigs[panelConfig]) {
3119
+ orderedConfigs.push(mergedConfigs[panelConfig]);
3549
3120
  }
3121
+ } else {
3122
+ orderedConfigs.push(mergedConfigs[panelConfig.id]);
3550
3123
  }
3551
- return page;
3552
- })
3553
- }));
3554
- }, [currentPageId]);
3555
- const moveElementDown = useCallback4((id) => {
3556
- setDesign((prev) => ({
3557
- ...prev,
3558
- pages: prev.pages.map((page) => {
3559
- if (page.id === currentPageId) {
3560
- const index = page.elements.findIndex((el) => el.id === id);
3561
- if (index > 0) {
3562
- const newElements = [...page.elements];
3563
- [newElements[index], newElements[index - 1]] = [
3564
- newElements[index - 1],
3565
- newElements[index]
3566
- ];
3567
- return { ...page, elements: newElements };
3568
- }
3124
+ });
3125
+ }
3126
+ return orderedConfigs.length > 0 ? orderedConfigs : Object.values(builtInConfigs);
3127
+ }, [addShape, addText, config, selectedElement, updateElement, store]);
3128
+ const DynamicPanelRenderer = useMemo(() => {
3129
+ const activePanel = panelConfigs.find((panel) => panel.id === store.activePanelId);
3130
+ if (!activePanel)
3131
+ return null;
3132
+ const PanelComponent = activePanel.component;
3133
+ return /* @__PURE__ */ React17.createElement(PanelComponent, {
3134
+ key: activePanel.id,
3135
+ ...activePanel.props
3136
+ });
3137
+ }, [panelConfigs, store.activePanelId]);
3138
+ const handleExportPNG = useCallback4(() => {
3139
+ exportToPNG(stageRef.current, store.design);
3140
+ }, [store.design]);
3141
+ const handleExportJPG = useCallback4(() => {
3142
+ exportToJPG(stageRef.current, store.design);
3143
+ }, [store.design]);
3144
+ const handleExportJSON = useCallback4(() => {
3145
+ exportToJSON(store.design);
3146
+ }, [store.design]);
3147
+ const handleImportJSON = useCallback4((e) => {
3148
+ const file = e.target.files?.[0];
3149
+ if (file) {
3150
+ const reader = new FileReader;
3151
+ reader.onload = (event) => {
3152
+ try {
3153
+ const jsonData = JSON.parse(event.target?.result);
3154
+ importFromJSON(jsonData, (loadedDesign) => {
3155
+ store.setDesign(loadedDesign);
3156
+ }, (errorMessage) => {
3157
+ console.error("Import error:", errorMessage);
3158
+ }, () => {
3159
+ console.log("Import requires user inputs - not implemented");
3160
+ });
3161
+ } catch (error) {
3162
+ console.error("Invalid JSON file:", error);
3569
3163
  }
3570
- return page;
3571
- })
3572
- }));
3573
- }, [currentPageId]);
3574
- const addPage = useCallback4(() => {
3575
- const newPage = {
3576
- id: Date.now().toString(),
3577
- name: `Page ${design.pages.length + 1}`,
3578
- elements: [],
3579
- background: "#ffffff",
3580
- backgroundImageObject: undefined
3581
- };
3582
- setDesign((prev) => ({
3583
- ...prev,
3584
- pages: [...prev.pages, newPage]
3585
- }));
3586
- setCurrentPageId(newPage.id);
3587
- }, [design.pages.length]);
3588
- const deletePage = useCallback4((pageId) => {
3589
- if (design.pages.length > 1) {
3590
- setDesign((prev) => ({
3591
- ...prev,
3592
- pages: prev.pages.filter((p) => p.id !== pageId)
3593
- }));
3594
- if (currentPageId === pageId) {
3595
- setCurrentPageId(design.pages[0].id);
3596
- }
3164
+ };
3165
+ reader.readAsText(file);
3597
3166
  }
3598
- }, [design.pages, currentPageId]);
3167
+ }, [store]);
3599
3168
  const handleImageUpload = useCallback4((e) => {
3600
3169
  const file = e.target.files?.[0];
3601
3170
  if (file) {
3602
3171
  const reader = new FileReader;
3603
3172
  reader.onload = (event) => {
3604
3173
  const img = new window.Image;
3605
- img.src = event.target?.result;
3606
3174
  img.onload = () => {
3607
- const newElement = {
3608
- id: Date.now().toString(),
3609
- type: "image",
3610
- name: file.name,
3611
- x: 100,
3612
- y: 100,
3613
- width: img.naturalWidth,
3614
- height: img.naturalHeight,
3615
- rotation: 0,
3616
- visible: true,
3617
- locked: false,
3618
- opacity: 1,
3619
- src: event.target?.result,
3620
- cropX: 0,
3621
- cropY: 0,
3622
- cropWidth: img.naturalWidth,
3623
- cropHeight: img.naturalHeight,
3624
- mask: undefined
3625
- };
3626
- setDesign((prev) => ({
3627
- ...prev,
3628
- pages: prev.pages.map((page) => page.id === currentPageId ? { ...page, elements: [...page.elements, newElement] } : page)
3629
- }));
3630
- saveToHistory(design);
3175
+ if (store.activePage) {
3176
+ store.activePage.addElement({
3177
+ type: "image",
3178
+ src: event.target?.result,
3179
+ x: store.design.width / 2 - img.width / 2,
3180
+ y: store.design.height / 2 - img.height / 2,
3181
+ width: img.width,
3182
+ height: img.height
3183
+ });
3184
+ }
3631
3185
  };
3186
+ img.src = event.target?.result;
3632
3187
  };
3633
3188
  reader.readAsDataURL(file);
3634
3189
  }
3635
- if (fileInputRef.current) {
3636
- fileInputRef.current.value = "";
3637
- }
3638
- }, [currentPageId, saveToHistory]);
3639
- const searchUnsplash = useCallback4(async () => {
3640
- if (!unsplashQuery)
3641
- return;
3642
- if (UNSPLASH_ACCESS_KEY === "YOUR_UNSPLASH_ACCESS_KEY_HERE" || !UNSPLASH_ACCESS_KEY) {
3643
- console.warn("Unsplash API key is not configured. Please set UNSPLASH_ACCESS_KEY in src/config.ts");
3644
- const mockResults = Array(9).fill(null).map((_, i) => ({
3645
- id: `unsplash-mock-${i}`,
3646
- urls: {
3647
- regular: `https://source.unsplash.com/400x300/?${unsplashQuery}&sig=${i}`,
3648
- thumb: `https://source.unsplash.com/200x150/?${unsplashQuery}&sig=${i}`
3649
- },
3650
- user: { name: "Mock User" }
3651
- }));
3652
- setUnsplashResults(mockResults);
3653
- return;
3654
- }
3655
- try {
3656
- const response = await fetch(`https://api.unsplash.com/search/photos?query=${encodeURIComponent(unsplashQuery)}&per_page=9`, {
3657
- headers: {
3658
- Authorization: `Client-ID ${UNSPLASH_ACCESS_KEY}`
3659
- }
3660
- });
3661
- if (!response.ok) {
3662
- console.error("Failed to fetch from Unsplash:", response.statusText);
3663
- setUnsplashResults([]);
3664
- return;
3665
- }
3666
- const data = await response.json();
3667
- setUnsplashResults(data.results.map((img) => ({
3668
- id: img.id,
3669
- urls: { regular: img.urls.regular, thumb: img.urls.thumb },
3670
- user: { name: img.user.name }
3671
- })));
3672
- } catch (error) {
3673
- console.error("Error searching Unsplash:", error);
3674
- setUnsplashResults([]);
3675
- }
3676
- }, [unsplashQuery]);
3677
- const addUnsplashImage = useCallback4((imageUrl) => {
3678
- if (unsplashMode === "element") {
3679
- const img = new window.Image;
3680
- img.crossOrigin = "anonymous";
3681
- img.src = imageUrl;
3682
- img.onload = () => {
3683
- const newElement = {
3684
- id: Date.now().toString(),
3685
- type: "image",
3686
- name: "Unsplash Image",
3687
- x: 100,
3688
- y: 100,
3689
- width: img.naturalWidth,
3690
- height: img.naturalHeight,
3691
- rotation: 0,
3692
- visible: true,
3693
- locked: false,
3694
- opacity: 1,
3695
- src: imageUrl,
3696
- cropX: 0,
3697
- cropY: 0,
3698
- cropWidth: img.naturalWidth,
3699
- cropHeight: img.naturalHeight,
3700
- mask: undefined
3701
- };
3702
- setDesign((prev) => ({
3703
- ...prev,
3704
- pages: prev.pages.map((page) => page.id === currentPageId ? { ...page, elements: [...page.elements, newElement] } : page)
3705
- }));
3706
- saveToHistory(design);
3707
- };
3708
- } else if (unsplashMode === "background") {
3709
- const bgImg = new window.Image;
3710
- bgImg.crossOrigin = "anonymous";
3711
- bgImg.src = imageUrl;
3712
- bgImg.onload = () => {
3713
- setDesign((prev) => ({
3714
- ...prev,
3715
- pages: prev.pages.map((p) => p.id === currentPageId ? {
3716
- ...p,
3717
- background: `url(${imageUrl})`,
3718
- backgroundImageObject: bgImg
3719
- } : p)
3720
- }));
3721
- saveToHistory(design);
3722
- };
3723
- }
3724
- setShowUnsplash(false);
3725
- setUnsplashQuery("");
3726
- setUnsplashResults([]);
3727
- setUnsplashMode("element");
3728
- }, [currentPageId, saveToHistory, unsplashMode]);
3729
- const handleImportJSON = (event) => {
3730
- importFromJSON(event, (newDesign) => {
3731
- if (newDesign && newDesign.pages && newDesign.pages.length > 0) {
3732
- setDesign(newDesign);
3733
- setSelectedId(null);
3734
- setSelectedIds([]);
3735
- const firstPageId = newDesign.pages[0]?.id;
3736
- setCurrentPageId(firstPageId || "1");
3737
- saveToHistory(newDesign);
3738
- } else {
3739
- alert("Invalid design structure in JSON file. Could not load.");
3740
- }
3741
- }, (errorMessage) => {
3742
- alert(`Error importing JSON: ${errorMessage}`);
3743
- }, (inputs, onComplete) => {
3744
- setPendingInputs(inputs);
3745
- setShowInputModal(true);
3746
- window.__pendingImportComplete = onComplete;
3747
- });
3748
- if (jsonInputRef.current) {
3749
- jsonInputRef.current.value = "";
3750
- }
3751
- };
3752
- const handleInputModalComplete = (values) => {
3753
- setShowInputModal(false);
3754
- const onComplete = window.__pendingImportComplete;
3755
- if (onComplete) {
3756
- onComplete(values);
3757
- delete window.__pendingImportComplete;
3758
- }
3759
- setPendingInputs([]);
3760
- };
3761
- const handleInputModalCancel = () => {
3762
- setShowInputModal(false);
3763
- setPendingInputs([]);
3764
- delete window.__pendingImportComplete;
3765
- };
3766
- const panelConfigs = useMemo(() => {
3767
- const builtInConfigs = {
3768
- elements: {
3769
- id: "elements",
3770
- title: "Elements",
3771
- component: ElementPanel_default,
3772
- props: { onAddShape: addShape, store }
3773
- },
3774
- text: {
3775
- id: "text",
3776
- title: "Text",
3777
- component: TextPanel_default,
3778
- props: {
3779
- selectedElement,
3780
- updateElement,
3781
- setTool,
3782
- onAddText: addText,
3783
- store
3784
- }
3785
- },
3786
- image: {
3787
- id: "image",
3788
- title: "Images",
3789
- component: ImagePanel_default,
3790
- props: {
3791
- selectedElement,
3792
- updateElement,
3793
- onUploadClick: () => fileInputRef.current?.click(),
3794
- onUnsplashClick: () => {
3795
- setShowUnsplash(true);
3796
- setUnsplashMode("element");
3797
- },
3798
- canvasWidth: design.width,
3799
- canvasHeight: design.height,
3800
- store
3801
- }
3802
- },
3803
- design: {
3804
- id: "design",
3805
- title: "Design",
3806
- component: DesignPanel_default,
3807
- props: {
3808
- design,
3809
- currentPage,
3810
- selectedElement,
3811
- setDesign,
3812
- updateElement,
3813
- onSetUnsplashBackground: () => {
3814
- setShowUnsplash(true);
3815
- setUnsplashMode("background");
3816
- },
3817
- config,
3818
- store
3819
- }
3820
- },
3821
- background: {
3822
- id: "background",
3823
- title: "Background",
3824
- component: BackgroundPanel_default,
3825
- props: {
3826
- design,
3827
- currentPage,
3828
- setDesign,
3829
- onSetUnsplashBackground: () => {
3830
- setShowUnsplash(true);
3831
- setUnsplashMode("background");
3832
- },
3833
- store
3834
- }
3835
- },
3836
- ...config?.variables ? {
3837
- variables: {
3838
- id: "variables",
3839
- title: "Variables",
3840
- component: VariablesPanel_default,
3841
- props: {
3842
- config,
3843
- design,
3844
- setDesign,
3845
- store
3846
- }
3847
- }
3848
- } : {},
3849
- ...config?.export ? {
3850
- export: {
3851
- id: "export",
3852
- title: "Export",
3853
- component: ExportPanel_default,
3854
- props: {
3855
- onExportToPNG: config.export?.png ? () => {
3856
- if (stageRef.current)
3857
- exportToPNG(stageRef.current, design);
3858
- } : undefined,
3859
- onExportToJPG: config.export?.jpg ? () => {
3860
- if (stageRef.current)
3861
- exportToJPG(stageRef.current, design);
3862
- } : undefined,
3863
- onExportToJSON: config.export?.json ? () => exportToJSON(design) : undefined,
3864
- onImportJSON: () => jsonInputRef.current?.click(),
3865
- store
3866
- }
3867
- }
3868
- } : {}
3869
- };
3870
- if (!config?.panels || config.panels.length === 0) {
3871
- return [
3872
- "text",
3873
- "elements",
3874
- "image",
3875
- "design",
3876
- "background",
3877
- ...config?.variables ? ["variables"] : [],
3878
- ...config?.export ? ["export"] : []
3879
- ].filter((id) => builtInConfigs[id]).map((id) => builtInConfigs[id]);
3880
- }
3881
- const ordered = [];
3882
- for (const panel of config.panels) {
3883
- if (typeof panel === "string") {
3884
- const config2 = builtInConfigs[panel];
3885
- if (config2) {
3886
- ordered.push(config2);
3887
- }
3888
- } else {
3889
- ordered.push({
3890
- id: panel.id,
3891
- title: panel.title,
3892
- component: panel.component,
3893
- props: { ...panel.props, store }
3894
- });
3895
- }
3896
- }
3897
- return ordered;
3898
- }, [
3899
- addShape,
3900
- addText,
3901
- config,
3902
- currentPage,
3903
- design,
3904
- selectedElement,
3905
- setTool,
3906
- updateElement,
3907
- store
3908
- ]);
3909
- const DynamicPanelRenderer = () => {
3910
- const activePanelConfig = panelConfigs.find((p) => p.id === activePanelId);
3911
- if (!activePanelConfig) {
3912
- return /* @__PURE__ */ React18.createElement("div", {
3913
- className: "text-white"
3914
- }, "Panel not found");
3915
- }
3916
- const PanelComponent = activePanelConfig.component;
3917
- return /* @__PURE__ */ React18.createElement(PanelComponent, {
3918
- ...activePanelConfig.props
3919
- });
3920
- };
3921
- useEffect4(() => {
3922
- const handleKeyDown = (e) => {
3923
- if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement)
3924
- return;
3925
- if (e.metaKey || e.ctrlKey) {
3926
- switch (e.key) {
3927
- case "z":
3928
- if (e.shiftKey) {
3929
- redo();
3930
- } else {
3931
- undo();
3932
- }
3933
- e.preventDefault();
3934
- break;
3935
- case "c":
3936
- break;
3937
- case "v":
3938
- break;
3939
- case "d":
3940
- duplicateSelected();
3941
- e.preventDefault();
3942
- break;
3943
- case "s":
3944
- exportToJSON(design);
3945
- e.preventDefault();
3946
- break;
3947
- }
3948
- } else {
3949
- switch (e.key) {
3950
- case "Delete":
3951
- case "Backspace":
3952
- deleteSelected();
3953
- break;
3954
- case "Escape":
3955
- setSelectedId(null);
3956
- setSelectedIds([]);
3957
- break;
3958
- }
3959
- }
3960
- };
3961
- window.addEventListener("keydown", handleKeyDown);
3962
- return () => window.removeEventListener("keydown", handleKeyDown);
3963
- }, [deleteSelected, duplicateSelected, exportToJSON, undo, redo]);
3964
- useEffect4(() => {
3965
- if (history.length === 0) {
3966
- setHistory([JSON.parse(JSON.stringify(design))]);
3967
- setHistoryIndex(0);
3968
- }
3969
- }, []);
3970
- return /* @__PURE__ */ React18.createElement("div", {
3971
- className: "h-screen flex flex-col bg-gray-900"
3972
- }, /* @__PURE__ */ React18.createElement(Header_default, {
3190
+ }, [store]);
3191
+ return /* @__PURE__ */ React17.createElement("div", {
3192
+ className: "w-full h-full flex flex-col bg-gray-900"
3193
+ }, /* @__PURE__ */ React17.createElement(Header_default, {
3973
3194
  name,
3974
- zoom,
3975
- historyIndex,
3976
- historyLength: history.length,
3977
- onUndo: undo,
3978
- onRedo: redo,
3979
- onZoomIn: () => setZoom(Math.min(3, zoom + 0.1)),
3980
- onZoomOut: () => setZoom(Math.max(0.1, zoom - 0.1)),
3981
- onZoomReset: () => setZoom(1),
3982
- onSetActivePanel: setActivePanelId
3983
- }), /* @__PURE__ */ React18.createElement("div", {
3195
+ zoom: store.zoom,
3196
+ historyIndex: 0,
3197
+ historyLength: 0,
3198
+ onUndo: () => {},
3199
+ onRedo: () => {},
3200
+ onZoomIn: () => store.setZoom(Math.min(3, store.zoom + 0.1)),
3201
+ onZoomOut: () => store.setZoom(Math.max(0.1, store.zoom - 0.1)),
3202
+ onZoomReset: () => store.setZoom(1),
3203
+ onSetActivePanel: store.openSidePanel
3204
+ }), /* @__PURE__ */ React17.createElement("div", {
3984
3205
  className: "flex-1 flex overflow-hidden"
3985
- }, /* @__PURE__ */ React18.createElement("div", {
3206
+ }, /* @__PURE__ */ React17.createElement("div", {
3986
3207
  className: "w-80 bg-gray-800 border-r border-gray-700 flex"
3987
- }, /* @__PURE__ */ React18.createElement(LeftMenu_default, {
3988
- tool,
3989
- activePanel: activePanelId,
3990
- onSetTool: setTool,
3991
- onSetActivePanel: setActivePanelId,
3208
+ }, /* @__PURE__ */ React17.createElement(LeftMenu_default, {
3209
+ tool: store.tool,
3210
+ activePanel: store.activePanelId || "elements",
3211
+ onSetTool: store.setTool,
3212
+ onSetActivePanel: store.openSidePanel,
3992
3213
  config
3993
- }), /* @__PURE__ */ React18.createElement("div", {
3214
+ }), /* @__PURE__ */ React17.createElement("div", {
3994
3215
  className: "flex-1 p-4 overflow-y-auto"
3995
- }, /* @__PURE__ */ React18.createElement(DynamicPanelRenderer, null))), /* @__PURE__ */ React18.createElement("div", {
3216
+ }, /* @__PURE__ */ React17.createElement(DynamicPanelRenderer, null))), /* @__PURE__ */ React17.createElement("div", {
3996
3217
  className: "flex-1 bg-gray-700 overflow-hidden relative",
3997
3218
  ref: containerRef
3998
- }, /* @__PURE__ */ React18.createElement("div", {
3219
+ }, !currentPage ? /* @__PURE__ */ React17.createElement("div", {
3220
+ className: "absolute inset-0 flex items-center justify-center text-gray-400"
3221
+ }, /* @__PURE__ */ React17.createElement("div", {
3222
+ className: "text-center"
3223
+ }, /* @__PURE__ */ React17.createElement("p", {
3224
+ className: "mb-4"
3225
+ }, "No pages available"), /* @__PURE__ */ React17.createElement("button", {
3226
+ onClick: () => store.addPage(),
3227
+ className: "px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700"
3228
+ }, "Add Page"))) : /* @__PURE__ */ React17.createElement("div", {
3999
3229
  className: "absolute inset-0 flex items-center justify-center"
4000
- }, /* @__PURE__ */ React18.createElement("div", {
3230
+ }, /* @__PURE__ */ React17.createElement("div", {
4001
3231
  className: "bg-white shadow-2xl",
4002
3232
  style: {
4003
- transform: `scale(${zoom})`,
3233
+ transform: `scale(${store.zoom})`,
4004
3234
  transformOrigin: "center center"
4005
3235
  }
4006
- }, /* @__PURE__ */ React18.createElement(Stage, {
3236
+ }, /* @__PURE__ */ React17.createElement(Stage, {
4007
3237
  ref: stageRef,
4008
- width: design.width,
4009
- height: design.height,
3238
+ width: store.design.width,
3239
+ height: store.design.height,
4010
3240
  onClick: handleStageClick,
4011
3241
  onTap: handleStageClick
4012
- }, /* @__PURE__ */ React18.createElement(Layer, null, currentPage && /* @__PURE__ */ React18.createElement(React18.Fragment, null, /* @__PURE__ */ React18.createElement(Rect2, {
3242
+ }, /* @__PURE__ */ React17.createElement(Layer, null, /* @__PURE__ */ React17.createElement(React17.Fragment, null, /* @__PURE__ */ React17.createElement(Rect2, {
4013
3243
  x: 0,
4014
3244
  y: 0,
4015
- width: design.width,
4016
- height: design.height,
3245
+ width: store.design.width,
3246
+ height: store.design.height,
4017
3247
  fill: currentPage.backgroundImageObject ? undefined : currentPage.background,
4018
3248
  fillPatternImage: currentPage.backgroundImageObject,
4019
3249
  fillPatternRepeat: "no-repeat"
4020
3250
  }), currentPage.elements.map((element) => {
4021
3251
  switch (element.type) {
4022
3252
  case "text":
4023
- return /* @__PURE__ */ React18.createElement(EditableTextElement, {
3253
+ return /* @__PURE__ */ React17.createElement(EditableTextElement, {
3254
+ key: element.id,
3255
+ element,
3256
+ isSelected: element.id === store.selectedId,
3257
+ onSelect: () => {
3258
+ store.setSelectedId(element.id);
3259
+ store.setSelectedIds([]);
3260
+ },
3261
+ onChange: (attrs) => updateElement(element.id, attrs)
3262
+ });
3263
+ case "image":
3264
+ return /* @__PURE__ */ React17.createElement(UrlImageElement, {
4024
3265
  key: element.id,
4025
3266
  element,
4026
- isSelected: element.id === selectedId,
3267
+ isSelected: element.id === store.selectedId,
4027
3268
  onSelect: () => {
4028
- setSelectedId(element.id);
4029
- setSelectedIds([]);
3269
+ store.setSelectedId(element.id);
3270
+ store.setSelectedIds([]);
3271
+ },
3272
+ onChange: (attrs) => updateElement(element.id, attrs)
3273
+ });
3274
+ case "shape":
3275
+ return /* @__PURE__ */ React17.createElement(ShapeElement, {
3276
+ key: element.id,
3277
+ element,
3278
+ isSelected: element.id === store.selectedId,
3279
+ onSelect: () => {
3280
+ store.setSelectedId(element.id);
3281
+ store.setSelectedIds([]);
4030
3282
  },
4031
3283
  onChange: (attrs) => updateElement(element.id, attrs)
4032
3284
  });
4033
3285
  default:
4034
3286
  return null;
4035
3287
  }
4036
- })))))), config?.multiPage && /* @__PURE__ */ React18.createElement("div", {
3288
+ })))))), config?.multiPage && /* @__PURE__ */ React17.createElement("div", {
4037
3289
  className: "absolute bottom-4 left-4 flex items-center space-x-2"
4038
- }, design.pages.map((page, index) => /* @__PURE__ */ React18.createElement("button", {
3290
+ }, store.design.pages.map((page, index) => /* @__PURE__ */ React17.createElement("button", {
4039
3291
  key: page.id,
4040
- onClick: () => setCurrentPageId(page.id),
4041
- 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"}`
4042
- }, "Page ", index + 1)), /* @__PURE__ */ React18.createElement("button", {
3292
+ onClick: () => store.setActivePage(page.id),
3293
+ 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"}`
3294
+ }, "Page ", index + 1)), /* @__PURE__ */ React17.createElement("button", {
4043
3295
  onClick: addPage,
4044
- className: "p-1 bg-gray-800 text-gray-300 rounded hover:bg-gray-700",
3296
+ className: "px-3 py-1 rounded text-sm bg-gray-800 text-gray-300 hover:bg-gray-700",
4045
3297
  title: "Add Page"
4046
- }, /* @__PURE__ */ React18.createElement(PlusCircle, {
3298
+ }, /* @__PURE__ */ React17.createElement(PlusCircle, {
4047
3299
  className: "w-4 h-4"
4048
- })))), /* @__PURE__ */ React18.createElement(RightSidebar_default, {
4049
- currentPageElements: currentPage.elements,
3300
+ })))), /* @__PURE__ */ React17.createElement(RightSidebar_default, {
3301
+ currentPageElements: currentPage?.elements || [],
4050
3302
  selectedElement,
4051
- selectedId,
4052
- onSelectElement: setSelectedId,
3303
+ selectedId: store.selectedId,
3304
+ onSelectElement: store.setSelectedId,
4053
3305
  onUpdateElement: updateElement,
4054
3306
  onDuplicateSelected: duplicateSelected,
4055
3307
  onMoveElementUp: moveElementUp,
4056
3308
  onMoveElementDown: moveElementDown,
4057
3309
  onDeleteSelected: deleteSelected
4058
- })), /* @__PURE__ */ React18.createElement("input", {
3310
+ })), /* @__PURE__ */ React17.createElement("input", {
4059
3311
  ref: fileInputRef,
4060
3312
  type: "file",
4061
3313
  accept: "image/*",
4062
3314
  onChange: handleImageUpload,
4063
- className: "hidden",
4064
- multiple: true
4065
- }), /* @__PURE__ */ React18.createElement("input", {
3315
+ style: { display: "none" }
3316
+ }), /* @__PURE__ */ React17.createElement("input", {
4066
3317
  ref: jsonInputRef,
4067
3318
  type: "file",
4068
3319
  accept: ".json",
4069
3320
  onChange: handleImportJSON,
4070
- className: "hidden"
4071
- }), showInputModal && /* @__PURE__ */ React18.createElement(TemplateInputModal_default, {
4072
- inputs: pendingInputs,
4073
- onComplete: handleInputModalComplete,
4074
- onCancel: handleInputModalCancel
4075
- }), showUnsplash && /* @__PURE__ */ React18.createElement("div", {
4076
- className: "fixed inset-0 bg-black bg-opacity-75 flex items-center justify-center z-50 p-8"
4077
- }, /* @__PURE__ */ React18.createElement("div", {
4078
- className: "bg-gray-800 rounded-lg w-full max-w-4xl max-h-full overflow-hidden flex flex-col"
4079
- }, /* @__PURE__ */ React18.createElement("div", {
4080
- className: "p-6 border-b border-gray-700"
4081
- }, /* @__PURE__ */ React18.createElement("h3", {
4082
- className: "text-xl font-semibold text-white mb-4"
4083
- }, "Search Unsplash"), /* @__PURE__ */ React18.createElement("div", {
4084
- className: "flex space-x-4"
4085
- }, /* @__PURE__ */ React18.createElement("input", {
4086
- type: "text",
4087
- value: unsplashQuery,
4088
- onChange: (e) => setUnsplashQuery(e.target.value),
4089
- onKeyDown: (e) => e.key === "Enter" && searchUnsplash(),
4090
- placeholder: "Search for images...",
4091
- className: "flex-1 bg-gray-700 text-white border border-gray-600 rounded px-4 py-2 focus:border-blue-500 focus:outline-none",
4092
- autoFocus: true
4093
- }), /* @__PURE__ */ React18.createElement("button", {
4094
- onClick: searchUnsplash,
4095
- className: "px-6 py-2 bg-blue-600 text-white rounded hover:bg-blue-700"
4096
- }, "Search"), /* @__PURE__ */ React18.createElement("button", {
4097
- onClick: () => {
4098
- setShowUnsplash(false);
4099
- setUnsplashQuery("");
4100
- setUnsplashResults([]);
4101
- },
4102
- className: "px-6 py-2 bg-gray-700 text-white rounded hover:bg-gray-600"
4103
- }, "Cancel"))), /* @__PURE__ */ React18.createElement("div", {
4104
- className: "flex-1 overflow-y-auto p-6"
4105
- }, unsplashResults.length > 0 ? /* @__PURE__ */ React18.createElement("div", {
4106
- className: "grid grid-cols-3 gap-4"
4107
- }, unsplashResults.map((result) => /* @__PURE__ */ React18.createElement("button", {
4108
- key: result.id,
4109
- onClick: () => addUnsplashImage(result.urls.regular),
4110
- className: "relative group overflow-hidden rounded-lg aspect-square"
4111
- }, /* @__PURE__ */ React18.createElement("img", {
4112
- src: result.urls.thumb,
4113
- alt: "",
4114
- className: "w-full h-full object-cover group-hover:scale-110 transition-transform duration-200"
4115
- }), /* @__PURE__ */ React18.createElement("div", {
4116
- className: "absolute inset-0 bg-black bg-opacity-0 group-hover:bg-opacity-50 transition-opacity duration-200 flex items-center justify-center"
4117
- }, /* @__PURE__ */ React18.createElement(PlusCircle, {
4118
- className: "w-8 h-8 text-white opacity-0 group-hover:opacity-100 transition-opacity duration-200"
4119
- }))))) : /* @__PURE__ */ React18.createElement("div", {
4120
- className: "text-center text-gray-400 py-12"
4121
- }, "Enter a search term to find images")))));
3321
+ style: { display: "none" }
3322
+ }));
4122
3323
  };
4123
3324
  var CanvasEditor_default = CanvasEditor;
4124
3325
  // src/context/CanvasStoreContext.tsx
@@ -4131,6 +3332,14 @@ var useCanvasStore = () => {
4131
3332
  }
4132
3333
  return store;
4133
3334
  };
3335
+ // src/config.ts
3336
+ var _unsplashAccessKey = typeof process !== "undefined" && process.env?.UNSPLASH_ACCESS_KEY ? process.env.UNSPLASH_ACCESS_KEY : undefined;
3337
+ function setUnsplashAccessKey(key) {
3338
+ _unsplashAccessKey = key;
3339
+ }
3340
+ function getUnsplashAccessKey() {
3341
+ return _unsplashAccessKey;
3342
+ }
4134
3343
 
4135
3344
  // src/lib/index.ts
4136
3345
  initJsxCompat();