svedit 0.11.1 → 0.13.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.
Files changed (93) hide show
  1. package/README.md +260 -82
  2. package/dist/Command.svelte.d.ts +54 -29
  3. package/dist/Command.svelte.d.ts.map +1 -1
  4. package/dist/Command.svelte.js +286 -283
  5. package/dist/CustomProperty.svelte +3 -5
  6. package/dist/CustomProperty.svelte.d.ts +3 -6
  7. package/dist/CustomProperty.svelte.d.ts.map +1 -1
  8. package/dist/KeyMapper.svelte.d.ts +19 -6
  9. package/dist/KeyMapper.svelte.d.ts.map +1 -1
  10. package/dist/KeyMapper.svelte.js +95 -112
  11. package/dist/Node.svelte +40 -9
  12. package/dist/Node.svelte.d.ts +3 -5
  13. package/dist/Node.svelte.d.ts.map +1 -1
  14. package/dist/NodeArrayProperty.svelte +142 -18
  15. package/dist/NodeArrayProperty.svelte.d.ts +3 -5
  16. package/dist/NodeArrayProperty.svelte.d.ts.map +1 -1
  17. package/dist/NodeCaret.svelte +1 -1
  18. package/dist/NodeCaret.svelte.d.ts +6 -14
  19. package/dist/NodeCaret.svelte.d.ts.map +1 -1
  20. package/dist/NodeGap.svelte +60 -12
  21. package/dist/NodeGap.svelte.d.ts +7 -14
  22. package/dist/NodeGap.svelte.d.ts.map +1 -1
  23. package/dist/NodeGapMarkers.svelte +19 -10
  24. package/dist/NodeGapMarkers.svelte.d.ts +6 -11
  25. package/dist/NodeGapMarkers.svelte.d.ts.map +1 -1
  26. package/dist/NodeSelectionMarkers.svelte +20 -6
  27. package/dist/NodeSelectionMarkers.svelte.d.ts +2 -5
  28. package/dist/NodeSelectionMarkers.svelte.d.ts.map +1 -1
  29. package/dist/Session.svelte.d.ts +85 -116
  30. package/dist/Session.svelte.d.ts.map +1 -1
  31. package/dist/Session.svelte.js +543 -571
  32. package/dist/Svedit.svelte +243 -216
  33. package/dist/Svedit.svelte.d.ts +30 -10
  34. package/dist/Svedit.svelte.d.ts.map +1 -1
  35. package/dist/{AnnotatedTextProperty.svelte → TextProperty.svelte} +71 -56
  36. package/dist/TextProperty.svelte.d.ts +5 -0
  37. package/dist/TextProperty.svelte.d.ts.map +1 -0
  38. package/dist/Transaction.svelte.d.ts +101 -126
  39. package/dist/Transaction.svelte.d.ts.map +1 -1
  40. package/dist/Transaction.svelte.js +843 -827
  41. package/dist/UnknownNode.svelte +4 -3
  42. package/dist/UnknownNode.svelte.d.ts +5 -9
  43. package/dist/UnknownNode.svelte.d.ts.map +1 -1
  44. package/dist/doc_utils.d.ts +109 -122
  45. package/dist/doc_utils.d.ts.map +1 -1
  46. package/dist/doc_utils.js +726 -698
  47. package/dist/index.d.ts +20 -17
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js +2 -18
  50. package/dist/node_visibility.svelte.d.ts +132 -5
  51. package/dist/node_visibility.svelte.d.ts.map +1 -1
  52. package/dist/node_visibility.svelte.js +535 -642
  53. package/dist/paste_utils.d.ts +11 -43
  54. package/dist/paste_utils.d.ts.map +1 -1
  55. package/dist/paste_utils.js +111 -149
  56. package/dist/transforms.svelte.d.ts +9 -7
  57. package/dist/transforms.svelte.d.ts.map +1 -1
  58. package/dist/transforms.svelte.js +126 -152
  59. package/dist/types.d.ts +329 -291
  60. package/dist/types.d.ts.map +1 -0
  61. package/dist/types.js +1 -0
  62. package/dist/utils.d.ts +79 -95
  63. package/dist/utils.d.ts.map +1 -1
  64. package/dist/utils.js +392 -305
  65. package/package.json +69 -67
  66. package/src/lib/{Command.svelte.js → Command.svelte.ts} +111 -41
  67. package/src/lib/CustomProperty.svelte +3 -5
  68. package/src/lib/{KeyMapper.svelte.js → KeyMapper.svelte.ts} +28 -11
  69. package/src/lib/Node.svelte +40 -9
  70. package/src/lib/NodeArrayProperty.svelte +142 -18
  71. package/src/lib/NodeCaret.svelte +1 -1
  72. package/src/lib/NodeGap.svelte +60 -12
  73. package/src/lib/NodeGapMarkers.svelte +19 -10
  74. package/src/lib/NodeSelectionMarkers.svelte +20 -6
  75. package/src/lib/Session.svelte.ts +656 -0
  76. package/src/lib/Svedit.svelte +243 -216
  77. package/src/lib/{AnnotatedTextProperty.svelte → TextProperty.svelte} +71 -56
  78. package/src/lib/Transaction.svelte.ts +1020 -0
  79. package/src/lib/UnknownNode.svelte +4 -3
  80. package/src/lib/doc_utils.ts +1120 -0
  81. package/src/lib/{index.js → index.ts} +48 -1
  82. package/src/lib/node_visibility.svelte.ts +658 -0
  83. package/src/lib/{paste_utils.js → paste_utils.ts} +51 -59
  84. package/src/lib/{transforms.svelte.js → transforms.svelte.ts} +44 -27
  85. package/src/lib/{types.d.ts → types.ts} +250 -103
  86. package/src/lib/utils.ts +660 -0
  87. package/dist/AnnotatedTextProperty.svelte.d.ts +0 -7
  88. package/dist/AnnotatedTextProperty.svelte.d.ts.map +0 -1
  89. package/src/lib/Session.svelte.js +0 -575
  90. package/src/lib/Transaction.svelte.js +0 -846
  91. package/src/lib/doc_utils.js +0 -807
  92. package/src/lib/node_visibility.svelte.js +0 -718
  93. package/src/lib/utils.js +0 -465
