@aicayzer/inkkit 0.0.5 → 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 +4 -0
- package/dist/callouts.d.ts +2 -0
- package/dist/callouts.js +11 -0
- package/dist/dialect.js +24 -4
- package/dist/editor.d.ts +27 -8
- package/dist/editor.js +460 -96
- package/dist/index.d.ts +3 -2
- package/dist/outline.d.ts +22 -0
- package/dist/outline.js +79 -0
- package/dist/paste.js +56 -6
- package/dist/preserve.js +38 -11
- 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/dist/spreadsheet.d.ts +3 -0
- package/dist/spreadsheet.js +275 -0
- package/dist/table-alignment.d.ts +2 -0
- package/dist/table-alignment.js +46 -0
- package/dist/tables.d.ts +15 -4
- package/dist/tables.js +168 -2
- 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,19 +13,22 @@ 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';
|
|
20
|
-
import { tablePlugins, tableCommand } from './tables.js';
|
|
22
|
+
import { tablePlugins, tableCommand, configureTableMovement, } from './tables.js';
|
|
21
23
|
import { InkKitError, } from './types.js';
|
|
22
24
|
import { highlightPlugin } from './highlight.js';
|
|
23
25
|
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)
|
|
@@ -589,14 +795,66 @@ export class InkKitEditor {
|
|
|
589
795
|
.use($prose(() => search()))
|
|
590
796
|
.use(options.images ? imageView(options.images) : [])
|
|
591
797
|
.create();
|
|
798
|
+
configureTableMovement(instance.editor.ctx, () => {
|
|
799
|
+
try {
|
|
800
|
+
instance.assertCurrent();
|
|
801
|
+
return !instance.literalSurface;
|
|
802
|
+
}
|
|
803
|
+
catch {
|
|
804
|
+
return false;
|
|
805
|
+
}
|
|
806
|
+
});
|
|
592
807
|
instance.plain = document.createElement('textarea');
|
|
593
808
|
instance.plain.className = 'inkkit-plain';
|
|
594
809
|
instance.plain.setAttribute('aria-label', 'Plain text editor');
|
|
595
810
|
instance.plain.hidden = true;
|
|
596
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
|
+
});
|
|
597
819
|
instance.plain.addEventListener('input', () => {
|
|
598
|
-
instance.
|
|
599
|
-
|
|
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));
|
|
600
858
|
});
|
|
601
859
|
instance.plain.addEventListener('compositionstart', () => {
|
|
602
860
|
instance.plainComposing = true;
|
|
@@ -648,6 +906,10 @@ export class InkKitEditor {
|
|
|
648
906
|
instance.copyHandler = (event) => {
|
|
649
907
|
if (instance.formatType === 'txt')
|
|
650
908
|
return;
|
|
909
|
+
if (instance.mode === 'source') {
|
|
910
|
+
instance.copySourceSelection(event);
|
|
911
|
+
return;
|
|
912
|
+
}
|
|
651
913
|
const view = instance.editor.ctx.get(editorViewCtx);
|
|
652
914
|
const fragment = commentSelectionContent(view.state.doc, view.state.selection.from, view.state.selection.to, view.state.selection.content().content);
|
|
653
915
|
const expanded = selectionContent(view.state.doc, fragment);
|
|
@@ -722,36 +984,51 @@ export class InkKitEditor {
|
|
|
722
984
|
this.assertAlive();
|
|
723
985
|
this.pasteController.cancelPending();
|
|
724
986
|
this.documentEpoch += 1;
|
|
987
|
+
this.outlineEpoch += 1;
|
|
988
|
+
this.sourcePreservations = new WeakMap();
|
|
989
|
+
this.mode = 'formatted';
|
|
725
990
|
this.plainComposing = false;
|
|
991
|
+
this.beforePlainInput = undefined;
|
|
726
992
|
this.plainSearch = '';
|
|
993
|
+
this.literalMatch = undefined;
|
|
727
994
|
this.documentId = input.documentId;
|
|
728
995
|
this.formatType = input.format;
|
|
729
996
|
this.revision = 0;
|
|
730
|
-
this.plain.hidden = input.format !== 'txt';
|
|
731
|
-
this.editor.ctx.get(editorViewCtx).dom.parentElement.hidden =
|
|
732
|
-
input.format === 'txt';
|
|
733
997
|
this.plain.value = input.text;
|
|
734
998
|
this.load(input.format === 'txt' ? '' : input.text, input.generation);
|
|
735
999
|
this.originalSource = input.text;
|
|
1000
|
+
this.lastMarkdown = input.text;
|
|
736
1001
|
this.ready = true;
|
|
1002
|
+
this.updateSurface();
|
|
737
1003
|
}
|
|
738
1004
|
reloadDocument(input) {
|
|
739
1005
|
this.assertAlive();
|
|
740
1006
|
const view = this.editor.ctx.get(editorViewCtx);
|
|
741
|
-
const
|
|
1007
|
+
const wasLiteral = this.literalSurface;
|
|
1008
|
+
const previousMode = this.editingMode;
|
|
1009
|
+
const previousFormat = this.formatType;
|
|
1010
|
+
const at = wasLiteral
|
|
742
1011
|
? this.plain.selectionStart
|
|
743
1012
|
: view.state.selection.from;
|
|
1013
|
+
const end = wasLiteral ? this.plain.selectionEnd : at;
|
|
744
1014
|
const top = this.root.scrollTop;
|
|
1015
|
+
const plainTop = this.plain.scrollTop, plainLeft = this.plain.scrollLeft;
|
|
745
1016
|
this.loadDocument(input);
|
|
746
|
-
if (
|
|
747
|
-
|
|
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));
|
|
748
1023
|
else
|
|
749
1024
|
view.dispatch(view.state.tr.setSelection(Selection.near(view.state.doc.resolve(Math.min(at, view.state.doc.content.size)))));
|
|
750
1025
|
this.root.scrollTop = top;
|
|
1026
|
+
if (wasLiteral && this.literalSurface) {
|
|
1027
|
+
this.plain.scrollTop = plainTop;
|
|
1028
|
+
this.plain.scrollLeft = plainLeft;
|
|
1029
|
+
}
|
|
751
1030
|
}
|
|
752
1031
|
load(markdown, generation) {
|
|
753
|
-
// A loaded document only counts as changed once it is edited, so its
|
|
754
|
-
// canonical form is the baseline, not the text as stored.
|
|
755
1032
|
this.generation = generation;
|
|
756
1033
|
this.originalSource = markdown;
|
|
757
1034
|
this.loading = true;
|
|
@@ -762,8 +1039,7 @@ export class InkKitEditor {
|
|
|
762
1039
|
this.loading = false;
|
|
763
1040
|
}
|
|
764
1041
|
this.preservation = new Preservation(this.editor.ctx, markdown);
|
|
765
|
-
this.
|
|
766
|
-
this.lastMarkdown = this.baseline;
|
|
1042
|
+
this.lastMarkdown = markdown;
|
|
767
1043
|
const view = this.editor.ctx.get(editorViewCtx);
|
|
768
1044
|
// A caret at the end, where writing carries on; at the start it would sit in a first-line heading and
|
|
769
1045
|
// show its marks. The last place text can go, so a document ending in a rule takes a caret above it
|
|
@@ -774,54 +1050,118 @@ export class InkKitEditor {
|
|
|
774
1050
|
view.dispatch(setSearchState(view.state.tr.setSelection(end).scrollIntoView(), new SearchQuery({ search: '' })));
|
|
775
1051
|
this.events.stateChanged(caretState(view.state));
|
|
776
1052
|
}
|
|
777
|
-
find(text) {
|
|
778
|
-
this.assertCurrent();
|
|
779
|
-
if (this.
|
|
780
|
-
const escaped = text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
781
|
-
const matches = text
|
|
782
|
-
? [...this.plain.value.matchAll(new RegExp(escaped, 'giu'))]
|
|
783
|
-
: [];
|
|
1053
|
+
find(text, expectedGeneration) {
|
|
1054
|
+
this.assertCurrent(expectedGeneration);
|
|
1055
|
+
if (this.literalSurface) {
|
|
784
1056
|
const from = this.plainSearch === text
|
|
785
1057
|
? this.plain.selectionEnd
|
|
786
1058
|
: this.plain.selectionStart;
|
|
787
|
-
const
|
|
788
|
-
|
|
789
|
-
|
|
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
|
+
}
|
|
790
1078
|
else if (this.plainSearch)
|
|
791
1079
|
this.plain.setSelectionRange(this.plain.selectionEnd, this.plain.selectionEnd);
|
|
792
1080
|
this.plainSearch = text;
|
|
1081
|
+
revealSourceCaret(this.plain);
|
|
793
1082
|
return;
|
|
794
1083
|
}
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
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;
|
|
808
1116
|
}
|
|
809
|
-
|
|
810
|
-
|
|
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;
|
|
811
1151
|
}
|
|
812
|
-
|
|
1152
|
+
return navigateFormattedHeading(this.editor.ctx.get(editorViewCtx), heading, this.outlineContext());
|
|
813
1153
|
}
|
|
814
1154
|
/** Buffered native typing follows the same input rules as direct typing. */
|
|
815
1155
|
insertText(text, generation) {
|
|
816
1156
|
this.assertAlive();
|
|
817
|
-
if ((this.
|
|
1157
|
+
if ((this.literalSurface
|
|
818
1158
|
? this.plainComposing
|
|
819
1159
|
: this.editor.ctx.get(editorViewCtx).composing) &&
|
|
820
1160
|
generation === this.generation)
|
|
821
1161
|
return false;
|
|
822
1162
|
this.assertCurrent(generation);
|
|
823
|
-
if (this.
|
|
824
|
-
this.
|
|
1163
|
+
if (this.literalSurface) {
|
|
1164
|
+
this.replaceLiteralSelection(text, false);
|
|
825
1165
|
return true;
|
|
826
1166
|
}
|
|
827
1167
|
const view = this.editor.ctx.get(editorViewCtx);
|
|
@@ -837,14 +1177,38 @@ export class InkKitEditor {
|
|
|
837
1177
|
/** Let existing keymaps handle buffered editing commands before native fallback. */
|
|
838
1178
|
keyDown(key, code, metaKey, ctrlKey, altKey, shiftKey, generation) {
|
|
839
1179
|
this.assertAlive();
|
|
840
|
-
if ((this.
|
|
1180
|
+
if ((this.literalSurface
|
|
841
1181
|
? this.plainComposing
|
|
842
1182
|
: this.editor.ctx.get(editorViewCtx).composing) &&
|
|
843
1183
|
generation === this.generation)
|
|
844
1184
|
return false;
|
|
845
1185
|
this.assertCurrent(generation);
|
|
846
|
-
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
|
+
}
|
|
847
1210
|
return false;
|
|
1211
|
+
}
|
|
848
1212
|
const view = this.editor.ctx.get(editorViewCtx);
|
|
849
1213
|
if (view.composing)
|
|
850
1214
|
return false;
|
|
@@ -864,7 +1228,7 @@ export class InkKitEditor {
|
|
|
864
1228
|
this.assertAlive();
|
|
865
1229
|
if (!this.ready)
|
|
866
1230
|
throw new InkKitError('not-ready', 'No document has been loaded');
|
|
867
|
-
if (this.
|
|
1231
|
+
if (this.literalSurface)
|
|
868
1232
|
this.plain.focus();
|
|
869
1233
|
else
|
|
870
1234
|
this.editor.ctx.get(editorViewCtx).focus();
|
|
@@ -890,7 +1254,7 @@ export class InkKitEditor {
|
|
|
890
1254
|
this.assertCurrent();
|
|
891
1255
|
if (paths.length === 0)
|
|
892
1256
|
return;
|
|
893
|
-
if (this.
|
|
1257
|
+
if (this.literalSurface) {
|
|
894
1258
|
this.pasteAsPlainText(paths.join('\n\n'));
|
|
895
1259
|
return;
|
|
896
1260
|
}
|
|
@@ -901,7 +1265,7 @@ export class InkKitEditor {
|
|
|
901
1265
|
this.assertCurrent();
|
|
902
1266
|
if (!images.length)
|
|
903
1267
|
return;
|
|
904
|
-
if (!this.options.images || this.
|
|
1268
|
+
if (!this.options.images || this.literalSurface)
|
|
905
1269
|
throw new InkKitError('image-unavailable', 'This editor has no image adapter');
|
|
906
1270
|
const view = this.editor.ctx.get(editorViewCtx);
|
|
907
1271
|
const { schema } = view.state;
|
|
@@ -1057,7 +1421,7 @@ export class InkKitEditor {
|
|
|
1057
1421
|
}
|
|
1058
1422
|
format(command, arg) {
|
|
1059
1423
|
this.assertCurrent();
|
|
1060
|
-
if (this.
|
|
1424
|
+
if (this.literalSurface)
|
|
1061
1425
|
return;
|
|
1062
1426
|
const run = (cmd, payload) => this.editor.action(callCommand(cmd, payload));
|
|
1063
1427
|
const state = caretState(this.editor.ctx.get(editorViewCtx).state);
|
|
@@ -1131,7 +1495,7 @@ export class InkKitEditor {
|
|
|
1131
1495
|
}
|
|
1132
1496
|
editReferenceDefinition(label, destination, title) {
|
|
1133
1497
|
this.assertCurrent();
|
|
1134
|
-
if (this.
|
|
1498
|
+
if (this.literalSurface)
|
|
1135
1499
|
return false;
|
|
1136
1500
|
const view = this.editor.ctx.get(editorViewCtx);
|
|
1137
1501
|
const definitions = referenceDefinitions(view.state.doc);
|
|
@@ -1148,7 +1512,7 @@ export class InkKitEditor {
|
|
|
1148
1512
|
}
|
|
1149
1513
|
insertFootnote(label) {
|
|
1150
1514
|
this.assertCurrent();
|
|
1151
|
-
if (this.
|
|
1515
|
+
if (this.literalSurface)
|
|
1152
1516
|
return;
|
|
1153
1517
|
const view = this.editor.ctx.get(editorViewCtx);
|
|
1154
1518
|
const definitions = footnoteDefinitions(view.state.doc);
|
|
@@ -1183,7 +1547,7 @@ export class InkKitEditor {
|
|
|
1183
1547
|
}
|
|
1184
1548
|
navigateFootnote(target) {
|
|
1185
1549
|
this.assertCurrent();
|
|
1186
|
-
if (this.
|
|
1550
|
+
if (this.literalSurface)
|
|
1187
1551
|
return false;
|
|
1188
1552
|
const view = this.editor.ctx.get(editorViewCtx);
|
|
1189
1553
|
const { $from } = view.state.selection;
|