@codemirror/view 6.43.11 → 6.43.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/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## 6.43.12 (2026-09-15)
2
+
3
+ ### Bug fixes
4
+
5
+ Make `visualLineSide` use bidi information when looking for the start or end of line with no wrap points.
6
+
7
+ Fix a bug in updating the DOM for marks around an active composition that could sometimes try to use the same DOM node twice.
8
+
9
+ Work around an issue on Firefox where a composition might be started outside of a line container.
10
+
11
+ Make sure repeated screen reader announcement messages don't get swallowed by VoiceOver.
12
+
1
13
  ## 6.43.11 (2026-09-03)
2
14
 
3
15
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -2323,12 +2323,13 @@ class TileBuilder {
2323
2323
  head = last;
2324
2324
  }
2325
2325
  else {
2326
+ let { dom } = mark;
2326
2327
  if (this.cache.reused.get(mark)) {
2327
2328
  let tile = Tile.get(mark.dom);
2328
2329
  if (tile)
2329
- tile.setDOM(freeNode(mark.dom));
2330
+ dom = freeNode(mark.dom);
2330
2331
  }
2331
- let nw = MarkTile.of(mark.mark, mark.dom);
2332
+ let nw = MarkTile.of(mark.mark, dom);
2332
2333
  head.append(nw);
2333
2334
  head = nw;
2334
2335
  }
@@ -2828,12 +2829,14 @@ class TileUpdate {
2828
2829
  else if (tile === null || tile === void 0 ? void 0 : tile.isLine())
2829
2830
  line = tile;
2830
2831
  else if (tile instanceof BlockWrapperTile) ; // Ignore
2831
- else if (parent.nodeName == "DIV" && !line && parent != this.view.contentDOM)
2832
+ else if (parent.nodeName == "DIV" && !line)
2832
2833
  line = new LineTile(parent, lineBaseAttrs);
2833
2834
  else if (!line)
2834
2835
  marks.push(MarkTile.of(new MarkDecoration({ tagName: parent.nodeName.toLowerCase(), attributes: getAttrs(parent) }), parent));
2835
2836
  }
2836
- return { line: line, marks };
2837
+ if (!line)
2838
+ return null;
2839
+ return { line, marks };
2837
2840
  }
2838
2841
  }
