vibrant-tool 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8368,9 +8368,15 @@
8368
8368
  var Sh = Object.freeze({ __proto__: null, BaseFilter: Va, BlackWhite: nh, BlendColor: Ga, BlendImage: Ua, Blur: qa, Brightness: Ka, Brownie: $a$1, ColorMatrix: Qa, Composed: oh, Contrast: ah, Convolute: ch, Gamma: uh, Grayscale: gh, HueRotation: ph, Invert: mh, Kodachrome: eh, Noise: vh, Pixelate: yh, Polaroid: ih, RemoveColor: _h, Resize: xh, Saturation: Ch, Sepia: rh, Technicolor: sh, Vibrance: bh, Vintage: th });
8369
8369
  function createOverlays(canvas2, workarea) {
8370
8370
  canvas2.getObjects().filter((o2) => o2.name?.startsWith("overlay-") || o2.name?.startsWith("guideline-")).forEach((o2) => canvas2.remove(o2));
8371
- const zoom = canvas2.getZoom();
8372
- const cvW = canvas2.getWidth() / zoom;
8373
- const cvH = canvas2.getHeight() / zoom;
8371
+ const vt2 = canvas2.viewportTransform;
8372
+ const zoom = vt2 ? vt2[0] : canvas2.getZoom() || 1;
8373
+ const panX = vt2 ? vt2[4] : 0;
8374
+ const panY = vt2 ? vt2[5] : 0;
8375
+ const vX0 = -panX / zoom;
8376
+ const vY0 = -panY / zoom;
8377
+ const vX1 = (canvas2.getWidth() - panX) / zoom;
8378
+ const vY1 = (canvas2.getHeight() - panY) / zoom;
8379
+ const cvW = vX1 - vX0;
8374
8380
  const waL = workarea.left ?? 0;
8375
8381
  const waT = workarea.top ?? 0;
8376
8382
  const waW = workarea.getScaledWidth();
@@ -8384,79 +8390,81 @@
8384
8390
  });
8385
8391
  const topOverlay = makeOverlay({
8386
8392
  name: "overlay-top",
8387
- left: 0,
8388
- top: 0,
8393
+ left: vX0,
8394
+ top: vY0,
8389
8395
  width: cvW,
8390
- height: Math.max(0, waT)
8396
+ height: Math.max(0, waT - vY0)
8391
8397
  });
8392
8398
  const bottomOverlay = makeOverlay({
8393
8399
  name: "overlay-bottom",
8394
- left: 0,
8400
+ left: vX0,
8395
8401
  top: waT + waH,
8396
8402
  width: cvW,
8397
- height: Math.max(0, cvH - (waT + waH))
8403
+ height: Math.max(0, vY1 - (waT + waH))
8398
8404
  });
8399
8405
  const leftOverlay = makeOverlay({
8400
8406
  name: "overlay-left",
8401
- left: 0,
8407
+ left: vX0,
8402
8408
  top: waT,
8403
- width: Math.max(0, waL),
8409
+ width: Math.max(0, waL - vX0),
8404
8410
  height: waH
8405
8411
  });
8406
8412
  const rightOverlay = makeOverlay({
8407
8413
  name: "overlay-right",
8408
8414
  left: waL + waW,
8409
8415
  top: waT,
8410
- width: Math.max(0, cvW - (waL + waW)),
8416
+ width: Math.max(0, vX1 - (waL + waW)),
8411
8417
  height: waH
8412
8418
  });
8413
- const printDPI = workarea.__printDPI || 72;
8414
- const bleedArea = workarea.__bleedArea !== void 0 ? Number(workarea.__bleedArea) : 0.125;
8415
- const safetyArea = workarea.__safetyArea !== void 0 ? Number(workarea.__safetyArea) : 0.125;
8416
- const bleedInsetX = Math.max(4, (bleedArea > 0 ? bleedArea : 0.0125) * printDPI * 1);
8417
- const bleedInsetY = Math.max(4, (bleedArea > 0 ? bleedArea : 0.0125) * printDPI * 1);
8418
- const safeInsetX = Math.max(6, (safetyArea > 0 ? safetyArea : 0.125) * printDPI * 1);
8419
- const safeInsetY = Math.max(6, (safetyArea > 0 ? safetyArea : 0.125) * printDPI * 1);
8420
- const trimLine = new jr({
8421
- name: "guideline-trim",
8422
- left: waL + bleedInsetX,
8423
- top: waT + bleedInsetY,
8424
- width: Math.max(10, waW - bleedInsetX * 2),
8425
- height: Math.max(10, waH - bleedInsetY * 2),
8426
- fill: "transparent",
8427
- stroke: "#ef4444",
8428
- // Red dashed trim line
8429
- strokeWidth: 1.2,
8430
- strokeDashArray: [4, 4],
8431
- strokeUniform: true,
8432
- selectable: false,
8433
- evented: false,
8434
- excludeFromExport: true
8435
- });
8436
- const safeLine = new jr({
8437
- name: "guideline-safe",
8438
- left: waL + bleedInsetX + safeInsetX,
8439
- top: waT + bleedInsetY + safeInsetY,
8440
- width: Math.max(10, waW - (bleedInsetX + safeInsetX) * 2),
8441
- height: Math.max(10, waH - (bleedInsetY + safeInsetY) * 2),
8442
- fill: "transparent",
8443
- stroke: "#10b981",
8444
- // Green dashed safe line
8445
- strokeWidth: 1.2,
8446
- strokeDashArray: [4, 4],
8447
- strokeUniform: true,
8448
- selectable: false,
8449
- evented: false,
8450
- excludeFromExport: true
8451
- });
8452
- canvas2.add(trimLine, safeLine, topOverlay, bottomOverlay, leftOverlay, rightOverlay);
8419
+ const isTextTemplate = workarea.__isTextTemplate === true || workarea.__hideGuidelines === true || workarea.__bleedArea === 0 && workarea.__safetyArea === 0 || typeof window !== "undefined" && ["text", "texttemplate", "text-template"].includes(
8420
+ (new URLSearchParams(window.location.search).get("designType") || new URLSearchParams(window.location.search).get("designtype") || new URLSearchParams(window.location.search).get("type") || "")?.toLowerCase()
8421
+ );
8422
+ const objectsToAdd = [topOverlay, bottomOverlay, leftOverlay, rightOverlay];
8423
+ if (!isTextTemplate) {
8424
+ const printDPI = workarea.__printDPI || 72;
8425
+ const bleedArea = workarea.__bleedArea !== void 0 ? Number(workarea.__bleedArea) : 0.125;
8426
+ const safetyArea = workarea.__safetyArea !== void 0 ? Number(workarea.__safetyArea) : 0.125;
8427
+ const bleedInsetX = Math.max(4, (bleedArea > 0 ? bleedArea : 0.0125) * printDPI * 1);
8428
+ const bleedInsetY = Math.max(4, (bleedArea > 0 ? bleedArea : 0.0125) * printDPI * 1);
8429
+ const safeInsetX = Math.max(6, (safetyArea > 0 ? safetyArea : 0.125) * printDPI * 1);
8430
+ const safeInsetY = Math.max(6, (safetyArea > 0 ? safetyArea : 0.125) * printDPI * 1);
8431
+ const trimLine = new jr({
8432
+ name: "guideline-trim",
8433
+ left: waL + bleedInsetX,
8434
+ top: waT + bleedInsetY,
8435
+ width: Math.max(10, waW - bleedInsetX * 2),
8436
+ height: Math.max(10, waH - bleedInsetY * 2),
8437
+ fill: "transparent",
8438
+ stroke: "#ef4444",
8439
+ // Red dashed trim line
8440
+ strokeWidth: 1.2,
8441
+ strokeDashArray: [4, 4],
8442
+ strokeUniform: true,
8443
+ selectable: false,
8444
+ evented: false,
8445
+ excludeFromExport: true
8446
+ });
8447
+ const safeLine = new jr({
8448
+ name: "guideline-safe",
8449
+ left: waL + bleedInsetX + safeInsetX,
8450
+ top: waT + bleedInsetY + safeInsetY,
8451
+ width: Math.max(10, waW - (bleedInsetX + safeInsetX) * 2),
8452
+ height: Math.max(10, waH - (bleedInsetY + safeInsetY) * 2),
8453
+ fill: "transparent",
8454
+ stroke: "#10b981",
8455
+ // Green dashed safe line
8456
+ strokeWidth: 1.2,
8457
+ strokeDashArray: [4, 4],
8458
+ strokeUniform: true,
8459
+ selectable: false,
8460
+ evented: false,
8461
+ excludeFromExport: true
8462
+ });
8463
+ objectsToAdd.unshift(trimLine, safeLine);
8464
+ }
8465
+ canvas2.add(...objectsToAdd);
8453
8466
  canvas2.sendObjectToBack(workarea);
8454
- canvas2.bringObjectToFront(trimLine);
8455
- canvas2.bringObjectToFront(safeLine);
8456
- canvas2.bringObjectToFront(topOverlay);
8457
- canvas2.bringObjectToFront(bottomOverlay);
8458
- canvas2.bringObjectToFront(leftOverlay);
8459
- canvas2.bringObjectToFront(rightOverlay);
8467
+ objectsToAdd.forEach((obj) => canvas2.bringObjectToFront(obj));
8460
8468
  canvas2.requestRenderAll();
8461
8469
  }
8462
8470
  function bringOverlaysToFront(canvas2) {
@@ -98808,15 +98816,18 @@ end`);
98808
98816
  };
98809
98817
  }
98810
98818
  function cmykToRgb(c2, m2, y2, k2) {
98811
- c2 = c2 / 100;
98812
- m2 = m2 / 100;
98813
- y2 = y2 / 100;
98814
- k2 = k2 / 100;
98815
- return {
98816
- r: Math.round(255 * (1 - c2) * (1 - k2)),
98817
- g: Math.round(255 * (1 - m2) * (1 - k2)),
98818
- b: Math.round(255 * (1 - y2) * (1 - k2))
98819
- };
98819
+ const cn2 = c2 / 100;
98820
+ const mn2 = m2 / 100;
98821
+ const yn2 = y2 / 100;
98822
+ const kn2 = k2 / 100;
98823
+ const base = 1 - kn2;
98824
+ const rTrans = (1 - cn2 * 0.9 - mn2 * 0.05 - yn2 * 0.02) * base;
98825
+ const gTrans = (1 - mn2 * 0.85 - cn2 * 0.12 - yn2 * 0.03) * base;
98826
+ const bTrans = (1 - yn2 * 0.92 - mn2 * 0.22 - cn2 * 0.02) * base;
98827
+ const r2 = Math.round(Math.max(0, Math.min(255, rTrans * 255)));
98828
+ const g2 = Math.round(Math.max(0, Math.min(255, gTrans * 255)));
98829
+ const b2 = Math.round(Math.max(0, Math.min(255, bTrans * 255)));
98830
+ return { r: r2, g: g2, b: b2 };
98820
98831
  }
98821
98832
  function rgbToHex(r2, g2, b2) {
98822
98833
  const toHex = (n2) => {
@@ -98841,16 +98852,6 @@ end`);
98841
98852
  const rgb2 = cmykToRgb(c2, m2, y2, k2);
98842
98853
  return rgbToHex(rgb2.r, rgb2.g, rgb2.b);
98843
98854
  }
98844
- function isOutOfCmykGamut(hex, threshold = 8) {
98845
- const rgb2 = hexToRgb(hex);
98846
- const cmyk2 = rgbToCmyk(rgb2.r, rgb2.g, rgb2.b);
98847
- const roundTrip = cmykToRgb(cmyk2.c, cmyk2.m, cmyk2.y, cmyk2.k);
98848
- const dr2 = rgb2.r - roundTrip.r;
98849
- const dg = rgb2.g - roundTrip.g;
98850
- const db = rgb2.b - roundTrip.b;
98851
- const distance = Math.sqrt(dr2 * dr2 + dg * dg + db * db);
98852
- return distance > threshold;
98853
- }
98854
98855
  function convertSvgColorsToCmyk(svg) {
98855
98856
  if (!svg) return svg;
98856
98857
  const hexToCmykString = (hex) => {
@@ -98953,6 +98954,7 @@ end`);
98953
98954
  return pdfDoc.save({ useObjectStreams: false });
98954
98955
  }
98955
98956
  const BASE_URL = "https://www.api.vibrantprintshop.com";
98957
+ const IMAGE_UPLOAD_URL = "https://upload.vibrantprint.com";
98956
98958
  async function apiFetch(endpoint, options = {}) {
98957
98959
  const token = localStorage.getItem("token");
98958
98960
  const response = await fetch(`${BASE_URL}${endpoint}`, {
@@ -98973,6 +98975,30 @@ end`);
98973
98975
  }
98974
98976
  return response.json();
98975
98977
  }
98978
+ async function apiUploadImage(endpoint, options = {}) {
98979
+ const token = localStorage.getItem("token");
98980
+ const isFormData = options.body instanceof FormData;
98981
+ const headers = {
98982
+ ...token && { Authorization: `Bearer ${token}` },
98983
+ ...options.headers
98984
+ };
98985
+ if (!isFormData && !("Content-Type" in (options.headers || {}))) {
98986
+ headers["Content-Type"] = "application/json";
98987
+ }
98988
+ const response = await fetch(`${IMAGE_UPLOAD_URL}${endpoint}`, {
98989
+ method: options.method ?? "POST",
98990
+ headers,
98991
+ body: isFormData ? options.body : options.body ? JSON.stringify(options.body) : void 0
98992
+ });
98993
+ if (!response.ok) {
98994
+ const message = await response.text();
98995
+ throw {
98996
+ status: response.status,
98997
+ message: message || "Something went wrong"
98998
+ };
98999
+ }
99000
+ return response.json();
99001
+ }
98976
99002
  const ProductService = {
98977
99003
  async getProductOptions(id) {
98978
99004
  const res = await apiFetch(
@@ -99472,7 +99498,7 @@ end`);
99472
99498
  {
99473
99499
  value: fileType,
99474
99500
  onChange: (e2) => setFileType(e2.target.value),
99475
- className: "w-full bg-[#1e1e1e] border border-gray-600 rounded-lg px-3 py-2 text-sm mb-5 focus:outline-none focus:ring-2 focus:ring-blue-500",
99501
+ className: "w-full bg-[#1e1e1e] border border-gray-600 rounded-lg px-3 py-2 text-sm mb-5 focus:outline-none focus:ring-2 focus:ring-[#02bc71]",
99476
99502
  children: [
99477
99503
  /* @__PURE__ */ jsxRuntime.jsx("option", { value: "png", children: "PNG" }),
99478
99504
  /* @__PURE__ */ jsxRuntime.jsx("option", { value: "jpeg", children: "JPEG" }),
@@ -99575,7 +99601,7 @@ end`);
99575
99601
  const currentSide = useHistoryStore.getState().currentPage || "front";
99576
99602
  const editorPages = useEditorStore.getState().pages || {};
99577
99603
  const historyPages = useHistoryStore.getState().pages || {};
99578
- const currentUrl = getWorkareaDataUrl(canvas2);
99604
+ const currentUrl = getWorkareaDataUrl(canvas2, 2);
99579
99605
  const opposingSide = currentSide === "front" ? "back" : "front";
99580
99606
  const opposingJson = editorPages[opposingSide] || historyPages[opposingSide]?.history?.at(-1) || null;
99581
99607
  let opposingUrl = "";
@@ -99613,6 +99639,10 @@ end`);
99613
99639
  setRotY((dragRef.current.rotY + (e2.clientX - dragRef.current.x) * 0.5) % 360);
99614
99640
  setRotX(Math.max(-60, Math.min(60, dragRef.current.rotX - (e2.clientY - dragRef.current.y) * 0.5)));
99615
99641
  };
99642
+ const workarea = canvas2 ? canvas2.getObjects().find((o2) => o2.name === "workarea") : null;
99643
+ const waW = (workarea?.width ?? 1050) * (workarea?.scaleX ?? 1);
99644
+ const waH = (workarea?.height ?? 600) * (workarea?.scaleY ?? 1);
99645
+ const cardAspectRatio = waW && waH ? waW / waH : 1.75;
99616
99646
  const currentProof = activeSide === "front" ? frontUrl : backUrl;
99617
99647
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fixed inset-0 z-[99999] bg-black/60 backdrop-blur-sm flex items-center justify-center p-4 select-none", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full max-w-4xl bg-white border border-gray-200 rounded-2xl shadow-2xl overflow-hidden flex flex-col h-[85vh]", children: [
99618
99648
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-5 py-3 border-b border-gray-200 bg-gray-50/90", children: [
@@ -99658,7 +99688,7 @@ end`);
99658
99688
  onMouseUp: () => setIsDragging(false),
99659
99689
  onMouseLeave: () => setIsDragging(false),
99660
99690
  className: `flex-1 relative overflow-hidden flex items-center justify-center bg-gradient-to-b from-slate-50 to-slate-100 ${viewMode === "3d" ? "cursor-grab active:cursor-grabbing" : ""}`,
99661
- children: viewMode === "3d" ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full h-full flex items-center justify-center [perspective:1200px]", children: [
99691
+ children: viewMode === "3d" ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full h-full flex items-center justify-center [perspective:1200px] p-6", children: [
99662
99692
  /* @__PURE__ */ jsxRuntime.jsxs(
99663
99693
  "div",
99664
99694
  {
@@ -99666,10 +99696,13 @@ end`);
99666
99696
  transform: `rotateX(${rotX}deg) rotateY(${rotY}deg)`,
99667
99697
  transformStyle: "preserve-3d",
99668
99698
  transition: isDragging ? "none" : "transform 0.15s cubic-bezier(0.1, 0.9, 0.2, 1)",
99669
- width: "420px",
99670
- height: "260px"
99699
+ aspectRatio: cardAspectRatio,
99700
+ width: cardAspectRatio >= 1 ? "min(85%, 680px)" : "auto",
99701
+ height: cardAspectRatio < 1 ? "min(80%, 520px)" : "auto",
99702
+ maxHeight: "80%",
99703
+ maxWidth: "85%"
99671
99704
  },
99672
- className: "relative will-change-transform shadow-2xl",
99705
+ className: "relative will-change-transform shadow-2xl rounded-xl",
99673
99706
  children: [
99674
99707
  /* @__PURE__ */ jsxRuntime.jsxs(
99675
99708
  "div",
@@ -99677,12 +99710,12 @@ end`);
99677
99710
  style: { backfaceVisibility: "hidden" },
99678
99711
  className: "absolute inset-0 rounded-xl overflow-hidden bg-white shadow-xl border border-gray-200",
99679
99712
  children: [
99680
- frontUrl ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: frontUrl, alt: "Front", className: "w-full h-full object-contain pointer-events-none" }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full flex items-center justify-center text-xs text-gray-400", children: "Front Design" }),
99713
+ frontUrl ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: frontUrl, alt: "Front", className: "w-full h-full object-fill pointer-events-none block" }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full flex items-center justify-center text-xs text-gray-400", children: "Front Design" }),
99681
99714
  /* @__PURE__ */ jsxRuntime.jsx(
99682
99715
  "div",
99683
99716
  {
99684
99717
  style: {
99685
- background: `linear-gradient(${120 + rotY * 0.8}deg, rgba(255,255,255,0.6) 0%, rgba(255,255,255,0) 50%, rgba(0,0,0,0.1) 100%)`
99718
+ background: `linear-gradient(${120 + rotY * 0.8}deg, rgba(255,255,255,0.4) 0%, rgba(255,255,255,0) 50%, rgba(0,0,0,0.08) 100%)`
99686
99719
  },
99687
99720
  className: "absolute inset-0 pointer-events-none mix-blend-overlay opacity-80"
99688
99721
  }
@@ -99696,7 +99729,7 @@ end`);
99696
99729
  style: { transform: "rotateY(180deg)", backfaceVisibility: "hidden" },
99697
99730
  className: "absolute inset-0 rounded-xl overflow-hidden bg-white shadow-xl border border-gray-200",
99698
99731
  children: [
99699
- isTwoSided ? backUrl ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: backUrl, alt: "Back", className: "w-full h-full object-contain pointer-events-none" }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full h-full flex flex-col items-center justify-center text-xs text-gray-400 p-4 text-center", children: [
99732
+ isTwoSided ? backUrl ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: backUrl, alt: "Back", className: "w-full h-full object-fill pointer-events-none block" }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full h-full flex flex-col items-center justify-center text-xs text-gray-400 p-4 text-center", children: [
99700
99733
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-gray-600", children: "Back Side" }),
99701
99734
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] text-gray-400 mt-1", children: "Design added to Back tab will render here" })
99702
99735
  ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full bg-slate-50 flex items-center justify-center text-xs text-gray-400", children: "Single-sided Product" }),
@@ -99704,37 +99737,29 @@ end`);
99704
99737
  "div",
99705
99738
  {
99706
99739
  style: {
99707
- background: `linear-gradient(${120 - rotY * 0.8}deg, rgba(255,255,255,0.6) 0%, rgba(255,255,255,0) 50%, rgba(0,0,0,0.1) 100%)`
99740
+ background: `linear-gradient(${120 - rotY * 0.8}deg, rgba(255,255,255,0.4) 0%, rgba(255,255,255,0) 50%, rgba(0,0,0,0.08) 100%)`
99708
99741
  },
99709
99742
  className: "absolute inset-0 pointer-events-none mix-blend-overlay opacity-80"
99710
99743
  }
99711
99744
  )
99712
99745
  ]
99713
99746
  }
99714
- ),
99715
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { transform: "translateZ(-1px)" }, className: "absolute inset-0 rounded-xl bg-gray-300 -z-10 shadow-md" })
99747
+ )
99716
99748
  ]
99717
99749
  }
99718
99750
  ),
99719
- /* @__PURE__ */ jsxRuntime.jsx(
99720
- "div",
99721
- {
99722
- style: { transform: `rotateX(90deg) translateZ(-150px)` },
99723
- className: "absolute w-72 h-24 bg-black/20 blur-xl rounded-full pointer-events-none"
99724
- }
99725
- ),
99726
99751
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute top-4 left-4 bg-white/80 backdrop-blur-md px-3 py-1 rounded-full border border-gray-200 text-[11px] text-gray-600 pointer-events-none flex items-center gap-1.5 shadow-sm", children: [
99727
99752
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-2 h-2 rounded-full bg-[#02bc71] animate-ping" }),
99728
99753
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Drag to Rotate 360°" })
99729
99754
  ] })
99730
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-6 flex flex-col items-center justify-center", children: [
99731
- isTwoSided && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-4 bg-gray-200 p-0.5 rounded-lg border border-gray-300 text-xs", children: [
99755
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full h-full flex items-center justify-center p-6", children: [
99756
+ isTwoSided && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute top-4 left-1/2 -translate-x-1/2 flex items-center gap-2 bg-gray-200/90 backdrop-blur-sm p-1 rounded-xl border border-gray-300 text-xs shadow-sm z-20", children: [
99732
99757
  /* @__PURE__ */ jsxRuntime.jsx(
99733
99758
  "button",
99734
99759
  {
99735
99760
  type: "button",
99736
99761
  onClick: () => setActiveSide("front"),
99737
- className: `px-3 py-1 rounded-md font-semibold cursor-pointer ${activeSide === "front" ? "bg-[#02bc71] text-white shadow-sm" : "text-gray-600 hover:text-gray-900"}`,
99762
+ className: `px-3 py-1 rounded-lg font-semibold cursor-pointer transition-all ${activeSide === "front" ? "bg-[#02bc71] text-white shadow-sm" : "text-gray-600 hover:text-gray-900"}`,
99738
99763
  children: "Front Proof"
99739
99764
  }
99740
99765
  ),
