@pixerate/editor-react 0.15.0 → 0.17.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/README.md +6 -2
- package/dist/canvas/index.cjs +484 -119
- package/dist/canvas/index.cjs.map +1 -1
- package/dist/canvas/index.d.cts +122 -3
- package/dist/canvas/index.d.ts +122 -3
- package/dist/canvas/index.js +380 -0
- package/dist/canvas/index.js.map +1 -1
- package/dist/chunk-I3E4KSA2.js +515 -0
- package/dist/chunk-I3E4KSA2.js.map +1 -0
- package/dist/chunk-JR5ZBDR7.js +2012 -0
- package/dist/chunk-JR5ZBDR7.js.map +1 -0
- package/dist/chunk-XWFU2CSG.js +167 -0
- package/dist/chunk-XWFU2CSG.js.map +1 -0
- package/dist/chunk-ZU54BMDC.js +2128 -0
- package/dist/chunk-ZU54BMDC.js.map +1 -0
- package/dist/generation/index.cjs +525 -0
- package/dist/generation/index.cjs.map +1 -0
- package/dist/generation/index.d.cts +124 -0
- package/dist/generation/index.d.ts +124 -0
- package/dist/generation/index.js +19 -0
- package/dist/generation/index.js.map +1 -0
- package/dist/image-editor/index.cjs +988 -337
- package/dist/image-editor/index.cjs.map +1 -1
- package/dist/image-editor/index.d.cts +92 -2
- package/dist/image-editor/index.d.ts +92 -2
- package/dist/image-editor/index.js +17 -3
- package/dist/index.cjs +3628 -754
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +226 -5
- package/dist/index.d.ts +226 -5
- package/dist/index.js +868 -77
- package/dist/index.js.map +1 -1
- package/dist/persistence/index.cjs +191 -0
- package/dist/persistence/index.cjs.map +1 -0
- package/dist/persistence/index.d.cts +77 -0
- package/dist/persistence/index.d.ts +77 -0
- package/dist/persistence/index.js +9 -0
- package/dist/persistence/index.js.map +1 -0
- package/dist/spreadsheet/index.cjs +949 -111
- package/dist/spreadsheet/index.cjs.map +1 -1
- package/dist/spreadsheet/index.d.cts +121 -7
- package/dist/spreadsheet/index.d.ts +121 -7
- package/dist/spreadsheet/index.js +11 -1
- package/dist/tokens/index.cjs +395 -0
- package/dist/tokens/index.cjs.map +1 -0
- package/dist/tokens/index.d.cts +54 -0
- package/dist/tokens/index.d.ts +54 -0
- package/dist/tokens/index.js +375 -0
- package/dist/tokens/index.js.map +1 -0
- package/package.json +26 -2
- package/dist/chunk-LPFZMZ3L.js +0 -1461
- package/dist/chunk-LPFZMZ3L.js.map +0 -1
- package/dist/chunk-QVQYYHJ5.js +0 -1161
- package/dist/chunk-QVQYYHJ5.js.map +0 -1
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,13 +17,28 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/image-editor/index.ts
|
|
21
31
|
var image_editor_exports = {};
|
|
22
32
|
__export(image_editor_exports, {
|
|
33
|
+
AdjustmentLayersPanel: () => AdjustmentLayersPanel,
|
|
34
|
+
ControlSlider: () => ControlSlider,
|
|
35
|
+
CurvesEditor: () => CurvesEditor,
|
|
36
|
+
Histogram: () => Histogram,
|
|
23
37
|
ImageEditor: () => ImageEditor,
|
|
24
|
-
|
|
38
|
+
RefineEdgePanel: () => RefineEdgePanel,
|
|
39
|
+
SelectionOptionsBar: () => SelectionOptionsBar,
|
|
40
|
+
useImageEditor: () => useImageEditor,
|
|
41
|
+
useImageHistogram: () => useImageHistogram
|
|
25
42
|
});
|
|
26
43
|
module.exports = __toCommonJS(image_editor_exports);
|
|
27
44
|
|
|
@@ -36,6 +53,8 @@ function useImageEditor(options = {}) {
|
|
|
36
53
|
const controller = controllerRef.current;
|
|
37
54
|
const [state, setState] = (0, import_react.useState)(() => controller.getState());
|
|
38
55
|
const [historyTicks, setHistoryTicks] = (0, import_react.useState)(0);
|
|
56
|
+
const [histogram, setHistogram] = (0, import_react.useState)(() => controller.getHistogram());
|
|
57
|
+
(0, import_react.useEffect)(() => controller.subscribeHistogram(setHistogram), [controller]);
|
|
39
58
|
(0, import_react.useEffect)(() => {
|
|
40
59
|
const unsub = controller.subscribe((next) => {
|
|
41
60
|
setState(next);
|
|
@@ -68,6 +87,20 @@ function useImageEditor(options = {}) {
|
|
|
68
87
|
);
|
|
69
88
|
const resetAdjustments = (0, import_react.useCallback)(() => controller.resetAdjustments(), [controller]);
|
|
70
89
|
const upgradeProcess = (0, import_react.useCallback)(() => controller.upgradeProcess(), [controller]);
|
|
90
|
+
const addAdjustmentLayer = (0, import_react.useCallback)(
|
|
91
|
+
(init, index) => controller.addAdjustmentLayer(init, index),
|
|
92
|
+
[controller]
|
|
93
|
+
);
|
|
94
|
+
const updateAdjustmentLayer = (0, import_react.useCallback)(
|
|
95
|
+
(id, update, commitToHistory = true) => controller.updateAdjustmentLayer(id, update, commitToHistory),
|
|
96
|
+
[controller]
|
|
97
|
+
);
|
|
98
|
+
const removeAdjustmentLayer = (0, import_react.useCallback)((id) => controller.removeAdjustmentLayer(id), [controller]);
|
|
99
|
+
const moveAdjustmentLayer = (0, import_react.useCallback)((id, to) => controller.moveAdjustmentLayer(id, to), [controller]);
|
|
100
|
+
const toggleAdjustmentLayer = (0, import_react.useCallback)(
|
|
101
|
+
(id, enabled) => controller.toggleAdjustmentLayer(id, enabled),
|
|
102
|
+
[controller]
|
|
103
|
+
);
|
|
71
104
|
const setDepthMask = (0, import_react.useCallback)((partial) => controller.setDepthMask(partial), [controller]);
|
|
72
105
|
const resetDepthMask = (0, import_react.useCallback)(() => controller.resetDepthMask(), [controller]);
|
|
73
106
|
const addAnnotation = (0, import_react.useCallback)((ann) => controller.addAnnotation(ann), [controller]);
|
|
@@ -129,6 +162,12 @@ function useImageEditor(options = {}) {
|
|
|
129
162
|
setAdjustments,
|
|
130
163
|
resetAdjustments,
|
|
131
164
|
upgradeProcess,
|
|
165
|
+
addAdjustmentLayer,
|
|
166
|
+
updateAdjustmentLayer,
|
|
167
|
+
removeAdjustmentLayer,
|
|
168
|
+
moveAdjustmentLayer,
|
|
169
|
+
toggleAdjustmentLayer,
|
|
170
|
+
histogram,
|
|
132
171
|
setDepthMask,
|
|
133
172
|
resetDepthMask,
|
|
134
173
|
addAnnotation,
|
|
@@ -157,14 +196,685 @@ function useImageEditor(options = {}) {
|
|
|
157
196
|
applyBackgroundRemovedImage
|
|
158
197
|
}),
|
|
159
198
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
160
|
-
[controller, state, historyTicks]
|
|
199
|
+
[controller, state, historyTicks, histogram]
|
|
161
200
|
);
|
|
162
201
|
}
|
|
163
202
|
|
|
164
203
|
// src/image-editor/ImageEditor.tsx
|
|
165
|
-
var
|
|
204
|
+
var import_react7 = __toESM(require("react"), 1);
|
|
205
|
+
var import_image_editor8 = require("@pixerate/editor/image-editor");
|
|
206
|
+
|
|
207
|
+
// src/image-editor/ControlSlider.tsx
|
|
166
208
|
var import_image_editor2 = require("@pixerate/editor/image-editor");
|
|
167
209
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
210
|
+
var ControlSlider = ({ spec, value, onChange, disabled, label }) => {
|
|
211
|
+
const name = label ?? spec.label;
|
|
212
|
+
const track = (0, import_image_editor2.controlTrackBackground)(spec);
|
|
213
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { "data-control-id": spec.id, children: [
|
|
214
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
215
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: name }),
|
|
216
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-mono", children: (0, import_image_editor2.formatControlValue)(spec, value) })
|
|
217
|
+
] }),
|
|
218
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
219
|
+
"input",
|
|
220
|
+
{
|
|
221
|
+
type: "range",
|
|
222
|
+
min: spec.min,
|
|
223
|
+
max: spec.max,
|
|
224
|
+
step: spec.step,
|
|
225
|
+
"aria-label": name,
|
|
226
|
+
disabled,
|
|
227
|
+
value,
|
|
228
|
+
onChange: (e) => onChange(Number(e.target.value)),
|
|
229
|
+
className: "w-full accent-indigo-500 disabled:opacity-40"
|
|
230
|
+
}
|
|
231
|
+
),
|
|
232
|
+
track && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "h-0.5 rounded-full opacity-70", style: { background: track } })
|
|
233
|
+
] });
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
// src/image-editor/Histogram.tsx
|
|
237
|
+
var import_react2 = require("react");
|
|
238
|
+
var import_image_editor3 = require("@pixerate/editor/image-editor");
|
|
239
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
240
|
+
var FILL = {
|
|
241
|
+
luminance: "rgba(148, 163, 184, 0.45)",
|
|
242
|
+
red: "rgba(239, 68, 68, 0.45)",
|
|
243
|
+
green: "rgba(34, 197, 94, 0.45)",
|
|
244
|
+
blue: "rgba(59, 130, 246, 0.45)"
|
|
245
|
+
};
|
|
246
|
+
var W = 256;
|
|
247
|
+
var Histogram = ({ histogram, channel = "rgb", height = 64, className = "", style }) => {
|
|
248
|
+
const channels = channel === "rgb" ? ["luminance", "red", "green", "blue"] : [channel];
|
|
249
|
+
const paths = (0, import_react2.useMemo)(
|
|
250
|
+
() => histogram && histogram.samples > 0 ? channels.map((c) => ({ c, d: (0, import_image_editor3.histogramPath)(histogram, c, W, height) })) : [],
|
|
251
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
252
|
+
[histogram, channel, height]
|
|
253
|
+
);
|
|
254
|
+
const clipLow = histogram && histogram.samples ? Math.max(...histogram.shadows) / histogram.samples : 0;
|
|
255
|
+
const clipHigh = histogram && histogram.samples ? Math.max(...histogram.highlights) / histogram.samples : 0;
|
|
256
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
257
|
+
"svg",
|
|
258
|
+
{
|
|
259
|
+
viewBox: `0 0 ${W} ${height}`,
|
|
260
|
+
preserveAspectRatio: "none",
|
|
261
|
+
role: "img",
|
|
262
|
+
"aria-label": histogram ? `Histogram of ${histogram.samples} pixels` : "Histogram (no data)",
|
|
263
|
+
"data-testid": "image-editor-histogram",
|
|
264
|
+
className: `w-full rounded bg-slate-950/60 ${className}`,
|
|
265
|
+
style: { height, ...style },
|
|
266
|
+
children: [
|
|
267
|
+
paths.map(({ c, d }) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d, fill: FILL[c], stroke: "none" }, c)),
|
|
268
|
+
clipLow > 5e-3 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: 0, y: 0, width: 3, height, fill: "#3b82f6", "data-testid": "histogram-clip-shadows" }),
|
|
269
|
+
clipHigh > 5e-3 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: W - 3, y: 0, width: 3, height, fill: "#ef4444", "data-testid": "histogram-clip-highlights" })
|
|
270
|
+
]
|
|
271
|
+
}
|
|
272
|
+
);
|
|
273
|
+
};
|
|
274
|
+
function useImageHistogram(controller) {
|
|
275
|
+
const [histogram, setHistogram] = (0, import_react2.useState)(() => controller.getHistogram());
|
|
276
|
+
(0, import_react2.useEffect)(() => controller.subscribeHistogram(setHistogram), [controller]);
|
|
277
|
+
return histogram;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// src/image-editor/AdjustmentLayersPanel.tsx
|
|
281
|
+
var import_react4 = require("react");
|
|
282
|
+
var import_image_editor5 = require("@pixerate/editor/image-editor");
|
|
283
|
+
|
|
284
|
+
// src/image-editor/CurvesEditor.tsx
|
|
285
|
+
var import_react3 = require("react");
|
|
286
|
+
var import_image_editor4 = require("@pixerate/editor/image-editor");
|
|
287
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
288
|
+
var BOX = 256;
|
|
289
|
+
var STROKE = { master: "#e2e8f0", red: "#ef4444", green: "#22c55e", blue: "#3b82f6" };
|
|
290
|
+
var CurvesEditor = ({ points, onChange, histogram, channel = "master", size = 200 }) => {
|
|
291
|
+
const svgRef = (0, import_react3.useRef)(null);
|
|
292
|
+
const [drag, setDrag] = (0, import_react3.useState)(null);
|
|
293
|
+
const toBox = (e) => {
|
|
294
|
+
const rect = svgRef.current?.getBoundingClientRect();
|
|
295
|
+
if (!rect || !rect.width || !rect.height) return { x: 0, y: 0 };
|
|
296
|
+
return { x: (e.clientX - rect.left) / rect.width * BOX, y: (e.clientY - rect.top) / rect.height * BOX };
|
|
297
|
+
};
|
|
298
|
+
const onPointerDown = (e) => {
|
|
299
|
+
const { x, y } = toBox(e);
|
|
300
|
+
let index = (0, import_image_editor4.hitTestCurvePoint)(points, x, y, BOX, BOX, 10);
|
|
301
|
+
if (index === -1) {
|
|
302
|
+
const inserted = (0, import_image_editor4.insertCurvePoint)(points, (0, import_image_editor4.boxToCurvePoint)(x, y, BOX, BOX));
|
|
303
|
+
onChange(inserted.points, true);
|
|
304
|
+
index = inserted.index;
|
|
305
|
+
} else {
|
|
306
|
+
onChange(points, true);
|
|
307
|
+
}
|
|
308
|
+
setDrag(index);
|
|
309
|
+
e.target.setPointerCapture?.(e.pointerId);
|
|
310
|
+
};
|
|
311
|
+
const onPointerMove = (e) => {
|
|
312
|
+
if (drag === null) return;
|
|
313
|
+
onChange((0, import_image_editor4.moveCurvePoint)(points, drag, (0, import_image_editor4.boxToCurvePoint)(toBox(e).x, toBox(e).y, BOX, BOX)), false);
|
|
314
|
+
};
|
|
315
|
+
const onDoubleClick = (e) => {
|
|
316
|
+
const { x, y } = toBox(e);
|
|
317
|
+
const index = (0, import_image_editor4.hitTestCurvePoint)(points, x, y, BOX, BOX, 10);
|
|
318
|
+
if (index !== -1 && points.length > 2) onChange((0, import_image_editor4.removeCurvePoint)(points, index), true);
|
|
319
|
+
};
|
|
320
|
+
const histChannel = channel === "master" ? "luminance" : channel;
|
|
321
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
322
|
+
"svg",
|
|
323
|
+
{
|
|
324
|
+
ref: svgRef,
|
|
325
|
+
viewBox: `0 0 ${BOX} ${BOX}`,
|
|
326
|
+
width: size,
|
|
327
|
+
height: size,
|
|
328
|
+
role: "application",
|
|
329
|
+
"aria-label": `${channel} curve`,
|
|
330
|
+
"data-testid": "curves-editor",
|
|
331
|
+
className: "rounded bg-slate-950/70 touch-none select-none cursor-crosshair",
|
|
332
|
+
onPointerDown,
|
|
333
|
+
onPointerMove,
|
|
334
|
+
onPointerUp: () => setDrag(null),
|
|
335
|
+
onPointerCancel: () => setDrag(null),
|
|
336
|
+
onDoubleClick,
|
|
337
|
+
children: [
|
|
338
|
+
histogram && histogram.samples > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: (0, import_image_editor4.histogramPath)(histogram, histChannel, BOX, BOX), fill: "rgba(148, 163, 184, 0.25)" }),
|
|
339
|
+
[64, 128, 192].map((v) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { stroke: "rgba(148, 163, 184, 0.2)", strokeWidth: 1, children: [
|
|
340
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: v, y1: 0, x2: v, y2: BOX }),
|
|
341
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: 0, y1: v, x2: BOX, y2: v })
|
|
342
|
+
] }, v)),
|
|
343
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: 0, y1: BOX, x2: BOX, y2: 0, stroke: "rgba(148, 163, 184, 0.35)", strokeDasharray: "4 4" }),
|
|
344
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: (0, import_image_editor4.curveSvgPath)(points, BOX, BOX), fill: "none", stroke: STROKE[channel], strokeWidth: 2 }),
|
|
345
|
+
points.map((p, i) => {
|
|
346
|
+
const b = (0, import_image_editor4.curvePointToBox)(p, BOX, BOX);
|
|
347
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
348
|
+
"circle",
|
|
349
|
+
{
|
|
350
|
+
cx: b.x,
|
|
351
|
+
cy: b.y,
|
|
352
|
+
r: 5,
|
|
353
|
+
fill: drag === i ? STROKE[channel] : "#0f172a",
|
|
354
|
+
stroke: STROKE[channel],
|
|
355
|
+
strokeWidth: 2,
|
|
356
|
+
"aria-label": `Point ${p.input} to ${p.output}`
|
|
357
|
+
},
|
|
358
|
+
i
|
|
359
|
+
);
|
|
360
|
+
})
|
|
361
|
+
]
|
|
362
|
+
}
|
|
363
|
+
);
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
// src/image-editor/AdjustmentLayersPanel.tsx
|
|
367
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
368
|
+
var CHANNELS = [
|
|
369
|
+
{ id: "master", label: "RGB" },
|
|
370
|
+
{ id: "red", label: "Red" },
|
|
371
|
+
{ id: "green", label: "Green" },
|
|
372
|
+
{ id: "blue", label: "Blue" }
|
|
373
|
+
];
|
|
374
|
+
var TONES = [
|
|
375
|
+
{ id: "shadows", label: "Shadows" },
|
|
376
|
+
{ id: "midtones", label: "Midtones" },
|
|
377
|
+
{ id: "highlights", label: "Highlights" }
|
|
378
|
+
];
|
|
379
|
+
var selectClass = "bg-slate-800 border border-slate-700 rounded px-1.5 py-1 text-slate-200";
|
|
380
|
+
var chipClass = (on) => `px-2 py-0.5 rounded text-[10px] ${on ? "bg-indigo-600 text-white" : "bg-slate-800 text-slate-400 hover:text-slate-200"}`;
|
|
381
|
+
var HueRangeStrip = ({ bounds }) => {
|
|
382
|
+
const pts = Array.from({ length: 73 }, (_, i) => `${i / 72 * 360},${20 - (0, import_image_editor5.hueRangeWeight)({ bounds }, i * 5) * 18}`).join(" ");
|
|
383
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { viewBox: "0 0 360 20", preserveAspectRatio: "none", className: "w-full h-4 rounded", "data-testid": "hue-range-strip", children: [
|
|
384
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("linearGradient", { id: "pixerate-hue-strip", children: [0, 60, 120, 180, 240, 300, 360].map((h) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("stop", { offset: `${h / 360 * 100}%`, stopColor: `hsl(${h}, 90%, 50%)` }, h)) }) }),
|
|
385
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { width: 360, height: 20, fill: "url(#pixerate-hue-strip)", opacity: 0.6 }),
|
|
386
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("polyline", { points: pts, fill: "none", stroke: "#fff", strokeWidth: 1.5 })
|
|
387
|
+
] });
|
|
388
|
+
};
|
|
389
|
+
var AdjustmentLayersPanel = ({ controller, layers: layersProp, histogram }) => {
|
|
390
|
+
const follow = layersProp === void 0;
|
|
391
|
+
const [, setTick] = (0, import_react4.useState)(0);
|
|
392
|
+
(0, import_react4.useEffect)(() => follow ? controller.subscribe(() => setTick((n) => n + 1)) : void 0, [controller, follow]);
|
|
393
|
+
const layers = layersProp ?? controller.getAdjustmentLayers();
|
|
394
|
+
const [newKind, setNewKind] = (0, import_react4.useState)("curves");
|
|
395
|
+
const [selectedId, setSelectedId] = (0, import_react4.useState)(null);
|
|
396
|
+
const [channel, setChannel] = (0, import_react4.useState)("master");
|
|
397
|
+
const [range, setRange] = (0, import_react4.useState)(-1);
|
|
398
|
+
const [tone, setTone] = (0, import_react4.useState)("midtones");
|
|
399
|
+
const selected = layers.find((l) => l.id === selectedId) ?? null;
|
|
400
|
+
const topFirst = [...layers].reverse();
|
|
401
|
+
const setParam = (layer, path, value, commit = true) => controller.updateAdjustmentLayer(layer.id, { params: (0, import_image_editor5.setControlPath)(layer.params, path, value) }, commit);
|
|
402
|
+
const slider = (layer, spec, label) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
403
|
+
ControlSlider,
|
|
404
|
+
{
|
|
405
|
+
spec,
|
|
406
|
+
label,
|
|
407
|
+
value: Number((0, import_image_editor5.getControlPath)(layer.params, spec.id) ?? spec.default),
|
|
408
|
+
onChange: (v) => setParam(layer, spec.id, v)
|
|
409
|
+
},
|
|
410
|
+
spec.id
|
|
411
|
+
);
|
|
412
|
+
const controlsOf = (layer, filter = () => true) => import_image_editor5.ADJUSTMENT_LAYER_CONTROLS[layer.kind].filter(filter);
|
|
413
|
+
const checkbox = (layer, path, label) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "flex items-center gap-2 text-slate-300", children: [
|
|
414
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
415
|
+
"input",
|
|
416
|
+
{
|
|
417
|
+
type: "checkbox",
|
|
418
|
+
checked: Boolean((0, import_image_editor5.getControlPath)(layer.params, path)),
|
|
419
|
+
onChange: (e) => setParam(layer, path, e.target.checked),
|
|
420
|
+
className: "accent-indigo-500"
|
|
421
|
+
}
|
|
422
|
+
),
|
|
423
|
+
label
|
|
424
|
+
] });
|
|
425
|
+
const editor = (layer) => {
|
|
426
|
+
switch (layer.kind) {
|
|
427
|
+
case "curves":
|
|
428
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
429
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "flex gap-1", role: "group", "aria-label": "Curve channel", children: CHANNELS.map((c) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: chipClass(channel === c.id), onClick: () => setChannel(c.id), children: c.label }, c.id)) }),
|
|
430
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
431
|
+
CurvesEditor,
|
|
432
|
+
{
|
|
433
|
+
points: layer.params[channel],
|
|
434
|
+
channel,
|
|
435
|
+
histogram,
|
|
436
|
+
onChange: (points, commit) => setParam(layer, channel, points, commit)
|
|
437
|
+
}
|
|
438
|
+
)
|
|
439
|
+
] });
|
|
440
|
+
case "levels":
|
|
441
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
442
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "flex gap-1", role: "group", "aria-label": "Levels channel", children: CHANNELS.map((c) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: chipClass(channel === c.id), onClick: () => setChannel(c.id), children: c.label }, c.id)) }),
|
|
443
|
+
controlsOf(layer, (s) => s.id.startsWith(`${channel}.`)).map((s) => slider(layer, s))
|
|
444
|
+
] });
|
|
445
|
+
case "hueSaturation":
|
|
446
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
447
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-wrap gap-1", role: "group", "aria-label": "Hue range", children: [
|
|
448
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: chipClass(range === -1), onClick: () => setRange(-1), children: "Master" }),
|
|
449
|
+
import_image_editor5.HUE_RANGE_LABELS.map((name, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: chipClass(range === i), onClick: () => setRange(i), disabled: layer.params.colorize, children: name }, name))
|
|
450
|
+
] }),
|
|
451
|
+
range >= 0 && !layer.params.colorize && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(HueRangeStrip, { bounds: layer.params.ranges[range].bounds }),
|
|
452
|
+
controlsOf(layer, (s) => range === -1 || layer.params.colorize ? !s.id.startsWith("ranges.") : s.id.startsWith(`ranges.${range}.`)).map(
|
|
453
|
+
(s) => slider(layer, s)
|
|
454
|
+
),
|
|
455
|
+
range === -1 && checkbox(layer, "colorize", "Colorize")
|
|
456
|
+
] });
|
|
457
|
+
case "colorBalance":
|
|
458
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
459
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "flex gap-1", role: "group", "aria-label": "Tone", children: TONES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: chipClass(tone === t.id), onClick: () => setTone(t.id), children: t.label }, t.id)) }),
|
|
460
|
+
controlsOf(layer, (s) => s.id.startsWith(`${tone}.`)).map((s) => slider(layer, s)),
|
|
461
|
+
checkbox(layer, "preserveLuminosity", "Preserve luminosity")
|
|
462
|
+
] });
|
|
463
|
+
case "blackWhite":
|
|
464
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
465
|
+
controlsOf(layer).map((s) => slider(layer, s)),
|
|
466
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "flex items-center gap-2 text-slate-300", children: [
|
|
467
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
468
|
+
"input",
|
|
469
|
+
{
|
|
470
|
+
type: "checkbox",
|
|
471
|
+
checked: layer.params.tint !== null,
|
|
472
|
+
onChange: (e) => setParam(layer, "tint", e.target.checked ? "#e1d3b3" : null),
|
|
473
|
+
className: "accent-indigo-500"
|
|
474
|
+
}
|
|
475
|
+
),
|
|
476
|
+
"Tint",
|
|
477
|
+
layer.params.tint && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
478
|
+
"input",
|
|
479
|
+
{
|
|
480
|
+
type: "color",
|
|
481
|
+
"aria-label": "Tint colour",
|
|
482
|
+
value: layer.params.tint,
|
|
483
|
+
onChange: (e) => setParam(layer, "tint", e.target.value),
|
|
484
|
+
className: "h-5 w-8 bg-transparent"
|
|
485
|
+
}
|
|
486
|
+
)
|
|
487
|
+
] })
|
|
488
|
+
] });
|
|
489
|
+
case "photoFilter":
|
|
490
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
491
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "flex items-center gap-2 text-slate-300", children: [
|
|
492
|
+
"Filter colour",
|
|
493
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
494
|
+
"input",
|
|
495
|
+
{
|
|
496
|
+
type: "color",
|
|
497
|
+
"aria-label": "Filter colour",
|
|
498
|
+
value: layer.params.color,
|
|
499
|
+
onChange: (e) => setParam(layer, "color", e.target.value),
|
|
500
|
+
className: "h-5 w-8 bg-transparent"
|
|
501
|
+
}
|
|
502
|
+
)
|
|
503
|
+
] }),
|
|
504
|
+
controlsOf(layer).map((s) => slider(layer, s)),
|
|
505
|
+
checkbox(layer, "preserveLuminosity", "Preserve luminosity")
|
|
506
|
+
] });
|
|
507
|
+
case "invert":
|
|
508
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "text-slate-500 text-[11px]", children: "Invert has no settings." });
|
|
509
|
+
default:
|
|
510
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "flex flex-col gap-2", children: controlsOf(layer).map((s) => slider(layer, s)) });
|
|
511
|
+
}
|
|
512
|
+
};
|
|
513
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-col gap-2 text-xs", "data-testid": "adjustment-layers-panel", children: [
|
|
514
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Adjustment Layers" }) }),
|
|
515
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex gap-1.5", children: [
|
|
516
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
517
|
+
"select",
|
|
518
|
+
{
|
|
519
|
+
"aria-label": "New adjustment layer kind",
|
|
520
|
+
className: `${selectClass} flex-1`,
|
|
521
|
+
value: newKind,
|
|
522
|
+
onChange: (e) => setNewKind(e.target.value),
|
|
523
|
+
children: import_image_editor5.ADJUSTMENT_KINDS.map((k) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: k.kind, children: k.label }, k.kind))
|
|
524
|
+
}
|
|
525
|
+
),
|
|
526
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
527
|
+
"button",
|
|
528
|
+
{
|
|
529
|
+
type: "button",
|
|
530
|
+
className: "px-2 py-1 rounded bg-indigo-600 text-white hover:bg-indigo-500",
|
|
531
|
+
onClick: () => {
|
|
532
|
+
setSelectedId(controller.addAdjustmentLayer(newKind));
|
|
533
|
+
setChannel("master");
|
|
534
|
+
setRange(-1);
|
|
535
|
+
},
|
|
536
|
+
children: "Add Layer"
|
|
537
|
+
}
|
|
538
|
+
)
|
|
539
|
+
] }),
|
|
540
|
+
topFirst.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ul", { className: "flex flex-col gap-1", "aria-label": "Adjustment layers", children: topFirst.map((layer) => {
|
|
541
|
+
const index = layers.indexOf(layer);
|
|
542
|
+
const label = layer.name || (0, import_image_editor5.adjustmentKindLabel)(layer.kind);
|
|
543
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
544
|
+
"li",
|
|
545
|
+
{
|
|
546
|
+
className: `flex items-center gap-1.5 rounded px-1.5 py-1 ${layer.id === selectedId ? "bg-indigo-600/20 ring-1 ring-indigo-500/50" : "bg-slate-800/60"}`,
|
|
547
|
+
children: [
|
|
548
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
549
|
+
"input",
|
|
550
|
+
{
|
|
551
|
+
type: "checkbox",
|
|
552
|
+
"aria-label": `Show ${label}`,
|
|
553
|
+
checked: layer.enabled,
|
|
554
|
+
onChange: () => controller.toggleAdjustmentLayer(layer.id),
|
|
555
|
+
className: "accent-indigo-500"
|
|
556
|
+
}
|
|
557
|
+
),
|
|
558
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
559
|
+
"button",
|
|
560
|
+
{
|
|
561
|
+
type: "button",
|
|
562
|
+
className: `flex-1 text-left truncate ${layer.enabled ? "text-slate-200" : "text-slate-500 line-through"}`,
|
|
563
|
+
onClick: () => setSelectedId(layer.id === selectedId ? null : layer.id),
|
|
564
|
+
"aria-pressed": layer.id === selectedId,
|
|
565
|
+
children: label
|
|
566
|
+
}
|
|
567
|
+
),
|
|
568
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
569
|
+
"button",
|
|
570
|
+
{
|
|
571
|
+
type: "button",
|
|
572
|
+
"aria-label": `Move ${label} up`,
|
|
573
|
+
disabled: index === layers.length - 1,
|
|
574
|
+
onClick: () => controller.moveAdjustmentLayer(layer.id, index + 1),
|
|
575
|
+
className: "px-1 text-slate-400 hover:text-slate-200 disabled:opacity-30",
|
|
576
|
+
children: "\u2191"
|
|
577
|
+
}
|
|
578
|
+
),
|
|
579
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
580
|
+
"button",
|
|
581
|
+
{
|
|
582
|
+
type: "button",
|
|
583
|
+
"aria-label": `Move ${label} down`,
|
|
584
|
+
disabled: index === 0,
|
|
585
|
+
onClick: () => controller.moveAdjustmentLayer(layer.id, index - 1),
|
|
586
|
+
className: "px-1 text-slate-400 hover:text-slate-200 disabled:opacity-30",
|
|
587
|
+
children: "\u2193"
|
|
588
|
+
}
|
|
589
|
+
),
|
|
590
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
591
|
+
"button",
|
|
592
|
+
{
|
|
593
|
+
type: "button",
|
|
594
|
+
"aria-label": `Remove ${label}`,
|
|
595
|
+
onClick: () => {
|
|
596
|
+
controller.removeAdjustmentLayer(layer.id);
|
|
597
|
+
if (selectedId === layer.id) setSelectedId(null);
|
|
598
|
+
},
|
|
599
|
+
className: "px-1 text-rose-400 hover:text-rose-300",
|
|
600
|
+
children: "\u2715"
|
|
601
|
+
}
|
|
602
|
+
)
|
|
603
|
+
]
|
|
604
|
+
},
|
|
605
|
+
layer.id
|
|
606
|
+
);
|
|
607
|
+
}) }),
|
|
608
|
+
selected && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-col gap-2 rounded border border-slate-700/60 p-2", "data-testid": "adjustment-layer-editor", children: [
|
|
609
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
610
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "text-slate-400", children: "Blend" }),
|
|
611
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
612
|
+
"select",
|
|
613
|
+
{
|
|
614
|
+
"aria-label": "Blend mode",
|
|
615
|
+
className: `${selectClass} flex-1`,
|
|
616
|
+
value: selected.blend,
|
|
617
|
+
onChange: (e) => controller.updateAdjustmentLayer(selected.id, { blend: e.target.value }),
|
|
618
|
+
children: import_image_editor5.BLEND_MODES.map((m) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: m.id, children: m.label }, m.id))
|
|
619
|
+
}
|
|
620
|
+
)
|
|
621
|
+
] }),
|
|
622
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
623
|
+
ControlSlider,
|
|
624
|
+
{
|
|
625
|
+
spec: import_image_editor5.ADJUSTMENT_LAYER_OPACITY_CONTROL,
|
|
626
|
+
label: "Layer opacity",
|
|
627
|
+
value: selected.opacity,
|
|
628
|
+
onChange: (v) => controller.updateAdjustmentLayer(selected.id, { opacity: v })
|
|
629
|
+
}
|
|
630
|
+
),
|
|
631
|
+
editor(selected)
|
|
632
|
+
] })
|
|
633
|
+
] });
|
|
634
|
+
};
|
|
635
|
+
|
|
636
|
+
// src/image-editor/SelectionOptionsBar.tsx
|
|
637
|
+
var import_react5 = require("react");
|
|
638
|
+
var import_image_editor6 = require("@pixerate/editor/image-editor");
|
|
639
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
640
|
+
var MODIFY_OPS = [
|
|
641
|
+
{ op: "grow", label: "Grow" },
|
|
642
|
+
{ op: "shrink", label: "Shrink" },
|
|
643
|
+
{ op: "feather", label: "Feather" },
|
|
644
|
+
{ op: "border", label: "Border" },
|
|
645
|
+
{ op: "smooth", label: "Smooth" }
|
|
646
|
+
];
|
|
647
|
+
var btn = (on) => `px-2 py-1 rounded-md border text-[11px] transition-colors ${on ? "bg-indigo-600 border-indigo-500 text-white" : "bg-slate-950 border-slate-800 text-slate-300 hover:text-white"}`;
|
|
648
|
+
var SelectionOptionsBar = ({
|
|
649
|
+
controller,
|
|
650
|
+
options,
|
|
651
|
+
onOptionsChange,
|
|
652
|
+
selection,
|
|
653
|
+
quickMask,
|
|
654
|
+
className = ""
|
|
655
|
+
}) => {
|
|
656
|
+
const [radius, setRadius] = (0, import_react5.useState)(2);
|
|
657
|
+
const set = (patch) => onOptionsChange({ ...options, ...patch });
|
|
658
|
+
const sel = selection === void 0 ? controller.getSelection() : selection;
|
|
659
|
+
const qm = quickMask ?? Boolean(controller.getState().quickMask);
|
|
660
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { "data-testid": "selection-options-bar", className: `flex flex-col gap-3 text-xs ${className}`, children: [
|
|
661
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { role: "group", "aria-label": "Selection tool", className: "flex flex-wrap gap-1", children: import_image_editor6.SELECTION_TOOLS.map((t) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", "aria-pressed": options.tool === t.id, className: btn(options.tool === t.id), onClick: () => set({ tool: t.id }), children: t.label }, t.id)) }),
|
|
662
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { role: "group", "aria-label": "Selection mode", className: "flex flex-wrap gap-1", children: import_image_editor6.SELECTION_MODES.map((m) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", "aria-pressed": options.mode === m, className: btn(options.mode === m), onClick: () => set({ mode: m }), children: import_image_editor6.SELECTION_MODE_LABELS[m] }, m)) }),
|
|
663
|
+
options.tool === "wand" && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
664
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "flex flex-col gap-1", children: [
|
|
665
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "flex justify-between text-slate-400", children: [
|
|
666
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Tolerance" }),
|
|
667
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "font-mono", children: options.tolerance })
|
|
668
|
+
] }),
|
|
669
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
670
|
+
"input",
|
|
671
|
+
{
|
|
672
|
+
type: "range",
|
|
673
|
+
min: 0,
|
|
674
|
+
max: 255,
|
|
675
|
+
step: 1,
|
|
676
|
+
"aria-label": "Tolerance",
|
|
677
|
+
value: options.tolerance,
|
|
678
|
+
onChange: (e) => set({ tolerance: Number(e.target.value) }),
|
|
679
|
+
className: "w-full accent-indigo-500"
|
|
680
|
+
}
|
|
681
|
+
)
|
|
682
|
+
] }),
|
|
683
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "flex items-center gap-2 text-slate-300", children: [
|
|
684
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("input", { type: "checkbox", checked: options.contiguous, onChange: (e) => set({ contiguous: e.target.checked }) }),
|
|
685
|
+
"Contiguous"
|
|
686
|
+
] }),
|
|
687
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "flex items-center gap-2 text-slate-300", children: [
|
|
688
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("input", { type: "checkbox", checked: options.sampleMerged, onChange: (e) => set({ sampleMerged: e.target.checked }) }),
|
|
689
|
+
"Sample adjusted image"
|
|
690
|
+
] })
|
|
691
|
+
] }),
|
|
692
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "flex items-center gap-2 text-slate-300", children: [
|
|
693
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("input", { type: "checkbox", checked: options.antiAlias, onChange: (e) => set({ antiAlias: e.target.checked }) }),
|
|
694
|
+
"Anti-alias"
|
|
695
|
+
] }),
|
|
696
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-wrap gap-1", children: [
|
|
697
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: btn(false), onClick: () => controller.selectAll(), children: "Select All" }),
|
|
698
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: btn(false), disabled: !sel, onClick: () => controller.deselect(), children: "Deselect" }),
|
|
699
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: btn(false), onClick: () => controller.invertSelection(), children: "Inverse" }),
|
|
700
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", "aria-pressed": qm, className: btn(qm), onClick: () => controller.setQuickMask(!qm), children: "Quick Mask" })
|
|
701
|
+
] }),
|
|
702
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-wrap items-center gap-1", children: [
|
|
703
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "flex items-center gap-1 text-slate-400", children: [
|
|
704
|
+
"Radius",
|
|
705
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
706
|
+
"input",
|
|
707
|
+
{
|
|
708
|
+
type: "number",
|
|
709
|
+
min: 0,
|
|
710
|
+
max: 500,
|
|
711
|
+
step: 1,
|
|
712
|
+
"aria-label": "Modify radius",
|
|
713
|
+
value: radius,
|
|
714
|
+
onChange: (e) => setRadius(Math.max(0, Number(e.target.value) || 0)),
|
|
715
|
+
className: "w-14 bg-slate-950 border border-slate-800 rounded px-1 py-0.5 text-slate-200"
|
|
716
|
+
}
|
|
717
|
+
)
|
|
718
|
+
] }),
|
|
719
|
+
MODIFY_OPS.map(({ op, label }) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: btn(false), disabled: !sel, onClick: () => controller.modifySelection(op, radius), children: label }, op))
|
|
720
|
+
] }),
|
|
721
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { "data-testid": "selection-status", className: "text-slate-400", children: (0, import_image_editor6.describeSelection)(sel) })
|
|
722
|
+
] });
|
|
723
|
+
};
|
|
724
|
+
|
|
725
|
+
// src/image-editor/RefineEdgePanel.tsx
|
|
726
|
+
var import_react6 = require("react");
|
|
727
|
+
var import_image_editor7 = require("@pixerate/editor/image-editor");
|
|
728
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
729
|
+
var DEFAULT_PARAMS = { radius: 8, smartRadius: false, smooth: 0, feather: 0, contrast: 0, shiftEdge: 0 };
|
|
730
|
+
var button = "px-2 py-1 rounded-md border border-slate-800 bg-slate-950 text-[11px] text-slate-200 hover:text-white disabled:opacity-40";
|
|
731
|
+
var RefineEdgePanel = ({ controller, selection, layers, className = "" }) => {
|
|
732
|
+
const [params, setParams] = (0, import_react6.useState)(DEFAULT_PARAMS);
|
|
733
|
+
const [busy, setBusy] = (0, import_react6.useState)(false);
|
|
734
|
+
const [error, setError] = (0, import_react6.useState)(null);
|
|
735
|
+
const [layerId, setLayerId] = (0, import_react6.useState)("");
|
|
736
|
+
const [newKind, setNewKind] = (0, import_react6.useState)("hueSaturation");
|
|
737
|
+
const sel = selection === void 0 ? controller.getSelection() : selection;
|
|
738
|
+
const list = layers ?? controller.getAdjustmentLayers();
|
|
739
|
+
const target = list.find((l) => l.id === layerId) ?? list[list.length - 1];
|
|
740
|
+
const refine = async () => {
|
|
741
|
+
setBusy(true);
|
|
742
|
+
setError(null);
|
|
743
|
+
try {
|
|
744
|
+
await controller.refineSelection(params);
|
|
745
|
+
} catch (err) {
|
|
746
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
747
|
+
} finally {
|
|
748
|
+
setBusy(false);
|
|
749
|
+
}
|
|
750
|
+
};
|
|
751
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { "data-testid": "refine-edge-panel", className: `flex flex-col gap-3 text-xs ${className}`, children: [
|
|
752
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Refine Edge" }),
|
|
753
|
+
import_image_editor7.REFINE_EDGE_CONTROLS.map((c) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "flex flex-col gap-1", children: [
|
|
754
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "flex justify-between text-slate-400", children: [
|
|
755
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: c.label }),
|
|
756
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "font-mono", children: [
|
|
757
|
+
params[c.id],
|
|
758
|
+
c.unit
|
|
759
|
+
] })
|
|
760
|
+
] }),
|
|
761
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
762
|
+
"input",
|
|
763
|
+
{
|
|
764
|
+
type: "range",
|
|
765
|
+
min: c.min,
|
|
766
|
+
max: c.max,
|
|
767
|
+
step: c.step,
|
|
768
|
+
"aria-label": c.label,
|
|
769
|
+
value: params[c.id],
|
|
770
|
+
onChange: (e) => setParams({ ...params, [c.id]: Number(e.target.value) }),
|
|
771
|
+
className: "w-full accent-indigo-500"
|
|
772
|
+
}
|
|
773
|
+
)
|
|
774
|
+
] }, c.id)),
|
|
775
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "flex items-center gap-2 text-slate-300", children: [
|
|
776
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { type: "checkbox", checked: params.smartRadius, onChange: (e) => setParams({ ...params, smartRadius: e.target.checked }) }),
|
|
777
|
+
"Smart Radius"
|
|
778
|
+
] }),
|
|
779
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex gap-1", children: [
|
|
780
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: button, disabled: !sel || busy, onClick: refine, children: "Refine Edge" }),
|
|
781
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: button, onClick: () => setParams(DEFAULT_PARAMS), children: "Reset" })
|
|
782
|
+
] }),
|
|
783
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { role: "alert", className: "text-rose-400", children: error }),
|
|
784
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px] mt-2", children: "Layer Mask" }),
|
|
785
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex gap-1 items-center", children: [
|
|
786
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
787
|
+
"select",
|
|
788
|
+
{
|
|
789
|
+
"aria-label": "New masked layer kind",
|
|
790
|
+
value: newKind,
|
|
791
|
+
onChange: (e) => setNewKind(e.target.value),
|
|
792
|
+
className: "flex-1 bg-slate-950 border border-slate-800 rounded px-1 py-1 text-slate-200",
|
|
793
|
+
children: import_image_editor7.ADJUSTMENT_KINDS.map((k) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: k.kind, children: k.label }, k.kind))
|
|
794
|
+
}
|
|
795
|
+
),
|
|
796
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
797
|
+
"button",
|
|
798
|
+
{
|
|
799
|
+
type: "button",
|
|
800
|
+
className: button,
|
|
801
|
+
disabled: !sel,
|
|
802
|
+
onClick: () => sel && setLayerId(controller.addAdjustmentLayer({ kind: newKind, mask: (0, import_image_editor7.layerMaskFromSelection)(sel) })),
|
|
803
|
+
children: "New Masked Layer"
|
|
804
|
+
}
|
|
805
|
+
)
|
|
806
|
+
] }),
|
|
807
|
+
list.length > 0 && target && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
808
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
809
|
+
"select",
|
|
810
|
+
{
|
|
811
|
+
"aria-label": "Mask layer",
|
|
812
|
+
value: target.id,
|
|
813
|
+
onChange: (e) => setLayerId(e.target.value),
|
|
814
|
+
className: "bg-slate-950 border border-slate-800 rounded px-1 py-1 text-slate-200",
|
|
815
|
+
children: list.map((l) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("option", { value: l.id, children: [
|
|
816
|
+
l.name || (0, import_image_editor7.adjustmentKindLabel)(l.kind),
|
|
817
|
+
l.mask ? " (masked)" : ""
|
|
818
|
+
] }, l.id))
|
|
819
|
+
}
|
|
820
|
+
),
|
|
821
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex flex-wrap gap-1", children: [
|
|
822
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: button, disabled: !sel, onClick: () => controller.applySelectionAsLayerMask(target.id), children: "Mask with Selection" }),
|
|
823
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: button, disabled: !target.mask, onClick: () => controller.selectionFromLayerMask(target.id), children: "Load as Selection" }),
|
|
824
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: button, disabled: !target.mask, onClick: () => controller.setAdjustmentLayerMask(target.id, null), children: "Remove Mask" })
|
|
825
|
+
] }),
|
|
826
|
+
target.mask && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
827
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "flex flex-col gap-1", children: [
|
|
828
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "flex justify-between text-slate-400", children: [
|
|
829
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: "Density" }),
|
|
830
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "font-mono", children: [
|
|
831
|
+
Math.round((target.mask.density ?? 1) * 100),
|
|
832
|
+
"%"
|
|
833
|
+
] })
|
|
834
|
+
] }),
|
|
835
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
836
|
+
"input",
|
|
837
|
+
{
|
|
838
|
+
type: "range",
|
|
839
|
+
min: 0,
|
|
840
|
+
max: 100,
|
|
841
|
+
step: 1,
|
|
842
|
+
"aria-label": "Mask density",
|
|
843
|
+
value: Math.round((target.mask.density ?? 1) * 100),
|
|
844
|
+
onChange: (e) => controller.setAdjustmentLayerMaskProps(target.id, { density: Number(e.target.value) / 100 }),
|
|
845
|
+
className: "w-full accent-indigo-500"
|
|
846
|
+
}
|
|
847
|
+
)
|
|
848
|
+
] }),
|
|
849
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "flex flex-col gap-1", children: [
|
|
850
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "flex justify-between text-slate-400", children: [
|
|
851
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: "Mask Feather" }),
|
|
852
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "font-mono", children: [
|
|
853
|
+
target.mask.feather ?? 0,
|
|
854
|
+
"px"
|
|
855
|
+
] })
|
|
856
|
+
] }),
|
|
857
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
858
|
+
"input",
|
|
859
|
+
{
|
|
860
|
+
type: "range",
|
|
861
|
+
min: 0,
|
|
862
|
+
max: 50,
|
|
863
|
+
step: 0.5,
|
|
864
|
+
"aria-label": "Mask feather",
|
|
865
|
+
value: target.mask.feather ?? 0,
|
|
866
|
+
onChange: (e) => controller.setAdjustmentLayerMaskProps(target.id, { feather: Number(e.target.value) }),
|
|
867
|
+
className: "w-full accent-indigo-500"
|
|
868
|
+
}
|
|
869
|
+
)
|
|
870
|
+
] })
|
|
871
|
+
] })
|
|
872
|
+
] })
|
|
873
|
+
] });
|
|
874
|
+
};
|
|
875
|
+
|
|
876
|
+
// src/image-editor/ImageEditor.tsx
|
|
877
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
168
878
|
var ImageEditor = ({
|
|
169
879
|
editor: externalEditor,
|
|
170
880
|
src,
|
|
@@ -179,17 +889,26 @@ var ImageEditor = ({
|
|
|
179
889
|
const internalEditor = useImageEditor({ image: src });
|
|
180
890
|
const editor = externalEditor || internalEditor;
|
|
181
891
|
const { state, controller } = editor;
|
|
182
|
-
const legacyProcess = (0,
|
|
183
|
-
const canvasRef = (0,
|
|
184
|
-
const containerRef = (0,
|
|
185
|
-
const [activeTab, setActiveTab] = (0,
|
|
186
|
-
const [activeCropRatio, setActiveCropRatio] = (0,
|
|
187
|
-
const [
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
892
|
+
const legacyProcess = (0, import_image_editor8.normalizeProcessVersion)(state.adjustments.process) < import_image_editor8.LATEST_PROCESS_VERSION;
|
|
893
|
+
const canvasRef = (0, import_react7.useRef)(null);
|
|
894
|
+
const containerRef = (0, import_react7.useRef)(null);
|
|
895
|
+
const [activeTab, setActiveTab] = (0, import_react7.useState)("crop");
|
|
896
|
+
const [activeCropRatio, setActiveCropRatio] = (0, import_react7.useState)(null);
|
|
897
|
+
const [selectionOptions, setSelectionOptions] = (0, import_react7.useState)(import_image_editor8.DEFAULT_SELECTION_TOOL_OPTIONS);
|
|
898
|
+
const selectionOptionsRef = (0, import_react7.useRef)(selectionOptions);
|
|
899
|
+
selectionOptionsRef.current = selectionOptions;
|
|
900
|
+
const selectionGestureRef = (0, import_react7.useRef)(null);
|
|
901
|
+
if (!selectionGestureRef.current || selectionGestureRef.current.controller !== controller) {
|
|
902
|
+
selectionGestureRef.current = new import_image_editor8.SelectionGesture(controller, () => selectionOptionsRef.current);
|
|
903
|
+
}
|
|
904
|
+
const selectionDraftRef = (0, import_react7.useRef)(null);
|
|
905
|
+
const [selectionError, setSelectionError] = (0, import_react7.useState)(null);
|
|
906
|
+
const [inpaintPrompt, setInpaintPrompt] = (0, import_react7.useState)("");
|
|
907
|
+
const [inpaintMaskMode, setInpaintMaskMode] = (0, import_react7.useState)("annotations");
|
|
908
|
+
const [isRemovingBg, setIsRemovingBg] = (0, import_react7.useState)(false);
|
|
909
|
+
const [isInpainting, setIsInpainting] = (0, import_react7.useState)(false);
|
|
910
|
+
const [aiError, setAiError] = (0, import_react7.useState)(null);
|
|
911
|
+
const [aiSuccess, setAiSuccess] = (0, import_react7.useState)(null);
|
|
193
912
|
const effectiveOnInpaint = onInpaint || aiHooks?.onInpaint;
|
|
194
913
|
const effectiveOnRemoveBg = onRemoveBackground || aiHooks?.onRemoveBackground;
|
|
195
914
|
const handleRemoveBackground = async () => {
|
|
@@ -251,18 +970,18 @@ var ImageEditor = ({
|
|
|
251
970
|
setIsInpainting(false);
|
|
252
971
|
}
|
|
253
972
|
};
|
|
254
|
-
const [activeColor, setActiveColor] = (0,
|
|
255
|
-
const [strokeWidth, setStrokeWidth] = (0,
|
|
256
|
-
const [fontSize, setFontSize] = (0,
|
|
257
|
-
const [textInput, setTextInput] = (0,
|
|
258
|
-
const draftAnnotationRef = (0,
|
|
259
|
-
const draftCropRef = (0,
|
|
260
|
-
const cropDragInfoRef = (0,
|
|
261
|
-
const annotationDragInfoRef = (0,
|
|
262
|
-
const isDrawingRef = (0,
|
|
263
|
-
const currentPathRef = (0,
|
|
264
|
-
const dragStartPointRef = (0,
|
|
265
|
-
const renderCanvas = (0,
|
|
973
|
+
const [activeColor, setActiveColor] = (0, import_react7.useState)("#ef4444");
|
|
974
|
+
const [strokeWidth, setStrokeWidth] = (0, import_react7.useState)(3);
|
|
975
|
+
const [fontSize, setFontSize] = (0, import_react7.useState)(24);
|
|
976
|
+
const [textInput, setTextInput] = (0, import_react7.useState)("");
|
|
977
|
+
const draftAnnotationRef = (0, import_react7.useRef)(null);
|
|
978
|
+
const draftCropRef = (0, import_react7.useRef)(null);
|
|
979
|
+
const cropDragInfoRef = (0, import_react7.useRef)(null);
|
|
980
|
+
const annotationDragInfoRef = (0, import_react7.useRef)(null);
|
|
981
|
+
const isDrawingRef = (0, import_react7.useRef)(false);
|
|
982
|
+
const currentPathRef = (0, import_react7.useRef)([]);
|
|
983
|
+
const dragStartPointRef = (0, import_react7.useRef)(null);
|
|
984
|
+
const renderCanvas = (0, import_react7.useCallback)(
|
|
266
985
|
async (draftAnnotation, draftCrop) => {
|
|
267
986
|
const canvas = canvasRef.current;
|
|
268
987
|
const container = containerRef.current;
|
|
@@ -276,12 +995,13 @@ var ImageEditor = ({
|
|
|
276
995
|
showSelectionOverlay: true,
|
|
277
996
|
interactive: true,
|
|
278
997
|
draftAnnotation: draftAnnotation !== void 0 ? draftAnnotation : draftAnnotationRef.current,
|
|
279
|
-
draftCrop: draftCrop !== void 0 ? draftCrop : draftCropRef.current
|
|
998
|
+
draftCrop: draftCrop !== void 0 ? draftCrop : draftCropRef.current,
|
|
999
|
+
draftSelectionPath: selectionDraftRef.current
|
|
280
1000
|
});
|
|
281
1001
|
},
|
|
282
1002
|
[controller, activeTab]
|
|
283
1003
|
);
|
|
284
|
-
(0,
|
|
1004
|
+
(0, import_react7.useEffect)(() => {
|
|
285
1005
|
const raf = typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : (cb) => setTimeout(cb, 0);
|
|
286
1006
|
const caf = typeof cancelAnimationFrame !== "undefined" ? cancelAnimationFrame : (id) => clearTimeout(id);
|
|
287
1007
|
const animId = raf(() => {
|
|
@@ -289,12 +1009,12 @@ var ImageEditor = ({
|
|
|
289
1009
|
});
|
|
290
1010
|
return () => caf(animId);
|
|
291
1011
|
}, [renderCanvas, state]);
|
|
292
|
-
(0,
|
|
1012
|
+
(0, import_react7.useEffect)(() => {
|
|
293
1013
|
if (activeTab === "crop" && editor.state.activeTool !== "crop") {
|
|
294
1014
|
editor.setTool("crop");
|
|
295
1015
|
}
|
|
296
1016
|
}, [activeTab, editor]);
|
|
297
|
-
(0,
|
|
1017
|
+
(0, import_react7.useEffect)(() => {
|
|
298
1018
|
const container = containerRef.current;
|
|
299
1019
|
if (!container) return;
|
|
300
1020
|
if (typeof ResizeObserver !== "undefined") {
|
|
@@ -324,6 +1044,12 @@ var ImageEditor = ({
|
|
|
324
1044
|
const canvas = canvasRef.current;
|
|
325
1045
|
if (!canvas) return;
|
|
326
1046
|
const canvasPt = getCanvasEventPoint(e);
|
|
1047
|
+
if (activeTab === "select") {
|
|
1048
|
+
const gesture = selectionGestureRef.current;
|
|
1049
|
+
const imgPt = (0, import_image_editor8.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
|
|
1050
|
+
void gesture.begin(imgPt, e).then(() => setSelectionError(gesture.error));
|
|
1051
|
+
return;
|
|
1052
|
+
}
|
|
327
1053
|
if (activeTab === "crop") {
|
|
328
1054
|
const activeCrop = draftCropRef.current || state.crop || {
|
|
329
1055
|
x: 0,
|
|
@@ -331,8 +1057,8 @@ var ImageEditor = ({
|
|
|
331
1057
|
width: state.imageDimensions.width,
|
|
332
1058
|
height: state.imageDimensions.height
|
|
333
1059
|
};
|
|
334
|
-
const handle = (0,
|
|
335
|
-
const startImgPt = (0,
|
|
1060
|
+
const handle = (0, import_image_editor8.hitTestCrop)(canvasPt, state, canvas.width, canvas.height, 14, activeCrop);
|
|
1061
|
+
const startImgPt = (0, import_image_editor8.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, true);
|
|
336
1062
|
if (handle) {
|
|
337
1063
|
cropDragInfoRef.current = {
|
|
338
1064
|
handle,
|
|
@@ -351,12 +1077,12 @@ var ImageEditor = ({
|
|
|
351
1077
|
return;
|
|
352
1078
|
}
|
|
353
1079
|
if (activeTab === "annotate") {
|
|
354
|
-
const imgPt = (0,
|
|
1080
|
+
const imgPt = (0, import_image_editor8.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
|
|
355
1081
|
dragStartPointRef.current = imgPt;
|
|
356
1082
|
if (state.activeTool === "select") {
|
|
357
1083
|
for (let i = state.annotations.length - 1; i >= 0; i--) {
|
|
358
1084
|
const ann = state.annotations[i];
|
|
359
|
-
if ((0,
|
|
1085
|
+
if ((0, import_image_editor8.hitTestAnnotation)(imgPt, ann)) {
|
|
360
1086
|
editor.selectAnnotation(ann.id);
|
|
361
1087
|
annotationDragInfoRef.current = {
|
|
362
1088
|
annId: ann.id,
|
|
@@ -390,10 +1116,18 @@ var ImageEditor = ({
|
|
|
390
1116
|
const canvas = canvasRef.current;
|
|
391
1117
|
if (!canvas) return;
|
|
392
1118
|
const canvasPt = getCanvasEventPoint(e);
|
|
1119
|
+
if (activeTab === "select") {
|
|
1120
|
+
const gesture = selectionGestureRef.current;
|
|
1121
|
+
if (gesture.active && gesture.move((0, import_image_editor8.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false))) {
|
|
1122
|
+
selectionDraftRef.current = gesture.draftPath;
|
|
1123
|
+
renderCanvas();
|
|
1124
|
+
}
|
|
1125
|
+
return;
|
|
1126
|
+
}
|
|
393
1127
|
if (activeTab === "crop") {
|
|
394
1128
|
const drag = cropDragInfoRef.current;
|
|
395
1129
|
if (drag) {
|
|
396
|
-
const currentImgPt = (0,
|
|
1130
|
+
const currentImgPt = (0, import_image_editor8.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, true);
|
|
397
1131
|
let newCrop;
|
|
398
1132
|
if (drag.handle === "new") {
|
|
399
1133
|
const minX = Math.max(0, Math.min(drag.startImgPt.x, currentImgPt.x));
|
|
@@ -407,7 +1141,7 @@ var ImageEditor = ({
|
|
|
407
1141
|
height: Math.round(Math.max(20, maxY - minY))
|
|
408
1142
|
};
|
|
409
1143
|
} else {
|
|
410
|
-
newCrop = (0,
|
|
1144
|
+
newCrop = (0, import_image_editor8.calculateCropDrag)(
|
|
411
1145
|
drag.startCrop,
|
|
412
1146
|
drag.handle,
|
|
413
1147
|
drag.startImgPt,
|
|
@@ -427,15 +1161,15 @@ var ImageEditor = ({
|
|
|
427
1161
|
width: state.imageDimensions.width,
|
|
428
1162
|
height: state.imageDimensions.height
|
|
429
1163
|
};
|
|
430
|
-
const handle = (0,
|
|
431
|
-
canvas.style.cursor = (0,
|
|
1164
|
+
const handle = (0, import_image_editor8.hitTestCrop)(canvasPt, state, canvas.width, canvas.height, 14, activeCrop);
|
|
1165
|
+
canvas.style.cursor = (0, import_image_editor8.getCropCursor)(handle, state.transform);
|
|
432
1166
|
}
|
|
433
1167
|
return;
|
|
434
1168
|
}
|
|
435
1169
|
if (activeTab === "annotate") {
|
|
436
1170
|
if (annotationDragInfoRef.current) {
|
|
437
1171
|
const drag = annotationDragInfoRef.current;
|
|
438
|
-
const currentImgPt2 = (0,
|
|
1172
|
+
const currentImgPt2 = (0, import_image_editor8.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
|
|
439
1173
|
const dx = currentImgPt2.x - drag.startImgPt.x;
|
|
440
1174
|
const dy = currentImgPt2.y - drag.startImgPt.y;
|
|
441
1175
|
editor.updateAnnotation(drag.annId, {
|
|
@@ -447,10 +1181,10 @@ var ImageEditor = ({
|
|
|
447
1181
|
const startPt = dragStartPointRef.current;
|
|
448
1182
|
if (!startPt) {
|
|
449
1183
|
if (state.activeTool === "select") {
|
|
450
|
-
const imgPt = (0,
|
|
1184
|
+
const imgPt = (0, import_image_editor8.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
|
|
451
1185
|
let hovered = false;
|
|
452
1186
|
for (let i = state.annotations.length - 1; i >= 0; i--) {
|
|
453
|
-
if ((0,
|
|
1187
|
+
if ((0, import_image_editor8.hitTestAnnotation)(imgPt, state.annotations[i])) {
|
|
454
1188
|
hovered = true;
|
|
455
1189
|
break;
|
|
456
1190
|
}
|
|
@@ -461,7 +1195,7 @@ var ImageEditor = ({
|
|
|
461
1195
|
}
|
|
462
1196
|
return;
|
|
463
1197
|
}
|
|
464
|
-
const currentImgPt = (0,
|
|
1198
|
+
const currentImgPt = (0, import_image_editor8.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
|
|
465
1199
|
if (state.activeTool === "pen" && isDrawingRef.current) {
|
|
466
1200
|
currentPathRef.current.push(currentImgPt);
|
|
467
1201
|
const draft = {
|
|
@@ -538,6 +1272,15 @@ var ImageEditor = ({
|
|
|
538
1272
|
const canvas = canvasRef.current;
|
|
539
1273
|
if (!canvas) return;
|
|
540
1274
|
const canvasPt = getCanvasEventPoint(e);
|
|
1275
|
+
if (activeTab === "select") {
|
|
1276
|
+
const gesture = selectionGestureRef.current;
|
|
1277
|
+
if (gesture.active) {
|
|
1278
|
+
gesture.end((0, import_image_editor8.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false), e);
|
|
1279
|
+
selectionDraftRef.current = null;
|
|
1280
|
+
renderCanvas();
|
|
1281
|
+
}
|
|
1282
|
+
return;
|
|
1283
|
+
}
|
|
541
1284
|
if (activeTab === "crop") {
|
|
542
1285
|
if (cropDragInfoRef.current) {
|
|
543
1286
|
if (draftCropRef.current && draftCropRef.current.width >= 20 && draftCropRef.current.height >= 20) {
|
|
@@ -556,7 +1299,7 @@ var ImageEditor = ({
|
|
|
556
1299
|
}
|
|
557
1300
|
const startPt = dragStartPointRef.current;
|
|
558
1301
|
if (startPt) {
|
|
559
|
-
const currentImgPt = (0,
|
|
1302
|
+
const currentImgPt = (0, import_image_editor8.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
|
|
560
1303
|
if (state.activeTool === "pen" && isDrawingRef.current) {
|
|
561
1304
|
if (currentPathRef.current.length > 1) {
|
|
562
1305
|
editor.addAnnotation({
|
|
@@ -652,15 +1395,15 @@ var ImageEditor = ({
|
|
|
652
1395
|
link.click();
|
|
653
1396
|
}
|
|
654
1397
|
};
|
|
655
|
-
return /* @__PURE__ */ (0,
|
|
1398
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
656
1399
|
"div",
|
|
657
1400
|
{
|
|
658
1401
|
className: `flex flex-col bg-slate-900 border border-slate-800 rounded-xl overflow-hidden shadow-2xl text-slate-100 ${className}`,
|
|
659
1402
|
style,
|
|
660
1403
|
children: [
|
|
661
|
-
/* @__PURE__ */ (0,
|
|
662
|
-
/* @__PURE__ */ (0,
|
|
663
|
-
/* @__PURE__ */ (0,
|
|
1404
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.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: [
|
|
1405
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center gap-1 bg-slate-900 p-1 rounded-lg border border-slate-800 text-xs", children: [
|
|
1406
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
664
1407
|
"button",
|
|
665
1408
|
{
|
|
666
1409
|
type: "button",
|
|
@@ -672,7 +1415,7 @@ var ImageEditor = ({
|
|
|
672
1415
|
children: "Transform & Crop"
|
|
673
1416
|
}
|
|
674
1417
|
),
|
|
675
|
-
/* @__PURE__ */ (0,
|
|
1418
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
676
1419
|
"button",
|
|
677
1420
|
{
|
|
678
1421
|
type: "button",
|
|
@@ -684,7 +1427,19 @@ var ImageEditor = ({
|
|
|
684
1427
|
children: "Adjustments"
|
|
685
1428
|
}
|
|
686
1429
|
),
|
|
687
|
-
/* @__PURE__ */ (0,
|
|
1430
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1431
|
+
"button",
|
|
1432
|
+
{
|
|
1433
|
+
type: "button",
|
|
1434
|
+
onClick: () => {
|
|
1435
|
+
setActiveTab("select");
|
|
1436
|
+
editor.setTool("select");
|
|
1437
|
+
},
|
|
1438
|
+
className: `px-3 py-1.5 rounded-md font-medium transition-colors ${activeTab === "select" ? "bg-indigo-600 text-white shadow" : "text-slate-400 hover:text-slate-200"}`,
|
|
1439
|
+
children: "Select & Mask"
|
|
1440
|
+
}
|
|
1441
|
+
),
|
|
1442
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
688
1443
|
"button",
|
|
689
1444
|
{
|
|
690
1445
|
type: "button",
|
|
@@ -696,7 +1451,7 @@ var ImageEditor = ({
|
|
|
696
1451
|
children: "Annotations"
|
|
697
1452
|
}
|
|
698
1453
|
),
|
|
699
|
-
/* @__PURE__ */ (0,
|
|
1454
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
700
1455
|
"button",
|
|
701
1456
|
{
|
|
702
1457
|
type: "button",
|
|
@@ -708,7 +1463,7 @@ var ImageEditor = ({
|
|
|
708
1463
|
children: "Depth Mask"
|
|
709
1464
|
}
|
|
710
1465
|
),
|
|
711
|
-
/* @__PURE__ */ (0,
|
|
1466
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
712
1467
|
"button",
|
|
713
1468
|
{
|
|
714
1469
|
type: "button",
|
|
@@ -718,14 +1473,14 @@ var ImageEditor = ({
|
|
|
718
1473
|
},
|
|
719
1474
|
className: `px-3 py-1.5 rounded-md font-medium transition-colors flex items-center gap-1.5 ${activeTab === "ai" ? "bg-indigo-600 text-white shadow" : "text-slate-400 hover:text-slate-200"}`,
|
|
720
1475
|
children: [
|
|
721
|
-
/* @__PURE__ */ (0,
|
|
1476
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("svg", { className: "w-3.5 h-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: "M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83" }) }),
|
|
722
1477
|
"AI Tools"
|
|
723
1478
|
]
|
|
724
1479
|
}
|
|
725
1480
|
)
|
|
726
1481
|
] }),
|
|
727
|
-
/* @__PURE__ */ (0,
|
|
728
|
-
/* @__PURE__ */ (0,
|
|
1482
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center gap-2 text-xs", children: [
|
|
1483
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
729
1484
|
"button",
|
|
730
1485
|
{
|
|
731
1486
|
type: "button",
|
|
@@ -736,7 +1491,7 @@ var ImageEditor = ({
|
|
|
736
1491
|
children: "\u21A9"
|
|
737
1492
|
}
|
|
738
1493
|
),
|
|
739
|
-
/* @__PURE__ */ (0,
|
|
1494
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
740
1495
|
"button",
|
|
741
1496
|
{
|
|
742
1497
|
type: "button",
|
|
@@ -747,7 +1502,7 @@ var ImageEditor = ({
|
|
|
747
1502
|
children: "\u21AA"
|
|
748
1503
|
}
|
|
749
1504
|
),
|
|
750
|
-
/* @__PURE__ */ (0,
|
|
1505
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
751
1506
|
"button",
|
|
752
1507
|
{
|
|
753
1508
|
type: "button",
|
|
@@ -756,8 +1511,8 @@ var ImageEditor = ({
|
|
|
756
1511
|
children: "Reset"
|
|
757
1512
|
}
|
|
758
1513
|
),
|
|
759
|
-
/* @__PURE__ */ (0,
|
|
760
|
-
/* @__PURE__ */ (0,
|
|
1514
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "h-4 w-px bg-slate-800 mx-1" }),
|
|
1515
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
761
1516
|
"button",
|
|
762
1517
|
{
|
|
763
1518
|
type: "button",
|
|
@@ -767,11 +1522,11 @@ var ImageEditor = ({
|
|
|
767
1522
|
children: "-"
|
|
768
1523
|
}
|
|
769
1524
|
),
|
|
770
|
-
/* @__PURE__ */ (0,
|
|
1525
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "font-mono text-slate-400 w-10 text-center", children: [
|
|
771
1526
|
Math.round(state.zoom * 100),
|
|
772
1527
|
"%"
|
|
773
1528
|
] }),
|
|
774
|
-
/* @__PURE__ */ (0,
|
|
1529
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
775
1530
|
"button",
|
|
776
1531
|
{
|
|
777
1532
|
type: "button",
|
|
@@ -781,8 +1536,8 @@ var ImageEditor = ({
|
|
|
781
1536
|
children: "+"
|
|
782
1537
|
}
|
|
783
1538
|
),
|
|
784
|
-
/* @__PURE__ */ (0,
|
|
785
|
-
/* @__PURE__ */ (0,
|
|
1539
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "h-4 w-px bg-slate-800 mx-1" }),
|
|
1540
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
786
1541
|
"button",
|
|
787
1542
|
{
|
|
788
1543
|
type: "button",
|
|
@@ -793,7 +1548,7 @@ var ImageEditor = ({
|
|
|
793
1548
|
children: "Hold Compare"
|
|
794
1549
|
}
|
|
795
1550
|
),
|
|
796
|
-
/* @__PURE__ */ (0,
|
|
1551
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
797
1552
|
"button",
|
|
798
1553
|
{
|
|
799
1554
|
type: "button",
|
|
@@ -804,13 +1559,13 @@ var ImageEditor = ({
|
|
|
804
1559
|
)
|
|
805
1560
|
] })
|
|
806
1561
|
] }),
|
|
807
|
-
/* @__PURE__ */ (0,
|
|
808
|
-
/* @__PURE__ */ (0,
|
|
1562
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-col md:flex-row flex-1 min-h-[520px] md:h-[560px]", children: [
|
|
1563
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
809
1564
|
"div",
|
|
810
1565
|
{
|
|
811
1566
|
ref: containerRef,
|
|
812
1567
|
className: "relative flex-1 h-[360px] md:h-full min-h-[320px] bg-slate-950 overflow-hidden select-none",
|
|
813
|
-
children: /* @__PURE__ */ (0,
|
|
1568
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
814
1569
|
"canvas",
|
|
815
1570
|
{
|
|
816
1571
|
ref: canvasRef,
|
|
@@ -823,11 +1578,11 @@ var ImageEditor = ({
|
|
|
823
1578
|
)
|
|
824
1579
|
}
|
|
825
1580
|
),
|
|
826
|
-
/* @__PURE__ */ (0,
|
|
827
|
-
activeTab === "crop" && /* @__PURE__ */ (0,
|
|
828
|
-
/* @__PURE__ */ (0,
|
|
829
|
-
/* @__PURE__ */ (0,
|
|
830
|
-
/* @__PURE__ */ (0,
|
|
1581
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.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: [
|
|
1582
|
+
activeTab === "crop" && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-col gap-4 text-xs", children: [
|
|
1583
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Aspect Ratio Presets" }),
|
|
1584
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "grid grid-cols-3 gap-2", children: [
|
|
1585
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
831
1586
|
"button",
|
|
832
1587
|
{
|
|
833
1588
|
type: "button",
|
|
@@ -839,7 +1594,7 @@ var ImageEditor = ({
|
|
|
839
1594
|
children: "Freeform"
|
|
840
1595
|
}
|
|
841
1596
|
),
|
|
842
|
-
/* @__PURE__ */ (0,
|
|
1597
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
843
1598
|
"button",
|
|
844
1599
|
{
|
|
845
1600
|
type: "button",
|
|
@@ -851,7 +1606,7 @@ var ImageEditor = ({
|
|
|
851
1606
|
children: "1:1 Square"
|
|
852
1607
|
}
|
|
853
1608
|
),
|
|
854
|
-
/* @__PURE__ */ (0,
|
|
1609
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
855
1610
|
"button",
|
|
856
1611
|
{
|
|
857
1612
|
type: "button",
|
|
@@ -863,7 +1618,7 @@ var ImageEditor = ({
|
|
|
863
1618
|
children: "4:3 Standard"
|
|
864
1619
|
}
|
|
865
1620
|
),
|
|
866
|
-
/* @__PURE__ */ (0,
|
|
1621
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
867
1622
|
"button",
|
|
868
1623
|
{
|
|
869
1624
|
type: "button",
|
|
@@ -875,7 +1630,7 @@ var ImageEditor = ({
|
|
|
875
1630
|
children: "16:9 Wide"
|
|
876
1631
|
}
|
|
877
1632
|
),
|
|
878
|
-
/* @__PURE__ */ (0,
|
|
1633
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
879
1634
|
"button",
|
|
880
1635
|
{
|
|
881
1636
|
type: "button",
|
|
@@ -887,7 +1642,7 @@ var ImageEditor = ({
|
|
|
887
1642
|
children: "3:2 Photo"
|
|
888
1643
|
}
|
|
889
1644
|
),
|
|
890
|
-
/* @__PURE__ */ (0,
|
|
1645
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
891
1646
|
"button",
|
|
892
1647
|
{
|
|
893
1648
|
type: "button",
|
|
@@ -900,10 +1655,10 @@ var ImageEditor = ({
|
|
|
900
1655
|
}
|
|
901
1656
|
)
|
|
902
1657
|
] }),
|
|
903
|
-
/* @__PURE__ */ (0,
|
|
904
|
-
/* @__PURE__ */ (0,
|
|
905
|
-
/* @__PURE__ */ (0,
|
|
906
|
-
/* @__PURE__ */ (0,
|
|
1658
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "h-px bg-slate-800 my-1" }),
|
|
1659
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Orientation & Flips" }),
|
|
1660
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
1661
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
907
1662
|
"button",
|
|
908
1663
|
{
|
|
909
1664
|
type: "button",
|
|
@@ -912,7 +1667,7 @@ var ImageEditor = ({
|
|
|
912
1667
|
children: "Rotate -90\xB0"
|
|
913
1668
|
}
|
|
914
1669
|
),
|
|
915
|
-
/* @__PURE__ */ (0,
|
|
1670
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
916
1671
|
"button",
|
|
917
1672
|
{
|
|
918
1673
|
type: "button",
|
|
@@ -921,7 +1676,7 @@ var ImageEditor = ({
|
|
|
921
1676
|
children: "Rotate +90\xB0"
|
|
922
1677
|
}
|
|
923
1678
|
),
|
|
924
|
-
/* @__PURE__ */ (0,
|
|
1679
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
925
1680
|
"button",
|
|
926
1681
|
{
|
|
927
1682
|
type: "button",
|
|
@@ -930,7 +1685,7 @@ var ImageEditor = ({
|
|
|
930
1685
|
children: "Flip Horizontal"
|
|
931
1686
|
}
|
|
932
1687
|
),
|
|
933
|
-
/* @__PURE__ */ (0,
|
|
1688
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
934
1689
|
"button",
|
|
935
1690
|
{
|
|
936
1691
|
type: "button",
|
|
@@ -941,10 +1696,10 @@ var ImageEditor = ({
|
|
|
941
1696
|
)
|
|
942
1697
|
] })
|
|
943
1698
|
] }),
|
|
944
|
-
activeTab === "adjust" && /* @__PURE__ */ (0,
|
|
945
|
-
/* @__PURE__ */ (0,
|
|
946
|
-
/* @__PURE__ */ (0,
|
|
947
|
-
/* @__PURE__ */ (0,
|
|
1699
|
+
activeTab === "adjust" && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-col gap-3 text-xs", children: [
|
|
1700
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
1701
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Color Adjustments" }),
|
|
1702
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
948
1703
|
"button",
|
|
949
1704
|
{
|
|
950
1705
|
type: "button",
|
|
@@ -954,174 +1709,49 @@ var ImageEditor = ({
|
|
|
954
1709
|
}
|
|
955
1710
|
)
|
|
956
1711
|
] }),
|
|
957
|
-
/* @__PURE__ */ (0,
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
] }),
|
|
962
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
963
|
-
"input",
|
|
964
|
-
{
|
|
965
|
-
type: "range",
|
|
966
|
-
min: "-100",
|
|
967
|
-
max: "100",
|
|
968
|
-
value: state.adjustments.brightness,
|
|
969
|
-
onChange: (e) => editor.setAdjustments({ brightness: Number(e.target.value) }),
|
|
970
|
-
className: "w-full accent-indigo-500"
|
|
971
|
-
}
|
|
972
|
-
)
|
|
973
|
-
] }),
|
|
974
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
975
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
976
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Contrast" }),
|
|
977
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-mono", children: state.adjustments.contrast })
|
|
978
|
-
] }),
|
|
979
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
980
|
-
"input",
|
|
981
|
-
{
|
|
982
|
-
type: "range",
|
|
983
|
-
min: "-100",
|
|
984
|
-
max: "100",
|
|
985
|
-
value: state.adjustments.contrast,
|
|
986
|
-
onChange: (e) => editor.setAdjustments({ contrast: Number(e.target.value) }),
|
|
987
|
-
className: "w-full accent-indigo-500"
|
|
988
|
-
}
|
|
989
|
-
)
|
|
990
|
-
] }),
|
|
991
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
992
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
993
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Saturation" }),
|
|
994
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-mono", children: state.adjustments.saturation })
|
|
995
|
-
] }),
|
|
996
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
997
|
-
"input",
|
|
998
|
-
{
|
|
999
|
-
type: "range",
|
|
1000
|
-
min: "-100",
|
|
1001
|
-
max: "100",
|
|
1002
|
-
value: state.adjustments.saturation,
|
|
1003
|
-
onChange: (e) => editor.setAdjustments({ saturation: Number(e.target.value) }),
|
|
1004
|
-
className: "w-full accent-indigo-500"
|
|
1005
|
-
}
|
|
1006
|
-
)
|
|
1007
|
-
] }),
|
|
1008
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
1009
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
1010
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Exposure" }),
|
|
1011
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-mono", children: state.adjustments.exposure })
|
|
1012
|
-
] }),
|
|
1013
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1014
|
-
"input",
|
|
1712
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Histogram, { histogram: editor.histogram }),
|
|
1713
|
+
import_image_editor8.IMAGE_ADJUSTMENT_CONTROLS.map((spec) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react7.default.Fragment, { children: [
|
|
1714
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1715
|
+
ControlSlider,
|
|
1015
1716
|
{
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
onChange: (e) => editor.setAdjustments({ exposure: Number(e.target.value) }),
|
|
1021
|
-
className: "w-full accent-indigo-500"
|
|
1717
|
+
spec,
|
|
1718
|
+
value: (0, import_image_editor8.readAdjustmentControls)(state.adjustments)[spec.id],
|
|
1719
|
+
disabled: spec.requiresProcess !== void 0 && legacyProcess,
|
|
1720
|
+
onChange: (v) => editor.setAdjustments({ [spec.id]: v })
|
|
1022
1721
|
}
|
|
1023
|
-
)
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
1027
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Warmth / Temperature" }),
|
|
1028
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-mono", children: state.adjustments.temperature })
|
|
1029
|
-
] }),
|
|
1030
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1031
|
-
"input",
|
|
1032
|
-
{
|
|
1033
|
-
type: "range",
|
|
1034
|
-
min: "-100",
|
|
1035
|
-
max: "100",
|
|
1036
|
-
value: state.adjustments.temperature,
|
|
1037
|
-
onChange: (e) => editor.setAdjustments({ temperature: Number(e.target.value) }),
|
|
1038
|
-
className: "w-full accent-indigo-500"
|
|
1039
|
-
}
|
|
1040
|
-
)
|
|
1041
|
-
] }),
|
|
1042
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
1043
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
1044
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Tint" }),
|
|
1045
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-mono", children: state.adjustments.tint ?? 0 })
|
|
1046
|
-
] }),
|
|
1047
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1048
|
-
"input",
|
|
1722
|
+
),
|
|
1723
|
+
spec.id === "tint" && legacyProcess && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
1724
|
+
"div",
|
|
1049
1725
|
{
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1726
|
+
className: "flex items-center justify-between gap-2 rounded border border-amber-500/30 bg-amber-500/10 px-2 py-1.5 text-[10px] text-amber-200",
|
|
1727
|
+
"data-testid": "image-editor-process-notice",
|
|
1728
|
+
children: [
|
|
1729
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: "Legacy rendering (process 1). Tint needs the current process." }),
|
|
1730
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1731
|
+
"button",
|
|
1732
|
+
{
|
|
1733
|
+
type: "button",
|
|
1734
|
+
onClick: () => editor.upgradeProcess(),
|
|
1735
|
+
className: "shrink-0 rounded bg-amber-500/20 px-2 py-0.5 hover:bg-amber-500/30",
|
|
1736
|
+
children: "Update"
|
|
1737
|
+
}
|
|
1738
|
+
)
|
|
1739
|
+
]
|
|
1058
1740
|
}
|
|
1059
1741
|
)
|
|
1060
|
-
] }),
|
|
1061
|
-
|
|
1062
|
-
|
|
1742
|
+
] }, spec.id)),
|
|
1743
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "border-t border-slate-700/60 pt-3", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1744
|
+
AdjustmentLayersPanel,
|
|
1063
1745
|
{
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Legacy rendering (process 1). Tint needs the current process." }),
|
|
1068
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1069
|
-
"button",
|
|
1070
|
-
{
|
|
1071
|
-
type: "button",
|
|
1072
|
-
onClick: () => editor.upgradeProcess(),
|
|
1073
|
-
className: "shrink-0 rounded bg-amber-500/20 px-2 py-0.5 hover:bg-amber-500/30",
|
|
1074
|
-
children: "Update"
|
|
1075
|
-
}
|
|
1076
|
-
)
|
|
1077
|
-
]
|
|
1746
|
+
controller,
|
|
1747
|
+
layers: state.adjustments.layers,
|
|
1748
|
+
histogram: editor.histogram
|
|
1078
1749
|
}
|
|
1079
|
-
)
|
|
1080
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
1081
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
1082
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Blur" }),
|
|
1083
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "font-mono", children: [
|
|
1084
|
-
state.adjustments.blur,
|
|
1085
|
-
"px"
|
|
1086
|
-
] })
|
|
1087
|
-
] }),
|
|
1088
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1089
|
-
"input",
|
|
1090
|
-
{
|
|
1091
|
-
type: "range",
|
|
1092
|
-
min: "0",
|
|
1093
|
-
max: "30",
|
|
1094
|
-
value: state.adjustments.blur,
|
|
1095
|
-
onChange: (e) => editor.setAdjustments({ blur: Number(e.target.value) }),
|
|
1096
|
-
className: "w-full accent-indigo-500"
|
|
1097
|
-
}
|
|
1098
|
-
)
|
|
1099
|
-
] }),
|
|
1100
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
1101
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
1102
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Opacity" }),
|
|
1103
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "font-mono", children: [
|
|
1104
|
-
Math.round(state.adjustments.opacity * 100),
|
|
1105
|
-
"%"
|
|
1106
|
-
] })
|
|
1107
|
-
] }),
|
|
1108
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1109
|
-
"input",
|
|
1110
|
-
{
|
|
1111
|
-
type: "range",
|
|
1112
|
-
min: "0",
|
|
1113
|
-
max: "1",
|
|
1114
|
-
step: "0.05",
|
|
1115
|
-
value: state.adjustments.opacity,
|
|
1116
|
-
onChange: (e) => editor.setAdjustments({ opacity: Number(e.target.value) }),
|
|
1117
|
-
className: "w-full accent-indigo-500"
|
|
1118
|
-
}
|
|
1119
|
-
)
|
|
1120
|
-
] })
|
|
1750
|
+
) })
|
|
1121
1751
|
] }),
|
|
1122
|
-
activeTab === "annotate" && /* @__PURE__ */ (0,
|
|
1123
|
-
/* @__PURE__ */ (0,
|
|
1124
|
-
/* @__PURE__ */ (0,
|
|
1752
|
+
activeTab === "annotate" && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-col gap-3 text-xs", children: [
|
|
1753
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Draw & Annotate" }),
|
|
1754
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "grid grid-cols-3 gap-1.5", children: ["pen", "rect", "circle", "arrow", "line"].map((t) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1125
1755
|
"button",
|
|
1126
1756
|
{
|
|
1127
1757
|
type: "button",
|
|
@@ -1131,9 +1761,9 @@ var ImageEditor = ({
|
|
|
1131
1761
|
},
|
|
1132
1762
|
t
|
|
1133
1763
|
)) }),
|
|
1134
|
-
/* @__PURE__ */ (0,
|
|
1135
|
-
/* @__PURE__ */ (0,
|
|
1136
|
-
/* @__PURE__ */ (0,
|
|
1764
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center gap-2 mt-2", children: [
|
|
1765
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("label", { className: "text-slate-400", children: "Color:" }),
|
|
1766
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1137
1767
|
"input",
|
|
1138
1768
|
{
|
|
1139
1769
|
type: "color",
|
|
@@ -1142,15 +1772,15 @@ var ImageEditor = ({
|
|
|
1142
1772
|
className: "w-8 h-8 rounded border border-slate-700 cursor-pointer bg-transparent"
|
|
1143
1773
|
}
|
|
1144
1774
|
),
|
|
1145
|
-
/* @__PURE__ */ (0,
|
|
1146
|
-
/* @__PURE__ */ (0,
|
|
1147
|
-
/* @__PURE__ */ (0,
|
|
1148
|
-
/* @__PURE__ */ (0,
|
|
1775
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex-1 ml-2", children: [
|
|
1776
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex justify-between text-slate-400 mb-0.5", children: [
|
|
1777
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: "Width:" }),
|
|
1778
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "font-mono", children: [
|
|
1149
1779
|
strokeWidth,
|
|
1150
1780
|
"px"
|
|
1151
1781
|
] })
|
|
1152
1782
|
] }),
|
|
1153
|
-
/* @__PURE__ */ (0,
|
|
1783
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1154
1784
|
"input",
|
|
1155
1785
|
{
|
|
1156
1786
|
type: "range",
|
|
@@ -1163,10 +1793,10 @@ var ImageEditor = ({
|
|
|
1163
1793
|
)
|
|
1164
1794
|
] })
|
|
1165
1795
|
] }),
|
|
1166
|
-
/* @__PURE__ */ (0,
|
|
1167
|
-
/* @__PURE__ */ (0,
|
|
1168
|
-
/* @__PURE__ */ (0,
|
|
1169
|
-
/* @__PURE__ */ (0,
|
|
1796
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "h-px bg-slate-800 my-1" }),
|
|
1797
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Add Text" }),
|
|
1798
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex gap-2", children: [
|
|
1799
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1170
1800
|
"input",
|
|
1171
1801
|
{
|
|
1172
1802
|
type: "text",
|
|
@@ -1177,7 +1807,7 @@ var ImageEditor = ({
|
|
|
1177
1807
|
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"
|
|
1178
1808
|
}
|
|
1179
1809
|
),
|
|
1180
|
-
/* @__PURE__ */ (0,
|
|
1810
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1181
1811
|
"button",
|
|
1182
1812
|
{
|
|
1183
1813
|
type: "button",
|
|
@@ -1187,14 +1817,14 @@ var ImageEditor = ({
|
|
|
1187
1817
|
}
|
|
1188
1818
|
)
|
|
1189
1819
|
] }),
|
|
1190
|
-
/* @__PURE__ */ (0,
|
|
1191
|
-
/* @__PURE__ */ (0,
|
|
1192
|
-
/* @__PURE__ */ (0,
|
|
1820
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center justify-between text-slate-400", children: [
|
|
1821
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: "Font Size:" }),
|
|
1822
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "font-mono", children: [
|
|
1193
1823
|
fontSize,
|
|
1194
1824
|
"px"
|
|
1195
1825
|
] })
|
|
1196
1826
|
] }),
|
|
1197
|
-
/* @__PURE__ */ (0,
|
|
1827
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1198
1828
|
"input",
|
|
1199
1829
|
{
|
|
1200
1830
|
type: "range",
|
|
@@ -1205,14 +1835,14 @@ var ImageEditor = ({
|
|
|
1205
1835
|
className: "w-full accent-indigo-500"
|
|
1206
1836
|
}
|
|
1207
1837
|
),
|
|
1208
|
-
/* @__PURE__ */ (0,
|
|
1209
|
-
/* @__PURE__ */ (0,
|
|
1210
|
-
/* @__PURE__ */ (0,
|
|
1838
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "h-px bg-slate-800 my-1" }),
|
|
1839
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
1840
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "text-slate-400", children: [
|
|
1211
1841
|
"Layers (",
|
|
1212
1842
|
state.annotations.length,
|
|
1213
1843
|
")"
|
|
1214
1844
|
] }),
|
|
1215
|
-
state.selectedAnnotationId && /* @__PURE__ */ (0,
|
|
1845
|
+
state.selectedAnnotationId && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1216
1846
|
"button",
|
|
1217
1847
|
{
|
|
1218
1848
|
type: "button",
|
|
@@ -1221,7 +1851,7 @@ var ImageEditor = ({
|
|
|
1221
1851
|
children: "Delete Selected"
|
|
1222
1852
|
}
|
|
1223
1853
|
),
|
|
1224
|
-
state.annotations.length > 0 && /* @__PURE__ */ (0,
|
|
1854
|
+
state.annotations.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1225
1855
|
"button",
|
|
1226
1856
|
{
|
|
1227
1857
|
type: "button",
|
|
@@ -1232,10 +1862,10 @@ var ImageEditor = ({
|
|
|
1232
1862
|
)
|
|
1233
1863
|
] })
|
|
1234
1864
|
] }),
|
|
1235
|
-
activeTab === "depth" && /* @__PURE__ */ (0,
|
|
1236
|
-
/* @__PURE__ */ (0,
|
|
1237
|
-
/* @__PURE__ */ (0,
|
|
1238
|
-
/* @__PURE__ */ (0,
|
|
1865
|
+
activeTab === "depth" && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-col gap-3 text-xs", children: [
|
|
1866
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
1867
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Depth Masking" }),
|
|
1868
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1239
1869
|
"button",
|
|
1240
1870
|
{
|
|
1241
1871
|
type: "button",
|
|
@@ -1245,8 +1875,8 @@ var ImageEditor = ({
|
|
|
1245
1875
|
}
|
|
1246
1876
|
)
|
|
1247
1877
|
] }),
|
|
1248
|
-
/* @__PURE__ */ (0,
|
|
1249
|
-
/* @__PURE__ */ (0,
|
|
1878
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "flex items-center gap-2 cursor-pointer text-slate-300", children: [
|
|
1879
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1250
1880
|
"input",
|
|
1251
1881
|
{
|
|
1252
1882
|
type: "checkbox",
|
|
@@ -1255,12 +1885,12 @@ var ImageEditor = ({
|
|
|
1255
1885
|
className: "rounded accent-indigo-500"
|
|
1256
1886
|
}
|
|
1257
1887
|
),
|
|
1258
|
-
/* @__PURE__ */ (0,
|
|
1888
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: "Enable Depth / Alpha Mask" })
|
|
1259
1889
|
] }),
|
|
1260
|
-
state.depthMask.enabled && /* @__PURE__ */ (0,
|
|
1261
|
-
/* @__PURE__ */ (0,
|
|
1262
|
-
/* @__PURE__ */ (0,
|
|
1263
|
-
/* @__PURE__ */ (0,
|
|
1890
|
+
state.depthMask.enabled && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
|
|
1891
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { children: [
|
|
1892
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("label", { className: "text-slate-400 block mb-1", children: "Mask Image URL:" }),
|
|
1893
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1264
1894
|
"input",
|
|
1265
1895
|
{
|
|
1266
1896
|
type: "text",
|
|
@@ -1271,8 +1901,8 @@ var ImageEditor = ({
|
|
|
1271
1901
|
}
|
|
1272
1902
|
)
|
|
1273
1903
|
] }),
|
|
1274
|
-
/* @__PURE__ */ (0,
|
|
1275
|
-
/* @__PURE__ */ (0,
|
|
1904
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex gap-2", children: [
|
|
1905
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1276
1906
|
"button",
|
|
1277
1907
|
{
|
|
1278
1908
|
type: "button",
|
|
@@ -1281,7 +1911,7 @@ var ImageEditor = ({
|
|
|
1281
1911
|
children: "Threshold Mode"
|
|
1282
1912
|
}
|
|
1283
1913
|
),
|
|
1284
|
-
/* @__PURE__ */ (0,
|
|
1914
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1285
1915
|
"button",
|
|
1286
1916
|
{
|
|
1287
1917
|
type: "button",
|
|
@@ -1291,12 +1921,12 @@ var ImageEditor = ({
|
|
|
1291
1921
|
}
|
|
1292
1922
|
)
|
|
1293
1923
|
] }),
|
|
1294
|
-
/* @__PURE__ */ (0,
|
|
1295
|
-
/* @__PURE__ */ (0,
|
|
1296
|
-
/* @__PURE__ */ (0,
|
|
1297
|
-
/* @__PURE__ */ (0,
|
|
1924
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { children: [
|
|
1925
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
1926
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: "Depth Start (Lower):" }),
|
|
1927
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "font-mono", children: state.depthMask.depthRange[0] })
|
|
1298
1928
|
] }),
|
|
1299
|
-
/* @__PURE__ */ (0,
|
|
1929
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1300
1930
|
"input",
|
|
1301
1931
|
{
|
|
1302
1932
|
type: "range",
|
|
@@ -1310,12 +1940,12 @@ var ImageEditor = ({
|
|
|
1310
1940
|
}
|
|
1311
1941
|
)
|
|
1312
1942
|
] }),
|
|
1313
|
-
/* @__PURE__ */ (0,
|
|
1314
|
-
/* @__PURE__ */ (0,
|
|
1315
|
-
/* @__PURE__ */ (0,
|
|
1316
|
-
/* @__PURE__ */ (0,
|
|
1943
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { children: [
|
|
1944
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
1945
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: "Depth Stop (Upper):" }),
|
|
1946
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "font-mono", children: state.depthMask.depthRange[1] })
|
|
1317
1947
|
] }),
|
|
1318
|
-
/* @__PURE__ */ (0,
|
|
1948
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1319
1949
|
"input",
|
|
1320
1950
|
{
|
|
1321
1951
|
type: "range",
|
|
@@ -1329,12 +1959,12 @@ var ImageEditor = ({
|
|
|
1329
1959
|
}
|
|
1330
1960
|
)
|
|
1331
1961
|
] }),
|
|
1332
|
-
/* @__PURE__ */ (0,
|
|
1333
|
-
/* @__PURE__ */ (0,
|
|
1334
|
-
/* @__PURE__ */ (0,
|
|
1335
|
-
/* @__PURE__ */ (0,
|
|
1962
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { children: [
|
|
1963
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
1964
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: "Softness:" }),
|
|
1965
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "font-mono", children: state.depthMask.softness })
|
|
1336
1966
|
] }),
|
|
1337
|
-
/* @__PURE__ */ (0,
|
|
1967
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1338
1968
|
"input",
|
|
1339
1969
|
{
|
|
1340
1970
|
type: "range",
|
|
@@ -1346,8 +1976,8 @@ var ImageEditor = ({
|
|
|
1346
1976
|
}
|
|
1347
1977
|
)
|
|
1348
1978
|
] }),
|
|
1349
|
-
/* @__PURE__ */ (0,
|
|
1350
|
-
/* @__PURE__ */ (0,
|
|
1979
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "flex items-center gap-2 cursor-pointer text-slate-300 mt-1", children: [
|
|
1980
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1351
1981
|
"input",
|
|
1352
1982
|
{
|
|
1353
1983
|
type: "checkbox",
|
|
@@ -1356,52 +1986,66 @@ var ImageEditor = ({
|
|
|
1356
1986
|
className: "rounded accent-indigo-500"
|
|
1357
1987
|
}
|
|
1358
1988
|
),
|
|
1359
|
-
/* @__PURE__ */ (0,
|
|
1989
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: "Invert Mask" })
|
|
1360
1990
|
] })
|
|
1361
1991
|
] })
|
|
1362
1992
|
] }),
|
|
1363
|
-
activeTab === "
|
|
1364
|
-
/* @__PURE__ */ (0,
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1993
|
+
activeTab === "select" && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-col gap-4 text-xs", children: [
|
|
1994
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1995
|
+
SelectionOptionsBar,
|
|
1996
|
+
{
|
|
1997
|
+
controller,
|
|
1998
|
+
options: selectionOptions,
|
|
1999
|
+
onOptionsChange: setSelectionOptions,
|
|
2000
|
+
selection: state.selection ?? null,
|
|
2001
|
+
quickMask: Boolean(state.quickMask)
|
|
2002
|
+
}
|
|
2003
|
+
),
|
|
2004
|
+
selectionError && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { role: "alert", className: "text-rose-400", children: selectionError }),
|
|
2005
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(RefineEdgePanel, { controller, selection: state.selection ?? null, layers: state.adjustments.layers })
|
|
2006
|
+
] }),
|
|
2007
|
+
activeTab === "ai" && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-col gap-4 text-xs", children: [
|
|
2008
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "p-3 rounded-lg bg-slate-950/60 border border-slate-800 flex flex-col gap-2.5", children: [
|
|
2009
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
2010
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px] flex items-center gap-1.5", children: [
|
|
2011
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("svg", { className: "w-3.5 h-3.5 text-indigo-400", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: "M6 3v12M18 9a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM6 21a3 3 0 1 0 0-6 3 3 0 0 0 0 6z" }) }),
|
|
1368
2012
|
"Background Removal"
|
|
1369
2013
|
] }),
|
|
1370
|
-
isRemovingBg && /* @__PURE__ */ (0,
|
|
2014
|
+
isRemovingBg && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-indigo-900/60 text-indigo-300 animate-pulse", children: "Removing..." })
|
|
1371
2015
|
] }),
|
|
1372
|
-
/* @__PURE__ */ (0,
|
|
1373
|
-
/* @__PURE__ */ (0,
|
|
2016
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "text-slate-400 text-[11px] leading-relaxed", children: "Isolate foreground subject with AI alpha transparency segmentation." }),
|
|
2017
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1374
2018
|
"button",
|
|
1375
2019
|
{
|
|
1376
2020
|
type: "button",
|
|
1377
2021
|
onClick: handleRemoveBackground,
|
|
1378
2022
|
disabled: isRemovingBg || isInpainting,
|
|
1379
2023
|
className: "mt-1 px-3 py-2 rounded-lg bg-indigo-600 hover:bg-indigo-500 disabled:opacity-50 text-white font-medium shadow transition-colors flex items-center justify-center gap-2",
|
|
1380
|
-
children: isRemovingBg ? /* @__PURE__ */ (0,
|
|
1381
|
-
/* @__PURE__ */ (0,
|
|
2024
|
+
children: isRemovingBg ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
|
|
2025
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "w-3.5 h-3.5 border-2 border-white/30 border-t-white rounded-full animate-spin" }),
|
|
1382
2026
|
"Removing Background..."
|
|
1383
|
-
] }) : /* @__PURE__ */ (0,
|
|
1384
|
-
/* @__PURE__ */ (0,
|
|
2027
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
|
|
2028
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("svg", { className: "w-3.5 h-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: "M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83" }) }),
|
|
1385
2029
|
"Remove Background"
|
|
1386
2030
|
] })
|
|
1387
2031
|
}
|
|
1388
2032
|
)
|
|
1389
2033
|
] }),
|
|
1390
|
-
/* @__PURE__ */ (0,
|
|
1391
|
-
/* @__PURE__ */ (0,
|
|
1392
|
-
/* @__PURE__ */ (0,
|
|
1393
|
-
/* @__PURE__ */ (0,
|
|
1394
|
-
/* @__PURE__ */ (0,
|
|
1395
|
-
/* @__PURE__ */ (0,
|
|
2034
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "p-3 rounded-lg bg-slate-950/60 border border-slate-800 flex flex-col gap-2.5", children: [
|
|
2035
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
2036
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px] flex items-center gap-1.5", children: [
|
|
2037
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("svg", { className: "w-3.5 h-3.5 text-purple-400", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
|
|
2038
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: "m21.64 3.64-1.28-1.28a1.2 1.2 0 0 0-1.7 0l-9.72 9.72-1.94 4.86 4.86-1.94 9.72-9.72a1.2 1.2 0 0 0 .06-1.64z" }),
|
|
2039
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: "m14 7 3 3" })
|
|
1396
2040
|
] }),
|
|
1397
2041
|
"Generative Inpainting"
|
|
1398
2042
|
] }),
|
|
1399
|
-
isInpainting && /* @__PURE__ */ (0,
|
|
2043
|
+
isInpainting && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-purple-900/60 text-purple-300 animate-pulse", children: "Inpainting..." })
|
|
1400
2044
|
] }),
|
|
1401
|
-
/* @__PURE__ */ (0,
|
|
1402
|
-
/* @__PURE__ */ (0,
|
|
1403
|
-
/* @__PURE__ */ (0,
|
|
1404
|
-
/* @__PURE__ */ (0,
|
|
2045
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "text-slate-400 text-[11px] leading-relaxed", children: "Replace, modify, or fill selected regions using prompt-guided generative inpainting." }),
|
|
2046
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
2047
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("label", { htmlFor: "ai-inpaint-prompt-react", className: "text-slate-300 text-[11px] font-medium", children: "Prompt" }),
|
|
2048
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1405
2049
|
"textarea",
|
|
1406
2050
|
{
|
|
1407
2051
|
id: "ai-inpaint-prompt-react",
|
|
@@ -1413,10 +2057,10 @@ var ImageEditor = ({
|
|
|
1413
2057
|
}
|
|
1414
2058
|
)
|
|
1415
2059
|
] }),
|
|
1416
|
-
/* @__PURE__ */ (0,
|
|
1417
|
-
/* @__PURE__ */ (0,
|
|
1418
|
-
/* @__PURE__ */ (0,
|
|
1419
|
-
/* @__PURE__ */ (0,
|
|
2060
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
2061
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-slate-300 text-[11px] font-medium", children: "Target Mask Region" }),
|
|
2062
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "grid grid-cols-2 gap-1.5", children: [
|
|
2063
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
1420
2064
|
"button",
|
|
1421
2065
|
{
|
|
1422
2066
|
type: "button",
|
|
@@ -1429,7 +2073,7 @@ var ImageEditor = ({
|
|
|
1429
2073
|
]
|
|
1430
2074
|
}
|
|
1431
2075
|
),
|
|
1432
|
-
/* @__PURE__ */ (0,
|
|
2076
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
1433
2077
|
"button",
|
|
1434
2078
|
{
|
|
1435
2079
|
type: "button",
|
|
@@ -1444,25 +2088,25 @@ var ImageEditor = ({
|
|
|
1444
2088
|
)
|
|
1445
2089
|
] })
|
|
1446
2090
|
] }),
|
|
1447
|
-
/* @__PURE__ */ (0,
|
|
2091
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1448
2092
|
"button",
|
|
1449
2093
|
{
|
|
1450
2094
|
type: "button",
|
|
1451
2095
|
onClick: handleInpaint,
|
|
1452
2096
|
disabled: isInpainting || isRemovingBg || !inpaintPrompt.trim(),
|
|
1453
2097
|
className: "mt-1 px-3 py-2 rounded-lg bg-purple-600 hover:bg-purple-500 disabled:opacity-50 text-white font-medium shadow transition-colors flex items-center justify-center gap-2",
|
|
1454
|
-
children: isInpainting ? /* @__PURE__ */ (0,
|
|
1455
|
-
/* @__PURE__ */ (0,
|
|
2098
|
+
children: isInpainting ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
|
|
2099
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "w-3.5 h-3.5 border-2 border-white/30 border-t-white rounded-full animate-spin" }),
|
|
1456
2100
|
"Generating Inpaint..."
|
|
1457
|
-
] }) : /* @__PURE__ */ (0,
|
|
1458
|
-
/* @__PURE__ */ (0,
|
|
2101
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
|
|
2102
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("svg", { className: "w-3.5 h-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: "M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83" }) }),
|
|
1459
2103
|
"Apply Inpainting"
|
|
1460
2104
|
] })
|
|
1461
2105
|
}
|
|
1462
2106
|
)
|
|
1463
2107
|
] }),
|
|
1464
|
-
aiError && /* @__PURE__ */ (0,
|
|
1465
|
-
aiSuccess && /* @__PURE__ */ (0,
|
|
2108
|
+
aiError && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "p-2.5 rounded-lg bg-rose-950/60 border border-rose-800 text-rose-300 text-[11px]", children: aiError }),
|
|
2109
|
+
aiSuccess && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "p-2.5 rounded-lg bg-emerald-950/60 border border-emerald-800 text-emerald-300 text-[11px]", children: aiSuccess })
|
|
1466
2110
|
] })
|
|
1467
2111
|
] })
|
|
1468
2112
|
] })
|
|
@@ -1472,7 +2116,14 @@ var ImageEditor = ({
|
|
|
1472
2116
|
};
|
|
1473
2117
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1474
2118
|
0 && (module.exports = {
|
|
2119
|
+
AdjustmentLayersPanel,
|
|
2120
|
+
ControlSlider,
|
|
2121
|
+
CurvesEditor,
|
|
2122
|
+
Histogram,
|
|
1475
2123
|
ImageEditor,
|
|
1476
|
-
|
|
2124
|
+
RefineEdgePanel,
|
|
2125
|
+
SelectionOptionsBar,
|
|
2126
|
+
useImageEditor,
|
|
2127
|
+
useImageHistogram
|
|
1477
2128
|
});
|
|
1478
2129
|
//# sourceMappingURL=index.cjs.map
|