svedit 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -259,6 +259,16 @@ const doc = {
259
259
 
260
260
  Documents need a config object that tells Svedit how to render and manipulate your content. See the full example in [`src/routes/create_demo_session.js`](src/routes/create_demo_session.js).
261
261
 
262
+ Two optional hooks are especially useful when integrating custom media workflows:
263
+
264
+ - `handle_media_paste(session, pasted_media)`
265
+ Called when media is pasted. You can upload/process files, replace an existing media property, or return a node payload to insert new content.
266
+ See example implementation in [`src/routes/create_demo_session.js`](src/routes/create_demo_session.js).
267
+
268
+ - `handle_property_deletion(session, path)`
269
+ Called when deleting/cutting a property selection. Use it to define app-specific reset behavior (for example clearing an image property or resetting a referenced media node).
270
+ See example implementation in [`src/routes/create_demo_session.js`](src/routes/create_demo_session.js).
271
+
262
272
  ```js
263
273
  const session_config = {
264
274
  // ID generator for creating new nodes
@@ -345,6 +355,20 @@ tr.insert_nodes(['new_node_id']);
345
355
  session.apply(tr); // Apply the transaction
346
356
  ```
347
357
 
358
+ #### Batching history entries
359
+
360
+ By default, every `session.apply(tr)` creates a new undo/redo entry. Pass `{ batch: true }` to merge the transaction into the previous history entry instead — useful for continuous interactions like dragging, where you want the entire gesture to undo as one step.
361
+
362
+ ```js
363
+ session.apply(tr, { batch: true });
364
+ ```
365
+
366
+ Batched transactions merge into the current history entry as long as they arrive within a 2-second window of the batch start. After 2 seconds of inactivity, the next `apply` starts a fresh entry. To force a new entry immediately (e.g. on pointer up), reset the batch timer:
367
+
368
+ ```js
369
+ session.last_batch_started = undefined;
370
+ ```
371
+
348
372
  ### History
349
373
 
350
374
  ```js
@@ -145,14 +145,14 @@
145
145
  if (
146
146
  ['deleteContentBackward', 'deleteWordBackward', 'deleteContent'].includes(event.inputType)
147
147
  ) {
148
- session.apply(session.tr.delete_selection('backward'));
148
+ delete_at_selection('backward');
149
149
  event.preventDefault();
150
150
  event.stopPropagation();
151
151
  return;
152
152
  }
153
153
 
154
154
  if (['deleteContentForward', 'deleteWordForward'].includes(event.inputType)) {
155
- session.apply(session.tr.delete_selection('forward'));
155
+ delete_at_selection('forward');
156
156
  event.preventDefault();
157
157
  event.stopPropagation();
158
158
  return;
@@ -494,7 +494,24 @@ ${fallback_html}`;
494
494
  }
495
495
 
496
496
  if (delete_selection) {
497
- session.apply(session.tr.delete_selection());
497
+ delete_at_selection();
498
+ }
499
+ }
500
+
501
+ /**
502
+ * Shared delete logic for cut, backspace, and forward-delete.
503
+ * For property selections, delegates to the config's handle_property_deletion hook.
504
+ * For text/node selections, uses the standard transaction delete_selection.
505
+ *
506
+ * @param {'backward' | 'forward'} [direction]
507
+ */
508
+ function delete_at_selection(direction = 'backward') {
509
+ if (session.selection?.type === 'property') {
510
+ if (session.config.handle_property_deletion) {
511
+ session.config.handle_property_deletion(session, session.selection.path);
512
+ }
513
+ } else {
514
+ session.apply(session.tr.delete_selection(direction));
498
515
  }
499
516
  }
500
517
 