2839
2842
  function hasContent(tile, requireText) {
@@ -3664,18 +3667,21 @@ function blockAt(view, pos, side) {
3664
3667
  return line;
3665
3668
  }
3666
3669
  function moveToLineBoundary(view, start, forward, includeWrap) {
3667
- let line = blockAt(view, start.head, start.assoc || -1);
3668
- let coords = !includeWrap || line.type != exports.BlockType.Text || !(view.lineWrapping || line.widgetLineBreaks) ? null
3669
- : view.coordsAtPos(start.assoc < 0 && start.head > line.from ? start.head - 1 : start.head);
3670
+ let block = blockAt(view, start.head, start.assoc || -1);
3671
+ let coords = !includeWrap || block.type != exports.BlockType.Text || !(view.lineWrapping || block.widgetLineBreaks) ? null
3672
+ : view.coordsAtPos(start.assoc < 0 && start.head > block.from ? start.head - 1 : start.head);
3670
3673
  if (coords) {
3671
3674
  let editorRect = view.dom.getBoundingClientRect();
3672
- let direction = view.textDirectionAt(line.from);
3675
+ let direction = view.textDirectionAt(block.from);
3673
3676
  let pos = view.posAtCoords({ x: forward == (direction == exports.Direction.LTR) ? editorRect.right - 1 : editorRect.left + 1,
3674
3677
  y: (coords.top + coords.bottom) / 2 });
3675
3678
  if (pos != null)
3676
3679
  return state.EditorSelection.cursor(pos, forward ? -1 : 1);
3677
3680
  }
3678
- return state.EditorSelection.cursor(forward ? line.to : line.from, forward ? -1 : 1);
3681
+ let line = view.state.doc.lineAt(start.head);
3682
+ if (forward ? line.to == block.to : line.from == block.from)
3683
+ return view.visualLineSide(line, forward);
3684
+ return state.EditorSelection.cursor(forward ? block.to : block.from, forward ? -1 : 1);
3679
3685
  }
3680
3686
  function moveByChar(view, start, forward, by) {
3681
3687
  let line = view.state.doc.lineAt(start.head), spans = view.bidiSpans(line);
@@ -5263,6 +5269,13 @@ observers.compositionstart = observers.compositionupdate = view => {
5263
5269
  if (view.inputState.compositionFirstChange == null)
5264
5270
  view.inputState.compositionFirstChange = true;
5265
5271
  if (view.inputState.composing < 0) {
5272
+ let { main } = view.state.selection;
5273
+ if (!main.empty && view.lineBlockAt(main.from).from != view.lineBlockAt(main.to).from) {
5274
+ view.dispatch({
5275
+ changes: view.state.selection.ranges.filter(r => !r.empty).map(r => ({ from: r.from, to: r.to })),
5276
+ userEvent: "input"
5277
+ });
5278
+ }
5266
5279
  // FIXME possibly set a timeout to clear it again on Android
5267
5280
  view.inputState.composing = 0;
5268
5281
  }
@@ -7896,6 +7909,7 @@ class EditorView {
7896
7909
  @internal
7897
7910
  */
7898
7911
  this.measureRequests = [];
7912
+ this.clearAnnouncement = -1;
7899
7913
  this.contentDOM = document.createElement("div");
7900
7914
  this.scrollDOM = document.createElement("div");
7901
7915
  this.scrollDOM.tabIndex = -1;
@@ -8301,9 +8315,14 @@ class EditorView {
8301
8315
  for (let tr of trs)
8302
8316
  for (let effect of tr.effects)
8303
8317
  if (effect.is(EditorView.announce)) {
8304
- if (first)
8318
+ if (first) {
8305
8319
  this.announceDOM.textContent = "";
8306
- first = false;
8320
+ this.win.clearTimeout(this.clearAnnouncement);
8321
+ this.clearAnnouncement = this.win.setTimeout(() => {
8322
+ this.announceDOM.textContent = "\u00a0";
8323
+ }, 200);
8324
+ first = false;
8325
+ }
8307
8326
  let div = this.announceDOM.appendChild(document.createElement("div"));
8308
8327
  div.textContent = effect.value;
8309
8328
  }
@@ -8653,6 +8672,7 @@ class EditorView {
8653
8672
  this.docView.destroy();
8654
8673
  this.dom.remove();
8655
8674
  this.observer.destroy();
8675
+ this.win.clearTimeout(this.clearAnnouncement);
8656
8676
  if (this.measureScheduled > -1)
8657
8677
  this.win.cancelAnimationFrame(this.measureScheduled);
8658
8678
  this.destroyed = true;
package/dist/index.d.cts CHANGED
@@ -813,6 +813,7 @@ declare class EditorView {
813
813
  private styleModules;
814
814
  private bidiCache;
815
815
  private destroyed;
816
+ private clearAnnouncement;
816
817
  /**
817
818
  Construct a new view. You'll want to either provide a `parent`
818
819
  option, or put `view.dom` into your document after creating a
package/dist/index.d.ts CHANGED
@@ -813,6 +813,7 @@ declare class EditorView {
813
813
  private styleModules;
814
814
  private bidiCache;
815
815
  private destroyed;
816
+ private clearAnnouncement;
816
817
  /**
817
818
  Construct a new view. You'll want to either provide a `parent`
818
819
  option, or put `view.dom` into your document after creating a
package/dist/index.js CHANGED
@@ -2319,12 +2319,13 @@ class TileBuilder {
2319
2319
  head = last;
2320
2320
  }
2321
2321
  else {
2322
+ let { dom } = mark;
2322
2323
  if (this.cache.reused.get(mark)) {
2323
2324
  let tile = Tile.get(mark.dom);
2324
2325
  if (tile)
2325
- tile.setDOM(freeNode(mark.dom));
2326
+ dom = freeNode(mark.dom);
2326
2327
  }
2327
- let nw = MarkTile.of(mark.mark, mark.dom);
2328
+ let nw = MarkTile.of(mark.mark, dom);
2328
2329
  head.append(nw);
2329
2330
  head = nw;
2330
2331
  }
@@ -2824,12 +2825,14 @@ class TileUpdate {
2824
2825
  else if (tile === null || tile === void 0 ? void 0 : tile.isLine())
2825
2826
  line = tile;
2826
2827
  else if (tile instanceof BlockWrapperTile) ; // Ignore
2827
- else if (parent.nodeName == "DIV" && !line && parent != this.view.contentDOM)
2828
+ else if (parent.nodeName == "DIV" && !line)
2828
2829
  line = new LineTile(parent, lineBaseAttrs);
2829
2830
  else if (!line)
2830
2831
  marks.push(MarkTile.of(new MarkDecoration({ tagName: parent.nodeName.toLowerCase(), attributes: getAttrs(parent) }), parent));
2831
2832
  }
2832
- return { line: line, marks };
2833
+ if (!line)
2834
+ return null;
2835
+ return { line, marks };
2833
2836
  }
2834
2837
  }
2835
2838
  function hasContent(tile, requireText) {
@@ -3660,18 +3663,21 @@ function blockAt(view, pos, side) {
3660
3663
  return line;
3661
3664
  }
3662
3665
  function moveToLineBoundary(view, start, forward, includeWrap) {
3663
- let line = blockAt(view, start.head, start.assoc || -1);
3664
- let coords = !includeWrap || line.type != BlockType.Text || !(view.lineWrapping || line.widgetLineBreaks) ? null
3665
- : view.coordsAtPos(start.assoc < 0 && start.head > line.from ? start.head - 1 : start.head);
3666
+ let block = blockAt(view, start.head, start.assoc || -1);
3667
+ let coords = !includeWrap || block.type != BlockType.Text || !(view.lineWrapping || block.widgetLineBreaks) ? null
3668
+ : view.coordsAtPos(start.assoc < 0 && start.head > block.from ? start.head - 1 : start.head);
3666
3669
  if (coords) {
3667
3670
  let editorRect = view.dom.getBoundingClientRect();
3668
- let direction = view.textDirectionAt(line.from);
3671
+ let direction = view.textDirectionAt(block.from);
3669
3672
  let pos = view.posAtCoords({ x: forward == (direction == Direction.LTR) ? editorRect.right - 1 : editorRect.left + 1,
3670
3673
  y: (coords.top + coords.bottom) / 2 });
3671
3674
  if (pos != null)
3672
3675
  return EditorSelection.cursor(pos, forward ? -1 : 1);
3673
3676
  }
3674
- return EditorSelection.cursor(forward ? line.to : line.from, forward ? -1 : 1);
3677
+ let line = view.state.doc.lineAt(start.head);
3678
+ if (forward ? line.to == block.to : line.from == block.from)
3679
+ return view.visualLineSide(line, forward);
3680
+ return EditorSelection.cursor(forward ? block.to : block.from, forward ? -1 : 1);
3675
3681
  }
3676
3682
  function moveByChar(view, start, forward, by) {
3677
3683
  let line = view.state.doc.lineAt(start.head), spans = view.bidiSpans(line);
@@ -5259,6 +5265,13 @@ observers.compositionstart = observers.compositionupdate = view => {
5259
5265
  if (view.inputState.compositionFirstChange == null)
5260
5266
  view.inputState.compositionFirstChange = true;
5261
5267
  if (view.inputState.composing < 0) {
5268
+ let { main } = view.state.selection;
5269
+ if (!main.empty && view.lineBlockAt(main.from).from != view.lineBlockAt(main.to).from) {
5270
+ view.dispatch({
5271
+ changes: view.state.selection.ranges.filter(r => !r.empty).map(r => ({ from: r.from, to: r.to })),
5272
+ userEvent: "input"
5273
+ });
5274
+ }
5262
5275
  // FIXME possibly set a timeout to clear it again on Android
5263
5276
  view.inputState.composing = 0;
5264
5277
  }
@@ -7891,6 +7904,7 @@ class EditorView {
7891
7904
  @internal
7892
7905
  */
7893
7906
  this.measureRequests = [];
7907
+ this.clearAnnouncement = -1;
7894
7908
  this.contentDOM = document.createElement("div");
7895
7909
  this.scrollDOM = document.createElement("div");
7896
7910
  this.scrollDOM.tabIndex = -1;
@@ -8296,9 +8310,14 @@ class EditorView {
8296
8310
  for (let tr of trs)
8297
8311
  for (let effect of tr.effects)
8298
8312
  if (effect.is(EditorView.announce)) {
8299
- if (first)
8313
+ if (first) {
8300
8314
  this.announceDOM.textContent = "";
8301
- first = false;
8315
+ this.win.clearTimeout(this.clearAnnouncement);
8316
+ this.clearAnnouncement = this.win.setTimeout(() => {
8317
+ this.announceDOM.textContent = "\u00a0";
8318
+ }, 200);
8319
+ first = false;
8320
+ }
8302
8321
  let div = this.announceDOM.appendChild(document.createElement("div"));
8303
8322
  div.textContent = effect.value;
8304
8323
  }
@@ -8648,6 +8667,7 @@ class EditorView {
8648
8667
  this.docView.destroy();
8649
8668
  this.dom.remove();
8650
8669
  this.observer.destroy();
8670
+ this.win.clearTimeout(this.clearAnnouncement);
8651
8671
  if (this.measureScheduled > -1)
8652
8672
  this.win.cancelAnimationFrame(this.measureScheduled);
8653
8673
  this.destroyed = true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/view",
3
- "version": "6.43.11",
3
+ "version": "6.43.12",
4
4
  "description": "DOM view component for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",