svedit 0.10.0 → 0.11.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 +387 -384
- package/dist/AnnotatedTextProperty.svelte +2 -5
- package/dist/AnnotatedTextProperty.svelte.d.ts.map +1 -1
- package/dist/Command.svelte.d.ts +0 -1
- package/dist/Command.svelte.d.ts.map +1 -1
- package/dist/Command.svelte.js +30 -5
- package/dist/KeyMapper.svelte.d.ts.map +1 -1
- package/dist/KeyMapper.svelte.js +18 -6
- package/dist/NodeArrayProperty.svelte +13 -25
- package/dist/NodeArrayProperty.svelte.d.ts.map +1 -1
- package/dist/NodeGap.svelte +49 -57
- package/dist/NodeGap.svelte.d.ts.map +1 -1
- package/dist/Svedit.svelte +189 -85
- package/dist/Svedit.svelte.d.ts.map +1 -1
- package/dist/doc_utils.d.ts +5 -0
- package/dist/doc_utils.d.ts.map +1 -1
- package/dist/doc_utils.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -0
- package/dist/paste_utils.d.ts +46 -0
- package/dist/paste_utils.d.ts.map +1 -0
- package/dist/paste_utils.js +167 -0
- package/dist/utils.d.ts +14 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +28 -7
- package/package.json +1 -1
- package/src/lib/AnnotatedTextProperty.svelte +2 -5
- package/src/lib/Command.svelte.js +30 -5
- package/src/lib/KeyMapper.svelte.js +18 -6
- package/src/lib/NodeArrayProperty.svelte +13 -25
- package/src/lib/NodeGap.svelte +49 -57
- package/src/lib/Svedit.svelte +189 -85
- package/src/lib/doc_utils.js +1 -1
- package/src/lib/index.js +1 -0
- package/src/lib/paste_utils.js +167 -0
- package/src/lib/utils.js +28 -7
package/src/lib/NodeGap.svelte
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
function cycle_z_index(/** @type {MouseEvent} */ e) {
|
|
3
3
|
const el = /** @type {HTMLElement} */ (e.currentTarget);
|
|
4
4
|
if (el.hasAttribute('data-sent-to-back')) {
|
|
5
|
-
document.querySelectorAll('.svedit-selectable[data-sent-to-back]').forEach(prev => {
|
|
5
|
+
document.querySelectorAll('.svedit-selectable[data-sent-to-back]').forEach((prev) => {
|
|
6
6
|
prev.removeAttribute('data-sent-to-back');
|
|
7
7
|
});
|
|
8
8
|
}
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
* Row/column detection uses var(--row, 1) with the * 99999 multiplier
|
|
51
51
|
* trick — no @container style() queries, works in all browsers.
|
|
52
52
|
*
|
|
53
|
-
* If you override this component via `system_components.
|
|
53
|
+
* If you override this component via `system_components.node_gap`, make
|
|
54
54
|
* sure your custom component also handles read-only mode by rendering a
|
|
55
55
|
* plain `.node-gap` placeholder without editable internals.
|
|
56
56
|
*/
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
});
|
|
75
75
|
|
|
76
76
|
let anchor_name = $derived.by(() => {
|
|
77
|
-
|
|
77
|
+
if (!is_editable) return '';
|
|
78
78
|
if (is_first) return `--g-${serialize_path([...array_path, 0])}-gap-before`;
|
|
79
79
|
return `--g-${serialize_path([...array_path, offset - 1])}-gap-after`;
|
|
80
80
|
});
|
|
@@ -93,7 +93,9 @@
|
|
|
93
93
|
style={gap_style}
|
|
94
94
|
>
|
|
95
95
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
96
|
-
<div class="svedit-selectable" style="anchor-name:{anchor_name}" onpointerdown={cycle_z_index}
|
|
96
|
+
<div class="svedit-selectable" style="anchor-name:{anchor_name}" onpointerdown={cycle_z_index}>
|
|
97
|
+
<br />
|
|
98
|
+
</div>
|
|
97
99
|
</div>
|
|
98
100
|
{:else}
|
|
99
101
|
<div class="node-gap"></div>
|
|
@@ -161,8 +163,14 @@
|
|
|
161
163
|
--_s-r: anchor(var(--_pa) right, 9999999px);
|
|
162
164
|
}
|
|
163
165
|
:global(.node-gap.positioned.gap-before:not(.empty)) {
|
|
164
|
-
--_b-t: anchor(
|
|
165
|
-
|
|
166
|
+
--_b-t: anchor(
|
|
167
|
+
var(--node-caret-boundary-y, var(--node-caret-boundary, --_no-boundary)) top,
|
|
168
|
+
0px
|
|
169
|
+
);
|
|
170
|
+
--_b-l: anchor(
|
|
171
|
+
var(--node-caret-boundary-x, var(--node-caret-boundary, --_no-boundary)) left,
|
|
172
|
+
0px
|
|
173
|
+
);
|
|
166
174
|
}
|
|
167
175
|
:global(.node-gap.positioned.gap-after:not(.last)) {
|
|
168
176
|
--_n-t: anchor(var(--_next) top);
|
|
@@ -175,10 +183,22 @@
|
|
|
175
183
|
--_c-b: anchor(var(--_container) bottom);
|
|
176
184
|
--_c-l: anchor(var(--_container) left);
|
|
177
185
|
--_c-r: anchor(var(--_container) right);
|
|
178
|
-
--_b-b: anchor(
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
186
|
+
--_b-b: anchor(
|
|
187
|
+
var(--node-caret-boundary-y, var(--node-caret-boundary, --_no-boundary)) bottom,
|
|
188
|
+
0px
|
|
189
|
+
);
|
|
190
|
+
--_b-r: anchor(
|
|
191
|
+
var(--node-caret-boundary-x, var(--node-caret-boundary, --_no-boundary)) right,
|
|
192
|
+
0px
|
|
193
|
+
);
|
|
194
|
+
--_b-bt: anchor(
|
|
195
|
+
var(--node-caret-boundary-y, var(--node-caret-boundary, --_no-boundary)) bottom,
|
|
196
|
+
9999999px
|
|
197
|
+
);
|
|
198
|
+
--_b-rl: anchor(
|
|
199
|
+
var(--node-caret-boundary-x, var(--node-caret-boundary, --_no-boundary)) right,
|
|
200
|
+
9999999px
|
|
201
|
+
);
|
|
182
202
|
}
|
|
183
203
|
|
|
184
204
|
:global(.node-gap.positioned .svedit-selectable) {
|
|
@@ -226,10 +246,8 @@
|
|
|
226
246
|
calc(var(--_s-l) + var(--_R) * 9999999px),
|
|
227
247
|
calc(var(--_s-r) + var(--_C) * 9999999px),
|
|
228
248
|
calc(
|
|
229
|
-
(var(--_s-r) + var(--_n-l)) / 2
|
|
230
|
-
|
|
231
|
-
+ max(0px, var(--_s-r) - var(--_n-l)) * 999
|
|
232
|
-
+ var(--_C) * 9999999px
|
|
249
|
+
(var(--_s-r) + var(--_n-l)) / 2 - var(--_gm) / 2 + max(0px, var(--_s-r) - var(--_n-l)) *
|
|
250
|
+
999 + var(--_C) * 9999999px
|
|
233
251
|
),
|
|
234
252
|
/* Safety clamp for wrap: pins gap inside CB when current/next
|
|
235
253
|
wrap across rows. Disabled in nowrap/horizontal-scroll where
|
|
@@ -237,35 +255,25 @@
|
|
|
237
255
|
the other branches position correctly and this clamp would
|
|
238
256
|
wrongly force the gap to CB right minus eg. */
|
|
239
257
|
calc(
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
)
|
|
258
|
+
100% - var(--_eg) + max(0px, var(--_n-l) - var(--_s-r) + 0.5px) * 9999 + var(--_C) *
|
|
259
|
+
9999999px
|
|
260
|
+
)
|
|
244
261
|
);
|
|
245
262
|
right: min(
|
|
246
263
|
calc(var(--_s-r) + var(--_R) * 9999999px),
|
|
247
264
|
calc(
|
|
248
265
|
max(
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
(
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
max(
|
|
257
|
-
min(
|
|
258
|
-
calc(var(--_c-r) - var(--_eg)),
|
|
259
|
-
calc(var(--_s-r) - var(--_eg))
|
|
260
|
-
),
|
|
261
|
-
calc(
|
|
262
|
-
var(--_s-r)
|
|
263
|
-
- (var(--_n-l) - var(--_s-r)) * 999
|
|
266
|
+
0px,
|
|
267
|
+
min(
|
|
268
|
+
var(--_n-l),
|
|
269
|
+
calc((var(--_s-r) + var(--_n-l)) / 2 - var(--_gm) / 2),
|
|
270
|
+
max(
|
|
271
|
+
min(calc(var(--_c-r) - var(--_eg)), calc(var(--_s-r) - var(--_eg))),
|
|
272
|
+
calc(var(--_s-r) - (var(--_n-l) - var(--_s-r)) * 999)
|
|
264
273
|
)
|
|
265
274
|
)
|
|
266
|
-
)
|
|
267
|
-
|
|
268
|
-
+ var(--_C) * 9999999px
|
|
275
|
+
) +
|
|
276
|
+
var(--_C) * 9999999px
|
|
269
277
|
)
|
|
270
278
|
);
|
|
271
279
|
min-height: calc(var(--_gm) * var(--_C));
|
|
@@ -307,23 +315,13 @@
|
|
|
307
315
|
left: min(
|
|
308
316
|
calc(var(--_s-l) + var(--_R) * 9999999px),
|
|
309
317
|
calc(min(var(--_s-r), calc(var(--_b-rl) - var(--_eg))) + var(--_C) * 9999999px),
|
|
310
|
-
calc(
|
|
311
|
-
100% - var(--_eg)
|
|
312
|
-
+ max(0px, var(--_s-r) - 100% + 0.5px) * 9999
|
|
313
|
-
+ var(--_C) * 9999999px
|
|
314
|
-
)
|
|
318
|
+
calc(100% - var(--_eg) + max(0px, var(--_s-r) - 100% + 0.5px) * 9999 + var(--_C) * 9999999px)
|
|
315
319
|
);
|
|
316
320
|
right: min(
|
|
317
321
|
calc(var(--_s-r) + var(--_R) * 9999999px),
|
|
318
322
|
calc(
|
|
319
|
-
max(
|
|
320
|
-
var(--
|
|
321
|
-
min(
|
|
322
|
-
calc(var(--_c-r) - var(--_eg)),
|
|
323
|
-
calc(var(--_s-r) - var(--_eg))
|
|
324
|
-
)
|
|
325
|
-
)
|
|
326
|
-
+ var(--_C) * 9999999px
|
|
323
|
+
max(var(--_b-r), min(calc(var(--_c-r) - var(--_eg)), calc(var(--_s-r) - var(--_eg)))) +
|
|
324
|
+
var(--_C) * 9999999px
|
|
327
325
|
)
|
|
328
326
|
);
|
|
329
327
|
min-height: calc(var(--_eg) * var(--_C));
|
|
@@ -333,10 +331,7 @@
|
|
|
333
331
|
/* Before first node: col extends up, row extends left */
|
|
334
332
|
:global(.node-gap.positioned.gap-before:not(.empty) .svedit-selectable) {
|
|
335
333
|
top: min(
|
|
336
|
-
calc(
|
|
337
|
-
max(var(--_b-t), var(--_s-t) - var(--_eg))
|
|
338
|
-
+ var(--_R) * 9999999px
|
|
339
|
-
),
|
|
334
|
+
calc(max(var(--_b-t), var(--_s-t) - var(--_eg)) + var(--_R) * 9999999px),
|
|
340
335
|
calc(var(--_s-t) + var(--_C) * 9999999px)
|
|
341
336
|
);
|
|
342
337
|
bottom: min(
|
|
@@ -345,10 +340,7 @@
|
|
|
345
340
|
);
|
|
346
341
|
left: min(
|
|
347
342
|
calc(var(--_s-l) + var(--_R) * 9999999px),
|
|
348
|
-
calc(
|
|
349
|
-
max(var(--_b-l), var(--_s-l) - var(--_eg))
|
|
350
|
-
+ var(--_C) * 9999999px
|
|
351
|
-
)
|
|
343
|
+
calc(max(var(--_b-l), var(--_s-l) - var(--_eg)) + var(--_C) * 9999999px)
|
|
352
344
|
);
|
|
353
345
|
right: min(
|
|
354
346
|
calc(var(--_s-r) + var(--_R) * 9999999px),
|
package/src/lib/Svedit.svelte
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import { flushSync, getContext, setContext } from 'svelte';
|
|
3
3
|
import {
|
|
4
|
-
snake_to_pascal,
|
|
5
4
|
get_char_length,
|
|
6
5
|
char_to_utf16_offset,
|
|
7
6
|
deserialize_path,
|
|
@@ -9,6 +8,17 @@
|
|
|
9
8
|
serialize_path,
|
|
10
9
|
is_selection_collapsed
|
|
11
10
|
} from './utils.js';
|
|
11
|
+
import {
|
|
12
|
+
normalize_line_endings,
|
|
13
|
+
dedent_plain_text,
|
|
14
|
+
split_plain_text_paragraphs,
|
|
15
|
+
normalize_plain_text_for_single_line_property,
|
|
16
|
+
get_text_property_name,
|
|
17
|
+
get_text_content,
|
|
18
|
+
is_text_like_node_payload,
|
|
19
|
+
get_default_text_node,
|
|
20
|
+
create_plain_text_nodes_payload
|
|
21
|
+
} from './paste_utils.js';
|
|
12
22
|
import { create_node_visibility } from './node_visibility.svelte.js';
|
|
13
23
|
import DefaultNodeSelectionMarkers from './NodeSelectionMarkers.svelte';
|
|
14
24
|
import './styles/svedit-colors.css';
|
|
@@ -36,10 +46,11 @@
|
|
|
36
46
|
|
|
37
47
|
let canvas_el;
|
|
38
48
|
let root_node = $derived(session.get(path));
|
|
39
|
-
let Overlays = $derived(session.config.system_components?.
|
|
40
|
-
let NodeSelectionMarkers = $derived(
|
|
41
|
-
|
|
42
|
-
|
|
49
|
+
let Overlays = $derived(session.config.system_components?.overlays);
|
|
50
|
+
let NodeSelectionMarkers = $derived(
|
|
51
|
+
session.config.system_components?.node_selection_markers ?? DefaultNodeSelectionMarkers
|
|
52
|
+
);
|
|
53
|
+
let RootComponent = $derived(session.config.node_components[root_node.type]);
|
|
43
54
|
|
|
44
55
|
let is_composing = $state(false);
|
|
45
56
|
let canvas_focused = $state(false);
|
|
@@ -49,7 +60,6 @@
|
|
|
49
60
|
// rerender on DOM-driven changes (the DOM is already in place).
|
|
50
61
|
let selection_source_is_dom = false;
|
|
51
62
|
|
|
52
|
-
|
|
53
63
|
// let is_mobile = $derived(is_mobile_browser());
|
|
54
64
|
// let is_chrome_desktop = $derived(is_chrome_desktop_browser());
|
|
55
65
|
|
|
@@ -552,13 +562,75 @@ ${fallback_html}`;
|
|
|
552
562
|
}
|
|
553
563
|
}
|
|
554
564
|
|
|
555
|
-
|
|
556
|
-
|
|
557
565
|
function oncut(event) {
|
|
558
566
|
if (!editable) return;
|
|
559
567
|
oncopy(event, true);
|
|
560
568
|
}
|
|
561
569
|
|
|
570
|
+
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* @returns {NodeSelection|null}
|
|
574
|
+
*/
|
|
575
|
+
function get_root_node_insert_caret() {
|
|
576
|
+
const root_node = session.get(path);
|
|
577
|
+
const root_schema = root_node ? session.schema[root_node.type] : null;
|
|
578
|
+
if (!root_schema?.properties) return null;
|
|
579
|
+
|
|
580
|
+
const preferred_property_name =
|
|
581
|
+
root_schema.properties.body?.type === 'node_array'
|
|
582
|
+
? 'body'
|
|
583
|
+
: Object.entries(root_schema.properties).find(([, property_definition]) => {
|
|
584
|
+
return property_definition.type === 'node_array';
|
|
585
|
+
})?.[0];
|
|
586
|
+
if (!preferred_property_name) return null;
|
|
587
|
+
|
|
588
|
+
const node_array_path = [...path, preferred_property_name];
|
|
589
|
+
const node_array = session.get(node_array_path);
|
|
590
|
+
if (!Array.isArray(node_array)) return null;
|
|
591
|
+
|
|
592
|
+
return {
|
|
593
|
+
type: /** @type {const} */ ('node'),
|
|
594
|
+
path: node_array_path,
|
|
595
|
+
anchor_offset: node_array.length,
|
|
596
|
+
focus_offset: node_array.length
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
/**
|
|
601
|
+
* @param {Selection|null} [selection]
|
|
602
|
+
* @returns {NodeSelection|null}
|
|
603
|
+
*/
|
|
604
|
+
function get_target_node_insert_caret(selection = session.selection) {
|
|
605
|
+
if (selection?.type === 'node') {
|
|
606
|
+
return selection;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
const next_node_insert_caret = session.get_next_node_insert_caret(selection);
|
|
610
|
+
if (next_node_insert_caret?.type === 'node') {
|
|
611
|
+
return next_node_insert_caret;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
return get_root_node_insert_caret();
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
/**
|
|
618
|
+
* @param {Selection|null} [selection]
|
|
619
|
+
* @returns {NodeSelection|null}
|
|
620
|
+
*/
|
|
621
|
+
function get_node_insert_caret_after_text_selection(selection = session.selection) {
|
|
622
|
+
if (selection?.type !== 'text') return null;
|
|
623
|
+
const node_index = parseInt(String(selection.path.at(-2)), 10);
|
|
624
|
+
if (Number.isNaN(node_index)) return null;
|
|
625
|
+
|
|
626
|
+
return {
|
|
627
|
+
type: /** @type {const} */ ('node'),
|
|
628
|
+
path: selection.path.slice(0, -2),
|
|
629
|
+
anchor_offset: node_index + 1,
|
|
630
|
+
focus_offset: node_index + 1
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
|
|
562
634
|
/**
|
|
563
635
|
* Attempts to paste JSON data as a node at the current selection.
|
|
564
636
|
*
|
|
@@ -567,40 +639,46 @@ ${fallback_html}`;
|
|
|
567
639
|
* @returns {boolean} True if the paste operation was successful, false otherwise
|
|
568
640
|
*/
|
|
569
641
|
function try_node_paste(pasted_json, selection) {
|
|
570
|
-
const { nodes, main_nodes } = pasted_json;
|
|
642
|
+
const { nodes, main_nodes } = pasted_json || {};
|
|
643
|
+
if (!nodes || !main_nodes?.length) return false;
|
|
571
644
|
|
|
572
|
-
// NOTE: At this point, nodes contains a subgraph from the copy
|
|
573
|
-
// with original ids.
|
|
574
645
|
let tr = session.tr;
|
|
575
646
|
if (selection) {
|
|
576
647
|
tr.set_selection(selection);
|
|
577
648
|
}
|
|
649
|
+
if (tr.selection?.type !== 'node') return false;
|
|
578
650
|
|
|
579
|
-
// We can safely assume we're dealing with a node_array property
|
|
580
651
|
const property_definition = session.inspect(tr.selection.path);
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
);
|
|
652
|
+
if (property_definition?.type !== 'node_array') return false;
|
|
653
|
+
|
|
654
|
+
const default_text_node_type = get_default_text_node(property_definition, session.schema);
|
|
655
|
+
const target_text_property_name = get_text_property_name(default_text_node_type, session.schema);
|
|
584
656
|
|
|
585
657
|
const nodes_to_insert = [];
|
|
586
658
|
let rejected = false;
|
|
587
659
|
for (const node_id of main_nodes) {
|
|
588
660
|
const node = nodes[node_id];
|
|
661
|
+
if (!node) {
|
|
662
|
+
rejected = true;
|
|
663
|
+
break;
|
|
664
|
+
}
|
|
665
|
+
|
|
589
666
|
if (!property_definition.node_types.includes(node.type)) {
|
|
590
|
-
|
|
591
|
-
if (
|
|
667
|
+
const text_content = get_text_content(node, session.schema);
|
|
668
|
+
if (
|
|
669
|
+
is_text_like_node_payload(node, session.schema) &&
|
|
670
|
+
default_text_node_type &&
|
|
671
|
+
target_text_property_name
|
|
672
|
+
) {
|
|
592
673
|
const new_node_id = tr.build('the_node', {
|
|
593
674
|
the_node: {
|
|
594
675
|
id: 'the_node',
|
|
595
|
-
type:
|
|
596
|
-
|
|
676
|
+
type: default_text_node_type,
|
|
677
|
+
[target_text_property_name]: text_content || { text: '', annotations: [] }
|
|
597
678
|
}
|
|
598
679
|
});
|
|
599
680
|
nodes_to_insert.push(new_node_id);
|
|
600
681
|
} else {
|
|
601
|
-
// console.log(
|
|
602
|
-
// `rejected ${node.type}. Only ${property_definition.node_types.join(', ')} allowed.`
|
|
603
|
-
// );
|
|
604
682
|
rejected = true;
|
|
605
683
|
break;
|
|
606
684
|
}
|
|
@@ -614,12 +692,12 @@ ${fallback_html}`;
|
|
|
614
692
|
tr.insert_nodes(nodes_to_insert);
|
|
615
693
|
session.apply(tr);
|
|
616
694
|
return true;
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
if (tr.selection.path.length >= 2) {
|
|
698
|
+
const next_node_insert_caret = session.get_next_node_insert_caret(tr.selection);
|
|
699
|
+
if (next_node_insert_caret) {
|
|
700
|
+
return try_node_paste(pasted_json, next_node_insert_caret);
|
|
623
701
|
}
|
|
624
702
|
}
|
|
625
703
|
return false;
|
|
@@ -638,7 +716,11 @@ ${fallback_html}`;
|
|
|
638
716
|
// NOTE: For some reason, await navigator.clipboard.read()
|
|
639
717
|
const clipboard_items = event.clipboardData?.items || [];
|
|
640
718
|
for (const item of clipboard_items || []) {
|
|
641
|
-
if (
|
|
719
|
+
if (
|
|
720
|
+
item.type.startsWith('image/') ||
|
|
721
|
+
item.type.startsWith('video/') ||
|
|
722
|
+
item.type.startsWith('audio/')
|
|
723
|
+
) {
|
|
642
724
|
const blob = item.getAsFile();
|
|
643
725
|
const data_url = URL.createObjectURL(blob);
|
|
644
726
|
pasted_media.push({
|
|
@@ -651,7 +733,8 @@ ${fallback_html}`;
|
|
|
651
733
|
}
|
|
652
734
|
|
|
653
735
|
if (pasted_media.length > 0) {
|
|
654
|
-
const handle_media_paste =
|
|
736
|
+
const handle_media_paste =
|
|
737
|
+
session.config.handle_media_paste || session.config.handle_image_paste;
|
|
655
738
|
pasted_json = await handle_media_paste(session, pasted_media);
|
|
656
739
|
// NOTE: If no pasted_json is returned from the custom handler, we assume that content creation has been
|
|
657
740
|
// handled inside handle_media_paste already.
|
|
@@ -676,31 +759,49 @@ ${fallback_html}`;
|
|
|
676
759
|
|
|
677
760
|
// Try to construct a node payload from plain text when applicable
|
|
678
761
|
if (!pasted_json && typeof plain_text === 'string') {
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
const
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
762
|
+
plain_text = normalize_line_endings(plain_text);
|
|
763
|
+
plain_text = dedent_plain_text(plain_text);
|
|
764
|
+
const plain_text_fragments = split_plain_text_paragraphs(plain_text);
|
|
765
|
+
const has_multiple_paragraphs = plain_text_fragments.length > 1;
|
|
766
|
+
|
|
767
|
+
if (session.selection?.type === 'text') {
|
|
768
|
+
const property_definition = session.inspect(session.selection.path);
|
|
769
|
+
if (property_definition?.type === 'annotated_text' && !property_definition.allow_newlines) {
|
|
770
|
+
plain_text = normalize_plain_text_for_single_line_property(plain_text);
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
const owner_node = session.get(session.selection.path.slice(0, -1));
|
|
774
|
+
const owner_is_text_node = owner_node && session.kind(owner_node) === 'text';
|
|
775
|
+
|
|
776
|
+
if (owner_is_text_node && has_multiple_paragraphs) {
|
|
777
|
+
const node_array_property_definition = session.inspect(
|
|
778
|
+
session.selection.path.slice(0, -2)
|
|
779
|
+
);
|
|
780
|
+
const default_text_node_type = get_default_text_node(node_array_property_definition, session.schema);
|
|
781
|
+
const node_insert_caret = get_node_insert_caret_after_text_selection(session.selection);
|
|
782
|
+
const plain_text_nodes_payload = create_plain_text_nodes_payload(
|
|
783
|
+
plain_text_fragments,
|
|
784
|
+
default_text_node_type,
|
|
785
|
+
session.schema
|
|
786
|
+
);
|
|
787
|
+
|
|
788
|
+
if (node_insert_caret && plain_text_nodes_payload) {
|
|
789
|
+
const did_paste_nodes = try_node_paste(plain_text_nodes_payload, node_insert_caret);
|
|
790
|
+
if (did_paste_nodes) {
|
|
791
|
+
return;
|
|
701
792
|
}
|
|
702
|
-
}
|
|
703
|
-
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
} else {
|
|
796
|
+
const target_node_insert_caret = get_target_node_insert_caret(session.selection);
|
|
797
|
+
if (target_node_insert_caret) {
|
|
798
|
+
const node_array_property_definition = session.inspect(target_node_insert_caret.path);
|
|
799
|
+
const default_text_node_type = get_default_text_node(node_array_property_definition, session.schema);
|
|
800
|
+
pasted_json = create_plain_text_nodes_payload(
|
|
801
|
+
plain_text_fragments,
|
|
802
|
+
default_text_node_type,
|
|
803
|
+
session.schema
|
|
804
|
+
);
|
|
704
805
|
}
|
|
705
806
|
}
|
|
706
807
|
}
|
|
@@ -738,20 +839,27 @@ ${fallback_html}`;
|
|
|
738
839
|
} else if (
|
|
739
840
|
session.selection?.type === 'text' &&
|
|
740
841
|
pasted_json?.main_nodes?.length === 1 &&
|
|
741
|
-
|
|
842
|
+
is_text_like_node_payload(pasted_json?.nodes[pasted_json.main_nodes[0]], session.schema)
|
|
742
843
|
) {
|
|
743
844
|
// Paste a single text node, at a text caret
|
|
744
|
-
const text_property =
|
|
745
|
-
|
|
746
|
-
session.
|
|
845
|
+
const text_property = get_text_content(
|
|
846
|
+
pasted_json.nodes[pasted_json.main_nodes[0]],
|
|
847
|
+
session.schema
|
|
747
848
|
);
|
|
849
|
+
if (text_property) {
|
|
850
|
+
session.apply(
|
|
851
|
+
session.tr.insert_text(text_property.text, text_property.annotations, pasted_json.nodes)
|
|
852
|
+
);
|
|
853
|
+
}
|
|
748
854
|
} else if (['text', 'property'].includes(session.selection?.type) && pasted_json?.nodes) {
|
|
749
855
|
// Paste nodes at a text or property selection by finding the next valid insert caret
|
|
750
|
-
const
|
|
751
|
-
|
|
856
|
+
const target_node_insert_caret = get_target_node_insert_caret(session.selection);
|
|
857
|
+
if (target_node_insert_caret) {
|
|
858
|
+
try_node_paste(pasted_json, target_node_insert_caret);
|
|
859
|
+
}
|
|
752
860
|
} else if (typeof plain_text === 'string') {
|
|
753
861
|
// External paste: Fallback to plain text when no svedit data is found
|
|
754
|
-
session.apply(session.tr.insert_text(plain_text
|
|
862
|
+
session.apply(session.tr.insert_text(plain_text));
|
|
755
863
|
}
|
|
756
864
|
}
|
|
757
865
|
|
|
@@ -839,7 +947,9 @@ ${fallback_html}`;
|
|
|
839
947
|
const offset = parseInt(gap.dataset.gapOffset, 10);
|
|
840
948
|
const node_idx = offset > 0 ? offset - 1 : 0;
|
|
841
949
|
return /** @type {HTMLElement | null} */ (
|
|
842
|
-
canvas_el.querySelector(
|
|
950
|
+
canvas_el.querySelector(
|
|
951
|
+
`[data-path="${serialize_path([...array_path, node_idx])}"][data-type="node"]`
|
|
952
|
+
)
|
|
843
953
|
);
|
|
844
954
|
}
|
|
845
955
|
|
|
@@ -887,9 +997,7 @@ ${fallback_html}`;
|
|
|
887
997
|
|
|
888
998
|
// EDGE CASE: Collapsed selection inside a node gap (gap-after or gap-before).
|
|
889
999
|
// Gaps are siblings of nodes with data-gap-array-path and data-gap-offset.
|
|
890
|
-
const gap_el = /** @type {HTMLElement | null} */ (
|
|
891
|
-
focus_node.closest('[data-gap-array-path]')
|
|
892
|
-
);
|
|
1000
|
+
const gap_el = /** @type {HTMLElement | null} */ (focus_node.closest('[data-gap-array-path]'));
|
|
893
1001
|
if (gap_el && focus_node === anchor_node) {
|
|
894
1002
|
const array_path = deserialize_path(gap_el.dataset.gapArrayPath);
|
|
895
1003
|
const gap_offset = parseInt(gap_el.dataset.gapOffset, 10);
|
|
@@ -901,12 +1009,14 @@ ${fallback_html}`;
|
|
|
901
1009
|
};
|
|
902
1010
|
}
|
|
903
1011
|
|
|
904
|
-
let focus_root =
|
|
905
|
-
|
|
1012
|
+
let focus_root =
|
|
1013
|
+
__resolve_node_from_gap(focus_node) ??
|
|
1014
|
+
/** @type {HTMLElement} */ (focus_node.closest('[data-path][data-type="node"]'));
|
|
906
1015
|
if (!focus_root) return null;
|
|
907
1016
|
|
|
908
|
-
let anchor_root =
|
|
909
|
-
|
|
1017
|
+
let anchor_root =
|
|
1018
|
+
__resolve_node_from_gap(anchor_node) ??
|
|
1019
|
+
/** @type {HTMLElement} */ (anchor_node.closest('[data-path][data-type="node"]'));
|
|
910
1020
|
if (!anchor_root) return null;
|
|
911
1021
|
|
|
912
1022
|
let focus_root_path = deserialize_path(focus_root.dataset.path);
|
|
@@ -1118,7 +1228,10 @@ ${fallback_html}`;
|
|
|
1118
1228
|
) {
|
|
1119
1229
|
// Find the last non-comment child node (comments are inserted by Svelte)
|
|
1120
1230
|
let last_element_index = child_nodes.length - 1;
|
|
1121
|
-
while (
|
|
1231
|
+
while (
|
|
1232
|
+
last_element_index >= 0 &&
|
|
1233
|
+
child_nodes[last_element_index].nodeType === Node.COMMENT_NODE
|
|
1234
|
+
) {
|
|
1122
1235
|
last_element_index--;
|
|
1123
1236
|
}
|
|
1124
1237
|
|
|
@@ -1165,6 +1278,7 @@ ${fallback_html}`;
|
|
|
1165
1278
|
}
|
|
1166
1279
|
|
|
1167
1280
|
function __get_node_element(node_array_path, node_offset) {
|
|
1281
|
+
if (!canvas_el) return null;
|
|
1168
1282
|
return canvas_el.querySelector(
|
|
1169
1283
|
`[data-path="${serialize_path([...node_array_path, node_offset])}"][data-type="node"]`
|
|
1170
1284
|
);
|
|
@@ -1180,10 +1294,7 @@ ${fallback_html}`;
|
|
|
1180
1294
|
function __intersects_viewport(el) {
|
|
1181
1295
|
if (!el) return false;
|
|
1182
1296
|
const r = el.getBoundingClientRect();
|
|
1183
|
-
return
|
|
1184
|
-
r.bottom > 0 && r.top < window.innerHeight &&
|
|
1185
|
-
r.right > 0 && r.left < window.innerWidth
|
|
1186
|
-
);
|
|
1297
|
+
return r.bottom > 0 && r.top < window.innerHeight && r.right > 0 && r.left < window.innerWidth;
|
|
1187
1298
|
}
|
|
1188
1299
|
|
|
1189
1300
|
function __render_node_selection() {
|
|
@@ -1252,9 +1363,8 @@ ${fallback_html}`;
|
|
|
1252
1363
|
// viewport stable and scroll nothing. node_before is null at
|
|
1253
1364
|
// offset 0: cursor_offset - 1 would be -1, and serialize_path
|
|
1254
1365
|
// rejects a negative index.
|
|
1255
|
-
const node_before =
|
|
1256
|
-
? __get_node_element(node_array_path, cursor_offset - 1)
|
|
1257
|
-
: null;
|
|
1366
|
+
const node_before =
|
|
1367
|
+
cursor_offset > 0 ? __get_node_element(node_array_path, cursor_offset - 1) : null;
|
|
1258
1368
|
const node_after = __get_node_element(node_array_path, cursor_offset);
|
|
1259
1369
|
if (__intersects_viewport(node_before) || __intersects_viewport(node_after)) return;
|
|
1260
1370
|
|
|
@@ -1264,14 +1374,8 @@ ${fallback_html}`;
|
|
|
1264
1374
|
return;
|
|
1265
1375
|
}
|
|
1266
1376
|
if (cursor_offset >= array_length) {
|
|
1267
|
-
const max_left = Math.max(
|
|
1268
|
-
|
|
1269
|
-
node_array_el.scrollWidth - node_array_el.clientWidth
|
|
1270
|
-
);
|
|
1271
|
-
const max_top = Math.max(
|
|
1272
|
-
0,
|
|
1273
|
-
node_array_el.scrollHeight - node_array_el.clientHeight
|
|
1274
|
-
);
|
|
1377
|
+
const max_left = Math.max(0, node_array_el.scrollWidth - node_array_el.clientWidth);
|
|
1378
|
+
const max_top = Math.max(0, node_array_el.scrollHeight - node_array_el.clientHeight);
|
|
1275
1379
|
node_array_el.scrollLeft = max_left;
|
|
1276
1380
|
node_array_el.scrollTop = max_top;
|
|
1277
1381
|
if (max_left === 0 && max_top === 0) {
|
package/src/lib/doc_utils.js
CHANGED
|
@@ -67,7 +67,7 @@ export function get_default_node_type(property_definition) {
|
|
|
67
67
|
* @param {import('./types').PropertyDefinition} property_definition
|
|
68
68
|
* @returns {any}
|
|
69
69
|
*/
|
|
70
|
-
function get_property_default(property_definition) {
|
|
70
|
+
export function get_property_default(property_definition) {
|
|
71
71
|
if ('default' in property_definition) return structuredClone(property_definition.default);
|
|
72
72
|
|
|
73
73
|
if (property_definition.type === 'string') return '';
|