markdownr 0.5.2 → 0.5.3

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e91138e164e026f52b6957f44693eb86d75b79eda7e4404d094f1ea0d4e35d26
4
- data.tar.gz: b549ff9d07b45f43cc1639c2b4a47171f3a38bb76e2e98c1bb0ffe03ddc53fbe
3
+ metadata.gz: 77751617a0f29c77c283ed5a5861539501bc6e5795af91b83d2f7ca15ff7e19c
4
+ data.tar.gz: fc0003a34b89f185e567954561d5a367a0a671d085f79719d4a292b82d23270f
5
5
  SHA512:
6
- metadata.gz: cfc1004e65a89e09263f3a35f2da2b3b354afe8351efa87e49194b0aec3bef5aaeedd7d4aba70f00c01ebca6468150a296be2bdad0d8ca238f87b5bc73ebca0d
7
- data.tar.gz: c0cc3d73580b42c40c3f9244ed5acc69a74363237c54af0d21877bf4f1a64d2b642150fc4c384d13a6efb74a1bf86f12819d10b913f935ff60298e1e055e8460
6
+ metadata.gz: 65dc66819263dd1367b965e3ec480b90dea99581c9e6039e7bf4e171cfd6cfba6d590c19ef9bbfd4e01a440f8e41478a0640734a6552999b9e69aa72672361f6
7
+ data.tar.gz: 825fe2505eb934401acd0881a8826a2de0b2900ba764b0b3f5e4c5da8a54d06efe567becb87f2d38beb5a2f746c46701272d6c30df95ef3902d8d64596d6738c
@@ -1,3 +1,3 @@
1
1
  module MarkdownServer
2
- VERSION = "0.5.2"
2
+ VERSION = "0.5.3"
3
3
  end
data/views/layout.erb CHANGED
@@ -1419,6 +1419,7 @@
1419
1419
  var touchMoved = false;
1420
1420
  var historyStack = [];
1421
1421
  var currentPopupPos = { x: 0, y: 0 };
1422
+ var mouseLeaveTimer = null;
1422
1423
 