@@ -99743,12 +99768,32 @@ end`);
99743
99768
  {
99744
99769
  type: "button",
99745
99770
  onClick: () => setActiveSide("back"),
99746
- className: `px-3 py-1 rounded-md font-semibold cursor-pointer ${activeSide === "back" ? "bg-[#02bc71] text-white shadow-sm" : "text-gray-600 hover:text-gray-900"}`,
99771
+ className: `px-3 py-1 rounded-lg font-semibold cursor-pointer transition-all ${activeSide === "back" ? "bg-[#02bc71] text-white shadow-sm" : "text-gray-600 hover:text-gray-900"}`,
99747
99772
  children: "Back Proof"
99748
99773
  }
99749
99774
  )
99750
99775
  ] }),
99751
- currentProof ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-xl border border-gray-200 bg-white p-2 shadow-xl max-w-full", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: currentProof, alt: "2D Proof", className: "max-h-[50vh] w-auto object-contain rounded-lg" }) }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-8 text-center bg-gray-50 rounded-xl border border-gray-200 text-xs text-gray-400", children: [
99776
+ currentProof ? /* @__PURE__ */ jsxRuntime.jsx(
99777
+ "div",
99778
+ {
99779
+ style: {
99780
+ aspectRatio: cardAspectRatio,
99781
+ width: cardAspectRatio >= 1 ? "min(85%, 680px)" : "auto",
99782
+ height: cardAspectRatio < 1 ? "min(80%, 520px)" : "auto",
99783
+ maxHeight: "80%",
99784
+ maxWidth: "85%"
99785
+ },
99786
+ className: "relative rounded-xl overflow-hidden bg-white shadow-2xl border border-gray-200 flex items-center justify-center",
99787
+ children: /* @__PURE__ */ jsxRuntime.jsx(
99788
+ "img",
99789
+ {
99790
+ src: currentProof,
99791
+ alt: `${activeSide} 2D Proof`,
99792
+ className: "w-full h-full object-fill pointer-events-none block"
99793
+ }
99794
+ )
99795
+ }
99796
+ ) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-8 text-center bg-gray-50 rounded-xl border border-gray-200 text-xs text-gray-400", children: [
99752
99797
  "No preview available for ",
99753
99798
  activeSide,
99754
99799
  " side."
@@ -99856,8 +99901,13 @@ end`);
99856
99901
  const TemplateDesignService = {
99857
99902
  async getTemplates(data) {
99858
99903
  try {
99904
+ const params = new URLSearchParams();
99905
+ if (data.product) params.append("product", data.product);
99906
+ params.append("status", "true");
99907
+ params.append("page", String(data.page));
99908
+ params.append("limit", String(data.limit));
99859
99909
  const res = await apiFetch(
99860
- `/api/template-designs?product=${data.product}&status=true&page=${data.page}&limit=${data.limit}`,
99910
+ `/api/template-designs?${params.toString()}`,
99861
99911
  { method: "GET" }
99862
99912
  );
99863
99913
  return res || null;
@@ -99894,6 +99944,100 @@ end`);
99894
99944
  }
99895
99945
  }
99896
99946
  };
99947
+ const ShapeService = {
99948
+ async getAll(data) {
99949
+ const res = await apiFetch(
99950
+ `/api/cliparts?page=${data.page}&limit=${data.limit}&category=${data.category}&status=true`,
99951
+ {
99952
+ method: "GET"
99953
+ }
99954
+ );
99955
+ return {
99956
+ items: res.data || []
99957
+ };
99958
+ },
99959
+ async getAllExternal(data) {
99960
+ const res = await apiFetch(
99961
+ `/api/external/images?page=${data.page}&limit=${data.limit}&keyType=${data.keyType}&status=true${data.query ? `&query=${data.query}` : ""}`,
99962
+ {
99963
+ method: "GET"
99964
+ }
99965
+ );
99966
+ return {
99967
+ items: res.data || []
99968
+ };
99969
+ },
99970
+ async getTextTemplates(data) {
99971
+ const res = await apiFetch(
99972
+ `/api/text-templates?page=${data.page}&limit=${data.limit}&status=true`,
99973
+ {
99974
+ method: "GET"
99975
+ }
99976
+ );
99977
+ return {
99978
+ items: res.data || []
99979
+ };
99980
+ },
99981
+ async getTextTemplateById(id) {
99982
+ const res = await apiFetch(
99983
+ `/api/text-templates/${id}`,
99984
+ {
99985
+ method: "GET"
99986
+ }
99987
+ );
99988
+ return res.data || null;
99989
+ },
99990
+ async updateTextTemplate(id, body) {
99991
+ const res = await apiFetch(`/api/text-templates/${id}`, {
99992
+ method: "PUT",
99993
+ body
99994
+ });
99995
+ return res;
99996
+ },
99997
+ async convertImages(url) {
99998
+ const res = await apiFetch(`/api/convert-images`, {
99999
+ method: "POST",
100000
+ body: { file: url }
100001
+ });
100002
+ return res;
100003
+ }
100004
+ };
100005
+ function dataUrlToFile(dataUrl, filename = `design-${Date.now()}.png`) {
100006
+ const arr = dataUrl.split(",");
100007
+ const mime = arr[0].match(/:(.*?);/)?.[1] || "image/png";
100008
+ const bstr = atob(arr[1]);
100009
+ let n2 = bstr.length;
100010
+ const u8arr = new Uint8Array(n2);
100011
+ while (n2--) {
100012
+ u8arr[n2] = bstr.charCodeAt(n2);
100013
+ }
100014
+ return new File([u8arr], filename, { type: mime });
100015
+ }
100016
+ const UploadService = {
100017
+ async uploadFileApi(file, filename = `design-${Date.now()}.png`) {
100018
+ const formData = new FormData();
100019
+ formData.append("file", file, file.name || filename);
100020
+ formData.append("name", "vibrantprint");
100021
+ const res = await apiUploadImage(`/upload`, {
100022
+ method: "POST",
100023
+ body: formData
100024
+ });
100025
+ const data = res?.data || res;
100026
+ const url = data?.name || data?.Location || data?.url || data?.data?.name || data?.data?.Location || data?.data?.url || (typeof data === "string" ? data : "");
100027
+ if (!url) {
100028
+ throw new Error(res?.message || "Failed to upload file");
100029
+ }
100030
+ return url;
100031
+ },
100032
+ async uploadDataUrl(dataUrl, filename) {
100033
+ if (!dataUrl) return "";
100034
+ if (dataUrl.startsWith("http://") || dataUrl.startsWith("https://")) {
100035
+ return dataUrl;
100036
+ }
100037
+ const file = dataUrlToFile(dataUrl, filename || `design-${Date.now()}.png`);
100038
+ return await this.uploadFileApi(file, filename);
100039
+ }
100040
+ };
99897
100041
  function Topbar() {
99898
100042
  const [open2, setOpen] = React.useState(false);
99899
100043
  const [previewOpen, setPreviewOpen] = React.useState(false);
@@ -99901,8 +100045,40 @@ end`);
99901
100045
  const [saveSuccess, setSaveSuccess] = React.useState(false);
99902
100046
  const [saveError, setSaveError] = React.useState(false);
99903
100047
  const resetTour = useTourStore((s2) => s2.reset);
99904
- const templateId = getQueryParam("templateId");
99905
- const designType = getQueryParam("design");
100048
+ const templateId = getQueryParam("templateId") || getQueryParam("id");
100049
+ const designType = (getQueryParam("designType") || getQueryParam("designtype") || getQueryParam("type"))?.toLowerCase();
100050
+ const handleSaveTextTemplate = async () => {
100051
+ const canvas2 = useEditorStore.getState().canvas;
100052
+ if (!templateId || !canvas2) return;
100053
+ try {
100054
+ setIsSaving(true);
100055
+ useLoaderStore.getState().startLoading();
100056
+ const currentJson = canvas2.toObject(["name"]);
100057
+ const currentImage = getWorkareaDataUrl(canvas2);
100058
+ const uploadedImageUrl = await UploadService.uploadDataUrl(
100059
+ currentImage,
100060
+ `text-template-${templateId}-${Date.now()}.png`
100061
+ );
100062
+ const res = await ShapeService.updateTextTemplate(templateId, {
100063
+ jsonData: currentJson,
100064
+ image: uploadedImageUrl
100065
+ });
100066
+ if (res && (res.success || res.status || res.data || res._id)) {
100067
+ setSaveSuccess(true);
100068
+ setTimeout(() => setSaveSuccess(false), 3e3);
100069
+ window.location.href = "/design-configuration/text-templates";
100070
+ } else {
100071
+ throw new Error("Save failed");
100072
+ }
100073
+ } catch (err) {
100074
+ console.error("Save text template error:", err);
100075
+ setSaveError(true);
100076
+ setTimeout(() => setSaveError(false), 3e3);
100077
+ } finally {
100078
+ setIsSaving(false);
100079
+ useLoaderStore.getState().stopLoading();
100080
+ }
100081
+ };
99906
100082
  const handleSaveTemplate = async () => {
99907
100083
  const canvas2 = useEditorStore.getState().canvas;
99908
100084
  if (!templateId || !canvas2) return;
@@ -99920,26 +100096,33 @@ end`);
99920
100096
  const designData = isCurrent ? currentJson : editorPages[side] || historyPages[side]?.history?.at(-1) || null;
99921
100097
  if (!designData) return null;
99922
100098
  const sideImage = isCurrent ? currentImage : await renderPageDataUrl(designData, canvas2);
100099
+ const uploadedImageUrl = await UploadService.uploadDataUrl(
100100
+ sideImage,
100101
+ `template-${templateId}-${side}-${Date.now()}.png`
100102
+ );
99923
100103
  return {
99924
100104
  side,
99925
100105
  designData,
99926
- image: sideImage,
100106
+ image: uploadedImageUrl,
99927
100107
  order: i2 + 1
99928
100108
  };
99929
100109
  })
99930
100110
  );
99931
100111
  const validPages = pages.filter(Boolean);
99932
- const mainImage = validPages[0]?.image || currentImage;
100112
+ const primaryImage = validPages[0]?.image || "";
99933
100113
  const res = await TemplateDesignService.updateTemplateDesign(templateId, {
99934
- pages: validPages
100114
+ pages: validPages,
100115
+ image: primaryImage
99935
100116
  });
99936
100117
  if (res && res.success) {
99937
100118
  setSaveSuccess(true);
99938
100119
  setTimeout(() => setSaveSuccess(false), 3e3);
100120
+ window.location.href = "/design-configuration/template-designs";
99939
100121
  } else {
99940
100122
  throw new Error("Save failed");
99941
100123
  }
99942
- } catch {
100124
+ } catch (err) {
100125
+ console.error("Save template error:", err);
99943
100126
  setSaveError(true);
99944
100127
  setTimeout(() => setSaveError(false), 3e3);
99945
100128
  } finally {
@@ -99965,17 +100148,20 @@ end`);
99965
100148
  ]
99966
100149
  }
99967
100150
  ),
99968
- templateId && designType == "template" && /* @__PURE__ */ jsxRuntime.jsx(
100151
+ templateId && (designType === "template" || designType === "text") && /* @__PURE__ */ jsxRuntime.jsx(
99969
100152
  "button",
99970
100153
  {
99971
100154
  id: "save-template-button",
99972
- onClick: handleSaveTemplate,
100155
+ onClick: designType === "text" ? handleSaveTextTemplate : handleSaveTemplate,
99973
100156
  disabled: isSaving,
99974
100157
  className: `flex items-center gap-1.5 px-3.5 py-1.5 rounded-lg text-xs font-semibold cursor-pointer transition-all shadow-sm ${saveSuccess ? "bg-[#02bc71] text-white" : saveError ? "bg-rose-600 text-white" : "bg-[#02bc71] hover:bg-[#03945a] text-white"}`,
99975
100158
  children: isSaving ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center gap-1.5", children: [
99976
100159
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-3 w-3 border-2 border-white border-t-transparent rounded-full animate-spin" }),
99977
100160
  "Saving..."
99978
- ] }) : saveSuccess ? /* @__PURE__ */ jsxRuntime.jsx("span", { children: "✓ Saved!" }) : saveError ? /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Save Failed" }) : /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Save Template" })
100161
+ ] }) : saveSuccess ? /* @__PURE__ */ jsxRuntime.jsx("span", { children: "✓ Saved!" }) : saveError ? /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Save Failed" }) : /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
100162
+ "Save ",
100163
+ designType === "text" ? "Text Template" : "Template"
100164
+ ] })
99979
100165
  }
99980
100166
  ),
99981
100167
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex items-center", children: [
@@ -100221,7 +100407,7 @@ end`);
100221
100407
  onClick: () => setActivePanel(null)
100222
100408
  }
100223
100409
  ),
100224
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `fixed md:relative bottom-0 left-0 right-0 md:bottom-auto md:left-auto z-50 transition-all duration-300 ease-in-out`, children: [
100410
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `fixed md:relative bottom-0 left-0 right-0 md:bottom-auto md:left-auto z-50 transition-all duration-300 ease-in-out md:h-full`, children: [
100225
100411
  /* @__PURE__ */ jsxRuntime.jsxs(
100226
100412
  "div",
100227
100413
  {
@@ -100241,7 +100427,7 @@ end`);
100241
100427
  }
100242
100428
  )
100243
100429
  ] }),
100244
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 overflow-y-auto pt-4", children: panelName })
100430
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-h-0 overflow-y-auto pt-4 flex flex-col custom-scrollbar", children: panelName })
100245
100431
  ]
100246
100432
  }
100247
100433
  ),
@@ -100277,20 +100463,39 @@ end`);
100277
100463
  function deriveType(objects) {
100278
100464
  if (objects.length === 0) return "none";
100279
100465
  if (objects.length > 1) return "multiple";
100280
- const t2 = objects[0].type;
100281
- if (t2 === "group") return "group";
100466
+ const obj = objects[0];
100467
+ const t2 = obj.type?.toLowerCase();
100468
+ if (t2 === "activeselection") return "multiple";
100469
+ if (t2 === "group") {
100470
+ if (obj.name === "qr") return "qr";
100471
+ if (obj.name === "barcode") return "barcode";
100472
+ return "group";
100473
+ }
100282
100474
  if (t2 === "textbox" || t2 === "i-text" || t2 === "text") return "textbox";
100283
100475
  if (t2 === "image") return "image";
100284
- return t2;
100476
+ return t2 || "none";
100285
100477
  }
100286
100478
  const useSelectionStore = create((set) => ({
100287
100479
  type: "none",
100288
100480
  objects: [],
100481
+ activeColor: null,
100482
+ activeColorIndex: 0,
100289
100483
  setSelection: (objects) => set({
100290
100484
  objects,
100291
- type: deriveType(objects)
100485
+ type: deriveType(objects),
100486
+ activeColor: null,
100487
+ activeColorIndex: 0
100488
+ }),
100489
+ setActiveColor: (color, index = 0) => set({
100490
+ activeColor: color,
100491
+ activeColorIndex: index
100292
100492
  }),
100293
- clearSelection: () => set({ type: "none", objects: [] })
100493
+ clearSelection: () => set({
100494
+ type: "none",
100495
+ objects: [],
100496
+ activeColor: null,
100497
+ activeColorIndex: 0
100498
+ })
100294
100499
  }));
100295
100500
  function TemplatesPanel() {
100296
100501
  const { productData } = useProductStore();
@@ -100387,7 +100592,7 @@ end`);
100387
100592
  }
100388
100593
  )
100389
100594
  ] }),
100390
- /* @__PURE__ */ jsxRuntime.jsx("div", { ref: scrollRef, className: "h-[calc(100vh-125px)] overflow-auto", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-3 pb-12 mx-4", children: [
100595
+ /* @__PURE__ */ jsxRuntime.jsx("div", { ref: scrollRef, className: "h-[calc(100vh-125px)] overflow-auto custom-scrollbar", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-3 pb-12 mx-4", children: [
100391
100596
  templates.map((tpl) => /* @__PURE__ */ jsxRuntime.jsx(
100392
100597
  "button",
100393
100598
  {
@@ -100852,27 +101057,23 @@ end`);
100852
101057
  });
100853
101058
  qrGroup.canvas?.requestRenderAll();
100854
101059
  }
100855
- function extractSVGColors(svg) {
100856
- if (svg.type !== "group") return [];
100857
- const colors = /* @__PURE__ */ new Set();
100858
- svg.getObjects().forEach((obj) => {
100859
- if (typeof obj.fill === "string" && obj.fill !== "none" && obj.fill !== "" && obj.fill !== "transparent") {
100860
- try {
100861
- const hex = new Ie(obj.fill).toHex();
100862
- colors.add(`#${hex}`);
100863
- } catch (e2) {
100864
- }
100865
- }
100866
- if (typeof obj.stroke === "string" && obj.stroke !== "none" && obj.stroke !== "" && obj.stroke !== "transparent") {
100867
- try {
100868
- const hex = new Ie(obj.stroke).toHex();
100869
- colors.add(`#${hex}`);
100870
- } catch (e2) {
100871
- }
101060
+ const MAX_RECENT_ELEMENTS = 15;
101061
+ const useRecentElementsStore = create()(
101062
+ persist(
101063
+ (set) => ({
101064
+ recentItems: [],
101065
+ addRecentItem: (item) => set((state) => ({
101066
+ recentItems: [
101067
+ item,
101068
+ ...state.recentItems.filter((recent) => recent.image !== item.image)
101069
+ ].slice(0, MAX_RECENT_ELEMENTS)
101070
+ }))
101071
+ }),
101072
+ {
101073
+ name: "vibrant-recent-elements"
100872
101074
  }
100873
- });
100874
- return [...colors];
100875
- }
101075
+ )
101076
+ );
100876
101077
  function AllElements({
100877
101078
  elements: elements2,
100878
101079
  onBack,
@@ -100881,6 +101082,7 @@ end`);
100881
101082
  }) {
100882
101083
  const [search, setSearch] = React.useState("");
100883
101084
  const { isLoading } = useLoaderStore.getState();
101085
+ const { addRecentItem } = useRecentElementsStore();
100884
101086
  const clearSearch = () => {
100885
101087
  setSearch("");
100886
101088
  onSearch("");
@@ -100957,6 +101159,7 @@ end`);
100957
101159
  } else {
100958
101160
  addSVGToCanvas("svg", shape.image);
100959
101161
  }
101162
+ addRecentItem(shape);
100960
101163
  if (window.innerWidth < 768) {
100961
101164
  useSidePanelStore.getState().setActivePanel(null);
100962
101165
  }
@@ -100979,50 +101182,6 @@ end`);
100979
101182
  ] }) }) })
100980
101183
  ] });
100981
101184
  }
100982
- const ShapeService = {
100983
- async getAll(data) {
100984
- const res = await apiFetch(
100985
- `/api/cliparts?page=${data.page}&limit=${data.limit}&category=${data.category}&status=true`,
100986
- {
100987
- method: "GET"
100988
- }
100989
- );
100990
- return {
100991
- items: res.data || []
100992
- };
100993
- },
100994
- async getAllExternal(data) {
100995
- const res = await apiFetch(
100996
- `/api/external/images?page=${data.page}&limit=${data.limit}&keyType=${data.keyType}&status=true${data.query ? `&query=${data.query}` : ""}`,
100997
- {
100998
- method: "GET"
100999
- }
101000
- );
101001
- return {
101002
- items: res.data || []
101003
- };
101004
- },
101005
- async getTextTemplates(data) {
101006
- const res = await apiFetch(
101007
- `/api/text-templates?page=${data.page}&limit=${data.limit}&status=true`,
101008
- {
101009
- method: "GET"
101010
- }
101011
- );
101012
- return {
101013
- items: res.data || []
101014
- };
101015
- },
101016
- async getTextTemplateById(id) {
101017
- const res = await apiFetch(
101018
- `/api/text-templates/${id}`,
101019
- {
101020
- method: "GET"
101021
- }
101022
- );
101023
- return res.data || null;
101024
- }
101025
- };
101026
101185
  const PixabayIcon = () => /* @__PURE__ */ jsxRuntime.jsx(
101027
101186
  "svg",
101028
101187
  {
@@ -101047,6 +101206,7 @@ end`);
101047
101206
  const FlatIcon = "data:image/webp;base64,UklGRhAEAABXRUJQVlA4TAMEAAAvX8AXEFfHoG0bQS5/0Lm7fxAQ2kiSJGX1Dn+Iy2Lv7Uo7bNtGkpi7/vv9LSaSJEXK/38MjSeQOCTsvS0kSKoJb6QA1AFJDYEC0UiSGiAkSEDSRtKmzkf5RO0kGlIhCSGQgKQ7JdAOkIRAYlOnHSCknWCnph0CJe7UEE0CdTTdAdKd7kBGUthgjsE/hLQhDv1HAxJDG/9TQwASAp7T5wcItCEElDYEbPqbMhKAb28hwBP8OpTAT6BIQM5ylM8jkUABYI4cB08Mhr2eDM4xeI5hzzFnz+kVcYhXDBFxiiIiqohoIqKLOCanjJ7er6WUGBxcAOSQj0xAASAFcPCT5cHQZn/0M5KCRAJqt22bkqRTttm2bdu2bdu6+TLqlW3bbHetiOhg27aN+zPinRe452N9iOj/BHi1WizkMDNXKL3h66xkMTdbSfNeHpPz7ypvYvZbCe9h+Ns1ecte995XML3ifda2rK9ifLVoXbFgXSFnXY4tWrt4XDPHJMXjYhfM1OV3X3n5FQ2//OpRNWMfuOrupVMC+fDs3tLU/TYB0cudRXqd+1EQ80+W5nZYFgFrDpPaU+YFMOJRafLpM4H4MUl+dETrzTqmSYM+c8APOynHzGq9FXs26ZrxwMRLRd1reettOrA5u/wCuPf7agcvaL0JFzbn6Qww5yTRrxnXeu6LQc04YhUQLRR92+8IcNwtKfa57do6LxvtgE/2S/FwHAK/7qrJs1PGpR/rgNF3in7QxwSZeTzF7l/TwJ+20Tq91h4GK47W5Ppx9U05X/RT5xBotKiHNvCfWM8kuN+7aQO+dKEw/RxNTn7w/uT77srUbDhG9OvGE+5/Q7W0R84GMk+LvvNvBDz69gb0WtcGfLZHikfikPh0r/oumAKMvVn0w1cTdPtzHerZ+l+AvwdqXZdEYbHp+HruHQnMOFP0s6YTuPure7qDPgaixV21wX8T/KSLUnV6KQLWHCb6rWPD49vt0pw2G4gfFH3PzzBw9D0p+v0PsHLnFM9kLOCjAzR5aCQw+Wzt2A2YGL2QYocfAff18KRufzobmHOSJhdPAsbeknT+FIx03/TX+qx1wFd71mzzM2ZOvESTE+YC7Ztr7hxtBz/sqMmLEbDxaJF9P8HQ+MkUe38KtK3vKc9HlrDmUE3uGA1MO+O4OZgavdJF2+ongDFjMHbmGZqcNwWL3fdDtG5/OIsYd5Mmx2wwiV930+SpjEmZJ1LsscEkVhylnTjDJvfBsKTtv3c20b54fxHpeMjnbVgdrdx84w2vbnAYHo0ZHbGF7Zx1uYJ1haJ1xap1VZ+1Let9xbaK9z5vWd5779+z7L0a/6Zdb/rkd/I25d/zKctZe7IVX2e1WMjZkSsUq14FAA==";
101048
101207
  function ElementsPanel() {
101049
101208
  const { startLoading, stopLoading, isLoading } = useLoaderStore();
101209
+ const { recentItems, addRecentItem } = useRecentElementsStore();
101050
101210
  const [elementsSVG, setElementsSVG] = React.useState([]);
101051
101211
  const [page, setPage] = React.useState(1);
101052
101212
  const [category, setCategory] = React.useState(null);
@@ -101156,45 +101316,96 @@ end`);
101156
101316
  ] })
101157
101317
  ] }),
101158
101318
  /* @__PURE__ */ jsxRuntime.jsx("hr", { className: "text-neutral-800 h-[0.5px]" }),
101159
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[calc(100dvh-180px)] overflow-auto", children: elements.map((item, i2) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-4", children: [
101160
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "py-5", children: [
101161
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
101162
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-sm font-semibold mb-3", children: item.category }),
101163
- /* @__PURE__ */ jsxRuntime.jsx(
101164
- "div",
101319
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "h-[calc(100dvh-180px)] overflow-auto custom-scrollbar", children: [
101320
+ recentItems.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-4", children: [
101321
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "py-5", children: [
101322
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
101323
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-sm font-semibold mb-3", children: "Recent items" }),
101324
+ recentItems.length > 3 && /* @__PURE__ */ jsxRuntime.jsx(
101325
+ "div",
101326
+ {
101327
+ className: "underline cursor-pointer",
101328
+ onClick: () => {
101329
+ setElementsSVG(recentItems);
101330
+ setCategory("Recent items");
101331
+ setHasMore(false);
101332
+ },
101333
+ children: "View all"
101334
+ }
101335
+ )
101336
+ ] }),
101337
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-4 md:grid-cols-3 gap-3", children: recentItems.slice(0, 3).map((shape, i2) => /* @__PURE__ */ jsxRuntime.jsx(
101338
+ "button",
101165
101339
  {
101166
- className: "underline cursor-pointer",
101167
- onClick: () => getSVG(null, item?.category, 1),
101168
- children: "View all"
101169
- }
101170
- )
101171
- ] }),
101172
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-4 md:grid-cols-3 gap-3", children: item.data.map((shape, j2) => /* @__PURE__ */ jsxRuntime.jsx(
101173
- "button",
101174
- {
101175
- onClick: () => {
101176
- addSVGToCanvas("svg", shape.image);
101340
+ onClick: () => {
101341
+ addSVGToCanvas("svg", shape.image);
101342
+ addRecentItem(shape);
101343
+ },
101344
+ className: "w-full cursor-pointer flex items-center justify-center",
101345
+ children: /* @__PURE__ */ jsxRuntime.jsx(
101346
+ "img",
101347
+ {
101348
+ src: shape.image,
101349
+ alt: shape.category,
101350
+ width: 150,
101351
+ height: 150,
101352
+ className: "w-full h-24 object-contain"
101353
+ }
101354
+ )
101177
101355
  },
101178
- className: "w-full cursor-pointer flex items-center justify-center",
101179
- children: /* @__PURE__ */ jsxRuntime.jsx(
101180
- "img",
101356
+ shape._id || shape.image || i2
101357
+ )) })
101358
+ ] }),
101359
+ /* @__PURE__ */ jsxRuntime.jsx("hr", { className: "text-neutral-800 h-[0.5px]" })
101360
+ ] }),
101361
+ elements.map((item, i2) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-4", children: [
101362
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "py-5", children: [
101363
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
101364
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-sm font-semibold mb-3", children: item.category }),
101365
+ /* @__PURE__ */ jsxRuntime.jsx(
101366
+ "div",
101181
101367
  {
101182
- src: shape.image,
101183
- alt: item.category,
101184
- width: 150,
101185
- height: 150,
101186
- className: "w-full h-24 object-contain"
101368
+ className: "underline cursor-pointer",
101369
+ onClick: () => getSVG(null, item?.category, 1),
101370
+ children: "View all"
101187
101371
  }
101188
101372
  )
101189
- },
101190
- j2
101191
- )) })
101192
- ] }),
101193
- /* @__PURE__ */ jsxRuntime.jsx("hr", { className: "text-neutral-800 h-[0.5px]" })
101194
- ] }, i2)) })
101373
+ ] }),
101374
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-4 md:grid-cols-3 gap-3", children: item.data.map((shape, j2) => /* @__PURE__ */ jsxRuntime.jsx(
101375
+ "button",
101376
+ {
101377
+ onClick: () => {
101378
+ addSVGToCanvas("svg", shape.image);
101379
+ addRecentItem({
101380
+ _id: shape.image,
101381
+ status: true,
101382
+ category: item.category,
101383
+ image: shape.image,
101384
+ largeImage: shape.image,
101385
+ tags: []
101386
+ });
101387
+ },
101388
+ className: "w-full cursor-pointer flex items-center justify-center",
101389
+ children: /* @__PURE__ */ jsxRuntime.jsx(
101390
+ "img",
101391
+ {
101392
+ src: shape.image,
101393
+ alt: item.category,
101394
+ width: 150,
101395
+ height: 150,
101396
+ className: "w-full h-24 object-contain"
101397
+ }
101398
+ )
101399
+ },
101400
+ j2
101401
+ )) })
101402
+ ] }),
101403
+ /* @__PURE__ */ jsxRuntime.jsx("hr", { className: "text-neutral-800 h-[0.5px]" })
101404
+ ] }, i2))
101405
+ ] })
101195
101406
  ] });
101196
101407
  }
101197
- const isTextbox = (obj) => obj.type === "textbox";
101408
+ const isTextbox = (obj) => obj.type === "textbox" || obj.type === "i-text" || obj.type === "text";
101198
101409
  async function addTextToCanvas(fontSize, width, textName) {
101199
101410
  const canvas2 = useEditorStore.getState().canvas;
101200
101411
  if (!canvas2) return;
@@ -101275,13 +101486,10 @@ end`);
101275
101486
  page,
101276
101487
  limit: 30
101277
101488
  }).then((res) => {
101278
- const newItems = res.items.filter(
101279
- (x2) => x2.status && x2.image
101280
- );
101281
101489
  setTextElements(
101282
- (prev) => page === 1 ? newItems : [...prev, ...newItems]
101490
+ (prev) => page === 1 ? res.items : [...prev, ...res.items]
101283
101491
  );
101284
- if (newItems.length < 20) setHasMore(false);
101492
+ if (res?.items?.length < 20) setHasMore(false);
101285
101493
  }).finally(() => stopLoading());
101286
101494
  }, [page]);
101287
101495
  React.useEffect(() => {
@@ -101365,7 +101573,7 @@ end`);
101365
101573
  )
101366
101574
  ] }),
101367
101575
  /* @__PURE__ */ jsxRuntime.jsx("hr", { className: "text-neutral-800 h-[0.5px] my-4" }),
101368
- /* @__PURE__ */ jsxRuntime.jsx("div", { ref: scrollRef, className: "h-[calc(100vh-250px)] overflow-auto", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-3 pb-12 mx-4", children: [
101576
+ /* @__PURE__ */ jsxRuntime.jsx("div", { ref: scrollRef, className: "h-[calc(100vh-250px)] overflow-auto custom-scrollbar", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-3 pb-12 mx-4", children: [
101369
101577
  textElements.map((tpl, index) => /* @__PURE__ */ jsxRuntime.jsx(
101370
101578
  "button",
101371
101579
  {
@@ -101547,6 +101755,23 @@ end`);
101547
101755
  );
101548
101756
  }
