1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2025-04-22 10:43:11 +00:00

lint: indent with tab

This commit is contained in:
lin onetwo 2025-03-25 22:53:14 +08:00
parent 348427733b
commit d32048fc6e
9 changed files with 567 additions and 563 deletions

@ -8,95 +8,95 @@ Get the Wiki AST from a Prosemirror AST
\*/
function doc(context, node) {
return convertNodes(context, node.content);
return convertNodes(context, node.content);
}
function paragraph(context, node) {
return {
type: "element",
tag: "p",
rule: "parseblock",
children: convertNodes(context, node.content)
};
return {
type: "element",
tag: "p",
rule: "parseblock",
children: convertNodes(context, node.content)
};
}
function text(context, node) {
return {
type: "text",
text: node.text
}
return {
type: "text",
text: node.text
}
}
function heading(context, node) {
return {
type: "element",
tag: "h" + node.attrs.level,
rule: "heading",
attributes: {
// TODO: restore class if any
},
children: convertNodes(context, node.content)
};
return {
type: "element",
tag: "h" + node.attrs.level,
rule: "heading",
attributes: {
// TODO: restore class if any
},
children: convertNodes(context, node.content)
};
}
function list(context, node) {
const listType = node.attrs && node.attrs.kind === "ordered" ? "ol" : "ul";
const listItems = node.content.map(item => {
return {
type: "element",
tag: "li",
children: convertANode(context, item)
};
});
return {
type: "element",
tag: listType,
rule: "list",
children: listItems
};
const listType = node.attrs && node.attrs.kind === "ordered" ? "ol" : "ul";
const listItems = node.content.map(item => {
return {
type: "element",
tag: "li",
children: convertANode(context, item)
};
});
return {
type: "element",
tag: listType,
rule: "list",
children: listItems
};
}
/**
* Key is `node.type`, value is node converter function.
*/
const builders = {
doc,
paragraph,
text,
heading,
list,
doc,
paragraph,
text,
heading,
list,
};
function wikiAstFromProseMirrorAst(input) {
return convertNodes(builders, Array.isArray(input) ? input : [input]);
return convertNodes(builders, Array.isArray(input) ? input : [input]);
}
exports.from = wikiAstFromProseMirrorAst;
function convertNodes(builders, nodes) {
if (nodes === undefined || nodes.length === 0) {
return [];
}
if (nodes === undefined || nodes.length === 0) {
return [];
}
return nodes.reduce((accumulator, node) => {
return [...accumulator, ...convertANode(builders, node)];
}, []);
return nodes.reduce((accumulator, node) => {
return [...accumulator, ...convertANode(builders, node)];
}, []);
}
function restoreMetadata(node) {
// TODO: restore attributes, orderedAttributes, isBlock
return {};
// TODO: restore attributes, orderedAttributes, isBlock
return {};
}
function convertANode(builders, node) {
var builder = builders[node.type];
if (typeof builder === 'function') {
var convertedNode = builder(builders, node);
var arrayOfNodes = (Array.isArray(convertedNode)
? convertedNode : [convertedNode]);
return arrayOfNodes.map((child) => ({ ...restoreMetadata(node), ...child }));
}
console.warn(`WikiAst get Unknown node type: ${JSON.stringify(node)}`);
return [];
var builder = builders[node.type];
if (typeof builder === 'function') {
var convertedNode = builder(builders, node);
var arrayOfNodes = (Array.isArray(convertedNode)
? convertedNode : [convertedNode]);
return arrayOfNodes.map((child) => ({ ...restoreMetadata(node), ...child }));
}
console.warn(`WikiAst get Unknown node type: ${JSON.stringify(node)}`);
return [];
}

