Stunning radial menu with pizza slice trigger – pure design
// ==UserScript==
// @name Quick Access Wheel - Premium
// @namespace http://tampermonkey.net/
// @version 5.0
// @description Stunning radial menu with pizza slice trigger – pure design
// @author Mustafa Hakan
// @match *://*/*
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_addStyle
// @run-at document-end
// ==/UserScript==
(function() {
'use strict';
const DEFAULT_ACTIONS = [
{ icon: '🔍', name: 'Search', action: 'search', color: '#60a5fa' },
{ icon: '🌐', name: 'Translate', action: 'translate', color: '#38bdf8' },
{ icon: '📝', name: 'Note', action: 'note', color: '#facc15' },
{ icon: '🌙', name: 'Dark Mode', action: 'darkmode', color: '#a78bfa' },
{ icon: '🖨️', name: 'Print', action: 'print', color: '#f472b6' },
{ icon: '🔗', name: 'Copy Link', action: 'copylink', color: '#fbbf24' },
{ icon: '⚙️', name: 'Edit', action: 'edit', color: '#94a3b8' },
{ icon: '✕', name: 'Close', action: 'close', color: '#ef4444' }
];
let actions = JSON.parse(GM_getValue('qw_actions', JSON.stringify(DEFAULT_ACTIONS)));
let wheel = null;
let isOpen = false;
let savedSelection = '';
function saveSelection() {
savedSelection = window.getSelection().toString();
}
function createWheel() {
if (wheel) wheel.remove();
savedSelection = '';
wheel = document.createElement('div');
wheel.id = 'qw-wheel';
Object.assign(wheel.style, {
position: 'fixed', top: '50%', left: '50%',
transform: 'translate(-50%, -50%)',
width: '340px', height: '340px', zIndex: '2147483647',
pointerEvents: 'all', borderRadius: '50%',
background: 'transparent',
animation: 'qwFadeIn 0.3s ease'
});
const cx = 170, cy = 170, r = 135;
const step = (2 * Math.PI) / actions.length;
actions.forEach((act, i) => {
const start = i * step - Math.PI/2;
const end = (i+1) * step - Math.PI/2;
const mid = start + step/2;
const x1 = cx + Math.cos(start) * r, y1 = cy + Math.sin(start) * r;
const x2 = cx + Math.cos(end) * r, y2 = cy + Math.sin(end) * r;
const lx = cx + Math.cos(mid) * (r - 32), ly = cy + Math.sin(mid) * (r - 32);
const slice = document.createElement('div');
const clip = `polygon(50% 50%, ${50+(x1-170)/1.7}% ${50+(y1-170)/1.7}%, ${50+(x2-170)/1.7}% ${50+(y2-170)/1.7}%)`;
Object.assign(slice.style, {
position: 'absolute', inset: '0', clipPath: clip,
background: act.color + 'cc', cursor: 'pointer',
transition: '0.2s ease', opacity: '0',
animation: `qwSliceIn 0.3s ease ${i * 0.03}s forwards`
});
slice.title = act.name;
slice.addEventListener('mouseenter', () => {
slice.style.background = act.color;
slice.style.transform = 'scale(1.02)';
slice.style.filter = 'brightness(1.2)';
slice.style.zIndex = '2';
});
slice.addEventListener('mouseleave', () => {
slice.style.background = act.color + 'cc';
slice.style.transform = 'scale(1)';
slice.style.filter = '';
slice.style.zIndex = '1';
});
slice.addEventListener('click', (e) => {
e.stopPropagation();
handleAction(act);
});
wheel.appendChild(slice);
const icon = document.createElement('span');
icon.textContent = act.icon;
Object.assign(icon.style, {
position: 'absolute', left: lx-14+'px', top: ly-14+'px',
width: '28px', height: '28px', fontSize: '20px',
display: 'flex', alignItems: 'center', justifyContent: 'center',
pointerEvents: 'none', zIndex: '3',
textShadow: '0 2px 5px rgba(0,0,0,0.4)',
opacity: '0', animation: `qwSliceIn 0.3s ease ${i * 0.03 + 0.1}s forwards`
});
wheel.appendChild(icon);
const label = document.createElement('div');
label.textContent = act.name;
Object.assign(label.style, {
position: 'absolute', left: lx-30+'px', top: ly+12+'px',
width: '60px', textAlign: 'center', fontSize: '10px',
color: '#e2e8f0', fontWeight: '500', pointerEvents: 'none',
opacity: '0', animation: `qwSliceIn 0.3s ease ${i * 0.03 + 0.15}s forwards`,
textShadow: '0 1px 3px rgba(0,0,0,0.5)'
});
wheel.appendChild(label);
});
const centerBtn = document.createElement('div');
Object.assign(centerBtn.style, {
position: 'absolute', left: '50%', top: '50%',
transform: 'translate(-50%, -50%)', width: '56px', height: '56px',
borderRadius: '50%', background: '#1e1e2e',
border: '2px solid #475569', color: '#e2e8f0',
display: 'flex', alignItems: 'center', justifyContent: 'center',
fontSize: '22px', cursor: 'pointer', zIndex: '4',
transition: 'border-color 0.2s, background 0.2s'
});
centerBtn.innerHTML = '✕';
centerBtn.addEventListener('mouseenter', () => {
centerBtn.style.borderColor = '#ef4444';
centerBtn.style.background = '#2d1e1e';
});
centerBtn.addEventListener('mouseleave', () => {
centerBtn.style.borderColor = '#475569';
centerBtn.style.background = '#1e1e2e';
});
centerBtn.addEventListener('click', closeWheel);
wheel.appendChild(centerBtn);
document.body.appendChild(wheel);
isOpen = true;
document.addEventListener('keydown', onKey);
setTimeout(() => document.addEventListener('click', outsideClick), 60);
}
function handleAction(act) {
closeWheel();
if (act.action === 'close') return;
if (act.action === 'search') {
const q = savedSelection || prompt('Search:');
if (q) window.open('https://www.google.com/search?q=' + encodeURIComponent(q), '_blank');
} else if (act.action === 'translate') {
const t = savedSelection || prompt('Text to translate:');
if (t) window.open('https://translate.google.com/?sl=auto&tl=en&text=' + encodeURIComponent(t), '_blank');
} else if (act.action === 'note') {
openNote();
} else if (act.action === 'darkmode') {
document.documentElement.style.filter = document.documentElement.style.filter ? '' : 'invert(1) hue-rotate(180deg)';
document.querySelectorAll('img, video, canvas').forEach(el => el.style.filter = document.documentElement.style.filter ? 'invert(1) hue-rotate(180deg)' : '');
showToast(document.documentElement.style.filter ? '🌙 Dark mode on' : '☀️ Light mode');
} else if (act.action === 'print') {
window.print();
} else if (act.action === 'copylink') {
navigator.clipboard.writeText(location.href).then(() => showToast('🔗 Link copied'));
} else if (act.action === 'edit') {
openEditor();
}
}
function openNote() {
const div = document.createElement('div');
Object.assign(div.style, {
position: 'fixed', top: '50%', left: '50%', transform: 'translate(-50%, -50%)',
background: '#1e1e2e', border: '1px solid #475569', borderRadius: '16px',
padding: '20px', zIndex: '2147483650', color: '#e2e8f0', width: '320px',
boxShadow: '0 20px 50px rgba(0,0,0,0.8)', animation: 'qwFadeIn 0.25s ease'
});
const saved = GM_getValue('qw_note_' + location.hostname, '');
div.innerHTML = `
<div style="font-weight:600;margin-bottom:10px;">📝 Quick Note</div>
<textarea id="qw-note-text" style="width:100%;height:120px;background:#0f0f17;border:1px solid #475569;color:#e2e8f0;padding:10px;border-radius:8px;resize:none;">${saved}</textarea>
<div style="display:flex;gap:8px;margin-top:10px;">
<button id="qw-note-save" style="flex:1;padding:10px;border-radius:8px;border:none;background:#4ade80;color:#1e1e1e;font-weight:600;cursor:pointer;">Save</button>
<button id="qw-note-close" style="flex:1;padding:10px;border-radius:8px;border:1px solid #444;background:transparent;color:#94a3b8;cursor:pointer;">Close</button>
</div>
`;
document.body.appendChild(div);
div.querySelector('#qw-note-save').onclick = () => {
GM_setValue('qw_note_' + location.hostname, div.querySelector('#qw-note-text').value);
showToast('✅ Note saved');
div.remove();
};
div.querySelector('#qw-note-close').onclick = () => div.remove();
}
function openEditor() {
const ed = document.createElement('div');
Object.assign(ed.style, {
position: 'fixed', top: '50%', left: '50%', transform: 'translate(-50%, -50%)',
background: 'rgba(15,15,20,0.96)', border: '1px solid #333', borderRadius: '16px',
padding: '20px', zIndex: '2147483650', color: '#e2e8f0', minWidth: '360px',
boxShadow: '0 20px 60px rgba(0,0,0,0.8)', animation: 'qwFadeIn 0.25s ease'
});
ed.innerHTML = `
<div style="font-weight:600;margin-bottom:12px;">⚙️ Edit Wheel</div>
${actions.map((a,i) => `
<div style="display:flex;gap:6px;align-items:center;margin:5px 0;padding:8px;background:rgba(255,255,255,0.03);border-radius:8px;">
<input value="${a.icon}" data-i="${i}" class="e-icon" style="width:36px;text-align:center;background:#1e1e2e;border:1px solid #475569;color:#e2e8f0;padding:4px;border-radius:6px;font-size:18px;">
<input value="${a.name}" data-i="${i}" class="e-name" style="flex:1;background:#1e1e2e;border:1px solid #475569;color:#e2e8f0;padding:6px;border-radius:6px;">
<select data-i="${i}" class="e-action" style="background:#1e1e2e;border:1px solid #475569;color:#e2e8f0;padding:6px;border-radius:6px;">
${['search','translate','note','darkmode','print','copylink','edit','close'].map(act => `<option ${a.action===act?'selected':''}>${act}</option>`).join('')}
</select>
<input type="color" value="${a.color}" data-i="${i}" class="e-color" style="width:32px;height:32px;border:none;border-radius:6px;">
</div>
`).join('')}
<div style="display:flex;gap:8px;margin-top:10px;">
<button id="e-save" style="flex:1;padding:10px;border-radius:8px;border:none;background:#4ade80;color:#1e1e1e;font-weight:600;cursor:pointer;">Save</button>
<button id="e-reset" style="flex:1;padding:10px;border-radius:8px;border:1px solid #444;background:transparent;color:#94a3b8;cursor:pointer;">Reset</button>
<button id="e-close" style="flex:1;padding:10px;border-radius:8px;border:1px solid #444;background:transparent;color:#94a3b8;cursor:pointer;">Close</button>
</div>
`;
document.body.appendChild(ed);
ed.querySelector('#e-save').onclick = () => {
const icons = ed.querySelectorAll('.e-icon'), names = ed.querySelectorAll('.e-name');
const acts = ed.querySelectorAll('.e-action'), colors = ed.querySelectorAll('.e-color');
icons.forEach((inp, i) => { actions[i].icon = inp.value || '•'; actions[i].name = names[i].value || 'Action'; actions[i].action = acts[i].value; actions[i].color = colors[i].value; });
GM_setValue('qw_actions', JSON.stringify(actions));
ed.remove();
showToast('✅ Wheel updated');
};
ed.querySelector('#e-reset').onclick = () => {
actions = JSON.parse(JSON.stringify(DEFAULT_ACTIONS));
GM_setValue('qw_actions', JSON.stringify(actions));
ed.remove();
showToast('🔄 Defaults restored');
};
ed.querySelector('#e-close').onclick = () => ed.remove();
}
function closeWheel() {
if (wheel) {
wheel.style.transition = 'opacity 0.2s, transform 0.2s';
wheel.style.opacity = '0';
wheel.style.transform = 'translate(-50%, -50%) scale(0.8)';
setTimeout(() => { if (wheel) wheel.remove(); wheel = null; }, 200);
}
isOpen = false;
document.removeEventListener('keydown', onKey);
document.removeEventListener('click', outsideClick);
}
function outsideClick(e) {
if (wheel && !wheel.contains(e.target)) closeWheel();
}
function onKey(e) {
if (e.key === 'Escape') { e.preventDefault(); closeWheel(); }
}
function showToast(msg) {
const t = document.createElement('div');
t.textContent = msg;
Object.assign(t.style, {
position: 'fixed', bottom: '80px', left: '50%', transform: 'translateX(-50%)',
background: '#1e1e2e', color: '#e2e8f0', padding: '10px 20px', borderRadius: '25px',
zIndex: '2147483648', font: '13px system-ui', boxShadow: '0 8px 24px rgba(0,0,0,0.6)',
animation: 'qwToast 2s forwards', pointerEvents: 'none'
});
document.body.appendChild(t);
setTimeout(() => t.remove(), 2000);
}
function createTrigger() {
const trig = document.createElement('div');
trig.id = 'qw-trigger';
trig.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="28" height="28"><path d="M50 10 L85 30 A45 45 0 0 1 50 90 Z" fill="#e2e8f0" stroke="#475569" stroke-width="2"/><circle cx="50" cy="50" r="6" fill="#e2e8f0" stroke="#475569" stroke-width="1.5"/></svg>`;
trig.title = 'Quick Access Wheel';
Object.assign(trig.style, {
position: 'fixed', bottom: '24px', right: '24px', width: '48px', height: '48px',
background: '#1e1e2e', border: '1px solid #475569', borderRadius: '12px',
display: 'flex', alignItems: 'center', justifyContent: 'center',
cursor: 'pointer', zIndex: '2147483644', boxShadow: '0 4px 12px rgba(0,0,0,0.6)',
transition: 'transform 0.2s, border-color 0.2s'
});
trig.addEventListener('mouseenter', () => {
trig.style.transform = 'rotate(15deg) scale(1.08)';
trig.style.borderColor = '#94a3b8';
});
trig.addEventListener('mouseleave', () => {
trig.style.transform = 'rotate(0deg) scale(1)';
trig.style.borderColor = '#475569';
});
trig.addEventListener('click', (e) => {
e.stopPropagation();
if (isOpen) closeWheel();
else { saveSelection(); createWheel(); }
});
document.body.appendChild(trig);
}
document.addEventListener('contextmenu', (e) => {
if (e.target.closest('#qw-wheel') || e.target.closest('#qw-trigger')) return;
if (isOpen) { closeWheel(); return; }
e.preventDefault();
saveSelection();
createWheel();
});
document.addEventListener('keydown', (e) => {
if (e.ctrlKey && e.shiftKey && e.key === 'M') {
e.preventDefault();
if (isOpen) closeWheel();
else openEditor();
}
});
GM_addStyle(`
@keyframes qwFadeIn { from { opacity: 0; transform: translate(-50%, -50%) scale(0.9); } }
@keyframes qwSliceIn { from { opacity: 0; transform: scale(0.8); } to { opacity: 1; transform: scale(1); } }
@keyframes qwToast { 0% { opacity: 0; transform: translateX(-50%) translateY(10px); } 15% { opacity: 1; transform: translateX(-50%) translateY(0); } 85% { opacity: 1; } 100% { opacity: 0; transform: translateX(-50%) translateY(-20px); } }
`);
setTimeout(createTrigger, 600);
})();