@pixerate/editor-react 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/canvas/index.cjs +26 -3
- package/dist/canvas/index.cjs.map +1 -1
- package/dist/canvas/index.d.cts +3 -1
- package/dist/canvas/index.d.ts +3 -1
- package/dist/canvas/index.js +27 -4
- package/dist/canvas/index.js.map +1 -1
- package/dist/chunk-NKQKD43U.js +1200 -0
- package/dist/chunk-NKQKD43U.js.map +1 -0
- package/dist/image-editor/index.cjs +1219 -0
- package/dist/image-editor/index.cjs.map +1 -0
- package/dist/image-editor/index.d.cts +53 -0
- package/dist/image-editor/index.d.ts +53 -0
- package/dist/image-editor/index.js +9 -0
- package/dist/image-editor/index.js.map +1 -0
- package/dist/index.cjs +1197 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/package.json +10 -2
package/dist/index.cjs
CHANGED
|
@@ -31,20 +31,22 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var src_exports = {};
|
|
32
32
|
__export(src_exports, {
|
|
33
33
|
BubbleMenu: () => BubbleMenu,
|
|
34
|
-
DirtyTracker: () =>
|
|
34
|
+
DirtyTracker: () => import_editor9.DirtyTracker,
|
|
35
35
|
EditorContent: () => EditorContent,
|
|
36
36
|
EditorSlotsProvider: () => EditorSlotsProvider,
|
|
37
|
-
HistoryManager: () =>
|
|
37
|
+
HistoryManager: () => import_editor9.HistoryManager,
|
|
38
|
+
ImageEditor: () => ImageEditor,
|
|
38
39
|
SpreadsheetEditor: () => SpreadsheetEditor,
|
|
39
40
|
TemplateRenderer: () => TemplateRenderer,
|
|
40
|
-
createDirtyTracker: () =>
|
|
41
|
-
createHistoryManager: () =>
|
|
42
|
-
defaultIsEqual: () =>
|
|
41
|
+
createDirtyTracker: () => import_editor9.createDirtyTracker,
|
|
42
|
+
createHistoryManager: () => import_editor9.createHistoryManager,
|
|
43
|
+
defaultIsEqual: () => import_editor9.defaultIsEqual,
|
|
43
44
|
useDismissGuard: () => useDismissGuard,
|
|
44
45
|
useEditor: () => useEditor2,
|
|
45
46
|
useEditorSlots: () => useEditorSlots,
|
|
46
47
|
useHistory: () => useHistory,
|
|
47
48
|
useHistoryShortcuts: () => useHistoryShortcuts,
|
|
49
|
+
useImageEditor: () => useImageEditor,
|
|
48
50
|
useNavigationGuard: () => useNavigationGuard,
|
|
49
51
|
usePromptEditor: () => usePromptEditor,
|
|
50
52
|
useSpreadsheetEditor: () => useSpreadsheetEditor
|
|
@@ -620,7 +622,7 @@ var TemplateRenderer = ({
|
|
|
620
622
|
};
|
|
621
623
|
|
|
622
624
|
// src/index.ts
|
|
623
|
-
var
|
|
625
|
+
var import_editor9 = require("@pixerate/editor");
|
|
624
626
|
|
|
625
627
|
// src/spreadsheet/useSpreadsheetEditor.ts
|
|
626
628
|
var import_react11 = require("react");
|
|
@@ -1249,6 +1251,1193 @@ function EditorSlotsProvider({ slots, children }) {
|
|
|
1249
1251
|
function useEditorSlots() {
|
|
1250
1252
|
return (0, import_react13.useContext)(EditorSlotsContext);
|
|
1251
1253
|
}
|
|
1254
|
+
|
|
1255
|
+
// src/image-editor/useImageEditor.ts
|
|
1256
|
+
var import_react14 = require("react");
|
|
1257
|
+
var import_editor7 = require("@pixerate/editor");
|
|
1258
|
+
function useImageEditor(options = {}) {
|
|
1259
|
+
const controllerRef = (0, import_react14.useRef)(null);
|
|
1260
|
+
if (!controllerRef.current) {
|
|
1261
|
+
controllerRef.current = new import_editor7.ImageEditorController(options);
|
|
1262
|
+
}
|
|
1263
|
+
const controller = controllerRef.current;
|
|
1264
|
+
const [state, setState] = (0, import_react14.useState)(() => controller.getState());
|
|
1265
|
+
const [historyTicks, setHistoryTicks] = (0, import_react14.useState)(0);
|
|
1266
|
+
(0, import_react14.useEffect)(() => {
|
|
1267
|
+
const unsub = controller.subscribe((next) => {
|
|
1268
|
+
setState(next);
|
|
1269
|
+
setHistoryTicks((t) => t + 1);
|
|
1270
|
+
});
|
|
1271
|
+
return unsub;
|
|
1272
|
+
}, [controller]);
|
|
1273
|
+
(0, import_react14.useEffect)(() => {
|
|
1274
|
+
if (options.image && options.image !== state.sourceUrl) {
|
|
1275
|
+
controller.loadImage(options.image);
|
|
1276
|
+
}
|
|
1277
|
+
}, [options.image, controller, state.sourceUrl]);
|
|
1278
|
+
const setTool = (0, import_react14.useCallback)((tool) => controller.setTool(tool), [controller]);
|
|
1279
|
+
const setCrop = (0, import_react14.useCallback)(
|
|
1280
|
+
(crop, addToHistory = true) => controller.setCrop(crop, addToHistory),
|
|
1281
|
+
[controller]
|
|
1282
|
+
);
|
|
1283
|
+
const applyCropPreset = (0, import_react14.useCallback)((ratio) => controller.applyCropPreset(ratio), [controller]);
|
|
1284
|
+
const resetCrop = (0, import_react14.useCallback)(() => controller.resetCrop(), [controller]);
|
|
1285
|
+
const rotate = (0, import_react14.useCallback)((deg) => controller.rotate(deg), [controller]);
|
|
1286
|
+
const setRotation = (0, import_react14.useCallback)((deg) => controller.setRotation(deg), [controller]);
|
|
1287
|
+
const flipHorizontal = (0, import_react14.useCallback)(() => controller.flipHorizontal(), [controller]);
|
|
1288
|
+
const flipVertical = (0, import_react14.useCallback)(() => controller.flipVertical(), [controller]);
|
|
1289
|
+
const setAdjustments = (0, import_react14.useCallback)(
|
|
1290
|
+
(partial, commitToHistory = true) => controller.setAdjustments(partial, commitToHistory),
|
|
1291
|
+
[controller]
|
|
1292
|
+
);
|
|
1293
|
+
const resetAdjustments = (0, import_react14.useCallback)(() => controller.resetAdjustments(), [controller]);
|
|
1294
|
+
const setDepthMask = (0, import_react14.useCallback)((partial) => controller.setDepthMask(partial), [controller]);
|
|
1295
|
+
const resetDepthMask = (0, import_react14.useCallback)(() => controller.resetDepthMask(), [controller]);
|
|
1296
|
+
const addAnnotation = (0, import_react14.useCallback)((ann) => controller.addAnnotation(ann), [controller]);
|
|
1297
|
+
const updateAnnotation = (0, import_react14.useCallback)(
|
|
1298
|
+
(id, partial, commitToHistory = true) => controller.updateAnnotation(id, partial, commitToHistory),
|
|
1299
|
+
[controller]
|
|
1300
|
+
);
|
|
1301
|
+
const removeAnnotation = (0, import_react14.useCallback)((id) => controller.removeAnnotation(id), [controller]);
|
|
1302
|
+
const clearAnnotations = (0, import_react14.useCallback)(() => controller.clearAnnotations(), [controller]);
|
|
1303
|
+
const selectAnnotation = (0, import_react14.useCallback)((id) => controller.selectAnnotation(id), [controller]);
|
|
1304
|
+
const undo = (0, import_react14.useCallback)(() => controller.undo(), [controller]);
|
|
1305
|
+
const redo = (0, import_react14.useCallback)(() => controller.redo(), [controller]);
|
|
1306
|
+
const reset = (0, import_react14.useCallback)(() => controller.reset(), [controller]);
|
|
1307
|
+
const zoomIn = (0, import_react14.useCallback)((factor) => controller.zoomIn(factor), [controller]);
|
|
1308
|
+
const zoomOut = (0, import_react14.useCallback)((factor) => controller.zoomOut(factor), [controller]);
|
|
1309
|
+
const resetZoom = (0, import_react14.useCallback)(() => controller.resetZoom(), [controller]);
|
|
1310
|
+
const setZoom = (0, import_react14.useCallback)((zoom) => controller.setZoom(zoom), [controller]);
|
|
1311
|
+
const setPan = (0, import_react14.useCallback)((pan) => controller.setPan(pan), [controller]);
|
|
1312
|
+
const setComparing = (0, import_react14.useCallback)((comp) => controller.setComparing(comp), [controller]);
|
|
1313
|
+
const loadImage = (0, import_react14.useCallback)((src) => controller.loadImage(src), [controller]);
|
|
1314
|
+
const toDataURL = (0, import_react14.useCallback)((opts) => controller.toDataURL(opts), [controller]);
|
|
1315
|
+
const toBlob = (0, import_react14.useCallback)((opts) => controller.toBlob(opts), [controller]);
|
|
1316
|
+
const toJSON = (0, import_react14.useCallback)(() => controller.toJSON(), [controller]);
|
|
1317
|
+
const loadJSON = (0, import_react14.useCallback)((data) => controller.loadJSON(data), [controller]);
|
|
1318
|
+
return (0, import_react14.useMemo)(
|
|
1319
|
+
() => ({
|
|
1320
|
+
controller,
|
|
1321
|
+
state,
|
|
1322
|
+
canUndo: controller.canUndo,
|
|
1323
|
+
canRedo: controller.canRedo,
|
|
1324
|
+
setTool,
|
|
1325
|
+
setCrop,
|
|
1326
|
+
applyCropPreset,
|
|
1327
|
+
resetCrop,
|
|
1328
|
+
rotate,
|
|
1329
|
+
setRotation,
|
|
1330
|
+
flipHorizontal,
|
|
1331
|
+
flipVertical,
|
|
1332
|
+
setAdjustments,
|
|
1333
|
+
resetAdjustments,
|
|
1334
|
+
setDepthMask,
|
|
1335
|
+
resetDepthMask,
|
|
1336
|
+
addAnnotation,
|
|
1337
|
+
updateAnnotation,
|
|
1338
|
+
removeAnnotation,
|
|
1339
|
+
clearAnnotations,
|
|
1340
|
+
selectAnnotation,
|
|
1341
|
+
undo,
|
|
1342
|
+
redo,
|
|
1343
|
+
reset,
|
|
1344
|
+
zoomIn,
|
|
1345
|
+
zoomOut,
|
|
1346
|
+
resetZoom,
|
|
1347
|
+
setZoom,
|
|
1348
|
+
setPan,
|
|
1349
|
+
setComparing,
|
|
1350
|
+
loadImage,
|
|
1351
|
+
toDataURL,
|
|
1352
|
+
toBlob,
|
|
1353
|
+
toJSON,
|
|
1354
|
+
loadJSON
|
|
1355
|
+
}),
|
|
1356
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1357
|
+
[controller, state, historyTicks]
|
|
1358
|
+
);
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
// src/image-editor/ImageEditor.tsx
|
|
1362
|
+
var import_react15 = require("react");
|
|
1363
|
+
var import_editor8 = require("@pixerate/editor");
|
|
1364
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
1365
|
+
var ImageEditor = ({
|
|
1366
|
+
editor: externalEditor,
|
|
1367
|
+
src,
|
|
1368
|
+
className = "",
|
|
1369
|
+
style,
|
|
1370
|
+
onSave,
|
|
1371
|
+
exportOptions
|
|
1372
|
+
}) => {
|
|
1373
|
+
const internalEditor = useImageEditor({ image: src });
|
|
1374
|
+
const editor = externalEditor || internalEditor;
|
|
1375
|
+
const { state, controller } = editor;
|
|
1376
|
+
const canvasRef = (0, import_react15.useRef)(null);
|
|
1377
|
+
const containerRef = (0, import_react15.useRef)(null);
|
|
1378
|
+
const [activeTab, setActiveTab] = (0, import_react15.useState)("crop");
|
|
1379
|
+
const [activeCropRatio, setActiveCropRatio] = (0, import_react15.useState)(null);
|
|
1380
|
+
const [activeColor, setActiveColor] = (0, import_react15.useState)("#ef4444");
|
|
1381
|
+
const [strokeWidth, setStrokeWidth] = (0, import_react15.useState)(3);
|
|
1382
|
+
const [fontSize, setFontSize] = (0, import_react15.useState)(24);
|
|
1383
|
+
const [textInput, setTextInput] = (0, import_react15.useState)("");
|
|
1384
|
+
const draftAnnotationRef = (0, import_react15.useRef)(null);
|
|
1385
|
+
const draftCropRef = (0, import_react15.useRef)(null);
|
|
1386
|
+
const cropDragInfoRef = (0, import_react15.useRef)(null);
|
|
1387
|
+
const annotationDragInfoRef = (0, import_react15.useRef)(null);
|
|
1388
|
+
const isDrawingRef = (0, import_react15.useRef)(false);
|
|
1389
|
+
const currentPathRef = (0, import_react15.useRef)([]);
|
|
1390
|
+
const dragStartPointRef = (0, import_react15.useRef)(null);
|
|
1391
|
+
const renderCanvas = (0, import_react15.useCallback)(
|
|
1392
|
+
async (draftAnnotation, draftCrop) => {
|
|
1393
|
+
const canvas = canvasRef.current;
|
|
1394
|
+
const container = containerRef.current;
|
|
1395
|
+
if (!canvas || !container) return;
|
|
1396
|
+
const vpWidth = container.clientWidth || 800;
|
|
1397
|
+
const vpHeight = container.clientHeight || 500;
|
|
1398
|
+
await controller.render(canvas, {
|
|
1399
|
+
viewportWidth: vpWidth,
|
|
1400
|
+
viewportHeight: vpHeight,
|
|
1401
|
+
showCropOverlay: activeTab === "crop",
|
|
1402
|
+
showSelectionOverlay: true,
|
|
1403
|
+
interactive: true,
|
|
1404
|
+
draftAnnotation: draftAnnotation !== void 0 ? draftAnnotation : draftAnnotationRef.current,
|
|
1405
|
+
draftCrop: draftCrop !== void 0 ? draftCrop : draftCropRef.current
|
|
1406
|
+
});
|
|
1407
|
+
},
|
|
1408
|
+
[controller, activeTab]
|
|
1409
|
+
);
|
|
1410
|
+
(0, import_react15.useEffect)(() => {
|
|
1411
|
+
const raf = typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : (cb) => setTimeout(cb, 0);
|
|
1412
|
+
const caf = typeof cancelAnimationFrame !== "undefined" ? cancelAnimationFrame : (id) => clearTimeout(id);
|
|
1413
|
+
const animId = raf(() => {
|
|
1414
|
+
renderCanvas();
|
|
1415
|
+
});
|
|
1416
|
+
return () => caf(animId);
|
|
1417
|
+
}, [renderCanvas, state]);
|
|
1418
|
+
(0, import_react15.useEffect)(() => {
|
|
1419
|
+
if (activeTab === "crop" && editor.state.activeTool !== "crop") {
|
|
1420
|
+
editor.setTool("crop");
|
|
1421
|
+
}
|
|
1422
|
+
}, [activeTab, editor]);
|
|
1423
|
+
(0, import_react15.useEffect)(() => {
|
|
1424
|
+
const container = containerRef.current;
|
|
1425
|
+
if (!container) return;
|
|
1426
|
+
if (typeof ResizeObserver !== "undefined") {
|
|
1427
|
+
const observer = new ResizeObserver(() => {
|
|
1428
|
+
renderCanvas();
|
|
1429
|
+
});
|
|
1430
|
+
observer.observe(container);
|
|
1431
|
+
return () => observer.disconnect();
|
|
1432
|
+
} else {
|
|
1433
|
+
const handleResize = () => renderCanvas();
|
|
1434
|
+
window.addEventListener("resize", handleResize);
|
|
1435
|
+
return () => window.removeEventListener("resize", handleResize);
|
|
1436
|
+
}
|
|
1437
|
+
}, [renderCanvas]);
|
|
1438
|
+
const getCanvasEventPoint = (e) => {
|
|
1439
|
+
const canvas = canvasRef.current;
|
|
1440
|
+
if (!canvas) return { x: 0, y: 0 };
|
|
1441
|
+
const rect = canvas.getBoundingClientRect();
|
|
1442
|
+
const scaleX = rect.width > 0 ? canvas.width / rect.width : 1;
|
|
1443
|
+
const scaleY = rect.height > 0 ? canvas.height / rect.height : 1;
|
|
1444
|
+
return {
|
|
1445
|
+
x: (e.clientX - rect.left) * scaleX,
|
|
1446
|
+
y: (e.clientY - rect.top) * scaleY
|
|
1447
|
+
};
|
|
1448
|
+
};
|
|
1449
|
+
const handleMouseDown = (e) => {
|
|
1450
|
+
const canvas = canvasRef.current;
|
|
1451
|
+
if (!canvas) return;
|
|
1452
|
+
const canvasPt = getCanvasEventPoint(e);
|
|
1453
|
+
if (activeTab === "crop") {
|
|
1454
|
+
const activeCrop = draftCropRef.current || state.crop || {
|
|
1455
|
+
x: 0,
|
|
1456
|
+
y: 0,
|
|
1457
|
+
width: state.imageDimensions.width,
|
|
1458
|
+
height: state.imageDimensions.height
|
|
1459
|
+
};
|
|
1460
|
+
const handle = (0, import_editor8.hitTestCrop)(canvasPt, state, canvas.width, canvas.height, 14, activeCrop);
|
|
1461
|
+
const startImgPt = (0, import_editor8.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, true);
|
|
1462
|
+
if (handle) {
|
|
1463
|
+
cropDragInfoRef.current = {
|
|
1464
|
+
handle,
|
|
1465
|
+
startCanvasPt: canvasPt,
|
|
1466
|
+
startImgPt,
|
|
1467
|
+
startCrop: activeCrop
|
|
1468
|
+
};
|
|
1469
|
+
} else {
|
|
1470
|
+
cropDragInfoRef.current = {
|
|
1471
|
+
handle: "new",
|
|
1472
|
+
startCanvasPt: canvasPt,
|
|
1473
|
+
startImgPt,
|
|
1474
|
+
startCrop: { x: startImgPt.x, y: startImgPt.y, width: 0, height: 0 }
|
|
1475
|
+
};
|
|
1476
|
+
}
|
|
1477
|
+
return;
|
|
1478
|
+
}
|
|
1479
|
+
if (activeTab === "annotate") {
|
|
1480
|
+
const imgPt = (0, import_editor8.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
|
|
1481
|
+
dragStartPointRef.current = imgPt;
|
|
1482
|
+
if (state.activeTool === "select") {
|
|
1483
|
+
for (let i = state.annotations.length - 1; i >= 0; i--) {
|
|
1484
|
+
const ann = state.annotations[i];
|
|
1485
|
+
if ((0, import_editor8.hitTestAnnotation)(imgPt, ann)) {
|
|
1486
|
+
editor.selectAnnotation(ann.id);
|
|
1487
|
+
annotationDragInfoRef.current = {
|
|
1488
|
+
annId: ann.id,
|
|
1489
|
+
startImgPt: imgPt,
|
|
1490
|
+
initialAnn: { ...ann }
|
|
1491
|
+
};
|
|
1492
|
+
return;
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
editor.selectAnnotation(null);
|
|
1496
|
+
return;
|
|
1497
|
+
}
|
|
1498
|
+
if (state.activeTool === "pen") {
|
|
1499
|
+
isDrawingRef.current = true;
|
|
1500
|
+
currentPathRef.current = [imgPt];
|
|
1501
|
+
const draft = {
|
|
1502
|
+
id: "draft",
|
|
1503
|
+
type: "pen",
|
|
1504
|
+
x: 0,
|
|
1505
|
+
y: 0,
|
|
1506
|
+
points: [imgPt],
|
|
1507
|
+
strokeColor: activeColor,
|
|
1508
|
+
strokeWidth
|
|
1509
|
+
};
|
|
1510
|
+
draftAnnotationRef.current = draft;
|
|
1511
|
+
renderCanvas(draft);
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
};
|
|
1515
|
+
const handleMouseMove = (e) => {
|
|
1516
|
+
const canvas = canvasRef.current;
|
|
1517
|
+
if (!canvas) return;
|
|
1518
|
+
const canvasPt = getCanvasEventPoint(e);
|
|
1519
|
+
if (activeTab === "crop") {
|
|
1520
|
+
const drag = cropDragInfoRef.current;
|
|
1521
|
+
if (drag) {
|
|
1522
|
+
const currentImgPt = (0, import_editor8.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, true);
|
|
1523
|
+
let newCrop;
|
|
1524
|
+
if (drag.handle === "new") {
|
|
1525
|
+
const minX = Math.max(0, Math.min(drag.startImgPt.x, currentImgPt.x));
|
|
1526
|
+
const minY = Math.max(0, Math.min(drag.startImgPt.y, currentImgPt.y));
|
|
1527
|
+
const maxX = Math.min(state.imageDimensions.width, Math.max(drag.startImgPt.x, currentImgPt.x));
|
|
1528
|
+
const maxY = Math.min(state.imageDimensions.height, Math.max(drag.startImgPt.y, currentImgPt.y));
|
|
1529
|
+
newCrop = {
|
|
1530
|
+
x: Math.round(minX),
|
|
1531
|
+
y: Math.round(minY),
|
|
1532
|
+
width: Math.round(Math.max(20, maxX - minX)),
|
|
1533
|
+
height: Math.round(Math.max(20, maxY - minY))
|
|
1534
|
+
};
|
|
1535
|
+
} else {
|
|
1536
|
+
newCrop = (0, import_editor8.calculateCropDrag)(
|
|
1537
|
+
drag.startCrop,
|
|
1538
|
+
drag.handle,
|
|
1539
|
+
drag.startImgPt,
|
|
1540
|
+
currentImgPt,
|
|
1541
|
+
state.imageDimensions.width,
|
|
1542
|
+
state.imageDimensions.height,
|
|
1543
|
+
activeCropRatio
|
|
1544
|
+
);
|
|
1545
|
+
}
|
|
1546
|
+
draftCropRef.current = newCrop;
|
|
1547
|
+
renderCanvas(null, newCrop);
|
|
1548
|
+
} else {
|
|
1549
|
+
const activeCrop = draftCropRef.current || state.crop || {
|
|
1550
|
+
x: 0,
|
|
1551
|
+
y: 0,
|
|
1552
|
+
width: state.imageDimensions.width,
|
|
1553
|
+
height: state.imageDimensions.height
|
|
1554
|
+
};
|
|
1555
|
+
const handle = (0, import_editor8.hitTestCrop)(canvasPt, state, canvas.width, canvas.height, 14, activeCrop);
|
|
1556
|
+
canvas.style.cursor = (0, import_editor8.getCropCursor)(handle);
|
|
1557
|
+
}
|
|
1558
|
+
return;
|
|
1559
|
+
}
|
|
1560
|
+
if (activeTab === "annotate") {
|
|
1561
|
+
if (annotationDragInfoRef.current) {
|
|
1562
|
+
const drag = annotationDragInfoRef.current;
|
|
1563
|
+
const currentImgPt2 = (0, import_editor8.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
|
|
1564
|
+
const dx = currentImgPt2.x - drag.startImgPt.x;
|
|
1565
|
+
const dy = currentImgPt2.y - drag.startImgPt.y;
|
|
1566
|
+
editor.updateAnnotation(drag.annId, {
|
|
1567
|
+
x: Math.round(drag.initialAnn.x + dx),
|
|
1568
|
+
y: Math.round(drag.initialAnn.y + dy)
|
|
1569
|
+
});
|
|
1570
|
+
return;
|
|
1571
|
+
}
|
|
1572
|
+
const startPt = dragStartPointRef.current;
|
|
1573
|
+
if (!startPt) {
|
|
1574
|
+
if (state.activeTool === "select") {
|
|
1575
|
+
const imgPt = (0, import_editor8.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
|
|
1576
|
+
let hovered = false;
|
|
1577
|
+
for (let i = state.annotations.length - 1; i >= 0; i--) {
|
|
1578
|
+
if ((0, import_editor8.hitTestAnnotation)(imgPt, state.annotations[i])) {
|
|
1579
|
+
hovered = true;
|
|
1580
|
+
break;
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
canvas.style.cursor = hovered ? "move" : "default";
|
|
1584
|
+
} else {
|
|
1585
|
+
canvas.style.cursor = "crosshair";
|
|
1586
|
+
}
|
|
1587
|
+
return;
|
|
1588
|
+
}
|
|
1589
|
+
const currentImgPt = (0, import_editor8.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
|
|
1590
|
+
if (state.activeTool === "pen" && isDrawingRef.current) {
|
|
1591
|
+
currentPathRef.current.push(currentImgPt);
|
|
1592
|
+
const draft = {
|
|
1593
|
+
id: "draft",
|
|
1594
|
+
type: "pen",
|
|
1595
|
+
x: 0,
|
|
1596
|
+
y: 0,
|
|
1597
|
+
points: [...currentPathRef.current],
|
|
1598
|
+
strokeColor: activeColor,
|
|
1599
|
+
strokeWidth
|
|
1600
|
+
};
|
|
1601
|
+
draftAnnotationRef.current = draft;
|
|
1602
|
+
renderCanvas(draft);
|
|
1603
|
+
} else if (state.activeTool === "rect") {
|
|
1604
|
+
const w = currentImgPt.x - startPt.x;
|
|
1605
|
+
const h = currentImgPt.y - startPt.y;
|
|
1606
|
+
const draft = {
|
|
1607
|
+
id: "draft",
|
|
1608
|
+
type: "rect",
|
|
1609
|
+
x: Math.min(startPt.x, currentImgPt.x),
|
|
1610
|
+
y: Math.min(startPt.y, currentImgPt.y),
|
|
1611
|
+
width: Math.abs(w),
|
|
1612
|
+
height: Math.abs(h),
|
|
1613
|
+
strokeColor: activeColor,
|
|
1614
|
+
strokeWidth
|
|
1615
|
+
};
|
|
1616
|
+
draftAnnotationRef.current = draft;
|
|
1617
|
+
renderCanvas(draft);
|
|
1618
|
+
} else if (state.activeTool === "circle") {
|
|
1619
|
+
const rx = Math.abs(currentImgPt.x - startPt.x) / 2;
|
|
1620
|
+
const ry = Math.abs(currentImgPt.y - startPt.y) / 2;
|
|
1621
|
+
const draft = {
|
|
1622
|
+
id: "draft",
|
|
1623
|
+
type: "circle",
|
|
1624
|
+
x: Math.min(startPt.x, currentImgPt.x) + rx,
|
|
1625
|
+
y: Math.min(startPt.y, currentImgPt.y) + ry,
|
|
1626
|
+
radiusX: rx,
|
|
1627
|
+
radiusY: ry,
|
|
1628
|
+
strokeColor: activeColor,
|
|
1629
|
+
strokeWidth
|
|
1630
|
+
};
|
|
1631
|
+
draftAnnotationRef.current = draft;
|
|
1632
|
+
renderCanvas(draft);
|
|
1633
|
+
} else if (state.activeTool === "arrow") {
|
|
1634
|
+
const draft = {
|
|
1635
|
+
id: "draft",
|
|
1636
|
+
type: "arrow",
|
|
1637
|
+
x: startPt.x,
|
|
1638
|
+
y: startPt.y,
|
|
1639
|
+
endX: currentImgPt.x,
|
|
1640
|
+
endY: currentImgPt.y,
|
|
1641
|
+
strokeColor: activeColor,
|
|
1642
|
+
strokeWidth
|
|
1643
|
+
};
|
|
1644
|
+
draftAnnotationRef.current = draft;
|
|
1645
|
+
renderCanvas(draft);
|
|
1646
|
+
} else if (state.activeTool === "line") {
|
|
1647
|
+
const draft = {
|
|
1648
|
+
id: "draft",
|
|
1649
|
+
type: "line",
|
|
1650
|
+
x: startPt.x,
|
|
1651
|
+
y: startPt.y,
|
|
1652
|
+
endX: currentImgPt.x,
|
|
1653
|
+
endY: currentImgPt.y,
|
|
1654
|
+
strokeColor: activeColor,
|
|
1655
|
+
strokeWidth
|
|
1656
|
+
};
|
|
1657
|
+
draftAnnotationRef.current = draft;
|
|
1658
|
+
renderCanvas(draft);
|
|
1659
|
+
}
|
|
1660
|
+
}
|
|
1661
|
+
};
|
|
1662
|
+
const handleMouseUp = (e) => {
|
|
1663
|
+
const canvas = canvasRef.current;
|
|
1664
|
+
if (!canvas) return;
|
|
1665
|
+
const canvasPt = getCanvasEventPoint(e);
|
|
1666
|
+
if (activeTab === "crop") {
|
|
1667
|
+
if (cropDragInfoRef.current) {
|
|
1668
|
+
if (draftCropRef.current && draftCropRef.current.width >= 20 && draftCropRef.current.height >= 20) {
|
|
1669
|
+
editor.setCrop(draftCropRef.current, true);
|
|
1670
|
+
}
|
|
1671
|
+
cropDragInfoRef.current = null;
|
|
1672
|
+
draftCropRef.current = null;
|
|
1673
|
+
renderCanvas();
|
|
1674
|
+
}
|
|
1675
|
+
return;
|
|
1676
|
+
}
|
|
1677
|
+
if (activeTab === "annotate") {
|
|
1678
|
+
if (annotationDragInfoRef.current) {
|
|
1679
|
+
annotationDragInfoRef.current = null;
|
|
1680
|
+
return;
|
|
1681
|
+
}
|
|
1682
|
+
const startPt = dragStartPointRef.current;
|
|
1683
|
+
if (startPt) {
|
|
1684
|
+
const currentImgPt = (0, import_editor8.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
|
|
1685
|
+
if (state.activeTool === "pen" && isDrawingRef.current) {
|
|
1686
|
+
if (currentPathRef.current.length > 1) {
|
|
1687
|
+
editor.addAnnotation({
|
|
1688
|
+
type: "pen",
|
|
1689
|
+
x: 0,
|
|
1690
|
+
y: 0,
|
|
1691
|
+
points: [...currentPathRef.current],
|
|
1692
|
+
strokeColor: activeColor,
|
|
1693
|
+
strokeWidth
|
|
1694
|
+
});
|
|
1695
|
+
}
|
|
1696
|
+
isDrawingRef.current = false;
|
|
1697
|
+
currentPathRef.current = [];
|
|
1698
|
+
} else if (state.activeTool === "rect") {
|
|
1699
|
+
const w = Math.abs(currentImgPt.x - startPt.x);
|
|
1700
|
+
const h = Math.abs(currentImgPt.y - startPt.y);
|
|
1701
|
+
if (w > 4 && h > 4) {
|
|
1702
|
+
editor.addAnnotation({
|
|
1703
|
+
type: "rect",
|
|
1704
|
+
x: Math.min(startPt.x, currentImgPt.x),
|
|
1705
|
+
y: Math.min(startPt.y, currentImgPt.y),
|
|
1706
|
+
width: w,
|
|
1707
|
+
height: h,
|
|
1708
|
+
strokeColor: activeColor,
|
|
1709
|
+
strokeWidth
|
|
1710
|
+
});
|
|
1711
|
+
}
|
|
1712
|
+
} else if (state.activeTool === "circle") {
|
|
1713
|
+
const rx = Math.abs(currentImgPt.x - startPt.x) / 2;
|
|
1714
|
+
const ry = Math.abs(currentImgPt.y - startPt.y) / 2;
|
|
1715
|
+
if (rx > 4 && ry > 4) {
|
|
1716
|
+
editor.addAnnotation({
|
|
1717
|
+
type: "circle",
|
|
1718
|
+
x: Math.min(startPt.x, currentImgPt.x) + rx,
|
|
1719
|
+
y: Math.min(startPt.y, currentImgPt.y) + ry,
|
|
1720
|
+
radiusX: rx,
|
|
1721
|
+
radiusY: ry,
|
|
1722
|
+
strokeColor: activeColor,
|
|
1723
|
+
strokeWidth
|
|
1724
|
+
});
|
|
1725
|
+
}
|
|
1726
|
+
} else if (state.activeTool === "arrow") {
|
|
1727
|
+
if (Math.hypot(currentImgPt.x - startPt.x, currentImgPt.y - startPt.y) > 4) {
|
|
1728
|
+
editor.addAnnotation({
|
|
1729
|
+
type: "arrow",
|
|
1730
|
+
x: startPt.x,
|
|
1731
|
+
y: startPt.y,
|
|
1732
|
+
endX: currentImgPt.x,
|
|
1733
|
+
endY: currentImgPt.y,
|
|
1734
|
+
strokeColor: activeColor,
|
|
1735
|
+
strokeWidth
|
|
1736
|
+
});
|
|
1737
|
+
}
|
|
1738
|
+
} else if (state.activeTool === "line") {
|
|
1739
|
+
if (Math.hypot(currentImgPt.x - startPt.x, currentImgPt.y - startPt.y) > 4) {
|
|
1740
|
+
editor.addAnnotation({
|
|
1741
|
+
type: "line",
|
|
1742
|
+
x: startPt.x,
|
|
1743
|
+
y: startPt.y,
|
|
1744
|
+
endX: currentImgPt.x,
|
|
1745
|
+
endY: currentImgPt.y,
|
|
1746
|
+
strokeColor: activeColor,
|
|
1747
|
+
strokeWidth
|
|
1748
|
+
});
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
draftAnnotationRef.current = null;
|
|
1752
|
+
dragStartPointRef.current = null;
|
|
1753
|
+
renderCanvas();
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
};
|
|
1757
|
+
const handleAddText = () => {
|
|
1758
|
+
if (!textInput.trim()) return;
|
|
1759
|
+
editor.addAnnotation({
|
|
1760
|
+
type: "text",
|
|
1761
|
+
x: 50,
|
|
1762
|
+
y: 50,
|
|
1763
|
+
text: textInput.trim(),
|
|
1764
|
+
fontSize,
|
|
1765
|
+
color: activeColor
|
|
1766
|
+
});
|
|
1767
|
+
setTextInput("");
|
|
1768
|
+
};
|
|
1769
|
+
const handleExport = async () => {
|
|
1770
|
+
const dataUrl = await editor.toDataURL(exportOptions);
|
|
1771
|
+
if (onSave) {
|
|
1772
|
+
onSave(dataUrl);
|
|
1773
|
+
} else {
|
|
1774
|
+
const link = document.createElement("a");
|
|
1775
|
+
link.download = `edited-image-${Date.now()}.png`;
|
|
1776
|
+
link.href = dataUrl;
|
|
1777
|
+
link.click();
|
|
1778
|
+
}
|
|
1779
|
+
};
|
|
1780
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
1781
|
+
"div",
|
|
1782
|
+
{
|
|
1783
|
+
className: `flex flex-col bg-slate-900 border border-slate-800 rounded-xl overflow-hidden shadow-2xl text-slate-100 ${className}`,
|
|
1784
|
+
style,
|
|
1785
|
+
children: [
|
|
1786
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-wrap items-center justify-between px-4 py-3 bg-slate-950/80 border-b border-slate-800 gap-3", children: [
|
|
1787
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center gap-1 bg-slate-900 p-1 rounded-lg border border-slate-800 text-xs", children: [
|
|
1788
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1789
|
+
"button",
|
|
1790
|
+
{
|
|
1791
|
+
type: "button",
|
|
1792
|
+
onClick: () => {
|
|
1793
|
+
setActiveTab("crop");
|
|
1794
|
+
editor.setTool("crop");
|
|
1795
|
+
},
|
|
1796
|
+
className: `px-3 py-1.5 rounded-md font-medium transition-colors ${activeTab === "crop" ? "bg-indigo-600 text-white shadow" : "text-slate-400 hover:text-slate-200"}`,
|
|
1797
|
+
children: "Transform & Crop"
|
|
1798
|
+
}
|
|
1799
|
+
),
|
|
1800
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1801
|
+
"button",
|
|
1802
|
+
{
|
|
1803
|
+
type: "button",
|
|
1804
|
+
onClick: () => {
|
|
1805
|
+
setActiveTab("adjust");
|
|
1806
|
+
editor.setTool("select");
|
|
1807
|
+
},
|
|
1808
|
+
className: `px-3 py-1.5 rounded-md font-medium transition-colors ${activeTab === "adjust" ? "bg-indigo-600 text-white shadow" : "text-slate-400 hover:text-slate-200"}`,
|
|
1809
|
+
children: "Adjustments"
|
|
1810
|
+
}
|
|
1811
|
+
),
|
|
1812
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1813
|
+
"button",
|
|
1814
|
+
{
|
|
1815
|
+
type: "button",
|
|
1816
|
+
onClick: () => {
|
|
1817
|
+
setActiveTab("annotate");
|
|
1818
|
+
editor.setTool("pen");
|
|
1819
|
+
},
|
|
1820
|
+
className: `px-3 py-1.5 rounded-md font-medium transition-colors ${activeTab === "annotate" ? "bg-indigo-600 text-white shadow" : "text-slate-400 hover:text-slate-200"}`,
|
|
1821
|
+
children: "Annotations"
|
|
1822
|
+
}
|
|
1823
|
+
),
|
|
1824
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1825
|
+
"button",
|
|
1826
|
+
{
|
|
1827
|
+
type: "button",
|
|
1828
|
+
onClick: () => {
|
|
1829
|
+
setActiveTab("depth");
|
|
1830
|
+
editor.setTool("select");
|
|
1831
|
+
},
|
|
1832
|
+
className: `px-3 py-1.5 rounded-md font-medium transition-colors ${activeTab === "depth" ? "bg-indigo-600 text-white shadow" : "text-slate-400 hover:text-slate-200"}`,
|
|
1833
|
+
children: "Depth Mask"
|
|
1834
|
+
}
|
|
1835
|
+
)
|
|
1836
|
+
] }),
|
|
1837
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center gap-2 text-xs", children: [
|
|
1838
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1839
|
+
"button",
|
|
1840
|
+
{
|
|
1841
|
+
type: "button",
|
|
1842
|
+
disabled: !editor.canUndo,
|
|
1843
|
+
onClick: () => editor.undo(),
|
|
1844
|
+
className: "p-1.5 rounded-lg border border-slate-800 bg-slate-900 text-slate-300 disabled:opacity-40 disabled:cursor-not-allowed hover:bg-slate-800",
|
|
1845
|
+
title: "Undo",
|
|
1846
|
+
children: "\u21A9"
|
|
1847
|
+
}
|
|
1848
|
+
),
|
|
1849
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1850
|
+
"button",
|
|
1851
|
+
{
|
|
1852
|
+
type: "button",
|
|
1853
|
+
disabled: !editor.canRedo,
|
|
1854
|
+
onClick: () => editor.redo(),
|
|
1855
|
+
className: "p-1.5 rounded-lg border border-slate-800 bg-slate-900 text-slate-300 disabled:opacity-40 disabled:cursor-not-allowed hover:bg-slate-800",
|
|
1856
|
+
title: "Redo",
|
|
1857
|
+
children: "\u21AA"
|
|
1858
|
+
}
|
|
1859
|
+
),
|
|
1860
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1861
|
+
"button",
|
|
1862
|
+
{
|
|
1863
|
+
type: "button",
|
|
1864
|
+
onClick: () => editor.reset(),
|
|
1865
|
+
className: "px-2 py-1.5 rounded-lg border border-slate-800 bg-slate-900 text-slate-400 hover:text-slate-200 hover:bg-slate-800",
|
|
1866
|
+
children: "Reset"
|
|
1867
|
+
}
|
|
1868
|
+
),
|
|
1869
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "h-4 w-px bg-slate-800 mx-1" }),
|
|
1870
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1871
|
+
"button",
|
|
1872
|
+
{
|
|
1873
|
+
type: "button",
|
|
1874
|
+
onClick: () => editor.zoomOut(),
|
|
1875
|
+
className: "p-1.5 rounded-lg border border-slate-800 bg-slate-900 text-slate-300 hover:bg-slate-800",
|
|
1876
|
+
title: "Zoom Out",
|
|
1877
|
+
children: "-"
|
|
1878
|
+
}
|
|
1879
|
+
),
|
|
1880
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "font-mono text-slate-400 w-10 text-center", children: [
|
|
1881
|
+
Math.round(state.zoom * 100),
|
|
1882
|
+
"%"
|
|
1883
|
+
] }),
|
|
1884
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1885
|
+
"button",
|
|
1886
|
+
{
|
|
1887
|
+
type: "button",
|
|
1888
|
+
onClick: () => editor.zoomIn(),
|
|
1889
|
+
className: "p-1.5 rounded-lg border border-slate-800 bg-slate-900 text-slate-300 hover:bg-slate-800",
|
|
1890
|
+
title: "Zoom In",
|
|
1891
|
+
children: "+"
|
|
1892
|
+
}
|
|
1893
|
+
),
|
|
1894
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "h-4 w-px bg-slate-800 mx-1" }),
|
|
1895
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1896
|
+
"button",
|
|
1897
|
+
{
|
|
1898
|
+
type: "button",
|
|
1899
|
+
onMouseDown: () => editor.setComparing(true),
|
|
1900
|
+
onMouseUp: () => editor.setComparing(false),
|
|
1901
|
+
onMouseLeave: () => editor.setComparing(false),
|
|
1902
|
+
className: "px-2.5 py-1.5 rounded-lg border border-slate-800 bg-slate-900 text-amber-400 hover:bg-slate-800 select-none active:bg-amber-950/40",
|
|
1903
|
+
children: "Hold Compare"
|
|
1904
|
+
}
|
|
1905
|
+
),
|
|
1906
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1907
|
+
"button",
|
|
1908
|
+
{
|
|
1909
|
+
type: "button",
|
|
1910
|
+
onClick: handleExport,
|
|
1911
|
+
className: "px-4 py-1.5 rounded-lg bg-emerald-600 hover:bg-emerald-500 text-white font-medium shadow-md shadow-emerald-900/30 transition-colors",
|
|
1912
|
+
children: "Export Image"
|
|
1913
|
+
}
|
|
1914
|
+
)
|
|
1915
|
+
] })
|
|
1916
|
+
] }),
|
|
1917
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col md:flex-row flex-1 min-h-[520px] md:h-[560px]", children: [
|
|
1918
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1919
|
+
"div",
|
|
1920
|
+
{
|
|
1921
|
+
ref: containerRef,
|
|
1922
|
+
className: "relative flex-1 h-[360px] md:h-full min-h-[320px] bg-slate-950 overflow-hidden select-none",
|
|
1923
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1924
|
+
"canvas",
|
|
1925
|
+
{
|
|
1926
|
+
ref: canvasRef,
|
|
1927
|
+
onMouseDown: handleMouseDown,
|
|
1928
|
+
onMouseMove: handleMouseMove,
|
|
1929
|
+
onMouseUp: handleMouseUp,
|
|
1930
|
+
onMouseLeave: handleMouseUp,
|
|
1931
|
+
className: "absolute inset-0 w-full h-full block"
|
|
1932
|
+
}
|
|
1933
|
+
)
|
|
1934
|
+
}
|
|
1935
|
+
),
|
|
1936
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "w-full md:w-80 bg-slate-900/90 border-t md:border-t-0 md:border-l border-slate-800 p-4 overflow-y-auto h-auto md:h-full md:max-h-[560px]", children: [
|
|
1937
|
+
activeTab === "crop" && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col gap-4 text-xs", children: [
|
|
1938
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Aspect Ratio Presets" }),
|
|
1939
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "grid grid-cols-3 gap-2", children: [
|
|
1940
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1941
|
+
"button",
|
|
1942
|
+
{
|
|
1943
|
+
type: "button",
|
|
1944
|
+
onClick: () => {
|
|
1945
|
+
setActiveCropRatio(null);
|
|
1946
|
+
editor.applyCropPreset(null);
|
|
1947
|
+
},
|
|
1948
|
+
className: `px-2.5 py-2 rounded-lg border font-medium ${activeCropRatio === null && state.crop ? "bg-indigo-600 border-indigo-500 text-white" : "bg-slate-800 hover:bg-slate-700 text-slate-200 border-slate-700"}`,
|
|
1949
|
+
children: "Freeform"
|
|
1950
|
+
}
|
|
1951
|
+
),
|
|
1952
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1953
|
+
"button",
|
|
1954
|
+
{
|
|
1955
|
+
type: "button",
|
|
1956
|
+
onClick: () => {
|
|
1957
|
+
setActiveCropRatio(1);
|
|
1958
|
+
editor.applyCropPreset(1);
|
|
1959
|
+
},
|
|
1960
|
+
className: `px-2.5 py-2 rounded-lg border font-medium ${activeCropRatio === 1 ? "bg-indigo-600 border-indigo-500 text-white" : "bg-slate-800 hover:bg-slate-700 text-slate-200 border-slate-700"}`,
|
|
1961
|
+
children: "1:1 Square"
|
|
1962
|
+
}
|
|
1963
|
+
),
|
|
1964
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1965
|
+
"button",
|
|
1966
|
+
{
|
|
1967
|
+
type: "button",
|
|
1968
|
+
onClick: () => {
|
|
1969
|
+
setActiveCropRatio(4 / 3);
|
|
1970
|
+
editor.applyCropPreset(4 / 3);
|
|
1971
|
+
},
|
|
1972
|
+
className: `px-2.5 py-2 rounded-lg border font-medium ${activeCropRatio === 4 / 3 ? "bg-indigo-600 border-indigo-500 text-white" : "bg-slate-800 hover:bg-slate-700 text-slate-200 border-slate-700"}`,
|
|
1973
|
+
children: "4:3 Standard"
|
|
1974
|
+
}
|
|
1975
|
+
),
|
|
1976
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1977
|
+
"button",
|
|
1978
|
+
{
|
|
1979
|
+
type: "button",
|
|
1980
|
+
onClick: () => {
|
|
1981
|
+
setActiveCropRatio(16 / 9);
|
|
1982
|
+
editor.applyCropPreset(16 / 9);
|
|
1983
|
+
},
|
|
1984
|
+
className: `px-2.5 py-2 rounded-lg border font-medium ${activeCropRatio === 16 / 9 ? "bg-indigo-600 border-indigo-500 text-white" : "bg-slate-800 hover:bg-slate-700 text-slate-200 border-slate-700"}`,
|
|
1985
|
+
children: "16:9 Wide"
|
|
1986
|
+
}
|
|
1987
|
+
),
|
|
1988
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1989
|
+
"button",
|
|
1990
|
+
{
|
|
1991
|
+
type: "button",
|
|
1992
|
+
onClick: () => {
|
|
1993
|
+
setActiveCropRatio(3 / 2);
|
|
1994
|
+
editor.applyCropPreset(3 / 2);
|
|
1995
|
+
},
|
|
1996
|
+
className: `px-2.5 py-2 rounded-lg border font-medium ${activeCropRatio === 3 / 2 ? "bg-indigo-600 border-indigo-500 text-white" : "bg-slate-800 hover:bg-slate-700 text-slate-200 border-slate-700"}`,
|
|
1997
|
+
children: "3:2 Photo"
|
|
1998
|
+
}
|
|
1999
|
+
),
|
|
2000
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2001
|
+
"button",
|
|
2002
|
+
{
|
|
2003
|
+
type: "button",
|
|
2004
|
+
onClick: () => {
|
|
2005
|
+
setActiveCropRatio(null);
|
|
2006
|
+
editor.resetCrop();
|
|
2007
|
+
},
|
|
2008
|
+
className: "px-2.5 py-2 rounded-lg bg-rose-950/40 hover:bg-rose-900/60 text-rose-300 border border-rose-800/40 font-medium",
|
|
2009
|
+
children: "Clear Crop"
|
|
2010
|
+
}
|
|
2011
|
+
)
|
|
2012
|
+
] }),
|
|
2013
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "h-px bg-slate-800 my-1" }),
|
|
2014
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Orientation & Flips" }),
|
|
2015
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
2016
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2017
|
+
"button",
|
|
2018
|
+
{
|
|
2019
|
+
type: "button",
|
|
2020
|
+
onClick: () => editor.rotate(-90),
|
|
2021
|
+
className: "px-3 py-2 rounded-lg bg-slate-800 hover:bg-slate-700 text-slate-200 border border-slate-700",
|
|
2022
|
+
children: "Rotate -90\xB0"
|
|
2023
|
+
}
|
|
2024
|
+
),
|
|
2025
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2026
|
+
"button",
|
|
2027
|
+
{
|
|
2028
|
+
type: "button",
|
|
2029
|
+
onClick: () => editor.rotate(90),
|
|
2030
|
+
className: "px-3 py-2 rounded-lg bg-slate-800 hover:bg-slate-700 text-slate-200 border border-slate-700",
|
|
2031
|
+
children: "Rotate +90\xB0"
|
|
2032
|
+
}
|
|
2033
|
+
),
|
|
2034
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2035
|
+
"button",
|
|
2036
|
+
{
|
|
2037
|
+
type: "button",
|
|
2038
|
+
onClick: () => editor.flipHorizontal(),
|
|
2039
|
+
className: `px-3 py-2 rounded-lg border transition-colors ${state.transform.flipH ? "bg-indigo-600 border-indigo-500 text-white" : "bg-slate-800 border-slate-700 text-slate-200 hover:bg-slate-700"}`,
|
|
2040
|
+
children: "Flip Horizontal"
|
|
2041
|
+
}
|
|
2042
|
+
),
|
|
2043
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2044
|
+
"button",
|
|
2045
|
+
{
|
|
2046
|
+
type: "button",
|
|
2047
|
+
onClick: () => editor.flipVertical(),
|
|
2048
|
+
className: `px-3 py-2 rounded-lg border transition-colors ${state.transform.flipV ? "bg-indigo-600 border-indigo-500 text-white" : "bg-slate-800 border-slate-700 text-slate-200 hover:bg-slate-700"}`,
|
|
2049
|
+
children: "Flip Vertical"
|
|
2050
|
+
}
|
|
2051
|
+
)
|
|
2052
|
+
] })
|
|
2053
|
+
] }),
|
|
2054
|
+
activeTab === "adjust" && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col gap-3 text-xs", children: [
|
|
2055
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
2056
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Color Adjustments" }),
|
|
2057
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2058
|
+
"button",
|
|
2059
|
+
{
|
|
2060
|
+
type: "button",
|
|
2061
|
+
onClick: () => editor.resetAdjustments(),
|
|
2062
|
+
className: "text-[10px] text-rose-400 hover:underline",
|
|
2063
|
+
children: "Reset Sliders"
|
|
2064
|
+
}
|
|
2065
|
+
)
|
|
2066
|
+
] }),
|
|
2067
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
|
|
2068
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
2069
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Brightness" }),
|
|
2070
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "font-mono", children: state.adjustments.brightness })
|
|
2071
|
+
] }),
|
|
2072
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2073
|
+
"input",
|
|
2074
|
+
{
|
|
2075
|
+
type: "range",
|
|
2076
|
+
min: "-100",
|
|
2077
|
+
max: "100",
|
|
2078
|
+
value: state.adjustments.brightness,
|
|
2079
|
+
onChange: (e) => editor.setAdjustments({ brightness: Number(e.target.value) }),
|
|
2080
|
+
className: "w-full accent-indigo-500"
|
|
2081
|
+
}
|
|
2082
|
+
)
|
|
2083
|
+
] }),
|
|
2084
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
|
|
2085
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
2086
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Contrast" }),
|
|
2087
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "font-mono", children: state.adjustments.contrast })
|
|
2088
|
+
] }),
|
|
2089
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2090
|
+
"input",
|
|
2091
|
+
{
|
|
2092
|
+
type: "range",
|
|
2093
|
+
min: "-100",
|
|
2094
|
+
max: "100",
|
|
2095
|
+
value: state.adjustments.contrast,
|
|
2096
|
+
onChange: (e) => editor.setAdjustments({ contrast: Number(e.target.value) }),
|
|
2097
|
+
className: "w-full accent-indigo-500"
|
|
2098
|
+
}
|
|
2099
|
+
)
|
|
2100
|
+
] }),
|
|
2101
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
|
|
2102
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
2103
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Saturation" }),
|
|
2104
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "font-mono", children: state.adjustments.saturation })
|
|
2105
|
+
] }),
|
|
2106
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2107
|
+
"input",
|
|
2108
|
+
{
|
|
2109
|
+
type: "range",
|
|
2110
|
+
min: "-100",
|
|
2111
|
+
max: "100",
|
|
2112
|
+
value: state.adjustments.saturation,
|
|
2113
|
+
onChange: (e) => editor.setAdjustments({ saturation: Number(e.target.value) }),
|
|
2114
|
+
className: "w-full accent-indigo-500"
|
|
2115
|
+
}
|
|
2116
|
+
)
|
|
2117
|
+
] }),
|
|
2118
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
|
|
2119
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
2120
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Exposure" }),
|
|
2121
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "font-mono", children: state.adjustments.exposure })
|
|
2122
|
+
] }),
|
|
2123
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2124
|
+
"input",
|
|
2125
|
+
{
|
|
2126
|
+
type: "range",
|
|
2127
|
+
min: "-100",
|
|
2128
|
+
max: "100",
|
|
2129
|
+
value: state.adjustments.exposure,
|
|
2130
|
+
onChange: (e) => editor.setAdjustments({ exposure: Number(e.target.value) }),
|
|
2131
|
+
className: "w-full accent-indigo-500"
|
|
2132
|
+
}
|
|
2133
|
+
)
|
|
2134
|
+
] }),
|
|
2135
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
|
|
2136
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
2137
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Warmth / Temperature" }),
|
|
2138
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "font-mono", children: state.adjustments.temperature })
|
|
2139
|
+
] }),
|
|
2140
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2141
|
+
"input",
|
|
2142
|
+
{
|
|
2143
|
+
type: "range",
|
|
2144
|
+
min: "-100",
|
|
2145
|
+
max: "100",
|
|
2146
|
+
value: state.adjustments.temperature,
|
|
2147
|
+
onChange: (e) => editor.setAdjustments({ temperature: Number(e.target.value) }),
|
|
2148
|
+
className: "w-full accent-indigo-500"
|
|
2149
|
+
}
|
|
2150
|
+
)
|
|
2151
|
+
] }),
|
|
2152
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
|
|
2153
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
2154
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Blur" }),
|
|
2155
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "font-mono", children: [
|
|
2156
|
+
state.adjustments.blur,
|
|
2157
|
+
"px"
|
|
2158
|
+
] })
|
|
2159
|
+
] }),
|
|
2160
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2161
|
+
"input",
|
|
2162
|
+
{
|
|
2163
|
+
type: "range",
|
|
2164
|
+
min: "0",
|
|
2165
|
+
max: "30",
|
|
2166
|
+
value: state.adjustments.blur,
|
|
2167
|
+
onChange: (e) => editor.setAdjustments({ blur: Number(e.target.value) }),
|
|
2168
|
+
className: "w-full accent-indigo-500"
|
|
2169
|
+
}
|
|
2170
|
+
)
|
|
2171
|
+
] }),
|
|
2172
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
|
|
2173
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
2174
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Opacity" }),
|
|
2175
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "font-mono", children: [
|
|
2176
|
+
Math.round(state.adjustments.opacity * 100),
|
|
2177
|
+
"%"
|
|
2178
|
+
] })
|
|
2179
|
+
] }),
|
|
2180
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2181
|
+
"input",
|
|
2182
|
+
{
|
|
2183
|
+
type: "range",
|
|
2184
|
+
min: "0",
|
|
2185
|
+
max: "1",
|
|
2186
|
+
step: "0.05",
|
|
2187
|
+
value: state.adjustments.opacity,
|
|
2188
|
+
onChange: (e) => editor.setAdjustments({ opacity: Number(e.target.value) }),
|
|
2189
|
+
className: "w-full accent-indigo-500"
|
|
2190
|
+
}
|
|
2191
|
+
)
|
|
2192
|
+
] })
|
|
2193
|
+
] }),
|
|
2194
|
+
activeTab === "annotate" && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col gap-3 text-xs", children: [
|
|
2195
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Draw & Annotate" }),
|
|
2196
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "grid grid-cols-3 gap-1.5", children: ["pen", "rect", "circle", "arrow", "line"].map((t) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2197
|
+
"button",
|
|
2198
|
+
{
|
|
2199
|
+
type: "button",
|
|
2200
|
+
onClick: () => editor.setTool(t),
|
|
2201
|
+
className: `py-1.5 px-2 rounded-lg font-medium capitalize border transition-colors ${state.activeTool === t ? "bg-indigo-600 border-indigo-500 text-white" : "bg-slate-800 border-slate-700 text-slate-300 hover:bg-slate-700"}`,
|
|
2202
|
+
children: t
|
|
2203
|
+
},
|
|
2204
|
+
t
|
|
2205
|
+
)) }),
|
|
2206
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center gap-2 mt-2", children: [
|
|
2207
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("label", { className: "text-slate-400", children: "Color:" }),
|
|
2208
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2209
|
+
"input",
|
|
2210
|
+
{
|
|
2211
|
+
type: "color",
|
|
2212
|
+
value: activeColor,
|
|
2213
|
+
onChange: (e) => setActiveColor(e.target.value),
|
|
2214
|
+
className: "w-8 h-8 rounded border border-slate-700 cursor-pointer bg-transparent"
|
|
2215
|
+
}
|
|
2216
|
+
),
|
|
2217
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex-1 ml-2", children: [
|
|
2218
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex justify-between text-slate-400 mb-0.5", children: [
|
|
2219
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Width:" }),
|
|
2220
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "font-mono", children: [
|
|
2221
|
+
strokeWidth,
|
|
2222
|
+
"px"
|
|
2223
|
+
] })
|
|
2224
|
+
] }),
|
|
2225
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2226
|
+
"input",
|
|
2227
|
+
{
|
|
2228
|
+
type: "range",
|
|
2229
|
+
min: "1",
|
|
2230
|
+
max: "20",
|
|
2231
|
+
value: strokeWidth,
|
|
2232
|
+
onChange: (e) => setStrokeWidth(Number(e.target.value)),
|
|
2233
|
+
className: "w-full accent-indigo-500"
|
|
2234
|
+
}
|
|
2235
|
+
)
|
|
2236
|
+
] })
|
|
2237
|
+
] }),
|
|
2238
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "h-px bg-slate-800 my-1" }),
|
|
2239
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Add Text" }),
|
|
2240
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex gap-2", children: [
|
|
2241
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2242
|
+
"input",
|
|
2243
|
+
{
|
|
2244
|
+
type: "text",
|
|
2245
|
+
placeholder: "Type watermark or label...",
|
|
2246
|
+
value: textInput,
|
|
2247
|
+
onChange: (e) => setTextInput(e.target.value),
|
|
2248
|
+
onKeyDown: (e) => e.key === "Enter" && handleAddText(),
|
|
2249
|
+
className: "flex-1 bg-slate-950 border border-slate-700 rounded-lg px-2.5 py-1.5 text-xs text-slate-100 placeholder-slate-500 focus:outline-none focus:border-indigo-500"
|
|
2250
|
+
}
|
|
2251
|
+
),
|
|
2252
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2253
|
+
"button",
|
|
2254
|
+
{
|
|
2255
|
+
type: "button",
|
|
2256
|
+
onClick: handleAddText,
|
|
2257
|
+
className: "px-3 py-1.5 rounded-lg bg-indigo-600 hover:bg-indigo-500 text-white font-medium shadow",
|
|
2258
|
+
children: "Add"
|
|
2259
|
+
}
|
|
2260
|
+
)
|
|
2261
|
+
] }),
|
|
2262
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center justify-between text-slate-400", children: [
|
|
2263
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Font Size:" }),
|
|
2264
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "font-mono", children: [
|
|
2265
|
+
fontSize,
|
|
2266
|
+
"px"
|
|
2267
|
+
] })
|
|
2268
|
+
] }),
|
|
2269
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2270
|
+
"input",
|
|
2271
|
+
{
|
|
2272
|
+
type: "range",
|
|
2273
|
+
min: "12",
|
|
2274
|
+
max: "72",
|
|
2275
|
+
value: fontSize,
|
|
2276
|
+
onChange: (e) => setFontSize(Number(e.target.value)),
|
|
2277
|
+
className: "w-full accent-indigo-500"
|
|
2278
|
+
}
|
|
2279
|
+
),
|
|
2280
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "h-px bg-slate-800 my-1" }),
|
|
2281
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
2282
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "text-slate-400", children: [
|
|
2283
|
+
"Layers (",
|
|
2284
|
+
state.annotations.length,
|
|
2285
|
+
")"
|
|
2286
|
+
] }),
|
|
2287
|
+
state.selectedAnnotationId && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2288
|
+
"button",
|
|
2289
|
+
{
|
|
2290
|
+
type: "button",
|
|
2291
|
+
onClick: () => editor.removeAnnotation(state.selectedAnnotationId),
|
|
2292
|
+
className: "text-rose-400 hover:underline text-[11px]",
|
|
2293
|
+
children: "Delete Selected"
|
|
2294
|
+
}
|
|
2295
|
+
),
|
|
2296
|
+
state.annotations.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2297
|
+
"button",
|
|
2298
|
+
{
|
|
2299
|
+
type: "button",
|
|
2300
|
+
onClick: () => editor.clearAnnotations(),
|
|
2301
|
+
className: "text-slate-400 hover:text-slate-200 text-[11px]",
|
|
2302
|
+
children: "Clear All"
|
|
2303
|
+
}
|
|
2304
|
+
)
|
|
2305
|
+
] })
|
|
2306
|
+
] }),
|
|
2307
|
+
activeTab === "depth" && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col gap-3 text-xs", children: [
|
|
2308
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
2309
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Depth Masking" }),
|
|
2310
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2311
|
+
"button",
|
|
2312
|
+
{
|
|
2313
|
+
type: "button",
|
|
2314
|
+
onClick: () => editor.resetDepthMask(),
|
|
2315
|
+
className: "text-[10px] text-rose-400 hover:underline",
|
|
2316
|
+
children: "Reset Mask"
|
|
2317
|
+
}
|
|
2318
|
+
)
|
|
2319
|
+
] }),
|
|
2320
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "flex items-center gap-2 cursor-pointer text-slate-300", children: [
|
|
2321
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2322
|
+
"input",
|
|
2323
|
+
{
|
|
2324
|
+
type: "checkbox",
|
|
2325
|
+
checked: state.depthMask.enabled,
|
|
2326
|
+
onChange: (e) => editor.setDepthMask({ enabled: e.target.checked }),
|
|
2327
|
+
className: "rounded accent-indigo-500"
|
|
2328
|
+
}
|
|
2329
|
+
),
|
|
2330
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Enable Depth / Alpha Mask" })
|
|
2331
|
+
] }),
|
|
2332
|
+
state.depthMask.enabled && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
|
|
2333
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
|
|
2334
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("label", { className: "text-slate-400 block mb-1", children: "Mask Image URL:" }),
|
|
2335
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2336
|
+
"input",
|
|
2337
|
+
{
|
|
2338
|
+
type: "text",
|
|
2339
|
+
placeholder: "https://.../depthmap.png",
|
|
2340
|
+
value: state.depthMask.maskSource || "",
|
|
2341
|
+
onChange: (e) => editor.setDepthMask({ maskSource: e.target.value || null }),
|
|
2342
|
+
className: "w-full bg-slate-950 border border-slate-700 rounded-lg px-2.5 py-1.5 text-xs text-slate-100 placeholder-slate-500"
|
|
2343
|
+
}
|
|
2344
|
+
)
|
|
2345
|
+
] }),
|
|
2346
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex gap-2", children: [
|
|
2347
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2348
|
+
"button",
|
|
2349
|
+
{
|
|
2350
|
+
type: "button",
|
|
2351
|
+
onClick: () => editor.setDepthMask({ mode: "threshold" }),
|
|
2352
|
+
className: `flex-1 py-1.5 rounded-lg border text-xs font-medium ${state.depthMask.mode === "threshold" ? "bg-indigo-600 border-indigo-500 text-white" : "bg-slate-800 border-slate-700 text-slate-300"}`,
|
|
2353
|
+
children: "Threshold Mode"
|
|
2354
|
+
}
|
|
2355
|
+
),
|
|
2356
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2357
|
+
"button",
|
|
2358
|
+
{
|
|
2359
|
+
type: "button",
|
|
2360
|
+
onClick: () => editor.setDepthMask({ mode: "brightness" }),
|
|
2361
|
+
className: `flex-1 py-1.5 rounded-lg border text-xs font-medium ${state.depthMask.mode === "brightness" ? "bg-indigo-600 border-indigo-500 text-white" : "bg-slate-800 border-slate-700 text-slate-300"}`,
|
|
2362
|
+
children: "Brightness Mode"
|
|
2363
|
+
}
|
|
2364
|
+
)
|
|
2365
|
+
] }),
|
|
2366
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
|
|
2367
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
2368
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Depth Start (Lower):" }),
|
|
2369
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "font-mono", children: state.depthMask.depthRange[0] })
|
|
2370
|
+
] }),
|
|
2371
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2372
|
+
"input",
|
|
2373
|
+
{
|
|
2374
|
+
type: "range",
|
|
2375
|
+
min: "0",
|
|
2376
|
+
max: "255",
|
|
2377
|
+
value: state.depthMask.depthRange[0],
|
|
2378
|
+
onChange: (e) => editor.setDepthMask({
|
|
2379
|
+
depthRange: [Number(e.target.value), state.depthMask.depthRange[1]]
|
|
2380
|
+
}),
|
|
2381
|
+
className: "w-full accent-indigo-500"
|
|
2382
|
+
}
|
|
2383
|
+
)
|
|
2384
|
+
] }),
|
|
2385
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
|
|
2386
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
2387
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Depth Stop (Upper):" }),
|
|
2388
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "font-mono", children: state.depthMask.depthRange[1] })
|
|
2389
|
+
] }),
|
|
2390
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2391
|
+
"input",
|
|
2392
|
+
{
|
|
2393
|
+
type: "range",
|
|
2394
|
+
min: "0",
|
|
2395
|
+
max: "255",
|
|
2396
|
+
value: state.depthMask.depthRange[1],
|
|
2397
|
+
onChange: (e) => editor.setDepthMask({
|
|
2398
|
+
depthRange: [state.depthMask.depthRange[0], Number(e.target.value)]
|
|
2399
|
+
}),
|
|
2400
|
+
className: "w-full accent-indigo-500"
|
|
2401
|
+
}
|
|
2402
|
+
)
|
|
2403
|
+
] }),
|
|
2404
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
|
|
2405
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
2406
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Softness:" }),
|
|
2407
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "font-mono", children: state.depthMask.softness })
|
|
2408
|
+
] }),
|
|
2409
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2410
|
+
"input",
|
|
2411
|
+
{
|
|
2412
|
+
type: "range",
|
|
2413
|
+
min: "1",
|
|
2414
|
+
max: "50",
|
|
2415
|
+
value: state.depthMask.softness,
|
|
2416
|
+
onChange: (e) => editor.setDepthMask({ softness: Number(e.target.value) }),
|
|
2417
|
+
className: "w-full accent-indigo-500"
|
|
2418
|
+
}
|
|
2419
|
+
)
|
|
2420
|
+
] }),
|
|
2421
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "flex items-center gap-2 cursor-pointer text-slate-300 mt-1", children: [
|
|
2422
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2423
|
+
"input",
|
|
2424
|
+
{
|
|
2425
|
+
type: "checkbox",
|
|
2426
|
+
checked: state.depthMask.invert,
|
|
2427
|
+
onChange: (e) => editor.setDepthMask({ invert: e.target.checked }),
|
|
2428
|
+
className: "rounded accent-indigo-500"
|
|
2429
|
+
}
|
|
2430
|
+
),
|
|
2431
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Invert Mask" })
|
|
2432
|
+
] })
|
|
2433
|
+
] })
|
|
2434
|
+
] })
|
|
2435
|
+
] })
|
|
2436
|
+
] })
|
|
2437
|
+
]
|
|
2438
|
+
}
|
|
2439
|
+
);
|
|
2440
|
+
};
|
|
1252
2441
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1253
2442
|
0 && (module.exports = {
|
|
1254
2443
|
BubbleMenu,
|
|
@@ -1256,6 +2445,7 @@ function useEditorSlots() {
|
|
|
1256
2445
|
EditorContent,
|
|
1257
2446
|
EditorSlotsProvider,
|
|
1258
2447
|
HistoryManager,
|
|
2448
|
+
ImageEditor,
|
|
1259
2449
|
SpreadsheetEditor,
|
|
1260
2450
|
TemplateRenderer,
|
|
1261
2451
|
createDirtyTracker,
|
|
@@ -1266,6 +2456,7 @@ function useEditorSlots() {
|
|
|
1266
2456
|
useEditorSlots,
|
|
1267
2457
|
useHistory,
|
|
1268
2458
|
useHistoryShortcuts,
|
|
2459
|
+
useImageEditor,
|
|
1269
2460
|
useNavigationGuard,
|
|
1270
2461
|
usePromptEditor,
|
|
1271
2462
|
useSpreadsheetEditor
|