101549
101757
  const VibrantLogo = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAABGCAMAAAC+PCsEAAAC7lBMVEUAAAAyMjIzMzMwMDAzMzNtmZJxfnoyMjI1NTUwMDBvmpYaGhoaFxdtmZIyMjJtmJIzMzNsmZFum5RtmZJiqJ8xMTEyMjJtmJIyLzAzMzNum5RwmpJtmZJtmZJtmZJtmZImJCVtmZJtmZImJiZtmZJtmpMvLy91pZRtmZJsmZJtmZI2NTYpJidum5QxMTFtmZJvmpUkIiRtmZJtmZJtmZJtmZIsLCwvLy9tmZIzMzMzMzMuLi5tmZIvLy8sLCxtmZL/Uml3qqIyMjL+DQEwMDAkIiPeanUmobj/YXoqKir2aXr2gY0p98tvopkvLy/xeoiS2V/+cI78ZX4F9PQaGhoJ4eYX7+mQ8YQ1NTUB/f32e5L2Y3EB/v4Lv6T4GRkC/fv8Umb57CYH+PME9fPeKjWBuqVUSE3bV2AL7Oxq7XT8OUcA/O0D9/P0R1n8U2T4d4gD9vXuFBo1Ozz+FBBKSUz/+yOiSVtJTE3rJS+Za2kQ7erGXGZXrWV1dEL/PVDhZXtN98fqGyA/aI72jJuA/pH5eIhXmnlvXl9MSk1mYj+//1k8+s0D/fT/rRL/bgv7GiEMkcbQ/1s8o16k/3wzMzM2NjYA//84ODgA//MA/+oA/u//OQMB/+AC/9b/gQv/Wgr/RAf/HwQA//gF/80E/az/dQ3/FAED7aNtm4//vgP/bBn/Xxj/4UX/yAb/KgQD/7z/TgT/R17/TxQC29b/Ywj/5Qb/bAMAztD/oAn/0ggMzMMC5rP/RUv/MkX/8Qb/2AX/BwID1Lf/UTT+3y//Kyn//QUP/OVS6qr/7C7/3R0Cvbz/Xkn/0jD//Sz/6B///h3/rgYA4ekC88w/7bPNTGP/bkjiwhT/jw0HqqkEnp1k55GhgFX/7Dv/NhID8OsWvLA7t62a3Xj/bmzM51mz2VNMTE5JSkz/ey/hWyWFkGrj40y2cUo4TkXDcj/sTDXQvij0xQ1WkHQ74myOr2nJ1l4/qlnNz1FJPUvHai7J1CjznehDAAAAlHRSTlMAzZQW274Fpk8ICWo5xOc3yS4VtA24RxwN8GZc3s/JqkH16I2BeFYR1YudOzkmdVQ/H6FJuKabh2/247+urmqW/ZT5tmQrD/v7el4h/jDWMv710n5hTTj87bmie/b22tnMs6+PWEwuHBn69unStJp8bjf89LezonVzaSj08t/TuKahj4dxRUD67NvWxMKzs66hmIeEWUQ3oQAADLhJREFUaN7UlkloE1EYx19U3JEcVOxNEPSgCF4sWpTgRtDgUq1SkZaqYLUuVRHEi4p6crvMvMyW2coMzkoakpCIJqExTUpXKG2hVsEFFEEUFMGbLyMdzTSJgh4mP0KYvI/M+/75/v83AWB7kQdXrrS13WhubgJ/YOnx5uNtbW1X7m9HtJwELmK7XCQ5WBh6A+FZ8AcOQnhkqDA4zGdkOZO5CNzETVkWMmZqsBA7AuEJUJUmCPOxQmE4nhFkuWUXcBfbZUHIcMNThb48hFXN1X4M9maHpoYTGUGQL58CLmNuiyyYmYnhqWw2AI+1g8qchfBNnz5oCIIpuMxXFrsuC6YpK8N69oMIW6sHBOmI0UiH7KqgT3NRNk2Bjg3qfdVichjC0aw+lYsLtClfBa7kqmDSQjxX0JG54GFQnlZIfdD1XMKkaeEhcCk3BZo3R3K6/oaoZK4TyFhZPZfkadpsmQvcSovJ83wypw9MQniwgrECWX0oJb3g6csuDPo0p57RYV5KDekDXRC2lz2xiLcDQ7kEH+afuTQgPzn5LMxFErm+gVcibJ5Z7kRJH+hLJblI+MU54GrOveA4LZ3qG0B5b5r5KBTJr8lYiuU4/ozbnuhOc52JRLWJVMx4JRKtM5IuinkjnRrRotzT3cDl7H4SlVQllTQCIuwsLV2DJPXNiCUlNd5zHriecz2SxKZjyitIHltaUjlOiKNKOjahSprP5cayzOVTWTURMz73EsTBkqNXJOFbJaaorNTtemMVueBjWVVJK5OOkTRTMKAk06zEjjWCmqBxjFXZtPFJRCMpHch+JZ1Q2Xf+faAm2HfgHasl0sooJDuW/jYQsXfcUFSJfX0U1AhHX7OqZCj7IUF12gMhSJhXkhOaNHagBpJuj0TSEsnxXki12kcWRcD9xogWl/y3QM1w9LUUVRUlDwmiCVi0UxREzmKLA3Hvn94Z7DkwFo9MGEVv3QMWB9FA8sYIF1f9NXH0TnPLr0a1kXFKJKhrALGwg6LQmSVx8f4aOXrtkfTHI6wxCinxBkA0UQRBfE9EojU2EJQSn8pxI6chKQbWWFEnYeCzFIn3H1ho1VesmL0A1AJ7/P3RCPsWkqR4G4BrJMnASTQQzXfBKs/2bF5Sb12t9TjxLqtfDmzWVy7bLFjm8a50LG3xLgZO5nmq0LCg0kg0LjzeSzDiXRT1opD98Ui037/rp5BNGDbPupqPlcNj97WxXNm7FvzOoiBacgipw+s2AAdzsGqsqPAs8fdEeTYAGbJ3DThLMhTxKcxZA7GELMGwWdbVcnQLPBjEEUELHEPg2Hzwk1lW3QaVi1xaDX4xd0lxcXmpkDkYPsfZmte+V3E33HHnCkJAY3c0HJ6EDEMeXkoyIbGL5bl+/54yQvCNhxb9xtZZqDG8bsW0EHxJSXX9vLqi0J3AZoclzusUguENM4TgHnurQxvR11b92vlQpczu9vVw9BfIhJgTnWQoBPPhsNZ9HZQTUmoUVG1Aje2wJ+JxlDcsw7Dgsl89b8awopSVTiFYcItTCNrUZh360gbwZ3b5n3D0R4IJhTo60Bt8RHM9vjvlhCAbOUARwrfYQuaAcma3e9iKPtRvRr+/UwhSUl8qZDqYdjoX/9UJ3K3x71+SoS4GDYSBz2nuSSOoLsQGjcRbRQjqHV89nZDNWHAvqEddL3ZaC61t/Q9C9vl6wvxjKtQVYtCL+shHui/8rZAt1YWstK2HRKFAHQIbtmHBBoeQhjpUmv/vQkDjD9Lr5yWROAoA+JuUbNiQLsJ4Exa8KEEXRSWJ0EKFuuxBkAXPwXbYw16C9rT/wXyxXVcIRqjbJHMYMD0UnropHVpiq2MRdN7jvnnojPMrmr7vIjqOMx/f9/vem8FZ++GooygGRHs8vtyovRWyI8tzS2vRH0IlKyrS91jKBmEfEghppfkhjd5l+/RIUdROR/nZPzvGlfVGSBI/LL6SkRKdZJas4rQzFeyQj1Bpyaxe5obUNgbtp7uJomJKfj3gymr4QBxdWSpuUvn1hwhMzoXMfZ+njGXph+wQyKBEEHkh0By0b8cIUX9P7k7bg17NByKsx+Yjjx+F02BCdpw1LYLLKOLMHaXECYEYw0rNDWn02s9jDSGdyRghTfCEeMY6WJBocilpRWnBgH5dm+YG+6VoDjMs4YSIAkoyvJBab/RP0xW1q2jjp7+9RgCIvLJmQjwil5gmhMlsVmGxcLGCEwKhMEoqnBBojh51faJ2VW18O+rt+kEyxVVbZPAoy0v+kHBqVt0Y7RDLvOyEQDlOWD5IY/jS188NiHY7ai76QVLgtwvo7jbDZhirKh4piXPVrWSdhSmJuSCQaGFjXOKD/Bi+3OsXarerac+jfYC3ll9q7XJ5VrUkUaLAV0xB1NZuVsCKVfpTnBAoYunKlbkgW3t7M8jBsBoEkjYGKI/ym2CUKishK9kFMyL4PuaGQBYl4RAPBJrDh/5Vt3tyrl8Pa0EgkKd7ckFgHSVpKyHuSLsh1E52RB7I/nB7BjmEQBC8qOAxotBGEMyS5REFJ4T4LaOdxN4PqSLk/pwg+8EgAi1/d0ZonZTM/h6xRcaYeZc9IIsCw/cckBpCbhByol9XA0GkHDZFT4gUZ6wuAkAKz6w4n7kQUnBC6EAdj8jvh8DhFNK/3g0EqeB1V70gNCayLNDiq0vgiAijwuWCQHmTyTyQL9tXNxcIuTjY8ockvB44mByyIO6Rkb5SAWeUaZe4INROuDLS2L76Q5Dv4A9JSiFbpHCSpbnCG7KMKckYOzfuSghta7nsBYFPXJAqQj4bkG+vQHLR+FzU47JMjd0HQiX4fztmsupEEIXhH6UKbSxiD9LSU263HdLdNyGxB40SCVdQV0E3oht3DrgVUQTdiDitHPAZzDu48i18AV25URC3Vp30zXAdEnSj4AeVFKmbc853T1d1kpOHaId8z2FZ7okfishvJ38gclaJvH79+s2dZZGLKz40njyKmchxEMtH8K6TOxqy+IteB3uP7xSh28nvi2hn3smOvHn9to8F6MvDkZ+KHNt/9PDix3iO7/63B7CLdsgPWnJM3Wi4FDmCnRx4dWJBhI7qtXl299ylS9evn9GwyN7OxsbhqdI+SWezIx868nlTzjq7F+va6JzCd2zuxt7NvfghpzY6G0BnHyXYwcY8lszc2XcQa3P+09P0/vPP9/Gvwx9deGi//HIZy7hbW1t9AO2WnIwBjK8AXSiMKzC20tYoBoouDBuKBl2aWmyKoYsF9OVOt+S6Q2mdOgFF1KYx4q0tR6312pSG05i/awW3rgMP7p3GElrkMxaqUieMidAFUgGUMeXzuM38CasA34HtkV5VqpUoSQdV5DdQYwvP6y9YeV46yKOkD9ihoARE4kBhRowljMOKdAB9z5JDA3QZ1ck8X8evuXxXv/riHpYxTAvoejrcJoBe0kZLTqpJocphbfUHlLoHPgk1AGkG2BMqzBIxaprMGHjYxgpH1IxhCugmVIIxJP2JCUWzB8AfQBNdAA2m0TDCFIpysOraenLj68c7O0VKQwUt4A5VBZ5NIqNR0iARSjMVsZtNUYtUJnbgDJHn2CZlqKlFkBSQCMek50qJZCNYggNoM0tm4cgY1uTWxw8f6HTQMMMSrZ4rSo4iKRqOl4FEmo1+aKCxLMJQDUlE+0Hz87Di2EYUmNEN3IbrM7VY+HBKANK518uiLrjfzLJsKLjMAgR9rMnmh/c36dofL7TJ9wKRa0A/CifNGLWIgzSBvSwSaPBipDkM36KdmWVxd7vcZOJgBqMEA7muw07CiZlb9PIAluo0RpEQTE54IsqyFEEtYmNN+OP3e4Br7pU2ZrRLHYSbj0MbcxE0c61cFrFhhH0nB59eKKnZDAcg7NBpJwOMLRCBC0ks12PoQ93rTWUnvi8mGYCqftv80iKRAuty8/1BXL3zcHmPdGsRE25kLYiAidxaEjEA3WcxMPIwhekgqlxVZeYGiFaEKWYfOoMeUo7mqFEUhSfnlQNicbNzpD5qP6zi9k3sub1n87BlzUXYdkcYMApUFUOADnMtDFUaoURKKeIbdPCMAC0Sam54GaaIGNDYpIUa37epziGkiAEnUq2NOCR5JZUGtUigTzPQ4JGp0Qk3wCp271EeV6/ap+ciwbgWEQCCEUdsAqaKVZ9agYouXNiJAUkqRWAlUVkNIxM1RTjKvJYezUyCkFVDT2iArhqZMQ1mCkU36k7DUxgd0wx9OWSGqFmVoWlgJQev7j64l3OOGZprgNBd9dCSMXtArzF9TYrYLleaOixHo+QkbsSBH8TzOC0hBoA7E9Fi4Qcp3bddDWjHbTg2CKerwhPcMUAZaMhZGvishf/8hXwDf/LQfNeBDzwAAAAASUVORK5CYII=";
101758
+ const VibrantImageService = {
101759
+ async getFolderData(params = {}) {
101760
+ const { item = "Small", page = 1, limit = 30, search = "" } = params;
101761
+ const query = new URLSearchParams();
101762
+ if (item) query.set("item", item);
101763
+ if (page) query.set("page", String(page));
101764
+ if (limit) query.set("limit", String(limit));
101765
+ if (search) query.set("search", search);
101766
+ const res = await apiUploadImage(
101767
+ `/folder-data?${query.toString()}`,
101768
+ {
101769
+ method: "GET"
101770
+ }
101771
+ );
101772
+ return res;
101773
+ }
101774
+ };
101550
101775
  function useSocialAuth({
101551
101776
  onImageSelect
101552
101777
  }) {
@@ -101704,10 +101929,12 @@ end`);
101704
101929
  ];
101705
101930
  function PhotosPanel() {
101706
101931
  const { startLoading, stopLoading, isLoading } = useLoaderStore();
101932
+ const [source, setSource] = React.useState("external");
101707
101933
  const [socialImages, setSocialImages] = React.useState([]);
101708
101934
  const [page, setPage] = React.useState(1);
101709
101935
  const [hasMore, setHasMore] = React.useState(true);
101710
101936
  const [category, setCategory] = React.useState(imageOptions[0]);
101937
+ const [searchQuery, setSearchQuery] = React.useState("");
101711
101938
  const scrollRef = React.useRef(null);
101712
101939
  const {
101713
101940
  handleGoogleAuth,
@@ -101715,40 +101942,74 @@ end`);
101715
101942
  handleFacebookAuth,
101716
101943
  handleInstagramAuth
101717
101944
  } = useSocialAuth({
101718
- onImageSelect: (url) => {
101719
- addImageToCanvas(url);
101720
- if (window.innerWidth < 768) {
101721
- useSidePanelStore.getState().setActivePanel(null);
101945
+ onImageSelect: async (url) => {
101946
+ try {
101947
+ startLoading();
101948
+ const image = await ShapeService.convertImages(url);
101949
+ const imageUrl = image?.success ? image?.url : null;
101950
+ if (imageUrl) addImageToCanvas(imageUrl);
101951
+ } catch (error2) {
101952
+ console.error("Failed to convert image:", error2);
101953
+ } finally {
101954
+ stopLoading();
101955
+ if (window.innerWidth < 768) {
101956
+ useSidePanelStore.getState().setActivePanel(null);
101957
+ }
101722
101958
  }
101723
101959
  }
101724
101960
  });
101725
101961
  const fetchImages = React.useCallback(
101726
- async (q2, pageNo) => {
101962
+ async (q2, pageNo, currentSource = source) => {
101727
101963
  startLoading();
101728
101964
  try {
101729
- const res = await ShapeService.getAllExternal({
101730
- page: pageNo,
101731
- limit: 30,
101732
- keyType: "all",
101733
- isVector: false,
101734
- query: q2
101735
- });
101736
- const newItems = res.items.filter(
101737
- (x2) => x2.status && x2.image
101738
- );
101739
- setSocialImages(
101740
- (prev) => pageNo === 1 ? newItems : [...prev, ...newItems]
101741
- );
101742
- if (newItems.length < 20) setHasMore(false);
101965
+ if (currentSource === "vibrant") {
101966
+ const res = await VibrantImageService.getFolderData({
101967
+ item: "Small",
101968
+ page: pageNo,
101969
+ limit: 30,
101970
+ search: q2 || void 0
101971
+ });
101972
+ const newItems = (res.files || []).map((f) => ({
101973
+ _id: f.key,
101974
+ status: true,
101975
+ category: "vibrant",
101976
+ image: f.smallUrl || f.url,
101977
+ largeImage: f.largeUrl || f.url,
101978
+ tags: [f.name]
101979
+ }));
101980
+ setSocialImages(
101981
+ (prev) => pageNo === 1 ? newItems : [...prev, ...newItems]
101982
+ );
101983
+ if (newItems.length < 20) setHasMore(false);
101984
+ else setHasMore(true);
101985
+ } else {
101986
+ const res = await ShapeService.getAllExternal({
101987
+ page: pageNo,
101988
+ limit: 30,
101989
+ keyType: "all",
101990
+ isVector: false,
101991
+ query: q2
101992
+ });
101993
+ const newItems = res.items.filter(
101994
+ (x2) => x2.status && x2.image
101995
+ );
101996
+ setSocialImages(
101997
+ (prev) => pageNo === 1 ? newItems : [...prev, ...newItems]
101998
+ );
101999
+ if (newItems.length < 20) setHasMore(false);
102000
+ else setHasMore(true);
102001
+ }
102002
+ } catch (err) {
102003
+ console.error("Failed to fetch images:", err);
101743
102004
  } finally {
101744
102005
  stopLoading();
101745
102006
  }
101746
102007
  },
101747
- [startLoading, stopLoading]
102008
+ [startLoading, stopLoading, source]
101748
102009
  );
101749
102010
  React.useEffect(() => {
101750
- fetchImages(category, 1);
101751
- }, []);
102011
+ fetchImages(searchQuery || (source === "vibrant" ? "" : category), 1, source);
102012
+ }, [source]);
101752
102013
  React.useEffect(() => {
101753
102014
  const el = scrollRef.current;
101754
102015
  if (!el) return;
@@ -101757,13 +102018,13 @@ end`);
101757
102018
  if (el.scrollTop + el.clientHeight >= el.scrollHeight - 200) {
101758
102019
  const next = page + 1;
101759
102020
  setPage(next);
101760
- fetchImages(category, next);
102021
+ fetchImages(searchQuery || (source === "vibrant" ? "" : category), next, source);
101761
102022
  }
101762
102023
  };
101763
102024
  el.addEventListener("scroll", onScroll);
101764
102025
  return () => el.removeEventListener("scroll", onScroll);
101765
- }, [hasMore, isLoading]);
101766
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
102026
+ }, [hasMore, isLoading, page, searchQuery, category, source, fetchImages]);
102027
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col h-full min-h-0", children: [
101767
102028
  /* @__PURE__ */ jsxRuntime.jsx("script", { src: "https://apis.google.com/js/api.js" }),
101768
102029
  /* @__PURE__ */ jsxRuntime.jsx(
101769
102030
  "script",
@@ -101783,70 +102044,166 @@ end`);
101783
102044
  });
101784
102045
  }
101785
102046
  } }),
101786
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-neutral-800 px-3 py-2 rounded flex items-center gap-2 mb-4 mx-4", children: /* @__PURE__ */ jsxRuntime.jsx(
101787
- "input",
101788
- {
101789
- className: "bg-transparent outline-none text-sm text-gray-300 w-full",
101790
- onKeyDown: (e2) => {
101791
- e2.stopPropagation();
101792
- if (e2.key === "Enter") {
101793
- fetchImages(e2.target.value, 1);
102047
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "shrink-0 pt-4", children: [
102048
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-neutral-800 px-3 py-2 rounded flex items-center gap-2 mb-3 mx-4", children: /* @__PURE__ */ jsxRuntime.jsx(
102049
+ "input",
102050
+ {
102051
+ className: "bg-transparent outline-none text-sm text-gray-300 w-full",
102052
+ placeholder: source === "vibrant" ? "Search Vibrant Images..." : "Search Stock Photos...",
102053
+ value: searchQuery,
102054
+ onChange: (e2) => setSearchQuery(e2.target.value),
102055
+ onKeyDown: (e2) => {
102056
+ e2.stopPropagation();
102057
+ if (e2.key === "Enter") {
102058
+ setPage(1);
102059
+ fetchImages(searchQuery, 1, source);
102060
+ }
101794
102061
  }
101795
102062
  }
101796
- }
101797
- ) }),
101798
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "m-4", children: [
101799
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-md font-medium mb-3", children: "Upload From Other Media" }),
101800
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-4", children: [
101801
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "cursor-pointer", onClick: () => handleGoogleAuth("googledrives"), children: /* @__PURE__ */ jsxRuntime.jsx(GoogleDriveIcon, {}) }),
101802
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "cursor-pointer", onClick: () => handleGoogleAuth("googlephotos"), children: /* @__PURE__ */ jsxRuntime.jsx(GooglePhotoIcon, {}) }),
101803
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "cursor-pointer", onClick: handleDropboxAuth, children: /* @__PURE__ */ jsxRuntime.jsx(
101804
- "img",
101805
- {
101806
- src: "https://vibrantprintshopimage.s3.amazonaws.com/dropbox.svg",
101807
- alt: "dropbox",
101808
- height: 30,
101809
- width: 30,
101810
- className: "object-contain"
101811
- }
101812
- ) }),
101813
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "cursor-pointer", onClick: handleFacebookAuth, children: /* @__PURE__ */ jsxRuntime.jsx(FacebookIcon, {}) }),
101814
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "cursor-pointer", onClick: handleInstagramAuth, children: /* @__PURE__ */ jsxRuntime.jsx(InstagramIcon, {}) }),
101815
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "cursor-pointer", children: /* @__PURE__ */ jsxRuntime.jsx(
101816
- "img",
102063
+ ) }),
102064
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-4 mb-3", children: [
102065
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-md font-medium mb-2 text-gray-200", children: "Upload From Other Media" }),
102066
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
102067
+ /* @__PURE__ */ jsxRuntime.jsx(
102068
+ "button",
102069
+ {
102070
+ type: "button",
102071
+ className: "cursor-pointer p-1.5 rounded-lg hover:bg-white/10 transition-colors",
102072
+ onClick: () => handleGoogleAuth("googledrives"),
102073
+ title: "Google Drive",
102074
+ children: /* @__PURE__ */ jsxRuntime.jsx(GoogleDriveIcon, {})
102075
+ }
102076
+ ),
102077
+ /* @__PURE__ */ jsxRuntime.jsx(
102078
+ "button",
102079
+ {
102080
+ type: "button",
102081
+ className: "cursor-pointer p-1.5 rounded-lg hover:bg-white/10 transition-colors",
102082
+ onClick: () => handleGoogleAuth("googlephotos"),
102083
+ title: "Google Photos",
102084
+ children: /* @__PURE__ */ jsxRuntime.jsx(GooglePhotoIcon, {})
102085
+ }
102086
+ ),
102087
+ /* @__PURE__ */ jsxRuntime.jsx(
102088
+ "button",
102089
+ {
102090
+ type: "button",
102091
+ className: "cursor-pointer p-1.5 rounded-lg hover:bg-white/10 transition-colors",
102092
+ onClick: handleDropboxAuth,
102093
+ title: "Dropbox",
102094
+ children: /* @__PURE__ */ jsxRuntime.jsx(
102095
+ "img",
102096
+ {
102097
+ src: "https://vibrantprintshopimage.s3.amazonaws.com/dropbox.svg",
102098
+ alt: "dropbox",
102099
+ height: 26,
102100
+ width: 26,
102101
+ className: "object-contain"
102102
+ }
102103
+ )
102104
+ }
102105
+ ),
102106
+ /* @__PURE__ */ jsxRuntime.jsx(
102107
+ "button",
102108
+ {
102109
+ type: "button",
102110
+ className: "cursor-pointer p-1.5 rounded-lg hover:bg-white/10 transition-colors",
102111
+ onClick: handleFacebookAuth,
102112
+ title: "Facebook",
102113
+ children: /* @__PURE__ */ jsxRuntime.jsx(FacebookIcon, {})
102114
+ }
102115
+ ),
102116
+ /* @__PURE__ */ jsxRuntime.jsx(
102117
+ "button",
102118
+ {
102119
+ type: "button",
102120
+ className: "cursor-pointer p-1.5 rounded-lg hover:bg-white/10 transition-colors",
102121
+ onClick: handleInstagramAuth,
102122
+ title: "Instagram",
102123
+ children: /* @__PURE__ */ jsxRuntime.jsx(InstagramIcon, {})
102124
+ }
102125
+ ),
102126
+ /* @__PURE__ */ jsxRuntime.jsx(
102127
+ "button",
102128
+ {
102129
+ type: "button",
102130
+ onClick: () => {
102131
+ setSource("vibrant");
102132
+ setPage(1);
102133
+ setSearchQuery("");
102134
+ fetchImages("", 1, "vibrant");
102135
+ },
102136
+ title: "Vibrant Stock Images",
102137
+ className: "cursor-pointer",
102138
+ children: /* @__PURE__ */ jsxRuntime.jsx(
102139
+ "img",
102140
+ {
102141
+ src: VibrantLogo,
102142
+ alt: "vibrant",
102143
+ height: 64,
102144
+ width: 64,
102145
+ className: "object-contain"
102146
+ }
102147
+ )
102148
+ }
102149
+ )
102150
+ ] })
102151
+ ] }),
102152
+ /* @__PURE__ */ jsxRuntime.jsx("hr", { className: "text-neutral-800 h-[0.5px] my-3" }),
102153
+ source === "external" ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mx-4 mb-3", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex gap-2 overflow-auto scrollbar-none custom-scrollbar", children: imageOptions.map((item) => /* @__PURE__ */ jsxRuntime.jsx(
102154
+ "button",
102155
+ {
102156
+ className: `px-2 py-1 rounded-sm shrink-0 text-xs ${item === category ? "bg-[#02bc71] text-white" : "bg-[#8f99a826]"}`,
102157
+ onClick: () => {
102158
+ setCategory(item);
102159
+ setSocialImages([]);
102160
+ setPage(1);
102161
+ fetchImages(item.toLowerCase(), 1, "external");
102162
+ },
102163
+ children: item
102164
+ },
102165
+ item
102166
+ )) }) }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-4 mb-3 flex items-center justify-between", children: [
102167
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs font-semibold text-[#02bc71] flex items-center gap-1.5", children: [
102168
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block w-2 h-2 rounded-full bg-[#02bc71]" }),
102169
+ "Vibrant Cloud Images"
102170
+ ] }),
102171
+ /* @__PURE__ */ jsxRuntime.jsx(
102172
+ "button",
101817
102173
  {
101818
- src: VibrantLogo,
101819
- alt: "vibrant",
101820
- height: 60,
101821
- width: 60,
101822
- className: "object-contain"
102174
+ type: "button",
102175
+ onClick: () => {
102176
+ setSource("external");
102177
+ setPage(1);
102178
+ setSearchQuery("");
102179
+ fetchImages(category.toLowerCase(), 1, "external");
102180
+ },
102181
+ className: "text-[11px] text-gray-400 hover:text-white cursor-pointer underline",
102182
+ children: "Explore Stock Categories"
101823
102183
  }
101824
- ) })
102184
+ )
101825
102185
  ] })
101826
102186
  ] }),
