// ==UserScript==
// @name Cool Calculator
// @namespace http://tampermonkey.net/
// @version 14.0
// @description Always-on-top draggable scientific calculator with optional AI chatbot and reset button
// @author Leon Luk
// @match *://*/*
// @grant none
// @license Proprietary
// ==/UserScript==
(function(){
'use strict';
const DEFAULT_WIDTH = 350;
const DEFAULT_HEIGHT = 600;
const DEFAULT_LEFT = 70;
const DEFAULT_TOP = window.innerHeight/2 - DEFAULT_HEIGHT/2;
const STORAGE_POS = 'leonluk_calc_pos';
const STORAGE_APIKEY = 'leonluk_openai_key';
let apiKey = localStorage.getItem(STORAGE_APIKEY);
function createCalculator(){
const calc = document.createElement('div');
Object.assign(calc.style,{
position:'fixed', top: DEFAULT_TOP+'px', left: DEFAULT_LEFT+'px',
width: DEFAULT_WIDTH+'px', height: DEFAULT_HEIGHT+'px',
padding:'15px', background:'linear-gradient(135deg,#ec4899,#8b5cf6)',
color:'white', borderRadius:'10px', boxShadow:'4px 0 15px rgba(0,0,0,0.6)',
zIndex:'99999999', fontFamily:'monospace', display:'flex', flexDirection:'column', overflow:'hidden'
});
const savedPos = JSON.parse(localStorage.getItem(STORAGE_POS));
if(savedPos){
calc.style.left = savedPos.left+'px';
calc.style.top = savedPos.top+'px';
calc.style.width = savedPos.width+'px';
calc.style.height = savedPos.height+'px';
}
calc.innerHTML = `
<div id="calc-header" style="display:flex;justify-content:space-between;align-items:center;font-weight:bold;margin-bottom:5px;cursor:move;font-size:16px;">
<span>Calculator</span>
<div>
<button id="refresh-btn" style="background:none;border:none;color:white;font-size:18px;cursor:pointer;line-height:1;padding:0;margin-right:5px;">⟳</button>
<button id="minimize-btn" style="background:none;border:none;color:white;font-size:20px;cursor:pointer;line-height:1;padding:0;">—</button>
</div>
</div>
<input type="text" id="calc-display" readonly style="width:100%;padding:10px;font-size:18px;border:none;border-radius:5px;text-align:right;background:rgba(0,0,0,0.3);color:white;margin-bottom:5px;flex-shrink:0;">
<div id="calc-buttons" style="display:grid;grid-template-columns:repeat(6,1fr);gap:5px;flex-shrink:0;"></div>
<div id="chat-container" style="margin-top:10px;flex-grow:1;"></div>
<div style="text-align:right;font-size:10px;opacity:0.8;">by Leon Luk</div>
`;
document.body.appendChild(calc);
const display = calc.querySelector('#calc-display');
const buttonsContainer = calc.querySelector('#calc-buttons');
const minimizeBtn = calc.querySelector('#minimize-btn');
const refreshBtn = calc.querySelector('#refresh-btn');
const chatContainer = calc.querySelector('#chat-container');
const buttons = ['7','8','9','/','C','(', '4','5','6','*','^',')', '1','2','3','-','√','x²','0','.','=','+','%','+/-','sin','cos','tan','asin','acos','atan','log','ln','pi','e'];
buttons.forEach(key=>{
const btn = document.createElement('button');
btn.innerText = key;
Object.assign(btn.style,{padding:'10px',fontSize:'14px',border:'none',borderRadius:'5px',background:'rgba(255,255,255,0.15)',color:'white',cursor:'pointer',transition:'background 0.2s ease, transform 0.1s ease'});
btn.onmouseenter = ()=>btn.style.background='rgba(255,255,255,0.25)';
btn.onmouseleave = ()=>btn.style.background='rgba(255,255,255,0.15)';
btn.onmousedown = ()=>btn.style.transform='scale(0.95)';
btn.onmouseup = ()=>btn.style.transform='scale(1)';
btn.addEventListener('click',()=>{
try{
if(key==='C'){display.value='';}
else if(key==='='){
let expr = display.value.replace(/\^/g,'**').replace(/√/g,'Math.sqrt')
.replace(/pi/g,'Math.PI').replace(/e/g,'Math.E')
.replace(/sin/g,'Math.sin').replace(/cos/g,'Math.cos').replace(/tan/g,'Math.tan')
.replace(/asin/g,'Math.asin').replace(/acos/g,'Math.acos').replace(/atan/g,'Math.atan')
.replace(/log/g,'Math.log10').replace(/ln/g,'Math.log');
display.value = eval(expr);
}
else if(key==='x²'){display.value+='**2';}
else if(key==='+/-'){display.value=display.value.startsWith('-')?display.value.slice(1):'-'+display.value;}
else{display.value += key;}
}catch{display.value='Error';}
});
buttonsContainer.appendChild(btn);
});
function setupChat(){
chatContainer.innerHTML=`
<div id="chatbot" style="flex-grow:1;display:flex;flex-direction:column;background:rgba(0,0,0,0.2);border-radius:5px;padding:5px;overflow:auto;">
<div id="chat-messages" style="flex-grow:1;font-size:12px;color:white;overflow-y:auto;display:flex;flex-direction:column;gap:3px;"></div>
<input id="chat-input" type="text" placeholder="Chat with AI..." style="width:100%;padding:5px;font-size:12px;border:none;border-radius:3px;margin-top:5px;background:rgba(255,255,255,0.15);color:white;">
</div>`;
const chatMessages = chatContainer.querySelector('#chat-messages');
const chatInput = chatContainer.querySelector('#chat-input');
chatInput.addEventListener('keydown', async e=>{
if(e.key==='Enter' && chatInput.value.trim()!=='' && apiKey){
const userMsg = chatInput.value.trim();
const msgDiv = document.createElement('div');
msgDiv.innerText='You: '+userMsg;
msgDiv.style.alignSelf='flex-end'; msgDiv.style.background='rgba(255,255,255,0.2)'; msgDiv.style.padding='3px 5px'; msgDiv.style.borderRadius='4px';
chatMessages.appendChild(msgDiv);
chatInput.value='';
const botDiv=document.createElement('div'); botDiv.innerText='AI: ...'; botDiv.style.alignSelf='flex-start'; botDiv.style.background='rgba(255,215,0,0.2)'; botDiv.style.padding='3px 5px'; botDiv.style.borderRadius='4px'; chatMessages.appendChild(botDiv);
try{
const response = await fetch('https://api.openai.com/v1/chat/completions',{method:'POST',headers:{'Content-Type':'application/json','Authorization':'Bearer '+apiKey},body:JSON.stringify({model:'gpt-3.5-turbo',messages:[{role:'user',content:userMsg}],max_tokens:150})});
const data = await response.json();
botDiv.innerText='AI: '+(data.choices?.[0]?.message?.content||'No response.');
}catch{botDiv.innerText='AI: Error connecting to API.';}
chatMessages.scrollTop = chatMessages.scrollHeight;
}
});
}
function showNoAPI(){
chatContainer.innerHTML=`<div style="flex-grow:1;display:flex;flex-direction:column;align-items:center;justify-content:center;color:white;font-size:12px;gap:5px;">
<div>No API key detected</div>
<button id="connect-api" style="padding:5px 10px;border:none;border-radius:5px;background:#7c3aed;color:white;cursor:pointer;">Connect API Key</button>
</div>`;
const connectBtn = chatContainer.querySelector('#connect-api');
connectBtn.addEventListener('click', ()=>{
apiKey = prompt('Enter your OpenAI API key:');
if(apiKey){
localStorage.setItem(STORAGE_APIKEY, apiKey);
setupChat();
}
});
}
if(apiKey){setupChat();} else {showNoAPI();}
minimizeBtn.addEventListener('click', ()=>{calc.style.display='none';});
refreshBtn.addEventListener('click', ()=>{
calc.style.width = DEFAULT_WIDTH+'px';
calc.style.height = DEFAULT_HEIGHT+'px';
calc.style.left = DEFAULT_LEFT+'px';
calc.style.top = DEFAULT_TOP+'px';
display.style.fontSize='18px';
buttonsContainer.querySelectorAll('button').forEach(b=>{b.style.fontSize='14px'; b.style.padding='10px';});
});
// Draggable logic
const header = calc.querySelector('#calc-header');
let dragging = false, offsetX=0, offsetY=0;
header.addEventListener('mousedown', e=>{dragging=true; offsetX=e.clientX-calc.getBoundingClientRect().left; offsetY=e.clientY-calc.getBoundingClientRect().top; calc.style.cursor='grabbing';});
document.addEventListener('mousemove', e=>{if(!dragging) return; e.preventDefault(); calc.style.left=(e.clientX-offsetX)+'px'; calc.style.top=(e.clientY-offsetY)+'px';});
document.addEventListener('mouseup', ()=>{if(dragging){dragging=false; calc.style.cursor='grab'; localStorage.setItem(STORAGE_POS, JSON.stringify({left:parseInt(calc.style.left), top:parseInt(calc.style.top), width:parseInt(calc.style.width), height:parseInt(calc.style.height)}));}});
// Resizable logic (same as before, omitted here for brevity)
// ... you can copy the previous resizer code from last version
return calc;
}
window.addEventListener('load', ()=>{
const calc = createCalculator();
const toggleBtn = document.createElement('button');
toggleBtn.innerText='🧮 Calculator';
Object.assign(toggleBtn.style,{position:'fixed',top:'50%',left:'10px',transform:'translateY(-50%)',zIndex:'10000000',padding:'10px 15px',fontSize:'14px',background:'linear-gradient(135deg,#d946ef,#7c3aed)',color:'white',border:'none',borderRadius:'8px',cursor:'pointer',boxShadow:'0 2px 8px rgba(0,0,0,0.3)'});
toggleBtn.onmouseenter=()=>toggleBtn.style.opacity='0.8';
toggleBtn.onmouseleave=()=>toggleBtn.style.opacity='1';
toggleBtn.addEventListener('click', ()=>{calc.style.display = (calc.style.display==='flex')?'none':'flex';});
document.body.appendChild(toggleBtn);
});
})();