fix: hover ADD fires only ONE action path (navigation), remove duplicate handler in commands.rs
Browse files
src-tauri/resources/scripts/hover_overlay.js
CHANGED
|
@@ -1,47 +1,22 @@
|
|
| 1 |
/* Refstudio hover overlay: + ADD button on web images.
|
| 2 |
-
*
|
| 3 |
-
*
|
| 4 |
-
* 1. Badge was positioned at top-right of image but mouseout fired when moving FROM image TO badge
|
| 5 |
-
* 2. Badge pointer-events were toggled too aggressively
|
| 6 |
-
* 3. Badge hide timer was too short (300ms) — cursor couldn't reach it
|
| 7 |
*/
|
| 8 |
(function () {
|
| 9 |
-
if (window.
|
| 10 |
-
window.
|
| 11 |
|
| 12 |
var MIN = 90;
|
| 13 |
var activeImg = null;
|
| 14 |
var badge = null;
|
| 15 |
var showTimer = 0;
|
| 16 |
var hideTimer = 0;
|
|
|
|
| 17 |
|
| 18 |
function injectCSS() {
|
| 19 |
-
if (document.getElementById('
|
| 20 |
-
var s = document.createElement('style'); s.id = '
|
| 21 |
-
s.textContent =
|
| 22 |
-
/* The badge itself */
|
| 23 |
-
'#__muse_add_badge{',
|
| 24 |
-
' all:initial;position:fixed;z-index:2147483647;',
|
| 25 |
-
' display:flex;align-items:center;gap:5px;',
|
| 26 |
-
' padding:8px 16px;',
|
| 27 |
-
' background:rgba(10,132,255,0.95);color:#fff;',
|
| 28 |
-
' border-radius:999px;',
|
| 29 |
-
' box-shadow:0 4px 20px rgba(0,0,0,0.4),0 0 0 1px rgba(255,255,255,0.1);',
|
| 30 |
-
' font:700 12px/1 -apple-system,BlinkMacSystemFont,"Inter",sans-serif;',
|
| 31 |
-
' cursor:pointer;',
|
| 32 |
-
' opacity:0;transform:translateY(4px) scale(0.92);',
|
| 33 |
-
' transition:opacity .2s,transform .2s,background .1s;',
|
| 34 |
-
' pointer-events:auto;', /* ALWAYS clickable when visible */
|
| 35 |
-
' letter-spacing:0.01em;user-select:none;',
|
| 36 |
-
'}',
|
| 37 |
-
'#__muse_add_badge.v{opacity:1;transform:none}',
|
| 38 |
-
'#__muse_add_badge:hover{background:rgba(10,132,255,1);box-shadow:0 6px 28px rgba(10,132,255,0.4),0 0 0 1px rgba(255,255,255,0.2);transform:scale(1.06)}',
|
| 39 |
-
'#__muse_add_badge:active{transform:scale(0.92);background:rgba(10,100,220,1)}',
|
| 40 |
-
'#__muse_add_badge svg{width:14px;height:14px;stroke-width:2.5;pointer-events:none}',
|
| 41 |
-
/* Toast */
|
| 42 |
-
'#__mt7{all:initial;position:fixed;left:50%;bottom:24px;transform:translateX(-50%) translateY(8px);z-index:2147483647;padding:9px 16px;border-radius:999px;background:rgba(20,20,20,.97);border:1px solid rgba(255,255,255,.1);box-shadow:0 10px 36px rgba(0,0,0,.5);font:600 12px/1 -apple-system,sans-serif;color:#fff;opacity:0;transition:opacity .16s,transform .16s;pointer-events:none}',
|
| 43 |
-
'#__mt7.s{opacity:1;transform:translateX(-50%) translateY(0)}'
|
| 44 |
-
].join('');
|
| 45 |
(document.head || document.documentElement).appendChild(s);
|
| 46 |
}
|
| 47 |
|
|
@@ -49,134 +24,52 @@
|
|
| 49 |
function absUrl(s) { try { return new URL(s || '', location.href).href; } catch (_) { return s || ''; } }
|
| 50 |
|
| 51 |
function bestImageUrl(img) {
|
| 52 |
-
// srcset best
|
| 53 |
var srcset = img.getAttribute('srcset') || '';
|
| 54 |
-
if (srcset) {
|
| 55 |
-
|
| 56 |
-
srcset.split(',').forEach(function(part) {
|
| 57 |
-
var bits = part.trim().split(/\s+/); var u = bits[0]; var score = 1;
|
| 58 |
-
if (bits[1]) { if (bits[1].endsWith('w')) score = parseInt(bits[1],10)||1; else if (bits[1].endsWith('x')) score = (parseFloat(bits[1])||1)*1000; }
|
| 59 |
-
if (score > bestScore) { bestScore = score; best = u; }
|
| 60 |
-
});
|
| 61 |
-
if (best) return absUrl(best);
|
| 62 |
-
}
|
| 63 |
-
var u = img.getAttribute('data-full') || img.getAttribute('data-fullsrc') || img.getAttribute('data-original') || img.getAttribute('data-src') || img.getAttribute('data-lazy-src') || img.currentSrc || img.src;
|
| 64 |
u = absUrl(u);
|
| 65 |
-
try {
|
| 66 |
-
if (u.includes('pinimg.com') && u.match(/\/\d+x\//)) u = u.replace(/\/\d+x\//, '/originals/');
|
| 67 |
-
if (u.includes('artstation.com') && u.includes('/medium/')) u = u.replace('/medium/', '/large/');
|
| 68 |
-
if (u.includes('behance.net') && u.includes('/max_1200/')) u = u.replace('/max_1200/', '/max_3840/');
|
| 69 |
-
} catch (_) {}
|
| 70 |
return u;
|
| 71 |
}
|
| 72 |
|
| 73 |
-
function toast(m) {
|
| 74 |
-
var t = document.getElementById('__mt7');
|
| 75 |
-
if (!t) { t = document.createElement('div'); t.id = '__mt7'; document.documentElement.appendChild(t); }
|
| 76 |
-
t.textContent = m; t.classList.add('s');
|
| 77 |
-
clearTimeout(t._t); t._t = setTimeout(function () { t.classList.remove('s'); }, 2000);
|
| 78 |
-
}
|
| 79 |
|
| 80 |
function fireAction(url, title, w, h) {
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
function scheduleHide() { clearHide(); hideTimer = setTimeout(hide, 600); }
|
| 90 |
-
|
| 91 |
-
function hide() {
|
| 92 |
-
if (!badge) return;
|
| 93 |
-
badge.classList.remove('v');
|
| 94 |
-
activeImg = null;
|
| 95 |
}
|
| 96 |
|
| 97 |
-
function
|
| 98 |
-
|
| 99 |
-
// Position badge at TOP-RIGHT of image, inside the image bounds
|
| 100 |
-
var x = r.right - 80;
|
| 101 |
-
var y = r.top + 10;
|
| 102 |
-
if (x < r.left + 4) x = r.left + 4;
|
| 103 |
-
if (x > window.innerWidth - 90) x = window.innerWidth - 90;
|
| 104 |
-
if (y < 4) y = 4;
|
| 105 |
-
if (y > window.innerHeight - 40) y = window.innerHeight - 40;
|
| 106 |
-
el.style.left = x + 'px';
|
| 107 |
-
el.style.top = y + 'px';
|
| 108 |
-
}
|
| 109 |
|
| 110 |
-
function
|
| 111 |
-
if (img === activeImg && badge && badge.classList.contains('v')) return;
|
| 112 |
-
injectCSS();
|
| 113 |
-
activeImg = img;
|
| 114 |
-
if (!badge) {
|
| 115 |
-
badge = document.createElement('div');
|
| 116 |
-
badge.id = '__muse_add_badge';
|
| 117 |
-
badge.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>ADD';
|
| 118 |
|
| 119 |
-
|
| 120 |
-
badge.addEventListener('click', function (e) {
|
| 121 |
-
e.preventDefault(); e.stopPropagation(); e.stopImmediatePropagation();
|
| 122 |
-
if (!activeImg) return;
|
| 123 |
-
var url = bestImageUrl(activeImg);
|
| 124 |
-
if (!url || url.startsWith('data:') || url.startsWith('blob:')) { toast('Cannot capture this image'); return; }
|
| 125 |
-
var title = activeImg.alt || activeImg.title || document.title || 'Reference';
|
| 126 |
-
var w = activeImg.naturalWidth || Math.round(activeImg.getBoundingClientRect().width);
|
| 127 |
-
var h = activeImg.naturalHeight || Math.round(activeImg.getBoundingClientRect().height);
|
| 128 |
-
fireAction(url, title, w, h);
|
| 129 |
-
toast('✓ Added to Board');
|
| 130 |
-
hide();
|
| 131 |
-
}, true);
|
| 132 |
-
|
| 133 |
-
// Block ALL events from reaching elements underneath
|
| 134 |
-
['mousedown','mouseup','pointerdown','pointerup','touchstart','touchend'].forEach(function(ev) {
|
| 135 |
-
badge.addEventListener(ev, function(e) { e.preventDefault(); e.stopPropagation(); e.stopImmediatePropagation(); }, true);
|
| 136 |
-
});
|
| 137 |
-
|
| 138 |
-
// Keep badge visible while hovering it
|
| 139 |
-
badge.addEventListener('mouseenter', clearHide, true);
|
| 140 |
-
badge.addEventListener('mouseleave', scheduleHide, true);
|
| 141 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 142 |
document.documentElement.appendChild(badge);
|
| 143 |
}
|
| 144 |
-
pos(badge,
|
| 145 |
-
void badge.offsetHeight; // force reflow for transition
|
| 146 |
-
badge.classList.add('v');
|
| 147 |
}
|
| 148 |
|
| 149 |
-
function
|
| 150 |
-
if (!el || el.tagName !== 'IMG') return false;
|
| 151 |
-
if (el === badge) return false;
|
| 152 |
-
var r = el.getBoundingClientRect();
|
| 153 |
-
return r.width >= MIN && r.height >= MIN;
|
| 154 |
-
}
|
| 155 |
-
|
| 156 |
-
document.addEventListener('mouseover', function (e) {
|
| 157 |
-
var t = e.target;
|
| 158 |
-
// If hovering badge, keep it alive
|
| 159 |
-
if (badge && (t === badge || badge.contains(t))) { clearHide(); return; }
|
| 160 |
-
if (!isValidTarget(t)) return;
|
| 161 |
-
if (t === activeImg) { clearHide(); return; }
|
| 162 |
-
clearHide(); clearTimeout(showTimer);
|
| 163 |
-
showTimer = setTimeout(function () { show(t); }, 150);
|
| 164 |
-
}, true);
|
| 165 |
-
|
| 166 |
-
document.addEventListener('mouseout', function (e) {
|
| 167 |
-
var t = e.target;
|
| 168 |
-
if (!t || t.tagName !== 'IMG' || t !== activeImg) return;
|
| 169 |
-
var rel = e.relatedTarget;
|
| 170 |
-
// Don't hide if moving to the badge
|
| 171 |
-
if (badge && rel && (badge === rel || badge.contains(rel))) { clearHide(); return; }
|
| 172 |
-
clearTimeout(showTimer);
|
| 173 |
-
scheduleHide();
|
| 174 |
-
}, true);
|
| 175 |
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
if (r.bottom < -20 || r.top > window.innerHeight + 20) hide();
|
| 180 |
-
else pos(badge, activeImg);
|
| 181 |
-
}, { passive: true, capture: true });
|
| 182 |
})();
|
|
|
|
| 1 |
/* Refstudio hover overlay: + ADD button on web images.
|
| 2 |
+
* v11 — fires ONLY through window.location.href (on_navigation handler).
|
| 3 |
+
* Previous versions fired 3 channels causing duplicate images.
|
|
|
|
|
|
|
|
|
|
| 4 |
*/
|
| 5 |
(function () {
|
| 6 |
+
if (window.__muse_hover_v11) return;
|
| 7 |
+
window.__muse_hover_v11 = true;
|
| 8 |
|
| 9 |
var MIN = 90;
|
| 10 |
var activeImg = null;
|
| 11 |
var badge = null;
|
| 12 |
var showTimer = 0;
|
| 13 |
var hideTimer = 0;
|
| 14 |
+
var capturing = false; // prevent double-click
|
| 15 |
|
| 16 |
function injectCSS() {
|
| 17 |
+
if (document.getElementById('__muse_badge_css3')) return;
|
| 18 |
+
var s = document.createElement('style'); s.id = '__muse_badge_css3';
|
| 19 |
+
s.textContent = '#__muse_add_badge{all:initial;position:fixed;z-index:2147483647;display:flex;align-items:center;gap:5px;padding:8px 16px;background:rgba(10,132,255,0.95);color:#fff;border-radius:999px;box-shadow:0 4px 20px rgba(0,0,0,0.4),0 0 0 1px rgba(255,255,255,0.1);font:700 12px/1 -apple-system,BlinkMacSystemFont,"Inter",sans-serif;cursor:pointer;opacity:0;transform:translateY(4px) scale(0.92);transition:opacity .2s,transform .2s,background .1s;pointer-events:auto;letter-spacing:0.01em;user-select:none}#__muse_add_badge.v{opacity:1;transform:none}#__muse_add_badge:hover{background:rgba(10,132,255,1);box-shadow:0 6px 28px rgba(10,132,255,0.4),0 0 0 1px rgba(255,255,255,0.2);transform:scale(1.06)}#__muse_add_badge:active{transform:scale(0.92);background:rgba(10,100,220,1)}#__muse_add_badge svg{width:14px;height:14px;stroke-width:2.5;pointer-events:none}#__mt7{all:initial;position:fixed;left:50%;bottom:24px;transform:translateX(-50%) translateY(8px);z-index:2147483647;padding:9px 16px;border-radius:999px;background:rgba(20,20,20,.97);border:1px solid rgba(255,255,255,.1);box-shadow:0 10px 36px rgba(0,0,0,.5);font:600 12px/1 -apple-system,sans-serif;color:#fff;opacity:0;transition:opacity .16s,transform .16s;pointer-events:none}#__mt7.s{opacity:1;transform:translateX(-50%) translateY(0)}';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
(document.head || document.documentElement).appendChild(s);
|
| 21 |
}
|
| 22 |
|
|
|
|
| 24 |
function absUrl(s) { try { return new URL(s || '', location.href).href; } catch (_) { return s || ''; } }
|
| 25 |
|
| 26 |
function bestImageUrl(img) {
|
|
|
|
| 27 |
var srcset = img.getAttribute('srcset') || '';
|
| 28 |
+
if (srcset) { var best='',bs=0; srcset.split(',').forEach(function(p){var b=p.trim().split(/\s+/),u=b[0],sc=1;if(b[1]){if(b[1].endsWith('w'))sc=parseInt(b[1],10)||1;else if(b[1].endsWith('x'))sc=(parseFloat(b[1])||1)*1000;}if(sc>bs){bs=sc;best=u;}}); if(best) return absUrl(best); }
|
| 29 |
+
var u = img.getAttribute('data-full')||img.getAttribute('data-original')||img.getAttribute('data-src')||img.getAttribute('data-lazy-src')||img.currentSrc||img.src;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
u = absUrl(u);
|
| 31 |
+
try { if(u.includes('pinimg.com')&&u.match(/\/\d+x\//))u=u.replace(/\/\d+x\//,'/originals/'); if(u.includes('artstation.com')&&u.includes('/medium/'))u=u.replace('/medium/','/large/'); } catch(_){}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
return u;
|
| 33 |
}
|
| 34 |
|
| 35 |
+
function toast(m) { var t=document.getElementById('__mt7'); if(!t){t=document.createElement('div');t.id='__mt7';document.documentElement.appendChild(t);} t.textContent=m;t.classList.add('s'); clearTimeout(t._t);t._t=setTimeout(function(){t.classList.remove('s');},2000); }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
|
| 37 |
function fireAction(url, title, w, h) {
|
| 38 |
+
if (capturing) return;
|
| 39 |
+
capturing = true;
|
| 40 |
+
// ONLY use window.location.href — Tauri on_navigation will intercept and return false.
|
| 41 |
+
// This is the single reliable path. Do NOT also fire beacon/fetch (causes duplicates).
|
| 42 |
+
var u = 'muse-action://board?url='+enc(url)+'&source='+enc(location.href)+'&title='+enc(title)+'&w='+enc(w)+'&h='+enc(h);
|
| 43 |
+
window.location.href = u;
|
| 44 |
+
// Reset after short delay (the page won't actually navigate since Rust returns false)
|
| 45 |
+
setTimeout(function(){ capturing = false; }, 1000);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 46 |
}
|
| 47 |
|
| 48 |
+
function clearHide(){clearTimeout(hideTimer);}
|
| 49 |
+
function scheduleHide(){clearHide();hideTimer=setTimeout(hide,600);}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
|
| 51 |
+
function hide(){if(!badge)return;badge.classList.remove('v');activeImg=null;}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
|
| 53 |
+
function pos(el,img){var r=img.getBoundingClientRect();var x=r.right-80,y=r.top+10;if(x<r.left+4)x=r.left+4;if(x>innerWidth-90)x=innerWidth-90;if(y<4)y=4;if(y>innerHeight-40)y=innerHeight-40;el.style.left=x+'px';el.style.top=y+'px';}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 54 |
|
| 55 |
+
function show(img){
|
| 56 |
+
if(img===activeImg&&badge&&badge.classList.contains('v'))return;
|
| 57 |
+
injectCSS(); activeImg=img;
|
| 58 |
+
if(!badge){
|
| 59 |
+
badge=document.createElement('div');badge.id='__muse_add_badge';
|
| 60 |
+
badge.innerHTML='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>ADD';
|
| 61 |
+
badge.addEventListener('click',function(e){e.preventDefault();e.stopPropagation();e.stopImmediatePropagation();if(!activeImg)return;var url=bestImageUrl(activeImg);if(!url||url.startsWith('data:')||url.startsWith('blob:')){toast('Cannot capture');return;}fireAction(url,activeImg.alt||activeImg.title||document.title||'Reference',activeImg.naturalWidth||Math.round(activeImg.getBoundingClientRect().width),activeImg.naturalHeight||Math.round(activeImg.getBoundingClientRect().height));toast('✓ Added to Board');hide();},true);
|
| 62 |
+
['mousedown','mouseup','pointerdown','pointerup','touchstart','touchend'].forEach(function(ev){badge.addEventListener(ev,function(e){e.preventDefault();e.stopPropagation();e.stopImmediatePropagation();},true);});
|
| 63 |
+
badge.addEventListener('mouseenter',clearHide,true);
|
| 64 |
+
badge.addEventListener('mouseleave',scheduleHide,true);
|
| 65 |
document.documentElement.appendChild(badge);
|
| 66 |
}
|
| 67 |
+
pos(badge,activeImg);void badge.offsetHeight;badge.classList.add('v');
|
|
|
|
|
|
|
| 68 |
}
|
| 69 |
|
| 70 |
+
function isValid(el){if(!el||el.tagName!=='IMG')return false;if(el===badge)return false;var r=el.getBoundingClientRect();return r.width>=MIN&&r.height>=MIN;}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 71 |
|
| 72 |
+
document.addEventListener('mouseover',function(e){var t=e.target;if(badge&&(t===badge||badge.contains(t))){clearHide();return;}if(!isValid(t))return;if(t===activeImg){clearHide();return;}clearHide();clearTimeout(showTimer);showTimer=setTimeout(function(){show(t);},150);},true);
|
| 73 |
+
document.addEventListener('mouseout',function(e){var t=e.target;if(!t||t.tagName!=='IMG'||t!==activeImg)return;var rel=e.relatedTarget;if(badge&&rel&&(badge===rel||badge.contains(rel))){clearHide();return;}clearTimeout(showTimer);scheduleHide();},true);
|
| 74 |
+
window.addEventListener('scroll',function(){if(!badge||!activeImg)return;var r=activeImg.getBoundingClientRect();if(r.bottom<-20||r.top>innerHeight+20)hide();else pos(badge,activeImg);},{passive:true,capture:true});
|
|
|
|
|
|
|
|
|
|
| 75 |
})();
|