litura-app 0.3.7 → 0.4.2

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/public/app.js CHANGED
@@ -1,4 +1,1000 @@
1
1
  (() => {
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __commonJS = (cb, mod) => function __require() {
9
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
+ // If the importer is in node compatibility mode or this is not an ESM
21
+ // file that has been converted to a CommonJS file using a Babel-
22
+ // compatible transform (i.e. "__esModule" has not been set), then set
23
+ // "default" to the CommonJS "module.exports" for node compatibility.
24
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25
+ mod
26
+ ));
27
+
28
+ // node_modules/is-buffer/index.js
29
+ var require_is_buffer = __commonJS({
30
+ "node_modules/is-buffer/index.js"(exports, module) {
31
+ module.exports = function isBuffer(obj) {
32
+ return obj != null && obj.constructor != null && typeof obj.constructor.isBuffer === "function" && obj.constructor.isBuffer(obj);
33
+ };
34
+ }
35
+ });
36
+
37
+ // node_modules/nspell/lib/util/rule-codes.js
38
+ var require_rule_codes = __commonJS({
39
+ "node_modules/nspell/lib/util/rule-codes.js"(exports, module) {
40
+ "use strict";
41
+ module.exports = ruleCodes;
42
+ var NO_CODES = [];
43
+ function ruleCodes(flags, value) {
44
+ var index = 0;
45
+ var result;
46
+ if (!value) return NO_CODES;
47
+ if (flags.FLAG === "long") {
48
+ result = new Array(Math.ceil(value.length / 2));
49
+ while (index < value.length) {
50
+ result[index / 2] = value.slice(index, index + 2);
51
+ index += 2;
52
+ }
53
+ return result;
54
+ }
55
+ return value.split(flags.FLAG === "num" ? "," : "");
56
+ }
57
+ }
58
+ });
59
+
60
+ // node_modules/nspell/lib/util/affix.js
61
+ var require_affix = __commonJS({
62
+ "node_modules/nspell/lib/util/affix.js"(exports, module) {
63
+ "use strict";
64
+ var parse = require_rule_codes();
65
+ module.exports = affix;
66
+ var push = [].push;
67
+ var alphabet = "etaoinshrdlcumwfgypbvkjxqz".split("");
68
+ var whiteSpaceExpression = /\s+/;
69
+ var defaultKeyboardLayout = [
70
+ "qwertzuop",
71
+ "yxcvbnm",
72
+ "qaw",
73
+ "say",
74
+ "wse",
75
+ "dsx",
76
+ "sy",
77
+ "edr",
78
+ "fdc",
79
+ "dx",
80
+ "rft",
81
+ "gfv",
82
+ "fc",
83
+ "tgz",
84
+ "hgb",
85
+ "gv",
86
+ "zhu",
87
+ "jhn",
88
+ "hb",
89
+ "uji",
90
+ "kjm",
91
+ "jn",
92
+ "iko",
93
+ "lkm"
94
+ ];
95
+ function affix(doc2) {
96
+ var rules = /* @__PURE__ */ Object.create(null);
97
+ var compoundRuleCodes = /* @__PURE__ */ Object.create(null);
98
+ var flags = /* @__PURE__ */ Object.create(null);
99
+ var replacementTable = [];
100
+ var conversion = { in: [], out: [] };
101
+ var compoundRules = [];
102
+ var aff = doc2.toString("utf8");
103
+ var lines = [];
104
+ var last = 0;
105
+ var index = aff.indexOf("\n");
106
+ var parts;
107
+ var line;
108
+ var ruleType;
109
+ var count;
110
+ var remove2;
111
+ var add2;
112
+ var source;
113
+ var entry;
114
+ var position;
115
+ var rule;
116
+ var value;
117
+ var offset;
118
+ var character;
119
+ flags.KEY = [];
120
+ while (index > -1) {
121
+ pushLine(aff.slice(last, index));
122
+ last = index + 1;
123
+ index = aff.indexOf("\n", last);
124
+ }
125
+ pushLine(aff.slice(last));
126
+ index = -1;
127
+ while (++index < lines.length) {
128
+ line = lines[index];
129
+ parts = line.split(whiteSpaceExpression);
130
+ ruleType = parts[0];
131
+ if (ruleType === "REP") {
132
+ count = index + parseInt(parts[1], 10);
133
+ while (++index <= count) {
134
+ parts = lines[index].split(whiteSpaceExpression);
135
+ replacementTable.push([parts[1], parts[2]]);
136
+ }
137
+ index--;
138
+ } else if (ruleType === "ICONV" || ruleType === "OCONV") {
139
+ count = index + parseInt(parts[1], 10);
140
+ entry = conversion[ruleType === "ICONV" ? "in" : "out"];
141
+ while (++index <= count) {
142
+ parts = lines[index].split(whiteSpaceExpression);
143
+ entry.push([new RegExp(parts[1], "g"), parts[2]]);
144
+ }
145
+ index--;
146
+ } else if (ruleType === "COMPOUNDRULE") {
147
+ count = index + parseInt(parts[1], 10);
148
+ while (++index <= count) {
149
+ rule = lines[index].split(whiteSpaceExpression)[1];
150
+ position = -1;
151
+ compoundRules.push(rule);
152
+ while (++position < rule.length) {
153
+ compoundRuleCodes[rule.charAt(position)] = [];
154
+ }
155
+ }
156
+ index--;
157
+ } else if (ruleType === "PFX" || ruleType === "SFX") {
158
+ count = index + parseInt(parts[3], 10);
159
+ rule = {
160
+ type: ruleType,
161
+ combineable: parts[2] === "Y",
162
+ entries: []
163
+ };
164
+ rules[parts[1]] = rule;
165
+ while (++index <= count) {
166
+ parts = lines[index].split(whiteSpaceExpression);
167
+ remove2 = parts[2];
168
+ add2 = parts[3].split("/");
169
+ source = parts[4];
170
+ entry = {
171
+ add: "",
172
+ remove: "",
173
+ match: "",
174
+ continuation: parse(flags, add2[1])
175
+ };
176
+ if (add2 && add2[0] !== "0") {
177
+ entry.add = add2[0];
178
+ }
179
+ try {
180
+ if (remove2 !== "0") {
181
+ entry.remove = ruleType === "SFX" ? end(remove2) : remove2;
182
+ }
183
+ if (source && source !== ".") {
184
+ entry.match = ruleType === "SFX" ? end(source) : start(source);
185
+ }
186
+ } catch (_) {
187
+ entry = null;
188
+ }
189
+ if (entry) {
190
+ rule.entries.push(entry);
191
+ }
192
+ }
193
+ index--;
194
+ } else if (ruleType === "TRY") {
195
+ source = parts[1];
196
+ offset = -1;
197
+ value = [];
198
+ while (++offset < source.length) {
199
+ character = source.charAt(offset);
200
+ if (character.toLowerCase() === character) {
201
+ value.push(character);
202
+ }
203
+ }
204
+ offset = -1;
205
+ while (++offset < alphabet.length) {
206
+ if (source.indexOf(alphabet[offset]) < 0) {
207
+ value.push(alphabet[offset]);
208
+ }
209
+ }
210
+ flags[ruleType] = value;
211
+ } else if (ruleType === "KEY") {
212
+ push.apply(flags[ruleType], parts[1].split("|"));
213
+ } else if (ruleType === "COMPOUNDMIN") {
214
+ flags[ruleType] = Number(parts[1]);
215
+ } else if (ruleType === "ONLYINCOMPOUND") {
216
+ flags[ruleType] = parts[1];
217
+ compoundRuleCodes[parts[1]] = [];
218
+ } else if (ruleType === "FLAG" || ruleType === "KEEPCASE" || ruleType === "NOSUGGEST" || ruleType === "WORDCHARS") {
219
+ flags[ruleType] = parts[1];
220
+ } else {
221
+ flags[ruleType] = parts[1];
222
+ }
223
+ }
224
+ if (isNaN(flags.COMPOUNDMIN)) {
225
+ flags.COMPOUNDMIN = 3;
226
+ }
227
+ if (!flags.KEY.length) {
228
+ flags.KEY = defaultKeyboardLayout;
229
+ }
230
+ if (!flags.TRY) {
231
+ flags.TRY = alphabet.concat();
232
+ }
233
+ if (!flags.KEEPCASE) {
234
+ flags.KEEPCASE = false;
235
+ }
236
+ return {
237
+ compoundRuleCodes,
238
+ replacementTable,
239
+ conversion,
240
+ compoundRules,
241
+ rules,
242
+ flags
243
+ };
244
+ function pushLine(line2) {
245
+ line2 = line2.trim();
246
+ if (line2 && line2.charCodeAt(0) !== 35) {
247
+ lines.push(line2);
248
+ }
249
+ }
250
+ }
251
+ function end(source) {
252
+ return new RegExp(source + "$");
253
+ }
254
+ function start(source) {
255
+ return new RegExp("^" + source);
256
+ }
257
+ }
258
+ });
259
+
260
+ // node_modules/nspell/lib/util/normalize.js
261
+ var require_normalize = __commonJS({
262
+ "node_modules/nspell/lib/util/normalize.js"(exports, module) {
263
+ "use strict";
264
+ module.exports = normalize;
265
+ function normalize(value, patterns) {
266
+ var index = -1;
267
+ while (++index < patterns.length) {
268
+ value = value.replace(patterns[index][0], patterns[index][1]);
269
+ }
270
+ return value;
271
+ }
272
+ }
273
+ });
274
+
275
+ // node_modules/nspell/lib/util/flag.js
276
+ var require_flag = __commonJS({
277
+ "node_modules/nspell/lib/util/flag.js"(exports, module) {
278
+ "use strict";
279
+ module.exports = flag;
280
+ function flag(values, value, flags) {
281
+ return flags && value in values && flags.indexOf(values[value]) > -1;
282
+ }
283
+ }
284
+ });
285
+
286
+ // node_modules/nspell/lib/util/exact.js
287
+ var require_exact = __commonJS({
288
+ "node_modules/nspell/lib/util/exact.js"(exports, module) {
289
+ "use strict";
290
+ var flag = require_flag();
291
+ module.exports = exact;
292
+ function exact(context, value) {
293
+ var index = -1;
294
+ if (context.data[value]) {
295
+ return !flag(context.flags, "ONLYINCOMPOUND", context.data[value]);
296
+ }
297
+ if (value.length >= context.flags.COMPOUNDMIN) {
298
+ while (++index < context.compoundRules.length) {
299
+ if (context.compoundRules[index].test(value)) {
300
+ return true;
301
+ }
302
+ }
303
+ }
304
+ return false;
305
+ }
306
+ }
307
+ });
308
+
309
+ // node_modules/nspell/lib/util/form.js
310
+ var require_form = __commonJS({
311
+ "node_modules/nspell/lib/util/form.js"(exports, module) {
312
+ "use strict";
313
+ var normalize = require_normalize();
314
+ var exact = require_exact();
315
+ var flag = require_flag();
316
+ module.exports = form;
317
+ function form(context, value, all) {
318
+ var normal = value.trim();
319
+ var alternative;
320
+ if (!normal) {
321
+ return null;
322
+ }
323
+ normal = normalize(normal, context.conversion.in);
324
+ if (exact(context, normal)) {
325
+ if (!all && flag(context.flags, "FORBIDDENWORD", context.data[normal])) {
326
+ return null;
327
+ }
328
+ return normal;
329
+ }
330
+ if (normal.toUpperCase() === normal) {
331
+ alternative = normal.charAt(0) + normal.slice(1).toLowerCase();
332
+ if (ignore(context.flags, context.data[alternative], all)) {
333
+ return null;
334
+ }
335
+ if (exact(context, alternative)) {
336
+ return alternative;
337
+ }
338
+ }
339
+ alternative = normal.toLowerCase();
340
+ if (alternative !== normal) {
341
+ if (ignore(context.flags, context.data[alternative], all)) {
342
+ return null;
343
+ }
344
+ if (exact(context, alternative)) {
345
+ return alternative;
346
+ }
347
+ }
348
+ return null;
349
+ }
350
+ function ignore(flags, dict, all) {
351
+ return flag(flags, "KEEPCASE", dict) || all || flag(flags, "FORBIDDENWORD", dict);
352
+ }
353
+ }
354
+ });
355
+
356
+ // node_modules/nspell/lib/correct.js
357
+ var require_correct = __commonJS({
358
+ "node_modules/nspell/lib/correct.js"(exports, module) {
359
+ "use strict";
360
+ var form = require_form();
361
+ module.exports = correct;
362
+ function correct(value) {
363
+ return Boolean(form(this, value));
364
+ }
365
+ }
366
+ });
367
+
368
+ // node_modules/nspell/lib/util/casing.js
369
+ var require_casing = __commonJS({
370
+ "node_modules/nspell/lib/util/casing.js"(exports, module) {
371
+ "use strict";
372
+ module.exports = casing;
373
+ function casing(value) {
374
+ var head = exact(value.charAt(0));
375
+ var rest = value.slice(1);
376
+ if (!rest) {
377
+ return head;
378
+ }
379
+ rest = exact(rest);
380
+ if (head === rest) {
381
+ return head;
382
+ }
383
+ if (head === "u" && rest === "l") {
384
+ return "s";
385
+ }
386
+ return null;
387
+ }
388
+ function exact(value) {
389
+ return value === value.toLowerCase() ? "l" : value === value.toUpperCase() ? "u" : null;
390
+ }
391
+ }
392
+ });
393
+
394
+ // node_modules/nspell/lib/suggest.js
395
+ var require_suggest = __commonJS({
396
+ "node_modules/nspell/lib/suggest.js"(exports, module) {
397
+ "use strict";
398
+ var casing = require_casing();
399
+ var normalize = require_normalize();
400
+ var flag = require_flag();
401
+ var form = require_form();
402
+ module.exports = suggest;
403
+ var push = [].push;
404
+ function suggest(value) {
405
+ var self = this;
406
+ var charAdded = {};
407
+ var suggestions = [];
408
+ var weighted = {};
409
+ var memory;
410
+ var replacement;
411
+ var edits = [];
412
+ var values;
413
+ var index;
414
+ var offset;
415
+ var position;
416
+ var count;
417
+ var otherOffset;
418
+ var otherCharacter;
419
+ var character;
420
+ var group;
421
+ var before;
422
+ var after;
423
+ var upper;
424
+ var insensitive;
425
+ var firstLevel;
426
+ var previous;
427
+ var next;
428
+ var nextCharacter;
429
+ var max;
430
+ var distance;
431
+ var size;
432
+ var normalized;
433
+ var suggestion;
434
+ var currentCase;
435
+ value = normalize(value.trim(), self.conversion.in);
436
+ if (!value || self.correct(value)) {
437
+ return [];
438
+ }
439
+ currentCase = casing(value);
440
+ index = -1;
441
+ while (++index < self.replacementTable.length) {
442
+ replacement = self.replacementTable[index];
443
+ offset = value.indexOf(replacement[0]);
444
+ while (offset > -1) {
445
+ edits.push(value.replace(replacement[0], replacement[1]));
446
+ offset = value.indexOf(replacement[0], offset + 1);
447
+ }
448
+ }
449
+ index = -1;
450
+ while (++index < value.length) {
451
+ character = value.charAt(index);
452
+ before = value.slice(0, index);
453
+ after = value.slice(index + 1);
454
+ insensitive = character.toLowerCase();
455
+ upper = insensitive !== character;
456
+ charAdded = {};
457
+ offset = -1;
458
+ while (++offset < self.flags.KEY.length) {
459
+ group = self.flags.KEY[offset];
460
+ position = group.indexOf(insensitive);
461
+ if (position < 0) {
462
+ continue;
463
+ }
464
+ otherOffset = -1;
465
+ while (++otherOffset < group.length) {
466
+ if (otherOffset !== position) {
467
+ otherCharacter = group.charAt(otherOffset);
468
+ if (charAdded[otherCharacter]) {
469
+ continue;
470
+ }
471
+ charAdded[otherCharacter] = true;
472
+ if (upper) {
473
+ otherCharacter = otherCharacter.toUpperCase();
474
+ }
475
+ edits.push(before + otherCharacter + after);
476
+ }
477
+ }
478
+ }
479
+ }
480
+ index = -1;
481
+ nextCharacter = value.charAt(0);
482
+ values = [""];
483
+ max = 1;
484
+ distance = 0;
485
+ while (++index < value.length) {
486
+ character = nextCharacter;
487
+ nextCharacter = value.charAt(index + 1);
488
+ before = value.slice(0, index);
489
+ replacement = character === nextCharacter ? "" : character + character;
490
+ offset = -1;
491
+ count = values.length;
492
+ while (++offset < count) {
493
+ if (offset <= max) {
494
+ values.push(values[offset] + replacement);
495
+ }
496
+ values[offset] += character;
497
+ }
498
+ if (++distance < 3) {
499
+ max = values.length;
500
+ }
501
+ }
502
+ push.apply(edits, values);
503
+ values = [value];
504
+ replacement = value.toLowerCase();
505
+ if (value === replacement || currentCase === null) {
506
+ values.push(value.charAt(0).toUpperCase() + replacement.slice(1));
507
+ }
508
+ replacement = value.toUpperCase();
509
+ if (value !== replacement) {
510
+ values.push(replacement);
511
+ }
512
+ memory = {
513
+ state: {},
514
+ weighted,
515
+ suggestions
516
+ };
517
+ firstLevel = generate(self, memory, values, edits);
518
+ previous = 0;
519
+ max = Math.min(firstLevel.length, Math.pow(Math.max(15 - value.length, 3), 3));
520
+ size = Math.max(Math.pow(10 - value.length, 3), 1);
521
+ while (!suggestions.length && previous < max) {
522
+ next = previous + size;
523
+ generate(self, memory, firstLevel.slice(previous, next));
524
+ previous = next;
525
+ }
526
+ suggestions.sort(sort);
527
+ values = [];
528
+ normalized = [];
529
+ index = -1;
530
+ while (++index < suggestions.length) {
531
+ suggestion = normalize(suggestions[index], self.conversion.out);
532
+ replacement = suggestion.toLowerCase();
533
+ if (normalized.indexOf(replacement) < 0) {
534
+ values.push(suggestion);
535
+ normalized.push(replacement);
536
+ }
537
+ }
538
+ return values;
539
+ function sort(a, b) {
540
+ return sortWeight(a, b) || sortCasing(a, b) || sortAlpha(a, b);
541
+ }
542
+ function sortWeight(a, b) {
543
+ return weighted[a] === weighted[b] ? 0 : weighted[a] > weighted[b] ? -1 : 1;
544
+ }
545
+ function sortCasing(a, b) {
546
+ var leftCasing = casing(a);
547
+ var rightCasing = casing(b);
548
+ return leftCasing === rightCasing ? 0 : leftCasing === currentCase ? -1 : rightCasing === currentCase ? 1 : void 0;
549
+ }
550
+ function sortAlpha(a, b) {
551
+ return a.localeCompare(b);
552
+ }
553
+ }
554
+ function generate(context, memory, words, edits) {
555
+ var characters = context.flags.TRY;
556
+ var data = context.data;
557
+ var flags = context.flags;
558
+ var result = [];
559
+ var index = -1;
560
+ var word;
561
+ var before;
562
+ var character;
563
+ var nextCharacter;
564
+ var nextAfter;
565
+ var nextNextAfter;
566
+ var nextUpper;
567
+ var currentCase;
568
+ var position;
569
+ var after;
570
+ var upper;
571
+ var inject;
572
+ var offset;
573
+ if (edits) {
574
+ while (++index < edits.length) {
575
+ check(edits[index], true);
576
+ }
577
+ }
578
+ index = -1;
579
+ while (++index < words.length) {
580
+ word = words[index];
581
+ before = "";
582
+ character = "";
583
+ nextCharacter = word.charAt(0);
584
+ nextAfter = word;
585
+ nextNextAfter = word.slice(1);
586
+ nextUpper = nextCharacter.toLowerCase() !== nextCharacter;
587
+ currentCase = casing(word);
588
+ position = -1;
589
+ while (++position <= word.length) {
590
+ before += character;
591
+ after = nextAfter;
592
+ nextAfter = nextNextAfter;
593
+ nextNextAfter = nextAfter.slice(1);
594
+ character = nextCharacter;
595
+ nextCharacter = word.charAt(position + 1);
596
+ upper = nextUpper;
597
+ if (nextCharacter) {
598
+ nextUpper = nextCharacter.toLowerCase() !== nextCharacter;
599
+ }
600
+ if (nextAfter && upper !== nextUpper) {
601
+ check(before + switchCase(nextAfter));
602
+ check(
603
+ before + switchCase(nextCharacter) + switchCase(character) + nextNextAfter
604
+ );
605
+ }
606
+ check(before + nextAfter);
607
+ if (nextAfter) {
608
+ check(before + nextCharacter + character + nextNextAfter);
609
+ }
610
+ offset = -1;
611
+ while (++offset < characters.length) {
612
+ inject = characters[offset];
613
+ if (upper && inject !== inject.toUpperCase()) {
614
+ if (currentCase !== "s") {
615
+ check(before + inject + after);
616
+ check(before + inject + nextAfter);
617
+ }
618
+ inject = inject.toUpperCase();
619
+ check(before + inject + after);
620
+ check(before + inject + nextAfter);
621
+ } else {
622
+ check(before + inject + after);
623
+ check(before + inject + nextAfter);
624
+ }
625
+ }
626
+ }
627
+ }
628
+ return result;
629
+ function check(value, double) {
630
+ var state = memory.state[value];
631
+ var corrected;
632
+ if (state !== Boolean(state)) {
633
+ result.push(value);
634
+ corrected = form(context, value);
635
+ state = corrected && !flag(flags, "NOSUGGEST", data[corrected]);
636
+ memory.state[value] = state;
637
+ if (state) {
638
+ memory.weighted[value] = double ? 10 : 0;
639
+ memory.suggestions.push(value);
640
+ }
641
+ }
642
+ if (state) {
643
+ memory.weighted[value]++;
644
+ }
645
+ }
646
+ function switchCase(fragment) {
647
+ var first = fragment.charAt(0);
648
+ return (first.toLowerCase() === first ? first.toUpperCase() : first.toLowerCase()) + fragment.slice(1);
649
+ }
650
+ }
651
+ }
652
+ });
653
+
654
+ // node_modules/nspell/lib/spell.js
655
+ var require_spell = __commonJS({
656
+ "node_modules/nspell/lib/spell.js"(exports, module) {
657
+ "use strict";
658
+ var form = require_form();
659
+ var flag = require_flag();
660
+ module.exports = spell;
661
+ function spell(word) {
662
+ var self = this;
663
+ var value = form(self, word, true);
664
+ return {
665
+ correct: self.correct(word),
666
+ forbidden: Boolean(
667
+ value && flag(self.flags, "FORBIDDENWORD", self.data[value])
668
+ ),
669
+ warn: Boolean(value && flag(self.flags, "WARN", self.data[value]))
670
+ };
671
+ }
672
+ }
673
+ });
674
+
675
+ // node_modules/nspell/lib/util/apply.js
676
+ var require_apply = __commonJS({
677
+ "node_modules/nspell/lib/util/apply.js"(exports, module) {
678
+ "use strict";
679
+ module.exports = apply;
680
+ function apply(value, rule, rules, words) {
681
+ var index = -1;
682
+ var entry;
683
+ var next;
684
+ var continuationRule;
685
+ var continuation;
686
+ var position;
687
+ while (++index < rule.entries.length) {
688
+ entry = rule.entries[index];
689
+ continuation = entry.continuation;
690
+ position = -1;
691
+ if (!entry.match || entry.match.test(value)) {
692
+ next = entry.remove ? value.replace(entry.remove, "") : value;
693
+ next = rule.type === "SFX" ? next + entry.add : entry.add + next;
694
+ words.push(next);
695
+ if (continuation && continuation.length) {
696
+ while (++position < continuation.length) {
697
+ continuationRule = rules[continuation[position]];
698
+ if (continuationRule) {
699
+ apply(next, continuationRule, rules, words);
700
+ }
701
+ }
702
+ }
703
+ }
704
+ }
705
+ return words;
706
+ }
707
+ }
708
+ });
709
+
710
+ // node_modules/nspell/lib/util/add.js
711
+ var require_add = __commonJS({
712
+ "node_modules/nspell/lib/util/add.js"(exports, module) {
713
+ "use strict";
714
+ var apply = require_apply();
715
+ module.exports = add2;
716
+ var push = [].push;
717
+ var NO_RULES = [];
718
+ function addRules(dict, word, rules) {
719
+ var curr = dict[word];
720
+ if (word in dict) {
721
+ if (curr === NO_RULES) {
722
+ dict[word] = rules.concat();
723
+ } else {
724
+ push.apply(curr, rules);
725
+ }
726
+ } else {
727
+ dict[word] = rules.concat();
728
+ }
729
+ }
730
+ function add2(dict, word, codes, options) {
731
+ var position = -1;
732
+ var rule;
733
+ var offset;
734
+ var subposition;
735
+ var suboffset;
736
+ var combined;
737
+ var newWords;
738
+ var otherNewWords;
739
+ if (!("NEEDAFFIX" in options.flags) || codes.indexOf(options.flags.NEEDAFFIX) < 0) {
740
+ addRules(dict, word, codes);
741
+ }
742
+ while (++position < codes.length) {
743
+ rule = options.rules[codes[position]];
744
+ if (codes[position] in options.compoundRuleCodes) {
745
+ options.compoundRuleCodes[codes[position]].push(word);
746
+ }
747
+ if (rule) {
748
+ newWords = apply(word, rule, options.rules, []);
749
+ offset = -1;
750
+ while (++offset < newWords.length) {
751
+ if (!(newWords[offset] in dict)) {
752
+ dict[newWords[offset]] = NO_RULES;
753
+ }
754
+ if (rule.combineable) {
755
+ subposition = position;
756
+ while (++subposition < codes.length) {
757
+ combined = options.rules[codes[subposition]];
758
+ if (combined && combined.combineable && rule.type !== combined.type) {
759
+ otherNewWords = apply(
760
+ newWords[offset],
761
+ combined,
762
+ options.rules,
763
+ []
764
+ );
765
+ suboffset = -1;
766
+ while (++suboffset < otherNewWords.length) {
767
+ if (!(otherNewWords[suboffset] in dict)) {
768
+ dict[otherNewWords[suboffset]] = NO_RULES;
769
+ }
770
+ }
771
+ }
772
+ }
773
+ }
774
+ }
775
+ }
776
+ }
777
+ }
778
+ }
779
+ });
780
+
781
+ // node_modules/nspell/lib/add.js
782
+ var require_add2 = __commonJS({
783
+ "node_modules/nspell/lib/add.js"(exports, module) {
784
+ "use strict";
785
+ var push = require_add();
786
+ module.exports = add2;
787
+ var NO_CODES = [];
788
+ function add2(value, model) {
789
+ var self = this;
790
+ push(self.data, value, self.data[model] || NO_CODES, self);
791
+ return self;
792
+ }
793
+ }
794
+ });
795
+
796
+ // node_modules/nspell/lib/remove.js
797
+ var require_remove = __commonJS({
798
+ "node_modules/nspell/lib/remove.js"(exports, module) {
799
+ "use strict";
800
+ module.exports = remove2;
801
+ function remove2(value) {
802
+ var self = this;
803
+ delete self.data[value];
804
+ return self;
805
+ }
806
+ }
807
+ });
808
+
809
+ // node_modules/nspell/lib/word-characters.js
810
+ var require_word_characters = __commonJS({
811
+ "node_modules/nspell/lib/word-characters.js"(exports, module) {
812
+ "use strict";
813
+ module.exports = wordCharacters;
814
+ function wordCharacters() {
815
+ return this.flags.WORDCHARS || null;
816
+ }
817
+ }
818
+ });
819
+
820
+ // node_modules/nspell/lib/util/dictionary.js
821
+ var require_dictionary = __commonJS({
822
+ "node_modules/nspell/lib/util/dictionary.js"(exports, module) {
823
+ "use strict";
824
+ var parseCodes = require_rule_codes();
825
+ var add2 = require_add();
826
+ module.exports = parse;
827
+ var whiteSpaceExpression = /\s/g;
828
+ function parse(buf, options, dict) {
829
+ var value = buf.toString("utf8");
830
+ var last = value.indexOf("\n") + 1;
831
+ var index = value.indexOf("\n", last);
832
+ while (index > -1) {
833
+ if (value.charCodeAt(last) !== 9) {
834
+ parseLine(value.slice(last, index), options, dict);
835
+ }
836
+ last = index + 1;
837
+ index = value.indexOf("\n", last);
838
+ }
839
+ parseLine(value.slice(last), options, dict);
840
+ }
841
+ function parseLine(line, options, dict) {
842
+ var slashOffset = line.indexOf("/");
843
+ var hashOffset = line.indexOf("#");
844
+ var codes = "";
845
+ var word;
846
+ var result;
847
+ while (slashOffset > -1 && line.charCodeAt(slashOffset - 1) === 92) {
848
+ line = line.slice(0, slashOffset - 1) + line.slice(slashOffset);
849
+ slashOffset = line.indexOf("/", slashOffset);
850
+ }
851
+ if (hashOffset > -1) {
852
+ if (slashOffset > -1 && slashOffset < hashOffset) {
853
+ word = line.slice(0, slashOffset);
854
+ whiteSpaceExpression.lastIndex = slashOffset + 1;
855
+ result = whiteSpaceExpression.exec(line);
856
+ codes = line.slice(slashOffset + 1, result ? result.index : void 0);
857
+ } else {
858
+ word = line.slice(0, hashOffset);
859
+ }
860
+ } else if (slashOffset > -1) {
861
+ word = line.slice(0, slashOffset);
862
+ codes = line.slice(slashOffset + 1);
863
+ } else {
864
+ word = line;
865
+ }
866
+ word = word.trim();
867
+ if (word) {
868
+ add2(dict, word, parseCodes(options.flags, codes.trim()), options);
869
+ }
870
+ }
871
+ }
872
+ });
873
+
874
+ // node_modules/nspell/lib/dictionary.js
875
+ var require_dictionary2 = __commonJS({
876
+ "node_modules/nspell/lib/dictionary.js"(exports, module) {
877
+ "use strict";
878
+ var parse = require_dictionary();
879
+ module.exports = add2;
880
+ function add2(buf) {
881
+ var self = this;
882
+ var index = -1;
883
+ var rule;
884
+ var source;
885
+ var character;
886
+ var offset;
887
+ parse(buf, self, self.data);
888
+ while (++index < self.compoundRules.length) {
889
+ rule = self.compoundRules[index];
890
+ source = "";
891
+ offset = -1;
892
+ while (++offset < rule.length) {
893
+ character = rule.charAt(offset);
894
+ source += self.compoundRuleCodes[character].length ? "(?:" + self.compoundRuleCodes[character].join("|") + ")" : character;
895
+ }
896
+ self.compoundRules[index] = new RegExp(source, "i");
897
+ }
898
+ return self;
899
+ }
900
+ }
901
+ });
902
+
903
+ // node_modules/nspell/lib/personal.js
904
+ var require_personal = __commonJS({
905
+ "node_modules/nspell/lib/personal.js"(exports, module) {
906
+ "use strict";
907
+ module.exports = add2;
908
+ function add2(buf) {
909
+ var self = this;
910
+ var lines = buf.toString("utf8").split("\n");
911
+ var index = -1;
912
+ var line;
913
+ var forbidden;
914
+ var word;
915
+ var flag;
916
+ if (self.flags.FORBIDDENWORD === void 0) self.flags.FORBIDDENWORD = false;
917
+ flag = self.flags.FORBIDDENWORD;
918
+ while (++index < lines.length) {
919
+ line = lines[index].trim();
920
+ if (!line) {
921
+ continue;
922
+ }
923
+ line = line.split("/");
924
+ word = line[0];
925
+ forbidden = word.charAt(0) === "*";
926
+ if (forbidden) {
927
+ word = word.slice(1);
928
+ }
929
+ self.add(word, line[1]);
930
+ if (forbidden) {
931
+ self.data[word].push(flag);
932
+ }
933
+ }
934
+ return self;
935
+ }
936
+ }
937
+ });
938
+
939
+ // node_modules/nspell/lib/index.js
940
+ var require_lib = __commonJS({
941
+ "node_modules/nspell/lib/index.js"(exports, module) {
942
+ "use strict";
943
+ var buffer = require_is_buffer();
944
+ var affix = require_affix();
945
+ module.exports = NSpell;
946
+ var proto = NSpell.prototype;
947
+ proto.correct = require_correct();
948
+ proto.suggest = require_suggest();
949
+ proto.spell = require_spell();
950
+ proto.add = require_add2();
951
+ proto.remove = require_remove();
952
+ proto.wordCharacters = require_word_characters();
953
+ proto.dictionary = require_dictionary2();
954
+ proto.personal = require_personal();
955
+ function NSpell(aff, dic) {
956
+ var index = -1;
957
+ var dictionaries;
958
+ if (!(this instanceof NSpell)) {
959
+ return new NSpell(aff, dic);
960
+ }
961
+ if (typeof aff === "string" || buffer(aff)) {
962
+ if (typeof dic === "string" || buffer(dic)) {
963
+ dictionaries = [{ dic }];
964
+ }
965
+ } else if (aff) {
966
+ if ("length" in aff) {
967
+ dictionaries = aff;
968
+ aff = aff[0] && aff[0].aff;
969
+ } else {
970
+ if (aff.dic) {
971
+ dictionaries = [aff];
972
+ }
973
+ aff = aff.aff;
974
+ }
975
+ }
976
+ if (!aff) {
977
+ throw new Error("Missing `aff` in dictionary");
978
+ }
979
+ aff = affix(aff);
980
+ this.data = /* @__PURE__ */ Object.create(null);
981
+ this.compoundRuleCodes = aff.compoundRuleCodes;
982
+ this.replacementTable = aff.replacementTable;
983
+ this.conversion = aff.conversion;
984
+ this.compoundRules = aff.compoundRules;
985
+ this.rules = aff.rules;
986
+ this.flags = aff.flags;
987
+ if (dictionaries) {
988
+ while (++index < dictionaries.length) {
989
+ if (dictionaries[index].dic) {
990
+ this.dictionary(dictionaries[index].dic);
991
+ }
992
+ }
993
+ }
994
+ }
995
+ }
996
+ });
997
+
2
998
  // node_modules/@marijn/find-cluster-break/src/index.js
3
999
  var rangeFrom = [];
4
1000
  var rangeTo = [];
@@ -16935,6 +17931,20 @@
16935
17931
  return ["pending", "applied", "kept"].includes(edit.state) && Number.isInteger(edit.from) && Number.isInteger(edit.to) && edit.from >= 0 && edit.to >= edit.from && edit.to <= document2.length && document2.slice(edit.from, edit.to) === expected;
16936
17932
  }
