getkirby / kirby

Kirby's core application folder
https://getkirby.com
Other
1.31k stars 168 forks source link

ProseMirror: allow setting order of marks #5481

Open rasteiner opened 1 year ago

rasteiner commented 1 year ago

Description

The order of nesting of the current prosemirror marks is unexpected: links are split up by formatting marks.

Expected behavior
Functional and focusable elements, such as links, should probably have a higher nesting priority than "decorative" marks

Screenshots

image image

To reproduce

  1. Have a writer field with default marks
  2. Write at least 3 words
  3. Select the middle word, apply the "italic" mark to it
  4. Select everything, apply the "link" mark to the whole line
  5. Your link is split into 3

Your setup

Kirby Version
4.0.0.alpha-6 (but I guess it isn't new)

Additional context
The nesting order of prosemirror is given by the order of the schemas. In Kirby the order of the schemas is given by the order of the marks in the blueprint, and the default blueprint for a writer field puts the link and email marks at the end. So this issue can be worked around by explicitly writing the order in your blueprint:

marks:
  - link
  - email
  - italic
  - bold
  # etc

A fix could be to move those marks in the default blueprint to the start, but I understand that this isn't ideal UX wise, as it also moves the buttons to the front. A more complete fix would be to uncouple schema and button order, for example by giving each mark some kind of "priority" score (either for the button order or the nesting order). However, this would have to be extended to custom writer marks too, as nesting order can be really important for custom tags. This could also be solved with the new toolbar option in v4 (you can reorder the buttons there).

rasteiner commented 1 year ago

Sorry to bother you again... Now the order is hardcoded, this makes it impossible to define the ordering of custom marks, since they always come last in the available array. Like, I have a "footnote" mark (mark because I need it to be inline), but it's important for me that it doesn't get split up by other marks.

Can I suggest a PR? Like a score based sorting. Default marks would have a score of something like 100, and therefore follow the predetermined order in the available array. Custom plugins would inherit that score, or be able to override it. The sorting would then happen in a postFilter callback, for example.

Similar to this: https://github.com/rasteiner/k4-table-writer/blob/aaee76c7d6643e77cd920f1c65e07c9d4f8ae109/src/components/Writer/Writer.vue#L255

bastianallgeier commented 1 year ago

I like the idea of a score. I was wondering if there could also be some sort of configurable schema instead.

markOrder: [
  'footnote', 
  'link', 
  'italic'
]

But maybe that's too much.

bastianallgeier commented 1 year ago

I've reopened the issue until we have a solution for this.

rasteiner commented 1 year ago

The advantage of a score is that it lets plugin authors eyeball what priority their mark should have without having to know what other marks will be installed. In most cases consumers would then be able to just install plugins from different vendors and don't think or know about it.

The disadvantage of a score, which is defined by plugin vendors, is that it takes away control from the consumer. Say if 2 plugin vendors eyeball too differently, and two marks end up in the wrong order, the consumer should have the option to manually reorder them without opening issues on github at the plugin repos.

I can't really think of an intuitive way to manually reorder them however... I mean the way it worked before (the order in the blueprint mattered), was easy if you knew about it, but on the other hand created an opportunity for developers to mess things up if they didn't know about it (also because the issue is invisible when not looking at the html, or listening to a screenreader which suddenly talks about 3 links instead of 1). So maybe a markOrder property in the blueprint?

Or an explicit priority property for "each mark"?

marks:
  - link
  - email
  - italic
  - bold
  - type: footnote
    priority: 1 # <- this shouldn't be split up

Unrelated, but this could also open the doors to additional properties for the marks:

marks:
  - type: link
    linktypes: 
      - page
    query: page.children
  - email
  - italic
  - bold
  - type: footnote
    priority: 1 # <- this shouldn't be split up
  - type: highlight
    colors: 
      - green
      - yellow
      - pink

PS: Contrary to what I wrote before: if we do scores, the default marks should probably also be sorted by score (we shouldn't leave them on "100"). This would give developers a scale of what the score actually represents. (like, set link: 70, italic: 140, bold: 150). And actually also let them put custom marks between core marks (think a contrived smallcaps: 145 for some reason).

distantnative commented 1 year ago

Scores remind me of (probably old) Wordpress menu items. I think without any classification this just doesn't work. Plugin developers assign arbitrary score values not being able to oversee what configurations of other plugins and their arbitrary scores would conflict with their own picked number. And because of this mess a lot of conflicts can occur and one moved from a 1-10 score to a 1-100 score to a 1-1000 hoping bigger gaps could solve the issue.

rasteiner commented 1 year ago

Well the classification would be the scale given by the core kirby marks. If you want to have your mark between "link" and "email", you need to have a score that puts you between them. If you end up with the same score as another plugin, chances are that it doesn't really matter.

I first also thought about classification systems, but it seemed like it would end up as a "score with less freedom". I mean, what would the classes be? "Decoration" & "Interactive" could be a start, as you don't care about the order of "italic" and "bold", you just care about "link" not being interrupted by "italic" or "bold". But then you add something like "footnote", and you don't want "link" to interrupt "footnote", so now you need "Decoration" & "Interactive" & "Super interactive"? It feels equally endless to me, just without the option for plugin devs to actually do something about it (I doubt we could add new classes - or the classification would be meaningless).

Another approach could be a classification like "before: bold", "after: link", etc. But that would end immediately with "undefined behaviour" when 2 plugins want "before: link". Or to be precise, the order would probably depend on the name of the plugin folders, and I personally think that an arbitrary score chosen by developers who don't know each other is still a better heuristic than the folder name.

In the end, "conflicts" aren't often a problem. I thought about this for some time, and the score still resulted the most flexible & comprehensible, even if the priority of marks at that point will probably, sometimes, be determined by the ego of developers (lots of "priority: 1").

lukasbestle commented 1 year ago

Another approach could be a classification like "before: bold", "after: link", etc. But that would end immediately with "undefined behaviour" when 2 plugins want "before: link". Or to be precise, the order would probably depend on the name of the plugin folders, and I personally think that an arbitrary score chosen by developers who don't know each other is still a better heuristic than the folder name.

I think such a "before and after" system makes a lot of sense because it defines the developer intent in a much more direct way.

I don't see the problem of "order by folder name". After all you have the same issue if two plugins choose the same score. The result will in both cases have to be sorted arbitrarily.

rasteiner commented 1 year ago

I don't see the problem of "order by folder name". After all you have the same issue if two plugins choose the same score. The result will in both cases have to be sorted arbitrarily.

Opinions I guess. With a system like "before:link" a conflict to me seems inevitable, with numeric scores it's only possible.

The most important part however, for me, is that we get some kind of control. And that the consumer has the last word on the order.