Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DecorationSet.map deletes all decorations #1497

Open
rChaoz opened this issue Nov 16, 2024 · 1 comment
Open

DecorationSet.map deletes all decorations #1497

rChaoz opened this issue Nov 16, 2024 · 1 comment

Comments

@rChaoz
Copy link

rChaoz commented Nov 16, 2024

I'm trying to implement image placeholders for uploading images using node decorations. After lots of debugging, however, it seems like this line from the documentation:

decorations = decorations.map(tr.mapping, tr.doc)

has some issues, as decorations.find().length is 2 before it and 0 afterwards. This is rather odd as I expect widget decorations to never be deleted (they are not deleted even when doing Ctrl + ABackspace). However when inserting an image programatically, they are deleted.

As a reproduction, I took dumps of all of the involved variables:

Variable dumps
decorations = {
  "children": [
    0,
    11,
    {
      "children": [],
      "local": [
        {
          "from": 9,
          "to": 9,
          "type": {
            "side": 0,
            "spec": "...",
            "toDOM": "..."
          }
        },
        {
          "from": 9,
          "to": 9,
          "type": {
            "side": 0,
            "spec": "...",
            "toDOM": "..."
          }
        }
      ]
    }
  ],
  "local": []
}

doc = {
  "type": "doc",
  "content": [
    {
      "type": "paragraph",
      "attrs": {},
      "content": [
        {
          "type": "text",
          "text": "Imagesss:"
        }
      ]
    },
    {
      "type": "image",
      "attrs": { ... }
    }
  ]
}

mapping = {
  "maps": [
    {
      "ranges": [10, 1, 2],
      "inverted": false
    }
  ],
  "from": 0,
  "to": 1
}

tr.steps = [
  {
    "stepType": "replace",
    "from": 10,
    "to": 11,
    "slice": {
      "content": [
        {
          "type": "paragraph",
          "attrs": {
            "textAlign": "left"
          }
        },
        {
          "type": "image",
          "attrs": { ... }
        }
      ],
      "openStart": 1
    }
  }
]

This is the code that adds the image:

const pos = (ImageKey.getState(state) as DecorationSet).find(undefined, undefined, (spec) => spec.id === id)[0]?.from
if (pos != null) tr.insert(pos, state.schema.nodeFromJSON({ type: "image", attrs } satisfies JSONContent))

And the code that adds the decoration (inside the apply() function of the plugin state):

if (action.type === "create") {
    const placeholder = document.createElement("div")
    return decorations.add(tr.doc, [
        Decoration.widget(action.pos, placeholder, {
            id: action.id,
            component: new ImagePlaceholder({ target: placeholder, props: { filename: action.filename } }),
        }),
    ])
}

Am I doing something wrong? I tried debugging the map function of DecorationSet with no luck, code too complex for me :(

Workaround

If anyone else has the same issue, this is the workaround I'm using right now instead of decorations = decorations.map(...):

const newDecorations = decorations.map(tr.mapping, tr.doc)
if (newDecorations.find().length === decorations.find().length) {
    decorations = newDecorations
}
@marijnh
Copy link
Member

marijnh commented Nov 16, 2024

Mapping will absolutely delete decorations that are covered by a deleted or replaced range. But in your example the decorations appear to be at position 9, not inside the deleted range. But given that their from equals their to position, they don't appear to be valid node decorations, which might explain why they get dropped.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants