Quick Access Wheel - Premium

Stunning radial menu with pizza slice trigger – pure design

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==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);
})();