@pixerate/editor-react 0.74.0 → 0.75.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 +216 -19
- package/dist/paint-editor/index.cjs.map +1 -1
- package/dist/paint-editor/index.d.cts +31 -6
- package/dist/paint-editor/index.d.ts +31 -6
- package/dist/paint-editor/index.js +244 -34
- package/dist/paint-editor/index.js.map +1 -1
- package/dist/vector/index.cjs +298 -174
- package/dist/vector/index.cjs.map +1 -1
- package/dist/vector/index.d.cts +22 -1
- package/dist/vector/index.d.ts +22 -1
- package/dist/vector/index.js +282 -160
- package/dist/vector/index.js.map +1 -1
- package/package.json +3 -3
package/dist/vector/index.js
CHANGED
|
@@ -645,6 +645,10 @@ function VectorCanvas({
|
|
|
645
645
|
/* @__PURE__ */ jsx2("path", { d: "M-3 0H3M0 -3V3", stroke: "#dc2626" })
|
|
646
646
|
] })
|
|
647
647
|
] }),
|
|
648
|
+
overlay.shading.map((sh) => /* @__PURE__ */ jsxs2("g", { "data-vector-shading": sh.kind, children: [
|
|
649
|
+
sh.lines.map((d, i) => /* @__PURE__ */ jsx2("path", { d, stroke: accentColor, strokeWidth: 1, opacity: 0.7 }, i)),
|
|
650
|
+
sh.points.map((p) => /* @__PURE__ */ jsx2("circle", { "data-vector-shading-point": p.index, "data-selected": p.selected || void 0, cx: p.x, cy: p.y, r: p.selected ? 6 : 4.5, fill: p.color, stroke: p.selected ? accentColor : "#ffffff", strokeWidth: p.selected ? 2 : 1.5 }, p.index))
|
|
651
|
+
] }, `shading:${sh.nodeId}`)),
|
|
648
652
|
overlay.trackingPoints.map((t, i) => /* @__PURE__ */ jsx2("path", { "data-vector-tracking-point": "", d: `M${t.x - 5} ${t.y}H${t.x + 5}M${t.x} ${t.y - 5}V${t.y + 5}`, stroke: guideColor, strokeWidth: 1.5 }, `track:${i}`)),
|
|
649
653
|
overlay.typedEntry && /* @__PURE__ */ jsxs2("g", { "data-vector-typed-entry": "", transform: `translate(${overlay.typedEntry.x + 14} ${overlay.typedEntry.y + 18})`, children: [
|
|
650
654
|
/* @__PURE__ */ jsx2("rect", { x: -3, y: -12, width: overlay.typedEntry.text.length * 7 + 8, height: 17, rx: 3, fill: THEME2.handleFill, stroke: accentColor }),
|
|
@@ -780,7 +784,7 @@ function VectorCanvas({
|
|
|
780
784
|
}
|
|
781
785
|
|
|
782
786
|
// src/vector/VectorToolbar.tsx
|
|
783
|
-
import
|
|
787
|
+
import React9 from "react";
|
|
784
788
|
import {
|
|
785
789
|
VECTOR_TOOLS,
|
|
786
790
|
VECTOR_TOOL_GLYPHS,
|
|
@@ -803,7 +807,7 @@ import {
|
|
|
803
807
|
isVectorMaskGroup,
|
|
804
808
|
widthProfilePreset,
|
|
805
809
|
widthProfilePresetId,
|
|
806
|
-
vectorPaintToHex as
|
|
810
|
+
vectorPaintToHex as vectorPaintToHex5,
|
|
807
811
|
vectorToolLabel,
|
|
808
812
|
vectorPolarAngles,
|
|
809
813
|
vectorPolarIncrementOf
|
|
@@ -1925,6 +1929,120 @@ function VectorConnectorPanel({ controller, className }) {
|
|
|
1925
1929
|
] });
|
|
1926
1930
|
}
|
|
1927
1931
|
|
|
1932
|
+
// src/vector/VectorShadingPanel.tsx
|
|
1933
|
+
import { useState as useState4 } from "react";
|
|
1934
|
+
import { isVectorFreeformGradient, isVectorMeshGradient, vectorPaintToHex as vectorPaintToHex3 } from "@pixerate/editor/vector";
|
|
1935
|
+
import { Fragment as Fragment3, jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1936
|
+
var buttonClass10 = "inline-flex h-7 min-w-7 items-center justify-center rounded-md border border-border bg-background px-1.5 text-xs font-medium text-foreground hover:bg-muted disabled:cursor-not-allowed disabled:opacity-40";
|
|
1937
|
+
var numberClass6 = "h-7 w-12 rounded border border-border bg-background px-1.5 text-xs";
|
|
1938
|
+
function VectorShadingPanel({ controller, className }) {
|
|
1939
|
+
const state = useVectorEditorState(controller);
|
|
1940
|
+
const [rows, setRows] = useState4(2);
|
|
1941
|
+
const [columns, setColumns] = useState4(2);
|
|
1942
|
+
const path = state.selection.map((id) => controller.getNode(id)).find((n) => n?.type === "path");
|
|
1943
|
+
if (!path) return null;
|
|
1944
|
+
const fill = path.fill;
|
|
1945
|
+
const sel = state.shadingPoint?.nodeId === path.id ? state.shadingPoint : null;
|
|
1946
|
+
const mesh = isVectorMeshGradient(fill) ? fill : null;
|
|
1947
|
+
const free = isVectorFreeformGradient(fill) ? fill : null;
|
|
1948
|
+
const color = sel ? mesh ? mesh.colors[sel.index] : free?.points[sel.index]?.color : void 0;
|
|
1949
|
+
const opacity = sel ? mesh ? mesh.opacities?.[sel.index] ?? 1 : free?.points[sel.index]?.opacity ?? 1 : 1;
|
|
1950
|
+
const spread = sel && free ? free.points[sel.index]?.spread ?? 0 : 0;
|
|
1951
|
+
const edit = (patch, merge) => {
|
|
1952
|
+
if (!sel) return;
|
|
1953
|
+
if (mesh) controller.setMeshPoint(path.id, sel.index, patch, merge ? { merge } : {});
|
|
1954
|
+
else controller.setFreeformPoint(path.id, sel.index, patch, merge ? { merge } : {});
|
|
1955
|
+
};
|
|
1956
|
+
return /* @__PURE__ */ jsxs12("div", { className: `space-y-2 ${className ?? ""}`, "data-vector-shading-panel": path.id, "aria-label": "Mesh and freeform gradient", children: [
|
|
1957
|
+
/* @__PURE__ */ jsxs12("div", { className: "flex flex-wrap items-center gap-1", children: [
|
|
1958
|
+
/* @__PURE__ */ jsx12("span", { className: "w-16 text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: "Shading" }),
|
|
1959
|
+
/* @__PURE__ */ jsx12("input", { type: "number", min: 1, max: 32, "aria-label": "Mesh rows", title: "Mesh rows", className: numberClass6, value: rows, onChange: (e) => setRows(Math.max(1, Math.min(32, parseInt(e.target.value, 10) || 1))) }),
|
|
1960
|
+
/* @__PURE__ */ jsx12("span", { className: "text-xs text-muted-foreground", children: "\xD7" }),
|
|
1961
|
+
/* @__PURE__ */ jsx12("input", { type: "number", min: 1, max: 32, "aria-label": "Mesh columns", title: "Mesh columns", className: numberClass6, value: columns, onChange: (e) => setColumns(Math.max(1, Math.min(32, parseInt(e.target.value, 10) || 1))) }),
|
|
1962
|
+
/* @__PURE__ */ jsx12("button", { type: "button", className: buttonClass10, "data-vector-shading-action": "mesh", title: "Create Gradient Mesh: smooth patches whose points blend colours (edit them with the Gradient tool)", onClick: () => controller.setMeshGradient({ rows, columns }, { ids: [path.id] }), children: "Mesh" }),
|
|
1963
|
+
/* @__PURE__ */ jsx12("button", { type: "button", className: buttonClass10, "data-vector-shading-action": "freeform", title: "Freeform Gradient: colour points blended across the shape (click inside with the Gradient tool to add one)", onClick: () => controller.setFreeformGradient({}, { ids: [path.id] }), children: "Freeform" })
|
|
1964
|
+
] }),
|
|
1965
|
+
(mesh || free) && !sel && /* @__PURE__ */ jsxs12("p", { className: "text-[11px] text-muted-foreground", children: [
|
|
1966
|
+
"Gradient tool (G): drag points; ",
|
|
1967
|
+
free ? "click inside to add one, Alt-click to remove." : "pick one to colour it."
|
|
1968
|
+
] }),
|
|
1969
|
+
sel && /* @__PURE__ */ jsxs12("div", { className: "flex flex-wrap items-center gap-2", "data-vector-shading-point": sel.index, children: [
|
|
1970
|
+
/* @__PURE__ */ jsxs12("span", { className: "w-16 text-xs text-foreground", children: [
|
|
1971
|
+
"Point ",
|
|
1972
|
+
sel.index
|
|
1973
|
+
] }),
|
|
1974
|
+
/* @__PURE__ */ jsx12("input", { type: "color", "aria-label": "Point colour", className: "h-7 w-10 cursor-pointer rounded border border-border bg-background", value: vectorPaintToHex3(color ?? "#000000") ?? "#000000", onChange: (e) => edit({ color: e.target.value }, "shading-color") }),
|
|
1975
|
+
/* @__PURE__ */ jsx12("input", { type: "range", min: 0, max: 1, step: 0.05, "aria-label": "Point opacity", title: "Opacity", className: "h-4 w-20 accent-primary", value: opacity, onChange: (e) => edit({ opacity: parseFloat(e.target.value) }, "shading-opacity") }),
|
|
1976
|
+
free && /* @__PURE__ */ jsxs12(Fragment3, { children: [
|
|
1977
|
+
/* @__PURE__ */ jsx12("input", { type: "range", min: 0, max: 1, step: 0.05, "aria-label": "Point spread", title: "Spread: pure colour round the point", className: "h-4 w-20 accent-primary", value: spread, onChange: (e) => edit({ spread: parseFloat(e.target.value) }, "shading-spread") }),
|
|
1978
|
+
/* @__PURE__ */ jsx12("button", { type: "button", className: buttonClass10, "aria-label": "Remove point", onClick: () => controller.removeFreeformPoint(path.id, sel.index), children: "Remove" })
|
|
1979
|
+
] })
|
|
1980
|
+
] })
|
|
1981
|
+
] });
|
|
1982
|
+
}
|
|
1983
|
+
|
|
1984
|
+
// src/vector/VectorTracePanel.tsx
|
|
1985
|
+
import { useRef as useRef4, useState as useState5 } from "react";
|
|
1986
|
+
import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1987
|
+
var buttonClass11 = "inline-flex h-7 min-w-7 items-center justify-center rounded-md border border-border bg-background px-1.5 text-xs font-medium text-foreground hover:bg-muted disabled:cursor-not-allowed disabled:opacity-40";
|
|
1988
|
+
var selectClass8 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
|
|
1989
|
+
var numberClass7 = "h-7 w-12 rounded border border-border bg-background px-1.5 text-xs";
|
|
1990
|
+
async function filePixels(file, max = 1e6) {
|
|
1991
|
+
const bitmap = await createImageBitmap(file);
|
|
1992
|
+
const k = Math.min(1, Math.sqrt(max / (bitmap.width * bitmap.height)));
|
|
1993
|
+
const width = Math.max(1, Math.round(bitmap.width * k));
|
|
1994
|
+
const height = Math.max(1, Math.round(bitmap.height * k));
|
|
1995
|
+
const canvas = document.createElement("canvas");
|
|
1996
|
+
canvas.width = width;
|
|
1997
|
+
canvas.height = height;
|
|
1998
|
+
const ctx = canvas.getContext("2d");
|
|
1999
|
+
ctx.drawImage(bitmap, 0, 0, width, height);
|
|
2000
|
+
return { width, height, data: ctx.getImageData(0, 0, width, height).data };
|
|
2001
|
+
}
|
|
2002
|
+
function VectorTracePanel({ controller, className }) {
|
|
2003
|
+
const input = useRef4(null);
|
|
2004
|
+
const [mode, setMode] = useState5("threshold");
|
|
2005
|
+
const [colors, setColors] = useState5(6);
|
|
2006
|
+
const [message, setMessage] = useState5(null);
|
|
2007
|
+
const trace = async (file) => {
|
|
2008
|
+
try {
|
|
2009
|
+
const pixels = await filePixels(file);
|
|
2010
|
+
const doc = controller.getState().document;
|
|
2011
|
+
const width = Math.min(doc.width, pixels.width);
|
|
2012
|
+
const r = controller.traceImage(pixels, { mode, colors, width, name: file.name.replace(/\.[^.]+$/, "") || "Image Trace" });
|
|
2013
|
+
setMessage(r.ok ? null : r.message);
|
|
2014
|
+
} catch {
|
|
2015
|
+
setMessage("That file could not be read as an image.");
|
|
2016
|
+
}
|
|
2017
|
+
};
|
|
2018
|
+
return /* @__PURE__ */ jsxs13("div", { className: `space-y-1 ${className ?? ""}`, "data-vector-trace-panel": "", "aria-label": "Image trace", children: [
|
|
2019
|
+
/* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-1", children: [
|
|
2020
|
+
/* @__PURE__ */ jsx13("span", { className: "w-16 text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: "Trace" }),
|
|
2021
|
+
/* @__PURE__ */ jsxs13("select", { "aria-label": "Trace mode", className: selectClass8, value: mode, onChange: (e) => setMode(e.target.value), children: [
|
|
2022
|
+
/* @__PURE__ */ jsx13("option", { value: "threshold", children: "Black & white" }),
|
|
2023
|
+
/* @__PURE__ */ jsx13("option", { value: "color", children: "Colours" })
|
|
2024
|
+
] }),
|
|
2025
|
+
mode === "color" && /* @__PURE__ */ jsx13("input", { type: "number", min: 2, max: 32, "aria-label": "Trace colours", title: "Colours", className: numberClass7, value: colors, onChange: (e) => setColors(Math.max(2, Math.min(32, parseInt(e.target.value, 10) || 2))) }),
|
|
2026
|
+
/* @__PURE__ */ jsx13("button", { type: "button", className: buttonClass11, "data-vector-trace-action": "", title: "Image Trace: pick an image to turn into paths", onClick: () => input.current?.click(), children: "Image\u2026" }),
|
|
2027
|
+
/* @__PURE__ */ jsx13(
|
|
2028
|
+
"input",
|
|
2029
|
+
{
|
|
2030
|
+
ref: input,
|
|
2031
|
+
type: "file",
|
|
2032
|
+
accept: "image/*",
|
|
2033
|
+
hidden: true,
|
|
2034
|
+
onChange: (e) => {
|
|
2035
|
+
const file = e.target.files?.[0];
|
|
2036
|
+
e.target.value = "";
|
|
2037
|
+
if (file) void trace(file);
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2040
|
+
)
|
|
2041
|
+
] }),
|
|
2042
|
+
message && /* @__PURE__ */ jsx13("p", { className: "text-[11px] text-red-600", children: message })
|
|
2043
|
+
] });
|
|
2044
|
+
}
|
|
2045
|
+
|
|
1928
2046
|
// src/vector/VectorGradientEditor.tsx
|
|
1929
2047
|
import {
|
|
1930
2048
|
VECTOR_GRADIENT_TYPES,
|
|
@@ -1936,12 +2054,12 @@ import {
|
|
|
1936
2054
|
VECTOR_GRADIENT_INTERPOLATION_LABELS,
|
|
1937
2055
|
VECTOR_STROKE_GRADIENT_MODE_LABELS,
|
|
1938
2056
|
getVectorGradientEditor,
|
|
1939
|
-
vectorPaintToHex as
|
|
2057
|
+
vectorPaintToHex as vectorPaintToHex4
|
|
1940
2058
|
} from "@pixerate/editor/vector";
|
|
1941
|
-
import { Fragment as
|
|
1942
|
-
var
|
|
1943
|
-
var
|
|
1944
|
-
var
|
|
2059
|
+
import { Fragment as Fragment4, jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
2060
|
+
var selectClass9 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
|
|
2061
|
+
var numberClass8 = "h-7 w-14 rounded border border-border bg-background px-1.5 text-xs";
|
|
2062
|
+
var buttonClass12 = "inline-flex h-7 min-w-7 items-center justify-center rounded border border-border bg-background px-1.5 text-xs text-foreground hover:bg-muted disabled:cursor-not-allowed disabled:opacity-40 aria-pressed:bg-primary aria-pressed:text-primary-foreground";
|
|
1945
2063
|
var percent = (v) => Math.round(v * 1e3) / 10;
|
|
1946
2064
|
function VectorGradientEditor({ controller, className }) {
|
|
1947
2065
|
const state = useVectorEditorState(controller);
|
|
@@ -1953,26 +2071,26 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
1953
2071
|
const setType = (value) => {
|
|
1954
2072
|
if (value === "solid") {
|
|
1955
2073
|
const first = ed.stops[0];
|
|
1956
|
-
controller.setStyle({ [ed.target]:
|
|
2074
|
+
controller.setStyle({ [ed.target]: vectorPaintToHex4(first?.color) ?? first?.color ?? "#000000" }, void 0, { label: "Solid Colour" });
|
|
1957
2075
|
} else controller.setGradient({ type: value });
|
|
1958
2076
|
};
|
|
1959
|
-
return /* @__PURE__ */
|
|
1960
|
-
/* @__PURE__ */
|
|
1961
|
-
/* @__PURE__ */
|
|
1962
|
-
["fill", "stroke"].map((t) => /* @__PURE__ */
|
|
1963
|
-
ed.mixed && /* @__PURE__ */
|
|
2077
|
+
return /* @__PURE__ */ jsxs14("div", { className: `space-y-1.5 ${className ?? ""}`, "data-vector-gradient-editor": ed.target, "aria-label": "Gradient", children: [
|
|
2078
|
+
/* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-1.5", children: [
|
|
2079
|
+
/* @__PURE__ */ jsx14("span", { className: "w-16 text-xs text-foreground", children: "Gradient" }),
|
|
2080
|
+
["fill", "stroke"].map((t) => /* @__PURE__ */ jsx14("button", { type: "button", className: buttonClass12, "aria-pressed": ed.target === t, onClick: () => controller.setGradientTarget(t), children: t === "fill" ? "Fill" : "Stroke" }, t)),
|
|
2081
|
+
ed.mixed && /* @__PURE__ */ jsx14("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
|
|
1964
2082
|
] }),
|
|
1965
|
-
/* @__PURE__ */
|
|
1966
|
-
/* @__PURE__ */
|
|
1967
|
-
/* @__PURE__ */
|
|
1968
|
-
/* @__PURE__ */
|
|
1969
|
-
VECTOR_GRADIENT_TYPES.map((t) => /* @__PURE__ */
|
|
2083
|
+
/* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-1.5", children: [
|
|
2084
|
+
/* @__PURE__ */ jsx14("span", { className: "w-16 text-xs text-foreground", children: "Type" }),
|
|
2085
|
+
/* @__PURE__ */ jsxs14("select", { "aria-label": "Gradient type", className: selectClass9, value: ed.type ?? "solid", onChange: (e) => setType(e.target.value), children: [
|
|
2086
|
+
/* @__PURE__ */ jsx14("option", { value: "solid", children: "Solid" }),
|
|
2087
|
+
VECTOR_GRADIENT_TYPES.map((t) => /* @__PURE__ */ jsx14("option", { value: t, children: VECTOR_GRADIENT_TYPE_LABELS[t] }, t))
|
|
1970
2088
|
] }),
|
|
1971
|
-
/* @__PURE__ */
|
|
2089
|
+
/* @__PURE__ */ jsx14("button", { type: "button", className: buttonClass12, "aria-label": "Reverse gradient", title: "Reverse gradient", disabled: !g, onClick: () => controller.reverseGradient(), children: "\u21C4" })
|
|
1972
2090
|
] }),
|
|
1973
|
-
g && /* @__PURE__ */
|
|
1974
|
-
/* @__PURE__ */
|
|
1975
|
-
/* @__PURE__ */
|
|
2091
|
+
g && /* @__PURE__ */ jsxs14(Fragment4, { children: [
|
|
2092
|
+
/* @__PURE__ */ jsxs14("div", { className: "relative h-8", "data-vector-gradient-ramp": "", children: [
|
|
2093
|
+
/* @__PURE__ */ jsx14(
|
|
1976
2094
|
"div",
|
|
1977
2095
|
{
|
|
1978
2096
|
role: "button",
|
|
@@ -1986,7 +2104,7 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
1986
2104
|
}
|
|
1987
2105
|
}
|
|
1988
2106
|
),
|
|
1989
|
-
ed.stops.map((s) => /* @__PURE__ */
|
|
2107
|
+
ed.stops.map((s) => /* @__PURE__ */ jsx14(
|
|
1990
2108
|
"button",
|
|
1991
2109
|
{
|
|
1992
2110
|
type: "button",
|
|
@@ -2000,21 +2118,21 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
2000
2118
|
s.index
|
|
2001
2119
|
))
|
|
2002
2120
|
] }),
|
|
2003
|
-
sel && /* @__PURE__ */
|
|
2004
|
-
/* @__PURE__ */
|
|
2121
|
+
sel && /* @__PURE__ */ jsxs14("div", { className: "flex flex-wrap items-center gap-1.5", "data-vector-gradient-selected-stop": sel.index, children: [
|
|
2122
|
+
/* @__PURE__ */ jsx14(
|
|
2005
2123
|
"input",
|
|
2006
2124
|
{
|
|
2007
2125
|
type: "color",
|
|
2008
2126
|
"aria-label": "Stop colour",
|
|
2009
2127
|
className: "h-7 w-9 cursor-pointer rounded border border-border bg-background",
|
|
2010
|
-
value:
|
|
2128
|
+
value: vectorPaintToHex4(sel.color) ?? "#000000",
|
|
2011
2129
|
onChange: (e) => controller.setGradientStop(sel.index, { color: e.target.value }, { merge: `gradient-stop-color-${sel.index}` }),
|
|
2012
2130
|
onBlur: end
|
|
2013
2131
|
}
|
|
2014
2132
|
),
|
|
2015
|
-
/* @__PURE__ */
|
|
2133
|
+
/* @__PURE__ */ jsxs14("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
|
|
2016
2134
|
"Opacity",
|
|
2017
|
-
/* @__PURE__ */
|
|
2135
|
+
/* @__PURE__ */ jsx14(
|
|
2018
2136
|
"input",
|
|
2019
2137
|
{
|
|
2020
2138
|
type: "number",
|
|
@@ -2022,7 +2140,7 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
2022
2140
|
max: 100,
|
|
2023
2141
|
step: 5,
|
|
2024
2142
|
"aria-label": "Stop opacity",
|
|
2025
|
-
className:
|
|
2143
|
+
className: numberClass8,
|
|
2026
2144
|
value: percent(sel.opacity ?? 1),
|
|
2027
2145
|
onChange: (e) => {
|
|
2028
2146
|
const n = parseFloat(e.target.value);
|
|
@@ -2031,9 +2149,9 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
2031
2149
|
}
|
|
2032
2150
|
)
|
|
2033
2151
|
] }),
|
|
2034
|
-
/* @__PURE__ */
|
|
2152
|
+
/* @__PURE__ */ jsxs14("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
|
|
2035
2153
|
"At",
|
|
2036
|
-
/* @__PURE__ */
|
|
2154
|
+
/* @__PURE__ */ jsx14(
|
|
2037
2155
|
"input",
|
|
2038
2156
|
{
|
|
2039
2157
|
type: "number",
|
|
@@ -2041,7 +2159,7 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
2041
2159
|
max: 100,
|
|
2042
2160
|
step: 1,
|
|
2043
2161
|
"aria-label": "Stop position",
|
|
2044
|
-
className:
|
|
2162
|
+
className: numberClass8,
|
|
2045
2163
|
value: percent(sel.offset),
|
|
2046
2164
|
onChange: (e) => {
|
|
2047
2165
|
const n = parseFloat(e.target.value);
|
|
@@ -2050,9 +2168,9 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
2050
2168
|
}
|
|
2051
2169
|
)
|
|
2052
2170
|
] }),
|
|
2053
|
-
!sel.last && /* @__PURE__ */
|
|
2171
|
+
!sel.last && /* @__PURE__ */ jsxs14("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
|
|
2054
2172
|
"Midpoint",
|
|
2055
|
-
/* @__PURE__ */
|
|
2173
|
+
/* @__PURE__ */ jsx14(
|
|
2056
2174
|
"input",
|
|
2057
2175
|
{
|
|
2058
2176
|
type: "number",
|
|
@@ -2060,7 +2178,7 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
2060
2178
|
max: 95,
|
|
2061
2179
|
step: 1,
|
|
2062
2180
|
"aria-label": "Stop midpoint",
|
|
2063
|
-
className:
|
|
2181
|
+
className: numberClass8,
|
|
2064
2182
|
value: percent(sel.midpoint ?? 0.5),
|
|
2065
2183
|
onChange: (e) => {
|
|
2066
2184
|
const n = parseFloat(e.target.value);
|
|
@@ -2069,15 +2187,15 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
2069
2187
|
}
|
|
2070
2188
|
)
|
|
2071
2189
|
] }),
|
|
2072
|
-
/* @__PURE__ */
|
|
2190
|
+
/* @__PURE__ */ jsx14("button", { type: "button", className: buttonClass12, "aria-label": "Delete stop", title: "Delete stop", disabled: ed.stops.length <= 2, onClick: () => controller.removeGradientStop(sel.index), children: "\u2715" })
|
|
2073
2191
|
] }),
|
|
2074
|
-
/* @__PURE__ */
|
|
2075
|
-
/* @__PURE__ */
|
|
2076
|
-
/* @__PURE__ */
|
|
2192
|
+
/* @__PURE__ */ jsxs14("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
|
|
2193
|
+
/* @__PURE__ */ jsx14("span", { className: "w-16", children: "Colours" }),
|
|
2194
|
+
/* @__PURE__ */ jsx14("select", { "aria-label": "Gradient interpolation", className: selectClass9, value: ed.interpolation, onChange: (e) => controller.setGradient({ interpolation: e.target.value }), children: VECTOR_GRADIENT_INTERPOLATIONS.map((i) => /* @__PURE__ */ jsx14("option", { value: i, children: VECTOR_GRADIENT_INTERPOLATION_LABELS[i] }, i)) })
|
|
2077
2195
|
] }),
|
|
2078
|
-
/* @__PURE__ */
|
|
2079
|
-
/* @__PURE__ */
|
|
2080
|
-
/* @__PURE__ */
|
|
2196
|
+
/* @__PURE__ */ jsxs14("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
|
|
2197
|
+
/* @__PURE__ */ jsx14("span", { className: "w-16", children: "Smooth" }),
|
|
2198
|
+
/* @__PURE__ */ jsx14(
|
|
2081
2199
|
"input",
|
|
2082
2200
|
{
|
|
2083
2201
|
type: "range",
|
|
@@ -2093,23 +2211,23 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
2093
2211
|
}
|
|
2094
2212
|
)
|
|
2095
2213
|
] }),
|
|
2096
|
-
/* @__PURE__ */
|
|
2097
|
-
/* @__PURE__ */
|
|
2098
|
-
/* @__PURE__ */
|
|
2214
|
+
/* @__PURE__ */ jsxs14("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
|
|
2215
|
+
/* @__PURE__ */ jsx14("span", { className: "w-16", children: "Spread" }),
|
|
2216
|
+
/* @__PURE__ */ jsx14("select", { "aria-label": "Gradient spread", className: selectClass9, value: ed.spread, onChange: (e) => controller.setGradient({ spread: e.target.value }), children: VECTOR_GRADIENT_SPREADS.map((sp) => /* @__PURE__ */ jsx14("option", { value: sp, children: VECTOR_GRADIENT_SPREAD_LABELS[sp] }, sp)) })
|
|
2099
2217
|
] }),
|
|
2100
|
-
ed.target === "stroke" && /* @__PURE__ */
|
|
2101
|
-
/* @__PURE__ */
|
|
2102
|
-
/* @__PURE__ */
|
|
2218
|
+
ed.target === "stroke" && /* @__PURE__ */ jsxs14("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
|
|
2219
|
+
/* @__PURE__ */ jsx14("span", { className: "w-16", children: "On stroke" }),
|
|
2220
|
+
/* @__PURE__ */ jsx14("select", { "aria-label": "Gradient on stroke", className: selectClass9, value: ed.strokeMode, onChange: (e) => controller.setGradient({ strokeMode: e.target.value }), children: VECTOR_STROKE_GRADIENT_MODES.map((m) => /* @__PURE__ */ jsx14("option", { value: m, children: VECTOR_STROKE_GRADIENT_MODE_LABELS[m] }, m)) })
|
|
2103
2221
|
] })
|
|
2104
2222
|
] })
|
|
2105
2223
|
] });
|
|
2106
2224
|
}
|
|
2107
2225
|
|
|
2108
2226
|
// src/vector/VectorToolbar.tsx
|
|
2109
|
-
import { Fragment as
|
|
2227
|
+
import { Fragment as Fragment5, jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
2110
2228
|
var VECTOR_SNAP_TITLE = "Snap to objects";
|
|
2111
2229
|
var modKeyLabel = () => typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform ?? "") ? "\u2318" : "Ctrl";
|
|
2112
|
-
var
|
|
2230
|
+
var buttonClass13 = "inline-flex h-8 min-w-8 items-center justify-center rounded-md border border-border bg-background px-2 text-xs font-medium text-foreground hover:bg-muted disabled:cursor-not-allowed disabled:opacity-40 aria-pressed:bg-primary aria-pressed:text-primary-foreground";
|
|
2113
2231
|
function VectorToolbar({
|
|
2114
2232
|
controller,
|
|
2115
2233
|
tools = VECTOR_TOOLS,
|
|
@@ -2127,16 +2245,16 @@ function VectorToolbar({
|
|
|
2127
2245
|
const editing = state.editing;
|
|
2128
2246
|
const groupSelected = state.selection.some((id) => controller.getNode(id)?.type === "group");
|
|
2129
2247
|
const maskSelected = state.selection.some((id) => isVectorMaskGroup(controller.getNode(id)));
|
|
2130
|
-
const [live, setLive] =
|
|
2248
|
+
const [live, setLive] = React9.useState(false);
|
|
2131
2249
|
const booleanDisabled = live ? count < 1 : count < 2;
|
|
2132
|
-
const act = (label, title, run, disabled = false) => /* @__PURE__ */
|
|
2133
|
-
return /* @__PURE__ */
|
|
2134
|
-
/* @__PURE__ */
|
|
2250
|
+
const act = (label, title, run, disabled = false) => /* @__PURE__ */ jsx15("button", { type: "button", className: buttonClass13, title, "aria-label": title, disabled, onClick: () => void run(), children: label }, label);
|
|
2251
|
+
return /* @__PURE__ */ jsxs15("div", { role: "toolbar", "aria-label": "Vector tools", className: `flex flex-wrap items-center gap-1 ${className ?? ""}`, children: [
|
|
2252
|
+
/* @__PURE__ */ jsx15("div", { className: "flex items-center gap-1", role: "group", "aria-label": "Tools", children: tools.map((tool) => /* @__PURE__ */ jsx15(
|
|
2135
2253
|
"button",
|
|
2136
2254
|
{
|
|
2137
2255
|
type: "button",
|
|
2138
2256
|
"data-vector-tool": tool,
|
|
2139
|
-
className:
|
|
2257
|
+
className: buttonClass13,
|
|
2140
2258
|
"aria-pressed": state.tool === tool,
|
|
2141
2259
|
title: `${vectorToolLabel(tool)} (${VECTOR_TOOL_SHORTCUTS[tool]})`,
|
|
2142
2260
|
"aria-label": vectorToolLabel(tool),
|
|
@@ -2145,17 +2263,17 @@ function VectorToolbar({
|
|
|
2145
2263
|
},
|
|
2146
2264
|
tool
|
|
2147
2265
|
)) }),
|
|
2148
|
-
showHistory && /* @__PURE__ */
|
|
2266
|
+
showHistory && /* @__PURE__ */ jsxs15("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "History", children: [
|
|
2149
2267
|
act("\u21B6", state.canUndo ? `Undo ${state.undoLabel}` : "Undo", () => controller.undo(), !state.canUndo),
|
|
2150
2268
|
act("\u21B7", state.canRedo ? `Redo ${state.redoLabel}` : "Redo", () => controller.redo(), !state.canRedo)
|
|
2151
2269
|
] }),
|
|
2152
|
-
showSnap && /* @__PURE__ */
|
|
2153
|
-
/* @__PURE__ */
|
|
2270
|
+
showSnap && /* @__PURE__ */ jsxs15("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Snapping", children: [
|
|
2271
|
+
/* @__PURE__ */ jsx15(
|
|
2154
2272
|
"button",
|
|
2155
2273
|
{
|
|
2156
2274
|
type: "button",
|
|
2157
2275
|
"data-vector-snap": "",
|
|
2158
|
-
className:
|
|
2276
|
+
className: buttonClass13,
|
|
2159
2277
|
"aria-pressed": state.snap.objects,
|
|
2160
2278
|
title: `${VECTOR_SNAP_TITLE} (hold ${modKeyLabel()} to bypass)`,
|
|
2161
2279
|
"aria-label": VECTOR_SNAP_TITLE,
|
|
@@ -2163,7 +2281,7 @@ function VectorToolbar({
|
|
|
2163
2281
|
children: "Snap"
|
|
2164
2282
|
}
|
|
2165
2283
|
),
|
|
2166
|
-
/* @__PURE__ */
|
|
2284
|
+
/* @__PURE__ */ jsxs15(
|
|
2167
2285
|
"select",
|
|
2168
2286
|
{
|
|
2169
2287
|
"data-vector-polar": "",
|
|
@@ -2173,8 +2291,8 @@ function VectorToolbar({
|
|
|
2173
2291
|
value: String(vectorPolarIncrementOf(state.snap.angles) ?? ""),
|
|
2174
2292
|
onChange: (e) => controller.setSnap({ angles: vectorPolarAngles(Number(e.target.value)) }),
|
|
2175
2293
|
children: [
|
|
2176
|
-
vectorPolarIncrementOf(state.snap.angles) === null && /* @__PURE__ */
|
|
2177
|
-
[90, 45, 30, 15].map((n) => /* @__PURE__ */
|
|
2294
|
+
vectorPolarIncrementOf(state.snap.angles) === null && /* @__PURE__ */ jsx15("option", { value: "", children: "Custom" }),
|
|
2295
|
+
[90, 45, 30, 15].map((n) => /* @__PURE__ */ jsxs15("option", { value: n, children: [
|
|
2178
2296
|
n,
|
|
2179
2297
|
"\xB0"
|
|
2180
2298
|
] }, n))
|
|
@@ -2182,13 +2300,13 @@ function VectorToolbar({
|
|
|
2182
2300
|
}
|
|
2183
2301
|
)
|
|
2184
2302
|
] }),
|
|
2185
|
-
showPathEdit && /* @__PURE__ */
|
|
2186
|
-
VECTOR_PATH_ACTIONS.map((a) => /* @__PURE__ */
|
|
2303
|
+
showPathEdit && /* @__PURE__ */ jsxs15("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Path editing", children: [
|
|
2304
|
+
VECTOR_PATH_ACTIONS.map((a) => /* @__PURE__ */ jsx15(
|
|
2187
2305
|
"button",
|
|
2188
2306
|
{
|
|
2189
2307
|
type: "button",
|
|
2190
2308
|
"data-vector-path-action": a.id,
|
|
2191
|
-
className:
|
|
2309
|
+
className: buttonClass13,
|
|
2192
2310
|
title: a.shortcut ? `${a.title} (${modKeyLabel()}+${a.shortcut})` : a.title,
|
|
2193
2311
|
"aria-label": a.title,
|
|
2194
2312
|
disabled: !a.enabled(count, points),
|
|
@@ -2197,12 +2315,12 @@ function VectorToolbar({
|
|
|
2197
2315
|
},
|
|
2198
2316
|
a.id
|
|
2199
2317
|
)),
|
|
2200
|
-
VECTOR_EDIT_TOGGLES.map((t) => /* @__PURE__ */
|
|
2318
|
+
VECTOR_EDIT_TOGGLES.map((t) => /* @__PURE__ */ jsx15(
|
|
2201
2319
|
"button",
|
|
2202
2320
|
{
|
|
2203
2321
|
type: "button",
|
|
2204
2322
|
"data-vector-edit": t.key,
|
|
2205
|
-
className:
|
|
2323
|
+
className: buttonClass13,
|
|
2206
2324
|
"aria-pressed": editing[t.key],
|
|
2207
2325
|
title: t.title,
|
|
2208
2326
|
"aria-label": t.title,
|
|
@@ -2211,9 +2329,9 @@ function VectorToolbar({
|
|
|
2211
2329
|
},
|
|
2212
2330
|
t.key
|
|
2213
2331
|
)),
|
|
2214
|
-
state.tool === "pencil" && /* @__PURE__ */
|
|
2332
|
+
state.tool === "pencil" && /* @__PURE__ */ jsxs15("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
|
|
2215
2333
|
"Smoothing",
|
|
2216
|
-
/* @__PURE__ */
|
|
2334
|
+
/* @__PURE__ */ jsx15(
|
|
2217
2335
|
"input",
|
|
2218
2336
|
{
|
|
2219
2337
|
type: "range",
|
|
@@ -2227,9 +2345,9 @@ function VectorToolbar({
|
|
|
2227
2345
|
}
|
|
2228
2346
|
)
|
|
2229
2347
|
] }),
|
|
2230
|
-
state.tool === "eraser" && /* @__PURE__ */
|
|
2348
|
+
state.tool === "eraser" && /* @__PURE__ */ jsxs15("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
|
|
2231
2349
|
"Size",
|
|
2232
|
-
/* @__PURE__ */
|
|
2350
|
+
/* @__PURE__ */ jsx15(
|
|
2233
2351
|
"input",
|
|
2234
2352
|
{
|
|
2235
2353
|
type: "number",
|
|
@@ -2245,12 +2363,12 @@ function VectorToolbar({
|
|
|
2245
2363
|
}
|
|
2246
2364
|
)
|
|
2247
2365
|
] }),
|
|
2248
|
-
state.tool === "paintBucket" && /* @__PURE__ */
|
|
2366
|
+
state.tool === "paintBucket" && /* @__PURE__ */ jsx15(
|
|
2249
2367
|
"button",
|
|
2250
2368
|
{
|
|
2251
2369
|
type: "button",
|
|
2252
2370
|
"data-vector-fill-gap": "",
|
|
2253
|
-
className:
|
|
2371
|
+
className: buttonClass13,
|
|
2254
2372
|
"aria-pressed": editing.fillGap > 0,
|
|
2255
2373
|
title: "Close small gaps between path ends when filling",
|
|
2256
2374
|
"aria-label": "Close gaps",
|
|
@@ -2259,12 +2377,12 @@ function VectorToolbar({
|
|
|
2259
2377
|
}
|
|
2260
2378
|
)
|
|
2261
2379
|
] }),
|
|
2262
|
-
showDistort && /* @__PURE__ */
|
|
2380
|
+
showDistort && /* @__PURE__ */ jsx15("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Distort", children: /* @__PURE__ */ jsx15(
|
|
2263
2381
|
"button",
|
|
2264
2382
|
{
|
|
2265
2383
|
type: "button",
|
|
2266
2384
|
"data-vector-free-distort": "",
|
|
2267
|
-
className:
|
|
2385
|
+
className: buttonClass13,
|
|
2268
2386
|
"aria-pressed": state.freeDistort,
|
|
2269
2387
|
title: `Free Distort: drag a corner of the selection box to distort in perspective (Shift+Alt+${modKeyLabel()}: symmetric perspective; ${modKeyLabel()}-drag a side to skew)`,
|
|
2270
2388
|
"aria-label": "Free Distort",
|
|
@@ -2272,15 +2390,15 @@ function VectorToolbar({
|
|
|
2272
2390
|
children: "Distort"
|
|
2273
2391
|
}
|
|
2274
2392
|
) }),
|
|
2275
|
-
showBlend && /* @__PURE__ */
|
|
2276
|
-
showArrange && /* @__PURE__ */
|
|
2277
|
-
/* @__PURE__ */
|
|
2278
|
-
/* @__PURE__ */
|
|
2393
|
+
showBlend && /* @__PURE__ */ jsx15(VectorBlendControls, { controller }),
|
|
2394
|
+
showArrange && /* @__PURE__ */ jsxs15(Fragment5, { children: [
|
|
2395
|
+
/* @__PURE__ */ jsxs15("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Combine", children: [
|
|
2396
|
+
/* @__PURE__ */ jsx15(
|
|
2279
2397
|
"button",
|
|
2280
2398
|
{
|
|
2281
2399
|
type: "button",
|
|
2282
2400
|
"data-vector-live-boolean": "",
|
|
2283
|
-
className:
|
|
2401
|
+
className: buttonClass13,
|
|
2284
2402
|
"aria-pressed": live,
|
|
2285
2403
|
title: "Live booleans: keep the shapes editable",
|
|
2286
2404
|
"aria-label": "Live booleans",
|
|
@@ -2297,12 +2415,12 @@ function VectorToolbar({
|
|
|
2297
2415
|
act("\u25D0", `Make Clipping Mask (${modKeyLabel()}+7)`, () => controller.makeMask("clip"), count < 2),
|
|
2298
2416
|
act("\u25D1", `Release Mask (${modKeyLabel()}+Alt+7)`, () => controller.releaseMask(), !maskSelected)
|
|
2299
2417
|
] }),
|
|
2300
|
-
/* @__PURE__ */
|
|
2418
|
+
/* @__PURE__ */ jsx15("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Pathfinder", children: VECTOR_PATHFINDER_ACTIONS.map((a) => /* @__PURE__ */ jsx15(
|
|
2301
2419
|
"button",
|
|
2302
2420
|
{
|
|
2303
2421
|
type: "button",
|
|
2304
2422
|
"data-vector-pathfinder": a.id,
|
|
2305
|
-
className:
|
|
2423
|
+
className: buttonClass13,
|
|
2306
2424
|
title: a.title,
|
|
2307
2425
|
"aria-label": a.title,
|
|
2308
2426
|
disabled: count < a.min,
|
|
@@ -2311,7 +2429,7 @@ function VectorToolbar({
|
|
|
2311
2429
|
},
|
|
2312
2430
|
a.id
|
|
2313
2431
|
)) }),
|
|
2314
|
-
/* @__PURE__ */
|
|
2432
|
+
/* @__PURE__ */ jsxs15("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Arrange", children: [
|
|
2315
2433
|
act("\u21C8", "Bring to Front", () => controller.arrange("front"), count < 1),
|
|
2316
2434
|
act("\u21CA", "Send to Back", () => controller.arrange("back"), count < 1),
|
|
2317
2435
|
act("\u21E4", "Align Left", () => controller.align("left"), count < 1),
|
|
@@ -2329,19 +2447,19 @@ function VectorBlendControls({ controller }) {
|
|
|
2329
2447
|
const blend = getVectorBlendControls(state);
|
|
2330
2448
|
const o = blend.options;
|
|
2331
2449
|
const selected = blend.ids.length > 0;
|
|
2332
|
-
return /* @__PURE__ */
|
|
2333
|
-
/* @__PURE__ */
|
|
2334
|
-
/* @__PURE__ */
|
|
2450
|
+
return /* @__PURE__ */ jsxs15("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Blend", "data-vector-blend-controls": "", children: [
|
|
2451
|
+
/* @__PURE__ */ jsx15("button", { type: "button", "data-vector-blend-action": "make", className: buttonClass13, title: "Make Blend", "aria-label": "Make Blend", disabled: !blend.canBlend, onClick: () => controller.blend(), children: "\u29D3" }),
|
|
2452
|
+
/* @__PURE__ */ jsx15(
|
|
2335
2453
|
"select",
|
|
2336
2454
|
{
|
|
2337
2455
|
"aria-label": "Blend spacing",
|
|
2338
2456
|
className: "h-7 rounded border border-border bg-background px-1 text-xs",
|
|
2339
2457
|
value: o.spacing,
|
|
2340
2458
|
onChange: (e) => applyVectorBlendOptions(controller, { spacing: e.target.value }),
|
|
2341
|
-
children: VECTOR_BLEND_SPACINGS.map((sp) => /* @__PURE__ */
|
|
2459
|
+
children: VECTOR_BLEND_SPACINGS.map((sp) => /* @__PURE__ */ jsx15("option", { value: sp, children: VECTOR_BLEND_SPACING_LABELS[sp] }, sp))
|
|
2342
2460
|
}
|
|
2343
2461
|
),
|
|
2344
|
-
o.spacing !== "smoothColor" && /* @__PURE__ */
|
|
2462
|
+
o.spacing !== "smoothColor" && /* @__PURE__ */ jsx15(
|
|
2345
2463
|
"input",
|
|
2346
2464
|
{
|
|
2347
2465
|
type: "number",
|
|
@@ -2357,12 +2475,12 @@ function VectorBlendControls({ controller }) {
|
|
|
2357
2475
|
}
|
|
2358
2476
|
}
|
|
2359
2477
|
),
|
|
2360
|
-
/* @__PURE__ */
|
|
2478
|
+
/* @__PURE__ */ jsx15(
|
|
2361
2479
|
"button",
|
|
2362
2480
|
{
|
|
2363
2481
|
type: "button",
|
|
2364
2482
|
"data-vector-blend-action": "orient",
|
|
2365
|
-
className:
|
|
2483
|
+
className: buttonClass13,
|
|
2366
2484
|
"aria-pressed": o.orientToSpine,
|
|
2367
2485
|
title: "Orient steps to the spine",
|
|
2368
2486
|
"aria-label": "Orient to spine",
|
|
@@ -2370,12 +2488,12 @@ function VectorBlendControls({ controller }) {
|
|
|
2370
2488
|
children: "\u293F"
|
|
2371
2489
|
}
|
|
2372
2490
|
),
|
|
2373
|
-
/* @__PURE__ */
|
|
2491
|
+
/* @__PURE__ */ jsx15(
|
|
2374
2492
|
"button",
|
|
2375
2493
|
{
|
|
2376
2494
|
type: "button",
|
|
2377
2495
|
"data-vector-blend-action": "spine",
|
|
2378
|
-
className:
|
|
2496
|
+
className: buttonClass13,
|
|
2379
2497
|
title: "Replace Spine (select a blend and a path)",
|
|
2380
2498
|
"aria-label": "Replace Spine",
|
|
2381
2499
|
disabled: !blend.canReplaceSpine,
|
|
@@ -2383,12 +2501,12 @@ function VectorBlendControls({ controller }) {
|
|
|
2383
2501
|
children: "\u2307"
|
|
2384
2502
|
}
|
|
2385
2503
|
),
|
|
2386
|
-
/* @__PURE__ */
|
|
2387
|
-
/* @__PURE__ */
|
|
2504
|
+
/* @__PURE__ */ jsx15("button", { type: "button", "data-vector-blend-action": "expand", className: buttonClass13, title: "Expand Blend", "aria-label": "Expand Blend", disabled: !selected, onClick: () => controller.expandBlend(), children: "\u29C9" }),
|
|
2505
|
+
/* @__PURE__ */ jsx15("button", { type: "button", "data-vector-blend-action": "release", className: buttonClass13, title: "Release Blend", "aria-label": "Release Blend", disabled: !selected, onClick: () => controller.releaseBlend(), children: "\u29C4" })
|
|
2388
2506
|
] });
|
|
2389
2507
|
}
|
|
2390
|
-
var
|
|
2391
|
-
var
|
|
2508
|
+
var selectClass10 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
|
|
2509
|
+
var numberClass9 = "h-7 w-16 rounded border border-border bg-background px-1.5 text-xs";
|
|
2392
2510
|
function VectorStylePanel({ controller, className }) {
|
|
2393
2511
|
const state = useVectorEditorState(controller);
|
|
2394
2512
|
const style = getVectorSelectionStyle(state);
|
|
@@ -2420,17 +2538,17 @@ function VectorStylePanel({ controller, className }) {
|
|
|
2420
2538
|
if (style) controller.setWidthProfile(id);
|
|
2421
2539
|
else controller.setDefaults({ strokeWidthProfile: widthProfilePreset(id)?.length ? widthProfilePreset(id) : null });
|
|
2422
2540
|
};
|
|
2423
|
-
const mixed = (key) => style?.mixed[key] ? /* @__PURE__ */
|
|
2541
|
+
const mixed = (key) => style?.mixed[key] ? /* @__PURE__ */ jsx15("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" }) : null;
|
|
2424
2542
|
const arrowRow = (end, label) => {
|
|
2425
2543
|
const key = end === "start" ? "startArrow" : "endArrow";
|
|
2426
2544
|
const scaleKey = end === "start" ? "startArrowScale" : "endArrowScale";
|
|
2427
|
-
return /* @__PURE__ */
|
|
2428
|
-
/* @__PURE__ */
|
|
2429
|
-
/* @__PURE__ */
|
|
2430
|
-
/* @__PURE__ */
|
|
2431
|
-
VECTOR_ARROWHEADS.map((h) => /* @__PURE__ */
|
|
2545
|
+
return /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
|
|
2546
|
+
/* @__PURE__ */ jsx15("span", { className: "w-16 text-xs text-foreground", children: label }),
|
|
2547
|
+
/* @__PURE__ */ jsxs15("select", { "aria-label": `${label} arrowhead`, className: selectClass10, value: source[key] ?? "", onChange: (e) => apply({ [key]: e.target.value || null }), children: [
|
|
2548
|
+
/* @__PURE__ */ jsx15("option", { value: "", children: "None" }),
|
|
2549
|
+
VECTOR_ARROWHEADS.map((h) => /* @__PURE__ */ jsx15("option", { value: h, children: VECTOR_ARROWHEAD_LABELS[h] }, h))
|
|
2432
2550
|
] }),
|
|
2433
|
-
/* @__PURE__ */
|
|
2551
|
+
/* @__PURE__ */ jsx15(
|
|
2434
2552
|
"input",
|
|
2435
2553
|
{
|
|
2436
2554
|
type: "number",
|
|
@@ -2438,7 +2556,7 @@ function VectorStylePanel({ controller, className }) {
|
|
|
2438
2556
|
step: 0.25,
|
|
2439
2557
|
"aria-label": `${label} arrowhead scale`,
|
|
2440
2558
|
title: "Scale (\xD7 stroke width)",
|
|
2441
|
-
className:
|
|
2559
|
+
className: numberClass9,
|
|
2442
2560
|
value: source[scaleKey],
|
|
2443
2561
|
onChange: (e) => {
|
|
2444
2562
|
const n = parseFloat(e.target.value);
|
|
@@ -2454,14 +2572,14 @@ function VectorStylePanel({ controller, className }) {
|
|
|
2454
2572
|
};
|
|
2455
2573
|
const paintRow = (key, label) => {
|
|
2456
2574
|
const paint = source[key];
|
|
2457
|
-
const hex =
|
|
2575
|
+
const hex = vectorPaintToHex5(paint);
|
|
2458
2576
|
const on = paint !== null && paint !== "none";
|
|
2459
|
-
return /* @__PURE__ */
|
|
2460
|
-
/* @__PURE__ */
|
|
2461
|
-
/* @__PURE__ */
|
|
2577
|
+
return /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
|
|
2578
|
+
/* @__PURE__ */ jsxs15("label", { className: "flex w-16 items-center gap-1.5 text-xs text-foreground", children: [
|
|
2579
|
+
/* @__PURE__ */ jsx15("input", { type: "checkbox", checked: on, "aria-label": `${label} on`, onChange: (e) => apply({ [key]: e.target.checked ? hex ?? "#000000" : null }) }),
|
|
2462
2580
|
label
|
|
2463
2581
|
] }),
|
|
2464
|
-
/* @__PURE__ */
|
|
2582
|
+
/* @__PURE__ */ jsx15(
|
|
2465
2583
|
"input",
|
|
2466
2584
|
{
|
|
2467
2585
|
type: "color",
|
|
@@ -2472,18 +2590,18 @@ function VectorStylePanel({ controller, className }) {
|
|
|
2472
2590
|
onChange: (e) => apply({ [key]: e.target.value })
|
|
2473
2591
|
}
|
|
2474
2592
|
),
|
|
2475
|
-
on && !hex && /* @__PURE__ */
|
|
2476
|
-
style?.mixed[key] && /* @__PURE__ */
|
|
2593
|
+
on && !hex && /* @__PURE__ */ jsx15("span", { className: "truncate text-[11px] text-muted-foreground", children: typeof paint === "string" ? paint : "Gradient" }),
|
|
2594
|
+
style?.mixed[key] && /* @__PURE__ */ jsx15("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
|
|
2477
2595
|
] });
|
|
2478
2596
|
};
|
|
2479
|
-
return /* @__PURE__ */
|
|
2480
|
-
/* @__PURE__ */
|
|
2597
|
+
return /* @__PURE__ */ jsxs15("div", { className: `space-y-2 ${className ?? ""}`, "aria-label": style ? "Selection style" : "New shape style", children: [
|
|
2598
|
+
/* @__PURE__ */ jsx15("p", { className: "text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: style ? "Selection" : "New shapes" }),
|
|
2481
2599
|
paintRow("fill", "Fill"),
|
|
2482
2600
|
paintRow("stroke", "Stroke"),
|
|
2483
|
-
style && /* @__PURE__ */
|
|
2484
|
-
/* @__PURE__ */
|
|
2485
|
-
/* @__PURE__ */
|
|
2486
|
-
/* @__PURE__ */
|
|
2601
|
+
style && /* @__PURE__ */ jsx15(VectorGradientEditor, { controller }),
|
|
2602
|
+
/* @__PURE__ */ jsxs15("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
|
|
2603
|
+
/* @__PURE__ */ jsx15("span", { className: "w-16", children: "Width" }),
|
|
2604
|
+
/* @__PURE__ */ jsx15(
|
|
2487
2605
|
"input",
|
|
2488
2606
|
{
|
|
2489
2607
|
type: "number",
|
|
@@ -2499,33 +2617,33 @@ function VectorStylePanel({ controller, className }) {
|
|
|
2499
2617
|
}
|
|
2500
2618
|
)
|
|
2501
2619
|
] }),
|
|
2502
|
-
/* @__PURE__ */
|
|
2503
|
-
/* @__PURE__ */
|
|
2504
|
-
/* @__PURE__ */
|
|
2620
|
+
/* @__PURE__ */ jsxs15("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "strokeAlign", children: [
|
|
2621
|
+
/* @__PURE__ */ jsx15("span", { className: "w-16", children: "Align" }),
|
|
2622
|
+
/* @__PURE__ */ jsx15("select", { "aria-label": "Stroke alignment", className: selectClass10, value: source.strokeAlign, onChange: (e) => apply({ strokeAlign: e.target.value }), children: Object.keys(VECTOR_STROKE_ALIGN_LABELS2).map((a) => /* @__PURE__ */ jsx15("option", { value: a, children: VECTOR_STROKE_ALIGN_LABELS2[a] }, a)) }),
|
|
2505
2623
|
mixed("strokeAlign")
|
|
2506
2624
|
] }),
|
|
2507
|
-
/* @__PURE__ */
|
|
2508
|
-
/* @__PURE__ */
|
|
2509
|
-
/* @__PURE__ */
|
|
2625
|
+
/* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "widthProfile", children: [
|
|
2626
|
+
/* @__PURE__ */ jsx15("span", { className: "w-16", children: "Profile" }),
|
|
2627
|
+
/* @__PURE__ */ jsxs15(
|
|
2510
2628
|
"select",
|
|
2511
2629
|
{
|
|
2512
2630
|
"aria-label": "Width profile",
|
|
2513
|
-
className:
|
|
2631
|
+
className: selectClass10,
|
|
2514
2632
|
value: source.widthProfile,
|
|
2515
2633
|
onChange: (e) => {
|
|
2516
2634
|
if (e.target.value !== "custom") setProfile(e.target.value);
|
|
2517
2635
|
},
|
|
2518
2636
|
children: [
|
|
2519
|
-
VECTOR_WIDTH_PRESETS.map((p) => /* @__PURE__ */
|
|
2520
|
-
source.widthProfile === "custom" && /* @__PURE__ */
|
|
2637
|
+
VECTOR_WIDTH_PRESETS.map((p) => /* @__PURE__ */ jsx15("option", { value: p.id, children: p.label }, p.id)),
|
|
2638
|
+
source.widthProfile === "custom" && /* @__PURE__ */ jsx15("option", { value: "custom", disabled: true, children: "Custom" })
|
|
2521
2639
|
]
|
|
2522
2640
|
}
|
|
2523
2641
|
),
|
|
2524
|
-
/* @__PURE__ */
|
|
2642
|
+
/* @__PURE__ */ jsx15(
|
|
2525
2643
|
"button",
|
|
2526
2644
|
{
|
|
2527
2645
|
type: "button",
|
|
2528
|
-
className:
|
|
2646
|
+
className: buttonClass13,
|
|
2529
2647
|
title: "Reset to uniform width",
|
|
2530
2648
|
"aria-label": "Reset to uniform width",
|
|
2531
2649
|
disabled: source.widthProfile === "uniform" && !style?.mixed.widthProfile,
|
|
@@ -2535,27 +2653,27 @@ function VectorStylePanel({ controller, className }) {
|
|
|
2535
2653
|
),
|
|
2536
2654
|
mixed("widthProfile")
|
|
2537
2655
|
] }),
|
|
2538
|
-
source.hasOpenPath && /* @__PURE__ */
|
|
2656
|
+
source.hasOpenPath && /* @__PURE__ */ jsxs15(Fragment5, { children: [
|
|
2539
2657
|
arrowRow("start", "Start"),
|
|
2540
2658
|
arrowRow("end", "End"),
|
|
2541
|
-
(source.startArrow || source.endArrow) && /* @__PURE__ */
|
|
2542
|
-
/* @__PURE__ */
|
|
2543
|
-
/* @__PURE__ */
|
|
2544
|
-
/* @__PURE__ */
|
|
2545
|
-
/* @__PURE__ */
|
|
2659
|
+
(source.startArrow || source.endArrow) && /* @__PURE__ */ jsxs15("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "arrowAlign", children: [
|
|
2660
|
+
/* @__PURE__ */ jsx15("span", { className: "w-16", children: "Heads" }),
|
|
2661
|
+
/* @__PURE__ */ jsxs15("select", { "aria-label": "Arrowhead alignment", className: selectClass10, value: source.arrowAlign, onChange: (e) => apply({ arrowAlign: e.target.value }), children: [
|
|
2662
|
+
/* @__PURE__ */ jsx15("option", { value: "extend", children: "Tip past end" }),
|
|
2663
|
+
/* @__PURE__ */ jsx15("option", { value: "tip", children: "Tip at end" })
|
|
2546
2664
|
] })
|
|
2547
2665
|
] })
|
|
2548
2666
|
] }),
|
|
2549
|
-
source.corners > 0 && /* @__PURE__ */
|
|
2550
|
-
/* @__PURE__ */
|
|
2551
|
-
/* @__PURE__ */
|
|
2667
|
+
source.corners > 0 && /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-2", "data-vector-style": "corners", children: [
|
|
2668
|
+
/* @__PURE__ */ jsx15("span", { className: "w-16 text-xs text-foreground", children: "Corners" }),
|
|
2669
|
+
/* @__PURE__ */ jsx15(
|
|
2552
2670
|
"input",
|
|
2553
2671
|
{
|
|
2554
2672
|
type: "number",
|
|
2555
2673
|
min: 0,
|
|
2556
2674
|
step: 1,
|
|
2557
2675
|
"aria-label": "Corner radius",
|
|
2558
|
-
className:
|
|
2676
|
+
className: numberClass9,
|
|
2559
2677
|
value: Math.round(source.cornerRadius * 100) / 100,
|
|
2560
2678
|
onChange: (e) => {
|
|
2561
2679
|
const n = parseFloat(e.target.value);
|
|
@@ -2563,12 +2681,12 @@ function VectorStylePanel({ controller, className }) {
|
|
|
2563
2681
|
}
|
|
2564
2682
|
}
|
|
2565
2683
|
),
|
|
2566
|
-
/* @__PURE__ */
|
|
2684
|
+
/* @__PURE__ */ jsx15("select", { "aria-label": "Corner kind", className: selectClass10, value: source.cornerKind, onChange: (e) => setCorners({ kind: e.target.value }), children: VECTOR_CORNER_KINDS.map((k) => /* @__PURE__ */ jsx15("option", { value: k, children: VECTOR_CORNER_KIND_LABELS[k] }, k)) }),
|
|
2567
2685
|
mixed("cornerRadius")
|
|
2568
2686
|
] }),
|
|
2569
|
-
style && source.corners > 0 && source.cornerKind === "round" && source.cornerRadius > 0 && /* @__PURE__ */
|
|
2570
|
-
/* @__PURE__ */
|
|
2571
|
-
/* @__PURE__ */
|
|
2687
|
+
style && source.corners > 0 && source.cornerKind === "round" && source.cornerRadius > 0 && /* @__PURE__ */ jsxs15("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "corner-smoothing", title: "Corner smoothing: squircle corners that ease into the curve (0.6 is iOS-like)", children: [
|
|
2688
|
+
/* @__PURE__ */ jsx15("span", { className: "w-16", children: "Smoothing" }),
|
|
2689
|
+
/* @__PURE__ */ jsx15(
|
|
2572
2690
|
"input",
|
|
2573
2691
|
{
|
|
2574
2692
|
type: "range",
|
|
@@ -2583,9 +2701,9 @@ function VectorStylePanel({ controller, className }) {
|
|
|
2583
2701
|
}
|
|
2584
2702
|
)
|
|
2585
2703
|
] }),
|
|
2586
|
-
style && /* @__PURE__ */
|
|
2587
|
-
/* @__PURE__ */
|
|
2588
|
-
/* @__PURE__ */
|
|
2704
|
+
style && /* @__PURE__ */ jsxs15("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
|
|
2705
|
+
/* @__PURE__ */ jsx15("span", { className: "w-16", children: "Opacity" }),
|
|
2706
|
+
/* @__PURE__ */ jsx15(
|
|
2589
2707
|
"input",
|
|
2590
2708
|
{
|
|
2591
2709
|
type: "range",
|
|
@@ -2599,26 +2717,28 @@ function VectorStylePanel({ controller, className }) {
|
|
|
2599
2717
|
}
|
|
2600
2718
|
)
|
|
2601
2719
|
] }),
|
|
2602
|
-
/* @__PURE__ */
|
|
2603
|
-
style && /* @__PURE__ */
|
|
2604
|
-
style && /* @__PURE__ */
|
|
2605
|
-
style && /* @__PURE__ */
|
|
2606
|
-
style && /* @__PURE__ */
|
|
2607
|
-
/* @__PURE__ */
|
|
2608
|
-
/* @__PURE__ */
|
|
2609
|
-
/* @__PURE__ */
|
|
2610
|
-
/* @__PURE__ */
|
|
2720
|
+
/* @__PURE__ */ jsx15(VectorTextPanel, { controller }),
|
|
2721
|
+
style && /* @__PURE__ */ jsx15(VectorAppearancePanel, { controller }),
|
|
2722
|
+
style && /* @__PURE__ */ jsx15(VectorEffectsPanel, { controller }),
|
|
2723
|
+
style && /* @__PURE__ */ jsx15(VectorEnvelopePanel, { controller }),
|
|
2724
|
+
style && /* @__PURE__ */ jsx15(VectorMaskPanel, { controller }),
|
|
2725
|
+
/* @__PURE__ */ jsx15(VectorFramePanel, { controller }),
|
|
2726
|
+
/* @__PURE__ */ jsx15(VectorConnectorPanel, { controller }),
|
|
2727
|
+
/* @__PURE__ */ jsx15(VectorShadingPanel, { controller }),
|
|
2728
|
+
/* @__PURE__ */ jsx15(VectorTracePanel, { controller }),
|
|
2729
|
+
/* @__PURE__ */ jsx15(VectorConstraintsPanel, { controller }),
|
|
2730
|
+
/* @__PURE__ */ jsx15(VectorSymbolsPanel, { controller })
|
|
2611
2731
|
] });
|
|
2612
2732
|
}
|
|
2613
2733
|
|
|
2614
2734
|
// src/vector/VectorEditor.tsx
|
|
2615
|
-
import { jsx as
|
|
2735
|
+
import { jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
2616
2736
|
function VectorEditor({ controller, tools, showStylePanel = true, height = 520, canvasProps, className }) {
|
|
2617
|
-
return /* @__PURE__ */
|
|
2618
|
-
/* @__PURE__ */
|
|
2619
|
-
/* @__PURE__ */
|
|
2620
|
-
/* @__PURE__ */
|
|
2621
|
-
showStylePanel && /* @__PURE__ */
|
|
2737
|
+
return /* @__PURE__ */ jsxs16("div", { className: `flex flex-col gap-2 ${className ?? ""}`, "data-vector-editor": "", children: [
|
|
2738
|
+
/* @__PURE__ */ jsx16(VectorToolbar, { controller, tools }),
|
|
2739
|
+
/* @__PURE__ */ jsxs16("div", { className: "flex min-h-0 flex-col gap-2 md:flex-row", children: [
|
|
2740
|
+
/* @__PURE__ */ jsx16("div", { className: "relative min-h-[320px] flex-1 overflow-hidden rounded-md border border-border bg-muted/40", style: { height }, children: /* @__PURE__ */ jsx16(VectorCanvas, { controller, ...canvasProps }) }),
|
|
2741
|
+
showStylePanel && /* @__PURE__ */ jsx16(VectorStylePanel, { controller, className: "w-full shrink-0 rounded-md border border-border p-3 md:w-56" })
|
|
2622
2742
|
] })
|
|
2623
2743
|
] });
|
|
2624
2744
|
}
|
|
@@ -2638,11 +2758,13 @@ export {
|
|
|
2638
2758
|
VectorMaskPanel,
|
|
2639
2759
|
VectorMaskedNodes,
|
|
2640
2760
|
VectorSceneNodes,
|
|
2761
|
+
VectorShadingPanel,
|
|
2641
2762
|
VectorStylePanel,
|
|
2642
2763
|
VectorSymbolsPanel,
|
|
2643
2764
|
VectorTextElement,
|
|
2644
2765
|
VectorTextPanel,
|
|
2645
2766
|
VectorToolbar,
|
|
2767
|
+
VectorTracePanel,
|
|
2646
2768
|
VectorWidthPointEditor,
|
|
2647
2769
|
useVectorCommands,
|
|
2648
2770
|
useVectorEditor,
|