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.esm.js
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
|
|
@@ -131,6 +131,17 @@ var MarkdownParser = class {
|
|
|
131
131
|
html = html.replace(new RegExp("(?<!_)_(?!_)(.+?)(?<!_)_(?!_)", "g"), '<em><span class="syntax-marker">_</span>$1<span class="syntax-marker">_</span></em>');
|
|
132
132
|
return html;
|
|
133
133
|
}
|
|
134
|
+
/**
|
|
135
|
+
* Parse strikethrough text
|
|
136
|
+
* Supports both single (~) and double (~~) tildes, but rejects 3+ tildes
|
|
137
|
+
* @param {string} html - HTML with potential strikethrough markdown
|
|
138
|
+
* @returns {string} HTML with strikethrough styling
|
|
139
|
+
*/
|
|
140
|
+
static parseStrikethrough(html) {
|
|
141
|
+
html = html.replace(new RegExp("(?<!~)~~(?!~)(.+?)(?<!~)~~(?!~)", "g"), '<del><span class="syntax-marker">~~</span>$1<span class="syntax-marker">~~</span></del>');
|
|
142
|
+
html = html.replace(new RegExp("(?<!~)~(?!~)(.+?)(?<!~)~(?!~)", "g"), '<del><span class="syntax-marker">~</span>$1<span class="syntax-marker">~</span></del>');
|
|
143
|
+
return html;
|
|
144
|
+
}
|
|
134
145
|
/**
|
|
135
146
|
* Parse inline code
|
|
136
147
|
* @param {string} html - HTML with potential code markdown
|
|
@@ -193,6 +204,7 @@ var MarkdownParser = class {
|
|
|
193
204
|
sanctuaries.set(placeholder, match);
|
|
194
205
|
return placeholder;
|
|
195
206
|
});
|
|
207
|
+
html = this.parseStrikethrough(html);
|
|
196
208
|
html = this.parseBold(html);
|
|
197
209
|
html = this.parseItalic(html);
|
|
198
210
|
sanctuaries.forEach((content, placeholder) => {
|
|
@@ -327,6 +339,17 @@ var MarkdownParser = class {
|
|
|
327
339
|
container.insertBefore(currentList, child);
|
|
328
340
|
listType = newType;
|
|
329
341
|
}
|
|
342
|
+
const indentationNodes = [];
|
|
343
|
+
for (const node of child.childNodes) {
|
|
344
|
+
if (node.nodeType === 3 && node.textContent.match(/^\u00A0+$/)) {
|
|
345
|
+
indentationNodes.push(node.cloneNode(true));
|
|
346
|
+
} else if (node === listItem) {
|
|
347
|
+
break;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
indentationNodes.forEach((node) => {
|
|
351
|
+
listItem.insertBefore(node, listItem.firstChild);
|
|
352
|
+
});
|
|
330
353
|
currentList.appendChild(listItem);
|
|
331
354
|
child.remove();
|
|
332
355
|
} else {
|
|
@@ -344,15 +367,35 @@ var MarkdownParser = class {
|
|
|
344
367
|
static postProcessHTMLManual(html) {
|
|
345
368
|
let processed = html;
|
|
346
369
|
processed = processed.replace(/((?:<div>(?: )*<li class="bullet-list">.*?<\/li><\/div>\s*)+)/gs, (match) => {
|
|
347
|
-
const
|
|
348
|
-
if (
|
|
370
|
+
const divs = match.match(/<div>(?: )*<li class="bullet-list">.*?<\/li><\/div>/gs) || [];
|
|
371
|
+
if (divs.length > 0) {
|
|
372
|
+
const items = divs.map((div) => {
|
|
373
|
+
const indentMatch = div.match(/<div>((?: )*)<li/);
|
|
374
|
+
const listItemMatch = div.match(/<li class="bullet-list">.*?<\/li>/);
|
|
375
|
+
if (indentMatch && listItemMatch) {
|
|
376
|
+
const indentation = indentMatch[1];
|
|
377
|
+
const listItem = listItemMatch[0];
|
|
378
|
+
return listItem.replace(/<li class="bullet-list">/, `<li class="bullet-list">${indentation}`);
|
|
379
|
+
}
|
|
380
|
+
return listItemMatch ? listItemMatch[0] : "";
|
|
381
|
+
}).filter(Boolean);
|
|
349
382
|
return "<ul>" + items.join("") + "</ul>";
|
|
350
383
|
}
|
|
351
384
|
return match;
|
|
352
385
|
});
|
|
353
386
|
processed = processed.replace(/((?:<div>(?: )*<li class="ordered-list">.*?<\/li><\/div>\s*)+)/gs, (match) => {
|
|
354
|
-
const
|
|
355
|
-
if (
|
|
387
|
+
const divs = match.match(/<div>(?: )*<li class="ordered-list">.*?<\/li><\/div>/gs) || [];
|
|
388
|
+
if (divs.length > 0) {
|
|
389
|
+
const items = divs.map((div) => {
|
|
390
|
+
const indentMatch = div.match(/<div>((?: )*)<li/);
|
|
391
|
+
const listItemMatch = div.match(/<li class="ordered-list">.*?<\/li>/);
|
|
392
|
+
if (indentMatch && listItemMatch) {
|
|
393
|
+
const indentation = indentMatch[1];
|
|
394
|
+
const listItem = listItemMatch[0];
|
|
395
|
+
return listItem.replace(/<li class="ordered-list">/, `<li class="ordered-list">${indentation}`);
|
|
396
|
+
}
|
|
397
|
+
return listItemMatch ? listItemMatch[0] : "";
|
|
398
|
+
}).filter(Boolean);
|
|
356
399
|
return "<ol>" + items.join("") + "</ol>";
|
|
357
400
|
}
|
|
358
401
|
return match;
|
|
@@ -373,9 +416,147 @@ var MarkdownParser = class {
|
|
|
373
416
|
});
|
|
374
417
|
return processed;
|
|
375
418
|
}
|
|
419
|
+
/**
|
|
420
|
+
* Get list context at cursor position
|
|
421
|
+
* @param {string} text - Full text content
|
|
422
|
+
* @param {number} cursorPosition - Current cursor position
|
|
423
|
+
* @returns {Object} List context information
|
|
424
|
+
*/
|
|
425
|
+
static getListContext(text, cursorPosition) {
|
|
426
|
+
const lines = text.split("\n");
|
|
427
|
+
let currentPos = 0;
|
|
428
|
+
let lineIndex = 0;
|
|
429
|
+
let lineStart = 0;
|
|
430
|
+
for (let i = 0; i < lines.length; i++) {
|
|
431
|
+
const lineLength = lines[i].length;
|
|
432
|
+
if (currentPos + lineLength >= cursorPosition) {
|
|
433
|
+
lineIndex = i;
|
|
434
|
+
lineStart = currentPos;
|
|
435
|
+
break;
|
|
436
|
+
}
|
|
437
|
+
currentPos += lineLength + 1;
|
|
438
|
+
}
|
|
439
|
+
const currentLine = lines[lineIndex];
|
|
440
|
+
const lineEnd = lineStart + currentLine.length;
|
|
441
|
+
const checkboxMatch = currentLine.match(this.LIST_PATTERNS.checkbox);
|
|
442
|
+
if (checkboxMatch) {
|
|
443
|
+
return {
|
|
444
|
+
inList: true,
|
|
445
|
+
listType: "checkbox",
|
|
446
|
+
indent: checkboxMatch[1],
|
|
447
|
+
marker: "-",
|
|
448
|
+
checked: checkboxMatch[2] === "x",
|
|
449
|
+
content: checkboxMatch[3],
|
|
450
|
+
lineStart,
|
|
451
|
+
lineEnd,
|
|
452
|
+
markerEndPos: lineStart + checkboxMatch[1].length + checkboxMatch[2].length + 5
|
|
453
|
+
// indent + "- [ ] "
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
const bulletMatch = currentLine.match(this.LIST_PATTERNS.bullet);
|
|
457
|
+
if (bulletMatch) {
|
|
458
|
+
return {
|
|
459
|
+
inList: true,
|
|
460
|
+
listType: "bullet",
|
|
461
|
+
indent: bulletMatch[1],
|
|
462
|
+
marker: bulletMatch[2],
|
|
463
|
+
content: bulletMatch[3],
|
|
464
|
+
lineStart,
|
|
465
|
+
lineEnd,
|
|
466
|
+
markerEndPos: lineStart + bulletMatch[1].length + bulletMatch[2].length + 1
|
|
467
|
+
// indent + marker + space
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
const numberedMatch = currentLine.match(this.LIST_PATTERNS.numbered);
|
|
471
|
+
if (numberedMatch) {
|
|
472
|
+
return {
|
|
473
|
+
inList: true,
|
|
474
|
+
listType: "numbered",
|
|
475
|
+
indent: numberedMatch[1],
|
|
476
|
+
marker: parseInt(numberedMatch[2]),
|
|
477
|
+
content: numberedMatch[3],
|
|
478
|
+
lineStart,
|
|
479
|
+
lineEnd,
|
|
480
|
+
markerEndPos: lineStart + numberedMatch[1].length + numberedMatch[2].length + 2
|
|
481
|
+
// indent + number + ". "
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
return {
|
|
485
|
+
inList: false,
|
|
486
|
+
listType: null,
|
|
487
|
+
indent: "",
|
|
488
|
+
marker: null,
|
|
489
|
+
content: currentLine,
|
|
490
|
+
lineStart,
|
|
491
|
+
lineEnd,
|
|
492
|
+
markerEndPos: lineStart
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* Create a new list item based on context
|
|
497
|
+
* @param {Object} context - List context from getListContext
|
|
498
|
+
* @returns {string} New list item text
|
|
499
|
+
*/
|
|
500
|
+
static createNewListItem(context) {
|
|
501
|
+
switch (context.listType) {
|
|
502
|
+
case "bullet":
|
|
503
|
+
return `${context.indent}${context.marker} `;
|
|
504
|
+
case "numbered":
|
|
505
|
+
return `${context.indent}${context.marker + 1}. `;
|
|
506
|
+
case "checkbox":
|
|
507
|
+
return `${context.indent}- [ ] `;
|
|
508
|
+
default:
|
|
509
|
+
return "";
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* Renumber all numbered lists in text
|
|
514
|
+
* @param {string} text - Text containing numbered lists
|
|
515
|
+
* @returns {string} Text with renumbered lists
|
|
516
|
+
*/
|
|
517
|
+
static renumberLists(text) {
|
|
518
|
+
const lines = text.split("\n");
|
|
519
|
+
const numbersByIndent = /* @__PURE__ */ new Map();
|
|
520
|
+
let inList = false;
|
|
521
|
+
const result = lines.map((line) => {
|
|
522
|
+
const match = line.match(this.LIST_PATTERNS.numbered);
|
|
523
|
+
if (match) {
|
|
524
|
+
const indent = match[1];
|
|
525
|
+
const indentLevel = indent.length;
|
|
526
|
+
const content = match[3];
|
|
527
|
+
if (!inList) {
|
|
528
|
+
numbersByIndent.clear();
|
|
529
|
+
}
|
|
530
|
+
const currentNumber = (numbersByIndent.get(indentLevel) || 0) + 1;
|
|
531
|
+
numbersByIndent.set(indentLevel, currentNumber);
|
|
532
|
+
for (const [level] of numbersByIndent) {
|
|
533
|
+
if (level > indentLevel) {
|
|
534
|
+
numbersByIndent.delete(level);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
inList = true;
|
|
538
|
+
return `${indent}${currentNumber}. ${content}`;
|
|
539
|
+
} else {
|
|
540
|
+
if (line.trim() === "" || !line.match(/^\s/)) {
|
|
541
|
+
inList = false;
|
|
542
|
+
numbersByIndent.clear();
|
|
543
|
+
}
|
|
544
|
+
return line;
|
|
545
|
+
}
|
|
546
|
+
});
|
|
547
|
+
return result.join("\n");
|
|
548
|
+
}
|
|
376
549
|
};
|
|
377
550
|
// Track link index for anchor naming
|
|
378
551
|
__publicField(MarkdownParser, "linkIndex", 0);
|
|
552
|
+
/**
|
|
553
|
+
* List pattern definitions
|
|
554
|
+
*/
|
|
555
|
+
__publicField(MarkdownParser, "LIST_PATTERNS", {
|
|
556
|
+
bullet: /^(\s*)([-*+])\s+(.*)$/,
|
|
557
|
+
numbered: /^(\s*)(\d+)\.\s+(.*)$/,
|
|
558
|
+
checkbox: /^(\s*)-\s+\[([ x])\]\s+(.*)$/
|
|
559
|
+
});
|
|
379
560
|
|
|
380
561
|
// node_modules/markdown-actions/dist/markdown-actions.esm.js
|
|
381
562
|
var __defProp2 = Object.defineProperty;
|
|
@@ -1756,6 +1937,14 @@ function generateStyles(options = {}) {
|
|
|
1756
1937
|
font-style: italic !important;
|
|
1757
1938
|
}
|
|
1758
1939
|
|
|
1940
|
+
/* Strikethrough text */
|
|
1941
|
+
.overtype-wrapper .overtype-preview del {
|
|
1942
|
+
color: var(--del, #ee964b) !important;
|
|
1943
|
+
text-decoration: line-through !important;
|
|
1944
|
+
text-decoration-color: var(--del, #ee964b) !important;
|
|
1945
|
+
text-decoration-thickness: 1px !important;
|
|
1946
|
+
}
|
|
1947
|
+
|
|
1759
1948
|
/* Inline code */
|
|
1760
1949
|
.overtype-wrapper .overtype-preview code {
|
|
1761
1950
|
background: var(--code-bg, rgba(244, 211, 94, 0.4)) !important;
|
|
@@ -1899,10 +2088,10 @@ function generateStyles(options = {}) {
|
|
|
1899
2088
|
height: 8px !important;
|
|
1900
2089
|
background: #4caf50 !important;
|
|
1901
2090
|
border-radius: 50% !important;
|
|
1902
|
-
animation: pulse 2s infinite !important;
|
|
2091
|
+
animation: overtype-pulse 2s infinite !important;
|
|
1903
2092
|
}
|
|
1904
2093
|
|
|
1905
|
-
@keyframes pulse {
|
|
2094
|
+
@keyframes overtype-pulse {
|
|
1906
2095
|
0%, 100% { opacity: 1; transform: scale(1); }
|
|
1907
2096
|
50% { opacity: 0.6; transform: scale(1.2); }
|
|
1908
2097
|
}
|
|
@@ -1910,19 +2099,19 @@ function generateStyles(options = {}) {
|
|
|
1910
2099
|
|
|
1911
2100
|
/* Toolbar Styles */
|
|
1912
2101
|
.overtype-toolbar {
|
|
1913
|
-
display: flex;
|
|
1914
|
-
align-items: center;
|
|
1915
|
-
gap: 4px;
|
|
2102
|
+
display: flex !important;
|
|
2103
|
+
align-items: center !important;
|
|
2104
|
+
gap: 4px !important;
|
|
1916
2105
|
padding: 8px !important; /* Override reset */
|
|
1917
2106
|
background: var(--toolbar-bg, var(--bg-primary, #f8f9fa)) !important; /* Override reset */
|
|
1918
2107
|
overflow-x: auto !important; /* Allow horizontal scrolling */
|
|
1919
2108
|
overflow-y: hidden !important; /* Hide vertical overflow */
|
|
1920
|
-
-webkit-overflow-scrolling: touch;
|
|
1921
|
-
flex-shrink: 0;
|
|
2109
|
+
-webkit-overflow-scrolling: touch !important;
|
|
2110
|
+
flex-shrink: 0 !important;
|
|
1922
2111
|
height: auto !important;
|
|
1923
2112
|
grid-row: 1 !important; /* Always first row in grid */
|
|
1924
2113
|
position: relative !important; /* Override reset */
|
|
1925
|
-
z-index: 100; /* Ensure toolbar is above wrapper */
|
|
2114
|
+
z-index: 100 !important; /* Ensure toolbar is above wrapper */
|
|
1926
2115
|
scrollbar-width: thin; /* Thin scrollbar on Firefox */
|
|
1927
2116
|
}
|
|
1928
2117
|
|
|
@@ -2304,20 +2493,67 @@ var eyeIcon = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke
|
|
|
2304
2493
|
|
|
2305
2494
|
// src/toolbar.js
|
|
2306
2495
|
var Toolbar = class {
|
|
2307
|
-
constructor(editor) {
|
|
2496
|
+
constructor(editor, buttonConfig = null) {
|
|
2308
2497
|
this.editor = editor;
|
|
2309
2498
|
this.container = null;
|
|
2310
2499
|
this.buttons = {};
|
|
2500
|
+
this.buttonConfig = buttonConfig;
|
|
2501
|
+
}
|
|
2502
|
+
/**
|
|
2503
|
+
* Check if cursor/selection is inside a markdown link
|
|
2504
|
+
* @param {HTMLTextAreaElement} textarea - The textarea element
|
|
2505
|
+
* @returns {boolean} True if inside a link
|
|
2506
|
+
*/
|
|
2507
|
+
isInsideLink(textarea) {
|
|
2508
|
+
const value = textarea.value;
|
|
2509
|
+
const start = textarea.selectionStart;
|
|
2510
|
+
const end = textarea.selectionEnd;
|
|
2511
|
+
let insideLink = false;
|
|
2512
|
+
let openBracket = -1;
|
|
2513
|
+
let closeBracket = -1;
|
|
2514
|
+
for (let i = start - 1; i >= 0; i--) {
|
|
2515
|
+
if (value[i] === "[") {
|
|
2516
|
+
openBracket = i;
|
|
2517
|
+
break;
|
|
2518
|
+
}
|
|
2519
|
+
if (value[i] === "\n") {
|
|
2520
|
+
break;
|
|
2521
|
+
}
|
|
2522
|
+
}
|
|
2523
|
+
if (openBracket >= 0) {
|
|
2524
|
+
for (let i = end; i < value.length - 1; i++) {
|
|
2525
|
+
if (value[i] === "]" && value[i + 1] === "(") {
|
|
2526
|
+
closeBracket = i;
|
|
2527
|
+
break;
|
|
2528
|
+
}
|
|
2529
|
+
if (value[i] === "\n") {
|
|
2530
|
+
break;
|
|
2531
|
+
}
|
|
2532
|
+
}
|
|
2533
|
+
}
|
|
2534
|
+
if (openBracket >= 0 && closeBracket >= 0) {
|
|
2535
|
+
for (let i = closeBracket + 2; i < value.length; i++) {
|
|
2536
|
+
if (value[i] === ")") {
|
|
2537
|
+
insideLink = true;
|
|
2538
|
+
break;
|
|
2539
|
+
}
|
|
2540
|
+
if (value[i] === "\n" || value[i] === " ") {
|
|
2541
|
+
break;
|
|
2542
|
+
}
|
|
2543
|
+
}
|
|
2544
|
+
}
|
|
2545
|
+
return insideLink;
|
|
2311
2546
|
}
|
|
2312
2547
|
/**
|
|
2313
2548
|
* Create and attach toolbar to editor
|
|
2314
2549
|
*/
|
|
2315
2550
|
create() {
|
|
2551
|
+
var _a;
|
|
2316
2552
|
this.container = document.createElement("div");
|
|
2317
2553
|
this.container.className = "overtype-toolbar";
|
|
2318
2554
|
this.container.setAttribute("role", "toolbar");
|
|
2319
2555
|
this.container.setAttribute("aria-label", "Text formatting");
|
|
2320
|
-
const buttonConfig = [
|
|
2556
|
+
const buttonConfig = (_a = this.buttonConfig) != null ? _a : [
|
|
2321
2557
|
{ name: "bold", icon: boldIcon, title: "Bold (Ctrl+B)", action: "toggleBold" },
|
|
2322
2558
|
{ name: "italic", icon: italicIcon, title: "Italic (Ctrl+I)", action: "toggleItalic" },
|
|
2323
2559
|
{ separator: true },
|
|
@@ -2411,6 +2647,9 @@ var Toolbar = class {
|
|
|
2411
2647
|
insertLink(textarea);
|
|
2412
2648
|
break;
|
|
2413
2649
|
case "toggleCode":
|
|
2650
|
+
if (this.isInsideLink(textarea)) {
|
|
2651
|
+
return;
|
|
2652
|
+
}
|
|
2414
2653
|
toggleCode(textarea);
|
|
2415
2654
|
break;
|
|
2416
2655
|
case "toggleBulletList":
|
|
@@ -2626,29 +2865,29 @@ var LinkTooltip = class {
|
|
|
2626
2865
|
position: absolute;
|
|
2627
2866
|
position-anchor: var(--target-anchor, --link-0);
|
|
2628
2867
|
position-area: block-end center;
|
|
2629
|
-
margin-top: 8px;
|
|
2868
|
+
margin-top: 8px !important;
|
|
2630
2869
|
|
|
2631
|
-
background: #333;
|
|
2632
|
-
color: white;
|
|
2633
|
-
padding: 6px 10px;
|
|
2634
|
-
border-radius: 16px;
|
|
2635
|
-
font-size: 12px;
|
|
2636
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
2637
|
-
display: none;
|
|
2638
|
-
z-index: 10000;
|
|
2639
|
-
cursor: pointer;
|
|
2640
|
-
box-shadow: 0 2px 8px rgba(0,0,0,0.3);
|
|
2641
|
-
max-width: 300px;
|
|
2642
|
-
white-space: nowrap;
|
|
2643
|
-
overflow: hidden;
|
|
2644
|
-
text-overflow: ellipsis;
|
|
2870
|
+
background: #333 !important;
|
|
2871
|
+
color: white !important;
|
|
2872
|
+
padding: 6px 10px !important;
|
|
2873
|
+
border-radius: 16px !important;
|
|
2874
|
+
font-size: 12px !important;
|
|
2875
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif !important;
|
|
2876
|
+
display: none !important;
|
|
2877
|
+
z-index: 10000 !important;
|
|
2878
|
+
cursor: pointer !important;
|
|
2879
|
+
box-shadow: 0 2px 8px rgba(0,0,0,0.3) !important;
|
|
2880
|
+
max-width: 300px !important;
|
|
2881
|
+
white-space: nowrap !important;
|
|
2882
|
+
overflow: hidden !important;
|
|
2883
|
+
text-overflow: ellipsis !important;
|
|
2645
2884
|
|
|
2646
2885
|
position-try: most-width block-end inline-end, flip-inline, block-start center;
|
|
2647
2886
|
position-visibility: anchors-visible;
|
|
2648
2887
|
}
|
|
2649
2888
|
|
|
2650
2889
|
.overtype-link-tooltip.visible {
|
|
2651
|
-
display: flex;
|
|
2890
|
+
display: flex !important;
|
|
2652
2891
|
}
|
|
2653
2892
|
}
|
|
2654
2893
|
`;
|
|
@@ -2796,7 +3035,8 @@ var _OverType = class _OverType {
|
|
|
2796
3035
|
this.shortcuts = new ShortcutsManager(this);
|
|
2797
3036
|
this.linkTooltip = new LinkTooltip(this);
|
|
2798
3037
|
if (this.options.toolbar) {
|
|
2799
|
-
this.toolbar
|
|
3038
|
+
const toolbarButtons = typeof this.options.toolbar === "object" ? this.options.toolbar.buttons : null;
|
|
3039
|
+
this.toolbar = new Toolbar(this, toolbarButtons);
|
|
2800
3040
|
this.toolbar.create();
|
|
2801
3041
|
this.textarea.addEventListener("selectionchange", () => {
|
|
2802
3042
|
this.toolbar.updateButtonStates();
|
|
@@ -2848,7 +3088,9 @@ var _OverType = class _OverType {
|
|
|
2848
3088
|
showActiveLineRaw: false,
|
|
2849
3089
|
showStats: false,
|
|
2850
3090
|
toolbar: false,
|
|
2851
|
-
statsFormatter: null
|
|
3091
|
+
statsFormatter: null,
|
|
3092
|
+
smartLists: true
|
|
3093
|
+
// Enable smart list continuation
|
|
2852
3094
|
};
|
|
2853
3095
|
const { theme, colors, ...cleanOptions } = options;
|
|
2854
3096
|
return {
|
|
@@ -3141,11 +3383,113 @@ var _OverType = class _OverType {
|
|
|
3141
3383
|
this.textarea.dispatchEvent(new Event("input", { bubbles: true }));
|
|
3142
3384
|
return;
|
|
3143
3385
|
}
|
|
3386
|
+
if (event.key === "Enter" && !event.shiftKey && !event.metaKey && !event.ctrlKey && this.options.smartLists) {
|
|
3387
|
+
if (this.handleSmartListContinuation()) {
|
|
3388
|
+
event.preventDefault();
|
|
3389
|
+
return;
|
|
3390
|
+
}
|
|
3391
|
+
}
|
|
3144
3392
|
const handled = this.shortcuts.handleKeydown(event);
|
|
3145
3393
|
if (!handled && this.options.onKeydown) {
|
|
3146
3394
|
this.options.onKeydown(event, this);
|
|
3147
3395
|
}
|
|
3148
3396
|
}
|
|
3397
|
+
/**
|
|
3398
|
+
* Handle smart list continuation
|
|
3399
|
+
* @returns {boolean} Whether the event was handled
|
|
3400
|
+
*/
|
|
3401
|
+
handleSmartListContinuation() {
|
|
3402
|
+
const textarea = this.textarea;
|
|
3403
|
+
const cursorPos = textarea.selectionStart;
|
|
3404
|
+
const context = MarkdownParser.getListContext(textarea.value, cursorPos);
|
|
3405
|
+
if (!context || !context.inList)
|
|
3406
|
+
return false;
|
|
3407
|
+
if (context.content.trim() === "" && cursorPos >= context.markerEndPos) {
|
|
3408
|
+
this.deleteListMarker(context);
|
|
3409
|
+
return true;
|
|
3410
|
+
}
|
|
3411
|
+
if (cursorPos > context.markerEndPos && cursorPos < context.lineEnd) {
|
|
3412
|
+
this.splitListItem(context, cursorPos);
|
|
3413
|
+
} else {
|
|
3414
|
+
this.insertNewListItem(context);
|
|
3415
|
+
}
|
|
3416
|
+
if (context.listType === "numbered") {
|
|
3417
|
+
this.scheduleNumberedListUpdate();
|
|
3418
|
+
}
|
|
3419
|
+
return true;
|
|
3420
|
+
}
|
|
3421
|
+
/**
|
|
3422
|
+
* Delete list marker and exit list
|
|
3423
|
+
* @private
|
|
3424
|
+
*/
|
|
3425
|
+
deleteListMarker(context) {
|
|
3426
|
+
this.textarea.setSelectionRange(context.lineStart, context.markerEndPos);
|
|
3427
|
+
document.execCommand("delete");
|
|
3428
|
+
this.textarea.dispatchEvent(new Event("input", { bubbles: true }));
|
|
3429
|
+
}
|
|
3430
|
+
/**
|
|
3431
|
+
* Insert new list item
|
|
3432
|
+
* @private
|
|
3433
|
+
*/
|
|
3434
|
+
insertNewListItem(context) {
|
|
3435
|
+
const newItem = MarkdownParser.createNewListItem(context);
|
|
3436
|
+
document.execCommand("insertText", false, "\n" + newItem);
|
|
3437
|
+
this.textarea.dispatchEvent(new Event("input", { bubbles: true }));
|
|
3438
|
+
}
|
|
3439
|
+
/**
|
|
3440
|
+
* Split list item at cursor position
|
|
3441
|
+
* @private
|
|
3442
|
+
*/
|
|
3443
|
+
splitListItem(context, cursorPos) {
|
|
3444
|
+
const textAfterCursor = context.content.substring(cursorPos - context.markerEndPos);
|
|
3445
|
+
this.textarea.setSelectionRange(cursorPos, context.lineEnd);
|
|
3446
|
+
document.execCommand("delete");
|
|
3447
|
+
const newItem = MarkdownParser.createNewListItem(context);
|
|
3448
|
+
document.execCommand("insertText", false, "\n" + newItem + textAfterCursor);
|
|
3449
|
+
const newCursorPos = this.textarea.selectionStart - textAfterCursor.length;
|
|
3450
|
+
this.textarea.setSelectionRange(newCursorPos, newCursorPos);
|
|
3451
|
+
this.textarea.dispatchEvent(new Event("input", { bubbles: true }));
|
|
3452
|
+
}
|
|
3453
|
+
/**
|
|
3454
|
+
* Schedule numbered list renumbering
|
|
3455
|
+
* @private
|
|
3456
|
+
*/
|
|
3457
|
+
scheduleNumberedListUpdate() {
|
|
3458
|
+
if (this.numberUpdateTimeout) {
|
|
3459
|
+
clearTimeout(this.numberUpdateTimeout);
|
|
3460
|
+
}
|
|
3461
|
+
this.numberUpdateTimeout = setTimeout(() => {
|
|
3462
|
+
this.updateNumberedLists();
|
|
3463
|
+
}, 10);
|
|
3464
|
+
}
|
|
3465
|
+
/**
|
|
3466
|
+
* Update/renumber all numbered lists
|
|
3467
|
+
* @private
|
|
3468
|
+
*/
|
|
3469
|
+
updateNumberedLists() {
|
|
3470
|
+
const value = this.textarea.value;
|
|
3471
|
+
const cursorPos = this.textarea.selectionStart;
|
|
3472
|
+
const newValue = MarkdownParser.renumberLists(value);
|
|
3473
|
+
if (newValue !== value) {
|
|
3474
|
+
let offset = 0;
|
|
3475
|
+
const oldLines = value.split("\n");
|
|
3476
|
+
const newLines = newValue.split("\n");
|
|
3477
|
+
let charCount = 0;
|
|
3478
|
+
for (let i = 0; i < oldLines.length && charCount < cursorPos; i++) {
|
|
3479
|
+
if (oldLines[i] !== newLines[i]) {
|
|
3480
|
+
const diff = newLines[i].length - oldLines[i].length;
|
|
3481
|
+
if (charCount + oldLines[i].length < cursorPos) {
|
|
3482
|
+
offset += diff;
|
|
3483
|
+
}
|
|
3484
|
+
}
|
|
3485
|
+
charCount += oldLines[i].length + 1;
|
|
3486
|
+
}
|
|
3487
|
+
this.textarea.value = newValue;
|
|
3488
|
+
const newCursorPos = cursorPos + offset;
|
|
3489
|
+
this.textarea.setSelectionRange(newCursorPos, newCursorPos);
|
|
3490
|
+
this.textarea.dispatchEvent(new Event("input", { bubbles: true }));
|
|
3491
|
+
}
|
|
3492
|
+
}
|
|
3149
3493
|
/**
|
|
3150
3494
|
* Handle scroll events
|
|
3151
3495
|
* @private
|
|
@@ -3174,24 +3518,36 @@ var _OverType = class _OverType {
|
|
|
3174
3518
|
}
|
|
3175
3519
|
/**
|
|
3176
3520
|
* Get the rendered HTML of the current content
|
|
3177
|
-
* @param {
|
|
3521
|
+
* @param {Object} options - Rendering options
|
|
3522
|
+
* @param {boolean} options.cleanHTML - If true, removes syntax markers and OverType-specific classes
|
|
3178
3523
|
* @returns {string} Rendered HTML
|
|
3179
3524
|
*/
|
|
3180
|
-
getRenderedHTML(
|
|
3525
|
+
getRenderedHTML(options = {}) {
|
|
3181
3526
|
const markdown = this.getValue();
|
|
3182
3527
|
let html = MarkdownParser.parse(markdown);
|
|
3183
|
-
if (
|
|
3184
|
-
html =
|
|
3528
|
+
if (options.cleanHTML) {
|
|
3529
|
+
html = html.replace(/<span class="syntax-marker[^"]*">.*?<\/span>/g, "");
|
|
3530
|
+
html = html.replace(/\sclass="(bullet-list|ordered-list|code-fence|hr-marker|blockquote|url-part)"/g, "");
|
|
3531
|
+
html = html.replace(/\sclass=""/g, "");
|
|
3185
3532
|
}
|
|
3186
3533
|
return html;
|
|
3187
3534
|
}
|
|
3188
3535
|
/**
|
|
3189
3536
|
* Get the current preview element's HTML
|
|
3537
|
+
* This includes all syntax markers and OverType styling
|
|
3190
3538
|
* @returns {string} Current preview HTML (as displayed)
|
|
3191
3539
|
*/
|
|
3192
3540
|
getPreviewHTML() {
|
|
3193
3541
|
return this.preview.innerHTML;
|
|
3194
3542
|
}
|
|
3543
|
+
/**
|
|
3544
|
+
* Get clean HTML without any OverType-specific markup
|
|
3545
|
+
* Useful for exporting to other formats or storage
|
|
3546
|
+
* @returns {string} Clean HTML suitable for export
|
|
3547
|
+
*/
|
|
3548
|
+
getCleanHTML() {
|
|
3549
|
+
return this.getRenderedHTML({ cleanHTML: true });
|
|
3550
|
+
}
|
|
3195
3551
|
/**
|
|
3196
3552
|
* Focus the editor
|
|
3197
3553
|
*/
|
|
@@ -3510,9 +3866,6 @@ OverType.ShortcutsManager = ShortcutsManager;
|
|
|
3510
3866
|
OverType.themes = { solar, cave: getTheme("cave") };
|
|
3511
3867
|
OverType.getTheme = getTheme;
|
|
3512
3868
|
OverType.currentTheme = solar;
|
|
3513
|
-
if (typeof window !== "undefined" && typeof window.document !== "undefined") {
|
|
3514
|
-
window.OverType = OverType;
|
|
3515
|
-
}
|
|
3516
3869
|
var overtype_default = OverType;
|
|
3517
3870
|
export {
|
|
3518
3871
|
OverType,
|