@rathkit/rte-core 0.1.0-alpha.4 → 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/dist/policy.js ADDED
@@ -0,0 +1,40 @@
1
+ export const DROPPED_ELEMENTS = new Set([
2
+ "script", "style", "link", "meta", "base", "object", "embed", "iframe", "frame", "frameset", "noscript", "template",
3
+ "head", "textarea", "input", "select", "option", "button", "canvas", "audio", "video", "applet", "xml",
4
+ ]);
5
+ export const GROUPING_ELEMENTS = new Set([
6
+ "div", "section", "article", "header", "footer", "aside", "nav", "figure", "figcaption", "address", "main", "form",
7
+ "body", "html", "center", "details", "summary", "dd", "dt", "fieldset", "hgroup",
8
+ ]);
9
+ export const DEFAULT_LINK_SCHEMES = ["http", "https", "mailto", "tel"];
10
+ export const IMAGE_SCHEMES = ["http", "https"];
11
+ export const DATA_IMAGE_SIGNATURES = [
12
+ ["image/png", "iVBORw0KGgo"], ["image/jpeg", "/9j/"], ["image/gif", "R0lGOD"], ["image/webp", "UklGR"], ["image/bmp", "Qk"],
13
+ ];
14
+ export const CONTENT_PROFILES = {
15
+ strict: { pasteRelativeImages: false, linkSchemes: ["http", "https", "mailto"], styles: "semantic", maxDataImageLength: 0, maxDepth: 32, maxInputLength: 1_000_000 },
16
+ standard: { pasteRelativeImages: false, linkSchemes: DEFAULT_LINK_SCHEMES, styles: "allowlist", maxDataImageLength: 12_000_000, maxDepth: 100, maxInputLength: 50_000_000 },
17
+ loose: { pasteRelativeImages: false, linkSchemes: ["http", "https", "mailto", "tel", "sms", "ftp", "ftps"], styles: "allowlist", maxDataImageLength: 25_000_000, maxDepth: 200, maxInputLength: 200_000_000 },
18
+ };
19
+ export function contentPolicy(options = {}, complain = () => undefined) {
20
+ const { profile = "standard", ...rest } = options;
21
+ if (!Object.hasOwn(CONTENT_PROFILES, profile))
22
+ complain(`content.profile "${String(profile)}" is not a profile: using standard (strict, standard or loose).`);
23
+ const base = Object.hasOwn(CONTENT_PROFILES, profile) ? CONTENT_PROFILES[profile] : CONTENT_PROFILES.standard;
24
+ const given = {};
25
+ for (const [key, value] of Object.entries(rest)) {
26
+ if (value === undefined)
27
+ continue;
28
+ const ok = key === "linkSchemes" ? Array.isArray(value) && value.every((v) => typeof v === "string" && /^[a-z][a-z0-9+.-]*$/i.test(v))
29
+ : key === "styles" ? value === "semantic" || value === "allowlist"
30
+ : key === "pasteRelativeImages" ? typeof value === "boolean"
31
+ : key === "maxDepth" ? Number.isInteger(value) && value >= 1
32
+ : (key === "maxInputLength" || key === "maxDataImageLength") ? Number.isInteger(value) && value >= 0
33
+ : false;
34
+ if (ok)
35
+ given[key] = value;
36
+ else
37
+ complain(`content.${key} cannot be ${JSON.stringify(value)}: using the ${profile in CONTENT_PROFILES ? profile : "standard"} profile's value.`);
38
+ }
39
+ return { ...base, ...given };
40
+ }
@@ -25,4 +25,3 @@ export interface StandardOptions extends Omit<EditorOptions, "plugins">, Standar
25
25
  /** The standard plugins, for hosts that build their own editor but want the usual parts. */
26
26
  export declare function standardPlugins(options?: StandardPluginOptions): Plugin[];
27
27
  export declare function createEditor(options: StandardOptions): Editor;
28
- export { Editor, type EditorOptions };
@@ -31,4 +31,3 @@ export function createEditor(options) {
31
31
  void [_lists, _tables, _find, _autoformat, _footnotes, _plugins, _u, _m, _f, _r];
32
32
  return new Editor({ ...editorOptions, plugins: standardPlugins(options) });
33
33
  }
34
- 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,6 +1,6 @@
1
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.
@@ -1,6 +1,6 @@
1
1
  import { pathKey } from "../path.js";
2
2
  import { inRow, locateCell, selectedCells } from "../tablegrid.js";
3
- export const SELECTED_CELL = "data-rk-cell-selected";
3
+ export const SELECTED_CELL = "data-rte-cell-selected";
4
4
  const GRAB = 5;
5
5
  const MIN_WIDTH = 24;
6
6
  const MIN_HEIGHT = 16;
@@ -96,7 +96,7 @@ export function installColumnResize(host) {
96
96
  const rowBox = row.getBoundingClientRect();
97
97
  const rowTable = rowCell.closest("table")?.getBoundingClientRect() ?? rowBox;
98
98
  const rowLine = document.createElement("div");
99
- rowLine.setAttribute("data-rk-resize-line", "");
99
+ rowLine.setAttribute("data-rte-resize-line", "");
100
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`;
101
101
  document.body.appendChild(rowLine);
102
102
  rowDrag = { path: rowPath, startY: event.clientY, startHeight: rowBox.height, top: rowBox.top, line: rowLine, height: rowBox.height };
@@ -106,7 +106,7 @@ export function installColumnResize(host) {
106
106
  const box = cell.getBoundingClientRect();
107
107
  const table = cell.closest("table")?.getBoundingClientRect() ?? box;
108
108
  const line = document.createElement("div");
109
- line.setAttribute("data-rk-resize-line", "");
109
+ line.setAttribute("data-rte-resize-line", "");
110
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`;
111
111
  document.body.appendChild(line);
112
112
  drag = { cell, path, startX: event.clientX, startWidth: box.width, line, width: box.width };
@@ -82,7 +82,7 @@ export class TableGrips {
82
82
  }
83
83
  make(kind) {
84
84
  const el = document.createElement("div");
85
- el.setAttribute("data-rk-grip", kind);
85
+ el.setAttribute("data-rte-grip", kind);
86
86
  el.setAttribute("role", "presentation");
87
87
  const cursor = kind === "row" ? "ns-resize" : kind === "column" ? "ew-resize" : kind === "width" ? "col-resize" : "row-resize";
88
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`;
@@ -120,7 +120,7 @@ export class TableGrips {
120
120
  const t = table.getBoundingClientRect();
121
121
  const horizontal = kind === "column" || kind === "width";
122
122
  const line = document.createElement("div");
123
- line.setAttribute("data-rk-resize-line", "");
123
+ line.setAttribute("data-rte-resize-line", "");
124
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`}`;
125
125
  document.body.appendChild(line);
126
126
  this.guide = line;
@@ -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.4",
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",