@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.
Files changed (111) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/LICENSE.txt +21 -0
  3. package/README.md +912 -2
  4. package/THIRD_PARTY_LICENSES.txt +93 -0
  5. package/dist/ReactWordgard.d.ts +13 -0
  6. package/dist/ReactWordgard.js +94 -0
  7. package/dist/changes.d.ts +14 -0
  8. package/dist/changes.js +163 -0
  9. package/dist/components/ChildNodeViews.d.ts +8 -0
  10. package/dist/components/ChildNodeViews.js +36 -0
  11. package/dist/components/DecorationRoot.d.ts +13 -0
  12. package/dist/components/DecorationRoot.js +31 -0
  13. package/dist/components/ErrorBoundary.d.ts +18 -0
  14. package/dist/components/ErrorBoundary.js +14 -0
  15. package/dist/components/SeparatorHackView.d.ts +2 -0
  16. package/dist/components/SeparatorHackView.js +4 -0
  17. package/dist/components/ShapeView.d.ts +12 -0
  18. package/dist/components/ShapeView.js +24 -0
  19. package/dist/components/TextNodeView.d.ts +5 -0
  20. package/dist/components/TextNodeView.js +4 -0
  21. package/dist/components/TrailingHackView.d.ts +2 -0
  22. package/dist/components/TrailingHackView.js +4 -0
  23. package/dist/components/WidgetView.d.ts +10 -0
  24. package/dist/components/WidgetView.js +32 -0
  25. package/dist/components/WordgardDoc.d.ts +9 -0
  26. package/dist/components/WordgardDoc.js +36 -0
  27. package/dist/components/WordgardEditor.d.ts +15 -0
  28. package/dist/components/WordgardEditor.js +17 -0
  29. package/dist/components/WordgardScope.d.ts +14 -0
  30. package/dist/components/WordgardScope.js +14 -0
  31. package/dist/components/WrapperView.d.ts +12 -0
  32. package/dist/components/WrapperView.js +23 -0
  33. package/dist/components/marks/MarkView.d.ts +11 -0
  34. package/dist/components/marks/MarkView.js +10 -0
  35. package/dist/components/marks/ReactMarkView.d.ts +10 -0
  36. package/dist/components/marks/ReactMarkView.js +30 -0
  37. package/dist/components/nodes/DefaultNodeView.d.ts +16 -0
  38. package/dist/components/nodes/DefaultNodeView.js +112 -0
  39. package/dist/components/nodes/DocNodeView.d.ts +7 -0
  40. package/dist/components/nodes/DocNodeView.js +5 -0
  41. package/dist/composition.d.ts +23 -0
  42. package/dist/composition.js +95 -0
  43. package/dist/contexts/DecorationContext.d.ts +15 -0
  44. package/dist/contexts/DecorationContext.js +35 -0
  45. package/dist/contexts/DecorationRootContext.d.ts +6 -0
  46. package/dist/contexts/DecorationRootContext.js +4 -0
  47. package/dist/contexts/EditorContext.d.ts +27 -0
  48. package/dist/contexts/EditorContext.js +87 -0
  49. package/dist/contexts/RenderStoreContext.d.ts +15 -0
  50. package/dist/contexts/RenderStoreContext.js +41 -0
  51. package/dist/contexts/editorEvents.d.ts +12 -0
  52. package/dist/contexts/editorEvents.js +92 -0
  53. package/dist/decorations/DecoIterator.d.ts +43 -0
  54. package/dist/decorations/DecoIterator.js +462 -0
  55. package/dist/decorations/ReactMarkType.d.ts +41 -0
  56. package/dist/decorations/ReactMarkType.js +47 -0
  57. package/dist/decorations/ReactShapeType.d.ts +65 -0
  58. package/dist/decorations/ReactShapeType.js +202 -0
  59. package/dist/decorations/ReactWidgetType.d.ts +29 -0
  60. package/dist/decorations/ReactWidgetType.js +67 -0
  61. package/dist/decorations/ReactWrapperType.d.ts +31 -0
  62. package/dist/decorations/ReactWrapperType.js +41 -0
  63. package/dist/decorations/iterDeco.d.ts +82 -0
  64. package/dist/decorations/iterDeco.js +406 -0
  65. package/dist/helpers/attributes.d.ts +2 -0
  66. package/dist/helpers/attributes.js +11 -0
  67. package/dist/helpers/index.d.ts +2 -0
  68. package/dist/helpers/index.js +2 -0
  69. package/dist/helpers/props.d.ts +8 -0
  70. package/dist/helpers/props.js +248 -0
  71. package/dist/helpers/refs.d.ts +6 -0
  72. package/dist/helpers/refs.js +20 -0
  73. package/dist/hooks/useEditorEffect.d.ts +10 -0
  74. package/dist/hooks/useEditorEffect.js +28 -0
  75. package/dist/hooks/useEditorEventCallback.d.ts +8 -0
  76. package/dist/hooks/useEditorEventCallback.js +17 -0
  77. package/dist/hooks/useEditorEventListener.d.ts +10 -0
  78. package/dist/hooks/useEditorEventListener.js +26 -0
  79. package/dist/hooks/useEditorLayoutEffect.d.ts +15 -0
  80. package/dist/hooks/useEditorLayoutEffect.js +34 -0
  81. package/dist/hooks/useEditorState.d.ts +6 -0
  82. package/dist/hooks/useEditorState.js +12 -0
  83. package/dist/hooks/useEditorStateSelector.d.ts +7 -0
  84. package/dist/hooks/useEditorStateSelector.js +11 -0
  85. package/dist/hooks/useIgnoreMutation.d.ts +6 -0
  86. package/dist/hooks/useIgnoreMutation.js +19 -0
  87. package/dist/hooks/useIsNodeSelected.d.ts +2 -0
  88. package/dist/hooks/useIsNodeSelected.js +5 -0
  89. package/dist/hooks/useMergedDOMRefs.d.ts +6 -0
  90. package/dist/hooks/useMergedDOMRefs.js +18 -0
  91. package/dist/hooks/useNodePos.d.ts +4 -0
  92. package/dist/hooks/useNodePos.js +7 -0
  93. package/dist/hooks/useSelectNode.d.ts +6 -0
  94. package/dist/hooks/useSelectNode.js +25 -0
  95. package/dist/hooks/useStopEvent.d.ts +8 -0
  96. package/dist/hooks/useStopEvent.js +21 -0
  97. package/dist/hooks/useWordgard.d.ts +2 -0
  98. package/dist/hooks/useWordgard.js +3 -0
  99. package/dist/index.d.ts +21 -0
  100. package/dist/index.js +21 -0
  101. package/dist/reactKeys.d.ts +39 -0
  102. package/dist/reactKeys.js +188 -0
  103. package/dist/tile.d.ts +32 -0
  104. package/dist/tile.js +1319 -0
  105. package/dist/viewRoots.d.ts +2 -0
  106. package/dist/viewRoots.js +7 -0
  107. package/dist/wordgardInternals.d.ts +211 -0
  108. package/dist/wordgardInternals.js +82 -0
  109. package/package.json +119 -4
  110. package/skills/react-wordgard/SKILL.md +525 -0
  111. 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": … })`.