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.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * OverType v1.1.6
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
- return `<a href="${url}" 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>`;
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
- fontFamily: "ui-monospace, 'SFMono-Regular', 'Menlo', 'Consolas', 'Liberation Mono', monospace",
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
  */