// ==UserScript== // @name OGS Custom Cosmetics + UI/UX // @namespace https://soumyak4.in // @version 2.9 // @description Clean UI, custom background (URL/upload/reset), scroll nav, dock buttons (incl. Toggle UI, AI Sensei & Move Timing). Includes Shift/Ctrl+Scroll behavior, dock item removal by text match, and SGF-to-AI-Sensei integration. // @author SoumyaK4 // @match https://online-go.com/game/* // @match https://online-go.com/review/* // @match https://online-go.com/demo/* // @downloadURL https://raw.githubusercontent.com/SoumyaK4/OGS-Tampermonkey/main/OGS-Cosmetic.user.js // @updateURL https://raw.githubusercontent.com/SoumyaK4/OGS-Tampermonkey/main/OGS-Cosmetic.user.js // @license MIT // ==/UserScript== (function () { 'use strict'; // Default background image URL const DEFAULT_BG = 'https://raw.githubusercontent.com/JaKooLit/Wallpaper-Bank/main/wallpapers/Sun-Setting-Horizon.png'; /** Utility: Wait for an element to appear in the DOM */ const waitFor = (selector) => new Promise(resolve => { const found = document.querySelector(selector); if (found) return resolve(found); const observer = new MutationObserver(() => { const el = document.querySelector(selector); if (el) { observer.disconnect(); resolve(el); } }); observer.observe(document.body, { childList: true, subtree: true }); }); /** Remove top navigation bars, sidebars, and action bars */ const cleanLayout = () => { ['.NavBar', '.AccessibilityMenu', '.Announcements', '.left-col'].forEach(sel => { document.querySelector(sel)?.remove(); }); const actionBar = document.querySelector('.action-bar'); if (actionBar) actionBar.style.display = 'none'; document.querySelector('div.Game.MainGobanView.wide')?.style.setProperty('top', '0'); }; /** Inject CSS overrides to hide headers and clean UI */ const injectCSS = () => { const css = ` .action-bar, .NavBar, header, .SiteHeader, .TopBar, .NavigationBar { display: none !important; height: 0 !important; padding: 0 !important; margin: 0 !important; } div.Game.MainGobanView.wide { top: 0 !important; } html, body { min-height: 100%; margin: 0; } #main-content { background-color: transparent !important; } `; const style = document.createElement('style'); style.textContent = css; document.head.appendChild(style); }; /** Apply custom background (default or user-set) */ const setCustomBackground = () => { const url = localStorage.getItem('ogs-custom-bg') || DEFAULT_BG; document.documentElement.style.backgroundImage = `url('${url}')`; document.documentElement.style.backgroundSize = 'cover'; document.documentElement.style.backgroundPosition = 'center'; document.documentElement.style.backgroundRepeat = 'no-repeat'; const container = document.getElementById('default-variant-container'); if (container) { container.style.backgroundImage = `url('${url}')`; container.style.backgroundSize = 'cover'; container.style.backgroundPosition = 'center'; container.style.backgroundRepeat = 'no-repeat'; container.style.backgroundColor = 'transparent'; } }; /** Show popup menu for background options (reset/url/upload) */ const backgroundOptionMenu = () => { const container = document.createElement('div'); container.style.cssText = ` position: fixed; top: 100px; left: 50%; transform: translateX(-50%); background: #222; color: white; padding: 10px 20px; border-radius: 8px; z-index: 9999; font-family: sans-serif; box-shadow: 0 0 10px rgba(0,0,0,0.5); text-align: center; `; container.innerHTML = `

Set Background


`; document.body.appendChild(container); document.getElementById('reset-bg').onclick = () => { localStorage.removeItem('ogs-custom-bg'); setCustomBackground(); container.remove(); }; document.getElementById('url-bg').onclick = () => { const url = prompt('Enter image URL:'); if (url?.trim()) { localStorage.setItem('ogs-custom-bg', url.trim()); setCustomBackground(); } container.remove(); }; document.getElementById('upload-bg').onclick = () => { const input = document.createElement('input'); input.type = 'file'; input.accept = 'image/*'; input.onchange = () => { const file = input.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = () => { localStorage.setItem('ogs-custom-bg', reader.result); setCustomBackground(); }; reader.readAsDataURL(file); }; input.click(); container.remove(); }; document.getElementById('close-bg').onclick = () => container.remove(); }; /** Add "Set Background" button to dock */ const addBackgroundSetterButton = async () => { const dock = await waitFor('div.Dock'); if (dock.querySelector('.set-bg-button')) return; const bgButton = document.createElement('div'); bgButton.className = 'TooltipContainer set-bg-button'; bgButton.innerHTML = `

Set Custom Background

Set Background
`; bgButton.addEventListener('click', backgroundOptionMenu); const homeBtn = dock.querySelector('.home-dock-button'); if (homeBtn) { dock.insertBefore(bgButton, homeBtn); } else { dock.appendChild(bgButton); } }; /** Add "Home" button to dock */ const addHomeDockButton = async () => { const dock = await waitFor('div.Dock'); if (!dock.querySelector('.home-dock-button')) { const home = document.createElement('div'); home.className = 'TooltipContainer home-dock-button'; home.innerHTML = `

