@aicayzer/inkkit 0.0.6 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/callouts.d.ts +2 -0
- package/dist/callouts.js +11 -0
- package/dist/dialect.js +10 -2
- package/dist/editor.d.ts +25 -3
- package/dist/editor.js +451 -96
- package/dist/index.d.ts +2 -1
- package/dist/outline.d.ts +22 -0
- package/dist/outline.js +79 -0
- package/dist/search.d.ts +14 -0
- package/dist/search.js +171 -0
- package/dist/source.d.ts +17 -0
- package/dist/source.js +117 -0
- package/package.json +1 -1
package/dist/editor.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { Editor, defaultValueCtx, editorViewCtx, editorViewOptionsCtx, remarkCtx, remarkStringifyOptionsCtx, rootCtx, } from '@milkdown/kit/core';
|
|
1
|
+
import { Editor, defaultValueCtx, editorViewCtx, parserCtx, editorViewOptionsCtx, remarkCtx, remarkStringifyOptionsCtx, rootCtx, } from '@milkdown/kit/core';
|
|
2
2
|
import { visibleClipboard, portableClipboard, clipboardText, clipboardContent, } from './clipboard.js';
|
|
3
3
|
import { clipboard } from '@milkdown/kit/plugin/clipboard';
|
|
4
4
|
import { history } from '@milkdown/kit/plugin/history';
|
|
5
|
+
import { closeHistory, undo as undoHistory, redo as redoHistory, } from '@milkdown/kit/prose/history';
|
|
5
6
|
import { cursor } from '@milkdown/kit/plugin/cursor';
|
|
6
7
|
import { blockquoteKeymap, blockquoteSchema, bulletListKeymap, codeBlockKeymap, createCodeBlockCommand, emphasisKeymap, headingKeymap, inlineCodeKeymap, inlineCodeSchema, linkSchema, listItemKeymap, orderedListKeymap, paragraphKeymap, strongKeymap, toggleEmphasisCommand, toggleInlineCodeCommand, toggleLinkCommand, toggleStrongCommand, turnIntoTextCommand, wrapInBulletListCommand, wrapInHeadingCommand, wrapInOrderedListCommand, } from '@milkdown/kit/preset/commonmark';
|
|
7
8
|
import { strikethroughKeymap, toggleStrikethroughCommand, } from '@milkdown/kit/preset/gfm';
|
|
@@ -12,8 +13,9 @@ import { findWrapping, liftTarget } from '@milkdown/kit/prose/transform';
|
|
|
12
13
|
import { AllSelection, NodeSelection, Plugin, PluginKey, Selection, TextSelection, } from '@milkdown/kit/prose/state';
|
|
13
14
|
import { $prose, callCommand, replaceAll, } from '@milkdown/kit/utils';
|
|
14
15
|
import { codeCopyPlugin, placeholderPlugin } from './decorations.js';
|
|
15
|
-
import { createDialect,
|
|
16
|
+
import { createDialect, stringifyOptions } from './dialect.js';
|
|
16
17
|
import { Preservation } from './preserve.js';
|
|
18
|
+
import { sourceAttribute, cleanSourceDoc, editedPlainSource, sourceSelection, revealSourceCaret, rawOffset, normalizedOffset, } from './source.js';
|
|
17
19
|
import { normaliseLabel, referenceDefinitions, footnoteDefinitions, } from './references.js';
|
|
18
20
|
import { selectionMarkdown, selectionContent, referenceMetadata, withReferenceMetadata, } from './reference-clipboard.js';
|
|
19
21
|
import { imageView } from './images.js';
|
|
@@ -24,7 +26,9 @@ import { highlightKeymap, toggleHighlightCommand } from './inline-highlight.js';
|
|
|
24
26
|
import { commentSelectionContent, setCommentVisibility } from './comments.js';
|
|
25
27
|
import { PasteController } from './paste.js';
|
|
26
28
|
import { selectionPlugin } from './selection.js';
|
|
27
|
-
import { search, SearchQuery,
|
|
29
|
+
import { search, SearchQuery, setSearchState } from 'prosemirror-search';
|
|
30
|
+
import { findFormatted, findLiteral, replaceFormatted, replaceLiteral, } from './search.js';
|
|
31
|
+
import { collectFormattedHeadings, collectSourceHeadings, navigateFormattedHeading, sourceHeadingPosition, } from './outline.js';
|
|
28
32
|
import { taskListPlugin, toggleTaskList } from './tasks.js';
|
|
29
33
|
import { isMermaid, mermaidPreview } from './mermaid.js';
|
|
30
34
|
import { hasAuthoredImagesInLiterals, printableMarkdown, printableText, } from './print.js';
|
|
@@ -187,11 +191,13 @@ const dropControlCharacters = $prose(() => new Plugin({
|
|
|
187
191
|
}));
|
|
188
192
|
// The listener plugin reports selection changes from inside state.apply, before
|
|
189
193
|
// the view holds the new state, so the caret state is read from the view instead.
|
|
190
|
-
function caretStatePlugin(events) {
|
|
194
|
+
function caretStatePlugin(events, active = () => true) {
|
|
191
195
|
return $prose(() => new Plugin({
|
|
192
196
|
key: new PluginKey('caretState'),
|
|
193
197
|
view: () => ({
|
|
194
198
|
update(view, previous) {
|
|
199
|
+
if (!active())
|
|
200
|
+
return;
|
|
195
201
|
const { state } = view;
|
|
196
202
|
if (state.selection.eq(previous.selection) &&
|
|
197
203
|
state.doc.eq(previous.doc) &&
|
|
@@ -233,7 +239,6 @@ export class InkKitEditor {
|
|
|
233
239
|
options;
|
|
234
240
|
editor;
|
|
235
241
|
lastMarkdown = '';
|
|
236
|
-
baseline = '';
|
|
237
242
|
documentId = '';
|
|
238
243
|
formatType = 'md';
|
|
239
244
|
revision = 0;
|
|
@@ -241,6 +246,11 @@ export class InkKitEditor {
|
|
|
241
246
|
operationSequence = 0;
|
|
242
247
|
plainComposing = false;
|
|
243
248
|
plainSearch = '';
|
|
249
|
+
literalMatch;
|
|
250
|
+
beforePlainInput;
|
|
251
|
+
mode = 'formatted';
|
|
252
|
+
sourcePreservations = new WeakMap();
|
|
253
|
+
outlineEpoch = 0;
|
|
244
254
|
ready = false;
|
|
245
255
|
destroyed = false;
|
|
246
256
|
preservation;
|
|
@@ -260,20 +270,30 @@ export class InkKitEditor {
|
|
|
260
270
|
if (this.loading || view.state.doc.eq(previous.doc))
|
|
261
271
|
return;
|
|
262
272
|
this.revision += 1;
|
|
273
|
+
this.literalMatch = undefined;
|
|
263
274
|
let markdown;
|
|
264
275
|
try {
|
|
265
|
-
markdown =
|
|
266
|
-
this.preservation?.serialize(view.state.doc) ??
|
|
267
|
-
serialize(this.editor.ctx, view.state.doc);
|
|
276
|
+
markdown = this.currentText(view.state.doc);
|
|
268
277
|
}
|
|
269
278
|
catch (error) {
|
|
270
279
|
this.events.error?.(error instanceof Error ? error : new Error(String(error)));
|
|
271
280
|
return;
|
|
272
281
|
}
|
|
282
|
+
if (this.literalSurface) {
|
|
283
|
+
const anchor = view.state.doc.attrs[sourceAttribute];
|
|
284
|
+
const from = anchor?.from ?? this.plain.selectionStart;
|
|
285
|
+
const to = anchor?.to ?? this.plain.selectionEnd;
|
|
286
|
+
if (!this.plainComposing)
|
|
287
|
+
this.plain.value = markdown;
|
|
288
|
+
if (!this.plainComposing) {
|
|
289
|
+
this.plain.setSelectionRange(Math.min(from, this.plain.value.length), Math.min(to, this.plain.value.length));
|
|
290
|
+
revealSourceCaret(this.plain);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
273
293
|
if (markdown === this.lastMarkdown)
|
|
274
294
|
return;
|
|
275
295
|
this.lastMarkdown = markdown;
|
|
276
|
-
this.events.changed(markdown
|
|
296
|
+
this.events.changed(markdown, this.generation);
|
|
277
297
|
},
|
|
278
298
|
}),
|
|
279
299
|
}));
|
|
@@ -299,29 +319,147 @@ export class InkKitEditor {
|
|
|
299
319
|
throw new InkKitError('not-ready', 'No document has been loaded');
|
|
300
320
|
if (generation !== this.generation)
|
|
301
321
|
throw new InkKitError('stale-document', 'Document changed');
|
|
302
|
-
if (this.
|
|
322
|
+
if (this.literalSurface
|
|
303
323
|
? this.plainComposing
|
|
304
324
|
: this.editor.ctx.get(editorViewCtx).composing)
|
|
305
325
|
throw new InkKitError('composition', 'Text composition is in progress');
|
|
306
326
|
if (this.pasteController.pending)
|
|
307
327
|
throw new InkKitError('operation-pending', 'An image import is in progress');
|
|
308
328
|
}
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
329
|
+
get literalSurface() {
|
|
330
|
+
return this.formatType === 'txt' || this.mode === 'source';
|
|
331
|
+
}
|
|
332
|
+
get editingMode() {
|
|
333
|
+
this.assertAlive();
|
|
334
|
+
return this.literalSurface ? 'source' : 'formatted';
|
|
335
|
+
}
|
|
336
|
+
currentText(doc = this.editor.ctx.get(editorViewCtx).state.doc) {
|
|
337
|
+
const provenance = doc.attrs[sourceAttribute];
|
|
338
|
+
if (this.formatType === 'txt')
|
|
339
|
+
return provenance?.text ?? this.originalSource;
|
|
340
|
+
let preservation = this.preservation;
|
|
341
|
+
if (provenance) {
|
|
342
|
+
preservation = this.sourcePreservations.get(provenance);
|
|
343
|
+
if (!preservation) {
|
|
344
|
+
preservation = new Preservation(this.editor.ctx, provenance.text);
|
|
345
|
+
this.sourcePreservations.set(provenance, preservation);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
return preservation.serialize(cleanSourceDoc(doc));
|
|
349
|
+
}
|
|
350
|
+
setEditingMode(mode, expectedGeneration) {
|
|
351
|
+
this.assertCurrent(expectedGeneration);
|
|
352
|
+
if (mode !== 'source' && mode !== 'formatted')
|
|
353
|
+
throw new RangeError('Editing mode must be source or formatted');
|
|
354
|
+
if (this.formatType === 'txt' || this.mode === mode)
|
|
355
|
+
return false;
|
|
356
|
+
const text = this.snapshot().text;
|
|
357
|
+
this.mode = mode;
|
|
358
|
+
this.outlineEpoch += 1;
|
|
359
|
+
this.plainSearch = '';
|
|
360
|
+
this.literalMatch = undefined;
|
|
361
|
+
if (mode === 'source') {
|
|
362
|
+
this.plain.value = text;
|
|
363
|
+
this.plain.setSelectionRange(this.plain.value.length, this.plain.value.length);
|
|
364
|
+
}
|
|
365
|
+
this.updateSurface();
|
|
366
|
+
this.events.stateChanged(this.literalSurface
|
|
367
|
+
? { marks: [], block: { type: 'paragraph' }, quoted: false }
|
|
368
|
+
: caretState(this.editor.ctx.get(editorViewCtx).state));
|
|
369
|
+
return true;
|
|
370
|
+
}
|
|
371
|
+
updateSurface() {
|
|
372
|
+
this.plain.hidden = !this.literalSurface;
|
|
373
|
+
this.plain.setAttribute('aria-label', this.formatType === 'txt' ? 'Plain text editor' : 'Markdown source');
|
|
374
|
+
this.editor.ctx.get(editorViewCtx).dom.parentElement.hidden =
|
|
375
|
+
this.literalSurface;
|
|
376
|
+
}
|
|
377
|
+
replaceSource(text, expectedGeneration) {
|
|
378
|
+
this.assertCurrent(expectedGeneration);
|
|
379
|
+
return this.applySource(text, true);
|
|
380
|
+
}
|
|
381
|
+
applySource(text, isolated, from, to) {
|
|
382
|
+
if (typeof text !== 'string')
|
|
383
|
+
throw new TypeError('Source must be a string');
|
|
384
|
+
if (text === this.currentText())
|
|
385
|
+
return false;
|
|
386
|
+
const view = this.editor.ctx.get(editorViewCtx);
|
|
387
|
+
const normalizedLength = text.replace(/\r\n?/g, '\n').length;
|
|
388
|
+
const provenance = Object.freeze({
|
|
389
|
+
text,
|
|
390
|
+
format: this.formatType,
|
|
391
|
+
from: from ?? normalizedLength,
|
|
392
|
+
to: to ?? from ?? normalizedLength,
|
|
393
|
+
});
|
|
394
|
+
let parsed;
|
|
395
|
+
try {
|
|
396
|
+
parsed =
|
|
397
|
+
this.formatType === 'txt'
|
|
398
|
+
? view.state.schema.topNodeType.create(null, view.state.schema.nodes.paragraph.create(null, text ? view.state.schema.text(text) : undefined))
|
|
399
|
+
: this.editor.ctx.get(parserCtx)(text);
|
|
400
|
+
if (this.formatType === 'md')
|
|
401
|
+
this.sourcePreservations.set(provenance, new Preservation(this.editor.ctx, text));
|
|
402
|
+
}
|
|
403
|
+
catch (error) {
|
|
404
|
+
throw new InkKitError('preservation', error instanceof Error ? error.message : 'Cannot read Markdown source');
|
|
405
|
+
}
|
|
406
|
+
if (this.literalSurface) {
|
|
407
|
+
const previous = Object.freeze({
|
|
408
|
+
text: this.currentText(),
|
|
409
|
+
format: this.formatType,
|
|
410
|
+
from: this.beforePlainInput?.from ?? this.plain.selectionStart,
|
|
411
|
+
to: this.beforePlainInput?.to ?? this.plain.selectionEnd,
|
|
412
|
+
});
|
|
413
|
+
// History must retain the source caret even when the formatted document has no corresponding position.
|
|
414
|
+
this.loading = true;
|
|
415
|
+
try {
|
|
416
|
+
view.dispatch(view.state.tr
|
|
417
|
+
.setDocAttribute(sourceAttribute, previous)
|
|
418
|
+
.setMeta('addToHistory', false));
|
|
419
|
+
}
|
|
420
|
+
finally {
|
|
421
|
+
this.loading = false;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
let tr = view.state.tr
|
|
425
|
+
.replaceWith(0, view.state.doc.content.size, parsed.content)
|
|
426
|
+
.setDocAttribute(sourceAttribute, provenance);
|
|
427
|
+
tr.setSelection(Selection.atEnd(tr.doc));
|
|
428
|
+
if (isolated)
|
|
429
|
+
tr = closeHistory(tr);
|
|
430
|
+
view.dispatch(tr);
|
|
431
|
+
if (isolated)
|
|
432
|
+
view.dispatch(closeHistory(view.state.tr).setMeta('addToHistory', false));
|
|
433
|
+
return true;
|
|
434
|
+
}
|
|
435
|
+
undo(expectedGeneration) {
|
|
436
|
+
this.assertCurrent(expectedGeneration);
|
|
437
|
+
const view = this.editor.ctx.get(editorViewCtx);
|
|
438
|
+
return undoHistory(view.state, view.dispatch);
|
|
439
|
+
}
|
|
440
|
+
redo(expectedGeneration) {
|
|
441
|
+
this.assertCurrent(expectedGeneration);
|
|
442
|
+
const view = this.editor.ctx.get(editorViewCtx);
|
|
443
|
+
return redoHistory(view.state, view.dispatch);
|
|
444
|
+
}
|
|
445
|
+
replaceLiteralSelection(text, isolated = true, retainEndings = false) {
|
|
446
|
+
const value = this.plain.value;
|
|
447
|
+
const start = this.plain.selectionStart, end = this.plain.selectionEnd;
|
|
448
|
+
const next = value.slice(0, start) + text.replace(/\r\n?/g, '\n') + value.slice(end);
|
|
449
|
+
const caret = start + text.replace(/\r\n?/g, '\n').length;
|
|
450
|
+
const previous = this.currentText();
|
|
451
|
+
const source = retainEndings
|
|
452
|
+
? previous.slice(0, rawOffset(previous, start)) +
|
|
453
|
+
text +
|
|
454
|
+
previous.slice(rawOffset(previous, end))
|
|
455
|
+
: editedPlainSource(previous, next);
|
|
456
|
+
this.applySource(source, isolated, caret, caret);
|
|
316
457
|
}
|
|
317
458
|
snapshot(expectedGeneration) {
|
|
318
459
|
this.assertCurrent(expectedGeneration);
|
|
319
460
|
let text;
|
|
320
461
|
try {
|
|
321
|
-
text =
|
|
322
|
-
this.formatType === 'txt'
|
|
323
|
-
? this.plainSource()
|
|
324
|
-
: this.preservation.serialize(this.editor.ctx.get(editorViewCtx).state.doc);
|
|
462
|
+
text = this.currentText();
|
|
325
463
|
}
|
|
326
464
|
catch (error) {
|
|
327
465
|
throw new InkKitError('preservation', error instanceof Error ? error.message : 'Cannot preserve Markdown');
|
|
@@ -377,7 +515,7 @@ export class InkKitEditor {
|
|
|
377
515
|
if (this.formatType === 'txt') {
|
|
378
516
|
const text = all
|
|
379
517
|
? snapshot.text
|
|
380
|
-
:
|
|
518
|
+
: snapshot.text.slice(rawOffset(snapshot.text, this.plain.selectionStart), rawOffset(snapshot.text, this.plain.selectionEnd));
|
|
381
519
|
const pre = document.createElement('pre');
|
|
382
520
|
pre.textContent = text;
|
|
383
521
|
return {
|
|
@@ -389,40 +527,54 @@ export class InkKitEditor {
|
|
|
389
527
|
};
|
|
390
528
|
}
|
|
391
529
|
const { doc, schema, selection } = this.editor.ctx.get(editorViewCtx).state;
|
|
530
|
+
const selectedSource = !all && this.mode === 'source'
|
|
531
|
+
? sourceSelection(snapshot.text, this.plain.selectionStart, this.plain.selectionEnd)
|
|
532
|
+
: undefined;
|
|
533
|
+
const selectedDoc = selectedSource
|
|
534
|
+
? this.editor.ctx.get(parserCtx)(selectedSource.shareable)
|
|
535
|
+
: undefined;
|
|
392
536
|
const completeDiagrams = new Set();
|
|
393
|
-
doc.descendants((node, pos) => {
|
|
537
|
+
(selectedDoc ?? doc).descendants((node, pos) => {
|
|
394
538
|
if (isMermaid(node) &&
|
|
395
|
-
(
|
|
396
|
-
(
|
|
397
|
-
|
|
539
|
+
(selectedDoc
|
|
540
|
+
? Boolean(node.attrs.authoredFence?.close)
|
|
541
|
+
: all ||
|
|
542
|
+
(selection.from <= pos + 1 &&
|
|
543
|
+
selection.to >= pos + node.nodeSize - 1))) {
|
|
398
544
|
completeDiagrams.add(node.textContent.replace(/\r\n?/g, '\n'));
|
|
399
545
|
}
|
|
400
546
|
});
|
|
401
|
-
const content =
|
|
402
|
-
?
|
|
403
|
-
:
|
|
547
|
+
const content = selectedDoc
|
|
548
|
+
? selectedDoc.content
|
|
549
|
+
: all
|
|
550
|
+
? doc.content
|
|
551
|
+
: commentSelectionContent(doc, selection.from, selection.to, selection.content().content);
|
|
404
552
|
const valid = content.firstChild?.isInline
|
|
405
553
|
? Fragment.from(schema.nodes.paragraph.create(null, content))
|
|
406
554
|
: content;
|
|
407
555
|
let markdown;
|
|
408
556
|
try {
|
|
409
|
-
markdown =
|
|
410
|
-
?
|
|
411
|
-
:
|
|
412
|
-
?
|
|
413
|
-
:
|
|
557
|
+
markdown = selectedSource
|
|
558
|
+
? selectedSource.markdown
|
|
559
|
+
: all
|
|
560
|
+
? snapshot.text
|
|
561
|
+
: completeDiagrams.size
|
|
562
|
+
? new Preservation(this.editor.ctx, snapshot.text).serialize(doc.type.create(null, selectionContent(doc, valid)))
|
|
563
|
+
: selectionMarkdown(this.editor.ctx, doc, valid);
|
|
414
564
|
}
|
|
415
565
|
catch (error) {
|
|
416
566
|
throw new InkKitError('preservation', error instanceof Error
|
|
417
567
|
? error.message
|
|
418
568
|
: 'Cannot copy the selected Markdown');
|
|
419
569
|
}
|
|
420
|
-
const result = await portableClipboard(all ? content : selectionContent(doc, content), schema, markdown, this.options.images, this.context(), completeDiagrams);
|
|
421
|
-
const metadata = referenceMetadata(this.editor.ctx, doc, content);
|
|
570
|
+
const result = await portableClipboard(all || selectedDoc ? content : selectionContent(doc, content), schema, markdown, this.options.images, this.context(), completeDiagrams);
|
|
571
|
+
const metadata = referenceMetadata(this.editor.ctx, selectedDoc ?? doc, content);
|
|
422
572
|
if (metadata != null)
|
|
423
573
|
result.html = withReferenceMetadata(result.html, metadata);
|
|
424
574
|
this.assertCurrent(snapshot.generation);
|
|
425
|
-
if (snapshot.documentId !== this.documentId ||
|
|
575
|
+
if (snapshot.documentId !== this.documentId ||
|
|
576
|
+
epoch !== this.documentEpoch ||
|
|
577
|
+
snapshot.revision !== this.revision)
|
|
426
578
|
throw new InkKitError('stale-document', 'Document changed');
|
|
427
579
|
if (result.images.some((image) => image.error))
|
|
428
580
|
this.events.error?.(new InkKitError('image-unavailable', 'Some copied images were unavailable; their descriptions were retained'));
|
|
@@ -432,8 +584,10 @@ export class InkKitEditor {
|
|
|
432
584
|
}
|
|
433
585
|
async paste(input) {
|
|
434
586
|
this.assertCurrent();
|
|
435
|
-
if (this.
|
|
436
|
-
this.pasteAsPlainText(
|
|
587
|
+
if (this.literalSurface || input.plainText) {
|
|
588
|
+
this.pasteAsPlainText(this.mode === 'source' && this.formatType === 'md'
|
|
589
|
+
? (input.markdown ?? input.text)
|
|
590
|
+
: input.text);
|
|
437
591
|
return;
|
|
438
592
|
}
|
|
439
593
|
await this.pasteController.paste(input);
|
|
@@ -444,9 +598,8 @@ export class InkKitEditor {
|
|
|
444
598
|
}
|
|
445
599
|
pasteAsPlainText(text) {
|
|
446
600
|
this.assertCurrent();
|
|
447
|
-
if (this.
|
|
448
|
-
this.
|
|
449
|
-
this.plain.dispatchEvent(new Event('input'));
|
|
601
|
+
if (this.literalSurface) {
|
|
602
|
+
this.replaceLiteralSelection(text, true, this.formatType === 'md');
|
|
450
603
|
}
|
|
451
604
|
else {
|
|
452
605
|
const view = this.editor.ctx.get(editorViewCtx);
|
|
@@ -484,7 +637,7 @@ export class InkKitEditor {
|
|
|
484
637
|
}
|
|
485
638
|
table(command, options) {
|
|
486
639
|
this.assertCurrent();
|
|
487
|
-
if (this.
|
|
640
|
+
if (this.literalSurface)
|
|
488
641
|
return false;
|
|
489
642
|
return tableCommand(this.editor.ctx, command, options);
|
|
490
643
|
}
|
|
@@ -517,6 +670,58 @@ export class InkKitEditor {
|
|
|
517
670
|
},
|
|
518
671
|
},
|
|
519
672
|
}));
|
|
673
|
+
copySourceSelection(event) {
|
|
674
|
+
event.preventDefault();
|
|
675
|
+
event.stopPropagation();
|
|
676
|
+
try {
|
|
677
|
+
const snapshot = this.snapshot();
|
|
678
|
+
const from = this.plain.selectionStart, to = this.plain.selectionEnd;
|
|
679
|
+
const selected = sourceSelection(snapshot.text, from, to);
|
|
680
|
+
const parsed = this.editor.ctx.get(parserCtx)(selected.shareable);
|
|
681
|
+
let portable = false;
|
|
682
|
+
parsed.descendants((node) => {
|
|
683
|
+
if (node.type.name === 'image' || isMermaid(node))
|
|
684
|
+
portable = true;
|
|
685
|
+
});
|
|
686
|
+
const cut = event.type === 'cut';
|
|
687
|
+
const epoch = this.documentEpoch;
|
|
688
|
+
if (portable && this.events.clipboard) {
|
|
689
|
+
void this.clipboardSnapshot(false)
|
|
690
|
+
.then(async (content) => {
|
|
691
|
+
if (cut &&
|
|
692
|
+
(content.images.some((image) => image.error) ||
|
|
693
|
+
content.diagrams?.some((diagram) => diagram.error)))
|
|
694
|
+
throw new InkKitError('image-unavailable', 'The selected content could not be cut safely');
|
|
695
|
+
await this.events.clipboard(content);
|
|
696
|
+
if (cut) {
|
|
697
|
+
this.assertCurrent(snapshot.generation);
|
|
698
|
+
if (epoch !== this.documentEpoch ||
|
|
699
|
+
snapshot.revision !== this.revision)
|
|
700
|
+
throw new InkKitError('stale-document', 'Document changed before cutting');
|
|
701
|
+
this.plain.setSelectionRange(from, to);
|
|
702
|
+
this.replaceLiteralSelection('');
|
|
703
|
+
}
|
|
704
|
+
})
|
|
705
|
+
.catch((error) => this.events.error?.(error));
|
|
706
|
+
}
|
|
707
|
+
else {
|
|
708
|
+
const output = clipboardContent(parsed.content, parsed.type.schema);
|
|
709
|
+
if (!event.clipboardData)
|
|
710
|
+
return;
|
|
711
|
+
event.clipboardData.setData('text/plain', output.text);
|
|
712
|
+
event.clipboardData.setData('text/html', output.html);
|
|
713
|
+
if (portable) {
|
|
714
|
+
this.events.error?.(new InkKitError('image-unavailable', 'The host must provide a clipboard handler for portable images and diagrams'));
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
if (cut)
|
|
718
|
+
this.replaceLiteralSelection('');
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
catch (error) {
|
|
722
|
+
this.events.error?.(error instanceof Error ? error : new Error(String(error)));
|
|
723
|
+
}
|
|
724
|
+
}
|
|
520
725
|
static async mount(root, events, options = {}) {
|
|
521
726
|
const instance = new InkKitEditor(events, options);
|
|
522
727
|
instance.root = root;
|
|
@@ -525,7 +730,8 @@ export class InkKitEditor {
|
|
|
525
730
|
context: () => instance.context(),
|
|
526
731
|
adapter: options.images,
|
|
527
732
|
onError: (error) => events.error?.(error instanceof Error ? error : new Error(String(error))),
|
|
528
|
-
literalText: () => instance.
|
|
733
|
+
literalText: () => instance.literalSurface,
|
|
734
|
+
editable: () => !instance.literalSurface,
|
|
529
735
|
});
|
|
530
736
|
instance.editor = await Editor.make()
|
|
531
737
|
.config((ctx) => {
|
|
@@ -566,7 +772,7 @@ export class InkKitEditor {
|
|
|
566
772
|
scrollMargin: { top: 8, right: 0, bottom: 24, left: 0 },
|
|
567
773
|
}));
|
|
568
774
|
})
|
|
569
|
-
.use(caretStatePlugin(events))
|
|
775
|
+
.use(caretStatePlugin(events, () => !instance.literalSurface))
|
|
570
776
|
.use(instance.pasteController.plugin)
|
|
571
777
|
.use(visibleClipboard)
|
|
572
778
|
.use(instance.keymapPlugin)
|
|
@@ -592,7 +798,7 @@ export class InkKitEditor {
|
|
|
592
798
|
configureTableMovement(instance.editor.ctx, () => {
|
|
593
799
|
try {
|
|
594
800
|
instance.assertCurrent();
|
|
595
|
-
return instance.
|
|
801
|
+
return !instance.literalSurface;
|
|
596
802
|
}
|
|
597
803
|
catch {
|
|
598
804
|
return false;
|
|
@@ -603,9 +809,52 @@ export class InkKitEditor {
|
|
|
603
809
|
instance.plain.setAttribute('aria-label', 'Plain text editor');
|
|
604
810
|
instance.plain.hidden = true;
|
|
605
811
|
root.append(instance.plain);
|
|
812
|
+
instance.plain.addEventListener('beforeinput', () => {
|
|
813
|
+
if (instance.ready && instance.literalSurface)
|
|
814
|
+
instance.beforePlainInput = {
|
|
815
|
+
from: instance.plain.selectionStart,
|
|
816
|
+
to: instance.plain.selectionEnd,
|
|
817
|
+
};
|
|
818
|
+
});
|
|
606
819
|
instance.plain.addEventListener('input', () => {
|
|
607
|
-
instance.
|
|
608
|
-
|
|
820
|
+
if (!instance.ready || !instance.literalSurface)
|
|
821
|
+
return;
|
|
822
|
+
try {
|
|
823
|
+
instance.applySource(editedPlainSource(instance.currentText(), instance.plain.value), false, instance.plain.selectionStart, instance.plain.selectionEnd);
|
|
824
|
+
}
|
|
825
|
+
catch (error) {
|
|
826
|
+
events.error?.(error instanceof Error ? error : new Error(String(error)));
|
|
827
|
+
}
|
|
828
|
+
finally {
|
|
829
|
+
instance.beforePlainInput = undefined;
|
|
830
|
+
}
|
|
831
|
+
});
|
|
832
|
+
instance.plain.addEventListener('keydown', (event) => {
|
|
833
|
+
if (instance.plainComposing || !(event.metaKey || event.ctrlKey))
|
|
834
|
+
return;
|
|
835
|
+
if (event.key.toLowerCase() !== 'z' && event.key.toLowerCase() !== 'y')
|
|
836
|
+
return;
|
|
837
|
+
event.preventDefault();
|
|
838
|
+
try {
|
|
839
|
+
if (event.key.toLowerCase() === 'y' || event.shiftKey)
|
|
840
|
+
instance.redo();
|
|
841
|
+
else
|
|
842
|
+
instance.undo();
|
|
843
|
+
}
|
|
844
|
+
catch (error) {
|
|
845
|
+
events.error?.(error instanceof Error ? error : new Error(String(error)));
|
|
846
|
+
}
|
|
847
|
+
});
|
|
848
|
+
instance.plain.addEventListener('paste', (event) => {
|
|
849
|
+
if (!event.clipboardData)
|
|
850
|
+
return;
|
|
851
|
+
event.preventDefault();
|
|
852
|
+
void instance
|
|
853
|
+
.paste({
|
|
854
|
+
text: event.clipboardData.getData('text/plain'),
|
|
855
|
+
markdown: event.clipboardData.getData('text/markdown') || undefined,
|
|
856
|
+
})
|
|
857
|
+
.catch((error) => events.error?.(error));
|
|
609
858
|
});
|
|
610
859
|
instance.plain.addEventListener('compositionstart', () => {
|
|
611
860
|
instance.plainComposing = true;
|
|
@@ -657,6 +906,10 @@ export class InkKitEditor {
|
|
|
657
906
|
instance.copyHandler = (event) => {
|
|
658
907
|
if (instance.formatType === 'txt')
|
|
659
908
|
return;
|
|
909
|
+
if (instance.mode === 'source') {
|
|
910
|
+
instance.copySourceSelection(event);
|
|
911
|
+
return;
|
|
912
|
+
}
|
|
660
913
|
const view = instance.editor.ctx.get(editorViewCtx);
|
|
661
914
|
const fragment = commentSelectionContent(view.state.doc, view.state.selection.from, view.state.selection.to, view.state.selection.content().content);
|
|
662
915
|
const expanded = selectionContent(view.state.doc, fragment);
|
|
@@ -731,36 +984,51 @@ export class InkKitEditor {
|
|
|
731
984
|
this.assertAlive();
|
|
732
985
|
this.pasteController.cancelPending();
|
|
733
986
|
this.documentEpoch += 1;
|
|
987
|
+
this.outlineEpoch += 1;
|
|
988
|
+
this.sourcePreservations = new WeakMap();
|
|
989
|
+
this.mode = 'formatted';
|
|
734
990
|
this.plainComposing = false;
|
|
991
|
+
this.beforePlainInput = undefined;
|
|
735
992
|
this.plainSearch = '';
|
|
993
|
+
this.literalMatch = undefined;
|
|
736
994
|
this.documentId = input.documentId;
|
|
737
995
|
this.formatType = input.format;
|
|
738
996
|
this.revision = 0;
|
|
739
|
-
this.plain.hidden = input.format !== 'txt';
|
|
740
|
-
this.editor.ctx.get(editorViewCtx).dom.parentElement.hidden =
|
|
741
|
-
input.format === 'txt';
|
|
742
997
|
this.plain.value = input.text;
|
|
743
998
|
this.load(input.format === 'txt' ? '' : input.text, input.generation);
|
|
744
999
|
this.originalSource = input.text;
|
|
1000
|
+
this.lastMarkdown = input.text;
|
|
745
1001
|
this.ready = true;
|
|
1002
|
+
this.updateSurface();
|
|
746
1003
|
}
|
|
747
1004
|
reloadDocument(input) {
|
|
748
1005
|
this.assertAlive();
|
|
749
1006
|
const view = this.editor.ctx.get(editorViewCtx);
|
|
750
|
-
const
|
|
1007
|
+
const wasLiteral = this.literalSurface;
|
|
1008
|
+
const previousMode = this.editingMode;
|
|
1009
|
+
const previousFormat = this.formatType;
|
|
1010
|
+
const at = wasLiteral
|
|
751
1011
|
? this.plain.selectionStart
|
|
752
1012
|
: view.state.selection.from;
|
|
1013
|
+
const end = wasLiteral ? this.plain.selectionEnd : at;
|
|
753
1014
|
const top = this.root.scrollTop;
|
|
1015
|
+
const plainTop = this.plain.scrollTop, plainLeft = this.plain.scrollLeft;
|
|
754
1016
|
this.loadDocument(input);
|
|
755
|
-
if (
|
|
756
|
-
|
|
1017
|
+
if (previousFormat === 'md' &&
|
|
1018
|
+
input.format === 'md' &&
|
|
1019
|
+
previousMode === 'source')
|
|
1020
|
+
this.setEditingMode('source', input.generation);
|
|
1021
|
+
if (this.literalSurface)
|
|
1022
|
+
this.plain.setSelectionRange(Math.min(at, this.plain.value.length), Math.min(end, this.plain.value.length));
|
|
757
1023
|
else
|
|
758
1024
|
view.dispatch(view.state.tr.setSelection(Selection.near(view.state.doc.resolve(Math.min(at, view.state.doc.content.size)))));
|
|
759
1025
|
this.root.scrollTop = top;
|
|
1026
|
+
if (wasLiteral && this.literalSurface) {
|
|
1027
|
+
this.plain.scrollTop = plainTop;
|
|
1028
|
+
this.plain.scrollLeft = plainLeft;
|
|
1029
|
+
}
|
|
760
1030
|
}
|
|
761
1031
|
load(markdown, generation) {
|
|
762
|
-
// A loaded document only counts as changed once it is edited, so its
|
|
763
|
-
// canonical form is the baseline, not the text as stored.
|
|
764
1032
|
this.generation = generation;
|
|
765
1033
|
this.originalSource = markdown;
|
|
766
1034
|
this.loading = true;
|
|
@@ -771,8 +1039,7 @@ export class InkKitEditor {
|
|
|
771
1039
|
this.loading = false;
|
|
772
1040
|
}
|
|
773
1041
|
this.preservation = new Preservation(this.editor.ctx, markdown);
|
|
774
|
-
this.
|
|
775
|
-
this.lastMarkdown = this.baseline;
|
|
1042
|
+
this.lastMarkdown = markdown;
|
|
776
1043
|
const view = this.editor.ctx.get(editorViewCtx);
|
|
777
1044
|
// A caret at the end, where writing carries on; at the start it would sit in a first-line heading and
|
|
778
1045
|
// show its marks. The last place text can go, so a document ending in a rule takes a caret above it
|
|
@@ -783,54 +1050,118 @@ export class InkKitEditor {
|
|
|
783
1050
|
view.dispatch(setSearchState(view.state.tr.setSelection(end).scrollIntoView(), new SearchQuery({ search: '' })));
|
|
784
1051
|
this.events.stateChanged(caretState(view.state));
|
|
785
1052
|
}
|
|
786
|
-
find(text) {
|
|
787
|
-
this.assertCurrent();
|
|
788
|
-
if (this.
|
|
789
|
-
const escaped = text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
790
|
-
const matches = text
|
|
791
|
-
? [...this.plain.value.matchAll(new RegExp(escaped, 'giu'))]
|
|
792
|
-
: [];
|
|
1053
|
+
find(text, expectedGeneration) {
|
|
1054
|
+
this.assertCurrent(expectedGeneration);
|
|
1055
|
+
if (this.literalSurface) {
|
|
793
1056
|
const from = this.plainSearch === text
|
|
794
1057
|
? this.plain.selectionEnd
|
|
795
1058
|
: this.plain.selectionStart;
|
|
796
|
-
const
|
|
797
|
-
|
|
798
|
-
|
|
1059
|
+
const raw = this.currentText();
|
|
1060
|
+
const match = findLiteral(raw, text, rawOffset(raw, from));
|
|
1061
|
+
this.literalMatch = undefined;
|
|
1062
|
+
if (match) {
|
|
1063
|
+
// A textarea displays CRLF as one character; retain a match of either raw half for replacement.
|
|
1064
|
+
const start = raw[match.from] === '\n' && raw[match.from - 1] === '\r'
|
|
1065
|
+
? match.from - 1
|
|
1066
|
+
: match.from;
|
|
1067
|
+
const displayedFrom = normalizedOffset(raw, start), displayedTo = normalizedOffset(raw, match.to);
|
|
1068
|
+
this.plain.setSelectionRange(displayedFrom, displayedTo);
|
|
1069
|
+
this.literalMatch = {
|
|
1070
|
+
...match,
|
|
1071
|
+
query: text,
|
|
1072
|
+
displayedFrom,
|
|
1073
|
+
displayedTo,
|
|
1074
|
+
revision: this.revision,
|
|
1075
|
+
epoch: this.documentEpoch,
|
|
1076
|
+
};
|
|
1077
|
+
}
|
|
799
1078
|
else if (this.plainSearch)
|
|
800
1079
|
this.plain.setSelectionRange(this.plain.selectionEnd, this.plain.selectionEnd);
|
|
801
1080
|
this.plainSearch = text;
|
|
1081
|
+
revealSourceCaret(this.plain);
|
|
802
1082
|
return;
|
|
803
1083
|
}
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
1084
|
+
findFormatted(this.editor.ctx.get(editorViewCtx), text);
|
|
1085
|
+
}
|
|
1086
|
+
replace(search, replacement, expectedGeneration) {
|
|
1087
|
+
this.assertCurrent(expectedGeneration);
|
|
1088
|
+
return this.replaceMatches(search, replacement, false) > 0;
|
|
1089
|
+
}
|
|
1090
|
+
replaceAll(search, replacement, expectedGeneration) {
|
|
1091
|
+
this.assertCurrent(expectedGeneration);
|
|
1092
|
+
return this.replaceMatches(search, replacement, true);
|
|
1093
|
+
}
|
|
1094
|
+
replaceMatches(search, replacement, all) {
|
|
1095
|
+
if (this.literalSurface) {
|
|
1096
|
+
const raw = this.currentText();
|
|
1097
|
+
const remembered = this.literalMatch;
|
|
1098
|
+
const selection = remembered &&
|
|
1099
|
+
remembered.query === search &&
|
|
1100
|
+
remembered.revision === this.revision &&
|
|
1101
|
+
remembered.epoch === this.documentEpoch &&
|
|
1102
|
+
remembered.displayedFrom === this.plain.selectionStart &&
|
|
1103
|
+
remembered.displayedTo === this.plain.selectionEnd
|
|
1104
|
+
? { from: remembered.from, to: remembered.to }
|
|
1105
|
+
: {
|
|
1106
|
+
from: rawOffset(raw, this.plain.selectionStart),
|
|
1107
|
+
to: rawOffset(raw, this.plain.selectionEnd),
|
|
1108
|
+
};
|
|
1109
|
+
if (remembered?.query !== search)
|
|
1110
|
+
this.literalMatch = undefined;
|
|
1111
|
+
const result = replaceLiteral(raw, search, replacement, selection, all);
|
|
1112
|
+
if (result.count)
|
|
1113
|
+
this.applySource(result.text, true, normalizedOffset(result.text, result.from), normalizedOffset(result.text, result.to));
|
|
1114
|
+
this.plainSearch = search;
|
|
1115
|
+
return result.count;
|
|
817
1116
|
}
|
|
818
|
-
|
|
819
|
-
|
|
1117
|
+
return replaceFormatted(this.editor.ctx.get(editorViewCtx), search, replacement, (doc) => {
|
|
1118
|
+
this.currentText(doc);
|
|
1119
|
+
}, all);
|
|
1120
|
+
}
|
|
1121
|
+
outlineContext() {
|
|
1122
|
+
return {
|
|
1123
|
+
documentId: this.documentId,
|
|
1124
|
+
generation: this.generation,
|
|
1125
|
+
revision: this.revision,
|
|
1126
|
+
epoch: this.outlineEpoch,
|
|
1127
|
+
mode: this.editingMode,
|
|
1128
|
+
};
|
|
1129
|
+
}
|
|
1130
|
+
headings(expectedGeneration) {
|
|
1131
|
+
this.assertCurrent(expectedGeneration);
|
|
1132
|
+
if (this.formatType === 'txt')
|
|
1133
|
+
return Object.freeze([]);
|
|
1134
|
+
return this.mode === 'source'
|
|
1135
|
+
? collectSourceHeadings(this.editor.ctx, this.plain.value, this.outlineContext())
|
|
1136
|
+
: collectFormattedHeadings(this.editor.ctx.get(editorViewCtx).state.doc, this.outlineContext());
|
|
1137
|
+
}
|
|
1138
|
+
navigateHeading(heading) {
|
|
1139
|
+
this.assertCurrent();
|
|
1140
|
+
if (!heading || typeof heading !== 'object')
|
|
1141
|
+
throw new InkKitError('stale-document', 'The heading is no longer current');
|
|
1142
|
+
this.assertCurrent(heading.generation);
|
|
1143
|
+
if (this.formatType === 'txt')
|
|
1144
|
+
throw new InkKitError('stale-document', 'The heading is no longer current');
|
|
1145
|
+
if (this.mode === 'source') {
|
|
1146
|
+
const position = sourceHeadingPosition(this.editor.ctx, this.plain.value, heading, this.outlineContext());
|
|
1147
|
+
this.plain.setSelectionRange(position, position);
|
|
1148
|
+
this.plain.focus({ preventScroll: true });
|
|
1149
|
+
revealSourceCaret(this.plain);
|
|
1150
|
+
return true;
|
|
820
1151
|
}
|
|
821
|
-
|
|
1152
|
+
return navigateFormattedHeading(this.editor.ctx.get(editorViewCtx), heading, this.outlineContext());
|
|
822
1153
|
}
|
|
823
1154
|
/** Buffered native typing follows the same input rules as direct typing. */
|
|
824
1155
|
insertText(text, generation) {
|
|
825
1156
|
this.assertAlive();
|
|
826
|
-
if ((this.
|
|
1157
|
+
if ((this.literalSurface
|
|
827
1158
|
? this.plainComposing
|
|
828
1159
|
: this.editor.ctx.get(editorViewCtx).composing) &&
|
|
829
1160
|
generation === this.generation)
|
|
830
1161
|
return false;
|
|
831
1162
|
this.assertCurrent(generation);
|
|
832
|
-
if (this.
|
|
833
|
-
this.
|
|
1163
|
+
if (this.literalSurface) {
|
|
1164
|
+
this.replaceLiteralSelection(text, false);
|
|
834
1165
|
return true;
|
|
835
1166
|
}
|
|
836
1167
|
const view = this.editor.ctx.get(editorViewCtx);
|
|
@@ -846,14 +1177,38 @@ export class InkKitEditor {
|
|
|
846
1177
|
/** Let existing keymaps handle buffered editing commands before native fallback. */
|
|
847
1178
|
keyDown(key, code, metaKey, ctrlKey, altKey, shiftKey, generation) {
|
|
848
1179
|
this.assertAlive();
|
|
849
|
-
if ((this.
|
|
1180
|
+
if ((this.literalSurface
|
|
850
1181
|
? this.plainComposing
|
|
851
1182
|
: this.editor.ctx.get(editorViewCtx).composing) &&
|
|
852
1183
|
generation === this.generation)
|
|
853
1184
|
return false;
|
|
854
1185
|
this.assertCurrent(generation);
|
|
855
|
-
if (this.
|
|
1186
|
+
if (this.literalSurface) {
|
|
1187
|
+
if ((metaKey || ctrlKey) && key.toLowerCase() === 'z')
|
|
1188
|
+
return shiftKey ? this.redo(generation) : this.undo(generation);
|
|
1189
|
+
if ((metaKey || ctrlKey) && key.toLowerCase() === 'y')
|
|
1190
|
+
return this.redo(generation);
|
|
1191
|
+
if (metaKey || ctrlKey || altKey)
|
|
1192
|
+
return false;
|
|
1193
|
+
if (key === 'Enter' || key === 'Tab') {
|
|
1194
|
+
this.replaceLiteralSelection(key === 'Enter' ? '\n' : '\t', false);
|
|
1195
|
+
return true;
|
|
1196
|
+
}
|
|
1197
|
+
if (key === 'Backspace' || key === 'Delete') {
|
|
1198
|
+
const start = this.plain.selectionStart, end = this.plain.selectionEnd;
|
|
1199
|
+
if (start === end) {
|
|
1200
|
+
if (key === 'Backspace' && start > 0)
|
|
1201
|
+
this.plain.setSelectionRange(start - [...this.plain.value.slice(0, start)].at(-1).length, end);
|
|
1202
|
+
else if (key === 'Delete' && end < this.plain.value.length)
|
|
1203
|
+
this.plain.setSelectionRange(start, end + [...this.plain.value.slice(end)][0].length);
|
|
1204
|
+
else
|
|
1205
|
+
return false;
|
|
1206
|
+
}
|
|
1207
|
+
this.replaceLiteralSelection('', false);
|
|
1208
|
+
return true;
|
|
1209
|
+
}
|
|
856
1210
|
return false;
|
|
1211
|
+
}
|
|
857
1212
|
const view = this.editor.ctx.get(editorViewCtx);
|
|
858
1213
|
if (view.composing)
|
|
859
1214
|
return false;
|
|
@@ -873,7 +1228,7 @@ export class InkKitEditor {
|
|
|
873
1228
|
this.assertAlive();
|
|
874
1229
|
if (!this.ready)
|
|
875
1230
|
throw new InkKitError('not-ready', 'No document has been loaded');
|
|
876
|
-
if (this.
|
|
1231
|
+
if (this.literalSurface)
|
|
877
1232
|
this.plain.focus();
|
|
878
1233
|
else
|
|
879
1234
|
this.editor.ctx.get(editorViewCtx).focus();
|
|
@@ -899,7 +1254,7 @@ export class InkKitEditor {
|
|
|
899
1254
|
this.assertCurrent();
|
|
900
1255
|
if (paths.length === 0)
|
|
901
1256
|
return;
|
|
902
|
-
if (this.
|
|
1257
|
+
if (this.literalSurface) {
|
|
903
1258
|
this.pasteAsPlainText(paths.join('\n\n'));
|
|
904
1259
|
return;
|
|
905
1260
|
}
|
|
@@ -910,7 +1265,7 @@ export class InkKitEditor {
|
|
|
910
1265
|
this.assertCurrent();
|
|
911
1266
|
if (!images.length)
|
|
912
1267
|
return;
|
|
913
|
-
if (!this.options.images || this.
|
|
1268
|
+
if (!this.options.images || this.literalSurface)
|
|
914
1269
|
throw new InkKitError('image-unavailable', 'This editor has no image adapter');
|
|
915
1270
|
const view = this.editor.ctx.get(editorViewCtx);
|
|
916
1271
|
const { schema } = view.state;
|
|
@@ -1066,7 +1421,7 @@ export class InkKitEditor {
|
|
|
1066
1421
|
}
|
|
1067
1422
|
format(command, arg) {
|
|
1068
1423
|
this.assertCurrent();
|
|
1069
|
-
if (this.
|
|
1424
|
+
if (this.literalSurface)
|
|
1070
1425
|
return;
|
|
1071
1426
|
const run = (cmd, payload) => this.editor.action(callCommand(cmd, payload));
|
|
1072
1427
|
const state = caretState(this.editor.ctx.get(editorViewCtx).state);
|
|
@@ -1140,7 +1495,7 @@ export class InkKitEditor {
|
|
|
1140
1495
|
}
|
|
1141
1496
|
editReferenceDefinition(label, destination, title) {
|
|
1142
1497
|
this.assertCurrent();
|
|
1143
|
-
if (this.
|
|
1498
|
+
if (this.literalSurface)
|
|
1144
1499
|
return false;
|
|
1145
1500
|
const view = this.editor.ctx.get(editorViewCtx);
|
|
1146
1501
|
const definitions = referenceDefinitions(view.state.doc);
|
|
@@ -1157,7 +1512,7 @@ export class InkKitEditor {
|
|
|
1157
1512
|
}
|
|
1158
1513
|
insertFootnote(label) {
|
|
1159
1514
|
this.assertCurrent();
|
|
1160
|
-
if (this.
|
|
1515
|
+
if (this.literalSurface)
|
|
1161
1516
|
return;
|
|
1162
1517
|
const view = this.editor.ctx.get(editorViewCtx);
|
|
1163
1518
|
const definitions = footnoteDefinitions(view.state.doc);
|
|
@@ -1192,7 +1547,7 @@ export class InkKitEditor {
|
|
|
1192
1547
|
}
|
|
1193
1548
|
navigateFootnote(target) {
|
|
1194
1549
|
this.assertCurrent();
|
|
1195
|
-
if (this.
|
|
1550
|
+
if (this.literalSurface)
|
|
1196
1551
|
return false;
|
|
1197
1552
|
const view = this.editor.ctx.get(editorViewCtx);
|
|
1198
1553
|
const { $from } = view.state.selection;
|