16937
17933
 
17934
+ // spellcheck.js
17935
+ var import_nspell = __toESM(require_lib(), 1);
17936
+ var cached = null;
17937
+ function loadSpellDictionary() {
17938
+ return cached ??= Promise.all([
17939
+ fetch("/dictionary/en.aff").then((res) => res.text()),
17940
+ fetch("/dictionary/en.dic").then((res) => res.text())
17941
+ ]).then(([aff, dic]) => (0, import_nspell.default)(aff, dic));
17942
+ }
17943
+ function spellingMarkers(text, spell) {
17944
+ if (!spell || !isLatinScript(text)) return [];
17945
+ return [...text.matchAll(WORD_RE)].filter((match) => match[0].length > 1 && !/^[A-Z]+$/.test(match[0]) && !spell.correct(match[0])).map((match) => ({ from: match.index, to: match.index + match[0].length, quote: match[0] }));
17946
+ }
17947
+
16938
17948
  // node_modules/@codemirror/search/dist/index.js
16939
17949
  var basicNormalize = typeof String.prototype.normalize == "function" ? (x) => x.normalize("NFKD") : (x) => x;
16940
17950
  var SearchCursor = class {
@@ -17127,21 +18137,21 @@
17127
18137
  return this.from + this.text.length;
17128
18138
  }
