overtype 2.0.1 → 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.
@@ -1,7 +1,7 @@
1
1
  /**
2
- * Link Tooltip - CSS Anchor Positioning with index-based anchors
2
+ * Link Tooltip - CSS Anchor Positioning with Floating UI fallback
3
3
  * Shows a clickable tooltip when cursor is within a link
4
- * Uses CSS anchor positioning with dynamically selected anchor
4
+ * Uses CSS anchor positioning for modern browsers, Floating UI for older browsers
5
5
  */
6
6
 
7
7
  export class LinkTooltip {
@@ -11,11 +11,35 @@ export class LinkTooltip {
11
11
  this.currentLink = null;
12
12
  this.hideTimeout = null;
13
13
  this.visibilityChangeHandler = null;
14
+ this.useFloatingUI = false;
15
+ this.floatingUI = null;
14
16
 
15
17
  this.init();
16
18
  }
17
19
 
18
- init() {
20
+ async init() {
21
+ // Detect CSS anchor positioning support
22
+ const supportsAnchorPositioning = CSS.supports('position-anchor: --x') &&
23
+ CSS.supports('position-area: center');
24
+
25
+ // Load Floating UI if needed
26
+ if (!supportsAnchorPositioning) {
27
+ try {
28
+ // Use indirect eval to prevent bundler from processing the import
29
+ const importFn = new Function('url', 'return import(url)');
30
+ const { computePosition, offset, shift, flip } = await importFn(
31
+ 'https://cdn.jsdelivr.net/npm/@floating-ui/dom@1.7.4/+esm'
32
+ );
33
+ this.floatingUI = { computePosition, offset, shift, flip };
34
+ this.useFloatingUI = true;
35
+ } catch (error) {
36
+ // If dynamic import fails, tooltips simply won't show
37
+ console.warn('Failed to load Floating UI fallback:', error);
38
+ this.floatingUI = null;
39
+ this.useFloatingUI = false;
40
+ }
41
+ }
42
+
19
43
  // Create tooltip element
20
44
  // Note: Styles are now in the main stylesheet (styles.js) with @supports wrapper
21
45
  this.createTooltip();
@@ -28,9 +52,19 @@ export class LinkTooltip {
28
52
  }
29
53
  });
30
54
 
31
- // Hide tooltip when typing or scrolling
55
+ // Hide tooltip when typing
32
56
  this.editor.textarea.addEventListener('input', () => this.hide());
33
- this.editor.textarea.addEventListener('scroll', () => this.hide());
57
+
58
+ // Reposition or hide tooltip when scrolling
59
+ this.editor.textarea.addEventListener('scroll', () => {
60
+ if (this.useFloatingUI && this.currentLink) {
61
+ // Reposition the tooltip for Floating UI
62
+ this.showWithFloatingUI(this.currentLink);
63
+ } else {
64
+ // Hide for CSS anchor positioning (native browser behavior handles this)
65
+ this.hide();
66
+ }
67
+ });
34
68
 
35
69
  // Hide tooltip when textarea loses focus
36
70
  this.editor.textarea.addEventListener('blur', () => this.hide());
@@ -43,9 +77,8 @@ export class LinkTooltip {
43
77
  };
44
78
  document.addEventListener('visibilitychange', this.visibilityChangeHandler);
45
79
 
46
- // Keep tooltip visible on hover
80
+ // Keep tooltip visible on hover (only prevent hide, don't schedule hide on leave)
47
81
  this.tooltip.addEventListener('mouseenter', () => this.cancelHide());
48
- this.tooltip.addEventListener('mouseleave', () => this.scheduleHide());
49
82
  }
50
83
 
