markupeditor 0.9.35 → 0.9.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config/behaviorconfig.json +2 -1
- package/dist/markup-editor.js +252 -13
- package/package.json +1 -1
package/dist/markup-editor.js
CHANGED
|
@@ -35476,6 +35476,96 @@ function testPasteTextPreprocessing(html) {
|
|
|
35476
35476
|
function testIsRecognizedLanguage(name) {
|
|
35477
35477
|
return isRecognizedLanguage(name);
|
|
35478
35478
|
}
|
|
35479
|
+
/**
|
|
35480
|
+
* For testing purposes, simulate typing `text` one character at a time
|
|
35481
|
+
* through the `handleTextInput` prop, so input rules (setup/inputrules.js)
|
|
35482
|
+
* fire as they do for live typing. Does not fire the DOM `input` event, so
|
|
35483
|
+
* `callbackInput` (markupeditor.js) is not exercised. Skips `\r`/`\n`
|
|
35484
|
+
* characters rather than inserting them, matching real typing, which never
|
|
35485
|
+
* routes a newline through `handleTextInput`.
|
|
35486
|
+
*
|
|
35487
|
+
* @param {string} text The text to type, one Unicode code point at a time.
|
|
35488
|
+
*/
|
|
35489
|
+
function testTypeText(text) {
|
|
35490
|
+
const view = activeView();
|
|
35491
|
+
for (const ch of text) {
|
|
35492
|
+
if (/[\r\n]/.test(ch)) continue
|
|
35493
|
+
const { from, to } = view.state.selection;
|
|
35494
|
+
const deflt = () => view.state.tr.insertText(ch, from, to);
|
|
35495
|
+
const handled = view.someProp('handleTextInput', f => f(view, from, to, ch, deflt));
|
|
35496
|
+
if (!handled) {
|
|
35497
|
+
view.dispatch(deflt());
|
|
35498
|
+
}
|
|
35499
|
+
}
|
|
35500
|
+
}
|
|
35501
|
+
// Modifier segments testTypeKey recognizes when parsing a keymap-style key
|
|
35502
|
+
// name. "Mod" (prosemirror-keymap's platform-dependent Cmd/Ctrl alias) is
|
|
35503
|
+
// deliberately excluded: it has no single fixed KeyboardEvent flag, and the
|
|
35504
|
+
// one scenario that needed testTypeKey (Backspace, no modifiers) never
|
|
35505
|
+
// required it.
|
|
35506
|
+
const _testTypeKeyModifiers = new Set(['Shift', 'Ctrl', 'Control', 'Alt', 'Cmd', 'Meta']);
|
|
35507
|
+
|
|
35508
|
+
// Reverse lookup from KeyboardEvent.key name to legacy keyCode, built from
|
|
35509
|
+
// w3c-keyname's public `base` table (keyCode -> name). Where more than one
|
|
35510
|
+
// keyCode maps to the same name (e.g. 10 and 13 both name "Enter"), the
|
|
35511
|
+
// higher/later entry wins, matching prosemirror-view's hardcoded
|
|
35512
|
+
// convention (its internal key-event helper in src/dom.ts pairs "Enter"
|
|
35513
|
+
// with 13 and "Backspace" with 8 at its call sites).
|
|
35514
|
+
const _testKeyCodeByName = Object.fromEntries(
|
|
35515
|
+
Object.entries(base).map(([code, name]) => [name, Number(code)])
|
|
35516
|
+
);
|
|
35517
|
+
|
|
35518
|
+
function _testKeyCodeFor(key) {
|
|
35519
|
+
return _testKeyCodeByName[key] ?? 0
|
|
35520
|
+
}
|
|
35521
|
+
|
|
35522
|
+
/**
|
|
35523
|
+
* For testing purposes, simulate a single keydown through the `handleKeyDown`
|
|
35524
|
+
* prop, so keymap bindings (setup/keymap.js) fire as they do for a live
|
|
35525
|
+
* keypress. Exercises only handleKeyDown-routed (keymap-bound) behavior: for
|
|
35526
|
+
* anything the browser would otherwise handle natively instead of a
|
|
35527
|
+
* registered binding claiming it (e.g. Backspace when there's no input-rule
|
|
35528
|
+
* state to undo, IME composition), this is a silent no-op — it returns
|
|
35529
|
+
* `false` and does nothing, unlike a real keypress.
|
|
35530
|
+
*
|
|
35531
|
+
* @param {string} key A keymap-style key name (e.g., "Backspace",
|
|
35532
|
+
* "Shift-Enter"): zero or more modifier prefixes
|
|
35533
|
+
* (Shift-, Ctrl-/Control-, Alt-, Cmd-/Meta-)
|
|
35534
|
+
* followed by a KeyboardEvent.key value.
|
|
35535
|
+
* @throws {Error} If `key` contains an unrecognized modifier
|
|
35536
|
+
* prefix, including the unsupported "Mod-".
|
|
35537
|
+
* @returns {boolean} Whether a handler returned `true` for the key.
|
|
35538
|
+
* NOT a reliable "the document changed" signal:
|
|
35539
|
+
* some commands (e.g. setStyleCommand,
|
|
35540
|
+
* toggleFormatCommand) dispatch without returning
|
|
35541
|
+
* `true`, so the document can still mutate while
|
|
35542
|
+
* this returns `false`.
|
|
35543
|
+
*/
|
|
35544
|
+
function testTypeKey(key) {
|
|
35545
|
+
const view = activeView();
|
|
35546
|
+
// Split like prosemirror-keymap's normalizeKeyName does, so a
|
|
35547
|
+
// trailing "-" (or a bare "-") is recognized as the literal key rather
|
|
35548
|
+
// than an empty string, e.g. "Ctrl--" -> ["Ctrl", "-"].
|
|
35549
|
+
const parts = key.split(/-(?!$)/);
|
|
35550
|
+
const eventKey = parts.pop();
|
|
35551
|
+
const modifiers = new Set(parts);
|
|
35552
|
+
for (const modifier of modifiers) {
|
|
35553
|
+
if (!_testTypeKeyModifiers.has(modifier)) {
|
|
35554
|
+
throw new Error(`testTypeKey: unrecognized modifier "${modifier}" in "${key}"`)
|
|
35555
|
+
}
|
|
35556
|
+
}
|
|
35557
|
+
const event = new KeyboardEvent('keydown', {
|
|
35558
|
+
key: eventKey,
|
|
35559
|
+
keyCode: _testKeyCodeFor(eventKey),
|
|
35560
|
+
shiftKey: modifiers.has('Shift'),
|
|
35561
|
+
ctrlKey: modifiers.has('Ctrl') || modifiers.has('Control'),
|
|
35562
|
+
altKey: modifiers.has('Alt'),
|
|
35563
|
+
metaKey: modifiers.has('Cmd') || modifiers.has('Meta'),
|
|
35564
|
+
bubbles: true,
|
|
35565
|
+
cancelable: true,
|
|
35566
|
+
});
|
|
35567
|
+
return view.someProp('handleKeyDown', f => f(view, event)) ?? false
|
|
35568
|
+
}
|
|
35479
35569
|
/**
|
|
35480
35570
|
* For testing purposes, invoke presentCodeLanguages on the active document.
|
|
35481
35571
|
*/
|
|
@@ -35533,6 +35623,19 @@ function insertLinkCommand(url) {
|
|
|
35533
35623
|
return commandAdapter;
|
|
35534
35624
|
}
|
|
35535
35625
|
|
|
35626
|
+
/**
|
|
35627
|
+
* Insert a link to the header identified by hTag ('H1'-'H6') and index (its position among
|
|
35628
|
+
* headers with that tag), assigning the header a unique id if it doesn't already have one.
|
|
35629
|
+
*
|
|
35630
|
+
* @param {string} hTag One of the strings `H1`-`H6`
|
|
35631
|
+
* @param {number} index Within existing elements with tag `hTag`, this is the index into them that is identified
|
|
35632
|
+
*/
|
|
35633
|
+
function insertInternalLink(hTag, index) {
|
|
35634
|
+
const view = activeView();
|
|
35635
|
+
let command = insertInternalLinkCommand(hTag, index);
|
|
35636
|
+
let result = command(view.state, view.dispatch, view);
|
|
35637
|
+
return result
|
|
35638
|
+
}
|
|
35536
35639
|
function insertInternalLinkCommand(hTag, index) {
|
|
35537
35640
|
const commandAdapter = (state, dispatch, view) => {
|
|
35538
35641
|
// Find the node matching hTag that is index into the nodes matching hTag
|
|
@@ -35647,12 +35750,34 @@ function headerMatching(hTag, index, state) {
|
|
|
35647
35750
|
}
|
|
35648
35751
|
}
|
|
35649
35752
|
|
|
35753
|
+
/**
|
|
35754
|
+
* Return a JSON-stringified array describing every header in the document, in document order
|
|
35755
|
+
* within each level. Each entry is `{hTag, index, text}`, where `hTag` and `index` identify the
|
|
35756
|
+
* header the same way `insertInternalLink(hTag, index)` and `idForInternalLinkCommand(hTag, index)` do.
|
|
35757
|
+
*
|
|
35758
|
+
* @returns {string} JSON-stringified array of `{hTag, index, text}`
|
|
35759
|
+
*/
|
|
35760
|
+
function getHeaders() {
|
|
35761
|
+
const view = activeView();
|
|
35762
|
+
const headersByLevel = headers(view.state);
|
|
35763
|
+
let result = [];
|
|
35764
|
+
for (let level = 1; level < 7; level++) {
|
|
35765
|
+
let hNodes = headersByLevel[level];
|
|
35766
|
+
if (hNodes) {
|
|
35767
|
+
hNodes.forEach((h, index) => {
|
|
35768
|
+
result.push({hTag: 'H' + level.toString(), index: index, text: h.node.textContent});
|
|
35769
|
+
});
|
|
35770
|
+
}
|
|
35771
|
+
}
|
|
35772
|
+
return JSON.stringify(result)
|
|
35773
|
+
}
|
|
35774
|
+
|
|
35650
35775
|
/**
|
|
35651
35776
|
* Return all the headers that exist in `state.doc` as arrays keyed by level.
|
|
35652
|
-
*
|
|
35777
|
+
*
|
|
35653
35778
|
* @ignore
|
|
35654
35779
|
* @param {EditorState} state The state of the ProseMirror editor
|
|
35655
|
-
*/
|
|
35780
|
+
*/
|
|
35656
35781
|
function headers(state) {
|
|
35657
35782
|
let headers = {};
|
|
35658
35783
|
let hType = state.schema.nodes.heading;
|
|
@@ -37009,12 +37134,14 @@ var selectImage = false;
|
|
|
37009
37134
|
var insertLink = false;
|
|
37010
37135
|
var insertImage = false;
|
|
37011
37136
|
var highlightCode = true;
|
|
37137
|
+
var markdownShorthand = true;
|
|
37012
37138
|
var behaviorConfig = {
|
|
37013
37139
|
focusAfterLoad: focusAfterLoad,
|
|
37014
37140
|
selectImage: selectImage,
|
|
37015
37141
|
insertLink: insertLink,
|
|
37016
37142
|
insertImage: insertImage,
|
|
37017
|
-
highlightCode: highlightCode
|
|
37143
|
+
highlightCode: highlightCode,
|
|
37144
|
+
markdownShorthand: markdownShorthand
|
|
37018
37145
|
};
|
|
37019
37146
|
|
|
37020
37147
|
/**
|
|
@@ -37053,7 +37180,9 @@ var behaviorConfig = {
|
|
|
37053
37180
|
* "selectImage": false, // Whether to show a "Select..." button in the Insert Image dialog
|
|
37054
37181
|
* "insertLink": false, // Whether to defer to the MarkupDelegate rather than use the default LinkDialog
|
|
37055
37182
|
* "insertImage": false, // Whether to defer to the MarkupDelagate rather than use the default ImageDialog
|
|
37056
|
-
* "highlightCode": true
|
|
37183
|
+
* "highlightCode": true, // Whether to highlight code blocks and support language identificaton in UI
|
|
37184
|
+
* "markdownShorthand": true // Whether typing markdown shorthand converts to formatting: block-level
|
|
37185
|
+
* // (`> `, list markers, ``` ``` ```, `#`) and inline (**, *, _, `, ~~)
|
|
37057
37186
|
* }
|
|
37058
37187
|
* ```
|
|
37059
37188
|
*/
|
|
@@ -40886,16 +41015,122 @@ function headingRule(nodeType, maxLevel) {
|
|
|
40886
41015
|
nodeType, match => ({level: match[1].length}))
|
|
40887
41016
|
}
|
|
40888
41017
|
|
|
40889
|
-
// : (
|
|
41018
|
+
// : (RegExp, MarkType, number, number) → InputRule
|
|
41019
|
+
// Given a regexp matching a markdown emphasis pattern ending at the
|
|
41020
|
+
// cursor, a mark type, and the lengths of the opening and closing
|
|
41021
|
+
// delimiters, returns an input rule that deletes only the
|
|
41022
|
+
// delimiters -- never the content between them -- and applies the
|
|
41023
|
+
// mark over the untouched range. This preserves any marks (links,
|
|
41024
|
+
// nested emphasis, etc.) already present in the converted range
|
|
41025
|
+
// instead of flattening it to plain text.
|
|
41026
|
+
//
|
|
41027
|
+
// prosemirror-inputrules invokes this handler from handleTextInput,
|
|
41028
|
+
// before the browser's pending keystroke has actually been applied
|
|
41029
|
+
// to the document -- match[0] (built from existing text plus the
|
|
41030
|
+
// not-yet-inserted text) can therefore be longer than end - start.
|
|
41031
|
+
// That trailing slice is inserted first so the full matched range
|
|
41032
|
+
// genuinely exists in the document before the delimiter surgery
|
|
41033
|
+
// below operates on it. Suppressed when the cursor already sits
|
|
41034
|
+
// inside a code mark, since markdown shorthand characters are meant
|
|
41035
|
+
// to stay literal there. Clears the mark from stored marks afterward
|
|
41036
|
+
// so text typed immediately after the conversion does not inherit
|
|
41037
|
+
// it.
|
|
41038
|
+
//
|
|
41039
|
+
// The leading delimiter is deleted before the trailing one, so the
|
|
41040
|
+
// transaction's last content-shifting step is the trailing-delimiter
|
|
41041
|
+
// delete, ending at the cursor -- prosemirror-history's grouping
|
|
41042
|
+
// heuristic only looks at that step's range when deciding whether
|
|
41043
|
+
// the next keystroke belongs to the same undo group (addMark's own
|
|
41044
|
+
// step contributes no range there), so ending at the cursor is what
|
|
41045
|
+
// keeps immediately-following typing (e.g. "**foo**bar") merged with
|
|
41046
|
+
// the conversion into a single undo.
|
|
41047
|
+
function markInputRule(regexp, markType, openLen, closeLen) {
|
|
41048
|
+
return new InputRule(regexp, (state, match, start, end) => {
|
|
41049
|
+
const codeType = state.schema.marks.code;
|
|
41050
|
+
if (codeType && state.doc.resolve(start).marks().some(mark => mark.type === codeType)) return null
|
|
41051
|
+
const tr = state.tr;
|
|
41052
|
+
const fullEnd = start + match[0].length;
|
|
41053
|
+
if (fullEnd > end) tr.insertText(match[0].slice(end - start), end);
|
|
41054
|
+
tr.delete(start, start + openLen);
|
|
41055
|
+
const closeStart = fullEnd - openLen - closeLen;
|
|
41056
|
+
tr.delete(closeStart, closeStart + closeLen);
|
|
41057
|
+
tr.addMark(start, closeStart, markType.create());
|
|
41058
|
+
tr.removeStoredMark(markType);
|
|
41059
|
+
return tr
|
|
41060
|
+
})
|
|
41061
|
+
}
|
|
41062
|
+
|
|
41063
|
+
// : (MarkType) → InputRule
|
|
41064
|
+
// Given the strong mark type, returns an input rule that converts
|
|
41065
|
+
// `**text**` at the cursor into bold.
|
|
41066
|
+
function strongRule(markType) {
|
|
41067
|
+
return markInputRule(/\*\*([^*\s](?:[^*]*[^*\s])?)\*\*$/, markType, 2, 2)
|
|
41068
|
+
}
|
|
41069
|
+
|
|
41070
|
+
// : (MarkType) → InputRule
|
|
41071
|
+
// Given the em mark type, returns an input rule that converts
|
|
41072
|
+
// `*text*` at the cursor into italic. The negative lookbehind on the
|
|
41073
|
+
// opening `*` defers matching until a preceding, still-unconsumed
|
|
41074
|
+
// `*` is no longer in the way, so this rule does not fire on a
|
|
41075
|
+
// partially typed `**text**` before bold's closing marker
|
|
41076
|
+
// completes.
|
|
41077
|
+
function emStarRule(markType) {
|
|
41078
|
+
return markInputRule(/(?<!\*)\*([^*\s](?:[^*]*[^*\s])?)\*$/, markType, 1, 1)
|
|
41079
|
+
}
|
|
41080
|
+
|
|
41081
|
+
// : (MarkType) → InputRule
|
|
41082
|
+
// Given the em mark type, returns an input rule that converts
|
|
41083
|
+
// `_text_` at the cursor into italic. The negative lookbehind on the
|
|
41084
|
+
// opening `_` blocks intraword underscores (`\w` includes `_`), per
|
|
41085
|
+
// CommonMark's flanking rule for `_` emphasis -- unlike `*`, `_`
|
|
41086
|
+
// emphasis must not fire inside a word, so identifiers like
|
|
41087
|
+
// `snake_case_name` stay literal.
|
|
41088
|
+
function emUnderscoreRule(markType) {
|
|
41089
|
+
return markInputRule(/(?<!\w)_([^_\s](?:[^_]*[^_\s])?)_$/, markType, 1, 1)
|
|
41090
|
+
}
|
|
41091
|
+
|
|
41092
|
+
// : (MarkType) → InputRule
|
|
41093
|
+
// Given the code mark type, returns an input rule that converts a
|
|
41094
|
+
// backtick-delimited `text` at the cursor into inline code.
|
|
41095
|
+
function codeRule(markType) {
|
|
41096
|
+
return markInputRule(/(?<!`)`([^`\s](?:[^`]*[^`\s])?)`$/, markType, 1, 1)
|
|
41097
|
+
}
|
|
41098
|
+
|
|
41099
|
+
// : (MarkType) → InputRule
|
|
41100
|
+
// Given the s (strikethrough) mark type, returns an input rule that
|
|
41101
|
+
// converts `~~text~~` at the cursor into strikethrough. No
|
|
41102
|
+
// self-referential lookbehind, matching strongRule -- both are
|
|
41103
|
+
// double-delimiter patterns with no single-character sibling rule to
|
|
41104
|
+
// defer against, unlike emStarRule's genuine need to defer to bold.
|
|
41105
|
+
function strikethroughRule(markType) {
|
|
41106
|
+
return markInputRule(/~~([^~\s](?:[^~]*[^~\s])?)~~$/, markType, 2, 2)
|
|
41107
|
+
}
|
|
41108
|
+
|
|
41109
|
+
// : (Schema, object) → Plugin
|
|
40890
41110
|
// A set of input rules for creating the basic block quotes, lists,
|
|
40891
|
-
// code blocks, and
|
|
40892
|
-
|
|
41111
|
+
// code blocks, heading, and markdown formatting shorthand (bold,
|
|
41112
|
+
// italic, inline code, strikethrough). All ten rules are gated
|
|
41113
|
+
// together by config.behavior.markdownShorthand. config defaults to
|
|
41114
|
+
// {}, so markdownShorthand defaults to enabled (only an explicit
|
|
41115
|
+
// `false` turns it off) when buildInputRules is called without a
|
|
41116
|
+
// config, matching BehaviorConfig's default.
|
|
41117
|
+
function buildInputRules(schema, config = {}) {
|
|
40893
41118
|
let rules = [], type;
|
|
40894
|
-
|
|
40895
|
-
if (
|
|
40896
|
-
|
|
40897
|
-
|
|
40898
|
-
|
|
41119
|
+
const markdownShorthand = config.behavior?.markdownShorthand !== false;
|
|
41120
|
+
if (markdownShorthand) {
|
|
41121
|
+
if (type = schema.nodes.blockquote) rules.push(blockQuoteRule(type));
|
|
41122
|
+
if (type = schema.nodes.ordered_list) rules.push(orderedListRule(type));
|
|
41123
|
+
if (type = schema.nodes.bullet_list) rules.push(bulletListRule(type));
|
|
41124
|
+
if (type = schema.nodes.code_block) rules.push(codeBlockRule(type));
|
|
41125
|
+
if (type = schema.nodes.heading) rules.push(headingRule(type, 6));
|
|
41126
|
+
if (type = schema.marks.strong) rules.push(strongRule(type));
|
|
41127
|
+
if (type = schema.marks.em) {
|
|
41128
|
+
rules.push(emStarRule(type));
|
|
41129
|
+
rules.push(emUnderscoreRule(type));
|
|
41130
|
+
}
|
|
41131
|
+
if (type = schema.marks.code) rules.push(codeRule(type));
|
|
41132
|
+
if (type = schema.marks.s) rules.push(strikethroughRule(type));
|
|
41133
|
+
}
|
|
40899
41134
|
return inputRules({rules})
|
|
40900
41135
|
}
|
|
40901
41136
|
|
|
@@ -41176,7 +41411,7 @@ function openImageDialog() {
|
|
|
41176
41411
|
function markupSetup(config, schema) {
|
|
41177
41412
|
setPrefix('Markup');
|
|
41178
41413
|
let plugins = [
|
|
41179
|
-
buildInputRules(schema),
|
|
41414
|
+
buildInputRules(schema, config),
|
|
41180
41415
|
keymap(buildKeymap(config, schema)),
|
|
41181
41416
|
keymap(baseKeymap),
|
|
41182
41417
|
dropCursor(),
|
|
@@ -42549,6 +42784,7 @@ const MU = {
|
|
|
42549
42784
|
getDataImages,
|
|
42550
42785
|
getLocalImages,
|
|
42551
42786
|
getHTML,
|
|
42787
|
+
getHeaders,
|
|
42552
42788
|
getHeight,
|
|
42553
42789
|
getImageAttributes,
|
|
42554
42790
|
getLinkAttributes,
|
|
@@ -42556,6 +42792,7 @@ const MU = {
|
|
|
42556
42792
|
getTestHTML,
|
|
42557
42793
|
indent: indent$1,
|
|
42558
42794
|
insertImage: insertImage$1,
|
|
42795
|
+
insertInternalLink,
|
|
42559
42796
|
insertLink: insertLink$1,
|
|
42560
42797
|
insertTable,
|
|
42561
42798
|
insertHRule,
|
|
@@ -42590,6 +42827,8 @@ const MU = {
|
|
|
42590
42827
|
testPasteHTMLPreprocessing,
|
|
42591
42828
|
testPasteTextPreprocessing,
|
|
42592
42829
|
testPresentCodeLanguages,
|
|
42830
|
+
testTypeKey,
|
|
42831
|
+
testTypeText,
|
|
42593
42832
|
toggleBold,
|
|
42594
42833
|
toggleCode,
|
|
42595
42834
|
toggleItalic,
|