17129
18139
  static get(doc2, from, to) {
17130
- let cached = flattened.get(doc2);
17131
- if (!cached || cached.from >= to || cached.to <= from) {
18140
+ let cached2 = flattened.get(doc2);
18141
+ if (!cached2 || cached2.from >= to || cached2.to <= from) {
17132
18142
  let flat = new _FlattenedDoc(from, doc2.sliceString(from, to));
17133
18143
  flattened.set(doc2, flat);
17134
18144
  return flat;
17135
18145
  }
17136
- if (cached.from == from && cached.to == to)
17137
- return cached;
17138
- let { text, from: cachedFrom } = cached;
18146
+ if (cached2.from == from && cached2.to == to)
18147
+ return cached2;
18148
+ let { text, from: cachedFrom } = cached2;
17139
18149
  if (cachedFrom > from) {
17140
18150
  text = doc2.sliceString(from, cachedFrom) + text;
17141
18151
  cachedFrom = from;
17142
18152
  }
17143
- if (cached.to < to)
17144
- text += doc2.sliceString(cached.to, to);
18153
+ if (cached2.to < to)
18154
+ text += doc2.sliceString(cached2.to, to);
17145
18155
  flattened.set(doc2, new _FlattenedDoc(cachedFrom, text));
17146
18156
  return new _FlattenedDoc(from, text.slice(from - cachedFrom, to - cachedFrom));
17147
18157
  }
@@ -18165,8 +19175,8 @@
18165
19175
  empty2.hidden = matched.length > 0;
18166
19176
  if (count) count.textContent = !state.options.length ? "" : matched.length > shown.length ? `Showing ${shown.length} of ${matched.length}` : "";
18167
19177
  }
