overtype 1.2.4 → 1.2.5
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/README.md +4 -4
- package/dist/overtype.cjs +104 -19
- package/dist/overtype.cjs.map +2 -2
- package/dist/overtype.esm.js +104 -19
- package/dist/overtype.esm.js.map +2 -2
- package/dist/overtype.js +104 -19
- package/dist/overtype.js.map +2 -2
- package/dist/overtype.min.js +49 -49
- package/package.json +3 -2
- package/src/parser.js +145 -32
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# OverType
|
|
2
2
|
|
|
3
|
-
A lightweight markdown editor library with perfect WYSIWYG alignment using an invisible textarea overlay technique. Includes optional toolbar. ~
|
|
3
|
+
A lightweight markdown editor library with perfect WYSIWYG alignment using an invisible textarea overlay technique. Includes optional toolbar. ~86KB minified with all features.
|
|
4
4
|
|
|
5
5
|
## Live Examples
|
|
6
6
|
|
|
@@ -19,7 +19,7 @@ A lightweight markdown editor library with perfect WYSIWYG alignment using an in
|
|
|
19
19
|
- ⌨️ **Keyboard shortcuts** - Common markdown shortcuts (Cmd/Ctrl+B for bold, etc.)
|
|
20
20
|
- 📱 **Mobile optimized** - Responsive design with mobile-specific styles
|
|
21
21
|
- 🔄 **DOM persistence aware** - Recovers from existing DOM (perfect for HyperClay and similar platforms)
|
|
22
|
-
- 🚀 **Lightweight** - ~
|
|
22
|
+
- 🚀 **Lightweight** - ~86KB minified
|
|
23
23
|
- 🎯 **Optional toolbar** - Clean, minimal toolbar with all essential formatting
|
|
24
24
|
- ✨ **Smart shortcuts** - Keyboard shortcuts with selection preservation
|
|
25
25
|
- 📝 **Smart list continuation** - GitHub-style automatic list continuation on Enter
|
|
@@ -35,7 +35,7 @@ We overlap an invisible textarea on top of styled output, giving the illusion of
|
|
|
35
35
|
|
|
36
36
|
| Feature | OverType | HyperMD | Milkdown | TUI Editor | EasyMDE |
|
|
37
37
|
|---------|----------|---------|----------|------------|---------|
|
|
38
|
-
| **Size** | ~
|
|
38
|
+
| **Size** | ~86KB | 364.02 KB | 344.51 KB | 560.99 KB | 323.69 KB |
|
|
39
39
|
| **Dependencies** | Bundled | CodeMirror | ProseMirror + plugins | Multiple libs | CodeMirror |
|
|
40
40
|
| **Setup** | Single file | Complex config | Build step required | Complex config | Moderate |
|
|
41
41
|
| **Approach** | Invisible textarea | ContentEditable | ContentEditable | ContentEditable | CodeMirror |
|
|
@@ -583,7 +583,7 @@ MIT
|
|
|
583
583
|
- **Pluggable parser system** - Support for any programming language or syntax
|
|
584
584
|
- **Parser registry** - Automatic language detection by file extension or MIME type
|
|
585
585
|
- **Cleaner separation** - Extracted the overlay technique without markdown-specific features
|
|
586
|
-
- **Smaller footprint** - ~
|
|
586
|
+
- **Smaller footprint** - ~86KB minified (vs OverType's ~78KB)
|
|
587
587
|
|
|
588
588
|
Key components extracted from OverType to Synesthesia:
|
|
589
589
|
- The transparent textarea overlay technique for perfect WYSIWYG alignment
|
package/dist/overtype.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* OverType v1.2.
|
|
2
|
+
* OverType v1.2.5
|
|
3
3
|
* A lightweight markdown editor library with perfect WYSIWYG alignment
|
|
4
4
|
* @license MIT
|
|
5
5
|
* @author Demo User
|
|
@@ -152,7 +152,7 @@ var MarkdownParser = class {
|
|
|
152
152
|
*/
|
|
153
153
|
static parseItalic(html) {
|
|
154
154
|
html = html.replace(new RegExp("(?<!\\*)\\*(?!\\*)(.+?)(?<!\\*)\\*(?!\\*)", "g"), '<em><span class="syntax-marker">*</span>$1<span class="syntax-marker">*</span></em>');
|
|
155
|
-
html = html.replace(new RegExp("(
|
|
155
|
+
html = html.replace(new RegExp("(?<=^|\\s)_(?!_)(.+?)(?<!_)_(?!_)(?=\\s|$)", "g"), '<em><span class="syntax-marker">_</span>$1<span class="syntax-marker">_</span></em>');
|
|
156
156
|
return html;
|
|
157
157
|
}
|
|
158
158
|
/**
|
|
@@ -209,31 +209,116 @@ var MarkdownParser = class {
|
|
|
209
209
|
});
|
|
210
210
|
}
|
|
211
211
|
/**
|
|
212
|
-
*
|
|
213
|
-
* @param {string} text - Text with potential
|
|
214
|
-
* @returns {
|
|
212
|
+
* Identify and protect sanctuaries (code and links) before parsing
|
|
213
|
+
* @param {string} text - Text with potential markdown
|
|
214
|
+
* @returns {Object} Object with protected text and sanctuary map
|
|
215
215
|
*/
|
|
216
|
-
static
|
|
217
|
-
let html = text;
|
|
218
|
-
html = this.parseInlineCode(html);
|
|
216
|
+
static identifyAndProtectSanctuaries(text) {
|
|
219
217
|
const sanctuaries = /* @__PURE__ */ new Map();
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
218
|
+
let sanctuaryCounter = 0;
|
|
219
|
+
let protectedText = text;
|
|
220
|
+
const protectedRegions = [];
|
|
221
|
+
const linkRegex = /\[([^\]]+)\]\(([^)]+)\)/g;
|
|
222
|
+
let linkMatch;
|
|
223
|
+
while ((linkMatch = linkRegex.exec(text)) !== null) {
|
|
224
|
+
const bracketPos = linkMatch.index + linkMatch[0].indexOf("](");
|
|
225
|
+
const urlStart = bracketPos + 2;
|
|
226
|
+
const urlEnd = urlStart + linkMatch[2].length;
|
|
227
|
+
protectedRegions.push({ start: urlStart, end: urlEnd });
|
|
228
|
+
}
|
|
229
|
+
const codeRegex = new RegExp("(?<!`)(`+)(?!`)((?:(?!\\1).)+?)(\\1)(?!`)", "g");
|
|
230
|
+
let codeMatch;
|
|
231
|
+
const codeMatches = [];
|
|
232
|
+
while ((codeMatch = codeRegex.exec(text)) !== null) {
|
|
233
|
+
const codeStart = codeMatch.index;
|
|
234
|
+
const codeEnd = codeMatch.index + codeMatch[0].length;
|
|
235
|
+
const inProtectedRegion = protectedRegions.some(
|
|
236
|
+
(region) => codeStart >= region.start && codeEnd <= region.end
|
|
237
|
+
);
|
|
238
|
+
if (!inProtectedRegion) {
|
|
239
|
+
codeMatches.push({
|
|
240
|
+
match: codeMatch[0],
|
|
241
|
+
index: codeMatch.index,
|
|
242
|
+
openTicks: codeMatch[1],
|
|
243
|
+
content: codeMatch[2],
|
|
244
|
+
closeTicks: codeMatch[3]
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
codeMatches.sort((a, b) => b.index - a.index);
|
|
249
|
+
codeMatches.forEach((codeInfo) => {
|
|
250
|
+
const placeholder = `\uE000${sanctuaryCounter++}\uE001`;
|
|
251
|
+
sanctuaries.set(placeholder, {
|
|
252
|
+
type: "code",
|
|
253
|
+
original: codeInfo.match,
|
|
254
|
+
openTicks: codeInfo.openTicks,
|
|
255
|
+
content: codeInfo.content,
|
|
256
|
+
closeTicks: codeInfo.closeTicks
|
|
257
|
+
});
|
|
258
|
+
protectedText = protectedText.substring(0, codeInfo.index) + placeholder + protectedText.substring(codeInfo.index + codeInfo.match.length);
|
|
224
259
|
});
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
260
|
+
protectedText = protectedText.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (match, linkText, url) => {
|
|
261
|
+
const placeholder = `\uE000${sanctuaryCounter++}\uE001`;
|
|
262
|
+
sanctuaries.set(placeholder, {
|
|
263
|
+
type: "link",
|
|
264
|
+
original: match,
|
|
265
|
+
linkText,
|
|
266
|
+
url
|
|
267
|
+
});
|
|
229
268
|
return placeholder;
|
|
230
269
|
});
|
|
270
|
+
return { protectedText, sanctuaries };
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Restore and transform sanctuaries back to HTML
|
|
274
|
+
* @param {string} html - HTML with sanctuary placeholders
|
|
275
|
+
* @param {Map} sanctuaries - Map of sanctuaries to restore
|
|
276
|
+
* @returns {string} HTML with sanctuaries restored and transformed
|
|
277
|
+
*/
|
|
278
|
+
static restoreAndTransformSanctuaries(html, sanctuaries) {
|
|
279
|
+
const placeholders = Array.from(sanctuaries.keys()).sort((a, b) => {
|
|
280
|
+
const indexA = html.indexOf(a);
|
|
281
|
+
const indexB = html.indexOf(b);
|
|
282
|
+
return indexA - indexB;
|
|
283
|
+
});
|
|
284
|
+
placeholders.forEach((placeholder) => {
|
|
285
|
+
const sanctuary = sanctuaries.get(placeholder);
|
|
286
|
+
let replacement;
|
|
287
|
+
if (sanctuary.type === "code") {
|
|
288
|
+
replacement = `<code><span class="syntax-marker">${sanctuary.openTicks}</span>${this.escapeHtml(sanctuary.content)}<span class="syntax-marker">${sanctuary.closeTicks}</span></code>`;
|
|
289
|
+
} else if (sanctuary.type === "link") {
|
|
290
|
+
let processedLinkText = sanctuary.linkText;
|
|
291
|
+
sanctuaries.forEach((innerSanctuary, innerPlaceholder) => {
|
|
292
|
+
if (processedLinkText.includes(innerPlaceholder)) {
|
|
293
|
+
if (innerSanctuary.type === "code") {
|
|
294
|
+
const codeHtml = `<code><span class="syntax-marker">${innerSanctuary.openTicks}</span>${this.escapeHtml(innerSanctuary.content)}<span class="syntax-marker">${innerSanctuary.closeTicks}</span></code>`;
|
|
295
|
+
processedLinkText = processedLinkText.replace(innerPlaceholder, codeHtml);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
processedLinkText = this.parseStrikethrough(processedLinkText);
|
|
300
|
+
processedLinkText = this.parseBold(processedLinkText);
|
|
301
|
+
processedLinkText = this.parseItalic(processedLinkText);
|
|
302
|
+
const anchorName = `--link-${this.linkIndex++}`;
|
|
303
|
+
const safeUrl = this.sanitizeUrl(sanctuary.url);
|
|
304
|
+
replacement = `<a href="${safeUrl}" style="anchor-name: ${anchorName}"><span class="syntax-marker">[</span>${processedLinkText}<span class="syntax-marker url-part">](${this.escapeHtml(sanctuary.url)})</span></a>`;
|
|
305
|
+
}
|
|
306
|
+
html = html.replace(placeholder, replacement);
|
|
307
|
+
});
|
|
308
|
+
return html;
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Parse all inline elements in correct order
|
|
312
|
+
* @param {string} text - Text with potential inline markdown
|
|
313
|
+
* @returns {string} HTML with all inline styling
|
|
314
|
+
*/
|
|
315
|
+
static parseInlineElements(text) {
|
|
316
|
+
const { protectedText, sanctuaries } = this.identifyAndProtectSanctuaries(text);
|
|
317
|
+
let html = protectedText;
|
|
231
318
|
html = this.parseStrikethrough(html);
|
|
232
319
|
html = this.parseBold(html);
|
|
233
320
|
html = this.parseItalic(html);
|
|
234
|
-
|
|
235
|
-
html = html.replace(placeholder, content);
|
|
236
|
-
});
|
|
321
|
+
html = this.restoreAndTransformSanctuaries(html, sanctuaries);
|
|
237
322
|
return html;
|
|
238
323
|
}
|
|
239
324
|
/**
|