1423
1424
  function escHtml(s) {
1424
1425
  return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
@@ -1446,11 +1447,11 @@
1446
1447
 
1447
1448
  var extLinkIcon = '<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:middle;flex-shrink:0"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>';
1448
1449
 
1449
- function showPopup(x, y, title, bodyHtml, href) {
1450
+ function showPopup(x, y, title, bodyHtml, href, linkRect) {
1450
1451
  // Remove old popup without clearing historyStack
1451
1452
  if (popup && popup.parentNode) popup.parentNode.removeChild(popup);
1452
1453
  popup = null;
1453
- currentPopupPos = { x: x, y: y };
1454
+ currentPopupPos = { x: x, y: y, linkRect: linkRect || null };
1454
1455
 
1455
1456
  popup = document.createElement('div');
1456
1457
  popup.className = 'link-ctx-popup';
@@ -1469,6 +1470,14 @@
1469
1470
  '<div class="link-ctx-popup-body">' + bodyHtml + '</div>';
1470
1471
  document.body.appendChild(popup);
1471
1472
 
1473
+ clearTimeout(mouseLeaveTimer);
1474
+ popup.addEventListener('mouseleave', function() {
1475
+ mouseLeaveTimer = setTimeout(hidePopup, 150);
1476
+ });
1477
+ popup.addEventListener('mouseenter', function() {
1478
+ clearTimeout(mouseLeaveTimer);
1479
+ });
1480
+
1472
1481
  repositionPopup();
1473
1482
 
1474
1483
  var backBtnEl = popup.querySelector('.link-ctx-popup-back');
@@ -1476,7 +1485,7 @@
1476
1485
  backBtnEl.addEventListener('click', function(e) {
1477
1486
  e.stopPropagation();
1478
1487
  var prev = historyStack.pop();
1479
- if (prev) showPopup(prev.x, prev.y, prev.title, prev.bodyHtml, prev.href);
1488
+ if (prev) showPopup(prev.x, prev.y, prev.title, prev.bodyHtml, prev.href, prev.linkRect);
1480
1489
  });
1481
1490
  }
1482
1491
  popup.querySelector('.link-ctx-popup-close').addEventListener('click', hidePopup);
@@ -1492,10 +1501,10 @@
1492
1501
  if (!linkHref || isAnchorOnly(linkHref)) return;
1493
1502
  e.stopPropagation();
1494
1503
  e.preventDefault();
1495
- var savedPos = { x: currentPopupPos.x, y: currentPopupPos.y };
1504
+ var savedPos = { x: currentPopupPos.x, y: currentPopupPos.y, linkRect: currentPopupPos.linkRect };
1496
1505
  var titleEl = popup.querySelector('.link-ctx-popup-title');
1497
1506
  historyStack.push({
1498
- x: savedPos.x, y: savedPos.y,
1507
+ x: savedPos.x, y: savedPos.y, linkRect: savedPos.linkRect,
1499
1508
  title: titleEl ? titleEl.querySelector('span').textContent : '',
1500
1509
  bodyHtml: body.innerHTML,
1501
1510
  href: titleEl ? (titleEl.getAttribute('href') || '') : ''
@@ -1510,10 +1519,10 @@
1510
1519
  if (!linkHref || isAnchorOnly(linkHref)) return;
1511
1520
  e.stopPropagation();
1512
1521
  e.preventDefault();
1513
- var savedPos = { x: currentPopupPos.x, y: currentPopupPos.y };
1522
+ var savedPos = { x: currentPopupPos.x, y: currentPopupPos.y, linkRect: currentPopupPos.linkRect };
1514
1523
  var titleEl = popup.querySelector('.link-ctx-popup-title');
1515
1524
  historyStack.push({
1516
- x: savedPos.x, y: savedPos.y,
1525
+ x: savedPos.x, y: savedPos.y, linkRect: savedPos.linkRect,
1517
1526
  title: titleEl ? titleEl.querySelector('span').textContent : '',
1518
1527
  bodyHtml: body.innerHTML,
1519
1528
  href: titleEl ? (titleEl.getAttribute('href') || '') : ''
@@ -1529,13 +1538,35 @@
1529
1538
  function repositionPopup() {
1530
1539
  if (!popup) return;
1531
1540
  var x = currentPopupPos.x, y = currentPopupPos.y;
1541
+ var rect = currentPopupPos.linkRect;
1532
1542
  var vw = Math.min(window.innerWidth, document.documentElement.clientWidth);
1533
1543
  var vh = window.innerHeight;
1534
- var left = x + 12;
1535
- var top = y + 12;
1536
- if (left + popup.offsetWidth > vw - 8) left = Math.max(8, vw - popup.offsetWidth - 8);
1537
- if (top + popup.offsetHeight > vh - 8) top = Math.max(8, y - popup.offsetHeight - 12);
1538
- if (top < 8) top = 8;
1544
+ var pw = popup.offsetWidth;
1545
+ var ph = popup.offsetHeight;
1546
+
1547
+ // Horizontal: position so mouse is ~16px inside left edge, clamped to viewport
1548
+ var left = Math.min(x - 16, vw - pw - 8);
1549
+ if (left < 8) left = 8;
1550
+
1551
+ // Vertical: tight to link bounds when available
1552
+ var top;
1553
+ if (rect) {
1554
+ var gap = 3;
1555
+ var belowTop = rect.bottom + gap;
1556
+ var aboveTop = rect.top - ph - gap;
1557
+ if (belowTop + ph <= vh - 8) {
1558
+ top = belowTop; // fits below
1559
+ } else if (aboveTop >= 8) {
1560
+ top = aboveTop; // fits above
1561
+ } else {
1562
+ top = Math.max(8, vh - ph - 8); // clamp: covers link
1563
+ }
1564
+ } else {
1565
+ top = y + 12;
1566
+ if (top + ph > vh - 8) top = Math.max(8, y - ph - 12);
1567
+ if (top < 8) top = 8;
1568
+ }
1569
+
1539
1570
  popup.style.left = left + 'px';
1540
1571
  popup.style.top = top + 'px';
1541
1572
  }
@@ -1550,28 +1581,32 @@
1550
1581
  }
1551
1582
 
1552
1583
  function hidePopup() {
1584
+ clearTimeout(mouseLeaveTimer);
1553
1585
  if (popup && popup.parentNode) popup.parentNode.removeChild(popup);
1554
1586
  popup = null;
1555
1587
  historyStack = [];
1556
1588
  }
1557
1589
 
1590
+
1558
1591
  function handleLink(anchor, x, y, chained) {
1559
1592
  if (!chained) historyStack = [];
1560
1593
  var href = anchor.getAttribute('href');
1561
1594
  if (!href || isAnchorOnly(href)) return;
1562
1595
  var label = anchor.textContent.trim() || href;
1596
+ // For chained popup navigation keep the current link rect; for new popups measure the anchor
1597
+ var linkRect = chained ? currentPopupPos.linkRect : anchor.getBoundingClientRect();
1563
1598
 
1564
1599
  if (isLocalMd(href)) {
1565
1600
  var path = previewPath(href);
1566
1601
  var cached = cache[path];
1567
1602
  if (cached && typeof cached === 'object') {
1568
- showPopup(x, y, cached.title || label, cached.html, href);
1603
+ showPopup(x, y, cached.title || label, cached.html, href, linkRect);
1569
1604
  } else if (cached === false) {
1570
1605
  showPopup(x, y, label,
1571
1606
  '<div class="link-ctx-popup-url">' + escHtml(href) + '</div>' +
1572
- '<p style="margin:0;color:#888;font-family:sans-serif;font-size:0.82rem">Preview not available</p>', href);
1607
+ '<p style="margin:0;color:#888;font-family:sans-serif;font-size:0.82rem">Preview not available</p>', href, linkRect);
1573
1608
  } else {
1574
- showPopup(x, y, label, '<p style="opacity:0.5;margin:0;font-family:sans-serif">Loading\u2026</p>', href);
1609
+ showPopup(x, y, label, '<p style="opacity:0.5;margin:0;font-family:sans-serif">Loading\u2026</p>', href, linkRect);
1575
1610
  if (cached === undefined) {
1576
1611
  cache[path] = null;
1577
1612
  fetch(path)
@@ -1592,13 +1627,13 @@
1592
1627
  var extKey = 'ext:' + href;
1593
1628
  var extCached = cache[extKey];
1594
1629
  if (extCached && typeof extCached === 'object') {
1595
- showPopup(x, y, extCached.title || label, extCached.html, href);
1630
+ showPopup(x, y, extCached.title || label, extCached.html, href, linkRect);
1596
1631
  } else if (extCached === false) {
1597
1632
  showPopup(x, y, label,
1598
1633
  '<div class="link-ctx-popup-url">' + escHtml(href) + '</div>' +
1599
- '<p style="margin:0.5rem 0 0;color:#888;font-family:sans-serif;font-size:0.82rem">Could not fetch page content.</p>', href);
1634
+ '<p style="margin:0.5rem 0 0;color:#888;font-family:sans-serif;font-size:0.82rem">Could not fetch page content.</p>', href, linkRect);
1600
1635
  } else {
1601
- showPopup(x, y, label, '<p style="opacity:0.5;margin:0;font-family:sans-serif">Loading\u2026</p>', href);
1636
+ showPopup(x, y, label, '<p style="opacity:0.5;margin:0;font-family:sans-serif">Loading\u2026</p>', href, linkRect);
1602
1637
  if (extCached === undefined) {
1603
1638
  cache[extKey] = null;
1604
1639
  fetch('/fetch?url=' + encodeURIComponent(href))
@@ -1623,7 +1658,7 @@
1623
1658
  }
1624
1659
  }
1625
1660
  } else {
1626
- showPopup(x, y, label, '<div class="link-ctx-popup-url">' + escHtml(href) + '</div>', href);
1661
+ showPopup(x, y, label, '<div class="link-ctx-popup-url">' + escHtml(href) + '</div>', href, linkRect);
1627
1662
  }
1628
1663
  }
1629
1664
 
@@ -1686,6 +1721,7 @@
1686
1721
  });
1687
1722
  a.addEventListener('mouseleave', function() {
1688
1723
  clearTimeout(hoverTimer);
1724
+ if (popup) mouseLeaveTimer = setTimeout(hidePopup, 150);
1689
1725
  });
1690
1726
  });
1691
1727
  })();
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: markdownr
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.2
4
+ version: 0.5.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian Dunn