18168
- function reposition() {
18169
- if (!popover.matches(":popover-open")) return;
19178
+ function reposition(force) {
19179
+ if (!force && !popover.matches(":popover-open")) return;
18170
19180
  const box = trigger.getBoundingClientRect();
18171
19181
  const width = Math.max(box.width, 260);
18172
19182
  popover.style.left = `${Math.min(box.left, window.innerWidth - width - 8)}px`;
@@ -18174,7 +19184,9 @@
18174
19184
  popover.style.width = `${width}px`;
18175
19185
  }
18176
19186
  state.reposition = reposition;
18177
- trigger.addEventListener("click", reposition);
19187
+ popover.addEventListener("beforetoggle", (event) => {
19188
+ if (event.newState === "open") reposition(true);
19189
+ });
18178
19190
  popover.addEventListener("toggle", (event) => {
18179
19191
  if (event.newState !== "open") return;
18180
19192
  search.value = "";
@@ -18486,6 +19498,24 @@
18486
19498
  attributes: { "data-style-from": String(marker.from), title: "Possible stock wording \u2014 click to explore" }
18487
19499
  }).range(marker.from, marker.to)), true))
18488
19500
  });
19501
+ var spellChecker = null;
19502
+ var refreshSpellFx = StateEffect.define();
19503
+ function visibleSpellMarkers(text) {
19504
+ return spellingMarkers(text, spellChecker);
19505
+ }
19506
+ var spellMarkerField = StateField.define({
19507
+ create: (state) => visibleSpellMarkers(state.doc.toString()),
19508
+ update: (markers, tr) => {
19509
+ for (const effect of tr.effects) {
19510
+ if (effect.is(refreshSpellFx)) return visibleSpellMarkers(tr.newDoc.toString());
19511
+ }
19512
+ return tr.docChanged ? visibleSpellMarkers(tr.newDoc.toString()) : markers;
19513
+ },
19514
+ provide: (field) => EditorView.decorations.from(field, (markers) => Decoration.set(markers.map((marker) => Decoration.mark({
19515
+ class: "cm-spell-marker",
19516
+ attributes: { "data-spell-from": String(marker.from), title: "Possible misspelling \u2014 click for suggestions" }
19517
+ }).range(marker.from, marker.to)), true))
19518
+ });
18489
19519
  var setReviewFx = StateEffect.define();
18490
19520
  var addReviewFx = StateEffect.define();
18491
19521
  var dropReviewFx = StateEffect.define();
@@ -18536,6 +19566,33 @@
18536
19566
  },
18537
19567
  provide: (field) => EditorView.decorations.from(field)
18538
19568
  });
