overtype 1.1.6 → 1.1.8
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/dist/overtype.esm.js +79 -5
- package/dist/overtype.esm.js.map +2 -2
- package/dist/overtype.js +79 -5
- package/dist/overtype.js.map +2 -2
- package/dist/overtype.min.js +36 -17
- package/package.json +1 -1
- package/src/icons.js +5 -0
- package/src/overtype.js +29 -1
- package/src/parser.js +41 -1
- package/src/styles.js +18 -1
- package/src/toolbar.js +12 -1
package/dist/overtype.esm.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* OverType v1.1.
|
|
2
|
+
* OverType v1.1.8
|
|
3
3
|
* A lightweight markdown editor library with perfect WYSIWYG alignment
|
|
4
4
|
* @license MIT
|
|
5
5
|
* @author Demo User
|
|
@@ -140,6 +140,28 @@ var MarkdownParser = class {
|
|
|
140
140
|
static parseInlineCode(html) {
|
|
141
141
|
return html.replace(new RegExp("(?<!`)(`+)(?!`)((?:(?!\\1).)+?)(\\1)(?!`)", "g"), '<code><span class="syntax-marker">$1</span>$2<span class="syntax-marker">$3</span></code>');
|
|
142
142
|
}
|
|
143
|
+
/**
|
|
144
|
+
* Sanitize URL to prevent XSS attacks
|
|
145
|
+
* @param {string} url - URL to sanitize
|
|
146
|
+
* @returns {string} Safe URL or '#' if dangerous
|
|
147
|
+
*/
|
|
148
|
+
static sanitizeUrl(url) {
|
|
149
|
+
const trimmed = url.trim();
|
|
150
|
+
const lower = trimmed.toLowerCase();
|
|
151
|
+
const safeProtocols = [
|
|
152
|
+
"http://",
|
|
153
|
+
"https://",
|
|
154
|
+
"mailto:",
|
|
155
|
+
"ftp://",
|
|
156
|
+
"ftps://"
|
|
157
|
+
];
|
|
158
|
+
const hasSafeProtocol = safeProtocols.some((protocol) => lower.startsWith(protocol));
|
|
159
|
+
const isRelative = trimmed.startsWith("/") || trimmed.startsWith("#") || trimmed.startsWith("?") || trimmed.startsWith(".") || !trimmed.includes(":") && !trimmed.includes("//");
|
|
160
|
+
if (hasSafeProtocol || isRelative) {
|
|
161
|
+
return url;
|
|
162
|
+
}
|
|
163
|
+
return "#";
|
|
164
|
+
}
|
|
143
165
|
/**
|
|
144
166
|
* Parse links
|
|
145
167
|
* @param {string} html - HTML with potential link markdown
|
|
@@ -148,7 +170,8 @@ var MarkdownParser = class {
|
|
|
148
170
|
static parseLinks(html) {
|
|
149
171
|
return html.replace(/\[(.+?)\]\((.+?)\)/g, (match, text, url) => {
|
|
150
172
|
const anchorName = `--link-${this.linkIndex++}`;
|
|
151
|
-
|
|
173
|
+
const safeUrl = this.sanitizeUrl(url);
|
|
174
|
+
return `<a href="${safeUrl}" style="anchor-name: ${anchorName}"><span class="syntax-marker">[</span>${text}<span class="syntax-marker">](</span><span class="syntax-marker link-url">${url}</span><span class="syntax-marker">)</span></a>`;
|
|
152
175
|
});
|
|
153
176
|
}
|
|
154
177
|
/**
|
|
@@ -1300,6 +1323,7 @@ function generateStyles(options = {}) {
|
|
|
1300
1323
|
const {
|
|
1301
1324
|
fontSize = "14px",
|
|
1302
1325
|
lineHeight = 1.6,
|
|
1326
|
+
/* System-first, guaranteed monospaced; avoids Android 'ui-monospace' pitfalls */
|
|
1303
1327
|
fontFamily = '"SF Mono", SFMono-Regular, Menlo, Monaco, "Cascadia Code", Consolas, "Roboto Mono", "Noto Sans Mono", "Droid Sans Mono", "Ubuntu Mono", "DejaVu Sans Mono", "Liberation Mono", "Courier New", Courier, monospace',
|
|
1304
1328
|
padding = "20px",
|
|
1305
1329
|
theme = null,
|
|
@@ -1363,7 +1387,6 @@ function generateStyles(options = {}) {
|
|
|
1363
1387
|
|
|
1364
1388
|
/* Font properties - any difference breaks alignment */
|
|
1365
1389
|
font-family: ${fontFamily} !important;
|
|
1366
|
-
font-synthesis: none !important; /* no faux bold/italic width drift */
|
|
1367
1390
|
font-variant-ligatures: none !important; /* keep metrics stable for code */
|
|
1368
1391
|
font-size: var(--instance-font-size, ${fontSize}) !important;
|
|
1369
1392
|
line-height: var(--instance-line-height, ${lineHeight}) !important;
|
|
@@ -1759,6 +1782,23 @@ function generateStyles(options = {}) {
|
|
|
1759
1782
|
margin: 0 2px;
|
|
1760
1783
|
}
|
|
1761
1784
|
}
|
|
1785
|
+
|
|
1786
|
+
/* Plain mode - hide preview and show textarea text */
|
|
1787
|
+
.overtype-container.plain-mode .overtype-preview {
|
|
1788
|
+
display: none !important;
|
|
1789
|
+
}
|
|
1790
|
+
|
|
1791
|
+
.overtype-container.plain-mode .overtype-input {
|
|
1792
|
+
color: var(--text, #0d3b66) !important;
|
|
1793
|
+
/* Use system font stack for better plain text readability */
|
|
1794
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
|
1795
|
+
"Helvetica Neue", Arial, sans-serif !important;
|
|
1796
|
+
}
|
|
1797
|
+
|
|
1798
|
+
/* Ensure textarea remains transparent in overlay mode */
|
|
1799
|
+
.overtype-container:not(.plain-mode) .overtype-input {
|
|
1800
|
+
color: transparent !important;
|
|
1801
|
+
}
|
|
1762
1802
|
|
|
1763
1803
|
${mobileStyles}
|
|
1764
1804
|
`;
|
|
@@ -1822,6 +1862,10 @@ var taskListIcon = `<svg viewBox="0 0 18 18">
|
|
|
1822
1862
|
<rect stroke="currentColor" fill="none" stroke-width="1.5" x="2" y="13" width="3" height="3" rx="0.5"></rect>
|
|
1823
1863
|
<polyline stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" points="2.65 9.5 3.5 10.5 5 8.5"></polyline>
|
|
1824
1864
|
</svg>`;
|
|
1865
|
+
var eyeIcon = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
1866
|
+
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" fill="none"></path>
|
|
1867
|
+
<circle cx="12" cy="12" r="3" fill="none"></circle>
|
|
1868
|
+
</svg>`;
|
|
1825
1869
|
|
|
1826
1870
|
// src/toolbar.js
|
|
1827
1871
|
var Toolbar = class {
|
|
@@ -1853,7 +1897,9 @@ var Toolbar = class {
|
|
|
1853
1897
|
{ separator: true },
|
|
1854
1898
|
{ name: "bulletList", icon: bulletListIcon, title: "Bullet List", action: "toggleBulletList" },
|
|
1855
1899
|
{ name: "orderedList", icon: orderedListIcon, title: "Numbered List", action: "toggleNumberedList" },
|
|
1856
|
-
{ name: "taskList", icon: taskListIcon, title: "Task List", action: "toggleTaskList" }
|
|
1900
|
+
{ name: "taskList", icon: taskListIcon, title: "Task List", action: "toggleTaskList" },
|
|
1901
|
+
{ separator: true },
|
|
1902
|
+
{ name: "togglePlain", icon: eyeIcon, title: "Show plain textarea", action: "toggle-plain" }
|
|
1857
1903
|
];
|
|
1858
1904
|
buttonConfig.forEach((config) => {
|
|
1859
1905
|
if (config.separator) {
|
|
@@ -1934,6 +1980,10 @@ var Toolbar = class {
|
|
|
1934
1980
|
case "toggleTaskList":
|
|
1935
1981
|
toggleTaskList(textarea);
|
|
1936
1982
|
break;
|
|
1983
|
+
case "toggle-plain":
|
|
1984
|
+
const isPlain = this.editor.container.classList.contains("plain-mode");
|
|
1985
|
+
this.editor.showPlainTextarea(!isPlain);
|
|
1986
|
+
break;
|
|
1937
1987
|
}
|
|
1938
1988
|
textarea.dispatchEvent(new Event("input", { bubbles: true }));
|
|
1939
1989
|
} catch (error) {
|
|
@@ -1982,6 +2032,9 @@ var Toolbar = class {
|
|
|
1982
2032
|
case "h3":
|
|
1983
2033
|
isActive = activeFormats.includes("header-3");
|
|
1984
2034
|
break;
|
|
2035
|
+
case "togglePlain":
|
|
2036
|
+
isActive = !this.editor.container.classList.contains("plain-mode");
|
|
2037
|
+
break;
|
|
1985
2038
|
}
|
|
1986
2039
|
button.classList.toggle("active", isActive);
|
|
1987
2040
|
button.setAttribute("aria-pressed", isActive.toString());
|
|
@@ -2230,7 +2283,8 @@ var _OverType = class _OverType {
|
|
|
2230
2283
|
// Typography
|
|
2231
2284
|
fontSize: "14px",
|
|
2232
2285
|
lineHeight: 1.6,
|
|
2233
|
-
|
|
2286
|
+
/* System-first, guaranteed monospaced; avoids Android 'ui-monospace' pitfalls */
|
|
2287
|
+
fontFamily: '"SF Mono", SFMono-Regular, Menlo, Monaco, "Cascadia Code", Consolas, "Roboto Mono", "Noto Sans Mono", "Droid Sans Mono", "Ubuntu Mono", "DejaVu Sans Mono", "Liberation Mono", "Courier New", Courier, monospace',
|
|
2234
2288
|
padding: "16px",
|
|
2235
2289
|
// Mobile styles
|
|
2236
2290
|
mobile: {
|
|
@@ -2708,6 +2762,26 @@ var _OverType = class _OverType {
|
|
|
2708
2762
|
this.statsBar = null;
|
|
2709
2763
|
}
|
|
2710
2764
|
}
|
|
2765
|
+
/**
|
|
2766
|
+
* Show or hide the plain textarea (toggle overlay visibility)
|
|
2767
|
+
* @param {boolean} show - true to show plain textarea (hide overlay), false to show overlay
|
|
2768
|
+
* @returns {boolean} Current plain textarea state
|
|
2769
|
+
*/
|
|
2770
|
+
showPlainTextarea(show) {
|
|
2771
|
+
if (show) {
|
|
2772
|
+
this.container.classList.add("plain-mode");
|
|
2773
|
+
} else {
|
|
2774
|
+
this.container.classList.remove("plain-mode");
|
|
2775
|
+
}
|
|
2776
|
+
if (this.toolbar) {
|
|
2777
|
+
const toggleBtn = this.container.querySelector('[data-action="toggle-plain"]');
|
|
2778
|
+
if (toggleBtn) {
|
|
2779
|
+
toggleBtn.classList.toggle("active", !show);
|
|
2780
|
+
toggleBtn.title = show ? "Show markdown preview" : "Show plain textarea";
|
|
2781
|
+
}
|
|
2782
|
+
}
|
|
2783
|
+
return show;
|
|
2784
|
+
}
|
|
2711
2785
|
/**
|
|
2712
2786
|
* Destroy the editor instance
|
|
2713
2787
|
*/
|