Skip to content

Instantly share code, notes, and snippets.

@SoumyaK4
Created July 16, 2026 19:31
Show Gist options
  • Select an option

  • Save SoumyaK4/527634f02694d3de9e73123f9c611726 to your computer and use it in GitHub Desktop.

Select an option

Save SoumyaK4/527634f02694d3de9e73123f9c611726 to your computer and use it in GitHub Desktop.
Sensei's Library Tampermonkey Script
// ==UserScript==
// @name Sensei's Library - SoumyaK4 Dark + Popup SGF
// @namespace https://senseis.xmp.net/
// @version 0.5.8
// @description Clean dark theme for Sensei's Library with a compact BesoGo SGF popup.
// @author SoumyaK4
// @license MIT
// @match https://senseis.xmp.net/*
// @match http://senseis.xmp.net/*
// @include file://*/senseis-library/*
// @require https://cdn.jsdelivr.net/gh/yewang/besogo@59cd44cc1a7ffd2e85bafb3634cdf7c874871b71/js/besogo.js
// @require https://cdn.jsdelivr.net/gh/yewang/besogo@59cd44cc1a7ffd2e85bafb3634cdf7c874871b71/js/editor.js
// @require https://cdn.jsdelivr.net/gh/yewang/besogo@59cd44cc1a7ffd2e85bafb3634cdf7c874871b71/js/gameRoot.js
// @require https://cdn.jsdelivr.net/gh/yewang/besogo@59cd44cc1a7ffd2e85bafb3634cdf7c874871b71/js/svgUtil.js
// @require https://cdn.jsdelivr.net/gh/yewang/besogo@59cd44cc1a7ffd2e85bafb3634cdf7c874871b71/js/parseSgf.js
// @require https://cdn.jsdelivr.net/gh/yewang/besogo@59cd44cc1a7ffd2e85bafb3634cdf7c874871b71/js/loadSgf.js
// @require https://cdn.jsdelivr.net/gh/yewang/besogo@59cd44cc1a7ffd2e85bafb3634cdf7c874871b71/js/boardDisplay.js
// @require https://cdn.jsdelivr.net/gh/yewang/besogo@59cd44cc1a7ffd2e85bafb3634cdf7c874871b71/js/coord.js
// @require https://cdn.jsdelivr.net/gh/yewang/besogo@59cd44cc1a7ffd2e85bafb3634cdf7c874871b71/js/commentPanel.js
// @require https://cdn.jsdelivr.net/gh/yewang/besogo@59cd44cc1a7ffd2e85bafb3634cdf7c874871b71/js/treePanel.js
// @resource besogoCSS https://yewang.github.io/besogo/css/besogo.css
// @resource boardCSS https://yewang.github.io/besogo/css/board-kibitz.css
// @grant GM_addStyle
// @grant GM_getResourceText
// @grant GM_xmlhttpRequest
// @connect *
// @run-at document-end
// ==/UserScript==
(() => {
'use strict';
const $ = (selector, root = document) => root.querySelector(selector);
const $$ = (selector, root = document) => [...root.querySelectorAll(selector)];
const sgfCache = new Map();
let requestId = 0;
const css = `
:root{color-scheme:dark;--bg:#090b0f;--panel:#0f141b;--panel2:#151b24;--text:#eef3f8;--muted:#c8d1dc;--faint:#8f9baa;--link:#f1cf88;--border:rgba(150,162,178,.18);--accent:rgba(216,177,95,.34);--bad:#ffd1db;--font:Inter,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Arial,sans-serif;--mono:"JetBrains Mono",Consolas,monospace}
*,*::before,*::after{box-sizing:border-box!important;letter-spacing:0!important}
html{background:var(--bg)!important}
html.sl-s4-popup-open{overflow:hidden!important}
body,body#browse{margin:0!important;min-height:100vh!important;background:linear-gradient(#090b0f,#0b0e13 50%,#090b0f)!important;color:var(--muted)!important;font:15.5px/1.68 var(--font)!important;-webkit-font-smoothing:antialiased!important}
body:has(#leftpane){display:grid!important;grid-template:"top top" auto "side main" 1fr/218px minmax(0,1fr)!important}
body:not(:has(#leftpane)){display:grid!important;grid-template:"top" auto "main" 1fr/minmax(0,1fr)!important}
.messagepage,.mainpage,.subpage,.discussionpage,#contentpane,#contentwrapper,#leftpane,.left-mainpage,.left-subpage,.left-discussionpage,.left-messagepage{background:transparent!important;background-image:none!important;color:var(--muted)!important}
#contentwrapper{grid-area:main!important;width:100%!important;max-width:1100px!important;margin:0 auto!important;padding:32px 34px 64px!important;position:relative!important}
#contentpane{position:static!important;float:none!important;width:auto!important;margin:0!important;padding:0!important;border:0!important}
.contentsize{max-width:82ch!important;margin:0 auto!important}
#toppane{grid-area:top!important;position:sticky!important;top:0!important;z-index:100000!important;display:flex!important;align-items:center!important;flex-wrap:wrap!important;gap:6px 8px!important;min-height:44px!important;padding:7px 14px!important;background:rgba(11,14,19,.96)!important;border-bottom:1px solid var(--border)!important;box-shadow:0 10px 28px rgba(0,0,0,.28)!important}
#toolbox,#toolbox2,#toolbox3,#editmenu,#searchboxdiv,#searchboxdiv form{display:inline-flex!important;align-items:center!important;gap:6px!important;margin:0!important;border-left:0!important;border-right:0!important}
#toolbox3{order:1!important}#editmenu{order:2!important}#toolbox2{order:3!important;margin-left:auto!important}#toolbox{order:4!important}#searchboxdiv{order:5!important;margin-left:8px!important}
#toppane ul,#toppane li{list-style:none!important;margin:0!important;padding:0!important}
#toolbox a,#editmenu a,.toolmenu>span,#searchboxdiv button,button,input[type=submit],input[type=button]{display:inline-flex!important;align-items:center!important;justify-content:center!important;min-height:30px!important;padding:0 10px!important;background:#12171f!important;border:1px solid var(--border)!important;border-radius:8px!important;color:var(--muted)!important;box-shadow:none!important;font:800 12.5px/1 var(--font)!important;text-decoration:none!important;cursor:pointer!important}
#toolbox a:hover,#editmenu a:hover,.toolmenu:not(#toolbox3):not(#toolbox2):hover>span,.toolmenu.sl-s4-open:not(#toolbox3):not(#toolbox2)>span,#searchboxdiv button:hover,button:hover,input[type=submit]:hover,input[type=button]:hover{background:#18202b!important;border-color:var(--accent)!important;color:var(--link)!important}
#toolbox2>span,#toolbox3>span{background:#12171f!important;border-color:var(--border)!important;color:var(--text)!important}
#toolbox2:hover>span,#toolbox2>span:hover,#toolbox3:hover>span,#toolbox3>span:hover{background:#12171f!important;border-color:var(--border)!important;color:var(--text)!important}
#toolbox a:hover{background:#12171f!important;border-color:var(--border)!important;color:var(--muted)!important}
#toolbox3>span::after{content:"Home";margin-left:8px;color:var(--faint);font-size:11px}
.toolmenu{position:relative!important;display:inline-block!important;background:transparent!important;border-left:0!important;border-right:0!important}
.toolmenu:hover,.toolmenu.over,.toolmenu.sl-s4-open{background:transparent!important;border-left:0!important;border-right:0!important}
.toolmenu ul{display:none!important;position:absolute!important;top:100%!important;left:0!important;z-index:2147483000!important;min-width:220px!important;margin:0!important;padding:8px!important;background:#0d1117!important;border:1px solid var(--border)!important;border-radius:8px!important;box-shadow:0 18px 40px rgba(0,0,0,.45)!important}
.toolmenu:hover ul,.toolmenu.sl-s4-open ul{display:block!important}
.toolmenu ul li.separator{border-top:1px solid var(--border)!important;margin-top:6px!important;padding-top:6px!important}
.toolmenu ul a{display:block!important;width:100%!important;padding:8px 10px!important;background:transparent!important;border:0!important;border-radius:6px!important;color:var(--muted)!important;text-align:left!important;white-space:nowrap!important}
.toolmenu ul a:hover{background:#171d26!important;color:var(--link)!important}
#searchboxdiv form{padding:2px!important;background:#0c1016!important;border:1px solid var(--border)!important;border-radius:9px!important}
#searchbox,#searchboxdiv input[name=searchstring],input[type=text],input[type=password],textarea,select{min-height:30px!important;padding:5px 10px!important;background:#090c11!important;border:1px solid var(--border)!important;border-radius:7px!important;color:var(--text)!important;font:600 13px/1.3 var(--font)!important;outline:none!important}
#searchbox{width:170px!important;border-color:transparent!important}
#searchbox:focus,input:focus,textarea:focus,select:focus{border-color:var(--accent)!important}
#searchboxdiv input[type=image]{display:none!important}
#searchboxdiv button.sl-s4-search{background:transparent!important;border-color:transparent!important;color:var(--faint)!important}
#leftpane{grid-area:side!important;width:218px!important;min-width:218px!important;margin:0!important;padding:18px 12px!important;background:#0b0f14!important;border-right:1px solid var(--border)!important;text-align:left!important}
#leftpane img{display:block!important;max-width:62px!important;height:auto!important;margin:0 auto 12px!important;filter:none!important}
#bookmarks,#related{margin:0 0 6px!important;padding:5px!important;background:var(--panel)!important;border:1px solid var(--border)!important;border-radius:8px!important;line-height:1!important}
#related p{margin:0 0 4px!important}#related p:last-child{margin-bottom:0!important}
#leftpane br{display:none!important}
#leftpane strong,#related b{display:block!important;margin:2px 0 4px!important;padding:0 0 4px!important;border-bottom:1px solid var(--border)!important;color:var(--faint)!important;font-size:10.5px!important;font-weight:900!important;text-transform:uppercase!important}
#leftpane a,#bookmarks a,#related a{display:block!important;margin:0!important;padding:3px 7px!important;background:transparent!important;border:1px solid transparent!important;border-radius:6px!important;color:var(--muted)!important;font-size:12px!important;font-weight:700!important;line-height:1.05!important;text-decoration:none!important;overflow-wrap:anywhere!important}
#bookmarks a{background:#121820!important;border-color:rgba(150,162,178,.12)!important}
#related a{color:#aeb9c7!important}
#leftpane a:hover,#bookmarks a:hover,#related a:hover{padding-left:9px!important;background:#171e28!important;border-color:rgba(216,177,95,.26)!important;color:var(--link)!important}
h1,h2,h3,h4,h1#pagetitle{color:var(--text)!important;line-height:1.22!important}
h1{font-size:2.3rem!important}h2{margin:42px 0 18px!important;padding-bottom:10px!important;border-bottom:1px solid var(--border)!important;font-size:1.48rem!important}h3{margin:30px 0 12px!important;font-size:1.18rem!important}h4{font-size:1rem!important}
h1#pagetitle{margin:4px 0 38px!important;text-align:center!important}h1#pagetitle img{max-width:100%!important;height:auto!important}
p{margin:0 0 1.05em!important;color:var(--muted)!important}b,strong{color:var(--text)!important}small,.path,#pageheaders,#pgfooter{color:var(--faint)!important}
a[href]{color:var(--link)!important;text-decoration:none!important;border-bottom:1px solid rgba(216,177,95,.28)!important}a[href]:visited{color:#c9b1ff!important}a[href]:hover{color:#fff0c6!important;border-bottom-color:var(--link)!important}
.contentsize>ul,.contentsize>ol,.contentsize dl,blockquote,#toc,#subs,table.wiki,.contentsize>table,.contentsize table[border],#pgfooter,#footer,#pagefooter,div.sidebarleft,div.sidebarright,div.inlineimg,fieldset,.postingheader,.postingbody,.postingfooter,.postinginfo,div.relatedchanges,table.white,table.threadlist,table.diff,.warning{background:var(--panel)!important;background-image:none!important;border:1px solid var(--border)!important;border-radius:8px!important;box-shadow:none!important;color:var(--muted)!important;padding:12px!important}
.contentsize>ul,.contentsize>ol{padding:20px 26px 20px 44px!important;margin:24px 0!important}.contentsize dl{padding:20px 24px!important;margin:24px 0!important}.contentsize dd{margin-left:0!important}
table,tbody,thead,tfoot,tr,td,th,.odd,.even,table.wiki tr.odd,table.wiki tr.even{background:transparent!important;background-image:none!important;color:var(--muted)!important;border-color:var(--border)!important}
table.wiki th,table.wiki tr:first-child td,.contentsize table tr:first-child td,#toc td,#subs td{background:var(--panel2)!important;color:var(--text)!important}
td,th{padding:.55em!important}hr{border:0!important;border-top:1px solid var(--border)!important}
a.forum,a.forum2,span.forumlocked{display:inline-flex!important;align-items:center!important;min-height:28px!important;padding:4px 8px!important;background:var(--panel2)!important;border:1px solid var(--border)!important;border-radius:6px!important;color:var(--link)!important;font:800 13px/1 var(--font)!important;text-decoration:none!important}
a.forum:hover,a.forum2:hover{background:#18202b!important;border-color:var(--accent)!important;color:#fff0c6!important}
pre,code{background:#070a0e!important;border:1px solid var(--border)!important;border-radius:7px!important;color:#dce9ff!important;font-family:var(--mono)!important}pre{padding:16px 18px!important;overflow:auto!important}
.delete{background:rgba(255,113,139,.16)!important;color:var(--bad)!important}.added{background:rgba(111,213,157,.14)!important;color:#c9f3d8!important}
#contentpane [bgcolor],#contentpane [style*="background-color"],#contentpane [style*="background:"]{background-color:var(--panel)!important;background-image:none!important;color:var(--muted)!important}
div.diagram{display:inline-block!important;margin:8px 8px 8px 0!important;padding:7px!important;background:var(--panel2)!important;border:1px solid var(--border)!important;border-radius:8px!important;box-shadow:none!important}
div.diagram:hover{border-color:var(--accent)!important}
div.diagram a.sl-s4-diagram{position:relative!important;display:inline-block!important;border:0!important}
div.diagram a.sl-s4-diagram::after{content:"Open SGF";position:absolute;right:8px;bottom:8px;padding:3px 8px;background:rgba(7,10,15,.84);border:1px solid var(--accent);border-radius:999px;color:var(--link);font-size:11px;font-weight:900;opacity:0;transform:translateY(3px);transition:.16s;pointer-events:none}
div.diagram:hover a.sl-s4-diagram::after,div.diagram a.sl-s4-diagram:focus::after{opacity:1;transform:none}
a.sl-s4-sgf:not(.sl-s4-diagram)::after{content:"SGF";margin-left:.45em;padding:1px 7px;background:rgba(216,177,95,.11);border:1px solid var(--accent);border-radius:999px;color:var(--link);font-size:.66em;font-weight:900}
img.right{float:right!important;max-width:min(320px,42vw)!important;height:auto!important;margin:0 0 22px 28px!important;border:1px solid var(--border)!important;border-radius:8px!important}
#sl-s4-sgf-overlay{position:fixed!important;inset:0!important;z-index:2147483647!important}
#sl-s4-sgf-backdrop{position:absolute!important;inset:0!important;background:rgba(0,0,0,.8)!important;backdrop-filter:blur(7px)!important}
#sl-s4-sgf-modal{position:absolute!important;top:50%!important;left:50%!important;width:min(980px,calc(100vw - 56px))!important;height:min(720px,calc(100vh - 56px))!important;transform:translate(-50%,-50%)!important;display:flex!important;flex-direction:column!important;overflow:hidden!important;background:#0a0d12!important;border:1px solid rgba(150,162,178,.22)!important;border-radius:12px!important;box-shadow:0 28px 90px rgba(0,0,0,.72)!important;color:var(--text)!important}
#sl-s4-sgf-header{display:flex!important;align-items:center!important;justify-content:space-between!important;flex-wrap:wrap!important;gap:10px 14px!important;padding:10px 12px!important;background:var(--panel)!important;border-bottom:1px solid var(--border)!important}
#sl-s4-sgf-title{display:flex!important;align-items:center!important;gap:10px!important;min-width:240px!important;flex:1 1 auto!important}
#sl-s4-sgf-title b{display:block!important;max-width:min(56vw,760px)!important;overflow:hidden!important;text-overflow:ellipsis!important;white-space:nowrap!important;font-size:15px!important}
#sl-s4-sgf-title span{display:inline-flex!important;align-items:center!important;justify-content:center!important;width:36px!important;height:36px!important;background:var(--panel2)!important;border:1px solid var(--accent)!important;border-radius:8px!important;color:var(--link)!important;font-size:12px!important;font-weight:900!important}
#sl-s4-sgf-title a{border:0!important;color:var(--faint)!important;font-size:12px!important;font-weight:800!important}
#sl-s4-sgf-actions{display:flex!important;align-items:center!important;justify-content:flex-end!important;gap:6px!important}
#sl-s4-sgf-actions button{display:inline-flex!important;align-items:center!important;justify-content:center!important;height:32px!important;min-height:32px!important;padding:0 10px!important;background:#131922!important;border:1px solid var(--border)!important;border-radius:7px!important;color:var(--muted)!important;font:800 12px/1 var(--font)!important}
#sl-s4-sgf-actions button:hover{background:#1a222d!important;border-color:var(--accent)!important;color:var(--link)!important}
#sl-s4-sgf-actions button:disabled{opacity:.42!important;pointer-events:none!important}
#sl-s4-sgf-close{border-color:rgba(255,113,139,.32)!important;background:rgba(255,113,139,.08)!important;color:var(--bad)!important}
#sl-s4-sgf-body{flex:1!important;display:flex!important;align-items:center!important;justify-content:center!important;min-height:0!important;padding:10px!important;overflow:hidden!important;background:#080b10!important}
.sl-s4-status{display:flex!important;align-items:center!important;justify-content:center!important;flex-direction:column!important;gap:8px!important;min-height:min(62vh,520px)!important;padding:24px!important;background:#0d1218!important;border:1px solid var(--border)!important;border-radius:8px!important;text-align:center!important}
.sl-s4-status b{font-size:15px!important}.sl-s4-status span{max-width:min(760px,80vw)!important;color:var(--faint)!important;font-size:12px!important;font-weight:700!important;overflow-wrap:anywhere!important}
#sl-s4-sgf-body .besogo-editor{width:100%!important;height:100%!important;max-width:100%!important;margin:auto!important;background:#0d1218!important;border:1px solid var(--accent)!important;border-radius:8px!important;overflow:auto!important}
#sl-s4-sgf-body .besogo-board,#sl-s4-sgf-body .besogo-panels{max-height:100%!important}
#sl-s4-sgf-body .besogo-board,#sl-s4-sgf-body svg{background:#e2bf67!important}
#sl-s4-sgf-body .besogo-panels,#sl-s4-sgf-body .besogo-panel,#sl-s4-sgf-body .besogo-comment,#sl-s4-sgf-body .besogo-tree{background:var(--panel)!important;color:var(--muted)!important;border-color:var(--border)!important}
#sl-s4-sgf-body .besogo-editor input{min-height:28px!important;margin:2px!important;padding:4px 8px!important;background:var(--panel2)!important;border:1px solid var(--border)!important;border-radius:6px!important;color:var(--text)!important;font-size:13px!important;font-weight:700!important;box-shadow:none!important}
#sl-s4-sgf-body .besogo-comment>input[value="Comment"],#sl-s4-sgf-body .besogo-comment>input[value="Edit Info"],#sl-s4-sgf-body .besogo-comment>table.besogo-gameInfo:nth-of-type(2),#sl-s4-sgf-body .besogo-comment>textarea{display:none!important}
#sl-s4-sgf-body .besogo-comment>table.besogo-gameInfo:first-of-type{display:table!important}
#sl-s4-sgf-body .besogo-board svg *{color:initial!important}
@media (max-width:1050px){body:has(#leftpane){grid-template:"top" auto "main" auto "side" auto/1fr!important}#contentwrapper{max-width:100%!important;padding:28px 18px 56px!important}.contentsize{max-width:100%!important}#leftpane{width:auto!important;min-width:0!important;border-top:1px solid var(--border)!important;border-right:0!important}img.right{float:none!important;display:block!important;max-width:100%!important;margin:18px auto!important}}
@media (max-width:760px){body,body#browse{font-size:16px!important}#toppane{padding:8px 10px!important}#searchbox{width:132px!important}#sl-s4-sgf-modal{width:calc(100vw - 12px)!important;height:calc(100vh - 12px)!important}#sl-s4-sgf-title b{max-width:72vw!important}#sl-s4-sgf-actions{width:100%!important;justify-content:stretch!important}#sl-s4-sgf-actions button{flex:1 1 0!important;padding:0 6px!important}.contentsize>ul,.contentsize>ol{padding:18px 18px 18px 38px!important}}
`;
const html = (value) => String(value).replace(/[&"<>]/g, (char) => ({
'&': '&amp;', '"': '&quot;', '<': '&lt;', '>': '&gt;',
})[char]);
const addStyle = (text) => {
if (!text) return;
GM_addStyle(text);
};
const hrefOf = (anchor) => new URL(anchor.getAttribute('href'), location.href).href;
const isSgf = (anchor) => {
const href = anchor?.getAttribute('href');
if (!href || href === '#') return false;
try {
const url = new URL(href, location.href);
return /\.sgf$/i.test(url.pathname) || /\.sgf(?:[?#]|$)/i.test(url.href);
} catch {
return /\.sgf(?:[?#]|$)/i.test(href);
}
};
const gmText = (url) => {
const ok = (response) => (response.status >= 200 && response.status < 300)
|| (response.status === 0 && response.responseText);
return new Promise((resolve, reject) => GM_xmlhttpRequest({
method: 'GET',
url,
timeout: 20000,
headers: { Accept: 'application/x-go-sgf,text/plain,*/*' },
onload: (response) => (ok(response) ? resolve(response.responseText) : reject(new Error(`HTTP ${response.status}`))),
onerror: () => reject(new Error('Network request failed')),
ontimeout: () => reject(new Error('Network request timed out')),
}));
};
const validateSgf = (text, url) => {
const sgf = String(text || '').trim();
if (!/\(\s*;/.test(sgf.slice(0, 1200))) {
throw new Error(`Response did not look like SGF (${new URL(url, location.href).hostname || 'local file'})`);
}
return sgf;
};
const fetchSgf = async (url) => {
if (!sgfCache.has(url)) {
sgfCache.set(url, gmText(url).then((text) => validateSgf(text, url)).catch((error) => {
sgfCache.delete(url);
throw error;
}));
}
return sgfCache.get(url);
};
const sgfTitle = (anchor, url) => {
const file = new URL(url, location.href).pathname.split('/').filter(Boolean).pop() || 'diagram.sgf';
return anchor?.closest('div.diagram')?.querySelector('p')?.textContent?.trim()
|| anchor?.textContent?.trim()
|| file;
};
const pageSgfs = () => $$('a[href].sl-s4-sgf').filter((anchor) => !anchor.closest('#sl-s4-sgf-overlay') && isSgf(anchor));
const closePopup = () => {
$('#sl-s4-sgf-overlay')?.remove();
document.documentElement.classList.remove('sl-s4-popup-open');
};
const setStatus = (body, title, detail = '') => {
body.innerHTML = `<div class="sl-s4-status"><b>${html(title)}</b>${detail ? `<span>${html(detail)}</span>` : ''}</div>`;
};
const createEditor = (body, sgf) => {
if (!window.besogo?.create) throw new Error('BesoGo did not load');
body.textContent = '';
const editor = Object.assign(document.createElement('div'), {
className: 'besogo-editor',
textContent: sgf,
});
editor.tabIndex = 0;
body.append(editor);
window.besogo.create(editor, {
resize: 'fixed',
panels: ['comment', 'tree'],
tool: 'navOnly',
coord: 'western',
variants: 1,
});
requestAnimationFrame(() => {
editor.focus();
window.dispatchEvent(new Event('resize'));
});
};
const openPopup = async (anchorOrUrl) => {
closePopup();
const anchor = typeof anchorOrUrl === 'string' ? null : anchorOrUrl;
const url = typeof anchorOrUrl === 'string' ? anchorOrUrl : hrefOf(anchor);
const title = sgfTitle(anchor, url);
const links = pageSgfs();
const index = anchor ? links.indexOf(anchor) : -1;
const id = ++requestId;
document.body.insertAdjacentHTML('beforeend', `
<div id="sl-s4-sgf-overlay">
<div id="sl-s4-sgf-backdrop"></div>
<section id="sl-s4-sgf-modal" role="dialog" aria-modal="true" aria-label="SGF viewer">
<header id="sl-s4-sgf-header">
<div id="sl-s4-sgf-title"><span>SGF</span><div><b>${html(title)}</b><a href="${html(url)}" target="_blank" rel="noopener noreferrer">Open original</a></div></div>
<div id="sl-s4-sgf-actions">
<button type="button" data-action="prev"${index <= 0 ? ' disabled' : ''}>Prev</button>
<button type="button" data-action="next"${index < 0 || index >= links.length - 1 ? ' disabled' : ''}>Next</button>
<button id="sl-s4-sgf-close" type="button" data-action="close">Close</button>
</div>
</header>
<div id="sl-s4-sgf-body"></div>
</section>
</div>`);
document.documentElement.classList.add('sl-s4-popup-open');
$('#sl-s4-sgf-backdrop').addEventListener('click', closePopup);
$('#sl-s4-sgf-actions').addEventListener('click', (event) => {
const action = event.target.closest('button')?.dataset.action;
if (action === 'close') closePopup();
if (action === 'prev' || action === 'next') openPopup(links[index + (action === 'next' ? 1 : -1)]);
});
const body = $('#sl-s4-sgf-body');
setStatus(body, 'Loading SGF...', new URL(url, location.href).pathname);
try {
const sgf = await fetchSgf(url);
if (id !== requestId || !$('#sl-s4-sgf-overlay')) return;
requestAnimationFrame(() => createEditor(body, sgf));
} catch (error) {
if ($('#sl-s4-sgf-overlay')) {
setStatus(body, 'Could not load this SGF in the popup.', `${error.message}. Use "Open original" above.`);
}
console.warn('[SL Theme] SGF load failed:', error);
}
};
const markSgfLinks = (root = document) => {
const links = root.matches?.('a[href]') ? [root] : $$('a[href]', root);
links.filter(isSgf).forEach((anchor) => {
anchor.classList.add('sl-s4-sgf');
anchor.title = 'Open SGF in popup viewer';
if (anchor.closest('div.diagram') && $('img', anchor)) anchor.classList.add('sl-s4-diagram');
});
};
const normalizeChrome = () => {
$('#partner')?.remove();
const pane = $('#toppane');
if (pane) {
[...pane.childNodes].forEach((node) => {
if (node.nodeType === Node.TEXT_NODE && /^[\s|·•¦]+$/.test(node.textContent)) node.remove();
});
}
const search = $('#searchboxdiv form');
if (search && !search.dataset.slS4Text) {
search.dataset.slS4Text = '1';
$$('input[type=image]', search).forEach((input) => {
const button = Object.assign(document.createElement('button'), {
type: 'submit',
className: 'sl-s4-search',
name: input.name,
value: input.value || input.name || '1',
title: input.title,
textContent: input.name === 'pagesearch' ? 'In titles' : 'Search',
});
input.replaceWith(button);
});
}
const home = $('#toolbox3 > span');
if (home && !home.dataset.slS4Home) {
home.dataset.slS4Home = '1';
home.role = 'link';
home.tabIndex = 0;
home.title = 'Go to Sensei\'s Library home';
const goHome = (event) => {
event.preventDefault();
event.stopImmediatePropagation();
location.assign(location.protocol === 'file:' ? ($('#leftpane > a[href]')?.href || location.href) : new URL('/', location.href).href);
};
home.addEventListener('click', goHome, true);
home.addEventListener('keydown', (event) => (event.key === 'Enter' || event.key === ' ') && goHome(event), true);
}
$$('.toolmenu > span').forEach((trigger) => {
if (trigger.dataset.slS4Menu || trigger === home) return;
trigger.dataset.slS4Menu = '1';
trigger.tabIndex = 0;
trigger.role = 'button';
trigger.addEventListener('click', (event) => {
event.preventDefault();
event.stopPropagation();
const menu = trigger.closest('.toolmenu');
$$('.toolmenu.sl-s4-open').forEach((item) => item !== menu && item.classList.remove('sl-s4-open'));
menu.classList.toggle('sl-s4-open');
});
});
};
addStyle(GM_getResourceText('besogoCSS'));
addStyle(GM_getResourceText('boardCSS'));
addStyle(css);
normalizeChrome();
markSgfLinks();
document.addEventListener('click', (event) => {
const target = event.target instanceof Element ? event.target : event.target.parentElement;
const sgf = target?.closest('a[href]');
if (sgf && !sgf.closest('#sl-s4-sgf-overlay') && isSgf(sgf) && !event.metaKey && !event.ctrlKey && !event.shiftKey && !event.altKey && event.button === 0) {
event.preventDefault();
openPopup(sgf);
return;
}
if (!target?.closest('.toolmenu')) $$('.toolmenu.sl-s4-open').forEach((menu) => menu.classList.remove('sl-s4-open'));
});
document.addEventListener('keydown', (event) => {
if (event.key === 'Escape') closePopup();
});
new MutationObserver((mutations) => {
mutations.forEach(({ addedNodes }) => addedNodes.forEach((node) => {
if (node.nodeType === Node.ELEMENT_NODE) {
normalizeChrome();
markSgfLinks(node);
}
}));
}).observe(document.body, { childList: true, subtree: true });
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment