overtype 1.2.6 → 2.0.0
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 +228 -40
- package/dist/overtype-webcomponent.esm.js +4763 -0
- package/dist/overtype-webcomponent.esm.js.map +7 -0
- package/dist/overtype-webcomponent.js +4785 -0
- package/dist/overtype-webcomponent.js.map +7 -0
- package/dist/overtype-webcomponent.min.js +991 -0
- package/dist/overtype.cjs +684 -391
- package/dist/overtype.cjs.map +4 -4
- package/dist/overtype.d.ts +57 -14
- package/dist/overtype.esm.js +681 -390
- package/dist/overtype.esm.js.map +4 -4
- package/dist/overtype.js +681 -390
- package/dist/overtype.js.map +4 -4
- package/dist/overtype.min.js +157 -125
- package/package.json +18 -4
- package/src/link-tooltip.js +48 -73
- package/src/overtype-webcomponent.js +676 -0
- package/src/overtype.d.ts +57 -14
- package/src/overtype.js +186 -59
- package/src/parser.js +122 -19
- package/src/styles.js +92 -30
- package/src/toolbar-buttons.js +163 -0
- package/src/toolbar.js +194 -249
- package/diagram.png +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "overtype",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
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",
|
|
@@ -10,24 +10,35 @@
|
|
|
10
10
|
"jsdelivr": "dist/overtype.min.js",
|
|
11
11
|
"exports": {
|
|
12
12
|
".": {
|
|
13
|
+
"types": "./dist/overtype.d.ts",
|
|
13
14
|
"import": "./dist/overtype.esm.js",
|
|
14
15
|
"require": "./dist/overtype.cjs",
|
|
15
16
|
"browser": "./dist/overtype.iife.min.js"
|
|
17
|
+
},
|
|
18
|
+
"./parser": {
|
|
19
|
+
"import": "./src/parser.js",
|
|
20
|
+
"require": "./src/parser.js"
|
|
21
|
+
},
|
|
22
|
+
"./webcomponent": {
|
|
23
|
+
"import": "./dist/overtype-webcomponent.esm.js",
|
|
24
|
+
"browser": "./dist/overtype-webcomponent.min.js"
|
|
16
25
|
}
|
|
17
26
|
},
|
|
18
27
|
"type": "module",
|
|
19
28
|
"scripts": {
|
|
20
29
|
"build": "node build.js",
|
|
21
30
|
"build:prod": "npm test && npm run build",
|
|
22
|
-
"dev": "http-server -p 8080 -c-1",
|
|
31
|
+
"dev": "http-server website -p 8080 -c-1",
|
|
23
32
|
"watch": "node build.js --watch",
|
|
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 && node test/sanctuary-parsing.test.js && npm run test:types",
|
|
33
|
+
"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 && node test/sanctuary-parsing.test.js && node test/mode-switching.test.js && node test/syntax-highlighting.test.js && node test/webcomponent.test.js && npm run test:types",
|
|
25
34
|
"test:main": "node test/overtype.test.js",
|
|
26
35
|
"test:preview": "node test/preview-mode.test.js",
|
|
27
36
|
"test:links": "node test/links.test.js",
|
|
28
37
|
"test:api": "node test/api-methods.test.js",
|
|
29
38
|
"test:alignment": "node test/comprehensive-alignment.test.js",
|
|
30
39
|
"test:sanctuary": "node test/sanctuary-parsing.test.js",
|
|
40
|
+
"test:modes": "node test/mode-switching.test.js",
|
|
41
|
+
"test:webcomponent": "node test/webcomponent.test.js",
|
|
31
42
|
"test:types": "tsc --noEmit test-types.ts",
|
|
32
43
|
"preversion": "npm test",
|
|
33
44
|
"size": "gzip-size dist/overtype.min.js",
|
|
@@ -40,7 +51,10 @@
|
|
|
40
51
|
"lightweight",
|
|
41
52
|
"ghost-caret",
|
|
42
53
|
"textarea",
|
|
43
|
-
"markdown-editor"
|
|
54
|
+
"markdown-editor",
|
|
55
|
+
"web-components",
|
|
56
|
+
"custom-elements",
|
|
57
|
+
"shadow-dom"
|
|
44
58
|
],
|
|
45
59
|
"author": "David Miranda",
|
|
46
60
|
"license": "MIT",
|
package/src/link-tooltip.js
CHANGED
|
@@ -10,82 +10,50 @@ export class LinkTooltip {
|
|
|
10
10
|
this.tooltip = null;
|
|
11
11
|
this.currentLink = null;
|
|
12
12
|
this.hideTimeout = null;
|
|
13
|
-
|
|
13
|
+
this.visibilityChangeHandler = null;
|
|
14
|
+
|
|
14
15
|
this.init();
|
|
15
16
|
}
|
|
16
|
-
|
|
17
|
+
|
|
17
18
|
init() {
|
|
18
|
-
// Check for CSS anchor positioning support
|
|
19
|
-
const supportsAnchor =
|
|
20
|
-
CSS.supports('position-anchor: --x') &&
|
|
21
|
-
CSS.supports('position-area: center');
|
|
22
|
-
|
|
23
|
-
if (!supportsAnchor) {
|
|
24
|
-
// Don't show anything if not supported
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
19
|
// Create tooltip element
|
|
20
|
+
// Note: Styles are now in the main stylesheet (styles.js) with @supports wrapper
|
|
29
21
|
this.createTooltip();
|
|
30
|
-
|
|
22
|
+
|
|
31
23
|
// Listen for cursor position changes
|
|
32
24
|
this.editor.textarea.addEventListener('selectionchange', () => this.checkCursorPosition());
|
|
33
|
-
this.editor.textarea.addEventListener('keyup',
|
|
25
|
+
this.editor.textarea.addEventListener('keyup', e => {
|
|
34
26
|
if (e.key.includes('Arrow') || e.key === 'Home' || e.key === 'End') {
|
|
35
27
|
this.checkCursorPosition();
|
|
36
28
|
}
|
|
37
29
|
});
|
|
38
|
-
|
|
30
|
+
|
|
39
31
|
// Hide tooltip when typing or scrolling
|
|
40
32
|
this.editor.textarea.addEventListener('input', () => this.hide());
|
|
41
33
|
this.editor.textarea.addEventListener('scroll', () => this.hide());
|
|
42
|
-
|
|
34
|
+
|
|
35
|
+
// Hide tooltip when textarea loses focus
|
|
36
|
+
this.editor.textarea.addEventListener('blur', () => this.hide());
|
|
37
|
+
|
|
38
|
+
// Hide tooltip when page loses visibility (tab switch, minimize, etc.)
|
|
39
|
+
this.visibilityChangeHandler = () => {
|
|
40
|
+
if (document.hidden) {
|
|
41
|
+
this.hide();
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
document.addEventListener('visibilitychange', this.visibilityChangeHandler);
|
|
45
|
+
|
|
43
46
|
// Keep tooltip visible on hover
|
|
44
47
|
this.tooltip.addEventListener('mouseenter', () => this.cancelHide());
|
|
45
48
|
this.tooltip.addEventListener('mouseleave', () => this.scheduleHide());
|
|
46
49
|
}
|
|
47
|
-
|
|
50
|
+
|
|
48
51
|
createTooltip() {
|
|
49
52
|
// Create tooltip element
|
|
53
|
+
// Styles are now included in the main stylesheet (styles.js)
|
|
50
54
|
this.tooltip = document.createElement('div');
|
|
51
55
|
this.tooltip.className = 'overtype-link-tooltip';
|
|
52
|
-
|
|
53
|
-
// Add CSS anchor positioning styles
|
|
54
|
-
const tooltipStyles = document.createElement('style');
|
|
55
|
-
tooltipStyles.textContent = `
|
|
56
|
-
@supports (position-anchor: --x) and (position-area: center) {
|
|
57
|
-
.overtype-link-tooltip {
|
|
58
|
-
position: absolute;
|
|
59
|
-
position-anchor: var(--target-anchor, --link-0);
|
|
60
|
-
position-area: block-end center;
|
|
61
|
-
margin-top: 8px !important;
|
|
62
|
-
|
|
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
|
-
|
|
78
|
-
position-try: most-width block-end inline-end, flip-inline, block-start center;
|
|
79
|
-
position-visibility: anchors-visible;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
.overtype-link-tooltip.visible {
|
|
83
|
-
display: flex !important;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
`;
|
|
87
|
-
document.head.appendChild(tooltipStyles);
|
|
88
|
-
|
|
56
|
+
|
|
89
57
|
// Add link icon and text container
|
|
90
58
|
this.tooltip.innerHTML = `
|
|
91
59
|
<span style="display: flex; align-items: center; gap: 6px;">
|
|
@@ -96,9 +64,9 @@ export class LinkTooltip {
|
|
|
96
64
|
<span class="overtype-link-tooltip-url"></span>
|
|
97
65
|
</span>
|
|
98
66
|
`;
|
|
99
|
-
|
|
67
|
+
|
|
100
68
|
// Click handler to open link
|
|
101
|
-
this.tooltip.addEventListener('click',
|
|
69
|
+
this.tooltip.addEventListener('click', e => {
|
|
102
70
|
e.preventDefault();
|
|
103
71
|
e.stopPropagation();
|
|
104
72
|
if (this.currentLink) {
|
|
@@ -106,18 +74,18 @@ export class LinkTooltip {
|
|
|
106
74
|
this.hide();
|
|
107
75
|
}
|
|
108
76
|
});
|
|
109
|
-
|
|
77
|
+
|
|
110
78
|
// Append tooltip to editor container
|
|
111
79
|
this.editor.container.appendChild(this.tooltip);
|
|
112
80
|
}
|
|
113
|
-
|
|
81
|
+
|
|
114
82
|
checkCursorPosition() {
|
|
115
83
|
const cursorPos = this.editor.textarea.selectionStart;
|
|
116
84
|
const text = this.editor.textarea.value;
|
|
117
|
-
|
|
85
|
+
|
|
118
86
|
// Find if cursor is within a markdown link
|
|
119
87
|
const linkInfo = this.findLinkAtPosition(text, cursorPos);
|
|
120
|
-
|
|
88
|
+
|
|
121
89
|
if (linkInfo) {
|
|
122
90
|
if (!this.currentLink || this.currentLink.url !== linkInfo.url || this.currentLink.index !== linkInfo.index) {
|
|
123
91
|
this.show(linkInfo);
|
|
@@ -126,17 +94,17 @@ export class LinkTooltip {
|
|
|
126
94
|
this.scheduleHide();
|
|
127
95
|
}
|
|
128
96
|
}
|
|
129
|
-
|
|
97
|
+
|
|
130
98
|
findLinkAtPosition(text, position) {
|
|
131
99
|
// Regex to find markdown links: [text](url)
|
|
132
100
|
const linkRegex = /\[([^\]]+)\]\(([^)]+)\)/g;
|
|
133
101
|
let match;
|
|
134
102
|
let linkIndex = 0;
|
|
135
|
-
|
|
103
|
+
|
|
136
104
|
while ((match = linkRegex.exec(text)) !== null) {
|
|
137
105
|
const start = match.index;
|
|
138
106
|
const end = match.index + match[0].length;
|
|
139
|
-
|
|
107
|
+
|
|
140
108
|
if (position >= start && position <= end) {
|
|
141
109
|
return {
|
|
142
110
|
text: match[1],
|
|
@@ -148,48 +116,55 @@ export class LinkTooltip {
|
|
|
148
116
|
}
|
|
149
117
|
linkIndex++;
|
|
150
118
|
}
|
|
151
|
-
|
|
119
|
+
|
|
152
120
|
return null;
|
|
153
121
|
}
|
|
154
|
-
|
|
122
|
+
|
|
155
123
|
show(linkInfo) {
|
|
156
124
|
this.currentLink = linkInfo;
|
|
157
125
|
this.cancelHide();
|
|
158
|
-
|
|
126
|
+
|
|
159
127
|
// Update tooltip content
|
|
160
128
|
const urlSpan = this.tooltip.querySelector('.overtype-link-tooltip-url');
|
|
161
129
|
urlSpan.textContent = linkInfo.url;
|
|
162
|
-
|
|
130
|
+
|
|
163
131
|
// Set the CSS variable to point to the correct anchor
|
|
164
132
|
this.tooltip.style.setProperty('--target-anchor', `--link-${linkInfo.index}`);
|
|
165
|
-
|
|
133
|
+
|
|
166
134
|
// Show tooltip (CSS anchor positioning handles the rest)
|
|
167
135
|
this.tooltip.classList.add('visible');
|
|
168
136
|
}
|
|
169
|
-
|
|
137
|
+
|
|
170
138
|
hide() {
|
|
171
139
|
this.tooltip.classList.remove('visible');
|
|
172
140
|
this.currentLink = null;
|
|
173
141
|
}
|
|
174
|
-
|
|
142
|
+
|
|
175
143
|
scheduleHide() {
|
|
176
144
|
this.cancelHide();
|
|
177
145
|
this.hideTimeout = setTimeout(() => this.hide(), 300);
|
|
178
146
|
}
|
|
179
|
-
|
|
147
|
+
|
|
180
148
|
cancelHide() {
|
|
181
149
|
if (this.hideTimeout) {
|
|
182
150
|
clearTimeout(this.hideTimeout);
|
|
183
151
|
this.hideTimeout = null;
|
|
184
152
|
}
|
|
185
153
|
}
|
|
186
|
-
|
|
154
|
+
|
|
187
155
|
destroy() {
|
|
188
156
|
this.cancelHide();
|
|
157
|
+
|
|
158
|
+
// Remove visibility change listener
|
|
159
|
+
if (this.visibilityChangeHandler) {
|
|
160
|
+
document.removeEventListener('visibilitychange', this.visibilityChangeHandler);
|
|
161
|
+
this.visibilityChangeHandler = null;
|
|
162
|
+
}
|
|
163
|
+
|
|
189
164
|
if (this.tooltip && this.tooltip.parentNode) {
|
|
190
165
|
this.tooltip.parentNode.removeChild(this.tooltip);
|
|
191
166
|
}
|
|
192
167
|
this.tooltip = null;
|
|
193
168
|
this.currentLink = null;
|
|
194
169
|
}
|
|
195
|
-
}
|
|
170
|
+
}
|