@rathkit/rte-core 0.1.0-alpha.3 → 0.1.0-alpha.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/README.md +1 -1
  2. package/dist/autoformat/autoformat.js +18 -3
  3. package/dist/base64.js +5 -1
  4. package/dist/dom/index.d.ts +1 -1
  5. package/dist/dom/render.js +26 -15
  6. package/dist/dom/view.d.ts +59 -6
  7. package/dist/dom/view.js +195 -38
  8. package/dist/edit.d.ts +6 -0
  9. package/dist/edit.js +101 -11
  10. package/dist/engine.d.ts +26 -4
  11. package/dist/engine.js +113 -23
  12. package/dist/export.d.ts +2 -2
  13. package/dist/export.js +22 -6
  14. package/dist/footnotes/index.d.ts +2 -0
  15. package/dist/footnotes/index.js +2 -0
  16. package/dist/footnotes/plugin.d.ts +6 -0
  17. package/dist/footnotes/plugin.js +130 -0
  18. package/dist/footnotes/reconcile.d.ts +4 -0
  19. package/dist/footnotes/reconcile.js +155 -0
  20. package/dist/html/parse.d.ts +8 -2
  21. package/dist/html/parse.js +6 -2
  22. package/dist/images/images.js +4 -3
  23. package/dist/images/index.d.ts +5 -1
  24. package/dist/images/index.js +3 -1
  25. package/dist/images/plugin.js +2 -1
  26. package/dist/{dom/imageview.d.ts → images/view.d.ts} +9 -6
  27. package/dist/{dom/imageview.js → images/view.js} +20 -14
  28. package/dist/import.d.ts +18 -2
  29. package/dist/import.js +87 -42
  30. package/dist/index.d.ts +4 -1
  31. package/dist/index.js +3 -1
  32. package/dist/lists/lists.js +55 -30
  33. package/dist/markdown/detect.d.ts +1 -0
  34. package/dist/markdown/detect.js +81 -0
  35. package/dist/markdown/export.d.ts +10 -0
  36. package/dist/markdown/export.js +252 -0
  37. package/dist/markdown/index.d.ts +5 -0
  38. package/dist/markdown/index.js +4 -0
  39. package/dist/markdown/inline.d.ts +37 -0
  40. package/dist/markdown/inline.js +604 -0
  41. package/dist/markdown/parse.d.ts +4 -0
  42. package/dist/markdown/parse.js +586 -0
  43. package/dist/markdown/plugin.d.ts +20 -0
  44. package/dist/markdown/plugin.js +56 -0
  45. package/dist/model.d.ts +30 -2
  46. package/dist/model.js +2 -0
  47. package/dist/notes.d.ts +6 -0
  48. package/dist/notes.js +49 -0
  49. package/dist/ops.d.ts +6 -1
  50. package/dist/ops.js +5 -4
  51. package/dist/paste.js +9 -2
  52. package/dist/path.d.ts +10 -1
  53. package/dist/path.js +110 -11
  54. package/dist/plugin.d.ts +53 -2
  55. package/dist/policy.d.ts +41 -0
  56. package/dist/policy.js +40 -0
  57. package/dist/standard/index.d.ts +5 -3
  58. package/dist/standard/index.js +6 -5
  59. package/dist/stats.js +1 -1
  60. package/dist/svg/svg.js +9 -1
  61. package/dist/{dom/tableview.d.ts → tables/cells.d.ts} +7 -6
  62. package/dist/{dom/tableview.js → tables/cells.js} +16 -12
  63. package/dist/{dom/tablegrips.d.ts → tables/grips.d.ts} +5 -4
  64. package/dist/{dom/tablegrips.js → tables/grips.js} +10 -9
  65. package/dist/tables/index.d.ts +5 -1
  66. package/dist/tables/index.js +3 -1
  67. package/dist/tables/view.d.ts +2 -0
  68. package/dist/tables/view.js +15 -0
  69. package/dist/warning.d.ts +16 -0
  70. package/dist/warning.js +5 -0
  71. package/package.json +23 -1
@@ -1,9 +1,10 @@
1
1
  import { type AutoformatOptions } from "../autoformat/plugin.ts";
2
2
  import { Editor, type EditorOptions } from "../dom/view.ts";
