bookmarklet.html (3203B)
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="utf-8" /> 5 <meta name="viewport" content="width=device-width, initial-scale=1" /> 6 <title>bookmarklet | marky</title> 7 <link rel="stylesheet" href="/styles.css" /> 8 <style> 9 a.btn{display:inline-block;background:#222;color:#fff;text-decoration:none;padding:10px 14px;border-radius:6px} 10 .notice{margin-top:12px;color:#666} 11 </style> 12 <script src="/vendor/vue.global.prod.js"></script> 13 </head> 14 <body> 15 <div id="hdr">{{HEADER}}</div> 16 <main class="container"> 17 <h2>Save to Marky</h2> 18 <p>Drag this to your bookmarks bar:</p> 19 <p> 20 <a class="btn" href="javascript:(function(){try{var d=document,s=encodeURIComponent,u=location.href,t=document.title,sel='';var ae=d.activeElement;if(ae&&((ae.tagName==='TEXTAREA')||(ae.tagName==='INPUT'&&/^(?:text|search|url|tel|email)$/i.test(ae.type)))){var ss=ae.selectionStart||0,se=ae.selectionEnd||0;if(typeof ss==='number'&&typeof se==='number'&&se>ss){sel=ae.value.substring(ss,se)}}if(!sel&&d.getSelection){sel=d.getSelection().toString()}else if(!sel&&window.getSelection){sel=window.getSelection().toString()}else if(!sel&&d.selection){sel=d.selection.createRange().text}var win=window.open('{{ORIGIN}}/bookmarklet?u='+s(u)+'&t='+s(t)+'&d='+s(sel)+'{{AUTH}}','marky','width=520,height=520');if(win){win.focus()}}catch(e){alert('Could not open Marky: '+e)}})();">Save to Marky</a> 21 </p> 22 <div class="notice"> 23 <p>When clicked on any page, this opens a small form prefilled with the page URL and title. On submit, it saves to your Marky account.</p> 24 </div> 25 </main> 26 <script> 27 const { createApp } = window.Vue; 28 createApp({ 29 data(){ return { me:null, api:{ present:false, value:'', show:false, busy:false, copied:false } } }, 30 async mounted(){ 31 try { const r = await fetch('/me'); if (r.ok) this.me = await r.json(); } catch {} 32 if (this.me && this.me.username) { 33 try { const r = await fetch('/api/token'); if (r.ok) { const j = await r.json(); this.api.present = !!j.present; } } catch {} 34 } 35 }, 36 methods:{ 37 async logout(){ await fetch('/logout', { method:'POST' }).catch(()=>{}); location.replace('/'); }, 38 async revealApiKey(){ this.api.busy=true; try { const r=await fetch('/api/token?reveal=1'); if(!r.ok) throw 0; const j=await r.json(); this.api.present=true; this.api.value=j.api_token||''; this.api.show=true; this.api.copied=false; } finally { this.api.busy=false; } }, 39 async generateApiKey(){ this.api.busy=true; try { const r=await fetch('/api/token',{method:'POST'}); if(!r.ok) throw 0; const j=await r.json(); this.api.present=true; this.api.value=j.api_token||''; this.api.show=true; this.api.copied=false; } finally { this.api.busy=false; } }, 40 async regenerateApiKey(){ return this.generateApiKey(); }, 41 async copyApiKey(){ try { await navigator.clipboard.writeText(this.api.value||''); this.api.copied=true; setTimeout(()=>{ this.api.copied=false; },1500); } catch(e){ alert('Failed to copy token'); } }, 42 } 43 }).mount('#hdr'); 44 </script> 45 </body> 46 </html>