overtype 1.2.1 → 1.2.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/README.md +28 -3
- package/dist/overtype.cjs +274 -23
- package/dist/overtype.cjs.map +2 -2
- package/dist/overtype.esm.js +274 -23
- package/dist/overtype.esm.js.map +2 -2
- package/dist/overtype.js +274 -23
- package/dist/overtype.js.map +2 -2
- package/dist/overtype.min.js +60 -47
- package/package.json +5 -2
- package/src/overtype.js +148 -20
- package/src/parser.js +210 -19
- package/src/styles.js +6 -0
package/dist/overtype.esm.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* OverType v1.2.
|
|
2
|
+
* OverType v1.2.2
|
|
3
3
|
* A lightweight markdown editor library with perfect WYSIWYG alignment
|
|
4
4
|
* @license MIT
|
|
5
5
|
* @author Demo User
|
|
@@ -234,11 +234,22 @@ var MarkdownParser = class {
|
|
|
234
234
|
static parse(text, activeLine = -1, showActiveLineRaw = false) {
|
|
235
235
|
this.resetLinkIndex();
|
|
236
236
|
const lines = text.split("\n");
|
|
237
|
+
let inCodeBlock = false;
|
|
237
238
|
const parsedLines = lines.map((line, index) => {
|
|
238
239
|
if (showActiveLineRaw && index === activeLine) {
|
|
239
240
|
const content = this.escapeHtml(line) || " ";
|
|
240
241
|
return `<div class="raw-line">${content}</div>`;
|
|
241
242
|
}
|
|
243
|
+
const codeFenceRegex = /^```[^`]*$/;
|
|
244
|
+
if (codeFenceRegex.test(line)) {
|
|
245
|
+
inCodeBlock = !inCodeBlock;
|
|
246
|
+
return this.parseLine(line);
|
|
247
|
+
}
|
|
248
|
+
if (inCodeBlock) {
|
|
249
|
+
const escaped = this.escapeHtml(line);
|
|
250
|
+
const indented = this.preserveIndentation(escaped, line);
|
|
251
|
+
return `<div>${indented || " "}</div>`;
|
|
252
|
+
}
|
|
242
253
|
return this.parseLine(line);
|
|
243
254
|
});
|
|
244
255
|
const html = parsedLines.join("");
|
|
@@ -278,23 +289,22 @@ var MarkdownParser = class {
|
|
|
278
289
|
if (lang) {
|
|
279
290
|
codeElement.className = `language-${lang}`;
|
|
280
291
|
}
|
|
281
|
-
container.insertBefore(currentCodeBlock, child);
|
|
282
|
-
|
|
292
|
+
container.insertBefore(currentCodeBlock, child.nextSibling);
|
|
293
|
+
currentCodeBlock._codeElement = codeElement;
|
|
283
294
|
continue;
|
|
284
295
|
} else {
|
|
285
296
|
inCodeBlock = false;
|
|
286
297
|
currentCodeBlock = null;
|
|
287
|
-
child.remove();
|
|
288
298
|
continue;
|
|
289
299
|
}
|
|
290
300
|
}
|
|
291
301
|
}
|
|
292
302
|
if (inCodeBlock && currentCodeBlock && child.tagName === "DIV" && !child.querySelector(".code-fence")) {
|
|
293
|
-
const codeElement = currentCodeBlock.querySelector("code");
|
|
303
|
+
const codeElement = currentCodeBlock._codeElement || currentCodeBlock.querySelector("code");
|
|
294
304
|
if (codeElement.textContent.length > 0) {
|
|
295
305
|
codeElement.textContent += "\n";
|
|
296
306
|
}
|
|
297
|
-
const lineText = child.
|
|
307
|
+
const lineText = child.textContent.replace(/\u00A0/g, " ");
|
|
298
308
|
codeElement.textContent += lineText;
|
|
299
309
|
child.remove();
|
|
300
310
|
continue;
|
|
@@ -347,21 +357,163 @@ var MarkdownParser = class {
|
|
|
347
357
|
}
|
|
348
358
|
return match;
|
|
349
359
|
});
|
|
350
|
-
const codeBlockRegex = /<div><span class="code-fence"
|
|
351
|
-
processed = processed.replace(codeBlockRegex, (match,
|
|
360
|
+
const codeBlockRegex = /<div><span class="code-fence">(```[^<]*)<\/span><\/div>(.*?)<div><span class="code-fence">(```)<\/span><\/div>/gs;
|
|
361
|
+
processed = processed.replace(codeBlockRegex, (match, openFence, content, closeFence) => {
|
|
352
362
|
const lines = content.match(/<div>(.*?)<\/div>/gs) || [];
|
|
353
363
|
const codeContent = lines.map((line) => {
|
|
354
|
-
const text = line.replace(/<div>(.*?)<\/div>/s, "$1").replace(/ /g, " ")
|
|
364
|
+
const text = line.replace(/<div>(.*?)<\/div>/s, "$1").replace(/ /g, " ");
|
|
355
365
|
return text;
|
|
356
366
|
}).join("\n");
|
|
357
|
-
const
|
|
358
|
-
|
|
367
|
+
const lang = openFence.slice(3).trim();
|
|
368
|
+
const langClass = lang ? ` class="language-${lang}"` : "";
|
|
369
|
+
let result = `<div><span class="code-fence">${openFence}</span></div>`;
|
|
370
|
+
result += `<pre class="code-block"><code${langClass}>${codeContent}</code></pre>`;
|
|
371
|
+
result += `<div><span class="code-fence">${closeFence}</span></div>`;
|
|
372
|
+
return result;
|
|
359
373
|
});
|
|
360
374
|
return processed;
|
|
361
375
|
}
|
|
376
|
+
/**
|
|
377
|
+
* Get list context at cursor position
|
|
378
|
+
* @param {string} text - Full text content
|
|
379
|
+
* @param {number} cursorPosition - Current cursor position
|
|
380
|
+
* @returns {Object} List context information
|
|
381
|
+
*/
|
|
382
|
+
static getListContext(text, cursorPosition) {
|
|
383
|
+
const lines = text.split("\n");
|
|
384
|
+
let currentPos = 0;
|
|
385
|
+
let lineIndex = 0;
|
|
386
|
+
let lineStart = 0;
|
|
387
|
+
for (let i = 0; i < lines.length; i++) {
|
|
388
|
+
const lineLength = lines[i].length;
|
|
389
|
+
if (currentPos + lineLength >= cursorPosition) {
|
|
390
|
+
lineIndex = i;
|
|
391
|
+
lineStart = currentPos;
|
|
392
|
+
break;
|
|
393
|
+
}
|
|
394
|
+
currentPos += lineLength + 1;
|
|
395
|
+
}
|
|
396
|
+
const currentLine = lines[lineIndex];
|
|
397
|
+
const lineEnd = lineStart + currentLine.length;
|
|
398
|
+
const checkboxMatch = currentLine.match(this.LIST_PATTERNS.checkbox);
|
|
399
|
+
if (checkboxMatch) {
|
|
400
|
+
return {
|
|
401
|
+
inList: true,
|
|
402
|
+
listType: "checkbox",
|
|
403
|
+
indent: checkboxMatch[1],
|
|
404
|
+
marker: "-",
|
|
405
|
+
checked: checkboxMatch[2] === "x",
|
|
406
|
+
content: checkboxMatch[3],
|
|
407
|
+
lineStart,
|
|
408
|
+
lineEnd,
|
|
409
|
+
markerEndPos: lineStart + checkboxMatch[1].length + checkboxMatch[2].length + 5
|
|
410
|
+
// indent + "- [ ] "
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
const bulletMatch = currentLine.match(this.LIST_PATTERNS.bullet);
|
|
414
|
+
if (bulletMatch) {
|
|
415
|
+
return {
|
|
416
|
+
inList: true,
|
|
417
|
+
listType: "bullet",
|
|
418
|
+
indent: bulletMatch[1],
|
|
419
|
+
marker: bulletMatch[2],
|
|
420
|
+
content: bulletMatch[3],
|
|
421
|
+
lineStart,
|
|
422
|
+
lineEnd,
|
|
423
|
+
markerEndPos: lineStart + bulletMatch[1].length + bulletMatch[2].length + 1
|
|
424
|
+
// indent + marker + space
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
const numberedMatch = currentLine.match(this.LIST_PATTERNS.numbered);
|
|
428
|
+
if (numberedMatch) {
|
|
429
|
+
return {
|
|
430
|
+
inList: true,
|
|
431
|
+
listType: "numbered",
|
|
432
|
+
indent: numberedMatch[1],
|
|
433
|
+
marker: parseInt(numberedMatch[2]),
|
|
434
|
+
content: numberedMatch[3],
|
|
435
|
+
lineStart,
|
|
436
|
+
lineEnd,
|
|
437
|
+
markerEndPos: lineStart + numberedMatch[1].length + numberedMatch[2].length + 2
|
|
438
|
+
// indent + number + ". "
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
return {
|
|
442
|
+
inList: false,
|
|
443
|
+
listType: null,
|
|
444
|
+
indent: "",
|
|
445
|
+
marker: null,
|
|
446
|
+
content: currentLine,
|
|
447
|
+
lineStart,
|
|
448
|
+
lineEnd,
|
|
449
|
+
markerEndPos: lineStart
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* Create a new list item based on context
|
|
454
|
+
* @param {Object} context - List context from getListContext
|
|
455
|
+
* @returns {string} New list item text
|
|
456
|
+
*/
|
|
457
|
+
static createNewListItem(context) {
|
|
458
|
+
switch (context.listType) {
|
|
459
|
+
case "bullet":
|
|
460
|
+
return `${context.indent}${context.marker} `;
|
|
461
|
+
case "numbered":
|
|
462
|
+
return `${context.indent}${context.marker + 1}. `;
|
|
463
|
+
case "checkbox":
|
|
464
|
+
return `${context.indent}- [ ] `;
|
|
465
|
+
default:
|
|
466
|
+
return "";
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Renumber all numbered lists in text
|
|
471
|
+
* @param {string} text - Text containing numbered lists
|
|
472
|
+
* @returns {string} Text with renumbered lists
|
|
473
|
+
*/
|
|
474
|
+
static renumberLists(text) {
|
|
475
|
+
const lines = text.split("\n");
|
|
476
|
+
const numbersByIndent = /* @__PURE__ */ new Map();
|
|
477
|
+
let inList = false;
|
|
478
|
+
const result = lines.map((line) => {
|
|
479
|
+
const match = line.match(this.LIST_PATTERNS.numbered);
|
|
480
|
+
if (match) {
|
|
481
|
+
const indent = match[1];
|
|
482
|
+
const indentLevel = indent.length;
|
|
483
|
+
const content = match[3];
|
|
484
|
+
if (!inList) {
|
|
485
|
+
numbersByIndent.clear();
|
|
486
|
+
}
|
|
487
|
+
const currentNumber = (numbersByIndent.get(indentLevel) || 0) + 1;
|
|
488
|
+
numbersByIndent.set(indentLevel, currentNumber);
|
|
489
|
+
for (const [level] of numbersByIndent) {
|
|
490
|
+
if (level > indentLevel) {
|
|
491
|
+
numbersByIndent.delete(level);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
inList = true;
|
|
495
|
+
return `${indent}${currentNumber}. ${content}`;
|
|
496
|
+
} else {
|
|
497
|
+
if (line.trim() === "" || !line.match(/^\s/)) {
|
|
498
|
+
inList = false;
|
|
499
|
+
numbersByIndent.clear();
|
|
500
|
+
}
|
|
501
|
+
return line;
|
|
502
|
+
}
|
|
503
|
+
});
|
|
504
|
+
return result.join("\n");
|
|
505
|
+
}
|
|
362
506
|
};
|
|
363
507
|
// Track link index for anchor naming
|
|
364
508
|
__publicField(MarkdownParser, "linkIndex", 0);
|
|
509
|
+
/**
|
|
510
|
+
* List pattern definitions
|
|
511
|
+
*/
|
|
512
|
+
__publicField(MarkdownParser, "LIST_PATTERNS", {
|
|
513
|
+
bullet: /^(\s*)([-*+])\s+(.*)$/,
|
|
514
|
+
numbered: /^(\s*)(\d+)\.\s+(.*)$/,
|
|
515
|
+
checkbox: /^(\s*)-\s+\[([ x])\]\s+(.*)$/
|
|
516
|
+
});
|
|
365
517
|
|
|
366
518
|
// node_modules/markdown-actions/dist/markdown-actions.esm.js
|
|
367
519
|
var __defProp2 = Object.defineProperty;
|
|
@@ -1496,11 +1648,17 @@ function generateStyles(options = {}) {
|
|
|
1496
1648
|
position: relative !important; /* Override reset - needed for absolute children */
|
|
1497
1649
|
overflow: visible !important; /* Allow dropdown to overflow container */
|
|
1498
1650
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important;
|
|
1651
|
+
text-align: left !important;
|
|
1499
1652
|
${themeVars ? `
|
|
1500
1653
|
/* Theme Variables */
|
|
1501
1654
|
${themeVars}` : ""}
|
|
1502
1655
|
}
|
|
1503
1656
|
|
|
1657
|
+
/* Force left alignment for all elements in the editor */
|
|
1658
|
+
.overtype-container .overtype-wrapper * {
|
|
1659
|
+
text-align: left !important;
|
|
1660
|
+
}
|
|
1661
|
+
|
|
1504
1662
|
/* Auto-resize mode styles */
|
|
1505
1663
|
.overtype-container.overtype-auto-resize {
|
|
1506
1664
|
height: auto !important;
|
|
@@ -2828,7 +2986,9 @@ var _OverType = class _OverType {
|
|
|
2828
2986
|
showActiveLineRaw: false,
|
|
2829
2987
|
showStats: false,
|
|
2830
2988
|
toolbar: false,
|
|
2831
|
-
statsFormatter: null
|
|
2989
|
+
statsFormatter: null,
|
|
2990
|
+
smartLists: true
|
|
2991
|
+
// Enable smart list continuation
|
|
2832
2992
|
};
|
|
2833
2993
|
const { theme, colors, ...cleanOptions } = options;
|
|
2834
2994
|
return {
|
|
@@ -3055,17 +3215,6 @@ var _OverType = class _OverType {
|
|
|
3055
3215
|
closeFence.style.display = "block";
|
|
3056
3216
|
openParent.classList.add("code-block-line");
|
|
3057
3217
|
closeParent.classList.add("code-block-line");
|
|
3058
|
-
let currentDiv = openParent.nextElementSibling;
|
|
3059
|
-
while (currentDiv && currentDiv !== closeParent) {
|
|
3060
|
-
if (currentDiv.tagName === "DIV") {
|
|
3061
|
-
currentDiv.classList.add("code-block-line");
|
|
3062
|
-
const plainText = currentDiv.textContent;
|
|
3063
|
-
currentDiv.textContent = plainText;
|
|
3064
|
-
}
|
|
3065
|
-
currentDiv = currentDiv.nextElementSibling;
|
|
3066
|
-
if (!currentDiv)
|
|
3067
|
-
break;
|
|
3068
|
-
}
|
|
3069
3218
|
}
|
|
3070
3219
|
}
|
|
3071
3220
|
/**
|
|
@@ -3132,11 +3281,113 @@ var _OverType = class _OverType {
|
|
|
3132
3281
|
this.textarea.dispatchEvent(new Event("input", { bubbles: true }));
|
|
3133
3282
|
return;
|
|
3134
3283
|
}
|
|
3284
|
+
if (event.key === "Enter" && !event.shiftKey && !event.metaKey && !event.ctrlKey && this.options.smartLists) {
|
|
3285
|
+
if (this.handleSmartListContinuation()) {
|
|
3286
|
+
event.preventDefault();
|
|
3287
|
+
return;
|
|
3288
|
+
}
|
|
3289
|
+
}
|
|
3135
3290
|
const handled = this.shortcuts.handleKeydown(event);
|
|
3136
3291
|
if (!handled && this.options.onKeydown) {
|
|
3137
3292
|
this.options.onKeydown(event, this);
|
|
3138
3293
|
}
|
|
3139
3294
|
}
|
|
3295
|
+
/**
|
|
3296
|
+
* Handle smart list continuation
|
|
3297
|
+
* @returns {boolean} Whether the event was handled
|
|
3298
|
+
*/
|
|
3299
|
+
handleSmartListContinuation() {
|
|
3300
|
+
const textarea = this.textarea;
|
|
3301
|
+
const cursorPos = textarea.selectionStart;
|
|
3302
|
+
const context = MarkdownParser.getListContext(textarea.value, cursorPos);
|
|
3303
|
+
if (!context || !context.inList)
|
|
3304
|
+
return false;
|
|
3305
|
+
if (context.content.trim() === "" && cursorPos >= context.markerEndPos) {
|
|
3306
|
+
this.deleteListMarker(context);
|
|
3307
|
+
return true;
|
|
3308
|
+
}
|
|
3309
|
+
if (cursorPos > context.markerEndPos && cursorPos < context.lineEnd) {
|
|
3310
|
+
this.splitListItem(context, cursorPos);
|
|
3311
|
+
} else {
|
|
3312
|
+
this.insertNewListItem(context);
|
|
3313
|
+
}
|
|
3314
|
+
if (context.listType === "numbered") {
|
|
3315
|
+
this.scheduleNumberedListUpdate();
|
|
3316
|
+
}
|
|
3317
|
+
return true;
|
|
3318
|
+
}
|
|
3319
|
+
/**
|
|
3320
|
+
* Delete list marker and exit list
|
|
3321
|
+
* @private
|
|
3322
|
+
*/
|
|
3323
|
+
deleteListMarker(context) {
|
|
3324
|
+
this.textarea.setSelectionRange(context.lineStart, context.markerEndPos);
|
|
3325
|
+
document.execCommand("delete");
|
|
3326
|
+
this.textarea.dispatchEvent(new Event("input", { bubbles: true }));
|
|
3327
|
+
}
|
|
3328
|
+
/**
|
|
3329
|
+
* Insert new list item
|
|
3330
|
+
* @private
|
|
3331
|
+
*/
|
|
3332
|
+
insertNewListItem(context) {
|
|
3333
|
+
const newItem = MarkdownParser.createNewListItem(context);
|
|
3334
|
+
document.execCommand("insertText", false, "\n" + newItem);
|
|
3335
|
+
this.textarea.dispatchEvent(new Event("input", { bubbles: true }));
|
|
3336
|
+
}
|
|
3337
|
+
/**
|
|
3338
|
+
* Split list item at cursor position
|
|
3339
|
+
* @private
|
|
3340
|
+
*/
|
|
3341
|
+
splitListItem(context, cursorPos) {
|
|
3342
|
+
const textAfterCursor = context.content.substring(cursorPos - context.markerEndPos);
|
|
3343
|
+
this.textarea.setSelectionRange(cursorPos, context.lineEnd);
|
|
3344
|
+
document.execCommand("delete");
|
|
3345
|
+
const newItem = MarkdownParser.createNewListItem(context);
|
|
3346
|
+
document.execCommand("insertText", false, "\n" + newItem + textAfterCursor);
|
|
3347
|
+
const newCursorPos = this.textarea.selectionStart - textAfterCursor.length;
|
|
3348
|
+
this.textarea.setSelectionRange(newCursorPos, newCursorPos);
|
|
3349
|
+
this.textarea.dispatchEvent(new Event("input", { bubbles: true }));
|
|
3350
|
+
}
|
|
3351
|
+
/**
|
|
3352
|
+
* Schedule numbered list renumbering
|
|
3353
|
+
* @private
|
|
3354
|
+
*/
|
|
3355
|
+
scheduleNumberedListUpdate() {
|
|
3356
|
+
if (this.numberUpdateTimeout) {
|
|
3357
|
+
clearTimeout(this.numberUpdateTimeout);
|
|
3358
|
+
}
|
|
3359
|
+
this.numberUpdateTimeout = setTimeout(() => {
|
|
3360
|
+
this.updateNumberedLists();
|
|
3361
|
+
}, 10);
|
|
3362
|
+
}
|
|
3363
|
+
/**
|
|
3364
|
+
* Update/renumber all numbered lists
|
|
3365
|
+
* @private
|
|
3366
|
+
*/
|
|
3367
|
+
updateNumberedLists() {
|
|
3368
|
+
const value = this.textarea.value;
|
|
3369
|
+
const cursorPos = this.textarea.selectionStart;
|
|
3370
|
+
const newValue = MarkdownParser.renumberLists(value);
|
|
3371
|
+
if (newValue !== value) {
|
|
3372
|
+
let offset = 0;
|
|
3373
|
+
const oldLines = value.split("\n");
|
|
3374
|
+
const newLines = newValue.split("\n");
|
|
3375
|
+
let charCount = 0;
|
|
3376
|
+
for (let i = 0; i < oldLines.length && charCount < cursorPos; i++) {
|
|
3377
|
+
if (oldLines[i] !== newLines[i]) {
|
|
3378
|
+
const diff = newLines[i].length - oldLines[i].length;
|
|
3379
|
+
if (charCount + oldLines[i].length < cursorPos) {
|
|
3380
|
+
offset += diff;
|
|
3381
|
+
}
|
|
3382
|
+
}
|
|
3383
|
+
charCount += oldLines[i].length + 1;
|
|
3384
|
+
}
|
|
3385
|
+
this.textarea.value = newValue;
|
|
3386
|
+
const newCursorPos = cursorPos + offset;
|
|
3387
|
+
this.textarea.setSelectionRange(newCursorPos, newCursorPos);
|
|
3388
|
+
this.textarea.dispatchEvent(new Event("input", { bubbles: true }));
|
|
3389
|
+
}
|
|
3390
|
+
}
|
|
3140
3391
|
/**
|
|
3141
3392
|
* Handle scroll events
|
|
3142
3393
|
* @private
|