@stesura/react-wordgard 0.0.0-stage → 0.2.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/CHANGELOG.md +41 -0
- package/LICENSE.txt +21 -0
- package/README.md +912 -2
- package/THIRD_PARTY_LICENSES.txt +93 -0
- package/dist/ReactWordgard.d.ts +13 -0
- package/dist/ReactWordgard.js +94 -0
- package/dist/changes.d.ts +14 -0
- package/dist/changes.js +163 -0
- package/dist/components/ChildNodeViews.d.ts +8 -0
- package/dist/components/ChildNodeViews.js +36 -0
- package/dist/components/DecorationRoot.d.ts +13 -0
- package/dist/components/DecorationRoot.js +31 -0
- package/dist/components/ErrorBoundary.d.ts +18 -0
- package/dist/components/ErrorBoundary.js +14 -0
- package/dist/components/SeparatorHackView.d.ts +2 -0
- package/dist/components/SeparatorHackView.js +4 -0
- package/dist/components/ShapeView.d.ts +12 -0
- package/dist/components/ShapeView.js +24 -0
- package/dist/components/TextNodeView.d.ts +5 -0
- package/dist/components/TextNodeView.js +4 -0
- package/dist/components/TrailingHackView.d.ts +2 -0
- package/dist/components/TrailingHackView.js +4 -0
- package/dist/components/WidgetView.d.ts +10 -0
- package/dist/components/WidgetView.js +32 -0
- package/dist/components/WordgardDoc.d.ts +9 -0
- package/dist/components/WordgardDoc.js +36 -0
- package/dist/components/WordgardEditor.d.ts +15 -0
- package/dist/components/WordgardEditor.js +17 -0
- package/dist/components/WordgardScope.d.ts +14 -0
- package/dist/components/WordgardScope.js +14 -0
- package/dist/components/WrapperView.d.ts +12 -0
- package/dist/components/WrapperView.js +23 -0
- package/dist/components/marks/MarkView.d.ts +11 -0
- package/dist/components/marks/MarkView.js +10 -0
- package/dist/components/marks/ReactMarkView.d.ts +10 -0
- package/dist/components/marks/ReactMarkView.js +30 -0
- package/dist/components/nodes/DefaultNodeView.d.ts +16 -0
- package/dist/components/nodes/DefaultNodeView.js +112 -0
- package/dist/components/nodes/DocNodeView.d.ts +7 -0
- package/dist/components/nodes/DocNodeView.js +5 -0
- package/dist/composition.d.ts +23 -0
- package/dist/composition.js +95 -0
- package/dist/contexts/DecorationContext.d.ts +15 -0
- package/dist/contexts/DecorationContext.js +35 -0
- package/dist/contexts/DecorationRootContext.d.ts +6 -0
- package/dist/contexts/DecorationRootContext.js +4 -0
- package/dist/contexts/EditorContext.d.ts +27 -0
- package/dist/contexts/EditorContext.js +87 -0
- package/dist/contexts/RenderStoreContext.d.ts +15 -0
- package/dist/contexts/RenderStoreContext.js +41 -0
- package/dist/contexts/editorEvents.d.ts +12 -0
- package/dist/contexts/editorEvents.js +92 -0
- package/dist/decorations/DecoIterator.d.ts +43 -0
- package/dist/decorations/DecoIterator.js +462 -0
- package/dist/decorations/ReactMarkType.d.ts +41 -0
- package/dist/decorations/ReactMarkType.js +47 -0
- package/dist/decorations/ReactShapeType.d.ts +65 -0
- package/dist/decorations/ReactShapeType.js +202 -0
- package/dist/decorations/ReactWidgetType.d.ts +29 -0
- package/dist/decorations/ReactWidgetType.js +67 -0
- package/dist/decorations/ReactWrapperType.d.ts +31 -0
- package/dist/decorations/ReactWrapperType.js +41 -0
- package/dist/decorations/iterDeco.d.ts +82 -0
- package/dist/decorations/iterDeco.js +406 -0
- package/dist/helpers/attributes.d.ts +2 -0
- package/dist/helpers/attributes.js +11 -0
- package/dist/helpers/index.d.ts +2 -0
- package/dist/helpers/index.js +2 -0
- package/dist/helpers/props.d.ts +8 -0
- package/dist/helpers/props.js +248 -0
- package/dist/helpers/refs.d.ts +6 -0
- package/dist/helpers/refs.js +20 -0
- package/dist/hooks/useEditorEffect.d.ts +10 -0
- package/dist/hooks/useEditorEffect.js +28 -0
- package/dist/hooks/useEditorEventCallback.d.ts +8 -0
- package/dist/hooks/useEditorEventCallback.js +17 -0
- package/dist/hooks/useEditorEventListener.d.ts +10 -0
- package/dist/hooks/useEditorEventListener.js +26 -0
- package/dist/hooks/useEditorLayoutEffect.d.ts +15 -0
- package/dist/hooks/useEditorLayoutEffect.js +34 -0
- package/dist/hooks/useEditorState.d.ts +6 -0
- package/dist/hooks/useEditorState.js +12 -0
- package/dist/hooks/useEditorStateSelector.d.ts +7 -0
- package/dist/hooks/useEditorStateSelector.js +11 -0
- package/dist/hooks/useIgnoreMutation.d.ts +6 -0
- package/dist/hooks/useIgnoreMutation.js +19 -0
- package/dist/hooks/useIsNodeSelected.d.ts +2 -0
- package/dist/hooks/useIsNodeSelected.js +5 -0
- package/dist/hooks/useMergedDOMRefs.d.ts +6 -0
- package/dist/hooks/useMergedDOMRefs.js +18 -0
- package/dist/hooks/useNodePos.d.ts +4 -0
- package/dist/hooks/useNodePos.js +7 -0
- package/dist/hooks/useSelectNode.d.ts +6 -0
- package/dist/hooks/useSelectNode.js +25 -0
- package/dist/hooks/useStopEvent.d.ts +8 -0
- package/dist/hooks/useStopEvent.js +21 -0
- package/dist/hooks/useWordgard.d.ts +2 -0
- package/dist/hooks/useWordgard.js +3 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +21 -0
- package/dist/reactKeys.d.ts +39 -0
- package/dist/reactKeys.js +188 -0
- package/dist/tile.d.ts +32 -0
- package/dist/tile.js +1319 -0
- package/dist/viewRoots.d.ts +2 -0
- package/dist/viewRoots.js +7 -0
- package/dist/wordgardInternals.d.ts +211 -0
- package/dist/wordgardInternals.js +82 -0
- package/package.json +119 -4
- package/skills/react-wordgard/SKILL.md +525 -0
- package/skills/react-wordgard/references/wordgard.md +189 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# Wordgard, for ProseMirror developers
|
|
2
|
+
|
|
3
|
+
Read this when you're new to Wordgard or porting ProseMirror code (react-prosemirror apps
|
|
4
|
+
included). It covers the model and the traps; for exact signatures, read the typings in
|
|
5
|
+
`node_modules/wordgard/dist/*.d.ts`, whose doc comments are Wordgard's reference manual. Wordgard's
|
|
6
|
+
own guides: https://wordgard.net/docs/guide/ and, for ProseMirror users,
|
|
7
|
+
https://wordgard.net/docs/prosemirror/. Written for Wordgard 0.6.
|
|
8
|
+
|
|
9
|
+
## Modules
|
|
10
|
+
|
|
11
|
+
One package, `wordgard`, in submodules:
|
|
12
|
+
|
|
13
|
+
| Wordgard | Replaces |
|
|
14
|
+
| ------------------ | ----------------------------------------------------------------------------------- |
|
|
15
|
+
| `wordgard/doc` | `prosemirror-model`, `prosemirror-transform` |
|
|
16
|
+
| `wordgard/state` | `prosemirror-state` |
|
|
17
|
+
| `wordgard/editor` | `prosemirror-view`, `prosemirror-keymap`, `prosemirror-menu`, input rules |
|
|
18
|
+
| `wordgard/command` | `prosemirror-commands` |
|
|
19
|
+
| `wordgard/history` | `prosemirror-history` |
|
|
20
|
+
| `wordgard/collab` | `prosemirror-collab` |
|
|
21
|
+
| `wordgard/types` | `prosemirror-schema-basic`, `prosemirror-schema-list`: the node and mark types |
|
|
22
|
+
| `wordgard/schema` | extensions adding those types with their commands, keys and menus (`basicSchema()`) |
|
|
23
|
+
|
|
24
|
+
## The document
|
|
25
|
+
|
|
26
|
+
- **A node is a plot or a leaf.** A `Plot` has content (`plot.content`, an array); a `Leaf` has
|
|
27
|
+
none. Text is a leaf (`Leaf.Text`, its parameter the string), and so is a line break.
|
|
28
|
+
- **A tag is a node's type, parameter and marks.** A leaf is its own tag; a plot has one
|
|
29
|
+
(`plot.tag`). Read a parameter as `plot.tag.param` or `leaf.param`, a mark as
|
|
30
|
+
`node.tag.mark(Type)` (the value, or `undefined`).
|
|
31
|
+
- **One parameter, no `attrs`.** What identifies a node (a heading's level, an image's `src`) is
|
|
32
|
+
its parameter. Anything else (alignment, size, a callout's kind) is a mark on its tag: a mark
|
|
33
|
+
type with a `target`, added or removed without touching the node.
|
|
34
|
+
- **Marks live on tags**, a plot's as a leaf's, and a mark type says which nodes it may go on
|
|
35
|
+
(`target`), not the parent which content it allows.
|
|
36
|
+
- **No content expressions.** A plot type says what it holds (`blockContent: Node.Group.Content`,
|
|
37
|
+
or `inlineContent: true`); finer rules go above the document (corrections, `wordgard/state`).
|
|
38
|
+
- **Positions count as in ProseMirror**: a character or leaf is 1, a plot 2 plus its content.
|
|
39
|
+
`node.length` is ProseMirror's `nodeSize`; the document's excludes its own tokens.
|
|
40
|
+
- **Walking it:** `doc.iterate(from, to, (node, pos, parent, index) => …)` for `descendants` and
|
|
41
|
+
`nodesBetween` (return `false` to skip a node's content), `doc.nodeAt(pos)`, `doc.plotAt(pos)`,
|
|
42
|
+
`doc.resolve(pos)` (a `Pos`, ProseMirror's `ResolvedPos`), `plot.textContent({ blockSeparator })`.
|
|
43
|
+
- **Types are objects, not names.** Import them (`Heading`, `Paragraph` from `wordgard/types`) and
|
|
44
|
+
compare with `node.type == Heading` or `node.is(Heading)`; names are for JSON and debugging.
|
|
45
|
+
|
|
46
|
+
### Defining types
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
import { Leaf, Mark, Node, Plot } from "wordgard/doc";
|
|
50
|
+
import { GardState } from "wordgard/state";
|
|
51
|
+
|
|
52
|
+
// A plot without a parameter: `define` gives its tag.
|
|
53
|
+
export const Callout = Plot.define("Callout", {
|
|
54
|
+
blockContent: Node.Group.Content,
|
|
55
|
+
group: Node.Group.Content,
|
|
56
|
+
shape: { element: "aside", selector: "aside.callout", attributes: { class: "callout" } },
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Data an editing action changes is a mark on the tag, not a parameter.
|
|
60
|
+
export const CalloutKind = Mark.Type.define<string>("CalloutKind", {
|
|
61
|
+
target: Callout,
|
|
62
|
+
validate: "string",
|
|
63
|
+
shape: { attribute: "data-kind", value: 0 },
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// A leaf with a parameter: `Type.define`, and nodes are `Mention.of(param)`.
|
|
67
|
+
export const Mention = Leaf.Type.define<string>("Mention", {
|
|
68
|
+
inline: true,
|
|
69
|
+
validate: "string",
|
|
70
|
+
shape: {
|
|
71
|
+
element: "span",
|
|
72
|
+
selector: "span[data-mention]",
|
|
73
|
+
attributes: (id) => ({ "data-mention": id }),
|
|
74
|
+
readElement: (elt) => elt.getAttribute("data-mention") ?? "",
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// The schema is assembled from the configuration.
|
|
79
|
+
export const schemaPart = GardState.schemaElement.of([Callout, CalloutKind, Mention]);
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
- `Plot.define` / `Leaf.define` / `Mark.define` for types without a parameter;
|
|
83
|
+
`Plot.Type.define<P>` / `Leaf.Type.define<P>` / `Mark.Type.define<P>` with one (`Heading.of(2)`).
|
|
84
|
+
- `shape` is both `toDOM` and `parseDOM`: an element (with `selector` and `readElement` for
|
|
85
|
+
parsing) or, for a mark, an attribute merged onto its node's element. A `structure` shape draws
|
|
86
|
+
inner elements; `0` marks where the content goes.
|
|
87
|
+
|
|
88
|
+
## Changes
|
|
89
|
+
|
|
90
|
+
ProseMirror's steps are gone. A transaction is a spec, every position in it counts in the
|
|
91
|
+
document before it, and its changes are one `ChangeSet`, which also maps positions.
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
import type { Node } from "wordgard/doc";
|
|
95
|
+
import type { Wordgard } from "wordgard/editor";
|
|
96
|
+
import { Heading, Strong } from "wordgard/types";
|
|
97
|
+
|
|
98
|
+
export const examples = (wg: Wordgard, pos: number, node: Node, target: number) => {
|
|
99
|
+
// Insert, mark a range, delete: all in the original document's positions.
|
|
100
|
+
wg.dispatch({
|
|
101
|
+
changes: [
|
|
102
|
+
{ from: 1, insert: [node] },
|
|
103
|
+
{ from: 8, to: 10, add: Strong },
|
|
104
|
+
{ from: 20, to: 30 },
|
|
105
|
+
],
|
|
106
|
+
selection: { anchor: 2 },
|
|
107
|
+
});
|
|
108
|
+
// A parameter change: replace the node's opening token by one of the same type.
|
|
109
|
+
wg.dispatch({ changes: { from: pos, to: pos + 1, insert: [Heading.of(2)] } });
|
|
110
|
+
// A move: delete the node and insert the same object elsewhere.
|
|
111
|
+
wg.dispatch({
|
|
112
|
+
changes: [
|
|
113
|
+
{ from: pos, to: pos + node.length },
|
|
114
|
+
{ from: target, insert: [node] },
|
|
115
|
+
],
|
|
116
|
+
});
|
|
117
|
+
};
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
| ProseMirror | Wordgard |
|
|
121
|
+
| ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------- |
|
|
122
|
+
| `tr.insert`, `tr.replaceWith`, `tr.delete` | `changes: { from, to?, insert? }`; `insert` is an array of tokens |
|
|
123
|
+
| `tr.addMark`, `tr.removeMark` | `{ from, to, add: Mark }`, `{ from, to, remove: Mark }` |
|
|
124
|
+
| `tr.setNodeMarkup`, `tr.setNodeAttribute` | a retag (`{ from: pos, to: pos + 1, insert: [Type.of(param)] }`) or a mark on the tag (`{ from: pos, add: Type.of(value) }`) |
|
|
125
|
+
| `tr.mapping.map(pos)` | `tr.changes.mapPos(pos, assoc?)` |
|
|
126
|
+
| `tr.setMeta` | `Transaction.Annotation` (about the transaction), `Transaction.Effect` (a change to state, mapped through changes) |
|
|
127
|
+
| `tr.steps`, `tr.docChanged` | `tr.changes`, `tr.docChanged` |
|
|
128
|
+
| `state.apply(tr)` | `tr.state` (`state.update(spec)` builds the transaction) |
|
|
129
|
+
| `Slice` with open depths | `Slice`, a flat run of tokens: nodes, `Plot.Tag` (an opening token), `Plot.End` |
|
|
130
|
+
|
|
131
|
+
## Configuration
|
|
132
|
+
|
|
133
|
+
- **Extensions, not plugins.** The configuration is a tree of extensions (CodeMirror 6's system):
|
|
134
|
+
facets (`GardState.Facet`), state fields, precedence (`GardState.prec.highest(…)`), and
|
|
135
|
+
compartments (`new GardState.Compartment()`, `.of(…)`, `.reconfigure(…)` in an effect) or
|
|
136
|
+
`GardState.appendConfig` to change it later.
|
|
137
|
+
- **Plugin state** is a field: `GardState.Field.define({ create, update: (value, tr) => …, provide })`.
|
|
138
|
+
- **Plugin views** are `Wordgard.Plugin.define((wg) => ({ update(update) {}, docUpdate(wg) {} }))`.
|
|
139
|
+
- **Props** are facets: `Wordgard.domEventHandler(type, (event, wg) => handled)`,
|
|
140
|
+
`Wordgard.contentAttributes.of({…})`, `Wordgard.updateListener.of((update) => …)`, decorations.
|
|
141
|
+
- **Keymaps:** `KeyBinding.of({ key: "Mod-b", run: command })`, each binding an extension.
|
|
142
|
+
- **Commands** are `(wg, param) => boolean | Transaction.Spec`, run with
|
|
143
|
+
`Command.dispatch(wg, command, param?)`. `Command.Pure` takes `{ state }` and returns a spec or
|
|
144
|
+
`false`; `Command.bind(command, param)` fixes the parameter for menus and keys;
|
|
145
|
+
`Command.handler(command, other)` lets an extension change what a command does.
|
|
146
|
+
- **History:** `history()` from `wordgard/history`, with the `undo` and `redo` commands.
|
|
147
|
+
- **HTML and JSON:** `parse(schema, element)` and `serialize(doc).toHTML()` from `wordgard/doc`;
|
|
148
|
+
`node.toJSON()` and `state.schema.nodeFromJSON(json)`. A spec's `doc` may be an HTML string.
|
|
149
|
+
|
|
150
|
+
## Selection
|
|
151
|
+
|
|
152
|
+
- `state.selection` (`GardSelection`) holds raw positions (`anchor`, `head`, `from`, `to`,
|
|
153
|
+
`empty`); `state.sel` the resolved ones (`sel.head.parent.node`).
|
|
154
|
+
- Set it in a spec, `selection: { anchor, head? }`, or with `GardSelection.node(pos, node)` for a
|
|
155
|
+
node selection.
|
|
156
|
+
|
|
157
|
+
## The editor
|
|
158
|
+
|
|
159
|
+
- **`Wordgard`** is the view. It updates the DOM asynchronously, on the next frame (its _flush_):
|
|
160
|
+
read layout with `coordsAtPos` and the like, which flush first, or after `wg.flush()`. A
|
|
161
|
+
dispatch during the flush throws.
|
|
162
|
+
- **It draws its own cursor** and handles selection keys itself; bind commands to them.
|
|
163
|
+
- **Decorations** come as point decorations (`Decoration.Point`: widgets, attributes, a node's
|
|
164
|
+
shape or wrapper, at a position), range decorations (`Decoration.Range`: wrappers and attributes
|
|
165
|
+
over a range) and tag decorations (`Decoration.Tag`: for every node of a type, in place of node
|
|
166
|
+
views). Point and range sources are functions of the state called on every update: keep large
|
|
167
|
+
sets in a state field and map them (`set.map(tr.changes)`). `decoratePlots` decorates each plot
|
|
168
|
+
of a type from its own content.
|
|
169
|
+
- **No node views.** A node's DOM is its shape; interactive parts are widgets in it, beside its
|
|
170
|
+
content. With react-wordgard, a React component can be the shape: see `SKILL.md`.
|
|
171
|
+
|
|
172
|
+
## Traps
|
|
173
|
+
|
|
174
|
+
- **Don't replace a node to change its data.** `{ from, to, insert: [newNode] }` makes a new node:
|
|
175
|
+
views remount, and history and collaboration see a replacement. Retag or change a mark.
|
|
176
|
+
- **Don't carry ProseMirror's `attrs` over as one object parameter.** Split identity (the
|
|
177
|
+
parameter) from data that changes (marks with a `target`).
|
|
178
|
+
- **Positions in one spec all count in the original document**: don't adjust the later ones for
|
|
179
|
+
the earlier changes, as with ProseMirror's step-by-step mapping.
|
|
180
|
+
- **Refer to types by object.** `node.type.name == "heading"` breaks when the name differs and
|
|
181
|
+
can't tell two types with one name apart.
|
|
182
|
+
- **Don't read layout right after `dispatch`** without letting the editor flush; and never
|
|
183
|
+
dispatch from inside an update (a plugin's `update`, a field's `update`).
|
|
184
|
+
- **Point and range sources run on every update**: computing a big set there costs every
|
|
185
|
+
keystroke.
|
|
186
|
+
- **Drag-and-drop inside the editor rebuilds what it moves from HTML** (Wordgard 0.6): moved nodes
|
|
187
|
+
are new objects, and whatever their HTML doesn't carry is lost.
|
|
188
|
+
- **`Wordgard.label` names the outer element in 0.6.2**, not the textbox: use
|
|
189
|
+
`Wordgard.contentAttributes.of({ "aria-label": … })`.
|