Spaces:
Running
Running
Hashtag inline: show sources on homepage + rewrite button"
Browse files- hashtag_inline.py +87 -0
hashtag_inline.py
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Hashtag inline: click hashtag → show sources on homepage + rewrite button."""
|
| 2 |
+
HASHTAG_INLINE_INJECT = r'''
|
| 3 |
+
<style>
|
| 4 |
+
.hashtag-sources{margin:8px 4px;background:#141414;border:1px solid #2a2a2a;border-radius:10px;padding:10px;display:none}
|
| 5 |
+
.hashtag-sources.active{display:block}
|
| 6 |
+
.hashtag-sources h3{font-size:13px;color:#5cb87a;margin-bottom:8px}
|
| 7 |
+
.hashtag-source-item{display:flex;gap:8px;padding:8px;background:#1a1a1a;border-radius:8px;margin:6px 0;cursor:pointer}
|
| 8 |
+
.hashtag-source-item:active{opacity:.8}
|
| 9 |
+
.hashtag-source-item img{width:80px;height:50px;object-fit:cover;border-radius:6px;background:#333;flex-shrink:0}
|
| 10 |
+
.hashtag-source-item .info{flex:1;min-width:0}
|
| 11 |
+
.hashtag-source-item .title{font-size:12px;font-weight:700;color:#eee;line-height:1.3;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
|
| 12 |
+
.hashtag-source-item .desc{font-size:10px;color:#888;margin-top:2px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
|
| 13 |
+
.hashtag-source-item .via{font-size:9px;color:#5cb87a;margin-top:2px}
|
| 14 |
+
.hashtag-rewrite-btn{display:block;width:100%;margin-top:10px;background:#2d8659;border:0;color:#fff;border-radius:14px;padding:10px;font-size:12px;font-weight:700;cursor:pointer}
|
| 15 |
+
.hashtag-rewrite-btn:disabled{opacity:.5}
|
| 16 |
+
.hashtag-close{float:right;background:none;border:0;color:#999;font-size:18px;cursor:pointer}
|
| 17 |
+
</style>
|
| 18 |
+
<div id="hashtag-sources-panel" class="hashtag-sources"></div>
|
| 19 |
+
<script>
|
| 20 |
+
(function(){
|
| 21 |
+
function esc(s){return String(s||'').replace(/[&<>"']/g,function(m){return{'&':'&','<':'<','>':'>','"':'"',"'":'''}[m]});}
|
| 22 |
+
|
| 23 |
+
// Override hashtag click: show sources inline on homepage
|
| 24 |
+
window.hashtagClick=async function(topic){
|
| 25 |
+
var panel=document.getElementById('hashtag-sources-panel');
|
| 26 |
+
if(!panel){panel=document.createElement('div');panel.id='hashtag-sources-panel';panel.className='hashtag-sources';var home=document.getElementById('view-home');if(home)home.insertBefore(panel,home.children[1]||null);}
|
| 27 |
+
panel.innerHTML='<button class="hashtag-close" onclick="closeHashtagPanel()">✕</button><h3>#'+esc(topic)+' — Đang tìm nguồn...</h3>';
|
| 28 |
+
panel.classList.add('active');
|
| 29 |
+
try{
|
| 30 |
+
var r=await fetch('/api/topic_sources?topic='+encodeURIComponent(topic));
|
| 31 |
+
var j=await r.json();
|
| 32 |
+
var sources=j.sources||[];
|
| 33 |
+
if(!sources.length){panel.innerHTML='<button class="hashtag-close" onclick="closeHashtagPanel()">✕</button><h3>#'+esc(topic)+'</h3><p style="color:#888;font-size:12px">Không tìm thấy nguồn cho chủ đề này.</p>';return;}
|
| 34 |
+
var h='<button class="hashtag-close" onclick="closeHashtagPanel()">✕</button>';
|
| 35 |
+
h+='<h3>#'+esc(topic)+' — '+sources.length+' nguồn</h3>';
|
| 36 |
+
sources.forEach(function(s){
|
| 37 |
+
h+='<div class="hashtag-source-item" onclick="if(typeof readArticle===\'function\')readArticle(\''+esc(s.url||'')+'\')">';
|
| 38 |
+
h+='<img src="/api/proxy/img?url='+encodeURIComponent(s.url||'')+'" onerror="this.style.display=\'none\'" loading="lazy">';
|
| 39 |
+
h+='<div class="info"><div class="title">'+esc(s.title||'')+'</div><div class="desc">'+esc((s.excerpt||s.snippet||'').slice(0,120))+'</div><div class="via">'+esc(s.via||'')+'</div></div>';
|
| 40 |
+
h+='</div>';
|
| 41 |
+
});
|
| 42 |
+
h+='<button class="hashtag-rewrite-btn" onclick="rewriteHashtagSources(\''+esc(topic)+'\',this)">🤖 Rewrite AI tổng hợp và đăng tường</button>';
|
| 43 |
+
panel.innerHTML=h;
|
| 44 |
+
// Try to load og:image for each source
|
| 45 |
+
sources.forEach(function(s,i){
|
| 46 |
+
if(!s.url)return;
|
| 47 |
+
fetch('/api/article?url='+encodeURIComponent(s.url)).then(function(r){return r.json()}).then(function(d){
|
| 48 |
+
if(d&&(d.og_image||d.img)){
|
| 49 |
+
var items=panel.querySelectorAll('.hashtag-source-item');
|
| 50 |
+
if(items[i]){var img=items[i].querySelector('img');if(img)img.src=d.og_image||d.img;}
|
| 51 |
+
}
|
| 52 |
+
}).catch(function(){});
|
| 53 |
+
});
|
| 54 |
+
}catch(e){panel.innerHTML='<button class="hashtag-close" onclick="closeHashtagPanel()">✕</button><h3>#'+esc(topic)+'</h3><p style="color:#f66;font-size:12px">Lỗi: '+esc(e.message)+'</p>';}
|
| 55 |
+
};
|
| 56 |
+
|
| 57 |
+
window.closeHashtagPanel=function(){
|
| 58 |
+
var panel=document.getElementById('hashtag-sources-panel');
|
| 59 |
+
if(panel)panel.classList.remove('active');
|
| 60 |
+
};
|
| 61 |
+
|
| 62 |
+
window.rewriteHashtagSources=async function(topic,btn){
|
| 63 |
+
btn.disabled=true;btn.textContent='Đang tổng hợp...';
|
| 64 |
+
try{
|
| 65 |
+
var r=await fetch('/api/topic_post',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({topic:topic})});
|
| 66 |
+
var j=await r.json();
|
| 67 |
+
if(!r.ok||j.error)throw new Error(j.error||'Lỗi');
|
| 68 |
+
btn.textContent='✅ Đã đăng tường AI!';
|
| 69 |
+
setTimeout(function(){closeHashtagPanel();},2000);
|
| 70 |
+
// Update wall
|
| 71 |
+
if(j.post&&typeof prependWallPost==='function')prependWallPost(j.post);
|
| 72 |
+
}catch(e){btn.textContent='❌ '+e.message;setTimeout(function(){btn.disabled=false;btn.textContent='🤖 Rewrite AI tổng hợp và đăng tường';},3000);}
|
| 73 |
+
};
|
| 74 |
+
|
| 75 |
+
// Patch existing hot-chip buttons to use hashtagClick instead of filling input
|
| 76 |
+
document.addEventListener('click',function(e){
|
| 77 |
+
var chip=e.target.closest('.hot-chip');
|
| 78 |
+
if(chip){
|
| 79 |
+
e.preventDefault();e.stopPropagation();
|
| 80 |
+
var topic=chip.textContent.replace(/^#/,'').trim()||chip.getAttribute('onclick')?.match(/value='([^']+)'/)?.[1]||'';
|
| 81 |
+
if(topic)hashtagClick(topic);
|
| 82 |
+
}
|
| 83 |
+
},true);
|
| 84 |
+
|
| 85 |
+
})();
|
| 86 |
+
</script>
|
| 87 |
+
'''
|