3
- import { type ImagesOptions } from "../images/plugin.ts";
3
+ import { type FootnotesOptions } from "../footnotes/plugin.ts";
4
+ import { type ImagesOptions } from "../images/index.ts";
4
5
  import { type ListsOptions } from "../lists/plugin.ts";
5
6
  import type { Plugin } from "../plugin.ts";
6
- import { type TablesOptions } from "../tables/plugin.ts";
7
+ import { type TablesOptions } from "../tables/index.ts";
7
8
  /** Which standard plugins to use, and how. */
8
9
  export interface StandardPluginOptions extends ImagesOptions {
9
10
  /** Options for lists, or `false` to leave lists out. */
@@ -14,6 +15,8 @@ export interface StandardPluginOptions extends ImagesOptions {
14
15
  readonly find?: false;
15
16
  /** Options for tables, or `false` to leave tables out. */
16
17
  readonly tables?: TablesOptions | false;
18
+ /** Options for footnotes, or `false` to leave them out. */
19
+ readonly footnotes?: FootnotesOptions | false;
17
20
  /** More plugins, added after the standard ones. */
18
21
  readonly plugins?: readonly Plugin[];
19
22
  }
@@ -22,4 +25,3 @@ export interface StandardOptions extends Omit<EditorOptions, "plugins">, Standar
22
25
  /** The standard plugins, for hosts that build their own editor but want the usual parts. */
23
26
  export declare function standardPlugins(options?: StandardPluginOptions): Plugin[];
24
27
  export declare function createEditor(options: StandardOptions): Editor;
25
- export { Editor, type EditorOptions };
@@ -1,10 +1,11 @@
1
1
  import { autoformat } from "../autoformat/plugin.js";
2
2
  import { Editor } from "../dom/view.js";
3
3
  import { find } from "../find/plugin.js";
4
- import { images } from "../images/plugin.js";
4
+ import { footnotes } from "../footnotes/plugin.js";
5
+ import { images } from "../images/index.js";
5
6
  import { lists } from "../lists/plugin.js";
6
7
  import { svg } from "../svg/plugin.js";
7
- import { tables } from "../tables/plugin.js";
8
+ import { tables } from "../tables/index.js";
8
9
  import { word } from "../word/plugin.js";
9
10
  export function standardPlugins(options = {}) {
10
11
  const { uploadImage, maxInlineImageBytes, maxImageFileBytes, onImageRejected } = options;
@@ -13,6 +14,7 @@ export function standardPlugins(options = {}) {
13
14
  ...(options.tables === false ? [] : [tables(options.tables)]),
14
15
  ...(options.find === false ? [] : [find()]),
15
16
  ...(options.autoformat === false ? [] : [autoformat(options.autoformat)]),
17
+ ...(options.footnotes === false ? [] : [footnotes(options.footnotes)]),
16
18
  word(),
17
19
  svg(),
18
20
  images({
@@ -25,8 +27,7 @@ export function standardPlugins(options = {}) {
25
27
  ];
26
28
  }
27
29
  export function createEditor(options) {
28
- const { lists: _lists, tables: _tables, find: _find, autoformat: _autoformat, plugins: _plugins, uploadImage: _u, maxInlineImageBytes: _m, maxImageFileBytes: _f, onImageRejected: _r, ...editorOptions } = options;
29
- void [_lists, _tables, _find, _autoformat, _plugins, _u, _m, _f, _r];
30
+ const { lists: _lists, tables: _tables, find: _find, autoformat: _autoformat, footnotes: _footnotes, plugins: _plugins, uploadImage: _u, maxInlineImageBytes: _m, maxImageFileBytes: _f, onImageRejected: _r, ...editorOptions } = options;
31
+ void [_lists, _tables, _find, _autoformat, _footnotes, _plugins, _u, _m, _f, _r];
30
32
  return new Editor({ ...editorOptions, plugins: standardPlugins(options) });
31
33
  }
32
- export { Editor };
package/dist/stats.js CHANGED
@@ -4,7 +4,7 @@ import { selectedText } from "./edit.js";
4
4
  const WORD = /[\p{L}\p{M}\p{N}]+(?:['’\-‑][\p{L}\p{M}\p{N}]+)*/gu;
5
5
  export function countText(text) {
6
6
  const clean = text.replace(//g, "").replace(/\n/g, " ");
7
- return { words: (clean.match(WORD) ?? []).length, characters: [...clean.replace(/\s+$/g, "").replace(/^\s+/g, "")].length };
7
+ return { words: (clean.match(WORD) ?? []).length, characters: [...clean.trim()].length };
8
8
  }
9
9
  export function countDocument(doc) {
10
10
  let words = 0;
package/dist/svg/svg.js CHANGED
@@ -44,7 +44,15 @@ const decodeXml = (value) => value.replace(ENTITY, (_, dec, hex, name) => {
44
44
  function parseXml(source, limits) {
45
45
  if (/<!ENTITY|<!ATTLIST|<!NOTATION/i.test(source) || /<!\[CDATA\[/i.test(source))
46
46
  return undefined;
47
- const stripped = source.replace(/^/, "").replace(/<\?xml[^>]*\?>/i, "").replace(/<!DOCTYPE[^>[]*(?:\[[^\]]*\])?[^>]*>/gi, "").replace(/<!--[\s\S]*?-->/g, "");
47
+ let stripped = source.replace(/^/, "").replace(/<\?xml[^>]*\?>/i, "").replace(/<!DOCTYPE[^>[]*(?:\[[^\]]*\])?[^>]*>/gi, "");
48
+ for (let pass = 0; pass < 4; pass++) {
49
+ const next = stripped.replace(/<!--[\s\S]*?-->/g, "");
50
+ if (next === stripped)
51
+ break;
52
+ stripped = next;
53
+ }
54
+ if (stripped.includes("<!--"))
55
+ return undefined;
48
56
  const stack = [];
49
57
  let root;
50
58
  let count = 0;
@@ -1,16 +1,17 @@
1
- import type { Engine } from "../engine.ts";
1
+ import type { ViewHost } from "../plugin.ts";
2
2
  /** The attribute on a cell that a selection covers, for styling. */
3
- export declare const SELECTED_CELL = "data-rk-cell-selected";
3
+ export declare const SELECTED_CELL = "data-rte-cell-selected";
4
4
  /**
5
5
  * Marks the cells of the rectangle a selection runs across (when it starts and ends in different cells of one table). The marks are
6
6
  * attributes on the drawn cells, not part of the model, and they are put on again after every change.
7
7
  */
8
8
  export declare class CellMarks {
9
+ private readonly host;
10
+ private marked;
9
11
  private readonly root;
10
12
  private readonly engine;
11
- private readonly quiet;
12
- private marked;
13
- constructor(root: HTMLElement, engine: Engine, quiet: () => void);
13
+ constructor(host: ViewHost);
14
+ private quiet;
14
15
  update(): void;
15
16
  }
16
17
  /**
@@ -19,4 +20,4 @@ export declare class CellMarks {
19
20
  * The bottom edge of a row works the same way through `table.rowHeight`.
20
21
  * Returns what takes it away again.
21
22
  */
22
- export declare function installColumnResize(root: HTMLElement, engine: Engine, listen: (target: EventTarget, type: string, handler: (event: Event) => void) => void, quiet: () => void): void;
23
+ export declare function installColumnResize(host: ViewHost): void;
@@ -1,20 +1,20 @@
1
1
  import { pathKey } from "../path.js";
2
2
  import { inRow, locateCell, selectedCells } from "../tablegrid.js";
3
- import { posFromDom } from "./render.js";
4
- export const SELECTED_CELL = "data-rk-cell-selected";
3
+ export const SELECTED_CELL = "data-rte-cell-selected";
5
4
  const GRAB = 5;
6
5
  const MIN_WIDTH = 24;
7
6
  const MIN_HEIGHT = 16;
8
7
  export class CellMarks {
8
+ host;
9
+ marked = [];
9
10
  root;
10
11
  engine;
11
- quiet;
12
- marked = [];
13
- constructor(root, engine, quiet) {
14
- this.root = root;
15
- this.engine = engine;
16
- this.quiet = quiet;
12
+ constructor(host) {
13
+ this.host = host;
14
+ this.root = host.element;
15
+ this.engine = host.engine;
17
16
  }
17
+ quiet() { this.host.quiet(); }
18
18
  update() {
19
19
  const sel = this.engine.selection;
20
20
  const found = pathKey(sel.anchor.path) === pathKey(sel.focus.path) ? undefined : selectedCells(this.engine.document, sel);
@@ -56,11 +56,15 @@ function rowEdgeAt(root, event) {
56
56
  const box = row.getBoundingClientRect();
57
57
  return event.clientY >= box.bottom - GRAB && event.clientY <= box.bottom + GRAB ? cell : null;
58
58
  }
59
- export function installColumnResize(root, engine, listen, quiet) {
59
+ export function installColumnResize(host) {
60
+ const root = host.element;
61
+ const engine = host.engine;
62
+ const quiet = () => host.quiet();
63
+ const listen = (target, type, handler) => host.listen(target, type, handler);
60
64
  const available = () => !engine.readOnly && engine.commands.list().some((c) => c.name === "table.columnWidth");
61
65
  const cellPath = (cell) => {
62
66
  const inside = cell.querySelector("[data-rte]");
63
- return inside ? posFromDom(root, inside, 0)?.path : undefined;
67
+ return inside ? host.posFromDom(inside, 0)?.path : undefined;
64
68
  };
65
69
  const rowAvailable = () => !engine.readOnly && engine.commands.list().some((c) => c.name === "table.rowHeight");
66
70
  let rowDrag;
@@ -92,7 +96,7 @@ export function installColumnResize(root, engine, listen, quiet) {
92
96
  const rowBox = row.getBoundingClientRect();
93
97
  const rowTable = rowCell.closest("table")?.getBoundingClientRect() ?? rowBox;
94
98
  const rowLine = document.createElement("div");
95
- rowLine.setAttribute("data-rk-resize-line", "");
99
+ rowLine.setAttribute("data-rte-resize-line", "");
96
100
  rowLine.style.cssText = `position:fixed;z-index:2147483647;pointer-events:none;height:2px;background:#17408b;top:${rowBox.bottom - 1}px;left:${rowTable.left}px;width:${rowTable.width}px`;
97
101
  document.body.appendChild(rowLine);
98
102
  rowDrag = { path: rowPath, startY: event.clientY, startHeight: rowBox.height, top: rowBox.top, line: rowLine, height: rowBox.height };
@@ -102,7 +106,7 @@ export function installColumnResize(root, engine, listen, quiet) {
102
106
  const box = cell.getBoundingClientRect();
103
107
  const table = cell.closest("table")?.getBoundingClientRect() ?? box;
104
108
  const line = document.createElement("div");
105
- line.setAttribute("data-rk-resize-line", "");
109
+ line.setAttribute("data-rte-resize-line", "");
106
110
  line.style.cssText = `position:fixed;z-index:2147483647;pointer-events:none;width:2px;background:#17408b;left:${box.right - 1}px;top:${table.top}px;height:${table.height}px`;
107
111
  document.body.appendChild(line);
108
112
  drag = { cell, path, startX: event.clientX, startWidth: box.width, line, width: box.width };
@@ -1,7 +1,6 @@
1
- import type { Engine } from "../engine.ts";
1
+ import type { ViewHost } from "../plugin.ts";
2
2
  export declare class TableGrips {
3
- private readonly root;
4
- private readonly engine;
3
+ private readonly host;
5
4
  private readonly grips;
6
5
  private guide;
7
6
  private readonly stops;
@@ -9,7 +8,9 @@ export declare class TableGrips {
9
8
  private table;
10
9
  private path;
11
10
  private touch;
12
- constructor(root: HTMLElement, engine: Engine);
11
+ private readonly root;
12
+ private readonly engine;
13
+ constructor(host: ViewHost);
13
14
  private available;
14
15
  /** The drawn cell the caret is in, and its table, when the table is at the top level. Found from the page, so it costs nothing in a big table. */
15
16
  private find;
@@ -1,11 +1,9 @@
1
1
  import { getBlock } from "../path.js";
2
2
  import { inRow, locateCell, toGrid } from "../tablegrid.js";
3
- import { domFromPos } from "./render.js";
4
3
  const SIZE = 22;
5
4
  const COLOR = "#17408b";
6
5
  export class TableGrips {
7
- root;
8
- engine;
6
+ host;
9
7
  grips = new Map();
10
8
  guide;
11
9
  stops = [];
@@ -13,9 +11,12 @@ export class TableGrips {
13
11
  table;
14
12
  path = [];
15
13
  touch = false;
16
- constructor(root, engine) {
17
- this.root = root;
18
- this.engine = engine;
14
+ root;
15
+ engine;
16
+ constructor(host) {
17
+ this.host = host;
18
+ this.root = host.element;
19
+ this.engine = host.engine;
19
20
  const reposition = () => this.place();
20
21
  window.addEventListener("scroll", reposition, true);
21
22
  window.addEventListener("resize", reposition);
@@ -40,7 +41,7 @@ export class TableGrips {
40
41
  const top = this.root.children[path[0]];
41
42
  if (!(top instanceof HTMLElement) || top.tagName !== "TABLE")
42
43
  return undefined;
43
- const at = domFromPos(this.root, this.engine.selection.focus);
44
+ const at = this.host.domFromPos(this.engine.selection.focus);
44
45
  const from = at ? (at.node instanceof Element ? at.node : at.node.parentElement) : null;
45
46
  const cell = from?.closest("td, th");
46
47
  return cell instanceof HTMLElement && cell.closest("table") === top ? { cell, table: top, path } : undefined;
@@ -81,7 +82,7 @@ export class TableGrips {
81
82
  }
82
83
  make(kind) {
83
84
  const el = document.createElement("div");
84
- el.setAttribute("data-rk-grip", kind);
85
+ el.setAttribute("data-rte-grip", kind);
85
86
  el.setAttribute("role", "presentation");
86
87
  const cursor = kind === "row" ? "ns-resize" : kind === "column" ? "ew-resize" : kind === "width" ? "col-resize" : "row-resize";
87
88
  el.style.cssText = `position:fixed;z-index:2147483646;width:${SIZE}px;height:${SIZE}px;box-sizing:border-box;background:${COLOR};border:2px solid #fff;border-radius:${kind === "row" || kind === "column" ? "4px" : "50%"};cursor:${cursor};touch-action:none;opacity:0.9`;
@@ -119,7 +120,7 @@ export class TableGrips {
119
120
  const t = table.getBoundingClientRect();
120
121
  const horizontal = kind === "column" || kind === "width";
121
122
  const line = document.createElement("div");
122
- line.setAttribute("data-rk-resize-line", "");
123
+ line.setAttribute("data-rte-resize-line", "");
123
124
  line.style.cssText = `position:fixed;z-index:2147483645;pointer-events:none;background:${COLOR};${horizontal ? `width:2px;top:${t.top}px;height:${t.height}px` : `height:2px;left:${t.left}px;width:${t.width}px`}`;
124
125
  document.body.appendChild(line);
125
126
  this.guide = line;
@@ -1,2 +1,6 @@
1
- export { tables, type TablesOptions } from "./plugin.ts";
1
+ import { type TablesOptions } from "./plugin.ts";
2
+ import type { Plugin } from "../plugin.ts";
3
+ export type { TablesOptions };
4
+ /** The tables plugin, with the part that draws table handles in a browser. (`./plugin.ts` is the same without it.) */
5
+ export declare const tables: (options?: TablesOptions) => Plugin;
2
6
  export { BORDER_PRESETS, borderPreset, cellBackground, cellBorderPreset, rowHeight, setCellBackground, setCellBorder, setRowHeight, columnWidth, setColumnWidth, canMerge, canSplit, deleteColumn, deleteRow, deleteTable, hasHeaderRow, inTable, insertColumn, insertRow, insertTable, makeTable, mergeCells, moveToCell, splitCell, toggleHeaderRow, type InsertTableOptions } from "./tables.ts";
@@ -1,2 +1,4 @@
1
- export { tables } from "./plugin.js";
1
+ import { tables as engineOnly } from "./plugin.js";
2
+ import { tablesView } from "./view.js";
3
+ export const tables = (options) => ({ ...engineOnly(options), view: tablesView });
2
4
  export { BORDER_PRESETS, borderPreset, cellBackground, cellBorderPreset, rowHeight, setCellBackground, setCellBorder, setRowHeight, columnWidth, setColumnWidth, canMerge, canSplit, deleteColumn, deleteRow, deleteTable, hasHeaderRow, inTable, insertColumn, insertRow, insertTable, makeTable, mergeCells, moveToCell, splitCell, toggleHeaderRow } from "./tables.js";
@@ -0,0 +1,2 @@
1
+ import type { ViewExtension } from "../plugin.ts";
2
+ export declare const tablesView: ViewExtension;
@@ -0,0 +1,15 @@
1
+ import { CellMarks, installColumnResize } from "./cells.js";
2
+ import { TableGrips } from "./grips.js";
3
+ export const tablesView = (host) => {
4
+ const marks = new CellMarks(host);
5
+ const grips = new TableGrips(host);
6
+ installColumnResize(host);
7
+ const stop = host.onChange((change) => {
8
+ if (change.docChanged || change.selectionChanged)
9
+ marks.update();
10
+ grips.update();
11
+ });
12
+ marks.update();
13
+ grips.update();
14
+ return () => { stop(); grips.destroy(); };
15
+ };
@@ -0,0 +1,16 @@
1
+ /** Why a warning was made. The codes are stable: a host can switch on them. */
2
+ export declare const WARNING_CODES: readonly ["plugin-failed", "plugin-rejected", "plugin-message", "key-conflict", "content-too-long", "content-unreadable", "copy-failed", "upload-failed", "bad-option", "missing-label", "late-options", "listener-failed"];
3
+ /** One of the codes in `WARNING_CODES`. */
4
+ export type WarningCode = (typeof WARNING_CODES)[number];
5
+ /** A problem the editor carried on from, reported to `onWarning`. Never thrown. */
6
+ export interface Warning {
7
+ readonly code: WarningCode;
8
+ /** For a person: what happened and what was done about it. */
9
+ readonly message: string;
10
+ /** The plugin it concerns, when there is one. */
11
+ readonly plugin?: string;
12
+ /** The error that was caught, when one was. */
13
+ readonly error?: unknown;
14
+ }
15
+ /** What an error says, whatever was thrown. */
16
+ export declare const describeError: (error: unknown) => string;
@@ -0,0 +1,5 @@
1
+ export const WARNING_CODES = [
2
+ "plugin-failed", "plugin-rejected", "plugin-message", "key-conflict", "content-too-long", "content-unreadable",
3
+ "copy-failed", "upload-failed", "bad-option", "missing-label", "late-options", "listener-failed",
4
+ ];
5
+ export const describeError = (error) => (error instanceof Error ? error.message : String(error));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rathkit/rte-core",
3
- "version": "0.1.0-alpha.3",
3
+ "version": "0.1.0-alpha.5",
4
4
  "description": "Document model, transforms, commands, view layer and plugin API for Rathkit RTE.",
5
5
  "keywords": [
6
6
  "rich-text",
@@ -64,6 +64,14 @@
64
64
  "types": "./dist/find/index.d.ts",
65
65
  "default": "./dist/find/index.js"
66
66
  },
67
+ "./footnotes": {
68
+ "types": "./dist/footnotes/index.d.ts",
69
+ "default": "./dist/footnotes/index.js"
70
+ },
71
+ "./markdown": {
72
+ "types": "./dist/markdown/index.d.ts",
73
+ "default": "./dist/markdown/index.js"
74
+ },
67
75
  "./word": {
68
76
  "types": "./dist/word/index.d.ts",
69
77
  "default": "./dist/word/index.js"
@@ -92,6 +100,8 @@
92
100
  "tables",
93
101
  "find",
94
102
  "autoformat",
103
+ "markdown",
104
+ "footnotes",
95
105
  "word",
96
106
  "svg",
97
107
  "images"
@@ -128,6 +138,18 @@
128
138
  "gzipKB": 15,
129
139
  "plugin": true
130
140
  },
141
+ {
142
+ "name": "footnotes plugin",
143
+ "entry": "dist/footnotes/index.js",
144
+ "gzipKB": 15,
145
+ "plugin": true
146
+ },
147
+ {
148
+ "name": "markdown plugin",
149
+ "entry": "dist/markdown/index.js",
150
+ "gzipKB": 15,
151
+ "plugin": true
152
+ },
131
153
  {
132
154
  "name": "find plugin",
133
155
  "entry": "dist/find/index.js",