vibrant-tool 0.1.9 → 0.1.10

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.
@@ -1124,11 +1124,25 @@
1124
1124
  back: null
1125
1125
  },
1126
1126
  templateData: null,
1127
+ // Grid state
1128
+ showGrid: false,
1129
+ setShowGrid: (showGrid) => set({ showGrid }),
1130
+ toggleGrid: () => set((state) => ({ showGrid: !state.showGrid })),
1127
1131
  history: [],
1128
1132
  historyIndex: -1,
1129
1133
  // Crop state
1130
1134
  isCropping: false,
1131
- startCropping: () => set({ isCropping: true }),
1135
+ startCropping: () => {
1136
+ const canvas2 = get().canvas;
1137
+ if (canvas2 && canvas2.isDrawingMode) {
1138
+ canvas2.isDrawingMode = false;
1139
+ canvas2.selection = true;
1140
+ canvas2.defaultCursor = "default";
1141
+ canvas2._isCurrentlyDrawing = false;
1142
+ canvas2.requestRenderAll();
1143
+ }
1144
+ set({ isCropping: true });
1145
+ },
1132
1146
  stopCropping: () => set({ isCropping: false }),
1133
1147
  // Canvas setter (NO history here)
1134
1148
  setCanvas: (canvas2) => {
@@ -8443,6 +8457,7 @@
8443
8457
  originX: "left",
8444
8458
  originY: "top",
8445
8459
  fill: overlayColor,
8460
+ opacity: 0.6,
8446
8461
  strokeWidth: 0,
8447
8462
  selectable: false,
8448
8463
  evented: false,
@@ -8494,6 +8509,7 @@
8494
8509
  originX: "left",
8495
8510
  originY: "top",
8496
8511
  fill: overlayColor,
8512
+ opacity: 0.6,
8497
8513
  strokeWidth: 0,
8498
8514
  selectable: false,
8499
8515
  evented: false,
@@ -8790,6 +8806,7 @@
8790
8806
  const overlayColor = canvas2.backgroundColor || "#F3F4F6";
8791
8807
  const makeOverlay = (props) => new jr({
8792
8808
  fill: overlayColor,
8809
+ opacity: 0.6,
8793
8810
  selectable: false,
8794
8811
  evented: false,
8795
8812
  excludeFromExport: true,
@@ -8863,10 +8880,14 @@
8863
8880
  canvas2.requestRenderAll();
8864
8881
  }
8865
8882
  function bringOverlaysToFront(canvas2) {
8883
+ if (!canvas2) return;
8884
+ canvas2.preserveObjectStacking = true;
8885
+ canvas2.controlsAboveOverlay = true;
8866
8886
  const overlays = canvas2.getObjects().filter((o2) => o2.name?.startsWith("overlay-"));
8867
8887
  const guidelines = canvas2.getObjects().filter((o2) => o2.name?.startsWith("guideline-"));
8868
8888
  overlays.forEach((overlay) => canvas2.bringObjectToFront(overlay));
8869
8889
  guidelines.forEach((g2) => canvas2.bringObjectToFront(g2));
8890
+ canvas2.requestRenderAll();
8870
8891
  }
8871
8892
  const BASE_URL = "https://www.api.vibrantprintshop.com";
8872
8893
  const IMAGE_UPLOAD_URL = "https://upload.vibrantprint.com";
@@ -9103,12 +9124,16 @@
9103
9124
  }
9104
9125
  }
9105
9126
  createOverlays(canvas2, workarea, currentSide);
9127
+ if (bgImg) {
9128
+ canvas2.sendObjectToBack(bgImg);
9129
+ }
9106
9130
  canvas2.sendObjectToBack(workarea);
9107
9131
  }
