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/src/overtype.js
CHANGED
|
@@ -164,7 +164,8 @@ class OverType {
|
|
|
164
164
|
showActiveLineRaw: false,
|
|
165
165
|
showStats: false,
|
|
166
166
|
toolbar: false,
|
|
167
|
-
statsFormatter: null
|
|
167
|
+
statsFormatter: null,
|
|
168
|
+
smartLists: true // Enable smart list continuation
|
|
168
169
|
};
|
|
169
170
|
|
|
170
171
|
// Remove theme and colors from options - these are now global
|
|
@@ -489,25 +490,9 @@ class OverType {
|
|
|
489
490
|
openParent.classList.add('code-block-line');
|
|
490
491
|
closeParent.classList.add('code-block-line');
|
|
491
492
|
|
|
492
|
-
//
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
// Apply class to divs between the fences
|
|
496
|
-
if (currentDiv.tagName === 'DIV') {
|
|
497
|
-
currentDiv.classList.add('code-block-line');
|
|
498
|
-
|
|
499
|
-
// Strip all formatting by replacing with plain text
|
|
500
|
-
// This prevents markdown formatting inside code blocks
|
|
501
|
-
const plainText = currentDiv.textContent;
|
|
502
|
-
currentDiv.textContent = plainText;
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
// Move to next sibling
|
|
506
|
-
currentDiv = currentDiv.nextElementSibling;
|
|
507
|
-
|
|
508
|
-
// Safety check to prevent infinite loop
|
|
509
|
-
if (!currentDiv) break;
|
|
510
|
-
}
|
|
493
|
+
// With the new structure, there's a <pre> block between fences, not DIVs
|
|
494
|
+
// We don't need to process anything between the fences anymore
|
|
495
|
+
// The <pre><code> structure already handles the content correctly
|
|
511
496
|
}
|
|
512
497
|
}
|
|
513
498
|
|
|
@@ -599,6 +584,14 @@ class OverType {
|
|
|
599
584
|
return;
|
|
600
585
|
}
|
|
601
586
|
|
|
587
|
+
// Handle Enter key for smart list continuation
|
|
588
|
+
if (event.key === 'Enter' && !event.shiftKey && !event.metaKey && !event.ctrlKey && this.options.smartLists) {
|
|
589
|
+
if (this.handleSmartListContinuation()) {
|
|
590
|
+
event.preventDefault();
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
602
595
|
// Let shortcuts manager handle other keys
|
|
603
596
|
const handled = this.shortcuts.handleKeydown(event);
|
|
604
597
|
|
|
@@ -608,6 +601,141 @@ class OverType {
|
|
|
608
601
|
}
|
|
609
602
|
}
|
|
610
603
|
|
|
604
|
+
/**
|
|
605
|
+
* Handle smart list continuation
|
|
606
|
+
* @returns {boolean} Whether the event was handled
|
|
607
|
+
*/
|
|
608
|
+
handleSmartListContinuation() {
|
|
609
|
+
const textarea = this.textarea;
|
|
610
|
+
const cursorPos = textarea.selectionStart;
|
|
611
|
+
const context = MarkdownParser.getListContext(textarea.value, cursorPos);
|
|
612
|
+
|
|
613
|
+
if (!context || !context.inList) return false;
|
|
614
|
+
|
|
615
|
+
// Handle empty list item (exit list)
|
|
616
|
+
if (context.content.trim() === '' && cursorPos >= context.markerEndPos) {
|
|
617
|
+
this.deleteListMarker(context);
|
|
618
|
+
return true;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
// Handle text splitting if cursor is in middle of content
|
|
622
|
+
if (cursorPos > context.markerEndPos && cursorPos < context.lineEnd) {
|
|
623
|
+
this.splitListItem(context, cursorPos);
|
|
624
|
+
} else {
|
|
625
|
+
// Just add new item after current line
|
|
626
|
+
this.insertNewListItem(context);
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
// Handle numbered list renumbering
|
|
630
|
+
if (context.listType === 'numbered') {
|
|
631
|
+
this.scheduleNumberedListUpdate();
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
return true;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
/**
|
|
638
|
+
* Delete list marker and exit list
|
|
639
|
+
* @private
|
|
640
|
+
*/
|
|
641
|
+
deleteListMarker(context) {
|
|
642
|
+
// Select from line start to marker end
|
|
643
|
+
this.textarea.setSelectionRange(context.lineStart, context.markerEndPos);
|
|
644
|
+
document.execCommand('delete');
|
|
645
|
+
|
|
646
|
+
// Trigger input event
|
|
647
|
+
this.textarea.dispatchEvent(new Event('input', { bubbles: true }));
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
/**
|
|
651
|
+
* Insert new list item
|
|
652
|
+
* @private
|
|
653
|
+
*/
|
|
654
|
+
insertNewListItem(context) {
|
|
655
|
+
const newItem = MarkdownParser.createNewListItem(context);
|
|
656
|
+
document.execCommand('insertText', false, '\n' + newItem);
|
|
657
|
+
|
|
658
|
+
// Trigger input event
|
|
659
|
+
this.textarea.dispatchEvent(new Event('input', { bubbles: true }));
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
/**
|
|
663
|
+
* Split list item at cursor position
|
|
664
|
+
* @private
|
|
665
|
+
*/
|
|
666
|
+
splitListItem(context, cursorPos) {
|
|
667
|
+
// Get text after cursor
|
|
668
|
+
const textAfterCursor = context.content.substring(cursorPos - context.markerEndPos);
|
|
669
|
+
|
|
670
|
+
// Delete text after cursor
|
|
671
|
+
this.textarea.setSelectionRange(cursorPos, context.lineEnd);
|
|
672
|
+
document.execCommand('delete');
|
|
673
|
+
|
|
674
|
+
// Insert new list item with remaining text
|
|
675
|
+
const newItem = MarkdownParser.createNewListItem(context);
|
|
676
|
+
document.execCommand('insertText', false, '\n' + newItem + textAfterCursor);
|
|
677
|
+
|
|
678
|
+
// Position cursor after new list marker
|
|
679
|
+
const newCursorPos = this.textarea.selectionStart - textAfterCursor.length;
|
|
680
|
+
this.textarea.setSelectionRange(newCursorPos, newCursorPos);
|
|
681
|
+
|
|
682
|
+
// Trigger input event
|
|
683
|
+
this.textarea.dispatchEvent(new Event('input', { bubbles: true }));
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
/**
|
|
687
|
+
* Schedule numbered list renumbering
|
|
688
|
+
* @private
|
|
689
|
+
*/
|
|
690
|
+
scheduleNumberedListUpdate() {
|
|
691
|
+
// Clear any pending update
|
|
692
|
+
if (this.numberUpdateTimeout) {
|
|
693
|
+
clearTimeout(this.numberUpdateTimeout);
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
// Schedule update after current input cycle
|
|
697
|
+
this.numberUpdateTimeout = setTimeout(() => {
|
|
698
|
+
this.updateNumberedLists();
|
|
699
|
+
}, 10);
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
/**
|
|
703
|
+
* Update/renumber all numbered lists
|
|
704
|
+
* @private
|
|
705
|
+
*/
|
|
706
|
+
updateNumberedLists() {
|
|
707
|
+
const value = this.textarea.value;
|
|
708
|
+
const cursorPos = this.textarea.selectionStart;
|
|
709
|
+
|
|
710
|
+
const newValue = MarkdownParser.renumberLists(value);
|
|
711
|
+
|
|
712
|
+
if (newValue !== value) {
|
|
713
|
+
// Calculate cursor offset
|
|
714
|
+
let offset = 0;
|
|
715
|
+
const oldLines = value.split('\n');
|
|
716
|
+
const newLines = newValue.split('\n');
|
|
717
|
+
let charCount = 0;
|
|
718
|
+
|
|
719
|
+
for (let i = 0; i < oldLines.length && charCount < cursorPos; i++) {
|
|
720
|
+
if (oldLines[i] !== newLines[i]) {
|
|
721
|
+
const diff = newLines[i].length - oldLines[i].length;
|
|
722
|
+
if (charCount + oldLines[i].length < cursorPos) {
|
|
723
|
+
offset += diff;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
charCount += oldLines[i].length + 1; // +1 for newline
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
// Update textarea
|
|
730
|
+
this.textarea.value = newValue;
|
|
731
|
+
const newCursorPos = cursorPos + offset;
|
|
732
|
+
this.textarea.setSelectionRange(newCursorPos, newCursorPos);
|
|
733
|
+
|
|
734
|
+
// Trigger update
|
|
735
|
+
this.textarea.dispatchEvent(new Event('input', { bubbles: true }));
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
|
|
611
739
|
/**
|
|
612
740
|
* Handle scroll events
|
|
613
741
|
* @private
|
package/src/parser.js
CHANGED
|
@@ -286,6 +286,8 @@ export class MarkdownParser {
|
|
|
286
286
|
|
|
287
287
|
// Wrap in div to maintain line structure
|
|
288
288
|
if (html.trim() === '') {
|
|
289
|
+
// Intentionally use for empty lines to maintain vertical spacing
|
|
290
|
+
// This causes a 0->1 character count difference but preserves visual alignment
|
|
289
291
|
return '<div> </div>';
|
|
290
292
|
}
|
|
291
293
|
|
|
@@ -304,6 +306,8 @@ export class MarkdownParser {
|
|
|
304
306
|
this.resetLinkIndex();
|
|
305
307
|
|
|
306
308
|
const lines = text.split('\n');
|
|
309
|
+
let inCodeBlock = false;
|
|
310
|
+
|
|
307
311
|
const parsedLines = lines.map((line, index) => {
|
|
308
312
|
// Show raw markdown on active line if requested
|
|
309
313
|
if (showActiveLineRaw && index === activeLine) {
|
|
@@ -311,6 +315,21 @@ export class MarkdownParser {
|
|
|
311
315
|
return `<div class="raw-line">${content}</div>`;
|
|
312
316
|
}
|
|
313
317
|
|
|
318
|
+
// Check if this line is a code fence
|
|
319
|
+
const codeFenceRegex = /^```[^`]*$/;
|
|
320
|
+
if (codeFenceRegex.test(line)) {
|
|
321
|
+
inCodeBlock = !inCodeBlock;
|
|
322
|
+
// Parse fence markers normally to get styled output
|
|
323
|
+
return this.parseLine(line);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// If we're inside a code block, don't parse as markdown
|
|
327
|
+
if (inCodeBlock) {
|
|
328
|
+
const escaped = this.escapeHtml(line);
|
|
329
|
+
const indented = this.preserveIndentation(escaped, line);
|
|
330
|
+
return `<div>${indented || ' '}</div>`;
|
|
331
|
+
}
|
|
332
|
+
|
|
314
333
|
// Otherwise, parse the markdown normally
|
|
315
334
|
return this.parseLine(line);
|
|
316
335
|
});
|
|
@@ -358,8 +377,10 @@ export class MarkdownParser {
|
|
|
358
377
|
const fenceText = codeFence.textContent;
|
|
359
378
|
if (fenceText.startsWith('```')) {
|
|
360
379
|
if (!inCodeBlock) {
|
|
361
|
-
// Start of code block
|
|
380
|
+
// Start of code block - keep fence visible, then add pre/code
|
|
362
381
|
inCodeBlock = true;
|
|
382
|
+
|
|
383
|
+
// Create the code block that will follow the fence
|
|
363
384
|
currentCodeBlock = document.createElement('pre');
|
|
364
385
|
const codeElement = document.createElement('code');
|
|
365
386
|
currentCodeBlock.appendChild(codeElement);
|
|
@@ -371,14 +392,16 @@ export class MarkdownParser {
|
|
|
371
392
|
codeElement.className = `language-${lang}`;
|
|
372
393
|
}
|
|
373
394
|
|
|
374
|
-
|
|
375
|
-
child.
|
|
395
|
+
// Insert code block after the fence div (don't remove the fence)
|
|
396
|
+
container.insertBefore(currentCodeBlock, child.nextSibling);
|
|
397
|
+
|
|
398
|
+
// Store reference to the code element for adding content
|
|
399
|
+
currentCodeBlock._codeElement = codeElement;
|
|
376
400
|
continue;
|
|
377
401
|
} else {
|
|
378
|
-
// End of code block
|
|
402
|
+
// End of code block - fence stays visible
|
|
379
403
|
inCodeBlock = false;
|
|
380
404
|
currentCodeBlock = null;
|
|
381
|
-
child.remove();
|
|
382
405
|
continue;
|
|
383
406
|
}
|
|
384
407
|
}
|
|
@@ -386,13 +409,15 @@ export class MarkdownParser {
|
|
|
386
409
|
|
|
387
410
|
// Check if we're in a code block - any div that's not a code fence
|
|
388
411
|
if (inCodeBlock && currentCodeBlock && child.tagName === 'DIV' && !child.querySelector('.code-fence')) {
|
|
389
|
-
const codeElement = currentCodeBlock.querySelector('code');
|
|
412
|
+
const codeElement = currentCodeBlock._codeElement || currentCodeBlock.querySelector('code');
|
|
390
413
|
// Add the line content to the code block
|
|
391
414
|
if (codeElement.textContent.length > 0) {
|
|
392
415
|
codeElement.textContent += '\n';
|
|
393
416
|
}
|
|
394
417
|
// Get the actual text content, preserving spaces
|
|
395
|
-
|
|
418
|
+
// Use textContent instead of innerHTML to avoid double-escaping
|
|
419
|
+
// textContent automatically decodes HTML entities
|
|
420
|
+
const lineText = child.textContent.replace(/\u00A0/g, ' '); // \u00A0 is nbsp
|
|
396
421
|
codeElement.textContent += lineText;
|
|
397
422
|
child.remove();
|
|
398
423
|
continue;
|
|
@@ -465,27 +490,193 @@ export class MarkdownParser {
|
|
|
465
490
|
return match;
|
|
466
491
|
});
|
|
467
492
|
|
|
468
|
-
// Process code blocks
|
|
469
|
-
const codeBlockRegex = /<div><span class="code-fence"
|
|
470
|
-
processed = processed.replace(codeBlockRegex, (match,
|
|
493
|
+
// Process code blocks - KEEP the fence markers for alignment AND use semantic pre/code
|
|
494
|
+
const codeBlockRegex = /<div><span class="code-fence">(```[^<]*)<\/span><\/div>(.*?)<div><span class="code-fence">(```)<\/span><\/div>/gs;
|
|
495
|
+
processed = processed.replace(codeBlockRegex, (match, openFence, content, closeFence) => {
|
|
471
496
|
// Extract the content between fences
|
|
472
497
|
const lines = content.match(/<div>(.*?)<\/div>/gs) || [];
|
|
473
498
|
const codeContent = lines.map(line => {
|
|
474
|
-
// Extract text from each div
|
|
499
|
+
// Extract text from each div - content is already escaped
|
|
475
500
|
const text = line.replace(/<div>(.*?)<\/div>/s, '$1')
|
|
476
|
-
.replace(/ /g, ' ')
|
|
477
|
-
.replace(/</g, '<')
|
|
478
|
-
.replace(/>/g, '>')
|
|
479
|
-
.replace(/"/g, '"')
|
|
480
|
-
.replace(/'/g, "'")
|
|
481
|
-
.replace(/&/g, '&');
|
|
501
|
+
.replace(/ /g, ' ');
|
|
482
502
|
return text;
|
|
483
503
|
}).join('\n');
|
|
484
504
|
|
|
485
|
-
|
|
486
|
-
|
|
505
|
+
// Extract language from the opening fence
|
|
506
|
+
const lang = openFence.slice(3).trim();
|
|
507
|
+
const langClass = lang ? ` class="language-${lang}"` : '';
|
|
508
|
+
|
|
509
|
+
// Keep fence markers visible as separate divs, with pre/code block between them
|
|
510
|
+
let result = `<div><span class="code-fence">${openFence}</span></div>`;
|
|
511
|
+
// Content is already escaped, don't double-escape
|
|
512
|
+
result += `<pre class="code-block"><code${langClass}>${codeContent}</code></pre>`;
|
|
513
|
+
result += `<div><span class="code-fence">${closeFence}</span></div>`;
|
|
514
|
+
|
|
515
|
+
return result;
|
|
487
516
|
});
|
|
488
517
|
|
|
489
518
|
return processed;
|
|
490
519
|
}
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* List pattern definitions
|
|
523
|
+
*/
|
|
524
|
+
static LIST_PATTERNS = {
|
|
525
|
+
bullet: /^(\s*)([-*+])\s+(.*)$/,
|
|
526
|
+
numbered: /^(\s*)(\d+)\.\s+(.*)$/,
|
|
527
|
+
checkbox: /^(\s*)-\s+\[([ x])\]\s+(.*)$/
|
|
528
|
+
};
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* Get list context at cursor position
|
|
532
|
+
* @param {string} text - Full text content
|
|
533
|
+
* @param {number} cursorPosition - Current cursor position
|
|
534
|
+
* @returns {Object} List context information
|
|
535
|
+
*/
|
|
536
|
+
static getListContext(text, cursorPosition) {
|
|
537
|
+
// Find the line containing the cursor
|
|
538
|
+
const lines = text.split('\n');
|
|
539
|
+
let currentPos = 0;
|
|
540
|
+
let lineIndex = 0;
|
|
541
|
+
let lineStart = 0;
|
|
542
|
+
|
|
543
|
+
for (let i = 0; i < lines.length; i++) {
|
|
544
|
+
const lineLength = lines[i].length;
|
|
545
|
+
if (currentPos + lineLength >= cursorPosition) {
|
|
546
|
+
lineIndex = i;
|
|
547
|
+
lineStart = currentPos;
|
|
548
|
+
break;
|
|
549
|
+
}
|
|
550
|
+
currentPos += lineLength + 1; // +1 for newline
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
const currentLine = lines[lineIndex];
|
|
554
|
+
const lineEnd = lineStart + currentLine.length;
|
|
555
|
+
|
|
556
|
+
// Check for checkbox first (most specific)
|
|
557
|
+
const checkboxMatch = currentLine.match(this.LIST_PATTERNS.checkbox);
|
|
558
|
+
if (checkboxMatch) {
|
|
559
|
+
return {
|
|
560
|
+
inList: true,
|
|
561
|
+
listType: 'checkbox',
|
|
562
|
+
indent: checkboxMatch[1],
|
|
563
|
+
marker: '-',
|
|
564
|
+
checked: checkboxMatch[2] === 'x',
|
|
565
|
+
content: checkboxMatch[3],
|
|
566
|
+
lineStart,
|
|
567
|
+
lineEnd,
|
|
568
|
+
markerEndPos: lineStart + checkboxMatch[1].length + checkboxMatch[2].length + 5 // indent + "- [ ] "
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// Check for bullet list
|
|
573
|
+
const bulletMatch = currentLine.match(this.LIST_PATTERNS.bullet);
|
|
574
|
+
if (bulletMatch) {
|
|
575
|
+
return {
|
|
576
|
+
inList: true,
|
|
577
|
+
listType: 'bullet',
|
|
578
|
+
indent: bulletMatch[1],
|
|
579
|
+
marker: bulletMatch[2],
|
|
580
|
+
content: bulletMatch[3],
|
|
581
|
+
lineStart,
|
|
582
|
+
lineEnd,
|
|
583
|
+
markerEndPos: lineStart + bulletMatch[1].length + bulletMatch[2].length + 1 // indent + marker + space
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
// Check for numbered list
|
|
588
|
+
const numberedMatch = currentLine.match(this.LIST_PATTERNS.numbered);
|
|
589
|
+
if (numberedMatch) {
|
|
590
|
+
return {
|
|
591
|
+
inList: true,
|
|
592
|
+
listType: 'numbered',
|
|
593
|
+
indent: numberedMatch[1],
|
|
594
|
+
marker: parseInt(numberedMatch[2]),
|
|
595
|
+
content: numberedMatch[3],
|
|
596
|
+
lineStart,
|
|
597
|
+
lineEnd,
|
|
598
|
+
markerEndPos: lineStart + numberedMatch[1].length + numberedMatch[2].length + 2 // indent + number + ". "
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
// Not in a list
|
|
603
|
+
return {
|
|
604
|
+
inList: false,
|
|
605
|
+
listType: null,
|
|
606
|
+
indent: '',
|
|
607
|
+
marker: null,
|
|
608
|
+
content: currentLine,
|
|
609
|
+
lineStart,
|
|
610
|
+
lineEnd,
|
|
611
|
+
markerEndPos: lineStart
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
/**
|
|
616
|
+
* Create a new list item based on context
|
|
617
|
+
* @param {Object} context - List context from getListContext
|
|
618
|
+
* @returns {string} New list item text
|
|
619
|
+
*/
|
|
620
|
+
static createNewListItem(context) {
|
|
621
|
+
switch (context.listType) {
|
|
622
|
+
case 'bullet':
|
|
623
|
+
return `${context.indent}${context.marker} `;
|
|
624
|
+
case 'numbered':
|
|
625
|
+
return `${context.indent}${context.marker + 1}. `;
|
|
626
|
+
case 'checkbox':
|
|
627
|
+
return `${context.indent}- [ ] `;
|
|
628
|
+
default:
|
|
629
|
+
return '';
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* Renumber all numbered lists in text
|
|
635
|
+
* @param {string} text - Text containing numbered lists
|
|
636
|
+
* @returns {string} Text with renumbered lists
|
|
637
|
+
*/
|
|
638
|
+
static renumberLists(text) {
|
|
639
|
+
const lines = text.split('\n');
|
|
640
|
+
const numbersByIndent = new Map();
|
|
641
|
+
let inList = false;
|
|
642
|
+
|
|
643
|
+
const result = lines.map(line => {
|
|
644
|
+
const match = line.match(this.LIST_PATTERNS.numbered);
|
|
645
|
+
|
|
646
|
+
if (match) {
|
|
647
|
+
const indent = match[1];
|
|
648
|
+
const indentLevel = indent.length;
|
|
649
|
+
const content = match[3];
|
|
650
|
+
|
|
651
|
+
// If we weren't in a list or indent changed, reset lower levels
|
|
652
|
+
if (!inList) {
|
|
653
|
+
numbersByIndent.clear();
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
// Get the next number for this indent level
|
|
657
|
+
const currentNumber = (numbersByIndent.get(indentLevel) || 0) + 1;
|
|
658
|
+
numbersByIndent.set(indentLevel, currentNumber);
|
|
659
|
+
|
|
660
|
+
// Clear deeper indent levels
|
|
661
|
+
for (const [level] of numbersByIndent) {
|
|
662
|
+
if (level > indentLevel) {
|
|
663
|
+
numbersByIndent.delete(level);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
inList = true;
|
|
668
|
+
return `${indent}${currentNumber}. ${content}`;
|
|
669
|
+
} else {
|
|
670
|
+
// Not a numbered list item
|
|
671
|
+
if (line.trim() === '' || !line.match(/^\s/)) {
|
|
672
|
+
// Empty line or non-indented line breaks the list
|
|
673
|
+
inList = false;
|
|
674
|
+
numbersByIndent.clear();
|
|
675
|
+
}
|
|
676
|
+
return line;
|
|
677
|
+
}
|
|
678
|
+
});
|
|
679
|
+
|
|
680
|
+
return result.join('\n');
|
|
681
|
+
}
|
|
491
682
|
}
|
package/src/styles.js
CHANGED
|
@@ -79,11 +79,17 @@ export function generateStyles(options = {}) {
|
|
|
79
79
|
position: relative !important; /* Override reset - needed for absolute children */
|
|
80
80
|
overflow: visible !important; /* Allow dropdown to overflow container */
|
|
81
81
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important;
|
|
82
|
+
text-align: left !important;
|
|
82
83
|
${themeVars ? `
|
|
83
84
|
/* Theme Variables */
|
|
84
85
|
${themeVars}` : ''}
|
|
85
86
|
}
|
|
86
87
|
|
|
88
|
+
/* Force left alignment for all elements in the editor */
|
|
89
|
+
.overtype-container .overtype-wrapper * {
|
|
90
|
+
text-align: left !important;
|
|
91
|
+
}
|
|
92
|
+
|
|
87
93
|
/* Auto-resize mode styles */
|
|
88
94
|
.overtype-container.overtype-auto-resize {
|
|
89
95
|
height: auto !important;
|