overtype 1.2.7 → 2.0.0

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/src/parser.js CHANGED
@@ -10,6 +10,9 @@ export class MarkdownParser {
10
10
  // Track link index for anchor naming
11
11
  static linkIndex = 0;
12
12
 
13
+ // Global code highlighter function
14
+ static codeHighlighter = null;
15
+
13
16
  /**
14
17
  * Reset link index (call before parsing a new document)
15
18
  */
@@ -17,6 +20,14 @@ export class MarkdownParser {
17
20
  this.linkIndex = 0;
18
21
  }
19
22
 
23
+ /**
24
+ * Set global code highlighter function
25
+ * @param {Function|null} highlighter - Function that takes (code, language) and returns highlighted HTML
26
+ */
27
+ static setCodeHighlighter(highlighter) {
28
+ this.codeHighlighter = highlighter;
29
+ }
30
+
20
31
  /**
21
32
  * Escape HTML special characters
22
33
  * @param {string} text - Raw text to escape
@@ -91,6 +102,25 @@ export class MarkdownParser {
91
102
  });
92
103
  }
93
104
 
105
+ /**
106
+ * Parse task lists (GitHub Flavored Markdown checkboxes)
107
+ * @param {string} html - HTML line to parse
108
+ * @param {boolean} isPreviewMode - Whether to render actual checkboxes (preview) or keep syntax visible (normal)
109
+ * @returns {string} Parsed task list item
110
+ */
111
+ static parseTaskList(html, isPreviewMode = false) {
112
+ return html.replace(/^((?: )*)-\s+\[([ xX])\]\s+(.+)$/, (match, indent, checked, content) => {
113
+ if (isPreviewMode) {
114
+ // Preview mode: render actual checkbox
115
+ const isChecked = checked.toLowerCase() === 'x';
116
+ return `${indent}<li class="task-list"><input type="checkbox" disabled ${isChecked ? 'checked' : ''}> ${content}</li>`;
117
+ } else {
118
+ // Normal mode: keep syntax visible for alignment
119
+ return `${indent}<li class="task-list"><span class="syntax-marker">- [${checked}] </span>${content}</li>`;
120
+ }
121
+ });
122
+ }
123
+
94
124
  /**
95
125
  * Parse numbered lists
96
126
  * @param {string} html - HTML line to parse
@@ -355,7 +385,7 @@ export class MarkdownParser {
355
385
  // URL should NOT be processed for markdown - use it as-is
356
386
  const anchorName = `--link-${this.linkIndex++}`;
357
387
  const safeUrl = this.sanitizeUrl(sanctuary.url);
358
- 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>`;
388
+ replacement = `<a href="${safeUrl}" style="anchor-name: ${anchorName}"><span class="syntax-marker">[</span>${processedLinkText}<span class="syntax-marker url-part">](${sanctuary.url})</span></a>`;
359
389
  }
360
390
 
361
391
  html = html.replace(placeholder, replacement);
@@ -390,7 +420,7 @@ export class MarkdownParser {
390
420
  * @param {string} line - Raw markdown line
391
421
  * @returns {string} Parsed HTML line
392
422
  */
393
- static parseLine(line) {
423
+ static parseLine(line, isPreviewMode = false) {
394
424
  let html = this.escapeHtml(line);
395
425
 
396
426
  // Preserve indentation
@@ -406,6 +436,7 @@ export class MarkdownParser {
406
436
  // Parse block elements
407
437
  html = this.parseHeader(html);
408
438
  html = this.parseBlockquote(html);
439
+ html = this.parseTaskList(html, isPreviewMode); // Check task lists before regular bullet lists
409
440
  html = this.parseBulletList(html);
410
441
  html = this.parseNumberedList(html);
411
442
 
@@ -427,9 +458,10 @@ export class MarkdownParser {
427
458
  * @param {string} text - Full markdown text
428
459
  * @param {number} activeLine - Currently active line index (optional)
429
460
  * @param {boolean} showActiveLineRaw - Show raw markdown on active line
461
+ * @param {Function} instanceHighlighter - Instance-specific code highlighter (optional, overrides global if provided)
430
462
  * @returns {string} Parsed HTML
431
463
  */
432
- static parse(text, activeLine = -1, showActiveLineRaw = false) {
464
+ static parse(text, activeLine = -1, showActiveLineRaw = false, instanceHighlighter, isPreviewMode = false) {
433
465
  // Reset link counter for each parse
434
466
  this.resetLinkIndex();
435
467
 
@@ -448,7 +480,7 @@ export class MarkdownParser {
448
480
  if (codeFenceRegex.test(line)) {
449
481
  inCodeBlock = !inCodeBlock;
450
482
  // Parse fence markers normally to get styled output
451
- return this.parseLine(line);
483
+ return this.parseLine(line, isPreviewMode);
452
484
  }
453
485
 
454
486
  // If we're inside a code block, don't parse as markdown
@@ -459,26 +491,27 @@ export class MarkdownParser {
459
491
  }
460
492
 
461
493
  // Otherwise, parse the markdown normally
462
- return this.parseLine(line);
494
+ return this.parseLine(line, isPreviewMode);
463
495
  });
464
496
 
465
497
  // Join without newlines to prevent extra spacing
466
498
  const html = parsedLines.join('');
467
499
 
468
500
  // Apply post-processing for list consolidation
469
- return this.postProcessHTML(html);
501
+ return this.postProcessHTML(html, instanceHighlighter);
470
502
  }
471
503
 
472
504
  /**
473
505
  * Post-process HTML to consolidate lists and code blocks
474
506
  * @param {string} html - HTML to post-process
507
+ * @param {Function} instanceHighlighter - Instance-specific code highlighter (optional, overrides global if provided)
475
508
  * @returns {string} Post-processed HTML with consolidated lists and code blocks
476
509
  */
477
- static postProcessHTML(html) {
510
+ static postProcessHTML(html, instanceHighlighter) {
478
511
  // Check if we're in a browser environment
479
512
  if (typeof document === 'undefined' || !document) {
480
513
  // In Node.js environment - do manual post-processing
481
- return this.postProcessHTMLManual(html);
514
+ return this.postProcessHTMLManual(html, instanceHighlighter);
482
515
  }
483
516
 
484
517
  // Parse HTML string into DOM
@@ -525,9 +558,38 @@ export class MarkdownParser {
525
558
 
526
559
  // Store reference to the code element for adding content
527
560
  currentCodeBlock._codeElement = codeElement;
561
+ currentCodeBlock._language = lang;
562
+ currentCodeBlock._codeContent = '';
528
563
  continue;
529
564
  } else {
530
- // End of code block - fence stays visible
565
+ // End of code block - apply highlighting if needed
566
+ // Use instance highlighter if provided, otherwise fall back to global highlighter
567
+ const highlighter = instanceHighlighter || this.codeHighlighter;
568
+ if (currentCodeBlock && highlighter && currentCodeBlock._codeContent) {
569
+ try {
570
+ const result = highlighter(
571
+ currentCodeBlock._codeContent,
572
+ currentCodeBlock._language || ''
573
+ );
574
+
575
+ // Check if result is a Promise (async highlighter)
576
+ if (result && typeof result.then === 'function') {
577
+ console.warn('Async highlighters are not supported in parse() because it returns an HTML string. The caller creates new DOM elements from that string, breaking references to the elements we would update. Use synchronous highlighters only.');
578
+ // Keep the plain text fallback that was already set
579
+ } else {
580
+ // Synchronous highlighter
581
+ // Verify highlighter returned non-empty string
582
+ if (result && typeof result === 'string' && result.trim()) {
583
+ currentCodeBlock._codeElement.innerHTML = result;
584
+ }
585
+ // else: keep the plain text fallback that was already set
586
+ }
587
+ } catch (error) {
588
+ console.warn('Code highlighting failed:', error);
589
+ // Keep the plain text content as fallback
590
+ }
591
+ }
592
+
531
593
  inCodeBlock = false;
532
594
  currentCodeBlock = null;
533
595
  continue;
@@ -538,14 +600,18 @@ export class MarkdownParser {
538
600
  // Check if we're in a code block - any div that's not a code fence
539
601
  if (inCodeBlock && currentCodeBlock && child.tagName === 'DIV' && !child.querySelector('.code-fence')) {
540
602
  const codeElement = currentCodeBlock._codeElement || currentCodeBlock.querySelector('code');
541
- // Add the line content to the code block
542
- if (codeElement.textContent.length > 0) {
543
- codeElement.textContent += '\n';
603
+ // Add the line content to the code block content (for highlighting)
604
+ if (currentCodeBlock._codeContent.length > 0) {
605
+ currentCodeBlock._codeContent += '\n';
544
606
  }
545
607
  // Get the actual text content, preserving spaces
546
- // Use textContent instead of innerHTML to avoid double-escaping
547
- // textContent automatically decodes HTML entities
548
608
  const lineText = child.textContent.replace(/\u00A0/g, ' '); // \u00A0 is nbsp
609
+ currentCodeBlock._codeContent += lineText;
610
+
611
+ // Also add to the code element (fallback if no highlighter)
612
+ if (codeElement.textContent.length > 0) {
613
+ codeElement.textContent += '\n';
614
+ }
549
615
  codeElement.textContent += lineText;
550
616
  child.remove();
551
617
  continue;
@@ -611,9 +677,10 @@ export class MarkdownParser {
611
677
  /**
612
678
  * Manual post-processing for Node.js environments (without DOM)
613
679
  * @param {string} html - HTML to post-process
680
+ * @param {Function} instanceHighlighter - Instance-specific code highlighter (optional, overrides global if provided)
614
681
  * @returns {string} Post-processed HTML
615
682
  */
616
- static postProcessHTMLManual(html) {
683
+ static postProcessHTMLManual(html, instanceHighlighter) {
617
684
  let processed = html;
618
685
 
619
686
  // Process unordered lists
@@ -678,10 +745,46 @@ export class MarkdownParser {
678
745
  const lang = openFence.slice(3).trim();
679
746
  const langClass = lang ? ` class="language-${lang}"` : '';
680
747
 
748
+ // Apply code highlighting if available
749
+ let highlightedContent = codeContent;
750
+ // Use instance highlighter if provided, otherwise fall back to global highlighter
751
+ const highlighter = instanceHighlighter || this.codeHighlighter;
752
+ if (highlighter) {
753
+ try {
754
+ // CRITICAL: Decode HTML entities before passing to highlighter
755
+ // In the DOM path, textContent automatically decodes entities.
756
+ // In the manual path, we need to decode explicitly to avoid double-escaping.
757
+ const decodedCode = codeContent
758
+ .replace(/&quot;/g, '"')
759
+ .replace(/&#39;/g, "'")
760
+ .replace(/&lt;/g, '<')
761
+ .replace(/&gt;/g, '>')
762
+ .replace(/&amp;/g, '&'); // Must be last to avoid double-decoding
763
+
764
+ const result = highlighter(decodedCode, lang);
765
+
766
+ // Check if result is a Promise (async highlighter)
767
+ // Note: In Node.js context, we can't easily defer rendering, so we warn
768
+ if (result && typeof result.then === 'function') {
769
+ console.warn('Async highlighters are not supported in Node.js (non-DOM) context. Use synchronous highlighters for server-side rendering.');
770
+ // Fall back to escaped content
771
+ } else {
772
+ // Synchronous highlighter - verify returned non-empty string
773
+ if (result && typeof result === 'string' && result.trim()) {
774
+ highlightedContent = result;
775
+ }
776
+ // else: keep the escaped codeContent as fallback
777
+ }
778
+ } catch (error) {
779
+ console.warn('Code highlighting failed:', error);
780
+ // Fall back to original content
781
+ }
782
+ }
783
+
681
784
  // Keep fence markers visible as separate divs, with pre/code block between them
682
785
  let result = `<div><span class="code-fence">${openFence}</span></div>`;
683
- // Content is already escaped, don't double-escape
684
- result += `<pre class="code-block"><code${langClass}>${codeContent}</code></pre>`;
786
+ // Use highlighted content if available, otherwise use escaped content
787
+ result += `<pre class="code-block"><code${langClass}>${highlightedContent}</code></pre>`;
685
788
  result += `<div><span class="code-fence">${closeFence}</span></div>`;
686
789
 
687
790
  return result;
package/src/styles.js CHANGED
@@ -356,12 +356,14 @@ export function generateStyles(options = {}) {
356
356
  /* Code block styling in normal mode - yellow background */
357
357
  .overtype-wrapper .overtype-preview pre.code-block {
358
358
  background: var(--code-bg, rgba(244, 211, 94, 0.4)) !important;
359
+ white-space: break-spaces !important; /* Prevent horizontal scrollbar that breaks alignment */
359
360
  }
360
361
 
361
362
  /* Code inside pre blocks - remove background */
362
363
  .overtype-wrapper .overtype-preview pre code {
363
364
  background: transparent !important;
364
365
  color: var(--code, #0d3b66) !important;
366
+ font-family: ${fontFamily} !important; /* Match textarea font exactly for alignment */
365
367
  }
366
368
 
367
369
  /* Blockquotes */
@@ -588,11 +590,11 @@ export function generateStyles(options = {}) {
588
590
  }
589
591
 
590
592
  /* Plain mode - hide preview and show textarea text */
591
- .overtype-container.plain-mode .overtype-preview {
593
+ .overtype-container[data-mode="plain"] .overtype-preview {
592
594
  display: none !important;
593
595
  }
594
596
 
595
- .overtype-container.plain-mode .overtype-input {
597
+ .overtype-container[data-mode="plain"] .overtype-input {
596
598
  color: var(--text, #0d3b66) !important;
597
599
  /* Use system font stack for better plain text readability */
598
600
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
@@ -600,7 +602,7 @@ export function generateStyles(options = {}) {
600
602
  }
601
603
 
602
604
  /* Ensure textarea remains transparent in overlay mode */
603
- .overtype-container:not(.plain-mode) .overtype-input {
605
+ .overtype-container:not([data-mode="plain"]) .overtype-input {
604
606
  color: transparent !important;
605
607
  }
606
608
 
@@ -653,37 +655,43 @@ export function generateStyles(options = {}) {
653
655
  color: var(--h1, #007bff);
654
656
  }
655
657
 
658
+ .overtype-dropdown-icon {
659
+ width: 20px;
660
+ margin-right: 8px;
661
+ text-align: center;
662
+ }
663
+
656
664
  /* Preview mode styles */
657
- .overtype-container.preview-mode .overtype-input {
665
+ .overtype-container[data-mode="preview"] .overtype-input {
658
666
  display: none !important;
659
667
  }
660
668
 
661
- .overtype-container.preview-mode .overtype-preview {
669
+ .overtype-container[data-mode="preview"] .overtype-preview {
662
670
  pointer-events: auto !important;
663
671
  user-select: text !important;
664
672
  cursor: text !important;
665
673
  }
666
674
 
667
675
  /* Hide syntax markers in preview mode */
668
- .overtype-container.preview-mode .syntax-marker {
676
+ .overtype-container[data-mode="preview"] .syntax-marker {
669
677
  display: none !important;
670
678
  }
671
679
 
672
680
  /* Hide URL part of links in preview mode - extra specificity */
673
- .overtype-container.preview-mode .syntax-marker.url-part,
674
- .overtype-container.preview-mode .url-part {
681
+ .overtype-container[data-mode="preview"] .syntax-marker.url-part,
682
+ .overtype-container[data-mode="preview"] .url-part {
675
683
  display: none !important;
676
684
  }
677
685
 
678
686
  /* Hide all syntax markers inside links too */
679
- .overtype-container.preview-mode a .syntax-marker {
687
+ .overtype-container[data-mode="preview"] a .syntax-marker {
680
688
  display: none !important;
681
689
  }
682
690
 
683
691
  /* Headers - restore proper sizing in preview mode */
684
- .overtype-container.preview-mode .overtype-wrapper .overtype-preview h1,
685
- .overtype-container.preview-mode .overtype-wrapper .overtype-preview h2,
686
- .overtype-container.preview-mode .overtype-wrapper .overtype-preview h3 {
692
+ .overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview h1,
693
+ .overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview h2,
694
+ .overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview h3 {
687
695
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important;
688
696
  font-weight: 600 !important;
689
697
  margin: 0 !important;
@@ -692,41 +700,63 @@ export function generateStyles(options = {}) {
692
700
  line-height: 1 !important; /* Tight line height for headings */
693
701
  }
694
702
 
695
- .overtype-container.preview-mode .overtype-wrapper .overtype-preview h1 {
703
+ .overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview h1 {
696
704
  font-size: 2em !important;
697
705
  }
698
706
 
699
- .overtype-container.preview-mode .overtype-wrapper .overtype-preview h2 {
707
+ .overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview h2 {
700
708
  font-size: 1.5em !important;
701
709
  }
702
710
 
703
- .overtype-container.preview-mode .overtype-wrapper .overtype-preview h3 {
711
+ .overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview h3 {
704
712
  font-size: 1.17em !important;
705
713
  }
706
714
 
707
715
  /* Lists - restore list styling in preview mode */
708
- .overtype-container.preview-mode .overtype-wrapper .overtype-preview ul {
716
+ .overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview ul {
709
717
  display: block !important;
710
718
  list-style: disc !important;
711
719
  padding-left: 2em !important;
712
720
  margin: 1em 0 !important;
713
721
  }
714
722
 
715
- .overtype-container.preview-mode .overtype-wrapper .overtype-preview ol {
723
+ .overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview ol {
716
724
  display: block !important;
717
725
  list-style: decimal !important;
718
726
  padding-left: 2em !important;
719
727
  margin: 1em 0 !important;
720
728
  }
721
729
 
722
- .overtype-container.preview-mode .overtype-wrapper .overtype-preview li {
730
+ .overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview li {
723
731
  display: list-item !important;
724
732
  margin: 0 !important;
725
733
  padding: 0 !important;
726
734
  }
727
735
 
736
+ /* Task list checkboxes - only in preview mode */
737
+ .overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview li.task-list {
738
+ list-style: none !important;
739
+ position: relative !important;
740
+ }
741
+
742
+ .overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview li.task-list input[type="checkbox"] {
743
+ margin-right: 0.5em !important;
744
+ cursor: default !important;
745
+ vertical-align: middle !important;
746
+ }
747
+
748
+ /* Task list in normal mode - keep syntax visible */
749
+ .overtype-container:not([data-mode="preview"]) .overtype-wrapper .overtype-preview li.task-list {
750
+ list-style: none !important;
751
+ }
752
+
753
+ .overtype-container:not([data-mode="preview"]) .overtype-wrapper .overtype-preview li.task-list .syntax-marker {
754
+ color: var(--syntax, #999999) !important;
755
+ font-weight: normal !important;
756
+ }
757
+
728
758
  /* Links - make clickable in preview mode */
729
- .overtype-container.preview-mode .overtype-wrapper .overtype-preview a {
759
+ .overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview a {
730
760
  pointer-events: auto !important;
731
761
  cursor: pointer !important;
732
762
  color: var(--link, #0066cc) !important;
@@ -734,7 +764,7 @@ export function generateStyles(options = {}) {
734
764
  }
735
765
 
736
766
  /* Code blocks - proper pre/code styling in preview mode */
737
- .overtype-container.preview-mode .overtype-wrapper .overtype-preview pre.code-block {
767
+ .overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview pre.code-block {
738
768
  background: #2d2d2d !important;
739
769
  color: #f8f8f2 !important;
740
770
  padding: 1.2em !important;
@@ -745,11 +775,11 @@ export function generateStyles(options = {}) {
745
775
  }
746
776
 
747
777
  /* Cave theme code block background in preview mode */
748
- .overtype-container[data-theme="cave"].preview-mode .overtype-wrapper .overtype-preview pre.code-block {
778
+ .overtype-container[data-theme="cave"][data-mode="preview"] .overtype-wrapper .overtype-preview pre.code-block {
749
779
  background: #11171F !important;
750
780
  }
751
781
 
752
- .overtype-container.preview-mode .overtype-wrapper .overtype-preview pre.code-block code {
782
+ .overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview pre.code-block code {
753
783
  background: transparent !important;
754
784
  color: inherit !important;
755
785
  padding: 0 !important;
@@ -759,16 +789,16 @@ export function generateStyles(options = {}) {
759
789
  }
760
790
 
761
791
  /* Hide old code block lines and fences in preview mode */
762
- .overtype-container.preview-mode .overtype-wrapper .overtype-preview .code-block-line {
792
+ .overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview .code-block-line {
763
793
  display: none !important;
764
794
  }
765
795
 
766
- .overtype-container.preview-mode .overtype-wrapper .overtype-preview .code-fence {
796
+ .overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview .code-fence {
767
797
  display: none !important;
768
798
  }
769
799
 
770
800
  /* Blockquotes - enhanced styling in preview mode */
771
- .overtype-container.preview-mode .overtype-wrapper .overtype-preview .blockquote {
801
+ .overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview .blockquote {
772
802
  display: block !important;
773
803
  border-left: 4px solid var(--blockquote, #ddd) !important;
774
804
  padding-left: 1em !important;
@@ -777,7 +807,7 @@ export function generateStyles(options = {}) {
777
807
  }
778
808
 
779
809
  /* Typography improvements in preview mode */
780
- .overtype-container.preview-mode .overtype-wrapper .overtype-preview {
810
+ .overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview {
781
811
  font-family: Georgia, 'Times New Roman', serif !important;
782
812
  font-size: 16px !important;
783
813
  line-height: 1.8 !important;
@@ -785,7 +815,7 @@ export function generateStyles(options = {}) {
785
815
  }
786
816
 
787
817
  /* Inline code in preview mode - keep monospace */
788
- .overtype-container.preview-mode .overtype-wrapper .overtype-preview code {
818
+ .overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview code {
789
819
  font-family: ${fontFamily} !important;
790
820
  font-size: 0.9em !important;
791
821
  background: rgba(135, 131, 120, 0.15) !important;
@@ -794,24 +824,56 @@ export function generateStyles(options = {}) {
794
824
  }
795
825
 
796
826
  /* Strong and em elements in preview mode */
797
- .overtype-container.preview-mode .overtype-wrapper .overtype-preview strong {
827
+ .overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview strong {
798
828
  font-weight: 700 !important;
799
829
  color: inherit !important; /* Use parent text color */
800
830
  }
801
831
 
802
- .overtype-container.preview-mode .overtype-wrapper .overtype-preview em {
832
+ .overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview em {
803
833
  font-style: italic !important;
804
834
  color: inherit !important; /* Use parent text color */
805
835
  }
806
836
 
807
837
  /* HR in preview mode */
808
- .overtype-container.preview-mode .overtype-wrapper .overtype-preview .hr-marker {
838
+ .overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview .hr-marker {
809
839
  display: block !important;
810
840
  border-top: 2px solid var(--hr, #ddd) !important;
811
841
  text-indent: -9999px !important;
812
842
  height: 2px !important;
813
843
  }
814
844
 
845
+ /* Link Tooltip - CSS Anchor Positioning */
846
+ @supports (position-anchor: --x) and (position-area: center) {
847
+ .overtype-link-tooltip {
848
+ position: absolute;
849
+ position-anchor: var(--target-anchor, --link-0);
850
+ position-area: block-end center;
851
+ 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
+ position-try: most-width block-end inline-end, flip-inline, block-start center;
869
+ position-visibility: anchors-visible;
870
+ }
871
+
872
+ .overtype-link-tooltip.visible {
873
+ display: flex !important;
874
+ }
875
+ }
876
+
815
877
  ${mobileStyles}
816
878
  `;
817
879
  }