poke/html/custom-css.ejs
2025-08-19 14:17:15 +02:00

244 lines
12 KiB
Plaintext

<!DOCTYPE html>
<html class="gradient-bg" lang="en">
<head>
<meta charset="utf-8" />
<title>PokeTube | Customize</title>
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover" />
<link href="/css/yt-ukraine.svg?v=6" rel="icon">
<meta content="Poke | Customize" property="og:title">
<meta content="Theme Poke and add custom scripts — go wild!" property="twitter:description">
<meta content="https://cdn.glitch.global/d68d17bb-f2c0-4bc3-993f-50902734f652/aa70111e-5bcd-4379-8b23-332a33012b78.image.png?v=1701898829884" property="og:image" />
<meta content="summary_large_image" name="twitter:card" />
<meta content="#f97794" name="theme-color" />
<link href="/css/app-cdn.min.css" rel="stylesheet">
<link href="/css/app.main.css?v=44600" rel="stylesheet">
<link href="/css/search.main.css?v=57" rel="stylesheet">
<link href="/css/watch.main.css" rel="stylesheet">
<style>
@import url("https://p.poketube.fun/https://site-assets.fontawesome.com/releases/v6.1.1/css/all.css");
:root{
--bg:#0c0c0f; --panel:#121218; --panel-2:#0f0f15; --text:#e9ecf1; --muted:#aab2c0;
--accent:#7cc7ff; --accent-2:#f97794; --ok:#20c997; --warn:#ffb400; --err:#ef476f;
--border:#212230; --chip:#161824; --chipb:#0f111a; --ring:0 0 0 2px rgba(124,199,255,.25);
--radius:16px; --radius-lg:22px; --shadow:0 10px 30px rgba(0,0,0,.35);
--mono: ui-monospace, SFMono-Regular, Menlo, Monaco, "Ubuntu Mono", Consolas, "Liberation Mono", monospace;
--sans: Inter, system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, "Helvetica Neue", Arial, "Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol", sans-serif;
--code-size:13.5px;
}
html.gradient-bg{
min-height:100%;
background:
radial-gradient(1200px 800px at 10% 10%, rgba(249,119,148,.22), transparent 60%),
radial-gradient(1200px 800px at 90% 20%, rgba(124,199,255,.18), transparent 60%),
radial-gradient(800px 600px at 30% 80%, rgba(124,255,208,.12), transparent 60%),
linear-gradient(135deg, #0a0a0f, #0b0b12 60%, #0c0c15);
background-attachment: fixed;
}
*{box-sizing:border-box}
body{margin:0;color:var(--text);font-family:var(--sans);background:transparent;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;overflow-x:hidden}
/* -------- NAVBAR -------- */
.customize-navbar{
display:flex; align-items:center; gap:16px; padding:12px 20px;
position:sticky; top:0; z-index:2000; min-height:62px;
backdrop-filter:blur(12px) saturate(120%);
background:linear-gradient(180deg, rgba(14,14,24,.78), rgba(14,14,24,.45));
border-bottom:1px solid rgba(255,255,255,.06);
}
.customize-navbar__left{display:flex;align-items:center}
.customize-navbar__logo{width:124px;display:block}
.customize-navbar__middle{flex:1; display:flex; justify-content:center; align-items:center}
.customize-navbar__search{position:relative; width:100%; max-width:640px}
.customize-navbar__input{
width:100%; height:40px; padding:0 44px 0 16px;
border-radius:14px; border:1px solid rgba(255,255,255,.12); outline:none;
background:#151720;
color:var(--text); box-shadow:inset 0 0 0 1px rgba(255,255,255,.02);
}
.customize-navbar__input::placeholder{color:#8b93a6}
.customize-navbar__btn{
position:absolute; right:6px; top:50%;
transform:translateY(-50%);
border:0; border-radius:10px; width:36px; height:36px; cursor:pointer;
background:linear-gradient(180deg,#1b2231,#151b27); color:var(--text);
display:grid; place-items:center;
}
.customize-navbar__right{display:flex;align-items:center;gap:10px;padding-left:8px;height:40px}
.customize-navbar__icon{
display:grid; place-items:center; width:34px; height:34px; border-radius:10px;
color:#cfd6e6; border:1px solid rgba(255,255,255,.07);
background:linear-gradient(180deg,#161925,#121521);
}
/* -------- PAGE LAYOUT -------- */
.container{max-width:1200px;margin:22px auto 64px;padding:0 16px}
.hero{display:grid;grid-template-columns:1fr;gap:16px;background:var(--panel);border:1px solid var(--border);border-radius:var(--radius-lg);padding:18px;box-shadow:var(--shadow)}
.title{text-align:left;font-weight:900;font-size:clamp(22px,3vw,34px)}
.subtitle{text-align:left;color:var(--muted);margin-top:6px}
.tabs{display:flex;justify-content:center;gap:8px;padding:14px;margin-top:14px;background:var(--panel-2);border:1px solid var(--border);border-radius:var(--radius)}
.tab{padding:10px 14px;border-radius:10px;cursor:pointer;text-transform:uppercase;font-size:12px;color:var(--muted);text-decoration:none}
.tab.active{color:var(--text);background:rgba(124,199,255,.06)}
.work{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-top:14px}
@media (max-width:1000px){.work{grid-template-columns:1fr}}
.panel{background:var(--panel);border:1px solid var(--border);border-radius:var(--radius);box-shadow:var(--shadow)}
.panel .head{display:flex;align-items:center;justify-content:space-between;padding:10px 12px;border-bottom:1px solid var(--border)}
.panel .head .title{font-weight:700;font-size:14px}
.panel .head .hint{color:var(--muted);font-size:12px}
.btns{display:flex;gap:8px;flex-wrap:wrap}
.btn{border:1px solid var(--border);background:var(--chip);color:var(--text);padding:8px 10px;border-radius:10px;font-size:12px;cursor:pointer}
/* -------- Editors (no syntax highlighting) -------- */
.editor-wrap{position:relative;height:min(70vh,680px);overflow:auto;background:#0a0b12}
.ln{
position:absolute; left:0; top:0; bottom:0; width:40px;
background:#08090f; color:#6272a4; border-right:1px solid #0d0f1a;
text-align:right; padding:18px 6px; font:500 var(--code-size)/1.6 var(--mono);
}
.live-editor{
margin:0; outline:0; border:0; background:transparent;
font:500 var(--code-size)/1.6 var(--mono);
color:var(--text);
white-space:pre; tab-size:2; -moz-tab-size:2;
padding:18px 18px 18px 54px;
min-height:100%;
}
</style>
</head>
<body>
<div class="app gradient-bg">
<!-- NAVBAR -->
<nav class="customize-navbar">
<div class="customize-navbar__left">
<a href="/143"><img class="customize-navbar__logo" src="/css/logo.svg?v=5" alt="PokeTube"></a>
</div>
<div class="customize-navbar__middle">
<form class="customize-navbar__search" action="/search">
<input id="fname" name="query" class="customize-navbar__input" type="search" placeholder="Search videos, channels…" />
<button class="customize-navbar__btn" type="submit"><i class="fa-light fa-search"></i></button>
</form>
</div>
<div class="customize-navbar__right">
<a class="customize-navbar__icon" href="/domains"><i class="fa-light fa-server"></i></a>
<a class="customize-navbar__icon" href="/privacy"><i class="fa-light fa-shield"></i></a>
<a class="customize-navbar__icon" href="/video/upload?from="><i class="fa-light fa-video"></i></a>
<a class="customize-navbar__icon" href="https://codeberg.org/Ashley/poketube/issues"><i class="fa-light fa-bug"></i></a>
</div>
</nav>
<div class="container">
<section class="hero">
<div>
<div class="title">Customize Poke</div>
<div class="subtitle">Edits are stored in the browser. Export a backup before clearing site data.</div>
</div>
</section>
<div class="tabs">
<% if (!tab) { %>
<a class="tab active" href="/customize">Custom CSS</a>
<a class="tab" href="/customize?tab=js">Custom JS</a>
<% } else { %>
<a class="tab" href="/customize">Custom CSS</a>
<a class="tab active" href="/customize?tab=js">Custom JS</a>
<% } %>
</div>
<% if (!tab) { %>
<section class="work">
<div class="panel" style=" min-width: 73em;">
<div class="head">
<div><div class="title">Custom CSS Editor</div><div class="hint">Saved to <code>localStorage['poke-custom-css']</code></div></div>
<div class="btns">
<button class="btn ok" id="saveCssBtn">Save</button>
<button class="btn" id="formatCssBtn" style="display:none" >Format</button>
<button class="btn" id="wrapCssBtn" style="display:none" data-wrap="1">Wrap: On</button>
<button class="btn" id="copyCssBtn">Copy</button>
<button class="btn warn" id="exportCssBtn">Export</button>
<button class="btn err" id="resetCssBtn">Reset</button>
</div>
</div>
<div class="editor-wrap"><pre class="ln" id="cssLines">1</pre><pre id="cssEd" class="live-editor" contenteditable></pre></div>
</div>
</section>
<% } %>
<% if (tab) { %>
<section class="work">
<div class="panel" style=" min-width: 73em;">
<div class="head"><div class="title">Custom JS Editor</div><div class="hint">Saved to <code>localStorage['poke-custom-script']</code></div></div>
<div class="btns">
<button class="btn ok" id="saveJsBtn">Save</button>
<button class="btn" style="display:none" id="formatJsBtn">Format</button>
<button class="btn" id="wrapJsBtn" style="display:none" data-wrap="1">Wrap: On</button>
<button class="btn" id="copyJsBtn">Copy</button>
<button class="btn warn" id="exportJsBtn">Export</button>
<button class="btn err" id="resetJsBtn">Reset</button>
</div>
<div class="editor-wrap"><pre class="ln" id="jsLines">1</pre><pre id="jsEd" class="live-editor" contenteditable></pre></div>
</div>
</section>
<% } %>
</div>
</div>
<!-- ================= SCRIPTS ================ -->
<script>
const $ = s => document.querySelector(s);
const on = (el, ev, fn) => el && el.addEventListener(ev, fn, { passive:true });
const saveFile = (name, content, type='text/plain') => {
const blob = new Blob([content], { type });
const url = URL.createObjectURL(blob);
const a = document.createElement('a'); a.href = url; a.download = name;
document.body.appendChild(a); a.click(); URL.revokeObjectURL(url); a.remove();
};
const copyText = async (txt) => { try { await navigator.clipboard.writeText(txt); } catch {} };
const debounce = (fn,ms=120)=>{ let t; return (...a)=>{ clearTimeout(t); t=setTimeout(()=>fn(...a),ms); }; };
function setLines(preEl, lnEl){
const lines = preEl.textContent.split('\n').length || 1;
let out=''; for (let i=1;i<=lines;i++) out += i + (i<lines?'\n':'');
lnEl.textContent = out;
}
function setWrap(preEl, onwrap){
preEl.style.whiteSpace = onwrap ? 'pre-wrap' : 'pre';
preEl.style.overflowWrap = onwrap ? 'anywhere' : 'normal';
}
function loadEditor(preEl, storageKey, fallback){
const saved = localStorage.getItem(storageKey);
preEl.textContent = saved ?? fallback;
setLines(preEl, preEl.previousElementSibling);
}
function saveEditor(preEl, storageKey){
localStorage.setItem(storageKey, preEl.textContent);
}
<% if (!tab) { %>
const cssEd = $('#cssEd'), cssLines = $('#cssLines');
loadEditor(cssEd, 'poke-custom-css', "/* Custom CSS */");
const cssPreviewStyle = document.createElement('style'); document.body.appendChild(cssPreviewStyle);
const applyCssPreview = ()=>{ cssPreviewStyle.textContent = cssEd.textContent || ''; };
on(cssEd,'input',debounce(()=>{setLines(cssEd,cssLines);applyCssPreview()},50));
on($('#saveCssBtn'),'click',()=> saveEditor(cssEd,'poke-custom-css'));
on($('#exportCssBtn'),'click',()=> saveFile('poke-custom-css.css', cssEd.textContent,'text/css'));
<% } %>
<% if (tab) { %>
const jsEd = $('#jsEd'), jsLines = $('#jsLines');
loadEditor(jsEd, 'poke-custom-script', "// Custom JS");
on(jsEd,'input',debounce(()=> setLines(jsEd,jsLines),50));
on($('#saveJsBtn'),'click',()=> saveEditor(jsEd,'poke-custom-script'));
on($('#exportJsBtn'),'click',()=> saveFile('poke-custom-script.js', jsEd.textContent,'application/javascript'));
<% } %>
</script>
<script src="/css/custom-css.js"></script>
</body>
</html>