101827
- /* @__PURE__ */ jsxRuntime.jsx("hr", { className: "text-neutral-800 h-[0.5px] my-4" }),
101828
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "m-4", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex gap-2 overflow-auto", children: imageOptions.map((item) => /* @__PURE__ */ jsxRuntime.jsx(
101829
- "button",
101830
- {
101831
- className: `px-2 py-1 rounded-sm shrink-0 ${item === category ? "bg-[#02bc71] text-white" : "bg-[#8f99a826]"}`,
101832
- onClick: () => {
101833
- setCategory(item);
101834
- setSocialImages([]);
101835
- fetchImages(item.toLowerCase(), 1);
101836
- },
101837
- children: item
101838
- },
101839
- item
101840
- )) }) }),
101841
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[calc(100vh-288px)] overflow-auto", ref: scrollRef, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-4 md:grid-cols-3 gap-3 mx-4 pb-5", children: socialImages.length > 0 && socialImages.map((item, i2) => {
102187
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-h-0 overflow-y-auto pb-8 custom-scrollbar", ref: scrollRef, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-4 md:grid-cols-3 gap-3 mx-4 pb-5", children: socialImages.length > 0 ? socialImages.map((item, i2) => {
101842
102188
  return /* @__PURE__ */ jsxRuntime.jsx(
101843
102189
  "button",
101844
102190
  {
101845
- className: "relative aspect-square rounded overflow-hidden group cursor-pointer",
101846
- onClick: () => {
101847
- addImageToCanvas(item.largeImage);
101848
- if (window.innerWidth < 768) {
101849
- useSidePanelStore.getState().setActivePanel(null);
102191
+ className: "relative aspect-square rounded overflow-hidden group cursor-pointer bg-neutral-900 transition-all",
102192
+ onClick: async () => {
102193
+ try {
102194
+ startLoading();
102195
+ const targetUrl = item.largeImage || item.image;
102196
+ if (!targetUrl) return;
102197
+ const image = await ShapeService.convertImages(targetUrl);
102198
+ const imageUrl = image?.success ? image?.url : targetUrl;
102199
+ if (imageUrl) addImageToCanvas(imageUrl);
102200
+ if (window.innerWidth < 768) {
102201
+ useSidePanelStore.getState().setActivePanel(null);
102202
+ }
102203
+ } catch (error2) {
102204
+ console.error("Failed to convert image:", error2);
102205
+ } finally {
102206
+ stopLoading();
101850
102207
  }
101851
102208
  },
101852
102209
  children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -101855,14 +102212,14 @@ end`);
101855
102212
  src: item.image,
101856
102213
  height: 300,
101857
102214
  width: 300,
101858
- alt: "stock",
102215
+ alt: item.tags?.[0] || "stock",
101859
102216
  className: "object-cover w-full h-full group-hover:scale-105 transition-transform"
101860
102217
  }
101861
102218
  )
101862
102219
  },
101863
102220
  i2
101864
102221
  );
101865
- }) }) })
102222
+ }) : !isLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-full py-8 text-center text-xs text-gray-500", children: "No images found" }) : null }) })
101866
102223
  ] });
101867
102224
  }
101868
102225
  async function addBackgroundImage(url) {
@@ -101877,7 +102234,7 @@ end`);
101877
102234
  const bounds = workarea.getBoundingRect();
101878
102235
  const centerX = bounds.left + bounds.width / 2;
101879
102236
  const centerY = bounds.top + bounds.height / 2;
101880
- oa.fromURL(url, { crossOrigin: "anonymous" }).then((img) => {
102237
+ oa.fromURL(url + "?t=vb", { crossOrigin: "anonymous" }).then((img) => {
101881
102238
  img.set({
101882
102239
  left: centerX,
101883
102240
  top: centerY,
@@ -101913,319 +102270,288 @@ end`);
101913
102270
  workarea.set("fill", color);
101914
102271
  canvas2.requestRenderAll();
101915
102272
  }
102273
+ function normalizeHexColor(c2) {
102274
+ if (!c2 || typeof c2 !== "string" || c2 === "none" || c2 === "transparent" || c2.trim() === "") return null;
102275
+ const trimmed = c2.trim();
102276
+ try {
102277
+ const hex = new Ie(trimmed).toHex();
102278
+ if (hex && hex.length === 6) {
102279
+ return `#${hex.toLowerCase()}`;
102280
+ }
102281
+ } catch {
102282
+ }
102283
+ if (trimmed.startsWith("#")) {
102284
+ let raw = trimmed.replace(/^#/, "");
102285
+ if (raw.length === 3) raw = raw[0] + raw[0] + raw[1] + raw[1] + raw[2] + raw[2];
102286
+ if (raw.length === 6) return `#${raw.toLowerCase()}`;
102287
+ }
102288
+ return null;
102289
+ }
102290
+ function extractAllTargetColors(targets) {
102291
+ if (!targets || !targets.length) return [];
102292
+ const colors = [];
102293
+ const seen = /* @__PURE__ */ new Set();
102294
+ const addColor = (raw) => {
102295
+ const norm = normalizeHexColor(raw);
102296
+ if (norm && !seen.has(norm)) {
102297
+ seen.add(norm);
102298
+ colors.push(norm);
102299
+ }
102300
+ };
102301
+ const traverse = (obj) => {
102302
+ if (!obj || obj.name === "workarea") return;
102303
+ if (obj.type === "image" && obj.name !== "qr" && obj.name !== "barcode" && !obj._objects) {
102304
+ return;
102305
+ }
102306
+ if (obj.type === "textbox" || obj.type === "i-text" || obj.type === "text") {
102307
+ const fill22 = obj.get?.("fill") ?? obj.fill;
102308
+ if (typeof fill22 === "string" && fill22 !== "none" && fill22 !== "transparent" && fill22 !== "") {
102309
+ addColor(fill22);
102310
+ }
102311
+ return;
102312
+ }
102313
+ if (obj.type === "group" && obj.name === "qr") {
102314
+ if (obj.__qrDark) addColor(obj.__qrDark);
102315
+ if (obj.__qrLight) addColor(obj.__qrLight);
102316
+ return;
102317
+ }
102318
+ if (obj.type === "group" && obj.name === "barcode") {
102319
+ if (obj.__barcodeForeground) addColor(obj.__barcodeForeground);
102320
+ if (obj.__barcodeBackground) addColor(obj.__barcodeBackground);
102321
+ return;
102322
+ }
102323
+ if (obj.type === "group" || obj._objects) {
102324
+ const children = obj.getObjects ? obj.getObjects() : obj._objects || [];
102325
+ children.forEach(traverse);
102326
+ return;
102327
+ }
102328
+ const fill2 = obj.get?.("fill") ?? obj.fill;
102329
+ if (typeof fill2 === "string" && fill2 !== "none" && fill2 !== "transparent" && fill2 !== "") {
102330
+ addColor(fill2);
102331
+ }
102332
+ const stroke2 = obj.get?.("stroke") ?? obj.stroke;
102333
+ const strokeWidth = obj.get?.("strokeWidth") ?? obj.strokeWidth;
102334
+ if (typeof stroke2 === "string" && stroke2 !== "none" && stroke2 !== "transparent" && stroke2 !== "" && (typeof strokeWidth !== "number" || strokeWidth > 0)) {
102335
+ addColor(stroke2);
102336
+ }
102337
+ };
102338
+ targets.forEach(traverse);
102339
+ return colors;
102340
+ }
101916
102341
  function extractTargetColor(targets) {
101917
102342
  if (!targets.length) return {};
102343
+ const allColors = extractAllTargetColors(targets);
102344
+ const primaryColor = allColors[0] || void 0;
101918
102345
  const obj = targets[0];
101919
- let color;
101920
- let cmyk2 = obj.__cmyk;
101921
- if (obj.type === "group") {
101922
- if (obj.name === "qr" && obj.__qrDark) {
101923
- color = obj.__qrDark;
101924
- if (obj.__qrDarkCmyk) cmyk2 = obj.__qrDarkCmyk;
101925
- } else if (obj.name === "barcode" && obj.__barcodeForeground) {
101926
- color = obj.__barcodeForeground;
101927
- if (obj.__barcodeFgCmyk) cmyk2 = obj.__barcodeFgCmyk;
101928
- } else {
101929
- const colors = extractSVGColors(obj);
101930
- if (colors.length > 0) color = colors[0];
101931
- }
101932
- } else {
101933
- const fill2 = obj.get?.("fill") ?? obj.fill;
101934
- if (typeof fill2 === "string") color = fill2;
102346
+ let cmyk2 = obj?.__cmyk;
102347
+ if (primaryColor && !cmyk2) {
102348
+ cmyk2 = hexToCmyk(primaryColor);
101935
102349
  }
101936
- if (cmyk2 && !color) {
101937
- color = cmykToHex(cmyk2.c, cmyk2.m, cmyk2.y, cmyk2.k);
101938
- } else if (color && !cmyk2) {
101939
- cmyk2 = hexToCmyk(color);
101940
- }
101941
- const opacity = typeof obj.opacity === "number" ? Math.round(obj.opacity * 100) : void 0;
101942
- return { color, opacity, cmyk: cmyk2 };
102350
+ const opacity = typeof obj?.opacity === "number" ? Math.round(obj.opacity * 100) : void 0;
102351
+ return { color: primaryColor, opacity, cmyk: cmyk2 };
101943
102352
  }
101944
- function applyFillToFabricObjects(objects, newColor, alpha, currentColor, canvas2, cmykValues) {
102353
+ function applyFillToFabricObjects(objects, newColor, alpha, targetOldColor, canvas2, cmykValues) {
101945
102354
  if (!canvas2 || !objects.length) return;
101946
- const resolvedCmyk = cmykValues || hexToCmyk(newColor);
102355
+ const targetNewHex = normalizeHexColor(newColor) || newColor.toLowerCase();
102356
+ const targetOldHex = targetOldColor ? normalizeHexColor(targetOldColor) : null;
102357
+ const resolvedCmyk = cmykValues || hexToCmyk(targetNewHex);
101947
102358
  objects.forEach((obj) => {
101948
- obj.set("opacity", alpha / 100);
101949
- obj.set("__cmyk", { ...resolvedCmyk });
101950
- if (obj.type === "textbox" || obj.type === "i-text") {
101951
- obj.set("fill", newColor);
101952
- } else if (obj.type === "group" && obj.name === "qr") {
101953
- updateQRObjectColors(obj, newColor, obj.__qrLight || "#ffffff");
101954
- } else if (obj.type === "group" && obj.name === "barcode") {
101955
- updateBarcodeObjectColors(obj, newColor, obj.__barcodeBackground || "#ffffff");
101956
- } else if (obj.type === "group") {
101957
- const applyColorRecursive = (child) => {
101958
- child.set("fill", newColor);
101959
- if (child.type === "group" && child._objects) {
101960
- child._objects.forEach((grandchild) => applyColorRecursive(grandchild));
102359
+ if (typeof alpha === "number") {
102360
+ obj.set("opacity", alpha / 100);
102361
+ }
102362
+ if (obj.type === "group" && obj.name === "qr") {
102363
+ const dark = normalizeHexColor(obj.__qrDark) || "#000000";
102364
+ const light = normalizeHexColor(obj.__qrLight) || "#ffffff";
102365
+ let newDark = dark;
102366
+ let newLight = light;
102367
+ if (targetOldHex) {
102368
+ if (dark === targetOldHex) newDark = targetNewHex;
102369
+ if (light === targetOldHex) newLight = targetNewHex;
102370
+ } else {
102371
+ newDark = targetNewHex;
102372
+ }
102373
+ updateQRObjectColors(obj, newDark, newLight);
102374
+ return;
102375
+ }
102376
+ if (obj.type === "group" && obj.name === "barcode") {
102377
+ const fg = normalizeHexColor(obj.__barcodeForeground) || "#000000";
102378
+ const bg = normalizeHexColor(obj.__barcodeBackground) || "#ffffff";
102379
+ let newFg = fg;
102380
+ let newBg = bg;
102381
+ if (targetOldHex) {
102382
+ if (fg === targetOldHex) newFg = targetNewHex;
102383
+ if (bg === targetOldHex) newBg = targetNewHex;
102384
+ } else {
102385
+ newFg = targetNewHex;
102386
+ }
102387
+ updateBarcodeObjectColors(obj, newFg, newBg);
102388
+ return;
102389
+ }
102390
+ if (obj.type === "textbox" || obj.type === "i-text" || obj.type === "text") {
102391
+ obj.set("fill", targetNewHex);
102392
+ obj.set("__cmyk", { ...resolvedCmyk });
102393
+ obj.setCoords();
102394
+ return;
102395
+ }
102396
+ if (obj.type === "group" || obj._objects) {
102397
+ const replaceRecursive = (child) => {
102398
+ if (!child) return;
102399
+ if (typeof child.fill === "string") {
102400
+ const childFillHex = normalizeHexColor(child.fill);
102401
+ if (!targetOldHex || childFillHex === targetOldHex) {
102402
+ child.set("fill", targetNewHex);
102403
+ child.__cmyk = { ...resolvedCmyk };
102404
+ }
102405
+ }
102406
+ if (typeof child.stroke === "string") {
102407
+ const childStrokeHex = normalizeHexColor(child.stroke);
102408
+ if (targetOldHex && childStrokeHex === targetOldHex) {
102409
+ child.set("stroke", targetNewHex);
102410
+ child.__cmyk = { ...resolvedCmyk };
102411
+ }
102412
+ }
102413
+ if (child.type === "group" || child._objects) {
102414
+ const sub = child.getObjects ? child.getObjects() : child._objects || [];
102415
+ sub.forEach(replaceRecursive);
101961
102416
  }
101962
102417
  };
101963
- if (obj._objects) {
101964
- obj._objects.forEach((child) => applyColorRecursive(child));
101965
- }
102418
+ const children = obj.getObjects ? obj.getObjects() : obj._objects || [];
102419
+ children.forEach(replaceRecursive);
101966
102420
  } else {
101967
- obj.set("fill", newColor);
102421
+ const currentFillHex = normalizeHexColor(obj.fill);
102422
+ if (!targetOldHex || currentFillHex === targetOldHex) {
102423
+ obj.set("fill", targetNewHex);
102424
+ obj.set("__cmyk", { ...resolvedCmyk });
102425
+ }
102426
+ const currentStrokeHex = normalizeHexColor(obj.stroke);
102427
+ if (targetOldHex && currentStrokeHex === targetOldHex) {
102428
+ obj.set("stroke", targetNewHex);
102429
+ obj.set("__cmyk", { ...resolvedCmyk });
102430
+ }
101968
102431
  }
101969
102432
  obj.setCoords();
101970
102433
  });
101971
102434
  canvas2.requestRenderAll();
101972
102435
  }
101973
- const useDraggable = (onDrag, onEnd) => {
101974
- return (e2) => {
101975
- const startX = e2.clientX;
101976
- const startY = e2.clientY;
101977
- onDrag(startX, startY);
101978
- const handleMove = (e22) => onDrag(e22.clientX, e22.clientY);
101979
- const handleUp = () => {
101980
- document.removeEventListener("mousemove", handleMove);
101981
- document.removeEventListener("mouseup", handleUp);
101982
- onEnd?.();
101983
- };
101984
- document.addEventListener("mousemove", handleMove);
101985
- document.addEventListener("mouseup", handleUp);
101986
- };
101987
- };
101988
- function GradientSelector({
101989
- hsv,
101990
- hueColor,
101991
- refEl,
101992
- onChange
101993
- }) {
102436
+ const STEP$1 = 1;
102437
+ const STEP_LARGE = 10;
102438
+ function ChannelSlider({ value, min, max, gradient, ariaLabel, onChange, onCommit, className, thumbClassName }) {
102439
+ const trackRef = React.useRef(null);
102440
+ function pick(event) {
102441
+ const element = trackRef.current;
102442
+ if (!element) return;
102443
+ const bounds = element.getBoundingClientRect();
102444
+ const positionRatio = Math.max(0, Math.min(1, (event.clientX - bounds.left) / bounds.width));
102445
+ onChange(Math.round(min + positionRatio * (max - min)));
102446
+ }
102447
+ function nudge(event) {
102448
+ let next = value;
102449
+ switch (event.key) {
102450
+ case "ArrowRight":
102451
+ case "ArrowUp":
102452
+ next += event.shiftKey ? STEP_LARGE : STEP$1;
102453
+ break;
102454
+ case "ArrowLeft":
102455
+ case "ArrowDown":
102456
+ next -= event.shiftKey ? STEP_LARGE : STEP$1;
102457
+ break;
102458
+ case "PageUp":
102459
+ next += STEP_LARGE;
102460
+ break;
102461
+ case "PageDown":
102462
+ next -= STEP_LARGE;
102463
+ break;
102464
+ case "Home":
102465
+ next = min;
102466
+ break;
102467
+ case "End":
102468
+ next = max;
102469
+ break;
102470
+ default:
102471
+ return;
102472
+ }
102473
+ event.preventDefault();
102474
+ next = Math.max(min, Math.min(max, next));
102475
+ if (next !== value) onChange(next);
102476
+ }
102477
+ const percent = (value - min) / (max - min) * 100;
101994
102478
  return /* @__PURE__ */ jsxRuntime.jsx(
101995
102479
  "div",
101996
102480
  {
101997
- ref: refEl,
101998
- className: "w-full h-48 mb-3 rounded cursor-crosshair relative",
101999
- style: {
102000
- background: `linear-gradient(to bottom, transparent, black), linear-gradient(to right, white, ${hueColor})`
102481
+ ref: trackRef,
102482
+ className: className ? `pqc-slider ${className}` : "pqc-slider",
102483
+ style: { ["--pqc-_grad"]: gradient },
102484
+ role: "slider",
102485
+ tabIndex: 0,
102486
+ "aria-label": ariaLabel,
102487
+ "aria-valuemin": min,
102488
+ "aria-valuemax": max,
102489
+ "aria-valuenow": value,
102490
+ onKeyDown: nudge,
102491
+ onPointerDown: (event) => {
102492
+ event.currentTarget.setPointerCapture(event.pointerId);
102493
+ pick(event);
102001
102494
  },
102002
- onMouseDown: useDraggable((x2, y2) => {
102003
- if (!refEl.current) return;
102004
- const rect = refEl.current.getBoundingClientRect();
102005
- const s2 = Math.max(0, Math.min(x2 - rect.left, rect.width)) / rect.width * 100;
102006
- const v2 = 100 - Math.max(0, Math.min(y2 - rect.top, rect.height)) / rect.height * 100;
102007
- onChange({ ...hsv, s: s2, v: v2 });
102008
- }),
102009
- children: /* @__PURE__ */ jsxRuntime.jsx(
102010
- "div",
102011
- {
102012
- className: "absolute w-4 h-4 border-2 border-white rounded-full shadow-lg pointer-events-none",
102013
- style: {
102014
- left: `${hsv.s}%`,
102015
- top: `${100 - hsv.v}%`,
102016
- transform: "translate(-50%, -50%)"
102017
- }
102018
- }
102019
- )
102020
- }
102021
- );
102022
- }
102023
- function HueSlider({ hsv, refEl, onChange }) {
102024
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-2", children: /* @__PURE__ */ jsxRuntime.jsx(
102025
- "div",
102026
- {
102027
- ref: refEl,
102028
- className: "w-full h-4 rounded-full cursor-pointer relative",
102029
- style: {
102030
- background: "linear-gradient(to right, #ff0000, #ffff00, #00ff00, #00ffff, #0000ff, #ff00ff, #ff0000)"
102495
+ onPointerMove: (event) => {
102496
+ if (event.buttons === 0) return;
102497
+ pick(event);
102031
102498
  },
102032
- onMouseDown: useDraggable((x2) => {
102033
- if (!refEl.current) return;
102034
- const rect = refEl.current.getBoundingClientRect();
102035
- const h2 = Math.max(0, Math.min(x2 - rect.left, rect.width)) / rect.width * 360;
102036
- onChange({ ...hsv, h: h2 });
102037
- }),
102038
- children: /* @__PURE__ */ jsxRuntime.jsx(
102039
- "div",
102040
- {
102041
- className: "absolute top-1/2 w-5 h-5 bg-white border-2 border-gray-400 rounded-full shadow-lg pointer-events-none",
102042
- style: {
102043
- left: `${hsv.h / 360 * 100}%`,
102044
- transform: "translate(-50%, -50%)"
102045
- }
102046
- }
102047
- )
102048
- }
102049
- ) });
102050
- }
102051
- function AlphaSlider({
102052
- alpha,
102053
- color,
102054
- refEl,
102055
- setAlpha,
102056
- canvas: canvas2,
102057
- targets,
102058
- saveState
102059
- }) {
102060
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsxRuntime.jsxs(
102061
- "div",
102062
- {
102063
- ref: refEl,
102064
- className: "w-full h-4 rounded-full cursor-pointer relative overflow-hidden",
102065
- onMouseDown: useDraggable(
102066
- (x2) => {
102067
- if (!refEl.current) return;
102068
- const rect = refEl.current.getBoundingClientRect();
102069
- const newAlpha = Math.round(
102070
- Math.max(0, Math.min(x2 - rect.left, rect.width)) / rect.width * 100
102071
- );
102072
- setAlpha(newAlpha);
102073
- if (canvas2 && targets.length) {
102074
- targets.forEach((obj) => {
102075
- obj.set("opacity", newAlpha / 100);
102076
- obj.setCoords();
102077
- });
102078
- canvas2.requestRenderAll();
102079
- }
102080
- },
102081
- saveState
102082
- ),
102083
- children: [
102084
- /* @__PURE__ */ jsxRuntime.jsx(
102085
- "div",
102086
- {
102087
- className: "absolute inset-0",
102088
- style: {
102089
- backgroundImage: `
102090
- linear-gradient(45deg, #ccc 25%, transparent 25%),
102091
- linear-gradient(-45deg, #ccc 25%, transparent 25%),
102092
- linear-gradient(45deg, transparent 75%, #ccc 75%),
102093
- linear-gradient(-45deg, transparent 75%, #ccc 75%)
102094
- `,
102095
- backgroundSize: "8px 8px",
102096
- backgroundPosition: "0 0, 0 4px, 4px -4px, -4px 0px"
102097
- }
102098
- }
102099
- ),
102100
- /* @__PURE__ */ jsxRuntime.jsx(
102101
- "div",
102102
- {
102103
- className: "absolute inset-0",
102104
- style: {
102105
- background: `linear-gradient(to right, transparent, ${color})`
102106
- }
102107
- }
102108
- ),
102109
- /* @__PURE__ */ jsxRuntime.jsx(
102110
- "div",
102111
- {
102112
- className: "absolute top-1/2 w-5 h-5 bg-white border-2 border-gray-400 rounded-full shadow-lg pointer-events-none",
102113
- style: {
102114
- left: `${alpha}%`,
102115
- transform: "translate(-50%, -50%)"
102116
- }
102117
- }
102118
- )
102119
- ]
102499
+ onPointerUp: () => onCommit?.(),
102500
+ onBlur: () => onCommit?.(),
102501
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: thumbClassName ? `pqc-slider-thumb ${thumbClassName}` : "pqc-slider-thumb", style: { ["--pqc-_x"]: `${percent}%` } })
102120
102502
  }
102121
- ) });
102122
- }
102123
- function CMYKInputs({
102124
- cmyk: cmyk2,
102125
- alpha,
102126
- setAlpha,
102127
- updateColor,
102128
- canvas: canvas2,
102129
- targets,
102130
- saveState
102131
- }) {
102132
- const inputClass = "w-full px-1.5 py-1.5 text-xs border border-gray-300 rounded text-center focus:outline-none focus:ring-1 focus:ring-cyan-500 focus:border-cyan-500";
102133
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-3", children: [
102134
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 mb-2", children: [
102135
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-bold tracking-wider text-gray-500 uppercase", children: "CMYK" }),
102136
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] text-gray-400", children: "Print Values (0–100%)" })
102137
- ] }),
102138
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-5 gap-1.5", children: [
102139
- ["c", "m", "y", "k"].map((key) => /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
102140
- /* @__PURE__ */ jsxRuntime.jsx(
102141
- "input",
102142
- {
102143
- type: "number",
102144
- min: 0,
102145
- max: 100,
102146
- value: cmyk2[key],
102147
- onChange: (e2) => {
102148
- const val = Math.max(0, Math.min(100, +e2.target.value || 0));
102149
- updateColor({ ...cmyk2, [key]: val });
102150
- },
102151
- className: inputClass
102152
- }
102153
- ),
102154
- /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-[10px] text-gray-500 block mt-0.5 text-center font-semibold", children: key.toUpperCase() })
102155
- ] }, key)),
102156
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
102157
- /* @__PURE__ */ jsxRuntime.jsx(
102158
- "input",
102159
- {
102160
- type: "number",
102161
- min: 0,
102162
- max: 100,
102163
- value: alpha,
102164
- onChange: (e2) => {
102165
- const newAlpha = Math.max(0, Math.min(100, +e2.target.value));
102166
- setAlpha(newAlpha);
102167
- if (canvas2 && targets.length) {
102168
- targets.forEach((obj) => {
102169
- obj.set("opacity", newAlpha / 100);
102170
- obj.setCoords();
102171
- });
102172
- canvas2.requestRenderAll();
102173
- saveState();
102174
- }
102175
- },
102176
- className: inputClass
102177
- }
102178
- ),
102179
- /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-[10px] text-gray-500 block mt-0.5 text-center font-semibold", children: "A" })
102180
- ] })
102181
- ] })
102182
- ] });
102503
+ );
102183
102504
  }
102184
- function ColorPreviewBox({ color, alpha }) {
102185
- const outOfGamut = isOutOfCmykGamut(color);
102186
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-start gap-3 mb-3", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-12 h-12 rounded border border-gray-300 flex-shrink-0", children: [
102187
- /* @__PURE__ */ jsxRuntime.jsx(
102188
- "div",
102189
- {
102190
- className: "absolute inset-0 rounded",
102191
- style: {
102192
- backgroundImage: `
102193
- linear-gradient(45deg, #ccc 25%, transparent 25%),
102194
- linear-gradient(-45deg, #ccc 25%, transparent 25%),
102195
- linear-gradient(45deg, transparent 75%, #ccc 75%),
102196
- linear-gradient(-45deg, transparent 75%, #ccc 75%)
102197
- `,
102198
- backgroundSize: "8px 8px",
102199
- backgroundPosition: "0 0, 0 4px, 4px -4px, -4px 0px"
102200
- }
102201
- }
102202
- ),
102505
+ function ChannelRow({ label, labelColor, ariaLabel, value, min, max, gradient, onChange, onCommit, sliderClassName, sliderThumbClassName }) {
102506
+ const [rawInput, setRawInput] = React.useState(String(value));
102507
+ React.useEffect(() => {
102508
+ setRawInput(String(value));
102509
+ }, [value]);
102510
+ function commit(rawValue) {
102511
+ const parsedValue = parseInt(rawValue, 10);
102512
+ if (!isNaN(parsedValue)) onChange(Math.max(min, Math.min(max, parsedValue)));
102513
+ setRawInput(String(value));
102514
+ onCommit?.("input");
102515
+ }
102516
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pqc-channel-row", children: [
102517
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pqc-channel-label", style: { color: labelColor }, "aria-hidden": "true", children: label }),
102518
+ /* @__PURE__ */ jsxRuntime.jsx(ChannelSlider, { value, min, max, gradient, ariaLabel, onChange, onCommit: () => onCommit?.("slider"), className: sliderClassName, thumbClassName: sliderThumbClassName }),
102203
102519
  /* @__PURE__ */ jsxRuntime.jsx(
102204
- "div",
102205
- {
102206
- className: "absolute inset-0 rounded",
102207
- style: {
102208
- background: color,
102209
- opacity: alpha / 100
102210
- }
102211
- }
102212
- ),
102213
- outOfGamut && /* @__PURE__ */ jsxRuntime.jsx(
102214
- "div",
102520
+ "input",
102215
102521
  {
102216
- className: "absolute -top-1.5 -right-1.5 w-4 h-4 bg-amber-500 rounded-full flex items-center justify-center text-white text-[8px] font-bold shadow-sm cursor-help",
102217
- title: "This color is outside the CMYK gamut. It may look different when printed.",
102218
- children: "!"
102522
+ type: "text",
102523
+ inputMode: "numeric",
102524
+ "aria-label": ariaLabel,
102525
+ value: rawInput,
102526
+ onChange: (event) => {
102527
+ setRawInput(event.target.value);
102528
+ },
102529
+ onBlur: (event) => commit(event.target.value),
102530
+ onKeyDown: (event) => {
102531
+ if (event.key === "Enter") commit(event.target.value);
102532
+ },
102533
+ className: "pqc-channel-input"
102219
102534
  }
102220
102535
  )
102221
- ] }) });
102536
+ ] });
102222
102537
  }
102538
+ const DEFAULT_COLORS = ["#f97316", "#ef4444", "#22c55e", "#3b82f6", "#a855f7", "#ec4899", "#000000", "#ffffff"];
102539
+ const CMYK_CHANNELS$1 = [
102540
+ { key: "c", label: "C", color: "#009fe3", name: "Cyan" },
102541
+ { key: "m", label: "M", color: "#e6007e", name: "Magenta" },
102542
+ { key: "y", label: "Y", color: "#dcb000", name: "Yellow" },
102543
+ { key: "k", label: "K", color: "#222222", name: "Black" }
102544
+ ];
102223
102545
  function CMYKColorPicker({ value, onChange, className = "" } = {}) {
102224
102546
  const canvas2 = useEditorStore((s2) => s2.canvas);
102225
102547
  const saveState = useHistoryStore((s2) => s2.saveState);
102226
- const { objects } = useSelectionStore();
102227
- const targets = objects.filter((o2) => o2.name !== "workarea");
102548
+ const { objects, activeColor, activeColorIndex, setActiveColor } = useSelectionStore();
102549
+ const targets = React.useMemo(() => objects.filter((o2) => o2.name !== "workarea"), [objects]);
102228
102550
  const isControlled = typeof onChange === "function";
102551
+ const allTargetColors = React.useMemo(() => {
102552
+ if (isControlled) return [];
102553
+ return extractAllTargetColors(targets);
102554
+ }, [isControlled, targets, objects]);
102229
102555
  const initialCmyk = typeof value === "object" && value !== null ? value : typeof value === "string" ? hexToCmyk(value) : { c: 0, m: 100, y: 100, k: 0 };
102230
102556
  const initialHex = typeof value === "string" ? value : cmykToHex(initialCmyk.c, initialCmyk.m, initialCmyk.y, initialCmyk.k);
102231
102557
  const [color, setColor] = React.useState(initialHex);
@@ -102235,17 +102561,32 @@ end`);
102235
102561
  return rgbToHsv(rgb2.r, rgb2.g, rgb2.b);
102236
102562
  });
102237
102563
  const [alpha, setAlpha] = React.useState(100);
102564
+ const [mode2, setMode] = React.useState("HEX");
102238
102565
  const [open2, setOpen] = React.useState(false);
102566
+ const [copied, setCopied] = React.useState(false);
102567
+ const [recentColors, setRecentColors] = React.useState([
102568
+ "#e11d48",
102569
+ "#9f1239",
102570
+ "#881337",
102571
+ "#a855f7",
102572
+ "#c084fc",
102573
+ "#475569",
102574
+ "#334155",
102575
+ "#22c55e",
102576
+ "#16a34a",
102577
+ "#6366f1",
102578
+ "#d97706"
102579
+ ]);
102239
102580
  const [coords, setCoords] = React.useState({ top: 0, left: 0 });
102240
102581
  const containerRef = React.useRef(null);
102241
102582
  const popoverRef = React.useRef(null);
102242
- const gradientRef = React.useRef(null);
102583
+ const svRef = React.useRef(null);
102243
102584
  const hueRef = React.useRef(null);
102244
102585
  const alphaRef = React.useRef(null);
102245
102586
  const updatePosition = React.useCallback(() => {
102246
102587
  if (!containerRef.current) return;
102247
102588
  const rect = containerRef.current.getBoundingClientRect();
102248
- const pickerW = 300, pickerH = 440;
102589
+ const pickerW = 270, pickerH = 430;
102249
102590
  let left = Math.max(10, Math.min(rect.left, window.innerWidth - pickerW - 10));
102250
102591
  let top = rect.bottom + 6;
102251
102592
  if (top + pickerH > window.innerHeight - 10) {
@@ -102256,29 +102597,23 @@ end`);
102256
102597
  React.useEffect(() => {
102257
102598
  if (!open2) return;
102258
102599
  updatePosition();
102600
+ const close = (e2) => {
102601
+ const t2 = e2.target;
102602
+ if (containerRef.current?.contains(t2) || popoverRef.current?.contains(t2)) return;
102603
+ setOpen(false);
102604
+ };
102259
102605
  window.addEventListener("resize", updatePosition);
102260
- window.addEventListener("scroll", updatePosition, true);
102606
+ document.addEventListener("mousedown", close, { capture: true });
102261
102607
  return () => {
102262
102608
  window.removeEventListener("resize", updatePosition);
102263
- window.removeEventListener("scroll", updatePosition, true);
102609
+ document.removeEventListener("mousedown", close, { capture: true });
102264
102610
  };
102265
102611
  }, [open2, updatePosition]);