@@ -1,165 +1,139 @@
1
- import { split_annotated_text, join_annotated_text, get_char_length } from './utils.js';
1
+ import { split_text, join_text, get_char_length } from './utils.js';
2
2
  import { get_default_node_type } from './doc_utils.js';
3
-
4
3
  /**
5
4
  * Set multiple properties on a node via a transaction.
6
5
  *
7
- * @param {object} tr - The transaction
8
- * @param {Array} path - Path to the node
9
- * @param {Record<string, any>} properties - Key/value pairs to set
6
+ * @param tr - The transaction
7
+ * @param path - Path to the node
8
+ * @param properties - Key/value pairs to set
10
9
  */
11
10
  export function set_properties(tr, path, properties) {
12
- for (const [key, value] of Object.entries(properties)) {
13
- tr.set([...path, key], value);
14
- }
11
+ for (const [key, value] of Object.entries(properties)) {
12
+ tr.set([...path, key], value);
13
+ }
15
14
  }
16
-
17
15
  export function break_text_node(tr) {
18
- // Keep a reference of the original selection (before any transforms are applied)
19
- const selection = tr.selection;
20
- // First we need to ensure we have a text selection
21
- if (selection.type !== 'text') return false;
22
-
23
- // Next, we need to determine if the enclosing node is a pure text node (e.g. paragraph),
24
- // which is wrapped inside a node_array (e.g. page.body)
25
-
26
- // Owner of the text property (e.g. paragraph)
27
- const node = tr.get(selection.path.slice(0, -1));
28
- if (tr.kind(node) !== 'text') return false;
29
- const is_inside_node_array = tr.inspect(selection.path.slice(0, -2))?.type === 'node_array';
30
- if (!is_inside_node_array) return false; // Do nothing if we're not inside a node_array
31
- const node_array_prop = selection.path.at(-3);
32
- // Get the node that owns the node_array property (e.g. a page.body)
33
- const node_array_node = tr.get(selection.path.slice(0, -3));
34
-
35
-
36
- // Delete selection unless collapsed
37
- if (selection.anchor_offset !== selection.focus_offset) {
38
- tr.delete_selection();
39
- }
40
-
41
- const split_at_position = tr.selection.anchor_offset;
42
- const content = tr.get(selection.path);
43
- const [left_text, right_text] = split_annotated_text(content, split_at_position);
44
-
45
- tr.set([node.id, 'content'], left_text);
46
-
47
- const node_insert_position = {
48
- type: 'node',
49
- path: tr.selection.path.slice(0, -2),
50
- anchor_offset: parseInt(tr.selection.path.at(-2), 10) + 1,
51
- focus_offset: parseInt(tr.selection.path.at(-2), 10) + 1
52
- };
53
-
54
- // TODO: Only use default_node_type when caret is at the end of
55
- const node_array_property_definition =
56
- tr.schema[node_array_node.type].properties[node_array_prop];
57
- const target_node_type = get_default_node_type(node_array_property_definition);
58
-
59
- if (!target_node_type) {
60
- console.warn(
61
- 'Cannot determine target node type for break_text_node - no default_ref_type and multiple node_types'
62
- );
63
- return false;
64
- }
65
-
66
- tr.set_selection(node_insert_position);
67
-
68
- tr.config.inserters[target_node_type](tr, right_text);
69
- return true;
16
+ // Keep a reference of the original selection (before any transforms are applied)
17
+ const selection = tr.selection;
18
+ // First we need to ensure we have a text selection
19
+ if (selection.type !== 'text')
20
+ return false;
21
+ // Next, we need to determine if the enclosing node is a pure text node (e.g. paragraph),
22
+ // which is wrapped inside a node_array (e.g. page.body)
23
+ // Owner of the text property (e.g. paragraph)
24
+ const node = tr.get(selection.path.slice(0, -1));
25
+ if (tr.kind(node) !== 'text')
26
+ return false;
27
+ const is_inside_node_array = tr.inspect(selection.path.slice(0, -2))?.type === 'node_array';
28
+ if (!is_inside_node_array)
29
+ return false; // Do nothing if we're not inside a node_array
30
+ const node_array_prop = String(selection.path.at(-3));
31
+ // Get the node that owns the node_array property (e.g. a page.body)
32
+ const node_array_node = tr.get(selection.path.slice(0, -3));
33
+ // Delete selection unless collapsed
34
+ if (selection.anchor_offset !== selection.focus_offset) {
35
+ tr.delete_selection();
36
+ }
37
+ // After delete_selection the selection is still a (collapsed) text selection
38
+ const split_at_position = tr.selection.anchor_offset;
39
+ const content = tr.get(selection.path);
40
+ const [left_text, right_text] = split_text(content, split_at_position);
41
+ tr.set([node.id, 'content'], left_text);
42
+ const node_insert_position = {
43
+ type: 'node',
44
+ path: tr.selection.path.slice(0, -2),
45
+ anchor_offset: tr.selection.path.at(-2) + 1,
46
+ focus_offset: tr.selection.path.at(-2) + 1
47
+ };
48
+ // TODO: Only use default_node_type when caret is at the end of
49
+ const node_array_property_definition = tr.schema[node_array_node.type].properties[node_array_prop];
50
+ const target_node_type = get_default_node_type(node_array_property_definition);
51
+ if (!target_node_type) {
52
+ console.warn('Cannot determine target node type for break_text_node - no default_ref_type and multiple node_types');
53
+ return false;
54
+ }
55
+ tr.set_selection(node_insert_position);
56
+ tr.config.inserters[target_node_type](tr, right_text);
57
+ return true;
70
58
  }
