@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
- return ctx.get(remarkCtx).parse(text);
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
- if (carries) {
46967
- let output;
46968
- try {
46969
- output = this.writeDocument(doc, current, pairing);
46970
- } catch {
46971
- output = void 0;
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
- if (output !== void 0 && this.reopens(doc, output, canonical)) return output;
46974
- }
46975
- const output = this.writeDocument(doc, current, carries ? {
46976
- positions: correspondence(this.blockForms, current),
46977
- carried: nothingCarried
46978
- } : pairing);
46979
- if (this.reopens(doc, output, canonical)) return output;
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((_, index) => possible[index] ? found.shift() : []);
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 editedBlocks(oldState.doc, newState.doc)) {
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
- let tr = linked(newState.tr, blocks);
47379
- const failed = blocks.filter((block) => !readsBack(ctx, tr, block.pos));
47380
- if (!failed.length) return tr;
47381
- for (const block of failed) for (const [at, want] of block.wanted) if (want?.form === "literal") block.wanted.set(at, null);
47382
- tr = linked(newState.tr, blocks);
47383
- const kept = blocks.filter((block) => readsBack(ctx, tr, block.pos));
47384
- if (kept.length === blocks.length) return tr;
47385
- return kept.length ? linked(newState.tr, kept) : null;
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 ? wikiLinkSchema : [],
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-yHFoKkKz.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, {
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-Bvj0PMMi.js";
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-Bvj0PMMi.js";
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-Bvj0PMMi.js";
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aicayzer/inkkit",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "A preserving Markdown editor for offline web views and the web",
5
5
  "license": "MIT",
6
6
  "type": "module",