9108
9132
  canvas2.getObjects().forEach((obj) => {
9109
9133
  const isWorkarea = obj.name === "workarea";
9110
9134
  const isOverlayOrGuide = obj.name?.startsWith("overlay-") || obj.name?.startsWith("guideline-");
9111
- if (!isWorkarea && !isOverlayOrGuide) {
9135
+ const isBgImage = obj.name === "bg-image";
9136
+ if (!isWorkarea && !isOverlayOrGuide && !isBgImage) {
9112
9137
  obj.set({
9113
9138
  selectable: true,
9114
9139
  evented: true,
@@ -9246,6 +9271,77 @@
9246
9271
  return "";
9247
9272
  }
9248
9273
  }
9274
+ const useDrawStore = create((set) => ({
9275
+ drawTool: "select",
9276
+ strokeWidth: 5,
9277
+ opacity: 100,
9278
+ color: "#000000",
9279
+ setDrawTool: (tool) => set(() => {
9280
+ if (tool === "highlighter") {
9281
+ return { drawTool: tool, opacity: 50 };
9282
+ }
9283
+ if (tool === "brush") {
9284
+ return { drawTool: tool, opacity: 100 };
9285
+ }
9286
+ return { drawTool: tool };
9287
+ }),
9288
+ setStrokeWidth: (v2) => set({ strokeWidth: v2 }),
9289
+ setOpacity: (v2) => set({ opacity: v2 }),
9290
+ setColor: (v2) => set({ color: v2 })
9291
+ }));
9292
+ function deriveType(objects) {
9293
+ if (objects.length === 0) return "none";
9294
+ if (objects.length > 1) return "multiple";
9295
+ const obj = objects[0];
9296
+ const t2 = obj.type?.toLowerCase();
9297
+ if (t2 === "activeselection") return "multiple";
9298
+ if (t2 === "group") {
9299
+ if (obj.name === "qr") return "qr";
9300
+ if (obj.name === "barcode") return "barcode";
9301
+ return "group";
9302
+ }
9303
+ if (t2 === "textbox" || t2 === "i-text" || t2 === "text") return "textbox";
9304
+ if (t2 === "image") return "image";
9305
+ return t2 || "none";
9306
+ }
9307
+ const useSelectionStore = create((set) => ({
9308
+ type: "none",
9309
+ objects: [],
9310
+ activeColor: null,
9311
+ activeColorIndex: 0,
9312
+ setSelection: (objects) => {
9313
+ if (objects && objects.length > 0) {
9314
+ const drawState = useDrawStore.getState();
9315
+ if (drawState.drawTool !== "select") {
9316
+ drawState.setDrawTool("select");
9317
+ }
9318
+ const canvas2 = useEditorStore.getState().canvas;
9319
+ if (canvas2 && canvas2.isDrawingMode) {
9320
+ canvas2.isDrawingMode = false;
9321
+ canvas2.selection = true;
9322
+ canvas2.defaultCursor = "default";
9323
+ canvas2._isCurrentlyDrawing = false;
9324
+ canvas2.requestRenderAll();
9325
+ }
9326
+ }
9327
+ set({
9328
+ objects,
9329
+ type: deriveType(objects),
9330
+ activeColor: null,
9331
+ activeColorIndex: 0
9332
+ });
9333
+ },
9334
+ setActiveColor: (color, index = 0) => set({
9335
+ activeColor: color,
9336
+ activeColorIndex: index
9337
+ }),
9338
+ clearSelection: () => set({
9339
+ type: "none",
9340
+ objects: [],
9341
+ activeColor: null,
9342
+ activeColorIndex: 0
9343
+ })
9344
+ }));
9249
9345
  const HISTORY_CUSTOM_PROPERTIES = [
9250
9346
  "name",
9251
9347
  "id",
@@ -9256,6 +9352,9 @@
9256
9352
  "lockMovementY",
9257
9353
  "rx",
9258
9354
  "ry",
9355
+ "src",
9356
+ "crossOrigin",
9357
+ "__maskPath",
9259
9358
  "__radiusOfCanvas",
9260
9359
  "__shapeType",
9261
9360
  "__pageSide",
@@ -9266,7 +9365,8 @@
9266
9365
  "__initialZoom",
9267
9366
  "__borderRect",
9268
9367
  "__rotationControlSize",
9269
- "__showDegree"
9368
+ "__showDegree",
9369
+ "textAlign"
9270
9370
  ];
9271
9371
  const useHistoryStore = create((set, get) => ({
9272
9372
  canvas: null,
@@ -9289,11 +9389,12 @@
9289
9389
  setPage: (page) => set({ currentPage: page }),
9290
9390
  saveState: () => {
9291
9391
  const { canvas: canvas2, currentPage, pages, isRestoring } = get();
9292
- if (!canvas2 || isRestoring) return;
9293
- const rawJson = canvas2.toObject(HISTORY_CUSTOM_PROPERTIES);
9392
+ const targetCanvas = canvas2 || useEditorStore.getState().canvas;
9393
+ if (!targetCanvas || isRestoring) return;
9394
+ const rawJson = targetCanvas.toObject(HISTORY_CUSTOM_PROPERTIES);
9294
9395
  const cleanObjects = (rawJson.objects || []).filter((obj) => {
9295
9396
  const name = obj.name || "";
9296
- return name !== "watermark" && !name.startsWith("overlay-") && !name.startsWith("guideline-");
9397
+ return name !== "watermark" && !name.startsWith("overlay-") && !name.startsWith("guideline-") && name !== "canvas-grid";
9297
9398
  });
9298
9399
  const json = { ...rawJson, objects: cleanObjects };
9299
9400
  const history = pages[currentPage]?.history ?? [];
@@ -9313,10 +9414,12 @@
9313
9414
  },
9314
9415
  undo: async () => {
9315
9416
  const { canvas: canvas2, currentPage, pages, isRestoring } = get();
9316
- if (!canvas2 || isRestoring) return;
9417
+ const targetCanvas = canvas2 || useEditorStore.getState().canvas;
9418
+ if (!targetCanvas || isRestoring) return;
9317
9419
  const page = pages[currentPage];
9318
9420
  if (!page || page.history.length <= 1) return;
9319
- canvas2.discardActiveObject();
9421
+ targetCanvas.discardActiveObject();
9422
+ useSelectionStore.getState().clearSelection();
9320
9423
  const current = page.history.at(-1);
9321
9424
  const previous = page.history.at(-2);
9322
9425
  set({
@@ -9330,8 +9433,8 @@
9330
9433
  }
9331
9434
  });
9332
9435
  try {
9333
- await loadPageToCanvas(canvas2, previous, currentPage);
9334
- canvas2.requestRenderAll();
9436
+ await loadPageToCanvas(targetCanvas, previous, currentPage);
9437
+ targetCanvas.requestRenderAll();
9335
9438
  const editorStore = useEditorStore.getState();
9336
9439
  if (editorStore.pages) {
9337
9440
  editorStore.setPages({
@@ -9347,10 +9450,12 @@
9347
9450
  },
9348
9451
  redoAction: async () => {
9349
9452
  const { canvas: canvas2, currentPage, pages, isRestoring } = get();
9350
- if (!canvas2 || isRestoring) return;
9453
+ const targetCanvas = canvas2 || useEditorStore.getState().canvas;
9454
+ if (!targetCanvas || isRestoring) return;
9351
9455
  const page = pages[currentPage];
9352
9456
  if (!page || page.redo.length === 0) return;
9353
- canvas2.discardActiveObject();
9457
+ targetCanvas.discardActiveObject();
9458
+ useSelectionStore.getState().clearSelection();
9354
9459
  const next = page.redo[0];
9355
9460
  set({
9356
9461
  isRestoring: true,
@@ -9363,8 +9468,8 @@
9363
9468
  }
9364
9469
  });
9365
9470
  try {
9366
- await loadPageToCanvas(canvas2, next, currentPage);
9367
- canvas2.requestRenderAll();
9471
+ await loadPageToCanvas(targetCanvas, next, currentPage);
9472
+ targetCanvas.requestRenderAll();
9368
9473
  const editorStore = useEditorStore.getState();
9369
9474
  if (editorStore.pages) {
9370
9475
  editorStore.setPages({
@@ -9664,7 +9769,7 @@
9664
9769
  }
9665
9770
  function exportDesignJSON(canvas2, workarea) {
9666
9771
  const objects = canvas2.getObjects();
9667
- const children = objects.filter((obj) => obj.name !== "workarea" && !obj.name?.startsWith("overlay-")).map((obj) => {
9772
+ const children = objects.filter((obj) => obj.name !== "workarea" && !obj.name?.startsWith("overlay-") && !obj.name?.startsWith("guideline-") && obj.name !== "canvas-grid").map((obj) => {
9668
9773
  const base = obj.toObject();
9669
9774
  return {
9670
9775
  id: obj.id || crypto.randomUUID(),
@@ -9889,6 +9994,7 @@
9889
9994
  });
9890
9995
  canvas2.add(img);
9891
9996
  canvas2.setActiveObject(img);
9997
+ bringOverlaysToFront(canvas2);
9892
9998
  canvas2.requestRenderAll();
9893
9999
  }).catch((err) => {
9894
10000
  console.warn("Fabric failed to load image from URL:", url, err);
@@ -9911,7 +10017,7 @@
9911
10017
  canvas2.renderAll();
9912
10018
  const originalProps = /* @__PURE__ */ new Map();
9913
10019
  objects.forEach((obj) => {
9914
- const isExcluded = obj.excludeFromExport || obj.name?.startsWith("overlay-") || obj.name?.startsWith("guide-") || obj.name?.startsWith("guideline-") || obj.name === "crop-grid";
10020
+ const isExcluded = obj.excludeFromExport || obj.name?.startsWith("overlay-") || obj.name?.startsWith("guide-") || obj.name?.startsWith("guideline-") || obj.name === "canvas-grid" || obj.name === "crop-grid";
9915
10021
  if (isExcluded && obj !== targetWorkarea) {
9916
10022
  originalProps.set(obj, {
9917
10023
  visible: obj.visible,
@@ -102391,7 +102497,23 @@ end`);
102391
102497
  const useSidePanelStore = create(
102392
102498
  (set) => ({
102393
102499
  activePanel: null,
102394
- setActivePanel: (val) => set({ activePanel: val })
102500
+ setActivePanel: (val) => {
102501
+ if (val !== "draw") {
102502
+ const drawState = useDrawStore.getState();
102503
+ if (drawState.drawTool !== "select") {
102504
+ drawState.setDrawTool("select");
102505
+ }
102506
+ const canvas2 = useEditorStore.getState().canvas;
102507
+ if (canvas2 && canvas2.isDrawingMode) {
102508
+ canvas2.isDrawingMode = false;
102509
+ canvas2.selection = true;
102510
+ canvas2.defaultCursor = "default";
102511
+ canvas2._isCurrentlyDrawing = false;
102512
+ canvas2.requestRenderAll();
102513
+ }
102514
+ }
102515
+ set({ activePanel: val });
102516
+ }
102395
102517
  })
102396
102518
  );
102397
102519
  function Panel({ panelName }) {
@@ -102457,43 +102579,6 @@ end`);
102457
102579
  ] })
102458
102580
  ] });
102459
102581
  }
102460
- function deriveType(objects) {
102461
- if (objects.length === 0) return "none";
102462
- if (objects.length > 1) return "multiple";
102463
- const obj = objects[0];
102464
- const t2 = obj.type?.toLowerCase();
102465
- if (t2 === "activeselection") return "multiple";
102466
- if (t2 === "group") {
102467
- if (obj.name === "qr") return "qr";
102468
- if (obj.name === "barcode") return "barcode";
102469
- return "group";
102470
- }
102471
- if (t2 === "textbox" || t2 === "i-text" || t2 === "text") return "textbox";
102472
- if (t2 === "image") return "image";
102473
- return t2 || "none";
102474
- }
102475
- const useSelectionStore = create((set) => ({
102476
- type: "none",
102477
- objects: [],
102478
- activeColor: null,
102479
- activeColorIndex: 0,
102480
- setSelection: (objects) => set({
102481
- objects,
102482
- type: deriveType(objects),
102483
- activeColor: null,
102484
- activeColorIndex: 0
102485
- }),
102486
- setActiveColor: (color, index = 0) => set({
102487
- activeColor: color,
102488
- activeColorIndex: index
102489
- }),
102490
- clearSelection: () => set({
102491
- type: "none",
102492
- objects: [],
102493
- activeColor: null,
102494
- activeColorIndex: 0
102495
- })
102496
- }));
102497
102582
  function TemplatesPanel() {
102498
102583
  const { productData } = useProductStore();
102499
102584
  const { startLoading, stopLoading, isLoading } = useLoaderStore();
@@ -102934,6 +103019,7 @@ end`);
102934
103019
  svg.setCoords();
102935
103020
  canvas2.add(svg);
102936
103021
  canvas2.setActiveObject(svg);
103022
+ bringOverlaysToFront(canvas2);
102937
103023
  useSelectionStore.getState().setSelection([svg]);
102938
103024
  canvas2.requestRenderAll();
102939
103025
  }
@@ -102984,6 +103070,7 @@ end`);
102984
103070
  group.setCoords();
102985
103071
  canvas2.add(group);
102986
103072
  canvas2.setActiveObject(group);
103073
+ bringOverlaysToFront(canvas2);
102987
103074
  useSelectionStore.getState().setSelection([group]);
102988
103075
  canvas2.requestRenderAll();
102989
103076
  return group;
@@ -103388,7 +103475,7 @@ end`);
103388
103475
  if (!workarea) return;
103389
103476
  const workareaCenter = workarea.getCenterPoint();
103390
103477
  const workareaBounds = workarea.getBoundingRect();
103391
- const maxTextWidth = Math.max(40, workareaBounds.width * 0.8);
103478
+ const maxTextWidth = Math.max(40, workareaBounds.width * 0.85);
103392
103479
  const textWidth = Math.min(width, maxTextWidth);
103393
103480
  const text = new Uo(textName, {
103394
103481
  fontSize,
@@ -103401,19 +103488,26 @@ end`);
103401
103488
  strokeWidth: 0,
103402
103489
  hasRotatingPoint: true,
103403
103490
  centerTransform: true,
103404
- padding: 5,
103491
+ padding: 0,
103405
103492
  strokeUniform: true,
103406
103493
  paintFirst: "stroke",
103407
103494
  objectCaching: false
103408
103495
  // Ensure maximum sharpness
103409
103496
  });
103497
+ const measuredWidth = Math.ceil(text.calcTextWidth());
103498
+ if (measuredWidth > 0 && !textName.includes("\n")) {
103499
+ text.set({ width: Math.min(Math.max(measuredWidth + 4, 20), maxTextWidth) });
103500
+ text.initDimensions();
103501
+ }
103410
103502
  canvas2.add(text);
103411
103503
  text.set({
103412
103504
  left: workareaCenter.x - text.getScaledWidth() / 2,
103413
103505
  top: workareaCenter.y - text.getScaledHeight() / 2
103414
103506
  });
103415
103507
  canvas2.setActiveObject(text);
103508
+ bringOverlaysToFront(canvas2);
103416
103509
  canvas2.requestRenderAll();
103510
+ useHistoryStore.getState().saveState();
103417
103511
  }
103418
103512
  async function addTextTemplate(id) {
103419
103513
  try {
@@ -103445,6 +103539,7 @@ end`);
103445
103539
  canvas2.add(...fabricObjects);
103446
103540
  canvas2.renderOnAddRemove = true;
103447
103541
  canvas2.requestRenderAll();
103542
+ useHistoryStore.getState().saveState();
103448
103543
  } catch (err) {
103449
103544
  console.error("Failed to add text template:", err);
103450
103545
  }
@@ -104212,11 +104307,20 @@ end`);
104212
104307
  }) : !isLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-full py-8 text-center text-xs text-gray-500", children: "No images found" }) : null }) })
104213
104308
  ] });
104214
104309
  }
104310
+ let bgSaveTimer = null;
104311
+ function flushBackgroundSave() {
104312
+ if (bgSaveTimer) {
104313
+ clearTimeout(bgSaveTimer);
104314
+ bgSaveTimer = null;
104315
+ useHistoryStore.getState().saveState();
104316
+ }
104317
+ }
104215
104318
  async function addBackgroundImage(url) {
104216
104319
  const canvas2 = useEditorStore.getState().canvas;
104217
104320
  if (!canvas2) return;
104218
104321
  const workarea = canvas2.getObjects().find((o2) => o2.name === "workarea");
104219
104322
  if (!workarea) return;
104323
+ flushBackgroundSave();
104220
104324
  workarea.set("fill", "#ffffff");
104221
104325
  canvas2.getObjects("image").forEach((o2) => {
104222
104326
  if (o2.name === "bg-image") canvas2.remove(o2);
@@ -104228,7 +104332,16 @@ end`);
104228
104332
  const activeSide = workarea.__pageSide || useHistoryStore.getState().currentPage || "front";
104229
104333
  const baseRx = (workarea.rx || workarea.__radiusOfCanvas || 0) * (workarea.scaleX || 1);
104230
104334
  const baseRy = (workarea.ry || workarea.__radiusOfCanvas || 0) * (workarea.scaleY || 1);
104231
- oa.fromURL(url + "?t=vb", { crossOrigin: "anonymous" }).then((img) => {
104335
+ let finalUrl = url;
104336
+ try {
104337
+ const b64 = await getBase64ImageFromURL(url);
104338
+ if (b64 && (b64.startsWith("data:image/png") || b64.startsWith("data:image/jpeg"))) {
104339
+ finalUrl = b64;
104340
+ }
104341
+ } catch {
104342
+ }
104343
+ try {
104344
+ const img = await oa.fromURL(finalUrl, { crossOrigin: "anonymous" });
104232
104345
  const imgW = img.width || 1;
104233
104346
  const imgH = img.height || 1;
104234
104347
  const scale2 = Math.max(waW / imgW, waH / imgH);
@@ -104264,12 +104377,14 @@ end`);
104264
104377
  canvas2.add(img);
104265
104378
  canvas2.sendObjectToBack(img);
104266
104379
  canvas2.sendObjectToBack(workarea);
104380
+ bringOverlaysToFront(canvas2);
104267
104381
  canvas2.requestRenderAll();
104268
- }).catch((err) => {
104382
+ useHistoryStore.getState().saveState();
104383
+ } catch (err) {
104269
104384
  console.warn("Fabric failed to load background image:", url, err);
104270
- });
104385
+ }
104271
104386
  }
104272
- async function addBackgroundColor(color) {
104387
+ async function addBackgroundColor(color, immediate = false) {
104273
104388
  const canvas2 = useEditorStore.getState().canvas;
104274
104389
  if (!canvas2) return;
104275
104390
  const workarea = canvas2.getObjects().find((o2) => o2.name === "workarea");
@@ -104278,7 +104393,20 @@ end`);
104278
104393
  if (o2.name === "bg-image") canvas2.remove(o2);
104279
104394
  });
104280
104395
  workarea.set("fill", color);
104396
+ bringOverlaysToFront(canvas2);
104281
104397
  canvas2.requestRenderAll();
104398
+ if (bgSaveTimer) {
104399
+ clearTimeout(bgSaveTimer);
104400
+ bgSaveTimer = null;
104401
+ }
104402
+ if (immediate) {
104403
+ useHistoryStore.getState().saveState();
104404
+ } else {
104405
+ bgSaveTimer = setTimeout(() => {
104406
+ useHistoryStore.getState().saveState();
104407
+ bgSaveTimer = null;
104408
+ }, 120);
104409
+ }
104282
104410
  }
104283
104411
  function normalizeHexColor(c2) {
104284
104412
  if (!c2 || typeof c2 !== "string" || c2 === "none" || c2 === "transparent" || c2.trim() === "") return null;
@@ -106038,11 +106166,11 @@ end`);
106038
106166
  const [hasMore, setHasMore] = React.useState(true);
106039
106167
  const [page, setPage] = React.useState(1);
106040
106168
  const scrollRef = React.useRef(null);
106041
- const handleApplyBackground = (type, value) => {
106169
+ const handleApplyBackground = async (type, value, immediate = false) => {
106042
106170
  if (type === "color") {
106043
- addBackgroundColor(value);
106171
+ await addBackgroundColor(value, immediate);
106044
106172
  } else {
106045
- addBackgroundImage(value);
106173
+ await addBackgroundImage(value);
106046
106174
  }
106047
106175
  if (window.innerWidth < 768) {
106048
106176
  setActivePanel(null);
@@ -106095,7 +106223,7 @@ end`);
106095
106223
  CMYKColorPicker,
106096
106224
  {
106097
106225
  value: "#ffffff",
106098
- onChange: (hex) => handleApplyBackground("color", hex)
106226
+ onChange: (hex) => handleApplyBackground("color", hex, false)
106099
106227
  }
106100
106228
  ),
106101
106229
  CMYK_SAFE_COLORS.map(({ hex, label }) => /* @__PURE__ */ jsxRuntime.jsx(
@@ -106103,7 +106231,7 @@ end`);
106103
106231
  {
106104
106232
  className: "w-8 h-8 rounded border border-neutral-600 cursor-pointer transition-transform hover:scale-110",
106105
106233
  style: { backgroundColor: hex },
106106
- onClick: () => handleApplyBackground("color", hex),
106234
+ onClick: () => handleApplyBackground("color", hex, true),
106107
106235
  title: label
106108
106236
  },
106109
106237
  hex
@@ -106119,7 +106247,7 @@ end`);
106119
106247
  const image = await ShapeService.convertImages(item.largeImage);
106120
106248
  const imageUrl = image?.success ? image?.url : null;
106121
106249
  if (!imageUrl) return;
106122
- handleApplyBackground("image", imageUrl);
106250
+ await handleApplyBackground("image", imageUrl);
106123
106251
  if (window.innerWidth < 768) {
106124
106252
  useSidePanelStore.getState().setActivePanel(null);
106125
106253
  }
@@ -106215,24 +106343,6 @@ end`);
106215
106343
  )) })
106216
106344
  ] });
