@pixerate/editor-react 0.76.0 → 0.78.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/PaintGradientEditorView-CCPCY5Ib.d.cts +14 -0
- package/dist/PaintGradientEditorView-CCPCY5Ib.d.ts +14 -0
- package/dist/canvas/index.cjs +662 -561
- package/dist/canvas/index.cjs.map +1 -1
- package/dist/canvas/index.d.cts +36 -5
- package/dist/canvas/index.d.ts +36 -5
- package/dist/canvas/index.js +232 -129
- package/dist/canvas/index.js.map +1 -1
- package/dist/{chunk-LWMUV5TC.js → chunk-2EW42UXC.js} +639 -461
- package/dist/chunk-2EW42UXC.js.map +1 -0
- package/dist/chunk-FX3EREF5.js +206 -0
- package/dist/chunk-FX3EREF5.js.map +1 -0
- package/dist/chunk-MO354USR.js +126 -0
- package/dist/chunk-MO354USR.js.map +1 -0
- package/dist/chunk-ZQZAKUUM.js +416 -0
- package/dist/chunk-ZQZAKUUM.js.map +1 -0
- package/dist/effects/index.cjs +606 -0
- package/dist/effects/index.cjs.map +1 -0
- package/dist/effects/index.d.cts +80 -0
- package/dist/effects/index.d.ts +80 -0
- package/dist/effects/index.js +16 -0
- package/dist/effects/index.js.map +1 -0
- package/dist/image-editor/index.cjs +1388 -534
- package/dist/image-editor/index.cjs.map +1 -1
- package/dist/image-editor/index.d.cts +36 -3
- package/dist/image-editor/index.d.ts +36 -3
- package/dist/image-editor/index.js +10 -1
- package/dist/index.cjs +1806 -952
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.js +10 -1
- package/dist/index.js.map +1 -1
- package/dist/paint-editor/index.cjs +438 -289
- package/dist/paint-editor/index.cjs.map +1 -1
- package/dist/paint-editor/index.d.cts +8 -3
- package/dist/paint-editor/index.d.ts +8 -3
- package/dist/paint-editor/index.js +215 -187
- package/dist/paint-editor/index.js.map +1 -1
- package/dist/parameters/index.js +55 -439
- package/dist/parameters/index.js.map +1 -1
- package/dist/video-editor/index.cjs +732 -17
- package/dist/video-editor/index.cjs.map +1 -1
- package/dist/video-editor/index.d.cts +31 -3
- package/dist/video-editor/index.d.ts +31 -3
- package/dist/video-editor/index.js +184 -17
- package/dist/video-editor/index.js.map +1 -1
- package/package.json +8 -3
- package/dist/chunk-LWMUV5TC.js.map +0 -1
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PaintGradientEditorView
|
|
3
|
+
} from "../chunk-MO354USR.js";
|
|
4
|
+
|
|
1
5
|
// src/paint-editor/usePaintEditor.ts
|
|
2
6
|
import { useMemo, useState, useSyncExternalStore } from "react";
|
|
3
7
|
import { PaintDirector, PaintEditorController, PaintPresence } from "@pixerate/editor/paint-editor";
|
|
@@ -550,7 +554,7 @@ import {
|
|
|
550
554
|
brushEngineControls,
|
|
551
555
|
brushLoadColor,
|
|
552
556
|
downloadBytes as downloadBytes2,
|
|
553
|
-
|
|
557
|
+
brushTakesPaint,
|
|
554
558
|
patchBrushValue,
|
|
555
559
|
patchPath,
|
|
556
560
|
readPath,
|
|
@@ -568,7 +572,15 @@ import {
|
|
|
568
572
|
STAMP_LABELS,
|
|
569
573
|
toggleStamp
|
|
570
574
|
} from "@pixerate/editor/paint-editor";
|
|
571
|
-
import {
|
|
575
|
+
import { DEFAULT_PAINT_GRADIENT } from "@pixerate/editor/paint-editor";
|
|
576
|
+
|
|
577
|
+
// src/paint-editor/PaintGradientEditor.tsx
|
|
578
|
+
import { PAINT_GRADIENT_KIT } from "@pixerate/editor/paint-editor";
|
|
579
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
580
|
+
var PaintGradientEditor = (props) => /* @__PURE__ */ jsx4(PaintGradientEditorView, { ...props, kit: PAINT_GRADIENT_KIT });
|
|
581
|
+
|
|
582
|
+
// src/paint-editor/BrushPanel.tsx
|
|
583
|
+
import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
572
584
|
var BrushPanel = ({ controller, thumbnailWidth = 112, thumbnailHeight = 32, className }) => {
|
|
573
585
|
const state = usePaintEditorState(controller);
|
|
574
586
|
const [error, attempt] = useAttempt();
|
|
@@ -583,12 +595,10 @@ var BrushPanel = ({ controller, thumbnailWidth = 112, thumbnailHeight = 32, clas
|
|
|
583
595
|
[controller, brushes, paper, thumbnailWidth, thumbnailHeight]
|
|
584
596
|
);
|
|
585
597
|
const engine = brushEngineControls(brush);
|
|
586
|
-
const mc = brush.behavior.multicolor;
|
|
587
598
|
const carried = brushLoadColor(state.mixerLoad);
|
|
588
|
-
const setColors = (colors) => controller.setBrush({ behavior: { multicolor: { colors } } });
|
|
589
599
|
const slug = (name) => name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60) || "brush";
|
|
590
600
|
return /* @__PURE__ */ jsxs4("div", { className: `${panel} ${className ?? ""}`, "data-testid": "brush-panel", children: [
|
|
591
|
-
/* @__PURE__ */
|
|
601
|
+
/* @__PURE__ */ jsx5("div", { className: "grid grid-cols-2 gap-1", role: "listbox", "aria-label": "Brushes", children: brushes.map((b) => /* @__PURE__ */ jsxs4(
|
|
592
602
|
"button",
|
|
593
603
|
{
|
|
594
604
|
type: "button",
|
|
@@ -598,52 +608,52 @@ var BrushPanel = ({ controller, thumbnailWidth = 112, thumbnailHeight = 32, clas
|
|
|
598
608
|
className: `flex flex-col items-start gap-0.5 rounded p-1 text-left ${brush.id === b.id ? "bg-indigo-600/30 ring-1 ring-indigo-500" : "bg-slate-900 hover:bg-slate-800"}`,
|
|
599
609
|
onClick: () => controller.setBrush({ id: b.id }),
|
|
600
610
|
children: [
|
|
601
|
-
/* @__PURE__ */
|
|
602
|
-
/* @__PURE__ */
|
|
611
|
+
/* @__PURE__ */ jsx5(PixelCanvas, { pixels: thumbs.get(b.id) ?? null, label: `${b.name} thumbnail`, className: "rounded", style: { width: "100%", aspectRatio: `${thumbnailWidth} / ${thumbnailHeight}` } }),
|
|
612
|
+
/* @__PURE__ */ jsx5("span", { className: "truncate text-[10px] text-slate-300", children: b.name })
|
|
603
613
|
]
|
|
604
614
|
},
|
|
605
615
|
b.id
|
|
606
616
|
)) }),
|
|
607
617
|
/* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-1.5", children: [
|
|
608
|
-
BRUSH_SLIDERS.map((c) => /* @__PURE__ */
|
|
618
|
+
BRUSH_SLIDERS.map((c) => /* @__PURE__ */ jsx5(PaintSlider, { control: c, value: readPath(brush, c.path), onChange: (v) => controller.setBrush(patchPath(brush, c.path, v)) }, c.path)),
|
|
609
619
|
/* @__PURE__ */ jsxs4("label", { className: "flex items-center justify-between gap-2", children: [
|
|
610
|
-
/* @__PURE__ */
|
|
611
|
-
/* @__PURE__ */
|
|
620
|
+
/* @__PURE__ */ jsx5("span", { className: "text-slate-400", children: "Rotation" }),
|
|
621
|
+
/* @__PURE__ */ jsx5("select", { "aria-label": "Brush rotation", value: brush.rotation, onChange: (e) => controller.setBrush({ rotation: e.target.value }), className: "rounded bg-slate-800 px-1 py-0.5", children: BRUSH_ROTATIONS.map((r) => /* @__PURE__ */ jsx5("option", { value: r, children: r === "stroke" ? "Follow stroke" : r === "barrel" ? "Pen barrel" : "Fixed" }, r)) })
|
|
612
622
|
] }),
|
|
613
623
|
/* @__PURE__ */ jsxs4("label", { className: "flex items-center gap-2", children: [
|
|
614
|
-
/* @__PURE__ */
|
|
624
|
+
/* @__PURE__ */ jsx5("input", { type: "checkbox", checked: brush.tip.pressureSize, onChange: (e) => controller.setBrush({ tip: { pressureSize: e.target.checked } }) }),
|
|
615
625
|
" Pressure \u2192 size"
|
|
616
626
|
] }),
|
|
617
627
|
/* @__PURE__ */ jsxs4("label", { className: "flex items-center gap-2", children: [
|
|
618
|
-
/* @__PURE__ */
|
|
628
|
+
/* @__PURE__ */ jsx5("input", { type: "checkbox", checked: brush.tip.pressureOpacity, onChange: (e) => controller.setBrush({ tip: { pressureOpacity: e.target.checked } }) }),
|
|
619
629
|
" Pressure \u2192 opacity"
|
|
620
630
|
] }),
|
|
621
631
|
brush.engine === "dab" && /* @__PURE__ */ jsxs4("label", { className: "flex items-center justify-between gap-2", children: [
|
|
622
|
-
/* @__PURE__ */
|
|
623
|
-
/* @__PURE__ */
|
|
632
|
+
/* @__PURE__ */ jsx5("span", { className: "text-slate-400", children: "Blend" }),
|
|
633
|
+
/* @__PURE__ */ jsx5("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: BLEND_MENU_GROUPS.map((g) => /* @__PURE__ */ jsx5("optgroup", { label: g.label, children: g.modes.map((m) => /* @__PURE__ */ jsx5("option", { value: m.id, title: m.description, children: m.label }, m.id)) }, g.label)) })
|
|
624
634
|
] })
|
|
625
635
|
] }),
|
|
626
636
|
engine && /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-1.5", "data-testid": "brush-engine", children: [
|
|
627
|
-
/* @__PURE__ */
|
|
637
|
+
/* @__PURE__ */ jsx5("span", { className: "font-medium text-slate-300", children: engine.title }),
|
|
628
638
|
engine.wetTool && /* @__PURE__ */ jsxs4("label", { className: "flex items-center justify-between gap-2", children: [
|
|
629
|
-
/* @__PURE__ */
|
|
630
|
-
/* @__PURE__ */
|
|
639
|
+
/* @__PURE__ */ jsx5("span", { className: "text-slate-400", children: "Wet tool" }),
|
|
640
|
+
/* @__PURE__ */ jsx5("select", { "aria-label": "Wet tool", value: brush.wet.tool, onChange: (e) => controller.setBrush({ wet: { tool: e.target.value } }), className: "rounded bg-slate-800 px-1 py-0.5", children: WET_TOOLS.map((t) => /* @__PURE__ */ jsx5("option", { value: t, children: WET_TOOL_LABELS[t] }, t)) })
|
|
631
641
|
] }),
|
|
632
642
|
engine.wetTool && /* @__PURE__ */ jsxs4("label", { className: "flex items-center justify-between gap-2", children: [
|
|
633
|
-
/* @__PURE__ */
|
|
634
|
-
/* @__PURE__ */
|
|
643
|
+
/* @__PURE__ */ jsx5("span", { className: "text-slate-400", children: "Medium" }),
|
|
644
|
+
/* @__PURE__ */ jsx5("select", { "aria-label": "Wet medium", value: brush.wet.medium, onChange: (e) => controller.setBrush({ wet: { medium: e.target.value } }), className: "rounded bg-slate-800 px-1 py-0.5", children: WET_MEDIA.map((m) => /* @__PURE__ */ jsx5("option", { value: m, children: WET_MEDIUM_LABELS[m] }, m)) })
|
|
635
645
|
] }),
|
|
636
646
|
engine.particleMenus && /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
637
647
|
/* @__PURE__ */ jsxs4("label", { className: "flex items-center justify-between gap-2", children: [
|
|
638
|
-
/* @__PURE__ */
|
|
639
|
-
/* @__PURE__ */
|
|
648
|
+
/* @__PURE__ */ jsx5("span", { className: "text-slate-400", children: "Particles" }),
|
|
649
|
+
/* @__PURE__ */ jsx5("select", { "aria-label": "Particle kind", value: brush.particle.kind, onChange: (e) => controller.setBrush({ particle: { kind: e.target.value } }), className: "rounded bg-slate-800 px-1 py-0.5", children: PARTICLE_KINDS.map((k) => /* @__PURE__ */ jsx5("option", { value: k, children: PARTICLE_KIND_LABELS[k] }, k)) })
|
|
640
650
|
] }),
|
|
641
651
|
/* @__PURE__ */ jsxs4("label", { className: "flex items-center justify-between gap-2", children: [
|
|
642
|
-
/* @__PURE__ */
|
|
643
|
-
/* @__PURE__ */
|
|
652
|
+
/* @__PURE__ */ jsx5("span", { className: "text-slate-400", children: "Drawn as" }),
|
|
653
|
+
/* @__PURE__ */ jsx5("select", { "aria-label": "Particle render", value: brush.particle.render, onChange: (e) => controller.setBrush({ particle: { render: e.target.value } }), className: "rounded bg-slate-800 px-1 py-0.5", children: PARTICLE_RENDERS.map((k) => /* @__PURE__ */ jsx5("option", { value: k, children: PARTICLE_RENDER_LABELS[k] }, k)) })
|
|
644
654
|
] })
|
|
645
655
|
] }),
|
|
646
|
-
engine.stickerStamps && /* @__PURE__ */
|
|
656
|
+
engine.stickerStamps && /* @__PURE__ */ jsx5("div", { className: "flex flex-wrap gap-1", role: "group", "aria-label": "Sticker stamps", children: STAMP_KINDS.map((k) => /* @__PURE__ */ jsx5(
|
|
647
657
|
"button",
|
|
648
658
|
{
|
|
649
659
|
type: "button",
|
|
@@ -655,12 +665,12 @@ var BrushPanel = ({ controller, thumbnailWidth = 112, thumbnailHeight = 32, clas
|
|
|
655
665
|
k
|
|
656
666
|
)) }),
|
|
657
667
|
engine.impastoTool && /* @__PURE__ */ jsxs4("label", { className: "flex items-center justify-between gap-2", children: [
|
|
658
|
-
/* @__PURE__ */
|
|
659
|
-
/* @__PURE__ */
|
|
668
|
+
/* @__PURE__ */ jsx5("span", { className: "text-slate-400", children: "Tool" }),
|
|
669
|
+
/* @__PURE__ */ jsx5("select", { "aria-label": "Impasto tool", value: brush.impasto.tool, onChange: (e) => controller.setBrush({ impasto: { tool: e.target.value } }), className: "rounded bg-slate-800 px-1 py-0.5", children: IMPASTO_TOOLS.map((t) => /* @__PURE__ */ jsx5("option", { value: t, children: IMPASTO_TOOL_LABELS[t] }, t)) })
|
|
660
670
|
] }),
|
|
661
|
-
engine.sliders.map((c) => /* @__PURE__ */
|
|
671
|
+
engine.sliders.map((c) => /* @__PURE__ */ jsx5(PaintSlider, { control: c, value: readPath(brush, c.path), onChange: (v) => controller.setBrush(patchPath(brush, c.path, v)) }, c.path)),
|
|
662
672
|
engine.mixingPath && /* @__PURE__ */ jsxs4("label", { className: "flex items-center justify-between gap-2", children: [
|
|
663
|
-
/* @__PURE__ */
|
|
673
|
+
/* @__PURE__ */ jsx5("span", { className: "text-slate-400", children: "Mixing" }),
|
|
664
674
|
/* @__PURE__ */ jsxs4(
|
|
665
675
|
"select",
|
|
666
676
|
{
|
|
@@ -669,57 +679,45 @@ var BrushPanel = ({ controller, thumbnailWidth = 112, thumbnailHeight = 32, clas
|
|
|
669
679
|
onChange: (e) => controller.setBrush(patchBrushValue(brush, engine.mixingPath, e.target.value)),
|
|
670
680
|
className: "rounded bg-slate-800 px-1 py-0.5",
|
|
671
681
|
children: [
|
|
672
|
-
/* @__PURE__ */
|
|
673
|
-
/* @__PURE__ */
|
|
682
|
+
/* @__PURE__ */ jsx5("option", { value: "pigment", children: "Pigment" }),
|
|
683
|
+
/* @__PURE__ */ jsx5("option", { value: "rgb", children: "RGB" })
|
|
674
684
|
]
|
|
675
685
|
}
|
|
676
686
|
)
|
|
677
687
|
] }),
|
|
678
688
|
engine.dirty && /* @__PURE__ */ jsxs4("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: [
|
|
679
|
-
/* @__PURE__ */
|
|
689
|
+
/* @__PURE__ */ jsx5("input", { type: "checkbox", "aria-label": "Dirty brush", checked: brush.behavior.dirty, onChange: (e) => controller.setBrush({ behavior: { dirty: e.target.checked } }) }),
|
|
680
690
|
" Dirty brush"
|
|
681
691
|
] }),
|
|
682
|
-
|
|
683
|
-
/* @__PURE__ */ jsxs4("
|
|
684
|
-
/* @__PURE__ */
|
|
685
|
-
/* @__PURE__ */
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
692
|
+
brushTakesPaint(brush) && /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-1.5", "data-testid": "brush-gradient", children: [
|
|
693
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex items-center justify-between gap-2", children: [
|
|
694
|
+
/* @__PURE__ */ jsx5("span", { className: "text-slate-400", title: "A gradient of the brush's own, used instead of the paint (the multi-colour brush)", children: "Brush gradient" }),
|
|
695
|
+
/* @__PURE__ */ jsx5(
|
|
696
|
+
"button",
|
|
697
|
+
{
|
|
698
|
+
type: "button",
|
|
699
|
+
"aria-pressed": brush.paint !== null,
|
|
700
|
+
className: chip(brush.paint !== null),
|
|
701
|
+
onClick: () => attempt(() => controller.setBrush({ paint: brush.paint ? null : DEFAULT_PAINT_GRADIENT })),
|
|
702
|
+
children: brush.paint ? "On" : "Off"
|
|
703
|
+
}
|
|
704
|
+
)
|
|
690
705
|
] }),
|
|
691
|
-
|
|
692
|
-
/* @__PURE__ */ jsxs4("div", { className: "flex flex-wrap items-center gap-1", "aria-label": "Multi-colour colours", children: [
|
|
693
|
-
mc.colors.map((col, i) => /* @__PURE__ */ jsxs4("span", { className: "flex items-center", children: [
|
|
694
|
-
/* @__PURE__ */ jsx4("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" }),
|
|
695
|
-
/* @__PURE__ */ jsx4("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" })
|
|
696
|
-
] }, i)),
|
|
697
|
-
mc.colors.length < 8 && /* @__PURE__ */ jsx4("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: "+" }),
|
|
698
|
-
mc.colors.length < 2 && /* @__PURE__ */ jsx4("span", { className: "text-[10px] text-slate-500", children: "Uses the paint and second colours" })
|
|
699
|
-
] }),
|
|
700
|
-
mc.mode === "dab" && /* @__PURE__ */ jsxs4("label", { className: "flex items-center justify-between gap-2", children: [
|
|
701
|
-
/* @__PURE__ */ jsx4("span", { className: "text-slate-400", children: "Order" }),
|
|
702
|
-
/* @__PURE__ */ jsxs4("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: [
|
|
703
|
-
/* @__PURE__ */ jsx4("option", { value: "sequence", children: "In order" }),
|
|
704
|
-
/* @__PURE__ */ jsx4("option", { value: "random", children: "Random" })
|
|
705
|
-
] })
|
|
706
|
-
] }),
|
|
707
|
-
multiColorSliders(mc.mode).map((c) => /* @__PURE__ */ jsx4(PaintSlider, { control: c, value: readPath(brush, c.path), onChange: (v) => controller.setBrush(patchPath(brush, c.path, v)) }, c.path))
|
|
708
|
-
] })
|
|
706
|
+
brush.paint && /* @__PURE__ */ jsx5(PaintGradientEditor, { value: brush.paint, colors: { paint: state.color, secondary: state.secondaryColor }, onChange: (g) => attempt(() => controller.setBrush({ paint: g })) })
|
|
709
707
|
] }),
|
|
710
708
|
engine.load && /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-1", children: [
|
|
711
|
-
/* @__PURE__ */
|
|
712
|
-
/* @__PURE__ */
|
|
713
|
-
brush.engine === "mixer" && /* @__PURE__ */
|
|
714
|
-
/* @__PURE__ */
|
|
709
|
+
/* @__PURE__ */ jsx5("span", { className: "text-slate-400", children: "Carries" }),
|
|
710
|
+
/* @__PURE__ */ jsx5("span", { "aria-label": "Brush load", title: carried ?? "Clean", className: "h-4 w-4 rounded ring-1 ring-slate-700", style: { background: carried ?? "transparent" } }),
|
|
711
|
+
brush.engine === "mixer" && /* @__PURE__ */ jsx5("button", { type: "button", className: button, onClick: () => controller.loadMixerBrush(), children: "Load" }),
|
|
712
|
+
/* @__PURE__ */ jsx5("button", { type: "button", className: button, onClick: () => controller.cleanMixerBrush(brush.engine === "mixer"), children: "Clean" })
|
|
715
713
|
] })
|
|
716
714
|
] }),
|
|
717
715
|
/* @__PURE__ */ jsxs4("div", { className: "flex flex-wrap items-center gap-1", children: [
|
|
718
|
-
/* @__PURE__ */
|
|
719
|
-
/* @__PURE__ */
|
|
720
|
-
/* @__PURE__ */
|
|
721
|
-
/* @__PURE__ */
|
|
722
|
-
/* @__PURE__ */
|
|
716
|
+
/* @__PURE__ */ jsx5("input", { value: saveName, placeholder: "Brush name", "aria-label": "Brush name", onChange: (e) => setSaveName(e.target.value), className: "w-28 rounded bg-slate-800 px-1.5 py-0.5" }),
|
|
717
|
+
/* @__PURE__ */ jsx5("button", { type: "button", className: button, disabled: !saveName.trim(), onClick: () => attempt(() => (controller.saveBrush({ id: slug(saveName), name: saveName.trim() }), setSaveName(""))), children: "Save brush" }),
|
|
718
|
+
/* @__PURE__ */ jsx5("button", { type: "button", className: button, onClick: () => attempt(() => downloadBytes2(JSON.stringify(controller.exportBrushes(), null, 2), "brushes.json", "application/json")), children: "Export" }),
|
|
719
|
+
/* @__PURE__ */ jsx5("button", { type: "button", className: button, onClick: () => fileRef.current?.click(), children: "Import\u2026" }),
|
|
720
|
+
/* @__PURE__ */ jsx5(
|
|
723
721
|
"input",
|
|
724
722
|
{
|
|
725
723
|
ref: fileRef,
|
|
@@ -735,14 +733,14 @@ var BrushPanel = ({ controller, thumbnailWidth = 112, thumbnailHeight = 32, clas
|
|
|
735
733
|
}
|
|
736
734
|
)
|
|
737
735
|
] }),
|
|
738
|
-
/* @__PURE__ */
|
|
736
|
+
/* @__PURE__ */ jsx5(ErrorLine, { error })
|
|
739
737
|
] });
|
|
740
738
|
};
|
|
741
739
|
|
|
742
740
|
// src/paint-editor/PaperPanel.tsx
|
|
743
741
|
import { useMemo as useMemo4 } from "react";
|
|
744
742
|
import { LIGHT_SLIDERS, PAPER_FAMILIES, PAPER_LIBRARY, PAPER_SLIDERS, patchPath as patchPath2, readPath as readPath2, renderPaper } from "@pixerate/editor/paint-editor";
|
|
745
|
-
import { jsx as
|
|
743
|
+
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
746
744
|
var SWATCH = { x: 0, y: 0, width: 64, height: 40 };
|
|
747
745
|
var PaperPanel = ({ controller, className }) => {
|
|
748
746
|
const state = usePaintEditorState(controller);
|
|
@@ -751,8 +749,8 @@ var PaperPanel = ({ controller, className }) => {
|
|
|
751
749
|
const previews = useMemo4(() => new Map(PAPER_LIBRARY.map((p) => [p.preset, { ...SWATCH, data: renderPaper({ ...p, shading: Math.max(0.8, p.shading) }, SWATCH) }])), []);
|
|
752
750
|
return /* @__PURE__ */ jsxs5("div", { className: `${panel} ${className ?? ""}`, "data-testid": "paper-panel", children: [
|
|
753
751
|
PAPER_FAMILIES.map((family) => /* @__PURE__ */ jsxs5("div", { children: [
|
|
754
|
-
/* @__PURE__ */
|
|
755
|
-
/* @__PURE__ */
|
|
752
|
+
/* @__PURE__ */ jsx6("div", { className: "mb-0.5 text-[10px] uppercase tracking-wide text-slate-500", children: family }),
|
|
753
|
+
/* @__PURE__ */ jsx6("div", { className: "flex flex-wrap gap-1", role: "listbox", "aria-label": `${family} papers`, children: PAPER_LIBRARY.filter((p) => p.family === family).map((p) => /* @__PURE__ */ jsx6(
|
|
756
754
|
"button",
|
|
757
755
|
{
|
|
758
756
|
type: "button",
|
|
@@ -761,28 +759,28 @@ var PaperPanel = ({ controller, className }) => {
|
|
|
761
759
|
title: p.name,
|
|
762
760
|
className: `rounded p-0.5 ${paper.preset === p.preset ? "ring-2 ring-indigo-500" : "ring-1 ring-slate-700 hover:ring-slate-500"}`,
|
|
763
761
|
onClick: () => attempt(() => controller.setPaper({ preset: p.preset })),
|
|
764
|
-
children: /* @__PURE__ */
|
|
762
|
+
children: /* @__PURE__ */ jsx6(PixelCanvas, { pixels: previews.get(p.preset) ?? null, label: p.name, style: { width: 48, height: 30 } })
|
|
765
763
|
},
|
|
766
764
|
p.preset
|
|
767
765
|
)) })
|
|
768
766
|
] }, family)),
|
|
769
|
-
/* @__PURE__ */
|
|
767
|
+
/* @__PURE__ */ jsx6("div", { className: "text-slate-300", children: paper.name }),
|
|
770
768
|
/* @__PURE__ */ jsxs5("label", { className: "flex items-center justify-between", children: [
|
|
771
|
-
/* @__PURE__ */
|
|
772
|
-
/* @__PURE__ */
|
|
769
|
+
/* @__PURE__ */ jsx6("span", { className: "text-slate-400", children: "Colour" }),
|
|
770
|
+
/* @__PURE__ */ jsx6(CommitColorInput, { label: "Paper colour", value: paper.color, onCommit: (v) => attempt(() => controller.setPaper({ color: v })), className: "h-6 w-10 rounded bg-transparent" })
|
|
773
771
|
] }),
|
|
774
|
-
PAPER_SLIDERS.map((c) => /* @__PURE__ */
|
|
772
|
+
PAPER_SLIDERS.map((c) => /* @__PURE__ */ jsx6(PaintSlider, { control: c, value: readPath2(paper, c.path), onCommit: (v) => attempt(() => controller.setPaper(patchPath2(paper, c.path, v))) }, c.path)),
|
|
775
773
|
/* @__PURE__ */ jsxs5("label", { className: "flex items-center gap-2", children: [
|
|
776
|
-
/* @__PURE__ */
|
|
774
|
+
/* @__PURE__ */ jsx6("input", { type: "checkbox", checked: paper.tiling === "mirror", onChange: (e) => attempt(() => controller.setPaper({ tiling: e.target.checked ? "mirror" : "repeat" })) }),
|
|
777
775
|
" Mirror tiling"
|
|
778
776
|
] }),
|
|
779
|
-
/* @__PURE__ */
|
|
777
|
+
/* @__PURE__ */ jsx6("button", { type: "button", className: "self-start px-2 py-1 rounded text-[11px] bg-slate-800 text-slate-200 hover:bg-slate-700", onClick: () => attempt(() => controller.setPaper({ seed: paper.seed + 1 })), children: "New sheet" }),
|
|
780
778
|
/* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-1.5", "data-testid": "light-panel", children: [
|
|
781
|
-
/* @__PURE__ */
|
|
782
|
-
LIGHT_SLIDERS.map((c) => /* @__PURE__ */
|
|
783
|
-
/* @__PURE__ */
|
|
779
|
+
/* @__PURE__ */ jsx6("span", { className: "font-medium text-slate-300", children: "Light (thick paint, metallic paper)" }),
|
|
780
|
+
LIGHT_SLIDERS.map((c) => /* @__PURE__ */ jsx6(PaintSlider, { control: c, value: readPath2(controller.getLighting(), c.path), onChange: (v) => attempt(() => controller.setLighting({ [c.path]: v })) }, c.path)),
|
|
781
|
+
/* @__PURE__ */ jsx6("button", { type: "button", className: "self-start px-2 py-1 rounded text-[11px] bg-slate-800 text-slate-200 hover:bg-slate-700", onClick: () => attempt(() => controller.flattenRelief()), children: "Flatten relief" })
|
|
784
782
|
] }),
|
|
785
|
-
/* @__PURE__ */
|
|
783
|
+
/* @__PURE__ */ jsx6(ErrorLine, { error })
|
|
786
784
|
] });
|
|
787
785
|
};
|
|
788
786
|
|
|
@@ -803,7 +801,8 @@ import {
|
|
|
803
801
|
wheelPick,
|
|
804
802
|
wheelPoint
|
|
805
803
|
} from "@pixerate/editor/paint-editor";
|
|
806
|
-
import {
|
|
804
|
+
import { DEFAULT_PAINT_GRADIENT as DEFAULT_PAINT_GRADIENT2, paintSourceCss } from "@pixerate/editor/paint-editor";
|
|
805
|
+
import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
807
806
|
var ColorPanel = ({ controller, wheelSize = 168, mixingPad = true, className }) => {
|
|
808
807
|
const state = usePaintEditorState(controller);
|
|
809
808
|
const [error, attempt] = useAttempt();
|
|
@@ -833,7 +832,7 @@ var ColorPanel = ({ controller, wheelSize = 168, mixingPad = true, className })
|
|
|
833
832
|
};
|
|
834
833
|
return /* @__PURE__ */ jsxs6("div", { className: `${panel} ${className ?? ""}`, "data-testid": "color-panel", children: [
|
|
835
834
|
/* @__PURE__ */ jsxs6("div", { className: "relative self-center", style: { width: wheelSize, height: wheelSize }, children: [
|
|
836
|
-
/* @__PURE__ */
|
|
835
|
+
/* @__PURE__ */ jsx7(
|
|
837
836
|
"div",
|
|
838
837
|
{
|
|
839
838
|
role: "slider",
|
|
@@ -847,20 +846,20 @@ var ColorPanel = ({ controller, wheelSize = 168, mixingPad = true, className })
|
|
|
847
846
|
const rect = e.currentTarget.getBoundingClientRect();
|
|
848
847
|
controller.setColor(wheelPick(e.clientX - rect.left - R, e.clientY - rect.top - R, wheel, color));
|
|
849
848
|
},
|
|
850
|
-
children: /* @__PURE__ */
|
|
849
|
+
children: /* @__PURE__ */ jsx7(PixelCanvas, { pixels: ring, label: "Colour wheel", style: { width: wheelSize, height: wheelSize } })
|
|
851
850
|
}
|
|
852
851
|
),
|
|
853
|
-
/* @__PURE__ */
|
|
852
|
+
/* @__PURE__ */ jsx7("svg", { className: "pointer-events-none absolute inset-0", width: wheelSize, height: wheelSize, "aria-hidden": "true", children: harmony.map((h, i) => {
|
|
854
853
|
const p = wheelPoint(h.angle, mid);
|
|
855
|
-
return /* @__PURE__ */
|
|
854
|
+
return /* @__PURE__ */ jsx7("circle", { cx: R + p.x, cy: R + p.y, r: i === 0 ? 7 : 5, fill: h.color, stroke: "#fff", strokeWidth: i === 0 ? 2 : 1 }, i);
|
|
856
855
|
}) }),
|
|
857
|
-
/* @__PURE__ */
|
|
856
|
+
/* @__PURE__ */ jsx7("div", { className: "absolute rounded-full border border-slate-700", style: { left: R - R * 0.45, top: R - R * 0.45, width: R * 0.9, height: R * 0.9, background: color } })
|
|
858
857
|
] }),
|
|
859
858
|
/* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-2", children: [
|
|
860
|
-
/* @__PURE__ */
|
|
861
|
-
/* @__PURE__ */
|
|
862
|
-
/* @__PURE__ */
|
|
863
|
-
/* @__PURE__ */
|
|
859
|
+
/* @__PURE__ */ jsx7("input", { type: "color", "aria-label": "Paint colour", value: color, onChange: (e) => controller.setColor(e.target.value), className: "h-7 w-9 rounded bg-transparent" }),
|
|
860
|
+
/* @__PURE__ */ jsx7("input", { type: "color", "aria-label": "Second colour", value: state.secondaryColor, onChange: (e) => controller.setSecondaryColor(e.target.value), className: "h-5 w-7 rounded bg-transparent" }),
|
|
861
|
+
/* @__PURE__ */ jsx7("button", { type: "button", className: button, title: "Swap colours (X)", onClick: () => controller.swapColors(), children: "Swap" }),
|
|
862
|
+
/* @__PURE__ */ jsx7(
|
|
864
863
|
"input",
|
|
865
864
|
{
|
|
866
865
|
"aria-label": "Hex colour",
|
|
@@ -871,35 +870,58 @@ var ColorPanel = ({ controller, wheelSize = 168, mixingPad = true, className })
|
|
|
871
870
|
color
|
|
872
871
|
)
|
|
873
872
|
] }),
|
|
873
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-2", "data-testid": "paint-source", children: [
|
|
874
|
+
/* @__PURE__ */ jsx7(
|
|
875
|
+
"span",
|
|
876
|
+
{
|
|
877
|
+
className: "h-5 flex-1 rounded ring-1 ring-slate-700",
|
|
878
|
+
"aria-label": state.paint.type === "gradient" ? "Gradient paint" : "Paint colour",
|
|
879
|
+
"data-testid": "paint-well",
|
|
880
|
+
style: { background: paintSourceCss(state.paint, { paint: color, secondary: state.secondaryColor }) }
|
|
881
|
+
}
|
|
882
|
+
),
|
|
883
|
+
/* @__PURE__ */ jsx7(
|
|
884
|
+
"button",
|
|
885
|
+
{
|
|
886
|
+
type: "button",
|
|
887
|
+
className: chip(state.paint.type === "gradient"),
|
|
888
|
+
"aria-pressed": state.paint.type === "gradient",
|
|
889
|
+
title: "Paint that changes colour as the stroke goes",
|
|
890
|
+
onClick: () => attempt(() => state.paint.type === "gradient" ? controller.setColor(color) : controller.setPaint(DEFAULT_PAINT_GRADIENT2)),
|
|
891
|
+
children: "Gradient"
|
|
892
|
+
}
|
|
893
|
+
)
|
|
894
|
+
] }),
|
|
895
|
+
state.paint.type === "gradient" && /* @__PURE__ */ jsx7(PaintGradientEditor, { value: state.paint, colors: { paint: color, secondary: state.secondaryColor }, onChange: (g2) => attempt(() => controller.setPaint(g2)) }),
|
|
874
896
|
/* @__PURE__ */ jsxs6("label", { className: "flex items-center gap-2", children: [
|
|
875
|
-
/* @__PURE__ */
|
|
876
|
-
/* @__PURE__ */
|
|
897
|
+
/* @__PURE__ */ jsx7("span", { className: "w-16 text-slate-400", children: "Saturation" }),
|
|
898
|
+
/* @__PURE__ */ jsx7("input", { type: "range", "aria-label": "Saturation", min: 0, max: 1, step: 0.01, value: hsv.s, onChange: (e) => setHsv(Number(e.target.value), hsv.v), className: "flex-1 accent-indigo-500" })
|
|
877
899
|
] }),
|
|
878
900
|
/* @__PURE__ */ jsxs6("label", { className: "flex items-center gap-2", children: [
|
|
879
|
-
/* @__PURE__ */
|
|
880
|
-
/* @__PURE__ */
|
|
901
|
+
/* @__PURE__ */ jsx7("span", { className: "w-16 text-slate-400", children: "Value" }),
|
|
902
|
+
/* @__PURE__ */ jsx7("input", { type: "range", "aria-label": "Value", min: 0, max: 1, step: 0.01, value: hsv.v, onChange: (e) => setHsv(hsv.s, Number(e.target.value)), className: "flex-1 accent-indigo-500" })
|
|
881
903
|
] }),
|
|
882
904
|
/* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-1", children: [
|
|
883
|
-
/* @__PURE__ */
|
|
884
|
-
/* @__PURE__ */
|
|
885
|
-
/* @__PURE__ */
|
|
905
|
+
/* @__PURE__ */ jsx7("select", { "aria-label": "Harmony", value: kind, onChange: (e) => setKind(e.target.value), className: "rounded bg-slate-800 px-1 py-0.5", children: HARMONY_KINDS.map((k) => /* @__PURE__ */ jsx7("option", { value: k, children: k }, k)) }),
|
|
906
|
+
/* @__PURE__ */ jsx7("button", { type: "button", className: chip(wheel === "ryb"), "aria-pressed": wheel === "ryb", onClick: () => setWheel("ryb"), children: "RYB" }),
|
|
907
|
+
/* @__PURE__ */ jsx7("button", { type: "button", className: chip(wheel === "rgb"), "aria-pressed": wheel === "rgb", onClick: () => setWheel("rgb"), children: "RGB" })
|
|
886
908
|
] }),
|
|
887
909
|
/* @__PURE__ */ jsxs6("div", { className: "flex flex-wrap gap-1", "aria-label": "Harmony colours", children: [
|
|
888
|
-
harmony.map((h, i) => /* @__PURE__ */
|
|
889
|
-
/* @__PURE__ */
|
|
910
|
+
harmony.map((h, i) => /* @__PURE__ */ jsx7("button", { type: "button", "aria-label": `Use ${h.color}`, title: h.color, className: "h-6 w-6 rounded ring-1 ring-slate-700", style: { background: h.color }, onClick: () => controller.setColor(h.color) }, i)),
|
|
911
|
+
/* @__PURE__ */ jsx7("button", { type: "button", className: button, onClick: () => harmony.forEach((h) => controller.addSwatch(h.color)), children: "+ swatches" })
|
|
890
912
|
] }),
|
|
891
913
|
/* @__PURE__ */ jsxs6("div", { className: "flex flex-wrap gap-1", role: "listbox", "aria-label": "Swatches", children: [
|
|
892
|
-
state.swatches.map((s, i) => /* @__PURE__ */
|
|
914
|
+
state.swatches.map((s, i) => /* @__PURE__ */ jsx7(
|
|
893
915
|
"button",
|
|
894
916
|
{
|
|
895
917
|
type: "button",
|
|
896
918
|
role: "option",
|
|
897
|
-
"aria-selected": s.color === color,
|
|
898
|
-
"aria-label": s.name ?? s.color,
|
|
899
|
-
title: `${s.name ?? s.color} (right-click to remove)`,
|
|
919
|
+
"aria-selected": s.paint ? state.paint === s.paint : state.paint.type === "color" && s.color === color,
|
|
920
|
+
"aria-label": s.name ?? (s.paint ? "Gradient" : s.color),
|
|
921
|
+
title: `${s.name ?? (s.paint ? "Gradient" : s.color)} (right-click to remove)`,
|
|
900
922
|
className: "h-5 w-5 rounded ring-1 ring-slate-700",
|
|
901
|
-
style: { background: s.color },
|
|
902
|
-
onClick: () => controller.setColor(s.color),
|
|
923
|
+
style: { background: s.paint ? paintSourceCss(s.paint, { paint: color, secondary: state.secondaryColor }) : s.color },
|
|
924
|
+
onClick: () => attempt(() => s.paint ? controller.setPaint(s.paint) : controller.setColor(s.color)),
|
|
903
925
|
onContextMenu: (e) => {
|
|
904
926
|
e.preventDefault();
|
|
905
927
|
controller.removeSwatch(i);
|
|
@@ -907,15 +929,15 @@ var ColorPanel = ({ controller, wheelSize = 168, mixingPad = true, className })
|
|
|
907
929
|
},
|
|
908
930
|
i
|
|
909
931
|
)),
|
|
910
|
-
/* @__PURE__ */
|
|
932
|
+
/* @__PURE__ */ jsx7("button", { type: "button", className: button, "aria-label": "Add swatch", onClick: () => attempt(() => controller.addSwatch(state.paint)), children: "+" })
|
|
911
933
|
] }),
|
|
912
934
|
mixingPad && /* @__PURE__ */ jsxs6("div", { className: "flex flex-col gap-1", "data-testid": "mixing-pad", children: [
|
|
913
935
|
/* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-1", children: [
|
|
914
|
-
/* @__PURE__ */
|
|
915
|
-
["paint", "knife", "pick"].map((t) => /* @__PURE__ */
|
|
916
|
-
/* @__PURE__ */
|
|
936
|
+
/* @__PURE__ */ jsx7("span", { className: "text-slate-400", children: "Mixing pad" }),
|
|
937
|
+
["paint", "knife", "pick"].map((t) => /* @__PURE__ */ jsx7("button", { type: "button", "aria-pressed": padTool === t, className: chip(padTool === t), onClick: () => setPadTool(t), children: t === "paint" ? "Paint" : t === "knife" ? "Knife" : "Pick" }, t)),
|
|
938
|
+
/* @__PURE__ */ jsx7("button", { type: "button", className: button, onClick: () => controller.clearMixingPad(), children: "Clear" })
|
|
917
939
|
] }),
|
|
918
|
-
/* @__PURE__ */
|
|
940
|
+
/* @__PURE__ */ jsx7(
|
|
919
941
|
"div",
|
|
920
942
|
{
|
|
921
943
|
role: "application",
|
|
@@ -936,20 +958,20 @@ var ColorPanel = ({ controller, wheelSize = 168, mixingPad = true, className })
|
|
|
936
958
|
padPoints.current = null;
|
|
937
959
|
if (pts?.length) attempt(() => controller.mixingPadStroke(pts, { tool: padTool === "knife" ? "knife" : "paint" }));
|
|
938
960
|
},
|
|
939
|
-
children: /* @__PURE__ */
|
|
961
|
+
children: /* @__PURE__ */ jsx7(PixelCanvas, { pixels: pad, label: "Mixing pad pixels", className: "w-full rounded", style: { aspectRatio: `${MIXING_PAD_WIDTH} / ${MIXING_PAD_HEIGHT}` } })
|
|
940
962
|
}
|
|
941
963
|
),
|
|
942
|
-
/* @__PURE__ */
|
|
943
|
-
/* @__PURE__ */
|
|
944
|
-
/* @__PURE__ */
|
|
964
|
+
/* @__PURE__ */ jsx7("div", { className: "flex flex-wrap items-center gap-1", children: /* @__PURE__ */ jsxs6("label", { className: "flex items-center gap-1", children: [
|
|
965
|
+
/* @__PURE__ */ jsx7("span", { className: "text-slate-400", children: "Sample" }),
|
|
966
|
+
/* @__PURE__ */ jsx7("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: PAD_SAMPLE_SIZES.map((n) => /* @__PURE__ */ jsx7("option", { value: n, children: n === 1 ? "1 px" : `${n}\xD7${n}` }, n)) })
|
|
945
967
|
] }) }),
|
|
946
968
|
/* @__PURE__ */ jsxs6("div", { className: "flex flex-wrap items-center gap-1", children: [
|
|
947
|
-
/* @__PURE__ */
|
|
948
|
-
/* @__PURE__ */
|
|
949
|
-
/* @__PURE__ */
|
|
950
|
-
/* @__PURE__ */
|
|
951
|
-
/* @__PURE__ */
|
|
952
|
-
/* @__PURE__ */
|
|
969
|
+
/* @__PURE__ */ jsx7("span", { className: "h-4 w-4 rounded ring-1 ring-slate-700", style: { background: color } }),
|
|
970
|
+
/* @__PURE__ */ jsx7("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" }),
|
|
971
|
+
/* @__PURE__ */ jsx7("span", { className: "text-slate-500", children: ":" }),
|
|
972
|
+
/* @__PURE__ */ jsx7("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" }),
|
|
973
|
+
/* @__PURE__ */ jsx7("span", { className: "h-4 w-4 rounded ring-1 ring-slate-700", style: { background: state.secondaryColor } }),
|
|
974
|
+
/* @__PURE__ */ jsx7(
|
|
953
975
|
"button",
|
|
954
976
|
{
|
|
955
977
|
type: "button",
|
|
@@ -969,22 +991,22 @@ var ColorPanel = ({ controller, wheelSize = 168, mixingPad = true, className })
|
|
|
969
991
|
)
|
|
970
992
|
] }),
|
|
971
993
|
/* @__PURE__ */ jsxs6("div", { className: "flex flex-wrap items-center gap-1", children: [
|
|
972
|
-
/* @__PURE__ */
|
|
973
|
-
/* @__PURE__ */
|
|
994
|
+
/* @__PURE__ */ jsx7("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" }),
|
|
995
|
+
/* @__PURE__ */ jsx7("button", { type: "button", className: button, onClick: () => attempt(() => (controller.saveMixingPad(padName || void 0), setPadName(""))), children: "Keep pad" })
|
|
974
996
|
] }),
|
|
975
|
-
state.savedMixingPads.length > 0 && /* @__PURE__ */
|
|
976
|
-
/* @__PURE__ */
|
|
977
|
-
/* @__PURE__ */
|
|
997
|
+
state.savedMixingPads.length > 0 && /* @__PURE__ */ jsx7("ul", { className: "flex flex-col gap-0.5", "aria-label": "Kept pads", children: state.savedMixingPads.map((p) => /* @__PURE__ */ jsxs6("li", { className: "flex items-center gap-1", children: [
|
|
998
|
+
/* @__PURE__ */ jsx7("button", { type: "button", className: `${button} flex-1 text-left`, onClick: () => attempt(() => controller.loadMixingPad(p.id)), children: p.name }),
|
|
999
|
+
/* @__PURE__ */ jsx7("button", { type: "button", className: button, "aria-label": `Remove ${p.name}`, onClick: () => attempt(() => controller.removeMixingPad(p.id)), children: "\xD7" })
|
|
978
1000
|
] }, p.id)) })
|
|
979
1001
|
] }),
|
|
980
|
-
/* @__PURE__ */
|
|
1002
|
+
/* @__PURE__ */ jsx7(ErrorLine, { error })
|
|
981
1003
|
] });
|
|
982
1004
|
};
|
|
983
1005
|
|
|
984
1006
|
// src/paint-editor/PaintLayersPanel.tsx
|
|
985
1007
|
import { useState as useState7 } from "react";
|
|
986
1008
|
import { BLEND_MENU_GROUPS as BLEND_MENU_GROUPS2 } from "@pixerate/editor/paint-editor";
|
|
987
|
-
import { jsx as
|
|
1009
|
+
import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
988
1010
|
function siblingIndex(layers, l) {
|
|
989
1011
|
const sibs = layers.filter((x) => x.parentId === l.parentId).reverse();
|
|
990
1012
|
return { index: sibs.findIndex((x) => x.id === l.id), count: sibs.length };
|
|
@@ -1003,15 +1025,15 @@ var PaintLayersPanel = ({ controller, className }) => {
|
|
|
1003
1025
|
};
|
|
1004
1026
|
return /* @__PURE__ */ jsxs7("div", { className: `${panel} ${className ?? ""}`, "data-testid": "paint-layers-panel", children: [
|
|
1005
1027
|
/* @__PURE__ */ jsxs7("div", { className: "flex flex-wrap gap-1", children: [
|
|
1006
|
-
/* @__PURE__ */
|
|
1007
|
-
/* @__PURE__ */
|
|
1008
|
-
/* @__PURE__ */
|
|
1009
|
-
/* @__PURE__ */
|
|
1010
|
-
/* @__PURE__ */
|
|
1011
|
-
/* @__PURE__ */
|
|
1012
|
-
/* @__PURE__ */
|
|
1028
|
+
/* @__PURE__ */ jsx8("button", { type: "button", className: button, onClick: () => attempt(() => controller.addLayer()), children: "New layer" }),
|
|
1029
|
+
/* @__PURE__ */ jsx8("button", { type: "button", className: button, onClick: () => attempt(() => controller.addLayer({ type: "group" })), children: "New group" }),
|
|
1030
|
+
/* @__PURE__ */ jsx8("button", { type: "button", className: button, disabled: !active, onClick: () => active && attempt(() => controller.duplicateLayer(active.id)), children: "Duplicate" }),
|
|
1031
|
+
/* @__PURE__ */ jsx8("button", { type: "button", className: button, disabled: !active, "aria-label": "Move layer up", onClick: () => move(1), children: "\u2191" }),
|
|
1032
|
+
/* @__PURE__ */ jsx8("button", { type: "button", className: button, disabled: !active, "aria-label": "Move layer down", onClick: () => move(-1), children: "\u2193" }),
|
|
1033
|
+
/* @__PURE__ */ jsx8("button", { type: "button", className: button, disabled: !active, onClick: () => active && attempt(() => controller.mergeDown(active.id)), children: "Merge down" }),
|
|
1034
|
+
/* @__PURE__ */ jsx8("button", { type: "button", className: button, disabled: !active || layers.length < 2, onClick: () => active && attempt(() => controller.removeLayer(active.id)), children: "Delete" })
|
|
1013
1035
|
] }),
|
|
1014
|
-
/* @__PURE__ */
|
|
1036
|
+
/* @__PURE__ */ jsx8("ul", { role: "tree", "aria-label": "Layers", className: "flex flex-col gap-0.5", children: layers.map((l) => /* @__PURE__ */ jsxs7(
|
|
1015
1037
|
"li",
|
|
1016
1038
|
{
|
|
1017
1039
|
role: "treeitem",
|
|
@@ -1020,7 +1042,7 @@ var PaintLayersPanel = ({ controller, className }) => {
|
|
|
1020
1042
|
style: { paddingLeft: 6 + l.depth * 12 },
|
|
1021
1043
|
onClick: () => controller.setActiveLayer(l.id),
|
|
1022
1044
|
children: [
|
|
1023
|
-
/* @__PURE__ */
|
|
1045
|
+
/* @__PURE__ */ jsx8(
|
|
1024
1046
|
"button",
|
|
1025
1047
|
{
|
|
1026
1048
|
type: "button",
|
|
@@ -1033,7 +1055,7 @@ var PaintLayersPanel = ({ controller, className }) => {
|
|
|
1033
1055
|
children: l.visible ? "\u25CF" : "\u25CB"
|
|
1034
1056
|
}
|
|
1035
1057
|
),
|
|
1036
|
-
renaming === l.id ? /* @__PURE__ */
|
|
1058
|
+
renaming === l.id ? /* @__PURE__ */ jsx8(
|
|
1037
1059
|
"input",
|
|
1038
1060
|
{
|
|
1039
1061
|
autoFocus: true,
|
|
@@ -1054,15 +1076,15 @@ var PaintLayersPanel = ({ controller, className }) => {
|
|
|
1054
1076
|
l.type === "group" ? "\u25B8 " : "",
|
|
1055
1077
|
l.name
|
|
1056
1078
|
] }),
|
|
1057
|
-
l.locks.pixels || l.locks.all ? /* @__PURE__ */
|
|
1079
|
+
l.locks.pixels || l.locks.all ? /* @__PURE__ */ jsx8("span", { title: "Locked", children: "\u{1F512}" }) : null
|
|
1058
1080
|
]
|
|
1059
1081
|
},
|
|
1060
1082
|
l.id
|
|
1061
1083
|
)) }),
|
|
1062
1084
|
active && /* @__PURE__ */ jsxs7("div", { className: "flex flex-col gap-1.5", children: [
|
|
1063
1085
|
/* @__PURE__ */ jsxs7("label", { className: "flex items-center gap-2", children: [
|
|
1064
|
-
/* @__PURE__ */
|
|
1065
|
-
/* @__PURE__ */
|
|
1086
|
+
/* @__PURE__ */ jsx8("span", { className: "w-14 text-slate-400", children: "Opacity" }),
|
|
1087
|
+
/* @__PURE__ */ jsx8(
|
|
1066
1088
|
"input",
|
|
1067
1089
|
{
|
|
1068
1090
|
type: "range",
|
|
@@ -1077,24 +1099,24 @@ var PaintLayersPanel = ({ controller, className }) => {
|
|
|
1077
1099
|
)
|
|
1078
1100
|
] }),
|
|
1079
1101
|
/* @__PURE__ */ jsxs7("label", { className: "flex items-center gap-2", children: [
|
|
1080
|
-
/* @__PURE__ */
|
|
1102
|
+
/* @__PURE__ */ jsx8("span", { className: "w-14 text-slate-400", children: "Blend" }),
|
|
1081
1103
|
/* @__PURE__ */ jsxs7("select", { "aria-label": "Layer blend", value: active.blend, onChange: (e) => attempt(() => controller.updateLayer(active.id, { blend: e.target.value })), className: "flex-1 rounded bg-slate-800 px-1 py-0.5", children: [
|
|
1082
|
-
active.type === "group" && /* @__PURE__ */
|
|
1083
|
-
BLEND_MENU_GROUPS2.map((g) => /* @__PURE__ */
|
|
1104
|
+
active.type === "group" && /* @__PURE__ */ jsx8("option", { value: "pass-through", children: "Pass through" }),
|
|
1105
|
+
BLEND_MENU_GROUPS2.map((g) => /* @__PURE__ */ jsx8("optgroup", { label: g.label, children: g.modes.map((m) => /* @__PURE__ */ jsx8("option", { value: m.id, title: m.description, children: m.label }, m.id)) }, g.label))
|
|
1084
1106
|
] })
|
|
1085
1107
|
] }),
|
|
1086
1108
|
/* @__PURE__ */ jsxs7("div", { className: "flex gap-1", children: [
|
|
1087
|
-
["pixels", "transparency"].map((k) => /* @__PURE__ */
|
|
1088
|
-
/* @__PURE__ */
|
|
1109
|
+
["pixels", "transparency"].map((k) => /* @__PURE__ */ jsx8("button", { type: "button", "aria-pressed": Boolean(active.locks[k]), className: chip(Boolean(active.locks[k])), onClick: () => attempt(() => controller.updateLayer(active.id, { locks: { [k]: !active.locks[k] } })), children: k === "pixels" ? "Lock pixels" : "Lock transparency" }, k)),
|
|
1110
|
+
/* @__PURE__ */ jsx8("button", { type: "button", "aria-pressed": active.clipped, className: chip(active.clipped), onClick: () => attempt(() => controller.updateLayer(active.id, { clipped: !active.clipped })), children: "Clip" })
|
|
1089
1111
|
] })
|
|
1090
1112
|
] }),
|
|
1091
|
-
/* @__PURE__ */
|
|
1113
|
+
/* @__PURE__ */ jsx8(ErrorLine, { error })
|
|
1092
1114
|
] });
|
|
1093
1115
|
};
|
|
1094
1116
|
|
|
1095
1117
|
// src/paint-editor/WetPanel.tsx
|
|
1096
|
-
import { WET_FLOW_KINDS, WET_PARAM_SLIDERS, WET_SPEED_SLIDER, WET_TILT_SLIDERS, readPath as readPath3 } from "@pixerate/editor/paint-editor";
|
|
1097
|
-
import { jsx as
|
|
1118
|
+
import { WET_FLOW_KINDS, WET_GPU_OPTIONS, WET_PARAM_SLIDERS, WET_SPEED_SLIDER, WET_TILT_SLIDERS, readPath as readPath3, wetEngineSummary } from "@pixerate/editor/paint-editor";
|
|
1119
|
+
import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1098
1120
|
var FLOW_LABELS = { none: "None", linear: "One way", swirl: "Swirl", radial: "Outwards", turbulent: "Eddies" };
|
|
1099
1121
|
var WetPanel = ({ controller, className }) => {
|
|
1100
1122
|
const state = usePaintEditorState(controller);
|
|
@@ -1105,29 +1127,29 @@ var WetPanel = ({ controller, className }) => {
|
|
|
1105
1127
|
const backend = controller.wetState();
|
|
1106
1128
|
return /* @__PURE__ */ jsxs8("div", { className: `${panel} ${className ?? ""}`, "data-testid": "wet-panel", children: [
|
|
1107
1129
|
/* @__PURE__ */ jsxs8("div", { className: "flex items-center justify-between", children: [
|
|
1108
|
-
/* @__PURE__ */
|
|
1130
|
+
/* @__PURE__ */ jsx9("span", { className: wet.wet ? "text-sky-300" : "text-slate-400", "data-testid": "wet-status", children: wet.wet ? wet.paused ? "Wet (paused)" : "Wet" : "Dry" }),
|
|
1109
1131
|
/* @__PURE__ */ jsxs8("span", { className: "text-[10px] text-slate-500", children: [
|
|
1110
1132
|
"step ",
|
|
1111
1133
|
wet.clock
|
|
1112
1134
|
] })
|
|
1113
1135
|
] }),
|
|
1114
1136
|
/* @__PURE__ */ jsxs8("div", { className: "flex flex-wrap gap-1", children: [
|
|
1115
|
-
/* @__PURE__ */
|
|
1116
|
-
/* @__PURE__ */
|
|
1117
|
-
/* @__PURE__ */
|
|
1118
|
-
/* @__PURE__ */
|
|
1119
|
-
/* @__PURE__ */
|
|
1137
|
+
/* @__PURE__ */ jsx9("button", { type: "button", className: button, disabled: !wet.layerIds.length, onClick: () => attempt(async () => (await controller.wetIdle(), controller.dryPaint())), children: "Dry now" }),
|
|
1138
|
+
/* @__PURE__ */ jsx9("button", { type: "button", className: chip(wet.paused), "aria-pressed": wet.paused, onClick: () => controller.setWetPaused(!wet.paused), children: wet.paused ? "Resume" : "Pause" }),
|
|
1139
|
+
/* @__PURE__ */ jsx9("button", { type: "button", className: chip(wet.overlay), "aria-pressed": wet.overlay, onClick: () => controller.setWaterOverlay(!wet.overlay), children: "Water" }),
|
|
1140
|
+
/* @__PURE__ */ jsx9("button", { type: "button", className: chip(wet.instantDry), "aria-pressed": wet.instantDry, title: "Each wet stroke dries as soon as it ends", onClick: () => controller.setInstantDry(!wet.instantDry), children: "Instant dry" }),
|
|
1141
|
+
/* @__PURE__ */ jsx9("button", { type: "button", className: button, disabled: !layerId, onClick: () => attempt(async () => (await controller.wetIdle(), controller.wetPaper({}))), children: "Wet paper" })
|
|
1120
1142
|
] }),
|
|
1121
|
-
/* @__PURE__ */
|
|
1122
|
-
WET_TILT_SLIDERS.map((c) => /* @__PURE__ */
|
|
1143
|
+
/* @__PURE__ */ jsx9(PaintSlider, { control: WET_SPEED_SLIDER, value: wet.speed, onChange: (v) => controller.setWetSpeed(v) }),
|
|
1144
|
+
WET_TILT_SLIDERS.map((c) => /* @__PURE__ */ jsx9(PaintSlider, { control: c, value: readPath3(wet.tilt, c.path), onCommit: (v) => attempt(async () => (await controller.wetIdle(), controller.setCanvasTilt({ [c.path]: v }))) }, c.path)),
|
|
1123
1145
|
info && /* @__PURE__ */ jsxs8("div", { className: "flex flex-col gap-1.5", "data-testid": "wet-layer", children: [
|
|
1124
|
-
/* @__PURE__ */
|
|
1125
|
-
WET_PARAM_SLIDERS.map((c) => /* @__PURE__ */
|
|
1146
|
+
/* @__PURE__ */ jsx9("span", { className: "font-medium text-slate-300", children: "This layer" }),
|
|
1147
|
+
WET_PARAM_SLIDERS.map((c) => /* @__PURE__ */ jsx9(PaintSlider, { control: c, value: readPath3(info.params, c.path), onCommit: (v) => attempt(async () => (await controller.wetIdle(), controller.setWetParams(layerId, { [c.path]: v }))) }, c.path)),
|
|
1126
1148
|
/* @__PURE__ */ jsxs8("label", { className: "flex items-center justify-between gap-2", children: [
|
|
1127
|
-
/* @__PURE__ */
|
|
1128
|
-
/* @__PURE__ */
|
|
1149
|
+
/* @__PURE__ */ jsx9("span", { className: "text-slate-400", children: "Flow map" }),
|
|
1150
|
+
/* @__PURE__ */ jsx9("select", { "aria-label": "Flow map", value: info.flow.kind, onChange: (e) => attempt(async () => (await controller.wetIdle(), controller.setFlowMap(layerId, { kind: e.target.value }))), className: "rounded bg-slate-800 px-1 py-0.5", children: WET_FLOW_KINDS.map((k) => /* @__PURE__ */ jsx9("option", { value: k, children: FLOW_LABELS[k] }, k)) })
|
|
1129
1151
|
] }),
|
|
1130
|
-
info.flow.kind !== "none" && /* @__PURE__ */
|
|
1152
|
+
info.flow.kind !== "none" && /* @__PURE__ */ jsx9(
|
|
1131
1153
|
PaintSlider,
|
|
1132
1154
|
{
|
|
1133
1155
|
control: { path: "strength", label: "Flow strength", min: 0, max: 1, step: 0.01 },
|
|
@@ -1136,16 +1158,21 @@ var WetPanel = ({ controller, className }) => {
|
|
|
1136
1158
|
}
|
|
1137
1159
|
)
|
|
1138
1160
|
] }),
|
|
1139
|
-
backend.message && /* @__PURE__ */
|
|
1140
|
-
backend.backend && /* @__PURE__ */
|
|
1141
|
-
/* @__PURE__ */
|
|
1161
|
+
backend.message && /* @__PURE__ */ jsx9("p", { className: "text-[10px] text-amber-300", "data-testid": "wet-message", children: backend.message }),
|
|
1162
|
+
backend.backend && /* @__PURE__ */ jsx9("p", { className: "text-[10px] text-slate-500", children: backend.backend.kind === "wasm-threads" ? `WebAssembly, ${backend.backend.threads} threads` : backend.backend.kind === "wasm" ? "WebAssembly, one thread" : "JavaScript" }),
|
|
1163
|
+
/* @__PURE__ */ jsxs8("label", { className: "flex items-center justify-between gap-2", children: [
|
|
1164
|
+
/* @__PURE__ */ jsx9("span", { className: "text-slate-400", children: "Engine" }),
|
|
1165
|
+
/* @__PURE__ */ jsx9("select", { "aria-label": "Wet engine", value: wet.gpu, onChange: (e) => attempt(() => controller.setWetEngine({ gpu: e.target.value })), className: "rounded bg-slate-800 px-1 py-0.5", children: WET_GPU_OPTIONS.map((o) => /* @__PURE__ */ jsx9("option", { value: o.value, children: o.label }, o.value)) })
|
|
1166
|
+
] }),
|
|
1167
|
+
/* @__PURE__ */ jsx9("p", { className: `text-[10px] ${backend.engine.state === "active" ? "text-emerald-300" : "text-slate-500"}`, "data-testid": "wet-engine", children: wetEngineSummary(backend.engine) }),
|
|
1168
|
+
/* @__PURE__ */ jsx9(ErrorLine, { error })
|
|
1142
1169
|
] });
|
|
1143
1170
|
};
|
|
1144
1171
|
|
|
1145
1172
|
// src/paint-editor/StickerPanel.tsx
|
|
1146
1173
|
import { useState as useState8 } from "react";
|
|
1147
1174
|
import { STAMP_KINDS as STAMP_KINDS2, STAMP_LABELS as STAMP_LABELS2, STICKER_SLIDERS, readPath as readPath4 } from "@pixerate/editor/paint-editor";
|
|
1148
|
-
import { jsx as
|
|
1175
|
+
import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1149
1176
|
var StickerPanel = ({ controller, className }) => {
|
|
1150
1177
|
const state = usePaintEditorState(controller);
|
|
1151
1178
|
const [error, attempt] = useAttempt();
|
|
@@ -1160,49 +1187,49 @@ var StickerPanel = ({ controller, className }) => {
|
|
|
1160
1187
|
stickers.length,
|
|
1161
1188
|
")"
|
|
1162
1189
|
] }),
|
|
1163
|
-
/* @__PURE__ */
|
|
1190
|
+
/* @__PURE__ */ jsx10("div", { className: "flex max-h-24 flex-wrap gap-1 overflow-y-auto", role: "listbox", "aria-label": "Stickers", children: stickers.map((s) => /* @__PURE__ */ jsx10("button", { type: "button", role: "option", "aria-selected": s.id === picked, title: `${STAMP_LABELS2[s.stamp]} ${s.id}`, className: `h-5 w-5 rounded ring-1 ${s.id === picked ? "ring-indigo-400" : "ring-slate-700"}`, style: { background: s.color }, onClick: () => setPicked(s.id) }, s.id)) }),
|
|
1164
1191
|
sel && /* @__PURE__ */ jsxs9("div", { className: "flex flex-col gap-1.5", "data-testid": "sticker-edit", children: [
|
|
1165
1192
|
/* @__PURE__ */ jsxs9("label", { className: "flex items-center justify-between gap-2", children: [
|
|
1166
|
-
/* @__PURE__ */
|
|
1167
|
-
/* @__PURE__ */
|
|
1193
|
+
/* @__PURE__ */ jsx10("span", { className: "text-slate-400", children: "Stamp" }),
|
|
1194
|
+
/* @__PURE__ */ jsx10("select", { "aria-label": "Sticker stamp", value: sel.stamp, onChange: (e) => attempt(() => controller.updateSticker(layerId, sel.id, { stamp: e.target.value })), className: "rounded bg-slate-800 px-1 py-0.5", children: STAMP_KINDS2.map((k) => /* @__PURE__ */ jsx10("option", { value: k, children: STAMP_LABELS2[k] }, k)) })
|
|
1168
1195
|
] }),
|
|
1169
|
-
STICKER_SLIDERS.map((c) => /* @__PURE__ */
|
|
1196
|
+
STICKER_SLIDERS.map((c) => /* @__PURE__ */ jsx10(PaintSlider, { control: c, value: readPath4(sel, c.path), onCommit: (v) => attempt(() => controller.updateSticker(layerId, sel.id, { [c.path]: v })) }, c.path)),
|
|
1170
1197
|
/* @__PURE__ */ jsxs9("label", { className: "flex items-center justify-between", children: [
|
|
1171
|
-
/* @__PURE__ */
|
|
1172
|
-
/* @__PURE__ */
|
|
1198
|
+
/* @__PURE__ */ jsx10("span", { className: "text-slate-400", children: "Colour" }),
|
|
1199
|
+
/* @__PURE__ */ jsx10(CommitColorInput, { label: "Sticker colour", value: sel.color, onCommit: (v) => attempt(() => controller.updateSticker(layerId, sel.id, { color: v })), className: "h-6 w-10 rounded bg-transparent" })
|
|
1173
1200
|
] }),
|
|
1174
1201
|
/* @__PURE__ */ jsxs9("div", { className: "flex flex-wrap gap-1", children: [
|
|
1175
|
-
/* @__PURE__ */
|
|
1176
|
-
/* @__PURE__ */
|
|
1177
|
-
/* @__PURE__ */
|
|
1202
|
+
/* @__PURE__ */ jsx10("button", { type: "button", className: button, onClick: () => attempt(() => controller.arrangeStickers(layerId, [sel.id], "front")), children: "To front" }),
|
|
1203
|
+
/* @__PURE__ */ jsx10("button", { type: "button", className: button, onClick: () => attempt(() => controller.arrangeStickers(layerId, [sel.id], "back")), children: "To back" }),
|
|
1204
|
+
/* @__PURE__ */ jsx10("button", { type: "button", className: button, onClick: () => attempt(() => (controller.removeStickers(layerId, [sel.id]), setPicked(null))), children: "Delete" })
|
|
1178
1205
|
] })
|
|
1179
1206
|
] }),
|
|
1180
|
-
/* @__PURE__ */
|
|
1207
|
+
/* @__PURE__ */ jsx10(ErrorLine, { error })
|
|
1181
1208
|
] });
|
|
1182
1209
|
};
|
|
1183
1210
|
|
|
1184
1211
|
// src/paint-editor/PaintEditor.tsx
|
|
1185
1212
|
import { useState as useState9 } from "react";
|
|
1186
|
-
import { jsx as
|
|
1213
|
+
import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1187
1214
|
var PaintEditor = ({ controller, fileName, height = 720, presence, className }) => {
|
|
1188
1215
|
const [tool, setTool] = useState9("brush");
|
|
1189
1216
|
const [tab, setTab] = useState9("color");
|
|
1190
1217
|
const [error, setError] = useState9(null);
|
|
1191
1218
|
return /* @__PURE__ */ jsxs10("div", { className: `flex flex-col gap-2 rounded-xl border border-slate-800 bg-slate-900 p-2 ${className ?? ""}`, style: { height }, "data-testid": "paint-editor", children: [
|
|
1192
|
-
/* @__PURE__ */
|
|
1219
|
+
/* @__PURE__ */ jsx11(PaintToolbar, { controller, tool, onToolChange: setTool, fileName }),
|
|
1193
1220
|
/* @__PURE__ */ jsxs10("div", { className: "flex min-h-0 flex-1 gap-2", children: [
|
|
1194
|
-
/* @__PURE__ */
|
|
1221
|
+
/* @__PURE__ */ jsx11(BrushPanel, { controller, className: "w-60 shrink-0 overflow-y-auto pr-1" }),
|
|
1195
1222
|
/* @__PURE__ */ jsxs10("div", { className: "flex min-w-0 flex-1 flex-col", children: [
|
|
1196
|
-
/* @__PURE__ */
|
|
1197
|
-
error && /* @__PURE__ */
|
|
1223
|
+
/* @__PURE__ */ jsx11(PaintCanvas, { controller, tool, onError: setError, presence, className: "flex-1 rounded-lg" }),
|
|
1224
|
+
error && /* @__PURE__ */ jsx11("p", { role: "alert", className: "mt-1 text-[11px] text-rose-400", children: error })
|
|
1198
1225
|
] }),
|
|
1199
1226
|
/* @__PURE__ */ jsxs10("div", { className: "flex w-64 shrink-0 flex-col gap-2 overflow-y-auto pr-1", children: [
|
|
1200
|
-
/* @__PURE__ */
|
|
1201
|
-
tab === "color" && /* @__PURE__ */
|
|
1202
|
-
tab === "wet" && /* @__PURE__ */
|
|
1203
|
-
tab === "paper" && /* @__PURE__ */
|
|
1204
|
-
tab === "layers" && /* @__PURE__ */
|
|
1205
|
-
tab === "layers" && /* @__PURE__ */
|
|
1227
|
+
/* @__PURE__ */ jsx11("div", { className: "flex gap-1", role: "tablist", "aria-label": "Paint panels", children: ["color", "wet", "paper", "layers"].map((t) => /* @__PURE__ */ jsx11("button", { type: "button", role: "tab", "aria-selected": tab === t, className: chip(tab === t), onClick: () => setTab(t), children: t === "color" ? "Colour" : t === "wet" ? "Wet" : t === "paper" ? "Paper" : "Layers" }, t)) }),
|
|
1228
|
+
tab === "color" && /* @__PURE__ */ jsx11(ColorPanel, { controller }),
|
|
1229
|
+
tab === "wet" && /* @__PURE__ */ jsx11(WetPanel, { controller }),
|
|
1230
|
+
tab === "paper" && /* @__PURE__ */ jsx11(PaperPanel, { controller }),
|
|
1231
|
+
tab === "layers" && /* @__PURE__ */ jsx11(PaintLayersPanel, { controller }),
|
|
1232
|
+
tab === "layers" && /* @__PURE__ */ jsx11(StickerPanel, { controller })
|
|
1206
1233
|
] })
|
|
1207
1234
|
] })
|
|
1208
1235
|
] });
|
|
@@ -1212,6 +1239,7 @@ export {
|
|
|
1212
1239
|
ColorPanel,
|
|
1213
1240
|
PaintCanvas,
|
|
1214
1241
|
PaintEditor,
|
|
1242
|
+
PaintGradientEditor,
|
|
1215
1243
|
PaintLayersPanel,
|
|
1216
1244
|
PaintPresenceLayer,
|
|
1217
1245
|
PaintSlider,
|