vibrant-tool 0.1.7 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/hooks/useCanvasZoom.d.ts +4 -1
- package/dist/lib/services/background-removal.service.d.ts +29 -0
- package/dist/lib/services/upload.service.d.ts +1 -0
- package/dist/lib/utils/canvas-setup.d.ts +6 -0
- package/dist/lib/utils/fabric-helper.d.ts +5 -2
- package/dist/lib/utils/image-utils.d.ts +4 -0
- package/dist/modules/components/position-picker/position-icons.d.ts +12 -0
- package/dist/modules/components/zoom-controls/index.d.ts +5 -1
- package/dist/modules/icons/bgremoveicon/index.d.ts +4 -0
- package/dist/modules/topbar/preview-modal.d.ts +8 -2
- package/dist/vibrant-tool.css +194 -21
- package/dist/vibrant-tool.es.js +1601 -310
- package/dist/vibrant-tool.umd.js +1601 -310
- package/dist/zustand/historyStore.d.ts +1 -0
- package/package.json +2 -1
package/dist/vibrant-tool.es.js
CHANGED
|
@@ -1074,9 +1074,9 @@ const useLoaderStore = create((set) => ({
|
|
|
1074
1074
|
function GlobalLoader() {
|
|
1075
1075
|
const { isLoading, text } = useLoaderStore();
|
|
1076
1076
|
if (!isLoading) return null;
|
|
1077
|
-
return /* @__PURE__ */ jsx("div", { className: "fixed inset-0 z-[999999] flex flex-col items-center justify-center bg-black/
|
|
1078
|
-
/* @__PURE__ */ jsx("div", { className: "h-
|
|
1079
|
-
|
|
1077
|
+
return /* @__PURE__ */ jsx("div", { className: "fixed inset-0 z-[999999] flex flex-col items-center justify-center bg-black/65 backdrop-blur-[2px] select-none", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-3.5 bg-neutral-900/95 border border-neutral-700/80 px-8 py-6 rounded-2xl shadow-2xl min-w-[240px] max-w-[360px] text-center", children: [
|
|
1078
|
+
/* @__PURE__ */ jsx("div", { className: "relative flex items-center justify-center", children: /* @__PURE__ */ jsx("div", { className: "h-10 w-10 animate-spin rounded-full border-3 border-[#02bc71] border-t-transparent shadow-sm" }) }),
|
|
1079
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-neutral-100 tracking-wide", children: text || "Please wait..." })
|
|
1080
1080
|
] }) });
|
|
1081
1081
|
}
|
|
1082
1082
|
function DownloadIcon() {
|
|
@@ -9026,7 +9026,7 @@ async function loadPageToCanvas(canvas2, json, pageSide) {
|
|
|
9026
9026
|
waH,
|
|
9027
9027
|
isVertical
|
|
9028
9028
|
);
|
|
9029
|
-
workarea.set({ rx, ry });
|
|
9029
|
+
workarea.set({ rx, ry, strokeWidth: 0, stroke: null });
|
|
9030
9030
|
workarea.__radiusOfCanvas = radiusOfCanvas;
|
|
9031
9031
|
workarea.__shapeType = shapeType;
|
|
9032
9032
|
workarea.__isVertical = isVertical;
|
|
@@ -9045,8 +9045,61 @@ async function loadPageToCanvas(canvas2, json, pageSide) {
|
|
|
9045
9045
|
top: (obj.top ?? 0) + dy
|
|
9046
9046
|
});
|
|
9047
9047
|
obj.setCoords();
|
|
9048
|
+
if (obj.clipPath) {
|
|
9049
|
+
obj.clipPath.set({
|
|
9050
|
+
left: (obj.clipPath.left ?? 0) + dx,
|
|
9051
|
+
top: (obj.clipPath.top ?? 0) + dy
|
|
9052
|
+
});
|
|
9053
|
+
obj.clipPath.setCoords();
|
|
9054
|
+
}
|
|
9048
9055
|
}
|
|
9049
9056
|
});
|
|
9057
|
+
if (workarea.clipPath) {
|
|
9058
|
+
workarea.clipPath.set({
|
|
9059
|
+
left: (workarea.clipPath.left ?? 0) + dx,
|
|
9060
|
+
top: (workarea.clipPath.top ?? 0) + dy
|
|
9061
|
+
});
|
|
9062
|
+
workarea.clipPath.setCoords();
|
|
9063
|
+
}
|
|
9064
|
+
if (canvas2.backgroundImage) {
|
|
9065
|
+
const bg = canvas2.backgroundImage;
|
|
9066
|
+
bg.set({
|
|
9067
|
+
left: (bg.left ?? 0) + dx,
|
|
9068
|
+
top: (bg.top ?? 0) + dy
|
|
9069
|
+
});
|
|
9070
|
+
bg.setCoords();
|
|
9071
|
+
if (bg.clipPath) {
|
|
9072
|
+
bg.clipPath.set({
|
|
9073
|
+
left: (bg.clipPath.left ?? 0) + dx,
|
|
9074
|
+
top: (bg.clipPath.top ?? 0) + dy
|
|
9075
|
+
});
|
|
9076
|
+
bg.clipPath.setCoords();
|
|
9077
|
+
}
|
|
9078
|
+
}
|
|
9079
|
+
}
|
|
9080
|
+
const bgImg = canvas2.getObjects().find((o2) => o2.name === "bg-image");
|
|
9081
|
+
if (bgImg) {
|
|
9082
|
+
const center = workarea.getCenterPoint();
|
|
9083
|
+
const curWaW = workarea.getScaledWidth();
|
|
9084
|
+
const curWaH = workarea.getScaledHeight();
|
|
9085
|
+
bgImg.set({
|
|
9086
|
+
left: center.x,
|
|
9087
|
+
top: center.y,
|
|
9088
|
+
originX: "center",
|
|
9089
|
+
originY: "center"
|
|
9090
|
+
});
|
|
9091
|
+
bgImg.setCoords();
|
|
9092
|
+
const exportLeft = Math.round(center.x - curWaW / 2);
|
|
9093
|
+
const exportTop = Math.round(center.y - curWaH / 2);
|
|
9094
|
+
const clip2 = createShapeExportClip(
|
|
9095
|
+
shapeType,
|
|
9096
|
+
{ left: exportLeft, top: exportTop, width: curWaW, height: curWaH, rx, ry },
|
|
9097
|
+
currentSide
|
|
9098
|
+
);
|
|
9099
|
+
if (clip2) {
|
|
9100
|
+
clip2.absolutePositioned = true;
|
|
9101
|
+
bgImg.clipPath = clip2;
|
|
9102
|
+
}
|
|
9050
9103
|
}
|
|
9051
9104
|
createOverlays(canvas2, workarea, currentSide);
|
|
9052
9105
|
canvas2.sendObjectToBack(workarea);
|
|
@@ -9065,8 +9118,7 @@ async function loadPageToCanvas(canvas2, json, pageSide) {
|
|
|
9065
9118
|
} else {
|
|
9066
9119
|
obj.set({
|
|
9067
9120
|
selectable: false,
|
|
9068
|
-
evented: false
|
|
9069
|
-
hasControls: false
|
|
9121
|
+
evented: false
|
|
9070
9122
|
});
|
|
9071
9123
|
}
|
|
9072
9124
|
obj.setCoords();
|
|
@@ -9084,69 +9136,89 @@ async function loadPageToCanvas(canvas2, json, pageSide) {
|
|
|
9084
9136
|
}, 40);
|
|
9085
9137
|
});
|
|
9086
9138
|
}
|
|
9087
|
-
function getWorkareaDataUrl(cvs, multiplier = 1, pageSide) {
|
|
9139
|
+
function getWorkareaDataUrl(cvs, multiplier = 1, pageSide, includeGuidelines = false) {
|
|
9088
9140
|
const wa2 = cvs.getObjects().find((o2) => o2.name === "workarea");
|
|
9089
9141
|
cvs.discardActiveObject();
|
|
9090
|
-
const
|
|
9142
|
+
const guidesAndOverlays = cvs.getObjects().filter(
|
|
9091
9143
|
(o2) => o2.name?.startsWith("guideline-") || o2.name?.startsWith("overlay-") || o2.name === "guideline-trim" || o2.name === "guideline-safe" || o2.excludeFromExport === true
|
|
9092
9144
|
);
|
|
9093
|
-
const prevVis =
|
|
9094
|
-
|
|
9095
|
-
g2.
|
|
9145
|
+
const prevVis = guidesAndOverlays.map((g2) => g2.visible);
|
|
9146
|
+
guidesAndOverlays.forEach((g2) => {
|
|
9147
|
+
const isGuideline = g2.name?.startsWith("guideline-") || g2.name === "guideline-trim" || g2.name === "guideline-safe";
|
|
9148
|
+
if (includeGuidelines && isGuideline) {
|
|
9149
|
+
g2.visible = true;
|
|
9150
|
+
} else {
|
|
9151
|
+
g2.visible = false;
|
|
9152
|
+
}
|
|
9096
9153
|
});
|
|
9154
|
+
if (includeGuidelines) {
|
|
9155
|
+
const activeGuides = guidesAndOverlays.filter(
|
|
9156
|
+
(g2) => g2.name?.startsWith("guideline-") || g2.name === "guideline-trim" || g2.name === "guideline-safe"
|
|
9157
|
+
);
|
|
9158
|
+
activeGuides.forEach((g2) => cvs.bringObjectToFront(g2));
|
|
9159
|
+
}
|
|
9097
9160
|
const prevBg = cvs.backgroundColor;
|
|
9098
9161
|
cvs.backgroundColor = "";
|
|
9162
|
+
const prevVt = cvs.viewportTransform ? [...cvs.viewportTransform] : [1, 0, 0, 1, 0, 0];
|
|
9163
|
+
const prevZoom = cvs.getZoom();
|
|
9164
|
+
cvs.setViewportTransform([1, 0, 0, 1, 0, 0]);
|
|
9165
|
+
cvs.setZoom(1);
|
|
9099
9166
|
let dataUrl = "";
|
|
9100
|
-
|
|
9101
|
-
|
|
9102
|
-
|
|
9103
|
-
|
|
9104
|
-
|
|
9105
|
-
|
|
9106
|
-
|
|
9107
|
-
|
|
9108
|
-
|
|
9109
|
-
|
|
9110
|
-
|
|
9111
|
-
|
|
9112
|
-
|
|
9113
|
-
|
|
9114
|
-
|
|
9115
|
-
|
|
9116
|
-
|
|
9117
|
-
|
|
9118
|
-
|
|
9119
|
-
|
|
9120
|
-
|
|
9121
|
-
|
|
9122
|
-
|
|
9123
|
-
|
|
9124
|
-
|
|
9125
|
-
|
|
9126
|
-
|
|
9127
|
-
|
|
9128
|
-
|
|
9129
|
-
|
|
9167
|
+
try {
|
|
9168
|
+
if (!wa2) {
|
|
9169
|
+
cvs.renderAll();
|
|
9170
|
+
dataUrl = cvs.toDataURL({ format: "png", multiplier });
|
|
9171
|
+
} else {
|
|
9172
|
+
const center = wa2.getCenterPoint();
|
|
9173
|
+
const scaledW = wa2.getScaledWidth();
|
|
9174
|
+
const scaledH = wa2.getScaledHeight();
|
|
9175
|
+
const shapeType = detectShape(wa2.__shapeType);
|
|
9176
|
+
const activeSide = pageSide || wa2.__pageSide || useHistoryStore.getState().currentPage || "front";
|
|
9177
|
+
const baseRx = (wa2.rx || wa2.__radiusOfCanvas || 0) * (wa2.scaleX || 1);
|
|
9178
|
+
const baseRy = (wa2.ry || wa2.__radiusOfCanvas || 0) * (wa2.scaleY || 1);
|
|
9179
|
+
let exportW = scaledW;
|
|
9180
|
+
let exportH = scaledH;
|
|
9181
|
+
let exportLeft = center.x - exportW / 2;
|
|
9182
|
+
let exportTop = center.y - exportH / 2;
|
|
9183
|
+
if (shapeType === "circle") {
|
|
9184
|
+
const diam = Math.min(scaledW, scaledH);
|
|
9185
|
+
exportW = diam;
|
|
9186
|
+
exportH = diam;
|
|
9187
|
+
exportLeft = center.x - diam / 2;
|
|
9188
|
+
exportTop = center.y - diam / 2;
|
|
9189
|
+
}
|
|
9190
|
+
const clipObj = createShapeExportClip(
|
|
9191
|
+
shapeType,
|
|
9192
|
+
{ left: exportLeft, top: exportTop, width: exportW, height: exportH, rx: baseRx, ry: baseRy },
|
|
9193
|
+
activeSide
|
|
9194
|
+
);
|
|
9195
|
+
const prevClip = cvs.clipPath;
|
|
9196
|
+
if (clipObj) {
|
|
9197
|
+
cvs.clipPath = clipObj;
|
|
9198
|
+
}
|
|
9199
|
+
cvs.renderAll();
|
|
9200
|
+
dataUrl = cvs.toDataURL({
|
|
9201
|
+
format: "png",
|
|
9202
|
+
multiplier,
|
|
9203
|
+
left: exportLeft,
|
|
9204
|
+
top: exportTop,
|
|
9205
|
+
width: exportW,
|
|
9206
|
+
height: exportH
|
|
9207
|
+
});
|
|
9208
|
+
cvs.clipPath = prevClip;
|
|
9130
9209
|
}
|
|
9131
|
-
|
|
9132
|
-
|
|
9133
|
-
|
|
9134
|
-
|
|
9135
|
-
|
|
9136
|
-
top: exportTop,
|
|
9137
|
-
width: exportW,
|
|
9138
|
-
height: exportH
|
|
9210
|
+
} finally {
|
|
9211
|
+
cvs.setViewportTransform(prevVt);
|
|
9212
|
+
cvs.setZoom(prevZoom);
|
|
9213
|
+
guidesAndOverlays.forEach((g2, i) => {
|
|
9214
|
+
g2.visible = prevVis[i];
|
|
9139
9215
|
});
|
|
9140
|
-
cvs.
|
|
9216
|
+
cvs.backgroundColor = prevBg;
|
|
9217
|
+
cvs.renderAll();
|
|
9141
9218
|
}
|
|
9142
|
-
guides.forEach((g2, i) => {
|
|
9143
|
-
g2.visible = prevVis[i];
|
|
9144
|
-
});
|
|
9145
|
-
cvs.backgroundColor = prevBg;
|
|
9146
|
-
cvs.renderAll();
|
|
9147
9219
|
return dataUrl;
|
|
9148
9220
|
}
|
|
9149
|
-
async function renderPageDataUrl(json, baseCanvas, multiplier = 1, targetSide) {
|
|
9221
|
+
async function renderPageDataUrl(json, baseCanvas, multiplier = 1, targetSide, includeGuidelines = false) {
|
|
9150
9222
|
if (!json) return "";
|
|
9151
9223
|
try {
|
|
9152
9224
|
const hiddenEl = document.createElement("canvas");
|
|
@@ -9164,7 +9236,7 @@ async function renderPageDataUrl(json, baseCanvas, multiplier = 1, targetSide) {
|
|
|
9164
9236
|
hiddenCanvas.requestRenderAll();
|
|
9165
9237
|
await new Promise((r2) => setTimeout(r2, 60));
|
|
9166
9238
|
hiddenCanvas.renderAll();
|
|
9167
|
-
const url = getWorkareaDataUrl(hiddenCanvas, multiplier, targetSide);
|
|
9239
|
+
const url = getWorkareaDataUrl(hiddenCanvas, multiplier, targetSide, includeGuidelines);
|
|
9168
9240
|
hiddenCanvas.dispose();
|
|
9169
9241
|
hiddenEl.remove();
|
|
9170
9242
|
return url;
|
|
@@ -9173,6 +9245,28 @@ async function renderPageDataUrl(json, baseCanvas, multiplier = 1, targetSide) {
|
|
|
9173
9245
|
return "";
|
|
9174
9246
|
}
|
|
9175
9247
|
}
|
|
9248
|
+
const HISTORY_CUSTOM_PROPERTIES = [
|
|
9249
|
+
"name",
|
|
9250
|
+
"id",
|
|
9251
|
+
"selectable",
|
|
9252
|
+
"evented",
|
|
9253
|
+
"hasControls",
|
|
9254
|
+
"lockMovementX",
|
|
9255
|
+
"lockMovementY",
|
|
9256
|
+
"rx",
|
|
9257
|
+
"ry",
|
|
9258
|
+
"__radiusOfCanvas",
|
|
9259
|
+
"__shapeType",
|
|
9260
|
+
"__pageSide",
|
|
9261
|
+
"__printWidth",
|
|
9262
|
+
"__printHeight",
|
|
9263
|
+
"__printDPI",
|
|
9264
|
+
"__baseScale",
|
|
9265
|
+
"__initialZoom",
|
|
9266
|
+
"__borderRect",
|
|
9267
|
+
"__rotationControlSize",
|
|
9268
|
+
"__showDegree"
|
|
9269
|
+
];
|
|
9176
9270
|
const useHistoryStore = create((set, get) => ({
|
|
9177
9271
|
canvas: null,
|
|
9178
9272
|
currentPage: "front",
|
|
@@ -9182,34 +9276,26 @@ const useHistoryStore = create((set, get) => ({
|
|
|
9182
9276
|
back: { history: [], redo: [] }
|
|
9183
9277
|
},
|
|
9184
9278
|
setCanvas: (canvas2) => {
|
|
9185
|
-
set(
|
|
9279
|
+
set({
|
|
9186
9280
|
canvas: canvas2,
|
|
9281
|
+
isRestoring: false,
|
|
9187
9282
|
pages: {
|
|
9188
9283
|
front: { history: [], redo: [] },
|
|
9189
9284
|
back: { history: [], redo: [] }
|
|
9190
9285
|
}
|
|
9191
|
-
})
|
|
9286
|
+
});
|
|
9192
9287
|
},
|
|
9193
|
-
// setCanvas: (canvas) => {
|
|
9194
|
-
// set((state) => ({
|
|
9195
|
-
// canvas,
|
|
9196
|
-
// pages: {
|
|
9197
|
-
// ...state.pages,
|
|
9198
|
-
// front: {
|
|
9199
|
-
// history: [
|
|
9200
|
-
// { objects: [] }
|
|
9201
|
-
// ],
|
|
9202
|
-
// redo: [],
|
|
9203
|
-
// },
|
|
9204
|
-
// },
|
|
9205
|
-
// }));
|
|
9206
|
-
// },
|
|
9207
9288
|
setPage: (page) => set({ currentPage: page }),
|
|
9208
9289
|
saveState: () => {
|
|
9209
9290
|
const { canvas: canvas2, currentPage, pages, isRestoring } = get();
|
|
9210
9291
|
if (!canvas2 || isRestoring) return;
|
|
9211
|
-
const
|
|
9212
|
-
const
|
|
9292
|
+
const rawJson = canvas2.toObject(HISTORY_CUSTOM_PROPERTIES);
|
|
9293
|
+
const cleanObjects = (rawJson.objects || []).filter((obj) => {
|
|
9294
|
+
const name = obj.name || "";
|
|
9295
|
+
return name !== "watermark" && !name.startsWith("overlay-") && !name.startsWith("guideline-");
|
|
9296
|
+
});
|
|
9297
|
+
const json = { ...rawJson, objects: cleanObjects };
|
|
9298
|
+
const history = pages[currentPage]?.history ?? [];
|
|
9213
9299
|
const last2 = history[history.length - 1];
|
|
9214
9300
|
if (last2 && JSON.stringify(last2) === JSON.stringify(json)) {
|
|
9215
9301
|
return;
|
|
@@ -9225,9 +9311,11 @@ const useHistoryStore = create((set, get) => ({
|
|
|
9225
9311
|
});
|
|
9226
9312
|
},
|
|
9227
9313
|
undo: async () => {
|
|
9228
|
-
const { canvas: canvas2, currentPage, pages } = get();
|
|
9314
|
+
const { canvas: canvas2, currentPage, pages, isRestoring } = get();
|
|
9315
|
+
if (!canvas2 || isRestoring) return;
|
|
9229
9316
|
const page = pages[currentPage];
|
|
9230
|
-
if (!
|
|
9317
|
+
if (!page || page.history.length <= 1) return;
|
|
9318
|
+
canvas2.discardActiveObject();
|
|
9231
9319
|
const current = page.history.at(-1);
|
|
9232
9320
|
const previous = page.history.at(-2);
|
|
9233
9321
|
set({
|
|
@@ -9240,14 +9328,28 @@ const useHistoryStore = create((set, get) => ({
|
|
|
9240
9328
|
}
|
|
9241
9329
|
}
|
|
9242
9330
|
});
|
|
9243
|
-
|
|
9244
|
-
|
|
9245
|
-
|
|
9331
|
+
try {
|
|
9332
|
+
await loadPageToCanvas(canvas2, previous, currentPage);
|
|
9333
|
+
canvas2.requestRenderAll();
|
|
9334
|
+
const editorStore = useEditorStore.getState();
|
|
9335
|
+
if (editorStore.pages) {
|
|
9336
|
+
editorStore.setPages({
|
|
9337
|
+
...editorStore.pages,
|
|
9338
|
+
[currentPage]: previous
|
|
9339
|
+
});
|
|
9340
|
+
}
|
|
9341
|
+
} finally {
|
|
9342
|
+
setTimeout(() => {
|
|
9343
|
+
set({ isRestoring: false });
|
|
9344
|
+
}, 80);
|
|
9345
|
+
}
|
|
9246
9346
|
},
|
|
9247
9347
|
redoAction: async () => {
|
|
9248
|
-
const { canvas: canvas2, currentPage, pages } = get();
|
|
9348
|
+
const { canvas: canvas2, currentPage, pages, isRestoring } = get();
|
|
9349
|
+
if (!canvas2 || isRestoring) return;
|
|
9249
9350
|
const page = pages[currentPage];
|
|
9250
|
-
if (!
|
|
9351
|
+
if (!page || page.redo.length === 0) return;
|
|
9352
|
+
canvas2.discardActiveObject();
|
|
9251
9353
|
const next = page.redo[0];
|
|
9252
9354
|
set({
|
|
9253
9355
|
isRestoring: true,
|
|
@@ -9259,9 +9361,21 @@ const useHistoryStore = create((set, get) => ({
|
|
|
9259
9361
|
}
|
|
9260
9362
|
}
|
|
9261
9363
|
});
|
|
9262
|
-
|
|
9263
|
-
|
|
9264
|
-
|
|
9364
|
+
try {
|
|
9365
|
+
await loadPageToCanvas(canvas2, next, currentPage);
|
|
9366
|
+
canvas2.requestRenderAll();
|
|
9367
|
+
const editorStore = useEditorStore.getState();
|
|
9368
|
+
if (editorStore.pages) {
|
|
9369
|
+
editorStore.setPages({
|
|
9370
|
+
...editorStore.pages,
|
|
9371
|
+
[currentPage]: next
|
|
9372
|
+
});
|
|
9373
|
+
}
|
|
9374
|
+
} finally {
|
|
9375
|
+
setTimeout(() => {
|
|
9376
|
+
set({ isRestoring: false });
|
|
9377
|
+
}, 80);
|
|
9378
|
+
}
|
|
9265
9379
|
}
|
|
9266
9380
|
}));
|
|
9267
9381
|
const FontService = {
|
|
@@ -9596,6 +9710,189 @@ function exportDesignJSON(canvas2, workarea) {
|
|
|
9596
9710
|
});
|
|
9597
9711
|
triggerDownload(URL.createObjectURL(blob), "design.json");
|
|
9598
9712
|
}
|
|
9713
|
+
const ShapeService = {
|
|
9714
|
+
async getAll(data) {
|
|
9715
|
+
const res = await apiFetch(
|
|
9716
|
+
`/api/cliparts?page=${data.page}&limit=${data.limit}&category=${data.category}&status=true`,
|
|
9717
|
+
{
|
|
9718
|
+
method: "GET"
|
|
9719
|
+
}
|
|
9720
|
+
);
|
|
9721
|
+
return {
|
|
9722
|
+
items: res.data || []
|
|
9723
|
+
};
|
|
9724
|
+
},
|
|
9725
|
+
async getAllExternal(data) {
|
|
9726
|
+
const res = await apiFetch(
|
|
9727
|
+
`/api/external/images?page=${data.page}&limit=${data.limit}&keyType=${data.keyType}&status=true${data.query ? `&query=${data.query}` : ""}`,
|
|
9728
|
+
{
|
|
9729
|
+
method: "GET"
|
|
9730
|
+
}
|
|
9731
|
+
);
|
|
9732
|
+
return {
|
|
9733
|
+
items: res.data || []
|
|
9734
|
+
};
|
|
9735
|
+
},
|
|
9736
|
+
async getTextTemplates(data) {
|
|
9737
|
+
const res = await apiFetch(
|
|
9738
|
+
`/api/text-templates?page=${data.page}&limit=${data.limit}&status=true`,
|
|
9739
|
+
{
|
|
9740
|
+
method: "GET"
|
|
9741
|
+
}
|
|
9742
|
+
);
|
|
9743
|
+
return {
|
|
9744
|
+
items: res.data || []
|
|
9745
|
+
};
|
|
9746
|
+
},
|
|
9747
|
+
async getTextTemplateById(id) {
|
|
9748
|
+
const res = await apiFetch(
|
|
9749
|
+
`/api/text-templates/${id}`,
|
|
9750
|
+
{
|
|
9751
|
+
method: "GET"
|
|
9752
|
+
}
|
|
9753
|
+
);
|
|
9754
|
+
return res.data || null;
|
|
9755
|
+
},
|
|
9756
|
+
async updateTextTemplate(id, body) {
|
|
9757
|
+
const res = await apiFetch(`/api/text-templates/${id}`, {
|
|
9758
|
+
method: "PUT",
|
|
9759
|
+
body
|
|
9760
|
+
});
|
|
9761
|
+
return res;
|
|
9762
|
+
},
|
|
9763
|
+
async convertImages(url) {
|
|
9764
|
+
const res = await apiFetch(`/api/convert-images`, {
|
|
9765
|
+
method: "POST",
|
|
9766
|
+
body: { file: url }
|
|
9767
|
+
});
|
|
9768
|
+
return res;
|
|
9769
|
+
}
|
|
9770
|
+
};
|
|
9771
|
+
async function getBase64ImageFromURL(url) {
|
|
9772
|
+
if (!url) return url;
|
|
9773
|
+
if (url.startsWith("data:image/png") || url.startsWith("data:image/jpeg")) {
|
|
9774
|
+
return url;
|
|
9775
|
+
}
|
|
9776
|
+
const cleanUrl = url.replace(/&/g, "&").trim();
|
|
9777
|
+
const fullUrl = cleanUrl.startsWith("http://") || cleanUrl.startsWith("https://") || cleanUrl.startsWith("blob:") || cleanUrl.startsWith("data:") ? cleanUrl : cleanUrl.startsWith("/") ? `https://upload.vibrantprint.com${cleanUrl}` : `https://upload.vibrantprint.com/${cleanUrl}`;
|
|
9778
|
+
if (typeof window === "undefined" || typeof document === "undefined") {
|
|
9779
|
+
return fullUrl;
|
|
9780
|
+
}
|
|
9781
|
+
try {
|
|
9782
|
+
const dataUrl = await new Promise((resolve, reject) => {
|
|
9783
|
+
const timer = setTimeout(() => reject(new Error("Image load timeout")), 4e3);
|
|
9784
|
+
const img = new Image();
|
|
9785
|
+
img.crossOrigin = "anonymous";
|
|
9786
|
+
img.onload = () => {
|
|
9787
|
+
clearTimeout(timer);
|
|
9788
|
+
try {
|
|
9789
|
+
const cvs = document.createElement("canvas");
|
|
9790
|
+
cvs.width = img.naturalWidth || img.width || 1;
|
|
9791
|
+
cvs.height = img.naturalHeight || img.height || 1;
|
|
9792
|
+
const ctx = cvs.getContext("2d");
|
|
9793
|
+
if (!ctx) return reject(new Error("No 2d context"));
|
|
9794
|
+
ctx.drawImage(img, 0, 0);
|
|
9795
|
+
resolve(cvs.toDataURL("image/png"));
|
|
9796
|
+
} catch (e3) {
|
|
9797
|
+
reject(e3);
|
|
9798
|
+
}
|
|
9799
|
+
};
|
|
9800
|
+
img.onerror = (e3) => {
|
|
9801
|
+
clearTimeout(timer);
|
|
9802
|
+
reject(e3);
|
|
9803
|
+
};
|
|
9804
|
+
img.src = fullUrl;
|
|
9805
|
+
});
|
|
9806
|
+
if (dataUrl && (dataUrl.startsWith("data:image/png") || dataUrl.startsWith("data:image/jpeg"))) {
|
|
9807
|
+
return dataUrl;
|
|
9808
|
+
}
|
|
9809
|
+
} catch {
|
|
9810
|
+
}
|
|
9811
|
+
try {
|
|
9812
|
+
const controller = new AbortController();
|
|
9813
|
+
const fetchTimer = setTimeout(() => controller.abort(), 4e3);
|
|
9814
|
+
const res = await fetch(fullUrl, { signal: controller.signal });
|
|
9815
|
+
clearTimeout(fetchTimer);
|
|
9816
|
+
const blob = await res.blob();
|
|
9817
|
+
const dataUrl = await new Promise((resolve, reject) => {
|
|
9818
|
+
const reader = new FileReader();
|
|
9819
|
+
reader.onloadend = () => {
|
|
9820
|
+
const result = reader.result;
|
|
9821
|
+
if (result && !result.startsWith("data:image/png") && !result.startsWith("data:image/jpeg")) {
|
|
9822
|
+
const img = new Image();
|
|
9823
|
+
const imgTimer = setTimeout(() => resolve(result), 3e3);
|
|
9824
|
+
img.onload = () => {
|
|
9825
|
+
clearTimeout(imgTimer);
|
|
9826
|
+
const cvs = document.createElement("canvas");
|
|
9827
|
+
cvs.width = img.naturalWidth || img.width || 1;
|
|
9828
|
+
cvs.height = img.naturalHeight || img.height || 1;
|
|
9829
|
+
const ctx = cvs.getContext("2d");
|
|
9830
|
+
if (ctx) {
|
|
9831
|
+
ctx.drawImage(img, 0, 0);
|
|
9832
|
+
resolve(cvs.toDataURL("image/png"));
|
|
9833
|
+
} else {
|
|
9834
|
+
resolve(result);
|
|
9835
|
+
}
|
|
9836
|
+
};
|
|
9837
|
+
img.onerror = () => {
|
|
9838
|
+
clearTimeout(imgTimer);
|
|
9839
|
+
resolve(result);
|
|
9840
|
+
};
|
|
9841
|
+
img.src = result;
|
|
9842
|
+
} else {
|
|
9843
|
+
resolve(result);
|
|
9844
|
+
}
|
|
9845
|
+
};
|
|
9846
|
+
reader.onerror = reject;
|
|
9847
|
+
reader.readAsDataURL(blob);
|
|
9848
|
+
});
|
|
9849
|
+
return dataUrl;
|
|
9850
|
+
} catch {
|
|
9851
|
+
try {
|
|
9852
|
+
const backendRes = await ShapeService.convertImages(fullUrl);
|
|
9853
|
+
const converted = backendRes?.url || backendRes?.dataUrl;
|
|
9854
|
+
if (converted && (converted.startsWith("data:image/png") || converted.startsWith("data:image/jpeg"))) {
|
|
9855
|
+
return converted;
|
|
9856
|
+
}
|
|
9857
|
+
} catch {
|
|
9858
|
+
}
|
|
9859
|
+
return fullUrl;
|
|
9860
|
+
}
|
|
9861
|
+
}
|
|
9862
|
+
async function addImageToCanvas(url) {
|
|
9863
|
+
const canvas2 = useEditorStore.getState().canvas;
|
|
9864
|
+
if (!canvas2) return;
|
|
9865
|
+
const workarea = canvas2.getObjects().find((o2) => o2.name === "workarea");
|
|
9866
|
+
if (!workarea) return;
|
|
9867
|
+
let finalUrl = url;
|
|
9868
|
+
try {
|
|
9869
|
+
const b64 = await getBase64ImageFromURL(url);
|
|
9870
|
+
if (b64 && (b64.startsWith("data:image/png") || b64.startsWith("data:image/jpeg"))) {
|
|
9871
|
+
finalUrl = b64;
|
|
9872
|
+
}
|
|
9873
|
+
} catch {
|
|
9874
|
+
}
|
|
9875
|
+
oa.fromURL(finalUrl, { crossOrigin: "anonymous" }).then((img) => {
|
|
9876
|
+
const workareaCenter = workarea.getCenterPoint();
|
|
9877
|
+
const workareaBounds = workarea.getBoundingRect();
|
|
9878
|
+
const maxWidth = workareaBounds.width * 0.7;
|
|
9879
|
+
const maxHeight = workareaBounds.height * 0.7;
|
|
9880
|
+
img.scale(Math.min(maxWidth / img.width, maxHeight / img.height));
|
|
9881
|
+
img.set({
|
|
9882
|
+
left: workareaCenter.x,
|
|
9883
|
+
top: workareaCenter.y,
|
|
9884
|
+
originX: "center",
|
|
9885
|
+
originY: "center",
|
|
9886
|
+
selectable: true,
|
|
9887
|
+
hasControls: true
|
|
9888
|
+
});
|
|
9889
|
+
canvas2.add(img);
|
|
9890
|
+
canvas2.setActiveObject(img);
|
|
9891
|
+
canvas2.requestRenderAll();
|
|
9892
|
+
}).catch((err) => {
|
|
9893
|
+
console.warn("Fabric failed to load image from URL:", url, err);
|
|
9894
|
+
});
|
|
9895
|
+
}
|
|
9599
9896
|
function captureWorkareaSVG(canvas2, workarea, pageSide) {
|
|
9600
9897
|
const targetWorkarea = workarea || canvas2.getObjects().find((obj) => obj.name === "workarea");
|
|
9601
9898
|
if (!targetWorkarea) return null;
|
|
@@ -9675,32 +9972,23 @@ async function processSVGWithEmbeds(svg) {
|
|
|
9675
9972
|
];
|
|
9676
9973
|
const urls = [
|
|
9677
9974
|
...new Set(
|
|
9678
|
-
matches.map((m2) => m2[1]).filter((url) => url && (url.startsWith("http") || url.startsWith("blob:")))
|
|
9975
|
+
matches.map((m2) => m2[1]).filter((url) => url && (url.startsWith("http") || url.startsWith("blob:") || url.startsWith("data:") && !url.startsWith("data:image/png") && !url.startsWith("data:image/jpeg")))
|
|
9679
9976
|
)
|
|
9680
9977
|
];
|
|
9681
9978
|
if (urls.length === 0) return svg;
|
|
9682
9979
|
await Promise.all(
|
|
9683
9980
|
urls.map(async (url) => {
|
|
9684
|
-
|
|
9685
|
-
|
|
9981
|
+
try {
|
|
9982
|
+
const base64 = await getBase64ImageFromURL(url);
|
|
9983
|
+
if (base64 && (base64.startsWith("data:image/png") || base64.startsWith("data:image/jpeg"))) {
|
|
9984
|
+
result = result.split(`"${url}"`).join(`"${base64}"`);
|
|
9985
|
+
}
|
|
9986
|
+
} catch {
|
|
9987
|
+
}
|
|
9686
9988
|
})
|
|
9687
9989
|
);
|
|
9688
9990
|
return result;
|
|
9689
9991
|
}
|
|
9690
|
-
async function fetchImageAsBase64(url) {
|
|
9691
|
-
try {
|
|
9692
|
-
const res = await fetch(url, { mode: "cors" });
|
|
9693
|
-
const blob = await res.blob();
|
|
9694
|
-
return new Promise((resolve, reject) => {
|
|
9695
|
-
const reader = new FileReader();
|
|
9696
|
-
reader.onloadend = () => resolve(reader.result);
|
|
9697
|
-
reader.onerror = reject;
|
|
9698
|
-
reader.readAsDataURL(blob);
|
|
9699
|
-
});
|
|
9700
|
-
} catch {
|
|
9701
|
-
return null;
|
|
9702
|
-
}
|
|
9703
|
-
}
|
|
9704
9992
|
var commonjsGlobal = typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : {};
|
|
9705
9993
|
function getDefaultExportFromCjs(x2) {
|
|
9706
9994
|
return x2 && x2.__esModule && Object.prototype.hasOwnProperty.call(x2, "default") ? x2["default"] : x2;
|
|
@@ -99899,7 +100187,16 @@ async function convertPdfToDeviceCMYK(rawBuffer) {
|
|
|
99899
100187
|
});
|
|
99900
100188
|
};
|
|
99901
100189
|
for (const [, obj] of context.enumerateIndirectObjects()) {
|
|
100190
|
+
const dict = obj.dict || (obj instanceof PDFDict ? obj : null);
|
|
100191
|
+
const subtype = dict?.get(PDFName.of("Subtype"))?.toString();
|
|
100192
|
+
const type = dict?.get(PDFName.of("Type"))?.toString();
|
|
100193
|
+
if (subtype === "/Image") {
|
|
100194
|
+
continue;
|
|
100195
|
+
}
|
|
99902
100196
|
if (obj && obj.contents && obj.dict) {
|
|
100197
|
+
if (type === "/Font" || type === "/FontDescriptor" || type === "/Metadata") {
|
|
100198
|
+
continue;
|
|
100199
|
+
}
|
|
99903
100200
|
let rawBytes = obj.contents;
|
|
99904
100201
|
const filterStr = obj.dict.get(PDFName.of("Filter"))?.toString() || "";
|
|
99905
100202
|
const isFlate = filterStr.includes("FlateDecode");
|
|
@@ -99921,7 +100218,7 @@ async function convertPdfToDeviceCMYK(rawBuffer) {
|
|
|
99921
100218
|
} catch {
|
|
99922
100219
|
}
|
|
99923
100220
|
}
|
|
99924
|
-
if (obj instanceof PDFDict) {
|
|
100221
|
+
if (obj instanceof PDFDict && subtype !== "/Image" && type !== "/Font") {
|
|
99925
100222
|
for (const [k2, v2] of obj.entries()) {
|
|
99926
100223
|
if (v2?.toString() === "/DeviceRGB") obj.set(k2, PDFName.of("DeviceCMYK"));
|
|
99927
100224
|
}
|
|
@@ -99943,7 +100240,6 @@ async function convertPdfToDeviceCMYK(rawBuffer) {
|
|
|
99943
100240
|
p2.node.set(PDFName.of("Resources"), res);
|
|
99944
100241
|
}
|
|
99945
100242
|
res.set(PDFName.of("ColorSpace"), context.obj({
|
|
99946
|
-
DefaultRGB: PDFName.of("DeviceCMYK"),
|
|
99947
100243
|
DefaultCMYK: PDFName.of("DeviceCMYK")
|
|
99948
100244
|
}));
|
|
99949
100245
|
}
|
|
@@ -100023,6 +100319,20 @@ async function registerFontsInPdfMake(fontList) {
|
|
|
100023
100319
|
function sanitizeSvgForPdf(svg) {
|
|
100024
100320
|
let result = svg;
|
|
100025
100321
|
result = result.replace(/<\?xml[^>]*\?>/gi, "").replace(/<!DOCTYPE[^>]*>/gi, "").trim();
|
|
100322
|
+
if (!result.includes("xmlns:xlink=") && (result.includes("xlink:href") || result.includes("<image"))) {
|
|
100323
|
+
result = result.replace(/<svg\b([^>]*)>/i, '<svg$1 xmlns:xlink="http://www.w3.org/1999/xlink">');
|
|
100324
|
+
}
|
|
100325
|
+
result = result.replace(/<image\b([^>]*)>/gi, (match, attrs) => {
|
|
100326
|
+
let newAttrs = attrs;
|
|
100327
|
+
const xlinkMatch = attrs.match(/xlink:href="([^"]*)"/i);
|
|
100328
|
+
const hrefMatch = attrs.match(/\bhref="([^"]*)"/i);
|
|
100329
|
+
if (xlinkMatch && !hrefMatch) {
|
|
100330
|
+
newAttrs += ` href="${xlinkMatch[1]}"`;
|
|
100331
|
+
} else if (hrefMatch && !xlinkMatch) {
|
|
100332
|
+
newAttrs += ` xlink:href="${hrefMatch[1]}"`;
|
|
100333
|
+
}
|
|
100334
|
+
return `<image${newAttrs}>`;
|
|
100335
|
+
});
|
|
100026
100336
|
result = result.replace(/font-family="([^"]+)"/g, (_2, p1) => {
|
|
100027
100337
|
const first = p1.split(",")[0].replace(/"/g, "").replace(/['"]/g, "").trim();
|
|
100028
100338
|
return `font-family="${first}"`;
|
|
@@ -100069,17 +100379,114 @@ async function generateAndDownloadPDF(pages, width, height, filename = `design-$
|
|
|
100069
100379
|
}
|
|
100070
100380
|
}
|
|
100071
100381
|
if (rawBuffer && rawBuffer.byteLength > 0) {
|
|
100072
|
-
|
|
100073
|
-
|
|
100074
|
-
|
|
100075
|
-
|
|
100076
|
-
|
|
100382
|
+
try {
|
|
100383
|
+
const cmykBytes = await convertPdfToDeviceCMYK(rawBuffer);
|
|
100384
|
+
const cmykBlob = new Blob([cmykBytes], { type: "application/pdf" });
|
|
100385
|
+
triggerDownload(URL.createObjectURL(cmykBlob), filename);
|
|
100386
|
+
return;
|
|
100387
|
+
} catch (cmykErr) {
|
|
100388
|
+
console.warn("CMYK tagging failed, falling back to direct clean PDF buffer:", cmykErr);
|
|
100389
|
+
const fallbackBlob = new Blob([rawBuffer], { type: "application/pdf" });
|
|
100390
|
+
triggerDownload(URL.createObjectURL(fallbackBlob), filename);
|
|
100391
|
+
return;
|
|
100392
|
+
}
|
|
100393
|
+
}
|
|
100394
|
+
if (typeof doc.getBlob === "function") {
|
|
100395
|
+
const blob = await doc.getBlob();
|
|
100396
|
+
triggerDownload(URL.createObjectURL(blob), filename);
|
|
100397
|
+
return;
|
|
100077
100398
|
}
|
|
100399
|
+
doc.download(filename);
|
|
100078
100400
|
} catch (err) {
|
|
100079
|
-
console.error("
|
|
100401
|
+
console.error("PDF generation/download error, trying fallback:", err);
|
|
100402
|
+
try {
|
|
100403
|
+
if (typeof doc.getBlob === "function") {
|
|
100404
|
+
const blob = await doc.getBlob();
|
|
100405
|
+
triggerDownload(URL.createObjectURL(blob), filename);
|
|
100406
|
+
return;
|
|
100407
|
+
}
|
|
100408
|
+
} catch {
|
|
100409
|
+
}
|
|
100080
100410
|
doc.download(filename);
|
|
100081
100411
|
}
|
|
100082
100412
|
}
|
|
100413
|
+
function getAllCanvasImages(container) {
|
|
100414
|
+
const list = [];
|
|
100415
|
+
const objs = typeof container.getObjects === "function" ? container.getObjects() : [];
|
|
100416
|
+
for (const o2 of objs) {
|
|
100417
|
+
if (o2.type === "image" || o2.type === "fabricImage") {
|
|
100418
|
+
list.push(o2);
|
|
100419
|
+
} else if (o2.type === "group" && typeof o2.getObjects === "function") {
|
|
100420
|
+
list.push(...getAllCanvasImages(o2));
|
|
100421
|
+
}
|
|
100422
|
+
}
|
|
100423
|
+
return list;
|
|
100424
|
+
}
|
|
100425
|
+
async function inlineImageForExport(obj) {
|
|
100426
|
+
try {
|
|
100427
|
+
const el = obj._element || obj._originalElement;
|
|
100428
|
+
const currentSrc = el?.src || obj.src || (typeof obj.getSrc === "function" ? obj.getSrc() : "");
|
|
100429
|
+
if (currentSrc.startsWith("data:image/png") || currentSrc.startsWith("data:image/jpeg")) {
|
|
100430
|
+
return;
|
|
100431
|
+
}
|
|
100432
|
+
let base64 = null;
|
|
100433
|
+
if (el) {
|
|
100434
|
+
try {
|
|
100435
|
+
if (typeof el.toDataURL === "function") {
|
|
100436
|
+
base64 = el.toDataURL("image/png");
|
|
100437
|
+
} else {
|
|
100438
|
+
const w2 = el.naturalWidth || el.width;
|
|
100439
|
+
const h2 = el.naturalHeight || el.height;
|
|
100440
|
+
if (w2 > 0 && h2 > 0) {
|
|
100441
|
+
const cvs = document.createElement("canvas");
|
|
100442
|
+
cvs.width = w2;
|
|
100443
|
+
cvs.height = h2;
|
|
100444
|
+
const ctx = cvs.getContext("2d");
|
|
100445
|
+
if (ctx) {
|
|
100446
|
+
ctx.drawImage(el, 0, 0);
|
|
100447
|
+
base64 = cvs.toDataURL("image/png");
|
|
100448
|
+
}
|
|
100449
|
+
}
|
|
100450
|
+
}
|
|
100451
|
+
} catch {
|
|
100452
|
+
}
|
|
100453
|
+
}
|
|
100454
|
+
if (!base64 || !base64.startsWith("data:image/png") && !base64.startsWith("data:image/jpeg")) {
|
|
100455
|
+
if (currentSrc) {
|
|
100456
|
+
base64 = await getBase64ImageFromURL(currentSrc);
|
|
100457
|
+
}
|
|
100458
|
+
}
|
|
100459
|
+
if (base64 && (base64.startsWith("data:image/png") || base64.startsWith("data:image/jpeg"))) {
|
|
100460
|
+
if (obj._element) {
|
|
100461
|
+
obj._element.src = base64;
|
|
100462
|
+
if (typeof obj._element.setAttribute === "function") {
|
|
100463
|
+
try {
|
|
100464
|
+
obj._element.setAttribute("src", base64);
|
|
100465
|
+
} catch {
|
|
100466
|
+
}
|
|
100467
|
+
}
|
|
100468
|
+
if (typeof obj._element.decode === "function") {
|
|
100469
|
+
try {
|
|
100470
|
+
await obj._element.decode();
|
|
100471
|
+
} catch {
|
|
100472
|
+
}
|
|
100473
|
+
}
|
|
100474
|
+
}
|
|
100475
|
+
if (obj._originalElement) {
|
|
100476
|
+
obj._originalElement.src = base64;
|
|
100477
|
+
if (typeof obj._originalElement.setAttribute === "function") {
|
|
100478
|
+
try {
|
|
100479
|
+
obj._originalElement.setAttribute("src", base64);
|
|
100480
|
+
} catch {
|
|
100481
|
+
}
|
|
100482
|
+
}
|
|
100483
|
+
}
|
|
100484
|
+
obj.src = base64;
|
|
100485
|
+
}
|
|
100486
|
+
} catch (e3) {
|
|
100487
|
+
console.warn("Failed to inline image for export:", e3);
|
|
100488
|
+
}
|
|
100489
|
+
}
|
|
100083
100490
|
async function downloadCanvasWorkarea(canvas2, type, scale2 = 1, pages, currentPageSide, dpi) {
|
|
100084
100491
|
const workarea = canvas2.getObjects().find((obj) => obj.name === "workarea");
|
|
100085
100492
|
if (!workarea) {
|
|
@@ -100090,6 +100497,10 @@ async function downloadCanvasWorkarea(canvas2, type, scale2 = 1, pages, currentP
|
|
|
100090
100497
|
exportDesignJSON(canvas2, workarea);
|
|
100091
100498
|
return;
|
|
100092
100499
|
}
|
|
100500
|
+
const activeImages = getAllCanvasImages(canvas2);
|
|
100501
|
+
for (const imgObj of activeImages) {
|
|
100502
|
+
await inlineImageForExport(imgObj);
|
|
100503
|
+
}
|
|
100093
100504
|
const hiddenEl = document.createElement("canvas");
|
|
100094
100505
|
hiddenEl.width = canvas2.width;
|
|
100095
100506
|
hiddenEl.height = canvas2.height;
|
|
@@ -100111,6 +100522,8 @@ async function downloadCanvasWorkarea(canvas2, type, scale2 = 1, pages, currentP
|
|
|
100111
100522
|
"cropY",
|
|
100112
100523
|
"rx",
|
|
100113
100524
|
"ry",
|
|
100525
|
+
"crossOrigin",
|
|
100526
|
+
"src",
|
|
100114
100527
|
"__radiusOfCanvas",
|
|
100115
100528
|
"__shapeType",
|
|
100116
100529
|
"__pageSide"
|
|
@@ -100120,8 +100533,31 @@ async function downloadCanvasWorkarea(canvas2, type, scale2 = 1, pages, currentP
|
|
|
100120
100533
|
const activeSide = pageSide || target?.__pageSide || currentSide;
|
|
100121
100534
|
const curW = target.getScaledWidth ? target.getScaledWidth() : target.width * (target.scaleX || 1);
|
|
100122
100535
|
const curH = target.getScaledHeight ? target.getScaledHeight() : target.height * (target.scaleY || 1);
|
|
100123
|
-
const
|
|
100124
|
-
const
|
|
100536
|
+
const center = target.getCenterPoint ? target.getCenterPoint() : { x: (target.left || 0) + curW / 2, y: (target.top || 0) + curH / 2 };
|
|
100537
|
+
const bgImg = hiddenCanvas.getObjects().find((o2) => o2.name === "bg-image");
|
|
100538
|
+
if (bgImg) {
|
|
100539
|
+
bgImg.set({
|
|
100540
|
+
left: center.x,
|
|
100541
|
+
top: center.y,
|
|
100542
|
+
originX: "center",
|
|
100543
|
+
originY: "center"
|
|
100544
|
+
});
|
|
100545
|
+
bgImg.setCoords();
|
|
100546
|
+
const shapeType2 = detectShape(target.__shapeType);
|
|
100547
|
+
const baseRx2 = (target.rx || target.__radiusOfCanvas || 0) * (target.scaleX || 1);
|
|
100548
|
+
const baseRy2 = (target.ry || target.__radiusOfCanvas || 0) * (target.scaleY || 1);
|
|
100549
|
+
const exportLeft2 = Math.round(center.x - curW / 2);
|
|
100550
|
+
const exportTop2 = Math.round(center.y - curH / 2);
|
|
100551
|
+
const clip2 = createShapeExportClip(
|
|
100552
|
+
shapeType2,
|
|
100553
|
+
{ left: exportLeft2, top: exportTop2, width: curW, height: curH, rx: baseRx2, ry: baseRy2 },
|
|
100554
|
+
activeSide
|
|
100555
|
+
);
|
|
100556
|
+
if (clip2) {
|
|
100557
|
+
clip2.absolutePositioned = true;
|
|
100558
|
+
bgImg.clipPath = clip2;
|
|
100559
|
+
}
|
|
100560
|
+
}
|
|
100125
100561
|
if (format === "jpeg") hiddenCanvas.backgroundColor = "#ffffff";
|
|
100126
100562
|
const wm = createWatermark(target, activeSide);
|
|
100127
100563
|
hiddenCanvas.add(wm);
|
|
@@ -100135,35 +100571,40 @@ async function downloadCanvasWorkarea(canvas2, type, scale2 = 1, pages, currentP
|
|
|
100135
100571
|
const shapeType = detectShape(target.__shapeType);
|
|
100136
100572
|
const baseRx = (target.rx || target.__radiusOfCanvas || 0) * (target.scaleX || 1);
|
|
100137
100573
|
const baseRy = (target.ry || target.__radiusOfCanvas || 0) * (target.scaleY || 1);
|
|
100138
|
-
let exportLeft = curLeft;
|
|
100139
|
-
let exportTop = curTop;
|
|
100140
100574
|
let exportW = curW;
|
|
100141
100575
|
let exportH = curH;
|
|
100576
|
+
let exportLeft = center.x - exportW / 2;
|
|
100577
|
+
let exportTop = center.y - exportH / 2;
|
|
100142
100578
|
if (shapeType === "circle") {
|
|
100143
100579
|
const diam = Math.min(curW, curH);
|
|
100144
|
-
exportLeft = curLeft + (curW - diam) / 2;
|
|
100145
|
-
exportTop = curTop + (curH - diam) / 2;
|
|
100146
100580
|
exportW = diam;
|
|
100147
100581
|
exportH = diam;
|
|
100582
|
+
exportLeft = center.x - diam / 2;
|
|
100583
|
+
exportTop = center.y - diam / 2;
|
|
100148
100584
|
}
|
|
100149
100585
|
const exportClip = createShapeExportClip(
|
|
100150
100586
|
shapeType,
|
|
100151
|
-
{ left:
|
|
100587
|
+
{ left: exportLeft, top: exportTop, width: exportW, height: exportH, rx: baseRx, ry: baseRy },
|
|
100152
100588
|
activeSide
|
|
100153
100589
|
);
|
|
100154
100590
|
if (exportClip) {
|
|
100155
100591
|
hiddenCanvas.clipPath = exportClip;
|
|
100156
100592
|
}
|
|
100157
|
-
|
|
100158
|
-
|
|
100159
|
-
|
|
100160
|
-
|
|
100161
|
-
|
|
100162
|
-
|
|
100163
|
-
|
|
100164
|
-
|
|
100165
|
-
|
|
100166
|
-
|
|
100593
|
+
let dataURL = "";
|
|
100594
|
+
try {
|
|
100595
|
+
dataURL = hiddenCanvas.toDataURL({
|
|
100596
|
+
format,
|
|
100597
|
+
quality: 1,
|
|
100598
|
+
multiplier: scale2,
|
|
100599
|
+
left: exportLeft,
|
|
100600
|
+
top: exportTop,
|
|
100601
|
+
width: exportW,
|
|
100602
|
+
height: exportH,
|
|
100603
|
+
enableRetinaScaling: false
|
|
100604
|
+
});
|
|
100605
|
+
} catch (taintErr) {
|
|
100606
|
+
console.warn("hiddenCanvas.toDataURL failed (tainted canvas):", taintErr);
|
|
100607
|
+
}
|
|
100167
100608
|
hiddenCanvas.clipPath = void 0;
|
|
100168
100609
|
hiddenCanvas.remove(wm);
|
|
100169
100610
|
if (format === "jpeg") hiddenCanvas.backgroundColor = "";
|
|
@@ -100222,18 +100663,41 @@ async function downloadCanvasWorkarea(canvas2, type, scale2 = 1, pages, currentP
|
|
|
100222
100663
|
if (curWorkarea) {
|
|
100223
100664
|
curWorkarea.__pageSide = page.side;
|
|
100224
100665
|
}
|
|
100666
|
+
const canvasImages = getAllCanvasImages(hiddenCanvas);
|
|
100667
|
+
for (const imgObj of canvasImages) {
|
|
100668
|
+
await inlineImageForExport(imgObj);
|
|
100669
|
+
}
|
|
100670
|
+
hiddenCanvas.renderAll();
|
|
100225
100671
|
const rawSvg = captureWithWatermark("svg", curWorkarea, page.side);
|
|
100226
100672
|
if (rawSvg) {
|
|
100227
100673
|
try {
|
|
100228
100674
|
const processed = await processSVGWithEmbeds(rawSvg);
|
|
100229
100675
|
const sanitized = sanitizeSvgForPdf(processed);
|
|
100230
|
-
|
|
100676
|
+
const hasUnembedded = /<image\b[^>]*(?:href="https?:\/\/|href="blob:)[^>]*>/i.test(sanitized);
|
|
100677
|
+
if (hasUnembedded) {
|
|
100678
|
+
const rasterImage = captureWithWatermark("png", curWorkarea, page.side);
|
|
100679
|
+
if (rasterImage) {
|
|
100680
|
+
vectorPages.push({ image: rasterImage });
|
|
100681
|
+
} else {
|
|
100682
|
+
vectorPages.push({ svg: sanitized });
|
|
100683
|
+
}
|
|
100684
|
+
} else {
|
|
100685
|
+
vectorPages.push({ svg: sanitized });
|
|
100686
|
+
}
|
|
100231
100687
|
} catch (err) {
|
|
100232
100688
|
console.error("Vector SVG processing failed, falling back to image:", err);
|
|
100233
|
-
|
|
100689
|
+
const rasterImage = captureWithWatermark("png", curWorkarea, page.side);
|
|
100690
|
+
if (rasterImage) {
|
|
100691
|
+
vectorPages.push({ image: rasterImage });
|
|
100692
|
+
} else {
|
|
100693
|
+
vectorPages.push({ svg: rawSvg });
|
|
100694
|
+
}
|
|
100234
100695
|
}
|
|
100235
100696
|
} else {
|
|
100236
|
-
|
|
100697
|
+
const rasterImage = captureWithWatermark("png", curWorkarea, page.side);
|
|
100698
|
+
if (rasterImage) {
|
|
100699
|
+
vectorPages.push({ image: rasterImage });
|
|
100700
|
+
}
|
|
100237
100701
|
}
|
|
100238
100702
|
}
|
|
100239
100703
|
await generateAndDownloadPDF(vectorPages, workarea.width, workarea.height);
|
|
@@ -100430,15 +100894,17 @@ function DownloadPopover({ onClose }) {
|
|
|
100430
100894
|
}
|
|
100431
100895
|
);
|
|
100432
100896
|
}
|
|
100433
|
-
function PreviewModal({ onClose }) {
|
|
100897
|
+
function PreviewModal({ onClose, onUpload }) {
|
|
100434
100898
|
const canvas2 = useEditorStore((s2) => s2.canvas);
|
|
100435
100899
|
const { currentPage } = useHistoryStore();
|
|
100436
100900
|
const productData = useProductStore((s2) => s2.productData);
|
|
100437
100901
|
const isTwoSided = productData?.optionsMap?.printing === "4/4";
|
|
100438
100902
|
const [viewMode, setViewMode] = useState("3d");
|
|
100903
|
+
const [showGuidelines, setShowGuidelines] = useState(true);
|
|
100439
100904
|
const [activeSide, setActiveSide] = useState(currentPage || "front");
|
|
100440
|
-
const [
|
|
100441
|
-
const [
|
|
100905
|
+
const [frontSnapshot, setFrontSnapshot] = useState("");
|
|
100906
|
+
const [backSnapshot, setBackSnapshot] = useState("");
|
|
100907
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
100442
100908
|
const [rotX, setRotX] = useState(15);
|
|
100443
100909
|
const [rotY, setRotY] = useState(-25);
|
|
100444
100910
|
const [isAutoSpin, setIsAutoSpin] = useState(true);
|
|
@@ -100448,24 +100914,27 @@ function PreviewModal({ onClose }) {
|
|
|
100448
100914
|
if (!canvas2) return;
|
|
100449
100915
|
useHistoryStore.getState().saveState();
|
|
100450
100916
|
let isMounted = true;
|
|
100917
|
+
setIsLoading(true);
|
|
100451
100918
|
const loadSides = async () => {
|
|
100452
100919
|
const currentSide = useHistoryStore.getState().currentPage || "front";
|
|
100453
100920
|
const editorPages = useEditorStore.getState().pages || {};
|
|
100454
100921
|
const historyPages = useHistoryStore.getState().pages || {};
|
|
100455
|
-
const
|
|
100922
|
+
const currentSnapshot = getWorkareaDataUrl(canvas2, 2, currentSide, showGuidelines);
|
|
100456
100923
|
const opposingSide = currentSide === "front" ? "back" : "front";
|
|
100457
100924
|
const opposingJson = editorPages[opposingSide] || historyPages[opposingSide]?.history?.at(-1) || null;
|
|
100458
|
-
let
|
|
100925
|
+
let opposingSnapshot = "";
|
|
100459
100926
|
if (isTwoSided && opposingJson) {
|
|
100460
|
-
|
|
100927
|
+
opposingSnapshot = await renderPageDataUrl(opposingJson, canvas2, 2, opposingSide, showGuidelines);
|
|
100461
100928
|
}
|
|
100929
|
+
const initialFront = currentSide === "front" ? currentSnapshot : opposingSnapshot;
|
|
100930
|
+
const initialBack = currentSide === "front" ? opposingSnapshot : currentSnapshot;
|
|
100462
100931
|
if (isMounted) {
|
|
100463
|
-
|
|
100464
|
-
|
|
100465
|
-
|
|
100466
|
-
|
|
100467
|
-
|
|
100468
|
-
|
|
100932
|
+
setFrontSnapshot(initialFront);
|
|
100933
|
+
setBackSnapshot(initialBack);
|
|
100934
|
+
setIsLoading(false);
|
|
100935
|
+
if (onUpload) {
|
|
100936
|
+
const activeUrl = (activeSide === "front" ? initialFront : initialBack) || initialFront;
|
|
100937
|
+
onUpload({ front: initialFront, back: initialBack || void 0, active: activeUrl });
|
|
100469
100938
|
}
|
|
100470
100939
|
}
|
|
100471
100940
|
};
|
|
@@ -100473,7 +100942,7 @@ function PreviewModal({ onClose }) {
|
|
|
100473
100942
|
return () => {
|
|
100474
100943
|
isMounted = false;
|
|
100475
100944
|
};
|
|
100476
|
-
}, [canvas2, isTwoSided]);
|
|
100945
|
+
}, [canvas2, isTwoSided, showGuidelines]);
|
|
100477
100946
|
useEffect(() => {
|
|
100478
100947
|
if (!isAutoSpin || isDragging || viewMode !== "3d") return;
|
|
100479
100948
|
let animId;
|
|
@@ -100506,6 +100975,9 @@ function PreviewModal({ onClose }) {
|
|
|
100506
100975
|
const isCircle = shapeType === "circle";
|
|
100507
100976
|
const cardAspectRatio = isCircle ? 1 : waW && waH ? waW / waH : 1.75;
|
|
100508
100977
|
const getShapeStyle = (side) => {
|
|
100978
|
+
if (showGuidelines) {
|
|
100979
|
+
return {};
|
|
100980
|
+
}
|
|
100509
100981
|
const baseRadius = workarea?.rx || workarea?.__radiusOfCanvas || 0;
|
|
100510
100982
|
const rxPct = waW > 0 ? (baseRadius || waW / 8) / waW * 100 : 12.5;
|
|
100511
100983
|
const ryPct = waH > 0 ? (baseRadius || waH / 8) / waH * 100 : 12.5;
|
|
@@ -100547,14 +101019,14 @@ function PreviewModal({ onClose }) {
|
|
|
100547
101019
|
return {};
|
|
100548
101020
|
}
|
|
100549
101021
|
};
|
|
100550
|
-
const currentProof = activeSide === "front" ?
|
|
101022
|
+
const currentProof = activeSide === "front" ? frontSnapshot : backSnapshot;
|
|
100551
101023
|
const normalizedRotY = (rotY % 360 + 360) % 360;
|
|
100552
101024
|
const isFrontFacing = normalizedRotY <= 90 || normalizedRotY >= 270;
|
|
100553
101025
|
return /* @__PURE__ */ 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__ */ jsxs("div", { className: "relative w-full max-w-6xl bg-white border border-gray-200 rounded-2xl shadow-2xl overflow-hidden flex flex-col h-[85vh]", children: [
|
|
100554
101026
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-5 py-3 border-b border-gray-200 bg-gray-50/90", children: [
|
|
100555
101027
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
100556
101028
|
/* @__PURE__ */ jsx("span", { className: "h-2.5 w-2.5 rounded-full bg-[#02bc71] animate-pulse" }),
|
|
100557
|
-
/* @__PURE__ */ jsx("h2", { className: "text-sm font-bold text-gray-900", children: "
|
|
101029
|
+
/* @__PURE__ */ jsx("h2", { className: "text-sm font-bold text-gray-900", children: "Design Preview" }),
|
|
100558
101030
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center bg-gray-200 p-0.5 rounded-lg border border-gray-300 text-xs", children: [
|
|
100559
101031
|
/* @__PURE__ */ jsx(
|
|
100560
101032
|
"button",
|
|
@@ -100574,7 +101046,23 @@ function PreviewModal({ onClose }) {
|
|
|
100574
101046
|
children: "2D Proof"
|
|
100575
101047
|
}
|
|
100576
101048
|
)
|
|
100577
|
-
] })
|
|
101049
|
+
] }),
|
|
101050
|
+
/* @__PURE__ */ jsxs(
|
|
101051
|
+
"button",
|
|
101052
|
+
{
|
|
101053
|
+
type: "button",
|
|
101054
|
+
onClick: () => setShowGuidelines((prev) => !prev),
|
|
101055
|
+
className: `flex items-center gap-1.5 px-3 py-1 rounded-lg text-xs font-semibold cursor-pointer border transition-all ${showGuidelines ? "bg-emerald-50 text-emerald-800 border-emerald-300 shadow-sm" : "bg-white text-gray-600 border-gray-300 hover:bg-gray-100"}`,
|
|
101056
|
+
title: showGuidelines ? "Click to view finished trimmed cut" : "Click to view bleed, trim & safe line guidelines",
|
|
101057
|
+
children: [
|
|
101058
|
+
/* @__PURE__ */ jsx("span", { className: `w-2 h-2 rounded-full ${showGuidelines ? "bg-emerald-500 animate-pulse" : "bg-gray-400"}` }),
|
|
101059
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
101060
|
+
"Guidelines: ",
|
|
101061
|
+
showGuidelines ? "ON" : "OFF"
|
|
101062
|
+
] })
|
|
101063
|
+
]
|
|
101064
|
+
}
|
|
101065
|
+
)
|
|
100578
101066
|
] }),
|
|
100579
101067
|
/* @__PURE__ */ jsx(
|
|
100580
101068
|
"button",
|
|
@@ -100586,6 +101074,27 @@ function PreviewModal({ onClose }) {
|
|
|
100586
101074
|
}
|
|
100587
101075
|
)
|
|
100588
101076
|
] }),
|
|
101077
|
+
showGuidelines && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between px-5 py-2 bg-emerald-50/70 border-b border-emerald-100 text-xs text-gray-700 gap-2", children: [
|
|
101078
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4 flex-wrap", children: [
|
|
101079
|
+
/* @__PURE__ */ jsxs("span", { className: "font-semibold text-emerald-950 flex items-center gap-1.5", children: [
|
|
101080
|
+
/* @__PURE__ */ jsx("span", { className: "inline-block w-2 h-2 rounded-full bg-emerald-600" }),
|
|
101081
|
+
"Print Guidelines:"
|
|
101082
|
+
] }),
|
|
101083
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
101084
|
+
/* @__PURE__ */ jsx("span", { className: "w-4 h-0 border-t-2 border-dashed border-red-500 inline-block" }),
|
|
101085
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium text-red-700", children: "Trim Line (Cut line)" })
|
|
101086
|
+
] }),
|
|
101087
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
101088
|
+
/* @__PURE__ */ jsx("span", { className: "w-4 h-0 border-t-2 border-dashed border-emerald-600 inline-block" }),
|
|
101089
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium text-emerald-700", children: "Safe Line (Keep text inside)" })
|
|
101090
|
+
] }),
|
|
101091
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
101092
|
+
/* @__PURE__ */ jsx("span", { className: "w-3.5 h-3 rounded-sm border border-dashed border-gray-400 bg-white inline-block" }),
|
|
101093
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium text-gray-600", children: "Bleed Area (Outer edge)" })
|
|
101094
|
+
] })
|
|
101095
|
+
] }),
|
|
101096
|
+
/* @__PURE__ */ jsx("span", { className: "text-[11px] text-gray-500 italic hidden md:inline", children: "Toggle Guidelines OFF for finished cut preview" })
|
|
101097
|
+
] }),
|
|
100589
101098
|
/* @__PURE__ */ jsx(
|
|
100590
101099
|
"div",
|
|
100591
101100
|
{
|
|
@@ -100619,10 +101128,10 @@ function PreviewModal({ onClose }) {
|
|
|
100619
101128
|
...getShapeStyle("front")
|
|
100620
101129
|
},
|
|
100621
101130
|
className: "absolute inset-0 overflow-hidden bg-transparent",
|
|
100622
|
-
children:
|
|
101131
|
+
children: frontSnapshot ? /* @__PURE__ */ jsx(
|
|
100623
101132
|
"img",
|
|
100624
101133
|
{
|
|
100625
|
-
src:
|
|
101134
|
+
src: frontSnapshot,
|
|
100626
101135
|
alt: "Front",
|
|
100627
101136
|
style: getShapeStyle("front"),
|
|
100628
101137
|
className: "w-full h-full object-contain pointer-events-none block"
|
|
@@ -100631,8 +101140,8 @@ function PreviewModal({ onClose }) {
|
|
|
100631
101140
|
"div",
|
|
100632
101141
|
{
|
|
100633
101142
|
style: getShapeStyle("front"),
|
|
100634
|
-
className: "w-full h-full bg-white flex items-center justify-center text-xs text-gray-
|
|
100635
|
-
children: "Front Design"
|
|
101143
|
+
className: "w-full h-full bg-white flex flex-col items-center justify-center text-xs text-gray-500 gap-2 p-4 text-center",
|
|
101144
|
+
children: /* @__PURE__ */ jsx("span", { children: "Front Design" })
|
|
100636
101145
|
}
|
|
100637
101146
|
)
|
|
100638
101147
|
}
|
|
@@ -100647,10 +101156,10 @@ function PreviewModal({ onClose }) {
|
|
|
100647
101156
|
...getShapeStyle("back")
|
|
100648
101157
|
},
|
|
100649
101158
|
className: "absolute inset-0 overflow-hidden bg-transparent",
|
|
100650
|
-
children: isTwoSided ?
|
|
101159
|
+
children: isTwoSided ? backSnapshot ? /* @__PURE__ */ jsx(
|
|
100651
101160
|
"img",
|
|
100652
101161
|
{
|
|
100653
|
-
src:
|
|
101162
|
+
src: backSnapshot,
|
|
100654
101163
|
alt: "Back",
|
|
100655
101164
|
style: getShapeStyle("back"),
|
|
100656
101165
|
className: "w-full h-full object-contain pointer-events-none block"
|
|
@@ -100659,7 +101168,8 @@ function PreviewModal({ onClose }) {
|
|
|
100659
101168
|
"div",
|
|
100660
101169
|
{
|
|
100661
101170
|
style: getShapeStyle("back"),
|
|
100662
|
-
className: "w-full h-full bg-white flex flex-col items-center justify-center text-xs text-gray-
|
|
101171
|
+
className: "w-full h-full bg-white flex flex-col items-center justify-center text-xs text-gray-500 gap-2 p-4 text-center",
|
|
101172
|
+
children: /* @__PURE__ */ jsx("span", { children: "Back Design" })
|
|
100663
101173
|
}
|
|
100664
101174
|
) : /* @__PURE__ */ jsx(
|
|
100665
101175
|
"div",
|
|
@@ -100710,7 +101220,7 @@ function PreviewModal({ onClose }) {
|
|
|
100710
101220
|
maxWidth: "85%",
|
|
100711
101221
|
...getShapeStyle(activeSide)
|
|
100712
101222
|
},
|
|
100713
|
-
className:
|
|
101223
|
+
className: `relative overflow-hidden flex items-center justify-center transition-all ${showGuidelines ? "bg-white shadow-xl ring-1 ring-black/10" : "bg-transparent shadow-lg"}`,
|
|
100714
101224
|
children: /* @__PURE__ */ jsx(
|
|
100715
101225
|
"img",
|
|
100716
101226
|
{
|
|
@@ -100721,7 +101231,10 @@ function PreviewModal({ onClose }) {
|
|
|
100721
101231
|
}
|
|
100722
101232
|
)
|
|
100723
101233
|
}
|
|
100724
|
-
) : /* @__PURE__ */ jsxs("div", { className: "p-8 text-center bg-
|
|
101234
|
+
) : isLoading ? /* @__PURE__ */ jsxs("div", { className: "p-8 text-center bg-white rounded-2xl border border-gray-200 shadow-sm flex flex-col items-center justify-center gap-3", children: [
|
|
101235
|
+
/* @__PURE__ */ jsx("div", { className: "w-8 h-8 border-3 border-[#02bc71] border-t-transparent rounded-full animate-spin" }),
|
|
101236
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-semibold text-gray-800", children: "Generating preview..." })
|
|
101237
|
+
] }) : /* @__PURE__ */ jsxs("div", { className: "p-8 text-center bg-gray-50 rounded-xl border border-gray-200 text-xs text-gray-400", children: [
|
|
100725
101238
|
"No preview available for ",
|
|
100726
101239
|
activeSide,
|
|
100727
101240
|
" side."
|
|
@@ -100779,7 +101292,10 @@ function PreviewModal({ onClose }) {
|
|
|
100779
101292
|
children: "Reset"
|
|
100780
101293
|
}
|
|
100781
101294
|
)
|
|
100782
|
-
] }) : /* @__PURE__ */
|
|
101295
|
+
] }) : /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5 text-xs", children: [
|
|
101296
|
+
/* @__PURE__ */ jsx("span", { className: "text-gray-500 font-medium", children: "Flat 2D High-Resolution Render" }),
|
|
101297
|
+
showGuidelines ? /* @__PURE__ */ jsx("span", { className: "px-2 py-0.5 rounded-full bg-emerald-100 text-emerald-800 font-semibold text-[11px] border border-emerald-300", children: "Bleed, Trim & Safe Lines" }) : /* @__PURE__ */ jsx("span", { className: "px-2 py-0.5 rounded-full bg-gray-200 text-gray-700 font-semibold text-[11px]", children: "Finished Trimmed Cut" })
|
|
101298
|
+
] }),
|
|
100783
101299
|
/* @__PURE__ */ jsx(
|
|
100784
101300
|
"button",
|
|
100785
101301
|
{
|
|
@@ -101472,64 +101988,6 @@ function setQueryParam(key, value) {
|
|
|
101472
101988
|
}
|
|
101473
101989
|
window.history.replaceState({}, "", url.toString());
|
|
101474
101990
|
}
|
|
101475
|
-
const ShapeService = {
|
|
101476
|
-
async getAll(data) {
|
|
101477
|
-
const res = await apiFetch(
|
|
101478
|
-
`/api/cliparts?page=${data.page}&limit=${data.limit}&category=${data.category}&status=true`,
|
|
101479
|
-
{
|
|
101480
|
-
method: "GET"
|
|
101481
|
-
}
|
|
101482
|
-
);
|
|
101483
|
-
return {
|
|
101484
|
-
items: res.data || []
|
|
101485
|
-
};
|
|
101486
|
-
},
|
|
101487
|
-
async getAllExternal(data) {
|
|
101488
|
-
const res = await apiFetch(
|
|
101489
|
-
`/api/external/images?page=${data.page}&limit=${data.limit}&keyType=${data.keyType}&status=true${data.query ? `&query=${data.query}` : ""}`,
|
|
101490
|
-
{
|
|
101491
|
-
method: "GET"
|
|
101492
|
-
}
|
|
101493
|
-
);
|
|
101494
|
-
return {
|
|
101495
|
-
items: res.data || []
|
|
101496
|
-
};
|
|
101497
|
-
},
|
|
101498
|
-
async getTextTemplates(data) {
|
|
101499
|
-
const res = await apiFetch(
|
|
101500
|
-
`/api/text-templates?page=${data.page}&limit=${data.limit}&status=true`,
|
|
101501
|
-
{
|
|
101502
|
-
method: "GET"
|
|
101503
|
-
}
|
|
101504
|
-
);
|
|
101505
|
-
return {
|
|
101506
|
-
items: res.data || []
|
|
101507
|
-
};
|
|
101508
|
-
},
|
|
101509
|
-
async getTextTemplateById(id) {
|
|
101510
|
-
const res = await apiFetch(
|
|
101511
|
-
`/api/text-templates/${id}`,
|
|
101512
|
-
{
|
|
101513
|
-
method: "GET"
|
|
101514
|
-
}
|
|
101515
|
-
);
|
|
101516
|
-
return res.data || null;
|
|
101517
|
-
},
|
|
101518
|
-
async updateTextTemplate(id, body) {
|
|
101519
|
-
const res = await apiFetch(`/api/text-templates/${id}`, {
|
|
101520
|
-
method: "PUT",
|
|
101521
|
-
body
|
|
101522
|
-
});
|
|
101523
|
-
return res;
|
|
101524
|
-
},
|
|
101525
|
-
async convertImages(url) {
|
|
101526
|
-
const res = await apiFetch(`/api/convert-images`, {
|
|
101527
|
-
method: "POST",
|
|
101528
|
-
body: { file: url }
|
|
101529
|
-
});
|
|
101530
|
-
return res;
|
|
101531
|
-
}
|
|
101532
|
-
};
|
|
101533
101991
|
function spaNavigate(path) {
|
|
101534
101992
|
if (typeof window === "undefined") return;
|
|
101535
101993
|
try {
|
|
@@ -102436,32 +102894,6 @@ const elements = [
|
|
|
102436
102894
|
]
|
|
102437
102895
|
}
|
|
102438
102896
|
];
|
|
102439
|
-
async function addImageToCanvas(url) {
|
|
102440
|
-
const canvas2 = useEditorStore.getState().canvas;
|
|
102441
|
-
if (!canvas2) return;
|
|
102442
|
-
const workarea = canvas2.getObjects().find((o2) => o2.name === "workarea");
|
|
102443
|
-
if (!workarea) return;
|
|
102444
|
-
oa.fromURL(url, { crossOrigin: "anonymous" }).then((img) => {
|
|
102445
|
-
const workareaCenter = workarea.getCenterPoint();
|
|
102446
|
-
const workareaBounds = workarea.getBoundingRect();
|
|
102447
|
-
const maxWidth = workareaBounds.width * 0.7;
|
|
102448
|
-
const maxHeight = workareaBounds.height * 0.7;
|
|
102449
|
-
img.scale(Math.min(maxWidth / img.width, maxHeight / img.height));
|
|
102450
|
-
img.set({
|
|
102451
|
-
left: workareaCenter.x,
|
|
102452
|
-
top: workareaCenter.y,
|
|
102453
|
-
originX: "center",
|
|
102454
|
-
originY: "center",
|
|
102455
|
-
selectable: true,
|
|
102456
|
-
hasControls: true
|
|
102457
|
-
});
|
|
102458
|
-
canvas2.add(img);
|
|
102459
|
-
canvas2.setActiveObject(img);
|
|
102460
|
-
canvas2.requestRenderAll();
|
|
102461
|
-
}).catch((err) => {
|
|
102462
|
-
console.warn("Fabric failed to load image from URL:", url, err);
|
|
102463
|
-
});
|
|
102464
|
-
}
|
|
102465
102897
|
function getWorkareaMetrics(canvas2) {
|
|
102466
102898
|
const workarea = canvas2.getObjects().find((obj) => obj.name === "workarea");
|
|
102467
102899
|
if (!workarea) return null;
|
|
@@ -103488,10 +103920,15 @@ function PhotosPanel() {
|
|
|
103488
103920
|
try {
|
|
103489
103921
|
startLoading();
|
|
103490
103922
|
const image = await ShapeService.convertImages(url);
|
|
103491
|
-
const imageUrl = image?.success ? image?.url : null;
|
|
103492
|
-
if (imageUrl)
|
|
103923
|
+
const imageUrl = image?.success || image?.status ? image?.url : null;
|
|
103924
|
+
if (imageUrl) {
|
|
103925
|
+
addImageToCanvas(imageUrl);
|
|
103926
|
+
} else {
|
|
103927
|
+
alert(image?.data || image?.message || "Could not load image. Provider rate limit reached (429).");
|
|
103928
|
+
}
|
|
103493
103929
|
} catch (error2) {
|
|
103494
103930
|
console.error("Failed to convert image:", error2);
|
|
103931
|
+
alert(error2?.message || "Failed to convert image");
|
|
103495
103932
|
} finally {
|
|
103496
103933
|
stopLoading();
|
|
103497
103934
|
if (window.innerWidth < 768) {
|
|
@@ -103736,14 +104173,24 @@ function PhotosPanel() {
|
|
|
103736
104173
|
startLoading();
|
|
103737
104174
|
const targetUrl = item.largeImage || item.image;
|
|
103738
104175
|
if (!targetUrl) return;
|
|
103739
|
-
|
|
103740
|
-
|
|
103741
|
-
if (imageUrl)
|
|
103742
|
-
|
|
103743
|
-
|
|
104176
|
+
let image = await ShapeService.convertImages(targetUrl);
|
|
104177
|
+
let imageUrl = image?.success || image?.status ? image?.url : null;
|
|
104178
|
+
if (!imageUrl && item.image && item.image !== targetUrl) {
|
|
104179
|
+
const fallbackRes = await ShapeService.convertImages(item.image);
|
|
104180
|
+
imageUrl = fallbackRes?.success || fallbackRes?.status ? fallbackRes?.url : item.image;
|
|
104181
|
+
}
|
|
104182
|
+
if (imageUrl) {
|
|
104183
|
+
await addImageToCanvas(imageUrl);
|
|
104184
|
+
if (window.innerWidth < 768) {
|
|
104185
|
+
useSidePanelStore.getState().setActivePanel(null);
|
|
104186
|
+
}
|
|
104187
|
+
} else {
|
|
104188
|
+
const errMsg = image?.data || image?.message || "Stock image provider rate limit reached (429). Please try another image.";
|
|
104189
|
+
alert(`Unable to load image: ${errMsg}`);
|
|
103744
104190
|
}
|
|
103745
104191
|
} catch (error2) {
|
|
103746
104192
|
console.error("Failed to convert image:", error2);
|
|
104193
|
+
alert(`Image loading error: ${error2?.message || "Please try another image."}`);
|
|
103747
104194
|
} finally {
|
|
103748
104195
|
stopLoading();
|
|
103749
104196
|
}
|
|
@@ -103773,17 +104220,21 @@ async function addBackgroundImage(url) {
|
|
|
103773
104220
|
canvas2.getObjects("image").forEach((o2) => {
|
|
103774
104221
|
if (o2.name === "bg-image") canvas2.remove(o2);
|
|
103775
104222
|
});
|
|
103776
|
-
const
|
|
103777
|
-
const
|
|
103778
|
-
const
|
|
104223
|
+
const center = workarea.getCenterPoint();
|
|
104224
|
+
const waW = workarea.getScaledWidth();
|
|
104225
|
+
const waH = workarea.getScaledHeight();
|
|
104226
|
+
const shapeType = detectShape(workarea.__shapeType);
|
|
104227
|
+
const activeSide = workarea.__pageSide || useHistoryStore.getState().currentPage || "front";
|
|
104228
|
+
const baseRx = (workarea.rx || workarea.__radiusOfCanvas || 0) * (workarea.scaleX || 1);
|
|
104229
|
+
const baseRy = (workarea.ry || workarea.__radiusOfCanvas || 0) * (workarea.scaleY || 1);
|
|
103779
104230
|
oa.fromURL(url + "?t=vb", { crossOrigin: "anonymous" }).then((img) => {
|
|
103780
104231
|
const imgW = img.width || 1;
|
|
103781
104232
|
const imgH = img.height || 1;
|
|
103782
|
-
const scale2 = Math.max(
|
|
104233
|
+
const scale2 = Math.max(waW / imgW, waH / imgH);
|
|
103783
104234
|
img.scale(scale2);
|
|
103784
104235
|
img.set({
|
|
103785
|
-
left:
|
|
103786
|
-
top:
|
|
104236
|
+
left: center.x,
|
|
104237
|
+
top: center.y,
|
|
103787
104238
|
originX: "center",
|
|
103788
104239
|
originY: "center",
|
|
103789
104240
|
selectable: false,
|
|
@@ -103793,15 +104244,22 @@ async function addBackgroundImage(url) {
|
|
|
103793
104244
|
lockMovementY: false,
|
|
103794
104245
|
name: "bg-image"
|
|
103795
104246
|
});
|
|
103796
|
-
|
|
103797
|
-
|
|
103798
|
-
|
|
103799
|
-
|
|
103800
|
-
height:
|
|
104247
|
+
const exportLeft = Math.round(center.x - waW / 2);
|
|
104248
|
+
const exportTop = Math.round(center.y - waH / 2);
|
|
104249
|
+
const clip2 = createShapeExportClip(
|
|
104250
|
+
shapeType,
|
|
104251
|
+
{ left: exportLeft, top: exportTop, width: waW, height: waH, rx: baseRx, ry: baseRy },
|
|
104252
|
+
activeSide
|
|
104253
|
+
) || new jr({
|
|
104254
|
+
left: center.x,
|
|
104255
|
+
top: center.y,
|
|
104256
|
+
width: waW,
|
|
104257
|
+
height: waH,
|
|
103801
104258
|
originX: "center",
|
|
103802
104259
|
originY: "center",
|
|
103803
104260
|
absolutePositioned: true
|
|
103804
104261
|
});
|
|
104262
|
+
img.clipPath = clip2;
|
|
103805
104263
|
canvas2.add(img);
|
|
103806
104264
|
canvas2.sendObjectToBack(img);
|
|
103807
104265
|
canvas2.sendObjectToBack(workarea);
|
|
@@ -109452,6 +109910,282 @@ function ShadowControl({
|
|
|
109452
109910
|
] })
|
|
109453
109911
|
] });
|
|
109454
109912
|
}
|
|
109913
|
+
function BgRemoveIcon({ size = 16, className = "" }) {
|
|
109914
|
+
return /* @__PURE__ */ jsxs(
|
|
109915
|
+
"svg",
|
|
109916
|
+
{
|
|
109917
|
+
width: size,
|
|
109918
|
+
height: size,
|
|
109919
|
+
viewBox: "0 0 24 24",
|
|
109920
|
+
fill: "none",
|
|
109921
|
+
stroke: "currentColor",
|
|
109922
|
+
strokeWidth: "2",
|
|
109923
|
+
strokeLinecap: "round",
|
|
109924
|
+
strokeLinejoin: "round",
|
|
109925
|
+
className: `shrink-0 ${className}`,
|
|
109926
|
+
children: [
|
|
109927
|
+
/* @__PURE__ */ jsx("path", { d: "M12 3v3" }),
|
|
109928
|
+
/* @__PURE__ */ jsx("path", { d: "M18.5 5.5l-2.1 2.1" }),
|
|
109929
|
+
/* @__PURE__ */ jsx("path", { d: "M21 12h-3" }),
|
|
109930
|
+
/* @__PURE__ */ jsx("path", { d: "M3 21l9-9" }),
|
|
109931
|
+
/* @__PURE__ */ jsx("path", { d: "M12.2 7.8a2 2 0 0 1 2.8 2.8L6.5 19.1a2 2 0 0 1-1.4.6H3v-2.1a2 2 0 0 1 .6-1.4l8.6-8.4z" })
|
|
109932
|
+
]
|
|
109933
|
+
}
|
|
109934
|
+
);
|
|
109935
|
+
}
|
|
109936
|
+
const IMGLY_CDN_PATH = "https://staticimgly.com/@imgly/background-removal-data/1.7.0/dist/";
|
|
109937
|
+
let removeBackgroundFn = null;
|
|
109938
|
+
async function getRemoveBackground() {
|
|
109939
|
+
if (!removeBackgroundFn) {
|
|
109940
|
+
const mod = await import("@imgly/background-removal");
|
|
109941
|
+
removeBackgroundFn = mod.removeBackground || mod.default;
|
|
109942
|
+
}
|
|
109943
|
+
return removeBackgroundFn;
|
|
109944
|
+
}
|
|
109945
|
+
function blobToDataUrl(blob) {
|
|
109946
|
+
return new Promise((resolve, reject) => {
|
|
109947
|
+
const reader = new FileReader();
|
|
109948
|
+
reader.onloadend = () => resolve(reader.result);
|
|
109949
|
+
reader.onerror = reject;
|
|
109950
|
+
reader.readAsDataURL(blob);
|
|
109951
|
+
});
|
|
109952
|
+
}
|
|
109953
|
+
function loadImageElement(src) {
|
|
109954
|
+
return new Promise((resolve, reject) => {
|
|
109955
|
+
const img = new Image();
|
|
109956
|
+
img.crossOrigin = "anonymous";
|
|
109957
|
+
img.onload = () => resolve(img);
|
|
109958
|
+
img.onerror = (err) => reject(err);
|
|
109959
|
+
img.src = src;
|
|
109960
|
+
});
|
|
109961
|
+
}
|
|
109962
|
+
async function extractImageBlob(imageObj) {
|
|
109963
|
+
const currentElement = imageObj.getElement();
|
|
109964
|
+
const currentSrc = imageObj.getSrc() || currentElement?.src;
|
|
109965
|
+
if (currentSrc && (currentSrc.startsWith("data:") || currentSrc.startsWith("blob:"))) {
|
|
109966
|
+
try {
|
|
109967
|
+
const res = await fetch(currentSrc);
|
|
109968
|
+
const blob = await res.blob();
|
|
109969
|
+
if (blob && blob.size > 0) return blob;
|
|
109970
|
+
} catch (e3) {
|
|
109971
|
+
console.warn("Direct data/blob URL fetch failed, trying canvas export:", e3);
|
|
109972
|
+
}
|
|
109973
|
+
}
|
|
109974
|
+
if (currentElement && (currentElement.naturalWidth || currentElement.width)) {
|
|
109975
|
+
try {
|
|
109976
|
+
const w2 = currentElement.naturalWidth || currentElement.width;
|
|
109977
|
+
const h2 = currentElement.naturalHeight || currentElement.height;
|
|
109978
|
+
const offscreen = document.createElement("canvas");
|
|
109979
|
+
offscreen.width = w2;
|
|
109980
|
+
offscreen.height = h2;
|
|
109981
|
+
const ctx = offscreen.getContext("2d");
|
|
109982
|
+
if (ctx) {
|
|
109983
|
+
ctx.drawImage(currentElement, 0, 0, w2, h2);
|
|
109984
|
+
const blob = await new Promise((resolve) => {
|
|
109985
|
+
offscreen.toBlob((b2) => resolve(b2), "image/png");
|
|
109986
|
+
});
|
|
109987
|
+
if (blob && blob.size > 0) return blob;
|
|
109988
|
+
}
|
|
109989
|
+
} catch (e3) {
|
|
109990
|
+
console.warn("Offscreen canvas render failed (possible CORS):", e3);
|
|
109991
|
+
}
|
|
109992
|
+
}
|
|
109993
|
+
try {
|
|
109994
|
+
const dataUrl = imageObj.toDataURL({ format: "png" });
|
|
109995
|
+
if (dataUrl && dataUrl.startsWith("data:")) {
|
|
109996
|
+
const res = await fetch(dataUrl);
|
|
109997
|
+
const blob = await res.blob();
|
|
109998
|
+
if (blob && blob.size > 0) return blob;
|
|
109999
|
+
}
|
|
110000
|
+
} catch (e3) {
|
|
110001
|
+
console.warn("Fabric toDataURL failed:", e3);
|
|
110002
|
+
}
|
|
110003
|
+
if (currentSrc) {
|
|
110004
|
+
try {
|
|
110005
|
+
const res = await fetch(currentSrc, { mode: "cors" });
|
|
110006
|
+
const blob = await res.blob();
|
|
110007
|
+
if (blob && blob.size > 0) return blob;
|
|
110008
|
+
} catch (e3) {
|
|
110009
|
+
console.warn("Remote URL fetch failed:", e3);
|
|
110010
|
+
}
|
|
110011
|
+
}
|
|
110012
|
+
throw new Error("Unable to extract valid image blob from object.");
|
|
110013
|
+
}
|
|
110014
|
+
class BackgroundRemovalService {
|
|
110015
|
+
/**
|
|
110016
|
+
* Automatically cuts out the background of a FabricImage using client-side AI
|
|
110017
|
+
*/
|
|
110018
|
+
static async removeBackgroundAI(canvas2, imageObj, onProgress) {
|
|
110019
|
+
if (!canvas2 || !imageObj) return false;
|
|
110020
|
+
const loader = useLoaderStore.getState();
|
|
110021
|
+
try {
|
|
110022
|
+
loader.startLoading("Extracting image data...");
|
|
110023
|
+
onProgress?.("Extracting image data...");
|
|
110024
|
+
const updateProgress = (msg) => {
|
|
110025
|
+
loader.setLoading(true, msg);
|
|
110026
|
+
onProgress?.(msg);
|
|
110027
|
+
};
|
|
110028
|
+
const currentElement = imageObj.getElement();
|
|
110029
|
+
const currentSrc = imageObj.getSrc() || currentElement?.src;
|
|
110030
|
+
if (!currentSrc && !currentElement) {
|
|
110031
|
+
throw new Error("Unable to locate image source");
|
|
110032
|
+
}
|
|
110033
|
+
if (!imageObj.__originalSrc) {
|
|
110034
|
+
imageObj.__originalSrc = currentSrc;
|
|
110035
|
+
}
|
|
110036
|
+
const imageBlob = await extractImageBlob(imageObj);
|
|
110037
|
+
updateProgress("Initializing AI model...");
|
|
110038
|
+
const removeBackground = await getRemoveBackground();
|
|
110039
|
+
let blob;
|
|
110040
|
+
try {
|
|
110041
|
+
blob = await removeBackground(imageBlob, {
|
|
110042
|
+
publicPath: IMGLY_CDN_PATH,
|
|
110043
|
+
model: "isnet_quint8",
|
|
110044
|
+
output: {
|
|
110045
|
+
format: "image/png",
|
|
110046
|
+
quality: 0.95
|
|
110047
|
+
},
|
|
110048
|
+
progress: (key, current, total) => {
|
|
110049
|
+
if (total > 0) {
|
|
110050
|
+
const pct = Math.round(current / total * 100);
|
|
110051
|
+
updateProgress(`Downloading AI model: ${pct}%`);
|
|
110052
|
+
} else {
|
|
110053
|
+
updateProgress("Processing AI model...");
|
|
110054
|
+
}
|
|
110055
|
+
}
|
|
110056
|
+
});
|
|
110057
|
+
} catch (err1) {
|
|
110058
|
+
console.warn("isnet_quint8 failed, attempting isnet_fp16 fallback:", err1);
|
|
110059
|
+
blob = await removeBackground(imageBlob, {
|
|
110060
|
+
publicPath: IMGLY_CDN_PATH,
|
|
110061
|
+
model: "isnet_fp16",
|
|
110062
|
+
output: {
|
|
110063
|
+
format: "image/png",
|
|
110064
|
+
quality: 0.95
|
|
110065
|
+
},
|
|
110066
|
+
progress: (key, current, total) => {
|
|
110067
|
+
if (total > 0) {
|
|
110068
|
+
const pct = Math.round(current / total * 100);
|
|
110069
|
+
updateProgress(`Downloading AI model: ${pct}%`);
|
|
110070
|
+
} else {
|
|
110071
|
+
updateProgress("Processing AI model...");
|
|
110072
|
+
}
|
|
110073
|
+
}
|
|
110074
|
+
});
|
|
110075
|
+
}
|
|
110076
|
+
updateProgress("Applying transparent cutout...");
|
|
110077
|
+
const transparentDataUrl = await blobToDataUrl(blob);
|
|
110078
|
+
const newImgElement = await loadImageElement(transparentDataUrl);
|
|
110079
|
+
const origScaleX = imageObj.scaleX ?? 1;
|
|
110080
|
+
const origScaleY = imageObj.scaleY ?? 1;
|
|
110081
|
+
const origWidth = imageObj.width ?? 1;
|
|
110082
|
+
const origHeight = imageObj.height ?? 1;
|
|
110083
|
+
imageObj.setElement(newImgElement);
|
|
110084
|
+
if (newImgElement.naturalWidth && newImgElement.naturalHeight) {
|
|
110085
|
+
imageObj.set({
|
|
110086
|
+
scaleX: origWidth * origScaleX / newImgElement.naturalWidth,
|
|
110087
|
+
scaleY: origHeight * origScaleY / newImgElement.naturalHeight
|
|
110088
|
+
});
|
|
110089
|
+
}
|
|
110090
|
+
imageObj.__isBgRemoved = true;
|
|
110091
|
+
imageObj.setCoords();
|
|
110092
|
+
canvas2.requestRenderAll();
|
|
110093
|
+
useHistoryStore.getState().saveState();
|
|
110094
|
+
return true;
|
|
110095
|
+
} catch (err) {
|
|
110096
|
+
console.error("Background removal error:", err);
|
|
110097
|
+
throw err;
|
|
110098
|
+
} finally {
|
|
110099
|
+
loader.stopLoading();
|
|
110100
|
+
}
|
|
110101
|
+
}
|
|
110102
|
+
/**
|
|
110103
|
+
* Restores the original image background prior to AI cutout
|
|
110104
|
+
*/
|
|
110105
|
+
static async restoreOriginalBackground(canvas2, imageObj) {
|
|
110106
|
+
if (!canvas2 || !imageObj) return false;
|
|
110107
|
+
const originalSrc = imageObj.__originalSrc;
|
|
110108
|
+
if (!originalSrc) return false;
|
|
110109
|
+
const loader = useLoaderStore.getState();
|
|
110110
|
+
try {
|
|
110111
|
+
loader.startLoading("Restoring original background...");
|
|
110112
|
+
const origImgElement = await loadImageElement(originalSrc);
|
|
110113
|
+
const origScaleX = imageObj.scaleX ?? 1;
|
|
110114
|
+
const origScaleY = imageObj.scaleY ?? 1;
|
|
110115
|
+
const origWidth = imageObj.width ?? 1;
|
|
110116
|
+
const origHeight = imageObj.height ?? 1;
|
|
110117
|
+
imageObj.setElement(origImgElement);
|
|
110118
|
+
if (origImgElement.naturalWidth && origImgElement.naturalHeight) {
|
|
110119
|
+
imageObj.set({
|
|
110120
|
+
scaleX: origWidth * origScaleX / origImgElement.naturalWidth,
|
|
110121
|
+
scaleY: origHeight * origScaleY / origImgElement.naturalHeight
|
|
110122
|
+
});
|
|
110123
|
+
}
|
|
110124
|
+
delete imageObj.__originalSrc;
|
|
110125
|
+
imageObj.__isBgRemoved = false;
|
|
110126
|
+
this.removeColorRemovalFilter(canvas2, imageObj, false);
|
|
110127
|
+
imageObj.setCoords();
|
|
110128
|
+
canvas2.requestRenderAll();
|
|
110129
|
+
useHistoryStore.getState().saveState();
|
|
110130
|
+
return true;
|
|
110131
|
+
} catch (err) {
|
|
110132
|
+
console.error("Failed to restore original background:", err);
|
|
110133
|
+
return false;
|
|
110134
|
+
} finally {
|
|
110135
|
+
loader.stopLoading();
|
|
110136
|
+
}
|
|
110137
|
+
}
|
|
110138
|
+
/**
|
|
110139
|
+
* Applies Fabric native RemoveColor filter for instant solid-color transparency
|
|
110140
|
+
*/
|
|
110141
|
+
static applyColorRemovalFilter(canvas2, imageObj, color = "#FFFFFF", distance = 0.15, saveHistory = true) {
|
|
110142
|
+
if (!canvas2 || !imageObj) return;
|
|
110143
|
+
const normDistance = Math.max(0.01, Math.min(0.99, distance));
|
|
110144
|
+
let filter = (imageObj.filters || []).find(
|
|
110145
|
+
(f) => f.type === "RemoveColor"
|
|
110146
|
+
);
|
|
110147
|
+
if (!filter) {
|
|
110148
|
+
filter = new Sh.RemoveColor({
|
|
110149
|
+
color,
|
|
110150
|
+
distance: normDistance
|
|
110151
|
+
});
|
|
110152
|
+
imageObj.filters = [...imageObj.filters || [], filter];
|
|
110153
|
+
} else {
|
|
110154
|
+
filter.color = color;
|
|
110155
|
+
filter.distance = normDistance;
|
|
110156
|
+
}
|
|
110157
|
+
imageObj.applyFilters();
|
|
110158
|
+
canvas2.requestRenderAll();
|
|
110159
|
+
if (saveHistory) {
|
|
110160
|
+
useHistoryStore.getState().saveState();
|
|
110161
|
+
}
|
|
110162
|
+
}
|
|
110163
|
+
/**
|
|
110164
|
+
* Removes the solid-color background filter
|
|
110165
|
+
*/
|
|
110166
|
+
static removeColorRemovalFilter(canvas2, imageObj, saveHistory = true) {
|
|
110167
|
+
if (!canvas2 || !imageObj || !imageObj.filters) return;
|
|
110168
|
+
imageObj.filters = imageObj.filters.filter(
|
|
110169
|
+
(f) => f.type !== "RemoveColor"
|
|
110170
|
+
);
|
|
110171
|
+
imageObj.applyFilters();
|
|
110172
|
+
canvas2.requestRenderAll();
|
|
110173
|
+
if (saveHistory) {
|
|
110174
|
+
useHistoryStore.getState().saveState();
|
|
110175
|
+
}
|
|
110176
|
+
}
|
|
110177
|
+
/**
|
|
110178
|
+
* Checks if an image has active background removal (AI or color filter)
|
|
110179
|
+
*/
|
|
110180
|
+
static hasBackgroundRemoval(imageObj) {
|
|
110181
|
+
if (!imageObj) return false;
|
|
110182
|
+
const hasAI = !!imageObj.__originalSrc || !!imageObj.__isBgRemoved;
|
|
110183
|
+
const hasFilter = !!(imageObj.filters || []).some(
|
|
110184
|
+
(f) => f.type === "RemoveColor"
|
|
110185
|
+
);
|
|
110186
|
+
return hasAI || hasFilter;
|
|
110187
|
+
}
|
|
110188
|
+
}
|
|
109455
110189
|
function EffectsPanel() {
|
|
109456
110190
|
const canvas2 = useEditorStore((s2) => s2.canvas);
|
|
109457
110191
|
const saveState = useHistoryStore((s2) => s2.saveState);
|
|
@@ -109473,6 +110207,84 @@ function EffectsPanel() {
|
|
|
109473
110207
|
const { objects } = useSelectionStore();
|
|
109474
110208
|
const replaceState = useEffectStore((s2) => s2.replaceState);
|
|
109475
110209
|
const activeObject = objects[0];
|
|
110210
|
+
const [isRemovingBg, setIsRemovingBg] = useState(false);
|
|
110211
|
+
const [bgProgress, setBgProgress] = useState("");
|
|
110212
|
+
const [colorRemovalEnabled, setColorRemovalEnabled] = useState(false);
|
|
110213
|
+
const [colorRemovalColor, setColorRemovalColor] = useState("#FFFFFF");
|
|
110214
|
+
const [colorRemovalDistance, setColorRemovalDistance] = useState(15);
|
|
110215
|
+
const isImg = !!activeObject && (activeObject.type === "image" || activeObject.type === "FabricImage" || activeObject.isType?.("image") || activeObject.isType?.("FabricImage") || typeof activeObject.getElement === "function");
|
|
110216
|
+
const hasOriginal = !!activeObject?.__originalSrc;
|
|
110217
|
+
const handleRemoveBgAI = async () => {
|
|
110218
|
+
if (!canvas2 || !activeObject || isRemovingBg) return;
|
|
110219
|
+
try {
|
|
110220
|
+
setIsRemovingBg(true);
|
|
110221
|
+
setBgProgress("Starting AI cutout...");
|
|
110222
|
+
await BackgroundRemovalService.removeBackgroundAI(
|
|
110223
|
+
canvas2,
|
|
110224
|
+
activeObject,
|
|
110225
|
+
(msg) => setBgProgress(msg)
|
|
110226
|
+
);
|
|
110227
|
+
} catch (err) {
|
|
110228
|
+
console.error("Failed to remove background:", err);
|
|
110229
|
+
alert("Could not remove background. Please ensure the image is accessible.");
|
|
110230
|
+
} finally {
|
|
110231
|
+
setIsRemovingBg(false);
|
|
110232
|
+
setBgProgress("");
|
|
110233
|
+
}
|
|
110234
|
+
};
|
|
110235
|
+
const handleRestoreBg = async () => {
|
|
110236
|
+
if (!canvas2 || !activeObject || isRemovingBg) return;
|
|
110237
|
+
await BackgroundRemovalService.restoreOriginalBackground(canvas2, activeObject);
|
|
110238
|
+
setColorRemovalEnabled(false);
|
|
110239
|
+
};
|
|
110240
|
+
const handleToggleColorRemoval = (enabled) => {
|
|
110241
|
+
if (!canvas2 || !activeObject) return;
|
|
110242
|
+
setColorRemovalEnabled(enabled);
|
|
110243
|
+
if (enabled) {
|
|
110244
|
+
BackgroundRemovalService.applyColorRemovalFilter(
|
|
110245
|
+
canvas2,
|
|
110246
|
+
activeObject,
|
|
110247
|
+
colorRemovalColor,
|
|
110248
|
+
colorRemovalDistance / 100,
|
|
110249
|
+
true
|
|
110250
|
+
);
|
|
110251
|
+
} else {
|
|
110252
|
+
BackgroundRemovalService.removeColorRemovalFilter(
|
|
110253
|
+
canvas2,
|
|
110254
|
+
activeObject,
|
|
110255
|
+
true
|
|
110256
|
+
);
|
|
110257
|
+
}
|
|
110258
|
+
};
|
|
110259
|
+
const handleColorChange = (hex) => {
|
|
110260
|
+
if (!canvas2 || !activeObject) return;
|
|
110261
|
+
setColorRemovalColor(hex);
|
|
110262
|
+
if (colorRemovalEnabled) {
|
|
110263
|
+
BackgroundRemovalService.applyColorRemovalFilter(
|
|
110264
|
+
canvas2,
|
|
110265
|
+
activeObject,
|
|
110266
|
+
hex,
|
|
110267
|
+
colorRemovalDistance / 100,
|
|
110268
|
+
true
|
|
110269
|
+
);
|
|
110270
|
+
}
|
|
110271
|
+
};
|
|
110272
|
+
const handleDistanceChange = (distPct) => {
|
|
110273
|
+
if (!canvas2 || !activeObject) return;
|
|
110274
|
+
setColorRemovalDistance(distPct);
|
|
110275
|
+
if (colorRemovalEnabled) {
|
|
110276
|
+
BackgroundRemovalService.applyColorRemovalFilter(
|
|
110277
|
+
canvas2,
|
|
110278
|
+
activeObject,
|
|
110279
|
+
colorRemovalColor,
|
|
110280
|
+
distPct / 100,
|
|
110281
|
+
false
|
|
110282
|
+
);
|
|
110283
|
+
}
|
|
110284
|
+
};
|
|
110285
|
+
const handleDistanceCommit = () => {
|
|
110286
|
+
saveState();
|
|
110287
|
+
};
|
|
109476
110288
|
useLayoutEffect(() => {
|
|
109477
110289
|
const newState = {
|
|
109478
110290
|
...initialState,
|
|
@@ -109501,6 +110313,16 @@ function EffectsPanel() {
|
|
|
109501
110313
|
newState.contrast = { enabled: true, value: Math.round(val * 100) };
|
|
109502
110314
|
}
|
|
109503
110315
|
});
|
|
110316
|
+
const rmFilter = obj.filters.find((f) => f.type === "RemoveColor");
|
|
110317
|
+
if (rmFilter) {
|
|
110318
|
+
setColorRemovalEnabled(true);
|
|
110319
|
+
setColorRemovalColor(rmFilter.color || "#FFFFFF");
|
|
110320
|
+
setColorRemovalDistance(Math.round((rmFilter.distance ?? 0.15) * 100));
|
|
110321
|
+
} else {
|
|
110322
|
+
setColorRemovalEnabled(false);
|
|
110323
|
+
}
|
|
110324
|
+
} else {
|
|
110325
|
+
setColorRemovalEnabled(false);
|
|
109504
110326
|
}
|
|
109505
110327
|
if (obj.shadow) {
|
|
109506
110328
|
try {
|
|
@@ -109548,6 +110370,105 @@ function EffectsPanel() {
|
|
|
109548
110370
|
{ id: "warm", label: "Warm" }
|
|
109549
110371
|
];
|
|
109550
110372
|
return /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto custom-scrollbar overflow-x-hidden px-4 custom-scrollbar", children: [
|
|
110373
|
+
isImg && /* @__PURE__ */ jsxs("div", { className: "bg-neutral-850 border border-neutral-700/70 rounded-xl p-3 mb-5 space-y-3", children: [
|
|
110374
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
110375
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
110376
|
+
/* @__PURE__ */ jsx(BgRemoveIcon, { size: 18, className: "text-[#02bc71]" }),
|
|
110377
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-white", children: "Background Removal" })
|
|
110378
|
+
] }),
|
|
110379
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] font-medium tracking-wide uppercase px-1.5 py-0.5 rounded bg-[#02bc71]/20 text-[#02bc71] border border-[#02bc71]/30", children: "AI Powered" })
|
|
110380
|
+
] }),
|
|
110381
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
110382
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
110383
|
+
/* @__PURE__ */ jsx(
|
|
110384
|
+
"button",
|
|
110385
|
+
{
|
|
110386
|
+
type: "button",
|
|
110387
|
+
disabled: isRemovingBg,
|
|
110388
|
+
onClick: handleRemoveBgAI,
|
|
110389
|
+
className: `flex-1 flex items-center justify-center gap-2 py-2 px-3 rounded-lg text-xs font-medium transition-all ${isRemovingBg ? "bg-neutral-700 text-neutral-400 cursor-not-allowed" : "bg-[#02bc71] hover:bg-[#029a5d] text-white shadow active:scale-95 cursor-pointer"}`,
|
|
110390
|
+
children: isRemovingBg ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
110391
|
+
/* @__PURE__ */ jsxs("svg", { className: "animate-spin h-3.5 w-3.5 text-white", viewBox: "0 0 24 24", fill: "none", children: [
|
|
110392
|
+
/* @__PURE__ */ jsx("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }),
|
|
110393
|
+
/* @__PURE__ */ jsx("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" })
|
|
110394
|
+
] }),
|
|
110395
|
+
/* @__PURE__ */ jsx("span", { children: "Processing..." })
|
|
110396
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
110397
|
+
/* @__PURE__ */ jsx(BgRemoveIcon, { size: 15 }),
|
|
110398
|
+
/* @__PURE__ */ jsx("span", { children: "Cutout Subject (AI)" })
|
|
110399
|
+
] })
|
|
110400
|
+
}
|
|
110401
|
+
),
|
|
110402
|
+
hasOriginal && /* @__PURE__ */ jsx(
|
|
110403
|
+
"button",
|
|
110404
|
+
{
|
|
110405
|
+
type: "button",
|
|
110406
|
+
disabled: isRemovingBg,
|
|
110407
|
+
onClick: handleRestoreBg,
|
|
110408
|
+
title: "Restore original image background",
|
|
110409
|
+
className: "px-3 py-2 rounded-lg text-xs font-medium bg-neutral-700 hover:bg-neutral-600 text-neutral-200 border border-neutral-600 transition cursor-pointer hover:text-white",
|
|
110410
|
+
children: "Restore"
|
|
110411
|
+
}
|
|
110412
|
+
)
|
|
110413
|
+
] }),
|
|
110414
|
+
bgProgress && /* @__PURE__ */ jsx("p", { className: "text-[11px] text-[#02bc71] italic text-center animate-pulse pt-0.5", children: bgProgress })
|
|
110415
|
+
] }),
|
|
110416
|
+
/* @__PURE__ */ jsxs("div", { className: "pt-2 border-t border-neutral-700/50", children: [
|
|
110417
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center mb-2", children: [
|
|
110418
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-neutral-300 font-medium", children: "Remove Solid Color" }),
|
|
110419
|
+
/* @__PURE__ */ jsxs("label", { className: "relative inline-flex items-center cursor-pointer", children: [
|
|
110420
|
+
/* @__PURE__ */ jsx(
|
|
110421
|
+
"input",
|
|
110422
|
+
{
|
|
110423
|
+
type: "checkbox",
|
|
110424
|
+
checked: colorRemovalEnabled,
|
|
110425
|
+
onChange: (e3) => handleToggleColorRemoval(e3.target.checked),
|
|
110426
|
+
className: "sr-only peer"
|
|
110427
|
+
}
|
|
110428
|
+
),
|
|
110429
|
+
/* @__PURE__ */ jsx("div", { className: "w-8 h-4 bg-neutral-700 rounded-full peer-checked:bg-[#02bc71] transition" }),
|
|
110430
|
+
/* @__PURE__ */ jsx("div", { className: "absolute left-0.5 top-0.5 w-3 h-3 bg-white rounded-full transition peer-checked:left-4.5" })
|
|
110431
|
+
] })
|
|
110432
|
+
] }),
|
|
110433
|
+
colorRemovalEnabled && /* @__PURE__ */ jsxs("div", { className: "space-y-2.5 pt-1", children: [
|
|
110434
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
110435
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs opacity-70", children: "Target Color" }),
|
|
110436
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
110437
|
+
/* @__PURE__ */ jsx("span", { className: "text-[11px] text-neutral-400 font-mono", children: colorRemovalColor.toUpperCase() }),
|
|
110438
|
+
/* @__PURE__ */ jsx(
|
|
110439
|
+
CMYKColorPicker,
|
|
110440
|
+
{
|
|
110441
|
+
value: colorRemovalColor,
|
|
110442
|
+
onChange: handleColorChange
|
|
110443
|
+
}
|
|
110444
|
+
)
|
|
110445
|
+
] })
|
|
110446
|
+
] }),
|
|
110447
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
110448
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center text-xs", children: [
|
|
110449
|
+
/* @__PURE__ */ jsx("span", { className: "opacity-70", children: "Tolerance / Distance" }),
|
|
110450
|
+
/* @__PURE__ */ jsxs("span", { className: "font-mono text-neutral-400", children: [
|
|
110451
|
+
colorRemovalDistance,
|
|
110452
|
+
"%"
|
|
110453
|
+
] })
|
|
110454
|
+
] }),
|
|
110455
|
+
/* @__PURE__ */ jsx(
|
|
110456
|
+
"input",
|
|
110457
|
+
{
|
|
110458
|
+
type: "range",
|
|
110459
|
+
min: 1,
|
|
110460
|
+
max: 99,
|
|
110461
|
+
value: colorRemovalDistance,
|
|
110462
|
+
onChange: (e3) => handleDistanceChange(Number(e3.target.value)),
|
|
110463
|
+
onMouseUp: handleDistanceCommit,
|
|
110464
|
+
onTouchEnd: handleDistanceCommit,
|
|
110465
|
+
className: "w-full accent-[#02bc71] cursor-pointer"
|
|
110466
|
+
}
|
|
110467
|
+
)
|
|
110468
|
+
] })
|
|
110469
|
+
] })
|
|
110470
|
+
] })
|
|
110471
|
+
] }),
|
|
109551
110472
|
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-3 mb-6", children: PRESETS.map((p2) => /* @__PURE__ */ jsx(
|
|
109552
110473
|
"button",
|
|
109553
110474
|
{
|
|
@@ -156143,7 +157064,7 @@ function RedoIcon() {
|
|
|
156143
157064
|
}
|
|
156144
157065
|
);
|
|
156145
157066
|
}
|
|
156146
|
-
function AlignLeftIcon() {
|
|
157067
|
+
function AlignLeftIcon$1() {
|
|
156147
157068
|
return /* @__PURE__ */ jsxs("svg", { width: "18", height: "18", viewBox: "0 0 16 16", fill: "currentColor", children: [
|
|
156148
157069
|
/* @__PURE__ */ jsx("rect", { x: "1", y: "2", width: "10", height: "2" }),
|
|
156149
157070
|
/* @__PURE__ */ jsx("rect", { x: "1", y: "6", width: "14", height: "2" }),
|
|
@@ -156151,7 +157072,7 @@ function AlignLeftIcon() {
|
|
|
156151
157072
|
/* @__PURE__ */ jsx("rect", { x: "1", y: "14", width: "14", height: "2" })
|
|
156152
157073
|
] });
|
|
156153
157074
|
}
|
|
156154
|
-
function AlignCenterIcon() {
|
|
157075
|
+
function AlignCenterIcon$1() {
|
|
156155
157076
|
return /* @__PURE__ */ jsxs("svg", { width: "18", height: "18", viewBox: "0 0 16 16", fill: "currentColor", children: [
|
|
156156
157077
|
/* @__PURE__ */ jsx("rect", { x: "3", y: "2", width: "10", height: "2" }),
|
|
156157
157078
|
/* @__PURE__ */ jsx("rect", { x: "1", y: "6", width: "14", height: "2" }),
|
|
@@ -156159,7 +157080,7 @@ function AlignCenterIcon() {
|
|
|
156159
157080
|
/* @__PURE__ */ jsx("rect", { x: "1", y: "14", width: "14", height: "2" })
|
|
156160
157081
|
] });
|
|
156161
157082
|
}
|
|
156162
|
-
function AlignRightIcon() {
|
|
157083
|
+
function AlignRightIcon$1() {
|
|
156163
157084
|
return /* @__PURE__ */ jsxs("svg", { width: "18", height: "18", viewBox: "0 0 16 16", fill: "currentColor", children: [
|
|
156164
157085
|
/* @__PURE__ */ jsx("rect", { x: "5", y: "2", width: "10", height: "2" }),
|
|
156165
157086
|
/* @__PURE__ */ jsx("rect", { x: "1", y: "6", width: "14", height: "2" }),
|
|
@@ -156228,9 +157149,9 @@ function TextAlignButton({
|
|
|
156228
157149
|
onClick: () => onChange(next),
|
|
156229
157150
|
className: "p-1 bg-neutral-800 rounded hover:bg-neutral-700",
|
|
156230
157151
|
children: [
|
|
156231
|
-
value === "left" && /* @__PURE__ */ jsx(AlignLeftIcon, {}),
|
|
156232
|
-
value === "center" && /* @__PURE__ */ jsx(AlignCenterIcon, {}),
|
|
156233
|
-
value === "right" && /* @__PURE__ */ jsx(AlignRightIcon, {})
|
|
157152
|
+
value === "left" && /* @__PURE__ */ jsx(AlignLeftIcon$1, {}),
|
|
157153
|
+
value === "center" && /* @__PURE__ */ jsx(AlignCenterIcon$1, {}),
|
|
157154
|
+
value === "right" && /* @__PURE__ */ jsx(AlignRightIcon$1, {})
|
|
156234
157155
|
]
|
|
156235
157156
|
}
|
|
156236
157157
|
);
|
|
@@ -156943,8 +157864,13 @@ function PictureToolbar() {
|
|
|
156943
157864
|
const { type, objects } = useSelectionStore();
|
|
156944
157865
|
const { canvas: canvas2, startCropping } = useEditorStore();
|
|
156945
157866
|
const saveState = useHistoryStore((s2) => s2.saveState);
|
|
156946
|
-
const hasMask = !!canvas2?.getActiveObject()?.clipPath;
|
|
156947
157867
|
const setActivePanel = useSidePanelStore((s2) => s2.setActivePanel);
|
|
157868
|
+
const [isRemovingBg, setIsRemovingBg] = useState(false);
|
|
157869
|
+
const [bgStatus, setBgStatus] = useState(null);
|
|
157870
|
+
const activeObj = canvas2?.getActiveObject();
|
|
157871
|
+
const hasMask = !!activeObj?.clipPath;
|
|
157872
|
+
activeObj && (activeObj.type === "image" || activeObj.isType?.("image"));
|
|
157873
|
+
const isBgRemoved = !!activeObj?.__originalSrc || !!activeObj?.__isBgRemoved;
|
|
156948
157874
|
if (type !== "image" || objects?.[0]?.name === "qr" || objects?.[0]?.name === "barcode")
|
|
156949
157875
|
return null;
|
|
156950
157876
|
const handleApplyMaskClick = () => {
|
|
@@ -156967,6 +157893,33 @@ function PictureToolbar() {
|
|
|
156967
157893
|
startCrop(canvas2, obj);
|
|
156968
157894
|
startCropping();
|
|
156969
157895
|
};
|
|
157896
|
+
const handleRemoveBgClick = async () => {
|
|
157897
|
+
if (!canvas2 || !activeObj || isRemovingBg) return;
|
|
157898
|
+
if (isBgRemoved) {
|
|
157899
|
+
setIsRemovingBg(true);
|
|
157900
|
+
try {
|
|
157901
|
+
await BackgroundRemovalService.restoreOriginalBackground(canvas2, activeObj);
|
|
157902
|
+
} finally {
|
|
157903
|
+
setIsRemovingBg(false);
|
|
157904
|
+
}
|
|
157905
|
+
return;
|
|
157906
|
+
}
|
|
157907
|
+
try {
|
|
157908
|
+
setIsRemovingBg(true);
|
|
157909
|
+
setBgStatus("Removing BG...");
|
|
157910
|
+
await BackgroundRemovalService.removeBackgroundAI(
|
|
157911
|
+
canvas2,
|
|
157912
|
+
activeObj,
|
|
157913
|
+
(status) => setBgStatus(status)
|
|
157914
|
+
);
|
|
157915
|
+
} catch (err) {
|
|
157916
|
+
console.error("BG removal failed:", err);
|
|
157917
|
+
alert("Background removal could not be completed: " + (err.message || err));
|
|
157918
|
+
} finally {
|
|
157919
|
+
setIsRemovingBg(false);
|
|
157920
|
+
setBgStatus(null);
|
|
157921
|
+
}
|
|
157922
|
+
};
|
|
156970
157923
|
return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-white py-2 rounded-md shadow-lg z-50", children: [
|
|
156971
157924
|
/* @__PURE__ */ jsxs(
|
|
156972
157925
|
"button",
|
|
@@ -156979,14 +157932,27 @@ function PictureToolbar() {
|
|
|
156979
157932
|
]
|
|
156980
157933
|
}
|
|
156981
157934
|
),
|
|
157935
|
+
/* @__PURE__ */ jsxs(
|
|
157936
|
+
"button",
|
|
157937
|
+
{
|
|
157938
|
+
className: `text-sm px-2 py-[5px] hover:bg-[#8f99a826] hover:rounded-sm flex gap-1.5 items-center cursor-pointer transition-colors ${isBgRemoved ? "text-emerald-400 font-medium" : ""} ${isRemovingBg ? "opacity-75 cursor-wait" : ""}`,
|
|
157939
|
+
onClick: handleRemoveBgClick,
|
|
157940
|
+
disabled: isRemovingBg,
|
|
157941
|
+
title: isBgRemoved ? "Click to restore original background" : "Automatically remove image background with AI",
|
|
157942
|
+
children: [
|
|
157943
|
+
isRemovingBg ? /* @__PURE__ */ jsx("div", { className: "w-3.5 h-3.5 border-2 border-white/30 border-t-white rounded-full animate-spin shrink-0" }) : /* @__PURE__ */ jsx(BgRemoveIcon, { size: 15 }),
|
|
157944
|
+
/* @__PURE__ */ jsx("span", { children: isRemovingBg ? bgStatus || "Processing..." : isBgRemoved ? "Restore BG" : "Remove BG" })
|
|
157945
|
+
]
|
|
157946
|
+
}
|
|
157947
|
+
),
|
|
156982
157948
|
/* @__PURE__ */ jsx(
|
|
156983
157949
|
"button",
|
|
156984
157950
|
{
|
|
156985
157951
|
className: "text-sm px-1 py-[5px] hover:bg-[#8f99a826] hover:rounded-sm cursor-pointer",
|
|
156986
157952
|
onClick: () => {
|
|
156987
|
-
const
|
|
156988
|
-
if (!
|
|
156989
|
-
fitImageToPage(
|
|
157953
|
+
const cvs = useEditorStore.getState().canvas;
|
|
157954
|
+
if (!cvs) return;
|
|
157955
|
+
fitImageToPage(cvs);
|
|
156990
157956
|
saveState();
|
|
156991
157957
|
},
|
|
156992
157958
|
children: "Fit to page"
|
|
@@ -157000,10 +157966,17 @@ function PictureToolbar() {
|
|
|
157000
157966
|
children: hasMask ? "Remove Mask" : "Apply Mask"
|
|
157001
157967
|
}
|
|
157002
157968
|
),
|
|
157003
|
-
/* @__PURE__ */ jsxs(
|
|
157004
|
-
|
|
157005
|
-
|
|
157006
|
-
|
|
157969
|
+
/* @__PURE__ */ jsxs(
|
|
157970
|
+
"button",
|
|
157971
|
+
{
|
|
157972
|
+
className: "text-sm px-1 py-[5px] hover:bg-[#8f99a826] hover:rounded-sm cursor-pointer flex items-center gap-1",
|
|
157973
|
+
onClick: handleCropClick,
|
|
157974
|
+
children: [
|
|
157975
|
+
/* @__PURE__ */ jsx(CropIcon, {}),
|
|
157976
|
+
/* @__PURE__ */ jsx("span", { children: "Crop" })
|
|
157977
|
+
]
|
|
157978
|
+
}
|
|
157979
|
+
)
|
|
157007
157980
|
] });
|
|
157008
157981
|
}
|
|
157009
157982
|
function QrEffectToolbar() {
|
|
@@ -157644,6 +158617,102 @@ const LayersIcon = () => /* @__PURE__ */ jsx(
|
|
|
157644
158617
|
)
|
|
157645
158618
|
}
|
|
157646
158619
|
);
|
|
158620
|
+
const AlignTopIcon = ({ className = "", size = 18 }) => /* @__PURE__ */ jsxs(
|
|
158621
|
+
"svg",
|
|
158622
|
+
{
|
|
158623
|
+
width: size,
|
|
158624
|
+
height: size,
|
|
158625
|
+
viewBox: "0 0 18 18",
|
|
158626
|
+
fill: "none",
|
|
158627
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
158628
|
+
className: `shrink-0 ${className}`,
|
|
158629
|
+
children: [
|
|
158630
|
+
/* @__PURE__ */ jsx("rect", { x: "2", y: "2", width: "14", height: "2", rx: "0.5", fill: "currentColor" }),
|
|
158631
|
+
/* @__PURE__ */ jsx("rect", { x: "4.5", y: "5.5", width: "2.5", height: "7.5", rx: "0.5", fill: "currentColor" }),
|
|
158632
|
+
/* @__PURE__ */ jsx("rect", { x: "10", y: "5.5", width: "2.5", height: "4.5", rx: "0.5", fill: "currentColor" })
|
|
158633
|
+
]
|
|
158634
|
+
}
|
|
158635
|
+
);
|
|
158636
|
+
const AlignLeftIcon = ({ className = "", size = 18 }) => /* @__PURE__ */ jsxs(
|
|
158637
|
+
"svg",
|
|
158638
|
+
{
|
|
158639
|
+
width: size,
|
|
158640
|
+
height: size,
|
|
158641
|
+
viewBox: "0 0 18 18",
|
|
158642
|
+
fill: "none",
|
|
158643
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
158644
|
+
className: `shrink-0 ${className}`,
|
|
158645
|
+
children: [
|
|
158646
|
+
/* @__PURE__ */ jsx("rect", { x: "2", y: "2", width: "2", height: "14", rx: "0.5", fill: "currentColor" }),
|
|
158647
|
+
/* @__PURE__ */ jsx("rect", { x: "5.5", y: "4.5", width: "7.5", height: "2.5", rx: "0.5", fill: "currentColor" }),
|
|
158648
|
+
/* @__PURE__ */ jsx("rect", { x: "5.5", y: "10", width: "4.5", height: "2.5", rx: "0.5", fill: "currentColor" })
|
|
158649
|
+
]
|
|
158650
|
+
}
|
|
158651
|
+
);
|
|
158652
|
+
const AlignMiddleIcon = ({ className = "", size = 18 }) => /* @__PURE__ */ jsxs(
|
|
158653
|
+
"svg",
|
|
158654
|
+
{
|
|
158655
|
+
width: size,
|
|
158656
|
+
height: size,
|
|
158657
|
+
viewBox: "0 0 18 18",
|
|
158658
|
+
fill: "none",
|
|
158659
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
158660
|
+
className: `shrink-0 ${className}`,
|
|
158661
|
+
children: [
|
|
158662
|
+
/* @__PURE__ */ jsx("line", { x1: "2", y1: "9", x2: "16", y2: "9", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }),
|
|
158663
|
+
/* @__PURE__ */ jsx("rect", { x: "4.5", y: "6", width: "2.5", height: "6", rx: "0.5", fill: "currentColor" }),
|
|
158664
|
+
/* @__PURE__ */ jsx("rect", { x: "10", y: "3.5", width: "2.5", height: "11", rx: "0.5", fill: "currentColor" })
|
|
158665
|
+
]
|
|
158666
|
+
}
|
|
158667
|
+
);
|
|
158668
|
+
const AlignCenterIcon = ({ className = "", size = 18 }) => /* @__PURE__ */ jsxs(
|
|
158669
|
+
"svg",
|
|
158670
|
+
{
|
|
158671
|
+
width: size,
|
|
158672
|
+
height: size,
|
|
158673
|
+
viewBox: "0 0 18 18",
|
|
158674
|
+
fill: "none",
|
|
158675
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
158676
|
+
className: `shrink-0 ${className}`,
|
|
158677
|
+
children: [
|
|
158678
|
+
/* @__PURE__ */ jsx("line", { x1: "9", y1: "2", x2: "9", y2: "16", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }),
|
|
158679
|
+
/* @__PURE__ */ jsx("rect", { x: "6", y: "4.5", width: "6", height: "2.5", rx: "0.5", fill: "currentColor" }),
|
|
158680
|
+
/* @__PURE__ */ jsx("rect", { x: "3.5", y: "10", width: "11", height: "2.5", rx: "0.5", fill: "currentColor" })
|
|
158681
|
+
]
|
|
158682
|
+
}
|
|
158683
|
+
);
|
|
158684
|
+
const AlignBottomIcon = ({ className = "", size = 18 }) => /* @__PURE__ */ jsxs(
|
|
158685
|
+
"svg",
|
|
158686
|
+
{
|
|
158687
|
+
width: size,
|
|
158688
|
+
height: size,
|
|
158689
|
+
viewBox: "0 0 18 18",
|
|
158690
|
+
fill: "none",
|
|
158691
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
158692
|
+
className: `shrink-0 ${className}`,
|
|
158693
|
+
children: [
|
|
158694
|
+
/* @__PURE__ */ jsx("rect", { x: "2", y: "14", width: "14", height: "2", rx: "0.5", fill: "currentColor" }),
|
|
158695
|
+
/* @__PURE__ */ jsx("rect", { x: "4.5", y: "8", width: "2.5", height: "4.5", rx: "0.5", fill: "currentColor" }),
|
|
158696
|
+
/* @__PURE__ */ jsx("rect", { x: "10", y: "5", width: "2.5", height: "7.5", rx: "0.5", fill: "currentColor" })
|
|
158697
|
+
]
|
|
158698
|
+
}
|
|
158699
|
+
);
|
|
158700
|
+
const AlignRightIcon = ({ className = "", size = 18 }) => /* @__PURE__ */ jsxs(
|
|
158701
|
+
"svg",
|
|
158702
|
+
{
|
|
158703
|
+
width: size,
|
|
158704
|
+
height: size,
|
|
158705
|
+
viewBox: "0 0 18 18",
|
|
158706
|
+
fill: "none",
|
|
158707
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
158708
|
+
className: `shrink-0 ${className}`,
|
|
158709
|
+
children: [
|
|
158710
|
+
/* @__PURE__ */ jsx("rect", { x: "14", y: "2", width: "2", height: "14", rx: "0.5", fill: "currentColor" }),
|
|
158711
|
+
/* @__PURE__ */ jsx("rect", { x: "8", y: "4.5", width: "4.5", height: "2.5", rx: "0.5", fill: "currentColor" }),
|
|
158712
|
+
/* @__PURE__ */ jsx("rect", { x: "5", y: "10", width: "7.5", height: "2.5", rx: "0.5", fill: "currentColor" })
|
|
158713
|
+
]
|
|
158714
|
+
}
|
|
158715
|
+
);
|
|
157647
158716
|
function PositionPicker() {
|
|
157648
158717
|
const canvas2 = useEditorStore((s2) => s2.canvas);
|
|
157649
158718
|
const saveState = useHistoryStore((s2) => s2.saveState);
|
|
@@ -157827,26 +158896,114 @@ function PositionPicker() {
|
|
|
157827
158896
|
showPosition && /* @__PURE__ */ jsxs(
|
|
157828
158897
|
"div",
|
|
157829
158898
|
{
|
|
157830
|
-
className: "fixed md:absolute top-[112px] md:top-10 left-1/2 -translate-x-1/2 bg-neutral-800 text-white rounded-xl shadow-xl p-4 w-[
|
|
158899
|
+
className: "fixed md:absolute top-[112px] md:top-10 left-1/2 -translate-x-1/2 bg-neutral-800 text-white rounded-xl shadow-xl p-4 w-[280px] z-[9999]",
|
|
157831
158900
|
onClick: (e3) => e3.stopPropagation(),
|
|
157832
158901
|
onMouseDown: (e3) => e3.stopPropagation(),
|
|
157833
158902
|
children: [
|
|
157834
|
-
/* @__PURE__ */ jsx("div", { className: "text-xs uppercase opacity-60 mb-2", children: "Layering" }),
|
|
157835
|
-
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-
|
|
157836
|
-
/* @__PURE__ */ jsx(
|
|
157837
|
-
|
|
157838
|
-
|
|
157839
|
-
|
|
158903
|
+
/* @__PURE__ */ jsx("div", { className: "text-xs uppercase opacity-60 mb-2 font-medium tracking-wide", children: "Layering" }),
|
|
158904
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-1.5 mb-3", children: [
|
|
158905
|
+
/* @__PURE__ */ jsx(
|
|
158906
|
+
"button",
|
|
158907
|
+
{
|
|
158908
|
+
onClick: () => bringForward(),
|
|
158909
|
+
className: "flex items-center px-2 py-1.5 rounded text-sm text-gray-200 hover:text-white hover:bg-white/10 transition-colors text-left",
|
|
158910
|
+
children: "Forward"
|
|
158911
|
+
}
|
|
158912
|
+
),
|
|
158913
|
+
/* @__PURE__ */ jsx(
|
|
158914
|
+
"button",
|
|
158915
|
+
{
|
|
158916
|
+
onClick: () => sendBackward(),
|
|
158917
|
+
className: "flex items-center px-2 py-1.5 rounded text-sm text-gray-200 hover:text-white hover:bg-white/10 transition-colors text-left",
|
|
158918
|
+
children: "Backward"
|
|
158919
|
+
}
|
|
158920
|
+
),
|
|
158921
|
+
/* @__PURE__ */ jsx(
|
|
158922
|
+
"button",
|
|
158923
|
+
{
|
|
158924
|
+
onClick: () => bringToFront(),
|
|
158925
|
+
className: "flex items-center px-2 py-1.5 rounded text-sm text-gray-200 hover:text-white hover:bg-white/10 transition-colors text-left",
|
|
158926
|
+
children: "To Front"
|
|
158927
|
+
}
|
|
158928
|
+
),
|
|
158929
|
+
/* @__PURE__ */ jsx(
|
|
158930
|
+
"button",
|
|
158931
|
+
{
|
|
158932
|
+
onClick: () => sendToBack(),
|
|
158933
|
+
className: "flex items-center px-2 py-1.5 rounded text-sm text-gray-200 hover:text-white hover:bg-white/10 transition-colors text-left",
|
|
158934
|
+
children: "To Back"
|
|
158935
|
+
}
|
|
158936
|
+
)
|
|
157840
158937
|
] }),
|
|
157841
158938
|
/* @__PURE__ */ jsx("hr", { className: "border-neutral-700 my-2" }),
|
|
157842
|
-
/* @__PURE__ */ jsx("div", { className: "text-xs uppercase opacity-60 mb-2", children: "
|
|
157843
|
-
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
157844
|
-
/* @__PURE__ */
|
|
157845
|
-
|
|
157846
|
-
|
|
157847
|
-
|
|
157848
|
-
|
|
157849
|
-
|
|
158939
|
+
/* @__PURE__ */ jsx("div", { className: "text-xs uppercase opacity-60 mb-2 font-medium tracking-wide", children: "Alignments" }),
|
|
158940
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-x-2 gap-y-1.5", children: [
|
|
158941
|
+
/* @__PURE__ */ jsxs(
|
|
158942
|
+
"button",
|
|
158943
|
+
{
|
|
158944
|
+
onClick: () => handleAlign("top"),
|
|
158945
|
+
className: "flex items-center gap-2.5 px-2 py-1.5 rounded text-sm text-gray-200 hover:text-white hover:bg-white/10 transition-colors text-left",
|
|
158946
|
+
children: [
|
|
158947
|
+
/* @__PURE__ */ jsx(AlignTopIcon, { size: 18 }),
|
|
158948
|
+
/* @__PURE__ */ jsx("span", { children: "Top" })
|
|
158949
|
+
]
|
|
158950
|
+
}
|
|
158951
|
+
),
|
|
158952
|
+
/* @__PURE__ */ jsxs(
|
|
158953
|
+
"button",
|
|
158954
|
+
{
|
|
158955
|
+
onClick: () => handleAlign("left"),
|
|
158956
|
+
className: "flex items-center gap-2.5 px-2 py-1.5 rounded text-sm text-gray-200 hover:text-white hover:bg-white/10 transition-colors text-left",
|
|
158957
|
+
children: [
|
|
158958
|
+
/* @__PURE__ */ jsx(AlignLeftIcon, { size: 18 }),
|
|
158959
|
+
/* @__PURE__ */ jsx("span", { children: "Left" })
|
|
158960
|
+
]
|
|
158961
|
+
}
|
|
158962
|
+
),
|
|
158963
|
+
/* @__PURE__ */ jsxs(
|
|
158964
|
+
"button",
|
|
158965
|
+
{
|
|
158966
|
+
onClick: () => handleAlign("middle"),
|
|
158967
|
+
className: "flex items-center gap-2.5 px-2 py-1.5 rounded text-sm text-gray-200 hover:text-white hover:bg-white/10 transition-colors text-left",
|
|
158968
|
+
children: [
|
|
158969
|
+
/* @__PURE__ */ jsx(AlignMiddleIcon, { size: 18 }),
|
|
158970
|
+
/* @__PURE__ */ jsx("span", { children: "Middle" })
|
|
158971
|
+
]
|
|
158972
|
+
}
|
|
158973
|
+
),
|
|
158974
|
+
/* @__PURE__ */ jsxs(
|
|
158975
|
+
"button",
|
|
158976
|
+
{
|
|
158977
|
+
onClick: () => handleAlign("center"),
|
|
158978
|
+
className: "flex items-center gap-2.5 px-2 py-1.5 rounded text-sm text-gray-200 hover:text-white hover:bg-white/10 transition-colors text-left",
|
|
158979
|
+
children: [
|
|
158980
|
+
/* @__PURE__ */ jsx(AlignCenterIcon, { size: 18 }),
|
|
158981
|
+
/* @__PURE__ */ jsx("span", { children: "Center" })
|
|
158982
|
+
]
|
|
158983
|
+
}
|
|
158984
|
+
),
|
|
158985
|
+
/* @__PURE__ */ jsxs(
|
|
158986
|
+
"button",
|
|
158987
|
+
{
|
|
158988
|
+
onClick: () => handleAlign("bottom"),
|
|
158989
|
+
className: "flex items-center gap-2.5 px-2 py-1.5 rounded text-sm text-gray-200 hover:text-white hover:bg-white/10 transition-colors text-left",
|
|
158990
|
+
children: [
|
|
158991
|
+
/* @__PURE__ */ jsx(AlignBottomIcon, { size: 18 }),
|
|
158992
|
+
/* @__PURE__ */ jsx("span", { children: "Bottom" })
|
|
158993
|
+
]
|
|
158994
|
+
}
|
|
158995
|
+
),
|
|
158996
|
+
/* @__PURE__ */ jsxs(
|
|
158997
|
+
"button",
|
|
158998
|
+
{
|
|
158999
|
+
onClick: () => handleAlign("right"),
|
|
159000
|
+
className: "flex items-center gap-2.5 px-2 py-1.5 rounded text-sm text-gray-200 hover:text-white hover:bg-white/10 transition-colors text-left",
|
|
159001
|
+
children: [
|
|
159002
|
+
/* @__PURE__ */ jsx(AlignRightIcon, { size: 18 }),
|
|
159003
|
+
/* @__PURE__ */ jsx("span", { children: "Right" })
|
|
159004
|
+
]
|
|
159005
|
+
}
|
|
159006
|
+
)
|
|
157850
159007
|
] })
|
|
157851
159008
|
]
|
|
157852
159009
|
}
|
|
@@ -158526,14 +159683,45 @@ function enablePanMode(canvas2) {
|
|
|
158526
159683
|
top: (workarea.top ?? 0) + dy
|
|
158527
159684
|
});
|
|
158528
159685
|
workarea.setCoords();
|
|
159686
|
+
if (workarea.clipPath) {
|
|
159687
|
+
workarea.clipPath.set({
|
|
159688
|
+
left: (workarea.clipPath.left ?? 0) + dx,
|
|
159689
|
+
top: (workarea.clipPath.top ?? 0) + dy
|
|
159690
|
+
});
|
|
159691
|
+
workarea.clipPath.setCoords();
|
|
159692
|
+
}
|
|
158529
159693
|
canvas2.getObjects().forEach((obj) => {
|
|
158530
|
-
if (obj.name === "workarea" || obj.name?.startsWith("overlay-"))
|
|
159694
|
+
if (obj.name === "workarea" || obj.name?.startsWith("overlay-") || obj.name?.startsWith("guideline-")) {
|
|
159695
|
+
return;
|
|
159696
|
+
}
|
|
158531
159697
|
obj.set({
|
|
158532
159698
|
left: (obj.left ?? 0) + dx,
|
|
158533
159699
|
top: (obj.top ?? 0) + dy
|
|
158534
159700
|
});
|
|
158535
159701
|
obj.setCoords();
|
|
159702
|
+
if (obj.clipPath) {
|
|
159703
|
+
obj.clipPath.set({
|
|
159704
|
+
left: (obj.clipPath.left ?? 0) + dx,
|
|
159705
|
+
top: (obj.clipPath.top ?? 0) + dy
|
|
159706
|
+
});
|
|
159707
|
+
obj.clipPath.setCoords();
|
|
159708
|
+
}
|
|
158536
159709
|
});
|
|
159710
|
+
if (canvas2.backgroundImage) {
|
|
159711
|
+
const bg = canvas2.backgroundImage;
|
|
159712
|
+
bg.set({
|
|
159713
|
+
left: (bg.left ?? 0) + dx,
|
|
159714
|
+
top: (bg.top ?? 0) + dy
|
|
159715
|
+
});
|
|
159716
|
+
bg.setCoords();
|
|
159717
|
+
if (bg.clipPath) {
|
|
159718
|
+
bg.clipPath.set({
|
|
159719
|
+
left: (bg.clipPath.left ?? 0) + dx,
|
|
159720
|
+
top: (bg.clipPath.top ?? 0) + dy
|
|
159721
|
+
});
|
|
159722
|
+
bg.clipPath.setCoords();
|
|
159723
|
+
}
|
|
159724
|
+
}
|
|
158537
159725
|
createOverlays(canvas2, workarea);
|
|
158538
159726
|
canvas2.requestRenderAll();
|
|
158539
159727
|
};
|
|
@@ -158559,7 +159747,7 @@ function disablePanMode(canvas2) {
|
|
|
158559
159747
|
canvas2.selection = true;
|
|
158560
159748
|
canvas2.defaultCursor = "default";
|
|
158561
159749
|
canvas2.forEachObject((obj) => {
|
|
158562
|
-
if (obj.name !== "workarea" && !obj.name?.startsWith("overlay-")) {
|
|
159750
|
+
if (obj.name !== "workarea" && obj.name !== "bg-image" && !obj.name?.startsWith("overlay-") && !obj.name?.startsWith("guideline-")) {
|
|
158563
159751
|
obj.selectable = true;
|
|
158564
159752
|
obj.evented = true;
|
|
158565
159753
|
} else {
|
|
@@ -158648,8 +159836,12 @@ function createWorkarea(canvas2, container, productData, pageSide) {
|
|
|
158648
159836
|
const containerWidth = container.clientWidth;
|
|
158649
159837
|
const containerHeight = container.clientHeight;
|
|
158650
159838
|
const size = productData?.optionsMap?.size?.split(" x ") || ["0", "0"];
|
|
158651
|
-
const
|
|
158652
|
-
const
|
|
159839
|
+
const bleed = Number(productData?.bleedArea || 0);
|
|
159840
|
+
const rawW = Number(size[0].replace('"', "")) || 3.5;
|
|
159841
|
+
const rawH = Number(size[1].replace('"', "")) || 2;
|
|
159842
|
+
const isVertical = productData?.optionsMap?.productorientation === "Vertical" || productData?.optionsMap?.orientation === "Vertical";
|
|
159843
|
+
const printWidth = (isVertical ? rawW : rawH) + bleed;
|
|
159844
|
+
const printHeight = (isVertical ? rawH : rawW) + bleed;
|
|
158653
159845
|
const printDPI = 72;
|
|
158654
159846
|
const fullWidth = printWidth * printDPI;
|
|
158655
159847
|
const fullHeight = printHeight * printDPI;
|
|
@@ -158680,7 +159872,6 @@ function createWorkarea(canvas2, container, productData, pageSide) {
|
|
|
158680
159872
|
);
|
|
158681
159873
|
const rawShape = productData?.optionsMap?.shape || productData?.optionsMap?.Shape || productData?.shape || "";
|
|
158682
159874
|
const shapeType = detectShape(rawShape);
|
|
158683
|
-
const isVertical = productData?.optionsMap?.productorientation === "Vertical" || productData?.optionsMap?.orientation === "Vertical";
|
|
158684
159875
|
const { rx, ry, radiusOfCanvas } = calculateShapeRadius(
|
|
158685
159876
|
shapeType,
|
|
158686
159877
|
fullWidth,
|
|
@@ -158691,6 +159882,8 @@ function createWorkarea(canvas2, container, productData, pageSide) {
|
|
|
158691
159882
|
width: fullWidth,
|
|
158692
159883
|
height: fullHeight,
|
|
158693
159884
|
fill: "#ffffff",
|
|
159885
|
+
strokeWidth: 0,
|
|
159886
|
+
stroke: null,
|
|
158694
159887
|
selectable: false,
|
|
158695
159888
|
evented: false,
|
|
158696
159889
|
name: "workarea",
|
|
@@ -158720,22 +159913,48 @@ function createWorkarea(canvas2, container, productData, pageSide) {
|
|
|
158720
159913
|
return workarea;
|
|
158721
159914
|
}
|
|
158722
159915
|
function setupHistoryRecording(canvas2) {
|
|
158723
|
-
|
|
159916
|
+
let modifiedTimer = null;
|
|
159917
|
+
const handleObjectAddedOrRemoved = (e3) => {
|
|
158724
159918
|
const target = e3?.target;
|
|
158725
159919
|
if (!target) return;
|
|
158726
|
-
|
|
159920
|
+
const name = target.name || "";
|
|
159921
|
+
if (name === "workarea" || name === "watermark" || name.startsWith("overlay-") || name.startsWith("guideline-")) {
|
|
159922
|
+
return;
|
|
159923
|
+
}
|
|
158727
159924
|
if (useHistoryStore.getState().isRestoring) return;
|
|
159925
|
+
if (modifiedTimer) {
|
|
159926
|
+
clearTimeout(modifiedTimer);
|
|
159927
|
+
modifiedTimer = null;
|
|
159928
|
+
}
|
|
158728
159929
|
requestAnimationFrame(() => {
|
|
158729
|
-
useHistoryStore.getState().
|
|
159930
|
+
if (!useHistoryStore.getState().isRestoring) {
|
|
159931
|
+
useHistoryStore.getState().saveState();
|
|
159932
|
+
}
|
|
158730
159933
|
});
|
|
158731
159934
|
};
|
|
158732
|
-
|
|
158733
|
-
|
|
158734
|
-
|
|
159935
|
+
const handleObjectModified = (e3) => {
|
|
159936
|
+
const target = e3?.target;
|
|
159937
|
+
if (!target) return;
|
|
159938
|
+
const name = target.name || "";
|
|
159939
|
+
if (name === "workarea" || name === "watermark" || name.startsWith("overlay-") || name.startsWith("guideline-")) {
|
|
159940
|
+
return;
|
|
159941
|
+
}
|
|
159942
|
+
if (useHistoryStore.getState().isRestoring) return;
|
|
159943
|
+
clearTimeout(modifiedTimer);
|
|
159944
|
+
modifiedTimer = setTimeout(() => {
|
|
159945
|
+
if (!useHistoryStore.getState().isRestoring) {
|
|
159946
|
+
useHistoryStore.getState().saveState();
|
|
159947
|
+
}
|
|
159948
|
+
}, 150);
|
|
159949
|
+
};
|
|
159950
|
+
canvas2.on("object:added", handleObjectAddedOrRemoved);
|
|
159951
|
+
canvas2.on("object:removed", handleObjectAddedOrRemoved);
|
|
159952
|
+
canvas2.on("object:modified", handleObjectModified);
|
|
158735
159953
|
return () => {
|
|
158736
|
-
|
|
158737
|
-
canvas2.off("object:
|
|
158738
|
-
canvas2.off("object:removed",
|
|
159954
|
+
if (modifiedTimer) clearTimeout(modifiedTimer);
|
|
159955
|
+
canvas2.off("object:added", handleObjectAddedOrRemoved);
|
|
159956
|
+
canvas2.off("object:removed", handleObjectAddedOrRemoved);
|
|
159957
|
+
canvas2.off("object:modified", handleObjectModified);
|
|
158739
159958
|
};
|
|
158740
159959
|
}
|
|
158741
159960
|
function setupDoublClickCrop(canvas2) {
|
|
@@ -158947,8 +160166,10 @@ async function switchCanvasPage(canvas2, currentPage, targetPage, pageJSON, clea
|
|
|
158947
160166
|
createWorkarea(canvas2, containerRef.current, productData, targetPage);
|
|
158948
160167
|
}
|
|
158949
160168
|
}
|
|
158950
|
-
historyStore.saveState();
|
|
158951
160169
|
useHistoryStore.setState({ isRestoring: false });
|
|
160170
|
+
if ((useHistoryStore.getState().pages[targetPage]?.history?.length ?? 0) === 0) {
|
|
160171
|
+
historyStore.saveState();
|
|
160172
|
+
}
|
|
158952
160173
|
} finally {
|
|
158953
160174
|
useLoaderStore.getState().stopLoading();
|
|
158954
160175
|
}
|
|
@@ -158988,9 +160209,62 @@ function doZoom(canvas2, newZoom, pivot) {
|
|
|
158988
160209
|
function doReset(canvas2, setZoom) {
|
|
158989
160210
|
const workarea = getWorkarea(canvas2);
|
|
158990
160211
|
const initialZoom = workarea ? workarea.__initialZoom || 1 : 1;
|
|
158991
|
-
const
|
|
158992
|
-
canvas2.
|
|
160212
|
+
const canvasW = canvas2.getWidth();
|
|
160213
|
+
const canvasH = canvas2.getHeight();
|
|
160214
|
+
canvas2.setViewportTransform([1, 0, 0, 1, 0, 0]);
|
|
160215
|
+
canvas2.setZoom(1);
|
|
158993
160216
|
if (workarea) {
|
|
160217
|
+
const prevLeft = workarea.left ?? 0;
|
|
160218
|
+
const prevTop = workarea.top ?? 0;
|
|
160219
|
+
canvas2.centerObject(workarea);
|
|
160220
|
+
workarea.setCoords();
|
|
160221
|
+
const dx = (workarea.left ?? 0) - prevLeft;
|
|
160222
|
+
const dy = (workarea.top ?? 0) - prevTop;
|
|
160223
|
+
if (Math.abs(dx) > 1e-3 || Math.abs(dy) > 1e-3) {
|
|
160224
|
+
canvas2.getObjects().forEach((obj) => {
|
|
160225
|
+
if (obj.name === "workarea" || obj.name?.startsWith("overlay-") || obj.name?.startsWith("guideline-")) {
|
|
160226
|
+
return;
|
|
160227
|
+
}
|
|
160228
|
+
obj.set({
|
|
160229
|
+
left: (obj.left ?? 0) + dx,
|
|
160230
|
+
top: (obj.top ?? 0) + dy
|
|
160231
|
+
});
|
|
160232
|
+
obj.setCoords();
|
|
160233
|
+
if (obj.clipPath) {
|
|
160234
|
+
obj.clipPath.set({
|
|
160235
|
+
left: (obj.clipPath.left ?? 0) + dx,
|
|
160236
|
+
top: (obj.clipPath.top ?? 0) + dy
|
|
160237
|
+
});
|
|
160238
|
+
obj.clipPath.setCoords();
|
|
160239
|
+
}
|
|
160240
|
+
});
|
|
160241
|
+
if (workarea.clipPath) {
|
|
160242
|
+
workarea.clipPath.set({
|
|
160243
|
+
left: (workarea.clipPath.left ?? 0) + dx,
|
|
160244
|
+
top: (workarea.clipPath.top ?? 0) + dy
|
|
160245
|
+
});
|
|
160246
|
+
workarea.clipPath.setCoords();
|
|
160247
|
+
}
|
|
160248
|
+
if (canvas2.backgroundImage) {
|
|
160249
|
+
const bg = canvas2.backgroundImage;
|
|
160250
|
+
bg.set({
|
|
160251
|
+
left: (bg.left ?? 0) + dx,
|
|
160252
|
+
top: (bg.top ?? 0) + dy
|
|
160253
|
+
});
|
|
160254
|
+
bg.setCoords();
|
|
160255
|
+
if (bg.clipPath) {
|
|
160256
|
+
bg.clipPath.set({
|
|
160257
|
+
left: (bg.clipPath.left ?? 0) + dx,
|
|
160258
|
+
top: (bg.clipPath.top ?? 0) + dy
|
|
160259
|
+
});
|
|
160260
|
+
bg.clipPath.setCoords();
|
|
160261
|
+
}
|
|
160262
|
+
}
|
|
160263
|
+
}
|
|
160264
|
+
if (initialZoom !== 1) {
|
|
160265
|
+
const center = new ot(canvasW / 2, canvasH / 2);
|
|
160266
|
+
canvas2.zoomToPoint(center, initialZoom);
|
|
160267
|
+
}
|
|
158994
160268
|
createOverlays(canvas2, workarea);
|
|
158995
160269
|
}
|
|
158996
160270
|
const activeObject = canvas2.getActiveObject();
|
|
@@ -158998,7 +160272,7 @@ function doReset(canvas2, setZoom) {
|
|
|
158998
160272
|
canvas2.requestRenderAll();
|
|
158999
160273
|
setZoom(initialZoom);
|
|
159000
160274
|
}
|
|
159001
|
-
function useCanvasZoom(canvas2) {
|
|
160275
|
+
function useCanvasZoom(canvas2, options) {
|
|
159002
160276
|
const [zoom, setZoom] = useState(1);
|
|
159003
160277
|
const rafRef = useRef(null);
|
|
159004
160278
|
const wheelAccum = useRef(0);
|
|
@@ -159089,12 +160363,13 @@ function useCanvasZoom(canvas2) {
|
|
|
159089
160363
|
} else if (e3.key === "0") {
|
|
159090
160364
|
e3.preventDefault();
|
|
159091
160365
|
doReset(canvas2, setZoom);
|
|
160366
|
+
options?.onReset?.();
|
|
159092
160367
|
}
|
|
159093
160368
|
}
|
|
159094
160369
|
};
|
|
159095
160370
|
window.addEventListener("keydown", onKey);
|
|
159096
160371
|
return () => window.removeEventListener("keydown", onKey);
|
|
159097
|
-
}, [canvas2, applyZoom]);
|
|
160372
|
+
}, [canvas2, applyZoom, options]);
|
|
159098
160373
|
const zoomIn = useCallback(() => {
|
|
159099
160374
|
if (!canvas2) return;
|
|
159100
160375
|
applyZoom(clamp(canvas2.getZoom() + STEP, MIN_ZOOM, MAX_ZOOM));
|
|
@@ -159106,7 +160381,8 @@ function useCanvasZoom(canvas2) {
|
|
|
159106
160381
|
const zoomReset = useCallback(() => {
|
|
159107
160382
|
if (!canvas2) return;
|
|
159108
160383
|
doReset(canvas2, setZoom);
|
|
159109
|
-
|
|
160384
|
+
options?.onReset?.();
|
|
160385
|
+
}, [canvas2, options]);
|
|
159110
160386
|
const zoomTo = useCallback(
|
|
159111
160387
|
(value) => applyZoom(clamp(value, MIN_ZOOM, MAX_ZOOM)),
|
|
159112
160388
|
[applyZoom]
|
|
@@ -159121,9 +160397,9 @@ function useCanvasZoom(canvas2) {
|
|
|
159121
160397
|
zoomTo
|
|
159122
160398
|
};
|
|
159123
160399
|
}
|
|
159124
|
-
function ZoomControls() {
|
|
160400
|
+
function ZoomControls({ onReset }) {
|
|
159125
160401
|
const canvas2 = useEditorStore((s2) => s2.canvas);
|
|
159126
|
-
const { zoom, atMin, atMax, zoomIn, zoomOut, zoomReset, zoomTo } = useCanvasZoom(canvas2);
|
|
160402
|
+
const { zoom, atMin, atMax, zoomIn, zoomOut, zoomReset, zoomTo } = useCanvasZoom(canvas2, { onReset });
|
|
159127
160403
|
const [sliderOpen, setSliderOpen] = useState(false);
|
|
159128
160404
|
const sliderRef = useRef(null);
|
|
159129
160405
|
useEffect(() => {
|
|
@@ -159349,6 +160625,18 @@ function useKeyboardEvents(canvas2) {
|
|
|
159349
160625
|
e3.preventDefault();
|
|
159350
160626
|
handlePaste();
|
|
159351
160627
|
}
|
|
160628
|
+
if (isModifier && e3.key.toLowerCase() === "z") {
|
|
160629
|
+
e3.preventDefault();
|
|
160630
|
+
if (e3.shiftKey) {
|
|
160631
|
+
useHistoryStore.getState().redoAction();
|
|
160632
|
+
} else {
|
|
160633
|
+
useHistoryStore.getState().undo();
|
|
160634
|
+
}
|
|
160635
|
+
}
|
|
160636
|
+
if (isModifier && e3.key.toLowerCase() === "y") {
|
|
160637
|
+
e3.preventDefault();
|
|
160638
|
+
useHistoryStore.getState().redoAction();
|
|
160639
|
+
}
|
|
159352
160640
|
};
|
|
159353
160641
|
window.addEventListener("keydown", handleKeyDown);
|
|
159354
160642
|
return () => {
|
|
@@ -159465,6 +160753,9 @@ function Workspace() {
|
|
|
159465
160753
|
pageJSON.current.back = backData;
|
|
159466
160754
|
}
|
|
159467
160755
|
}
|
|
160756
|
+
if (useHistoryStore.getState().pages.front.history.length === 0) {
|
|
160757
|
+
useHistoryStore.getState().saveState();
|
|
160758
|
+
}
|
|
159468
160759
|
useEditorStore.getState().setPages({ ...pageJSON.current });
|
|
159469
160760
|
return () => {
|
|
159470
160761
|
cleanupHistory();
|
|
@@ -159582,7 +160873,7 @@ function Workspace() {
|
|
|
159582
160873
|
)
|
|
159583
160874
|
] }),
|
|
159584
160875
|
/* @__PURE__ */ jsx("div", { className: "w-[1px] h-6 bg-gray-300/50 mx-3" }),
|
|
159585
|
-
/* @__PURE__ */ jsx(ZoomControls, {})
|
|
160876
|
+
/* @__PURE__ */ jsx(ZoomControls, { onReset: () => setIsPanning(false) })
|
|
159586
160877
|
] }),
|
|
159587
160878
|
/* @__PURE__ */ jsx(
|
|
159588
160879
|
"div",
|