106217
106345
  }
106218
- const useDrawStore = create((set) => ({
106219
- drawTool: "select",
106220
- strokeWidth: 5,
106221
- opacity: 100,
106222
- color: "#000000",
106223
- setDrawTool: (tool) => set(() => {
106224
- if (tool === "highlighter") {
106225
- return { drawTool: tool, opacity: 50 };
106226
- }
106227
- if (tool === "brush") {
106228
- return { drawTool: tool, opacity: 100 };
106229
- }
106230
- return { drawTool: tool };
106231
- }),
106232
- setStrokeWidth: (v2) => set({ strokeWidth: v2 }),
106233
- setOpacity: (v2) => set({ opacity: v2 }),
106234
- setColor: (v2) => set({ color: v2 })
106235
- }));
106236
106346
  function DrawPanel() {
106237
106347
  const {
106238
106348
  drawTool,
@@ -106247,7 +106357,7 @@ end`);
106247
106357
  const isDisabled = drawTool === "select";
106248
106358
  const TOOLS = [
106249
106359
  { key: "select", label: "✦ Selection" },
106250
- { key: "brush", label: "🖊 Brush" },
106360
+ { key: "brush", label: "✏️ Brush" },
106251
106361
  { key: "highlighter", label: "✎ Highlighter" }
106252
106362
  ];
106253
106363
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-4 select-none", children: [
@@ -111038,8 +111148,8 @@ END:VCARD`;
111038
111148
  const objWidth = obj.width ?? 1;
111039
111149
  const objHeight = obj.height ?? 1;
111040
111150
  const pathDims = clipPath.getBoundingRect();
111041
- const scaleX = objWidth / pathDims.width;
111042
- const scaleY = objHeight / pathDims.height;
111151
+ const scaleX = objWidth / (clipPath.width || pathDims.width);
111152
+ const scaleY = objHeight / (clipPath.height || pathDims.height);
111043
111153
  clipPath.set({
111044
111154
  scaleX,
111045
111155
  scaleY,
@@ -111048,6 +111158,8 @@ END:VCARD`;
111048
111158
  angle: 0
111049
111159
  // Should not inherit angle since the parent handles rotation
111050
111160
  });
111161
+ clipPath.__maskPath = svgPath;
111162
+ obj.__maskPath = svgPath;
111051
111163
  obj.set({ clipPath });
111052
111164
  obj.setCoords();
111053
111165
  canvas2.requestRenderAll();
@@ -156998,7 +157110,7 @@ ${elements2.join("\n")}
156998
157110
  id: item.id,
156999
157111
  item,
157000
157112
  isActive: activePanel === item.id,
157001
- onClick: () => setActivePanel(item.id)
157113
+ onClick: () => setActivePanel(activePanel === item.id ? null : item.id)
157002
157114
  },
157003
157115
  item.id
157004
157116
  )
@@ -157013,7 +157125,7 @@ ${elements2.join("\n")}
157013
157125
  item,
157014
157126
  variant: "mobile",
157015
157127
  isActive: activePanel === item.id,
157016
- onClick: () => setActivePanel(item.id)
157128
+ onClick: () => setActivePanel(activePanel === item.id ? null : item.id)
157017
157129
  },
157018
157130
  item.id
157019
157131
  )
@@ -157139,20 +157251,48 @@ ${elements2.join("\n")}
157139
157251
  }
157140
157252
  );
157141
157253
  }
157142
- function TextAlignButton({
157254
+ function TextAlignGroup({
157143
157255
  value,
157144
157256
  onChange
157145
157257
  }) {
157146
- const next = value === "left" ? "center" : value === "center" ? "right" : "left";
157258
+ const current = value === "center" || value === "right" ? value : "left";
157147
157259
  return /* @__PURE__ */ jsxRuntime.jsxs(
157148
- "button",
157260
+ "div",
157149
157261
  {
157150
- onClick: () => onChange(next),
157151
- className: "p-1 bg-neutral-800 rounded hover:bg-neutral-700",
157262
+ className: "flex items-center bg-neutral-800 rounded p-0.5 gap-0.5 border border-neutral-700",
157263
+ role: "group",
157264
+ "aria-label": "Text alignment",
157152
157265
  children: [
157153
- value === "left" && /* @__PURE__ */ jsxRuntime.jsx(AlignLeftIcon$1, {}),
157154
- value === "center" && /* @__PURE__ */ jsxRuntime.jsx(AlignCenterIcon$1, {}),
157155
- value === "right" && /* @__PURE__ */ jsxRuntime.jsx(AlignRightIcon$1, {})
157266
+ /* @__PURE__ */ jsxRuntime.jsx(
157267
+ "button",
157268
+ {
157269
+ type: "button",
157270
+ title: "Align Left",
157271
+ onClick: () => onChange("left"),
157272
+ className: `p-1 rounded transition-colors ${current === "left" ? "bg-neutral-600 text-white shadow-sm" : "text-neutral-400 hover:text-white hover:bg-neutral-700"}`,
157273
+ children: /* @__PURE__ */ jsxRuntime.jsx(AlignLeftIcon$1, {})
157274
+ }
157275
+ ),
157276
+ /* @__PURE__ */ jsxRuntime.jsx(
157277
+ "button",
157278
+ {
157279
+ type: "button",
157280
+ title: "Align Center",
157281
+ onClick: () => onChange("center"),
157282
+ className: `p-1 rounded transition-colors ${current === "center" ? "bg-neutral-600 text-white shadow-sm" : "text-neutral-400 hover:text-white hover:bg-neutral-700"}`,
157283
+ children: /* @__PURE__ */ jsxRuntime.jsx(AlignCenterIcon$1, {})
157284
+ }
157285
+ ),
157286
+ /* @__PURE__ */ jsxRuntime.jsx(
157287
+ "button",
157288
+ {
157289
+ type: "button",
157290
+ title: "Align Right",
157291
+ onClick: () => onChange("right"),
157292
+ className: `p-1 rounded transition-colors ${current === "right" ? "bg-neutral-600 text-white shadow-sm" : "text-neutral-400 hover:text-white hover:bg-neutral-700"}`,
157293
+ children: /* @__PURE__ */ jsxRuntime.jsx(AlignRightIcon$1, {})
157294
+ }
157295
+ )
157156
157296
  ]
157157
157297
  }
157158
157298
  );
@@ -157205,15 +157345,29 @@ ${elements2.join("\n")}
157205
157345
  }, [type, objects]);
157206
157346
  React.useEffect(() => {
157207
157347
  if (!text) return;
157208
- setFontSize(text.fontSize ?? 18);
157209
- setLineHeight2((text.lineHeight ?? 1) * 100);
157210
- setCharSpacing(text.charSpacing ?? 0);
157211
- setAlign(text.textAlign ?? "left");
157212
- const dashArray = text.strokeDashArray;
157213
- 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";
157214
- setStrokeColor(typeof text.stroke === "string" && text.stroke ? text.stroke : "#000000");
157215
- setStrokeSize(Math.max(0, Number(text.strokeWidth ?? 0)));
157216
- setStrokeStyle(currentStrokeStyle);
157348
+ const syncProps = () => {
157349
+ setFontSize(text.fontSize ?? 18);
157350
+ setLineHeight2((text.lineHeight ?? 1) * 100);
157351
+ setCharSpacing(text.charSpacing ?? 0);
157352
+ setAlign(text.textAlign ?? "left");
157353
+ const dashArray = text.strokeDashArray;
157354
+ 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";
157355
+ setStrokeColor(typeof text.stroke === "string" && text.stroke ? text.stroke : "#000000");
157356
+ setStrokeSize(Math.max(0, Number(text.strokeWidth ?? 0)));
157357
+ setStrokeStyle(currentStrokeStyle);
157358
+ };
157359
+ syncProps();
157360
+ const canvas2 = text.canvas;
157361
+ if (canvas2) {
157362
+ canvas2.on("object:modified", syncProps);
157363
+ canvas2.on("selection:updated", syncProps);
157364
+ }
157365
+ return () => {
157366
+ if (canvas2) {
157367
+ canvas2.off("object:modified", syncProps);
157368
+ canvas2.off("selection:updated", syncProps);
157369
+ }
157370
+ };
157217
157371
  }, [text]);
157218
157372
  const updateText = React.useCallback(
157219
157373
  (props) => {
@@ -157221,6 +157375,11 @@ ${elements2.join("\n")}
157221
157375
  const canvas2 = text.canvas;
157222
157376
  if (!canvas2) return;
157223
157377
  text.set(props);
157378
+ if (props.textAlign) {
157379
+ text.dirty = true;
157380
+ text.initDimensions?.();
157381
+ text.setCoords();
157382
+ }
157224
157383
  canvas2.requestRenderAll();
157225
157384
  useHistoryStore.getState().saveState();
157226
157385
  },
@@ -157277,7 +157436,7 @@ ${elements2.join("\n")}
157277
157436
  }
157278
157437
  ),
157279
157438
  /* @__PURE__ */ jsxRuntime.jsx(
157280
- TextAlignButton,
157439
+ TextAlignGroup,
157281
157440
  {
157282
157441
  value: align,
157283
157442
  onChange: (v2) => {
@@ -157533,6 +157692,7 @@ ${elements2.join("\n")}
157533
157692
  darkOverlay = null;
157534
157693
  focusedImage = null;
157535
157694
  cropZone = null;
157695
+ maskPath = null;
157536
157696
  onEnd = null;
157537
157697
  init(canvas2) {
157538
157698
  this.canvas = canvas2;
@@ -157544,6 +157704,7 @@ ${elements2.join("\n")}
157544
157704
  this.cleanup();
157545
157705
  this.targetImage = image;
157546
157706
  this.onEnd = onEnd || null;
157707
+ this.maskPath = image.__maskPath || image.clipPath?.__maskPath || null;
157547
157708
  const canvas2 = this.canvas;
157548
157709
  canvas2.renderOnAddRemove = false;
157549
157710
  canvas2.discardActiveObject();
@@ -157560,7 +157721,8 @@ ${elements2.join("\n")}
157560
157721
  width: naturalWidth,
157561
157722
  height: naturalHeight,
157562
157723
  selectable: false,
157563
- evented: false
157724
+ evented: false,
157725
+ clipPath: void 0
157564
157726
  });
157565
157727
  const cropX = image.cropX || 0;
157566
157728
  const cropY = image.cropY || 0;
@@ -157691,14 +157853,31 @@ ${elements2.join("\n")}
157691
157853
  const local = Mn.transformPoint(center, inv);
157692
157854
  const localW = zone.getScaledWidth() / Math.abs(img.scaleX);
157693
157855
  const localH = zone.getScaledHeight() / Math.abs(img.scaleY);
157694
- img.clipPath = new jr({
157695
- left: local.x,
157696
- top: local.y,
157697
- width: localW,
157698
- height: localH,
157699
- originX: "center",
157700
- originY: "center"
157701
- });
157856
+ if (this.maskPath) {
157857
+ const maskClip = new no(this.maskPath, {
157858
+ originX: "center",
157859
+ originY: "center"
157860
+ });
157861
+ const pathW = maskClip.width || 1;
157862
+ const pathH = maskClip.height || 1;
157863
+ maskClip.set({
157864
+ left: local.x,
157865
+ top: local.y,
157866
+ scaleX: localW / pathW,
157867
+ scaleY: localH / pathH,
157868
+ angle: 0
157869
+ });
157870
+ img.clipPath = maskClip;
157871
+ } else {
157872
+ img.clipPath = new jr({
157873
+ left: local.x,
157874
+ top: local.y,
157875
+ width: localW,
157876
+ height: localH,
157877
+ originX: "center",
157878
+ originY: "center"
157879
+ });
157880
+ }
157702
157881
  img.dirty = true;
157703
157882
  }
157704
157883
  /* ======================= APPLY CROP ======================= */
@@ -157723,11 +157902,13 @@ ${elements2.join("\n")}
157723
157902
  const minY = Math.min(...ys2);
157724
157903
  const maxX = Math.max(...xs2);
157725
157904
  const maxY = Math.max(...ys2);
157905
+ const newW = Math.min(full.width, maxX - minX);
157906
+ const newH = Math.min(full.height, maxY - minY);
157726
157907
  img.set({
157727
157908
  cropX: Math.max(0, minX),
157728
157909
  cropY: Math.max(0, minY),
157729
- width: Math.min(full.width, maxX - minX),
157730
- height: Math.min(full.height, maxY - minY),
157910
+ width: newW,
157911
+ height: newH,
157731
157912
  scaleX: full.scaleX,
157732
157913
  scaleY: full.scaleY,
157733
157914
  angle: full.angle,
@@ -157739,6 +157920,35 @@ ${elements2.join("\n")}
157739
157920
  selectable: true,
157740
157921
  evented: true
157741
157922
  });
157923
+ if (this.maskPath) {
157924
+ const newClip = new no(this.maskPath, {
157925
+ originX: "center",
157926
+ originY: "center"
157927
+ });
157928
+ const pathW = newClip.width || 1;
157929
+ const pathH = newClip.height || 1;
157930
+ newClip.set({
157931
+ scaleX: newW / pathW,
157932
+ scaleY: newH / pathH,
157933
+ left: 0,
157934
+ top: 0,
157935
+ angle: 0
157936
+ });
157937
+ newClip.__maskPath = this.maskPath;
157938
+ img.__maskPath = this.maskPath;
157939
+ img.set({ clipPath: newClip });
157940
+ } else if (img.clipPath && img.clipPath.type === "path") {
157941
+ const pW = img.clipPath.width || 1;
157942
+ const pH = img.clipPath.height || 1;
157943
+ img.clipPath.set({
157944
+ scaleX: newW / pW,
157945
+ scaleY: newH / pH,
157946
+ left: 0,
157947
+ top: 0,
157948
+ originX: "center",
157949
+ originY: "center"
157950
+ });
157951
+ }
157742
157952
  img.setPositionByOrigin(
157743
157953
  new ot(zone.left, zone.top),
157744
157954
  "center",
@@ -157747,7 +157957,9 @@ ${elements2.join("\n")}
157747
157957
  img.setCoords();
157748
157958
  this.cleanup();
157749
157959
  this.canvas.setActiveObject(img);
157960
+ bringOverlaysToFront(this.canvas);
157750
157961
  this.canvas.requestRenderAll();
157962
+ useHistoryStore.getState().saveState();
157751
157963
  this.onEnd?.();
157752
157964
  }
157753
157965
  /* ======================== CANCEL ========================== */
@@ -157783,6 +157995,7 @@ ${elements2.join("\n")}
157783
157995
  this.fullImage = null;
157784
157996
  this.darkOverlay = null;
157785
157997
  this.targetImage = null;
157998
+ this.maskPath = null;
157786
157999
  }
157787
158000
  }
157788
158001
  const cropService = new CropService();
@@ -157869,7 +158082,7 @@ ${elements2.join("\n")}
157869
158082
  const [isRemovingBg, setIsRemovingBg] = React.useState(false);
157870
158083
  const [bgStatus, setBgStatus] = React.useState(null);
157871
158084
  const activeObj = canvas2?.getActiveObject();
157872
- const hasMask = !!activeObj?.clipPath;
158085
+ const hasMask = !!activeObj?.clipPath || !!activeObj?.__maskPath;
157873
158086
  activeObj && (activeObj.type === "image" || activeObj.isType?.("image"));
157874
158087
  const isBgRemoved = !!activeObj?.__originalSrc || !!activeObj?.__isBgRemoved;
157875
158088
  if (type !== "image" || objects?.[0]?.name === "qr" || objects?.[0]?.name === "barcode")
@@ -157878,8 +158091,9 @@ ${elements2.join("\n")}
157878
158091
  if (!canvas2) return;
157879
158092
  const obj = canvas2.getActiveObject();
157880
158093
  if (!obj) return;
157881
- if (obj.clipPath) {
158094
+ if (obj.clipPath || obj.__maskPath) {
157882
158095
  obj.set({ clipPath: null });
158096
+ obj.__maskPath = null;
157883
158097
  obj.setCoords();
157884
158098
  canvas2.requestRenderAll();
157885
158099
  saveState();
@@ -158170,6 +158384,7 @@ ${elements2.join("\n")}
158170
158384
  );
158171
158385
  const newSelection = clones.length > 1 ? new Qo(clones, { canvas: canvas2 }) : clones[0];
158172
158386
  canvas2.setActiveObject(newSelection);
158387
+ bringOverlaysToFront(canvas2);
158173
158388
  canvas2.requestRenderAll();
158174
158389
  saveState();
158175
158390
  return;
@@ -158185,6 +158400,7 @@ ${elements2.join("\n")}
158185
158400
  canvas2.discardActiveObject();
158186
158401
  canvas2.add(cloned);
158187
158402
  canvas2.setActiveObject(cloned);
158403
+ bringOverlaysToFront(canvas2);
158188
158404
  canvas2.requestRenderAll();
158189
158405
  };
158190
158406
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -158738,44 +158954,50 @@ ${elements2.join("\n")}
158738
158954
  if (!active) return [];
158739
158955
  return active.type?.toLowerCase() === "activeselection" ? active.getObjects() : [active];
158740
158956
  };
158741
- const getWorkarea2 = () => canvas2.getObjects().find((o2) => o2.name === "workarea");
158742
- const normalizeOrigin = (obj) => {
158743
- if (obj.originX === "left" && obj.originY === "top") return;
158744
- const point = obj.getPointByOrigin("left", "top");
158745
- obj.set({ originX: "left", originY: "top" });
158746
- obj.setPositionByOrigin(point, "left", "top");
158747
- };
158957
+ const getAlignmentBoundary = () => canvas2.getObjects().find((o2) => o2.name === "workarea");
158748
158958
  const applyAlign = (align) => {
158749
- const workarea = getWorkarea2();
158750
- if (!workarea) return;
158751
- const left = workarea.left ?? 0;
158752
- const top = workarea.top ?? 0;
158753
- const width = workarea.getScaledWidth();
158754
- const height = workarea.getScaledHeight();
158959
+ const boundary = getAlignmentBoundary();
158960
+ if (!boundary) return;
158961
+ const bBounds = boundary.getBoundingRect();
158962
+ const bLeft = bBounds.left;
158963
+ const bTop = bBounds.top;
158964
+ const bWidth = bBounds.width;
158965
+ const bHeight = bBounds.height;
158755
158966
  getTargets().forEach((obj) => {
158756
- normalizeOrigin(obj);
158757
- const w2 = obj.getScaledWidth();
158758
- const h2 = obj.getScaledHeight();
158967
+ const objBounds = obj.getBoundingRect();
158968
+ const pad = obj.padding || 0;
158969
+ const effLeft = objBounds.left - pad;
158970
+ const effTop = objBounds.top - pad;
158971
+ const effWidth = objBounds.width + pad * 2;
158972
+ const effHeight = objBounds.height + pad * 2;
158973
+ let dx = 0;
158974
+ let dy = 0;
158759
158975
  switch (align) {
158760
158976
  case "left":
158761
- obj.set({ left });
158977
+ dx = bLeft - effLeft;
158762
158978
  break;
158763
158979
  case "center":
158764
- obj.set({ left: left + (width - w2) / 2 });
158980
+ dx = bLeft + (bWidth - effWidth) / 2 - effLeft;
158765
158981
  break;
158766
158982
  case "right":
158767
- obj.set({ left: left + width - w2 });
158983
+ dx = bLeft + bWidth - effWidth - effLeft;
158768
158984
  break;
158769
158985
  case "top":
158770
- obj.set({ top });
158986
+ dy = bTop - effTop;
158771
158987
  break;
158772
158988
  case "middle":
158773
- obj.set({ top: top + (height - h2) / 2 });
158989
+ dy = bTop + (bHeight - effHeight) / 2 - effTop;
158774
158990
  break;
158775
158991
  case "bottom":
158776
- obj.set({ top: top + height - h2 });
158992
+ dy = bTop + bHeight - effHeight - effTop;
158777
158993
  break;
158778
158994
  }
158995
+ if (dx !== 0) {
158996
+ obj.set({ left: (obj.left ?? 0) + dx });
158997
+ }
158998
+ if (dy !== 0) {
158999
+ obj.set({ top: (obj.top ?? 0) + dy });
159000
+ }
158779
159001
  obj.setCoords();
158780
159002
  });
158781
159003
  canvas2.requestRenderAll();
@@ -158798,42 +159020,40 @@ ${elements2.join("\n")}
158798
159020
  const objects = active.getObjects();
158799
159021
  if (objects.length && objects.length > 1) {
158800
159022
  objects.forEach(function(obj) {
158801
- const itemWidth = obj.getBoundingRect().width;
159023
+ const itemWidth = obj.getScaledWidth();
159024
+ const itemHeight = obj.getScaledHeight();
159025
+ obj.set({ originX: "center", originY: "center" });
158802
159026
  switch (align) {
158803
159027
  case "left":
158804
- obj.set({ left: -(groupWidth / 2), originX: "left" });
159028
+ obj.set({ left: -groupWidth / 2 + itemWidth / 2 });
158805
159029
  break;
158806
159030
  case "center":
158807
- obj.set({ left: 0 - itemWidth / 2, originX: "left" });
159031
+ obj.set({ left: 0 });
158808
159032
  break;
158809
159033
  case "right":
158810
- obj.set({
158811
- left: groupWidth / 2 - itemWidth / 2,
158812
- originX: "center"
158813
- });
159034
+ obj.set({ left: groupWidth / 2 - itemWidth / 2 });
158814
159035
  break;
158815
159036
  case "top":
158816
- obj.set({ originY: "top", top: -groupHeight / 2 });
158817
- break;
158818
- case "bottom":
158819
- obj.set({ originY: "bottom", top: groupHeight / 2 });
159037
+ obj.set({ top: -groupHeight / 2 + itemHeight / 2 });
158820
159038
  break;
158821
159039
  case "middle":
158822
- obj.set({
158823
- originY: "center",
158824
- top: -groupHeight / 2 + groupHeight / 2
158825
- });
159040
+ obj.set({ top: 0 });
159041
+ break;
159042
+ case "bottom":
159043
+ obj.set({ top: groupHeight / 2 - itemHeight / 2 });
158826
159044
  break;
158827
159045
  }
158828
159046
  obj.setCoords();
158829
- canvas2.requestRenderAll();
158830
- saveState();
158831
159047
  });
159048
+ active.setCoords();
159049
+ canvas2.requestRenderAll();
159050
+ saveState();
158832
159051
  }
158833
159052
  };
158834
159053
  const bringForward = () => {
158835
159054
  const objects = canvas2.getObjects();
158836
159055
  [...getTargets()].sort((a2, b2) => objects.indexOf(a2) - objects.indexOf(b2)).reverse().forEach((obj) => canvas2.bringObjectForward(obj, true));
159056
+ bringOverlaysToFront(canvas2);
158837
159057
  canvas2.requestRenderAll();
158838
159058
  saveState();
158839
159059
  };
@@ -158862,6 +159082,7 @@ ${elements2.join("\n")}
158862
159082
  const bringToFront = () => {
158863
159083
  const objects = canvas2.getObjects();
158864
159084
  [...getTargets()].sort((a2, b2) => objects.indexOf(a2) - objects.indexOf(b2)).reverse().forEach((obj) => canvas2.bringObjectToFront(obj));
159085
+ bringOverlaysToFront(canvas2);
158865
159086
  canvas2.requestRenderAll();
158866
159087
  saveState();
158867
159088
  };
@@ -159216,6 +159437,7 @@ ${elements2.join("\n")}
159216
159437
  canvas2.add(group);
159217
159438
  }
159218
159439
  canvas2.setActiveObject(group);
159440
+ bringOverlaysToFront(canvas2);
159219
159441
  canvas2.requestRenderAll();
159220
159442
  useSelectionStore.getState().setSelection([group]);
159221
159443
  saveState();
@@ -159367,6 +159589,9 @@ ${elements2.join("\n")}
159367
159589
  obj.__rotationControlSize = rotationSize;
159368
159590
  obj.controls.mtr = createRotateControl(rotationSize, rotationOffset);
159369
159591
  const textObject = isTextObject(obj);
159592
+ if (textObject) {
159593
+ obj.set({ padding: 0 });
159594
+ }
159370
159595
  if (!obj.controls.mb) {
159371
159596
  obj.controls.mb = new ai({
159372
159597
  x: 0,
@@ -159485,6 +159710,7 @@ ${elements2.join("\n")}
159485
159710
  });
159486
159711
  canvas2.discardActiveObject();
159487
159712
  useSelectionStore.getState().clearSelection();
159713
+ bringOverlaysToFront(canvas2);
159488
159714
  canvas2.requestRenderAll();
159489
159715
  saveState();
159490
159716
  };
@@ -159594,12 +159820,31 @@ ${elements2.join("\n")}
159594
159820
  ) });
159595
159821
  }
159596
159822
  function applyOpacity(hex, opacity) {
159597
- const alpha = Math.max(0, Math.min(1, opacity / 100));
159598
- const r2 = parseInt(hex.slice(1, 3), 16);
159599
- const g2 = parseInt(hex.slice(3, 5), 16);
159600
- const b2 = parseInt(hex.slice(5, 7), 16);
159601
- return `rgba(${r2}, ${g2}, ${b2}, ${alpha})`;
159602
- }
159823
+ const alpha = Math.max(0, Math.min(1, (opacity ?? 100) / 100));
159824
+ if (!hex) return `rgba(0, 0, 0, ${alpha})`;
159825
+ let clean = hex.trim();
159826
+ if (clean.startsWith("#")) {
159827
+ clean = clean.slice(1);
159828
+ if (clean.length === 3) {
159829
+ clean = clean.split("").map((c2) => c2 + c2).join("");
159830
+ }
159831
+ if (clean.length >= 6) {
159832
+ const r2 = parseInt(clean.slice(0, 2), 16) || 0;
159833
+ const g2 = parseInt(clean.slice(2, 4), 16) || 0;
159834
+ const b2 = parseInt(clean.slice(4, 6), 16) || 0;
159835
+ return `rgba(${r2}, ${g2}, ${b2}, ${alpha})`;
159836
+ }
159837
+ }
159838
+ return clean;
159839
+ }
159840
+ const PENCIL_CURSOR_SVG = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
159841
+ <path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z" fill="#ffffff" stroke="#ffffff" stroke-width="2.5" stroke-linejoin="round"/>
159842
+ <path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z" fill="#1e293b" stroke="#0f172a" stroke-width="1.2" stroke-linejoin="round"/>
159843
+ <polygon points="2,22 5.5,20 4,18.5" fill="#f59e0b"/>
159844
+ <polygon points="2,22 3.2,21.3 2.7,20.8" fill="#000000"/>
159845
+ <path d="m15 5 4 4" stroke="#94a3b8" stroke-width="1.2" stroke-linecap="round"/>
159846
+ </svg>`;
159847
+ const PENCIL_CURSOR = `url("data:image/svg+xml;utf8,${encodeURIComponent(PENCIL_CURSOR_SVG)}") 2 22, crosshair`;
159603
159848
  function useFabricDrawing() {
159604
159849
  const { drawTool, strokeWidth, opacity, color } = useDrawStore();
159605
159850
  React.useEffect(() => {
@@ -159609,19 +159854,99 @@ ${elements2.join("\n")}
159609
159854
  canvas2.isDrawingMode = false;
159610
159855
  canvas2.selection = true;
159611
159856
  canvas2.defaultCursor = "default";
159857
+ canvas2.freeDrawingCursor = "default";
159858
+ canvas2.setCursor("default");
159859
+ if (canvas2.upperCanvasEl) {
159860
+ canvas2.upperCanvasEl.style.cursor = "default";
159861
+ }
159862
+ canvas2._isCurrentlyDrawing = false;
159612
159863
  return;
159613
159864
  }
159865
+ if (canvas2.getActiveObject()) {
159866
+ canvas2.discardActiveObject();
159867
+ useSelectionStore.getState().clearSelection();
159868
+ canvas2.requestRenderAll();
159869
+ }
159870
+ const activeCursor = drawTool === "brush" ? PENCIL_CURSOR : "crosshair";
159614
159871
  canvas2.isDrawingMode = true;
159615
159872
  canvas2.selection = false;
159616
- canvas2.defaultCursor = "crosshair";
159873
+ canvas2.defaultCursor = activeCursor;
159874
+ canvas2.freeDrawingCursor = activeCursor;
159875
+ canvas2.setCursor(activeCursor);
159876
+ if (canvas2.upperCanvasEl) {
159877
+ canvas2.upperCanvasEl.style.cursor = activeCursor;
159878
+ }
159617
159879
  const brush = new oo(canvas2);
159618
- brush.width = strokeWidth;
159619
- brush.color = applyOpacity(color, opacity);
159620
159880
  if (drawTool === "highlighter") {
159621
159881
  brush.width = strokeWidth * 1.5;
159622
- brush.color = applyOpacity(color, 30);
159882
+ brush.color = applyOpacity(color, opacity ?? 50);
159883
+ } else {
159884
+ brush.width = strokeWidth;
159885
+ brush.color = applyOpacity(color, opacity ?? 100);
159623
159886
  }
159624
159887
  canvas2.freeDrawingBrush = brush;
159888
+ const handleMouseDownBefore = (opt) => {
159889
+ if (!canvas2.isDrawingMode) return;
159890
+ const target = opt.target || canvas2.findTarget(opt.e);
159891
+ if (!target) return;
159892
+ const name = target.name || "";
159893
+ if (name === "workarea" || name === "watermark" || name.startsWith("overlay-") || name.startsWith("guideline-")) {
159894
+ return;
159895
+ }
159896
+ if (target.isDrawingPath || name === "drawing-path") {
159897
+ return;
159898
+ }
159899
+ if (target.selectable !== false && target.evented !== false) {
159900
+ canvas2.isDrawingMode = false;
159901
+ canvas2.selection = true;
159902
+ canvas2.defaultCursor = "default";
159903
+ canvas2.freeDrawingCursor = "default";
159904
+ canvas2.setCursor("default");
159905
+ if (canvas2.upperCanvasEl) {
159906
+ canvas2.upperCanvasEl.style.cursor = "default";
159907
+ }
159908
+ canvas2._isCurrentlyDrawing = false;
159909
+ useDrawStore.getState().setDrawTool("select");
159910
+ canvas2.setActiveObject(target);
159911
+ canvas2.requestRenderAll();
159912
+ }
159913
+ };
159914
+ const handlePathCreated = (e2) => {
159915
+ if (e2?.path) {
159916
+ e2.path.name = "drawing-path";
159917
+ e2.path.isDrawingPath = true;
159918
+ }
159919
+ };
159920
+ const handleSelectionChange = () => {
159921
+ if (useDrawStore.getState().drawTool !== "select") {
159922
+ useDrawStore.getState().setDrawTool("select");
159923
+ }
159924
+ if (canvas2.isDrawingMode) {
159925
+ canvas2.isDrawingMode = false;
159926
+ canvas2.selection = true;
159927
+ canvas2.defaultCursor = "default";
159928
+ canvas2.freeDrawingCursor = "default";
159929
+ canvas2.setCursor("default");
159930
+ if (canvas2.upperCanvasEl) {
159931
+ canvas2.upperCanvasEl.style.cursor = "default";
159932
+ }
159933
+ canvas2._isCurrentlyDrawing = false;
159934
+ canvas2.requestRenderAll();
159935
+ }
159936
+ };
159937
+ canvas2.on("mouse:down:before", handleMouseDownBefore);
159938
+ canvas2.on("path:created", handlePathCreated);
159939
+ canvas2.on("selection:created", handleSelectionChange);
159940
+ canvas2.on("selection:updated", handleSelectionChange);
159941
+ return () => {
159942
+ canvas2.off("mouse:down:before", handleMouseDownBefore);
159943
+ canvas2.off("path:created", handlePathCreated);
159944
+ canvas2.off("selection:created", handleSelectionChange);
159945
+ canvas2.off("selection:updated", handleSelectionChange);
159946
+ if (canvas2.upperCanvasEl) {
159947
+ canvas2.upperCanvasEl.style.cursor = "default";
159948
+ }
159949
+ };
159625
159950
  }, [drawTool, strokeWidth, opacity, color]);
159626
159951
  }
159627
159952
  function PointerIcon() {
@@ -159652,6 +159977,89 @@ ${elements2.join("\n")}
159652
159977
  /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 8a2 2 0 1 1 4 0v6a8 8 0 0 1-8 8h-2c-2.8 0-4.5-.86-5.99-2.34l-3.6-3.6a2 2 0 0 1 2.83-2.82L7 15" })
159653
159978
  ] });
159654
159979
  }
