svedit 0.15.0 → 0.15.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/README.md CHANGED
@@ -75,6 +75,8 @@ Each node has a `kind` that determines its behavior:
75
75
  - Pressing Enter splits the node into two nodes of the same type
76
76
  - Pressing Backspace at position 0 joins it with the previous node
77
77
 
78
+ When `break_text_node` splits through a mark or annotation, the right-hand occurrence receives an independent attachment node with the same properties. Editing one link or annotation therefore does not change the other. Ranges moved wholly to one side retain their ids. Subtree copying and pasting through `Transaction.build` also detach repeated attachment references in the copied data. These operations do not rewrite attachment references in existing stored documents.
79
+
78
80
  If **any** of those assumptions don't hold, use `kind: 'block'`. Blocks can still have `text` properties with full editing support (typing, formatting, selection) — they just don't participate in split/join.
79
81
 
80
82
  > **Common mistake:** A quote node with `content` + `author` properties might seem like `kind: 'text'` because both fields are editable text. But splitting a quote into two half-quotes doesn't make sense, and `join_text_node` hard-codes `node.content` — so Backspace in the `author` field would join the wrong property with the previous block and drop the author text. The correct kind is `'block'`.
@@ -1 +1 @@
1
- {"version":3,"file":"Transaction.svelte.d.ts","sourceRoot":"","sources":["../src/lib/Transaction.svelte.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Transaction.svelte.d.ts","sourceRoot":"","sources":["../src/lib/Transaction.svelte.ts"],"names":[],"mappings":"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"}
@@ -240,6 +240,8 @@ export default class Transaction {
240
240
  const keep_ids = preserve_ids && depth_first_nodes.every((node) => !this.doc.nodes[node.id]);
241
241
  // This maps original ids to newly generated ids
242
242
  const id_map = {};
243
+ // eslint-disable-next-line svelte/prefer-svelte-reactivity -- Transaction-local ownership lookup.
244
+ const used_attachment_ids = new Set();
243
245
  for (const node of depth_first_nodes) {
244
246
  const new_id = keep_ids ? node.id : this.generate_id();
245
247
  id_map[node.id] = new_id;
@@ -250,7 +252,12 @@ export default class Transaction {
250
252
  const prop_type = property_definition.type;
251
253
  const value = new_node[property_name];
252
254
  const remap_ranges = (ranges) => (ranges ?? []).map(({ start_offset, end_offset, node_id }) => {
253
- return { start_offset, end_offset, node_id: id_map[node_id] || node_id };
255
+ // Clipboard subtrees may contain legacy shared attachments; detach each occurrence.
256
+ const next_id = used_attachment_ids.has(node_id)
257
+ ? this.build(node_id, nodes)
258
+ : id_map[node_id] || node_id;
259
+ used_attachment_ids.add(node_id);
260
+ return { start_offset, end_offset, node_id: next_id };
254
261
  });
255
262
  if (prop_type === 'node_array' && value && typeof value === 'object') {
256
263
  new_node[property_name] = {
@@ -1 +1 @@
1
- {"version":3,"file":"transforms.svelte.d.ts","sourceRoot":"","sources":["../src/lib/transforms.svelte.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AACvD,OAAO,KAAK,EAEX,YAAY,EAKZ,MAAM,YAAY,CAAC;AAEpB;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC7B,EAAE,EAAE,WAAW,EACf,IAAI,EAAE,YAAY,EAClB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACjC,IAAI,CAIN;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,WAAW,GAAG,OAAO,CAuDxD;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,WAAW,GAAG,OAAO,CAoEvD;AAED,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,WAAW,GAAG,OAAO,CAyB5D"}
1
+ {"version":3,"file":"transforms.svelte.d.ts","sourceRoot":"","sources":["../src/lib/transforms.svelte.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AACvD,OAAO,KAAK,EAEX,YAAY,EAKZ,MAAM,YAAY,CAAC;AAEpB;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC7B,EAAE,EAAE,WAAW,EACf,IAAI,EAAE,YAAY,EAClB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACjC,IAAI,CAIN;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,WAAW,GAAG,OAAO,CAqExD;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,WAAW,GAAG,OAAO,CAoEvD;AAED,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,WAAW,GAAG,OAAO,CAyB5D"}
@@ -38,6 +38,18 @@ export function break_text_node(tr) {
38
38
  const split_at_position = tr.selection.anchor_offset;
39
39
  const content = tr.get(selection.path);
40
40
  const [left_text, right_text] = split_text(content, split_at_position);
41
+ // Each attachment occurrence belongs to one range after the paragraph split.
42
+ // eslint-disable-next-line svelte/prefer-svelte-reactivity -- Transaction-local ownership lookup.
43
+ const used_attachment_ids = new Set([...left_text.marks, ...left_text.annotations].map((range) => range.node_id));
44
+ for (const range of [...right_text.marks, ...right_text.annotations]) {
45
+ const original_id = range.node_id;
46
+ if (used_attachment_ids.has(original_id)) {
47
+ range.node_id = tr.build(original_id, tr.doc.nodes);
48
+ }
49
+ else {
50
+ used_attachment_ids.add(original_id);
51
+ }
52
+ }
41
53
  tr.set([node.id, 'content'], left_text);
42
54
  const node_insert_position = {
43
55
  type: 'node',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svedit",
3
- "version": "0.15.0",
3
+ "version": "0.15.1",
4
4
  "homepage": "https://svedit.dev",
5
5
  "license": "MIT",
6
6
  "files": [
@@ -324,6 +324,8 @@ export default class Transaction {
324
324
  const keep_ids = preserve_ids && depth_first_nodes.every((node) => !this.doc.nodes[node.id]);
325
325
  // This maps original ids to newly generated ids
326
326
  const id_map: Record<string, string> = {};
327
+ // eslint-disable-next-line svelte/prefer-svelte-reactivity -- Transaction-local ownership lookup.
328
+ const used_attachment_ids = new Set<NodeId>();
327
329
 
328
330
  for (const node of depth_first_nodes) {
329
331
  const new_id = keep_ids ? node.id : this.generate_id();
@@ -338,7 +340,12 @@ export default class Transaction {
338
340
 
339
341
  const remap_ranges = (ranges: Attachment[] | undefined) =>
340
342
  (ranges ?? []).map(({ start_offset, end_offset, node_id }) => {
341
- return { start_offset, end_offset, node_id: id_map[node_id] || node_id };
343
+ // Clipboard subtrees may contain legacy shared attachments; detach each occurrence.
344
+ const next_id = used_attachment_ids.has(node_id)
345
+ ? this.build(node_id, nodes)
346
+ : id_map[node_id] || node_id;
347
+ used_attachment_ids.add(node_id);
348
+ return { start_offset, end_offset, node_id: next_id };
342
349
  });
343
350
 
344
351
  if (prop_type === 'node_array' && value && typeof value === 'object') {
@@ -55,6 +55,20 @@ export function break_text_node(tr: Transaction): boolean {
55
55
  const content = tr.get(selection.path);
56
56
  const [left_text, right_text] = split_text(content, split_at_position);
57
57
 
58
+ // Each attachment occurrence belongs to one range after the paragraph split.
59
+ // eslint-disable-next-line svelte/prefer-svelte-reactivity -- Transaction-local ownership lookup.
60
+ const used_attachment_ids = new Set(
61
+ [...left_text.marks, ...left_text.annotations].map((range) => range.node_id)
62
+ );
63
+ for (const range of [...right_text.marks, ...right_text.annotations]) {
64
+ const original_id = range.node_id;
65
+ if (used_attachment_ids.has(original_id)) {
66
+ range.node_id = tr.build(original_id, tr.doc.nodes);
67
+ } else {
68
+ used_attachment_ids.add(original_id);
69
+ }
70
+ }
71
+
58
72
  tr.set([node.id, 'content'], left_text);
59
73
 
60
74
  const node_insert_position: Selection = {