102266
- React.useEffect(() => {
102267
- if (!open2) return;
102268
- const handleClickOutside = (e2) => {
102269
- const t2 = e2.target;
102270
- if (containerRef.current?.contains(t2) || popoverRef.current?.contains(t2)) return;
102271
- setOpen(false);
102272
- };
102273
- document.addEventListener("mousedown", handleClickOutside, { capture: true });
102274
- return () => document.removeEventListener("mousedown", handleClickOutside, { capture: true });
102275
- }, [open2]);
102276
102612
  React.useEffect(() => {
102277
102613
  if (isControlled && value) {
102278
102614
  if (typeof value === "object") {
102279
102615
  const rgb2 = cmykToRgb(value.c, value.m, value.y, value.k);
102280
- const hex = rgbToHex(rgb2.r, rgb2.g, rgb2.b);
102281
- setColor(hex);
102616
+ setColor(rgbToHex(rgb2.r, rgb2.g, rgb2.b));
102282
102617
  setCmyk(value);
102283
102618
  setHsv(rgbToHsv(rgb2.r, rgb2.g, rgb2.b));
102284
102619
  } else if (typeof value === "string") {
@@ -102295,35 +102630,32 @@ end`);
102295
102630
  setOpen(false);
102296
102631
  return;
102297
102632
  }
102298
- const { color: targetColor, opacity: targetOpacity, cmyk: targetCmyk } = extractTargetColor(targets);
102299
- if (targetCmyk) {
102300
- setCmyk(targetCmyk);
102301
- const rgb2 = cmykToRgb(targetCmyk.c, targetCmyk.m, targetCmyk.y, targetCmyk.k);
102302
- const hex = rgbToHex(rgb2.r, rgb2.g, rgb2.b);
102303
- setColor(hex);
102633
+ const { color: tColor, opacity: tOpacity, cmyk: tCmyk } = extractTargetColor(targets);
102634
+ if (tCmyk) {
102635
+ setCmyk(tCmyk);
102636
+ const rgb2 = cmykToRgb(tCmyk.c, tCmyk.m, tCmyk.y, tCmyk.k);
102637
+ setColor(rgbToHex(rgb2.r, rgb2.g, rgb2.b));
102304
102638
  setHsv(rgbToHsv(rgb2.r, rgb2.g, rgb2.b));
102305
- } else if (targetColor && targetColor !== color) {
102306
- const rgb2 = hexToRgb(targetColor);
102307
- setColor(targetColor);
102639
+ } else if (tColor && tColor !== color) {
102640
+ const rgb2 = hexToRgb(tColor);
102641
+ setColor(tColor);
102308
102642
  setHsv(rgbToHsv(rgb2.r, rgb2.g, rgb2.b));
102309
102643
  setCmyk(rgbToCmyk(rgb2.r, rgb2.g, rgb2.b));
102310
102644
  }
102311
- if (typeof targetOpacity === "number" && targetOpacity !== alpha) {
102312
- setAlpha(targetOpacity);
102313
- }
102645
+ if (typeof tOpacity === "number" && tOpacity !== alpha) setAlpha(tOpacity);
102314
102646
  }, [isControlled, objects]);
102315
- const applyColor = React.useCallback(
102647
+ const applyControlledColor = React.useCallback(
102316
102648
  (newColor, newCmyk) => {
102317
102649
  const finalCmyk = newCmyk || cmyk2;
102318
102650
  setColor(newColor);
102319
102651
  if (isControlled && onChange) {
102320
102652
  onChange(newColor, finalCmyk);
102321
102653
  } else if (canvas2 && targets.length) {
102322
- applyFillToFabricObjects(targets, newColor, alpha, color, canvas2, finalCmyk);
102654
+ applyFillToFabricObjects(targets, newColor, alpha, activeColor || color, canvas2, finalCmyk);
102323
102655
  saveState();
102324
102656
  }
102325
102657
  },
102326
- [isControlled, onChange, canvas2, targets, alpha, color, cmyk2, saveState]
102658
+ [isControlled, onChange, canvas2, targets, alpha, color, activeColor, cmyk2, saveState]
102327
102659
  );
102328
102660
  const updateFromHsv = (newHsv) => {
102329
102661
  const rgb2 = hsvToRgb(newHsv.h, newHsv.s, newHsv.v);
@@ -102331,25 +102663,76 @@ end`);
102331
102663
  const hex = rgbToHex(rgb2.r, rgb2.g, rgb2.b);
102332
102664
  setHsv(newHsv);
102333
102665
  setCmyk(newC);
102334
- applyColor(hex, newC);
102666
+ applyControlledColor(hex, newC);
102335
102667
  };
102336
102668
  const updateFromCmyk = (newCmyk) => {
102337
102669
  const rgb2 = cmykToRgb(newCmyk.c, newCmyk.m, newCmyk.y, newCmyk.k);
102338
102670
  const hex = rgbToHex(rgb2.r, rgb2.g, rgb2.b);
102339
102671
  setCmyk(newCmyk);
102340
102672
  setHsv(rgbToHsv(rgb2.r, rgb2.g, rgb2.b));
102341
- applyColor(hex, newCmyk);
102673
+ applyControlledColor(hex, newCmyk);
102674
+ };
102675
+ const updateFromHex = (inputHex) => {
102676
+ let clean = inputHex.trim().replace(/^#/, "");
102677
+ if (clean.length === 3) clean = clean[0] + clean[0] + clean[1] + clean[1] + clean[2] + clean[2];
102678
+ if (/^[0-9a-fA-F]{6}$/.test(clean)) {
102679
+ const hex = `#${clean.toLowerCase()}`;
102680
+ const rgb2 = hexToRgb(hex);
102681
+ const newC = rgbToCmyk(rgb2.r, rgb2.g, rgb2.b);
102682
+ setHsv(rgbToHsv(rgb2.r, rgb2.g, rgb2.b));
102683
+ setCmyk(newC);
102684
+ applyControlledColor(hex, newC);
102685
+ setRecentColors((prev) => [hex, ...prev.filter((c2) => c2.toLowerCase() !== hex)].slice(0, 16));
102686
+ }
102342
102687
  };
102343
- if (!isControlled && !targets.length) return null;
102688
+ const handleCopy = () => {
102689
+ if (navigator.clipboard) {
102690
+ navigator.clipboard.writeText(color);
102691
+ setCopied(true);
102692
+ setTimeout(() => setCopied(false), 2e3);
102693
+ }
102694
+ };
102695
+ const handleEyeDropper = async () => {
102696
+ if (typeof window === "undefined" || !("EyeDropper" in window)) return;
102697
+ try {
102698
+ const result = await new window.EyeDropper().open();
102699
+ if (result?.sRGBHex) updateFromHex(result.sRGBHex);
102700
+ } catch {
102701
+ }
102702
+ };
102703
+ if (!isControlled) {
102704
+ if (!targets.length || !allTargetColors.length) return null;
102705
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `flex items-center gap-1.5 ${className}`, children: allTargetColors.map((c2, idx2) => {
102706
+ const isSelected = activeColor ? activeColor.toLowerCase() === c2.toLowerCase() : activeColorIndex === idx2 || idx2 === 0 && !activeColor;
102707
+ return /* @__PURE__ */ jsxRuntime.jsx(
102708
+ "button",
102709
+ {
102710
+ type: "button",
102711
+ onClick: () => {
102712
+ setActiveColor(c2, idx2);
102713
+ const { activePanel, setActivePanel } = useSidePanelStore.getState();
102714
+ if (activePanel !== "color") {
102715
+ setActivePanel("color");
102716
+ }
102717
+ },
102718
+ className: `w-7 h-7 rounded-md border shadow-xs transition-transform hover:scale-105 cursor-pointer relative ${isSelected ? "border-white ring-2 ring-[#02bc71] scale-105 z-10" : "border-neutral-600 hover:border-neutral-400"}`,
102719
+ style: { backgroundColor: c2 },
102720
+ title: `Edit color ${c2.toUpperCase()}`
102721
+ },
102722
+ c2 + "-" + idx2
102723
+ );
102724
+ }) });
102725
+ }
102344
102726
  const hueRgb = hsvToRgb(hsv.h, 100, 100);
102345
102727
  const hueColor = rgbToHex(hueRgb.r, hueRgb.g, hueRgb.b);
102346
102728
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `relative ${className}`, ref: containerRef, children: [
102347
102729
  /* @__PURE__ */ jsxRuntime.jsx(
102348
102730
  "div",
102349
102731
  {
102350
- className: "cursor-pointer w-7 h-7 rounded-md border border-neutral-600 shadow-sm",
102732
+ className: "cursor-pointer w-7 h-7 rounded-md border border-neutral-600 shadow-sm transition-transform hover:scale-105",
102351
102733
  style: { background: color },
102352
- onClick: () => setOpen((v2) => !v2)
102734
+ onClick: () => setOpen((v2) => !v2),
102735
+ title: "Color Picker"
102353
102736
  }
102354
102737
  ),
102355
102738
  open2 && reactDom.createPortal(
@@ -102357,47 +102740,204 @@ end`);
102357
102740
  "div",
102358
102741
  {
102359
102742
  ref: popoverRef,
102743
+ "data-cmyk-popover": "true",
102360
102744
  style: { position: "fixed", top: `${coords.top}px`, left: `${coords.left}px`, zIndex: 999999 },
102361
- className: "bg-white border border-gray-300 rounded-lg shadow-2xl p-3 w-[290px] text-gray-800",
102745
+ className: "cmyk-picker-popover bg-white rounded-2xl shadow-2xl p-3.5 w-[270px] text-gray-800 border border-gray-100 select-none font-sans",
102362
102746
  children: [
102363
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center mb-3", children: [
102364
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
102365
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-gray-800 font-semibold text-sm", children: "Color Picker" }),
102366
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[9px] font-bold tracking-wider text-cyan-600 bg-cyan-50 px-1.5 py-0.5 rounded uppercase", children: "CMYK" })
102367
- ] }),
102368
- /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: () => setOpen(false), className: "text-gray-400 hover:text-gray-600 text-lg leading-none", children: "✕" })
102369
- ] }),
102370
- /* @__PURE__ */ jsxRuntime.jsx(GradientSelector, { hsv, hueColor, refEl: gradientRef, onChange: updateFromHsv }),
102371
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-4", children: [
102372
- /* @__PURE__ */ jsxRuntime.jsx(ColorPreviewBox, { color, alpha }),
102373
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1", children: [
102374
- /* @__PURE__ */ jsxRuntime.jsx(HueSlider, { hsv, refEl: hueRef, onChange: updateFromHsv }),
102375
- !isControlled && /* @__PURE__ */ jsxRuntime.jsx(
102376
- AlphaSlider,
102747
+ /* @__PURE__ */ jsxRuntime.jsx(
102748
+ "div",
102749
+ {
102750
+ ref: svRef,
102751
+ className: "w-full h-[135px] rounded-xl relative cursor-crosshair overflow-hidden touch-none",
102752
+ style: { background: `linear-gradient(to top, #000, transparent), linear-gradient(to right, #fff, ${hueColor})` },
102753
+ onPointerDown: (e2) => {
102754
+ e2.currentTarget.setPointerCapture(e2.pointerId);
102755
+ const rect = e2.currentTarget.getBoundingClientRect();
102756
+ updateFromHsv({
102757
+ ...hsv,
102758
+ s: Math.round(Math.max(0, Math.min(1, (e2.clientX - rect.left) / rect.width)) * 100),
102759
+ v: Math.round((1 - Math.max(0, Math.min(1, (e2.clientY - rect.top) / rect.height))) * 100)
102760
+ });
102761
+ },
102762
+ onPointerMove: (e2) => {
102763
+ if (e2.buttons === 0) return;
102764
+ const rect = e2.currentTarget.getBoundingClientRect();
102765
+ updateFromHsv({
102766
+ ...hsv,
102767
+ s: Math.round(Math.max(0, Math.min(1, (e2.clientX - rect.left) / rect.width)) * 100),
102768
+ v: Math.round((1 - Math.max(0, Math.min(1, (e2.clientY - rect.top) / rect.height))) * 100)
102769
+ });
102770
+ },
102771
+ children: /* @__PURE__ */ jsxRuntime.jsx(
102772
+ "div",
102377
102773
  {
102378
- alpha,
102379
- color,
102380
- refEl: alphaRef,
102381
- setAlpha,
102382
- canvas: canvas2,
102383
- targets,
102384
- saveState
102774
+ className: "w-4 h-4 rounded-full border-2 border-white shadow-md absolute pointer-events-none -translate-x-1/2 -translate-y-1/2",
102775
+ style: { left: `${hsv.s}%`, top: `${100 - hsv.v}%`, backgroundColor: color }
102385
102776
  }
102386
102777
  )
102387
- ] })
102388
- ] }),
102778
+ }
102779
+ ),
102389
102780
  /* @__PURE__ */ jsxRuntime.jsx(
102390
- CMYKInputs,
102781
+ "div",
102391
102782
  {
102392
- cmyk: cmyk2,
102393
- alpha,
102394
- setAlpha,
102395
- updateColor: updateFromCmyk,
102396
- canvas: canvas2,
102397
- targets,
102398
- saveState
102783
+ ref: hueRef,
102784
+ className: "w-full h-3 rounded-full relative cursor-pointer mt-3 mb-2 touch-none shadow-inner",
102785
+ style: { background: "linear-gradient(to right, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%)" },
102786
+ onPointerDown: (e2) => {
102787
+ e2.currentTarget.setPointerCapture(e2.pointerId);
102788
+ const rect = e2.currentTarget.getBoundingClientRect();
102789
+ updateFromHsv({ ...hsv, h: Math.round(Math.max(0, Math.min(1, (e2.clientX - rect.left) / rect.width)) * 360) % 360 });
102790
+ },
102791
+ onPointerMove: (e2) => {
102792
+ if (e2.buttons === 0) return;
102793
+ const rect = e2.currentTarget.getBoundingClientRect();
102794
+ updateFromHsv({ ...hsv, h: Math.round(Math.max(0, Math.min(1, (e2.clientX - rect.left) / rect.width)) * 360) % 360 });
102795
+ },
102796
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-4 h-4 rounded-full border-2 border-white shadow-md absolute top-1/2 -translate-y-1/2 -translate-x-1/2 pointer-events-none", style: { left: `${hsv.h / 360 * 100}%`, backgroundColor: hueColor } })
102399
102797
  }
102400
- )
102798
+ ),
102799
+ /* @__PURE__ */ jsxRuntime.jsxs(
102800
+ "div",
102801
+ {
102802
+ ref: alphaRef,
102803
+ className: "w-full h-3 rounded-full relative cursor-pointer mb-3 touch-none pqc-checkerboard shadow-inner overflow-hidden",
102804
+ onPointerDown: (e2) => {
102805
+ e2.currentTarget.setPointerCapture(e2.pointerId);
102806
+ const rect = e2.currentTarget.getBoundingClientRect();
102807
+ const newAlpha = Math.round(Math.max(0, Math.min(1, (e2.clientX - rect.left) / rect.width)) * 100);
102808
+ setAlpha(newAlpha);
102809
+ if (canvas2 && targets.length) {
102810
+ targets.forEach((obj) => obj.set("opacity", newAlpha / 100));
102811
+ canvas2.requestRenderAll();
102812
+ }
102813
+ },
102814
+ onPointerMove: (e2) => {
102815
+ if (e2.buttons === 0) return;
102816
+ const rect = e2.currentTarget.getBoundingClientRect();
102817
+ const newAlpha = Math.round(Math.max(0, Math.min(1, (e2.clientX - rect.left) / rect.width)) * 100);
102818
+ setAlpha(newAlpha);
102819
+ if (canvas2 && targets.length) {
102820
+ targets.forEach((obj) => obj.set("opacity", newAlpha / 100));
102821
+ canvas2.requestRenderAll();
102822
+ }
102823
+ },
102824
+ children: [
102825
+ /* @__PURE__ */ jsxRuntime.jsx(
102826
+ "div",
102827
+ {
102828
+ className: "absolute inset-0",
102829
+ style: {
102830
+ background: `linear-gradient(to right, transparent, ${color})`
102831
+ }
102832
+ }
102833
+ ),
102834
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-4 h-4 rounded-full border-2 border-white shadow-md absolute top-1/2 -translate-y-1/2 -translate-x-1/2 pointer-events-none", style: { left: `${alpha}%` } })
102835
+ ]
102836
+ }
102837
+ ),
102838
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-1.5 mb-3 bg-gray-50 p-1 rounded-xl border border-gray-100", children: [
102839
+ /* @__PURE__ */ jsxRuntime.jsx(
102840
+ "button",
102841
+ {
102842
+ onClick: () => setMode("HEX"),
102843
+ className: `flex-1 py-1 text-xs font-semibold rounded-lg transition-all ${mode2 === "HEX" ? "bg-white text-gray-900 shadow-sm" : "text-gray-500 hover:text-gray-900"}`,
102844
+ children: "HEX"
102845
+ }
102846
+ ),
102847
+ /* @__PURE__ */ jsxRuntime.jsx(
102848
+ "button",
102849
+ {
102850
+ onClick: () => setMode("CMYK"),
102851
+ className: `flex-1 py-1 text-xs font-semibold rounded-lg transition-all ${mode2 === "CMYK" ? "bg-white text-gray-900 shadow-sm" : "text-gray-500 hover:text-gray-900"}`,
102852
+ children: "CMYK"
102853
+ }
102854
+ )
102855
+ ] }),
102856
+ mode2 === "HEX" ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 mb-3", children: [
102857
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 flex items-center bg-gray-50 border border-gray-200 rounded-xl px-2.5 py-1.5", children: [
102858
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-400 text-xs font-mono", children: "#" }),
102859
+ /* @__PURE__ */ jsxRuntime.jsx(
102860
+ "input",
102861
+ {
102862
+ type: "text",
102863
+ value: color.replace("#", "").toUpperCase(),
102864
+ onChange: (e2) => updateFromHex(e2.target.value),
102865
+ className: "w-full bg-transparent text-xs font-mono font-medium focus:outline-none uppercase pl-1 text-gray-800"
102866
+ }
102867
+ )
102868
+ ] }),
102869
+ /* @__PURE__ */ jsxRuntime.jsx(
102870
+ "button",
102871
+ {
102872
+ onClick: handleCopy,
102873
+ className: "p-2 rounded-xl bg-gray-50 hover:bg-gray-100 border border-gray-200 text-gray-600 transition-colors cursor-pointer text-xs",
102874
+ title: "Copy HEX",
102875
+ children: copied ? "✓" : "Copy"
102876
+ }
102877
+ ),
102878
+ typeof window !== "undefined" && "EyeDropper" in window && /* @__PURE__ */ jsxRuntime.jsx(
102879
+ "button",
102880
+ {
102881
+ onClick: handleEyeDropper,
102882
+ className: "p-2 rounded-xl bg-gray-50 hover:bg-gray-100 border border-gray-200 text-gray-600 hover:text-cyan-600 transition-colors cursor-pointer",
102883
+ title: "Eyedropper",
102884
+ children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
102885
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m14 7 3 3" }),
102886
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M19 10 9 20l-4 1 1-4 10-10a2.828 2.828 0 0 1 4 4Z" }),
102887
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m15 4 2-2 4 4-2 2" })
102888
+ ] })
102889
+ }
102890
+ )
102891
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-1.5 mb-3", children: CMYK_CHANNELS$1.map(({ key, label, color: lColor, name }) => /* @__PURE__ */ jsxRuntime.jsx(
102892
+ ChannelRow,
102893
+ {
102894
+ label,
102895
+ labelColor: lColor,
102896
+ ariaLabel: name,
102897
+ value: cmyk2[key],
102898
+ min: 0,
102899
+ max: 100,
102900
+ gradient: `linear-gradient(to right, ${cmykToHex(
102901
+ key === "c" ? 0 : cmyk2.c,
102902
+ key === "m" ? 0 : cmyk2.m,
102903
+ key === "y" ? 0 : cmyk2.y,
102904
+ key === "k" ? 0 : cmyk2.k
102905
+ )}, ${cmykToHex(
102906
+ key === "c" ? 100 : cmyk2.c,
102907
+ key === "m" ? 100 : cmyk2.m,
102908
+ key === "y" ? 100 : cmyk2.y,
102909
+ key === "k" ? 100 : cmyk2.k
102910
+ )})`,
102911
+ onChange: (val) => updateFromCmyk({ ...cmyk2, [key]: val })
102912
+ },
102913
+ key
102914
+ )) }),
102915
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pt-2 border-t border-gray-100", children: [
102916
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-bold text-gray-400 uppercase tracking-wider block mb-1.5", children: "Preset Swatches" }),
102917
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-8 gap-1.5", children: DEFAULT_COLORS.map((c2) => /* @__PURE__ */ jsxRuntime.jsx(
102918
+ "button",
102919
+ {
102920
+ className: "w-6 h-6 rounded-md border border-gray-200 shadow-xs cursor-pointer hover:scale-110 transition-transform",
102921
+ style: { backgroundColor: c2 },
102922
+ onClick: () => updateFromHex(c2),
102923
+ title: c2
102924
+ },
102925
+ c2
102926
+ )) })
102927
+ ] }),
102928
+ recentColors.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pt-2 mt-2 border-t border-gray-100", children: [
102929
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-bold text-gray-400 uppercase tracking-wider block mb-1.5", children: "Recent Colors" }),
102930
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-8 gap-1.5", children: recentColors.map((c2, i2) => /* @__PURE__ */ jsxRuntime.jsx(
102931
+ "button",
102932
+ {
102933
+ className: "w-6 h-6 rounded-md border border-gray-200 shadow-xs cursor-pointer hover:scale-110 transition-transform",
102934
+ style: { backgroundColor: c2 },
102935
+ onClick: () => updateFromHex(c2),
102936
+ title: c2
102937
+ },
102938
+ c2 + i2
102939
+ )) })
102940
+ ] })
102401
102941
  ]
102402
102942
  }
102403
102943
  ),
@@ -102493,11 +103033,26 @@ end`);
102493
103033
  hex
102494
103034
  ))
102495
103035
  ] }),
102496
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[calc(100vh-122px)] overflow-auto", ref: scrollRef, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-2 gap-3 mx-4 pb-5", children: backgroundImages.map((item, i2) => /* @__PURE__ */ jsxRuntime.jsx(
103036
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[calc(100vh-165px)] overflow-auto custom-scrollbar", ref: scrollRef, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-2 gap-3 mx-4 pb-5", children: backgroundImages.map((item, i2) => /* @__PURE__ */ jsxRuntime.jsx(
102497
103037
  "button",
102498
103038
  {
102499
103039
  className: "relative aspect-square rounded overflow-hidden group cursor-pointer",
102500
- onClick: () => handleApplyBackground("image", item.largeImage),
103040
+ onClick: async () => {
103041
+ try {
103042
+ startLoading();
103043
+ const image = await ShapeService.convertImages(item.largeImage);
103044
+ const imageUrl = image?.success ? image?.url : null;
103045
+ if (!imageUrl) return;
103046
+ handleApplyBackground("image", imageUrl);
103047
+ if (window.innerWidth < 768) {
103048
+ useSidePanelStore.getState().setActivePanel(null);
103049
+ }
103050
+ } catch (error2) {
103051
+ console.error("Failed to convert image:", error2);
103052
+ } finally {
103053
+ stopLoading();
103054
+ }
103055
+ },
102501
103056
  children: /* @__PURE__ */ jsxRuntime.jsx(
102502
103057
  "img",
102503
103058
  {
@@ -102515,14 +103070,40 @@ end`);
102515
103070
  }
102516
103071
  function UploadPanel() {
102517
103072
  const [assets, setAssets] = React.useState([]);
102518
- function handleFileChange(e2) {
103073
+ const { startLoading, stopLoading } = useLoaderStore();
103074
+ async function handleFileChange(e2) {
102519
103075
  const file = e2.target.files?.[0];
102520
103076
  if (!file) return;
102521
103077
  if (!file.type.startsWith("image/")) return;
102522
- const url = URL.createObjectURL(file);
102523
- setAssets((prev) => [...prev, { id: crypto.randomUUID(), url }]);
103078
+ const previewUrl = URL.createObjectURL(file);
103079
+ const newAsset = { id: crypto.randomUUID(), url: previewUrl, file };
103080
+ setAssets((prev) => [newAsset, ...prev]);
102524
103081
  e2.target.value = "";
102525
103082
  }
103083
+ async function handleAssetClick(asset) {
103084
+ try {
103085
+ startLoading();
103086
+ let targetUrl = asset.uploadedUrl;
103087
+ if (!targetUrl && asset.file) {
103088
+ targetUrl = await UploadService.uploadFileApi(asset.file);
103089
+ setAssets(
103090
+ (prev) => prev.map((a2) => a2.id === asset.id ? { ...a2, uploadedUrl: targetUrl } : a2)
103091
+ );
103092
+ }
103093
+ const finalUrl = targetUrl || asset.url;
103094
+ if (finalUrl) {
103095
+ addImageToCanvas(finalUrl);
103096
+ if (window.innerWidth < 768) {
103097
+ useSidePanelStore.getState().setActivePanel(null);
103098
+ }
103099
+ }
103100
+ } catch (error2) {
103101
+ console.error("Failed to add image to canvas:", error2);
103102
+ addImageToCanvas(asset.url);
103103
+ } finally {
103104
+ stopLoading();
103105
+ }
103106
+ }
102526
103107
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-4", children: [
102527
103108
  /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-sm font-semibold mb-4 mt-2", children: "Upload your assets" }),
102528
103109
  /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "w-full bg-[#02bc71] border border-neutral-700 hover:bg-[#03945a] py-2 rounded-md font-medium flex items-center justify-center gap-2 cursor-pointer", children: [
@@ -102541,17 +103122,12 @@ end`);
102541
103122
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-2 gap-3 mt-3", children: assets.map((asset) => /* @__PURE__ */ jsxRuntime.jsx(
102542
103123
  "button",
102543
103124
  {
102544
- onClick: () => {
102545
- addImageToCanvas(asset.url);
102546
- if (window.innerWidth < 768) {
102547
- useSidePanelStore.getState().setActivePanel(null);
102548
- }
102549
- },
103125
+ onClick: () => handleAssetClick(asset),
102550
103126
  className: "rounded-md overflow-hidden transition cursor-pointer",
102551
103127
  children: /* @__PURE__ */ jsxRuntime.jsx(
102552
103128
  "img",
102553
103129
  {
102554
- src: asset.url,
103130
+ src: asset.uploadedUrl || asset.url,
102555
103131
  alt: "asset",
102556
103132
  height: 300,
102557
103133
  width: 300,
@@ -104837,7 +105413,7 @@ end`);
104837
105413
  <path fill="${dark}" d="${pathData.trim()}" />
104838
105414
  </svg>`;
104839
105415
  }
104840
- const inputStyle$1 = "w-full bg-neutral-800 border border-neutral-600 rounded-md px-3 py-2 text-sm outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500 transition";
105416
+ const inputStyle$1 = "w-full bg-neutral-800 border border-neutral-600 rounded-md px-3 py-2 text-sm outline-none focus:border-[#02bc71] focus:ring-1 focus:ring-[#02bc71] transition";
104841
105417
  const FormField$1 = ({
104842
105418
  label,
104843
105419
  error: error2,
@@ -105044,7 +105620,7 @@ END:VCARD`;
105044
105620
  ]
105045
105621
  }
105046
105622
  ) }),
