1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2025-04-06 18:56:56 +00:00

feat: remove prosemirror-example-setup, and allow Tab indent

* remove prosemirror-example-setup, move all configs here. And remove `prosemirror-schema-list` that it depends on.
* prosemirror-flat-list only add dot style to list container, not each list item. Make it looks weird. And when creating new list, it will make each item to be a full new list, which is also weird.
This commit is contained in:
lin onetwo 2025-03-24 16:49:14 +08:00
parent eecab2c0b8
commit 8c320892f5
10 changed files with 508 additions and 489 deletions

0
menu.js Normal file
View File

View File

@ -25,8 +25,3 @@ Downloaded from JsDelivr CDN (like `https://cdn.jsdelivr.net/npm/prosemirror-sta
* [ext[crelt|https://cdn.jsdelivr.net/npm/crelt@latest/dist/index.cjs]]: v1.0.6
* [ext[rope-sequence|https://cdn.jsdelivr.net/npm/rope-sequence@latest/dist/index.cjs]]: v1.3.4
* [ext[prosemirror-safari-ime-span|https://cdn.jsdelivr.net/npm/prosemirror-safari-ime-span@latest/dist/index.cjs]]: v1.0.2
!! TODO
* remove prosemirror-example-setup, move all configs here. And remove `prosemirror-schema-list` that it depends on.
* prosemirror-flat-list only add dot style to list container, not each list item. Make it looks weird. And when creating new list, it will make each item to be a full new list, which is also weird.

View File

@ -1,469 +0,0 @@
'use strict';
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); Object.defineProperty(subClass, "prototype", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); }
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); }
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); }
function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
var prosemirrorKeymap = require('prosemirror-keymap');
var prosemirrorHistory = require('prosemirror-history');
var prosemirrorCommands = require('prosemirror-commands');
var prosemirrorState = require('prosemirror-state');
var prosemirrorDropcursor = require('prosemirror-dropcursor');
var prosemirrorGapcursor = require('prosemirror-gapcursor');
var prosemirrorMenu = require('prosemirror-menu');
var prosemirrorSchemaList = require('prosemirror-schema-list');
var prosemirrorInputrules = require('prosemirror-inputrules');
var prefix = "ProseMirror-prompt";
function openPrompt(options) {
var wrapper = document.body.appendChild(document.createElement("div"));
wrapper.className = prefix;
var mouseOutside = function mouseOutside(e) {
if (!wrapper.contains(e.target)) close();
};
setTimeout(function () {
return window.addEventListener("mousedown", mouseOutside);
}, 50);
var close = function close() {
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 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 box = wrapper.getBoundingClientRect();
wrapper.style.top = (window.innerHeight - box.height) / 2 + "px";
wrapper.style.left = (window.innerWidth - box.width) / 2 + "px";
var submit = function submit() {
var params = getValues(options.fields, domFields);
if (params) {
close();
options.callback(params);
}
};
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);
}
});
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;
}
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 () {
return parent.removeChild(msg);
}, 1500);
}
var Field = function () {
function Field(options) {
_classCallCheck(this, Field);
this.options = options;
}
_createClass(Field, [{
key: "read",
value: function read(dom) {
return dom.value;
}
}, {
key: "validateType",
value: function validateType(value) {
return null;
}
}, {
key: "validate",
value: function validate(value) {
if (!value && this.options.required) return "Required field";
return this.validateType(value) || (this.options.validate ? this.options.validate(value) : null);
}
}, {
key: "clean",
value: function clean(value) {
return this.options.clean ? this.options.clean(value) : value;
}
}]);
return Field;
}();
var TextField = function (_Field) {
_inherits(TextField, _Field);
var _super = _createSuper(TextField);
function TextField() {
_classCallCheck(this, TextField);
return _super.apply(this, arguments);
}
_createClass(TextField, [{
key: "render",
value: function render() {
var input = document.createElement("input");
input.type = "text";
input.placeholder = this.options.label;
input.value = this.options.value || "";
input.autocomplete = "off";
return input;
}
}]);
return TextField;
}(Field);
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;
}
function insertImageItem(nodeType) {
return new prosemirrorMenu.MenuItem({
title: "Insert image",
label: "Image",
enable: function enable(state) {
return canInsert(state, nodeType);
},
run: function run(state, _, view) {
var _state$selection = state.selection,
from = _state$selection.from,
to = _state$selection.to,
attrs = null;
if (state.selection instanceof prosemirrorState.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 callback(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);
};
return new prosemirrorMenu.MenuItem(passedOptions);
}
function markActive(state, type) {
var _state$selection2 = state.selection,
from = _state$selection2.from,
$from = _state$selection2.$from,
to = _state$selection2.to,
empty = _state$selection2.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 active(state) {
return markActive(state, markType);
}
};
for (var prop in options) passedOptions[prop] = options[prop];
return cmdItem(prosemirrorCommands.toggleMark(markType), passedOptions);
}
function linkItem(markType) {
return new prosemirrorMenu.MenuItem({
title: "Add or remove link",
icon: prosemirrorMenu.icons.link,
active: function active(state) {
return markActive(state, markType);
},
enable: function enable(state) {
return !state.selection.empty;
},
run: function run(state, dispatch, view) {
if (markActive(state, markType)) {
prosemirrorCommands.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 callback(attrs) {
prosemirrorCommands.toggleMark(markType, attrs)(view.state, view.dispatch);
view.focus();
}
});
}
});
}
function wrapListItem(nodeType, options) {
return cmdItem(prosemirrorSchemaList.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: prosemirrorMenu.icons.strong
});
if (mark = schema.marks.em) r.toggleEm = markItem(mark, {
title: "Toggle emphasis",
icon: prosemirrorMenu.icons.em
});
if (mark = schema.marks.code) r.toggleCode = markItem(mark, {
title: "Toggle code font",
icon: prosemirrorMenu.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: prosemirrorMenu.icons.bulletList
});
if (node = schema.nodes.ordered_list) r.wrapOrderedList = wrapListItem(node, {
title: "Wrap in ordered list",
icon: prosemirrorMenu.icons.orderedList
});
if (node = schema.nodes.blockquote) r.wrapBlockQuote = prosemirrorMenu.wrapItem(node, {
title: "Wrap in block quote",
icon: prosemirrorMenu.icons.blockquote
});
if (node = schema.nodes.paragraph) r.makeParagraph = prosemirrorMenu.blockTypeItem(node, {
title: "Change to paragraph",
label: "Plain"
});
if (node = schema.nodes.code_block) r.makeCodeBlock = prosemirrorMenu.blockTypeItem(node, {
title: "Change to code block",
label: "Code"
});
if (node = schema.nodes.heading) for (var i = 1; i <= 10; i++) r["makeHead" + i] = prosemirrorMenu.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 prosemirrorMenu.MenuItem({
title: "Insert horizontal rule",
label: "Horizontal rule",
enable: function enable(state) {
return canInsert(state, hr);
},
run: function run(state, dispatch) {
dispatch(state.tr.replaceSelectionWith(hr.create()));
}
});
}
var cut = function cut(arr) {
return arr.filter(function (x) {
return x;
});
};
r.insertMenu = new prosemirrorMenu.Dropdown(cut([r.insertImage, r.insertHorizontalRule]), {
label: "Insert"
});
r.typeMenu = new prosemirrorMenu.Dropdown(cut([r.makeParagraph, r.makeCodeBlock, r.makeHead1 && new prosemirrorMenu.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, prosemirrorMenu.joinUpItem, prosemirrorMenu.liftItem, prosemirrorMenu.selectParentNodeItem])];
r.fullMenu = r.inlineMenu.concat([[r.insertMenu, r.typeMenu]], [[prosemirrorMenu.undoItem, prosemirrorMenu.redoItem]], r.blockMenu);
return r;
}
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;
}
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);
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.bullet_list) bind("Shift-Ctrl-8", prosemirrorSchemaList.wrapInList(type));
if (type = schema.nodes.ordered_list) bind("Shift-Ctrl-9", prosemirrorSchemaList.wrapInList(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_item) {
bind("Enter", prosemirrorSchemaList.splitListItem(type));
bind("Mod-[", prosemirrorSchemaList.liftListItem(type));
bind("Mod-]", prosemirrorSchemaList.sinkListItem(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;
}
function blockQuoteRule(nodeType) {
return prosemirrorInputrules.wrappingInputRule(/^\s*>\s$/, nodeType);
}
function orderedListRule(nodeType) {
return prosemirrorInputrules.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 prosemirrorInputrules.wrappingInputRule(/^\s*([-+*])\s$/, nodeType);
}
function codeBlockRule(nodeType) {
return prosemirrorInputrules.textblockTypeInputRule(/^```$/, nodeType);
}
function headingRule(nodeType, maxLevel) {
return prosemirrorInputrules.textblockTypeInputRule(new RegExp("^(#{1," + maxLevel + "})\\s$"), nodeType, function (match) {
return {
level: match[1].length
};
});
}
function buildInputRules(schema) {
var rules = prosemirrorInputrules.smartQuotes.concat(prosemirrorInputrules.ellipsis, prosemirrorInputrules.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 prosemirrorInputrules.inputRules({
rules: rules
});
}
function exampleSetup(options) {
var plugins = [buildInputRules(options.schema), prosemirrorKeymap.keymap(buildKeymap(options.schema, options.mapKeys)), prosemirrorKeymap.keymap(prosemirrorCommands.baseKeymap), prosemirrorDropcursor.dropCursor(), prosemirrorGapcursor.gapCursor()];
if (options.menuBar !== false) plugins.push(prosemirrorMenu.menuBar({
floating: options.floatingMenu !== false,
content: options.menuContent || buildMenuItems(options.schema).fullMenu
}));
if (options.history !== false) plugins.push(prosemirrorHistory.history());
return plugins.concat(new prosemirrorState.Plugin({
props: {
attributes: {
"class": "ProseMirror-example-setup-style"
}
}
}));
}
exports.buildInputRules = buildInputRules;
exports.buildKeymap = buildKeymap;
exports.buildMenuItems = buildMenuItems;
exports.exampleSetup = exampleSetup;

View File

@ -35,13 +35,6 @@
"title": "prosemirror-flat-list",
"module-type": "library"
}
},{
"file": "prosemirror-schema-list.cjs",
"fields": {
"type": "application/javascript",
"title": "prosemirror-schema-list",
"module-type": "library"
}
},{
"file": "prosemirror-flat-list.css",
"fields": {
@ -49,13 +42,6 @@
"title": "$:/plugins/tiddlywiki/prosemirror/lib/prosemirror-flat-list.css",
"tags": "[[$:/tags/Stylesheet]]"
}
},{
"file": "prosemirror-example-setup.cjs",
"fields": {
"type": "application/javascript",
"title": "prosemirror-example-setup",
"module-type": "library"
}
},{
"file": "prosemirror-schema-basic.cjs",
"fields": {

View File

@ -0,0 +1,44 @@
/*\
title: $:/plugins/tiddlywiki/prosemirror/setup/inputrules.js
type: application/javascript
module-type: library
\*/
"use strict";
var { inputRules, wrappingInputRule, textblockTypeInputRule, smartQuotes, emDash, ellipsis } = require("prosemirror-inputrules");
var { NodeType, Schema } = require("prosemirror-model");
function blockQuoteRule(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]; });
}
function bulletListRule(nodeType) {
return wrappingInputRule(/^\s*([-+*])\s$/, nodeType);
}
function codeBlockRule(nodeType) {
return textblockTypeInputRule(/^```$/, nodeType);
}
function headingRule(nodeType, maxLevel) {
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 });
}
exports.buildInputRules = buildInputRules;

View File

@ -0,0 +1,84 @@
/*\
title: $:/plugins/tiddlywiki/prosemirror/setup/keymap.js
type: application/javascript
module-type: library
\*/
"use strict";
var prosemirrorCommands = require("prosemirror-commands");
var prosemirrorFlatList = require("prosemirror-flat-list");
var prosemirrorHistory = require("prosemirror-history");
var prosemirrorInputrules = require("prosemirror-inputrules");
var prosemirrorState = require("prosemirror-state");
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;
}
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);
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;
}
module.exports = {
buildKeymap: buildKeymap
};

View File

@ -0,0 +1,171 @@
/*\
title: $:/plugins/tiddlywiki/prosemirror/setup/menu.js
type: application/javascript
module-type: library
\*/
"use strict";
var {
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");
var { toggleMark } = require("prosemirror-commands");
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;
}
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();
}
});
}
});
}
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); };
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);
}
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);
}
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();
}
});
}
});
}
function wrapListItem(nodeType, 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 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..."});
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;
}
exports.buildMenuItems = buildMenuItems;

View File

@ -0,0 +1,161 @@
/*\
title: $:/plugins/tiddlywiki/prosemirror/setup/prompt.js
type: application/javascript
module-type: library
\*/
"use strict";
var { Attrs } = require("prosemirror-model");
var prefix = "ProseMirror-prompt";
function openPrompt(options) {
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 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 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 submit = function() {
var params = getValues(options.fields, domFields);
if (params) {
close();
options.callback(params);
}
};
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);
}
});
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;
}
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);
}
function Field(options) {
this.options = options;
}
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);
};
Field.prototype.clean = function(value) {
return this.options.clean ? this.options.clean(value) : value;
};
function TextField(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;
};
function SelectField(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;
};
exports.openPrompt = openPrompt;
exports.Field = Field;
exports.TextField = TextField;
exports.SelectField = SelectField;

View File

@ -0,0 +1,47 @@
/*\
title: $:/plugins/tiddlywiki/prosemirror/setup/setup.js
type: application/javascript
module-type: library
\*/
"use strict";
var { keymap } = require("prosemirror-keymap");
var { history } = require("prosemirror-history");
var { baseKeymap } = require("prosemirror-commands");
var { Plugin } = require("prosemirror-state");
var { dropCursor } = require("prosemirror-dropcursor");
var { gapCursor } = require("prosemirror-gapcursor");
var { menuBar } = require("prosemirror-menu");
var { Schema } = require("prosemirror-model");
var { buildMenuItems } = require("$:/plugins/tiddlywiki/prosemirror/setup/menu.js");
var { buildKeymap } = require("$:/plugins/tiddlywiki/prosemirror/setup/keymap.js");
var { buildInputRules } = require("$:/plugins/tiddlywiki/prosemirror/setup/inputrules.js");
exports.buildMenuItems = buildMenuItems;
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());
return plugins.concat(new Plugin({
props: {
attributes: { class: "ProseMirror-example-setup-style" }
}
}));
}
exports.exampleSetup = exampleSetup;

View File

@ -22,7 +22,7 @@ var {
listInputRules,
listKeymap
} = require("prosemirror-flat-list");
var { exampleSetup } = require("prosemirror-example-setup");
var { exampleSetup } = require("$:/plugins/tiddlywiki/prosemirror/setup/setup.js");
var { keymap } = require("prosemirror-keymap");
var { inputRules } = require("prosemirror-inputrules");