draftly 0.1.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1144 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +257 -0
- package/dist/index.d.ts +257 -0
- package/dist/index.js +1128 -0
- package/dist/index.js.map +1 -0
- package/package.json +65 -0
- package/src/draftly.ts +198 -0
- package/src/index.ts +7 -0
- package/src/plugin.ts +245 -0
- package/src/plugins/heading-plugin.ts +163 -0
- package/src/plugins/html-plugin.ts +347 -0
- package/src/plugins/inline-plugin.ts +152 -0
- package/src/plugins/list-plugin.ts +211 -0
- package/src/plugins/plugins.ts +9 -0
- package/src/theme.ts +86 -0
- package/src/utils.ts +21 -0
- package/src/view-plugin.ts +304 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1144 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var state = require('@codemirror/state');
|
|
6
|
+
var view = require('@codemirror/view');
|
|
7
|
+
var langMarkdown = require('@codemirror/lang-markdown');
|
|
8
|
+
var language = require('@codemirror/language');
|
|
9
|
+
var commands = require('@codemirror/commands');
|
|
10
|
+
var languageData = require('@codemirror/language-data');
|
|
11
|
+
var DOMPurify = require('dompurify');
|
|
12
|
+
|
|
13
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
14
|
+
|
|
15
|
+
var DOMPurify__default = /*#__PURE__*/_interopDefault(DOMPurify);
|
|
16
|
+
|
|
17
|
+
// src/draftly.ts
|
|
18
|
+
|
|
19
|
+
// src/utils.ts
|
|
20
|
+
function cursorInRange(view, from, to) {
|
|
21
|
+
const selection = view.state.selection.main;
|
|
22
|
+
return selection.from <= to && selection.to >= from;
|
|
23
|
+
}
|
|
24
|
+
function selectionOverlapsRange(view, from, to) {
|
|
25
|
+
for (const range of view.state.selection.ranges) {
|
|
26
|
+
if (range.from <= to && range.to >= from) {
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
var highlightStyle = language.HighlightStyle.define([]);
|
|
33
|
+
var draftlyBaseTheme = view.EditorView.baseTheme({
|
|
34
|
+
// Container styles - only apply when view plugin is enabled
|
|
35
|
+
"&.cm-draftly-enabled": {
|
|
36
|
+
fontSize: "16px",
|
|
37
|
+
lineHeight: "1.6"
|
|
38
|
+
},
|
|
39
|
+
"&.cm-draftly-enabled .cm-content": {
|
|
40
|
+
maxWidth: "48rem",
|
|
41
|
+
margin: "0 auto",
|
|
42
|
+
fontFamily: "var(--font-sans, sans-serif)",
|
|
43
|
+
fontSize: "16px",
|
|
44
|
+
lineHeight: "1.6"
|
|
45
|
+
},
|
|
46
|
+
// Inline code
|
|
47
|
+
".cm-draftly-inline-code": {
|
|
48
|
+
fontFamily: "var(--font-mono, monospace)",
|
|
49
|
+
fontSize: "0.9em",
|
|
50
|
+
backgroundColor: "rgba(175, 184, 193, 0.2)",
|
|
51
|
+
padding: "0.1em 0.3em",
|
|
52
|
+
borderRadius: "4px"
|
|
53
|
+
},
|
|
54
|
+
".cm-draftly-code-mark": {
|
|
55
|
+
opacity: "0.4"
|
|
56
|
+
},
|
|
57
|
+
// Links
|
|
58
|
+
".cm-draftly-link": {
|
|
59
|
+
color: "#0969da",
|
|
60
|
+
textDecoration: "none"
|
|
61
|
+
},
|
|
62
|
+
".cm-draftly-link:hover": {
|
|
63
|
+
textDecoration: "underline"
|
|
64
|
+
},
|
|
65
|
+
".cm-draftly-url": {
|
|
66
|
+
opacity: "0.6",
|
|
67
|
+
fontSize: "0.9em"
|
|
68
|
+
},
|
|
69
|
+
// Images (placeholder styling)
|
|
70
|
+
".cm-draftly-image": {
|
|
71
|
+
color: "#8250df"
|
|
72
|
+
},
|
|
73
|
+
// Code blocks
|
|
74
|
+
".cm-draftly-fenced-code": {
|
|
75
|
+
fontFamily: "var(--font-mono, monospace)",
|
|
76
|
+
fontSize: "0.9em"
|
|
77
|
+
},
|
|
78
|
+
".cm-draftly-line-code": {
|
|
79
|
+
backgroundColor: "rgba(175, 184, 193, 0.15)",
|
|
80
|
+
borderRadius: "0"
|
|
81
|
+
},
|
|
82
|
+
".cm-draftly-code-info": {
|
|
83
|
+
color: "#6e7781",
|
|
84
|
+
fontStyle: "italic"
|
|
85
|
+
},
|
|
86
|
+
// Blockquote
|
|
87
|
+
".cm-draftly-line-blockquote": {
|
|
88
|
+
borderLeft: "3px solid #d0d7de",
|
|
89
|
+
paddingLeft: "1em",
|
|
90
|
+
color: "#656d76"
|
|
91
|
+
},
|
|
92
|
+
".cm-draftly-quote-mark": {
|
|
93
|
+
opacity: "0.4"
|
|
94
|
+
},
|
|
95
|
+
// Horizontal rule
|
|
96
|
+
".cm-draftly-line-hr": {
|
|
97
|
+
textAlign: "center"
|
|
98
|
+
},
|
|
99
|
+
".cm-draftly-hr": {
|
|
100
|
+
opacity: "0.4"
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// src/view-plugin.ts
|
|
105
|
+
var markDecorations = {
|
|
106
|
+
// Inline styles
|
|
107
|
+
"inline-code": view.Decoration.mark({ class: "cm-draftly-inline-code" }),
|
|
108
|
+
// Links and images
|
|
109
|
+
link: view.Decoration.mark({ class: "cm-draftly-link" }),
|
|
110
|
+
"link-text": view.Decoration.mark({ class: "cm-draftly-link-text" }),
|
|
111
|
+
url: view.Decoration.mark({ class: "cm-draftly-url" }),
|
|
112
|
+
image: view.Decoration.mark({ class: "cm-draftly-image" }),
|
|
113
|
+
// Emphasis markers (* _ ~~ `)
|
|
114
|
+
"emphasis-mark": view.Decoration.mark({ class: "cm-draftly-emphasis-mark" }),
|
|
115
|
+
// Code blocks
|
|
116
|
+
"fenced-code": view.Decoration.mark({ class: "cm-draftly-fenced-code" }),
|
|
117
|
+
"code-mark": view.Decoration.mark({ class: "cm-draftly-code-mark" }),
|
|
118
|
+
"code-info": view.Decoration.mark({ class: "cm-draftly-code-info" }),
|
|
119
|
+
// Blockquote
|
|
120
|
+
blockquote: view.Decoration.mark({ class: "cm-draftly-blockquote" }),
|
|
121
|
+
"quote-mark": view.Decoration.mark({ class: "cm-draftly-quote-mark" }),
|
|
122
|
+
// Horizontal rule
|
|
123
|
+
hr: view.Decoration.mark({ class: "cm-draftly-hr" })
|
|
124
|
+
};
|
|
125
|
+
var lineDecorations = {
|
|
126
|
+
blockquote: view.Decoration.line({ class: "cm-draftly-line-blockquote" }),
|
|
127
|
+
"code-block": view.Decoration.line({ class: "cm-draftly-line-code" }),
|
|
128
|
+
hr: view.Decoration.line({ class: "cm-draftly-line-hr" })
|
|
129
|
+
};
|
|
130
|
+
var DraftlyPluginsFacet = state.Facet.define({
|
|
131
|
+
combine: (values) => values.flat()
|
|
132
|
+
});
|
|
133
|
+
var draftlyOnNodesChangeFacet = state.Facet.define({
|
|
134
|
+
combine: (values) => values.find((v) => v !== void 0)
|
|
135
|
+
});
|
|
136
|
+
function buildDecorations(view, plugins = []) {
|
|
137
|
+
const builder = new state.RangeSetBuilder();
|
|
138
|
+
const decorations = [];
|
|
139
|
+
const tree = language.syntaxTree(view.state);
|
|
140
|
+
tree.iterate({
|
|
141
|
+
enter: (node) => {
|
|
142
|
+
const { from, to, name } = node;
|
|
143
|
+
const cursorInNode = selectionOverlapsRange(view, from, to);
|
|
144
|
+
if (name === "InlineCode") {
|
|
145
|
+
decorations.push(markDecorations["inline-code"].range(from, to));
|
|
146
|
+
if (!cursorInNode) {
|
|
147
|
+
const marks = node.node.getChildren("CodeMark");
|
|
148
|
+
for (const mark of marks) {
|
|
149
|
+
decorations.push(markDecorations["code-mark"].range(mark.from, mark.to));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (name === "Link") {
|
|
154
|
+
decorations.push(markDecorations.link.range(from, to));
|
|
155
|
+
const url = node.node.getChild("URL");
|
|
156
|
+
if (url) {
|
|
157
|
+
decorations.push(markDecorations.url.range(url.from, url.to));
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (name === "Image") {
|
|
161
|
+
decorations.push(markDecorations.image.range(from, to));
|
|
162
|
+
}
|
|
163
|
+
if (name === "FencedCode") {
|
|
164
|
+
decorations.push(markDecorations["fenced-code"].range(from, to));
|
|
165
|
+
const startLine = view.state.doc.lineAt(from);
|
|
166
|
+
const endLine = view.state.doc.lineAt(to);
|
|
167
|
+
for (let i = startLine.number; i <= endLine.number; i++) {
|
|
168
|
+
const line = view.state.doc.line(i);
|
|
169
|
+
decorations.push(lineDecorations["code-block"].range(line.from));
|
|
170
|
+
}
|
|
171
|
+
const codeInfo = node.node.getChild("CodeInfo");
|
|
172
|
+
if (codeInfo) {
|
|
173
|
+
decorations.push(markDecorations["code-info"].range(codeInfo.from, codeInfo.to));
|
|
174
|
+
}
|
|
175
|
+
const codeMarks = node.node.getChildren("CodeMark");
|
|
176
|
+
for (const mark of codeMarks) {
|
|
177
|
+
decorations.push(markDecorations["code-mark"].range(mark.from, mark.to));
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if (name === "Blockquote") {
|
|
181
|
+
decorations.push(markDecorations.blockquote.range(from, to));
|
|
182
|
+
const startLine = view.state.doc.lineAt(from);
|
|
183
|
+
const endLine = view.state.doc.lineAt(to);
|
|
184
|
+
for (let i = startLine.number; i <= endLine.number; i++) {
|
|
185
|
+
const line = view.state.doc.line(i);
|
|
186
|
+
decorations.push(lineDecorations.blockquote.range(line.from));
|
|
187
|
+
}
|
|
188
|
+
const quoteMarks = node.node.getChildren("QuoteMark");
|
|
189
|
+
for (const mark of quoteMarks) {
|
|
190
|
+
decorations.push(markDecorations["quote-mark"].range(mark.from, mark.to));
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if (name === "HorizontalRule") {
|
|
194
|
+
const line = view.state.doc.lineAt(from);
|
|
195
|
+
decorations.push(lineDecorations.hr.range(line.from));
|
|
196
|
+
decorations.push(markDecorations.hr.range(from, to));
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
if (plugins.length > 0) {
|
|
201
|
+
const ctx = {
|
|
202
|
+
view,
|
|
203
|
+
decorations,
|
|
204
|
+
selectionOverlapsRange: (from, to) => selectionOverlapsRange(view, from, to),
|
|
205
|
+
cursorInRange: (from, to) => cursorInRange(view, from, to)
|
|
206
|
+
};
|
|
207
|
+
const sortedPlugins = [...plugins].sort((a, b) => a.decorationPriority - b.decorationPriority);
|
|
208
|
+
for (const plugin of sortedPlugins) {
|
|
209
|
+
plugin.buildDecorations(ctx);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
decorations.sort((a, b) => a.from - b.from || a.value.startSide - b.value.startSide);
|
|
213
|
+
for (const decoration of decorations) {
|
|
214
|
+
builder.add(decoration.from, decoration.to, decoration.value);
|
|
215
|
+
}
|
|
216
|
+
return builder.finish();
|
|
217
|
+
}
|
|
218
|
+
var draftlyViewPluginClass = class {
|
|
219
|
+
decorations;
|
|
220
|
+
plugins;
|
|
221
|
+
onNodesChange;
|
|
222
|
+
constructor(view) {
|
|
223
|
+
this.plugins = view.state.facet(DraftlyPluginsFacet);
|
|
224
|
+
this.onNodesChange = view.state.facet(draftlyOnNodesChangeFacet);
|
|
225
|
+
this.decorations = buildDecorations(view, this.plugins);
|
|
226
|
+
for (const plugin of this.plugins) {
|
|
227
|
+
plugin.onViewReady(view);
|
|
228
|
+
}
|
|
229
|
+
if (this.onNodesChange) {
|
|
230
|
+
this.onNodesChange(this.buildNodes(view));
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
update(update) {
|
|
234
|
+
this.plugins = update.view.state.facet(DraftlyPluginsFacet);
|
|
235
|
+
this.onNodesChange = update.view.state.facet(draftlyOnNodesChangeFacet);
|
|
236
|
+
for (const plugin of this.plugins) {
|
|
237
|
+
plugin.onViewUpdate(update);
|
|
238
|
+
}
|
|
239
|
+
if (update.docChanged || update.selectionSet || update.viewportChanged) {
|
|
240
|
+
this.decorations = buildDecorations(update.view, this.plugins);
|
|
241
|
+
if (this.onNodesChange) {
|
|
242
|
+
this.onNodesChange(this.buildNodes(update.view));
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
buildNodes(view) {
|
|
247
|
+
const tree = language.syntaxTree(view.state);
|
|
248
|
+
const roots = [];
|
|
249
|
+
const stack = [];
|
|
250
|
+
tree.iterate({
|
|
251
|
+
enter: (nodeRef) => {
|
|
252
|
+
const node = {
|
|
253
|
+
from: nodeRef.from,
|
|
254
|
+
to: nodeRef.to,
|
|
255
|
+
name: nodeRef.name,
|
|
256
|
+
children: [],
|
|
257
|
+
isSelected: selectionOverlapsRange(view, nodeRef.from, nodeRef.to)
|
|
258
|
+
};
|
|
259
|
+
if (stack.length > 0) {
|
|
260
|
+
stack[stack.length - 1].children.push(node);
|
|
261
|
+
} else {
|
|
262
|
+
roots.push(node);
|
|
263
|
+
}
|
|
264
|
+
stack.push(node);
|
|
265
|
+
},
|
|
266
|
+
leave: () => {
|
|
267
|
+
stack.pop();
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
return roots;
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
var draftlyViewPlugin = view.ViewPlugin.fromClass(draftlyViewPluginClass, {
|
|
274
|
+
decorations: (v) => v.decorations,
|
|
275
|
+
provide: () => [language.syntaxHighlighting(highlightStyle)]
|
|
276
|
+
});
|
|
277
|
+
var draftlyEditorClass = view.EditorView.editorAttributes.of({ class: "cm-draftly-enabled" });
|
|
278
|
+
function createDraftlyViewExtension(plugins = [], onNodesChange) {
|
|
279
|
+
return [
|
|
280
|
+
DraftlyPluginsFacet.of(plugins),
|
|
281
|
+
draftlyOnNodesChangeFacet.of(onNodesChange),
|
|
282
|
+
draftlyViewPlugin,
|
|
283
|
+
draftlyBaseTheme,
|
|
284
|
+
draftlyEditorClass
|
|
285
|
+
];
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// src/plugin.ts
|
|
289
|
+
var DraftlyPlugin = class {
|
|
290
|
+
/** Plugin dependencies - names of required plugins */
|
|
291
|
+
dependencies = [];
|
|
292
|
+
/** Private configuration storage */
|
|
293
|
+
_config = {};
|
|
294
|
+
/** Protected context - accessible to subclasses */
|
|
295
|
+
_context = null;
|
|
296
|
+
/** Get plugin configuration */
|
|
297
|
+
get config() {
|
|
298
|
+
return this._config;
|
|
299
|
+
}
|
|
300
|
+
/** Set plugin configuration */
|
|
301
|
+
set config(value) {
|
|
302
|
+
this._config = value;
|
|
303
|
+
}
|
|
304
|
+
/** Get plugin context */
|
|
305
|
+
get context() {
|
|
306
|
+
return this._context;
|
|
307
|
+
}
|
|
308
|
+
// ============================================
|
|
309
|
+
// EXTENSION METHODS (overridable by subclasses)
|
|
310
|
+
// ============================================
|
|
311
|
+
/**
|
|
312
|
+
* Return CodeMirror extensions for this plugin
|
|
313
|
+
* Override to provide custom extensions
|
|
314
|
+
*/
|
|
315
|
+
getExtensions() {
|
|
316
|
+
return [];
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Return markdown parser extensions
|
|
320
|
+
* Override to extend markdown parsing
|
|
321
|
+
*/
|
|
322
|
+
getMarkdownConfig() {
|
|
323
|
+
return null;
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Return keybindings for this plugin
|
|
327
|
+
* Override to add custom keyboard shortcuts
|
|
328
|
+
*/
|
|
329
|
+
getKeymap() {
|
|
330
|
+
return [];
|
|
331
|
+
}
|
|
332
|
+
// ============================================
|
|
333
|
+
// DECORATION METHODS (overridable by subclasses)
|
|
334
|
+
// ============================================
|
|
335
|
+
/**
|
|
336
|
+
* Decoration priority (higher = applied later)
|
|
337
|
+
* Override to customize priority. Default: 100
|
|
338
|
+
*/
|
|
339
|
+
get decorationPriority() {
|
|
340
|
+
return 100;
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Build decorations for the current view state
|
|
344
|
+
* Override to contribute decorations to the editor
|
|
345
|
+
*
|
|
346
|
+
* @param ctx - Decoration context with view and decoration array
|
|
347
|
+
*/
|
|
348
|
+
buildDecorations(_ctx) {
|
|
349
|
+
}
|
|
350
|
+
// ============================================
|
|
351
|
+
// LIFECYCLE HOOKS (overridable by subclasses)
|
|
352
|
+
// ============================================
|
|
353
|
+
/**
|
|
354
|
+
* Called when plugin is registered with draftly
|
|
355
|
+
* Override to perform initialization
|
|
356
|
+
*
|
|
357
|
+
* @param context - Plugin context with configuration
|
|
358
|
+
*/
|
|
359
|
+
onRegister(context) {
|
|
360
|
+
this._context = context;
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Called when plugin is unregistered
|
|
364
|
+
* Override to perform cleanup
|
|
365
|
+
*/
|
|
366
|
+
onUnregister() {
|
|
367
|
+
this._context = null;
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Called when EditorView is created and ready
|
|
371
|
+
* Override to perform view-specific initialization
|
|
372
|
+
*
|
|
373
|
+
* @param view - The EditorView instance
|
|
374
|
+
*/
|
|
375
|
+
onViewReady(_view) {
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Called on view updates (document changes, selection changes, etc.)
|
|
379
|
+
* Override to react to editor changes
|
|
380
|
+
*
|
|
381
|
+
* @param update - The ViewUpdate with change information
|
|
382
|
+
*/
|
|
383
|
+
onViewUpdate(_update) {
|
|
384
|
+
}
|
|
385
|
+
// ============================================
|
|
386
|
+
// ACCESSIBILITY METHODS (overridable by subclasses)
|
|
387
|
+
// ============================================
|
|
388
|
+
/**
|
|
389
|
+
* Return ARIA attributes to add to the editor
|
|
390
|
+
* Override to improve accessibility
|
|
391
|
+
*/
|
|
392
|
+
getAriaAttributes() {
|
|
393
|
+
return {};
|
|
394
|
+
}
|
|
395
|
+
// ============================================
|
|
396
|
+
// VIEW CONTRIBUTIONS (overridable by subclasses)
|
|
397
|
+
// ============================================
|
|
398
|
+
/**
|
|
399
|
+
* Return widget types this plugin provides
|
|
400
|
+
* Override to contribute custom widgets
|
|
401
|
+
*/
|
|
402
|
+
getWidgets() {
|
|
403
|
+
return [];
|
|
404
|
+
}
|
|
405
|
+
// ============================================
|
|
406
|
+
// PROTECTED UTILITIES (for subclasses)
|
|
407
|
+
// ============================================
|
|
408
|
+
/**
|
|
409
|
+
* Helper to get current editor state
|
|
410
|
+
* @param view - The EditorView instance
|
|
411
|
+
*/
|
|
412
|
+
getState(view) {
|
|
413
|
+
return view.state;
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Helper to get current document
|
|
417
|
+
* @param view - The EditorView instance
|
|
418
|
+
*/
|
|
419
|
+
getDocument(view) {
|
|
420
|
+
return view.state.doc;
|
|
421
|
+
}
|
|
422
|
+
};
|
|
423
|
+
var DecorationPlugin = class extends DraftlyPlugin {
|
|
424
|
+
/**
|
|
425
|
+
* Decoration priority - lower than default for decoration plugins
|
|
426
|
+
* Override to customize
|
|
427
|
+
*/
|
|
428
|
+
get decorationPriority() {
|
|
429
|
+
return 50;
|
|
430
|
+
}
|
|
431
|
+
};
|
|
432
|
+
var SyntaxPlugin = class extends DraftlyPlugin {
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
// src/plugins/heading-plugin.ts
|
|
436
|
+
var HEADING_TYPES = ["ATXHeading1", "ATXHeading2", "ATXHeading3", "ATXHeading4", "ATXHeading5", "ATXHeading6"];
|
|
437
|
+
var headingMarkDecorations = {
|
|
438
|
+
"heading-1": view.Decoration.mark({ class: "cm-draftly-h1" }),
|
|
439
|
+
"heading-2": view.Decoration.mark({ class: "cm-draftly-h2" }),
|
|
440
|
+
"heading-3": view.Decoration.mark({ class: "cm-draftly-h3" }),
|
|
441
|
+
"heading-4": view.Decoration.mark({ class: "cm-draftly-h4" }),
|
|
442
|
+
"heading-5": view.Decoration.mark({ class: "cm-draftly-h5" }),
|
|
443
|
+
"heading-6": view.Decoration.mark({ class: "cm-draftly-h6" }),
|
|
444
|
+
"heading-mark": view.Decoration.mark({ class: "cm-draftly-heading-mark" })
|
|
445
|
+
};
|
|
446
|
+
var headingLineDecorations = {
|
|
447
|
+
"heading-1": view.Decoration.line({ class: "cm-draftly-line-h1" }),
|
|
448
|
+
"heading-2": view.Decoration.line({ class: "cm-draftly-line-h2" }),
|
|
449
|
+
"heading-3": view.Decoration.line({ class: "cm-draftly-line-h3" }),
|
|
450
|
+
"heading-4": view.Decoration.line({ class: "cm-draftly-line-h4" }),
|
|
451
|
+
"heading-5": view.Decoration.line({ class: "cm-draftly-line-h5" }),
|
|
452
|
+
"heading-6": view.Decoration.line({ class: "cm-draftly-line-h6" })
|
|
453
|
+
};
|
|
454
|
+
var HeadingPlugin = class extends DecorationPlugin {
|
|
455
|
+
name = "heading";
|
|
456
|
+
version = "1.0.0";
|
|
457
|
+
/**
|
|
458
|
+
* Higher priority to ensure headings are styled first
|
|
459
|
+
*/
|
|
460
|
+
get decorationPriority() {
|
|
461
|
+
return 10;
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* Get the extensions for this plugin
|
|
465
|
+
*/
|
|
466
|
+
getExtensions() {
|
|
467
|
+
return [headingTheme];
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Build heading decorations by iterating the syntax tree
|
|
471
|
+
*/
|
|
472
|
+
buildDecorations(ctx) {
|
|
473
|
+
const { view, decorations } = ctx;
|
|
474
|
+
const tree = language.syntaxTree(view.state);
|
|
475
|
+
tree.iterate({
|
|
476
|
+
enter: (node) => {
|
|
477
|
+
const { from, to, name } = node;
|
|
478
|
+
if (!HEADING_TYPES.includes(name)) {
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
const level = parseInt(name.slice(-1), 10);
|
|
482
|
+
const headingClass = `heading-${level}`;
|
|
483
|
+
const lineClass = `heading-${level}`;
|
|
484
|
+
const line = view.state.doc.lineAt(from);
|
|
485
|
+
decorations.push(headingLineDecorations[lineClass].range(line.from));
|
|
486
|
+
decorations.push(headingMarkDecorations[headingClass].range(from, to + 1));
|
|
487
|
+
const cursorInNode = ctx.selectionOverlapsRange(from, to);
|
|
488
|
+
if (!cursorInNode) {
|
|
489
|
+
const headingMark = node.node.getChild("HeaderMark");
|
|
490
|
+
if (headingMark) {
|
|
491
|
+
decorations.push(headingMarkDecorations["heading-mark"].range(headingMark.from, headingMark.to + 1));
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
};
|
|
498
|
+
var headingTheme = view.EditorView.theme({
|
|
499
|
+
".cm-draftly-h1": {
|
|
500
|
+
fontSize: "2em",
|
|
501
|
+
fontWeight: "bold",
|
|
502
|
+
fontFamily: "sans-serif",
|
|
503
|
+
textDecoration: "none"
|
|
504
|
+
},
|
|
505
|
+
".cm-draftly-h2": {
|
|
506
|
+
fontSize: "1.75em",
|
|
507
|
+
fontWeight: "bold",
|
|
508
|
+
fontFamily: "sans-serif",
|
|
509
|
+
textDecoration: "none"
|
|
510
|
+
},
|
|
511
|
+
".cm-draftly-h3": {
|
|
512
|
+
fontSize: "1.5em",
|
|
513
|
+
fontWeight: "bold",
|
|
514
|
+
fontFamily: "sans-serif",
|
|
515
|
+
textDecoration: "none"
|
|
516
|
+
},
|
|
517
|
+
".cm-draftly-h4": {
|
|
518
|
+
fontSize: "1.25em",
|
|
519
|
+
fontWeight: "bold",
|
|
520
|
+
fontFamily: "sans-serif",
|
|
521
|
+
textDecoration: "none"
|
|
522
|
+
},
|
|
523
|
+
".cm-draftly-h5": {
|
|
524
|
+
fontSize: "1em",
|
|
525
|
+
fontWeight: "bold",
|
|
526
|
+
fontFamily: "sans-serif",
|
|
527
|
+
textDecoration: "none"
|
|
528
|
+
},
|
|
529
|
+
".cm-draftly-h6": {
|
|
530
|
+
fontSize: "0.75em",
|
|
531
|
+
fontWeight: "bold",
|
|
532
|
+
fontFamily: "sans-serif",
|
|
533
|
+
textDecoration: "none"
|
|
534
|
+
},
|
|
535
|
+
// Heading line styles
|
|
536
|
+
".cm-draftly-line-h1": {
|
|
537
|
+
paddingTop: "1.5em",
|
|
538
|
+
paddingBottom: "0.5em"
|
|
539
|
+
},
|
|
540
|
+
".cm-draftly-line-h2": {
|
|
541
|
+
paddingTop: "1.25em",
|
|
542
|
+
paddingBottom: "0.5em"
|
|
543
|
+
},
|
|
544
|
+
".cm-draftly-line-h3, .cm-draftly-line-h4, .cm-draftly-line-h5, .cm-draftly-line-h6": {
|
|
545
|
+
paddingTop: "1em",
|
|
546
|
+
paddingBottom: "0.5em"
|
|
547
|
+
},
|
|
548
|
+
// Heading mark (# symbols)
|
|
549
|
+
".cm-draftly-heading-mark": {
|
|
550
|
+
display: "none"
|
|
551
|
+
}
|
|
552
|
+
});
|
|
553
|
+
var INLINE_TYPES = {
|
|
554
|
+
Emphasis: "emphasis",
|
|
555
|
+
StrongEmphasis: "strong",
|
|
556
|
+
Strikethrough: "strikethrough",
|
|
557
|
+
Subscript: "subscript",
|
|
558
|
+
Superscript: "superscript"
|
|
559
|
+
};
|
|
560
|
+
var inlineMarkDecorations = {
|
|
561
|
+
emphasis: view.Decoration.mark({ class: "cm-draftly-emphasis" }),
|
|
562
|
+
strong: view.Decoration.mark({ class: "cm-draftly-strong" }),
|
|
563
|
+
strikethrough: view.Decoration.mark({ class: "cm-draftly-strikethrough" }),
|
|
564
|
+
subscript: view.Decoration.mark({ class: "cm-draftly-subscript" }),
|
|
565
|
+
superscript: view.Decoration.mark({ class: "cm-draftly-superscript" }),
|
|
566
|
+
// Markers (* _ ~~ ^ ~)
|
|
567
|
+
"inline-mark": view.Decoration.mark({ class: "cm-draftly-inline-mark" })
|
|
568
|
+
};
|
|
569
|
+
var InlinePlugin = class extends DecorationPlugin {
|
|
570
|
+
name = "inline";
|
|
571
|
+
version = "1.0.0";
|
|
572
|
+
/**
|
|
573
|
+
* Moderate priority for inline styling
|
|
574
|
+
*/
|
|
575
|
+
get decorationPriority() {
|
|
576
|
+
return 20;
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* Get the extensions for this plugin (theme)
|
|
580
|
+
*/
|
|
581
|
+
getExtensions() {
|
|
582
|
+
return [inlineTheme];
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* Build inline decorations by iterating the syntax tree
|
|
586
|
+
*/
|
|
587
|
+
buildDecorations(ctx) {
|
|
588
|
+
const { view, decorations } = ctx;
|
|
589
|
+
const tree = language.syntaxTree(view.state);
|
|
590
|
+
tree.iterate({
|
|
591
|
+
enter: (node) => {
|
|
592
|
+
const { from, to, name } = node;
|
|
593
|
+
const inlineType = INLINE_TYPES[name];
|
|
594
|
+
if (!inlineType) {
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
decorations.push(inlineMarkDecorations[inlineType].range(from, to));
|
|
598
|
+
const cursorInNode = ctx.selectionOverlapsRange(from, to);
|
|
599
|
+
if (!cursorInNode) {
|
|
600
|
+
const markerNames = this.getMarkerNames(name);
|
|
601
|
+
for (const markerName of markerNames) {
|
|
602
|
+
const marks = node.node.getChildren(markerName);
|
|
603
|
+
for (const mark of marks) {
|
|
604
|
+
decorations.push(inlineMarkDecorations["inline-mark"].range(mark.from, mark.to));
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
/**
|
|
612
|
+
* Get the marker node names for a given inline type
|
|
613
|
+
*/
|
|
614
|
+
getMarkerNames(nodeType) {
|
|
615
|
+
switch (nodeType) {
|
|
616
|
+
case "Emphasis":
|
|
617
|
+
case "StrongEmphasis":
|
|
618
|
+
return ["EmphasisMark"];
|
|
619
|
+
case "Strikethrough":
|
|
620
|
+
return ["StrikethroughMark"];
|
|
621
|
+
case "Subscript":
|
|
622
|
+
return ["SubscriptMark"];
|
|
623
|
+
case "Superscript":
|
|
624
|
+
return ["SuperscriptMark"];
|
|
625
|
+
default:
|
|
626
|
+
return [];
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
};
|
|
630
|
+
var inlineTheme = view.EditorView.theme({
|
|
631
|
+
// Emphasis (italic)
|
|
632
|
+
".cm-draftly-emphasis": {
|
|
633
|
+
fontStyle: "italic"
|
|
634
|
+
},
|
|
635
|
+
// Strong (bold)
|
|
636
|
+
".cm-draftly-strong": {
|
|
637
|
+
fontWeight: "bold"
|
|
638
|
+
},
|
|
639
|
+
// Strikethrough
|
|
640
|
+
".cm-draftly-strikethrough": {
|
|
641
|
+
textDecoration: "line-through",
|
|
642
|
+
opacity: "0.7"
|
|
643
|
+
},
|
|
644
|
+
// Subscript
|
|
645
|
+
".cm-draftly-subscript": {
|
|
646
|
+
fontSize: "0.75em",
|
|
647
|
+
verticalAlign: "sub"
|
|
648
|
+
},
|
|
649
|
+
// Superscript
|
|
650
|
+
".cm-draftly-superscript": {
|
|
651
|
+
fontSize: "0.75em",
|
|
652
|
+
verticalAlign: "super"
|
|
653
|
+
},
|
|
654
|
+
// Inline markers (* _ ~~ ^ ~) - hidden when not focused
|
|
655
|
+
".cm-draftly-inline-mark": {
|
|
656
|
+
display: "none"
|
|
657
|
+
}
|
|
658
|
+
});
|
|
659
|
+
var listMarkDecorations = {
|
|
660
|
+
"list-mark-ul": view.Decoration.mark({ class: "cm-draftly-list-mark-ul" }),
|
|
661
|
+
"list-mark-ol": view.Decoration.mark({ class: "cm-draftly-list-mark-ol" }),
|
|
662
|
+
"task-marker": view.Decoration.mark({ class: "cm-draftly-task-marker" })
|
|
663
|
+
};
|
|
664
|
+
var TaskCheckboxWidget = class extends view.WidgetType {
|
|
665
|
+
constructor(checked) {
|
|
666
|
+
super();
|
|
667
|
+
this.checked = checked;
|
|
668
|
+
}
|
|
669
|
+
eq(other) {
|
|
670
|
+
return other.checked === this.checked;
|
|
671
|
+
}
|
|
672
|
+
toDOM(view) {
|
|
673
|
+
const wrap = document.createElement("span");
|
|
674
|
+
wrap.className = `cm-draftly-task-checkbox ${this.checked ? "checked" : ""}`;
|
|
675
|
+
wrap.setAttribute("aria-hidden", "true");
|
|
676
|
+
const checkbox = document.createElement("input");
|
|
677
|
+
checkbox.type = "checkbox";
|
|
678
|
+
checkbox.checked = this.checked;
|
|
679
|
+
checkbox.tabIndex = -1;
|
|
680
|
+
checkbox.addEventListener("mousedown", (e) => {
|
|
681
|
+
e.preventDefault();
|
|
682
|
+
const pos = view.posAtDOM(wrap);
|
|
683
|
+
const line = view.state.doc.lineAt(pos);
|
|
684
|
+
const match = line.text.match(/^(\s*[-*+]\s*)\[([ xX])\]/);
|
|
685
|
+
if (match) {
|
|
686
|
+
const markerStart = line.from + match[1].length + 1;
|
|
687
|
+
const newChar = this.checked ? " " : "x";
|
|
688
|
+
view.dispatch({
|
|
689
|
+
changes: { from: markerStart, to: markerStart + 1, insert: newChar }
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
});
|
|
693
|
+
wrap.appendChild(checkbox);
|
|
694
|
+
return wrap;
|
|
695
|
+
}
|
|
696
|
+
ignoreEvent() {
|
|
697
|
+
return false;
|
|
698
|
+
}
|
|
699
|
+
};
|
|
700
|
+
var ListPlugin = class extends DecorationPlugin {
|
|
701
|
+
name = "list";
|
|
702
|
+
version = "1.0.0";
|
|
703
|
+
/**
|
|
704
|
+
* Moderate priority
|
|
705
|
+
*/
|
|
706
|
+
get decorationPriority() {
|
|
707
|
+
return 20;
|
|
708
|
+
}
|
|
709
|
+
/**
|
|
710
|
+
* Get the extensions for this plugin (theme)
|
|
711
|
+
*/
|
|
712
|
+
getExtensions() {
|
|
713
|
+
return [listTheme];
|
|
714
|
+
}
|
|
715
|
+
/**
|
|
716
|
+
* Build list decorations by iterating the syntax tree
|
|
717
|
+
*/
|
|
718
|
+
buildDecorations(ctx) {
|
|
719
|
+
const { view: view$1, decorations } = ctx;
|
|
720
|
+
const tree = language.syntaxTree(view$1.state);
|
|
721
|
+
tree.iterate({
|
|
722
|
+
enter: (node) => {
|
|
723
|
+
const { from, to, name } = node;
|
|
724
|
+
const line = view$1.state.doc.lineAt(from);
|
|
725
|
+
const cursorInLine = ctx.cursorInRange(line.from, line.to);
|
|
726
|
+
if (name === "ListMark") {
|
|
727
|
+
const parent = node.node.parent;
|
|
728
|
+
const grandparent = parent?.parent;
|
|
729
|
+
const listType = grandparent?.name;
|
|
730
|
+
if (!cursorInLine) {
|
|
731
|
+
if (listType === "OrderedList") {
|
|
732
|
+
decorations.push(listMarkDecorations["list-mark-ol"].range(from, to));
|
|
733
|
+
} else {
|
|
734
|
+
decorations.push(listMarkDecorations["list-mark-ul"].range(from, to));
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
if (name === "TaskMarker") {
|
|
739
|
+
const text = view$1.state.sliceDoc(from, to);
|
|
740
|
+
const isChecked = text.includes("x") || text.includes("X");
|
|
741
|
+
if (cursorInLine) {
|
|
742
|
+
decorations.push(listMarkDecorations["task-marker"].range(from, to));
|
|
743
|
+
} else {
|
|
744
|
+
decorations.push(
|
|
745
|
+
view.Decoration.replace({
|
|
746
|
+
widget: new TaskCheckboxWidget(isChecked)
|
|
747
|
+
}).range(from, to)
|
|
748
|
+
);
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
});
|
|
753
|
+
}
|
|
754
|
+
};
|
|
755
|
+
var listTheme = view.EditorView.theme({
|
|
756
|
+
// Unordered List markers (*, -, +)
|
|
757
|
+
".cm-draftly-list-mark-ul": {
|
|
758
|
+
position: "relative"
|
|
759
|
+
},
|
|
760
|
+
".cm-draftly-list-mark-ul > span": {
|
|
761
|
+
visibility: "hidden"
|
|
762
|
+
},
|
|
763
|
+
".cm-draftly-list-mark-ul::after": {
|
|
764
|
+
content: '"\u2022"',
|
|
765
|
+
position: "absolute",
|
|
766
|
+
left: "50%",
|
|
767
|
+
top: "50%",
|
|
768
|
+
transform: "translate(-50%, -50%)",
|
|
769
|
+
color: "var(--color-link)",
|
|
770
|
+
fontWeight: "bold",
|
|
771
|
+
pointerEvents: "none"
|
|
772
|
+
},
|
|
773
|
+
// Ordered List markers (1., 2.)
|
|
774
|
+
".cm-draftly-list-mark-ol": {
|
|
775
|
+
color: "var(--draftly-highlight, #a4a4a4)",
|
|
776
|
+
fontFamily: "monospace",
|
|
777
|
+
marginRight: "2px"
|
|
778
|
+
},
|
|
779
|
+
// Task markers text ([ ] or [x]) - visible only when editing
|
|
780
|
+
".cm-draftly-task-marker": {
|
|
781
|
+
color: "var(--draftly-highlight, #a4a4a4)",
|
|
782
|
+
fontFamily: "monospace"
|
|
783
|
+
},
|
|
784
|
+
// Task Checkbox Widget
|
|
785
|
+
".cm-draftly-task-checkbox": {
|
|
786
|
+
display: "inline-flex",
|
|
787
|
+
verticalAlign: "middle",
|
|
788
|
+
marginRight: "0.3em",
|
|
789
|
+
cursor: "pointer",
|
|
790
|
+
userSelect: "none",
|
|
791
|
+
alignItems: "center",
|
|
792
|
+
height: "1.2em"
|
|
793
|
+
},
|
|
794
|
+
".cm-draftly-task-checkbox input": {
|
|
795
|
+
cursor: "pointer",
|
|
796
|
+
margin: 0,
|
|
797
|
+
width: "1.1em",
|
|
798
|
+
height: "1.1em",
|
|
799
|
+
appearance: "none",
|
|
800
|
+
border: "1px solid",
|
|
801
|
+
borderRadius: "0.25em",
|
|
802
|
+
backgroundColor: "transparent",
|
|
803
|
+
position: "relative"
|
|
804
|
+
},
|
|
805
|
+
".cm-draftly-task-checkbox.checked input::after": {
|
|
806
|
+
content: '"\u2713"',
|
|
807
|
+
position: "absolute",
|
|
808
|
+
left: "1px",
|
|
809
|
+
top: "-3px"
|
|
810
|
+
}
|
|
811
|
+
});
|
|
812
|
+
var htmlMarkDecorations = {
|
|
813
|
+
"html-tag": view.Decoration.mark({ class: "cm-draftly-html-tag" }),
|
|
814
|
+
"html-comment": view.Decoration.mark({ class: "cm-draftly-html-comment" })
|
|
815
|
+
};
|
|
816
|
+
var htmlLineDecorations = {
|
|
817
|
+
"html-block": view.Decoration.line({ class: "cm-draftly-line-html-block" }),
|
|
818
|
+
"hidden-line": view.Decoration.line({ class: "cm-draftly-hidden-line" })
|
|
819
|
+
};
|
|
820
|
+
var HTMLPreviewWidget = class extends view.WidgetType {
|
|
821
|
+
constructor(html) {
|
|
822
|
+
super();
|
|
823
|
+
this.html = html;
|
|
824
|
+
}
|
|
825
|
+
eq(other) {
|
|
826
|
+
return other.html === this.html;
|
|
827
|
+
}
|
|
828
|
+
toDOM() {
|
|
829
|
+
const div = document.createElement("div");
|
|
830
|
+
div.className = "cm-draftly-html-preview";
|
|
831
|
+
div.innerHTML = DOMPurify__default.default.sanitize(this.html);
|
|
832
|
+
return div;
|
|
833
|
+
}
|
|
834
|
+
ignoreEvent() {
|
|
835
|
+
return false;
|
|
836
|
+
}
|
|
837
|
+
};
|
|
838
|
+
var InlineHTMLPreviewWidget = class extends view.WidgetType {
|
|
839
|
+
constructor(html) {
|
|
840
|
+
super();
|
|
841
|
+
this.html = html;
|
|
842
|
+
}
|
|
843
|
+
eq(other) {
|
|
844
|
+
return other.html === this.html;
|
|
845
|
+
}
|
|
846
|
+
toDOM() {
|
|
847
|
+
const span = document.createElement("span");
|
|
848
|
+
span.className = "cm-draftly-inline-html-preview";
|
|
849
|
+
span.innerHTML = DOMPurify__default.default.sanitize(this.html);
|
|
850
|
+
return span;
|
|
851
|
+
}
|
|
852
|
+
ignoreEvent() {
|
|
853
|
+
return false;
|
|
854
|
+
}
|
|
855
|
+
};
|
|
856
|
+
function parseHTMLTag(content) {
|
|
857
|
+
const match = content.match(/^<\s*(\/?)([a-zA-Z][a-zA-Z0-9-]*)[^>]*(\/?)>$/);
|
|
858
|
+
if (!match) return null;
|
|
859
|
+
return {
|
|
860
|
+
tagName: match[2].toLowerCase(),
|
|
861
|
+
isClosing: match[1] === "/",
|
|
862
|
+
isSelfClosing: match[3] === "/" || ["br", "hr", "img", "input", "meta", "link", "area", "base", "col", "embed", "source", "track", "wbr"].includes(
|
|
863
|
+
match[2].toLowerCase()
|
|
864
|
+
)
|
|
865
|
+
};
|
|
866
|
+
}
|
|
867
|
+
var HTMLPlugin = class extends DecorationPlugin {
|
|
868
|
+
name = "html";
|
|
869
|
+
version = "1.0.0";
|
|
870
|
+
get decorationPriority() {
|
|
871
|
+
return 30;
|
|
872
|
+
}
|
|
873
|
+
getExtensions() {
|
|
874
|
+
return [htmlTheme];
|
|
875
|
+
}
|
|
876
|
+
buildDecorations(ctx) {
|
|
877
|
+
const { view: view$1, decorations } = ctx;
|
|
878
|
+
const tree = language.syntaxTree(view$1.state);
|
|
879
|
+
const htmlGroups = [];
|
|
880
|
+
const htmlTags = [];
|
|
881
|
+
tree.iterate({
|
|
882
|
+
enter: (node) => {
|
|
883
|
+
const { from, to, name } = node;
|
|
884
|
+
if (name === "Comment") {
|
|
885
|
+
decorations.push(htmlMarkDecorations["html-comment"].range(from, to));
|
|
886
|
+
return;
|
|
887
|
+
}
|
|
888
|
+
if (name === "HTMLTag") {
|
|
889
|
+
const content = view$1.state.sliceDoc(from, to);
|
|
890
|
+
const parsed = parseHTMLTag(content);
|
|
891
|
+
if (parsed) {
|
|
892
|
+
htmlTags.push({
|
|
893
|
+
from,
|
|
894
|
+
to,
|
|
895
|
+
tagName: parsed.tagName,
|
|
896
|
+
isClosing: parsed.isClosing,
|
|
897
|
+
isSelfClosing: parsed.isSelfClosing
|
|
898
|
+
});
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
if (name === "HTMLBlock") {
|
|
902
|
+
const last = htmlGroups[htmlGroups.length - 1];
|
|
903
|
+
if (last) {
|
|
904
|
+
const gap = view$1.state.sliceDoc(last.to, from);
|
|
905
|
+
if (!gap.trim()) {
|
|
906
|
+
last.to = to;
|
|
907
|
+
return;
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
htmlGroups.push({ from, to });
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
});
|
|
914
|
+
const inlineElements = [];
|
|
915
|
+
const usedTags = /* @__PURE__ */ new Set();
|
|
916
|
+
for (let i = 0; i < htmlTags.length; i++) {
|
|
917
|
+
if (usedTags.has(i)) continue;
|
|
918
|
+
const openTag = htmlTags[i];
|
|
919
|
+
if (openTag.isClosing) continue;
|
|
920
|
+
if (openTag.isSelfClosing) {
|
|
921
|
+
inlineElements.push({
|
|
922
|
+
from: openTag.from,
|
|
923
|
+
to: openTag.to,
|
|
924
|
+
content: view$1.state.sliceDoc(openTag.from, openTag.to)
|
|
925
|
+
});
|
|
926
|
+
usedTags.add(i);
|
|
927
|
+
continue;
|
|
928
|
+
}
|
|
929
|
+
const openLine = view$1.state.doc.lineAt(openTag.from);
|
|
930
|
+
let depth = 1;
|
|
931
|
+
let closeTagIndex = null;
|
|
932
|
+
for (let j = i + 1; j < htmlTags.length && depth > 0; j++) {
|
|
933
|
+
const tag = htmlTags[j];
|
|
934
|
+
if (tag.from > openLine.to) break;
|
|
935
|
+
if (tag.tagName === openTag.tagName) {
|
|
936
|
+
if (tag.isClosing) {
|
|
937
|
+
depth--;
|
|
938
|
+
if (depth === 0) {
|
|
939
|
+
closeTagIndex = j;
|
|
940
|
+
}
|
|
941
|
+
} else if (!tag.isSelfClosing) {
|
|
942
|
+
depth++;
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
if (closeTagIndex !== null) {
|
|
947
|
+
const closeTag = htmlTags[closeTagIndex];
|
|
948
|
+
inlineElements.push({
|
|
949
|
+
from: openTag.from,
|
|
950
|
+
to: closeTag.to,
|
|
951
|
+
content: view$1.state.sliceDoc(openTag.from, closeTag.to)
|
|
952
|
+
});
|
|
953
|
+
for (let k = i; k <= closeTagIndex; k++) {
|
|
954
|
+
usedTags.add(k);
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
inlineElements.sort((a, b) => a.from - b.from);
|
|
959
|
+
const filteredElements = [];
|
|
960
|
+
let lastEnd = -1;
|
|
961
|
+
for (const elem of inlineElements) {
|
|
962
|
+
if (elem.from >= lastEnd) {
|
|
963
|
+
filteredElements.push(elem);
|
|
964
|
+
lastEnd = elem.to;
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
for (const elem of filteredElements) {
|
|
968
|
+
const cursorInRange2 = ctx.cursorInRange(elem.from, elem.to);
|
|
969
|
+
if (cursorInRange2) {
|
|
970
|
+
for (const tag of htmlTags) {
|
|
971
|
+
if (tag.from >= elem.from && tag.to <= elem.to) {
|
|
972
|
+
decorations.push(htmlMarkDecorations["html-tag"].range(tag.from, tag.to));
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
} else {
|
|
976
|
+
decorations.push(
|
|
977
|
+
view.Decoration.replace({
|
|
978
|
+
widget: new InlineHTMLPreviewWidget(elem.content)
|
|
979
|
+
}).range(elem.from, elem.to)
|
|
980
|
+
);
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
for (let i = 0; i < htmlTags.length; i++) {
|
|
984
|
+
if (!usedTags.has(i)) {
|
|
985
|
+
const tag = htmlTags[i];
|
|
986
|
+
decorations.push(htmlMarkDecorations["html-tag"].range(tag.from, tag.to));
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
for (const group of htmlGroups) {
|
|
990
|
+
const { from, to } = group;
|
|
991
|
+
const nodeLineStart = view$1.state.doc.lineAt(from);
|
|
992
|
+
const nodeLineEnd = view$1.state.doc.lineAt(to);
|
|
993
|
+
const cursorInRange2 = ctx.cursorInRange(nodeLineStart.from, nodeLineEnd.to);
|
|
994
|
+
if (cursorInRange2) {
|
|
995
|
+
for (let i = nodeLineStart.number; i <= nodeLineEnd.number; i++) {
|
|
996
|
+
const line = view$1.state.doc.line(i);
|
|
997
|
+
decorations.push(htmlLineDecorations["html-block"].range(line.from));
|
|
998
|
+
}
|
|
999
|
+
} else {
|
|
1000
|
+
const htmlContent = view$1.state.sliceDoc(from, to);
|
|
1001
|
+
decorations.push(
|
|
1002
|
+
view.Decoration.replace({
|
|
1003
|
+
widget: new HTMLPreviewWidget(htmlContent.trim())
|
|
1004
|
+
}).range(from, nodeLineStart.to)
|
|
1005
|
+
);
|
|
1006
|
+
for (let i = nodeLineStart.number + 1; i <= nodeLineEnd.number; i++) {
|
|
1007
|
+
const line = view$1.state.doc.line(i);
|
|
1008
|
+
decorations.push(htmlLineDecorations["hidden-line"].range(line.from));
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
};
|
|
1014
|
+
var htmlTheme = view.EditorView.theme({
|
|
1015
|
+
".cm-draftly-html-tag": {
|
|
1016
|
+
color: "#6a737d",
|
|
1017
|
+
fontFamily: "var(--font-jetbrains-mono, monospace)",
|
|
1018
|
+
fontSize: "0.85em"
|
|
1019
|
+
},
|
|
1020
|
+
".cm-draftly-html-comment": {
|
|
1021
|
+
color: "#6a737d",
|
|
1022
|
+
fontStyle: "italic",
|
|
1023
|
+
fontFamily: "var(--font-jetbrains-mono, monospace)",
|
|
1024
|
+
fontSize: "0.85em",
|
|
1025
|
+
opacity: 0.5
|
|
1026
|
+
},
|
|
1027
|
+
".cm-draftly-line-html-block": {
|
|
1028
|
+
backgroundColor: "rgba(0, 0, 0, 0.02)"
|
|
1029
|
+
},
|
|
1030
|
+
".cm-draftly-hidden-line": {
|
|
1031
|
+
display: "none"
|
|
1032
|
+
},
|
|
1033
|
+
".cm-draftly-html-preview": {
|
|
1034
|
+
display: "inline-block",
|
|
1035
|
+
width: "100%",
|
|
1036
|
+
verticalAlign: "top",
|
|
1037
|
+
margin: "0",
|
|
1038
|
+
whiteSpace: "normal",
|
|
1039
|
+
lineHeight: "1.4"
|
|
1040
|
+
},
|
|
1041
|
+
".cm-draftly-html-preview > *:first-child": {
|
|
1042
|
+
marginTop: "0"
|
|
1043
|
+
},
|
|
1044
|
+
".cm-draftly-html-preview > *:last-child": {
|
|
1045
|
+
marginBottom: "0"
|
|
1046
|
+
},
|
|
1047
|
+
".cm-draftly-inline-html-preview": {
|
|
1048
|
+
display: "inline",
|
|
1049
|
+
whiteSpace: "normal"
|
|
1050
|
+
}
|
|
1051
|
+
});
|
|
1052
|
+
|
|
1053
|
+
// src/plugins/plugins.ts
|
|
1054
|
+
var defaultPlugins = [new HeadingPlugin(), new InlinePlugin(), new ListPlugin(), new HTMLPlugin()];
|
|
1055
|
+
|
|
1056
|
+
// src/draftly.ts
|
|
1057
|
+
function draftly(config = {}) {
|
|
1058
|
+
const {
|
|
1059
|
+
plugins = [],
|
|
1060
|
+
extensions = [],
|
|
1061
|
+
keymap: configKeymap = [],
|
|
1062
|
+
disableViewPlugin = false,
|
|
1063
|
+
defaultKeybindings = true,
|
|
1064
|
+
history: configHistory = true,
|
|
1065
|
+
indentWithTab: configIndentWithTab = true,
|
|
1066
|
+
drawSelection: configDrawSelection = true,
|
|
1067
|
+
highlightActiveLine: configHighlightActiveLine = true,
|
|
1068
|
+
rectangularSelection: configRectangularSelection = true,
|
|
1069
|
+
lineWrapping: configLineWrapping = true,
|
|
1070
|
+
onNodesChange: configOnNodesChange = void 0
|
|
1071
|
+
} = config;
|
|
1072
|
+
const allPlugins = [...defaultPlugins, ...plugins];
|
|
1073
|
+
const pluginExtensions = [];
|
|
1074
|
+
const pluginKeymaps = [];
|
|
1075
|
+
const markdownExtensions = [];
|
|
1076
|
+
const pluginContext = { config };
|
|
1077
|
+
for (const plugin of allPlugins) {
|
|
1078
|
+
plugin.onRegister(pluginContext);
|
|
1079
|
+
const exts = plugin.getExtensions();
|
|
1080
|
+
if (exts.length > 0) {
|
|
1081
|
+
pluginExtensions.push(...exts);
|
|
1082
|
+
}
|
|
1083
|
+
const keys = plugin.getKeymap();
|
|
1084
|
+
if (keys.length > 0) {
|
|
1085
|
+
pluginKeymaps.push(...keys);
|
|
1086
|
+
}
|
|
1087
|
+
const md = plugin.getMarkdownConfig();
|
|
1088
|
+
if (md) {
|
|
1089
|
+
markdownExtensions.push(md);
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
if (config.markdown) {
|
|
1093
|
+
markdownExtensions.push(...config.markdown);
|
|
1094
|
+
}
|
|
1095
|
+
const markdownSupport = langMarkdown.markdown({
|
|
1096
|
+
base: langMarkdown.markdownLanguage,
|
|
1097
|
+
codeLanguages: languageData.languages,
|
|
1098
|
+
extensions: markdownExtensions,
|
|
1099
|
+
addKeymap: true,
|
|
1100
|
+
completeHTMLTags: true
|
|
1101
|
+
});
|
|
1102
|
+
const baseExtensions = [
|
|
1103
|
+
...defaultKeybindings ? [view.keymap.of(commands.defaultKeymap)] : [],
|
|
1104
|
+
...configHistory ? [commands.history(), view.keymap.of(commands.historyKeymap)] : [],
|
|
1105
|
+
...configIndentWithTab ? [language.indentOnInput(), view.keymap.of([commands.indentWithTab])] : [],
|
|
1106
|
+
...configDrawSelection ? [view.drawSelection()] : [],
|
|
1107
|
+
...configHighlightActiveLine && disableViewPlugin ? [view.highlightActiveLine()] : [],
|
|
1108
|
+
...configRectangularSelection ? [view.rectangularSelection()] : []
|
|
1109
|
+
];
|
|
1110
|
+
const draftlyExtensions = [];
|
|
1111
|
+
if (!disableViewPlugin) draftlyExtensions.push(createDraftlyViewExtension(allPlugins, configOnNodesChange));
|
|
1112
|
+
if (!disableViewPlugin || configLineWrapping) draftlyExtensions.push(view.EditorView.lineWrapping);
|
|
1113
|
+
const composedExtensions = [
|
|
1114
|
+
// Core markdown support (highest priority)
|
|
1115
|
+
state.Prec.high(markdownSupport),
|
|
1116
|
+
state.Prec.high(view.keymap.of(langMarkdown.markdownKeymap)),
|
|
1117
|
+
// Core CodeMirror extensions
|
|
1118
|
+
...baseExtensions,
|
|
1119
|
+
// draftly view plugin for rich rendering
|
|
1120
|
+
...draftlyExtensions,
|
|
1121
|
+
// Plugin extensions & keymaps
|
|
1122
|
+
...pluginExtensions,
|
|
1123
|
+
pluginKeymaps.length > 0 ? view.keymap.of(pluginKeymaps) : [],
|
|
1124
|
+
// Config keymaps & extensions
|
|
1125
|
+
configKeymap.length > 0 ? view.keymap.of(configKeymap) : [],
|
|
1126
|
+
...extensions
|
|
1127
|
+
];
|
|
1128
|
+
return composedExtensions;
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
// src/index.ts
|
|
1132
|
+
var index_default = draftly;
|
|
1133
|
+
|
|
1134
|
+
exports.DecorationPlugin = DecorationPlugin;
|
|
1135
|
+
exports.DraftlyPlugin = DraftlyPlugin;
|
|
1136
|
+
exports.DraftlyPluginsFacet = DraftlyPluginsFacet;
|
|
1137
|
+
exports.SyntaxPlugin = SyntaxPlugin;
|
|
1138
|
+
exports.createDraftlyViewExtension = createDraftlyViewExtension;
|
|
1139
|
+
exports.default = index_default;
|
|
1140
|
+
exports.draftly = draftly;
|
|
1141
|
+
exports.draftlyOnNodesChangeFacet = draftlyOnNodesChangeFacet;
|
|
1142
|
+
exports.draftlyViewPlugin = draftlyViewPlugin;
|
|
1143
|
+
//# sourceMappingURL=index.cjs.map
|
|
1144
|
+
//# sourceMappingURL=index.cjs.map
|