@rtif-sdk/web 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 +67 -0
- package/dist/block-drag-handler.d.ts +189 -0
- package/dist/block-drag-handler.d.ts.map +1 -0
- package/dist/block-drag-handler.js +745 -0
- package/dist/block-drag-handler.js.map +1 -0
- package/dist/block-renderer.d.ts +402 -0
- package/dist/block-renderer.d.ts.map +1 -0
- package/dist/block-renderer.js +424 -0
- package/dist/block-renderer.js.map +1 -0
- package/dist/clipboard.d.ts +178 -0
- package/dist/clipboard.d.ts.map +1 -0
- package/dist/clipboard.js +432 -0
- package/dist/clipboard.js.map +1 -0
- package/dist/command-bus.d.ts +113 -0
- package/dist/command-bus.d.ts.map +1 -0
- package/dist/command-bus.js +70 -0
- package/dist/command-bus.js.map +1 -0
- package/dist/composition.d.ts +220 -0
- package/dist/composition.d.ts.map +1 -0
- package/dist/composition.js +271 -0
- package/dist/composition.js.map +1 -0
- package/dist/content-extraction.d.ts +69 -0
- package/dist/content-extraction.d.ts.map +1 -0
- package/dist/content-extraction.js +228 -0
- package/dist/content-extraction.js.map +1 -0
- package/dist/content-handler-file.d.ts +40 -0
- package/dist/content-handler-file.d.ts.map +1 -0
- package/dist/content-handler-file.js +91 -0
- package/dist/content-handler-file.js.map +1 -0
- package/dist/content-handler-image.d.ts +82 -0
- package/dist/content-handler-image.d.ts.map +1 -0
- package/dist/content-handler-image.js +120 -0
- package/dist/content-handler-image.js.map +1 -0
- package/dist/content-handler-url.d.ts +129 -0
- package/dist/content-handler-url.d.ts.map +1 -0
- package/dist/content-handler-url.js +244 -0
- package/dist/content-handler-url.js.map +1 -0
- package/dist/content-handlers.d.ts +67 -0
- package/dist/content-handlers.d.ts.map +1 -0
- package/dist/content-handlers.js +263 -0
- package/dist/content-handlers.js.map +1 -0
- package/dist/content-pipeline.d.ts +383 -0
- package/dist/content-pipeline.d.ts.map +1 -0
- package/dist/content-pipeline.js +232 -0
- package/dist/content-pipeline.js.map +1 -0
- package/dist/cursor-nav.d.ts +149 -0
- package/dist/cursor-nav.d.ts.map +1 -0
- package/dist/cursor-nav.js +230 -0
- package/dist/cursor-nav.js.map +1 -0
- package/dist/cursor-rect.d.ts +65 -0
- package/dist/cursor-rect.d.ts.map +1 -0
- package/dist/cursor-rect.js +98 -0
- package/dist/cursor-rect.js.map +1 -0
- package/dist/drop-indicator.d.ts +108 -0
- package/dist/drop-indicator.d.ts.map +1 -0
- package/dist/drop-indicator.js +236 -0
- package/dist/drop-indicator.js.map +1 -0
- package/dist/editor.d.ts +41 -0
- package/dist/editor.d.ts.map +1 -0
- package/dist/editor.js +710 -0
- package/dist/editor.js.map +1 -0
- package/dist/floating-toolbar.d.ts +93 -0
- package/dist/floating-toolbar.d.ts.map +1 -0
- package/dist/floating-toolbar.js +159 -0
- package/dist/floating-toolbar.js.map +1 -0
- package/dist/index.d.ts +62 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +119 -0
- package/dist/index.js.map +1 -0
- package/dist/input-bridge.d.ts +273 -0
- package/dist/input-bridge.d.ts.map +1 -0
- package/dist/input-bridge.js +884 -0
- package/dist/input-bridge.js.map +1 -0
- package/dist/link-popover.d.ts +38 -0
- package/dist/link-popover.d.ts.map +1 -0
- package/dist/link-popover.js +278 -0
- package/dist/link-popover.js.map +1 -0
- package/dist/mark-renderer.d.ts +275 -0
- package/dist/mark-renderer.d.ts.map +1 -0
- package/dist/mark-renderer.js +210 -0
- package/dist/mark-renderer.js.map +1 -0
- package/dist/perf.d.ts +145 -0
- package/dist/perf.d.ts.map +1 -0
- package/dist/perf.js +260 -0
- package/dist/perf.js.map +1 -0
- package/dist/plugin-kit.d.ts +265 -0
- package/dist/plugin-kit.d.ts.map +1 -0
- package/dist/plugin-kit.js +234 -0
- package/dist/plugin-kit.js.map +1 -0
- package/dist/plugins/alignment-plugin.d.ts +68 -0
- package/dist/plugins/alignment-plugin.d.ts.map +1 -0
- package/dist/plugins/alignment-plugin.js +98 -0
- package/dist/plugins/alignment-plugin.js.map +1 -0
- package/dist/plugins/block-utils.d.ts +113 -0
- package/dist/plugins/block-utils.d.ts.map +1 -0
- package/dist/plugins/block-utils.js +191 -0
- package/dist/plugins/block-utils.js.map +1 -0
- package/dist/plugins/blockquote-plugin.d.ts +39 -0
- package/dist/plugins/blockquote-plugin.d.ts.map +1 -0
- package/dist/plugins/blockquote-plugin.js +88 -0
- package/dist/plugins/blockquote-plugin.js.map +1 -0
- package/dist/plugins/bold-plugin.d.ts +37 -0
- package/dist/plugins/bold-plugin.d.ts.map +1 -0
- package/dist/plugins/bold-plugin.js +48 -0
- package/dist/plugins/bold-plugin.js.map +1 -0
- package/dist/plugins/callout-plugin.d.ts +100 -0
- package/dist/plugins/callout-plugin.d.ts.map +1 -0
- package/dist/plugins/callout-plugin.js +200 -0
- package/dist/plugins/callout-plugin.js.map +1 -0
- package/dist/plugins/code-block-plugin.d.ts +62 -0
- package/dist/plugins/code-block-plugin.d.ts.map +1 -0
- package/dist/plugins/code-block-plugin.js +176 -0
- package/dist/plugins/code-block-plugin.js.map +1 -0
- package/dist/plugins/code-plugin.d.ts +37 -0
- package/dist/plugins/code-plugin.d.ts.map +1 -0
- package/dist/plugins/code-plugin.js +48 -0
- package/dist/plugins/code-plugin.js.map +1 -0
- package/dist/plugins/embed-plugin.d.ts +90 -0
- package/dist/plugins/embed-plugin.d.ts.map +1 -0
- package/dist/plugins/embed-plugin.js +147 -0
- package/dist/plugins/embed-plugin.js.map +1 -0
- package/dist/plugins/font-family-plugin.d.ts +58 -0
- package/dist/plugins/font-family-plugin.d.ts.map +1 -0
- package/dist/plugins/font-family-plugin.js +57 -0
- package/dist/plugins/font-family-plugin.js.map +1 -0
- package/dist/plugins/font-size-plugin.d.ts +57 -0
- package/dist/plugins/font-size-plugin.d.ts.map +1 -0
- package/dist/plugins/font-size-plugin.js +56 -0
- package/dist/plugins/font-size-plugin.js.map +1 -0
- package/dist/plugins/heading-plugin.d.ts +52 -0
- package/dist/plugins/heading-plugin.d.ts.map +1 -0
- package/dist/plugins/heading-plugin.js +114 -0
- package/dist/plugins/heading-plugin.js.map +1 -0
- package/dist/plugins/hr-plugin.d.ts +33 -0
- package/dist/plugins/hr-plugin.d.ts.map +1 -0
- package/dist/plugins/hr-plugin.js +75 -0
- package/dist/plugins/hr-plugin.js.map +1 -0
- package/dist/plugins/image-plugin.d.ts +115 -0
- package/dist/plugins/image-plugin.d.ts.map +1 -0
- package/dist/plugins/image-plugin.js +199 -0
- package/dist/plugins/image-plugin.js.map +1 -0
- package/dist/plugins/indent-plugin.d.ts +62 -0
- package/dist/plugins/indent-plugin.d.ts.map +1 -0
- package/dist/plugins/indent-plugin.js +128 -0
- package/dist/plugins/indent-plugin.js.map +1 -0
- package/dist/plugins/index.d.ts +45 -0
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/index.js +42 -0
- package/dist/plugins/index.js.map +1 -0
- package/dist/plugins/italic-plugin.d.ts +37 -0
- package/dist/plugins/italic-plugin.d.ts.map +1 -0
- package/dist/plugins/italic-plugin.js +48 -0
- package/dist/plugins/italic-plugin.js.map +1 -0
- package/dist/plugins/link-plugin.d.ts +129 -0
- package/dist/plugins/link-plugin.d.ts.map +1 -0
- package/dist/plugins/link-plugin.js +212 -0
- package/dist/plugins/link-plugin.js.map +1 -0
- package/dist/plugins/list-plugin.d.ts +53 -0
- package/dist/plugins/list-plugin.d.ts.map +1 -0
- package/dist/plugins/list-plugin.js +309 -0
- package/dist/plugins/list-plugin.js.map +1 -0
- package/dist/plugins/mark-utils.d.ts +173 -0
- package/dist/plugins/mark-utils.d.ts.map +1 -0
- package/dist/plugins/mark-utils.js +425 -0
- package/dist/plugins/mark-utils.js.map +1 -0
- package/dist/plugins/mention-plugin.d.ts +191 -0
- package/dist/plugins/mention-plugin.d.ts.map +1 -0
- package/dist/plugins/mention-plugin.js +295 -0
- package/dist/plugins/mention-plugin.js.map +1 -0
- package/dist/plugins/strikethrough-plugin.d.ts +37 -0
- package/dist/plugins/strikethrough-plugin.d.ts.map +1 -0
- package/dist/plugins/strikethrough-plugin.js +48 -0
- package/dist/plugins/strikethrough-plugin.js.map +1 -0
- package/dist/plugins/text-color-plugin.d.ts +57 -0
- package/dist/plugins/text-color-plugin.d.ts.map +1 -0
- package/dist/plugins/text-color-plugin.js +56 -0
- package/dist/plugins/text-color-plugin.js.map +1 -0
- package/dist/plugins/underline-plugin.d.ts +37 -0
- package/dist/plugins/underline-plugin.d.ts.map +1 -0
- package/dist/plugins/underline-plugin.js +48 -0
- package/dist/plugins/underline-plugin.js.map +1 -0
- package/dist/presets.d.ts +95 -0
- package/dist/presets.d.ts.map +1 -0
- package/dist/presets.js +159 -0
- package/dist/presets.js.map +1 -0
- package/dist/renderer.d.ts +125 -0
- package/dist/renderer.d.ts.map +1 -0
- package/dist/renderer.js +415 -0
- package/dist/renderer.js.map +1 -0
- package/dist/scroll-to-cursor.d.ts +25 -0
- package/dist/scroll-to-cursor.d.ts.map +1 -0
- package/dist/scroll-to-cursor.js +59 -0
- package/dist/scroll-to-cursor.js.map +1 -0
- package/dist/selection-sync.d.ts +159 -0
- package/dist/selection-sync.d.ts.map +1 -0
- package/dist/selection-sync.js +527 -0
- package/dist/selection-sync.js.map +1 -0
- package/dist/shortcut-handler.d.ts +98 -0
- package/dist/shortcut-handler.d.ts.map +1 -0
- package/dist/shortcut-handler.js +155 -0
- package/dist/shortcut-handler.js.map +1 -0
- package/dist/toolbar.d.ts +103 -0
- package/dist/toolbar.d.ts.map +1 -0
- package/dist/toolbar.js +134 -0
- package/dist/toolbar.js.map +1 -0
- package/dist/trigger-manager.d.ts +205 -0
- package/dist/trigger-manager.d.ts.map +1 -0
- package/dist/trigger-manager.js +466 -0
- package/dist/trigger-manager.js.map +1 -0
- package/dist/types.d.ts +216 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +30 -0
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared utilities for inline mark plugins.
|
|
3
|
+
*
|
|
4
|
+
* Provides `createToggleMarkCommand()` for building mark toggle commands,
|
|
5
|
+
* `toggleMark()` for the toggle logic, and `isMarkActiveAtSelection()`
|
|
6
|
+
* for querying active mark state.
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
import { docLength, blockTextLength, resolve } from '@rtif-sdk/core';
|
|
11
|
+
/**
|
|
12
|
+
* Create a `CommandDescriptor` for toggling a mark type on/off.
|
|
13
|
+
*
|
|
14
|
+
* When the cursor is collapsed, toggles a pending mark.
|
|
15
|
+
* When a selection exists, applies or removes the mark via `set_span_marks`.
|
|
16
|
+
*
|
|
17
|
+
* @param markType - The mark type string (e.g., "bold", "italic")
|
|
18
|
+
* @param markValue - The mark value to apply (default: `true`)
|
|
19
|
+
* @returns A `CommandDescriptor` with execute, canExecute, and isActive
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* const boldCommand = createToggleMarkCommand('bold');
|
|
24
|
+
* registries.registerCommand('toggleMark:bold', boldCommand);
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export function createToggleMarkCommand(markType, markValue = true) {
|
|
28
|
+
return {
|
|
29
|
+
execute(engine) {
|
|
30
|
+
toggleMark(engine, markType, markValue);
|
|
31
|
+
},
|
|
32
|
+
canExecute(_engine) {
|
|
33
|
+
return true;
|
|
34
|
+
},
|
|
35
|
+
isActive(engine) {
|
|
36
|
+
return isMarkActiveAtSelection(engine, markType);
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Toggle a mark on the current selection or as a pending mark.
|
|
42
|
+
*
|
|
43
|
+
* If the cursor is collapsed, toggles the mark as a pending mark (the next
|
|
44
|
+
* typed text will have or not have the mark). If a range is selected,
|
|
45
|
+
* dispatches `set_span_marks` to apply or remove the mark.
|
|
46
|
+
*
|
|
47
|
+
* @param engine - The editor engine instance
|
|
48
|
+
* @param markType - The mark type string (e.g., "bold")
|
|
49
|
+
* @param markValue - The value to apply (default: `true`)
|
|
50
|
+
* @returns `true` if the toggle was performed
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```ts
|
|
54
|
+
* toggleMark(engine, 'bold');
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export function toggleMark(engine, markType, markValue = true) {
|
|
58
|
+
const { anchor, focus } = engine.state.selection;
|
|
59
|
+
const start = Math.min(anchor.offset, focus.offset);
|
|
60
|
+
const end = Math.max(anchor.offset, focus.offset);
|
|
61
|
+
if (start === end) {
|
|
62
|
+
// Collapsed cursor — toggle pending mark
|
|
63
|
+
engine.togglePendingMark(markType, markValue);
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
// Range selection — check if mark is active across the range
|
|
67
|
+
const { common } = engine.getMarksInRange(start, end - start);
|
|
68
|
+
const isActive = markType in common;
|
|
69
|
+
engine.dispatch({
|
|
70
|
+
type: 'set_span_marks',
|
|
71
|
+
offset: start,
|
|
72
|
+
count: end - start,
|
|
73
|
+
marks: { [markType]: isActive ? null : markValue },
|
|
74
|
+
});
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Check whether a mark type is active at the current selection.
|
|
79
|
+
*
|
|
80
|
+
* For collapsed cursors, checks the marks at the cursor offset and
|
|
81
|
+
* pending marks. For range selections, checks if the mark is present
|
|
82
|
+
* across the entire range (`common` marks).
|
|
83
|
+
*
|
|
84
|
+
* @param engine - The editor engine instance
|
|
85
|
+
* @param markType - The mark type to check (e.g., "bold")
|
|
86
|
+
* @returns `true` if the mark is active at the selection
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```ts
|
|
90
|
+
* if (isMarkActiveAtSelection(engine, 'bold')) {
|
|
91
|
+
* // Bold button should show as pressed
|
|
92
|
+
* }
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
export function isMarkActiveAtSelection(engine, markType) {
|
|
96
|
+
const { anchor, focus } = engine.state.selection;
|
|
97
|
+
const start = Math.min(anchor.offset, focus.offset);
|
|
98
|
+
const end = Math.max(anchor.offset, focus.offset);
|
|
99
|
+
// Check pending marks first (they override document marks for collapsed cursor)
|
|
100
|
+
const pending = engine.getPendingMarks();
|
|
101
|
+
if (markType in pending) {
|
|
102
|
+
return pending[markType] != null;
|
|
103
|
+
}
|
|
104
|
+
if (start === end) {
|
|
105
|
+
// Collapsed cursor — check marks at offset
|
|
106
|
+
const marks = engine.getMarksAtOffset(start);
|
|
107
|
+
return markType in marks;
|
|
108
|
+
}
|
|
109
|
+
// Range selection — check if mark is common across the entire range
|
|
110
|
+
const { common } = engine.getMarksInRange(start, end - start);
|
|
111
|
+
return markType in common;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Create a `CommandDescriptor` that sets a parameterized mark value.
|
|
115
|
+
*
|
|
116
|
+
* On a collapsed cursor, sets the mark as a pending mark so the next typed
|
|
117
|
+
* text inherits the value. On a range selection, dispatches `set_span_marks`
|
|
118
|
+
* to apply the mark to the selected text.
|
|
119
|
+
*
|
|
120
|
+
* @param markType - The mark type string (e.g., "color", "fontSize")
|
|
121
|
+
* @param extractValue - A function that extracts the mark value from the command payload
|
|
122
|
+
* @returns A `CommandDescriptor` with execute and isActive
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* ```ts
|
|
126
|
+
* const cmd = createSetMarkCommand('color', (p) => (p as { color: string }).color);
|
|
127
|
+
* registries.registerCommand('setTextColor', cmd);
|
|
128
|
+
* ```
|
|
129
|
+
*/
|
|
130
|
+
export function createSetMarkCommand(markType, extractValue) {
|
|
131
|
+
return {
|
|
132
|
+
execute(engine, payload) {
|
|
133
|
+
const value = extractValue(payload);
|
|
134
|
+
const { anchor, focus } = engine.state.selection;
|
|
135
|
+
const start = Math.min(anchor.offset, focus.offset);
|
|
136
|
+
const end = Math.max(anchor.offset, focus.offset);
|
|
137
|
+
if (start === end) {
|
|
138
|
+
// Collapsed cursor — set pending mark
|
|
139
|
+
engine.setPendingMark(markType, value);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
// Range selection — apply mark
|
|
143
|
+
engine.dispatch({
|
|
144
|
+
type: 'set_span_marks',
|
|
145
|
+
offset: start,
|
|
146
|
+
count: end - start,
|
|
147
|
+
marks: { [markType]: value },
|
|
148
|
+
});
|
|
149
|
+
},
|
|
150
|
+
isActive(engine) {
|
|
151
|
+
return isMarkActiveAtSelection(engine, markType);
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Create a `CommandDescriptor` that removes a mark.
|
|
157
|
+
*
|
|
158
|
+
* On a collapsed cursor, sets the pending mark to `null` so the next typed
|
|
159
|
+
* text will not have the mark. On a range selection, dispatches `set_span_marks`
|
|
160
|
+
* with `null` to remove the mark from the selected text.
|
|
161
|
+
*
|
|
162
|
+
* @param markType - The mark type string (e.g., "color", "fontSize")
|
|
163
|
+
* @returns A `CommandDescriptor` with execute and canExecute
|
|
164
|
+
*
|
|
165
|
+
* @example
|
|
166
|
+
* ```ts
|
|
167
|
+
* const cmd = createRemoveMarkCommand('color');
|
|
168
|
+
* registries.registerCommand('removeTextColor', cmd);
|
|
169
|
+
* ```
|
|
170
|
+
*/
|
|
171
|
+
export function createRemoveMarkCommand(markType) {
|
|
172
|
+
return {
|
|
173
|
+
execute(engine) {
|
|
174
|
+
const { anchor, focus } = engine.state.selection;
|
|
175
|
+
const start = Math.min(anchor.offset, focus.offset);
|
|
176
|
+
const end = Math.max(anchor.offset, focus.offset);
|
|
177
|
+
if (start === end) {
|
|
178
|
+
// Collapsed cursor — set pending mark to null (remove on next insert)
|
|
179
|
+
engine.setPendingMark(markType, null);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
// Range selection — remove mark
|
|
183
|
+
engine.dispatch({
|
|
184
|
+
type: 'set_span_marks',
|
|
185
|
+
offset: start,
|
|
186
|
+
count: end - start,
|
|
187
|
+
marks: { [markType]: null },
|
|
188
|
+
});
|
|
189
|
+
},
|
|
190
|
+
canExecute(engine) {
|
|
191
|
+
return isMarkActiveAtSelection(engine, markType);
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
// ---------------------------------------------------------------------------
|
|
196
|
+
// Contiguous mark range finder
|
|
197
|
+
// ---------------------------------------------------------------------------
|
|
198
|
+
/**
|
|
199
|
+
* Find the absolute document range of a contiguous mark span containing
|
|
200
|
+
* the given offset.
|
|
201
|
+
*
|
|
202
|
+
* Scans the spans of the block containing the offset to find the contiguous
|
|
203
|
+
* range of spans that share the same mark key (with a non-null object value).
|
|
204
|
+
*
|
|
205
|
+
* @param doc - The document to search
|
|
206
|
+
* @param offset - The absolute offset to find the mark at
|
|
207
|
+
* @param markKey - The mark key to search for (e.g., 'link', 'mention')
|
|
208
|
+
* @returns The absolute offset and count of the mark range, or null if no mark at offset
|
|
209
|
+
*
|
|
210
|
+
* @example
|
|
211
|
+
* ```ts
|
|
212
|
+
* const range = findContiguousMarkRange(doc, 5, 'link');
|
|
213
|
+
* if (range) {
|
|
214
|
+
* engine.dispatch({ type: 'set_span_marks', offset: range.offset, count: range.count, marks: { link: null } });
|
|
215
|
+
* }
|
|
216
|
+
* ```
|
|
217
|
+
*/
|
|
218
|
+
export function findContiguousMarkRange(doc, offset, markKey) {
|
|
219
|
+
const totalLen = docLength(doc);
|
|
220
|
+
if (offset < 0 || offset > totalLen)
|
|
221
|
+
return null;
|
|
222
|
+
// Resolve to block and local offset
|
|
223
|
+
let blockStartOffset = 0;
|
|
224
|
+
for (let bi = 0; bi < doc.blocks.length; bi++) {
|
|
225
|
+
const block = doc.blocks[bi];
|
|
226
|
+
const blockLen = blockTextLength(block);
|
|
227
|
+
if (offset <= blockStartOffset + blockLen) {
|
|
228
|
+
const localOffset = offset - blockStartOffset;
|
|
229
|
+
return findContiguousMarkRangeInBlock(block, localOffset, blockStartOffset, markKey);
|
|
230
|
+
}
|
|
231
|
+
blockStartOffset += blockLen + 1; // +1 for virtual \n
|
|
232
|
+
}
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
/** Find a contiguous mark range within a specific block at a local offset. */
|
|
236
|
+
function findContiguousMarkRangeInBlock(block, localOffset, blockStartOffset, markKey) {
|
|
237
|
+
// Find which span the offset is in
|
|
238
|
+
let spanStart = 0;
|
|
239
|
+
let targetSpanIndex = -1;
|
|
240
|
+
for (let si = 0; si < block.spans.length; si++) {
|
|
241
|
+
const span = block.spans[si];
|
|
242
|
+
const spanEnd = spanStart + span.text.length;
|
|
243
|
+
if (localOffset <= spanEnd && localOffset >= spanStart) {
|
|
244
|
+
// At span boundary, prefer previous span (consistent with mark inheritance)
|
|
245
|
+
if (localOffset === spanStart && si > 0) {
|
|
246
|
+
const prevSpan = block.spans[si - 1];
|
|
247
|
+
if (hasMarkKey(prevSpan, markKey)) {
|
|
248
|
+
targetSpanIndex = si - 1;
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
if (hasMarkKey(span, markKey)) {
|
|
253
|
+
targetSpanIndex = si;
|
|
254
|
+
break;
|
|
255
|
+
}
|
|
256
|
+
// Check previous span at boundary
|
|
257
|
+
if (localOffset === spanStart && si > 0 && hasMarkKey(block.spans[si - 1], markKey)) {
|
|
258
|
+
targetSpanIndex = si - 1;
|
|
259
|
+
break;
|
|
260
|
+
}
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
spanStart = spanEnd;
|
|
264
|
+
}
|
|
265
|
+
if (targetSpanIndex < 0)
|
|
266
|
+
return null;
|
|
267
|
+
// Expand left — find first contiguous span with same mark
|
|
268
|
+
let rangeStart = 0;
|
|
269
|
+
for (let i = 0; i < targetSpanIndex; i++) {
|
|
270
|
+
rangeStart += block.spans[i].text.length;
|
|
271
|
+
}
|
|
272
|
+
let startIdx = targetSpanIndex;
|
|
273
|
+
while (startIdx > 0 && hasMarkKey(block.spans[startIdx - 1], markKey)) {
|
|
274
|
+
startIdx--;
|
|
275
|
+
rangeStart -= block.spans[startIdx].text.length;
|
|
276
|
+
}
|
|
277
|
+
// Expand right — find last contiguous span with same mark
|
|
278
|
+
let rangeEnd = rangeStart;
|
|
279
|
+
let endIdx = startIdx;
|
|
280
|
+
while (endIdx < block.spans.length && hasMarkKey(block.spans[endIdx], markKey)) {
|
|
281
|
+
rangeEnd += block.spans[endIdx].text.length;
|
|
282
|
+
endIdx++;
|
|
283
|
+
}
|
|
284
|
+
if (rangeEnd <= rangeStart)
|
|
285
|
+
return null;
|
|
286
|
+
return {
|
|
287
|
+
offset: blockStartOffset + rangeStart,
|
|
288
|
+
count: rangeEnd - rangeStart,
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
// ---------------------------------------------------------------------------
|
|
292
|
+
// Atomic mark cursor adjustment
|
|
293
|
+
// ---------------------------------------------------------------------------
|
|
294
|
+
/**
|
|
295
|
+
* Adjust an offset that falls inside an atomic mark range to the nearest boundary.
|
|
296
|
+
*
|
|
297
|
+
* When arrow keys or clicks land the cursor inside a `contenteditable="false"`
|
|
298
|
+
* span (e.g., an @mention), this function snaps the offset to the start or end
|
|
299
|
+
* of the atomic range based on the movement direction.
|
|
300
|
+
*
|
|
301
|
+
* Iterates up to `MAX_ITERATIONS` to handle adjacent atomic marks (e.g., two
|
|
302
|
+
* mentions side by side) — each iteration snaps to a boundary, then re-checks
|
|
303
|
+
* whether the boundary itself is inside another atomic range.
|
|
304
|
+
*
|
|
305
|
+
* @param offset - The absolute document offset to adjust
|
|
306
|
+
* @param direction - Which direction to snap: 'forward' snaps to end, 'backward' snaps to start, 'nearest' picks closer boundary
|
|
307
|
+
* @param findAtomicRange - Function that returns the atomic mark range at an offset, or null
|
|
308
|
+
* @returns The adjusted offset (unchanged if not inside an atomic range)
|
|
309
|
+
*
|
|
310
|
+
* @example
|
|
311
|
+
* ```ts
|
|
312
|
+
* // Mention "@alice" spans offsets 5..10
|
|
313
|
+
* const find = (o: number) =>
|
|
314
|
+
* o > 5 && o < 10 ? { offset: 5, count: 5 } : null;
|
|
315
|
+
* adjustOffsetAroundAtomicMarks(7, 'forward', find); // => 10
|
|
316
|
+
* adjustOffsetAroundAtomicMarks(7, 'backward', find); // => 5
|
|
317
|
+
* ```
|
|
318
|
+
*/
|
|
319
|
+
export function adjustOffsetAroundAtomicMarks(offset, direction, findAtomicRange) {
|
|
320
|
+
const MAX_ITERATIONS = 10;
|
|
321
|
+
let adjusted = offset;
|
|
322
|
+
for (let i = 0; i < MAX_ITERATIONS; i++) {
|
|
323
|
+
const range = findAtomicRange(adjusted);
|
|
324
|
+
if (!range)
|
|
325
|
+
return adjusted;
|
|
326
|
+
// Offset is at the boundary (start or end) — not truly "inside"
|
|
327
|
+
if (adjusted === range.offset || adjusted === range.offset + range.count) {
|
|
328
|
+
return adjusted;
|
|
329
|
+
}
|
|
330
|
+
// Snap based on direction
|
|
331
|
+
if (direction === 'forward') {
|
|
332
|
+
adjusted = range.offset + range.count;
|
|
333
|
+
}
|
|
334
|
+
else if (direction === 'backward') {
|
|
335
|
+
adjusted = range.offset;
|
|
336
|
+
}
|
|
337
|
+
else {
|
|
338
|
+
// nearest: pick the closer boundary
|
|
339
|
+
const distToStart = adjusted - range.offset;
|
|
340
|
+
const distToEnd = (range.offset + range.count) - adjusted;
|
|
341
|
+
adjusted = distToStart <= distToEnd ? range.offset : range.offset + range.count;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
return adjusted;
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Adjust an absolute offset so it does not land inside an atomic block.
|
|
348
|
+
*
|
|
349
|
+
* When the cursor resolves to a block whose type is atomic (e.g., HR, image,
|
|
350
|
+
* embed), snap it to the nearest non-atomic block boundary based on the
|
|
351
|
+
* inferred movement direction.
|
|
352
|
+
*
|
|
353
|
+
* @param doc - The document
|
|
354
|
+
* @param offset - The absolute offset to adjust
|
|
355
|
+
* @param direction - Movement direction for choosing which boundary to snap to
|
|
356
|
+
* @param isAtomicBlock - Predicate that returns true for atomic block types
|
|
357
|
+
* @returns The adjusted offset, or the original if no adjustment needed
|
|
358
|
+
*
|
|
359
|
+
* @example
|
|
360
|
+
* ```ts
|
|
361
|
+
* const adjusted = adjustOffsetAroundAtomicBlocks(doc, 6, 'forward', (t) => t === 'hr');
|
|
362
|
+
* ```
|
|
363
|
+
*/
|
|
364
|
+
export function adjustOffsetAroundAtomicBlocks(doc, offset, direction, isAtomicBlock) {
|
|
365
|
+
// Resolve offset to block index
|
|
366
|
+
let blockIndex;
|
|
367
|
+
try {
|
|
368
|
+
const resolved = resolve(doc, offset);
|
|
369
|
+
blockIndex = resolved.blockIndex;
|
|
370
|
+
}
|
|
371
|
+
catch {
|
|
372
|
+
return offset;
|
|
373
|
+
}
|
|
374
|
+
const block = doc.blocks[blockIndex];
|
|
375
|
+
if (!block || !isAtomicBlock(block.type))
|
|
376
|
+
return offset;
|
|
377
|
+
// Compute absolute offset of the start of this atomic block
|
|
378
|
+
let blockStart = 0;
|
|
379
|
+
for (let i = 0; i < blockIndex; i++) {
|
|
380
|
+
blockStart += blockTextLength(doc.blocks[i]) + 1; // +1 for virtual \n
|
|
381
|
+
}
|
|
382
|
+
if (direction === 'forward') {
|
|
383
|
+
// Snap to start of next non-atomic block
|
|
384
|
+
for (let i = blockIndex + 1; i < doc.blocks.length; i++) {
|
|
385
|
+
const b = doc.blocks[i];
|
|
386
|
+
blockStart += blockTextLength(doc.blocks[i - 1]) + 1;
|
|
387
|
+
if (!isAtomicBlock(b.type)) {
|
|
388
|
+
return blockStart;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
// All remaining blocks are atomic — snap to end of document
|
|
392
|
+
return docLength(doc);
|
|
393
|
+
}
|
|
394
|
+
else if (direction === 'backward') {
|
|
395
|
+
// Snap to end of previous non-atomic block
|
|
396
|
+
for (let i = blockIndex - 1; i >= 0; i--) {
|
|
397
|
+
const b = doc.blocks[i];
|
|
398
|
+
if (!isAtomicBlock(b.type)) {
|
|
399
|
+
let prevEnd = 0;
|
|
400
|
+
for (let j = 0; j <= i; j++) {
|
|
401
|
+
prevEnd += blockTextLength(doc.blocks[j]);
|
|
402
|
+
if (j < i)
|
|
403
|
+
prevEnd += 1;
|
|
404
|
+
}
|
|
405
|
+
return prevEnd;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
// All previous blocks are atomic — snap to offset 0 if first block
|
|
409
|
+
// is also atomic, or to doc start
|
|
410
|
+
return 0;
|
|
411
|
+
}
|
|
412
|
+
else {
|
|
413
|
+
// nearest: try both directions, pick closer
|
|
414
|
+
const fwd = adjustOffsetAroundAtomicBlocks(doc, offset, 'forward', isAtomicBlock);
|
|
415
|
+
const bwd = adjustOffsetAroundAtomicBlocks(doc, offset, 'backward', isAtomicBlock);
|
|
416
|
+
return Math.abs(offset - bwd) <= Math.abs(offset - fwd) ? bwd : fwd;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
/** Check if a span has a non-null object mark for the given key. */
|
|
420
|
+
function hasMarkKey(span, markKey) {
|
|
421
|
+
return (span.marks != null &&
|
|
422
|
+
typeof span.marks[markKey] === 'object' &&
|
|
423
|
+
span.marks[markKey] !== null);
|
|
424
|
+
}
|
|
425
|
+
//# sourceMappingURL=mark-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mark-utils.js","sourceRoot":"","sources":["../../src/plugins/mark-utils.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mention plugin — parameterized mark for user mentions with trigger-based
|
|
3
|
+
* picker integration.
|
|
4
|
+
*
|
|
5
|
+
* The plugin has two parts:
|
|
6
|
+
* - **Engine plugin** (`plugin` property): Mark type registration + commands.
|
|
7
|
+
* - **Web `attach()`**: Trigger registration + mark renderer.
|
|
8
|
+
*
|
|
9
|
+
* Consumers render their own UI (dropdown, popover, etc.) using the
|
|
10
|
+
* session callbacks. The plugin handles all the RTIF operation plumbing.
|
|
11
|
+
*
|
|
12
|
+
* @module
|
|
13
|
+
*/
|
|
14
|
+
import type { Plugin } from '@rtif-sdk/engine';
|
|
15
|
+
import type { WebEditor } from '../types.js';
|
|
16
|
+
import type { TriggerDeactivateReason, TriggerPosition } from '../trigger-manager.js';
|
|
17
|
+
import type { Disposable } from '../types.js';
|
|
18
|
+
/**
|
|
19
|
+
* The value stored in a `mention` mark on a span.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* const mark: MentionMark = { id: 'user-123', displayName: 'Alice Johnson' };
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export interface MentionMark {
|
|
27
|
+
/** Unique identifier for the mentioned entity. */
|
|
28
|
+
readonly id: string;
|
|
29
|
+
/** Display name shown in the mention chip. */
|
|
30
|
+
readonly displayName: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* A type-safe mention session exposed to consumer callbacks.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```ts
|
|
37
|
+
* function onActivate(session: MentionSession) {
|
|
38
|
+
* showPicker(session.query, session.getTriggerRect());
|
|
39
|
+
* }
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export interface MentionSession {
|
|
43
|
+
/** The current query text (text typed after the trigger character). */
|
|
44
|
+
readonly query: string;
|
|
45
|
+
/** Absolute offset of the trigger character in the document. */
|
|
46
|
+
readonly triggerOffset: number;
|
|
47
|
+
/** Get the screen rect of the trigger character for positioning. */
|
|
48
|
+
getTriggerRect(): DOMRect | null;
|
|
49
|
+
/** Get the screen rect of the current cursor position. */
|
|
50
|
+
getCursorRect(): DOMRect | null;
|
|
51
|
+
/**
|
|
52
|
+
* Accept a mention — replaces trigger+query with a mention chip.
|
|
53
|
+
*
|
|
54
|
+
* @param mention - The mention to insert
|
|
55
|
+
*/
|
|
56
|
+
accept(mention: MentionMark): void;
|
|
57
|
+
/** Dismiss the session — leaves text as-is. */
|
|
58
|
+
dismiss(): void;
|
|
59
|
+
/** Returns true if this session is still active. */
|
|
60
|
+
isActive(): boolean;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Configuration for the mention plugin.
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* ```ts
|
|
67
|
+
* const mention = createMentionPlugin({
|
|
68
|
+
* onActivate(session) { showDropdown(session.query); },
|
|
69
|
+
* onChange(session) { filterDropdown(session.query); },
|
|
70
|
+
* onDeactivate() { hideDropdown(); },
|
|
71
|
+
* });
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
export interface MentionPluginConfig {
|
|
75
|
+
/** The trigger character. Default: `"@"`. */
|
|
76
|
+
readonly triggerChar?: string;
|
|
77
|
+
/** Position constraint for activation. Default: `"after-whitespace"`. */
|
|
78
|
+
readonly triggerPosition?: TriggerPosition;
|
|
79
|
+
/** Called when a mention session activates. */
|
|
80
|
+
readonly onActivate: (session: MentionSession) => void;
|
|
81
|
+
/** Called when a mention session deactivates. */
|
|
82
|
+
readonly onDeactivate?: (reason: TriggerDeactivateReason) => void;
|
|
83
|
+
/** Called when the query text changes. */
|
|
84
|
+
readonly onChange?: (session: MentionSession) => void;
|
|
85
|
+
/**
|
|
86
|
+
* Called on keydown during an active session.
|
|
87
|
+
* Return `true` to stop event propagation, `false` to let it through.
|
|
88
|
+
*/
|
|
89
|
+
readonly onKeyDown?: (session: MentionSession, event: KeyboardEvent) => boolean;
|
|
90
|
+
/** Whether to append a space after accepting a mention. Default: true. */
|
|
91
|
+
readonly appendSpace?: boolean;
|
|
92
|
+
/**
|
|
93
|
+
* Called when the mention trigger position may have changed due to scroll or resize.
|
|
94
|
+
* Re-query `session.getTriggerRect()` or `session.getCursorRect()` to reposition UI.
|
|
95
|
+
*/
|
|
96
|
+
readonly onPositionChange?: (session: MentionSession) => void;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Command name constants for the mention plugin.
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* ```ts
|
|
103
|
+
* engine.exec(MentionCommands.INSERT, { id: 'user-123', displayName: 'Alice' });
|
|
104
|
+
* engine.exec(MentionCommands.REMOVE);
|
|
105
|
+
* ```
|
|
106
|
+
*/
|
|
107
|
+
export declare const MentionCommands: {
|
|
108
|
+
readonly INSERT: "insertMention";
|
|
109
|
+
readonly REMOVE: "removeMention";
|
|
110
|
+
};
|
|
111
|
+
/**
|
|
112
|
+
* The result of `createMentionPlugin()`.
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* ```ts
|
|
116
|
+
* const mention = createMentionPlugin(config);
|
|
117
|
+
* engine.use(mention.plugin);
|
|
118
|
+
* const editor = createWebEditor({ root, engine, accessibleLabel: 'Editor' });
|
|
119
|
+
* const detach = mention.attach(editor);
|
|
120
|
+
* ```
|
|
121
|
+
*/
|
|
122
|
+
export interface MentionPluginResult {
|
|
123
|
+
/** The engine plugin — pass to `engine.use()`. */
|
|
124
|
+
readonly plugin: Plugin;
|
|
125
|
+
/**
|
|
126
|
+
* Attach the web-side behavior (trigger + renderer).
|
|
127
|
+
* Call AFTER `createWebEditor()`.
|
|
128
|
+
*
|
|
129
|
+
* @param editor - The web editor instance
|
|
130
|
+
* @returns A disposable to detach
|
|
131
|
+
*/
|
|
132
|
+
attach(editor: WebEditor): Disposable;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Create a mention plugin with trigger-based picker integration.
|
|
136
|
+
*
|
|
137
|
+
* @param config - The mention plugin configuration
|
|
138
|
+
* @returns A result with `plugin` (for engine) and `attach()` (for web editor)
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* ```ts
|
|
142
|
+
* const mention = createMentionPlugin({
|
|
143
|
+
* onActivate(session) { showDropdown(session.query, session.getTriggerRect()); },
|
|
144
|
+
* onDeactivate() { hideDropdown(); },
|
|
145
|
+
* onChange(session) { filterDropdown(session.query); },
|
|
146
|
+
* onKeyDown(session, event) {
|
|
147
|
+
* if (event.key === 'Enter') { session.accept(selected); return true; }
|
|
148
|
+
* return false;
|
|
149
|
+
* },
|
|
150
|
+
* });
|
|
151
|
+
*
|
|
152
|
+
* engine.use(mention.plugin);
|
|
153
|
+
* const editor = createWebEditor({ root, engine, accessibleLabel: 'Editor' });
|
|
154
|
+
* const detach = mention.attach(editor);
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
157
|
+
export declare function createMentionPlugin(config: MentionPluginConfig): MentionPluginResult;
|
|
158
|
+
/**
|
|
159
|
+
* Mention serializer for plaintext format.
|
|
160
|
+
* Produces: `@Alice` (just the display text, matching the span text).
|
|
161
|
+
*
|
|
162
|
+
* @example
|
|
163
|
+
* ```ts
|
|
164
|
+
* mentionPlaintextSerializer.serialize('@Alice', { id: 'u1', displayName: 'Alice' });
|
|
165
|
+
* // => '@Alice'
|
|
166
|
+
* ```
|
|
167
|
+
*/
|
|
168
|
+
export declare const mentionPlaintextSerializer: import('@rtif-sdk/core').MarkSerializer;
|
|
169
|
+
/**
|
|
170
|
+
* Mention serializer for markdown format.
|
|
171
|
+
* Produces: `[@Alice](mention:user-123)` (link-like syntax with mention: scheme).
|
|
172
|
+
*
|
|
173
|
+
* @example
|
|
174
|
+
* ```ts
|
|
175
|
+
* mentionMarkdownSerializer.serialize('@Alice', { id: 'u1', displayName: 'Alice' });
|
|
176
|
+
* // => '[@Alice](mention:u1)'
|
|
177
|
+
* ```
|
|
178
|
+
*/
|
|
179
|
+
export declare const mentionMarkdownSerializer: import('@rtif-sdk/core').MarkSerializer;
|
|
180
|
+
/**
|
|
181
|
+
* Mention serializer for HTML format.
|
|
182
|
+
* Produces: `<span data-mention-id="user-123" data-mention-name="Alice">@Alice</span>`.
|
|
183
|
+
*
|
|
184
|
+
* @example
|
|
185
|
+
* ```ts
|
|
186
|
+
* mentionHtmlSerializer.serialize('@Alice', { id: 'u1', displayName: 'Alice' });
|
|
187
|
+
* // => '<span data-mention-id="u1" data-mention-name="Alice">@Alice</span>'
|
|
188
|
+
* ```
|
|
189
|
+
*/
|
|
190
|
+
export declare const mentionHtmlSerializer: import('@rtif-sdk/core').MarkSerializer;
|
|
191
|
+
//# sourceMappingURL=mention-plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mention-plugin.d.ts","sourceRoot":"","sources":["../../src/plugins/mention-plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAiB,MAAM,kBAAkB,CAAC;AAE9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,KAAK,EAEV,uBAAuB,EACvB,eAAe,EAEhB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAM9C;;;;;;;GAOG;AACH,MAAM,WAAW,WAAW;IAC1B,kDAAkD;IAClD,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAEpB,8CAA8C;IAC9C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,cAAc;IAC7B,uEAAuE;IACvE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAEvB,gEAAgE;IAChE,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAE/B,oEAAoE;IACpE,cAAc,IAAI,OAAO,GAAG,IAAI,CAAC;IAEjC,0DAA0D;IAC1D,aAAa,IAAI,OAAO,GAAG,IAAI,CAAC;IAEhC;;;;OAIG;IACH,MAAM,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,CAAC;IAEnC,+CAA+C;IAC/C,OAAO,IAAI,IAAI,CAAC;IAEhB,oDAAoD;IACpD,QAAQ,IAAI,OAAO,CAAC;CACrB;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,mBAAmB;IAClC,6CAA6C;IAC7C,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAE9B,yEAAyE;IACzE,QAAQ,CAAC,eAAe,CAAC,EAAE,eAAe,CAAC;IAE3C,+CAA+C;IAC/C,QAAQ,CAAC,UAAU,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;IAEvD,iDAAiD;IACjD,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,uBAAuB,KAAK,IAAI,CAAC;IAElE,0CAA0C;IAC1C,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;IAEtD;;;OAGG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,aAAa,KAAK,OAAO,CAAC;IAEhF,0EAA0E;IAC1E,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAE/B;;;OAGG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;CAC/D;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe;;;CAGlB,CAAC;AAEX;;;;;;;;;;GAUG;AACH,MAAM,WAAW,mBAAmB;IAClC,kDAAkD;IAClD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAExB;;;;;;OAMG;IACH,MAAM,CAAC,MAAM,EAAE,SAAS,GAAG,UAAU,CAAC;CACvC;AAMD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,mBAAmB,GAAG,mBAAmB,CA0LpF;AAMD;;;;;;;;;GASG;AACH,eAAO,MAAM,0BAA0B,EAAE,OAAO,gBAAgB,EAAE,cAIjE,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,yBAAyB,EAAE,OAAO,gBAAgB,EAAE,cAehE,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,qBAAqB,EAAE,OAAO,gBAAgB,EAAE,cAgB5D,CAAC"}
|