@ -9,28 +9,28 @@
// destroy() { this.textarea.remove() }
// destroy() { this.textarea.remove() }
// }
// }
import { EditorView } from "prosemirror-view"
import { EditorView } from "prosemirror-view" ;
import { EditorState } from "prosemirror-state"
import { EditorState , TextSelection } from "prosemirror-state" ;
import { exampleSetup } from "prosemirror-example-setup"
import { exampleSetup } from "prosemirror-example-setup" ;
import { keymap } from "prosemirror-keymap" ;
import { keymap } from "prosemirror-keymap" ;
import { writeAsMarkdownParser } from "./markdownParser"
import { writeAsMarkdownParser } from "./markdownParser" ;
import { writeAsMarkdownSerializer } from "./markdownSerializer"
import { writeAsMarkdownSerializer } from "./markdownSerializer" ;
import { writeFreelySchema } from "./schema"
import { writeFreelySchema } from "./schema" ;
import { getMenu } from "./menu"
import { getMenu } from "./menu" ;
let $title = document . querySelector ( '#title' )
let $title = document . querySelector ( "#title" ) ;
let $content = document . querySelector ( '#content' )
let $content = document . querySelector ( "#content" ) ;
// Bugs:
// 1. When there's just an empty line and a hard break is inserted with shift-enter then two enters are inserted
// which do not show up in the markdown ( maybe bc. they are training enters )
class ProseMirrorView {
class ProseMirrorView {
constructor ( target , content ) {
constructor ( target , content ) {
this . view = new EditorView ( target , {
state : EditorState . create ( {
doc : function ( content ) {
// console.log('loading '+window.draftKey)
let localDraft = localStorage . getItem ( window . draftKey ) ;
let localDraft = localStorage . getItem ( window . draftKey ) ;
if ( localDraft != null ) {
if ( localDraft != null ) {
content = localDraft
content = localDraft ;
}
}
if ( content . indexOf ( "# " ) === 0 ) {
if ( content . indexOf ( "# " ) === 0 ) {
let eol = content . indexOf ( "\n" ) ;
let eol = content . indexOf ( "\n" ) ;
@ -38,8 +38,15 @@ class ProseMirrorView {
content = content . substring ( eol + "\n\n" . length ) ;
content = content . substring ( eol + "\n\n" . length ) ;
$title . value = title ;
$title . value = title ;
}
}
return writeAsMarkdownParser . parse ( content )
} ( content ) ,
const doc = writeAsMarkdownParser . parse (
// Replace all "solo" \n's with \\\n for correct markdown parsing
content . replaceAll ( /(?<!\n)\n(?!\n)/g , "\\\n" )
) ;
this . view = new EditorView ( target , {
state : EditorState . create ( {
doc ,
plugins : [
plugins : [
keymap ( {
keymap ( {
"Mod-Enter" : ( ) => {
"Mod-Enter" : ( ) => {
@ -47,51 +54,56 @@ class ProseMirrorView {
return true ;
return true ;
} ,
} ,
"Mod-k" : ( ) => {
"Mod-k" : ( ) => {
console . log ( "TODO-link" ) ;
const linkButton = document . querySelector ( ".ProseMirror-icon[title='Add or remove link']" )
linkButton . dispatchEvent ( new Event ( 'mousedown' ) ) ;
return true ;
return true ;
}
} ,
} ) ,
} ) ,
... exampleSetup ( { schema : writeFreelySchema , menuContent : getMenu ( ) } ) ,
... exampleSetup ( {
schema : writeFreelySchema ,
]
menuContent : getMenu ( ) ,
} ) ,
] ,
} ) ,
} ) ,
dispatchTransaction ( transaction ) {
dispatchTransaction ( transaction ) {
// console.log('saving to '+window.draftKey)
const newContent = writeAsMarkdownSerializer
const newContent = writeAsMarkdownSerializer . serialize ( transaction . doc )
. serialize ( transaction . doc )
console . log ( { newContent } )
// Replace all \\\ns ( not followed by a \n ) with \n
$content . value = newContent
. replaceAll ( /\\\n(?!\n)/g , "\n" ) ;
localStorage . setItem ( window . draftKey , function ( ) {
$content . value = newContent ;
let draft = "" ;
let draft = "" ;
if ( $title . value != null && $title . value !== "" ) {
if ( $title . value != null && $title . value !== "" ) {
draft = "# " + $title . value + "\n\n"
draft = "# " + $title . value + "\n\n" ;
}
}
draft += $content . value
draft += newContent ;
return draft
localStorage . setItem ( window . draftKey , draft ) ;
} ( ) ) ;
let newState = this . state . apply ( transaction ) ;
let newState = this . state . apply ( transaction )
this . updateState ( newState ) ;
this . updateState ( newState )
} ,
}
} ) ;
} )
// Editor is focused to the last position. This is a workaround for a bug:
// 1. 1 type something in an existing entry
// 2. reload - works fine, the draft is reloaded
// 3. reload again - the draft is somehow removed from localStorage and the original content is loaded
// When the editor is focused the content is re-saved to localStorage
// This is also useful for editing, so it's not a bad thing even
const lastPosition = this . view . state . doc . content . size ;
const selection = TextSelection . create ( this . view . state . doc , lastPosition ) ;
this . view . dispatch ( this . view . state . tr . setSelection ( selection ) ) ;
this . view . focus ( ) ;
}
}
get content ( ) {
get content ( ) {
return defaultMarkdownSerializer . serialize ( this . view . state . doc )
return defaultMarkdownSerializer . serialize ( this . view . state . doc ) ;
}
focus ( ) {
this . view . focus ( ) ;
}
destroy ( ) {
this . view . destroy ( ) ;
}
}
focus ( ) { this . view . focus ( ) }
destroy ( ) { this . view . destroy ( ) }
}
}
let place = document . querySelector ( "#editor" )
let place = document . querySelector ( "#editor" ) ;
let view = new ProseMirrorView ( place , $content . value )
let view = new ProseMirrorView ( place , $content . value ) ;
// document.querySelectorAll("input[type=radio]").forEach(button => {
// button.addEventListener("change", () => {
// if (!button.checked) return
// let View = button.value == "markdown" ? MarkdownView : ProseMirrorView
// if (view instanceof View) return
// let content = view.content
// view.destroy()
// view = new View(place, content)
// view.focus()
// })
// })