@mocanvas/sync 4.0.2 → 4.1.1
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/ARCHITECTURE.md +8 -2
- package/MIGRATION.md +48 -22
- package/README.md +48 -1
- package/UI.md +24 -2
- package/dist/index.d.ts +27 -3
- package/dist/index.js +54 -12
- package/dist/index.js.map +1 -1
- package/package.json +6 -4
package/ARCHITECTURE.md
CHANGED
|
@@ -18,9 +18,15 @@ document model, tools, and UI live in TypeScript.
|
|
|
18
18
|
|
|
19
19
|
## Non-goals (v1)
|
|
20
20
|
|
|
21
|
-
- Wire compatibility with tldraw's sync protocol.
|
|
22
|
-
|
|
21
|
+
- Wire compatibility with tldraw's sync protocol. `@mocanvas/sync` is a
|
|
22
|
+
transport of its own; it does not speak tldraw's wire format.
|
|
23
23
|
- Pixel-identical rendering of every built-in shape.
|
|
24
|
+
- Shipping message catalogues. The translation seam exists
|
|
25
|
+
(`TLUiOverrides.translations`); the strings are the host's.
|
|
26
|
+
|
|
27
|
+
Slot-compatible UI components were a non-goal here and are no longer: the
|
|
28
|
+
`TLComponents` and `TLUiOverrides` surfaces are implemented. See
|
|
29
|
+
[MIGRATION.md §7](MIGRATION.md).
|
|
24
30
|
|
|
25
31
|
## Clean-room rule
|
|
26
32
|
|
package/MIGRATION.md
CHANGED
|
@@ -10,7 +10,8 @@ The short version: your **documents, records, ids and `.tldr` files carry over
|
|
|
10
10
|
untouched**, most of your **`Editor` calls carry over untouched**, your
|
|
11
11
|
**`ShapeUtil` / `StateNode` / `BindingUtil` subclasses keep their shape**, and
|
|
12
12
|
the real work is (a) imports, (b) teaching custom shapes about the GPU
|
|
13
|
-
renderer, and (c)
|
|
13
|
+
renderer, and (c) UI, where the slots are compatible but the message
|
|
14
|
+
catalogues are yours to supply (§7).
|
|
14
15
|
|
|
15
16
|
---
|
|
16
17
|
|
|
@@ -237,6 +238,21 @@ the `export * from "@mocanvas/mocanvas"` at the top of the package.
|
|
|
237
238
|
Everything `@mocanvas/editor` exports is re-exported by `@mocanvas/mocanvas`, so in app
|
|
238
239
|
code you can import from `@mocanvas/mocanvas` alone.
|
|
239
240
|
|
|
241
|
+
### Step 1b — the stylesheet
|
|
242
|
+
|
|
243
|
+
| Old import | New import |
|
|
244
|
+
| ------------------------ | --------------------------------- |
|
|
245
|
+
| `"tldraw/tldraw.css"` | `"@mocanvas/mocanvas/mocanvas.css"` |
|
|
246
|
+
|
|
247
|
+
Same arrangement, same one line, wherever you had it. The default UI is
|
|
248
|
+
unstyled without it.
|
|
249
|
+
|
|
250
|
+
`@mocanvas/compat` does not re-export the stylesheet — a package can only
|
|
251
|
+
export files it contains — so this import names `@mocanvas/mocanvas` even
|
|
252
|
+
during the zero-rename step. Under pnpm's strict `node_modules` that means
|
|
253
|
+
adding `@mocanvas/mocanvas` to your own dependencies alongside
|
|
254
|
+
`@mocanvas/compat`.
|
|
255
|
+
|
|
240
256
|
### Step 2 — drop the prefixes
|
|
241
257
|
|
|
242
258
|
Once the app builds and runs against `@mocanvas/compat`, rename `TLFoo` →
|
|
@@ -499,26 +515,35 @@ returns both and `putContentOntoCurrentPage` accepts both.
|
|
|
499
515
|
|
|
500
516
|
## 7. UI
|
|
501
517
|
|
|
502
|
-
**
|
|
503
|
-
|
|
518
|
+
**Slot compatibility exists.** This section used to say it did not and that it
|
|
519
|
+
was a v1 non-goal; both stopped being true before 4.0. Your existing overrides
|
|
520
|
+
of tldraw's UI components are the shape mocanvas expects.
|
|
504
521
|
|
|
505
522
|
What you have:
|
|
506
523
|
|
|
507
|
-
- `
|
|
508
|
-
|
|
509
|
-
- `
|
|
510
|
-
|
|
524
|
+
- `components` — the `TLComponents` map, at both the canvas level and the
|
|
525
|
+
chrome level. Pass a component to replace a slot, `null` to remove it.
|
|
526
|
+
- `overrides` — `TLUiOverrides`, with `tools`, `actions` and `translations`.
|
|
527
|
+
The tool and action lists are what the toolbar, the menus and the keyboard
|
|
528
|
+
bindings are all built from, so rewriting an entry changes all three
|
|
529
|
+
together rather than only what is drawn.
|
|
530
|
+
- The default chrome as named exports — `TldrawUi`, `DefaultUi`,
|
|
531
|
+
`DefaultToolbar`, `DefaultMainMenu` and the rest — so you can render one
|
|
532
|
+
piece of it inside chrome of your own.
|
|
533
|
+
- `useTools()` and `useActions()` return those lists after overrides, which is
|
|
534
|
+
what a toolbar of your own should render from.
|
|
535
|
+
- `hideUi` on `<Mocanvas />` still turns the chrome off entirely, leaving you
|
|
536
|
+
the canvas and your `children`.
|
|
511
537
|
- `useEditor()` inside any descendant of `<Canvas>` / `<EditorProvider>`
|
|
512
538
|
returns the `Editor` (`useMaybeEditor()` returns `Editor | null`).
|
|
513
539
|
- `track(Component)` and `useValue` from `@mocanvas/state/react` — re-exported
|
|
514
|
-
from `@mocanvas/editor` and `@mocanvas/mocanvas` — make a component re-render
|
|
515
|
-
signals it reads change.
|
|
540
|
+
from `@mocanvas/editor` and `@mocanvas/mocanvas` — make a component re-render
|
|
541
|
+
when the signals it reads change.
|
|
516
542
|
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
components.
|
|
543
|
+
One real difference remains: mocanvas ships **no message catalogues**, so
|
|
544
|
+
`overrides.translations` is where UI strings come from. The language menu lists
|
|
545
|
+
only the locales you supply a non-empty dictionary for, and renders nothing at
|
|
546
|
+
all if you supply none — see the note in `UI.md`.
|
|
522
547
|
|
|
523
548
|
---
|
|
524
549
|
|
|
@@ -626,19 +651,20 @@ before.
|
|
|
626
651
|
|
|
627
652
|
## 10. Known gaps
|
|
628
653
|
|
|
629
|
-
|
|
630
|
-
the
|
|
654
|
+
Every row below was re-checked against the built package rather than carried
|
|
655
|
+
forward from the roadmap. Rows that used to sit here and no longer belong —
|
|
656
|
+
`editor.resizeShape` and `stretchShapes`, `editor.getSvgString` and `toImage`,
|
|
657
|
+
`editor.textMeasure`/`user`/`menus`, presence records, and slot-compatible UI —
|
|
658
|
+
are all present, and the entries claiming otherwise were stale rather than
|
|
659
|
+
aspirational.
|
|
631
660
|
|
|
632
661
|
| Gap | Status |
|
|
633
662
|
| --- | ------ |
|
|
634
|
-
| `editor.resizeShape`, `editor.stretchShapes` | phase 3. Interactive resize lives in the select tool; `ShapeUtil.onResize` and `BaseBoxShapeUtil` work, but there is no imperative resize entry point on `Editor`. |
|
|
635
|
-
| `editor.getSvgString`, `editor.toImage` | Not `Editor` methods. Export is a set of free functions in `@mocanvas/mocanvas`: `getSvgString(editor, ids?, opts?)`, `exportToBlob(editor, opts)`, `downloadBlob(blob, filename)`, `copyBlobToClipboard(blob)`. |
|
|
636
663
|
| `ShapeUtil.toSvg`, `ShapeUtil.toBackgroundSvg` | Not `ShapeUtil` members. Custom shapes contribute to SVG export through `registerShapeSvgRenderer(type, renderer)`; without one they fall back to `geometryFallbackSvg`. |
|
|
637
|
-
|
|
|
638
|
-
| Sync protocol | Explicit v1 non-goal. Wire compatibility with tldraw's sync protocol is not planned for v1. |
|
|
639
|
-
| Slot-compatible UI | Explicit v1 non-goal. See §7. |
|
|
664
|
+
| Sync protocol | Wire compatibility with tldraw's own sync protocol is not planned. `@mocanvas/sync` is a working transport of its own — presence records, `store.mergeRemoteChanges`, and a relay — but it does not speak tldraw's wire format. |
|
|
640
665
|
| `image` shape on the GPU | The texture path exists in the engine (`StyleWords.texture` + `uploadTexture`) but the `image` shape still draws an `<img>` in the DOM overlay. |
|
|
641
|
-
| `
|
|
666
|
+
| `FrameShapeUtil` default size | mocanvas creates a frame at **160×90**; tldraw creates one at 320×180. Same aspect, half the size. It only bites code that creates a frame *programmatically* without passing `w`/`h` — drawing one with the tool sizes it from the drag either way. Pass explicit dimensions if the size matters to you. Aligning the default is a behaviour change and is not being made in a patch release. |
|
|
667
|
+
| An unregistered shape type in `store.put()` | Accepted, deliberately, and confirmed rather than fixed. A `.tldr` written by a build that knows a shape type this one does not must survive a load/save round trip instead of being dropped on the next save, so an unknown `type` passes its props through untouched. Its *other* fields are still validated — `x` must be a number whatever the type is — and a type you did register is validated in full. See `COMPAT.md`. |
|
|
642
668
|
| Text rendering | DOM overlay. Glyph-atlas text in WASM is phase 3. |
|
|
643
669
|
| GPU frame clipping | The `CLIP` flag and `isClipShape` hook are wired end to end, but no built-in shape enables it yet (phase 3). |
|
|
644
670
|
| WebGPU backend | Phase 3. WebGL2 is the only backend today, behind `RenderBackend`. |
|
package/README.md
CHANGED
|
@@ -48,6 +48,47 @@ const { status } = useSync(editor, { roomId, transport: () => createBroadcastCha
|
|
|
48
48
|
selection outlines in screen space. It positions itself absolutely, so it goes
|
|
49
49
|
anywhere inside the canvas container.
|
|
50
50
|
|
|
51
|
+
That is the whole integration: nothing above pins a page, and nothing names a
|
|
52
|
+
person. Two things make it work, and both are worth knowing because both used
|
|
53
|
+
to have to be worked around by hand.
|
|
54
|
+
|
|
55
|
+
### The page both replicas start on
|
|
56
|
+
|
|
57
|
+
A blank document's first page has a **fixed id** — `DEFAULT_PAGE_ID`, i.e.
|
|
58
|
+
`page:page` — so two `<Mocanvas />`s that each built their own store start on
|
|
59
|
+
the *same* page. Shapes drawn in one tab land on the page the other is looking
|
|
60
|
+
at, and `getCollaboratorsOnCurrentPage()` has something to return. (Creating
|
|
61
|
+
the same record id on two peers is a field-by-field merge, and the two bodies
|
|
62
|
+
are identical, so there is nothing to settle.) Pages created *after* that get
|
|
63
|
+
random ids, so two people genuinely on different pages of one document stay
|
|
64
|
+
independent.
|
|
65
|
+
|
|
66
|
+
When a replica does end up alone on its page — someone moved to another page,
|
|
67
|
+
or two apps built their documents separately — the client says so once, in
|
|
68
|
+
development:
|
|
69
|
+
|
|
70
|
+
> mocanvas/sync: this replica is on page `page:…`, and none of the 1 peer(s) in
|
|
71
|
+
> room "…" is — they are on `page:…`. Their cursors and their shapes will not be
|
|
72
|
+
> visible here.
|
|
73
|
+
|
|
74
|
+
Everything is still `online` in that state, every message still arrives, and
|
|
75
|
+
nothing is drawn. It is the failure with the least evidence, which is why it
|
|
76
|
+
gets a sentence instead of silence.
|
|
77
|
+
|
|
78
|
+
### A tab is not a person
|
|
79
|
+
|
|
80
|
+
Presence is per **editor instance**, not per user. `editor.user.getId()` is per
|
|
81
|
+
*browser* — the same in every tab, and the same on a phone and a laptop signed
|
|
82
|
+
in as one person — so a person with three tabs open would otherwise discard
|
|
83
|
+
their own other tabs as themselves. The identity that decides is
|
|
84
|
+
`editor.getInstancePresenceId()`: every editor mints one, this package
|
|
85
|
+
publishes the tab's presence record under it, and `editor.getCollaborators()`
|
|
86
|
+
drops that one record and no other.
|
|
87
|
+
|
|
88
|
+
So two tabs of one browser are two collaborators — which is the entire point of
|
|
89
|
+
`createBroadcastChannelTransport` — and an app does not have to invent a fake
|
|
90
|
+
per-tab identity to make its own demo work.
|
|
91
|
+
|
|
51
92
|
## Protocol
|
|
52
93
|
|
|
53
94
|
Every message is one JSON object on the wire.
|
|
@@ -93,7 +134,13 @@ half-parsed. Nothing on the wire can crash this client.
|
|
|
93
134
|
- Presence records are derived from the editor's camera,
|
|
94
135
|
`inputs.currentPagePoint` and selection, throttled to at most 30 Hz, and
|
|
95
136
|
re-sent on a heartbeat. A collaborator is dropped on `bye` or after 10s of
|
|
96
|
-
silence.
|
|
137
|
+
silence. The record is keyed on the sending editor's instance id (see
|
|
138
|
+
"A tab is not a person" above), so one person's tabs do not overwrite each
|
|
139
|
+
other's presence.
|
|
140
|
+
- A `hello` makes every established peer **re-announce** its presence, even
|
|
141
|
+
when its own record has not changed — what changed is who is listening. A
|
|
142
|
+
peer that has been sitting still since before you joined is therefore visible
|
|
143
|
+
immediately rather than at its next heartbeat.
|
|
97
144
|
|
|
98
145
|
## Conflict policy: a CRDT over the record fields
|
|
99
146
|
|
package/UI.md
CHANGED
|
@@ -4,6 +4,27 @@
|
|
|
4
4
|
style panel, an optional statistics chip, and the chrome the editor draws on the
|
|
5
5
|
canvas itself. All of it is optional and all of it is themeable from CSS.
|
|
6
6
|
|
|
7
|
+
## Loading the stylesheet
|
|
8
|
+
|
|
9
|
+
The theme is published as `@mocanvas/mocanvas/mocanvas.css` and your app
|
|
10
|
+
imports it:
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
import "@mocanvas/mocanvas/mocanvas.css"
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
The package's JavaScript entry does not import it. That is deliberate: Node has
|
|
17
|
+
no loader for `.css`, so a CSS import inside `dist/index.js` makes
|
|
18
|
+
`import "@mocanvas/mocanvas"` throw `ERR_UNKNOWN_FILE_EXTENSION` anywhere there
|
|
19
|
+
is no bundler — vitest, SSR, a plain script. Up to and including 4.0.2 it did,
|
|
20
|
+
and the emitted file was content-hashed (`dist/ui-4C5V5GYT.css`) so there was no
|
|
21
|
+
stable name to import instead. Both are fixed; the cost is this one line in your
|
|
22
|
+
app.
|
|
23
|
+
|
|
24
|
+
`<Canvas />` from `@mocanvas/editor` carries fallback values for the canvas
|
|
25
|
+
chrome tokens, so an editor mounted without the stylesheet still renders
|
|
26
|
+
legibly — but the toolbar, panels, menus and dialogs need it.
|
|
27
|
+
|
|
7
28
|
Source lives in `packages/mocanvas/src/ui/`:
|
|
8
29
|
|
|
9
30
|
| File | What it holds |
|
|
@@ -122,8 +143,9 @@ so inline marks like the "mixed" badge keep the size they ask for.
|
|
|
122
143
|
<Mocanvas hideUi />
|
|
123
144
|
```
|
|
124
145
|
|
|
125
|
-
This drops the toolbar, zoom bar, style panel and stats chip.
|
|
126
|
-
|
|
146
|
+
This drops the toolbar, zoom bar, style panel and stats chip. The stylesheet is
|
|
147
|
+
imported by your app, not by the component, so the canvas chrome tokens above
|
|
148
|
+
keep working. Default keyboard
|
|
127
149
|
shortcuts are wired by `<Mocanvas />` itself and are unaffected; call
|
|
128
150
|
`useKeyboardShortcuts(editor)` yourself if you build on `<Canvas />` directly.
|
|
129
151
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Signal } from '@mocanvas/state';
|
|
2
2
|
import { Store, UnknownRecord, RecordsDiff } from '@mocanvas/store';
|
|
3
|
-
import { UserId,
|
|
3
|
+
import { UserId, InstancePresenceId, InstancePresence, Editor } from '@mocanvas/editor';
|
|
4
4
|
import * as react from 'react';
|
|
5
5
|
|
|
6
6
|
/** Presence is sent at most this often (30 Hz). */
|
|
@@ -47,6 +47,16 @@ interface PresenceEditor {
|
|
|
47
47
|
scribbles: readonly unknown[];
|
|
48
48
|
followingUserId: UserId | null;
|
|
49
49
|
};
|
|
50
|
+
/**
|
|
51
|
+
* Optional: the id this instance's presence record is published under.
|
|
52
|
+
*
|
|
53
|
+
* A real `Editor` has one and it is the identity that matters here — a user
|
|
54
|
+
* id is per browser, so two tabs of one browser are one user and would
|
|
55
|
+
* discard each other's presence as their own. Structural stand-ins (tests,
|
|
56
|
+
* a headless participant) may omit it and fall back to
|
|
57
|
+
* {@link presenceIdForClient}.
|
|
58
|
+
*/
|
|
59
|
+
getInstancePresenceId?(): InstancePresenceId;
|
|
50
60
|
/** Optional: used to sample the cursor, which is not itself a signal. */
|
|
51
61
|
on?(name: "event", fn: () => void): () => void;
|
|
52
62
|
}
|
|
@@ -61,8 +71,17 @@ interface PresenceSyncOptions {
|
|
|
61
71
|
interface PresenceSync {
|
|
62
72
|
start(): void;
|
|
63
73
|
stop(): void;
|
|
64
|
-
/**
|
|
65
|
-
|
|
74
|
+
/**
|
|
75
|
+
* Ask for a send; collapses with any other request inside the throttle
|
|
76
|
+
* window.
|
|
77
|
+
*
|
|
78
|
+
* `force` sends even when nothing about this client has changed, which is
|
|
79
|
+
* what a *re-announcement* is: somebody who was not listening when we last
|
|
80
|
+
* spoke has arrived, so the record has to go out again although it says the
|
|
81
|
+
* same thing. Without it a peer that has been sitting still since before the
|
|
82
|
+
* newcomer joined stays invisible until its next heartbeat.
|
|
83
|
+
*/
|
|
84
|
+
poke(force?: boolean): void;
|
|
66
85
|
/** The record last handed to `send`, for tests and debugging. */
|
|
67
86
|
getLastSent(): InstancePresence | null;
|
|
68
87
|
dispose(): void;
|
|
@@ -443,6 +462,11 @@ interface CollaboratorCursorsProps {
|
|
|
443
462
|
/**
|
|
444
463
|
* Other people's cursors and selections, drawn in screen space above the
|
|
445
464
|
* canvas. Drop it inside `<Mocanvas>` or `<Canvas>`; it positions itself.
|
|
465
|
+
*
|
|
466
|
+
* The arrow below is the default. An app that supplies a
|
|
467
|
+
* {@link TLEditorComponents.CollaboratorCursor} component gets that instead —
|
|
468
|
+
* the slot was declared on two component maps and consulted by nothing, so an
|
|
469
|
+
* app that replaced the cursor silently kept seeing ours.
|
|
446
470
|
*/
|
|
447
471
|
declare const CollaboratorCursors: ({ editor, showSelection, showNames, }: CollaboratorCursorsProps) => react.JSX.Element | null;
|
|
448
472
|
interface UseSyncOptions {
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { react, atom } from '@mocanvas/state';
|
|
2
|
+
import { useEditorComponents, InstancePresenceRecordType, warnOnce } from '@mocanvas/editor';
|
|
2
3
|
import { uniqueId, isRecordsDiffEmpty, createEmptyRecordsDiff } from '@mocanvas/store';
|
|
3
|
-
import { InstancePresenceRecordType } from '@mocanvas/editor';
|
|
4
4
|
import { track, useValue } from '@mocanvas/state/react';
|
|
5
5
|
import { useRef, useState, useEffect } from 'react';
|
|
6
|
-
import {
|
|
6
|
+
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
7
7
|
|
|
8
8
|
// src/SyncClient.ts
|
|
9
9
|
var CRDT_STATE_VERSION = 1;
|
|
@@ -504,7 +504,7 @@ function createPresenceSync(options) {
|
|
|
504
504
|
const { editor, clientId, send } = options;
|
|
505
505
|
const throttleMs = options.throttleMs ?? DEFAULT_PRESENCE_THROTTLE_MS;
|
|
506
506
|
const heartbeatMs = options.heartbeatMs ?? DEFAULT_PRESENCE_HEARTBEAT_MS;
|
|
507
|
-
const id = presenceIdForClient(clientId);
|
|
507
|
+
const id = editor.getInstancePresenceId?.() ?? presenceIdForClient(clientId);
|
|
508
508
|
let timer = null;
|
|
509
509
|
let heartbeat = null;
|
|
510
510
|
let stopReactor = null;
|
|
@@ -538,11 +538,16 @@ function createPresenceSync(options) {
|
|
|
538
538
|
last = next;
|
|
539
539
|
send(next);
|
|
540
540
|
};
|
|
541
|
-
|
|
542
|
-
|
|
541
|
+
let pendingForce = false;
|
|
542
|
+
const poke = (force = false) => {
|
|
543
|
+
if (!running) return;
|
|
544
|
+
if (force) pendingForce = true;
|
|
545
|
+
if (timer !== null) return;
|
|
543
546
|
timer = setTimeout(() => {
|
|
544
547
|
timer = null;
|
|
545
|
-
|
|
548
|
+
const forced = pendingForce;
|
|
549
|
+
pendingForce = false;
|
|
550
|
+
flush(forced);
|
|
546
551
|
}, throttleMs);
|
|
547
552
|
};
|
|
548
553
|
return {
|
|
@@ -564,6 +569,7 @@ function createPresenceSync(options) {
|
|
|
564
569
|
},
|
|
565
570
|
stop() {
|
|
566
571
|
running = false;
|
|
572
|
+
pendingForce = false;
|
|
567
573
|
if (timer !== null) {
|
|
568
574
|
clearTimeout(timer);
|
|
569
575
|
timer = null;
|
|
@@ -736,6 +742,8 @@ function createSyncClient(options) {
|
|
|
736
742
|
});
|
|
737
743
|
const unsubscribes = [];
|
|
738
744
|
const reportedVersions = /* @__PURE__ */ new Set();
|
|
745
|
+
const peerPages = /* @__PURE__ */ new Map();
|
|
746
|
+
let warnedAboutPages = false;
|
|
739
747
|
let presence = null;
|
|
740
748
|
let seq = 0;
|
|
741
749
|
let awaitingSnapshot = false;
|
|
@@ -830,7 +838,7 @@ function createSyncClient(options) {
|
|
|
830
838
|
}
|
|
831
839
|
awaitingSnapshot = false;
|
|
832
840
|
sendSnapshot();
|
|
833
|
-
presence?.poke();
|
|
841
|
+
presence?.poke(true);
|
|
834
842
|
return;
|
|
835
843
|
}
|
|
836
844
|
case "snapshot": {
|
|
@@ -864,21 +872,38 @@ function createSyncClient(options) {
|
|
|
864
872
|
case "presence": {
|
|
865
873
|
if (message.clientId === clientId) return;
|
|
866
874
|
room.receive(message.clientId, message.record);
|
|
875
|
+
notePeerPage(message.clientId, message.record);
|
|
867
876
|
return;
|
|
868
877
|
}
|
|
869
878
|
case "bye": {
|
|
870
879
|
if (message.clientId === clientId) return;
|
|
871
880
|
room.remove(message.clientId);
|
|
881
|
+
peerPages.delete(message.clientId);
|
|
872
882
|
return;
|
|
873
883
|
}
|
|
874
884
|
}
|
|
875
885
|
};
|
|
886
|
+
const notePeerPage = (peerId, record) => {
|
|
887
|
+
const editor = options.presence?.editor;
|
|
888
|
+
if (!editor || warnedAboutPages) return;
|
|
889
|
+
const page = record.currentPageId;
|
|
890
|
+
if (typeof page !== "string") return;
|
|
891
|
+
peerPages.set(peerId, page);
|
|
892
|
+
const mine = editor.getCurrentPageId();
|
|
893
|
+
for (const peerPage of peerPages.values()) if (peerPage === mine) return;
|
|
894
|
+
warnedAboutPages = true;
|
|
895
|
+
const theirs = Array.from(new Set(peerPages.values())).join(", ");
|
|
896
|
+
warnOnce(
|
|
897
|
+
`sync.page:${roomId}:${clientId}`,
|
|
898
|
+
`mocanvas/sync: this replica is on page ${mine}, and none of the ${peerPages.size} peer(s) in room "${roomId}" is \u2014 they are on ${theirs}. Their cursors and their shapes will not be visible here. That is correct if you moved to another page on purpose; if you did not, the two replicas are on pages that were built separately. Every editor that starts from a blank document starts on the same default page, so check whether one side pinned, created or loaded a page of its own \u2014 and if so, call editor.setCurrentPage() with a page id both sides agree on.`
|
|
899
|
+
);
|
|
900
|
+
};
|
|
876
901
|
const onOpen = () => {
|
|
877
902
|
if (disposed || !connected) return;
|
|
878
903
|
status.set("online");
|
|
879
904
|
send({ type: "hello", clientId, version: PROTOCOL_VERSION });
|
|
880
905
|
if (!awaitingSnapshot) sendSnapshot();
|
|
881
|
-
presence?.poke();
|
|
906
|
+
presence?.poke(true);
|
|
882
907
|
};
|
|
883
908
|
const onClose = () => {
|
|
884
909
|
if (disposed || !connected) return;
|
|
@@ -1138,12 +1163,29 @@ var CollaboratorCursors = track(function CollaboratorCursors2({
|
|
|
1138
1163
|
showSelection = true,
|
|
1139
1164
|
showNames = true
|
|
1140
1165
|
}) {
|
|
1166
|
+
const components = useEditorComponents();
|
|
1141
1167
|
const collaborators = editor.getCollaboratorsOnCurrentPage();
|
|
1142
1168
|
if (collaborators.length === 0) return null;
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
/* @__PURE__ */ jsx(
|
|
1146
|
-
|
|
1169
|
+
const Slot = components.CollaboratorCursor;
|
|
1170
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1171
|
+
/* @__PURE__ */ jsx("svg", { className: "mocanvas-collaborators", style: layerStyle, children: collaborators.map((presence) => /* @__PURE__ */ jsxs("g", { children: [
|
|
1172
|
+
showSelection ? /* @__PURE__ */ jsx(CollaboratorSelection, { editor, presence }) : null,
|
|
1173
|
+
Slot === void 0 ? /* @__PURE__ */ jsx(CollaboratorCursor, { editor, presence, showName: showNames }) : null
|
|
1174
|
+
] }, presence.id)) }),
|
|
1175
|
+
Slot ? /* @__PURE__ */ jsx("div", { className: "mocanvas-collaborator-cursors", style: layerStyle, children: collaborators.map(
|
|
1176
|
+
(presence) => presence.cursor === null ? null : /* @__PURE__ */ jsx(
|
|
1177
|
+
Slot,
|
|
1178
|
+
{
|
|
1179
|
+
type: "default",
|
|
1180
|
+
rotation: presence.cursor.rotation,
|
|
1181
|
+
color: presence.color,
|
|
1182
|
+
name: showNames ? presence.userName || "Anonymous" : null,
|
|
1183
|
+
point: editor.pageToViewport(presence.cursor)
|
|
1184
|
+
},
|
|
1185
|
+
presence.id
|
|
1186
|
+
)
|
|
1187
|
+
) }) : null
|
|
1188
|
+
] });
|
|
1147
1189
|
});
|
|
1148
1190
|
var CollaboratorCursor = track(function CollaboratorCursor2({
|
|
1149
1191
|
editor,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/crdt.ts","../src/presence.ts","../src/protocol.ts","../src/SyncClient.ts","../src/transport.ts","../src/react.tsx"],"names":["createEmptyRecordsDiff","CollaboratorCursors","CollaboratorCursor","CollaboratorSelection"],"mappings":"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","file":"index.js","sourcesContent":["import { createEmptyRecordsDiff, type IdOf, type RecordsDiff, type UnknownRecord } from \"@mocanvas/store\"\n\n/**\n * A state-based CRDT over record fields.\n *\n * Every leaf field of every record is its own last-writer-wins register,\n * keyed by a dotted path (`x`, `props.w`, `meta.notes.title`). A write carries\n * a Lamport stamp; the greater stamp wins, and equal Lamport values are broken\n * by client id so that both sides of a race reach the same answer from the\n * same pair. Deletion is a stamped tombstone rather than a removal, so a late\n * message cannot resurrect a record that was deleted after it was written.\n *\n * Consequences worth knowing before reading the code:\n *\n * - Arrays are opaque. `props.points` is one register holding the whole array,\n * not one register per element. Two concurrent edits to one array pick a\n * winner instead of merging; splitting arrays into elements would need\n * element identity, which record data does not carry.\n * - A stamp covers a whole subtree. Writing `meta` (setting it, or deleting\n * the key) dominates `meta.a`: the claim loses to any stamp at or above its\n * own path, and dropping a subtree keeps descendants stamped later than the\n * write. Without that, `delete meta` racing `set meta.a` lands differently\n * depending on arrival order.\n * - A record also has one `base` register: the stamp at which some sender's\n * whole record body was current. Fields nobody holds a stamp for — a record\n * materialized without its creation message, or one that came back from\n * under a tombstone, which erases field stamps — are governed by that single\n * register, so every replica takes its body from the same message instead of\n * from whichever one happened to arrive first.\n * - Merging is idempotent (the same message twice is a no-op the second time)\n * and commutative (per path the result is the maximum stamp, which does not\n * depend on arrival order).\n * - The merge is a pure function of the stamps; values only ever ride along.\n */\n\n/** Version of the `CrdtState` blob on the wire. */\nexport const CRDT_STATE_VERSION = 1\n\n/** How many collected-and-gone records keep a tombstone before it is dropped. */\nexport const DEFAULT_TOMBSTONE_LIMIT = 5000\n/** How long a tombstone for a vanished record is kept. */\nexport const DEFAULT_TOMBSTONE_MAX_AGE_MS = 60 * 60 * 1000\n\n/* -------------------------------------------------------------------------- */\n/* stamps */\n/* -------------------------------------------------------------------------- */\n\n/** A point in the partial order of writes, made total by the client tiebreak. */\nexport interface Stamp {\n readonly lamport: number\n readonly client: string\n}\n\n/**\n * A total order on stamps: Lamport first, then client id. Two peers holding\n * the same pair always agree, which is what makes the merge deterministic.\n */\nexport function compareStamps(a: Stamp, b: Stamp): number {\n if (a.lamport !== b.lamport) return a.lamport < b.lamport ? -1 : 1\n if (a.client === b.client) return 0\n return a.client < b.client ? -1 : 1\n}\n\nexport function isStamp(value: unknown): value is Stamp {\n if (typeof value !== \"object\" || value === null) return false\n const stamp = value as { lamport?: unknown; client?: unknown }\n return typeof stamp.lamport === \"number\" && Number.isFinite(stamp.lamport) && typeof stamp.client === \"string\"\n}\n\nexport interface LamportClock {\n /** The current value. */\n get(): number\n /** Advance for a local write and return the new value. */\n tick(): number\n /** Fold in a stamp seen from a peer: `max(local, remote) + 1`. */\n observe(remote: number): void\n}\n\nexport function createLamportClock(start = 0): LamportClock {\n let value = start\n return {\n get: () => value,\n tick: () => (value += 1),\n observe(remote: number) {\n if (!Number.isFinite(remote)) return\n value = Math.max(value, remote) + 1\n },\n }\n}\n\n/* -------------------------------------------------------------------------- */\n/* wire shapes */\n/* -------------------------------------------------------------------------- */\n\n/**\n * One record's changed fields. `record` is the whole record as the sender saw\n * it after the change; `fields` says which paths this message actually claims.\n * A receiver that already has the record takes only the winning paths out of\n * `record`; a receiver that does not have it materializes from `record` whole.\n */\nexport interface StampedPut<R extends UnknownRecord = UnknownRecord> {\n record: R\n fields: Record<string, Stamp>\n /**\n * The stamp at which `record` was the sender's whole view of that record.\n * Paths the receiver holds no stamp for are taken from `record` when this is\n * the greatest such stamp it has seen, and left alone otherwise.\n */\n base?: Stamp\n}\n\nexport interface StampedRemove {\n id: string\n stamp: Stamp\n}\n\n/** What a `diff` message carries: puts and removes, each field stamped. */\nexport interface StampedDiff<R extends UnknownRecord = UnknownRecord> {\n puts: StampedPut<R>[]\n removes: StampedRemove[]\n}\n\nexport function createEmptyStampedDiff<R extends UnknownRecord = UnknownRecord>(): StampedDiff<R> {\n return { puts: [], removes: [] }\n}\n\nexport function isStampedDiffEmpty<R extends UnknownRecord>(diff: StampedDiff<R>): boolean {\n return diff.puts.length === 0 && diff.removes.length === 0\n}\n\n/** Structural check used by `decodeMessage`; deliberately shallow but strict. */\nexport function isStampedDiffLike(value: unknown): boolean {\n if (typeof value !== \"object\" || value === null) return false\n const diff = value as { puts?: unknown; removes?: unknown }\n if (!Array.isArray(diff.puts) || !Array.isArray(diff.removes)) return false\n for (const put of diff.puts) {\n if (typeof put !== \"object\" || put === null) return false\n const { record, fields, base } = put as { record?: unknown; fields?: unknown; base?: unknown }\n if (typeof record !== \"object\" || record === null) return false\n if (typeof (record as { id?: unknown }).id !== \"string\") return false\n if (typeof (record as { typeName?: unknown }).typeName !== \"string\") return false\n if (!isStampMap(fields)) return false\n if (base !== undefined && !isStamp(base)) return false\n }\n for (const remove of diff.removes) {\n if (typeof remove !== \"object\" || remove === null) return false\n const { id, stamp } = remove as { id?: unknown; stamp?: unknown }\n if (typeof id !== \"string\" || !isStamp(stamp)) return false\n }\n return true\n}\n\n/** What one record's registers look like once serialized. */\nexport interface CrdtRecordState {\n fields: Record<string, Stamp>\n /** The stamp of the message this record's unstamped fields came from. */\n base?: Stamp\n deleted?: Stamp\n /** Local wall clock at the time the tombstone was taken, for collection. */\n deletedAt?: number\n}\n\n/** The whole replica's stamps, as carried by a `snapshot` message. */\nexport interface CrdtState {\n version: number\n clock: number\n records: Record<string, CrdtRecordState>\n}\n\nexport function createEmptyCrdtState(): CrdtState {\n return { version: CRDT_STATE_VERSION, clock: 0, records: {} }\n}\n\nexport function isCrdtStateLike(value: unknown): boolean {\n if (typeof value !== \"object\" || value === null) return false\n const state = value as { version?: unknown; clock?: unknown; records?: unknown }\n if (typeof state.version !== \"number\") return false\n if (typeof state.clock !== \"number\") return false\n if (typeof state.records !== \"object\" || state.records === null || Array.isArray(state.records)) return false\n for (const key of Object.keys(state.records as Record<string, unknown>)) {\n const record = (state.records as Record<string, unknown>)[key]\n if (typeof record !== \"object\" || record === null) return false\n const { fields, deleted, base } = record as { fields?: unknown; deleted?: unknown; base?: unknown }\n if (!isStampMap(fields)) return false\n if (deleted !== undefined && !isStamp(deleted)) return false\n if (base !== undefined && !isStamp(base)) return false\n }\n return true\n}\n\nfunction isStampMap(value: unknown): boolean {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return false\n for (const key of Object.keys(value as Record<string, unknown>)) {\n if (!isStamp((value as Record<string, unknown>)[key])) return false\n }\n return true\n}\n\n/* -------------------------------------------------------------------------- */\n/* the CRDT */\n/* -------------------------------------------------------------------------- */\n\nexport interface CrdtOptions<R extends UnknownRecord = UnknownRecord> {\n /** Identifies this replica; also the tiebreak in `compareStamps`. */\n clientId: string\n /**\n * Read the replica's current value for a record. The CRDT owns the stamps,\n * never the values: the store stays the source of truth and a merge is\n * expressed as a diff against whatever `getRecord` reports.\n */\n getRecord?: ((id: string) => R | undefined) | undefined\n /** Tombstones kept for records that are gone. Default 5000. */\n tombstoneLimit?: number | undefined\n /** How long such a tombstone lives. Default one hour. */\n tombstoneMaxAgeMs?: number | undefined\n /** Injectable wall clock, for tests. */\n now?: (() => number) | undefined\n}\n\nexport interface Crdt<R extends UnknownRecord = UnknownRecord> {\n readonly clientId: string\n readonly clock: LamportClock\n /**\n * Stamp a diff the local user just made and return the message to send.\n * The whole diff shares one stamp: it was one operation.\n */\n stampLocal(diff: RecordsDiff<R>): StampedDiff<R>\n /**\n * Merge a peer's message. The returned diff holds only the fields that won,\n * as whole records the store can apply — never a blind overwrite.\n */\n mergeRemote(stamped: StampedDiff<R>): RecordsDiff<R>\n /** This replica's stamps, for a `snapshot` message. */\n getState(): CrdtState\n /**\n * Adopt a peer's stamps wholesale. This is what a replica that is joining\n * with no state of its own does, together with that peer's records: it takes\n * the peer's stamps instead of starting blank, so its own later writes sort\n * after everything already in the room. The returned diff removes whatever\n * the peer has tombstoned and we still hold. Only for a replica holding no\n * stamps at all: the caller adopts the peer's records wholesale alongside it,\n * so a replica with work of its own must merge the snapshot through\n * `stampedDiffFromSnapshot` instead of taking it.\n */\n applyState(state: CrdtState): RecordsDiff<R>\n /**\n * Writes this replica minted while merging, which the caller must broadcast.\n * A record kept alive by an edit that outranks a delete is re-claimed whole\n * under a fresh stamp: the tombstone leaves the fields it dominates with no\n * provenance at all, and only a stamped body can put every replica on the\n * same one. Empty when there is nothing to send.\n */\n takeOutgoing(): StampedDiff<R> | null\n /** Number of records the state currently tracks (tests and diagnostics). */\n size(): number\n /** Drop tombstones past the count or age bound. Called after every merge. */\n collectTombstones(): void\n}\n\ninterface RecordState {\n fields: Map<string, Stamp>\n /** The stamp of the message this record's unstamped fields came from. */\n base?: Stamp | undefined\n deleted?: Stamp | undefined\n deletedAt?: number | undefined\n}\n\n/** The greater of two stamps, either of which may be missing. */\nfunction maxStamp(a: Stamp | undefined, b: Stamp | undefined): Stamp | undefined {\n if (!a) return b\n if (!b) return a\n return compareStamps(a, b) >= 0 ? a : b\n}\n\n/** The greatest stamp a put claims, which is what its body is current as of. */\nfunction greatestStamp(fields: Record<string, Stamp>): Stamp | undefined {\n let best: Stamp | undefined\n for (const path of Object.keys(fields)) best = maxStamp(best, fields[path])\n return best\n}\n\n/** Every proper ancestor of a dotted path, outermost first. */\nfunction ancestorPaths(path: string): string[] {\n const out: string[] = []\n let index = path.indexOf(\".\")\n while (index !== -1) {\n out.push(path.slice(0, index))\n index = path.indexOf(\".\", index + 1)\n }\n return out\n}\n\n/**\n * Take `path` for `stamp`, unless something at or above it is stamped at least\n * as late — a write to `meta` covers `meta.a`, so the two cannot be compared\n * as if they were unrelated registers. Descendants the write covers are\n * dropped; descendants stamped later than it survive and are returned, because\n * their values have to be put back on top of whatever the write does.\n */\nfunction claimPath(state: RecordState, path: string, stamp: Stamp): string[] | null {\n const own = state.fields.get(path)\n if (own && compareStamps(stamp, own) <= 0) return null\n for (const ancestor of ancestorPaths(path)) {\n const above = state.fields.get(ancestor)\n if (above && compareStamps(stamp, above) <= 0) return null\n }\n const survivors: string[] = []\n const prefix = `${path}.`\n for (const [other, existing] of Array.from(state.fields)) {\n if (!other.startsWith(prefix)) continue\n if (compareStamps(existing, stamp) <= 0) state.fields.delete(other)\n else survivors.push(other)\n }\n state.fields.set(path, stamp)\n return survivors\n}\n\n/** True when this path, or one above it, carries a stamp of its own. */\nfunction isStamped(state: RecordState, path: string): boolean {\n if (state.fields.has(path)) return true\n for (const ancestor of ancestorPaths(path)) {\n if (state.fields.has(ancestor)) return true\n }\n return false\n}\n\n/**\n * The highest ancestor of `path` that the body has nothing at and no stamp\n * covers — the level a \"this is not here any more\" has to be applied at.\n */\nfunction highestAbsent(body: unknown, state: RecordState, path: string): string {\n for (const ancestor of ancestorPaths(path)) {\n if (readPath(body, ancestor) !== ABSENT) continue\n if (isStamped(state, ancestor) || hasStampedDescendant(state, ancestor)) continue\n return ancestor\n }\n return path\n}\n\n/** True when something strictly below this path carries a stamp. */\nfunction hasStampedDescendant(state: RecordState, path: string): boolean {\n const prefix = `${path}.`\n for (const other of state.fields.keys()) {\n if (other.startsWith(prefix)) return true\n }\n return false\n}\n\nexport function createCrdt<R extends UnknownRecord = UnknownRecord>(options: CrdtOptions<R>): Crdt<R> {\n const { clientId } = options\n const getRecord = options.getRecord ?? (() => undefined)\n const tombstoneLimit = options.tombstoneLimit ?? DEFAULT_TOMBSTONE_LIMIT\n const tombstoneMaxAgeMs = options.tombstoneMaxAgeMs ?? DEFAULT_TOMBSTONE_MAX_AGE_MS\n const now = options.now ?? (() => Date.now())\n const clock = createLamportClock()\n const states = new Map<string, RecordState>()\n let outgoing = createEmptyStampedDiff<R>()\n\n const stateFor = (id: string): RecordState => {\n let state = states.get(id)\n if (!state) {\n state = { fields: new Map() }\n states.set(id, state)\n }\n return state\n }\n\n /**\n * A merge in progress: `before` remembers what the store had when we first\n * looked at a record, `after` what it should end up with. Keeping both lets\n * one message both remove and re-create a record without contradicting\n * itself in the diff we hand back.\n */\n interface Pass {\n before: Map<string, R | undefined>\n after: Map<string, R | undefined>\n }\n\n const startPass = (): Pass => ({ before: new Map(), after: new Map() })\n\n const readBefore = (pass: Pass, id: string): R | undefined => {\n if (!pass.before.has(id)) pass.before.set(id, getRecord(id))\n return pass.before.get(id)\n }\n\n const readCurrent = (pass: Pass, id: string): R | undefined =>\n pass.after.has(id) ? pass.after.get(id) : readBefore(pass, id)\n\n const write = (pass: Pass, id: string, value: R | undefined): void => {\n readBefore(pass, id)\n pass.after.set(id, value)\n }\n\n const finishPass = (pass: Pass): RecordsDiff<R> => {\n const result = createEmptyRecordsDiff<R>()\n for (const [id, next] of pass.after) {\n const prev = pass.before.get(id)\n if (prev === next) continue\n const key = id as IdOf<R>\n if (prev === undefined) {\n if (next !== undefined) result.added[key] = next\n } else if (next === undefined) {\n result.removed[key] = prev\n } else {\n result.updated[key] = [prev, next]\n }\n }\n return result\n }\n\n /** Record a tombstone. Fields it dominates are dropped; survivors keep the record alive. */\n const applyRemove = (pass: Pass, id: string, stamp: Stamp): void => {\n const state = stateFor(id)\n if (state.deleted && compareStamps(stamp, state.deleted) <= 0) return\n state.deleted = stamp\n state.deletedAt = now()\n // The body older messages carry dies with the fields: only a message that\n // outranks the tombstone may supply one from here on.\n state.base = maxStamp(state.base, stamp)\n let alive = false\n for (const [path, existing] of Array.from(state.fields)) {\n if (compareStamps(existing, stamp) > 0) alive = true\n else state.fields.delete(path)\n }\n // A concurrent edit newer than the delete keeps the record: the delete only\n // wins when its stamp is greater than every field that would survive it.\n if (!alive) {\n if (readCurrent(pass, id) !== undefined) write(pass, id, undefined)\n return\n }\n // It survives — but the tombstone has just taken the provenance of every\n // field it outranks, and each replica is holding a different leftover for\n // those. Re-claim this body whole under a fresh stamp and send it, so the\n // record comes back as one agreed record rather than as a race.\n const record = readCurrent(pass, id)\n if (!record) return\n const revival: Stamp = { lamport: clock.tick(), client: clientId }\n const fields: Record<string, Stamp> = {}\n for (const path of leafPaths(record)) {\n fields[path] = revival\n claimPath(state, path, revival)\n }\n state.base = revival\n outgoing.puts.push({ record, fields, base: revival })\n }\n\n /**\n * Take every path neither the record nor the sender holds a stamp for from\n * the sender's body. Those are the fields the registers say nothing about,\n * and `base` is the one register that decides them.\n */\n const adoptBody = (record: R, body: R, state: RecordState): R => {\n const paths = new Set<string>([...leafPaths(body), ...leafPaths(record)])\n let next = record\n for (const path of Array.from(paths).sort()) {\n if (path === \"id\" || path === \"typeName\") continue\n if (isStamped(state, path) || hasStampedDescendant(state, path)) continue\n const value = readPath(body, path)\n // Take the whole subtree the body does not have, not just the leaf, so\n // dropping the last key of an object does not leave an empty one behind.\n next = writePath(next, value === ABSENT ? highestAbsent(body, state, path) : path, value)\n }\n return next\n }\n\n const applyPut = (pass: Pass, put: StampedPut<R>): void => {\n const id = put.record.id as string\n const state = stateFor(id)\n const claims: { path: string; survivors: string[] }[] = []\n for (const path of Object.keys(put.fields)) {\n const stamp = put.fields[path]\n if (!stamp) continue\n // A write older than the tombstone cannot resurrect the record.\n if (state.deleted && compareStamps(stamp, state.deleted) <= 0) continue\n // `<= 0` and not `< 0` inside: the same message twice changes nothing.\n const survivors = claimPath(state, path, stamp)\n if (survivors === null) continue\n claims.push({ path, survivors })\n }\n const base = put.base ?? greatestStamp(put.fields)\n const bodyWins = base !== undefined && (state.base === undefined || compareStamps(base, state.base) > 0)\n if (claims.length === 0 && !bodyWins) return\n\n const current = readCurrent(pass, id)\n if (current === undefined) {\n // Nothing here to merge into. A body alone must never bring a record\n // back: without a claim that outranks the tombstone this is a straggler.\n if (claims.length === 0) return\n write(pass, id, put.record)\n state.base = maxStamp(state.base, base)\n return\n }\n\n let next = current\n for (const { path, survivors } of claims) {\n if (path === \"id\") continue // identity is never merged\n const before = next\n next = writePath(next, path, readPath(put.record, path))\n // A subtree stamped later than this write outlives it, so put those\n // values back: `delete meta` racing `set meta.a` must land the same way\n // whichever arrives first.\n for (const survivor of survivors) next = writePath(next, survivor, readPath(before, survivor))\n }\n if (bodyWins) {\n next = adoptBody(next, put.record, state)\n state.base = base\n }\n if (next !== current) write(pass, id, next)\n }\n\n const observeStamps = (stamped: StampedDiff<R>): void => {\n let max = -1\n for (const put of stamped.puts) {\n for (const path of Object.keys(put.fields)) {\n const stamp = put.fields[path]\n if (stamp && stamp.lamport > max) max = stamp.lamport\n }\n if (put.base && put.base.lamport > max) max = put.base.lamport\n }\n for (const remove of stamped.removes) {\n if (remove.stamp.lamport > max) max = remove.stamp.lamport\n }\n if (max >= 0) clock.observe(max)\n }\n\n const collectTombstones = (): void => {\n const gone: { id: string; at: number }[] = []\n for (const [id, state] of states) {\n if (state.deleted && state.fields.size === 0) gone.push({ id, at: state.deletedAt ?? 0 })\n }\n if (gone.length === 0) return\n const cutoff = now() - tombstoneMaxAgeMs\n let overflow = gone.length - tombstoneLimit\n if (overflow <= 0 && gone.every((entry) => entry.at >= cutoff)) return\n gone.sort((a, b) => a.at - b.at)\n for (const entry of gone) {\n if (overflow > 0) {\n states.delete(entry.id)\n overflow--\n continue\n }\n if (entry.at < cutoff) states.delete(entry.id)\n }\n }\n\n return {\n clientId,\n clock,\n\n stampLocal(diff: RecordsDiff<R>): StampedDiff<R> {\n const stamp: Stamp = { lamport: clock.tick(), client: clientId }\n const result = createEmptyStampedDiff<R>()\n\n for (const id in diff.added) {\n const record = diff.added[id as IdOf<R>]\n if (!record) continue\n const fields: Record<string, Stamp> = {}\n const state = stateFor(id)\n for (const path of leafPaths(record)) {\n fields[path] = stamp\n claimPath(state, path, stamp)\n }\n // Our record is our whole view of it, as of now.\n state.base = stamp\n result.puts.push({ record, fields, base: stamp })\n }\n\n for (const id in diff.updated) {\n const pair = diff.updated[id as IdOf<R>]\n if (!pair) continue\n const [from, to] = pair\n const paths = changedPaths(from, to)\n if (paths.length === 0) continue\n const fields: Record<string, Stamp> = {}\n const state = stateFor(id)\n for (const path of paths) {\n fields[path] = stamp\n claimPath(state, path, stamp)\n }\n state.base = stamp\n result.puts.push({ record: to, fields, base: stamp })\n }\n\n for (const id in diff.removed) {\n const state = stateFor(id)\n state.deleted = stamp\n state.deletedAt = now()\n state.fields.clear()\n state.base = stamp\n result.removes.push({ id, stamp })\n }\n\n collectTombstones()\n return result\n },\n\n mergeRemote(stamped: StampedDiff<R>): RecordsDiff<R> {\n observeStamps(stamped)\n const pass = startPass()\n for (const remove of stamped.removes) applyRemove(pass, remove.id, remove.stamp)\n for (const put of stamped.puts) applyPut(pass, put)\n collectTombstones()\n return finishPass(pass)\n },\n\n getState(): CrdtState {\n const records: Record<string, CrdtRecordState> = {}\n for (const [id, state] of states) {\n const fields: Record<string, Stamp> = {}\n for (const [path, stamp] of state.fields) fields[path] = stamp\n records[id] = {\n fields,\n ...(state.base ? { base: state.base } : {}),\n ...(state.deleted ? { deleted: state.deleted, deletedAt: state.deletedAt ?? 0 } : {}),\n }\n }\n return { version: CRDT_STATE_VERSION, clock: clock.get(), records }\n },\n\n applyState(state: CrdtState): RecordsDiff<R> {\n const pass = startPass()\n if (state.version !== CRDT_STATE_VERSION) return finishPass(pass)\n clock.observe(state.clock)\n for (const id of Object.keys(state.records)) {\n const incoming = state.records[id]\n if (!incoming) continue\n if (incoming.deleted) applyRemove(pass, id, incoming.deleted)\n const local = stateFor(id)\n for (const path of Object.keys(incoming.fields)) {\n const stamp = incoming.fields[path]\n if (!stamp) continue\n clock.observe(stamp.lamport)\n if (local.deleted && compareStamps(stamp, local.deleted) <= 0) continue\n claimPath(local, path, stamp)\n }\n if (incoming.base) {\n clock.observe(incoming.base.lamport)\n local.base = maxStamp(local.base, incoming.base)\n }\n }\n collectTombstones()\n return finishPass(pass)\n },\n\n takeOutgoing(): StampedDiff<R> | null {\n if (isStampedDiffEmpty(outgoing)) return null\n const result = outgoing\n outgoing = createEmptyStampedDiff<R>()\n return result\n },\n\n size: () => states.size,\n collectTombstones,\n }\n}\n\n/**\n * Turn a peer's snapshot into a message the ordinary merge can eat: its\n * records supply the values, its state supplies the stamps. Paths the state\n * says nothing about are not claimed, so a snapshot can never overwrite a\n * field the receiver has a stamp for.\n */\nexport function stampedDiffFromSnapshot<R extends UnknownRecord>(\n records: readonly R[],\n state: CrdtState,\n): StampedDiff<R> {\n const result = createEmptyStampedDiff<R>()\n for (const record of records) {\n const entry = state.records[record.id as string]\n const fields = entry?.fields ?? {}\n const base = entry?.base ?? greatestStamp(fields)\n result.puts.push(base ? { record, fields, base } : { record, fields })\n }\n for (const id of Object.keys(state.records)) {\n const deleted = state.records[id]?.deleted\n if (deleted) result.removes.push({ id, stamp: deleted })\n }\n return result\n}\n\n/* -------------------------------------------------------------------------- */\n/* field paths */\n/* -------------------------------------------------------------------------- */\n\n/** A path that is not present at all, as distinct from one holding `undefined`. */\nconst ABSENT = Symbol(\"absent\")\n\nexport function isPlainObject(value: unknown): value is Record<string, unknown> {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return false\n const proto = Object.getPrototypeOf(value) as unknown\n return proto === Object.prototype || proto === null\n}\n\n/**\n * Every leaf path of a value. Plain objects are recursed into; arrays,\n * primitives, `null` and empty objects are leaves — an array is one register\n * holding the whole array.\n */\nexport function leafPaths(value: unknown, prefix = \"\"): string[] {\n const out: string[] = []\n collectLeaves(value, prefix, out)\n return out\n}\n\nfunction collectLeaves(value: unknown, prefix: string, out: string[]): void {\n if (isPlainObject(value)) {\n const keys = Object.keys(value)\n if (keys.length > 0) {\n for (const key of keys) collectLeaves(value[key], prefix ? `${prefix}.${key}` : key, out)\n return\n }\n }\n if (prefix !== \"\") out.push(prefix)\n}\n\n/** The paths whose values differ between two versions of a record. */\nexport function changedPaths(from: unknown, to: unknown): string[] {\n const out: string[] = []\n collectChanges(from, to, \"\", out)\n return out\n}\n\nfunction collectChanges(from: unknown, to: unknown, prefix: string, out: string[]): void {\n if (isPlainObject(from) && isPlainObject(to)) {\n for (const key of Object.keys(from)) {\n const path = prefix ? `${prefix}.${key}` : key\n // A key that disappeared: stamp the whole subtree as one removal.\n if (!(key in to)) out.push(path)\n else collectChanges(from[key], to[key], path, out)\n }\n for (const key of Object.keys(to)) {\n if (key in from) continue\n collectLeaves(to[key], prefix ? `${prefix}.${key}` : key, out)\n }\n return\n }\n if (!deepEqual(from, to) && prefix !== \"\") out.push(prefix)\n}\n\nfunction readPath(source: unknown, path: string): unknown {\n let current: unknown = source\n for (const segment of path.split(\".\")) {\n if (!isPlainObject(current) || !(segment in current)) return ABSENT\n current = current[segment]\n }\n return current\n}\n\n/**\n * A copy of `record` with `path` set to `value` (or the key deleted when the\n * path is absent in the source). Every level along the path is cloned, because\n * the store freezes records, their `props` and their `meta`.\n */\nfunction writePath<R extends UnknownRecord>(record: R, path: string, value: unknown): R {\n const current = readPath(record, path)\n if (value === ABSENT) {\n if (current === ABSENT) return record\n } else if (current !== ABSENT && deepEqual(current, value)) {\n return record\n }\n const segments = path.split(\".\")\n return setIn(record as unknown as Record<string, unknown>, segments, 0, value) as unknown as R\n}\n\nfunction setIn(\n target: Record<string, unknown>,\n segments: string[],\n index: number,\n value: unknown,\n): Record<string, unknown> {\n const key = segments[index]\n if (key === undefined) return target\n const next: Record<string, unknown> = { ...target }\n if (index === segments.length - 1) {\n if (value === ABSENT) delete next[key]\n else next[key] = value\n return next\n }\n const child = next[key]\n next[key] = setIn(isPlainObject(child) ? child : {}, segments, index + 1, value)\n return next\n}\n\nexport function deepEqual(a: unknown, b: unknown): boolean {\n if (Object.is(a, b)) return true\n if (Array.isArray(a)) {\n if (!Array.isArray(b) || a.length !== b.length) return false\n for (let i = 0; i < a.length; i++) {\n if (!deepEqual(a[i], b[i])) return false\n }\n return true\n }\n if (isPlainObject(a) && isPlainObject(b)) {\n const keysA = Object.keys(a)\n const keysB = Object.keys(b)\n if (keysA.length !== keysB.length) return false\n for (const key of keysA) {\n if (!(key in b)) return false\n if (!deepEqual(a[key], b[key])) return false\n }\n return true\n }\n return false\n}\n","import { react } from \"@mocanvas/state\"\nimport {\n InstancePresenceRecordType,\n type Editor,\n type InstancePresence,\n type InstancePresenceId,\n type UserId,\n} from \"@mocanvas/editor\"\nimport type { IdOf, Store, UnknownRecord } from \"@mocanvas/store\"\n\n/** Presence is sent at most this often (30 Hz). */\nexport const DEFAULT_PRESENCE_THROTTLE_MS = Math.ceil(1000 / 30)\n/** A presence record is re-sent this often even when nothing changed. */\nexport const DEFAULT_PRESENCE_HEARTBEAT_MS = 3000\n/** A collaborator we have not heard from in this long is dropped. */\nexport const DEFAULT_PRESENCE_TIMEOUT_MS = 10_000\n\n/**\n * The part of the editor presence reads. Declared structurally so that the\n * sync layer does not need a live `Editor` (and its WASM engine) to be tested.\n */\nexport interface PresenceEditor {\n readonly store: Store<any, any>\n readonly inputs: { readonly currentPagePoint: { x: number; y: number } }\n readonly user: { getId(): UserId; getName(): string; getColor(): string }\n getCurrentPageId(): string\n getCamera(): { x: number; y: number; z: number }\n getSelectedShapeIds(): readonly string[]\n getInstanceState(): {\n cursor: { type: string; rotation: number }\n brush: { x: number; y: number; w: number; h: number } | null\n scribbles: readonly unknown[]\n followingUserId: UserId | null\n }\n /** Optional: used to sample the cursor, which is not itself a signal. */\n on?(name: \"event\", fn: () => void): () => void\n}\n\n// A real Editor must satisfy the structural interface above; this fails to\n// compile if the two ever drift apart.\ntype Assert<T extends true> = T\nexport type EditorIsPresenceEditor = Assert<Editor extends PresenceEditor ? true : false>\n\nexport interface PresenceSyncOptions {\n editor: PresenceEditor\n /** Identifies this tab; one user may have several. */\n clientId: string\n send(record: InstancePresence): void\n throttleMs?: number\n heartbeatMs?: number\n}\n\nexport interface PresenceSync {\n start(): void\n stop(): void\n /** Ask for a send; collapses with any other request inside the throttle window. */\n poke(): void\n /** The record last handed to `send`, for tests and debugging. */\n getLastSent(): InstancePresence | null\n dispose(): void\n}\n\n/** The id a client's presence record always uses, so updates overwrite in place. */\nexport function presenceIdForClient(clientId: string): InstancePresenceId {\n return InstancePresenceRecordType.createId(clientId) as InstancePresenceId\n}\n\n/**\n * Watches the editor and pushes the local presence record out, at most once per\n * throttle window, plus a heartbeat so that idle collaborators do not time out.\n */\nexport function createPresenceSync(options: PresenceSyncOptions): PresenceSync {\n const { editor, clientId, send } = options\n const throttleMs = options.throttleMs ?? DEFAULT_PRESENCE_THROTTLE_MS\n const heartbeatMs = options.heartbeatMs ?? DEFAULT_PRESENCE_HEARTBEAT_MS\n const id = presenceIdForClient(clientId)\n\n let timer: ReturnType<typeof setTimeout> | null = null\n let heartbeat: ReturnType<typeof setInterval> | null = null\n let stopReactor: (() => void) | null = null\n let stopEvents: (() => void) | null = null\n let last: InstancePresence | null = null\n let running = false\n\n const build = (): InstancePresence => {\n const instance = editor.getInstanceState()\n const point = editor.inputs.currentPagePoint\n return InstancePresenceRecordType.create({\n id,\n userId: editor.user.getId(),\n userName: editor.user.getName(),\n color: editor.user.getColor(),\n currentPageId: editor.getCurrentPageId() as InstancePresence[\"currentPageId\"],\n cursor: { x: point.x, y: point.y, type: instance.cursor.type, rotation: instance.cursor.rotation },\n camera: { ...editor.getCamera() },\n selectedShapeIds: [...editor.getSelectedShapeIds()] as InstancePresence[\"selectedShapeIds\"],\n brush: instance.brush ? { ...instance.brush } : null,\n scribbles: [...instance.scribbles] as InstancePresence[\"scribbles\"],\n followingUserId: instance.followingUserId,\n lastActivityTimestamp: Date.now(),\n chatMessage: \"\",\n meta: {},\n })\n }\n\n const flush = (force: boolean) => {\n if (!running) return\n const next = build()\n if (!force && last && isSamePresence(last, next)) return\n last = next\n send(next)\n }\n\n const poke = () => {\n if (!running || timer !== null) return\n timer = setTimeout(() => {\n timer = null\n flush(false)\n }, throttleMs)\n }\n\n return {\n start() {\n if (running) return\n running = true\n // Camera, page and selection are signals, so a reactor covers them.\n stopReactor = react(\"sync.presence\", () => {\n editor.getCamera()\n editor.getCurrentPageId()\n editor.getSelectedShapeIds()\n editor.getInstanceState()\n editor.user.getName()\n editor.user.getColor()\n poke()\n })\n // The pointer position lives on `inputs`, which is not reactive.\n stopEvents = editor.on?.(\"event\", poke) ?? null\n heartbeat = setInterval(() => flush(true), heartbeatMs)\n flush(true)\n },\n stop() {\n running = false\n if (timer !== null) {\n clearTimeout(timer)\n timer = null\n }\n if (heartbeat !== null) {\n clearInterval(heartbeat)\n heartbeat = null\n }\n stopReactor?.()\n stopReactor = null\n stopEvents?.()\n stopEvents = null\n },\n poke,\n getLastSent: () => last,\n dispose() {\n this.stop()\n last = null\n },\n }\n}\n\n/** Equal apart from `lastActivityTimestamp`, which always moves. */\nexport function isSamePresence(a: InstancePresence, b: InstancePresence): boolean {\n return (\n a.userId === b.userId &&\n a.userName === b.userName &&\n a.color === b.color &&\n a.currentPageId === b.currentPageId &&\n a.chatMessage === b.chatMessage &&\n a.followingUserId === b.followingUserId &&\n sameCursor(a.cursor, b.cursor) &&\n sameCamera(a.camera, b.camera) &&\n sameIds(a.selectedShapeIds, b.selectedShapeIds) &&\n sameBrush(a.brush, b.brush) &&\n a.scribbles.length === b.scribbles.length &&\n a.scribbles.every((s, i) => s === b.scribbles[i])\n )\n}\n\n/** Cameras compare equal when both are absent, or when all three fields match. */\nfunction sameCamera(a: InstancePresence[\"camera\"], b: InstancePresence[\"camera\"]): boolean {\n if (a === null || b === null) return a === b\n return a.x === b.x && a.y === b.y && a.z === b.z\n}\n\n/** Cursors compare equal when both are absent, or when all four fields match. */\nfunction sameCursor(a: InstancePresence[\"cursor\"], b: InstancePresence[\"cursor\"]): boolean {\n if (a === null || b === null) return a === b\n return a.x === b.x && a.y === b.y && a.type === b.type && a.rotation === b.rotation\n}\n\nfunction sameIds(a: readonly string[], b: readonly string[]): boolean {\n return a.length === b.length && a.every((id, i) => id === b[i])\n}\n\nfunction sameBrush(a: InstancePresence[\"brush\"], b: InstancePresence[\"brush\"]): boolean {\n if (a === b) return true\n if (!a || !b) return false\n return a.x === b.x && a.y === b.y && a.w === b.w && a.h === b.h\n}\n\n/**\n * Holds the presence records of everyone else: puts them in the store as they\n * arrive, drops them on `bye`, and sweeps clients that went quiet.\n */\nexport class PresenceRoom<R extends UnknownRecord> {\n private readonly seen = new Map<string, { id: string; at: number }>()\n private sweeper: ReturnType<typeof setInterval> | null = null\n\n constructor(\n private readonly store: Store<R, any>,\n private readonly timeoutMs: number = DEFAULT_PRESENCE_TIMEOUT_MS,\n ) {}\n\n /** Store an incoming presence record and remember when we saw its client. */\n receive(clientId: string, record: R): void {\n const previous = this.seen.get(clientId)\n if (previous && previous.id !== record.id) this.removeRecord(previous.id)\n this.seen.set(clientId, { id: record.id, at: Date.now() })\n this.store.mergeRemoteChanges(() => {\n this.store.put([record])\n })\n }\n\n /** Drop one client's presence (it said goodbye). */\n remove(clientId: string): void {\n const entry = this.seen.get(clientId)\n if (!entry) return\n this.seen.delete(clientId)\n this.removeRecord(entry.id)\n }\n\n /** Drop every client we have not heard from within the timeout. */\n sweep(now: number = Date.now()): void {\n const stale: string[] = []\n for (const [clientId, entry] of this.seen) {\n if (now - entry.at > this.timeoutMs) stale.push(clientId)\n }\n for (const clientId of stale) this.remove(clientId)\n }\n\n startSweeping(intervalMs: number = Math.max(1000, Math.floor(this.timeoutMs / 4))): void {\n if (this.sweeper !== null) return\n this.sweeper = setInterval(() => this.sweep(), intervalMs)\n }\n\n stopSweeping(): void {\n if (this.sweeper === null) return\n clearInterval(this.sweeper)\n this.sweeper = null\n }\n\n /** Remove every record this room put in the store. */\n clear(): void {\n const ids = Array.from(this.seen.values(), (entry) => entry.id)\n this.seen.clear()\n for (const id of ids) this.removeRecord(id)\n }\n\n private removeRecord(id: string): void {\n const recordId = id as IdOf<R>\n if (!this.store.unsafeGetWithoutCapture(recordId)) return\n this.store.mergeRemoteChanges(() => {\n this.store.remove([recordId])\n })\n }\n}\n","import type { UnknownRecord } from \"@mocanvas/store\"\nimport { isCrdtStateLike, isStampedDiffLike, type CrdtState, type StampedDiff } from \"./crdt\"\n\n/**\n * Bumped when the wire format changes incompatibly.\n *\n * 2: `diff` carries a `StampedDiff` (per-field Lamport stamps) instead of a\n * plain `RecordsDiff`, `snapshot` carries the CRDT state next to the\n * records, and every message carries `version`.\n * 3: a put carries `base`, the stamp its whole record body is current as of,\n * and a record kept alive by an edit that outranks a delete is re-claimed\n * whole under a fresh stamp. A version 2 peer merges the same messages to a\n * different answer, so the two must not share a room.\n */\nexport const PROTOCOL_VERSION = 3\n\n/**\n * Every message is one JSON object with a `type` discriminator and a\n * `version`, which `encodeMessage` stamps on and `decodeMessage` checks.\n *\n * ```\n * hello a client joined; existing peers answer with a snapshot\n * snapshot the document-scope records plus the sender's CRDT stamps\n * diff one squashed store diff, stamped field by field\n * presence one presence record (cursor, camera, selection)\n * bye the client is leaving; drop its presence\n * unsupported synthesised locally for a peer on another protocol version\n * ```\n */\nexport type SyncMessage<R extends UnknownRecord = UnknownRecord> =\n | HelloMessage\n | SnapshotMessage<R>\n | DiffMessage<R>\n | PresenceMessage<R>\n | ByeMessage\n | UnsupportedMessage\n\nexport interface HelloMessage {\n type: \"hello\"\n clientId: string\n version: number\n}\n\nexport interface SnapshotMessage<R extends UnknownRecord = UnknownRecord> {\n type: \"snapshot\"\n records: R[]\n /** The stamps that go with those records, so a joiner does not start blank. */\n state: CrdtState\n}\n\nexport interface DiffMessage<R extends UnknownRecord = UnknownRecord> {\n type: \"diff\"\n clientId: string\n /** Per-client counter; only used for logging and de-duplication. */\n seq: number\n diff: StampedDiff<R>\n}\n\nexport interface PresenceMessage<R extends UnknownRecord = UnknownRecord> {\n type: \"presence\"\n clientId: string\n record: R\n}\n\nexport interface ByeMessage {\n type: \"bye\"\n clientId: string\n}\n\n/**\n * Never sent: `decodeMessage` returns this instead of guessing at a message\n * whose `version` is not ours, so a peer on another protocol is reported once\n * rather than half-parsed.\n */\nexport interface UnsupportedMessage {\n type: \"unsupported\"\n version: number\n clientId?: string\n}\n\nexport function encodeMessage<R extends UnknownRecord>(message: SyncMessage<R>): string {\n return JSON.stringify({ ...message, version: PROTOCOL_VERSION })\n}\n\n/**\n * Parse a message off the wire. Returns `null` for anything malformed and an\n * `unsupported` message for a peer on another protocol version: neither can\n * crash this client, and neither is ever mistaken for a message we understand.\n */\nexport function decodeMessage<R extends UnknownRecord = UnknownRecord>(data: unknown): SyncMessage<R> | null {\n let value: unknown = data\n if (typeof data === \"string\") {\n try {\n value = JSON.parse(data)\n } catch {\n return null\n }\n }\n if (typeof value !== \"object\" || value === null) return null\n const message = value as Record<string, unknown>\n\n const version = message[\"version\"]\n if (typeof version === \"number\" && version !== PROTOCOL_VERSION) {\n const clientId = message[\"clientId\"]\n return typeof clientId === \"string\"\n ? { type: \"unsupported\", version, clientId }\n : { type: \"unsupported\", version }\n }\n\n switch (message[\"type\"]) {\n case \"hello\":\n if (typeof message[\"clientId\"] !== \"string\") return null\n if (typeof message[\"version\"] !== \"number\") return null\n return { type: \"hello\", clientId: message[\"clientId\"], version: message[\"version\"] }\n case \"snapshot\": {\n const records = message[\"records\"]\n if (!Array.isArray(records)) return null\n if (!records.every(isRecordLike)) return null\n const state = message[\"state\"]\n if (!isCrdtStateLike(state)) return null\n return { type: \"snapshot\", records: records as R[], state: state as CrdtState }\n }\n case \"diff\": {\n if (typeof message[\"clientId\"] !== \"string\") return null\n if (typeof message[\"seq\"] !== \"number\") return null\n if (!isStampedDiffLike(message[\"diff\"])) return null\n return {\n type: \"diff\",\n clientId: message[\"clientId\"],\n seq: message[\"seq\"],\n diff: message[\"diff\"] as StampedDiff<R>,\n }\n }\n case \"presence\": {\n if (typeof message[\"clientId\"] !== \"string\") return null\n if (!isRecordLike(message[\"record\"])) return null\n return { type: \"presence\", clientId: message[\"clientId\"], record: message[\"record\"] as R }\n }\n case \"bye\":\n if (typeof message[\"clientId\"] !== \"string\") return null\n return { type: \"bye\", clientId: message[\"clientId\"] }\n default:\n return null\n }\n}\n\nfunction isRecordLike(value: unknown): boolean {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as { id?: unknown }).id === \"string\" &&\n typeof (value as { typeName?: unknown }).typeName === \"string\"\n )\n}\n","import { atom, type Signal } from \"@mocanvas/state\"\nimport {\n createEmptyRecordsDiff,\n isRecordsDiffEmpty,\n uniqueId,\n type IdOf,\n type RecordsDiff,\n type Store,\n type UnknownRecord,\n} from \"@mocanvas/store\"\nimport { createCrdt, deepEqual, isStampedDiffEmpty, stampedDiffFromSnapshot, type Crdt, type StampedDiff } from \"./crdt\"\nimport {\n createPresenceSync,\n DEFAULT_PRESENCE_TIMEOUT_MS,\n PresenceRoom,\n type PresenceEditor,\n type PresenceSync,\n} from \"./presence\"\nimport { PROTOCOL_VERSION, type SyncMessage } from \"./protocol\"\nimport type { Transport } from \"./transport\"\n\nexport type SyncStatus = \"offline\" | \"connecting\" | \"online\"\n\nexport interface SyncClientOptions<R extends UnknownRecord = UnknownRecord> {\n store: Store<R, any>\n /** Identifies the document. The transport is expected to be scoped to it too. */\n roomId: string\n transport: Transport<R>\n /** Turn on cursor/selection sharing for an editor. */\n presence?: { editor: PresenceEditor } | undefined\n /** Defaults to a fresh random id; one per tab, not per user. */\n clientId?: string | undefined\n /** Drop a collaborator after this long without a presence message. Default 10s. */\n presenceTimeoutMs?: number | undefined\n /** Upper bound on presence send rate. Default 34ms (~30 Hz). */\n presenceThrottleMs?: number | undefined\n presenceHeartbeatMs?: number | undefined\n /** How many tombstones for vanished records to keep. See `crdt.ts`. */\n tombstoneLimit?: number | undefined\n /** How long such a tombstone lives, in ms. */\n tombstoneMaxAgeMs?: number | undefined\n /** Called for protocol-level problems (a peer on another version, say). */\n onError?: ((error: Error) => void) | undefined\n}\n\nexport interface SyncClient {\n readonly clientId: string\n readonly roomId: string\n connect(): void\n disconnect(): void\n /** A signal, so React and reactors can follow the connection. */\n getStatus(): Signal<SyncStatus>\n dispose(): void\n}\n\n/**\n * Joins one room over `transport` and keeps `store` in step with its peers.\n *\n * Document changes made locally by the user are stamped by the CRDT (see\n * `crdt.ts`) and broadcast; incoming messages are merged field by field and\n * only the fields that won are applied, as remote changes, so they neither\n * echo back nor land in the undo stack. Concurrent edits to different fields\n * of one record all survive, and replicas converge whatever order messages\n * arrive in.\n */\nexport function createSyncClient<R extends UnknownRecord = UnknownRecord>(\n options: SyncClientOptions<R>,\n): SyncClient {\n const { store, roomId, transport } = options\n const clientId = options.clientId ?? uniqueId(12)\n const status = atom<SyncStatus>(`sync.status:${roomId}`, \"offline\")\n const room = new PresenceRoom<R>(store, options.presenceTimeoutMs ?? DEFAULT_PRESENCE_TIMEOUT_MS)\n\n const crdt: Crdt<R> = createCrdt<R>({\n clientId,\n getRecord: (id) => store.unsafeGetWithoutCapture(id as IdOf<R>) as R | undefined,\n ...(options.tombstoneLimit !== undefined ? { tombstoneLimit: options.tombstoneLimit } : {}),\n ...(options.tombstoneMaxAgeMs !== undefined ? { tombstoneMaxAgeMs: options.tombstoneMaxAgeMs } : {}),\n })\n\n const unsubscribes: (() => void)[] = []\n const reportedVersions = new Set<number>()\n let presence: PresenceSync | null = null\n let seq = 0\n /**\n * True while this replica is waiting to be handed the room, which only a\n * replica holding no stamps at all ever is: one that has just loaded the\n * page has nothing to lose by taking a peer's document whole, and nothing\n * worth pushing at anyone either. A replica with stamps — including one that\n * edited while it was disconnected — merges instead.\n */\n let awaitingSnapshot = false\n let connected = false\n let disposed = false\n\n const send = (message: SyncMessage<R>) => {\n try {\n transport.send(message)\n } catch (error) {\n options.onError?.(error instanceof Error ? error : new Error(String(error)))\n }\n }\n\n const broadcast = (stamped: StampedDiff<R>) => {\n if (isStampedDiffEmpty(stamped)) return\n send({ type: \"diff\", clientId, seq: seq++, diff: stamped })\n }\n\n /**\n * Writes the CRDT minted while merging — a record revived by an edit that\n * outranks a delete — go out like any other local diff.\n */\n const flushCrdt = () => {\n const outgoing = crdt.takeOutgoing()\n if (outgoing) broadcast(outgoing)\n }\n\n const sendSnapshot = () => {\n send({\n type: \"snapshot\",\n records: Object.values(store.serialize(\"document\")) as R[],\n state: crdt.getState(),\n })\n }\n\n const applyRemote = (diff: RecordsDiff<R>) => {\n if (isRecordsDiffEmpty(diff)) return\n const actual = store.extractingChanges(() => {\n store.mergeRemoteChanges(() => {\n store.applyDiff(diff)\n })\n })\n reportRewrites(diff, actual)\n }\n\n /**\n * What the store actually did, minus what we asked it to do. A validator or\n * a side effect that rewrites a merged record — or touches another one —\n * changes this replica only: the change is reported as `remote`, so the\n * listener below never sees it, and no peer would ever hear about it. Stamp\n * that difference as a local write and send it, so the rewrite is one more\n * write everyone can agree on rather than a private divergence.\n *\n * A side effect that is a function of the record and the document rewrites\n * the same way everywhere, so this settles in one round: the peers apply a\n * value they already hold and produce nothing further. A side effect that is\n * not a function of its inputs cannot converge, here or anywhere.\n */\n const reportRewrites = (asked: RecordsDiff<R>, actual: RecordsDiff<R>) => {\n const rewrite = createEmptyRecordsDiff<R>()\n const intended = (id: IdOf<R>): R | undefined => asked.added[id] ?? asked.updated[id]?.[1]\n const isDocument = (record: R) => store.getScope(record.typeName) === \"document\"\n\n for (const key in actual.added) {\n const id = key as IdOf<R>\n const after = actual.added[id]\n if (!after || !isDocument(after)) continue\n const want = intended(id)\n if (!want) rewrite.added[id] = after\n else if (!deepEqual(want, after)) rewrite.updated[id] = [want, after]\n }\n for (const key in actual.updated) {\n const id = key as IdOf<R>\n const pair = actual.updated[id]\n if (!pair || !isDocument(pair[1])) continue\n const want = intended(id)\n if (!want) rewrite.updated[id] = pair\n else if (!deepEqual(want, pair[1])) rewrite.updated[id] = [want, pair[1]]\n }\n for (const key in actual.removed) {\n const id = key as IdOf<R>\n const before = actual.removed[id]\n if (!before || !isDocument(before)) continue\n if (!asked.removed[id]) rewrite.removed[id] = before\n }\n if (isRecordsDiffEmpty(rewrite)) return\n broadcast(crdt.stampLocal(rewrite))\n }\n\n /**\n * Outgoing document changes: only what this user did, never what we applied\n * from a peer (those arrive with source \"remote\"). It is wired up when the\n * client is built and stays wired until `dispose`, so an edit made before\n * `connect()` or after `disconnect()` is stamped when it happens rather than\n * when the socket is up. That is what carries offline work through a\n * reconnect: unstamped, it would lose to every stamped write in the room.\n */\n const stopListening = store.listen(\n (entry) => {\n if (isRecordsDiffEmpty(entry.changes)) return\n const stamped = crdt.stampLocal(entry.changes)\n if (connected) broadcast(stamped)\n },\n { source: \"user\", scope: \"document\" },\n )\n\n const handle = (message: SyncMessage<R>) => {\n if (disposed) return\n switch (message.type) {\n case \"unsupported\": {\n if (reportedVersions.has(message.version)) return\n reportedVersions.add(message.version)\n options.onError?.(\n new Error(`A peer speaks protocol ${message.version}, we speak ${PROTOCOL_VERSION}`),\n )\n return\n }\n case \"hello\": {\n if (message.clientId === clientId) return\n if (message.version !== PROTOCOL_VERSION) {\n options.onError?.(\n new Error(`Peer ${message.clientId} speaks protocol ${message.version}, we speak ${PROTOCOL_VERSION}`),\n )\n return\n }\n // A newcomer needs the document; every established peer answers with\n // its records and its stamps. Answering also makes us established: we\n // hold state of our own now, so nobody's snapshot can start us blank.\n awaitingSnapshot = false\n sendSnapshot()\n // Re-announce our presence so the newcomer sees us immediately.\n presence?.poke()\n return\n }\n case \"snapshot\": {\n // Wholesale adoption is only safe with nothing of our own to lose:\n // one stamp means one edit that would be overwritten by the sender's\n // body, so `crdt.size()` and not just the flag decides.\n if (awaitingSnapshot && crdt.size() === 0) {\n // We have no stamps at all. Take the sender's records and its clock\n // wholesale, so our own later writes sort after everything already\n // in the room instead of racing it from zero.\n awaitingSnapshot = false\n const removals = crdt.applyState(message.state)\n const actual = store.extractingChanges(() => {\n store.mergeRemoteChanges(() => {\n store.put(message.records)\n store.applyDiff(removals)\n })\n })\n const asked = createEmptyRecordsDiff<R>()\n for (const record of message.records) asked.added[record.id as IdOf<R>] = record\n reportRewrites(asked, actual)\n flushCrdt()\n return\n }\n // Otherwise this is a reconciliation (a peer reconnected, or answered\n // someone else's hello), or we hold edits of our own: merge it field\n // by field. Nothing we hold a newer stamp for can be overwritten, so\n // it is always safe to apply.\n awaitingSnapshot = false\n applyRemote(crdt.mergeRemote(stampedDiffFromSnapshot(message.records, message.state)))\n flushCrdt()\n return\n }\n case \"diff\": {\n if (message.clientId === clientId) return\n if (isStampedDiffEmpty(message.diff)) return\n applyRemote(crdt.mergeRemote(message.diff))\n flushCrdt()\n return\n }\n case \"presence\": {\n if (message.clientId === clientId) return\n room.receive(message.clientId, message.record)\n return\n }\n case \"bye\": {\n if (message.clientId === clientId) return\n room.remove(message.clientId)\n return\n }\n }\n }\n\n const onOpen = () => {\n if (disposed || !connected) return\n status.set(\"online\")\n send({ type: \"hello\", clientId, version: PROTOCOL_VERSION })\n // On a reconnect we already hold stamps, possibly for work done while the\n // transport was down. Push them: the peers merge field by field, so\n // neither side of the split loses anything.\n if (!awaitingSnapshot) sendSnapshot()\n presence?.poke()\n }\n\n const onClose = () => {\n if (disposed || !connected) return\n // The transport reconnects on its own; from here it is just \"not online\".\n // Local edits keep flowing into the CRDT and go out on the next open.\n status.set(\"connecting\")\n }\n\n return {\n clientId,\n roomId,\n\n connect() {\n if (disposed || connected) return\n connected = true\n // Only a replica holding no stamps at all is a newcomer. One that edited\n // before it ever connected, or while it was disconnected, has work of\n // its own and merges the room's snapshot instead of taking it whole.\n awaitingSnapshot = crdt.size() === 0\n status.set(\"connecting\")\n\n unsubscribes.push(transport.onMessage(handle))\n if (transport.onOpen) unsubscribes.push(transport.onOpen(onOpen))\n if (transport.onClose) unsubscribes.push(transport.onClose(onClose))\n\n if (options.presence) {\n presence = createPresenceSync({\n editor: options.presence.editor,\n clientId,\n send: (record) => send({ type: \"presence\", clientId, record: record as unknown as R }),\n ...(options.presenceThrottleMs !== undefined ? { throttleMs: options.presenceThrottleMs } : {}),\n ...(options.presenceHeartbeatMs !== undefined ? { heartbeatMs: options.presenceHeartbeatMs } : {}),\n })\n presence.start()\n }\n room.startSweeping()\n\n if (!transport.onOpen) onOpen()\n },\n\n disconnect() {\n if (!connected) return\n connected = false\n send({ type: \"bye\", clientId })\n presence?.stop()\n presence = null\n room.stopSweeping()\n room.clear()\n for (const off of unsubscribes.splice(0)) off()\n status.set(\"offline\")\n },\n\n getStatus() {\n return status\n },\n\n dispose() {\n if (disposed) return\n this.disconnect()\n disposed = true\n stopListening()\n transport.close()\n },\n }\n}\n","import type { UnknownRecord } from \"@mocanvas/store\"\nimport { decodeMessage, encodeMessage, type SyncMessage } from \"./protocol\"\n\n/**\n * A duplex channel that carries `SyncMessage`s between peers in one room.\n *\n * The transport owns serialization and reconnection; the client above it only\n * sees decoded messages. `onOpen` fires every time the channel becomes usable\n * (including after a reconnect), `onClose` every time it stops being usable.\n * A transport with neither is treated as open from the moment it is connected.\n */\nexport interface Transport<R extends UnknownRecord = UnknownRecord> {\n send(message: SyncMessage<R>): void\n onMessage(callback: (message: SyncMessage<R>) => void): () => void\n onOpen?(callback: () => void): () => void\n onClose?(callback: () => void): () => void\n close(): void\n}\n\n/** @internal A tiny callback set with an unsubscribe function. */\nexport function createEmitter(): {\n add(cb: () => void): () => void\n emit(): void\n clear(): void\n} {\n const callbacks = new Set<() => void>()\n return {\n add(cb) {\n callbacks.add(cb)\n return () => {\n callbacks.delete(cb)\n }\n },\n emit() {\n for (const cb of Array.from(callbacks)) cb()\n },\n clear() {\n callbacks.clear()\n },\n }\n}\n\n/* -------------------------------------------------------------------------- */\n/* BroadcastChannel */\n/* -------------------------------------------------------------------------- */\n\nexport interface BroadcastChannelTransportOptions {\n /** Channel name prefix, so two apps on one origin do not collide. */\n prefix?: string\n}\n\n/**\n * Same-origin tabs of one browser. There is no server, so every peer is\n * equally authoritative and the channel is open as soon as it is created.\n */\nexport function createBroadcastChannelTransport<R extends UnknownRecord = UnknownRecord>(\n roomId: string,\n options: BroadcastChannelTransportOptions = {},\n): Transport<R> {\n const name = `${options.prefix ?? \"mocanvas-sync\"}:${roomId}`\n const channel = new BroadcastChannel(name)\n const listeners = new Set<(message: SyncMessage<R>) => void>()\n let closed = false\n\n channel.onmessage = (event: MessageEvent) => {\n const message = decodeMessage<R>(event.data)\n if (!message) return\n for (const listener of Array.from(listeners)) listener(message)\n }\n\n return {\n send(message) {\n if (closed) return\n channel.postMessage(encodeMessage(message))\n },\n onMessage(callback) {\n listeners.add(callback)\n return () => {\n listeners.delete(callback)\n }\n },\n onOpen(callback) {\n // Already usable; hand control back to the caller before firing so that\n // `connect()` has finished wiring itself up.\n let cancelled = false\n queueMicrotask(() => {\n if (!cancelled && !closed) callback()\n })\n return () => {\n cancelled = true\n }\n },\n close() {\n closed = true\n listeners.clear()\n channel.onmessage = null\n channel.close()\n },\n }\n}\n\n/* -------------------------------------------------------------------------- */\n/* WebSocket */\n/* -------------------------------------------------------------------------- */\n\nexport interface WebSocketTransportOptions {\n /** Reconnect with exponential backoff after an unexpected close. Default `true`. */\n reconnect?: boolean\n /** First backoff delay in ms. Default 500. */\n minDelayMs?: number\n /** Backoff ceiling in ms. Default 15000. */\n maxDelayMs?: number\n /** Injectable for tests and for Node (`ws`). Defaults to `globalThis.WebSocket`. */\n WebSocketImpl?: typeof WebSocket\n}\n\n/**\n * A WebSocket to a relay (see `scripts/relay.mjs`). Messages sent while the\n * socket is down are dropped rather than queued: the next `hello`/`snapshot`\n * exchange re-establishes the document anyway, and presence is re-sent on a\n * heartbeat.\n */\nexport function createWebSocketTransport<R extends UnknownRecord = UnknownRecord>(\n url: string,\n options: WebSocketTransportOptions = {},\n): Transport<R> {\n const reconnect = options.reconnect ?? true\n const minDelay = options.minDelayMs ?? 500\n const maxDelay = options.maxDelayMs ?? 15_000\n const Impl = options.WebSocketImpl ?? (globalThis as { WebSocket?: typeof WebSocket }).WebSocket\n if (!Impl) throw new Error(\"No WebSocket implementation available; pass options.WebSocketImpl\")\n\n const listeners = new Set<(message: SyncMessage<R>) => void>()\n const open = createEmitter()\n const close = createEmitter()\n\n let socket: WebSocket | null = null\n let attempt = 0\n let timer: ReturnType<typeof setTimeout> | null = null\n let disposed = false\n\n const connect = () => {\n if (disposed) return\n const ws = new Impl(url)\n socket = ws\n ws.onopen = () => {\n attempt = 0\n open.emit()\n }\n ws.onmessage = (event: MessageEvent) => {\n const message = decodeMessage<R>(event.data)\n if (!message) return\n for (const listener of Array.from(listeners)) listener(message)\n }\n ws.onerror = () => {\n // `onclose` always follows; the retry is scheduled there.\n }\n ws.onclose = () => {\n if (socket === ws) socket = null\n close.emit()\n if (!disposed && reconnect) schedule()\n }\n }\n\n const schedule = () => {\n if (timer !== null) return\n // Exponential backoff with full jitter, so a relay restart does not get a\n // thundering herd from every open tab.\n const ceiling = Math.min(maxDelay, minDelay * 2 ** attempt)\n attempt++\n const delay = Math.random() * ceiling\n timer = setTimeout(() => {\n timer = null\n connect()\n }, delay)\n }\n\n connect()\n\n return {\n send(message) {\n if (!socket || socket.readyState !== 1 /* OPEN */) return\n socket.send(encodeMessage(message))\n },\n onMessage(callback) {\n listeners.add(callback)\n return () => {\n listeners.delete(callback)\n }\n },\n onOpen(callback) {\n const remove = open.add(callback)\n if (socket && socket.readyState === 1) queueMicrotask(callback)\n return remove\n },\n onClose(callback) {\n return close.add(callback)\n },\n close() {\n disposed = true\n if (timer !== null) {\n clearTimeout(timer)\n timer = null\n }\n listeners.clear()\n open.clear()\n close.clear()\n const ws = socket\n socket = null\n if (ws) {\n ws.onopen = null\n ws.onmessage = null\n ws.onerror = null\n ws.onclose = null\n if (ws.readyState === 0 || ws.readyState === 1) ws.close()\n }\n },\n }\n}\n\n/* -------------------------------------------------------------------------- */\n/* in-memory */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Two transports wired to each other, going through the same JSON encode and\n * decode as the real ones. Delivery is asynchronous (a microtask) so that a\n * peer never observes a message inside its own `send()` call stack.\n */\nexport function createMemoryTransportPair<R extends UnknownRecord = UnknownRecord>(): [Transport<R>, Transport<R>] {\n const hub = createMemoryHub<R>()\n return [hub.join(), hub.join()]\n}\n\nexport interface MemoryHub<R extends UnknownRecord = UnknownRecord> {\n /** Add another peer to the room. */\n join(): Transport<R>\n}\n\n/** A room every joined transport broadcasts into (itself excluded). */\nexport function createMemoryHub<R extends UnknownRecord = UnknownRecord>(): MemoryHub<R> {\n const peers = new Set<{ deliver(data: string): void }>()\n return {\n join(): Transport<R> {\n const listeners = new Set<(message: SyncMessage<R>) => void>()\n let closed = false\n const peer = {\n deliver(data: string) {\n if (closed) return\n const message = decodeMessage<R>(data)\n if (!message) return\n for (const listener of Array.from(listeners)) listener(message)\n },\n }\n peers.add(peer)\n return {\n send(message) {\n if (closed) return\n const data = encodeMessage(message)\n for (const other of Array.from(peers)) {\n if (other === peer) continue\n queueMicrotask(() => other.deliver(data))\n }\n },\n onMessage(callback) {\n listeners.add(callback)\n return () => {\n listeners.delete(callback)\n }\n },\n onOpen(callback) {\n let cancelled = false\n queueMicrotask(() => {\n if (!cancelled && !closed) callback()\n })\n return () => {\n cancelled = true\n }\n },\n close() {\n closed = true\n listeners.clear()\n peers.delete(peer)\n },\n }\n },\n }\n}\n","import { track, useValue } from \"@mocanvas/state/react\"\nimport type { Editor, EditorRecord, InstancePresence, UnknownShape } from \"@mocanvas/editor\"\nimport { useEffect, useRef, useState, type CSSProperties } from \"react\"\nimport { createSyncClient, type SyncClient, type SyncClientOptions, type SyncStatus } from \"./SyncClient\"\nimport type { Transport } from \"./transport\"\n\nconst layerStyle: CSSProperties = {\n position: \"absolute\",\n inset: 0,\n width: \"100%\",\n height: \"100%\",\n pointerEvents: \"none\",\n overflow: \"visible\",\n}\n\nexport interface CollaboratorCursorsProps {\n editor: Editor\n /** Draw what each collaborator has selected. Default `true`. */\n showSelection?: boolean\n /** Draw a name chip next to each cursor. Default `true`. */\n showNames?: boolean\n}\n\n/**\n * Other people's cursors and selections, drawn in screen space above the\n * canvas. Drop it inside `<Mocanvas>` or `<Canvas>`; it positions itself.\n */\nexport const CollaboratorCursors = track(function CollaboratorCursors({\n editor,\n showSelection = true,\n showNames = true,\n}: CollaboratorCursorsProps) {\n const collaborators = editor.getCollaboratorsOnCurrentPage()\n if (collaborators.length === 0) return null\n return (\n <svg className=\"mocanvas-collaborators\" style={layerStyle}>\n {collaborators.map((presence) => (\n <g key={presence.id}>\n {showSelection ? <CollaboratorSelection editor={editor} presence={presence} /> : null}\n <CollaboratorCursor editor={editor} presence={presence} showName={showNames} />\n </g>\n ))}\n </svg>\n )\n})\n\nconst CollaboratorCursor = track(function CollaboratorCursor({\n editor,\n presence,\n showName,\n}: {\n editor: Editor\n presence: InstancePresence\n showName: boolean\n}) {\n // A collaborator with no pointer — an agent, or someone whose pointer has left\n // the canvas — draws no cursor at all.\n if (presence.cursor === null) return null\n // Viewport space: the overlay is positioned inside the canvas container, not\n // the window.\n const point = editor.pageToViewport(presence.cursor)\n const name = presence.userName || \"Anonymous\"\n // A 16x22 arrow drawn from the hotspot, then the name chip below it.\n return (\n <g transform={`translate(${point.x}, ${point.y}) rotate(${presence.cursor.rotation})`}>\n <path\n d=\"M0 0 L0 17.3 L4.3 13.4 L7 19.4 L9.9 18 L7.2 12.2 L12.8 12 Z\"\n fill={presence.color}\n stroke=\"#fff\"\n strokeWidth={1}\n strokeLinejoin=\"round\"\n />\n {showName ? (\n <g transform=\"translate(11, 18)\">\n <rect rx={4} ry={4} width={Math.max(24, name.length * 7 + 12)} height={18} fill={presence.color} />\n <text x={6} y={13} fontSize={11} fontFamily=\"system-ui, sans-serif\" fill=\"#fff\">\n {name}\n </text>\n </g>\n ) : null}\n </g>\n )\n})\n\nconst CollaboratorSelection = track(function CollaboratorSelection({\n editor,\n presence,\n}: {\n editor: Editor\n presence: InstancePresence\n}) {\n const outlines: string[] = []\n for (const id of presence.selectedShapeIds) {\n const shape: UnknownShape | undefined = editor.getShape(id)\n if (!shape) continue\n const bounds = editor.getShapeGeometryBounds(shape)\n if (!bounds) continue\n const m = editor.getShapePageTransform(shape)\n // Transform the local corners to page space, then to screen space, so the\n // outline follows rotation without a nested SVG transform.\n const corners: [number, number][] = [\n [bounds.x, bounds.y],\n [bounds.maxX, bounds.y],\n [bounds.maxX, bounds.maxY],\n [bounds.x, bounds.maxY],\n ]\n const points = corners.map(([x, y]) => {\n // Viewport space, like the cursor above: this overlay lives in the canvas\n // container, so window offsets must not be added.\n const p = editor.pageToViewport({ x: m.a * x + m.c * y + m.e, y: m.b * x + m.d * y + m.f })\n return `${p.x},${p.y}`\n })\n outlines.push(points.join(\" \"))\n }\n if (outlines.length === 0) return null\n return (\n <>\n {outlines.map((points, i) => (\n <polygon key={i} points={points} fill=\"none\" stroke={presence.color} strokeWidth={1.5} opacity={0.8} />\n ))}\n </>\n )\n})\n\nexport interface UseSyncOptions {\n roomId: string\n /**\n * The channel to the room. Pass a factory when the transport should be\n * recreated with the room (the usual case); it is read once per connection.\n */\n transport: Transport | (() => Transport)\n /** Share this editor's cursor and selection. Default `true`. */\n presence?: boolean\n /** Set `false` to stay offline (a read-only view, say). Default `true`. */\n enabled?: boolean\n clientId?: string\n presenceTimeoutMs?: number\n presenceThrottleMs?: number\n onError?: (error: Error) => void\n}\n\nexport interface UseSyncResult {\n status: SyncStatus\n client: SyncClient | null\n}\n\n/**\n * Connect an editor's store to a room for as long as the component is mounted.\n * Everything is torn down (including the transport) on unmount.\n */\nexport function useSync(editor: Editor | null, options: UseSyncOptions): UseSyncResult {\n const { roomId, enabled = true, presence = true } = options\n // Kept in a ref so that inline factories and callbacks do not reconnect.\n const latest = useRef(options)\n latest.current = options\n\n const [client, setClient] = useState<SyncClient | null>(null)\n\n useEffect(() => {\n if (!editor || !enabled) {\n setClient(null)\n return\n }\n const current = latest.current\n const transport = typeof current.transport === \"function\" ? current.transport() : current.transport\n const clientOptions: SyncClientOptions<EditorRecord> = {\n store: editor.store,\n roomId,\n transport: transport as Transport<EditorRecord>,\n ...(presence ? { presence: { editor } } : {}),\n ...(current.clientId !== undefined ? { clientId: current.clientId } : {}),\n ...(current.presenceTimeoutMs !== undefined ? { presenceTimeoutMs: current.presenceTimeoutMs } : {}),\n ...(current.presenceThrottleMs !== undefined ? { presenceThrottleMs: current.presenceThrottleMs } : {}),\n onError: (error: Error) => latest.current.onError?.(error),\n }\n const next = createSyncClient<EditorRecord>(clientOptions)\n setClient(next)\n next.connect()\n return () => {\n setClient(null)\n next.dispose()\n }\n }, [editor, roomId, enabled, presence])\n\n const status = useValue(\"sync.status\", () => client?.getStatus().get() ?? \"offline\", [client])\n return { status, client }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/crdt.ts","../src/presence.ts","../src/protocol.ts","../src/SyncClient.ts","../src/transport.ts","../src/react.tsx"],"names":["createEmptyRecordsDiff","CollaboratorCursors","CollaboratorCursor","CollaboratorSelection"],"mappings":"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","file":"index.js","sourcesContent":["import { createEmptyRecordsDiff, type IdOf, type RecordsDiff, type UnknownRecord } from \"@mocanvas/store\"\n\n/**\n * A state-based CRDT over record fields.\n *\n * Every leaf field of every record is its own last-writer-wins register,\n * keyed by a dotted path (`x`, `props.w`, `meta.notes.title`). A write carries\n * a Lamport stamp; the greater stamp wins, and equal Lamport values are broken\n * by client id so that both sides of a race reach the same answer from the\n * same pair. Deletion is a stamped tombstone rather than a removal, so a late\n * message cannot resurrect a record that was deleted after it was written.\n *\n * Consequences worth knowing before reading the code:\n *\n * - Arrays are opaque. `props.points` is one register holding the whole array,\n * not one register per element. Two concurrent edits to one array pick a\n * winner instead of merging; splitting arrays into elements would need\n * element identity, which record data does not carry.\n * - A stamp covers a whole subtree. Writing `meta` (setting it, or deleting\n * the key) dominates `meta.a`: the claim loses to any stamp at or above its\n * own path, and dropping a subtree keeps descendants stamped later than the\n * write. Without that, `delete meta` racing `set meta.a` lands differently\n * depending on arrival order.\n * - A record also has one `base` register: the stamp at which some sender's\n * whole record body was current. Fields nobody holds a stamp for — a record\n * materialized without its creation message, or one that came back from\n * under a tombstone, which erases field stamps — are governed by that single\n * register, so every replica takes its body from the same message instead of\n * from whichever one happened to arrive first.\n * - Merging is idempotent (the same message twice is a no-op the second time)\n * and commutative (per path the result is the maximum stamp, which does not\n * depend on arrival order).\n * - The merge is a pure function of the stamps; values only ever ride along.\n */\n\n/** Version of the `CrdtState` blob on the wire. */\nexport const CRDT_STATE_VERSION = 1\n\n/** How many collected-and-gone records keep a tombstone before it is dropped. */\nexport const DEFAULT_TOMBSTONE_LIMIT = 5000\n/** How long a tombstone for a vanished record is kept. */\nexport const DEFAULT_TOMBSTONE_MAX_AGE_MS = 60 * 60 * 1000\n\n/* -------------------------------------------------------------------------- */\n/* stamps */\n/* -------------------------------------------------------------------------- */\n\n/** A point in the partial order of writes, made total by the client tiebreak. */\nexport interface Stamp {\n readonly lamport: number\n readonly client: string\n}\n\n/**\n * A total order on stamps: Lamport first, then client id. Two peers holding\n * the same pair always agree, which is what makes the merge deterministic.\n */\nexport function compareStamps(a: Stamp, b: Stamp): number {\n if (a.lamport !== b.lamport) return a.lamport < b.lamport ? -1 : 1\n if (a.client === b.client) return 0\n return a.client < b.client ? -1 : 1\n}\n\nexport function isStamp(value: unknown): value is Stamp {\n if (typeof value !== \"object\" || value === null) return false\n const stamp = value as { lamport?: unknown; client?: unknown }\n return typeof stamp.lamport === \"number\" && Number.isFinite(stamp.lamport) && typeof stamp.client === \"string\"\n}\n\nexport interface LamportClock {\n /** The current value. */\n get(): number\n /** Advance for a local write and return the new value. */\n tick(): number\n /** Fold in a stamp seen from a peer: `max(local, remote) + 1`. */\n observe(remote: number): void\n}\n\nexport function createLamportClock(start = 0): LamportClock {\n let value = start\n return {\n get: () => value,\n tick: () => (value += 1),\n observe(remote: number) {\n if (!Number.isFinite(remote)) return\n value = Math.max(value, remote) + 1\n },\n }\n}\n\n/* -------------------------------------------------------------------------- */\n/* wire shapes */\n/* -------------------------------------------------------------------------- */\n\n/**\n * One record's changed fields. `record` is the whole record as the sender saw\n * it after the change; `fields` says which paths this message actually claims.\n * A receiver that already has the record takes only the winning paths out of\n * `record`; a receiver that does not have it materializes from `record` whole.\n */\nexport interface StampedPut<R extends UnknownRecord = UnknownRecord> {\n record: R\n fields: Record<string, Stamp>\n /**\n * The stamp at which `record` was the sender's whole view of that record.\n * Paths the receiver holds no stamp for are taken from `record` when this is\n * the greatest such stamp it has seen, and left alone otherwise.\n */\n base?: Stamp\n}\n\nexport interface StampedRemove {\n id: string\n stamp: Stamp\n}\n\n/** What a `diff` message carries: puts and removes, each field stamped. */\nexport interface StampedDiff<R extends UnknownRecord = UnknownRecord> {\n puts: StampedPut<R>[]\n removes: StampedRemove[]\n}\n\nexport function createEmptyStampedDiff<R extends UnknownRecord = UnknownRecord>(): StampedDiff<R> {\n return { puts: [], removes: [] }\n}\n\nexport function isStampedDiffEmpty<R extends UnknownRecord>(diff: StampedDiff<R>): boolean {\n return diff.puts.length === 0 && diff.removes.length === 0\n}\n\n/** Structural check used by `decodeMessage`; deliberately shallow but strict. */\nexport function isStampedDiffLike(value: unknown): boolean {\n if (typeof value !== \"object\" || value === null) return false\n const diff = value as { puts?: unknown; removes?: unknown }\n if (!Array.isArray(diff.puts) || !Array.isArray(diff.removes)) return false\n for (const put of diff.puts) {\n if (typeof put !== \"object\" || put === null) return false\n const { record, fields, base } = put as { record?: unknown; fields?: unknown; base?: unknown }\n if (typeof record !== \"object\" || record === null) return false\n if (typeof (record as { id?: unknown }).id !== \"string\") return false\n if (typeof (record as { typeName?: unknown }).typeName !== \"string\") return false\n if (!isStampMap(fields)) return false\n if (base !== undefined && !isStamp(base)) return false\n }\n for (const remove of diff.removes) {\n if (typeof remove !== \"object\" || remove === null) return false\n const { id, stamp } = remove as { id?: unknown; stamp?: unknown }\n if (typeof id !== \"string\" || !isStamp(stamp)) return false\n }\n return true\n}\n\n/** What one record's registers look like once serialized. */\nexport interface CrdtRecordState {\n fields: Record<string, Stamp>\n /** The stamp of the message this record's unstamped fields came from. */\n base?: Stamp\n deleted?: Stamp\n /** Local wall clock at the time the tombstone was taken, for collection. */\n deletedAt?: number\n}\n\n/** The whole replica's stamps, as carried by a `snapshot` message. */\nexport interface CrdtState {\n version: number\n clock: number\n records: Record<string, CrdtRecordState>\n}\n\nexport function createEmptyCrdtState(): CrdtState {\n return { version: CRDT_STATE_VERSION, clock: 0, records: {} }\n}\n\nexport function isCrdtStateLike(value: unknown): boolean {\n if (typeof value !== \"object\" || value === null) return false\n const state = value as { version?: unknown; clock?: unknown; records?: unknown }\n if (typeof state.version !== \"number\") return false\n if (typeof state.clock !== \"number\") return false\n if (typeof state.records !== \"object\" || state.records === null || Array.isArray(state.records)) return false\n for (const key of Object.keys(state.records as Record<string, unknown>)) {\n const record = (state.records as Record<string, unknown>)[key]\n if (typeof record !== \"object\" || record === null) return false\n const { fields, deleted, base } = record as { fields?: unknown; deleted?: unknown; base?: unknown }\n if (!isStampMap(fields)) return false\n if (deleted !== undefined && !isStamp(deleted)) return false\n if (base !== undefined && !isStamp(base)) return false\n }\n return true\n}\n\nfunction isStampMap(value: unknown): boolean {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return false\n for (const key of Object.keys(value as Record<string, unknown>)) {\n if (!isStamp((value as Record<string, unknown>)[key])) return false\n }\n return true\n}\n\n/* -------------------------------------------------------------------------- */\n/* the CRDT */\n/* -------------------------------------------------------------------------- */\n\nexport interface CrdtOptions<R extends UnknownRecord = UnknownRecord> {\n /** Identifies this replica; also the tiebreak in `compareStamps`. */\n clientId: string\n /**\n * Read the replica's current value for a record. The CRDT owns the stamps,\n * never the values: the store stays the source of truth and a merge is\n * expressed as a diff against whatever `getRecord` reports.\n */\n getRecord?: ((id: string) => R | undefined) | undefined\n /** Tombstones kept for records that are gone. Default 5000. */\n tombstoneLimit?: number | undefined\n /** How long such a tombstone lives. Default one hour. */\n tombstoneMaxAgeMs?: number | undefined\n /** Injectable wall clock, for tests. */\n now?: (() => number) | undefined\n}\n\nexport interface Crdt<R extends UnknownRecord = UnknownRecord> {\n readonly clientId: string\n readonly clock: LamportClock\n /**\n * Stamp a diff the local user just made and return the message to send.\n * The whole diff shares one stamp: it was one operation.\n */\n stampLocal(diff: RecordsDiff<R>): StampedDiff<R>\n /**\n * Merge a peer's message. The returned diff holds only the fields that won,\n * as whole records the store can apply — never a blind overwrite.\n */\n mergeRemote(stamped: StampedDiff<R>): RecordsDiff<R>\n /** This replica's stamps, for a `snapshot` message. */\n getState(): CrdtState\n /**\n * Adopt a peer's stamps wholesale. This is what a replica that is joining\n * with no state of its own does, together with that peer's records: it takes\n * the peer's stamps instead of starting blank, so its own later writes sort\n * after everything already in the room. The returned diff removes whatever\n * the peer has tombstoned and we still hold. Only for a replica holding no\n * stamps at all: the caller adopts the peer's records wholesale alongside it,\n * so a replica with work of its own must merge the snapshot through\n * `stampedDiffFromSnapshot` instead of taking it.\n */\n applyState(state: CrdtState): RecordsDiff<R>\n /**\n * Writes this replica minted while merging, which the caller must broadcast.\n * A record kept alive by an edit that outranks a delete is re-claimed whole\n * under a fresh stamp: the tombstone leaves the fields it dominates with no\n * provenance at all, and only a stamped body can put every replica on the\n * same one. Empty when there is nothing to send.\n */\n takeOutgoing(): StampedDiff<R> | null\n /** Number of records the state currently tracks (tests and diagnostics). */\n size(): number\n /** Drop tombstones past the count or age bound. Called after every merge. */\n collectTombstones(): void\n}\n\ninterface RecordState {\n fields: Map<string, Stamp>\n /** The stamp of the message this record's unstamped fields came from. */\n base?: Stamp | undefined\n deleted?: Stamp | undefined\n deletedAt?: number | undefined\n}\n\n/** The greater of two stamps, either of which may be missing. */\nfunction maxStamp(a: Stamp | undefined, b: Stamp | undefined): Stamp | undefined {\n if (!a) return b\n if (!b) return a\n return compareStamps(a, b) >= 0 ? a : b\n}\n\n/** The greatest stamp a put claims, which is what its body is current as of. */\nfunction greatestStamp(fields: Record<string, Stamp>): Stamp | undefined {\n let best: Stamp | undefined\n for (const path of Object.keys(fields)) best = maxStamp(best, fields[path])\n return best\n}\n\n/** Every proper ancestor of a dotted path, outermost first. */\nfunction ancestorPaths(path: string): string[] {\n const out: string[] = []\n let index = path.indexOf(\".\")\n while (index !== -1) {\n out.push(path.slice(0, index))\n index = path.indexOf(\".\", index + 1)\n }\n return out\n}\n\n/**\n * Take `path` for `stamp`, unless something at or above it is stamped at least\n * as late — a write to `meta` covers `meta.a`, so the two cannot be compared\n * as if they were unrelated registers. Descendants the write covers are\n * dropped; descendants stamped later than it survive and are returned, because\n * their values have to be put back on top of whatever the write does.\n */\nfunction claimPath(state: RecordState, path: string, stamp: Stamp): string[] | null {\n const own = state.fields.get(path)\n if (own && compareStamps(stamp, own) <= 0) return null\n for (const ancestor of ancestorPaths(path)) {\n const above = state.fields.get(ancestor)\n if (above && compareStamps(stamp, above) <= 0) return null\n }\n const survivors: string[] = []\n const prefix = `${path}.`\n for (const [other, existing] of Array.from(state.fields)) {\n if (!other.startsWith(prefix)) continue\n if (compareStamps(existing, stamp) <= 0) state.fields.delete(other)\n else survivors.push(other)\n }\n state.fields.set(path, stamp)\n return survivors\n}\n\n/** True when this path, or one above it, carries a stamp of its own. */\nfunction isStamped(state: RecordState, path: string): boolean {\n if (state.fields.has(path)) return true\n for (const ancestor of ancestorPaths(path)) {\n if (state.fields.has(ancestor)) return true\n }\n return false\n}\n\n/**\n * The highest ancestor of `path` that the body has nothing at and no stamp\n * covers — the level a \"this is not here any more\" has to be applied at.\n */\nfunction highestAbsent(body: unknown, state: RecordState, path: string): string {\n for (const ancestor of ancestorPaths(path)) {\n if (readPath(body, ancestor) !== ABSENT) continue\n if (isStamped(state, ancestor) || hasStampedDescendant(state, ancestor)) continue\n return ancestor\n }\n return path\n}\n\n/** True when something strictly below this path carries a stamp. */\nfunction hasStampedDescendant(state: RecordState, path: string): boolean {\n const prefix = `${path}.`\n for (const other of state.fields.keys()) {\n if (other.startsWith(prefix)) return true\n }\n return false\n}\n\nexport function createCrdt<R extends UnknownRecord = UnknownRecord>(options: CrdtOptions<R>): Crdt<R> {\n const { clientId } = options\n const getRecord = options.getRecord ?? (() => undefined)\n const tombstoneLimit = options.tombstoneLimit ?? DEFAULT_TOMBSTONE_LIMIT\n const tombstoneMaxAgeMs = options.tombstoneMaxAgeMs ?? DEFAULT_TOMBSTONE_MAX_AGE_MS\n const now = options.now ?? (() => Date.now())\n const clock = createLamportClock()\n const states = new Map<string, RecordState>()\n let outgoing = createEmptyStampedDiff<R>()\n\n const stateFor = (id: string): RecordState => {\n let state = states.get(id)\n if (!state) {\n state = { fields: new Map() }\n states.set(id, state)\n }\n return state\n }\n\n /**\n * A merge in progress: `before` remembers what the store had when we first\n * looked at a record, `after` what it should end up with. Keeping both lets\n * one message both remove and re-create a record without contradicting\n * itself in the diff we hand back.\n */\n interface Pass {\n before: Map<string, R | undefined>\n after: Map<string, R | undefined>\n }\n\n const startPass = (): Pass => ({ before: new Map(), after: new Map() })\n\n const readBefore = (pass: Pass, id: string): R | undefined => {\n if (!pass.before.has(id)) pass.before.set(id, getRecord(id))\n return pass.before.get(id)\n }\n\n const readCurrent = (pass: Pass, id: string): R | undefined =>\n pass.after.has(id) ? pass.after.get(id) : readBefore(pass, id)\n\n const write = (pass: Pass, id: string, value: R | undefined): void => {\n readBefore(pass, id)\n pass.after.set(id, value)\n }\n\n const finishPass = (pass: Pass): RecordsDiff<R> => {\n const result = createEmptyRecordsDiff<R>()\n for (const [id, next] of pass.after) {\n const prev = pass.before.get(id)\n if (prev === next) continue\n const key = id as IdOf<R>\n if (prev === undefined) {\n if (next !== undefined) result.added[key] = next\n } else if (next === undefined) {\n result.removed[key] = prev\n } else {\n result.updated[key] = [prev, next]\n }\n }\n return result\n }\n\n /** Record a tombstone. Fields it dominates are dropped; survivors keep the record alive. */\n const applyRemove = (pass: Pass, id: string, stamp: Stamp): void => {\n const state = stateFor(id)\n if (state.deleted && compareStamps(stamp, state.deleted) <= 0) return\n state.deleted = stamp\n state.deletedAt = now()\n // The body older messages carry dies with the fields: only a message that\n // outranks the tombstone may supply one from here on.\n state.base = maxStamp(state.base, stamp)\n let alive = false\n for (const [path, existing] of Array.from(state.fields)) {\n if (compareStamps(existing, stamp) > 0) alive = true\n else state.fields.delete(path)\n }\n // A concurrent edit newer than the delete keeps the record: the delete only\n // wins when its stamp is greater than every field that would survive it.\n if (!alive) {\n if (readCurrent(pass, id) !== undefined) write(pass, id, undefined)\n return\n }\n // It survives — but the tombstone has just taken the provenance of every\n // field it outranks, and each replica is holding a different leftover for\n // those. Re-claim this body whole under a fresh stamp and send it, so the\n // record comes back as one agreed record rather than as a race.\n const record = readCurrent(pass, id)\n if (!record) return\n const revival: Stamp = { lamport: clock.tick(), client: clientId }\n const fields: Record<string, Stamp> = {}\n for (const path of leafPaths(record)) {\n fields[path] = revival\n claimPath(state, path, revival)\n }\n state.base = revival\n outgoing.puts.push({ record, fields, base: revival })\n }\n\n /**\n * Take every path neither the record nor the sender holds a stamp for from\n * the sender's body. Those are the fields the registers say nothing about,\n * and `base` is the one register that decides them.\n */\n const adoptBody = (record: R, body: R, state: RecordState): R => {\n const paths = new Set<string>([...leafPaths(body), ...leafPaths(record)])\n let next = record\n for (const path of Array.from(paths).sort()) {\n if (path === \"id\" || path === \"typeName\") continue\n if (isStamped(state, path) || hasStampedDescendant(state, path)) continue\n const value = readPath(body, path)\n // Take the whole subtree the body does not have, not just the leaf, so\n // dropping the last key of an object does not leave an empty one behind.\n next = writePath(next, value === ABSENT ? highestAbsent(body, state, path) : path, value)\n }\n return next\n }\n\n const applyPut = (pass: Pass, put: StampedPut<R>): void => {\n const id = put.record.id as string\n const state = stateFor(id)\n const claims: { path: string; survivors: string[] }[] = []\n for (const path of Object.keys(put.fields)) {\n const stamp = put.fields[path]\n if (!stamp) continue\n // A write older than the tombstone cannot resurrect the record.\n if (state.deleted && compareStamps(stamp, state.deleted) <= 0) continue\n // `<= 0` and not `< 0` inside: the same message twice changes nothing.\n const survivors = claimPath(state, path, stamp)\n if (survivors === null) continue\n claims.push({ path, survivors })\n }\n const base = put.base ?? greatestStamp(put.fields)\n const bodyWins = base !== undefined && (state.base === undefined || compareStamps(base, state.base) > 0)\n if (claims.length === 0 && !bodyWins) return\n\n const current = readCurrent(pass, id)\n if (current === undefined) {\n // Nothing here to merge into. A body alone must never bring a record\n // back: without a claim that outranks the tombstone this is a straggler.\n if (claims.length === 0) return\n write(pass, id, put.record)\n state.base = maxStamp(state.base, base)\n return\n }\n\n let next = current\n for (const { path, survivors } of claims) {\n if (path === \"id\") continue // identity is never merged\n const before = next\n next = writePath(next, path, readPath(put.record, path))\n // A subtree stamped later than this write outlives it, so put those\n // values back: `delete meta` racing `set meta.a` must land the same way\n // whichever arrives first.\n for (const survivor of survivors) next = writePath(next, survivor, readPath(before, survivor))\n }\n if (bodyWins) {\n next = adoptBody(next, put.record, state)\n state.base = base\n }\n if (next !== current) write(pass, id, next)\n }\n\n const observeStamps = (stamped: StampedDiff<R>): void => {\n let max = -1\n for (const put of stamped.puts) {\n for (const path of Object.keys(put.fields)) {\n const stamp = put.fields[path]\n if (stamp && stamp.lamport > max) max = stamp.lamport\n }\n if (put.base && put.base.lamport > max) max = put.base.lamport\n }\n for (const remove of stamped.removes) {\n if (remove.stamp.lamport > max) max = remove.stamp.lamport\n }\n if (max >= 0) clock.observe(max)\n }\n\n const collectTombstones = (): void => {\n const gone: { id: string; at: number }[] = []\n for (const [id, state] of states) {\n if (state.deleted && state.fields.size === 0) gone.push({ id, at: state.deletedAt ?? 0 })\n }\n if (gone.length === 0) return\n const cutoff = now() - tombstoneMaxAgeMs\n let overflow = gone.length - tombstoneLimit\n if (overflow <= 0 && gone.every((entry) => entry.at >= cutoff)) return\n gone.sort((a, b) => a.at - b.at)\n for (const entry of gone) {\n if (overflow > 0) {\n states.delete(entry.id)\n overflow--\n continue\n }\n if (entry.at < cutoff) states.delete(entry.id)\n }\n }\n\n return {\n clientId,\n clock,\n\n stampLocal(diff: RecordsDiff<R>): StampedDiff<R> {\n const stamp: Stamp = { lamport: clock.tick(), client: clientId }\n const result = createEmptyStampedDiff<R>()\n\n for (const id in diff.added) {\n const record = diff.added[id as IdOf<R>]\n if (!record) continue\n const fields: Record<string, Stamp> = {}\n const state = stateFor(id)\n for (const path of leafPaths(record)) {\n fields[path] = stamp\n claimPath(state, path, stamp)\n }\n // Our record is our whole view of it, as of now.\n state.base = stamp\n result.puts.push({ record, fields, base: stamp })\n }\n\n for (const id in diff.updated) {\n const pair = diff.updated[id as IdOf<R>]\n if (!pair) continue\n const [from, to] = pair\n const paths = changedPaths(from, to)\n if (paths.length === 0) continue\n const fields: Record<string, Stamp> = {}\n const state = stateFor(id)\n for (const path of paths) {\n fields[path] = stamp\n claimPath(state, path, stamp)\n }\n state.base = stamp\n result.puts.push({ record: to, fields, base: stamp })\n }\n\n for (const id in diff.removed) {\n const state = stateFor(id)\n state.deleted = stamp\n state.deletedAt = now()\n state.fields.clear()\n state.base = stamp\n result.removes.push({ id, stamp })\n }\n\n collectTombstones()\n return result\n },\n\n mergeRemote(stamped: StampedDiff<R>): RecordsDiff<R> {\n observeStamps(stamped)\n const pass = startPass()\n for (const remove of stamped.removes) applyRemove(pass, remove.id, remove.stamp)\n for (const put of stamped.puts) applyPut(pass, put)\n collectTombstones()\n return finishPass(pass)\n },\n\n getState(): CrdtState {\n const records: Record<string, CrdtRecordState> = {}\n for (const [id, state] of states) {\n const fields: Record<string, Stamp> = {}\n for (const [path, stamp] of state.fields) fields[path] = stamp\n records[id] = {\n fields,\n ...(state.base ? { base: state.base } : {}),\n ...(state.deleted ? { deleted: state.deleted, deletedAt: state.deletedAt ?? 0 } : {}),\n }\n }\n return { version: CRDT_STATE_VERSION, clock: clock.get(), records }\n },\n\n applyState(state: CrdtState): RecordsDiff<R> {\n const pass = startPass()\n if (state.version !== CRDT_STATE_VERSION) return finishPass(pass)\n clock.observe(state.clock)\n for (const id of Object.keys(state.records)) {\n const incoming = state.records[id]\n if (!incoming) continue\n if (incoming.deleted) applyRemove(pass, id, incoming.deleted)\n const local = stateFor(id)\n for (const path of Object.keys(incoming.fields)) {\n const stamp = incoming.fields[path]\n if (!stamp) continue\n clock.observe(stamp.lamport)\n if (local.deleted && compareStamps(stamp, local.deleted) <= 0) continue\n claimPath(local, path, stamp)\n }\n if (incoming.base) {\n clock.observe(incoming.base.lamport)\n local.base = maxStamp(local.base, incoming.base)\n }\n }\n collectTombstones()\n return finishPass(pass)\n },\n\n takeOutgoing(): StampedDiff<R> | null {\n if (isStampedDiffEmpty(outgoing)) return null\n const result = outgoing\n outgoing = createEmptyStampedDiff<R>()\n return result\n },\n\n size: () => states.size,\n collectTombstones,\n }\n}\n\n/**\n * Turn a peer's snapshot into a message the ordinary merge can eat: its\n * records supply the values, its state supplies the stamps. Paths the state\n * says nothing about are not claimed, so a snapshot can never overwrite a\n * field the receiver has a stamp for.\n */\nexport function stampedDiffFromSnapshot<R extends UnknownRecord>(\n records: readonly R[],\n state: CrdtState,\n): StampedDiff<R> {\n const result = createEmptyStampedDiff<R>()\n for (const record of records) {\n const entry = state.records[record.id as string]\n const fields = entry?.fields ?? {}\n const base = entry?.base ?? greatestStamp(fields)\n result.puts.push(base ? { record, fields, base } : { record, fields })\n }\n for (const id of Object.keys(state.records)) {\n const deleted = state.records[id]?.deleted\n if (deleted) result.removes.push({ id, stamp: deleted })\n }\n return result\n}\n\n/* -------------------------------------------------------------------------- */\n/* field paths */\n/* -------------------------------------------------------------------------- */\n\n/** A path that is not present at all, as distinct from one holding `undefined`. */\nconst ABSENT = Symbol(\"absent\")\n\nexport function isPlainObject(value: unknown): value is Record<string, unknown> {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return false\n const proto = Object.getPrototypeOf(value) as unknown\n return proto === Object.prototype || proto === null\n}\n\n/**\n * Every leaf path of a value. Plain objects are recursed into; arrays,\n * primitives, `null` and empty objects are leaves — an array is one register\n * holding the whole array.\n */\nexport function leafPaths(value: unknown, prefix = \"\"): string[] {\n const out: string[] = []\n collectLeaves(value, prefix, out)\n return out\n}\n\nfunction collectLeaves(value: unknown, prefix: string, out: string[]): void {\n if (isPlainObject(value)) {\n const keys = Object.keys(value)\n if (keys.length > 0) {\n for (const key of keys) collectLeaves(value[key], prefix ? `${prefix}.${key}` : key, out)\n return\n }\n }\n if (prefix !== \"\") out.push(prefix)\n}\n\n/** The paths whose values differ between two versions of a record. */\nexport function changedPaths(from: unknown, to: unknown): string[] {\n const out: string[] = []\n collectChanges(from, to, \"\", out)\n return out\n}\n\nfunction collectChanges(from: unknown, to: unknown, prefix: string, out: string[]): void {\n if (isPlainObject(from) && isPlainObject(to)) {\n for (const key of Object.keys(from)) {\n const path = prefix ? `${prefix}.${key}` : key\n // A key that disappeared: stamp the whole subtree as one removal.\n if (!(key in to)) out.push(path)\n else collectChanges(from[key], to[key], path, out)\n }\n for (const key of Object.keys(to)) {\n if (key in from) continue\n collectLeaves(to[key], prefix ? `${prefix}.${key}` : key, out)\n }\n return\n }\n if (!deepEqual(from, to) && prefix !== \"\") out.push(prefix)\n}\n\nfunction readPath(source: unknown, path: string): unknown {\n let current: unknown = source\n for (const segment of path.split(\".\")) {\n if (!isPlainObject(current) || !(segment in current)) return ABSENT\n current = current[segment]\n }\n return current\n}\n\n/**\n * A copy of `record` with `path` set to `value` (or the key deleted when the\n * path is absent in the source). Every level along the path is cloned, because\n * the store freezes records, their `props` and their `meta`.\n */\nfunction writePath<R extends UnknownRecord>(record: R, path: string, value: unknown): R {\n const current = readPath(record, path)\n if (value === ABSENT) {\n if (current === ABSENT) return record\n } else if (current !== ABSENT && deepEqual(current, value)) {\n return record\n }\n const segments = path.split(\".\")\n return setIn(record as unknown as Record<string, unknown>, segments, 0, value) as unknown as R\n}\n\nfunction setIn(\n target: Record<string, unknown>,\n segments: string[],\n index: number,\n value: unknown,\n): Record<string, unknown> {\n const key = segments[index]\n if (key === undefined) return target\n const next: Record<string, unknown> = { ...target }\n if (index === segments.length - 1) {\n if (value === ABSENT) delete next[key]\n else next[key] = value\n return next\n }\n const child = next[key]\n next[key] = setIn(isPlainObject(child) ? child : {}, segments, index + 1, value)\n return next\n}\n\nexport function deepEqual(a: unknown, b: unknown): boolean {\n if (Object.is(a, b)) return true\n if (Array.isArray(a)) {\n if (!Array.isArray(b) || a.length !== b.length) return false\n for (let i = 0; i < a.length; i++) {\n if (!deepEqual(a[i], b[i])) return false\n }\n return true\n }\n if (isPlainObject(a) && isPlainObject(b)) {\n const keysA = Object.keys(a)\n const keysB = Object.keys(b)\n if (keysA.length !== keysB.length) return false\n for (const key of keysA) {\n if (!(key in b)) return false\n if (!deepEqual(a[key], b[key])) return false\n }\n return true\n }\n return false\n}\n","import { react } from \"@mocanvas/state\"\nimport {\n InstancePresenceRecordType,\n type Editor,\n type InstancePresence,\n type InstancePresenceId,\n type UserId,\n} from \"@mocanvas/editor\"\nimport type { IdOf, Store, UnknownRecord } from \"@mocanvas/store\"\n\n/** Presence is sent at most this often (30 Hz). */\nexport const DEFAULT_PRESENCE_THROTTLE_MS = Math.ceil(1000 / 30)\n/** A presence record is re-sent this often even when nothing changed. */\nexport const DEFAULT_PRESENCE_HEARTBEAT_MS = 3000\n/** A collaborator we have not heard from in this long is dropped. */\nexport const DEFAULT_PRESENCE_TIMEOUT_MS = 10_000\n\n/**\n * The part of the editor presence reads. Declared structurally so that the\n * sync layer does not need a live `Editor` (and its WASM engine) to be tested.\n */\nexport interface PresenceEditor {\n readonly store: Store<any, any>\n readonly inputs: { readonly currentPagePoint: { x: number; y: number } }\n readonly user: { getId(): UserId; getName(): string; getColor(): string }\n getCurrentPageId(): string\n getCamera(): { x: number; y: number; z: number }\n getSelectedShapeIds(): readonly string[]\n getInstanceState(): {\n cursor: { type: string; rotation: number }\n brush: { x: number; y: number; w: number; h: number } | null\n scribbles: readonly unknown[]\n followingUserId: UserId | null\n }\n /**\n * Optional: the id this instance's presence record is published under.\n *\n * A real `Editor` has one and it is the identity that matters here — a user\n * id is per browser, so two tabs of one browser are one user and would\n * discard each other's presence as their own. Structural stand-ins (tests,\n * a headless participant) may omit it and fall back to\n * {@link presenceIdForClient}.\n */\n getInstancePresenceId?(): InstancePresenceId\n /** Optional: used to sample the cursor, which is not itself a signal. */\n on?(name: \"event\", fn: () => void): () => void\n}\n\n// A real Editor must satisfy the structural interface above; this fails to\n// compile if the two ever drift apart.\ntype Assert<T extends true> = T\nexport type EditorIsPresenceEditor = Assert<Editor extends PresenceEditor ? true : false>\n\nexport interface PresenceSyncOptions {\n editor: PresenceEditor\n /** Identifies this tab; one user may have several. */\n clientId: string\n send(record: InstancePresence): void\n throttleMs?: number\n heartbeatMs?: number\n}\n\nexport interface PresenceSync {\n start(): void\n stop(): void\n /**\n * Ask for a send; collapses with any other request inside the throttle\n * window.\n *\n * `force` sends even when nothing about this client has changed, which is\n * what a *re-announcement* is: somebody who was not listening when we last\n * spoke has arrived, so the record has to go out again although it says the\n * same thing. Without it a peer that has been sitting still since before the\n * newcomer joined stays invisible until its next heartbeat.\n */\n poke(force?: boolean): void\n /** The record last handed to `send`, for tests and debugging. */\n getLastSent(): InstancePresence | null\n dispose(): void\n}\n\n/** The id a client's presence record always uses, so updates overwrite in place. */\nexport function presenceIdForClient(clientId: string): InstancePresenceId {\n return InstancePresenceRecordType.createId(clientId) as InstancePresenceId\n}\n\n/**\n * Watches the editor and pushes the local presence record out, at most once per\n * throttle window, plus a heartbeat so that idle collaborators do not time out.\n */\nexport function createPresenceSync(options: PresenceSyncOptions): PresenceSync {\n const { editor, clientId, send } = options\n const throttleMs = options.throttleMs ?? DEFAULT_PRESENCE_THROTTLE_MS\n const heartbeatMs = options.heartbeatMs ?? DEFAULT_PRESENCE_HEARTBEAT_MS\n // The editor's own instance id when it has one, so that the id this tab\n // publishes under is the same id the editor filters its own record by.\n // `presenceIdForClient` is the fallback for a structural stand-in.\n const id = editor.getInstancePresenceId?.() ?? presenceIdForClient(clientId)\n\n let timer: ReturnType<typeof setTimeout> | null = null\n let heartbeat: ReturnType<typeof setInterval> | null = null\n let stopReactor: (() => void) | null = null\n let stopEvents: (() => void) | null = null\n let last: InstancePresence | null = null\n let running = false\n\n const build = (): InstancePresence => {\n const instance = editor.getInstanceState()\n const point = editor.inputs.currentPagePoint\n return InstancePresenceRecordType.create({\n id,\n userId: editor.user.getId(),\n userName: editor.user.getName(),\n color: editor.user.getColor(),\n currentPageId: editor.getCurrentPageId() as InstancePresence[\"currentPageId\"],\n cursor: { x: point.x, y: point.y, type: instance.cursor.type, rotation: instance.cursor.rotation },\n camera: { ...editor.getCamera() },\n selectedShapeIds: [...editor.getSelectedShapeIds()] as InstancePresence[\"selectedShapeIds\"],\n brush: instance.brush ? { ...instance.brush } : null,\n scribbles: [...instance.scribbles] as InstancePresence[\"scribbles\"],\n followingUserId: instance.followingUserId,\n lastActivityTimestamp: Date.now(),\n chatMessage: \"\",\n meta: {},\n })\n }\n\n const flush = (force: boolean) => {\n if (!running) return\n const next = build()\n if (!force && last && isSamePresence(last, next)) return\n last = next\n send(next)\n }\n\n /** Set while a forced send is waiting out the throttle window. */\n let pendingForce = false\n\n const poke = (force = false) => {\n if (!running) return\n if (force) pendingForce = true\n if (timer !== null) return\n timer = setTimeout(() => {\n timer = null\n const forced = pendingForce\n pendingForce = false\n flush(forced)\n }, throttleMs)\n }\n\n return {\n start() {\n if (running) return\n running = true\n // Camera, page and selection are signals, so a reactor covers them.\n stopReactor = react(\"sync.presence\", () => {\n editor.getCamera()\n editor.getCurrentPageId()\n editor.getSelectedShapeIds()\n editor.getInstanceState()\n editor.user.getName()\n editor.user.getColor()\n poke()\n })\n // The pointer position lives on `inputs`, which is not reactive.\n stopEvents = editor.on?.(\"event\", poke) ?? null\n heartbeat = setInterval(() => flush(true), heartbeatMs)\n flush(true)\n },\n stop() {\n running = false\n pendingForce = false\n if (timer !== null) {\n clearTimeout(timer)\n timer = null\n }\n if (heartbeat !== null) {\n clearInterval(heartbeat)\n heartbeat = null\n }\n stopReactor?.()\n stopReactor = null\n stopEvents?.()\n stopEvents = null\n },\n poke,\n getLastSent: () => last,\n dispose() {\n this.stop()\n last = null\n },\n }\n}\n\n/** Equal apart from `lastActivityTimestamp`, which always moves. */\nexport function isSamePresence(a: InstancePresence, b: InstancePresence): boolean {\n return (\n a.userId === b.userId &&\n a.userName === b.userName &&\n a.color === b.color &&\n a.currentPageId === b.currentPageId &&\n a.chatMessage === b.chatMessage &&\n a.followingUserId === b.followingUserId &&\n sameCursor(a.cursor, b.cursor) &&\n sameCamera(a.camera, b.camera) &&\n sameIds(a.selectedShapeIds, b.selectedShapeIds) &&\n sameBrush(a.brush, b.brush) &&\n a.scribbles.length === b.scribbles.length &&\n a.scribbles.every((s, i) => s === b.scribbles[i])\n )\n}\n\n/** Cameras compare equal when both are absent, or when all three fields match. */\nfunction sameCamera(a: InstancePresence[\"camera\"], b: InstancePresence[\"camera\"]): boolean {\n if (a === null || b === null) return a === b\n return a.x === b.x && a.y === b.y && a.z === b.z\n}\n\n/** Cursors compare equal when both are absent, or when all four fields match. */\nfunction sameCursor(a: InstancePresence[\"cursor\"], b: InstancePresence[\"cursor\"]): boolean {\n if (a === null || b === null) return a === b\n return a.x === b.x && a.y === b.y && a.type === b.type && a.rotation === b.rotation\n}\n\nfunction sameIds(a: readonly string[], b: readonly string[]): boolean {\n return a.length === b.length && a.every((id, i) => id === b[i])\n}\n\nfunction sameBrush(a: InstancePresence[\"brush\"], b: InstancePresence[\"brush\"]): boolean {\n if (a === b) return true\n if (!a || !b) return false\n return a.x === b.x && a.y === b.y && a.w === b.w && a.h === b.h\n}\n\n/**\n * Holds the presence records of everyone else: puts them in the store as they\n * arrive, drops them on `bye`, and sweeps clients that went quiet.\n */\nexport class PresenceRoom<R extends UnknownRecord> {\n private readonly seen = new Map<string, { id: string; at: number }>()\n private sweeper: ReturnType<typeof setInterval> | null = null\n\n constructor(\n private readonly store: Store<R, any>,\n private readonly timeoutMs: number = DEFAULT_PRESENCE_TIMEOUT_MS,\n ) {}\n\n /** Store an incoming presence record and remember when we saw its client. */\n receive(clientId: string, record: R): void {\n const previous = this.seen.get(clientId)\n if (previous && previous.id !== record.id) this.removeRecord(previous.id)\n this.seen.set(clientId, { id: record.id, at: Date.now() })\n this.store.mergeRemoteChanges(() => {\n this.store.put([record])\n })\n }\n\n /** Drop one client's presence (it said goodbye). */\n remove(clientId: string): void {\n const entry = this.seen.get(clientId)\n if (!entry) return\n this.seen.delete(clientId)\n this.removeRecord(entry.id)\n }\n\n /** Drop every client we have not heard from within the timeout. */\n sweep(now: number = Date.now()): void {\n const stale: string[] = []\n for (const [clientId, entry] of this.seen) {\n if (now - entry.at > this.timeoutMs) stale.push(clientId)\n }\n for (const clientId of stale) this.remove(clientId)\n }\n\n startSweeping(intervalMs: number = Math.max(1000, Math.floor(this.timeoutMs / 4))): void {\n if (this.sweeper !== null) return\n this.sweeper = setInterval(() => this.sweep(), intervalMs)\n }\n\n stopSweeping(): void {\n if (this.sweeper === null) return\n clearInterval(this.sweeper)\n this.sweeper = null\n }\n\n /** Remove every record this room put in the store. */\n clear(): void {\n const ids = Array.from(this.seen.values(), (entry) => entry.id)\n this.seen.clear()\n for (const id of ids) this.removeRecord(id)\n }\n\n private removeRecord(id: string): void {\n const recordId = id as IdOf<R>\n if (!this.store.unsafeGetWithoutCapture(recordId)) return\n this.store.mergeRemoteChanges(() => {\n this.store.remove([recordId])\n })\n }\n}\n","import type { UnknownRecord } from \"@mocanvas/store\"\nimport { isCrdtStateLike, isStampedDiffLike, type CrdtState, type StampedDiff } from \"./crdt\"\n\n/**\n * Bumped when the wire format changes incompatibly.\n *\n * 2: `diff` carries a `StampedDiff` (per-field Lamport stamps) instead of a\n * plain `RecordsDiff`, `snapshot` carries the CRDT state next to the\n * records, and every message carries `version`.\n * 3: a put carries `base`, the stamp its whole record body is current as of,\n * and a record kept alive by an edit that outranks a delete is re-claimed\n * whole under a fresh stamp. A version 2 peer merges the same messages to a\n * different answer, so the two must not share a room.\n */\nexport const PROTOCOL_VERSION = 3\n\n/**\n * Every message is one JSON object with a `type` discriminator and a\n * `version`, which `encodeMessage` stamps on and `decodeMessage` checks.\n *\n * ```\n * hello a client joined; existing peers answer with a snapshot\n * snapshot the document-scope records plus the sender's CRDT stamps\n * diff one squashed store diff, stamped field by field\n * presence one presence record (cursor, camera, selection)\n * bye the client is leaving; drop its presence\n * unsupported synthesised locally for a peer on another protocol version\n * ```\n */\nexport type SyncMessage<R extends UnknownRecord = UnknownRecord> =\n | HelloMessage\n | SnapshotMessage<R>\n | DiffMessage<R>\n | PresenceMessage<R>\n | ByeMessage\n | UnsupportedMessage\n\nexport interface HelloMessage {\n type: \"hello\"\n clientId: string\n version: number\n}\n\nexport interface SnapshotMessage<R extends UnknownRecord = UnknownRecord> {\n type: \"snapshot\"\n records: R[]\n /** The stamps that go with those records, so a joiner does not start blank. */\n state: CrdtState\n}\n\nexport interface DiffMessage<R extends UnknownRecord = UnknownRecord> {\n type: \"diff\"\n clientId: string\n /** Per-client counter; only used for logging and de-duplication. */\n seq: number\n diff: StampedDiff<R>\n}\n\nexport interface PresenceMessage<R extends UnknownRecord = UnknownRecord> {\n type: \"presence\"\n clientId: string\n record: R\n}\n\nexport interface ByeMessage {\n type: \"bye\"\n clientId: string\n}\n\n/**\n * Never sent: `decodeMessage` returns this instead of guessing at a message\n * whose `version` is not ours, so a peer on another protocol is reported once\n * rather than half-parsed.\n */\nexport interface UnsupportedMessage {\n type: \"unsupported\"\n version: number\n clientId?: string\n}\n\nexport function encodeMessage<R extends UnknownRecord>(message: SyncMessage<R>): string {\n return JSON.stringify({ ...message, version: PROTOCOL_VERSION })\n}\n\n/**\n * Parse a message off the wire. Returns `null` for anything malformed and an\n * `unsupported` message for a peer on another protocol version: neither can\n * crash this client, and neither is ever mistaken for a message we understand.\n */\nexport function decodeMessage<R extends UnknownRecord = UnknownRecord>(data: unknown): SyncMessage<R> | null {\n let value: unknown = data\n if (typeof data === \"string\") {\n try {\n value = JSON.parse(data)\n } catch {\n return null\n }\n }\n if (typeof value !== \"object\" || value === null) return null\n const message = value as Record<string, unknown>\n\n const version = message[\"version\"]\n if (typeof version === \"number\" && version !== PROTOCOL_VERSION) {\n const clientId = message[\"clientId\"]\n return typeof clientId === \"string\"\n ? { type: \"unsupported\", version, clientId }\n : { type: \"unsupported\", version }\n }\n\n switch (message[\"type\"]) {\n case \"hello\":\n if (typeof message[\"clientId\"] !== \"string\") return null\n if (typeof message[\"version\"] !== \"number\") return null\n return { type: \"hello\", clientId: message[\"clientId\"], version: message[\"version\"] }\n case \"snapshot\": {\n const records = message[\"records\"]\n if (!Array.isArray(records)) return null\n if (!records.every(isRecordLike)) return null\n const state = message[\"state\"]\n if (!isCrdtStateLike(state)) return null\n return { type: \"snapshot\", records: records as R[], state: state as CrdtState }\n }\n case \"diff\": {\n if (typeof message[\"clientId\"] !== \"string\") return null\n if (typeof message[\"seq\"] !== \"number\") return null\n if (!isStampedDiffLike(message[\"diff\"])) return null\n return {\n type: \"diff\",\n clientId: message[\"clientId\"],\n seq: message[\"seq\"],\n diff: message[\"diff\"] as StampedDiff<R>,\n }\n }\n case \"presence\": {\n if (typeof message[\"clientId\"] !== \"string\") return null\n if (!isRecordLike(message[\"record\"])) return null\n return { type: \"presence\", clientId: message[\"clientId\"], record: message[\"record\"] as R }\n }\n case \"bye\":\n if (typeof message[\"clientId\"] !== \"string\") return null\n return { type: \"bye\", clientId: message[\"clientId\"] }\n default:\n return null\n }\n}\n\nfunction isRecordLike(value: unknown): boolean {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as { id?: unknown }).id === \"string\" &&\n typeof (value as { typeName?: unknown }).typeName === \"string\"\n )\n}\n","import { atom, type Signal } from \"@mocanvas/state\"\nimport { warnOnce } from \"@mocanvas/editor\"\nimport {\n createEmptyRecordsDiff,\n isRecordsDiffEmpty,\n uniqueId,\n type IdOf,\n type RecordsDiff,\n type Store,\n type UnknownRecord,\n} from \"@mocanvas/store\"\nimport { createCrdt, deepEqual, isStampedDiffEmpty, stampedDiffFromSnapshot, type Crdt, type StampedDiff } from \"./crdt\"\nimport {\n createPresenceSync,\n DEFAULT_PRESENCE_TIMEOUT_MS,\n PresenceRoom,\n type PresenceEditor,\n type PresenceSync,\n} from \"./presence\"\nimport { PROTOCOL_VERSION, type SyncMessage } from \"./protocol\"\nimport type { Transport } from \"./transport\"\n\nexport type SyncStatus = \"offline\" | \"connecting\" | \"online\"\n\nexport interface SyncClientOptions<R extends UnknownRecord = UnknownRecord> {\n store: Store<R, any>\n /** Identifies the document. The transport is expected to be scoped to it too. */\n roomId: string\n transport: Transport<R>\n /** Turn on cursor/selection sharing for an editor. */\n presence?: { editor: PresenceEditor } | undefined\n /** Defaults to a fresh random id; one per tab, not per user. */\n clientId?: string | undefined\n /** Drop a collaborator after this long without a presence message. Default 10s. */\n presenceTimeoutMs?: number | undefined\n /** Upper bound on presence send rate. Default 34ms (~30 Hz). */\n presenceThrottleMs?: number | undefined\n presenceHeartbeatMs?: number | undefined\n /** How many tombstones for vanished records to keep. See `crdt.ts`. */\n tombstoneLimit?: number | undefined\n /** How long such a tombstone lives, in ms. */\n tombstoneMaxAgeMs?: number | undefined\n /** Called for protocol-level problems (a peer on another version, say). */\n onError?: ((error: Error) => void) | undefined\n}\n\nexport interface SyncClient {\n readonly clientId: string\n readonly roomId: string\n connect(): void\n disconnect(): void\n /** A signal, so React and reactors can follow the connection. */\n getStatus(): Signal<SyncStatus>\n dispose(): void\n}\n\n/**\n * Joins one room over `transport` and keeps `store` in step with its peers.\n *\n * Document changes made locally by the user are stamped by the CRDT (see\n * `crdt.ts`) and broadcast; incoming messages are merged field by field and\n * only the fields that won are applied, as remote changes, so they neither\n * echo back nor land in the undo stack. Concurrent edits to different fields\n * of one record all survive, and replicas converge whatever order messages\n * arrive in.\n */\nexport function createSyncClient<R extends UnknownRecord = UnknownRecord>(\n options: SyncClientOptions<R>,\n): SyncClient {\n const { store, roomId, transport } = options\n const clientId = options.clientId ?? uniqueId(12)\n const status = atom<SyncStatus>(`sync.status:${roomId}`, \"offline\")\n const room = new PresenceRoom<R>(store, options.presenceTimeoutMs ?? DEFAULT_PRESENCE_TIMEOUT_MS)\n\n const crdt: Crdt<R> = createCrdt<R>({\n clientId,\n getRecord: (id) => store.unsafeGetWithoutCapture(id as IdOf<R>) as R | undefined,\n ...(options.tombstoneLimit !== undefined ? { tombstoneLimit: options.tombstoneLimit } : {}),\n ...(options.tombstoneMaxAgeMs !== undefined ? { tombstoneMaxAgeMs: options.tombstoneMaxAgeMs } : {}),\n })\n\n const unsubscribes: (() => void)[] = []\n const reportedVersions = new Set<number>()\n /** Which page each peer says it is looking at, for the check below. */\n const peerPages = new Map<string, string>()\n let warnedAboutPages = false\n let presence: PresenceSync | null = null\n let seq = 0\n /**\n * True while this replica is waiting to be handed the room, which only a\n * replica holding no stamps at all ever is: one that has just loaded the\n * page has nothing to lose by taking a peer's document whole, and nothing\n * worth pushing at anyone either. A replica with stamps — including one that\n * edited while it was disconnected — merges instead.\n */\n let awaitingSnapshot = false\n let connected = false\n let disposed = false\n\n const send = (message: SyncMessage<R>) => {\n try {\n transport.send(message)\n } catch (error) {\n options.onError?.(error instanceof Error ? error : new Error(String(error)))\n }\n }\n\n const broadcast = (stamped: StampedDiff<R>) => {\n if (isStampedDiffEmpty(stamped)) return\n send({ type: \"diff\", clientId, seq: seq++, diff: stamped })\n }\n\n /**\n * Writes the CRDT minted while merging — a record revived by an edit that\n * outranks a delete — go out like any other local diff.\n */\n const flushCrdt = () => {\n const outgoing = crdt.takeOutgoing()\n if (outgoing) broadcast(outgoing)\n }\n\n const sendSnapshot = () => {\n send({\n type: \"snapshot\",\n records: Object.values(store.serialize(\"document\")) as R[],\n state: crdt.getState(),\n })\n }\n\n const applyRemote = (diff: RecordsDiff<R>) => {\n if (isRecordsDiffEmpty(diff)) return\n const actual = store.extractingChanges(() => {\n store.mergeRemoteChanges(() => {\n store.applyDiff(diff)\n })\n })\n reportRewrites(diff, actual)\n }\n\n /**\n * What the store actually did, minus what we asked it to do. A validator or\n * a side effect that rewrites a merged record — or touches another one —\n * changes this replica only: the change is reported as `remote`, so the\n * listener below never sees it, and no peer would ever hear about it. Stamp\n * that difference as a local write and send it, so the rewrite is one more\n * write everyone can agree on rather than a private divergence.\n *\n * A side effect that is a function of the record and the document rewrites\n * the same way everywhere, so this settles in one round: the peers apply a\n * value they already hold and produce nothing further. A side effect that is\n * not a function of its inputs cannot converge, here or anywhere.\n */\n const reportRewrites = (asked: RecordsDiff<R>, actual: RecordsDiff<R>) => {\n const rewrite = createEmptyRecordsDiff<R>()\n const intended = (id: IdOf<R>): R | undefined => asked.added[id] ?? asked.updated[id]?.[1]\n const isDocument = (record: R) => store.getScope(record.typeName) === \"document\"\n\n for (const key in actual.added) {\n const id = key as IdOf<R>\n const after = actual.added[id]\n if (!after || !isDocument(after)) continue\n const want = intended(id)\n if (!want) rewrite.added[id] = after\n else if (!deepEqual(want, after)) rewrite.updated[id] = [want, after]\n }\n for (const key in actual.updated) {\n const id = key as IdOf<R>\n const pair = actual.updated[id]\n if (!pair || !isDocument(pair[1])) continue\n const want = intended(id)\n if (!want) rewrite.updated[id] = pair\n else if (!deepEqual(want, pair[1])) rewrite.updated[id] = [want, pair[1]]\n }\n for (const key in actual.removed) {\n const id = key as IdOf<R>\n const before = actual.removed[id]\n if (!before || !isDocument(before)) continue\n if (!asked.removed[id]) rewrite.removed[id] = before\n }\n if (isRecordsDiffEmpty(rewrite)) return\n broadcast(crdt.stampLocal(rewrite))\n }\n\n /**\n * Outgoing document changes: only what this user did, never what we applied\n * from a peer (those arrive with source \"remote\"). It is wired up when the\n * client is built and stays wired until `dispose`, so an edit made before\n * `connect()` or after `disconnect()` is stamped when it happens rather than\n * when the socket is up. That is what carries offline work through a\n * reconnect: unstamped, it would lose to every stamped write in the room.\n */\n const stopListening = store.listen(\n (entry) => {\n if (isRecordsDiffEmpty(entry.changes)) return\n const stamped = crdt.stampLocal(entry.changes)\n if (connected) broadcast(stamped)\n },\n { source: \"user\", scope: \"document\" },\n )\n\n const handle = (message: SyncMessage<R>) => {\n if (disposed) return\n switch (message.type) {\n case \"unsupported\": {\n if (reportedVersions.has(message.version)) return\n reportedVersions.add(message.version)\n options.onError?.(\n new Error(`A peer speaks protocol ${message.version}, we speak ${PROTOCOL_VERSION}`),\n )\n return\n }\n case \"hello\": {\n if (message.clientId === clientId) return\n if (message.version !== PROTOCOL_VERSION) {\n options.onError?.(\n new Error(`Peer ${message.clientId} speaks protocol ${message.version}, we speak ${PROTOCOL_VERSION}`),\n )\n return\n }\n // A newcomer needs the document; every established peer answers with\n // its records and its stamps. Answering also makes us established: we\n // hold state of our own now, so nobody's snapshot can start us blank.\n awaitingSnapshot = false\n sendSnapshot()\n // Re-announce our presence so the newcomer sees us immediately.\n // Forced: our record has not changed — it is the audience that has —\n // and an unforced poke would compare equal and send nothing, leaving\n // us invisible to the newcomer until our next heartbeat.\n presence?.poke(true)\n return\n }\n case \"snapshot\": {\n // Wholesale adoption is only safe with nothing of our own to lose:\n // one stamp means one edit that would be overwritten by the sender's\n // body, so `crdt.size()` and not just the flag decides.\n if (awaitingSnapshot && crdt.size() === 0) {\n // We have no stamps at all. Take the sender's records and its clock\n // wholesale, so our own later writes sort after everything already\n // in the room instead of racing it from zero.\n awaitingSnapshot = false\n const removals = crdt.applyState(message.state)\n const actual = store.extractingChanges(() => {\n store.mergeRemoteChanges(() => {\n store.put(message.records)\n store.applyDiff(removals)\n })\n })\n const asked = createEmptyRecordsDiff<R>()\n for (const record of message.records) asked.added[record.id as IdOf<R>] = record\n reportRewrites(asked, actual)\n flushCrdt()\n return\n }\n // Otherwise this is a reconciliation (a peer reconnected, or answered\n // someone else's hello), or we hold edits of our own: merge it field\n // by field. Nothing we hold a newer stamp for can be overwritten, so\n // it is always safe to apply.\n awaitingSnapshot = false\n applyRemote(crdt.mergeRemote(stampedDiffFromSnapshot(message.records, message.state)))\n flushCrdt()\n return\n }\n case \"diff\": {\n if (message.clientId === clientId) return\n if (isStampedDiffEmpty(message.diff)) return\n applyRemote(crdt.mergeRemote(message.diff))\n flushCrdt()\n return\n }\n case \"presence\": {\n if (message.clientId === clientId) return\n room.receive(message.clientId, message.record)\n notePeerPage(message.clientId, message.record)\n return\n }\n case \"bye\": {\n if (message.clientId === clientId) return\n room.remove(message.clientId)\n peerPages.delete(message.clientId)\n return\n }\n }\n }\n\n /**\n * Say something when this replica is looking at a page none of its peers is.\n *\n * Everything still works — diffs and presence arrive, the status says\n * `online` — and nothing is drawn: `getCollaboratorsOnCurrentPage()` filters\n * every peer out and their shapes are on a page this replica is not showing.\n * It is the failure mode with the least evidence, so the library says it out\n * loud rather than letting a developer hunt for a transport bug that is not\n * there. Once per client, and only outside production.\n */\n const notePeerPage = (peerId: string, record: R) => {\n const editor = options.presence?.editor\n if (!editor || warnedAboutPages) return\n const page = (record as unknown as { currentPageId?: unknown }).currentPageId\n if (typeof page !== \"string\") return\n peerPages.set(peerId, page)\n const mine = editor.getCurrentPageId()\n for (const peerPage of peerPages.values()) if (peerPage === mine) return\n warnedAboutPages = true\n const theirs = Array.from(new Set(peerPages.values())).join(\", \")\n warnOnce(\n `sync.page:${roomId}:${clientId}`,\n `mocanvas/sync: this replica is on page ${mine}, and none of the ${peerPages.size} peer(s) in room \"${roomId}\" is — ` +\n `they are on ${theirs}. Their cursors and their shapes will not be visible here. That is correct if you moved to ` +\n `another page on purpose; if you did not, the two replicas are on pages that were built separately. Every editor ` +\n `that starts from a blank document starts on the same default page, so check whether one side pinned, created or ` +\n `loaded a page of its own — and if so, call editor.setCurrentPage() with a page id both sides agree on.`,\n )\n }\n\n const onOpen = () => {\n if (disposed || !connected) return\n status.set(\"online\")\n send({ type: \"hello\", clientId, version: PROTOCOL_VERSION })\n // On a reconnect we already hold stamps, possibly for work done while the\n // transport was down. Push them: the peers merge field by field, so\n // neither side of the split loses anything.\n if (!awaitingSnapshot) sendSnapshot()\n // Forced, for the same reason as the `hello` case: after a reconnect the\n // room has not heard from us in a while and our record may be unchanged.\n presence?.poke(true)\n }\n\n const onClose = () => {\n if (disposed || !connected) return\n // The transport reconnects on its own; from here it is just \"not online\".\n // Local edits keep flowing into the CRDT and go out on the next open.\n status.set(\"connecting\")\n }\n\n return {\n clientId,\n roomId,\n\n connect() {\n if (disposed || connected) return\n connected = true\n // Only a replica holding no stamps at all is a newcomer. One that edited\n // before it ever connected, or while it was disconnected, has work of\n // its own and merges the room's snapshot instead of taking it whole.\n awaitingSnapshot = crdt.size() === 0\n status.set(\"connecting\")\n\n unsubscribes.push(transport.onMessage(handle))\n if (transport.onOpen) unsubscribes.push(transport.onOpen(onOpen))\n if (transport.onClose) unsubscribes.push(transport.onClose(onClose))\n\n if (options.presence) {\n presence = createPresenceSync({\n editor: options.presence.editor,\n clientId,\n send: (record) => send({ type: \"presence\", clientId, record: record as unknown as R }),\n ...(options.presenceThrottleMs !== undefined ? { throttleMs: options.presenceThrottleMs } : {}),\n ...(options.presenceHeartbeatMs !== undefined ? { heartbeatMs: options.presenceHeartbeatMs } : {}),\n })\n presence.start()\n }\n room.startSweeping()\n\n if (!transport.onOpen) onOpen()\n },\n\n disconnect() {\n if (!connected) return\n connected = false\n send({ type: \"bye\", clientId })\n presence?.stop()\n presence = null\n room.stopSweeping()\n room.clear()\n for (const off of unsubscribes.splice(0)) off()\n status.set(\"offline\")\n },\n\n getStatus() {\n return status\n },\n\n dispose() {\n if (disposed) return\n this.disconnect()\n disposed = true\n stopListening()\n transport.close()\n },\n }\n}\n","import type { UnknownRecord } from \"@mocanvas/store\"\nimport { decodeMessage, encodeMessage, type SyncMessage } from \"./protocol\"\n\n/**\n * A duplex channel that carries `SyncMessage`s between peers in one room.\n *\n * The transport owns serialization and reconnection; the client above it only\n * sees decoded messages. `onOpen` fires every time the channel becomes usable\n * (including after a reconnect), `onClose` every time it stops being usable.\n * A transport with neither is treated as open from the moment it is connected.\n */\nexport interface Transport<R extends UnknownRecord = UnknownRecord> {\n send(message: SyncMessage<R>): void\n onMessage(callback: (message: SyncMessage<R>) => void): () => void\n onOpen?(callback: () => void): () => void\n onClose?(callback: () => void): () => void\n close(): void\n}\n\n/** @internal A tiny callback set with an unsubscribe function. */\nexport function createEmitter(): {\n add(cb: () => void): () => void\n emit(): void\n clear(): void\n} {\n const callbacks = new Set<() => void>()\n return {\n add(cb) {\n callbacks.add(cb)\n return () => {\n callbacks.delete(cb)\n }\n },\n emit() {\n for (const cb of Array.from(callbacks)) cb()\n },\n clear() {\n callbacks.clear()\n },\n }\n}\n\n/* -------------------------------------------------------------------------- */\n/* BroadcastChannel */\n/* -------------------------------------------------------------------------- */\n\nexport interface BroadcastChannelTransportOptions {\n /** Channel name prefix, so two apps on one origin do not collide. */\n prefix?: string\n}\n\n/**\n * Same-origin tabs of one browser. There is no server, so every peer is\n * equally authoritative and the channel is open as soon as it is created.\n */\nexport function createBroadcastChannelTransport<R extends UnknownRecord = UnknownRecord>(\n roomId: string,\n options: BroadcastChannelTransportOptions = {},\n): Transport<R> {\n const name = `${options.prefix ?? \"mocanvas-sync\"}:${roomId}`\n const channel = new BroadcastChannel(name)\n const listeners = new Set<(message: SyncMessage<R>) => void>()\n let closed = false\n\n channel.onmessage = (event: MessageEvent) => {\n const message = decodeMessage<R>(event.data)\n if (!message) return\n for (const listener of Array.from(listeners)) listener(message)\n }\n\n return {\n send(message) {\n if (closed) return\n channel.postMessage(encodeMessage(message))\n },\n onMessage(callback) {\n listeners.add(callback)\n return () => {\n listeners.delete(callback)\n }\n },\n onOpen(callback) {\n // Already usable; hand control back to the caller before firing so that\n // `connect()` has finished wiring itself up.\n let cancelled = false\n queueMicrotask(() => {\n if (!cancelled && !closed) callback()\n })\n return () => {\n cancelled = true\n }\n },\n close() {\n closed = true\n listeners.clear()\n channel.onmessage = null\n channel.close()\n },\n }\n}\n\n/* -------------------------------------------------------------------------- */\n/* WebSocket */\n/* -------------------------------------------------------------------------- */\n\nexport interface WebSocketTransportOptions {\n /** Reconnect with exponential backoff after an unexpected close. Default `true`. */\n reconnect?: boolean\n /** First backoff delay in ms. Default 500. */\n minDelayMs?: number\n /** Backoff ceiling in ms. Default 15000. */\n maxDelayMs?: number\n /** Injectable for tests and for Node (`ws`). Defaults to `globalThis.WebSocket`. */\n WebSocketImpl?: typeof WebSocket\n}\n\n/**\n * A WebSocket to a relay (see `scripts/relay.mjs`). Messages sent while the\n * socket is down are dropped rather than queued: the next `hello`/`snapshot`\n * exchange re-establishes the document anyway, and presence is re-sent on a\n * heartbeat.\n */\nexport function createWebSocketTransport<R extends UnknownRecord = UnknownRecord>(\n url: string,\n options: WebSocketTransportOptions = {},\n): Transport<R> {\n const reconnect = options.reconnect ?? true\n const minDelay = options.minDelayMs ?? 500\n const maxDelay = options.maxDelayMs ?? 15_000\n const Impl = options.WebSocketImpl ?? (globalThis as { WebSocket?: typeof WebSocket }).WebSocket\n if (!Impl) throw new Error(\"No WebSocket implementation available; pass options.WebSocketImpl\")\n\n const listeners = new Set<(message: SyncMessage<R>) => void>()\n const open = createEmitter()\n const close = createEmitter()\n\n let socket: WebSocket | null = null\n let attempt = 0\n let timer: ReturnType<typeof setTimeout> | null = null\n let disposed = false\n\n const connect = () => {\n if (disposed) return\n const ws = new Impl(url)\n socket = ws\n ws.onopen = () => {\n attempt = 0\n open.emit()\n }\n ws.onmessage = (event: MessageEvent) => {\n const message = decodeMessage<R>(event.data)\n if (!message) return\n for (const listener of Array.from(listeners)) listener(message)\n }\n ws.onerror = () => {\n // `onclose` always follows; the retry is scheduled there.\n }\n ws.onclose = () => {\n if (socket === ws) socket = null\n close.emit()\n if (!disposed && reconnect) schedule()\n }\n }\n\n const schedule = () => {\n if (timer !== null) return\n // Exponential backoff with full jitter, so a relay restart does not get a\n // thundering herd from every open tab.\n const ceiling = Math.min(maxDelay, minDelay * 2 ** attempt)\n attempt++\n const delay = Math.random() * ceiling\n timer = setTimeout(() => {\n timer = null\n connect()\n }, delay)\n }\n\n connect()\n\n return {\n send(message) {\n if (!socket || socket.readyState !== 1 /* OPEN */) return\n socket.send(encodeMessage(message))\n },\n onMessage(callback) {\n listeners.add(callback)\n return () => {\n listeners.delete(callback)\n }\n },\n onOpen(callback) {\n const remove = open.add(callback)\n if (socket && socket.readyState === 1) queueMicrotask(callback)\n return remove\n },\n onClose(callback) {\n return close.add(callback)\n },\n close() {\n disposed = true\n if (timer !== null) {\n clearTimeout(timer)\n timer = null\n }\n listeners.clear()\n open.clear()\n close.clear()\n const ws = socket\n socket = null\n if (ws) {\n ws.onopen = null\n ws.onmessage = null\n ws.onerror = null\n ws.onclose = null\n if (ws.readyState === 0 || ws.readyState === 1) ws.close()\n }\n },\n }\n}\n\n/* -------------------------------------------------------------------------- */\n/* in-memory */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Two transports wired to each other, going through the same JSON encode and\n * decode as the real ones. Delivery is asynchronous (a microtask) so that a\n * peer never observes a message inside its own `send()` call stack.\n */\nexport function createMemoryTransportPair<R extends UnknownRecord = UnknownRecord>(): [Transport<R>, Transport<R>] {\n const hub = createMemoryHub<R>()\n return [hub.join(), hub.join()]\n}\n\nexport interface MemoryHub<R extends UnknownRecord = UnknownRecord> {\n /** Add another peer to the room. */\n join(): Transport<R>\n}\n\n/** A room every joined transport broadcasts into (itself excluded). */\nexport function createMemoryHub<R extends UnknownRecord = UnknownRecord>(): MemoryHub<R> {\n const peers = new Set<{ deliver(data: string): void }>()\n return {\n join(): Transport<R> {\n const listeners = new Set<(message: SyncMessage<R>) => void>()\n let closed = false\n const peer = {\n deliver(data: string) {\n if (closed) return\n const message = decodeMessage<R>(data)\n if (!message) return\n for (const listener of Array.from(listeners)) listener(message)\n },\n }\n peers.add(peer)\n return {\n send(message) {\n if (closed) return\n const data = encodeMessage(message)\n for (const other of Array.from(peers)) {\n if (other === peer) continue\n queueMicrotask(() => other.deliver(data))\n }\n },\n onMessage(callback) {\n listeners.add(callback)\n return () => {\n listeners.delete(callback)\n }\n },\n onOpen(callback) {\n let cancelled = false\n queueMicrotask(() => {\n if (!cancelled && !closed) callback()\n })\n return () => {\n cancelled = true\n }\n },\n close() {\n closed = true\n listeners.clear()\n peers.delete(peer)\n },\n }\n },\n }\n}\n","import { track, useValue } from \"@mocanvas/state/react\"\nimport { useEditorComponents, type Editor, type EditorRecord, type InstancePresence, type UnknownShape } from \"@mocanvas/editor\"\nimport { useEffect, useRef, useState, type CSSProperties } from \"react\"\nimport { createSyncClient, type SyncClient, type SyncClientOptions, type SyncStatus } from \"./SyncClient\"\nimport type { Transport } from \"./transport\"\n\nconst layerStyle: CSSProperties = {\n position: \"absolute\",\n inset: 0,\n width: \"100%\",\n height: \"100%\",\n pointerEvents: \"none\",\n overflow: \"visible\",\n}\n\nexport interface CollaboratorCursorsProps {\n editor: Editor\n /** Draw what each collaborator has selected. Default `true`. */\n showSelection?: boolean\n /** Draw a name chip next to each cursor. Default `true`. */\n showNames?: boolean\n}\n\n/**\n * Other people's cursors and selections, drawn in screen space above the\n * canvas. Drop it inside `<Mocanvas>` or `<Canvas>`; it positions itself.\n *\n * The arrow below is the default. An app that supplies a\n * {@link TLEditorComponents.CollaboratorCursor} component gets that instead —\n * the slot was declared on two component maps and consulted by nothing, so an\n * app that replaced the cursor silently kept seeing ours.\n */\nexport const CollaboratorCursors = track(function CollaboratorCursors({\n editor,\n showSelection = true,\n showNames = true,\n}: CollaboratorCursorsProps) {\n const components = useEditorComponents()\n const collaborators = editor.getCollaboratorsOnCurrentPage()\n if (collaborators.length === 0) return null\n // `null` is an app switching the cursors off, which is different from not\n // supplying one; `undefined` means \"use ours\".\n const Slot = components.CollaboratorCursor\n return (\n <>\n <svg className=\"mocanvas-collaborators\" style={layerStyle}>\n {collaborators.map((presence) => (\n <g key={presence.id}>\n {showSelection ? <CollaboratorSelection editor={editor} presence={presence} /> : null}\n {Slot === undefined ? <CollaboratorCursor editor={editor} presence={presence} showName={showNames} /> : null}\n </g>\n ))}\n </svg>\n {/* A supplied slot is HTML, not SVG — `DefaultCursor` is a div — so it\n gets its own layer rather than being nested inside the <svg> above,\n where it would not render at all. */}\n {Slot ? (\n <div className=\"mocanvas-collaborator-cursors\" style={layerStyle}>\n {collaborators.map((presence) =>\n presence.cursor === null ? null : (\n <Slot\n key={presence.id}\n type=\"default\"\n rotation={presence.cursor.rotation}\n color={presence.color}\n name={showNames ? presence.userName || \"Anonymous\" : null}\n point={editor.pageToViewport(presence.cursor)}\n />\n ),\n )}\n </div>\n ) : null}\n </>\n )\n})\n\nconst CollaboratorCursor = track(function CollaboratorCursor({\n editor,\n presence,\n showName,\n}: {\n editor: Editor\n presence: InstancePresence\n showName: boolean\n}) {\n // A collaborator with no pointer — an agent, or someone whose pointer has left\n // the canvas — draws no cursor at all.\n if (presence.cursor === null) return null\n // Viewport space: the overlay is positioned inside the canvas container, not\n // the window.\n const point = editor.pageToViewport(presence.cursor)\n const name = presence.userName || \"Anonymous\"\n // A 16x22 arrow drawn from the hotspot, then the name chip below it.\n return (\n <g transform={`translate(${point.x}, ${point.y}) rotate(${presence.cursor.rotation})`}>\n <path\n d=\"M0 0 L0 17.3 L4.3 13.4 L7 19.4 L9.9 18 L7.2 12.2 L12.8 12 Z\"\n fill={presence.color}\n stroke=\"#fff\"\n strokeWidth={1}\n strokeLinejoin=\"round\"\n />\n {showName ? (\n <g transform=\"translate(11, 18)\">\n <rect rx={4} ry={4} width={Math.max(24, name.length * 7 + 12)} height={18} fill={presence.color} />\n <text x={6} y={13} fontSize={11} fontFamily=\"system-ui, sans-serif\" fill=\"#fff\">\n {name}\n </text>\n </g>\n ) : null}\n </g>\n )\n})\n\nconst CollaboratorSelection = track(function CollaboratorSelection({\n editor,\n presence,\n}: {\n editor: Editor\n presence: InstancePresence\n}) {\n const outlines: string[] = []\n for (const id of presence.selectedShapeIds) {\n const shape: UnknownShape | undefined = editor.getShape(id)\n if (!shape) continue\n const bounds = editor.getShapeGeometryBounds(shape)\n if (!bounds) continue\n const m = editor.getShapePageTransform(shape)\n // Transform the local corners to page space, then to screen space, so the\n // outline follows rotation without a nested SVG transform.\n const corners: [number, number][] = [\n [bounds.x, bounds.y],\n [bounds.maxX, bounds.y],\n [bounds.maxX, bounds.maxY],\n [bounds.x, bounds.maxY],\n ]\n const points = corners.map(([x, y]) => {\n // Viewport space, like the cursor above: this overlay lives in the canvas\n // container, so window offsets must not be added.\n const p = editor.pageToViewport({ x: m.a * x + m.c * y + m.e, y: m.b * x + m.d * y + m.f })\n return `${p.x},${p.y}`\n })\n outlines.push(points.join(\" \"))\n }\n if (outlines.length === 0) return null\n return (\n <>\n {outlines.map((points, i) => (\n <polygon key={i} points={points} fill=\"none\" stroke={presence.color} strokeWidth={1.5} opacity={0.8} />\n ))}\n </>\n )\n})\n\nexport interface UseSyncOptions {\n roomId: string\n /**\n * The channel to the room. Pass a factory when the transport should be\n * recreated with the room (the usual case); it is read once per connection.\n */\n transport: Transport | (() => Transport)\n /** Share this editor's cursor and selection. Default `true`. */\n presence?: boolean\n /** Set `false` to stay offline (a read-only view, say). Default `true`. */\n enabled?: boolean\n clientId?: string\n presenceTimeoutMs?: number\n presenceThrottleMs?: number\n onError?: (error: Error) => void\n}\n\nexport interface UseSyncResult {\n status: SyncStatus\n client: SyncClient | null\n}\n\n/**\n * Connect an editor's store to a room for as long as the component is mounted.\n * Everything is torn down (including the transport) on unmount.\n */\nexport function useSync(editor: Editor | null, options: UseSyncOptions): UseSyncResult {\n const { roomId, enabled = true, presence = true } = options\n // Kept in a ref so that inline factories and callbacks do not reconnect.\n const latest = useRef(options)\n latest.current = options\n\n const [client, setClient] = useState<SyncClient | null>(null)\n\n useEffect(() => {\n if (!editor || !enabled) {\n setClient(null)\n return\n }\n const current = latest.current\n const transport = typeof current.transport === \"function\" ? current.transport() : current.transport\n const clientOptions: SyncClientOptions<EditorRecord> = {\n store: editor.store,\n roomId,\n transport: transport as Transport<EditorRecord>,\n ...(presence ? { presence: { editor } } : {}),\n ...(current.clientId !== undefined ? { clientId: current.clientId } : {}),\n ...(current.presenceTimeoutMs !== undefined ? { presenceTimeoutMs: current.presenceTimeoutMs } : {}),\n ...(current.presenceThrottleMs !== undefined ? { presenceThrottleMs: current.presenceThrottleMs } : {}),\n onError: (error: Error) => latest.current.onError?.(error),\n }\n const next = createSyncClient<EditorRecord>(clientOptions)\n setClient(next)\n next.connect()\n return () => {\n setClient(null)\n next.dispose()\n }\n }, [editor, roomId, enabled, presence])\n\n const status = useValue(\"sync.status\", () => client?.getStatus().get() ?? \"offline\", [client])\n return { status, client }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mocanvas/sync",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.1.1",
|
|
4
4
|
"description": "Multiplayer for a mocanvas store: record diffs over a transport, plus presence and cursors.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"author": "Symbio Digital",
|
|
@@ -43,14 +43,16 @@
|
|
|
43
43
|
"access": "public"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@mocanvas/editor": "4.
|
|
47
|
-
"@mocanvas/state": "4.
|
|
48
|
-
"@mocanvas/store": "4.
|
|
46
|
+
"@mocanvas/editor": "4.1.1",
|
|
47
|
+
"@mocanvas/state": "4.1.1",
|
|
48
|
+
"@mocanvas/store": "4.1.1"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@types/react": "^19.1.0",
|
|
52
|
+
"@types/react-dom": "^19.1.0",
|
|
52
53
|
"@types/ws": "^8.5.0",
|
|
53
54
|
"react": "^19.1.0",
|
|
55
|
+
"react-dom": "^19.1.0",
|
|
54
56
|
"ws": "^8.18.0"
|
|
55
57
|
},
|
|
56
58
|
"peerDependencies": {
|