@aicayzer/inkkit 0.3.6 → 0.4.0
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/dist/chunks/{core-DtLNFuDg.js → core-Dl4jPn_d.js} +1989 -117
- package/dist/chunks/{highlight-wulvkYDg.js → highlight-4lYN38Zo.js} +1 -1
- package/dist/core.d.ts +1 -1
- package/dist/core.js +1 -1
- package/dist/editor.d.ts +37 -19
- package/dist/formats/convert.d.ts +17 -0
- package/dist/formats/index.d.ts +5 -3
- package/dist/formats/rich-text-import.d.ts +11 -0
- package/dist/formats/rich-text-model.d.ts +56 -0
- package/dist/formats/rich-text-prose.d.ts +23 -0
- package/dist/formats/rich-text.d.ts +5 -0
- package/dist/formats/types.d.ts +40 -4
- package/dist/index.js +1 -1
- package/dist/types.d.ts +94 -7
- package/package.json +1 -1
|
@@ -6759,7 +6759,7 @@ function tokenizeThematicBreak(effects, ok, nok) {
|
|
|
6759
6759
|
* } from 'micromark-util-types'
|
|
6760
6760
|
*/
|
|
6761
6761
|
/** @type {Construct} */
|
|
6762
|
-
var list$
|
|
6762
|
+
var list$4 = {
|
|
6763
6763
|
continuation: { tokenize: tokenizeListContinuation },
|
|
6764
6764
|
exit: tokenizeListEnd,
|
|
6765
6765
|
name: "list",
|
|
@@ -6879,7 +6879,7 @@ function tokenizeListContinuation(effects, ok, nok) {
|
|
|
6879
6879
|
function notInCurrentItem(code) {
|
|
6880
6880
|
self.containerState._closeFlow = true;
|
|
6881
6881
|
self.interrupt = void 0;
|
|
6882
|
-
return factorySpace(effects, effects.attempt(list$
|
|
6882
|
+
return factorySpace(effects, effects.attempt(list$4, ok, nok), "linePrefix", self.parser.constructs.disable.null.includes("codeIndented") ? void 0 : 4)(code);
|
|
6883
6883
|
}
|
|
6884
6884
|
}
|
|
6885
6885
|
/**
|
|
@@ -7313,19 +7313,19 @@ var constructs_exports = /* @__PURE__ */ __exportAll({
|
|
|
7313
7313
|
});
|
|
7314
7314
|
/** @satisfies {Extension['document']} */
|
|
7315
7315
|
var document$1 = {
|
|
7316
|
-
[42]: list$
|
|
7317
|
-
[43]: list$
|
|
7318
|
-
[45]: list$
|
|
7319
|
-
[48]: list$
|
|
7320
|
-
[49]: list$
|
|
7321
|
-
[50]: list$
|
|
7322
|
-
[51]: list$
|
|
7323
|
-
[52]: list$
|
|
7324
|
-
[53]: list$
|
|
7325
|
-
[54]: list$
|
|
7326
|
-
[55]: list$
|
|
7327
|
-
[56]: list$
|
|
7328
|
-
[57]: list$
|
|
7316
|
+
[42]: list$4,
|
|
7317
|
+
[43]: list$4,
|
|
7318
|
+
[45]: list$4,
|
|
7319
|
+
[48]: list$4,
|
|
7320
|
+
[49]: list$4,
|
|
7321
|
+
[50]: list$4,
|
|
7322
|
+
[51]: list$4,
|
|
7323
|
+
[52]: list$4,
|
|
7324
|
+
[53]: list$4,
|
|
7325
|
+
[54]: list$4,
|
|
7326
|
+
[55]: list$4,
|
|
7327
|
+
[56]: list$4,
|
|
7328
|
+
[57]: list$4,
|
|
7329
7329
|
[62]: blockQuote
|
|
7330
7330
|
};
|
|
7331
7331
|
/** @satisfies {Extension['contentInitial']} */
|
|
@@ -9452,10 +9452,10 @@ function configure(base, extension) {
|
|
|
9452
9452
|
case "extensions": break;
|
|
9453
9453
|
/* c8 ignore next 4 */
|
|
9454
9454
|
case "unsafe":
|
|
9455
|
-
list$
|
|
9455
|
+
list$3(base[key], extension[key]);
|
|
9456
9456
|
break;
|
|
9457
9457
|
case "join":
|
|
9458
|
-
list$
|
|
9458
|
+
list$3(base[key], extension[key]);
|
|
9459
9459
|
break;
|
|
9460
9460
|
case "handlers":
|
|
9461
9461
|
map$2(base[key], extension[key]);
|
|
@@ -9476,7 +9476,7 @@ function configure(base, extension) {
|
|
|
9476
9476
|
* @returns {undefined}
|
|
9477
9477
|
* Nothing.
|
|
9478
9478
|
*/
|
|
9479
|
-
function list$
|
|
9479
|
+
function list$3(left, right) {
|
|
9480
9480
|
if (right) left.push(...right);
|
|
9481
9481
|
}
|
|
9482
9482
|
/**
|
|
@@ -11096,7 +11096,7 @@ function checkRule(state) {
|
|
|
11096
11096
|
* @returns {string}
|
|
11097
11097
|
* Serialized markdown.
|
|
11098
11098
|
*/
|
|
11099
|
-
function list$
|
|
11099
|
+
function list$2(node, parent, state, info) {
|
|
11100
11100
|
const exit = state.enter("list");
|
|
11101
11101
|
const bulletCurrent = state.bulletCurrent;
|
|
11102
11102
|
/** @type {string} */
|
|
@@ -11416,7 +11416,7 @@ var handle = {
|
|
|
11416
11416
|
inlineCode,
|
|
11417
11417
|
link: link$1,
|
|
11418
11418
|
linkReference,
|
|
11419
|
-
list: list$
|
|
11419
|
+
list: list$2,
|
|
11420
11420
|
listItem,
|
|
11421
11421
|
paragraph: paragraph$1,
|
|
11422
11422
|
root: root$1,
|
|
@@ -12759,7 +12759,7 @@ function toMarkdown(tree, options) {
|
|
|
12759
12759
|
configure(state, settings);
|
|
12760
12760
|
if (state.options.tightDefinitions) state.join.push(joinDefinition);
|
|
12761
12761
|
state.handle = zwitch("type", {
|
|
12762
|
-
invalid,
|
|
12762
|
+
invalid: invalid$1,
|
|
12763
12763
|
unknown,
|
|
12764
12764
|
handlers: state.handlers
|
|
12765
12765
|
});
|
|
@@ -12806,7 +12806,7 @@ function toMarkdown(tree, options) {
|
|
|
12806
12806
|
* @throws {Error}
|
|
12807
12807
|
* Always.
|
|
12808
12808
|
*/
|
|
12809
|
-
function invalid(value) {
|
|
12809
|
+
function invalid$1(value) {
|
|
12810
12810
|
throw new Error("Cannot handle value `" + value + "`, expected node");
|
|
12811
12811
|
}
|
|
12812
12812
|
/**
|
|
@@ -17808,7 +17808,7 @@ var DOMParser$1 = class DOMParser$1 {
|
|
|
17808
17808
|
return schema.cached.domParser || (schema.cached.domParser = new DOMParser$1(schema, DOMParser$1.schemaRules(schema)));
|
|
17809
17809
|
}
|
|
17810
17810
|
};
|
|
17811
|
-
var blockTags = {
|
|
17811
|
+
var blockTags$1 = {
|
|
17812
17812
|
address: true,
|
|
17813
17813
|
article: true,
|
|
17814
17814
|
aside: true,
|
|
@@ -17904,7 +17904,7 @@ var NodeContext = class {
|
|
|
17904
17904
|
inlineContext(node) {
|
|
17905
17905
|
if (this.type) return this.type.inlineContent;
|
|
17906
17906
|
if (this.content.length) return this.content[0].isInline;
|
|
17907
|
-
return node.parentNode && !blockTags.hasOwnProperty(node.parentNode.nodeName.toLowerCase());
|
|
17907
|
+
return node.parentNode && !blockTags$1.hasOwnProperty(node.parentNode.nodeName.toLowerCase());
|
|
17908
17908
|
}
|
|
17909
17909
|
};
|
|
17910
17910
|
var ParseContext = class {
|
|
@@ -17968,7 +17968,7 @@ var ParseContext = class {
|
|
|
17968
17968
|
if (rule && rule.closeParent) this.open = Math.max(0, this.open - 1);
|
|
17969
17969
|
else if (rule && rule.skip.nodeType) dom = rule.skip;
|
|
17970
17970
|
let sync, oldNeedsBlock = this.needsBlock;
|
|
17971
|
-
if (blockTags.hasOwnProperty(name)) {
|
|
17971
|
+
if (blockTags$1.hasOwnProperty(name)) {
|
|
17972
17972
|
if (top.content.length && top.content[0].isInline && this.open) {
|
|
17973
17973
|
this.open--;
|
|
17974
17974
|
top = this.top;
|
|
@@ -28368,6 +28368,7 @@ var defaultLabels = {
|
|
|
28368
28368
|
slashWarning: "Warning",
|
|
28369
28369
|
slashCaution: "Caution",
|
|
28370
28370
|
formattedEditor: "Formatted Markdown editor",
|
|
28371
|
+
richTextEditor: "Rich text editor",
|
|
28371
28372
|
plainTextEditor: "Plain text editor",
|
|
28372
28373
|
sourceEditor: "Markdown source editor",
|
|
28373
28374
|
placeholder: "Start typing…",
|
|
@@ -28408,6 +28409,33 @@ function editorLabels(ctx) {
|
|
|
28408
28409
|
}
|
|
28409
28410
|
//#endregion
|
|
28410
28411
|
//#region src/types.ts
|
|
28412
|
+
/** The closed list of what a conversion can lose. The API reference describes each one. */
|
|
28413
|
+
var formatLossCodes = [
|
|
28414
|
+
"heading",
|
|
28415
|
+
"code-block",
|
|
28416
|
+
"diagram",
|
|
28417
|
+
"quote",
|
|
28418
|
+
"callout",
|
|
28419
|
+
"task",
|
|
28420
|
+
"rule",
|
|
28421
|
+
"footnote",
|
|
28422
|
+
"comment",
|
|
28423
|
+
"highlight",
|
|
28424
|
+
"image",
|
|
28425
|
+
"file",
|
|
28426
|
+
"wiki-link",
|
|
28427
|
+
"literal",
|
|
28428
|
+
"link",
|
|
28429
|
+
"table-alignment",
|
|
28430
|
+
"list-start",
|
|
28431
|
+
"list-item-blocks",
|
|
28432
|
+
"list-depth",
|
|
28433
|
+
"link-underline",
|
|
28434
|
+
"invalid-character",
|
|
28435
|
+
"alignment",
|
|
28436
|
+
"mark-spaces",
|
|
28437
|
+
"empty-line"
|
|
28438
|
+
];
|
|
28411
28439
|
/** Every failure InkKit reports. Hosts branch on `code`; `message` is for logs. When the failure
|
|
28412
28440
|
* started elsewhere, such as in an adapter, the host clipboard or the renderer, `cause` holds the
|
|
28413
28441
|
* original error. */
|
|
@@ -28940,6 +28968,61 @@ function remarkLinkForms() {
|
|
|
28940
28968
|
//#endregion
|
|
28941
28969
|
//#region node_modules/.pnpm/prosemirror-schema-list@1.5.1/node_modules/prosemirror-schema-list/dist/index.js
|
|
28942
28970
|
/**
|
|
28971
|
+
Returns a command function that wraps the selection in a list with
|
|
28972
|
+
the given type an attributes. If `dispatch` is null, only return a
|
|
28973
|
+
value to indicate whether this is possible, but don't actually
|
|
28974
|
+
perform the change.
|
|
28975
|
+
*/
|
|
28976
|
+
function wrapInList(listType, attrs = null) {
|
|
28977
|
+
return function(state, dispatch) {
|
|
28978
|
+
let { $from, $to } = state.selection;
|
|
28979
|
+
let range = $from.blockRange($to);
|
|
28980
|
+
if (!range) return false;
|
|
28981
|
+
let tr = dispatch ? state.tr : null;
|
|
28982
|
+
if (!wrapRangeInList(tr, range, listType, attrs)) return false;
|
|
28983
|
+
if (dispatch) dispatch(tr.scrollIntoView());
|
|
28984
|
+
return true;
|
|
28985
|
+
};
|
|
28986
|
+
}
|
|
28987
|
+
/**
|
|
28988
|
+
Try to wrap the given node range in a list of the given type.
|
|
28989
|
+
Return `true` when this is possible, `false` otherwise. When `tr`
|
|
28990
|
+
is non-null, the wrapping is added to that transaction. When it is
|
|
28991
|
+
`null`, the function only queries whether the wrapping is
|
|
28992
|
+
possible.
|
|
28993
|
+
*/
|
|
28994
|
+
function wrapRangeInList(tr, range, listType, attrs = null) {
|
|
28995
|
+
let doJoin = false, outerRange = range, doc = range.$from.doc;
|
|
28996
|
+
if (range.depth >= 2 && range.$from.node(range.depth - 1).type.compatibleContent(listType) && range.startIndex == 0) {
|
|
28997
|
+
if (range.$from.index(range.depth - 1) == 0) return false;
|
|
28998
|
+
let $insert = doc.resolve(range.start - 2);
|
|
28999
|
+
outerRange = new NodeRange($insert, $insert, range.depth);
|
|
29000
|
+
if (range.endIndex < range.parent.childCount) range = new NodeRange(range.$from, doc.resolve(range.$to.end(range.depth)), range.depth);
|
|
29001
|
+
doJoin = true;
|
|
29002
|
+
}
|
|
29003
|
+
let wrap = findWrapping(outerRange, listType, attrs, range);
|
|
29004
|
+
if (!wrap) return false;
|
|
29005
|
+
if (tr) doWrapInList(tr, range, wrap, doJoin, listType);
|
|
29006
|
+
return true;
|
|
29007
|
+
}
|
|
29008
|
+
function doWrapInList(tr, range, wrappers, joinBefore, listType) {
|
|
29009
|
+
let content = Fragment.empty;
|
|
29010
|
+
for (let i = wrappers.length - 1; i >= 0; i--) content = Fragment.from(wrappers[i].type.create(wrappers[i].attrs, content));
|
|
29011
|
+
tr.step(new ReplaceAroundStep(range.start - (joinBefore ? 2 : 0), range.end, range.start, range.end, new Slice(content, 0, 0), wrappers.length, true));
|
|
29012
|
+
let found = 0;
|
|
29013
|
+
for (let i = 0; i < wrappers.length; i++) if (wrappers[i].type == listType) found = i + 1;
|
|
29014
|
+
let splitDepth = wrappers.length - found;
|
|
29015
|
+
let splitPos = range.start + wrappers.length - (joinBefore ? 2 : 0), parent = range.parent;
|
|
29016
|
+
for (let i = range.startIndex, e = range.endIndex, first = true; i < e; i++, first = false) {
|
|
29017
|
+
if (!first && canSplit(tr.doc, splitPos, splitDepth)) {
|
|
29018
|
+
tr.split(splitPos, splitDepth);
|
|
29019
|
+
splitPos += 2 * splitDepth;
|
|
29020
|
+
}
|
|
29021
|
+
splitPos += parent.child(i).nodeSize;
|
|
29022
|
+
}
|
|
29023
|
+
return tr;
|
|
29024
|
+
}
|
|
29025
|
+
/**
|
|
28943
29026
|
Build a command that splits a non-empty textblock at the top level
|
|
28944
29027
|
of a list item by also splitting that list item.
|
|
28945
29028
|
*/
|
|
@@ -40191,26 +40274,32 @@ var quotes$1 = {
|
|
|
40191
40274
|
"\"": ["“", "”"],
|
|
40192
40275
|
"'": ["‘", "’"]
|
|
40193
40276
|
};
|
|
40277
|
+
/** Input rules that are not Markdown syntax, which apply in every formatted document. */
|
|
40278
|
+
var formatNeutralRules = /* @__PURE__ */ new WeakSet();
|
|
40279
|
+
function formatNeutral(rule) {
|
|
40280
|
+
formatNeutralRules.add(rule);
|
|
40281
|
+
return rule;
|
|
40282
|
+
}
|
|
40194
40283
|
var smartPunctuation = [
|
|
40195
40284
|
smartPunctuationCtx,
|
|
40196
40285
|
inputTracking,
|
|
40197
|
-
$inputRule((ctx) => new InputRule(/["']$/, (state, match, start, end) => {
|
|
40286
|
+
$inputRule((ctx) => formatNeutral(new InputRule(/["']$/, (state, match, start, end) => {
|
|
40198
40287
|
if (!applies(ctx.get(smartPunctuationCtx.key), state, match, start)) return null;
|
|
40199
40288
|
const [opening, closing] = quotes$1[match[0]];
|
|
40200
40289
|
return state.tr.insertText(opensHere(state, start) ? opening : closing, start, end).setMeta(smartRule, "quote");
|
|
40201
|
-
}, { inCodeMark: false })),
|
|
40202
|
-
$inputRule((ctx) => new InputRule(/--$/, (state, match, start, end) => {
|
|
40290
|
+
}, { inCodeMark: false }))),
|
|
40291
|
+
$inputRule((ctx) => formatNeutral(new InputRule(/--$/, (state, match, start, end) => {
|
|
40203
40292
|
if (!applies(ctx.get(smartPunctuationCtx.key), state, match, start)) return null;
|
|
40204
40293
|
if (input.getState(state)?.hyphen !== state.selection.from) return null;
|
|
40205
40294
|
const before = textBefore(state, start);
|
|
40206
40295
|
if (!before.trim() || before.endsWith("-") || /^\s*\|/.test(before)) return null;
|
|
40207
40296
|
return state.tr.insertText(enDash, start, end).setMeta(smartRule, "en");
|
|
40208
|
-
}, { inCodeMark: false })),
|
|
40209
|
-
$inputRule((ctx) => new InputRule(/\u2013-$/, (state, match, start, end) => {
|
|
40297
|
+
}, { inCodeMark: false }))),
|
|
40298
|
+
$inputRule((ctx) => formatNeutral(new InputRule(/\u2013-$/, (state, match, start, end) => {
|
|
40210
40299
|
if (!applies(ctx.get(smartPunctuationCtx.key), state, match, start)) return null;
|
|
40211
40300
|
if (lastRule(state)?.transform.getMeta(smartRule) !== "en") return null;
|
|
40212
40301
|
return state.tr.insertText(emDash, start, end).setMeta(smartRule, "em");
|
|
40213
|
-
}, { inCodeMark: false }))
|
|
40302
|
+
}, { inCodeMark: false })))
|
|
40214
40303
|
].flat();
|
|
40215
40304
|
/**
|
|
40216
40305
|
* Undo straight after a smart quote or dash puts back the character that was typed, as Backspace
|
|
@@ -42702,6 +42791,16 @@ function insertBlocks(view, blocks, point) {
|
|
|
42702
42791
|
view.dispatch(tr.setSelection(TextSelection.create(tr.doc, end)).scrollIntoView());
|
|
42703
42792
|
}
|
|
42704
42793
|
//#endregion
|
|
42794
|
+
//#region src/format-input.ts
|
|
42795
|
+
var markdown = {
|
|
42796
|
+
markdownInput: () => true,
|
|
42797
|
+
richText: () => void 0
|
|
42798
|
+
};
|
|
42799
|
+
var formatInputCtx = $ctx(markdown, "inkkitFormatInput");
|
|
42800
|
+
function formatInput(ctx) {
|
|
42801
|
+
return ctx.isInjected(formatInputCtx.key) ? ctx.get(formatInputCtx.key) : markdown;
|
|
42802
|
+
}
|
|
42803
|
+
//#endregion
|
|
42705
42804
|
//#region src/literals.ts
|
|
42706
42805
|
var unsupported = /* @__PURE__ */ new Set([
|
|
42707
42806
|
"html",
|
|
@@ -43650,6 +43749,7 @@ function editedBlocks(before, after) {
|
|
|
43650
43749
|
var autolinkLiterals = $prose((ctx) => new Plugin({
|
|
43651
43750
|
key: new PluginKey("autolinkLiterals"),
|
|
43652
43751
|
appendTransaction(transactions, oldState, newState) {
|
|
43752
|
+
if (!formatInput(ctx).markdownInput()) return null;
|
|
43653
43753
|
if (!transactions.some((tr) => tr.docChanged)) return null;
|
|
43654
43754
|
const base = newState.tr;
|
|
43655
43755
|
const written = transactions.every(isWrittenEdit);
|
|
@@ -46013,8 +46113,8 @@ function shareFormatting(children) {
|
|
|
46013
46113
|
};
|
|
46014
46114
|
const previous = children[index - 1];
|
|
46015
46115
|
const next = children[index + 1];
|
|
46016
|
-
const before = previous && same(previous, formatting);
|
|
46017
|
-
const after = next && same(next, formatting);
|
|
46116
|
+
const before = Boolean(previous && same(previous, formatting));
|
|
46117
|
+
const after = Boolean(next && same(next, formatting));
|
|
46018
46118
|
if (!before && !after) continue;
|
|
46019
46119
|
const merged = {
|
|
46020
46120
|
...formatting,
|
|
@@ -46271,6 +46371,26 @@ var preserveSpacerParagraphs = paragraphSchema.extendSchema((base) => (ctx) => {
|
|
|
46271
46371
|
const schema = base(ctx);
|
|
46272
46372
|
return {
|
|
46273
46373
|
...schema,
|
|
46374
|
+
attrs: {
|
|
46375
|
+
...schema.attrs,
|
|
46376
|
+
align: { default: null }
|
|
46377
|
+
},
|
|
46378
|
+
parseDOM: [{ tag: "p" }],
|
|
46379
|
+
toDOM: (node) => {
|
|
46380
|
+
const [tag, attrs, hole] = schema.toDOM(node);
|
|
46381
|
+
return node.attrs.align ? [
|
|
46382
|
+
tag,
|
|
46383
|
+
{
|
|
46384
|
+
...attrs,
|
|
46385
|
+
style: `text-align: ${String(node.attrs.align)}`
|
|
46386
|
+
},
|
|
46387
|
+
hole
|
|
46388
|
+
] : [
|
|
46389
|
+
tag,
|
|
46390
|
+
attrs,
|
|
46391
|
+
hole
|
|
46392
|
+
];
|
|
46393
|
+
},
|
|
46274
46394
|
parseMarkdown: {
|
|
46275
46395
|
...schema.parseMarkdown,
|
|
46276
46396
|
runner(state, node, type) {
|
|
@@ -46595,7 +46715,7 @@ function serialize(ctx, doc = ctx.get(editorViewCtx).state.doc) {
|
|
|
46595
46715
|
}
|
|
46596
46716
|
//#endregion
|
|
46597
46717
|
//#region src/editor-selection.ts
|
|
46598
|
-
var markNames = {
|
|
46718
|
+
var markNames$1 = {
|
|
46599
46719
|
strong: "bold",
|
|
46600
46720
|
emphasis: "italic",
|
|
46601
46721
|
strike_through: "strikethrough",
|
|
@@ -46640,10 +46760,10 @@ function caretState(state) {
|
|
|
46640
46760
|
const active = /* @__PURE__ */ new Set();
|
|
46641
46761
|
const marks = empty ? state.storedMarks ?? $from.marks() : [];
|
|
46642
46762
|
for (const mark of marks) {
|
|
46643
|
-
const name = markNames[mark.type.name];
|
|
46763
|
+
const name = markNames$1[mark.type.name];
|
|
46644
46764
|
if (name) active.add(name);
|
|
46645
46765
|
}
|
|
46646
|
-
if (!empty) for (const [name, mark] of Object.entries(markNames)) {
|
|
46766
|
+
if (!empty) for (const [name, mark] of Object.entries(markNames$1)) {
|
|
46647
46767
|
const type = state.schema.marks[name];
|
|
46648
46768
|
if (type && state.doc.rangeHasMark($from.pos, $to.pos, type)) active.add(mark);
|
|
46649
46769
|
}
|
|
@@ -46685,9 +46805,10 @@ function blockAt(parent, $from) {
|
|
|
46685
46805
|
}
|
|
46686
46806
|
//#endregion
|
|
46687
46807
|
//#region src/editor-links.ts
|
|
46688
|
-
function checkDestination(ctx, destination, name, linkSchemes) {
|
|
46808
|
+
function checkDestination(ctx, destination, name, linkSchemes, markdown = true) {
|
|
46689
46809
|
if (destination.trim() === "") throw invalidArgument(`${name} must not be empty`);
|
|
46690
46810
|
if (!isSafeLinkTarget(destination, linkSchemes)) throw invalidArgument(`${name} must be relative, or an address with an allowed scheme (http, https, mailto, tel or one of linkSchemes), with no control characters`);
|
|
46811
|
+
if (!markdown) return;
|
|
46691
46812
|
const { schema } = ctx.get(editorViewCtx).state;
|
|
46692
46813
|
const written = serialize(ctx, schema.node("doc", null, [schema.node("paragraph", null, [schema.text("x", [schema.marks.link.create({ href: destination })])])]));
|
|
46693
46814
|
let read;
|
|
@@ -47427,6 +47548,22 @@ var EditorClipboard = class {
|
|
|
47427
47548
|
return;
|
|
47428
47549
|
}
|
|
47429
47550
|
const view = this.runtime.ctx().get(editorViewCtx);
|
|
47551
|
+
const rich = this.runtime.handler().richText;
|
|
47552
|
+
if (rich) {
|
|
47553
|
+
if (!event.clipboardData || view.state.selection.empty) return;
|
|
47554
|
+
event.preventDefault();
|
|
47555
|
+
event.stopPropagation();
|
|
47556
|
+
try {
|
|
47557
|
+
this.runtime.snapshot();
|
|
47558
|
+
const output = rich.copy(view.state, this.runtime.linkSchemes());
|
|
47559
|
+
event.clipboardData.setData("text/plain", output.text);
|
|
47560
|
+
event.clipboardData.setData("text/html", output.html);
|
|
47561
|
+
if (event.type === "cut" && this.runtime.writable()) view.dispatch(view.state.tr.deleteSelection().scrollIntoView());
|
|
47562
|
+
} catch (error) {
|
|
47563
|
+
this.runtime.report(error);
|
|
47564
|
+
}
|
|
47565
|
+
return;
|
|
47566
|
+
}
|
|
47430
47567
|
const fragment = commentSelectionContent(view.state.doc, view.state.selection.from, view.state.selection.to, view.state.selection.content().content);
|
|
47431
47568
|
const expanded = selectionContent(view.state.doc, fragment);
|
|
47432
47569
|
let hasImages = false;
|
|
@@ -48988,7 +49125,7 @@ function correspondence(before, after) {
|
|
|
48988
49125
|
if (work.budget < 0) throw tooManyBlocks();
|
|
48989
49126
|
const anchors = uniqueAnchors(before, after, b, bEnd, a, aEnd);
|
|
48990
49127
|
if (!anchors.length) {
|
|
48991
|
-
align(before, after, b, bEnd, a, aEnd, result, work);
|
|
49128
|
+
align$1(before, after, b, bEnd, a, aEnd, result, work);
|
|
48992
49129
|
continue;
|
|
48993
49130
|
}
|
|
48994
49131
|
for (const anchor of anchors) {
|
|
@@ -49053,7 +49190,7 @@ function uniqueAnchors(before, after, b, bEnd, a, aEnd) {
|
|
|
49053
49190
|
}
|
|
49054
49191
|
var INDEL = 2;
|
|
49055
49192
|
var REPLACE = 3;
|
|
49056
|
-
function align(before, after, b, bEnd, a, aEnd, result, work) {
|
|
49193
|
+
function align$1(before, after, b, bEnd, a, aEnd, result, work) {
|
|
49057
49194
|
const rows = bEnd - b + 1;
|
|
49058
49195
|
const columns = aEnd - a + 1;
|
|
49059
49196
|
const offset = Math.abs(rows - columns);
|
|
@@ -51482,7 +51619,7 @@ function cleanSemantics(container, localIDs) {
|
|
|
51482
51619
|
if (title) element.setAttribute("title", title);
|
|
51483
51620
|
if (id && localIDs.has(id)) element.setAttribute("id", id);
|
|
51484
51621
|
if (order && /^\d+$/.test(order)) element.setAttribute("start", order);
|
|
51485
|
-
if (/^(left|center|right)$/.test(alignment) && /^(TH|TD)$/.test(element.tagName)) element.setAttribute("style", `text-align: ${alignment}`);
|
|
51622
|
+
if (/^(left|center|right)$/.test(alignment) && /^(TH|TD)$/.test(element.tagName) || /^(center|right|justify)$/.test(alignment) && element.tagName === "P") element.setAttribute("style", `text-align: ${alignment}`);
|
|
51486
51623
|
}
|
|
51487
51624
|
const walker = document.createTreeWalker(container, NodeFilter.SHOW_COMMENT);
|
|
51488
51625
|
const comments = [];
|
|
@@ -51601,10 +51738,12 @@ var markdownFormat = {
|
|
|
51601
51738
|
promise: "exact",
|
|
51602
51739
|
literalOnly: false,
|
|
51603
51740
|
sourceMode: true,
|
|
51741
|
+
formattedLabel: "formattedEditor",
|
|
51604
51742
|
literalLabel: "sourceEditor",
|
|
51605
51743
|
headings: true,
|
|
51606
51744
|
nativeCopy: false,
|
|
51607
51745
|
keepsInsertedLineEndings: true,
|
|
51746
|
+
markdownInput: true,
|
|
51608
51747
|
load: (ctx, text) => Preservation.read(ctx, text),
|
|
51609
51748
|
parse: (ctx, text) => Preservation.read(ctx, text),
|
|
51610
51749
|
pendingExport: (ctx, text) => ctx.get(parserCtx)(text),
|
|
@@ -51642,6 +51781,1067 @@ var markdownFormat = {
|
|
|
51642
51781
|
};
|
|
51643
51782
|
}
|
|
51644
51783
|
};
|
|
51784
|
+
/** The largest first number of an ordered list that RTF writes and reads back unchanged, as
|
|
51785
|
+
* measured with the host's converter. */
|
|
51786
|
+
var maximumListStart = 2147483647;
|
|
51787
|
+
/** Characters a run never holds: they would change the structure, stand for an attachment or end
|
|
51788
|
+
* the text when RTF is written. */
|
|
51789
|
+
var forbidden = /[\u0000\n\r\u000b\u000c\u0085\u2028\u2029\ufffc]/;
|
|
51790
|
+
var loneSurrogate = /[\ud800-\udbff](?![\udc00-\udfff])|(?<![\ud800-\udbff])[\udc00-\udfff]/;
|
|
51791
|
+
/** Why a run cannot hold `text`, or undefined. Loading and editing share the rule. */
|
|
51792
|
+
function textProblem(text) {
|
|
51793
|
+
const bad = forbidden.exec(text);
|
|
51794
|
+
if (bad) return `text cannot hold ${scalarName(bad[0])}`;
|
|
51795
|
+
if (loneSurrogate.test(text)) return "text cannot hold a lone surrogate";
|
|
51796
|
+
}
|
|
51797
|
+
/** Why `href` cannot be a link's address, or undefined. Any address that is not empty and holds
|
|
51798
|
+
* no control character is kept, as the host's converter keeps it; loading, editing, paste and the
|
|
51799
|
+
* converter share the rule. Edits and paste also apply Inkkit's link safety. */
|
|
51800
|
+
function linkProblem(href) {
|
|
51801
|
+
if (!href) return "a link needs an address";
|
|
51802
|
+
if (/[\u0000-\u001f\u007f-\u009f]/.test(href)) return "a link address cannot hold a control character";
|
|
51803
|
+
const problem = textProblem(href);
|
|
51804
|
+
return problem && `a link address ${problem.replace(/^text /, "")}`;
|
|
51805
|
+
}
|
|
51806
|
+
/** The canonical order of marks. */
|
|
51807
|
+
var markRank = {
|
|
51808
|
+
bold: 0,
|
|
51809
|
+
italic: 1,
|
|
51810
|
+
underline: 2,
|
|
51811
|
+
strikethrough: 3,
|
|
51812
|
+
superscript: 4,
|
|
51813
|
+
subscript: 5,
|
|
51814
|
+
code: 6,
|
|
51815
|
+
link: 7
|
|
51816
|
+
};
|
|
51817
|
+
function invalid(path, problem) {
|
|
51818
|
+
throw invalidArgument(`${path}: ${problem}`);
|
|
51819
|
+
}
|
|
51820
|
+
function record(value, path, keys) {
|
|
51821
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) invalid(path, "must be an object");
|
|
51822
|
+
for (const key of Object.keys(value)) if (!keys.includes(key)) invalid(`${path}.${key}`, "is not a known field");
|
|
51823
|
+
return value;
|
|
51824
|
+
}
|
|
51825
|
+
function list$1(value, path) {
|
|
51826
|
+
if (!Array.isArray(value)) invalid(path, "must be a list");
|
|
51827
|
+
return value;
|
|
51828
|
+
}
|
|
51829
|
+
function scalarName(text) {
|
|
51830
|
+
return `U+${text.codePointAt(0).toString(16).toUpperCase().padStart(4, "0")}`;
|
|
51831
|
+
}
|
|
51832
|
+
/** Collects inline content in canonical form: adjacent runs with equal marks merge, and each run
|
|
51833
|
+
* is NFC after merging, because joining two NFC strings can leave a sequence that composes. */
|
|
51834
|
+
var InlineBuilder = class {
|
|
51835
|
+
content = [];
|
|
51836
|
+
marksKey;
|
|
51837
|
+
text(text, marks) {
|
|
51838
|
+
if (!text) return;
|
|
51839
|
+
const key = JSON.stringify(marks);
|
|
51840
|
+
const last = this.content.at(-1);
|
|
51841
|
+
if (last?.type === "text" && this.marksKey === key) {
|
|
51842
|
+
last.text += text;
|
|
51843
|
+
return;
|
|
51844
|
+
}
|
|
51845
|
+
this.content.push(marks.length ? {
|
|
51846
|
+
type: "text",
|
|
51847
|
+
text,
|
|
51848
|
+
marks: [...marks]
|
|
51849
|
+
} : {
|
|
51850
|
+
type: "text",
|
|
51851
|
+
text
|
|
51852
|
+
});
|
|
51853
|
+
this.marksKey = key;
|
|
51854
|
+
}
|
|
51855
|
+
break() {
|
|
51856
|
+
this.content.push({ type: "break" });
|
|
51857
|
+
this.marksKey = void 0;
|
|
51858
|
+
}
|
|
51859
|
+
finish() {
|
|
51860
|
+
for (const inline of this.content) if (inline.type === "text") inline.text = inline.text.normalize("NFC");
|
|
51861
|
+
return this.content;
|
|
51862
|
+
}
|
|
51863
|
+
};
|
|
51864
|
+
/** Marks in canonical order. A link's run never also has underline: a link's underline is how it
|
|
51865
|
+
* is drawn, so RTF readers report it on every link. */
|
|
51866
|
+
function canonicalMarks(marks) {
|
|
51867
|
+
const sorted = [...marks].sort((a, b) => markRank[a.type] - markRank[b.type]);
|
|
51868
|
+
return sorted.some((mark) => mark.type === "link") ? sorted.filter((mark) => mark.type !== "underline") : sorted;
|
|
51869
|
+
}
|
|
51870
|
+
function readMarks(value, path) {
|
|
51871
|
+
const sorted = [...list$1(value, path).map((entry, index) => {
|
|
51872
|
+
const at = `${path}[${index}]`;
|
|
51873
|
+
const mark = record(entry, at, ["type", "href"]);
|
|
51874
|
+
const type = mark.type;
|
|
51875
|
+
if (typeof type !== "string" || !(type in markRank)) invalid(`${at}.type`, `unknown mark type ${JSON.stringify(type)}`);
|
|
51876
|
+
if (type === "link") {
|
|
51877
|
+
if (typeof mark.href !== "string") invalid(`${at}.href`, "must be text");
|
|
51878
|
+
return {
|
|
51879
|
+
type: "link",
|
|
51880
|
+
href: mark.href
|
|
51881
|
+
};
|
|
51882
|
+
}
|
|
51883
|
+
if (!isOmitted(mark.href)) invalid(`${at}.href`, "only a link has an address");
|
|
51884
|
+
return { type };
|
|
51885
|
+
})].sort((a, b) => markRank[a.type] - markRank[b.type]);
|
|
51886
|
+
const types = /* @__PURE__ */ new Set();
|
|
51887
|
+
for (const mark of sorted) {
|
|
51888
|
+
if (types.has(mark.type)) invalid(path, `the ${mark.type} mark appears more than once`);
|
|
51889
|
+
types.add(mark.type);
|
|
51890
|
+
const problem = mark.type === "link" ? linkProblem(mark.href) : void 0;
|
|
51891
|
+
if (problem) invalid(path, problem);
|
|
51892
|
+
}
|
|
51893
|
+
if (types.has("superscript") && types.has("subscript")) invalid(path, "a run cannot be both superscript and subscript");
|
|
51894
|
+
return canonicalMarks(sorted);
|
|
51895
|
+
}
|
|
51896
|
+
function readInlines(value, path) {
|
|
51897
|
+
const builder = new InlineBuilder();
|
|
51898
|
+
list$1(value, path).forEach((entry, index) => {
|
|
51899
|
+
const at = `${path}[${index}]`;
|
|
51900
|
+
const inline = record(entry, at, [
|
|
51901
|
+
"type",
|
|
51902
|
+
"text",
|
|
51903
|
+
"marks"
|
|
51904
|
+
]);
|
|
51905
|
+
if (inline.type === "break") {
|
|
51906
|
+
if (!isOmitted(inline.text) || !isOmitted(inline.marks)) invalid(at, "a break holds no text or marks");
|
|
51907
|
+
builder.break();
|
|
51908
|
+
return;
|
|
51909
|
+
}
|
|
51910
|
+
if (inline.type !== "text") invalid(`${at}.type`, `unknown inline type ${JSON.stringify(inline.type)}`);
|
|
51911
|
+
const text = inline.text;
|
|
51912
|
+
if (typeof text !== "string") invalid(`${at}.text`, "must be text");
|
|
51913
|
+
if (!text) invalid(`${at}.text`, "a text run cannot be empty");
|
|
51914
|
+
const problem = textProblem(text);
|
|
51915
|
+
if (problem) invalid(`${at}.text`, problem);
|
|
51916
|
+
builder.text(text, isOmitted(inline.marks) ? [] : readMarks(inline.marks, `${at}.marks`));
|
|
51917
|
+
});
|
|
51918
|
+
return builder.finish();
|
|
51919
|
+
}
|
|
51920
|
+
function readBlock(value, path, listDepth) {
|
|
51921
|
+
const block = record(value, path, [
|
|
51922
|
+
"type",
|
|
51923
|
+
"align",
|
|
51924
|
+
"content",
|
|
51925
|
+
"ordered",
|
|
51926
|
+
"start",
|
|
51927
|
+
"items",
|
|
51928
|
+
"rows"
|
|
51929
|
+
]);
|
|
51930
|
+
const only = (...keys) => {
|
|
51931
|
+
for (const key of Object.keys(block)) if (key !== "type" && !keys.includes(key)) invalid(`${path}.${key}`, `a ${String(block.type)} has no ${key}`);
|
|
51932
|
+
};
|
|
51933
|
+
switch (block.type) {
|
|
51934
|
+
case "paragraph": {
|
|
51935
|
+
only("align", "content");
|
|
51936
|
+
const align = block.align;
|
|
51937
|
+
if (!isOmitted(align) && align !== "center" && align !== "right" && align !== "justify") invalid(`${path}.align`, "must be center, right or justify");
|
|
51938
|
+
const content = readInlines(block.content, `${path}.content`);
|
|
51939
|
+
return isOmitted(align) ? {
|
|
51940
|
+
type: "paragraph",
|
|
51941
|
+
content
|
|
51942
|
+
} : {
|
|
51943
|
+
type: "paragraph",
|
|
51944
|
+
align,
|
|
51945
|
+
content
|
|
51946
|
+
};
|
|
51947
|
+
}
|
|
51948
|
+
case "list":
|
|
51949
|
+
only("ordered", "start", "items");
|
|
51950
|
+
return readList(block, path, listDepth + 1);
|
|
51951
|
+
case "table": {
|
|
51952
|
+
only("rows");
|
|
51953
|
+
if (listDepth > 0) invalid(path, "a table cannot be inside a list item");
|
|
51954
|
+
const rows = list$1(block.rows, `${path}.rows`);
|
|
51955
|
+
if (!rows.length) invalid(`${path}.rows`, "a table needs at least one row");
|
|
51956
|
+
const width = list$1(rows[0], `${path}.rows[0]`).length;
|
|
51957
|
+
if (!width) invalid(`${path}.rows[0]`, "a table row needs at least one cell");
|
|
51958
|
+
return {
|
|
51959
|
+
type: "table",
|
|
51960
|
+
rows: rows.map((row, rowIndex) => {
|
|
51961
|
+
const at = `${path}.rows[${rowIndex}]`;
|
|
51962
|
+
const cells = list$1(row, at);
|
|
51963
|
+
if (cells.length !== width) invalid(at, `the row has ${cells.length} cells, but the first row has ${width}`);
|
|
51964
|
+
return cells.map((cell, cellIndex) => {
|
|
51965
|
+
const cellPath = `${at}[${cellIndex}]`;
|
|
51966
|
+
const content = record(cell, cellPath, ["content"]).content;
|
|
51967
|
+
return { content: readInlines(content, `${cellPath}.content`) };
|
|
51968
|
+
});
|
|
51969
|
+
})
|
|
51970
|
+
};
|
|
51971
|
+
}
|
|
51972
|
+
default: invalid(`${path}.type`, `unknown block type ${JSON.stringify(block.type)}`);
|
|
51973
|
+
}
|
|
51974
|
+
}
|
|
51975
|
+
function readList(block, path, depth) {
|
|
51976
|
+
if (depth > 9) invalid(path, `lists nest deeper than 9 levels`);
|
|
51977
|
+
if (typeof block.ordered !== "boolean") invalid(`${path}.ordered`, "must be true or false");
|
|
51978
|
+
let start;
|
|
51979
|
+
if (!isOmitted(block.start)) {
|
|
51980
|
+
if (!block.ordered) invalid(`${path}.start`, "only an ordered list has a start");
|
|
51981
|
+
if (!Number.isSafeInteger(block.start)) invalid(`${path}.start`, "must be a whole number");
|
|
51982
|
+
if (block.start < 1) invalid(`${path}.start`, "the start must be at least 1");
|
|
51983
|
+
if (block.start > 2147483647) invalid(`${path}.start`, `the start must be at most ${maximumListStart}`);
|
|
51984
|
+
if (block.start !== 1) start = block.start;
|
|
51985
|
+
}
|
|
51986
|
+
const entries = list$1(block.items, `${path}.items`);
|
|
51987
|
+
if (!entries.length) invalid(`${path}.items`, "a list needs at least one item");
|
|
51988
|
+
const items = entries.map((entry, itemIndex) => {
|
|
51989
|
+
const at = `${path}.items[${itemIndex}]`;
|
|
51990
|
+
const blocks = list$1(record(entry, at, ["blocks"]).blocks, `${at}.blocks`);
|
|
51991
|
+
const first = blocks[0];
|
|
51992
|
+
if (!first || typeof first !== "object" || first.type !== "paragraph") invalid(`${at}.blocks[0]`, "a list item must start with a paragraph");
|
|
51993
|
+
return { blocks: blocks.map((child, blockIndex) => {
|
|
51994
|
+
const childPath = `${at}.blocks[${blockIndex}]`;
|
|
51995
|
+
if (blockIndex > 0 && (!child || typeof child !== "object" || child.type !== "list")) invalid(childPath, "after its paragraph, a list item can hold only lists");
|
|
51996
|
+
return readBlock(child, childPath, depth);
|
|
51997
|
+
}) };
|
|
51998
|
+
});
|
|
51999
|
+
return start === void 0 ? {
|
|
52000
|
+
type: "list",
|
|
52001
|
+
ordered: block.ordered,
|
|
52002
|
+
items
|
|
52003
|
+
} : {
|
|
52004
|
+
type: "list",
|
|
52005
|
+
ordered: block.ordered,
|
|
52006
|
+
start,
|
|
52007
|
+
items
|
|
52008
|
+
};
|
|
52009
|
+
}
|
|
52010
|
+
/**
|
|
52011
|
+
* Checks `value` as a version 1 document and returns its canonical form: marks sorted, a link's
|
|
52012
|
+
* underline removed, adjacent equal runs merged, runs in NFC, and `start` of 1 left out. Throws
|
|
52013
|
+
* `invalid-argument` naming the first problem, under `path`.
|
|
52014
|
+
*/
|
|
52015
|
+
function readRichTextDocument(value, path) {
|
|
52016
|
+
const document = record(value, path, ["version", "blocks"]);
|
|
52017
|
+
if (document.version !== 1) invalid(`${path}.version`, `version ${JSON.stringify(document.version)} is not supported; the version must be 1`);
|
|
52018
|
+
return {
|
|
52019
|
+
version: 1,
|
|
52020
|
+
blocks: list$1(document.blocks, `${path}.blocks`).map((block, index) => readBlock(block, `${path}.blocks[${index}]`, 0))
|
|
52021
|
+
};
|
|
52022
|
+
}
|
|
52023
|
+
/** The text that stands for a canonical document inside the editor: its JSON. Two documents are
|
|
52024
|
+
* the same exactly when their texts are. */
|
|
52025
|
+
function richTextText(document) {
|
|
52026
|
+
return JSON.stringify(document);
|
|
52027
|
+
}
|
|
52028
|
+
/** The document a text from `richTextText` stands for, as a new object. */
|
|
52029
|
+
function richTextFromText(text) {
|
|
52030
|
+
return JSON.parse(text);
|
|
52031
|
+
}
|
|
52032
|
+
/** Text from outside a document, such as a paste or another format, as canonical runs: a line or
|
|
52033
|
+
* paragraph separator inside a paragraph becomes a line break, and characters RTF cannot hold as
|
|
52034
|
+
* text become U+FFFD, so that no character disappears. */
|
|
52035
|
+
function appendText(builder, text, marks) {
|
|
52036
|
+
text.replace(/[\u0000\ufffc]/g, "�").replace(/[\ud800-\udbff](?![\udc00-\udfff])|(?<![\ud800-\udbff])[\udc00-\udfff]/g, "�").split(/\r\n|[\n\r\u000b\u000c\u0085\u2028\u2029]/).forEach((line, index) => {
|
|
52037
|
+
if (index) builder.break();
|
|
52038
|
+
builder.text(line, marks);
|
|
52039
|
+
});
|
|
52040
|
+
}
|
|
52041
|
+
/** Typed or pasted text as paragraphs: each paragraph separator ends one, including a final one,
|
|
52042
|
+
* so inserting `'a\n'` leaves the caret in a new paragraph. A line separator is a line break.
|
|
52043
|
+
* Unlike the host's `RichTextDocument(plainText:)`, which reads a file, nothing is trimmed. */
|
|
52044
|
+
function textBlocks(text) {
|
|
52045
|
+
if (text === "") return [];
|
|
52046
|
+
return text.split(/\r\n|[\n\r\u000c\u0085\u2029]/).map((line) => {
|
|
52047
|
+
const builder = new InlineBuilder();
|
|
52048
|
+
appendText(builder, line, []);
|
|
52049
|
+
return {
|
|
52050
|
+
type: "paragraph",
|
|
52051
|
+
content: builder.finish()
|
|
52052
|
+
};
|
|
52053
|
+
});
|
|
52054
|
+
}
|
|
52055
|
+
//#endregion
|
|
52056
|
+
//#region src/formats/rich-text-prose.ts
|
|
52057
|
+
/** Editor marks by rich text mark, in canonical order. */
|
|
52058
|
+
var markNames = {
|
|
52059
|
+
bold: "strong",
|
|
52060
|
+
italic: "emphasis",
|
|
52061
|
+
underline: "underline",
|
|
52062
|
+
strikethrough: "strike_through",
|
|
52063
|
+
superscript: "superscript",
|
|
52064
|
+
subscript: "subscript",
|
|
52065
|
+
code: "inlineCode",
|
|
52066
|
+
link: "link"
|
|
52067
|
+
};
|
|
52068
|
+
var richMarks$1 = Object.fromEntries(Object.entries(markNames).map(([rich, name]) => [name, rich]));
|
|
52069
|
+
/** The editor nodes an RTF document can hold. */
|
|
52070
|
+
var allowedNodes = /* @__PURE__ */ new Set([
|
|
52071
|
+
"doc",
|
|
52072
|
+
"paragraph",
|
|
52073
|
+
"text",
|
|
52074
|
+
"hardbreak",
|
|
52075
|
+
"bullet_list",
|
|
52076
|
+
"ordered_list",
|
|
52077
|
+
"list_item",
|
|
52078
|
+
"table",
|
|
52079
|
+
"table_header_row",
|
|
52080
|
+
"table_row",
|
|
52081
|
+
"table_header",
|
|
52082
|
+
"table_cell"
|
|
52083
|
+
]);
|
|
52084
|
+
function unrepresentable(what) {
|
|
52085
|
+
return new InkKitError("preservation", `The document holds ${what}, which an RTF document cannot hold`);
|
|
52086
|
+
}
|
|
52087
|
+
function proseMarks(schema, marks = []) {
|
|
52088
|
+
return marks.map((mark) => mark.type === "link" ? schema.marks.link.create({ href: mark.href }) : schema.marks[markNames[mark.type]].create());
|
|
52089
|
+
}
|
|
52090
|
+
function proseInlines(schema, content, around) {
|
|
52091
|
+
return content.map((inline) => {
|
|
52092
|
+
if (inline.type === "break") return schema.nodes.hardbreak.create({
|
|
52093
|
+
isInline: false,
|
|
52094
|
+
isHTML: false
|
|
52095
|
+
});
|
|
52096
|
+
let marks = [...around];
|
|
52097
|
+
for (const mark of proseMarks(schema, inline.marks)) marks = mark.addToSet(marks);
|
|
52098
|
+
return schema.text(inline.text, marks);
|
|
52099
|
+
});
|
|
52100
|
+
}
|
|
52101
|
+
function proseParagraph(schema, content, around, align = null) {
|
|
52102
|
+
return schema.nodes.paragraph.create({ align }, proseInlines(schema, content, around));
|
|
52103
|
+
}
|
|
52104
|
+
/** The editor block for a rich text block. Its text also takes `around`, the marks of text that a
|
|
52105
|
+
* paste replaces. */
|
|
52106
|
+
function proseBlock(schema, block, around = []) {
|
|
52107
|
+
switch (block.type) {
|
|
52108
|
+
case "paragraph": return proseParagraph(schema, block.content, around, block.align ?? null);
|
|
52109
|
+
case "list": {
|
|
52110
|
+
const start = block.start ?? 1;
|
|
52111
|
+
const items = block.items.map((item, index) => schema.nodes.list_item.create({
|
|
52112
|
+
label: block.ordered ? `${start + index}.` : "•",
|
|
52113
|
+
listType: block.ordered ? "ordered" : "bullet",
|
|
52114
|
+
spread: false,
|
|
52115
|
+
checked: null
|
|
52116
|
+
}, item.blocks.map((child) => proseBlock(schema, child, around))));
|
|
52117
|
+
return block.ordered ? schema.nodes.ordered_list.create({
|
|
52118
|
+
order: start,
|
|
52119
|
+
spread: false
|
|
52120
|
+
}, items) : schema.nodes.bullet_list.create({ spread: false }, items);
|
|
52121
|
+
}
|
|
52122
|
+
case "table": {
|
|
52123
|
+
const row = (cells, index) => {
|
|
52124
|
+
const header = index === 0;
|
|
52125
|
+
return schema.nodes[header ? "table_header_row" : "table_row"].create(null, cells.rows[index].map((cell) => schema.nodes[header ? "table_header" : "table_cell"].create({ alignment: null }, proseParagraph(schema, cell.content, around))));
|
|
52126
|
+
};
|
|
52127
|
+
return schema.nodes.table.create(null, block.rows.map((_, index) => row(block, index)));
|
|
52128
|
+
}
|
|
52129
|
+
}
|
|
52130
|
+
}
|
|
52131
|
+
/** The editor document for a canonical rich text document. */
|
|
52132
|
+
function richTextToProse(schema, document) {
|
|
52133
|
+
const blocks = document.blocks.map((block) => proseBlock(schema, block));
|
|
52134
|
+
if (!blocks.length) blocks.push(proseParagraph(schema, [], []));
|
|
52135
|
+
return schema.topNodeType.create(null, blocks);
|
|
52136
|
+
}
|
|
52137
|
+
function richMarksOf(marks) {
|
|
52138
|
+
const result = [];
|
|
52139
|
+
for (const mark of marks) {
|
|
52140
|
+
const type = richMarks$1[mark.type.name];
|
|
52141
|
+
if (!type) throw unrepresentable(`the ${mark.type.name} mark`);
|
|
52142
|
+
result.push(type === "link" ? {
|
|
52143
|
+
type: "link",
|
|
52144
|
+
href: String(mark.attrs.href ?? "")
|
|
52145
|
+
} : { type });
|
|
52146
|
+
}
|
|
52147
|
+
return canonicalMarks(result);
|
|
52148
|
+
}
|
|
52149
|
+
function richInlines(node) {
|
|
52150
|
+
const builder = new InlineBuilder();
|
|
52151
|
+
node.forEach((child) => {
|
|
52152
|
+
if (child.type.name === "hardbreak") builder.break();
|
|
52153
|
+
else if (child.isText) {
|
|
52154
|
+
const text = child.text;
|
|
52155
|
+
if (textProblem(text)) throw unrepresentable("a separator in its text");
|
|
52156
|
+
const marks = richMarksOf(child.marks);
|
|
52157
|
+
if (marks.some((mark) => mark.type === "link" && linkProblem(mark.href))) throw unrepresentable("a link without an address");
|
|
52158
|
+
builder.text(text, marks);
|
|
52159
|
+
} else throw unrepresentable(`a ${child.type.name}`);
|
|
52160
|
+
});
|
|
52161
|
+
return builder.finish();
|
|
52162
|
+
}
|
|
52163
|
+
function richParagraph(node, aligned) {
|
|
52164
|
+
if (node.type.name !== "paragraph") throw unrepresentable(`a ${node.type.name}`);
|
|
52165
|
+
const align = node.attrs.align;
|
|
52166
|
+
if (align != null && !aligned) throw unrepresentable("an aligned table cell");
|
|
52167
|
+
if (align != null && align !== "center" && align !== "right" && align !== "justify") throw unrepresentable(`${align} alignment`);
|
|
52168
|
+
const content = richInlines(node);
|
|
52169
|
+
return align == null ? {
|
|
52170
|
+
type: "paragraph",
|
|
52171
|
+
content
|
|
52172
|
+
} : {
|
|
52173
|
+
type: "paragraph",
|
|
52174
|
+
align,
|
|
52175
|
+
content
|
|
52176
|
+
};
|
|
52177
|
+
}
|
|
52178
|
+
function richBlock(node, listDepth) {
|
|
52179
|
+
switch (node.type.name) {
|
|
52180
|
+
case "paragraph": return richParagraph(node, true);
|
|
52181
|
+
case "bullet_list":
|
|
52182
|
+
case "ordered_list": {
|
|
52183
|
+
const depth = listDepth + 1;
|
|
52184
|
+
if (depth > 9) throw unrepresentable(`lists deeper than 9 levels`);
|
|
52185
|
+
const ordered = node.type.name === "ordered_list";
|
|
52186
|
+
const items = [];
|
|
52187
|
+
node.forEach((item) => {
|
|
52188
|
+
if (item.type.name !== "list_item") throw unrepresentable(`a ${item.type.name}`);
|
|
52189
|
+
if (item.attrs.checked != null) throw unrepresentable("a task");
|
|
52190
|
+
const blocks = [];
|
|
52191
|
+
item.forEach((child, _offset, index) => {
|
|
52192
|
+
if (index === 0) blocks.push(richParagraph(child, true));
|
|
52193
|
+
else if (/^(bullet|ordered)_list$/.test(child.type.name)) blocks.push(richBlock(child, depth));
|
|
52194
|
+
else throw unrepresentable(`a ${child.type.name} in a list item`);
|
|
52195
|
+
});
|
|
52196
|
+
items.push({ blocks });
|
|
52197
|
+
});
|
|
52198
|
+
const start = Number(node.attrs.order ?? 1);
|
|
52199
|
+
if (ordered && (!Number.isSafeInteger(start) || start < 1 || start > 2147483647)) throw unrepresentable(`a list that starts below 1 or above ${maximumListStart}`);
|
|
52200
|
+
return ordered && start !== 1 ? {
|
|
52201
|
+
type: "list",
|
|
52202
|
+
ordered,
|
|
52203
|
+
start,
|
|
52204
|
+
items
|
|
52205
|
+
} : {
|
|
52206
|
+
type: "list",
|
|
52207
|
+
ordered,
|
|
52208
|
+
items
|
|
52209
|
+
};
|
|
52210
|
+
}
|
|
52211
|
+
case "table": {
|
|
52212
|
+
if (listDepth > 0) throw unrepresentable("a table in a list");
|
|
52213
|
+
const rows = [];
|
|
52214
|
+
node.forEach((row) => {
|
|
52215
|
+
const cells = [];
|
|
52216
|
+
row.forEach((cell) => {
|
|
52217
|
+
if (cell.attrs.alignment != null || Number(cell.attrs.colspan ?? 1) !== 1 || Number(cell.attrs.rowspan ?? 1) !== 1) throw unrepresentable("an aligned or merged table cell");
|
|
52218
|
+
if (cell.childCount !== 1) throw unrepresentable("a cell of several blocks");
|
|
52219
|
+
const paragraph = richParagraph(cell.firstChild, false);
|
|
52220
|
+
cells.push({ content: paragraph.content });
|
|
52221
|
+
});
|
|
52222
|
+
rows.push(cells);
|
|
52223
|
+
});
|
|
52224
|
+
if (!rows.length || !rows[0].length || rows.some((row) => row.length !== rows[0].length)) throw unrepresentable("a table with uneven rows");
|
|
52225
|
+
return {
|
|
52226
|
+
type: "table",
|
|
52227
|
+
rows
|
|
52228
|
+
};
|
|
52229
|
+
}
|
|
52230
|
+
default: throw unrepresentable(`a ${node.type.name}`);
|
|
52231
|
+
}
|
|
52232
|
+
}
|
|
52233
|
+
/** The canonical rich text document for an editor document. Throws `preservation` when the
|
|
52234
|
+
* document holds something RTF cannot. The editor always holds a block, so its one empty
|
|
52235
|
+
* paragraph is an empty document when `emptyAsNone`, as it is when the document loaded empty. */
|
|
52236
|
+
function proseToRichText(doc, emptyAsNone = true) {
|
|
52237
|
+
const blocks = [];
|
|
52238
|
+
doc.forEach((node) => blocks.push(richBlock(node, 0)));
|
|
52239
|
+
if (emptyAsNone && blocks.length === 1 && blocks[0].type === "paragraph" && !blocks[0].align && !blocks[0].content.length) blocks.length = 0;
|
|
52240
|
+
return {
|
|
52241
|
+
version: 1,
|
|
52242
|
+
blocks
|
|
52243
|
+
};
|
|
52244
|
+
}
|
|
52245
|
+
/** Why `node` cannot be in an RTF document at `$depth` lists deep, or undefined. Checks the node
|
|
52246
|
+
* itself and its direct children, not its descendants. */
|
|
52247
|
+
function refusal(node, listDepth, inCell) {
|
|
52248
|
+
const name = node.type.name;
|
|
52249
|
+
if (!allowedNodes.has(name)) return name;
|
|
52250
|
+
for (const mark of node.marks) if (!richMarks$1[mark.type.name]) return mark.type.name;
|
|
52251
|
+
if (node.isText && textProblem(node.text)) return "text";
|
|
52252
|
+
const href = node.type.schema.marks.link?.isInSet(node.marks)?.attrs.href;
|
|
52253
|
+
if (href !== void 0 && linkProblem(String(href ?? ""))) return "link";
|
|
52254
|
+
if (name === "paragraph" && node.attrs.align != null && inCell) return "aligned cell";
|
|
52255
|
+
if (name === "list_item") {
|
|
52256
|
+
if (node.attrs.checked != null) return "task";
|
|
52257
|
+
for (let index = 1; index < node.childCount; index++) if (!/^(bullet|ordered)_list$/.test(node.child(index).type.name)) return "list item block";
|
|
52258
|
+
}
|
|
52259
|
+
if ((name === "bullet_list" || name === "ordered_list") && listDepth > 9) return "list depth";
|
|
52260
|
+
const order = Number(node.attrs.order);
|
|
52261
|
+
if (name === "ordered_list" && !(order >= 1 && order <= 2147483647)) return "list start";
|
|
52262
|
+
if (name === "table" && listDepth > 0) return "table in list";
|
|
52263
|
+
if ((name === "table_cell" || name === "table_header") && (node.attrs.alignment != null || Number(node.attrs.colspan ?? 1) !== 1 || Number(node.attrs.rowspan ?? 1) !== 1)) return "cell";
|
|
52264
|
+
}
|
|
52265
|
+
/** Whether the document a transaction reaches holds only what RTF can, read in the part it
|
|
52266
|
+
* changed and that part's ancestors. */
|
|
52267
|
+
function richTextAllows(tr, before) {
|
|
52268
|
+
if (!tr.docChanged) return true;
|
|
52269
|
+
const doc = tr.doc;
|
|
52270
|
+
const start = before.doc.content.findDiffStart(doc.content);
|
|
52271
|
+
if (start == null) return true;
|
|
52272
|
+
const end = before.doc.content.findDiffEnd(doc.content);
|
|
52273
|
+
const to = Math.max(start, end?.b ?? doc.content.size);
|
|
52274
|
+
let allowed = true;
|
|
52275
|
+
const $start = doc.resolve(start);
|
|
52276
|
+
let lists = 0;
|
|
52277
|
+
for (let depth = 1; depth <= $start.depth; depth++) {
|
|
52278
|
+
const node = $start.node(depth);
|
|
52279
|
+
if (/^(bullet|ordered)_list$/.test(node.type.name)) lists++;
|
|
52280
|
+
if (refusal(node, lists, false)) return false;
|
|
52281
|
+
}
|
|
52282
|
+
doc.nodesBetween(start, Math.min(to, doc.content.size), (node, pos) => {
|
|
52283
|
+
if (!allowed) return false;
|
|
52284
|
+
const $pos = doc.resolve(pos);
|
|
52285
|
+
let depth = /^(bullet|ordered)_list$/.test(node.type.name) ? 1 : 0;
|
|
52286
|
+
let inCell = false;
|
|
52287
|
+
for (let level = $pos.depth; level > 0; level--) {
|
|
52288
|
+
const name = $pos.node(level).type.name;
|
|
52289
|
+
if (/^(bullet|ordered)_list$/.test(name)) depth++;
|
|
52290
|
+
if (name === "table_cell" || name === "table_header") inCell = true;
|
|
52291
|
+
}
|
|
52292
|
+
if (refusal(node, depth, inCell)) allowed = false;
|
|
52293
|
+
return allowed;
|
|
52294
|
+
});
|
|
52295
|
+
return allowed;
|
|
52296
|
+
}
|
|
52297
|
+
/** The ranges of runs that have both a link and underline, which canonical form removes the
|
|
52298
|
+
* underline from, in the part of `doc` from `from` to `to`. */
|
|
52299
|
+
function linkedUnderlines(doc, from, to) {
|
|
52300
|
+
const ranges = [];
|
|
52301
|
+
const { link, underline } = doc.type.schema.marks;
|
|
52302
|
+
if (!link || !underline) return ranges;
|
|
52303
|
+
doc.nodesBetween(from, to, (node, pos) => {
|
|
52304
|
+
if (node.isText && link.isInSet(node.marks) && underline.isInSet(node.marks)) ranges.push({
|
|
52305
|
+
from: pos,
|
|
52306
|
+
to: pos + node.nodeSize
|
|
52307
|
+
});
|
|
52308
|
+
});
|
|
52309
|
+
return ranges;
|
|
52310
|
+
}
|
|
52311
|
+
/** Whether every character from `from` to `to`, or the caret's marks, is in a link. */
|
|
52312
|
+
function inLink(state) {
|
|
52313
|
+
const { link } = state.schema.marks;
|
|
52314
|
+
if (!link) return false;
|
|
52315
|
+
const { from, to, empty, $from } = state.selection;
|
|
52316
|
+
if (empty) return Boolean(link.isInSet(state.storedMarks ?? $from.marks()));
|
|
52317
|
+
let text = false;
|
|
52318
|
+
let all = true;
|
|
52319
|
+
state.doc.nodesBetween(from, to, (node) => {
|
|
52320
|
+
if (!node.isText) return true;
|
|
52321
|
+
text = true;
|
|
52322
|
+
if (!link.isInSet(node.marks)) all = false;
|
|
52323
|
+
return all;
|
|
52324
|
+
});
|
|
52325
|
+
return text && all;
|
|
52326
|
+
}
|
|
52327
|
+
//#endregion
|
|
52328
|
+
//#region src/formats/rich-text-import.ts
|
|
52329
|
+
var skipped = /* @__PURE__ */ new Set([
|
|
52330
|
+
"SCRIPT",
|
|
52331
|
+
"STYLE",
|
|
52332
|
+
"TEMPLATE",
|
|
52333
|
+
"NOSCRIPT",
|
|
52334
|
+
"IFRAME",
|
|
52335
|
+
"OBJECT",
|
|
52336
|
+
"EMBED",
|
|
52337
|
+
"HEAD",
|
|
52338
|
+
"TITLE",
|
|
52339
|
+
"META",
|
|
52340
|
+
"LINK",
|
|
52341
|
+
"SVG",
|
|
52342
|
+
"MATH",
|
|
52343
|
+
"IMG",
|
|
52344
|
+
"PICTURE",
|
|
52345
|
+
"VIDEO",
|
|
52346
|
+
"AUDIO",
|
|
52347
|
+
"CANVAS",
|
|
52348
|
+
"BUTTON",
|
|
52349
|
+
"INPUT",
|
|
52350
|
+
"SELECT",
|
|
52351
|
+
"TEXTAREA",
|
|
52352
|
+
"HR"
|
|
52353
|
+
]);
|
|
52354
|
+
var blockTags = /* @__PURE__ */ new Set([
|
|
52355
|
+
"ADDRESS",
|
|
52356
|
+
"ARTICLE",
|
|
52357
|
+
"ASIDE",
|
|
52358
|
+
"BLOCKQUOTE",
|
|
52359
|
+
"BODY",
|
|
52360
|
+
"CENTER",
|
|
52361
|
+
"DD",
|
|
52362
|
+
"DETAILS",
|
|
52363
|
+
"DIV",
|
|
52364
|
+
"DL",
|
|
52365
|
+
"DT",
|
|
52366
|
+
"FIELDSET",
|
|
52367
|
+
"FIGCAPTION",
|
|
52368
|
+
"FIGURE",
|
|
52369
|
+
"FOOTER",
|
|
52370
|
+
"FORM",
|
|
52371
|
+
"H1",
|
|
52372
|
+
"H2",
|
|
52373
|
+
"H3",
|
|
52374
|
+
"H4",
|
|
52375
|
+
"H5",
|
|
52376
|
+
"H6",
|
|
52377
|
+
"HEADER",
|
|
52378
|
+
"HTML",
|
|
52379
|
+
"LI",
|
|
52380
|
+
"MAIN",
|
|
52381
|
+
"NAV",
|
|
52382
|
+
"OL",
|
|
52383
|
+
"P",
|
|
52384
|
+
"PRE",
|
|
52385
|
+
"SECTION",
|
|
52386
|
+
"SUMMARY",
|
|
52387
|
+
"TABLE",
|
|
52388
|
+
"UL"
|
|
52389
|
+
]);
|
|
52390
|
+
function withMark(marks, mark) {
|
|
52391
|
+
if (marks.some((held) => held.type === mark.type)) return marks;
|
|
52392
|
+
const other = mark.type === "superscript" ? "subscript" : mark.type === "subscript" ? "superscript" : "";
|
|
52393
|
+
return canonicalMarks([...marks.filter((held) => held.type !== other), mark]);
|
|
52394
|
+
}
|
|
52395
|
+
function withoutMark(marks, type) {
|
|
52396
|
+
return marks.filter((mark) => mark.type !== type);
|
|
52397
|
+
}
|
|
52398
|
+
/** The marks an element adds to its text, from its tag and its inline style. */
|
|
52399
|
+
function elementMarks(element, marks, schemes) {
|
|
52400
|
+
const tag = element.tagName;
|
|
52401
|
+
if (tag === "B" || tag === "STRONG") marks = withMark(marks, { type: "bold" });
|
|
52402
|
+
if (tag === "I" || tag === "EM" || tag === "CITE" || tag === "DFN" || tag === "VAR") marks = withMark(marks, { type: "italic" });
|
|
52403
|
+
if (tag === "U" || tag === "INS") marks = withMark(marks, { type: "underline" });
|
|
52404
|
+
if (tag === "S" || tag === "DEL" || tag === "STRIKE") marks = withMark(marks, { type: "strikethrough" });
|
|
52405
|
+
if (tag === "SUP") marks = withMark(marks, { type: "superscript" });
|
|
52406
|
+
if (tag === "SUB") marks = withMark(marks, { type: "subscript" });
|
|
52407
|
+
if (tag === "CODE" || tag === "KBD" || tag === "SAMP" || tag === "TT") marks = withMark(marks, { type: "code" });
|
|
52408
|
+
if (tag === "A") {
|
|
52409
|
+
const href = element.getAttribute("href")?.trim();
|
|
52410
|
+
if (href && isSafeLinkTarget(href, schemes) && !linkProblem(href)) marks = withMark(withoutMark(marks, "link"), {
|
|
52411
|
+
type: "link",
|
|
52412
|
+
href
|
|
52413
|
+
});
|
|
52414
|
+
}
|
|
52415
|
+
const style = element instanceof HTMLElement ? element.style : void 0;
|
|
52416
|
+
if (style) {
|
|
52417
|
+
const weight = style.fontWeight;
|
|
52418
|
+
if (weight === "bold" || weight === "bolder" || Number(weight) >= 600) marks = withMark(marks, { type: "bold" });
|
|
52419
|
+
else if (weight === "normal" || weight && Number(weight) < 600) marks = withoutMark(marks, "bold");
|
|
52420
|
+
if (style.fontStyle === "italic" || style.fontStyle === "oblique") marks = withMark(marks, { type: "italic" });
|
|
52421
|
+
else if (style.fontStyle === "normal") marks = withoutMark(marks, "italic");
|
|
52422
|
+
const decoration = `${style.textDecoration} ${style.textDecorationLine}`;
|
|
52423
|
+
if (/underline/.test(decoration)) marks = withMark(marks, { type: "underline" });
|
|
52424
|
+
if (/line-through/.test(decoration)) marks = withMark(marks, { type: "strikethrough" });
|
|
52425
|
+
if (style.verticalAlign === "super") marks = withMark(marks, { type: "superscript" });
|
|
52426
|
+
if (style.verticalAlign === "sub") marks = withMark(marks, { type: "subscript" });
|
|
52427
|
+
}
|
|
52428
|
+
return canonicalMarks(marks);
|
|
52429
|
+
}
|
|
52430
|
+
function alignment(element) {
|
|
52431
|
+
const value = (element instanceof HTMLElement ? element.style.textAlign : "") || element.getAttribute("align") || (element.tagName === "CENTER" ? "center" : "");
|
|
52432
|
+
return value === "center" || value === "right" || value === "justify" ? value : void 0;
|
|
52433
|
+
}
|
|
52434
|
+
/** Reads blocks from a DOM, the way a person reads the page. */
|
|
52435
|
+
var Reader = class Reader {
|
|
52436
|
+
schemes;
|
|
52437
|
+
depth;
|
|
52438
|
+
blocks = [];
|
|
52439
|
+
inline;
|
|
52440
|
+
align;
|
|
52441
|
+
space;
|
|
52442
|
+
constructor(schemes, depth) {
|
|
52443
|
+
this.schemes = schemes;
|
|
52444
|
+
this.depth = depth;
|
|
52445
|
+
}
|
|
52446
|
+
/** Ends the open paragraph, if any. */
|
|
52447
|
+
flush() {
|
|
52448
|
+
if (!this.inline) return;
|
|
52449
|
+
const content = this.inline.finish();
|
|
52450
|
+
if (content.at(-1)?.type === "break") content.pop();
|
|
52451
|
+
this.blocks.push(this.align ? {
|
|
52452
|
+
type: "paragraph",
|
|
52453
|
+
align: this.align,
|
|
52454
|
+
content
|
|
52455
|
+
} : {
|
|
52456
|
+
type: "paragraph",
|
|
52457
|
+
content
|
|
52458
|
+
});
|
|
52459
|
+
this.inline = void 0;
|
|
52460
|
+
this.align = void 0;
|
|
52461
|
+
this.space = void 0;
|
|
52462
|
+
}
|
|
52463
|
+
open() {
|
|
52464
|
+
this.inline ??= new InlineBuilder();
|
|
52465
|
+
return this.inline;
|
|
52466
|
+
}
|
|
52467
|
+
text(value, marks, pre) {
|
|
52468
|
+
if (pre) {
|
|
52469
|
+
value.split(/\r\n|[\n\r]/).forEach((line, index) => {
|
|
52470
|
+
if (index) this.flush();
|
|
52471
|
+
appendText(this.open(), line, marks);
|
|
52472
|
+
});
|
|
52473
|
+
return;
|
|
52474
|
+
}
|
|
52475
|
+
const collapsed = value.replace(/[ \t\n\r\f]+/g, " ");
|
|
52476
|
+
if (!collapsed) return;
|
|
52477
|
+
if (collapsed === " ") {
|
|
52478
|
+
if (this.inline?.content.length) this.space ??= marks;
|
|
52479
|
+
return;
|
|
52480
|
+
}
|
|
52481
|
+
const builder = this.open();
|
|
52482
|
+
const empty = !builder.content.length;
|
|
52483
|
+
let text = collapsed;
|
|
52484
|
+
if (text.startsWith(" ")) {
|
|
52485
|
+
if (!empty) this.space ??= marks;
|
|
52486
|
+
text = text.slice(1);
|
|
52487
|
+
}
|
|
52488
|
+
if (!text) return;
|
|
52489
|
+
if (this.space) {
|
|
52490
|
+
builder.text(" ", this.space);
|
|
52491
|
+
this.space = void 0;
|
|
52492
|
+
}
|
|
52493
|
+
if (text.endsWith(" ")) {
|
|
52494
|
+
this.space = marks;
|
|
52495
|
+
text = text.slice(0, -1);
|
|
52496
|
+
}
|
|
52497
|
+
appendText(builder, text, marks);
|
|
52498
|
+
}
|
|
52499
|
+
lineBreak() {
|
|
52500
|
+
this.space = void 0;
|
|
52501
|
+
this.open().break();
|
|
52502
|
+
}
|
|
52503
|
+
/** Starts a paragraph that takes `element`'s alignment. */
|
|
52504
|
+
paragraph(element) {
|
|
52505
|
+
this.flush();
|
|
52506
|
+
this.align = alignment(element);
|
|
52507
|
+
}
|
|
52508
|
+
push(block) {
|
|
52509
|
+
this.flush();
|
|
52510
|
+
this.blocks.push(block);
|
|
52511
|
+
}
|
|
52512
|
+
read(node, marks, pre) {
|
|
52513
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
52514
|
+
this.text(node.textContent ?? "", marks, pre);
|
|
52515
|
+
return;
|
|
52516
|
+
}
|
|
52517
|
+
if (node.nodeType !== Node.ELEMENT_NODE) return;
|
|
52518
|
+
const element = node;
|
|
52519
|
+
const tag = element.tagName.toUpperCase();
|
|
52520
|
+
if (skipped.has(tag) || element instanceof HTMLElement && element.hidden) return;
|
|
52521
|
+
if (element instanceof HTMLElement && element.style.display === "none") return;
|
|
52522
|
+
if (tag === "BR") {
|
|
52523
|
+
this.lineBreak();
|
|
52524
|
+
return;
|
|
52525
|
+
}
|
|
52526
|
+
if (tag === "UL" || tag === "OL") {
|
|
52527
|
+
this.push(this.list(element, marks));
|
|
52528
|
+
return;
|
|
52529
|
+
}
|
|
52530
|
+
if (tag === "TABLE") {
|
|
52531
|
+
const table = this.depth === 0 && !spansRows(element);
|
|
52532
|
+
const rows = tableRows(element, marks, this.schemes, table);
|
|
52533
|
+
if (!rows.length) return;
|
|
52534
|
+
if (table) this.push({
|
|
52535
|
+
type: "table",
|
|
52536
|
+
rows
|
|
52537
|
+
});
|
|
52538
|
+
else for (const row of rows) {
|
|
52539
|
+
this.paragraph(element);
|
|
52540
|
+
this.open();
|
|
52541
|
+
row.forEach((cell, index) => {
|
|
52542
|
+
if (index) this.open().text(" ", []);
|
|
52543
|
+
for (const inline of cell.content) if (inline.type === "break") this.open().break();
|
|
52544
|
+
else this.open().text(inline.text, inline.marks ?? []);
|
|
52545
|
+
});
|
|
52546
|
+
this.flush();
|
|
52547
|
+
}
|
|
52548
|
+
return;
|
|
52549
|
+
}
|
|
52550
|
+
let inner = elementMarks(element, marks, this.schemes);
|
|
52551
|
+
if (/^H[1-6]$/.test(tag)) inner = withMark(inner, { type: "bold" });
|
|
52552
|
+
const block = blockTags.has(tag);
|
|
52553
|
+
if (block) this.paragraph(element);
|
|
52554
|
+
if (tag === "P" || /^H[1-6]$/.test(tag)) this.open();
|
|
52555
|
+
const preformatted = pre || tag === "PRE" || element instanceof HTMLElement && /^pre/.test(element.style.whiteSpace);
|
|
52556
|
+
if (tag === "PRE") inner = withMark(inner, { type: "code" });
|
|
52557
|
+
for (const child of element.childNodes) this.read(child, inner, preformatted);
|
|
52558
|
+
if (block) this.flush();
|
|
52559
|
+
}
|
|
52560
|
+
list(element, marks) {
|
|
52561
|
+
const ordered = element.tagName.toUpperCase() === "OL";
|
|
52562
|
+
const start = Number(element.getAttribute("start") ?? 1);
|
|
52563
|
+
const items = [];
|
|
52564
|
+
const inner = elementMarks(element, marks, this.schemes);
|
|
52565
|
+
for (const child of element.childNodes) {
|
|
52566
|
+
if (child.nodeType === Node.ELEMENT_NODE && child.tagName.toUpperCase() === "LI") {
|
|
52567
|
+
items.push({ blocks: this.item(child, inner) });
|
|
52568
|
+
continue;
|
|
52569
|
+
}
|
|
52570
|
+
const reader = new Reader(this.schemes, this.depth + 1);
|
|
52571
|
+
reader.read(child, inner, false);
|
|
52572
|
+
reader.flush();
|
|
52573
|
+
if (!reader.blocks.length) continue;
|
|
52574
|
+
const blocks = itemBlocks(reader.blocks, this.depth + 1);
|
|
52575
|
+
if (items.length && blocks[0].type === "paragraph" && !blocks[0].content.length) items.at(-1).blocks.push(...blocks.slice(1));
|
|
52576
|
+
else items.push({ blocks });
|
|
52577
|
+
}
|
|
52578
|
+
if (!items.length) items.push({ blocks: [{
|
|
52579
|
+
type: "paragraph",
|
|
52580
|
+
content: []
|
|
52581
|
+
}] });
|
|
52582
|
+
return ordered && Number.isSafeInteger(start) && start > 1 ? {
|
|
52583
|
+
type: "list",
|
|
52584
|
+
ordered,
|
|
52585
|
+
start: Math.min(start, maximumListStart),
|
|
52586
|
+
items
|
|
52587
|
+
} : {
|
|
52588
|
+
type: "list",
|
|
52589
|
+
ordered,
|
|
52590
|
+
items
|
|
52591
|
+
};
|
|
52592
|
+
}
|
|
52593
|
+
item(element, marks) {
|
|
52594
|
+
const reader = new Reader(this.schemes, this.depth + 1);
|
|
52595
|
+
const inner = elementMarks(element, marks, this.schemes);
|
|
52596
|
+
for (const child of element.childNodes) reader.read(child, inner, false);
|
|
52597
|
+
reader.flush();
|
|
52598
|
+
return itemBlocks(reader.blocks, this.depth + 1);
|
|
52599
|
+
}
|
|
52600
|
+
};
|
|
52601
|
+
/** A list item's blocks as RTF holds them: one paragraph, then lists. Further paragraphs join the
|
|
52602
|
+
* first as lines. Lists keep every level here; the paste lifts those deeper than RTF holds. */
|
|
52603
|
+
function itemBlocks(blocks, depth) {
|
|
52604
|
+
const builder = new InlineBuilder();
|
|
52605
|
+
let align;
|
|
52606
|
+
let lines = 0;
|
|
52607
|
+
const nested = [];
|
|
52608
|
+
const line = (content) => {
|
|
52609
|
+
if (lines++) builder.break();
|
|
52610
|
+
for (const inline of content) if (inline.type === "break") builder.break();
|
|
52611
|
+
else builder.text(inline.text, inline.marks ?? []);
|
|
52612
|
+
};
|
|
52613
|
+
const flatten = (block) => {
|
|
52614
|
+
if (block.type === "paragraph") line(block.content);
|
|
52615
|
+
else if (block.type === "list") for (const item of block.items) for (const child of item.blocks) flatten(child);
|
|
52616
|
+
else for (const row of block.rows) for (const cell of row) line(cell.content);
|
|
52617
|
+
};
|
|
52618
|
+
for (const block of blocks) if (block.type === "list") nested.push(block);
|
|
52619
|
+
else if (block.type === "paragraph" && !nested.length) {
|
|
52620
|
+
if (!lines) align = block.align;
|
|
52621
|
+
line(block.content);
|
|
52622
|
+
} else flatten(block);
|
|
52623
|
+
const content = builder.finish();
|
|
52624
|
+
return [align ? {
|
|
52625
|
+
type: "paragraph",
|
|
52626
|
+
align,
|
|
52627
|
+
content
|
|
52628
|
+
} : {
|
|
52629
|
+
type: "paragraph",
|
|
52630
|
+
content
|
|
52631
|
+
}, ...nested];
|
|
52632
|
+
}
|
|
52633
|
+
/** Whether a cell of `table` itself, not of a table inside it, spans more than one row. */
|
|
52634
|
+
function spansRows(table) {
|
|
52635
|
+
return [...table.querySelectorAll("td[rowspan], th[rowspan]")].some((cell) => cell.closest("table") === table && Number(cell.getAttribute("rowspan")) > 1);
|
|
52636
|
+
}
|
|
52637
|
+
function tableRows(table, marks, schemes, even) {
|
|
52638
|
+
const rows = [];
|
|
52639
|
+
for (const row of table.querySelectorAll("tr")) {
|
|
52640
|
+
if (row.closest("table") !== table) continue;
|
|
52641
|
+
const cells = [];
|
|
52642
|
+
for (const cell of row.children) {
|
|
52643
|
+
if (cell.tagName !== "TD" && cell.tagName !== "TH") continue;
|
|
52644
|
+
const reader = new Reader(schemes, 1);
|
|
52645
|
+
const inner = elementMarks(cell, marks, schemes);
|
|
52646
|
+
for (const child of cell.childNodes) reader.read(child, inner, false);
|
|
52647
|
+
reader.flush();
|
|
52648
|
+
const builder = new InlineBuilder();
|
|
52649
|
+
let lines = 0;
|
|
52650
|
+
const add = (block) => {
|
|
52651
|
+
if (block.type === "paragraph") {
|
|
52652
|
+
if (lines++) builder.break();
|
|
52653
|
+
for (const inline of block.content) if (inline.type === "break") builder.break();
|
|
52654
|
+
else builder.text(inline.text, inline.marks ?? []);
|
|
52655
|
+
} else if (block.type === "list") for (const item of block.items) item.blocks.forEach(add);
|
|
52656
|
+
else for (const nestedRow of block.rows) for (const nestedCell of nestedRow) add({
|
|
52657
|
+
type: "paragraph",
|
|
52658
|
+
content: nestedCell.content
|
|
52659
|
+
});
|
|
52660
|
+
};
|
|
52661
|
+
reader.blocks.forEach(add);
|
|
52662
|
+
const content = builder.finish();
|
|
52663
|
+
const span = Math.max(1, Math.min(100, Number(cell.getAttribute("colspan") ?? 1) || 1));
|
|
52664
|
+
cells.push({ content });
|
|
52665
|
+
for (let index = 1; index < span; index++) cells.push({ content: [] });
|
|
52666
|
+
}
|
|
52667
|
+
if (cells.length) rows.push(cells);
|
|
52668
|
+
}
|
|
52669
|
+
const width = even ? Math.max(0, ...rows.map((row) => row.length)) : 0;
|
|
52670
|
+
for (const row of rows) while (row.length < width) row.push({ content: [] });
|
|
52671
|
+
return rows;
|
|
52672
|
+
}
|
|
52673
|
+
/** Blocks for pasted HTML. */
|
|
52674
|
+
function htmlBlocks(html, schemes) {
|
|
52675
|
+
const parsed = new DOMParser().parseFromString(html, "text/html");
|
|
52676
|
+
const reader = new Reader(schemes, 0);
|
|
52677
|
+
reader.read(parsed.body, [], false);
|
|
52678
|
+
reader.flush();
|
|
52679
|
+
return reader.blocks;
|
|
52680
|
+
}
|
|
52681
|
+
/** Every block's text as lines of one paragraph, for a place that holds only one, such as a
|
|
52682
|
+
* table cell. */
|
|
52683
|
+
function asLines(blocks) {
|
|
52684
|
+
const builder = new InlineBuilder();
|
|
52685
|
+
let lines = 0;
|
|
52686
|
+
const add = (content) => {
|
|
52687
|
+
if (lines++) builder.break();
|
|
52688
|
+
for (const inline of content) if (inline.type === "break") builder.break();
|
|
52689
|
+
else builder.text(inline.text, inline.marks ?? []);
|
|
52690
|
+
};
|
|
52691
|
+
const visit = (block) => {
|
|
52692
|
+
if (block.type === "paragraph") add(block.content);
|
|
52693
|
+
else if (block.type === "list") block.items.forEach((item) => item.blocks.forEach(visit));
|
|
52694
|
+
else block.rows.forEach((row) => add(row.flatMap((cell, index) => index ? [{
|
|
52695
|
+
type: "text",
|
|
52696
|
+
text: " "
|
|
52697
|
+
}, ...cell.content] : cell.content)));
|
|
52698
|
+
};
|
|
52699
|
+
blocks.forEach(visit);
|
|
52700
|
+
return [{
|
|
52701
|
+
type: "paragraph",
|
|
52702
|
+
content: builder.finish()
|
|
52703
|
+
}];
|
|
52704
|
+
}
|
|
52705
|
+
/** `list`, at `depth` lists deep, with every level beyond the deepest RTF holds lifted to the
|
|
52706
|
+
* deepest: the items of a list that would be too deep become items of its parent list. */
|
|
52707
|
+
function liftDeepLevels(list, depth) {
|
|
52708
|
+
const flat = (deep) => deep.items.flatMap((item) => [{ blocks: [item.blocks[0]] }, ...item.blocks.slice(1).flatMap((nested) => flat(nested))]);
|
|
52709
|
+
return {
|
|
52710
|
+
...list,
|
|
52711
|
+
items: list.items.flatMap((item) => {
|
|
52712
|
+
const [paragraph, ...nested] = item.blocks;
|
|
52713
|
+
const kept = [];
|
|
52714
|
+
const lifted = [];
|
|
52715
|
+
for (const child of nested) if (depth + 1 <= 9) kept.push(liftDeepLevels(child, depth + 1));
|
|
52716
|
+
else lifted.push(...flat(child));
|
|
52717
|
+
return [{ blocks: [paragraph, ...kept] }, ...lifted];
|
|
52718
|
+
})
|
|
52719
|
+
};
|
|
52720
|
+
}
|
|
52721
|
+
/**
|
|
52722
|
+
* A paste of several blocks into a list item, which holds one paragraph: each paragraph becomes
|
|
52723
|
+
* an item beside the one at the caret, at its level. The first joins the item at the caret, the
|
|
52724
|
+
* text after the caret moves to the last, and a pasted list nests in the item before it.
|
|
52725
|
+
*/
|
|
52726
|
+
function listItemsSlice(schema, blocks, list, depth, marks) {
|
|
52727
|
+
const empty = {
|
|
52728
|
+
type: "paragraph",
|
|
52729
|
+
content: []
|
|
52730
|
+
};
|
|
52731
|
+
const items = [];
|
|
52732
|
+
for (const block of blocks) if (block.type === "paragraph") items.push({ blocks: [block] });
|
|
52733
|
+
else if (block.type === "list") {
|
|
52734
|
+
if (!items.length) items.push({ blocks: [empty] });
|
|
52735
|
+
items.at(-1).blocks.push(block);
|
|
52736
|
+
} else for (const line of asLines([block])) items.push({ blocks: [line] });
|
|
52737
|
+
if (!items.length || items.at(-1).blocks.length > 1) items.push({ blocks: [empty] });
|
|
52738
|
+
const ordered = list.type.name === "ordered_list";
|
|
52739
|
+
const nodes = liftDeepLevels({
|
|
52740
|
+
type: "list",
|
|
52741
|
+
ordered,
|
|
52742
|
+
items
|
|
52743
|
+
}, depth).items.map((item) => schema.nodes.list_item.create({
|
|
52744
|
+
label: ordered ? "1." : "•",
|
|
52745
|
+
listType: ordered ? "ordered" : "bullet",
|
|
52746
|
+
spread: false,
|
|
52747
|
+
checked: null
|
|
52748
|
+
}, item.blocks.map((block) => proseBlock(schema, block, marks))));
|
|
52749
|
+
return new Slice(Fragment.fromArray(nodes), 2, 2);
|
|
52750
|
+
}
|
|
52751
|
+
/**
|
|
52752
|
+
* The slice that `input` pastes into an RTF document at the selection. Explicit Markdown pastes as
|
|
52753
|
+
* its text, unparsed; HTML keeps what RTF can hold; plain text gives a paragraph for each line.
|
|
52754
|
+
* Plain text takes the marks of the text it replaces, as typing would.
|
|
52755
|
+
*/
|
|
52756
|
+
function richTextPaste(input, state, schemes) {
|
|
52757
|
+
const { schema, selection } = state;
|
|
52758
|
+
const { $from, $to } = selection;
|
|
52759
|
+
const html = input.markdown == null && input.html ? htmlBlocks(input.html, schemes) : void 0;
|
|
52760
|
+
const plain = html == null;
|
|
52761
|
+
let blocks = html ?? textBlocks(input.markdown ?? input.text);
|
|
52762
|
+
if (html && !blocks.some((block) => block.type !== "paragraph" || block.content.length)) blocks = textBlocks(input.text);
|
|
52763
|
+
let cell = false;
|
|
52764
|
+
let lists = 0;
|
|
52765
|
+
let list;
|
|
52766
|
+
for (let depth = $from.depth; depth > 0; depth--) {
|
|
52767
|
+
const node = $from.node(depth);
|
|
52768
|
+
const name = node.type.name;
|
|
52769
|
+
if (name === "table_cell" || name === "table_header") cell = true;
|
|
52770
|
+
if (name === "bullet_list" || name === "ordered_list") {
|
|
52771
|
+
lists++;
|
|
52772
|
+
list ??= node;
|
|
52773
|
+
}
|
|
52774
|
+
}
|
|
52775
|
+
if (cell) blocks = asLines(blocks);
|
|
52776
|
+
else blocks = blocks.map((block) => block.type === "list" ? liftDeepLevels(block, lists + 1) : block);
|
|
52777
|
+
const marks = plain ? state.storedMarks ?? ($from.marksAcross($to) ?? $from.marks()).filter((mark) => mark.type.name !== "link") : [];
|
|
52778
|
+
if (list && !cell && !(blocks.length === 1 && blocks[0].type === "paragraph")) return listItemsSlice(schema, blocks, list, lists, marks);
|
|
52779
|
+
const nodes = blocks.map((block) => proseBlock(schema, block, marks));
|
|
52780
|
+
if (!nodes.length) return Slice.empty;
|
|
52781
|
+
const openStart = nodes[0].type.name === "paragraph" ? 1 : 0;
|
|
52782
|
+
const openEnd = nodes.at(-1).type.name === "paragraph" ? 1 : 0;
|
|
52783
|
+
return new Slice(Fragment.fromArray(nodes), openStart, openEnd);
|
|
52784
|
+
}
|
|
52785
|
+
//#endregion
|
|
52786
|
+
//#region src/formats/rich-text.ts
|
|
52787
|
+
function source(ctx, text) {
|
|
52788
|
+
const { schema } = ctx.get(editorViewCtx).state;
|
|
52789
|
+
const document = richTextFromText(text);
|
|
52790
|
+
const emptyAsNone = document.blocks.length === 0;
|
|
52791
|
+
return {
|
|
52792
|
+
baseline: richTextToProse(schema, document),
|
|
52793
|
+
serialize: (doc) => richTextText(proseToRichText(doc, emptyAsNone))
|
|
52794
|
+
};
|
|
52795
|
+
}
|
|
52796
|
+
/** Rich text, which the host converts to and from RTF. The editor holds the canonical JSON as the
|
|
52797
|
+
* document's text. It reaches no Markdown parsing or preservation;
|
|
52798
|
+
* `test/formats/boundary.test.ts` checks its imports. */
|
|
52799
|
+
var richTextFormat = {
|
|
52800
|
+
format: "rtf",
|
|
52801
|
+
promise: "structure",
|
|
52802
|
+
literalOnly: false,
|
|
52803
|
+
sourceMode: false,
|
|
52804
|
+
formattedLabel: "richTextEditor",
|
|
52805
|
+
literalLabel: "sourceEditor",
|
|
52806
|
+
headings: false,
|
|
52807
|
+
nativeCopy: false,
|
|
52808
|
+
keepsInsertedLineEndings: false,
|
|
52809
|
+
markdownInput: false,
|
|
52810
|
+
load: source,
|
|
52811
|
+
parse: source,
|
|
52812
|
+
pendingExport: () => void 0,
|
|
52813
|
+
literalPaste: (input) => input.text,
|
|
52814
|
+
richText: {
|
|
52815
|
+
allows: richTextAllows,
|
|
52816
|
+
linkedUnderlines,
|
|
52817
|
+
inLink,
|
|
52818
|
+
paste: richTextPaste,
|
|
52819
|
+
copy(state, linkSchemes) {
|
|
52820
|
+
const content = state.selection.content().content;
|
|
52821
|
+
return clipboardContent(content, state.schema, linkSchemes);
|
|
52822
|
+
}
|
|
52823
|
+
},
|
|
52824
|
+
async print({ doc, signal, output }) {
|
|
52825
|
+
return printableMarkdown(doc.content, output.ctx().get(editorViewCtx).state.schema, void 0, output.context(), void 0, signal);
|
|
52826
|
+
},
|
|
52827
|
+
async clipboard({ all, version, output, assertCurrent }) {
|
|
52828
|
+
const { state } = output.ctx().get(editorViewCtx);
|
|
52829
|
+
const slice = all ? new Slice(state.doc.content, 0, 0) : state.selection.content();
|
|
52830
|
+
const content = slice.content;
|
|
52831
|
+
const markdown = output.richTextMarkdown(slice);
|
|
52832
|
+
const { html } = clipboardContent(content, state.schema, output.linkSchemes());
|
|
52833
|
+
const text = clipboardText(content);
|
|
52834
|
+
assertCurrent();
|
|
52835
|
+
return {
|
|
52836
|
+
...version,
|
|
52837
|
+
text,
|
|
52838
|
+
html,
|
|
52839
|
+
markdown,
|
|
52840
|
+
images: [],
|
|
52841
|
+
diagrams: []
|
|
52842
|
+
};
|
|
52843
|
+
}
|
|
52844
|
+
};
|
|
51645
52845
|
//#endregion
|
|
51646
52846
|
//#region src/formats/text.ts
|
|
51647
52847
|
/** A paragraph holding `text`, or an empty one. */
|
|
@@ -51656,10 +52856,12 @@ var textFormat = {
|
|
|
51656
52856
|
promise: "exact",
|
|
51657
52857
|
literalOnly: true,
|
|
51658
52858
|
sourceMode: false,
|
|
52859
|
+
formattedLabel: "formattedEditor",
|
|
51659
52860
|
literalLabel: "plainTextEditor",
|
|
51660
52861
|
headings: false,
|
|
51661
52862
|
nativeCopy: true,
|
|
51662
52863
|
keepsInsertedLineEndings: false,
|
|
52864
|
+
markdownInput: false,
|
|
51663
52865
|
load: (ctx) => paragraph(ctx, ""),
|
|
51664
52866
|
parse: paragraph,
|
|
51665
52867
|
pendingExport: () => void 0,
|
|
@@ -51681,11 +52883,18 @@ var textFormat = {
|
|
|
51681
52883
|
};
|
|
51682
52884
|
//#endregion
|
|
51683
52885
|
//#region src/formats/index.ts
|
|
51684
|
-
/** Every format Inkkit can open
|
|
51685
|
-
var documentFormats = [
|
|
52886
|
+
/** Every format Inkkit can open. */
|
|
52887
|
+
var documentFormats = [
|
|
52888
|
+
"md",
|
|
52889
|
+
"txt",
|
|
52890
|
+
"rtf"
|
|
52891
|
+
];
|
|
52892
|
+
/** The formats a host is offered when it chooses none. */
|
|
52893
|
+
var defaultFormats = ["md", "txt"];
|
|
51686
52894
|
var formatHandlers = {
|
|
51687
52895
|
md: markdownFormat,
|
|
51688
|
-
txt: textFormat
|
|
52896
|
+
txt: textFormat,
|
|
52897
|
+
rtf: richTextFormat
|
|
51689
52898
|
};
|
|
51690
52899
|
//#endregion
|
|
51691
52900
|
//#region src/editor-document.ts
|
|
@@ -51746,7 +52955,7 @@ var EditorDocument = class {
|
|
|
51746
52955
|
this.sourcePreservations.set(provenance, preservation);
|
|
51747
52956
|
}
|
|
51748
52957
|
}
|
|
51749
|
-
text = preservation.serialize(cleanSourceDoc(doc), this.carried);
|
|
52958
|
+
text = preservation.serialize(this.handler.richText ? doc : cleanSourceDoc(doc), this.carried);
|
|
51750
52959
|
} catch (error) {
|
|
51751
52960
|
throw coded(error, "preservation");
|
|
51752
52961
|
}
|
|
@@ -51793,7 +53002,7 @@ var EditorDocument = class {
|
|
|
51793
53002
|
*/
|
|
51794
53003
|
authoredSources(doc, positions) {
|
|
51795
53004
|
const none = /* @__PURE__ */ new Map();
|
|
51796
|
-
if (this.runtime.literal() || !positions.length) return none;
|
|
53005
|
+
if (this.runtime.literal() || this.handler.richText || !positions.length) return none;
|
|
51797
53006
|
try {
|
|
51798
53007
|
let marked = doc;
|
|
51799
53008
|
for (let index = positions.length - 1; index >= 0; index--) {
|
|
@@ -52305,7 +53514,7 @@ var rejoinLists = $prose(() => keymap$3({ Backspace: (state, dispatch) => {
|
|
|
52305
53514
|
const list = $from.node(-2);
|
|
52306
53515
|
const sublist = container.maybeChild(1);
|
|
52307
53516
|
const previous = list.maybeChild($from.index(-2) - 1)?.lastChild;
|
|
52308
|
-
if (index !== 0 || container.childCount !== 2 || !sublist || !isList(sublist) || previous?.type !== sublist.type) return false;
|
|
53517
|
+
if (index !== 0 || container.childCount !== 2 || !sublist || !isList$1(sublist) || previous?.type !== sublist.type) return false;
|
|
52309
53518
|
if (dispatch) {
|
|
52310
53519
|
const at = $from.before(-1);
|
|
52311
53520
|
const end = at - 2;
|
|
@@ -52317,7 +53526,7 @@ var rejoinLists = $prose(() => keymap$3({ Backspace: (state, dispatch) => {
|
|
|
52317
53526
|
}
|
|
52318
53527
|
const before = container.maybeChild(index - 1);
|
|
52319
53528
|
const after = container.maybeChild(index + 1);
|
|
52320
|
-
if (!before || !after || !isList(before) || after.type !== before.type) return false;
|
|
53529
|
+
if (!before || !after || !isList$1(before) || after.type !== before.type) return false;
|
|
52321
53530
|
if (dispatch) {
|
|
52322
53531
|
const at = $from.before();
|
|
52323
53532
|
const tr = state.tr.delete(at, $from.after());
|
|
@@ -52390,21 +53599,21 @@ var movedListItems = $prose(() => new Plugin({
|
|
|
52390
53599
|
}));
|
|
52391
53600
|
var textInputPolicy = $prose(() => textInputPlugin());
|
|
52392
53601
|
function outermostListDepth($pos) {
|
|
52393
|
-
for (let depth = 1; depth <= $pos.depth; depth++) if (isList($pos.node(depth))) return depth;
|
|
53602
|
+
for (let depth = 1; depth <= $pos.depth; depth++) if (isList$1($pos.node(depth))) return depth;
|
|
52394
53603
|
return null;
|
|
52395
53604
|
}
|
|
52396
53605
|
function innermostListDepth($pos) {
|
|
52397
|
-
for (let depth = $pos.depth; depth >= 1; depth--) if (isList($pos.node(depth))) return depth;
|
|
53606
|
+
for (let depth = $pos.depth; depth >= 1; depth--) if (isList$1($pos.node(depth))) return depth;
|
|
52398
53607
|
return null;
|
|
52399
53608
|
}
|
|
52400
|
-
function isList(node) {
|
|
53609
|
+
function isList$1(node) {
|
|
52401
53610
|
return node.type.name === "bullet_list" || node.type.name === "ordered_list";
|
|
52402
53611
|
}
|
|
52403
53612
|
function joinLists(tr, start, index) {
|
|
52404
53613
|
const $start = tr.doc.resolve(start);
|
|
52405
53614
|
const before = $start.parent.maybeChild(index - 1);
|
|
52406
53615
|
const after = $start.parent.maybeChild(index);
|
|
52407
|
-
if (before && after && isList(before) && after.type === before.type) tr.join($start.posAtIndex(index));
|
|
53616
|
+
if (before && after && isList$1(before) && after.type === before.type) tr.join($start.posAtIndex(index));
|
|
52408
53617
|
}
|
|
52409
53618
|
//#endregion
|
|
52410
53619
|
//#region src/editor-commands.ts
|
|
@@ -52412,7 +53621,67 @@ function headingLevel(command) {
|
|
|
52412
53621
|
const match = /^heading([1-6])$/.exec(command);
|
|
52413
53622
|
return match ? Number(match[1]) : void 0;
|
|
52414
53623
|
}
|
|
52415
|
-
|
|
53624
|
+
/** What an RTF document cannot hold: headings, code blocks, quotes, tasks and highlight. Every
|
|
53625
|
+
* other command is available as in Markdown, and alignment only here. */
|
|
53626
|
+
var unavailableInRichText = [
|
|
53627
|
+
"heading1",
|
|
53628
|
+
"heading2",
|
|
53629
|
+
"heading3",
|
|
53630
|
+
"heading4",
|
|
53631
|
+
"heading5",
|
|
53632
|
+
"heading6",
|
|
53633
|
+
"paragraph",
|
|
53634
|
+
"highlight",
|
|
53635
|
+
"codeBlock",
|
|
53636
|
+
"quote",
|
|
53637
|
+
"taskList",
|
|
53638
|
+
"toggleTask"
|
|
53639
|
+
];
|
|
53640
|
+
var alignments = {
|
|
53641
|
+
alignLeft: null,
|
|
53642
|
+
alignCenter: "center",
|
|
53643
|
+
alignRight: "right",
|
|
53644
|
+
alignJustify: "justify"
|
|
53645
|
+
};
|
|
53646
|
+
function isAlignCommand(command) {
|
|
53647
|
+
return command in alignments;
|
|
53648
|
+
}
|
|
53649
|
+
/** Calls `found` for each paragraph the selection touches that can take an alignment, which
|
|
53650
|
+
* those in table cells cannot, until it returns false. */
|
|
53651
|
+
function eachAlignable(state, found) {
|
|
53652
|
+
const { from, to } = state.selection;
|
|
53653
|
+
let going = true;
|
|
53654
|
+
state.doc.nodesBetween(from, to, (node, pos) => {
|
|
53655
|
+
if (!going || node.type.name === "table") return false;
|
|
53656
|
+
if (node.type.name === "paragraph") going = found(node, pos);
|
|
53657
|
+
return going && !node.isTextblock;
|
|
53658
|
+
});
|
|
53659
|
+
}
|
|
53660
|
+
function canAlign(state) {
|
|
53661
|
+
let any = false;
|
|
53662
|
+
eachAlignable(state, () => {
|
|
53663
|
+
any = true;
|
|
53664
|
+
return false;
|
|
53665
|
+
});
|
|
53666
|
+
return any;
|
|
53667
|
+
}
|
|
53668
|
+
/** The alignment of the paragraph that holds the start of the selection. */
|
|
53669
|
+
function caretAlignment(state) {
|
|
53670
|
+
const { parent } = state.selection.$from;
|
|
53671
|
+
return parent.type.name === "paragraph" && parent.attrs.align ? parent.attrs.align : "left";
|
|
53672
|
+
}
|
|
53673
|
+
function formatAvailability(editor, state, formatted, handler) {
|
|
53674
|
+
const available = markdownAvailability(editor, state, formatted);
|
|
53675
|
+
const rich = handler.richText;
|
|
53676
|
+
if (!rich) return available;
|
|
53677
|
+
for (const command of unavailableInRichText) available[command] = false;
|
|
53678
|
+
if (available.underline && rich.inLink(state)) available.underline = false;
|
|
53679
|
+
const align = formatted && canAlign(state);
|
|
53680
|
+
available.alignLeft = available.alignCenter = align;
|
|
53681
|
+
available.alignRight = available.alignJustify = align;
|
|
53682
|
+
return available;
|
|
53683
|
+
}
|
|
53684
|
+
function markdownAvailability(editor, state, formatted) {
|
|
52416
53685
|
const manager = editor.ctx.get(commandsCtx);
|
|
52417
53686
|
const check = (key, payload) => formatted && manager.get(key)(payload)(state);
|
|
52418
53687
|
const canBecome = (type, level) => formatted && textblockType(editor, type, level)(state);
|
|
@@ -52439,9 +53708,27 @@ function formatAvailability(editor, state, formatted) {
|
|
|
52439
53708
|
orderedList: formatted && (innermostListDepth(textBounds(state).$from) != null || check(wrapInOrderedListCommand.key)),
|
|
52440
53709
|
taskList: formatted && (innermostListDepth(textBounds(state).$from) != null || check(wrapInBulletListCommand.key)),
|
|
52441
53710
|
toggleTask: formatted && taskAtCaret(state) != null,
|
|
53711
|
+
alignLeft: false,
|
|
53712
|
+
alignCenter: false,
|
|
53713
|
+
alignRight: false,
|
|
53714
|
+
alignJustify: false,
|
|
52442
53715
|
link: check(toggleLinkCommand.key, {})
|
|
52443
53716
|
};
|
|
52444
53717
|
}
|
|
53718
|
+
/** Sets the alignment of the paragraphs the selection touches. */
|
|
53719
|
+
function align(editor, command) {
|
|
53720
|
+
const view = editor.ctx.get(editorViewCtx);
|
|
53721
|
+
const value = alignments[command];
|
|
53722
|
+
const tr = view.state.tr;
|
|
53723
|
+
eachAlignable(view.state, (node, pos) => {
|
|
53724
|
+
if (node.attrs.align !== value) tr.setNodeMarkup(pos, void 0, {
|
|
53725
|
+
...node.attrs,
|
|
53726
|
+
align: value
|
|
53727
|
+
});
|
|
53728
|
+
return true;
|
|
53729
|
+
});
|
|
53730
|
+
if (tr.docChanged) view.dispatch(tr);
|
|
53731
|
+
}
|
|
52445
53732
|
function canQuote(editor, state) {
|
|
52446
53733
|
const { $from, $to } = textBounds(state);
|
|
52447
53734
|
if (quoted($from, $to)) {
|
|
@@ -52496,12 +53783,32 @@ function toggleInlineCode(editor) {
|
|
|
52496
53783
|
if (!type.isInSet(selection.$from.marks())) view.dispatch(view.state.tr.addStoredMark(type.create()));
|
|
52497
53784
|
else withMarkRunSelected(editor, type, run);
|
|
52498
53785
|
}
|
|
52499
|
-
function list(editor, kind) {
|
|
53786
|
+
function list(editor, kind, itemPerBlock) {
|
|
52500
53787
|
const view = editor.ctx.get(editorViewCtx);
|
|
52501
53788
|
const { state } = view;
|
|
52502
53789
|
const { $from } = textBounds(state);
|
|
52503
53790
|
const wanted = kind === "bulletList" ? "bullet_list" : "ordered_list";
|
|
52504
53791
|
const depth = innermostListDepth($from);
|
|
53792
|
+
if (depth == null && itemPerBlock) {
|
|
53793
|
+
const ordered = wanted === "ordered_list";
|
|
53794
|
+
wrapInList(state.schema.nodes[wanted], ordered ? {
|
|
53795
|
+
order: 1,
|
|
53796
|
+
spread: false
|
|
53797
|
+
} : { spread: false })(state, (tr) => {
|
|
53798
|
+
tr.doc.nodesBetween(tr.mapping.map(state.selection.from), tr.mapping.map(state.selection.to), (node, pos) => {
|
|
53799
|
+
if (node.type.name !== "list_item") return true;
|
|
53800
|
+
tr.setNodeMarkup(pos, void 0, {
|
|
53801
|
+
...node.attrs,
|
|
53802
|
+
listType: ordered ? "ordered" : "bullet",
|
|
53803
|
+
label: ordered ? "1." : "•",
|
|
53804
|
+
spread: false
|
|
53805
|
+
});
|
|
53806
|
+
return false;
|
|
53807
|
+
});
|
|
53808
|
+
view.dispatch(tr);
|
|
53809
|
+
});
|
|
53810
|
+
return;
|
|
53811
|
+
}
|
|
52505
53812
|
if (depth == null) {
|
|
52506
53813
|
const command = kind === "bulletList" ? wrapInBulletListCommand : wrapInOrderedListCommand;
|
|
52507
53814
|
editor.action(callCommand(command.key));
|
|
@@ -52560,11 +53867,15 @@ function textblockType(editor, type, attr) {
|
|
|
52560
53867
|
if (type === "heading") return setTextblockType(ctx, headingSchema.type(ctx), { level: Number(attr) });
|
|
52561
53868
|
return setTextblockType(ctx, paragraphSchema.type(ctx));
|
|
52562
53869
|
}
|
|
52563
|
-
function applyFormat(editor, command, checked, link) {
|
|
53870
|
+
function applyFormat(editor, command, checked, link, itemPerBlock = false) {
|
|
52564
53871
|
const view = editor.ctx.get(editorViewCtx);
|
|
52565
53872
|
const run = (cmd, payload) => editor.action(callCommand(cmd, payload));
|
|
52566
53873
|
const state = caretState(view.state);
|
|
52567
53874
|
const become = (type, attr) => textblockType(editor, type, attr)(view.state, view.dispatch);
|
|
53875
|
+
if (isAlignCommand(command)) {
|
|
53876
|
+
align(editor, command);
|
|
53877
|
+
return;
|
|
53878
|
+
}
|
|
52568
53879
|
const level = headingLevel(command);
|
|
52569
53880
|
if (level != null) {
|
|
52570
53881
|
if (state.block.type === "heading" && state.block.level === level) become("paragraph");
|
|
@@ -52608,7 +53919,7 @@ function applyFormat(editor, command, checked, link) {
|
|
|
52608
53919
|
break;
|
|
52609
53920
|
case "bulletList":
|
|
52610
53921
|
case "orderedList":
|
|
52611
|
-
list(editor, command);
|
|
53922
|
+
list(editor, command, itemPerBlock);
|
|
52612
53923
|
break;
|
|
52613
53924
|
case "taskList":
|
|
52614
53925
|
toggleTaskList(editor.ctx);
|
|
@@ -55583,6 +56894,7 @@ async function createMilkdownEditor(root, options, runtime) {
|
|
|
55583
56894
|
ctx.set(rootCtx, root);
|
|
55584
56895
|
ctx.set(authoredSourcesCtx.key, (doc, positions) => runtime.authoredSources(doc, positions));
|
|
55585
56896
|
ctx.set(smartPunctuationCtx.key, () => runtime.smartPunctuation());
|
|
56897
|
+
ctx.set(formatInputCtx.key, runtime.format);
|
|
55586
56898
|
ctx.set(linkSchemesCtx.key, runtime.linkSchemes);
|
|
55587
56899
|
ctx.update(labelsCtx.key, () => ({
|
|
55588
56900
|
...defaultLabels,
|
|
@@ -55632,7 +56944,7 @@ async function createMilkdownEditor(root, options, runtime) {
|
|
|
55632
56944
|
return true;
|
|
55633
56945
|
}
|
|
55634
56946
|
}));
|
|
55635
|
-
}).use(labelsCtx).use(linkSchemesCtx).use(runtime.plugins.paste).use(visibleClipboard).use(runtime.plugins.suggestions).use(runtime.plugins.keymap).use(createDialect(Boolean(options.images || options.files), Boolean(options.wikiLinks), Boolean(options.files))).use(codeControlCtx).use(codeControlsPlugin(() => runtime.visibleRect())).use(tablePlugins).use(tableBlock).use(runtime.plugins.change).use(runtime.plugins.historyInput).use(history).use(clipboard).use(cursor).use(taskListPlugin((operation) => runtime.mutate(operation))).use(undoTypedRule).use(quoteBackspace).use(calloutEnter).use(literalEnter).use(literalReread).use(listItemBackspace).use(nestedItemEnter).use(rejoinLists).use(movedListItems).use(textInputPolicy).use(runtime.copy ? codeCopyPlugin((text) => runtime.copy?.(text)) : []).use(placeholderPlugin).use(options.rendering?.codeHighlighting === false ? [] : (await import("./highlight-
|
|
56947
|
+
}).use(labelsCtx).use(linkSchemesCtx).use(formatInputCtx).use(richTextGuard).use(runtime.plugins.paste).use(visibleClipboard).use(runtime.plugins.suggestions).use(runtime.plugins.keymap).use(createDialect(Boolean(options.images || options.files), Boolean(options.wikiLinks), Boolean(options.files))).use(codeControlCtx).use(codeControlsPlugin(() => runtime.visibleRect())).use(tablePlugins).use(tableBlock).use(runtime.plugins.change).use(runtime.plugins.historyInput).use(history).use(clipboard).use(cursor).use(taskListPlugin((operation) => runtime.mutate(operation))).use(undoTypedRule).use(quoteBackspace).use(calloutEnter).use(literalEnter).use(literalReread).use(listItemBackspace).use(nestedItemEnter).use(rejoinLists).use(movedListItems).use(textInputPolicy).use(runtime.copy ? codeCopyPlugin((text) => runtime.copy?.(text)) : []).use(placeholderPlugin).use(options.rendering?.codeHighlighting === false ? [] : (await import("./highlight-4lYN38Zo.js")).highlightPlugin).use(options.rendering?.diagramPreview === false || !diagramsAvailable() ? [] : mermaidPreview(runtime.diagrams, (error) => runtime.report(error))).use(selectionPlugin).use($prose(() => search())).use(runtime.plugins.policy).use(options.wikiLinks ? wikiLinkView(options.wikiLinks, {
|
|
55636
56948
|
capture: () => runtime.capture(),
|
|
55637
56949
|
active: () => runtime.active(),
|
|
55638
56950
|
labels: {
|
|
@@ -55648,8 +56960,46 @@ async function createMilkdownEditor(root, options, runtime) {
|
|
|
55648
56960
|
...options.labels
|
|
55649
56961
|
},
|
|
55650
56962
|
error: (error) => runtime.report(error)
|
|
55651
|
-
}, options.files, runtime.canMutate) : []).create()
|
|
56963
|
+
}, options.files, runtime.canMutate) : []).create().then((editor) => {
|
|
56964
|
+
markdownInputRules(editor.ctx);
|
|
56965
|
+
return editor;
|
|
56966
|
+
});
|
|
56967
|
+
}
|
|
56968
|
+
/** Markdown input rules, such as `# ` for a heading, do nothing in a format that is not Markdown.
|
|
56969
|
+
* Rules that are not Markdown syntax, such as smart punctuation, keep applying. */
|
|
56970
|
+
function markdownInputRules(ctx) {
|
|
56971
|
+
for (const rule of ctx.get(inputRulesCtx)) {
|
|
56972
|
+
if (formatNeutralRules.has(rule)) continue;
|
|
56973
|
+
const held = rule;
|
|
56974
|
+
const handler = held.handler;
|
|
56975
|
+
held.handler = (state, match, start, end) => formatInput(ctx).markdownInput() ? handler(state, match, start, end) : null;
|
|
56976
|
+
}
|
|
55652
56977
|
}
|
|
56978
|
+
/**
|
|
56979
|
+
* In a format that holds less than Markdown, such as RTF, refuses any transaction that would bring
|
|
56980
|
+
* in a node, mark or structure the format cannot hold, so a disallowed edit changes nothing. A
|
|
56981
|
+
* link's underline, which RTF does not keep, is removed in the same step.
|
|
56982
|
+
*/
|
|
56983
|
+
var richTextGuard = $prose((ctx) => new Plugin({
|
|
56984
|
+
key: new PluginKey("inkkitRichTextGuard"),
|
|
56985
|
+
filterTransaction(tr, state) {
|
|
56986
|
+
const rich = formatInput(ctx).richText();
|
|
56987
|
+
return !rich || rich.allows(tr, state);
|
|
56988
|
+
},
|
|
56989
|
+
appendTransaction(transactions, oldState, newState) {
|
|
56990
|
+
const rich = formatInput(ctx).richText();
|
|
56991
|
+
if (!rich || !transactions.some((tr) => tr.docChanged)) return null;
|
|
56992
|
+
const start = oldState.doc.content.findDiffStart(newState.doc.content);
|
|
56993
|
+
if (start == null) return null;
|
|
56994
|
+
const end = oldState.doc.content.findDiffEnd(newState.doc.content).b;
|
|
56995
|
+
const ranges = rich.linkedUnderlines(newState.doc, start, Math.max(start, end));
|
|
56996
|
+
if (!ranges.length) return null;
|
|
56997
|
+
const tr = newState.tr;
|
|
56998
|
+
for (const { from, to } of ranges) tr.removeMark(from, to, newState.schema.marks.underline);
|
|
56999
|
+
if (newState.storedMarks) tr.setStoredMarks(newState.storedMarks);
|
|
57000
|
+
return tr;
|
|
57001
|
+
}
|
|
57002
|
+
}));
|
|
55653
57003
|
function createDocumentState(ctx, doc) {
|
|
55654
57004
|
return EditorState.create(ctx.get(editorStateOptionsCtx)({
|
|
55655
57005
|
schema: ctx.get(schemaCtx),
|
|
@@ -55662,6 +57012,402 @@ function initialDocumentSelection(state) {
|
|
|
55662
57012
|
return setSearchState(state.tr.setSelection(end), new SearchQuery({ search: "" }));
|
|
55663
57013
|
}
|
|
55664
57014
|
//#endregion
|
|
57015
|
+
//#region src/formats/convert.ts
|
|
57016
|
+
var Losses = class {
|
|
57017
|
+
counts = /* @__PURE__ */ new Map();
|
|
57018
|
+
add(code) {
|
|
57019
|
+
this.counts.set(code, (this.counts.get(code) ?? 0) + 1);
|
|
57020
|
+
}
|
|
57021
|
+
list() {
|
|
57022
|
+
return Object.freeze(formatLossCodes.filter((code) => this.counts.has(code)).map((code) => Object.freeze({
|
|
57023
|
+
code,
|
|
57024
|
+
count: this.counts.get(code)
|
|
57025
|
+
})));
|
|
57026
|
+
}
|
|
57027
|
+
};
|
|
57028
|
+
var richMarks = {
|
|
57029
|
+
strong: "bold",
|
|
57030
|
+
emphasis: "italic",
|
|
57031
|
+
underline: "underline",
|
|
57032
|
+
strike_through: "strikethrough",
|
|
57033
|
+
superscript: "superscript",
|
|
57034
|
+
subscript: "subscript",
|
|
57035
|
+
inlineCode: "code",
|
|
57036
|
+
link: "link"
|
|
57037
|
+
};
|
|
57038
|
+
var isList = (node) => node.type.name === "bullet_list" || node.type.name === "ordered_list";
|
|
57039
|
+
/** Reads a formatted Markdown document as rich text, counting what RTF cannot hold. */
|
|
57040
|
+
var MarkdownReader = class {
|
|
57041
|
+
lost = new Losses();
|
|
57042
|
+
/** Appends text, counting characters RTF can't hold, which become U+FFFD. */
|
|
57043
|
+
append(builder, text, marks) {
|
|
57044
|
+
if (/[\u0000\ufffc]|[\ud800-\udbff](?![\udc00-\udfff])|(?<![\ud800-\udbff])[\udc00-\udfff]/.test(text)) this.lost.add("invalid-character");
|
|
57045
|
+
appendText(builder, text, marks);
|
|
57046
|
+
}
|
|
57047
|
+
marks(marks, extra) {
|
|
57048
|
+
const result = [...extra];
|
|
57049
|
+
for (const mark of marks) {
|
|
57050
|
+
const type = richMarks[mark.type.name];
|
|
57051
|
+
if (!type) {
|
|
57052
|
+
if (mark.type.name === "highlight") this.lost.add("highlight");
|
|
57053
|
+
continue;
|
|
57054
|
+
}
|
|
57055
|
+
if (result.some((held) => held.type === type)) continue;
|
|
57056
|
+
if (type === "link") {
|
|
57057
|
+
const href = String(mark.attrs.href ?? "");
|
|
57058
|
+
if (linkProblem(href)) {
|
|
57059
|
+
this.lost.add("link");
|
|
57060
|
+
continue;
|
|
57061
|
+
}
|
|
57062
|
+
result.push({
|
|
57063
|
+
type: "link",
|
|
57064
|
+
href
|
|
57065
|
+
});
|
|
57066
|
+
} else result.push({ type });
|
|
57067
|
+
}
|
|
57068
|
+
if (result.some((mark) => mark.type === "link") && result.some((mark) => mark.type === "underline")) this.lost.add("link-underline");
|
|
57069
|
+
return canonicalMarks(result);
|
|
57070
|
+
}
|
|
57071
|
+
/** Appends a textblock's inline content. */
|
|
57072
|
+
inline(node, builder, extra = []) {
|
|
57073
|
+
node.forEach((child) => {
|
|
57074
|
+
const name = child.type.name;
|
|
57075
|
+
if (child.isText) this.append(builder, child.text, this.marks(child.marks, extra));
|
|
57076
|
+
else if (name === "hardbreak") builder.break();
|
|
57077
|
+
else if (name === "footnote_reference") {
|
|
57078
|
+
this.lost.add("footnote");
|
|
57079
|
+
this.append(builder, `[${String(child.attrs.label)}]`, this.marks(child.marks, extra));
|
|
57080
|
+
} else if (name === "inkkit_wiki_link") {
|
|
57081
|
+
this.lost.add("wiki-link");
|
|
57082
|
+
this.append(builder, String(child.attrs.label || child.attrs.target || child.attrs.raw || ""), this.marks(child.marks, extra));
|
|
57083
|
+
} else if (name === "image") this.lost.add(child.attrs.inkkitFileTarget || child.attrs.inkkitFileRaw ? "file" : "image");
|
|
57084
|
+
else if (name === "comment_inline") this.lost.add("comment");
|
|
57085
|
+
else if (name === "html") {
|
|
57086
|
+
this.lost.add("literal");
|
|
57087
|
+
this.append(builder, String(child.attrs.value ?? ""), this.marks(child.marks, extra));
|
|
57088
|
+
} else if (child.isInline) this.inline(child, builder, extra);
|
|
57089
|
+
});
|
|
57090
|
+
}
|
|
57091
|
+
paragraph(node, extra = []) {
|
|
57092
|
+
const builder = new InlineBuilder();
|
|
57093
|
+
this.inline(node, builder, extra);
|
|
57094
|
+
return {
|
|
57095
|
+
type: "paragraph",
|
|
57096
|
+
content: builder.finish()
|
|
57097
|
+
};
|
|
57098
|
+
}
|
|
57099
|
+
lines(text, marks) {
|
|
57100
|
+
return text.split(/\r\n|[\n\r]/).map((line) => {
|
|
57101
|
+
const builder = new InlineBuilder();
|
|
57102
|
+
this.append(builder, line, marks);
|
|
57103
|
+
return {
|
|
57104
|
+
type: "paragraph",
|
|
57105
|
+
content: builder.finish()
|
|
57106
|
+
};
|
|
57107
|
+
});
|
|
57108
|
+
}
|
|
57109
|
+
/** The blocks for `node`, `depth` lists deep. */
|
|
57110
|
+
blocks(node, depth) {
|
|
57111
|
+
switch (node.type.name) {
|
|
57112
|
+
case "paragraph": return [this.paragraph(node)];
|
|
57113
|
+
case "heading":
|
|
57114
|
+
this.lost.add("heading");
|
|
57115
|
+
return [this.paragraph(node, [{ type: "bold" }])];
|
|
57116
|
+
case "code_block":
|
|
57117
|
+
this.lost.add(String(node.attrs.language).toLowerCase() === "mermaid" ? "diagram" : "code-block");
|
|
57118
|
+
return this.lines(node.textContent, [{ type: "code" }]);
|
|
57119
|
+
case "literal_markdown":
|
|
57120
|
+
this.lost.add("literal");
|
|
57121
|
+
return this.lines(node.textContent, []);
|
|
57122
|
+
case "blockquote":
|
|
57123
|
+
case "inkkit_callout": {
|
|
57124
|
+
this.lost.add(node.type.name === "blockquote" ? "quote" : "callout");
|
|
57125
|
+
const blocks = [];
|
|
57126
|
+
const title = node.attrs.title;
|
|
57127
|
+
if (title) blocks.push(...this.lines(title, [{ type: "bold" }]));
|
|
57128
|
+
node.forEach((child) => blocks.push(...this.blocks(child, depth)));
|
|
57129
|
+
return blocks;
|
|
57130
|
+
}
|
|
57131
|
+
case "footnote_definition": {
|
|
57132
|
+
this.lost.add("footnote");
|
|
57133
|
+
const blocks = [];
|
|
57134
|
+
node.forEach((child) => blocks.push(...this.blocks(child, depth)));
|
|
57135
|
+
const label = `[${String(node.attrs.label)}] `;
|
|
57136
|
+
const first = blocks[0];
|
|
57137
|
+
const builder = new InlineBuilder();
|
|
57138
|
+
builder.text(first?.type === "paragraph" ? label : label.trim(), []);
|
|
57139
|
+
if (first?.type === "paragraph") {
|
|
57140
|
+
for (const inline of first.content) if (inline.type === "break") builder.break();
|
|
57141
|
+
else builder.text(inline.text, inline.marks ?? []);
|
|
57142
|
+
blocks[0] = {
|
|
57143
|
+
...first,
|
|
57144
|
+
content: builder.finish()
|
|
57145
|
+
};
|
|
57146
|
+
} else blocks.unshift({
|
|
57147
|
+
type: "paragraph",
|
|
57148
|
+
content: builder.finish()
|
|
57149
|
+
});
|
|
57150
|
+
return blocks;
|
|
57151
|
+
}
|
|
57152
|
+
case "bullet_list":
|
|
57153
|
+
case "ordered_list": return [this.list(node, depth + 1)];
|
|
57154
|
+
case "table": return depth > 0 ? this.tableLines(node) : [this.table(node)];
|
|
57155
|
+
case "hr":
|
|
57156
|
+
this.lost.add("rule");
|
|
57157
|
+
return [];
|
|
57158
|
+
case "comment_block":
|
|
57159
|
+
this.lost.add("comment");
|
|
57160
|
+
return [];
|
|
57161
|
+
case "image_block":
|
|
57162
|
+
this.lost.add(node.attrs.inkkitFileTarget || node.attrs.inkkitFileRaw ? "file" : "image");
|
|
57163
|
+
return [];
|
|
57164
|
+
case "reference_definition": return [];
|
|
57165
|
+
default: {
|
|
57166
|
+
if (node.isTextblock) return [this.paragraph(node)];
|
|
57167
|
+
const blocks = [];
|
|
57168
|
+
node.forEach((child) => blocks.push(...this.blocks(child, depth)));
|
|
57169
|
+
return blocks;
|
|
57170
|
+
}
|
|
57171
|
+
}
|
|
57172
|
+
}
|
|
57173
|
+
table(node) {
|
|
57174
|
+
let aligned = false;
|
|
57175
|
+
const rows = [];
|
|
57176
|
+
node.forEach((row) => {
|
|
57177
|
+
const cells = [];
|
|
57178
|
+
row.forEach((cell) => {
|
|
57179
|
+
if (cell.attrs.alignment != null) aligned = true;
|
|
57180
|
+
const builder = new InlineBuilder();
|
|
57181
|
+
cell.forEach((child, _offset, index) => {
|
|
57182
|
+
if (index) builder.break();
|
|
57183
|
+
this.inline(child, builder);
|
|
57184
|
+
});
|
|
57185
|
+
cells.push({ content: builder.finish() });
|
|
57186
|
+
});
|
|
57187
|
+
rows.push(cells);
|
|
57188
|
+
});
|
|
57189
|
+
if (aligned) this.lost.add("table-alignment");
|
|
57190
|
+
return {
|
|
57191
|
+
type: "table",
|
|
57192
|
+
rows
|
|
57193
|
+
};
|
|
57194
|
+
}
|
|
57195
|
+
/** A table inside a list item, as lines of tab-separated cells. */
|
|
57196
|
+
tableLines(node) {
|
|
57197
|
+
return this.table(node).rows.map((row) => {
|
|
57198
|
+
const builder = new InlineBuilder();
|
|
57199
|
+
row.forEach((cell, index) => {
|
|
57200
|
+
if (index) builder.text(" ", []);
|
|
57201
|
+
for (const inline of cell.content) if (inline.type === "break") builder.break();
|
|
57202
|
+
else builder.text(inline.text, inline.marks ?? []);
|
|
57203
|
+
});
|
|
57204
|
+
return {
|
|
57205
|
+
type: "paragraph",
|
|
57206
|
+
content: builder.finish()
|
|
57207
|
+
};
|
|
57208
|
+
});
|
|
57209
|
+
}
|
|
57210
|
+
list(node, depth) {
|
|
57211
|
+
const ordered = node.type.name === "ordered_list";
|
|
57212
|
+
let start = Number(node.attrs.order ?? 1);
|
|
57213
|
+
if (ordered && !(Number.isSafeInteger(start) && start >= 1)) {
|
|
57214
|
+
this.lost.add("list-start");
|
|
57215
|
+
start = 1;
|
|
57216
|
+
} else if (ordered && start > 2147483647) {
|
|
57217
|
+
this.lost.add("list-start");
|
|
57218
|
+
start = maximumListStart;
|
|
57219
|
+
}
|
|
57220
|
+
const items = [];
|
|
57221
|
+
node.forEach((item) => {
|
|
57222
|
+
if (item.attrs.checked != null) this.lost.add("task");
|
|
57223
|
+
const builder = new InlineBuilder();
|
|
57224
|
+
let lines = 0;
|
|
57225
|
+
const nested = [];
|
|
57226
|
+
let flattened = false;
|
|
57227
|
+
const line = (content) => {
|
|
57228
|
+
if (lines++) builder.break();
|
|
57229
|
+
for (const inline of content) if (inline.type === "break") builder.break();
|
|
57230
|
+
else builder.text(inline.text, inline.marks ?? []);
|
|
57231
|
+
};
|
|
57232
|
+
const flatten = (block) => {
|
|
57233
|
+
if (block.type === "paragraph") line(block.content);
|
|
57234
|
+
else if (block.type === "list") for (const listItem of block.items) listItem.blocks.forEach(flatten);
|
|
57235
|
+
else for (const row of block.rows) for (const cell of row) line(cell.content);
|
|
57236
|
+
};
|
|
57237
|
+
item.forEach((child, _offset, index) => {
|
|
57238
|
+
if (isList(child)) {
|
|
57239
|
+
if (depth < 9) nested.push(...this.blocks(child, depth));
|
|
57240
|
+
else {
|
|
57241
|
+
this.lost.add("list-depth");
|
|
57242
|
+
this.blocks(child, depth).forEach(flatten);
|
|
57243
|
+
}
|
|
57244
|
+
return;
|
|
57245
|
+
}
|
|
57246
|
+
for (const block of this.blocks(child, depth)) {
|
|
57247
|
+
if (index === 0 && !lines && block.type === "paragraph") {
|
|
57248
|
+
line(block.content);
|
|
57249
|
+
continue;
|
|
57250
|
+
}
|
|
57251
|
+
flattened = true;
|
|
57252
|
+
flatten(block);
|
|
57253
|
+
}
|
|
57254
|
+
});
|
|
57255
|
+
if (flattened) this.lost.add("list-item-blocks");
|
|
57256
|
+
items.push({ blocks: [{
|
|
57257
|
+
type: "paragraph",
|
|
57258
|
+
content: builder.finish()
|
|
57259
|
+
}, ...nested] });
|
|
57260
|
+
});
|
|
57261
|
+
return ordered && start !== 1 ? {
|
|
57262
|
+
type: "list",
|
|
57263
|
+
ordered,
|
|
57264
|
+
start,
|
|
57265
|
+
items
|
|
57266
|
+
} : {
|
|
57267
|
+
type: "list",
|
|
57268
|
+
ordered,
|
|
57269
|
+
items
|
|
57270
|
+
};
|
|
57271
|
+
}
|
|
57272
|
+
};
|
|
57273
|
+
/** A formatted Markdown document as rich text, with what RTF could not hold. */
|
|
57274
|
+
function markdownToRichText(doc) {
|
|
57275
|
+
const reader = new MarkdownReader();
|
|
57276
|
+
const blocks = [];
|
|
57277
|
+
doc.forEach((node) => blocks.push(...reader.blocks(node, 0)));
|
|
57278
|
+
if (blocks.length === 1 && blocks[0].type === "paragraph" && !blocks[0].content.length) blocks.length = 0;
|
|
57279
|
+
return {
|
|
57280
|
+
document: {
|
|
57281
|
+
version: 1,
|
|
57282
|
+
blocks
|
|
57283
|
+
},
|
|
57284
|
+
lost: reader.lost.list()
|
|
57285
|
+
};
|
|
57286
|
+
}
|
|
57287
|
+
/** A rich text document as Markdown, with what Markdown could not hold: alignment. Underline,
|
|
57288
|
+
* superscript and subscript are written as `<u>`, `<sup>` and `<sub>`. */
|
|
57289
|
+
function richTextToMarkdown(ctx, document) {
|
|
57290
|
+
const lost = new Losses();
|
|
57291
|
+
const { schema } = ctx.get(editorViewCtx).state;
|
|
57292
|
+
const ready = markdownReady(document, lost);
|
|
57293
|
+
const text = serialize(ctx, htmlBreaksInCells(richTextToProse(schema, ready)));
|
|
57294
|
+
let reread;
|
|
57295
|
+
try {
|
|
57296
|
+
reread = markdownToRichText(ctx.get(parserCtx)(text)).document;
|
|
57297
|
+
} catch {
|
|
57298
|
+
reread = void 0;
|
|
57299
|
+
}
|
|
57300
|
+
if (!reread || !sameDocument(reread, ready)) throw new InkKitError("preservation", "The document could not be written as Markdown that reads back the same");
|
|
57301
|
+
return {
|
|
57302
|
+
text,
|
|
57303
|
+
lost: lost.list()
|
|
57304
|
+
};
|
|
57305
|
+
}
|
|
57306
|
+
/** Marks that show on a space: moving a space out of one changes what the reader sees. */
|
|
57307
|
+
var visibleOnSpace = /* @__PURE__ */ new Set([
|
|
57308
|
+
"underline",
|
|
57309
|
+
"strikethrough",
|
|
57310
|
+
"link"
|
|
57311
|
+
]);
|
|
57312
|
+
/**
|
|
57313
|
+
* `content` as Markdown can write it. An emphasis, strikethrough, link or HTML mark can't start or
|
|
57314
|
+
* end with a space, so leading and trailing spaces move outside the run, and a run of only spaces
|
|
57315
|
+
* loses its marks. A code span keeps its spaces. Counts `mark-spaces` where a visible mark moved.
|
|
57316
|
+
*/
|
|
57317
|
+
function markdownInlines(content, lost) {
|
|
57318
|
+
const builder = new InlineBuilder();
|
|
57319
|
+
for (const inline of content) {
|
|
57320
|
+
if (inline.type === "break") {
|
|
57321
|
+
builder.break();
|
|
57322
|
+
continue;
|
|
57323
|
+
}
|
|
57324
|
+
let marks = inline.marks ?? [];
|
|
57325
|
+
if (/^ +$/.test(inline.text) && marks.some((mark) => mark.type === "code")) {
|
|
57326
|
+
const kept = marks.filter((mark) => mark.type === "code");
|
|
57327
|
+
if (marks.some((mark) => mark.type !== "code" && visibleOnSpace.has(mark.type))) lost.add("mark-spaces");
|
|
57328
|
+
marks = kept;
|
|
57329
|
+
}
|
|
57330
|
+
const [, lead, core, trail] = /^( *)([^]*?)( *)$/.exec(inline.text);
|
|
57331
|
+
if (!marks.length || marks.some((mark) => mark.type === "code") || !lead && !trail) {
|
|
57332
|
+
builder.text(inline.text, marks);
|
|
57333
|
+
continue;
|
|
57334
|
+
}
|
|
57335
|
+
if (marks.some((mark) => visibleOnSpace.has(mark.type))) lost.add("mark-spaces");
|
|
57336
|
+
builder.text(lead, []);
|
|
57337
|
+
builder.text(core, marks);
|
|
57338
|
+
builder.text(trail, []);
|
|
57339
|
+
}
|
|
57340
|
+
return builder.finish();
|
|
57341
|
+
}
|
|
57342
|
+
/**
|
|
57343
|
+
* `document` as Markdown can hold it, with what that drops counted in `lost`: alignment, a mark's
|
|
57344
|
+
* edge spaces, a paragraph's only line break, which Markdown reads back as an empty paragraph, and
|
|
57345
|
+
* empty paragraphs at the end, which Markdown doesn't keep.
|
|
57346
|
+
*/
|
|
57347
|
+
function markdownReady(document, lost) {
|
|
57348
|
+
const block = (item) => {
|
|
57349
|
+
if (item.type === "paragraph") {
|
|
57350
|
+
if (item.align) lost.add("alignment");
|
|
57351
|
+
let content = markdownInlines(item.content, lost);
|
|
57352
|
+
if (content.length === 1 && content[0].type === "break") {
|
|
57353
|
+
lost.add("empty-line");
|
|
57354
|
+
content = [];
|
|
57355
|
+
}
|
|
57356
|
+
return {
|
|
57357
|
+
type: "paragraph",
|
|
57358
|
+
content
|
|
57359
|
+
};
|
|
57360
|
+
}
|
|
57361
|
+
if (item.type === "list") return {
|
|
57362
|
+
...item,
|
|
57363
|
+
items: item.items.map((listItem) => ({ blocks: listItem.blocks.map(block) }))
|
|
57364
|
+
};
|
|
57365
|
+
return {
|
|
57366
|
+
type: "table",
|
|
57367
|
+
rows: item.rows.map((row) => row.map((cell) => ({ content: markdownInlines(cell.content, lost) })))
|
|
57368
|
+
};
|
|
57369
|
+
};
|
|
57370
|
+
const blocks = document.blocks.map(block);
|
|
57371
|
+
while (blocks.length > 1 && blocks.at(-1).type === "paragraph" && !blocks.at(-1).content.length) {
|
|
57372
|
+
blocks.pop();
|
|
57373
|
+
lost.add("empty-line");
|
|
57374
|
+
}
|
|
57375
|
+
return {
|
|
57376
|
+
version: 1,
|
|
57377
|
+
blocks
|
|
57378
|
+
};
|
|
57379
|
+
}
|
|
57380
|
+
/** Whether two documents are the same, counting one empty paragraph as an empty document. */
|
|
57381
|
+
function sameDocument(a, b) {
|
|
57382
|
+
const text = (document) => JSON.stringify(document.blocks.length === 1 && document.blocks[0].type === "paragraph" && !document.blocks[0].content.length ? [] : document.blocks);
|
|
57383
|
+
return text(a) === text(b);
|
|
57384
|
+
}
|
|
57385
|
+
/** Markdown for content copied from a rich text document. Unlike a conversion it reports no
|
|
57386
|
+
* losses and isn't read back: a copy may lose alignment. */
|
|
57387
|
+
function richContentMarkdown(ctx, content) {
|
|
57388
|
+
const { schema } = ctx.get(editorViewCtx).state;
|
|
57389
|
+
const empty = schema.topNodeType.createAndFill();
|
|
57390
|
+
let doc = new Transform(empty).replace(0, empty.content.size, content).doc;
|
|
57391
|
+
try {
|
|
57392
|
+
doc = richTextToProse(schema, markdownReady(proseToRichText(doc, false), new Losses()));
|
|
57393
|
+
} catch {}
|
|
57394
|
+
return serialize(ctx, htmlBreaksInCells(doc));
|
|
57395
|
+
}
|
|
57396
|
+
/** `node` with each line break in a table cell written as `<br />`, since a cell is one line of
|
|
57397
|
+
* Markdown. */
|
|
57398
|
+
function htmlBreaksInCells(node, inCell = false) {
|
|
57399
|
+
if (node.type.name === "hardbreak") return inCell ? node.type.create({
|
|
57400
|
+
...node.attrs,
|
|
57401
|
+
isHTML: true
|
|
57402
|
+
}, null, node.marks) : node;
|
|
57403
|
+
if (node.isLeaf) return node;
|
|
57404
|
+
const role = node.type.spec.tableRole;
|
|
57405
|
+
const cell = inCell || role === "cell" || role === "header_cell";
|
|
57406
|
+
const children = [];
|
|
57407
|
+
node.forEach((child) => children.push(htmlBreaksInCells(child, cell)));
|
|
57408
|
+
return node.copy(Fragment.fromArray(children));
|
|
57409
|
+
}
|
|
57410
|
+
//#endregion
|
|
55665
57411
|
//#region src/paste-html.ts
|
|
55666
57412
|
var rich = /* @__PURE__ */ new Set([
|
|
55667
57413
|
"P",
|
|
@@ -56772,6 +58518,14 @@ var PasteController = class {
|
|
|
56772
58518
|
transformPasted: (slice, view, plain) => this.claimed || plain || view.dragging ? slice : importableSlice(this.options.ctx(), slice),
|
|
56773
58519
|
handlePaste: (view, event) => {
|
|
56774
58520
|
this.claimed = false;
|
|
58521
|
+
if (this.options.richText?.() && event.clipboardData && view.editable) {
|
|
58522
|
+
const data = event.clipboardData;
|
|
58523
|
+
this.run({
|
|
58524
|
+
text: data.getData("text/plain"),
|
|
58525
|
+
html: data.getData("text/html") || void 0
|
|
58526
|
+
}).catch((error) => options.onError?.(error));
|
|
58527
|
+
return true;
|
|
58528
|
+
}
|
|
56775
58529
|
const claim = claimsPaste(view, event);
|
|
56776
58530
|
if (claim === false) return false;
|
|
56777
58531
|
const data = event.clipboardData;
|
|
@@ -56876,6 +58630,17 @@ var PasteController = class {
|
|
|
56876
58630
|
},
|
|
56877
58631
|
controller: new AbortController()
|
|
56878
58632
|
};
|
|
58633
|
+
const rich = this.options.richText?.();
|
|
58634
|
+
if (rich) {
|
|
58635
|
+
const anchor = operation;
|
|
58636
|
+
const state = view.state;
|
|
58637
|
+
const slice = input.plainText ? rich.paste({ text: input.text }, state, this.options.linkSchemes ?? /* @__PURE__ */ new Set()) : rich.paste(input, state, this.options.linkSchemes ?? /* @__PURE__ */ new Set());
|
|
58638
|
+
const tr = state.tr.replaceRange(anchor.from, anchor.to, slice);
|
|
58639
|
+
const end = tr.doc.resolve(tr.mapping.map(anchor.to, 1));
|
|
58640
|
+
this.dispatch(view, tr.setSelection(Selection.near(end, -1)).scrollIntoView());
|
|
58641
|
+
if (slice.size && view.state.doc === state.doc) throw new InkKitError("preservation", "The paste was not applied because the RTF document cannot hold it there");
|
|
58642
|
+
return;
|
|
58643
|
+
}
|
|
56879
58644
|
const inCode = () => view.state.doc.resolve(this.anchor(operation).from).parent.type.spec.code;
|
|
56880
58645
|
this.active = operation;
|
|
56881
58646
|
this.options.onPendingChanged?.();
|
|
@@ -57502,6 +59267,10 @@ var formatCommands = [
|
|
|
57502
59267
|
"orderedList",
|
|
57503
59268
|
"taskList",
|
|
57504
59269
|
"toggleTask",
|
|
59270
|
+
"alignLeft",
|
|
59271
|
+
"alignCenter",
|
|
59272
|
+
"alignRight",
|
|
59273
|
+
"alignJustify",
|
|
57505
59274
|
"link"
|
|
57506
59275
|
];
|
|
57507
59276
|
/** Checks a format call's options against its command. */
|
|
@@ -57685,16 +59454,49 @@ function hostEvents(value) {
|
|
|
57685
59454
|
};
|
|
57686
59455
|
} });
|
|
57687
59456
|
}
|
|
57688
|
-
/** Checks a load's input. A format the host did not offer is refused like an unknown one
|
|
59457
|
+
/** Checks a load's input. A format the host did not offer is refused like an unknown one, and an
|
|
59458
|
+
* RTF document is checked and put in canonical form. */
|
|
57689
59459
|
function documentInput(value, offered) {
|
|
57690
59460
|
const input = expectRecord(value, "input");
|
|
59461
|
+
const documentId = expectString(input.documentId, "input.documentId");
|
|
59462
|
+
const generation = expectGeneration(input.generation, "input.generation");
|
|
59463
|
+
const format = expectOneOf(input.format, offered, "input.format");
|
|
59464
|
+
if (format === "rtf") {
|
|
59465
|
+
if (!isOmitted(input.text)) throw invalidArgument("input.text must be left out for an RTF document");
|
|
59466
|
+
return {
|
|
59467
|
+
documentId,
|
|
59468
|
+
generation,
|
|
59469
|
+
format,
|
|
59470
|
+
text: richTextText(readRichTextDocument(input.document, "input.document"))
|
|
59471
|
+
};
|
|
59472
|
+
}
|
|
59473
|
+
if (!isOmitted(input.document)) throw invalidArgument(`input.document must be left out for ${format}`);
|
|
57691
59474
|
return {
|
|
57692
|
-
documentId
|
|
57693
|
-
generation
|
|
57694
|
-
format
|
|
59475
|
+
documentId,
|
|
59476
|
+
generation,
|
|
59477
|
+
format,
|
|
57695
59478
|
text: expectString(input.text, "input.text")
|
|
57696
59479
|
};
|
|
57697
59480
|
}
|
|
59481
|
+
/** The public snapshot of the editor's text for a document. */
|
|
59482
|
+
function publicSnapshot(saved) {
|
|
59483
|
+
const { documentId, generation, revision, format, text, dirty } = saved;
|
|
59484
|
+
return format === "rtf" ? {
|
|
59485
|
+
documentId,
|
|
59486
|
+
generation,
|
|
59487
|
+
revision,
|
|
59488
|
+
format,
|
|
59489
|
+
document: richTextFromText(text),
|
|
59490
|
+
dirty
|
|
59491
|
+
} : {
|
|
59492
|
+
documentId,
|
|
59493
|
+
generation,
|
|
59494
|
+
revision,
|
|
59495
|
+
format,
|
|
59496
|
+
text,
|
|
59497
|
+
dirty
|
|
59498
|
+
};
|
|
59499
|
+
}
|
|
57698
59500
|
/** A range's shape. Whether its offsets fit the snapshot is `invalid-range`, checked later. */
|
|
57699
59501
|
function textRange(value, name) {
|
|
57700
59502
|
const range = expectRecord(value, name);
|
|
@@ -57818,6 +59620,7 @@ var InkKitEditor = class InkKitEditor {
|
|
|
57818
59620
|
output = new EditorOutput({
|
|
57819
59621
|
ctx: () => this.editor.ctx,
|
|
57820
59622
|
exportDocument: (text) => this.document.exportDocument(text),
|
|
59623
|
+
richTextMarkdown: (content) => richContentMarkdown(this.editor.ctx, content),
|
|
57821
59624
|
diagrams: () => this.diagrams,
|
|
57822
59625
|
root: () => this.root,
|
|
57823
59626
|
handler: () => this.document.handler,
|
|
@@ -57837,7 +59640,7 @@ var InkKitEditor = class InkKitEditor {
|
|
|
57837
59640
|
element: () => this.source.element,
|
|
57838
59641
|
replaceSelection: (text) => this.source.replaceLiteralSelection(text),
|
|
57839
59642
|
writable: () => this.writable,
|
|
57840
|
-
snapshot: () => this.
|
|
59643
|
+
snapshot: () => this.savedText(),
|
|
57841
59644
|
prepareSelection: () => this.clipboardSnapshot("selection"),
|
|
57842
59645
|
capture: () => this.session.capture(),
|
|
57843
59646
|
assertCut: (captured) => this.assertCut(captured),
|
|
@@ -57917,7 +59720,7 @@ var InkKitEditor = class InkKitEditor {
|
|
|
57917
59720
|
};
|
|
57918
59721
|
} });
|
|
57919
59722
|
this.linkSchemes = new Set(options.linkSchemes);
|
|
57920
|
-
this.offered = options.formats ??
|
|
59723
|
+
this.offered = options.formats ?? defaultFormats;
|
|
57921
59724
|
this.formatList = Object.freeze(this.offered.map((format) => Object.freeze({
|
|
57922
59725
|
format,
|
|
57923
59726
|
promise: formatHandlers[format].promise
|
|
@@ -57934,6 +59737,9 @@ var InkKitEditor = class InkKitEditor {
|
|
|
57934
59737
|
}
|
|
57935
59738
|
/** What `changed` reports for the document's new text. */
|
|
57936
59739
|
changeSnapshot(text) {
|
|
59740
|
+
return publicSnapshot(this.saved(text));
|
|
59741
|
+
}
|
|
59742
|
+
saved(text) {
|
|
57937
59743
|
return {
|
|
57938
59744
|
...this.session.version(),
|
|
57939
59745
|
format: this.document.format,
|
|
@@ -57941,6 +59747,11 @@ var InkKitEditor = class InkKitEditor {
|
|
|
57941
59747
|
dirty: text !== this.document.original
|
|
57942
59748
|
};
|
|
57943
59749
|
}
|
|
59750
|
+
/** The current text, as `snapshot` checks and reads it. */
|
|
59751
|
+
savedText(expectedGeneration) {
|
|
59752
|
+
this.assertCurrent(expectedGeneration);
|
|
59753
|
+
return this.saved(this.document.currentText());
|
|
59754
|
+
}
|
|
57944
59755
|
/** Throws unless the document `captured` describes is still current, alive, not composing and
|
|
57945
59756
|
* has no import pending. */
|
|
57946
59757
|
assertCaptured(captured, aborted = false) {
|
|
@@ -58146,7 +59957,7 @@ var InkKitEditor = class InkKitEditor {
|
|
|
58146
59957
|
this.publishCommandState();
|
|
58147
59958
|
}
|
|
58148
59959
|
applyInputPolicy() {
|
|
58149
|
-
applyInputPolicy(this.editor.ctx.get(editorViewCtx), this.source.element, this.inputPreferences, this.options.labels?.
|
|
59960
|
+
applyInputPolicy(this.editor.ctx.get(editorViewCtx), this.source.element, this.inputPreferences, this.options.labels?.[this.document.handler.formattedLabel] ?? defaultLabels[this.document.handler.formattedLabel], {
|
|
58150
59961
|
writable: () => this.writable,
|
|
58151
59962
|
literal: () => this.literalSurface
|
|
58152
59963
|
});
|
|
@@ -58163,8 +59974,16 @@ var InkKitEditor = class InkKitEditor {
|
|
|
58163
59974
|
const { state } = this.editor.ctx.get(editorViewCtx);
|
|
58164
59975
|
const mutable = this.writable && !this.composing && !this.pasteController.pending;
|
|
58165
59976
|
const formatted = mutable && !this.literalSurface;
|
|
58166
|
-
const
|
|
59977
|
+
const rich = this.document.handler.richText != null;
|
|
59978
|
+
const format = formatAvailability(this.editor, state, formatted, this.document.handler);
|
|
58167
59979
|
const tables = formatted ? tableAvailability(this.editor.ctx) : Object.fromEntries(tableCommands.map((name) => [name, false]));
|
|
59980
|
+
if (rich) tables.alignLeft = tables.alignCenter = tables.alignRight = false;
|
|
59981
|
+
const caret = this.literalSurface ? {
|
|
59982
|
+
marks: [],
|
|
59983
|
+
block: { type: "paragraph" },
|
|
59984
|
+
quoted: false
|
|
59985
|
+
} : caretState(state);
|
|
59986
|
+
if (rich) caret.align = caretAlignment(state);
|
|
58168
59987
|
return {
|
|
58169
59988
|
...this.session.version(),
|
|
58170
59989
|
format: this.document.format,
|
|
@@ -58172,11 +59991,7 @@ var InkKitEditor = class InkKitEditor {
|
|
|
58172
59991
|
editable: this.writable,
|
|
58173
59992
|
composing: this.composing,
|
|
58174
59993
|
pending: this.pasteController.pending,
|
|
58175
|
-
caret
|
|
58176
|
-
marks: [],
|
|
58177
|
-
block: { type: "paragraph" },
|
|
58178
|
-
quoted: false
|
|
58179
|
-
} : caretState(state),
|
|
59994
|
+
caret,
|
|
58180
59995
|
table: this.literalSurface ? void 0 : tableContext(state),
|
|
58181
59996
|
commands: {
|
|
58182
59997
|
undo: mutable && this.history.canUndo(state),
|
|
@@ -58184,12 +59999,12 @@ var InkKitEditor = class InkKitEditor {
|
|
|
58184
59999
|
insertText: mutable,
|
|
58185
60000
|
paste: mutable,
|
|
58186
60001
|
replace: mutable,
|
|
58187
|
-
replaceSource: mutable,
|
|
58188
|
-
insertImages: formatted && this.canInsertImages,
|
|
58189
|
-
appendImages: formatted && this.canInsertImages,
|
|
60002
|
+
replaceSource: mutable && !rich,
|
|
60003
|
+
insertImages: formatted && !rich && this.canInsertImages,
|
|
60004
|
+
appendImages: formatted && !rich && this.canInsertImages,
|
|
58190
60005
|
insertPaths: mutable,
|
|
58191
|
-
insertFootnote: formatted,
|
|
58192
|
-
editReferenceDefinition: formatted && referenceDefinitions(state.doc).size > 0,
|
|
60006
|
+
insertFootnote: formatted && !rich,
|
|
60007
|
+
editReferenceDefinition: formatted && !rich && referenceDefinitions(state.doc).size > 0,
|
|
58193
60008
|
format,
|
|
58194
60009
|
table: tables,
|
|
58195
60010
|
moveBlockUp: formatted && canMoveBlock(state, "up"),
|
|
@@ -58197,9 +60012,10 @@ var InkKitEditor = class InkKitEditor {
|
|
|
58197
60012
|
}
|
|
58198
60013
|
};
|
|
58199
60014
|
}
|
|
58200
|
-
/** An image adapter shows and exports an inserted reference, and so does a file adapter.
|
|
60015
|
+
/** An image adapter shows and exports an inserted reference, and so does a file adapter. An RTF
|
|
60016
|
+
* document holds no images. */
|
|
58201
60017
|
get canInsertImages() {
|
|
58202
|
-
return !!this.options.images || !!this.options.files;
|
|
60018
|
+
return (!!this.options.images || !!this.options.files) && !this.document.handler.richText;
|
|
58203
60019
|
}
|
|
58204
60020
|
historyInputPlugin = createHistoryInputPlugin({
|
|
58205
60021
|
writable: () => this.writable,
|
|
@@ -58238,7 +60054,7 @@ var InkKitEditor = class InkKitEditor {
|
|
|
58238
60054
|
this.history.settleSource();
|
|
58239
60055
|
this.history.modeChanged();
|
|
58240
60056
|
},
|
|
58241
|
-
text: () => this.
|
|
60057
|
+
text: () => this.savedText().text,
|
|
58242
60058
|
changed: () => {
|
|
58243
60059
|
this.mode = mode;
|
|
58244
60060
|
this.session.modeChanged();
|
|
@@ -58270,6 +60086,7 @@ var InkKitEditor = class InkKitEditor {
|
|
|
58270
60086
|
this.assertAlive();
|
|
58271
60087
|
expectString(text, "text");
|
|
58272
60088
|
this.assertMutation(expectedGeneration);
|
|
60089
|
+
if (this.document.handler.richText) throw invalidArgument("An RTF document has no source to replace");
|
|
58273
60090
|
const changed = this.history.applySource(text, true);
|
|
58274
60091
|
if (changed) this.clipboard.clear();
|
|
58275
60092
|
return changed;
|
|
@@ -58284,22 +60101,52 @@ var InkKitEditor = class InkKitEditor {
|
|
|
58284
60101
|
this.assertMutation(expectedGeneration);
|
|
58285
60102
|
return this.history.redo();
|
|
58286
60103
|
}
|
|
58287
|
-
/** The complete current
|
|
58288
|
-
*
|
|
60104
|
+
/** The complete current document, which is what a host saves. It throws rather than return a
|
|
60105
|
+
* partial document. */
|
|
58289
60106
|
snapshot(expectedGeneration) {
|
|
58290
|
-
this.
|
|
58291
|
-
|
|
58292
|
-
|
|
58293
|
-
|
|
58294
|
-
|
|
58295
|
-
|
|
58296
|
-
|
|
60107
|
+
return publicSnapshot(this.savedText(expectedGeneration));
|
|
60108
|
+
}
|
|
60109
|
+
/**
|
|
60110
|
+
* The loaded document converted to `format`, which must be offered, with what the conversion
|
|
60111
|
+
* lost. It changes nothing: load the result as a new generation. Markdown converts to RTF and
|
|
60112
|
+
* RTF to Markdown; the same format returns a copy. Throws `invalid-argument` for any other pair.
|
|
60113
|
+
*/
|
|
60114
|
+
convertDocument(format, expectedGeneration) {
|
|
60115
|
+
this.assertAlive();
|
|
60116
|
+
expectOneOf(format, this.offered, "format");
|
|
60117
|
+
const saved = this.savedText(expectedGeneration);
|
|
60118
|
+
const from = saved.format;
|
|
60119
|
+
if (from === format) return from === "rtf" ? {
|
|
60120
|
+
format: "rtf",
|
|
60121
|
+
document: richTextFromText(saved.text),
|
|
60122
|
+
lost: Object.freeze([])
|
|
60123
|
+
} : {
|
|
60124
|
+
format: from,
|
|
60125
|
+
text: saved.text,
|
|
60126
|
+
lost: Object.freeze([])
|
|
58297
60127
|
};
|
|
60128
|
+
if (from === "md" && format === "rtf") {
|
|
60129
|
+
const { document, lost } = markdownToRichText(this.document.exportDocument(saved.text));
|
|
60130
|
+
return {
|
|
60131
|
+
format,
|
|
60132
|
+
document,
|
|
60133
|
+
lost
|
|
60134
|
+
};
|
|
60135
|
+
}
|
|
60136
|
+
if (from === "rtf" && format === "md") {
|
|
60137
|
+
const { text, lost } = richTextToMarkdown(this.editor.ctx, richTextFromText(saved.text));
|
|
60138
|
+
return {
|
|
60139
|
+
format,
|
|
60140
|
+
text,
|
|
60141
|
+
lost
|
|
60142
|
+
};
|
|
60143
|
+
}
|
|
60144
|
+
throw invalidArgument(`Inkkit does not convert ${from} to ${format}; ${from === "txt" || format === "txt" ? "the host converts plain text" : "convert through another format"}`);
|
|
58298
60145
|
}
|
|
58299
60146
|
/** The whole document as standalone HTML for printing. Rejects rather than return partial
|
|
58300
60147
|
* output. */
|
|
58301
60148
|
async printableSnapshot(expectedGeneration) {
|
|
58302
|
-
const snapshot = this.
|
|
60149
|
+
const snapshot = this.savedText(expectedGeneration);
|
|
58303
60150
|
const captured = this.session.capture();
|
|
58304
60151
|
return this.output.printable(snapshot, captured);
|
|
58305
60152
|
}
|
|
@@ -58308,7 +60155,7 @@ var InkKitEditor = class InkKitEditor {
|
|
|
58308
60155
|
async clipboardSnapshot(scope, expectedGeneration) {
|
|
58309
60156
|
this.assertAlive();
|
|
58310
60157
|
const all = (isOmitted(scope) ? "document" : expectOneOf(scope, clipboardScopes, "scope")) === "document";
|
|
58311
|
-
const snapshot = this.
|
|
60158
|
+
const snapshot = this.savedText(expectedGeneration);
|
|
58312
60159
|
const captured = this.session.capture();
|
|
58313
60160
|
return this.output.clipboard(snapshot, captured, all);
|
|
58314
60161
|
}
|
|
@@ -58342,12 +60189,25 @@ var InkKitEditor = class InkKitEditor {
|
|
|
58342
60189
|
expectString(text, "text");
|
|
58343
60190
|
this.assertMutation(expectedGeneration);
|
|
58344
60191
|
const before = this.session.capture();
|
|
60192
|
+
const doc = this.editor.ctx.get(editorViewCtx).state.doc;
|
|
58345
60193
|
this.insertPlainText(text);
|
|
60194
|
+
if (text && this.refusedRichEdit(doc)) throw new InkKitError("preservation", "The text was not pasted because the RTF document cannot hold it there");
|
|
58346
60195
|
return !before.isCurrent();
|
|
58347
60196
|
}
|
|
60197
|
+
/** Whether an RTF edit that should have changed `before` was refused. */
|
|
60198
|
+
refusedRichEdit(before) {
|
|
60199
|
+
return this.document.handler.richText != null && !this.literalSurface && this.editor.ctx.get(editorViewCtx).state.doc === before;
|
|
60200
|
+
}
|
|
58348
60201
|
insertPlainText(text) {
|
|
58349
60202
|
if (this.literalSurface) this.source.replaceLiteralSelection(text, true, this.document.handler.keepsInsertedLineEndings);
|
|
58350
|
-
else
|
|
60203
|
+
else if (this.document.handler.richText) {
|
|
60204
|
+
const view = this.editor.ctx.get(editorViewCtx);
|
|
60205
|
+
const slice = this.document.handler.richText.paste({ text }, view.state, this.linkSchemes);
|
|
60206
|
+
const lines = /[\r\n\u000c\u0085\u2028\u2029\u000b]/.test(text);
|
|
60207
|
+
const tr = view.state.tr.replaceSelection(slice).scrollIntoView();
|
|
60208
|
+
view.dispatch(lines ? closeHistory(tr) : tr);
|
|
60209
|
+
if (lines) view.dispatch(closeHistory(view.state.tr).setMeta("addToHistory", false));
|
|
60210
|
+
} else insertPlainText(this.editor.ctx.get(editorViewCtx), text);
|
|
58351
60211
|
}
|
|
58352
60212
|
/** Runs a table command and returns whether it applied. False in source and TXT modes, outside a
|
|
58353
60213
|
* table for commands that need one, and at a boundary a move can't cross. */
|
|
@@ -58421,15 +60281,13 @@ var InkKitEditor = class InkKitEditor {
|
|
|
58421
60281
|
}
|
|
58422
60282
|
} catch (error) {
|
|
58423
60283
|
view.updateState(view.state);
|
|
58424
|
-
this.refuse(new InkKitError("preservation", `The edit was not applied because its Markdown could not be preserved: ${error instanceof Error ? error.message : String(error)}`, { cause: error }));
|
|
60284
|
+
this.refuse(new InkKitError("preservation", this.document.handler.richText ? `The edit was not applied because the document could not be saved as RTF: ${error instanceof Error ? error.message : String(error)}` : `The edit was not applied because its Markdown could not be preserved: ${error instanceof Error ? error.message : String(error)}`, { cause: error }));
|
|
58425
60285
|
return;
|
|
58426
60286
|
}
|
|
58427
60287
|
}
|
|
58428
60288
|
this.history.didApply(view.state, next);
|
|
58429
60289
|
view.updateState(next);
|
|
58430
60290
|
}
|
|
58431
|
-
/** Builds the editor in `root`. It has no document until `loadDocument`. Rejects with
|
|
58432
|
-
* `invalid-argument` for a bad root, handler or option. */
|
|
58433
60291
|
static async mount(root, events, options) {
|
|
58434
60292
|
if (!(root instanceof HTMLElement)) throw invalidArgument("root must be an HTML element");
|
|
58435
60293
|
events = hostEvents(events);
|
|
@@ -58448,7 +60306,9 @@ var InkKitEditor = class InkKitEditor {
|
|
|
58448
60306
|
commit: (apply) => instance.facadeCall(apply),
|
|
58449
60307
|
literalText: () => instance.literalSurface,
|
|
58450
60308
|
editable: () => instance.writable && !instance.literalSurface,
|
|
58451
|
-
onPendingChanged: () => instance.publishCommandState()
|
|
60309
|
+
onPendingChanged: () => instance.publishCommandState(),
|
|
60310
|
+
richText: () => instance.document.handler.richText,
|
|
60311
|
+
linkSchemes: instance.linkSchemes
|
|
58452
60312
|
});
|
|
58453
60313
|
instance.suggestions = createEditorSuggestions({
|
|
58454
60314
|
root,
|
|
@@ -58476,6 +60336,10 @@ var InkKitEditor = class InkKitEditor {
|
|
|
58476
60336
|
historyInput: instance.historyInputPlugin,
|
|
58477
60337
|
policy: instance.policyPlugin
|
|
58478
60338
|
},
|
|
60339
|
+
format: {
|
|
60340
|
+
markdownInput: () => instance.document.handler.markdownInput,
|
|
60341
|
+
richText: () => instance.document.handler.richText
|
|
60342
|
+
},
|
|
58479
60343
|
authoredSources: (doc, positions) => instance.document.authoredSources(doc, positions),
|
|
58480
60344
|
smartPunctuation: () => instance.inputPreferences.smartPunctuation === true,
|
|
58481
60345
|
linkSchemes: instance.linkSchemes,
|
|
@@ -58537,9 +60401,9 @@ var InkKitEditor = class InkKitEditor {
|
|
|
58537
60401
|
}
|
|
58538
60402
|
/** Replaces the document. The generation must be greater than the loaded one. A failed load
|
|
58539
60403
|
* changes nothing. */
|
|
58540
|
-
loadDocument(
|
|
60404
|
+
loadDocument(document) {
|
|
58541
60405
|
this.assertAlive();
|
|
58542
|
-
input = documentInput(
|
|
60406
|
+
const input = documentInput(document, this.offered);
|
|
58543
60407
|
this.session.checkLoad(input.generation, "input.generation");
|
|
58544
60408
|
const handler = formatHandlers[input.format];
|
|
58545
60409
|
const markdown = handler.literalOnly ? "" : input.text;
|
|
@@ -58547,7 +60411,7 @@ var InkKitEditor = class InkKitEditor {
|
|
|
58547
60411
|
try {
|
|
58548
60412
|
source = handler.load(this.editor.ctx, markdown);
|
|
58549
60413
|
} catch (error) {
|
|
58550
|
-
throw
|
|
60414
|
+
throw coded(error, "preservation", "The document was not loaded because its Markdown could not be shown without losing part of it");
|
|
58551
60415
|
}
|
|
58552
60416
|
const container = this.geometry.container;
|
|
58553
60417
|
const top = container.scrollTop, left = container.scrollLeft;
|
|
@@ -58564,7 +60428,7 @@ var InkKitEditor = class InkKitEditor {
|
|
|
58564
60428
|
this.source.reset();
|
|
58565
60429
|
this.search.reset();
|
|
58566
60430
|
this.document.handler = handler;
|
|
58567
|
-
this.source.setSourceValue(input.text);
|
|
60431
|
+
this.source.setSourceValue(handler.richText ? "" : input.text);
|
|
58568
60432
|
this.source.element.setSelectionRange(this.source.element.value.length, this.source.element.value.length);
|
|
58569
60433
|
this.load(markdown, source);
|
|
58570
60434
|
this.document.original = input.text;
|
|
@@ -58583,9 +60447,9 @@ var InkKitEditor = class InkKitEditor {
|
|
|
58583
60447
|
/** Replaces the document with a newer version of it, keeping the selection, focus, scroll
|
|
58584
60448
|
* position and Markdown editing mode. With `expectedGeneration`, the loaded document must be
|
|
58585
60449
|
* that generation. */
|
|
58586
|
-
reloadDocument(
|
|
60450
|
+
reloadDocument(document, expectedGeneration) {
|
|
58587
60451
|
this.assertAlive();
|
|
58588
|
-
input = documentInput(
|
|
60452
|
+
const input = documentInput(document, this.offered);
|
|
58589
60453
|
if (!isOmitted(expectedGeneration)) this.assertReady(expectedGeneration);
|
|
58590
60454
|
this.session.checkLoad(input.generation, "input.generation");
|
|
58591
60455
|
const view = this.editor.ctx.get(editorViewCtx);
|
|
@@ -58595,7 +60459,7 @@ var InkKitEditor = class InkKitEditor {
|
|
|
58595
60459
|
const saved = captureReloadSurface(view, this.source.element, this.geometry.container, wasLiteral);
|
|
58596
60460
|
this.reportingSuppressed = true;
|
|
58597
60461
|
try {
|
|
58598
|
-
this.loadDocument(
|
|
60462
|
+
this.loadDocument(document);
|
|
58599
60463
|
if (previous.sourceMode && this.document.handler.sourceMode && previousMode === "source") this.setEditingMode("source", input.generation);
|
|
58600
60464
|
restoreReloadSurface(view, this.source.element, () => this.literalSurface, saved);
|
|
58601
60465
|
} finally {
|
|
@@ -58730,7 +60594,8 @@ var InkKitEditor = class InkKitEditor {
|
|
|
58730
60594
|
expectString(search, "search");
|
|
58731
60595
|
expectString(replacement, "replacement");
|
|
58732
60596
|
this.assertMutation(expectedGeneration);
|
|
58733
|
-
|
|
60597
|
+
const doc = this.editor.ctx.get(editorViewCtx).state.doc;
|
|
60598
|
+
return this.search.replace(search, replacement, false) > 0 && (replacement === search || !this.refusedRichEdit(doc));
|
|
58734
60599
|
}
|
|
58735
60600
|
/** Replaces every non-overlapping match once and returns how many. */
|
|
58736
60601
|
replaceAll(search, replacement, expectedGeneration) {
|
|
@@ -58738,7 +60603,9 @@ var InkKitEditor = class InkKitEditor {
|
|
|
58738
60603
|
expectString(search, "search");
|
|
58739
60604
|
expectString(replacement, "replacement");
|
|
58740
60605
|
this.assertMutation(expectedGeneration);
|
|
58741
|
-
|
|
60606
|
+
const doc = this.editor.ctx.get(editorViewCtx).state.doc;
|
|
60607
|
+
const count = this.search.replace(search, replacement, true);
|
|
60608
|
+
return count && replacement !== search && this.refusedRichEdit(doc) ? 0 : count;
|
|
58742
60609
|
}
|
|
58743
60610
|
outlineContext() {
|
|
58744
60611
|
const { documentId, generation, revision, key } = this.session.capture();
|
|
@@ -58781,11 +60648,10 @@ var InkKitEditor = class InkKitEditor {
|
|
|
58781
60648
|
this.source.replaceLiteralSelection(text, false);
|
|
58782
60649
|
return true;
|
|
58783
60650
|
}
|
|
58784
|
-
|
|
58785
|
-
|
|
58786
|
-
|
|
58787
|
-
|
|
58788
|
-
return insertNativeText(this.editor.ctx.get(editorViewCtx), text);
|
|
60651
|
+
const doc = this.editor.ctx.get(editorViewCtx).state.doc;
|
|
60652
|
+
if (/[\r\n]/.test(text)) this.insertPlainText(text);
|
|
60653
|
+
else if (!insertNativeText(this.editor.ctx.get(editorViewCtx), text)) return false;
|
|
60654
|
+
return !(text && this.refusedRichEdit(doc));
|
|
58789
60655
|
}
|
|
58790
60656
|
/**
|
|
58791
60657
|
* A key the host's native input received, run through InkKit's key handling. Returns whether
|
|
@@ -58964,12 +60830,14 @@ var InkKitEditor = class InkKitEditor {
|
|
|
58964
60830
|
format(command, options, expectedGeneration) {
|
|
58965
60831
|
this.assertAlive();
|
|
58966
60832
|
const checked = formatOptions(command, options);
|
|
60833
|
+
if (checked?.destination !== void 0 && this.document.handler.richText) checked.destination = checked.destination.trim();
|
|
58967
60834
|
if (checked?.destination !== void 0) this.checkDestination(checked.destination, "options.destination");
|
|
58968
60835
|
this.assertMutation(expectedGeneration);
|
|
58969
60836
|
if (this.literalSurface) return false;
|
|
58970
60837
|
const view = this.editor.ctx.get(editorViewCtx);
|
|
58971
60838
|
const before = view.state;
|
|
58972
|
-
|
|
60839
|
+
if ((this.document.handler.richText || command.startsWith("align")) && !this.commandState().commands.format[command]) return false;
|
|
60840
|
+
applyFormat(this.editor, command, checked, (destination) => this.link(destination), this.document.handler.richText != null);
|
|
58973
60841
|
this.focus();
|
|
58974
60842
|
const after = view.state;
|
|
58975
60843
|
const marks = (state) => state.storedMarks ?? state.selection.$from.marks();
|
|
@@ -58977,11 +60845,14 @@ var InkKitEditor = class InkKitEditor {
|
|
|
58977
60845
|
}
|
|
58978
60846
|
/**
|
|
58979
60847
|
* Refuses a link destination that is empty, that opens anything but a web, mail or phone address
|
|
58980
|
-
* or a scheme the host listed, or that the
|
|
58981
|
-
* same.
|
|
60848
|
+
* or a scheme the host listed, or, in Markdown, that the writer cannot save so that it reads back
|
|
60849
|
+
* the same. In RTF, it also refuses what the rich text link rule refuses.
|
|
58982
60850
|
*/
|
|
58983
60851
|
checkDestination(destination, name) {
|
|
58984
|
-
|
|
60852
|
+
const rich = this.document.handler.richText != null;
|
|
60853
|
+
checkDestination(this.editor.ctx, destination, name, this.linkSchemes, !rich);
|
|
60854
|
+
const problem = rich ? linkProblem(destination) : void 0;
|
|
60855
|
+
if (problem) throw invalidArgument(`${name}: ${problem}`);
|
|
58985
60856
|
}
|
|
58986
60857
|
/**
|
|
58987
60858
|
* With a destination, sets the link: on the whole link that holds the caret or the selection,
|
|
@@ -59000,7 +60871,7 @@ var InkKitEditor = class InkKitEditor {
|
|
|
59000
60871
|
this.checkDestination(expectString(destination, "destination"), "destination");
|
|
59001
60872
|
optionalString(title, "title");
|
|
59002
60873
|
this.assertMutation(expectedGeneration);
|
|
59003
|
-
if (this.literalSurface) return false;
|
|
60874
|
+
if (this.literalSurface || this.document.handler.richText) return false;
|
|
59004
60875
|
return editReferenceDefinition(this.editor.ctx.get(editorViewCtx), label, destination, title);
|
|
59005
60876
|
}
|
|
59006
60877
|
/** Inserts a footnote reference, and a definition when the label has none, and returns whether
|
|
@@ -59010,8 +60881,8 @@ var InkKitEditor = class InkKitEditor {
|
|
|
59010
60881
|
const given = optionalString(label, "label");
|
|
59011
60882
|
if (given !== void 0 && !footnoteLabel(given)) throw invalidArgument("A footnote label must be 1 to 999 characters without whitespace, brackets or backslashes");
|
|
59012
60883
|
this.assertMutation(expectedGeneration);
|
|
59013
|
-
if (this.literalSurface) return false;
|
|
59014
|
-
insertFootnote(this.editor.ctx.get(editorViewCtx), label, () => this.
|
|
60884
|
+
if (this.literalSurface || this.document.handler.richText) return false;
|
|
60885
|
+
insertFootnote(this.editor.ctx.get(editorViewCtx), label, () => this.savedText().text);
|
|
59015
60886
|
this.focus();
|
|
59016
60887
|
return true;
|
|
59017
60888
|
}
|
|
@@ -59031,6 +60902,7 @@ var InkKitEditor = class InkKitEditor {
|
|
|
59031
60902
|
"loadDocument",
|
|
59032
60903
|
"reloadDocument",
|
|
59033
60904
|
"snapshot",
|
|
60905
|
+
"convertDocument",
|
|
59034
60906
|
"printableSnapshot",
|
|
59035
60907
|
"clipboardSnapshot",
|
|
59036
60908
|
"paste",
|
|
@@ -59094,13 +60966,13 @@ var InkKitEditor = class InkKitEditor {
|
|
|
59094
60966
|
});
|
|
59095
60967
|
}
|
|
59096
60968
|
const mount = InkKitEditor.mount;
|
|
59097
|
-
InkKitEditor.mount = async (...args) => {
|
|
60969
|
+
InkKitEditor.mount = (async (...args) => {
|
|
59098
60970
|
try {
|
|
59099
60971
|
return await mount(...args);
|
|
59100
60972
|
} catch (error) {
|
|
59101
60973
|
throw coded(error);
|
|
59102
60974
|
}
|
|
59103
|
-
};
|
|
60975
|
+
});
|
|
59104
60976
|
}
|
|
59105
60977
|
};
|
|
59106
60978
|
//#endregion
|