@bobfrankston/rmfmail 1.0.677 → 1.0.679

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.
@@ -0,0 +1,403 @@
1
+ /**
2
+ * Live spell-check for the TinyMCE compose editor.
3
+ *
4
+ * Why this exists: WebView2's built-in spell checker isn't reliably
5
+ * enabled on our msger-hosted compose iframe (see msger main.rs around
6
+ * AreDefaultContextMenusEnabled — the comment claims it leaves
7
+ * defaults for spell-suggest menus, but `IsSpellcheckEnabled` is never
8
+ * explicitly set and the default varies by WebView2 runtime). Rather
9
+ * than depend on the host, we ship a JS spellchecker (`nspell`) + the
10
+ * standard `dictionary-en` Hunspell files.
11
+ *
12
+ * Two layers:
13
+ * 1. Live decoration — words that nspell flags get wrapped in a
14
+ * `<span data-mailx-spellerror="1">` while editing. CSS gives them
15
+ * a wavy red underline (text-decoration: underline wavy #d33). The
16
+ * markers are stripped at serialization time so they never reach
17
+ * the sent message or the saved draft.
18
+ * 2. Right-click — clicking on a marker shows our own popup with
19
+ * suggestions plus "Add to dictionary" / "Ignore (this session)".
20
+ *
21
+ * Decoration runs:
22
+ * - Once after the editor inits + dict loads (initial scan of pre-
23
+ * populated quote / signature, though we skip blockquote content).
24
+ * - Debounced after every input (~500 ms idle).
25
+ * - After setContent (paste, reply-quote insertion, …).
26
+ *
27
+ * The decoration walker:
28
+ * - Skips `<blockquote>` (the quoted reply — not the user's words),
29
+ * `<code>`, `<pre>`, `<a>` (URLs aren't natural-language words),
30
+ * and content inside any existing marker (so re-scans don't double-
31
+ * wrap).
32
+ * - Uses TinyMCE's `undoManager.ignore` + selection bookmarks so the
33
+ * decoration mutations don't pollute undo and don't move the caret.
34
+ *
35
+ * Dictionary persistence:
36
+ * - User additions: `localStorage["mailx-user-dict"]` (a JSON array).
37
+ * - "Ignore this session": in-memory only via `nspell.add()`.
38
+ */
39
+ // @ts-expect-error — nspell ships no type defs. Treated as `any`; the
40
+ // surface we use (`new NSpell({aff, dic})`, `.correct`, `.suggest`,
41
+ // `.add`) is small and stable.
42
+ import NSpell from "nspell";
43
+ const USER_DICT_KEY = "mailx-user-dict";
44
+ const MARKER_ATTR = "data-mailx-spellerror";
45
+ const DECORATE_DEBOUNCE_MS = 500;
46
+ const MIN_WORD_LEN = 3;
47
+ const SKIP_TAGS = new Set(["BLOCKQUOTE", "CODE", "PRE", "A", "SCRIPT", "STYLE", "KBD", "SAMP", "VAR"]);
48
+ let spellPromise = null;
49
+ async function getSpell() {
50
+ if (spellPromise)
51
+ return spellPromise;
52
+ spellPromise = (async () => {
53
+ const [affRes, dicRes] = await Promise.all([
54
+ fetch("../lib/dict/en.aff"),
55
+ fetch("../lib/dict/en.dic"),
56
+ ]);
57
+ if (!affRes.ok || !dicRes.ok) {
58
+ throw new Error(`spellcheck: dict fetch failed (aff=${affRes.status} dic=${dicRes.status})`);
59
+ }
60
+ const [aff, dic] = await Promise.all([affRes.text(), dicRes.text()]);
61
+ const sp = new NSpell({ aff, dic });
62
+ try {
63
+ const raw = localStorage.getItem(USER_DICT_KEY);
64
+ if (raw)
65
+ for (const w of JSON.parse(raw))
66
+ sp.add(w);
67
+ }
68
+ catch { /* corrupt entry — start clean */ }
69
+ return sp;
70
+ })();
71
+ return spellPromise;
72
+ }
73
+ function addToUserDict(word, sp) {
74
+ try {
75
+ const raw = localStorage.getItem(USER_DICT_KEY);
76
+ const arr = raw ? JSON.parse(raw) : [];
77
+ if (arr.includes(word))
78
+ return;
79
+ arr.push(word);
80
+ localStorage.setItem(USER_DICT_KEY, JSON.stringify(arr));
81
+ }
82
+ catch { /* */ }
83
+ sp.add(word);
84
+ }
85
+ // ── Live decoration ───────────────────────────────────────────────
86
+ /** Walk the editor body, wrap newly-misspelled words, unwrap markers
87
+ * that are now correct. Mutations are wrapped in undoManager.ignore so
88
+ * they don't pollute undo history; selection is preserved via a
89
+ * TinyMCE bookmark. */
90
+ function decorate(editor, sp) {
91
+ const body = editor.getBody?.();
92
+ const doc = editor.getDoc?.();
93
+ if (!body || !doc)
94
+ return;
95
+ // Don't fight active typing — if the caret is INSIDE a current
96
+ // misspelling marker, the user is mid-correction. Let them finish.
97
+ // We'll re-scan after the debounce on their next pause.
98
+ const sel = doc.getSelection();
99
+ if (sel && sel.rangeCount > 0) {
100
+ const focus = sel.focusNode;
101
+ let p = focus;
102
+ while (p && p !== body) {
103
+ if (p.nodeType === Node.ELEMENT_NODE && p.hasAttribute?.(MARKER_ATTR)) {
104
+ // Cursor inside a marker — skip this pass; re-decorate
105
+ // when they next stop typing.
106
+ return;
107
+ }
108
+ p = p.parentNode;
109
+ }
110
+ }
111
+ const bookmark = editor.selection?.getBookmark?.(2);
112
+ try {
113
+ editor.undoManager?.ignore?.(() => {
114
+ // 1. Unwrap any existing markers — we'll re-wrap fresh based
115
+ // on the current content. Cheaper than incremental update
116
+ // and avoids stale markers if the user fixed a word
117
+ // without going through our menu (just retyped it).
118
+ const old = body.querySelectorAll(`span[${MARKER_ATTR}]`);
119
+ for (const m of old) {
120
+ const parent = m.parentNode;
121
+ if (!parent)
122
+ continue;
123
+ while (m.firstChild)
124
+ parent.insertBefore(m.firstChild, m);
125
+ parent.removeChild(m);
126
+ }
127
+ // Merge text nodes that were split by the now-removed spans.
128
+ body.normalize();
129
+ // 2. Walk text nodes and collect words to wrap.
130
+ const walker = doc.createTreeWalker(body, NodeFilter.SHOW_TEXT, {
131
+ acceptNode(node) {
132
+ let p = node.parentNode;
133
+ while (p && p !== body) {
134
+ if (p.nodeType === Node.ELEMENT_NODE && SKIP_TAGS.has(p.tagName)) {
135
+ return NodeFilter.FILTER_REJECT;
136
+ }
137
+ p = p.parentNode;
138
+ }
139
+ return NodeFilter.FILTER_ACCEPT;
140
+ },
141
+ });
142
+ const hits = [];
143
+ let n = walker.nextNode();
144
+ // Letter / digit / apostrophe / hyphen — tokenize words via
145
+ // Unicode-aware regex so we don't false-flag accented words.
146
+ const WORD_RE = /[\p{L}][\p{L}'’\-]*/gu;
147
+ while (n) {
148
+ const tn = n;
149
+ const text = tn.data;
150
+ let m;
151
+ WORD_RE.lastIndex = 0;
152
+ while ((m = WORD_RE.exec(text)) !== null) {
153
+ const word = m[0];
154
+ if (word.length < MIN_WORD_LEN)
155
+ continue;
156
+ if (sp.correct(word))
157
+ continue;
158
+ hits.push({ node: tn, start: m.index, end: m.index + word.length });
159
+ }
160
+ n = walker.nextNode();
161
+ }
162
+ // 3. Wrap hits in reverse order — wrapping a span splits the
163
+ // text node, which would invalidate earlier offsets. Going
164
+ // right-to-left keeps the not-yet-touched offsets valid.
165
+ hits.reverse();
166
+ for (const h of hits) {
167
+ const range = doc.createRange();
168
+ range.setStart(h.node, h.start);
169
+ range.setEnd(h.node, h.end);
170
+ const span = doc.createElement("span");
171
+ span.setAttribute(MARKER_ATTR, "1");
172
+ try {
173
+ range.surroundContents(span);
174
+ }
175
+ catch { /* range spans a node boundary — rare; skip */ }
176
+ }
177
+ });
178
+ }
179
+ finally {
180
+ if (bookmark)
181
+ try {
182
+ editor.selection?.moveToBookmark?.(bookmark);
183
+ }
184
+ catch { /* */ }
185
+ }
186
+ }
187
+ /** Inject the wavy-red CSS into the editor iframe. */
188
+ function installDecorationStyle(editor) {
189
+ const doc = editor.getDoc?.();
190
+ if (!doc)
191
+ return;
192
+ if (doc.getElementById("mailx-spell-style"))
193
+ return;
194
+ const style = doc.createElement("style");
195
+ style.id = "mailx-spell-style";
196
+ style.textContent = `
197
+ span[${MARKER_ATTR}] {
198
+ text-decoration: underline wavy #d33;
199
+ text-decoration-skip-ink: none;
200
+ text-underline-offset: 2px;
201
+ /* No background — keeps the styling subtle, like a native
202
+ * spell underline, not a Find-highlight. */
203
+ background: transparent;
204
+ }
205
+ `;
206
+ doc.head.appendChild(style);
207
+ }
208
+ /** Strip decoration markers from serialized output. TinyMCE fires
209
+ * attribute filters during getContent / draft-save; this filter
210
+ * unwraps the span so the saved/sent HTML carries only the text. */
211
+ function installSerializerFilter(editor) {
212
+ if (editor.__mailxSpellSerializerWired)
213
+ return;
214
+ editor.__mailxSpellSerializerWired = true;
215
+ try {
216
+ editor.serializer.addAttributeFilter(MARKER_ATTR, (nodes) => {
217
+ for (const node of nodes) {
218
+ // TinyMCE's html-node API: `unwrap()` replaces the node
219
+ // with its children. Exactly what we want — keep the
220
+ // text, drop the span.
221
+ if (typeof node.unwrap === "function")
222
+ node.unwrap();
223
+ }
224
+ });
225
+ }
226
+ catch (e) {
227
+ console.warn("[spellcheck] serializer filter setup failed:", e);
228
+ }
229
+ }
230
+ // ── Context menu ──────────────────────────────────────────────────
231
+ function showSuggestionsMenu(parentDoc, x, y, items) {
232
+ parentDoc.getElementById("mailx-spell-menu")?.remove();
233
+ const menu = parentDoc.createElement("div");
234
+ menu.id = "mailx-spell-menu";
235
+ menu.style.cssText = `
236
+ position: fixed;
237
+ left: ${x}px; top: ${y}px;
238
+ z-index: 10000;
239
+ background: var(--color-bg, #fff);
240
+ color: var(--color-text, #222);
241
+ border: 1px solid var(--color-border, #ccc);
242
+ border-radius: 6px;
243
+ box-shadow: 0 4px 16px rgba(0,0,0,0.18);
244
+ padding: 4px 0;
245
+ font: 13px system-ui, sans-serif;
246
+ min-width: 180px;
247
+ max-width: 320px;
248
+ `;
249
+ for (const it of items) {
250
+ if (it.separator) {
251
+ const sep = parentDoc.createElement("div");
252
+ sep.style.cssText = "border-top:1px solid var(--color-border,#ddd); margin: 4px 0;";
253
+ menu.appendChild(sep);
254
+ continue;
255
+ }
256
+ const btn = parentDoc.createElement("button");
257
+ btn.type = "button";
258
+ btn.textContent = it.label;
259
+ btn.style.cssText = `
260
+ display: block; width: 100%; text-align: left;
261
+ padding: 5px 12px; border: none; background: none;
262
+ color: inherit; cursor: pointer; font: inherit;
263
+ ${it.emphasized ? "font-weight: 600;" : ""}
264
+ `;
265
+ btn.addEventListener("mouseenter", () => { btn.style.background = "var(--color-bg-hover, #eef)"; });
266
+ btn.addEventListener("mouseleave", () => { btn.style.background = "none"; });
267
+ btn.addEventListener("click", () => {
268
+ try {
269
+ it.action();
270
+ }
271
+ finally {
272
+ menu.remove();
273
+ }
274
+ });
275
+ menu.appendChild(btn);
276
+ }
277
+ parentDoc.body.appendChild(menu);
278
+ const r = menu.getBoundingClientRect();
279
+ if (r.right > window.innerWidth)
280
+ menu.style.left = `${Math.max(8, window.innerWidth - r.width - 8)}px`;
281
+ if (r.bottom > window.innerHeight)
282
+ menu.style.top = `${Math.max(8, window.innerHeight - r.height - 8)}px`;
283
+ const dismiss = (e) => {
284
+ if (e.type === "keydown" && e.key !== "Escape")
285
+ return;
286
+ if (e.type === "mousedown" && menu.contains(e.target))
287
+ return;
288
+ menu.remove();
289
+ parentDoc.removeEventListener("mousedown", dismiss, true);
290
+ parentDoc.removeEventListener("keydown", dismiss, true);
291
+ };
292
+ setTimeout(() => {
293
+ parentDoc.addEventListener("mousedown", dismiss, true);
294
+ parentDoc.addEventListener("keydown", dismiss, true);
295
+ }, 0);
296
+ }
297
+ /** Replace a misspelling marker span with the correction. Done via
298
+ * range-based selection + insertText so TinyMCE's undo stack and
299
+ * dirty-tracking pick it up properly. */
300
+ function replaceMarker(editor, marker, replacement) {
301
+ const doc = editor.getDoc();
302
+ const range = doc.createRange();
303
+ range.selectNode(marker);
304
+ const sel = doc.getSelection();
305
+ if (!sel)
306
+ return;
307
+ sel.removeAllRanges();
308
+ sel.addRange(range);
309
+ try {
310
+ if (!doc.execCommand("insertText", false, replacement)) {
311
+ range.deleteContents();
312
+ range.insertNode(doc.createTextNode(replacement));
313
+ }
314
+ }
315
+ catch {
316
+ range.deleteContents();
317
+ range.insertNode(doc.createTextNode(replacement));
318
+ }
319
+ }
320
+ // ── Public entry point ────────────────────────────────────────────
321
+ /** Wire the spell-check into a TinyMCE editor instance. Idempotent. */
322
+ export function wireSpellcheck(editor) {
323
+ if (editor.__mailxSpellWired)
324
+ return;
325
+ editor.__mailxSpellWired = true;
326
+ let sp = null;
327
+ let decorateTimer = null;
328
+ const scheduleDecorate = () => {
329
+ if (!sp)
330
+ return;
331
+ if (decorateTimer)
332
+ clearTimeout(decorateTimer);
333
+ decorateTimer = setTimeout(() => {
334
+ decorateTimer = null;
335
+ if (sp)
336
+ decorate(editor, sp);
337
+ }, DECORATE_DEBOUNCE_MS);
338
+ };
339
+ // Kick off the dictionary load. First decoration runs as soon as
340
+ // it resolves; subsequent runs are triggered by editor events.
341
+ getSpell().then((loaded) => {
342
+ sp = loaded;
343
+ installDecorationStyle(editor);
344
+ installSerializerFilter(editor);
345
+ decorate(editor, loaded);
346
+ }).catch((err) => {
347
+ console.error("[spellcheck] dict load failed:", err);
348
+ });
349
+ // Re-decorate on edits / paste / content swap. `nodechange` covers
350
+ // most of these; `input` catches typing in finer-grained events on
351
+ // some TinyMCE versions.
352
+ editor.on("input nodechange setcontent paste keyup", scheduleDecorate);
353
+ // Right-click handler. If the click landed on a marker span, show
354
+ // suggestions; otherwise let the default menu (WebView2's) fire.
355
+ const iframeDoc = editor.getDoc();
356
+ iframeDoc.addEventListener("contextmenu", (ev) => {
357
+ const e = ev;
358
+ const target = e.target;
359
+ if (!target)
360
+ return;
361
+ const marker = target.closest?.(`span[${MARKER_ATTR}]`);
362
+ if (!marker)
363
+ return; // not on a misspelled word — default menu fires
364
+ const word = marker.textContent || "";
365
+ if (!word || !sp)
366
+ return;
367
+ e.preventDefault();
368
+ e.stopPropagation();
369
+ const sugs = sp.suggest(word).slice(0, 7);
370
+ const iframeEl = editor.iframeElement;
371
+ const iframeRect = iframeEl ? iframeEl.getBoundingClientRect() : { left: 0, top: 0 };
372
+ const items = [];
373
+ if (sugs.length === 0) {
374
+ items.push({ label: "(no suggestions)", action: () => { } });
375
+ }
376
+ else {
377
+ for (const s of sugs) {
378
+ items.push({
379
+ label: s,
380
+ emphasized: true,
381
+ action: () => {
382
+ replaceMarker(editor, marker, s);
383
+ // Re-decorate so the replacement is checked too.
384
+ scheduleDecorate();
385
+ },
386
+ });
387
+ }
388
+ }
389
+ items.push({ label: "", action: () => { }, separator: true });
390
+ items.push({
391
+ label: `Add "${word}" to dictionary`,
392
+ action: () => { if (sp)
393
+ addToUserDict(word, sp); scheduleDecorate(); },
394
+ });
395
+ items.push({
396
+ label: "Ignore (this session)",
397
+ action: () => { if (sp)
398
+ sp.add(word); scheduleDecorate(); },
399
+ });
400
+ showSuggestionsMenu(document, iframeRect.left + e.clientX, iframeRect.top + e.clientY, items);
401
+ }, true);
402
+ }
403
+ //# sourceMappingURL=spellcheck.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spellcheck.js","sourceRoot":"","sources":["spellcheck.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,sEAAsE;AACtE,sEAAsE;AACtE,iCAAiC;AACjC,OAAO,MAAM,MAAM,QAAQ,CAAC;AAG5B,MAAM,aAAa,GAAG,iBAAiB,CAAC;AACxC,MAAM,WAAW,GAAG,uBAAuB,CAAC;AAC5C,MAAM,oBAAoB,GAAG,GAAG,CAAC;AACjC,MAAM,YAAY,GAAG,CAAC,CAAC;AACvB,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;AAEvG,IAAI,YAAY,GAA2B,IAAI,CAAC;AAChD,KAAK,UAAU,QAAQ;IACnB,IAAI,YAAY;QAAE,OAAO,YAAY,CAAC;IACtC,YAAY,GAAG,CAAC,KAAK,IAAI,EAAE;QACvB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACvC,KAAK,CAAC,oBAAoB,CAAC;YAC3B,KAAK,CAAC,oBAAoB,CAAC;SAC9B,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;YAC3B,MAAM,IAAI,KAAK,CAAC,sCAAsC,MAAM,CAAC,MAAM,QAAQ,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;QACjG,CAAC;QACD,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QACrE,MAAM,EAAE,GAAG,IAAI,MAAM,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;QACpC,IAAI,CAAC;YACD,MAAM,GAAG,GAAG,YAAY,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;YAChD,IAAI,GAAG;gBAAE,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAa;oBAAE,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACpE,CAAC;QAAC,MAAM,CAAC,CAAC,iCAAiC,CAAC,CAAC;QAC7C,OAAO,EAAE,CAAC;IACd,CAAC,CAAC,EAAE,CAAC;IACL,OAAO,YAAY,CAAC;AACxB,CAAC;AAED,SAAS,aAAa,CAAC,IAAY,EAAE,EAAU;IAC3C,IAAI,CAAC;QACD,MAAM,GAAG,GAAG,YAAY,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QAChD,MAAM,GAAG,GAAG,GAAG,CAAC,CAAC,CAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAc,CAAC,CAAC,CAAC,EAAE,CAAC;QACrD,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC;YAAE,OAAO;QAC/B,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACf,YAAY,CAAC,OAAO,CAAC,aAAa,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;IAC7D,CAAC;IAAC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC;IACjB,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AACjB,CAAC;AAED,qEAAqE;AAErE;;;wBAGwB;AACxB,SAAS,QAAQ,CAAC,MAAW,EAAE,EAAU;IACrC,MAAM,IAAI,GAAuB,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC;IACpD,MAAM,GAAG,GAAoB,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC;IAC/C,IAAI,CAAC,IAAI,IAAI,CAAC,GAAG;QAAE,OAAO;IAE1B,+DAA+D;IAC/D,mEAAmE;IACnE,wDAAwD;IACxD,MAAM,GAAG,GAAG,GAAG,CAAC,YAAY,EAAE,CAAC;IAC/B,IAAI,GAAG,IAAI,GAAG,CAAC,UAAU,GAAG,CAAC,EAAE,CAAC;QAC5B,MAAM,KAAK,GAAG,GAAG,CAAC,SAAwB,CAAC;QAC3C,IAAI,CAAC,GAAgB,KAAK,CAAC;QAC3B,OAAO,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;YACrB,IAAI,CAAC,CAAC,QAAQ,KAAK,IAAI,CAAC,YAAY,IAAK,CAAa,CAAC,YAAY,EAAE,CAAC,WAAW,CAAC,EAAE,CAAC;gBACjF,uDAAuD;gBACvD,8BAA8B;gBAC9B,OAAO;YACX,CAAC;YACD,CAAC,GAAG,CAAC,CAAC,UAAU,CAAC;QACrB,CAAC;IACL,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,CAAC,SAAS,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC;IACpD,IAAI,CAAC;QACD,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,CAAC,GAAG,EAAE;YAC9B,6DAA6D;YAC7D,6DAA6D;YAC7D,uDAAuD;YACvD,uDAAuD;YACvD,MAAM,GAAG,GAAG,IAAI,CAAC,gBAAgB,CAAC,QAAQ,WAAW,GAAG,CAAC,CAAC;YAC1D,KAAK,MAAM,CAAC,IAAI,GAAG,EAAE,CAAC;gBAClB,MAAM,MAAM,GAAG,CAAC,CAAC,UAAU,CAAC;gBAC5B,IAAI,CAAC,MAAM;oBAAE,SAAS;gBACtB,OAAO,CAAC,CAAC,UAAU;oBAAE,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;gBAC1D,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;YAC1B,CAAC;YACD,6DAA6D;YAC7D,IAAI,CAAC,SAAS,EAAE,CAAC;YAEjB,gDAAgD;YAChD,MAAM,MAAM,GAAG,GAAG,CAAC,gBAAgB,CAAC,IAAI,EAAE,UAAU,CAAC,SAAS,EAAE;gBAC5D,UAAU,CAAC,IAAI;oBACX,IAAI,CAAC,GAAgB,IAAI,CAAC,UAAU,CAAC;oBACrC,OAAO,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;wBACrB,IAAI,CAAC,CAAC,QAAQ,KAAK,IAAI,CAAC,YAAY,IAAI,SAAS,CAAC,GAAG,CAAE,CAAa,CAAC,OAAO,CAAC,EAAE,CAAC;4BAC5E,OAAO,UAAU,CAAC,aAAa,CAAC;wBACpC,CAAC;wBACD,CAAC,GAAG,CAAC,CAAC,UAAU,CAAC;oBACrB,CAAC;oBACD,OAAO,UAAU,CAAC,aAAa,CAAC;gBACpC,CAAC;aACJ,CAAC,CAAC;YAEH,MAAM,IAAI,GAAU,EAAE,CAAC;YACvB,IAAI,CAAC,GAAgB,MAAM,CAAC,QAAQ,EAAE,CAAC;YACvC,4DAA4D;YAC5D,6DAA6D;YAC7D,MAAM,OAAO,GAAG,uBAAuB,CAAC;YACxC,OAAO,CAAC,EAAE,CAAC;gBACP,MAAM,EAAE,GAAG,CAAS,CAAC;gBACrB,MAAM,IAAI,GAAG,EAAE,CAAC,IAAI,CAAC;gBACrB,IAAI,CAAyB,CAAC;gBAC9B,OAAO,CAAC,SAAS,GAAG,CAAC,CAAC;gBACtB,OAAO,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;oBACvC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;oBAClB,IAAI,IAAI,CAAC,MAAM,GAAG,YAAY;wBAAE,SAAS;oBACzC,IAAI,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;wBAAE,SAAS;oBAC/B,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;gBACxE,CAAC;gBACD,CAAC,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAC;YAC1B,CAAC;YAED,6DAA6D;YAC7D,8DAA8D;YAC9D,4DAA4D;YAC5D,IAAI,CAAC,OAAO,EAAE,CAAC;YACf,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;gBACnB,MAAM,KAAK,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;gBAChC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC;gBAChC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC;gBAC5B,MAAM,IAAI,GAAG,GAAG,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;gBACvC,IAAI,CAAC,YAAY,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;gBACpC,IAAI,CAAC;oBAAC,KAAK,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;gBAAC,CAAC;gBACrC,MAAM,CAAC,CAAC,8CAA8C,CAAC,CAAC;YAC5D,CAAC;QACL,CAAC,CAAC,CAAC;IACP,CAAC;YAAS,CAAC;QACP,IAAI,QAAQ;YAAE,IAAI,CAAC;gBAAC,MAAM,CAAC,SAAS,EAAE,cAAc,EAAE,CAAC,QAAQ,CAAC,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC;IACvF,CAAC;AACL,CAAC;AAED,sDAAsD;AACtD,SAAS,sBAAsB,CAAC,MAAW;IACvC,MAAM,GAAG,GAAoB,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC;IAC/C,IAAI,CAAC,GAAG;QAAE,OAAO;IACjB,IAAI,GAAG,CAAC,cAAc,CAAC,mBAAmB,CAAC;QAAE,OAAO;IACpD,MAAM,KAAK,GAAG,GAAG,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IACzC,KAAK,CAAC,EAAE,GAAG,mBAAmB,CAAC;IAC/B,KAAK,CAAC,WAAW,GAAG;eACT,WAAW;;;;;;;;KAQrB,CAAC;IACF,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;AAChC,CAAC;AAED;;qEAEqE;AACrE,SAAS,uBAAuB,CAAC,MAAW;IACxC,IAAK,MAAc,CAAC,2BAA2B;QAAE,OAAO;IACvD,MAAc,CAAC,2BAA2B,GAAG,IAAI,CAAC;IACnD,IAAI,CAAC;QACD,MAAM,CAAC,UAAU,CAAC,kBAAkB,CAAC,WAAW,EAAE,CAAC,KAAY,EAAE,EAAE;YAC/D,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACvB,wDAAwD;gBACxD,qDAAqD;gBACrD,uBAAuB;gBACvB,IAAI,OAAO,IAAI,CAAC,MAAM,KAAK,UAAU;oBAAE,IAAI,CAAC,MAAM,EAAE,CAAC;YACzD,CAAC;QACL,CAAC,CAAC,CAAC;IACP,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACT,OAAO,CAAC,IAAI,CAAC,8CAA8C,EAAE,CAAC,CAAC,CAAC;IACpE,CAAC;AACL,CAAC;AAED,qEAAqE;AAErE,SAAS,mBAAmB,CACxB,SAAmB,EAAE,CAAS,EAAE,CAAS,EACzC,KAA8F;IAE9F,SAAS,CAAC,cAAc,CAAC,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;IACvD,MAAM,IAAI,GAAG,SAAS,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;IAC5C,IAAI,CAAC,EAAE,GAAG,kBAAkB,CAAC;IAC7B,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG;;gBAET,CAAC,YAAY,CAAC;;;;;;;;;;;KAWzB,CAAC;IACF,KAAK,MAAM,EAAE,IAAI,KAAK,EAAE,CAAC;QACrB,IAAI,EAAE,CAAC,SAAS,EAAE,CAAC;YACf,MAAM,GAAG,GAAG,SAAS,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;YAC3C,GAAG,CAAC,KAAK,CAAC,OAAO,GAAG,+DAA+D,CAAC;YACpF,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;YACtB,SAAS;QACb,CAAC;QACD,MAAM,GAAG,GAAG,SAAS,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;QAC9C,GAAG,CAAC,IAAI,GAAG,QAAQ,CAAC;QACpB,GAAG,CAAC,WAAW,GAAG,EAAE,CAAC,KAAK,CAAC;QAC3B,GAAG,CAAC,KAAK,CAAC,OAAO,GAAG;;;;cAId,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,EAAE;SAC7C,CAAC;QACF,GAAG,CAAC,gBAAgB,CAAC,YAAY,EAAE,GAAG,EAAE,GAAG,GAAG,CAAC,KAAK,CAAC,UAAU,GAAG,6BAA6B,CAAC,CAAC,CAAC,CAAC,CAAC;QACpG,GAAG,CAAC,gBAAgB,CAAC,YAAY,EAAE,GAAG,EAAE,GAAG,GAAG,CAAC,KAAK,CAAC,UAAU,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7E,GAAG,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE;YAC/B,IAAI,CAAC;gBAAC,EAAE,CAAC,MAAM,EAAE,CAAC;YAAC,CAAC;oBAAS,CAAC;gBAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YAAC,CAAC;QACnD,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;IACD,SAAS,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IACjC,MAAM,CAAC,GAAG,IAAI,CAAC,qBAAqB,EAAE,CAAC;IACvC,IAAI,CAAC,CAAC,KAAK,GAAG,MAAM,CAAC,UAAU;QAAE,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,UAAU,GAAG,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,IAAI,CAAC;IACvG,IAAI,CAAC,CAAC,MAAM,GAAG,MAAM,CAAC,WAAW;QAAE,IAAI,CAAC,KAAK,CAAC,GAAG,GAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,WAAW,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC;IAC3G,MAAM,OAAO,GAAG,CAAC,CAAQ,EAAE,EAAE;QACzB,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS,IAAK,CAAmB,CAAC,GAAG,KAAK,QAAQ;YAAE,OAAO;QAC1E,IAAI,CAAC,CAAC,IAAI,KAAK,WAAW,IAAI,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAc,CAAC;YAAE,OAAO;QACtE,IAAI,CAAC,MAAM,EAAE,CAAC;QACd,SAAS,CAAC,mBAAmB,CAAC,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;QAC1D,SAAS,CAAC,mBAAmB,CAAC,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IAC5D,CAAC,CAAC;IACF,UAAU,CAAC,GAAG,EAAE;QACZ,SAAS,CAAC,gBAAgB,CAAC,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;QACvD,SAAS,CAAC,gBAAgB,CAAC,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IACzD,CAAC,EAAE,CAAC,CAAC,CAAC;AACV,CAAC;AAED;;0CAE0C;AAC1C,SAAS,aAAa,CAAC,MAAW,EAAE,MAAmB,EAAE,WAAmB;IACxE,MAAM,GAAG,GAAa,MAAM,CAAC,MAAM,EAAE,CAAC;IACtC,MAAM,KAAK,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;IAChC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;IACzB,MAAM,GAAG,GAAG,GAAG,CAAC,YAAY,EAAE,CAAC;IAC/B,IAAI,CAAC,GAAG;QAAE,OAAO;IACjB,GAAG,CAAC,eAAe,EAAE,CAAC;IACtB,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IACpB,IAAI,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,YAAY,EAAE,KAAK,EAAE,WAAW,CAAC,EAAE,CAAC;YACrD,KAAK,CAAC,cAAc,EAAE,CAAC;YACvB,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC,CAAC;QACtD,CAAC;IACL,CAAC;IAAC,MAAM,CAAC;QACL,KAAK,CAAC,cAAc,EAAE,CAAC;QACvB,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC,CAAC;IACtD,CAAC;AACL,CAAC;AAED,qEAAqE;AAErE,uEAAuE;AACvE,MAAM,UAAU,cAAc,CAAC,MAAW;IACtC,IAAK,MAAc,CAAC,iBAAiB;QAAE,OAAO;IAC7C,MAAc,CAAC,iBAAiB,GAAG,IAAI,CAAC;IAEzC,IAAI,EAAE,GAAkB,IAAI,CAAC;IAC7B,IAAI,aAAa,GAAyC,IAAI,CAAC;IAC/D,MAAM,gBAAgB,GAAG,GAAS,EAAE;QAChC,IAAI,CAAC,EAAE;YAAE,OAAO;QAChB,IAAI,aAAa;YAAE,YAAY,CAAC,aAAa,CAAC,CAAC;QAC/C,aAAa,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,aAAa,GAAG,IAAI,CAAC;YACrB,IAAI,EAAE;gBAAE,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QACjC,CAAC,EAAE,oBAAoB,CAAC,CAAC;IAC7B,CAAC,CAAC;IAEF,iEAAiE;IACjE,+DAA+D;IAC/D,QAAQ,EAAE,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE;QACvB,EAAE,GAAG,MAAM,CAAC;QACZ,sBAAsB,CAAC,MAAM,CAAC,CAAC;QAC/B,uBAAuB,CAAC,MAAM,CAAC,CAAC;QAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;QACb,OAAO,CAAC,KAAK,CAAC,gCAAgC,EAAE,GAAG,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;IAEH,mEAAmE;IACnE,mEAAmE;IACnE,yBAAyB;IACzB,MAAM,CAAC,EAAE,CAAC,yCAAyC,EAAE,gBAAgB,CAAC,CAAC;IAEvE,kEAAkE;IAClE,iEAAiE;IACjE,MAAM,SAAS,GAAa,MAAM,CAAC,MAAM,EAAE,CAAC;IAC5C,SAAS,CAAC,gBAAgB,CAAC,aAAa,EAAE,CAAC,EAAS,EAAE,EAAE;QACpD,MAAM,CAAC,GAAG,EAAgB,CAAC;QAC3B,MAAM,MAAM,GAAG,CAAC,CAAC,MAA4B,CAAC;QAC9C,IAAI,CAAC,MAAM;YAAE,OAAO;QACpB,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC,QAAQ,WAAW,GAAG,CAAuB,CAAC;QAC9E,IAAI,CAAC,MAAM;YAAE,OAAO,CAAC,gDAAgD;QACrE,MAAM,IAAI,GAAG,MAAM,CAAC,WAAW,IAAI,EAAE,CAAC;QACtC,IAAI,CAAC,IAAI,IAAI,CAAC,EAAE;YAAE,OAAO;QACzB,CAAC,CAAC,cAAc,EAAE,CAAC;QACnB,CAAC,CAAC,eAAe,EAAE,CAAC;QACpB,MAAM,IAAI,GAAa,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACpD,MAAM,QAAQ,GAAG,MAAM,CAAC,aAA8C,CAAC;QACvE,MAAM,UAAU,GAAG,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,qBAAqB,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;QACrF,MAAM,KAAK,GAA4F,EAAE,CAAC;QAC1G,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACpB,KAAK,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,kBAAkB,EAAE,MAAM,EAAE,GAAG,EAAE,GAAS,CAAC,EAAE,CAAC,CAAC;QACvE,CAAC;aAAM,CAAC;YACJ,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;gBACnB,KAAK,CAAC,IAAI,CAAC;oBACP,KAAK,EAAE,CAAC;oBACR,UAAU,EAAE,IAAI;oBAChB,MAAM,EAAE,GAAG,EAAE;wBACT,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;wBACjC,iDAAiD;wBACjD,gBAAgB,EAAE,CAAC;oBACvB,CAAC;iBACJ,CAAC,CAAC;YACP,CAAC;QACL,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,GAAS,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACpE,KAAK,CAAC,IAAI,CAAC;YACP,KAAK,EAAE,QAAQ,IAAI,iBAAiB;YACpC,MAAM,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE;gBAAE,aAAa,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAAC;SACzE,CAAC,CAAC;QACH,KAAK,CAAC,IAAI,CAAC;YACP,KAAK,EAAE,uBAAuB;YAC9B,MAAM,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE;gBAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAAC;SAC9D,CAAC,CAAC;QACH,mBAAmB,CAAC,QAAQ,EAAE,UAAU,CAAC,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,GAAG,CAAC,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAClG,CAAC,EAAE,IAAI,CAAC,CAAC;AACb,CAAC"}