@ -11,85 +11,85 @@ Get the Prosemirror AST from a Wiki AST
* Many node shares same type `element` in wikiAst, we need to distinguish them by tag.
*/
const elementBuilders = {
p: function(context, node) {
return {
type: "paragraph",
content: convertNodes(context, node.children)
};
},
h1: function(context, node) {
return {
type: "heading",
attrs: { level: 1 },
content: convertNodes(context, node.children)
};
},
h2: function(context, node) {
return {
type: "heading",
attrs: { level: 2 },
content: convertNodes(context, node.children)
};
},
h3: function(context, node) {
return {
type: "heading",
attrs: { level: 3 },
content: convertNodes(context, node.children)
};
},
h4: function(context, node) {
return {
type: "heading",
attrs: { level: 4 },
content: convertNodes(context, node.children)
};
},
h5: function(context, node) {
return {
type: "heading",
attrs: { level: 5 },
content: convertNodes(context, node.children)
};
},
h6: function(context, node) {
return {
type: "heading",
attrs: { level: 6 },
content: convertNodes(context, node.children)
};
},
ul: function(context, node) {
return {
type: "list",
attrs: {
kind: "bullet",
order: null,
checked: false,
collapsed: false
},
content: convertNodes(context, node.children)
};
},
ol: function(context, node) {
return {
type: "list",
attrs: {
kind: "ordered",
order: null,
checked: false,
collapsed: false
},
content: convertNodes(context, node.children)
};
},
li: function(context, node) {
// In ProseMirror, list items are converted to paragraphs or other block content
// directly under the list node, no special list_item type needed
const processedContent = convertNodes(context, node.children);
// Ensure content starts with a block element (typically paragraph)
return wrapTextNodesInParagraphs(context, processedContent);
}
p: function(context, node) {
return {
type: "paragraph",
content: convertNodes(context, node.children)
};
},
h1: function(context, node) {
return {
type: "heading",
attrs: { level: 1 },
content: convertNodes(context, node.children)
};
},
h2: function(context, node) {
return {
type: "heading",
attrs: { level: 2 },
content: convertNodes(context, node.children)
};
},
h3: function(context, node) {
return {
type: "heading",
attrs: { level: 3 },
content: convertNodes(context, node.children)
};
},
h4: function(context, node) {
return {
type: "heading",
attrs: { level: 4 },
content: convertNodes(context, node.children)
};
},
h5: function(context, node) {
return {
type: "heading",
attrs: { level: 5 },
content: convertNodes(context, node.children)
};
},
h6: function(context, node) {
return {
type: "heading",
attrs: { level: 6 },
content: convertNodes(context, node.children)
};
},
ul: function(context, node) {
return {
type: "list",
attrs: {
kind: "bullet",
order: null,
checked: false,
collapsed: false
},
content: convertNodes(context, node.children)
};
},
ol: function(context, node) {
return {
type: "list",
attrs: {
kind: "ordered",
order: null,
checked: false,
collapsed: false
},
content: convertNodes(context, node.children)
};
},
li: function(context, node) {
// In ProseMirror, list items are converted to paragraphs or other block content
// directly under the list node, no special list_item type needed
const processedContent = convertNodes(context, node.children);
// Ensure content starts with a block element (typically paragraph)
return wrapTextNodesInParagraphs(context, processedContent);
}
};
/**
@ -97,101 +97,101 @@ const elementBuilders = {
* ProseMirror requires list items to contain block content, not bare text
*/
function wrapTextNodesInParagraphs(context, nodes) {
if (!nodes || nodes.length === 0) {
return [];
}
if (!nodes || nodes.length === 0) {
return [];
}
const result = [];
let currentTextNodes = [];
const result = [];
let currentTextNodes = [];
function flushTextNodes() {
if (currentTextNodes.length > 0) {
result.push({
type: "paragraph",
content: currentTextNodes
});
currentTextNodes = [];
}
}
function flushTextNodes() {
if (currentTextNodes.length > 0) {
result.push({
type: "paragraph",
content: currentTextNodes
});
currentTextNodes = [];
}
}
nodes.forEach(node => {
// If it's a text node, collect it
if (node.type === "text") {
currentTextNodes.push(node);
} else {
// If we encounter a non-text node, flush any collected text nodes
flushTextNodes();
// Add the non-text node as is
result.push(node);
}
});
nodes.forEach(node => {
// If it's a text node, collect it
if (node.type === "text") {
currentTextNodes.push(node);
} else {
// If we encounter a non-text node, flush any collected text nodes
flushTextNodes();
// Add the non-text node as is
result.push(node);
}
});
// Flush any remaining text nodes
flushTextNodes();
// Flush any remaining text nodes
flushTextNodes();
return result;
return result;
}
function element(context, node) {
const builder = elementBuilders[node.tag];
if (builder) {
return builder(context, node);
} else {
console.warn(`Unknown element tag: ${node.tag}`);
return [];
}
const builder = elementBuilders[node.tag];
if (builder) {
return builder(context, node);
} else {
console.warn(`Unknown element tag: ${node.tag}`);
return [];
}
}
function text(context, node) {
return {
type: "text",
text: node.text
};
return {
type: "text",
text: node.text
};
}
/**
* Key is wikiAst node type, value is node converter function.
*/
const builders = {
element,
text
element,
text
};
function wikiAstToProsemirrorAst(node, options) {
const context = { ...builders, ...options };
const result = convertNodes(context, Array.isArray(node) ? node : [node]);
// Wrap in a doc if needed
if (result.length > 0 && result[0].type !== "doc") {
return {
type: "doc",
content: result
};
}
return result;
const context = { ...builders, ...options };
const result = convertNodes(context, Array.isArray(node) ? node : [node]);
// Wrap in a doc if needed
if (result.length > 0 && result[0].type !== "doc") {
return {
type: "doc",
content: result
};
}
return result;
}
exports.to = wikiAstToProsemirrorAst;
function convertNodes(context, nodes) {
if (nodes === undefined || nodes.length === 0) {
return [];
}
if (nodes === undefined || nodes.length === 0) {
return [];
}
return nodes.reduce((accumulator, node) => {
return [...accumulator, ...convertANode(context, node)];
}, []);
return nodes.reduce((accumulator, node) => {
return [...accumulator, ...convertANode(context, node)];
}, []);
}
function convertANode(context, node) {
var builder = context[node.type];
if (typeof builder === 'function') {
var convertedNode = builder(context, node);
var arrayOfNodes = (Array.isArray(convertedNode)
? convertedNode : [convertedNode]);
return arrayOfNodes;
}
console.warn(`ProseMirror get Unknown node type: ${JSON.stringify(node)}`);
return [];
var builder = context[node.type];
if (typeof builder === 'function') {
var convertedNode = builder(context, node);
var arrayOfNodes = (Array.isArray(convertedNode)
? convertedNode : [convertedNode]);
return arrayOfNodes;
}
console.warn(`ProseMirror get Unknown node type: ${JSON.stringify(node)}`);
return [];
}