@@ -871,6 +888,11 @@ ${fallback_html}`;
871
888
  // (e.g. selecting between nav and page body). The resulting path would be empty.
872
889
  if (anchor_root_path.length <= 1) return null;
873
890
 
891
+ const parent_array_path = anchor_root_path.slice(0, -1);
892
+ // A node selection is only valid inside a node_array property.
893
+ const parent_property = session.inspect(parent_array_path);
894
+ if (!parent_property || parent_property.type !== 'node_array') return null;
895
+
874
896
  let anchor_offset = parseInt(anchor_root_path.at(-1));
875
897
  let focus_offset = parseInt(focus_root_path.at(-1));
876
898
 
@@ -910,7 +932,7 @@ ${fallback_html}`;
910
932
 
911
933
  return {
912
934
  type: 'node',
913
- path: anchor_root_path.slice(0, -1),
935
+ path: parent_array_path,
914
936
  anchor_offset: anchor_offset,
915
937
  focus_offset: focus_offset
916
938
  };
@@ -1 +1 @@
1
- {"version":3,"file":"Svedit.svelte.d.ts","sourceRoot":"","sources":["../src/lib/Svedit.svelte.js"],"names":[],"mappings":";;;;;;;AA60CA;;eAAqD;iCAhzC1C,cAAc"}
1
+ {"version":3,"file":"Svedit.svelte.d.ts","sourceRoot":"","sources":["../src/lib/Svedit.svelte.js"],"names":[],"mappings":";;;;;;;AAm2CA;;eAAqD;iCAt0C1C,cAAc"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svedit",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "license": "MIT",
5
5
  "scripts": {
6
6
  "dev": "vite dev",
@@ -145,14 +145,14 @@
145
145
  if (
146
146
  ['deleteContentBackward', 'deleteWordBackward', 'deleteContent'].includes(event.inputType)
147
147
  ) {
148
- session.apply(session.tr.delete_selection('backward'));
148
+ delete_at_selection('backward');
149
149
  event.preventDefault();
150
150
  event.stopPropagation();
151
151
  return;
152
152
  }
153
153
 
154
154
  if (['deleteContentForward', 'deleteWordForward'].includes(event.inputType)) {
155
- session.apply(session.tr.delete_selection('forward'));
155
+ delete_at_selection('forward');
156
156
  event.preventDefault();
157
157
  event.stopPropagation();
158
158
  return;
@@ -494,7 +494,24 @@ ${fallback_html}`;
494
494
  }
495
495
 
496
496
  if (delete_selection) {
497
- session.apply(session.tr.delete_selection());
497
+ delete_at_selection();
498
+ }
499
+ }
500
+
501
+ /**
502
+ * Shared delete logic for cut, backspace, and forward-delete.
503
+ * For property selections, delegates to the config's handle_property_deletion hook.
504
+ * For text/node selections, uses the standard transaction delete_selection.
505
+ *
506
+ * @param {'backward' | 'forward'} [direction]
507
+ */
508
+ function delete_at_selection(direction = 'backward') {
509
+ if (session.selection?.type === 'property') {
510
+ if (session.config.handle_property_deletion) {
511
+ session.config.handle_property_deletion(session, session.selection.path);
512
+ }
513
+ } else {
514
+ session.apply(session.tr.delete_selection(direction));
498
515
  }
499
516
  }
500
517
 
@@ -871,6 +888,11 @@ ${fallback_html}`;
871
888
  // (e.g. selecting between nav and page body). The resulting path would be empty.
872
889
  if (anchor_root_path.length <= 1) return null;
873
890
 
891
+ const parent_array_path = anchor_root_path.slice(0, -1);
892
+ // A node selection is only valid inside a node_array property.
893
+ const parent_property = session.inspect(parent_array_path);
894
+ if (!parent_property || parent_property.type !== 'node_array') return null;
895
+
874
896
  let anchor_offset = parseInt(anchor_root_path.at(-1));
875
897
  let focus_offset = parseInt(focus_root_path.at(-1));
876
898
 
@@ -910,7 +932,7 @@ ${fallback_html}`;
910
932
 
911
933
  return {
912
934
  type: 'node',
913
- path: anchor_root_path.slice(0, -1),
935
+ path: parent_array_path,
914
936
  anchor_offset: anchor_offset,
915
937
  focus_offset: focus_offset
916
938
  };