@synclineapi/editor 3.0.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -75,16 +75,6 @@ Ships as both an **ES module** and **UMD bundle**, runs entirely inside a **Shad
75
75
  - [Indent Guides](#indent-guides)
76
76
  - [Active Line Highlight](#active-line-highlight)
77
77
  - [Read-Only Mode](#read-only-mode)
78
- - [Collaborative Editing](#collaborative-editing)
79
- - [Quick Start — Same Page](#quick-start--same-page)
80
- - [Cross-Tab Collaboration](#cross-tab-collaboration)
81
- - [Using WebSocket for Realtime Collaboration](#using-websocket-for-realtime-collaboration)
82
- - [CollabConfig Reference](#collabconfig-reference)
83
- - [Transport Reference](#transport-reference)
84
- - [Peer Awareness & Cursors](#peer-awareness--cursors)
85
- - [Remote Cursor CSS Classes](#remote-cursor-css-classes)
86
- - [Offline & Reconnect](#offline--reconnect)
87
- - [Playground Collab Panel](#playground-collab-panel)
88
78
  - [Behavioral Options](#behavioral-options)
89
79
  - [Auto-Close Pairs](#auto-close-pairs)
90
80
  - [Line Comment Token](#line-comment-token)
@@ -144,7 +134,6 @@ Ships as both an **ES module** and **UMD bundle**, runs entirely inside a **Shad
144
134
  | **Whitespace rendering** | Visible `·` / `→` for spaces and tabs (`none` / `boundary` / `all`) |
145
135
  | **Cursor styles** | `line`, `block`, or `underline`; configurable blink rate |
146
136
  | **Read-only mode** | All edits blocked; navigation, selection, and copy still work |
147
- | **Collaborative editing** | Real-time multi-user editing via CRDT (RGA/YATA); remote cursors + selections rendered inline; three built-in transports |
148
137
 
149
138
  ---
150
139
 
@@ -1315,253 +1304,6 @@ editor.updateConfig({ readOnly: true }); // lock again
1315
1304
 
1316
1305
  ---
1317
1306
 
1318
- ## Collaborative Editing
1319
-
1320
- Syncline Editor has **built-in real-time collaboration** powered by an RGA/YATA CRDT. All you need is a transport — every operation is causally ordered and converges identically on every peer with no server-side merge logic required.
1321
-
1322
- Collaboration is activated by adding a `collab` property to `EditorConfig`:
1323
-
1324
- ```ts
1325
- import { createEditor, LocalTransport } from 'syncline-editor';
1326
-
1327
- const transport = new LocalTransport('my-room');
1328
-
1329
- const editor = createEditor(container, {
1330
- value: '',
1331
- language: 'typescript',
1332
- collab: {
1333
- transport,
1334
- name: 'Alice',
1335
- },
1336
- });
1337
- ```
1338
-
1339
- That's all. The editor auto-creates the internal `RGADocument`, wires bidirectional sync, and starts rendering remote peers' cursors and selections.
1340
-
1341
- ---
1342
-
1343
- ### Quick Start — Same Page
1344
-
1345
- `LocalTransport` uses a same-page event bus — perfect for demos and tests where multiple `createEditor` calls share one HTML document (e.g. an **A / B** split demo).
1346
-
1347
- ```ts
1348
- import { createEditor, LocalTransport } from 'syncline-editor';
1349
-
1350
- // Both editors share the same room ID — they sync instantly
1351
- const transport = new LocalTransport('demo-room');
1352
-
1353
- const editorA = createEditor(containerA, {
1354
- collab: { transport, name: 'Alice' },
1355
- });
1356
-
1357
- const editorB = createEditor(containerB, {
1358
- collab: { transport: new LocalTransport('demo-room'), name: 'Bob' },
1359
- });
1360
- ```
1361
-
1362
- > Each `LocalTransport` instance gets its own `siteId` automatically. Two instances with the same room string form a peer pair.
1363
-
1364
- ---
1365
-
1366
- ### Cross-Tab Collaboration
1367
-
1368
- `BroadcastChannelTransport` uses the browser's built-in `BroadcastChannel` API — syncs across **multiple tabs on the same origin** with no server needed.
1369
-
1370
- ```ts
1371
- import { createEditor, BroadcastChannelTransport } from 'syncline-editor';
1372
-
1373
- const editor = createEditor(container, {
1374
- collab: {
1375
- transport: new BroadcastChannelTransport('my-room'),
1376
- name: 'Alice',
1377
- },
1378
- });
1379
- ```
1380
-
1381
- Open the same page in two tabs — edits in one appear in the other within milliseconds.
1382
-
1383
- ---
1384
-
1385
- ### Using WebSocket for Realtime Collaboration
1386
-
1387
- For cross-device / cross-origin collaboration, use `WebSocketTransport` with any WebSocket server that broadcasts messages to room participants. The server never needs to understand CRDT operations — it simply relays raw messages.
1388
-
1389
- ```ts
1390
- import { createEditor, WebSocketTransport } from 'syncline-editor';
1391
-
1392
- const editor = createEditor(container, {
1393
- collab: {
1394
- transport: new WebSocketTransport('wss://your-server.example.com', 'my-room'),
1395
- name: 'Alice',
1396
- onStatus: (s) => console.log('collab status:', s),
1397
- },
1398
- });
1399
- ```
1400
-
1401
- You can point `WebSocketTransport` at **any** WebSocket endpoint — a custom Node.js server, a managed service, or a serverless WebSocket API. All CRDT merging happens client-side; the server only needs to broadcast incoming messages to every other participant in the same room.
1402
-
1403
- #### `WebSocketTransport` resilience
1404
-
1405
- - **Exponential backoff** reconnect (100 ms → 1.6 s cap) with automatic re-send of any operations queued while offline
1406
- - **State-vector sync** on reconnect — only missing operations are exchanged, not the full document
1407
- - **Send queue** — operations generated while disconnected are buffered and flushed once the socket reopens
1408
-
1409
- ---
1410
-
1411
- ### CollabConfig Reference
1412
-
1413
- ```ts
1414
- interface CollabConfig {
1415
- /** Transport layer — required. Determines how ops are exchanged. */
1416
- transport: CRDTTransport;
1417
-
1418
- /**
1419
- * Display name shown in the cursor label of remote peers.
1420
- * Defaults to the site ID if omitted.
1421
- */
1422
- name?: string;
1423
-
1424
- /**
1425
- * Called whenever the set of connected peers changes (join / leave / heartbeat).
1426
- * Receives the full current peer map: siteId → AwarenessState.
1427
- */
1428
- onPeersChange?: (peers: Map<string, AwarenessState>) => void;
1429
-
1430
- /**
1431
- * Called when the transport connection status changes.
1432
- * Useful for showing a status indicator in your UI.
1433
- */
1434
- onStatus?: (status: 'connecting' | 'connected' | 'disconnected' | 'syncing') => void;
1435
- }
1436
- ```
1437
-
1438
- Pass `collab: undefined` (or omit it) to disable collaboration.
1439
- Disable at runtime by calling `editor.updateConfig({ collab: undefined })`.
1440
-
1441
- ---
1442
-
1443
- ### Transport Reference
1444
-
1445
- | Transport | Import | Description |
1446
- |---|---|---|
1447
- | `LocalTransport` | `syncline-editor` | In-page event bus. Ideal for split-screen demos and unit tests. |
1448
- | `BroadcastChannelTransport` | `syncline-editor` | Cross-tab sync on the same origin. No server required. |
1449
- | `WebSocketTransport` | `syncline-editor` | Realtime WebSocket. Works across devices and origins. |
1450
-
1451
- All three implement the same `CRDTTransport` interface, so you can provide your own transport (e.g. WebRTC data channels, SSE, etc.) if needed:
1452
-
1453
- ```ts
1454
- interface CRDTTransport {
1455
- readonly siteId: string;
1456
- connect(onMessage: (msg: ChannelMessage) => void): void;
1457
- send(msg: ChannelMessage): void;
1458
- disconnect(): void;
1459
- }
1460
- ```
1461
-
1462
- ---
1463
-
1464
- ### Peer Awareness & Cursors
1465
-
1466
- Syncline broadcasts awareness state (cursor position, selection, display name) over the transport alongside document operations.
1467
-
1468
- - Each connected peer gets a **deterministic color** derived from its `siteId` hash
1469
- - Peer presence is maintained via **3-second heartbeats**; peers that miss 3 beats (9 s) are garbage-collected
1470
- - Remote cursors and selections update on every render cycle — they respond to scrolling, word wrap changes, and virtual-scroll position automatically
1471
-
1472
- #### Reacting to peer changes
1473
-
1474
- ```ts
1475
- const editor = createEditor(container, {
1476
- collab: {
1477
- transport,
1478
- name: 'Alice',
1479
- onPeersChange: (peers) => {
1480
- // peers: Map<siteId, AwarenessState>
1481
- renderPeerAvatars([...peers.values()]);
1482
- },
1483
- },
1484
- });
1485
- ```
1486
-
1487
- #### `AwarenessState` shape
1488
-
1489
- ```ts
1490
- interface AwarenessState {
1491
- siteId: string;
1492
- name?: string;
1493
- color: string; // hex color derived from siteId
1494
- cursor?: { row: number; col: number };
1495
- selection?: { anchor: { row: number; col: number }; focus: { row: number; col: number } };
1496
- updatedAt: number; // timestamp of last heartbeat
1497
- }
1498
- ```
1499
-
1500
- ---
1501
-
1502
- ### Remote Cursor CSS Classes
1503
-
1504
- Remote cursors and selections are rendered as inline `<span>` elements inside the Shadow DOM. You can override their appearance using Shadow DOM part selectors or by targeting the CSS custom properties from outside the shadow root if your host page injects styles into it.
1505
-
1506
- | Class | Element | Description |
1507
- |---|---|---|
1508
- | `.sl-rc` | `<span>` | Zero-width anchor for a remote cursor beam |
1509
- | `.sl-rc::after` | pseudo | The 2 px vertical cursor beam |
1510
- | `.sl-rc-label` | `<span>` | Name chip that floats above the beam |
1511
- | `.sl-rs` | `<span>` | Highlighted text span for a remote selection |
1512
-
1513
- CSS custom properties set inline on each `.sl-rc` and `.sl-rs`:
1514
-
1515
- | Property | Description |
1516
- |---|---|
1517
- | `--rc-color` | Peer color (hex) — controls the beam and label background |
1518
- | `--rs-color` | Peer color with `33` alpha suffix — semi-transparent selection fill |
1519
-
1520
- Example override (injected via `adoptedStyleSheets` or a `<style>` tag appended to the shadow root):
1521
-
1522
- ```css
1523
- /* Thicker cursor beam */
1524
- .sl-rc::after { width: 3px; }
1525
-
1526
- /* Fully opaque name label */
1527
- .sl-rc-label { opacity: 1; }
1528
-
1529
- /* Rounder selection highlight */
1530
- .sl-rs { border-radius: 4px; }
1531
- ```
1532
-
1533
- ---
1534
-
1535
- ### Offline & Reconnect
1536
-
1537
- When a peer disconnects and reconnects:
1538
-
1539
- 1. The transport fires a `connect` event with a **state vector** — a map of `siteId → clock` representing everything the reconnecting peer already has.
1540
- 2. The local `RGADocument` computes the **diff**: operations the peer is missing.
1541
- 3. Only the missing ops are retransmitted — not the full document history.
1542
-
1543
- This makes reconnect efficient even for long editing sessions.
1544
-
1545
- While disconnected, any local edits are stored in the **send queue** and flushed automatically once the socket reopens.
1546
-
1547
- ---
1548
-
1549
- ### Playground Collab Panel
1550
-
1551
- The interactive playground (`npm run dev`) includes a dedicated **Collaboration** panel in the left sidebar:
1552
-
1553
- - **Enable / disable** toggle — connect or disconnect with one click
1554
- - **Display name** — the name shown in your cursor label on other peers' screens
1555
- - **Transport selector** — `Local` / `BroadcastChannel` / `WebSocket`
1556
- - **Room ID** — shared room name (all peers must use the same room)
1557
- - **WebSocket URL** — shown only when the WebSocket transport is selected
1558
- - **Status** — live `connecting` / `connected` / `disconnected` indicator
1559
- - **Connected peers** — colored avatar chips with each peer's display name
1560
-
1561
- The playground CRDT demo section also shows a live **Alice ↔ Bob** split view — both editors on the same page using `LocalTransport` — so you can see remote cursors and selections in action immediately.
1562
-
1563
- ---
1564
-
1565
1307
  ## Behavioral Options
1566
1308
 
1567
1309
  ### Auto-Close Pairs
@@ -1937,12 +1679,6 @@ import type {
1937
1679
  // Themes
1938
1680
  ThemeDefinition,
1939
1681
  ThemeTokens,
1940
-
1941
- // Collaboration
1942
- CollabConfig,
1943
- CRDTTransport,
1944
- AwarenessState,
1945
- ChannelMessage,
1946
1682
  } from 'syncline-editor';
1947
1683
  ```
1948
1684
 
@@ -2053,15 +1789,6 @@ syncline-editor/
2053
1789
  │ │ ├── document.ts # Document model, undo/redo, selection helpers
2054
1790
  │ │ ├── tokeniser.ts # Language-aware syntax tokeniser (zero deps)
2055
1791
  │ │ └── wrap-map.ts # Soft-wrap virtual line map
2056
- │ ├── crdt/
2057
- │ │ ├── rga.ts # RGA/YATA CRDT document — insert/delete with causal ordering
2058
- │ │ ├── binding.ts # Two-way bridge between SynclineEditor and RGADocument
2059
- │ │ ├── awareness.ts # Peer presence heartbeat + garbage collection
2060
- │ │ ├── transport-local.ts # LocalTransport — same-page event bus
2061
- │ │ ├── transport-bc.ts # BroadcastChannelTransport — cross-tab
2062
- │ │ ├── transport-ws.ts # WebSocketTransport — realtime WebSocket + reconnect queue
2063
- │ │ ├── types.ts # CRDT type definitions (CharId, CRDTOp, CollabConfig, …)
2064
- │ │ └── index.ts # CRDT barrel export
2065
1792
  │ ├── features/
2066
1793
  │ │ ├── autocomplete.ts # Completion engine — ranking, filtering, snippet items
2067
1794
  │ │ ├── bracket-matcher.ts # Bracket-pair finder
@@ -2138,7 +1865,6 @@ The playground at `playground/index.html` is a fully self-contained interactive
2138
1865
  - **Behavior** — auto-close pairs, line comment token, word separators, undo batch window
2139
1866
  - **Actions** — buttons for every `executeCommand` and `getValue` / `setValue`
2140
1867
  - **Event log** — live feed of all `onChange` / `onCursorChange` / `onSelectionChange` / `onFocus` / `onBlur` events
2141
- - **Collaboration** — enable/disable collab, choose transport (Local / BroadcastChannel / WebSocket), set display name and room ID, see live peer avatars and connection status
2142
1868
 
2143
1869
  ---
2144
1870
 
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "3.0.0",
6
+ "version": "4.0.0",
7
7
  "description": "A zero-dependency, pixel-perfect, fully customisable browser-based code editor",
8
8
  "type": "module",
9
9
  "main": "./dist/syncline-editor.umd.js",