@ -11,34 +11,38 @@ var { inputRules, wrappingInputRule, textblockTypeInputRule, smartQuotes, emDash
var { NodeType, Schema } = require("prosemirror-model");
function blockQuoteRule(nodeType) {
return wrappingInputRule(/^\s*>\s$/, nodeType);
return wrappingInputRule(/^\s*>\s$/, nodeType);
}
function orderedListRule(nodeType) {
return wrappingInputRule(/^(\d+)\.\s$/, nodeType, function(match) { return { order: +match[1] }; },
function(match, node) { return node.childCount + node.attrs.order == +match[1]; });
return wrappingInputRule(
/^(\d+)\.\s$/,
nodeType,
function(match) { return { order: +match[1] }; },
function(match, node) { return node.childCount + node.attrs.order == +match[1]; }
);
}
function bulletListRule(nodeType) {
return wrappingInputRule(/^\s*([-+*])\s$/, nodeType);
return wrappingInputRule(/^\s*([-+*])\s$/, nodeType);
}
function codeBlockRule(nodeType) {
return textblockTypeInputRule(/^```$/, nodeType);
return textblockTypeInputRule(/^```$/, nodeType);
}
function headingRule(nodeType, maxLevel) {
return textblockTypeInputRule(new RegExp("^(#{1," + maxLevel + "})\\s$"), nodeType, function(match) { return { level: match[1].length }; });
return textblockTypeInputRule(new RegExp("^(#{1," + maxLevel + "})\\s$"), nodeType, function(match) { return { level: match[1].length }; });
}
function buildInputRules(schema) {
var rules = smartQuotes.concat(ellipsis, emDash), type;
if (type = schema.nodes.blockquote) rules.push(blockQuoteRule(type));
if (type = schema.nodes.ordered_list) rules.push(orderedListRule(type));
if (type = schema.nodes.bullet_list) rules.push(bulletListRule(type));
if (type = schema.nodes.code_block) rules.push(codeBlockRule(type));
if (type = schema.nodes.heading) rules.push(headingRule(type, 6));
return inputRules({ rules: rules });
var rules = smartQuotes.concat(ellipsis, emDash), type;
if (type = schema.nodes.blockquote) rules.push(blockQuoteRule(type));
if (type = schema.nodes.ordered_list) rules.push(orderedListRule(type));
if (type = schema.nodes.bullet_list) rules.push(bulletListRule(type));
if (type = schema.nodes.code_block) rules.push(codeBlockRule(type));
if (type = schema.nodes.heading) rules.push(headingRule(type, 6));
return inputRules({ rules: rules });
}
exports.buildInputRules = buildInputRules;

@ -17,68 +17,68 @@ var prosemirrorModel = require("prosemirror-model");
var mac = typeof navigator != "undefined" ? /Mac|iP(hone|[oa]d)/.test(navigator.platform) : false;
function buildKeymap(schema, mapKeys) {
var keys = {}, type;
function bind(key, cmd) {
if (mapKeys) {
var mapped = mapKeys[key];
if (mapped === false) return;
if (mapped) key = mapped;
}
keys[key] = cmd;
}
var keys = {}, type;
function bind(key, cmd) {
if (mapKeys) {
var mapped = mapKeys[key];
if (mapped === false) return;
if (mapped) key = mapped;
}
keys[key] = cmd;
}
bind("Mod-z", prosemirrorHistory.undo);
bind("Shift-Mod-z", prosemirrorHistory.redo);
bind("Backspace", prosemirrorInputrules.undoInputRule);
if (!mac) bind("Mod-y", prosemirrorHistory.redo);
bind("Mod-z", prosemirrorHistory.undo);
bind("Shift-Mod-z", prosemirrorHistory.redo);
bind("Backspace", prosemirrorInputrules.undoInputRule);
if (!mac) bind("Mod-y", prosemirrorHistory.redo);
bind("Alt-ArrowUp", prosemirrorCommands.joinUp);
bind("Alt-ArrowDown", prosemirrorCommands.joinDown);
bind("Mod-BracketLeft", prosemirrorCommands.lift);
bind("Escape", prosemirrorCommands.selectParentNode);
bind("Alt-ArrowUp", prosemirrorCommands.joinUp);
bind("Alt-ArrowDown", prosemirrorCommands.joinDown);
bind("Mod-BracketLeft", prosemirrorCommands.lift);
bind("Escape", prosemirrorCommands.selectParentNode);
if (type = schema.marks.strong) {
bind("Mod-b", prosemirrorCommands.toggleMark(type));
bind("Mod-B", prosemirrorCommands.toggleMark(type));
}
if (type = schema.marks.em) {
bind("Mod-i", prosemirrorCommands.toggleMark(type));
bind("Mod-I", prosemirrorCommands.toggleMark(type));
}
if (type = schema.marks.code)
bind("Mod-`", prosemirrorCommands.toggleMark(type));
if (type = schema.nodes.blockquote)
bind("Ctrl->", prosemirrorCommands.wrapIn(type));
if (type = schema.nodes.hard_break) {
var br = type, cmd = prosemirrorCommands.chainCommands(prosemirrorCommands.exitCode, function(state, dispatch) {
if (dispatch) dispatch(state.tr.replaceSelectionWith(br.create()).scrollIntoView());
return true;
});
bind("Mod-Enter", cmd);
bind("Shift-Enter", cmd);
if (mac) bind("Ctrl-Enter", cmd);
}
if (type = schema.nodes.list) {
bind("Shift-Tab", prosemirrorFlatList.createDedentListCommand(type));
bind("Tab", prosemirrorFlatList.createIndentListCommand(type));
}
if (type = schema.nodes.paragraph)
bind("Shift-Ctrl-0", prosemirrorCommands.setBlockType(type));
if (type = schema.nodes.code_block)
bind("Shift-Ctrl-\\", prosemirrorCommands.setBlockType(type));
if (type = schema.nodes.heading)
for (var i = 1; i <= 6; i++) bind("Shift-Ctrl-" + i, prosemirrorCommands.setBlockType(type, {level: i}));
if (type = schema.nodes.horizontal_rule) {
var hr = type;
bind("Mod-_", function(state, dispatch) {
if (dispatch) dispatch(state.tr.replaceSelectionWith(hr.create()).scrollIntoView());
return true;
});
}
if (type = schema.marks.strong) {
bind("Mod-b", prosemirrorCommands.toggleMark(type));
bind("Mod-B", prosemirrorCommands.toggleMark(type));
}
if (type = schema.marks.em) {
bind("Mod-i", prosemirrorCommands.toggleMark(type));
bind("Mod-I", prosemirrorCommands.toggleMark(type));
}
if (type = schema.marks.code)
bind("Mod-`", prosemirrorCommands.toggleMark(type));
if (type = schema.nodes.blockquote)
bind("Ctrl->", prosemirrorCommands.wrapIn(type));
if (type = schema.nodes.hard_break) {
var br = type, cmd = prosemirrorCommands.chainCommands(prosemirrorCommands.exitCode, function(state, dispatch) {
if (dispatch) dispatch(state.tr.replaceSelectionWith(br.create()).scrollIntoView());
return true;
});
bind("Mod-Enter", cmd);
bind("Shift-Enter", cmd);
if (mac) bind("Ctrl-Enter", cmd);
}
if (type = schema.nodes.list) {
bind("Shift-Tab", prosemirrorFlatList.createDedentListCommand(type));
bind("Tab", prosemirrorFlatList.createIndentListCommand(type));
}
if (type = schema.nodes.paragraph)
bind("Shift-Ctrl-0", prosemirrorCommands.setBlockType(type));
if (type = schema.nodes.code_block)
bind("Shift-Ctrl-\\", prosemirrorCommands.setBlockType(type));
if (type = schema.nodes.heading)
for (var i = 1; i <= 6; i++) bind("Shift-Ctrl-" + i, prosemirrorCommands.setBlockType(type, {level: i}));
if (type = schema.nodes.horizontal_rule) {
var hr = type;
bind("Mod-_", function(state, dispatch) {
if (dispatch) dispatch(state.tr.replaceSelectionWith(hr.create()).scrollIntoView());
return true;
});
}
return keys;
return keys;
}
module.exports = {
buildKeymap: buildKeymap
buildKeymap: buildKeymap
};

@ -8,18 +8,18 @@ module-type: library
"use strict";
var {
wrapItem,
blockTypeItem,
Dropdown,
DropdownSubmenu,
joinUpItem,
liftItem,
selectParentNodeItem,
undoItem,
redoItem,
icons,
MenuItem,
MenuElement
wrapItem,
blockTypeItem,
Dropdown,
DropdownSubmenu,
joinUpItem,
liftItem,
selectParentNodeItem,
undoItem,
redoItem,
icons,
MenuItem,
MenuElement
} = require("prosemirror-menu");
var { NodeSelection, EditorState } = require("prosemirror-state");
var { Schema, NodeType, MarkType } = require("prosemirror-model");
@ -28,144 +28,144 @@ var { wrapInList } = require("prosemirror-flat-list");
var { TextField, openPrompt } = require("$:/plugins/tiddlywiki/prosemirror/setup/prompt.js");
function canInsert(state, nodeType) {
var $from = state.selection.$from;
for (var d = $from.depth; d >= 0; d--) {
var index = $from.index(d);
if ($from.node(d).canReplaceWith(index, index, nodeType)) return true;
}
return false;
var $from = state.selection.$from;
for (var d = $from.depth; d >= 0; d--) {
var index = $from.index(d);
if ($from.node(d).canReplaceWith(index, index, nodeType)) return true;
}
return false;
}
function insertImageItem(nodeType) {
return new MenuItem({
title: "Insert image",
label: "Image",
enable: function(state) { return canInsert(state, nodeType); },
run: function(state, _, view) {
var from = state.selection.from, to = state.selection.to, attrs = null;
if (state.selection instanceof NodeSelection && state.selection.node.type == nodeType)
attrs = state.selection.node.attrs;
openPrompt({
title: "Insert image",
fields: {
src: new TextField({label: "Location", required: true, value: attrs && attrs.src}),
title: new TextField({label: "Title", value: attrs && attrs.title}),
alt: new TextField({label: "Description", value: attrs ? attrs.alt : state.doc.textBetween(from, to, " ")})
},
callback: function(attrs) {
view.dispatch(view.state.tr.replaceSelectionWith(nodeType.createAndFill(attrs)));
view.focus();
}
});
}
});
return new MenuItem({
title: "Insert image",
label: "Image",
enable: function(state) { return canInsert(state, nodeType); },
run: function(state, _, view) {
var from = state.selection.from, to = state.selection.to, attrs = null;
if (state.selection instanceof NodeSelection && state.selection.node.type == nodeType)
attrs = state.selection.node.attrs;
openPrompt({
title: "Insert image",
fields: {
src: new TextField({label: "Location", required: true, value: attrs && attrs.src}),
title: new TextField({label: "Title", value: attrs && attrs.title}),
alt: new TextField({label: "Description", value: attrs ? attrs.alt : state.doc.textBetween(from, to, " ")})
},
callback: function(attrs) {
view.dispatch(view.state.tr.replaceSelectionWith(nodeType.createAndFill(attrs)));
view.focus();
}
});
}
});
}
function cmdItem(cmd, options) {
var passedOptions = {
label: options.title,
run: cmd
};
for (var prop in options) passedOptions[prop] = options[prop];
if (!options.enable && !options.select)
passedOptions[options.enable ? "enable" : "select"] = function(state) { return cmd(state); };
var passedOptions = {
label: options.title,
run: cmd
};
for (var prop in options) passedOptions[prop] = options[prop];
if (!options.enable && !options.select)
passedOptions[options.enable ? "enable" : "select"] = function(state) { return cmd(state); };
return new MenuItem(passedOptions);
return new MenuItem(passedOptions);
}
function markActive(state, type) {
var from = state.selection.from, $from = state.selection.$from, to = state.selection.to, empty = state.selection.empty;
if (empty) return !!type.isInSet(state.storedMarks || $from.marks());
else return state.doc.rangeHasMark(from, to, type);
var from = state.selection.from, $from = state.selection.$from, to = state.selection.to, empty = state.selection.empty;
if (empty) return !!type.isInSet(state.storedMarks || $from.marks());
else return state.doc.rangeHasMark(from, to, type);
}
function markItem(markType, options) {
var passedOptions = {
active: function(state) { return markActive(state, markType); }
};
for (var prop in options) passedOptions[prop] = options[prop];
return cmdItem(toggleMark(markType), passedOptions);
var passedOptions = {
active: function(state) { return markActive(state, markType); }
};
for (var prop in options) passedOptions[prop] = options[prop];
return cmdItem(toggleMark(markType), passedOptions);
}
function linkItem(markType) {
return new MenuItem({
title: "Add or remove link",
icon: icons.link,
active: function(state) { return markActive(state, markType); },
enable: function(state) { return !state.selection.empty; },
run: function(state, dispatch, view) {
if (markActive(state, markType)) {
toggleMark(markType)(state, dispatch);
return true;
}
openPrompt({
title: "Create a link",
fields: {
href: new TextField({label: "Link target", required: true}),
title: new TextField({label: "Title"})
},
callback: function(attrs) {
toggleMark(markType, attrs)(view.state, view.dispatch);
view.focus();
}
});
}
});
return new MenuItem({
title: "Add or remove link",
icon: icons.link,
active: function(state) { return markActive(state, markType); },
enable: function(state) { return !state.selection.empty; },
run: function(state, dispatch, view) {
if (markActive(state, markType)) {
toggleMark(markType)(state, dispatch);
return true;
}
openPrompt({
title: "Create a link",
fields: {
href: new TextField({label: "Link target", required: true}),
title: new TextField({label: "Title"})
},
callback: function(attrs) {
toggleMark(markType, attrs)(view.state, view.dispatch);
view.focus();
}
});
}
});
}
function wrapListItem(nodeType, options) {
return cmdItem(wrapInList(nodeType, options.attrs), options);
return cmdItem(wrapInList(nodeType, options.attrs), options);
}
function buildMenuItems(schema) {
var r = {};
var mark;
if (mark = schema.marks.strong)
r.toggleStrong = markItem(mark, {title: "Toggle strong style", icon: icons.strong});
if (mark = schema.marks.em)
r.toggleEm = markItem(mark, {title: "Toggle emphasis", icon: icons.em});
if (mark = schema.marks.code)
r.toggleCode = markItem(mark, {title: "Toggle code font", icon: icons.code});
if (mark = schema.marks.link)
r.toggleLink = linkItem(mark);
var r = {};
var mark;
if (mark = schema.marks.strong)
r.toggleStrong = markItem(mark, {title: "Toggle strong style", icon: icons.strong});
if (mark = schema.marks.em)
r.toggleEm = markItem(mark, {title: "Toggle emphasis", icon: icons.em});
if (mark = schema.marks.code)
r.toggleCode = markItem(mark, {title: "Toggle code font", icon: icons.code});
if (mark = schema.marks.link)
r.toggleLink = linkItem(mark);
var node;
if (node = schema.nodes.image)
r.insertImage = insertImageItem(node);
if (node = schema.nodes.bullet_list)
r.wrapBulletList = wrapListItem(node, {title: "Wrap in bullet list", icon: icons.bulletList});
if (node = schema.nodes.ordered_list)
r.wrapOrderedList = wrapListItem(node, {title: "Wrap in ordered list", icon: icons.orderedList});
if (node = schema.nodes.blockquote)
r.wrapBlockQuote = wrapItem(node, {title: "Wrap in block quote", icon: icons.blockquote});
if (node = schema.nodes.paragraph)
r.makeParagraph = blockTypeItem(node, {title: "Change to paragraph", label: "Plain"});
if (node = schema.nodes.code_block)
r.makeCodeBlock = blockTypeItem(node, {title: "Change to code block", label: "Code"});
if (node = schema.nodes.heading)
for (var i = 1; i <= 10; i++)
r["makeHead" + i] = blockTypeItem(node, {title: "Change to heading " + i, label: "Level " + i, attrs: {level: i}});
if (node = schema.nodes.horizontal_rule) {
var hr = node;
r.insertHorizontalRule = new MenuItem({
title: "Insert horizontal rule",
label: "Horizontal rule",
enable: function(state) { return canInsert(state, hr); },
run: function(state, dispatch) { dispatch(state.tr.replaceSelectionWith(hr.create())); }
});
}
var node;
if (node = schema.nodes.image)
r.insertImage = insertImageItem(node);
if (node = schema.nodes.bullet_list)
r.wrapBulletList = wrapListItem(node, {title: "Wrap in bullet list", icon: icons.bulletList});
if (node = schema.nodes.ordered_list)
r.wrapOrderedList = wrapListItem(node, {title: "Wrap in ordered list", icon: icons.orderedList});
if (node = schema.nodes.blockquote)
r.wrapBlockQuote = wrapItem(node, {title: "Wrap in block quote", icon: icons.blockquote});
if (node = schema.nodes.paragraph)
r.makeParagraph = blockTypeItem(node, {title: "Change to paragraph", label: "Plain"});
if (node = schema.nodes.code_block)
r.makeCodeBlock = blockTypeItem(node, {title: "Change to code block", label: "Code"});
if (node = schema.nodes.heading)
for (var i = 1; i <= 10; i++)
r["makeHead" + i] = blockTypeItem(node, {title: "Change to heading " + i, label: "Level " + i, attrs: {level: i}});
if (node = schema.nodes.horizontal_rule) {
var hr = node;
r.insertHorizontalRule = new MenuItem({
title: "Insert horizontal rule",
label: "Horizontal rule",
enable: function(state) { return canInsert(state, hr); },
run: function(state, dispatch) { dispatch(state.tr.replaceSelectionWith(hr.create())); }
});
}
var cut = function(arr) { return arr.filter(function(x) { return x; }); };
r.insertMenu = new Dropdown(cut([r.insertImage, r.insertHorizontalRule]), {label: "Insert"});
r.typeMenu = new Dropdown(cut([r.makeParagraph, r.makeCodeBlock, r.makeHead1 && new DropdownSubmenu(cut([
r.makeHead1, r.makeHead2, r.makeHead3, r.makeHead4, r.makeHead5, r.makeHead6
]), {label: "Heading"})]), {label: "Type..."});
var cut = function(arr) { return arr.filter(function(x) { return x; }); };
r.insertMenu = new Dropdown(cut([r.insertImage, r.insertHorizontalRule]), {label: "Insert"});
r.typeMenu = new Dropdown(cut([r.makeParagraph, r.makeCodeBlock, r.makeHead1 && new DropdownSubmenu(cut([
r.makeHead1, r.makeHead2, r.makeHead3, r.makeHead4, r.makeHead5, r.makeHead6
]), {label: "Heading"})]), {label: "Type..."});
r.inlineMenu = [cut([r.toggleStrong, r.toggleEm, r.toggleCode, r.toggleLink])];
r.blockMenu = [cut([r.wrapBulletList, r.wrapOrderedList, r.wrapBlockQuote, joinUpItem, liftItem, selectParentNodeItem])];
r.fullMenu = r.inlineMenu.concat([[r.insertMenu, r.typeMenu]], [[undoItem, redoItem]], r.blockMenu);
r.inlineMenu = [cut([r.toggleStrong, r.toggleEm, r.toggleCode, r.toggleLink])];
r.blockMenu = [cut([r.wrapBulletList, r.wrapOrderedList, r.wrapBlockQuote, joinUpItem, liftItem, selectParentNodeItem])];
r.fullMenu = r.inlineMenu.concat([[r.insertMenu, r.typeMenu]], [[undoItem, redoItem]], r.blockMenu);
return r;
return r;
}
exports.buildMenuItems = buildMenuItems;

@ -12,101 +12,101 @@ var { Attrs } = require("prosemirror-model");
var prefix = "ProseMirror-prompt";
function openPrompt(options) {
var wrapper = document.body.appendChild(document.createElement("div"));
wrapper.className = prefix;
var wrapper = document.body.appendChild(document.createElement("div"));
wrapper.className = prefix;
var mouseOutside = function(e) { if (!wrapper.contains(e.target)) close(); };
setTimeout(function() { window.addEventListener("mousedown", mouseOutside); }, 50);
var close = function() {
window.removeEventListener("mousedown", mouseOutside);
if (wrapper.parentNode) wrapper.parentNode.removeChild(wrapper);
};
var mouseOutside = function(e) { if (!wrapper.contains(e.target)) close(); };
setTimeout(function() { window.addEventListener("mousedown", mouseOutside); }, 50);
var close = function() {
window.removeEventListener("mousedown", mouseOutside);
if (wrapper.parentNode) wrapper.parentNode.removeChild(wrapper);
};
var domFields = [];
for (var name in options.fields) domFields.push(options.fields[name].render());
var domFields = [];
for (var name in options.fields) domFields.push(options.fields[name].render());
var submitButton = document.createElement("button");
submitButton.type = "submit";
submitButton.className = prefix + "-submit";
submitButton.textContent = "OK";
var cancelButton = document.createElement("button");
cancelButton.type = "button";
cancelButton.className = prefix + "-cancel";
cancelButton.textContent = "Cancel";
cancelButton.addEventListener("click", close);
var submitButton = document.createElement("button");
submitButton.type = "submit";
submitButton.className = prefix + "-submit";
submitButton.textContent = "OK";
var cancelButton = document.createElement("button");
cancelButton.type = "button";
cancelButton.className = prefix + "-cancel";
cancelButton.textContent = "Cancel";
cancelButton.addEventListener("click", close);
var form = wrapper.appendChild(document.createElement("form"));
if (options.title) form.appendChild(document.createElement("h5")).textContent = options.title;
domFields.forEach(function(field) {
form.appendChild(document.createElement("div")).appendChild(field);
});
var buttons = form.appendChild(document.createElement("div"));
buttons.className = prefix + "-buttons";
buttons.appendChild(submitButton);
buttons.appendChild(document.createTextNode(" "));
buttons.appendChild(cancelButton);
var form = wrapper.appendChild(document.createElement("form"));
if (options.title) form.appendChild(document.createElement("h5")).textContent = options.title;
domFields.forEach(function(field) {
form.appendChild(document.createElement("div")).appendChild(field);
});
var buttons = form.appendChild(document.createElement("div"));
buttons.className = prefix + "-buttons";
buttons.appendChild(submitButton);
buttons.appendChild(document.createTextNode(" "));
buttons.appendChild(cancelButton);
var box = wrapper.getBoundingClientRect();
wrapper.style.top = ((window.innerHeight - box.height) / 2) + "px";
wrapper.style.left = ((window.innerWidth - box.width) / 2) + "px";
var box = wrapper.getBoundingClientRect();
wrapper.style.top = ((window.innerHeight - box.height) / 2) + "px";
wrapper.style.left = ((window.innerWidth - box.width) / 2) + "px";
var submit = function() {
var params = getValues(options.fields, domFields);
if (params) {
close();
options.callback(params);
}
};
var submit = function() {
var params = getValues(options.fields, domFields);
if (params) {
close();
options.callback(params);
}
};
form.addEventListener("submit", function(e) {
e.preventDefault();
submit();
});
form.addEventListener("submit", function(e) {
e.preventDefault();
submit();
});
form.addEventListener("keydown", function(e) {
if (e.keyCode == 27) {
e.preventDefault();
close();
} else if (e.keyCode == 13 && !(e.ctrlKey || e.metaKey || e.shiftKey)) {
e.preventDefault();
submit();
} else if (e.keyCode == 9) {
window.setTimeout(function() {
if (!wrapper.contains(document.activeElement)) close();
}, 500);
}
});
form.addEventListener("keydown", function(e) {
if (e.keyCode == 27) {
e.preventDefault();
close();
} else if (e.keyCode == 13 && !(e.ctrlKey || e.metaKey || e.shiftKey)) {
e.preventDefault();
submit();
} else if (e.keyCode == 9) {
window.setTimeout(function() {
if (!wrapper.contains(document.activeElement)) close();
}, 500);
}
});
var input = form.elements[0];
if (input) input.focus();
var input = form.elements[0];
if (input) input.focus();
}
function getValues(fields, domFields) {
var result = Object.create(null), i = 0;
for (var name in fields) {
var field = fields[name], dom = domFields[i++];
var value = field.read(dom), bad = field.validate(value);
if (bad) {
reportInvalid(dom, bad);
return null;
}
result[name] = field.clean(value);
}
return result;
var result = Object.create(null), i = 0;
for (var name in fields) {
var field = fields[name], dom = domFields[i++];
var value = field.read(dom), bad = field.validate(value);
if (bad) {
reportInvalid(dom, bad);
return null;
}
result[name] = field.clean(value);
}
return result;
}
function reportInvalid(dom, message) {
var parent = dom.parentNode;
var msg = parent.appendChild(document.createElement("div"));
msg.style.left = (dom.offsetLeft + dom.offsetWidth + 2) + "px";
msg.style.top = (dom.offsetTop - 5) + "px";
msg.className = "ProseMirror-invalid";
msg.textContent = message;
setTimeout(function() { parent.removeChild(msg); }, 1500);
var parent = dom.parentNode;
var msg = parent.appendChild(document.createElement("div"));
msg.style.left = (dom.offsetLeft + dom.offsetWidth + 2) + "px";
msg.style.top = (dom.offsetTop - 5) + "px";
msg.className = "ProseMirror-invalid";
msg.textContent = message;
setTimeout(function() { parent.removeChild(msg); }, 1500);
}
function Field(options) {
this.options = options;
this.options = options;
}
Field.prototype.read = function(dom) { return dom.value; };
@ -114,45 +114,45 @@ Field.prototype.read = function(dom) { return dom.value; };
Field.prototype.validateType = function(value) { return null; };
Field.prototype.validate = function(value) {
if (!value && this.options.required)
return "Required field";
return this.validateType(value) || (this.options.validate ? this.options.validate(value) : null);
if (!value && this.options.required)
return "Required field";
return this.validateType(value) || (this.options.validate ? this.options.validate(value) : null);
};
Field.prototype.clean = function(value) {
return this.options.clean ? this.options.clean(value) : value;
return this.options.clean ? this.options.clean(value) : value;
};
function TextField(options) {
Field.call(this, options);
Field.call(this, options);
}
TextField.prototype = Object.create(Field.prototype);
TextField.prototype.render = function() {
var input = document.createElement("input");
input.type = "text";
input.placeholder = this.options.label;
input.value = this.options.value || "";
input.autocomplete = "off";
return input;
var input = document.createElement("input");
input.type = "text";
input.placeholder = this.options.label;
input.value = this.options.value || "";
input.autocomplete = "off";
return input;
};
function SelectField(options) {
Field.call(this, options);
Field.call(this, options);
}
SelectField.prototype = Object.create(Field.prototype);
SelectField.prototype.render = function() {
var select = document.createElement("select");
this.options.options.forEach(function(o) {
var opt = select.appendChild(document.createElement("option"));
opt.value = o.value;
opt.selected = o.value == this.options.value;
opt.label = o.label;
}, this);
return select;
var select = document.createElement("select");
this.options.options.forEach(function(o) {
var opt = select.appendChild(document.createElement("option"));
opt.value = o.value;
opt.selected = o.value == this.options.value;
opt.label = o.label;
}, this);
return select;
};
exports.openPrompt = openPrompt;

@ -25,23 +25,23 @@ exports.buildKeymap = buildKeymap;
exports.buildInputRules = buildInputRules;
function exampleSetup(options) {
var plugins = [
buildInputRules(options.schema),
keymap(buildKeymap(options.schema, options.mapKeys)),
keymap(baseKeymap),
dropCursor(),
gapCursor()
];
if (options.menuBar !== false)
plugins.push(menuBar({ floating: options.floatingMenu !== false, content: options.menuContent || buildMenuItems(options.schema).fullMenu }));
if (options.history !== false)
plugins.push(history());
var plugins = [
buildInputRules(options.schema),
keymap(buildKeymap(options.schema, options.mapKeys)),
keymap(baseKeymap),
dropCursor(),
gapCursor()
];
if (options.menuBar !== false)
plugins.push(menuBar({ floating: options.floatingMenu !== false, content: options.menuContent || buildMenuItems(options.schema).fullMenu }));
if (options.history !== false)
plugins.push(history());
return plugins.concat(new Plugin({
props: {
attributes: { class: "ProseMirror-example-setup-style" }
}
}));
return plugins.concat(new Plugin({
props: {
attributes: { class: "ProseMirror-example-setup-style" }
}
}));
}
exports.exampleSetup = exampleSetup;

@ -6,7 +6,7 @@ module-type: widget
\*/
if (!$tw.browser) {
return;
return;
}
// separate the widget from the exports here, so we can skip the require of react code if `!$tw.browser`. Those ts code will error if loaded in the nodejs side.
const components = require('$:/plugins/tiddlywiki/prosemirror/widget.js');

@ -17,10 +17,10 @@ var { EditorView } = require("prosemirror-view");
var { Schema, DOMParser } = require("prosemirror-model");
var { schema: basicSchema } = require("prosemirror-schema-basic");
var {
createListPlugins,
createListSpec,
listInputRules,
listKeymap
createListPlugins,
createListSpec,
listInputRules,
listKeymap
} = require("prosemirror-flat-list");
var { exampleSetup } = require("$:/plugins/tiddlywiki/prosemirror/setup/setup.js");
var { keymap } = require("prosemirror-keymap");
@ -43,58 +43,58 @@ ProsemirrorWidget.prototype.render = function(parent,nextSibling) {
this.computeAttributes();
this.execute();
var tiddler = this.getAttribute("tiddler");
var initialText = this.wiki.getTiddlerText(tiddler, "");
var initialWikiAst = $tw.wiki.parseText(null, initialText).tree;
var doc = wikiAstToProseMirrorAst(initialWikiAst);
// DEBUG: console doc
console.log(`initial doc`, doc);
var tiddler = this.getAttribute("tiddler");
var initialText = this.wiki.getTiddlerText(tiddler, "");
var initialWikiAst = $tw.wiki.parseText(null, initialText).tree;
var doc = wikiAstToProseMirrorAst(initialWikiAst);
// DEBUG: console doc
console.log(`initial doc`, doc);
var container = $tw.utils.domMaker('div', {
class: 'tc-prosemirror-container',
});
var schema = new Schema({
nodes: basicSchema.spec.nodes.append({ list: createListSpec() }),
marks: basicSchema.spec.marks,
})
var listKeymapPlugin = keymap(listKeymap)
var listInputRulePlugin = inputRules({ rules: listInputRules })
var listPlugins = createListPlugins({ schema })
var container = $tw.utils.domMaker('div', {
class: 'tc-prosemirror-container',
});
var schema = new Schema({
nodes: basicSchema.spec.nodes.append({ list: createListSpec() }),
marks: basicSchema.spec.marks,
})
var listKeymapPlugin = keymap(listKeymap)
var listInputRulePlugin = inputRules({ rules: listInputRules })
var listPlugins = createListPlugins({ schema })
var self = this;
this.view = new EditorView(container, {
state: EditorState.create({
// doc: schema.node("doc", null, [schema.node("paragraph")]),
doc: schema.nodeFromJSON(doc),
plugins: [
listKeymapPlugin,
listInputRulePlugin,
...listPlugins,
...exampleSetup({ schema }),
],
}),
dispatchTransaction: function(transaction) {
var newState = self.view.state.apply(transaction);
self.view.updateState(newState);
self.debouncedSaveEditorContent();
}
})
var self = this;
this.view = new EditorView(container, {
state: EditorState.create({
// doc: schema.node("doc", null, [schema.node("paragraph")]),
doc: schema.nodeFromJSON(doc),
plugins: [
listKeymapPlugin,
listInputRulePlugin,
...listPlugins,
...exampleSetup({ schema }),
],
}),
dispatchTransaction: function(transaction) {
var newState = self.view.state.apply(transaction);
self.view.updateState(newState);
self.debouncedSaveEditorContent();
}
})
parent.insertBefore(container,nextSibling);
this.domNodes.push(container);
};
ProsemirrorWidget.prototype.saveEditorContent = function() {
var content = this.view.state.doc.toJSON();
console.log(`ProseMirror: ${JSON.stringify(content)}`, content);
var wikiast = wikiAstFromProseMirrorAst(content);
console.log(`WikiAST: ${JSON.stringify(wikiast)}`, wikiast);
var wikiText = $tw.utils.serializeParseTree(wikiast);
console.log(`WikiText: ${wikiText}`);
var tiddler = this.getAttribute("tiddler");
this.wiki.setText(tiddler, "text", undefined, wikiText);
var content = this.view.state.doc.toJSON();
console.log(`ProseMirror: ${JSON.stringify(content)}`, content);
var wikiast = wikiAstFromProseMirrorAst(content);
console.log(`WikiAST: ${JSON.stringify(wikiast)}`, wikiast);
var wikiText = $tw.utils.serializeParseTree(wikiast);
console.log(`WikiText: ${wikiText}`);
var tiddler = this.getAttribute("tiddler");
this.wiki.setText(tiddler, "text", undefined, wikiText);
}
// Debounced save function for performance