svedit 0.3.0 → 0.4.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.
@@ -10,6 +10,8 @@
10
10
  editable = $bindable(false),
11
11
  path,
12
12
  class: css_class,
13
+ autocapitalize = "on",
14
+ spellcheck = "true",
13
15
  } = $props();
14
16
 
15
17
  let canvas_ref;
@@ -27,13 +29,26 @@
27
29
  // Detect Chrome on desktop (not mobile) - only available in browser
28
30
  let is_chrome_desktop = $state(false);
29
31
 
32
+ /**
33
+ * Detect if the current browser is on a mobile device
34
+ * @returns {boolean} true if mobile browser, false otherwise
35
+ */
36
+ function is_mobile_browser() {
37
+ if (typeof window === 'undefined' || typeof navigator === 'undefined') {
38
+ return false;
39
+ }
40
+
41
+ const user_agent = navigator.userAgent;
42
+ return /iPhone|iPad|iPod|Android|Mobile/i.test(user_agent) ||
43
+ ('ontouchstart' in window) ||
44
+ (navigator.maxTouchPoints > 0);
45
+ }
46
+
30
47
  $effect(() => {
31
48
  if (typeof window !== 'undefined' && typeof navigator !== 'undefined') {
32
49
  const user_agent = navigator.userAgent;
33
50
  const is_chrome = user_agent.includes('Chrome') && !user_agent.includes('Edg');
34
- const is_mobile = /iPhone|iPad|iPod|Android|Mobile/i.test(user_agent) ||
35
- ('ontouchstart' in window) ||
36
- (navigator.maxTouchPoints > 0);
51
+ const is_mobile = is_mobile_browser();
37
52
  is_chrome_desktop = is_chrome && !is_mobile;
38
53
  }
39
54
  });
@@ -86,7 +101,7 @@
86
101
 
