@grida/svg-editor 1.0.0-alpha.18 → 1.0.0-alpha.20

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.
@@ -34,8 +34,168 @@ _grida_cmath = __toESM(_grida_cmath);
34
34
  let _grida_svg_parser = require("@grida/svg/parser");
35
35
  let _grida_svg_parse = require("@grida/svg/parse");
36
36
  let _grida_svg_pathdata = require("@grida/svg/pathdata");
37
+ let _grida_color = require("@grida/color");
38
+ _grida_color = __toESM(_grida_color);
37
39
  let _grida_vn = require("@grida/vn");
38
40
  _grida_vn = __toESM(_grida_vn);
41
+ //#region src/commands/defaults.ts
42
+ /** Command id for `tool.set`. Bound to V/R/O/L in `keymap/defaults.ts`. */
43
+ const TOOL_SET = "tool.set";
44
+ /**
45
+ * The headless default `transform.nudge` handler. Exported so a host
46
+ * surface that overrides nudge (e.g. for faux-snap UX) can restore the
47
+ * default on teardown — the registry doesn't stack handlers, so a plain
48
+ * unregister leaves the slot empty.
49
+ */
50
+ function default_nudge_handler(editor) {
51
+ return (args) => {
52
+ if (editor.state.selection.length === 0) return false;
53
+ const { dx, dy } = args;
54
+ editor.commands.nudge({
55
+ dx,
56
+ dy
57
+ });
58
+ return true;
59
+ };
60
+ }
61
+ function registerDefaultCommands(reg, editor) {
62
+ reg.register("history.undo", () => {
63
+ if (!editor.state.can_undo) return false;
64
+ editor.commands.undo();
65
+ return true;
66
+ });
67
+ reg.register("history.redo", () => {
68
+ if (!editor.state.can_redo) return false;
69
+ editor.commands.redo();
70
+ return true;
71
+ });
72
+ reg.register("selection.deselect", () => {
73
+ if (editor.state.selection.length === 0) return false;
74
+ editor.commands.deselect();
75
+ return true;
76
+ });
77
+ reg.register("selection.remove", () => {
78
+ if (editor.state.selection.length === 0) return false;
79
+ editor.commands.remove();
80
+ return true;
81
+ });
82
+ reg.register("selection.group", () => {
83
+ if (editor.state.mode !== "select") return false;
84
+ if (editor.state.selection.length === 0) return false;
85
+ return editor.commands.group();
86
+ });
87
+ reg.register("selection.duplicate", () => {
88
+ if (editor.state.mode !== "select") return false;
89
+ if (editor.state.selection.length === 0) return false;
90
+ return editor.commands.duplicate().length > 0;
91
+ });
92
+ reg.register("selection.ungroup", () => {
93
+ if (editor.state.mode !== "select") return false;
94
+ if (editor.state.selection.length !== 1) return false;
95
+ return editor.commands.ungroup();
96
+ });
97
+ reg.register("selection.resize_to", (args) => {
98
+ if (editor.state.mode !== "select") return false;
99
+ if (editor.state.selection.length === 0) return false;
100
+ const target = args;
101
+ return editor.commands.resize_to(target);
102
+ });
103
+ reg.register("selection.nudge_resize", (args) => {
104
+ if (editor.state.mode !== "select") return false;
105
+ if (editor.state.selection.length === 0) return false;
106
+ const { dw, dh } = args;
107
+ return editor.commands.resize_by({
108
+ dw,
109
+ dh
110
+ });
111
+ });
112
+ reg.register("selection.rotate", (args) => {
113
+ if (editor.state.mode !== "select") return false;
114
+ if (editor.state.selection.length === 0) return false;
115
+ const a = args;
116
+ return editor.commands.rotate(a.angle, { pivot: a.pivot });
117
+ });
118
+ reg.register("selection.rotate_to", (args) => {
119
+ if (editor.state.mode !== "select") return false;
120
+ if (editor.state.selection.length === 0) return false;
121
+ const a = args;
122
+ return editor.commands.rotate_to(a.angle, { pivot: a.pivot });
123
+ });
124
+ reg.register("selection.flatten_transform", () => {
125
+ if (editor.state.mode !== "select") return false;
126
+ if (editor.state.selection.length === 0) return false;
127
+ return editor.commands.flatten_transform();
128
+ });
129
+ reg.register("selection.all", () => {
130
+ if (editor.state.mode !== "select") return false;
131
+ return editor.commands.select_all();
132
+ });
133
+ reg.register("selection.sibling", (args) => {
134
+ if (editor.state.mode !== "select") return false;
135
+ return editor.commands.select_sibling(args);
136
+ });
137
+ reg.register("selection.align", (args) => {
138
+ if (editor.state.mode !== "select") return false;
139
+ return editor.commands.align(args);
140
+ });
141
+ reg.register("clipboard.copy", () => {
142
+ if (editor.state.mode !== "select") return false;
143
+ if (editor.state.selection.length === 0) return false;
144
+ return editor.commands.copy() !== null;
145
+ });
146
+ reg.register("clipboard.cut", () => {
147
+ if (editor.state.mode !== "select") return false;
148
+ if (editor.state.selection.length === 0) return false;
149
+ return editor.commands.cut() !== null;
150
+ });
151
+ reg.register("clipboard.paste", (args) => {
152
+ if (editor.state.mode !== "select") return false;
153
+ const text = args?.text;
154
+ if (typeof text === "string") return editor.commands.paste(text).length > 0;
155
+ const provider = editor.providers.clipboard;
156
+ if (provider) {
157
+ provider.read().then((text) => {
158
+ if (text) editor.commands.paste(text);
159
+ }).catch((err) => {
160
+ console.warn("[svg-editor] clipboard provider read failed:", err);
161
+ });
162
+ return true;
163
+ }
164
+ return editor.commands.paste().length > 0;
165
+ });
166
+ reg.register("content.enter", () => editor.enter_content_edit());
167
+ reg.register("hierarchy.enter", () => {
168
+ if (editor.state.selection.length !== 1) return false;
169
+ const id = editor.state.selection[0];
170
+ const node = editor.tree().nodes.get(id);
171
+ if (!node || node.children.length === 0) return false;
172
+ editor.commands.select(node.children[0]);
173
+ return true;
174
+ });
175
+ reg.register("hierarchy.exit", () => {
176
+ if (editor.state.selection.length !== 1) return false;
177
+ const id = editor.state.selection[0];
178
+ const tree = editor.tree();
179
+ const node = tree.nodes.get(id);
180
+ if (!node || node.parent === null || node.parent === tree.root) return false;
181
+ editor.commands.select(node.parent);
182
+ return true;
183
+ });
184
+ reg.register("transform.nudge", default_nudge_handler(editor));
185
+ reg.register("reorder", (args) => {
186
+ if (editor.state.selection.length !== 1) return false;
187
+ editor.commands.reorder(args);
188
+ return true;
189
+ });
190
+ reg.register(TOOL_SET, (args) => {
191
+ const next = args;
192
+ const required_mode = next.type === "lasso" || next.type === "bend" ? "edit-content" : next.type === "insert" || next.type === "insert-text" ? "select" : null;
193
+ if (required_mode !== null && editor.state.mode !== required_mode) return false;
194
+ editor.set_tool(next);
195
+ return true;
196
+ });
197
+ }
198
+ //#endregion
39
199
  //#region src/util/dom.ts
