braid-text 0.3.9 → 0.3.10

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.
@@ -62,8 +62,18 @@ function textarea_highlights(textarea) {
62
62
 
63
63
  // Move textarea's background to the wrapper so backdrops show through
64
64
  var bg = getComputedStyle(textarea).backgroundColor
65
- if (!wrap.style.backgroundColor)
66
- wrap.style.backgroundColor = (!bg || bg === 'rgba(0, 0, 0, 0)') ? 'white' : bg
65
+ if (!wrap.style.backgroundColor) {
66
+ if (!bg || bg === 'rgba(0, 0, 0, 0)') {
67
+ // Walk up the DOM to find the effective background
68
+ var el = wrap
69
+ while (el) {
70
+ var elBg = getComputedStyle(el).backgroundColor
71
+ if (elBg && elBg !== 'rgba(0, 0, 0, 0)') { bg = elBg; break }
72
+ el = el.parentElement
73
+ }
74
+ }
75
+ wrap.style.backgroundColor = bg || 'white'
76
+ }
67
77
  textarea.style.backgroundColor = 'transparent'
68
78
  textarea.style.position = 'relative'
69
79
  textarea.style.zIndex = '2'
@@ -158,6 +168,77 @@ function textarea_highlights(textarea) {
158
168
  return result
159
169
  }
160
170
 
171
+ // --- render implementation ---
172
+
173
+ function do_render() {
174
+ var text = textarea.value
175
+ var len = text.length
176
+ var style_str = backdrop_style()
177
+
178
+ // Remove divs for layers that no longer exist
179
+ for (var id of Object.keys(layer_divs)) {
180
+ if (!layer_data[id]) {
181
+ layer_divs[id].remove()
182
+ delete layer_divs[id]
183
+ }
184
+ }
185
+
186
+ // Render each layer
187
+ for (var id of Object.keys(layer_data)) {
188
+ var highlights = layer_data[id].map(h => ({
189
+ from: Math.min(h.from, len),
190
+ to: Math.min(h.to, len),
191
+ color: h.color
192
+ }))
193
+
194
+ if (!layer_divs[id]) {
195
+ var div = document.createElement('div')
196
+ div.className = 'textarea-hl-backdrop'
197
+ wrap.insertBefore(div, textarea)
198
+ layer_divs[id] = div
199
+ }
200
+
201
+ // Font/padding/border are set inline to match textarea;
202
+ // positioning/pointer-events/etc come from the CSS class.
203
+ layer_divs[id].style.cssText = style_str
204
+
205
+ layer_divs[id].innerHTML = build_html(text, highlights)
206
+ layer_divs[id].scrollTop = textarea.scrollTop
207
+ layer_divs[id].scrollLeft = textarea.scrollLeft
208
+ }
209
+ }
210
+
211
+ // --- show local cursor/selection when textarea is not focused ---
212
+
213
+ var local_id = '__local__'
214
+
215
+ function on_blur() {
216
+ var from = textarea.selectionStart
217
+ var to = textarea.selectionEnd
218
+ var color = getComputedStyle(textarea).caretColor
219
+ if (!color || color === 'auto') color = getComputedStyle(textarea).color
220
+ if (from === to) {
221
+ layer_data[local_id] = [{ from, to, color: color }]
222
+ } else {
223
+ var match = color.match(/(\d+),\s*(\d+),\s*(\d+)/)
224
+ var sel_color = match ? 'rgba(' + match[1] + ', ' + match[2] + ', ' + match[3] + ', 0.3)' : color
225
+ layer_data[local_id] = [{ from, to, color: sel_color }]
226
+ }
227
+ do_render()
228
+ }
229
+
230
+ function on_focus() {
231
+ delete layer_data[local_id]
232
+ if (layer_divs[local_id]) {
233
+ layer_divs[local_id].remove()
234
+ delete layer_divs[local_id]
235
+ }
236
+ do_render()
237
+ }
238
+
239
+ textarea.addEventListener('blur', on_blur)
240
+ textarea.addEventListener('focus', on_focus)
241
+
161
242
  return {
162
243
  set: function(layer_id, highlights) {
163
244
  layer_data[layer_id] = highlights
@@ -171,43 +252,7 @@ function textarea_highlights(textarea) {
171
252
  }
172
253
  },
173
254
 
174
- render: function() {
175
- var text = textarea.value
176
- var len = text.length
177
- var style_str = backdrop_style()
178
-
179
- // Remove divs for layers that no longer exist
180
- for (var id of Object.keys(layer_divs)) {
181
- if (!layer_data[id]) {
182
- layer_divs[id].remove()
183
- delete layer_divs[id]
184
- }
185
- }
186
-
187
- // Render each layer
188
- for (var id of Object.keys(layer_data)) {
189
- var highlights = layer_data[id].map(h => ({
190
- from: Math.min(h.from, len),
191
- to: Math.min(h.to, len),
192
- color: h.color
193
- }))
194
-
195
- if (!layer_divs[id]) {
196
- var div = document.createElement('div')
197
- div.className = 'textarea-hl-backdrop'
198
- wrap.insertBefore(div, textarea)
199
- layer_divs[id] = div
200
- }
201
-
202
- // Font/padding/border are set inline to match textarea;
203
- // positioning/pointer-events/etc come from the CSS class.
204
- layer_divs[id].style.cssText = style_str
205
-
206
- layer_divs[id].innerHTML = build_html(text, highlights)
207
- layer_divs[id].scrollTop = textarea.scrollTop
208
- layer_divs[id].scrollLeft = textarea.scrollLeft
209
- }
210
- },
255
+ render: do_render,
211
256
 
212
257
  layers: function() {
213
258
  return Object.keys(layer_data)
@@ -215,6 +260,8 @@ function textarea_highlights(textarea) {
215
260
 
216
261
  destroy: function() {
217
262
  textarea.removeEventListener('scroll', sync_scroll)
263
+ textarea.removeEventListener('blur', on_blur)
264
+ textarea.removeEventListener('focus', on_focus)
218
265
  for (var div of Object.values(layer_divs)) div.remove()
219
266
  layer_data = {}
220
267
  layer_divs = {}
@@ -238,7 +285,8 @@ function peer_bg_color(peer_id) {
238
285
  var r = parseInt(c.slice(1, 3), 16)
239
286
  var g = parseInt(c.slice(3, 5), 16)
240
287
  var b = parseInt(c.slice(5, 7), 16)
241
- return `rgba(${r}, ${g}, ${b}, 0.25)`
288
+ var dark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
289
+ return `rgba(${r}, ${g}, ${b}, ${dark ? 0.4 : 0.25})`
242
290
  }
243
291
 
244
292
  // --- High-level wrapper ---
@@ -141,13 +141,13 @@ async function cursor_client(url, { peer, get_text, on_change }) {
141
141
  braid_fetch(url, {
142
142
  subscribe: true,
143
143
  retry: { onRes: function() {
144
- if (connected_before && online) {
145
- // Reconnecting — go offline to clear stale cursors.
146
- // The application will call online() again when text is ready.
144
+ if (connected_before) {
145
+ // Reconnecting — clear stale cursors; fresh snapshot incoming.
146
+ // Stay in current online state so the snapshot is processed
147
+ // immediately (the text subscription manages online/offline).
147
148
  var changed = {}
148
149
  for (var id of Object.keys(selections)) changed[id] = []
149
150
  selections = {}
150
- online = false
151
151
  pending = null
152
152
  if (on_change && Object.keys(changed).length) on_change(changed)
153
153
  }
@@ -1,5 +1,5 @@
1
1
  <body style="margin: 0px; padding: 0px; box-sizing: border-box">
2
- <textarea id="the_editor" style="width: 100%; height: 100%;"></textarea>
2
+ <textarea id="the_editor" style="width: 100%; height: 100%;" disabled></textarea>
3
3
  </body>
4
4
  <script src="https://braid.org/code/myers-diff1.js"></script>
5
5
  <script src="https://unpkg.com/braid-http@~1.3/braid-http-client.js"></script>
@@ -12,8 +12,9 @@
12
12
  var cursors = cursor_highlights(the_editor, location.pathname)
13
13
 
14
14
  var simpleton = simpleton_client(location.pathname, {
15
- on_online: ({online}) => { online ? cursors.online() : cursors.offline() },
15
+ on_online: (online) => { online ? cursors.online() : cursors.offline() },
16
16
  on_patches: (patches) => {
17
+ the_editor.disabled = false
17
18
  apply_patches_and_update_selection(the_editor, patches)
18
19
  cursors.on_patches(patches)
19
20
  },
@@ -18,6 +18,7 @@
18
18
  ">
19
19
  <textarea
20
20
  id="the_editor"
21
+ disabled
21
22
  style="
22
23
  width: 100%;
23
24
  height: 100%;
@@ -48,8 +49,9 @@ var render_delay = 100
48
49
  var cursors = cursor_highlights(the_editor, location.pathname)
49
50
 
50
51
  var simpleton = simpleton_client(location.pathname, {
51
- on_online: ({online}) => { online ? cursors.online() : cursors.offline() },
52
+ on_online: (online) => { online ? cursors.online() : cursors.offline() },
52
53
  on_patches: (patches) => {
54
+ the_editor.disabled = false
53
55
  apply_patches_and_update_selection(the_editor, patches)
54
56
  cursors.on_patches(patches)
55
57
  update_markdown_later()
@@ -127,7 +127,7 @@ function simpleton_client(url, {
127
127
  ...(content_type ? {Accept: content_type} : {}) },
128
128
  subscribe: true,
129
129
  retry: () => true,
130
- onSubscriptionStatus: (status) => { if (on_online) on_online(status) },
130
+ onSubscriptionStatus: (status) => { if (on_online) on_online(status.online) },
131
131
  parents: () => client_version.length ? client_version : null,
132
132
  peer,
133
133
  signal: ac.signal
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "braid-text",
3
- "version": "0.3.9",
3
+ "version": "0.3.10",
4
4
  "description": "Library for collaborative text over http using braid.",
5
5
  "author": "Braid Working Group",
6
6
  "repository": "braid-org/braid-text",