Back to OGS Home

Home
`; dock.appendChild(home); } }; /** Add "Toggle UI" button to dock */ const addToggleUIButton = async () => { const dock = await waitFor('div.Dock'); if (dock.querySelector('.toggle-ui-button')) return; const toggleButton = document.createElement('div'); toggleButton.className = 'TooltipContainer toggle-ui-button'; toggleButton.innerHTML = `

Toggle Analyze Tools

Toggle Analyze UI
`; toggleButton.addEventListener('click', () => { const playControls = document.querySelector('.PlayControls'); if (playControls) { playControls.style.display = (playControls.style.display === 'none') ? '' : 'none'; } ['.NavBar', '.AccessibilityMenu', '.Announcements', '.left-col', '.action-bar'].forEach(sel => { const el = document.querySelector(sel); if (el) el.style.display = (el.style.display === 'none') ? '' : 'none'; }); }); const homeBtn = dock.querySelector('.home-dock-button'); if (homeBtn) { dock.insertBefore(toggleButton, homeBtn); } else { dock.appendChild(toggleButton); } }; /** Add "AI Sensei" button to dock */ const addAISenseiButton = async () => { const dock = await waitFor('div.Dock'); if (dock.querySelector('.ai-sensei-button')) return; const aiButton = document.createElement('div'); aiButton.className = 'TooltipContainer ai-sensei-button'; aiButton.innerHTML = `

Analyze with AI Sensei

AI Sensei AI Sensei
`; aiButton.addEventListener('click', () => { try { // 1. Extract game ID from URL const gameUrl = location.href; const link = `https://ai-sensei.com/upload?sgf=${encodeURIComponent(gameUrl)}`; window.open(link, '_blank'); } catch (err) { console.error("AI Sensei Error:", err); alert("Failed to send SGF to AI Sensei."); } }); const homeBtn = dock.querySelector('.move-timing-button'); if (homeBtn) { dock.insertBefore(aiButton, homeBtn); } else { dock.appendChild(aiButton); } }; /** Add "Move Timing" button to dock (toggles external script) */ const addMoveTimingButton = async () => { const dock = await waitFor('div.Dock'); if (dock.querySelector('.move-timing-button')) return; const mtButton = document.createElement('div'); mtButton.className = 'TooltipContainer move-timing-button'; mtButton.innerHTML = `

Toggle Move Timing

Move Timing
`; let enabled = false; mtButton.addEventListener('click', () => { if (!enabled) { const e = document.createElement("script"); e.src = `https://psalaets.github.io/ogs-move-timing/bookmarklet.js?${Date.now()}`; e.type = "module"; e.onload = () => setTimeout(() => e.remove(), 1000); document.body.appendChild(e); enabled = true; } else { location.reload(); // easiest way to disable cleanly } }); const homeBtn = dock.querySelector('.home-dock-button'); if (homeBtn) { dock.insertBefore(mtButton, homeBtn); } else { dock.appendChild(mtButton); } }; /** Remove unwanted dock items by text match */ const removeDockItems = async () => { const dock = await waitFor('div.Dock'); const items = dock.querySelectorAll('div.TooltipContainer'); const removeTexts = [ 'Zen mode', 'Fork game', 'Analyze game', 'Link to game', 'Add to library', 'Download SGF', 'SGF with comments', 'Link to review' ]; items.forEach(item => { const text = item.innerText.trim(); if (removeTexts.some(word => text.includes(word))) { item.remove(); } }); }; /** Enable scroll navigation on the board */ const enableScrollNavigation = async () => { const goban = await waitFor('.goban-container'); goban.onwheel = (e) => { e.preventDefault(); const controls = document.querySelector('.action-bar .controls')?.children; if (!controls) return; if (e.ctrlKey) { (e.deltaY > 0 ? controls[6] : controls[0])?.click(); } else if (e.shiftKey) { (e.deltaY > 0 ? controls[5] : controls[1])?.click(); } else { (e.deltaY > 0 ? controls[4] : controls[2])?.click(); } }; }; /** Initialize all modifications when page loads */ const onPageLoad = async () => { cleanLayout(); injectCSS(); setCustomBackground(); addBackgroundSetterButton(); addHomeDockButton(); addToggleUIButton(); addMoveTimingButton(); addAISenseiButton(); removeDockItems(); enableScrollNavigation(); }; /** Observe URL changes (for SPA navigation) */ const observeUrlChange = () => { let currentUrl = location.href; new MutationObserver(() => { if (location.href !== currentUrl) { currentUrl = location.href; onPageLoad(); } }).observe(document, { childList: true, subtree: true }); }; // Run on page load and observe future navigation window.addEventListener('load', onPageLoad); observeUrlChange(); })();