51
84
  createTooltip() {
@@ -128,11 +161,67 @@ export class LinkTooltip {
128
161
  const urlSpan = this.tooltip.querySelector('.overtype-link-tooltip-url');
129
162
  urlSpan.textContent = linkInfo.url;
130
163
 
164
+ if (this.useFloatingUI) {
165
+ this.showWithFloatingUI(linkInfo);
166
+ } else {
167
+ this.showWithAnchorPositioning(linkInfo);
168
+ }
169
+
170
+ this.tooltip.classList.add('visible');
171
+ }
172
+
173
+ showWithAnchorPositioning(linkInfo) {
131
174
  // Set the CSS variable to point to the correct anchor
132
175
  this.tooltip.style.setProperty('--target-anchor', `--link-${linkInfo.index}`);
176
+ }
133
177
 
134
- // Show tooltip (CSS anchor positioning handles the rest)
135
- this.tooltip.classList.add('visible');
178
+ async showWithFloatingUI(linkInfo) {
179
+ // Find the <a> element in preview that corresponds to this link
180
+ const anchorElement = this.findAnchorElement(linkInfo.index);
181
+
182
+ if (!anchorElement) {
183
+ return;
184
+ }
185
+
186
+ // Check if anchor element is visible and in viewport
187
+ const rect = anchorElement.getBoundingClientRect();
188
+ if (rect.width === 0 || rect.height === 0) {
189
+ return;
190
+ }
191
+
192
+ try {
193
+ // Compute position using Floating UI
194
+ const { x, y } = await this.floatingUI.computePosition(
195
+ anchorElement,
196
+ this.tooltip,
197
+ {
198
+ placement: 'bottom',
199
+ middleware: [
200
+ this.floatingUI.offset(8),
201
+ this.floatingUI.shift({ padding: 8 }),
202
+ this.floatingUI.flip()
203
+ ]
204
+ }
205
+ );
206
+
207
+ // Apply position
208
+ Object.assign(this.tooltip.style, {
209
+ left: `${x}px`,
210
+ top: `${y}px`,
211
+ position: 'absolute'
212
+ });
213
+ } catch (error) {
214
+ // If Floating UI computation fails, don't show tooltip
215
+ console.warn('Floating UI positioning failed:', error);
216
+ return;
217
+ }
218
+ }
219
+
220
+ findAnchorElement(linkIndex) {
221
+ // Find the <a> element with the matching anchor-name style
222
+ const preview = this.editor.preview;
223
+ // Direct query for the specific link - more efficient than iterating
224
+ return preview.querySelector(`a[style*="--link-${linkIndex}"]`);
136
225
  }
137
226
 
138
227
  hide() {
@@ -166,5 +255,7 @@ export class LinkTooltip {
166
255
  }
167
256
  this.tooltip = null;
168
257
  this.currentLink = null;
258
+ this.floatingUI = null;
259
+ this.useFloatingUI = false;
169
260
  }
170
261
  }
package/src/overtype.js CHANGED
@@ -362,27 +362,12 @@ class OverType {
362
362
  // Add container to element
363
363
  this.element.appendChild(this.container);
364
364
 
365
- // Debug logging
366
- if (window.location.pathname.includes('demo.html')) {
367
- console.log('_createDOM completed:', {
368
- elementId: this.element.id,
369
- autoResize: this.options.autoResize,
370
- containerClasses: this.container.className,
371
- hasStats: !!this.statsBar,
372
- hasToolbar: this.options.toolbar
373
- });
374
- }
375
-
376
365
  // Setup auto-resize if enabled
377
366
  if (this.options.autoResize) {
378
367
  this._setupAutoResize();
379
368
  } else {
380
369
  // Ensure auto-resize class is removed if not using auto-resize
381
370
  this.container.classList.remove('overtype-auto-resize');
382
-
383
- if (window.location.pathname.includes('demo.html')) {
384
- console.log('Removed auto-resize class from:', this.element.id);
385
- }
386
371
  }
387
372
  }
388
373
 