87
102
  function commit_input() {
88
103
  if (input_selection?.type !== 'text') return;
89
- const model_text = doc.get(input_selection.path)[0];
104
+ const model_text = doc.get(input_selection.path).text;
90
105
  const dom_text_element = document.querySelector(`[data-path="${input_selection.path.join('.')}"]`);
91
106
  const dom_text = dom_text_element.textContent;
92
107
  const op = diff_text(model_text, dom_text);
@@ -142,25 +157,98 @@
142
157
 
143
158
  // Only take input when in a valid text selection inside the canvas
144
159
  if (
145
- !(canvas_ref?.contains(document.activeElement)) ||
146
- (doc.selection?.type !== 'text')
160
+ !(canvas_ref?.contains(document.activeElement))
147
161
  ) {
148
162
  event.preventDefault();
149
163
  return;
150
164
  }
151
165
 
152
- if (event.inputType === 'formatBold') {
166
+ if (event.inputType === 'formatBold' && doc.selection?.type === 'text') {
153
167
  doc.apply(doc.tr.annotate_text('strong'));
154
168
  event.preventDefault();
155
169
  event.stopPropagation();
156
170
  }
157
171
 
158
- if (event.inputType === 'formatItalic') {
172
+ if (event.inputType === 'formatItalic' && doc.selection?.type === 'text') {
159
173
  doc.apply(doc.tr.annotate_text('emphasis'));
160
174
  event.preventDefault();
161
175
  event.stopPropagation();
162
176
  }
163
177
 
178
+ // NOTE: underline doesn't make much sense as a semantic annotation,
179
+ // so we rewire `cmd + u` to toggle highlights
180
+ if (event.inputType === 'formatUnderline' && doc.selection?.type === 'text') {
181
+ doc.apply(doc.tr.annotate_text('highlight'));
182
+ event.preventDefault();
183
+ event.stopPropagation();
184
+ }
185
+
186
+
187
+ if (event.inputType === 'deleteContentBackward') {
188
+ if (doc.selection?.type === 'property') {
189
+ // For property selections, clear the property value only if it's not already falsy
190
+ if (doc.get(doc.selection.path)) {
191
+ const tr = doc.tr;
192
+ tr.set(doc.selection.path, '');
193
+ doc.apply(tr);
194
+ }
195
+ } else {
196
+ if (doc.selection?.type === 'text' && doc.selection?.anchor_offset === 0 && doc.selection?.focus_offset === 0) {
197
+ const tr = doc.tr;
198
+ join_text_node(tr);
199
+ doc.apply(tr);
200
+ } else {
201
+ doc.apply(doc.tr.delete_selection());
202
+ }
203
+ }
204
+
205
+ event.preventDefault();
206
+ event.stopPropagation();
207
+ }
208
+
209
+ if (event.inputType === 'deleteContentForward') {
210
+ // Forward delete: only handle collapsed text selections
211
+ if (doc.selection?.type === 'text' && doc.selection?.anchor_offset === doc.selection?.focus_offset) {
212
+ const text_content = doc.get(doc.selection.path).text;
213
+ const text_length = get_char_length(text_content);
214
+
215
+ if (doc.selection.focus_offset < text_length) {
216
+ // There's a character after the cursor - select and delete it
217
+ const tr = doc.tr;
218
+ tr.set_selection({
219
+ type: 'text',
220
+ path: [...doc.selection.path],
221
+ anchor_offset: doc.selection.focus_offset,
222
+ focus_offset: doc.selection.focus_offset + 1
223
+ });
224
+ tr.delete_selection();
225
+ doc.apply(tr);
226
+ } else {
227
+ // At end of text - try to join with next text node
228
+ const tr = doc.tr;
229
+ const node_index = parseInt(String(doc.selection.path.at(-2)), 10);
230
+ const successor_node = doc.get([...doc.selection.path.slice(0, -2), node_index + 1]);
231
+
232
+ // Check if next node is a text node
233
+ if (successor_node && doc.kind(successor_node) === 'text') {
234
+ // Set selection to beginning of next text node
235
+ tr.set_selection({
236
+ type: 'text',
237
+ path: [...doc.selection.path.slice(0, -2), node_index + 1, 'content'],
238
+ anchor_offset: 0,
239
+ focus_offset: 0
240
+ });
241
+ // Use join_text_node to merge with previous
242
+ join_text_node(tr);
243
+ doc.apply(tr);
244
+ }
245
+ }
246
+ }
247
+
248
+ event.preventDefault();
249
+ event.stopPropagation();
250
+ }
251
+
164
252
 
165
253
  // We remember the "input selection", because in some mobile browsers (Samsung Android) the onbeforeinput event
166
254
  // is fired on a different selection than the input event.
@@ -302,9 +390,9 @@ ${fallback_html}`;
302
390
  for (const [prop_name, prop_value] of Object.entries(node)) {
303
391
  if (prop_name === 'id' || prop_name === 'type') continue;
304
392
 
305
- // Check if this is an annotated_string property (array with string as first element)
306
- if (Array.isArray(prop_value) && typeof prop_value[0] === 'string') {
307
- const text_content = prop_value[0];
393
+ // Check if this is an annotated_text property (object with text property)
394
+ if (typeof prop_value === 'object' && prop_value !== null && typeof prop_value.text === 'string') {
395
+ const text_content = prop_value.text;
308
396
  if (text_content.trim()) {
309
397
  html += `<p>${text_content}</p>\n`;
310
398
  has_content = true;
@@ -313,7 +401,7 @@ ${fallback_html}`;
313
401
  }
314
402
 
315
403
  if (!has_content) {
316
- // Generic fallback for unknown node types with no annotated_string content
404
+ // Generic fallback for unknown node types with no annotated_text content
317
405
  html += `<div data-node-type="${node.type}">Content from ${node.type}</div>\n`;
318
406
  }
319
407
 
@@ -389,7 +477,7 @@ ${fallback_html}`;
389
477
 
390
478
  console.log('Copy operation:', {
391
479
  selected_nodes,
392
- main_nodes,
480
+ nodes,
393
481
  total_nodes: Object.keys(nodes).length,
394
482
  operation: delete_selection ? 'cut' : 'copy'
395
483
  });
@@ -453,8 +541,6 @@ ${fallback_html}`;
453
541
  pasted_json = undefined;
454
542
  }
455
543
 
456
-
457
-
458
544
  if (pasted_json) {
459
545
  const tr = doc.tr;
460
546
 
@@ -478,12 +564,18 @@ ${fallback_html}`;
478
564
  for (const [property, value] of Object.entries(new_node)) {
479
565
  if (property === 'id' || property === 'type') continue;
480
566
 
481
- const prop_type = doc.schema[node.type]?.[property]?.type;
567
+ const prop_type = doc.schema[node.type]?.properties?.[property]?.type;
482
568
 
483
569
  if (prop_type === 'node_array' && Array.isArray(value)) {
484
570
  new_node[property] = value.map(ref_id => id_mapping[ref_id] || ref_id);
485
571
  } else if (prop_type === 'node' && typeof value === 'string') {
486
572
  new_node[property] = id_mapping[value] || value;
573
+ } else if (prop_type === 'annotated_text') {
574
+ const annotations = value.annotations.map(annotation => {
575
+ const {start_offset, end_offset, node_id} = annotation;
576
+ return {start_offset, end_offset, node_id: id_mapping[node_id] || node_id};
577
+ });
578
+ new_node[property] = {text: value.text, annotations};
487
579
  }
488
580
  }
489
581
 
@@ -588,37 +680,39 @@ ${fallback_html}`;
588
680
  const is_collapsed = selection?.anchor_offset === selection?.focus_offset;
589
681
 
590
682
  if (
591
- (e.key === 'ArrowRight' && e.altKey && e.ctrlKey && doc.selected_node) ||
592
- (e.key === 'ArrowRight' && e.altKey && e.ctrlKey && e.shiftKey && doc.selected_node)
683
+ (e.key === 'ArrowRight' && e.altKey && e.ctrlKey && doc.layout_node) ||
684
+ (e.key === 'ArrowRight' && e.altKey && e.ctrlKey && e.shiftKey && doc.layout_node)
593
685
  ) {
594
- const node = doc.selected_node;
686
+ const node = doc.layout_node;
595
687
  const layout_count = doc.config.node_layouts[node.type];
596
688
 
597
689
  if (layout_count > 1 && node?.layout) {
598
690
  const next_layout = (node.layout % layout_count) + 1;
599
691
  console.log('layout / count / next_layout', node.layout, layout_count, next_layout);
600
692
  const tr = doc.tr;
601
- tr.set([doc.selected_node?.id, 'layout'], next_layout);
693
+ tr.set([doc.layout_node?.id, 'layout'], next_layout);
602
694
  doc.apply(tr);
603
695
  }
696
+ e.preventDefault();
604
697
  } else if (
605
- (e.key === 'ArrowLeft' && e.altKey && e.ctrlKey && doc.selected_node) ||
606
- (e.key === 'ArrowLeft' && e.altKey && e.ctrlKey && e.shiftKey && doc.selected_node)
698
+ (e.key === 'ArrowLeft' && e.altKey && e.ctrlKey && doc.layout_node) ||
699
+ (e.key === 'ArrowLeft' && e.altKey && e.ctrlKey && e.shiftKey && doc.layout_node)
607
700
  ) {
608
- const node = doc.selected_node;
701
+ const node = doc.layout_node;
609
702
  const layout_count = doc.config.node_layouts[node.type];
610
703
  if (layout_count > 1 && node?.layout) {
611
704
  const prev_layout = ((node.layout - 2 + layout_count) % layout_count) + 1;
612
705
  const tr = doc.tr;
613
- tr.set([doc.selected_node?.id, 'layout'], prev_layout);
706
+ tr.set([doc.layout_node?.id, 'layout'], prev_layout);
614
707
  doc.apply(tr);
615
708
  console.log('layout / count / prev_layout', node.layout, layout_count, prev_layout);
616
709
  }
710
+ e.preventDefault();
617
711
  } else if (
618
- (e.key === 'ArrowDown' && e.altKey && e.ctrlKey && doc.selected_node) ||
619
- (e.key === 'ArrowDown' && e.altKey && e.ctrlKey && e.shiftKey && doc.selected_node )
712
+ (e.key === 'ArrowDown' && e.altKey && e.ctrlKey && doc.layout_node) ||
713
+ (e.key === 'ArrowDown' && e.altKey && e.ctrlKey && e.shiftKey && doc.layout_node )
620
714
  ) {
621
- const node = doc.selected_node;
715
+ const node = doc.layout_node;
622
716
 
623
717
  if (doc.selection.type !== 'node') {
624
718
  doc.select_parent();
@@ -636,11 +730,12 @@ ${fallback_html}`;
636
730
  doc.config.inserters[next_type](tr);
637
731
  tr.set_selection(old_selection);
638
732
  doc.apply(tr);
733
+ e.preventDefault();
639
734
  } else if (
640
- (e.key === 'ArrowUp' && e.altKey && e.ctrlKey && doc.selected_node) ||
641
- (e.key === 'ArrowUp' && e.altKey && e.ctrlKey && e.shiftKey && doc.selected_node)
735
+ (e.key === 'ArrowUp' && e.altKey && e.ctrlKey && doc.layout_node) ||
736
+ (e.key === 'ArrowUp' && e.altKey && e.ctrlKey && e.shiftKey && doc.layout_node)
642
737
  ) {
643
- const node = doc.selected_node;
738
+ const node = doc.layout_node;
644
739
 
645
740
  if (doc.selection.type !== 'node') {
646
741
  doc.select_parent();
@@ -660,6 +755,7 @@ ${fallback_html}`;
660
755
  doc.config.inserters[prev_type](tr);
661
756
  tr.set_selection(old_selection);
662
757
  doc.apply(tr);
758
+ e.preventDefault();
663
759
 
664
760
  } else if (e.key === 'a' && (e.metaKey || e.ctrlKey)) {
665
761
  const tr = doc.tr;
@@ -676,76 +772,32 @@ ${fallback_html}`;
676
772
  doc.redo();
677
773
  e.preventDefault();
678
774
  e.stopPropagation();
679
- } else if (e.key === 'Enter' && (e.shiftKey)) {
775
+ } else if (e.key === 'Enter' && (e.shiftKey) && !is_mobile_browser() && selection?.type === 'text' && doc.inspect(selection.path).allow_newlines) {
680
776
  doc.apply(doc.tr.insert_text('\n'));
681
777
  e.preventDefault();
682
778
  e.stopPropagation();
683
- } else if (e.key === 'k' && (e.ctrlKey || e.metaKey)) {
684
- doc.apply(doc.tr.annotate_text('link', {
685
- href: window.prompt('Enter the URL', 'https://example.com')
686
- }));
779
+ } else if (e.key === 'b' && (e.ctrlKey || e.metaKey) && selection?.type === 'text') {
780
+ doc.apply(doc.tr.annotate_text('strong'));
687
781
  e.preventDefault();
688
782
  e.stopPropagation();
689
- } else if (e.key === 'Backspace') {
690
- if (selection?.type === 'property') {
691
- // For property selections, clear the property value only if it's not already falsy
692
- if (doc.get(selection.path)) {
693
- const tr = doc.tr;
694
- tr.set(selection.path, '');
695
- doc.apply(tr);
696
- }
697
- } else {
698
- if (doc.selection?.type === 'text' && doc.selection?.anchor_offset === 0 && doc.selection?.focus_offset === 0) {
699
- const tr = doc.tr;
700
- join_text_node(tr);
701
- doc.apply(tr);
702
- } else {
703
- // TODO: Things are not yet properly composable, I think we need to
704
- // realize delete_selection as a command as well.
705
- // See https://prosemirror.net/docs/guide/#commands for inspiration.
706
- // For other selections, use the normal delete behavior
707
- doc.apply(doc.tr.delete_selection());
708
- }
709
- }
710
-
783
+ } else if (e.key === 'i' && (e.ctrlKey || e.metaKey) && selection?.type === 'text') {
784
+ doc.apply(doc.tr.annotate_text('emphasis'));
711
785
  e.preventDefault();
712
786
  e.stopPropagation();
713
- } else if (e.key === 'Delete') {
714
- // Forward delete: only handle collapsed text selections
715
- if (selection?.type === 'text' && is_collapsed) {
716
- const text_content = doc.get(selection.path)[0];
717
- const text_length = get_char_length(text_content);
718
-
719
- if (selection.focus_offset < text_length) {
720
- // There's a character after the cursor - select and delete it
721
- const tr = doc.tr;
722
- tr.set_selection({
723
- type: 'text',
724
- path: [...selection.path],
725
- anchor_offset: selection.focus_offset,
726
- focus_offset: selection.focus_offset + 1
727
- });
728
- tr.delete_selection();
729
- doc.apply(tr);
730
- } else {
731
- // At end of text - try to join with next text node
732
- const tr = doc.tr;
733
- const node_index = parseInt(selection.path.at(-2), 10);
734
- const successor_node = doc.get([...selection.path.slice(0, -2), node_index + 1]);
735
-
736
- // Check if next node is a text node
737
- if (successor_node && doc.kind(successor_node) === 'text') {
738
- // Set selection to beginning of next text node
739
- tr.set_selection({
740
- type: 'text',
741
- path: [...selection.path.slice(0, -2), node_index + 1, 'content'],
742
- anchor_offset: 0,
743
- focus_offset: 0
744
- });
745
- // Use join_text_node to merge with previous
746
- join_text_node(tr);
747
- doc.apply(tr);
748
- }
787
+ } else if (e.key === 'u' && (e.ctrlKey || e.metaKey) && selection?.type === 'text') {
788
+ doc.apply(doc.tr.annotate_text('highlight'));
789
+ e.preventDefault();
790
+ e.stopPropagation();
791
+ } else if (e.key === 'k' && (e.ctrlKey || e.metaKey)) {
792
+ const has_link = doc.active_annotation('link');
793
+ if (has_link) {
794
+ // Delete link
795
+ doc.apply(doc.tr.annotate_text('link'));
796
+ } else {
797
+ // Create link
798
+ const href = window.prompt('Enter the URL', 'https://example.com');
799
+ if (href) {
800
+ doc.apply(doc.tr.annotate_text('link', { href }));
749
801
  }
750
802
  }
751
803
 
@@ -1023,7 +1075,7 @@ ${fallback_html}`;
1023
1075
  // 2. Last child node is a <br> (the visual representation of that \n)
1024
1076
  // 3. Cursor is positioned at or near the end
1025
1077
  // And returns the correct text.length position instead.
1026
- const text_content = doc.get(path)[0];
1078
+ const text_content = doc.get(path).text;
1027
1079
  const text_length = get_char_length(text_content);
1028
1080
  if (text_length > 0) {
1029
1081
  const last_char = get_char_at(text_content, text_length - 1);
@@ -1208,7 +1260,7 @@ ${fallback_html}`;
1208
1260
  const selection = /** @type {any} */ (doc.selection);
1209
1261
  // The element that holds the annotated string
1210
1262
  const el = canvas_ref.querySelector(`[data-path="${selection.path.join('.')}"][data-type="text"]`);
1211
- const empty_text = doc.get(selection.path)[0].length === 0;
1263
+ const empty_text = doc.get(selection.path).text.length === 0;
1212
1264
 
1213
1265
  const range = window.document.createRange();
1214
1266
  const dom_selection = window.getSelection();
@@ -1385,8 +1437,8 @@ ${fallback_html}`;
1385
1437
  {oncompositionstart}
1386
1438
  {oncompositionend}
1387
1439
  contenteditable={editable ? 'true' : 'false'}
1388
- autocapitalize="off"
1389
- spellcheck="true"
1440
+ {autocapitalize}
1441
+ {spellcheck}
1390
1442
  {...{
1391
1443
  // NOTE: Autocomplete and autocorrect make immense troubles
1392
1444
  // on Desktop Chrome, so we disable them only for Chrome desktop.
@@ -1 +1 @@
1
- {"version":3,"file":"Svedit.svelte.d.ts","sourceRoot":"","sources":["../src/lib/Svedit.svelte.js"],"names":[],"mappings":";;;;;;;AAg3CA;;eAAqD;iCAp2CgE,cAAc"}
1
+ {"version":3,"file":"Svedit.svelte.d.ts","sourceRoot":"","sources":["../src/lib/Svedit.svelte.js"],"names":[],"mappings":";;;;;;;AAo6CA;;eAAqD;iCAx5CgE,cAAc"}
@@ -51,7 +51,7 @@ export default class Transaction {
51
51
  * @example
52
52
  * ```js
53
53
  * tr.set(["list_1", "list_items"], [1, 2, 3]);
54
- * tr.set(["page_1", "body", "0", "description"], ["Hello world", []]);
54
+ * tr.set(["page_1", "body", "0", "description"], {text: "Hello world", annotations: []});
55
55
  * ```
56
56
  */
57
57
  set(path: DocumentPath, value: any): void;
@@ -94,7 +94,7 @@ export default class Transaction {
94
94
  *
95
95
  * @param {Selection} selection - The new selection object
96
96
  * @returns {Transaction} This transaction instance for method chaining
97
- * @todo Check if selection is valid and throw error if not
97
+ * @throws {Error} Throws if the selection is invalid or out of bounds
98
98
  */
99
99
  set_selection(selection: Selection): Transaction;
100
100
  /**
@@ -105,7 +105,7 @@ export default class Transaction {
105
105
  * when conflicting types are applied.
106
106
  *
107
107
  * @param {any} annotation_type - The type of annotation (e.g., 'link', 'bold', 'italic')
108
- * @param {any} annotation_data - Additional data for the annotation (e.g., href for links)
108
+ * @param {any} annotation_properties - Additional data for the annotation (e.g., href for links)
109
109
  * @returns {Transaction} This transaction instance for method chaining
110
110
  *
111
111
  * @example
@@ -117,7 +117,7 @@ export default class Transaction {
117
117
  * tr.annotate_text('emphasis', {});
118
118
  * ```
119
119
  */
120
- annotate_text(annotation_type: any, annotation_data: any): Transaction;
120
+ annotate_text(annotation_type: any, annotation_properties: any): Transaction;
121
121
  /**
122
122
  * Deletes the currently selected text or nodes.
123
123
  *
@@ -1 +1 @@
1
- {"version":3,"file":"Transaction.svelte.d.ts","sourceRoot":"","sources":["../src/lib/Transaction.svelte.js"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;GAcG;AACH;IACE;;;;OAIG;IACH,iBAFW,GAAG,EAWb;IARC,SAAc;IAEd,oCAAoC;IACpC,KADW,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CACnB;IACb,oCAAoC;IACpC,aADW,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CACX;IAErB,sBAA0C;IAG5C;;;;;OAKG;IACH,UAHW,YAAY,GACV,GAAG,CAIf;IAED;;;;OAIG;IACH,iBAFa,SAAS,CAIrB;IAED;;;;;;;;;;;;;;OAcG;IACH,UATW,YAAY,SACZ,GAAG,QA2Bb;IAED;;;;;;;;;;;;;;;;;;OAkBG;IACH,aAbW,GAAG,GACD,WAAW,CAyBvB;IAED;;;;;;;;;;;;OAYG;IACH,WARW,GAAG,GACD,WAAW,CAcvB;IAED;;;;;;OAMG;IACH,yBAJW,SAAS,GACP,WAAW,CAOvB;IAED;;;;;;;;;;;;;;;;;;;OAmBG;IACH,+BAbW,GAAG,mBACH,GAAG,GACD,WAAW,CAqFvB;IAED;;;;;;;;;OASG;IACH,oBAFa,WAAW,CA+FvB;IAED;;;;;;;;;;;;;;;;;;OAkBG;IACH,oBAbW,cAAc,EAAE,GACd,WAAW,CAqDvB;IAED;;;;;;;;;;;;;;;OAeG;IACH,2BAVW,MAAM,GACJ,WAAW,CA6EvB;IAED;;;;;;;;OAQG;IACH,2CA+BC;IAED;;;;;;;;;;OAUG;IACH,4CAqBC;CACF;kCAzkBmE,YAAY;+BAAZ,YAAY;oCAAZ,YAAY"}
1
+ {"version":3,"file":"Transaction.svelte.d.ts","sourceRoot":"","sources":["../src/lib/Transaction.svelte.js"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;GAcG;AACH;IACE;;;;OAIG;IACH,iBAFW,GAAG,EAWb;IARC,SAAc;IAEd,oCAAoC;IACpC,KADW,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CACnB;IACb,oCAAoC;IACpC,aADW,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CACX;IAErB,sBAA0C;IAG5C;;;;;OAKG;IACH,UAHW,YAAY,GACV,GAAG,CAIf;IAED;;;;OAIG;IACH,iBAFa,SAAS,CAIrB;IAED;;;;;;;;;;;;;;OAcG;IACH,UATW,YAAY,SACZ,GAAG,QA2Bb;IAED;;;;;;;;;;;;;;;;;;OAkBG;IACH,aAbW,GAAG,GACD,WAAW,CAyBvB;IAED;;;;;;;;;;;;OAYG;IACH,WARW,GAAG,GACD,WAAW,CAcvB;IAED;;;;;;OAMG;IACH,yBAJW,SAAS,GACP,WAAW,CAOvB;IAED;;;;;;;;;;;;;;;;;;;OAmBG;IACH,+BAbW,GAAG,yBACH,GAAG,GACD,WAAW,CAuDvB;IAED;;;;;;;;;OASG;IACH,oBAFa,WAAW,CAyGvB;IAED;;;;;;;;;;;;;;;;;;OAkBG;IACH,oBAbW,cAAc,EAAE,GACd,WAAW,CAqDvB;IAED;;;;;;;;;;;;;;;OAeG;IACH,2BAVW,MAAM,GACJ,WAAW,CA8EvB;IAED;;;;;;;;OAQG;IACH,2CA+BC;IAED;;;;;;;;;;OAUG;IACH,4CAuBC;CACF;kCAxjBmE,YAAY;+BAAZ,YAAY;oCAAZ,YAAY"}