19569
+ var setVariantTargetFx = StateEffect.define();
19570
+ var variantTargetField = StateField.define({
19571
+ create: () => Decoration.none,
19572
+ update(decorations2, tr) {
19573
+ for (const effect of tr.effects) {
19574
+ if (effect.is(setVariantTargetFx)) {
19575
+ return effect.value ? Decoration.set([Decoration.mark({ class: "cm-variant-target" }).range(effect.value.from, effect.value.to)]) : Decoration.none;
19576
+ }
19577
+ }
19578
+ if (!tr.docChanged) return decorations2;
19579
+ const edited = [];
19580
+ tr.changes.iterChangedRanges((_fromA, _toA, fromB, toB) => edited.push([fromB, toB]));
19581
+ return decorations2.map(tr.changes).update({
19582
+ filter: (from, to) => !edited.some(([a, b]) => a < to && b > from)
19583
+ });
19584
+ },
19585
+ provide: (field) => EditorView.decorations.from(field)
19586
+ });
19587
+ function markVariantTarget(target) {
19588
+ try {
19589
+ workView.dispatch({ effects: setVariantTargetFx.of(target ? { from: target.from, to: target.to } : null) });
19590
+ } catch {
19591
+ }
19592
+ }
19593
+ function syncVariantTarget() {
19594
+ if (!document.querySelector(".chat-variants:not(.is-spent):not(.is-loading)")) markVariantTarget(null);
19595
+ }
18539
19596
  function findingRange(id) {
18540
19597
  if (id === -1) return localFinding && chatEditMatches(localFinding, workView.state.doc.toString()) ? { from: localFinding.from, to: localFinding.to } : null;
18541
19598
  let found = null;
@@ -18594,8 +19651,6 @@
18594
19651
  if (localFinding) keepStyleMarker(localFinding);
18595
19652
  return;
18596
19653
  }
18597
- clearTimeout(appliedHideTimers.get(id));
18598
- appliedHideTimers.delete(id);
18599
19654
  const finding = reviewFindings.find((item) => item.id === id);
18600
19655
  const document2 = workView.state.doc.toString();
18601
19656
  const range = findingRange(id);
@@ -18608,7 +19663,7 @@
18608
19663
  if (card) {
18609
19664
  railAnchors.delete(card);
18610
19665
  railHomes.delete(card);
18611
- card.remove();
19666
+ fadeRemove(card);
18612
19667
  }
18613
19668
  findingCards.delete(id);
18614
19669
  workView.dispatch({ effects: dropReviewFx.of(id) });
@@ -18617,6 +19672,7 @@
18617
19672
  else clearPlainPreview();
18618
19673
  refocusAfterRemoval(id);
18619
19674
  saveFindings();
19675
+ syncVariantTarget();
18620
19676
  syncReviewLabel();
18621
19677
  workView.focus();
18622
19678
  if (decision) showEditFeedback("Kept as is", () => {
@@ -18649,6 +19705,7 @@
18649
19705
  reviewFindings = [];
18650
19706
  checkedSentences.clear();
18651
19707
  workView.dispatch({ effects: setReviewFx.of([]) });
19708
+ markVariantTarget(null);
18652
19709
  docStorage.removeItem("wa-findings");
18653
19710
  syncReviewLabel();
18654
19711
  }
@@ -18765,16 +19822,16 @@
18765
19822
  updateBadge.title = command2;
18766
19823
  });
18767
19824
  async function refreshUpdate({ force = false } = {}) {
18768
- let cached = null;
19825
+ let cached2 = null;
18769
19826
  try {
18770
- cached = JSON.parse(localStorage.getItem("wa-update") || "null");
19827
+ cached2 = JSON.parse(localStorage.getItem("wa-update") || "null");
18771
19828
  } catch {
18772
19829
  }
18773
- const check = updateCheckOn && (force || !(cached && Date.now() - cached.at < UPDATE_INTERVAL));
19830
+ const check = updateCheckOn && (force || !(cached2 && Date.now() - cached2.at < UPDATE_INTERVAL));
18774
19831
  try {
18775
19832
  const info = await api(`/api/version${check ? "?check=1" : ""}`);
18776
19833
  if (info.latest) localStorage.setItem("wa-update", JSON.stringify({ latest: info.latest, at: Date.now() }));
18777
- renderUpdate({ ...info, latest: info.latest ?? (updateCheckOn ? cached?.latest : void 0) });
19834
+ renderUpdate({ ...info, latest: info.latest ?? (updateCheckOn ? cached2?.latest : void 0) });
18778
19835
  } catch {
18779
19836
  }
18780
19837
  }
@@ -18885,7 +19942,7 @@
18885
19942
  if (card) {
18886
19943
  railAnchors.delete(card);
18887
19944
  railHomes.delete(card);
18888
- card.remove();
19945
+ fadeRemove(card);
18889
19946
  findingCards.delete(-1);
18890
19947
  }
18891
19948
  localFinding = null;
@@ -18987,7 +20044,7 @@
18987
20044
  suggestTimer = null;
18988
20045
  const view = workView;
18989
20046
  const state = view.state;
18990
- if (state.readOnly || loadingDocument || savePaused || !view.hasFocus || document.hidden || view.composing) return;
20047
+ if (state.readOnly || loadingDocument || savePaused || !manual && !view.hasFocus || document.hidden || view.composing) return;
18991
20048
  if (!manual && !autoSuggestOn) return;
18992
20049
  if (!atParagraphEnd(state)) {
18993
20050
  if (manual) setReviewStatus("Place the caret at the end of a paragraph to request a continuation.", void 0, "suggest");
@@ -19003,11 +20060,11 @@
19003
20060
  if (/^\s*\/idea(?:\s|$)/i.test(line.text)) return;
19004
20061
  const version = suggestVersion;
19005
20062
  if (manual && !await ensureAgent()) return;
19006
- if (version !== suggestVersion || !view.hasFocus || !currentAgent()) return;
20063
+ if (version !== suggestVersion || !manual && !view.hasFocus || !currentAgent()) return;
19007
20064
  const agent = currentAgent();
19008
20065
  const job = startJob({ background: !manual });
19009
20066
  suggestAbort = job;
19010
- const isCurrent = () => suggestAbort === job && !job.signal.aborted && version === suggestVersion && !loadingDocument && !savePaused && !document.hidden && view.hasFocus && !view.composing && !view.state.readOnly && (manual || autoSuggestOn) && view.state.doc === state.doc && view.state.selection.eq(state.selection) && JSON.stringify(currentAgent()) === JSON.stringify(agent);
20067
+ const isCurrent = () => suggestAbort === job && !job.signal.aborted && version === suggestVersion && !loadingDocument && !savePaused && !document.hidden && (manual || view.hasFocus) && !view.composing && !view.state.readOnly && (manual || autoSuggestOn) && view.state.doc === state.doc && view.state.selection.eq(state.selection) && JSON.stringify(currentAgent()) === JSON.stringify(agent);
19011
20068
  if (manual) setReviewStatus("Suggesting\u2026", void 0, "suggest");
19012
20069
  try {
19013
20070
  const res = await fetch("/suggest", {
@@ -19016,7 +20073,8 @@
19016
20073
  body: JSON.stringify({
19017
20074
  document: doc2,
19018
20075
  cursor: pos,
19019
- agent
20076
+ agent,
20077
+ manual
19020
20078
  }),
19021
20079
  signal: job.signal
19022
20080
  });
@@ -19098,9 +20156,38 @@
19098
20156
  return node;
19099
20157
  }
19100
20158
  var chatAtBottom = () => chatStream.scrollHeight - chatStream.scrollTop - chatStream.clientHeight < 48;
19101
- function chatScroll(stick) {
19102
- chatStream.scrollTop = chatStream.scrollHeight;
20159
+ var reduceMotion = () => window.matchMedia("(prefers-reduced-motion: reduce)").matches;
20160
+ function chatScroll(stick, smooth = false) {
19103
20161
  void stick;
20162
+ const top2 = chatStream.scrollHeight;
20163
+ if (smooth && !reduceMotion()) {
20164
+ try {
20165
+ chatStream.scrollTo({ top: top2, behavior: "smooth" });
20166
+ return;
20167
+ } catch {
20168
+ }
20169
+ }
20170
+ chatStream.scrollTop = top2;
20171
+ }
20172
+ function scrollToNode(node, block2 = "nearest") {
20173
+ if (!node?.isConnected) return;
20174
+ try {
20175
+ node.scrollIntoView({ block: block2, behavior: reduceMotion() ? "auto" : "smooth" });
20176
+ } catch {
20177
+ try {
20178
+ node.scrollIntoView();
20179
+ } catch {
20180
+ }
20181
+ }
20182
+ }
20183
+ function fadeRemove(node, ms = 160) {
20184
+ if (!node?.isConnected) return;
20185
+ if (reduceMotion()) {
20186
+ node.remove();
20187
+ return;
20188
+ }
20189
+ node.classList.add("is-leaving");
20190
+ setTimeout(() => node.remove(), ms);
19104
20191
  }
19105
20192
  function saveChat() {
19106
20193
  docStorage.setItem("wa-chat", JSON.stringify({ document: workView.state.doc.toString(), messages: chatHistory.slice(-20) }));
@@ -19176,7 +20263,12 @@ ${message.content}`;
19176
20263
  syncChatEditCards();
19177
20264
  undo2.focus();
19178
20265
  });
19179
- const undo2 = button("Undo", () => {
20266
+ const undoFoot = chatEl("div", "variant-undo-row");
20267
+ undoFoot.hidden = true;
20268
+ const undo2 = chatEl("button", "chat-again", "Undo");
20269
+ undo2.type = "button";
20270
+ undo2.title = "Put the passage back as it was";
20271
+ undo2.addEventListener("click", () => {
19180
20272
  if (edit.state === "kept") {
19181
20273
  edit.state = "pending";
19182
20274
  saveChat();
@@ -19184,6 +20276,7 @@ ${message.content}`;
19184
20276
  apply.focus();
19185
20277
  } else changeChatEdit(edit, true);
19186
20278
  });
