From d32048fc6ead4f3229082fab587d93f6ed17cddc Mon Sep 17 00:00:00 2001 From: lin onetwo Date: Tue, 25 Mar 2025 22:53:14 +0800 Subject: [PATCH] lint: indent with tab --- .../prosemirror/ast/from-prosemirror.js | 118 +++---- .../prosemirror/ast/to-prosemirror.js | 294 +++++++++--------- .../prosemirror/setup/inputrules.js | 30 +- .../tiddlywiki/prosemirror/setup/keymap.js | 114 +++---- plugins/tiddlywiki/prosemirror/setup/menu.js | 248 +++++++-------- .../tiddlywiki/prosemirror/setup/prompt.js | 194 ++++++------ plugins/tiddlywiki/prosemirror/setup/setup.js | 32 +- .../tiddlywiki/prosemirror/widget-loader.js | 2 +- plugins/tiddlywiki/prosemirror/widget.js | 98 +++--- 9 files changed, 567 insertions(+), 563 deletions(-) diff --git a/plugins/tiddlywiki/prosemirror/ast/from-prosemirror.js b/plugins/tiddlywiki/prosemirror/ast/from-prosemirror.js index cfa4a66f6..9cc969a96 100644 --- a/plugins/tiddlywiki/prosemirror/ast/from-prosemirror.js +++ b/plugins/tiddlywiki/prosemirror/ast/from-prosemirror.js @@ -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 []; } diff --git a/plugins/tiddlywiki/prosemirror/ast/to-prosemirror.js b/plugins/tiddlywiki/prosemirror/ast/to-prosemirror.js index 14ecbfe67..aac4117b3 100644 --- a/plugins/tiddlywiki/prosemirror/ast/to-prosemirror.js +++ b/plugins/tiddlywiki/prosemirror/ast/to-prosemirror.js @@ -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 []; } diff --git a/plugins/tiddlywiki/prosemirror/setup/inputrules.js b/plugins/tiddlywiki/prosemirror/setup/inputrules.js index ff62bf375..425af3c51 100644 --- a/plugins/tiddlywiki/prosemirror/setup/inputrules.js +++ b/plugins/tiddlywiki/prosemirror/setup/inputrules.js @@ -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; diff --git a/plugins/tiddlywiki/prosemirror/setup/keymap.js b/plugins/tiddlywiki/prosemirror/setup/keymap.js index d5ad980fd..ddf683d78 100644 --- a/plugins/tiddlywiki/prosemirror/setup/keymap.js +++ b/plugins/tiddlywiki/prosemirror/setup/keymap.js @@ -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 }; diff --git a/plugins/tiddlywiki/prosemirror/setup/menu.js b/plugins/tiddlywiki/prosemirror/setup/menu.js index a25c456b7..e209cc4ab 100644 --- a/plugins/tiddlywiki/prosemirror/setup/menu.js +++ b/plugins/tiddlywiki/prosemirror/setup/menu.js @@ -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; diff --git a/plugins/tiddlywiki/prosemirror/setup/prompt.js b/plugins/tiddlywiki/prosemirror/setup/prompt.js index fb0669a22..2078ea51e 100644 --- a/plugins/tiddlywiki/prosemirror/setup/prompt.js +++ b/plugins/tiddlywiki/prosemirror/setup/prompt.js @@ -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; diff --git a/plugins/tiddlywiki/prosemirror/setup/setup.js b/plugins/tiddlywiki/prosemirror/setup/setup.js index 0affc0fe5..83efc2010 100644 --- a/plugins/tiddlywiki/prosemirror/setup/setup.js +++ b/plugins/tiddlywiki/prosemirror/setup/setup.js @@ -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; diff --git a/plugins/tiddlywiki/prosemirror/widget-loader.js b/plugins/tiddlywiki/prosemirror/widget-loader.js index 849c7c98c..498b2d387 100644 --- a/plugins/tiddlywiki/prosemirror/widget-loader.js +++ b/plugins/tiddlywiki/prosemirror/widget-loader.js @@ -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'); diff --git a/plugins/tiddlywiki/prosemirror/widget.js b/plugins/tiddlywiki/prosemirror/widget.js index bd2982060..3ca27809b 100644 --- a/plugins/tiddlywiki/prosemirror/widget.js +++ b/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