@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.
Files changed (90) hide show
  1. package/NOTICE +4 -2
  2. package/README.md +11 -7
  3. package/dist/THIRD-PARTY-NOTICES.txt +2742 -0
  4. package/dist/chunks/core-vtCr4MBS.js +56503 -0
  5. package/dist/chunks/highlight-Daj2fv_u.js +14122 -0
  6. package/dist/core.d.ts +2 -5
  7. package/dist/core.js +2 -2
  8. package/dist/editor.d.ts +229 -47
  9. package/dist/index.js +5 -3
  10. package/dist/style.css +231 -202
  11. package/dist/types.d.ts +143 -18
  12. package/package.json +16 -13
  13. package/dist/autolink.d.ts +0 -1
  14. package/dist/autolink.js +0 -18
  15. package/dist/callouts.d.ts +0 -25
  16. package/dist/callouts.js +0 -288
  17. package/dist/clipboard.d.ts +0 -11
  18. package/dist/clipboard.js +0 -261
  19. package/dist/code-fences.d.ts +0 -8
  20. package/dist/code-fences.js +0 -171
  21. package/dist/comments.d.ts +0 -76
  22. package/dist/comments.js +0 -545
  23. package/dist/cursor.d.ts +0 -2
  24. package/dist/cursor.js +0 -40
  25. package/dist/decorations.d.ts +0 -2
  26. package/dist/decorations.js +0 -67
  27. package/dist/dialect.d.ts +0 -9
  28. package/dist/dialect.js +0 -446
  29. package/dist/drop-placement.d.ts +0 -19
  30. package/dist/drop-placement.js +0 -47
  31. package/dist/editor.js +0 -2673
  32. package/dist/files.d.ts +0 -40
  33. package/dist/files.js +0 -133
  34. package/dist/highlight.d.ts +0 -1
  35. package/dist/highlight.js +0 -136
  36. package/dist/html-import.d.ts +0 -13
  37. package/dist/html-import.js +0 -477
  38. package/dist/image-blocks.d.ts +0 -86
  39. package/dist/image-blocks.js +0 -329
  40. package/dist/images.d.ts +0 -51
  41. package/dist/images.js +0 -398
  42. package/dist/inline-highlight.d.ts +0 -6
  43. package/dist/inline-highlight.js +0 -112
  44. package/dist/labels.d.ts +0 -36
  45. package/dist/labels.js +0 -34
  46. package/dist/link-safety.d.ts +0 -17
  47. package/dist/link-safety.js +0 -29
  48. package/dist/linked-syntax.d.ts +0 -48
  49. package/dist/linked-syntax.js +0 -320
  50. package/dist/literals.d.ts +0 -20
  51. package/dist/literals.js +0 -216
  52. package/dist/mermaid.d.ts +0 -28
  53. package/dist/mermaid.js +0 -404
  54. package/dist/outline.d.ts +0 -22
  55. package/dist/outline.js +0 -79
  56. package/dist/paste-html.d.ts +0 -31
  57. package/dist/paste-html.js +0 -462
  58. package/dist/paste.d.ts +0 -33
  59. package/dist/paste.js +0 -591
  60. package/dist/portable-files.d.ts +0 -10
  61. package/dist/portable-files.js +0 -36
  62. package/dist/preserve.d.ts +0 -38
  63. package/dist/preserve.js +0 -1396
  64. package/dist/print.d.ts +0 -7
  65. package/dist/print.js +0 -406
  66. package/dist/reference-clipboard.d.ts +0 -8
  67. package/dist/reference-clipboard.js +0 -201
  68. package/dist/references.d.ts +0 -16
  69. package/dist/references.js +0 -418
  70. package/dist/scroll.d.ts +0 -15
  71. package/dist/scroll.js +0 -27
  72. package/dist/search.d.ts +0 -15
  73. package/dist/search.js +0 -202
  74. package/dist/selection.d.ts +0 -1
  75. package/dist/selection.js +0 -59
  76. package/dist/source.d.ts +0 -16
  77. package/dist/source.js +0 -57
  78. package/dist/spreadsheet.d.ts +0 -8
  79. package/dist/spreadsheet.js +0 -289
  80. package/dist/table-alignment.d.ts +0 -2
  81. package/dist/table-alignment.js +0 -46
  82. package/dist/tables.d.ts +0 -27
  83. package/dist/tables.js +0 -344
  84. package/dist/tasks.d.ts +0 -3
  85. package/dist/tasks.js +0 -51
  86. package/dist/text-ranges.d.ts +0 -29
  87. package/dist/text-ranges.js +0 -256
  88. package/dist/types.js +0 -8
  89. package/dist/wiki-view.d.ts +0 -21
  90. package/dist/wiki-view.js +0 -78
