A clean, Markdown-based publishing platform made for writers. Write together, and build a community. https://writefreely.org
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 
 

110 строки
3.7 KiB

  1. // class MarkdownView {
  2. // constructor(target, content) {
  3. // this.textarea = target.appendChild(document.createElement("textarea"))
  4. // this.textarea.value = content
  5. // }
  6. // get content() { return this.textarea.value }
  7. // focus() { this.textarea.focus() }
  8. // destroy() { this.textarea.remove() }
  9. // }
  10. import { EditorView } from "prosemirror-view";
  11. import { EditorState, TextSelection } from "prosemirror-state";
  12. import { exampleSetup } from "prosemirror-example-setup";
  13. import { keymap } from "prosemirror-keymap";
  14. import { writeAsMarkdownParser } from "./markdownParser";
  15. import { writeAsMarkdownSerializer } from "./markdownSerializer";
  16. import { writeFreelySchema } from "./schema";
  17. import { getMenu } from "./menu";
  18. let $title = document.querySelector("#title");
  19. let $content = document.querySelector("#content");
  20. // Bugs:
  21. // 1. When there's just an empty line and a hard break is inserted with shift-enter then two enters are inserted
  22. // which do not show up in the markdown ( maybe bc. they are training enters )
  23. class ProseMirrorView {
  24. constructor(target, content) {
  25. let localDraft = localStorage.getItem(window.draftKey);
  26. if (localDraft != null) {
  27. content = localDraft;
  28. }
  29. if (content.indexOf("# ") === 0) {
  30. let eol = content.indexOf("\n");
  31. let title = content.substring("# ".length, eol);
  32. content = content.substring(eol + "\n\n".length);
  33. $title.value = title;
  34. }
  35. const doc = writeAsMarkdownParser.parse(
  36. // Replace all "solo" \n's with \\\n for correct markdown parsing
  37. content.replaceAll(/(?<!\n)\n(?!\n)/g, "\\\n")
  38. );
  39. this.view = new EditorView(target, {
  40. state: EditorState.create({
  41. doc,
  42. plugins: [
  43. keymap({
  44. "Mod-Enter": () => {
  45. document.getElementById("publish").click();
  46. return true;
  47. },
  48. "Mod-k": () => {
  49. const linkButton = document.querySelector(".ProseMirror-icon[title='Add or remove link']")
  50. linkButton.dispatchEvent(new Event('mousedown'));
  51. return true;
  52. },
  53. }),
  54. ...exampleSetup({
  55. schema: writeFreelySchema,
  56. menuContent: getMenu(),
  57. }),
  58. ],
  59. }),
  60. dispatchTransaction(transaction) {
  61. const newContent = writeAsMarkdownSerializer
  62. .serialize(transaction.doc)
  63. // Replace all \\\ns ( not followed by a \n ) with \n
  64. .replaceAll(/\\\n(?!\n)/g, "\n");
  65. $content.value = newContent;
  66. let draft = "";
  67. if ($title.value != null && $title.value !== "") {
  68. draft = "# " + $title.value + "\n\n";
  69. }
  70. draft += newContent;
  71. localStorage.setItem(window.draftKey, draft);
  72. let newState = this.state.apply(transaction);
  73. this.updateState(newState);
  74. },
  75. });
  76. // Editor is focused to the last position. This is a workaround for a bug:
  77. // 1. 1 type something in an existing entry
  78. // 2. reload - works fine, the draft is reloaded
  79. // 3. reload again - the draft is somehow removed from localStorage and the original content is loaded
  80. // When the editor is focused the content is re-saved to localStorage
  81. // This is also useful for editing, so it's not a bad thing even
  82. const lastPosition = this.view.state.doc.content.size;
  83. const selection = TextSelection.create(this.view.state.doc, lastPosition);
  84. this.view.dispatch(this.view.state.tr.setSelection(selection));
  85. this.view.focus();
  86. }
  87. get content() {
  88. return defaultMarkdownSerializer.serialize(this.view.state.doc);
  89. }
  90. focus() {
  91. this.view.focus();
  92. }
  93. destroy() {
  94. this.view.destroy();
  95. }
  96. }
  97. let place = document.querySelector("#editor");
  98. let view = new ProseMirrorView(place, $content.value);