159980
+ function GridIcon({
159981
+ size = 18,
159982
+ className = ""
159983
+ }) {
159984
+ return /* @__PURE__ */ jsxRuntime.jsxs(
159985
+ "svg",
159986
+ {
159987
+ width: size,
159988
+ height: size,
159989
+ viewBox: "0 0 24 24",
159990
+ fill: "none",
159991
+ stroke: "currentColor",
159992
+ strokeWidth: "2",
159993
+ strokeLinecap: "round",
159994
+ strokeLinejoin: "round",
159995
+ className,
159996
+ children: [
159997
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { width: "18", height: "18", x: "3", y: "3", rx: "2" }),
159998
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3 9h18" }),
159999
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3 15h18" }),
160000
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M9 3v18" }),
160001
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M15 3v18" })
160002
+ ]
160003
+ }
160004
+ );
160005
+ }
160006
+ function getGridStep(width, height) {
160007
+ const minDim = Math.min(width, height);
160008
+ if (minDim <= 200) return 10;
160009
+ if (minDim <= 400) return 20;
160010
+ if (minDim <= 800) return 25;
160011
+ return 40;
160012
+ }
160013
+ function renderGrid(canvas2) {
160014
+ if (!canvas2) return;
160015
+ canvas2.getObjects().forEach((o2) => {
160016
+ if (o2.name === "canvas-grid") {
160017
+ canvas2.remove(o2);
160018
+ }
160019
+ });
160020
+ const showGrid = useEditorStore.getState().showGrid;
160021
+ if (!showGrid) {
160022
+ canvas2.requestRenderAll();
160023
+ return;
160024
+ }
160025
+ const workarea = canvas2.getObjects().find((o2) => o2.name === "workarea");
160026
+ if (!workarea) {
160027
+ canvas2.requestRenderAll();
160028
+ return;
160029
+ }
160030
+ const waL = workarea.left ?? 0;
160031
+ const waT = workarea.top ?? 0;
160032
+ const waW = workarea.getScaledWidth();
160033
+ const waH = workarea.getScaledHeight();
160034
+ if (waW <= 0 || waH <= 0) return;
160035
+ const step = getGridStep(waW, waH);
160036
+ let pathData = "";
160037
+ for (let x2 = step; x2 < waW; x2 += step) {
160038
+ const roundX = Math.round(x2 * 100) / 100;
160039
+ pathData += `M ${roundX} 0 V ${waH} `;
160040
+ }
160041
+ for (let y2 = step; y2 < waH; y2 += step) {
160042
+ const roundY = Math.round(y2 * 100) / 100;
160043
+ pathData += `M 0 ${roundY} H ${waW} `;
160044
+ }
160045
+ if (!pathData) return;
160046
+ const gridPath = new no(pathData, {
160047
+ name: "canvas-grid",
160048
+ left: waL,
160049
+ top: waT,
160050
+ originX: "left",
160051
+ originY: "top",
160052
+ stroke: "rgba(100, 116, 139, 0.28)",
160053
+ strokeWidth: 1,
160054
+ fill: "",
160055
+ selectable: false,
160056
+ evented: false,
160057
+ excludeFromExport: true
160058
+ });
160059
+ canvas2.add(gridPath);
160060
+ bringOverlaysToFront(canvas2);
160061
+ canvas2.requestRenderAll();
160062
+ }
159655
160063
  function enablePanMode(canvas2) {
159656
160064
  let isDragging = false;
159657
160065
  let lastClientX = 0;
@@ -159919,10 +160327,11 @@ ${elements2.join("\n")}
159919
160327
  const target = e2?.target;
159920
160328
  if (!target) return;
159921
160329
  const name = target.name || "";
159922
- if (name === "workarea" || name === "watermark" || name.startsWith("overlay-") || name.startsWith("guideline-")) {
160330
+ if (name === "workarea" || name === "watermark" || name === "bg-image" || name.startsWith("overlay-") || name.startsWith("guideline-") || name === "canvas-grid") {
159923
160331
  return;
159924
160332
  }
159925
160333
  if (useHistoryStore.getState().isRestoring) return;
160334
+ flushBackgroundSave();
159926
160335
  if (modifiedTimer) {
159927
160336
  clearTimeout(modifiedTimer);
159928
160337
  modifiedTimer = null;
@@ -159937,7 +160346,7 @@ ${elements2.join("\n")}
159937
160346
  const target = e2?.target;
159938
160347
  if (!target) return;
159939
160348
  const name = target.name || "";
159940
- if (name === "workarea" || name === "watermark" || name.startsWith("overlay-") || name.startsWith("guideline-")) {
160349
+ if (name === "workarea" || name === "watermark" || name.startsWith("overlay-") || name.startsWith("guideline-") || name === "canvas-grid") {
159941
160350
  return;
159942
160351
  }
159943
160352
  if (useHistoryStore.getState().isRestoring) return;
@@ -159960,6 +160369,12 @@ ${elements2.join("\n")}
159960
160369
  }
159961
160370
  function setupDoublClickCrop(canvas2) {
159962
160371
  const handleDoubleClick = (e2) => {
160372
+ const isCropping = useEditorStore.getState().isCropping;
160373
+ if (isCropping) {
160374
+ applyCrop(canvas2);
160375
+ useEditorStore.getState().stopCropping();
160376
+ return;
160377
+ }
159963
160378
  const target = e2.target;
159964
160379
  if (target && target.type === "image" && target.name !== "bg-image") {
159965
160380
  useEditorStore.getState().startCropping();
@@ -160005,6 +160420,13 @@ ${elements2.join("\n")}
160005
160420
  canvas2.requestRenderAll();
160006
160421
  };
160007
160422
  canvas2.on("selection:created", (e2) => {
160423
+ if (useDrawStore.getState().drawTool !== "select") {
160424
+ useDrawStore.getState().setDrawTool("select");
160425
+ canvas2.isDrawingMode = false;
160426
+ canvas2.selection = true;
160427
+ canvas2.defaultCursor = "default";
160428
+ canvas2._isCurrentlyDrawing = false;
160429
+ }
160008
160430
  if (e2.selected) {
160009
160431
  e2.selected.forEach((o2) => {
160010
160432
  o2.__showDegree = false;
@@ -160012,8 +160434,16 @@ ${elements2.join("\n")}
160012
160434
  }
160013
160435
  updateSelection();
160014
160436
  configureSelectionControls();
160437
+ bringOverlaysToFront(canvas2);
160015
160438
  });
160016
160439
  canvas2.on("selection:updated", (e2) => {
160440
+ if (useDrawStore.getState().drawTool !== "select") {
160441
+ useDrawStore.getState().setDrawTool("select");
160442
+ canvas2.isDrawingMode = false;
160443
+ canvas2.selection = true;
160444
+ canvas2.defaultCursor = "default";
160445
+ canvas2._isCurrentlyDrawing = false;
160446
+ }
160017
160447
  const active = canvas2.getActiveObject();
160018
160448
  const bgImg = canvas2.getObjects().find((o2) => o2.name === "bg-image");
160019
160449
  if (bgImg && active !== bgImg) {
@@ -160034,6 +160464,7 @@ ${elements2.join("\n")}
160034
160464
  }
160035
160465
  updateSelection();
160036
160466
  configureSelectionControls();
160467
+ bringOverlaysToFront(canvas2);
160037
160468
  });
160038
160469
  canvas2.on("object:scaling", configureSelectionControls);
160039
160470
  canvas2.on("object:rotating", (e2) => {
@@ -160050,7 +160481,12 @@ ${elements2.join("\n")}
160050
160481
  o2.__showDegree = false;
160051
160482
  });
160052
160483
  configureSelectionControls();
160484
+ bringOverlaysToFront(canvas2);
160485
+ };
160486
+ const handleObjectMoving = () => {
160487
+ bringOverlaysToFront(canvas2);
160053
160488
  };
160489
+ canvas2.on("object:moving", handleObjectMoving);
160054
160490
  canvas2.on("object:modified", hideDegree);
160055
160491
  canvas2.on("mouse:up", hideDegree);
160056
160492
  canvas2.on("selection:cleared", () => {
@@ -160067,6 +160503,7 @@ ${elements2.join("\n")}
160067
160503
  }
160068
160504
  }
160069
160505
  hideDegree();
160506
+ bringOverlaysToFront(canvas2);
160070
160507
  clearSelection();
160071
160508
  });
160072
160509
  return () => {
@@ -160074,6 +160511,7 @@ ${elements2.join("\n")}
160074
160511
  canvas2.off("selection:updated");
160075
160512
  canvas2.off("object:scaling", configureSelectionControls);
160076
160513
  canvas2.off("object:rotating");
160514
+ canvas2.off("object:moving", handleObjectMoving);
160077
160515
  canvas2.off("object:modified", hideDegree);
160078
160516
  canvas2.off("mouse:up", hideDegree);
160079
160517
  canvas2.off("selection:cleared");
@@ -160082,7 +160520,9 @@ ${elements2.join("\n")}
160082
160520
  function setupObjectAddedHandler(canvas2) {
160083
160521
  const handleObjectAdded = (e2) => {
160084
160522
  const obj = e2.target;
160085
- if (!obj || obj.name === "workarea") return;
160523
+ if (!obj || obj.name === "workarea" || obj.name?.startsWith("overlay-") || obj.name?.startsWith("guideline-")) {
160524
+ return;
160525
+ }
160086
160526
  configureObjectControls(obj);
160087
160527
  bringOverlaysToFront(canvas2);
160088
160528
  };
@@ -160576,21 +161016,27 @@ ${elements2.join("\n")}
160576
161016
  ] }) });
160577
161017
  }
