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
|
@@ -1,12 +1,23 @@
|
|
|
1
|
-
|
|
2
|
-
* @import { DocumentSchema } from './types.d.ts'
|
|
3
|
-
*/
|
|
1
|
+
import type { Text, DocumentNode, DocumentSchema } from './types.js';
|
|
4
2
|
|
|
5
|
-
|
|
3
|
+
function is_record(value: unknown): value is Record<string, unknown> {
|
|
4
|
+
return typeof value === 'object' && value !== null;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function is_text(value: unknown): value is Text {
|
|
8
|
+
return (
|
|
9
|
+
is_record(value) &&
|
|
10
|
+
typeof value.content === 'string' &&
|
|
11
|
+
Array.isArray(value.marks) &&
|
|
12
|
+
Array.isArray(value.annotations)
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function normalize_line_endings(text: string): string {
|
|
6
17
|
return text.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
7
18
|
}
|
|
8
19
|
|
|
9
|
-
export function dedent_plain_text(plain_text) {
|
|
20
|
+
export function dedent_plain_text(plain_text: string): string {
|
|
10
21
|
const lines = normalize_line_endings(plain_text).split('\n');
|
|
11
22
|
if (lines.length < 2) return plain_text;
|
|
12
23
|
|
|
@@ -42,101 +53,81 @@ export function dedent_plain_text(plain_text) {
|
|
|
42
53
|
.join('\n');
|
|
43
54
|
}
|
|
44
55
|
|
|
45
|
-
export function split_plain_text_paragraphs(plain_text) {
|
|
56
|
+
export function split_plain_text_paragraphs(plain_text: string): string[] {
|
|
46
57
|
return normalize_line_endings(plain_text)
|
|
47
58
|
.split(/\n{2,}/)
|
|
48
59
|
.map((fragment) => fragment.trim())
|
|
49
60
|
.filter(Boolean);
|
|
50
61
|
}
|
|
51
62
|
|
|
52
|
-
export function normalize_plain_text_for_single_line_property(plain_text) {
|
|
63
|
+
export function normalize_plain_text_for_single_line_property(plain_text: string): string {
|
|
53
64
|
return normalize_line_endings(plain_text).replace(/\s*\n+\s*/g, ' ');
|
|
54
65
|
}
|
|
55
66
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
*/
|
|
61
|
-
export function get_text_property_name(node_type, schema) {
|
|
67
|
+
export function get_text_property_name(
|
|
68
|
+
node_type: string | null | undefined,
|
|
69
|
+
schema: DocumentSchema
|
|
70
|
+
): string | null {
|
|
62
71
|
if (!node_type) return null;
|
|
63
72
|
const node_schema = schema[node_type];
|
|
64
73
|
if (!node_schema || node_schema.kind !== 'text') return null;
|
|
65
|
-
if (node_schema.properties?.content?.type === '
|
|
74
|
+
if (node_schema.properties?.content?.type === 'text') return 'content';
|
|
66
75
|
return (
|
|
67
76
|
Object.entries(node_schema.properties).find(([, property_definition]) => {
|
|
68
|
-
return property_definition.type === '
|
|
77
|
+
return property_definition.type === 'text';
|
|
69
78
|
})?.[0] || null
|
|
70
79
|
);
|
|
71
80
|
}
|
|
72
81
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
* @param {DocumentSchema} schema
|
|
76
|
-
* @returns {{ text: string, annotations: any[] } | null}
|
|
77
|
-
*/
|
|
78
|
-
export function get_text_content(node, schema) {
|
|
79
|
-
if (!node || typeof node !== 'object') return null;
|
|
82
|
+
export function get_text_content(node: unknown, schema: DocumentSchema): Text | null {
|
|
83
|
+
if (!is_record(node)) return null;
|
|
80
84
|
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
node[text_property_name] &&
|
|
85
|
-
typeof node[text_property_name].text === 'string' &&
|
|
86
|
-
Array.isArray(node[text_property_name].annotations)
|
|
87
|
-
) {
|
|
85
|
+
const node_type = typeof node.type === 'string' ? node.type : null;
|
|
86
|
+
const text_property_name = get_text_property_name(node_type, schema);
|
|
87
|
+
if (text_property_name && is_text(node[text_property_name])) {
|
|
88
88
|
return node[text_property_name];
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
if (
|
|
91
|
+
if (is_text(node.content)) {
|
|
92
92
|
return node.content;
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
return null;
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
* @returns {boolean}
|
|
102
|
-
*/
|
|
103
|
-
export function is_text_like_node_payload(node, schema) {
|
|
104
|
-
if (!node || typeof node !== 'object') return false;
|
|
105
|
-
if (schema[node.type]?.kind === 'text') return true;
|
|
98
|
+
export function is_text_like_node_payload(node: unknown, schema: DocumentSchema): boolean {
|
|
99
|
+
if (!is_record(node)) return false;
|
|
100
|
+
if (typeof node.type === 'string' && schema[node.type]?.kind === 'text') return true;
|
|
106
101
|
return !!get_text_content(node, schema);
|
|
107
102
|
}
|
|
108
103
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
*/
|
|
114
|
-
export function get_default_text_node(node_array_property_definition, schema) {
|
|
104
|
+
export function get_default_text_node(
|
|
105
|
+
node_array_property_definition: unknown,
|
|
106
|
+
schema: DocumentSchema
|
|
107
|
+
): string | null {
|
|
115
108
|
if (
|
|
116
|
-
node_array_property_definition
|
|
109
|
+
!is_record(node_array_property_definition) ||
|
|
110
|
+
node_array_property_definition.type !== 'node_array' ||
|
|
117
111
|
!Array.isArray(node_array_property_definition.node_types)
|
|
118
112
|
) {
|
|
119
113
|
return null;
|
|
120
114
|
}
|
|
121
115
|
|
|
122
116
|
const default_node_type = node_array_property_definition.default_node_type;
|
|
123
|
-
if (default_node_type && schema[default_node_type]?.kind === 'text') {
|
|
117
|
+
if (typeof default_node_type === 'string' && schema[default_node_type]?.kind === 'text') {
|
|
124
118
|
return default_node_type;
|
|
125
119
|
}
|
|
126
120
|
|
|
127
|
-
return (
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
);
|
|
121
|
+
return (node_array_property_definition.node_types.find(
|
|
122
|
+
(node_type: unknown) => typeof node_type === 'string' && schema[node_type]?.kind === 'text'
|
|
123
|
+
) || null) as string | null;
|
|
131
124
|
}
|
|
132
125
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
*/
|
|
139
|
-
export function create_plain_text_nodes_payload(paragraph_fragments, node_type, schema) {
|
|
126
|
+
export function create_plain_text_nodes_payload(
|
|
127
|
+
paragraph_fragments: string[],
|
|
128
|
+
node_type: string | null | undefined,
|
|
129
|
+
schema: DocumentSchema
|
|
130
|
+
): { main_nodes: string[]; nodes: Record<string, DocumentNode> } | null {
|
|
140
131
|
if (!Array.isArray(paragraph_fragments) || paragraph_fragments.length === 0 || !node_type) {
|
|
141
132
|
return null;
|
|
142
133
|
}
|
|
@@ -144,7 +135,7 @@ export function create_plain_text_nodes_payload(paragraph_fragments, node_type,
|
|
|
144
135
|
const text_property_name = get_text_property_name(node_type, schema);
|
|
145
136
|
if (!text_property_name) return null;
|
|
146
137
|
|
|
147
|
-
const payload = {
|
|
138
|
+
const payload: { main_nodes: string[]; nodes: Record<string, DocumentNode> } = {
|
|
148
139
|
main_nodes: [],
|
|
149
140
|
nodes: {}
|
|
150
141
|
};
|
|
@@ -156,7 +147,8 @@ export function create_plain_text_nodes_payload(paragraph_fragments, node_type,
|
|
|
156
147
|
id: node_id,
|
|
157
148
|
type: node_type,
|
|
158
149
|
[text_property_name]: {
|
|
159
|
-
|
|
150
|
+
content: fragment,
|
|
151
|
+
marks: [],
|
|
160
152
|
annotations: []
|
|
161
153
|
}
|
|
162
154
|
};
|
|
@@ -1,20 +1,33 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { split_text, join_text, get_char_length } from './utils.js';
|
|
2
2
|
import { get_default_node_type } from './doc_utils.js';
|
|
3
|
+
import type Transaction from './Transaction.svelte.js';
|
|
4
|
+
import type {
|
|
5
|
+
DocumentNode,
|
|
6
|
+
DocumentPath,
|
|
7
|
+
NodeArrayProperty,
|
|
8
|
+
NodeProperty,
|
|
9
|
+
Selection,
|
|
10
|
+
TextSelection
|
|
11
|
+
} from './types.js';
|
|
3
12
|
|
|
4
13
|
/**
|
|
5
14
|
* Set multiple properties on a node via a transaction.
|
|
6
15
|
*
|
|
7
|
-
* @param
|
|
8
|
-
* @param
|
|
9
|
-
* @param
|
|
16
|
+
* @param tr - The transaction
|
|
17
|
+
* @param path - Path to the node
|
|
18
|
+
* @param properties - Key/value pairs to set
|
|
10
19
|
*/
|
|
11
|
-
export function set_properties(
|
|
20
|
+
export function set_properties(
|
|
21
|
+
tr: Transaction,
|
|
22
|
+
path: DocumentPath,
|
|
23
|
+
properties: Record<string, unknown>
|
|
24
|
+
): void {
|
|
12
25
|
for (const [key, value] of Object.entries(properties)) {
|
|
13
26
|
tr.set([...path, key], value);
|
|
14
27
|
}
|
|
15
28
|
}
|
|
16
29
|
|
|
17
|
-
export function break_text_node(tr) {
|
|
30
|
+
export function break_text_node(tr: Transaction): boolean {
|
|
18
31
|
// Keep a reference of the original selection (before any transforms are applied)
|
|
19
32
|
const selection = tr.selection;
|
|
20
33
|
// First we need to ensure we have a text selection
|
|
@@ -28,33 +41,35 @@ export function break_text_node(tr) {
|
|
|
28
41
|
if (tr.kind(node) !== 'text') return false;
|
|
29
42
|
const is_inside_node_array = tr.inspect(selection.path.slice(0, -2))?.type === 'node_array';
|
|
30
43
|
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);
|
|
44
|
+
const node_array_prop = String(selection.path.at(-3));
|
|
32
45
|
// Get the node that owns the node_array property (e.g. a page.body)
|
|
33
46
|
const node_array_node = tr.get(selection.path.slice(0, -3));
|
|
34
47
|
|
|
35
|
-
|
|
36
48
|
// Delete selection unless collapsed
|
|
37
49
|
if (selection.anchor_offset !== selection.focus_offset) {
|
|
38
50
|
tr.delete_selection();
|
|
39
51
|
}
|
|
40
52
|
|
|
41
|
-
|
|
53
|
+
// After delete_selection the selection is still a (collapsed) text selection
|
|
54
|
+
const split_at_position = (tr.selection as TextSelection).anchor_offset;
|
|
42
55
|
const content = tr.get(selection.path);
|
|
43
|
-
const [left_text, right_text] =
|
|
56
|
+
const [left_text, right_text] = split_text(content, split_at_position);
|
|
44
57
|
|
|
45
58
|
tr.set([node.id, 'content'], left_text);
|
|
46
59
|
|
|
47
|
-
const node_insert_position = {
|
|
60
|
+
const node_insert_position: Selection = {
|
|
48
61
|
type: 'node',
|
|
49
62
|
path: tr.selection.path.slice(0, -2),
|
|
50
|
-
anchor_offset:
|
|
51
|
-
focus_offset:
|
|
63
|
+
anchor_offset: (tr.selection.path.at(-2) as number) + 1,
|
|
64
|
+
focus_offset: (tr.selection.path.at(-2) as number) + 1
|
|
52
65
|
};
|
|
53
66
|
|
|
54
67
|
// TODO: Only use default_node_type when caret is at the end of
|
|
55
68
|
const node_array_property_definition =
|
|
56
69
|
tr.schema[node_array_node.type].properties[node_array_prop];
|
|
57
|
-
const target_node_type = get_default_node_type(
|
|
70
|
+
const target_node_type = get_default_node_type(
|
|
71
|
+
node_array_property_definition as NodeProperty | NodeArrayProperty
|
|
72
|
+
);
|
|
58
73
|
|
|
59
74
|
if (!target_node_type) {
|
|
60
75
|
console.warn(
|
|
@@ -69,7 +84,7 @@ export function break_text_node(tr) {
|
|
|
69
84
|
return true;
|
|
70
85
|
}
|
|
71
86
|
|
|
72
|
-
export function join_text_node(tr) {
|
|
87
|
+
export function join_text_node(tr: Transaction): boolean {
|
|
73
88
|
// Keep a reference of the original selection (before any transforms are applied)
|
|
74
89
|
const selection = tr.selection;
|
|
75
90
|
// First we need to ensure we have a text selection
|
|
@@ -80,20 +95,20 @@ export function join_text_node(tr) {
|
|
|
80
95
|
const is_inside_node_array = tr.inspect(selection.path.slice(0, -2))?.type === 'node_array';
|
|
81
96
|
if (!is_inside_node_array) return false; // Do nothing if we're not inside a node_array
|
|
82
97
|
|
|
83
|
-
const node_index =
|
|
98
|
+
const node_index = tr.selection.path.at(-2) as number;
|
|
84
99
|
|
|
85
100
|
// Determine if we can join with the previous node
|
|
86
101
|
let can_join = false;
|
|
87
|
-
let predecessor_node = null;
|
|
102
|
+
let predecessor_node: DocumentNode | null = null;
|
|
88
103
|
|
|
89
104
|
if (node_index > 0) {
|
|
90
105
|
const previous_text_path = [...tr.selection.path.slice(0, -2), node_index - 1];
|
|
91
106
|
predecessor_node = tr.get(previous_text_path);
|
|
92
|
-
can_join = tr.kind(predecessor_node) === 'text';
|
|
107
|
+
can_join = predecessor_node !== null && tr.kind(predecessor_node) === 'text';
|
|
93
108
|
}
|
|
94
109
|
|
|
95
110
|
// Special behavior: if we can't join and current node is empty, delete it
|
|
96
|
-
if (!can_join && node.content.
|
|
111
|
+
if (!can_join && node.content.content === '') {
|
|
97
112
|
tr.set_selection({
|
|
98
113
|
type: 'node',
|
|
99
114
|
path: tr.selection.path.slice(0, -2),
|
|
@@ -105,16 +120,16 @@ export function join_text_node(tr) {
|
|
|
105
120
|
}
|
|
106
121
|
|
|
107
122
|
// If we can't join for any reason, return false
|
|
108
|
-
if (!can_join) {
|
|
123
|
+
if (!can_join || !predecessor_node) {
|
|
109
124
|
return false;
|
|
110
125
|
}
|
|
111
126
|
|
|
112
127
|
// Normal joining logic - both nodes are text nodes
|
|
113
128
|
const previous_text_path = [...tr.selection.path.slice(0, -2), node_index - 1];
|
|
114
|
-
const joined_text =
|
|
129
|
+
const joined_text = join_text(predecessor_node.content, node.content);
|
|
115
130
|
|
|
116
131
|
// Calculate caret position based on original predecessor content length
|
|
117
|
-
const caret_position = get_char_length(predecessor_node.content.
|
|
132
|
+
const caret_position = get_char_length(predecessor_node.content.content);
|
|
118
133
|
|
|
119
134
|
// First set the joined content on the predecessor node (preserves annotations)
|
|
120
135
|
tr.set([predecessor_node.id, 'content'], joined_text);
|
|
@@ -139,7 +154,7 @@ export function join_text_node(tr) {
|
|
|
139
154
|
return true;
|
|
140
155
|
}
|
|
141
156
|
|
|
142
|
-
export function insert_default_node(tr) {
|
|
157
|
+
export function insert_default_node(tr: Transaction): boolean {
|
|
143
158
|
const selection = tr.selection;
|
|
144
159
|
|
|
145
160
|
// Only work with collapsed node selections
|
|
@@ -149,15 +164,17 @@ export function insert_default_node(tr) {
|
|
|
149
164
|
|
|
150
165
|
const path = selection.path;
|
|
151
166
|
const node_array_node = tr.get(path.slice(0, -1));
|
|
152
|
-
const property_name = path.at(-1);
|
|
167
|
+
const property_name = String(path.at(-1));
|
|
153
168
|
|
|
154
169
|
// Get the definition for this property
|
|
155
170
|
const property_definition = tr.schema[node_array_node.type].properties[property_name];
|
|
156
|
-
const default_type = get_default_node_type(
|
|
171
|
+
const default_type = get_default_node_type(
|
|
172
|
+
property_definition as NodeProperty | NodeArrayProperty
|
|
173
|
+
);
|
|
157
174
|
|
|
158
175
|
// Use the inserter function if available
|
|
159
|
-
if (tr.config?.inserters?.[default_type]) {
|
|
160
|
-
tr.config.inserters[default_type](tr);
|
|
176
|
+
if (tr.config?.inserters?.[default_type as string]) {
|
|
177
|
+
tr.config.inserters[default_type as string](tr);
|
|
161
178
|
return true;
|
|
162
179
|
} else {
|
|
163
180
|
throw new Error(`No inserter function available for default node type '${default_type}'`);
|