@thomasfosterau/effect-richtext 0.0.0 → 0.1.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/LICENSE +48 -0
- package/README.md +117 -1
- package/dist/_virtual/_rolldown/runtime.js +12 -0
- package/dist/commands/commands.d.ts +323 -0
- package/dist/commands/commands.js +722 -0
- package/dist/commands/internal.js +43 -0
- package/dist/commands/keymaps.d.ts +46 -0
- package/dist/commands/keymaps.js +64 -0
- package/dist/commands.d.ts +3 -0
- package/dist/commands.js +3 -0
- package/dist/compat/commands.d.ts +250 -0
- package/dist/compat/commands.js +275 -0
- package/dist/compat/internal/core.d.ts +20 -0
- package/dist/compat/internal/core.js +148 -0
- package/dist/compat/internal/props.js +82 -0
- package/dist/compat/keymap.d.ts +21 -0
- package/dist/compat/keymap.js +38 -0
- package/dist/compat/model.d.ts +893 -0
- package/dist/compat/model.js +1212 -0
- package/dist/compat/orderedmap.d.ts +74 -0
- package/dist/compat/orderedmap.js +124 -0
- package/dist/compat/schema-basic.d.ts +38 -0
- package/dist/compat/schema-basic.js +238 -0
- package/dist/compat/schema-list.d.ts +81 -0
- package/dist/compat/schema-list.js +156 -0
- package/dist/compat/state.d.ts +415 -0
- package/dist/compat/state.js +689 -0
- package/dist/compat/test-builder.d.ts +98 -0
- package/dist/compat/test-builder.js +197 -0
- package/dist/compat/transform.d.ts +458 -0
- package/dist/compat/transform.js +803 -0
- package/dist/compat/view.d.ts +411 -0
- package/dist/compat/view.js +409 -0
- package/dist/dropcursor.d.ts +46 -0
- package/dist/dropcursor.js +176 -0
- package/dist/extensions/defaults.d.ts +716 -0
- package/dist/extensions/defaults.js +209 -0
- package/dist/extensions/editor.d.ts +246 -0
- package/dist/extensions/editor.js +271 -0
- package/dist/extensions/extension.d.ts +375 -0
- package/dist/extensions/extension.js +233 -0
- package/dist/extensions.d.ts +4 -0
- package/dist/extensions.js +4 -0
- package/dist/gapcursor.d.ts +66 -0
- package/dist/gapcursor.js +273 -0
- package/dist/history.d.ts +178 -0
- package/dist/history.js +412 -0
- package/dist/html.d.ts +158 -0
- package/dist/html.js +164 -0
- package/dist/inputrules.d.ts +271 -0
- package/dist/inputrules.js +321 -0
- package/dist/internal/attempt.js +15 -0
- package/dist/internal/json.js +29 -0
- package/dist/keymap/keymap.d.ts +130 -0
- package/dist/keymap/keymap.js +159 -0
- package/dist/keymap.d.ts +2 -0
- package/dist/keymap.js +1 -0
- package/dist/markdown/errors.d.ts +23 -0
- package/dist/markdown/errors.js +26 -0
- package/dist/markdown/from_markdown.d.ts +185 -0
- package/dist/markdown/from_markdown.js +266 -0
- package/dist/markdown/schema.d.ts +522 -0
- package/dist/markdown/schema.js +292 -0
- package/dist/markdown/to_markdown.d.ts +251 -0
- package/dist/markdown/to_markdown.js +413 -0
- package/dist/markdown.d.ts +5 -0
- package/dist/markdown.js +5 -0
- package/dist/model/attrsCodec.js +48 -0
- package/dist/model/comparedeep.js +17 -0
- package/dist/model/content.d.ts +232 -0
- package/dist/model/content.js +630 -0
- package/dist/model/diff.js +66 -0
- package/dist/model/dom.d.ts +3 -0
- package/dist/model/errors.d.ts +90 -0
- package/dist/model/errors.js +76 -0
- package/dist/model/fragment.d.ts +131 -0
- package/dist/model/fragment.js +262 -0
- package/dist/model/from_dom.d.ts +299 -0
- package/dist/model/from_dom.js +618 -0
- package/dist/model/json.d.ts +37 -0
- package/dist/model/json.js +126 -0
- package/dist/model/mark.d.ts +73 -0
- package/dist/model/mark.js +121 -0
- package/dist/model/node.d.ts +267 -0
- package/dist/model/node.js +434 -0
- package/dist/model/replace.d.ts +78 -0
- package/dist/model/replace.js +207 -0
- package/dist/model/resolvedpos.d.ts +196 -0
- package/dist/model/resolvedpos.js +305 -0
- package/dist/model/schema.d.ts +740 -0
- package/dist/model/schema.js +451 -0
- package/dist/model/to_dom.d.ts +169 -0
- package/dist/model/to_dom.js +238 -0
- package/dist/model.d.ts +12 -0
- package/dist/model.js +12 -0
- package/dist/placeholder.d.ts +53 -0
- package/dist/placeholder.js +79 -0
- package/dist/reactive/observer.d.ts +54 -0
- package/dist/reactive/observer.js +61 -0
- package/dist/reactive/selectors.d.ts +22 -0
- package/dist/reactive/selectors.js +61 -0
- package/dist/reactive/stateref.d.ts +72 -0
- package/dist/reactive/stateref.js +71 -0
- package/dist/reactive/streams.d.ts +83 -0
- package/dist/reactive/streams.js +85 -0
- package/dist/reactive.d.ts +16 -0
- package/dist/reactive.js +26 -0
- package/dist/schema-basic.d.ts +448 -0
- package/dist/schema-basic.js +269 -0
- package/dist/schema-list/commands.d.ts +66 -0
- package/dist/schema-list/commands.js +203 -0
- package/dist/schema-list.d.ts +92 -0
- package/dist/schema-list.js +100 -0
- package/dist/state/errors.d.ts +39 -0
- package/dist/state/errors.js +32 -0
- package/dist/state/plugin.d.ts +232 -0
- package/dist/state/plugin.js +82 -0
- package/dist/state/selection.d.ts +297 -0
- package/dist/state/selection.js +486 -0
- package/dist/state/state.d.ts +212 -0
- package/dist/state/state.js +489 -0
- package/dist/state/transaction.d.ts +145 -0
- package/dist/state/transaction.js +195 -0
- package/dist/state.d.ts +6 -0
- package/dist/state.js +6 -0
- package/dist/test-builder.d.ts +3582 -0
- package/dist/test-builder.js +239 -0
- package/dist/transform/attr_step.d.ts +60 -0
- package/dist/transform/attr_step.js +105 -0
- package/dist/transform/errors.d.ts +50 -0
- package/dist/transform/errors.js +35 -0
- package/dist/transform/map.d.ts +182 -0
- package/dist/transform/map.js +280 -0
- package/dist/transform/mark.js +98 -0
- package/dist/transform/mark_set_step.js +69 -0
- package/dist/transform/mark_step.d.ts +113 -0
- package/dist/transform/mark_step.js +225 -0
- package/dist/transform/replace.d.ts +18 -0
- package/dist/transform/replace.js +381 -0
- package/dist/transform/replace_step.d.ts +109 -0
- package/dist/transform/replace_step.js +175 -0
- package/dist/transform/step.d.ts +157 -0
- package/dist/transform/step.js +168 -0
- package/dist/transform/structure.d.ts +89 -0
- package/dist/transform/structure.js +350 -0
- package/dist/transform/transform.d.ts +248 -0
- package/dist/transform/transform.js +373 -0
- package/dist/transform.d.ts +10 -0
- package/dist/transform.js +10 -0
- package/dist/view/browser.js +39 -0
- package/dist/view/capturekeys.js +282 -0
- package/dist/view/clipboard.d.ts +8 -0
- package/dist/view/clipboard.js +245 -0
- package/dist/view/decoration.d.ts +339 -0
- package/dist/view/decoration.js +566 -0
- package/dist/view/dom.d.ts +18 -0
- package/dist/view/dom.js +140 -0
- package/dist/view/domchange.js +295 -0
- package/dist/view/domcoords.d.ts +22 -0
- package/dist/view/domcoords.js +448 -0
- package/dist/view/domobserver.d.ts +47 -0
- package/dist/view/domobserver.js +328 -0
- package/dist/view/errors.d.ts +18 -0
- package/dist/view/errors.js +21 -0
- package/dist/view/input.d.ts +58 -0
- package/dist/view/input.js +704 -0
- package/dist/view/selection.js +198 -0
- package/dist/view/view.d.ts +699 -0
- package/dist/view/view.js +623 -0
- package/dist/view/viewdesc.d.ts +236 -0
- package/dist/view/viewdesc.js +1205 -0
- package/dist/view-css.d.ts +43 -0
- package/dist/view-css.js +124 -0
- package/dist/view.d.ts +6 -0
- package/dist/view.js +4 -0
- package/package.json +200 -4
package/dist/html.d.ts
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { ModelError } from "./model/errors.js";
|
|
2
|
+
import { Slice } from "./model/replace.js";
|
|
3
|
+
import { DOMParser, ParseOptions } from "./model/from_dom.js";
|
|
4
|
+
import { DOMSerializer } from "./model/to_dom.js";
|
|
5
|
+
import { AnySchema } from "./model/schema.js";
|
|
6
|
+
import { Node } from "./model/node.js";
|
|
7
|
+
import { Fragment } from "./model/fragment.js";
|
|
8
|
+
import "./model.js";
|
|
9
|
+
import * as Effect from "effect/Effect";
|
|
10
|
+
import * as Schema from "effect/Schema";
|
|
11
|
+
import * as Context from "effect/Context";
|
|
12
|
+
import * as Layer from "effect/Layer";
|
|
13
|
+
declare const MissingDocumentError_base: Schema.Class<MissingDocumentError, Schema.TaggedStruct<"MissingDocumentError", {
|
|
14
|
+
readonly message: Schema.String;
|
|
15
|
+
}>, import("effect/Cause").YieldableError>;
|
|
16
|
+
/**
|
|
17
|
+
* Raised (as a defect) when no DOM `document` is available: neither one
|
|
18
|
+
* passed in the options nor a global `document`.
|
|
19
|
+
*
|
|
20
|
+
* @since 0.1.0
|
|
21
|
+
* @category errors
|
|
22
|
+
*/
|
|
23
|
+
export declare class MissingDocumentError extends MissingDocumentError_base {}
|
|
24
|
+
/**
|
|
25
|
+
* Something that can parse an HTML string into a document, like the
|
|
26
|
+
* browser's `window.DOMParser`.
|
|
27
|
+
*
|
|
28
|
+
* @since 0.1.0
|
|
29
|
+
* @category models
|
|
30
|
+
*/
|
|
31
|
+
export interface HTMLStringParser {
|
|
32
|
+
readonly parseFromString: (string: string, type: DOMParserSupportedType) => Document;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Options for `toHTML` and `serialize`.
|
|
36
|
+
*
|
|
37
|
+
* @since 0.1.0
|
|
38
|
+
* @category models
|
|
39
|
+
*/
|
|
40
|
+
export interface ToHTMLOptions<Sc extends AnySchema = AnySchema> {
|
|
41
|
+
/** The DOM document used to create nodes. Defaults to the global `document`. */
|
|
42
|
+
readonly document?: Document | undefined;
|
|
43
|
+
/** The serializer to use. Defaults to `DOMSerializer.fromSchema(schema)`. */
|
|
44
|
+
readonly serializer?: DOMSerializer<Sc> | undefined;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Options for `fromHTML`, `sliceFromHTML`, `parse` and `parseSlice`.
|
|
48
|
+
* The model's `ParseOptions` are passed through to the parser.
|
|
49
|
+
*
|
|
50
|
+
* @since 0.1.0
|
|
51
|
+
* @category models
|
|
52
|
+
*/
|
|
53
|
+
export interface FromHTMLOptions<Sc extends AnySchema = AnySchema> extends ParseOptions<Sc> {
|
|
54
|
+
/**
|
|
55
|
+
* The DOM document used to create an inert HTML document to parse
|
|
56
|
+
* into. Defaults to the global `document`. Ignored when `htmlParser`
|
|
57
|
+
* is given.
|
|
58
|
+
*/
|
|
59
|
+
readonly document?: Document | undefined;
|
|
60
|
+
/** An HTML string parser, such as an instance of the browser's `DOMParser`. */
|
|
61
|
+
readonly htmlParser?: HTMLStringParser | undefined;
|
|
62
|
+
/** The parser to use. Defaults to `DOMParser.fromSchema(schema)`. */
|
|
63
|
+
readonly parser?: DOMParser<Sc> | undefined;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Serialize a node or fragment to an HTML string. When given the top
|
|
67
|
+
* node of its schema (usually `doc`), its content is serialized;
|
|
68
|
+
* other nodes are serialized including their own wrapper element (and
|
|
69
|
+
* marks).
|
|
70
|
+
*
|
|
71
|
+
* Throws a `MissingDocumentError` when no document is available, and a
|
|
72
|
+
* `DOMSpecError` for invalid `toDOM` output specs (both programming
|
|
73
|
+
* errors).
|
|
74
|
+
*
|
|
75
|
+
* @since 0.1.0
|
|
76
|
+
* @category serialization
|
|
77
|
+
*/
|
|
78
|
+
export declare const toHTML: <Sc extends AnySchema>(content: Node<Sc, any> | Fragment<Sc, any>, options?: ToHTMLOptions<Sc>) => string;
|
|
79
|
+
/**
|
|
80
|
+
* Like `fromHTML`, but throws a `ModelError` when the content can't be
|
|
81
|
+
* parsed into a valid document.
|
|
82
|
+
*
|
|
83
|
+
* @since 0.1.0
|
|
84
|
+
* @category parsing
|
|
85
|
+
*/
|
|
86
|
+
export declare const fromHTMLUnsafe: <Sc extends AnySchema>(schema: Sc, html: string, options?: FromHTMLOptions<Sc>) => Node<Sc>;
|
|
87
|
+
/**
|
|
88
|
+
* Parse an HTML string into a document for the given schema.
|
|
89
|
+
*
|
|
90
|
+
* @since 0.1.0
|
|
91
|
+
* @category parsing
|
|
92
|
+
*/
|
|
93
|
+
export declare const fromHTML: <Sc extends AnySchema>(schema: Sc, html: string, options?: FromHTMLOptions<Sc>) => Effect.Effect<Node<Sc>, ModelError>;
|
|
94
|
+
/**
|
|
95
|
+
* Like `sliceFromHTML`, but throws a `ModelError` on failure.
|
|
96
|
+
*
|
|
97
|
+
* @since 0.1.0
|
|
98
|
+
* @category parsing
|
|
99
|
+
*/
|
|
100
|
+
export declare const sliceFromHTMLUnsafe: <Sc extends AnySchema>(schema: Sc, html: string, options?: FromHTMLOptions<Sc>) => Slice<Sc>;
|
|
101
|
+
/**
|
|
102
|
+
* Parse an HTML string into a slice that is open at the sides (see
|
|
103
|
+
* `DOMParser.parseSlice`), as for pasted content.
|
|
104
|
+
*
|
|
105
|
+
* @since 0.1.0
|
|
106
|
+
* @category parsing
|
|
107
|
+
*/
|
|
108
|
+
export declare const sliceFromHTML: <Sc extends AnySchema>(schema: Sc, html: string, options?: FromHTMLOptions<Sc>) => Effect.Effect<Slice<Sc>, ModelError>;
|
|
109
|
+
declare const HtmlEnvironment_base: Context.ServiceClass<HtmlEnvironment, "effect-richtext/html/HtmlEnvironment", {
|
|
110
|
+
readonly document: Document;
|
|
111
|
+
}>;
|
|
112
|
+
/**
|
|
113
|
+
* A service providing the DOM `document` used to serialize and parse
|
|
114
|
+
* HTML.
|
|
115
|
+
*
|
|
116
|
+
* @since 0.1.0
|
|
117
|
+
* @category services
|
|
118
|
+
*/
|
|
119
|
+
export declare class HtmlEnvironment extends HtmlEnvironment_base {
|
|
120
|
+
/**
|
|
121
|
+
* A layer providing the given document.
|
|
122
|
+
*
|
|
123
|
+
* @since 0.1.0
|
|
124
|
+
*/
|
|
125
|
+
static readonly layer: (document: Document) => Layer.Layer<HtmlEnvironment>;
|
|
126
|
+
/**
|
|
127
|
+
* A layer reading `globalThis.document` when built. Dies with a
|
|
128
|
+
* `MissingDocumentError` when there is no global document.
|
|
129
|
+
*
|
|
130
|
+
* @since 0.1.0
|
|
131
|
+
*/
|
|
132
|
+
static readonly layerGlobal: Layer.Layer<HtmlEnvironment>;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Serialize a node or fragment to an HTML string (see `toHTML`), using
|
|
136
|
+
* the `HtmlEnvironment`'s document.
|
|
137
|
+
*
|
|
138
|
+
* @since 0.1.0
|
|
139
|
+
* @category serialization
|
|
140
|
+
*/
|
|
141
|
+
export declare const serialize: <Sc extends AnySchema>(content: Node<Sc, any> | Fragment<Sc, any>, options?: Omit<ToHTMLOptions<Sc>, "document">) => Effect.Effect<string, never, HtmlEnvironment>;
|
|
142
|
+
/**
|
|
143
|
+
* Parse an HTML string into a document (see `fromHTML`), using the
|
|
144
|
+
* `HtmlEnvironment`'s document.
|
|
145
|
+
*
|
|
146
|
+
* @since 0.1.0
|
|
147
|
+
* @category parsing
|
|
148
|
+
*/
|
|
149
|
+
export declare const parse: <Sc extends AnySchema>(schema: Sc, html: string, options?: Omit<FromHTMLOptions<Sc>, "document">) => Effect.Effect<Node<Sc>, ModelError, HtmlEnvironment>;
|
|
150
|
+
/**
|
|
151
|
+
* Parse an HTML string into an open slice (see `sliceFromHTML`), using
|
|
152
|
+
* the `HtmlEnvironment`'s document.
|
|
153
|
+
*
|
|
154
|
+
* @since 0.1.0
|
|
155
|
+
* @category parsing
|
|
156
|
+
*/
|
|
157
|
+
export declare const parseSlice: <Sc extends AnySchema>(schema: Sc, html: string, options?: Omit<FromHTMLOptions<Sc>, "document">) => Effect.Effect<Slice<Sc>, ModelError, HtmlEnvironment>;
|
|
158
|
+
export {};
|
package/dist/html.js
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { attempt } from "./internal/attempt.js";
|
|
2
|
+
import { DOMParser } from "./model/from_dom.js";
|
|
3
|
+
import { DOMSerializer } from "./model/to_dom.js";
|
|
4
|
+
import { Fragment } from "./model/fragment.js";
|
|
5
|
+
import { isModelError } from "./model/errors.js";
|
|
6
|
+
import * as Effect from "effect/Effect";
|
|
7
|
+
import * as Option from "effect/Option";
|
|
8
|
+
import * as Schema from "effect/Schema";
|
|
9
|
+
import * as Context from "effect/Context";
|
|
10
|
+
import * as Layer from "effect/Layer";
|
|
11
|
+
// src/html.ts
|
|
12
|
+
/**
|
|
13
|
+
* Small helpers converting between documents and HTML strings, built
|
|
14
|
+
* on the model's `DOMParser` and `DOMSerializer`.
|
|
15
|
+
*
|
|
16
|
+
* The helpers need a DOM implementation. In the browser the global
|
|
17
|
+
* `document` is used; elsewhere (for example in Node with `happy-dom`
|
|
18
|
+
* or `jsdom`), pass a `document` in the options, or use the Effect
|
|
19
|
+
* variants (`serialize`, `parse`, `parseSlice`), which read the
|
|
20
|
+
* document from the `HtmlEnvironment` service.
|
|
21
|
+
*
|
|
22
|
+
* ```ts skip-type-checking
|
|
23
|
+
* const html = toHTML(doc) // "<p>Hello</p>"
|
|
24
|
+
* const doc2 = yield* fromHTML(schema, html)
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* @since 0.1.0
|
|
28
|
+
*/
|
|
29
|
+
/**
|
|
30
|
+
* Raised (as a defect) when no DOM `document` is available: neither one
|
|
31
|
+
* passed in the options nor a global `document`.
|
|
32
|
+
*
|
|
33
|
+
* @since 0.1.0
|
|
34
|
+
* @category errors
|
|
35
|
+
*/
|
|
36
|
+
var MissingDocumentError = class extends Schema.TaggedError()("MissingDocumentError", { message: Schema.String }) {};
|
|
37
|
+
const resolveDocument = (document) => {
|
|
38
|
+
const result = document ?? globalThis.document;
|
|
39
|
+
if (!result) throw new MissingDocumentError({ message: "No DOM document available: pass a `document` option or provide an HtmlEnvironment" });
|
|
40
|
+
return result;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Serialize a node or fragment to an HTML string. When given the top
|
|
44
|
+
* node of its schema (usually `doc`), its content is serialized;
|
|
45
|
+
* other nodes are serialized including their own wrapper element (and
|
|
46
|
+
* marks).
|
|
47
|
+
*
|
|
48
|
+
* Throws a `MissingDocumentError` when no document is available, and a
|
|
49
|
+
* `DOMSpecError` for invalid `toDOM` output specs (both programming
|
|
50
|
+
* errors).
|
|
51
|
+
*
|
|
52
|
+
* @since 0.1.0
|
|
53
|
+
* @category serialization
|
|
54
|
+
*/
|
|
55
|
+
const toHTML = (content, options = {}) => {
|
|
56
|
+
const document = resolveDocument(options.document);
|
|
57
|
+
const container = document.createElement("div");
|
|
58
|
+
if (content instanceof Fragment) {
|
|
59
|
+
const first = content.firstChild;
|
|
60
|
+
if (Option.isNone(first)) return "";
|
|
61
|
+
const schema = first.value.type.schema;
|
|
62
|
+
(options.serializer ?? DOMSerializer.fromSchema(schema)).serializeFragment(content, { document }, container);
|
|
63
|
+
} else {
|
|
64
|
+
const schema = content.type.schema;
|
|
65
|
+
const serializer = options.serializer ?? DOMSerializer.fromSchema(schema);
|
|
66
|
+
if (content.type === schema.topNodeType) serializer.serializeFragment(content.content, { document }, container);
|
|
67
|
+
else container.appendChild(serializer.serializeNode(content, { document }));
|
|
68
|
+
}
|
|
69
|
+
return container.innerHTML;
|
|
70
|
+
};
|
|
71
|
+
const parseDOM = (html, options) => {
|
|
72
|
+
if (options.htmlParser) return options.htmlParser.parseFromString(html, "text/html").body;
|
|
73
|
+
const inert = resolveDocument(options.document).implementation.createHTMLDocument("");
|
|
74
|
+
inert.body.innerHTML = html;
|
|
75
|
+
return inert.body;
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Like `fromHTML`, but throws a `ModelError` when the content can't be
|
|
79
|
+
* parsed into a valid document.
|
|
80
|
+
*
|
|
81
|
+
* @since 0.1.0
|
|
82
|
+
* @category parsing
|
|
83
|
+
*/
|
|
84
|
+
const fromHTMLUnsafe = (schema, html, options = {}) => (options.parser ?? DOMParser.fromSchema(schema)).parseUnsafe(parseDOM(html, options), options);
|
|
85
|
+
/**
|
|
86
|
+
* Parse an HTML string into a document for the given schema.
|
|
87
|
+
*
|
|
88
|
+
* @since 0.1.0
|
|
89
|
+
* @category parsing
|
|
90
|
+
*/
|
|
91
|
+
const fromHTML = (schema, html, options = {}) => attempt(() => fromHTMLUnsafe(schema, html, options), isModelError);
|
|
92
|
+
/**
|
|
93
|
+
* Like `sliceFromHTML`, but throws a `ModelError` on failure.
|
|
94
|
+
*
|
|
95
|
+
* @since 0.1.0
|
|
96
|
+
* @category parsing
|
|
97
|
+
*/
|
|
98
|
+
const sliceFromHTMLUnsafe = (schema, html, options = {}) => (options.parser ?? DOMParser.fromSchema(schema)).parseSliceUnsafe(parseDOM(html, options), options);
|
|
99
|
+
/**
|
|
100
|
+
* Parse an HTML string into a slice that is open at the sides (see
|
|
101
|
+
* `DOMParser.parseSlice`), as for pasted content.
|
|
102
|
+
*
|
|
103
|
+
* @since 0.1.0
|
|
104
|
+
* @category parsing
|
|
105
|
+
*/
|
|
106
|
+
const sliceFromHTML = (schema, html, options = {}) => attempt(() => sliceFromHTMLUnsafe(schema, html, options), isModelError);
|
|
107
|
+
/**
|
|
108
|
+
* A service providing the DOM `document` used to serialize and parse
|
|
109
|
+
* HTML.
|
|
110
|
+
*
|
|
111
|
+
* @since 0.1.0
|
|
112
|
+
* @category services
|
|
113
|
+
*/
|
|
114
|
+
var HtmlEnvironment = class HtmlEnvironment extends Context.Service()("effect-richtext/html/HtmlEnvironment") {
|
|
115
|
+
static {
|
|
116
|
+
this.layer = (document) => Layer.succeed(HtmlEnvironment, { document });
|
|
117
|
+
}
|
|
118
|
+
static {
|
|
119
|
+
this.layerGlobal = Layer.effect(HtmlEnvironment, Effect.sync(() => ({ document: resolveDocument(void 0) })));
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
/**
|
|
123
|
+
* Serialize a node or fragment to an HTML string (see `toHTML`), using
|
|
124
|
+
* the `HtmlEnvironment`'s document.
|
|
125
|
+
*
|
|
126
|
+
* @since 0.1.0
|
|
127
|
+
* @category serialization
|
|
128
|
+
*/
|
|
129
|
+
const serialize = (content, options = {}) => Effect.gen(function* () {
|
|
130
|
+
const { document } = yield* HtmlEnvironment;
|
|
131
|
+
return yield* Effect.sync(() => toHTML(content, {
|
|
132
|
+
...options,
|
|
133
|
+
document
|
|
134
|
+
}));
|
|
135
|
+
});
|
|
136
|
+
/**
|
|
137
|
+
* Parse an HTML string into a document (see `fromHTML`), using the
|
|
138
|
+
* `HtmlEnvironment`'s document.
|
|
139
|
+
*
|
|
140
|
+
* @since 0.1.0
|
|
141
|
+
* @category parsing
|
|
142
|
+
*/
|
|
143
|
+
const parse = (schema, html, options = {}) => Effect.gen(function* () {
|
|
144
|
+
const { document } = yield* HtmlEnvironment;
|
|
145
|
+
return yield* fromHTML(schema, html, {
|
|
146
|
+
...options,
|
|
147
|
+
document
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
/**
|
|
151
|
+
* Parse an HTML string into an open slice (see `sliceFromHTML`), using
|
|
152
|
+
* the `HtmlEnvironment`'s document.
|
|
153
|
+
*
|
|
154
|
+
* @since 0.1.0
|
|
155
|
+
* @category parsing
|
|
156
|
+
*/
|
|
157
|
+
const parseSlice = (schema, html, options = {}) => Effect.gen(function* () {
|
|
158
|
+
const { document } = yield* HtmlEnvironment;
|
|
159
|
+
return yield* sliceFromHTML(schema, html, {
|
|
160
|
+
...options,
|
|
161
|
+
document
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
export { HtmlEnvironment, MissingDocumentError, fromHTML, fromHTMLUnsafe, parse, parseSlice, serialize, sliceFromHTML, sliceFromHTMLUnsafe, toHTML };
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import { AnySchema, NodeAttrsInput, NodeNames, NodeType } from "./model/schema.js";
|
|
2
|
+
import { Node } from "./model/node.js";
|
|
3
|
+
import "./model.js";
|
|
4
|
+
import { Dispatch, Transaction } from "./state/transaction.js";
|
|
5
|
+
import { EditorState } from "./state/state.js";
|
|
6
|
+
import { EditorViewLike, Plugin } from "./state/plugin.js";
|
|
7
|
+
import "./state.js";
|
|
8
|
+
import * as Effect from "effect/Effect";
|
|
9
|
+
import * as Option from "effect/Option";
|
|
10
|
+
/**
|
|
11
|
+
* What an input rule handler may return: a transaction describing the
|
|
12
|
+
* rule's effect, `null`/`undefined` (or `None`) when the input was not
|
|
13
|
+
* handled, an `Option` of a transaction, or an Effect producing such an
|
|
14
|
+
* `Option`.
|
|
15
|
+
*
|
|
16
|
+
* @since 0.1.0
|
|
17
|
+
* @category models
|
|
18
|
+
*/
|
|
19
|
+
export type InputRuleResult<Sc extends AnySchema = AnySchema, E = never, R = never> = Transaction<Sc> | null | undefined | Option.Option<Transaction<Sc>> | Effect.Effect<Option.Option<Transaction<Sc>>, E, R>;
|
|
20
|
+
/**
|
|
21
|
+
* An input rule handler. Called with the editor state, the match array
|
|
22
|
+
* produced by `RegExp.exec`, and the start and end of the matched range.
|
|
23
|
+
*
|
|
24
|
+
* @since 0.1.0
|
|
25
|
+
* @category models
|
|
26
|
+
*/
|
|
27
|
+
export type InputRuleHandler<Sc extends AnySchema = AnySchema, E = never, R = never> = (state: EditorState<Sc, any, any>, match: RegExpMatchArray, start: number, end: number) => InputRuleResult<Sc, E, R>;
|
|
28
|
+
/**
|
|
29
|
+
* Options for `InputRule`.
|
|
30
|
+
*
|
|
31
|
+
* @since 0.1.0
|
|
32
|
+
* @category models
|
|
33
|
+
*/
|
|
34
|
+
export interface InputRuleOptions {
|
|
35
|
+
/**
|
|
36
|
+
* When set to false, `undoInputRule` doesn't work on this rule.
|
|
37
|
+
*/
|
|
38
|
+
readonly undoable?: boolean | undefined;
|
|
39
|
+
/**
|
|
40
|
+
* By default, input rules will not apply inside nodes marked as
|
|
41
|
+
* `code`. Set this to true to change that, or to `"only"` to _only_
|
|
42
|
+
* match in such nodes.
|
|
43
|
+
*/
|
|
44
|
+
readonly inCode?: boolean | "only" | undefined;
|
|
45
|
+
/**
|
|
46
|
+
* When set to `false`, this rule will not fire inside marks marked as
|
|
47
|
+
* `code`. The default is `true`.
|
|
48
|
+
*/
|
|
49
|
+
readonly inCodeMark?: boolean | undefined;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* @since 0.1.0
|
|
53
|
+
* @category symbols
|
|
54
|
+
*/
|
|
55
|
+
export declare const InputRuleTypeId: unique symbol;
|
|
56
|
+
/**
|
|
57
|
+
* @since 0.1.0
|
|
58
|
+
* @category symbols
|
|
59
|
+
*/
|
|
60
|
+
export type InputRuleTypeId = typeof InputRuleTypeId;
|
|
61
|
+
/**
|
|
62
|
+
* Input rules are regular expressions describing a piece of text that,
|
|
63
|
+
* when typed, causes something to happen. This might be changing two
|
|
64
|
+
* dashes into an emdash, wrapping a paragraph starting with `"> "` into
|
|
65
|
+
* a blockquote, or something entirely different.
|
|
66
|
+
*
|
|
67
|
+
* @since 0.1.0
|
|
68
|
+
* @category models
|
|
69
|
+
*/
|
|
70
|
+
export declare class InputRule<Sc extends AnySchema = AnySchema, E = never, R = never> {
|
|
71
|
+
/** The regular expression the text before the cursor is matched against. */
|
|
72
|
+
readonly match: RegExp;
|
|
73
|
+
readonly [InputRuleTypeId]: {
|
|
74
|
+
readonly _E: (_: never) => E;
|
|
75
|
+
readonly _R: (_: never) => R;
|
|
76
|
+
};
|
|
77
|
+
/** @internal */
|
|
78
|
+
readonly handler: InputRuleHandler<Sc, E, R>;
|
|
79
|
+
/** @internal */
|
|
80
|
+
readonly undoable: boolean;
|
|
81
|
+
/** Whether the rule applies in code nodes (`"only"`: only there). */
|
|
82
|
+
readonly inCode: boolean | "only";
|
|
83
|
+
/** Whether the rule applies inside code marks. */
|
|
84
|
+
readonly inCodeMark: boolean;
|
|
85
|
+
/**
|
|
86
|
+
* Create an input rule. The rule applies when the user typed something
|
|
87
|
+
* and the text directly in front of the cursor matches `match`, which
|
|
88
|
+
* should end with `$`.
|
|
89
|
+
*
|
|
90
|
+
* The `handler` can be a string, in which case the matched text, or
|
|
91
|
+
* the first matched group in the regexp, is replaced by that string.
|
|
92
|
+
*
|
|
93
|
+
* Or it can be a function, which will be called with the match array
|
|
94
|
+
* produced by `RegExp.exec`, as well as the start and end of the
|
|
95
|
+
* matched range, and which can return a transaction that describes the
|
|
96
|
+
* rule's effect, or `null` to indicate the input was not handled. It
|
|
97
|
+
* may also return an `Option` of a transaction, or an Effect producing
|
|
98
|
+
* one.
|
|
99
|
+
*/
|
|
100
|
+
constructor(
|
|
101
|
+
/** The regular expression the text before the cursor is matched against. */
|
|
102
|
+
match: RegExp, handler: string | InputRuleHandler<Sc, E, R>, options?: InputRuleOptions);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* The union of the error types of the given input rules.
|
|
106
|
+
*
|
|
107
|
+
* @since 0.1.0
|
|
108
|
+
* @category type-level
|
|
109
|
+
*/
|
|
110
|
+
export type InputRuleErrors<Rules extends ReadonlyArray<InputRule<any, any, any>>> = Rules[number] extends (infer X) ? X extends InputRule<any, infer E, any> ? E : never : never;
|
|
111
|
+
/**
|
|
112
|
+
* The union of the requirements of the given input rules.
|
|
113
|
+
*
|
|
114
|
+
* @since 0.1.0
|
|
115
|
+
* @category type-level
|
|
116
|
+
*/
|
|
117
|
+
export type InputRuleRequirements<Rules extends ReadonlyArray<InputRule<any, any, any>>> = Rules[number] extends (infer X) ? X extends InputRule<any, any, infer R> ? R : never : never;
|
|
118
|
+
/**
|
|
119
|
+
* The last applied (undoable) input rule, as remembered by the input
|
|
120
|
+
* rules plugin so that `undoInputRule` can revert it.
|
|
121
|
+
*
|
|
122
|
+
* @since 0.1.0
|
|
123
|
+
* @category models
|
|
124
|
+
*/
|
|
125
|
+
export interface AppliedInputRule<Sc extends AnySchema = AnySchema> {
|
|
126
|
+
/** The transaction produced by the rule. */
|
|
127
|
+
readonly transform: Transaction<Sc>;
|
|
128
|
+
/** The start of the text input that triggered the rule. */
|
|
129
|
+
readonly from: number;
|
|
130
|
+
/** The end of the text input that triggered the rule. */
|
|
131
|
+
readonly to: number;
|
|
132
|
+
/** The typed text that triggered the rule. */
|
|
133
|
+
readonly text: string;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* The state of an input rules plugin: the last applied rule, while it
|
|
137
|
+
* is still undoable (until the selection or document changes).
|
|
138
|
+
*
|
|
139
|
+
* @since 0.1.0
|
|
140
|
+
* @category models
|
|
141
|
+
*/
|
|
142
|
+
export type InputRulesState = Option.Option<AppliedInputRule<any>>;
|
|
143
|
+
/**
|
|
144
|
+
* Options for `inputRules`.
|
|
145
|
+
*
|
|
146
|
+
* @since 0.1.0
|
|
147
|
+
* @category models
|
|
148
|
+
*/
|
|
149
|
+
export interface InputRulesConfig<Rules extends ReadonlyArray<InputRule<any, any, any>>> {
|
|
150
|
+
readonly rules: Rules;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Create an input rules plugin. When enabled, it will cause text input
|
|
154
|
+
* that matches any of the given rules to trigger the rule's action.
|
|
155
|
+
*
|
|
156
|
+
* Rules are tried in order. Rules whose handler returns an Effect run
|
|
157
|
+
* with the view's context: when such an effect completes synchronously,
|
|
158
|
+
* it behaves exactly like a plain rule. When it is asynchronous, the
|
|
159
|
+
* input is considered handled; when it eventually finds no transaction
|
|
160
|
+
* (or fails), the typed text is inserted, and when it produces a
|
|
161
|
+
* transaction, it is dispatched—both only if the editor state didn't
|
|
162
|
+
* change in the meantime.
|
|
163
|
+
*
|
|
164
|
+
* @since 0.1.0
|
|
165
|
+
* @category constructors
|
|
166
|
+
*/
|
|
167
|
+
export declare const inputRules: <const Rules extends ReadonlyArray<InputRule<any, any, any>>>({ rules }: InputRulesConfig<Rules>) => Plugin<InputRulesState, AnySchema, InputRuleErrors<Rules>, InputRuleRequirements<Rules>>;
|
|
168
|
+
/**
|
|
169
|
+
* A command that works with any schema and needs no services.
|
|
170
|
+
*
|
|
171
|
+
* @since 0.1.0
|
|
172
|
+
* @category models
|
|
173
|
+
*/
|
|
174
|
+
export type InputRuleCommand = <Sc extends AnySchema>(state: EditorState<Sc, any, any>, dispatch?: Dispatch<Sc>, view?: EditorViewLike) => Effect.Effect<boolean>;
|
|
175
|
+
/**
|
|
176
|
+
* This is a command that will undo an input rule, if applying such a
|
|
177
|
+
* rule was the last thing that the user did.
|
|
178
|
+
*
|
|
179
|
+
* @since 0.1.0
|
|
180
|
+
* @category commands
|
|
181
|
+
*/
|
|
182
|
+
export declare const undoInputRule: InputRuleCommand;
|
|
183
|
+
/**
|
|
184
|
+
* Attributes for a rule-built node: fixed, or computed from the match.
|
|
185
|
+
*
|
|
186
|
+
* @since 0.1.0
|
|
187
|
+
* @category models
|
|
188
|
+
*/
|
|
189
|
+
export type InputRuleAttrs<Sc extends AnySchema, N extends NodeNames<Sc>> = NodeAttrsInput<Sc, N> | null | ((match: RegExpMatchArray) => NodeAttrsInput<Sc, N> | null);
|
|
190
|
+
/**
|
|
191
|
+
* Build an input rule for automatically wrapping a textblock when a
|
|
192
|
+
* given string is typed. The `regexp` argument is directly passed
|
|
193
|
+
* through to the `InputRule` constructor. You'll probably want the
|
|
194
|
+
* regexp to start with `^`, so that the pattern can only occur at the
|
|
195
|
+
* start of a textblock.
|
|
196
|
+
*
|
|
197
|
+
* `nodeType` is the type of node to wrap in. If it needs attributes,
|
|
198
|
+
* you can either pass them directly, or pass a function that will
|
|
199
|
+
* compute them from the regular expression match.
|
|
200
|
+
*
|
|
201
|
+
* By default, if there's a node with the same type above the newly
|
|
202
|
+
* wrapped node, the rule will try to join those two nodes. You can pass
|
|
203
|
+
* a join predicate, which takes a regular expression match and the node
|
|
204
|
+
* before the wrapped node, and can return a boolean to indicate whether
|
|
205
|
+
* a join should happen.
|
|
206
|
+
*
|
|
207
|
+
* @since 0.1.0
|
|
208
|
+
* @category constructors
|
|
209
|
+
*/
|
|
210
|
+
export declare const wrappingInputRule: <Sc extends AnySchema, N extends NodeNames<Sc>>(regexp: RegExp, nodeType: NodeType<Sc, N>, getAttrs?: InputRuleAttrs<Sc, N>, joinPredicate?: (match: RegExpMatchArray, node: Node<Sc>) => boolean) => InputRule<Sc>;
|
|
211
|
+
/**
|
|
212
|
+
* Build an input rule that changes the type of a textblock when the
|
|
213
|
+
* matched text is typed into it. You'll usually want to start your
|
|
214
|
+
* regexp with `^` so that it is only matched at the start of a
|
|
215
|
+
* textblock. The optional `getAttrs` parameter can be used to compute
|
|
216
|
+
* the new node's attributes, and works the same as in the
|
|
217
|
+
* `wrappingInputRule` function.
|
|
218
|
+
*
|
|
219
|
+
* @since 0.1.0
|
|
220
|
+
* @category constructors
|
|
221
|
+
*/
|
|
222
|
+
export declare const textblockTypeInputRule: <Sc extends AnySchema, N extends NodeNames<Sc>>(regexp: RegExp, nodeType: NodeType<Sc, N>, getAttrs?: InputRuleAttrs<Sc, N>) => InputRule<Sc>;
|
|
223
|
+
/**
|
|
224
|
+
* Converts double dashes to an emdash.
|
|
225
|
+
*
|
|
226
|
+
* @since 0.1.0
|
|
227
|
+
* @category rules
|
|
228
|
+
*/
|
|
229
|
+
export declare const emDash: InputRule;
|
|
230
|
+
/**
|
|
231
|
+
* Converts three dots to an ellipsis character.
|
|
232
|
+
*
|
|
233
|
+
* @since 0.1.0
|
|
234
|
+
* @category rules
|
|
235
|
+
*/
|
|
236
|
+
export declare const ellipsis: InputRule;
|
|
237
|
+
/**
|
|
238
|
+
* “Smart” opening double quotes.
|
|
239
|
+
*
|
|
240
|
+
* @since 0.1.0
|
|
241
|
+
* @category rules
|
|
242
|
+
*/
|
|
243
|
+
export declare const openDoubleQuote: InputRule;
|
|
244
|
+
/**
|
|
245
|
+
* “Smart” closing double quotes.
|
|
246
|
+
*
|
|
247
|
+
* @since 0.1.0
|
|
248
|
+
* @category rules
|
|
249
|
+
*/
|
|
250
|
+
export declare const closeDoubleQuote: InputRule;
|
|
251
|
+
/**
|
|
252
|
+
* “Smart” opening single quotes.
|
|
253
|
+
*
|
|
254
|
+
* @since 0.1.0
|
|
255
|
+
* @category rules
|
|
256
|
+
*/
|
|
257
|
+
export declare const openSingleQuote: InputRule;
|
|
258
|
+
/**
|
|
259
|
+
* “Smart” closing single quotes.
|
|
260
|
+
*
|
|
261
|
+
* @since 0.1.0
|
|
262
|
+
* @category rules
|
|
263
|
+
*/
|
|
264
|
+
export declare const closeSingleQuote: InputRule;
|
|
265
|
+
/**
|
|
266
|
+
* Smart-quote related input rules.
|
|
267
|
+
*
|
|
268
|
+
* @since 0.1.0
|
|
269
|
+
* @category rules
|
|
270
|
+
*/
|
|
271
|
+
export declare const smartQuotes: ReadonlyArray<InputRule>;
|