@atom.io/template-react-realtime-text-editor 0.0.11 → 0.0.13
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/CHANGELOG.md +25 -0
- package/INNOVATIONS.md +242 -113
- package/README.md +75 -39
- package/eslint.config.ts +1 -1
- package/index.html +1 -0
- package/node/http-server.ts +81 -0
- package/node/server.ts +12 -60
- package/node/service.ts +310 -0
- package/package.json +30 -21
- package/playwright.config.ts +34 -0
- package/src/LexicalMarkdownEditor.css +88 -0
- package/src/LexicalMarkdownEditor.module.css +8 -0
- package/src/LexicalMarkdownEditor.tsx +43 -0
- package/src/MarkdownWorkspace.module.css +154 -98
- package/src/MarkdownWorkspace.tsx +163 -296
- package/src/collaboration-client.ts +238 -0
- package/src/document-domain.ts +169 -0
- package/src/globals.css +9 -2
- package/src/incremental-markdown.ts +279 -0
- package/src/main.tsx +30 -9
- package/src/protocol.ts +35 -0
- package/src/virtualization.ts +53 -0
- package/src/workspace-state.ts +275 -0
- package/tests/browser/editor-conformance.spec.ts +806 -0
- package/tests/collaboration.test.tsx +465 -213
- package/tests/incremental-markdown.test.tsx +101 -0
- package/tsconfig.app.json +1 -1
- package/tsconfig.node.json +8 -2
- package/tsconfig.test.json +1 -1
- package/vite.config.ts +13 -9
- package/node/mosaic-atom.ts +0 -88
- package/src/collaboration/mosaic.ts +0 -46
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# @atom.io/template-react-realtime-text-editor
|
|
2
2
|
|
|
3
|
+
## 0.0.13
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [2bbaa59]
|
|
8
|
+
- atom.io@0.54.1
|
|
9
|
+
- @atom.io/react@0.1.0
|
|
10
|
+
- @atom.io/realtime@0.1.0
|
|
11
|
+
|
|
12
|
+
## 0.0.12
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- aa30b42: Use Mosaic Domains and the reusable Lexical adapter for collaborative Markdown editing, with optimistic drafts, logical collaborator carets and selections, renewable presence, viewport residency, atomic text/index updates, and actor-selective undo and redo. Demonstrate incremental Markdown parsing and virtualized source and preview rendering, with deterministic headless and two-browser conformance coverage.
|
|
17
|
+
|
|
18
|
+
Demonstrate consumer-owned structural CSS Modules and typed part classes for utility-compatible styling. Add automatic light and dark color schemes using component-owned Lasertag theme tokens, with a dark initial canvas and fallback theme that still follows the system's light-mode preference.
|
|
19
|
+
|
|
20
|
+
Use the standalone `@atom.io/react`, `@atom.io/realtime`, and `@atom.io/eslint-plugin` packages. Include pinned Oxlint, ESLint, TypeScript, and dprint quality checks, with TypeScript 7 for builds and TypeScript 6 for ESLint's compiler API. Pin dprint 0.64.1 to distrust the compromised eMudhra certificates when downloading formatter plugins.
|
|
21
|
+
- Updated dependencies [bb1d8de]
|
|
22
|
+
- Updated dependencies [aa30b42]
|
|
23
|
+
- Updated dependencies [bb1d8de]
|
|
24
|
+
- atom.io@0.54.0
|
|
25
|
+
- @atom.io/realtime@0.1.0
|
|
26
|
+
- @atom.io/react@0.1.0
|
|
27
|
+
|
|
3
28
|
## 0.0.11
|
|
4
29
|
|
|
5
30
|
### Patch Changes
|
package/INNOVATIONS.md
CHANGED
|
@@ -1,113 +1,242 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
and
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
the
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
the
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
1
|
+
# Incremental Markdown Decisions
|
|
2
|
+
|
|
3
|
+
MOS-18 is the first application that composes the run-text model, bounded text
|
|
4
|
+
index, partial residency, Domain history, and renderer projections as one user
|
|
5
|
+
experience. Most of the implementation is application composition. One missing
|
|
6
|
+
generic seam had to move into realtime core so the result could remain correct
|
|
7
|
+
at every accepted revision.
|
|
8
|
+
|
|
9
|
+
## One Logical Source, Bounded Resident Views
|
|
10
|
+
|
|
11
|
+
The authoritative service owns one Mosaic run-text checkpoint and the MOS-15
|
|
12
|
+
bounded-fanout index derived from it. The index root, nodes, aliases, and leaves
|
|
13
|
+
are ordinary durable Domain members. A browser never acquires the source member:
|
|
14
|
+
MOS-12 authorizes and hydrates only the root and the leaves resolved for a
|
|
15
|
+
bounded UTF-16 range, and MOS-17 projects those leaves through ordinary
|
|
16
|
+
selectors. Full materialization remains an explicit command.
|
|
17
|
+
|
|
18
|
+
This is not application-authored paragraph or file sharding. Syntax cannot
|
|
19
|
+
change storage shape, so a huge paragraph or fenced block crosses the same
|
|
20
|
+
physical thresholds as ordinary prose. Presence and render keys name logical
|
|
21
|
+
run positions rather than physical leaves.
|
|
22
|
+
|
|
23
|
+
MOS-18 exposed a scalability flaw that small MOS-15 conformance fixtures could
|
|
24
|
+
not: index composition expanded every source grapheme into a separate JavaScript
|
|
25
|
+
object. The core index now streams large fragments into bounded spans, caches
|
|
26
|
+
unchanged source spans on the resulting index bundle, and reuses physical leaf
|
|
27
|
+
ownership. Ordinary edits scan run/span identities and touch changed leaves plus
|
|
28
|
+
their bounded tree paths; they do not manufacture a document-sized unit array.
|
|
29
|
+
|
|
30
|
+
The same exercise exposed that `replace-text` is the wrong input seam for a
|
|
31
|
+
large editor because it materializes and diffs the entire old and new strings.
|
|
32
|
+
Mosaic Text now accepts a logical `replace-selection` intent and exposes pure
|
|
33
|
+
`prepare` and `preview` phases. The authoritative service can derive a bounded
|
|
34
|
+
operation and its next visible run projection before proposing the atomic
|
|
35
|
+
Domain batch, without mutating accepted state or asking the application to
|
|
36
|
+
construct CRDT runs. Unicode chunking is streaming, preserves CRLF and complex
|
|
37
|
+
grapheme boundaries, and remains core-owned rather than application sharding.
|
|
38
|
+
|
|
39
|
+
## Atomic History Completion
|
|
40
|
+
|
|
41
|
+
MOS-16 previously generated a correct run-text compensation but had no way to
|
|
42
|
+
include the index maintenance describing that compensated text. Appending
|
|
43
|
+
maintenance afterward would expose one revision whose source and range index
|
|
44
|
+
disagreed.
|
|
45
|
+
|
|
46
|
+
The history coordinator now accepts an optional `completeCompensation` callback.
|
|
47
|
+
It runs after member policies create compensation operations and before the
|
|
48
|
+
private history proposal is preflighted. Returned operations join that same
|
|
49
|
+
batch only when their member policies classify them as history-free. The
|
|
50
|
+
coordinator still proves that the original target set was compensated exactly;
|
|
51
|
+
an appended change or second compensation fails closed. This seam is generic to
|
|
52
|
+
derived durable indexes and contains no Markdown branch.
|
|
53
|
+
|
|
54
|
+
## Local Input and Logical Presence
|
|
55
|
+
|
|
56
|
+
The mounted Lexical plain-text editor contains only one resident source window.
|
|
57
|
+
The renderer-neutral Mosaic text editor hook keeps keystrokes in an optimistic
|
|
58
|
+
draft, derives minimal replacement intent, and retains that draft until a
|
|
59
|
+
complete newer projection cut settles. It also publishes local selections as
|
|
60
|
+
run-relative positions and resolves collaborator selections through the same
|
|
61
|
+
visible text deltas, so renderers do not briefly display an absolute cursor at a
|
|
62
|
+
stale offset. DOM selection, composition, scroll, and caret geometry remain
|
|
63
|
+
renderer-owned.
|
|
64
|
+
|
|
65
|
+
Lexical is deliberately a browser editing and geometry layer rather than a
|
|
66
|
+
second collaboration authority. Its Yjs collaboration integration and history
|
|
67
|
+
plugin are not mounted. Mosaic owns accepted text, convergence, logical
|
|
68
|
+
positions, actor history, partial residency, and renewable presence leases. The
|
|
69
|
+
core editor lifecycle projects the current bounded source, translates local
|
|
70
|
+
selection offsets into Mosaic positions, and resolves foreign positions. The
|
|
71
|
+
Lexical adapter only maps those offsets to DOM ranges for colored caret labels
|
|
72
|
+
and selection overlays. A projection-tagged update cannot echo back as a local
|
|
73
|
+
edit, while native composition remains local until it is ready for the existing
|
|
74
|
+
coalesced Domain gesture.
|
|
75
|
+
|
|
76
|
+
The React range hook now reacquires a failed viewport when residency transitions
|
|
77
|
+
back to live. This closes the cold-start race where Vite could render before the
|
|
78
|
+
realtime server accepted its first Socket.IO connection, leaving an otherwise
|
|
79
|
+
healthy client permanently parked on an internal resnapshot error.
|
|
80
|
+
|
|
81
|
+
An accepted edit may also shorten a document while an existing subscription
|
|
82
|
+
still names the old viewport end. The Markdown range adapter therefore clamps
|
|
83
|
+
that durable subscription to the new authoritative length, and command
|
|
84
|
+
acknowledgements do not clear the optimistic draft until residency has settled
|
|
85
|
+
the accepted revision. This prevents a valid remote replacement from stranding
|
|
86
|
+
the UI on a stale projection or briefly presenting the stale text as saved.
|
|
87
|
+
|
|
88
|
+
The demo deliberately coalesces an offline draft for its current viewport. It
|
|
89
|
+
does not claim semantic intent recovery for arbitrary rewrites made in several
|
|
90
|
+
unloaded regions. A richer product can retain several bounded draft windows and
|
|
91
|
+
still submit them as one Domain gesture.
|
|
92
|
+
|
|
93
|
+
The production patterns discovered while stabilizing that lifecycle no longer
|
|
94
|
+
live as Markdown-specific template machinery. Core now publishes the Mosaic
|
|
95
|
+
text source/index models and schemas, a server text-document coordinator for
|
|
96
|
+
atomic import/edit/index/history work, a validated residency socket bridge, and
|
|
97
|
+
a browser session coordinator for reconnect and accepted-revision settlement.
|
|
98
|
+
The template supplies its member names, limits, authorization policy, simulated
|
|
99
|
+
identities, viewport, and renderer. This keeps the correctness boundary reusable
|
|
100
|
+
without pretending that editor layout or product policy is generic.
|
|
101
|
+
|
|
102
|
+
## Incremental, Cancelable Semantics
|
|
103
|
+
|
|
104
|
+
Parsing is not a render-time whole-document function. The headless parser caches
|
|
105
|
+
input and output block state, yields after a bounded amount of UTF-16 work, and
|
|
106
|
+
cancels an obsolete generation as soon as a newer projection arrives. Fence
|
|
107
|
+
state propagates through following blocks only until an unchanged cached input
|
|
108
|
+
state establishes a stable boundary. React mounts only semantic blocks in the
|
|
109
|
+
current preview window.
|
|
110
|
+
|
|
111
|
+
The included grammar is intentionally a safe illustrative Markdown subset. A
|
|
112
|
+
production CommonMark/GFM worker can replace it behind the same source-block,
|
|
113
|
+
cancelation, stable-state, and instrumentation contract. Sanitization remains a
|
|
114
|
+
renderer responsibility; this template creates React nodes and never injects
|
|
115
|
+
HTML.
|
|
116
|
+
|
|
117
|
+
## Mosaic Text v3: Atomic Roots Instead of Giant Operations
|
|
118
|
+
|
|
119
|
+
A 50 MB import cannot be both a bounded operation and a single giant Domain
|
|
120
|
+
payload. Splitting that payload into several accepted source revisions was also
|
|
121
|
+
rejected: a crash would expose a prefix, and retry could collide with the
|
|
122
|
+
already-accepted prefix or duplicate it. Mosaic Text v3 takes a different path.
|
|
123
|
+
It stages immutable content-addressed leaves and bounded-fanout branches while
|
|
124
|
+
they are unreachable, protects the staged graph with the same atomic storage
|
|
125
|
+
write, and publishes one small root operation. Before that root is accepted the
|
|
126
|
+
old document remains authoritative; afterward the complete new graph is
|
|
127
|
+
authoritative. Exact retry is idempotent.
|
|
128
|
+
|
|
129
|
+
This required one model-neutral addition to the MOS-13 checkpoint spine:
|
|
130
|
+
checkpoints may name validated external roots. Staging and its expiring proposal
|
|
131
|
+
lease are atomic; the accepted append atomically promotes that lease to durable
|
|
132
|
+
accepted-root protection; and checkpoint publication adopts and releases the
|
|
133
|
+
protection in the same commit. Restart exposes accepted-but-not-yet-checkpointed
|
|
134
|
+
roots to the coordinator, while history and outbox leases protect older roots.
|
|
135
|
+
Garbage collection traces every edge behind a retention-epoch fence. Missing
|
|
136
|
+
parents, stale parents, forged summaries, hash collisions, corrupt objects, and
|
|
137
|
+
stage/append/checkpoint/GC races all fail closed. Abandoned proposals expire by
|
|
138
|
+
time, revision, and bounded retention epochs instead of becoming perpetual
|
|
139
|
+
leases.
|
|
140
|
+
|
|
141
|
+
The external root stores its incremental proof separately from the root header.
|
|
142
|
+
An initial import necessarily reads the whole input once. A later edit
|
|
143
|
+
path-copies only the touched leaf and branch path, sends removals for superseded
|
|
144
|
+
paths, and verifies its delta against the protected parent summary. Commit can
|
|
145
|
+
therefore reuse authenticated subtree totals instead of rehashing a document
|
|
146
|
+
manifest. Range recovery is paged and visits addressed paths rather than
|
|
147
|
+
hydrating the graph.
|
|
148
|
+
|
|
149
|
+
This is deliberately versioned as Mosaic Text v3 rather than silently changing
|
|
150
|
+
v2's run snapshot. The generic Domain hook knows only content-addressed object
|
|
151
|
+
graphs, bounds, and lifecycle dependencies; rope shape, grapheme-safe chunking,
|
|
152
|
+
reference counts, range reads, and root replacement remain text-owned. That is
|
|
153
|
+
where this work takes its own path beyond the original push/pull and monolithic
|
|
154
|
+
checkpoint facilities.
|
|
155
|
+
|
|
156
|
+
## History and Checkpoint Amplification
|
|
157
|
+
|
|
158
|
+
Scale auditing also found a non-text-specific amplifier in Domain history.
|
|
159
|
+
Accepted gestures were deep-cloned into every retained checkpoint-race cut, so a
|
|
160
|
+
large operation could be retained many times even when text storage itself was
|
|
161
|
+
segmented. History now clones and freezes accepted JSON once at its trusted
|
|
162
|
+
ingress, shares those immutable gesture payloads across race cuts, replaces a
|
|
163
|
+
gesture immutably when it is extended, and clones only at public or storage
|
|
164
|
+
egress. Caller mutation, malicious compensation callbacks, restart hydration,
|
|
165
|
+
and checkpoint observers remain isolated.
|
|
166
|
+
|
|
167
|
+
Mosaic v2 compaction received a related correctness fix. It removes only retired
|
|
168
|
+
runs that are provably unreachable while preserving logical positions, foreign
|
|
169
|
+
descendants, undo/redo protection, checkpoints, restart, and duplicate replay.
|
|
170
|
+
The scale gate keeps the v2 history/index stabilization loop because those are
|
|
171
|
+
the user-facing selective-history and alias contracts, then drives more than
|
|
172
|
+
100,000 operations through the real Domain receipt, checkpoint, retention, and
|
|
173
|
+
garbage-collection lifecycle.
|
|
174
|
+
|
|
175
|
+
## Deterministic Release Gate
|
|
176
|
+
|
|
177
|
+
The MOS-20 manifest is the sole corpus authority. The gate verifies its source
|
|
178
|
+
and derived digests before opening the canonical 5.6 MB source, deterministic
|
|
179
|
+
50 MB repetition, huge paragraph, fenced block, heading-rich, and adversarial
|
|
180
|
+
Unicode variants. A single worker owns one authoritative graph at a time. Each
|
|
181
|
+
document runs multi-client disjoint and shared-boundary edits, duplicate,
|
|
182
|
+
delayed and reordered delivery, rejection, disconnect/resnapshot, partial
|
|
183
|
+
hydration and eviction, individualized undo/redo, and storage-plus-coordinator
|
|
184
|
+
restart. The seed shuffles the client, delivery, and lifecycle fault order that
|
|
185
|
+
is actually executed. Failure output includes that seed, the realized fault and
|
|
186
|
+
client schedules, resident ranges, source and client revisions, and a bounded
|
|
187
|
+
semantic transcript containing the exact commands needed for replay.
|
|
188
|
+
|
|
189
|
+
The gate never constructs a second full-document oracle. Its independent flat
|
|
190
|
+
model is a UTF-16-addressed piece table over the canonical file, and final
|
|
191
|
+
verification streams source ranges directly into the digest. The same pass
|
|
192
|
+
independently counts graphemes, line breaks, and UTF-16 units and reconstructs
|
|
193
|
+
each client's bounded resident ranges; all are compared with the published root
|
|
194
|
+
summary and client projections. Instrumentation fails the gate if any service
|
|
195
|
+
read materializes a document larger than the configured residency bound;
|
|
196
|
+
clients, parsing, join, and resnapshot remain bounded projections over the one
|
|
197
|
+
authoritative segmented graph.
|
|
198
|
+
|
|
199
|
+
Normative counters cover leaf and branch visits and writes, UTF-16 scanned,
|
|
200
|
+
object reads, validation hashing and serialization, bytes persisted, checkpoint
|
|
201
|
+
and batch payloads, resident and delivered bytes, selector invalidations, and
|
|
202
|
+
parser work. Ordinary edits are bounded to addressed leaves and tree height;
|
|
203
|
+
join and parser work are bounded to resident windows; checkpoints reuse clean
|
|
204
|
+
objects. A local checkpoint may persist at most 512 KiB and a viewport transfer
|
|
205
|
+
or accepted delivery at most 384 KiB. The 100,001-operation stabilization
|
|
206
|
+
assertion bounds history, aliases, receipts, tail, sessions, and live checkpoint
|
|
207
|
+
objects, then forces a real index split, resolves its stale leaf alias through
|
|
208
|
+
the public reader, and verifies the leaves merge after contention settles.
|
|
209
|
+
Elapsed time and RSS are reported observations only because runner load and
|
|
210
|
+
allocator behavior are not deterministic correctness signals.
|
|
211
|
+
|
|
212
|
+
Current architecture limits are explicit: text leaves target 32,768 graphemes
|
|
213
|
+
and at most 65,536 UTF-16 units; import chunks are at most 262,144 UTF-16 units;
|
|
214
|
+
branches contain at most 32 children; one edit inserts at most 256 KiB; a range
|
|
215
|
+
read defaults to 128 objects; an external graph defaults to 64 MiB total while
|
|
216
|
+
one staging call may add at most 16 MiB across 4,096 objects and 256 logical
|
|
217
|
+
updates, with 4 MiB and depth 64 per object; and a coordinator defaults to 64
|
|
218
|
+
roots, 256 recovery reads, and 64 MiB aggregate external bytes. Proposal
|
|
219
|
+
retention defaults to 64 epochs and is limited to 1–1,024. These are safety
|
|
220
|
+
contracts, not claims that arbitrarily large documents are fully resident.
|
|
221
|
+
|
|
222
|
+
External staging is a trusted server composition seam, not a socket protocol.
|
|
223
|
+
Client intents first cross Domain authorization and proposal limits; a product
|
|
224
|
+
that derives external updates from an untrusted request must also apply its own
|
|
225
|
+
per-document quota, rate limit, and deadline before hashing begins. Stage work
|
|
226
|
+
is counted before content hashing, checks an abort signal and absolute deadline
|
|
227
|
+
between bounded units, and becomes visible with its proposal lease only after
|
|
228
|
+
the whole attempt succeeds. The pinned 50 MiB corpus gate raises the incremental
|
|
229
|
+
stage budget because its input is authenticated by the MOS-20 manifest and read
|
|
230
|
+
under a fixed deadline. That exception is not the default production request
|
|
231
|
+
budget: it permits at most 128 MiB of cumulative authenticated staging work and
|
|
232
|
+
32,768 objects while the ordinary defaults remain 16 MiB and 4,096 objects. The
|
|
233
|
+
reported text-stage bytes count every immutable text or index node serialized
|
|
234
|
+
and hashed at the model boundary; directory and proof staging, then storage
|
|
235
|
+
verification, independently enforce the same work budget before publication.
|
|
236
|
+
These cumulative work budgets are not retained bytes, resident memory, or the
|
|
237
|
+
protected graph's 64 MiB value-size cap.
|
|
238
|
+
|
|
239
|
+
The zero-setup editor server remains in-memory and uses simulated authorization.
|
|
240
|
+
A product adapter must provide the linearizable semantics exercised by the
|
|
241
|
+
in-memory conformance adapter, plus durable ACLs, rate limits, observability, and
|
|
242
|
+
an offline-retention policy.
|
package/README.md
CHANGED
|
@@ -1,51 +1,87 @@
|
|
|
1
1
|
# React Realtime Text Editor
|
|
2
2
|
|
|
3
|
-
Mosaic is a React + atom.io multiplayer Markdown editor with
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
Mosaic is a React + atom.io multiplayer Markdown editor with simulated
|
|
4
|
+
identities. It demonstrates the scalable long-form text path: bounded Domain
|
|
5
|
+
members, viewport-first residency, logical presence, selective history,
|
|
6
|
+
incremental parsing, and virtualized source and preview rendering.
|
|
6
7
|
|
|
7
8
|
## Run It
|
|
8
9
|
|
|
9
|
-
Run `npm run dev`, then open the displayed Vite URL
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
Run `npm run dev`, then open the displayed Vite URL in two tabs and choose a
|
|
11
|
+
different identity in each tab. The browser connects to `window.location.origin`.
|
|
12
|
+
Vite proxies `/socket.io` and `/health` to the collaboration service in both
|
|
13
|
+
development and preview, so the deployment contract does not require browser
|
|
14
|
+
CORS exceptions.
|
|
14
15
|
|
|
15
16
|
## What It Shows
|
|
16
17
|
|
|
17
|
-
-
|
|
18
|
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
|
|
22
|
-
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
18
|
+
- one logical Markdown document indexed by bounded-fanout ordinary Domain atom
|
|
19
|
+
families;
|
|
20
|
+
- viewport hydration before full materialization, with released ranges evicted;
|
|
21
|
+
- local input drafts that remain responsive while offline or while an
|
|
22
|
+
authoritative gesture settles;
|
|
23
|
+
- run-relative selection, viewport, and collaborator presence across physical
|
|
24
|
+
leaf splits and merges;
|
|
25
|
+
- a Lexical plain-text editing surface that renders named remote carets and
|
|
26
|
+
translucent selections without delegating convergence or history to a second
|
|
27
|
+
collaboration model;
|
|
28
|
+
- actor-selective undo and redo whose text compensation and index maintenance
|
|
29
|
+
settle in one Domain revision;
|
|
30
|
+
- cancelable incremental Markdown parsing that yields outside the input turn and
|
|
31
|
+
stops propagating when cached parser state reaches a stable boundary;
|
|
32
|
+
- source and preview virtualization with no architecture switch at the 5.6 MB or
|
|
33
|
+
deterministic 50 MB corpus sizes; and
|
|
34
|
+
- one explicitly authorized reset/import transaction rather than an effect run
|
|
35
|
+
by every joining browser.
|
|
36
|
+
|
|
37
|
+
Run `npm test` for the multi-client contention, offline/reconnect, history,
|
|
38
|
+
presence, import, parser, and virtualization scenarios. Run `npm run lint` to
|
|
39
|
+
enforce ESLint, type-aware Oxlint, React hook call-order correctness, TypeScript,
|
|
40
|
+
and the Lasertag render-story/CSS relationship with zero warnings. Run
|
|
28
41
|
`npm run fmt:check` in CI and `npm run fmt` to apply the pinned dprint
|
|
29
|
-
configuration locally.
|
|
42
|
+
configuration locally. The scheduled repository command
|
|
43
|
+
`pnpm test:large-document` verifies the pinned corpus and exercises the same
|
|
44
|
+
logical editing, history, residency, and viewport/parser contracts over Mosaic
|
|
45
|
+
Text v3's content-addressed storage roots. It runs sequentially with one
|
|
46
|
+
authoritative document graph at a time across the canonical 5.6 MB document,
|
|
47
|
+
the deterministic 50 MB repetition, huge-paragraph and fenced-block shapes, and
|
|
48
|
+
the Unicode adversarial corpus.
|
|
30
49
|
|
|
31
50
|
## Start Reading
|
|
32
51
|
|
|
33
|
-
- `src/
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
- `node/
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
-
|
|
45
|
-
the
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
+
- `src/document-domain.ts` names the application's durable source and index
|
|
53
|
+
members plus its ephemeral collaborator shape. Core supplies the text and
|
|
54
|
+
index models, schemas, and history policy.
|
|
55
|
+
- `node/service.ts` chooses persistence, authorization, presence lifetime, and
|
|
56
|
+
the app-specific Socket.IO commands. The core text document coordinator owns
|
|
57
|
+
atomic source/index proposals, range reads, and history compensation, while
|
|
58
|
+
the core residency binder owns its wire protocol.
|
|
59
|
+
- `src/collaboration-client.ts` supplies browser identity, viewport policy, and
|
|
60
|
+
application commands. Core owns residency transport, reconnect
|
|
61
|
+
synchronization, accepted-revision settlement, presence renewal, and
|
|
62
|
+
selective history sequencing.
|
|
63
|
+
- `@atom.io/realtime/client` owns the Store-backed range controller, preserving
|
|
64
|
+
the last complete projection while replacement residency is acquired.
|
|
65
|
+
- `@atom.io/realtime/react` owns optimistic draft settlement and maps local and
|
|
66
|
+
remote selections across complete, revision-tagged projection cuts.
|
|
67
|
+
- `@atom.io/realtime/react/lexical` owns projection replacement, native input
|
|
68
|
+
boundaries, DOM selection restoration, collaborator geometry, and semantic
|
|
69
|
+
structure without visual styling. The local `src/LexicalMarkdownEditor.tsx`
|
|
70
|
+
opts into Lasertag's validated render-story adoption for Atom.io's owned root.
|
|
71
|
+
Its CSS Module styles that root, while its ordinary component stylesheet uses
|
|
72
|
+
the adapter's typed `classNames` parts across Lexical's context-provider
|
|
73
|
+
boundary. Utility-CSS consumers can supply those same parts directly.
|
|
74
|
+
- `src/incremental-markdown.ts` contains the renderer-neutral, cancelable parser.
|
|
75
|
+
- `src/workspace-state.ts` expresses status, presence, viewport, parser output,
|
|
76
|
+
diagnostics, and derived peers as Atom state. `src/MarkdownWorkspace.tsx`
|
|
77
|
+
reads those tokens and owns only application presentation and interaction.
|
|
78
|
+
- `INNOVATIONS.md` records the new core seam and the deliberate production
|
|
79
|
+
boundaries.
|
|
80
|
+
|
|
81
|
+
The template uses in-memory persistence and simulated authorization for a
|
|
82
|
+
zero-setup demo. A production service still needs a linearizable storage
|
|
83
|
+
adapter, document ACLs, rate limits, metrics, and a durable offline-retention
|
|
84
|
+
policy. The corpus gate deliberately uses the same public checkpoint adapter
|
|
85
|
+
contract as production, including restart, proposal expiry, retention fences,
|
|
86
|
+
and garbage collection; wall-clock and RSS readings are diagnostics rather than
|
|
87
|
+
portable release thresholds.
|
package/eslint.config.ts
CHANGED
package/index.html
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<meta name="color-scheme" content="dark light" />
|
|
6
7
|
<meta
|
|
7
8
|
name="description"
|
|
8
9
|
content="Mosaic, a realtime collaborative Markdown editor built with atom.io."
|