braid-text 0.5.9 → 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.
@@ -1,5 +1,5 @@
1
1
  <body style="margin: 0; padding: 0; box-sizing: border-box">
2
- <synced-textarea id="the_editor" style="width: 100%; height: 100vh;"></synced-textarea>
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>
@@ -63,8 +63,8 @@ function simpleton_client(url, {
63
63
  get_patches,
64
64
 
65
65
  on_update,
66
- on_error,
67
- on_online,
66
+ on_error, // (error) =>
67
+ on_online, // (is_online) =>
68
68
  on_ack,
69
69
 
70
70
  headers, // The user can pass in custom headers
@@ -1,5 +1,5 @@
1
1
  // ============================================================
2
- // <synced-textarea> — a textarea wired to a braid-http resource
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 bearer restart the sync.
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 SyncedTextarea extends HTMLElement {
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('<synced-textarea> ignoring existing child content')
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 <synced-textarea> see them.
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('synced-textarea', SyncedTextarea)
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.9",
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.124"
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)
@@ -408,8 +401,8 @@ function create_braid_text() {
408
401
  Version: ascii_ify(unknowns.map(e => JSON.stringify(e)).join(', '))
409
402
  })
410
403
 
411
- var has_parents = req.parents && req.parents.length > 0
412
- var has_version = req.version && req.version.length > 0
404
+ var has_parents = Array.isArray(req.parents)
405
+ var has_version = Array.isArray(req.version)
413
406
 
414
407
  if (req.subscribe && has_version)
415
408
  return my_end(400, 'Version header is not allowed with Subscribe — use Parents instead')
@@ -442,7 +435,7 @@ function create_braid_text() {
442
435
 
443
436
  // HEAD: headers only, no body needed
444
437
  if (is_head) {
445
- // Always include the version of what would be returned
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.sendVersion({
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
- res.sendVersion(update)
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
@@ -685,8 +692,8 @@ function create_braid_text() {
685
692
  var version = resource.version
686
693
  var merge_type = options.range_unit === 'yjs-text' ? 'yjs'
687
694
  : (options.merge_type || 'simpleton')
688
- var has_parents = options.parents && options.parents.length > 0
689
- var has_version = options.version && options.version.length > 0
695
+ var has_parents = Array.isArray(options.parents)
696
+ var has_version = Array.isArray(options.version)
690
697
 
691
698
  if (options.subscribe && has_version)
692
699
  throw new Error('version is not allowed with subscribe — use parents instead')
@@ -704,21 +711,9 @@ function create_braid_text() {
704
711
  }
705
712
  getting.single_snapshot = !getting.subscribe && !getting.history
706
713
 
707
- // Single snapshot: return the text (optionally at a specific version)
708
- if (getting.single_snapshot) {
709
- if (has_version) {
710
- await ensure_dt_exists(resource)
711
- return options.full_response
712
- ? { version: options.version, body: dt_get_string(resource.dt.doc, options.version) }
713
- : dt_get_string(resource.dt.doc, options.version)
714
- }
715
- return options.full_response ? { version, body: resource.val } : resource.val
716
- }
717
-
718
714
  // DT binary encoding: a transport optimization usable by any merge type.
719
715
  // Returns raw DT bytes instead of text.
720
- if (getting.history && !getting.subscribe && getting.transfer_encoding === 'dt') {
721
- // TODO: move this into the dt/simpleton merge_type cases below
716
+ if (!getting.subscribe && getting.transfer_encoding === 'dt') {
722
717
  await ensure_dt_exists(resource)
723
718
  // If requesting the current version, skip the version lookup
724
719
  // (faster than asking DT about a version we already have)
@@ -742,6 +737,17 @@ function create_braid_text() {
742
737
  return { body: bytes }
743
738
  }
744
739
 
740
+ // Single snapshot: return the text (optionally at a specific version)
741
+ if (getting.single_snapshot) {
742
+ if (has_version) {
743
+ await ensure_dt_exists(resource)
744
+ return options.full_response
745
+ ? { version: options.version, body: dt_get_string(resource.dt.doc, options.version) }
746
+ : dt_get_string(resource.dt.doc, options.version)
747
+ }
748
+ return options.full_response ? { version, body: resource.val } : resource.val
749
+ }
750
+
745
751
  // Each merge-type has a different way of getting history
746
752
  switch (merge_type) {
747
753