@rtif-sdk/engine 1.0.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/LICENSE +21 -0
- package/README.md +79 -0
- package/dist/commands.d.ts +29 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/commands.js +29 -0
- package/dist/commands.js.map +1 -0
- package/dist/engine.d.ts +137 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +527 -0
- package/dist/engine.js.map +1 -0
- package/dist/errors.d.ts +34 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +21 -0
- package/dist/errors.js.map +1 -0
- package/dist/history.d.ts +130 -0
- package/dist/history.d.ts.map +1 -0
- package/dist/history.js +156 -0
- package/dist/history.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/plugin-host.d.ts +34 -0
- package/dist/plugin-host.d.ts.map +1 -0
- package/dist/plugin-host.js +6 -0
- package/dist/plugin-host.js.map +1 -0
- package/dist/registries.d.ts +130 -0
- package/dist/registries.d.ts.map +1 -0
- package/dist/registries.js +23 -0
- package/dist/registries.js.map +1 -0
- package/dist/selection-transform.d.ts +23 -0
- package/dist/selection-transform.d.ts.map +1 -0
- package/dist/selection-transform.js +87 -0
- package/dist/selection-transform.js.map +1 -0
- package/package.json +28 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Undo/redo history stack management.
|
|
3
|
+
* All functions are pure — they return new HistoryState, never mutate.
|
|
4
|
+
*
|
|
5
|
+
* History stores `{ forward, inverse }` pairs so that:
|
|
6
|
+
* - Undo applies inverse ops in reverse order (undoing last op first)
|
|
7
|
+
* - Redo replays the original forward ops — no recomputation needed
|
|
8
|
+
*/
|
|
9
|
+
import type { Operation } from '@rtif-sdk/core';
|
|
10
|
+
/** Default max undo depth */
|
|
11
|
+
export declare const DEFAULT_MAX_DEPTH = 100;
|
|
12
|
+
/**
|
|
13
|
+
* A single undo/redo entry storing both the forward ops and their inverses.
|
|
14
|
+
* Forward ops are the original operations as dispatched.
|
|
15
|
+
* Inverse ops are the operations that reverse them (in application order,
|
|
16
|
+
* meaning `inverse[i]` undoes `forward[i]`).
|
|
17
|
+
*/
|
|
18
|
+
export interface HistoryEntry {
|
|
19
|
+
readonly forward: ReadonlyArray<Operation>;
|
|
20
|
+
readonly inverse: ReadonlyArray<Operation>;
|
|
21
|
+
}
|
|
22
|
+
/** History state for undo/redo */
|
|
23
|
+
export interface HistoryState {
|
|
24
|
+
/** Stack of operation groups for undo (last element = most recent) */
|
|
25
|
+
readonly undoStack: ReadonlyArray<HistoryEntry>;
|
|
26
|
+
/** Stack of operation groups for redo (last element = most recent) */
|
|
27
|
+
readonly redoStack: ReadonlyArray<HistoryEntry>;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Create an empty history state.
|
|
31
|
+
*
|
|
32
|
+
* @returns A fresh history with empty undo and redo stacks
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```ts
|
|
36
|
+
* const history = createHistory();
|
|
37
|
+
* // { undoStack: [], redoStack: [] }
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare function createHistory(): HistoryState;
|
|
41
|
+
/**
|
|
42
|
+
* Push an operation group onto the undo stack, clearing redo.
|
|
43
|
+
* A new action invalidates the redo stack. Trims oldest entries if
|
|
44
|
+
* the stack exceeds `maxDepth`.
|
|
45
|
+
*
|
|
46
|
+
* @param history - The current history state
|
|
47
|
+
* @param entry - The history entry containing forward and inverse ops
|
|
48
|
+
* @param maxDepth - Maximum undo stack depth (default 100)
|
|
49
|
+
* @returns New history state with the entry pushed
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```ts
|
|
53
|
+
* const h1 = pushUndoGroup(history, { forward: [op], inverse: [invOp] }, 100);
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
export declare function pushUndoGroup(history: HistoryState, entry: HistoryEntry, maxDepth?: number): HistoryState;
|
|
57
|
+
/**
|
|
58
|
+
* Pop the most recent undo entry from the stack.
|
|
59
|
+
* Returns `undefined` if the undo stack is empty.
|
|
60
|
+
* Does NOT push to redo — the engine handles that after applying the ops.
|
|
61
|
+
*
|
|
62
|
+
* @param history - The current history state
|
|
63
|
+
* @returns The popped entry and new history, or undefined if empty
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* ```ts
|
|
67
|
+
* const result = popUndo(history);
|
|
68
|
+
* if (result) {
|
|
69
|
+
* const { entry, history: newHistory } = result;
|
|
70
|
+
* // entry.inverse contains the ops to apply (in reverse order)
|
|
71
|
+
* // entry.forward is available for the redo stack
|
|
72
|
+
* }
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
export declare function popUndo(history: HistoryState): {
|
|
76
|
+
entry: HistoryEntry;
|
|
77
|
+
history: HistoryState;
|
|
78
|
+
} | undefined;
|
|
79
|
+
/**
|
|
80
|
+
* Pop the most recent redo entry from the stack.
|
|
81
|
+
* Returns `undefined` if the redo stack is empty.
|
|
82
|
+
* Does NOT push to undo — the engine handles that after applying the ops.
|
|
83
|
+
*
|
|
84
|
+
* @param history - The current history state
|
|
85
|
+
* @returns The popped entry and new history, or undefined if empty
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```ts
|
|
89
|
+
* const result = popRedo(history);
|
|
90
|
+
* if (result) {
|
|
91
|
+
* const { entry, history: newHistory } = result;
|
|
92
|
+
* // entry.forward contains the ops to replay
|
|
93
|
+
* // entry.inverse is available for the undo stack
|
|
94
|
+
* }
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
export declare function popRedo(history: HistoryState): {
|
|
98
|
+
entry: HistoryEntry;
|
|
99
|
+
history: HistoryState;
|
|
100
|
+
} | undefined;
|
|
101
|
+
/**
|
|
102
|
+
* Push an entry onto the redo stack.
|
|
103
|
+
* Used internally by the engine after undo applies the inverse ops.
|
|
104
|
+
*
|
|
105
|
+
* @param history - The current history state
|
|
106
|
+
* @param entry - The history entry to push
|
|
107
|
+
* @returns New history state with the entry pushed to redo
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* ```ts
|
|
111
|
+
* const h1 = pushRedoGroup(history, { forward: [op], inverse: [invOp] });
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
114
|
+
export declare function pushRedoGroup(history: HistoryState, entry: HistoryEntry): HistoryState;
|
|
115
|
+
/**
|
|
116
|
+
* Push an entry onto the undo stack without clearing redo.
|
|
117
|
+
* Used internally by the engine after redo applies forward ops.
|
|
118
|
+
*
|
|
119
|
+
* @param history - The current history state
|
|
120
|
+
* @param entry - The history entry to push
|
|
121
|
+
* @param maxDepth - Maximum undo stack depth (default 100)
|
|
122
|
+
* @returns New history state with the entry pushed to undo
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* ```ts
|
|
126
|
+
* const h1 = pushUndoGroupKeepRedo(history, { forward: [op], inverse: [invOp] });
|
|
127
|
+
* ```
|
|
128
|
+
*/
|
|
129
|
+
export declare function pushUndoGroupKeepRedo(history: HistoryState, entry: HistoryEntry, maxDepth?: number): HistoryState;
|
|
130
|
+
//# sourceMappingURL=history.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history.d.ts","sourceRoot":"","sources":["../src/history.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD,6BAA6B;AAC7B,eAAO,MAAM,iBAAiB,MAAM,CAAC;AAErC;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;IAC3C,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;CAC5C;AAED,kCAAkC;AAClC,MAAM,WAAW,YAAY;IAC3B,sEAAsE;IACtE,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;IAChD,sEAAsE;IACtE,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;CACjD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,IAAI,YAAY,CAE5C;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,YAAY,EACrB,KAAK,EAAE,YAAY,EACnB,QAAQ,GAAE,MAA0B,GACnC,YAAY,CAWd;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,OAAO,CACrB,OAAO,EAAE,YAAY,GACpB;IAAE,KAAK,EAAE,YAAY,CAAC;IAAC,OAAO,EAAE,YAAY,CAAA;CAAE,GAAG,SAAS,CAa5D;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,OAAO,CACrB,OAAO,EAAE,YAAY,GACpB;IAAE,KAAK,EAAE,YAAY,CAAC;IAAC,OAAO,EAAE,YAAY,CAAA;CAAE,GAAG,SAAS,CAa5D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,YAAY,EACrB,KAAK,EAAE,YAAY,GAClB,YAAY,CAKd;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,YAAY,EACrB,KAAK,EAAE,YAAY,EACnB,QAAQ,GAAE,MAA0B,GACnC,YAAY,CAUd"}
|
package/dist/history.js
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Undo/redo history stack management.
|
|
3
|
+
* All functions are pure — they return new HistoryState, never mutate.
|
|
4
|
+
*
|
|
5
|
+
* History stores `{ forward, inverse }` pairs so that:
|
|
6
|
+
* - Undo applies inverse ops in reverse order (undoing last op first)
|
|
7
|
+
* - Redo replays the original forward ops — no recomputation needed
|
|
8
|
+
*/
|
|
9
|
+
/** Default max undo depth */
|
|
10
|
+
export const DEFAULT_MAX_DEPTH = 100;
|
|
11
|
+
/**
|
|
12
|
+
* Create an empty history state.
|
|
13
|
+
*
|
|
14
|
+
* @returns A fresh history with empty undo and redo stacks
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* const history = createHistory();
|
|
19
|
+
* // { undoStack: [], redoStack: [] }
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export function createHistory() {
|
|
23
|
+
return { undoStack: [], redoStack: [] };
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Push an operation group onto the undo stack, clearing redo.
|
|
27
|
+
* A new action invalidates the redo stack. Trims oldest entries if
|
|
28
|
+
* the stack exceeds `maxDepth`.
|
|
29
|
+
*
|
|
30
|
+
* @param history - The current history state
|
|
31
|
+
* @param entry - The history entry containing forward and inverse ops
|
|
32
|
+
* @param maxDepth - Maximum undo stack depth (default 100)
|
|
33
|
+
* @returns New history state with the entry pushed
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```ts
|
|
37
|
+
* const h1 = pushUndoGroup(history, { forward: [op], inverse: [invOp] }, 100);
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export function pushUndoGroup(history, entry, maxDepth = DEFAULT_MAX_DEPTH) {
|
|
41
|
+
const newStack = [...history.undoStack, entry];
|
|
42
|
+
// Trim oldest if over maxDepth
|
|
43
|
+
const trimmed = newStack.length > maxDepth
|
|
44
|
+
? newStack.slice(newStack.length - maxDepth)
|
|
45
|
+
: newStack;
|
|
46
|
+
return {
|
|
47
|
+
undoStack: trimmed,
|
|
48
|
+
redoStack: [],
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Pop the most recent undo entry from the stack.
|
|
53
|
+
* Returns `undefined` if the undo stack is empty.
|
|
54
|
+
* Does NOT push to redo — the engine handles that after applying the ops.
|
|
55
|
+
*
|
|
56
|
+
* @param history - The current history state
|
|
57
|
+
* @returns The popped entry and new history, or undefined if empty
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```ts
|
|
61
|
+
* const result = popUndo(history);
|
|
62
|
+
* if (result) {
|
|
63
|
+
* const { entry, history: newHistory } = result;
|
|
64
|
+
* // entry.inverse contains the ops to apply (in reverse order)
|
|
65
|
+
* // entry.forward is available for the redo stack
|
|
66
|
+
* }
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
export function popUndo(history) {
|
|
70
|
+
if (history.undoStack.length === 0) {
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
const entry = history.undoStack[history.undoStack.length - 1];
|
|
74
|
+
return {
|
|
75
|
+
entry,
|
|
76
|
+
history: {
|
|
77
|
+
undoStack: history.undoStack.slice(0, -1),
|
|
78
|
+
redoStack: history.redoStack,
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Pop the most recent redo entry from the stack.
|
|
84
|
+
* Returns `undefined` if the redo stack is empty.
|
|
85
|
+
* Does NOT push to undo — the engine handles that after applying the ops.
|
|
86
|
+
*
|
|
87
|
+
* @param history - The current history state
|
|
88
|
+
* @returns The popped entry and new history, or undefined if empty
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* ```ts
|
|
92
|
+
* const result = popRedo(history);
|
|
93
|
+
* if (result) {
|
|
94
|
+
* const { entry, history: newHistory } = result;
|
|
95
|
+
* // entry.forward contains the ops to replay
|
|
96
|
+
* // entry.inverse is available for the undo stack
|
|
97
|
+
* }
|
|
98
|
+
* ```
|
|
99
|
+
*/
|
|
100
|
+
export function popRedo(history) {
|
|
101
|
+
if (history.redoStack.length === 0) {
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
const entry = history.redoStack[history.redoStack.length - 1];
|
|
105
|
+
return {
|
|
106
|
+
entry,
|
|
107
|
+
history: {
|
|
108
|
+
undoStack: history.undoStack,
|
|
109
|
+
redoStack: history.redoStack.slice(0, -1),
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Push an entry onto the redo stack.
|
|
115
|
+
* Used internally by the engine after undo applies the inverse ops.
|
|
116
|
+
*
|
|
117
|
+
* @param history - The current history state
|
|
118
|
+
* @param entry - The history entry to push
|
|
119
|
+
* @returns New history state with the entry pushed to redo
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* ```ts
|
|
123
|
+
* const h1 = pushRedoGroup(history, { forward: [op], inverse: [invOp] });
|
|
124
|
+
* ```
|
|
125
|
+
*/
|
|
126
|
+
export function pushRedoGroup(history, entry) {
|
|
127
|
+
return {
|
|
128
|
+
undoStack: history.undoStack,
|
|
129
|
+
redoStack: [...history.redoStack, entry],
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Push an entry onto the undo stack without clearing redo.
|
|
134
|
+
* Used internally by the engine after redo applies forward ops.
|
|
135
|
+
*
|
|
136
|
+
* @param history - The current history state
|
|
137
|
+
* @param entry - The history entry to push
|
|
138
|
+
* @param maxDepth - Maximum undo stack depth (default 100)
|
|
139
|
+
* @returns New history state with the entry pushed to undo
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* ```ts
|
|
143
|
+
* const h1 = pushUndoGroupKeepRedo(history, { forward: [op], inverse: [invOp] });
|
|
144
|
+
* ```
|
|
145
|
+
*/
|
|
146
|
+
export function pushUndoGroupKeepRedo(history, entry, maxDepth = DEFAULT_MAX_DEPTH) {
|
|
147
|
+
const newStack = [...history.undoStack, entry];
|
|
148
|
+
const trimmed = newStack.length > maxDepth
|
|
149
|
+
? newStack.slice(newStack.length - maxDepth)
|
|
150
|
+
: newStack;
|
|
151
|
+
return {
|
|
152
|
+
undoStack: trimmed,
|
|
153
|
+
redoStack: history.redoStack,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=history.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history.js","sourceRoot":"","sources":["../src/history.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,6BAA6B;AAC7B,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,CAAC;AAqBrC;;;;;;;;;;GAUG;AACH,MAAM,UAAU,aAAa;IAC3B,OAAO,EAAE,SAAS,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;AAC1C,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,aAAa,CAC3B,OAAqB,EACrB,KAAmB,EACnB,WAAmB,iBAAiB;IAEpC,MAAM,QAAQ,GAAG,CAAC,GAAG,OAAO,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IAC/C,+BAA+B;IAC/B,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,GAAG,QAAQ;QACxC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,GAAG,QAAQ,CAAC;QAC5C,CAAC,CAAC,QAAQ,CAAC;IAEb,OAAO;QACL,SAAS,EAAE,OAAO;QAClB,SAAS,EAAE,EAAE;KACd,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,OAAO,CACrB,OAAqB;IAErB,IAAI,OAAO,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACnC,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,KAAK,GAAG,OAAO,CAAC,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAE,CAAC;IAC/D,OAAO;QACL,KAAK;QACL,OAAO,EAAE;YACP,SAAS,EAAE,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YACzC,SAAS,EAAE,OAAO,CAAC,SAAS;SAC7B;KACF,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,OAAO,CACrB,OAAqB;IAErB,IAAI,OAAO,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACnC,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,KAAK,GAAG,OAAO,CAAC,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAE,CAAC;IAC/D,OAAO;QACL,KAAK;QACL,OAAO,EAAE;YACP,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,SAAS,EAAE,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;SAC1C;KACF,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,aAAa,CAC3B,OAAqB,EACrB,KAAmB;IAEnB,OAAO;QACL,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,SAAS,EAAE,CAAC,GAAG,OAAO,CAAC,SAAS,EAAE,KAAK,CAAC;KACzC,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,qBAAqB,CACnC,OAAqB,EACrB,KAAmB,EACnB,WAAmB,iBAAiB;IAEpC,MAAM,QAAQ,GAAG,CAAC,GAAG,OAAO,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IAC/C,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,GAAG,QAAQ;QACxC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,GAAG,QAAQ,CAAC;QAC5C,CAAC,CAAC,QAAQ,CAAC;IAEb,OAAO;QACL,SAAS,EAAE,OAAO;QAClB,SAAS,EAAE,OAAO,CAAC,SAAS;KAC7B,CAAC;AACJ,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { createEngine } from './engine.js';
|
|
2
|
+
export type { EditorState, IEditorEngine } from './engine.js';
|
|
3
|
+
export { EngineCommands } from './commands.js';
|
|
4
|
+
export type { Plugin, PluginContext } from './plugin-host.js';
|
|
5
|
+
export { createHistory, pushUndoGroup, popUndo, popRedo, pushRedoGroup, pushUndoGroupKeepRedo, DEFAULT_MAX_DEPTH, } from './history.js';
|
|
6
|
+
export type { HistoryState, HistoryEntry } from './history.js';
|
|
7
|
+
export { EngineError } from './errors.js';
|
|
8
|
+
export type { EngineErrorCode, PluginError } from './errors.js';
|
|
9
|
+
export { transformSelection } from './selection-transform.js';
|
|
10
|
+
export { normalizeCommandHandler } from './registries.js';
|
|
11
|
+
export type { PluginRegistries, BlockTypeConfig, MarkTypeConfig, InputRule, CommandHandler, CommandDescriptor, ShortcutDescriptor, ShortcutBinding, } from './registries.js';
|
|
12
|
+
export type { RangeMarksResult } from '@rtif-sdk/core';
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE9D,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAE9D,OAAO,EACL,aAAa,EACb,aAAa,EACb,OAAO,EACP,OAAO,EACP,aAAa,EACb,qBAAqB,EACrB,iBAAiB,GAClB,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE/D,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,YAAY,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEhE,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAE9D,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAC1D,YAAY,EACV,gBAAgB,EAChB,eAAe,EACf,cAAc,EACd,SAAS,EACT,cAAc,EACd,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,GAChB,MAAM,iBAAiB,CAAC;AAGzB,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { createEngine } from './engine.js';
|
|
2
|
+
export { EngineCommands } from './commands.js';
|
|
3
|
+
export { createHistory, pushUndoGroup, popUndo, popRedo, pushRedoGroup, pushUndoGroupKeepRedo, DEFAULT_MAX_DEPTH, } from './history.js';
|
|
4
|
+
export { EngineError } from './errors.js';
|
|
5
|
+
export { transformSelection } from './selection-transform.js';
|
|
6
|
+
export { normalizeCommandHandler } from './registries.js';
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3C,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAI/C,OAAO,EACL,aAAa,EACb,aAAa,EACb,OAAO,EACP,OAAO,EACP,aAAa,EACb,qBAAqB,EACrB,iBAAiB,GAClB,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAE9D,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin interface and lifecycle management.
|
|
3
|
+
* See docs/spec/plugin-contract.md for specification.
|
|
4
|
+
*/
|
|
5
|
+
import type { Operation } from '@rtif-sdk/core';
|
|
6
|
+
import type { EditorState, IEditorEngine } from './engine.js';
|
|
7
|
+
import type { PluginRegistries } from './registries.js';
|
|
8
|
+
/**
|
|
9
|
+
* Context passed to `Plugin.init()`.
|
|
10
|
+
* Extends the engine interface with registry methods available during init only.
|
|
11
|
+
* Registry calls outside of init throw `EngineError('REGISTRY_CLOSED')`.
|
|
12
|
+
*/
|
|
13
|
+
export type PluginContext = IEditorEngine & PluginRegistries;
|
|
14
|
+
/** Plugin interface — extensions that observe, intercept, and transform editor operations */
|
|
15
|
+
export interface Plugin {
|
|
16
|
+
/** Unique identifier */
|
|
17
|
+
readonly id: string;
|
|
18
|
+
/** Called once when the plugin is registered via `engine.use(plugin)` */
|
|
19
|
+
init?(ctx: PluginContext): void;
|
|
20
|
+
/**
|
|
21
|
+
* Called before operations are applied.
|
|
22
|
+
* Can inspect, modify, or cancel operations.
|
|
23
|
+
* Return empty array to cancel all ops.
|
|
24
|
+
*/
|
|
25
|
+
beforeApply?(ops: Operation[], state: EditorState): Operation[];
|
|
26
|
+
/**
|
|
27
|
+
* Called after operations are applied, before onChange fires.
|
|
28
|
+
* Cannot modify the operations, but can dispatch new ones.
|
|
29
|
+
*/
|
|
30
|
+
afterApply?(ops: Operation[], prevState: EditorState, nextState: EditorState): void;
|
|
31
|
+
/** Called when the plugin is removed */
|
|
32
|
+
destroy?(): void;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=plugin-host.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-host.d.ts","sourceRoot":"","sources":["../src/plugin-host.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAExD;;;;GAIG;AACH,MAAM,MAAM,aAAa,GAAG,aAAa,GAAG,gBAAgB,CAAC;AAE7D,6FAA6F;AAC7F,MAAM,WAAW,MAAM;IACrB,wBAAwB;IACxB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAEpB,yEAAyE;IACzE,IAAI,CAAC,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CAAC;IAEhC;;;;OAIG;IACH,WAAW,CAAC,CAAC,GAAG,EAAE,SAAS,EAAE,EAAE,KAAK,EAAE,WAAW,GAAG,SAAS,EAAE,CAAC;IAEhE;;;OAGG;IACH,UAAU,CAAC,CAAC,GAAG,EAAE,SAAS,EAAE,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,GAAG,IAAI,CAAC;IAEpF,wCAAwC;IACxC,OAAO,CAAC,IAAI,IAAI,CAAC;CAClB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-host.js","sourceRoot":"","sources":["../src/plugin-host.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin registries — block types, mark types, commands, input rules, and shortcuts.
|
|
3
|
+
* See SPEC.md §5.2 and docs/spec/plugin-contract.md for specification.
|
|
4
|
+
*/
|
|
5
|
+
import type { IEditorEngine } from './engine.js';
|
|
6
|
+
/** Configuration for a registered block type */
|
|
7
|
+
export interface BlockTypeConfig {
|
|
8
|
+
defaultAttrs?: Record<string, unknown>;
|
|
9
|
+
validate?: (attrs: Record<string, unknown>) => boolean;
|
|
10
|
+
}
|
|
11
|
+
/** Configuration for a registered mark type */
|
|
12
|
+
export interface MarkTypeConfig {
|
|
13
|
+
validate?: (value: unknown) => boolean;
|
|
14
|
+
}
|
|
15
|
+
/** An input rule that transforms text on insert */
|
|
16
|
+
export interface InputRule {
|
|
17
|
+
/** Regex tested against the block text after the insert */
|
|
18
|
+
pattern: RegExp;
|
|
19
|
+
/** Handler called when pattern matches */
|
|
20
|
+
handler: (engine: IEditorEngine, match: RegExpMatchArray, blockId: string) => void;
|
|
21
|
+
/** If true, no further rules are tested after this one matches. Default: false. */
|
|
22
|
+
exclusive?: boolean;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Full command descriptor with optional `canExecute` and `isActive` queries.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* const toggleBold: CommandDescriptor = {
|
|
30
|
+
* execute: (engine) => { engine.dispatch({ ... }); },
|
|
31
|
+
* canExecute: (engine) => {
|
|
32
|
+
* const sel = engine.state.selection;
|
|
33
|
+
* return sel.anchor.offset !== sel.focus.offset;
|
|
34
|
+
* },
|
|
35
|
+
* isActive: (engine) => {
|
|
36
|
+
* const marks = engine.getMarksAtOffset(engine.state.selection.focus.offset);
|
|
37
|
+
* return marks.bold === true;
|
|
38
|
+
* },
|
|
39
|
+
* };
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export interface CommandDescriptor {
|
|
43
|
+
/** Execute the command */
|
|
44
|
+
readonly execute: (engine: IEditorEngine, payload?: unknown) => void;
|
|
45
|
+
/** Whether the command can execute in the current state. Default: true. */
|
|
46
|
+
readonly canExecute?: (engine: IEditorEngine, payload?: unknown) => boolean;
|
|
47
|
+
/** Whether the command's effect is currently active. Default: false. */
|
|
48
|
+
readonly isActive?: (engine: IEditorEngine, payload?: unknown) => boolean;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Command handler — either a plain function or a full descriptor with queries.
|
|
52
|
+
*
|
|
53
|
+
* Plain functions are normalized to `{ execute: fn }` internally.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```ts
|
|
57
|
+
* // Simple function handler
|
|
58
|
+
* registerCommand('myCommand', (engine) => { ... });
|
|
59
|
+
*
|
|
60
|
+
* // Full descriptor
|
|
61
|
+
* registerCommand('toggleBold', {
|
|
62
|
+
* execute: (engine) => { ... },
|
|
63
|
+
* canExecute: (engine) => true,
|
|
64
|
+
* isActive: (engine) => false,
|
|
65
|
+
* });
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
export type CommandHandler = ((engine: IEditorEngine, payload?: unknown) => void) | CommandDescriptor;
|
|
69
|
+
/**
|
|
70
|
+
* Describes a keyboard shortcut in a platform-agnostic way.
|
|
71
|
+
*
|
|
72
|
+
* The `mod` key maps to Cmd on macOS/iOS and Ctrl on Windows/Linux.
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```ts
|
|
76
|
+
* const boldShortcut: ShortcutDescriptor = { key: 'b', mod: true };
|
|
77
|
+
* const strikeShortcut: ShortcutDescriptor = { key: 'x', mod: true, shift: true };
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
export interface ShortcutDescriptor {
|
|
81
|
+
/** The key identifier (e.g., "b", "k", "Enter", "Backspace") */
|
|
82
|
+
readonly key: string;
|
|
83
|
+
/** Whether the platform modifier (Cmd/Ctrl) must be held */
|
|
84
|
+
readonly mod?: boolean;
|
|
85
|
+
/** Whether Shift must be held */
|
|
86
|
+
readonly shift?: boolean;
|
|
87
|
+
/** Whether Alt/Option must be held */
|
|
88
|
+
readonly alt?: boolean;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* A binding from a keyboard shortcut to a command name and optional payload.
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* ```ts
|
|
95
|
+
* const binding: ShortcutBinding = {
|
|
96
|
+
* descriptor: { key: 'b', mod: true },
|
|
97
|
+
* command: 'toggleMark:bold',
|
|
98
|
+
* };
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
export interface ShortcutBinding {
|
|
102
|
+
/** The shortcut key combination */
|
|
103
|
+
readonly descriptor: ShortcutDescriptor;
|
|
104
|
+
/** The command name to execute when this shortcut is triggered */
|
|
105
|
+
readonly command: string;
|
|
106
|
+
/** Optional payload to pass to the command handler */
|
|
107
|
+
readonly payload?: unknown;
|
|
108
|
+
}
|
|
109
|
+
/** Registry interface available to plugins during init */
|
|
110
|
+
export interface PluginRegistries {
|
|
111
|
+
registerBlockType(type: string, config: BlockTypeConfig): void;
|
|
112
|
+
registerMarkType(type: string, config: MarkTypeConfig): void;
|
|
113
|
+
registerCommand(name: string, handler: CommandHandler): void;
|
|
114
|
+
registerInputRule(rule: InputRule): void;
|
|
115
|
+
registerShortcut(descriptor: ShortcutDescriptor, command: string, payload?: unknown): void;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Normalize a CommandHandler (function or descriptor) to a CommandDescriptor.
|
|
119
|
+
*
|
|
120
|
+
* @param handler - A plain function or a CommandDescriptor
|
|
121
|
+
* @returns A normalized CommandDescriptor
|
|
122
|
+
*
|
|
123
|
+
* @example
|
|
124
|
+
* ```ts
|
|
125
|
+
* const desc = normalizeCommandHandler((engine) => engine.undo());
|
|
126
|
+
* // => { execute: (engine) => engine.undo() }
|
|
127
|
+
* ```
|
|
128
|
+
*/
|
|
129
|
+
export declare function normalizeCommandHandler(handler: CommandHandler): CommandDescriptor;
|
|
130
|
+
//# sourceMappingURL=registries.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registries.d.ts","sourceRoot":"","sources":["../src/registries.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,gDAAgD;AAChD,MAAM,WAAW,eAAe;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC;CACxD;AAED,+CAA+C;AAC/C,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC;CACxC;AAED,mDAAmD;AACnD,MAAM,WAAW,SAAS;IACxB,2DAA2D;IAC3D,OAAO,EAAE,MAAM,CAAC;IAChB,0CAA0C;IAC1C,OAAO,EAAE,CAAC,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACnF,mFAAmF;IACnF,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,iBAAiB;IAChC,0BAA0B;IAC1B,QAAQ,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACrE,2EAA2E;IAC3E,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC;IAC5E,wEAAwE;IACxE,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC;CAC3E;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,MAAM,cAAc,GACtB,CAAC,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC,GACpD,iBAAiB,CAAC;AAEtB;;;;;;;;;;GAUG;AACH,MAAM,WAAW,kBAAkB;IACjC,gEAAgE;IAChE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,4DAA4D;IAC5D,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC;IACvB,iCAAiC;IACjC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,sCAAsC;IACtC,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,eAAe;IAC9B,mCAAmC;IACnC,QAAQ,CAAC,UAAU,EAAE,kBAAkB,CAAC;IACxC,kEAAkE;IAClE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,sDAAsD;IACtD,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,0DAA0D;AAC1D,MAAM,WAAW,gBAAgB;IAC/B,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAAC;IAC/D,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,IAAI,CAAC;IAC7D,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,IAAI,CAAC;IAC7D,iBAAiB,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI,CAAC;IACzC,gBAAgB,CAAC,UAAU,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;CAC5F;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,cAAc,GAAG,iBAAiB,CAKlF"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin registries — block types, mark types, commands, input rules, and shortcuts.
|
|
3
|
+
* See SPEC.md §5.2 and docs/spec/plugin-contract.md for specification.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Normalize a CommandHandler (function or descriptor) to a CommandDescriptor.
|
|
7
|
+
*
|
|
8
|
+
* @param handler - A plain function or a CommandDescriptor
|
|
9
|
+
* @returns A normalized CommandDescriptor
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* const desc = normalizeCommandHandler((engine) => engine.undo());
|
|
14
|
+
* // => { execute: (engine) => engine.undo() }
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export function normalizeCommandHandler(handler) {
|
|
18
|
+
if (typeof handler === 'function') {
|
|
19
|
+
return { execute: handler };
|
|
20
|
+
}
|
|
21
|
+
return handler;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=registries.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registries.js","sourceRoot":"","sources":["../src/registries.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA6HH;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,uBAAuB,CAAC,OAAuB;IAC7D,IAAI,OAAO,OAAO,KAAK,UAAU,EAAE,CAAC;QAClC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;IAC9B,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure selection transform functions.
|
|
3
|
+
* Adjusts cursor/selection positions in response to document operations.
|
|
4
|
+
*/
|
|
5
|
+
import type { Selection, Operation } from '@rtif-sdk/core';
|
|
6
|
+
/**
|
|
7
|
+
* Transform a selection in response to an operation.
|
|
8
|
+
* Both anchor and focus are independently adjusted.
|
|
9
|
+
*
|
|
10
|
+
* @param selection - The current selection
|
|
11
|
+
* @param op - The operation that was applied
|
|
12
|
+
* @param mergePointOffset - For merge_block ops, the absolute offset where the merge happened
|
|
13
|
+
* @returns The transformed selection
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* const sel = { anchor: { offset: 0 }, focus: { offset: 5 } };
|
|
18
|
+
* const newSel = transformSelection(sel, { type: 'insert_text', offset: 0, text: 'hi' });
|
|
19
|
+
* // newSel = { anchor: { offset: 2 }, focus: { offset: 7 } }
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export declare function transformSelection(selection: Selection, op: Operation, mergePointOffset?: number): Selection;
|
|
23
|
+
//# sourceMappingURL=selection-transform.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selection-transform.d.ts","sourceRoot":"","sources":["../src/selection-transform.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AA6D3D;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,SAAS,EACpB,EAAE,EAAE,SAAS,EACb,gBAAgB,CAAC,EAAE,MAAM,GACxB,SAAS,CAaX"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure selection transform functions.
|
|
3
|
+
* Adjusts cursor/selection positions in response to document operations.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Transform a single offset in response to an operation.
|
|
7
|
+
* Returns the adjusted offset.
|
|
8
|
+
*
|
|
9
|
+
* @param offset - The absolute document offset to transform
|
|
10
|
+
* @param op - The operation that was applied
|
|
11
|
+
* @param mergePointOffset - For merge_block ops, the absolute offset of the merge point
|
|
12
|
+
* @returns The transformed offset
|
|
13
|
+
*/
|
|
14
|
+
function transformOffset(offset, op, mergePointOffset) {
|
|
15
|
+
switch (op.type) {
|
|
16
|
+
case 'insert_text': {
|
|
17
|
+
// Text inserted at op.offset pushes everything at or after forward
|
|
18
|
+
if (offset >= op.offset) {
|
|
19
|
+
return offset + op.text.length;
|
|
20
|
+
}
|
|
21
|
+
return offset;
|
|
22
|
+
}
|
|
23
|
+
case 'delete_text': {
|
|
24
|
+
const deleteEnd = op.offset + op.count;
|
|
25
|
+
if (offset <= op.offset) {
|
|
26
|
+
return offset;
|
|
27
|
+
}
|
|
28
|
+
if (offset <= deleteEnd) {
|
|
29
|
+
// Cursor was inside deleted range — collapse to delete start
|
|
30
|
+
return op.offset;
|
|
31
|
+
}
|
|
32
|
+
// Cursor was after deleted range
|
|
33
|
+
return offset - op.count;
|
|
34
|
+
}
|
|
35
|
+
case 'split_block': {
|
|
36
|
+
// A virtual \n is inserted at the split point.
|
|
37
|
+
// Positions AT the split point shift forward so the cursor lands
|
|
38
|
+
// at the start of the new block (standard Enter-key behaviour).
|
|
39
|
+
if (offset >= op.offset) {
|
|
40
|
+
return offset + 1;
|
|
41
|
+
}
|
|
42
|
+
return offset;
|
|
43
|
+
}
|
|
44
|
+
case 'merge_block': {
|
|
45
|
+
// A virtual \n is removed at the merge point
|
|
46
|
+
if (mergePointOffset !== undefined && offset > mergePointOffset) {
|
|
47
|
+
return offset - 1;
|
|
48
|
+
}
|
|
49
|
+
return offset;
|
|
50
|
+
}
|
|
51
|
+
// These operations don't change offsets
|
|
52
|
+
case 'set_span_marks':
|
|
53
|
+
case 'set_block_attrs':
|
|
54
|
+
case 'set_block_type':
|
|
55
|
+
case 'set_meta':
|
|
56
|
+
return offset;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Transform a selection in response to an operation.
|
|
61
|
+
* Both anchor and focus are independently adjusted.
|
|
62
|
+
*
|
|
63
|
+
* @param selection - The current selection
|
|
64
|
+
* @param op - The operation that was applied
|
|
65
|
+
* @param mergePointOffset - For merge_block ops, the absolute offset where the merge happened
|
|
66
|
+
* @returns The transformed selection
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```ts
|
|
70
|
+
* const sel = { anchor: { offset: 0 }, focus: { offset: 5 } };
|
|
71
|
+
* const newSel = transformSelection(sel, { type: 'insert_text', offset: 0, text: 'hi' });
|
|
72
|
+
* // newSel = { anchor: { offset: 2 }, focus: { offset: 7 } }
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
export function transformSelection(selection, op, mergePointOffset) {
|
|
76
|
+
const newAnchor = transformOffset(selection.anchor.offset, op, mergePointOffset);
|
|
77
|
+
const newFocus = transformOffset(selection.focus.offset, op, mergePointOffset);
|
|
78
|
+
// Avoid creating a new object if nothing changed
|
|
79
|
+
if (newAnchor === selection.anchor.offset && newFocus === selection.focus.offset) {
|
|
80
|
+
return selection;
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
anchor: { offset: newAnchor },
|
|
84
|
+
focus: { offset: newFocus },
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=selection-transform.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selection-transform.js","sourceRoot":"","sources":["../src/selection-transform.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH;;;;;;;;GAQG;AACH,SAAS,eAAe,CAAC,MAAc,EAAE,EAAa,EAAE,gBAAyB;IAC/E,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC;QAChB,KAAK,aAAa,CAAC,CAAC,CAAC;YACnB,mEAAmE;YACnE,IAAI,MAAM,IAAI,EAAE,CAAC,MAAM,EAAE,CAAC;gBACxB,OAAO,MAAM,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC;YACjC,CAAC;YACD,OAAO,MAAM,CAAC;QAChB,CAAC;QAED,KAAK,aAAa,CAAC,CAAC,CAAC;YACnB,MAAM,SAAS,GAAG,EAAE,CAAC,MAAM,GAAG,EAAE,CAAC,KAAK,CAAC;YACvC,IAAI,MAAM,IAAI,EAAE,CAAC,MAAM,EAAE,CAAC;gBACxB,OAAO,MAAM,CAAC;YAChB,CAAC;YACD,IAAI,MAAM,IAAI,SAAS,EAAE,CAAC;gBACxB,6DAA6D;gBAC7D,OAAO,EAAE,CAAC,MAAM,CAAC;YACnB,CAAC;YACD,iCAAiC;YACjC,OAAO,MAAM,GAAG,EAAE,CAAC,KAAK,CAAC;QAC3B,CAAC;QAED,KAAK,aAAa,CAAC,CAAC,CAAC;YACnB,+CAA+C;YAC/C,iEAAiE;YACjE,gEAAgE;YAChE,IAAI,MAAM,IAAI,EAAE,CAAC,MAAM,EAAE,CAAC;gBACxB,OAAO,MAAM,GAAG,CAAC,CAAC;YACpB,CAAC;YACD,OAAO,MAAM,CAAC;QAChB,CAAC;QAED,KAAK,aAAa,CAAC,CAAC,CAAC;YACnB,6CAA6C;YAC7C,IAAI,gBAAgB,KAAK,SAAS,IAAI,MAAM,GAAG,gBAAgB,EAAE,CAAC;gBAChE,OAAO,MAAM,GAAG,CAAC,CAAC;YACpB,CAAC;YACD,OAAO,MAAM,CAAC;QAChB,CAAC;QAED,wCAAwC;QACxC,KAAK,gBAAgB,CAAC;QACtB,KAAK,iBAAiB,CAAC;QACvB,KAAK,gBAAgB,CAAC;QACtB,KAAK,UAAU;YACb,OAAO,MAAM,CAAC;IAClB,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,kBAAkB,CAChC,SAAoB,EACpB,EAAa,EACb,gBAAyB;IAEzB,MAAM,SAAS,GAAG,eAAe,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,EAAE,gBAAgB,CAAC,CAAC;IACjF,MAAM,QAAQ,GAAG,eAAe,CAAC,SAAS,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,EAAE,gBAAgB,CAAC,CAAC;IAE/E,iDAAiD;IACjD,IAAI,SAAS,KAAK,SAAS,CAAC,MAAM,CAAC,MAAM,IAAI,QAAQ,KAAK,SAAS,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;QACjF,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO;QACL,MAAM,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE;QAC7B,KAAK,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE;KAC5B,CAAC;AACJ,CAAC"}
|