40
200
  /**
41
201
  * `true` when the document's active element is a text-input-like control
@@ -4142,7 +4302,7 @@ let paint;
4142
4302
  kind: "color",
4143
4303
  value: {
4144
4304
  kind: "rgb",
4145
- value: trimmed
4305
+ value: _grida_color.default.resolveHEX(trimmed) ?? trimmed
4146
4306
  }
4147
4307
  };
4148
4308
  }
@@ -5243,6 +5403,12 @@ Object.defineProperty(exports, "TOOL_CURSOR", {
5243
5403
  return TOOL_CURSOR;
5244
5404
  }
5245
5405
  });
5406
+ Object.defineProperty(exports, "TOOL_SET", {
5407
+ enumerable: true,
5408
+ get: function() {
5409
+ return TOOL_SET;
5410
+ }
5411
+ });
5246
5412
  Object.defineProperty(exports, "TranslateOrchestrator", {
5247
5413
  enumerable: true,
5248
5414
  get: function() {
@@ -5273,6 +5439,12 @@ Object.defineProperty(exports, "array_shallow_equal", {
5273
5439
  return array_shallow_equal;
5274
5440
  }
5275
5441
  });
5442
+ Object.defineProperty(exports, "default_nudge_handler", {
5443
+ enumerable: true,
5444
+ get: function() {
5445
+ return default_nudge_handler;
5446
+ }
5447
+ });
5276
5448
  Object.defineProperty(exports, "group", {
5277
5449
  enumerable: true,
5278
5450
  get: function() {
@@ -5303,6 +5475,12 @@ Object.defineProperty(exports, "paint", {
5303
5475
  return paint;
5304
5476
  }
5305
5477
  });
5478
+ Object.defineProperty(exports, "registerDefaultCommands", {
5479
+ enumerable: true,
5480
+ get: function() {
5481
+ return registerDefaultCommands;
5482
+ }
5483
+ });
5306
5484
  Object.defineProperty(exports, "resize_pipeline", {
5307
5485
  enumerable: true,
5308
5486
  get: function() {
@@ -2,7 +2,166 @@ import cmath from "@grida/cmath";
2
2
  import { SVG_NS, XLINK_NS as XLINK_NS$1, XMLNS_NS, encode_attr_value, encode_text, parse_svg } from "@grida/svg/parser";
3
3
  import { svg_parse } from "@grida/svg/parse";
4
4
  import { SVGPathData, SVGPathDataTransformer, encodeSVGPath } from "@grida/svg/pathdata";
5
+ import kolor from "@grida/color";
5
6
  import vn from "@grida/vn";
7
+ //#region src/commands/defaults.ts
8
+ /** Command id for `tool.set`. Bound to V/R/O/L in `keymap/defaults.ts`. */
9
+ const TOOL_SET = "tool.set";
10
+ /**
11
+ * The headless default `transform.nudge` handler. Exported so a host
12
+ * surface that overrides nudge (e.g. for faux-snap UX) can restore the
13
+ * default on teardown — the registry doesn't stack handlers, so a plain
14
+ * unregister leaves the slot empty.
15
+ */
16
+ function default_nudge_handler(editor) {
17
+ return (args) => {
18
+ if (editor.state.selection.length === 0) return false;
19
+ const { dx, dy } = args;
20
+ editor.commands.nudge({
21
+ dx,
22
+ dy
23
+ });
24
+ return true;
25
+ };
26
+ }
27
+ function registerDefaultCommands(reg, editor) {
28
+ reg.register("history.undo", () => {
29
+ if (!editor.state.can_undo) return false;
30
+ editor.commands.undo();
31
+ return true;
32
+ });
33
+ reg.register("history.redo", () => {
34
+ if (!editor.state.can_redo) return false;
35
+ editor.commands.redo();
36
+ return true;
37
+ });
38
+ reg.register("selection.deselect", () => {
39
+ if (editor.state.selection.length === 0) return false;
40
+ editor.commands.deselect();
41
+ return true;
42
+ });
43
+ reg.register("selection.remove", () => {
44
+ if (editor.state.selection.length === 0) return false;
45
+ editor.commands.remove();
46
+ return true;
47
+ });
48
+ reg.register("selection.group", () => {
49
+ if (editor.state.mode !== "select") return false;
50
+ if (editor.state.selection.length === 0) return false;
51
+ return editor.commands.group();
52
+ });
53
+ reg.register("selection.duplicate", () => {
54
+ if (editor.state.mode !== "select") return false;
55
+ if (editor.state.selection.length === 0) return false;
56
+ return editor.commands.duplicate().length > 0;
57
+ });
58
+ reg.register("selection.ungroup", () => {
59
+ if (editor.state.mode !== "select") return false;
60
+ if (editor.state.selection.length !== 1) return false;
61
+ return editor.commands.ungroup();
62
+ });
63
+ reg.register("selection.resize_to", (args) => {
64
+ if (editor.state.mode !== "select") return false;
65
+ if (editor.state.selection.length === 0) return false;
66
+ const target = args;
67
+ return editor.commands.resize_to(target);
68
+ });
69
+ reg.register("selection.nudge_resize", (args) => {
70
+ if (editor.state.mode !== "select") return false;
71
+ if (editor.state.selection.length === 0) return false;
72
+ const { dw, dh } = args;
73
+ return editor.commands.resize_by({
74
+ dw,
75
+ dh
76
+ });
77
+ });
78
+ reg.register("selection.rotate", (args) => {
79
+ if (editor.state.mode !== "select") return false;
80
+ if (editor.state.selection.length === 0) return false;
81
+ const a = args;
82
+ return editor.commands.rotate(a.angle, { pivot: a.pivot });
83
+ });
84
+ reg.register("selection.rotate_to", (args) => {
85
+ if (editor.state.mode !== "select") return false;
86
+ if (editor.state.selection.length === 0) return false;
87
+ const a = args;
88
+ return editor.commands.rotate_to(a.angle, { pivot: a.pivot });
89
+ });
90
+ reg.register("selection.flatten_transform", () => {
91
+ if (editor.state.mode !== "select") return false;
92
+ if (editor.state.selection.length === 0) return false;
93
+ return editor.commands.flatten_transform();
94
+ });
95
+ reg.register("selection.all", () => {
96
+ if (editor.state.mode !== "select") return false;
97
+ return editor.commands.select_all();
98
+ });
99
+ reg.register("selection.sibling", (args) => {
100
+ if (editor.state.mode !== "select") return false;
101
+ return editor.commands.select_sibling(args);
102
+ });
103
+ reg.register("selection.align", (args) => {
104
+ if (editor.state.mode !== "select") return false;
105
+ return editor.commands.align(args);
106
+ });
107
+ reg.register("clipboard.copy", () => {
108
+ if (editor.state.mode !== "select") return false;
109
+ if (editor.state.selection.length === 0) return false;
110
+ return editor.commands.copy() !== null;
111
+ });
112
+ reg.register("clipboard.cut", () => {
113
+ if (editor.state.mode !== "select") return false;
114
+ if (editor.state.selection.length === 0) return false;
115
+ return editor.commands.cut() !== null;
116
+ });
117
+ reg.register("clipboard.paste", (args) => {
118
+ if (editor.state.mode !== "select") return false;
119
+ const text = args?.text;
120
+ if (typeof text === "string") return editor.commands.paste(text).length > 0;
121
+ const provider = editor.providers.clipboard;
122
+ if (provider) {
123
+ provider.read().then((text) => {
124
+ if (text) editor.commands.paste(text);
125
+ }).catch((err) => {
126
+ console.warn("[svg-editor] clipboard provider read failed:", err);
127
+ });
128
+ return true;
129
+ }
130
+ return editor.commands.paste().length > 0;
131
+ });
132
+ reg.register("content.enter", () => editor.enter_content_edit());
133
+ reg.register("hierarchy.enter", () => {
134
+ if (editor.state.selection.length !== 1) return false;
135
+ const id = editor.state.selection[0];
136
+ const node = editor.tree().nodes.get(id);
137
+ if (!node || node.children.length === 0) return false;
138
+ editor.commands.select(node.children[0]);
139
+ return true;
140
+ });
141
+ reg.register("hierarchy.exit", () => {
142
+ if (editor.state.selection.length !== 1) return false;
143
+ const id = editor.state.selection[0];
144
+ const tree = editor.tree();
145
+ const node = tree.nodes.get(id);
146
+ if (!node || node.parent === null || node.parent === tree.root) return false;
147
+ editor.commands.select(node.parent);
148
+ return true;
149
+ });
150
+ reg.register("transform.nudge", default_nudge_handler(editor));
151
+ reg.register("reorder", (args) => {
152
+ if (editor.state.selection.length !== 1) return false;
153
+ editor.commands.reorder(args);
154
+ return true;
155
+ });
156
+ reg.register(TOOL_SET, (args) => {
157
+ const next = args;
158
+ const required_mode = next.type === "lasso" || next.type === "bend" ? "edit-content" : next.type === "insert" || next.type === "insert-text" ? "select" : null;
159
+ if (required_mode !== null && editor.state.mode !== required_mode) return false;
160
+ editor.set_tool(next);
161
+ return true;
162
+ });
163
+ }
164
+ //#endregion
6
165
  //#region src/util/dom.ts