160578
161018
  const OFFSET = 20;
160579
- function useKeyboardEvents(canvas2) {
161019
+ function useKeyboardEvents(canvasParam) {
160580
161020
  const clipboardRef = React.useRef(null);
161021
+ const storeCanvas = useEditorStore((s2) => s2.canvas);
161022
+ const historyCanvas = useHistoryStore((s2) => s2.canvas);
161023
+ const canvas2 = canvasParam || storeCanvas || historyCanvas;
160581
161024
  const saveState = useHistoryStore((s2) => s2.saveState);
160582
161025
  React.useEffect(() => {
160583
- if (!canvas2) return;
161026
+ const getActiveCanvas = () => canvas2 || useEditorStore.getState().canvas || useHistoryStore.getState().canvas;
160584
161027
  const handleCopy = async () => {
160585
- const activeObject = canvas2.getActiveObject();
161028
+ const activeCanvas = getActiveCanvas();
161029
+ if (!activeCanvas) return;
161030
+ const activeObject = activeCanvas.getActiveObject();
160586
161031
  if (!activeObject || activeObject.name === "workarea") return;
160587
161032
  const cloned = await activeObject.clone();
160588
161033
  clipboardRef.current = cloned;
160589
161034
  };
160590
161035
  const handlePaste = async () => {
160591
- if (!clipboardRef.current) return;
161036
+ const activeCanvas = getActiveCanvas();
161037
+ if (!activeCanvas || !clipboardRef.current) return;
160592
161038
  const clonedObj = await clipboardRef.current.clone();
160593
- canvas2.discardActiveObject();
161039
+ activeCanvas.discardActiveObject();
160594
161040
  clonedObj.set({
160595
161041
  left: (clonedObj.left ?? 0) + OFFSET,
160596
161042
  top: (clonedObj.top ?? 0) + OFFSET,
@@ -160601,16 +161047,17 @@ ${elements2.join("\n")}
160601
161047
  top: (clipboardRef.current.top ?? 0) + OFFSET
160602
161048
  });
160603
161049
  if (clonedObj.type?.toLowerCase() === "activeselection") {
160604
- clonedObj.canvas = canvas2;
161050
+ clonedObj.canvas = activeCanvas;
160605
161051
  clonedObj.forEachObject((obj) => {
160606
- canvas2.add(obj);
161052
+ activeCanvas.add(obj);
160607
161053
  });
160608
161054
  clonedObj.setCoords();
160609
161055
  } else {
160610
- canvas2.add(clonedObj);
161056
+ activeCanvas.add(clonedObj);
160611
161057
  }
160612
- canvas2.setActiveObject(clonedObj);
160613
- canvas2.requestRenderAll();
161058
+ activeCanvas.setActiveObject(clonedObj);
161059
+ bringOverlaysToFront(activeCanvas);
161060
+ activeCanvas.requestRenderAll();
160614
161061
  saveState();
160615
161062
  };
160616
161063
  const handleKeyDown = (e2) => {
@@ -160618,25 +161065,36 @@ ${elements2.join("\n")}
160618
161065
  const isInput = activeElement instanceof HTMLInputElement || activeElement instanceof HTMLTextAreaElement || activeElement?.isContentEditable;
160619
161066
  if (isInput) return;
160620
161067
  const isModifier = e2.ctrlKey || e2.metaKey;
160621
- if (isModifier && e2.key.toLowerCase() === "c") {
161068
+ if (!isModifier) return;
161069
+ const isZ = e2.key?.toLowerCase() === "z" || e2.code === "KeyZ";
161070
+ const isY = e2.key?.toLowerCase() === "y" || e2.code === "KeyY";
161071
+ const isC = e2.key?.toLowerCase() === "c" || e2.code === "KeyC";
161072
+ const isV = e2.key?.toLowerCase() === "v" || e2.code === "KeyV";
161073
+ if (isZ && !e2.shiftKey) {
160622
161074
  e2.preventDefault();
160623
- handleCopy();
161075
+ useHistoryStore.getState().undo();
161076
+ return;
160624
161077
  }
160625
- if (isModifier && e2.key.toLowerCase() === "v") {
161078
+ if (isZ && e2.shiftKey || isY) {
160626
161079
  e2.preventDefault();
160627
- handlePaste();
161080
+ useHistoryStore.getState().redoAction();
161081
+ return;
160628
161082
  }
160629
- if (isModifier && e2.key.toLowerCase() === "z") {
161083
+ if (isC) {
160630
161084
  e2.preventDefault();
160631
- if (e2.shiftKey) {
160632
- useHistoryStore.getState().redoAction();
160633
- } else {
160634
- useHistoryStore.getState().undo();
160635
- }
161085
+ handleCopy();
161086
+ return;
160636
161087
  }
160637
- if (isModifier && e2.key.toLowerCase() === "y") {
161088
+ if (isV) {
160638
161089
  e2.preventDefault();
160639
- useHistoryStore.getState().redoAction();
161090
+ handlePaste();
161091
+ return;
161092
+ }
161093
+ const isQuote = e2.key === "'" || e2.code === "Quote";
161094
+ if (isQuote) {
161095
+ e2.preventDefault();
161096
+ useEditorStore.getState().toggleGrid();
161097
+ return;
160640
161098
  }
160641
161099
  };
160642
161100
  window.addEventListener("keydown", handleKeyDown);
@@ -160652,9 +161110,14 @@ ${elements2.join("\n")}
160652
161110
  const workareaRef = React.useRef(null);
160653
161111
  const panHandlersRef = React.useRef(null);
160654
161112
  const isCropping = useEditorStore((s2) => s2.isCropping);
161113
+ const canvas2 = useEditorStore((s2) => s2.canvas);
160655
161114
  const setCanvas = useEditorStore((s2) => s2.setCanvas);
161115
+ const showGrid = useEditorStore((s2) => s2.showGrid);
161116
+ const toggleGrid = useEditorStore((s2) => s2.toggleGrid);
160656
161117
  const setSelection = useSelectionStore((s2) => s2.setSelection);
160657
161118
  const clearSelection = useSelectionStore((s2) => s2.clearSelection);
161119
+ const drawTool = useDrawStore((s2) => s2.drawTool);
161120
+ const setDrawTool = useDrawStore((s2) => s2.setDrawTool);
160658
161121
  const productData = useProductStore((s2) => s2.productData);
160659
161122
  const isTwoSided = productData?.optionsMap?.printing === "4/4";
160660
161123
  const [currentPage, setCurrentPage] = React.useState("front");
@@ -160704,20 +161167,20 @@ ${elements2.join("\n")}
160704
161167
  }
160705
161168
  if (!isMounted || !canvasRef.current || !containerRef.current || fabricRef.current) return;
160706
161169
  initializeRotateImage();
160707
- const canvas2 = initializeCanvas(canvasRef.current, containerRef.current);
160708
- fabricRef.current = canvas2;
160709
- setCanvas(canvas2);
160710
- useHistoryStore.getState().setCanvas(canvas2);
160711
- const workarea = createWorkarea(canvas2, containerRef.current, product);
161170
+ const canvas22 = initializeCanvas(canvasRef.current, containerRef.current);
161171
+ fabricRef.current = canvas22;
161172
+ setCanvas(canvas22);
161173
+ useHistoryStore.getState().setCanvas(canvas22);
161174
+ const workarea = createWorkarea(canvas22, containerRef.current, product);
160712
161175
  workareaRef.current = workarea;
160713
- const cleanupHistory = setupHistoryRecording(canvas2);
160714
- const cleanupCrop = setupDoublClickCrop(canvas2);
161176
+ const cleanupHistory = setupHistoryRecording(canvas22);
161177
+ const cleanupCrop = setupDoublClickCrop(canvas22);
160715
161178
  const cleanupSelection = setupSelectionHandlers(
160716
- canvas2,
161179
+ canvas22,
160717
161180
  setSelection,
160718
161181
  clearSelection
160719
161182
  );
160720
- const cleanupObjectAdded = setupObjectAddedHandler(canvas2);
161183
+ const cleanupObjectAdded = setupObjectAddedHandler(canvas22);
160721
161184
  if (isTextDesign && templateId) {
160722
161185
  const item = await ShapeService.getTextTemplateById(templateId);
160723
161186
  if (item?.jsonData) {
@@ -160725,7 +161188,7 @@ ${elements2.join("\n")}
160725
161188
  pageJSON.current.front = parsed;
160726
161189
  await loadTemplateFontsIntoHead(null, parsed);
160727
161190
  useHistoryStore.setState({ isRestoring: true });
160728
- await loadPageToCanvas(canvas2, parsed);
161191
+ await loadPageToCanvas(canvas22, parsed);
160729
161192
  useHistoryStore.setState({ isRestoring: false });
160730
161193
  useHistoryStore.getState().saveState();
160731
161194
  }
@@ -160746,7 +161209,7 @@ ${elements2.join("\n")}
160746
161209
  if (frontData) {
160747
161210
  pageJSON.current.front = frontData;
160748
161211
  useHistoryStore.setState({ isRestoring: true });
160749
- await loadPageToCanvas(canvas2, frontData);
161212
+ await loadPageToCanvas(canvas22, frontData);
160750
161213
  useHistoryStore.setState({ isRestoring: false });
160751
161214
  useHistoryStore.getState().saveState();
160752
161215
  }
@@ -160763,10 +161226,10 @@ ${elements2.join("\n")}
160763
161226
  cleanupCrop();
160764
161227
  cleanupSelection();
160765
161228
  cleanupObjectAdded();
160766
- if (canvas2.__cleanupResizeObserver) {
160767
- canvas2.__cleanupResizeObserver();
161229
+ if (canvas22.__cleanupResizeObserver) {
161230
+ canvas22.__cleanupResizeObserver();
160768
161231
  }
160769
- canvas2.dispose();
161232
+ canvas22.dispose();
160770
161233
  fabricRef.current = null;
160771
161234
  setCanvas(null);
160772
161235
  clearSelection();
@@ -160804,23 +161267,30 @@ ${elements2.join("\n")}
160804
161267
  return setupBorderSync(fabricRef.current);
160805
161268
  }, [fabricRef.current]);
160806
161269
  React.useEffect(() => {
160807
- const canvas2 = fabricRef.current;
160808
- if (!canvas2) return;
160809
- removePanHandlers(canvas2, panHandlersRef.current);
161270
+ const canvas22 = fabricRef.current;
161271
+ if (!canvas22) return;
161272
+ removePanHandlers(canvas22, panHandlersRef.current);
160810
161273
  panHandlersRef.current = null;
160811
161274
  if (isPanning) {
160812
- panHandlersRef.current = enablePanMode(canvas2);
161275
+ panHandlersRef.current = enablePanMode(canvas22);
160813
161276
  } else {
160814
- disablePanMode(canvas2);
161277
+ disablePanMode(canvas22);
160815
161278
  }
160816
161279
  return () => {
160817
- removePanHandlers(canvas2, panHandlersRef.current);
161280
+ removePanHandlers(canvas22, panHandlersRef.current);
160818
161281
  };
160819
161282
  }, [isPanning]);
160820
161283
  useFabricDrawing();
160821
- useKeyboardEvents(fabricRef.current);
161284
+ useKeyboardEvents(canvas2 || fabricRef.current);
161285
+ React.useEffect(() => {
161286
+ const activeCanvas = canvas2 || fabricRef.current;
161287
+ if (activeCanvas) {
161288
+ renderGrid(activeCanvas);
161289
+ }
161290
+ }, [showGrid, canvas2, currentPage]);
160822
161291
  const switchPage = async (page) => {
160823
161292
  if (!fabricRef.current) return;
161293
+ setDrawTool("select");
160824
161294
  await switchCanvasPage(
160825
161295
  fabricRef.current,
160826
161296
  currentPage,
@@ -160831,6 +161301,7 @@ ${elements2.join("\n")}
160831
161301
  );
160832
161302
  useEditorStore.getState().setPages({ ...pageJSON.current });
160833
161303
  setCurrentPage(page);
161304
+ renderGrid(fabricRef.current);
160834
161305
  };
160835
161306
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
160836
161307
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute bottom-6 z-50 flex items-center bg-white/80 backdrop-blur-xl rounded-2xl shadow-[0_8px_32px_rgba(0,0,0,0.12)] px-1.5 py-1.5 border border-white/40 ring-1 ring-black/5", children: [
@@ -160857,20 +161328,35 @@ ${elements2.join("\n")}
160857
161328
  /* @__PURE__ */ jsxRuntime.jsx(
160858
161329
  "button",
160859
161330
  {
160860
- onClick: () => setIsPanning(false),
161331
+ onClick: () => {
161332
+ setIsPanning(false);
161333
+ setDrawTool("select");
161334
+ },
160861
161335
  title: "Selection Mode",
160862
- className: `p-2 rounded-xl transition-all duration-200 ${!isPanning ? "bg-[#02bc71] text-white shadow-lg shadow-blue-200" : "text-gray-400 hover:bg-gray-100 hover:text-gray-600"}`,
161336
+ className: `p-2 rounded-xl transition-all duration-200 cursor-pointer ${!isPanning && drawTool === "select" ? "bg-[#02bc71] text-white shadow-lg shadow-green-200" : "text-gray-400 hover:bg-gray-100 hover:text-gray-600"}`,
160863
161337
  children: /* @__PURE__ */ jsxRuntime.jsx(PointerIcon, {})
160864
161338
  }
160865
161339
  ),
160866
161340
  /* @__PURE__ */ jsxRuntime.jsx(
160867
161341
  "button",
160868
161342
  {
160869
- onClick: () => setIsPanning(true),
161343
+ onClick: () => {
161344
+ setIsPanning(true);
161345
+ setDrawTool("select");
161346
+ },
160870
161347
  title: "Hand Mode",
160871
- className: `p-2 rounded-xl transition-all duration-200 ${isPanning ? "bg-[#02bc71] text-white shadow-lg shadow-blue-200" : "text-gray-400 hover:bg-gray-100 hover:text-gray-600"}`,
161348
+ className: `p-2 rounded-xl transition-all duration-200 cursor-pointer ${isPanning ? "bg-[#02bc71] text-white shadow-lg shadow-green-200" : "text-gray-400 hover:bg-gray-100 hover:text-gray-600"}`,
160872
161349
  children: /* @__PURE__ */ jsxRuntime.jsx(HandIcon, {})
160873
161350
  }
161351
+ ),
161352
+ /* @__PURE__ */ jsxRuntime.jsx(
161353
+ "button",
161354
+ {
161355
+ onClick: () => toggleGrid(),
161356
+ title: showGrid ? "Hide Grid (Ctrl+')" : "Show Grid (Ctrl+')",
161357
+ className: `p-2 rounded-xl transition-all duration-200 cursor-pointer ${showGrid ? "bg-[#02bc71] text-white shadow-lg shadow-green-200" : "text-gray-400 hover:bg-gray-100 hover:text-gray-600"}`,
161358
+ children: /* @__PURE__ */ jsxRuntime.jsx(GridIcon, {})
161359
+ }
160874
161360
  )
160875
161361
  ] }),
160876
161362
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-[1px] h-6 bg-gray-300/50 mx-3" }),