105047
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "h-[calc(100vh-200px)] overflow-auto", children: [
105623
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "h-[calc(100vh-200px)] overflow-auto custom-scrollbar", children: [
105048
105624
  type === "url" && /* @__PURE__ */ jsxRuntime.jsx(FormField$1, { label: "Website URL", error: errors.url, children: /* @__PURE__ */ jsxRuntime.jsx(
105049
105625
  "input",
105050
105626
  {
@@ -105642,17 +106218,38 @@ END:VCARD`;
105642
106218
  canvas2.requestRenderAll();
105643
106219
  }, [canvas2, active, blur.enabled, blur.value]);
105644
106220
  }
105645
- function useBrightnessEffect(canvas2, enabled) {
105646
- const { brightness } = useEffectStore();
106221
+ function useBrightnessEffect(canvas2, active) {
106222
+ const brightness = useEffectStore((s2) => s2.brightness);
105647
106223
  React.useEffect(() => {
105648
- if (!enabled) return;
105649
- if (!canvas2) return;
105650
- if (!brightness.enabled) return;
105651
- const target = getActiveEffectTarget(canvas2);
105652
- if (!target) return;
105653
- target.set("opacity", 1 + brightness.value / 100);
106224
+ if (!canvas2 || !active) return;
106225
+ const obj = getActiveEffectTarget(canvas2);
106226
+ if (!obj) return;
106227
+ if (obj.type !== "image" && obj.type !== "FabricImage") return;
106228
+ const image = obj;
106229
+ image.filters = image.filters || [];
106230
+ const BRIGHTNESS_INDEX = image.filters.findIndex(
106231
+ (f) => f && (f.type === "Brightness" || f instanceof Sh.Brightness)
106232
+ );
106233
+ if (!brightness.enabled || brightness.value === 0) {
106234
+ if (BRIGHTNESS_INDEX !== -1) {
106235
+ image.filters.splice(BRIGHTNESS_INDEX, 1);
106236
+ image.applyFilters();
106237
+ canvas2.requestRenderAll();
106238
+ }
106239
+ return;
106240
+ }
106241
+ const strength = Math.max(-1, Math.min(1, brightness.value / 100));
106242
+ const brightnessFilter = new Sh.Brightness({
106243
+ brightness: strength
106244
+ });
106245
+ if (BRIGHTNESS_INDEX !== -1) {
106246
+ image.filters[BRIGHTNESS_INDEX] = brightnessFilter;
106247
+ } else {
106248
+ image.filters.push(brightnessFilter);
106249
+ }
106250
+ image.applyFilters();
105654
106251
  canvas2.requestRenderAll();
105655
- }, [canvas2, enabled, brightness.enabled, brightness.value]);
106252
+ }, [canvas2, active, brightness.enabled, brightness.value]);
105656
106253
  }
105657
106254
  function EffectSection({
105658
106255
  label,
@@ -106246,7 +106843,6 @@ END:VCARD`;
106246
106843
  const { toggleShadow, setShadow } = useEffectStore();
106247
106844
  useBlurEffect(canvas2, isOpen);
106248
106845
  useBrightnessEffect(canvas2, isOpen);
106249
- useBrightnessEffect(canvas2, isOpen);
106250
106846
  useTemperatureEffect(canvas2, isOpen);
106251
106847
  useContrastEffect(canvas2, isOpen);
106252
106848
  useShadowEffect(canvas2, isOpen);
@@ -106334,7 +106930,7 @@ END:VCARD`;
106334
106930
  { id: "natural", label: "Natural" },
106335
106931
  { id: "warm", label: "Warm" }
106336
106932
  ];
106337
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 overflow-y-auto custom-scrollbar overflow-x-hidden px-4", children: [
106933
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 overflow-y-auto custom-scrollbar overflow-x-hidden px-4 custom-scrollbar", children: [
106338
106934
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-2 gap-3 mb-6", children: PRESETS.map((p2) => /* @__PURE__ */ jsxRuntime.jsx(
106339
106935
  "button",
106340
106936
  {
@@ -106920,7 +107516,7 @@ END:VCARD`;
106920
107516
  saveState();
106921
107517
  setActivePanel(null);
106922
107518
  };
106923
- return /* @__PURE__ */ jsxRuntime.jsxs("aside", { className: "bg-neutral-900 border-r border-neutral-800 p-4 text-white h-full overflow-y-auto", children: [
107519
+ return /* @__PURE__ */ jsxRuntime.jsxs("aside", { className: "bg-neutral-900 border-r border-neutral-800 p-4 text-white h-full overflow-y-auto custom-scrollbar", children: [
106924
107520
  /* @__PURE__ */ jsxRuntime.jsx("div", { onClick: () => setActivePanel(null), className: "cursor-pointer mb-2", children: "← Back" }),
106925
107521
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-between items-center mb-4", children: /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-semibold ", children: "Mask image" }) }),
106926
107522
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-2 gap-3", children: mask.map((m2, index) => /* @__PURE__ */ jsxRuntime.jsx(
@@ -107094,7 +107690,7 @@ END:VCARD`;
107094
107690
  )
107095
107691
  ] }),
107096
107692
  /* @__PURE__ */ jsxRuntime.jsx("hr", { className: "text-neutral-800 h-[0.5px]" }),
107097
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[calc(100vh-125px)] overflow-auto", ref: scrollRef, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mx-4 py-5", children: fontData.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-400 text-sm text-center", children: "No results found" }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
107693
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[calc(100vh-126px)] overflow-auto custom-scrollbar", ref: scrollRef, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mx-4 py-5", children: fontData.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-400 text-sm text-center", children: "No results found" }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
107098
107694
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-1 gap-4", children: fontData.map((item, i2) => /* @__PURE__ */ jsxRuntime.jsx(
107099
107695
  "button",
107100
107696
  {
@@ -152406,7 +153002,7 @@ ${elements2.join("\n")}
152406
153002
  }
152407
153003
  };
152408
153004
  const stopPropagation = (e2) => e2.stopPropagation();
152409
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col h-full mx-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 overflow-y-auto space-y-5 pr-2", children: [
153005
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col h-full mx-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 overflow-y-auto space-y-5 pr-2 custom-scrollbar", children: [
152410
153006
  /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-semibold text-white", children: "Bar Code" }),
152411
153007
  /* @__PURE__ */ jsxRuntime.jsx(FormField, { label: "Select Bar Code Type", children: /* @__PURE__ */ jsxRuntime.jsx(
152412
153008
  "select",
@@ -152464,82 +153060,99 @@ ${elements2.join("\n")}
152464
153060
  )
152465
153061
  ] }) });
152466
153062
  }
152467
- const QUOTES = [
152468
- {
152469
- content: "There are no absolutes in this world, and there will always be mistakes that are made within brokerage firms; there will always be people who set out to deceive the regulators and even deceive their own senior management.",
152470
- author: "Mary Schapiro"
152471
- },
152472
- {
152473
- content: "Things will get better if you just hold out long enough.",
152474
- author: "Desmond Dekker"
152475
- },
152476
- {
152477
- content: "The thing I am attracted to is just good writing and stories that are based somewhat in reality.",
152478
- author: "Jennifer Aniston"
152479
- },
152480
- {
152481
- content: "Tax reform and expanded trade are going to be so important to the economy of Illinois.",
152482
- author: "Bill Foster"
152483
- },
152484
- {
152485
- content: "In the middle of every difficulty lies opportunity.",
152486
- author: "Albert Einstein"
152487
- },
152488
- {
152489
- content: "It does not matter how slowly you go as long as you do not stop.",
152490
- author: "Confucius"
152491
- },
152492
- {
152493
- content: "Life is what happens when you're busy making other plans.",
152494
- author: "John Lennon"
152495
- },
152496
- {
152497
- content: "The future belongs to those who believe in the beauty of their dreams.",
152498
- author: "Eleanor Roosevelt"
152499
- },
152500
- {
152501
- content: "Success is not final, failure is not fatal: it is the courage to continue that counts.",
152502
- author: "Winston Churchill"
152503
- },
152504
- {
152505
- content: "You miss 100% of the shots you don't take.",
152506
- author: "Wayne Gretzky"
152507
- },
152508
- {
152509
- content: "Whether you think you can or you think you can't, you're right.",
152510
- author: "Henry Ford"
152511
- },
152512
- {
152513
- content: "I have not failed. I've just found 10,000 ways that won't work.",
152514
- author: "Thomas Edison"
153063
+ const QuoteService = {
153064
+ async getAllQuotes(params = {}) {
153065
+ try {
153066
+ const query = new URLSearchParams();
153067
+ if (params.page) query.append("page", String(params.page));
153068
+ if (params.limit) query.append("limit", String(params.limit));
153069
+ if (params.search) query.append("search", params.search);
153070
+ query.append("status", "true");
153071
+ const res = await apiFetch(`/api/quotes?${query.toString()}`, {
153072
+ method: "GET"
153073
+ });
153074
+ return Array.isArray(res?.data) ? res.data : [];
153075
+ } catch (err) {
153076
+ console.error("Failed to fetch quotes:", err);
153077
+ return [];
153078
+ }
152515
153079
  }
152516
- ];
153080
+ };
152517
153081
  function QuotesPanel() {
153082
+ const [quotes, setQuotes] = React.useState([]);
153083
+ const [page, setPage] = React.useState(1);
153084
+ const [hasMore, setHasMore] = React.useState(true);
153085
+ const [isLoading, setIsLoading] = React.useState(false);
153086
+ const scrollRef = React.useRef(null);
153087
+ const fetchQuotes = React.useCallback(async (pageNo) => {
153088
+ setIsLoading(true);
153089
+ try {
153090
+ const data = await QuoteService.getAllQuotes({ page: pageNo, limit: 20 });
153091
+ if (data && data.length > 0) {
153092
+ setQuotes((prev) => pageNo === 1 ? data : [...prev, ...data]);
153093
+ if (data.length < 20) {
153094
+ setHasMore(false);
153095
+ }
153096
+ } else {
153097
+ setHasMore(false);
153098
+ }
153099
+ } catch (err) {
153100
+ console.error("Failed to load quotes:", err);
153101
+ } finally {
153102
+ setIsLoading(false);
153103
+ }
153104
+ }, []);
153105
+ React.useEffect(() => {
153106
+ fetchQuotes(1);
153107
+ }, [fetchQuotes]);
153108
+ React.useEffect(() => {
153109
+ const el = scrollRef.current;
153110
+ if (!el) return;
153111
+ const onScroll = () => {
153112
+ if (!hasMore || isLoading) return;
153113
+ if (el.scrollTop + el.clientHeight >= el.scrollHeight - 150) {
153114
+ setPage((prev) => {
153115
+ const next = prev + 1;
153116
+ fetchQuotes(next);
153117
+ return next;
153118
+ });
153119
+ }
153120
+ };
153121
+ el.addEventListener("scroll", onScroll);
153122
+ return () => el.removeEventListener("scroll", onScroll);
153123
+ }, [hasMore, isLoading, fetchQuotes]);
152518
153124
  const addQuote = async (content, author) => {
152519
- await addTextToCanvas(18, 300, `${content}
153125
+ await addTextToCanvas(18, 300, author ? `${content}
152520
153126
 
152521
- — ${author}`);
153127
+ — ${author}` : content);
153128
+ if (window.innerWidth < 768) {
153129
+ useSidePanelStore.getState().setActivePanel(null);
153130
+ }
152522
153131
  };
152523
153132
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col h-full", children: [
152524
153133
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 pb-3", children: /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-base font-semibold text-white", children: "Quotes" }) }),
152525
153134
  /* @__PURE__ */ jsxRuntime.jsx("hr", { className: "border-neutral-800 mx-4 mb-3" }),
152526
- /* @__PURE__ */ jsxRuntime.jsx(
153135
+ /* @__PURE__ */ jsxRuntime.jsxs(
152527
153136
  "div",
152528
153137
  {
152529
- className: "flex-1 min-h-0 overflow-y-auto flex flex-col gap-3 px-4 py-2",
152530
- children: QUOTES.map((q2, i2) => /* @__PURE__ */ jsxRuntime.jsxs(
152531
- "button",
152532
- {
152533
- onClick: () => addQuote(q2.content, q2.author),
152534
- title: "Click to add to canvas",
152535
- className: "\n w-full text-left rounded-lg\n bg-neutral-800 hover:bg-neutral-700\n border border-neutral-700 hover:border-[#02bc71]/40\n px-4 py-3.5 cursor-pointer\n transition-all duration-150 active:scale-[0.98]\n group\n ",
152536
- children: [
152537
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-neutral-200 text-[13px] leading-relaxed mb-2 group-hover:text-white transition-colors", children: q2.content }),
152538
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-right text-[12px] font-semibold text-[#02bc71]", children: q2.author })
152539
- ]
152540
- },
152541
- i2
152542
- ))
153138
+ ref: scrollRef,
153139
+ className: "flex-1 min-h-0 overflow-y-auto flex flex-col gap-3 px-4 py-2 custom-scrollbar",
153140
+ children: [
153141
+ quotes.map((q2, i2) => /* @__PURE__ */ jsxRuntime.jsxs(
153142
+ "button",
153143
+ {
153144
+ onClick: () => addQuote(q2.content, q2.author),
153145
+ title: "Click to add to canvas",
153146
+ className: "\n w-full text-left rounded-lg\n bg-neutral-800 hover:bg-neutral-700\n border border-neutral-700 hover:border-[#02bc71]/40\n px-4 py-3.5 cursor-pointer\n transition-all duration-150 active:scale-[0.98]\n group\n ",
153147
+ children: [
153148
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-neutral-200 text-[13px] leading-relaxed mb-2 group-hover:text-white transition-colors", children: q2.content }),
153149
+ q2.author && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-right text-[12px] font-semibold text-[#02bc71]", children: q2.author })
153150
+ ]
153151
+ },
153152
+ q2._id || i2
153153
+ )),
153154
+ isLoading && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center py-4 text-xs text-neutral-500", children: "Loading more quotes..." })
153155
+ ]
152543
153156
  }
152544
153157
  )
152545
153158
  ] });
@@ -152567,6 +153180,559 @@ ${elements2.join("\n")}
152567
153180
  ) })
152568
153181
  }
152569
153182
  );
153183
+ const ColorService = {
153184
+ async getAll(params) {
153185
+ try {
153186
+ const searchParams = new URLSearchParams();
153187
+ if (params?.search) searchParams.append("search", params.search);
153188
+ if (params?.status !== void 0) searchParams.append("status", String(params.status));
153189
+ searchParams.append("limit", String(params?.limit || 100));
153190
+ if (params?.page) searchParams.append("page", String(params.page));
153191
+ const res = await apiFetch(`/api/colors?${searchParams.toString()}`, {
153192
+ method: "GET"
153193
+ });
153194
+ if (Array.isArray(res)) return res;
153195
+ if (Array.isArray(res?.data)) return res.data;
153196
+ return [];
153197
+ } catch (err) {
153198
+ console.error("Failed to fetch colors:", err);
153199
+ return [];
153200
+ }
153201
+ }
153202
+ };
153203
+ const DEFAULT_PRESET_COLORS = [
153204
+ "#ffffff",
153205
+ "#000000",
153206
+ "#1e293b",
153207
+ "#475569",
153208
+ "#94a3b8",
153209
+ "#e2e8f0",
153210
+ "#ef4444",
153211
+ "#f97316",
153212
+ "#f59e0b",
153213
+ "#eab308",
153214
+ "#84cc16",
153215
+ "#22c55e",
153216
+ "#10b981",
153217
+ "#06b6d4",
153218
+ "#0ea5e9",
153219
+ "#3b82f6",
153220
+ "#6366f1",
153221
+ "#8b5cf6",
153222
+ "#a855f7",
153223
+ "#d946ef",
153224
+ "#ec4899",
153225
+ "#f43f5e"
153226
+ ];
153227
+ const CMYK_CHANNELS = [
153228
+ { key: "c", label: "C", color: "#009fe3", name: "Cyan" },
153229
+ { key: "m", label: "M", color: "#e6007e", name: "Magenta" },
153230
+ { key: "y", label: "Y", color: "#dcb000", name: "Yellow" },
153231
+ { key: "k", label: "K", color: "#222222", name: "Black" }
153232
+ ];
153233
+ function ColorPanel() {
153234
+ const canvas2 = useEditorStore((s2) => s2.canvas);
153235
+ const { objects, activeColor, activeColorIndex, setActiveColor } = useSelectionStore();
153236
+ const { setActivePanel } = useSidePanelStore();
153237
+ const saveState = useHistoryStore((s2) => s2.saveState);
153238
+ const targets = React.useMemo(
153239
+ () => objects.filter((o2) => o2.name !== "workarea"),
153240
+ [objects]
153241
+ );
153242
+ const objectColors = React.useMemo(() => extractAllTargetColors(targets), [targets, objects]);
153243
+ const targetInfo = React.useMemo(() => extractTargetColor(targets), [targets]);
153244
+ const initialColor = activeColor || (objectColors.length > 0 ? objectColors[0] : targetInfo.color || "#000000");
153245
+ const [currentColor, setCurrentColor] = React.useState(initialColor);
153246
+ const [currentCmyk, setCurrentCmyk] = React.useState(() => hexToCmyk(initialColor));
153247
+ const [hsv, setHsv] = React.useState(() => {
153248
+ const rgb2 = hexToRgb(initialColor);
153249
+ return rgbToHsv(rgb2.r, rgb2.g, rgb2.b);
153250
+ });
153251
+ const [alpha, setAlpha] = React.useState(() => targetInfo.opacity ?? 100);
153252
+ const [colorMode, setColorMode] = React.useState("HEX");
153253
+ const [dbColors, setDbColors] = React.useState([]);
153254
+ const [isLoadingColors, setIsLoadingColors] = React.useState(false);
153255
+ const [copied, setCopied] = React.useState(false);
153256
+ const svRef = React.useRef(null);
153257
+ const hueRef = React.useRef(null);
153258
+ const alphaRef = React.useRef(null);
153259
+ React.useEffect(() => {
153260
+ if (targets.length > 0) {
153261
+ const colors = extractAllTargetColors(targets);
153262
+ if (colors.length > 0) {
153263
+ let chosenColor = activeColor;
153264
+ if (!chosenColor || !colors.includes(chosenColor)) {
153265
+ chosenColor = colors[activeColorIndex] || colors[0];
153266
+ }
153267
+ setCurrentColor(chosenColor);
153268
+ const c2 = hexToCmyk(chosenColor);
153269
+ setCurrentCmyk(c2);
153270
+ const rgb2 = hexToRgb(chosenColor);
153271
+ setHsv(rgbToHsv(rgb2.r, rgb2.g, rgb2.b));
153272
+ const info = extractTargetColor(targets);
153273
+ if (info.opacity !== void 0) setAlpha(info.opacity);
153274
+ if (activeColor !== chosenColor) {
153275
+ const idx2 = colors.indexOf(chosenColor);
153276
+ setActiveColor(chosenColor, idx2 >= 0 ? idx2 : 0);
153277
+ }
153278
+ }
153279
+ }
153280
+ }, [targets, activeColor, activeColorIndex]);
153281
+ React.useEffect(() => {
153282
+ let isMounted = true;
153283
+ const fetchColors = async () => {
153284
+ setIsLoadingColors(true);
153285
+ try {
153286
+ const data = await ColorService.getAll({ status: true, limit: 100 });
153287
+ if (isMounted) setDbColors(data);
153288
+ } finally {
153289
+ if (isMounted) setIsLoadingColors(false);
153290
+ }
153291
+ };
153292
+ fetchColors();
153293
+ return () => {
153294
+ isMounted = false;
153295
+ };
153296
+ }, []);
153297
+ const documentColors = React.useMemo(() => {
153298
+ if (!canvas2) return [];
153299
+ const allObjs = canvas2.getObjects ? canvas2.getObjects() : [];
153300
+ return extractAllTargetColors(allObjs);
153301
+ }, [canvas2, objects]);
153302
+ const applyColor = React.useCallback(
153303
+ (newHex, cmykVal, opacityVal = alpha) => {
153304
+ const resolvedCmyk = cmykVal || hexToCmyk(newHex);
153305
+ const oldTargetColor = currentColor;
153306
+ setCurrentColor(newHex);
153307
+ setCurrentCmyk(resolvedCmyk);
153308
+ if (targets.length > 0) {
153309
+ applyFillToFabricObjects(targets, newHex, opacityVal, oldTargetColor, canvas2, resolvedCmyk);
153310
+ setActiveColor(newHex, activeColorIndex);
153311
+ } else {
153312
+ addBackgroundColor(newHex);
153313
+ }
153314
+ saveState();
153315
+ },
153316
+ [targets, canvas2, alpha, currentColor, activeColorIndex, setActiveColor, saveState]
153317
+ );
153318
+ const updateFromHsv = React.useCallback(
153319
+ (newHsv) => {
153320
+ const rgb2 = hsvToRgb(newHsv.h, newHsv.s, newHsv.v);
153321
+ const newC = rgbToCmyk(rgb2.r, rgb2.g, rgb2.b);
153322
+ const hex = rgbToHex(rgb2.r, rgb2.g, rgb2.b);
153323
+ setHsv(newHsv);
153324
+ setCurrentCmyk(newC);
153325
+ applyColor(hex, newC, alpha);
153326
+ },
153327
+ [alpha, applyColor]
153328
+ );
153329
+ const updateFromCmyk = React.useCallback(
153330
+ (newCmyk) => {
153331
+ const rgb2 = cmykToRgb(newCmyk.c, newCmyk.m, newCmyk.y, newCmyk.k);
153332
+ const hex = rgbToHex(rgb2.r, rgb2.g, rgb2.b);
153333
+ setCurrentCmyk(newCmyk);
153334
+ setHsv(rgbToHsv(rgb2.r, rgb2.g, rgb2.b));
153335
+ applyColor(hex, newCmyk, alpha);
153336
+ },
153337
+ [alpha, applyColor]
153338
+ );
153339
+ const updateFromHex = React.useCallback(
153340
+ (inputHex) => {
153341
+ let clean = inputHex.trim().replace(/^#/, "");
153342
+ if (clean.length === 3) clean = clean[0] + clean[0] + clean[1] + clean[1] + clean[2] + clean[2];
153343
+ if (/^[0-9a-fA-F]{6}$/.test(clean)) {
153344
+ const hex = `#${clean.toLowerCase()}`;
153345
+ const rgb2 = hexToRgb(hex);
153346
+ const newC = rgbToCmyk(rgb2.r, rgb2.g, rgb2.b);
153347
+ setHsv(rgbToHsv(rgb2.r, rgb2.g, rgb2.b));
153348
+ setCurrentCmyk(newC);
153349
+ applyColor(hex, newC, alpha);
153350
+ }
153351
+ },
153352
+ [alpha, applyColor]
153353
+ );
153354
+ const handleSelectObjectColor = (hex, idx2) => {
153355
+ setActiveColor(hex, idx2);
153356
+ setCurrentColor(hex);
153357
+ const c2 = hexToCmyk(hex);
153358
+ setCurrentCmyk(c2);
153359
+ const rgb2 = hexToRgb(hex);
153360
+ setHsv(rgbToHsv(rgb2.r, rgb2.g, rgb2.b));
153361
+ };
153362
+ const handleCopyHex = () => {
153363
+ navigator.clipboard.writeText(currentColor);
153364
+ setCopied(true);
153365
+ setTimeout(() => setCopied(false), 1500);
153366
+ };
153367
+ const handleEyeDropper = async () => {
153368
+ if (typeof window !== "undefined" && "EyeDropper" in window) {
153369
+ try {
153370
+ const eyeDropper = new window.EyeDropper();
153371
+ const result = await eyeDropper.open();
153372
+ if (result?.sRGBHex) {
153373
+ updateFromHex(result.sRGBHex);
153374
+ }
153375
+ } catch (err) {
153376
+ console.error("EyeDropper error:", err);
153377
+ }
153378
+ }
153379
+ };
153380
+ const groupedDbColors = React.useMemo(() => {
153381
+ const map = /* @__PURE__ */ new Map();
153382
+ dbColors.forEach((c2) => {
153383
+ const group = c2.colorGroup || "Custom Palettes";
153384
+ if (!map.has(group)) map.set(group, []);
153385
+ map.get(group).push(c2);
153386
+ });
153387
+ return map;
153388
+ }, [dbColors]);
153389
+ const hueRgb = hsvToRgb(hsv.h, 100, 100);
153390
+ const hueColor = rgbToHex(hueRgb.r, hueRgb.g, hueRgb.b);
153391
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full h-full bg-neutral-900 border-r border-neutral-800 flex flex-col select-none text-white text-xs", children: [
153392
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-2 flex flex-col gap-3 shrink-0", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
153393
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
153394
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-2 h-2 rounded-full bg-[#02bc71] animate-pulse" }),
153395
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "font-semibold text-sm text-neutral-100", children: "Color Palette" })
153396
+ ] }),
153397
+ /* @__PURE__ */ jsxRuntime.jsx(
153398
+ "button",
153399
+ {
153400
+ onClick: () => setActivePanel(null),
153401
+ className: "p-1 rounded-md text-neutral-400 hover:text-white hover:bg-neutral-800 transition-colors cursor-pointer",
153402
+ title: "Close Panel",
153403
+ children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 6 6 18M6 6l12 12" }) })
153404
+ }
153405
+ )
153406
+ ] }) }),
153407
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 overflow-y-auto space-y-5 p-4 pr-3 custom-scrollbar", children: [
153408
+ objectColors.length > 1 && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
153409
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-2", children: [
153410
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[11px] font-bold text-neutral-400 uppercase tracking-wider", children: [
153411
+ "Object Colors (",
153412
+ objectColors.length,
153413
+ ")"
153414
+ ] }),
153415
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] text-neutral-500", children: "Select to customize" })
153416
+ ] }),
153417
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap items-center gap-2 p-2.5 bg-neutral-950 border border-neutral-800 rounded-xl", children: objectColors.map((hex, idx2) => {
153418
+ const isSelected = currentColor.toLowerCase() === hex.toLowerCase();
153419
+ return /* @__PURE__ */ jsxRuntime.jsx(
153420
+ "button",
153421
+ {
153422
+ type: "button",
153423
+ onClick: () => handleSelectObjectColor(hex, idx2),
153424
+ className: `w-8 h-8 rounded-lg border transition-all cursor-pointer hover:scale-105 shadow-xs relative ${isSelected ? "border-white ring-2 ring-[#02bc71] scale-105 z-10" : "border-neutral-700 hover:border-neutral-500"}`,
153425
+ style: { backgroundColor: hex },
153426
+ title: `Object color ${idx2 + 1}: ${hex.toUpperCase()}`,
153427
+ children: isSelected && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute inset-0 flex items-center justify-center text-xs font-bold mix-blend-difference text-white", children: "✓" })
153428
+ },
153429
+ hex + "-" + idx2
153430
+ );
153431
+ }) })
153432
+ ] }),
153433
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-3.5 bg-neutral-950 border border-neutral-800 rounded-2xl shadow-xl space-y-3", children: [
153434
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between pb-1", children: [
153435
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
153436
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-bold text-neutral-300", children: "Custom Color" }),
153437
+ /* @__PURE__ */ jsxRuntime.jsx(
153438
+ "span",
153439
+ {
153440
+ className: "w-3.5 h-3.5 rounded-full border border-neutral-700 shadow-inner",
153441
+ style: { backgroundColor: currentColor }
153442
+ }
153443
+ )
153444
+ ] }),
153445
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex bg-neutral-900 rounded-lg p-0.5 border border-neutral-800", children: [
153446
+ /* @__PURE__ */ jsxRuntime.jsx(
153447
+ "button",
153448
+ {
153449
+ type: "button",
153450
+ onClick: () => setColorMode("HEX"),
153451
+ className: `px-2 py-0.5 rounded text-[10px] font-bold transition-colors cursor-pointer ${colorMode === "HEX" ? "bg-[#02bc71] text-white" : "text-neutral-400 hover:text-white"}`,
153452
+ children: "HEX"
153453
+ }
153454
+ ),
153455
+ /* @__PURE__ */ jsxRuntime.jsx(
153456
+ "button",
153457
+ {
153458
+ type: "button",
153459
+ onClick: () => setColorMode("CMYK"),
153460
+ className: `px-2 py-0.5 rounded text-[10px] font-bold transition-colors cursor-pointer ${colorMode === "CMYK" ? "bg-[#02bc71] text-white" : "text-neutral-400 hover:text-white"}`,
153461
+ children: "CMYK"
153462
+ }
153463
+ )
153464
+ ] })
153465
+ ] }),
153466
+ /* @__PURE__ */ jsxRuntime.jsx(
153467
+ "div",
153468
+ {
153469
+ ref: svRef,
153470
+ className: "relative w-full h-36 rounded-xl overflow-hidden cursor-crosshair select-none border border-neutral-800 touch-none",
153471
+ style: {
153472
+ background: `linear-gradient(to top, #000, transparent), linear-gradient(to right, #fff, ${hueColor})`
153473
+ },
153474
+ onPointerDown: (e2) => {
153475
+ e2.currentTarget.setPointerCapture(e2.pointerId);
153476
+ const rect = e2.currentTarget.getBoundingClientRect();
153477
+ updateFromHsv({
153478
+ ...hsv,
153479
+ s: Math.round(Math.max(0, Math.min(1, (e2.clientX - rect.left) / rect.width)) * 100),
153480
+ v: Math.round((1 - Math.max(0, Math.min(1, (e2.clientY - rect.top) / rect.height))) * 100)
153481
+ });
153482
+ },
153483
+ onPointerMove: (e2) => {
153484
+ if (e2.buttons === 0) return;
153485
+ const rect = e2.currentTarget.getBoundingClientRect();
153486
+ updateFromHsv({
153487
+ ...hsv,
153488
+ s: Math.round(Math.max(0, Math.min(1, (e2.clientX - rect.left) / rect.width)) * 100),
153489
+ v: Math.round((1 - Math.max(0, Math.min(1, (e2.clientY - rect.top) / rect.height))) * 100)
153490
+ });
153491
+ },
153492
+ children: /* @__PURE__ */ jsxRuntime.jsx(
153493
+ "div",
153494
+ {
153495
+ className: "absolute w-4 h-4 rounded-full border-2 border-white shadow-md -translate-x-1/2 -translate-y-1/2 pointer-events-none ring-1 ring-black/40",
153496
+ style: {
153497
+ left: `${hsv.s}%`,
153498
+ top: `${100 - hsv.v}%`,
153499
+ backgroundColor: currentColor
153500
+ }
153501
+ }
153502
+ )
153503
+ }
153504
+ ),
153505
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
153506
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between text-[10px] text-neutral-400 font-medium", children: [
153507
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Hue" }),
153508
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
153509
+ Math.round(hsv.h),
153510
+ "°"
153511
+ ] })
153512
+ ] }),
153513
+ /* @__PURE__ */ jsxRuntime.jsx(
153514
+ "div",
153515
+ {
153516
+ ref: hueRef,
153517
+ className: "relative h-4 rounded-lg cursor-pointer select-none border border-neutral-800 touch-none",
153518
+ style: {
153519
+ background: "linear-gradient(to right, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%)"
153520
+ },
153521
+ onPointerDown: (e2) => {
153522
+ e2.currentTarget.setPointerCapture(e2.pointerId);
153523
+ const rect = e2.currentTarget.getBoundingClientRect();
153524
+ updateFromHsv({
153525
+ ...hsv,
153526
+ h: Math.round(Math.max(0, Math.min(1, (e2.clientX - rect.left) / rect.width)) * 360) % 360
153527
+ });
153528
+ },
153529
+ onPointerMove: (e2) => {
153530
+ if (e2.buttons === 0) return;
153531
+ const rect = e2.currentTarget.getBoundingClientRect();
153532
+ updateFromHsv({
153533
+ ...hsv,
153534
+ h: Math.round(Math.max(0, Math.min(1, (e2.clientX - rect.left) / rect.width)) * 360) % 360
153535
+ });
153536
+ },
153537
+ children: /* @__PURE__ */ jsxRuntime.jsx(
153538
+ "div",
153539
+ {
153540
+ className: "absolute top-1/2 w-4 h-4 -translate-y-1/2 -translate-x-1/2 rounded-full border-2 border-white shadow-md pointer-events-none ring-1 ring-black/30",
153541
+ style: { left: `${hsv.h / 360 * 100}%`, backgroundColor: hueColor }
153542
+ }
153543
+ )
153544
+ }
153545
+ )
153546
+ ] }),
153547
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
153548
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between text-[10px] text-neutral-400 font-medium", children: [
153549
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Opacity" }),
153550
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
153551
+ alpha,
153552
+ "%"
153553
+ ] })
153554
+ ] }),
153555
+ /* @__PURE__ */ jsxRuntime.jsxs(
153556
+ "div",
153557
+ {
153558
+ ref: alphaRef,
153559
+ className: "relative h-4 rounded-lg cursor-pointer select-none border border-neutral-800 overflow-hidden touch-none pqc-checkerboard",
153560
+ onPointerDown: (e2) => {
153561
+ e2.currentTarget.setPointerCapture(e2.pointerId);
153562
+ const rect = e2.currentTarget.getBoundingClientRect();
153563
+ const newAlpha = Math.round(Math.max(0, Math.min(1, (e2.clientX - rect.left) / rect.width)) * 100);
153564
+ setAlpha(newAlpha);
153565
+ applyColor(currentColor, currentCmyk, newAlpha);
153566
+ },
153567
+ onPointerMove: (e2) => {
153568
+ if (e2.buttons === 0) return;
153569
+ const rect = e2.currentTarget.getBoundingClientRect();
153570
+ const newAlpha = Math.round(Math.max(0, Math.min(1, (e2.clientX - rect.left) / rect.width)) * 100);
153571
+ setAlpha(newAlpha);
153572
+ applyColor(currentColor, currentCmyk, newAlpha);
153573
+ },
153574
+ children: [
153575
+ /* @__PURE__ */ jsxRuntime.jsx(
153576
+ "div",
153577
+ {
153578
+ className: "absolute inset-0",
153579
+ style: {
153580
+ background: `linear-gradient(to right, transparent, ${currentColor})`
153581
+ }
153582
+ }
153583
+ ),
153584
+ /* @__PURE__ */ jsxRuntime.jsx(
153585
+ "div",
153586
+ {
153587
+ className: "absolute top-1/2 w-4 h-4 -translate-y-1/2 -translate-x-1/2 rounded-full border-2 border-white shadow-md bg-white pointer-events-none ring-1 ring-black/30",
153588
+ style: { left: `${alpha}%` }
153589
+ }
153590
+ )
153591
+ ]
153592
+ }
153593
+ )
153594
+ ] }),
153595
+ colorMode === "HEX" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2 pt-1", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
153596
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex-1", children: [
153597
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute left-2.5 top-2 text-xs font-mono text-neutral-500", children: "#" }),
153598
+ /* @__PURE__ */ jsxRuntime.jsx(
153599
+ "input",
153600
+ {
153601
+ type: "text",
153602
+ maxLength: 7,
153603
+ value: currentColor.replace("#", "").toUpperCase(),
153604
+ onChange: (e2) => {
153605
+ const val = e2.target.value.replace(/[^0-9A-Fa-f]/g, "");
153606
+ if (val.length <= 6) {
153607
+ const newHex = `#${val}`;
153608
+ setCurrentColor(newHex);
153609
+ if (val.length === 6) {
153610
+ updateFromHex(newHex);
153611
+ }
153612
+ }
153613
+ },
153614
+ className: "w-full bg-neutral-900 border border-neutral-800 rounded-lg pl-6 pr-2 py-1.5 text-xs text-white font-mono uppercase focus:outline-none focus:border-[#02bc71]"
153615
+ }
153616
+ )
153617
+ ] }),
153618
+ /* @__PURE__ */ jsxRuntime.jsx(
153619
+ "button",
153620
+ {
153621
+ type: "button",
153622
+ onClick: handleCopyHex,
153623
+ className: "px-2.5 py-1.5 bg-neutral-900 hover:bg-neutral-800 border border-neutral-800 hover:border-neutral-700 rounded-lg text-[11px] text-neutral-300 font-medium flex items-center gap-1 transition-colors cursor-pointer",
153624
+ title: "Copy Hex Code",
153625
+ children: copied ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[#02bc71]", children: "Copied!" }) : /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Copy" })
153626
+ }
153627
+ ),
153628
+ typeof window !== "undefined" && "EyeDropper" in window && /* @__PURE__ */ jsxRuntime.jsx(
153629
+ "button",
153630
+ {
153631
+ type: "button",
153632
+ onClick: handleEyeDropper,
153633
+ className: "p-1.5 bg-neutral-900 hover:bg-neutral-800 border border-neutral-800 hover:border-neutral-700 rounded-lg text-neutral-300 hover:text-white transition-colors cursor-pointer",
153634
+ title: "Eyedropper Tool",
153635
+ children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
153636
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m14 2 8 8-4 4-8-8 4-4Z" }),
153637
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m18 10-4-4" }),
153638
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m6.5 13.5-3 3c-.8.8-.8 2.2 0 3 .8.8 2.2.8 3 0l3-3" })
153639
+ ] })
153640
+ }
153641
+ )
153642
+ ] }) }),
153643
+ colorMode === "CMYK" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-1.5 pt-1", children: CMYK_CHANNELS.map(({ key, label, color: lColor, name }) => /* @__PURE__ */ jsxRuntime.jsx(
153644
+ ChannelRow,
153645
+ {
153646
+ label,
153647
+ labelColor: lColor,
153648
+ ariaLabel: name,
153649
+ value: currentCmyk[key],
153650
+ min: 0,
153651
+ max: 100,
153652
+ gradient: `linear-gradient(to right, ${cmykToHex(
153653
+ key === "c" ? 0 : currentCmyk.c,
153654
+ key === "m" ? 0 : currentCmyk.m,
153655
+ key === "y" ? 0 : currentCmyk.y,
153656
+ key === "k" ? 0 : currentCmyk.k
153657
+ )}, ${cmykToHex(
153658
+ key === "c" ? 100 : currentCmyk.c,
153659
+ key === "m" ? 100 : currentCmyk.m,
153660
+ key === "y" ? 100 : currentCmyk.y,
153661
+ key === "k" ? 100 : currentCmyk.k
153662
+ )})`,
153663
+ onChange: (val) => updateFromCmyk({ ...currentCmyk, [key]: val })
153664
+ },
153665
+ key
153666
+ )) })
153667
+ ] }),
153668
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
153669
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-2", children: [
153670
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] font-bold text-neutral-400 uppercase tracking-wider", children: "Document Colors" }),
153671
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[10px] text-neutral-500", children: [
153672
+ documentColors.length,
153673
+ " colors"
153674
+ ] })
153675
+ ] }),
153676
+ documentColors.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-3 text-center rounded-xl bg-neutral-900/40 border border-dashed border-neutral-800", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-neutral-500", children: "No colors detected in document yet" }) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap items-center gap-2", children: documentColors.map((hex) => /* @__PURE__ */ jsxRuntime.jsx(
153677
+ "button",
153678
+ {
153679
+ onClick: () => updateFromHex(hex),
153680
+ className: `w-8 h-8 rounded-lg border transition-all cursor-pointer hover:scale-105 shadow-xs relative ${currentColor.toLowerCase() === hex.toLowerCase() ? "border-white ring-2 ring-[#02bc71]" : "border-neutral-700/80"}`,
153681
+ style: { backgroundColor: hex },
153682
+ title: hex.toUpperCase(),
153683
+ children: currentColor.toLowerCase() === hex.toLowerCase() && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute inset-0 flex items-center justify-center text-xs font-bold mix-blend-difference text-white", children: "✓" })
153684
+ },
153685
+ hex
153686
+ )) })
153687
+ ] }),
153688
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
153689
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] font-bold text-neutral-400 uppercase tracking-wider block mb-2", children: "Default Colors" }),
153690
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-6 gap-2", children: DEFAULT_PRESET_COLORS.map((hex) => /* @__PURE__ */ jsxRuntime.jsx(
153691
+ "button",
153692
+ {
153693
+ type: "button",
153694
+ onClick: () => updateFromHex(hex),
153695
+ className: `w-full aspect-square rounded-lg border transition-all cursor-pointer hover:scale-105 shadow-xs relative ${currentColor.toLowerCase() === hex.toLowerCase() ? "border-white ring-2 ring-[#02bc71]" : "border-neutral-800"}`,
153696
+ style: { backgroundColor: hex },
153697
+ title: hex,
153698
+ children: currentColor.toLowerCase() === hex.toLowerCase() && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute inset-0 flex items-center justify-center text-xs font-bold mix-blend-difference text-white", children: "✓" })
153699
+ },
153700
+ hex
153701
+ )) })
153702
+ ] }),
153703
+ isLoadingColors ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center py-4 text-xs text-neutral-500", children: "Loading color library..." }) : Array.from(groupedDbColors.entries()).map(([groupName, colors]) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
153704
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
153705
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] font-bold text-neutral-400 uppercase tracking-wider", children: groupName }),
153706
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] text-neutral-500", children: colors.length })
153707
+ ] }),
153708
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-6 gap-2", children: colors.map((c2) => /* @__PURE__ */ jsxRuntime.jsx(
153709
+ "button",
153710
+ {
153711
+ onClick: () => updateFromHex(c2.hexCode),
153712
+ className: `w-full aspect-square rounded-lg border transition-all cursor-pointer hover:scale-105 shadow-xs relative group ${currentColor.toLowerCase() === c2.hexCode.toLowerCase() ? "border-white ring-2 ring-[#02bc71]" : "border-neutral-800"}`,
153713
+ style: { backgroundColor: c2.hexCode },
153714
+ title: `${c2.colorName} (${c2.hexCode})`,
153715
+ children: currentColor.toLowerCase() === c2.hexCode.toLowerCase() && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute inset-0 flex items-center justify-center text-xs font-bold mix-blend-difference text-white", children: "✓" })
153716
+ },
153717
+ c2._id || c2.hexCode
153718
+ )) })
153719
+ ] }, groupName))
153720
+ ] })
153721
+ ] });
153722
+ }
153723
+ const ColorPickerIcon = () => /* @__PURE__ */ jsxRuntime.jsx(
153724
+ "svg",
153725
+ {
153726
+ xmlns: "http://www.w3.org/2000/svg",
153727
+ viewBox: "0 0 512 512",
153728
+ width: "80",
153729
+ height: "80",
153730
+ fill: "currentColor",
153731
+ "aria-hidden": "true",
153732
+ style: { mixBlendMode: "difference" },
153733
+ children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M430.1 347.9c-6.6-6.1-16.3-7.6-24.6-9-11.5-1.9-15.9-4-22.6-10-14.3-12.7-14.3-31.1 0-43.8l30.3-26.9c46.4-41 46.4-108.2 0-149.2-34.2-30.1-80.1-45-127.8-45-55.7 0-113.9 20.3-158.8 60.1-83.5 73.8-83.5 194.7 0 268.5 41.5 36.7 97.5 55 152.9 55.4h1.7c55.4 0 110-17.9 148.8-52.4 14.4-12.7 12-36.6.1-47.7zM120 216c0-17.7 14.3-32 32-32s32 14.3 32 32-14.3 32-32 32-32-14.3-32-32zm40 126c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32zm64-161c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32zm72 219c-26.5 0-48-21.5-48-48s21.5-48 48-48 48 21.5 48 48-21.5 48-48 48zm24-208c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32z" })
153734
+ }
153735
+ );
152570
153736
  const DEFAULT_SECTIONS = [
152571
153737
  {
152572
153738
  id: "templates",
@@ -152628,6 +153794,13 @@ ${elements2.join("\n")}
152628
153794
  Tab: /* @__PURE__ */ jsxRuntime.jsx(QRCodeIcon, {}),
152629
153795
  Panel: /* @__PURE__ */ jsxRuntime.jsx(BarcodePanel, {})
152630
153796
  },
153797
+ {
153798
+ id: "color",
153799
+ name: "Color",
153800
+ Tab: /* @__PURE__ */ jsxRuntime.jsx(ColorPickerIcon, {}),
153801
+ Panel: /* @__PURE__ */ jsxRuntime.jsx(ColorPanel, {}),
153802
+ visibleInList: false
153803
+ },
152631
153804
  {
152632
153805
  id: "effects",
152633
153806
  name: "Effects",
@@ -152821,6 +153994,18 @@ ${elements2.join("\n")}
152821
153994
  }
152822
153995
  );