package/src/styles.js CHANGED
@@ -72,8 +72,8 @@ export function generateStyles(options = {}) {
72
72
 
73
73
  /* Container base styles after reset */
74
74
  .overtype-container {
75
- display: grid !important;
76
- grid-template-rows: auto 1fr auto !important;
75
+ display: flex !important;
76
+ flex-direction: column !important;
77
77
  width: 100% !important;
78
78
  height: 100% !important;
79
79
  position: relative !important; /* Override reset - needed for absolute children */
@@ -93,10 +93,10 @@ export function generateStyles(options = {}) {
93
93
  /* Auto-resize mode styles */
94
94
  .overtype-container.overtype-auto-resize {
95
95
  height: auto !important;
96
- grid-template-rows: auto auto auto !important;
97
96
  }
98
-
97
+
99
98
  .overtype-container.overtype-auto-resize .overtype-wrapper {
99
+ flex: 0 0 auto !important; /* Don't grow/shrink, use explicit height */
100
100
  height: auto !important;
101
101
  min-height: 60px !important;
102
102
  overflow: visible !important;
@@ -105,11 +105,10 @@ export function generateStyles(options = {}) {
105
105
  .overtype-wrapper {
106
106
  position: relative !important; /* Override reset - needed for absolute children */
107
107
  width: 100% !important;
108
- height: 100% !important; /* Take full height of grid cell */
108
+ flex: 1 1 0 !important; /* Grow to fill remaining space, with flex-basis: 0 */
109
109
  min-height: 60px !important; /* Minimum usable height */
110
110
  overflow: hidden !important;
111
111
  background: var(--bg-secondary, #ffffff) !important;
112
- grid-row: 2 !important; /* Always second row in grid */
113
112
  z-index: 1; /* Below toolbar and dropdown */
114
113
  }
115
114
 
@@ -444,7 +443,7 @@ export function generateStyles(options = {}) {
444
443
 
445
444
  /* Stats bar */
446
445
 
447
- /* Stats bar - positioned by grid, not absolute */
446
+ /* Stats bar - positioned by flexbox */
448
447
  .overtype-stats {
449
448
  height: 40px !important;
450
449
  padding: 0 20px !important;
@@ -456,7 +455,7 @@ export function generateStyles(options = {}) {
456
455
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif !important;
457
456
  font-size: 0.85rem !important;
458
457
  color: #666 !important;
459
- grid-row: 3 !important; /* Always third row in grid */
458
+ flex-shrink: 0 !important; /* Don't shrink */
460
459
  }
461
460
 
462
461
  /* Dark theme stats bar */
@@ -499,7 +498,6 @@ export function generateStyles(options = {}) {
499
498
  -webkit-overflow-scrolling: touch !important;
500
499
  flex-shrink: 0 !important;
501
500
  height: auto !important;
502
- grid-row: 1 !important; /* Always first row in grid */
503
501
  position: relative !important; /* Override reset */
504
502
  z-index: 100 !important; /* Ensure toolbar is above wrapper */
505
503
  scrollbar-width: thin; /* Thin scrollbar on Firefox */
@@ -569,9 +567,6 @@ export function generateStyles(options = {}) {
569
567
  }
570
568
 
571
569
  /* Adjust wrapper when toolbar is present */
572
- .overtype-container .overtype-toolbar + .overtype-wrapper {
573
- }
574
-
575
570
  /* Mobile toolbar adjustments */
576
571
  @media (max-width: 640px) {
577
572
  .overtype-toolbar {
@@ -842,36 +837,42 @@ export function generateStyles(options = {}) {
842
837
  height: 2px !important;
843
838
  }
844
839
 
845
- /* Link Tooltip - CSS Anchor Positioning */
840
+ /* Link Tooltip - Base styles (all browsers) */
841
+ .overtype-link-tooltip {
842
+ /* Visual styles that work for both positioning methods */
843
+ background: #333 !important;
844
+ color: white !important;
845
+ padding: 6px 10px !important;
846
+ border-radius: 16px !important;
847
+ font-size: 12px !important;
848
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif !important;
849
+ display: none !important;
850
+ z-index: 10000 !important;
851
+ cursor: pointer !important;
852
+ box-shadow: 0 2px 8px rgba(0,0,0,0.3) !important;
853
+ max-width: 300px !important;
854
+ white-space: nowrap !important;
855
+ overflow: hidden !important;
856
+ text-overflow: ellipsis !important;
857
+
858
+ /* Base positioning for Floating UI fallback */
859
+ position: absolute;
860
+ }
861
+
862
+ .overtype-link-tooltip.visible {
863
+ display: flex !important;
864
+ }
865
+
866
+ /* CSS Anchor Positioning (modern browsers only) */
846
867
  @supports (position-anchor: --x) and (position-area: center) {
847
868
  .overtype-link-tooltip {
848
- position: absolute;
869
+ /* Only anchor positioning specific properties */
849
870
  position-anchor: var(--target-anchor, --link-0);
850
871
  position-area: block-end center;
851
872
  margin-top: 8px !important;
852
-
853
- background: #333 !important;
854
- color: white !important;
855
- padding: 6px 10px !important;
856
- border-radius: 16px !important;
857
- font-size: 12px !important;
858
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif !important;
859
- display: none !important;
860
- z-index: 10000 !important;
861
- cursor: pointer !important;
862
- box-shadow: 0 2px 8px rgba(0,0,0,0.3) !important;
863
- max-width: 300px !important;
864
- white-space: nowrap !important;
865
- overflow: hidden !important;
866
- text-overflow: ellipsis !important;
867
-
868
873
  position-try: most-width block-end inline-end, flip-inline, block-start center;
869
874
  position-visibility: anchors-visible;
870
875
  }
871
-
872
- .overtype-link-tooltip.visible {
873
- display: flex !important;
874
- }
875
876
  }
876
877
 
877
878
  ${mobileStyles}
package/src/toolbar.js CHANGED
@@ -36,7 +36,8 @@ export class Toolbar {
36
36
  }
37
37
  });
38
38
 
39
- this.editor.wrapper.insertBefore(this.container, this.editor.wrapper.firstChild);
39
+ // Insert toolbar before the wrapper (as sibling, not child)
40
+ this.editor.container.insertBefore(this.container, this.editor.wrapper);
40
41
  }
41
42
 
42
43
  /**