@pixerate/editor-react 0.68.1 → 0.70.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/paint-editor/index.cjs +133 -5
- package/dist/paint-editor/index.cjs.map +1 -1
- package/dist/paint-editor/index.d.cts +37 -5
- package/dist/paint-editor/index.d.ts +37 -5
- package/dist/paint-editor/index.js +151 -11
- package/dist/paint-editor/index.js.map +1 -1
- package/dist/vector/index.cjs +302 -152
- package/dist/vector/index.cjs.map +1 -1
- package/dist/vector/index.d.cts +23 -2
- package/dist/vector/index.d.ts +23 -2
- package/dist/vector/index.js +285 -136
- package/dist/vector/index.js.map +1 -1
- package/package.json +3 -3
|
@@ -25,9 +25,11 @@ __export(paint_editor_exports, {
|
|
|
25
25
|
PaintCanvas: () => PaintCanvas,
|
|
26
26
|
PaintEditor: () => PaintEditor,
|
|
27
27
|
PaintLayersPanel: () => PaintLayersPanel,
|
|
28
|
+
PaintPresenceLayer: () => PaintPresenceLayer,
|
|
28
29
|
PaintSlider: () => PaintSlider,
|
|
29
30
|
PaintToolbar: () => PaintToolbar,
|
|
30
31
|
PaperPanel: () => PaperPanel,
|
|
32
|
+
usePaintDirector: () => usePaintDirector,
|
|
31
33
|
usePaintEditor: () => usePaintEditor,
|
|
32
34
|
usePaintEditorState: () => usePaintEditorState
|
|
33
35
|
});
|
|
@@ -48,12 +50,19 @@ function usePaintEditorState(controller) {
|
|
|
48
50
|
() => controller.getState()
|
|
49
51
|
);
|
|
50
52
|
}
|
|
53
|
+
function usePaintDirector(controller, options = {}) {
|
|
54
|
+
const [value] = (0, import_react.useState)(() => {
|
|
55
|
+
const presence = new import_paint_editor.PaintPresence();
|
|
56
|
+
return { presence, director: new import_paint_editor.PaintDirector(controller, presence, options) };
|
|
57
|
+
});
|
|
58
|
+
return value;
|
|
59
|
+
}
|
|
51
60
|
|
|
52
61
|
// src/paint-editor/PaintCanvas.tsx
|
|
53
62
|
var import_react2 = require("react");
|
|
54
63
|
var import_paint_editor2 = require("@pixerate/editor/paint-editor");
|
|
55
64
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
56
|
-
var PaintCanvas = ({ controller, tool = "brush", showGuides = true, fitOnMount = true, onError, className }) => {
|
|
65
|
+
var PaintCanvas = ({ controller, tool = "brush", showGuides = true, fitOnMount = true, onError, presence, className }) => {
|
|
57
66
|
const state = usePaintEditorState(controller);
|
|
58
67
|
const viewport = (0, import_react2.useRef)(null);
|
|
59
68
|
const canvas = (0, import_react2.useRef)(null);
|
|
@@ -168,6 +177,7 @@ var PaintCanvas = ({ controller, tool = "brush", showGuides = true, fitOnMount =
|
|
|
168
177
|
predicted.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("polyline", { points: predicted.map((p) => `${p.x},${p.y}`).join(" "), fill: "none", stroke: state.color, strokeOpacity: 0.5, strokeWidth: Math.max(1, state.brush.tip.size / 2), strokeLinecap: "round" })
|
|
169
178
|
] }),
|
|
170
179
|
state.guides.references.filter((r) => r.visible).map((r) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ReferencePanel, { controller, id: r.id }, r.id)),
|
|
180
|
+
presence && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PaintPresenceLayer, { presence, view: state.view, canvas: doc, viewport: size }),
|
|
171
181
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "absolute bottom-1 right-2 text-[10px] text-slate-500 pointer-events-none", "data-testid": "paint-view-status", children: [
|
|
172
182
|
Math.round(state.view.zoom * 100),
|
|
173
183
|
"% \xB7 ",
|
|
@@ -179,6 +189,16 @@ var PaintCanvas = ({ controller, tool = "brush", showGuides = true, fitOnMount =
|
|
|
179
189
|
}
|
|
180
190
|
);
|
|
181
191
|
};
|
|
192
|
+
var PaintPresenceLayer = ({ presence, view, canvas, viewport }) => {
|
|
193
|
+
const { actors } = (0, import_react2.useSyncExternalStore)(presence.subscribe, presence.getState, presence.getState);
|
|
194
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "pointer-events-none absolute inset-0 overflow-hidden", "data-paint-presence": "", "aria-hidden": "true", children: actors.map((a) => {
|
|
195
|
+
const p = (0, import_paint_editor2.documentToScreen)(view, canvas, viewport, a.x, a.y);
|
|
196
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "absolute", style: { left: p.x, top: p.y }, "data-actor": a.id, children: [
|
|
197
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { width: "18", height: "20", viewBox: "0 0 18 20", className: "drop-shadow-md", style: { transform: a.pressed ? "scale(0.85)" : void 0, transformOrigin: "0 0" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M0 0 L0 15 L4.5 10.8 L7.6 17 L10.2 15.8 L7.2 9.7 L13.5 9.4 Z", fill: a.color, stroke: "white", strokeWidth: "1.4", strokeLinejoin: "round" }) }),
|
|
198
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "absolute left-4 top-4 w-max max-w-[240px] rounded-md px-2 py-0.5 text-[11px] font-semibold text-white shadow", style: { background: a.color }, children: a.message ?? a.name })
|
|
199
|
+
] }, a.id);
|
|
200
|
+
}) });
|
|
201
|
+
};
|
|
182
202
|
var ReferencePanel = ({ controller, id }) => {
|
|
183
203
|
const state = usePaintEditorState(controller);
|
|
184
204
|
const ref = state.guides.references.find((r) => r.id === id);
|
|
@@ -501,6 +521,10 @@ var BrushPanel = ({ controller, thumbnailWidth = 112, thumbnailHeight = 32, clas
|
|
|
501
521
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
502
522
|
[controller, brushes, paper, thumbnailWidth, thumbnailHeight]
|
|
503
523
|
);
|
|
524
|
+
const engine = (0, import_paint_editor4.brushEngineControls)(brush);
|
|
525
|
+
const mc = brush.behavior.multicolor;
|
|
526
|
+
const carried = (0, import_paint_editor4.brushLoadColor)(state.mixerLoad);
|
|
527
|
+
const setColors = (colors) => controller.setBrush({ behavior: { multicolor: { colors } } });
|
|
504
528
|
const slug = (name) => name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60) || "brush";
|
|
505
529
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: `${panel} ${className ?? ""}`, "data-testid": "brush-panel", children: [
|
|
506
530
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "grid grid-cols-2 gap-1", role: "listbox", "aria-label": "Brushes", children: brushes.map((b) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
@@ -532,6 +556,68 @@ var BrushPanel = ({ controller, thumbnailWidth = 112, thumbnailHeight = 32, clas
|
|
|
532
556
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "flex items-center gap-2", children: [
|
|
533
557
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { type: "checkbox", checked: brush.tip.pressureOpacity, onChange: (e) => controller.setBrush({ tip: { pressureOpacity: e.target.checked } }) }),
|
|
534
558
|
" Pressure \u2192 opacity"
|
|
559
|
+
] }),
|
|
560
|
+
brush.engine === "dab" && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "flex items-center justify-between gap-2", children: [
|
|
561
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "text-slate-400", children: "Blend" }),
|
|
562
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("select", { "aria-label": "Brush blend", value: brush.tip.blend, onChange: (e) => controller.setBrush({ tip: { blend: e.target.value } }), className: "rounded bg-slate-800 px-1 py-0.5", children: import_paint_editor4.BLEND_MENU_GROUPS.map((g) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("optgroup", { label: g.label, children: g.modes.map((m) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: m.id, title: m.description, children: m.label }, m.id)) }, g.label)) })
|
|
563
|
+
] })
|
|
564
|
+
] }),
|
|
565
|
+
engine && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-col gap-1.5", "data-testid": "brush-engine", children: [
|
|
566
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "font-medium text-slate-300", children: engine.title }),
|
|
567
|
+
engine.sliders.map((c) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PaintSlider, { control: c, value: (0, import_paint_editor4.readPath)(brush, c.path), onChange: (v) => controller.setBrush((0, import_paint_editor4.patchPath)(brush, c.path, v)) }, c.path)),
|
|
568
|
+
engine.mixingPath && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "flex items-center justify-between gap-2", children: [
|
|
569
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "text-slate-400", children: "Mixing" }),
|
|
570
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
571
|
+
"select",
|
|
572
|
+
{
|
|
573
|
+
"aria-label": "Brush mixing",
|
|
574
|
+
value: engine.mixingPath === "blender.mixing" ? brush.blender.mixing : engine.mixingPath === "smudge.mixing" ? brush.smudge.mixing : brush.behavior.mixing,
|
|
575
|
+
onChange: (e) => controller.setBrush((0, import_paint_editor4.patchBrushValue)(brush, engine.mixingPath, e.target.value)),
|
|
576
|
+
className: "rounded bg-slate-800 px-1 py-0.5",
|
|
577
|
+
children: [
|
|
578
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "pigment", children: "Pigment" }),
|
|
579
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "rgb", children: "RGB" })
|
|
580
|
+
]
|
|
581
|
+
}
|
|
582
|
+
)
|
|
583
|
+
] }),
|
|
584
|
+
engine.dirty && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "flex items-center gap-2", title: "Keep the colour the brush picked up for the next stroke (needs Pick-up above 0)", children: [
|
|
585
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { type: "checkbox", "aria-label": "Dirty brush", checked: brush.behavior.dirty, onChange: (e) => controller.setBrush({ behavior: { dirty: e.target.checked } }) }),
|
|
586
|
+
" Dirty brush"
|
|
587
|
+
] }),
|
|
588
|
+
engine.multicolor && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-col gap-1.5", "data-testid": "multicolor", children: [
|
|
589
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "flex items-center justify-between gap-2", children: [
|
|
590
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "text-slate-400", children: "Multi-colour" }),
|
|
591
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("select", { "aria-label": "Multi-colour", value: mc.mode, onChange: (e) => controller.setBrush({ behavior: { multicolor: { mode: e.target.value } } }), className: "rounded bg-slate-800 px-1 py-0.5", children: [
|
|
592
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "off", children: "Off" }),
|
|
593
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "tip", children: "Across the tip" }),
|
|
594
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "dab", children: "Per dab" })
|
|
595
|
+
] })
|
|
596
|
+
] }),
|
|
597
|
+
mc.mode !== "off" && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
|
|
598
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-wrap items-center gap-1", "aria-label": "Multi-colour colours", children: [
|
|
599
|
+
mc.colors.map((col, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "flex items-center", children: [
|
|
600
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { type: "color", "aria-label": `Multi-colour ${i + 1}`, value: col, onChange: (e) => setColors(mc.colors.map((x, j) => j === i ? e.target.value : x)), className: "h-5 w-6 rounded bg-transparent" }),
|
|
601
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", "aria-label": `Remove multi-colour ${i + 1}`, className: "px-0.5 text-slate-500 hover:text-white", onClick: () => setColors(mc.colors.filter((_, j) => j !== i)), children: "\xD7" })
|
|
602
|
+
] }, i)),
|
|
603
|
+
mc.colors.length < 8 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: button, "aria-label": "Add multi-colour", onClick: () => setColors([...mc.colors.length ? mc.colors : [state.color, state.secondaryColor], state.color].slice(0, 8)), children: "+" }),
|
|
604
|
+
mc.colors.length < 2 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "text-[10px] text-slate-500", children: "Uses the paint and second colours" })
|
|
605
|
+
] }),
|
|
606
|
+
mc.mode === "dab" && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "flex items-center justify-between gap-2", children: [
|
|
607
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "text-slate-400", children: "Order" }),
|
|
608
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("select", { "aria-label": "Colour order", value: mc.order, onChange: (e) => controller.setBrush({ behavior: { multicolor: { order: e.target.value } } }), className: "rounded bg-slate-800 px-1 py-0.5", children: [
|
|
609
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "sequence", children: "In order" }),
|
|
610
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "random", children: "Random" })
|
|
611
|
+
] })
|
|
612
|
+
] }),
|
|
613
|
+
(0, import_paint_editor4.multiColorSliders)(mc.mode).map((c) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PaintSlider, { control: c, value: (0, import_paint_editor4.readPath)(brush, c.path), onChange: (v) => controller.setBrush((0, import_paint_editor4.patchPath)(brush, c.path, v)) }, c.path))
|
|
614
|
+
] })
|
|
615
|
+
] }),
|
|
616
|
+
engine.load && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center gap-1", children: [
|
|
617
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "text-slate-400", children: "Carries" }),
|
|
618
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { "aria-label": "Brush load", title: carried ?? "Clean", className: "h-4 w-4 rounded ring-1 ring-slate-700", style: { background: carried ?? "transparent" } }),
|
|
619
|
+
brush.engine === "mixer" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: button, onClick: () => controller.loadMixerBrush(), children: "Load" }),
|
|
620
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: button, onClick: () => controller.cleanMixerBrush(brush.engine === "mixer"), children: "Clean" })
|
|
535
621
|
] })
|
|
536
622
|
] }),
|
|
537
623
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-wrap items-center gap-1", children: [
|
|
@@ -611,6 +697,9 @@ var ColorPanel = ({ controller, wheelSize = 168, mixingPad = true, className })
|
|
|
611
697
|
const [wheel, setWheel] = (0, import_react7.useState)("ryb");
|
|
612
698
|
const [kind, setKind] = (0, import_react7.useState)("complementary");
|
|
613
699
|
const [padTool, setPadTool] = (0, import_react7.useState)("paint");
|
|
700
|
+
const [sample, setSample] = (0, import_react7.useState)(3);
|
|
701
|
+
const [parts, setParts] = (0, import_react7.useState)([1, 1]);
|
|
702
|
+
const [padName, setPadName] = (0, import_react7.useState)("");
|
|
614
703
|
const padPoints = (0, import_react7.useRef)(null);
|
|
615
704
|
const ring = (0, import_react7.useMemo)(() => (0, import_paint_editor6.renderColorWheel)(wheelSize, wheel), [wheelSize, wheel]);
|
|
616
705
|
const color = state.color;
|
|
@@ -722,7 +811,7 @@ var ColorPanel = ({ controller, wheelSize = 168, mixingPad = true, className })
|
|
|
722
811
|
onPointerDown: (e) => {
|
|
723
812
|
const p = padPoint(e);
|
|
724
813
|
if (padTool === "pick") {
|
|
725
|
-
attempt(() => controller.mixingPadPick(p.x, p.y, { setColor: true }));
|
|
814
|
+
attempt(() => controller.mixingPadPick(p.x, p.y, { setColor: true, radius: (0, import_paint_editor6.padSampleRadius)(sample) }));
|
|
726
815
|
return;
|
|
727
816
|
}
|
|
728
817
|
padPoints.current = [p];
|
|
@@ -736,7 +825,44 @@ var ColorPanel = ({ controller, wheelSize = 168, mixingPad = true, className })
|
|
|
736
825
|
},
|
|
737
826
|
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PixelCanvas, { pixels: pad, label: "Mixing pad pixels", className: "w-full rounded", style: { aspectRatio: `${import_paint_editor6.MIXING_PAD_WIDTH} / ${import_paint_editor6.MIXING_PAD_HEIGHT}` } })
|
|
738
827
|
}
|
|
739
|
-
)
|
|
828
|
+
),
|
|
829
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex flex-wrap items-center gap-1", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "flex items-center gap-1", children: [
|
|
830
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "text-slate-400", children: "Sample" }),
|
|
831
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("select", { "aria-label": "Pick sample size", value: sample, onChange: (e) => setSample(Number(e.target.value)), className: "rounded bg-slate-800 px-1 py-0.5", children: import_paint_editor6.PAD_SAMPLE_SIZES.map((n) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: n, children: n === 1 ? "1 px" : `${n}\xD7${n}` }, n)) })
|
|
832
|
+
] }) }),
|
|
833
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex flex-wrap items-center gap-1", children: [
|
|
834
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "h-4 w-4 rounded ring-1 ring-slate-700", style: { background: color } }),
|
|
835
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { type: "number", "aria-label": "Paint colour parts", min: 0, max: 20, value: parts[0], onChange: (e) => setParts([Number(e.target.value), parts[1]]), className: "w-10 rounded bg-slate-800 px-1 py-0.5" }),
|
|
836
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "text-slate-500", children: ":" }),
|
|
837
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { type: "number", "aria-label": "Second colour parts", min: 0, max: 20, value: parts[1], onChange: (e) => setParts([parts[0], Number(e.target.value)]), className: "w-10 rounded bg-slate-800 px-1 py-0.5" }),
|
|
838
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "h-4 w-4 rounded ring-1 ring-slate-700", style: { background: state.secondaryColor } }),
|
|
839
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
840
|
+
"button",
|
|
841
|
+
{
|
|
842
|
+
type: "button",
|
|
843
|
+
className: button,
|
|
844
|
+
title: "Mix the paint and second colours in these parts on the pad, and use the mix",
|
|
845
|
+
onClick: () => attempt(
|
|
846
|
+
() => controller.mixOnPad(
|
|
847
|
+
[
|
|
848
|
+
{ color, parts: parts[0] },
|
|
849
|
+
{ color: state.secondaryColor, parts: parts[1] }
|
|
850
|
+
],
|
|
851
|
+
{ setColor: true }
|
|
852
|
+
)
|
|
853
|
+
),
|
|
854
|
+
children: "Mix"
|
|
855
|
+
}
|
|
856
|
+
)
|
|
857
|
+
] }),
|
|
858
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex flex-wrap items-center gap-1", children: [
|
|
859
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { value: padName, placeholder: "Pad name", "aria-label": "Pad name", onChange: (e) => setPadName(e.target.value), className: "w-24 rounded bg-slate-800 px-1.5 py-0.5" }),
|
|
860
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: button, onClick: () => attempt(() => (controller.saveMixingPad(padName || void 0), setPadName(""))), children: "Keep pad" })
|
|
861
|
+
] }),
|
|
862
|
+
state.savedMixingPads.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("ul", { className: "flex flex-col gap-0.5", "aria-label": "Kept pads", children: state.savedMixingPads.map((p) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("li", { className: "flex items-center gap-1", children: [
|
|
863
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: `${button} flex-1 text-left`, onClick: () => attempt(() => controller.loadMixingPad(p.id)), children: p.name }),
|
|
864
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: button, "aria-label": `Remove ${p.name}`, onClick: () => attempt(() => controller.removeMixingPad(p.id)), children: "\xD7" })
|
|
865
|
+
] }, p.id)) })
|
|
740
866
|
] }),
|
|
741
867
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ErrorLine, { error })
|
|
742
868
|
] });
|
|
@@ -856,7 +982,7 @@ var PaintLayersPanel = ({ controller, className }) => {
|
|
|
856
982
|
// src/paint-editor/PaintEditor.tsx
|
|
857
983
|
var import_react9 = require("react");
|
|
858
984
|
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
859
|
-
var PaintEditor = ({ controller, fileName, height = 720, className }) => {
|
|
985
|
+
var PaintEditor = ({ controller, fileName, height = 720, presence, className }) => {
|
|
860
986
|
const [tool, setTool] = (0, import_react9.useState)("brush");
|
|
861
987
|
const [tab, setTab] = (0, import_react9.useState)("color");
|
|
862
988
|
const [error, setError] = (0, import_react9.useState)(null);
|
|
@@ -865,7 +991,7 @@ var PaintEditor = ({ controller, fileName, height = 720, className }) => {
|
|
|
865
991
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex min-h-0 flex-1 gap-2", children: [
|
|
866
992
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(BrushPanel, { controller, className: "w-60 shrink-0 overflow-y-auto pr-1" }),
|
|
867
993
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex min-w-0 flex-1 flex-col", children: [
|
|
868
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(PaintCanvas, { controller, tool, onError: setError, className: "flex-1 rounded-lg" }),
|
|
994
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(PaintCanvas, { controller, tool, onError: setError, presence, className: "flex-1 rounded-lg" }),
|
|
869
995
|
error && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { role: "alert", className: "mt-1 text-[11px] text-rose-400", children: error })
|
|
870
996
|
] }),
|
|
871
997
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex w-64 shrink-0 flex-col gap-2 overflow-y-auto pr-1", children: [
|
|
@@ -884,9 +1010,11 @@ var PaintEditor = ({ controller, fileName, height = 720, className }) => {
|
|
|
884
1010
|
PaintCanvas,
|
|
885
1011
|
PaintEditor,
|
|
886
1012
|
PaintLayersPanel,
|
|
1013
|
+
PaintPresenceLayer,
|
|
887
1014
|
PaintSlider,
|
|
888
1015
|
PaintToolbar,
|
|
889
1016
|
PaperPanel,
|
|
1017
|
+
usePaintDirector,
|
|
890
1018
|
usePaintEditor,
|
|
891
1019
|
usePaintEditorState
|
|
892
1020
|
});
|