@pixerate/editor-react 0.15.0 → 0.16.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 +4 -0
- package/dist/canvas/index.cjs +219 -118
- package/dist/canvas/index.cjs.map +1 -1
- package/dist/canvas/index.d.cts +59 -3
- package/dist/canvas/index.d.ts +59 -3
- package/dist/canvas/index.js +103 -0
- package/dist/canvas/index.js.map +1 -1
- package/dist/chunk-7SPRQZ6H.js +216 -0
- package/dist/chunk-7SPRQZ6H.js.map +1 -0
- package/dist/{chunk-QVQYYHJ5.js → chunk-FYIPXHAE.js} +21 -13
- package/dist/chunk-FYIPXHAE.js.map +1 -0
- package/dist/{chunk-LPFZMZ3L.js → chunk-XTFR3IMK.js} +676 -322
- package/dist/chunk-XTFR3IMK.js.map +1 -0
- package/dist/chunk-XWFU2CSG.js +167 -0
- package/dist/chunk-XWFU2CSG.js.map +1 -0
- package/dist/generation/index.cjs +235 -0
- package/dist/generation/index.cjs.map +1 -0
- package/dist/generation/index.d.cts +60 -0
- package/dist/generation/index.d.ts +60 -0
- package/dist/generation/index.js +13 -0
- package/dist/generation/index.js.map +1 -0
- package/dist/image-editor/index.cjs +684 -336
- package/dist/image-editor/index.cjs.map +1 -1
- package/dist/image-editor/index.d.cts +60 -2
- package/dist/image-editor/index.d.ts +60 -2
- package/dist/image-editor/index.js +13 -3
- package/dist/index.cjs +1881 -648
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +143 -4
- package/dist/index.d.ts +143 -4
- package/dist/index.js +626 -73
- 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 +17 -10
- package/dist/spreadsheet/index.cjs.map +1 -1
- package/dist/spreadsheet/index.d.cts +2 -0
- package/dist/spreadsheet/index.d.ts +2 -0
- package/dist/spreadsheet/index.js +1 -1
- package/package.json +18 -2
- package/dist/chunk-LPFZMZ3L.js.map +0 -1
- package/dist/chunk-QVQYYHJ5.js.map +0 -1
|
@@ -11,6 +11,8 @@ function useImageEditor(options = {}) {
|
|
|
11
11
|
const controller = controllerRef.current;
|
|
12
12
|
const [state, setState] = useState(() => controller.getState());
|
|
13
13
|
const [historyTicks, setHistoryTicks] = useState(0);
|
|
14
|
+
const [histogram, setHistogram] = useState(() => controller.getHistogram());
|
|
15
|
+
useEffect(() => controller.subscribeHistogram(setHistogram), [controller]);
|
|
14
16
|
useEffect(() => {
|
|
15
17
|
const unsub = controller.subscribe((next) => {
|
|
16
18
|
setState(next);
|
|
@@ -43,6 +45,20 @@ function useImageEditor(options = {}) {
|
|
|
43
45
|
);
|
|
44
46
|
const resetAdjustments = useCallback(() => controller.resetAdjustments(), [controller]);
|
|
45
47
|
const upgradeProcess = useCallback(() => controller.upgradeProcess(), [controller]);
|
|
48
|
+
const addAdjustmentLayer = useCallback(
|
|
49
|
+
(init, index) => controller.addAdjustmentLayer(init, index),
|
|
50
|
+
[controller]
|
|
51
|
+
);
|
|
52
|
+
const updateAdjustmentLayer = useCallback(
|
|
53
|
+
(id, update, commitToHistory = true) => controller.updateAdjustmentLayer(id, update, commitToHistory),
|
|
54
|
+
[controller]
|
|
55
|
+
);
|
|
56
|
+
const removeAdjustmentLayer = useCallback((id) => controller.removeAdjustmentLayer(id), [controller]);
|
|
57
|
+
const moveAdjustmentLayer = useCallback((id, to) => controller.moveAdjustmentLayer(id, to), [controller]);
|
|
58
|
+
const toggleAdjustmentLayer = useCallback(
|
|
59
|
+
(id, enabled) => controller.toggleAdjustmentLayer(id, enabled),
|
|
60
|
+
[controller]
|
|
61
|
+
);
|
|
46
62
|
const setDepthMask = useCallback((partial) => controller.setDepthMask(partial), [controller]);
|
|
47
63
|
const resetDepthMask = useCallback(() => controller.resetDepthMask(), [controller]);
|
|
48
64
|
const addAnnotation = useCallback((ann) => controller.addAnnotation(ann), [controller]);
|
|
@@ -104,6 +120,12 @@ function useImageEditor(options = {}) {
|
|
|
104
120
|
setAdjustments,
|
|
105
121
|
resetAdjustments,
|
|
106
122
|
upgradeProcess,
|
|
123
|
+
addAdjustmentLayer,
|
|
124
|
+
updateAdjustmentLayer,
|
|
125
|
+
removeAdjustmentLayer,
|
|
126
|
+
moveAdjustmentLayer,
|
|
127
|
+
toggleAdjustmentLayer,
|
|
128
|
+
histogram,
|
|
107
129
|
setDepthMask,
|
|
108
130
|
resetDepthMask,
|
|
109
131
|
addAnnotation,
|
|
@@ -132,12 +154,12 @@ function useImageEditor(options = {}) {
|
|
|
132
154
|
applyBackgroundRemovedImage
|
|
133
155
|
}),
|
|
134
156
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
135
|
-
[controller, state, historyTicks]
|
|
157
|
+
[controller, state, historyTicks, histogram]
|
|
136
158
|
);
|
|
137
159
|
}
|
|
138
160
|
|
|
139
161
|
// src/image-editor/ImageEditor.tsx
|
|
140
|
-
import { useRef as
|
|
162
|
+
import React4, { useRef as useRef3, useEffect as useEffect4, useState as useState5, useCallback as useCallback2 } from "react";
|
|
141
163
|
import {
|
|
142
164
|
canvasToImagePoint,
|
|
143
165
|
hitTestCrop,
|
|
@@ -145,9 +167,461 @@ import {
|
|
|
145
167
|
calculateCropDrag,
|
|
146
168
|
hitTestAnnotation,
|
|
147
169
|
normalizeProcessVersion,
|
|
148
|
-
LATEST_PROCESS_VERSION
|
|
170
|
+
LATEST_PROCESS_VERSION,
|
|
171
|
+
IMAGE_ADJUSTMENT_CONTROLS,
|
|
172
|
+
readAdjustmentControls
|
|
149
173
|
} from "@pixerate/editor/image-editor";
|
|
150
|
-
|
|
174
|
+
|
|
175
|
+
// src/image-editor/ControlSlider.tsx
|
|
176
|
+
import { controlTrackBackground, formatControlValue } from "@pixerate/editor/image-editor";
|
|
177
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
178
|
+
var ControlSlider = ({ spec, value, onChange, disabled, label }) => {
|
|
179
|
+
const name = label ?? spec.label;
|
|
180
|
+
const track = controlTrackBackground(spec);
|
|
181
|
+
return /* @__PURE__ */ jsxs("div", { "data-control-id": spec.id, children: [
|
|
182
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
183
|
+
/* @__PURE__ */ jsx("span", { children: name }),
|
|
184
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono", children: formatControlValue(spec, value) })
|
|
185
|
+
] }),
|
|
186
|
+
/* @__PURE__ */ jsx(
|
|
187
|
+
"input",
|
|
188
|
+
{
|
|
189
|
+
type: "range",
|
|
190
|
+
min: spec.min,
|
|
191
|
+
max: spec.max,
|
|
192
|
+
step: spec.step,
|
|
193
|
+
"aria-label": name,
|
|
194
|
+
disabled,
|
|
195
|
+
value,
|
|
196
|
+
onChange: (e) => onChange(Number(e.target.value)),
|
|
197
|
+
className: "w-full accent-indigo-500 disabled:opacity-40"
|
|
198
|
+
}
|
|
199
|
+
),
|
|
200
|
+
track && /* @__PURE__ */ jsx("div", { className: "h-0.5 rounded-full opacity-70", style: { background: track } })
|
|
201
|
+
] });
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
// src/image-editor/Histogram.tsx
|
|
205
|
+
import { useEffect as useEffect2, useMemo as useMemo2, useState as useState2 } from "react";
|
|
206
|
+
import { histogramPath } from "@pixerate/editor/image-editor";
|
|
207
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
208
|
+
var FILL = {
|
|
209
|
+
luminance: "rgba(148, 163, 184, 0.45)",
|
|
210
|
+
red: "rgba(239, 68, 68, 0.45)",
|
|
211
|
+
green: "rgba(34, 197, 94, 0.45)",
|
|
212
|
+
blue: "rgba(59, 130, 246, 0.45)"
|
|
213
|
+
};
|
|
214
|
+
var W = 256;
|
|
215
|
+
var Histogram = ({ histogram, channel = "rgb", height = 64, className = "", style }) => {
|
|
216
|
+
const channels = channel === "rgb" ? ["luminance", "red", "green", "blue"] : [channel];
|
|
217
|
+
const paths = useMemo2(
|
|
218
|
+
() => histogram && histogram.samples > 0 ? channels.map((c) => ({ c, d: histogramPath(histogram, c, W, height) })) : [],
|
|
219
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
220
|
+
[histogram, channel, height]
|
|
221
|
+
);
|
|
222
|
+
const clipLow = histogram && histogram.samples ? Math.max(...histogram.shadows) / histogram.samples : 0;
|
|
223
|
+
const clipHigh = histogram && histogram.samples ? Math.max(...histogram.highlights) / histogram.samples : 0;
|
|
224
|
+
return /* @__PURE__ */ jsxs2(
|
|
225
|
+
"svg",
|
|
226
|
+
{
|
|
227
|
+
viewBox: `0 0 ${W} ${height}`,
|
|
228
|
+
preserveAspectRatio: "none",
|
|
229
|
+
role: "img",
|
|
230
|
+
"aria-label": histogram ? `Histogram of ${histogram.samples} pixels` : "Histogram (no data)",
|
|
231
|
+
"data-testid": "image-editor-histogram",
|
|
232
|
+
className: `w-full rounded bg-slate-950/60 ${className}`,
|
|
233
|
+
style: { height, ...style },
|
|
234
|
+
children: [
|
|
235
|
+
paths.map(({ c, d }) => /* @__PURE__ */ jsx2("path", { d, fill: FILL[c], stroke: "none" }, c)),
|
|
236
|
+
clipLow > 5e-3 && /* @__PURE__ */ jsx2("rect", { x: 0, y: 0, width: 3, height, fill: "#3b82f6", "data-testid": "histogram-clip-shadows" }),
|
|
237
|
+
clipHigh > 5e-3 && /* @__PURE__ */ jsx2("rect", { x: W - 3, y: 0, width: 3, height, fill: "#ef4444", "data-testid": "histogram-clip-highlights" })
|
|
238
|
+
]
|
|
239
|
+
}
|
|
240
|
+
);
|
|
241
|
+
};
|
|
242
|
+
function useImageHistogram(controller) {
|
|
243
|
+
const [histogram, setHistogram] = useState2(() => controller.getHistogram());
|
|
244
|
+
useEffect2(() => controller.subscribeHistogram(setHistogram), [controller]);
|
|
245
|
+
return histogram;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// src/image-editor/AdjustmentLayersPanel.tsx
|
|
249
|
+
import { useEffect as useEffect3, useState as useState4 } from "react";
|
|
250
|
+
import {
|
|
251
|
+
ADJUSTMENT_KINDS,
|
|
252
|
+
ADJUSTMENT_LAYER_CONTROLS,
|
|
253
|
+
ADJUSTMENT_LAYER_OPACITY_CONTROL,
|
|
254
|
+
BLEND_MODES,
|
|
255
|
+
HUE_RANGE_LABELS,
|
|
256
|
+
adjustmentKindLabel,
|
|
257
|
+
getControlPath,
|
|
258
|
+
hueRangeWeight,
|
|
259
|
+
setControlPath
|
|
260
|
+
} from "@pixerate/editor/image-editor";
|
|
261
|
+
|
|
262
|
+
// src/image-editor/CurvesEditor.tsx
|
|
263
|
+
import { useRef as useRef2, useState as useState3 } from "react";
|
|
264
|
+
import {
|
|
265
|
+
boxToCurvePoint,
|
|
266
|
+
curvePointToBox,
|
|
267
|
+
curveSvgPath,
|
|
268
|
+
hitTestCurvePoint,
|
|
269
|
+
histogramPath as histogramPath2,
|
|
270
|
+
insertCurvePoint,
|
|
271
|
+
moveCurvePoint,
|
|
272
|
+
removeCurvePoint
|
|
273
|
+
} from "@pixerate/editor/image-editor";
|
|
274
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
275
|
+
var BOX = 256;
|
|
276
|
+
var STROKE = { master: "#e2e8f0", red: "#ef4444", green: "#22c55e", blue: "#3b82f6" };
|
|
277
|
+
var CurvesEditor = ({ points, onChange, histogram, channel = "master", size = 200 }) => {
|
|
278
|
+
const svgRef = useRef2(null);
|
|
279
|
+
const [drag, setDrag] = useState3(null);
|
|
280
|
+
const toBox = (e) => {
|
|
281
|
+
const rect = svgRef.current?.getBoundingClientRect();
|
|
282
|
+
if (!rect || !rect.width || !rect.height) return { x: 0, y: 0 };
|
|
283
|
+
return { x: (e.clientX - rect.left) / rect.width * BOX, y: (e.clientY - rect.top) / rect.height * BOX };
|
|
284
|
+
};
|
|
285
|
+
const onPointerDown = (e) => {
|
|
286
|
+
const { x, y } = toBox(e);
|
|
287
|
+
let index = hitTestCurvePoint(points, x, y, BOX, BOX, 10);
|
|
288
|
+
if (index === -1) {
|
|
289
|
+
const inserted = insertCurvePoint(points, boxToCurvePoint(x, y, BOX, BOX));
|
|
290
|
+
onChange(inserted.points, true);
|
|
291
|
+
index = inserted.index;
|
|
292
|
+
} else {
|
|
293
|
+
onChange(points, true);
|
|
294
|
+
}
|
|
295
|
+
setDrag(index);
|
|
296
|
+
e.target.setPointerCapture?.(e.pointerId);
|
|
297
|
+
};
|
|
298
|
+
const onPointerMove = (e) => {
|
|
299
|
+
if (drag === null) return;
|
|
300
|
+
onChange(moveCurvePoint(points, drag, boxToCurvePoint(toBox(e).x, toBox(e).y, BOX, BOX)), false);
|
|
301
|
+
};
|
|
302
|
+
const onDoubleClick = (e) => {
|
|
303
|
+
const { x, y } = toBox(e);
|
|
304
|
+
const index = hitTestCurvePoint(points, x, y, BOX, BOX, 10);
|
|
305
|
+
if (index !== -1 && points.length > 2) onChange(removeCurvePoint(points, index), true);
|
|
306
|
+
};
|
|
307
|
+
const histChannel = channel === "master" ? "luminance" : channel;
|
|
308
|
+
return /* @__PURE__ */ jsxs3(
|
|
309
|
+
"svg",
|
|
310
|
+
{
|
|
311
|
+
ref: svgRef,
|
|
312
|
+
viewBox: `0 0 ${BOX} ${BOX}`,
|
|
313
|
+
width: size,
|
|
314
|
+
height: size,
|
|
315
|
+
role: "application",
|
|
316
|
+
"aria-label": `${channel} curve`,
|
|
317
|
+
"data-testid": "curves-editor",
|
|
318
|
+
className: "rounded bg-slate-950/70 touch-none select-none cursor-crosshair",
|
|
319
|
+
onPointerDown,
|
|
320
|
+
onPointerMove,
|
|
321
|
+
onPointerUp: () => setDrag(null),
|
|
322
|
+
onPointerCancel: () => setDrag(null),
|
|
323
|
+
onDoubleClick,
|
|
324
|
+
children: [
|
|
325
|
+
histogram && histogram.samples > 0 && /* @__PURE__ */ jsx3("path", { d: histogramPath2(histogram, histChannel, BOX, BOX), fill: "rgba(148, 163, 184, 0.25)" }),
|
|
326
|
+
[64, 128, 192].map((v) => /* @__PURE__ */ jsxs3("g", { stroke: "rgba(148, 163, 184, 0.2)", strokeWidth: 1, children: [
|
|
327
|
+
/* @__PURE__ */ jsx3("line", { x1: v, y1: 0, x2: v, y2: BOX }),
|
|
328
|
+
/* @__PURE__ */ jsx3("line", { x1: 0, y1: v, x2: BOX, y2: v })
|
|
329
|
+
] }, v)),
|
|
330
|
+
/* @__PURE__ */ jsx3("line", { x1: 0, y1: BOX, x2: BOX, y2: 0, stroke: "rgba(148, 163, 184, 0.35)", strokeDasharray: "4 4" }),
|
|
331
|
+
/* @__PURE__ */ jsx3("path", { d: curveSvgPath(points, BOX, BOX), fill: "none", stroke: STROKE[channel], strokeWidth: 2 }),
|
|
332
|
+
points.map((p, i) => {
|
|
333
|
+
const b = curvePointToBox(p, BOX, BOX);
|
|
334
|
+
return /* @__PURE__ */ jsx3(
|
|
335
|
+
"circle",
|
|
336
|
+
{
|
|
337
|
+
cx: b.x,
|
|
338
|
+
cy: b.y,
|
|
339
|
+
r: 5,
|
|
340
|
+
fill: drag === i ? STROKE[channel] : "#0f172a",
|
|
341
|
+
stroke: STROKE[channel],
|
|
342
|
+
strokeWidth: 2,
|
|
343
|
+
"aria-label": `Point ${p.input} to ${p.output}`
|
|
344
|
+
},
|
|
345
|
+
i
|
|
346
|
+
);
|
|
347
|
+
})
|
|
348
|
+
]
|
|
349
|
+
}
|
|
350
|
+
);
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
// src/image-editor/AdjustmentLayersPanel.tsx
|
|
354
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
355
|
+
var CHANNELS = [
|
|
356
|
+
{ id: "master", label: "RGB" },
|
|
357
|
+
{ id: "red", label: "Red" },
|
|
358
|
+
{ id: "green", label: "Green" },
|
|
359
|
+
{ id: "blue", label: "Blue" }
|
|
360
|
+
];
|
|
361
|
+
var TONES = [
|
|
362
|
+
{ id: "shadows", label: "Shadows" },
|
|
363
|
+
{ id: "midtones", label: "Midtones" },
|
|
364
|
+
{ id: "highlights", label: "Highlights" }
|
|
365
|
+
];
|
|
366
|
+
var selectClass = "bg-slate-800 border border-slate-700 rounded px-1.5 py-1 text-slate-200";
|
|
367
|
+
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"}`;
|
|
368
|
+
var HueRangeStrip = ({ bounds }) => {
|
|
369
|
+
const pts = Array.from({ length: 73 }, (_, i) => `${i / 72 * 360},${20 - hueRangeWeight({ bounds }, i * 5) * 18}`).join(" ");
|
|
370
|
+
return /* @__PURE__ */ jsxs4("svg", { viewBox: "0 0 360 20", preserveAspectRatio: "none", className: "w-full h-4 rounded", "data-testid": "hue-range-strip", children: [
|
|
371
|
+
/* @__PURE__ */ jsx4("defs", { children: /* @__PURE__ */ jsx4("linearGradient", { id: "pixerate-hue-strip", children: [0, 60, 120, 180, 240, 300, 360].map((h) => /* @__PURE__ */ jsx4("stop", { offset: `${h / 360 * 100}%`, stopColor: `hsl(${h}, 90%, 50%)` }, h)) }) }),
|
|
372
|
+
/* @__PURE__ */ jsx4("rect", { width: 360, height: 20, fill: "url(#pixerate-hue-strip)", opacity: 0.6 }),
|
|
373
|
+
/* @__PURE__ */ jsx4("polyline", { points: pts, fill: "none", stroke: "#fff", strokeWidth: 1.5 })
|
|
374
|
+
] });
|
|
375
|
+
};
|
|
376
|
+
var AdjustmentLayersPanel = ({ controller, layers: layersProp, histogram }) => {
|
|
377
|
+
const follow = layersProp === void 0;
|
|
378
|
+
const [, setTick] = useState4(0);
|
|
379
|
+
useEffect3(() => follow ? controller.subscribe(() => setTick((n) => n + 1)) : void 0, [controller, follow]);
|
|
380
|
+
const layers = layersProp ?? controller.getAdjustmentLayers();
|
|
381
|
+
const [newKind, setNewKind] = useState4("curves");
|
|
382
|
+
const [selectedId, setSelectedId] = useState4(null);
|
|
383
|
+
const [channel, setChannel] = useState4("master");
|
|
384
|
+
const [range, setRange] = useState4(-1);
|
|
385
|
+
const [tone, setTone] = useState4("midtones");
|
|
386
|
+
const selected = layers.find((l) => l.id === selectedId) ?? null;
|
|
387
|
+
const topFirst = [...layers].reverse();
|
|
388
|
+
const setParam = (layer, path, value, commit = true) => controller.updateAdjustmentLayer(layer.id, { params: setControlPath(layer.params, path, value) }, commit);
|
|
389
|
+
const slider = (layer, spec, label) => /* @__PURE__ */ jsx4(
|
|
390
|
+
ControlSlider,
|
|
391
|
+
{
|
|
392
|
+
spec,
|
|
393
|
+
label,
|
|
394
|
+
value: Number(getControlPath(layer.params, spec.id) ?? spec.default),
|
|
395
|
+
onChange: (v) => setParam(layer, spec.id, v)
|
|
396
|
+
},
|
|
397
|
+
spec.id
|
|
398
|
+
);
|
|
399
|
+
const controlsOf = (layer, filter = () => true) => ADJUSTMENT_LAYER_CONTROLS[layer.kind].filter(filter);
|
|
400
|
+
const checkbox = (layer, path, label) => /* @__PURE__ */ jsxs4("label", { className: "flex items-center gap-2 text-slate-300", children: [
|
|
401
|
+
/* @__PURE__ */ jsx4(
|
|
402
|
+
"input",
|
|
403
|
+
{
|
|
404
|
+
type: "checkbox",
|
|
405
|
+
checked: Boolean(getControlPath(layer.params, path)),
|
|
406
|
+
onChange: (e) => setParam(layer, path, e.target.checked),
|
|
407
|
+
className: "accent-indigo-500"
|
|
408
|
+
}
|
|
409
|
+
),
|
|
410
|
+
label
|
|
411
|
+
] });
|
|
412
|
+
const editor = (layer) => {
|
|
413
|
+
switch (layer.kind) {
|
|
414
|
+
case "curves":
|
|
415
|
+
return /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-2", children: [
|
|
416
|
+
/* @__PURE__ */ jsx4("div", { className: "flex gap-1", role: "group", "aria-label": "Curve channel", children: CHANNELS.map((c) => /* @__PURE__ */ jsx4("button", { type: "button", className: chipClass(channel === c.id), onClick: () => setChannel(c.id), children: c.label }, c.id)) }),
|
|
417
|
+
/* @__PURE__ */ jsx4(
|
|
418
|
+
CurvesEditor,
|
|
419
|
+
{
|
|
420
|
+
points: layer.params[channel],
|
|
421
|
+
channel,
|
|
422
|
+
histogram,
|
|
423
|
+
onChange: (points, commit) => setParam(layer, channel, points, commit)
|
|
424
|
+
}
|
|
425
|
+
)
|
|
426
|
+
] });
|
|
427
|
+
case "levels":
|
|
428
|
+
return /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-2", children: [
|
|
429
|
+
/* @__PURE__ */ jsx4("div", { className: "flex gap-1", role: "group", "aria-label": "Levels channel", children: CHANNELS.map((c) => /* @__PURE__ */ jsx4("button", { type: "button", className: chipClass(channel === c.id), onClick: () => setChannel(c.id), children: c.label }, c.id)) }),
|
|
430
|
+
controlsOf(layer, (s) => s.id.startsWith(`${channel}.`)).map((s) => slider(layer, s))
|
|
431
|
+
] });
|
|
432
|
+
case "hueSaturation":
|
|
433
|
+
return /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-2", children: [
|
|
434
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex flex-wrap gap-1", role: "group", "aria-label": "Hue range", children: [
|
|
435
|
+
/* @__PURE__ */ jsx4("button", { type: "button", className: chipClass(range === -1), onClick: () => setRange(-1), children: "Master" }),
|
|
436
|
+
HUE_RANGE_LABELS.map((name, i) => /* @__PURE__ */ jsx4("button", { type: "button", className: chipClass(range === i), onClick: () => setRange(i), disabled: layer.params.colorize, children: name }, name))
|
|
437
|
+
] }),
|
|
438
|
+
range >= 0 && !layer.params.colorize && /* @__PURE__ */ jsx4(HueRangeStrip, { bounds: layer.params.ranges[range].bounds }),
|
|
439
|
+
controlsOf(layer, (s) => range === -1 || layer.params.colorize ? !s.id.startsWith("ranges.") : s.id.startsWith(`ranges.${range}.`)).map(
|
|
440
|
+
(s) => slider(layer, s)
|
|
441
|
+
),
|
|
442
|
+
range === -1 && checkbox(layer, "colorize", "Colorize")
|
|
443
|
+
] });
|
|
444
|
+
case "colorBalance":
|
|
445
|
+
return /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-2", children: [
|
|
446
|
+
/* @__PURE__ */ jsx4("div", { className: "flex gap-1", role: "group", "aria-label": "Tone", children: TONES.map((t) => /* @__PURE__ */ jsx4("button", { type: "button", className: chipClass(tone === t.id), onClick: () => setTone(t.id), children: t.label }, t.id)) }),
|
|
447
|
+
controlsOf(layer, (s) => s.id.startsWith(`${tone}.`)).map((s) => slider(layer, s)),
|
|
448
|
+
checkbox(layer, "preserveLuminosity", "Preserve luminosity")
|
|
449
|
+
] });
|
|
450
|
+
case "blackWhite":
|
|
451
|
+
return /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-2", children: [
|
|
452
|
+
controlsOf(layer).map((s) => slider(layer, s)),
|
|
453
|
+
/* @__PURE__ */ jsxs4("label", { className: "flex items-center gap-2 text-slate-300", children: [
|
|
454
|
+
/* @__PURE__ */ jsx4(
|
|
455
|
+
"input",
|
|
456
|
+
{
|
|
457
|
+
type: "checkbox",
|
|
458
|
+
checked: layer.params.tint !== null,
|
|
459
|
+
onChange: (e) => setParam(layer, "tint", e.target.checked ? "#e1d3b3" : null),
|
|
460
|
+
className: "accent-indigo-500"
|
|
461
|
+
}
|
|
462
|
+
),
|
|
463
|
+
"Tint",
|
|
464
|
+
layer.params.tint && /* @__PURE__ */ jsx4(
|
|
465
|
+
"input",
|
|
466
|
+
{
|
|
467
|
+
type: "color",
|
|
468
|
+
"aria-label": "Tint colour",
|
|
469
|
+
value: layer.params.tint,
|
|
470
|
+
onChange: (e) => setParam(layer, "tint", e.target.value),
|
|
471
|
+
className: "h-5 w-8 bg-transparent"
|
|
472
|
+
}
|
|
473
|
+
)
|
|
474
|
+
] })
|
|
475
|
+
] });
|
|
476
|
+
case "photoFilter":
|
|
477
|
+
return /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-2", children: [
|
|
478
|
+
/* @__PURE__ */ jsxs4("label", { className: "flex items-center gap-2 text-slate-300", children: [
|
|
479
|
+
"Filter colour",
|
|
480
|
+
/* @__PURE__ */ jsx4(
|
|
481
|
+
"input",
|
|
482
|
+
{
|
|
483
|
+
type: "color",
|
|
484
|
+
"aria-label": "Filter colour",
|
|
485
|
+
value: layer.params.color,
|
|
486
|
+
onChange: (e) => setParam(layer, "color", e.target.value),
|
|
487
|
+
className: "h-5 w-8 bg-transparent"
|
|
488
|
+
}
|
|
489
|
+
)
|
|
490
|
+
] }),
|
|
491
|
+
controlsOf(layer).map((s) => slider(layer, s)),
|
|
492
|
+
checkbox(layer, "preserveLuminosity", "Preserve luminosity")
|
|
493
|
+
] });
|
|
494
|
+
case "invert":
|
|
495
|
+
return /* @__PURE__ */ jsx4("p", { className: "text-slate-500 text-[11px]", children: "Invert has no settings." });
|
|
496
|
+
default:
|
|
497
|
+
return /* @__PURE__ */ jsx4("div", { className: "flex flex-col gap-2", children: controlsOf(layer).map((s) => slider(layer, s)) });
|
|
498
|
+
}
|
|
499
|
+
};
|
|
500
|
+
return /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-2 text-xs", "data-testid": "adjustment-layers-panel", children: [
|
|
501
|
+
/* @__PURE__ */ jsx4("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsx4("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Adjustment Layers" }) }),
|
|
502
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex gap-1.5", children: [
|
|
503
|
+
/* @__PURE__ */ jsx4(
|
|
504
|
+
"select",
|
|
505
|
+
{
|
|
506
|
+
"aria-label": "New adjustment layer kind",
|
|
507
|
+
className: `${selectClass} flex-1`,
|
|
508
|
+
value: newKind,
|
|
509
|
+
onChange: (e) => setNewKind(e.target.value),
|
|
510
|
+
children: ADJUSTMENT_KINDS.map((k) => /* @__PURE__ */ jsx4("option", { value: k.kind, children: k.label }, k.kind))
|
|
511
|
+
}
|
|
512
|
+
),
|
|
513
|
+
/* @__PURE__ */ jsx4(
|
|
514
|
+
"button",
|
|
515
|
+
{
|
|
516
|
+
type: "button",
|
|
517
|
+
className: "px-2 py-1 rounded bg-indigo-600 text-white hover:bg-indigo-500",
|
|
518
|
+
onClick: () => {
|
|
519
|
+
setSelectedId(controller.addAdjustmentLayer(newKind));
|
|
520
|
+
setChannel("master");
|
|
521
|
+
setRange(-1);
|
|
522
|
+
},
|
|
523
|
+
children: "Add Layer"
|
|
524
|
+
}
|
|
525
|
+
)
|
|
526
|
+
] }),
|
|
527
|
+
topFirst.length > 0 && /* @__PURE__ */ jsx4("ul", { className: "flex flex-col gap-1", "aria-label": "Adjustment layers", children: topFirst.map((layer) => {
|
|
528
|
+
const index = layers.indexOf(layer);
|
|
529
|
+
const label = layer.name || adjustmentKindLabel(layer.kind);
|
|
530
|
+
return /* @__PURE__ */ jsxs4(
|
|
531
|
+
"li",
|
|
532
|
+
{
|
|
533
|
+
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"}`,
|
|
534
|
+
children: [
|
|
535
|
+
/* @__PURE__ */ jsx4(
|
|
536
|
+
"input",
|
|
537
|
+
{
|
|
538
|
+
type: "checkbox",
|
|
539
|
+
"aria-label": `Show ${label}`,
|
|
540
|
+
checked: layer.enabled,
|
|
541
|
+
onChange: () => controller.toggleAdjustmentLayer(layer.id),
|
|
542
|
+
className: "accent-indigo-500"
|
|
543
|
+
}
|
|
544
|
+
),
|
|
545
|
+
/* @__PURE__ */ jsx4(
|
|
546
|
+
"button",
|
|
547
|
+
{
|
|
548
|
+
type: "button",
|
|
549
|
+
className: `flex-1 text-left truncate ${layer.enabled ? "text-slate-200" : "text-slate-500 line-through"}`,
|
|
550
|
+
onClick: () => setSelectedId(layer.id === selectedId ? null : layer.id),
|
|
551
|
+
"aria-pressed": layer.id === selectedId,
|
|
552
|
+
children: label
|
|
553
|
+
}
|
|
554
|
+
),
|
|
555
|
+
/* @__PURE__ */ jsx4(
|
|
556
|
+
"button",
|
|
557
|
+
{
|
|
558
|
+
type: "button",
|
|
559
|
+
"aria-label": `Move ${label} up`,
|
|
560
|
+
disabled: index === layers.length - 1,
|
|
561
|
+
onClick: () => controller.moveAdjustmentLayer(layer.id, index + 1),
|
|
562
|
+
className: "px-1 text-slate-400 hover:text-slate-200 disabled:opacity-30",
|
|
563
|
+
children: "\u2191"
|
|
564
|
+
}
|
|
565
|
+
),
|
|
566
|
+
/* @__PURE__ */ jsx4(
|
|
567
|
+
"button",
|
|
568
|
+
{
|
|
569
|
+
type: "button",
|
|
570
|
+
"aria-label": `Move ${label} down`,
|
|
571
|
+
disabled: index === 0,
|
|
572
|
+
onClick: () => controller.moveAdjustmentLayer(layer.id, index - 1),
|
|
573
|
+
className: "px-1 text-slate-400 hover:text-slate-200 disabled:opacity-30",
|
|
574
|
+
children: "\u2193"
|
|
575
|
+
}
|
|
576
|
+
),
|
|
577
|
+
/* @__PURE__ */ jsx4(
|
|
578
|
+
"button",
|
|
579
|
+
{
|
|
580
|
+
type: "button",
|
|
581
|
+
"aria-label": `Remove ${label}`,
|
|
582
|
+
onClick: () => {
|
|
583
|
+
controller.removeAdjustmentLayer(layer.id);
|
|
584
|
+
if (selectedId === layer.id) setSelectedId(null);
|
|
585
|
+
},
|
|
586
|
+
className: "px-1 text-rose-400 hover:text-rose-300",
|
|
587
|
+
children: "\u2715"
|
|
588
|
+
}
|
|
589
|
+
)
|
|
590
|
+
]
|
|
591
|
+
},
|
|
592
|
+
layer.id
|
|
593
|
+
);
|
|
594
|
+
}) }),
|
|
595
|
+
selected && /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-2 rounded border border-slate-700/60 p-2", "data-testid": "adjustment-layer-editor", children: [
|
|
596
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2", children: [
|
|
597
|
+
/* @__PURE__ */ jsx4("span", { className: "text-slate-400", children: "Blend" }),
|
|
598
|
+
/* @__PURE__ */ jsx4(
|
|
599
|
+
"select",
|
|
600
|
+
{
|
|
601
|
+
"aria-label": "Blend mode",
|
|
602
|
+
className: `${selectClass} flex-1`,
|
|
603
|
+
value: selected.blend,
|
|
604
|
+
onChange: (e) => controller.updateAdjustmentLayer(selected.id, { blend: e.target.value }),
|
|
605
|
+
children: BLEND_MODES.map((m) => /* @__PURE__ */ jsx4("option", { value: m.id, children: m.label }, m.id))
|
|
606
|
+
}
|
|
607
|
+
)
|
|
608
|
+
] }),
|
|
609
|
+
/* @__PURE__ */ jsx4(
|
|
610
|
+
ControlSlider,
|
|
611
|
+
{
|
|
612
|
+
spec: ADJUSTMENT_LAYER_OPACITY_CONTROL,
|
|
613
|
+
label: "Layer opacity",
|
|
614
|
+
value: selected.opacity,
|
|
615
|
+
onChange: (v) => controller.updateAdjustmentLayer(selected.id, { opacity: v })
|
|
616
|
+
}
|
|
617
|
+
),
|
|
618
|
+
editor(selected)
|
|
619
|
+
] })
|
|
620
|
+
] });
|
|
621
|
+
};
|
|
622
|
+
|
|
623
|
+
// src/image-editor/ImageEditor.tsx
|
|
624
|
+
import { Fragment, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
151
625
|
var ImageEditor = ({
|
|
152
626
|
editor: externalEditor,
|
|
153
627
|
src,
|
|
@@ -163,16 +637,16 @@ var ImageEditor = ({
|
|
|
163
637
|
const editor = externalEditor || internalEditor;
|
|
164
638
|
const { state, controller } = editor;
|
|
165
639
|
const legacyProcess = normalizeProcessVersion(state.adjustments.process) < LATEST_PROCESS_VERSION;
|
|
166
|
-
const canvasRef =
|
|
167
|
-
const containerRef =
|
|
168
|
-
const [activeTab, setActiveTab] =
|
|
169
|
-
const [activeCropRatio, setActiveCropRatio] =
|
|
170
|
-
const [inpaintPrompt, setInpaintPrompt] =
|
|
171
|
-
const [inpaintMaskMode, setInpaintMaskMode] =
|
|
172
|
-
const [isRemovingBg, setIsRemovingBg] =
|
|
173
|
-
const [isInpainting, setIsInpainting] =
|
|
174
|
-
const [aiError, setAiError] =
|
|
175
|
-
const [aiSuccess, setAiSuccess] =
|
|
640
|
+
const canvasRef = useRef3(null);
|
|
641
|
+
const containerRef = useRef3(null);
|
|
642
|
+
const [activeTab, setActiveTab] = useState5("crop");
|
|
643
|
+
const [activeCropRatio, setActiveCropRatio] = useState5(null);
|
|
644
|
+
const [inpaintPrompt, setInpaintPrompt] = useState5("");
|
|
645
|
+
const [inpaintMaskMode, setInpaintMaskMode] = useState5("annotations");
|
|
646
|
+
const [isRemovingBg, setIsRemovingBg] = useState5(false);
|
|
647
|
+
const [isInpainting, setIsInpainting] = useState5(false);
|
|
648
|
+
const [aiError, setAiError] = useState5(null);
|
|
649
|
+
const [aiSuccess, setAiSuccess] = useState5(null);
|
|
176
650
|
const effectiveOnInpaint = onInpaint || aiHooks?.onInpaint;
|
|
177
651
|
const effectiveOnRemoveBg = onRemoveBackground || aiHooks?.onRemoveBackground;
|
|
178
652
|
const handleRemoveBackground = async () => {
|
|
@@ -234,17 +708,17 @@ var ImageEditor = ({
|
|
|
234
708
|
setIsInpainting(false);
|
|
235
709
|
}
|
|
236
710
|
};
|
|
237
|
-
const [activeColor, setActiveColor] =
|
|
238
|
-
const [strokeWidth, setStrokeWidth] =
|
|
239
|
-
const [fontSize, setFontSize] =
|
|
240
|
-
const [textInput, setTextInput] =
|
|
241
|
-
const draftAnnotationRef =
|
|
242
|
-
const draftCropRef =
|
|
243
|
-
const cropDragInfoRef =
|
|
244
|
-
const annotationDragInfoRef =
|
|
245
|
-
const isDrawingRef =
|
|
246
|
-
const currentPathRef =
|
|
247
|
-
const dragStartPointRef =
|
|
711
|
+
const [activeColor, setActiveColor] = useState5("#ef4444");
|
|
712
|
+
const [strokeWidth, setStrokeWidth] = useState5(3);
|
|
713
|
+
const [fontSize, setFontSize] = useState5(24);
|
|
714
|
+
const [textInput, setTextInput] = useState5("");
|
|
715
|
+
const draftAnnotationRef = useRef3(null);
|
|
716
|
+
const draftCropRef = useRef3(null);
|
|
717
|
+
const cropDragInfoRef = useRef3(null);
|
|
718
|
+
const annotationDragInfoRef = useRef3(null);
|
|
719
|
+
const isDrawingRef = useRef3(false);
|
|
720
|
+
const currentPathRef = useRef3([]);
|
|
721
|
+
const dragStartPointRef = useRef3(null);
|
|
248
722
|
const renderCanvas = useCallback2(
|
|
249
723
|
async (draftAnnotation, draftCrop) => {
|
|
250
724
|
const canvas = canvasRef.current;
|
|
@@ -264,7 +738,7 @@ var ImageEditor = ({
|
|
|
264
738
|
},
|
|
265
739
|
[controller, activeTab]
|
|
266
740
|
);
|
|
267
|
-
|
|
741
|
+
useEffect4(() => {
|
|
268
742
|
const raf = typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : (cb) => setTimeout(cb, 0);
|
|
269
743
|
const caf = typeof cancelAnimationFrame !== "undefined" ? cancelAnimationFrame : (id) => clearTimeout(id);
|
|
270
744
|
const animId = raf(() => {
|
|
@@ -272,12 +746,12 @@ var ImageEditor = ({
|
|
|
272
746
|
});
|
|
273
747
|
return () => caf(animId);
|
|
274
748
|
}, [renderCanvas, state]);
|
|
275
|
-
|
|
749
|
+
useEffect4(() => {
|
|
276
750
|
if (activeTab === "crop" && editor.state.activeTool !== "crop") {
|
|
277
751
|
editor.setTool("crop");
|
|
278
752
|
}
|
|
279
753
|
}, [activeTab, editor]);
|
|
280
|
-
|
|
754
|
+
useEffect4(() => {
|
|
281
755
|
const container = containerRef.current;
|
|
282
756
|
if (!container) return;
|
|
283
757
|
if (typeof ResizeObserver !== "undefined") {
|
|
@@ -635,15 +1109,15 @@ var ImageEditor = ({
|
|
|
635
1109
|
link.click();
|
|
636
1110
|
}
|
|
637
1111
|
};
|
|
638
|
-
return /* @__PURE__ */
|
|
1112
|
+
return /* @__PURE__ */ jsxs5(
|
|
639
1113
|
"div",
|
|
640
1114
|
{
|
|
641
1115
|
className: `flex flex-col bg-slate-900 border border-slate-800 rounded-xl overflow-hidden shadow-2xl text-slate-100 ${className}`,
|
|
642
1116
|
style,
|
|
643
1117
|
children: [
|
|
644
|
-
/* @__PURE__ */
|
|
645
|
-
/* @__PURE__ */
|
|
646
|
-
/* @__PURE__ */
|
|
1118
|
+
/* @__PURE__ */ jsxs5("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: [
|
|
1119
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-1 bg-slate-900 p-1 rounded-lg border border-slate-800 text-xs", children: [
|
|
1120
|
+
/* @__PURE__ */ jsx5(
|
|
647
1121
|
"button",
|
|
648
1122
|
{
|
|
649
1123
|
type: "button",
|
|
@@ -655,7 +1129,7 @@ var ImageEditor = ({
|
|
|
655
1129
|
children: "Transform & Crop"
|
|
656
1130
|
}
|
|
657
1131
|
),
|
|
658
|
-
/* @__PURE__ */
|
|
1132
|
+
/* @__PURE__ */ jsx5(
|
|
659
1133
|
"button",
|
|
660
1134
|
{
|
|
661
1135
|
type: "button",
|
|
@@ -667,7 +1141,7 @@ var ImageEditor = ({
|
|
|
667
1141
|
children: "Adjustments"
|
|
668
1142
|
}
|
|
669
1143
|
),
|
|
670
|
-
/* @__PURE__ */
|
|
1144
|
+
/* @__PURE__ */ jsx5(
|
|
671
1145
|
"button",
|
|
672
1146
|
{
|
|
673
1147
|
type: "button",
|
|
@@ -679,7 +1153,7 @@ var ImageEditor = ({
|
|
|
679
1153
|
children: "Annotations"
|
|
680
1154
|
}
|
|
681
1155
|
),
|
|
682
|
-
/* @__PURE__ */
|
|
1156
|
+
/* @__PURE__ */ jsx5(
|
|
683
1157
|
"button",
|
|
684
1158
|
{
|
|
685
1159
|
type: "button",
|
|
@@ -691,7 +1165,7 @@ var ImageEditor = ({
|
|
|
691
1165
|
children: "Depth Mask"
|
|
692
1166
|
}
|
|
693
1167
|
),
|
|
694
|
-
/* @__PURE__ */
|
|
1168
|
+
/* @__PURE__ */ jsxs5(
|
|
695
1169
|
"button",
|
|
696
1170
|
{
|
|
697
1171
|
type: "button",
|
|
@@ -701,14 +1175,14 @@ var ImageEditor = ({
|
|
|
701
1175
|
},
|
|
702
1176
|
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"}`,
|
|
703
1177
|
children: [
|
|
704
|
-
/* @__PURE__ */
|
|
1178
|
+
/* @__PURE__ */ jsx5("svg", { className: "w-3.5 h-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx5("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" }) }),
|
|
705
1179
|
"AI Tools"
|
|
706
1180
|
]
|
|
707
1181
|
}
|
|
708
1182
|
)
|
|
709
1183
|
] }),
|
|
710
|
-
/* @__PURE__ */
|
|
711
|
-
/* @__PURE__ */
|
|
1184
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-2 text-xs", children: [
|
|
1185
|
+
/* @__PURE__ */ jsx5(
|
|
712
1186
|
"button",
|
|
713
1187
|
{
|
|
714
1188
|
type: "button",
|
|
@@ -719,7 +1193,7 @@ var ImageEditor = ({
|
|
|
719
1193
|
children: "\u21A9"
|
|
720
1194
|
}
|
|
721
1195
|
),
|
|
722
|
-
/* @__PURE__ */
|
|
1196
|
+
/* @__PURE__ */ jsx5(
|
|
723
1197
|
"button",
|
|
724
1198
|
{
|
|
725
1199
|
type: "button",
|
|
@@ -730,7 +1204,7 @@ var ImageEditor = ({
|
|
|
730
1204
|
children: "\u21AA"
|
|
731
1205
|
}
|
|
732
1206
|
),
|
|
733
|
-
/* @__PURE__ */
|
|
1207
|
+
/* @__PURE__ */ jsx5(
|
|
734
1208
|
"button",
|
|
735
1209
|
{
|
|
736
1210
|
type: "button",
|
|
@@ -739,8 +1213,8 @@ var ImageEditor = ({
|
|
|
739
1213
|
children: "Reset"
|
|
740
1214
|
}
|
|
741
1215
|
),
|
|
742
|
-
/* @__PURE__ */
|
|
743
|
-
/* @__PURE__ */
|
|
1216
|
+
/* @__PURE__ */ jsx5("div", { className: "h-4 w-px bg-slate-800 mx-1" }),
|
|
1217
|
+
/* @__PURE__ */ jsx5(
|
|
744
1218
|
"button",
|
|
745
1219
|
{
|
|
746
1220
|
type: "button",
|
|
@@ -750,11 +1224,11 @@ var ImageEditor = ({
|
|
|
750
1224
|
children: "-"
|
|
751
1225
|
}
|
|
752
1226
|
),
|
|
753
|
-
/* @__PURE__ */
|
|
1227
|
+
/* @__PURE__ */ jsxs5("span", { className: "font-mono text-slate-400 w-10 text-center", children: [
|
|
754
1228
|
Math.round(state.zoom * 100),
|
|
755
1229
|
"%"
|
|
756
1230
|
] }),
|
|
757
|
-
/* @__PURE__ */
|
|
1231
|
+
/* @__PURE__ */ jsx5(
|
|
758
1232
|
"button",
|
|
759
1233
|
{
|
|
760
1234
|
type: "button",
|
|
@@ -764,8 +1238,8 @@ var ImageEditor = ({
|
|
|
764
1238
|
children: "+"
|
|
765
1239
|
}
|
|
766
1240
|
),
|
|
767
|
-
/* @__PURE__ */
|
|
768
|
-
/* @__PURE__ */
|
|
1241
|
+
/* @__PURE__ */ jsx5("div", { className: "h-4 w-px bg-slate-800 mx-1" }),
|
|
1242
|
+
/* @__PURE__ */ jsx5(
|
|
769
1243
|
"button",
|
|
770
1244
|
{
|
|
771
1245
|
type: "button",
|
|
@@ -776,7 +1250,7 @@ var ImageEditor = ({
|
|
|
776
1250
|
children: "Hold Compare"
|
|
777
1251
|
}
|
|
778
1252
|
),
|
|
779
|
-
/* @__PURE__ */
|
|
1253
|
+
/* @__PURE__ */ jsx5(
|
|
780
1254
|
"button",
|
|
781
1255
|
{
|
|
782
1256
|
type: "button",
|
|
@@ -787,13 +1261,13 @@ var ImageEditor = ({
|
|
|
787
1261
|
)
|
|
788
1262
|
] })
|
|
789
1263
|
] }),
|
|
790
|
-
/* @__PURE__ */
|
|
791
|
-
/* @__PURE__ */
|
|
1264
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex flex-col md:flex-row flex-1 min-h-[520px] md:h-[560px]", children: [
|
|
1265
|
+
/* @__PURE__ */ jsx5(
|
|
792
1266
|
"div",
|
|
793
1267
|
{
|
|
794
1268
|
ref: containerRef,
|
|
795
1269
|
className: "relative flex-1 h-[360px] md:h-full min-h-[320px] bg-slate-950 overflow-hidden select-none",
|
|
796
|
-
children: /* @__PURE__ */
|
|
1270
|
+
children: /* @__PURE__ */ jsx5(
|
|
797
1271
|
"canvas",
|
|
798
1272
|
{
|
|
799
1273
|
ref: canvasRef,
|
|
@@ -806,11 +1280,11 @@ var ImageEditor = ({
|
|
|
806
1280
|
)
|
|
807
1281
|
}
|
|
808
1282
|
),
|
|
809
|
-
/* @__PURE__ */
|
|
810
|
-
activeTab === "crop" && /* @__PURE__ */
|
|
811
|
-
/* @__PURE__ */
|
|
812
|
-
/* @__PURE__ */
|
|
813
|
-
/* @__PURE__ */
|
|
1283
|
+
/* @__PURE__ */ jsxs5("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: [
|
|
1284
|
+
activeTab === "crop" && /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-4 text-xs", children: [
|
|
1285
|
+
/* @__PURE__ */ jsx5("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Aspect Ratio Presets" }),
|
|
1286
|
+
/* @__PURE__ */ jsxs5("div", { className: "grid grid-cols-3 gap-2", children: [
|
|
1287
|
+
/* @__PURE__ */ jsx5(
|
|
814
1288
|
"button",
|
|
815
1289
|
{
|
|
816
1290
|
type: "button",
|
|
@@ -822,7 +1296,7 @@ var ImageEditor = ({
|
|
|
822
1296
|
children: "Freeform"
|
|
823
1297
|
}
|
|
824
1298
|
),
|
|
825
|
-
/* @__PURE__ */
|
|
1299
|
+
/* @__PURE__ */ jsx5(
|
|
826
1300
|
"button",
|
|
827
1301
|
{
|
|
828
1302
|
type: "button",
|
|
@@ -834,7 +1308,7 @@ var ImageEditor = ({
|
|
|
834
1308
|
children: "1:1 Square"
|
|
835
1309
|
}
|
|
836
1310
|
),
|
|
837
|
-
/* @__PURE__ */
|
|
1311
|
+
/* @__PURE__ */ jsx5(
|
|
838
1312
|
"button",
|
|
839
1313
|
{
|
|
840
1314
|
type: "button",
|
|
@@ -846,7 +1320,7 @@ var ImageEditor = ({
|
|
|
846
1320
|
children: "4:3 Standard"
|
|
847
1321
|
}
|
|
848
1322
|
),
|
|
849
|
-
/* @__PURE__ */
|
|
1323
|
+
/* @__PURE__ */ jsx5(
|
|
850
1324
|
"button",
|
|
851
1325
|
{
|
|
852
1326
|
type: "button",
|
|
@@ -858,7 +1332,7 @@ var ImageEditor = ({
|
|
|
858
1332
|
children: "16:9 Wide"
|
|
859
1333
|
}
|
|
860
1334
|
),
|
|
861
|
-
/* @__PURE__ */
|
|
1335
|
+
/* @__PURE__ */ jsx5(
|
|
862
1336
|
"button",
|
|
863
1337
|
{
|
|
864
1338
|
type: "button",
|
|
@@ -870,7 +1344,7 @@ var ImageEditor = ({
|
|
|
870
1344
|
children: "3:2 Photo"
|
|
871
1345
|
}
|
|
872
1346
|
),
|
|
873
|
-
/* @__PURE__ */
|
|
1347
|
+
/* @__PURE__ */ jsx5(
|
|
874
1348
|
"button",
|
|
875
1349
|
{
|
|
876
1350
|
type: "button",
|
|
@@ -883,10 +1357,10 @@ var ImageEditor = ({
|
|
|
883
1357
|
}
|
|
884
1358
|
)
|
|
885
1359
|
] }),
|
|
886
|
-
/* @__PURE__ */
|
|
887
|
-
/* @__PURE__ */
|
|
888
|
-
/* @__PURE__ */
|
|
889
|
-
/* @__PURE__ */
|
|
1360
|
+
/* @__PURE__ */ jsx5("div", { className: "h-px bg-slate-800 my-1" }),
|
|
1361
|
+
/* @__PURE__ */ jsx5("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Orientation & Flips" }),
|
|
1362
|
+
/* @__PURE__ */ jsxs5("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
1363
|
+
/* @__PURE__ */ jsx5(
|
|
890
1364
|
"button",
|
|
891
1365
|
{
|
|
892
1366
|
type: "button",
|
|
@@ -895,7 +1369,7 @@ var ImageEditor = ({
|
|
|
895
1369
|
children: "Rotate -90\xB0"
|
|
896
1370
|
}
|
|
897
1371
|
),
|
|
898
|
-
/* @__PURE__ */
|
|
1372
|
+
/* @__PURE__ */ jsx5(
|
|
899
1373
|
"button",
|
|
900
1374
|
{
|
|
901
1375
|
type: "button",
|
|
@@ -904,7 +1378,7 @@ var ImageEditor = ({
|
|
|
904
1378
|
children: "Rotate +90\xB0"
|
|
905
1379
|
}
|
|
906
1380
|
),
|
|
907
|
-
/* @__PURE__ */
|
|
1381
|
+
/* @__PURE__ */ jsx5(
|
|
908
1382
|
"button",
|
|
909
1383
|
{
|
|
910
1384
|
type: "button",
|
|
@@ -913,7 +1387,7 @@ var ImageEditor = ({
|
|
|
913
1387
|
children: "Flip Horizontal"
|
|
914
1388
|
}
|
|
915
1389
|
),
|
|
916
|
-
/* @__PURE__ */
|
|
1390
|
+
/* @__PURE__ */ jsx5(
|
|
917
1391
|
"button",
|
|
918
1392
|
{
|
|
919
1393
|
type: "button",
|
|
@@ -924,10 +1398,10 @@ var ImageEditor = ({
|
|
|
924
1398
|
)
|
|
925
1399
|
] })
|
|
926
1400
|
] }),
|
|
927
|
-
activeTab === "adjust" && /* @__PURE__ */
|
|
928
|
-
/* @__PURE__ */
|
|
929
|
-
/* @__PURE__ */
|
|
930
|
-
/* @__PURE__ */
|
|
1401
|
+
activeTab === "adjust" && /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-3 text-xs", children: [
|
|
1402
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex items-center justify-between", children: [
|
|
1403
|
+
/* @__PURE__ */ jsx5("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Color Adjustments" }),
|
|
1404
|
+
/* @__PURE__ */ jsx5(
|
|
931
1405
|
"button",
|
|
932
1406
|
{
|
|
933
1407
|
type: "button",
|
|
@@ -937,174 +1411,49 @@ var ImageEditor = ({
|
|
|
937
1411
|
}
|
|
938
1412
|
)
|
|
939
1413
|
] }),
|
|
940
|
-
/* @__PURE__ */
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
] }),
|
|
945
|
-
/* @__PURE__ */ jsx(
|
|
946
|
-
"input",
|
|
947
|
-
{
|
|
948
|
-
type: "range",
|
|
949
|
-
min: "-100",
|
|
950
|
-
max: "100",
|
|
951
|
-
value: state.adjustments.brightness,
|
|
952
|
-
onChange: (e) => editor.setAdjustments({ brightness: Number(e.target.value) }),
|
|
953
|
-
className: "w-full accent-indigo-500"
|
|
954
|
-
}
|
|
955
|
-
)
|
|
956
|
-
] }),
|
|
957
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
958
|
-
/* @__PURE__ */ jsxs("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
959
|
-
/* @__PURE__ */ jsx("span", { children: "Contrast" }),
|
|
960
|
-
/* @__PURE__ */ jsx("span", { className: "font-mono", children: state.adjustments.contrast })
|
|
961
|
-
] }),
|
|
962
|
-
/* @__PURE__ */ jsx(
|
|
963
|
-
"input",
|
|
964
|
-
{
|
|
965
|
-
type: "range",
|
|
966
|
-
min: "-100",
|
|
967
|
-
max: "100",
|
|
968
|
-
value: state.adjustments.contrast,
|
|
969
|
-
onChange: (e) => editor.setAdjustments({ contrast: Number(e.target.value) }),
|
|
970
|
-
className: "w-full accent-indigo-500"
|
|
971
|
-
}
|
|
972
|
-
)
|
|
973
|
-
] }),
|
|
974
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
975
|
-
/* @__PURE__ */ jsxs("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
976
|
-
/* @__PURE__ */ jsx("span", { children: "Saturation" }),
|
|
977
|
-
/* @__PURE__ */ jsx("span", { className: "font-mono", children: state.adjustments.saturation })
|
|
978
|
-
] }),
|
|
979
|
-
/* @__PURE__ */ jsx(
|
|
980
|
-
"input",
|
|
1414
|
+
/* @__PURE__ */ jsx5(Histogram, { histogram: editor.histogram }),
|
|
1415
|
+
IMAGE_ADJUSTMENT_CONTROLS.map((spec) => /* @__PURE__ */ jsxs5(React4.Fragment, { children: [
|
|
1416
|
+
/* @__PURE__ */ jsx5(
|
|
1417
|
+
ControlSlider,
|
|
981
1418
|
{
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
onChange: (e) => editor.setAdjustments({ saturation: Number(e.target.value) }),
|
|
987
|
-
className: "w-full accent-indigo-500"
|
|
1419
|
+
spec,
|
|
1420
|
+
value: readAdjustmentControls(state.adjustments)[spec.id],
|
|
1421
|
+
disabled: spec.requiresProcess !== void 0 && legacyProcess,
|
|
1422
|
+
onChange: (v) => editor.setAdjustments({ [spec.id]: v })
|
|
988
1423
|
}
|
|
989
|
-
)
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
/* @__PURE__ */ jsxs("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
993
|
-
/* @__PURE__ */ jsx("span", { children: "Exposure" }),
|
|
994
|
-
/* @__PURE__ */ jsx("span", { className: "font-mono", children: state.adjustments.exposure })
|
|
995
|
-
] }),
|
|
996
|
-
/* @__PURE__ */ jsx(
|
|
997
|
-
"input",
|
|
998
|
-
{
|
|
999
|
-
type: "range",
|
|
1000
|
-
min: "-100",
|
|
1001
|
-
max: "100",
|
|
1002
|
-
value: state.adjustments.exposure,
|
|
1003
|
-
onChange: (e) => editor.setAdjustments({ exposure: Number(e.target.value) }),
|
|
1004
|
-
className: "w-full accent-indigo-500"
|
|
1005
|
-
}
|
|
1006
|
-
)
|
|
1007
|
-
] }),
|
|
1008
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
1009
|
-
/* @__PURE__ */ jsxs("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
1010
|
-
/* @__PURE__ */ jsx("span", { children: "Warmth / Temperature" }),
|
|
1011
|
-
/* @__PURE__ */ jsx("span", { className: "font-mono", children: state.adjustments.temperature })
|
|
1012
|
-
] }),
|
|
1013
|
-
/* @__PURE__ */ jsx(
|
|
1014
|
-
"input",
|
|
1015
|
-
{
|
|
1016
|
-
type: "range",
|
|
1017
|
-
min: "-100",
|
|
1018
|
-
max: "100",
|
|
1019
|
-
value: state.adjustments.temperature,
|
|
1020
|
-
onChange: (e) => editor.setAdjustments({ temperature: Number(e.target.value) }),
|
|
1021
|
-
className: "w-full accent-indigo-500"
|
|
1022
|
-
}
|
|
1023
|
-
)
|
|
1024
|
-
] }),
|
|
1025
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
1026
|
-
/* @__PURE__ */ jsxs("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
1027
|
-
/* @__PURE__ */ jsx("span", { children: "Tint" }),
|
|
1028
|
-
/* @__PURE__ */ jsx("span", { className: "font-mono", children: state.adjustments.tint ?? 0 })
|
|
1029
|
-
] }),
|
|
1030
|
-
/* @__PURE__ */ jsx(
|
|
1031
|
-
"input",
|
|
1424
|
+
),
|
|
1425
|
+
spec.id === "tint" && legacyProcess && /* @__PURE__ */ jsxs5(
|
|
1426
|
+
"div",
|
|
1032
1427
|
{
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1428
|
+
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",
|
|
1429
|
+
"data-testid": "image-editor-process-notice",
|
|
1430
|
+
children: [
|
|
1431
|
+
/* @__PURE__ */ jsx5("span", { children: "Legacy rendering (process 1). Tint needs the current process." }),
|
|
1432
|
+
/* @__PURE__ */ jsx5(
|
|
1433
|
+
"button",
|
|
1434
|
+
{
|
|
1435
|
+
type: "button",
|
|
1436
|
+
onClick: () => editor.upgradeProcess(),
|
|
1437
|
+
className: "shrink-0 rounded bg-amber-500/20 px-2 py-0.5 hover:bg-amber-500/30",
|
|
1438
|
+
children: "Update"
|
|
1439
|
+
}
|
|
1440
|
+
)
|
|
1441
|
+
]
|
|
1041
1442
|
}
|
|
1042
1443
|
)
|
|
1043
|
-
] }),
|
|
1044
|
-
|
|
1045
|
-
|
|
1444
|
+
] }, spec.id)),
|
|
1445
|
+
/* @__PURE__ */ jsx5("div", { className: "border-t border-slate-700/60 pt-3", children: /* @__PURE__ */ jsx5(
|
|
1446
|
+
AdjustmentLayersPanel,
|
|
1046
1447
|
{
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
/* @__PURE__ */ jsx("span", { children: "Legacy rendering (process 1). Tint needs the current process." }),
|
|
1051
|
-
/* @__PURE__ */ jsx(
|
|
1052
|
-
"button",
|
|
1053
|
-
{
|
|
1054
|
-
type: "button",
|
|
1055
|
-
onClick: () => editor.upgradeProcess(),
|
|
1056
|
-
className: "shrink-0 rounded bg-amber-500/20 px-2 py-0.5 hover:bg-amber-500/30",
|
|
1057
|
-
children: "Update"
|
|
1058
|
-
}
|
|
1059
|
-
)
|
|
1060
|
-
]
|
|
1448
|
+
controller,
|
|
1449
|
+
layers: state.adjustments.layers,
|
|
1450
|
+
histogram: editor.histogram
|
|
1061
1451
|
}
|
|
1062
|
-
)
|
|
1063
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
1064
|
-
/* @__PURE__ */ jsxs("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
1065
|
-
/* @__PURE__ */ jsx("span", { children: "Blur" }),
|
|
1066
|
-
/* @__PURE__ */ jsxs("span", { className: "font-mono", children: [
|
|
1067
|
-
state.adjustments.blur,
|
|
1068
|
-
"px"
|
|
1069
|
-
] })
|
|
1070
|
-
] }),
|
|
1071
|
-
/* @__PURE__ */ jsx(
|
|
1072
|
-
"input",
|
|
1073
|
-
{
|
|
1074
|
-
type: "range",
|
|
1075
|
-
min: "0",
|
|
1076
|
-
max: "30",
|
|
1077
|
-
value: state.adjustments.blur,
|
|
1078
|
-
onChange: (e) => editor.setAdjustments({ blur: Number(e.target.value) }),
|
|
1079
|
-
className: "w-full accent-indigo-500"
|
|
1080
|
-
}
|
|
1081
|
-
)
|
|
1082
|
-
] }),
|
|
1083
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
1084
|
-
/* @__PURE__ */ jsxs("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
1085
|
-
/* @__PURE__ */ jsx("span", { children: "Opacity" }),
|
|
1086
|
-
/* @__PURE__ */ jsxs("span", { className: "font-mono", children: [
|
|
1087
|
-
Math.round(state.adjustments.opacity * 100),
|
|
1088
|
-
"%"
|
|
1089
|
-
] })
|
|
1090
|
-
] }),
|
|
1091
|
-
/* @__PURE__ */ jsx(
|
|
1092
|
-
"input",
|
|
1093
|
-
{
|
|
1094
|
-
type: "range",
|
|
1095
|
-
min: "0",
|
|
1096
|
-
max: "1",
|
|
1097
|
-
step: "0.05",
|
|
1098
|
-
value: state.adjustments.opacity,
|
|
1099
|
-
onChange: (e) => editor.setAdjustments({ opacity: Number(e.target.value) }),
|
|
1100
|
-
className: "w-full accent-indigo-500"
|
|
1101
|
-
}
|
|
1102
|
-
)
|
|
1103
|
-
] })
|
|
1452
|
+
) })
|
|
1104
1453
|
] }),
|
|
1105
|
-
activeTab === "annotate" && /* @__PURE__ */
|
|
1106
|
-
/* @__PURE__ */
|
|
1107
|
-
/* @__PURE__ */
|
|
1454
|
+
activeTab === "annotate" && /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-3 text-xs", children: [
|
|
1455
|
+
/* @__PURE__ */ jsx5("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Draw & Annotate" }),
|
|
1456
|
+
/* @__PURE__ */ jsx5("div", { className: "grid grid-cols-3 gap-1.5", children: ["pen", "rect", "circle", "arrow", "line"].map((t) => /* @__PURE__ */ jsx5(
|
|
1108
1457
|
"button",
|
|
1109
1458
|
{
|
|
1110
1459
|
type: "button",
|
|
@@ -1114,9 +1463,9 @@ var ImageEditor = ({
|
|
|
1114
1463
|
},
|
|
1115
1464
|
t
|
|
1116
1465
|
)) }),
|
|
1117
|
-
/* @__PURE__ */
|
|
1118
|
-
/* @__PURE__ */
|
|
1119
|
-
/* @__PURE__ */
|
|
1466
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-2 mt-2", children: [
|
|
1467
|
+
/* @__PURE__ */ jsx5("label", { className: "text-slate-400", children: "Color:" }),
|
|
1468
|
+
/* @__PURE__ */ jsx5(
|
|
1120
1469
|
"input",
|
|
1121
1470
|
{
|
|
1122
1471
|
type: "color",
|
|
@@ -1125,15 +1474,15 @@ var ImageEditor = ({
|
|
|
1125
1474
|
className: "w-8 h-8 rounded border border-slate-700 cursor-pointer bg-transparent"
|
|
1126
1475
|
}
|
|
1127
1476
|
),
|
|
1128
|
-
/* @__PURE__ */
|
|
1129
|
-
/* @__PURE__ */
|
|
1130
|
-
/* @__PURE__ */
|
|
1131
|
-
/* @__PURE__ */
|
|
1477
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex-1 ml-2", children: [
|
|
1478
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex justify-between text-slate-400 mb-0.5", children: [
|
|
1479
|
+
/* @__PURE__ */ jsx5("span", { children: "Width:" }),
|
|
1480
|
+
/* @__PURE__ */ jsxs5("span", { className: "font-mono", children: [
|
|
1132
1481
|
strokeWidth,
|
|
1133
1482
|
"px"
|
|
1134
1483
|
] })
|
|
1135
1484
|
] }),
|
|
1136
|
-
/* @__PURE__ */
|
|
1485
|
+
/* @__PURE__ */ jsx5(
|
|
1137
1486
|
"input",
|
|
1138
1487
|
{
|
|
1139
1488
|
type: "range",
|
|
@@ -1146,10 +1495,10 @@ var ImageEditor = ({
|
|
|
1146
1495
|
)
|
|
1147
1496
|
] })
|
|
1148
1497
|
] }),
|
|
1149
|
-
/* @__PURE__ */
|
|
1150
|
-
/* @__PURE__ */
|
|
1151
|
-
/* @__PURE__ */
|
|
1152
|
-
/* @__PURE__ */
|
|
1498
|
+
/* @__PURE__ */ jsx5("div", { className: "h-px bg-slate-800 my-1" }),
|
|
1499
|
+
/* @__PURE__ */ jsx5("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Add Text" }),
|
|
1500
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex gap-2", children: [
|
|
1501
|
+
/* @__PURE__ */ jsx5(
|
|
1153
1502
|
"input",
|
|
1154
1503
|
{
|
|
1155
1504
|
type: "text",
|
|
@@ -1160,7 +1509,7 @@ var ImageEditor = ({
|
|
|
1160
1509
|
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"
|
|
1161
1510
|
}
|
|
1162
1511
|
),
|
|
1163
|
-
/* @__PURE__ */
|
|
1512
|
+
/* @__PURE__ */ jsx5(
|
|
1164
1513
|
"button",
|
|
1165
1514
|
{
|
|
1166
1515
|
type: "button",
|
|
@@ -1170,14 +1519,14 @@ var ImageEditor = ({
|
|
|
1170
1519
|
}
|
|
1171
1520
|
)
|
|
1172
1521
|
] }),
|
|
1173
|
-
/* @__PURE__ */
|
|
1174
|
-
/* @__PURE__ */
|
|
1175
|
-
/* @__PURE__ */
|
|
1522
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex items-center justify-between text-slate-400", children: [
|
|
1523
|
+
/* @__PURE__ */ jsx5("span", { children: "Font Size:" }),
|
|
1524
|
+
/* @__PURE__ */ jsxs5("span", { className: "font-mono", children: [
|
|
1176
1525
|
fontSize,
|
|
1177
1526
|
"px"
|
|
1178
1527
|
] })
|
|
1179
1528
|
] }),
|
|
1180
|
-
/* @__PURE__ */
|
|
1529
|
+
/* @__PURE__ */ jsx5(
|
|
1181
1530
|
"input",
|
|
1182
1531
|
{
|
|
1183
1532
|
type: "range",
|
|
@@ -1188,14 +1537,14 @@ var ImageEditor = ({
|
|
|
1188
1537
|
className: "w-full accent-indigo-500"
|
|
1189
1538
|
}
|
|
1190
1539
|
),
|
|
1191
|
-
/* @__PURE__ */
|
|
1192
|
-
/* @__PURE__ */
|
|
1193
|
-
/* @__PURE__ */
|
|
1540
|
+
/* @__PURE__ */ jsx5("div", { className: "h-px bg-slate-800 my-1" }),
|
|
1541
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex items-center justify-between", children: [
|
|
1542
|
+
/* @__PURE__ */ jsxs5("span", { className: "text-slate-400", children: [
|
|
1194
1543
|
"Layers (",
|
|
1195
1544
|
state.annotations.length,
|
|
1196
1545
|
")"
|
|
1197
1546
|
] }),
|
|
1198
|
-
state.selectedAnnotationId && /* @__PURE__ */
|
|
1547
|
+
state.selectedAnnotationId && /* @__PURE__ */ jsx5(
|
|
1199
1548
|
"button",
|
|
1200
1549
|
{
|
|
1201
1550
|
type: "button",
|
|
@@ -1204,7 +1553,7 @@ var ImageEditor = ({
|
|
|
1204
1553
|
children: "Delete Selected"
|
|
1205
1554
|
}
|
|
1206
1555
|
),
|
|
1207
|
-
state.annotations.length > 0 && /* @__PURE__ */
|
|
1556
|
+
state.annotations.length > 0 && /* @__PURE__ */ jsx5(
|
|
1208
1557
|
"button",
|
|
1209
1558
|
{
|
|
1210
1559
|
type: "button",
|
|
@@ -1215,10 +1564,10 @@ var ImageEditor = ({
|
|
|
1215
1564
|
)
|
|
1216
1565
|
] })
|
|
1217
1566
|
] }),
|
|
1218
|
-
activeTab === "depth" && /* @__PURE__ */
|
|
1219
|
-
/* @__PURE__ */
|
|
1220
|
-
/* @__PURE__ */
|
|
1221
|
-
/* @__PURE__ */
|
|
1567
|
+
activeTab === "depth" && /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-3 text-xs", children: [
|
|
1568
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex items-center justify-between", children: [
|
|
1569
|
+
/* @__PURE__ */ jsx5("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Depth Masking" }),
|
|
1570
|
+
/* @__PURE__ */ jsx5(
|
|
1222
1571
|
"button",
|
|
1223
1572
|
{
|
|
1224
1573
|
type: "button",
|
|
@@ -1228,8 +1577,8 @@ var ImageEditor = ({
|
|
|
1228
1577
|
}
|
|
1229
1578
|
)
|
|
1230
1579
|
] }),
|
|
1231
|
-
/* @__PURE__ */
|
|
1232
|
-
/* @__PURE__ */
|
|
1580
|
+
/* @__PURE__ */ jsxs5("label", { className: "flex items-center gap-2 cursor-pointer text-slate-300", children: [
|
|
1581
|
+
/* @__PURE__ */ jsx5(
|
|
1233
1582
|
"input",
|
|
1234
1583
|
{
|
|
1235
1584
|
type: "checkbox",
|
|
@@ -1238,12 +1587,12 @@ var ImageEditor = ({
|
|
|
1238
1587
|
className: "rounded accent-indigo-500"
|
|
1239
1588
|
}
|
|
1240
1589
|
),
|
|
1241
|
-
/* @__PURE__ */
|
|
1590
|
+
/* @__PURE__ */ jsx5("span", { children: "Enable Depth / Alpha Mask" })
|
|
1242
1591
|
] }),
|
|
1243
|
-
state.depthMask.enabled && /* @__PURE__ */
|
|
1244
|
-
/* @__PURE__ */
|
|
1245
|
-
/* @__PURE__ */
|
|
1246
|
-
/* @__PURE__ */
|
|
1592
|
+
state.depthMask.enabled && /* @__PURE__ */ jsxs5(Fragment, { children: [
|
|
1593
|
+
/* @__PURE__ */ jsxs5("div", { children: [
|
|
1594
|
+
/* @__PURE__ */ jsx5("label", { className: "text-slate-400 block mb-1", children: "Mask Image URL:" }),
|
|
1595
|
+
/* @__PURE__ */ jsx5(
|
|
1247
1596
|
"input",
|
|
1248
1597
|
{
|
|
1249
1598
|
type: "text",
|
|
@@ -1254,8 +1603,8 @@ var ImageEditor = ({
|
|
|
1254
1603
|
}
|
|
1255
1604
|
)
|
|
1256
1605
|
] }),
|
|
1257
|
-
/* @__PURE__ */
|
|
1258
|
-
/* @__PURE__ */
|
|
1606
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex gap-2", children: [
|
|
1607
|
+
/* @__PURE__ */ jsx5(
|
|
1259
1608
|
"button",
|
|
1260
1609
|
{
|
|
1261
1610
|
type: "button",
|
|
@@ -1264,7 +1613,7 @@ var ImageEditor = ({
|
|
|
1264
1613
|
children: "Threshold Mode"
|
|
1265
1614
|
}
|
|
1266
1615
|
),
|
|
1267
|
-
/* @__PURE__ */
|
|
1616
|
+
/* @__PURE__ */ jsx5(
|
|
1268
1617
|
"button",
|
|
1269
1618
|
{
|
|
1270
1619
|
type: "button",
|
|
@@ -1274,12 +1623,12 @@ var ImageEditor = ({
|
|
|
1274
1623
|
}
|
|
1275
1624
|
)
|
|
1276
1625
|
] }),
|
|
1277
|
-
/* @__PURE__ */
|
|
1278
|
-
/* @__PURE__ */
|
|
1279
|
-
/* @__PURE__ */
|
|
1280
|
-
/* @__PURE__ */
|
|
1626
|
+
/* @__PURE__ */ jsxs5("div", { children: [
|
|
1627
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
1628
|
+
/* @__PURE__ */ jsx5("span", { children: "Depth Start (Lower):" }),
|
|
1629
|
+
/* @__PURE__ */ jsx5("span", { className: "font-mono", children: state.depthMask.depthRange[0] })
|
|
1281
1630
|
] }),
|
|
1282
|
-
/* @__PURE__ */
|
|
1631
|
+
/* @__PURE__ */ jsx5(
|
|
1283
1632
|
"input",
|
|
1284
1633
|
{
|
|
1285
1634
|
type: "range",
|
|
@@ -1293,12 +1642,12 @@ var ImageEditor = ({
|
|
|
1293
1642
|
}
|
|
1294
1643
|
)
|
|
1295
1644
|
] }),
|
|
1296
|
-
/* @__PURE__ */
|
|
1297
|
-
/* @__PURE__ */
|
|
1298
|
-
/* @__PURE__ */
|
|
1299
|
-
/* @__PURE__ */
|
|
1645
|
+
/* @__PURE__ */ jsxs5("div", { children: [
|
|
1646
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
1647
|
+
/* @__PURE__ */ jsx5("span", { children: "Depth Stop (Upper):" }),
|
|
1648
|
+
/* @__PURE__ */ jsx5("span", { className: "font-mono", children: state.depthMask.depthRange[1] })
|
|
1300
1649
|
] }),
|
|
1301
|
-
/* @__PURE__ */
|
|
1650
|
+
/* @__PURE__ */ jsx5(
|
|
1302
1651
|
"input",
|
|
1303
1652
|
{
|
|
1304
1653
|
type: "range",
|
|
@@ -1312,12 +1661,12 @@ var ImageEditor = ({
|
|
|
1312
1661
|
}
|
|
1313
1662
|
)
|
|
1314
1663
|
] }),
|
|
1315
|
-
/* @__PURE__ */
|
|
1316
|
-
/* @__PURE__ */
|
|
1317
|
-
/* @__PURE__ */
|
|
1318
|
-
/* @__PURE__ */
|
|
1664
|
+
/* @__PURE__ */ jsxs5("div", { children: [
|
|
1665
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
1666
|
+
/* @__PURE__ */ jsx5("span", { children: "Softness:" }),
|
|
1667
|
+
/* @__PURE__ */ jsx5("span", { className: "font-mono", children: state.depthMask.softness })
|
|
1319
1668
|
] }),
|
|
1320
|
-
/* @__PURE__ */
|
|
1669
|
+
/* @__PURE__ */ jsx5(
|
|
1321
1670
|
"input",
|
|
1322
1671
|
{
|
|
1323
1672
|
type: "range",
|
|
@@ -1329,8 +1678,8 @@ var ImageEditor = ({
|
|
|
1329
1678
|
}
|
|
1330
1679
|
)
|
|
1331
1680
|
] }),
|
|
1332
|
-
/* @__PURE__ */
|
|
1333
|
-
/* @__PURE__ */
|
|
1681
|
+
/* @__PURE__ */ jsxs5("label", { className: "flex items-center gap-2 cursor-pointer text-slate-300 mt-1", children: [
|
|
1682
|
+
/* @__PURE__ */ jsx5(
|
|
1334
1683
|
"input",
|
|
1335
1684
|
{
|
|
1336
1685
|
type: "checkbox",
|
|
@@ -1339,52 +1688,52 @@ var ImageEditor = ({
|
|
|
1339
1688
|
className: "rounded accent-indigo-500"
|
|
1340
1689
|
}
|
|
1341
1690
|
),
|
|
1342
|
-
/* @__PURE__ */
|
|
1691
|
+
/* @__PURE__ */ jsx5("span", { children: "Invert Mask" })
|
|
1343
1692
|
] })
|
|
1344
1693
|
] })
|
|
1345
1694
|
] }),
|
|
1346
|
-
activeTab === "ai" && /* @__PURE__ */
|
|
1347
|
-
/* @__PURE__ */
|
|
1348
|
-
/* @__PURE__ */
|
|
1349
|
-
/* @__PURE__ */
|
|
1350
|
-
/* @__PURE__ */
|
|
1695
|
+
activeTab === "ai" && /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-4 text-xs", children: [
|
|
1696
|
+
/* @__PURE__ */ jsxs5("div", { className: "p-3 rounded-lg bg-slate-950/60 border border-slate-800 flex flex-col gap-2.5", children: [
|
|
1697
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex items-center justify-between", children: [
|
|
1698
|
+
/* @__PURE__ */ jsxs5("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px] flex items-center gap-1.5", children: [
|
|
1699
|
+
/* @__PURE__ */ jsx5("svg", { className: "w-3.5 h-3.5 text-indigo-400", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx5("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" }) }),
|
|
1351
1700
|
"Background Removal"
|
|
1352
1701
|
] }),
|
|
1353
|
-
isRemovingBg && /* @__PURE__ */
|
|
1702
|
+
isRemovingBg && /* @__PURE__ */ jsx5("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..." })
|
|
1354
1703
|
] }),
|
|
1355
|
-
/* @__PURE__ */
|
|
1356
|
-
/* @__PURE__ */
|
|
1704
|
+
/* @__PURE__ */ jsx5("p", { className: "text-slate-400 text-[11px] leading-relaxed", children: "Isolate foreground subject with AI alpha transparency segmentation." }),
|
|
1705
|
+
/* @__PURE__ */ jsx5(
|
|
1357
1706
|
"button",
|
|
1358
1707
|
{
|
|
1359
1708
|
type: "button",
|
|
1360
1709
|
onClick: handleRemoveBackground,
|
|
1361
1710
|
disabled: isRemovingBg || isInpainting,
|
|
1362
1711
|
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",
|
|
1363
|
-
children: isRemovingBg ? /* @__PURE__ */
|
|
1364
|
-
/* @__PURE__ */
|
|
1712
|
+
children: isRemovingBg ? /* @__PURE__ */ jsxs5(Fragment, { children: [
|
|
1713
|
+
/* @__PURE__ */ jsx5("div", { className: "w-3.5 h-3.5 border-2 border-white/30 border-t-white rounded-full animate-spin" }),
|
|
1365
1714
|
"Removing Background..."
|
|
1366
|
-
] }) : /* @__PURE__ */
|
|
1367
|
-
/* @__PURE__ */
|
|
1715
|
+
] }) : /* @__PURE__ */ jsxs5(Fragment, { children: [
|
|
1716
|
+
/* @__PURE__ */ jsx5("svg", { className: "w-3.5 h-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx5("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" }) }),
|
|
1368
1717
|
"Remove Background"
|
|
1369
1718
|
] })
|
|
1370
1719
|
}
|
|
1371
1720
|
)
|
|
1372
1721
|
] }),
|
|
1373
|
-
/* @__PURE__ */
|
|
1374
|
-
/* @__PURE__ */
|
|
1375
|
-
/* @__PURE__ */
|
|
1376
|
-
/* @__PURE__ */
|
|
1377
|
-
/* @__PURE__ */
|
|
1378
|
-
/* @__PURE__ */
|
|
1722
|
+
/* @__PURE__ */ jsxs5("div", { className: "p-3 rounded-lg bg-slate-950/60 border border-slate-800 flex flex-col gap-2.5", children: [
|
|
1723
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex items-center justify-between", children: [
|
|
1724
|
+
/* @__PURE__ */ jsxs5("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px] flex items-center gap-1.5", children: [
|
|
1725
|
+
/* @__PURE__ */ jsxs5("svg", { className: "w-3.5 h-3.5 text-purple-400", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
|
|
1726
|
+
/* @__PURE__ */ jsx5("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" }),
|
|
1727
|
+
/* @__PURE__ */ jsx5("path", { d: "m14 7 3 3" })
|
|
1379
1728
|
] }),
|
|
1380
1729
|
"Generative Inpainting"
|
|
1381
1730
|
] }),
|
|
1382
|
-
isInpainting && /* @__PURE__ */
|
|
1731
|
+
isInpainting && /* @__PURE__ */ jsx5("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..." })
|
|
1383
1732
|
] }),
|
|
1384
|
-
/* @__PURE__ */
|
|
1385
|
-
/* @__PURE__ */
|
|
1386
|
-
/* @__PURE__ */
|
|
1387
|
-
/* @__PURE__ */
|
|
1733
|
+
/* @__PURE__ */ jsx5("p", { className: "text-slate-400 text-[11px] leading-relaxed", children: "Replace, modify, or fill selected regions using prompt-guided generative inpainting." }),
|
|
1734
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-1", children: [
|
|
1735
|
+
/* @__PURE__ */ jsx5("label", { htmlFor: "ai-inpaint-prompt-react", className: "text-slate-300 text-[11px] font-medium", children: "Prompt" }),
|
|
1736
|
+
/* @__PURE__ */ jsx5(
|
|
1388
1737
|
"textarea",
|
|
1389
1738
|
{
|
|
1390
1739
|
id: "ai-inpaint-prompt-react",
|
|
@@ -1396,10 +1745,10 @@ var ImageEditor = ({
|
|
|
1396
1745
|
}
|
|
1397
1746
|
)
|
|
1398
1747
|
] }),
|
|
1399
|
-
/* @__PURE__ */
|
|
1400
|
-
/* @__PURE__ */
|
|
1401
|
-
/* @__PURE__ */
|
|
1402
|
-
/* @__PURE__ */
|
|
1748
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-1", children: [
|
|
1749
|
+
/* @__PURE__ */ jsx5("span", { className: "text-slate-300 text-[11px] font-medium", children: "Target Mask Region" }),
|
|
1750
|
+
/* @__PURE__ */ jsxs5("div", { className: "grid grid-cols-2 gap-1.5", children: [
|
|
1751
|
+
/* @__PURE__ */ jsxs5(
|
|
1403
1752
|
"button",
|
|
1404
1753
|
{
|
|
1405
1754
|
type: "button",
|
|
@@ -1412,7 +1761,7 @@ var ImageEditor = ({
|
|
|
1412
1761
|
]
|
|
1413
1762
|
}
|
|
1414
1763
|
),
|
|
1415
|
-
/* @__PURE__ */
|
|
1764
|
+
/* @__PURE__ */ jsxs5(
|
|
1416
1765
|
"button",
|
|
1417
1766
|
{
|
|
1418
1767
|
type: "button",
|
|
@@ -1427,25 +1776,25 @@ var ImageEditor = ({
|
|
|
1427
1776
|
)
|
|
1428
1777
|
] })
|
|
1429
1778
|
] }),
|
|
1430
|
-
/* @__PURE__ */
|
|
1779
|
+
/* @__PURE__ */ jsx5(
|
|
1431
1780
|
"button",
|
|
1432
1781
|
{
|
|
1433
1782
|
type: "button",
|
|
1434
1783
|
onClick: handleInpaint,
|
|
1435
1784
|
disabled: isInpainting || isRemovingBg || !inpaintPrompt.trim(),
|
|
1436
1785
|
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",
|
|
1437
|
-
children: isInpainting ? /* @__PURE__ */
|
|
1438
|
-
/* @__PURE__ */
|
|
1786
|
+
children: isInpainting ? /* @__PURE__ */ jsxs5(Fragment, { children: [
|
|
1787
|
+
/* @__PURE__ */ jsx5("div", { className: "w-3.5 h-3.5 border-2 border-white/30 border-t-white rounded-full animate-spin" }),
|
|
1439
1788
|
"Generating Inpaint..."
|
|
1440
|
-
] }) : /* @__PURE__ */
|
|
1441
|
-
/* @__PURE__ */
|
|
1789
|
+
] }) : /* @__PURE__ */ jsxs5(Fragment, { children: [
|
|
1790
|
+
/* @__PURE__ */ jsx5("svg", { className: "w-3.5 h-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx5("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" }) }),
|
|
1442
1791
|
"Apply Inpainting"
|
|
1443
1792
|
] })
|
|
1444
1793
|
}
|
|
1445
1794
|
)
|
|
1446
1795
|
] }),
|
|
1447
|
-
aiError && /* @__PURE__ */
|
|
1448
|
-
aiSuccess && /* @__PURE__ */
|
|
1796
|
+
aiError && /* @__PURE__ */ jsx5("div", { className: "p-2.5 rounded-lg bg-rose-950/60 border border-rose-800 text-rose-300 text-[11px]", children: aiError }),
|
|
1797
|
+
aiSuccess && /* @__PURE__ */ jsx5("div", { className: "p-2.5 rounded-lg bg-emerald-950/60 border border-emerald-800 text-emerald-300 text-[11px]", children: aiSuccess })
|
|
1449
1798
|
] })
|
|
1450
1799
|
] })
|
|
1451
1800
|
] })
|
|
@@ -1456,6 +1805,11 @@ var ImageEditor = ({
|
|
|
1456
1805
|
|
|
1457
1806
|
export {
|
|
1458
1807
|
useImageEditor,
|
|
1808
|
+
ControlSlider,
|
|
1809
|
+
Histogram,
|
|
1810
|
+
useImageHistogram,
|
|
1811
|
+
CurvesEditor,
|
|
1812
|
+
AdjustmentLayersPanel,
|
|
1459
1813
|
ImageEditor
|
|
1460
1814
|
};
|
|
1461
|
-
//# sourceMappingURL=chunk-
|
|
1815
|
+
//# sourceMappingURL=chunk-XTFR3IMK.js.map
|