overtype 2.0.0 → 2.0.2

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,6 @@
1
1
  # OverType
2
2
 
3
- A lightweight markdown editor library with perfect WYSIWYG alignment using an invisible textarea overlay technique. Includes optional toolbar. ~91KB minified with all features.
3
+ A lightweight markdown editor library with perfect WYSIWYG alignment using an invisible textarea overlay technique. Includes optional toolbar. ~92KB minified with all features.
4
4
 
5
5
  ## Live Examples
6
6
 
@@ -19,7 +19,7 @@ A lightweight markdown editor library with perfect WYSIWYG alignment using an in
19
19
  - ⌨️ **Keyboard shortcuts** - Common markdown shortcuts (Cmd/Ctrl+B for bold, etc.)
20
20
  - 📱 **Mobile optimized** - Responsive design with mobile-specific styles
21
21
  - 🔄 **DOM persistence aware** - Recovers from existing DOM (perfect for HyperClay and similar platforms)
22
- - 🚀 **Lightweight** - ~91KB minified
22
+ - 🚀 **Lightweight** - ~92KB minified
23
23
  - 🎯 **Optional toolbar** - Clean, minimal toolbar with all essential formatting
24
24
  - ✨ **Smart shortcuts** - Keyboard shortcuts with selection preservation
25
25
  - 📝 **Smart list continuation** - GitHub-style automatic list continuation on Enter
@@ -35,7 +35,7 @@ We overlap an invisible textarea on top of styled output, giving the illusion of
35
35
 
36
36
  | Feature | OverType | HyperMD | Milkdown | TUI Editor | EasyMDE |
37
37
  |---------|----------|---------|----------|------------|---------|
38
- | **Size** | ~91KB | 364.02 KB | 344.51 KB | 560.99 KB | 323.69 KB |
38
+ | **Size** | ~92KB | 364.02 KB | 344.51 KB | 560.99 KB | 323.69 KB |
39
39
  | **Dependencies** | Bundled | CodeMirror | ProseMirror + plugins | Multiple libs | CodeMirror |
40
40
  | **Setup** | Single file | Complex config | Build step required | Complex config | Moderate |
41
41
  | **Approach** | Invisible textarea | ContentEditable | ContentEditable | ContentEditable | CodeMirror |
