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.
- package/README.md +35 -22
- package/dist/Command.svelte.d.ts +38 -30
- package/dist/Command.svelte.d.ts.map +1 -1
- package/dist/Command.svelte.js +271 -326
- package/dist/CustomProperty.svelte +3 -5
- package/dist/CustomProperty.svelte.d.ts +3 -6
- package/dist/CustomProperty.svelte.d.ts.map +1 -1
- package/dist/KeyMapper.svelte.d.ts +19 -6
- package/dist/KeyMapper.svelte.d.ts.map +1 -1
- package/dist/KeyMapper.svelte.js +95 -112
- package/dist/Node.svelte +16 -13
- package/dist/Node.svelte.d.ts +3 -5
- package/dist/Node.svelte.d.ts.map +1 -1
- package/dist/NodeArrayProperty.svelte +39 -22
- package/dist/NodeArrayProperty.svelte.d.ts +3 -5
- package/dist/NodeArrayProperty.svelte.d.ts.map +1 -1
- package/dist/NodeCaret.svelte +2 -2
- package/dist/NodeCaret.svelte.d.ts +6 -14
- package/dist/NodeCaret.svelte.d.ts.map +1 -1
- package/dist/NodeGap.svelte +12 -6
- package/dist/NodeGap.svelte.d.ts +7 -14
- package/dist/NodeGap.svelte.d.ts.map +1 -1
- package/dist/NodeGapMarkers.svelte +36 -14
- package/dist/NodeGapMarkers.svelte.d.ts +6 -11
- package/dist/NodeGapMarkers.svelte.d.ts.map +1 -1
- package/dist/NodeSelectionMarkers.svelte +21 -7
- package/dist/NodeSelectionMarkers.svelte.d.ts +2 -5
- package/dist/NodeSelectionMarkers.svelte.d.ts.map +1 -1
- package/dist/Session.svelte.d.ts +81 -129
- package/dist/Session.svelte.d.ts.map +1 -1
- package/dist/Session.svelte.js +542 -648
- package/dist/Svedit.svelte +146 -140
- package/dist/Svedit.svelte.d.ts +30 -10
- package/dist/Svedit.svelte.d.ts.map +1 -1
- package/dist/TextProperty.svelte +37 -23
- package/dist/TextProperty.svelte.d.ts +3 -5
- package/dist/TextProperty.svelte.d.ts.map +1 -1
- package/dist/Transaction.svelte.d.ts +64 -127
- package/dist/Transaction.svelte.d.ts.map +1 -1
- package/dist/Transaction.svelte.js +839 -1001
- package/dist/UnknownNode.svelte +4 -3
- package/dist/UnknownNode.svelte.d.ts +5 -9
- package/dist/UnknownNode.svelte.d.ts.map +1 -1
- package/dist/doc_utils.d.ts +67 -155
- package/dist/doc_utils.d.ts.map +1 -1
- package/dist/doc_utils.js +619 -863
- package/dist/index.d.ts +20 -17
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -17
- package/dist/node_visibility.svelte.d.ts +110 -9
- package/dist/node_visibility.svelte.d.ts.map +1 -1
- package/dist/node_visibility.svelte.js +473 -544
- package/dist/paste_utils.d.ts +11 -44
- package/dist/paste_utils.d.ts.map +1 -1
- package/dist/paste_utils.js +111 -152
- package/dist/styles/svedit-colors.css +8 -5
- package/dist/transforms.svelte.d.ts +9 -7
- package/dist/transforms.svelte.d.ts.map +1 -1
- package/dist/transforms.svelte.js +125 -150
- package/dist/types.d.ts +294 -309
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/dist/utils.d.ts +40 -119
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +346 -473
- package/package.json +70 -70
- package/src/lib/{Command.svelte.js → Command.svelte.ts} +29 -17
- package/src/lib/CustomProperty.svelte +3 -5
- package/src/lib/{KeyMapper.svelte.js → KeyMapper.svelte.ts} +28 -11
- package/src/lib/Node.svelte +16 -13
- package/src/lib/NodeArrayProperty.svelte +39 -22
- package/src/lib/NodeCaret.svelte +2 -2
- package/src/lib/NodeGap.svelte +12 -6
- package/src/lib/NodeGapMarkers.svelte +36 -14
- package/src/lib/NodeSelectionMarkers.svelte +21 -7
- package/src/lib/{Session.svelte.js → Session.svelte.ts} +130 -127
- package/src/lib/Svedit.svelte +146 -140
- package/src/lib/TextProperty.svelte +37 -23
- package/src/lib/{Transaction.svelte.js → Transaction.svelte.ts} +139 -143
- package/src/lib/UnknownNode.svelte +4 -3
- package/src/lib/{doc_utils.js → doc_utils.ts} +247 -206
- package/src/lib/{index.js → index.ts} +48 -0
- package/src/lib/{node_visibility.svelte.js → node_visibility.svelte.ts} +68 -92
- package/src/lib/{paste_utils.js → paste_utils.ts} +47 -58
- package/src/lib/styles/svedit-colors.css +8 -5
- package/src/lib/{transforms.svelte.js → transforms.svelte.ts} +39 -21
- package/src/lib/{types.d.ts → types.ts} +161 -66
- 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
|
|
8
|
-
* @param
|
|
9
|
-
* @param
|
|
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
|
-
|
|
13
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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
|
}
|