@codemirror/view 6.17.1 → 6.18.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,11 @@
1
+ ## 6.18.0 (2023-09-05)
2
+
3
+ ### New features
4
+
5
+ The new `EditorView.scaleX` and `scaleY` properties return the CSS-transformed scale of the editor (or 1 when not scaled).
6
+
7
+ The editor now supports being scaled with CSS.
8
+
1
9
  ## 6.17.1 (2023-08-31)
2
10
 
3
11
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -101,6 +101,7 @@ function scrollRectIntoView(dom, rect, side, x, y, xMargin, yMargin, ltr) {
101
101
  for (let cur = dom, stop = false; cur && !stop;) {
102
102
  if (cur.nodeType == 1) { // Element
103
103
  let bounding, top = cur == doc.body;
104
+ let scaleX = 1, scaleY = 1;
104
105
  if (top) {
105
106
  bounding = windowRect(win);
106
107
  }
@@ -112,9 +113,11 @@ function scrollRectIntoView(dom, rect, side, x, y, xMargin, yMargin, ltr) {
112
113
  continue;
113
114
  }
114
115
  let rect = cur.getBoundingClientRect();
116
+ scaleX = rect.width / cur.offsetWidth;
117
+ scaleY = rect.height / cur.offsetHeight;
115
118
  // Make sure scrollbar width isn't included in the rectangle
116
- bounding = { left: rect.left, right: rect.left + cur.clientWidth,
117
- top: rect.top, bottom: rect.top + cur.clientHeight };
119
+ bounding = { left: rect.left, right: rect.left + cur.clientWidth * scaleX,
120
+ top: rect.top, bottom: rect.top + cur.clientHeight * scaleY };
118
121
  }
119
122
  let moveX = 0, moveY = 0;
120
123
  if (y == "nearest") {
@@ -162,13 +165,13 @@ function scrollRectIntoView(dom, rect, side, x, y, xMargin, yMargin, ltr) {
162
165
  let movedX = 0, movedY = 0;
163
166
  if (moveY) {
164
167
  let start = cur.scrollTop;
165
- cur.scrollTop += moveY;
166
- movedY = cur.scrollTop - start;
168
+ cur.scrollTop += moveY / scaleY;
169
+ movedY = (cur.scrollTop - start) * scaleY;
167
170
  }
168
171
  if (moveX) {
169
172
  let start = cur.scrollLeft;
170
- cur.scrollLeft += moveX;
171
- movedX = cur.scrollLeft - start;
173
+ cur.scrollLeft += moveX / scaleX;
174
+ movedX = (cur.scrollLeft - start) * scaleX;
172
175
  }
173
176
  rect = { left: rect.left - movedX, top: rect.top - movedY,
174
177
  right: rect.right - movedX, bottom: rect.bottom - movedY };
@@ -2781,7 +2784,7 @@ class DocView extends ContentView {
2781
2784
  // messes with the scroll position during DOM mutation (though
2782
2785
  // no relayout is triggered and I cannot imagine how it can
2783
2786
  // recompute the scroll position without a layout)
2784
- this.dom.style.height = this.view.viewState.contentHeight + "px";
2787
+ this.dom.style.height = this.view.viewState.contentHeight / this.view.scaleY + "px";
2785
2788
  this.dom.style.flexBasis = this.minWidth ? this.minWidth + "px" : "";
2786
2789
  // Chrome will sometimes, when DOM mutations occur directly
2787
2790
  // around the selection, get confused and report a different
@@ -2853,7 +2856,7 @@ class DocView extends ContentView {
2853
2856
  }
2854
2857
  fixCompositionDOM(composition) {
2855
2858
  let fix = (dom, cView) => {
2856
- cView.flags |= 8 /* ViewFlag.Composition */;
2859
+ cView.flags |= 8 /* ViewFlag.Composition */ | (cView.children.some(c => c.flags & 7 /* ViewFlag.Dirty */) ? 1 /* ViewFlag.ChildDirty */ : 0);
2857
2860
  this.markedForComposition.add(cView);
2858
2861
  let prev = ContentView.get(dom);
2859
2862
  if (prev != cView) {
@@ -3119,7 +3122,7 @@ class DocView extends ContentView {
3119
3122
  let next = i == vs.viewports.length ? null : vs.viewports[i];
3120
3123
  let end = next ? next.from - 1 : this.length;
3121
3124
  if (end > pos) {
3122
- let height = vs.lineBlockAt(end).bottom - vs.lineBlockAt(pos).top;
3125
+ let height = (vs.lineBlockAt(end).bottom - vs.lineBlockAt(pos).top) / this.view.scaleY;
3123
3126
  deco.push(Decoration.replace({
3124
3127
  widget: new BlockGapWidget(height),
3125
3128
  block: true,
@@ -5276,8 +5279,10 @@ class LineGap {
5276
5279
  }
5277
5280
  return true;
5278
5281
  }
5279
- draw(wrapping) {
5280
- return Decoration.replace({ widget: new LineGapWidget(this.size, wrapping) }).range(this.from, this.to);
5282
+ draw(viewState, wrapping) {
5283
+ return Decoration.replace({
5284
+ widget: new LineGapWidget(this.size * (wrapping ? viewState.scaleY : viewState.scaleX), wrapping)
5285
+ }).range(this.from, this.to);
5281
5286
  }
5282
5287
  }
5283
5288
  class LineGapWidget extends WidgetType {
@@ -5307,14 +5312,18 @@ class ViewState {
5307
5312
  // These are contentDOM-local coordinates
5308
5313
  this.pixelViewport = { left: 0, right: window.innerWidth, top: 0, bottom: 0 };
5309
5314
  this.inView = true;
5310
- this.paddingTop = 0;
5311
- this.paddingBottom = 0;
5312
- this.contentDOMWidth = 0;
5313
- this.contentDOMHeight = 0;
5314
- this.editorHeight = 0;
5315
- this.editorWidth = 0;
5316
- this.scrollTop = 0;
5315
+ this.paddingTop = 0; // Padding above the document, scaled
5316
+ this.paddingBottom = 0; // Padding below the document, scaled
5317
+ this.contentDOMWidth = 0; // contentDOM.getBoundingClientRect().width
5318
+ this.contentDOMHeight = 0; // contentDOM.getBoundingClientRect().height
5319
+ this.editorHeight = 0; // scrollDOM.clientHeight, unscaled
5320
+ this.editorWidth = 0; // scrollDOM.clientWidth, unscaled
5321
+ this.scrollTop = 0; // Last seen scrollDOM.scrollTop, scaled
5317
5322
  this.scrolledToBottom = true;
5323
+ // The CSS-transformation scale of the editor (transformed size /
5324
+ // concrete size)
5325
+ this.scaleX = 1;
5326
+ this.scaleY = 1;
5318
5327
  // The vertical position (document-relative) to which to anchor the
5319
5328
  // scroll position. -1 means anchor to the end of the document.
5320
5329
  this.scrollAnchorPos = 0;
@@ -5348,7 +5357,7 @@ class ViewState {
5348
5357
  this.updateViewportLines();
5349
5358
  this.updateForViewport();
5350
5359
  this.lineGaps = this.ensureLineGaps([]);
5351
- this.lineGapDeco = Decoration.set(this.lineGaps.map(gap => gap.draw(false)));
5360
+ this.lineGapDeco = Decoration.set(this.lineGaps.map(gap => gap.draw(this, false)));
5352
5361
  this.computeVisibleRanges();
5353
5362
  }
5354
5363
  updateForViewport() {
@@ -5420,8 +5429,23 @@ class ViewState {
5420
5429
  this.contentDOMHeight = domRect.height;
5421
5430
  this.mustMeasureContent = false;
5422
5431
  let result = 0, bias = 0;
5432
+ if (domRect.width && domRect.height) {
5433
+ let scaleX = domRect.width / dom.offsetWidth;
5434
+ let scaleY = domRect.height / dom.offsetHeight;
5435
+ if (scaleX > 0.995 && scaleX < 1.005)
5436
+ scaleX = 1;
5437
+ if (scaleY > 0.995 && scaleY < 1.005)
5438
+ scaleY = 1;
5439
+ if (this.scaleX != scaleX || this.scaleY != scaleY) {
5440
+ this.scaleX = scaleX;
5441
+ this.scaleY = scaleY;
5442
+ result |= 8 /* UpdateFlag.Geometry */;
5443
+ refresh = measureContent = true;
5444
+ }
5445
+ }
5423
5446
  // Vertical padding
5424
- let paddingTop = parseInt(style.paddingTop) || 0, paddingBottom = parseInt(style.paddingBottom) || 0;
5447
+ let paddingTop = (parseInt(style.paddingTop) || 0) * this.scaleY;
5448
+ let paddingBottom = (parseInt(style.paddingBottom) || 0) * this.scaleY;
5425
5449
  if (this.paddingTop != paddingTop || this.paddingBottom != paddingBottom) {
5426
5450
  this.paddingTop = paddingTop;
5427
5451
  this.paddingBottom = paddingBottom;
@@ -5433,9 +5457,10 @@ class ViewState {
5433
5457
  this.editorWidth = view.scrollDOM.clientWidth;
5434
5458
  result |= 8 /* UpdateFlag.Geometry */;
5435
5459
  }
5436
- if (this.scrollTop != view.scrollDOM.scrollTop) {
5460
+ let scrollTop = view.scrollDOM.scrollTop * this.scaleY;
5461
+ if (this.scrollTop != scrollTop) {
5437
5462
  this.scrollAnchorHeight = -1;
5438
- this.scrollTop = view.scrollDOM.scrollTop;
5463
+ this.scrollTop = scrollTop;
5439
5464
  }
5440
5465
  this.scrolledToBottom = isScrolledToBottom(view.scrollDOM);
5441
5466
  // Pixel viewport
@@ -5656,7 +5681,7 @@ class ViewState {
5656
5681
  updateLineGaps(gaps) {
5657
5682
  if (!LineGap.same(gaps, this.lineGaps)) {
5658
5683
  this.lineGaps = gaps;
5659
- this.lineGapDeco = Decoration.set(gaps.map(gap => gap.draw(this.heightOracle.lineWrapping)));
5684
+ this.lineGapDeco = Decoration.set(gaps.map(gap => gap.draw(this, this.heightOracle.lineWrapping)));
5660
5685
  }
5661
5686
  }
5662
5687
  computeVisibleRanges() {
@@ -7067,9 +7092,9 @@ class EditorView {
7067
7092
  if (flush)
7068
7093
  this.observer.forceFlush();
7069
7094
  let updated = null;
7070
- let sDOM = this.scrollDOM, { scrollTop } = sDOM;
7095
+ let sDOM = this.scrollDOM, scrollTop = sDOM.scrollTop * this.scaleY;
7071
7096
  let { scrollAnchorPos, scrollAnchorHeight } = this.viewState;
7072
- if (scrollTop != this.viewState.scrollTop)
7097
+ if (Math.abs(scrollTop - this.viewState.scrollTop) > 1)
7073
7098
  scrollAnchorHeight = -1;
7074
7099
  this.viewState.scrollAnchorHeight = -1;
7075
7100
  try {
@@ -7146,7 +7171,8 @@ class EditorView {
7146
7171
  this.viewState.lineBlockAt(scrollAnchorPos).top;
7147
7172
  let diff = newAnchorHeight - scrollAnchorHeight;
7148
7173
  if (diff > 1 || diff < -1) {
7149
- scrollTop = sDOM.scrollTop = scrollTop + diff;
7174
+ scrollTop = scrollTop + diff;
7175
+ sDOM.scrollTop = scrollTop / this.scaleY;
7150
7176
  scrollAnchorHeight = -1;
7151
7177
  continue;
7152
7178
  }
@@ -7273,6 +7299,16 @@ class EditorView {
7273
7299
  return { top: this.viewState.paddingTop, bottom: this.viewState.paddingBottom };
7274
7300
  }
7275
7301
  /**
7302
+ If the editor is transformed with CSS, this provides the scale
7303
+ along the X axis. Otherwise, it will just be 1. Note that
7304
+ transforms other than translation and scaling are not supported.
7305
+ */
7306
+ get scaleX() { return this.viewState.scaleX; }
7307
+ /**
7308
+ Provide the CSS transformed scale along the Y axis.
7309
+ */
7310
+ get scaleY() { return this.viewState.scaleY; }
7311
+ /**
7276
7312
  Find the text line or block widget at the given vertical
7277
7313
  position (which is interpreted as relative to the [top of the
7278
7314
  document](https://codemirror.net/6/docs/ref/#view.EditorView.documentTop)).
@@ -8063,8 +8099,8 @@ class RectangleMarker {
8063
8099
  }
8064
8100
  function getBase(view) {
8065
8101
  let rect = view.scrollDOM.getBoundingClientRect();
8066
- let left = view.textDirection == exports.Direction.LTR ? rect.left : rect.right - view.scrollDOM.clientWidth;
8067
- return { left: left - view.scrollDOM.scrollLeft, top: rect.top - view.scrollDOM.scrollTop };
8102
+ let left = view.textDirection == exports.Direction.LTR ? rect.left : rect.right - view.scrollDOM.clientWidth * view.scaleX;
8103
+ return { left: left - view.scrollDOM.scrollLeft * view.scaleX, top: rect.top - view.scrollDOM.scrollTop * view.scaleY };
8068
8104
  }
8069
8105
  function wrappedLine(view, pos, inside) {
8070
8106
  let range = state.EditorSelection.cursor(pos);
@@ -8166,6 +8202,8 @@ class LayerView {
8166
8202
  this.view = view;
8167
8203
  this.layer = layer;
8168
8204
  this.drawn = [];
8205
+ this.scaleX = 1;
8206
+ this.scaleY = 1;
8169
8207
  this.measureReq = { read: this.measure.bind(this), write: this.draw.bind(this) };
8170
8208
  this.dom = view.scrollDOM.appendChild(document.createElement("div"));
8171
8209
  this.dom.classList.add("cm-layer");
@@ -8173,6 +8211,7 @@ class LayerView {
8173
8211
  this.dom.classList.add("cm-layer-above");
8174
8212
  if (layer.class)
8175
8213
  this.dom.classList.add(layer.class);
8214
+ this.scale();
8176
8215
  this.dom.setAttribute("aria-hidden", "true");
8177
8216
  this.setOrder(view.state);
8178
8217
  view.requestMeasure(this.measureReq);
@@ -8182,8 +8221,10 @@ class LayerView {
8182
8221
  update(update) {
8183
8222
  if (update.startState.facet(layerOrder) != update.state.facet(layerOrder))
8184
8223
  this.setOrder(update.state);
8185
- if (this.layer.update(update, this.dom) || update.geometryChanged)
8224
+ if (this.layer.update(update, this.dom) || update.geometryChanged) {
8225
+ this.scale();
8186
8226
  update.view.requestMeasure(this.measureReq);
8227
+ }
8187
8228
  }
8188
8229
  setOrder(state) {
8189
8230
  let pos = 0, order = state.facet(layerOrder);
@@ -8194,6 +8235,14 @@ class LayerView {
8194
8235
  measure() {
8195
8236
  return this.layer.markers(this.view);
8196
8237
  }
8238
+ scale() {
8239
+ let { scaleX, scaleY } = this.view;
8240
+ if (scaleX != this.scaleX || scaleY != this.scaleY) {
8241
+ this.scaleX = scaleX;
8242
+ this.scaleY = scaleY;
8243
+ this.dom.style.transform = `scale(${1 / scaleX}, ${1 / scaleY})`;
8244
+ }
8245
+ }
8197
8246
  draw(markers) {
8198
8247
  if (markers.length != this.drawn.length || markers.some((p, i) => !sameMarker(p, this.drawn[i]))) {
8199
8248
  let old = this.dom.firstChild, oldI = 0;
@@ -8363,23 +8412,25 @@ const drawDropCursor = ViewPlugin.fromClass(class {
8363
8412
  }
8364
8413
  }
8365
8414
  readPos() {
8366
- let pos = this.view.state.field(dropCursorPos);
8367
- let rect = pos != null && this.view.coordsAtPos(pos);
8415
+ let { view } = this;
8416
+ let pos = view.state.field(dropCursorPos);
8417
+ let rect = pos != null && view.coordsAtPos(pos);
8368
8418
  if (!rect)
8369
8419
  return null;
8370
- let outer = this.view.scrollDOM.getBoundingClientRect();
8420
+ let outer = view.scrollDOM.getBoundingClientRect();
8371
8421
  return {
8372
- left: rect.left - outer.left + this.view.scrollDOM.scrollLeft,
8373
- top: rect.top - outer.top + this.view.scrollDOM.scrollTop,
8422
+ left: rect.left - outer.left + view.scrollDOM.scrollLeft * view.scaleX,
8423
+ top: rect.top - outer.top + view.scrollDOM.scrollTop * view.scaleY,
8374
8424
  height: rect.bottom - rect.top
8375
8425
  };
8376
8426
  }
8377
8427
  drawCursor(pos) {
8378
8428
  if (this.cursor) {
8429
+ let { scaleX, scaleY } = this.view;
8379
8430
  if (pos) {
8380
- this.cursor.style.left = pos.left + "px";
8381
- this.cursor.style.top = pos.top + "px";
8382
- this.cursor.style.height = pos.height + "px";
8431
+ this.cursor.style.left = pos.left / scaleX + "px";
8432
+ this.cursor.style.top = pos.top / scaleY + "px";
8433
+ this.cursor.style.height = pos.height / scaleY + "px";
8383
8434
  }
8384
8435
  else {
8385
8436
  this.cursor.style.left = "-100000px";
@@ -8621,7 +8672,9 @@ function specialCharPlugin() {
8621
8672
  if (code == 9) {
8622
8673
  let line = doc.lineAt(pos);
8623
8674
  let size = view.state.tabSize, col = state.countColumn(line.text, size, pos - line.from);
8624
- return Decoration.replace({ widget: new TabWidget((size - (col % size)) * this.view.defaultCharacterWidth) });
8675
+ return Decoration.replace({
8676
+ widget: new TabWidget((size - (col % size)) * this.view.defaultCharacterWidth / this.view.scaleX)
8677
+ });
8625
8678
  }
8626
8679
  return this.decorationCache[code] ||
8627
8680
  (this.decorationCache[code] = Decoration.replace({ widget: new SpecialCharWidget(conf, code) }));
@@ -8698,7 +8751,8 @@ const plugin = ViewPlugin.fromClass(class {
8698
8751
  }
8699
8752
  update(update) {
8700
8753
  let { view } = update;
8701
- let height = view.viewState.editorHeight - view.defaultLineHeight - view.documentPadding.top - 0.5;
8754
+ let height = view.viewState.editorHeight * view.scaleY -
8755
+ view.defaultLineHeight - view.documentPadding.top - 0.5;
8702
8756
  if (height >= 0 && height != this.height) {
8703
8757
  this.height = height;
8704
8758
  this.attrs = { style: `padding-bottom: ${height}px` };
@@ -8995,6 +9049,7 @@ const tooltipPlugin = ViewPlugin.fromClass(class {
8995
9049
  constructor(view) {
8996
9050
  this.view = view;
8997
9051
  this.inView = true;
9052
+ this.madeAbsolute = false;
8998
9053
  this.lastTransaction = 0;
8999
9054
  this.measureTimeout = -1;
9000
9055
  let config = view.state.facet(tooltipConfig);
@@ -9046,7 +9101,7 @@ const tooltipPlugin = ViewPlugin.fromClass(class {
9046
9101
  this.observeIntersection();
9047
9102
  let shouldMeasure = updated || update.geometryChanged;
9048
9103
  let newConfig = update.state.facet(tooltipConfig);
9049
- if (newConfig.position != this.position) {
9104
+ if (newConfig.position != this.position && !this.madeAbsolute) {
9050
9105
  this.position = newConfig.position;
9051
9106
  for (let t of this.manager.tooltipViews)
9052
9107
  t.dom.style.position = this.position;
@@ -9094,6 +9149,27 @@ const tooltipPlugin = ViewPlugin.fromClass(class {
9094
9149
  }
9095
9150
  readMeasure() {
9096
9151
  let editor = this.view.dom.getBoundingClientRect();
9152
+ let scaleX = 1, scaleY = 1, makeAbsolute = false;
9153
+ if (this.position == "fixed") {
9154
+ let views = this.manager.tooltipViews;
9155
+ // When the dialog's offset parent isn't the body, we are
9156
+ // probably in a transformed container, and should use absolute
9157
+ // positioning instead, since fixed positioning inside a
9158
+ // transform works in a very broken way.
9159
+ makeAbsolute = views.length > 0 && views[0].dom.offsetParent != this.container.ownerDocument.body;
9160
+ }
9161
+ if (makeAbsolute || this.position == "absolute") {
9162
+ if (this.parent) {
9163
+ let rect = this.parent.getBoundingClientRect();
9164
+ if (rect.width && rect.height) {
9165
+ scaleX = rect.width / this.parent.offsetWidth;
9166
+ scaleY = rect.height / this.parent.offsetHeight;
9167
+ }
9168
+ }
9169
+ else {
9170
+ ({ scaleX, scaleY } = this.view.viewState);
9171
+ }
9172
+ }
9097
9173
  return {
9098
9174
  editor,
9099
9175
  parent: this.parent ? this.container.getBoundingClientRect() : editor,
@@ -9103,11 +9179,18 @@ const tooltipPlugin = ViewPlugin.fromClass(class {
9103
9179
  }),
9104
9180
  size: this.manager.tooltipViews.map(({ dom }) => dom.getBoundingClientRect()),
9105
9181
  space: this.view.state.facet(tooltipConfig).tooltipSpace(this.view),
9182
+ scaleX, scaleY, makeAbsolute
9106
9183
  };
9107
9184
  }
9108
9185
  writeMeasure(measured) {
9109
9186
  var _a;
9110
- let { editor, space } = measured;
9187
+ if (measured.makeAbsolute) {
9188
+ this.madeAbsolute = true;
9189
+ this.position = "absolute";
9190
+ for (let t of this.manager.tooltipViews)
9191
+ t.dom.style.position = "absolute";
9192
+ }
9193
+ let { editor, space, scaleX, scaleY } = measured;
9111
9194
  let others = [];
9112
9195
  for (let i = 0; i < this.manager.tooltips.length; i++) {
9113
9196
  let tooltip = this.manager.tooltips[i], tView = this.manager.tooltipViews[i], { dom } = tView;
@@ -9140,7 +9223,7 @@ const tooltipPlugin = ViewPlugin.fromClass(class {
9140
9223
  continue;
9141
9224
  }
9142
9225
  knownHeight.set(tView, height);
9143
- dom.style.height = (height = spaceVert) + "px";
9226
+ dom.style.height = (height = spaceVert) / scaleY + "px";
9144
9227
  }
9145
9228
  else if (dom.style.height) {
9146
9229
  dom.style.height = "";
@@ -9152,15 +9235,17 @@ const tooltipPlugin = ViewPlugin.fromClass(class {
9152
9235
  if (r.left < right && r.right > left && r.top < top + height && r.bottom > top)
9153
9236
  top = above ? r.top - height - 2 - arrowHeight : r.bottom + arrowHeight + 2;
9154
9237
  if (this.position == "absolute") {
9155
- dom.style.top = (top - measured.parent.top) + "px";
9156
- dom.style.left = (left - measured.parent.left) + "px";
9238
+ dom.style.top = (top - measured.parent.top) / scaleY + "px";
9239
+ dom.style.left = (left - measured.parent.left) / scaleX + "px";
9157
9240
  }
9158
9241
  else {
9159
- dom.style.top = top + "px";
9160
- dom.style.left = left + "px";
9242
+ dom.style.top = top / scaleY + "px";
9243
+ dom.style.left = left / scaleX + "px";
9244
+ }
9245
+ if (arrow) {
9246
+ let arrowLeft = pos.left + (ltr ? offset.x : -offset.x) - (left + 14 /* Arrow.Offset */ - 7 /* Arrow.Size */);
9247
+ arrow.style.left = arrowLeft / scaleX + "px";
9161
9248
  }
9162
- if (arrow)
9163
- arrow.style.left = `${pos.left + (ltr ? offset.x : -offset.x) - (left + 14 /* Arrow.Offset */ - 7 /* Arrow.Size */)}px`;
9164
9249
  if (tView.overlap !== true)
9165
9250
  others.push({ left, top, right, bottom: top + height });
9166
9251
  dom.classList.toggle("cm-tooltip-above", above);
@@ -9762,7 +9847,7 @@ const gutterView = ViewPlugin.fromClass(class {
9762
9847
  this.dom = document.createElement("div");
9763
9848
  this.dom.className = "cm-gutters";
9764
9849
  this.dom.setAttribute("aria-hidden", "true");
9765
- this.dom.style.minHeight = this.view.contentHeight + "px";
9850
+ this.dom.style.minHeight = (this.view.contentHeight / this.view.scaleY) + "px";
9766
9851
  this.gutters = view.state.facet(activeGutters).map(conf => new SingleGutterView(view, conf));
9767
9852
  for (let gutter of this.gutters)
9768
9853
  this.dom.appendChild(gutter.dom);
@@ -9872,7 +9957,9 @@ const gutterView = ViewPlugin.fromClass(class {
9872
9957
  let value = view.plugin(plugin);
9873
9958
  if (!value || value.gutters.length == 0 || !value.fixed)
9874
9959
  return null;
9875
- return view.textDirection == exports.Direction.LTR ? { left: value.dom.offsetWidth } : { right: value.dom.offsetWidth };
9960
+ return view.textDirection == exports.Direction.LTR
9961
+ ? { left: value.dom.offsetWidth * view.scaleX }
9962
+ : { right: value.dom.offsetWidth * view.scaleX };
9876
9963
  })
9877
9964
  });
9878
9965
  function asArray(val) { return (Array.isArray(val) ? val : [val]); }
@@ -9989,10 +10076,12 @@ class GutterElement {
9989
10076
  this.update(view, height, above, markers);
9990
10077
  }
9991
10078
  update(view, height, above, markers) {
9992
- if (this.height != height)
9993
- this.dom.style.height = (this.height = height) + "px";
10079
+ if (this.height != height) {
10080
+ this.height = height;
10081
+ this.dom.style.height = height / view.scaleY + "px";
10082
+ }
9994
10083
  if (this.above != above)
9995
- this.dom.style.marginTop = (this.above = above) ? above + "px" : "";
10084
+ this.dom.style.marginTop = (this.above = above) ? above / view.scaleY + "px" : "";
9996
10085
  if (!sameMarkers(this.markers, markers))
9997
10086
  this.setMarkers(view, markers);
9998
10087
  }
package/dist/index.d.cts CHANGED
@@ -802,6 +802,16 @@ declare class EditorView {
802
802
  bottom: number;
803
803
  };
804
804
  /**
805
+ If the editor is transformed with CSS, this provides the scale
806
+ along the X axis. Otherwise, it will just be 1. Note that
807
+ transforms other than translation and scaling are not supported.
808
+ */
809
+ get scaleX(): number;
810
+ /**
811
+ Provide the CSS transformed scale along the Y axis.
812
+ */
813
+ get scaleY(): number;
814
+ /**
805
815
  Find the text line or block widget at the given vertical
806
816
  position (which is interpreted as relative to the [top of the
807
817
  document](https://codemirror.net/6/docs/ref/#view.EditorView.documentTop)).
@@ -1653,6 +1663,9 @@ declare function tooltips(config?: {
1653
1663
  On iOS, which at the time of writing still doesn't properly
1654
1664
  support fixed positioning, the library always uses absolute
1655
1665
  positioning.
1666
+
1667
+ If the tooltip parent element sits in a transformed element, the
1668
+ library also falls back to absolute positioning.
1656
1669
  */
1657
1670
  position?: "fixed" | "absolute";
1658
1671
  /**
package/dist/index.d.ts CHANGED
@@ -802,6 +802,16 @@ declare class EditorView {
802
802
  bottom: number;
803
803
  };
804
804
  /**
805
+ If the editor is transformed with CSS, this provides the scale
806
+ along the X axis. Otherwise, it will just be 1. Note that
807
+ transforms other than translation and scaling are not supported.
808
+ */
809
+ get scaleX(): number;
810
+ /**
811
+ Provide the CSS transformed scale along the Y axis.
812
+ */
813
+ get scaleY(): number;
814
+ /**
805
815
  Find the text line or block widget at the given vertical
806
816
  position (which is interpreted as relative to the [top of the
807
817
  document](https://codemirror.net/6/docs/ref/#view.EditorView.documentTop)).
@@ -1653,6 +1663,9 @@ declare function tooltips(config?: {
1653
1663
  On iOS, which at the time of writing still doesn't properly
1654
1664
  support fixed positioning, the library always uses absolute
1655
1665
  positioning.
1666
+
1667
+ If the tooltip parent element sits in a transformed element, the
1668
+ library also falls back to absolute positioning.
1656
1669
  */
1657
1670
  position?: "fixed" | "absolute";
1658
1671
  /**
package/dist/index.js CHANGED
@@ -99,6 +99,7 @@ function scrollRectIntoView(dom, rect, side, x, y, xMargin, yMargin, ltr) {
99
99
  for (let cur = dom, stop = false; cur && !stop;) {
100
100
  if (cur.nodeType == 1) { // Element
101
101
  let bounding, top = cur == doc.body;
102
+ let scaleX = 1, scaleY = 1;
102
103
  if (top) {
103
104
  bounding = windowRect(win);
104
105
  }
@@ -110,9 +111,11 @@ function scrollRectIntoView(dom, rect, side, x, y, xMargin, yMargin, ltr) {
110
111
  continue;
111
112
  }
112
113
  let rect = cur.getBoundingClientRect();
114
+ scaleX = rect.width / cur.offsetWidth;
115
+ scaleY = rect.height / cur.offsetHeight;
113
116
  // Make sure scrollbar width isn't included in the rectangle
114
- bounding = { left: rect.left, right: rect.left + cur.clientWidth,
115
- top: rect.top, bottom: rect.top + cur.clientHeight };
117
+ bounding = { left: rect.left, right: rect.left + cur.clientWidth * scaleX,
118
+ top: rect.top, bottom: rect.top + cur.clientHeight * scaleY };
116
119
  }
117
120
  let moveX = 0, moveY = 0;
118
121
  if (y == "nearest") {
@@ -160,13 +163,13 @@ function scrollRectIntoView(dom, rect, side, x, y, xMargin, yMargin, ltr) {
160
163
  let movedX = 0, movedY = 0;
161
164
  if (moveY) {
162
165
  let start = cur.scrollTop;
163
- cur.scrollTop += moveY;
164
- movedY = cur.scrollTop - start;
166
+ cur.scrollTop += moveY / scaleY;
167
+ movedY = (cur.scrollTop - start) * scaleY;
165
168
  }
166
169
  if (moveX) {
167
170
  let start = cur.scrollLeft;
168
- cur.scrollLeft += moveX;
169
- movedX = cur.scrollLeft - start;
171
+ cur.scrollLeft += moveX / scaleX;
172
+ movedX = (cur.scrollLeft - start) * scaleX;
170
173
  }
171
174
  rect = { left: rect.left - movedX, top: rect.top - movedY,
172
175
  right: rect.right - movedX, bottom: rect.bottom - movedY };
@@ -2777,7 +2780,7 @@ class DocView extends ContentView {
2777
2780
  // messes with the scroll position during DOM mutation (though
2778
2781
  // no relayout is triggered and I cannot imagine how it can
2779
2782
  // recompute the scroll position without a layout)
2780
- this.dom.style.height = this.view.viewState.contentHeight + "px";
2783
+ this.dom.style.height = this.view.viewState.contentHeight / this.view.scaleY + "px";
2781
2784
  this.dom.style.flexBasis = this.minWidth ? this.minWidth + "px" : "";
2782
2785
  // Chrome will sometimes, when DOM mutations occur directly
2783
2786
  // around the selection, get confused and report a different
@@ -2849,7 +2852,7 @@ class DocView extends ContentView {
2849
2852
  }
2850
2853
  fixCompositionDOM(composition) {
2851
2854
  let fix = (dom, cView) => {
2852
- cView.flags |= 8 /* ViewFlag.Composition */;
2855
+ cView.flags |= 8 /* ViewFlag.Composition */ | (cView.children.some(c => c.flags & 7 /* ViewFlag.Dirty */) ? 1 /* ViewFlag.ChildDirty */ : 0);
2853
2856
  this.markedForComposition.add(cView);
2854
2857
  let prev = ContentView.get(dom);
2855
2858
  if (prev != cView) {
@@ -3115,7 +3118,7 @@ class DocView extends ContentView {
3115
3118
  let next = i == vs.viewports.length ? null : vs.viewports[i];
3116
3119
  let end = next ? next.from - 1 : this.length;
3117
3120
  if (end > pos) {
3118
- let height = vs.lineBlockAt(end).bottom - vs.lineBlockAt(pos).top;
3121
+ let height = (vs.lineBlockAt(end).bottom - vs.lineBlockAt(pos).top) / this.view.scaleY;
3119
3122
  deco.push(Decoration.replace({
3120
3123
  widget: new BlockGapWidget(height),
3121
3124
  block: true,
@@ -5271,8 +5274,10 @@ class LineGap {
5271
5274
  }
5272
5275
  return true;
5273
5276
  }
5274
- draw(wrapping) {
5275
- return Decoration.replace({ widget: new LineGapWidget(this.size, wrapping) }).range(this.from, this.to);
5277
+ draw(viewState, wrapping) {
5278
+ return Decoration.replace({
5279
+ widget: new LineGapWidget(this.size * (wrapping ? viewState.scaleY : viewState.scaleX), wrapping)
5280
+ }).range(this.from, this.to);
5276
5281
  }
5277
5282
  }
5278
5283
  class LineGapWidget extends WidgetType {
@@ -5302,14 +5307,18 @@ class ViewState {
5302
5307
  // These are contentDOM-local coordinates
5303
5308
  this.pixelViewport = { left: 0, right: window.innerWidth, top: 0, bottom: 0 };
5304
5309
  this.inView = true;
5305
- this.paddingTop = 0;
5306
- this.paddingBottom = 0;
5307
- this.contentDOMWidth = 0;
5308
- this.contentDOMHeight = 0;
5309
- this.editorHeight = 0;
5310
- this.editorWidth = 0;
5311
- this.scrollTop = 0;
5310
+ this.paddingTop = 0; // Padding above the document, scaled
5311
+ this.paddingBottom = 0; // Padding below the document, scaled
5312
+ this.contentDOMWidth = 0; // contentDOM.getBoundingClientRect().width
5313
+ this.contentDOMHeight = 0; // contentDOM.getBoundingClientRect().height
5314
+ this.editorHeight = 0; // scrollDOM.clientHeight, unscaled
5315
+ this.editorWidth = 0; // scrollDOM.clientWidth, unscaled
5316
+ this.scrollTop = 0; // Last seen scrollDOM.scrollTop, scaled
5312
5317
  this.scrolledToBottom = true;
5318
+ // The CSS-transformation scale of the editor (transformed size /
5319
+ // concrete size)
5320
+ this.scaleX = 1;
5321
+ this.scaleY = 1;
5313
5322
  // The vertical position (document-relative) to which to anchor the
5314
5323
  // scroll position. -1 means anchor to the end of the document.
5315
5324
  this.scrollAnchorPos = 0;
@@ -5343,7 +5352,7 @@ class ViewState {
5343
5352
  this.updateViewportLines();
5344
5353
  this.updateForViewport();
5345
5354
  this.lineGaps = this.ensureLineGaps([]);
5346
- this.lineGapDeco = Decoration.set(this.lineGaps.map(gap => gap.draw(false)));
5355
+ this.lineGapDeco = Decoration.set(this.lineGaps.map(gap => gap.draw(this, false)));
5347
5356
  this.computeVisibleRanges();
5348
5357
  }
5349
5358
  updateForViewport() {
@@ -5415,8 +5424,23 @@ class ViewState {
5415
5424
  this.contentDOMHeight = domRect.height;
5416
5425
  this.mustMeasureContent = false;
5417
5426
  let result = 0, bias = 0;
5427
+ if (domRect.width && domRect.height) {
5428
+ let scaleX = domRect.width / dom.offsetWidth;
5429
+ let scaleY = domRect.height / dom.offsetHeight;
5430
+ if (scaleX > 0.995 && scaleX < 1.005)
5431
+ scaleX = 1;
5432
+ if (scaleY > 0.995 && scaleY < 1.005)
5433
+ scaleY = 1;
5434
+ if (this.scaleX != scaleX || this.scaleY != scaleY) {
5435
+ this.scaleX = scaleX;
5436
+ this.scaleY = scaleY;
5437
+ result |= 8 /* UpdateFlag.Geometry */;
5438
+ refresh = measureContent = true;
5439
+ }
5440
+ }
5418
5441
  // Vertical padding
5419
- let paddingTop = parseInt(style.paddingTop) || 0, paddingBottom = parseInt(style.paddingBottom) || 0;
5442
+ let paddingTop = (parseInt(style.paddingTop) || 0) * this.scaleY;
5443
+ let paddingBottom = (parseInt(style.paddingBottom) || 0) * this.scaleY;
5420
5444
  if (this.paddingTop != paddingTop || this.paddingBottom != paddingBottom) {
5421
5445
  this.paddingTop = paddingTop;
5422
5446
  this.paddingBottom = paddingBottom;
@@ -5428,9 +5452,10 @@ class ViewState {
5428
5452
  this.editorWidth = view.scrollDOM.clientWidth;
5429
5453
  result |= 8 /* UpdateFlag.Geometry */;
5430
5454
  }
5431
- if (this.scrollTop != view.scrollDOM.scrollTop) {
5455
+ let scrollTop = view.scrollDOM.scrollTop * this.scaleY;
5456
+ if (this.scrollTop != scrollTop) {
5432
5457
  this.scrollAnchorHeight = -1;
5433
- this.scrollTop = view.scrollDOM.scrollTop;
5458
+ this.scrollTop = scrollTop;
5434
5459
  }
5435
5460
  this.scrolledToBottom = isScrolledToBottom(view.scrollDOM);
5436
5461
  // Pixel viewport
@@ -5651,7 +5676,7 @@ class ViewState {
5651
5676
  updateLineGaps(gaps) {
5652
5677
  if (!LineGap.same(gaps, this.lineGaps)) {
5653
5678
  this.lineGaps = gaps;
5654
- this.lineGapDeco = Decoration.set(gaps.map(gap => gap.draw(this.heightOracle.lineWrapping)));
5679
+ this.lineGapDeco = Decoration.set(gaps.map(gap => gap.draw(this, this.heightOracle.lineWrapping)));
5655
5680
  }
5656
5681
  }
5657
5682
  computeVisibleRanges() {
@@ -7062,9 +7087,9 @@ class EditorView {
7062
7087
  if (flush)
7063
7088
  this.observer.forceFlush();
7064
7089
  let updated = null;
7065
- let sDOM = this.scrollDOM, { scrollTop } = sDOM;
7090
+ let sDOM = this.scrollDOM, scrollTop = sDOM.scrollTop * this.scaleY;
7066
7091
  let { scrollAnchorPos, scrollAnchorHeight } = this.viewState;
7067
- if (scrollTop != this.viewState.scrollTop)
7092
+ if (Math.abs(scrollTop - this.viewState.scrollTop) > 1)
7068
7093
  scrollAnchorHeight = -1;
7069
7094
  this.viewState.scrollAnchorHeight = -1;
7070
7095
  try {
@@ -7141,7 +7166,8 @@ class EditorView {
7141
7166
  this.viewState.lineBlockAt(scrollAnchorPos).top;
7142
7167
  let diff = newAnchorHeight - scrollAnchorHeight;
7143
7168
  if (diff > 1 || diff < -1) {
7144
- scrollTop = sDOM.scrollTop = scrollTop + diff;
7169
+ scrollTop = scrollTop + diff;
7170
+ sDOM.scrollTop = scrollTop / this.scaleY;
7145
7171
  scrollAnchorHeight = -1;
7146
7172
  continue;
7147
7173
  }
@@ -7268,6 +7294,16 @@ class EditorView {
7268
7294
  return { top: this.viewState.paddingTop, bottom: this.viewState.paddingBottom };
7269
7295
  }
7270
7296
  /**
7297
+ If the editor is transformed with CSS, this provides the scale
7298
+ along the X axis. Otherwise, it will just be 1. Note that
7299
+ transforms other than translation and scaling are not supported.
7300
+ */
7301
+ get scaleX() { return this.viewState.scaleX; }
7302
+ /**
7303
+ Provide the CSS transformed scale along the Y axis.
7304
+ */
7305
+ get scaleY() { return this.viewState.scaleY; }
7306
+ /**
7271
7307
  Find the text line or block widget at the given vertical
7272
7308
  position (which is interpreted as relative to the [top of the
7273
7309
  document](https://codemirror.net/6/docs/ref/#view.EditorView.documentTop)).
@@ -8058,8 +8094,8 @@ class RectangleMarker {
8058
8094
  }
8059
8095
  function getBase(view) {
8060
8096
  let rect = view.scrollDOM.getBoundingClientRect();
8061
- let left = view.textDirection == Direction.LTR ? rect.left : rect.right - view.scrollDOM.clientWidth;
8062
- return { left: left - view.scrollDOM.scrollLeft, top: rect.top - view.scrollDOM.scrollTop };
8097
+ let left = view.textDirection == Direction.LTR ? rect.left : rect.right - view.scrollDOM.clientWidth * view.scaleX;
8098
+ return { left: left - view.scrollDOM.scrollLeft * view.scaleX, top: rect.top - view.scrollDOM.scrollTop * view.scaleY };
8063
8099
  }
8064
8100
  function wrappedLine(view, pos, inside) {
8065
8101
  let range = EditorSelection.cursor(pos);
@@ -8161,6 +8197,8 @@ class LayerView {
8161
8197
  this.view = view;
8162
8198
  this.layer = layer;
8163
8199
  this.drawn = [];
8200
+ this.scaleX = 1;
8201
+ this.scaleY = 1;
8164
8202
  this.measureReq = { read: this.measure.bind(this), write: this.draw.bind(this) };
8165
8203
  this.dom = view.scrollDOM.appendChild(document.createElement("div"));
8166
8204
  this.dom.classList.add("cm-layer");
@@ -8168,6 +8206,7 @@ class LayerView {
8168
8206
  this.dom.classList.add("cm-layer-above");
8169
8207
  if (layer.class)
8170
8208
  this.dom.classList.add(layer.class);
8209
+ this.scale();
8171
8210
  this.dom.setAttribute("aria-hidden", "true");
8172
8211
  this.setOrder(view.state);
8173
8212
  view.requestMeasure(this.measureReq);
@@ -8177,8 +8216,10 @@ class LayerView {
8177
8216
  update(update) {
8178
8217
  if (update.startState.facet(layerOrder) != update.state.facet(layerOrder))
8179
8218
  this.setOrder(update.state);
8180
- if (this.layer.update(update, this.dom) || update.geometryChanged)
8219
+ if (this.layer.update(update, this.dom) || update.geometryChanged) {
8220
+ this.scale();
8181
8221
  update.view.requestMeasure(this.measureReq);
8222
+ }
8182
8223
  }
8183
8224
  setOrder(state) {
8184
8225
  let pos = 0, order = state.facet(layerOrder);
@@ -8189,6 +8230,14 @@ class LayerView {
8189
8230
  measure() {
8190
8231
  return this.layer.markers(this.view);
8191
8232
  }
8233
+ scale() {
8234
+ let { scaleX, scaleY } = this.view;
8235
+ if (scaleX != this.scaleX || scaleY != this.scaleY) {
8236
+ this.scaleX = scaleX;
8237
+ this.scaleY = scaleY;
8238
+ this.dom.style.transform = `scale(${1 / scaleX}, ${1 / scaleY})`;
8239
+ }
8240
+ }
8192
8241
  draw(markers) {
8193
8242
  if (markers.length != this.drawn.length || markers.some((p, i) => !sameMarker(p, this.drawn[i]))) {
8194
8243
  let old = this.dom.firstChild, oldI = 0;
@@ -8358,23 +8407,25 @@ const drawDropCursor = /*@__PURE__*/ViewPlugin.fromClass(class {
8358
8407
  }
8359
8408
  }
8360
8409
  readPos() {
8361
- let pos = this.view.state.field(dropCursorPos);
8362
- let rect = pos != null && this.view.coordsAtPos(pos);
8410
+ let { view } = this;
8411
+ let pos = view.state.field(dropCursorPos);
8412
+ let rect = pos != null && view.coordsAtPos(pos);
8363
8413
  if (!rect)
8364
8414
  return null;
8365
- let outer = this.view.scrollDOM.getBoundingClientRect();
8415
+ let outer = view.scrollDOM.getBoundingClientRect();
8366
8416
  return {
8367
- left: rect.left - outer.left + this.view.scrollDOM.scrollLeft,
8368
- top: rect.top - outer.top + this.view.scrollDOM.scrollTop,
8417
+ left: rect.left - outer.left + view.scrollDOM.scrollLeft * view.scaleX,
8418
+ top: rect.top - outer.top + view.scrollDOM.scrollTop * view.scaleY,
8369
8419
  height: rect.bottom - rect.top
8370
8420
  };
8371
8421
  }
8372
8422
  drawCursor(pos) {
8373
8423
  if (this.cursor) {
8424
+ let { scaleX, scaleY } = this.view;
8374
8425
  if (pos) {
8375
- this.cursor.style.left = pos.left + "px";
8376
- this.cursor.style.top = pos.top + "px";
8377
- this.cursor.style.height = pos.height + "px";
8426
+ this.cursor.style.left = pos.left / scaleX + "px";
8427
+ this.cursor.style.top = pos.top / scaleY + "px";
8428
+ this.cursor.style.height = pos.height / scaleY + "px";
8378
8429
  }
8379
8430
  else {
8380
8431
  this.cursor.style.left = "-100000px";
@@ -8616,7 +8667,9 @@ function specialCharPlugin() {
8616
8667
  if (code == 9) {
8617
8668
  let line = doc.lineAt(pos);
8618
8669
  let size = view.state.tabSize, col = countColumn(line.text, size, pos - line.from);
8619
- return Decoration.replace({ widget: new TabWidget((size - (col % size)) * this.view.defaultCharacterWidth) });
8670
+ return Decoration.replace({
8671
+ widget: new TabWidget((size - (col % size)) * this.view.defaultCharacterWidth / this.view.scaleX)
8672
+ });
8620
8673
  }
8621
8674
  return this.decorationCache[code] ||
8622
8675
  (this.decorationCache[code] = Decoration.replace({ widget: new SpecialCharWidget(conf, code) }));
@@ -8693,7 +8746,8 @@ const plugin = /*@__PURE__*/ViewPlugin.fromClass(class {
8693
8746
  }
8694
8747
  update(update) {
8695
8748
  let { view } = update;
8696
- let height = view.viewState.editorHeight - view.defaultLineHeight - view.documentPadding.top - 0.5;
8749
+ let height = view.viewState.editorHeight * view.scaleY -
8750
+ view.defaultLineHeight - view.documentPadding.top - 0.5;
8697
8751
  if (height >= 0 && height != this.height) {
8698
8752
  this.height = height;
8699
8753
  this.attrs = { style: `padding-bottom: ${height}px` };
@@ -8990,6 +9044,7 @@ const tooltipPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
8990
9044
  constructor(view) {
8991
9045
  this.view = view;
8992
9046
  this.inView = true;
9047
+ this.madeAbsolute = false;
8993
9048
  this.lastTransaction = 0;
8994
9049
  this.measureTimeout = -1;
8995
9050
  let config = view.state.facet(tooltipConfig);
@@ -9041,7 +9096,7 @@ const tooltipPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
9041
9096
  this.observeIntersection();
9042
9097
  let shouldMeasure = updated || update.geometryChanged;
9043
9098
  let newConfig = update.state.facet(tooltipConfig);
9044
- if (newConfig.position != this.position) {
9099
+ if (newConfig.position != this.position && !this.madeAbsolute) {
9045
9100
  this.position = newConfig.position;
9046
9101
  for (let t of this.manager.tooltipViews)
9047
9102
  t.dom.style.position = this.position;
@@ -9089,6 +9144,27 @@ const tooltipPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
9089
9144
  }
9090
9145
  readMeasure() {
9091
9146
  let editor = this.view.dom.getBoundingClientRect();
9147
+ let scaleX = 1, scaleY = 1, makeAbsolute = false;
9148
+ if (this.position == "fixed") {
9149
+ let views = this.manager.tooltipViews;
9150
+ // When the dialog's offset parent isn't the body, we are
9151
+ // probably in a transformed container, and should use absolute
9152
+ // positioning instead, since fixed positioning inside a
9153
+ // transform works in a very broken way.
9154
+ makeAbsolute = views.length > 0 && views[0].dom.offsetParent != this.container.ownerDocument.body;
9155
+ }
9156
+ if (makeAbsolute || this.position == "absolute") {
9157
+ if (this.parent) {
9158
+ let rect = this.parent.getBoundingClientRect();
9159
+ if (rect.width && rect.height) {
9160
+ scaleX = rect.width / this.parent.offsetWidth;
9161
+ scaleY = rect.height / this.parent.offsetHeight;
9162
+ }
9163
+ }
9164
+ else {
9165
+ ({ scaleX, scaleY } = this.view.viewState);
9166
+ }
9167
+ }
9092
9168
  return {
9093
9169
  editor,
9094
9170
  parent: this.parent ? this.container.getBoundingClientRect() : editor,
@@ -9098,11 +9174,18 @@ const tooltipPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
9098
9174
  }),
9099
9175
  size: this.manager.tooltipViews.map(({ dom }) => dom.getBoundingClientRect()),
9100
9176
  space: this.view.state.facet(tooltipConfig).tooltipSpace(this.view),
9177
+ scaleX, scaleY, makeAbsolute
9101
9178
  };
9102
9179
  }
9103
9180
  writeMeasure(measured) {
9104
9181
  var _a;
9105
- let { editor, space } = measured;
9182
+ if (measured.makeAbsolute) {
9183
+ this.madeAbsolute = true;
9184
+ this.position = "absolute";
9185
+ for (let t of this.manager.tooltipViews)
9186
+ t.dom.style.position = "absolute";
9187
+ }
9188
+ let { editor, space, scaleX, scaleY } = measured;
9106
9189
  let others = [];
9107
9190
  for (let i = 0; i < this.manager.tooltips.length; i++) {
9108
9191
  let tooltip = this.manager.tooltips[i], tView = this.manager.tooltipViews[i], { dom } = tView;
@@ -9135,7 +9218,7 @@ const tooltipPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
9135
9218
  continue;
9136
9219
  }
9137
9220
  knownHeight.set(tView, height);
9138
- dom.style.height = (height = spaceVert) + "px";
9221
+ dom.style.height = (height = spaceVert) / scaleY + "px";
9139
9222
  }
9140
9223
  else if (dom.style.height) {
9141
9224
  dom.style.height = "";
@@ -9147,15 +9230,17 @@ const tooltipPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
9147
9230
  if (r.left < right && r.right > left && r.top < top + height && r.bottom > top)
9148
9231
  top = above ? r.top - height - 2 - arrowHeight : r.bottom + arrowHeight + 2;
9149
9232
  if (this.position == "absolute") {
9150
- dom.style.top = (top - measured.parent.top) + "px";
9151
- dom.style.left = (left - measured.parent.left) + "px";
9233
+ dom.style.top = (top - measured.parent.top) / scaleY + "px";
9234
+ dom.style.left = (left - measured.parent.left) / scaleX + "px";
9152
9235
  }
9153
9236
  else {
9154
- dom.style.top = top + "px";
9155
- dom.style.left = left + "px";
9237
+ dom.style.top = top / scaleY + "px";
9238
+ dom.style.left = left / scaleX + "px";
9239
+ }
9240
+ if (arrow) {
9241
+ let arrowLeft = pos.left + (ltr ? offset.x : -offset.x) - (left + 14 /* Arrow.Offset */ - 7 /* Arrow.Size */);
9242
+ arrow.style.left = arrowLeft / scaleX + "px";
9156
9243
  }
9157
- if (arrow)
9158
- arrow.style.left = `${pos.left + (ltr ? offset.x : -offset.x) - (left + 14 /* Arrow.Offset */ - 7 /* Arrow.Size */)}px`;
9159
9244
  if (tView.overlap !== true)
9160
9245
  others.push({ left, top, right, bottom: top + height });
9161
9246
  dom.classList.toggle("cm-tooltip-above", above);
@@ -9757,7 +9842,7 @@ const gutterView = /*@__PURE__*/ViewPlugin.fromClass(class {
9757
9842
  this.dom = document.createElement("div");
9758
9843
  this.dom.className = "cm-gutters";
9759
9844
  this.dom.setAttribute("aria-hidden", "true");
9760
- this.dom.style.minHeight = this.view.contentHeight + "px";
9845
+ this.dom.style.minHeight = (this.view.contentHeight / this.view.scaleY) + "px";
9761
9846
  this.gutters = view.state.facet(activeGutters).map(conf => new SingleGutterView(view, conf));
9762
9847
  for (let gutter of this.gutters)
9763
9848
  this.dom.appendChild(gutter.dom);
@@ -9867,7 +9952,9 @@ const gutterView = /*@__PURE__*/ViewPlugin.fromClass(class {
9867
9952
  let value = view.plugin(plugin);
9868
9953
  if (!value || value.gutters.length == 0 || !value.fixed)
9869
9954
  return null;
9870
- return view.textDirection == Direction.LTR ? { left: value.dom.offsetWidth } : { right: value.dom.offsetWidth };
9955
+ return view.textDirection == Direction.LTR
9956
+ ? { left: value.dom.offsetWidth * view.scaleX }
9957
+ : { right: value.dom.offsetWidth * view.scaleX };
9871
9958
  })
9872
9959
  });
9873
9960
  function asArray(val) { return (Array.isArray(val) ? val : [val]); }
@@ -9984,10 +10071,12 @@ class GutterElement {
9984
10071
  this.update(view, height, above, markers);
9985
10072
  }
9986
10073
  update(view, height, above, markers) {
9987
- if (this.height != height)
9988
- this.dom.style.height = (this.height = height) + "px";
10074
+ if (this.height != height) {
10075
+ this.height = height;
10076
+ this.dom.style.height = height / view.scaleY + "px";
10077
+ }
9989
10078
  if (this.above != above)
9990
- this.dom.style.marginTop = (this.above = above) ? above + "px" : "";
10079
+ this.dom.style.marginTop = (this.above = above) ? above / view.scaleY + "px" : "";
9991
10080
  if (!sameMarkers(this.markers, markers))
9992
10081
  this.setMarkers(view, markers);
9993
10082
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/view",
3
- "version": "6.17.1",
3
+ "version": "6.18.0",
4
4
  "description": "DOM view component for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",