overtype 1.2.3 → 1.2.5

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 CHANGED
@@ -1,6 +1,16 @@
1
1
  # OverType
2
2
 
3
- A lightweight markdown editor library with perfect WYSIWYG alignment using an invisible textarea overlay technique. Includes optional toolbar. ~82KB minified with all features.
3
+ A lightweight markdown editor library with perfect WYSIWYG alignment using an invisible textarea overlay technique. Includes optional toolbar. ~86KB minified with all features.
4
+
5
+ ## Live Examples
6
+
7
+ 🎮 **Try it out**: [Interactive demos on overtype.dev](https://overtype.dev)
8
+ - [Basic Editor](https://overtype.dev/#basic-editor)
9
+ - [With Toolbar](https://overtype.dev/#toolbar)
10
+ - [Multiple Instances](https://overtype.dev/#multiple-instances)
11
+ - [View Modes](https://overtype.dev/#view-modes)
12
+ - [Custom Themes](https://overtype.dev/#custom-themes)
13
+ - [All Markdown Features](https://overtype.dev/#markdown-features)
4
14
 
5
15
  ## Features
6
16
 
@@ -9,7 +19,7 @@ A lightweight markdown editor library with perfect WYSIWYG alignment using an in
9
19
  - ⌨️ **Keyboard shortcuts** - Common markdown shortcuts (Cmd/Ctrl+B for bold, etc.)
10
20
  - 📱 **Mobile optimized** - Responsive design with mobile-specific styles
11
21
  - 🔄 **DOM persistence aware** - Recovers from existing DOM (perfect for HyperClay and similar platforms)
12
- - 🚀 **Lightweight** - ~82KB minified
22
+ - 🚀 **Lightweight** - ~86KB minified
13
23
  - 🎯 **Optional toolbar** - Clean, minimal toolbar with all essential formatting
14
24
  - ✨ **Smart shortcuts** - Keyboard shortcuts with selection preservation
15
25
  - 📝 **Smart list continuation** - GitHub-style automatic list continuation on Enter
@@ -25,7 +35,7 @@ We overlap an invisible textarea on top of styled output, giving the illusion of
25
35
 
26
36
  | Feature | OverType | HyperMD | Milkdown | TUI Editor | EasyMDE |
27
37
  |---------|----------|---------|----------|------------|---------|
28
- | **Size** | ~82KB | 364.02 KB | 344.51 KB | 560.99 KB | 323.69 KB |
38
+ | **Size** | ~86KB | 364.02 KB | 344.51 KB | 560.99 KB | 323.69 KB |
29
39
  | **Dependencies** | Bundled | CodeMirror | ProseMirror + plugins | Multiple libs | CodeMirror |
30
40
  | **Setup** | Single file | Complex config | Build step required | Complex config | Moderate |
31
41
  | **Approach** | Invisible textarea | ContentEditable | ContentEditable | ContentEditable | CodeMirror |
@@ -212,20 +222,26 @@ const [editor] = new OverType('#editor', {
212
222
  const markdown = editor.getValue();
213
223
  // Returns: "# Title\n\n**Bold** text with [links](https://example.com)"
214
224
 
215
- // Get rendered HTML for display
225
+ // Get rendered HTML with syntax markers (for debugging/inspection)
216
226
  const html = editor.getRenderedHTML();
217
- // Returns basic HTML with markdown elements
227
+ // Returns HTML with <span class="syntax-marker"> elements visible
218
228
 
219
- // Get HTML with post-processing (consolidated lists/code blocks)
220
- const processedHTML = editor.getRenderedHTML(true);
221
- // Returns HTML optimized for preview mode
229
+ // Get clean HTML for export (no OverType-specific markup)
230
+ const cleanHTML = editor.getRenderedHTML({ cleanHTML: true });
231
+ // Returns clean HTML suitable for saving/exporting
222
232
 
223
- // Get the current preview element's HTML
233
+ // Convenience method for clean HTML
234
+ const exportHTML = editor.getCleanHTML();
235
+ // Same as getRenderedHTML({ cleanHTML: true })
236
+
237
+ // Get the current preview element's HTML (actual DOM content)
224
238
  const previewHTML = editor.getPreviewHTML();
225
239
  // Returns exactly what's shown in the editor's preview layer
226
240
 
227
- // Example: Create external preview
228
- document.getElementById('external-preview').innerHTML = editor.getRenderedHTML(true);
241
+ // Example: Export clean HTML to server
242
+ const htmlToSave = editor.getCleanHTML(); // No syntax markers
243
+ // Example: Clone exact preview appearance
244
+ document.getElementById('clone').innerHTML = editor.getPreviewHTML();
229
245
  ```
230
246
 
231
247
  ### Stats Bar
@@ -371,11 +387,12 @@ editor.getValue()
371
387
  editor.setValue(markdown)
372
388
 
373
389
  // Get rendered HTML of the current content
374
- editor.getRenderedHTML() // Basic HTML rendering
375
- editor.getRenderedHTML(true) // With post-processing (consolidated lists/code blocks)
390
+ editor.getRenderedHTML() // With syntax markers (for debugging)
391
+ editor.getRenderedHTML({ cleanHTML: true }) // Clean HTML without OverType markup
392
+ editor.getCleanHTML() // Alias for getRenderedHTML({ cleanHTML: true })
376
393
 
377
394
  // Get the current preview element's HTML
378
- editor.getPreviewHTML() // Returns exactly what's displayed in the preview
395
+ editor.getPreviewHTML() // Actual DOM content from preview layer
379
396
 
380
397
  // Change theme
381
398
  editor.setTheme('cave') // Built-in theme name
@@ -566,7 +583,7 @@ MIT
566
583
  - **Pluggable parser system** - Support for any programming language or syntax
567
584
  - **Parser registry** - Automatic language detection by file extension or MIME type
568
585
  - **Cleaner separation** - Extracted the overlay technique without markdown-specific features
569
- - **Smaller footprint** - ~82KB minified (vs OverType's ~78KB)
586
+ - **Smaller footprint** - ~86KB minified (vs OverType's ~78KB)
570
587
 
571
588
  Key components extracted from OverType to Synesthesia:
572
589
  - The transparent textarea overlay technique for perfect WYSIWYG alignment
@@ -581,3 +598,7 @@ If you need a markdown editor with toolbar and formatting features, use OverType
581
598
 
582
599
  Contributions are welcome! Please feel free to submit a Pull Request.
583
600
 
601
+ ---
602
+
603
+ Ready for another radical idea?
604
+ [Let's remove every layer of the web application stack.](https://hyperclay.com)
package/dist/overtype.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * OverType v1.2.2
2
+ * OverType v1.2.5
3
3
  * A lightweight markdown editor library with perfect WYSIWYG alignment
4
4
  * @license MIT
5
5
  * @author Demo User
@@ -152,7 +152,18 @@ var MarkdownParser = class {
152
152
  */
153
153
  static parseItalic(html) {
154
154
  html = html.replace(new RegExp("(?<!\\*)\\*(?!\\*)(.+?)(?<!\\*)\\*(?!\\*)", "g"), '<em><span class="syntax-marker">*</span>$1<span class="syntax-marker">*</span></em>');
155
- html = html.replace(new RegExp("(?<!_)_(?!_)(.+?)(?<!_)_(?!_)", "g"), '<em><span class="syntax-marker">_</span>$1<span class="syntax-marker">_</span></em>');
155
+ html = html.replace(new RegExp("(?<=^|\\s)_(?!_)(.+?)(?<!_)_(?!_)(?=\\s|$)", "g"), '<em><span class="syntax-marker">_</span>$1<span class="syntax-marker">_</span></em>');
156
+ return html;
157
+ }
158
+ /**
159
+ * Parse strikethrough text
160
+ * Supports both single (~) and double (~~) tildes, but rejects 3+ tildes
161
+ * @param {string} html - HTML with potential strikethrough markdown
162
+ * @returns {string} HTML with strikethrough styling
163
+ */
164
+ static parseStrikethrough(html) {
165
+ html = html.replace(new RegExp("(?<!~)~~(?!~)(.+?)(?<!~)~~(?!~)", "g"), '<del><span class="syntax-marker">~~</span>$1<span class="syntax-marker">~~</span></del>');
166
+ html = html.replace(new RegExp("(?<!~)~(?!~)(.+?)(?<!~)~(?!~)", "g"), '<del><span class="syntax-marker">~</span>$1<span class="syntax-marker">~</span></del>');
156
167
  return html;
157
168
  }
158
169
  /**
@@ -198,30 +209,116 @@ var MarkdownParser = class {
198
209
  });
199
210
  }
200
211
  /**
201
- * Parse all inline elements in correct order
202
- * @param {string} text - Text with potential inline markdown
203
- * @returns {string} HTML with all inline styling
212
+ * Identify and protect sanctuaries (code and links) before parsing
213
+ * @param {string} text - Text with potential markdown
214
+ * @returns {Object} Object with protected text and sanctuary map
204
215
  */
205
- static parseInlineElements(text) {
206
- let html = text;
207
- html = this.parseInlineCode(html);
216
+ static identifyAndProtectSanctuaries(text) {
208
217
  const sanctuaries = /* @__PURE__ */ new Map();
209
- html = html.replace(/(<code>.*?<\/code>)/g, (match) => {
210
- const placeholder = `\uE000${sanctuaries.size}\uE001`;
211
- sanctuaries.set(placeholder, match);
212
- return placeholder;
218
+ let sanctuaryCounter = 0;
219
+ let protectedText = text;
220
+ const protectedRegions = [];
221
+ const linkRegex = /\[([^\]]+)\]\(([^)]+)\)/g;
222
+ let linkMatch;
223
+ while ((linkMatch = linkRegex.exec(text)) !== null) {
224
+ const bracketPos = linkMatch.index + linkMatch[0].indexOf("](");
225
+ const urlStart = bracketPos + 2;
226
+ const urlEnd = urlStart + linkMatch[2].length;
227
+ protectedRegions.push({ start: urlStart, end: urlEnd });
228
+ }
229
+ const codeRegex = new RegExp("(?<!`)(`+)(?!`)((?:(?!\\1).)+?)(\\1)(?!`)", "g");
230
+ let codeMatch;
231
+ const codeMatches = [];
232
+ while ((codeMatch = codeRegex.exec(text)) !== null) {
233
+ const codeStart = codeMatch.index;
234
+ const codeEnd = codeMatch.index + codeMatch[0].length;
235
+ const inProtectedRegion = protectedRegions.some(
236
+ (region) => codeStart >= region.start && codeEnd <= region.end
237
+ );
238
+ if (!inProtectedRegion) {
239
+ codeMatches.push({
240
+ match: codeMatch[0],
241
+ index: codeMatch.index,
242
+ openTicks: codeMatch[1],
243
+ content: codeMatch[2],
244
+ closeTicks: codeMatch[3]
245
+ });
246
+ }
247
+ }
248
+ codeMatches.sort((a, b) => b.index - a.index);
249
+ codeMatches.forEach((codeInfo) => {
250
+ const placeholder = `\uE000${sanctuaryCounter++}\uE001`;
251
+ sanctuaries.set(placeholder, {
252
+ type: "code",
253
+ original: codeInfo.match,
254
+ openTicks: codeInfo.openTicks,
255
+ content: codeInfo.content,
256
+ closeTicks: codeInfo.closeTicks
257
+ });
258
+ protectedText = protectedText.substring(0, codeInfo.index) + placeholder + protectedText.substring(codeInfo.index + codeInfo.match.length);
213
259
  });
214
- html = this.parseLinks(html);
215
- html = html.replace(/(<a[^>]*>.*?<\/a>)/g, (match) => {
216
- const placeholder = `\uE000${sanctuaries.size}\uE001`;
217
- sanctuaries.set(placeholder, match);
260
+ protectedText = protectedText.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (match, linkText, url) => {
261
+ const placeholder = `\uE000${sanctuaryCounter++}\uE001`;
262
+ sanctuaries.set(placeholder, {
263
+ type: "link",
264
+ original: match,
265
+ linkText,
266
+ url
267
+ });
218
268
  return placeholder;
219
269
  });
270
+ return { protectedText, sanctuaries };
271
+ }
272
+ /**
273
+ * Restore and transform sanctuaries back to HTML
274
+ * @param {string} html - HTML with sanctuary placeholders
275
+ * @param {Map} sanctuaries - Map of sanctuaries to restore
276
+ * @returns {string} HTML with sanctuaries restored and transformed
277
+ */
278
+ static restoreAndTransformSanctuaries(html, sanctuaries) {
279
+ const placeholders = Array.from(sanctuaries.keys()).sort((a, b) => {
280
+ const indexA = html.indexOf(a);
281
+ const indexB = html.indexOf(b);
282
+ return indexA - indexB;
283
+ });
284
+ placeholders.forEach((placeholder) => {
285
+ const sanctuary = sanctuaries.get(placeholder);
286
+ let replacement;
287
+ if (sanctuary.type === "code") {
288
+ replacement = `<code><span class="syntax-marker">${sanctuary.openTicks}</span>${this.escapeHtml(sanctuary.content)}<span class="syntax-marker">${sanctuary.closeTicks}</span></code>`;
289
+ } else if (sanctuary.type === "link") {
290
+ let processedLinkText = sanctuary.linkText;
291
+ sanctuaries.forEach((innerSanctuary, innerPlaceholder) => {
292
+ if (processedLinkText.includes(innerPlaceholder)) {
293
+ if (innerSanctuary.type === "code") {
294
+ const codeHtml = `<code><span class="syntax-marker">${innerSanctuary.openTicks}</span>${this.escapeHtml(innerSanctuary.content)}<span class="syntax-marker">${innerSanctuary.closeTicks}</span></code>`;
295
+ processedLinkText = processedLinkText.replace(innerPlaceholder, codeHtml);
296
+ }
297
+ }
298
+ });
299
+ processedLinkText = this.parseStrikethrough(processedLinkText);
300
+ processedLinkText = this.parseBold(processedLinkText);
301
+ processedLinkText = this.parseItalic(processedLinkText);
302
+ const anchorName = `--link-${this.linkIndex++}`;
303
+ const safeUrl = this.sanitizeUrl(sanctuary.url);
304
+ replacement = `<a href="${safeUrl}" style="anchor-name: ${anchorName}"><span class="syntax-marker">[</span>${processedLinkText}<span class="syntax-marker url-part">](${this.escapeHtml(sanctuary.url)})</span></a>`;
305
+ }
306
+ html = html.replace(placeholder, replacement);
307
+ });
308
+ return html;
309
+ }
310
+ /**
311
+ * Parse all inline elements in correct order
312
+ * @param {string} text - Text with potential inline markdown
313
+ * @returns {string} HTML with all inline styling
314
+ */
315
+ static parseInlineElements(text) {
316
+ const { protectedText, sanctuaries } = this.identifyAndProtectSanctuaries(text);
317
+ let html = protectedText;
318
+ html = this.parseStrikethrough(html);
220
319
  html = this.parseBold(html);
221
320
  html = this.parseItalic(html);
222
- sanctuaries.forEach((content, placeholder) => {
223
- html = html.replace(placeholder, content);
224
- });
321
+ html = this.restoreAndTransformSanctuaries(html, sanctuaries);
225
322
  return html;
226
323
  }
227
324
  /**
@@ -351,6 +448,17 @@ var MarkdownParser = class {
351
448
  container.insertBefore(currentList, child);
352
449
  listType = newType;
353
450
  }
451
+ const indentationNodes = [];
452
+ for (const node of child.childNodes) {
453
+ if (node.nodeType === 3 && node.textContent.match(/^\u00A0+$/)) {
454
+ indentationNodes.push(node.cloneNode(true));
455
+ } else if (node === listItem) {
456
+ break;
457
+ }
458
+ }
459
+ indentationNodes.forEach((node) => {
460
+ listItem.insertBefore(node, listItem.firstChild);
461
+ });
354
462
  currentList.appendChild(listItem);
355
463
  child.remove();
356
464
  } else {
@@ -368,15 +476,35 @@ var MarkdownParser = class {
368
476
  static postProcessHTMLManual(html) {
369
477
  let processed = html;
370
478
  processed = processed.replace(/((?:<div>(?:&nbsp;)*<li class="bullet-list">.*?<\/li><\/div>\s*)+)/gs, (match) => {
371
- const items = match.match(/<li class="bullet-list">.*?<\/li>/gs) || [];
372
- if (items.length > 0) {
479
+ const divs = match.match(/<div>(?:&nbsp;)*<li class="bullet-list">.*?<\/li><\/div>/gs) || [];
480
+ if (divs.length > 0) {
481
+ const items = divs.map((div) => {
482
+ const indentMatch = div.match(/<div>((?:&nbsp;)*)<li/);
483
+ const listItemMatch = div.match(/<li class="bullet-list">.*?<\/li>/);
484
+ if (indentMatch && listItemMatch) {
485
+ const indentation = indentMatch[1];
486
+ const listItem = listItemMatch[0];
487
+ return listItem.replace(/<li class="bullet-list">/, `<li class="bullet-list">${indentation}`);
488
+ }
489
+ return listItemMatch ? listItemMatch[0] : "";
490
+ }).filter(Boolean);
373
491
  return "<ul>" + items.join("") + "</ul>";
374
492
  }
375
493
  return match;
376
494
  });
377
495
  processed = processed.replace(/((?:<div>(?:&nbsp;)*<li class="ordered-list">.*?<\/li><\/div>\s*)+)/gs, (match) => {
378
- const items = match.match(/<li class="ordered-list">.*?<\/li>/gs) || [];
379
- if (items.length > 0) {
496
+ const divs = match.match(/<div>(?:&nbsp;)*<li class="ordered-list">.*?<\/li><\/div>/gs) || [];
497
+ if (divs.length > 0) {
498
+ const items = divs.map((div) => {
499
+ const indentMatch = div.match(/<div>((?:&nbsp;)*)<li/);
500
+ const listItemMatch = div.match(/<li class="ordered-list">.*?<\/li>/);
501
+ if (indentMatch && listItemMatch) {
502
+ const indentation = indentMatch[1];
503
+ const listItem = listItemMatch[0];
504
+ return listItem.replace(/<li class="ordered-list">/, `<li class="ordered-list">${indentation}`);
505
+ }
506
+ return listItemMatch ? listItemMatch[0] : "";
507
+ }).filter(Boolean);
380
508
  return "<ol>" + items.join("") + "</ol>";
381
509
  }
382
510
  return match;
@@ -1918,6 +2046,14 @@ function generateStyles(options = {}) {
1918
2046
  font-style: italic !important;
1919
2047
  }
1920
2048
 
2049
+ /* Strikethrough text */
2050
+ .overtype-wrapper .overtype-preview del {
2051
+ color: var(--del, #ee964b) !important;
2052
+ text-decoration: line-through !important;
2053
+ text-decoration-color: var(--del, #ee964b) !important;
2054
+ text-decoration-thickness: 1px !important;
2055
+ }
2056
+
1921
2057
  /* Inline code */
1922
2058
  .overtype-wrapper .overtype-preview code {
1923
2059
  background: var(--code-bg, rgba(244, 211, 94, 0.4)) !important;
@@ -2061,10 +2197,10 @@ function generateStyles(options = {}) {
2061
2197
  height: 8px !important;
2062
2198
  background: #4caf50 !important;
2063
2199
  border-radius: 50% !important;
2064
- animation: pulse 2s infinite !important;
2200
+ animation: overtype-pulse 2s infinite !important;
2065
2201
  }
2066
2202
 
2067
- @keyframes pulse {
2203
+ @keyframes overtype-pulse {
2068
2204
  0%, 100% { opacity: 1; transform: scale(1); }
2069
2205
  50% { opacity: 0.6; transform: scale(1.2); }
2070
2206
  }
@@ -2072,19 +2208,19 @@ function generateStyles(options = {}) {
2072
2208
 
2073
2209
  /* Toolbar Styles */
2074
2210
  .overtype-toolbar {
2075
- display: flex;
2076
- align-items: center;
2077
- gap: 4px;
2211
+ display: flex !important;
2212
+ align-items: center !important;
2213
+ gap: 4px !important;
2078
2214
  padding: 8px !important; /* Override reset */
2079
2215
  background: var(--toolbar-bg, var(--bg-primary, #f8f9fa)) !important; /* Override reset */
2080
2216
  overflow-x: auto !important; /* Allow horizontal scrolling */
2081
2217
  overflow-y: hidden !important; /* Hide vertical overflow */
2082
- -webkit-overflow-scrolling: touch;
2083
- flex-shrink: 0;
2218
+ -webkit-overflow-scrolling: touch !important;
2219
+ flex-shrink: 0 !important;
2084
2220
  height: auto !important;
2085
2221
  grid-row: 1 !important; /* Always first row in grid */
2086
2222
  position: relative !important; /* Override reset */
2087
- z-index: 100; /* Ensure toolbar is above wrapper */
2223
+ z-index: 100 !important; /* Ensure toolbar is above wrapper */
2088
2224
  scrollbar-width: thin; /* Thin scrollbar on Firefox */
2089
2225
  }
2090
2226
 
@@ -2466,20 +2602,67 @@ var eyeIcon = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke
2466
2602
 
2467
2603
  // src/toolbar.js
2468
2604
  var Toolbar = class {
2469
- constructor(editor) {
2605
+ constructor(editor, buttonConfig = null) {
2470
2606
  this.editor = editor;
2471
2607
  this.container = null;
2472
2608
  this.buttons = {};
2609
+ this.buttonConfig = buttonConfig;
2610
+ }
2611
+ /**
2612
+ * Check if cursor/selection is inside a markdown link
2613
+ * @param {HTMLTextAreaElement} textarea - The textarea element
2614
+ * @returns {boolean} True if inside a link
2615
+ */
2616
+ isInsideLink(textarea) {
2617
+ const value = textarea.value;
2618
+ const start = textarea.selectionStart;
2619
+ const end = textarea.selectionEnd;
2620
+ let insideLink = false;
2621
+ let openBracket = -1;
2622
+ let closeBracket = -1;
2623
+ for (let i = start - 1; i >= 0; i--) {
2624
+ if (value[i] === "[") {
2625
+ openBracket = i;
2626
+ break;
2627
+ }
2628
+ if (value[i] === "\n") {
2629
+ break;
2630
+ }
2631
+ }
2632
+ if (openBracket >= 0) {
2633
+ for (let i = end; i < value.length - 1; i++) {
2634
+ if (value[i] === "]" && value[i + 1] === "(") {
2635
+ closeBracket = i;
2636
+ break;
2637
+ }
2638
+ if (value[i] === "\n") {
2639
+ break;
2640
+ }
2641
+ }
2642
+ }
2643
+ if (openBracket >= 0 && closeBracket >= 0) {
2644
+ for (let i = closeBracket + 2; i < value.length; i++) {
2645
+ if (value[i] === ")") {
2646
+ insideLink = true;
2647
+ break;
2648
+ }
2649
+ if (value[i] === "\n" || value[i] === " ") {
2650
+ break;
2651
+ }
2652
+ }
2653
+ }
2654
+ return insideLink;
2473
2655
  }
2474
2656
  /**
2475
2657
  * Create and attach toolbar to editor
2476
2658
  */
2477
2659
  create() {
2660
+ var _a;
2478
2661
  this.container = document.createElement("div");
2479
2662
  this.container.className = "overtype-toolbar";
2480
2663
  this.container.setAttribute("role", "toolbar");
2481
2664
  this.container.setAttribute("aria-label", "Text formatting");
2482
- const buttonConfig = [
2665
+ const buttonConfig = (_a = this.buttonConfig) != null ? _a : [
2483
2666
  { name: "bold", icon: boldIcon, title: "Bold (Ctrl+B)", action: "toggleBold" },
2484
2667
  { name: "italic", icon: italicIcon, title: "Italic (Ctrl+I)", action: "toggleItalic" },
2485
2668
  { separator: true },
@@ -2573,6 +2756,9 @@ var Toolbar = class {
2573
2756
  insertLink(textarea);
2574
2757
  break;
2575
2758
  case "toggleCode":
2759
+ if (this.isInsideLink(textarea)) {
2760
+ return;
2761
+ }
2576
2762
  toggleCode(textarea);
2577
2763
  break;
2578
2764
  case "toggleBulletList":
@@ -2788,29 +2974,29 @@ var LinkTooltip = class {
2788
2974
  position: absolute;
2789
2975
  position-anchor: var(--target-anchor, --link-0);
2790
2976
  position-area: block-end center;
2791
- margin-top: 8px;
2977
+ margin-top: 8px !important;
2792
2978
 
2793
- background: #333;
2794
- color: white;
2795
- padding: 6px 10px;
2796
- border-radius: 16px;
2797
- font-size: 12px;
2798
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
2799
- display: none;
2800
- z-index: 10000;
2801
- cursor: pointer;
2802
- box-shadow: 0 2px 8px rgba(0,0,0,0.3);
2803
- max-width: 300px;
2804
- white-space: nowrap;
2805
- overflow: hidden;
2806
- text-overflow: ellipsis;
2979
+ background: #333 !important;
2980
+ color: white !important;
2981
+ padding: 6px 10px !important;
2982
+ border-radius: 16px !important;
2983
+ font-size: 12px !important;
2984
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif !important;
2985
+ display: none !important;
2986
+ z-index: 10000 !important;
2987
+ cursor: pointer !important;
2988
+ box-shadow: 0 2px 8px rgba(0,0,0,0.3) !important;
2989
+ max-width: 300px !important;
2990
+ white-space: nowrap !important;
2991
+ overflow: hidden !important;
2992
+ text-overflow: ellipsis !important;
2807
2993
 
2808
2994
  position-try: most-width block-end inline-end, flip-inline, block-start center;
2809
2995
  position-visibility: anchors-visible;
2810
2996
  }
2811
2997
 
2812
2998
  .overtype-link-tooltip.visible {
2813
- display: flex;
2999
+ display: flex !important;
2814
3000
  }
2815
3001
  }
2816
3002
  `;
@@ -2958,7 +3144,8 @@ var _OverType = class _OverType {
2958
3144
  this.shortcuts = new ShortcutsManager(this);
2959
3145
  this.linkTooltip = new LinkTooltip(this);
2960
3146
  if (this.options.toolbar) {
2961
- this.toolbar = new Toolbar(this);
3147
+ const toolbarButtons = typeof this.options.toolbar === "object" ? this.options.toolbar.buttons : null;
3148
+ this.toolbar = new Toolbar(this, toolbarButtons);
2962
3149
  this.toolbar.create();
2963
3150
  this.textarea.addEventListener("selectionchange", () => {
2964
3151
  this.toolbar.updateButtonStates();
@@ -3440,24 +3627,36 @@ var _OverType = class _OverType {
3440
3627
  }
3441
3628
  /**
3442
3629
  * Get the rendered HTML of the current content
3443
- * @param {boolean} processForPreview - If true, post-processes HTML for preview mode (consolidates lists/code blocks)
3630
+ * @param {Object} options - Rendering options
3631
+ * @param {boolean} options.cleanHTML - If true, removes syntax markers and OverType-specific classes
3444
3632
  * @returns {string} Rendered HTML
3445
3633
  */
3446
- getRenderedHTML(processForPreview = false) {
3634
+ getRenderedHTML(options = {}) {
3447
3635
  const markdown = this.getValue();
3448
3636
  let html = MarkdownParser.parse(markdown);
3449
- if (processForPreview) {
3450
- html = MarkdownParser.postProcessHTML(html);
3637
+ if (options.cleanHTML) {
3638
+ html = html.replace(/<span class="syntax-marker[^"]*">.*?<\/span>/g, "");
3639
+ html = html.replace(/\sclass="(bullet-list|ordered-list|code-fence|hr-marker|blockquote|url-part)"/g, "");
3640
+ html = html.replace(/\sclass=""/g, "");
3451
3641
  }
3452
3642
  return html;
3453
3643
  }
3454
3644
  /**
3455
3645
  * Get the current preview element's HTML
3646
+ * This includes all syntax markers and OverType styling
3456
3647
  * @returns {string} Current preview HTML (as displayed)
3457
3648
  */
3458
3649
  getPreviewHTML() {
3459
3650
  return this.preview.innerHTML;
3460
3651
  }
3652
+ /**
3653
+ * Get clean HTML without any OverType-specific markup
3654
+ * Useful for exporting to other formats or storage
3655
+ * @returns {string} Clean HTML suitable for export
3656
+ */
3657
+ getCleanHTML() {
3658
+ return this.getRenderedHTML({ cleanHTML: true });
3659
+ }
3461
3660
  /**
3462
3661
  * Focus the editor
3463
3662
  */
@@ -3776,9 +3975,6 @@ OverType.ShortcutsManager = ShortcutsManager;
3776
3975
  OverType.themes = { solar, cave: getTheme("cave") };
3777
3976
  OverType.getTheme = getTheme;
3778
3977
  OverType.currentTheme = solar;
3779
- if (typeof window !== "undefined" && typeof window.document !== "undefined") {
3780
- window.OverType = OverType;
3781
- }
3782
3978
  var overtype_default = OverType;
3783
3979
  // Annotate the CommonJS export names for ESM import in node:
3784
3980
  0 && (module.exports = {