overtype 1.2.2 → 1.2.4
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 +41 -15
- package/dist/overtype.cjs +393 -40
- package/dist/overtype.cjs.map +2 -2
- package/dist/overtype.d.ts +169 -0
- package/dist/overtype.esm.js +393 -40
- package/dist/overtype.esm.js.map +2 -2
- package/dist/overtype.js +398 -40
- package/dist/overtype.js.map +2 -2
- package/dist/overtype.min.js +97 -74
- package/package.json +4 -2
- package/src/link-tooltip.js +16 -16
- package/src/overtype.d.ts +23 -1
- package/src/overtype.js +167 -13
- package/src/parser.js +276 -55
- package/src/styles.js +16 -8
- package/src/toolbar.js +63 -2
package/dist/overtype.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* OverType v1.2.
|
|
2
|
+
* OverType v1.2.4
|
|
3
3
|
* A lightweight markdown editor library with perfect WYSIWYG alignment
|
|
4
4
|
* @license MIT
|
|
5
5
|
* @author Demo User
|
|
@@ -155,6 +155,17 @@ var MarkdownParser = class {
|
|
|
155
155
|
html = html.replace(new RegExp("(?<!_)_(?!_)(.+?)(?<!_)_(?!_)", "g"), '<em><span class="syntax-marker">_</span>$1<span class="syntax-marker">_</span></em>');
|
|
156
156
|
return html;
|
|
157
157
|
}
|
|
158
|
+
/**
|
|
159
|
+
* Parse strikethrough text
|
|
160
|
+
* Supports both single (~) and double (~~) tildes, but rejects 3+ tildes
|
|
161
|
+
* @param {string} html - HTML with potential strikethrough markdown
|
|
162
|
+
* @returns {string} HTML with strikethrough styling
|
|
163
|
+
*/
|
|
164
|
+
static parseStrikethrough(html) {
|
|
165
|
+
html = html.replace(new RegExp("(?<!~)~~(?!~)(.+?)(?<!~)~~(?!~)", "g"), '<del><span class="syntax-marker">~~</span>$1<span class="syntax-marker">~~</span></del>');
|
|
166
|
+
html = html.replace(new RegExp("(?<!~)~(?!~)(.+?)(?<!~)~(?!~)", "g"), '<del><span class="syntax-marker">~</span>$1<span class="syntax-marker">~</span></del>');
|
|
167
|
+
return html;
|
|
168
|
+
}
|
|
158
169
|
/**
|
|
159
170
|
* Parse inline code
|
|
160
171
|
* @param {string} html - HTML with potential code markdown
|
|
@@ -217,6 +228,7 @@ var MarkdownParser = class {
|
|
|
217
228
|
sanctuaries.set(placeholder, match);
|
|
218
229
|
return placeholder;
|
|
219
230
|
});
|
|
231
|
+
html = this.parseStrikethrough(html);
|
|
220
232
|
html = this.parseBold(html);
|
|
221
233
|
html = this.parseItalic(html);
|
|
222
234
|
sanctuaries.forEach((content, placeholder) => {
|
|
@@ -351,6 +363,17 @@ var MarkdownParser = class {
|
|
|
351
363
|
container.insertBefore(currentList, child);
|
|
352
364
|
listType = newType;
|
|
353
365
|
}
|
|
366
|
+
const indentationNodes = [];
|
|
367
|
+
for (const node of child.childNodes) {
|
|
368
|
+
if (node.nodeType === 3 && node.textContent.match(/^\u00A0+$/)) {
|
|
369
|
+
indentationNodes.push(node.cloneNode(true));
|
|
370
|
+
} else if (node === listItem) {
|
|
371
|
+
break;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
indentationNodes.forEach((node) => {
|
|
375
|
+
listItem.insertBefore(node, listItem.firstChild);
|
|
376
|
+
});
|
|
354
377
|
currentList.appendChild(listItem);
|
|
355
378
|
child.remove();
|
|
356
379
|
} else {
|
|
@@ -368,15 +391,35 @@ var MarkdownParser = class {
|
|
|
368
391
|
static postProcessHTMLManual(html) {
|
|
369
392
|
let processed = html;
|
|
370
393
|
processed = processed.replace(/((?:<div>(?: )*<li class="bullet-list">.*?<\/li><\/div>\s*)+)/gs, (match) => {
|
|
371
|
-
const
|
|
372
|
-
if (
|
|
394
|
+
const divs = match.match(/<div>(?: )*<li class="bullet-list">.*?<\/li><\/div>/gs) || [];
|
|
395
|
+
if (divs.length > 0) {
|
|
396
|
+
const items = divs.map((div) => {
|
|
397
|
+
const indentMatch = div.match(/<div>((?: )*)<li/);
|
|
398
|
+
const listItemMatch = div.match(/<li class="bullet-list">.*?<\/li>/);
|
|
399
|
+
if (indentMatch && listItemMatch) {
|
|
400
|
+
const indentation = indentMatch[1];
|
|
401
|
+
const listItem = listItemMatch[0];
|
|
402
|
+
return listItem.replace(/<li class="bullet-list">/, `<li class="bullet-list">${indentation}`);
|
|
403
|
+
}
|
|
404
|
+
return listItemMatch ? listItemMatch[0] : "";
|
|
405
|
+
}).filter(Boolean);
|
|
373
406
|
return "<ul>" + items.join("") + "</ul>";
|
|
374
407
|
}
|
|
375
408
|
return match;
|
|
376
409
|
});
|
|
377
410
|
processed = processed.replace(/((?:<div>(?: )*<li class="ordered-list">.*?<\/li><\/div>\s*)+)/gs, (match) => {
|
|
378
|
-
const
|
|
379
|
-
if (
|
|
411
|
+
const divs = match.match(/<div>(?: )*<li class="ordered-list">.*?<\/li><\/div>/gs) || [];
|
|
412
|
+
if (divs.length > 0) {
|
|
413
|
+
const items = divs.map((div) => {
|
|
414
|
+
const indentMatch = div.match(/<div>((?: )*)<li/);
|
|
415
|
+
const listItemMatch = div.match(/<li class="ordered-list">.*?<\/li>/);
|
|
416
|
+
if (indentMatch && listItemMatch) {
|
|
417
|
+
const indentation = indentMatch[1];
|
|
418
|
+
const listItem = listItemMatch[0];
|
|
419
|
+
return listItem.replace(/<li class="ordered-list">/, `<li class="ordered-list">${indentation}`);
|
|
420
|
+
}
|
|
421
|
+
return listItemMatch ? listItemMatch[0] : "";
|
|
422
|
+
}).filter(Boolean);
|
|
380
423
|
return "<ol>" + items.join("") + "</ol>";
|
|
381
424
|
}
|
|
382
425
|
return match;
|
|
@@ -397,9 +440,147 @@ var MarkdownParser = class {
|
|
|
397
440
|
});
|
|
398
441
|
return processed;
|
|
399
442
|
}
|
|
443
|
+
/**
|
|
444
|
+
* Get list context at cursor position
|
|
445
|
+
* @param {string} text - Full text content
|
|
446
|
+
* @param {number} cursorPosition - Current cursor position
|
|
447
|
+
* @returns {Object} List context information
|
|
448
|
+
*/
|
|
449
|
+
static getListContext(text, cursorPosition) {
|
|
450
|
+
const lines = text.split("\n");
|
|
451
|
+
let currentPos = 0;
|
|
452
|
+
let lineIndex = 0;
|
|
453
|
+
let lineStart = 0;
|
|
454
|
+
for (let i = 0; i < lines.length; i++) {
|
|
455
|
+
const lineLength = lines[i].length;
|
|
456
|
+
if (currentPos + lineLength >= cursorPosition) {
|
|
457
|
+
lineIndex = i;
|
|
458
|
+
lineStart = currentPos;
|
|
459
|
+
break;
|
|
460
|
+
}
|
|
461
|
+
currentPos += lineLength + 1;
|
|
462
|
+
}
|
|
463
|
+
const currentLine = lines[lineIndex];
|
|
464
|
+
const lineEnd = lineStart + currentLine.length;
|
|
465
|
+
const checkboxMatch = currentLine.match(this.LIST_PATTERNS.checkbox);
|
|
466
|
+
if (checkboxMatch) {
|
|
467
|
+
return {
|
|
468
|
+
inList: true,
|
|
469
|
+
listType: "checkbox",
|
|
470
|
+
indent: checkboxMatch[1],
|
|
471
|
+
marker: "-",
|
|
472
|
+
checked: checkboxMatch[2] === "x",
|
|
473
|
+
content: checkboxMatch[3],
|
|
474
|
+
lineStart,
|
|
475
|
+
lineEnd,
|
|
476
|
+
markerEndPos: lineStart + checkboxMatch[1].length + checkboxMatch[2].length + 5
|
|
477
|
+
// indent + "- [ ] "
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
const bulletMatch = currentLine.match(this.LIST_PATTERNS.bullet);
|
|
481
|
+
if (bulletMatch) {
|
|
482
|
+
return {
|
|
483
|
+
inList: true,
|
|
484
|
+
listType: "bullet",
|
|
485
|
+
indent: bulletMatch[1],
|
|
486
|
+
marker: bulletMatch[2],
|
|
487
|
+
content: bulletMatch[3],
|
|
488
|
+
lineStart,
|
|
489
|
+
lineEnd,
|
|
490
|
+
markerEndPos: lineStart + bulletMatch[1].length + bulletMatch[2].length + 1
|
|
491
|
+
// indent + marker + space
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
const numberedMatch = currentLine.match(this.LIST_PATTERNS.numbered);
|
|
495
|
+
if (numberedMatch) {
|
|
496
|
+
return {
|
|
497
|
+
inList: true,
|
|
498
|
+
listType: "numbered",
|
|
499
|
+
indent: numberedMatch[1],
|
|
500
|
+
marker: parseInt(numberedMatch[2]),
|
|
501
|
+
content: numberedMatch[3],
|
|
502
|
+
lineStart,
|
|
503
|
+
lineEnd,
|
|
504
|
+
markerEndPos: lineStart + numberedMatch[1].length + numberedMatch[2].length + 2
|
|
505
|
+
// indent + number + ". "
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
return {
|
|
509
|
+
inList: false,
|
|
510
|
+
listType: null,
|
|
511
|
+
indent: "",
|
|
512
|
+
marker: null,
|
|
513
|
+
content: currentLine,
|
|
514
|
+
lineStart,
|
|
515
|
+
lineEnd,
|
|
516
|
+
markerEndPos: lineStart
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
520
|
+
* Create a new list item based on context
|
|
521
|
+
* @param {Object} context - List context from getListContext
|
|
522
|
+
* @returns {string} New list item text
|
|
523
|
+
*/
|
|
524
|
+
static createNewListItem(context) {
|
|
525
|
+
switch (context.listType) {
|
|
526
|
+
case "bullet":
|
|
527
|
+
return `${context.indent}${context.marker} `;
|
|
528
|
+
case "numbered":
|
|
529
|
+
return `${context.indent}${context.marker + 1}. `;
|
|
530
|
+
case "checkbox":
|
|
531
|
+
return `${context.indent}- [ ] `;
|
|
532
|
+
default:
|
|
533
|
+
return "";
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* Renumber all numbered lists in text
|
|
538
|
+
* @param {string} text - Text containing numbered lists
|
|
539
|
+
* @returns {string} Text with renumbered lists
|
|
540
|
+
*/
|
|
541
|
+
static renumberLists(text) {
|
|
542
|
+
const lines = text.split("\n");
|
|
543
|
+
const numbersByIndent = /* @__PURE__ */ new Map();
|
|
544
|
+
let inList = false;
|
|
545
|
+
const result = lines.map((line) => {
|
|
546
|
+
const match = line.match(this.LIST_PATTERNS.numbered);
|
|
547
|
+
if (match) {
|
|
548
|
+
const indent = match[1];
|
|
549
|
+
const indentLevel = indent.length;
|
|
550
|
+
const content = match[3];
|
|
551
|
+
if (!inList) {
|
|
552
|
+
numbersByIndent.clear();
|
|
553
|
+
}
|
|
554
|
+
const currentNumber = (numbersByIndent.get(indentLevel) || 0) + 1;
|
|
555
|
+
numbersByIndent.set(indentLevel, currentNumber);
|
|
556
|
+
for (const [level] of numbersByIndent) {
|
|
557
|
+
if (level > indentLevel) {
|
|
558
|
+
numbersByIndent.delete(level);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
inList = true;
|
|
562
|
+
return `${indent}${currentNumber}. ${content}`;
|
|
563
|
+
} else {
|
|
564
|
+
if (line.trim() === "" || !line.match(/^\s/)) {
|
|
565
|
+
inList = false;
|
|
566
|
+
numbersByIndent.clear();
|
|
567
|
+
}
|
|
568
|
+
return line;
|
|
569
|
+
}
|
|
570
|
+
});
|
|
571
|
+
return result.join("\n");
|
|
572
|
+
}
|
|
400
573
|
};
|
|
401
574
|
// Track link index for anchor naming
|
|
402
575
|
__publicField(MarkdownParser, "linkIndex", 0);
|
|
576
|
+
/**
|
|
577
|
+
* List pattern definitions
|
|
578
|
+
*/
|
|
579
|
+
__publicField(MarkdownParser, "LIST_PATTERNS", {
|
|
580
|
+
bullet: /^(\s*)([-*+])\s+(.*)$/,
|
|
581
|
+
numbered: /^(\s*)(\d+)\.\s+(.*)$/,
|
|
582
|
+
checkbox: /^(\s*)-\s+\[([ x])\]\s+(.*)$/
|
|
583
|
+
});
|
|
403
584
|
|
|
404
585
|
// node_modules/markdown-actions/dist/markdown-actions.esm.js
|
|
405
586
|
var __defProp2 = Object.defineProperty;
|
|
@@ -1780,6 +1961,14 @@ function generateStyles(options = {}) {
|
|
|
1780
1961
|
font-style: italic !important;
|
|
1781
1962
|
}
|
|
1782
1963
|
|
|
1964
|
+
/* Strikethrough text */
|
|
1965
|
+
.overtype-wrapper .overtype-preview del {
|
|
1966
|
+
color: var(--del, #ee964b) !important;
|
|
1967
|
+
text-decoration: line-through !important;
|
|
1968
|
+
text-decoration-color: var(--del, #ee964b) !important;
|
|
1969
|
+
text-decoration-thickness: 1px !important;
|
|
1970
|
+
}
|
|
1971
|
+
|
|
1783
1972
|
/* Inline code */
|
|
1784
1973
|
.overtype-wrapper .overtype-preview code {
|
|
1785
1974
|
background: var(--code-bg, rgba(244, 211, 94, 0.4)) !important;
|
|
@@ -1923,10 +2112,10 @@ function generateStyles(options = {}) {
|
|
|
1923
2112
|
height: 8px !important;
|
|
1924
2113
|
background: #4caf50 !important;
|
|
1925
2114
|
border-radius: 50% !important;
|
|
1926
|
-
animation: pulse 2s infinite !important;
|
|
2115
|
+
animation: overtype-pulse 2s infinite !important;
|
|
1927
2116
|
}
|
|
1928
2117
|
|
|
1929
|
-
@keyframes pulse {
|
|
2118
|
+
@keyframes overtype-pulse {
|
|
1930
2119
|
0%, 100% { opacity: 1; transform: scale(1); }
|
|
1931
2120
|
50% { opacity: 0.6; transform: scale(1.2); }
|
|
1932
2121
|
}
|
|
@@ -1934,19 +2123,19 @@ function generateStyles(options = {}) {
|
|
|
1934
2123
|
|
|
1935
2124
|
/* Toolbar Styles */
|
|
1936
2125
|
.overtype-toolbar {
|
|
1937
|
-
display: flex;
|
|
1938
|
-
align-items: center;
|
|
1939
|
-
gap: 4px;
|
|
2126
|
+
display: flex !important;
|
|
2127
|
+
align-items: center !important;
|
|
2128
|
+
gap: 4px !important;
|
|
1940
2129
|
padding: 8px !important; /* Override reset */
|
|
1941
2130
|
background: var(--toolbar-bg, var(--bg-primary, #f8f9fa)) !important; /* Override reset */
|
|
1942
2131
|
overflow-x: auto !important; /* Allow horizontal scrolling */
|
|
1943
2132
|
overflow-y: hidden !important; /* Hide vertical overflow */
|
|
1944
|
-
-webkit-overflow-scrolling: touch;
|
|
1945
|
-
flex-shrink: 0;
|
|
2133
|
+
-webkit-overflow-scrolling: touch !important;
|
|
2134
|
+
flex-shrink: 0 !important;
|
|
1946
2135
|
height: auto !important;
|
|
1947
2136
|
grid-row: 1 !important; /* Always first row in grid */
|
|
1948
2137
|
position: relative !important; /* Override reset */
|
|
1949
|
-
z-index: 100; /* Ensure toolbar is above wrapper */
|
|
2138
|
+
z-index: 100 !important; /* Ensure toolbar is above wrapper */
|
|
1950
2139
|
scrollbar-width: thin; /* Thin scrollbar on Firefox */
|
|
1951
2140
|
}
|
|
1952
2141
|
|
|
@@ -2328,20 +2517,67 @@ var eyeIcon = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke
|
|
|
2328
2517
|
|
|
2329
2518
|
// src/toolbar.js
|
|
2330
2519
|
var Toolbar = class {
|
|
2331
|
-
constructor(editor) {
|
|
2520
|
+
constructor(editor, buttonConfig = null) {
|
|
2332
2521
|
this.editor = editor;
|
|
2333
2522
|
this.container = null;
|
|
2334
2523
|
this.buttons = {};
|
|
2524
|
+
this.buttonConfig = buttonConfig;
|
|
2525
|
+
}
|
|
2526
|
+
/**
|
|
2527
|
+
* Check if cursor/selection is inside a markdown link
|
|
2528
|
+
* @param {HTMLTextAreaElement} textarea - The textarea element
|
|
2529
|
+
* @returns {boolean} True if inside a link
|
|
2530
|
+
*/
|
|
2531
|
+
isInsideLink(textarea) {
|
|
2532
|
+
const value = textarea.value;
|
|
2533
|
+
const start = textarea.selectionStart;
|
|
2534
|
+
const end = textarea.selectionEnd;
|
|
2535
|
+
let insideLink = false;
|
|
2536
|
+
let openBracket = -1;
|
|
2537
|
+
let closeBracket = -1;
|
|
2538
|
+
for (let i = start - 1; i >= 0; i--) {
|
|
2539
|
+
if (value[i] === "[") {
|
|
2540
|
+
openBracket = i;
|
|
2541
|
+
break;
|
|
2542
|
+
}
|
|
2543
|
+
if (value[i] === "\n") {
|
|
2544
|
+
break;
|
|
2545
|
+
}
|
|
2546
|
+
}
|
|
2547
|
+
if (openBracket >= 0) {
|
|
2548
|
+
for (let i = end; i < value.length - 1; i++) {
|
|
2549
|
+
if (value[i] === "]" && value[i + 1] === "(") {
|
|
2550
|
+
closeBracket = i;
|
|
2551
|
+
break;
|
|
2552
|
+
}
|
|
2553
|
+
if (value[i] === "\n") {
|
|
2554
|
+
break;
|
|
2555
|
+
}
|
|
2556
|
+
}
|
|
2557
|
+
}
|
|
2558
|
+
if (openBracket >= 0 && closeBracket >= 0) {
|
|
2559
|
+
for (let i = closeBracket + 2; i < value.length; i++) {
|
|
2560
|
+
if (value[i] === ")") {
|
|
2561
|
+
insideLink = true;
|
|
2562
|
+
break;
|
|
2563
|
+
}
|
|
2564
|
+
if (value[i] === "\n" || value[i] === " ") {
|
|
2565
|
+
break;
|
|
2566
|
+
}
|
|
2567
|
+
}
|
|
2568
|
+
}
|
|
2569
|
+
return insideLink;
|
|
2335
2570
|
}
|
|
2336
2571
|
/**
|
|
2337
2572
|
* Create and attach toolbar to editor
|
|
2338
2573
|
*/
|
|
2339
2574
|
create() {
|
|
2575
|
+
var _a;
|
|
2340
2576
|
this.container = document.createElement("div");
|
|
2341
2577
|
this.container.className = "overtype-toolbar";
|
|
2342
2578
|
this.container.setAttribute("role", "toolbar");
|
|
2343
2579
|
this.container.setAttribute("aria-label", "Text formatting");
|
|
2344
|
-
const buttonConfig = [
|
|
2580
|
+
const buttonConfig = (_a = this.buttonConfig) != null ? _a : [
|
|
2345
2581
|
{ name: "bold", icon: boldIcon, title: "Bold (Ctrl+B)", action: "toggleBold" },
|
|
2346
2582
|
{ name: "italic", icon: italicIcon, title: "Italic (Ctrl+I)", action: "toggleItalic" },
|
|
2347
2583
|
{ separator: true },
|
|
@@ -2435,6 +2671,9 @@ var Toolbar = class {
|
|
|
2435
2671
|
insertLink(textarea);
|
|
2436
2672
|
break;
|
|
2437
2673
|
case "toggleCode":
|
|
2674
|
+
if (this.isInsideLink(textarea)) {
|
|
2675
|
+
return;
|
|
2676
|
+
}
|
|
2438
2677
|
toggleCode(textarea);
|
|
2439
2678
|
break;
|
|
2440
2679
|
case "toggleBulletList":
|
|
@@ -2650,29 +2889,29 @@ var LinkTooltip = class {
|
|
|
2650
2889
|
position: absolute;
|
|
2651
2890
|
position-anchor: var(--target-anchor, --link-0);
|
|
2652
2891
|
position-area: block-end center;
|
|
2653
|
-
margin-top: 8px;
|
|
2892
|
+
margin-top: 8px !important;
|
|
2654
2893
|
|
|
2655
|
-
background: #333;
|
|
2656
|
-
color: white;
|
|
2657
|
-
padding: 6px 10px;
|
|
2658
|
-
border-radius: 16px;
|
|
2659
|
-
font-size: 12px;
|
|
2660
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
2661
|
-
display: none;
|
|
2662
|
-
z-index: 10000;
|
|
2663
|
-
cursor: pointer;
|
|
2664
|
-
box-shadow: 0 2px 8px rgba(0,0,0,0.3);
|
|
2665
|
-
max-width: 300px;
|
|
2666
|
-
white-space: nowrap;
|
|
2667
|
-
overflow: hidden;
|
|
2668
|
-
text-overflow: ellipsis;
|
|
2894
|
+
background: #333 !important;
|
|
2895
|
+
color: white !important;
|
|
2896
|
+
padding: 6px 10px !important;
|
|
2897
|
+
border-radius: 16px !important;
|
|
2898
|
+
font-size: 12px !important;
|
|
2899
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif !important;
|
|
2900
|
+
display: none !important;
|
|
2901
|
+
z-index: 10000 !important;
|
|
2902
|
+
cursor: pointer !important;
|
|
2903
|
+
box-shadow: 0 2px 8px rgba(0,0,0,0.3) !important;
|
|
2904
|
+
max-width: 300px !important;
|
|
2905
|
+
white-space: nowrap !important;
|
|
2906
|
+
overflow: hidden !important;
|
|
2907
|
+
text-overflow: ellipsis !important;
|
|
2669
2908
|
|
|
2670
2909
|
position-try: most-width block-end inline-end, flip-inline, block-start center;
|
|
2671
2910
|
position-visibility: anchors-visible;
|
|
2672
2911
|
}
|
|
2673
2912
|
|
|
2674
2913
|
.overtype-link-tooltip.visible {
|
|
2675
|
-
display: flex;
|
|
2914
|
+
display: flex !important;
|
|
2676
2915
|
}
|
|
2677
2916
|
}
|
|
2678
2917
|
`;
|
|
@@ -2820,7 +3059,8 @@ var _OverType = class _OverType {
|
|
|
2820
3059
|
this.shortcuts = new ShortcutsManager(this);
|
|
2821
3060
|
this.linkTooltip = new LinkTooltip(this);
|
|
2822
3061
|
if (this.options.toolbar) {
|
|
2823
|
-
this.toolbar
|
|
3062
|
+
const toolbarButtons = typeof this.options.toolbar === "object" ? this.options.toolbar.buttons : null;
|
|
3063
|
+
this.toolbar = new Toolbar(this, toolbarButtons);
|
|
2824
3064
|
this.toolbar.create();
|
|
2825
3065
|
this.textarea.addEventListener("selectionchange", () => {
|
|
2826
3066
|
this.toolbar.updateButtonStates();
|
|
@@ -2872,7 +3112,9 @@ var _OverType = class _OverType {
|
|
|
2872
3112
|
showActiveLineRaw: false,
|
|
2873
3113
|
showStats: false,
|
|
2874
3114
|
toolbar: false,
|
|
2875
|
-
statsFormatter: null
|
|
3115
|
+
statsFormatter: null,
|
|
3116
|
+
smartLists: true
|
|
3117
|
+
// Enable smart list continuation
|
|
2876
3118
|
};
|
|
2877
3119
|
const { theme, colors, ...cleanOptions } = options;
|
|
2878
3120
|
return {
|
|
@@ -3165,11 +3407,113 @@ var _OverType = class _OverType {
|
|
|
3165
3407
|
this.textarea.dispatchEvent(new Event("input", { bubbles: true }));
|
|
3166
3408
|
return;
|
|
3167
3409
|
}
|
|
3410
|
+
if (event.key === "Enter" && !event.shiftKey && !event.metaKey && !event.ctrlKey && this.options.smartLists) {
|
|
3411
|
+
if (this.handleSmartListContinuation()) {
|
|
3412
|
+
event.preventDefault();
|
|
3413
|
+
return;
|
|
3414
|
+
}
|
|
3415
|
+
}
|
|
3168
3416
|
const handled = this.shortcuts.handleKeydown(event);
|
|
3169
3417
|
if (!handled && this.options.onKeydown) {
|
|
3170
3418
|
this.options.onKeydown(event, this);
|
|
3171
3419
|
}
|
|
3172
3420
|
}
|
|
3421
|
+
/**
|
|
3422
|
+
* Handle smart list continuation
|
|
3423
|
+
* @returns {boolean} Whether the event was handled
|
|
3424
|
+
*/
|
|
3425
|
+
handleSmartListContinuation() {
|
|
3426
|
+
const textarea = this.textarea;
|
|
3427
|
+
const cursorPos = textarea.selectionStart;
|
|
3428
|
+
const context = MarkdownParser.getListContext(textarea.value, cursorPos);
|
|
3429
|
+
if (!context || !context.inList)
|
|
3430
|
+
return false;
|
|
3431
|
+
if (context.content.trim() === "" && cursorPos >= context.markerEndPos) {
|
|
3432
|
+
this.deleteListMarker(context);
|
|
3433
|
+
return true;
|
|
3434
|
+
}
|
|
3435
|
+
if (cursorPos > context.markerEndPos && cursorPos < context.lineEnd) {
|
|
3436
|
+
this.splitListItem(context, cursorPos);
|
|
3437
|
+
} else {
|
|
3438
|
+
this.insertNewListItem(context);
|
|
3439
|
+
}
|
|
3440
|
+
if (context.listType === "numbered") {
|
|
3441
|
+
this.scheduleNumberedListUpdate();
|
|
3442
|
+
}
|
|
3443
|
+
return true;
|
|
3444
|
+
}
|
|
3445
|
+
/**
|
|
3446
|
+
* Delete list marker and exit list
|
|
3447
|
+
* @private
|
|
3448
|
+
*/
|
|
3449
|
+
deleteListMarker(context) {
|
|
3450
|
+
this.textarea.setSelectionRange(context.lineStart, context.markerEndPos);
|
|
3451
|
+
document.execCommand("delete");
|
|
3452
|
+
this.textarea.dispatchEvent(new Event("input", { bubbles: true }));
|
|
3453
|
+
}
|
|
3454
|
+
/**
|
|
3455
|
+
* Insert new list item
|
|
3456
|
+
* @private
|
|
3457
|
+
*/
|
|
3458
|
+
insertNewListItem(context) {
|
|
3459
|
+
const newItem = MarkdownParser.createNewListItem(context);
|
|
3460
|
+
document.execCommand("insertText", false, "\n" + newItem);
|
|
3461
|
+
this.textarea.dispatchEvent(new Event("input", { bubbles: true }));
|
|
3462
|
+
}
|
|
3463
|
+
/**
|
|
3464
|
+
* Split list item at cursor position
|
|
3465
|
+
* @private
|
|
3466
|
+
*/
|
|
3467
|
+
splitListItem(context, cursorPos) {
|
|
3468
|
+
const textAfterCursor = context.content.substring(cursorPos - context.markerEndPos);
|
|
3469
|
+
this.textarea.setSelectionRange(cursorPos, context.lineEnd);
|
|
3470
|
+
document.execCommand("delete");
|
|
3471
|
+
const newItem = MarkdownParser.createNewListItem(context);
|
|
3472
|
+
document.execCommand("insertText", false, "\n" + newItem + textAfterCursor);
|
|
3473
|
+
const newCursorPos = this.textarea.selectionStart - textAfterCursor.length;
|
|
3474
|
+
this.textarea.setSelectionRange(newCursorPos, newCursorPos);
|
|
3475
|
+
this.textarea.dispatchEvent(new Event("input", { bubbles: true }));
|
|
3476
|
+
}
|
|
3477
|
+
/**
|
|
3478
|
+
* Schedule numbered list renumbering
|
|
3479
|
+
* @private
|
|
3480
|
+
*/
|
|
3481
|
+
scheduleNumberedListUpdate() {
|
|
3482
|
+
if (this.numberUpdateTimeout) {
|
|
3483
|
+
clearTimeout(this.numberUpdateTimeout);
|
|
3484
|
+
}
|
|
3485
|
+
this.numberUpdateTimeout = setTimeout(() => {
|
|
3486
|
+
this.updateNumberedLists();
|
|
3487
|
+
}, 10);
|
|
3488
|
+
}
|
|
3489
|
+
/**
|
|
3490
|
+
* Update/renumber all numbered lists
|
|
3491
|
+
* @private
|
|
3492
|
+
*/
|
|
3493
|
+
updateNumberedLists() {
|
|
3494
|
+
const value = this.textarea.value;
|
|
3495
|
+
const cursorPos = this.textarea.selectionStart;
|
|
3496
|
+
const newValue = MarkdownParser.renumberLists(value);
|
|
3497
|
+
if (newValue !== value) {
|
|
3498
|
+
let offset = 0;
|
|
3499
|
+
const oldLines = value.split("\n");
|
|
3500
|
+
const newLines = newValue.split("\n");
|
|
3501
|
+
let charCount = 0;
|
|
3502
|
+
for (let i = 0; i < oldLines.length && charCount < cursorPos; i++) {
|
|
3503
|
+
if (oldLines[i] !== newLines[i]) {
|
|
3504
|
+
const diff = newLines[i].length - oldLines[i].length;
|
|
3505
|
+
if (charCount + oldLines[i].length < cursorPos) {
|
|
3506
|
+
offset += diff;
|
|
3507
|
+
}
|
|
3508
|
+
}
|
|
3509
|
+
charCount += oldLines[i].length + 1;
|
|
3510
|
+
}
|
|
3511
|
+
this.textarea.value = newValue;
|
|
3512
|
+
const newCursorPos = cursorPos + offset;
|
|
3513
|
+
this.textarea.setSelectionRange(newCursorPos, newCursorPos);
|
|
3514
|
+
this.textarea.dispatchEvent(new Event("input", { bubbles: true }));
|
|
3515
|
+
}
|
|
3516
|
+
}
|
|
3173
3517
|
/**
|
|
3174
3518
|
* Handle scroll events
|
|
3175
3519
|
* @private
|
|
@@ -3198,24 +3542,36 @@ var _OverType = class _OverType {
|
|
|
3198
3542
|
}
|
|
3199
3543
|
/**
|
|
3200
3544
|
* Get the rendered HTML of the current content
|
|
3201
|
-
* @param {
|
|
3545
|
+
* @param {Object} options - Rendering options
|
|
3546
|
+
* @param {boolean} options.cleanHTML - If true, removes syntax markers and OverType-specific classes
|
|
3202
3547
|
* @returns {string} Rendered HTML
|
|
3203
3548
|
*/
|
|
3204
|
-
getRenderedHTML(
|
|
3549
|
+
getRenderedHTML(options = {}) {
|
|
3205
3550
|
const markdown = this.getValue();
|
|
3206
3551
|
let html = MarkdownParser.parse(markdown);
|
|
3207
|
-
if (
|
|
3208
|
-
html =
|
|
3552
|
+
if (options.cleanHTML) {
|
|
3553
|
+
html = html.replace(/<span class="syntax-marker[^"]*">.*?<\/span>/g, "");
|
|
3554
|
+
html = html.replace(/\sclass="(bullet-list|ordered-list|code-fence|hr-marker|blockquote|url-part)"/g, "");
|
|
3555
|
+
html = html.replace(/\sclass=""/g, "");
|
|
3209
3556
|
}
|
|
3210
3557
|
return html;
|
|
3211
3558
|
}
|
|
3212
3559
|
/**
|
|
3213
3560
|
* Get the current preview element's HTML
|
|
3561
|
+
* This includes all syntax markers and OverType styling
|
|
3214
3562
|
* @returns {string} Current preview HTML (as displayed)
|
|
3215
3563
|
*/
|
|
3216
3564
|
getPreviewHTML() {
|
|
3217
3565
|
return this.preview.innerHTML;
|
|
3218
3566
|
}
|
|
3567
|
+
/**
|
|
3568
|
+
* Get clean HTML without any OverType-specific markup
|
|
3569
|
+
* Useful for exporting to other formats or storage
|
|
3570
|
+
* @returns {string} Clean HTML suitable for export
|
|
3571
|
+
*/
|
|
3572
|
+
getCleanHTML() {
|
|
3573
|
+
return this.getRenderedHTML({ cleanHTML: true });
|
|
3574
|
+
}
|
|
3219
3575
|
/**
|
|
3220
3576
|
* Focus the editor
|
|
3221
3577
|
*/
|
|
@@ -3534,9 +3890,6 @@ OverType.ShortcutsManager = ShortcutsManager;
|
|
|
3534
3890
|
OverType.themes = { solar, cave: getTheme("cave") };
|
|
3535
3891
|
OverType.getTheme = getTheme;
|
|
3536
3892
|
OverType.currentTheme = solar;
|
|
3537
|
-
if (typeof window !== "undefined" && typeof window.document !== "undefined") {
|
|
3538
|
-
window.OverType = OverType;
|
|
3539
|
-
}
|
|
3540
3893
|
var overtype_default = OverType;
|
|
3541
3894
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3542
3895
|
0 && (module.exports = {
|