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