From 912deeb28e1ea20ee08ec06bb255918e89e24753 Mon Sep 17 00:00:00 2001 From: Kibi Kelburton Date: Mon, 14 Sep 2026 22:30:09 +0200 Subject: [PATCH] fdsfsda --- public/s/css/f0ckm.css | 272 +++++- public/s/css/upload.css | 27 + public/s/css/v0ck.css | 160 +++- public/s/js/admin.js | 92 +- public/s/js/danmaku.js | 434 ++++++++-- public/s/js/f0ckm.js | 1380 +++++++++++++++++++++++++----- public/s/js/scroller.js | 15 +- public/s/js/settings.js | 37 + public/s/js/upload.js | 83 +- public/s/js/user.js | 92 +- public/s/js/v0ck.js | 6 +- src/inc/lib.mjs | 34 +- src/inc/locales/de.json | 4 +- src/inc/locales/en.json | 4 +- src/inc/locales/nl.json | 4 +- src/inc/locales/zange.json | 4 +- src/inc/routeinc/f0cklib.mjs | 478 ++++++++--- src/inc/routes/ajax.mjs | 29 +- src/inc/routes/apiv2/index.mjs | 119 ++- src/inc/routes/comments.mjs | 26 +- src/inc/routes/index.mjs | 29 +- src/inc/routes/mod.mjs | 5 + src/inc/routes/notifications.mjs | 43 +- src/inc/routes/reports.mjs | 7 + src/inc/routes/scroller.mjs | 8 +- src/inc/routes/toptags.mjs | 31 +- src/inc/routes/user_halls.mjs | 20 +- src/inc/routes/user_tags.mjs | 32 +- src/index.mjs | 3 + src/upload_handler.mjs | 2 +- views/admin/nsfp.html | 13 +- views/error-partial.html | 11 +- views/error.html | 11 +- views/index-partial.html | 2 +- views/item-partial-legacy.html | 2 +- views/item-partial-modern.html | 27 +- views/mod_reports.html | 3 + views/settings.html | 54 ++ views/snippets/footer.html | 2 +- views/snippets/item-media.html | 2 +- views/snippets/items-grid.html | 2 +- views/snippets/upload-form.html | 7 + 42 files changed, 2998 insertions(+), 618 deletions(-) diff --git a/public/s/css/f0ckm.css b/public/s/css/f0ckm.css index 3910eb1..d4017bd 100644 --- a/public/s/css/f0ckm.css +++ b/public/s/css/f0ckm.css @@ -40,6 +40,11 @@ --badge-tag: #090909; --posts-meta-bg: #000000b8; --x2154: #215483; + --bg-canvas-opacity: 1.0; + --bg-canvas-blur: 300px; + --onara-bg-opacity: 0.84; + --onara-backdrop-blur: 5px; + --onara-grid-dim-opacity: 0.15; } html:not([theme]), html[theme=""] { @@ -3716,6 +3721,144 @@ body.sidebar-right-hidden #sidebar-drag-zone { color: #fff; } +/* ── Tuner Sub-Tabs Navigation ────────────────────────────── */ +.f0ck-tuner-subtabs-nav { + display: flex; + gap: 4px; + background: rgba(0, 0, 0, 0.45); + padding: 3px; + border-radius: 8px; + border: 1px solid rgba(255, 255, 255, 0.08); + flex: 1; + margin-right: 8px; +} + +.f0ck-tuner-subtab-btn { + flex: 1; + display: inline-flex; + align-items: center; + justify-content: center; + gap: 5px; + background: transparent; + border: none; + color: #8e8e93; + font-size: 11px; + font-weight: 600; + padding: 5px 8px; + border-radius: 6px; + cursor: pointer; + transition: all 0.15s ease; + white-space: nowrap; +} + +.f0ck-tuner-subtab-btn:hover { + color: #fff; + background: rgba(255, 255, 255, 0.06); +} + +.f0ck-tuner-subtab-btn.active { + color: #000; + background: var(--accent, #99ff00); + font-weight: 700; + box-shadow: 0 2px 8px color-mix(in srgb, var(--accent, #99ff00) 40%, transparent); +} + +.f0ck-tuner-subtab-pane { + display: none; +} + +.f0ck-tuner-subtab-pane.active { + display: block; +} + +/* ── Danmaku Debug Tools & Buttons Grid ───────────────────── */ +.f0ck-danmaku-debug-card { + background: rgba(0, 0, 0, 0.25); + border: 1px solid rgba(255, 255, 255, 0.08); + border-radius: 8px; + padding: 10px; + margin-bottom: 12px; +} + +.f0ck-danmaku-spawner-row { + display: flex; + gap: 6px; + margin-bottom: 8px; +} + +.f0ck-danmaku-spawner-meta { + display: flex; + gap: 6px; + margin-bottom: 8px; + align-items: center; +} + +.f0ck-danmaku-debug-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 6px; + margin-bottom: 8px; +} + +.f0ck-danmaku-debug-btn { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 5px; + background: rgba(255, 255, 255, 0.06); + border: 1px solid rgba(255, 255, 255, 0.12); + color: #ddd; + padding: 6px 8px; + border-radius: 6px; + font-size: 11px; + font-weight: 600; + cursor: pointer; + transition: all 0.15s ease; + white-space: nowrap; +} + +.f0ck-danmaku-debug-btn:hover { + background: rgba(var(--accent-rgb, 153, 255, 0), 0.15); + border-color: var(--accent, #99ff00); + color: var(--accent, #99ff00); +} + +.f0ck-danmaku-debug-btn.btn-primary { + background: var(--accent, #99ff00); + color: #000; + border-color: var(--accent, #99ff00); + font-weight: 700; +} + +.f0ck-danmaku-debug-btn.btn-primary:hover { + filter: brightness(1.15); + color: #000; +} + +.f0ck-danmaku-debug-btn.btn-danger { + background: rgba(255, 68, 68, 0.12); + border-color: rgba(255, 68, 68, 0.35); + color: #ff6b6b; +} + +.f0ck-danmaku-debug-btn.btn-danger:hover { + background: rgba(255, 68, 68, 0.28); + border-color: #ff4444; + color: #fff; +} + +.f0ck-danmaku-debug-btn.btn-warning { + background: rgba(255, 180, 0, 0.12); + border-color: rgba(255, 180, 0, 0.35); + color: #ffb833; +} + +.f0ck-danmaku-debug-btn.btn-warning:hover { + background: rgba(255, 180, 0, 0.28); + border-color: #ffaa00; + color: #fff; +} + .sidebar-video-details { display: flex; margin-top: 8px; @@ -9846,6 +9989,43 @@ span#favs[hidden] { opacity: 0.8; } +._error_filter_actions { + margin-top: 10px; + display: flex; + justify-content: center; +} + +._error_see_anyways_btn { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 6px 14px; + background: var(--accent, #f2ef0b); + color: #111; + font-weight: 700; + font-size: 0.95em; + border-radius: 4px; + border: none; + cursor: pointer; + text-decoration: none; + transition: opacity 0.15s ease, transform 0.1s ease; +} + +._error_see_anyways_btn i { + color: #111; + opacity: 0.9; + margin-right: 2px; +} + +._error_see_anyways_btn:hover { + opacity: 0.9; + transform: translateY(-1px); +} + +._error_see_anyways_btn:active { + transform: translateY(0); +} + /* Admin search css early test */ .admin-search { margin-top: 15px; @@ -10139,10 +10319,9 @@ div.favs div.posts { a GPU blur pass on every compositor frame (incl. during the fade transition), which is extremely expensive. Firefox uses a cheaper path for this. */ transform: translate3d(0, 0, 0); - z-index: 0; - transition: opacity 1.5s cubic-bezier(0.4, 0, 0.2, 1); - opacity: 0.2; z-index: -2; + transition: opacity 1.5s cubic-bezier(0.4, 0, 0.2, 1); + opacity: var(--bg-canvas-opacity, 1.0); } button#togglebg { @@ -10179,13 +10358,13 @@ button#togglebg { } 100% { - opacity: 0.2; + opacity: var(--bg-canvas-opacity, 1.0); } } @keyframes fadeOut { 0% { - opacity: 0.2; + opacity: var(--bg-canvas-opacity, 1.0); } 100% { @@ -10193,13 +10372,12 @@ button#togglebg { } } +#bg.fader-in, .fader-in { - /*opacity: 0.4 !important;*/ - /* default */ - opacity: 0.08 !important; - /* darker */ + opacity: var(--bg-canvas-opacity, 1.0) !important; } +#bg.fader-out, .fader-out { opacity: 0 !important; } @@ -12503,6 +12681,45 @@ html[theme='paper'] .steuerung.steuerung-icon button:hover, width: 100%; } +body.layout-modern .blahlol { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 8px; + width: 100%; +} + +body.layout-modern .blahlol .blahlol-meta { + display: inline-flex; + align-items: center; + justify-content: center; + flex-wrap: wrap; + gap: 6px; + width: 100%; +} + +body.layout-modern .blahlol .gapRight { + display: inline-flex; + align-items: center; + justify-content: center; + flex-wrap: wrap; + gap: 6px; +} + +body.layout-modern .blahlol:not(:has(.blahlol-meta)) { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: center; + gap: 8px; +} + +body.layout-modern .blahlol:not(:has(.blahlol-meta)) .gapRight { + flex-basis: 100%; + width: 100%; +} + span#favs { background: transparent !important; } @@ -20500,31 +20717,35 @@ html:has(body.onara-modal-open) { scrollbar-gutter: auto !important; } -/* Background main page elements (outside posts) are dimmed and softly blurred */ +/* Background main page elements and containers are transparent so #bg canvas and grid shine through */ body.onara-modal-open .pagewrapper { pointer-events: none !important; user-select: none !important; margin-top: var(--navbar-h, 50px) !important; + background: transparent !important; +} + +body.onara-modal-open .index-container, +body.onara-modal-open div.posts { + background: transparent !important; } body.onara-modal-open .pagewrapper > *:not(.index-layout-wrapper):not(#main), body.onara-modal-open .index-container > *:not(.posts), body.onara-modal-open #footbar, body.onara-modal-open .pagination-container-fluid { - opacity: 0.25 !important; - filter: blur(1.5px) !important; - transition: opacity 0.25s cubic-bezier(0.16, 1, 0.3, 1), - filter 0.25s cubic-bezier(0.16, 1, 0.3, 1) !important; + opacity: var(--onara-grid-dim-opacity, 1) !important; + filter: none !important; + transition: opacity 0.25s cubic-bezier(0.16, 1, 0.3, 1) !important; } -/* All non-active background thumbnails are strictly dimmed, blurred, with no outlines or badges */ +/* All non-active background thumbnails shine through with the configured grid opacity */ body.onara-modal-open .posts > a.thumb:not(.onara-active) { - opacity: 0.25 !important; - filter: blur(1.5px) !important; + opacity: var(--onara-grid-dim-opacity, 1) !important; + filter: none !important; box-shadow: none !important; outline: none !important; - transition: opacity 0.25s cubic-bezier(0.16, 1, 0.3, 1), - filter 0.25s cubic-bezier(0.16, 1, 0.3, 1) !important; + transition: opacity 0.25s cubic-bezier(0.16, 1, 0.3, 1) !important; } body.onara-modal-open .posts > a.thumb:not(.onara-active)::after { @@ -20532,12 +20753,13 @@ body.onara-modal-open .posts > a.thumb:not(.onara-active)::after { opacity: 0 !important; } -/* ONLY the single currently active thumbnail in the background shines through with TAB highlight */ +/* ONLY the single currently active thumbnail in the background shines through with prominent glow */ body.onara-modal-open .posts > a.thumb.onara-active { opacity: 1 !important; filter: none !important; - box-shadow: 0 0 0 2px var(--white, #fff), 0 0 16px rgba(255, 255, 255, 0.6) !important; - z-index: 25 !important; + box-shadow: 0 0 0 3px var(--accent, #9f0), 0 0 20px var(--accent, #9f0), 0 0 35px rgba(255, 255, 255, 0.9) !important; + z-index: 50 !important; + position: relative !important; } body.onara-modal-open .posts > a.thumb.onara-active::after { @@ -20599,9 +20821,9 @@ body.onara-modal-open .admin-bar.open { z-index: 10050 !important; display: none; flex-direction: column !important; - background: rgba(0, 0, 0, 0.35) !important; - backdrop-filter: blur(5px) saturate(130%) !important; - -webkit-backdrop-filter: blur(5px) saturate(130%) !important; + background: rgba(0, 0, 0, var(--onara-bg-opacity, 0.84)) !important; + backdrop-filter: blur(var(--onara-backdrop-blur, 5px)) saturate(130%) !important; + -webkit-backdrop-filter: blur(var(--onara-backdrop-blur, 5px)) saturate(130%) !important; overflow-y: auto !important; overflow-x: hidden !important; -webkit-overflow-scrolling: touch; diff --git a/public/s/css/upload.css b/public/s/css/upload.css index f981924..02524d2 100644 --- a/public/s/css/upload.css +++ b/public/s/css/upload.css @@ -978,6 +978,33 @@ font-family: monospace; } +.global-redirect-section { + margin: 4px 0 2px 0; +} + +.global-redirect-section .upload-checkbox-label { + display: inline-flex; + align-items: center; + gap: 8px; + cursor: pointer; + font-size: 0.88rem; + color: rgba(255, 255, 255, 0.85); + user-select: none; + transition: color 0.15s ease; +} + +.global-redirect-section .upload-checkbox-label:hover { + color: #fff; +} + +.global-redirect-section input[type="checkbox"] { + accent-color: var(--accent); + cursor: pointer; + width: 15px; + height: 15px; + margin: 0; +} + .upload-status { text-align: center; padding: 1rem; diff --git a/public/s/css/v0ck.css b/public/s/css/v0ck.css index 8153898..d8a0c10 100644 --- a/public/s/css/v0ck.css +++ b/public/s/css/v0ck.css @@ -393,13 +393,14 @@ display: inline-flex; flex-direction: column; /* stack lines vertically */ align-items: flex-start; - padding: 2px 0; + padding: 2px 4px; font-family: var(--font, inherit); - font-size: 35px; - font-weight: 700; + font-size: var(--danmaku-font-size, 35px); + font-weight: var(--danmaku-font-weight, 700); line-height: 1.1; gap: 0; - color: #fff; + color: var(--danmaku-color, #fff); + opacity: var(--danmaku-opacity, 1); /* Multi-layer outline shadow for readability over any background */ text-shadow: -1px -1px 0 #000, @@ -412,12 +413,48 @@ background: none; border: none; text-align: left; + transition: opacity 0.15s ease; +} + +/* Pill Background Variations */ +.danmaku-overlay.danmaku-bg-glass .danmaku-pill { + background: rgba(10, 10, 14, 0.65); + backdrop-filter: blur(6px); + -webkit-backdrop-filter: blur(6px); + padding: 4px 12px; + border-radius: 8px; + border: 1px solid rgba(255, 255, 255, 0.12); +} + +.danmaku-overlay.danmaku-bg-capsule .danmaku-pill { + background: rgba(18, 18, 24, 0.85); + padding: 4px 14px; + border-radius: 999px; + border: 1px solid rgba(var(--accent-rgb, 153, 255, 0), 0.35); + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.6); +} + +/* Shadow / Glow Variations */ +.danmaku-overlay.danmaku-glow-neon .danmaku-pill { + text-shadow: + 0 0 8px var(--danmaku-color, #99ff00), + 0 0 18px var(--danmaku-color, #99ff00), + -1px -1px 0 #000, + 1px 1px 0 #000; +} + +.danmaku-overlay.danmaku-glow-none .danmaku-pill { + text-shadow: none; +} + +.danmaku-overlay.danmaku-glow-subtle .danmaku-pill { + text-shadow: 0 2px 4px rgba(0, 0, 0, 0.8); } .danmaku-pill .dpill-text { font-family: var(--font, inherit); - font-size: 35px; - font-weight: 700; + font-size: var(--danmaku-font-size, 35px); + font-weight: var(--danmaku-font-weight, 700); white-space: nowrap; } @@ -434,6 +471,10 @@ white-space: nowrap; } +.danmaku-overlay.danmaku-no-greentext .danmaku-pill .dpill-greentext { + color: inherit; +} + /* Spoiler inside a flying pill — black-on-black, click to reveal */ .danmaku-pill .dpill-spoiler { background: #000; @@ -492,8 +533,8 @@ /* Inline image URL embedded in pill */ .danmaku-pill .dpill-img { - max-height: 80px; - max-width: 120px; + max-height: var(--danmaku-media-max-h, 80px); + max-width: 140px; width: auto; height: auto; vertical-align: middle; @@ -505,8 +546,8 @@ /* Inline video (converted GIF) in pill */ .danmaku-pill .dpill-video { - max-height: 80px; - max-width: 120px; + max-height: var(--danmaku-media-max-h, 80px); + max-width: 140px; width: auto; height: auto; vertical-align: middle; @@ -522,6 +563,105 @@ font-size: 0.9em; } +/* ── Danmaku Debug Overlays ────────────────────────────────── */ +.danmaku-lane-guides-container { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + pointer-events: none; + z-index: 2; + box-sizing: border-box; +} + +.danmaku-lane-guide { + position: absolute; + left: 0; + width: 100%; + border-top: 1px dashed rgba(var(--accent-rgb, 153, 255, 0), 0.35); + box-sizing: border-box; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 8px; + font-family: monospace; + font-size: 10px; + color: rgba(255, 255, 255, 0.6); + background: rgba(0, 0, 0, 0.04); + transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease; +} + +.danmaku-lane-guide.occupied { + background: rgba(255, 60, 60, 0.12); + border-top: 1px dashed rgba(255, 60, 60, 0.65); + color: #ff7070; +} + +.danmaku-lane-badge { + background: rgba(0, 0, 0, 0.65); + border: 1px solid rgba(255, 255, 255, 0.15); + padding: 1px 5px; + border-radius: 4px; + font-weight: 700; +} + +.danmaku-lane-status { + font-size: 9px; + opacity: 0.9; +} + +/* Diagnostic Live HUD */ +.danmaku-debug-hud { + position: absolute; + top: 14px; + left: 14px; + z-index: 10; + background: rgba(12, 12, 16, 0.88); + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); + border: 1px solid rgba(var(--accent-rgb, 153, 255, 0), 0.45); + border-radius: 8px; + padding: 8px 12px; + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; + font-size: 11px; + color: #e0e0e0; + pointer-events: none; + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.7); + display: flex; + flex-direction: column; + gap: 4px; + line-height: 1.3; + min-width: 170px; +} + +.danmaku-debug-hud .hud-title { + color: var(--accent, #99ff00); + font-weight: 700; + font-size: 11px; + border-bottom: 1px solid rgba(255, 255, 255, 0.12); + padding-bottom: 3px; + margin-bottom: 2px; + display: flex; + align-items: center; + gap: 6px; +} + +.danmaku-debug-hud .hud-row { + display: flex; + justify-content: space-between; + gap: 12px; +} + +.danmaku-debug-hud .hud-val { + color: #fff; + font-weight: 700; +} + +.danmaku-debug-hud .hud-val.accent { + color: var(--accent, #99ff00); +} + @keyframes danmaku-fly { from { transform: translateX(calc(100vw + 100%)); } to { transform: translateX(calc(-100% - 200px)); } diff --git a/public/s/js/admin.js b/public/s/js/admin.js index ddfd6f8..05d8d30 100644 --- a/public/s/js/admin.js +++ b/public/s/js/admin.js @@ -288,9 +288,9 @@ if (!ctx) return; const { postid } = ctx; - // Read state BEFORE the API call so we know which direction to toggle - const favoBtn = document.querySelector("#a_favo"); - const wasAlreadyFav = favoBtn && favoBtn.classList.contains('fa-solid'); + // Read state BEFORE the API call so we have a fallback + const favoBtns = document.querySelectorAll("#a_favo"); + const wasAlreadyFav = favoBtns.length > 0 && favoBtns[0].classList.contains('fa-solid'); try { const res = await post('/api/v2/togglefav', { @@ -300,35 +300,77 @@ if (window.invalidateItemCache) { window.invalidateItemCache(postid); } - // New state is the logical opposite of what it was before the API call - const isNowFav = !wasAlreadyFav; + // Authoritative server state takes priority; fallback to inverting previous UI state + const isNowFav = (res.favorited !== undefined) ? !!res.favorited : !wasAlreadyFav; - if (favoBtn) { + favoBtns.forEach(favoBtn => { favoBtn.classList.toggle('fa-solid', isNowFav); favoBtn.classList.toggle('fa-regular', !isNowFav); - } + }); const favcontainer = document.querySelector('#favs'); - favcontainer.innerHTML = ""; - if (res.favs && res.favs.length > 0) { - res.favs.forEach(f => { - const a = document.createElement('a'); - a.href = `/user/${f.user}`; - a.setAttribute('tooltip', f.display_name || f.user); - a.setAttribute('flow', 'up'); + if (favcontainer) { + favcontainer.innerHTML = ""; + if (res.favs && res.favs.length > 0) { + const isAnonymized = !!(window.f0ckSession?.is_anonymized ?? (window.f0ckSession?.guest_anonymize && !window.f0ckSession?.logged_in) ?? (window.f0ckSession?.is_anon && window.f0ckSession?.anon_anonymize)); + const fragment = document.createDocumentFragment(); + res.favs.forEach(f => { + const isSelf = window.f0ckSession && ( + (window.f0ckSession.id && f.user_id && Number(window.f0ckSession.id) === Number(f.user_id)) || + (window.f0ckSession.user && f.user && window.f0ckSession.user.toLowerCase() === f.user.toLowerCase()) || + (window.f0ckSession.login && f.login && window.f0ckSession.login.toLowerCase() === f.login.toLowerCase()) + ); + const isFavAnon = f.is_anon || f.user === 'anonymous' || (typeof f.user === 'string' && f.user.startsWith('anon_')); - const img = document.createElement('img'); - img.src = f.avatar_file ? `/a/${f.avatar_file}` : (f.avatar ? `/t/${f.avatar}.webp` : '/a/default.png'); - img.style.height = "32px"; - img.style.width = "32px"; - if (f.username_color) img.style.borderColor = f.username_color; + if (f.hide_fav_badge && !isSelf) { + const a = document.createElement('a'); + a.className = 'ghost-fav'; + a.setAttribute('tooltip', '?'); + a.setAttribute('flow', 'up'); + a.style.cursor = 'default'; - a.appendChild(img); - favcontainer.appendChild(a); - }); - favcontainer.hidden = false; - } else { - favcontainer.hidden = true; + const img = document.createElement('img'); + img.src = '/s/img/ghost_fav.svg'; + img.style.height = "32px"; + img.style.width = "32px"; + + a.appendChild(img); + fragment.appendChild(a); + } else if (isAnonymized || isFavAnon) { + const a = document.createElement('a'); + a.className = 'ghost-fav'; + a.setAttribute('tooltip', 'anonymous'); + a.setAttribute('flow', 'up'); + a.style.cursor = 'default'; + + const img = document.createElement('img'); + img.src = '/a/default.png'; + img.style.height = "32px"; + img.style.width = "32px"; + + a.appendChild(img); + fragment.appendChild(a); + } else { + const a = document.createElement('a'); + a.href = `/user/${(f.user || '').toLowerCase()}`; + a.setAttribute('tooltip', f.display_name || f.user || 'anonymous'); + a.setAttribute('flow', 'up'); + + const img = document.createElement('img'); + img.src = f.avatar_file ? `/a/${f.avatar_file}` : (f.avatar ? `/t/${f.avatar}.webp` : '/a/default.png'); + img.style.height = "32px"; + img.style.width = "32px"; + if (f.username_color) img.style.borderColor = f.username_color; + + a.appendChild(img); + fragment.appendChild(a); + } + }); + favcontainer.appendChild(fragment); + favcontainer.hidden = false; + } else { + favcontainer.hidden = true; + } } window.flashMessage((window.f0ckI18n && (isNowFav ? window.f0ckI18n.fav_added : window.f0ckI18n.fav_removed)) || (isNowFav ? 'ADDED TO FAVORITES' : 'REMOVED FROM FAVORITES')); diff --git a/public/s/js/danmaku.js b/public/s/js/danmaku.js index 10078d7..45bae55 100644 --- a/public/s/js/danmaku.js +++ b/public/s/js/danmaku.js @@ -12,11 +12,42 @@ const PILL_MIN_MS = 6000; // Fastest a pill can cross the screen const PILL_MAX_MS = 12000; // Slowest a pill can cross the screen -const LANE_COUNT = 10; // Vertical lane slots const LOOKAHEAD_SEC = 0.25; // How far ahead of currentTime we look when scanning const MIN_RANDOM_SECS = 2; // Random timecode lower bound (avoid very start) const RANDOM_SPREAD = 0.85; // Use 85% of duration for random spread +const DEFAULT_DANMAKU_TUNING = { + fontSize: 35, + opacity: 1.0, + speedMultiplier: 1.0, + laneCount: 10, + laneCoverage: 100, + fontWeight: 700, + outlineStyle: 2, // 0=None, 1=Subtle, 2=Default Outline, 3=Neon Glow + useCustomColor: 0, + customColor: '#ffffff', + pillBackground: 0, // 0=None, 1=Glass Card, 2=Solid Capsule + allowMediaEmbeds: 1, + mediaMaxHeight: 80, + showGreentext: 1, + densityLimit: 35, + flashInterval: 2.5, + showLaneGuides: 0, + showDebugHUD: 0 +}; + +const loadDanmakuTuning = () => { + try { + const raw = localStorage.getItem('f0ck_danmaku_tuning'); + return raw ? Object.assign({}, DEFAULT_DANMAKU_TUNING, JSON.parse(raw)) : Object.assign({}, DEFAULT_DANMAKU_TUNING); + } catch (e) { + return Object.assign({}, DEFAULT_DANMAKU_TUNING); + } +}; + +window.DEFAULT_DANMAKU_TUNING = DEFAULT_DANMAKU_TUNING; +window.danmakuTuning = window.danmakuTuning || loadDanmakuTuning(); + /** * SyntheticClock — emulates a