@pixerate/editor-react 0.14.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 +6 -1
- package/dist/canvas/index.cjs +352 -118
- package/dist/canvas/index.cjs.map +1 -1
- package/dist/canvas/index.d.cts +112 -8
- package/dist/canvas/index.d.ts +112 -8
- package/dist/canvas/index.js +258 -19
- package/dist/canvas/index.js.map +1 -1
- package/dist/chunk-4ZC2WCUO.js +196 -0
- package/dist/chunk-4ZC2WCUO.js.map +1 -0
- package/dist/chunk-7SPRQZ6H.js +216 -0
- package/dist/chunk-7SPRQZ6H.js.map +1 -0
- package/dist/{chunk-25S5EHH4.js → chunk-FYIPXHAE.js} +22 -13
- package/dist/chunk-FYIPXHAE.js.map +1 -0
- package/dist/{chunk-TL27COKM.js → chunk-XTFR3IMK.js} +683 -286
- 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/commands/index.cjs +232 -0
- package/dist/commands/index.cjs.map +1 -0
- package/dist/commands/index.d.cts +69 -0
- package/dist/commands/index.d.ts +69 -0
- package/dist/commands/index.js +13 -0
- package/dist/commands/index.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 +688 -299
- package/dist/image-editor/index.cjs.map +1 -1
- package/dist/image-editor/index.d.cts +65 -5
- package/dist/image-editor/index.d.ts +65 -5
- package/dist/image-editor/index.js +13 -3
- package/dist/index.cjs +2200 -683
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +201 -23
- package/dist/index.d.ts +201 -23
- package/dist/index.js +743 -129
- 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 +9 -7
- package/dist/spreadsheet/index.d.ts +9 -7
- package/dist/spreadsheet/index.js +1 -1
- package/package.json +26 -2
- package/dist/chunk-25S5EHH4.js.map +0 -1
- package/dist/chunk-TL27COKM.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);
|
|
@@ -42,6 +44,21 @@ function useImageEditor(options = {}) {
|
|
|
42
44
|
[controller]
|
|
43
45
|
);
|
|
44
46
|
const resetAdjustments = useCallback(() => controller.resetAdjustments(), [controller]);
|
|
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
|
+
);
|
|
45
62
|
const setDepthMask = useCallback((partial) => controller.setDepthMask(partial), [controller]);
|
|
46
63
|
const resetDepthMask = useCallback(() => controller.resetDepthMask(), [controller]);
|
|
47
64
|
const addAnnotation = useCallback((ann) => controller.addAnnotation(ann), [controller]);
|
|
@@ -102,6 +119,13 @@ function useImageEditor(options = {}) {
|
|
|
102
119
|
flipVertical,
|
|
103
120
|
setAdjustments,
|
|
104
121
|
resetAdjustments,
|
|
122
|
+
upgradeProcess,
|
|
123
|
+
addAdjustmentLayer,
|
|
124
|
+
updateAdjustmentLayer,
|
|
125
|
+
removeAdjustmentLayer,
|
|
126
|
+
moveAdjustmentLayer,
|
|
127
|
+
toggleAdjustmentLayer,
|
|
128
|
+
histogram,
|
|
105
129
|
setDepthMask,
|
|
106
130
|
resetDepthMask,
|
|
107
131
|
addAnnotation,
|
|
@@ -130,20 +154,474 @@ function useImageEditor(options = {}) {
|
|
|
130
154
|
applyBackgroundRemovedImage
|
|
131
155
|
}),
|
|
132
156
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
133
|
-
[controller, state, historyTicks]
|
|
157
|
+
[controller, state, historyTicks, histogram]
|
|
134
158
|
);
|
|
135
159
|
}
|
|
136
160
|
|
|
137
161
|
// src/image-editor/ImageEditor.tsx
|
|
138
|
-
import { useRef as
|
|
162
|
+
import React4, { useRef as useRef3, useEffect as useEffect4, useState as useState5, useCallback as useCallback2 } from "react";
|
|
139
163
|
import {
|
|
140
164
|
canvasToImagePoint,
|
|
141
165
|
hitTestCrop,
|
|
142
166
|
getCropCursor,
|
|
143
167
|
calculateCropDrag,
|
|
144
|
-
hitTestAnnotation
|
|
168
|
+
hitTestAnnotation,
|
|
169
|
+
normalizeProcessVersion,
|
|
170
|
+
LATEST_PROCESS_VERSION,
|
|
171
|
+
IMAGE_ADJUSTMENT_CONTROLS,
|
|
172
|
+
readAdjustmentControls
|
|
145
173
|
} from "@pixerate/editor/image-editor";
|
|
146
|
-
|
|
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";
|
|
147
625
|
var ImageEditor = ({
|
|
148
626
|
editor: externalEditor,
|
|
149
627
|
src,
|
|
@@ -158,16 +636,17 @@ var ImageEditor = ({
|
|
|
158
636
|
const internalEditor = useImageEditor({ image: src });
|
|
159
637
|
const editor = externalEditor || internalEditor;
|
|
160
638
|
const { state, controller } = editor;
|
|
161
|
-
const
|
|
162
|
-
const
|
|
163
|
-
const
|
|
164
|
-
const [
|
|
165
|
-
const [
|
|
166
|
-
const [
|
|
167
|
-
const [
|
|
168
|
-
const [
|
|
169
|
-
const [
|
|
170
|
-
const [
|
|
639
|
+
const legacyProcess = normalizeProcessVersion(state.adjustments.process) < LATEST_PROCESS_VERSION;
|
|
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);
|
|
171
650
|
const effectiveOnInpaint = onInpaint || aiHooks?.onInpaint;
|
|
172
651
|
const effectiveOnRemoveBg = onRemoveBackground || aiHooks?.onRemoveBackground;
|
|
173
652
|
const handleRemoveBackground = async () => {
|
|
@@ -229,17 +708,17 @@ var ImageEditor = ({
|
|
|
229
708
|
setIsInpainting(false);
|
|
230
709
|
}
|
|
231
710
|
};
|
|
232
|
-
const [activeColor, setActiveColor] =
|
|
233
|
-
const [strokeWidth, setStrokeWidth] =
|
|
234
|
-
const [fontSize, setFontSize] =
|
|
235
|
-
const [textInput, setTextInput] =
|
|
236
|
-
const draftAnnotationRef =
|
|
237
|
-
const draftCropRef =
|
|
238
|
-
const cropDragInfoRef =
|
|
239
|
-
const annotationDragInfoRef =
|
|
240
|
-
const isDrawingRef =
|
|
241
|
-
const currentPathRef =
|
|
242
|
-
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);
|
|
243
722
|
const renderCanvas = useCallback2(
|
|
244
723
|
async (draftAnnotation, draftCrop) => {
|
|
245
724
|
const canvas = canvasRef.current;
|
|
@@ -259,7 +738,7 @@ var ImageEditor = ({
|
|
|
259
738
|
},
|
|
260
739
|
[controller, activeTab]
|
|
261
740
|
);
|
|
262
|
-
|
|
741
|
+
useEffect4(() => {
|
|
263
742
|
const raf = typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : (cb) => setTimeout(cb, 0);
|
|
264
743
|
const caf = typeof cancelAnimationFrame !== "undefined" ? cancelAnimationFrame : (id) => clearTimeout(id);
|
|
265
744
|
const animId = raf(() => {
|
|
@@ -267,12 +746,12 @@ var ImageEditor = ({
|
|
|
267
746
|
});
|
|
268
747
|
return () => caf(animId);
|
|
269
748
|
}, [renderCanvas, state]);
|
|
270
|
-
|
|
749
|
+
useEffect4(() => {
|
|
271
750
|
if (activeTab === "crop" && editor.state.activeTool !== "crop") {
|
|
272
751
|
editor.setTool("crop");
|
|
273
752
|
}
|
|
274
753
|
}, [activeTab, editor]);
|
|
275
|
-
|
|
754
|
+
useEffect4(() => {
|
|
276
755
|
const container = containerRef.current;
|
|
277
756
|
if (!container) return;
|
|
278
757
|
if (typeof ResizeObserver !== "undefined") {
|
|
@@ -630,15 +1109,15 @@ var ImageEditor = ({
|
|
|
630
1109
|
link.click();
|
|
631
1110
|
}
|
|
632
1111
|
};
|
|
633
|
-
return /* @__PURE__ */
|
|
1112
|
+
return /* @__PURE__ */ jsxs5(
|
|
634
1113
|
"div",
|
|
635
1114
|
{
|
|
636
1115
|
className: `flex flex-col bg-slate-900 border border-slate-800 rounded-xl overflow-hidden shadow-2xl text-slate-100 ${className}`,
|
|
637
1116
|
style,
|
|
638
1117
|
children: [
|
|
639
|
-
/* @__PURE__ */
|
|
640
|
-
/* @__PURE__ */
|
|
641
|
-
/* @__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(
|
|
642
1121
|
"button",
|
|
643
1122
|
{
|
|
644
1123
|
type: "button",
|
|
@@ -650,7 +1129,7 @@ var ImageEditor = ({
|
|
|
650
1129
|
children: "Transform & Crop"
|
|
651
1130
|
}
|
|
652
1131
|
),
|
|
653
|
-
/* @__PURE__ */
|
|
1132
|
+
/* @__PURE__ */ jsx5(
|
|
654
1133
|
"button",
|
|
655
1134
|
{
|
|
656
1135
|
type: "button",
|
|
@@ -662,7 +1141,7 @@ var ImageEditor = ({
|
|
|
662
1141
|
children: "Adjustments"
|
|
663
1142
|
}
|
|
664
1143
|
),
|
|
665
|
-
/* @__PURE__ */
|
|
1144
|
+
/* @__PURE__ */ jsx5(
|
|
666
1145
|
"button",
|
|
667
1146
|
{
|
|
668
1147
|
type: "button",
|
|
@@ -674,7 +1153,7 @@ var ImageEditor = ({
|
|
|
674
1153
|
children: "Annotations"
|
|
675
1154
|
}
|
|
676
1155
|
),
|
|
677
|
-
/* @__PURE__ */
|
|
1156
|
+
/* @__PURE__ */ jsx5(
|
|
678
1157
|
"button",
|
|
679
1158
|
{
|
|
680
1159
|
type: "button",
|
|
@@ -686,7 +1165,7 @@ var ImageEditor = ({
|
|
|
686
1165
|
children: "Depth Mask"
|
|
687
1166
|
}
|
|
688
1167
|
),
|
|
689
|
-
/* @__PURE__ */
|
|
1168
|
+
/* @__PURE__ */ jsxs5(
|
|
690
1169
|
"button",
|
|
691
1170
|
{
|
|
692
1171
|
type: "button",
|
|
@@ -696,14 +1175,14 @@ var ImageEditor = ({
|
|
|
696
1175
|
},
|
|
697
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"}`,
|
|
698
1177
|
children: [
|
|
699
|
-
/* @__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" }) }),
|
|
700
1179
|
"AI Tools"
|
|
701
1180
|
]
|
|
702
1181
|
}
|
|
703
1182
|
)
|
|
704
1183
|
] }),
|
|
705
|
-
/* @__PURE__ */
|
|
706
|
-
/* @__PURE__ */
|
|
1184
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-2 text-xs", children: [
|
|
1185
|
+
/* @__PURE__ */ jsx5(
|
|
707
1186
|
"button",
|
|
708
1187
|
{
|
|
709
1188
|
type: "button",
|
|
@@ -714,7 +1193,7 @@ var ImageEditor = ({
|
|
|
714
1193
|
children: "\u21A9"
|
|
715
1194
|
}
|
|
716
1195
|
),
|
|
717
|
-
/* @__PURE__ */
|
|
1196
|
+
/* @__PURE__ */ jsx5(
|
|
718
1197
|
"button",
|
|
719
1198
|
{
|
|
720
1199
|
type: "button",
|
|
@@ -725,7 +1204,7 @@ var ImageEditor = ({
|
|
|
725
1204
|
children: "\u21AA"
|
|
726
1205
|
}
|
|
727
1206
|
),
|
|
728
|
-
/* @__PURE__ */
|
|
1207
|
+
/* @__PURE__ */ jsx5(
|
|
729
1208
|
"button",
|
|
730
1209
|
{
|
|
731
1210
|
type: "button",
|
|
@@ -734,8 +1213,8 @@ var ImageEditor = ({
|
|
|
734
1213
|
children: "Reset"
|
|
735
1214
|
}
|
|
736
1215
|
),
|
|
737
|
-
/* @__PURE__ */
|
|
738
|
-
/* @__PURE__ */
|
|
1216
|
+
/* @__PURE__ */ jsx5("div", { className: "h-4 w-px bg-slate-800 mx-1" }),
|
|
1217
|
+
/* @__PURE__ */ jsx5(
|
|
739
1218
|
"button",
|
|
740
1219
|
{
|
|
741
1220
|
type: "button",
|
|
@@ -745,11 +1224,11 @@ var ImageEditor = ({
|
|
|
745
1224
|
children: "-"
|
|
746
1225
|
}
|
|
747
1226
|
),
|
|
748
|
-
/* @__PURE__ */
|
|
1227
|
+
/* @__PURE__ */ jsxs5("span", { className: "font-mono text-slate-400 w-10 text-center", children: [
|
|
749
1228
|
Math.round(state.zoom * 100),
|
|
750
1229
|
"%"
|
|
751
1230
|
] }),
|
|
752
|
-
/* @__PURE__ */
|
|
1231
|
+
/* @__PURE__ */ jsx5(
|
|
753
1232
|
"button",
|
|
754
1233
|
{
|
|
755
1234
|
type: "button",
|
|
@@ -759,8 +1238,8 @@ var ImageEditor = ({
|
|
|
759
1238
|
children: "+"
|
|
760
1239
|
}
|
|
761
1240
|
),
|
|
762
|
-
/* @__PURE__ */
|
|
763
|
-
/* @__PURE__ */
|
|
1241
|
+
/* @__PURE__ */ jsx5("div", { className: "h-4 w-px bg-slate-800 mx-1" }),
|
|
1242
|
+
/* @__PURE__ */ jsx5(
|
|
764
1243
|
"button",
|
|
765
1244
|
{
|
|
766
1245
|
type: "button",
|
|
@@ -771,7 +1250,7 @@ var ImageEditor = ({
|
|
|
771
1250
|
children: "Hold Compare"
|
|
772
1251
|
}
|
|
773
1252
|
),
|
|
774
|
-
/* @__PURE__ */
|
|
1253
|
+
/* @__PURE__ */ jsx5(
|
|
775
1254
|
"button",
|
|
776
1255
|
{
|
|
777
1256
|
type: "button",
|
|
@@ -782,13 +1261,13 @@ var ImageEditor = ({
|
|
|
782
1261
|
)
|
|
783
1262
|
] })
|
|
784
1263
|
] }),
|
|
785
|
-
/* @__PURE__ */
|
|
786
|
-
/* @__PURE__ */
|
|
1264
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex flex-col md:flex-row flex-1 min-h-[520px] md:h-[560px]", children: [
|
|
1265
|
+
/* @__PURE__ */ jsx5(
|
|
787
1266
|
"div",
|
|
788
1267
|
{
|
|
789
1268
|
ref: containerRef,
|
|
790
1269
|
className: "relative flex-1 h-[360px] md:h-full min-h-[320px] bg-slate-950 overflow-hidden select-none",
|
|
791
|
-
children: /* @__PURE__ */
|
|
1270
|
+
children: /* @__PURE__ */ jsx5(
|
|
792
1271
|
"canvas",
|
|
793
1272
|
{
|
|
794
1273
|
ref: canvasRef,
|
|
@@ -801,11 +1280,11 @@ var ImageEditor = ({
|
|
|
801
1280
|
)
|
|
802
1281
|
}
|
|
803
1282
|
),
|
|
804
|
-
/* @__PURE__ */
|
|
805
|
-
activeTab === "crop" && /* @__PURE__ */
|
|
806
|
-
/* @__PURE__ */
|
|
807
|
-
/* @__PURE__ */
|
|
808
|
-
/* @__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(
|
|
809
1288
|
"button",
|
|
810
1289
|
{
|
|
811
1290
|
type: "button",
|
|
@@ -817,7 +1296,7 @@ var ImageEditor = ({
|
|
|
817
1296
|
children: "Freeform"
|
|
818
1297
|
}
|
|
819
1298
|
),
|
|
820
|
-
/* @__PURE__ */
|
|
1299
|
+
/* @__PURE__ */ jsx5(
|
|
821
1300
|
"button",
|
|
822
1301
|
{
|
|
823
1302
|
type: "button",
|
|
@@ -829,7 +1308,7 @@ var ImageEditor = ({
|
|
|
829
1308
|
children: "1:1 Square"
|
|
830
1309
|
}
|
|
831
1310
|
),
|
|
832
|
-
/* @__PURE__ */
|
|
1311
|
+
/* @__PURE__ */ jsx5(
|
|
833
1312
|
"button",
|
|
834
1313
|
{
|
|
835
1314
|
type: "button",
|
|
@@ -841,7 +1320,7 @@ var ImageEditor = ({
|
|
|
841
1320
|
children: "4:3 Standard"
|
|
842
1321
|
}
|
|
843
1322
|
),
|
|
844
|
-
/* @__PURE__ */
|
|
1323
|
+
/* @__PURE__ */ jsx5(
|
|
845
1324
|
"button",
|
|
846
1325
|
{
|
|
847
1326
|
type: "button",
|
|
@@ -853,7 +1332,7 @@ var ImageEditor = ({
|
|
|
853
1332
|
children: "16:9 Wide"
|
|
854
1333
|
}
|
|
855
1334
|
),
|
|
856
|
-
/* @__PURE__ */
|
|
1335
|
+
/* @__PURE__ */ jsx5(
|
|
857
1336
|
"button",
|
|
858
1337
|
{
|
|
859
1338
|
type: "button",
|
|
@@ -865,7 +1344,7 @@ var ImageEditor = ({
|
|
|
865
1344
|
children: "3:2 Photo"
|
|
866
1345
|
}
|
|
867
1346
|
),
|
|
868
|
-
/* @__PURE__ */
|
|
1347
|
+
/* @__PURE__ */ jsx5(
|
|
869
1348
|
"button",
|
|
870
1349
|
{
|
|
871
1350
|
type: "button",
|
|
@@ -878,10 +1357,10 @@ var ImageEditor = ({
|
|
|
878
1357
|
}
|
|
879
1358
|
)
|
|
880
1359
|
] }),
|
|
881
|
-
/* @__PURE__ */
|
|
882
|
-
/* @__PURE__ */
|
|
883
|
-
/* @__PURE__ */
|
|
884
|
-
/* @__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(
|
|
885
1364
|
"button",
|
|
886
1365
|
{
|
|
887
1366
|
type: "button",
|
|
@@ -890,7 +1369,7 @@ var ImageEditor = ({
|
|
|
890
1369
|
children: "Rotate -90\xB0"
|
|
891
1370
|
}
|
|
892
1371
|
),
|
|
893
|
-
/* @__PURE__ */
|
|
1372
|
+
/* @__PURE__ */ jsx5(
|
|
894
1373
|
"button",
|
|
895
1374
|
{
|
|
896
1375
|
type: "button",
|
|
@@ -899,7 +1378,7 @@ var ImageEditor = ({
|
|
|
899
1378
|
children: "Rotate +90\xB0"
|
|
900
1379
|
}
|
|
901
1380
|
),
|
|
902
|
-
/* @__PURE__ */
|
|
1381
|
+
/* @__PURE__ */ jsx5(
|
|
903
1382
|
"button",
|
|
904
1383
|
{
|
|
905
1384
|
type: "button",
|
|
@@ -908,7 +1387,7 @@ var ImageEditor = ({
|
|
|
908
1387
|
children: "Flip Horizontal"
|
|
909
1388
|
}
|
|
910
1389
|
),
|
|
911
|
-
/* @__PURE__ */
|
|
1390
|
+
/* @__PURE__ */ jsx5(
|
|
912
1391
|
"button",
|
|
913
1392
|
{
|
|
914
1393
|
type: "button",
|
|
@@ -919,10 +1398,10 @@ var ImageEditor = ({
|
|
|
919
1398
|
)
|
|
920
1399
|
] })
|
|
921
1400
|
] }),
|
|
922
|
-
activeTab === "adjust" && /* @__PURE__ */
|
|
923
|
-
/* @__PURE__ */
|
|
924
|
-
/* @__PURE__ */
|
|
925
|
-
/* @__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(
|
|
926
1405
|
"button",
|
|
927
1406
|
{
|
|
928
1407
|
type: "button",
|
|
@@ -932,136 +1411,49 @@ var ImageEditor = ({
|
|
|
932
1411
|
}
|
|
933
1412
|
)
|
|
934
1413
|
] }),
|
|
935
|
-
/* @__PURE__ */
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
] }),
|
|
940
|
-
/* @__PURE__ */ jsx(
|
|
941
|
-
"input",
|
|
942
|
-
{
|
|
943
|
-
type: "range",
|
|
944
|
-
min: "-100",
|
|
945
|
-
max: "100",
|
|
946
|
-
value: state.adjustments.brightness,
|
|
947
|
-
onChange: (e) => editor.setAdjustments({ brightness: Number(e.target.value) }),
|
|
948
|
-
className: "w-full accent-indigo-500"
|
|
949
|
-
}
|
|
950
|
-
)
|
|
951
|
-
] }),
|
|
952
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
953
|
-
/* @__PURE__ */ jsxs("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
954
|
-
/* @__PURE__ */ jsx("span", { children: "Contrast" }),
|
|
955
|
-
/* @__PURE__ */ jsx("span", { className: "font-mono", children: state.adjustments.contrast })
|
|
956
|
-
] }),
|
|
957
|
-
/* @__PURE__ */ jsx(
|
|
958
|
-
"input",
|
|
959
|
-
{
|
|
960
|
-
type: "range",
|
|
961
|
-
min: "-100",
|
|
962
|
-
max: "100",
|
|
963
|
-
value: state.adjustments.contrast,
|
|
964
|
-
onChange: (e) => editor.setAdjustments({ contrast: Number(e.target.value) }),
|
|
965
|
-
className: "w-full accent-indigo-500"
|
|
966
|
-
}
|
|
967
|
-
)
|
|
968
|
-
] }),
|
|
969
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
970
|
-
/* @__PURE__ */ jsxs("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
971
|
-
/* @__PURE__ */ jsx("span", { children: "Saturation" }),
|
|
972
|
-
/* @__PURE__ */ jsx("span", { className: "font-mono", children: state.adjustments.saturation })
|
|
973
|
-
] }),
|
|
974
|
-
/* @__PURE__ */ jsx(
|
|
975
|
-
"input",
|
|
976
|
-
{
|
|
977
|
-
type: "range",
|
|
978
|
-
min: "-100",
|
|
979
|
-
max: "100",
|
|
980
|
-
value: state.adjustments.saturation,
|
|
981
|
-
onChange: (e) => editor.setAdjustments({ saturation: Number(e.target.value) }),
|
|
982
|
-
className: "w-full accent-indigo-500"
|
|
983
|
-
}
|
|
984
|
-
)
|
|
985
|
-
] }),
|
|
986
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
987
|
-
/* @__PURE__ */ jsxs("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
988
|
-
/* @__PURE__ */ jsx("span", { children: "Exposure" }),
|
|
989
|
-
/* @__PURE__ */ jsx("span", { className: "font-mono", children: state.adjustments.exposure })
|
|
990
|
-
] }),
|
|
991
|
-
/* @__PURE__ */ jsx(
|
|
992
|
-
"input",
|
|
993
|
-
{
|
|
994
|
-
type: "range",
|
|
995
|
-
min: "-100",
|
|
996
|
-
max: "100",
|
|
997
|
-
value: state.adjustments.exposure,
|
|
998
|
-
onChange: (e) => editor.setAdjustments({ exposure: Number(e.target.value) }),
|
|
999
|
-
className: "w-full accent-indigo-500"
|
|
1000
|
-
}
|
|
1001
|
-
)
|
|
1002
|
-
] }),
|
|
1003
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
1004
|
-
/* @__PURE__ */ jsxs("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
1005
|
-
/* @__PURE__ */ jsx("span", { children: "Warmth / Temperature" }),
|
|
1006
|
-
/* @__PURE__ */ jsx("span", { className: "font-mono", children: state.adjustments.temperature })
|
|
1007
|
-
] }),
|
|
1008
|
-
/* @__PURE__ */ jsx(
|
|
1009
|
-
"input",
|
|
1010
|
-
{
|
|
1011
|
-
type: "range",
|
|
1012
|
-
min: "-100",
|
|
1013
|
-
max: "100",
|
|
1014
|
-
value: state.adjustments.temperature,
|
|
1015
|
-
onChange: (e) => editor.setAdjustments({ temperature: Number(e.target.value) }),
|
|
1016
|
-
className: "w-full accent-indigo-500"
|
|
1017
|
-
}
|
|
1018
|
-
)
|
|
1019
|
-
] }),
|
|
1020
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
1021
|
-
/* @__PURE__ */ jsxs("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
1022
|
-
/* @__PURE__ */ jsx("span", { children: "Blur" }),
|
|
1023
|
-
/* @__PURE__ */ jsxs("span", { className: "font-mono", children: [
|
|
1024
|
-
state.adjustments.blur,
|
|
1025
|
-
"px"
|
|
1026
|
-
] })
|
|
1027
|
-
] }),
|
|
1028
|
-
/* @__PURE__ */ jsx(
|
|
1029
|
-
"input",
|
|
1414
|
+
/* @__PURE__ */ jsx5(Histogram, { histogram: editor.histogram }),
|
|
1415
|
+
IMAGE_ADJUSTMENT_CONTROLS.map((spec) => /* @__PURE__ */ jsxs5(React4.Fragment, { children: [
|
|
1416
|
+
/* @__PURE__ */ jsx5(
|
|
1417
|
+
ControlSlider,
|
|
1030
1418
|
{
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
onChange: (e) => editor.setAdjustments({ blur: Number(e.target.value) }),
|
|
1036
|
-
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 })
|
|
1037
1423
|
}
|
|
1038
|
-
)
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
/* @__PURE__ */ jsxs("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
1042
|
-
/* @__PURE__ */ jsx("span", { children: "Opacity" }),
|
|
1043
|
-
/* @__PURE__ */ jsxs("span", { className: "font-mono", children: [
|
|
1044
|
-
Math.round(state.adjustments.opacity * 100),
|
|
1045
|
-
"%"
|
|
1046
|
-
] })
|
|
1047
|
-
] }),
|
|
1048
|
-
/* @__PURE__ */ jsx(
|
|
1049
|
-
"input",
|
|
1424
|
+
),
|
|
1425
|
+
spec.id === "tint" && legacyProcess && /* @__PURE__ */ jsxs5(
|
|
1426
|
+
"div",
|
|
1050
1427
|
{
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
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
|
+
]
|
|
1058
1442
|
}
|
|
1059
1443
|
)
|
|
1060
|
-
] })
|
|
1444
|
+
] }, spec.id)),
|
|
1445
|
+
/* @__PURE__ */ jsx5("div", { className: "border-t border-slate-700/60 pt-3", children: /* @__PURE__ */ jsx5(
|
|
1446
|
+
AdjustmentLayersPanel,
|
|
1447
|
+
{
|
|
1448
|
+
controller,
|
|
1449
|
+
layers: state.adjustments.layers,
|
|
1450
|
+
histogram: editor.histogram
|
|
1451
|
+
}
|
|
1452
|
+
) })
|
|
1061
1453
|
] }),
|
|
1062
|
-
activeTab === "annotate" && /* @__PURE__ */
|
|
1063
|
-
/* @__PURE__ */
|
|
1064
|
-
/* @__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(
|
|
1065
1457
|
"button",
|
|
1066
1458
|
{
|
|
1067
1459
|
type: "button",
|
|
@@ -1071,9 +1463,9 @@ var ImageEditor = ({
|
|
|
1071
1463
|
},
|
|
1072
1464
|
t
|
|
1073
1465
|
)) }),
|
|
1074
|
-
/* @__PURE__ */
|
|
1075
|
-
/* @__PURE__ */
|
|
1076
|
-
/* @__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(
|
|
1077
1469
|
"input",
|
|
1078
1470
|
{
|
|
1079
1471
|
type: "color",
|
|
@@ -1082,15 +1474,15 @@ var ImageEditor = ({
|
|
|
1082
1474
|
className: "w-8 h-8 rounded border border-slate-700 cursor-pointer bg-transparent"
|
|
1083
1475
|
}
|
|
1084
1476
|
),
|
|
1085
|
-
/* @__PURE__ */
|
|
1086
|
-
/* @__PURE__ */
|
|
1087
|
-
/* @__PURE__ */
|
|
1088
|
-
/* @__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: [
|
|
1089
1481
|
strokeWidth,
|
|
1090
1482
|
"px"
|
|
1091
1483
|
] })
|
|
1092
1484
|
] }),
|
|
1093
|
-
/* @__PURE__ */
|
|
1485
|
+
/* @__PURE__ */ jsx5(
|
|
1094
1486
|
"input",
|
|
1095
1487
|
{
|
|
1096
1488
|
type: "range",
|
|
@@ -1103,10 +1495,10 @@ var ImageEditor = ({
|
|
|
1103
1495
|
)
|
|
1104
1496
|
] })
|
|
1105
1497
|
] }),
|
|
1106
|
-
/* @__PURE__ */
|
|
1107
|
-
/* @__PURE__ */
|
|
1108
|
-
/* @__PURE__ */
|
|
1109
|
-
/* @__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(
|
|
1110
1502
|
"input",
|
|
1111
1503
|
{
|
|
1112
1504
|
type: "text",
|
|
@@ -1117,7 +1509,7 @@ var ImageEditor = ({
|
|
|
1117
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"
|
|
1118
1510
|
}
|
|
1119
1511
|
),
|
|
1120
|
-
/* @__PURE__ */
|
|
1512
|
+
/* @__PURE__ */ jsx5(
|
|
1121
1513
|
"button",
|
|
1122
1514
|
{
|
|
1123
1515
|
type: "button",
|
|
@@ -1127,14 +1519,14 @@ var ImageEditor = ({
|
|
|
1127
1519
|
}
|
|
1128
1520
|
)
|
|
1129
1521
|
] }),
|
|
1130
|
-
/* @__PURE__ */
|
|
1131
|
-
/* @__PURE__ */
|
|
1132
|
-
/* @__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: [
|
|
1133
1525
|
fontSize,
|
|
1134
1526
|
"px"
|
|
1135
1527
|
] })
|
|
1136
1528
|
] }),
|
|
1137
|
-
/* @__PURE__ */
|
|
1529
|
+
/* @__PURE__ */ jsx5(
|
|
1138
1530
|
"input",
|
|
1139
1531
|
{
|
|
1140
1532
|
type: "range",
|
|
@@ -1145,14 +1537,14 @@ var ImageEditor = ({
|
|
|
1145
1537
|
className: "w-full accent-indigo-500"
|
|
1146
1538
|
}
|
|
1147
1539
|
),
|
|
1148
|
-
/* @__PURE__ */
|
|
1149
|
-
/* @__PURE__ */
|
|
1150
|
-
/* @__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: [
|
|
1151
1543
|
"Layers (",
|
|
1152
1544
|
state.annotations.length,
|
|
1153
1545
|
")"
|
|
1154
1546
|
] }),
|
|
1155
|
-
state.selectedAnnotationId && /* @__PURE__ */
|
|
1547
|
+
state.selectedAnnotationId && /* @__PURE__ */ jsx5(
|
|
1156
1548
|
"button",
|
|
1157
1549
|
{
|
|
1158
1550
|
type: "button",
|
|
@@ -1161,7 +1553,7 @@ var ImageEditor = ({
|
|
|
1161
1553
|
children: "Delete Selected"
|
|
1162
1554
|
}
|
|
1163
1555
|
),
|
|
1164
|
-
state.annotations.length > 0 && /* @__PURE__ */
|
|
1556
|
+
state.annotations.length > 0 && /* @__PURE__ */ jsx5(
|
|
1165
1557
|
"button",
|
|
1166
1558
|
{
|
|
1167
1559
|
type: "button",
|
|
@@ -1172,10 +1564,10 @@ var ImageEditor = ({
|
|
|
1172
1564
|
)
|
|
1173
1565
|
] })
|
|
1174
1566
|
] }),
|
|
1175
|
-
activeTab === "depth" && /* @__PURE__ */
|
|
1176
|
-
/* @__PURE__ */
|
|
1177
|
-
/* @__PURE__ */
|
|
1178
|
-
/* @__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(
|
|
1179
1571
|
"button",
|
|
1180
1572
|
{
|
|
1181
1573
|
type: "button",
|
|
@@ -1185,8 +1577,8 @@ var ImageEditor = ({
|
|
|
1185
1577
|
}
|
|
1186
1578
|
)
|
|
1187
1579
|
] }),
|
|
1188
|
-
/* @__PURE__ */
|
|
1189
|
-
/* @__PURE__ */
|
|
1580
|
+
/* @__PURE__ */ jsxs5("label", { className: "flex items-center gap-2 cursor-pointer text-slate-300", children: [
|
|
1581
|
+
/* @__PURE__ */ jsx5(
|
|
1190
1582
|
"input",
|
|
1191
1583
|
{
|
|
1192
1584
|
type: "checkbox",
|
|
@@ -1195,12 +1587,12 @@ var ImageEditor = ({
|
|
|
1195
1587
|
className: "rounded accent-indigo-500"
|
|
1196
1588
|
}
|
|
1197
1589
|
),
|
|
1198
|
-
/* @__PURE__ */
|
|
1590
|
+
/* @__PURE__ */ jsx5("span", { children: "Enable Depth / Alpha Mask" })
|
|
1199
1591
|
] }),
|
|
1200
|
-
state.depthMask.enabled && /* @__PURE__ */
|
|
1201
|
-
/* @__PURE__ */
|
|
1202
|
-
/* @__PURE__ */
|
|
1203
|
-
/* @__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(
|
|
1204
1596
|
"input",
|
|
1205
1597
|
{
|
|
1206
1598
|
type: "text",
|
|
@@ -1211,8 +1603,8 @@ var ImageEditor = ({
|
|
|
1211
1603
|
}
|
|
1212
1604
|
)
|
|
1213
1605
|
] }),
|
|
1214
|
-
/* @__PURE__ */
|
|
1215
|
-
/* @__PURE__ */
|
|
1606
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex gap-2", children: [
|
|
1607
|
+
/* @__PURE__ */ jsx5(
|
|
1216
1608
|
"button",
|
|
1217
1609
|
{
|
|
1218
1610
|
type: "button",
|
|
@@ -1221,7 +1613,7 @@ var ImageEditor = ({
|
|
|
1221
1613
|
children: "Threshold Mode"
|
|
1222
1614
|
}
|
|
1223
1615
|
),
|
|
1224
|
-
/* @__PURE__ */
|
|
1616
|
+
/* @__PURE__ */ jsx5(
|
|
1225
1617
|
"button",
|
|
1226
1618
|
{
|
|
1227
1619
|
type: "button",
|
|
@@ -1231,12 +1623,12 @@ var ImageEditor = ({
|
|
|
1231
1623
|
}
|
|
1232
1624
|
)
|
|
1233
1625
|
] }),
|
|
1234
|
-
/* @__PURE__ */
|
|
1235
|
-
/* @__PURE__ */
|
|
1236
|
-
/* @__PURE__ */
|
|
1237
|
-
/* @__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] })
|
|
1238
1630
|
] }),
|
|
1239
|
-
/* @__PURE__ */
|
|
1631
|
+
/* @__PURE__ */ jsx5(
|
|
1240
1632
|
"input",
|
|
1241
1633
|
{
|
|
1242
1634
|
type: "range",
|
|
@@ -1250,12 +1642,12 @@ var ImageEditor = ({
|
|
|
1250
1642
|
}
|
|
1251
1643
|
)
|
|
1252
1644
|
] }),
|
|
1253
|
-
/* @__PURE__ */
|
|
1254
|
-
/* @__PURE__ */
|
|
1255
|
-
/* @__PURE__ */
|
|
1256
|
-
/* @__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] })
|
|
1257
1649
|
] }),
|
|
1258
|
-
/* @__PURE__ */
|
|
1650
|
+
/* @__PURE__ */ jsx5(
|
|
1259
1651
|
"input",
|
|
1260
1652
|
{
|
|
1261
1653
|
type: "range",
|
|
@@ -1269,12 +1661,12 @@ var ImageEditor = ({
|
|
|
1269
1661
|
}
|
|
1270
1662
|
)
|
|
1271
1663
|
] }),
|
|
1272
|
-
/* @__PURE__ */
|
|
1273
|
-
/* @__PURE__ */
|
|
1274
|
-
/* @__PURE__ */
|
|
1275
|
-
/* @__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 })
|
|
1276
1668
|
] }),
|
|
1277
|
-
/* @__PURE__ */
|
|
1669
|
+
/* @__PURE__ */ jsx5(
|
|
1278
1670
|
"input",
|
|
1279
1671
|
{
|
|
1280
1672
|
type: "range",
|
|
@@ -1286,8 +1678,8 @@ var ImageEditor = ({
|
|
|
1286
1678
|
}
|
|
1287
1679
|
)
|
|
1288
1680
|
] }),
|
|
1289
|
-
/* @__PURE__ */
|
|
1290
|
-
/* @__PURE__ */
|
|
1681
|
+
/* @__PURE__ */ jsxs5("label", { className: "flex items-center gap-2 cursor-pointer text-slate-300 mt-1", children: [
|
|
1682
|
+
/* @__PURE__ */ jsx5(
|
|
1291
1683
|
"input",
|
|
1292
1684
|
{
|
|
1293
1685
|
type: "checkbox",
|
|
@@ -1296,52 +1688,52 @@ var ImageEditor = ({
|
|
|
1296
1688
|
className: "rounded accent-indigo-500"
|
|
1297
1689
|
}
|
|
1298
1690
|
),
|
|
1299
|
-
/* @__PURE__ */
|
|
1691
|
+
/* @__PURE__ */ jsx5("span", { children: "Invert Mask" })
|
|
1300
1692
|
] })
|
|
1301
1693
|
] })
|
|
1302
1694
|
] }),
|
|
1303
|
-
activeTab === "ai" && /* @__PURE__ */
|
|
1304
|
-
/* @__PURE__ */
|
|
1305
|
-
/* @__PURE__ */
|
|
1306
|
-
/* @__PURE__ */
|
|
1307
|
-
/* @__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" }) }),
|
|
1308
1700
|
"Background Removal"
|
|
1309
1701
|
] }),
|
|
1310
|
-
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..." })
|
|
1311
1703
|
] }),
|
|
1312
|
-
/* @__PURE__ */
|
|
1313
|
-
/* @__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(
|
|
1314
1706
|
"button",
|
|
1315
1707
|
{
|
|
1316
1708
|
type: "button",
|
|
1317
1709
|
onClick: handleRemoveBackground,
|
|
1318
1710
|
disabled: isRemovingBg || isInpainting,
|
|
1319
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",
|
|
1320
|
-
children: isRemovingBg ? /* @__PURE__ */
|
|
1321
|
-
/* @__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" }),
|
|
1322
1714
|
"Removing Background..."
|
|
1323
|
-
] }) : /* @__PURE__ */
|
|
1324
|
-
/* @__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" }) }),
|
|
1325
1717
|
"Remove Background"
|
|
1326
1718
|
] })
|
|
1327
1719
|
}
|
|
1328
1720
|
)
|
|
1329
1721
|
] }),
|
|
1330
|
-
/* @__PURE__ */
|
|
1331
|
-
/* @__PURE__ */
|
|
1332
|
-
/* @__PURE__ */
|
|
1333
|
-
/* @__PURE__ */
|
|
1334
|
-
/* @__PURE__ */
|
|
1335
|
-
/* @__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" })
|
|
1336
1728
|
] }),
|
|
1337
1729
|
"Generative Inpainting"
|
|
1338
1730
|
] }),
|
|
1339
|
-
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..." })
|
|
1340
1732
|
] }),
|
|
1341
|
-
/* @__PURE__ */
|
|
1342
|
-
/* @__PURE__ */
|
|
1343
|
-
/* @__PURE__ */
|
|
1344
|
-
/* @__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(
|
|
1345
1737
|
"textarea",
|
|
1346
1738
|
{
|
|
1347
1739
|
id: "ai-inpaint-prompt-react",
|
|
@@ -1353,10 +1745,10 @@ var ImageEditor = ({
|
|
|
1353
1745
|
}
|
|
1354
1746
|
)
|
|
1355
1747
|
] }),
|
|
1356
|
-
/* @__PURE__ */
|
|
1357
|
-
/* @__PURE__ */
|
|
1358
|
-
/* @__PURE__ */
|
|
1359
|
-
/* @__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(
|
|
1360
1752
|
"button",
|
|
1361
1753
|
{
|
|
1362
1754
|
type: "button",
|
|
@@ -1369,7 +1761,7 @@ var ImageEditor = ({
|
|
|
1369
1761
|
]
|
|
1370
1762
|
}
|
|
1371
1763
|
),
|
|
1372
|
-
/* @__PURE__ */
|
|
1764
|
+
/* @__PURE__ */ jsxs5(
|
|
1373
1765
|
"button",
|
|
1374
1766
|
{
|
|
1375
1767
|
type: "button",
|
|
@@ -1384,25 +1776,25 @@ var ImageEditor = ({
|
|
|
1384
1776
|
)
|
|
1385
1777
|
] })
|
|
1386
1778
|
] }),
|
|
1387
|
-
/* @__PURE__ */
|
|
1779
|
+
/* @__PURE__ */ jsx5(
|
|
1388
1780
|
"button",
|
|
1389
1781
|
{
|
|
1390
1782
|
type: "button",
|
|
1391
1783
|
onClick: handleInpaint,
|
|
1392
1784
|
disabled: isInpainting || isRemovingBg || !inpaintPrompt.trim(),
|
|
1393
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",
|
|
1394
|
-
children: isInpainting ? /* @__PURE__ */
|
|
1395
|
-
/* @__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" }),
|
|
1396
1788
|
"Generating Inpaint..."
|
|
1397
|
-
] }) : /* @__PURE__ */
|
|
1398
|
-
/* @__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" }) }),
|
|
1399
1791
|
"Apply Inpainting"
|
|
1400
1792
|
] })
|
|
1401
1793
|
}
|
|
1402
1794
|
)
|
|
1403
1795
|
] }),
|
|
1404
|
-
aiError && /* @__PURE__ */
|
|
1405
|
-
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 })
|
|
1406
1798
|
] })
|
|
1407
1799
|
] })
|
|
1408
1800
|
] })
|
|
@@ -1413,6 +1805,11 @@ var ImageEditor = ({
|
|
|
1413
1805
|
|
|
1414
1806
|
export {
|
|
1415
1807
|
useImageEditor,
|
|
1808
|
+
ControlSlider,
|
|
1809
|
+
Histogram,
|
|
1810
|
+
useImageHistogram,
|
|
1811
|
+
CurvesEditor,
|
|
1812
|
+
AdjustmentLayersPanel,
|
|
1416
1813
|
ImageEditor
|
|
1417
1814
|
};
|
|
1418
|
-
//# sourceMappingURL=chunk-
|
|
1815
|
+
//# sourceMappingURL=chunk-XTFR3IMK.js.map
|