A clean, Markdown-based publishing platform made for writers. Write together, and build a community. https://writefreely.org
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

113 lines
3.8 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 { writeFreelyMarkdownParser } from "./markdownParser";
  15. import { writeFreelyMarkdownSerializer } 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 typingTimer;
  26. let localDraft = localStorage.getItem(window.draftKey);
  27. if (localDraft != null) {
  28. content = localDraft;
  29. }
  30. if (content.indexOf("# ") === 0) {
  31. let eol = content.indexOf("\n");
  32. let title = content.substring("# ".length, eol);
  33. content = content.substring(eol + "\n\n".length);
  34. $title.value = title;
  35. }
  36. const doc = writeFreelyMarkdownParser.parse(content)
  37. this.view = new EditorView(target, {
  38. state: EditorState.create({
  39. doc,
  40. plugins: [
  41. keymap({
  42. "Mod-Enter": () => {
  43. document.getElementById("publish").click();
  44. return true;
  45. },
  46. "Mod-k": () => {
  47. const linkButton = document.querySelector(
  48. ".ProseMirror-icon[title='Add or remove link']"
  49. );
  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. let newState = this.state.apply(transaction);
  62. const newContent = writeFreelyMarkdownSerializer
  63. .serialize(newState.doc)
  64. // Replace all \\\ns ( not followed by a \n ) with \n
  65. .replace(/(\\\n)(\n{0,1})/g, (match, p1, p2) =>
  66. p2 !== "\n" ? "\n" + p2 : match
  67. );
  68. $content.value = newContent;
  69. let draft = "";
  70. if ($title.value != null && $title.value !== "") {
  71. draft = "# " + $title.value + "\n\n";
  72. }
  73. draft += newContent;
  74. clearTimeout(typingTimer);
  75. typingTimer = setTimeout(doneTyping, doneTypingInterval);
  76. this.updateState(newState);
  77. },
  78. });
  79. // Editor is focused to the last position. This is a workaround for a bug:
  80. // 1. 1 type something in an existing entry
  81. // 2. reload - works fine, the draft is reloaded
  82. // 3. reload again - the draft is somehow removed from localStorage and the original content is loaded
  83. // When the editor is focused the content is re-saved to localStorage
  84. // This is also useful for editing, so it's not a bad thing even
  85. const lastPosition = this.view.state.doc.content.size;
  86. const selection = TextSelection.create(this.view.state.doc, lastPosition);
  87. this.view.dispatch(this.view.state.tr.setSelection(selection));
  88. this.view.focus();
  89. }
  90. get content() {
  91. return writeFreelyMarkdownSerializer.serialize(this.view.state.doc);
  92. }
  93. focus() {
  94. this.view.focus();
  95. }
  96. destroy() {
  97. this.view.destroy();
  98. }
  99. }
  100. let place = document.querySelector("#editor");
  101. let view = new ProseMirrorView(place, $content.value);