mirror of
https://0xacab.org/sutty/sutty
synced 2025-02-23 03:21:50 +00:00
resaltado y tirar error cuando el seralizer falla
This commit is contained in:
parent
33fafedcd9
commit
fb5623ba3e
7 changed files with 184 additions and 31 deletions
|
@ -30,9 +30,9 @@ import "prosemirror-gapcursor/style/gapcursor.css"
|
||||||
import {inputRules, wrappingInputRule, textblockTypeInputRule,
|
import {inputRules, wrappingInputRule, textblockTypeInputRule,
|
||||||
emDash, ellipsis} from "prosemirror-inputrules"
|
emDash, ellipsis} from "prosemirror-inputrules"
|
||||||
|
|
||||||
// markdown (commonmark de markdown-it)
|
import serializer from './serializer.js'
|
||||||
import {schema, defaultMarkdownParser,
|
import parser from './deserializer.js'
|
||||||
defaultMarkdownSerializer} from "prosemirror-markdown"
|
import schema from './schema.js'
|
||||||
|
|
||||||
import {toggleMark, setBlockType, wrapIn, baseKeymap} from "prosemirror-commands"
|
import {toggleMark, setBlockType, wrapIn, baseKeymap} from "prosemirror-commands"
|
||||||
import {toggleWrap, toggleBlockType, toggleList,
|
import {toggleWrap, toggleBlockType, toggleList,
|
||||||
|
@ -42,6 +42,7 @@ import {startImageUpload, placeholderPlugin} from "./imageUpload"
|
||||||
|
|
||||||
const toggleBold = toggleMark(schema.marks.strong)
|
const toggleBold = toggleMark(schema.marks.strong)
|
||||||
const toggleItalic = toggleMark(schema.marks.em)
|
const toggleItalic = toggleMark(schema.marks.em)
|
||||||
|
const toggleHighlight = toggleMark(schema.marks.mark)
|
||||||
const wrapInQuote = toggleWrap(schema.nodes.blockquote)
|
const wrapInQuote = toggleWrap(schema.nodes.blockquote)
|
||||||
|
|
||||||
const toggleUl = toggleList(schema.nodes.bullet_list, schema.nodes.list_item)
|
const toggleUl = toggleList(schema.nodes.bullet_list, schema.nodes.list_item)
|
||||||
|
@ -96,6 +97,7 @@ class SuttyEditor {
|
||||||
}
|
}
|
||||||
setupBtn('bold', () => this.runCommand(toggleBold))
|
setupBtn('bold', () => this.runCommand(toggleBold))
|
||||||
setupBtn('italic', () => this.runCommand(toggleItalic))
|
setupBtn('italic', () => this.runCommand(toggleItalic))
|
||||||
|
setupBtn('highlight', () => this.runCommand(toggleHighlight))
|
||||||
setupBtn('quote', () => this.runCommand(wrapInQuote))
|
setupBtn('quote', () => this.runCommand(wrapInQuote))
|
||||||
setupBtn('link', () => this.runCommand(makeLink))
|
setupBtn('link', () => this.runCommand(makeLink))
|
||||||
setupBtn('ul', () => this.runCommand(toggleUl))
|
setupBtn('ul', () => this.runCommand(toggleUl))
|
||||||
|
@ -121,7 +123,7 @@ class SuttyEditor {
|
||||||
return EditorState.create({
|
return EditorState.create({
|
||||||
schema,
|
schema,
|
||||||
...(markdown ? {
|
...(markdown ? {
|
||||||
doc: defaultMarkdownParser.parse(markdown)
|
doc: parser.parse(markdown)
|
||||||
} : {}),
|
} : {}),
|
||||||
plugins: [
|
plugins: [
|
||||||
gapCursor(),
|
gapCursor(),
|
||||||
|
@ -142,39 +144,49 @@ class SuttyEditor {
|
||||||
return new EditorView(element, {state})
|
return new EditorView(element, {state})
|
||||||
}
|
}
|
||||||
|
|
||||||
get markdown () {
|
getMarkdown () {
|
||||||
return defaultMarkdownSerializer.serialize(this.view.state.doc)
|
return serializer.serialize(this.view.state.doc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener('turbolinks:load', e => {
|
document.addEventListener('turbolinks:load', e => {
|
||||||
const editorEls = document.querySelectorAll('.sutty-editor-prosemirror')
|
try {
|
||||||
for (const el of editorEls) {
|
const editorEls = document.querySelectorAll('.sutty-editor-prosemirror')
|
||||||
// conseguir textarea con el markdown
|
for (const el of editorEls) {
|
||||||
const textareaEl = document.querySelector(
|
// conseguir textarea con el markdown
|
||||||
`.sutty-editor-content[data-sutty-identifier="${el.dataset.suttyIdentifier}"]`
|
const textareaEl = document.querySelector(
|
||||||
)
|
`.sutty-editor-content[data-sutty-identifier="${el.dataset.suttyIdentifier}"]`
|
||||||
|
)
|
||||||
|
|
||||||
// conseguir toolbar
|
// conseguir toolbar
|
||||||
const toolbarEl = document.querySelector(
|
const toolbarEl = document.querySelector(
|
||||||
`.sutty-editor-toolbar[data-sutty-identifier="${el.dataset.suttyIdentifier}"]`
|
`.sutty-editor-toolbar[data-sutty-identifier="${el.dataset.suttyIdentifier}"]`
|
||||||
)
|
)
|
||||||
|
|
||||||
// ocultarlo (no se oculta originalmente para usuaries noscript
|
// ocultarlo (no se oculta originalmente para usuaries noscript)
|
||||||
textareaEl.style.display = 'none'
|
textareaEl.style.display = 'none'
|
||||||
|
|
||||||
// crear editor con el markdown del textarea
|
// crear editor con el markdown del textarea
|
||||||
const editor = new SuttyEditor({
|
const editor = new SuttyEditor({
|
||||||
element: el,
|
element: el,
|
||||||
markdown: textareaEl.value,
|
markdown: textareaEl.value,
|
||||||
toolbar: toolbarEl,
|
toolbar: toolbarEl,
|
||||||
})
|
})
|
||||||
|
|
||||||
// hookear a la form para inyectar el contenido del editor al textarea oculto para que se mande
|
// hookear a la form para inyectar el contenido del editor al textarea oculto para que se mande
|
||||||
textareaEl.form.addEventListener('submit', e => {
|
textareaEl.form.addEventListener('submit', e => {
|
||||||
console.log('enviando formulario: inyectando markdown en textarea...')
|
console.log('enviando formulario: inyectando markdown en textarea...')
|
||||||
textareaEl.value = editor.markdown
|
try {
|
||||||
}, true)
|
textareaEl.value = editor.getMarkdown()
|
||||||
|
} catch (error) {
|
||||||
|
console.error('no se pudo inyectar markdown!', error)
|
||||||
|
// TODO: mostrar esto bien
|
||||||
|
e.preventDefault()
|
||||||
|
}
|
||||||
|
}, true)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('algo falló en la carga del editor', error)
|
||||||
}
|
}
|
||||||
|
|
||||||
const table = document.querySelector('.table-draggable')
|
const table = document.querySelector('.table-draggable')
|
||||||
|
|
37
app/javascript/packs/deserializer.js
Normal file
37
app/javascript/packs/deserializer.js
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
// From https://github.com/ProseMirror/prosemirror-markdown/blob/master/src/from_markdown.js
|
||||||
|
import {MarkdownParser} from 'prosemirror-markdown'
|
||||||
|
import markdownit from "markdown-it"
|
||||||
|
import markdownitMark from "markdown-it-mark"
|
||||||
|
|
||||||
|
import schema from './schema.js'
|
||||||
|
|
||||||
|
export default new MarkdownParser(
|
||||||
|
schema,
|
||||||
|
markdownit("commonmark", {html: false}).use(markdownitMark),
|
||||||
|
{
|
||||||
|
blockquote: {block: "blockquote"},
|
||||||
|
paragraph: {block: "paragraph"},
|
||||||
|
list_item: {block: "list_item"},
|
||||||
|
bullet_list: {block: "bullet_list"},
|
||||||
|
ordered_list: {block: "ordered_list", getAttrs: tok => ({order: +tok.attrGet("start") || 1})},
|
||||||
|
heading: {block: "heading", getAttrs: tok => ({level: +tok.tag.slice(1)})},
|
||||||
|
code_block: {block: "code_block"},
|
||||||
|
fence: {block: "code_block", getAttrs: tok => ({params: tok.info || ""})},
|
||||||
|
hr: {node: "horizontal_rule"},
|
||||||
|
image: {node: "image", getAttrs: tok => ({
|
||||||
|
src: tok.attrGet("src"),
|
||||||
|
title: tok.attrGet("title") || null,
|
||||||
|
alt: tok.children[0] && tok.children[0].content || null,
|
||||||
|
})},
|
||||||
|
hardbreak: {node: "hard_break"},
|
||||||
|
|
||||||
|
em: {mark: "em"},
|
||||||
|
strong: {mark: "strong"},
|
||||||
|
mark: {mark: "mark"},
|
||||||
|
link: {mark: "link", getAttrs: tok => ({
|
||||||
|
href: tok.attrGet("href"),
|
||||||
|
title: tok.attrGet("title") || null
|
||||||
|
})},
|
||||||
|
code_inline: {mark: "code"},
|
||||||
|
},
|
||||||
|
)
|
11
app/javascript/packs/schema.js
Normal file
11
app/javascript/packs/schema.js
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import {schema as defaultMarkdownSchema, defaultMarkdownParser} from "prosemirror-markdown"
|
||||||
|
import {Schema} from "prosemirror-model"
|
||||||
|
|
||||||
|
export default new Schema({
|
||||||
|
nodes: defaultMarkdownSchema.spec.nodes,
|
||||||
|
marks: defaultMarkdownSchema.spec.marks.addBefore('text', 'mark', {
|
||||||
|
toDOM: node => ['mark'],
|
||||||
|
parseDOM: [{ tag: 'mark' }],
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
74
app/javascript/packs/serializer.js
Normal file
74
app/javascript/packs/serializer.js
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
// From https://raw.githubusercontent.com/ProseMirror/prosemirror-markdown/master/src/to_markdown.js
|
||||||
|
import {MarkdownSerializer} from "prosemirror-markdown"
|
||||||
|
|
||||||
|
export default new MarkdownSerializer({
|
||||||
|
blockquote(state, node) {
|
||||||
|
state.wrapBlock("> ", null, node, () => state.renderContent(node))
|
||||||
|
},
|
||||||
|
code_block(state, node) {
|
||||||
|
state.write("```" + (node.attrs.params || "") + "\n")
|
||||||
|
state.text(node.textContent, false)
|
||||||
|
state.ensureNewLine()
|
||||||
|
state.write("```")
|
||||||
|
state.closeBlock(node)
|
||||||
|
},
|
||||||
|
heading(state, node) {
|
||||||
|
state.write(state.repeat("#", node.attrs.level) + " ")
|
||||||
|
state.renderInline(node)
|
||||||
|
state.closeBlock(node)
|
||||||
|
},
|
||||||
|
horizontal_rule(state, node) {
|
||||||
|
state.write(node.attrs.markup || "---")
|
||||||
|
state.closeBlock(node)
|
||||||
|
},
|
||||||
|
bullet_list(state, node) {
|
||||||
|
state.renderList(node, " ", () => (node.attrs.bullet || "*") + " ")
|
||||||
|
},
|
||||||
|
ordered_list(state, node) {
|
||||||
|
let start = node.attrs.order || 1
|
||||||
|
let maxW = String(start + node.childCount - 1).length
|
||||||
|
let space = state.repeat(" ", maxW + 2)
|
||||||
|
state.renderList(node, space, i => {
|
||||||
|
let nStr = String(start + i)
|
||||||
|
return state.repeat(" ", maxW - nStr.length) + nStr + ". "
|
||||||
|
})
|
||||||
|
},
|
||||||
|
list_item(state, node) {
|
||||||
|
state.renderContent(node)
|
||||||
|
},
|
||||||
|
paragraph(state, node) {
|
||||||
|
state.renderInline(node)
|
||||||
|
state.closeBlock(node)
|
||||||
|
},
|
||||||
|
|
||||||
|
image(state, node) {
|
||||||
|
state.write(" +
|
||||||
|
(node.attrs.title ? " " + state.quote(node.attrs.title) : "") + ")")
|
||||||
|
},
|
||||||
|
hard_break(state, node, parent, index) {
|
||||||
|
for (let i = index + 1; i < parent.childCount; i++)
|
||||||
|
if (parent.child(i).type != node.type) {
|
||||||
|
state.write("\\\n")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
},
|
||||||
|
text(state, node) {
|
||||||
|
state.text(node.text)
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
em: {open: "*", close: "*", mixable: true, expelEnclosingWhitespace: true},
|
||||||
|
strong: {open: "**", close: "**", mixable: true, expelEnclosingWhitespace: true},
|
||||||
|
mark: {open: "==", close: "==", mixable: true, expelEnclosingWhitespace: true},
|
||||||
|
link: {
|
||||||
|
open(_state, mark, parent, index) {
|
||||||
|
return isPlainURL(mark, parent, index, 1) ? "<" : "["
|
||||||
|
},
|
||||||
|
close(state, mark, parent, index) {
|
||||||
|
return isPlainURL(mark, parent, index, -1) ? ">"
|
||||||
|
: "](" + state.esc(mark.attrs.href) + (mark.attrs.title ? " " + state.quote(mark.attrs.title) : "") + ")"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
code: {open(_state, _mark, parent, index) { return backticksFor(parent.child(index), -1) },
|
||||||
|
close(_state, _mark, parent, index) { return backticksFor(parent.child(index - 1), 1) },
|
||||||
|
escape: false}
|
||||||
|
})
|
|
@ -2,6 +2,7 @@
|
||||||
.sutty-editor-toolbar{ 'data-sutty-identifier': identifier }
|
.sutty-editor-toolbar{ 'data-sutty-identifier': identifier }
|
||||||
%button.bold-btn= "bold"
|
%button.bold-btn= "bold"
|
||||||
%button.italic-btn= "italic"
|
%button.italic-btn= "italic"
|
||||||
|
%button.highlight-btn= "highlight"
|
||||||
%button.quote-btn= "quote"
|
%button.quote-btn= "quote"
|
||||||
%button.ul-btn= "ul"
|
%button.ul-btn= "ul"
|
||||||
%button.ol-btn= "ol"
|
%button.ol-btn= "ol"
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
"commonmark": "^0.29.0",
|
"commonmark": "^0.29.0",
|
||||||
"input-map": "https://0xacab.org/sutty/input-map.git",
|
"input-map": "https://0xacab.org/sutty/input-map.git",
|
||||||
"input-tag": "https://0xacab.org/sutty/input-tag.git",
|
"input-tag": "https://0xacab.org/sutty/input-tag.git",
|
||||||
|
"markdown-it": "^10.0.0",
|
||||||
|
"markdown-it-mark": "^3.0.0",
|
||||||
"prosemirror-commands": "^1.0.8",
|
"prosemirror-commands": "^1.0.8",
|
||||||
"prosemirror-gapcursor": "^1.0.4",
|
"prosemirror-gapcursor": "^1.0.4",
|
||||||
"prosemirror-inputrules": "^1.0.4",
|
"prosemirror-inputrules": "^1.0.4",
|
||||||
|
|
18
yarn.lock
18
yarn.lock
|
@ -2478,7 +2478,7 @@ enhanced-resolve@4.1.0, enhanced-resolve@^4.1.0:
|
||||||
memory-fs "^0.4.0"
|
memory-fs "^0.4.0"
|
||||||
tapable "^1.0.0"
|
tapable "^1.0.0"
|
||||||
|
|
||||||
entities@^2.0.0:
|
entities@^2.0.0, entities@~2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4"
|
resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4"
|
||||||
integrity sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==
|
integrity sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==
|
||||||
|
@ -4035,6 +4035,22 @@ map-visit@^1.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
object-visit "^1.0.0"
|
object-visit "^1.0.0"
|
||||||
|
|
||||||
|
markdown-it-mark@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/markdown-it-mark/-/markdown-it-mark-3.0.0.tgz#27c3e39ef3cc310b2dde5375082c9fa912983cda"
|
||||||
|
integrity sha512-HqMWeKfMMOu4zBO0emmxsoMWmbf2cPKZY1wP6FsTbKmicFfp5y4L3KXAsNeO1rM6NTJVOrNlLKMPjWzriBGspw==
|
||||||
|
|
||||||
|
markdown-it@^10.0.0:
|
||||||
|
version "10.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-10.0.0.tgz#abfc64f141b1722d663402044e43927f1f50a8dc"
|
||||||
|
integrity sha512-YWOP1j7UbDNz+TumYP1kpwnP0aEa711cJjrAQrzd0UXlbJfc5aAq0F/PZHjiioqDC1NKgvIMX+o+9Bk7yuM2dg==
|
||||||
|
dependencies:
|
||||||
|
argparse "^1.0.7"
|
||||||
|
entities "~2.0.0"
|
||||||
|
linkify-it "^2.0.0"
|
||||||
|
mdurl "^1.0.1"
|
||||||
|
uc.micro "^1.0.5"
|
||||||
|
|
||||||
markdown-it@^8.4.2:
|
markdown-it@^8.4.2:
|
||||||
version "8.4.2"
|
version "8.4.2"
|
||||||
resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-8.4.2.tgz#386f98998dc15a37722aa7722084f4020bdd9b54"
|
resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-8.4.2.tgz#386f98998dc15a37722aa7722084f4020bdd9b54"
|
||||||
|
|
Loading…
Reference in a new issue