20279
+ undoFoot.append(undo2);
19187
20280
  const refresh = button("Request new edit", () => {
19188
20281
  if (edit.state === "unlocated") {
19189
20282
  chatInput.value = `Prepare an edit for the passage I select. Previous suggestion: ${edit.label}`;
@@ -19196,24 +20289,28 @@ ${message.content}`;
19196
20289
  const status = chatEl("p", "chat-edit-status");
19197
20290
  status.setAttribute("role", "status");
19198
20291
  actions.prepend(status);
19199
- card.append(actions);
19200
- chatEditCards.set(edit, { card, preview, apply, keep, undo: undo2, refresh, status });
20292
+ card.append(actions, undoFoot);
20293
+ chatEditCards.set(edit, { card, actions, preview, apply, keep, undo: undo2, undoFoot, refresh, status });
19201
20294
  syncChatEditCard(edit, chatEditCards.get(edit));
19202
20295
  return card;
19203
20296
  }
19204
20297
  function syncChatEditCard(edit, ui) {
19205
20298
  const valid = chatEditMatches(edit, workView.state.doc.toString());
19206
20299
  const pending = edit.state === "pending" && valid;
20300
+ const showUndo = valid && ["applied", "kept"].includes(edit.state);
20301
+ const appliedNow = valid && edit.state === "applied";
19207
20302
  ui.preview.hidden = ui.apply.hidden = ui.keep.hidden = !pending;
19208
- ui.undo.hidden = !valid || !["applied", "kept"].includes(edit.state);
20303
+ ui.undo.hidden = !showUndo;
20304
+ ui.undoFoot.hidden = !showUndo;
20305
+ ui.card.classList.toggle("is-applied", appliedNow);
20306
+ ui.actions.hidden = appliedNow;
19209
20307
  ui.refresh.hidden = valid;
19210
20308
  ui.refresh.textContent = edit.state === "unlocated" ? "Choose passage" : "Request new edit";
19211
20309
  ui.preview.textContent = plainPreview?.edit === edit ? "End preview" : "Preview";
19212
20310
  ui.preview.setAttribute("aria-pressed", String(plainPreview?.edit === edit));
19213
20311
  ui.apply.disabled = ui.undo.disabled = workView.state.readOnly || loadingDocument || savePaused;
19214
- ui.status.textContent = !valid ? edit.state === "unlocated" ? "Select the intended passage and request this edit again." : "" : edit.state === "applied" ? "Applied" : edit.state === "kept" ? "Original kept" : "";
20312
+ ui.status.textContent = !valid ? edit.state === "unlocated" ? "Select the intended passage and request this edit again." : "" : edit.state === "kept" ? "Original kept" : "";
19215
20313
  ui.status.hidden = !ui.status.textContent;
19216
- ui.status.classList.toggle("is-ok", valid && edit.state === "applied");
19217
20314
  }
19218
20315
  function syncChatEditCards() {
19219
20316
  for (const [edit, ui] of chatEditCards) {
@@ -19249,35 +20346,11 @@ ${message.content}`;
19249
20346
  var rail = document.getElementById("rail");
19250
20347
  var railAnchors = /* @__PURE__ */ new Map();
19251
20348
  var railHomes = /* @__PURE__ */ new Map();
19252
- var RAIL_WIDTH = 300;
19253
- var RAIL_GUTTER = 24;
19254
20349
  var railOn = false;
19255
20350
  function syncRail() {
19256
- const margin = parseFloat(getComputedStyle(workView.contentDOM).paddingRight) || 0;
19257
- const fits = margin >= RAIL_WIDTH + RAIL_GUTTER;
19258
- if (fits) {
19259
- const box = workView.contentDOM.getBoundingClientRect();
19260
- const pane = document.getElementById("pane-work").getBoundingClientRect();
19261
- rail.style.left = `${box.right - margin - pane.left + RAIL_GUTTER}px`;
19262
- }
19263
- if (fits === railOn) return;
19264
- railOn = fits;
19265
- rail.hidden = !fits;
19266
- const focused = focusedFindingId;
19267
- for (const node of railAnchors.keys()) {
19268
- if (!node.isConnected) continue;
19269
- node.style.top = "";
19270
- node.style.visibility = "";
19271
- if (fits) {
19272
- node.style.display = "";
19273
- rail.append(node);
19274
- } else {
19275
- placeHome(node);
19276
- const nodeId = Number(node.dataset?.findingId);
19277
- node.style.display = focused === null || nodeId === focused ? "" : "none";
19278
- }
19279
- }
19280
- if (!fits) chatScroll(true);
20351
+ railOn = false;
20352
+ if (!rail.hidden) rail.hidden = true;
20353
+ return;
19281
20354
  }
19282
20355
  function placeHome(node) {
19283
20356
  const home = railHomes.get(node) ?? chatStream;
@@ -19287,9 +20360,7 @@ ${message.content}`;
19287
20360
  function railAdd(node, anchor, home = chatStream) {
19288
20361
  railAnchors.set(node, anchor);
19289
20362
  railHomes.set(node, home);
19290
- syncRail();
19291
- if (railOn) rail.append(node);
19292
- else placeHome(node);
20363
+ placeHome(node);
19293
20364
  layoutRail();
19294
20365
  return node;
19295
20366
  }
@@ -19320,19 +20391,11 @@ ${message.content}`;
19320
20391
  chatClear.title = open ? "Close \u2014 nothing is discarded" : "Show the conversation";
19321
20392
  if (open) {
19322
20393
  chatClear.hidden = false;
19323
- chatScroll(true);
20394
+ chatScroll(true, true);
19324
20395
  return;
19325
20396
  }
19326
20397
  chatStream.hidden = true;
19327
- detach();
19328
- for (const card of findingCards.values()) {
19329
- const kept = card.querySelector(".chat-variants:has(.variant-card.is-applied)");
19330
- if (kept) streamAppend(kept);
19331
- }
19332
- discardFindingCards();
19333
- for (const stale of document.querySelectorAll(".chat-variants")) {
19334
- if (!stale.querySelector(".variant-card.is-applied")) stale.remove();
19335
- }
20398
+ clearPlainPreview();
19336
20399
  chatClear.hidden = chatStream.children.length === 0;
19337
20400
  layoutRail();
19338
20401
  }
@@ -19458,6 +20521,35 @@ ${message.content}`;
19458
20521
  markBubble.replaceChildren(actions);
19459
20522
  placeBubble(x, y);
19460
20523
  }
20524
+ function showContinueBubble(x, y) {
20525
+ bubbleMode = "continue";
20526
+ const actions = chatEl("div", "mark-bubble-actions");
20527
+ actions.append(bubbleButton("Continue", () => {
20528
+ workView.focus();
20529
+ cancelSuggestion();
20530
+ ghostClear(workView);
20531
+ void suggestFetch(true);
20532
+ }));
20533
+ markBubble.replaceChildren(actions);
20534
+ placeBubble(x, y);
20535
+ }
20536
+ function showSpellBubble(marker, x, y) {
20537
+ bubbleMode = "spell";
20538
+ const actions = chatEl("div", "mark-bubble-actions");
20539
+ const replace2 = (suggestion) => {
20540
+ if (workView.state.sliceDoc(marker.from, marker.to) !== marker.quote) return;
20541
+ workView.dispatch({ changes: { from: marker.from, to: marker.to, insert: suggestion }, userEvent: "input.spellfix" });
20542
+ };
20543
+ for (const suggestion of (spellChecker?.suggest(marker.quote) ?? []).slice(0, 5)) {
20544
+ actions.append(bubbleButton(suggestion, () => replace2(suggestion)));
20545
+ }
20546
+ actions.append(bubbleButton("Ignore", () => {
20547
+ spellChecker?.add(marker.quote);
20548
+ workView.dispatch({ effects: refreshSpellFx.of(null) });
20549
+ }));
20550
+ markBubble.replaceChildren(actions);
20551
+ placeBubble(x, y);
20552
+ }
19461
20553
  var PlainVariantWidget = class extends WidgetType {
19462
20554
  constructor(text, block2) {
19463
20555
  super();
@@ -19533,6 +20625,7 @@ ${message.content}`;
19533
20625
  }
19534
20626
  function applyText(text, target, wrap = null, card = null) {
19535
20627
  if (!replacementTarget(workView.state.doc.toString(), target) || workView.state.readOnly) {
20628
+ markVariantTarget(null);
19536
20629
  chatAdd(chatEl("div", "chat-error", "The draft changed since this answer. Select the passage and request new options."));
19537
20630
  return;
19538
20631
  }
@@ -19544,6 +20637,7 @@ ${message.content}`;
19544
20637
  selection: { anchor: range.from + text.length },
19545
20638
  effects: [
19546
20639
  setPlainVariantFx.of(null),
20640
+ setVariantTargetFx.of(null),
19547
20641
  setFlashFx.of({ from: range.from, to: range.from + text.length })
19548
20642
  ]
19549
20643
  });
@@ -19564,77 +20658,41 @@ ${message.content}`;
19564
20658
  if (card) card.classList.add("is-applied");
19565
20659
  if (wrap) {
19566
20660
  wrap.classList.add("is-spent");
19567
- for (const sibling of wrap.querySelectorAll(".variant-card")) {
19568
- if (sibling !== card) sibling.hidden = true;
19569
- }
19570
- const foot = wrap.querySelector(".variant-foot");
19571
- if (foot) foot.hidden = true;
19572
- const soloHint = wrap.querySelector(":scope > .variant-hint");
19573
- if (soloHint) soloHint.hidden = true;
19574
20661
  attachUndo(card, text, target);
19575
20662
  const hostCard = wrap.closest(".chat-card");
19576
20663
  if (hostCard && findingCards.has(Number(hostCard.dataset.findingId))) {
19577
- railAnchors.set(hostCard, () => target.from);
20664
+ const findingId = Number(hostCard.dataset.findingId);
20665
+ for (const sibling of wrap.querySelectorAll(".variant-card")) {
20666
+ if (sibling !== card) sibling.hidden = true;
20667
+ }
20668
+ const foot = wrap.querySelector(".variant-foot");
20669
+ if (foot) foot.hidden = true;
20670
+ const soloHint = wrap.querySelector(":scope > .variant-hint");
20671
+ if (soloHint) soloHint.hidden = true;
20672
+ hostCard.querySelector(".finding-actions")?.remove();
20673
+ railAnchors.delete(hostCard);
20674
+ railHomes.delete(hostCard);
20675
+ if (findingId === -1) localFinding = null;
20676
+ else {
20677
+ reviewFindings = reviewFindings.filter((finding) => finding.id !== findingId);
20678
+ try {
20679
+ workView.dispatch({ effects: dropReviewFx.of(findingId) });
20680
+ } catch {
20681
+ }
20682
+ if (focusedFindingId === findingId) {
20683
+ focusedFindingId = null;
20684
+ syncActiveCard();
20685
+ }
20686
+ }
20687
+ saveFindings();
20688
+ syncReviewLabel();
20689
+ syncStyleScore();
19578
20690
  layoutRail();
19579
- scheduleAppliedHide(hostCard, Number(hostCard.dataset.findingId));
19580
20691
  } else {
19581
20692
  setChatOpen(true);
19582
20693
  }
19583
20694
  }
19584
20695
  }
19585
- var APPLIED_HIDE_MS = 6e3;
19586
- var appliedHideTimers = /* @__PURE__ */ new Map();
19587
- function scheduleAppliedHide(hostCard, id) {
19588
- clearTimeout(appliedHideTimers.get(id));
19589
- appliedHideTimers.set(id, setTimeout(() => {
19590
- const card = findingCards.get(id);
19591
- if (!card || !card.isConnected) {
19592
- appliedHideTimers.delete(id);
19593
- return;
19594
- }
19595
- if (card.matches(":hover") || card.contains(document.activeElement)) {
19596
- scheduleAppliedHide(card, id);
19597
- return;
19598
- }
19599
- appliedHideTimers.delete(id);
19600
- resolveFinding(id);
19601
- }, APPLIED_HIDE_MS));
19602
- }
19603
- function resolveFinding(id) {
19604
- clearTimeout(appliedHideTimers.get(id));
19605
- appliedHideTimers.delete(id);
19606
- if (id === -1) {
19607
- removeLocalFinding();
19608
- saveFindings();
19609
- syncReviewLabel();
19610
- syncStyleScore();
19611
- updateFindingNav();
19612
- layoutRail();
19613
- return;
19614
- }
19615
- reviewFindings = reviewFindings.filter((finding) => finding.id !== id);
19616
- const card = findingCards.get(id);
19617
- if (card) {
19618
- railAnchors.delete(card);
19619
- railHomes.delete(card);
19620
- card.remove();
19621
- }
19622
- findingCards.delete(id);
19623
- try {
19624
- workView.dispatch({ effects: dropReviewFx.of(id) });
19625
- } catch {
19626
- }
19627
- if (focusedFindingId === id) {
19628
- focusedFindingId = null;
19629
- syncActiveCard();
19630
- }
19631
- saveFindings();
19632
- syncReviewLabel();
19633
- syncStyleScore();
19634
- updateFindingNav();
19635
- layoutRail();
19636
- if (chatStream.children.length === 0) setChatOpen(false);
19637
- }
19638
20696
  function attachUndo(card, text, target) {
19639
20697
  if (!card) return;
19640
20698
  let row = card.querySelector(".variant-undo-row");
@@ -19644,11 +20702,6 @@ ${message.content}`;
19644
20702
  undo3.type = "button";
19645
20703
  undo3.title = "Put the passage back as it was";
19646
20704
  undo3.addEventListener("click", () => {
19647
- const host = undo3.closest(".chat-card");
19648
- if (host?.dataset.findingId !== void 0 && host.dataset.findingId !== "") {
19649
- clearTimeout(appliedHideTimers.get(Number(host.dataset.findingId)));
19650
- appliedHideTimers.delete(Number(host.dataset.findingId));
19651
- }
19652
20705
  const at = Number(undo3.dataset.from);
19653
20706
  const { applied, previous } = undo3.dataset;
19654
20707
  if (!undo3.isConnected || undo3.dataset.stale || workView.state.sliceDoc(at, at + applied.length) !== applied) {
@@ -19660,13 +20713,14 @@ ${message.content}`;
19660
20713
  save();
19661
20714
  const spentWrap = undo3.closest(".chat-variants");
19662
20715
  const hostCard = undo3.closest(".chat-card");
19663
- spentWrap?.remove();
19664
- if (hostCard && hostCard.dataset.findingId && !hostCard.querySelector(".chat-variants")) {
20716
+ fadeRemove(spentWrap);
20717
+ syncVariantTarget();
20718
+ if (hostCard && hostCard.dataset.findingId && !hostCard.querySelector(".chat-variants:not(.is-leaving)")) {
19665
20719
  const hostId = Number(hostCard.dataset.findingId);
19666
20720
  railAnchors.delete(hostCard);
19667
20721
  railHomes.delete(hostCard);
19668
20722
  findingCards.delete(hostId);
19669
- hostCard.remove();
20723
+ fadeRemove(hostCard);
19670
20724
  refocusAfterRemoval(hostId);
19671
20725
  }
19672
20726
  layoutRail();
@@ -19698,13 +20752,8 @@ ${message.content}`;
19698
20752
  return findingRange(id)?.from ?? null;
19699
20753
  });
19700
20754
  }
19701
- if (!railOn && card.isConnected && card.parentElement === chatStream) {
19702
- card.style.display = focused === null || isFocused ? "" : "none";
19703
- } else {
19704
- card.style.display = "";
19705
- }
20755
+ card.style.display = "";
19706
20756
  }
19707
- updateFindingNav();
19708
20757
  layoutRail();
19709
20758
  }
19710
20759
  function refocusAfterRemoval(removedId) {
@@ -19719,19 +20768,6 @@ ${message.content}`;
19719
20768
  card.dataset.findingId = String(finding.id);
19720
20769
  card.addEventListener("mouseenter", () => markFor(finding.id)?.classList.add("is-hot"));
19721
20770
  card.addEventListener("mouseleave", () => markFor(finding.id)?.classList.remove("is-hot"));
19722
- const nav2 = chatEl("div", "finding-nav");
19723
- const prev = chatEl("button", "finding-nav-btn", "\u2039");
19724
- prev.type = "button";
19725
- prev.title = "Previous finding (Shift+F8)";
19726
- prev.setAttribute("aria-label", "Previous finding");
19727
- prev.addEventListener("click", () => jumpToNextFinding(-1));
19728
- const count = chatEl("span", "finding-nav-count", "");
19729
- const next = chatEl("button", "finding-nav-btn", "\u203A");
19730
- next.type = "button";
19731
- next.title = "Next finding (F8)";
19732
- next.setAttribute("aria-label", "Next finding");
19733
- next.addEventListener("click", () => jumpToNextFinding(1));
19734
- nav2.append(prev, count, next);
19735
20771
  const dismiss = chatEl("button", "chat-card-dismiss", "Keep as is");
19736
20772
  dismiss.type = "button";
19737
20773
  dismiss.title = "Keep this wording and dismiss the finding";
@@ -19741,12 +20777,10 @@ ${message.content}`;
19741
20777
  dismissFinding(finding.id);
19742
20778
  });
19743
20779
  card.append(
19744
- nav2,
19745
20780
  chatEl("strong", "", finding.pattern),
19746
20781
  chatEl("span", "", finding.reason),
19747
20782
  chatEl("small", "", finding.fix)
19748
20783
  );
19749
- if (finding.id === -1) nav2.hidden = true;
19750
20784
  const offer = chatEl("button", "chat-offer", "Options");
19751
20785
  offer.type = "button";
19752
20786
  offer.addEventListener("click", () => {
@@ -19775,18 +20809,8 @@ ${message.content}`;
19775
20809
  if (finding.id === -1) dismiss.textContent = "Keep here";
19776
20810
  const variantsBox = chatEl("div", "finding-variants");
19777
20811
  card.append(actions, variantsBox);
19778
- updateFindingNav();
19779
20812
  return card;
19780
20813
  }
19781
- function updateFindingNav() {
19782
- const live = reviewFindings.map((finding) => ({ finding, range: findingRange(finding.id) })).filter((item) => item.range).sort((a, b) => a.range.from - b.range.from || a.finding.id - b.finding.id);
19783
- for (const [id, card] of findingCards) {
19784
- const count = card.querySelector(".finding-nav-count");
19785
- if (!count) continue;
19786
- const index = live.findIndex((item) => item.finding.id === id);
19787
- count.textContent = live.length ? `${index >= 0 ? index + 1 : "\u2013"} of ${live.length}` : "";
19788
- }
19789
- }
19790
20814
  function variantCards(variants, instruction, target, card = null) {
19791
20815
  const doc2 = workView.state.doc.toString();
19792
20816
  const ordered = isLatinScript(doc2) ? variants.map((text) => ({ text, score: styleScore(doc2.slice(0, target.from) + text + doc2.slice(target.to)).score })).sort((a, b) => a.score - b.score).map((item) => item.text) : variants.slice();
@@ -19799,9 +20823,8 @@ ${message.content}`;
19799
20823
  item.tabIndex = 0;
19800
20824
  item.setAttribute("role", "button");
19801
20825
  item.setAttribute("aria-label", `Apply option ${index + 1}`);
19802
- const label = chatEl("div", "variant-label", `Option ${index + 1}`);
19803
20826
  const body = chatEl("div", "variant-text", text);
19804
- item.append(label, body);
20827
+ item.append(body);
19805
20828
  const previewIt = () => {
19806
20829
  if (wrap.classList.contains("is-spent")) return;
19807
20830
  if (!replacementTarget(workView.state.doc.toString(), target)) return;
@@ -19892,8 +20915,8 @@ ${message.content}`;
19892
20915
  layoutRail();
19893
20916
  }
19894
20917
  function retireStaleVariants(except) {
19895
- for (const stale of document.querySelectorAll(".chat-variants:not(.is-spent):not(.is-loading)")) {
19896
- if (stale !== except) stale.remove();
20918
+ for (const stale of document.querySelectorAll(".chat-variants:not(.is-spent):not(.is-loading):not(.is-leaving)")) {
20919
+ if (stale !== except) fadeRemove(stale);
19897
20920
  }
19898
20921
  layoutRail();
19899
20922
  }
@@ -19901,6 +20924,7 @@ ${message.content}`;
19901
20924
  const wrap = variantCards(variants, instruction, target);
19902
20925
  retireStaleVariants(wrap);
19903
20926
  chatAdd(wrap);
20927
+ markVariantTarget(target);
19904
20928
  try {
19905
20929
  workView.dispatch({ effects: EditorView.scrollIntoView(target.from, { y: "center" }) });
19906
20930
  } catch {
@@ -19916,6 +20940,7 @@ ${message.content}`;
19916
20940
  if (!await ensureAgent()) return;
19917
20941
  if (!replacementTarget(workView.state.doc.toString(), target)) return;
19918
20942
  clearPlainPreview();
20943
+ markVariantTarget(null);
19919
20944
  const inCard = !!(card && card.isConnected && card.querySelector(".finding-variants"));
19920
20945
  const box = inCard ? card.querySelector(".finding-variants") : null;
19921
20946
  if (inCard) {
@@ -19955,6 +20980,8 @@ ${message.content}`;
19955
20980
  done.textContent = "Options";
19956
20981
  }
19957
20982
  layoutRail();
20983
+ markVariantTarget(target);
20984
+ scrollToNode(wrap);
19958
20985
  try {
19959
20986
  workView.dispatch({ effects: EditorView.scrollIntoView(target.from, { y: "center" }) });
19960
20987
  } catch {
@@ -19967,21 +20994,25 @@ ${message.content}`;
19967
20994
  placeholder2.remove();
19968
20995
  chatAdd(wrap);
19969
20996
  }
20997
+ markVariantTarget(target);
19970
20998
  }
19971
20999
  } catch (error) {
19972
21000
  if (error.name === "AbortError") {
19973
21001
  placeholder2.remove();
19974
21002
  if (card) restoreFindingCard(card);
19975
21003
  else layoutRail();
21004
+ syncVariantTarget();
19976
21005
  return;
19977
21006
  }
19978
21007
  console.error("[/rewrite]", error);
19979
21008
  placeholder2.remove();
21009
+ syncVariantTarget();
19980
21010
  if (!inCard) restoreFindingCard(card);
19981
21011
  const err = errorCard(modelError(error), () => requestVariants(instruction, target, card));
19982
21012
  if (inCard) {
19983
21013
  box.append(err);
19984
21014
  restoreFindingCard(card);
21015
+ scrollToNode(err);
19985
21016
  } else chatAdd(err);
19986
21017
  } finally {
19987
21018
  finishJob(job);
@@ -20136,10 +21167,6 @@ Editing direction: ${activeFinding.fix}`,
20136
21167
  chatInput.closest(".chat-composer")?.classList.toggle("is-busy", busy);
20137
21168
  chatInput.placeholder = busy ? "Working\u2026" : basePlaceholder();
20138
21169
  }
20139
- document.getElementById("chat-rewrite").addEventListener("click", () => {
20140
- const range = attachedRange();
20141
- if (range) requestVariants(chatInput.value.trim() || "Make this passage clearer. Preserve meaning, facts and voice.");
20142
- });
20143
21170
  chatSendButton.addEventListener("click", () => {
20144
21171
  if ([...jobs].some((job) => !job.background)) {
20145
21172
  stopJobs();
@@ -20255,27 +21282,24 @@ Editing direction: ${activeFinding.fix}`,
20255
21282
  },
20256
21283
  // The attached passage. Survives losing focus, unlike a native selection —
20257
21284
  // that is the only job this mark has for a plain selection, which carries
20258
- // no marking of its own.
21285
+ // no marking of its own. A background wash: underlines belong to errors
21286
+ // (dictionary, typos) and are never used for working states.
20259
21287
  ".cm-attached": {
20260
21288
  background: "var(--attached-tint)",
20261
21289
  borderRadius: "3px",
20262
21290
  boxShadow: "0 0 0 2px var(--attached-tint)"
20263
21291
  },
20264
- // A finding already carries its own warm band. The attachment still needs a
20265
- // visible mark of its own — transparent here meant an open finding showed
20266
- // nothing of the working state — so it draws a blue underline over the band
20267
- // instead of a second fill. Tracking the live position is unchanged: that is
20268
- // what lets an edit end the attachment.
21292
+ // Over a finding two washes would merge into one unreadable stain, so the
21293
+ // attachment keeps a thin accent edge here on top of its wash — the single
21294
+ // outlined exception, and only over findings. Tracking the live position
21295
+ // is unchanged: that is what lets an edit end the attachment.
20269
21296
  ".cm-attached.cm-attached-finding": {
20270
- background: "transparent",
20271
- borderRadius: "2px",
20272
- boxShadow: "inset 0 -2px 0 var(--accent)"
21297
+ background: "var(--attached-tint)",
21298
+ borderRadius: "3px",
21299
+ boxShadow: "0 0 0 2px var(--attached-tint), 0 0 0 3.5px var(--accent)"
20273
21300
  },
20274
- // A quiet amber band, not a spell-checker's wavy red and not a blue
20275
- // selection. Findings run over whole clauses here, and a wave under three
20276
- // lines of prose reads as an error the writer must clear rather than a
20277
- // remark they may weigh. Blue stays reserved for what the writer does:
20278
- // selection, attachment, suggestion.
21301
+ // Model findings read as a background band. The wavy underline is reserved
21302
+ // for errors (local dictionary, typos) — findings never underline.
20279
21303
  ".cm-slop": {
20280
21304
  background: "var(--remark-tint)",
20281
21305
  borderRadius: "2px",
@@ -20284,14 +21308,23 @@ Editing direction: ${activeFinding.fix}`,
20284
21308
  transition: "background .12s ease"
20285
21309
  },
20286
21310
  ".cm-slop:hover": { background: "var(--remark-tint-hover)" },
20287
- // Hover preview of a variant: a plain white sheet over the passage, no
20288
- // colours. The attached tint stays underneath for the passage itself; this
20289
- // replace decoration only shows while a card is hovered or focused.
21311
+ // The passage whose variants are currently open: a dashed accent outline
21312
+ // with a faint wash — the only dashed mark in the draft, reading as
21313
+ // "deciding here". Paints over the attachment wash when they coincide;
21314
+ // hover previews paint over everything while they last.
21315
+ ".cm-variant-target": {
21316
+ background: "color-mix(in srgb, var(--accent) 8%, transparent)",
21317
+ borderRadius: "3px",
21318
+ outline: "1.5px dashed var(--accent)",
21319
+ outlineOffset: "3px"
21320
+ },
21321
+ // Hover preview of a variant: the same white sheet, but dashed — a sketch
21322
+ // of the text, not the text. Only shows while a card is hovered or focused.
20290
21323
  ".cm-variant-plain": {
20291
21324
  background: "var(--overlay)",
20292
21325
  borderRadius: "2px",
20293
21326
  boxShadow: "0 0 0 2px var(--overlay)",
20294
- borderBottom: "1px solid var(--border-strong)"
21327
+ border: "1px dashed var(--accent)"
20295
21328
  },
20296
21329
  // Slightly dim the content while /idea is streaming
20297
21330
  "&.streaming .cm-content": { opacity: "0.8" },
@@ -20354,12 +21387,23 @@ Editing direction: ${activeFinding.fix}`,
20354
21387
  ])),
