@synclineapi/editor 3.0.1 → 4.0.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/README.md +0 -274
- package/package.json +1 -1
- package/dist/syncline-editor.es.js +0 -5293
- package/dist/syncline-editor.es.js.map +0 -1
- package/dist/syncline-editor.umd.js +0 -569
- package/dist/syncline-editor.umd.js.map +0 -1
- package/dist/types/index.d.ts +0 -1596
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