@tiptap/extension-invisible-characters 3.30.2 → 3.30.3
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/index.cjs +237 -302
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +77 -70
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.ts +77 -70
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +226 -269
- package/dist/index.js.map +1 -1
- package/package.json +8 -8
package/dist/index.js
CHANGED
|
@@ -1,20 +1,17 @@
|
|
|
1
|
-
// src/invisible-characters.ts
|
|
2
1
|
import { Extension } from "@tiptap/core";
|
|
3
|
-
|
|
4
|
-
// src/plugin/InvisibleCharactersPlugin.ts
|
|
5
2
|
import { AllSelection, Plugin, PluginKey } from "@tiptap/pm/state";
|
|
6
|
-
import { DecorationSet } from "@tiptap/pm/view";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
3
|
+
import { Decoration, DecorationSet } from "@tiptap/pm/view";
|
|
4
|
+
//#region src/plugin/reducers.ts
|
|
5
|
+
const stateReducer = (state, visible) => {
|
|
6
|
+
if (visible === void 0) return state;
|
|
7
|
+
return {
|
|
8
|
+
...state,
|
|
9
|
+
visible
|
|
10
|
+
};
|
|
14
11
|
};
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
//#endregion
|
|
13
|
+
//#region src/plugin/style.ts
|
|
14
|
+
const style = `.tiptap-invisible-character {
|
|
18
15
|
height: 0;
|
|
19
16
|
padding: 0;
|
|
20
17
|
pointer-events: none;
|
|
@@ -33,15 +30,15 @@ var style = `.tiptap-invisible-character {
|
|
|
33
30
|
}
|
|
34
31
|
|
|
35
32
|
.tiptap-invisible-character--space::before {
|
|
36
|
-
content: '
|
|
33
|
+
content: '·'
|
|
37
34
|
}
|
|
38
35
|
|
|
39
36
|
.tiptap-invisible-character--break::before {
|
|
40
|
-
content: '
|
|
37
|
+
content: '¬'
|
|
41
38
|
}
|
|
42
39
|
|
|
43
40
|
.tiptap-invisible-character--paragraph::before {
|
|
44
|
-
content: '
|
|
41
|
+
content: '¶'
|
|
45
42
|
}
|
|
46
43
|
|
|
47
44
|
.tiptap-invisible-character + img.ProseMirror-separator {
|
|
@@ -55,273 +52,233 @@ var style = `.tiptap-invisible-character {
|
|
|
55
52
|
display: none;
|
|
56
53
|
}
|
|
57
54
|
`;
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
styleNode.setAttribute("data-tiptap-extension-invisible-characters-style", "");
|
|
70
|
-
styleNode.innerHTML = style2;
|
|
71
|
-
document.getElementsByTagName("head")[0].appendChild(styleNode);
|
|
72
|
-
return styleNode;
|
|
55
|
+
//#endregion
|
|
56
|
+
//#region src/plugin/utils/create-style-tag.ts
|
|
57
|
+
const createStyleTag = (style, nonce) => {
|
|
58
|
+
const tiptapStyleTag = document.querySelector("style[data-tiptap-extension-invisible-characters-style]");
|
|
59
|
+
if (tiptapStyleTag !== null) return tiptapStyleTag;
|
|
60
|
+
const styleNode = document.createElement("style");
|
|
61
|
+
if (nonce) styleNode.setAttribute("nonce", nonce);
|
|
62
|
+
styleNode.setAttribute("data-tiptap-extension-invisible-characters-style", "");
|
|
63
|
+
styleNode.innerHTML = style;
|
|
64
|
+
document.getElementsByTagName("head")[0].appendChild(styleNode);
|
|
65
|
+
return styleNode;
|
|
73
66
|
};
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
67
|
+
//#endregion
|
|
68
|
+
//#region src/plugin/utils/get-updated-ranges.ts
|
|
69
|
+
const getUpdatedRanges = ({ mapping }) => {
|
|
70
|
+
const ranges = [];
|
|
71
|
+
mapping.maps.forEach((stepMap, i) => {
|
|
72
|
+
stepMap.forEach((_oldStart, _oldEnd, newStart, newEnd) => {
|
|
73
|
+
ranges.push([mapping.slice(i + 1).map(newStart), mapping.slice(i + 1).map(newEnd)]);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
return ranges;
|
|
84
77
|
};
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
newPluginState.decorations.map(tr.mapping, tr.doc)
|
|
129
|
-
);
|
|
130
|
-
return {
|
|
131
|
-
...newPluginState,
|
|
132
|
-
decorations
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
},
|
|
136
|
-
props: {
|
|
137
|
-
decorations(editorState) {
|
|
138
|
-
const pluginState = this.getState(editorState);
|
|
139
|
-
const visible = pluginState == null ? void 0 : pluginState.visible;
|
|
140
|
-
const decorations = pluginState == null ? void 0 : pluginState.decorations;
|
|
141
|
-
return visible ? decorations : emptyDecorationSet;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
});
|
|
78
|
+
//#endregion
|
|
79
|
+
//#region src/plugin/InvisibleCharactersPlugin.ts
|
|
80
|
+
const InvisibleCharactersPluginKey = new PluginKey("invisibleCharacters");
|
|
81
|
+
const InvisibleCharactersPlugin = (state, options) => {
|
|
82
|
+
const key = InvisibleCharactersPluginKey;
|
|
83
|
+
const emptyDecorationSet = DecorationSet.create(state.doc, []);
|
|
84
|
+
const addDecorationsBetweenPositions = (from, to, editorState, decorations) => {
|
|
85
|
+
return options.builders.sort((builderA, builderB) => {
|
|
86
|
+
if (builderA.priority > builderB.priority) return 1;
|
|
87
|
+
return -1;
|
|
88
|
+
}).reduce((newDecos, builder) => {
|
|
89
|
+
return builder.createDecoration(from, to, editorState.doc, newDecos);
|
|
90
|
+
}, decorations);
|
|
91
|
+
};
|
|
92
|
+
return new Plugin({
|
|
93
|
+
key,
|
|
94
|
+
state: {
|
|
95
|
+
init: () => {
|
|
96
|
+
const { $from, $to } = new AllSelection(state.doc);
|
|
97
|
+
if (options.injectCSS && document) createStyleTag(style, options.injectNonce);
|
|
98
|
+
return {
|
|
99
|
+
visible: options.visible,
|
|
100
|
+
decorations: addDecorationsBetweenPositions($from.pos, $to.pos, state, DecorationSet.empty)
|
|
101
|
+
};
|
|
102
|
+
},
|
|
103
|
+
apply: (tr, pluginState, _, currentState) => {
|
|
104
|
+
const newPluginState = stateReducer(pluginState, tr.getMeta("setInvisibleCharactersVisible"));
|
|
105
|
+
const decorations = getUpdatedRanges(tr).reduce((nextDecorations, [from, to]) => {
|
|
106
|
+
return addDecorationsBetweenPositions(from, to, currentState, nextDecorations);
|
|
107
|
+
}, newPluginState.decorations.map(tr.mapping, tr.doc));
|
|
108
|
+
return {
|
|
109
|
+
...newPluginState,
|
|
110
|
+
decorations
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
props: { decorations(editorState) {
|
|
115
|
+
const pluginState = this.getState(editorState);
|
|
116
|
+
const visible = pluginState === null || pluginState === void 0 ? void 0 : pluginState.visible;
|
|
117
|
+
const decorations = pluginState === null || pluginState === void 0 ? void 0 : pluginState.decorations;
|
|
118
|
+
return visible ? decorations : emptyDecorationSet;
|
|
119
|
+
} }
|
|
120
|
+
});
|
|
145
121
|
};
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
marks: [],
|
|
162
|
-
side: 1e3
|
|
163
|
-
});
|
|
122
|
+
//#endregion
|
|
123
|
+
//#region src/plugin/utils/create-decoration-widget.ts
|
|
124
|
+
const createDecorationWidget = (pos, type, content) => {
|
|
125
|
+
const createElement = () => {
|
|
126
|
+
const newElement = document.createElement("span");
|
|
127
|
+
newElement.classList.add("tiptap-invisible-character");
|
|
128
|
+
newElement.classList.add(`tiptap-invisible-character--${type}`);
|
|
129
|
+
if (content) newElement.textContent = content;
|
|
130
|
+
return newElement;
|
|
131
|
+
};
|
|
132
|
+
return Decoration.widget(pos, createElement, {
|
|
133
|
+
key: type,
|
|
134
|
+
marks: [],
|
|
135
|
+
side: 1e3
|
|
136
|
+
});
|
|
164
137
|
};
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
138
|
+
//#endregion
|
|
139
|
+
//#region src/plugin/InvisibleNode.ts
|
|
140
|
+
/** Default position for nodes is always at the end of the node */
|
|
141
|
+
const defaultPosition = (node, pos) => pos + node.nodeSize - 1;
|
|
168
142
|
var InvisibleNode = class {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}
|
|
191
|
-
test(value) {
|
|
192
|
-
return this.predicate(value);
|
|
193
|
-
}
|
|
143
|
+
constructor(options) {
|
|
144
|
+
this.predicate = options.predicate;
|
|
145
|
+
this.type = options.type;
|
|
146
|
+
this.position = options.position || defaultPosition;
|
|
147
|
+
this.content = options.content;
|
|
148
|
+
this.priority = options.priority || 100;
|
|
149
|
+
}
|
|
150
|
+
createDecoration(from, to, doc, decorations) {
|
|
151
|
+
let newDecorations = decorations;
|
|
152
|
+
doc.nodesBetween(from, to, (node, pos) => {
|
|
153
|
+
if (this.test(node)) {
|
|
154
|
+
const decorationPosition = this.position(node, pos);
|
|
155
|
+
const oldDecorations = newDecorations.find(decorationPosition, decorationPosition, (spec) => spec.key === this.type);
|
|
156
|
+
newDecorations = newDecorations.remove(oldDecorations).add(doc, [createDecorationWidget(decorationPosition, this.type, this.content)]);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
return newDecorations;
|
|
160
|
+
}
|
|
161
|
+
test(value) {
|
|
162
|
+
return this.predicate(value);
|
|
163
|
+
}
|
|
194
164
|
};
|
|
195
|
-
|
|
196
|
-
|
|
165
|
+
//#endregion
|
|
166
|
+
//#region src/plugin/invisible-characters/hardBreak.ts
|
|
197
167
|
var HardBreakNode = class extends InvisibleNode {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
168
|
+
constructor() {
|
|
169
|
+
super({
|
|
170
|
+
type: "break",
|
|
171
|
+
predicate: (node) => node.type === node.type.schema.nodes.hardBreak
|
|
172
|
+
});
|
|
173
|
+
}
|
|
204
174
|
};
|
|
205
|
-
|
|
206
|
-
|
|
175
|
+
//#endregion
|
|
176
|
+
//#region src/plugin/invisible-characters/paragraph.ts
|
|
207
177
|
var ParagraphNode = class extends InvisibleNode {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
178
|
+
constructor() {
|
|
179
|
+
super({
|
|
180
|
+
type: "paragraph",
|
|
181
|
+
predicate: (node) => node.type === node.type.schema.nodes.paragraph
|
|
182
|
+
});
|
|
183
|
+
}
|
|
214
184
|
};
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
185
|
+
//#endregion
|
|
186
|
+
//#region src/plugin/utils/text-between.ts
|
|
187
|
+
const textBetween = (from, to, doc) => {
|
|
188
|
+
const positions = [];
|
|
189
|
+
doc.nodesBetween(from, to, (node, pos) => {
|
|
190
|
+
if (node.isText) {
|
|
191
|
+
var _node$text;
|
|
192
|
+
const offset = Math.max(from, pos) - pos;
|
|
193
|
+
positions.push({
|
|
194
|
+
pos: pos + offset,
|
|
195
|
+
text: ((_node$text = node.text) === null || _node$text === void 0 ? void 0 : _node$text.slice(offset, to - pos)) || ""
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
return positions;
|
|
230
200
|
};
|
|
231
|
-
|
|
232
|
-
|
|
201
|
+
//#endregion
|
|
202
|
+
//#region src/plugin/InvisibleCharacter.ts
|
|
233
203
|
var InvisibleCharacter = class {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
test(value) {
|
|
251
|
-
return this.predicate(value);
|
|
252
|
-
}
|
|
204
|
+
constructor(options) {
|
|
205
|
+
this.predicate = options.predicate;
|
|
206
|
+
this.type = options.type;
|
|
207
|
+
this.content = options.content;
|
|
208
|
+
this.priority = options.priority || 100;
|
|
209
|
+
}
|
|
210
|
+
createDecoration(from, to, doc, decorations) {
|
|
211
|
+
return textBetween(from, to, doc).reduce((oldDecorations, currentPosition) => {
|
|
212
|
+
return currentPosition.text.split("").reduce((innerDecorations, char, i) => {
|
|
213
|
+
return this.test(char) ? innerDecorations.add(doc, [createDecorationWidget(currentPosition.pos + i, this.type, this.content)]) : innerDecorations;
|
|
214
|
+
}, oldDecorations);
|
|
215
|
+
}, decorations);
|
|
216
|
+
}
|
|
217
|
+
test(value) {
|
|
218
|
+
return this.predicate(value);
|
|
219
|
+
}
|
|
253
220
|
};
|
|
254
|
-
|
|
255
|
-
|
|
221
|
+
//#endregion
|
|
222
|
+
//#region src/plugin/invisible-characters/space.ts
|
|
256
223
|
var SpaceCharacter = class extends InvisibleCharacter {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
224
|
+
constructor() {
|
|
225
|
+
super({
|
|
226
|
+
type: "space",
|
|
227
|
+
predicate: (char) => char === " "
|
|
228
|
+
});
|
|
229
|
+
}
|
|
263
230
|
};
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
return true;
|
|
311
|
-
}
|
|
312
|
-
};
|
|
313
|
-
}
|
|
231
|
+
//#endregion
|
|
232
|
+
//#region src/invisible-characters.ts
|
|
233
|
+
const InvisibleCharacters = Extension.create({
|
|
234
|
+
name: "invisibleCharacters",
|
|
235
|
+
addOptions() {
|
|
236
|
+
return {
|
|
237
|
+
visible: true,
|
|
238
|
+
builders: [
|
|
239
|
+
new SpaceCharacter(),
|
|
240
|
+
new ParagraphNode(),
|
|
241
|
+
new HardBreakNode()
|
|
242
|
+
],
|
|
243
|
+
injectCSS: true,
|
|
244
|
+
injectNonce: void 0
|
|
245
|
+
};
|
|
246
|
+
},
|
|
247
|
+
addProseMirrorPlugins() {
|
|
248
|
+
return [InvisibleCharactersPlugin(this.editor.state, this.options)];
|
|
249
|
+
},
|
|
250
|
+
addStorage() {
|
|
251
|
+
return { visibility: () => this.options.visible };
|
|
252
|
+
},
|
|
253
|
+
onBeforeCreate() {
|
|
254
|
+
this.storage.visibility = () => {
|
|
255
|
+
var _InvisibleCharactersP;
|
|
256
|
+
return (_InvisibleCharactersP = InvisibleCharactersPluginKey.getState(this.editor.state)) === null || _InvisibleCharactersP === void 0 ? void 0 : _InvisibleCharactersP.visible;
|
|
257
|
+
};
|
|
258
|
+
},
|
|
259
|
+
addCommands() {
|
|
260
|
+
return {
|
|
261
|
+
showInvisibleCharacters: (visibility = true) => ({ dispatch, tr }) => {
|
|
262
|
+
if (dispatch) tr.setMeta("setInvisibleCharactersVisible", visibility);
|
|
263
|
+
return true;
|
|
264
|
+
},
|
|
265
|
+
hideInvisibleCharacters: () => ({ dispatch, tr }) => {
|
|
266
|
+
if (dispatch) tr.setMeta("setInvisibleCharactersVisible", false);
|
|
267
|
+
return true;
|
|
268
|
+
},
|
|
269
|
+
toggleInvisibleCharacters: () => ({ dispatch, tr, state }) => {
|
|
270
|
+
var _InvisibleCharactersP2;
|
|
271
|
+
const visibility = !((_InvisibleCharactersP2 = InvisibleCharactersPluginKey.getState(state)) === null || _InvisibleCharactersP2 === void 0 ? void 0 : _InvisibleCharactersP2.visible);
|
|
272
|
+
if (dispatch) tr.setMeta("setInvisibleCharactersVisible", visibility);
|
|
273
|
+
return true;
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
}
|
|
314
277
|
});
|
|
278
|
+
//#endregion
|
|
279
|
+
//#region src/index.ts
|
|
280
|
+
var src_default = InvisibleCharacters;
|
|
281
|
+
//#endregion
|
|
282
|
+
export { HardBreakNode, InvisibleCharacter, InvisibleCharacters, InvisibleNode, ParagraphNode, SpaceCharacter, src_default as default };
|
|
315
283
|
|
|
316
|
-
// src/index.ts
|
|
317
|
-
var index_default = InvisibleCharacters;
|
|
318
|
-
export {
|
|
319
|
-
HardBreakNode,
|
|
320
|
-
InvisibleCharacter,
|
|
321
|
-
InvisibleCharacters,
|
|
322
|
-
InvisibleNode,
|
|
323
|
-
ParagraphNode,
|
|
324
|
-
SpaceCharacter,
|
|
325
|
-
index_default as default
|
|
326
|
-
};
|
|
327
284
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/invisible-characters.ts","../src/plugin/InvisibleCharactersPlugin.ts","../src/plugin/reducers.ts","../src/plugin/style.ts","../src/plugin/utils/create-style-tag.ts","../src/plugin/utils/get-updated-ranges.ts","../src/plugin/utils/create-decoration-widget.ts","../src/plugin/InvisibleNode.ts","../src/plugin/invisible-characters/hardBreak.ts","../src/plugin/invisible-characters/paragraph.ts","../src/plugin/utils/text-between.ts","../src/plugin/InvisibleCharacter.ts","../src/plugin/invisible-characters/space.ts","../src/index.ts"],"sourcesContent":["import { Extension } from '@tiptap/core'\n\nimport { InvisibleCharactersPlugin, InvisibleCharactersPluginKey } from './plugin/index.js'\nimport { HardBreakNode } from './plugin/invisible-characters/hardBreak.js'\nimport { ParagraphNode } from './plugin/invisible-characters/paragraph.js'\nimport { SpaceCharacter } from './plugin/invisible-characters/space.js'\nimport type { InvisibleCharactersOptions } from './types.js'\n\nexport { HardBreakNode, ParagraphNode, SpaceCharacter }\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n invisibleCharacters: {\n /**\n * Show invisible characters\n */\n showInvisibleCharacters: (show?: boolean) => ReturnType\n\n /**\n * Hide invisible characters\n */\n hideInvisibleCharacters: () => ReturnType\n\n /**\n * Toggle invisible characters\n */\n toggleInvisibleCharacters: () => ReturnType\n }\n }\n}\n\nexport const InvisibleCharacters = Extension.create<InvisibleCharactersOptions>({\n name: 'invisibleCharacters',\n\n addOptions() {\n return {\n visible: true,\n builders: [new SpaceCharacter(), new ParagraphNode(), new HardBreakNode()],\n injectCSS: true,\n injectNonce: undefined,\n }\n },\n\n addProseMirrorPlugins() {\n return [InvisibleCharactersPlugin(this.editor.state, this.options)]\n },\n\n addStorage() {\n return {\n visibility: () => this.options.visible,\n }\n },\n\n onBeforeCreate() {\n this.storage.visibility = () => {\n return InvisibleCharactersPluginKey.getState(this.editor.state)?.visible\n }\n },\n\n addCommands() {\n return {\n showInvisibleCharacters:\n (visibility = true) =>\n ({ dispatch, tr }) => {\n if (dispatch) {\n tr.setMeta('setInvisibleCharactersVisible', visibility)\n }\n\n return true\n },\n hideInvisibleCharacters:\n () =>\n ({ dispatch, tr }) => {\n if (dispatch) {\n tr.setMeta('setInvisibleCharactersVisible', false)\n }\n\n return true\n },\n toggleInvisibleCharacters:\n () =>\n ({ dispatch, tr, state }) => {\n const visibility = !InvisibleCharactersPluginKey.getState(state)?.visible\n\n if (dispatch) {\n tr.setMeta('setInvisibleCharactersVisible', visibility)\n }\n\n return true\n },\n }\n },\n})\n\nexport default InvisibleCharacters\n","import type { EditorState } from '@tiptap/pm/state'\nimport { AllSelection, Plugin, PluginKey } from '@tiptap/pm/state'\nimport { DecorationSet } from '@tiptap/pm/view'\n\nimport type { InvisibleCharactersOptions, PluginState } from '../types.js'\nimport { stateReducer } from './reducers.js'\nimport { style } from './style.js'\nimport { createStyleTag } from './utils/create-style-tag.js'\nimport { getUpdatedRanges } from './utils/get-updated-ranges.js'\n\nexport const InvisibleCharactersPluginKey = new PluginKey<PluginState>('invisibleCharacters')\n\nexport const InvisibleCharactersPlugin = (\n state: EditorState,\n options: InvisibleCharactersOptions,\n) => {\n const key = InvisibleCharactersPluginKey\n const emptyDecorationSet = DecorationSet.create(state.doc, [])\n\n const addDecorationsBetweenPositions = (\n from: number,\n to: number,\n editorState: EditorState,\n decorations: DecorationSet,\n ) => {\n return options.builders\n .sort((builderA, builderB) => {\n if (builderA.priority > builderB.priority) {\n return 1\n }\n\n return -1\n })\n .reduce((newDecos, builder) => {\n return builder.createDecoration(from, to, editorState.doc, newDecos)\n }, decorations)\n }\n\n return new Plugin({\n key,\n\n state: {\n init: () => {\n const { $from, $to } = new AllSelection(state.doc)\n\n if (options.injectCSS && document) {\n createStyleTag(style, options.injectNonce)\n }\n\n return {\n visible: options.visible,\n decorations: addDecorationsBetweenPositions(\n $from.pos,\n $to.pos,\n state,\n DecorationSet.empty,\n ),\n }\n },\n apply: (tr, pluginState, _, currentState) => {\n const newPluginState = stateReducer(\n pluginState,\n tr.getMeta('setInvisibleCharactersVisible'),\n )\n\n const decorations = getUpdatedRanges(tr).reduce(\n (nextDecorations, [from, to]) => {\n return addDecorationsBetweenPositions(from, to, currentState, nextDecorations)\n },\n newPluginState.decorations.map(tr.mapping, tr.doc),\n )\n\n return {\n ...newPluginState,\n decorations,\n }\n },\n },\n\n props: {\n decorations(editorState) {\n const pluginState = this.getState(editorState)\n\n const visible = pluginState?.visible\n const decorations = pluginState?.decorations\n\n return visible ? decorations : emptyDecorationSet\n },\n },\n })\n}\n\nexport default InvisibleCharactersPlugin\n","import type { PluginState } from '../types.js'\n\nexport const stateReducer = (state: PluginState, visible: boolean): PluginState => {\n if (visible === undefined) {\n return state\n }\n\n return { ...state, visible }\n}\n\nexport default stateReducer\n","export const style = `.tiptap-invisible-character {\n height: 0;\n padding: 0;\n pointer-events: none;\n user-select: none;\n width: 0;\n}\n\n.tiptap-invisible-character::before {\n caret-color: inherit;\n color: #aaa;\n display: inline-block;\n font-style: normal;\n font-weight: 400;\n line-height: 1em;\n width: 0;\n}\n\n.tiptap-invisible-character--space::before {\n content: '·'\n}\n\n.tiptap-invisible-character--break::before {\n content: '¬'\n}\n\n.tiptap-invisible-character--paragraph::before {\n content: '¶'\n}\n\n.tiptap-invisible-character + img.ProseMirror-separator {\n height: 0 !important;\n pointer-events: none;\n user-select: none;\n width: 0 !important;\n}\n\n.is-empty[data-placeholder].has-focus > .tiptap-invisible-character {\n display: none;\n}\n`\n\nexport default style\n","export const createStyleTag = (style: string, nonce?: string): HTMLStyleElement => {\n const tiptapStyleTag = <HTMLStyleElement>(\n document.querySelector('style[data-tiptap-extension-invisible-characters-style]')\n )\n\n if (tiptapStyleTag !== null) {\n return tiptapStyleTag\n }\n\n const styleNode = document.createElement('style')\n\n if (nonce) {\n styleNode.setAttribute('nonce', nonce)\n }\n\n styleNode.setAttribute('data-tiptap-extension-invisible-characters-style', '')\n styleNode.innerHTML = style\n document.getElementsByTagName('head')[0].appendChild(styleNode)\n\n return styleNode\n}\n\nexport default createStyleTag\n","import type { Transaction } from '@tiptap/pm/state'\n\ntype StepRange = [from: number, to: number]\n\nexport const getUpdatedRanges = ({ mapping }: Transaction): StepRange[] => {\n const ranges: StepRange[] = []\n\n mapping.maps.forEach((stepMap, i) => {\n stepMap.forEach((_oldStart, _oldEnd, newStart, newEnd) => {\n ranges.push([mapping.slice(i + 1).map(newStart), mapping.slice(i + 1).map(newEnd)])\n })\n })\n\n return ranges\n}\n\nexport default getUpdatedRanges\n","import { Decoration } from '@tiptap/pm/view'\n\nexport const createDecorationWidget = (pos: number, type: string, content?: string) => {\n const createElement = () => {\n const newElement = document.createElement('span')\n\n newElement.classList.add('tiptap-invisible-character')\n newElement.classList.add(`tiptap-invisible-character--${type}`)\n\n if (content) {\n newElement.textContent = content\n }\n\n return newElement\n }\n\n return Decoration.widget(pos, createElement, {\n key: type,\n marks: [],\n side: 1000,\n })\n}\n\nexport default createDecorationWidget\n","import type { Node } from '@tiptap/pm/model'\nimport type { DecorationSet } from '@tiptap/pm/view'\n\nimport { createDecorationWidget } from './utils/create-decoration-widget.js'\n\nexport interface InvisibleNodeOptions {\n content?: string\n position?: (node: Node, pos: number) => number\n predicate: (value: Node) => boolean\n priority?: number\n type: string\n}\n\n/** Default position for nodes is always at the end of the node */\nconst defaultPosition = (node: Node, pos: number) => pos + node.nodeSize - 1\n\nexport class InvisibleNode {\n predicate: (value: Node) => boolean\n\n position: (node: Node, pos: number) => number\n\n content?: string\n\n type: string\n\n priority: number\n\n constructor(options: InvisibleNodeOptions) {\n this.predicate = options.predicate\n this.type = options.type\n this.position = options.position || defaultPosition\n this.content = options.content\n this.priority = options.priority || 100\n }\n\n createDecoration(from: number, to: number, doc: Node, decorations: DecorationSet) {\n let newDecorations = decorations\n\n doc.nodesBetween(from, to, (node, pos) => {\n if (this.test(node)) {\n const decorationPosition = this.position(node, pos)\n const oldDecorations = newDecorations.find(\n decorationPosition,\n decorationPosition,\n spec => spec.key === this.type,\n )\n\n newDecorations = newDecorations\n .remove(oldDecorations)\n .add(doc, [createDecorationWidget(decorationPosition, this.type, this.content)])\n }\n })\n return newDecorations\n }\n\n test(value: Node) {\n return this.predicate(value)\n }\n}\n\nexport default InvisibleNode\n","import { InvisibleNode } from '../InvisibleNode.js'\n\nexport class HardBreakNode extends InvisibleNode {\n constructor() {\n super({\n type: 'break',\n predicate: node => node.type === node.type.schema.nodes.hardBreak,\n })\n }\n}\n\nexport default HardBreakNode\n","import { InvisibleNode } from '../InvisibleNode.js'\n\nexport class ParagraphNode extends InvisibleNode {\n constructor() {\n super({\n type: 'paragraph',\n predicate: node => node.type === node.type.schema.nodes.paragraph,\n })\n }\n}\n\nexport default ParagraphNode\n","import type { Node } from '@tiptap/pm/model'\n\nimport type { Position } from '../../types.js'\n\nexport const textBetween = (from: number, to: number, doc: Node) => {\n const positions: Position[] = []\n\n doc.nodesBetween(from, to, (node, pos) => {\n if (node.isText) {\n const offset = Math.max(from, pos) - pos\n\n positions.push({\n pos: pos + offset,\n text: node.text?.slice(offset, to - pos) || '',\n })\n }\n })\n\n return positions\n}\n\nexport default textBetween\n","import type { Node } from '@tiptap/pm/model'\nimport type { DecorationSet } from '@tiptap/pm/view'\n\nimport { createDecorationWidget } from './utils/create-decoration-widget.js'\nimport { textBetween } from './utils/text-between.js'\n\nexport interface InvisibleCharacterOptions {\n type: string\n predicate: (value: string) => boolean\n priority?: number\n content?: string\n}\n\nexport class InvisibleCharacter {\n predicate: (value: string) => boolean\n\n content?: string\n\n type: string\n\n priority: number\n\n constructor(options: InvisibleCharacterOptions) {\n this.predicate = options.predicate\n this.type = options.type\n this.content = options.content\n this.priority = options.priority || 100\n }\n\n createDecoration(from: number, to: number, doc: Node, decorations: DecorationSet) {\n const textContent = textBetween(from, to, doc)\n\n return textContent.reduce((oldDecorations, currentPosition) => {\n return currentPosition.text.split('').reduce((innerDecorations, char, i) => {\n return this.test(char)\n ? innerDecorations.add(doc, [\n createDecorationWidget(currentPosition.pos + i, this.type, this.content),\n ])\n : innerDecorations\n }, oldDecorations)\n }, decorations)\n }\n\n test(value: string) {\n return this.predicate(value)\n }\n}\n\nexport default InvisibleCharacter\n","import { InvisibleCharacter } from '../InvisibleCharacter.js'\n\nexport class SpaceCharacter extends InvisibleCharacter {\n constructor() {\n super({\n type: 'space',\n predicate: (char: string) => char === ' ',\n })\n }\n}\n\nexport default SpaceCharacter\n","import { InvisibleCharacters } from './invisible-characters.js'\n\nexport * from './invisible-characters.js'\nexport * from './plugin/invisible-characters/hardBreak.js'\nexport * from './plugin/invisible-characters/paragraph.js'\nexport * from './plugin/invisible-characters/space.js'\nexport * from './plugin/InvisibleCharacter.js'\nexport * from './plugin/InvisibleNode.js'\nexport * from './types.js'\n\nexport default InvisibleCharacters\n"],"mappings":"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","names":["style"]}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/plugin/reducers.ts","../src/plugin/style.ts","../src/plugin/utils/create-style-tag.ts","../src/plugin/utils/get-updated-ranges.ts","../src/plugin/InvisibleCharactersPlugin.ts","../src/plugin/utils/create-decoration-widget.ts","../src/plugin/InvisibleNode.ts","../src/plugin/invisible-characters/hardBreak.ts","../src/plugin/invisible-characters/paragraph.ts","../src/plugin/utils/text-between.ts","../src/plugin/InvisibleCharacter.ts","../src/plugin/invisible-characters/space.ts","../src/invisible-characters.ts","../src/index.ts"],"sourcesContent":["import type { PluginState } from '../types.js'\n\nexport const stateReducer = (state: PluginState, visible: boolean): PluginState => {\n if (visible === undefined) {\n return state\n }\n\n return { ...state, visible }\n}\n\nexport default stateReducer\n","export const style = `.tiptap-invisible-character {\n height: 0;\n padding: 0;\n pointer-events: none;\n user-select: none;\n width: 0;\n}\n\n.tiptap-invisible-character::before {\n caret-color: inherit;\n color: #aaa;\n display: inline-block;\n font-style: normal;\n font-weight: 400;\n line-height: 1em;\n width: 0;\n}\n\n.tiptap-invisible-character--space::before {\n content: '·'\n}\n\n.tiptap-invisible-character--break::before {\n content: '¬'\n}\n\n.tiptap-invisible-character--paragraph::before {\n content: '¶'\n}\n\n.tiptap-invisible-character + img.ProseMirror-separator {\n height: 0 !important;\n pointer-events: none;\n user-select: none;\n width: 0 !important;\n}\n\n.is-empty[data-placeholder].has-focus > .tiptap-invisible-character {\n display: none;\n}\n`\n\nexport default style\n","export const createStyleTag = (style: string, nonce?: string): HTMLStyleElement => {\n const tiptapStyleTag = <HTMLStyleElement>(\n document.querySelector('style[data-tiptap-extension-invisible-characters-style]')\n )\n\n if (tiptapStyleTag !== null) {\n return tiptapStyleTag\n }\n\n const styleNode = document.createElement('style')\n\n if (nonce) {\n styleNode.setAttribute('nonce', nonce)\n }\n\n styleNode.setAttribute('data-tiptap-extension-invisible-characters-style', '')\n styleNode.innerHTML = style\n document.getElementsByTagName('head')[0].appendChild(styleNode)\n\n return styleNode\n}\n\nexport default createStyleTag\n","import type { Transaction } from '@tiptap/pm/state'\n\ntype StepRange = [from: number, to: number]\n\nexport const getUpdatedRanges = ({ mapping }: Transaction): StepRange[] => {\n const ranges: StepRange[] = []\n\n mapping.maps.forEach((stepMap, i) => {\n stepMap.forEach((_oldStart, _oldEnd, newStart, newEnd) => {\n ranges.push([mapping.slice(i + 1).map(newStart), mapping.slice(i + 1).map(newEnd)])\n })\n })\n\n return ranges\n}\n\nexport default getUpdatedRanges\n","import type { EditorState } from '@tiptap/pm/state'\nimport { AllSelection, Plugin, PluginKey } from '@tiptap/pm/state'\nimport { DecorationSet } from '@tiptap/pm/view'\n\nimport type { InvisibleCharactersOptions, PluginState } from '../types.js'\nimport { stateReducer } from './reducers.js'\nimport { style } from './style.js'\nimport { createStyleTag } from './utils/create-style-tag.js'\nimport { getUpdatedRanges } from './utils/get-updated-ranges.js'\n\nexport const InvisibleCharactersPluginKey = new PluginKey<PluginState>('invisibleCharacters')\n\nexport const InvisibleCharactersPlugin = (\n state: EditorState,\n options: InvisibleCharactersOptions,\n) => {\n const key = InvisibleCharactersPluginKey\n const emptyDecorationSet = DecorationSet.create(state.doc, [])\n\n const addDecorationsBetweenPositions = (\n from: number,\n to: number,\n editorState: EditorState,\n decorations: DecorationSet,\n ) => {\n return options.builders\n .sort((builderA, builderB) => {\n if (builderA.priority > builderB.priority) {\n return 1\n }\n\n return -1\n })\n .reduce((newDecos, builder) => {\n return builder.createDecoration(from, to, editorState.doc, newDecos)\n }, decorations)\n }\n\n return new Plugin({\n key,\n\n state: {\n init: () => {\n const { $from, $to } = new AllSelection(state.doc)\n\n if (options.injectCSS && document) {\n createStyleTag(style, options.injectNonce)\n }\n\n return {\n visible: options.visible,\n decorations: addDecorationsBetweenPositions(\n $from.pos,\n $to.pos,\n state,\n DecorationSet.empty,\n ),\n }\n },\n apply: (tr, pluginState, _, currentState) => {\n const newPluginState = stateReducer(\n pluginState,\n tr.getMeta('setInvisibleCharactersVisible'),\n )\n\n const decorations = getUpdatedRanges(tr).reduce(\n (nextDecorations, [from, to]) => {\n return addDecorationsBetweenPositions(from, to, currentState, nextDecorations)\n },\n newPluginState.decorations.map(tr.mapping, tr.doc),\n )\n\n return {\n ...newPluginState,\n decorations,\n }\n },\n },\n\n props: {\n decorations(editorState) {\n const pluginState = this.getState(editorState)\n\n const visible = pluginState?.visible\n const decorations = pluginState?.decorations\n\n return visible ? decorations : emptyDecorationSet\n },\n },\n })\n}\n\nexport default InvisibleCharactersPlugin\n","import { Decoration } from '@tiptap/pm/view'\n\nexport const createDecorationWidget = (pos: number, type: string, content?: string) => {\n const createElement = () => {\n const newElement = document.createElement('span')\n\n newElement.classList.add('tiptap-invisible-character')\n newElement.classList.add(`tiptap-invisible-character--${type}`)\n\n if (content) {\n newElement.textContent = content\n }\n\n return newElement\n }\n\n return Decoration.widget(pos, createElement, {\n key: type,\n marks: [],\n side: 1000,\n })\n}\n\nexport default createDecorationWidget\n","import type { Node } from '@tiptap/pm/model'\nimport type { DecorationSet } from '@tiptap/pm/view'\n\nimport { createDecorationWidget } from './utils/create-decoration-widget.js'\n\nexport interface InvisibleNodeOptions {\n content?: string\n position?: (node: Node, pos: number) => number\n predicate: (value: Node) => boolean\n priority?: number\n type: string\n}\n\n/** Default position for nodes is always at the end of the node */\nconst defaultPosition = (node: Node, pos: number) => pos + node.nodeSize - 1\n\nexport class InvisibleNode {\n predicate: (value: Node) => boolean\n\n position: (node: Node, pos: number) => number\n\n content?: string\n\n type: string\n\n priority: number\n\n constructor(options: InvisibleNodeOptions) {\n this.predicate = options.predicate\n this.type = options.type\n this.position = options.position || defaultPosition\n this.content = options.content\n this.priority = options.priority || 100\n }\n\n createDecoration(from: number, to: number, doc: Node, decorations: DecorationSet) {\n let newDecorations = decorations\n\n doc.nodesBetween(from, to, (node, pos) => {\n if (this.test(node)) {\n const decorationPosition = this.position(node, pos)\n const oldDecorations = newDecorations.find(\n decorationPosition,\n decorationPosition,\n spec => spec.key === this.type,\n )\n\n newDecorations = newDecorations\n .remove(oldDecorations)\n .add(doc, [createDecorationWidget(decorationPosition, this.type, this.content)])\n }\n })\n return newDecorations\n }\n\n test(value: Node) {\n return this.predicate(value)\n }\n}\n\nexport default InvisibleNode\n","import { InvisibleNode } from '../InvisibleNode.js'\n\nexport class HardBreakNode extends InvisibleNode {\n constructor() {\n super({\n type: 'break',\n predicate: node => node.type === node.type.schema.nodes.hardBreak,\n })\n }\n}\n\nexport default HardBreakNode\n","import { InvisibleNode } from '../InvisibleNode.js'\n\nexport class ParagraphNode extends InvisibleNode {\n constructor() {\n super({\n type: 'paragraph',\n predicate: node => node.type === node.type.schema.nodes.paragraph,\n })\n }\n}\n\nexport default ParagraphNode\n","import type { Node } from '@tiptap/pm/model'\n\nimport type { Position } from '../../types.js'\n\nexport const textBetween = (from: number, to: number, doc: Node) => {\n const positions: Position[] = []\n\n doc.nodesBetween(from, to, (node, pos) => {\n if (node.isText) {\n const offset = Math.max(from, pos) - pos\n\n positions.push({\n pos: pos + offset,\n text: node.text?.slice(offset, to - pos) || '',\n })\n }\n })\n\n return positions\n}\n\nexport default textBetween\n","import type { Node } from '@tiptap/pm/model'\nimport type { DecorationSet } from '@tiptap/pm/view'\n\nimport { createDecorationWidget } from './utils/create-decoration-widget.js'\nimport { textBetween } from './utils/text-between.js'\n\nexport interface InvisibleCharacterOptions {\n type: string\n predicate: (value: string) => boolean\n priority?: number\n content?: string\n}\n\nexport class InvisibleCharacter {\n predicate: (value: string) => boolean\n\n content?: string\n\n type: string\n\n priority: number\n\n constructor(options: InvisibleCharacterOptions) {\n this.predicate = options.predicate\n this.type = options.type\n this.content = options.content\n this.priority = options.priority || 100\n }\n\n createDecoration(from: number, to: number, doc: Node, decorations: DecorationSet) {\n const textContent = textBetween(from, to, doc)\n\n return textContent.reduce((oldDecorations, currentPosition) => {\n return currentPosition.text.split('').reduce((innerDecorations, char, i) => {\n return this.test(char)\n ? innerDecorations.add(doc, [\n createDecorationWidget(currentPosition.pos + i, this.type, this.content),\n ])\n : innerDecorations\n }, oldDecorations)\n }, decorations)\n }\n\n test(value: string) {\n return this.predicate(value)\n }\n}\n\nexport default InvisibleCharacter\n","import { InvisibleCharacter } from '../InvisibleCharacter.js'\n\nexport class SpaceCharacter extends InvisibleCharacter {\n constructor() {\n super({\n type: 'space',\n predicate: (char: string) => char === ' ',\n })\n }\n}\n\nexport default SpaceCharacter\n","import { Extension } from '@tiptap/core'\n\nimport { InvisibleCharactersPlugin, InvisibleCharactersPluginKey } from './plugin/index.js'\nimport { HardBreakNode } from './plugin/invisible-characters/hardBreak.js'\nimport { ParagraphNode } from './plugin/invisible-characters/paragraph.js'\nimport { SpaceCharacter } from './plugin/invisible-characters/space.js'\nimport type { InvisibleCharactersOptions } from './types.js'\n\nexport { HardBreakNode, ParagraphNode, SpaceCharacter }\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n invisibleCharacters: {\n /**\n * Show invisible characters\n */\n showInvisibleCharacters: (show?: boolean) => ReturnType\n\n /**\n * Hide invisible characters\n */\n hideInvisibleCharacters: () => ReturnType\n\n /**\n * Toggle invisible characters\n */\n toggleInvisibleCharacters: () => ReturnType\n }\n }\n}\n\nexport const InvisibleCharacters = Extension.create<InvisibleCharactersOptions>({\n name: 'invisibleCharacters',\n\n addOptions() {\n return {\n visible: true,\n builders: [new SpaceCharacter(), new ParagraphNode(), new HardBreakNode()],\n injectCSS: true,\n injectNonce: undefined,\n }\n },\n\n addProseMirrorPlugins() {\n return [InvisibleCharactersPlugin(this.editor.state, this.options)]\n },\n\n addStorage() {\n return {\n visibility: () => this.options.visible,\n }\n },\n\n onBeforeCreate() {\n this.storage.visibility = () => {\n return InvisibleCharactersPluginKey.getState(this.editor.state)?.visible\n }\n },\n\n addCommands() {\n return {\n showInvisibleCharacters:\n (visibility = true) =>\n ({ dispatch, tr }) => {\n if (dispatch) {\n tr.setMeta('setInvisibleCharactersVisible', visibility)\n }\n\n return true\n },\n hideInvisibleCharacters:\n () =>\n ({ dispatch, tr }) => {\n if (dispatch) {\n tr.setMeta('setInvisibleCharactersVisible', false)\n }\n\n return true\n },\n toggleInvisibleCharacters:\n () =>\n ({ dispatch, tr, state }) => {\n const visibility = !InvisibleCharactersPluginKey.getState(state)?.visible\n\n if (dispatch) {\n tr.setMeta('setInvisibleCharactersVisible', visibility)\n }\n\n return true\n },\n }\n },\n})\n\nexport default InvisibleCharacters\n","import { InvisibleCharacters } from './invisible-characters.js'\n\nexport * from './invisible-characters.js'\nexport * from './plugin/invisible-characters/hardBreak.js'\nexport * from './plugin/invisible-characters/paragraph.js'\nexport * from './plugin/invisible-characters/space.js'\nexport * from './plugin/InvisibleCharacter.js'\nexport * from './plugin/InvisibleNode.js'\nexport * from './types.js'\n\nexport default InvisibleCharacters\n"],"mappings":"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"}
|