20355
21388
  // Standard text-editing and history keymaps
20356
21389
  keymap.of([...searchKeymap, ...historyKeymap, ...defaultKeymap]),
20357
- EditorView.contentAttributes.of({ "aria-label": "Draft editor", "aria-describedby": "suggest-shortcut" }),
21390
+ // Native spellcheck: free red squiggly + right-click suggestions from
21391
+ // the OS/browser dictionary. Autocorrect/autocapitalize stay off — a
21392
+ // silent rewrite is not what "quick fix" means here.
21393
+ EditorView.contentAttributes.of({
21394
+ "aria-label": "Draft editor",
21395
+ "aria-describedby": "suggest-shortcut",
21396
+ spellcheck: "true",
21397
+ autocorrect: "off",
21398
+ autocapitalize: "off"
21399
+ }),
20358
21400
  // Ghost text state + decoration provider
20359
21401
  ghostField,
20360
21402
  reviewField,
20361
21403
  styleMarkerField,
21404
+ spellMarkerField,
20362
21405
  attachField,
21406
+ variantTargetField,
20363
21407
  variantPlainField,
20364
21408
  flashField,
20365
21409
  // Read-only compartment — toggled during /idea streaming
@@ -20389,9 +21433,16 @@ Editing direction: ${activeFinding.fix}`,
20389
21433
  const at = { x: event.clientX, y: event.clientY };
20390
21434
  const mark = event.target.closest?.(".cm-slop");
20391
21435
  if (!mark) {
21436
+ const spellLocal = event.target.closest?.(".cm-spell-marker");
21437
+ const spellMarker = spellLocal && view.state.field(spellMarkerField).find((item) => item.from === Number(spellLocal.dataset.spellFrom));
21438
+ if (spellMarker) {
21439
+ showSpellBubble(spellMarker, at.x, at.y);
21440
+ return false;
21441
+ }
20392
21442
  const local = event.target.closest?.(".cm-style-marker");
20393
21443
  const marker = local && view.state.field(styleMarkerField).find((item) => item.from === Number(local.dataset.styleFrom));
20394
21444
  if (marker) selectStyleMarker(marker);
21445
+ else if (atParagraphEnd(view.state)) showContinueBubble(at.x, at.y);
20395
21446
  return false;
20396
21447
  }
20397
21448
  const finding = reviewFindings.find((item) => item.id === Number(mark.dataset.slopId));
@@ -20471,6 +21522,11 @@ Editing direction: ${activeFinding.fix}`,
20471
21522
  }),
