braid-text 0.5.10 → 0.5.12
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/client/editor.html +1 -1
- package/client/syncarea.js +10 -7
- package/package.json +5 -2
- package/server.js +23 -16
package/client/editor.html
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<body style="margin: 0; padding: 0; box-sizing: border-box">
|
|
2
|
-
<
|
|
2
|
+
<sync-area id="the_editor" style="width: 100%; height: 100vh;"></sync-area>
|
|
3
3
|
</body>
|
|
4
4
|
<script src="https://unpkg.com/braid-http@~1.3/braid-http-client.js"></script>
|
|
5
5
|
<script src="/myers-diff.js"></script>
|
package/client/syncarea.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// ============================================================
|
|
2
|
-
// <
|
|
2
|
+
// <sync-area> — a textarea wired to a braid-http resource
|
|
3
3
|
// ============================================================
|
|
4
4
|
//
|
|
5
5
|
// A drop-in replacement for <textarea> that stays in sync with a
|
|
@@ -12,19 +12,20 @@
|
|
|
12
12
|
// src — the URL to sync to
|
|
13
13
|
// cursors — sync cursors too (default: true; set cursors="false" to disable)
|
|
14
14
|
// bearer — Bearer token for servers using "Authorization: Bearer X" headers
|
|
15
|
+
// accept — Accept (and Content-Type) for the resource (e.g. "text/markdown")
|
|
15
16
|
//
|
|
16
17
|
// The following attributes are relayed to the inner textarea:
|
|
17
18
|
// placeholder, readonly, rows, cols, wrap, spellcheck, autofocus,
|
|
18
19
|
// disabled, aria-label, aria-labelledby, aria-describedby
|
|
19
20
|
//
|
|
20
|
-
// Changes to src, cursors, or
|
|
21
|
+
// Changes to src, cursors, bearer, or accept restart the sync.
|
|
21
22
|
// Changes to any other attribute are just forwarded to the inner textarea.
|
|
22
23
|
//
|
|
23
24
|
// You can always manipulate the inner textarea directly via the .textarea property.
|
|
24
25
|
|
|
25
26
|
|
|
26
27
|
// Attributes in this list restart the sync when they change.
|
|
27
|
-
const CONNECTION_ATTRS = ['src', 'bearer', 'cursors']
|
|
28
|
+
const CONNECTION_ATTRS = ['src', 'bearer', 'cursors', 'accept']
|
|
28
29
|
|
|
29
30
|
// Attributes in this list are relayed straight to the inner textarea.
|
|
30
31
|
const FORWARD_ATTRS = ['placeholder', 'readonly', 'rows', 'cols',
|
|
@@ -32,7 +33,7 @@ const FORWARD_ATTRS = ['placeholder', 'readonly', 'rows', 'cols',
|
|
|
32
33
|
'aria-label', 'aria-labelledby', 'aria-describedby']
|
|
33
34
|
|
|
34
35
|
|
|
35
|
-
class
|
|
36
|
+
class SyncArea extends HTMLElement {
|
|
36
37
|
|
|
37
38
|
static observedAttributes = [...CONNECTION_ATTRS, ...FORWARD_ATTRS, 'disabled']
|
|
38
39
|
|
|
@@ -58,8 +59,9 @@ class SyncedTextarea extends HTMLElement {
|
|
|
58
59
|
// Builds the inner textarea, wires up events, starts simpleton_client
|
|
59
60
|
// and cursor_highlights if enabled.
|
|
60
61
|
connect() {
|
|
62
|
+
console.log('DBG sync-area: connect', {src: this.getAttribute('src'), accept: this.getAttribute('accept')})
|
|
61
63
|
if (this.firstChild)
|
|
62
|
-
console.warn('<
|
|
64
|
+
console.warn('<sync-area> ignoring existing child content')
|
|
63
65
|
while (this.firstChild) this.removeChild(this.firstChild)
|
|
64
66
|
|
|
65
67
|
// Create the inner textarea, and fill the outer element with it.
|
|
@@ -80,7 +82,7 @@ class SyncedTextarea extends HTMLElement {
|
|
|
80
82
|
this.update_disabled()
|
|
81
83
|
|
|
82
84
|
// Re-dispatch the inner textarea's focus/blur events on the outer,
|
|
83
|
-
// so listeners on <
|
|
85
|
+
// so listeners on <sync-area> see them.
|
|
84
86
|
textarea.addEventListener('focus', () =>
|
|
85
87
|
this.dispatchEvent(new FocusEvent('focus')))
|
|
86
88
|
textarea.addEventListener('blur', () =>
|
|
@@ -109,6 +111,7 @@ class SyncedTextarea extends HTMLElement {
|
|
|
109
111
|
// The main sync client.
|
|
110
112
|
this.client = simpleton_client(url, {
|
|
111
113
|
headers,
|
|
114
|
+
content_type: this.getAttribute('accept') || undefined,
|
|
112
115
|
on_online: (online) => { online ? cursors?.online() : cursors?.offline() },
|
|
113
116
|
on_update: (update) => {
|
|
114
117
|
this.waiting_for_first_update = false
|
|
@@ -218,4 +221,4 @@ class SyncedTextarea extends HTMLElement {
|
|
|
218
221
|
}
|
|
219
222
|
|
|
220
223
|
|
|
221
|
-
customElements.define('
|
|
224
|
+
customElements.define('sync-area', SyncArea)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "braid-text",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.12",
|
|
4
4
|
"description": "Library for collaborative text over http using braid.",
|
|
5
5
|
"author": "Braid Working Group",
|
|
6
6
|
"repository": "braid-org/braid-text",
|
|
@@ -27,7 +27,10 @@
|
|
|
27
27
|
],
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@braid.org/diamond-types-node": "^2.0.1",
|
|
30
|
-
"braid-http": "~1.3.
|
|
30
|
+
"braid-http": "~1.3.125"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"yjs": "^13.6.0"
|
|
31
34
|
},
|
|
32
35
|
"peerDependencies": {
|
|
33
36
|
"yjs": "^13.6.0"
|
package/server.js
CHANGED
|
@@ -329,10 +329,16 @@ function create_braid_text() {
|
|
|
329
329
|
braid_text.serve = async (req, res, options = {}) => {
|
|
330
330
|
options = {
|
|
331
331
|
key: req.url.split('?')[0],
|
|
332
|
+
content_type: 'text/plain',
|
|
332
333
|
put_cb: (key, val, params) => { },
|
|
333
334
|
...options
|
|
334
335
|
}
|
|
335
336
|
|
|
337
|
+
// Ensure charset=utf-8 on the content type
|
|
338
|
+
var content_type = options.content_type
|
|
339
|
+
if (!content_type.includes('charset='))
|
|
340
|
+
content_type += '; charset=utf-8'
|
|
341
|
+
|
|
336
342
|
// ── Setup: prepare the response and load the resource ──
|
|
337
343
|
|
|
338
344
|
if (braid_text.cors !== false) braid_text.free_cors(res)
|
|
@@ -370,19 +376,6 @@ function create_braid_text() {
|
|
|
370
376
|
is_write = req.method === 'PUT' || req.method === 'POST' || req.method === 'PATCH',
|
|
371
377
|
is_head = req.method === 'HEAD'
|
|
372
378
|
|
|
373
|
-
// ── Ensure the response is labeled as utf-8 text ──
|
|
374
|
-
|
|
375
|
-
if (!res.getHeader('content-type')) res.setHeader('Content-Type', 'text/plain')
|
|
376
|
-
var ct = res.getHeader('Content-Type'),
|
|
377
|
-
ct_parts = ct.split(';').map(p => p.trim())
|
|
378
|
-
var charset = ct_parts.find(p => p.toLowerCase().startsWith('charset='))
|
|
379
|
-
if (!charset)
|
|
380
|
-
res.setHeader('Content-Type', `${ct}; charset=utf-8`)
|
|
381
|
-
else if (charset.toLowerCase() !== 'charset=utf-8')
|
|
382
|
-
res.setHeader('Content-Type', ct_parts
|
|
383
|
-
.map(p => p.toLowerCase().startsWith('charset=') ? 'charset=utf-8' : p)
|
|
384
|
-
.join('; '))
|
|
385
|
-
|
|
386
379
|
// ── Handle simple methods that don't need further processing ──
|
|
387
380
|
|
|
388
381
|
if (req.method === 'OPTIONS') return my_end(200)
|
|
@@ -442,7 +435,7 @@ function create_braid_text() {
|
|
|
442
435
|
|
|
443
436
|
// HEAD: headers only, no body needed
|
|
444
437
|
if (is_head) {
|
|
445
|
-
|
|
438
|
+
res.setHeader('Content-Type', content_type)
|
|
446
439
|
if (!getting.history)
|
|
447
440
|
res.setHeader('Version', current_version())
|
|
448
441
|
return my_end(200)
|
|
@@ -469,13 +462,25 @@ function create_braid_text() {
|
|
|
469
462
|
// Range of history: send as 209 Multiresponse
|
|
470
463
|
res.startSubscription()
|
|
471
464
|
for (var u of result)
|
|
472
|
-
res.
|
|
465
|
+
res.sendUpdate({
|
|
473
466
|
version: [u.version],
|
|
474
467
|
parents: u.parents,
|
|
475
468
|
patches: [{ unit: u.unit, range: u.range, content: u.content }],
|
|
469
|
+
content_type,
|
|
476
470
|
})
|
|
477
471
|
return res.end()
|
|
472
|
+
} else if (false) {
|
|
473
|
+
// New path: use sendUpdate (currently broken, investigating)
|
|
474
|
+
res.sendUpdate({
|
|
475
|
+
version: result.version,
|
|
476
|
+
'Repr-Digest': get_digest(result.body),
|
|
477
|
+
content_type,
|
|
478
|
+
body: result.body,
|
|
479
|
+
})
|
|
480
|
+
return res.end()
|
|
478
481
|
} else {
|
|
482
|
+
// Old path: explicit headers
|
|
483
|
+
res.setHeader('Content-Type', content_type)
|
|
479
484
|
res.setHeader('Version', ascii_ify(result.version
|
|
480
485
|
.map(v => JSON.stringify(v))
|
|
481
486
|
.join(', ')))
|
|
@@ -514,7 +519,9 @@ function create_braid_text() {
|
|
|
514
519
|
update.patch = update.patches[0]
|
|
515
520
|
delete update.patches
|
|
516
521
|
}
|
|
517
|
-
|
|
522
|
+
if (!update.encoding)
|
|
523
|
+
update.content_type = content_type
|
|
524
|
+
res.sendUpdate(update)
|
|
518
525
|
},
|
|
519
526
|
})
|
|
520
527
|
// Ensure headers are sent even if .get() didn't send
|