@rathkit/rte-core 0.1.0-alpha.5 → 0.2.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @rathkit/rte-core
2
2
 
3
- > **Alpha.** Not for production: the APIs will change between releases. Published to the `next` tag only.
3
+ > **Beta.** Not for production yet: the APIs may still change between releases. Published to the `next` tag only.
4
4
 
5
5
  The editor itself: the document model, HTML import and export, editing operations, commands and keys, the plugin host, the browser view, and the free plugins (lists, tables, find and replace, word-processor lists, SVG, images). The model does not touch the DOM, so it also runs in Node.
6
6
 
@@ -103,6 +103,8 @@ export declare class Editor {
103
103
  /** Adds a plugin. Returns a function that removes it. */
104
104
  /** Adds a plugin, with its page-facing part if it has one. Returns what takes both away again. */
105
105
  use: Engine["use"];
106
+ /** What takes each added plugin away again, so `destroy` can take away every one that is still in (a plugin's page part holds listeners on the window). */
107
+ private readonly installed;
106
108
  /** The document as HTML. `separator` goes between blocks (a newline makes it readable). */
107
109
  getHtml(separator?: string): string;
108
110
  /** Addresses of remote images in the document, so the host can decide whether to show them. */
package/dist/dom/view.js CHANGED
@@ -165,9 +165,17 @@ export class Editor {
165
165
  use = (plugin) => {
166
166
  const remove = this.engine.use(plugin);
167
167
  const stop = this.engine.attempt(plugin.id, "its page part", () => plugin.view?.(this.viewHost), () => undefined);
168
- return () => { if (stop)
169
- this.engine.attempt(plugin.id, "taking its page part away", stop, undefined); remove(); };
168
+ const undo = () => {
169
+ if (!this.installed.delete(undo))
170
+ return;
171
+ if (stop)
172
+ this.engine.attempt(plugin.id, "taking its page part away", stop, undefined);
173
+ remove();
174
+ };
175
+ this.installed.add(undo);
176
+ return undo;
170
177
  };
178
+ installed = new Set();
171
179
  getHtml(separator = "") { return exportHtml(this.engine.document, separator); }
172
180
  remoteImages() {
173
181
  const found = [];
@@ -284,6 +292,8 @@ export class Editor {
284
292
  return this.engine.subscribe(() => listener(this));
285
293
  }
286
294
  destroy() {
295
+ for (const undo of [...this.installed].reverse())
296
+ undo();
287
297
  for (const cleanup of this.cleanups)
288
298
  cleanup();
289
299
  this.cleanups.length = 0;
@@ -8,6 +8,11 @@ export interface MarkdownOptions {
8
8
  readonly maxDataImageLength?: number;
9
9
  /** Keep an image whose address is relative. Default true. */
10
10
  readonly relativeImages?: boolean;
11
+ /**
12
+ * How deep lists and quotes nest before what is inside stays text, at most 24 whatever is given (the editor passes its content profile's
13
+ * `maxDepth`, so a stricter profile reads less). Each level reads the lines inside it again, so this also bounds the work to 24 times the text.
14
+ */
15
+ readonly maxDepth?: number;
11
16
  }
12
17
  /** Link reference definitions found in the document: `[label]: address "title"`. Keys come from `refLabel`. */
13
18
  export type Refs = ReadonlyMap<string, {
@@ -39,10 +39,21 @@ export function parseInline(src, options = {}) {
39
39
  let head = null;
40
40
  let tail = null;
41
41
  let buffer = "";
42
+ let pending = "";
43
+ const add = (text) => {
44
+ const kept = text.trimEnd().length;
45
+ if (kept === 0) {
46
+ pending += text;
47
+ return;
48
+ }
49
+ buffer += pending + text.slice(0, kept);
50
+ pending = text.slice(kept);
51
+ };
42
52
  const flush = () => {
43
- if (buffer !== "") {
44
- pieces.push({ k: "t", s: buffer });
53
+ if (buffer !== "" || pending !== "") {
54
+ pieces.push({ k: "t", s: buffer + pending });
45
55
  buffer = "";
56
+ pending = "";
46
57
  }
47
58
  };
48
59
  const unlink = (d) => {
@@ -187,25 +198,23 @@ export function parseInline(src, options = {}) {
187
198
  continue;
188
199
  }
189
200
  if (isAsciiPunct(n)) {
190
- buffer += n;
201
+ add(n);
191
202
  i += 2;
192
203
  continue;
193
204
  }
194
- buffer += c;
205
+ add(c);
195
206
  i++;
196
207
  continue;
197
208
  }
198
209
  if (c === "\n") {
199
- let spaces = 0;
200
- const kept = buffer.trimEnd().length;
201
- spaces = buffer.length - kept;
202
- buffer = buffer.slice(0, kept);
210
+ const spaces = pending.length;
211
+ pending = "";
203
212
  if (spaces >= 2) {
204
213
  flush();
205
214
  pieces.push({ k: "br" });
206
215
  }
207
216
  else
208
- buffer += " ";
217
+ add(" ");
209
218
  i++;
210
219
  while (src[i] === " " || src[i] === "\t")
211
220
  i++;
@@ -214,11 +223,11 @@ export function parseInline(src, options = {}) {
214
223
  if (c === "&") {
215
224
  const m = /^&(?:#\d{1,7}|#[xX][0-9a-fA-F]{1,6}|[A-Za-z][A-Za-z0-9]{1,31});/.exec(src.slice(i, i + 40));
216
225
  if (m) {
217
- buffer += decodeEntities(m[0]);
226
+ add(decodeEntities(m[0]));
218
227
  i += m[0].length;
219
228
  continue;
220
229
  }
221
- buffer += c;
230
+ add(c);
222
231
  i++;
223
232
  continue;
224
233
  }
@@ -228,7 +237,7 @@ export function parseInline(src, options = {}) {
228
237
  run++;
229
238
  const end = closingTicks(src, i + run, run);
230
239
  if (end === -1) {
231
- buffer += "`".repeat(run);
240
+ add("`".repeat(run));
232
241
  i += run;
233
242
  continue;
234
243
  }
@@ -245,7 +254,7 @@ export function parseInline(src, options = {}) {
245
254
  while (src[i + run] === c)
246
255
  run++;
247
256
  if (c === "~" && run > 2) {
248
- buffer += c.repeat(run);
257
+ add(c.repeat(run));
249
258
  i += run;
250
259
  continue;
251
260
  }
@@ -270,7 +279,7 @@ export function parseInline(src, options = {}) {
270
279
  const image = c === "!";
271
280
  flush();
272
281
  if (brackets.length >= MAX_BRACKETS) {
273
- buffer += image ? "![" : "[";
282
+ add(image ? "![" : "[");
274
283
  i += image ? 2 : 1;
275
284
  continue;
276
285
  }
@@ -283,14 +292,14 @@ export function parseInline(src, options = {}) {
283
292
  if (c === "]") {
284
293
  const top = brackets.pop();
285
294
  if (!top) {
286
- buffer += c;
295
+ add(c);
287
296
  i++;
288
297
  continue;
289
298
  }
290
299
  if (!top.active) {
291
300
  flush();
292
301
  asText(top);
293
- buffer += c;
302
+ add(c);
294
303
  i++;
295
304
  continue;
296
305
  }
@@ -311,7 +320,7 @@ export function parseInline(src, options = {}) {
311
320
  if (!tailInfo) {
312
321
  flush();
313
322
  asText(top);
314
- buffer += c;
323
+ add(c);
315
324
  i++;
316
325
  continue;
317
326
  }
@@ -368,7 +377,7 @@ export function parseInline(src, options = {}) {
368
377
  i = tag.end;
369
378
  continue;
370
379
  }
371
- buffer += c;
380
+ add(c);
372
381
  i++;
373
382
  continue;
374
383
  }
@@ -381,7 +390,7 @@ export function parseInline(src, options = {}) {
381
390
  continue;
382
391
  }
383
392
  }
384
- buffer += c;
393
+ add(c);
385
394
  i++;
386
395
  }
387
396
  flush();
@@ -394,7 +394,7 @@ function parseList(lines, start, depth, options) {
394
394
  }
395
395
  function parseBlocks(lines, depth, options) {
396
396
  const out = [];
397
- if (depth >= MAX_DEPTH) {
397
+ if (depth >= Math.min(MAX_DEPTH, options.maxDepth ?? MAX_DEPTH)) {
398
398
  const p = paragraph(lines.join("\n"), options);
399
399
  return p ? [p] : [];
400
400
  }
@@ -48,6 +48,7 @@ export function markdown(options = {}) {
48
48
  ...(options.maxDataImageLength !== undefined ? { maxDataImageLength: options.maxDataImageLength } : {}),
49
49
  sanitizeSvg: host.editor.importOptions.sanitizeSvg,
50
50
  relativeImages: host.editor.importOptions.pasteRelativeImages ?? false,
51
+ ...(host.editor.importOptions.maxDepth !== undefined ? { maxDepth: host.editor.importOptions.maxDepth } : {}),
51
52
  });
52
53
  return blocks.length > 0 ? blocks : undefined;
53
54
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rathkit/rte-core",
3
- "version": "0.1.0-alpha.5",
3
+ "version": "0.2.0-beta.0",
4
4
  "description": "Document model, transforms, commands, view layer and plugin API for Rathkit RTE.",
5
5
  "keywords": [
6
6
  "rich-text",