edytor 3.0.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/README.md +2 -2
  2. package/dist/cloudflare/DocumentRoom.d.ts +13 -9
  3. package/dist/cloudflare/DocumentRoom.js +10 -6
  4. package/dist/cloudflare/room/context.d.ts +27 -26
  5. package/dist/cloudflare/room/context.js +2 -2
  6. package/dist/cloudflare/room/purge.d.ts +6 -3
  7. package/dist/cloudflare/room/purge.js +44 -19
  8. package/dist/cloudflare/room/updates.d.ts +3 -4
  9. package/dist/cloudflare/room/updates.js +6 -8
  10. package/dist/crdt/attribution/attribution.d.ts +31 -12
  11. package/dist/crdt/attribution/attribution.js +56 -18
  12. package/dist/crdt/attribution/block.d.ts +3 -3
  13. package/dist/crdt/attribution/block.js +7 -8
  14. package/dist/crdt/attribution/index.d.ts +1 -1
  15. package/dist/crdt/attribution/index.js +1 -1
  16. package/dist/crdt/data.d.ts +62 -31
  17. package/dist/crdt/data.js +211 -103
  18. package/dist/crdt/doc/delete.d.ts +1 -1
  19. package/dist/crdt/doc/delete.js +13 -30
  20. package/dist/crdt/doc/funnel.js +29 -24
  21. package/dist/crdt/doc/gate.d.ts +3 -3
  22. package/dist/crdt/doc/history.d.ts +25 -9
  23. package/dist/crdt/doc/history.js +150 -40
  24. package/dist/crdt/doc/layout.js +11 -22
  25. package/dist/crdt/doc/meta.js +12 -17
  26. package/dist/crdt/doc/moves.d.ts +1 -1
  27. package/dist/crdt/doc/moves.js +52 -32
  28. package/dist/crdt/doc/reads.js +2 -2
  29. package/dist/crdt/doc/seed.js +2 -2
  30. package/dist/crdt/doc/split-merge.js +12 -15
  31. package/dist/crdt/doc/steps.d.ts +25 -13
  32. package/dist/crdt/doc/steps.js +52 -122
  33. package/dist/crdt/doc/types.d.ts +21 -9
  34. package/dist/crdt/document.d.ts +16 -5
  35. package/dist/crdt/document.js +21 -4
  36. package/dist/crdt/edytor-doc.d.ts +62 -64
  37. package/dist/crdt/edytor-doc.js +18 -13
  38. package/dist/crdt/engine-api.d.ts +33 -1
  39. package/dist/crdt/engine.d.ts +0 -1
  40. package/dist/crdt/engine.js +1 -4
  41. package/dist/crdt/flow.d.ts +1 -1
  42. package/dist/crdt/flow.js +25 -27
  43. package/dist/crdt/index.d.ts +26 -25
  44. package/dist/crdt/index.js +1 -1
  45. package/dist/crdt/placement/display.d.ts +5 -30
  46. package/dist/crdt/placement/display.js +39 -144
  47. package/dist/crdt/placement/keys.d.ts +58 -0
  48. package/dist/crdt/placement/keys.js +90 -0
  49. package/dist/crdt/placement/model.d.ts +119 -118
  50. package/dist/crdt/placement/model.js +242 -142
  51. package/dist/crdt/placement/resolve.d.ts +123 -35
  52. package/dist/crdt/placement/resolve.js +187 -153
  53. package/dist/crdt/placement/slots.d.ts +134 -0
  54. package/dist/crdt/placement/slots.js +152 -0
  55. package/dist/crdt/providers/room.d.ts +16 -2
  56. package/dist/crdt/providers/room.js +64 -17
  57. package/dist/crdt/providers/websocket.js +5 -3
  58. package/dist/crdt/purge.d.ts +17 -8
  59. package/dist/crdt/purge.js +236 -126
  60. package/dist/crdt/restore.d.ts +2 -2
  61. package/dist/crdt/restore.js +18 -34
  62. package/dist/crdt/schema.d.ts +22 -21
  63. package/dist/crdt/schema.js +29 -23
  64. package/dist/crdt/storage.d.ts +1 -1
  65. package/dist/crdt/storage.js +1 -1
  66. package/dist/crdt/structs.d.ts +15 -15
  67. package/dist/crdt/structs.js +8 -22
  68. package/dist/crdt/text/index/cache.js +4 -0
  69. package/dist/crdt/text/index/claims.js +12 -0
  70. package/dist/crdt/text/index/fold.d.ts +2 -3
  71. package/dist/crdt/text/index/fold.js +194 -44
  72. package/dist/crdt/text/index/layout.js +5 -8
  73. package/dist/crdt/text/index/placement.d.ts +26 -18
  74. package/dist/crdt/text/index/placement.js +404 -138
  75. package/dist/crdt/text/index/records.d.ts +3 -3
  76. package/dist/crdt/text/index/records.js +24 -31
  77. package/dist/crdt/text/index/shared.d.ts +11 -3
  78. package/dist/crdt/text/index/shared.js +12 -3
  79. package/dist/crdt/text/index/state.d.ts +21 -17
  80. package/dist/crdt/text/index/state.js +15 -17
  81. package/dist/crdt/text/index/streams.d.ts +10 -5
  82. package/dist/crdt/text/index/streams.js +78 -27
  83. package/dist/crdt/text/items.d.ts +10 -3
  84. package/dist/crdt/text/items.js +7 -0
  85. package/dist/crdt/text/model.d.ts +77 -62
  86. package/dist/crdt/text/model.js +84 -169
  87. package/dist/crdt/text/ranges.d.ts +1 -1
  88. package/dist/crdt/text/ranges.js +3 -3
  89. package/dist/crdt/text/rows.d.ts +13 -7
  90. package/dist/crdt/text/rows.js +24 -7
  91. package/dist/crdt/text/runs.d.ts +17 -12
  92. package/dist/crdt/text/runs.js +24 -7
  93. package/dist/crdt/text/streams.d.ts +26 -13
  94. package/dist/crdt/text/streams.js +41 -14
  95. package/dist/crdt/vendor/yjs/UPSTREAM.md +568 -21
  96. package/dist/crdt/vendor/yjs/dts/global.d.ts +2 -0
  97. package/dist/crdt/vendor/yjs/dts/index.d.ts +1 -1
  98. package/dist/crdt/vendor/yjs/dts/structs/AbstractStruct.d.ts +7 -0
  99. package/dist/crdt/vendor/yjs/dts/structs/Item.d.ts +194 -10
  100. package/dist/crdt/vendor/yjs/dts/utils/Doc.d.ts +6 -3
  101. package/dist/crdt/vendor/yjs/dts/utils/StructStore.d.ts +5 -18
  102. package/dist/crdt/vendor/yjs/dts/utils/Transaction.d.ts +15 -1
  103. package/dist/crdt/vendor/yjs/dts/utils/UpdateDecoder.d.ts +14 -6
  104. package/dist/crdt/vendor/yjs/dts/utils/UpdateEncoder.d.ts +33 -6
  105. package/dist/crdt/vendor/yjs/dts/utils/ids.d.ts +1 -65
  106. package/dist/crdt/vendor/yjs/dts/utils/marks.d.ts +6 -1
  107. package/dist/crdt/vendor/yjs/dts/ynode.d.ts +43 -7
  108. package/dist/crdt/vendor/yjs/global.d.ts +2 -0
  109. package/dist/crdt/vendor/yjs/src/index.js +1 -1
  110. package/dist/crdt/vendor/yjs/src/structs/AbstractStruct.js +10 -0
  111. package/dist/crdt/vendor/yjs/src/structs/Item.js +610 -51
  112. package/dist/crdt/vendor/yjs/src/utils/BlockSet.js +18 -9
  113. package/dist/crdt/vendor/yjs/src/utils/Doc.js +5 -3
  114. package/dist/crdt/vendor/yjs/src/utils/StructStore.js +5 -0
  115. package/dist/crdt/vendor/yjs/src/utils/Transaction.js +36 -13
  116. package/dist/crdt/vendor/yjs/src/utils/UpdateDecoder.js +88 -18
  117. package/dist/crdt/vendor/yjs/src/utils/UpdateEncoder.js +119 -32
  118. package/dist/crdt/vendor/yjs/src/utils/encoding.js +13 -1
  119. package/dist/crdt/vendor/yjs/src/utils/ids.js +7 -7
  120. package/dist/crdt/vendor/yjs/src/utils/marks.js +193 -8
  121. package/dist/crdt/vendor/yjs/src/utils/redo.js +1 -1
  122. package/dist/crdt/vendor/yjs/src/utils/transaction-helpers.js +1 -1
  123. package/dist/crdt/vendor/yjs/src/utils/updates.js +3 -3
  124. package/dist/crdt/vendor/yjs/src/ynode.js +177 -12
  125. package/dist/edytor.svelte.js +4 -1
  126. package/dist/events/beforeInputCommands.js +2 -2
  127. package/dist/events/onPageHide.d.ts +3 -0
  128. package/dist/events/onPageHide.js +16 -0
  129. package/dist/export/forms.d.ts +25 -25
  130. package/dist/surface/observer.svelte.js +2 -2
  131. package/dist/surface/projector.svelte.d.ts +15 -3
  132. package/dist/surface/projector.svelte.js +59 -8
  133. package/package.json +2 -2
  134. package/dist/crdt/placement/rank.d.ts +0 -87
  135. package/dist/crdt/placement/rank.js +0 -332
  136. package/dist/crdt/placement/source.d.ts +0 -43
  137. package/dist/crdt/placement/source.js +0 -66
  138. package/dist/crdt/text/delete-index.d.ts +0 -129
  139. package/dist/crdt/text/delete-index.js +0 -365
  140. package/dist/crdt/text/deletes.d.ts +0 -76
  141. package/dist/crdt/text/deletes.js +0 -362
  142. package/dist/crdt/text/index/anchored.d.ts +0 -17
  143. package/dist/crdt/text/index/anchored.js +0 -197