package/dist/paste.js DELETED
@@ -1,591 +0,0 @@
1
- import { editorViewCtx, parserCtx } from '@milkdown/kit/core';
2
- import { DOMParser as ProseParser, DOMSerializer, Fragment, Slice, } from '@milkdown/kit/prose/model';
3
- import { Plugin, PluginKey } from '@milkdown/kit/prose/state';
4
- import { $prose } from '@milkdown/kit/utils';
5
- import { pasteSpreadsheet } from './spreadsheet.js';
6
- import { importableSlice, topLevel } from './html-import.js';
7
- import { blockDropRange, caretAfter } from './drop-placement.js';
8
- import { isImage } from './image-blocks.js';
9
- import { collapseClipboardWhitespace, isBlockElement, isRewrittenElement, isRichElement, normalizeClipboardHTML, } from './paste-html.js';
10
- import { avoidReferenceCollisions, markdownFromHTML, } from './reference-clipboard.js';
11
- import { InkKitError, } from './types.js';
12
- export class PasteController {
13
- options;
14
- active;
15
- key = new PluginKey('inkkitPaste');
16
- dead = false;
17
- claimed = false;
18
- get pending() {
19
- return this.active != null;
20
- }
21
- plugin;
22
- constructor(options) {
23
- this.options = options;
24
- this.plugin = $prose(() => new Plugin({
25
- key: this.key,
26
- state: {
27
- init: () => null,
28
- apply: (transaction, previous) => {
29
- if (!this.active)
30
- return null;
31
- const anchor = previous?.operation === this.active ? previous : this.active;
32
- return {
33
- operation: this.active,
34
- from: transaction.mapping.map(anchor.from, 1),
35
- to: transaction.mapping.map(anchor.to, anchor.from === anchor.to ? 1 : -1),
36
- };
37
- },
38
- },
39
- props: {
40
- handleDOMEvents: {
41
- paste: (view, event) => {
42
- // ProseMirror parses the clipboard before asking handlePaste,
43
- // and this controller then imports it again itself.
44
- this.claimed = claimsPaste(view, event) !== false;
45
- queueMicrotask(() => {
46
- this.claimed = false;
47
- });
48
- return false;
49
- },
50
- // A drop of HTML from outside the editor imports as a paste of
51
- // whole blocks, placed as dropped images are. ProseMirror keeps
52
- // moves within the editor and drops of plain text.
53
- drop: (view, event) => {
54
- const data = event.dataTransfer;
55
- if (!data?.getData('text/html') ||
56
- !view.editable ||
57
- view.dragging)
58
- return false;
59
- event.preventDefault();
60
- const target = blockDropRange(view, event.clientX, event.clientY);
61
- view.focus();
62
- void this.run({
63
- text: data.getData('text/plain'),
64
- html: data.getData('text/html'),
65
- plainText: this.options.literalText?.() ?? false,
66
- }, [...data.files].filter((file) => file.type.startsWith('image/')), target).catch((error) => options.onError?.(error));
67
- return true;
68
- },
69
- },
70
- // Pastes and drops that ProseMirror inserts itself.
71
- transformPasted: (slice, view, plain) => this.claimed || plain || view.dragging
72
- ? slice
73
- : importableSlice(this.options.ctx(), slice),
74
- handlePaste: (view, event) => {
75
- this.claimed = false;
76
- const claim = claimsPaste(view, event);
77
- if (claim === false)
78
- return false;
79
- const data = event.clipboardData;
80
- const text = data.getData('text/plain');
81
- const html = data.getData('text/html');
82
- if (claim === 'plain') {
83
- void this.paste({ text, plainText: true }).catch((error) => options.onError?.(error));
84
- return true;
85
- }
86
- const files = [...data.files].filter((file) => file.type.startsWith('image/'));
87
- void this.run({
88
- text,
89
- html,
90
- plainText: this.options.literalText?.() ?? false,
91
- }, files).catch((error) => options.onError?.(error));
92
- return true;
93
- },
94
- },
95
- }));
96
- }
97
- paste(input) {
98
- return this.run(input);
99
- }
100
- anchor(operation) {
101
- const mapped = this.key.getState(this.options.ctx().get(editorViewCtx).state);
102
- return mapped?.operation === operation ? mapped : operation;
103
- }
104
- cancelPending() {
105
- this.active = undefined;
106
- this.options.onPendingChanged?.();
107
- }
108
- destroy() {
109
- this.dead = true;
110
- this.cancelPending();
111
- }
112
- assertContext(context, operation) {
113
- if (this.dead)
114
- throw new InkKitError('destroyed', 'The editor was destroyed.');
115
- const current = this.options.context();
116
- if (this.active !== operation ||
117
- current.documentId !== context.documentId ||
118
- current.generation !== context.generation)
119
- throw new InkKitError('stale-document', 'The document changed while images were importing.');
120
- }
121
- /**
122
- * Inserts dropped content as whole blocks, so none joins the block beside
123
- * it, and puts the caret after them.
124
- */
125
- insertBlocks(view, slice, range) {
126
- const blocks = Fragment.fromArray(topLevel(view.state.schema, slice.content));
127
- const tr = view.state.tr.replaceWith(range.from, range.to, blocks);
128
- const end = tr.mapping.map(range.to);
129
- view.dispatch(tr.setSelection(caretAfter(tr.doc, end)).scrollIntoView());
130
- }
131
- /** Imports the input at the selection, or in place of `target`. */
132
- async run(input, files = [], target) {
133
- if (this.dead)
134
- throw new InkKitError('destroyed', 'The editor was destroyed.');
135
- if (this.pending)
136
- throw new InkKitError('operation-pending', 'An image paste is still pending.');
137
- const context = { ...this.options.context() };
138
- const ctx = this.options.ctx();
139
- const view = ctx.get(editorViewCtx);
140
- if (!view.editable || this.options.editable?.() === false)
141
- return;
142
- if (view.composing)
143
- throw new InkKitError('composition', 'Finish composing text before pasting.');
144
- const operation = target
145
- ? { ...target }
146
- : { from: view.state.selection.from, to: view.state.selection.to };
147
- const inCode = () => view.state.doc.resolve(this.anchor(operation).from).parent.type.spec.code;
148
- this.active = operation;
149
- this.options.onPendingChanged?.();
150
- try {
151
- const images = [...(input.images ?? [])];
152
- for (const file of files)
153
- images.push({
154
- bytes: new Uint8Array(await file.arrayBuffer()),
155
- mimeType: file.type,
156
- filename: file.name,
157
- });
158
- this.assertContext(context, operation);
159
- const schema = view.state.schema;
160
- let slice;
161
- const plainText = input.plainText || this.options.literalText?.() || inCode();
162
- if (!plainText &&
163
- input.markdown == null &&
164
- pasteSpreadsheet(ctx, { ...input, images }, target && dropRange(view.state.doc, this.anchor(operation))))
165
- return;
166
- const markdown = plainText
167
- ? undefined
168
- : (input.markdown ??
169
- (input.html ? markdownFromHTML(input.html) : undefined));
170
- if (plainText) {
171
- if (inCode()) {
172
- const anchor = this.anchor(operation);
173
- view.dispatch(view.state.tr.insertText(input.text, anchor.from, anchor.to));
174
- return;
175
- }
176
- const paragraphs = input.text
177
- .replace(/\r\n?/g, '\n')
178
- .split('\n')
179
- .map((line) => schema.nodes.paragraph.create(null, line ? schema.text(line) : undefined));
180
- slice = new Slice(Fragment.fromArray(paragraphs), 1, 1);
181
- }
182
- else if (input.html) {
183
- // Template contents remain inert while unsupported source is replaced
184
- // and managed image references are imported.
185
- const template = document.createElement('template');
186
- template.innerHTML = input.html;
187
- const container = template.content;
188
- // Preserve whole unsupported tables before rewriting their descendants,
189
- // so comments retain their source and ordinary-copy privacy.
190
- for (const table of [...container.querySelectorAll('table')]) {
191
- if (table.parentElement?.closest('table'))
192
- continue;
193
- // Inside an element that stays literal source, the whole element is
194
- // kept as written, so its tables are not rewritten or judged here.
195
- if (!insideRewrittenContent(table))
196
- continue;
197
- if (schema.nodes.table) {
198
- // Judge the table as it will import, so wrappers such as `u` or
199
- // `label` in a cell do not make it literal. A table that is still
200
- // unsupported keeps its original source. An input in a cell would
201
- // be dropped with its state, so that table stays source too.
202
- const trial = table.cloneNode(true);
203
- const holder = table.ownerDocument.createDocumentFragment();
204
- holder.append(trial);
205
- normalizeClipboardHTML(holder);
206
- if (!table.querySelector('td input,th input') &&
207
- !unsupportedClipboardTable(trial)) {
208
- table.replaceWith(trial);
209
- continue;
210
- }
211
- }
212
- const preserved = ctx.get(parserCtx)(table.outerHTML);
213
- table.replaceWith(DOMSerializer.fromSchema(schema).serializeFragment(preserved.content));
214
- }
215
- for (const warning of container.querySelectorAll('[data-inkkit-diagram-error]'))
216
- warning.remove();
217
- for (const diagram of container.querySelectorAll('figure[data-inkkit-mermaid]')) {
218
- const source = diagram.getAttribute('data-inkkit-mermaid') ?? '';
219
- if (source.length > 30000)
220
- continue;
221
- const pre = document.createElement('pre');
222
- pre.dataset.language = 'mermaid';
223
- const fence = diagram.getAttribute('data-inkkit-fence');
224
- if (fence)
225
- pre.setAttribute('data-inkkit-fence', fence);
226
- const code = document.createElement('code');
227
- code.className = 'language-mermaid';
228
- code.textContent = source;
229
- pre.append(code);
230
- diagram.replaceWith(pre);
231
- }
232
- if (markdown == null) {
233
- // Native destinations can keep node metadata after dropping the
234
- // Markdown wrapper and its definitions. Import their visible HTML.
235
- for (const element of container.querySelectorAll('[data-inkkit-reference],[data-inkkit-footnote-reference],[data-inkkit-footnote-definition]')) {
236
- for (const name of [
237
- 'data-inkkit-reference',
238
- 'data-inkkit-footnote-reference',
239
- 'data-inkkit-footnote-definition',
240
- 'data-inkkit-label',
241
- 'data-inkkit-reference-type',
242
- 'data-inkkit-reference-content',
243
- ])
244
- element.removeAttribute(name);
245
- }
246
- }
247
- // Native HTML clipboards add encoding metadata and fragment markers.
248
- normalizeClipboardHTML(container);
249
- // Obsidian's reading view also copies its title and collapse controls
250
- // with document HTML.
251
- for (const control of container.querySelectorAll('div.mod-header.mod-ui,div.mod-footer.mod-ui')) {
252
- if (control.querySelector('.inline-title[contenteditable],.embedded-backlinks'))
253
- control.remove();
254
- }
255
- for (const heading of container.querySelectorAll('h1[data-heading],h2[data-heading],h3[data-heading],h4[data-heading],h5[data-heading],h6[data-heading]')) {
256
- for (const control of heading.querySelectorAll(':scope > span.heading-collapse-indicator.collapse-indicator.collapse-icon')) {
257
- if (control.querySelector(':scope > svg.svg-icon.right-triangle') &&
258
- !control.textContent?.trim())
259
- control.remove();
260
- }
261
- }
262
- for (const control of container.querySelectorAll('pre > button.copy-code-button')) {
263
- if (control.querySelector('svg.svg-icon'))
264
- control.remove();
265
- }
266
- for (const element of [...container.querySelectorAll('*')]) {
267
- if (!container.contains(element) || isRichElement(element))
268
- continue;
269
- const literal = document.createElement('pre');
270
- literal.className = 'literal-markdown';
271
- literal.textContent = element.outerHTML;
272
- element.replaceWith(literal);
273
- }
274
- for (const table of [...container.querySelectorAll('table')]) {
275
- if (table.parentElement?.closest('table'))
276
- continue;
277
- const rows = [...table.querySelectorAll('tr')];
278
- const unsupported = !schema.nodes.table || unsupportedClipboardTable(table);
279
- if (!unsupported && !table.querySelector('th')) {
280
- for (const cell of rows[0]?.querySelectorAll(':scope > td') ?? []) {
281
- const heading = document.createElement('th');
282
- for (const attribute of [...cell.attributes])
283
- heading.setAttribute(attribute.name, attribute.value);
284
- heading.append(...cell.childNodes);
285
- cell.replaceWith(heading);
286
- }
287
- }
288
- if (unsupported) {
289
- const literal = document.createElement('pre');
290
- literal.className = 'literal-markdown';
291
- literal.textContent = table.outerHTML;
292
- table.replaceWith(literal);
293
- }
294
- }
295
- collapseClipboardWhitespace(container);
296
- // An authored comment is read in Markdown as an InkKit comment, so it
297
- // imports as one, after whitespace collapse has passed it by.
298
- const walker = document.createTreeWalker(container, NodeFilter.SHOW_COMMENT);
299
- const comments = [];
300
- while (walker.nextNode())
301
- comments.push(walker.currentNode);
302
- for (const comment of comments) {
303
- const element = document.createElement(inlineComment(comment) ? 'span' : 'div');
304
- element.setAttribute('data-inkkit-comment', 'html');
305
- element.textContent = comment.data;
306
- comment.replaceWith(element);
307
- }
308
- const imported = new Map();
309
- const elements = [...container.querySelectorAll('img')];
310
- const uniqueSources = new Set(elements.map((element) => element.getAttribute('src') ?? ''));
311
- // A host-supplied array explicitly associates unnamed bytes by unique
312
- // preview order. A browser FileList has no such association, except the
313
- // unambiguous single-file/single-source case.
314
- const positional = (files.length === 0 || (files.length === 1 && uniqueSources.size === 1)
315
- ? images
316
- : (input.images ?? [])).filter((image) => !image.source);
317
- let index = 0;
318
- for (const element of elements) {
319
- const source = element.getAttribute('src') ?? '';
320
- const alt = element.getAttribute('alt') ?? 'Image';
321
- try {
322
- let reference = imported.get(source);
323
- if (!reference) {
324
- const matched = images.find((image) => image.source ===
325
- source);
326
- const captured = matched ?? dataImage(source) ?? positional[index++];
327
- if (!captured || !this.options.adapter)
328
- throw new Error('The pasted image is unavailable.');
329
- reference = (await this.options.adapter.importImage(captured, { ...context })).reference;
330
- this.assertContext(context, operation);
331
- imported.set(source, reference);
332
- }
333
- element.setAttribute('src', reference);
334
- element.removeAttribute('srcset');
335
- }
336
- catch (error) {
337
- if (error instanceof InkKitError)
338
- throw error;
339
- element.replaceWith(document.createTextNode(`[${alt}: image unavailable]`));
340
- this.options.onError?.(new InkKitError('image-unavailable', error instanceof Error
341
- ? error.message
342
- : 'The pasted image is unavailable.'));
343
- }
344
- }
345
- if (markdown != null) {
346
- const parsed = ctx.get(parserCtx)(markdown);
347
- let imageIndex = 0;
348
- const hydrate = (node) => {
349
- if (isImage(node)) {
350
- const slot = /^inkkit-clipboard-image:(\d+)$/.exec(String(node.attrs.src));
351
- const position = slot ? Number(slot[1]) : imageIndex++;
352
- const element = elements.some((element) => element.hasAttribute('data-inkkit-image-slot'))
353
- ? elements.find((element) => Number(element.getAttribute('data-inkkit-authored-image-slot') ?? element.getAttribute('data-inkkit-image-slot')) === position)
354
- : elements[position];
355
- if (!element || !container.contains(element)) {
356
- const text = schema.text(`[${node.attrs.alt || 'Image'}: image unavailable]`);
357
- return node.isBlock
358
- ? schema.nodes.paragraph.create(null, text)
359
- : text;
360
- }
361
- return node.type.create({ ...node.attrs, src: element.getAttribute('src') }, null, node.marks);
362
- }
363
- if (node.isText)
364
- return node;
365
- const children = [];
366
- node.forEach((child) => children.push(hydrate(child)));
367
- return node.copy(Fragment.fromArray(children));
368
- };
369
- slice = new Slice(hydrate(parsed).content, 0, 0);
370
- }
371
- else
372
- slice = importableSlice(ctx, ProseParser.fromSchema(schema).parseSlice(container, {
373
- preserveWhitespace: true,
374
- }));
375
- }
376
- else if (images.length) {
377
- const nodes = [];
378
- if (input.text)
379
- nodes.push(schema.nodes.paragraph.create(null, schema.text(input.text)));
380
- for (const image of images) {
381
- try {
382
- if (!this.options.adapter)
383
- throw new InkKitError('image-unavailable', 'No image adapter is configured.');
384
- const imported = await this.options.adapter.importImage(image, {
385
- ...context,
386
- });
387
- this.assertContext(context, operation);
388
- const imageNode = schema.nodes.image?.create({
389
- src: imported.reference,
390
- alt: image.filename ?? 'Image',
391
- });
392
- if (!imageNode)
393
- throw new InkKitError('image-unavailable', 'Images are disabled for this document.');
394
- nodes.push(schema.nodes.paragraph.create(null, imageNode));
395
- }
396
- catch (error) {
397
- if (error instanceof InkKitError &&
398
- error.code !== 'image-unavailable')
399
- throw error;
400
- nodes.push(schema.nodes.paragraph.create(null, schema.text(`[${image.filename ?? 'Image'}: image unavailable]`)));
401
- this.options.onError?.(new InkKitError('image-unavailable', error instanceof Error
402
- ? error.message
403
- : 'The pasted image is unavailable.'));
404
- }
405
- }
406
- slice = new Slice(Fragment.fromArray(nodes), 1, 1);
407
- }
408
- else {
409
- const selection = view.state.selection;
410
- const text = input.text.trim();
411
- if (markdown == null &&
412
- !selection.empty &&
413
- /^https?:\/\/\S+$/.test(text) &&
414
- schema.marks.link) {
415
- const anchor = this.anchor(operation);
416
- const mark = schema.marks.link.create({ href: text });
417
- view.dispatch(view.state.tr
418
- .addMark(anchor.from, anchor.to, mark)
419
- .removeStoredMark(mark));
420
- return;
421
- }
422
- const doc = ctx.get(parserCtx)(markdown ?? input.text);
423
- slice = new Slice(doc.content, 0, 0);
424
- }
425
- this.assertContext(context, operation);
426
- if (!view.editable || this.options.editable?.() === false)
427
- return;
428
- const anchor = this.anchor(operation);
429
- if (!plainText) {
430
- const valid = slice.content.firstChild?.isInline
431
- ? Fragment.from(schema.nodes.paragraph.create(null, slice.content))
432
- : slice.content;
433
- const remapped = avoidReferenceCollisions(schema.topNodeType.create(null, valid), view.state.doc);
434
- slice = new Slice(slice.content.firstChild?.isInline
435
- ? remapped.firstChild.content
436
- : remapped.content, slice.openStart, slice.openEnd);
437
- }
438
- if (target) {
439
- this.insertBlocks(view, slice, dropRange(view.state.doc, anchor));
440
- return;
441
- }
442
- const insertion = view.state.doc.resolve(anchor.from);
443
- // Blocks pasted into an empty top-level paragraph take its place whole,
444
- // as a drop does; joining the first of them into it could lose a
445
- // wrapper whose only content was empty.
446
- if (anchor.from === anchor.to &&
447
- insertion.depth === 1 &&
448
- insertion.parent.type.name === 'paragraph' &&
449
- insertion.parent.content.size === 0 &&
450
- slice.content.firstChild?.isBlock) {
451
- this.insertBlocks(view, slice, {
452
- from: insertion.before(),
453
- to: insertion.after(),
454
- });
455
- return;
456
- }
457
- // An isolated block such as a table cannot fit inside an empty paragraph.
458
- // Replace that empty block rather than retaining a spurious spacer.
459
- if (anchor.from === anchor.to &&
460
- insertion.parent.type.name === 'paragraph' &&
461
- insertion.parent.content.size === 0 &&
462
- slice.content.firstChild?.isBlock &&
463
- slice.content.firstChild.type.spec.isolating) {
464
- view.dispatch(view.state.tr
465
- .replaceRange(insertion.before(), insertion.after(), new Slice(slice.content, 0, 0))
466
- .scrollIntoView());
467
- }
468
- else
469
- view.dispatch(view.state.tr
470
- .replaceRange(anchor.from, anchor.to, slice)
471
- .scrollIntoView());
472
- }
473
- finally {
474
- if (this.active === operation) {
475
- this.active = undefined;
476
- this.options.onPendingChanged?.();
477
- }
478
- }
479
- }
480
- }
481
- /**
482
- * The range a drop replaces, mapped to the current document. It replaces an
483
- * empty paragraph only while that is still empty, so text typed there while
484
- * images import is kept, and the drop then follows it.
485
- */
486
- function dropRange(doc, range) {
487
- const node = doc.nodeAt(range.from);
488
- return range.from < range.to &&
489
- node?.type.name === 'paragraph' &&
490
- node.content.size === 0 &&
491
- range.from + node.nodeSize === range.to
492
- ? range
493
- : { from: range.to, to: range.to };
494
- }
495
- /**
496
- * Whether `handlePaste` takes the paste, and as what. Valid VS Code metadata
497
- * keeps the provider's code-language behaviour; invalid metadata pastes plain
498
- * text.
499
- */
500
- function claimsPaste(view, event) {
501
- const data = event.clipboardData;
502
- if (!data || !view.editable)
503
- return false;
504
- if (view.state.selection.$from.parent.type.spec.code)
505
- return false;
506
- const metadata = data.getData('vscode-editor-data');
507
- if (!metadata)
508
- return 'import';
509
- try {
510
- const value = JSON.parse(metadata);
511
- return value &&
512
- typeof value === 'object' &&
513
- 'mode' in value &&
514
- typeof value.mode === 'string'
515
- ? false
516
- : 'plain';
517
- }
518
- catch {
519
- return 'plain';
520
- }
521
- }
522
- // Inside text or an element that holds text, a comment is part of a line.
523
- function inlineComment(comment) {
524
- const parent = comment.parentElement;
525
- if (parent && /^(?:P|H[1-6]|TD|TH|LI|DT|DD)$/.test(parent.tagName))
526
- return true;
527
- if (parent && isRichElement(parent) && !isBlockElement(parent))
528
- return true;
529
- for (const sibling of comment.parentNode?.childNodes ?? [])
530
- if ((sibling.nodeType === Node.TEXT_NODE && sibling.textContent?.trim()) ||
531
- (sibling.nodeType === Node.ELEMENT_NODE &&
532
- isRichElement(sibling) &&
533
- !isBlockElement(sibling)))
534
- return true;
535
- return false;
536
- }
537
- function insideRewrittenContent(element) {
538
- for (let parent = element.parentElement; parent; parent = parent.parentElement)
539
- if (!isRewrittenElement(parent))
540
- return false;
541
- return true;
542
- }
543
- function unsupportedClipboardTable(table) {
544
- const rows = [...table.querySelectorAll('tr')];
545
- const widths = rows.map((row) => row.querySelectorAll(':scope > th,:scope > td').length);
546
- const supported = new Set([
547
- 'THEAD',
548
- 'TBODY',
549
- 'TFOOT',
550
- 'TR',
551
- 'TH',
552
- 'TD',
553
- 'P',
554
- 'STRONG',
555
- 'B',
556
- 'EM',
557
- 'I',
558
- 'S',
559
- 'DEL',
560
- 'STRIKE',
561
- 'CODE',
562
- 'A',
563
- 'MARK',
564
- 'SPAN',
565
- 'SUP',
566
- 'FONT',
567
- 'IMG',
568
- ]);
569
- return (widths.length === 0 ||
570
- widths.some((width) => !width || width !== widths[0]) ||
571
- !!document.createTreeWalker(table, NodeFilter.SHOW_COMMENT).nextNode() ||
572
- [...table.querySelectorAll('*')].some((element) => !supported.has(element.tagName)) ||
573
- [...table.querySelectorAll('p')].some((paragraph) => {
574
- const cell = paragraph.parentElement;
575
- return (!cell ||
576
- !['TH', 'TD'].includes(cell.tagName) ||
577
- cell.children.length !== 1 ||
578
- [...cell.childNodes].some((node) => node !== paragraph && !!node.textContent?.trim()));
579
- }) ||
580
- !!table.querySelector('[rowspan]:not([rowspan="1"]),[colspan]:not([colspan="1"])'));
581
- }
582
- function dataImage(source) {
583
- const match = /^data:(image\/(?:png|jpeg|gif|webp|avif|bmp|tiff));base64,([A-Za-z0-9+/=\s]+)$/.exec(source);
584
- if (!match)
585
- return undefined;
586
- const binary = atob(match[2]);
587
- return {
588
- mimeType: match[1],
589
- bytes: Uint8Array.from(binary, (character) => character.charCodeAt(0)),
590
- };
591
- }
@@ -1,10 +0,0 @@
1
- import type { Node } from '@milkdown/kit/prose/model';
2
- import type { DocumentContext, FileAdapter, FilePresentation, FileReference, ImageAdapter, PortableImage } from './types.js';
3
- export interface PortableFile {
4
- reference: FileReference;
5
- presentation: FilePresentation;
6
- label: string;
7
- description: string;
8
- }
9
- export declare function portableFile(node: Node, adapter: FileAdapter | undefined, context: DocumentContext, signal: AbortSignal): Promise<PortableFile | undefined>;
10
- export declare function portableFileImage(file: PortableFile, files: FileAdapter, legacy: ImageAdapter | undefined, context: DocumentContext, signal: AbortSignal): Promise<PortableImage>;
@@ -1,36 +0,0 @@
1
- import { resolveFile, withFileSignal } from './files.js';
2
- import { fileReference } from './linked-syntax.js';
3
- export async function portableFile(node, adapter, context, signal) {
4
- if (!adapter)
5
- return undefined;
6
- const reference = fileReference(node);
7
- let presentation;
8
- try {
9
- presentation = await resolveFile(adapter, reference, { ...context, signal });
10
- }
11
- catch (error) {
12
- signal.throwIfAborted();
13
- presentation = {
14
- kind: 'error',
15
- message: error instanceof Error ? error.message : 'The file is unavailable.',
16
- };
17
- }
18
- const label = presentation.label || reference.label || reference.reference || 'File';
19
- const kind = presentation.kind;
20
- const description = kind === 'missing' || kind === 'error'
21
- ? `[File: ${label} (${kind === 'missing' ? 'unavailable' : 'error'})]`
22
- : `[${kind === 'pdf' ? 'PDF' : kind[0].toUpperCase() + kind.slice(1)}: ${label}]`;
23
- return { reference, presentation, label, description };
24
- }
25
- export async function portableFileImage(file, files, legacy, context, signal) {
26
- signal.throwIfAborted();
27
- const image = files.exportImage
28
- ? await withFileSignal(files.exportImage(file.reference, { ...context, signal }), signal)
29
- : file.reference.kind === 'path' && legacy
30
- ? await withFileSignal(legacy.exportImage(file.reference.reference, { ...context }), signal)
31
- : undefined;
32
- signal.throwIfAborted();
33
- if (!image)
34
- throw new Error('The image has no portable export adapter.');
35
- return image;
36
- }
@@ -1,38 +0,0 @@
1
- import type { Ctx } from '@milkdown/kit/ctx';
2
- import type { Node as ProseNode } from '@milkdown/kit/prose/model';
3
- export declare class PreservationError extends Error {
4
- constructor(message: string, options?: ErrorOptions);
5
- }
6
- export declare function semanticSignature(doc: ProseNode): string;
7
- export declare function correspondence(before: string[], after: string[]): Array<number | undefined>;
8
- /** Retains the loaded source of blocks the edit leaves alone. */
9
- export declare class Preservation {
10
- private readonly ctx;
11
- private readonly source;
12
- private readonly baseline;
13
- private readonly blocks;
14
- private readonly lineEnding;
15
- private readonly originalForms;
16
- private readonly forms;
17
- private readonly blockEdits;
18
- private readonly itemEdits;
19
- private readonly definitionNodes;
20
- private readonly definitionSources?;
21
- constructor(ctx: Ctx, source: string);
22
- private retainBlock;
23
- private localEdits;
24
- private pairedInPlace;
25
- private definitions;
26
- private inContext;
27
- private parseLocally;
28
- private canonicalTree;
29
- private static plainInline;
30
- private separated;
31
- private localBlock;
32
- private formOf;
33
- private localItems;
34
- private localItem;
35
- private separateLists;
36
- private relabelList;
37
- serialize(doc: ProseNode): string;
38
- }