@aicayzer/inkkit 0.0.13 → 0.0.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/images.js CHANGED
@@ -3,6 +3,7 @@ import { closeHistory } from '@milkdown/kit/prose/history';
3
3
  import { $view } from '@milkdown/kit/utils';
4
4
  import { FileResolution } from './files.js';
5
5
  import { fileReference } from './linked-syntax.js';
6
+ import { imageBlockSchema } from './image-blocks.js';
6
7
  /** A width rides in the alt text, as `![a picture|400](images/….png)`. Other Markdown readers
7
8
  * show the picture and keep the number without requiring a private extension. */
8
9
  export function splitAlt(alt) {
@@ -23,6 +24,8 @@ class ImageView {
23
24
  runtime;
24
25
  canMutate;
25
26
  dom;
27
+ /** The card or picture, which a standalone image's block row holds. */
28
+ element;
26
29
  image;
27
30
  media;
28
31
  pdf;
@@ -37,8 +40,15 @@ class ImageView {
37
40
  this.adapter = adapter;
38
41
  this.runtime = runtime;
39
42
  this.canMutate = canMutate;
40
- this.dom = document.createElement('span');
41
- this.dom.className = 'image';
43
+ this.element = document.createElement('span');
44
+ this.element.className = 'image';
45
+ if (node.isBlock) {
46
+ this.dom = document.createElement('div');
47
+ this.dom.className = 'inkkit-image-block';
48
+ this.dom.append(this.element);
49
+ }
50
+ else
51
+ this.dom = this.element;
42
52
  this.view.dom.addEventListener('inkkit-cancel-resize', this.cancelResize);
43
53
  this.view.dom.addEventListener('inkkit-media-policy', this.mediaPolicy);
44
54
  if (runtime) {
@@ -143,19 +153,34 @@ class ImageView {
143
153
  this.runtime?.error?.(error instanceof Error ? error : new Error(String(error)));
144
154
  }
145
155
  card(label, status, retry = false) {
156
+ const footer = document.createElement('span');
157
+ footer.className = 'inkkit-file-footer';
158
+ const name = document.createElement('span');
159
+ name.className = 'inkkit-file-name';
146
160
  const text = document.createElement('span');
147
161
  text.className = 'inkkit-file-label';
148
- text.textContent = status ? `${label}: ${status}` : label;
149
- if (status)
150
- text.setAttribute('role', 'status');
151
- this.dom.append(text);
152
- if (retry && this.runtime)
153
- this.dom.append(this.button(this.runtime.labels.fileRetry, () => this.resolution?.retry()));
162
+ text.textContent = label;
163
+ text.title = label;
164
+ name.append(text);
154
165
  if (this.runtime?.adapter.open) {
155
- const open = this.button(this.runtime.labels.fileOpen, this.activate);
166
+ const open = this.button('', this.activate);
167
+ open.classList.add('inkkit-file-open');
156
168
  open.setAttribute('aria-label', `${this.runtime.labels.fileOpen} ${label}`);
157
- this.dom.append(open);
169
+ open.title = this.runtime.labels.fileOpen;
170
+ open.append(openArrow());
171
+ name.append(open);
158
172
  }
173
+ footer.append(name);
174
+ if (status) {
175
+ const description = document.createElement('span');
176
+ description.className = 'inkkit-file-status';
177
+ description.setAttribute('role', 'status');
178
+ description.textContent = status;
179
+ footer.append(description);
180
+ }
181
+ if (retry && this.runtime)
182
+ footer.append(this.button(this.runtime.labels.fileRetry, () => this.resolution?.retry()));
183
+ this.element.append(footer);
159
184
  }
160
185
  failPreview() {
161
186
  this.previewFailed = true;
@@ -174,11 +199,11 @@ class ImageView {
174
199
  }
175
200
  : splitAlt(String(this.node.attrs.alt ?? ''));
176
201
  this.width = width;
177
- this.dom.textContent = '';
202
+ this.element.textContent = '';
178
203
  this.image = undefined;
179
204
  if (this.runtime) {
180
- this.dom.classList.add('inkkit-file');
181
- this.dom.oncontextmenu = (event) => {
205
+ this.element.classList.add('inkkit-file');
206
+ this.element.oncontextmenu = (event) => {
182
207
  if (!this.runtime?.adapter.contextMenu || !this.active())
183
208
  return;
184
209
  const captured = this.runtime.capture();
@@ -196,11 +221,11 @@ class ImageView {
196
221
  const label = state.status === 'resolved'
197
222
  ? state.presentation.label || alt || reference.reference
198
223
  : alt || reference.reference;
199
- this.dom.setAttribute('aria-label', label);
200
- this.dom.dataset.inkkitFileState = this.previewFailed
224
+ this.element.setAttribute('aria-label', label);
225
+ this.element.dataset.inkkitFileState = this.previewFailed
201
226
  ? 'error'
202
227
  : state.status;
203
- this.dom.dataset.inkkitFileKind =
228
+ this.element.dataset.inkkitFileKind =
204
229
  state.status === 'resolved' ? state.presentation.kind : 'file';
205
230
  if (this.previewFailed) {
206
231
  this.card(label, this.runtime.labels.filePreviewUnavailable, true);
@@ -238,7 +263,7 @@ class ImageView {
238
263
  });
239
264
  media.src = presentation.url;
240
265
  this.media = media;
241
- this.dom.append(media);
266
+ this.element.append(media);
242
267
  this.card(label);
243
268
  }
244
269
  else if (presentation.kind === 'pdf') {
@@ -254,7 +279,7 @@ class ImageView {
254
279
  });
255
280
  pdf.src = presentation.url;
256
281
  this.pdf = pdf;
257
- this.dom.append(pdf);
282
+ this.element.append(pdf);
258
283
  this.card(label, this.runtime.labels.filePDFPreview);
259
284
  }
260
285
  else
@@ -263,11 +288,11 @@ class ImageView {
263
288
  }
264
289
  const presentation = this.adapter?.presentation(String(this.node.attrs.src ?? ''));
265
290
  if (!presentation) {
266
- this.dom.classList.add('image-absent');
267
- this.dom.textContent = alt || String(this.node.attrs.src ?? '');
291
+ this.element.classList.add('image-absent');
292
+ this.element.textContent = alt || String(this.node.attrs.src ?? '');
268
293
  return;
269
294
  }
270
- this.dom.classList.remove('image-absent');
295
+ this.element.classList.remove('image-absent');
271
296
  this.renderImage(presentation.url, alt, width);
272
297
  }
273
298
  renderImage(url, alt, width) {
@@ -284,10 +309,14 @@ class ImageView {
284
309
  });
285
310
  image.src = url;
286
311
  this.image = image;
312
+ const preview = document.createElement('span');
313
+ preview.className = 'inkkit-image-preview';
314
+ preview.append(image);
315
+ // The grip sits on the whole card, so it centres on the picture and its name together.
287
316
  const handle = document.createElement('span');
288
317
  handle.className = 'image-handle';
289
318
  handle.addEventListener('pointerdown', this.startResize);
290
- this.dom.append(image, handle);
319
+ this.element.append(preview, handle);
291
320
  }
292
321
  startResize = (event) => {
293
322
  event.preventDefault();
@@ -352,6 +381,18 @@ class ImageView {
352
381
  return this.view.editable && !this.view.composing && this.canMutate();
353
382
  }
354
383
  }
384
+ const svg = 'http://www.w3.org/2000/svg';
385
+ function openArrow() {
386
+ const icon = document.createElementNS(svg, 'svg');
387
+ icon.setAttribute('viewBox', '0 0 12 12');
388
+ icon.setAttribute('aria-hidden', 'true');
389
+ icon.setAttribute('focusable', 'false');
390
+ const path = document.createElementNS(svg, 'path');
391
+ path.setAttribute('d', 'M4 2.5h5.5V8M9.5 2.5 2.5 9.5');
392
+ icon.append(path);
393
+ return icon;
394
+ }
355
395
  export function imageView(adapter, runtime, canMutate = () => true) {
356
- return $view(imageSchema.node, () => (node, view, getPos) => new ImageView(node, view, getPos, adapter, runtime, canMutate));
396
+ const view = (schema) => $view(schema.node, () => (node, view, getPos) => new ImageView(node, view, getPos, adapter, runtime, canMutate));
397
+ return [view(imageSchema), view(imageBlockSchema)];
357
398
  }
package/dist/paste.d.ts CHANGED
@@ -12,11 +12,13 @@ export interface PasteControllerOptions {
12
12
  export declare class PasteController {
13
13
  private readonly options;
14
14
  private active?;
15
+ private readonly key;
15
16
  private dead;
16
17
  get pending(): boolean;
17
18
  readonly plugin: import("@milkdown/kit/utils").$Prose;
18
19
  constructor(options: PasteControllerOptions);
19
20
  paste(input: ClipboardInput): Promise<void>;
21
+ private anchor;
20
22
  cancelPending(): void;
21
23
  destroy(): void;
22
24
  private assertContext;
package/dist/paste.js CHANGED
@@ -3,11 +3,13 @@ import { DOMParser as ProseParser, DOMSerializer, Fragment, Slice, } from '@milk
3
3
  import { Plugin, PluginKey } from '@milkdown/kit/prose/state';
4
4
  import { $prose } from '@milkdown/kit/utils';
5
5
  import { pasteSpreadsheet } from './spreadsheet.js';
6
+ import { isImage } from './image-blocks.js';
6
7
  import { avoidReferenceCollisions, markdownFromHTML, } from './reference-clipboard.js';
7
8
  import { InkKitError, } from './types.js';
8
9
  export class PasteController {
9
10
  options;
10
11
  active;
12
+ key = new PluginKey('inkkitPaste');
11
13
  dead = false;
12
14
  get pending() {
13
15
  return this.active != null;
@@ -16,16 +18,18 @@ export class PasteController {
16
18
  constructor(options) {
17
19
  this.options = options;
18
20
  this.plugin = $prose(() => new Plugin({
19
- key: new PluginKey('inkkitPaste'),
21
+ key: this.key,
20
22
  state: {
21
23
  init: () => null,
22
- apply: (transaction) => {
23
- if (this.active) {
24
- const empty = this.active.from === this.active.to;
25
- this.active.from = transaction.mapping.map(this.active.from, 1);
26
- this.active.to = transaction.mapping.map(this.active.to, empty ? 1 : -1);
27
- }
28
- return null;
24
+ apply: (transaction, previous) => {
25
+ if (!this.active)
26
+ return null;
27
+ const anchor = previous?.operation === this.active ? previous : this.active;
28
+ return {
29
+ operation: this.active,
30
+ from: transaction.mapping.map(anchor.from, 1),
31
+ to: transaction.mapping.map(anchor.to, anchor.from === anchor.to ? 1 : -1),
32
+ };
29
33
  },
30
34
  },
31
35
  props: {
@@ -68,6 +72,10 @@ export class PasteController {
68
72
  paste(input) {
69
73
  return this.run(input);
70
74
  }
75
+ anchor(operation) {
76
+ const mapped = this.key.getState(this.options.ctx().get(editorViewCtx).state);
77
+ return mapped?.operation === operation ? mapped : operation;
78
+ }
71
79
  cancelPending() {
72
80
  this.active = undefined;
73
81
  this.options.onPendingChanged?.();
@@ -127,7 +135,7 @@ export class PasteController {
127
135
  (input.html ? markdownFromHTML(input.html) : undefined));
128
136
  if (plainText) {
129
137
  if (view.state.selection.$from.parent.type.spec.code) {
130
- const anchor = operation;
138
+ const anchor = this.anchor(operation);
131
139
  view.dispatch(view.state.tr.insertText(input.text, anchor.from, anchor.to));
132
140
  return;
133
141
  }
@@ -327,14 +335,18 @@ export class PasteController {
327
335
  const parsed = ctx.get(parserCtx)(markdown);
328
336
  let imageIndex = 0;
329
337
  const hydrate = (node) => {
330
- if (node.type.name === 'image') {
338
+ if (isImage(node)) {
331
339
  const slot = /^inkkit-clipboard-image:(\d+)$/.exec(String(node.attrs.src));
332
340
  const position = slot ? Number(slot[1]) : imageIndex++;
333
341
  const element = elements.some((element) => element.hasAttribute('data-inkkit-image-slot'))
334
342
  ? elements.find((element) => Number(element.getAttribute('data-inkkit-authored-image-slot') ?? element.getAttribute('data-inkkit-image-slot')) === position)
335
343
  : elements[position];
336
- if (!element || !container.contains(element))
337
- return schema.text(`[${node.attrs.alt || 'Image'}: image unavailable]`);
344
+ if (!element || !container.contains(element)) {
345
+ const text = schema.text(`[${node.attrs.alt || 'Image'}: image unavailable]`);
346
+ return node.isBlock
347
+ ? schema.nodes.paragraph.create(null, text)
348
+ : text;
349
+ }
338
350
  return node.type.create({ ...node.attrs, src: element.getAttribute('src') }, null, node.marks);
339
351
  }
340
352
  if (node.isText)
@@ -389,8 +401,8 @@ export class PasteController {
389
401
  !selection.empty &&
390
402
  /^https?:\/\/\S+$/.test(text) &&
391
403
  schema.marks.link) {
392
- const anchor = operation;
393
- const mark = schema.marks.link.create({ href: text, title: '' });
404
+ const anchor = this.anchor(operation);
405
+ const mark = schema.marks.link.create({ href: text });
394
406
  view.dispatch(view.state.tr
395
407
  .addMark(anchor.from, anchor.to, mark)
396
408
  .removeStoredMark(mark));
@@ -402,7 +414,7 @@ export class PasteController {
402
414
  this.assertContext(context, operation);
403
415
  if (!view.editable || this.options.editable?.() === false)
404
416
  return;
405
- const anchor = operation;
417
+ const anchor = this.anchor(operation);
406
418
  if (!plainText) {
407
419
  const valid = slice.content.firstChild?.isInline
408
420
  ? Fragment.from(schema.nodes.paragraph.create(null, slice.content))
@@ -10,7 +10,11 @@ export declare class Preservation {
10
10
  private readonly baseline;
11
11
  private readonly blocks;
12
12
  private readonly lineEnding;
13
+ private readonly originalForms;
13
14
  private readonly forms;
15
+ private readonly textBlockEdits;
14
16
  constructor(ctx: Ctx, source: string);
17
+ private retainBlock;
18
+ private independentTextBlocks;
15
19
  serialize(doc: ProseNode): string;
16
20
  }
package/dist/preserve.js CHANGED
@@ -317,6 +317,12 @@ function semanticSignature(doc) {
317
317
  : value));
318
318
  return JSON.stringify(clean(json));
319
319
  }
320
+ // Milkdown derives heading DOM IDs; Markdown stores the level and content.
321
+ function withoutHeadingId(node) {
322
+ return node.type.name === 'heading' && node.attrs.id
323
+ ? node.type.create({ ...node.attrs, id: '' }, node.content, node.marks)
324
+ : node;
325
+ }
320
326
  // Edit distance distinguishes replacements from insertions, so replacing a block
321
327
  // retains its surrounding whitespace without assigning it a neighbour's source.
322
328
  function correspondence(before, after) {
@@ -337,32 +343,52 @@ function correspondence(before, after) {
337
343
  }
338
344
  const rows = endBefore - head + 1;
339
345
  const columns = endAfter - head + 1;
340
- if (rows * columns > 1_000_000)
341
- throw new PreservationError('The edit changes too many blocks to preserve safely');
342
- const costs = Array.from({ length: rows }, () => new Uint32Array(columns));
343
- for (let i = 0; i < rows; i++)
344
- costs[i][0] = i;
345
- for (let j = 0; j < columns; j++)
346
- costs[0][j] = j;
347
- for (let i = 1; i < rows; i++) {
348
- for (let j = 1; j < columns; j++) {
349
- const change = before[head + i - 1] === after[head + j - 1] ? 0 : 1;
350
- costs[i][j] = Math.min(costs[i - 1][j] + 1, costs[i][j - 1] + 1, costs[i - 1][j - 1] + change);
346
+ let budget = 1_000_000;
347
+ let width = Math.max(1, Math.abs(rows - columns));
348
+ for (;;) {
349
+ const costs = [];
350
+ const get = (i, j) => i < 0 || j < 0 || Math.abs(i - j) > width
351
+ ? width + 1
352
+ : (costs[i]?.[j - Math.max(0, i - width)] ?? width + 1);
353
+ for (let i = 0; i < rows; i++) {
354
+ const from = Math.max(0, i - width);
355
+ const to = Math.min(columns - 1, i + width);
356
+ budget -= to - from + 1;
357
+ if (budget < 0)
358
+ throw new PreservationError('The edit changes too many blocks to preserve safely');
359
+ const row = new Uint32Array(to - from + 1);
360
+ costs.push(row);
361
+ for (let j = from; j <= to; j++) {
362
+ const change = before[head + i - 1] === after[head + j - 1] ? 0 : 1;
363
+ row[j - from] =
364
+ i === 0
365
+ ? j
366
+ : j === 0
367
+ ? i
368
+ : Math.min(get(i - 1, j) + 1, get(i, j - 1) + 1, get(i - 1, j - 1) + change);
369
+ }
351
370
  }
352
- }
353
- let i = rows - 1;
354
- let j = columns - 1;
355
- while (i || j) {
356
- const change = i && j && before[head + i - 1] === after[head + j - 1] ? 0 : 1;
357
- if (i && j && costs[i][j] === costs[i - 1][j - 1] + change) {
358
- result[head + j - 1] = head + i - 1;
359
- i--;
360
- j--;
371
+ // A path with at most width edits cannot leave this diagonal band.
372
+ // Widen only when needed, charging every attempt to the same work budget.
373
+ if (get(rows - 1, columns - 1) > width) {
374
+ width *= 2;
375
+ continue;
361
376
  }
362
- else if (i && costs[i][j] === costs[i - 1][j] + 1)
363
- i--;
364
- else
365
- j--;
377
+ let i = rows - 1;
378
+ let j = columns - 1;
379
+ while (i || j) {
380
+ const change = i && j && before[head + i - 1] === after[head + j - 1] ? 0 : 1;
381
+ if (i && j && get(i, j) === get(i - 1, j - 1) + change) {
382
+ result[head + j - 1] = head + i - 1;
383
+ i--;
384
+ j--;
385
+ }
386
+ else if (i && get(i, j) === get(i - 1, j) + 1)
387
+ i--;
388
+ else
389
+ j--;
390
+ }
391
+ break;
366
392
  }
367
393
  return result;
368
394
  }
@@ -373,7 +399,9 @@ export class Preservation {
373
399
  baseline;
374
400
  blocks = [];
375
401
  lineEnding;
402
+ originalForms = new Set();
376
403
  forms = new WeakMap();
404
+ textBlockEdits = new WeakMap();
377
405
  constructor(ctx, source) {
378
406
  this.ctx = ctx;
379
407
  this.source = source;
@@ -414,6 +442,8 @@ export class Preservation {
414
442
  ast: positionalNode(rawTree, node),
415
443
  });
416
444
  }
445
+ for (const block of this.blocks)
446
+ this.originalForms.add(block.form);
417
447
  }
418
448
  catch (cause) {
419
449
  if (cause instanceof PreservationError)
@@ -423,10 +453,79 @@ export class Preservation {
423
453
  });
424
454
  }
425
455
  }
456
+ retainBlock(block, after, canonical) {
457
+ const offset = this.source.startsWith('\uFEFF') ? 1 : 0;
458
+ const start = block.ast.position?.start.offset;
459
+ if (start == null)
460
+ throw new PreservationError('A source block has no location');
461
+ return (this.source.slice(block.start, start + offset) +
462
+ retainSource(block.ast, after, this.source.slice(offset), canonical, this.lineEnding));
463
+ }
464
+ independentTextBlocks(doc) {
465
+ if (this.baseline.childCount !== this.blocks.length ||
466
+ doc.childCount !== this.blocks.length ||
467
+ !doc.sameMarkup(this.baseline))
468
+ return undefined;
469
+ let output = '', previous = 0;
470
+ for (let index = 0; index < doc.childCount; index++) {
471
+ const node = doc.child(index);
472
+ const before = this.baseline.child(index);
473
+ const block = this.blocks[index];
474
+ if (node.eq(before))
475
+ continue;
476
+ let retained = this.textBlockEdits.get(node);
477
+ if (retained?.index !== index) {
478
+ const raw = this.source.slice(block.start, block.end);
479
+ const expected = withoutHeadingId(node);
480
+ // References and HTML/comments can depend on other blocks. Restrict
481
+ // local validation to text blocks without these context-sensitive forms.
482
+ const contextual = /[\[<]|%%/;
483
+ const textOnly = (block) => {
484
+ let valid = true;
485
+ block.forEach((child) => {
486
+ if (!child.isText)
487
+ valid = false;
488
+ });
489
+ return valid;
490
+ };
491
+ if (!['paragraph', 'heading'].includes(node.type.name) ||
492
+ !expected.sameMarkup(withoutHeadingId(before)) ||
493
+ !textOnly(node) ||
494
+ !textOnly(before) ||
495
+ contextual.test(raw) ||
496
+ (index > 0 &&
497
+ !/\r?\n[ \t]*\r?\n/.test(this.source.slice(this.blocks[index - 1].end, block.start))) ||
498
+ (index + 1 < this.blocks.length &&
499
+ !/\r?\n[ \t]*\r?\n/.test(this.source.slice(block.end, this.blocks[index + 1].start))))
500
+ return undefined;
501
+ const canonical = blockForm(this.ctx, node);
502
+ if (contextual.test(canonical) ||
503
+ (canonical !== block.form && this.originalForms.has(canonical)))
504
+ return undefined;
505
+ const tree = this.ctx.get(remarkCtx).parse(canonical);
506
+ if (tree.children.length !== 1)
507
+ return undefined;
508
+ const text = this.retainBlock(block, tree.children[0], canonical);
509
+ const reopened = this.ctx.get(parserCtx)(text);
510
+ if (reopened.childCount !== 1)
511
+ return undefined;
512
+ if (!withoutHeadingId(reopened.firstChild).eq(expected))
513
+ return undefined;
514
+ retained = { index, text };
515
+ this.textBlockEdits.set(node, retained);
516
+ }
517
+ output += this.source.slice(previous, block.start) + retained.text;
518
+ previous = block.end;
519
+ }
520
+ return output + this.source.slice(previous);
521
+ }
426
522
  serialize(doc) {
427
523
  if (doc.eq(this.baseline))
428
524
  return this.source;
429
525
  try {
526
+ const independent = this.independentTextBlocks(doc);
527
+ if (independent !== undefined)
528
+ return independent;
430
529
  const canonical = serialize(this.ctx, doc);
431
530
  if (canonical === '')
432
531
  return this.source.startsWith('\uFEFF') ? '\uFEFF' : '';
@@ -445,7 +544,6 @@ export class Preservation {
445
544
  const currentTree = processor.runSync(structuredClone(currentRawTree), {
446
545
  value: canonical,
447
546
  });
448
- const body = this.source.replace(/^\uFEFF/, '');
449
547
  let output = '';
450
548
  let previous;
451
549
  current.forEach((text, index) => {
@@ -465,7 +563,7 @@ export class Preservation {
465
563
  if (block && block.form === text)
466
564
  output += this.source.slice(block.start, block.end);
467
565
  else if (block && currentTree.children.length === current.length) {
468
- output += retainSource(block.ast, positionalNode(currentRawTree, currentTree.children[index]), body, canonical, this.lineEnding);
566
+ output += this.retainBlock(block, positionalNode(currentRawTree, currentTree.children[index]), canonical);
469
567
  }
470
568
  else
471
569
  output += restoreCodeEndings(this.ctx, doc.type.create(null, [doc.child(index)]), text.replaceAll('\n', this.lineEnding));
@@ -477,10 +575,16 @@ export class Preservation {
477
575
  output += finalCodeEnding(doc) ?? this.lineEnding;
478
576
  const parse = this.ctx.get(parserCtx);
479
577
  const reopened = parse(output);
480
- if (serialize(this.ctx, reopened).replace(/\r\n?/g, '\n') ===
481
- canonical.replace(/\r\n?/g, '\n') &&
482
- semanticSignature(reopened) === semanticSignature(doc))
483
- return output;
578
+ const normalized = (text) => text.replace(/\r\n?/g, '\n');
579
+ if (semanticSignature(reopened) === semanticSignature(doc)) {
580
+ const actual = normalized(serialize(this.ctx, reopened));
581
+ if (actual === normalized(canonical))
582
+ return output;
583
+ // Parsing may normalize loose-list spacing. Compare both reconstructed
584
+ // source and intended canonical source after that same normalization.
585
+ if (actual === normalized(serialize(this.ctx, parse(canonical))))
586
+ return output;
587
+ }
484
588
  throw new PreservationError('The edited Markdown cannot be reopened without changing its content');
485
589
  }
486
590
  catch (cause) {
package/dist/search.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Node as ProseNode } from '@milkdown/kit/prose/model';
1
+ import { type Transaction } from '@milkdown/kit/prose/state';
2
2
  import type { EditorView } from '@milkdown/kit/prose/view';
3
3
  export interface TextMatch {
4
4
  from: number;
@@ -11,5 +11,5 @@ export interface LiteralReplacement extends TextMatch {
11
11
  export declare function findLiteral(text: string, search: string, from?: number): TextMatch | undefined;
12
12
  export declare function replaceLiteral(text: string, search: string, replacement: string, selection: TextMatch, all?: boolean): LiteralReplacement;
13
13
  export declare function findFormatted(view: EditorView, text: string): boolean;
14
- export declare function replaceFormatted(view: EditorView, search: string, replacement: string, preflight: (doc: ProseNode) => void, all?: boolean): number;
15
- export declare function replaceFormattedRange(view: EditorView, from: number, to: number, replacement: string, preflight: (doc: ProseNode) => void): boolean;
14
+ export declare function replaceFormatted(view: EditorView, search: string, replacement: string, preflight: (tr: Transaction) => void, all?: boolean): number;
15
+ export declare function replaceFormattedRange(view: EditorView, from: number, to: number, replacement: string, preflight: (tr: Transaction) => void): boolean;
package/dist/search.js CHANGED
@@ -162,7 +162,7 @@ export function replaceFormatted(view, search, replacement, preflight, all = fal
162
162
  tr = tr.setSelection(TextSelection.create(tr.doc, match.from, tr.mapping.map(match.to, 1)));
163
163
  }
164
164
  try {
165
- preflight(tr.doc);
165
+ preflight(tr);
166
166
  }
167
167
  catch (error) {
168
168
  if (error instanceof InkKitError)
@@ -189,7 +189,7 @@ export function replaceFormattedRange(view, from, to, replacement, preflight) {
189
189
  const tr = closeHistory(view.state.tr.replace(from, to, replacementSlice(view.state, from, to, value)));
190
190
  tr.setSelection(TextSelection.create(tr.doc, from, tr.mapping.map(to, 1)));
191
191
  try {
192
- preflight(tr.doc);
192
+ preflight(tr);
193
193
  }
194
194
  catch (error) {
195
195
  if (error instanceof InkKitError)