@pixerate/editor-react 0.78.1 → 0.80.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.
Files changed (38) hide show
  1. package/dist/animation-editor/index.cjs +679 -37
  2. package/dist/animation-editor/index.cjs.map +1 -1
  3. package/dist/animation-editor/index.d.cts +29 -3
  4. package/dist/animation-editor/index.d.ts +29 -3
  5. package/dist/animation-editor/index.js +128 -37
  6. package/dist/animation-editor/index.js.map +1 -1
  7. package/dist/{chunk-S5SSL3ZO.js → chunk-VKWTKESA.js} +5 -5
  8. package/dist/{chunk-7RMNM3KC.js → chunk-WOQ3H7KA.js} +4 -4
  9. package/dist/chunk-WOQ3H7KA.js.map +1 -0
  10. package/dist/effects/index.cjs +3 -3
  11. package/dist/effects/index.cjs.map +1 -1
  12. package/dist/effects/index.d.cts +4 -2
  13. package/dist/effects/index.d.ts +4 -2
  14. package/dist/effects/index.js +1 -1
  15. package/dist/image-editor/index.cjs +3 -3
  16. package/dist/image-editor/index.cjs.map +1 -1
  17. package/dist/image-editor/index.js +3 -3
  18. package/dist/index.cjs +3 -3
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.js +3 -3
  21. package/dist/paint-editor/index.cjs +734 -19
  22. package/dist/paint-editor/index.cjs.map +1 -1
  23. package/dist/paint-editor/index.d.cts +22 -3
  24. package/dist/paint-editor/index.d.ts +22 -3
  25. package/dist/paint-editor/index.js +173 -19
  26. package/dist/paint-editor/index.js.map +1 -1
  27. package/dist/vector/index.cjs +649 -9
  28. package/dist/vector/index.cjs.map +1 -1
  29. package/dist/vector/index.d.cts +27 -3
  30. package/dist/vector/index.d.ts +27 -3
  31. package/dist/vector/index.js +98 -9
  32. package/dist/vector/index.js.map +1 -1
  33. package/dist/video-editor/index.cjs +3 -3
  34. package/dist/video-editor/index.cjs.map +1 -1
  35. package/dist/video-editor/index.js +1 -1
  36. package/package.json +3 -3
  37. package/dist/chunk-7RMNM3KC.js.map +0 -1
  38. /package/dist/{chunk-S5SSL3ZO.js.map → chunk-VKWTKESA.js.map} +0 -0
@@ -1,6 +1,8 @@
1
- import { PaintEditorController, PaintEditorState, PaintDirectorOptions, PaintPresence, PaintDirector, PaintEditorOptions, PaintTool, PaintView, SliderControl } from '@pixerate/editor/paint-editor';
1
+ import { PaintEditorController, PaintEditorState, PaintDirectorOptions, PaintPresence, PaintDirector, PaintEditorOptions, PaintTool, PaintView, SliderControl, PaintEffectsToolProviderOptions } from '@pixerate/editor/paint-editor';
2
2
  import React__default from 'react';
3
3
  import { P as PaintGradientEditorProps } from '../PaintGradientEditorView-CCPCY5Ib.cjs';
4
+ import { EffectProvider } from '@pixerate/editor/effects';
5
+ import { ToolRegistry } from '@pixerate/editor/tools';
4
6
 
