mirror of https://github.com/writeas/writefreely
Removed unnecessary schema nodes, custom markdown parser/serializer, publish keyboard shortcut, readmore node
parent
a96d4474ef
commit
b1cea637cb
@ -0,0 +1,58 @@ |
|||||||
|
import { MarkdownParser } from "prosemirror-markdown"; |
||||||
|
import markdownit from "markdown-it"; |
||||||
|
|
||||||
|
import { writeFreelySchema } from "./schema"; |
||||||
|
|
||||||
|
export const writeAsMarkdownParser = new MarkdownParser( |
||||||
|
writeFreelySchema, |
||||||
|
markdownit("commonmark", { html: true }), |
||||||
|
{ |
||||||
|
// 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", noCloseToken: true }, |
||||||
|
fence: { |
||||||
|
block: "code_block", |
||||||
|
getAttrs: (tok) => ({ params: tok.info || "" }), |
||||||
|
noCloseToken: true, |
||||||
|
}, |
||||||
|
// hr: { node: "horizontal_rule" },
|
||||||
|
image: { |
||||||
|
node: "image", |
||||||
|
getAttrs: (tok) => ({ |
||||||
|
src: tok.attrGet("src"), |
||||||
|
title: tok.attrGet("title") || null, |
||||||
|
alt: tok.children?.[0].content || null, |
||||||
|
}), |
||||||
|
}, |
||||||
|
// hardbreak: { node: "hard_break" },
|
||||||
|
|
||||||
|
em: { mark: "em" }, |
||||||
|
strong: { mark: "strong" }, |
||||||
|
link: { |
||||||
|
mark: "link", |
||||||
|
getAttrs: (tok) => ({ |
||||||
|
href: tok.attrGet("href"), |
||||||
|
title: tok.attrGet("title") || null, |
||||||
|
}), |
||||||
|
}, |
||||||
|
code_inline: { mark: "code", noCloseToken: true }, |
||||||
|
html_block: { |
||||||
|
node: "readmore", |
||||||
|
getAttrs(token) { |
||||||
|
console.log({ token }); |
||||||
|
// TODO: Give different attributes depending on the token content
|
||||||
|
return {}; |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
); |
@ -0,0 +1,127 @@ |
|||||||
|
import { MarkdownSerializer } from "prosemirror-markdown"; |
||||||
|
|
||||||
|
function backticksFor(node, side) { |
||||||
|
const ticks = /`+/g; |
||||||
|
let m; |
||||||
|
let len = 0; |
||||||
|
if (node.isText) |
||||||
|
while ((m = ticks.exec(node.text))) len = Math.max(len, m[0].length); |
||||||
|
let result = len > 0 && side > 0 ? " `" : "`"; |
||||||
|
for (let i = 0; i < len; i++) result += "`"; |
||||||
|
if (len > 0 && side < 0) result += " "; |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
function isPlainURL(link, parent, index, side) { |
||||||
|
if (link.attrs.title || !/^\w+:/.test(link.attrs.href)) return false; |
||||||
|
const content = parent.child(index + (side < 0 ? -1 : 0)); |
||||||
|
if ( |
||||||
|
!content.isText || |
||||||
|
content.text != link.attrs.href || |
||||||
|
content.marks[content.marks.length - 1] != link |
||||||
|
) |
||||||
|
return false; |
||||||
|
if (index == (side < 0 ? 1 : parent.childCount - 1)) return true; |
||||||
|
const next = parent.child(index + (side < 0 ? -2 : 1)); |
||||||
|
return !link.isInSet(next.marks); |
||||||
|
} |
||||||
|
|
||||||
|
export const writeAsMarkdownSerializer = new MarkdownSerializer( |
||||||
|
{ |
||||||
|
readmore(state, node) { |
||||||
|
state.write("<!--more-->"); |
||||||
|
state.closeBlock(node); |
||||||
|
}, |
||||||
|
// blockquote(state, node) {
|
||||||
|
// state.wrapBlock("> ", undefined, 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) { |
||||||
|
const start = node.attrs.order || 1; |
||||||
|
const maxW = String(start + node.childCount - 1).length; |
||||||
|
const space = state.repeat(" ", maxW + 2); |
||||||
|
state.renderList(node, space, (i) => { |
||||||
|
const 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( |
||||||
|
`![${state.esc(node.attrs.alt || "")}](${state.esc(node.attrs.src)}${ |
||||||
|
node.attrs.title ? ` ${state.quote(node.attrs.title)}` : "" |
||||||
|
})`,
|
||||||
|
); |
||||||
|
}, |
||||||
|
// hard_break(state, node, parent, index) {
|
||||||
|
// for (let i = index + 1; i < parent.childCount; i += 1)
|
||||||
|
// 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, |
||||||
|
}, |
||||||
|
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, |
||||||
|
}, |
||||||
|
}, |
||||||
|
); |
@ -0,0 +1,26 @@ |
|||||||
|
import { MenuItem } from "prosemirror-menu"; |
||||||
|
import { buildMenuItems } from "prosemirror-example-setup"; |
||||||
|
|
||||||
|
import { writeFreelySchema } from "./schema"; |
||||||
|
|
||||||
|
function canInsert(state, nodeType, attrs) { |
||||||
|
let $from = state.selection.$from |
||||||
|
for (let d = $from.depth; d >= 0; d--) { |
||||||
|
let index = $from.index(d) |
||||||
|
if ($from.node(d).canReplaceWith(index, index, nodeType, attrs)) return true |
||||||
|
} |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
const ReadMoreItem = new MenuItem({ |
||||||
|
label: "Read more", |
||||||
|
select: (state) => canInsert(state, writeFreelySchema.nodes.readmore), |
||||||
|
run(state, dispatch) { |
||||||
|
dispatch(state.tr.replaceSelectionWith(writeFreelySchema.nodes.readmore.create())) |
||||||
|
}, |
||||||
|
}); |
||||||
|
|
||||||
|
export const getMenu = ()=> { |
||||||
|
const menuContent = [...buildMenuItems(writeFreelySchema).fullMenu, [ReadMoreItem]]; |
||||||
|
return menuContent |
||||||
|
} |
@ -0,0 +1,19 @@ |
|||||||
|
import { schema } from "prosemirror-markdown" |
||||||
|
import { Schema } from "prosemirror-model"; |
||||||
|
|
||||||
|
export const writeFreelySchema = new Schema({ |
||||||
|
nodes: schema.spec.nodes.remove("blockquote") |
||||||
|
.remove("horizontal_rule") |
||||||
|
.remove("hard_break") |
||||||
|
.addToEnd("readmore", { |
||||||
|
inline: false, |
||||||
|
content: "", |
||||||
|
group: "block", |
||||||
|
draggable: true, |
||||||
|
toDOM: (node) => ["div", { class: "editorreadmore", style: "width: 100%;text-align:center" }, "Read more..."], |
||||||
|
parseDOM: [{ tag: "div.editorreadmore" }], |
||||||
|
}), |
||||||
|
marks: schema.spec.marks, |
||||||
|
}); |
||||||
|
|
||||||
|
console.log({ writeFreelySchema }) |
File diff suppressed because one or more lines are too long
Loading…
Reference in new issue