@codemirror/view 6.37.2 → 6.38.0

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,9 @@
1
+ ## 6.38.0 (2025-06-27)
2
+
3
+ ### New features
4
+
5
+ Gutters can now specify that they should be displayed after the content (which would be to the right in a left-to-right layout).
6
+
1
7
  ## 6.37.2 (2025-06-12)
2
8
 
3
9
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -6591,13 +6591,16 @@ const baseTheme$1 = buildTheme("." + baseThemeID, {
6591
6591
  display: "flex",
6592
6592
  height: "100%",
6593
6593
  boxSizing: "border-box",
6594
- insetInlineStart: 0,
6595
- zIndex: 200
6594
+ zIndex: 200,
6596
6595
  },
6596
+ ".cm-gutters-before": { insetInlineStart: 0 },
6597
+ ".cm-gutters-after": { insetInlineEnd: 0 },
6597
6598
  "&light .cm-gutters": {
6598
6599
  backgroundColor: "#f5f5f5",
6599
6600
  color: "#6c6c6c",
6600
- borderRight: "1px solid #ddd"
6601
+ border: "0px solid #ddd",
6602
+ "&.cm-gutters-before": { borderRightWidth: "1px" },
6603
+ "&.cm-gutters-after": { borderLeftWidth: "1px" },
6601
6604
  },
6602
6605
  "&dark .cm-gutters": {
6603
6606
  backgroundColor: "#333338",
@@ -6788,7 +6791,7 @@ class DOMObserver {
6788
6791
  else
6789
6792
  this.flush();
6790
6793
  });
6791
- if (window.EditContext && view.constructor.EDIT_CONTEXT !== false &&
6794
+ if (window.EditContext && browser.android && view.constructor.EDIT_CONTEXT !== false &&
6792
6795
  // Chrome <126 doesn't support inverted selections in edit context (#1392)
6793
6796
  !(browser.chrome && browser.chrome_version < 126)) {
6794
6797
  this.editContext = new EditContextManager(view);
@@ -10868,7 +10871,8 @@ const defaults = {
10868
10871
  lineMarkerChange: null,
10869
10872
  initialSpacer: null,
10870
10873
  updateSpacer: null,
10871
- domEventHandlers: {}
10874
+ domEventHandlers: {},
10875
+ side: "before"
10872
10876
  };
10873
10877
  const activeGutters = state.Facet.define();
10874
10878
  /**
@@ -10902,15 +10906,20 @@ function gutters(config) {
10902
10906
  const gutterView = ViewPlugin.fromClass(class {
10903
10907
  constructor(view) {
10904
10908
  this.view = view;
10909
+ this.domAfter = null;
10905
10910
  this.prevViewport = view.viewport;
10906
10911
  this.dom = document.createElement("div");
10907
- this.dom.className = "cm-gutters";
10912
+ this.dom.className = "cm-gutters cm-gutters-before";
10908
10913
  this.dom.setAttribute("aria-hidden", "true");
10909
10914
  this.dom.style.minHeight = (this.view.contentHeight / this.view.scaleY) + "px";
10910
10915
  this.gutters = view.state.facet(activeGutters).map(conf => new SingleGutterView(view, conf));
10911
- for (let gutter of this.gutters)
10912
- this.dom.appendChild(gutter.dom);
10913
10916
  this.fixed = !view.state.facet(unfixGutters);
10917
+ for (let gutter of this.gutters) {
10918
+ if (gutter.config.side == "after")
10919
+ this.getDOMAfter().appendChild(gutter.dom);
10920
+ else
10921
+ this.dom.appendChild(gutter.dom);
10922
+ }
10914
10923
  if (this.fixed) {
10915
10924
  // FIXME IE11 fallback, which doesn't support position: sticky,
10916
10925
  // by using position: relative + event handlers that realign the
@@ -10920,6 +10929,17 @@ const gutterView = ViewPlugin.fromClass(class {
10920
10929
  this.syncGutters(false);
10921
10930
  view.scrollDOM.insertBefore(this.dom, view.contentDOM);
10922
10931
  }
10932
+ getDOMAfter() {
10933
+ if (!this.domAfter) {
10934
+ this.domAfter = document.createElement("div");
10935
+ this.domAfter.className = "cm-gutters cm-gutters-after";
10936
+ this.domAfter.setAttribute("aria-hidden", "true");
10937
+ this.domAfter.style.minHeight = (this.view.contentHeight / this.view.scaleY) + "px";
10938
+ this.domAfter.style.position = this.fixed ? "sticky" : "";
10939
+ this.view.scrollDOM.appendChild(this.domAfter);
10940
+ }
10941
+ return this.domAfter;
10942
+ }
10923
10943
  update(update) {
10924
10944
  if (this.updateGutters(update)) {
10925
10945
  // Detach during sync when the viewport changed significantly
@@ -10930,18 +10950,26 @@ const gutterView = ViewPlugin.fromClass(class {
10930
10950
  this.syncGutters(vpOverlap < (vpB.to - vpB.from) * 0.8);
10931
10951
  }
10932
10952
  if (update.geometryChanged) {
10933
- this.dom.style.minHeight = (this.view.contentHeight / this.view.scaleY) + "px";
10953
+ let min = (this.view.contentHeight / this.view.scaleY) + "px";
10954
+ this.dom.style.minHeight = min;
10955
+ if (this.domAfter)
10956
+ this.domAfter.style.minHeight = min;
10934
10957
  }
10935
10958
  if (this.view.state.facet(unfixGutters) != !this.fixed) {
10936
10959
  this.fixed = !this.fixed;
10937
10960
  this.dom.style.position = this.fixed ? "sticky" : "";
10961
+ if (this.domAfter)
10962
+ this.domAfter.style.position = this.fixed ? "sticky" : "";
10938
10963
  }
10939
10964
  this.prevViewport = update.view.viewport;
10940
10965
  }
10941
10966
  syncGutters(detach) {
10942
10967
  let after = this.dom.nextSibling;
10943
- if (detach)
10968
+ if (detach) {
10944
10969
  this.dom.remove();
10970
+ if (this.domAfter)
10971
+ this.domAfter.remove();
10972
+ }
10945
10973
  let lineClasses = state.RangeSet.iter(this.view.state.facet(gutterLineClass), this.view.viewport.from);
10946
10974
  let classSet = [];
10947
10975
  let contexts = this.gutters.map(gutter => new UpdateContext(gutter, this.view.viewport, -this.view.documentPadding.top));
@@ -10975,8 +11003,11 @@ const gutterView = ViewPlugin.fromClass(class {
10975
11003
  }
10976
11004
  for (let cx of contexts)
10977
11005
  cx.finish();
10978
- if (detach)
11006
+ if (detach) {
10979
11007
  this.view.scrollDOM.insertBefore(this.dom, after);
11008
+ if (this.domAfter)
11009
+ this.view.scrollDOM.appendChild(this.domAfter);
11010
+ }
10980
11011
  }
10981
11012
  updateGutters(update) {
10982
11013
  let prev = update.startState.facet(activeGutters), cur = update.state.facet(activeGutters);
@@ -11005,8 +11036,12 @@ const gutterView = ViewPlugin.fromClass(class {
11005
11036
  if (gutters.indexOf(g) < 0)
11006
11037
  g.destroy();
11007
11038
  }
11008
- for (let g of gutters)
11009
- this.dom.appendChild(g.dom);
11039
+ for (let g of gutters) {
11040
+ if (g.config.side == "after")
11041
+ this.getDOMAfter().appendChild(g.dom);
11042
+ else
11043
+ this.dom.appendChild(g.dom);
11044
+ }
11010
11045
  this.gutters = gutters;
11011
11046
  }
11012
11047
  return change;
@@ -11015,15 +11050,18 @@ const gutterView = ViewPlugin.fromClass(class {
11015
11050
  for (let view of this.gutters)
11016
11051
  view.destroy();
11017
11052
  this.dom.remove();
11053
+ if (this.domAfter)
11054
+ this.domAfter.remove();
11018
11055
  }
11019
11056
  }, {
11020
11057
  provide: plugin => EditorView.scrollMargins.of(view => {
11021
11058
  let value = view.plugin(plugin);
11022
11059
  if (!value || value.gutters.length == 0 || !value.fixed)
11023
11060
  return null;
11061
+ let before = value.dom.offsetWidth * view.scaleX, after = value.domAfter ? value.domAfter.offsetWidth * view.scaleX : 0;
11024
11062
  return view.textDirection == exports.Direction.LTR
11025
- ? { left: value.dom.offsetWidth * view.scaleX }
11026
- : { right: value.dom.offsetWidth * view.scaleX };
11063
+ ? { left: before, right: after }
11064
+ : { right: before, left: after };
11027
11065
  })
11028
11066
  });
11029
11067
  function asArray(val) { return (Array.isArray(val) ? val : [val]); }
@@ -11265,7 +11303,8 @@ const lineNumberGutter = activeGutters.compute([lineNumberConfig], state => ({
11265
11303
  let max = formatNumber(update.view, maxLineNumber(update.view.state.doc.lines));
11266
11304
  return max == spacer.number ? spacer : new NumberMarker(max);
11267
11305
  },
11268
- domEventHandlers: state.facet(lineNumberConfig).domEventHandlers
11306
+ domEventHandlers: state.facet(lineNumberConfig).domEventHandlers,
11307
+ side: "before"
11269
11308
  }));
11270
11309
  /**
11271
11310
  Create a line number gutter extension.
package/dist/index.d.cts CHANGED
@@ -2235,6 +2235,12 @@ interface GutterConfig {
2235
2235
  Supply event handlers for DOM events on this gutter.
2236
2236
  */
2237
2237
  domEventHandlers?: Handlers;
2238
+ /**
2239
+ By default, gutters are shown horizontally before the editor
2240
+ content (to the left in a left-to-right layout). Set this to
2241
+ `"after"` to show a gutter on the other side of the content.
2242
+ */
2243
+ side?: "before" | "after";
2238
2244
  }
2239
2245
  /**
2240
2246
  Define an editor gutter. The order in which the gutters appear is
package/dist/index.d.ts CHANGED
@@ -2235,6 +2235,12 @@ interface GutterConfig {
2235
2235
  Supply event handlers for DOM events on this gutter.
2236
2236
  */
2237
2237
  domEventHandlers?: Handlers;
2238
+ /**
2239
+ By default, gutters are shown horizontally before the editor
2240
+ content (to the left in a left-to-right layout). Set this to
2241
+ `"after"` to show a gutter on the other side of the content.
2242
+ */
2243
+ side?: "before" | "after";
2238
2244
  }
2239
2245
  /**
2240
2246
  Define an editor gutter. The order in which the gutters appear is
package/dist/index.js CHANGED
@@ -6586,13 +6586,16 @@ const baseTheme$1 = /*@__PURE__*/buildTheme("." + baseThemeID, {
6586
6586
  display: "flex",
6587
6587
  height: "100%",
6588
6588
  boxSizing: "border-box",
6589
- insetInlineStart: 0,
6590
- zIndex: 200
6589
+ zIndex: 200,
6591
6590
  },
6591
+ ".cm-gutters-before": { insetInlineStart: 0 },
6592
+ ".cm-gutters-after": { insetInlineEnd: 0 },
6592
6593
  "&light .cm-gutters": {
6593
6594
  backgroundColor: "#f5f5f5",
6594
6595
  color: "#6c6c6c",
6595
- borderRight: "1px solid #ddd"
6596
+ border: "0px solid #ddd",
6597
+ "&.cm-gutters-before": { borderRightWidth: "1px" },
6598
+ "&.cm-gutters-after": { borderLeftWidth: "1px" },
6596
6599
  },
6597
6600
  "&dark .cm-gutters": {
6598
6601
  backgroundColor: "#333338",
@@ -6783,7 +6786,7 @@ class DOMObserver {
6783
6786
  else
6784
6787
  this.flush();
6785
6788
  });
6786
- if (window.EditContext && view.constructor.EDIT_CONTEXT !== false &&
6789
+ if (window.EditContext && browser.android && view.constructor.EDIT_CONTEXT !== false &&
6787
6790
  // Chrome <126 doesn't support inverted selections in edit context (#1392)
6788
6791
  !(browser.chrome && browser.chrome_version < 126)) {
6789
6792
  this.editContext = new EditContextManager(view);
@@ -10863,7 +10866,8 @@ const defaults = {
10863
10866
  lineMarkerChange: null,
10864
10867
  initialSpacer: null,
10865
10868
  updateSpacer: null,
10866
- domEventHandlers: {}
10869
+ domEventHandlers: {},
10870
+ side: "before"
10867
10871
  };
10868
10872
  const activeGutters = /*@__PURE__*/Facet.define();
10869
10873
  /**
@@ -10897,15 +10901,20 @@ function gutters(config) {
10897
10901
  const gutterView = /*@__PURE__*/ViewPlugin.fromClass(class {
10898
10902
  constructor(view) {
10899
10903
  this.view = view;
10904
+ this.domAfter = null;
10900
10905
  this.prevViewport = view.viewport;
10901
10906
  this.dom = document.createElement("div");
10902
- this.dom.className = "cm-gutters";
10907
+ this.dom.className = "cm-gutters cm-gutters-before";
10903
10908
  this.dom.setAttribute("aria-hidden", "true");
10904
10909
  this.dom.style.minHeight = (this.view.contentHeight / this.view.scaleY) + "px";
10905
10910
  this.gutters = view.state.facet(activeGutters).map(conf => new SingleGutterView(view, conf));
10906
- for (let gutter of this.gutters)
10907
- this.dom.appendChild(gutter.dom);
10908
10911
  this.fixed = !view.state.facet(unfixGutters);
10912
+ for (let gutter of this.gutters) {
10913
+ if (gutter.config.side == "after")
10914
+ this.getDOMAfter().appendChild(gutter.dom);
10915
+ else
10916
+ this.dom.appendChild(gutter.dom);
10917
+ }
10909
10918
  if (this.fixed) {
10910
10919
  // FIXME IE11 fallback, which doesn't support position: sticky,
10911
10920
  // by using position: relative + event handlers that realign the
@@ -10915,6 +10924,17 @@ const gutterView = /*@__PURE__*/ViewPlugin.fromClass(class {
10915
10924
  this.syncGutters(false);
10916
10925
  view.scrollDOM.insertBefore(this.dom, view.contentDOM);
10917
10926
  }
10927
+ getDOMAfter() {
10928
+ if (!this.domAfter) {
10929
+ this.domAfter = document.createElement("div");
10930
+ this.domAfter.className = "cm-gutters cm-gutters-after";
10931
+ this.domAfter.setAttribute("aria-hidden", "true");
10932
+ this.domAfter.style.minHeight = (this.view.contentHeight / this.view.scaleY) + "px";
10933
+ this.domAfter.style.position = this.fixed ? "sticky" : "";
10934
+ this.view.scrollDOM.appendChild(this.domAfter);
10935
+ }
10936
+ return this.domAfter;
10937
+ }
10918
10938
  update(update) {
10919
10939
  if (this.updateGutters(update)) {
10920
10940
  // Detach during sync when the viewport changed significantly
@@ -10925,18 +10945,26 @@ const gutterView = /*@__PURE__*/ViewPlugin.fromClass(class {
10925
10945
  this.syncGutters(vpOverlap < (vpB.to - vpB.from) * 0.8);
10926
10946
  }
10927
10947
  if (update.geometryChanged) {
10928
- this.dom.style.minHeight = (this.view.contentHeight / this.view.scaleY) + "px";
10948
+ let min = (this.view.contentHeight / this.view.scaleY) + "px";
10949
+ this.dom.style.minHeight = min;
10950
+ if (this.domAfter)
10951
+ this.domAfter.style.minHeight = min;
10929
10952
  }
10930
10953
  if (this.view.state.facet(unfixGutters) != !this.fixed) {
10931
10954
  this.fixed = !this.fixed;
10932
10955
  this.dom.style.position = this.fixed ? "sticky" : "";
10956
+ if (this.domAfter)
10957
+ this.domAfter.style.position = this.fixed ? "sticky" : "";
10933
10958
  }
10934
10959
  this.prevViewport = update.view.viewport;
10935
10960
  }
10936
10961
  syncGutters(detach) {
10937
10962
  let after = this.dom.nextSibling;
10938
- if (detach)
10963
+ if (detach) {
10939
10964
  this.dom.remove();
10965
+ if (this.domAfter)
10966
+ this.domAfter.remove();
10967
+ }
10940
10968
  let lineClasses = RangeSet.iter(this.view.state.facet(gutterLineClass), this.view.viewport.from);
10941
10969
  let classSet = [];
10942
10970
  let contexts = this.gutters.map(gutter => new UpdateContext(gutter, this.view.viewport, -this.view.documentPadding.top));
@@ -10970,8 +10998,11 @@ const gutterView = /*@__PURE__*/ViewPlugin.fromClass(class {
10970
10998
  }
10971
10999
  for (let cx of contexts)
10972
11000
  cx.finish();
10973
- if (detach)
11001
+ if (detach) {
10974
11002
  this.view.scrollDOM.insertBefore(this.dom, after);
11003
+ if (this.domAfter)
11004
+ this.view.scrollDOM.appendChild(this.domAfter);
11005
+ }
10975
11006
  }
10976
11007
  updateGutters(update) {
10977
11008
  let prev = update.startState.facet(activeGutters), cur = update.state.facet(activeGutters);
@@ -11000,8 +11031,12 @@ const gutterView = /*@__PURE__*/ViewPlugin.fromClass(class {
11000
11031
  if (gutters.indexOf(g) < 0)
11001
11032
  g.destroy();
11002
11033
  }
11003
- for (let g of gutters)
11004
- this.dom.appendChild(g.dom);
11034
+ for (let g of gutters) {
11035
+ if (g.config.side == "after")
11036
+ this.getDOMAfter().appendChild(g.dom);
11037
+ else
11038
+ this.dom.appendChild(g.dom);
11039
+ }
11005
11040
  this.gutters = gutters;
11006
11041
  }
11007
11042
  return change;
@@ -11010,15 +11045,18 @@ const gutterView = /*@__PURE__*/ViewPlugin.fromClass(class {
11010
11045
  for (let view of this.gutters)
11011
11046
  view.destroy();
11012
11047
  this.dom.remove();
11048
+ if (this.domAfter)
11049
+ this.domAfter.remove();
11013
11050
  }
11014
11051
  }, {
11015
11052
  provide: plugin => EditorView.scrollMargins.of(view => {
11016
11053
  let value = view.plugin(plugin);
11017
11054
  if (!value || value.gutters.length == 0 || !value.fixed)
11018
11055
  return null;
11056
+ let before = value.dom.offsetWidth * view.scaleX, after = value.domAfter ? value.domAfter.offsetWidth * view.scaleX : 0;
11019
11057
  return view.textDirection == Direction.LTR
11020
- ? { left: value.dom.offsetWidth * view.scaleX }
11021
- : { right: value.dom.offsetWidth * view.scaleX };
11058
+ ? { left: before, right: after }
11059
+ : { right: before, left: after };
11022
11060
  })
11023
11061
  });
11024
11062
  function asArray(val) { return (Array.isArray(val) ? val : [val]); }
@@ -11260,7 +11298,8 @@ const lineNumberGutter = /*@__PURE__*/activeGutters.compute([lineNumberConfig],
11260
11298
  let max = formatNumber(update.view, maxLineNumber(update.view.state.doc.lines));
11261
11299
  return max == spacer.number ? spacer : new NumberMarker(max);
11262
11300
  },
11263
- domEventHandlers: state.facet(lineNumberConfig).domEventHandlers
11301
+ domEventHandlers: state.facet(lineNumberConfig).domEventHandlers,
11302
+ side: "before"
11264
11303
  }));
11265
11304
  /**
11266
11305
  Create a line number gutter extension.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/view",
3
- "version": "6.37.2",
3
+ "version": "6.38.0",
4
4
  "description": "DOM view component for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",