7
166
  /**
8
167
  * `true` when the document's active element is a text-input-like control
@@ -4109,7 +4268,7 @@ let paint;
4109
4268
  kind: "color",
4110
4269
  value: {
4111
4270
  kind: "rgb",
4112
- value: trimmed
4271
+ value: kolor.resolveHEX(trimmed) ?? trimmed
4113
4272
  }
4114
4273
  };
4115
4274
  }
@@ -5168,4 +5327,4 @@ function emitWithVerbs(network, meta) {
5168
5327
  return encodeSVGPath(commands);
5169
5328
  }
5170
5329
  //#endregion
5171
- export { is_text_input_focused as S, SVG_NS as _, paint as a, XMLNS_NS as b, hit_shape_svg as c, NudgeDwellWatcher as d, TranslateOrchestrator as f, subtree as g, transform as h, TOOL_CURSOR as i, RotateOrchestrator as l, group as m, insertions as n, ResizeOrchestrator as o, translate_pipeline as p, DEFAULT_STYLE as r, resize_pipeline as s, PathModel as t, rotate_pipeline as u, SvgDocument as v, array_shallow_equal as x, WELL_KNOWN_NS_PREFIXES as y };
5330
+ export { TOOL_SET as C, is_text_input_focused as S, registerDefaultCommands as T, SVG_NS as _, paint as a, XMLNS_NS as b, hit_shape_svg as c, NudgeDwellWatcher as d, TranslateOrchestrator as f, subtree as g, transform as h, TOOL_CURSOR as i, RotateOrchestrator as l, group as m, insertions as n, ResizeOrchestrator as o, translate_pipeline as p, DEFAULT_STYLE as r, resize_pipeline as s, PathModel as t, rotate_pipeline as u, SvgDocument as v, default_nudge_handler as w, array_shallow_equal as x, WELL_KNOWN_NS_PREFIXES as y };
@@ -1,5 +1,5 @@
1
- import { c as SvgEditor } from "./editor-KqpIW1qm.mjs";
2
- import { n as DomSurfaceOptions, t as DomSurfaceHandle } from "./dom-TctdgRnn.mjs";
1
+ import { c as SvgEditor } from "./editor-CcW4BVth.mjs";
2
+ import { n as DomSurfaceHandle, r as DomSurfaceOptions } from "./dom-Dw2SPHgc.mjs";
3
3
 
4
4
  //#region src/presets/keynote.d.ts
5
5
  declare namespace keynote_d_exports {
package/dist/presets.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { c as SvgEditor } from "./editor-BSxTUsW_.js";
2
- import { n as DomSurfaceOptions, t as DomSurfaceHandle } from "./dom-BMzX1CXZ.js";
1
+ import { c as SvgEditor } from "./editor-CxqRhhzP.js";
2
+ import { n as DomSurfaceHandle, r as DomSurfaceOptions } from "./dom-CQkWJNrK.js";
3
3
 
4
4
  //#region \0rolldown/runtime.js
5
5
  declare namespace keynote_d_exports {
package/dist/presets.js CHANGED
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_model = require("./model-BLhMJZKJ.js");
3
- const require_dom = require("./dom-DKQ4Vt3z.js");
2
+ const require_model = require("./model-CzL6_zId.js");
3
+ const require_dom = require("./dom-CfYDV311.js");
4
4
  //#region src/presets/keynote.ts
5
5
  var keynote_exports = /* @__PURE__ */ require_model.__exportAll({ attach: () => attach });
