prosemirror-view 1.33.3 → 1.33.5

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
+ ## 1.33.5 (2024-04-22)
2
+
3
+ ### Bug fixes
4
+
5
+ Fix a regression in composition handling in 1.33.3 that occurs for some IMEs when starting composition on an empty line.
6
+
7
+ ## 1.33.4 (2024-04-08)
8
+
9
+ ### Bug fixes
10
+
11
+ When available, use `Selection.getComposedRanges` on Safari to find the selection inside a shadow DOM.
12
+
1
13
  ## 1.33.3 (2024-03-22)
2
14
 
3
15
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -3248,7 +3248,7 @@ function findCompositionNode(view) {
3248
3248
  if (!(!descBefore || !descBefore.isText(textBefore.nodeValue))) return textAfter;
3249
3249
  }
3250
3250
  }
3251
- return textBefore;
3251
+ return textBefore || textAfter;
3252
3252
  }
3253
3253
  function timestampFromCustomEvent() {
3254
3254
  var event = document.createEvent("Event");
@@ -4349,20 +4349,11 @@ function checkCSS(view) {
4349
4349
  cssCheckWarned = true;
4350
4350
  }
4351
4351
  }
4352
- function safariShadowSelectionRange(view) {
4353
- var found;
4354
- function read(event) {
4355
- event.preventDefault();
4356
- event.stopImmediatePropagation();
4357
- found = event.getTargetRanges()[0];
4358
- }
4359
- view.dom.addEventListener("beforeinput", read, true);
4360
- document.execCommand("indent");
4361
- view.dom.removeEventListener("beforeinput", read, true);
4362
- var anchorNode = found.startContainer,
4363
- anchorOffset = found.startOffset;
4364
- var focusNode = found.endContainer,
4365
- focusOffset = found.endOffset;
4352
+ function rangeToSelectionRange(view, range) {
4353
+ var anchorNode = range.startContainer,
4354
+ anchorOffset = range.startOffset;
4355
+ var focusNode = range.endContainer,
4356
+ focusOffset = range.endOffset;
4366
4357
  var currentAnchor = view.domAtPos(view.state.selection.anchor);
4367
4358
  if (isEquivalentPosition(currentAnchor.node, currentAnchor.offset, focusNode, focusOffset)) {
4368
4359
  var _ref3 = [focusNode, focusOffset, anchorNode, anchorOffset];
@@ -4378,6 +4369,22 @@ function safariShadowSelectionRange(view) {
4378
4369
  focusOffset: focusOffset
4379
4370
  };
4380
4371
  }
4372
+ function safariShadowSelectionRange(view, selection) {
4373
+ if (selection.getComposedRanges) {
4374
+ var range = selection.getComposedRanges(view.root)[0];
4375
+ if (range) return rangeToSelectionRange(view, range);
4376
+ }
4377
+ var found;
4378
+ function read(event) {
4379
+ event.preventDefault();
4380
+ event.stopImmediatePropagation();
4381
+ found = event.getTargetRanges()[0];
4382
+ }
4383
+ view.dom.addEventListener("beforeinput", read, true);
4384
+ document.execCommand("indent");
4385
+ view.dom.removeEventListener("beforeinput", read, true);
4386
+ return found ? rangeToSelectionRange(view, found) : null;
4387
+ }
4381
4388
  function parseBetween(view, from_, to_) {
4382
4389
  var _view$docView$parseRa = view.docView.parseRange(from_, to_),
4383
4390
  parent = _view$docView$parseRa.node,
@@ -5049,7 +5056,8 @@ var EditorView = function () {
5049
5056
  }, {
5050
5057
  key: "domSelectionRange",
5051
5058
  value: function domSelectionRange() {
5052
- return safari && this.root.nodeType === 11 && deepActiveElement(this.dom.ownerDocument) == this.dom ? safariShadowSelectionRange(this) : this.domSelection();
5059
+ var sel = this.domSelection();
5060
+ return safari && this.root.nodeType === 11 && deepActiveElement(this.dom.ownerDocument) == this.dom && safariShadowSelectionRange(this, sel) || sel;
5053
5061
  }
5054
5062
  }, {
5055
5063
  key: "domSelection",
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { EditorState, Transaction, Selection, Plugin } from 'prosemirror-state';
2
- import { Mark, Node, ParseRule, Slice, ResolvedPos, DOMParser, DOMSerializer } from 'prosemirror-model';
2
+ import { Mark, Node, TagParseRule, Slice, ResolvedPos, DOMParser, DOMSerializer } from 'prosemirror-model';
3
3
  import { Mapping } from 'prosemirror-transform';
4
4
 
5
5
  type DOMNode = InstanceType<typeof window.Node>;
@@ -324,7 +324,7 @@ declare class ViewDesc {
324
324
  matchesMark(mark: Mark): boolean;
325
325
  matchesNode(node: Node, outerDeco: readonly Decoration[], innerDeco: DecorationSource): boolean;
326
326
  matchesHack(nodeName: string): boolean;
327
- parseRule(): ParseRule | null;
327
+ parseRule(): Omit<TagParseRule, "tag"> | null;
328
328
  stopEvent(event: Event): boolean;
329
329
  get size(): number;
330
330
  get border(): number;
@@ -370,7 +370,7 @@ declare class NodeViewDesc extends ViewDesc {
370
370
  readonly nodeDOM: DOMNode;
371
371
  constructor(parent: ViewDesc | undefined, node: Node, outerDeco: readonly Decoration[], innerDeco: DecorationSource, dom: DOMNode, contentDOM: HTMLElement | null, nodeDOM: DOMNode, view: EditorView, pos: number);
372
372
  static create(parent: ViewDesc | undefined, node: Node, outerDeco: readonly Decoration[], innerDeco: DecorationSource, view: EditorView, pos: number): NodeViewDesc | TextViewDesc;
373
- parseRule(): ParseRule | null;
373
+ parseRule(): Omit<TagParseRule, "tag"> | null;
374
374
  matchesNode(node: Node, outerDeco: readonly Decoration[], innerDeco: DecorationSource): boolean;
375
375
  get size(): number;
376
376
  get border(): 0 | 1;
@@ -394,7 +394,9 @@ declare class NodeViewDesc extends ViewDesc {
394
394
  }
395
395
  declare class TextViewDesc extends NodeViewDesc {
396
396
  constructor(parent: ViewDesc | undefined, node: Node, outerDeco: readonly Decoration[], innerDeco: DecorationSource, dom: DOMNode, nodeDOM: DOMNode, view: EditorView);
397
- parseRule(): ParseRule;
397
+ parseRule(): {
398
+ skip: any;
399
+ };
398
400
  update(node: Node, outerDeco: readonly Decoration[], innerDeco: DecorationSource, view: EditorView): boolean;
399
401
  inParent(): boolean;
400
402
  domFromPos(pos: number): {
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { EditorState, Transaction, Selection, Plugin } from 'prosemirror-state';
2
- import { Mark, Node, ParseRule, Slice, ResolvedPos, DOMParser, DOMSerializer } from 'prosemirror-model';
2
+ import { Mark, Node, TagParseRule, Slice, ResolvedPos, DOMParser, DOMSerializer } from 'prosemirror-model';
3
3
  import { Mapping } from 'prosemirror-transform';
4
4
 
5
5
  type DOMNode = InstanceType<typeof window.Node>;
@@ -324,7 +324,7 @@ declare class ViewDesc {
324
324
  matchesMark(mark: Mark): boolean;
325
325
  matchesNode(node: Node, outerDeco: readonly Decoration[], innerDeco: DecorationSource): boolean;
326
326
  matchesHack(nodeName: string): boolean;
327
- parseRule(): ParseRule | null;
327
+ parseRule(): Omit<TagParseRule, "tag"> | null;
328
328
  stopEvent(event: Event): boolean;
329
329
  get size(): number;
330
330
  get border(): number;
@@ -370,7 +370,7 @@ declare class NodeViewDesc extends ViewDesc {
370
370
  readonly nodeDOM: DOMNode;
371
371
  constructor(parent: ViewDesc | undefined, node: Node, outerDeco: readonly Decoration[], innerDeco: DecorationSource, dom: DOMNode, contentDOM: HTMLElement | null, nodeDOM: DOMNode, view: EditorView, pos: number);
372
372
  static create(parent: ViewDesc | undefined, node: Node, outerDeco: readonly Decoration[], innerDeco: DecorationSource, view: EditorView, pos: number): NodeViewDesc | TextViewDesc;
373
- parseRule(): ParseRule | null;
373
+ parseRule(): Omit<TagParseRule, "tag"> | null;
374
374
  matchesNode(node: Node, outerDeco: readonly Decoration[], innerDeco: DecorationSource): boolean;
375
375
  get size(): number;
376
376
  get border(): 0 | 1;
@@ -394,7 +394,9 @@ declare class NodeViewDesc extends ViewDesc {
394
394
  }
395
395
  declare class TextViewDesc extends NodeViewDesc {
396
396
  constructor(parent: ViewDesc | undefined, node: Node, outerDeco: readonly Decoration[], innerDeco: DecorationSource, dom: DOMNode, nodeDOM: DOMNode, view: EditorView);
397
- parseRule(): ParseRule;
397
+ parseRule(): {
398
+ skip: any;
399
+ };
398
400
  update(node: Node, outerDeco: readonly Decoration[], innerDeco: DecorationSource, view: EditorView): boolean;
399
401
  inParent(): boolean;
400
402
  domFromPos(pos: number): {
package/dist/index.js CHANGED
@@ -3468,7 +3468,7 @@ function findCompositionNode(view) {
3468
3468
  return textAfter;
3469
3469
  }
3470
3470
  }
3471
- return textBefore;
3471
+ return textBefore || textAfter;
3472
3472
  }
3473
3473
  function timestampFromCustomEvent() {
3474
3474
  let event = document.createEvent("Event");
@@ -4704,9 +4704,25 @@ function checkCSS(view) {
4704
4704
  cssCheckWarned = true;
4705
4705
  }
4706
4706
  }
4707
+ function rangeToSelectionRange(view, range) {
4708
+ let anchorNode = range.startContainer, anchorOffset = range.startOffset;
4709
+ let focusNode = range.endContainer, focusOffset = range.endOffset;
4710
+ let currentAnchor = view.domAtPos(view.state.selection.anchor);
4711
+ // Since such a range doesn't distinguish between anchor and head,
4712
+ // use a heuristic that flips it around if its end matches the
4713
+ // current anchor.
4714
+ if (isEquivalentPosition(currentAnchor.node, currentAnchor.offset, focusNode, focusOffset))
4715
+ [anchorNode, anchorOffset, focusNode, focusOffset] = [focusNode, focusOffset, anchorNode, anchorOffset];
4716
+ return { anchorNode, anchorOffset, focusNode, focusOffset };
4717
+ }
4707
4718
  // Used to work around a Safari Selection/shadow DOM bug
4708
4719
  // Based on https://github.com/codemirror/dev/issues/414 fix
4709
- function safariShadowSelectionRange(view) {
4720
+ function safariShadowSelectionRange(view, selection) {
4721
+ if (selection.getComposedRanges) {
4722
+ let range = selection.getComposedRanges(view.root)[0];
4723
+ if (range)
4724
+ return rangeToSelectionRange(view, range);
4725
+ }
4710
4726
  let found;
4711
4727
  function read(event) {
4712
4728
  event.preventDefault();
@@ -4721,15 +4737,7 @@ function safariShadowSelectionRange(view) {
4721
4737
  view.dom.addEventListener("beforeinput", read, true);
4722
4738
  document.execCommand("indent");
4723
4739
  view.dom.removeEventListener("beforeinput", read, true);
4724
- let anchorNode = found.startContainer, anchorOffset = found.startOffset;
4725
- let focusNode = found.endContainer, focusOffset = found.endOffset;
4726
- let currentAnchor = view.domAtPos(view.state.selection.anchor);
4727
- // Since such a range doesn't distinguish between anchor and head,
4728
- // use a heuristic that flips it around if its end matches the
4729
- // current anchor.
4730
- if (isEquivalentPosition(currentAnchor.node, currentAnchor.offset, focusNode, focusOffset))
4731
- [anchorNode, anchorOffset, focusNode, focusOffset] = [focusNode, focusOffset, anchorNode, anchorOffset];
4732
- return { anchorNode, anchorOffset, focusNode, focusOffset };
4740
+ return found ? rangeToSelectionRange(view, found) : null;
4733
4741
  }
4734
4742
 
4735
4743
  // Note that all referencing and parsing is done with the
@@ -5607,8 +5615,9 @@ class EditorView {
5607
5615
  @internal
5608
5616
  */
5609
5617
  domSelectionRange() {
5610
- return safari && this.root.nodeType === 11 && deepActiveElement(this.dom.ownerDocument) == this.dom
5611
- ? safariShadowSelectionRange(this) : this.domSelection();
5618
+ let sel = this.domSelection();
5619
+ return safari && this.root.nodeType === 11 &&
5620
+ deepActiveElement(this.dom.ownerDocument) == this.dom && safariShadowSelectionRange(this, sel) || sel;
5612
5621
  }
5613
5622
  /**
5614
5623
  @internal
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prosemirror-view",
3
- "version": "1.33.3",
3
+ "version": "1.33.5",
4
4
  "description": "ProseMirror's view component",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -28,7 +28,7 @@
28
28
  "url": "git://github.com/prosemirror/prosemirror-view.git"
29
29
  },
30
30
  "dependencies": {
31
- "prosemirror-model": "^1.16.0",
31
+ "prosemirror-model": "^1.20.0",
32
32
  "prosemirror-state": "^1.0.0",
33
33
  "prosemirror-transform": "^1.1.0"
34
34
  },
package/src/domchange.ts CHANGED
@@ -1,4 +1,4 @@
1
- import {Fragment, DOMParser, ParseRule, Node, Mark, ResolvedPos} from "prosemirror-model"
1
+ import {Fragment, DOMParser, TagParseRule, Node, Mark, ResolvedPos} from "prosemirror-model"
2
2
  import {Selection, TextSelection} from "prosemirror-state"
3
3
 
4
4
  import {selectionBetween, selectionFromDOM, selectionToDOM} from "./selection"
@@ -55,7 +55,7 @@ function parseBetween(view: EditorView, from_: number, to_: number) {
55
55
  return {doc, sel, from, to}
56
56
  }
57
57
 
58
- function ruleFromNode(dom: DOMNode): ParseRule | null {
58
+ function ruleFromNode(dom: DOMNode): Omit<TagParseRule, "tag"> | null {
59
59
  let desc = dom.pmViewDesc
60
60
  if (desc) {
61
61
  return desc.parseRule()
@@ -1,6 +1,6 @@
1
1
  import {Selection} from "prosemirror-state"
2
2
  import * as browser from "./browser"
3
- import {domIndex, isEquivalentPosition, selectionCollapsed, parentNode, DOMSelectionRange, DOMNode} from "./dom"
3
+ import {domIndex, isEquivalentPosition, selectionCollapsed, parentNode, DOMSelectionRange, DOMNode, DOMSelection} from "./dom"
4
4
  import {hasFocusAndSelection, selectionToDOM, selectionFromDOM} from "./selection"
5
5
  import {EditorView} from "./index"
6
6
 
@@ -278,9 +278,27 @@ function checkCSS(view: EditorView) {
278
278
  }
279
279
  }
280
280
 
281
+ function rangeToSelectionRange(view: EditorView, range: StaticRange) {
282
+ let anchorNode = range.startContainer, anchorOffset = range.startOffset
283
+ let focusNode = range.endContainer, focusOffset = range.endOffset
284
+
285
+ let currentAnchor = view.domAtPos(view.state.selection.anchor)
286
+ // Since such a range doesn't distinguish between anchor and head,
287
+ // use a heuristic that flips it around if its end matches the
288
+ // current anchor.
289
+ if (isEquivalentPosition(currentAnchor.node, currentAnchor.offset, focusNode, focusOffset))
290
+ [anchorNode, anchorOffset, focusNode, focusOffset] = [focusNode, focusOffset, anchorNode, anchorOffset]
291
+ return {anchorNode, anchorOffset, focusNode, focusOffset}
292
+ }
293
+
281
294
  // Used to work around a Safari Selection/shadow DOM bug
282
295
  // Based on https://github.com/codemirror/dev/issues/414 fix
283
- export function safariShadowSelectionRange(view: EditorView): DOMSelectionRange {
296
+ export function safariShadowSelectionRange(view: EditorView, selection: DOMSelection): DOMSelectionRange | null {
297
+ if ((selection as any).getComposedRanges) {
298
+ let range = (selection as any).getComposedRanges(view.root)[0] as StaticRange
299
+ if (range) return rangeToSelectionRange(view, range)
300
+ }
301
+
284
302
  let found: StaticRange | undefined
285
303
  function read(event: InputEvent) {
286
304
  event.preventDefault()
@@ -297,14 +315,5 @@ export function safariShadowSelectionRange(view: EditorView): DOMSelectionRange
297
315
  document.execCommand("indent")
298
316
  view.dom.removeEventListener("beforeinput", read, true)
299
317
 
300
- let anchorNode = found!.startContainer, anchorOffset = found!.startOffset
301
- let focusNode = found!.endContainer, focusOffset = found!.endOffset
302
-
303
- let currentAnchor = view.domAtPos(view.state.selection.anchor)
304
- // Since such a range doesn't distinguish between anchor and head,
305
- // use a heuristic that flips it around if its end matches the
306
- // current anchor.
307
- if (isEquivalentPosition(currentAnchor.node, currentAnchor.offset, focusNode, focusOffset))
308
- [anchorNode, anchorOffset, focusNode, focusOffset] = [focusNode, focusOffset, anchorNode, anchorOffset]
309
- return {anchorNode, anchorOffset, focusNode, focusOffset}
318
+ return found ? rangeToSelectionRange(view, found) : null
310
319
  }
package/src/index.ts CHANGED
@@ -488,8 +488,9 @@ export class EditorView {
488
488
 
489
489
  /// @internal
490
490
  domSelectionRange(): DOMSelectionRange {
491
- return browser.safari && this.root.nodeType === 11 && deepActiveElement(this.dom.ownerDocument) == this.dom
492
- ? safariShadowSelectionRange(this) : this.domSelection()
491
+ let sel = this.domSelection()
492
+ return browser.safari && this.root.nodeType === 11 &&
493
+ deepActiveElement(this.dom.ownerDocument) == this.dom && safariShadowSelectionRange(this, sel) || sel
493
494
  }
494
495
 
495
496
  /// @internal
package/src/input.ts CHANGED
@@ -526,7 +526,7 @@ export function findCompositionNode(view: EditorView) {
526
526
  return textAfter
527
527
  }
528
528
  }
529
- return textBefore
529
+ return textBefore || textAfter
530
530
  }
531
531
 
532
532
  function timestampFromCustomEvent() {
package/src/viewdesc.ts CHANGED
@@ -1,4 +1,4 @@
1
- import {DOMSerializer, Fragment, Mark, Node, ParseRule} from "prosemirror-model"
1
+ import {DOMSerializer, Fragment, Mark, Node, TagParseRule} from "prosemirror-model"
2
2
  import {TextSelection} from "prosemirror-state"
3
3
 
4
4
  import {domIndex, isEquivalentPosition, DOMNode} from "./dom"
@@ -124,7 +124,7 @@ export class ViewDesc {
124
124
  // When parsing in-editor content (in domchange.js), we allow
125
125
  // descriptions to determine the parse rules that should be used to
126
126
  // parse them.
127
- parseRule(): ParseRule | null { return null }
127
+ parseRule(): Omit<TagParseRule, "tag"> | null { return null }
128
128
 
129
129
  // Used by the editor's event handler to ignore events that come
130
130
  // from certain descs.
@@ -576,7 +576,7 @@ class MarkViewDesc extends ViewDesc {
576
576
  return new MarkViewDesc(parent, mark, spec.dom, spec.contentDOM || spec.dom as HTMLElement)
577
577
  }
578
578
 
579
- parseRule(): ParseRule | null {
579
+ parseRule() {
580
580
  if ((this.dirty & NODE_DIRTY) || this.mark.type.spec.reparseInView) return null
581
581
  return {mark: this.mark.type.name, attrs: this.mark.attrs, contentElement: this.contentDOM!}
582
582
  }
@@ -666,14 +666,14 @@ export class NodeViewDesc extends ViewDesc {
666
666
  return new NodeViewDesc(parent, node, outerDeco, innerDeco, dom, contentDOM || null, nodeDOM, view, pos + 1)
667
667
  }
668
668
 
669
- parseRule(): ParseRule | null {
669
+ parseRule() {
670
670
  // Experimental kludge to allow opt-in re-parsing of nodes
671
671
  if (this.node.type.spec.reparseInView) return null
672
672
  // FIXME the assumption that this can always return the current
673
673
  // attrs means that if the user somehow manages to change the
674
674
  // attrs in the dom, that won't be picked up. Not entirely sure
675
675
  // whether this is a problem
676
- let rule: ParseRule = {node: this.node.type.name, attrs: this.node.attrs}
676
+ let rule: Omit<TagParseRule, "tag"> = {node: this.node.type.name, attrs: this.node.attrs}
677
677
  if (this.node.type.whitespace == "pre") rule.preserveWhitespace = "full"
678
678
  if (!this.contentDOM) {
679
679
  rule.getContent = () => this.node.content
@@ -857,7 +857,7 @@ class TextViewDesc extends NodeViewDesc {
857
857
  super(parent, node, outerDeco, innerDeco, dom, null, nodeDOM, view, 0)
858
858
  }
859
859
 
860
- parseRule(): ParseRule {
860
+ parseRule() {
861
861
  let skip = this.nodeDOM.parentNode
862
862
  while (skip && skip != this.dom && !(skip as any).pmIsDeco) skip = skip.parentNode
863
863
  return {skip: (skip || true) as any}