71
-
72
59
  export function join_text_node(tr) {
73
- // Keep a reference of the original selection (before any transforms are applied)
74
- const selection = tr.selection;
75
- // First we need to ensure we have a text selection
76
- if (selection.type !== 'text') return false;
77
-
78
- const node = tr.get(selection.path.slice(0, -1));
79
- if (tr.kind(node) !== 'text') return false;
80
- const is_inside_node_array = tr.inspect(selection.path.slice(0, -2))?.type === 'node_array';
81
- if (!is_inside_node_array) return false; // Do nothing if we're not inside a node_array
82
-
83
- const node_index = parseInt(tr.selection.path.at(-2), 10);
84
-
85
- // Determine if we can join with the previous node
86
- let can_join = false;
87
- let predecessor_node = null;
88
-
89
- if (node_index > 0) {
90
- const previous_text_path = [...tr.selection.path.slice(0, -2), node_index - 1];
91
- predecessor_node = tr.get(previous_text_path);
92
- can_join = tr.kind(predecessor_node) === 'text';
93
- }
94
-
95
- // Special behavior: if we can't join and current node is empty, delete it
96
- if (!can_join && node.content.text === '') {
97
- tr.set_selection({
98
- type: 'node',
99
- path: tr.selection.path.slice(0, -2),
100
- anchor_offset: node_index,
101
- focus_offset: node_index + 1
102
- });
103
- tr.delete_selection();
104
- return true;
105
- }
106
-
107
- // If we can't join for any reason, return false
108
- if (!can_join) {
109
- return false;
110
- }
111
-
112
- // Normal joining logic - both nodes are text nodes
113
- const previous_text_path = [...tr.selection.path.slice(0, -2), node_index - 1];
114
- const joined_text = join_annotated_text(predecessor_node.content, node.content);
115
-
116
- // Calculate caret position based on original predecessor content length
117
- const caret_position = get_char_length(predecessor_node.content.text);
118
-
119
- // First set the joined content on the predecessor node (preserves annotations)
120
- tr.set([predecessor_node.id, 'content'], joined_text);
121
-
122
- // Then delete the current node
123
- tr.set_selection({
124
- type: 'node',
125
- path: tr.selection.path.slice(0, -2),
126
- anchor_offset: node_index,
127
- focus_offset: node_index + 1
128
- });
129
-
130
- tr.delete_selection();
131
-
132
- // Finally set the caret position at the join point using the pre-calculated position
133
- tr.set_selection({
134
- type: 'text',
135
- path: [...previous_text_path, 'content'],
136
- anchor_offset: caret_position,
137
- focus_offset: caret_position
138
- });
139
- return true;
60
+ // Keep a reference of the original selection (before any transforms are applied)
61
+ const selection = tr.selection;
62
+ // First we need to ensure we have a text selection
63
+ if (selection.type !== 'text')
64
+ return false;
65
+ const node = tr.get(selection.path.slice(0, -1));
66
+ if (tr.kind(node) !== 'text')
67
+ return false;
68
+ const is_inside_node_array = tr.inspect(selection.path.slice(0, -2))?.type === 'node_array';
69
+ if (!is_inside_node_array)
70
+ return false; // Do nothing if we're not inside a node_array
71
+ const node_index = tr.selection.path.at(-2);
72
+ // Determine if we can join with the previous node
73
+ let can_join = false;
74
+ let predecessor_node = null;
75
+ if (node_index > 0) {
76
+ const previous_text_path = [...tr.selection.path.slice(0, -2), node_index - 1];
77
+ predecessor_node = tr.get(previous_text_path);
78
+ can_join = predecessor_node !== null && tr.kind(predecessor_node) === 'text';
79
+ }
80
+ // Special behavior: if we can't join and current node is empty, delete it
81
+ if (!can_join && node.content.content === '') {
82
+ tr.set_selection({
83
+ type: 'node',
84
+ path: tr.selection.path.slice(0, -2),
85
+ anchor_offset: node_index,
86
+ focus_offset: node_index + 1
87
+ });
88
+ tr.delete_selection();
89
+ return true;
90
+ }
91
+ // If we can't join for any reason, return false
92
+ if (!can_join || !predecessor_node) {
93
+ return false;
94
+ }
95
+ // Normal joining logic - both nodes are text nodes
96
+ const previous_text_path = [...tr.selection.path.slice(0, -2), node_index - 1];
97
+ const joined_text = join_text(predecessor_node.content, node.content);
98
+ // Calculate caret position based on original predecessor content length
99
+ const caret_position = get_char_length(predecessor_node.content.content);
100
+ // First set the joined content on the predecessor node (preserves annotations)
101
+ tr.set([predecessor_node.id, 'content'], joined_text);
102
+ // Then delete the current node
103
+ tr.set_selection({
104
+ type: 'node',
105
+ path: tr.selection.path.slice(0, -2),
106
+ anchor_offset: node_index,
107
+ focus_offset: node_index + 1
108
+ });
109
+ tr.delete_selection();
110
+ // Finally set the caret position at the join point using the pre-calculated position
111
+ tr.set_selection({
112
+ type: 'text',
113
+ path: [...previous_text_path, 'content'],
114
+ anchor_offset: caret_position,
115
+ focus_offset: caret_position
116
+ });
117
+ return true;
140
118
  }