6
6
  /**
@@ -35,8 +35,7 @@ function attach(editor, opts) {
35
35
  apply();
36
36
  const unsub_load = editor.subscribe_with_selector((s) => s.load_version, () => inner.camera.fit("<root>", { margin: inner.camera.constraints?.padding ?? 0 }));
37
37
  return {
38
- camera: inner.camera,
39
- gestures: inner.gestures,
38
+ ...inner,
40
39
  set_padding(p) {
41
40
  padding = p;
42
41
  apply();
package/dist/presets.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { t as __exportAll } from "./chunk-D7D4PA-g.mjs";
2
- import { t as attach_dom_surface } from "./dom-OP-kmK8k.mjs";
2
+ import { t as attach_dom_surface } from "./dom-Dub-TMoN.mjs";
3
3
  //#region src/presets/keynote.ts
4
4
  var keynote_exports = /* @__PURE__ */ __exportAll({ attach: () => attach });
5
5
  /**
@@ -34,8 +34,7 @@ function attach(editor, opts) {
34
34
  apply();
35
35
  const unsub_load = editor.subscribe_with_selector((s) => s.load_version, () => inner.camera.fit("<root>", { margin: inner.camera.constraints?.padding ?? 0 }));
36
36
  return {
37
- camera: inner.camera,
38
- gestures: inner.gestures,
37
+ ...inner,
39
38
  set_padding(p) {
40
39
  padding = p;
41
40
  apply();
package/dist/react.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { A as EditorState, H as Mode, J as PickEvent, K as PaintPreviewSession, Q as Providers, U as NodeId, Y as PreviewSession, c as SvgEditor, j as EditorStyle, rt as Tool, t as Commands } from "./editor-KqpIW1qm.mjs";
2
- import { t as DomSurfaceHandle } from "./dom-TctdgRnn.mjs";
1
+ import { A as EditorState, H as Mode, J as PickEvent, K as PaintPreviewSession, Q as Providers, U as NodeId, Y as PreviewSession, c as SvgEditor, j as EditorStyle, rt as Tool, t as Commands } from "./editor-CcW4BVth.mjs";
2
+ import { n as DomSurfaceHandle } from "./dom-Dw2SPHgc.mjs";
3
3
  import cmath from "@grida/cmath";
4
4
  import { ReactNode } from "react";
5
5
 
package/dist/react.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { A as EditorState, H as Mode, J as PickEvent, K as PaintPreviewSession, Q as Providers, U as NodeId, Y as PreviewSession, c as SvgEditor, j as EditorStyle, rt as Tool, t as Commands } from "./editor-BSxTUsW_.js";
2
- import { t as DomSurfaceHandle } from "./dom-BMzX1CXZ.js";
1
+ import { A as EditorState, H as Mode, J as PickEvent, K as PaintPreviewSession, Q as Providers, U as NodeId, Y as PreviewSession, c as SvgEditor, j as EditorStyle, rt as Tool, t as Commands } from "./editor-CxqRhhzP.js";
2
+ import { n as DomSurfaceHandle } from "./dom-CQkWJNrK.js";
3
3
  import cmath from "@grida/cmath";
4
4
  import { ReactNode } from "react";
5
5
 
package/dist/react.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const require_editor = require("./editor-Be6UrMeV.js");
4
- const require_dom = require("./dom-DKQ4Vt3z.js");
3
+ const require_editor = require("./editor-4U9LAZ6r.js");
4
+ const require_dom = require("./dom-CfYDV311.js");
5
5
  let react = require("react");
6
6
  let react_jsx_runtime = require("react/jsx-runtime");
7
7
  //#region src/react.tsx
@@ -171,9 +171,14 @@ function use_lifecycle_session(open, deps) {
171
171
  const sessionRef = (0, react.useRef)(null);
172
172
  const ops = (0, react.useMemo)(() => ({
173
173
  ensure() {
174
+ if (sessionRef.current?.live === false) sessionRef.current = null;
174
175
  if (!sessionRef.current) sessionRef.current = open();
175
176
  return sessionRef.current;
176
177
  },
178
+ /** Non-creating read: is a gesture currently open underneath? */
179
+ live() {
180
+ return sessionRef.current?.live === true;
181
+ },
177
182
  finalize(action, commit) {
178
183
  const s = sessionRef.current;
179
184
  if (!s) return;
@@ -192,6 +197,9 @@ function usePaintPreview(channel) {
192
197
  const editor = useSvgEditor();
193
198
  const lc = use_lifecycle_session(() => editor.commands.preview_paint(channel), [editor, channel]);
194
199
  return (0, react.useMemo)(() => ({
200
+ get live() {
201
+ return lc.live();
202
+ },
195
203
  update: (paint) => lc.ensure().update(paint),
196
204
  commit: () => lc.finalize("commit", (s) => s.commit()),
197
205
  discard: () => lc.finalize("discard", () => {})
@@ -202,6 +210,9 @@ function usePropertyPreview(name) {
202
210
  const editor = useSvgEditor();
203
211
  const lc = use_lifecycle_session(() => editor.commands.preview_property(name), [editor, name]);
204
212
  return (0, react.useMemo)(() => ({
213
+ get live() {
214
+ return lc.live();
215
+ },
205
216
  update: (value) => lc.ensure().update(value),
206
217
  commit: () => lc.finalize("commit", (s) => s.commit()),
207
218
  discard: () => lc.finalize("discard", () => {})
package/dist/react.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  "use client";
2
- import { t as createSvgEditor } from "./editor-BkCbYCz2.mjs";
3
- import { t as attach_dom_surface } from "./dom-OP-kmK8k.mjs";
2
+ import { t as createSvgEditor } from "./editor-B1GmFnS9.mjs";
3
+ import { t as attach_dom_surface } from "./dom-Dub-TMoN.mjs";
4
4
  import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useSyncExternalStore } from "react";
5
5
  import { jsx } from "react/jsx-runtime";
6
6
  //#region src/react.tsx
@@ -170,9 +170,14 @@ function use_lifecycle_session(open, deps) {
170
170
  const sessionRef = useRef(null);
171
171
  const ops = useMemo(() => ({
172
172
  ensure() {
173
+ if (sessionRef.current?.live === false) sessionRef.current = null;
173
174
  if (!sessionRef.current) sessionRef.current = open();
174
175
  return sessionRef.current;
175
176
  },
177
+ /** Non-creating read: is a gesture currently open underneath? */
178
+ live() {
179
+ return sessionRef.current?.live === true;
180
+ },
176
181
  finalize(action, commit) {
177
182
  const s = sessionRef.current;
178
183
  if (!s) return;
@@ -191,6 +196,9 @@ function usePaintPreview(channel) {
191
196
  const editor = useSvgEditor();
192
197
  const lc = use_lifecycle_session(() => editor.commands.preview_paint(channel), [editor, channel]);
193
198
  return useMemo(() => ({
199
+ get live() {
200
+ return lc.live();
201
+ },
194
202
  update: (paint) => lc.ensure().update(paint),
195
203
  commit: () => lc.finalize("commit", (s) => s.commit()),
196
204
  discard: () => lc.finalize("discard", () => {})
@@ -201,6 +209,9 @@ function usePropertyPreview(name) {
201
209
  const editor = useSvgEditor();
202
210
  const lc = use_lifecycle_session(() => editor.commands.preview_property(name), [editor, name]);
203
211
  return useMemo(() => ({
212
+ get live() {
213
+ return lc.live();
214
+ },
204
215
  update: (value) => lc.ensure().update(value),
205
216
  commit: () => lc.finalize("commit", (s) => s.commit()),
206
217
  discard: () => lc.finalize("discard", () => {})
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grida/svg-editor",
3
- "version": "1.0.0-alpha.18",
3
+ "version": "1.0.0-alpha.20",
4
4
  "description": "Headless SVG editor (experimental).",
5
5
  "keywords": [
6
6
  "bezier",
@@ -58,13 +58,14 @@
58
58
  "tag": "alpha"
59
59
  },
60
60
  "dependencies": {
61
- "@grida/history": "0.1.1",
61
+ "@grida/history": "0.1.2",
62
62
  "@grida/keybinding": "0.2.1",
63
+ "@grida/color": "0.1.0",
63
64
  "@grida/hud": "0.2.2",
64
- "@grida/svg": "0.2.0",
65
- "@grida/text-editor": "0.1.2",
66
65
  "@grida/cmath": "0.2.3",
67
- "@grida/vn": "0.1.0"
66
+ "@grida/svg": "0.2.0",
67
+ "@grida/vn": "0.1.0",
68
+ "@grida/text-editor": "0.1.2"
68
69
  },
69
70
  "devDependencies": {
70
71
  "@types/react": "^19",