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.
- package/README.md +260 -82
- package/dist/Command.svelte.d.ts +54 -29
- package/dist/Command.svelte.d.ts.map +1 -1
- package/dist/Command.svelte.js +286 -283
- 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 +40 -9
- package/dist/Node.svelte.d.ts +3 -5
- package/dist/Node.svelte.d.ts.map +1 -1
- package/dist/NodeArrayProperty.svelte +142 -18
- package/dist/NodeArrayProperty.svelte.d.ts +3 -5
- package/dist/NodeArrayProperty.svelte.d.ts.map +1 -1
- package/dist/NodeCaret.svelte +1 -1
- package/dist/NodeCaret.svelte.d.ts +6 -14
- package/dist/NodeCaret.svelte.d.ts.map +1 -1
- package/dist/NodeGap.svelte +60 -12
- package/dist/NodeGap.svelte.d.ts +7 -14
- package/dist/NodeGap.svelte.d.ts.map +1 -1
- package/dist/NodeGapMarkers.svelte +19 -10
- package/dist/NodeGapMarkers.svelte.d.ts +6 -11
- package/dist/NodeGapMarkers.svelte.d.ts.map +1 -1
- package/dist/NodeSelectionMarkers.svelte +20 -6
- package/dist/NodeSelectionMarkers.svelte.d.ts +2 -5
- package/dist/NodeSelectionMarkers.svelte.d.ts.map +1 -1
- package/dist/Session.svelte.d.ts +85 -116
- package/dist/Session.svelte.d.ts.map +1 -1
- package/dist/Session.svelte.js +543 -571
- package/dist/Svedit.svelte +243 -216
- package/dist/Svedit.svelte.d.ts +30 -10
- package/dist/Svedit.svelte.d.ts.map +1 -1
- package/dist/{AnnotatedTextProperty.svelte → TextProperty.svelte} +71 -56
- package/dist/TextProperty.svelte.d.ts +5 -0
- package/dist/TextProperty.svelte.d.ts.map +1 -0
- package/dist/Transaction.svelte.d.ts +101 -126
- package/dist/Transaction.svelte.d.ts.map +1 -1
- package/dist/Transaction.svelte.js +843 -827
- 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 +109 -122
- package/dist/doc_utils.d.ts.map +1 -1
- package/dist/doc_utils.js +726 -698
- package/dist/index.d.ts +20 -17
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -18
- package/dist/node_visibility.svelte.d.ts +132 -5
- package/dist/node_visibility.svelte.d.ts.map +1 -1
- package/dist/node_visibility.svelte.js +535 -642
- package/dist/paste_utils.d.ts +11 -43
- package/dist/paste_utils.d.ts.map +1 -1
- package/dist/paste_utils.js +111 -149
- package/dist/transforms.svelte.d.ts +9 -7
- package/dist/transforms.svelte.d.ts.map +1 -1
- package/dist/transforms.svelte.js +126 -152
- package/dist/types.d.ts +329 -291
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/dist/utils.d.ts +79 -95
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +392 -305
- package/package.json +69 -67
- package/src/lib/{Command.svelte.js → Command.svelte.ts} +111 -41
- package/src/lib/CustomProperty.svelte +3 -5
- package/src/lib/{KeyMapper.svelte.js → KeyMapper.svelte.ts} +28 -11
- package/src/lib/Node.svelte +40 -9
- package/src/lib/NodeArrayProperty.svelte +142 -18
- package/src/lib/NodeCaret.svelte +1 -1
- package/src/lib/NodeGap.svelte +60 -12
- package/src/lib/NodeGapMarkers.svelte +19 -10
- package/src/lib/NodeSelectionMarkers.svelte +20 -6
- package/src/lib/Session.svelte.ts +656 -0
- package/src/lib/Svedit.svelte +243 -216
- package/src/lib/{AnnotatedTextProperty.svelte → TextProperty.svelte} +71 -56
- package/src/lib/Transaction.svelte.ts +1020 -0
- package/src/lib/UnknownNode.svelte +4 -3
- package/src/lib/doc_utils.ts +1120 -0
- package/src/lib/{index.js → index.ts} +48 -1
- package/src/lib/node_visibility.svelte.ts +658 -0
- package/src/lib/{paste_utils.js → paste_utils.ts} +51 -59
- package/src/lib/{transforms.svelte.js → transforms.svelte.ts} +44 -27
- package/src/lib/{types.d.ts → types.ts} +250 -103
- package/src/lib/utils.ts +660 -0
- package/dist/AnnotatedTextProperty.svelte.d.ts +0 -7
- package/dist/AnnotatedTextProperty.svelte.d.ts.map +0 -1
- package/src/lib/Session.svelte.js +0 -575
- package/src/lib/Transaction.svelte.js +0 -846
- package/src/lib/doc_utils.js +0 -807
- package/src/lib/node_visibility.svelte.js +0 -718
- package/src/lib/utils.js +0 -465
package/dist/Command.svelte.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { insert_default_node, break_text_node } from './transforms.svelte.js';
|
|
2
|
-
import {
|
|
2
|
+
import { can_switch_mark_type, get_selected_range_types } from './doc_utils.js';
|
|
3
3
|
import { is_selection_collapsed, get_char_length, char_slice } from './utils.js';
|
|
4
|
-
|
|
5
4
|
/**
|
|
6
5
|
* Base class for commands that can be executed in response to user actions
|
|
7
6
|
* like keyboard shortcuts, menu items, or toolbar buttons.
|
|
@@ -25,330 +24,334 @@ import { is_selection_collapsed, get_char_length, char_slice } from './utils.js'
|
|
|
25
24
|
* ```
|
|
26
25
|
*/
|
|
27
26
|
export default class Command {
|
|
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
|
-
* @returns {void | Promise<void>}
|
|
59
|
-
*/
|
|
60
|
-
execute() {
|
|
61
|
-
throw new Error('Not implemented');
|
|
62
|
-
}
|
|
27
|
+
context;
|
|
28
|
+
/**
|
|
29
|
+
* Derived state that indicates if the command is disabled.
|
|
30
|
+
* Automatically computed from is_enabled().
|
|
31
|
+
*/
|
|
32
|
+
disabled = $derived(!this.is_enabled());
|
|
33
|
+
/**
|
|
34
|
+
* Creates a new Command instance.
|
|
35
|
+
*
|
|
36
|
+
* @param context - The context object providing access to application state
|
|
37
|
+
*/
|
|
38
|
+
constructor(context) {
|
|
39
|
+
this.context = context;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Determines if the command can currently be executed.
|
|
43
|
+
* Override this method to implement command-specific logic.
|
|
44
|
+
*/
|
|
45
|
+
is_enabled() {
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Executes the command.
|
|
50
|
+
* Override this method to implement the command's behavior.
|
|
51
|
+
* Can be async for commands that need to perform asynchronous operations.
|
|
52
|
+
*/
|
|
53
|
+
execute() {
|
|
54
|
+
throw new Error('Not implemented');
|
|
55
|
+
}
|
|
63
56
|
}
|
|
64
|
-
|
|
65
57
|
/**
|
|
66
58
|
* Command that undoes the last change to the document.
|
|
67
59
|
*/
|
|
68
60
|
export class UndoCommand extends Command {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
61
|
+
is_enabled() {
|
|
62
|
+
return this.context.editable && this.context.session.can_undo;
|
|
63
|
+
}
|
|
64
|
+
execute() {
|
|
65
|
+
this.context.session.undo();
|
|
66
|
+
}
|
|
76
67
|
}
|
|
77
|
-
|
|
78
68
|
/**
|
|
79
69
|
* Command that redoes the last undone change to the document.
|
|
80
70
|
*/
|
|
81
71
|
export class RedoCommand extends Command {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
72
|
+
is_enabled() {
|
|
73
|
+
return this.context.editable && this.context.session.can_redo;
|
|
74
|
+
}
|
|
75
|
+
execute() {
|
|
76
|
+
this.context.session.redo();
|
|
77
|
+
}
|
|
89
78
|
}
|
|
90
|
-
|
|
91
79
|
/**
|
|
92
80
|
* Command that selects the parent of the current selection.
|
|
93
81
|
* Useful for navigating up the document hierarchy.
|
|
94
82
|
*/
|
|
95
83
|
export class SelectParentCommand extends Command {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
execute() {
|
|
105
|
-
this.context.session.select_parent();
|
|
106
|
-
}
|
|
84
|
+
is_enabled() {
|
|
85
|
+
return Boolean(this.context.editable &&
|
|
86
|
+
this.context.session.selection &&
|
|
87
|
+
this.context.session.selection.path.length > 3);
|
|
88
|
+
}
|
|
89
|
+
execute() {
|
|
90
|
+
this.context.session.select_parent();
|
|
91
|
+
}
|
|
107
92
|
}
|
|
108
|
-
|
|
109
93
|
/**
|
|
110
|
-
* Generic command that toggles
|
|
111
|
-
*
|
|
94
|
+
* Generic command that toggles a mark on the current text or node selection.
|
|
95
|
+
*
|
|
96
|
+
* Marks are mutually exclusive, so all touched marks compete: same-type marks
|
|
97
|
+
* are removed, a single touched property-less mark may switch type, and mixed
|
|
98
|
+
* touched types disable the command. Annotations never affect mark toggling.
|
|
99
|
+
*/
|
|
100
|
+
export class ToggleMarkCommand extends Command {
|
|
101
|
+
node_type;
|
|
102
|
+
constructor(node_type, context) {
|
|
103
|
+
super(context);
|
|
104
|
+
this.node_type = node_type;
|
|
105
|
+
}
|
|
106
|
+
active = $derived(this.is_active());
|
|
107
|
+
is_active() {
|
|
108
|
+
const selected_marks = this.context.session.selected_marks;
|
|
109
|
+
return (selected_marks.length > 0 && selected_marks.every(({ node }) => node?.type === this.node_type));
|
|
110
|
+
}
|
|
111
|
+
is_enabled() {
|
|
112
|
+
const { session, editable } = this.context;
|
|
113
|
+
const selection = session.selection;
|
|
114
|
+
const is_valid_selection = selection?.type === 'text' ||
|
|
115
|
+
(selection?.type === 'node' && !is_selection_collapsed(selection));
|
|
116
|
+
const mark_type_is_allowed = session.available_mark_types.includes(this.node_type);
|
|
117
|
+
const selected_marks = session.selected_marks;
|
|
118
|
+
const selected_mark_types = get_selected_range_types(selected_marks);
|
|
119
|
+
if (!editable || !is_valid_selection || !mark_type_is_allowed)
|
|
120
|
+
return false;
|
|
121
|
+
if (selected_mark_types.size > 1)
|
|
122
|
+
return false;
|
|
123
|
+
if (selected_marks.length === 0) {
|
|
124
|
+
return Boolean(selection && !is_selection_collapsed(selection));
|
|
125
|
+
}
|
|
126
|
+
const first_selected_mark = selected_marks[0];
|
|
127
|
+
const selected_mark_type = first_selected_mark.node.type;
|
|
128
|
+
if (selected_mark_type === this.node_type)
|
|
129
|
+
return true;
|
|
130
|
+
if (selected_marks.length !== 1)
|
|
131
|
+
return false;
|
|
132
|
+
return can_switch_mark_type(session.schema, selected_mark_type, this.node_type);
|
|
133
|
+
}
|
|
134
|
+
execute() {
|
|
135
|
+
this.context.session.apply(this.context.session.tr.toggle_mark(this.node_type));
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Generic command that toggles an annotation on the current text or node selection.
|
|
140
|
+
*
|
|
141
|
+
* Annotations only compete with touched annotations of the same type: touched
|
|
142
|
+
* same-type annotations are removed, otherwise a new annotation is created.
|
|
143
|
+
* Marks and other annotation types never block the toggle.
|
|
112
144
|
*/
|
|
113
145
|
export class ToggleAnnotationCommand extends Command {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
execute() {
|
|
149
|
-
this.context.session.apply(this.context.session.tr.annotate_text(this.node_type));
|
|
150
|
-
}
|
|
146
|
+
node_type;
|
|
147
|
+
constructor(node_type, context) {
|
|
148
|
+
super(context);
|
|
149
|
+
this.node_type = node_type;
|
|
150
|
+
}
|
|
151
|
+
active = $derived(this.is_active());
|
|
152
|
+
// Mirrors the filter in tr.toggle_annotation, so active/enabled match what
|
|
153
|
+
// execute() does.
|
|
154
|
+
relevant_annotations() {
|
|
155
|
+
return this.context.session.selected_annotations.filter(({ node }) => node?.type === this.node_type);
|
|
156
|
+
}
|
|
157
|
+
is_active() {
|
|
158
|
+
return this.relevant_annotations().length > 0;
|
|
159
|
+
}
|
|
160
|
+
is_enabled() {
|
|
161
|
+
const { session, editable } = this.context;
|
|
162
|
+
const selection = session.selection;
|
|
163
|
+
const is_valid_selection = selection?.type === 'text' ||
|
|
164
|
+
(selection?.type === 'node' && !is_selection_collapsed(selection));
|
|
165
|
+
const annotation_type_is_allowed = session.available_annotation_types.includes(this.node_type);
|
|
166
|
+
if (!editable || !is_valid_selection || !annotation_type_is_allowed)
|
|
167
|
+
return false;
|
|
168
|
+
// Removing touched same-type annotations also works from a collapsed
|
|
169
|
+
// caret inside the annotation; creating a new one requires an expanded
|
|
170
|
+
// selection.
|
|
171
|
+
if (this.relevant_annotations().length > 0)
|
|
172
|
+
return true;
|
|
173
|
+
return Boolean(selection && !is_selection_collapsed(selection));
|
|
174
|
+
}
|
|
175
|
+
execute() {
|
|
176
|
+
this.context.session.apply(this.context.session.tr.toggle_annotation(this.node_type));
|
|
177
|
+
}
|
|
151
178
|
}
|
|
152
|
-
|
|
153
179
|
/**
|
|
154
180
|
* Command that adds a new line character at the current caret position.
|
|
155
181
|
* Only works in text selections where newlines are allowed.
|
|
156
182
|
*/
|
|
157
183
|
export class AddNewLineCommand extends Command {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
tr.insert_text(`\n${indentation}`);
|
|
189
|
-
session.apply(tr);
|
|
190
|
-
}
|
|
184
|
+
is_enabled() {
|
|
185
|
+
const session = this.context.session;
|
|
186
|
+
const selection = session.selection;
|
|
187
|
+
return (this.context.editable &&
|
|
188
|
+
selection?.type === 'text' &&
|
|
189
|
+
session.inspect(selection.path).allow_newlines);
|
|
190
|
+
}
|
|
191
|
+
execute() {
|
|
192
|
+
const session = this.context.session;
|
|
193
|
+
const selection = session.selection;
|
|
194
|
+
if (!selection || selection.type !== 'text')
|
|
195
|
+
return;
|
|
196
|
+
if (!session.inspect(selection.path).allow_newlines)
|
|
197
|
+
return;
|
|
198
|
+
const tr = session.tr;
|
|
199
|
+
if (selection.anchor_offset !== selection.focus_offset) {
|
|
200
|
+
tr.delete_selection();
|
|
201
|
+
}
|
|
202
|
+
if (tr.selection.type !== 'text')
|
|
203
|
+
return;
|
|
204
|
+
const collapsed_offset = tr.selection.anchor_offset;
|
|
205
|
+
const content = tr.get(tr.selection.path);
|
|
206
|
+
const text_before_caret = char_slice(content.content, 0, collapsed_offset);
|
|
207
|
+
const line_start_index = text_before_caret.lastIndexOf('\n') + 1;
|
|
208
|
+
const current_line_prefix = text_before_caret.slice(line_start_index);
|
|
209
|
+
const indentation_match = current_line_prefix.match(/^[\t ]*/);
|
|
210
|
+
const indentation = indentation_match ? indentation_match[0] : '';
|
|
211
|
+
tr.insert_text(`\n${indentation}`);
|
|
212
|
+
session.apply(tr);
|
|
213
|
+
}
|
|
191
214
|
}
|
|
192
|
-
|
|
193
215
|
/**
|
|
194
216
|
* Command that breaks a text node at the caret position.
|
|
195
217
|
* Creates a new node and splits the content between the current and new node.
|
|
196
218
|
* Only works in text selections.
|
|
197
219
|
*/
|
|
198
220
|
export class BreakTextNodeCommand extends Command {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
}
|
|
221
|
+
is_enabled() {
|
|
222
|
+
const session = this.context.session;
|
|
223
|
+
const selection = session.selection;
|
|
224
|
+
if (!this.context.editable || selection?.type !== 'text')
|
|
225
|
+
return false;
|
|
226
|
+
const owner_node = session.get(selection.path.slice(0, -1));
|
|
227
|
+
const owner_node_schema = owner_node ? session.schema[owner_node.type] : null;
|
|
228
|
+
if (!owner_node_schema || owner_node_schema.kind !== 'text')
|
|
229
|
+
return false;
|
|
230
|
+
return session.inspect(selection.path.slice(0, -2))?.type === 'node_array';
|
|
231
|
+
}
|
|
232
|
+
execute() {
|
|
233
|
+
const tr = this.context.session.tr;
|
|
234
|
+
if (break_text_node(tr)) {
|
|
235
|
+
this.context.session.apply(tr);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
217
238
|
}
|
|
218
|
-
|
|
219
239
|
/**
|
|
220
240
|
* Command that selects all content in the current context.
|
|
221
241
|
* Progressively expands selection from text → node → parent node array.
|
|
222
242
|
*/
|
|
223
243
|
export class SelectAllCommand extends Command {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
if (is_inside_node_array) {
|
|
321
|
-
const node_index = parseInt(node_path.at(-1));
|
|
322
|
-
session.selection = {
|
|
323
|
-
type: 'node',
|
|
324
|
-
path: node_path.slice(0, -1),
|
|
325
|
-
anchor_offset: node_index,
|
|
326
|
-
focus_offset: node_index + 1
|
|
327
|
-
};
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
// Stop expanding - property is not in a selectable node_array
|
|
331
|
-
}
|
|
332
|
-
}
|
|
244
|
+
is_enabled() {
|
|
245
|
+
return Boolean(this.context.editable && this.context.session.selection);
|
|
246
|
+
}
|
|
247
|
+
execute() {
|
|
248
|
+
const session = this.context.session;
|
|
249
|
+
const selection = session.selection;
|
|
250
|
+
if (!selection) {
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
if (selection.type === 'text') {
|
|
254
|
+
const text_content = session.get(selection.path);
|
|
255
|
+
const text_length = get_char_length(text_content.content);
|
|
256
|
+
// Check if all text is already selected
|
|
257
|
+
const is_all_text_selected = Math.min(selection.anchor_offset, selection.focus_offset) === 0 &&
|
|
258
|
+
Math.max(selection.anchor_offset, selection.focus_offset) === text_length;
|
|
259
|
+
if (!is_all_text_selected) {
|
|
260
|
+
// Select all text in the current text node
|
|
261
|
+
session.selection = {
|
|
262
|
+
type: 'text',
|
|
263
|
+
path: selection.path,
|
|
264
|
+
anchor_offset: 0,
|
|
265
|
+
focus_offset: text_length
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
else {
|
|
269
|
+
// All text is selected, move up to select the containing node
|
|
270
|
+
const node_path = selection.path.slice(0, -1); // Remove the property name (e.g., 'content')
|
|
271
|
+
// Check if we have enough path segments and if we're inside a node_array
|
|
272
|
+
if (node_path.length >= 2) {
|
|
273
|
+
const is_inside_node_array = session.inspect(node_path.slice(0, -1))?.type === 'node_array';
|
|
274
|
+
if (is_inside_node_array) {
|
|
275
|
+
const node_index = node_path.at(-1);
|
|
276
|
+
session.selection = {
|
|
277
|
+
type: 'node',
|
|
278
|
+
path: node_path.slice(0, -1),
|
|
279
|
+
anchor_offset: node_index,
|
|
280
|
+
focus_offset: node_index + 1
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
// Stop expanding - text is not in a selectable node_array
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
else if (selection.type === 'node') {
|
|
288
|
+
const node_array_path = selection.path;
|
|
289
|
+
const node_array = session.get(node_array_path);
|
|
290
|
+
// Check if the entire node_array is already selected
|
|
291
|
+
const is_entire_node_array_selected = Math.min(selection.anchor_offset, selection.focus_offset) === 0 &&
|
|
292
|
+
Math.max(selection.anchor_offset, selection.focus_offset) === node_array.nodes.length;
|
|
293
|
+
if (!is_entire_node_array_selected) {
|
|
294
|
+
// Select the entire node_array
|
|
295
|
+
session.selection = {
|
|
296
|
+
type: 'node',
|
|
297
|
+
path: node_array_path,
|
|
298
|
+
anchor_offset: 0,
|
|
299
|
+
focus_offset: node_array.nodes.length
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
else {
|
|
303
|
+
// Entire node_array is selected, try to move up to parent node_array
|
|
304
|
+
const parent_path = node_array_path.slice(0, -1);
|
|
305
|
+
// Check if we have enough path segments and if parent is a valid node_array
|
|
306
|
+
if (parent_path.length >= 2) {
|
|
307
|
+
const is_parent_node_array = session.inspect(parent_path.slice(0, -1))?.type === 'node_array';
|
|
308
|
+
if (is_parent_node_array) {
|
|
309
|
+
const parent_node_index = parent_path.at(-1);
|
|
310
|
+
session.selection = {
|
|
311
|
+
type: 'node',
|
|
312
|
+
path: parent_path.slice(0, -1),
|
|
313
|
+
anchor_offset: parent_node_index,
|
|
314
|
+
focus_offset: parent_node_index + 1
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
// Stop expanding - we've reached the top level
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
else if (selection.type === 'property') {
|
|
322
|
+
// For property selections, select the containing node
|
|
323
|
+
const node_path = selection.path.slice(0, -1);
|
|
324
|
+
// Check if we have enough path segments and if we're inside a node_array
|
|
325
|
+
if (node_path.length >= 2) {
|
|
326
|
+
const is_inside_node_array = session.inspect(node_path.slice(0, -1))?.type === 'node_array';
|
|
327
|
+
if (is_inside_node_array) {
|
|
328
|
+
const node_index = node_path.at(-1);
|
|
329
|
+
session.selection = {
|
|
330
|
+
type: 'node',
|
|
331
|
+
path: node_path.slice(0, -1),
|
|
332
|
+
anchor_offset: node_index,
|
|
333
|
+
focus_offset: node_index + 1
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
// Stop expanding - property is not in a selectable node_array
|
|
338
|
+
}
|
|
339
|
+
}
|
|
333
340
|
}
|
|
334
|
-
|
|
335
341
|
/**
|
|
336
342
|
* Command that inserts a default node at the current caret position.
|
|
337
343
|
* Only works when a collapsed node selection is active.
|
|
338
344
|
*/
|
|
339
345
|
export class InsertDefaultNodeCommand extends Command {
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
insert_default_node(tr);
|
|
352
|
-
this.context.session.apply(tr);
|
|
353
|
-
}
|
|
346
|
+
is_enabled() {
|
|
347
|
+
const selection = this.context.session.selection;
|
|
348
|
+
return (this.context.editable &&
|
|
349
|
+
selection?.type === 'node' &&
|
|
350
|
+
selection.anchor_offset === selection.focus_offset);
|
|
351
|
+
}
|
|
352
|
+
execute() {
|
|
353
|
+
const tr = this.context.session.tr;
|
|
354
|
+
insert_default_node(tr);
|
|
355
|
+
this.context.session.apply(tr);
|
|
356
|
+
}
|
|
354
357
|
}
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
<script>
|
|
1
|
+
<script lang="ts">
|
|
2
2
|
import { serialize_path } from './utils.js';
|
|
3
|
+
import type { CustomPropertyProps } from './types.js';
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
/** @type {CustomPropertyProps} */
|
|
7
|
-
let { path, tag = 'div', class: css_class, children, style, ...rest } = $props();
|
|
5
|
+
let { path, tag = 'div', class: css_class, children, style, ...rest }: CustomPropertyProps = $props();
|
|
8
6
|
let path_str = $derived(serialize_path(path));
|
|
9
7
|
|
|
10
8
|
</script>
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
type CustomProperty = {
|
|
3
|
-
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
-
$set?(props: Partial<CustomPropertyProps>): void;
|
|
5
|
-
};
|
|
1
|
+
import type { CustomPropertyProps } from './types.js';
|
|
6
2
|
declare const CustomProperty: import("svelte").Component<CustomPropertyProps, {}, "">;
|
|
7
|
-
|
|
3
|
+
type CustomProperty = ReturnType<typeof CustomProperty>;
|
|
4
|
+
export default CustomProperty;
|
|
8
5
|
//# sourceMappingURL=CustomProperty.svelte.d.ts.map
|