152823
153996
  }
153997
+ const STROKE_WIDTH_OPTIONS = [0, 1, 2, 3, 4, 5];
153998
+ const getStrokeDashArray = (style) => {
153999
+ switch (style) {
154000
+ case "Dashed":
154001
+ return [10, 5];
154002
+ case "Dotted":
154003
+ return [2, 4];
154004
+ case "Line":
154005
+ default:
154006
+ return null;
154007
+ }
154008
+ };
152824
154009
  function TextFontSize({
152825
154010
  value,
152826
154011
  onChange
@@ -152862,6 +154047,40 @@ ${elements2.join("\n")}
152862
154047
  const [charSpacing, setCharSpacing] = React.useState(0);
152863
154048
  const [align, setAlign] = React.useState("left");
152864
154049
  const [showTextPopup, setShowTextPopup] = React.useState(false);
154050
+ const [showStrokeControls, setShowStrokeControls] = React.useState(false);
154051
+ const [strokeColor, setStrokeColor] = React.useState("#000000");
154052
+ const [strokeSize, setStrokeSize] = React.useState(0);
154053
+ const [strokeStyle, setStrokeStyle] = React.useState("Line");
154054
+ const strokeControlsRef = React.useRef(null);
154055
+ const textPopupRef = React.useRef(null);
154056
+ const textPopupButtonRef = React.useRef(null);
154057
+ React.useEffect(() => {
154058
+ if (!showStrokeControls) return;
154059
+ const handleClickOutside = (e2) => {
154060
+ const target = e2.target;
154061
+ if (!target) return;
154062
+ if (strokeControlsRef.current?.contains(target)) return;
154063
+ if (target.closest?.('[data-cmyk-popover="true"], .cmyk-picker-popover')) return;
154064
+ setShowStrokeControls(false);
154065
+ };
154066
+ document.addEventListener("mousedown", handleClickOutside);
154067
+ return () => {
154068
+ document.removeEventListener("mousedown", handleClickOutside);
154069
+ };
154070
+ }, [showStrokeControls]);
154071
+ React.useEffect(() => {
154072
+ if (!showTextPopup) return;
154073
+ const handleClickOutside = (e2) => {
154074
+ const target = e2.target;
154075
+ if (!target) return;
154076
+ if (textPopupRef.current?.contains(target) || textPopupButtonRef.current?.contains(target)) return;
154077
+ setShowTextPopup(false);
154078
+ };
154079
+ document.addEventListener("mousedown", handleClickOutside);
154080
+ return () => {
154081
+ document.removeEventListener("mousedown", handleClickOutside);
154082
+ };
154083
+ }, [showTextPopup]);
152865
154084
  const text = React.useMemo(() => {
152866
154085
  if (type !== "textbox") return null;
152867
154086
  return isTextbox(objects[0]) ? objects[0] : null;
@@ -152872,6 +154091,11 @@ ${elements2.join("\n")}
152872
154091
  setLineHeight2((text.lineHeight ?? 1) * 100);
152873
154092
  setCharSpacing(text.charSpacing ?? 0);
152874
154093
  setAlign(text.textAlign ?? "left");
154094
+ const dashArray = text.strokeDashArray;
154095
+ const currentStrokeStyle = Array.isArray(dashArray) && dashArray.length === 2 && dashArray[0] === 2 && dashArray[1] === 4 ? "Dotted" : Array.isArray(dashArray) && dashArray.length === 2 && dashArray[0] === 10 && dashArray[1] === 5 ? "Dashed" : "Line";
154096
+ setStrokeColor(typeof text.stroke === "string" && text.stroke ? text.stroke : "#000000");
154097
+ setStrokeSize(Math.max(0, Number(text.strokeWidth ?? 0)));
154098
+ setStrokeStyle(currentStrokeStyle);
152875
154099
  }, [text]);
152876
154100
  const updateText = React.useCallback(
152877
154101
  (props) => {
@@ -152884,6 +154108,24 @@ ${elements2.join("\n")}
152884
154108
  },
152885
154109
  [text]
152886
154110
  );
154111
+ const applyTextStroke = React.useCallback(
154112
+ (nextColor, nextSize, nextStyle) => {
154113
+ if (!text) return;
154114
+ const canvas2 = text.canvas;
154115
+ if (!canvas2) return;
154116
+ const normalizedSize = Number.isFinite(nextSize) ? Math.min(5, Math.max(0, Math.round(nextSize))) : 0;
154117
+ text.set({
154118
+ stroke: normalizedSize > 0 ? nextColor : null,
154119
+ strokeWidth: normalizedSize,
154120
+ strokeDashArray: normalizedSize === 0 ? null : getStrokeDashArray(nextStyle),
154121
+ strokeUniform: true,
154122
+ paintFirst: "stroke"
154123
+ });
154124
+ canvas2.requestRenderAll();
154125
+ useHistoryStore.getState().saveState();
154126
+ },
154127
+ [text]
154128
+ );
152887
154129
  const applyTextCase = (mode2) => {
152888
154130
  if (!text?.text) return;
152889
154131
  const anyText = text;
@@ -152965,7 +154207,10 @@ ${elements2.join("\n")}
152965
154207
  /* @__PURE__ */ jsxRuntime.jsx(
152966
154208
  "button",
152967
154209
  {
152968
- className: "cursor-pointer p-1 hover:bg-neutral-700 rounded",
154210
+ ref: textPopupButtonRef,
154211
+ type: "button",
154212
+ "aria-expanded": showTextPopup,
154213
+ className: `cursor-pointer rounded p-1 transition-colors duration-150 ${showTextPopup ? "bg-neutral-700 text-white ring-1 ring-neutral-600 shadow-sm" : "hover:bg-neutral-700"}`,
152969
154214
  onClick: () => setShowTextPopup((prev) => !prev),
152970
154215
  children: /* @__PURE__ */ jsxRuntime.jsx(
152971
154216
  "svg",
@@ -152984,10 +154229,10 @@ ${elements2.join("\n")}
152984
154229
  showTextPopup && /* @__PURE__ */ jsxRuntime.jsxs(
152985
154230
  "div",
152986
154231
  {
152987
- className: "fixed md:absolute z-[9999] md:z-50 bg-neutral-900 text-white shadow-xl rounded-lg p-4 top-[112px] md:top-[40px] left-1/2 md:left-auto md:right-0 -translate-x-1/2 md:-translate-x-0",
154232
+ ref: textPopupRef,
154233
+ className: "fixed md:absolute z-[9999] md:z-50 bg-[#1d1d1d] text-white shadow-[0_12px_30px_rgba(0,0,0,0.35)] rounded-lg p-4 top-[112px] md:top-[40px] left-1/2 md:left-auto md:right-0 -translate-x-1/2 md:-translate-x-0 border border-neutral-700",
152988
154234
  style: {
152989
- width: "230px",
152990
- border: "1px solid #333"
154235
+ width: "230px"
152991
154236
  },
152992
154237
  children: [
152993
154238
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between mb-2", children: [
@@ -153076,7 +154321,72 @@ ${elements2.join("\n")}
153076
154321
  },
153077
154322
  children: "Aa"
153078
154323
  }
153079
- )
154324
+ ),
154325
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", ref: strokeControlsRef, children: [
154326
+ /* @__PURE__ */ jsxRuntime.jsx(
154327
+ "button",
154328
+ {
154329
+ type: "button",
154330
+ onClick: () => setShowStrokeControls((prev) => !prev),
154331
+ className: `text-left text-[15px] font-semibold ${text?.fontWeight === "bold" ? "text-blue-400" : ""} transition hover:text-blue-400`,
154332
+ children: "Stroke"
154333
+ }
154334
+ ),
154335
+ showStrokeControls && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute left-0 top-full z-50 mt-2 flex items-center gap-3 rounded-md border border-neutral-700 bg-[#1d1d1d] px-3 py-2 text-white shadow-[0_12px_30px_rgba(0,0,0,0.35)]", children: [
154336
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-[13px] font-medium text-white", children: [
154337
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Color" }),
154338
+ /* @__PURE__ */ jsxRuntime.jsx(
154339
+ CMYKColorPicker,
154340
+ {
154341
+ value: strokeColor,
154342
+ onChange: (hex) => {
154343
+ const nextColor = typeof hex === "string" ? hex : "#000000";
154344
+ setStrokeColor(nextColor);
154345
+ applyTextStroke(nextColor, strokeSize, strokeStyle);
154346
+ }
154347
+ }
154348
+ )
154349
+ ] }),
154350
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex items-center gap-2 text-[13px] font-medium text-white", children: [
154351
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Size" }),
154352
+ /* @__PURE__ */ jsxRuntime.jsx(
154353
+ "select",
154354
+ {
154355
+ value: String(strokeSize),
154356
+ onChange: (e2) => {
154357
+ const nextSize = Math.min(5, Number(e2.target.value));
154358
+ setStrokeSize(nextSize);
154359
+ applyTextStroke(strokeColor, nextSize, strokeStyle);
154360
+ },
154361
+ className: "h-8 min-w-[72px] rounded border border-neutral-600 bg-neutral-800 px-2 text-[13px] text-white outline-none",
154362
+ "aria-label": "Text stroke size",
154363
+ children: STROKE_WIDTH_OPTIONS.map((size) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: String(size), className: "bg-neutral-800 text-white", children: size }, size))
154364
+ }
154365
+ )
154366
+ ] }),
154367
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex items-center gap-2 text-[13px] font-medium text-white", children: [
154368
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Style" }),
154369
+ /* @__PURE__ */ jsxRuntime.jsxs(
154370
+ "select",
154371
+ {
154372
+ value: strokeStyle,
154373
+ onChange: (e2) => {
154374
+ const nextStyle = e2.target.value;
154375
+ setStrokeStyle(nextStyle);
154376
+ applyTextStroke(strokeColor, strokeSize, nextStyle);
154377
+ },
154378
+ className: "h-8 min-w-[90px] rounded border border-neutral-600 bg-neutral-800 px-2 text-[13px] text-white outline-none",
154379
+ "aria-label": "Text stroke style",
154380
+ children: [
154381
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "Dotted", className: "bg-neutral-800 text-white", children: "Dotted" }),
154382
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "Line", className: "bg-neutral-800 text-white", children: "Line" }),
154383
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "Dashed", className: "bg-neutral-800 text-white", children: "Dashed" })
154384
+ ]
154385
+ }
154386
+ )
154387
+ ] })
154388
+ ] })
154389
+ ] })
153080
154390
  ] }) });
153081
154391
  }
