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/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "overtype",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.4",
|
|
4
4
|
"description": "A lightweight markdown editor library with perfect WYSIWYG alignment using an invisible textarea overlay",
|
|
5
5
|
"main": "dist/overtype.cjs",
|
|
6
6
|
"module": "dist/overtype.esm.js",
|
|
7
7
|
"browser": "dist/overtype.min.js",
|
|
8
|
+
"types": "dist/overtype.d.ts",
|
|
8
9
|
"unpkg": "dist/overtype.min.js",
|
|
9
10
|
"jsdelivr": "dist/overtype.min.js",
|
|
10
11
|
"exports": {
|
|
@@ -20,12 +21,13 @@
|
|
|
20
21
|
"build:prod": "npm test && npm run build",
|
|
21
22
|
"dev": "http-server -p 8080 -c-1",
|
|
22
23
|
"watch": "node build.js --watch",
|
|
23
|
-
"test": "node test/overtype.test.js && node test/preview-mode.test.js && node test/links.test.js && node test/api-methods.test.js && node test/comprehensive-alignment.test.js",
|
|
24
|
+
"test": "node test/overtype.test.js && node test/preview-mode.test.js && node test/links.test.js && node test/api-methods.test.js && node test/comprehensive-alignment.test.js && npm run test:types",
|
|
24
25
|
"test:main": "node test/overtype.test.js",
|
|
25
26
|
"test:preview": "node test/preview-mode.test.js",
|
|
26
27
|
"test:links": "node test/links.test.js",
|
|
27
28
|
"test:api": "node test/api-methods.test.js",
|
|
28
29
|
"test:alignment": "node test/comprehensive-alignment.test.js",
|
|
30
|
+
"test:types": "tsc --noEmit test-types.ts",
|
|
29
31
|
"preversion": "npm test",
|
|
30
32
|
"size": "gzip-size dist/overtype.min.js",
|
|
31
33
|
"serve": "http-server -p 8080 -c-1"
|
package/src/link-tooltip.js
CHANGED
|
@@ -58,29 +58,29 @@ export class LinkTooltip {
|
|
|
58
58
|
position: absolute;
|
|
59
59
|
position-anchor: var(--target-anchor, --link-0);
|
|
60
60
|
position-area: block-end center;
|
|
61
|
-
margin-top: 8px;
|
|
61
|
+
margin-top: 8px !important;
|
|
62
62
|
|
|
63
|
-
background: #333;
|
|
64
|
-
color: white;
|
|
65
|
-
padding: 6px 10px;
|
|
66
|
-
border-radius: 16px;
|
|
67
|
-
font-size: 12px;
|
|
68
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
69
|
-
display: none;
|
|
70
|
-
z-index: 10000;
|
|
71
|
-
cursor: pointer;
|
|
72
|
-
box-shadow: 0 2px 8px rgba(0,0,0,0.3);
|
|
73
|
-
max-width: 300px;
|
|
74
|
-
white-space: nowrap;
|
|
75
|
-
overflow: hidden;
|
|
76
|
-
text-overflow: ellipsis;
|
|
63
|
+
background: #333 !important;
|
|
64
|
+
color: white !important;
|
|
65
|
+
padding: 6px 10px !important;
|
|
66
|
+
border-radius: 16px !important;
|
|
67
|
+
font-size: 12px !important;
|
|
68
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif !important;
|
|
69
|
+
display: none !important;
|
|
70
|
+
z-index: 10000 !important;
|
|
71
|
+
cursor: pointer !important;
|
|
72
|
+
box-shadow: 0 2px 8px rgba(0,0,0,0.3) !important;
|
|
73
|
+
max-width: 300px !important;
|
|
74
|
+
white-space: nowrap !important;
|
|
75
|
+
overflow: hidden !important;
|
|
76
|
+
text-overflow: ellipsis !important;
|
|
77
77
|
|
|
78
78
|
position-try: most-width block-end inline-end, flip-inline, block-start center;
|
|
79
79
|
position-visibility: anchors-visible;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
.overtype-link-tooltip.visible {
|
|
83
|
-
display: flex;
|
|
83
|
+
display: flex !important;
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
`;
|
package/src/overtype.d.ts
CHANGED
|
@@ -71,7 +71,16 @@ export interface Options {
|
|
|
71
71
|
// Features
|
|
72
72
|
showActiveLineRaw?: boolean;
|
|
73
73
|
showStats?: boolean;
|
|
74
|
-
toolbar?: boolean
|
|
74
|
+
toolbar?: boolean | {
|
|
75
|
+
buttons?: Array<{
|
|
76
|
+
name?: string;
|
|
77
|
+
icon?: string;
|
|
78
|
+
title?: string;
|
|
79
|
+
action?: string;
|
|
80
|
+
separator?: boolean;
|
|
81
|
+
}>;
|
|
82
|
+
};
|
|
83
|
+
smartLists?: boolean; // v1.2.3+ Smart list continuation
|
|
75
84
|
statsFormatter?: (stats: Stats) => string;
|
|
76
85
|
|
|
77
86
|
// Theme (deprecated in favor of global theme)
|
|
@@ -107,6 +116,10 @@ export interface OverTypeConstructor {
|
|
|
107
116
|
getTheme(name: string): Theme;
|
|
108
117
|
}
|
|
109
118
|
|
|
119
|
+
export interface RenderOptions {
|
|
120
|
+
cleanHTML?: boolean;
|
|
121
|
+
}
|
|
122
|
+
|
|
110
123
|
export interface OverTypeInstance {
|
|
111
124
|
// Public properties
|
|
112
125
|
container: HTMLElement;
|
|
@@ -135,6 +148,15 @@ export interface OverTypeInstance {
|
|
|
135
148
|
showStats(show: boolean): void;
|
|
136
149
|
setTheme(theme: string | Theme): void;
|
|
137
150
|
updatePreview(): void;
|
|
151
|
+
|
|
152
|
+
// HTML output methods
|
|
153
|
+
getRenderedHTML(options?: RenderOptions): string;
|
|
154
|
+
getCleanHTML(): string;
|
|
155
|
+
getPreviewHTML(): string;
|
|
156
|
+
|
|
157
|
+
// View mode methods
|
|
158
|
+
showPlainTextarea(show: boolean): void;
|
|
159
|
+
showPreviewMode(show: boolean): void;
|
|
138
160
|
}
|
|
139
161
|
|
|
140
162
|
// Declare the constructor as a constant with proper typing
|
package/src/overtype.js
CHANGED
|
@@ -104,7 +104,8 @@ class OverType {
|
|
|
104
104
|
|
|
105
105
|
// Setup toolbar if enabled
|
|
106
106
|
if (this.options.toolbar) {
|
|
107
|
-
this.toolbar
|
|
107
|
+
const toolbarButtons = typeof this.options.toolbar === 'object' ? this.options.toolbar.buttons : null;
|
|
108
|
+
this.toolbar = new Toolbar(this, toolbarButtons);
|
|
108
109
|
this.toolbar.create();
|
|
109
110
|
|
|
110
111
|
// Update toolbar states on selection change
|
|
@@ -164,7 +165,8 @@ class OverType {
|
|
|
164
165
|
showActiveLineRaw: false,
|
|
165
166
|
showStats: false,
|
|
166
167
|
toolbar: false,
|
|
167
|
-
statsFormatter: null
|
|
168
|
+
statsFormatter: null,
|
|
169
|
+
smartLists: true // Enable smart list continuation
|
|
168
170
|
};
|
|
169
171
|
|
|
170
172
|
// Remove theme and colors from options - these are now global
|
|
@@ -583,6 +585,14 @@ class OverType {
|
|
|
583
585
|
return;
|
|
584
586
|
}
|
|
585
587
|
|
|
588
|
+
// Handle Enter key for smart list continuation
|
|
589
|
+
if (event.key === 'Enter' && !event.shiftKey && !event.metaKey && !event.ctrlKey && this.options.smartLists) {
|
|
590
|
+
if (this.handleSmartListContinuation()) {
|
|
591
|
+
event.preventDefault();
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
586
596
|
// Let shortcuts manager handle other keys
|
|
587
597
|
const handled = this.shortcuts.handleKeydown(event);
|
|
588
598
|
|
|
@@ -592,6 +602,141 @@ class OverType {
|
|
|
592
602
|
}
|
|
593
603
|
}
|
|
594
604
|
|
|
605
|
+
/**
|
|
606
|
+
* Handle smart list continuation
|
|
607
|
+
* @returns {boolean} Whether the event was handled
|
|
608
|
+
*/
|
|
609
|
+
handleSmartListContinuation() {
|
|
610
|
+
const textarea = this.textarea;
|
|
611
|
+
const cursorPos = textarea.selectionStart;
|
|
612
|
+
const context = MarkdownParser.getListContext(textarea.value, cursorPos);
|
|
613
|
+
|
|
614
|
+
if (!context || !context.inList) return false;
|
|
615
|
+
|
|
616
|
+
// Handle empty list item (exit list)
|
|
617
|
+
if (context.content.trim() === '' && cursorPos >= context.markerEndPos) {
|
|
618
|
+
this.deleteListMarker(context);
|
|
619
|
+
return true;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
// Handle text splitting if cursor is in middle of content
|
|
623
|
+
if (cursorPos > context.markerEndPos && cursorPos < context.lineEnd) {
|
|
624
|
+
this.splitListItem(context, cursorPos);
|
|
625
|
+
} else {
|
|
626
|
+
// Just add new item after current line
|
|
627
|
+
this.insertNewListItem(context);
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
// Handle numbered list renumbering
|
|
631
|
+
if (context.listType === 'numbered') {
|
|
632
|
+
this.scheduleNumberedListUpdate();
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
return true;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
/**
|
|
639
|
+
* Delete list marker and exit list
|
|
640
|
+
* @private
|
|
641
|
+
*/
|
|
642
|
+
deleteListMarker(context) {
|
|
643
|
+
// Select from line start to marker end
|
|
644
|
+
this.textarea.setSelectionRange(context.lineStart, context.markerEndPos);
|
|
645
|
+
document.execCommand('delete');
|
|
646
|
+
|
|
647
|
+
// Trigger input event
|
|
648
|
+
this.textarea.dispatchEvent(new Event('input', { bubbles: true }));
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
/**
|
|
652
|
+
* Insert new list item
|
|
653
|
+
* @private
|
|
654
|
+
*/
|
|
655
|
+
insertNewListItem(context) {
|
|
656
|
+
const newItem = MarkdownParser.createNewListItem(context);
|
|
657
|
+
document.execCommand('insertText', false, '\n' + newItem);
|
|
658
|
+
|
|
659
|
+
// Trigger input event
|
|
660
|
+
this.textarea.dispatchEvent(new Event('input', { bubbles: true }));
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
/**
|
|
664
|
+
* Split list item at cursor position
|
|
665
|
+
* @private
|
|
666
|
+
*/
|
|
667
|
+
splitListItem(context, cursorPos) {
|
|
668
|
+
// Get text after cursor
|
|
669
|
+
const textAfterCursor = context.content.substring(cursorPos - context.markerEndPos);
|
|
670
|
+
|
|
671
|
+
// Delete text after cursor
|
|
672
|
+
this.textarea.setSelectionRange(cursorPos, context.lineEnd);
|
|
673
|
+
document.execCommand('delete');
|
|
674
|
+
|
|
675
|
+
// Insert new list item with remaining text
|
|
676
|
+
const newItem = MarkdownParser.createNewListItem(context);
|
|
677
|
+
document.execCommand('insertText', false, '\n' + newItem + textAfterCursor);
|
|
678
|
+
|
|
679
|
+
// Position cursor after new list marker
|
|
680
|
+
const newCursorPos = this.textarea.selectionStart - textAfterCursor.length;
|
|
681
|
+
this.textarea.setSelectionRange(newCursorPos, newCursorPos);
|
|
682
|
+
|
|
683
|
+
// Trigger input event
|
|
684
|
+
this.textarea.dispatchEvent(new Event('input', { bubbles: true }));
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
/**
|
|
688
|
+
* Schedule numbered list renumbering
|
|
689
|
+
* @private
|
|
690
|
+
*/
|
|
691
|
+
scheduleNumberedListUpdate() {
|
|
692
|
+
// Clear any pending update
|
|
693
|
+
if (this.numberUpdateTimeout) {
|
|
694
|
+
clearTimeout(this.numberUpdateTimeout);
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
// Schedule update after current input cycle
|
|
698
|
+
this.numberUpdateTimeout = setTimeout(() => {
|
|
699
|
+
this.updateNumberedLists();
|
|
700
|
+
}, 10);
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
/**
|
|
704
|
+
* Update/renumber all numbered lists
|
|
705
|
+
* @private
|
|
706
|
+
*/
|
|
707
|
+
updateNumberedLists() {
|
|
708
|
+
const value = this.textarea.value;
|
|
709
|
+
const cursorPos = this.textarea.selectionStart;
|
|
710
|
+
|
|
711
|
+
const newValue = MarkdownParser.renumberLists(value);
|
|
712
|
+
|
|
713
|
+
if (newValue !== value) {
|
|
714
|
+
// Calculate cursor offset
|
|
715
|
+
let offset = 0;
|
|
716
|
+
const oldLines = value.split('\n');
|
|
717
|
+
const newLines = newValue.split('\n');
|
|
718
|
+
let charCount = 0;
|
|
719
|
+
|
|
720
|
+
for (let i = 0; i < oldLines.length && charCount < cursorPos; i++) {
|
|
721
|
+
if (oldLines[i] !== newLines[i]) {
|
|
722
|
+
const diff = newLines[i].length - oldLines[i].length;
|
|
723
|
+
if (charCount + oldLines[i].length < cursorPos) {
|
|
724
|
+
offset += diff;
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
charCount += oldLines[i].length + 1; // +1 for newline
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
// Update textarea
|
|
731
|
+
this.textarea.value = newValue;
|
|
732
|
+
const newCursorPos = cursorPos + offset;
|
|
733
|
+
this.textarea.setSelectionRange(newCursorPos, newCursorPos);
|
|
734
|
+
|
|
735
|
+
// Trigger update
|
|
736
|
+
this.textarea.dispatchEvent(new Event('input', { bubbles: true }));
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
|
|
595
740
|
/**
|
|
596
741
|
* Handle scroll events
|
|
597
742
|
* @private
|
|
@@ -627,16 +772,21 @@ class OverType {
|
|
|
627
772
|
|
|
628
773
|
/**
|
|
629
774
|
* Get the rendered HTML of the current content
|
|
630
|
-
* @param {
|
|
775
|
+
* @param {Object} options - Rendering options
|
|
776
|
+
* @param {boolean} options.cleanHTML - If true, removes syntax markers and OverType-specific classes
|
|
631
777
|
* @returns {string} Rendered HTML
|
|
632
778
|
*/
|
|
633
|
-
getRenderedHTML(
|
|
779
|
+
getRenderedHTML(options = {}) {
|
|
634
780
|
const markdown = this.getValue();
|
|
635
781
|
let html = MarkdownParser.parse(markdown);
|
|
636
782
|
|
|
637
|
-
if (
|
|
638
|
-
//
|
|
639
|
-
html =
|
|
783
|
+
if (options.cleanHTML) {
|
|
784
|
+
// Remove all syntax marker spans for clean HTML export
|
|
785
|
+
html = html.replace(/<span class="syntax-marker[^"]*">.*?<\/span>/g, '');
|
|
786
|
+
// Remove OverType-specific classes
|
|
787
|
+
html = html.replace(/\sclass="(bullet-list|ordered-list|code-fence|hr-marker|blockquote|url-part)"/g, '');
|
|
788
|
+
// Clean up empty class attributes
|
|
789
|
+
html = html.replace(/\sclass=""/g, '');
|
|
640
790
|
}
|
|
641
791
|
|
|
642
792
|
return html;
|
|
@@ -644,11 +794,21 @@ class OverType {
|
|
|
644
794
|
|
|
645
795
|
/**
|
|
646
796
|
* Get the current preview element's HTML
|
|
797
|
+
* This includes all syntax markers and OverType styling
|
|
647
798
|
* @returns {string} Current preview HTML (as displayed)
|
|
648
799
|
*/
|
|
649
800
|
getPreviewHTML() {
|
|
650
801
|
return this.preview.innerHTML;
|
|
651
802
|
}
|
|
803
|
+
|
|
804
|
+
/**
|
|
805
|
+
* Get clean HTML without any OverType-specific markup
|
|
806
|
+
* Useful for exporting to other formats or storage
|
|
807
|
+
* @returns {string} Clean HTML suitable for export
|
|
808
|
+
*/
|
|
809
|
+
getCleanHTML() {
|
|
810
|
+
return this.getRenderedHTML({ cleanHTML: true });
|
|
811
|
+
}
|
|
652
812
|
|
|
653
813
|
/**
|
|
654
814
|
* Focus the editor
|
|
@@ -1065,12 +1225,6 @@ OverType.getTheme = getTheme;
|
|
|
1065
1225
|
// Set default theme
|
|
1066
1226
|
OverType.currentTheme = solar;
|
|
1067
1227
|
|
|
1068
|
-
// Only attach to global in browser environments (not Node.js)
|
|
1069
|
-
if (typeof window !== 'undefined' && typeof window.document !== 'undefined') {
|
|
1070
|
-
// Browser environment - attach to window
|
|
1071
|
-
window.OverType = OverType;
|
|
1072
|
-
}
|
|
1073
|
-
|
|
1074
1228
|
// Export for module systems
|
|
1075
1229
|
export default OverType;
|
|
1076
1230
|
export { OverType };
|