svedit 0.4.0 → 0.6.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 +762 -120
- package/dist/AnnotatedTextProperty.svelte +122 -75
- package/dist/AnnotatedTextProperty.svelte.d.ts.map +1 -1
- package/dist/Command.svelte.d.ts +118 -0
- package/dist/Command.svelte.d.ts.map +1 -0
- package/dist/Command.svelte.js +314 -0
- package/dist/CustomProperty.svelte +31 -42
- package/dist/CustomProperty.svelte.d.ts.map +1 -1
- package/dist/KeyMapper.svelte.d.ts +36 -0
- package/dist/KeyMapper.svelte.d.ts.map +1 -0
- package/dist/KeyMapper.svelte.js +141 -0
- package/dist/Node.svelte +33 -35
- package/dist/Node.svelte.d.ts.map +1 -1
- package/dist/NodeArrayProperty.svelte +40 -37
- package/dist/NodeArrayProperty.svelte.d.ts.map +1 -1
- package/dist/Session.svelte.d.ts +223 -0
- package/dist/Session.svelte.d.ts.map +1 -0
- package/dist/Session.svelte.js +594 -0
- package/dist/Svedit.svelte +1352 -1465
- package/dist/Svedit.svelte.d.ts.map +1 -1
- package/dist/Transaction.svelte.d.ts +122 -51
- package/dist/Transaction.svelte.d.ts.map +1 -1
- package/dist/Transaction.svelte.js +845 -551
- package/dist/UnknownNode.svelte +6 -6
- package/dist/UnknownNode.svelte.d.ts.map +1 -1
- package/dist/doc_utils.d.ts +127 -0
- package/dist/doc_utils.d.ts.map +1 -0
- package/dist/doc_utils.js +482 -0
- package/dist/index.d.ts +7 -3
- package/dist/index.js +20 -4
- package/dist/{commands.svelte.d.ts → transforms.svelte.d.ts} +1 -2
- package/dist/transforms.svelte.d.ts.map +1 -0
- package/dist/transforms.svelte.js +155 -0
- package/dist/types.d.ts +256 -110
- package/dist/{util.d.ts → utils.d.ts} +23 -2
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +338 -0
- package/package.json +25 -23
- package/src/lib/AnnotatedTextProperty.svelte +122 -75
- package/src/lib/Command.svelte.js +314 -0
- package/src/lib/CustomProperty.svelte +31 -42
- package/src/lib/KeyMapper.svelte.js +141 -0
- package/src/lib/Node.svelte +33 -35
- package/src/lib/NodeArrayProperty.svelte +40 -37
- package/src/lib/Session.svelte.js +594 -0
- package/src/lib/Svedit.svelte +1352 -1465
- package/src/lib/Transaction.svelte.js +845 -551
- package/src/lib/UnknownNode.svelte +6 -6
- package/src/lib/doc_utils.js +482 -0
- package/src/lib/index.js +20 -4
- package/src/lib/transforms.svelte.js +155 -0
- package/src/lib/types.d.ts +256 -110
- package/src/lib/utils.js +338 -0
- package/dist/Document.svelte.d.ts +0 -214
- package/dist/Document.svelte.d.ts.map +0 -1
- package/dist/Document.svelte.js +0 -796
- package/dist/commands.svelte.d.ts.map +0 -1
- package/dist/commands.svelte.js +0 -271
- package/dist/util.d.ts.map +0 -1
- package/dist/util.js +0 -235
- package/src/lib/Document.svelte.js +0 -796
- package/src/lib/commands.svelte.js +0 -271
- package/src/lib/util.js +0 -235
|
@@ -1,51 +1,40 @@
|
|
|
1
1
|
<script>
|
|
2
|
-
|
|
2
|
+
import { getContext } from 'svelte';
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
/** @import { CustomPropertyProps } from './types.d.ts'; */
|
|
5
|
+
const svedit = getContext('svedit');
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
/** @type {CustomPropertyProps} */
|
|
9
|
-
let {
|
|
10
|
-
path,
|
|
11
|
-
class: css_class,
|
|
12
|
-
children,
|
|
13
|
-
} = $props();
|
|
7
|
+
/** @type {CustomPropertyProps} */
|
|
8
|
+
let { path, tag = 'div', class: css_class, children } = $props();
|
|
14
9
|
</script>
|
|
15
10
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
11
|
+
<svelte:element
|
|
12
|
+
this={tag}
|
|
13
|
+
class={css_class}
|
|
14
|
+
data-type="property"
|
|
15
|
+
data-path={path.join('.')}
|
|
16
|
+
style="anchor-name: --{path.join('-')};"
|
|
17
|
+
>
|
|
18
|
+
{#if svedit.editable}
|
|
19
|
+
<div class="cursor-trap">
|
|
20
|
+
<div class="svedit-selectable"><br /></div>
|
|
21
|
+
</div>
|
|
22
|
+
{/if}
|
|
27
23
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
{:else}
|
|
31
|
-
{@render children()}
|
|
32
|
-
{/if}
|
|
24
|
+
{@render children()}
|
|
25
|
+
</svelte:element>
|
|
33
26
|
|
|
34
27
|
<style>
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
.property-content {
|
|
48
|
-
width: 100%;
|
|
49
|
-
height: 100%;
|
|
50
|
-
}
|
|
28
|
+
[data-type='property'] {
|
|
29
|
+
position: relative;
|
|
30
|
+
}
|
|
31
|
+
.cursor-trap {
|
|
32
|
+
position: absolute;
|
|
33
|
+
top: 0;
|
|
34
|
+
left: 0;
|
|
35
|
+
right: 0;
|
|
36
|
+
bottom: 0;
|
|
37
|
+
z-index: 1;
|
|
38
|
+
outline: none;
|
|
39
|
+
}
|
|
51
40
|
</style>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CustomProperty.svelte.d.ts","sourceRoot":"","sources":["../src/lib/CustomProperty.svelte.js"],"names":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"CustomProperty.svelte.d.ts","sourceRoot":"","sources":["../src/lib/CustomProperty.svelte.js"],"names":[],"mappings":";;;;;AA6BA,6GAA6D"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validates and defines a keymap.
|
|
3
|
+
* Throws an error if any key combo is invalid.
|
|
4
|
+
* Valid formats: 'meta+e,ctrl+e' or 'meta+shift+a'
|
|
5
|
+
* Invalid: 'meta+e+a' (only one non-modifier key allowed)
|
|
6
|
+
*/
|
|
7
|
+
export function define_keymap(keymap: any): any;
|
|
8
|
+
/**
|
|
9
|
+
* KeyMapper manages keyboard shortcuts using a stack-based scope system.
|
|
10
|
+
*
|
|
11
|
+
* Scopes are tried from top to bottom (most specific to most general).
|
|
12
|
+
* This makes it completely general-purpose - no knowledge of specific contexts.
|
|
13
|
+
*
|
|
14
|
+
* Usage:
|
|
15
|
+
* const mapper = new KeyMapper();
|
|
16
|
+
* mapper.push_scope(app_keymap); // Base layer
|
|
17
|
+
* mapper.push_scope(editor_keymap); // When editor gains focus
|
|
18
|
+
* mapper.pop_scope(); // When editor loses focus
|
|
19
|
+
*/
|
|
20
|
+
export class KeyMapper {
|
|
21
|
+
scope_stack: any[];
|
|
22
|
+
skip_onkeydown: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Push a new scope onto the stack (becomes highest priority)
|
|
25
|
+
*/
|
|
26
|
+
push_scope(keymap: any): void;
|
|
27
|
+
/**
|
|
28
|
+
* Pop the most recent scope from the stack
|
|
29
|
+
*/
|
|
30
|
+
pop_scope(): any;
|
|
31
|
+
/**
|
|
32
|
+
* Handle keyboard event by trying scopes from top to bottom
|
|
33
|
+
*/
|
|
34
|
+
handle_keydown(event: any): void;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=KeyMapper.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"KeyMapper.svelte.d.ts","sourceRoot":"","sources":["../src/lib/KeyMapper.svelte.js"],"names":[],"mappings":"AAQA;;;;;GAKG;AACH,gDAgBC;AA6DD;;;;;;;;;;;GAWG;AACH;IAEE,mBAAqB;IACrB,wBAA2B;IAG5B;;OAEG;IACH,8BAGC;IAED;;OAEG;IACH,iBAIC;IAED;;OAEG;IACH,iCAUC;CACD"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
const MODIFIER_KEYS = ['meta', 'ctrl', 'alt', 'shift'];
|
|
2
|
+
const MODIFIER_EVENT_KEYS = {
|
|
3
|
+
meta: 'metaKey',
|
|
4
|
+
ctrl: 'ctrlKey',
|
|
5
|
+
alt: 'altKey',
|
|
6
|
+
shift: 'shiftKey'
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Validates and defines a keymap.
|
|
11
|
+
* Throws an error if any key combo is invalid.
|
|
12
|
+
* Valid formats: 'meta+e,ctrl+e' or 'meta+shift+a'
|
|
13
|
+
* Invalid: 'meta+e+a' (only one non-modifier key allowed)
|
|
14
|
+
*/
|
|
15
|
+
export function define_keymap(keymap) {
|
|
16
|
+
for (const [key_combo] of Object.entries(keymap)) {
|
|
17
|
+
const alternatives = key_combo.split(',');
|
|
18
|
+
|
|
19
|
+
for (const alternative of alternatives) {
|
|
20
|
+
const parts = alternative.trim().toLowerCase().split('+');
|
|
21
|
+
const non_modifiers = parts.filter((part) => !MODIFIER_KEYS.includes(part));
|
|
22
|
+
|
|
23
|
+
if (non_modifiers.length !== 1) {
|
|
24
|
+
throw new Error(
|
|
25
|
+
`Invalid key combo: "${alternative}". Must have exactly one non-modifier key. Found: ${non_modifiers.length}`
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return keymap;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Matches a keyboard event against a key combo string.
|
|
35
|
+
* Example: 'meta+e,ctrl+e' matches either (metaKey && key==='e') OR (ctrlKey && key==='e')
|
|
36
|
+
*/
|
|
37
|
+
function matches_key_combo(key_combo, event) {
|
|
38
|
+
const alternatives = key_combo.split(',');
|
|
39
|
+
|
|
40
|
+
return alternatives.some((alternative) => {
|
|
41
|
+
const parts = alternative.trim().toLowerCase().split('+');
|
|
42
|
+
const modifiers = parts.filter((part) => MODIFIER_KEYS.includes(part));
|
|
43
|
+
const non_modifier = parts.find((part) => !MODIFIER_KEYS.includes(part));
|
|
44
|
+
|
|
45
|
+
// Check if all specified modifiers are pressed
|
|
46
|
+
const modifiers_match = modifiers.every((mod) => event[MODIFIER_EVENT_KEYS[mod]]);
|
|
47
|
+
|
|
48
|
+
// Check if no unspecified modifiers are pressed
|
|
49
|
+
const no_extra_modifiers = MODIFIER_KEYS.every((mod) => {
|
|
50
|
+
if (modifiers.includes(mod)) return true; // This modifier is expected
|
|
51
|
+
return !event[MODIFIER_EVENT_KEYS[mod]]; // This modifier should NOT be pressed
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Check if the key matches
|
|
55
|
+
const key_matches = event.key.toLowerCase() === non_modifier;
|
|
56
|
+
return modifiers_match && no_extra_modifiers && key_matches;
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Handles a key map by matching keyboard event against registered key combos
|
|
62
|
+
* and executing the first enabled command.
|
|
63
|
+
*
|
|
64
|
+
* Supports both sync and async commands. For async commands, errors are logged
|
|
65
|
+
* but don't crash the application.
|
|
66
|
+
*/
|
|
67
|
+
function handle_key_map(key_map, event) {
|
|
68
|
+
for (const [key_combo, commands] of Object.entries(key_map)) {
|
|
69
|
+
if (matches_key_combo(key_combo, event)) {
|
|
70
|
+
// Find the first enabled command and execute it
|
|
71
|
+
const enabled_command = commands.find((cmd) => cmd.is_enabled());
|
|
72
|
+
if (enabled_command) {
|
|
73
|
+
event.preventDefault();
|
|
74
|
+
|
|
75
|
+
// Execute command (may be sync or async)
|
|
76
|
+
const result = enabled_command.execute();
|
|
77
|
+
|
|
78
|
+
// If it's a promise, handle errors (fire-and-forget)
|
|
79
|
+
if (result instanceof Promise) {
|
|
80
|
+
result.catch((err) => {
|
|
81
|
+
console.error('Command execution failed:', err);
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* KeyMapper manages keyboard shortcuts using a stack-based scope system.
|
|
94
|
+
*
|
|
95
|
+
* Scopes are tried from top to bottom (most specific to most general).
|
|
96
|
+
* This makes it completely general-purpose - no knowledge of specific contexts.
|
|
97
|
+
*
|
|
98
|
+
* Usage:
|
|
99
|
+
* const mapper = new KeyMapper();
|
|
100
|
+
* mapper.push_scope(app_keymap); // Base layer
|
|
101
|
+
* mapper.push_scope(editor_keymap); // When editor gains focus
|
|
102
|
+
* mapper.pop_scope(); // When editor loses focus
|
|
103
|
+
*/
|
|
104
|
+
export class KeyMapper {
|
|
105
|
+
constructor() {
|
|
106
|
+
this.scope_stack = [];
|
|
107
|
+
this.skip_onkeydown = false;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Push a new scope onto the stack (becomes highest priority)
|
|
112
|
+
*/
|
|
113
|
+
push_scope(keymap) {
|
|
114
|
+
// console.log('pushed keymap', keymap);
|
|
115
|
+
this.scope_stack.push(keymap);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Pop the most recent scope from the stack
|
|
120
|
+
*/
|
|
121
|
+
pop_scope() {
|
|
122
|
+
const keymap = this.scope_stack.pop();
|
|
123
|
+
// console.log('popped keymap', keymap);
|
|
124
|
+
return keymap;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Handle keyboard event by trying scopes from top to bottom
|
|
129
|
+
*/
|
|
130
|
+
handle_keydown(event) {
|
|
131
|
+
// Key handling temporarily disabled (e.g. while character composition takes place)
|
|
132
|
+
if (this.skip_onkeydown) return;
|
|
133
|
+
// console.log('KeyMapper.handle_keydown', event);
|
|
134
|
+
// Try from most specific (top of stack) to most general (bottom)
|
|
135
|
+
for (let i = this.scope_stack.length - 1; i >= 0; i--) {
|
|
136
|
+
if (handle_key_map(this.scope_stack[i], event)) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
package/dist/Node.svelte
CHANGED
|
@@ -1,45 +1,43 @@
|
|
|
1
1
|
<script>
|
|
2
|
-
|
|
2
|
+
import { getContext } from 'svelte';
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
/** @import { NodeProps } from './types.d.ts'; */
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
const svedit = getContext('svedit');
|
|
7
|
+
let NodeCursorTrap = $derived(svedit.session.config.system_components.NodeCursorTrap);
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
path,
|
|
12
|
-
children
|
|
13
|
-
} = $props();
|
|
9
|
+
/** @type {NodeProps} */
|
|
10
|
+
let { path, children, tag = 'div', class: css_class } = $props();
|
|
14
11
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
12
|
+
// NOTE: When the next to last path segment is a node_array property, the node is wrapped in a node_array
|
|
13
|
+
let is_inside_node_array = $derived(
|
|
14
|
+
path.length > 1 && svedit.session.inspect(path.slice(0, -1))?.type === 'node_array'
|
|
15
|
+
);
|
|
16
|
+
let node = $derived(svedit.session.get(path));
|
|
17
|
+
let is_first_node_array_child = $derived(
|
|
18
|
+
is_inside_node_array && parseInt(String(path.at(-1)), 10) === 0
|
|
19
|
+
);
|
|
19
20
|
</script>
|
|
20
21
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
{:else}
|
|
38
|
-
{@render children()}
|
|
39
|
-
{/if}
|
|
22
|
+
<svelte:element
|
|
23
|
+
this={tag}
|
|
24
|
+
class={css_class}
|
|
25
|
+
data-node-id={node.id}
|
|
26
|
+
data-path={path.join('.')}
|
|
27
|
+
data-type="node"
|
|
28
|
+
style="anchor-name: --{path.join('-')};"
|
|
29
|
+
>
|
|
30
|
+
{#if svedit.editable && is_first_node_array_child}
|
|
31
|
+
<NodeCursorTrap {path} type="position-zero-cursor-trap" />
|
|
32
|
+
{/if}
|
|
33
|
+
{@render children()}
|
|
34
|
+
{#if svedit.editable && is_inside_node_array}
|
|
35
|
+
<NodeCursorTrap {path} type="after-node-cursor-trap" />
|
|
36
|
+
{/if}
|
|
37
|
+
</svelte:element>
|
|
40
38
|
|
|
41
39
|
<style>
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
div {
|
|
41
|
+
position: relative;
|
|
42
|
+
}
|
|
45
43
|
</style>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Node.svelte.d.ts","sourceRoot":"","sources":["../src/lib/Node.svelte.js"],"names":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"Node.svelte.d.ts","sourceRoot":"","sources":["../src/lib/Node.svelte.js"],"names":[],"mappings":";;;;;AAwCA,yFAAmD"}
|
|
@@ -1,45 +1,48 @@
|
|
|
1
1
|
<script>
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import { getContext } from 'svelte';
|
|
3
|
+
import UnknownNode from './UnknownNode.svelte';
|
|
4
|
+
import { snake_to_pascal } from './utils.js';
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
/** @import { NodeArrayPropertyProps } from './types.d.ts'; */
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
const svedit = getContext('svedit');
|
|
9
|
+
let NodeCursorTrap = $derived(svedit.session.config.system_components.NodeCursorTrap);
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
path,
|
|
14
|
-
class: css_class,
|
|
15
|
-
} = $props();
|
|
11
|
+
/** @type {NodeArrayPropertyProps} */
|
|
12
|
+
let { path, tag = 'div', class: css_class } = $props();
|
|
16
13
|
|
|
17
|
-
|
|
14
|
+
let nodes = $derived(
|
|
15
|
+
svedit.session
|
|
16
|
+
.get(path)
|
|
17
|
+
.map(/** @param {string} node_id */ (node_id) => svedit.session.get(node_id))
|
|
18
|
+
);
|
|
18
19
|
</script>
|
|
19
20
|
|
|
20
|
-
<
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
<svelte:element this={tag} class={css_class} data-type="node_array" data-path={path.join('.')}>
|
|
22
|
+
{#if nodes.length === 0 && svedit.editable}
|
|
23
|
+
<!--
|
|
24
|
+
Experimental: We'll let .empty-node-array act like a node, so the existing
|
|
25
|
+
code paths for selection mapping will work as expected.
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
27
|
+
TODO: Need to figure out a way to make .empty-node-array customizable.
|
|
28
|
+
-->
|
|
29
|
+
<div
|
|
30
|
+
class="node empty-node-array"
|
|
31
|
+
data-path={[...path, 0].join('.')}
|
|
32
|
+
data-type="node"
|
|
33
|
+
style="anchor-name: --{[...path, 0].join(
|
|
34
|
+
'-'
|
|
35
|
+
)}; position: relative; min-height: 40px; min-width: 40px;"
|
|
36
|
+
>
|
|
37
|
+
<NodeCursorTrap path={[...path, 0]} type="position-zero-cursor-trap" />
|
|
38
|
+
</div>
|
|
39
|
+
{/if}
|
|
40
|
+
{#each nodes as node, index (index)}
|
|
41
|
+
{@const Component = svedit.session.config.node_components[snake_to_pascal(node.type)]}
|
|
42
|
+
{#if Component}
|
|
43
|
+
<Component path={[...path, index]} />
|
|
44
|
+
{:else}
|
|
45
|
+
<UnknownNode path={[...path, index]} />
|
|
46
|
+
{/if}
|
|
47
|
+
{/each}
|
|
48
|
+
</svelte:element>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NodeArrayProperty.svelte.d.ts","sourceRoot":"","sources":["../src/lib/NodeArrayProperty.svelte.js"],"names":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"NodeArrayProperty.svelte.d.ts","sourceRoot":"","sources":["../src/lib/NodeArrayProperty.svelte.js"],"names":[],"mappings":";;;;;AAgDA,mHAAgE"}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {Object} SessionOptions
|
|
3
|
+
* @property {Selection} [selection] - Initial selection state
|
|
4
|
+
*/
|
|
5
|
+
export default class Session {
|
|
6
|
+
/**
|
|
7
|
+
* @param {DocumentSchema} schema - The document schema
|
|
8
|
+
* @param {Document} doc - The document
|
|
9
|
+
* @param {object} config - configuration object
|
|
10
|
+
* @param {Object} [options]
|
|
11
|
+
* @param {Selection} [options.selection] - Initial selection state
|
|
12
|
+
*/
|
|
13
|
+
constructor(schema: DocumentSchema, doc: Document, config: object, options?: {
|
|
14
|
+
selection?: Selection;
|
|
15
|
+
});
|
|
16
|
+
/** @type {DocumentSchema} */
|
|
17
|
+
schema: DocumentSchema;
|
|
18
|
+
/** @type {Document} */
|
|
19
|
+
doc: Document;
|
|
20
|
+
/** @type {any} */
|
|
21
|
+
config: any;
|
|
22
|
+
history: any[];
|
|
23
|
+
history_index: number;
|
|
24
|
+
last_batch_started: any;
|
|
25
|
+
commands: {};
|
|
26
|
+
keymap: {};
|
|
27
|
+
can_undo: boolean;
|
|
28
|
+
can_redo: boolean;
|
|
29
|
+
selected_node: any;
|
|
30
|
+
available_annotation_types: any;
|
|
31
|
+
/**
|
|
32
|
+
* Sets the selection with validation
|
|
33
|
+
* @param {Selection | undefined} value - The new selection
|
|
34
|
+
* @throws {Error} Throws if the selection is invalid
|
|
35
|
+
*/
|
|
36
|
+
set selection(value: Selection | undefined);
|
|
37
|
+
/**
|
|
38
|
+
* Gets the current selection
|
|
39
|
+
* @returns {Selection | undefined}
|
|
40
|
+
*/
|
|
41
|
+
get selection(): Selection | undefined;
|
|
42
|
+
/**
|
|
43
|
+
* Validates that a selection is within bounds and refers to valid paths.
|
|
44
|
+
*
|
|
45
|
+
* @param {Selection} selection - The selection to validate
|
|
46
|
+
* @throws {Error} Throws if the selection is invalid
|
|
47
|
+
* @private
|
|
48
|
+
*/
|
|
49
|
+
private _validate_selection;
|
|
50
|
+
/**
|
|
51
|
+
* Gets the document_id from the doc
|
|
52
|
+
* @returns {string}
|
|
53
|
+
*/
|
|
54
|
+
get document_id(): string;
|
|
55
|
+
validate_doc(): void;
|
|
56
|
+
generate_id(): any;
|
|
57
|
+
/**
|
|
58
|
+
* Initialize commands and keymap for this session.
|
|
59
|
+
* Called by Svedit component when it has the necessary context.
|
|
60
|
+
*
|
|
61
|
+
* NOTE: This assumes a single Svedit instance per session.
|
|
62
|
+
* For multiple editors on the same document, this architecture would need
|
|
63
|
+
* to be refactored to support multiple sessions per document.
|
|
64
|
+
*
|
|
65
|
+
* @param {object} context - The svedit context with session, editable, canvas, etc.
|
|
66
|
+
*/
|
|
67
|
+
initialize_commands(context: object): void;
|
|
68
|
+
get_available_annotation_types(): any;
|
|
69
|
+
get_selected_node(): any;
|
|
70
|
+
/**
|
|
71
|
+
* Creates a new transaction for making atomic changes to the document.
|
|
72
|
+
*
|
|
73
|
+
* @returns {Transaction} A new transaction instance
|
|
74
|
+
*/
|
|
75
|
+
get tr(): Transaction;
|
|
76
|
+
/**
|
|
77
|
+
* Applies a transaction to the document.
|
|
78
|
+
* Auto-batches history entries with debounced behavior (max one entry per 2 seconds) when batch is true.
|
|
79
|
+
*
|
|
80
|
+
* @param {Transaction} transaction - The transaction to apply
|
|
81
|
+
* @param {object} [options] - Optional configuration
|
|
82
|
+
* @param {boolean} [options.batch=false] - Whether to allow batching with previous transaction
|
|
83
|
+
*/
|
|
84
|
+
apply(transaction: Transaction, { batch }?: {
|
|
85
|
+
batch?: boolean;
|
|
86
|
+
}): this;
|
|
87
|
+
undo(): this;
|
|
88
|
+
redo(): this;
|
|
89
|
+
/**
|
|
90
|
+
* Gets a node instance or property value at the specified path.
|
|
91
|
+
* @param {DocumentPath|string} path - Path to the node or property
|
|
92
|
+
* @returns {any} Either a node instance object or the value of a property
|
|
93
|
+
* @example
|
|
94
|
+
* // Get a node by ID
|
|
95
|
+
* session.get('list_1') // => { type: 'list', id: 'list_1', ... }
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* // Get a node array property
|
|
99
|
+
* session.get(['list_1', 'list_items']) // => [ 'list_item_1', 'list_item_2' ]
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* // Get a specific node from an array
|
|
103
|
+
* session.get(['page_1', 'body', 3, 'list_items', 0]) // => { type: 'list_item', id: 'list_item_1', ... }
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* // Get an annotated text property
|
|
107
|
+
* session.get(['page_1', 'cover', 'title']) // => {text: 'Hello world', annotations: []}
|
|
108
|
+
*/
|
|
109
|
+
get(path: DocumentPath | string): any;
|
|
110
|
+
/**
|
|
111
|
+
* While .get gives you the value of a path, inspect gives you
|
|
112
|
+
* the type info of that value.
|
|
113
|
+
*
|
|
114
|
+
* @todo The layout of these should be improved and more explictly typed
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* session.inspect(['page_1', 'body']) => {
|
|
118
|
+
* kind: 'property',
|
|
119
|
+
* name: 'body',
|
|
120
|
+
* type: 'node_array',
|
|
121
|
+
* node_types: ['text', 'story', 'list'],
|
|
122
|
+
* default_node_type: 'text'
|
|
123
|
+
* }
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* session.inspect(['page_1', 'body', 1]) => {
|
|
127
|
+
* kind: 'node',
|
|
128
|
+
* id: 'paragraph_234',
|
|
129
|
+
* type: 'paragraph',
|
|
130
|
+
* properties: {...}
|
|
131
|
+
* }
|
|
132
|
+
*
|
|
133
|
+
* @param {DocumentPath} path
|
|
134
|
+
* @returns {{kind: 'property'|'node', [key: string]: any}}
|
|
135
|
+
*/
|
|
136
|
+
inspect(path: DocumentPath): {
|
|
137
|
+
kind: "property" | "node";
|
|
138
|
+
[key: string]: any;
|
|
139
|
+
};
|
|
140
|
+
/**
|
|
141
|
+
* Determines the kind of a node ('block' for structured blocks, 'text' for pure
|
|
142
|
+
* text nodes or 'annotation' for annotation nodes.
|
|
143
|
+
* @param {any} node
|
|
144
|
+
* @returns {NodeKind}
|
|
145
|
+
*/
|
|
146
|
+
kind(node: any): NodeKind;
|
|
147
|
+
/**
|
|
148
|
+
* Determines whether a node type can be inserted at a given selection.
|
|
149
|
+
* @param {string} node_type - The type of node to insert.
|
|
150
|
+
* @param {Selection} [selection] - The selection at which to insert the node.
|
|
151
|
+
* @returns {boolean} True if the node type can be inserted, false otherwise.
|
|
152
|
+
*/
|
|
153
|
+
can_insert(node_type: string, selection?: Selection): boolean;
|
|
154
|
+
/**
|
|
155
|
+
* Compute next possible insert position from a given selection
|
|
156
|
+
*
|
|
157
|
+
* @param {Selection} [selection] - Reference selection
|
|
158
|
+
* @returns {Selection} True if the paste operation was successful, false otherwise
|
|
159
|
+
*/
|
|
160
|
+
get_next_node_insert_cursor(selection?: Selection): Selection;
|
|
161
|
+
/**
|
|
162
|
+
* Returns the annotation object that is currently "under the cursor".
|
|
163
|
+
* NOTE: Annotations in Svedit are exclusive, so there can only be one active_annotation
|
|
164
|
+
*
|
|
165
|
+
* @param {string} [annotation_type] Optional annotation type to filter by
|
|
166
|
+
* @returns {Annotation|null}
|
|
167
|
+
*/
|
|
168
|
+
active_annotation(annotation_type?: string): Annotation | null;
|
|
169
|
+
get_selected_annotated_text(): {
|
|
170
|
+
text: string;
|
|
171
|
+
annotations: any;
|
|
172
|
+
nodes: {};
|
|
173
|
+
};
|
|
174
|
+
get_selected_plain_text(): string;
|
|
175
|
+
get_selected_nodes(): any;
|
|
176
|
+
select_parent(): void;
|
|
177
|
+
/**
|
|
178
|
+
* Traverses the document and returns a list of nodes in depth-first order.
|
|
179
|
+
*
|
|
180
|
+
* The traversal order is:
|
|
181
|
+
* 1. Leaf nodes first
|
|
182
|
+
* 2. Branch nodes second
|
|
183
|
+
* 3. Root node (entry point) last
|
|
184
|
+
*
|
|
185
|
+
* @param {string} node_id - The ID of the node to start traversing from
|
|
186
|
+
* @returns {Array<DocumentNode>} Array of nodes in depth-first order
|
|
187
|
+
* @note Nodes that are not reachable from the entry point node will not be included
|
|
188
|
+
*/
|
|
189
|
+
traverse(node_id: string): Array<DocumentNode>;
|
|
190
|
+
/**
|
|
191
|
+
* Convert the document to a clean format for persistence.
|
|
192
|
+
*
|
|
193
|
+
* We make a traversal to ensure that orphaned nodes are not included,
|
|
194
|
+
* and that leaf nodes go first, followed by branches and the root node at last.
|
|
195
|
+
*
|
|
196
|
+
* @returns {Document} The document
|
|
197
|
+
*/
|
|
198
|
+
to_json(): Document;
|
|
199
|
+
property_type(type: any, property: any): string;
|
|
200
|
+
count_references(node_id: any): number;
|
|
201
|
+
/**
|
|
202
|
+
* @param {NodeId} node_id
|
|
203
|
+
* @returns {NodeId[]}
|
|
204
|
+
*/
|
|
205
|
+
get_referenced_nodes(node_id: NodeId): NodeId[];
|
|
206
|
+
#private;
|
|
207
|
+
}
|
|
208
|
+
export type SessionOptions = {
|
|
209
|
+
/**
|
|
210
|
+
* - Initial selection state
|
|
211
|
+
*/
|
|
212
|
+
selection?: Selection;
|
|
213
|
+
};
|
|
214
|
+
import type { DocumentSchema } from './types.d.ts';
|
|
215
|
+
import type { Document } from './types.d.ts';
|
|
216
|
+
import type { Selection } from './types.d.ts';
|
|
217
|
+
import Transaction from './Transaction.svelte.js';
|
|
218
|
+
import type { DocumentPath } from './types.d.ts';
|
|
219
|
+
import type { NodeKind } from './types.d.ts';
|
|
220
|
+
import type { Annotation } from './types.d.ts';
|
|
221
|
+
import type { DocumentNode } from './types.d.ts';
|
|
222
|
+
import type { NodeId } from './types.d.ts';
|
|
223
|
+
//# sourceMappingURL=Session.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Session.svelte.d.ts","sourceRoot":"","sources":["../src/lib/Session.svelte.js"],"names":[],"mappings":"AA6BA;;;GAGG;AAEH;IA8BC;;;;;;OAMG;IACH,oBANW,cAAc,OACd,QAAQ,UACR,MAAM,YAEd;QAA4B,SAAS,GAA7B,SAAS;KACnB,EAWA;IA3CD,6BAA6B;IAC7B,QADW,cAAc,CACH;IAEtB,uBAAuB;IACvB,KADW,QAAQ,CACA;IAEnB,kBAAkB;IAClB,QADW,GAAG,CACQ;IAEtB,eAAyB;IACzB,sBAA+B;IAC/B,wBAA2C;IAI3C,aAA0B;IAC1B,WAAwB;IAGxB,kBAA6C;IAC7C,kBAAkE;IAGlE,mBAAmD;IACnD,gCAA6E;IA6B7E;;;;OAIG;IACH,qBAHW,SAAS,GAAG,SAAS,EAQ/B;IAlBD;;;OAGG;IACH,iBAFa,SAAS,GAAG,SAAS,CAIjC;IAcD;;;;;;OAMG;IACH,4BAyDC;IAED;;;OAGG;IACH,mBAFa,MAAM,CAIlB;IAED,qBAIC;IAED,mBAMC;IAED;;;;;;;;;OASG;IACH,6BAFW,MAAM,QAQhB;IAED,sCAKC;IAGD,yBAkBC;IAED;;;;OAIG;IACH,UAFa,WAAW,CAKvB;IAED;;;;;;;OAOG;IACH,mBAJW,WAAW,cAEnB;QAA0B,KAAK,GAAvB,OAAO;KACjB,QA4CA;IAED,aAgBC;IAED,aAaC;IAED;;;;;;;;;;;;;;;;;;;OAmBG;IACH,UAlBW,YAAY,GAAC,MAAM,GACjB,GAAG,CAmBf;IAED;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACH,cAHW,YAAY,GACV;QAAC,IAAI,EAAE,UAAU,GAAC,MAAM,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;KAAC,CAIzD;IAED;;;;;OAKG;IACH,WAHW,GAAG,GACD,QAAQ,CAIpB;IAED;;;;;OAKG;IACH,sBAJW,MAAM,cACN,SAAS,GACP,OAAO,CAcnB;IAED;;;;;OAKG;IACH,wCAHW,SAAS,GACP,SAAS,CAerB;IAED;;;;;;OAMG;IACH,oCAHW,MAAM,GACJ,UAAU,GAAC,IAAI,CAI3B;IAED;;;;MA6BC;IAGD,kCAOC;IAED,0BAOC;IAED,sBAmCC;IAED;;;;;;;;;;;OAWG;IACH,kBAJW,MAAM,GACJ,KAAK,CAAC,YAAY,CAAC,CAK/B;IAED;;;;;;;OAOG;IACH,WAFa,QAAQ,CAWpB;IAID,gDAEC;IAGD,uCAEC;IAGD;;;OAGG;IACH,8BAHW,MAAM,GACJ,MAAM,EAAE,CAOpB;;CACD;;;;;gBAljBa,SAAS;;oCAPb,cAAc;8BAAd,cAAc;+BAAd,cAAc;wBAxBA,yBAAyB;kCAwBvC,cAAc;8BAAd,cAAc;gCAAd,cAAc;kCAAd,cAAc;4BAAd,cAAc"}
|