153082
154392
  function CropIcon() {
@@ -153194,7 +154504,7 @@ ${elements2.join("\n")}
153194
154504
  stroke: "#2196F3",
153195
154505
  strokeWidth: 2,
153196
154506
  cornerColor: "#ffffff",
153197
- cornerStrokeColor: "#202020",
154507
+ cornerStrokeColor: "#2196F3",
153198
154508
  cornerSize: 12,
153199
154509
  cornerStyle: "circle",
153200
154510
  transparentCorners: false,
@@ -153530,7 +154840,7 @@ ${elements2.join("\n")}
153530
154840
  /* @__PURE__ */ jsxRuntime.jsxs(
153531
154841
  "button",
153532
154842
  {
153533
- className: "hover:text-blue-400 flex",
154843
+ className: "hover:text-emerald-400 flex items-center gap-1.5 cursor-pointer",
153534
154844
  onClick: () => setActivePanel("effects"),
153535
154845
  children: [
153536
154846
  /* @__PURE__ */ jsxRuntime.jsx(EffectIcon, {}),
@@ -153557,7 +154867,7 @@ ${elements2.join("\n")}
153557
154867
  /* @__PURE__ */ jsxRuntime.jsx(
153558
154868
  "button",
153559
154869
  {
153560
- className: "hover:text-blue-400",
154870
+ className: "hover:text-emerald-400 cursor-pointer",
153561
154871
  onClick: () => setActivePanel("animate"),
153562
154872
  children: "Animate"
153563
154873
  }
@@ -153670,7 +154980,7 @@ ${elements2.join("\n")}
153670
154980
  const duplicate = async () => {
153671
154981
  const active = canvas2.getActiveObject();
153672
154982
  if (!active) return;
153673
- if (active.type === "activeselection") {
154983
+ if (active.type?.toLowerCase() === "activeselection") {
153674
154984
  const selection = active;
153675
154985
  const originals = selection.getObjects().filter((o2) => o2.name !== "workarea");
153676
154986
  if (!originals.length) return;
@@ -153797,7 +155107,7 @@ ${elements2.join("\n")}
153797
155107
  const getTargets = () => {
153798
155108
  const active = canvas2.getActiveObject();
153799
155109
  if (!active) return [];
153800
- const objects = active.type === "activeselection" ? active.getObjects() : [active];
155110
+ const objects = active.type?.toLowerCase() === "activeselection" ? active.getObjects() : [active];
153801
155111
  return objects.filter((o2) => o2.name !== "workarea");
153802
155112
  };
153803
155113
  const handleFlip = (flip) => {
@@ -153942,7 +155252,7 @@ ${elements2.join("\n")}
153942
155252
  const getTargets = () => {
153943
155253
  const active = canvas2?.getActiveObject();
153944
155254
  if (!active) return [];
153945
- const objects = active.type === "activeselection" ? active.getObjects() : [active];
155255
+ const objects = active.type?.toLowerCase() === "activeselection" ? active.getObjects() : [active];
153946
155256
  return objects.filter((o2) => o2.name !== "workarea");
153947
155257
  };
153948
155258
  React.useEffect(() => {
@@ -154035,12 +155345,12 @@ ${elements2.join("\n")}
154035
155345
  const getTargets = () => {
154036
155346
  const active = getActive();
154037
155347
  if (!active) return [];
154038
- return active.type === "activeselection" ? active.getObjects() : [active];
155348
+ return active.type?.toLowerCase() === "activeselection" ? active.getObjects() : [active];
154039
155349
  };
154040
155350
  const readOpacityFromSelection = () => {
154041
155351
  const active = getActive();
154042
155352
  if (!active) return 100;
154043
- if (active.type === "activeselection") {
155353
+ if (active.type?.toLowerCase() === "activeselection") {
154044
155354
  const objects = active.getObjects();
154045
155355
  return Math.round((objects[0]?.opacity ?? 1) * 100);
154046
155356
  }
@@ -154160,7 +155470,7 @@ ${elements2.join("\n")}
154160
155470
  const getTargets = () => {
154161
155471
  const active = getActive();
154162
155472
  if (!active) return [];
154163
- return active.type === "activeselection" ? active.getObjects() : [active];
155473
+ return active.type?.toLowerCase() === "activeselection" ? active.getObjects() : [active];
154164
155474
  };
154165
155475
  const getWorkarea2 = () => canvas2.getObjects().find((o2) => o2.name === "workarea");
154166
155476
  const normalizeOrigin = (obj) => {
@@ -154208,7 +155518,7 @@ ${elements2.join("\n")}
154208
155518
  const handleAlign = (align) => {
154209
155519
  const active = getActive();
154210
155520
  if (!active) return;
154211
- if (active.type === "activeselection") {
155521
+ if (active.type?.toLowerCase() === "activeselection") {
154212
155522
  alignInsideSelection(align);
154213
155523
  } else {
154214
155524
  applyAlign(align);
@@ -154375,7 +155685,7 @@ ${elements2.join("\n")}
154375
155685
  const getTargets = () => {
154376
155686
  const active = canvas2?.getActiveObject();
154377
155687
  if (!active) return [];
154378
- const objects = active.type === "activeselection" ? active.getObjects() : [active];
155688
+ const objects = active.type?.toLowerCase() === "activeselection" ? active.getObjects() : [active];
154379
155689
  return objects.filter((o2) => o2.name !== "workarea");
154380
155690
  };
154381
155691
  const deleteObjects = () => {
@@ -154449,7 +155759,7 @@ ${elements2.join("\n")}
154449
155759
  const getTargets = () => {
154450
155760
  const active = canvas2.getActiveObject();
154451
155761
  if (!active) return [];
154452
- const objects = active.type === "activeselection" ? active.getObjects() : [active];
155762
+ const objects = active.type?.toLowerCase() === "activeselection" ? active.getObjects() : [active];
154453
155763
  return objects.filter((o2) => o2.name !== "workarea");
154454
155764
  };
154455
155765
  const handleRotate = (dir, step = 45) => {
@@ -154538,13 +155848,19 @@ ${elements2.join("\n")}
154538
155848
  const handleGroup = () => {
154539
155849
  if (!canvas2) return;
154540
155850
  const active = canvas2.getActiveObject();
154541
- if (!active || active.type !== "activeselection") return;
155851
+ if (!active || active.type?.toLowerCase() !== "activeselection") return;
154542
155852
  const selection = active;
154543
155853
  const objects2 = [...selection.getObjects()];
155854
+ const canvasObjects = canvas2.getObjects();
155855
+ const minZIndex = Math.min(...objects2.map((o2) => canvasObjects.indexOf(o2)));
154544
155856
  canvas2.discardActiveObject();
154545
155857
  objects2.forEach((obj) => canvas2.remove(obj));
154546
155858
  const group = new Ur(objects2);
154547
- canvas2.add(group);
155859
+ if (minZIndex !== -1 && minZIndex < canvas2.getObjects().length) {
155860
+ canvas2.insertAt(minZIndex, group);
155861
+ } else {
155862
+ canvas2.add(group);
155863
+ }
154548
155864
  canvas2.setActiveObject(group);
154549
155865
  canvas2.requestRenderAll();
154550
155866
  useSelectionStore.getState().setSelection([group]);
@@ -154593,6 +155909,14 @@ ${elements2.join("\n")}
154593
155909
  </g>
154594
155910
  </svg>`;
154595
155911
  let rotateImg = null;
155912
+ const SMALL_OBJECT_THRESHOLD = 40;
155913
+ const SMALL_CONTROL_SIZE = 6;
155914
+ const NORMAL_CONTROL_SIZE = 10;
155915
+ const ROTATION_GAP = 6;
155916
+ const isTextObject = (obj) => obj.type === "textbox" || obj.type === "i-text" || obj.type === "text";
155917
+ function normalizeAngle(angle = 0) {
155918
+ return (Math.round(angle) % 360 + 360) % 360;
155919
+ }
154596
155920
  function renderRotateIcon(ctx, left, top, fabricObject) {
154597
155921
  if (!rotateImg) {
154598
155922
  rotateImg = new Image();
@@ -154601,7 +155925,7 @@ ${elements2.join("\n")}
154601
155925
  fabricObject.canvas?.requestRenderAll();
154602
155926
  };
154603
155927
  }
154604
- const size = 24;
155928
+ const size = fabricObject.__rotationControlSize || 24;
154605
155929
  ctx.save();
154606
155930
  ctx.translate(left, top);
154607
155931
  if (rotateImg.complete && rotateImg.naturalHeight !== 0) {
@@ -154609,11 +155933,13 @@ ${elements2.join("\n")}
154609
155933
  }
154610
155934
  ctx.restore();
154611
155935
  }
154612
- function createRotateControl() {
155936
+ function createRotateControl(size = 24, offsetY = -30) {
154613
155937
  return new ai({
154614
155938
  x: 0,
154615
155939
  y: -0.5,
154616
- offsetY: -30,
155940
+ offsetY,
155941
+ sizeX: size,
155942
+ sizeY: size,
154617
155943
  cursorStyle: "crosshair",
154618
155944
  actionHandler: Fa.rotationWithSnapping,
154619
155945
  actionName: "rotate",
@@ -154621,23 +155947,147 @@ ${elements2.join("\n")}
154621
155947
  withConnection: true
154622
155948
  });
154623
155949
  }
155950
+ function renderRotationAngle(ctx, left, top, _styleOverride, fabricObject) {
155951
+ if (!fabricObject.__showDegree) {
155952
+ return;
155953
+ }
155954
+ const angle = normalizeAngle(fabricObject.angle);
155955
+ const label = `${angle}°`;
155956
+ const paddingX = 8;
155957
+ const height = 22;
155958
+ const radius = 6;
155959
+ ctx.save();
155960
+ ctx.font = "bold 12px sans-serif";
155961
+ ctx.textAlign = "center";
155962
+ ctx.textBaseline = "middle";
155963
+ const textWidth = ctx.measureText(label).width;
155964
+ const width = Math.max(34, textWidth + paddingX * 2);
155965
+ const x2 = left - width / 2;
155966
+ const y2 = top - height / 2;
155967
+ ctx.beginPath();
155968
+ ctx.fillStyle = "rgba(15, 23, 42, 0.9)";
155969
+ ctx.strokeStyle = "#02bc71";
155970
+ ctx.lineWidth = 1.5;
155971
+ if (typeof ctx.roundRect === "function") {
155972
+ ctx.roundRect(x2, y2, width, height, radius);
155973
+ } else {
155974
+ ctx.rect(x2, y2, width, height);
155975
+ }
155976
+ ctx.fill();
155977
+ ctx.stroke();
155978
+ ctx.fillStyle = "#ffffff";
155979
+ ctx.fillText(label, left, top + 0.5);
155980
+ ctx.restore();
155981
+ }
155982
+ function createRotationAngleControl() {
155983
+ return new ai({
155984
+ x: -0.5,
155985
+ y: 0,
155986
+ offsetX: -28,
155987
+ offsetY: 0,
155988
+ sizeX: 1,
155989
+ sizeY: 1,
155990
+ cursorStyle: "default",
155991
+ render: renderRotationAngle
155992
+ });
155993
+ }
155994
+ const isImageObject = (obj) => obj.type === "image" || obj.type === "FabricImage" || typeof obj.isType === "function" && (obj.isType("image") || obj.isType("FabricImage"));
154624
155995
  function configureObjectControls(obj) {
155996
+ const zoom = obj.canvas ? obj.canvas.getZoom() : 1;
155997
+ const scaledWidth = obj.getScaledWidth() * zoom;
155998
+ const scaledHeight = obj.getScaledHeight() * zoom;
155999
+ const renderedSize = Math.min(scaledWidth, scaledHeight);
156000
+ const isSmall = renderedSize < SMALL_OBJECT_THRESHOLD;
156001
+ const cornerSize = isSmall ? Math.min(SMALL_CONTROL_SIZE, Math.max(4, renderedSize * 0.3)) : NORMAL_CONTROL_SIZE;
156002
+ const rotationSize = isSmall ? Math.min(16, Math.max(12, renderedSize * 0.4)) : 24;
156003
+ const rotationOffset = -(rotationSize / 2 + ROTATION_GAP);
154625
156004
  obj.set({
154626
156005
  transparentCorners: false,
154627
156006
  cornerColor: "#ffffff",
154628
156007
  cornerStrokeColor: "#02bc71",
154629
156008
  borderColor: "#02bc71",
154630
- cornerSize: 10,
156009
+ cornerSize,
154631
156010
  cornerStyle: "rect",
154632
156011
  borderScaleFactor: 1
154633
156012
  });
154634
- obj.controls.mtr = createRotateControl();
156013
+ obj.__rotationControlSize = rotationSize;
156014
+ obj.controls.mtr = createRotateControl(rotationSize, rotationOffset);
156015
+ const textObject = isTextObject(obj);
156016
+ if (!obj.controls.mb) {
156017
+ obj.controls.mb = new ai({
156018
+ x: 0,
156019
+ y: 0.5,
156020
+ cursorStyleHandler: Fa.scaleSkewCursorStyleHandler,
156021
+ actionHandler: textObject ? Fa.changeWidth : Fa.scalingYOrSkewingX,
156022
+ actionName: "resizing"
156023
+ });
156024
+ }
156025
+ const rotationControl = createRotationAngleControl();
156026
+ obj.controls.rotationAngle = rotationControl;
156027
+ delete obj.controls.bmc;
156028
+ delete obj.controls.textAngle;
156029
+ if (isSmall) {
156030
+ obj.setControlsVisibility({
156031
+ mtr: true,
156032
+ br: true,
156033
+ tl: false,
156034
+ tr: false,
156035
+ bl: false,
156036
+ mb: false,
156037
+ ml: false,
156038
+ mr: false,
156039
+ mt: false,
156040
+ bmc: false,
156041
+ rotationAngle: true
156042
+ });
156043
+ return;
156044
+ }
156045
+ if (isImageObject(obj)) {
156046
+ obj.setControlsVisibility({
156047
+ mtr: true,
156048
+ tl: true,
156049
+ tr: true,
156050
+ bl: true,
156051
+ br: true,
156052
+ ml: false,
156053
+ mr: false,
156054
+ mb: true,
156055
+ mt: false,
156056
+ bmc: false,
156057
+ rotationAngle: true
156058
+ });
156059
+ return;
156060
+ }
156061
+ if (!obj.controls.ml) {
156062
+ obj.controls.ml = new ai({
156063
+ x: -0.5,
156064
+ y: 0,
156065
+ cursorStyleHandler: Fa.scaleSkewCursorStyleHandler,
156066
+ actionHandler: textObject ? Fa.changeWidth : Fa.scalingXOrSkewingY,
156067
+ actionName: "resizing"
156068
+ });
156069
+ }
156070
+ if (!obj.controls.mr) {
156071
+ obj.controls.mr = new ai({
156072
+ x: 0.5,
156073
+ y: 0,
156074
+ cursorStyleHandler: Fa.scaleSkewCursorStyleHandler,
156075
+ actionHandler: textObject ? Fa.changeWidth : Fa.scalingXOrSkewingY,
156076
+ actionName: "resizing"
156077
+ });
156078
+ }
154635
156079
  obj.setControlsVisibility({
154636
156080
  mtr: true,
154637
- ml: false,
154638
- mr: false,
156081
+ tl: true,
156082
+ tr: true,
156083
+ bl: true,
156084
+ br: true,
156085
+ ml: true,
156086
+ mr: true,
156087
+ mb: true,
154639
156088
  mt: false,
154640
- mb: false
156089
+ bmc: false,
156090
+ rotationAngle: true
154641
156091
  });
154642
156092
  }
154643
156093
  function initializeRotateImage() {
@@ -154654,20 +156104,38 @@ ${elements2.join("\n")}
154654
156104
  const handleUngroup = () => {
154655
156105
  if (!canvas2) return;
154656
156106
  const active = canvas2.getActiveObject();
154657
- if (!active || active.type !== "group") return;
156107
+ if (!active || active.type?.toLowerCase() !== "group") return;
154658
156108
  const group = active;
154659
- const objects2 = [...group.getObjects()];
154660
- group.remove(...objects2);
156109
+ const groupIndex = canvas2.getObjects().indexOf(group);
156110
+ const childObjects = [...group.getObjects()];
156111
+ canvas2.discardActiveObject();
156112
+ group.remove(...childObjects);
154661
156113
  canvas2.remove(group);
154662
- objects2.forEach((obj) => canvas2.add(obj));
154663
- const sel = new Qo(objects2, { canvas: canvas2 });
154664
- configureObjectControls(sel);
154665
- canvas2.setActiveObject(sel);
154666
- canvas2.renderAll();
154667
- useSelectionStore.getState().setSelection(objects2);
156114
+ if (typeof group.dispose === "function") {
156115
+ group.dispose();
156116
+ }
156117
+ childObjects.forEach((obj, idx2) => {
156118
+ obj.set({
156119
+ selectable: true,
156120
+ evented: true,
156121
+ hasControls: true,
156122
+ hasBorders: true
156123
+ });
156124
+ obj.setCoords();
156125
+ if (groupIndex !== -1) {
156126
+ canvas2.insertAt(groupIndex + idx2, obj);
156127
+ } else {
156128
+ canvas2.add(obj);
156129
+ }
156130
+ configureObjectControls(obj);
156131
+ });
156132
+ canvas2.discardActiveObject();
156133
+ useSelectionStore.getState().clearSelection();
156134
+ canvas2.requestRenderAll();
154668
156135
  saveState();
154669
156136
  };
154670
- if (selectionType !== "multiple" || objects.length !== 1) return null;
156137
+ const isGroupSelected = selectionType === "group" || objects.length === 1 && objects[0]?.type?.toLowerCase() === "group" && objects[0]?.name !== "qr" && objects[0]?.name !== "barcode";
156138
+ if (!isGroupSelected) return null;
154671
156139
  return /* @__PURE__ */ jsxRuntime.jsx(
154672
156140
  "button",
154673
156141
  {
@@ -155013,6 +156481,9 @@ ${elements2.join("\n")}
155013
156481
  initialZoom = 1;
155014
156482
  }
155015
156483
  const initialScale = baseScale * initialZoom;
156484
+ const isTextTemplate = productData?.isTextTemplate || productData?.hideGuidelines || typeof window !== "undefined" && ["text", "texttemplate", "text-template"].includes(
156485
+ (new URLSearchParams(window.location.search).get("designType") || new URLSearchParams(window.location.search).get("designtype") || new URLSearchParams(window.location.search).get("type") || "")?.toLowerCase()
156486
+ );
155016
156487
  const workarea = new jr({
155017
156488
  width: fullWidth,
155018
156489
  height: fullHeight,
@@ -155028,6 +156499,8 @@ ${elements2.join("\n")}
155028
156499
  workarea.__printDPI = printDPI;
155029
156500
  workarea.__baseScale = baseScale;
155030
156501
  workarea.__initialZoom = initialZoom;
156502
+ workarea.__hideGuidelines = isTextTemplate || productData?.hideGuidelines;
156503
+ workarea.__isTextTemplate = isTextTemplate;
155031
156504
  workarea.__bleedArea = productData?.bleedArea ?? 0.125;
155032
156505
  workarea.__safetyArea = productData?.safetyArea ?? 0.125;
155033
156506
  workarea.__trimArea = productData?.trimArea ?? 0;
@@ -155075,27 +156548,63 @@ ${elements2.join("\n")}
155075
156548
  clearSelection();
155076
156549
  return;
155077
156550
  }
155078
- const objects = active.type === "activeselection" ? active.getObjects() : [active];
156551
+ const isMulti = active.type?.toLowerCase() === "activeselection";
156552
+ const objects = isMulti ? active.getObjects() : [active];
155079
156553
  setSelection(objects);
155080
156554
  };
155081
- const configureSelectionControls = (e2) => {
156555
+ const configureSelectionControls = () => {
155082
156556
  const activeObject = canvas2.getActiveObject();
155083
156557
  if (!activeObject) return;
155084
156558
  configureObjectControls(activeObject);
155085
156559
  canvas2.requestRenderAll();
155086
156560
  };
155087
156561
  canvas2.on("selection:created", (e2) => {
156562
+ if (e2.selected) {
156563
+ e2.selected.forEach((o2) => {
156564
+ o2.__showDegree = false;
156565
+ });
156566
+ }
155088
156567
  updateSelection();
155089
156568
  configureSelectionControls();
155090
156569
  });
155091
156570
  canvas2.on("selection:updated", (e2) => {
156571
+ if (e2.selected) {
156572
+ e2.selected.forEach((o2) => {
156573
+ o2.__showDegree = false;
156574
+ });
156575
+ }
155092
156576
  updateSelection();
155093
156577
  configureSelectionControls();
155094
156578
  });
155095
- canvas2.on("selection:cleared", clearSelection);
156579
+ canvas2.on("object:scaling", configureSelectionControls);
156580
+ canvas2.on("object:rotating", (e2) => {
156581
+ if (e2.target) {
156582
+ e2.target.__showDegree = true;
156583
+ }
156584
+ configureSelectionControls();
156585
+ });
156586
+ const hideDegree = (e2) => {
156587
+ if (e2?.target) {
156588
+ e2.target.__showDegree = false;
156589
+ }
156590
+ canvas2.getObjects().forEach((o2) => {
156591
+ o2.__showDegree = false;
156592
+ });
156593
+ configureSelectionControls();
156594
+ };
156595
+ canvas2.on("object:modified", hideDegree);
156596
+ canvas2.on("mouse:up", hideDegree);
156597
+ canvas2.on("selection:cleared", () => {
156598
+ hideDegree();
156599
+ clearSelection();
156600
+ });
155096
156601
  return () => {
155097
156602
  canvas2.off("selection:created");
155098
156603
  canvas2.off("selection:updated");
156604
+ canvas2.off("object:scaling", configureSelectionControls);
156605
+ canvas2.off("object:rotating");
156606
+ canvas2.off("object:modified", hideDegree);
156607
+ canvas2.off("mouse:up", hideDegree);
155099
156608
  canvas2.off("selection:cleared");
155100
156609
  };
155101
156610
  }
@@ -155140,6 +156649,23 @@ ${elements2.join("\n")}
155140
156649
  canvas2.off("object:rotating", syncBorder);
155141
156650
  };
155142
156651
  }
156652
+ const CUSTOM_PROPERTIES = [
156653
+ "name",
156654
+ "id",
156655
+ "selectable",
156656
+ "evented",
156657
+ "hasControls",
156658
+ "lockMovementX",
156659
+ "lockMovementY",
156660
+ "__printWidth",
156661
+ "__printHeight",
156662
+ "__printDPI",
156663
+ "__baseScale",
156664
+ "__initialZoom",
156665
+ "__borderRect",
156666
+ "__rotationControlSize",
156667
+ "__showDegree"
156668
+ ];
155143
156669
  async function switchCanvasPage(canvas2, currentPage, targetPage, pageJSON, clearSelection, containerRef) {
155144
156670
  if (targetPage === currentPage) return;
155145
156671
  const historyStore = useHistoryStore.getState();
@@ -155148,8 +156674,7 @@ ${elements2.join("\n")}
155148
156674
  const storePages = useEditorStore.getState().pages || {};
155149
156675
  if (storePages.front && !pageJSON.front) pageJSON.front = storePages.front;
155150
156676
  if (storePages.back && !pageJSON.back) pageJSON.back = storePages.back;
155151
- const currentObject = canvas2.toObject(["name"]);
155152
- pageJSON[currentPage] = currentObject;
156677
+ pageJSON[currentPage] = canvas2.toObject(CUSTOM_PROPERTIES);
155153
156678
  useEditorStore.getState().setPages({ ...pageJSON });
155154
156679
  clearSelection();
155155
156680
  historyStore.setPage(targetPage);
@@ -155171,6 +156696,16 @@ ${elements2.join("\n")}
155171
156696
  } finally {
155172
156697
  useLoaderStore.getState().stopLoading();
155173
156698
  }
156699
+ const currentVt = canvas2.viewportTransform ? [...canvas2.viewportTransform] : [canvas2.getZoom(), 0, 0, canvas2.getZoom(), 0, 0];
156700
+ canvas2.setViewportTransform(currentVt);
156701
+ const workarea = canvas2.getObjects().find((obj) => obj.name === "workarea");
156702
+ if (workarea) {
156703
+ createOverlays(canvas2, workarea);
156704
+ bringOverlaysToFront(canvas2);
156705
+ }
156706
+ canvas2.requestRenderAll();
156707
+ canvas2.fire("after:render");
156708
+ useHistoryStore.setState({ isRestoring: false });
155174
156709
  }
155175
156710
  const MIN_ZOOM = 0.09;
155176
156711
  const MAX_ZOOM = 5;
@@ -155183,53 +156718,28 @@ ${elements2.join("\n")}
155183
156718
  function getWorkarea(canvas2) {
155184
156719
  return canvas2.getObjects().find((o2) => o2.name === "workarea") ?? null;
155185
156720
  }
155186
- function normalizeViewportPan(canvas2) {
155187
- const vt2 = canvas2.viewportTransform;
155188
- const zoom = vt2[0];
155189
- const panX = vt2[4];
155190
- const panY = vt2[5];
155191
- if (Math.abs(panX) < 0.01 && Math.abs(panY) < 0.01) return;
155192
- const dx = panX / zoom;
155193
- const dy = panY / zoom;
155194
- canvas2.getObjects().forEach((obj) => {
155195
- obj.set({
155196
- left: (obj.left ?? 0) + dx,
155197
- top: (obj.top ?? 0) + dy
155198
- });
155199
- obj.setCoords();
155200
- });
155201
- canvas2.setViewportTransform([zoom, 0, 0, zoom, 0, 0]);
155202
- }
155203
156721
  function doZoom(canvas2, newZoom, pivot) {
155204
156722
  const clamped = clamp(newZoom, MIN_ZOOM, MAX_ZOOM);
155205
156723
  canvas2.zoomToPoint(pivot, clamped);
155206
- normalizeViewportPan(canvas2);
155207
156724
  const workarea = getWorkarea(canvas2);
155208
156725
  if (workarea) createOverlays(canvas2, workarea);
156726
+ const activeObject = canvas2.getActiveObject();
156727
+ if (activeObject) configureObjectControls(activeObject);
155209
156728
  canvas2.requestRenderAll();
155210
156729
  return clamped;
155211
156730
  }
155212
156731
  function doReset(canvas2, setZoom) {
155213
- const center = new ot(canvas2.getWidth() / 2, canvas2.getHeight() / 2);
155214
- canvas2.zoomToPoint(center, 1);
155215
- normalizeViewportPan(canvas2);
155216
156732
  const workarea = getWorkarea(canvas2);
156733
+ const initialZoom = workarea ? workarea.__initialZoom || 1 : 1;
156734
+ const center = new ot(canvas2.getWidth() / 2, canvas2.getHeight() / 2);
156735
+ canvas2.zoomToPoint(center, initialZoom);
155217
156736
  if (workarea) {
155218
- const waW = workarea.getScaledWidth();
155219
- const waH = workarea.getScaledHeight();
155220
- const dx = canvas2.getWidth() / 2 - ((workarea.left ?? 0) + waW / 2);
155221
- const dy = canvas2.getHeight() / 2 - ((workarea.top ?? 0) + waH / 2);
155222
- canvas2.getObjects().forEach((obj) => {
155223
- obj.set({
155224
- left: (obj.left ?? 0) + dx,
155225
- top: (obj.top ?? 0) + dy
155226
- });
155227
- obj.setCoords();
155228
- });
155229
- createOverlays(canvas2, getWorkarea(canvas2));
156737
+ createOverlays(canvas2, workarea);
155230
156738
  }
156739
+ const activeObject = canvas2.getActiveObject();
156740
+ if (activeObject) configureObjectControls(activeObject);
155231
156741
  canvas2.requestRenderAll();
155232
- setZoom(1);
156742
+ setZoom(initialZoom);
155233
156743
  }
155234
156744
  function useCanvasZoom(canvas2) {
155235
156745
  const [zoom, setZoom] = React.useState(1);
@@ -155556,7 +157066,7 @@ ${elements2.join("\n")}
155556
157066
  left: (clipboardRef.current.left ?? 0) + OFFSET,
155557
157067
  top: (clipboardRef.current.top ?? 0) + OFFSET
155558
157068
  });
155559
- if (clonedObj.type === "activeselection") {
157069
+ if (clonedObj.type?.toLowerCase() === "activeselection") {
155560
157070
  clonedObj.canvas = canvas2;
155561
157071
  clonedObj.forEachObject((obj) => {
155562
157072
  canvas2.add(obj);
@@ -155612,8 +157122,11 @@ ${elements2.join("\n")}
155612
157122
  if (fabricRef.current) return;
155613
157123
  let isMounted = true;
155614
157124
  const setupCanvasWithProduct = async () => {
157125
+ const designType = (getQueryParam("designType") || getQueryParam("designtype") || getQueryParam("type"))?.toLowerCase();
157126
+ const isTextDesign = designType === "text";
155615
157127
  const productId = getQueryParam("id") || getQueryParam("productId");
155616
- if (!productId) {
157128
+ const templateId = getQueryParam("templateId") || (isTextDesign ? getQueryParam("id") : null);
157129
+ if (!productId && !isTextDesign) {
155617
157130
  useProductStore.setState({
155618
157131
  isNotFound: true,
155619
157132
  error: "Product ID is missing from URL query parameters (e.g. ?id=...)."
@@ -155622,9 +157135,26 @@ ${elements2.join("\n")}
155622
157135
  }
155623
157136
  useLoaderStore.getState().startLoading();
155624
157137
  try {
155625
- const product = await useProductStore.getState().fetchProduct(productId);
155626
- if (!product || !product._id) {
155627
- return;
157138
+ let product = null;
157139
+ if (isTextDesign) {
157140
+ product = {
157141
+ _id: "text-template-product",
157142
+ bleedArea: 0,
157143
+ trimArea: 0,
157144
+ safetyArea: 0,
157145
+ hideGuidelines: true,
157146
+ isTextTemplate: true,
157147
+ optionsMap: {
157148
+ size: '2" x 2"',
157149
+ productorientation: "Horizontal"
157150
+ }
157151
+ };
157152
+ useProductStore.setState({ productData: product, isNotFound: false, error: null });
157153
+ } else if (productId) {
157154
+ product = await useProductStore.getState().fetchProduct(productId);
157155
+ if (!product || !product._id) {
157156
+ return;
157157
+ }
155628
157158
  }
155629
157159
  if (!isMounted || !canvasRef.current || !containerRef.current || fabricRef.current) return;
155630
157160
  initializeRotateImage();
@@ -155642,11 +157172,20 @@ ${elements2.join("\n")}
155642
157172
  clearSelection
155643
157173
  );
155644
157174
  const cleanupObjectAdded = setupObjectAddedHandler(canvas2);
155645
- const twoSided = product?.optionsMap?.printing === "4/4";
155646
- const templateId = getQueryParam("templateId");
155647
- let frontData = null;
155648
- let backData = null;
155649
- if (templateId) {
157175
+ if (isTextDesign && templateId) {
157176
+ const item = await ShapeService.getTextTemplateById(templateId);
157177
+ if (item?.jsonData) {
157178
+ const parsed = typeof item.jsonData === "string" ? JSON.parse(item.jsonData) : item.jsonData;
157179
+ pageJSON.current.front = parsed;
157180
+ useHistoryStore.setState({ isRestoring: true });
157181
+ await loadPageToCanvas(canvas2, parsed);
157182
+ useHistoryStore.setState({ isRestoring: false });
157183
+ useHistoryStore.getState().saveState();
157184
+ }
157185
+ } else if (templateId) {
157186
+ const twoSided = product?.optionsMap?.printing === "4/4";
157187
+ let frontData = null;
157188
+ let backData = null;
155650
157189
  const res = await TemplateDesignService.getTemplateDesignById(templateId);
155651
157190
  const tpl = res?.data;
155652
157191
  if (tpl?.pages && Array.isArray(tpl.pages)) {
@@ -155655,16 +157194,16 @@ ${elements2.join("\n")}
155655
157194
  if (frontPage?.designData) frontData = frontPage.designData;
155656
157195
  if (backPage?.designData) backData = backPage.designData;
155657
157196
  }
155658
- }
155659
- if (frontData) {
155660
- pageJSON.current.front = frontData;
155661
- useHistoryStore.setState({ isRestoring: true });
155662
- await loadPageToCanvas(canvas2, frontData);
155663
- useHistoryStore.setState({ isRestoring: false });
155664
- useHistoryStore.getState().saveState();
155665
- }
155666
- if (twoSided && backData) {
155667
- pageJSON.current.back = backData;
157197
+ if (frontData) {
157198
+ pageJSON.current.front = frontData;
157199
+ useHistoryStore.setState({ isRestoring: true });
157200
+ await loadPageToCanvas(canvas2, frontData);
157201
+ useHistoryStore.setState({ isRestoring: false });
157202
+ useHistoryStore.getState().saveState();
157203
+ }
157204
+ if (twoSided && backData) {
157205
+ pageJSON.current.back = backData;
157206
+ }
155668
157207
  }
155669
157208
  useEditorStore.getState().setPages({ ...pageJSON.current });
155670
157209
  return () => {