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.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
|
|
@@ -164,6 +164,28 @@ var OverType = (() => {
|
|
|
164
164
|
static parseInlineCode(html) {
|
|
165
165
|
return html.replace(new RegExp("(?<!`)(`+)(?!`)((?:(?!\\1).)+?)(\\1)(?!`)", "g"), '<code><span class="syntax-marker">$1</span>$2<span class="syntax-marker">$3</span></code>');
|
|
166
166
|
}
|
|
167
|
+
/**
|
|
168
|
+
* Sanitize URL to prevent XSS attacks
|
|
169
|
+
* @param {string} url - URL to sanitize
|
|
170
|
+
* @returns {string} Safe URL or '#' if dangerous
|
|
171
|
+
*/
|
|
172
|
+
static sanitizeUrl(url) {
|
|
173
|
+
const trimmed = url.trim();
|
|
174
|
+
const lower = trimmed.toLowerCase();
|
|
175
|
+
const safeProtocols = [
|
|
176
|
+
"http://",
|
|
177
|
+
"https://",
|
|
178
|
+
"mailto:",
|
|
179
|
+
"ftp://",
|
|
180
|
+
"ftps://"
|
|
181
|
+
];
|
|
182
|
+
const hasSafeProtocol = safeProtocols.some((protocol) => lower.startsWith(protocol));
|
|
183
|
+
const isRelative = trimmed.startsWith("/") || trimmed.startsWith("#") || trimmed.startsWith("?") || trimmed.startsWith(".") || !trimmed.includes(":") && !trimmed.includes("//");
|
|
184
|
+
if (hasSafeProtocol || isRelative) {
|
|
185
|
+
return url;
|
|
186
|
+
}
|
|
187
|
+
return "#";
|
|
188
|
+
}
|
|
167
189
|
/**
|
|
168
190
|
* Parse links
|
|
169
191
|
* @param {string} html - HTML with potential link markdown
|
|
@@ -172,7 +194,8 @@ var OverType = (() => {
|
|
|
172
194
|
static parseLinks(html) {
|
|
173
195
|
return html.replace(/\[(.+?)\]\((.+?)\)/g, (match, text, url) => {
|
|
174
196
|
const anchorName = `--link-${this.linkIndex++}`;
|
|
175
|
-
|
|
197
|
+
const safeUrl = this.sanitizeUrl(url);
|
|
198
|
+
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>`;
|
|
176
199
|
});
|
|
177
200
|
}
|
|
178
201
|
/**
|
|
@@ -1324,6 +1347,7 @@ ${blockSuffix}` : suffix;
|
|
|
1324
1347
|
const {
|
|
1325
1348
|
fontSize = "14px",
|
|
1326
1349
|
lineHeight = 1.6,
|
|
1350
|
+
/* System-first, guaranteed monospaced; avoids Android 'ui-monospace' pitfalls */
|
|
1327
1351
|
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',
|
|
1328
1352
|
padding = "20px",
|
|
1329
1353
|
theme = null,
|
|
@@ -1387,7 +1411,6 @@ ${blockSuffix}` : suffix;
|
|
|
1387
1411
|
|
|
1388
1412
|
/* Font properties - any difference breaks alignment */
|
|
1389
1413
|
font-family: ${fontFamily} !important;
|
|
1390
|
-
font-synthesis: none !important; /* no faux bold/italic width drift */
|
|
1391
1414
|
font-variant-ligatures: none !important; /* keep metrics stable for code */
|
|
1392
1415
|
font-size: var(--instance-font-size, ${fontSize}) !important;
|
|
1393
1416
|
line-height: var(--instance-line-height, ${lineHeight}) !important;
|
|
@@ -1783,6 +1806,23 @@ ${blockSuffix}` : suffix;
|
|
|
1783
1806
|
margin: 0 2px;
|
|
1784
1807
|
}
|
|
1785
1808
|
}
|
|
1809
|
+
|
|
1810
|
+
/* Plain mode - hide preview and show textarea text */
|
|
1811
|
+
.overtype-container.plain-mode .overtype-preview {
|
|
1812
|
+
display: none !important;
|
|
1813
|
+
}
|
|
1814
|
+
|
|
1815
|
+
.overtype-container.plain-mode .overtype-input {
|
|
1816
|
+
color: var(--text, #0d3b66) !important;
|
|
1817
|
+
/* Use system font stack for better plain text readability */
|
|
1818
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
|
1819
|
+
"Helvetica Neue", Arial, sans-serif !important;
|
|
1820
|
+
}
|
|
1821
|
+
|
|
1822
|
+
/* Ensure textarea remains transparent in overlay mode */
|
|
1823
|
+
.overtype-container:not(.plain-mode) .overtype-input {
|
|
1824
|
+
color: transparent !important;
|
|
1825
|
+
}
|
|
1786
1826
|
|
|
1787
1827
|
${mobileStyles}
|
|
1788
1828
|
`;
|
|
@@ -1846,6 +1886,10 @@ ${blockSuffix}` : suffix;
|
|
|
1846
1886
|
<rect stroke="currentColor" fill="none" stroke-width="1.5" x="2" y="13" width="3" height="3" rx="0.5"></rect>
|
|
1847
1887
|
<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>
|
|
1848
1888
|
</svg>`;
|
|
1889
|
+
var eyeIcon = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
1890
|
+
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" fill="none"></path>
|
|
1891
|
+
<circle cx="12" cy="12" r="3" fill="none"></circle>
|
|
1892
|
+
</svg>`;
|
|
1849
1893
|
|
|
1850
1894
|
// src/toolbar.js
|
|
1851
1895
|
var Toolbar = class {
|
|
@@ -1877,7 +1921,9 @@ ${blockSuffix}` : suffix;
|
|
|
1877
1921
|
{ separator: true },
|
|
1878
1922
|
{ name: "bulletList", icon: bulletListIcon, title: "Bullet List", action: "toggleBulletList" },
|
|
1879
1923
|
{ name: "orderedList", icon: orderedListIcon, title: "Numbered List", action: "toggleNumberedList" },
|
|
1880
|
-
{ name: "taskList", icon: taskListIcon, title: "Task List", action: "toggleTaskList" }
|
|
1924
|
+
{ name: "taskList", icon: taskListIcon, title: "Task List", action: "toggleTaskList" },
|
|
1925
|
+
{ separator: true },
|
|
1926
|
+
{ name: "togglePlain", icon: eyeIcon, title: "Show plain textarea", action: "toggle-plain" }
|
|
1881
1927
|
];
|
|
1882
1928
|
buttonConfig.forEach((config) => {
|
|
1883
1929
|
if (config.separator) {
|
|
@@ -1958,6 +2004,10 @@ ${blockSuffix}` : suffix;
|
|
|
1958
2004
|
case "toggleTaskList":
|
|
1959
2005
|
toggleTaskList(textarea);
|
|
1960
2006
|
break;
|
|
2007
|
+
case "toggle-plain":
|
|
2008
|
+
const isPlain = this.editor.container.classList.contains("plain-mode");
|
|
2009
|
+
this.editor.showPlainTextarea(!isPlain);
|
|
2010
|
+
break;
|
|
1961
2011
|
}
|
|
1962
2012
|
textarea.dispatchEvent(new Event("input", { bubbles: true }));
|
|
1963
2013
|
} catch (error) {
|
|
@@ -2006,6 +2056,9 @@ ${blockSuffix}` : suffix;
|
|
|
2006
2056
|
case "h3":
|
|
2007
2057
|
isActive = activeFormats.includes("header-3");
|
|
2008
2058
|
break;
|
|
2059
|
+
case "togglePlain":
|
|
2060
|
+
isActive = !this.editor.container.classList.contains("plain-mode");
|
|
2061
|
+
break;
|
|
2009
2062
|
}
|
|
2010
2063
|
button.classList.toggle("active", isActive);
|
|
2011
2064
|
button.setAttribute("aria-pressed", isActive.toString());
|
|
@@ -2254,7 +2307,8 @@ ${blockSuffix}` : suffix;
|
|
|
2254
2307
|
// Typography
|
|
2255
2308
|
fontSize: "14px",
|
|
2256
2309
|
lineHeight: 1.6,
|
|
2257
|
-
|
|
2310
|
+
/* System-first, guaranteed monospaced; avoids Android 'ui-monospace' pitfalls */
|
|
2311
|
+
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',
|
|
2258
2312
|
padding: "16px",
|
|
2259
2313
|
// Mobile styles
|
|
2260
2314
|
mobile: {
|
|
@@ -2732,6 +2786,26 @@ ${blockSuffix}` : suffix;
|
|
|
2732
2786
|
this.statsBar = null;
|
|
2733
2787
|
}
|
|
2734
2788
|
}
|
|
2789
|
+
/**
|
|
2790
|
+
* Show or hide the plain textarea (toggle overlay visibility)
|
|
2791
|
+
* @param {boolean} show - true to show plain textarea (hide overlay), false to show overlay
|
|
2792
|
+
* @returns {boolean} Current plain textarea state
|
|
2793
|
+
*/
|
|
2794
|
+
showPlainTextarea(show) {
|
|
2795
|
+
if (show) {
|
|
2796
|
+
this.container.classList.add("plain-mode");
|
|
2797
|
+
} else {
|
|
2798
|
+
this.container.classList.remove("plain-mode");
|
|
2799
|
+
}
|
|
2800
|
+
if (this.toolbar) {
|
|
2801
|
+
const toggleBtn = this.container.querySelector('[data-action="toggle-plain"]');
|
|
2802
|
+
if (toggleBtn) {
|
|
2803
|
+
toggleBtn.classList.toggle("active", !show);
|
|
2804
|
+
toggleBtn.title = show ? "Show markdown preview" : "Show plain textarea";
|
|
2805
|
+
}
|
|
2806
|
+
}
|
|
2807
|
+
return show;
|
|
2808
|
+
}
|
|
2735
2809
|
/**
|
|
2736
2810
|
* Destroy the editor instance
|
|
2737
2811
|
*/
|