@aicayzer/inkkit 0.1.3 → 0.1.4
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/README.md
CHANGED
|
@@ -9,6 +9,17 @@ A Markdown editor for the web and for apps that host a web view. It edits format
|
|
|
9
9
|
- Images and files through your own storage adapter
|
|
10
10
|
- Your app owns the toolbar, menus, files and appearance
|
|
11
11
|
|
|
12
|
+
## How it works
|
|
13
|
+
|
|
14
|
+
<picture>
|
|
15
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/aicayzer/inkkit/main/docs/images/how-inkkit-works-dark.svg">
|
|
16
|
+
<img src="https://raw.githubusercontent.com/aicayzer/inkkit/main/docs/images/how-inkkit-works.svg" width="800" alt="An ordinary Markdown editor rebuilds the whole file when it saves, so list markers, spacing and escaping change. InkKit loads the file and edits it formatted with Milkdown. A preservation layer between Milkdown and the file saves a note you open and leave byte for byte unchanged. After an edit, it keeps unchanged blocks as their original bytes, writes only edited blocks with Milkdown, and reads the result back. If it reopens as the same document, the edit is kept and saved. If not, the edit is refused and the note stays as it was. Your app owns the files, storage, toolbar and appearance.">
|
|
17
|
+
</picture>
|
|
18
|
+
|
|
19
|
+
A formatted Markdown editor usually rebuilds the whole file when it saves, so opening and saving a note can change list markers, spacing and escaping that you never touched. InkKit adds a preservation layer between [Milkdown](https://milkdown.dev) and the file. A note that you open and leave is saved byte for byte unchanged, and after an edit only the blocks you edited are rewritten. InkKit reads the result of each edit back and refuses the edit if it would not reopen as the same document.
|
|
20
|
+
|
|
21
|
+
For the exact guarantees, see [Preservation](https://github.com/aicayzer/inkkit/blob/main/docs/preservation.md).
|
|
22
|
+
|
|
12
23
|
## Install
|
|
13
24
|
|
|
14
25
|
```sh
|
|
@@ -35086,6 +35086,194 @@ var references = [
|
|
|
35086
35086
|
}))
|
|
35087
35087
|
].flat();
|
|
35088
35088
|
//#endregion
|
|
35089
|
+
//#region src/smart-punctuation.ts
|
|
35090
|
+
/**
|
|
35091
|
+
* Smart punctuation: the quote or dash being typed becomes a curly quote or a dash.
|
|
35092
|
+
*
|
|
35093
|
+
* - `"` and `'` open after the start of a block, a line break, whitespace, an opening bracket, a
|
|
35094
|
+
* dash or another opening quote, and close everywhere else. A `'` between letters is an
|
|
35095
|
+
* apostrophe, which is the closing form.
|
|
35096
|
+
* - `--` gives an en dash and `---` an em dash: the third hyphen turns the en dash that the second
|
|
35097
|
+
* one made into an em dash. Only a hyphen that text input has just inserted counts as the first
|
|
35098
|
+
* of the two, so a hyphen already in the note, or typed before the caret moved, stays one.
|
|
35099
|
+
* - A quote that a dead key made arrives as a composition. When the composition commits exactly
|
|
35100
|
+
* that quote at the caret, it follows the same rules.
|
|
35101
|
+
*
|
|
35102
|
+
* These are input rules, so only the character being typed changes, Backspace and Undo straight
|
|
35103
|
+
* after restore what was typed, and nothing in a note is rewritten. The editor answers
|
|
35104
|
+
* `smartPunctuationCtx` with the current option, so turning it on or off needs no new editor.
|
|
35105
|
+
*/
|
|
35106
|
+
var smartPunctuationCtx = $ctx(() => false, "inkkitSmartPunctuation");
|
|
35107
|
+
/** Set on the transaction of each rule that this file makes. */
|
|
35108
|
+
var smartRule = "inkkitSmartPunctuation";
|
|
35109
|
+
var enDash = "–";
|
|
35110
|
+
var emDash = "—";
|
|
35111
|
+
/** What may come before a quote for it to open. */
|
|
35112
|
+
var opensAfter = /[\s([{<\u2013\u2014\u2018\u201C]/u;
|
|
35113
|
+
/**
|
|
35114
|
+
* Markdown that a person is typing out in a line of text, not words: a code span, a link
|
|
35115
|
+
* destination, a web address, a wiki link or footnote label, an HTML tag or comment and a
|
|
35116
|
+
* reference definition. Smart punctuation leaves a line alone while one is open, because the
|
|
35117
|
+
* curly character would change what the Markdown means.
|
|
35118
|
+
*/
|
|
35119
|
+
var typingSyntax = [
|
|
35120
|
+
(before) => (before.match(/`+/g)?.length ?? 0) % 2 === 1,
|
|
35121
|
+
(before) => /\]\([^)]*$/.test(before),
|
|
35122
|
+
(before) => /\[\[[^\]]*$/.test(before),
|
|
35123
|
+
(before) => /\[\^[^\]]*$/.test(before),
|
|
35124
|
+
(before) => /(?:^|[^\w])(?:[a-z][a-z0-9+.-]*:\/\/|www\.|mailto:)\S*$/i.test(before),
|
|
35125
|
+
(before) => /(?:^|[^\w])<[A-Za-z!/?][^>]*$/.test(before),
|
|
35126
|
+
(before) => /^\s*\[[^\]]+\]:/.test(before)
|
|
35127
|
+
];
|
|
35128
|
+
/**
|
|
35129
|
+
* The text of the block before `pos`, with each code span and each non-text node as one
|
|
35130
|
+
* placeholder, so that a backtick or `<` inside code is not syntax being typed.
|
|
35131
|
+
*/
|
|
35132
|
+
function textBefore(state, pos) {
|
|
35133
|
+
const $pos = state.doc.resolve(pos);
|
|
35134
|
+
let text = "";
|
|
35135
|
+
$pos.parent.forEach((child, offset) => {
|
|
35136
|
+
if (offset >= $pos.parentOffset) return;
|
|
35137
|
+
text += child.isText && !child.marks.some((mark) => mark.type.spec.code) ? child.text.slice(0, $pos.parentOffset - offset) : "";
|
|
35138
|
+
});
|
|
35139
|
+
return text;
|
|
35140
|
+
}
|
|
35141
|
+
var input = new PluginKey("inkkitSmartPunctuationInput");
|
|
35142
|
+
/** The text a transaction inserted, when it is one step that inserts text at `at` and nothing else. */
|
|
35143
|
+
function insertedAt(tr, at) {
|
|
35144
|
+
const [step] = tr.steps;
|
|
35145
|
+
if (tr.steps.length !== 1 || !(step instanceof ReplaceStep)) return void 0;
|
|
35146
|
+
if (step.from !== at || step.slice.openStart || step.slice.openEnd) return void 0;
|
|
35147
|
+
return step.slice.content.textBetween(0, step.slice.content.size);
|
|
35148
|
+
}
|
|
35149
|
+
var inputTracking = $prose(() => new Plugin({
|
|
35150
|
+
key: input,
|
|
35151
|
+
state: {
|
|
35152
|
+
init: () => ({
|
|
35153
|
+
pending: null,
|
|
35154
|
+
hyphen: null,
|
|
35155
|
+
composition: null,
|
|
35156
|
+
composedQuote: null
|
|
35157
|
+
}),
|
|
35158
|
+
apply(tr, value) {
|
|
35159
|
+
const { pending, composition } = value;
|
|
35160
|
+
value.pending = value.hyphen = value.composedQuote = null;
|
|
35161
|
+
if (tr.getMeta(hyphenReceived)) value.hyphen = tr.selection.from;
|
|
35162
|
+
if (!tr.docChanged) return value;
|
|
35163
|
+
if (pending !== null && insertedAt(tr, pending) === "-") value.hyphen = pending + 1;
|
|
35164
|
+
if (composition) {
|
|
35165
|
+
value.composition = null;
|
|
35166
|
+
const text = insertedAt(tr, composition.pos);
|
|
35167
|
+
if (tr.getMeta("composition") !== void 0 && (text === "\"" || text === "'") && tr.doc.content.size === composition.size + 1) value.composedQuote = composition.pos + 1;
|
|
35168
|
+
}
|
|
35169
|
+
return value;
|
|
35170
|
+
}
|
|
35171
|
+
},
|
|
35172
|
+
props: {
|
|
35173
|
+
handleTextInput(view, from, _to, text) {
|
|
35174
|
+
input.getState(view.state).pending = text === "-" ? from : null;
|
|
35175
|
+
return false;
|
|
35176
|
+
},
|
|
35177
|
+
handleDOMEvents: { compositionstart(view) {
|
|
35178
|
+
const { from, empty } = view.state.selection;
|
|
35179
|
+
const tracked = input.getState(view.state);
|
|
35180
|
+
tracked.composedQuote = null;
|
|
35181
|
+
tracked.composition = empty ? {
|
|
35182
|
+
pos: from,
|
|
35183
|
+
size: view.state.doc.content.size
|
|
35184
|
+
} : null;
|
|
35185
|
+
return false;
|
|
35186
|
+
} }
|
|
35187
|
+
}
|
|
35188
|
+
}));
|
|
35189
|
+
/** Set on a transaction that inserted text ending in a hyphen, whatever else came with it. */
|
|
35190
|
+
var hyphenReceived = "inkkitHyphenReceived";
|
|
35191
|
+
/** Marks a transaction that inserted text ending in a hyphen that no rule changed. */
|
|
35192
|
+
function insertedHyphen(tr) {
|
|
35193
|
+
return tr.setMeta(hyphenReceived, true);
|
|
35194
|
+
}
|
|
35195
|
+
/**
|
|
35196
|
+
* The state that input rules see for text that arrived in one piece, which holds only what the
|
|
35197
|
+
* rules need: it follows the document, with the tracking plugin when the editor has one. When
|
|
35198
|
+
* `rest` ends in a hyphen, the last character follows a hyphen that came in the same piece.
|
|
35199
|
+
*/
|
|
35200
|
+
function chunkState(state, config, rest) {
|
|
35201
|
+
const tracker = input.get(state);
|
|
35202
|
+
const prepared = EditorState.create({
|
|
35203
|
+
...config,
|
|
35204
|
+
plugins: tracker ? [tracker] : []
|
|
35205
|
+
});
|
|
35206
|
+
return tracker && rest.endsWith("-") ? prepared.apply(insertedHyphen(prepared.tr)) : prepared;
|
|
35207
|
+
}
|
|
35208
|
+
/**
|
|
35209
|
+
* Whether a rule may change what `match` found at `pos`: the option is on, the last character of
|
|
35210
|
+
* the match is the one being typed, which is not in the document yet, and the text is ordinary,
|
|
35211
|
+
* with no Markdown being typed. The input rules plugin also runs every rule when a composition
|
|
35212
|
+
* ends, over text that is all in the document; that is not typing, and is left alone.
|
|
35213
|
+
*/
|
|
35214
|
+
function applies(on, state, match, pos) {
|
|
35215
|
+
if (!on()) return false;
|
|
35216
|
+
const typed = pos + match[0].length - 1 === state.selection.from;
|
|
35217
|
+
const composed = !typed && match[0].length === 1 && input.getState(state)?.composedQuote === state.selection.from && pos === state.selection.from - 1;
|
|
35218
|
+
if (!typed && !composed) return false;
|
|
35219
|
+
const $pos = state.doc.resolve(pos);
|
|
35220
|
+
if ($pos.parent.type.spec.code || !$pos.parent.inlineContent) return false;
|
|
35221
|
+
if ((state.storedMarks ?? $pos.marks()).some((mark) => mark.type.spec.code)) return false;
|
|
35222
|
+
const before = textBefore(state, pos);
|
|
35223
|
+
return !typingSyntax.some((open) => open(before));
|
|
35224
|
+
}
|
|
35225
|
+
function opensHere(state, pos) {
|
|
35226
|
+
const node = state.doc.resolve(pos).nodeBefore;
|
|
35227
|
+
if (!node) return true;
|
|
35228
|
+
if (node.isText) {
|
|
35229
|
+
const text = node.text ?? "";
|
|
35230
|
+
return opensAfter.test(String.fromCodePoint(text.codePointAt(text.length - 1)));
|
|
35231
|
+
}
|
|
35232
|
+
return node.type.name === "hardbreak";
|
|
35233
|
+
}
|
|
35234
|
+
/** The input rule that ran at the last change, when nothing has happened since. */
|
|
35235
|
+
function lastRule(state) {
|
|
35236
|
+
for (const plugin of state.plugins) if (plugin.spec.isInputRules) {
|
|
35237
|
+
const stored = plugin.getState(state);
|
|
35238
|
+
if (stored) return stored;
|
|
35239
|
+
}
|
|
35240
|
+
}
|
|
35241
|
+
var quotes$1 = {
|
|
35242
|
+
"\"": ["“", "”"],
|
|
35243
|
+
"'": ["‘", "’"]
|
|
35244
|
+
};
|
|
35245
|
+
var smartPunctuation = [
|
|
35246
|
+
smartPunctuationCtx,
|
|
35247
|
+
inputTracking,
|
|
35248
|
+
$inputRule((ctx) => new InputRule(/["']$/, (state, match, start, end) => {
|
|
35249
|
+
if (!applies(ctx.get(smartPunctuationCtx.key), state, match, start)) return null;
|
|
35250
|
+
const [opening, closing] = quotes$1[match[0]];
|
|
35251
|
+
return state.tr.insertText(opensHere(state, start) ? opening : closing, start, end).setMeta(smartRule, "quote");
|
|
35252
|
+
}, { inCodeMark: false })),
|
|
35253
|
+
$inputRule((ctx) => new InputRule(/--$/, (state, match, start, end) => {
|
|
35254
|
+
if (!applies(ctx.get(smartPunctuationCtx.key), state, match, start)) return null;
|
|
35255
|
+
if (input.getState(state)?.hyphen !== state.selection.from) return null;
|
|
35256
|
+
const before = textBefore(state, start);
|
|
35257
|
+
if (!before.trim() || before.endsWith("-") || /^\s*\|/.test(before)) return null;
|
|
35258
|
+
return state.tr.insertText(enDash, start, end).setMeta(smartRule, "en");
|
|
35259
|
+
}, { inCodeMark: false })),
|
|
35260
|
+
$inputRule((ctx) => new InputRule(/\u2013-$/, (state, match, start, end) => {
|
|
35261
|
+
if (!applies(ctx.get(smartPunctuationCtx.key), state, match, start)) return null;
|
|
35262
|
+
if (lastRule(state)?.transform.getMeta(smartRule) !== "en") return null;
|
|
35263
|
+
return state.tr.insertText(emDash, start, end).setMeta(smartRule, "em");
|
|
35264
|
+
}, { inCodeMark: false }))
|
|
35265
|
+
].flat();
|
|
35266
|
+
/**
|
|
35267
|
+
* Undo straight after a smart quote or dash puts back the character that was typed, as Backspace
|
|
35268
|
+
* does for any input rule. The replacement joins the typing's history step, so the next Undo
|
|
35269
|
+
* removes the typing and not the curly character. Returns whether it applied.
|
|
35270
|
+
*/
|
|
35271
|
+
var undoSmartPunctuation = (state, dispatch) => {
|
|
35272
|
+
const rule = lastRule(state);
|
|
35273
|
+
if (!rule?.transform.getMeta(smartRule)) return false;
|
|
35274
|
+
return undoInputRule(state, dispatch && ((tr) => dispatch(tr.setMeta("appendedTransaction", rule.transform))));
|
|
35275
|
+
};
|
|
35276
|
+
//#endregion
|
|
35089
35277
|
//#region src/linked-syntax.ts
|
|
35090
35278
|
var fromTable = (raw) => raw.replace(/(\\*)\|/g, (_match, escapes) => `${"\\".repeat(Math.floor(escapes.length / 2))}|`);
|
|
35091
35279
|
var toTable = (raw) => raw.replace(/(\\*)\|/g, (_match, escapes) => `${"\\".repeat(escapes.length * 2 + 1)}|`);
|
|
@@ -35277,6 +35465,76 @@ var wikiLinkSchema = $node("inkkit_wiki_link", () => ({
|
|
|
35277
35465
|
}
|
|
35278
35466
|
}
|
|
35279
35467
|
}));
|
|
35468
|
+
var composed = new PluginKey("inkkitWikiLinkComposition");
|
|
35469
|
+
/**
|
|
35470
|
+
* Lets the rule below tell a `]` that a composition committed from text that was already in the
|
|
35471
|
+
* note: ProseMirror runs every input rule when a composition ends, with nothing typed.
|
|
35472
|
+
*/
|
|
35473
|
+
var wikiLinkComposition = $prose(() => new Plugin({
|
|
35474
|
+
key: composed,
|
|
35475
|
+
state: {
|
|
35476
|
+
init: () => ({
|
|
35477
|
+
composition: null,
|
|
35478
|
+
closing: null
|
|
35479
|
+
}),
|
|
35480
|
+
apply(tr, value) {
|
|
35481
|
+
const { composition } = value;
|
|
35482
|
+
value.closing = null;
|
|
35483
|
+
if (!tr.docChanged || !composition) return value;
|
|
35484
|
+
if (tr.getMeta("composition") === void 0) value.composition = null;
|
|
35485
|
+
else if (insertedAt(tr, composition.pos) === "]" && tr.doc.content.size === composition.size + 1) value.closing = composition.pos + 1;
|
|
35486
|
+
return value;
|
|
35487
|
+
}
|
|
35488
|
+
},
|
|
35489
|
+
props: { handleDOMEvents: { compositionstart(view) {
|
|
35490
|
+
const { from, empty } = view.state.selection;
|
|
35491
|
+
const tracked = composed.getState(view.state);
|
|
35492
|
+
tracked.closing = null;
|
|
35493
|
+
tracked.composition = empty ? {
|
|
35494
|
+
pos: from,
|
|
35495
|
+
size: view.state.doc.content.size
|
|
35496
|
+
} : null;
|
|
35497
|
+
return false;
|
|
35498
|
+
} } }
|
|
35499
|
+
}));
|
|
35500
|
+
/**
|
|
35501
|
+
* Turns `[[target]]`, as the person types its last bracket, into the wiki link
|
|
35502
|
+
* it reads as when the note is opened, so the note keeps the characters that
|
|
35503
|
+
* were typed. Left as text, the brackets save as `\[\[target]]`, which no
|
|
35504
|
+
* other tool reads as a link. The last bracket has to be the one just typed,
|
|
35505
|
+
* or committed by a composition: text already in the note, such as escaped
|
|
35506
|
+
* brackets, is never changed, and that includes the run of every rule when a
|
|
35507
|
+
* composition ends.
|
|
35508
|
+
*/
|
|
35509
|
+
var wikiLinkInputRule = $inputRule(() => new InputRule(/\[\[(?:\\.|[^[\]\\\ufffc])*\]\]$/, (state, match, start, end) => {
|
|
35510
|
+
const { schema, doc } = state;
|
|
35511
|
+
const type = schema.nodes.inkkit_wiki_link;
|
|
35512
|
+
const caret = state.selection.from;
|
|
35513
|
+
if (start + match[0].length - 1 !== caret && !(start + match[0].length === caret && composed.getState(state)?.closing === caret)) return null;
|
|
35514
|
+
const $start = doc.resolve(start);
|
|
35515
|
+
const parent = $start.parent;
|
|
35516
|
+
if (!type || !parent.inlineContent || !parent.canReplaceWith($start.index(), $start.index(), type) || /[!\\[]/.test(doc.textBetween(Math.max($start.start(), start - 1), start))) return null;
|
|
35517
|
+
let marks;
|
|
35518
|
+
let plain = true;
|
|
35519
|
+
doc.nodesBetween(start, end, (node) => {
|
|
35520
|
+
if (!node.isInline) return;
|
|
35521
|
+
if (!node.isText) plain = false;
|
|
35522
|
+
else if (marks && !Mark.sameSet(marks, node.marks)) plain = false;
|
|
35523
|
+
else marks ??= node.marks;
|
|
35524
|
+
});
|
|
35525
|
+
if (!plain || !marks || marks.some((mark) => ["inlineCode", "link"].includes(mark.type.name)) || state.storedMarks && !Mark.sameSet(state.storedMarks, marks)) return null;
|
|
35526
|
+
let inTable = false;
|
|
35527
|
+
for (let depth = $start.depth; depth > 0; depth--) if (["table_cell", "table_header"].includes($start.node(depth).type.name)) inTable = true;
|
|
35528
|
+
const parsed = parseLinkedSyntax(inTable ? toTable(match[0]) : match[0], inTable);
|
|
35529
|
+
if (!parsed || parsed.embed) return null;
|
|
35530
|
+
return state.tr.replaceWith(start, end, type.create({
|
|
35531
|
+
raw: parsed.raw,
|
|
35532
|
+
target: parsed.target,
|
|
35533
|
+
fragment: parsed.fragment,
|
|
35534
|
+
label: parsed.label,
|
|
35535
|
+
inTable: parsed.inTable
|
|
35536
|
+
}, void 0, marks));
|
|
35537
|
+
}, { inCodeMark: false }));
|
|
35280
35538
|
function linkedSyntax(wikiLinks, files) {
|
|
35281
35539
|
const syntax = { text: {} };
|
|
35282
35540
|
if (wikiLinks) syntax.text[91] = {
|
|
@@ -44433,194 +44691,6 @@ var cursor = [
|
|
|
44433
44691
|
})
|
|
44434
44692
|
].flat();
|
|
44435
44693
|
//#endregion
|
|
44436
|
-
//#region src/smart-punctuation.ts
|
|
44437
|
-
/**
|
|
44438
|
-
* Smart punctuation: the quote or dash being typed becomes a curly quote or a dash.
|
|
44439
|
-
*
|
|
44440
|
-
* - `"` and `'` open after the start of a block, a line break, whitespace, an opening bracket, a
|
|
44441
|
-
* dash or another opening quote, and close everywhere else. A `'` between letters is an
|
|
44442
|
-
* apostrophe, which is the closing form.
|
|
44443
|
-
* - `--` gives an en dash and `---` an em dash: the third hyphen turns the en dash that the second
|
|
44444
|
-
* one made into an em dash. Only a hyphen that text input has just inserted counts as the first
|
|
44445
|
-
* of the two, so a hyphen already in the note, or typed before the caret moved, stays one.
|
|
44446
|
-
* - A quote that a dead key made arrives as a composition. When the composition commits exactly
|
|
44447
|
-
* that quote at the caret, it follows the same rules.
|
|
44448
|
-
*
|
|
44449
|
-
* These are input rules, so only the character being typed changes, Backspace and Undo straight
|
|
44450
|
-
* after restore what was typed, and nothing in a note is rewritten. The editor answers
|
|
44451
|
-
* `smartPunctuationCtx` with the current option, so turning it on or off needs no new editor.
|
|
44452
|
-
*/
|
|
44453
|
-
var smartPunctuationCtx = $ctx(() => false, "inkkitSmartPunctuation");
|
|
44454
|
-
/** Set on the transaction of each rule that this file makes. */
|
|
44455
|
-
var smartRule = "inkkitSmartPunctuation";
|
|
44456
|
-
var enDash = "–";
|
|
44457
|
-
var emDash = "—";
|
|
44458
|
-
/** What may come before a quote for it to open. */
|
|
44459
|
-
var opensAfter = /[\s([{<\u2013\u2014\u2018\u201C]/u;
|
|
44460
|
-
/**
|
|
44461
|
-
* Markdown that a person is typing out in a line of text, not words: a code span, a link
|
|
44462
|
-
* destination, a web address, a wiki link or footnote label, an HTML tag or comment and a
|
|
44463
|
-
* reference definition. Smart punctuation leaves a line alone while one is open, because the
|
|
44464
|
-
* curly character would change what the Markdown means.
|
|
44465
|
-
*/
|
|
44466
|
-
var typingSyntax = [
|
|
44467
|
-
(before) => (before.match(/`+/g)?.length ?? 0) % 2 === 1,
|
|
44468
|
-
(before) => /\]\([^)]*$/.test(before),
|
|
44469
|
-
(before) => /\[\[[^\]]*$/.test(before),
|
|
44470
|
-
(before) => /\[\^[^\]]*$/.test(before),
|
|
44471
|
-
(before) => /(?:^|[^\w])(?:[a-z][a-z0-9+.-]*:\/\/|www\.|mailto:)\S*$/i.test(before),
|
|
44472
|
-
(before) => /(?:^|[^\w])<[A-Za-z!/?][^>]*$/.test(before),
|
|
44473
|
-
(before) => /^\s*\[[^\]]+\]:/.test(before)
|
|
44474
|
-
];
|
|
44475
|
-
/**
|
|
44476
|
-
* The text of the block before `pos`, with each code span and each non-text node as one
|
|
44477
|
-
* placeholder, so that a backtick or `<` inside code is not syntax being typed.
|
|
44478
|
-
*/
|
|
44479
|
-
function textBefore(state, pos) {
|
|
44480
|
-
const $pos = state.doc.resolve(pos);
|
|
44481
|
-
let text = "";
|
|
44482
|
-
$pos.parent.forEach((child, offset) => {
|
|
44483
|
-
if (offset >= $pos.parentOffset) return;
|
|
44484
|
-
text += child.isText && !child.marks.some((mark) => mark.type.spec.code) ? child.text.slice(0, $pos.parentOffset - offset) : "";
|
|
44485
|
-
});
|
|
44486
|
-
return text;
|
|
44487
|
-
}
|
|
44488
|
-
var input = new PluginKey("inkkitSmartPunctuationInput");
|
|
44489
|
-
/** The text a transaction inserted, when it is one step that inserts text at `at` and nothing else. */
|
|
44490
|
-
function insertedAt(tr, at) {
|
|
44491
|
-
const [step] = tr.steps;
|
|
44492
|
-
if (tr.steps.length !== 1 || !(step instanceof ReplaceStep)) return void 0;
|
|
44493
|
-
if (step.from !== at || step.slice.openStart || step.slice.openEnd) return void 0;
|
|
44494
|
-
return step.slice.content.textBetween(0, step.slice.content.size);
|
|
44495
|
-
}
|
|
44496
|
-
var inputTracking = $prose(() => new Plugin({
|
|
44497
|
-
key: input,
|
|
44498
|
-
state: {
|
|
44499
|
-
init: () => ({
|
|
44500
|
-
pending: null,
|
|
44501
|
-
hyphen: null,
|
|
44502
|
-
composition: null,
|
|
44503
|
-
composedQuote: null
|
|
44504
|
-
}),
|
|
44505
|
-
apply(tr, value) {
|
|
44506
|
-
const { pending, composition } = value;
|
|
44507
|
-
value.pending = value.hyphen = value.composedQuote = null;
|
|
44508
|
-
if (tr.getMeta(hyphenReceived)) value.hyphen = tr.selection.from;
|
|
44509
|
-
if (!tr.docChanged) return value;
|
|
44510
|
-
if (pending !== null && insertedAt(tr, pending) === "-") value.hyphen = pending + 1;
|
|
44511
|
-
if (composition) {
|
|
44512
|
-
value.composition = null;
|
|
44513
|
-
const text = insertedAt(tr, composition.pos);
|
|
44514
|
-
if (tr.getMeta("composition") !== void 0 && (text === "\"" || text === "'") && tr.doc.content.size === composition.size + 1) value.composedQuote = composition.pos + 1;
|
|
44515
|
-
}
|
|
44516
|
-
return value;
|
|
44517
|
-
}
|
|
44518
|
-
},
|
|
44519
|
-
props: {
|
|
44520
|
-
handleTextInput(view, from, _to, text) {
|
|
44521
|
-
input.getState(view.state).pending = text === "-" ? from : null;
|
|
44522
|
-
return false;
|
|
44523
|
-
},
|
|
44524
|
-
handleDOMEvents: { compositionstart(view) {
|
|
44525
|
-
const { from, empty } = view.state.selection;
|
|
44526
|
-
const tracked = input.getState(view.state);
|
|
44527
|
-
tracked.composedQuote = null;
|
|
44528
|
-
tracked.composition = empty ? {
|
|
44529
|
-
pos: from,
|
|
44530
|
-
size: view.state.doc.content.size
|
|
44531
|
-
} : null;
|
|
44532
|
-
return false;
|
|
44533
|
-
} }
|
|
44534
|
-
}
|
|
44535
|
-
}));
|
|
44536
|
-
/** Set on a transaction that inserted text ending in a hyphen, whatever else came with it. */
|
|
44537
|
-
var hyphenReceived = "inkkitHyphenReceived";
|
|
44538
|
-
/** Marks a transaction that inserted text ending in a hyphen that no rule changed. */
|
|
44539
|
-
function insertedHyphen(tr) {
|
|
44540
|
-
return tr.setMeta(hyphenReceived, true);
|
|
44541
|
-
}
|
|
44542
|
-
/**
|
|
44543
|
-
* The state that input rules see for text that arrived in one piece, which holds only what the
|
|
44544
|
-
* rules need: it follows the document, with the tracking plugin when the editor has one. When
|
|
44545
|
-
* `rest` ends in a hyphen, the last character follows a hyphen that came in the same piece.
|
|
44546
|
-
*/
|
|
44547
|
-
function chunkState(state, config, rest) {
|
|
44548
|
-
const tracker = input.get(state);
|
|
44549
|
-
const prepared = EditorState.create({
|
|
44550
|
-
...config,
|
|
44551
|
-
plugins: tracker ? [tracker] : []
|
|
44552
|
-
});
|
|
44553
|
-
return tracker && rest.endsWith("-") ? prepared.apply(insertedHyphen(prepared.tr)) : prepared;
|
|
44554
|
-
}
|
|
44555
|
-
/**
|
|
44556
|
-
* Whether a rule may change what `match` found at `pos`: the option is on, the last character of
|
|
44557
|
-
* the match is the one being typed, which is not in the document yet, and the text is ordinary,
|
|
44558
|
-
* with no Markdown being typed. The input rules plugin also runs every rule when a composition
|
|
44559
|
-
* ends, over text that is all in the document; that is not typing, and is left alone.
|
|
44560
|
-
*/
|
|
44561
|
-
function applies(on, state, match, pos) {
|
|
44562
|
-
if (!on()) return false;
|
|
44563
|
-
const typed = pos + match[0].length - 1 === state.selection.from;
|
|
44564
|
-
const composed = !typed && match[0].length === 1 && input.getState(state)?.composedQuote === state.selection.from && pos === state.selection.from - 1;
|
|
44565
|
-
if (!typed && !composed) return false;
|
|
44566
|
-
const $pos = state.doc.resolve(pos);
|
|
44567
|
-
if ($pos.parent.type.spec.code || !$pos.parent.inlineContent) return false;
|
|
44568
|
-
if ((state.storedMarks ?? $pos.marks()).some((mark) => mark.type.spec.code)) return false;
|
|
44569
|
-
const before = textBefore(state, pos);
|
|
44570
|
-
return !typingSyntax.some((open) => open(before));
|
|
44571
|
-
}
|
|
44572
|
-
function opensHere(state, pos) {
|
|
44573
|
-
const node = state.doc.resolve(pos).nodeBefore;
|
|
44574
|
-
if (!node) return true;
|
|
44575
|
-
if (node.isText) {
|
|
44576
|
-
const text = node.text ?? "";
|
|
44577
|
-
return opensAfter.test(String.fromCodePoint(text.codePointAt(text.length - 1)));
|
|
44578
|
-
}
|
|
44579
|
-
return node.type.name === "hardbreak";
|
|
44580
|
-
}
|
|
44581
|
-
/** The input rule that ran at the last change, when nothing has happened since. */
|
|
44582
|
-
function lastRule(state) {
|
|
44583
|
-
for (const plugin of state.plugins) if (plugin.spec.isInputRules) {
|
|
44584
|
-
const stored = plugin.getState(state);
|
|
44585
|
-
if (stored) return stored;
|
|
44586
|
-
}
|
|
44587
|
-
}
|
|
44588
|
-
var quotes$1 = {
|
|
44589
|
-
"\"": ["“", "”"],
|
|
44590
|
-
"'": ["‘", "’"]
|
|
44591
|
-
};
|
|
44592
|
-
var smartPunctuation = [
|
|
44593
|
-
smartPunctuationCtx,
|
|
44594
|
-
inputTracking,
|
|
44595
|
-
$inputRule((ctx) => new InputRule(/["']$/, (state, match, start, end) => {
|
|
44596
|
-
if (!applies(ctx.get(smartPunctuationCtx.key), state, match, start)) return null;
|
|
44597
|
-
const [opening, closing] = quotes$1[match[0]];
|
|
44598
|
-
return state.tr.insertText(opensHere(state, start) ? opening : closing, start, end).setMeta(smartRule, "quote");
|
|
44599
|
-
}, { inCodeMark: false })),
|
|
44600
|
-
$inputRule((ctx) => new InputRule(/--$/, (state, match, start, end) => {
|
|
44601
|
-
if (!applies(ctx.get(smartPunctuationCtx.key), state, match, start)) return null;
|
|
44602
|
-
if (input.getState(state)?.hyphen !== state.selection.from) return null;
|
|
44603
|
-
const before = textBefore(state, start);
|
|
44604
|
-
if (!before.trim() || before.endsWith("-") || /^\s*\|/.test(before)) return null;
|
|
44605
|
-
return state.tr.insertText(enDash, start, end).setMeta(smartRule, "en");
|
|
44606
|
-
}, { inCodeMark: false })),
|
|
44607
|
-
$inputRule((ctx) => new InputRule(/\u2013-$/, (state, match, start, end) => {
|
|
44608
|
-
if (!applies(ctx.get(smartPunctuationCtx.key), state, match, start)) return null;
|
|
44609
|
-
if (lastRule(state)?.transform.getMeta(smartRule) !== "en") return null;
|
|
44610
|
-
return state.tr.insertText(emDash, start, end).setMeta(smartRule, "em");
|
|
44611
|
-
}, { inCodeMark: false }))
|
|
44612
|
-
].flat();
|
|
44613
|
-
/**
|
|
44614
|
-
* Undo straight after a smart quote or dash puts back the character that was typed, as Backspace
|
|
44615
|
-
* does for any input rule. The replacement joins the typing's history step, so the next Undo
|
|
44616
|
-
* removes the typing and not the curly character. Returns whether it applied.
|
|
44617
|
-
*/
|
|
44618
|
-
var undoSmartPunctuation = (state, dispatch) => {
|
|
44619
|
-
const rule = lastRule(state);
|
|
44620
|
-
if (!rule?.transform.getMeta(smartRule)) return false;
|
|
44621
|
-
return undoInputRule(state, dispatch && ((tr) => dispatch(tr.setMeta("appendedTransaction", rule.transform))));
|
|
44622
|
-
};
|
|
44623
|
-
//#endregion
|
|
44624
44694
|
//#region src/text-input.ts
|
|
44625
44695
|
var isInputRules = (plugin) => plugin.spec.isInputRules === true;
|
|
44626
44696
|
/**
|
|
@@ -45125,14 +45195,25 @@ var literalBlock = $node("literal_markdown", (ctx) => ({
|
|
|
45125
45195
|
}));
|
|
45126
45196
|
//#endregion
|
|
45127
45197
|
//#region src/preserve.ts
|
|
45198
|
+
var linkFormsMatter = false;
|
|
45199
|
+
function withLinkForms(run) {
|
|
45200
|
+
const before = linkFormsMatter;
|
|
45201
|
+
linkFormsMatter = true;
|
|
45202
|
+
try {
|
|
45203
|
+
return run();
|
|
45204
|
+
} finally {
|
|
45205
|
+
linkFormsMatter = before;
|
|
45206
|
+
}
|
|
45207
|
+
}
|
|
45128
45208
|
function signature(node) {
|
|
45129
45209
|
return JSON.stringify(ordered(node), function(key, value) {
|
|
45210
|
+
const holder = this;
|
|
45211
|
+
if (key === "data" && holder.type === "link" && linkFormsMatter) return { inkkitLinkForm: value?.inkkitLinkForm };
|
|
45130
45212
|
if ([
|
|
45131
45213
|
"position",
|
|
45132
45214
|
"data",
|
|
45133
45215
|
"marker"
|
|
45134
45216
|
].includes(key)) return void 0;
|
|
45135
|
-
const holder = this;
|
|
45136
45217
|
if (key === "value" && holder.type === "html" && lineBreakHTML(value)) return "<br />";
|
|
45137
45218
|
if (key === "children" && holder.type === "tableRow") {
|
|
45138
45219
|
let end = value.length;
|
|
@@ -45955,7 +46036,17 @@ function parseDoc(ctx, text) {
|
|
|
45955
46036
|
}
|
|
45956
46037
|
function parseTree(ctx, text) {
|
|
45957
46038
|
preservationWork.characters += text.length;
|
|
45958
|
-
|
|
46039
|
+
const tree = ctx.get(remarkCtx).parse(text);
|
|
46040
|
+
const body = text.replace(/^\uFEFF/, "");
|
|
46041
|
+
const visit = (node) => {
|
|
46042
|
+
if (node.type === "link") node.data = {
|
|
46043
|
+
...node.data,
|
|
46044
|
+
inkkitLinkForm: linkForm(node, body)
|
|
46045
|
+
};
|
|
46046
|
+
node.children?.forEach(visit);
|
|
46047
|
+
};
|
|
46048
|
+
visit(tree);
|
|
46049
|
+
return tree;
|
|
45959
46050
|
}
|
|
45960
46051
|
function write(ctx, doc) {
|
|
45961
46052
|
const text = serialize(ctx, doc);
|
|
@@ -46233,6 +46324,7 @@ var Preservation = class Preservation {
|
|
|
46233
46324
|
blocks = [];
|
|
46234
46325
|
lineEnding;
|
|
46235
46326
|
originalForms = /* @__PURE__ */ new Set();
|
|
46327
|
+
linkEdits = false;
|
|
46236
46328
|
blockForms = [];
|
|
46237
46329
|
forms = /* @__PURE__ */ new WeakMap();
|
|
46238
46330
|
blockEdits = /* @__PURE__ */ new WeakMap();
|
|
@@ -46759,7 +46851,7 @@ var Preservation = class Preservation {
|
|
|
46759
46851
|
const kind = block && block.form === text && !carried.within.has(index) ? position == null ? "moved" : "unchanged" : block && tree && position != null ? "edited" : "canonical";
|
|
46760
46852
|
kept.push(kind);
|
|
46761
46853
|
const node = tree ? positionalNode(tree.raw, transformed(index)) : void 0;
|
|
46762
|
-
pieces.push(kind === "unchanged" || kind === "moved" ? this.source.slice(block.start, block.end) : kind === "edited" ? this.retainBlock(block, node, canonical, this.around(positions, index).filter((other) => !paired.has(other) && other.ast.type === block.ast.type).flatMap((other) => other.ast.children ?? []), carried.children ? this.carriedChildren(doc.child(index), position) : void 0) : (node && this.retainPart(positions, index, node, canonical, taken)) ?? restoreCodeEndings(this.ctx, doc.type.create(null, [doc.child(index)]), withEnding(text, this.lineEnding)));
|
|
46854
|
+
pieces.push(kind === "unchanged" || kind === "moved" ? this.source.slice(block.start, block.end) : kind === "edited" ? this.retainingForms(block, doc.child(index), () => this.retainBlock(block, node, canonical, this.around(positions, index).filter((other) => !paired.has(other) && other.ast.type === block.ast.type).flatMap((other) => other.ast.children ?? []), carried.children ? this.carriedChildren(doc.child(index), position) : void 0)) : (node && this.retainPart(positions, index, node, canonical, taken)) ?? restoreCodeEndings(this.ctx, doc.type.create(null, [doc.child(index)]), withEnding(text, this.lineEnding)));
|
|
46763
46855
|
}
|
|
46764
46856
|
return {
|
|
46765
46857
|
gaps,
|
|
@@ -46955,6 +47047,31 @@ var Preservation = class Preservation {
|
|
|
46955
47047
|
for (let index = 0; index < nodes.length; index++) rewritten.push(again.child(index));
|
|
46956
47048
|
return actual === normalized(write(this.ctx, read.type.create(null, rewritten)));
|
|
46957
47049
|
}
|
|
47050
|
+
withLinkEdits(run) {
|
|
47051
|
+
this.linkEdits = true;
|
|
47052
|
+
try {
|
|
47053
|
+
return run();
|
|
47054
|
+
} finally {
|
|
47055
|
+
this.linkEdits = false;
|
|
47056
|
+
}
|
|
47057
|
+
}
|
|
47058
|
+
retainingForms(block, node, retain) {
|
|
47059
|
+
if (!this.linkEdits) return retain();
|
|
47060
|
+
const forms = (value) => {
|
|
47061
|
+
const found = [];
|
|
47062
|
+
let previous;
|
|
47063
|
+
value.descendants((child) => {
|
|
47064
|
+
if (!child.isText) return true;
|
|
47065
|
+
const link = child.marks.find((mark) => mark.type.name === "link");
|
|
47066
|
+
if (link && !previous?.eq(link)) found.push(link.attrs.form ?? null);
|
|
47067
|
+
previous = link;
|
|
47068
|
+
return false;
|
|
47069
|
+
});
|
|
47070
|
+
return JSON.stringify(found);
|
|
47071
|
+
};
|
|
47072
|
+
const original = this.baseline.maybeChild(this.blocks.indexOf(block));
|
|
47073
|
+
return !original || this.baseline.childCount !== this.blocks.length || forms(original) !== forms(node) ? withLinkForms(retain) : retain();
|
|
47074
|
+
}
|
|
46958
47075
|
wholeDocument(doc) {
|
|
46959
47076
|
try {
|
|
46960
47077
|
const canonical = write(this.ctx, doc);
|
|
@@ -46963,20 +47080,24 @@ var Preservation = class Preservation {
|
|
|
46963
47080
|
doc.forEach((node) => current.push(this.formOf(node)));
|
|
46964
47081
|
const pairing = this.pairing(doc, current);
|
|
46965
47082
|
const carries = pairing.carried.moved.size > 0 || pairing.carried.children;
|
|
46966
|
-
|
|
46967
|
-
|
|
46968
|
-
|
|
46969
|
-
|
|
46970
|
-
|
|
46971
|
-
|
|
47083
|
+
const attempt = () => {
|
|
47084
|
+
if (carries) {
|
|
47085
|
+
let output;
|
|
47086
|
+
try {
|
|
47087
|
+
output = this.writeDocument(doc, current, pairing);
|
|
47088
|
+
} catch {
|
|
47089
|
+
output = void 0;
|
|
47090
|
+
}
|
|
47091
|
+
if (output !== void 0 && this.reopens(doc, output, canonical)) return output;
|
|
46972
47092
|
}
|
|
46973
|
-
|
|
46974
|
-
|
|
46975
|
-
|
|
46976
|
-
|
|
46977
|
-
|
|
46978
|
-
}
|
|
46979
|
-
|
|
47093
|
+
const output = this.writeDocument(doc, current, carries ? {
|
|
47094
|
+
positions: correspondence(this.blockForms, current),
|
|
47095
|
+
carried: nothingCarried
|
|
47096
|
+
} : pairing);
|
|
47097
|
+
return this.reopens(doc, output, canonical) ? output : void 0;
|
|
47098
|
+
};
|
|
47099
|
+
const output = attempt() ?? this.withLinkEdits(attempt);
|
|
47100
|
+
if (output !== void 0) return output;
|
|
46980
47101
|
throw new PreservationError("The edited Markdown cannot be reopened without changing its content");
|
|
46981
47102
|
} catch (cause) {
|
|
46982
47103
|
if (cause instanceof PreservationError) throw cause;
|
|
@@ -47180,16 +47301,126 @@ function read(ctx, sources, texts) {
|
|
|
47180
47301
|
return valid ? found : void 0;
|
|
47181
47302
|
});
|
|
47182
47303
|
}
|
|
47304
|
+
function literalFormattingLinks(text) {
|
|
47305
|
+
if (!/[*_~`]/.test(text) || /[<>]/.test(text)) return void 0;
|
|
47306
|
+
const tree = fromMarkdown(`x ${text}`, {
|
|
47307
|
+
extensions: [gfmAutolinkLiteral(), { disable: { null: [
|
|
47308
|
+
"attention",
|
|
47309
|
+
"codeText",
|
|
47310
|
+
"htmlText",
|
|
47311
|
+
"labelStartLink",
|
|
47312
|
+
"labelStartImage",
|
|
47313
|
+
"characterEscape",
|
|
47314
|
+
"characterReference",
|
|
47315
|
+
"autolink"
|
|
47316
|
+
] } }],
|
|
47317
|
+
mdastExtensions: [gfmAutolinkLiteralFromMarkdown()]
|
|
47318
|
+
});
|
|
47319
|
+
const paragraph = tree.children[0];
|
|
47320
|
+
if (tree.children.length !== 1 || paragraph?.type !== "paragraph") return void 0;
|
|
47321
|
+
const found = [];
|
|
47322
|
+
let readable = "";
|
|
47323
|
+
for (const child of paragraph.children) if (child.type === "text") readable += child.value;
|
|
47324
|
+
else if (child.type === "link" && child.children.every((node) => node.type === "text")) {
|
|
47325
|
+
const start = readable.length - 2;
|
|
47326
|
+
for (const node of child.children) if (node.type === "text") readable += node.value;
|
|
47327
|
+
found.push([
|
|
47328
|
+
start,
|
|
47329
|
+
readable.length - 2,
|
|
47330
|
+
child.url
|
|
47331
|
+
]);
|
|
47332
|
+
} else return void 0;
|
|
47333
|
+
return readable === `x ${text}` ? found : void 0;
|
|
47334
|
+
}
|
|
47183
47335
|
function literals(ctx, texts) {
|
|
47184
47336
|
const possible = texts.map((text) => /www\.|https?:\/\/|@/i.test(text));
|
|
47185
47337
|
const candidates = texts.filter((_, index) => possible[index]);
|
|
47186
47338
|
const found = read(ctx, candidates.map((text) => text.replace(/[\\`[\]<>]/g, "\\$&")), candidates);
|
|
47187
|
-
return texts.map((
|
|
47339
|
+
return texts.map((text, index) => possible[index] ? endAtBackticks(ctx, found.shift() ?? literalFormattingLinks(text), text) : []);
|
|
47340
|
+
}
|
|
47341
|
+
function endAtBackticks(ctx, found, text) {
|
|
47342
|
+
if (!found) return found;
|
|
47343
|
+
const result = [];
|
|
47344
|
+
for (const [start, end, href] of found) {
|
|
47345
|
+
const parts = text.slice(start, end).split("`");
|
|
47346
|
+
if (parts.length === 1) {
|
|
47347
|
+
result.push([
|
|
47348
|
+
start,
|
|
47349
|
+
end,
|
|
47350
|
+
href
|
|
47351
|
+
]);
|
|
47352
|
+
continue;
|
|
47353
|
+
}
|
|
47354
|
+
let at = start;
|
|
47355
|
+
literals(ctx, parts).forEach((links, index) => {
|
|
47356
|
+
for (const [from, to, target] of links ?? []) result.push([
|
|
47357
|
+
at + from,
|
|
47358
|
+
at + to,
|
|
47359
|
+
target
|
|
47360
|
+
]);
|
|
47361
|
+
at += parts[index].length + 1;
|
|
47362
|
+
});
|
|
47363
|
+
}
|
|
47364
|
+
return result;
|
|
47188
47365
|
}
|
|
47189
47366
|
function angleHref(text) {
|
|
47190
47367
|
if (/^[a-zA-Z][a-zA-Z0-9+.-]{1,31}:[^\s<>\u0000-\u001f]*$/.test(text)) return text;
|
|
47191
47368
|
if (/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/.test(text)) return `mailto:${text}`;
|
|
47192
47369
|
}
|
|
47370
|
+
function completedAngle(tr, chars, from, to) {
|
|
47371
|
+
if (to !== from + 1) return void 0;
|
|
47372
|
+
const end = chars.findIndex((char) => char?.pos === from);
|
|
47373
|
+
const closing = chars[end];
|
|
47374
|
+
if (closing?.text !== ">" || closing.marks.some((mark) => mark.type.spec.code) || closing.link && closing.form !== "literal") return void 0;
|
|
47375
|
+
let start = end - 1;
|
|
47376
|
+
for (; start >= 0; start--) {
|
|
47377
|
+
const char = chars[start];
|
|
47378
|
+
if (!char || char.marks.some((mark) => mark.type.spec.code) || char.link && char.form !== "literal" || !sameMarks(char.marks, closing.marks) || /[\s>]/.test(char.text)) return void 0;
|
|
47379
|
+
if (char.text === "<") break;
|
|
47380
|
+
}
|
|
47381
|
+
if (start < 0) return void 0;
|
|
47382
|
+
const href = angleHref(chars.slice(start + 1, end).map((char) => char.text).join(""));
|
|
47383
|
+
if (!href) return void 0;
|
|
47384
|
+
const opening = chars[start].pos;
|
|
47385
|
+
return tr.delete(from, to).delete(opening, opening + 1).addMark(opening, from - 1, tr.doc.type.schema.marks.link.create({
|
|
47386
|
+
href,
|
|
47387
|
+
form: "angle"
|
|
47388
|
+
})).removeStoredMark(tr.doc.type.schema.marks.link);
|
|
47389
|
+
}
|
|
47390
|
+
function closeAngleEnds(tr, transactions) {
|
|
47391
|
+
const steps = transactions.flatMap((transaction) => transaction.steps.map((step, index) => ({
|
|
47392
|
+
doc: transaction.docs[index],
|
|
47393
|
+
map: step.getMap()
|
|
47394
|
+
})));
|
|
47395
|
+
steps.forEach(({ doc, map }, index) => {
|
|
47396
|
+
const remaining = new Mapping(steps.slice(index + 1).map(({ map }) => map));
|
|
47397
|
+
map.forEach((from, to, start, end) => {
|
|
47398
|
+
if (from !== to || start === end) return;
|
|
47399
|
+
const at = doc.resolve(from);
|
|
47400
|
+
const before = at.nodeBefore?.marks.find((mark) => mark.attrs.form === "angle");
|
|
47401
|
+
const after = at.nodeAfter?.marks.find((mark) => mark.attrs.form === "angle");
|
|
47402
|
+
if (before && after && before.eq(after)) return;
|
|
47403
|
+
for (const mark of [before, after]) if (mark) tr.removeMark(remaining.map(start, 1), remaining.map(end, -1), mark);
|
|
47404
|
+
});
|
|
47405
|
+
});
|
|
47406
|
+
}
|
|
47407
|
+
function codeAngles(tr, edited) {
|
|
47408
|
+
const spans = [];
|
|
47409
|
+
for (const block of edited) tr.doc.nodesBetween(block.pos, block.pos + block.block.nodeSize, (node, pos) => {
|
|
47410
|
+
if (!node.isText || !node.marks.some((mark) => mark.type.spec.code)) return;
|
|
47411
|
+
const link = node.marks.find((mark) => mark.attrs.form === "angle");
|
|
47412
|
+
if (link) spans.push({
|
|
47413
|
+
from: pos,
|
|
47414
|
+
to: pos + node.nodeSize,
|
|
47415
|
+
marks: node.marks.filter((mark) => mark !== link)
|
|
47416
|
+
});
|
|
47417
|
+
});
|
|
47418
|
+
for (const { from, to, marks } of spans.reverse()) {
|
|
47419
|
+
tr.removeMark(from, to, tr.doc.type.schema.marks.link);
|
|
47420
|
+
tr.insert(to, tr.doc.type.schema.text(">", marks));
|
|
47421
|
+
tr.insert(from, tr.doc.type.schema.text("<", marks));
|
|
47422
|
+
}
|
|
47423
|
+
}
|
|
47193
47424
|
function wantedLinks(ctx, chars, from, to) {
|
|
47194
47425
|
const wanted = /* @__PURE__ */ new Map();
|
|
47195
47426
|
const released = /* @__PURE__ */ new Set();
|
|
@@ -47214,6 +47445,20 @@ function wantedLinks(ctx, chars, from, to) {
|
|
|
47214
47445
|
}
|
|
47215
47446
|
index = end;
|
|
47216
47447
|
}
|
|
47448
|
+
const codeAddress = (index) => {
|
|
47449
|
+
const char = chars[index];
|
|
47450
|
+
return char?.form === "literal" && char.marks.some((mark) => mark.type.spec.code);
|
|
47451
|
+
};
|
|
47452
|
+
for (let index = 0; index < chars.length;) {
|
|
47453
|
+
if (!codeAddress(index)) {
|
|
47454
|
+
index++;
|
|
47455
|
+
continue;
|
|
47456
|
+
}
|
|
47457
|
+
let end = index + 1;
|
|
47458
|
+
while (end < chars.length && codeAddress(end)) end++;
|
|
47459
|
+
if (end >= from && index <= to) for (let at = index; at < end; at++) wanted.set(at, null);
|
|
47460
|
+
index = end;
|
|
47461
|
+
}
|
|
47217
47462
|
const free = (index) => {
|
|
47218
47463
|
const char = chars[index];
|
|
47219
47464
|
return char !== void 0 && !char.marks.some((mark) => mark.type.spec.code) && (released.has(index) || char.form !== "angle" && !(char.link && !char.form));
|
|
@@ -47360,8 +47605,25 @@ var autolinkLiterals = $prose((ctx) => new Plugin({
|
|
|
47360
47605
|
key: new PluginKey("autolinkLiterals"),
|
|
47361
47606
|
appendTransaction(transactions, oldState, newState) {
|
|
47362
47607
|
if (!transactions.some((tr) => tr.docChanged)) return null;
|
|
47608
|
+
const base = newState.tr;
|
|
47609
|
+
const written = transactions.every(isWrittenEdit);
|
|
47610
|
+
if (written) {
|
|
47611
|
+
closeAngleEnds(base, transactions);
|
|
47612
|
+
codeAngles(base, editedBlocks(oldState.doc, base.doc));
|
|
47613
|
+
}
|
|
47614
|
+
let edited = editedBlocks(oldState.doc, base.doc);
|
|
47615
|
+
if (written && edited.length === 1) {
|
|
47616
|
+
const { pos, block, from, to } = edited[0];
|
|
47617
|
+
const start = base.steps.length;
|
|
47618
|
+
if (completedAngle(base, characters(block, pos + 1), from, to)) {
|
|
47619
|
+
edited = editedBlocks(oldState.doc, base.doc);
|
|
47620
|
+
const mapping = base.mapping.slice(start);
|
|
47621
|
+
edited[0].from = Math.min(edited[0].from, mapping.map(from, -1));
|
|
47622
|
+
edited[0].to = Math.max(edited[0].to, mapping.map(to, 1));
|
|
47623
|
+
}
|
|
47624
|
+
}
|
|
47363
47625
|
const blocks = [];
|
|
47364
|
-
for (const { pos, block, from, to } of
|
|
47626
|
+
for (const { pos, block, from, to } of edited) {
|
|
47365
47627
|
const chars = characters(block, pos + 1);
|
|
47366
47628
|
const index = (at) => {
|
|
47367
47629
|
const found = chars.findIndex((char) => char !== void 0 && char.pos >= at);
|
|
@@ -47374,15 +47636,32 @@ var autolinkLiterals = $prose((ctx) => new Plugin({
|
|
|
47374
47636
|
wanted
|
|
47375
47637
|
});
|
|
47376
47638
|
}
|
|
47377
|
-
if (!blocks.length) return null;
|
|
47378
|
-
|
|
47379
|
-
const
|
|
47380
|
-
|
|
47381
|
-
|
|
47382
|
-
|
|
47383
|
-
|
|
47384
|
-
|
|
47385
|
-
|
|
47639
|
+
if (!base.steps.length && !blocks.length) return null;
|
|
47640
|
+
const stored = base.storedMarks ?? newState.storedMarks;
|
|
47641
|
+
const finish = (tr) => {
|
|
47642
|
+
if (stored) tr.setStoredMarks(stored);
|
|
47643
|
+
if (!tr.steps.length) return null;
|
|
47644
|
+
for (const plugin of newState.plugins) if (plugin.spec.isInputRules) for (const transaction of transactions) {
|
|
47645
|
+
const meta = transaction.getMeta(plugin);
|
|
47646
|
+
if (meta !== void 0) tr.setMeta(plugin, meta);
|
|
47647
|
+
}
|
|
47648
|
+
return tr;
|
|
47649
|
+
};
|
|
47650
|
+
const attempt = (wanted) => {
|
|
47651
|
+
const tr = newState.tr;
|
|
47652
|
+
for (const step of base.steps) tr.step(step);
|
|
47653
|
+
tr.setSelection(base.selection.getBookmark().resolve(tr.doc));
|
|
47654
|
+
return linked(tr, wanted);
|
|
47655
|
+
};
|
|
47656
|
+
let tr = attempt(blocks);
|
|
47657
|
+
const failed = edited.filter((block) => !readsBack(ctx, tr, block.pos));
|
|
47658
|
+
if (!failed.length) return finish(tr);
|
|
47659
|
+
for (const block of blocks) if (failed.some(({ pos }) => pos === block.pos)) {
|
|
47660
|
+
for (const [at, want] of block.wanted) if (want?.form === "literal") block.wanted.set(at, null);
|
|
47661
|
+
}
|
|
47662
|
+
tr = attempt(blocks);
|
|
47663
|
+
if (edited.every((block) => readsBack(ctx, tr, block.pos))) return finish(tr);
|
|
47664
|
+
return null;
|
|
47386
47665
|
}
|
|
47387
47666
|
}));
|
|
47388
47667
|
//#endregion
|
|
@@ -49720,7 +49999,8 @@ function formattedOutside(node) {
|
|
|
49720
49999
|
};
|
|
49721
50000
|
return result;
|
|
49722
50001
|
}
|
|
49723
|
-
function written(node) {
|
|
50002
|
+
function written(node, inTable = false) {
|
|
50003
|
+
if (inTable && node.url.includes("|")) return "brackets";
|
|
49724
50004
|
if (bracketLinks.has(node)) return "brackets";
|
|
49725
50005
|
const form = formOf(node);
|
|
49726
50006
|
if (form === "literal" && bareLinks.has(node)) return "bare";
|
|
@@ -49736,12 +50016,12 @@ function withResourceLink(state, resourceLink, write) {
|
|
|
49736
50016
|
}
|
|
49737
50017
|
}
|
|
49738
50018
|
var link = (node, parent, state, info) => {
|
|
49739
|
-
const form = written(node);
|
|
50019
|
+
const form = written(node, state.stack.includes("tableCell"));
|
|
49740
50020
|
if (form === "bare") return node.children[0].value;
|
|
49741
50021
|
return withResourceLink(state, form === "brackets", () => handle.link(node, parent, state, info));
|
|
49742
50022
|
};
|
|
49743
50023
|
link.peek = (node, parent, state) => {
|
|
49744
|
-
const form = written(node);
|
|
50024
|
+
const form = written(node, state.stack.includes("tableCell"));
|
|
49745
50025
|
if (form === "bare") return node.children[0].value[0];
|
|
49746
50026
|
return withResourceLink(state, form === "brackets", () => handle.link.peek(node, parent, state));
|
|
49747
50027
|
};
|
|
@@ -50096,7 +50376,11 @@ function createDialect(images, wikiLinks = false, files = false) {
|
|
|
50096
50376
|
}
|
|
50097
50377
|
})),
|
|
50098
50378
|
references,
|
|
50099
|
-
wikiLinks ?
|
|
50379
|
+
wikiLinks ? [
|
|
50380
|
+
wikiLinkSchema,
|
|
50381
|
+
wikiLinkComposition,
|
|
50382
|
+
wikiLinkInputRule
|
|
50383
|
+
] : [],
|
|
50100
50384
|
callouts,
|
|
50101
50385
|
comments,
|
|
50102
50386
|
hiddenCommentEditing,
|
|
@@ -56557,7 +56841,7 @@ var InkKitEditor = class InkKitEditor {
|
|
|
56557
56841
|
return true;
|
|
56558
56842
|
}
|
|
56559
56843
|
}));
|
|
56560
|
-
}).use(labelsCtx).use(instance.pasteController.plugin).use(visibleClipboard).use($prose(() => instance.suggestions.plugin())).use(instance.keymapPlugin).use(createDialect(Boolean(options.images || options.files), Boolean(options.wikiLinks), Boolean(options.files))).use(tablePlugins).use(tableBlock).use(instance.changePlugin).use(instance.historyInputPlugin).use(history).use(clipboard).use(cursor).use(taskListPlugin).use(quoteBackspace).use(calloutEnter).use(literalEnter).use(literalReread).use(listItemBackspace).use(listItemShiftTab).use(nestedItemEnter).use(rejoinLists).use(movedListItems).use(textInputPolicy).use(events.copy ? codeCopyPlugin((text) => events.copy?.(text)) : []).use(placeholderPlugin).use(options.rendering?.codeHighlighting === false ? [] : (await import("./highlight-
|
|
56844
|
+
}).use(labelsCtx).use(instance.pasteController.plugin).use(visibleClipboard).use($prose(() => instance.suggestions.plugin())).use(instance.keymapPlugin).use(createDialect(Boolean(options.images || options.files), Boolean(options.wikiLinks), Boolean(options.files))).use(tablePlugins).use(tableBlock).use(instance.changePlugin).use(instance.historyInputPlugin).use(history).use(clipboard).use(cursor).use(taskListPlugin).use(quoteBackspace).use(calloutEnter).use(literalEnter).use(literalReread).use(listItemBackspace).use(listItemShiftTab).use(nestedItemEnter).use(rejoinLists).use(movedListItems).use(textInputPolicy).use(events.copy ? codeCopyPlugin((text) => events.copy?.(text)) : []).use(placeholderPlugin).use(options.rendering?.codeHighlighting === false ? [] : (await import("./highlight-DOQk0Eyj.js")).highlightPlugin).use(options.rendering?.diagramPreview === false || !diagramsAvailable() ? [] : mermaidPreview((error) => instance.report(error))).use(selectionPlugin).use($prose(() => search())).use(instance.policyPlugin).use(options.wikiLinks ? wikiLinkView(options.wikiLinks, {
|
|
56561
56845
|
capture: () => instance.adapterCapture(),
|
|
56562
56846
|
active: () => instance.ready && !instance.destroyed && !instance.literalSurface,
|
|
56563
56847
|
labels: {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as Decoration, c as PluginKey, d as __toESM, i as $prose, o as DecorationSet, s as Plugin, u as __commonJSMin } from "./core-
|
|
1
|
+
import { a as Decoration, c as PluginKey, d as __toESM, i as $prose, o as DecorationSet, s as Plugin, u as __commonJSMin } from "./core-DP-izJDM.js";
|
|
2
2
|
//#region node_modules/.pnpm/highlight.js@11.11.2/node_modules/highlight.js/es/languages/arduino.js
|
|
3
3
|
/** @type LanguageFn */
|
|
4
4
|
function cPlusPlus(hljs) {
|
package/dist/core.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { r as InkKitError, t as InkKitEditor } from "./chunks/core-
|
|
1
|
+
import { r as InkKitError, t as InkKitEditor } from "./chunks/core-DP-izJDM.js";
|
|
2
2
|
export { InkKitEditor, InkKitError };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as registerMermaid, r as InkKitError, t as InkKitEditor } from "./chunks/core-
|
|
1
|
+
import { n as registerMermaid, r as InkKitError, t as InkKitEditor } from "./chunks/core-DP-izJDM.js";
|
|
2
2
|
//#region src/index.ts
|
|
3
3
|
registerMermaid(async () => (await import("mermaid")).default);
|
|
4
4
|
//#endregion
|