@aicayzer/inkkit 0.0.18 → 0.1.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/NOTICE +4 -2
- package/README.md +11 -7
- package/dist/THIRD-PARTY-NOTICES.txt +2742 -0
- package/dist/chunks/core-vtCr4MBS.js +56503 -0
- package/dist/chunks/highlight-Daj2fv_u.js +14122 -0
- package/dist/core.d.ts +2 -5
- package/dist/core.js +2 -2
- package/dist/editor.d.ts +229 -47
- package/dist/index.js +5 -3
- package/dist/style.css +231 -202
- package/dist/types.d.ts +143 -18
- package/package.json +16 -13
- package/dist/autolink.d.ts +0 -1
- package/dist/autolink.js +0 -18
- package/dist/callouts.d.ts +0 -25
- package/dist/callouts.js +0 -288
- package/dist/clipboard.d.ts +0 -11
- package/dist/clipboard.js +0 -261
- package/dist/code-fences.d.ts +0 -8
- package/dist/code-fences.js +0 -171
- package/dist/comments.d.ts +0 -76
- package/dist/comments.js +0 -545
- package/dist/cursor.d.ts +0 -2
- package/dist/cursor.js +0 -40
- package/dist/decorations.d.ts +0 -2
- package/dist/decorations.js +0 -67
- package/dist/dialect.d.ts +0 -9
- package/dist/dialect.js +0 -446
- package/dist/drop-placement.d.ts +0 -19
- package/dist/drop-placement.js +0 -47
- package/dist/editor.js +0 -2673
- package/dist/files.d.ts +0 -40
- package/dist/files.js +0 -133
- package/dist/highlight.d.ts +0 -1
- package/dist/highlight.js +0 -136
- package/dist/html-import.d.ts +0 -13
- package/dist/html-import.js +0 -477
- package/dist/image-blocks.d.ts +0 -86
- package/dist/image-blocks.js +0 -329
- package/dist/images.d.ts +0 -51
- package/dist/images.js +0 -398
- package/dist/inline-highlight.d.ts +0 -6
- package/dist/inline-highlight.js +0 -112
- package/dist/labels.d.ts +0 -36
- package/dist/labels.js +0 -34
- package/dist/link-safety.d.ts +0 -17
- package/dist/link-safety.js +0 -29
- package/dist/linked-syntax.d.ts +0 -48
- package/dist/linked-syntax.js +0 -320
- package/dist/literals.d.ts +0 -20
- package/dist/literals.js +0 -216
- package/dist/mermaid.d.ts +0 -28
- package/dist/mermaid.js +0 -404
- package/dist/outline.d.ts +0 -22
- package/dist/outline.js +0 -79
- package/dist/paste-html.d.ts +0 -31
- package/dist/paste-html.js +0 -462
- package/dist/paste.d.ts +0 -33
- package/dist/paste.js +0 -591
- package/dist/portable-files.d.ts +0 -10
- package/dist/portable-files.js +0 -36
- package/dist/preserve.d.ts +0 -38
- package/dist/preserve.js +0 -1396
- package/dist/print.d.ts +0 -7
- package/dist/print.js +0 -406
- package/dist/reference-clipboard.d.ts +0 -8
- package/dist/reference-clipboard.js +0 -201
- package/dist/references.d.ts +0 -16
- package/dist/references.js +0 -418
- package/dist/scroll.d.ts +0 -15
- package/dist/scroll.js +0 -27
- package/dist/search.d.ts +0 -15
- package/dist/search.js +0 -202
- package/dist/selection.d.ts +0 -1
- package/dist/selection.js +0 -59
- package/dist/source.d.ts +0 -16
- package/dist/source.js +0 -57
- package/dist/spreadsheet.d.ts +0 -8
- package/dist/spreadsheet.js +0 -289
- package/dist/table-alignment.d.ts +0 -2
- package/dist/table-alignment.js +0 -46
- package/dist/tables.d.ts +0 -27
- package/dist/tables.js +0 -344
- package/dist/tasks.d.ts +0 -3
- package/dist/tasks.js +0 -51
- package/dist/text-ranges.d.ts +0 -29
- package/dist/text-ranges.js +0 -256
- package/dist/types.js +0 -8
- package/dist/wiki-view.d.ts +0 -21
- package/dist/wiki-view.js +0 -78
package/dist/html-import.js
DELETED
|
@@ -1,477 +0,0 @@
|
|
|
1
|
-
import { editorViewCtx, parserCtx } from '@milkdown/kit/core';
|
|
2
|
-
import { Fragment, Slice, } from '@milkdown/kit/prose/model';
|
|
3
|
-
import { EditorState } from '@milkdown/kit/prose/state';
|
|
4
|
-
import { fromMarkdown } from 'mdast-util-from-markdown';
|
|
5
|
-
import { gfmAutolinkLiteralFromMarkdown } from 'mdast-util-gfm-autolink-literal';
|
|
6
|
-
import { gfmFootnoteFromMarkdown } from 'mdast-util-gfm-footnote';
|
|
7
|
-
import { gfmStrikethroughFromMarkdown } from 'mdast-util-gfm-strikethrough';
|
|
8
|
-
import { gfmTableFromMarkdown } from 'mdast-util-gfm-table';
|
|
9
|
-
import { gfmTaskListItemFromMarkdown } from 'mdast-util-gfm-task-list-item';
|
|
10
|
-
import { gfmAutolinkLiteral } from 'micromark-extension-gfm-autolink-literal';
|
|
11
|
-
import { gfmFootnote } from 'micromark-extension-gfm-footnote';
|
|
12
|
-
import { gfmStrikethrough } from 'micromark-extension-gfm-strikethrough';
|
|
13
|
-
import { gfmTable } from 'micromark-extension-gfm-table';
|
|
14
|
-
import { gfmTaskListItem } from 'micromark-extension-gfm-task-list-item';
|
|
15
|
-
import { commentSource, commentTextEndsEarly } from './comments.js';
|
|
16
|
-
import { imageBlockName } from './image-blocks.js';
|
|
17
|
-
import { serialize } from './dialect.js';
|
|
18
|
-
import { isSafeLinkTarget, isScriptAddress } from './link-safety.js';
|
|
19
|
-
import { semanticSignature } from './preserve.js';
|
|
20
|
-
import { singleLine } from './references.js';
|
|
21
|
-
// HTML can carry values that the Markdown writer saves as they are, so content
|
|
22
|
-
// imported from HTML is kept only where its saved Markdown reads back as the
|
|
23
|
-
// same structure in InkKit, and where no reading of it holds an address that
|
|
24
|
-
// HTML import refuses. InkKit's own syntax, such as wiki links, literal source
|
|
25
|
-
// and comments, is read differently by other apps, so the saved Markdown is
|
|
26
|
-
// also read as GitHub Flavored Markdown, as strict CommonMark, and as
|
|
27
|
-
// CommonMark with raw HTML turned off.
|
|
28
|
-
const standard = () => ({
|
|
29
|
-
extensions: [
|
|
30
|
-
gfmAutolinkLiteral(),
|
|
31
|
-
gfmFootnote(),
|
|
32
|
-
gfmStrikethrough(),
|
|
33
|
-
gfmTable(),
|
|
34
|
-
gfmTaskListItem(),
|
|
35
|
-
],
|
|
36
|
-
mdastExtensions: [
|
|
37
|
-
gfmAutolinkLiteralFromMarkdown(),
|
|
38
|
-
gfmFootnoteFromMarkdown(),
|
|
39
|
-
gfmStrikethroughFromMarkdown(),
|
|
40
|
-
gfmTableFromMarkdown(),
|
|
41
|
-
gfmTaskListItemFromMarkdown(),
|
|
42
|
-
],
|
|
43
|
-
});
|
|
44
|
-
// An app that turns raw HTML off reads the Markdown inside HTML as text.
|
|
45
|
-
const withoutRawHTML = {
|
|
46
|
-
extensions: [{ disable: { null: ['htmlFlow', 'htmlText'] } }],
|
|
47
|
-
};
|
|
48
|
-
function standardTree(markdown) {
|
|
49
|
-
return fromMarkdown(markdown, standard());
|
|
50
|
-
}
|
|
51
|
-
function safeAddresses(markdown) {
|
|
52
|
-
// A scheme needs a colon, written as itself or as a character reference.
|
|
53
|
-
if (!/[:&]/.test(markdown))
|
|
54
|
-
return true;
|
|
55
|
-
const visit = (node) => {
|
|
56
|
-
if ((node.type === 'link' || node.type === 'definition') &&
|
|
57
|
-
!isSafeLinkTarget(node.url))
|
|
58
|
-
return false;
|
|
59
|
-
if (node.type === 'image' && isScriptAddress(node.url))
|
|
60
|
-
return false;
|
|
61
|
-
return !('children' in node) || node.children.every(visit);
|
|
62
|
-
};
|
|
63
|
-
return (visit(standardTree(markdown)) &&
|
|
64
|
-
visit(fromMarkdown(markdown)) &&
|
|
65
|
-
visit(fromMarkdown(markdown, withoutRawHTML)));
|
|
66
|
-
}
|
|
67
|
-
class Check {
|
|
68
|
-
ctx;
|
|
69
|
-
schema;
|
|
70
|
-
constructor(ctx, schema) {
|
|
71
|
-
this.ctx = ctx;
|
|
72
|
-
this.schema = schema;
|
|
73
|
-
}
|
|
74
|
-
base;
|
|
75
|
-
/**
|
|
76
|
-
* The document as the editor holds it once inserted, after the editor's
|
|
77
|
-
* plugins have normalised it, such as a paragraph holding only an image
|
|
78
|
-
* becoming an image block.
|
|
79
|
-
*/
|
|
80
|
-
settled(doc) {
|
|
81
|
-
this.base ??= EditorState.create({
|
|
82
|
-
schema: this.schema,
|
|
83
|
-
plugins: this.ctx.get(editorViewCtx).state.plugins,
|
|
84
|
-
});
|
|
85
|
-
const { state } = this.base.applyTransaction(this.base.tr.replaceWith(0, this.base.doc.content.size, doc.content));
|
|
86
|
-
return state === this.base ? doc : state.doc;
|
|
87
|
-
}
|
|
88
|
-
/** Whether these blocks, saved and read back, give the same structure. */
|
|
89
|
-
readsBack(blocks) {
|
|
90
|
-
return this.reading(blocks) === true;
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* The blocks in a form that reads back as itself: as they are, or as their
|
|
94
|
-
* Markdown reads back when the only difference is a link that the reader
|
|
95
|
-
* makes of text, such as a bare web address.
|
|
96
|
-
*/
|
|
97
|
-
kept(blocks) {
|
|
98
|
-
const reading = this.reading(blocks);
|
|
99
|
-
if (reading === true)
|
|
100
|
-
return [...blocks];
|
|
101
|
-
if (!reading || !reading.autolinks)
|
|
102
|
-
return undefined;
|
|
103
|
-
const children = reading.reread.children;
|
|
104
|
-
return this.readsBack(children) ? [...children] : undefined;
|
|
105
|
-
}
|
|
106
|
-
reading(blocks) {
|
|
107
|
-
try {
|
|
108
|
-
const doc = this.schema.topNodeType.createChecked(null, blocks);
|
|
109
|
-
const markdown = serialize(this.ctx, doc);
|
|
110
|
-
const reread = this.ctx.get(parserCtx)(markdown);
|
|
111
|
-
const read = semanticSignature(reread);
|
|
112
|
-
if (read === semanticSignature(doc) ||
|
|
113
|
-
read === this.settledSignature(doc))
|
|
114
|
-
return safeAddresses(markdown) || undefined;
|
|
115
|
-
return {
|
|
116
|
-
reread,
|
|
117
|
-
autolinks: semanticSignature(withoutAutolinks(reread)) ===
|
|
118
|
-
this.settledSignature(doc),
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
catch {
|
|
122
|
-
return undefined;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
settledSignature(doc) {
|
|
126
|
-
return semanticSignature(this.settled(doc));
|
|
127
|
-
}
|
|
128
|
-
/** Index of the first block that reads back differently in context. */
|
|
129
|
-
firstDifference(blocks) {
|
|
130
|
-
try {
|
|
131
|
-
const doc = this.schema.topNodeType.createChecked(null, blocks);
|
|
132
|
-
const reread = this.ctx.get(parserCtx)(serialize(this.ctx, doc));
|
|
133
|
-
const settled = this.settled(doc);
|
|
134
|
-
const signature = (node) => semanticSignature(this.schema.topNodeType.create(null, node));
|
|
135
|
-
for (let index = 0; index < settled.childCount; index++)
|
|
136
|
-
if (index >= reread.childCount ||
|
|
137
|
-
signature(settled.child(index)) !== signature(reread.child(index)))
|
|
138
|
-
return index;
|
|
139
|
-
}
|
|
140
|
-
catch {
|
|
141
|
-
return 0;
|
|
142
|
-
}
|
|
143
|
-
// The structure matches, so an address is unsafe only in context.
|
|
144
|
-
return blocks.findIndex((block) => !isPlain(block));
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
// An address that GitHub Flavored Markdown reads from text as a link.
|
|
148
|
-
function autolink(href, text) {
|
|
149
|
-
return [text, `http://${text}`, `mailto:${text}`].includes(href);
|
|
150
|
-
}
|
|
151
|
-
function withoutAutolinks(node) {
|
|
152
|
-
if (node.isText)
|
|
153
|
-
return node.mark(node.marks.filter((mark) => mark.type.name !== 'link' ||
|
|
154
|
-
!autolink(String(mark.attrs.href), node.text)));
|
|
155
|
-
if (node.isLeaf)
|
|
156
|
-
return node;
|
|
157
|
-
return node.copy(Fragment.fromArray(node.children.map(withoutAutolinks)));
|
|
158
|
-
}
|
|
159
|
-
function isPlain(node) {
|
|
160
|
-
if (node.type.name !== 'paragraph')
|
|
161
|
-
return false;
|
|
162
|
-
let plain = true;
|
|
163
|
-
node.forEach((child) => {
|
|
164
|
-
if (!child.isText || child.marks.length)
|
|
165
|
-
plain = false;
|
|
166
|
-
});
|
|
167
|
-
return plain;
|
|
168
|
-
}
|
|
169
|
-
function leafText(node) {
|
|
170
|
-
switch (node.type.name) {
|
|
171
|
-
case 'image':
|
|
172
|
-
case imageBlockName:
|
|
173
|
-
return String(node.attrs.alt ?? '');
|
|
174
|
-
case 'footnote_reference':
|
|
175
|
-
return `[${String(node.attrs.label ?? '')}]`;
|
|
176
|
-
case 'inkkit_wiki_link':
|
|
177
|
-
return String(node.attrs.label || node.attrs.target || '');
|
|
178
|
-
case 'hardbreak':
|
|
179
|
-
return '\n';
|
|
180
|
-
case 'html':
|
|
181
|
-
return String(node.attrs.value ?? '');
|
|
182
|
-
default:
|
|
183
|
-
return node.textContent;
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
function text(schema, value, marks) {
|
|
187
|
-
return value ? [schema.text(value, marks)] : [];
|
|
188
|
-
}
|
|
189
|
-
/** The node's text as plain paragraphs, one for each line that has text. */
|
|
190
|
-
function paragraphs(check, node) {
|
|
191
|
-
const { schema } = check;
|
|
192
|
-
const source = node.isLeaf
|
|
193
|
-
? leafText(node)
|
|
194
|
-
: node.textBetween(0, node.content.size, '\n', leafText);
|
|
195
|
-
const result = [];
|
|
196
|
-
for (const line of source.split(/\r\n?|\n/)) {
|
|
197
|
-
const value = line.trim();
|
|
198
|
-
if (!value)
|
|
199
|
-
continue;
|
|
200
|
-
const paragraph = schema.nodes.paragraph.create(null, schema.text(value));
|
|
201
|
-
result.push(...(check.kept([paragraph]) ?? []));
|
|
202
|
-
}
|
|
203
|
-
return result;
|
|
204
|
-
}
|
|
205
|
-
function codeBlock(schema, value) {
|
|
206
|
-
return schema.nodes.code_block.create(null, text(schema, value));
|
|
207
|
-
}
|
|
208
|
-
function literalSource(node) {
|
|
209
|
-
let source = '';
|
|
210
|
-
node.forEach((child) => {
|
|
211
|
-
source += child.isText ? child.text : commentSource(child);
|
|
212
|
-
});
|
|
213
|
-
return source;
|
|
214
|
-
}
|
|
215
|
-
/**
|
|
216
|
-
* Whether other readers take the comment as InkKit does: its text must not end
|
|
217
|
-
* it early, and GitHub Flavored Markdown, which has no `%%` comments, must
|
|
218
|
-
* read the text of one as plain text.
|
|
219
|
-
*/
|
|
220
|
-
function commentHolds(node) {
|
|
221
|
-
const body = node.textContent;
|
|
222
|
-
if (commentTextEndsEarly(String(node.attrs.syntax), body))
|
|
223
|
-
return false;
|
|
224
|
-
if (node.attrs.syntax === 'html')
|
|
225
|
-
return true;
|
|
226
|
-
const source = commentSource(node);
|
|
227
|
-
const { children } = standardTree(source);
|
|
228
|
-
const paragraph = children[0];
|
|
229
|
-
const only = paragraph?.type === 'paragraph' ? paragraph.children : [];
|
|
230
|
-
return (children.length === 1 &&
|
|
231
|
-
only.length === 1 &&
|
|
232
|
-
only[0].type === 'text' &&
|
|
233
|
-
only[0].value === source);
|
|
234
|
-
}
|
|
235
|
-
function plainComment(check, node) {
|
|
236
|
-
const { schema } = check;
|
|
237
|
-
const body = singleLine(node.textContent).trim();
|
|
238
|
-
if (node.isInline)
|
|
239
|
-
return text(schema, body);
|
|
240
|
-
return [schema.nodes.paragraph.create(null, text(schema, body))];
|
|
241
|
-
}
|
|
242
|
-
/**
|
|
243
|
-
* Literal source and comments: InkKit shows their text, so they are kept only
|
|
244
|
-
* where every reading agrees. A literal that does not read back as itself,
|
|
245
|
-
* ending where it was written, becomes a code block with the same text. A
|
|
246
|
-
* `%%` comment whose text other readers would take as Markdown becomes an HTML
|
|
247
|
-
* comment, which they hide, and a comment that still does not hold becomes
|
|
248
|
-
* plain text.
|
|
249
|
-
*/
|
|
250
|
-
function opaque(check, node) {
|
|
251
|
-
const { schema } = check;
|
|
252
|
-
const name = node.type.name;
|
|
253
|
-
// A literal must also end where it was written, so a paragraph follows it.
|
|
254
|
-
// One that holds only whitespace holds nothing.
|
|
255
|
-
if (name === 'literal_markdown')
|
|
256
|
-
return !literalSource(node).trim()
|
|
257
|
-
? []
|
|
258
|
-
: check.readsBack([
|
|
259
|
-
node,
|
|
260
|
-
schema.nodes.paragraph.create(null, schema.text('x')),
|
|
261
|
-
])
|
|
262
|
-
? [node]
|
|
263
|
-
: [codeBlock(schema, literalSource(node))];
|
|
264
|
-
if (name === 'comment_inline' || name === 'comment_block') {
|
|
265
|
-
if (commentHolds(node))
|
|
266
|
-
return [node];
|
|
267
|
-
if (node.attrs.syntax === 'obsidian') {
|
|
268
|
-
const html = node.type.create({ ...node.attrs, syntax: 'html' }, node.content);
|
|
269
|
-
if (commentHolds(html))
|
|
270
|
-
return [html];
|
|
271
|
-
}
|
|
272
|
-
return plainComment(check, node);
|
|
273
|
-
}
|
|
274
|
-
if (node.isLeaf || node.isText)
|
|
275
|
-
return [node];
|
|
276
|
-
const children = [];
|
|
277
|
-
let changed = false;
|
|
278
|
-
node.forEach((child) => {
|
|
279
|
-
const replaced = opaque(check, child);
|
|
280
|
-
if (replaced.length !== 1 || replaced[0] !== child)
|
|
281
|
-
changed = true;
|
|
282
|
-
children.push(...replaced);
|
|
283
|
-
});
|
|
284
|
-
if (!changed)
|
|
285
|
-
return [node];
|
|
286
|
-
const content = Fragment.fromArray(children);
|
|
287
|
-
// A replacement the parent cannot hold turns the parent into text.
|
|
288
|
-
return node.type.validContent(content)
|
|
289
|
-
? [node.copy(content)]
|
|
290
|
-
: paragraphs(check, node);
|
|
291
|
-
}
|
|
292
|
-
function plainMarks(schema, marks) {
|
|
293
|
-
return marks.map((mark) => mark.type === schema.marks.link
|
|
294
|
-
? mark.type.create({ href: mark.attrs.href, title: mark.attrs.title })
|
|
295
|
-
: mark);
|
|
296
|
-
}
|
|
297
|
-
/**
|
|
298
|
-
* The block with InkKit's own metadata removed: inline nodes other than images
|
|
299
|
-
* and line breaks become their text, links lose their reference, callouts
|
|
300
|
-
* become quotes, definitions are dropped and code fences take default markers.
|
|
301
|
-
*/
|
|
302
|
-
function plainForm(check, node) {
|
|
303
|
-
const { schema } = check;
|
|
304
|
-
const name = node.type.name;
|
|
305
|
-
if (node.isText)
|
|
306
|
-
return [schema.text(node.text, plainMarks(schema, node.marks))];
|
|
307
|
-
if (name === 'comment_inline' || name === 'comment_block')
|
|
308
|
-
return plainComment(check, node);
|
|
309
|
-
if (node.isInline) {
|
|
310
|
-
if (name === 'image' || name === 'hardbreak')
|
|
311
|
-
return [node.mark(plainMarks(schema, node.marks))];
|
|
312
|
-
return text(schema, leafText(node), plainMarks(schema, node.marks));
|
|
313
|
-
}
|
|
314
|
-
if (name === 'reference_definition')
|
|
315
|
-
return [];
|
|
316
|
-
if (name === 'literal_markdown')
|
|
317
|
-
return [codeBlock(schema, literalSource(node))];
|
|
318
|
-
if (name === 'code_block')
|
|
319
|
-
return [node.type.create({ language: node.attrs.language }, node.content)];
|
|
320
|
-
const children = [];
|
|
321
|
-
node.forEach((child) => children.push(...plainForm(check, child)));
|
|
322
|
-
if (name === 'footnote_definition')
|
|
323
|
-
return children;
|
|
324
|
-
if (name === 'inkkit_callout') {
|
|
325
|
-
const title = String(node.attrs.title ?? '').trim();
|
|
326
|
-
const quote = schema.nodes.blockquote.createChecked(null, [
|
|
327
|
-
...(title
|
|
328
|
-
? [schema.nodes.paragraph.create(null, schema.text(title))]
|
|
329
|
-
: []),
|
|
330
|
-
...children,
|
|
331
|
-
]);
|
|
332
|
-
return [quote];
|
|
333
|
-
}
|
|
334
|
-
const content = Fragment.fromArray(children);
|
|
335
|
-
return node.type.validContent(content)
|
|
336
|
-
? [node.type.create(node.attrs, content, node.marks)]
|
|
337
|
-
: paragraphs(check, node);
|
|
338
|
-
}
|
|
339
|
-
function repair(check, block) {
|
|
340
|
-
const kept = check.kept([block]);
|
|
341
|
-
if (kept)
|
|
342
|
-
return kept;
|
|
343
|
-
try {
|
|
344
|
-
const plain = check.kept(plainForm(check, block));
|
|
345
|
-
if (plain)
|
|
346
|
-
return plain;
|
|
347
|
-
}
|
|
348
|
-
catch {
|
|
349
|
-
/* A plain form that the schema refuses falls back to text. */
|
|
350
|
-
}
|
|
351
|
-
return paragraphs(check, block);
|
|
352
|
-
}
|
|
353
|
-
function blocks(check, content) {
|
|
354
|
-
const result = [];
|
|
355
|
-
content.forEach((node) => result.push(...opaque(check, node)));
|
|
356
|
-
const kept = check.kept(result);
|
|
357
|
-
if (kept)
|
|
358
|
-
return kept;
|
|
359
|
-
let repaired = result.flatMap((node) => repair(check, node));
|
|
360
|
-
// Blocks that read back alone can still merge with a neighbour. The first
|
|
361
|
-
// block that differs takes its plain form, then becomes text, then goes.
|
|
362
|
-
const simplified = new Set();
|
|
363
|
-
for (let attempt = 0, limit = repaired.length * 3 + 1; attempt < limit && !check.readsBack(repaired); attempt++) {
|
|
364
|
-
const index = Math.max(0, check.firstDifference(repaired));
|
|
365
|
-
const block = repaired[index];
|
|
366
|
-
if (!block)
|
|
367
|
-
return [];
|
|
368
|
-
let replacement;
|
|
369
|
-
if (isPlain(block))
|
|
370
|
-
replacement = [];
|
|
371
|
-
else if (simplified.has(block))
|
|
372
|
-
replacement = paragraphs(check, block);
|
|
373
|
-
else {
|
|
374
|
-
try {
|
|
375
|
-
replacement = plainForm(check, block);
|
|
376
|
-
}
|
|
377
|
-
catch {
|
|
378
|
-
replacement = paragraphs(check, block);
|
|
379
|
-
}
|
|
380
|
-
for (const node of replacement)
|
|
381
|
-
simplified.add(node);
|
|
382
|
-
}
|
|
383
|
-
repaired = [
|
|
384
|
-
...repaired.slice(0, index),
|
|
385
|
-
...replacement,
|
|
386
|
-
...repaired.slice(index + 1),
|
|
387
|
-
];
|
|
388
|
-
}
|
|
389
|
-
return check.kept(repaired) ?? [];
|
|
390
|
-
}
|
|
391
|
-
function openDepth(content, open, start) {
|
|
392
|
-
let depth = 0;
|
|
393
|
-
let node = start ? content.firstChild : content.lastChild;
|
|
394
|
-
// Code and preserved source are never opened, so their text is not joined
|
|
395
|
-
// to the paragraph they are inserted beside.
|
|
396
|
-
while (depth < open &&
|
|
397
|
-
node &&
|
|
398
|
-
node.isBlock &&
|
|
399
|
-
!node.isLeaf &&
|
|
400
|
-
!node.type.spec.code) {
|
|
401
|
-
depth++;
|
|
402
|
-
node = start ? node.firstChild : node.lastChild;
|
|
403
|
-
}
|
|
404
|
-
return depth;
|
|
405
|
-
}
|
|
406
|
-
/**
|
|
407
|
-
* The slice's nodes as blocks a document holds, wrapped as insertion would
|
|
408
|
-
* wrap them: inline nodes in a paragraph and list items in a list.
|
|
409
|
-
*/
|
|
410
|
-
export function topLevel(schema, content) {
|
|
411
|
-
const top = schema.topNodeType.contentMatch;
|
|
412
|
-
const result = [];
|
|
413
|
-
let run = [];
|
|
414
|
-
const close = () => {
|
|
415
|
-
const type = run[0]?.type;
|
|
416
|
-
const wrapping = type
|
|
417
|
-
? type.isInline
|
|
418
|
-
? [schema.nodes.paragraph]
|
|
419
|
-
: top.findWrapping(type)
|
|
420
|
-
: null;
|
|
421
|
-
if (wrapping?.length)
|
|
422
|
-
result.push(wrapping.reduceRight((inner, wrapper) => wrapper.create(null, inner), run));
|
|
423
|
-
else
|
|
424
|
-
result.push(...run);
|
|
425
|
-
run = [];
|
|
426
|
-
};
|
|
427
|
-
content.forEach((node) => {
|
|
428
|
-
const previous = run[0];
|
|
429
|
-
if (previous &&
|
|
430
|
-
(node.isInline ? !previous.isInline : node.type !== previous.type))
|
|
431
|
-
close();
|
|
432
|
-
if (node.isInline || !top.matchType(node.type))
|
|
433
|
-
run.push(node);
|
|
434
|
-
else {
|
|
435
|
-
close();
|
|
436
|
-
result.push(node);
|
|
437
|
-
}
|
|
438
|
-
});
|
|
439
|
-
close();
|
|
440
|
-
return result;
|
|
441
|
-
}
|
|
442
|
-
/**
|
|
443
|
-
* Returns the slice with every part whose Markdown would read back as
|
|
444
|
-
* different structure replaced by a form that reads back the same, keeping as
|
|
445
|
-
* much of its text and formatting as possible.
|
|
446
|
-
*/
|
|
447
|
-
export function importableSlice(ctx, slice) {
|
|
448
|
-
const first = slice.content.firstChild;
|
|
449
|
-
if (!first)
|
|
450
|
-
return slice;
|
|
451
|
-
const check = new Check(ctx, first.type.schema);
|
|
452
|
-
const inline = first.isInline;
|
|
453
|
-
const content = Fragment.fromArray(topLevel(check.schema, slice.content));
|
|
454
|
-
const result = Fragment.fromArray(blocks(check, content));
|
|
455
|
-
// Inline nodes beside blocks were wrapped, so that form is the one to
|
|
456
|
-
// insert; other wrapping, such as list items in a list, is left to insertion.
|
|
457
|
-
let inlineCount = 0;
|
|
458
|
-
slice.content.forEach((node) => {
|
|
459
|
-
if (node.isInline)
|
|
460
|
-
inlineCount++;
|
|
461
|
-
});
|
|
462
|
-
const mixed = inlineCount > 0 && inlineCount < slice.content.childCount;
|
|
463
|
-
if (result.eq(content) && !mixed) {
|
|
464
|
-
const start = openDepth(slice.content, slice.openStart, true);
|
|
465
|
-
const end = openDepth(slice.content, slice.openEnd, false);
|
|
466
|
-
return start === slice.openStart && end === slice.openEnd
|
|
467
|
-
? slice
|
|
468
|
-
: new Slice(slice.content, start, end);
|
|
469
|
-
}
|
|
470
|
-
if (inline &&
|
|
471
|
-
result.childCount === 1 &&
|
|
472
|
-
result.firstChild.type.name === 'paragraph')
|
|
473
|
-
return new Slice(result.firstChild.content, 0, 0);
|
|
474
|
-
const openStart = inline ? 1 : slice.openStart;
|
|
475
|
-
const openEnd = inline ? 1 : slice.openEnd;
|
|
476
|
-
return new Slice(result, openDepth(result, openStart, true), openDepth(result, openEnd, false));
|
|
477
|
-
}
|
package/dist/image-blocks.d.ts
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import type { MilkdownPlugin } from '@milkdown/kit/ctx';
|
|
2
|
-
import { type Node as ProseNode, type NodeType } from '@milkdown/kit/prose/model';
|
|
3
|
-
import { type EditorState } from '@milkdown/kit/prose/state';
|
|
4
|
-
import type { ParserState } from '@milkdown/kit/transformer';
|
|
5
|
-
export declare const imageBlockName = "image_block";
|
|
6
|
-
/** Attributes shared by an inline image and a standalone image block. */
|
|
7
|
-
export declare const imageAttrs: {
|
|
8
|
-
src: {
|
|
9
|
-
default: string;
|
|
10
|
-
validate: string;
|
|
11
|
-
};
|
|
12
|
-
alt: {
|
|
13
|
-
default: string;
|
|
14
|
-
validate: string;
|
|
15
|
-
};
|
|
16
|
-
title: {
|
|
17
|
-
default: string;
|
|
18
|
-
validate: string;
|
|
19
|
-
};
|
|
20
|
-
inkkitFileRaw: {
|
|
21
|
-
default: null;
|
|
22
|
-
};
|
|
23
|
-
inkkitFileTarget: {
|
|
24
|
-
default: null;
|
|
25
|
-
};
|
|
26
|
-
inkkitFileFragment: {
|
|
27
|
-
default: null;
|
|
28
|
-
};
|
|
29
|
-
inkkitFileWidth: {
|
|
30
|
-
default: null;
|
|
31
|
-
};
|
|
32
|
-
inkkitFileInTable: {
|
|
33
|
-
default: boolean;
|
|
34
|
-
};
|
|
35
|
-
};
|
|
36
|
-
interface MarkdownParagraph {
|
|
37
|
-
children?: unknown[];
|
|
38
|
-
value?: unknown;
|
|
39
|
-
}
|
|
40
|
-
interface MarkdownImage {
|
|
41
|
-
type: string;
|
|
42
|
-
url?: unknown;
|
|
43
|
-
alt?: unknown;
|
|
44
|
-
title?: unknown;
|
|
45
|
-
inkkitFileRaw?: unknown;
|
|
46
|
-
inkkitFileTarget?: unknown;
|
|
47
|
-
inkkitFileFragment?: unknown;
|
|
48
|
-
inkkitFileWidth?: unknown;
|
|
49
|
-
inkkitFileInTable?: unknown;
|
|
50
|
-
}
|
|
51
|
-
export declare function markdownImageAttrs(node: MarkdownImage): {
|
|
52
|
-
src: string;
|
|
53
|
-
alt: string;
|
|
54
|
-
title: string;
|
|
55
|
-
inkkitFileRaw: {} | null;
|
|
56
|
-
inkkitFileTarget: {} | null;
|
|
57
|
-
inkkitFileFragment: {} | null;
|
|
58
|
-
inkkitFileWidth: {} | null;
|
|
59
|
-
inkkitFileInTable: {};
|
|
60
|
-
};
|
|
61
|
-
/** Whether a node displays an authored image, inline or as its own block. */
|
|
62
|
-
export declare function isImage(node: ProseNode): boolean;
|
|
63
|
-
export declare function isImageBlock(node: ProseNode | null | undefined): boolean;
|
|
64
|
-
export type ImagePlacement = {
|
|
65
|
-
kind: 'blocks';
|
|
66
|
-
} | {
|
|
67
|
-
kind: 'inline';
|
|
68
|
-
} | {
|
|
69
|
-
kind: 'after';
|
|
70
|
-
pos: number;
|
|
71
|
-
};
|
|
72
|
-
/** Where `insertImages` puts images for a selection. `blocks` replaces the selection with one block per
|
|
73
|
-
* image. `inline` places them on the one line of a table cell, which cannot hold blocks. `after` puts
|
|
74
|
-
* them below the nearest block that takes a following paragraph, leaving the selection and its
|
|
75
|
-
* block as written: the selection is in source that cannot hold an image, such as code, a comment
|
|
76
|
-
* or an unsupported literal block, or it spans table cells. Nothing is ever split or discarded. */
|
|
77
|
-
export declare function imagePlacement(state: EditorState): ImagePlacement;
|
|
78
|
-
/** The image a paragraph holds as its only content, without marks, which makes the paragraph a
|
|
79
|
-
* standalone image block. A linked or emphasised image stays inline so its marks are kept. */
|
|
80
|
-
export declare function standaloneImage(children: readonly ProseNode[]): ProseNode | undefined;
|
|
81
|
-
/** Reads a Markdown paragraph, as an image block when it holds a standalone image and the enclosing
|
|
82
|
-
* block accepts one. */
|
|
83
|
-
export declare function readParagraph(state: ParserState, node: MarkdownParagraph, paragraph: NodeType): void;
|
|
84
|
-
export declare const imageBlockSchema: import("@milkdown/kit/utils").$NodeSchema<"image_block">;
|
|
85
|
-
export declare const imageBlocks: MilkdownPlugin[];
|
|
86
|
-
export {};
|