@@ -740,6 +740,7 @@ Special thanks to:
740
740
  - [kbhomes](https://github.com/kbhomes) - Fixed text selection desynchronization during overscroll ([#17](https://github.com/panphora/overtype/pull/17))
741
741
  - [Kristián Kostecký](https://github.com/kristiankostecky) - Fixed toolbar option being ignored in reinit() ([#62](https://github.com/panphora/overtype/pull/62))
742
742
  - [Lyric Wai](https://github.com/lyricat) - Fixed double-escaping of links ([#64](https://github.com/panphora/overtype/pull/64)), reported code block alignment issues ([#65](https://github.com/panphora/overtype/issues/65))
743
+ - [kozi](https://github.com/kozi) - Reported link tooltip issues in Firefox ([#68](https://github.com/panphora/overtype/issues/68)) and toolbar positioning ([#69](https://github.com/panphora/overtype/issues/69))
743
744
 
744
745
  ### TypeScript & Framework Support
745
746
  - [merlinz01](https://github.com/merlinz01) - Initial TypeScript definitions implementation ([#20](https://github.com/panphora/overtype/pull/20))
@@ -766,12 +767,12 @@ MIT
766
767
 
767
768
  Contributions are welcome! Please feel free to submit a Pull Request.
768
769
 
770
+ <p>&nbsp;</p>
771
+
769
772
  ---
770
773
 
771
774
  Built with the radical idea that sometimes dumb ideas work.
772
775
 
773
- ---
774
-
775
776
  **Ready for another radical idea?**
776
777
  Let's remove every layer of the web application stack.
777
778
 
@@ -1,5 +1,5 @@
1
1
  /**
2
- * OverType v2.0.0
2
+ * OverType v2.0.2
3
3
  * A lightweight markdown editor library with perfect WYSIWYG alignment
4
4
  * @license MIT
5
5
  * @author Demo User
@@ -105,7 +105,7 @@ var MarkdownParser = class {
105
105
  return html.replace(/^((?:&nbsp;)*)-\s+\[([ xX])\]\s+(.+)$/, (match, indent, checked, content) => {
106
106
  if (isPreviewMode) {
107
107
  const isChecked = checked.toLowerCase() === "x";
108
- return `${indent}<li class="task-list"><input type="checkbox" disabled ${isChecked ? "checked" : ""}> ${content}</li>`;
108
+ return `${indent}<li class="task-list"><input type="checkbox" ${isChecked ? "checked" : ""}> ${content}</li>`;
109
109
  } else {
110
110
  return `${indent}<li class="task-list"><span class="syntax-marker">- [${checked}] </span>${content}</li>`;
111
111
  }
@@ -1839,8 +1839,8 @@ function generateStyles(options = {}) {
1839
1839
 
1840
1840
  /* Container base styles after reset */
1841
1841
  .overtype-container {
1842
- display: grid !important;
1843
- grid-template-rows: auto 1fr auto !important;
1842
+ display: flex !important;
1843
+ flex-direction: column !important;
1844
1844
  width: 100% !important;
1845
1845
  height: 100% !important;
1846
1846
  position: relative !important; /* Override reset - needed for absolute children */
@@ -1860,10 +1860,10 @@ function generateStyles(options = {}) {
1860
1860
  /* Auto-resize mode styles */
1861
1861
  .overtype-container.overtype-auto-resize {
1862
1862
  height: auto !important;
1863
- grid-template-rows: auto auto auto !important;
1864
1863
  }
1865
-
1864
+
1866
1865
  .overtype-container.overtype-auto-resize .overtype-wrapper {
1866
+ flex: 0 0 auto !important; /* Don't grow/shrink, use explicit height */
1867
1867
  height: auto !important;
1868
1868
  min-height: 60px !important;
1869
1869
  overflow: visible !important;
@@ -1872,11 +1872,10 @@ function generateStyles(options = {}) {
1872
1872
  .overtype-wrapper {
1873
1873
  position: relative !important; /* Override reset - needed for absolute children */
1874
1874
  width: 100% !important;
1875
- height: 100% !important; /* Take full height of grid cell */
1875
+ flex: 1 1 0 !important; /* Grow to fill remaining space, with flex-basis: 0 */
1876
1876
  min-height: 60px !important; /* Minimum usable height */
1877
1877
  overflow: hidden !important;
1878
1878
  background: var(--bg-secondary, #ffffff) !important;
1879
- grid-row: 2 !important; /* Always second row in grid */
1880
1879
  z-index: 1; /* Below toolbar and dropdown */
1881
1880
  }
1882
1881
 
@@ -2211,7 +2210,7 @@ function generateStyles(options = {}) {
2211
2210
 
2212
2211
  /* Stats bar */
2213
2212
 
2214
- /* Stats bar - positioned by grid, not absolute */
2213
+ /* Stats bar - positioned by flexbox */
2215
2214
  .overtype-stats {
2216
2215
  height: 40px !important;
2217
2216
  padding: 0 20px !important;
@@ -2223,7 +2222,7 @@ function generateStyles(options = {}) {
2223
2222
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif !important;
2224
2223
  font-size: 0.85rem !important;
2225
2224
  color: #666 !important;
2226
- grid-row: 3 !important; /* Always third row in grid */
2225
+ flex-shrink: 0 !important; /* Don't shrink */
2227
2226
  }
2228
2227
 
2229
2228
  /* Dark theme stats bar */
@@ -2266,7 +2265,6 @@ function generateStyles(options = {}) {
2266
2265
  -webkit-overflow-scrolling: touch !important;
2267
2266
  flex-shrink: 0 !important;
2268
2267
  height: auto !important;
2269
- grid-row: 1 !important; /* Always first row in grid */
2270
2268
  position: relative !important; /* Override reset */
2271
2269
  z-index: 100 !important; /* Ensure toolbar is above wrapper */
2272
2270
  scrollbar-width: thin; /* Thin scrollbar on Firefox */
@@ -2336,9 +2334,6 @@ function generateStyles(options = {}) {
2336
2334
  }
2337
2335
 
2338
2336
  /* Adjust wrapper when toolbar is present */
2339
- .overtype-container .overtype-toolbar + .overtype-wrapper {
2340
- }
2341
-
2342
2337
  /* Mobile toolbar adjustments */
2343
2338
  @media (max-width: 640px) {
2344
2339
  .overtype-toolbar {
@@ -2609,36 +2604,42 @@ function generateStyles(options = {}) {
2609
2604
  height: 2px !important;
2610
2605
  }
2611
2606
 
2612
- /* Link Tooltip - CSS Anchor Positioning */
2607
+ /* Link Tooltip - Base styles (all browsers) */
2608
+ .overtype-link-tooltip {
2609
+ /* Visual styles that work for both positioning methods */
2610
+ background: #333 !important;
2611
+ color: white !important;
2612
+ padding: 6px 10px !important;
2613
+ border-radius: 16px !important;
2614
+ font-size: 12px !important;
2615
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif !important;
2616
+ display: none !important;
2617
+ z-index: 10000 !important;
2618
+ cursor: pointer !important;
2619
+ box-shadow: 0 2px 8px rgba(0,0,0,0.3) !important;
2620
+ max-width: 300px !important;
2621
+ white-space: nowrap !important;
2622
+ overflow: hidden !important;
2623
+ text-overflow: ellipsis !important;
2624
+
2625
+ /* Base positioning for Floating UI fallback */
2626
+ position: absolute;
2627
+ }
2628
+
2629
+ .overtype-link-tooltip.visible {
2630
+ display: flex !important;
2631
+ }
2632
+
2633
+ /* CSS Anchor Positioning (modern browsers only) */
2613
2634
  @supports (position-anchor: --x) and (position-area: center) {
2614
2635
  .overtype-link-tooltip {
2615
- position: absolute;
2636
+ /* Only anchor positioning specific properties */
2616
2637
  position-anchor: var(--target-anchor, --link-0);
2617
2638
  position-area: block-end center;
2618
2639
  margin-top: 8px !important;
2619
-
2620
- background: #333 !important;
2621
- color: white !important;
2622
- padding: 6px 10px !important;
2623
- border-radius: 16px !important;
2624
- font-size: 12px !important;
2625
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif !important;
2626
- display: none !important;
2627
- z-index: 10000 !important;
2628
- cursor: pointer !important;
2629
- box-shadow: 0 2px 8px rgba(0,0,0,0.3) !important;
2630
- max-width: 300px !important;
2631
- white-space: nowrap !important;
2632
- overflow: hidden !important;
2633
- text-overflow: ellipsis !important;
2634
-
2635
2640
  position-try: most-width block-end inline-end, flip-inline, block-start center;
2636
2641
  position-visibility: anchors-visible;
2637
2642
  }
2638
-
2639
- .overtype-link-tooltip.visible {
2640
- display: flex !important;
2641
- }
2642
2643
  }
2643
2644
 
2644
2645
  ${mobileStyles}
@@ -2671,7 +2672,7 @@ var Toolbar = class {
2671
2672
  this.container.appendChild(button);
2672
2673
  }
2673
2674
  });
2674
- this.editor.wrapper.insertBefore(this.container, this.editor.wrapper.firstChild);
2675
+ this.editor.container.insertBefore(this.container, this.editor.wrapper);
2675
2676
  }
2676
2677
  /**
2677
2678
  * Create a toolbar separator
@@ -2896,9 +2897,26 @@ var LinkTooltip = class {
2896
2897
  this.currentLink = null;
2897
2898
  this.hideTimeout = null;
2898
2899
  this.visibilityChangeHandler = null;
2900
+ this.useFloatingUI = false;
2901
+ this.floatingUI = null;
2899
2902
  this.init();
2900
2903
  }
2901
- init() {
2904
+ async init() {
2905
+ const supportsAnchorPositioning = CSS.supports("position-anchor: --x") && CSS.supports("position-area: center");
2906
+ if (!supportsAnchorPositioning) {
2907
+ try {
2908
+ const importFn = new Function("url", "return import(url)");
2909
+ const { computePosition, offset, shift, flip } = await importFn(
2910
+ "https://cdn.jsdelivr.net/npm/@floating-ui/dom@1.7.4/+esm"
2911
+ );
2912
+ this.floatingUI = { computePosition, offset, shift, flip };
2913
+ this.useFloatingUI = true;
2914
+ } catch (error) {
2915
+ console.warn("Failed to load Floating UI fallback:", error);
2916
+ this.floatingUI = null;
2917
+ this.useFloatingUI = false;
2918
+ }
2919
+ }
2902
2920
  this.createTooltip();
2903
2921
  this.editor.textarea.addEventListener("selectionchange", () => this.checkCursorPosition());
2904
2922
  this.editor.textarea.addEventListener("keyup", (e) => {
@@ -2907,7 +2925,13 @@ var LinkTooltip = class {
2907
2925
  }
2908
2926
  });
2909
2927
  this.editor.textarea.addEventListener("input", () => this.hide());
2910
- this.editor.textarea.addEventListener("scroll", () => this.hide());
2928
+ this.editor.textarea.addEventListener("scroll", () => {
2929
+ if (this.useFloatingUI && this.currentLink) {
2930
+ this.showWithFloatingUI(this.currentLink);
2931
+ } else {
2932
+ this.hide();
2933
+ }
2934
+ });
2911
2935
  this.editor.textarea.addEventListener("blur", () => this.hide());
2912
2936
  this.visibilityChangeHandler = () => {
2913
2937
  if (document.hidden) {
@@ -2916,7 +2940,6 @@ var LinkTooltip = class {
2916
2940
  };
2917
2941
  document.addEventListener("visibilitychange", this.visibilityChangeHandler);
2918
2942
  this.tooltip.addEventListener("mouseenter", () => this.cancelHide());
2919
- this.tooltip.addEventListener("mouseleave", () => this.scheduleHide());
2920
2943
  }
2921
2944
  createTooltip() {
2922
2945
  this.tooltip = document.createElement("div");
@@ -2977,9 +3000,52 @@ var LinkTooltip = class {
2977
3000
  this.cancelHide();
2978
3001
  const urlSpan = this.tooltip.querySelector(".overtype-link-tooltip-url");
2979
3002
  urlSpan.textContent = linkInfo.url;
2980
- this.tooltip.style.setProperty("--target-anchor", `--link-${linkInfo.index}`);
3003
+ if (this.useFloatingUI) {
3004
+ this.showWithFloatingUI(linkInfo);
3005
+ } else {
3006
+ this.showWithAnchorPositioning(linkInfo);
3007
+ }
2981
3008
  this.tooltip.classList.add("visible");
2982
3009
  }
3010
+ showWithAnchorPositioning(linkInfo) {
3011
+ this.tooltip.style.setProperty("--target-anchor", `--link-${linkInfo.index}`);
3012
+ }
3013
+ async showWithFloatingUI(linkInfo) {
3014
+ const anchorElement = this.findAnchorElement(linkInfo.index);
3015
+ if (!anchorElement) {
3016
+ return;
3017
+ }
3018
+ const rect = anchorElement.getBoundingClientRect();
3019
+ if (rect.width === 0 || rect.height === 0) {
3020
+ return;
3021
+ }
3022
+ try {
3023
+ const { x, y } = await this.floatingUI.computePosition(
3024
+ anchorElement,
3025
+ this.tooltip,
3026
+ {
3027
+ placement: "bottom",
3028
+ middleware: [
3029
+ this.floatingUI.offset(8),
3030
+ this.floatingUI.shift({ padding: 8 }),
3031
+ this.floatingUI.flip()
3032
+ ]
3033
+ }
3034
+ );
3035
+ Object.assign(this.tooltip.style, {
3036
+ left: `${x}px`,
3037
+ top: `${y}px`,
3038
+ position: "absolute"
3039
+ });
3040
+ } catch (error) {
3041
+ console.warn("Floating UI positioning failed:", error);
3042
+ return;
3043
+ }
3044
+ }
3045
+ findAnchorElement(linkIndex) {
3046
+ const preview = this.editor.preview;
3047
+ return preview.querySelector(`a[style*="--link-${linkIndex}"]`);
3048
+ }
2983
3049
  hide() {
2984
3050
  this.tooltip.classList.remove("visible");
2985
3051
  this.currentLink = null;
@@ -3005,6 +3071,8 @@ var LinkTooltip = class {
3005
3071
  }
3006
3072
  this.tooltip = null;
3007
3073
  this.currentLink = null;
3074
+ this.floatingUI = null;
3075
+ this.useFloatingUI = false;
3008
3076
  }
3009
3077
  };
3010
3078
 
@@ -3459,22 +3527,10 @@ var _OverType = class _OverType {
3459
3527
  this._updateStats();
3460
3528
  }
3461
3529
  this.element.appendChild(this.container);
3462
- if (window.location.pathname.includes("demo.html")) {
3463
- console.log("_createDOM completed:", {
3464
- elementId: this.element.id,
3465
- autoResize: this.options.autoResize,
3466
- containerClasses: this.container.className,
3467
- hasStats: !!this.statsBar,
3468
- hasToolbar: this.options.toolbar
3469
- });
3470
- }
3471
3530
  if (this.options.autoResize) {
3472
3531
  this._setupAutoResize();
3473
3532
  } else {
3474
3533
  this.container.classList.remove("overtype-auto-resize");
3475
- if (window.location.pathname.includes("demo.html")) {
3476
- console.log("Removed auto-resize class from:", this.element.id);
3477
- }
3478
3534
  }
3479
3535
  }
3480
3536
  /**
@@ -3977,6 +4033,7 @@ var _OverType = class _OverType {
3977
4033
  */
3978
4034
  showNormalEditMode() {
3979
4035
  this.container.dataset.mode = "normal";
4036
+ this.updatePreview();
3980
4037
  requestAnimationFrame(() => {
3981
4038
  this.textarea.scrollTop = this.preview.scrollTop;
3982
4039
  this.textarea.scrollLeft = this.preview.scrollLeft;
@@ -4004,6 +4061,7 @@ var _OverType = class _OverType {
4004
4061
  */
4005
4062
  showPreviewMode() {
4006
4063
  this.container.dataset.mode = "preview";
4064
+ this.updatePreview();
4007
4065
  return this;
4008
4066
  }
4009
4067
  /**