@pixerate/editor-react 0.74.0 → 0.75.1
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 +328 -220
- 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 +318 -212
- package/dist/vector/index.js.map +1 -1
- package/package.json +3 -3
package/dist/vector/index.cjs
CHANGED
|
@@ -45,11 +45,13 @@ __export(vector_exports, {
|
|
|
45
45
|
VectorMaskPanel: () => VectorMaskPanel,
|
|
46
46
|
VectorMaskedNodes: () => VectorMaskedNodes,
|
|
47
47
|
VectorSceneNodes: () => VectorSceneNodes,
|
|
48
|
+
VectorShadingPanel: () => VectorShadingPanel,
|
|
48
49
|
VectorStylePanel: () => VectorStylePanel,
|
|
49
50
|
VectorSymbolsPanel: () => VectorSymbolsPanel,
|
|
50
51
|
VectorTextElement: () => VectorTextElement,
|
|
51
52
|
VectorTextPanel: () => VectorTextPanel,
|
|
52
53
|
VectorToolbar: () => VectorToolbar,
|
|
54
|
+
VectorTracePanel: () => VectorTracePanel,
|
|
53
55
|
VectorWidthPointEditor: () => VectorWidthPointEditor,
|
|
54
56
|
useVectorCommands: () => useVectorCommands,
|
|
55
57
|
useVectorEditor: () => useVectorEditor,
|
|
@@ -275,48 +277,19 @@ function VectorTextElement({ node }) {
|
|
|
275
277
|
)) });
|
|
276
278
|
}
|
|
277
279
|
function VectorSceneNodes({ nodes }) {
|
|
278
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: nodes.map(
|
|
279
|
-
(node) => node.kind === "text" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(VectorTextElement, { node }, node.id) : node.kind === "group" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
280
|
-
"g",
|
|
281
|
-
{
|
|
282
|
-
"data-vector-id": node.id,
|
|
283
|
-
"data-vector-mask": node.mask?.type,
|
|
284
|
-
transform: node.transform,
|
|
285
|
-
opacity: node.opacity < 1 ? node.opacity : void 0,
|
|
286
|
-
filter: node.filter,
|
|
287
|
-
style: blendStyle(node.blendMode),
|
|
288
|
-
children: node.mask ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(VectorMaskedNodes, { mask: node.mask, nodes: node.children }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(VectorSceneNodes, { nodes: node.children })
|
|
289
|
-
},
|
|
290
|
-
node.id
|
|
291
|
-
) : node.layers ? (
|
|
292
|
-
// Fill and stroke stacks: each layer in order, bottom to top.
|
|
293
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
294
|
-
"g",
|
|
295
|
-
{
|
|
296
|
-
"data-vector-id": node.id,
|
|
297
|
-
transform: node.transform,
|
|
298
|
-
opacity: node.opacity < 1 ? node.opacity : void 0,
|
|
299
|
-
filter: node.filter,
|
|
300
|
-
style: blendStyle(node.blendMode),
|
|
301
|
-
children: node.layers.map((layer) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(VectorPaintPath, { paint: layer, d: node.d, fillRule: node.fillRule, layer: `${layer.kind}-${layer.index}` }, `${layer.kind}-${layer.index}`))
|
|
302
|
-
},
|
|
303
|
-
node.id
|
|
304
|
-
)
|
|
305
|
-
) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
306
|
-
VectorPaintPath,
|
|
307
|
-
{
|
|
308
|
-
paint: node,
|
|
309
|
-
d: node.d,
|
|
310
|
-
fillRule: node.fillRule,
|
|
311
|
-
id: node.id,
|
|
312
|
-
transform: node.transform,
|
|
313
|
-
filter: node.filter,
|
|
314
|
-
blendMode: node.blendMode
|
|
315
|
-
},
|
|
316
|
-
node.id
|
|
317
|
-
)
|
|
318
|
-
) });
|
|
280
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: nodes.map((node) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(VectorSceneNode, { node }, node.id)) });
|
|
319
281
|
}
|
|
282
|
+
var VectorSceneNode = import_react3.default.memo(function VectorSceneNode2({ node }) {
|
|
283
|
+
if (node.kind === "text") return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(VectorTextElement, { node });
|
|
284
|
+
if (node.kind === "group")
|
|
285
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("g", { "data-vector-id": node.id, "data-vector-mask": node.mask?.type, transform: node.transform, opacity: node.opacity < 1 ? node.opacity : void 0, filter: node.filter, style: blendStyle(node.blendMode), children: node.mask ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(VectorMaskedNodes, { mask: node.mask, nodes: node.children }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(VectorSceneNodes, { nodes: node.children }) });
|
|
286
|
+
if (node.layers)
|
|
287
|
+
return (
|
|
288
|
+
// Fill and stroke stacks: each layer in order, bottom to top.
|
|
289
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("g", { "data-vector-id": node.id, transform: node.transform, opacity: node.opacity < 1 ? node.opacity : void 0, filter: node.filter, style: blendStyle(node.blendMode), children: node.layers.map((layer) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(VectorPaintPath, { paint: layer, d: node.d, fillRule: node.fillRule, layer: `${layer.kind}-${layer.index}` }, `${layer.kind}-${layer.index}`)) })
|
|
290
|
+
);
|
|
291
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(VectorPaintPath, { paint: node, d: node.d, fillRule: node.fillRule, id: node.id, transform: node.transform, filter: node.filter, blendMode: node.blendMode });
|
|
292
|
+
});
|
|
320
293
|
function VectorMaskedNodes({ mask, nodes }) {
|
|
321
294
|
const b = mask.bounds;
|
|
322
295
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
|
|
@@ -411,7 +384,8 @@ function VectorCanvas({
|
|
|
411
384
|
const editingText = state.textEdit?.nodeId ?? null;
|
|
412
385
|
const fitted = (0, import_react3.useRef)(!fitOnMount);
|
|
413
386
|
const uid = (0, import_react3.useId)().replace(/:/g, "");
|
|
414
|
-
const
|
|
387
|
+
const [size, setSize] = (0, import_react3.useState)(null);
|
|
388
|
+
const scene = (0, import_vector3.getVectorSceneRenderData)(state, { idPrefix: `ve${uid}-`, ...size ? { viewportSize: size } : {} });
|
|
415
389
|
const overlay = (0, import_vector3.getVectorOverlayRenderData)(state);
|
|
416
390
|
const host = (0, import_vector3.getVectorSymbolEditBackground)(state, { idPrefix: `ve${uid}-host-` });
|
|
417
391
|
(0, import_react3.useEffect)(() => {
|
|
@@ -429,6 +403,18 @@ function VectorCanvas({
|
|
|
429
403
|
observer.observe(el);
|
|
430
404
|
return () => observer.disconnect();
|
|
431
405
|
}, [controller]);
|
|
406
|
+
(0, import_react3.useEffect)(() => {
|
|
407
|
+
const el = svgRef.current;
|
|
408
|
+
if (!el || typeof ResizeObserver === "undefined") return;
|
|
409
|
+
const measure = () => {
|
|
410
|
+
const r = el.getBoundingClientRect();
|
|
411
|
+
if (r.width > 0 && r.height > 0) setSize((s) => s && s.width === r.width && s.height === r.height ? s : { width: r.width, height: r.height });
|
|
412
|
+
};
|
|
413
|
+
measure();
|
|
414
|
+
const observer = new ResizeObserver(measure);
|
|
415
|
+
observer.observe(el);
|
|
416
|
+
return () => observer.disconnect();
|
|
417
|
+
}, []);
|
|
432
418
|
(0, import_react3.useEffect)(() => {
|
|
433
419
|
if (!editingText) return;
|
|
434
420
|
const t = setTimeout(() => textRef.current?.focus({ preventScroll: true }), 0);
|
|
@@ -564,8 +550,8 @@ function VectorCanvas({
|
|
|
564
550
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: h.x, cy: h.y, r: import_vector3.VECTOR_OVERLAY_THEME.handleRadius + (h.hovered || h.active ? 1 : 0), fill: h.active ? accentColor : import_vector3.VECTOR_OVERLAY_THEME.handleFill, stroke: accentColor })
|
|
565
551
|
] }, h.index)),
|
|
566
552
|
overlay.envelope.anchors.map((a) => {
|
|
567
|
-
const
|
|
568
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { "data-vector-envelope-anchor": a.index, x: a.x -
|
|
553
|
+
const size2 = as + (a.hovered || a.active ? 2 : 0);
|
|
554
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { "data-vector-envelope-anchor": a.index, x: a.x - size2 / 2, y: a.y - size2 / 2, width: size2, height: size2, fill: a.active ? accentColor : import_vector3.VECTOR_OVERLAY_THEME.handleFill, stroke: accentColor }, a.index);
|
|
569
555
|
})
|
|
570
556
|
] }),
|
|
571
557
|
overlay.handles.map((h) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { "data-vector-handle": h.ref.handle, children: [
|
|
@@ -573,10 +559,10 @@ function VectorCanvas({
|
|
|
573
559
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: h.x, cy: h.y, r: import_vector3.VECTOR_OVERLAY_THEME.handleRadius + (h.hovered ? 1 : 0), fill: import_vector3.VECTOR_OVERLAY_THEME.handleFill, stroke: accentColor })
|
|
574
560
|
] }, `${h.ref.nodeId}:${h.ref.subpath}:${h.ref.index}:${h.ref.handle}`)),
|
|
575
561
|
overlay.anchors.map((a) => {
|
|
576
|
-
const
|
|
562
|
+
const size2 = as + (a.hovered ? 2 : 0);
|
|
577
563
|
const fill = a.selected ? accentColor : import_vector3.VECTOR_OVERLAY_THEME.handleFill;
|
|
578
564
|
const key = `${a.ref.nodeId}:${a.ref.subpath}:${a.ref.index}`;
|
|
579
|
-
return a.type === "corner" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { "data-vector-anchor": a.ref.index, x: a.x -
|
|
565
|
+
return a.type === "corner" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { "data-vector-anchor": a.ref.index, x: a.x - size2 / 2, y: a.y - size2 / 2, width: size2, height: size2, fill, stroke: accentColor }, key) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { "data-vector-anchor": a.ref.index, cx: a.x, cy: a.y, r: size2 / 2, fill, stroke: accentColor }, key);
|
|
580
566
|
}),
|
|
581
567
|
overlay.cornerWidgets.map((w) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { "data-vector-corner-widget": w.ref.index, "data-corner-kind": w.kind, children: [
|
|
582
568
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: w.x, cy: w.y, r: import_vector3.VECTOR_OVERLAY_THEME.cornerWidgetRadius + (w.hovered ? 1 : 0), fill: import_vector3.VECTOR_OVERLAY_THEME.handleFill, stroke: accentColor }),
|
|
@@ -693,6 +679,10 @@ function VectorCanvas({
|
|
|
693
679
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M-3 0H3M0 -3V3", stroke: "#dc2626" })
|
|
694
680
|
] })
|
|
695
681
|
] }),
|
|
682
|
+
overlay.shading.map((sh) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { "data-vector-shading": sh.kind, children: [
|
|
683
|
+
sh.lines.map((d, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d, stroke: accentColor, strokeWidth: 1, opacity: 0.7 }, i)),
|
|
684
|
+
sh.points.map((p) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("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))
|
|
685
|
+
] }, `shading:${sh.nodeId}`)),
|
|
696
686
|
overlay.trackingPoints.map((t, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("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}`)),
|
|
697
687
|
overlay.typedEntry && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { "data-vector-typed-entry": "", transform: `translate(${overlay.typedEntry.x + 14} ${overlay.typedEntry.y + 18})`, children: [
|
|
698
688
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: -3, y: -12, width: overlay.typedEntry.text.length * 7 + 8, height: 17, rx: 3, fill: import_vector3.VECTOR_OVERLAY_THEME.handleFill, stroke: accentColor }),
|
|
@@ -828,8 +818,8 @@ function VectorCanvas({
|
|
|
828
818
|
}
|
|
829
819
|
|
|
830
820
|
// src/vector/VectorToolbar.tsx
|
|
831
|
-
var
|
|
832
|
-
var
|
|
821
|
+
var import_react10 = __toESM(require("react"), 1);
|
|
822
|
+
var import_vector15 = require("@pixerate/editor/vector");
|
|
833
823
|
|
|
834
824
|
// src/vector/VectorEffectsPanel.tsx
|
|
835
825
|
var import_vector4 = require("@pixerate/editor/vector");
|
|
@@ -1920,16 +1910,130 @@ function VectorConnectorPanel({ controller, className }) {
|
|
|
1920
1910
|
] });
|
|
1921
1911
|
}
|
|
1922
1912
|
|
|
1923
|
-
// src/vector/
|
|
1913
|
+
// src/vector/VectorShadingPanel.tsx
|
|
1914
|
+
var import_react8 = require("react");
|
|
1924
1915
|
var import_vector13 = require("@pixerate/editor/vector");
|
|
1925
1916
|
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
1917
|
+
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";
|
|
1918
|
+
var numberClass6 = "h-7 w-12 rounded border border-border bg-background px-1.5 text-xs";
|
|
1919
|
+
function VectorShadingPanel({ controller, className }) {
|
|
1920
|
+
const state = useVectorEditorState(controller);
|
|
1921
|
+
const [rows, setRows] = (0, import_react8.useState)(2);
|
|
1922
|
+
const [columns, setColumns] = (0, import_react8.useState)(2);
|
|
1923
|
+
const path = state.selection.map((id) => controller.getNode(id)).find((n) => n?.type === "path");
|
|
1924
|
+
if (!path) return null;
|
|
1925
|
+
const fill = path.fill;
|
|
1926
|
+
const sel = state.shadingPoint?.nodeId === path.id ? state.shadingPoint : null;
|
|
1927
|
+
const mesh = (0, import_vector13.isVectorMeshGradient)(fill) ? fill : null;
|
|
1928
|
+
const free = (0, import_vector13.isVectorFreeformGradient)(fill) ? fill : null;
|
|
1929
|
+
const color = sel ? mesh ? mesh.colors[sel.index] : free?.points[sel.index]?.color : void 0;
|
|
1930
|
+
const opacity = sel ? mesh ? mesh.opacities?.[sel.index] ?? 1 : free?.points[sel.index]?.opacity ?? 1 : 1;
|
|
1931
|
+
const spread = sel && free ? free.points[sel.index]?.spread ?? 0 : 0;
|
|
1932
|
+
const edit = (patch, merge) => {
|
|
1933
|
+
if (!sel) return;
|
|
1934
|
+
if (mesh) controller.setMeshPoint(path.id, sel.index, patch, merge ? { merge } : {});
|
|
1935
|
+
else controller.setFreeformPoint(path.id, sel.index, patch, merge ? { merge } : {});
|
|
1936
|
+
};
|
|
1937
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: `space-y-2 ${className ?? ""}`, "data-vector-shading-panel": path.id, "aria-label": "Mesh and freeform gradient", children: [
|
|
1938
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex flex-wrap items-center gap-1", children: [
|
|
1939
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "w-16 text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: "Shading" }),
|
|
1940
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("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))) }),
|
|
1941
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "text-xs text-muted-foreground", children: "\xD7" }),
|
|
1942
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("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))) }),
|
|
1943
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("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" }),
|
|
1944
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("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" })
|
|
1945
|
+
] }),
|
|
1946
|
+
(mesh || free) && !sel && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("p", { className: "text-[11px] text-muted-foreground", children: [
|
|
1947
|
+
"Gradient tool (G): drag points; ",
|
|
1948
|
+
free ? "click inside to add one, Alt-click to remove." : "pick one to colour it."
|
|
1949
|
+
] }),
|
|
1950
|
+
sel && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex flex-wrap items-center gap-2", "data-vector-shading-point": sel.index, children: [
|
|
1951
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "w-16 text-xs text-foreground", children: [
|
|
1952
|
+
"Point ",
|
|
1953
|
+
sel.index
|
|
1954
|
+
] }),
|
|
1955
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("input", { type: "color", "aria-label": "Point colour", className: "h-7 w-10 cursor-pointer rounded border border-border bg-background", value: (0, import_vector13.vectorPaintToHex)(color ?? "#000000") ?? "#000000", onChange: (e) => edit({ color: e.target.value }, "shading-color") }),
|
|
1956
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("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") }),
|
|
1957
|
+
free && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
|
|
1958
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("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") }),
|
|
1959
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { type: "button", className: buttonClass10, "aria-label": "Remove point", onClick: () => controller.removeFreeformPoint(path.id, sel.index), children: "Remove" })
|
|
1960
|
+
] })
|
|
1961
|
+
] })
|
|
1962
|
+
] });
|
|
1963
|
+
}
|
|
1964
|
+
|
|
1965
|
+
// src/vector/VectorTracePanel.tsx
|
|
1966
|
+
var import_react9 = require("react");
|
|
1967
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
1968
|
+
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";
|
|
1926
1969
|
var selectClass8 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
|
|
1927
|
-
var
|
|
1928
|
-
|
|
1970
|
+
var numberClass7 = "h-7 w-12 rounded border border-border bg-background px-1.5 text-xs";
|
|
1971
|
+
async function filePixels(file, max = 1e6) {
|
|
1972
|
+
const bitmap = await createImageBitmap(file);
|
|
1973
|
+
const k = Math.min(1, Math.sqrt(max / (bitmap.width * bitmap.height)));
|
|
1974
|
+
const width = Math.max(1, Math.round(bitmap.width * k));
|
|
1975
|
+
const height = Math.max(1, Math.round(bitmap.height * k));
|
|
1976
|
+
const canvas = document.createElement("canvas");
|
|
1977
|
+
canvas.width = width;
|
|
1978
|
+
canvas.height = height;
|
|
1979
|
+
const ctx = canvas.getContext("2d");
|
|
1980
|
+
ctx.drawImage(bitmap, 0, 0, width, height);
|
|
1981
|
+
return { width, height, data: ctx.getImageData(0, 0, width, height).data };
|
|
1982
|
+
}
|
|
1983
|
+
function VectorTracePanel({ controller, className }) {
|
|
1984
|
+
const input = (0, import_react9.useRef)(null);
|
|
1985
|
+
const [mode, setMode] = (0, import_react9.useState)("threshold");
|
|
1986
|
+
const [colors, setColors] = (0, import_react9.useState)(6);
|
|
1987
|
+
const [message, setMessage] = (0, import_react9.useState)(null);
|
|
1988
|
+
const trace = async (file) => {
|
|
1989
|
+
try {
|
|
1990
|
+
const pixels = await filePixels(file);
|
|
1991
|
+
const doc = controller.getState().document;
|
|
1992
|
+
const width = Math.min(doc.width, pixels.width);
|
|
1993
|
+
const r = controller.traceImage(pixels, { mode, colors, width, name: file.name.replace(/\.[^.]+$/, "") || "Image Trace" });
|
|
1994
|
+
setMessage(r.ok ? null : r.message);
|
|
1995
|
+
} catch {
|
|
1996
|
+
setMessage("That file could not be read as an image.");
|
|
1997
|
+
}
|
|
1998
|
+
};
|
|
1999
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: `space-y-1 ${className ?? ""}`, "data-vector-trace-panel": "", "aria-label": "Image trace", children: [
|
|
2000
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-1", children: [
|
|
2001
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "w-16 text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: "Trace" }),
|
|
2002
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("select", { "aria-label": "Trace mode", className: selectClass8, value: mode, onChange: (e) => setMode(e.target.value), children: [
|
|
2003
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("option", { value: "threshold", children: "Black & white" }),
|
|
2004
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("option", { value: "color", children: "Colours" })
|
|
2005
|
+
] }),
|
|
2006
|
+
mode === "color" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("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))) }),
|
|
2007
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("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" }),
|
|
2008
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2009
|
+
"input",
|
|
2010
|
+
{
|
|
2011
|
+
ref: input,
|
|
2012
|
+
type: "file",
|
|
2013
|
+
accept: "image/*",
|
|
2014
|
+
hidden: true,
|
|
2015
|
+
onChange: (e) => {
|
|
2016
|
+
const file = e.target.files?.[0];
|
|
2017
|
+
e.target.value = "";
|
|
2018
|
+
if (file) void trace(file);
|
|
2019
|
+
}
|
|
2020
|
+
}
|
|
2021
|
+
)
|
|
2022
|
+
] }),
|
|
2023
|
+
message && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-[11px] text-red-600", children: message })
|
|
2024
|
+
] });
|
|
2025
|
+
}
|
|
2026
|
+
|
|
2027
|
+
// src/vector/VectorGradientEditor.tsx
|
|
2028
|
+
var import_vector14 = require("@pixerate/editor/vector");
|
|
2029
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
2030
|
+
var selectClass9 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
|
|
2031
|
+
var numberClass8 = "h-7 w-14 rounded border border-border bg-background px-1.5 text-xs";
|
|
2032
|
+
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";
|
|
1929
2033
|
var percent = (v) => Math.round(v * 1e3) / 10;
|
|
1930
2034
|
function VectorGradientEditor({ controller, className }) {
|
|
1931
2035
|
const state = useVectorEditorState(controller);
|
|
1932
|
-
const ed = (0,
|
|
2036
|
+
const ed = (0, import_vector14.getVectorGradientEditor)(state);
|
|
1933
2037
|
if (!ed) return null;
|
|
1934
2038
|
const g = ed.gradient;
|
|
1935
2039
|
const sel = ed.selected;
|
|
@@ -1937,26 +2041,26 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
1937
2041
|
const setType = (value) => {
|
|
1938
2042
|
if (value === "solid") {
|
|
1939
2043
|
const first = ed.stops[0];
|
|
1940
|
-
controller.setStyle({ [ed.target]: (0,
|
|
2044
|
+
controller.setStyle({ [ed.target]: (0, import_vector14.vectorPaintToHex)(first?.color) ?? first?.color ?? "#000000" }, void 0, { label: "Solid Colour" });
|
|
1941
2045
|
} else controller.setGradient({ type: value });
|
|
1942
2046
|
};
|
|
1943
|
-
return /* @__PURE__ */ (0,
|
|
1944
|
-
/* @__PURE__ */ (0,
|
|
1945
|
-
/* @__PURE__ */ (0,
|
|
1946
|
-
["fill", "stroke"].map((t) => /* @__PURE__ */ (0,
|
|
1947
|
-
ed.mixed && /* @__PURE__ */ (0,
|
|
2047
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: `space-y-1.5 ${className ?? ""}`, "data-vector-gradient-editor": ed.target, "aria-label": "Gradient", children: [
|
|
2048
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex items-center gap-1.5", children: [
|
|
2049
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "w-16 text-xs text-foreground", children: "Gradient" }),
|
|
2050
|
+
["fill", "stroke"].map((t) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", className: buttonClass12, "aria-pressed": ed.target === t, onClick: () => controller.setGradientTarget(t), children: t === "fill" ? "Fill" : "Stroke" }, t)),
|
|
2051
|
+
ed.mixed && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
|
|
1948
2052
|
] }),
|
|
1949
|
-
/* @__PURE__ */ (0,
|
|
1950
|
-
/* @__PURE__ */ (0,
|
|
1951
|
-
/* @__PURE__ */ (0,
|
|
1952
|
-
/* @__PURE__ */ (0,
|
|
1953
|
-
|
|
2053
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex items-center gap-1.5", children: [
|
|
2054
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "w-16 text-xs text-foreground", children: "Type" }),
|
|
2055
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("select", { "aria-label": "Gradient type", className: selectClass9, value: ed.type ?? "solid", onChange: (e) => setType(e.target.value), children: [
|
|
2056
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("option", { value: "solid", children: "Solid" }),
|
|
2057
|
+
import_vector14.VECTOR_GRADIENT_TYPES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("option", { value: t, children: import_vector14.VECTOR_GRADIENT_TYPE_LABELS[t] }, t))
|
|
1954
2058
|
] }),
|
|
1955
|
-
/* @__PURE__ */ (0,
|
|
2059
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", className: buttonClass12, "aria-label": "Reverse gradient", title: "Reverse gradient", disabled: !g, onClick: () => controller.reverseGradient(), children: "\u21C4" })
|
|
1956
2060
|
] }),
|
|
1957
|
-
g && /* @__PURE__ */ (0,
|
|
1958
|
-
/* @__PURE__ */ (0,
|
|
1959
|
-
/* @__PURE__ */ (0,
|
|
2061
|
+
g && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
|
|
2062
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "relative h-8", "data-vector-gradient-ramp": "", children: [
|
|
2063
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
1960
2064
|
"div",
|
|
1961
2065
|
{
|
|
1962
2066
|
role: "button",
|
|
@@ -1970,7 +2074,7 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
1970
2074
|
}
|
|
1971
2075
|
}
|
|
1972
2076
|
),
|
|
1973
|
-
ed.stops.map((s) => /* @__PURE__ */ (0,
|
|
2077
|
+
ed.stops.map((s) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
1974
2078
|
"button",
|
|
1975
2079
|
{
|
|
1976
2080
|
type: "button",
|
|
@@ -1984,21 +2088,21 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
1984
2088
|
s.index
|
|
1985
2089
|
))
|
|
1986
2090
|
] }),
|
|
1987
|
-
sel && /* @__PURE__ */ (0,
|
|
1988
|
-
/* @__PURE__ */ (0,
|
|
2091
|
+
sel && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex flex-wrap items-center gap-1.5", "data-vector-gradient-selected-stop": sel.index, children: [
|
|
2092
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
1989
2093
|
"input",
|
|
1990
2094
|
{
|
|
1991
2095
|
type: "color",
|
|
1992
2096
|
"aria-label": "Stop colour",
|
|
1993
2097
|
className: "h-7 w-9 cursor-pointer rounded border border-border bg-background",
|
|
1994
|
-
value: (0,
|
|
2098
|
+
value: (0, import_vector14.vectorPaintToHex)(sel.color) ?? "#000000",
|
|
1995
2099
|
onChange: (e) => controller.setGradientStop(sel.index, { color: e.target.value }, { merge: `gradient-stop-color-${sel.index}` }),
|
|
1996
2100
|
onBlur: end
|
|
1997
2101
|
}
|
|
1998
2102
|
),
|
|
1999
|
-
/* @__PURE__ */ (0,
|
|
2103
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
|
|
2000
2104
|
"Opacity",
|
|
2001
|
-
/* @__PURE__ */ (0,
|
|
2105
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2002
2106
|
"input",
|
|
2003
2107
|
{
|
|
2004
2108
|
type: "number",
|
|
@@ -2006,7 +2110,7 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
2006
2110
|
max: 100,
|
|
2007
2111
|
step: 5,
|
|
2008
2112
|
"aria-label": "Stop opacity",
|
|
2009
|
-
className:
|
|
2113
|
+
className: numberClass8,
|
|
2010
2114
|
value: percent(sel.opacity ?? 1),
|
|
2011
2115
|
onChange: (e) => {
|
|
2012
2116
|
const n = parseFloat(e.target.value);
|
|
@@ -2015,9 +2119,9 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
2015
2119
|
}
|
|
2016
2120
|
)
|
|
2017
2121
|
] }),
|
|
2018
|
-
/* @__PURE__ */ (0,
|
|
2122
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
|
|
2019
2123
|
"At",
|
|
2020
|
-
/* @__PURE__ */ (0,
|
|
2124
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2021
2125
|
"input",
|
|
2022
2126
|
{
|
|
2023
2127
|
type: "number",
|
|
@@ -2025,7 +2129,7 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
2025
2129
|
max: 100,
|
|
2026
2130
|
step: 1,
|
|
2027
2131
|
"aria-label": "Stop position",
|
|
2028
|
-
className:
|
|
2132
|
+
className: numberClass8,
|
|
2029
2133
|
value: percent(sel.offset),
|
|
2030
2134
|
onChange: (e) => {
|
|
2031
2135
|
const n = parseFloat(e.target.value);
|
|
@@ -2034,9 +2138,9 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
2034
2138
|
}
|
|
2035
2139
|
)
|
|
2036
2140
|
] }),
|
|
2037
|
-
!sel.last && /* @__PURE__ */ (0,
|
|
2141
|
+
!sel.last && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
|
|
2038
2142
|
"Midpoint",
|
|
2039
|
-
/* @__PURE__ */ (0,
|
|
2143
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2040
2144
|
"input",
|
|
2041
2145
|
{
|
|
2042
2146
|
type: "number",
|
|
@@ -2044,7 +2148,7 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
2044
2148
|
max: 95,
|
|
2045
2149
|
step: 1,
|
|
2046
2150
|
"aria-label": "Stop midpoint",
|
|
2047
|
-
className:
|
|
2151
|
+
className: numberClass8,
|
|
2048
2152
|
value: percent(sel.midpoint ?? 0.5),
|
|
2049
2153
|
onChange: (e) => {
|
|
2050
2154
|
const n = parseFloat(e.target.value);
|
|
@@ -2053,15 +2157,15 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
2053
2157
|
}
|
|
2054
2158
|
)
|
|
2055
2159
|
] }),
|
|
2056
|
-
/* @__PURE__ */ (0,
|
|
2160
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", className: buttonClass12, "aria-label": "Delete stop", title: "Delete stop", disabled: ed.stops.length <= 2, onClick: () => controller.removeGradientStop(sel.index), children: "\u2715" })
|
|
2057
2161
|
] }),
|
|
2058
|
-
/* @__PURE__ */ (0,
|
|
2059
|
-
/* @__PURE__ */ (0,
|
|
2060
|
-
/* @__PURE__ */ (0,
|
|
2162
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
|
|
2163
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "w-16", children: "Colours" }),
|
|
2164
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("select", { "aria-label": "Gradient interpolation", className: selectClass9, value: ed.interpolation, onChange: (e) => controller.setGradient({ interpolation: e.target.value }), children: import_vector14.VECTOR_GRADIENT_INTERPOLATIONS.map((i) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("option", { value: i, children: import_vector14.VECTOR_GRADIENT_INTERPOLATION_LABELS[i] }, i)) })
|
|
2061
2165
|
] }),
|
|
2062
|
-
/* @__PURE__ */ (0,
|
|
2063
|
-
/* @__PURE__ */ (0,
|
|
2064
|
-
/* @__PURE__ */ (0,
|
|
2166
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
|
|
2167
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "w-16", children: "Smooth" }),
|
|
2168
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2065
2169
|
"input",
|
|
2066
2170
|
{
|
|
2067
2171
|
type: "range",
|
|
@@ -2077,26 +2181,26 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
2077
2181
|
}
|
|
2078
2182
|
)
|
|
2079
2183
|
] }),
|
|
2080
|
-
/* @__PURE__ */ (0,
|
|
2081
|
-
/* @__PURE__ */ (0,
|
|
2082
|
-
/* @__PURE__ */ (0,
|
|
2184
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
|
|
2185
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "w-16", children: "Spread" }),
|
|
2186
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("select", { "aria-label": "Gradient spread", className: selectClass9, value: ed.spread, onChange: (e) => controller.setGradient({ spread: e.target.value }), children: import_vector14.VECTOR_GRADIENT_SPREADS.map((sp) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("option", { value: sp, children: import_vector14.VECTOR_GRADIENT_SPREAD_LABELS[sp] }, sp)) })
|
|
2083
2187
|
] }),
|
|
2084
|
-
ed.target === "stroke" && /* @__PURE__ */ (0,
|
|
2085
|
-
/* @__PURE__ */ (0,
|
|
2086
|
-
/* @__PURE__ */ (0,
|
|
2188
|
+
ed.target === "stroke" && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
|
|
2189
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "w-16", children: "On stroke" }),
|
|
2190
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("select", { "aria-label": "Gradient on stroke", className: selectClass9, value: ed.strokeMode, onChange: (e) => controller.setGradient({ strokeMode: e.target.value }), children: import_vector14.VECTOR_STROKE_GRADIENT_MODES.map((m) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("option", { value: m, children: import_vector14.VECTOR_STROKE_GRADIENT_MODE_LABELS[m] }, m)) })
|
|
2087
2191
|
] })
|
|
2088
2192
|
] })
|
|
2089
2193
|
] });
|
|
2090
2194
|
}
|
|
2091
2195
|
|
|
2092
2196
|
// src/vector/VectorToolbar.tsx
|
|
2093
|
-
var
|
|
2197
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
2094
2198
|
var VECTOR_SNAP_TITLE = "Snap to objects";
|
|
2095
2199
|
var modKeyLabel = () => typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform ?? "") ? "\u2318" : "Ctrl";
|
|
2096
|
-
var
|
|
2200
|
+
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";
|
|
2097
2201
|
function VectorToolbar({
|
|
2098
2202
|
controller,
|
|
2099
|
-
tools =
|
|
2203
|
+
tools = import_vector15.VECTOR_TOOLS,
|
|
2100
2204
|
showHistory = true,
|
|
2101
2205
|
showArrange = true,
|
|
2102
2206
|
showSnap = true,
|
|
@@ -2110,36 +2214,36 @@ function VectorToolbar({
|
|
|
2110
2214
|
const points = state.tool === "direct" ? state.selectedAnchors.length : 0;
|
|
2111
2215
|
const editing = state.editing;
|
|
2112
2216
|
const groupSelected = state.selection.some((id) => controller.getNode(id)?.type === "group");
|
|
2113
|
-
const maskSelected = state.selection.some((id) => (0,
|
|
2114
|
-
const [live, setLive] =
|
|
2217
|
+
const maskSelected = state.selection.some((id) => (0, import_vector15.isVectorMaskGroup)(controller.getNode(id)));
|
|
2218
|
+
const [live, setLive] = import_react10.default.useState(false);
|
|
2115
2219
|
const booleanDisabled = live ? count < 1 : count < 2;
|
|
2116
|
-
const act = (label, title, run, disabled = false) => /* @__PURE__ */ (0,
|
|
2117
|
-
return /* @__PURE__ */ (0,
|
|
2118
|
-
/* @__PURE__ */ (0,
|
|
2220
|
+
const act = (label, title, run, disabled = false) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("button", { type: "button", className: buttonClass13, title, "aria-label": title, disabled, onClick: () => void run(), children: label }, label);
|
|
2221
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { role: "toolbar", "aria-label": "Vector tools", className: `flex flex-wrap items-center gap-1 ${className ?? ""}`, children: [
|
|
2222
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "flex items-center gap-1", role: "group", "aria-label": "Tools", children: tools.map((tool) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2119
2223
|
"button",
|
|
2120
2224
|
{
|
|
2121
2225
|
type: "button",
|
|
2122
2226
|
"data-vector-tool": tool,
|
|
2123
|
-
className:
|
|
2227
|
+
className: buttonClass13,
|
|
2124
2228
|
"aria-pressed": state.tool === tool,
|
|
2125
|
-
title: `${(0,
|
|
2126
|
-
"aria-label": (0,
|
|
2229
|
+
title: `${(0, import_vector15.vectorToolLabel)(tool)} (${import_vector15.VECTOR_TOOL_SHORTCUTS[tool]})`,
|
|
2230
|
+
"aria-label": (0, import_vector15.vectorToolLabel)(tool),
|
|
2127
2231
|
onClick: () => controller.setTool(tool),
|
|
2128
|
-
children:
|
|
2232
|
+
children: import_vector15.VECTOR_TOOL_GLYPHS[tool]
|
|
2129
2233
|
},
|
|
2130
2234
|
tool
|
|
2131
2235
|
)) }),
|
|
2132
|
-
showHistory && /* @__PURE__ */ (0,
|
|
2236
|
+
showHistory && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "History", children: [
|
|
2133
2237
|
act("\u21B6", state.canUndo ? `Undo ${state.undoLabel}` : "Undo", () => controller.undo(), !state.canUndo),
|
|
2134
2238
|
act("\u21B7", state.canRedo ? `Redo ${state.redoLabel}` : "Redo", () => controller.redo(), !state.canRedo)
|
|
2135
2239
|
] }),
|
|
2136
|
-
showSnap && /* @__PURE__ */ (0,
|
|
2137
|
-
/* @__PURE__ */ (0,
|
|
2240
|
+
showSnap && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Snapping", children: [
|
|
2241
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2138
2242
|
"button",
|
|
2139
2243
|
{
|
|
2140
2244
|
type: "button",
|
|
2141
2245
|
"data-vector-snap": "",
|
|
2142
|
-
className:
|
|
2246
|
+
className: buttonClass13,
|
|
2143
2247
|
"aria-pressed": state.snap.objects,
|
|
2144
2248
|
title: `${VECTOR_SNAP_TITLE} (hold ${modKeyLabel()} to bypass)`,
|
|
2145
2249
|
"aria-label": VECTOR_SNAP_TITLE,
|
|
@@ -2147,18 +2251,18 @@ function VectorToolbar({
|
|
|
2147
2251
|
children: "Snap"
|
|
2148
2252
|
}
|
|
2149
2253
|
),
|
|
2150
|
-
/* @__PURE__ */ (0,
|
|
2254
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
2151
2255
|
"select",
|
|
2152
2256
|
{
|
|
2153
2257
|
"data-vector-polar": "",
|
|
2154
2258
|
"aria-label": "Polar tracking increment",
|
|
2155
2259
|
title: "Polar tracking: the angles drawn points snap to from where they leave",
|
|
2156
2260
|
className: "h-8 rounded-md border border-border bg-background px-1 text-xs",
|
|
2157
|
-
value: String((0,
|
|
2158
|
-
onChange: (e) => controller.setSnap({ angles: (0,
|
|
2261
|
+
value: String((0, import_vector15.vectorPolarIncrementOf)(state.snap.angles) ?? ""),
|
|
2262
|
+
onChange: (e) => controller.setSnap({ angles: (0, import_vector15.vectorPolarAngles)(Number(e.target.value)) }),
|
|
2159
2263
|
children: [
|
|
2160
|
-
(0,
|
|
2161
|
-
[90, 45, 30, 15].map((n) => /* @__PURE__ */ (0,
|
|
2264
|
+
(0, import_vector15.vectorPolarIncrementOf)(state.snap.angles) === null && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("option", { value: "", children: "Custom" }),
|
|
2265
|
+
[90, 45, 30, 15].map((n) => /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("option", { value: n, children: [
|
|
2162
2266
|
n,
|
|
2163
2267
|
"\xB0"
|
|
2164
2268
|
] }, n))
|
|
@@ -2166,13 +2270,13 @@ function VectorToolbar({
|
|
|
2166
2270
|
}
|
|
2167
2271
|
)
|
|
2168
2272
|
] }),
|
|
2169
|
-
showPathEdit && /* @__PURE__ */ (0,
|
|
2170
|
-
|
|
2273
|
+
showPathEdit && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Path editing", children: [
|
|
2274
|
+
import_vector15.VECTOR_PATH_ACTIONS.map((a) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2171
2275
|
"button",
|
|
2172
2276
|
{
|
|
2173
2277
|
type: "button",
|
|
2174
2278
|
"data-vector-path-action": a.id,
|
|
2175
|
-
className:
|
|
2279
|
+
className: buttonClass13,
|
|
2176
2280
|
title: a.shortcut ? `${a.title} (${modKeyLabel()}+${a.shortcut})` : a.title,
|
|
2177
2281
|
"aria-label": a.title,
|
|
2178
2282
|
disabled: !a.enabled(count, points),
|
|
@@ -2181,12 +2285,12 @@ function VectorToolbar({
|
|
|
2181
2285
|
},
|
|
2182
2286
|
a.id
|
|
2183
2287
|
)),
|
|
2184
|
-
|
|
2288
|
+
import_vector15.VECTOR_EDIT_TOGGLES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2185
2289
|
"button",
|
|
2186
2290
|
{
|
|
2187
2291
|
type: "button",
|
|
2188
2292
|
"data-vector-edit": t.key,
|
|
2189
|
-
className:
|
|
2293
|
+
className: buttonClass13,
|
|
2190
2294
|
"aria-pressed": editing[t.key],
|
|
2191
2295
|
title: t.title,
|
|
2192
2296
|
"aria-label": t.title,
|
|
@@ -2195,9 +2299,9 @@ function VectorToolbar({
|
|
|
2195
2299
|
},
|
|
2196
2300
|
t.key
|
|
2197
2301
|
)),
|
|
2198
|
-
state.tool === "pencil" && /* @__PURE__ */ (0,
|
|
2302
|
+
state.tool === "pencil" && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
|
|
2199
2303
|
"Smoothing",
|
|
2200
|
-
/* @__PURE__ */ (0,
|
|
2304
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2201
2305
|
"input",
|
|
2202
2306
|
{
|
|
2203
2307
|
type: "range",
|
|
@@ -2211,9 +2315,9 @@ function VectorToolbar({
|
|
|
2211
2315
|
}
|
|
2212
2316
|
)
|
|
2213
2317
|
] }),
|
|
2214
|
-
state.tool === "eraser" && /* @__PURE__ */ (0,
|
|
2318
|
+
state.tool === "eraser" && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
|
|
2215
2319
|
"Size",
|
|
2216
|
-
/* @__PURE__ */ (0,
|
|
2320
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2217
2321
|
"input",
|
|
2218
2322
|
{
|
|
2219
2323
|
type: "number",
|
|
@@ -2229,26 +2333,26 @@ function VectorToolbar({
|
|
|
2229
2333
|
}
|
|
2230
2334
|
)
|
|
2231
2335
|
] }),
|
|
2232
|
-
state.tool === "paintBucket" && /* @__PURE__ */ (0,
|
|
2336
|
+
state.tool === "paintBucket" && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2233
2337
|
"button",
|
|
2234
2338
|
{
|
|
2235
2339
|
type: "button",
|
|
2236
2340
|
"data-vector-fill-gap": "",
|
|
2237
|
-
className:
|
|
2341
|
+
className: buttonClass13,
|
|
2238
2342
|
"aria-pressed": editing.fillGap > 0,
|
|
2239
2343
|
title: "Close small gaps between path ends when filling",
|
|
2240
2344
|
"aria-label": "Close gaps",
|
|
2241
|
-
onClick: () => controller.setEditing({ fillGap: editing.fillGap > 0 ? 0 :
|
|
2345
|
+
onClick: () => controller.setEditing({ fillGap: editing.fillGap > 0 ? 0 : import_vector15.VECTOR_FILL_GAP }),
|
|
2242
2346
|
children: "Gaps"
|
|
2243
2347
|
}
|
|
2244
2348
|
)
|
|
2245
2349
|
] }),
|
|
2246
|
-
showDistort && /* @__PURE__ */ (0,
|
|
2350
|
+
showDistort && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Distort", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2247
2351
|
"button",
|
|
2248
2352
|
{
|
|
2249
2353
|
type: "button",
|
|
2250
2354
|
"data-vector-free-distort": "",
|
|
2251
|
-
className:
|
|
2355
|
+
className: buttonClass13,
|
|
2252
2356
|
"aria-pressed": state.freeDistort,
|
|
2253
2357
|
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)`,
|
|
2254
2358
|
"aria-label": "Free Distort",
|
|
@@ -2256,15 +2360,15 @@ function VectorToolbar({
|
|
|
2256
2360
|
children: "Distort"
|
|
2257
2361
|
}
|
|
2258
2362
|
) }),
|
|
2259
|
-
showBlend && /* @__PURE__ */ (0,
|
|
2260
|
-
showArrange && /* @__PURE__ */ (0,
|
|
2261
|
-
/* @__PURE__ */ (0,
|
|
2262
|
-
/* @__PURE__ */ (0,
|
|
2363
|
+
showBlend && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(VectorBlendControls, { controller }),
|
|
2364
|
+
showArrange && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
|
|
2365
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Combine", children: [
|
|
2366
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2263
2367
|
"button",
|
|
2264
2368
|
{
|
|
2265
2369
|
type: "button",
|
|
2266
2370
|
"data-vector-live-boolean": "",
|
|
2267
|
-
className:
|
|
2371
|
+
className: buttonClass13,
|
|
2268
2372
|
"aria-pressed": live,
|
|
2269
2373
|
title: "Live booleans: keep the shapes editable",
|
|
2270
2374
|
"aria-label": "Live booleans",
|
|
@@ -2281,12 +2385,12 @@ function VectorToolbar({
|
|
|
2281
2385
|
act("\u25D0", `Make Clipping Mask (${modKeyLabel()}+7)`, () => controller.makeMask("clip"), count < 2),
|
|
2282
2386
|
act("\u25D1", `Release Mask (${modKeyLabel()}+Alt+7)`, () => controller.releaseMask(), !maskSelected)
|
|
2283
2387
|
] }),
|
|
2284
|
-
/* @__PURE__ */ (0,
|
|
2388
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Pathfinder", children: import_vector15.VECTOR_PATHFINDER_ACTIONS.map((a) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2285
2389
|
"button",
|
|
2286
2390
|
{
|
|
2287
2391
|
type: "button",
|
|
2288
2392
|
"data-vector-pathfinder": a.id,
|
|
2289
|
-
className:
|
|
2393
|
+
className: buttonClass13,
|
|
2290
2394
|
title: a.title,
|
|
2291
2395
|
"aria-label": a.title,
|
|
2292
2396
|
disabled: count < a.min,
|
|
@@ -2295,7 +2399,7 @@ function VectorToolbar({
|
|
|
2295
2399
|
},
|
|
2296
2400
|
a.id
|
|
2297
2401
|
)) }),
|
|
2298
|
-
/* @__PURE__ */ (0,
|
|
2402
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Arrange", children: [
|
|
2299
2403
|
act("\u21C8", "Bring to Front", () => controller.arrange("front"), count < 1),
|
|
2300
2404
|
act("\u21CA", "Send to Back", () => controller.arrange("back"), count < 1),
|
|
2301
2405
|
act("\u21E4", "Align Left", () => controller.align("left"), count < 1),
|
|
@@ -2310,22 +2414,22 @@ function VectorToolbar({
|
|
|
2310
2414
|
}
|
|
2311
2415
|
function VectorBlendControls({ controller }) {
|
|
2312
2416
|
const state = useVectorEditorState(controller);
|
|
2313
|
-
const blend = (0,
|
|
2417
|
+
const blend = (0, import_vector15.getVectorBlendControls)(state);
|
|
2314
2418
|
const o = blend.options;
|
|
2315
2419
|
const selected = blend.ids.length > 0;
|
|
2316
|
-
return /* @__PURE__ */ (0,
|
|
2317
|
-
/* @__PURE__ */ (0,
|
|
2318
|
-
/* @__PURE__ */ (0,
|
|
2420
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Blend", "data-vector-blend-controls": "", children: [
|
|
2421
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("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" }),
|
|
2422
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2319
2423
|
"select",
|
|
2320
2424
|
{
|
|
2321
2425
|
"aria-label": "Blend spacing",
|
|
2322
2426
|
className: "h-7 rounded border border-border bg-background px-1 text-xs",
|
|
2323
2427
|
value: o.spacing,
|
|
2324
|
-
onChange: (e) => (0,
|
|
2325
|
-
children:
|
|
2428
|
+
onChange: (e) => (0, import_vector15.applyVectorBlendOptions)(controller, { spacing: e.target.value }),
|
|
2429
|
+
children: import_vector15.VECTOR_BLEND_SPACINGS.map((sp) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("option", { value: sp, children: import_vector15.VECTOR_BLEND_SPACING_LABELS[sp] }, sp))
|
|
2326
2430
|
}
|
|
2327
2431
|
),
|
|
2328
|
-
o.spacing !== "smoothColor" && /* @__PURE__ */ (0,
|
|
2432
|
+
o.spacing !== "smoothColor" && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2329
2433
|
"input",
|
|
2330
2434
|
{
|
|
2331
2435
|
type: "number",
|
|
@@ -2337,29 +2441,29 @@ function VectorBlendControls({ controller }) {
|
|
|
2337
2441
|
onChange: (e) => {
|
|
2338
2442
|
const n = parseFloat(e.target.value);
|
|
2339
2443
|
if (!Number.isFinite(n) || n <= 0) return;
|
|
2340
|
-
(0,
|
|
2444
|
+
(0, import_vector15.applyVectorBlendOptions)(controller, o.spacing === "steps" ? { steps: n } : { distance: n });
|
|
2341
2445
|
}
|
|
2342
2446
|
}
|
|
2343
2447
|
),
|
|
2344
|
-
/* @__PURE__ */ (0,
|
|
2448
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2345
2449
|
"button",
|
|
2346
2450
|
{
|
|
2347
2451
|
type: "button",
|
|
2348
2452
|
"data-vector-blend-action": "orient",
|
|
2349
|
-
className:
|
|
2453
|
+
className: buttonClass13,
|
|
2350
2454
|
"aria-pressed": o.orientToSpine,
|
|
2351
2455
|
title: "Orient steps to the spine",
|
|
2352
2456
|
"aria-label": "Orient to spine",
|
|
2353
|
-
onClick: () => (0,
|
|
2457
|
+
onClick: () => (0, import_vector15.applyVectorBlendOptions)(controller, { orientToSpine: !o.orientToSpine }),
|
|
2354
2458
|
children: "\u293F"
|
|
2355
2459
|
}
|
|
2356
2460
|
),
|
|
2357
|
-
/* @__PURE__ */ (0,
|
|
2461
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2358
2462
|
"button",
|
|
2359
2463
|
{
|
|
2360
2464
|
type: "button",
|
|
2361
2465
|
"data-vector-blend-action": "spine",
|
|
2362
|
-
className:
|
|
2466
|
+
className: buttonClass13,
|
|
2363
2467
|
title: "Replace Spine (select a blend and a path)",
|
|
2364
2468
|
"aria-label": "Replace Spine",
|
|
2365
2469
|
disabled: !blend.canReplaceSpine,
|
|
@@ -2367,15 +2471,15 @@ function VectorBlendControls({ controller }) {
|
|
|
2367
2471
|
children: "\u2307"
|
|
2368
2472
|
}
|
|
2369
2473
|
),
|
|
2370
|
-
/* @__PURE__ */ (0,
|
|
2371
|
-
/* @__PURE__ */ (0,
|
|
2474
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("button", { type: "button", "data-vector-blend-action": "expand", className: buttonClass13, title: "Expand Blend", "aria-label": "Expand Blend", disabled: !selected, onClick: () => controller.expandBlend(), children: "\u29C9" }),
|
|
2475
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("button", { type: "button", "data-vector-blend-action": "release", className: buttonClass13, title: "Release Blend", "aria-label": "Release Blend", disabled: !selected, onClick: () => controller.releaseBlend(), children: "\u29C4" })
|
|
2372
2476
|
] });
|
|
2373
2477
|
}
|
|
2374
|
-
var
|
|
2375
|
-
var
|
|
2478
|
+
var selectClass10 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
|
|
2479
|
+
var numberClass9 = "h-7 w-16 rounded border border-border bg-background px-1.5 text-xs";
|
|
2376
2480
|
function VectorStylePanel({ controller, className }) {
|
|
2377
2481
|
const state = useVectorEditorState(controller);
|
|
2378
|
-
const style = (0,
|
|
2482
|
+
const style = (0, import_vector15.getVectorSelectionStyle)(state);
|
|
2379
2483
|
const d = state.defaults;
|
|
2380
2484
|
const source = style ?? {
|
|
2381
2485
|
fill: d.fill ?? null,
|
|
@@ -2388,7 +2492,7 @@ function VectorStylePanel({ controller, className }) {
|
|
|
2388
2492
|
arrowAlign: d.arrowAlign ?? "extend",
|
|
2389
2493
|
startArrowScale: d.startArrowScale ?? 1,
|
|
2390
2494
|
endArrowScale: d.endArrowScale ?? 1,
|
|
2391
|
-
widthProfile: (0,
|
|
2495
|
+
widthProfile: (0, import_vector15.widthProfilePresetId)(d.strokeWidthProfile),
|
|
2392
2496
|
cornerRadius: d.cornerRadius,
|
|
2393
2497
|
cornerKind: d.cornerKind ?? "round",
|
|
2394
2498
|
cornerSmoothing: 0,
|
|
@@ -2402,19 +2506,19 @@ function VectorStylePanel({ controller, className }) {
|
|
|
2402
2506
|
};
|
|
2403
2507
|
const setProfile = (id) => {
|
|
2404
2508
|
if (style) controller.setWidthProfile(id);
|
|
2405
|
-
else controller.setDefaults({ strokeWidthProfile: (0,
|
|
2509
|
+
else controller.setDefaults({ strokeWidthProfile: (0, import_vector15.widthProfilePreset)(id)?.length ? (0, import_vector15.widthProfilePreset)(id) : null });
|
|
2406
2510
|
};
|
|
2407
|
-
const mixed = (key) => style?.mixed[key] ? /* @__PURE__ */ (0,
|
|
2511
|
+
const mixed = (key) => style?.mixed[key] ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" }) : null;
|
|
2408
2512
|
const arrowRow = (end, label) => {
|
|
2409
2513
|
const key = end === "start" ? "startArrow" : "endArrow";
|
|
2410
2514
|
const scaleKey = end === "start" ? "startArrowScale" : "endArrowScale";
|
|
2411
|
-
return /* @__PURE__ */ (0,
|
|
2412
|
-
/* @__PURE__ */ (0,
|
|
2413
|
-
/* @__PURE__ */ (0,
|
|
2414
|
-
/* @__PURE__ */ (0,
|
|
2415
|
-
|
|
2515
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
|
|
2516
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "w-16 text-xs text-foreground", children: label }),
|
|
2517
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("select", { "aria-label": `${label} arrowhead`, className: selectClass10, value: source[key] ?? "", onChange: (e) => apply({ [key]: e.target.value || null }), children: [
|
|
2518
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("option", { value: "", children: "None" }),
|
|
2519
|
+
import_vector15.VECTOR_ARROWHEADS.map((h) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("option", { value: h, children: import_vector15.VECTOR_ARROWHEAD_LABELS[h] }, h))
|
|
2416
2520
|
] }),
|
|
2417
|
-
/* @__PURE__ */ (0,
|
|
2521
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2418
2522
|
"input",
|
|
2419
2523
|
{
|
|
2420
2524
|
type: "number",
|
|
@@ -2422,7 +2526,7 @@ function VectorStylePanel({ controller, className }) {
|
|
|
2422
2526
|
step: 0.25,
|
|
2423
2527
|
"aria-label": `${label} arrowhead scale`,
|
|
2424
2528
|
title: "Scale (\xD7 stroke width)",
|
|
2425
|
-
className:
|
|
2529
|
+
className: numberClass9,
|
|
2426
2530
|
value: source[scaleKey],
|
|
2427
2531
|
onChange: (e) => {
|
|
2428
2532
|
const n = parseFloat(e.target.value);
|
|
@@ -2438,14 +2542,14 @@ function VectorStylePanel({ controller, className }) {
|
|
|
2438
2542
|
};
|
|
2439
2543
|
const paintRow = (key, label) => {
|
|
2440
2544
|
const paint = source[key];
|
|
2441
|
-
const hex = (0,
|
|
2545
|
+
const hex = (0, import_vector15.vectorPaintToHex)(paint);
|
|
2442
2546
|
const on = paint !== null && paint !== "none";
|
|
2443
|
-
return /* @__PURE__ */ (0,
|
|
2444
|
-
/* @__PURE__ */ (0,
|
|
2445
|
-
/* @__PURE__ */ (0,
|
|
2547
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
|
|
2548
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { className: "flex w-16 items-center gap-1.5 text-xs text-foreground", children: [
|
|
2549
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("input", { type: "checkbox", checked: on, "aria-label": `${label} on`, onChange: (e) => apply({ [key]: e.target.checked ? hex ?? "#000000" : null }) }),
|
|
2446
2550
|
label
|
|
2447
2551
|
] }),
|
|
2448
|
-
/* @__PURE__ */ (0,
|
|
2552
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2449
2553
|
"input",
|
|
2450
2554
|
{
|
|
2451
2555
|
type: "color",
|
|
@@ -2456,18 +2560,18 @@ function VectorStylePanel({ controller, className }) {
|
|
|
2456
2560
|
onChange: (e) => apply({ [key]: e.target.value })
|
|
2457
2561
|
}
|
|
2458
2562
|
),
|
|
2459
|
-
on && !hex && /* @__PURE__ */ (0,
|
|
2460
|
-
style?.mixed[key] && /* @__PURE__ */ (0,
|
|
2563
|
+
on && !hex && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "truncate text-[11px] text-muted-foreground", children: typeof paint === "string" ? paint : "Gradient" }),
|
|
2564
|
+
style?.mixed[key] && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
|
|
2461
2565
|
] });
|
|
2462
2566
|
};
|
|
2463
|
-
return /* @__PURE__ */ (0,
|
|
2464
|
-
/* @__PURE__ */ (0,
|
|
2567
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: `space-y-2 ${className ?? ""}`, "aria-label": style ? "Selection style" : "New shape style", children: [
|
|
2568
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: style ? "Selection" : "New shapes" }),
|
|
2465
2569
|
paintRow("fill", "Fill"),
|
|
2466
2570
|
paintRow("stroke", "Stroke"),
|
|
2467
|
-
style && /* @__PURE__ */ (0,
|
|
2468
|
-
/* @__PURE__ */ (0,
|
|
2469
|
-
/* @__PURE__ */ (0,
|
|
2470
|
-
/* @__PURE__ */ (0,
|
|
2571
|
+
style && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(VectorGradientEditor, { controller }),
|
|
2572
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
|
|
2573
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "w-16", children: "Width" }),
|
|
2574
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2471
2575
|
"input",
|
|
2472
2576
|
{
|
|
2473
2577
|
type: "number",
|
|
@@ -2483,33 +2587,33 @@ function VectorStylePanel({ controller, className }) {
|
|
|
2483
2587
|
}
|
|
2484
2588
|
)
|
|
2485
2589
|
] }),
|
|
2486
|
-
/* @__PURE__ */ (0,
|
|
2487
|
-
/* @__PURE__ */ (0,
|
|
2488
|
-
/* @__PURE__ */ (0,
|
|
2590
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "strokeAlign", children: [
|
|
2591
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "w-16", children: "Align" }),
|
|
2592
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("select", { "aria-label": "Stroke alignment", className: selectClass10, value: source.strokeAlign, onChange: (e) => apply({ strokeAlign: e.target.value }), children: Object.keys(import_vector15.VECTOR_STROKE_ALIGN_LABELS).map((a) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("option", { value: a, children: import_vector15.VECTOR_STROKE_ALIGN_LABELS[a] }, a)) }),
|
|
2489
2593
|
mixed("strokeAlign")
|
|
2490
2594
|
] }),
|
|
2491
|
-
/* @__PURE__ */ (0,
|
|
2492
|
-
/* @__PURE__ */ (0,
|
|
2493
|
-
/* @__PURE__ */ (0,
|
|
2595
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "widthProfile", children: [
|
|
2596
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "w-16", children: "Profile" }),
|
|
2597
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
2494
2598
|
"select",
|
|
2495
2599
|
{
|
|
2496
2600
|
"aria-label": "Width profile",
|
|
2497
|
-
className:
|
|
2601
|
+
className: selectClass10,
|
|
2498
2602
|
value: source.widthProfile,
|
|
2499
2603
|
onChange: (e) => {
|
|
2500
2604
|
if (e.target.value !== "custom") setProfile(e.target.value);
|
|
2501
2605
|
},
|
|
2502
2606
|
children: [
|
|
2503
|
-
|
|
2504
|
-
source.widthProfile === "custom" && /* @__PURE__ */ (0,
|
|
2607
|
+
import_vector15.VECTOR_WIDTH_PRESETS.map((p) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("option", { value: p.id, children: p.label }, p.id)),
|
|
2608
|
+
source.widthProfile === "custom" && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("option", { value: "custom", disabled: true, children: "Custom" })
|
|
2505
2609
|
]
|
|
2506
2610
|
}
|
|
2507
2611
|
),
|
|
2508
|
-
/* @__PURE__ */ (0,
|
|
2612
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2509
2613
|
"button",
|
|
2510
2614
|
{
|
|
2511
2615
|
type: "button",
|
|
2512
|
-
className:
|
|
2616
|
+
className: buttonClass13,
|
|
2513
2617
|
title: "Reset to uniform width",
|
|
2514
2618
|
"aria-label": "Reset to uniform width",
|
|
2515
2619
|
disabled: source.widthProfile === "uniform" && !style?.mixed.widthProfile,
|
|
@@ -2519,27 +2623,27 @@ function VectorStylePanel({ controller, className }) {
|
|
|
2519
2623
|
),
|
|
2520
2624
|
mixed("widthProfile")
|
|
2521
2625
|
] }),
|
|
2522
|
-
source.hasOpenPath && /* @__PURE__ */ (0,
|
|
2626
|
+
source.hasOpenPath && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
|
|
2523
2627
|
arrowRow("start", "Start"),
|
|
2524
2628
|
arrowRow("end", "End"),
|
|
2525
|
-
(source.startArrow || source.endArrow) && /* @__PURE__ */ (0,
|
|
2526
|
-
/* @__PURE__ */ (0,
|
|
2527
|
-
/* @__PURE__ */ (0,
|
|
2528
|
-
/* @__PURE__ */ (0,
|
|
2529
|
-
/* @__PURE__ */ (0,
|
|
2629
|
+
(source.startArrow || source.endArrow) && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "arrowAlign", children: [
|
|
2630
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "w-16", children: "Heads" }),
|
|
2631
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("select", { "aria-label": "Arrowhead alignment", className: selectClass10, value: source.arrowAlign, onChange: (e) => apply({ arrowAlign: e.target.value }), children: [
|
|
2632
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("option", { value: "extend", children: "Tip past end" }),
|
|
2633
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("option", { value: "tip", children: "Tip at end" })
|
|
2530
2634
|
] })
|
|
2531
2635
|
] })
|
|
2532
2636
|
] }),
|
|
2533
|
-
source.corners > 0 && /* @__PURE__ */ (0,
|
|
2534
|
-
/* @__PURE__ */ (0,
|
|
2535
|
-
/* @__PURE__ */ (0,
|
|
2637
|
+
source.corners > 0 && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": "corners", children: [
|
|
2638
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "w-16 text-xs text-foreground", children: "Corners" }),
|
|
2639
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2536
2640
|
"input",
|
|
2537
2641
|
{
|
|
2538
2642
|
type: "number",
|
|
2539
2643
|
min: 0,
|
|
2540
2644
|
step: 1,
|
|
2541
2645
|
"aria-label": "Corner radius",
|
|
2542
|
-
className:
|
|
2646
|
+
className: numberClass9,
|
|
2543
2647
|
value: Math.round(source.cornerRadius * 100) / 100,
|
|
2544
2648
|
onChange: (e) => {
|
|
2545
2649
|
const n = parseFloat(e.target.value);
|
|
@@ -2547,12 +2651,12 @@ function VectorStylePanel({ controller, className }) {
|
|
|
2547
2651
|
}
|
|
2548
2652
|
}
|
|
2549
2653
|
),
|
|
2550
|
-
/* @__PURE__ */ (0,
|
|
2654
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("select", { "aria-label": "Corner kind", className: selectClass10, value: source.cornerKind, onChange: (e) => setCorners({ kind: e.target.value }), children: import_vector15.VECTOR_CORNER_KINDS.map((k) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("option", { value: k, children: import_vector15.VECTOR_CORNER_KIND_LABELS[k] }, k)) }),
|
|
2551
2655
|
mixed("cornerRadius")
|
|
2552
2656
|
] }),
|
|
2553
|
-
style && source.corners > 0 && source.cornerKind === "round" && source.cornerRadius > 0 && /* @__PURE__ */ (0,
|
|
2554
|
-
/* @__PURE__ */ (0,
|
|
2555
|
-
/* @__PURE__ */ (0,
|
|
2657
|
+
style && source.corners > 0 && source.cornerKind === "round" && source.cornerRadius > 0 && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("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: [
|
|
2658
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "w-16", children: "Smoothing" }),
|
|
2659
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2556
2660
|
"input",
|
|
2557
2661
|
{
|
|
2558
2662
|
type: "range",
|
|
@@ -2567,9 +2671,9 @@ function VectorStylePanel({ controller, className }) {
|
|
|
2567
2671
|
}
|
|
2568
2672
|
)
|
|
2569
2673
|
] }),
|
|
2570
|
-
style && /* @__PURE__ */ (0,
|
|
2571
|
-
/* @__PURE__ */ (0,
|
|
2572
|
-
/* @__PURE__ */ (0,
|
|
2674
|
+
style && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
|
|
2675
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "w-16", children: "Opacity" }),
|
|
2676
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2573
2677
|
"input",
|
|
2574
2678
|
{
|
|
2575
2679
|
type: "range",
|
|
@@ -2583,26 +2687,28 @@ function VectorStylePanel({ controller, className }) {
|
|
|
2583
2687
|
}
|
|
2584
2688
|
)
|
|
2585
2689
|
] }),
|
|
2586
|
-
/* @__PURE__ */ (0,
|
|
2587
|
-
style && /* @__PURE__ */ (0,
|
|
2588
|
-
style && /* @__PURE__ */ (0,
|
|
2589
|
-
style && /* @__PURE__ */ (0,
|
|
2590
|
-
style && /* @__PURE__ */ (0,
|
|
2591
|
-
/* @__PURE__ */ (0,
|
|
2592
|
-
/* @__PURE__ */ (0,
|
|
2593
|
-
/* @__PURE__ */ (0,
|
|
2594
|
-
/* @__PURE__ */ (0,
|
|
2690
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(VectorTextPanel, { controller }),
|
|
2691
|
+
style && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(VectorAppearancePanel, { controller }),
|
|
2692
|
+
style && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(VectorEffectsPanel, { controller }),
|
|
2693
|
+
style && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(VectorEnvelopePanel, { controller }),
|
|
2694
|
+
style && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(VectorMaskPanel, { controller }),
|
|
2695
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(VectorFramePanel, { controller }),
|
|
2696
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(VectorConnectorPanel, { controller }),
|
|
2697
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(VectorShadingPanel, { controller }),
|
|
2698
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(VectorTracePanel, { controller }),
|
|
2699
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(VectorConstraintsPanel, { controller }),
|
|
2700
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(VectorSymbolsPanel, { controller })
|
|
2595
2701
|
] });
|
|
2596
2702
|
}
|
|
2597
2703
|
|
|
2598
2704
|
// src/vector/VectorEditor.tsx
|
|
2599
|
-
var
|
|
2705
|
+
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
2600
2706
|
function VectorEditor({ controller, tools, showStylePanel = true, height = 520, canvasProps, className }) {
|
|
2601
|
-
return /* @__PURE__ */ (0,
|
|
2602
|
-
/* @__PURE__ */ (0,
|
|
2603
|
-
/* @__PURE__ */ (0,
|
|
2604
|
-
/* @__PURE__ */ (0,
|
|
2605
|
-
showStylePanel && /* @__PURE__ */ (0,
|
|
2707
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: `flex flex-col gap-2 ${className ?? ""}`, "data-vector-editor": "", children: [
|
|
2708
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VectorToolbar, { controller, tools }),
|
|
2709
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex min-h-0 flex-col gap-2 md:flex-row", children: [
|
|
2710
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "relative min-h-[320px] flex-1 overflow-hidden rounded-md border border-border bg-muted/40", style: { height }, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VectorCanvas, { controller, ...canvasProps }) }),
|
|
2711
|
+
showStylePanel && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VectorStylePanel, { controller, className: "w-full shrink-0 rounded-md border border-border p-3 md:w-56" })
|
|
2606
2712
|
] })
|
|
2607
2713
|
] });
|
|
2608
2714
|
}
|
|
@@ -2623,11 +2729,13 @@ function VectorEditor({ controller, tools, showStylePanel = true, height = 520,
|
|
|
2623
2729
|
VectorMaskPanel,
|
|
2624
2730
|
VectorMaskedNodes,
|
|
2625
2731
|
VectorSceneNodes,
|
|
2732
|
+
VectorShadingPanel,
|
|
2626
2733
|
VectorStylePanel,
|
|
2627
2734
|
VectorSymbolsPanel,
|
|
2628
2735
|
VectorTextElement,
|
|
2629
2736
|
VectorTextPanel,
|
|
2630
2737
|
VectorToolbar,
|
|
2738
|
+
VectorTracePanel,
|
|
2631
2739
|
VectorWidthPointEditor,
|
|
2632
2740
|
useVectorCommands,
|
|
2633
2741
|
useVectorEditor,
|