@@ -170,8 +170,8 @@ export declare const equationForms: (mathml?: MathML) => {
170
170
  equation: {
171
171
  itemKind?: string;
172
172
  island?: boolean;
173
- rendersContent: boolean;
174
173
  defaultChild?: string;
174
+ rendersContent: boolean;
175
175
  void: boolean;
176
176
  lines?: boolean;
177
177
  html?: string | ((block: JSONBlock, content: string, children: string) => string);
@@ -234,8 +234,8 @@ export declare const bundledRead: () => {
234
234
  }[];
235
235
  itemKind?: string | undefined;
236
236
  island?: boolean | undefined;
237
- rendersContent?: boolean | undefined;
238
237
  defaultChild?: string | undefined;
238
+ rendersContent?: boolean | undefined;
239
239
  void?: boolean | undefined;
240
240
  lines?: boolean | undefined;
241
241
  html?: string | ((block: JSONBlock, content: string, children: string) => string) | undefined;
@@ -249,8 +249,8 @@ export declare const bundledRead: () => {
249
249
  presets: {}[];
250
250
  itemKind?: string | undefined;
251
251
  island?: boolean | undefined;
252
- rendersContent?: boolean | undefined;
253
252
  defaultChild?: string | undefined;
253
+ rendersContent?: boolean | undefined;
254
254
  void?: boolean | undefined;
255
255
  lines?: boolean | undefined;
256
256
  html?: string | ((block: JSONBlock, content: string, children: string) => string) | undefined;
@@ -264,8 +264,8 @@ export declare const bundledRead: () => {
264
264
  presets: {}[];
265
265
  itemKind?: string | undefined;
266
266
  island?: boolean | undefined;
267
- rendersContent?: boolean | undefined;
268
267
  defaultChild?: string | undefined;
268
+ rendersContent?: boolean | undefined;
269
269
  void?: boolean | undefined;
270
270
  lines?: boolean | undefined;
271
271
  html?: string | ((block: JSONBlock, content: string, children: string) => string) | undefined;
@@ -286,8 +286,8 @@ export declare const bundledRead: () => {
286
286
  presets: {}[];
287
287
  itemKind?: string | undefined;
288
288
  island?: boolean | undefined;
289
- rendersContent?: boolean | undefined;
290
289
  defaultChild?: string | undefined;
290
+ rendersContent?: boolean | undefined;
291
291
  void?: boolean | undefined;
292
292
  lines?: boolean | undefined;
293
293
  html?: string | ((block: JSONBlock, content: string, children: string) => string) | undefined;
@@ -305,8 +305,8 @@ export declare const bundledRead: () => {
305
305
  }[];
306
306
  itemKind?: string | undefined;
307
307
  island?: boolean | undefined;
308
- rendersContent?: boolean | undefined;
309
308
  defaultChild?: string | undefined;
309
+ rendersContent?: boolean | undefined;
310
310
  void?: boolean | undefined;
311
311
  lines?: boolean | undefined;
312
312
  html?: string | ((block: JSONBlock, content: string, children: string) => string) | undefined;
@@ -320,8 +320,8 @@ export declare const bundledRead: () => {
320
320
  presets: {}[];
321
321
  itemKind?: string | undefined;
322
322
  island?: boolean | undefined;
323
- rendersContent?: boolean | undefined;
324
323
  defaultChild?: string | undefined;
324
+ rendersContent?: boolean | undefined;
325
325
  void?: boolean | undefined;
326
326
  lines?: boolean | undefined;
327
327
  html?: string | ((block: JSONBlock, content: string, children: string) => string) | undefined;
@@ -335,8 +335,8 @@ export declare const bundledRead: () => {
335
335
  presets: {}[];
336
336
  itemKind?: string | undefined;
337
337
  island?: boolean | undefined;
338
- rendersContent?: boolean | undefined;
339
338
  defaultChild?: string | undefined;
339
+ rendersContent?: boolean | undefined;
340
340
  void?: boolean | undefined;
341
341
  lines?: boolean | undefined;
342
342
  html?: string | ((block: JSONBlock, content: string, children: string) => string) | undefined;
@@ -350,8 +350,8 @@ export declare const bundledRead: () => {
350
350
  presets: {}[];
351
351
  itemKind?: string | undefined;
352
352
  island?: boolean | undefined;
353
- rendersContent: boolean;
354
353
  defaultChild?: string | undefined;
354
+ rendersContent: boolean;
355
355
  void: boolean;
356
356
  lines?: boolean | undefined;
357
357
  html?: string | ((block: JSONBlock, content: string, children: string) => string) | undefined;
@@ -364,8 +364,8 @@ export declare const bundledRead: () => {
364
364
  'ordered-list': {
365
365
  itemKind?: string | undefined;
366
366
  island?: boolean | undefined;
367
- rendersContent: boolean;
368
367
  defaultChild: string;
368
+ rendersContent: boolean;
369
369
  void?: boolean | undefined;
370
370
  lines?: boolean | undefined;
371
371
  html?: string | ((block: JSONBlock, content: string, children: string) => string) | undefined;
@@ -378,8 +378,8 @@ export declare const bundledRead: () => {
378
378
  'unordered-list': {
379
379
  itemKind?: string | undefined;
380
380
  island?: boolean | undefined;
381
- rendersContent: boolean;
382
381
  defaultChild: string;
382
+ rendersContent: boolean;
383
383
  void?: boolean | undefined;
384
384
  lines?: boolean | undefined;
385
385
  html?: string | ((block: JSONBlock, content: string, children: string) => string) | undefined;
@@ -400,8 +400,8 @@ export declare const bundledRead: () => {
400
400
  table: {
401
401
  itemKind?: string | undefined;
402
402
  island?: boolean | undefined;
403
- rendersContent: boolean;
404
403
  defaultChild: string;
404
+ rendersContent: boolean;
405
405
  void?: boolean | undefined;
406
406
  lines?: boolean | undefined;
407
407
  html?: string | ((block: JSONBlock, content: string, children: string) => string) | undefined;
@@ -415,8 +415,8 @@ export declare const bundledRead: () => {
415
415
  tableRow: {
416
416
  itemKind?: string | undefined;
417
417
  island?: boolean | undefined;
418
- rendersContent: boolean;
419
418
  defaultChild: string;
419
+ rendersContent: boolean;
420
420
  void?: boolean | undefined;
421
421
  lines?: boolean | undefined;
422
422
  html?: string | ((block: JSONBlock, content: string, children: string) => string) | undefined;
@@ -429,8 +429,8 @@ export declare const bundledRead: () => {
429
429
  tableCell: {
430
430
  itemKind?: string | undefined;
431
431
  island: boolean;
432
- rendersContent?: boolean | undefined;
433
432
  defaultChild?: string | undefined;
433
+ rendersContent?: boolean | undefined;
434
434
  void?: boolean | undefined;
435
435
  lines?: boolean | undefined;
436
436
  html?: string | ((block: JSONBlock, content: string, children: string) => string) | undefined;
@@ -443,8 +443,8 @@ export declare const bundledRead: () => {
443
443
  image: {
444
444
  itemKind?: string | undefined;
445
445
  island?: boolean | undefined;
446
- rendersContent?: boolean | undefined;
447
446
  defaultChild?: string | undefined;
447
+ rendersContent?: boolean | undefined;
448
448
  void: boolean;
449
449
  lines?: boolean | undefined;
450
450
  html?: string | ((block: JSONBlock, content: string, children: string) => string) | undefined;
@@ -457,8 +457,8 @@ export declare const bundledRead: () => {
457
457
  embed: {
458
458
  itemKind?: string | undefined;
459
459
  island?: boolean | undefined;
460
- rendersContent?: boolean | undefined;
461
460
  defaultChild?: string | undefined;
461
+ rendersContent?: boolean | undefined;
462
462
  void: boolean;
463
463
  lines?: boolean | undefined;
464
464
  html?: string | ((block: JSONBlock, content: string, children: string) => string) | undefined;
@@ -471,8 +471,8 @@ export declare const bundledRead: () => {
471
471
  bookmark: {
472
472
  itemKind?: string | undefined;
473
473
  island?: boolean | undefined;
474
- rendersContent?: boolean | undefined;
475
474
  defaultChild?: string | undefined;
475
+ rendersContent?: boolean | undefined;
476
476
  void: boolean;
477
477
  lines?: boolean | undefined;
478
478
  html?: string | ((block: JSONBlock, content: string, children: string) => string) | undefined;
@@ -485,8 +485,8 @@ export declare const bundledRead: () => {
485
485
  file: {
486
486
  itemKind?: string | undefined;
487
487
  island?: boolean | undefined;
488
- rendersContent?: boolean | undefined;
489
488
  defaultChild?: string | undefined;
489
+ rendersContent?: boolean | undefined;
490
490
  void: boolean;
491
491
  lines?: boolean | undefined;
492
492
  html?: string | ((block: JSONBlock, content: string, children: string) => string) | undefined;
@@ -499,8 +499,8 @@ export declare const bundledRead: () => {
499
499
  video: {
500
500
  itemKind?: string | undefined;
501
501
  island?: boolean | undefined;
502
- rendersContent?: boolean | undefined;
503
502
  defaultChild?: string | undefined;
503
+ rendersContent?: boolean | undefined;
504
504
  void: boolean;
505
505
  lines?: boolean | undefined;
506
506
  html?: string | ((block: JSONBlock, content: string, children: string) => string) | undefined;
@@ -513,8 +513,8 @@ export declare const bundledRead: () => {
513
513
  audio: {
514
514
  itemKind?: string | undefined;
515
515
  island?: boolean | undefined;
516
- rendersContent?: boolean | undefined;
517
516
  defaultChild?: string | undefined;
517
+ rendersContent?: boolean | undefined;
518
518
  void: boolean;
519
519
  lines?: boolean | undefined;
520
520
  html?: string | ((block: JSONBlock, content: string, children: string) => string) | undefined;
@@ -527,8 +527,8 @@ export declare const bundledRead: () => {
527
527
  page: {
528
528
  itemKind?: string | undefined;
529
529
  island?: boolean | undefined;
530
- rendersContent: boolean;
531
530
  defaultChild?: string | undefined;
531
+ rendersContent: boolean;
532
532
  void: boolean;
533
533
  lines?: boolean | undefined;
534
534
  html?: string | ((block: JSONBlock, content: string, children: string) => string) | undefined;
@@ -541,8 +541,8 @@ export declare const bundledRead: () => {
541
541
  toc: {
542
542
  itemKind?: string | undefined;
543
543
  island?: boolean | undefined;
544
- rendersContent: boolean;
545
544
  defaultChild?: string | undefined;
545
+ rendersContent: boolean;
546
546
  void: boolean;
547
547
  lines?: boolean | undefined;
548
548
  html?: string | ((block: JSONBlock, content: string, children: string) => string) | undefined;
@@ -555,8 +555,8 @@ export declare const bundledRead: () => {
555
555
  equation: {
556
556
  itemKind?: string | undefined;
557
557
  island?: boolean | undefined;
558
- rendersContent: boolean;
559
558
  defaultChild?: string | undefined;
559
+ rendersContent: boolean;
560
560
  void: boolean;
561
561
  lines?: boolean | undefined;
562
562
  html?: string | ((block: JSONBlock, content: string, children: string) => string) | undefined;
@@ -569,8 +569,8 @@ export declare const bundledRead: () => {
569
569
  columns: {
570
570
  itemKind?: string | undefined;
571
571
  island?: boolean | undefined;
572
- rendersContent: boolean;
573
572
  defaultChild: string;
573
+ rendersContent: boolean;
574
574
  void?: boolean | undefined;
575
575
  lines?: boolean | undefined;
576
576
  html?: string | ((block: JSONBlock, content: string, children: string) => string) | undefined;
@@ -584,8 +584,8 @@ export declare const bundledRead: () => {
584
584
  column: {
585
585
  itemKind?: string | undefined;
586
586
  island?: boolean | undefined;
587
- rendersContent: boolean;
588
587
  defaultChild?: string | undefined;
588
+ rendersContent: boolean;
589
589
  void?: boolean | undefined;
590
590
  lines?: boolean | undefined;
591
591
  html?: string | ((block: JSONBlock, content: string, children: string) => string) | undefined;
@@ -187,7 +187,7 @@ export class SurfaceObserver {
187
187
  #heals = 0;
188
188
  /** The pass wrote the host itself: a render (the projector displays after it). */
189
189
  #wrote = false;
190
- /** Records arrived since the last pass (a display waiting on them gets a pass). */
190
+ /** Records arrived since the last pass (the projector reads what they did: `rendered`). */
191
191
  #fresh = false;
192
192
  #window = null;
193
193
  constructor(edytor) {
@@ -534,7 +534,7 @@ export class SurfaceObserver {
534
534
  }
535
535
  if (this.#fresh) {
536
536
  this.#fresh = false;
537
- void settle(work).then(edytor.projector.inputHandled);
537
+ void settle(work).then(edytor.projector.rendered);
538
538
  }
539
539
  return work;
540
540
  };
@@ -28,11 +28,23 @@ export declare class Projector {
28
28
  * removal re-rendering the text under a range) runs now. The flag is no
29
29
  * reactive source, so no later flush is owed: without this, the range the
30
30
  * render moved (Firefox keeps it where the removed element stood) would be
31
- * adopted as the gesture's when its `selectionchange` lands. The observer
32
- * calls it too once it processed DOM records: a display that waits gets a
33
- * pass.
31
+ * adopted as the gesture's when its `selectionchange` lands.
34
32
  */
35
33
  inputHandled: () => void;
34
+ /**
35
+ * The observer compared the records of a DOM change (a render, a browser
36
+ * edit): a display that waits on them gets a pass (`inputHandled`), and a
37
+ * DOM selection they moved off the last observation with no gesture since
38
+ * is render drift no event reports (`sel.drift.unreported`). A render no
39
+ * commit asked for (a kind's decoration reading state of its own: a code
40
+ * grammar that lands re-tokenizes its lines) replaced the text node the
41
+ * caret was in; the browser left the caret where that node stood (the
42
+ * line's start) and Chromium and WebKit send no `selectionchange` for it,
43
+ * while one that comes would read as foreign (no flush ran the projector
44
+ * since). The value is displayed again now, in the render's own task:
45
+ * before a key, or its `beforeinput` declaring that caret, can read it.
46
+ */
47
+ rendered: () => void;
36
48
  /**
37
49
  * Classify a `selectionchange` by comparing the DOM selection with the
38
50
  * last display, the render epoch and the gesture serial:
@@ -34,7 +34,9 @@
34
34
  * the render epoch and the gesture serial: echo, render drift (displayed
35
35
  * again), composition, foreign write or intent (adopted). Two named, counted,
36
36
  * time-bounded browser rules are the only signatures:
37
- * the Android post-delete snap-back and the IME post-commit jump.
37
+ * the Android post-delete snap-back and the IME post-commit jump. A render
38
+ * that moved the DOM selection with no event to say so is drift too, read
39
+ * from the observer's records (`rendered`, `sel.drift.unreported`).
38
40
  */
39
41
  import { untrack } from 'svelte';
40
42
  import { clearDomSelection, createDomRange, getActiveElement, getDomSelection, getDomSelectionSnapshot, scrollCaretIntoView } from '../selection/domSelection.js';
@@ -88,6 +90,8 @@ export class Projector {
88
90
  #opening = false;
89
91
  /** The gesture serial at the last observation (a display or an adopted `selectionchange`). */
90
92
  #serial = -1;
93
+ /** Where the DOM selection was at that observation (`#note`; null: no range). */
94
+ #at = null;
91
95
  /** The named rules' evidence: the last model-owned delete and the last IME commit. */
92
96
  #deleted = null;
93
97
  #committed = null;
@@ -183,14 +187,51 @@ export class Projector {
183
187
  * removal re-rendering the text under a range) runs now. The flag is no
184
188
  * reactive source, so no later flush is owed: without this, the range the
185
189
  * render moved (Firefox keeps it where the removed element stood) would be
186
- * adopted as the gesture's when its `selectionchange` lands. The observer
187
- * calls it too once it processed DOM records: a display that waits gets a
188
- * pass.
190
+ * adopted as the gesture's when its `selectionchange` lands.
189
191
  */
190
192
  inputHandled = () => {
191
193
  if (this.#pending)
192
194
  this.edytor.surface.update();
193
195
  };
196
+ /**
197
+ * The observer compared the records of a DOM change (a render, a browser
198
+ * edit): a display that waits on them gets a pass (`inputHandled`), and a
199
+ * DOM selection they moved off the last observation with no gesture since
200
+ * is render drift no event reports (`sel.drift.unreported`). A render no
201
+ * commit asked for (a kind's decoration reading state of its own: a code
202
+ * grammar that lands re-tokenizes its lines) replaced the text node the
203
+ * caret was in; the browser left the caret where that node stood (the
204
+ * line's start) and Chromium and WebKit send no `selectionchange` for it,
205
+ * while one that comes would read as foreign (no flush ran the projector
206
+ * since). The value is displayed again now, in the render's own task:
207
+ * before a key, or its `beforeinput` declaring that caret, can read it.
208
+ */
209
+ rendered = () => {
210
+ if (this.#unreported())
211
+ this.edytor.selection.display();
212
+ else
213
+ this.inputHandled();
214
+ };
215
+ /**
216
+ * `rendered`'s drift: a text value, no display waiting, no gesture, drag
217
+ * or composition since the last observation, and the DOM selection in the
218
+ * host no longer where that observation found it.
219
+ */
220
+ #unreported = () => {
221
+ const { edytor } = this;
222
+ const { selection } = edytor;
223
+ const at = this.#at;
224
+ const node = edytor.node;
225
+ if (!at || !node || selection.value.kind !== 'text' || edytor.intentSerial !== this.#serial)
226
+ return false;
227
+ if (selection.request !== this.#request || selection.dragging || edytor.composition.live)
228
+ return false;
229
+ const dom = getDomSelection(node);
230
+ if (!dom || !isOwnPoint(node, dom.anchorNode))
231
+ return false;
232
+ return !(samePoint(at.anchor, dom.anchorNode, dom.anchorOffset) &&
233
+ samePoint(at.focus, dom.focusNode, dom.focusOffset));
234
+ };
194
235
  /**
195
236
  * Classify a `selectionchange` by comparing the DOM selection with the
196
237
  * last display, the render epoch and the gesture serial:
@@ -283,9 +324,19 @@ export class Projector {
283
324
  /** The DOM selection is observed now (adopted, or ignored by the adopter's own rules). */
284
325
  #observed = (observation) => {
285
326
  this.#seen = this.#flushes;
286
- this.#serial = this.edytor.intentSerial;
327
+ this.#note();
287
328
  return observation;
288
329
  };
330
+ /** An observation: its gesture serial, and where the DOM selection is (`#at`). */
331
+ #note = () => {
332
+ const { edytor } = this;
333
+ this.#serial = edytor.intentSerial;
334
+ const dom = edytor.node && getDomSelection(edytor.node);
335
+ this.#at =
336
+ dom?.anchorNode && dom.focusNode
337
+ ? { anchor: [dom.anchorNode, dom.anchorOffset], focus: [dom.focusNode, dom.focusOffset] }
338
+ : null;
339
+ };
289
340
  /** The DOM selection is our last display unchanged, or a text-bound range that shows the value. */
290
341
  #echoes = (dom) => {
291
342
  const displayed = this.#displayed;
@@ -344,7 +395,7 @@ export class Projector {
344
395
  observe = () => {
345
396
  this.#parking = false;
346
397
  this.#seen = this.#flushes;
347
- this.#serial = this.edytor.intentSerial;
398
+ this.#note();
348
399
  this.#displayed = null;
349
400
  };
350
401
  /** Answers whether the pass completed (false: wait for a later flush). */
@@ -388,7 +439,7 @@ export class Projector {
388
439
  if (inside)
389
440
  clearDomSelection(node);
390
441
  this.#displayed = null;
391
- this.#serial = edytor.intentSerial;
442
+ this.#note();
392
443
  }
393
444
  return true;
394
445
  }
@@ -425,7 +476,7 @@ export class Projector {
425
476
  selection.scrollOnDisplay = false;
426
477
  this.#displayed = { ...points, epoch: selection.epoch };
427
478
  this.#seen = this.#flushes;
428
- this.#serial = edytor.intentSerial;
479
+ this.#note();
429
480
  selection.observed({
430
481
  edge: isCollapsed ? getMarkEdgeSide(startText, start[0], yStart) : undefined
431
482
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "edytor",
3
3
  "license": "MIT",
4
- "version": "3.0.0",
4
+ "version": "4.0.0",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/beynar/edytor.git"
@@ -163,7 +163,7 @@
163
163
  }
164
164
  },
165
165
  "dependencies": {
166
- "@tanstack/highlight": "^0.1.0",
166
+ "@tanstack/highlight": "^1.1.0",
167
167
  "@tanstack/markdown": "1.0.0",
168
168
  "esm-env": "^1.2.2",
169
169
  "lib0-v14": "npm:lib0@1.0.0-rc.32"
@@ -1,87 +0,0 @@
1
- /**
2
- * Rank keys for the placement model — a Logoot-style dense ordering.
3
- *
4
- * A rank is a non-empty sequence of *segments* `(v, t)`:
5
- *
6
- * - `v` — a signed digit in `[VMIN, VMAX]` (±2^40). There is always room
7
- * between two distinct digits (`rv - lv > 1`); when there is not, the
8
- * generator descends one level instead of renumbering.
9
- * - `t` — the allocator's `doc.clientID` (uint53), the *tiebreak* that makes
10
- * concurrent allocations of the same digit path distinct, or none
11
- * (`Infinity`: sorts above every client) on a segment that only leads to
12
- * deeper ones. The last segment always carries one (schema
13
- * generation 5): a rank's own client sits on its last segment, and a
14
- * copied segment keeps its tie only where the order needs it (between two
15
- * keys that differ only by their ties).
16
- *
17
- * Encoding is variable-length and order-preserving, so the persisted rank
18
- * is a plain string and the comparator is `a < b` (CRDT study 2026-10;
19
- * up to generation 4 every segment was 16 characters, 9 of them a tie):
20
- *
21
- * - `v` → a length character (`'H'` for zero, `'H' + k` for a positive
22
- * digit of `k` base-64 places, `'H' - k` for a negative one, its places
23
- * complemented) and its places — small digits are short;
24
- * - a tie → `'!'`, then `t` as a digit (`'!'` sorts below every length
25
- * character, so a tied segment sorts before the same digit leading on);
26
- * - rank = segments concatenated; a prefix sorts before its extension, as
27
- * string `<` does.
28
- *
29
- * Allocation (`rankBetween(left, right, clientId)`):
30
- *
31
- * - gap at level `i` (`rv - lv > 1`): emit `lv + 1 + floor(rand·(rv-lv-1))`,
32
- * own tie — random-in-gap keeps sequential inserts shallow and concurrent
33
- * same-gap inserts deterministic-distinct.
34
- * - adjacent or equal digits (`rv - lv ≤ 1`): the left digit or the right
35
- * one with the allocator's own tie when that tie sorts between theirs;
36
- * else copy the left segment and descend — untied when that already sorts
37
- * below the right bound (the result is then above left and below right,
38
- * and the next level is free), tied otherwise.
39
- * - left exhausted/open: emit `rSeg.v - 1` (boundary-minus). Sequential
40
- * prepends stay at depth 1; the bound can only underflow after ~2^40
41
- * consecutive boundary pushes — documented unreachable, guarded by a
42
- * descriptive throw.
43
- *
44
- * No floating point, no global renumbering, no wall clocks.
45
- */
46
- /** Digit range. VMIN = -2^40, VMAX = 2^40 - 1. */
47
- export declare const RANK_VMIN: number;
48
- export declare const RANK_VMAX: number;
49
- export type RankSeg = {
50
- v: number;
51
- t: number;
52
- };
53
- /** Thrown when rank generation would need a digit below VMIN (unreachable). */
54
- export declare class RankSpaceExhausted extends Error {
55
- constructor();
56
- }
57
- /** Decode a rank string into its segment array (`t: Infinity` where a segment has no tie). */
58
- export declare const decodeRank: (rank: string) => RankSeg[];
59
- /** Encode segments back to the persisted string form. */
60
- export declare const encodeRank: (segs: readonly RankSeg[]) => string;
61
- /**
62
- * Allocate a rank strictly between `left` and `right` (either may be
63
- * `undefined` for the open end of a sibling list). `clientId` is the
64
- * allocating doc's clientID; `rand` supplies the in-gap pick (defaults to
65
- * `Math.random`, tests inject a seeded stream).
66
- *
67
- * Guaranteed: `left < result < right` for any `left < right`, including
68
- * equal-prefix/concurrent-collision inputs — the tiebreak dimension makes a
69
- * strictly-between key always exist (modulo the documented VMIN bound).
70
- */
71
- export declare const rankBetween: (left: string | undefined, right: string | undefined, clientId: number, rand?: () => number, compact?: boolean) => string;
72
- /** The base of the run band: a run member's digit lies just above it. */
73
- export declare const RANK_RUN: number;
74
- /**
75
- * A rank in `(left, right)` for an insert right after `left` — one rule for
76
- * array items (`crdt/data.ts`) and inserted blocks (`order.insert.run`).
77
- * When `client` made `left`, the rank is in the client's run after it: `left`
78
- * itself as the prefix, then one segment of the run band — or, when `left`
79
- * is already a member of the client's run, that run's prefix and a member
80
- * between `left` and the next one. So what one client inserts after its own
81
- * items stays together whatever a peer inserts in that gap meanwhile: a
82
- * peer's rank there is either no extension of the run's prefix (it sorts
83
- * after the whole run) or one whose next digit is above the band (YATA's
84
- * origin rule, as `Y.Array` keeps an insert after its origin). Elsewhere, or
85
- * when the run rank would not sort below `right`, a plain {@link rankBetween}.
86
- */
87
- export declare const rankAfter: (left: string | undefined, right: string | undefined, client: number, rand?: () => number) => string;