@rtif-sdk/web 1.0.0 → 1.1.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/dist/block-drag-handler.d.ts +5 -0
- package/dist/block-drag-handler.d.ts.map +1 -1
- package/dist/block-drag-handler.js +28 -2
- package/dist/block-drag-handler.js.map +1 -1
- package/dist/block-renderer.d.ts +12 -6
- package/dist/block-renderer.d.ts.map +1 -1
- package/dist/block-renderer.js +98 -9
- package/dist/block-renderer.js.map +1 -1
- package/dist/block-type-dropdown.d.ts +78 -0
- package/dist/block-type-dropdown.d.ts.map +1 -0
- package/dist/block-type-dropdown.js +276 -0
- package/dist/block-type-dropdown.js.map +1 -0
- package/dist/color-picker.d.ts +91 -0
- package/dist/color-picker.d.ts.map +1 -0
- package/dist/color-picker.js +346 -0
- package/dist/color-picker.js.map +1 -0
- package/dist/content-handlers.d.ts +7 -8
- package/dist/content-handlers.d.ts.map +1 -1
- package/dist/content-handlers.js +122 -93
- package/dist/content-handlers.js.map +1 -1
- package/dist/editor.d.ts.map +1 -1
- package/dist/editor.js +117 -14
- package/dist/editor.js.map +1 -1
- package/dist/embed-utils.d.ts +148 -0
- package/dist/embed-utils.d.ts.map +1 -0
- package/dist/embed-utils.js +197 -0
- package/dist/embed-utils.js.map +1 -0
- package/dist/font-family-picker.d.ts +105 -0
- package/dist/font-family-picker.d.ts.map +1 -0
- package/dist/font-family-picker.js +314 -0
- package/dist/font-family-picker.js.map +1 -0
- package/dist/font-size-picker.d.ts +82 -0
- package/dist/font-size-picker.d.ts.map +1 -0
- package/dist/font-size-picker.js +290 -0
- package/dist/font-size-picker.js.map +1 -0
- package/dist/index.d.ts +12 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -1
- package/dist/index.js.map +1 -1
- package/dist/plugins/index.d.ts +2 -1
- package/dist/plugins/index.d.ts.map +1 -1
- package/dist/plugins/index.js +1 -1
- package/dist/plugins/index.js.map +1 -1
- package/dist/plugins/link-plugin.d.ts +4 -0
- package/dist/plugins/link-plugin.d.ts.map +1 -1
- package/dist/plugins/link-plugin.js +17 -0
- package/dist/plugins/link-plugin.js.map +1 -1
- package/dist/plugins/mark-utils.d.ts +31 -0
- package/dist/plugins/mark-utils.d.ts.map +1 -1
- package/dist/plugins/mark-utils.js +46 -0
- package/dist/plugins/mark-utils.js.map +1 -1
- package/dist/renderer.d.ts +2 -2
- package/dist/renderer.d.ts.map +1 -1
- package/dist/renderer.js +62 -16
- package/dist/renderer.js.map +1 -1
- package/dist/selection-sync.d.ts +2 -26
- package/dist/selection-sync.d.ts.map +1 -1
- package/dist/selection-sync.js +49 -13
- package/dist/selection-sync.js.map +1 -1
- package/dist/types.d.ts +24 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +17 -5
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Font family picker — a dropdown for selecting text font.
|
|
3
|
+
*
|
|
4
|
+
* Each option is rendered in its own font face so the user can preview
|
|
5
|
+
* how the font looks. Shows the current font name in the trigger button.
|
|
6
|
+
* Uses the same `mousedown + preventDefault()` pattern as the toolbar to
|
|
7
|
+
* avoid stealing editor focus.
|
|
8
|
+
*
|
|
9
|
+
* @module
|
|
10
|
+
*/
|
|
11
|
+
import { getMarkValueAtSelection } from './plugins/mark-utils.js';
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// Module-scoped counter for unique IDs
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
let instanceCounter = 0;
|
|
16
|
+
/**
|
|
17
|
+
* Default font families displayed in the picker dropdown.
|
|
18
|
+
*
|
|
19
|
+
* Organized by category: sans-serif, serif, monospace.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* const picker = createFontFamilyPicker({
|
|
24
|
+
* container, bus, engine,
|
|
25
|
+
* families: DEFAULT_FONT_FAMILIES.filter(f => f.stack.includes('sans')),
|
|
26
|
+
* });
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export const DEFAULT_FONT_FAMILIES = [
|
|
30
|
+
// Sans-serif
|
|
31
|
+
{ value: 'Arial', label: 'Arial', stack: 'Arial, sans-serif' },
|
|
32
|
+
{ value: 'Helvetica Neue', label: 'Helvetica Neue', stack: '"Helvetica Neue", Helvetica, sans-serif' },
|
|
33
|
+
{ value: 'Verdana', label: 'Verdana', stack: 'Verdana, sans-serif' },
|
|
34
|
+
{ value: 'Trebuchet MS', label: 'Trebuchet MS', stack: '"Trebuchet MS", sans-serif' },
|
|
35
|
+
{ value: 'Tahoma', label: 'Tahoma', stack: 'Tahoma, sans-serif' },
|
|
36
|
+
// Serif
|
|
37
|
+
{ value: 'Times New Roman', label: 'Times New Roman', stack: '"Times New Roman", Times, serif' },
|
|
38
|
+
{ value: 'Georgia', label: 'Georgia', stack: 'Georgia, serif' },
|
|
39
|
+
{ value: 'Garamond', label: 'Garamond', stack: 'Garamond, serif' },
|
|
40
|
+
{ value: 'Palatino', label: 'Palatino', stack: 'Palatino, "Palatino Linotype", serif' },
|
|
41
|
+
// Monospace
|
|
42
|
+
{ value: 'Courier New', label: 'Courier New', stack: '"Courier New", Courier, monospace' },
|
|
43
|
+
{ value: 'Lucida Console', label: 'Lucida Console', stack: '"Lucida Console", Monaco, monospace' },
|
|
44
|
+
{ value: 'Consolas', label: 'Consolas', stack: 'Consolas, monospace' },
|
|
45
|
+
{ value: 'Monaco', label: 'Monaco', stack: 'Monaco, monospace' },
|
|
46
|
+
];
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
// Factory
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
/**
|
|
51
|
+
* Create a font family picker dropdown backed by a {@link CommandBus}.
|
|
52
|
+
*
|
|
53
|
+
* The picker reflects the current font family by querying the engine's
|
|
54
|
+
* mark state. It subscribes to the bus for live updates. Each option
|
|
55
|
+
* is rendered in its own font face for preview.
|
|
56
|
+
*
|
|
57
|
+
* @param config - Picker configuration
|
|
58
|
+
* @returns A handle for lifecycle management
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```ts
|
|
62
|
+
* const picker = createFontFamilyPicker({
|
|
63
|
+
* container: toolbar,
|
|
64
|
+
* bus: editor.commandBus,
|
|
65
|
+
* engine,
|
|
66
|
+
* });
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
export function createFontFamilyPicker(config) {
|
|
70
|
+
const { container, bus, engine, families = DEFAULT_FONT_FAMILIES, } = config;
|
|
71
|
+
const doc = container.ownerDocument;
|
|
72
|
+
const id = ++instanceCounter;
|
|
73
|
+
const menuId = `rtif-font-family-menu-${id}`;
|
|
74
|
+
let isOpen = false;
|
|
75
|
+
let highlightIndex = -1;
|
|
76
|
+
let destroyed = false;
|
|
77
|
+
// Total selectable items: "Default" + family options
|
|
78
|
+
const totalItems = 1 + families.length;
|
|
79
|
+
// -----------------------------------------------------------------------
|
|
80
|
+
// DOM creation
|
|
81
|
+
// -----------------------------------------------------------------------
|
|
82
|
+
const wrapper = doc.createElement('div');
|
|
83
|
+
wrapper.className = 'rtif-font-family-picker';
|
|
84
|
+
// Trigger button
|
|
85
|
+
const trigger = doc.createElement('button');
|
|
86
|
+
trigger.type = 'button';
|
|
87
|
+
trigger.className = 'rtif-font-family-picker-trigger';
|
|
88
|
+
trigger.setAttribute('role', 'combobox');
|
|
89
|
+
trigger.setAttribute('aria-haspopup', 'listbox');
|
|
90
|
+
trigger.setAttribute('aria-expanded', 'false');
|
|
91
|
+
trigger.setAttribute('aria-controls', menuId);
|
|
92
|
+
trigger.setAttribute('aria-label', 'Font family');
|
|
93
|
+
const labelSpan = doc.createElement('span');
|
|
94
|
+
labelSpan.className = 'rtif-font-family-picker-label';
|
|
95
|
+
const chevron = doc.createElement('span');
|
|
96
|
+
chevron.className = 'rtif-font-family-picker-chevron';
|
|
97
|
+
chevron.textContent = '\u25BE'; // ▾
|
|
98
|
+
trigger.appendChild(labelSpan);
|
|
99
|
+
trigger.appendChild(chevron);
|
|
100
|
+
// Menu
|
|
101
|
+
const menu = doc.createElement('div');
|
|
102
|
+
menu.id = menuId;
|
|
103
|
+
menu.className = 'rtif-font-family-picker-menu';
|
|
104
|
+
menu.setAttribute('role', 'listbox');
|
|
105
|
+
menu.setAttribute('aria-label', 'Font families');
|
|
106
|
+
menu.hidden = true;
|
|
107
|
+
// "Default" option (removes font family)
|
|
108
|
+
const defaultOption = doc.createElement('div');
|
|
109
|
+
defaultOption.className = 'rtif-font-family-picker-option rtif-font-family-picker-default';
|
|
110
|
+
defaultOption.id = `rtif-font-family-opt-${id}-default`;
|
|
111
|
+
defaultOption.setAttribute('role', 'option');
|
|
112
|
+
defaultOption.setAttribute('aria-selected', 'false');
|
|
113
|
+
defaultOption.textContent = 'Default';
|
|
114
|
+
defaultOption.addEventListener('mousedown', (e) => {
|
|
115
|
+
e.preventDefault();
|
|
116
|
+
selectDefault();
|
|
117
|
+
});
|
|
118
|
+
menu.appendChild(defaultOption);
|
|
119
|
+
// Separator
|
|
120
|
+
const sep = doc.createElement('div');
|
|
121
|
+
sep.className = 'rtif-font-family-picker-separator';
|
|
122
|
+
sep.setAttribute('role', 'separator');
|
|
123
|
+
menu.appendChild(sep);
|
|
124
|
+
// Family options
|
|
125
|
+
const optionEls = [];
|
|
126
|
+
for (let i = 0; i < families.length; i++) {
|
|
127
|
+
const family = families[i];
|
|
128
|
+
const option = doc.createElement('div');
|
|
129
|
+
option.className = 'rtif-font-family-picker-option';
|
|
130
|
+
option.id = `rtif-font-family-opt-${id}-${i}`;
|
|
131
|
+
option.setAttribute('role', 'option');
|
|
132
|
+
option.setAttribute('aria-selected', 'false');
|
|
133
|
+
option.setAttribute('data-family', family.value);
|
|
134
|
+
option.textContent = family.label;
|
|
135
|
+
option.style.fontFamily = family.stack;
|
|
136
|
+
option.addEventListener('mousedown', (e) => {
|
|
137
|
+
e.preventDefault();
|
|
138
|
+
selectFamily(family.value);
|
|
139
|
+
});
|
|
140
|
+
menu.appendChild(option);
|
|
141
|
+
optionEls.push(option);
|
|
142
|
+
}
|
|
143
|
+
wrapper.appendChild(trigger);
|
|
144
|
+
wrapper.appendChild(menu);
|
|
145
|
+
container.appendChild(wrapper);
|
|
146
|
+
// -----------------------------------------------------------------------
|
|
147
|
+
// Active state detection
|
|
148
|
+
// -----------------------------------------------------------------------
|
|
149
|
+
function findFamilyOption(value) {
|
|
150
|
+
return families.find((f) => f.value === value);
|
|
151
|
+
}
|
|
152
|
+
function updateDisplay() {
|
|
153
|
+
const { value, isMixed } = getMarkValueAtSelection(engine, 'fontFamily');
|
|
154
|
+
if (isMixed) {
|
|
155
|
+
labelSpan.textContent = '\u2014'; // em dash
|
|
156
|
+
labelSpan.style.fontFamily = '';
|
|
157
|
+
}
|
|
158
|
+
else if (value != null) {
|
|
159
|
+
const familyStr = value;
|
|
160
|
+
const matched = findFamilyOption(familyStr);
|
|
161
|
+
labelSpan.textContent = matched?.label ?? familyStr;
|
|
162
|
+
labelSpan.style.fontFamily = matched?.stack ?? familyStr;
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
labelSpan.textContent = 'Default';
|
|
166
|
+
labelSpan.style.fontFamily = '';
|
|
167
|
+
}
|
|
168
|
+
// Update option selection
|
|
169
|
+
defaultOption.setAttribute('aria-selected', value == null && !isMixed ? 'true' : 'false');
|
|
170
|
+
for (const opt of optionEls) {
|
|
171
|
+
const optFamily = opt.getAttribute('data-family');
|
|
172
|
+
opt.setAttribute('aria-selected', !isMixed && value === optFamily ? 'true' : 'false');
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
// -----------------------------------------------------------------------
|
|
176
|
+
// Open / close
|
|
177
|
+
// -----------------------------------------------------------------------
|
|
178
|
+
function openMenu() {
|
|
179
|
+
if (isOpen)
|
|
180
|
+
return;
|
|
181
|
+
isOpen = true;
|
|
182
|
+
highlightIndex = -1;
|
|
183
|
+
menu.hidden = false;
|
|
184
|
+
trigger.setAttribute('aria-expanded', 'true');
|
|
185
|
+
trigger.removeAttribute('aria-activedescendant');
|
|
186
|
+
doc.addEventListener('mousedown', onDocumentMouseDown);
|
|
187
|
+
}
|
|
188
|
+
function closeMenu() {
|
|
189
|
+
if (!isOpen)
|
|
190
|
+
return;
|
|
191
|
+
isOpen = false;
|
|
192
|
+
highlightIndex = -1;
|
|
193
|
+
menu.hidden = true;
|
|
194
|
+
trigger.setAttribute('aria-expanded', 'false');
|
|
195
|
+
trigger.removeAttribute('aria-activedescendant');
|
|
196
|
+
// Clear highlight
|
|
197
|
+
defaultOption.classList.remove('rtif-font-family-picker-option-highlighted');
|
|
198
|
+
for (const el of optionEls) {
|
|
199
|
+
el.classList.remove('rtif-font-family-picker-option-highlighted');
|
|
200
|
+
}
|
|
201
|
+
doc.removeEventListener('mousedown', onDocumentMouseDown);
|
|
202
|
+
}
|
|
203
|
+
// -----------------------------------------------------------------------
|
|
204
|
+
// Selection
|
|
205
|
+
// -----------------------------------------------------------------------
|
|
206
|
+
function selectDefault() {
|
|
207
|
+
bus.execute('removeFontFamily');
|
|
208
|
+
closeMenu();
|
|
209
|
+
}
|
|
210
|
+
function selectFamily(family) {
|
|
211
|
+
bus.execute('setFontFamily', { family });
|
|
212
|
+
closeMenu();
|
|
213
|
+
}
|
|
214
|
+
// -----------------------------------------------------------------------
|
|
215
|
+
// Keyboard navigation
|
|
216
|
+
// -----------------------------------------------------------------------
|
|
217
|
+
function getOptionId(index) {
|
|
218
|
+
if (index === 0)
|
|
219
|
+
return defaultOption.id;
|
|
220
|
+
return optionEls[index - 1]?.id ?? '';
|
|
221
|
+
}
|
|
222
|
+
function onKeyDown(e) {
|
|
223
|
+
if (!isOpen)
|
|
224
|
+
return;
|
|
225
|
+
switch (e.key) {
|
|
226
|
+
case 'ArrowDown': {
|
|
227
|
+
e.preventDefault();
|
|
228
|
+
highlightIndex = (highlightIndex + 1) % totalItems;
|
|
229
|
+
updateHighlight();
|
|
230
|
+
break;
|
|
231
|
+
}
|
|
232
|
+
case 'ArrowUp': {
|
|
233
|
+
e.preventDefault();
|
|
234
|
+
highlightIndex = highlightIndex <= 0
|
|
235
|
+
? totalItems - 1
|
|
236
|
+
: highlightIndex - 1;
|
|
237
|
+
updateHighlight();
|
|
238
|
+
break;
|
|
239
|
+
}
|
|
240
|
+
case 'Enter': {
|
|
241
|
+
e.preventDefault();
|
|
242
|
+
if (highlightIndex === 0) {
|
|
243
|
+
selectDefault();
|
|
244
|
+
}
|
|
245
|
+
else if (highlightIndex > 0 && highlightIndex <= families.length) {
|
|
246
|
+
selectFamily(families[highlightIndex - 1].value);
|
|
247
|
+
}
|
|
248
|
+
break;
|
|
249
|
+
}
|
|
250
|
+
case 'Escape': {
|
|
251
|
+
e.preventDefault();
|
|
252
|
+
closeMenu();
|
|
253
|
+
break;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
function updateHighlight() {
|
|
258
|
+
defaultOption.classList.toggle('rtif-font-family-picker-option-highlighted', highlightIndex === 0);
|
|
259
|
+
for (let i = 0; i < optionEls.length; i++) {
|
|
260
|
+
optionEls[i].classList.toggle('rtif-font-family-picker-option-highlighted', highlightIndex === i + 1);
|
|
261
|
+
}
|
|
262
|
+
if (highlightIndex >= 0 && highlightIndex < totalItems) {
|
|
263
|
+
trigger.setAttribute('aria-activedescendant', getOptionId(highlightIndex));
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
trigger.removeAttribute('aria-activedescendant');
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
// -----------------------------------------------------------------------
|
|
270
|
+
// Event handlers
|
|
271
|
+
// -----------------------------------------------------------------------
|
|
272
|
+
function onTriggerMouseDown(e) {
|
|
273
|
+
e.preventDefault();
|
|
274
|
+
}
|
|
275
|
+
function onTriggerClick() {
|
|
276
|
+
if (isOpen) {
|
|
277
|
+
closeMenu();
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
openMenu();
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
function onDocumentMouseDown(e) {
|
|
284
|
+
if (!wrapper.contains(e.target)) {
|
|
285
|
+
closeMenu();
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
trigger.addEventListener('mousedown', onTriggerMouseDown);
|
|
289
|
+
trigger.addEventListener('click', onTriggerClick);
|
|
290
|
+
trigger.addEventListener('keydown', onKeyDown);
|
|
291
|
+
// -----------------------------------------------------------------------
|
|
292
|
+
// Bus subscription
|
|
293
|
+
// -----------------------------------------------------------------------
|
|
294
|
+
updateDisplay();
|
|
295
|
+
const unsubscribe = bus.subscribe(() => updateDisplay());
|
|
296
|
+
// -----------------------------------------------------------------------
|
|
297
|
+
// Handle
|
|
298
|
+
// -----------------------------------------------------------------------
|
|
299
|
+
return {
|
|
300
|
+
element: wrapper,
|
|
301
|
+
destroy() {
|
|
302
|
+
if (destroyed)
|
|
303
|
+
return;
|
|
304
|
+
destroyed = true;
|
|
305
|
+
unsubscribe();
|
|
306
|
+
closeMenu();
|
|
307
|
+
trigger.removeEventListener('mousedown', onTriggerMouseDown);
|
|
308
|
+
trigger.removeEventListener('click', onTriggerClick);
|
|
309
|
+
trigger.removeEventListener('keydown', onKeyDown);
|
|
310
|
+
wrapper.remove();
|
|
311
|
+
},
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
//# sourceMappingURL=font-family-picker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"font-family-picker.js","sourceRoot":"","sources":["../src/font-family-picker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAElE,8EAA8E;AAC9E,uCAAuC;AACvC,8EAA8E;AAE9E,IAAI,eAAe,GAAG,CAAC,CAAC;AA2BxB;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAgC;IAChE,aAAa;IACb,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,mBAAmB,EAAE;IAC9D,EAAE,KAAK,EAAE,gBAAgB,EAAE,KAAK,EAAE,gBAAgB,EAAE,KAAK,EAAE,yCAAyC,EAAE;IACtG,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,qBAAqB,EAAE;IACpE,EAAE,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,4BAA4B,EAAE;IACrF,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,oBAAoB,EAAE;IACjE,QAAQ;IACR,EAAE,KAAK,EAAE,iBAAiB,EAAE,KAAK,EAAE,iBAAiB,EAAE,KAAK,EAAE,iCAAiC,EAAE;IAChG,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,gBAAgB,EAAE;IAC/D,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,iBAAiB,EAAE;IAClE,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,sCAAsC,EAAE;IACvF,YAAY;IACZ,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,mCAAmC,EAAE;IAC1F,EAAE,KAAK,EAAE,gBAAgB,EAAE,KAAK,EAAE,gBAAgB,EAAE,KAAK,EAAE,qCAAqC,EAAE;IAClG,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,qBAAqB,EAAE;IACtE,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,mBAAmB,EAAE;CACjE,CAAC;AA8CF,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,sBAAsB,CAAC,MAA8B;IACnE,MAAM,EACJ,SAAS,EACT,GAAG,EACH,MAAM,EACN,QAAQ,GAAG,qBAAqB,GACjC,GAAG,MAAM,CAAC;IAEX,MAAM,GAAG,GAAG,SAAS,CAAC,aAAa,CAAC;IACpC,MAAM,EAAE,GAAG,EAAE,eAAe,CAAC;IAC7B,MAAM,MAAM,GAAG,yBAAyB,EAAE,EAAE,CAAC;IAE7C,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,IAAI,cAAc,GAAG,CAAC,CAAC,CAAC;IACxB,IAAI,SAAS,GAAG,KAAK,CAAC;IAEtB,qDAAqD;IACrD,MAAM,UAAU,GAAG,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC;IAEvC,0EAA0E;IAC1E,eAAe;IACf,0EAA0E;IAE1E,MAAM,OAAO,GAAG,GAAG,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;IACzC,OAAO,CAAC,SAAS,GAAG,yBAAyB,CAAC;IAE9C,iBAAiB;IACjB,MAAM,OAAO,GAAG,GAAG,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;IAC5C,OAAO,CAAC,IAAI,GAAG,QAAQ,CAAC;IACxB,OAAO,CAAC,SAAS,GAAG,iCAAiC,CAAC;IACtD,OAAO,CAAC,YAAY,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACzC,OAAO,CAAC,YAAY,CAAC,eAAe,EAAE,SAAS,CAAC,CAAC;IACjD,OAAO,CAAC,YAAY,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC;IAC/C,OAAO,CAAC,YAAY,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;IAC9C,OAAO,CAAC,YAAY,CAAC,YAAY,EAAE,aAAa,CAAC,CAAC;IAElD,MAAM,SAAS,GAAG,GAAG,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;IAC5C,SAAS,CAAC,SAAS,GAAG,+BAA+B,CAAC;IAEtD,MAAM,OAAO,GAAG,GAAG,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;IAC1C,OAAO,CAAC,SAAS,GAAG,iCAAiC,CAAC;IACtD,OAAO,CAAC,WAAW,GAAG,QAAQ,CAAC,CAAC,IAAI;IAEpC,OAAO,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;IAC/B,OAAO,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IAE7B,OAAO;IACP,MAAM,IAAI,GAAG,GAAG,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;IACtC,IAAI,CAAC,EAAE,GAAG,MAAM,CAAC;IACjB,IAAI,CAAC,SAAS,GAAG,8BAA8B,CAAC;IAChD,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACrC,IAAI,CAAC,YAAY,CAAC,YAAY,EAAE,eAAe,CAAC,CAAC;IACjD,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;IAEnB,yCAAyC;IACzC,MAAM,aAAa,GAAG,GAAG,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;IAC/C,aAAa,CAAC,SAAS,GAAG,gEAAgE,CAAC;IAC3F,aAAa,CAAC,EAAE,GAAG,wBAAwB,EAAE,UAAU,CAAC;IACxD,aAAa,CAAC,YAAY,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC7C,aAAa,CAAC,YAAY,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC;IACrD,aAAa,CAAC,WAAW,GAAG,SAAS,CAAC;IACtC,aAAa,CAAC,gBAAgB,CAAC,WAAW,EAAE,CAAC,CAAa,EAAE,EAAE;QAC5D,CAAC,CAAC,cAAc,EAAE,CAAC;QACnB,aAAa,EAAE,CAAC;IAClB,CAAC,CAAC,CAAC;IACH,IAAI,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC;IAEhC,YAAY;IACZ,MAAM,GAAG,GAAG,GAAG,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;IACrC,GAAG,CAAC,SAAS,GAAG,mCAAmC,CAAC;IACpD,GAAG,CAAC,YAAY,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACtC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IAEtB,iBAAiB;IACjB,MAAM,SAAS,GAAkB,EAAE,CAAC;IACpC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACzC,MAAM,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAE,CAAC;QAC5B,MAAM,MAAM,GAAG,GAAG,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QACxC,MAAM,CAAC,SAAS,GAAG,gCAAgC,CAAC;QACpD,MAAM,CAAC,EAAE,GAAG,wBAAwB,EAAE,IAAI,CAAC,EAAE,CAAC;QAC9C,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QACtC,MAAM,CAAC,YAAY,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC;QAC9C,MAAM,CAAC,YAAY,CAAC,aAAa,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;QACjD,MAAM,CAAC,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC;QAClC,MAAM,CAAC,KAAK,CAAC,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC;QAEvC,MAAM,CAAC,gBAAgB,CAAC,WAAW,EAAE,CAAC,CAAa,EAAE,EAAE;YACrD,CAAC,CAAC,cAAc,EAAE,CAAC;YACnB,YAAY,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC7B,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QACzB,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACzB,CAAC;IAED,OAAO,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IAC7B,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IAC1B,SAAS,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IAE/B,0EAA0E;IAC1E,yBAAyB;IACzB,0EAA0E;IAE1E,SAAS,gBAAgB,CAAC,KAAa;QACrC,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,CAAC;IACjD,CAAC;IAED,SAAS,aAAa;QACpB,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,uBAAuB,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;QAEzE,IAAI,OAAO,EAAE,CAAC;YACZ,SAAS,CAAC,WAAW,GAAG,QAAQ,CAAC,CAAC,UAAU;YAC5C,SAAS,CAAC,KAAK,CAAC,UAAU,GAAG,EAAE,CAAC;QAClC,CAAC;aAAM,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC;YACzB,MAAM,SAAS,GAAG,KAAe,CAAC;YAClC,MAAM,OAAO,GAAG,gBAAgB,CAAC,SAAS,CAAC,CAAC;YAC5C,SAAS,CAAC,WAAW,GAAG,OAAO,EAAE,KAAK,IAAI,SAAS,CAAC;YACpD,SAAS,CAAC,KAAK,CAAC,UAAU,GAAG,OAAO,EAAE,KAAK,IAAI,SAAS,CAAC;QAC3D,CAAC;aAAM,CAAC;YACN,SAAS,CAAC,WAAW,GAAG,SAAS,CAAC;YAClC,SAAS,CAAC,KAAK,CAAC,UAAU,GAAG,EAAE,CAAC;QAClC,CAAC;QAED,0BAA0B;QAC1B,aAAa,CAAC,YAAY,CACxB,eAAe,EACf,KAAK,IAAI,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAC7C,CAAC;QACF,KAAK,MAAM,GAAG,IAAI,SAAS,EAAE,CAAC;YAC5B,MAAM,SAAS,GAAG,GAAG,CAAC,YAAY,CAAC,aAAa,CAAC,CAAC;YAClD,GAAG,CAAC,YAAY,CACd,eAAe,EACf,CAAC,OAAO,IAAI,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CACnD,CAAC;QACJ,CAAC;IACH,CAAC;IAED,0EAA0E;IAC1E,eAAe;IACf,0EAA0E;IAE1E,SAAS,QAAQ;QACf,IAAI,MAAM;YAAE,OAAO;QACnB,MAAM,GAAG,IAAI,CAAC;QACd,cAAc,GAAG,CAAC,CAAC,CAAC;QACpB,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QACpB,OAAO,CAAC,YAAY,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;QAC9C,OAAO,CAAC,eAAe,CAAC,uBAAuB,CAAC,CAAC;QAEjD,GAAG,CAAC,gBAAgB,CAAC,WAAW,EAAE,mBAAmB,CAAC,CAAC;IACzD,CAAC;IAED,SAAS,SAAS;QAChB,IAAI,CAAC,MAAM;YAAE,OAAO;QACpB,MAAM,GAAG,KAAK,CAAC;QACf,cAAc,GAAG,CAAC,CAAC,CAAC;QACpB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACnB,OAAO,CAAC,YAAY,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC;QAC/C,OAAO,CAAC,eAAe,CAAC,uBAAuB,CAAC,CAAC;QAEjD,kBAAkB;QAClB,aAAa,CAAC,SAAS,CAAC,MAAM,CAAC,4CAA4C,CAAC,CAAC;QAC7E,KAAK,MAAM,EAAE,IAAI,SAAS,EAAE,CAAC;YAC3B,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,4CAA4C,CAAC,CAAC;QACpE,CAAC;QAED,GAAG,CAAC,mBAAmB,CAAC,WAAW,EAAE,mBAAmB,CAAC,CAAC;IAC5D,CAAC;IAED,0EAA0E;IAC1E,YAAY;IACZ,0EAA0E;IAE1E,SAAS,aAAa;QACpB,GAAG,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC;QAChC,SAAS,EAAE,CAAC;IACd,CAAC;IAED,SAAS,YAAY,CAAC,MAAc;QAClC,GAAG,CAAC,OAAO,CAAC,eAAe,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;QACzC,SAAS,EAAE,CAAC;IACd,CAAC;IAED,0EAA0E;IAC1E,sBAAsB;IACtB,0EAA0E;IAE1E,SAAS,WAAW,CAAC,KAAa;QAChC,IAAI,KAAK,KAAK,CAAC;YAAE,OAAO,aAAa,CAAC,EAAE,CAAC;QACzC,OAAO,SAAS,CAAC,KAAK,GAAG,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC;IACxC,CAAC;IAED,SAAS,SAAS,CAAC,CAAgB;QACjC,IAAI,CAAC,MAAM;YAAE,OAAO;QAEpB,QAAQ,CAAC,CAAC,GAAG,EAAE,CAAC;YACd,KAAK,WAAW,CAAC,CAAC,CAAC;gBACjB,CAAC,CAAC,cAAc,EAAE,CAAC;gBACnB,cAAc,GAAG,CAAC,cAAc,GAAG,CAAC,CAAC,GAAG,UAAU,CAAC;gBACnD,eAAe,EAAE,CAAC;gBAClB,MAAM;YACR,CAAC;YACD,KAAK,SAAS,CAAC,CAAC,CAAC;gBACf,CAAC,CAAC,cAAc,EAAE,CAAC;gBACnB,cAAc,GAAG,cAAc,IAAI,CAAC;oBAClC,CAAC,CAAC,UAAU,GAAG,CAAC;oBAChB,CAAC,CAAC,cAAc,GAAG,CAAC,CAAC;gBACvB,eAAe,EAAE,CAAC;gBAClB,MAAM;YACR,CAAC;YACD,KAAK,OAAO,CAAC,CAAC,CAAC;gBACb,CAAC,CAAC,cAAc,EAAE,CAAC;gBACnB,IAAI,cAAc,KAAK,CAAC,EAAE,CAAC;oBACzB,aAAa,EAAE,CAAC;gBAClB,CAAC;qBAAM,IAAI,cAAc,GAAG,CAAC,IAAI,cAAc,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;oBACnE,YAAY,CAAC,QAAQ,CAAC,cAAc,GAAG,CAAC,CAAE,CAAC,KAAK,CAAC,CAAC;gBACpD,CAAC;gBACD,MAAM;YACR,CAAC;YACD,KAAK,QAAQ,CAAC,CAAC,CAAC;gBACd,CAAC,CAAC,cAAc,EAAE,CAAC;gBACnB,SAAS,EAAE,CAAC;gBACZ,MAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC;IAED,SAAS,eAAe;QACtB,aAAa,CAAC,SAAS,CAAC,MAAM,CAC5B,4CAA4C,EAC5C,cAAc,KAAK,CAAC,CACrB,CAAC;QACF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAC1C,SAAS,CAAC,CAAC,CAAE,CAAC,SAAS,CAAC,MAAM,CAC5B,4CAA4C,EAC5C,cAAc,KAAK,CAAC,GAAG,CAAC,CACzB,CAAC;QACJ,CAAC;QAED,IAAI,cAAc,IAAI,CAAC,IAAI,cAAc,GAAG,UAAU,EAAE,CAAC;YACvD,OAAO,CAAC,YAAY,CAAC,uBAAuB,EAAE,WAAW,CAAC,cAAc,CAAC,CAAC,CAAC;QAC7E,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,eAAe,CAAC,uBAAuB,CAAC,CAAC;QACnD,CAAC;IACH,CAAC;IAED,0EAA0E;IAC1E,iBAAiB;IACjB,0EAA0E;IAE1E,SAAS,kBAAkB,CAAC,CAAa;QACvC,CAAC,CAAC,cAAc,EAAE,CAAC;IACrB,CAAC;IAED,SAAS,cAAc;QACrB,IAAI,MAAM,EAAE,CAAC;YACX,SAAS,EAAE,CAAC;QACd,CAAC;aAAM,CAAC;YACN,QAAQ,EAAE,CAAC;QACb,CAAC;IACH,CAAC;IAED,SAAS,mBAAmB,CAAC,CAAa;QACxC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAc,CAAC,EAAE,CAAC;YACxC,SAAS,EAAE,CAAC;QACd,CAAC;IACH,CAAC;IAED,OAAO,CAAC,gBAAgB,CAAC,WAAW,EAAE,kBAAkB,CAAC,CAAC;IAC1D,OAAO,CAAC,gBAAgB,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;IAClD,OAAO,CAAC,gBAAgB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IAE/C,0EAA0E;IAC1E,mBAAmB;IACnB,0EAA0E;IAE1E,aAAa,EAAE,CAAC;IAChB,MAAM,WAAW,GAAG,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC,aAAa,EAAE,CAAC,CAAC;IAEzD,0EAA0E;IAC1E,SAAS;IACT,0EAA0E;IAE1E,OAAO;QACL,OAAO,EAAE,OAAO;QAChB,OAAO;YACL,IAAI,SAAS;gBAAE,OAAO;YACtB,SAAS,GAAG,IAAI,CAAC;YACjB,WAAW,EAAE,CAAC;YACd,SAAS,EAAE,CAAC;YACZ,OAAO,CAAC,mBAAmB,CAAC,WAAW,EAAE,kBAAkB,CAAC,CAAC;YAC7D,OAAO,CAAC,mBAAmB,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;YACrD,OAAO,CAAC,mBAAmB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;YAClD,OAAO,CAAC,MAAM,EAAE,CAAC;QACnB,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Font size picker — a dropdown for selecting text size.
|
|
3
|
+
*
|
|
4
|
+
* Shows the current font size in the trigger button and lets the user
|
|
5
|
+
* choose from a list of preset sizes. Uses the same
|
|
6
|
+
* `mousedown + preventDefault()` pattern as the toolbar to avoid stealing
|
|
7
|
+
* editor focus.
|
|
8
|
+
*
|
|
9
|
+
* @module
|
|
10
|
+
*/
|
|
11
|
+
import type { IEditorEngine } from '@rtif-sdk/engine';
|
|
12
|
+
import type { CommandBus } from './command-bus.js';
|
|
13
|
+
/**
|
|
14
|
+
* Default font sizes displayed in the picker dropdown.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* const picker = createFontSizePicker({
|
|
19
|
+
* container, bus, engine,
|
|
20
|
+
* sizes: DEFAULT_FONT_SIZES.filter(s => s >= 12 && s <= 36),
|
|
21
|
+
* });
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export declare const DEFAULT_FONT_SIZES: readonly number[];
|
|
25
|
+
/**
|
|
26
|
+
* Configuration for the font size picker.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```ts
|
|
30
|
+
* const picker = createFontSizePicker({
|
|
31
|
+
* container: document.getElementById('toolbar')!,
|
|
32
|
+
* bus: editor.commandBus,
|
|
33
|
+
* engine,
|
|
34
|
+
* });
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export interface FontSizePickerConfig {
|
|
38
|
+
/** The DOM element to render the picker into. */
|
|
39
|
+
readonly container: HTMLElement;
|
|
40
|
+
/** The command bus for executing font size commands. */
|
|
41
|
+
readonly bus: CommandBus;
|
|
42
|
+
/** The engine for querying the current mark value. */
|
|
43
|
+
readonly engine: IEditorEngine;
|
|
44
|
+
/** Font sizes to display (default: {@link DEFAULT_FONT_SIZES}). */
|
|
45
|
+
readonly sizes?: readonly number[];
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Handle returned by {@link createFontSizePicker} for lifecycle management.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```ts
|
|
52
|
+
* const picker = createFontSizePicker({ container, bus, engine });
|
|
53
|
+
* // ... later:
|
|
54
|
+
* picker.destroy();
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export interface FontSizePickerHandle {
|
|
58
|
+
/** The picker's root wrapper element. */
|
|
59
|
+
readonly element: HTMLElement;
|
|
60
|
+
/** Remove all event listeners and DOM nodes. Idempotent. */
|
|
61
|
+
destroy(): void;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Create a font size picker dropdown backed by a {@link CommandBus}.
|
|
65
|
+
*
|
|
66
|
+
* The picker reflects the current font size by querying the engine's
|
|
67
|
+
* mark state. It subscribes to the bus for live updates.
|
|
68
|
+
*
|
|
69
|
+
* @param config - Picker configuration
|
|
70
|
+
* @returns A handle for lifecycle management
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```ts
|
|
74
|
+
* const picker = createFontSizePicker({
|
|
75
|
+
* container: toolbar,
|
|
76
|
+
* bus: editor.commandBus,
|
|
77
|
+
* engine,
|
|
78
|
+
* });
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
export declare function createFontSizePicker(config: FontSizePickerConfig): FontSizePickerHandle;
|
|
82
|
+
//# sourceMappingURL=font-size-picker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"font-size-picker.d.ts","sourceRoot":"","sources":["../src/font-size-picker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAanD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,kBAAkB,EAAE,SAAS,MAAM,EAE/C,CAAC;AAMF;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,oBAAoB;IACnC,iDAAiD;IACjD,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAC;IAChC,wDAAwD;IACxD,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC;IACzB,sDAAsD;IACtD,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,mEAAmE;IACnE,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,oBAAoB;IACnC,yCAAyC;IACzC,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAC9B,4DAA4D;IAC5D,OAAO,IAAI,IAAI,CAAC;CACjB;AAMD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,oBAAoB,GAAG,oBAAoB,CA8RvF"}
|