20472
21523
  parent: editorWrap
20473
21524
  });
21525
+ loadSpellDictionary().then((spell) => {
21526
+ spellChecker = spell;
21527
+ workView.dispatch({ effects: refreshSpellFx.of(null) });
21528
+ }).catch(() => {
21529
+ });
20474
21530
  new ResizeObserver(() => {
20475
21531
  chatHeight = Math.round(chatPanel.getBoundingClientRect().height) + 48;
20476
21532
  document.documentElement.style.setProperty("--chat-height", `${chatHeight}px`);
@@ -20675,8 +21731,8 @@ Editing direction: ${activeFinding.fix}`,
20675
21731
  documentName = current.path.split(/[\\/]/).pop();
20676
21732
  diskText = current.text;
20677
21733
  diskRevision = current.revision;
20678
- const cached = docStorage.getItem("wa-working");
20679
- workView.dispatch({ changes: { from: 0, to: workView.state.doc.length, insert: cached ?? current.text } });
21734
+ const cached2 = docStorage.getItem("wa-working");
21735
+ workView.dispatch({ changes: { from: 0, to: workView.state.doc.length, insert: cached2 ?? current.text } });
20680
21736
  loadingDocument = false;
20681
21737
  editorSetReadonly(workView, false);
20682
21738
  for (const paragraph of reviewParagraphs(workView.state.doc.toString())) checkedSentences.add(paragraph.key);
@@ -20691,7 +21747,7 @@ Editing direction: ${activeFinding.fix}`,
20691
21747
  restoreFindings();
20692
21748
  restoreChat();
20693
21749
  syncStyleScore();
20694
- if (cached !== null && cached !== current.text) promptDiskDrift(current);
21750
+ if (cached2 !== null && cached2 !== current.text) promptDiskDrift(current);
20695
21751
  else {
20696
21752
  saveStatus.textContent = "";
20697
21753
  saveStatus.title = "";
@@ -20916,3 +21972,13 @@ Editing direction: ${activeFinding.fix}`,
20916
21972
  workView.focus();
20917
21973
  syncStyleScore();
20918
21974
  })();
21975
+ /*! Bundled license information:
21976
+
21977
+ is-buffer/index.js:
21978
+ (*!
21979
+ * Determine if an object is a Buffer
21980
+ *
21981
+ * @author Feross Aboukhadijeh <https://feross.org>
21982
+ * @license MIT
21983
+ *)
21984
+ */