141
-
142
119
  export function insert_default_node(tr) {
143
- const selection = tr.selection;
144
-
145
- // Only work with collapsed node selections
146
- if (selection?.type !== 'node' || selection.anchor_offset !== selection.focus_offset) {
147
- return false;
148
- }
149
-
150
- const path = selection.path;
151
- const node_array_node = tr.get(path.slice(0, -1));
152
- const property_name = path.at(-1);
153
-
154
- // Get the definition for this property
155
- const property_definition = tr.schema[node_array_node.type].properties[property_name];
156
- const default_type = get_default_node_type(property_definition);
157
-
158
- // Use the inserter function if available
159
- if (tr.config?.inserters?.[default_type]) {
160
- tr.config.inserters[default_type](tr);
161
- return true;
162
- } else {
163
- throw new Error(`No inserter function available for default node type '${default_type}'`);
164
- }
120
+ const selection = tr.selection;
121
+ // Only work with collapsed node selections
122
+ if (selection?.type !== 'node' || selection.anchor_offset !== selection.focus_offset) {
123
+ return false;
124
+ }
125
+ const path = selection.path;
126
+ const node_array_node = tr.get(path.slice(0, -1));
127
+ const property_name = String(path.at(-1));
128
+ // Get the definition for this property
129
+ const property_definition = tr.schema[node_array_node.type].properties[property_name];
130
+ const default_type = get_default_node_type(property_definition);
131
+ // Use the inserter function if available
132
+ if (tr.config?.inserters?.[default_type]) {
133
+ tr.config.inserters[default_type](tr);
134
+ return true;
135
+ }
136
+ else {
137
+ throw new Error(`No inserter function available for default node type '${default_type}'`);
138
+ }
165
139
  }