5
7
  interface UsePaintEditorReturn {
6
8
  controller: PaintEditorController;
@@ -171,7 +173,8 @@ interface PaintEditorProps {
171
173
  }
172
174
  /**
173
175
  * The paint editor: toolbar, brush library on the left, the canvas, and colour (wheel,
174
- * harmonies, swatches, mixing pad), wet media, paper and layers on the right.
176
+ * harmonies, swatches, mixing pad), wet media, paper and layers on the right, and provider effects
177
+ * when an effects provider is registered.
175
178
  */
176
179
  declare const PaintEditor: React__default.FC<PaintEditorProps>;
177
180
 
@@ -189,4 +192,20 @@ declare const PaintSlider: React__default.FC<{
189
192
  /** The gradient paint editor (see `PaintGradientEditorView`). */
190
193
  declare const PaintGradientEditor: React__default.FC<PaintGradientEditorProps>;
191
194
 
192
- export { BrushPanel, type BrushPanelProps, ColorPanel, type ColorPanelProps, PaintCanvas, type PaintCanvasProps, PaintEditor, type PaintEditorProps, PaintGradientEditor, PaintGradientEditorProps, PaintLayersPanel, type PaintLayersPanelProps, PaintPresenceLayer, PaintSlider, PaintToolbar, type PaintToolbarProps, PaperPanel, type PaperPanelProps, StickerPanel, type StickerPanelProps, type UsePaintEditorReturn, WetPanel, type WetPanelProps, usePaintDirector, usePaintEditor, usePaintEditorState };
195
+ interface PaintEffectsPanelProps {
196
+ controller: PaintEditorController;
197
+ className?: string;
198
+ }
199
+ /** The controller's effects provider, following `setEffectsProvider`. */
200
+ declare function usePaintEffectsProvider(controller: PaintEditorController): EffectProvider | null;
201
+ /** Registers the paint effect tools (`set_paper_effect`, `add_effect_layer`, `apply_effect`, …) while mounted. */
202
+ declare function usePaintEffectsTools(registry: ToolRegistry | null | undefined, controller: PaintEditorController, options?: PaintEffectsToolProviderOptions): void;
203
+ /**
204
+ * Provider effects in the paint editor: effect paper (a generator over the sheet, multiplied so
205
+ * the grain shows), effect layers, effect fills and light baked onto the active layer's dried
206
+ * paint; plus the controls of the effect paper and of an active effect layer. Each change is one
207
+ * undo step (a slider drag is one). Without a provider it says why nothing can be added.
208
+ */
209
+ declare const PaintEffectsPanel: React__default.FC<PaintEffectsPanelProps>;
210
+
211
+ export { BrushPanel, type BrushPanelProps, ColorPanel, type ColorPanelProps, PaintCanvas, type PaintCanvasProps, PaintEditor, type PaintEditorProps, PaintEffectsPanel, type PaintEffectsPanelProps, PaintGradientEditor, PaintGradientEditorProps, PaintLayersPanel, type PaintLayersPanelProps, PaintPresenceLayer, PaintSlider, PaintToolbar, type PaintToolbarProps, PaperPanel, type PaperPanelProps, StickerPanel, type StickerPanelProps, type UsePaintEditorReturn, WetPanel, type WetPanelProps, usePaintDirector, usePaintEditor, usePaintEditorState, usePaintEffectsProvider, usePaintEffectsTools };
@@ -1,6 +1,8 @@
1
- import { PaintEditorController, PaintEditorState, PaintDirectorOptions, PaintPresence, PaintDirector, PaintEditorOptions, PaintTool, PaintView, SliderControl } from '@pixerate/editor/paint-editor';
1
+ import { PaintEditorController, PaintEditorState, PaintDirectorOptions, PaintPresence, PaintDirector, PaintEditorOptions, PaintTool, PaintView, SliderControl, PaintEffectsToolProviderOptions } from '@pixerate/editor/paint-editor';
2
2
  import React__default from 'react';
3
3
  import { P as PaintGradientEditorProps } from '../PaintGradientEditorView-CCPCY5Ib.js';
4
+ import { EffectProvider } from '@pixerate/editor/effects';
5
+ import { ToolRegistry } from '@pixerate/editor/tools';
4
6
 
5
7
  interface UsePaintEditorReturn {
6
8
  controller: PaintEditorController;
@@ -171,7 +173,8 @@ interface PaintEditorProps {
171
173
  }
172
174
  /**
173
175
  * The paint editor: toolbar, brush library on the left, the canvas, and colour (wheel,
174
- * harmonies, swatches, mixing pad), wet media, paper and layers on the right.
176
+ * harmonies, swatches, mixing pad), wet media, paper and layers on the right, and provider effects
177
+ * when an effects provider is registered.
175
178
  */
176
179
  declare const PaintEditor: React__default.FC<PaintEditorProps>;
177
180
 
@@ -189,4 +192,20 @@ declare const PaintSlider: React__default.FC<{
189
192
  /** The gradient paint editor (see `PaintGradientEditorView`). */
190
193
  declare const PaintGradientEditor: React__default.FC<PaintGradientEditorProps>;
191
194
 
192
- export { BrushPanel, type BrushPanelProps, ColorPanel, type ColorPanelProps, PaintCanvas, type PaintCanvasProps, PaintEditor, type PaintEditorProps, PaintGradientEditor, PaintGradientEditorProps, PaintLayersPanel, type PaintLayersPanelProps, PaintPresenceLayer, PaintSlider, PaintToolbar, type PaintToolbarProps, PaperPanel, type PaperPanelProps, StickerPanel, type StickerPanelProps, type UsePaintEditorReturn, WetPanel, type WetPanelProps, usePaintDirector, usePaintEditor, usePaintEditorState };
195
+ interface PaintEffectsPanelProps {
196
+ controller: PaintEditorController;
197
+ className?: string;
198
+ }
199
+ /** The controller's effects provider, following `setEffectsProvider`. */
200
+ declare function usePaintEffectsProvider(controller: PaintEditorController): EffectProvider | null;
201
+ /** Registers the paint effect tools (`set_paper_effect`, `add_effect_layer`, `apply_effect`, …) while mounted. */
202
+ declare function usePaintEffectsTools(registry: ToolRegistry | null | undefined, controller: PaintEditorController, options?: PaintEffectsToolProviderOptions): void;
203
+ /**
204
+ * Provider effects in the paint editor: effect paper (a generator over the sheet, multiplied so
205
+ * the grain shows), effect layers, effect fills and light baked onto the active layer's dried
206
+ * paint; plus the controls of the effect paper and of an active effect layer. Each change is one
207
+ * undo step (a slider drag is one). Without a provider it says why nothing can be added.
208
+ */
209
+ declare const PaintEffectsPanel: React__default.FC<PaintEffectsPanelProps>;
210
+
211
+ export { BrushPanel, type BrushPanelProps, ColorPanel, type ColorPanelProps, PaintCanvas, type PaintCanvasProps, PaintEditor, type PaintEditorProps, PaintEffectsPanel, type PaintEffectsPanelProps, PaintGradientEditor, PaintGradientEditorProps, PaintLayersPanel, type PaintLayersPanelProps, PaintPresenceLayer, PaintSlider, PaintToolbar, type PaintToolbarProps, PaperPanel, type PaperPanelProps, StickerPanel, type StickerPanelProps, type UsePaintEditorReturn, WetPanel, type WetPanelProps, usePaintDirector, usePaintEditor, usePaintEditorState, usePaintEffectsProvider, usePaintEffectsTools };
@@ -1,6 +1,12 @@
1
1
  import {
2
2
  PaintGradientEditorView
3
3
  } from "../chunk-MO354USR.js";
4
+ import {
5
+ EffectParameters,
6
+ EffectsBrowser,
7
+ useEffectDescription
8
+ } from "../chunk-WOQ3H7KA.js";
9
+ import "../chunk-ZQZAKUUM.js";
4
10
 
5
11
  // src/paint-editor/usePaintEditor.ts
6
12
  import { useMemo, useState, useSyncExternalStore } from "react";
@@ -1209,27 +1215,172 @@ var StickerPanel = ({ controller, className }) => {
1209
1215
  };
1210
1216
 
1211
1217
  // src/paint-editor/PaintEditor.tsx
1212
- import { useState as useState9 } from "react";
1218
+ import { useState as useState10 } from "react";
1219
+
1220
+ // src/paint-editor/PaintEffectsPanel.tsx
1221
+ import { useEffect as useEffect3, useRef as useRef6, useState as useState9 } from "react";
1222
+ import { createPaintEffectsToolProvider, PAPER_EFFECT_BLENDS } from "@pixerate/editor/paint-editor";
1213
1223
  import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
1224
+ function usePaintEffectsProvider(controller) {
1225
+ const [provider, setProvider] = useState9(controller.effectsProvider);
1226
+ useEffect3(() => {
1227
+ setProvider(controller.effectsProvider);
1228
+ return controller.effects.subscribe(setProvider);
1229
+ }, [controller]);
1230
+ return provider;
1231
+ }
1232
+ function usePaintEffectsTools(registry, controller, options = {}) {
1233
+ const { id, namespace, readOnly } = options;
1234
+ useEffect3(() => {
1235
+ if (!registry) return;
1236
+ return registry.register(createPaintEffectsToolProvider(controller, { id, namespace, readOnly }));
1237
+ }, [registry, controller, id, namespace, readOnly]);
1238
+ }
1239
+ function Controls({ provider, effect, name, values, apply }) {
1240
+ const { description, error } = useEffectDescription(provider, effect);
1241
+ const drags = useRef6(/* @__PURE__ */ new Set());
1242
+ if (!provider) return /* @__PURE__ */ jsxs10("p", { className: "text-[11px] text-slate-400", children: [
1243
+ 'No effects provider: "',
1244
+ name ?? effect,
1245
+ '" is kept as it was drawn.'
1246
+ ] });
1247
+ if (error) return /* @__PURE__ */ jsx11("p", { className: "text-[11px] text-amber-400", children: error });
1248
+ if (!description) return /* @__PURE__ */ jsx11("p", { className: "text-[11px] text-slate-400", children: "Loading\u2026" });
1249
+ const commit = (gesture) => {
1250
+ if (!gesture || drags.current.has(gesture)) return !gesture;
1251
+ drags.current.add(gesture);
1252
+ return true;
1253
+ };
1254
+ return /* @__PURE__ */ jsx11(
1255
+ EffectParameters,
1256
+ {
1257
+ description,
1258
+ values,
1259
+ onChange: (k, v, gesture) => apply({ [k]: v }, commit(gesture)),
1260
+ onReset: (k) => apply({ [k]: null }, true),
1261
+ onPreset: (v) => apply({ ...v }, true)
1262
+ }
1263
+ );
1264
+ }
1265
+ var BROWSERS = {
1266
+ paper: { uses: ["generator"], title: "Effect paper" },
1267
+ layer: { uses: ["generator", "overlay", "title"], title: "Effect layer" },
1268
+ fill: { uses: ["generator", "overlay", "title"], title: "Fill the layer" },
1269
+ light: { uses: ["filter"], title: "Light on the paint" }
1270
+ };
1271
+ var PaintEffectsPanel = ({ controller, className }) => {
1272
+ const state = usePaintEditorState(controller);
1273
+ const provider = usePaintEffectsProvider(controller);
1274
+ const [error, attempt] = useAttempt();
1275
+ const [browse, setBrowse] = useState9(null);
1276
+ const [busy, setBusy] = useState9(false);
1277
+ const paper = controller.getPaperEffect();
1278
+ const activeId = state.document.activeLayerId;
1279
+ const layerEffect = activeId ? controller.getEffectLayer(activeId) : void 0;
1280
+ const active = activeId ? controller.getLayer(activeId) : void 0;
1281
+ const run = (work) => attempt(() => {
1282
+ setBusy(true);
1283
+ return work().finally(() => setBusy(false));
1284
+ });
1285
+ const pick = (id) => {
1286
+ const kind = browse;
1287
+ setBrowse(null);
1288
+ if (kind === "paper") run(() => controller.setPaperEffect(id));
1289
+ else if (kind === "layer") run(() => controller.addEffectLayer(id));
1290
+ else if (kind === "fill") run(() => controller.fillLayerWithEffect(id));
1291
+ else if (kind === "light" && activeId) run(() => controller.applyLightEffect(activeId, id));
1292
+ };
1293
+ const toggle = (b) => setBrowse(browse === b ? null : b);
1294
+ const pixel = active?.type === "pixel";
1295
+ return /* @__PURE__ */ jsxs10("div", { className: `${panel} ${className ?? ""}`, "data-testid": "paint-effects-panel", "aria-busy": busy, children: [
1296
+ /* @__PURE__ */ jsxs10("div", { className: "flex flex-wrap items-center gap-1", children: [
1297
+ /* @__PURE__ */ jsx11("span", { className: "flex-1 font-medium text-slate-300", children: "Effects" }),
1298
+ /* @__PURE__ */ jsx11("button", { type: "button", className: button, disabled: !provider, "aria-pressed": browse === "paper", onClick: () => toggle("paper"), children: "Paper\u2026" }),
1299
+ /* @__PURE__ */ jsx11("button", { type: "button", className: button, disabled: !provider, "aria-pressed": browse === "layer", onClick: () => toggle("layer"), children: "Layer\u2026" }),
1300
+ /* @__PURE__ */ jsx11("button", { type: "button", className: button, disabled: !provider || !pixel, "aria-pressed": browse === "fill", onClick: () => toggle("fill"), children: "Fill\u2026" }),
1301
+ /* @__PURE__ */ jsx11("button", { type: "button", className: button, disabled: !provider || !pixel, "aria-pressed": browse === "light", onClick: () => toggle("light"), title: "Bake a light, glow or grade into the active layer's dried paint", children: "Light\u2026" })
1302
+ ] }),
1303
+ !provider && /* @__PURE__ */ jsx11("p", { className: "text-[11px] text-slate-400", children: "No effects provider is registered. Effects in the painting are kept." }),
1304
+ browse && /* @__PURE__ */ jsx11(EffectsBrowser, { provider, uses: BROWSERS[browse].uses, title: BROWSERS[browse].title, onPick: (e) => pick(e.id) }),
1305
+ paper && /* @__PURE__ */ jsxs10("div", { className: "flex flex-col gap-1 rounded border border-slate-700 p-1", "data-effect-part": "paper", children: [
1306
+ /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-1", children: [
1307
+ /* @__PURE__ */ jsxs10("span", { className: "flex-1 truncate", children: [
1308
+ "Paper: ",
1309
+ paper.name ?? paper.effect
1310
+ ] }),
1311
+ /* @__PURE__ */ jsx11("button", { type: "button", className: button, "aria-label": "Remove the effect paper", onClick: () => run(() => controller.setPaperEffect(null)), children: "\u2715" })
1312
+ ] }),
1313
+ /* @__PURE__ */ jsxs10("label", { className: "flex items-center gap-1", children: [
1314
+ /* @__PURE__ */ jsx11("span", { className: "w-14 text-[11px] text-slate-400", children: "Opacity" }),
1315
+ /* @__PURE__ */ jsx11(
1316
+ "input",
1317
+ {
1318
+ type: "range",
1319
+ min: 0,
1320
+ max: 1,
1321
+ step: 0.01,
1322
+ className: "flex-1",
1323
+ "aria-label": "Effect paper opacity",
1324
+ defaultValue: paper.opacity,
1325
+ onPointerUp: (e) => {
1326
+ const opacity = Number(e.currentTarget.value);
1327
+ run(() => controller.updatePaperEffect({ opacity }));
1328
+ },
1329
+ onKeyUp: (e) => {
1330
+ const opacity = Number(e.currentTarget.value);
1331
+ run(() => controller.updatePaperEffect({ opacity }));
1332
+ }
1333
+ },
1334
+ `o${paper.opacity}`
1335
+ )
1336
+ ] }),
1337
+ /* @__PURE__ */ jsxs10("label", { className: "flex items-center gap-1", children: [
1338
+ /* @__PURE__ */ jsx11("span", { className: "w-14 text-[11px] text-slate-400", children: "Blend" }),
1339
+ /* @__PURE__ */ jsx11("select", { className: "flex-1 rounded bg-slate-800 px-1 py-0.5 text-[11px]", "aria-label": "Effect paper blend", value: paper.blend, onChange: (e) => {
1340
+ const blend = e.currentTarget.value;
1341
+ run(() => controller.updatePaperEffect({ blend }));
1342
+ }, children: PAPER_EFFECT_BLENDS.map((b) => /* @__PURE__ */ jsx11("option", { value: b, children: b }, b)) })
1343
+ ] }),
1344
+ /* @__PURE__ */ jsx11(Controls, { provider, effect: paper.effect, name: paper.name, values: paper.values, apply: (values, commit) => run(() => controller.updatePaperEffect({ values }, commit)) })
1345
+ ] }),
1346
+ layerEffect && activeId && /* @__PURE__ */ jsxs10("div", { className: "flex flex-col gap-1 rounded border border-slate-700 p-1", "data-effect-part": "layer", children: [
1347
+ /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-1", children: [
1348
+ /* @__PURE__ */ jsxs10("span", { className: "flex-1 truncate", children: [
1349
+ "Layer: ",
1350
+ layerEffect.name ?? layerEffect.effect
1351
+ ] }),
1352
+ /* @__PURE__ */ jsx11("button", { type: "button", className: button, title: "Keep the pixels as a plain layer", onClick: () => attempt(() => controller.rasterizeEffectLayer(activeId)), children: "Rasterize" })
1353
+ ] }),
1354
+ /* @__PURE__ */ jsx11(Controls, { provider, effect: layerEffect.effect, name: layerEffect.name, values: layerEffect.values, apply: (values, commit) => run(() => controller.updateEffectLayer(activeId, { values }, commit)) })
1355
+ ] }),
1356
+ /* @__PURE__ */ jsx11(ErrorLine, { error })
1357
+ ] });
1358
+ };
1359
+
1360
+ // src/paint-editor/PaintEditor.tsx
1361
+ import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
1214
1362
  var PaintEditor = ({ controller, fileName, height = 720, presence, className }) => {
1215
- const [tool, setTool] = useState9("brush");
1216
- const [tab, setTab] = useState9("color");
1217
- const [error, setError] = useState9(null);
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: [
1219
- /* @__PURE__ */ jsx11(PaintToolbar, { controller, tool, onToolChange: setTool, fileName }),
1220
- /* @__PURE__ */ jsxs10("div", { className: "flex min-h-0 flex-1 gap-2", children: [
1221
- /* @__PURE__ */ jsx11(BrushPanel, { controller, className: "w-60 shrink-0 overflow-y-auto pr-1" }),
1222
- /* @__PURE__ */ jsxs10("div", { className: "flex min-w-0 flex-1 flex-col", children: [
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 })
1363
+ const [tool, setTool] = useState10("brush");
1364
+ const [tab, setTab] = useState10("color");
1365
+ const [error, setError] = useState10(null);
1366
+ const effects = usePaintEffectsProvider(controller);
1367
+ const tabs = effects ? ["color", "wet", "paper", "layers", "effects"] : ["color", "wet", "paper", "layers"];
1368
+ return /* @__PURE__ */ jsxs11("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: [
1369
+ /* @__PURE__ */ jsx12(PaintToolbar, { controller, tool, onToolChange: setTool, fileName }),
1370
+ /* @__PURE__ */ jsxs11("div", { className: "flex min-h-0 flex-1 gap-2", children: [
1371
+ /* @__PURE__ */ jsx12(BrushPanel, { controller, className: "w-60 shrink-0 overflow-y-auto pr-1" }),
1372
+ /* @__PURE__ */ jsxs11("div", { className: "flex min-w-0 flex-1 flex-col", children: [
1373
+ /* @__PURE__ */ jsx12(PaintCanvas, { controller, tool, onError: setError, presence, className: "flex-1 rounded-lg" }),
1374
+ error && /* @__PURE__ */ jsx12("p", { role: "alert", className: "mt-1 text-[11px] text-rose-400", children: error })
1225
1375
  ] }),
1226
- /* @__PURE__ */ jsxs10("div", { className: "flex w-64 shrink-0 flex-col gap-2 overflow-y-auto pr-1", children: [
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 })
1376
+ /* @__PURE__ */ jsxs11("div", { className: "flex w-64 shrink-0 flex-col gap-2 overflow-y-auto pr-1", children: [
1377
+ /* @__PURE__ */ jsx12("div", { className: "flex gap-1", role: "tablist", "aria-label": "Paint panels", children: tabs.map((t) => /* @__PURE__ */ jsx12("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" : t === "layers" ? "Layers" : "Effects" }, t)) }),
1378
+ tab === "color" && /* @__PURE__ */ jsx12(ColorPanel, { controller }),
1379
+ tab === "wet" && /* @__PURE__ */ jsx12(WetPanel, { controller }),
1380
+ tab === "paper" && /* @__PURE__ */ jsx12(PaperPanel, { controller }),
1381
+ tab === "layers" && /* @__PURE__ */ jsx12(PaintLayersPanel, { controller }),
1382
+ tab === "layers" && /* @__PURE__ */ jsx12(StickerPanel, { controller }),
1383
+ tab === "effects" && effects && /* @__PURE__ */ jsx12(PaintEffectsPanel, { controller })
1233
1384
  ] })
1234
1385
  ] })
1235
1386
  ] });
@@ -1239,6 +1390,7 @@ export {
1239
1390
  ColorPanel,
1240
1391
  PaintCanvas,
1241
1392
  PaintEditor,
1393
+ PaintEffectsPanel,
1242
1394
  PaintGradientEditor,
1243
1395
  PaintLayersPanel,
1244
1396
  PaintPresenceLayer,
@@ -1249,6 +1401,8 @@ export {
1249
1401
  WetPanel,
1250
1402
  usePaintDirector,
1251
1403
  usePaintEditor,
1252
- usePaintEditorState
1404
+ usePaintEditorState,
1405
+ usePaintEffectsProvider,
1406
+ usePaintEffectsTools
1253
1407
  };
1254
1408
  //# sourceMappingURL=index.js.map