// ==UserScript== // @name 加强糯米! // @namespace https://www.nuomill.com // @version 1.6 // @description 糯米洛洛网站增强 // @author Sunse666 // @match https://www.nuomill.com/* // @grant none // @run-at document-start // ==/UserScript== (function () { 'use strict'; (function () { var match = location.pathname.match(/^\/user\/(\d+)/); if (match) { try { sessionStorage.setItem('nml_user_redirect', match[1]); } catch (e) {} location.replace('/'); return; } if (location.pathname === '/' || location.pathname === '') { var userId; try { userId = sessionStorage.getItem('nml_user_redirect'); } catch (e) {} if (userId) { try { sessionStorage.removeItem('nml_user_redirect'); } catch (e) {} var tries = 0; (function tryNav() { var router; try { var app = document.getElementById('__nuxt').__vue_app__; router = app.config.globalProperties.$router; } catch (e) {} if (router) { router.push('/user/' + userId); } else if (tries++ < 50) { setTimeout(tryNav, 200); } })(); } } })(); const SPEEDS = [0.25, 0.5, 1.0, 1.25, 1.5, 2.0]; const DBLCLICK_THRESHOLD = 320; const VOLUME_KEY = 'nml_saved_volume'; const SPEED_KEY = 'nml_saved_speed'; let savedSpeed = parseFloat(localStorage.getItem(SPEED_KEY)); if (isNaN(savedSpeed) || !SPEEDS.includes(savedSpeed)) savedSpeed = 1.0; let currentSpeed = savedSpeed; let savedVolume = parseFloat(localStorage.getItem(VOLUME_KEY)); if (isNaN(savedVolume) || savedVolume < 0 || savedVolume > 1) savedVolume = null; let videoEl = null; let speedBtn = null; let speedMenu = null; let toastEl = null; let mounted = false; let clickTimer = null; let lastClickTime = 0; function setupDoubleClick(video) { if (!video || video.dataset.nmlDblclick) return; video.dataset.nmlDblclick = '1'; video.addEventListener('click', e => { const now = Date.now(); const elapsed = now - lastClickTime; lastClickTime = now; e.stopPropagation(); e.stopImmediatePropagation(); if (elapsed < DBLCLICK_THRESHOLD && elapsed > 0) { clearTimeout(clickTimer); lastClickTime = 0; toggleFullscreen(); return; } clearTimeout(clickTimer); clickTimer = setTimeout(() => { lastClickTime = 0; const v = findVideo(); if (v) { v.paused ? v.play() : v.pause(); } }, DBLCLICK_THRESHOLD); }, true); } function toggleFullscreen() { const wrapper = document.querySelector('.player-wrapper'); if (!wrapper) return; if (document.fullscreenElement || document.webkitFullscreenElement) { (document.exitFullscreen || document.webkitExitFullscreen).call(document); } else { (wrapper.requestFullscreen || wrapper.webkitRequestFullscreen).call(wrapper); } showToast(currentSpeed); } // Volume memory function saveVolume(vol) { savedVolume = vol; try { localStorage.setItem(VOLUME_KEY, String(vol)); } catch (e) {} } function applySavedVolume(video) { if (savedVolume == null) return; video.volume = savedVolume; video.muted = savedVolume === 0; // Also sync the slider const slider = document.querySelector('.custom-video-player .volume-slider'); if (slider) { slider.value = savedVolume; slider.dispatchEvent(new Event('input', { bubbles: true })); } } // Video tracking function findVideo() { const player = document.querySelector('.custom-video-player'); if (!player) return null; const v = player.querySelector('video'); if (v && v !== videoEl) { videoEl = v; v.playbackRate = currentSpeed; applySavedVolume(v); setupDoubleClick(v); watchVideo(v); } return v; } function watchVideo(video) { // Apply saved volume at the earliest opportunity (loadstart fires before loadedmetadata) video.addEventListener('loadstart', () => { applySavedVolume(video); setTimeout(() => { applySavedVolume(video); if (video.playbackRate !== currentSpeed) { video.playbackRate = currentSpeed; } }, 60); }); video.addEventListener('loadedmetadata', () => { applySavedVolume(video); if (video.playbackRate !== currentSpeed) { video.playbackRate = currentSpeed; } }); // Save volume whenever user changes it via the player UI video.addEventListener('volumechange', () => { if (!video.muted && video.volume > 0) { saveVolume(video.volume); } }); } // Observe DOM for video element const videoObserver = new MutationObserver(() => { findVideo(); }); function startVideoObserver() { const player = document.querySelector('.custom-video-player'); if (player) { videoObserver.observe(player, { childList: true, subtree: true }); } findVideo(); } // Speed control function applySpeed(speed) { currentSpeed = speed; const v = findVideo() || document.querySelector('video'); if (v) { v.playbackRate = speed; requestAnimationFrame(() => { if (v.playbackRate !== speed) v.playbackRate = speed; }); } syncDanmaku(speed); updateUI(); showToast(speed); savedSpeed = speed; try { localStorage.setItem(SPEED_KEY, String(speed)); } catch (e) {} } function syncDanmaku(speed) { try { const app = document.getElementById('__nuxt'); if (!app?.__vue_app__) return; walkVue(app.__vue_app__._instance?.vnode, comp => { if (comp.props && 'speed' in comp.props) { comp.props.speed = 100 * speed; } }); } catch (e) {} } function walkVue(vnode, fn) { if (!vnode) return; if (vnode.component) { fn(vnode.component); if (vnode.component.subTree) walkVue(vnode.component.subTree, fn); } if (Array.isArray(vnode.children)) vnode.children.forEach(c => walkVue(c, fn)); if (vnode.dynamicChildren) vnode.dynamicChildren.forEach(c => walkVue(c, fn)); } // Toast let toastTimer = null; function showToast(speed) { if (!toastEl) { toastEl = document.createElement('div'); toastEl.style.cssText = 'position:absolute;top:12px;left:50%;transform:translateX(-50%);' + 'z-index:10000;background:rgba(0,0,0,0.85);color:#fff;' + 'font-size:15px;font-weight:600;padding:6px 16px;border-radius:6px;' + 'pointer-events:none;opacity:0;transition:opacity 0.2s;' + 'font-family:-apple-system,BlinkMacSystemFont,sans-serif;' + 'white-space:nowrap;letter-spacing:0.5px;'; } if (!toastEl.parentNode) { const vc = document.querySelector('.custom-video-player'); if (vc) vc.appendChild(toastEl); } if (!toastEl.parentNode) return; toastEl.textContent = speed === 1.0 ? '1×' : speed + '×'; toastEl.style.opacity = '1'; clearTimeout(toastTimer); toastTimer = setTimeout(() => { toastEl.style.opacity = '0'; }, 700); } // Update UI function updateUI() { if (speedBtn) { speedBtn.textContent = currentSpeed === 1.0 ? '倍速' : currentSpeed + '×'; } if (speedMenu) { const resOpt = document.querySelector('.resolution-option'); const defaultColor = resOpt ? getComputedStyle(resOpt).color : '#ccc'; speedMenu.querySelectorAll('.nml-speed-opt').forEach(opt => { const s = parseFloat(opt.textContent); opt.style.color = s === currentSpeed ? '#ff6fae' : defaultColor; opt.style.fontWeight = s === currentSpeed ? '600' : ''; }); } } // Build UI function createSpeedSelector() { const controlBtn = document.querySelector('.custom-video-player .control-btn'); if (!controlBtn) return null; const wrapper = document.createElement('div'); wrapper.className = 'nml-speed-wrapper'; wrapper.setAttribute('data-v-65d53e0f', ''); wrapper.style.cssText = 'position:relative;display:inline-flex;align-items:center;'; speedBtn = controlBtn.cloneNode(true); while (speedBtn.firstChild) speedBtn.removeChild(speedBtn.firstChild); speedBtn.textContent = '倍速'; speedBtn.removeAttribute('title'); speedBtn.removeAttribute('aria-label'); speedBtn.addEventListener('mousedown', handleSpeedBtnMouseDown); speedBtn.addEventListener('click', e => { e.stopPropagation(); e.preventDefault(); }); wrapper.appendChild(speedBtn); // Menu speedMenu = document.createElement('div'); speedMenu.setAttribute('data-v-65d53e0f', ''); speedMenu.style.cssText = 'display:none;position:absolute;bottom:100%;right:0;margin-bottom:6px;' + 'background:rgba(28,28,28,0.96);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);' + 'border:1px solid rgba(255,255,255,0.12);border-radius:8px;' + 'padding:4px;min-width:82px;z-index:10001;' + 'box-shadow:0 4px 24px rgba(0,0,0,0.5);'; const resMenu = document.querySelector('.resolution-menu'); const resOpt = resMenu?.querySelector('.resolution-option'); SPEEDS.forEach(s => { const opt = document.createElement('div'); opt.className = 'nml-speed-opt'; opt.setAttribute('data-v-65d53e0f', ''); if (resOpt) { const cs = getComputedStyle(resOpt); opt.style.cssText = cs.cssText; } else { opt.style.cssText = 'padding:8px 14px;border-radius:6px;font-size:13px;' + 'text-align:center;cursor:pointer;white-space:nowrap;' + 'transition:background 0.15s,color 0.15s;color:#ccc;'; } if (s === currentSpeed) { opt.style.color = '#ff6fae'; opt.style.fontWeight = '600'; } opt.textContent = s + '×'; opt.addEventListener('mousedown', e => { e.stopPropagation(); e.preventDefault(); applySpeed(s); speedMenu.style.display = 'none'; }); opt.addEventListener('click', e => { e.stopPropagation(); e.preventDefault(); }); opt.addEventListener('mouseenter', function () { this.style.background = 'rgba(255,255,255,0.1)'; this.style.color = '#fff'; }); opt.addEventListener('mouseleave', function () { this.style.background = ''; const defColor = resOpt ? getComputedStyle(resOpt).color : '#ccc'; this.style.color = parseFloat(this.textContent) === currentSpeed ? '#ff6fae' : defColor; }); speedMenu.appendChild(opt); }); wrapper.appendChild(speedMenu); // Combined outside-click-to-close for both menus document.addEventListener('mousedown', closeMenusOnOutside, true); document.addEventListener('touchstart', closeMenusOnOutside, true); return wrapper; } function closeMenusOnOutside(e) { // Never interfere with search-bar interactions if (e.target.closest('#nav-searchform, .center-search__bar, .search-panel')) return; // Close speed menu if (speedMenu && speedMenu.style.display !== 'none') { const sw = speedMenu.parentNode; if (sw && !sw.contains(e.target)) { speedMenu.style.display = 'none'; } } // Close resolution menu — only when it's actually visible const resMenu = document.querySelector('.resolution-menu'); if (resMenu && resMenu.offsetParent !== null) { const resBtn = document.querySelector('.resolution-selector .control-btn'); if (resBtn && !resBtn.contains(e.target) && !resMenu.contains(e.target)) { resBtn.click(); } } } function handleSpeedBtnMouseDown(e) { e.stopPropagation(); e.preventDefault(); if (!speedMenu) return; // Close resolution menu before opening speed menu const resBtn = document.querySelector('.resolution-selector .control-btn'); const resMenu = document.querySelector('.resolution-menu'); if (resMenu && resBtn) { resBtn.click(); } const isHidden = speedMenu.style.display === 'none'; speedMenu.style.display = isHidden ? '' : 'none'; } // Mount function mountUI() { if (mounted) return true; // Defensive: remove any stale duplicates before creating a new one var stale = document.querySelectorAll('.nml-speed-wrapper'); for (var si = 0; si < stale.length; si++) stale[si].remove(); speedBtn = null; speedMenu = null; const resSelector = document.querySelector('.resolution-selector'); const danmakuToggle = document.querySelector('.danmaku-toggle-btn'); const volControl = document.querySelector('.volume-control'); let insertAfter = resSelector; if (!insertAfter) insertAfter = danmakuToggle; if (!insertAfter && volControl) { insertAfter = volControl.previousElementSibling; } if (!insertAfter || !insertAfter.parentNode) return false; const speedSelector = createSpeedSelector(); if (!speedSelector) return false; insertAfter.parentNode.insertBefore(speedSelector, insertAfter.nextSibling); updateUI(); startVideoObserver(); mounted = true; return true; } // Keyboard shortcuts document.addEventListener('keydown', e => { const tag = document.activeElement?.tagName; if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') return; // Only intercept keys when video is present on the page if (!findVideo() && !document.querySelector('.custom-video-player video')) return; // Vim-style scrolling: j k h l if (e.key === 'j') { window.scrollBy({ top: 120, behavior: 'smooth' }); return; } if (e.key === 'k') { window.scrollBy({ top: -120, behavior: 'smooth' }); return; } if (e.key === 'h') { window.scrollBy({ left: -120, behavior: 'smooth' }); return; } if (e.key === 'l') { window.scrollBy({ left: 120, behavior: 'smooth' }); return; } // Speed: > < if (e.key === '>' || (e.key === '.' && e.shiftKey)) { e.preventDefault(); const idx = SPEEDS.indexOf(currentSpeed); if (idx < SPEEDS.length - 1) applySpeed(SPEEDS[idx + 1]); return; } if (e.key === '<' || (e.key === ',' && e.shiftKey)) { e.preventDefault(); const idx = SPEEDS.indexOf(currentSpeed); if (idx > 0) applySpeed(SPEEDS[idx - 1]); return; } // Seek: ← → (10s) if (e.key === 'ArrowLeft') { e.preventDefault(); seekRelative(-10); return; } if (e.key === 'ArrowRight') { e.preventDefault(); seekRelative(10); return; } // Volume: ↑ ↓ if (e.key === 'ArrowUp') { e.preventDefault(); adjustVolume(0.05); return; } if (e.key === 'ArrowDown') { e.preventDefault(); adjustVolume(-0.05); return; } // Play/Pause: Space if (e.key === ' ' || e.code === 'Space') { e.preventDefault(); const v = findVideo() || document.querySelector('video'); if (v) { v.paused ? v.play() : v.pause(); } return; } // Fullscreen: F if (e.key === 'f' || e.key === 'F') { e.preventDefault(); toggleFullscreen(); return; } }); function seekRelative(delta) { const v = findVideo() || document.querySelector('video'); if (!v) return; const newTime = Math.max(0, Math.min(v.currentTime + delta, v.duration || Infinity)); v.currentTime = newTime; } function adjustVolume(delta) { const v = findVideo() || document.querySelector('video'); if (!v) return; const newVol = Math.max(0, Math.min(1, Math.round((v.volume + delta) * 100) / 100)); v.volume = newVol; v.muted = newVol === 0; // Sync the volume slider so the player's Vue state stays consistent const slider = document.querySelector('.custom-video-player .volume-slider'); if (slider) { slider.value = v.muted ? 0 : newVol; slider.dispatchEvent(new Event('input', { bubbles: true })); } saveVolume(newVol); showToastVolume(newVol); } function showToastVolume(vol) { if (!toastEl) { toastEl = document.createElement('div'); toastEl.style.cssText = 'position:absolute;top:12px;left:50%;transform:translateX(-50%);' + 'z-index:10000;background:rgba(0,0,0,0.85);color:#fff;' + 'font-size:15px;font-weight:600;padding:6px 16px;border-radius:6px;' + 'pointer-events:none;opacity:0;transition:opacity 0.2s;' + 'font-family:-apple-system,BlinkMacSystemFont,sans-serif;' + 'white-space:nowrap;letter-spacing:0.5px;'; } if (!toastEl.parentNode) { const vc = document.querySelector('.custom-video-player'); if (vc) vc.appendChild(toastEl); } if (!toastEl.parentNode) return; var pct = Math.round(vol * 100); toastEl.textContent = 'Vol ' + pct; toastEl.style.opacity = '1'; clearTimeout(toastTimer); toastTimer = setTimeout(function () { toastEl.style.opacity = '0'; }, 700); } // Channel panel: hover logo to show category nav const CATEGORIES = [ { name: '动漫', slug: 'anime' }, { name: '游戏', slug: 'game' }, { name: '音乐', slug: 'music' }, { name: '舞蹈', slug: 'dance' }, { name: '科技', slug: 'tech' }, { name: '吐槽', slug: 'commentary' }, { name: '漫画', slug: 'manga' }, { name: '鬼畜', slug: 'kichiku' }, { name: 'mmd', slug: 'mmd' }, { name: '日常', slug: 'daily' }, { name: 'AI-娱乐', slug: 'ai-entertainment' }, { name: '音mad', slug: 'otomad' } ]; let channelPanel = null; let channelTimer = null; function buildChannelPanel() { if (channelPanel) return; channelPanel = document.createElement('div'); channelPanel.className = 'nml-channel-panel'; channelPanel.setAttribute('data-v-e737bc8a', ''); channelPanel.innerHTML = '
' + '' + '' + '
' + '
' + CATEGORIES.map(function (c) { return ''; }).join('') + '
'; // Click: store target channel in sessionStorage, then navigate to homepage channelPanel.addEventListener('click', function (e) { var link = e.target.closest('.nml-chan-link'); if (!link) return; var slug = link.getAttribute('data-slug'); if (slug) { try { sessionStorage.setItem('nml_pending_channel', slug); } catch (e) {} } window.location.href = '/'; }); } function setupChannelPanel() { // Don't create if native header-channel already exists (e.g. on homepage) if (document.querySelector('.header-channel')) return; var logo = document.querySelector('.entry-title'); if (!logo) return; buildChannelPanel(); // Insert panel right after the logo logo.style.position = 'relative'; if (!channelPanel.parentNode) { logo.appendChild(channelPanel); } logo.addEventListener('mouseenter', function () { clearTimeout(channelTimer); channelPanel.classList.add('show'); }); logo.addEventListener('mouseleave', function () { channelTimer = setTimeout(function () { channelPanel.classList.remove('show'); }, 200); }); channelPanel.addEventListener('mouseenter', function () { clearTimeout(channelTimer); }); channelPanel.addEventListener('mouseleave', function () { channelPanel.classList.remove('show'); }); } // Channel panel styles (function injectChannelStyles() { var s = document.createElement('style'); s.textContent = '.nml-channel-panel{' + 'display:none;position:absolute;top:100%;left:0;margin-top:4px;' + 'background:var(--bg1,#fff);border:1px solid var(--line_regular,#e5e5e5);' + 'border-radius:10px;padding:12px 16px;z-index:1003;' + 'box-shadow:0 8px 32px rgba(0,0,0,0.12);white-space:nowrap;' + 'min-width:360px;' + '}' + '.nml-channel-panel.show{display:flex;gap:24px;}' + '.nml-channel-panel .channel-icons{display:flex;gap:16px;flex-shrink:0;}' + '.nml-channel-panel .channel-icons__item{display:flex;flex-direction:column;align-items:center;gap:6px;cursor:pointer;}' + '.nml-channel-panel .icon-bg{width:42px;height:42px;border-radius:10px;display:flex;align-items:center;justify-content:center;transition:transform 0.2s;}' + '.nml-channel-panel .icon-bg__dynamic{background:#ff6fae;color:#fff;}' + '.nml-channel-panel .icon-bg__popular{background:#ff9500;color:#fff;}' + '.nml-channel-panel .icon-title{font-size:12px;color:var(--text2,#666);}' + '.nml-channel-panel .channel-icons__item:hover .icon-bg{transform:scale(1.08);}' + '.nml-channel-panel .right-channel-container{display:flex;}' + '.nml-channel-panel .channel-items__left{display:grid;grid-template-columns:repeat(3,1fr);gap:4px 8px;}' + '.nml-channel-panel .channel-link{font-size:13px;color:var(--text2,#666);cursor:pointer;padding:4px 8px;border-radius:6px;transition:all 0.15s;}' + '.nml-channel-panel .channel-link:hover{background:var(--graph_bg_thin,rgba(0,0,0,0.04));color:var(--brand_pink,#ff6fae);}'; document.head.appendChild(s); })(); // Search box: keep history panel positioned below the search bar (function injectSearchStyles() { var s = document.createElement('style'); s.id = 'nml-search-fix'; s.textContent = /* Absolute-position the search panel right below #nav-searchform */ '.search-panel{position:absolute!important;top:100%!important;left:0!important;right:0!important;width:auto!important;z-index:1001!important;}' + '#nav-searchform{position:relative!important;overflow:visible!important;}' + '.center-search__bar{overflow:visible!important;}'; document.head.appendChild(s); })(); // Mobile responsive styles (<=760px) (function injectMobileStyles() { var s = document.createElement('style'); s.id = 'nml-mobile-styles'; s.textContent = '@media (max-width: 760px) {' + /* Player area: full-width, no rounded corners */ '.player-wrapper{width:100%!important;max-width:100%!important;margin-left:0!important;margin-right:0!important;}' + '.player-wrap{border-radius:0!important;margin-left:-10px!important;margin-right:-10px!important;margin-bottom:8px!important;}' + /* Player controls: prevent button overflow */ /* Hide volume slider on mobile (hardware buttons + keyboard arrows) */ '.custom-video-player .volume-control{display:none!important;}' + /* Allow controls to wrap onto a second line if needed */ '.custom-video-player .controls{flex-wrap:wrap!important;height:auto!important;padding:4px 4px!important;}' + '.custom-video-player .controls-row{flex-wrap:wrap!important;gap:3px!important;padding:0 2px!important;}' + /* Make buttons compact */ '.custom-video-player .control-btn{width:30px!important;height:30px!important;padding:2px!important;font-size:11px!important;min-width:30px!important;}' + /* Progress bar area: keep it full-width */ '.custom-video-player .progress-area{width:100%!important;flex-basis:100%!important;}' + /* Non-fullscreen: minimal controls, only progress + fullscreen */ /* Hide all control buttons */ '.player-wrapper:not(:fullscreen) .custom-video-player .control-btn{display:none!important;}' + /* Show only the last control button (fullscreen / enlarge) */ '.player-wrapper:not(:fullscreen) .custom-video-player .controls-row .control-btn:last-of-type{display:flex!important;width:34px!important;height:34px!important;}' + /* Also try by title */ '.player-wrapper:not(:fullscreen) .custom-video-player .control-btn[title*="屏"]{display:flex!important;}' + '.player-wrapper:not(:fullscreen) .custom-video-player .control-btn[title*="fullscreen"]{display:flex!important;}' + '.player-wrapper:not(:fullscreen) .custom-video-player .control-btn[title*="Fullscreen"]{display:flex!important;}' + /* Hide volume, resolution, speed, danmaku toggle */ '.player-wrapper:not(:fullscreen) .custom-video-player .volume-control{display:none!important;}' + '.player-wrapper:not(:fullscreen) .resolution-selector{display:none!important;}' + '.player-wrapper:not(:fullscreen) .nml-speed-wrapper{display:none!important;}' + '.player-wrapper:not(:fullscreen) .danmaku-toggle-btn{display:none!important;}' + /* Hide danmaku layer */ '.player-wrapper:not(:fullscreen) .danmaku-container{display:none!important;}' + /* When fullscreen, restore everything */ '.player-wrapper:fullscreen .custom-video-player .control-btn{display:flex!important;}' + '.player-wrapper:fullscreen .custom-video-player .volume-control{display:flex!important;}' + '.player-wrapper:fullscreen .resolution-selector{display:flex!important;}' + '.player-wrapper:fullscreen .nml-speed-wrapper{display:flex!important;}' + '.player-wrapper:fullscreen .danmaku-toggle-btn{display:flex!important;}' + '.player-wrapper:fullscreen .danmaku-container{display:block!important;}' + /* Danmaku input area */ '.danmaku-input-wrapper{padding:6px 8px!important;gap:4px!important;flex-wrap:wrap!important;}' + '.danmaku-input{flex:1!important;font-size:13px!important;height:34px!important;min-width:0!important;padding:6px 48px 6px 10px!important;}' + '.danmaku-input-wrapper .send-btn{width:32px!important;height:32px!important;min-width:32px!important;padding:3px!important;}' + '.danmaku-input-wrapper .settings-btn{width:28px!important;height:28px!important;min-width:28px!important;padding:2px!important;flex-shrink:0!important;}' + /* Danmaku settings panel: fix for mobile */ /* The panel is injected by Vue; make it screen-friendly */ '.danmaku-input-wrapper .settings-panel{' + 'position:fixed!important;top:auto!important;bottom:0!important;left:0!important;right:0!important;' + 'width:100%!important;max-width:100%!important;max-height:65vh!important;overflow-y:auto!important;' + 'border-radius:16px 16px 0 0!important;z-index:10020!important;' + 'padding:12px 14px!important;box-sizing:border-box!important;' + '}' + /* Darken backdrop when settings panel is open */ '.danmaku-input-wrapper .settings-panel::before{' + 'content:"";position:fixed;inset:0;bottom:65vh;background:rgba(0,0,0,0.4);z-index:-1;pointer-events:none;' + '}' + /* Video info action bar: compact */ '.action-bar{gap:6px!important;flex-wrap:wrap!important;}' + '.action-btn{font-size:12px!important;padding:6px 12px!important;min-height:32px!important;gap:4px!important;}' + '.action-btn svg{width:15px!important;height:15px!important;}' + /* Sidebar / related: tighten spacing */ '.detail-sidebar .section-title{font-size:14px!important;margin-bottom:10px!important;}' + '.related-cover{width:140px!important;height:79px!important;}' + '.related-title{font-size:13px!important;}' + /* Channel panel: narrower, 2 columns, hide icons */ '.nml-channel-panel{min-width:auto!important;width:92vw!important;left:50%!important;transform:translateX(-50%)!important;gap:0!important;padding:8px 12px!important;}' + '.nml-channel-panel .channel-icons{display:none!important;}' + '.nml-channel-panel .channel-items__left{grid-template-columns:repeat(2,1fr)!important;gap:2px 4px!important;}' + '.nml-channel-panel .channel-link{font-size:12px!important;padding:4px 6px!important;}' + /* Kaomoji panel: viewport-constrained */ '.nml-kao-panel{min-width:auto!important;max-width:96vw!important;left:2vw!important;right:2vw!important;padding:6px!important;border-radius:8px!important;}' + '.nml-kao-tabs{flex-wrap:nowrap!important;overflow-x:auto!important;max-width:100%!important;padding-bottom:4px!important;gap:2px!important;}' + '.nml-kao-tab{font-size:11px!important;padding:3px 8px!important;white-space:nowrap!important;flex-shrink:0!important;}' + '.nml-kao-grid{max-height:180px!important;gap:3px!important;}' + '.nml-kao-item{font-size:13px!important;padding:4px 8px!important;}' + /* Advanced danmaku settings: 3 columns → stack */ '.nml-adv-controls{padding:6px!important;border-radius:6px!important;}' + '.nml-adv-columns{flex-direction:column!important;gap:6px!important;}' + '.nml-adv-time-row{flex-direction:column!important;gap:4px!important;}' + '.nml-adv-col-row input[type=range]{height:5px!important;}' + '.nml-adv-col-title{font-size:10px!important;}' + /* Danmaku list panel */ '.nml-dm-list-panel{width:100%!important;margin-left:0!important;margin-right:0!important;}' + '.nml-dm-list-body{max-height:200px!important;padding:6px 8px!important;}' + '.nml-dm-params{padding-left:0!important;font-size:10px!important;white-space:normal!important;}' + '.nml-dm-entry{font-size:12px!important;padding:4px 0!important;}' + /* Speed menu */ '.nml-speed-wrapper .control-btn{font-size:12px!important;padding:2px 6px!important;}' + '.nml-speed-wrapper .nml-speed-opt{font-size:12px!important;padding:6px 10px!important;}' + /* Easing / Mode buttons: tighter on mobile */ '.nml-easing-btns{gap:2px!important;}' + '.nml-easing-btn{font-size:10px!important;padding:2px 5px!important;}' + '.nml-dm-mode-btn{font-size:11px!important;padding:2px 8px!important;}' + /* Settings row: compact sliders */ '.setting-row{gap:6px!important;margin-bottom:2px!important;}' + '.setting-row>span:first-child{font-size:12px!important;min-width:36px!important;}' + '.setting-row input[type=range]{flex:1!important;min-width:0!important;}' + '.setting-row .setting-val{font-size:11px!important;min-width:32px!important;text-align:right!important;}' + /* Header restructure */ /* Hide logo area, right-entry--outside, download float */ '.left-entry{display:none!important;}' + '.header-bar .right-entry--outside{display:none!important;}' + '.mobile-download-float{display:none!important;}' + /* Avatar: pin to top-left corner */ '.header-avatar-wrap{position:absolute!important;left:10px!important;top:50%!important;transform:translateY(-50%)!important;z-index:10!important;padding-right:0!important;}' + '.header-avatar-wrap .default-login,.header-avatar-wrap .mini-avatar--small{left:0!important;top:50%!important;transform:translateY(-50%)!important;}' + '.header-avatar-wrap:hover .mini-avatar--small{height:38px!important;left:0!important;top:50%!important;transform:translateY(-50%)!important;width:38px!important;}' + /* Moved message button: icon-only, matches theme-toggle style */ '.nml-header-msg{display:flex!important;align-items:center!important;justify-content:center!important;' + 'color:var(--text2)!important;width:32px!important;height:32px!important;' + 'border-radius:50%!important;cursor:pointer!important;' + 'transition:all 0.2s!important;flex-shrink:0!important;margin-right:2px!important;}' + '.nml-header-msg:hover{background:var(--graph_bg_thick)!important;color:var(--text1)!important;}' + '.nml-header-msg svg{width:20px!important;height:20px!important;}' + '.nml-header-msg span{display:none!important;}' + /* Search bar: fill remaining header width, leave space for avatar (38px) + right icons */ '.center-search-container{flex:1!important;margin-left:44px!important;margin-right:4px!important;max-width:none!important;min-width:0!important;}' + '.center-search-container .center-search__bar{max-width:none!important;}' + /* Header: tighter padding */ '.header-bar{padding:0 8px!important;height:50px!important;}' + /* Right entry: tighter spacing */ '.right-entry{margin-left:4px!important;}' + '.theme-toggle{margin-right:2px!important;width:32px!important;height:32px!important;}' + '.content-switch{gap:2px!important;}' + '.content-switch__item{font-size:11px!important;padding:0 8px!important;min-width:0!important;height:28px!important;}' + /* Channel bar: horizontal scroll */ '.header-channel{overflow-x:auto!important;overflow-y:hidden!important;white-space:nowrap!important;-webkit-overflow-scrolling:touch!important;scrollbar-width:none!important;flex-wrap:nowrap!important;}' + '.header-channel::-webkit-scrollbar{display:none!important;}' + '.header-channel>*{flex-shrink:0!important;}' + /* Bottom nav: keep original message btn hidden */ '.mobile-nav-bar .nml-msg-hidden{display:none!important;}' + /* Creator center: mobile layout */ /* Stack sidebar + content vertically */ '.creator-layout{flex-direction:column!important;height:auto!important;}' + '.creator-container{height:auto!important;min-height:100vh!important;}' + /* Sidebar: collapse to horizontal tab bar */ '.creator-aside{width:100%!important;border-right:none!important;border-bottom:1px solid var(--line_light)!important;}' + '.aside-header{display:none!important;}' + '.creator-menu{' + 'display:flex!important;flex-direction:row!important;overflow-x:auto!important;' + '-webkit-overflow-scrolling:touch!important;scrollbar-width:none!important;' + 'border-right:none!important;flex:none!important;' + '}' + '.creator-menu::-webkit-scrollbar{display:none!important;}' + '.creator-menu .el-menu-item{' + 'flex-shrink:0!important;white-space:nowrap!important;' + 'height:40px!important;line-height:40px!important;padding:0 16px!important;' + 'font-size:13px!important;border-bottom:2px solid transparent!important;' + '}' + '.creator-menu .el-menu-item.is-active{border-bottom-color:var(--brand_pink)!important;}' + /* Main content: reduce padding */ '.creator-main{padding:10px!important;overflow-y:visible!important;}' + '.content-wrapper{padding:12px!important;border-radius:6px!important;}' + '.content-header{margin-bottom:12px!important;padding-bottom:10px!important;}' + '.content-header h2{font-size:17px!important;}' + /* Toolbar: wrap buttons */ '.toolbar{display:flex!important;flex-wrap:wrap!important;gap:8px!important;margin-bottom:12px!important;}' + /* Table: horizontal scroll */ '.tab-content{overflow-x:auto!important;-webkit-overflow-scrolling:touch!important;}' + '.tab-content .el-table{min-width:600px!important;}' + /* Pagination: center on mobile */ '.pagination{justify-content:center!important;margin-top:16px!important;}' + /* Collection grid: single column */ '.collection-grid{grid-template-columns:1fr!important;gap:12px!important;}' + /* Card: tighter */ '.collection-card .card-header{flex-wrap:wrap!important;gap:6px!important;}' + '.col-title{font-size:14px!important;}' + '.description{font-size:13px!important;height:36px!important;}' + /* Cover: smaller */ '.cover-wrapper{width:100px!important;height:56px!important;}' + /* Add video section */ '.add-video-section{flex-wrap:wrap!important;gap:8px!important;}' + '}'; document.head.appendChild(s); })(); // Homepage: auto-select pending channel function trySelectChannel() { var slug = null; try { slug = sessionStorage.getItem('nml_pending_channel'); } catch (e) {} if (!slug) return; // Map slug to display name var nameMap = {}; CATEGORIES.forEach(function (c) { nameMap[c.slug] = c.name; }); if (slug === 'dynamic') nameMap.dynamic = '动态'; if (slug === 'popular') nameMap.popular = '热门'; var targetName = nameMap[slug]; if (!targetName) { clearPending(); return; } // Try to find a clickable channel element by text content var candidates = document.querySelectorAll( '[class*="channel"] a, [class*="channel"] button, [class*="channel"] span,' + '[class*="category"] a, [class*="category"] button, [class*="category"] span,' + '.channel-link, .category-item, .tab-btn,' + '[class*="ChannelBar"] a, [class*="ChannelBar"] button' ); for (var i = 0; i < candidates.length; i++) { var el = candidates[i]; if ((el.textContent || '').trim() === targetName) { el.click(); clearPending(); return; } } // Loose match for (var j = 0; j < candidates.length; j++) { var el2 = candidates[j]; if ((el2.textContent || '').indexOf(targetName) !== -1) { el2.click(); clearPending(); return; } } } function clearPending() { try { sessionStorage.removeItem('nml_pending_channel'); } catch (e) {} } // Kaomoji panel const KAOMOJI = [ { c: '开心', list: [ '╰(*°▽°*)╯', '(^▽^)', '(◕‿◕)', '✧(≖ ◡ ≖✿)', '(。•̀ᴗ-✿)', '(◠‿◠)', '(๑˃̵ᴗ˂̵)و', '(≧∇≦)', '(◍•ᴗ•◍)', '(。・ω・。)', '(ノ◕ヮ◕)ノ', '(●\'◡\'●)', '(◕ᴗ◕✿)', '♪(^∇^*)', '☆*:.。.o(≧▽≦)o.。.:*☆', 'ヽ(>∀<☆)ノ', '♪(๑ᴖ◡ᴖ๑)♪', '(▰˘◡˘▰)', '╭(●`∀´●)╯', 'ヾ(@^∇^@)ノ', 'o(*≧▽≦)ツ', '(ᗒᗨᗕ)', '٩(◕‿◕。)۶', '(★ω★)', '╰(▔∀▔)╯', '(✪ω✪)', 'o(≧∇≦o)', '(๑•̀ㅂ•́)و✧', '٩(ˊᗜˋ*)و', 'ヽ(✿゚▽゚)ノ', '( ^ω^)', '( ̄︶ ̄)', '( ´∀`)', '(o゚ω゚o)', '⌒(。・.・。)⌒','♪(╯^-^)╯♪' ]}, { c: '大笑', list: [ '(σ゚д゚)σ', 'ヾ(@>▽<@)ノ', '♪ヽ(^^ヽ)♪', '(・∀・)', '(●´∀`)●', 'Ψ( ̄∀ ̄)Ψ', 'ヽ(´▽`)/', '(๑´ㅂ`๑)', '哈哈哈哈', '恍恍惚惚', '红红火火恍恍惚惚', '啊哈哈哈哈(((o(*゚▽゚*)o)))', '笑死(≧∀≦)', '(σ゚∀゚)σ', '(((o(*゚▽゚*)o)))', 'ヽ(〃∀〃)ノ', '(ノ≧∀≦)ノ', '٩( ᐛ )و', '└(^o^)┘', '(๑>ᴗ<๑)', '(◡‿◡✿)', '∩(︶▽︶)∩', '( ゚∀゚)', '(●`∀´●)', '╭(●`∀´●)╯', '( ゜∀゜)' ]}, { c: '喜欢', list: [ '(。♥‿♥。)', '(❤ω❤)', '(´▽`ʃ♡ƪ)', '♡(◡‿◡✿)', '(⁄ ⁄>⁄ ▽ ⁄<⁄ ⁄)', '(。・//ε//・。)', '(♡°▽°♡)', '(✿◠‿◠)', '♥(ノ´∀`)', '(´ ε ` )♡', '(。・ω・。)ノ♡', '(*^3^)/~☆', '(◕‿◕✿)', '(っ˘з(˘⌣˘ )', '(*^3^)', 'ヽ(愛´∀`愛)ノ', '(。・ω・。)ノ♡', 'チュ~(´ε`*)', '(๑ơ ₃ ơ)♥', '(●♡∀♡)', '(. ❛ ᴗ ❛.)', '(⁄ ⁄•⁄ω⁄•⁄ ⁄)' ]}, { c: '难过', list: [ '(╥_╥)', '(╯︵╰,)', '(。•́︿•̀。)', '(╥﹏╥)', 'ಥ_ಥ', '(个_个)', '(╯_╰)', '(。ŏ﹏ŏ)', '(╥ω╥)', '(´;︵;`)', '。゚(゚´Д`゚)゚。', '(´°̥̥̥̥̥̥̥̥ω°̥̥̥̥̥̥̥̥`)', '(;´༎ຶД༎ຶ`)', '(´;ω;`)', '(༎ຶ ෴ ༎ຶ)', '。・゚゚・(>д<)・゚゚・。', '(;_;)', '(ノД`)', '(ノ_<。)','。・゜・(ノД`)・゜・。', '(iДi)', '( >﹏< )', 'ヽ(*。>Д<)o゜' ]}, { c: '生气', list: [ '(╬ Ò﹏Ó)', '(#`д´)ノ', '(╯°□°)╯︵ ┻━┻', '(≖_≖ )', '(¬_¬)', '(`皿´#)', '(⋋▂⋌)', 'ヽ(`⌒´)ノ', '(╬゚◥益◤゚)', '(◣_◢)', '(`Δ´)!', '(#`皿´)', '(╬ ̄皿 ̄)', '凸(`0´)凸', '━━━╋══◥◣_◢◤', '╋══◥◣_◢◤', '(≧m≦)', 'ヽ(●-`Д´-)ノ', 's(・`ヘ´・;)ゞ', '(; ̄Д ̄)', '(#°Д°)', 'ヽ(・ω・´メ)', '(。・`ω´・)' ]}, { c: '惊讶', list: [ '(⊙_⊙)', '(º ロ º)', '(°ロ°)', '(⊙﹏⊙)', '(〇o〇;)', '( Д ) ゚ ゚', '∑(O_O;)', '(;° ロ°)', '(⊙.☉)', '∑(゚Д゚)', '(((;꒪ꈊ꒪;)))', '(゚ロ゚)', 'щ(゚Д゚щ)', '(。ӧ◡ӧ。)', '!!!∑(゚Д゚ノ)ノ', '‼(•\'╻\'• )', '〣( ºΔº )〣', 'щ(゜ロ゜щ)', 'Σ(°△°|||)', '!!!w(゚Д゚)w', '!!!(ʘ言ʘ╬)', '(゚Д゚≡゚д゚)', 'щ(゚Д゚щ)' ]}, { c: '害羞', list: [ '(〃ω〃)', '(。・//ω//・。)', '(〃∀〃)', '(。-人-。)', '(๑•́ ₃ •̀๑)', '(*/ω\*)', '(〃⌒ー⌒〃)', '(❁´◡`❁)', '(。•́︿•̀。)', '( ̄▽ ̄*)ゞ', '(>/////<)', '(*/∇\*)', '(*ノωノ)', '(〃▽〃)','(´,,•ω•,,)♡', '(⁄ ⁄>⁄ ▽ ⁄<⁄ ⁄)', '(◞‸◟)', '(:3[▓▓]', '(*ノ▽ノ)', '(´_っ`)' ]}, { c: '眨眼', list: [ '(◕‿↼)', '(。•̀ᴗ-)✧', '◔ ⌣ ◔', '(・ω<)', '(。•ᴗ•。)♡', '( ̄ε ̄")', '(。・ω・。)ノ♡', '(^◡^)っ', '(๑¯◡¯๑)', '(✿◠‿◠)', '(≖‿≖)✧', '(・ω<)☆', '(●´ω`●)', '(・`ω´・)', '(^_-)','(. ❛ ᴗ ❛.)', '☆~(ゝ。∂)', '(=∀=*)', '(ΦωΦ)', '(ΦωΦ)' ]}, { c: '困惑', list: [ '( ̄ω ̄;)', '(@_@)', '(・_・;)', '(¯ . ¯;)', '(;¬_¬)', '(´・ω・`)?', '┐( ̄ヘ ̄")┌', '╮( ̄▽ ̄")╭', '( ̄~ ̄;)', '(´-ω-`)', '(。-ω-)zzz', '( ̄▽ ̄*)ゞ', '( ̄□ ̄;)', '(´-﹏-`;)', '(;一_一)', '(´゚ω゚`)', 'щ(ºДºщ)', '(O_o)', '( ・◇・)?','( ̄. ̄)', '(´・ω・`)' ]}, { c: '无语', list: [ '( ̄▽ ̄")', '(;´Д`)', '(´-ι_-`)', '( ̄ω ̄)', '(=_=;)', '(´・_・`)', '( ˘・з・)', '( ̄. ̄)', '┐(´-`)┌', '╮( ̄▽ ̄)╭', '( ̄ェ ̄;)', '(눈_눈)', '≖_≖', 'ー( ̄~ ̄)ξ', '( ̄^ ̄)', 'ヘ(´o`)ヘ', '(´_ゝ`)', '( ̄へ ̄)', '(›´ω`‹ )', '(  ̄  ̄ )', '(。_。)' ]}, { c: '傲娇', list: [ '哼!( ̄^ ̄)ゞ', '( ̄ε ̄")', '(,-`д´-)', '( `ー´)', '(-`ω´-)', '( ̄へ  ̄ )', '╭(╯^╰)╮', '(。-`ω´-)', '(`・ω・´)', '( ̄~ ̄)', '(≧へ≦)╯', '(-`ェ´-╬)', '(`へ´*)ノ', '( ・`⌓´・)', '(〃` 3´〃)', '(`ε´)', '(,,Ծ‸Ծ,,)', 'ヽ(`⌒´)ノ', '(◣_◢)', '(`・ω・´)', '(`・ω・´)', '(。-`ω´-)' ]}, { c: '舞蹈', list: [ '┗(^0^)┓', '♪(┌・。・)┌', '♪ヽ(^^ヽ)♪', 'ƪ(˘⌣˘)ʃ', 'ヾ(⌐■_■)ノ♪', '♬♩♫♪☻(●´∀`●)☺♪♫♩♬', '┏(^0^)┛', 'ヘ(^_^ヘ)', 'ヾ(´〇`)ノ', '♪♪♪ ヽ(ˇ∀ˇ )ゞ', '♫꒰・‿・๑꒱', '♪(o・ω・)ノ', '(〜^∇^)〜', '♪ヽ( ⌒o⌒)人(⌒-⌒ )v ♪', 'ヘ( ̄ω ̄ヘ)', 'ƪ(‾ε‾")ʃ', '┏(^0^)┛┗(^0^)┓', 'ヘ(^_^ヘ) ヘ(^o^ヘ)', '♪ ヾ(⌒ε⌒*)ゞ', 'ヾ(´・ω・`)ノ' ]}, { c: '摊手', list: [ '¯\\_(ツ)_/¯', '┐(´д`)┌', '╮(╯_╰)╭', '(;-_-)︵', '(ツ)', 'ヽ(。_°)ノ', '┐( ̄ヮ ̄)┌', '(´-ι_-`)', '~( ̄▽ ̄~)', '┐(´~`)┌', '╮( ̄~ ̄)╭', '┐( ̄ー ̄)┌', '( ̄ο ̄)', '(。-ω-)', '┐(´∀`)┌', '~(´ー`~)', '╮(╯∀╰)╭', '┐(シ)┌', '( ´_ゝ`)', '┐( ̄∀ ̄)┌' ]}, { c: '动物', list: [ '(=\\^・ω・\\^=)', '(◕ᴥ◕)', '(=\'◉ω◉\'=)', 'V●ᴥ●V', '(=^ ◡ ^=)', '(=\\^._.\\^=)∫', '( ̄(00) ̄)', '(°(oo)°)', 'くコ:彡', '/(=;x;=)\', '(=▼ω▼=)', '(=ФωФ=)', '(^=˃ᆺ˂)', '(=\'×\'=)', 'U•ᴥ•U', '(ᵔᴥᵔ)', 'ฅ(^ω^ฅ)', '/(・×・)\', '/(˃ᆺ˂)\', '(=`ェ´=)' ]}, { c: '颜艺', list: [ '( ͡° ͜ʖ ͡°)', '( ͡~ ͜ʖ ͡~)', '( ͠° ͟ʖ ͡°)', 'ヽ(͡◕ ͜ʖ ͡◕)ノ', '( ͡°╭ͮ ͟ʖ╮ ͡° )', '(☞゚ヮ゚)☞', '☜(゚ヮ゚☜)', '(⌐■_■)', '(•̀ᴗ•́)و ̑̑', '(◕ᴥ◕ʋ)', '( ͡ಠ ʖ̯ ͡ಠ)', '凸(¬‿¬)凸', 't(-_-t)', '(╭ರ_⊙)', '(♯`∧´)', '(ز ͡° ͜ʖ ͡°)ز', '┌∩┐(◣_◢)┌∩┐', '( ͡° ͜ʖ ͡°)=ε/̵͇̿̿/\'̿̿ ̿ ̿̿', '(ง\'̀-\'́)ง', 'щ(゚Д゚щ)', '( ゚∀。)' ]}, { c: '经典', list: [ '(づ。◕‿‿◕。)づ', '(づ ̄ ³ ̄)づ', '(◡ ω ◡)', '(ノ´ヮ`)ノ*: ・゚', '(>^ω^<)', '*:・゚✧(ꈍᴗꈍ)✧・゚:*', '☆*。★゚*♪ヾ(☆ゝз・)ノ', '✧*:・゚ヾ(●´∀`●)', '°˖✧◝(⁰▿⁰)◜✧˖°', '(✿⊙‿⊙)', '(ノ◕ヮ◕)ノ*:・゚✧', '(ノ>ω<)ノ :。・:*:・゚\'', '★,。・:*:・゚☆ ヽ(〃・ω・)ノ', ':・゚✧゚・: *ヽ(◕ヮ◕ヽ)', '~(^з^)-☆', '(>ω^<)ノ彡☆', '☆⌒(≧▽° )', '(*´▽`*)ノシ', '*・゜゚・*:.。..。.:*・\'(*゚▽゚*)\'・*:.。. .。.:*・゜゚・*' ]}, { c: '卖萌', list: [ '(◕‿◕✿)', '✿(。◕‿◕。)✿', '(◕ω◕✿)', '(◕ᴗ◕✿)', '(◡ ω ◡)', '(★^ー^★)', '✿♥‿♥✿', '(。・ω・。)', '(^ω^)', '(≧ω≦)', '٩(ˊᗜˋ*)و', 'ヾ(@⌒ー⌒@)ノ', '(っ´▽`)っ', '\(^ω^\)', '∪・ω・∪', '(๑•̀ㅂ•́)و✧', '(ᗒᗨᗕ)', '(●´艸`)', '(´。• ᵕ •。`)', '( ˘•ω•˘ )', '(。・ω・。)' ]}, { c: '躺平', list: [ '_(:3 」∠)_', '_(:3⌒゙)_', '_(´ཀ`」 ∠)_', '(:3[▓▓]', '(:3[」_]', '(:3_ヽ)_', '_ノ乙(、ン、)_', '(๑•́ ₃ •̀๑)エー', '(:3[▓▓▓]', '(。-ω-)zzz', '( ̄ρ ̄)..zzZZ', '(◎−◎;)', '(´。`)', '(ˇ▽ˇ)ZZz', '∪・ω・∪zzZ', '(´〜`*) zzz', 'ZZzz(。-_-。)ZZzz', '(。-ω-)' ]}, { c: '战斗', list: [ '(╯°□°)╯︵ ┻━┻', '┬─┬ノ( º _ ºノ)', '(ノಠ益ಠ)ノ', '(ง •̀_•́)ง', '(╯°益°)╯彡┻━┻', '┬─┬ ノ( ゜-゜ノ)', '(ʘ言ʘ╬)', '(ノ`Д´)ノ彡┻━┻', '┬──┬ ノ(ò_óノ)', '(ノ°Д°)ノ︵ ┻━┻', '┻━┻ ︵ヽ(´Д`ヽ)', '(╯ಠ_ಠ)╯︵ ┻━┻', '(ノ`□´)ノ⌒┻━┻', '(╯°□°)╯︵(\\.o.)\\', '┬─┬⃰͡ (ᵔᵕᵔ͜ )', '┻━┻︵ \\(°□°)/ ︵ ┻━┻', '(ノ-_-)ノ~┻━┻', 'ヽ(゚Д゚)ノ', '┗(`Д゚┗(`゚Д゚)┓゚Д´)┛' ]}, { c: 'fumo', list: [ '(ᗜᴗ⁠ᗜ)', '(ᗜˬᗜ)', '(ᗜ_ᗜ)', '(ᗜ‸ᗜ)' ]} ]; let kaoPanel = null; let kaoActiveInput = null; let kaoHideTimer = null; let kaoActiveCat = 0; function buildKaoPanel() { if (kaoPanel) return; kaoPanel = document.createElement('div'); kaoPanel.className = 'nml-kao-panel'; kaoPanel.addEventListener('mousedown', function (e) { e.preventDefault(); }); // Category tabs var tabs = document.createElement('div'); tabs.className = 'nml-kao-tabs'; KAOMOJI.forEach(function (cat, i) { var tab = document.createElement('span'); tab.className = 'nml-kao-tab' + (i === 0 ? ' active' : ''); tab.textContent = cat.c; tab.addEventListener('mousedown', function (e) { e.stopPropagation(); e.preventDefault(); switchKaoCat(i); }); tabs.appendChild(tab); }); kaoPanel.appendChild(tabs); // Kaomoji grid var grid = document.createElement('div'); grid.className = 'nml-kao-grid'; renderKaoGrid(grid, 0); kaoPanel.appendChild(grid); document.body.appendChild(kaoPanel); // Styles var s = document.createElement('style'); s.textContent = '.nml-kao-panel{' + 'display:none;position:fixed;z-index:10010;' + 'background:var(--bg1,#1a1a1a);border:2px solid var(--brand_pink,#ff6fae);' + 'border-radius:10px;padding:10px;min-width:400px;max-width:680px;' + 'box-shadow:0 8px 32px rgba(0,0,0,0.5),0 0 0 1px rgba(255,111,174,0.3) inset;' + '}' + '.nml-kao-panel.show{display:block;}' + '.nml-kao-tabs{' + 'display:flex;flex-wrap:wrap;gap:3px;margin-bottom:10px;' + 'border-bottom:2px dashed var(--brand_pink,rgba(255,111,174,0.4));padding-bottom:8px;' + '}' + '.nml-kao-tab{' + 'font-size:12px;padding:4px 10px;border-radius:12px;cursor:pointer;' + 'color:var(--text2,#999);transition:all 0.15s;user-select:none;' + 'border:1px solid transparent;' + '}' + '.nml-kao-tab:hover{background:var(--graph_bg_thin,rgba(255,255,255,0.06));color:var(--text1,#ddd);border-color:var(--brand_pink,rgba(255,111,174,0.3));}' + '.nml-kao-tab.active{background:var(--brand_pink,#ff6fae);color:#fff;border-color:var(--brand_pink,#ff6fae);}' + '.nml-kao-grid{' + 'display:flex;flex-wrap:wrap;gap:4px;max-height:280px;overflow-y:auto;' + 'scrollbar-width:thin;scrollbar-color:var(--brand_pink,#ff6fae) transparent;' + '}' + '.nml-kao-grid::-webkit-scrollbar{width:4px;}' + '.nml-kao-grid::-webkit-scrollbar-thumb{background:var(--brand_pink,#ff6fae);border-radius:4px;}' + '.nml-kao-item{' + 'font-size:14px;padding:5px 10px;border-radius:6px;cursor:pointer;' + 'color:var(--text1,#ddd);transition:all 0.12s;user-select:none;' + 'white-space:nowrap;line-height:1.7;border:1px solid transparent;' + '}' + '.nml-kao-item:hover{background:var(--brand_pink,#ff6fae);color:#fff;border-color:rgba(255,255,255,0.2);}'; document.head.appendChild(s); } function renderKaoGrid(grid, catIdx) { grid.innerHTML = ''; var list = KAOMOJI[catIdx].list; list.forEach(function (kao) { var item = document.createElement('span'); item.className = 'nml-kao-item'; item.textContent = kao; item.addEventListener('mousedown', function (e) { e.stopPropagation(); e.preventDefault(); insertKaomoji(kao); }); grid.appendChild(item); }); } function switchKaoCat(idx) { kaoActiveCat = idx; var tabs = kaoPanel.querySelectorAll('.nml-kao-tab'); tabs.forEach(function (t, i) { t.classList.toggle('active', i === idx); }); var grid = kaoPanel.querySelector('.nml-kao-grid'); renderKaoGrid(grid, idx); } function insertKaomoji(text) { var el = kaoActiveInput; if (!el) return; el.focus(); if (el.tagName === 'TEXTAREA' || el.tagName === 'INPUT') { var s = el.selectionStart; var e = el.selectionEnd; el.value = el.value.substring(0, s) + text + el.value.substring(e); el.selectionStart = el.selectionEnd = s + text.length; } else if (el.isContentEditable) { var sel = window.getSelection(); if (sel.rangeCount) { var r = sel.getRangeAt(0); r.deleteContents(); var tn = document.createTextNode(text); r.insertNode(tn); r.setStartAfter(tn); r.collapse(true); sel.removeAllRanges(); sel.addRange(r); } } // Dispatch input event so Vue/Element Plus picks up the change el.dispatchEvent(new Event('input', { bubbles: true })); } function positionKaoPanel(input) { var rect = input.getBoundingClientRect(); var top = rect.bottom + 6; var left = rect.left; // Keep panel within viewport (panel is up to 680px wide) if (left + 420 > window.innerWidth) left = window.innerWidth - 430; if (left < 4) left = 4; if (top + 320 > window.innerHeight) top = rect.top - 330; if (top < 4) top = 4; kaoPanel.style.top = top + 'px'; kaoPanel.style.left = left + 'px'; kaoPanel.classList.add('show'); } function hideKaoPanel() { if (kaoPanel) kaoPanel.classList.remove('show'); kaoActiveInput = null; } function onCommentFocus(e) { buildKaoPanel(); kaoActiveInput = e.target; positionKaoPanel(e.target); } function onCommentBlur() { clearTimeout(kaoHideTimer); var active = document.activeElement; kaoHideTimer = setTimeout(function () { if (kaoPanel && !kaoPanel.contains(active)) { hideKaoPanel(); } }, 150); } // Check if an element is a comment/chat/post input we should show kaomoji for function isCommentLike(el) { if (el.tagName !== 'TEXTAREA' && !el.isContentEditable) return false; // Skip search bars, login forms, etc. if (el.closest('[class*=search], [class*=login], [class*=sign], input[type=search]')) return false; // Match: comment-input, comment-editor, reply-*, chat-*, post-comment, message-input, editor-* if (el.closest('.comment-input, .comment-editor, [class*=comment], [class*=reply], [class*=chat-input], [class*=message-input], [class*=post-comment], [class*=editor], [class*=rich-text]')) return true; // Also match any textarea with a comment-like placeholder if (el.tagName === 'TEXTAREA') { var ph = (el.placeholder || '').toLowerCase(); if (/评论|回复|留言|说点什么|发(表|布|送)|聊|输入|看法|弹幕|吐槽|写(下|点)|参与|讨论/.test(ph)) return true; } return false; } // Use click delegation (capture phase) — most reliable trigger document.addEventListener('click', function (e) { var el = e.target; if (isCommentLike(el)) { onCommentFocus({ target: el }); } }, true); // Also hook focus for keyboard-only users (Tab navigation) document.addEventListener('focusin', function (e) { var el = e.target; if (isCommentLike(el)) { onCommentFocus({ target: el }); } }); // Blur — hide after short delay document.addEventListener('focusout', function (e) { if (isCommentLike(e.target)) { onCommentBlur(); } }); var keepActive = false; var styleObserver = null; function hookControlsStyle() { if (styleObserver) return; // Vue scoped selector from CustomVideoPlayer component var controls = document.querySelector('.custom-video-player .controls'); if (!controls) return; styleObserver = new MutationObserver(function (mutations) { if (!keepActive) return; mutations.forEach(function (m) { if (m.type === 'attributes' && m.attributeName === 'style') { // v-show sets style="display: none;" — clear it if (m.target.style.display === 'none') { m.target.style.display = ''; } } }); }); styleObserver.observe(controls, { attributes: true, attributeFilter: ['style'] }); } function startGuard() { if (keepActive) return; keepActive = true; hookControlsStyle(); } function stopGuard() { keepActive = false; } // Try to hook as soon as controls appear (retry a few times) function tryHookControlsStyle() { if (styleObserver) return; if (document.querySelector('.custom-video-player .controls')) { hookControlsStyle(); } } document.addEventListener('focusin', function (e) { if (e.target.classList && e.target.classList.contains('danmaku-input')) { startGuard(); } }); document.addEventListener('focusout', function (e) { if (e.target.classList && e.target.classList.contains('danmaku-input')) { setTimeout(function () { var a = document.activeElement; if (a && (a.classList.contains('danmaku-input') || a.closest('.danmaku-input-wrapper') || a.closest('.settings-panel') || a.closest('.nml-kao-panel') || a.closest('.nml-dm-mode-section'))) return; stopGuard(); }, 300); } }); function nmlCleanupKeepControls() { keepActive = false; if (styleObserver) { styleObserver.disconnect(); styleObserver = null; } if (_hookInterval) { clearInterval(_hookInterval); _hookInterval = null; } } // Hook early and retry for SPA lazy-load tryHookControlsStyle(); var _hookRetries = 0; var _hookInterval = setInterval(function () { if (styleObserver || _hookRetries++ > 30) { clearInterval(_hookInterval); return; } tryHookControlsStyle(); }, 500); document.addEventListener('focusin', function (e) { if (e.target.classList && e.target.classList.contains('nav-search-input')) { var form = document.getElementById('nav-searchform'); if (form) form.classList.add('nml-search-active'); } }); document.addEventListener('focusout', function (e) { if (e.target.classList && e.target.classList.contains('nav-search-input')) { setTimeout(function () { if (document.activeElement && document.activeElement.closest('#nav-searchform')) return; var form = document.getElementById('nav-searchform'); if (form) form.classList.remove('nml-search-active'); }, 200); } }); var miniPlayerEl = null; var miniVideoEl = null; var miniSyncTimer = null; var miniVisible = false; var miniObserver = null; function buildMiniPlayer() { if (miniPlayerEl) return; var EDGE = 6; // px from border to activate resize miniPlayerEl = document.createElement('div'); miniPlayerEl.className = 'nml-mini-player'; miniPlayerEl.innerHTML = '' + '' + '
' + ''; miniPlayerEl.querySelector('.nml-mini-close').addEventListener('click', function (e) { e.stopPropagation(); hideMiniPlayer(); }); miniPlayerEl.querySelector('.nml-mini-back').addEventListener('click', function (e) { e.stopPropagation(); var pw = document.querySelector('.player-wrapper'); if (pw) pw.scrollIntoView({ behavior: 'smooth', block: 'center' }); }); // Pause/play toggle — only via center button miniPlayerEl.querySelector('.nml-mini-pause-btn').addEventListener('click', function (e) { e.stopPropagation(); var ov = findVideo() || document.querySelector('.custom-video-player video'); if (!ov) return; if (ov.paused) { ov.play(); miniVideoEl.play(); } else { ov.pause(); miniVideoEl.pause(); } }); miniVideoEl = document.createElement('video'); miniVideoEl.playsInline = true; miniVideoEl.setAttribute('playsinline', ''); miniVideoEl.style.pointerEvents = 'none'; // clicks pass through to overlay miniPlayerEl.appendChild(miniVideoEl); document.body.appendChild(miniPlayerEl); if (!document.getElementById('nml-mini-style')) { var ms = document.createElement('style'); ms.id = 'nml-mini-style'; ms.textContent = '.nml-mini-player{' + 'display:none;position:fixed;bottom:20px;right:20px;z-index:9999;' + 'width:300px;height:169px;background:#000;border-radius:10px;overflow:visible;' + 'box-shadow:0 4px 24px rgba(0,0,0,.5);' + 'min-width:160px;min-height:90px;max-width:80vw;max-height:80vh;' + '}' + '.nml-mini-player video{' + 'width:100%;height:100%;object-fit:contain;pointer-events:none;' + '}' + '.nml-mini-close,.nml-mini-back{' + 'position:absolute;top:4px;z-index:20;width:26px;height:26px;' + 'border:none;background:rgba(0,0,0,.55);color:#fff;border-radius:50%;' + 'cursor:pointer;font-size:15px;line-height:26px;text-align:center;padding:0;' + 'opacity:0;transition:opacity .2s;' + '}' + '.nml-mini-close{right:4px;}' + '.nml-mini-back{right:34px;font-size:14px;}' + '.nml-mini-player:hover .nml-mini-close,.nml-mini-player:hover .nml-mini-back{opacity:1;}' + '.nml-mini-close:hover,.nml-mini-back:hover{background:rgba(0,0,0,.8);}' + '.nml-mini-pause-bg{' + 'position:absolute;inset:0;z-index:3;' + 'background:rgba(0,0,0,.35);opacity:0;transition:opacity .2s;' + 'pointer-events:none;' + // visual only, never blocks interaction '}' + '.nml-mini-player.paused .nml-mini-pause-bg,.nml-mini-player:hover .nml-mini-pause-bg{opacity:1;}' + '.nml-mini-pause-btn{' + 'position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);z-index:15;' + 'width:44px;height:44px;border:none;border-radius:50%;' + 'background:rgba(255,255,255,.15);cursor:pointer;' + 'display:flex;align-items:center;justify-content:center;' + 'opacity:0;transition:opacity .2s;padding:0;' + '}' + '.nml-mini-player.paused .nml-mini-pause-btn,.nml-mini-player:hover .nml-mini-pause-btn{opacity:1;}' + '.nml-mini-pause-btn:hover{background:rgba(255,255,255,.3);}' + '.nml-mini-player.paused .nml-mini-pause-icon{display:block;}' + '.nml-mini-player.paused .nml-mini-play-icon{display:none;}' + '.nml-mini-player:not(.paused) .nml-mini-pause-icon{display:none;}' + '.nml-mini-player:not(.paused) .nml-mini-play-icon{display:block;}' + '@media(max-width:768px){.nml-mini-player{width:200px;height:113px;bottom:12px;right:8px;}}'; document.head.appendChild(ms); } // Single mousedown: detect drag vs resize var actionInfo = null; // { type:'drag'|'resize', dir:'L'|'R'|'T'|'B'|... } miniPlayerEl.addEventListener('mousedown', function (e) { if (e.target.closest('.nml-mini-close,.nml-mini-back,.nml-mini-pause-btn')) return; var rect = miniPlayerEl.getBoundingClientRect(); var l = e.clientX - rect.left < EDGE, r = rect.right - e.clientX < EDGE, t = e.clientY - rect.top < EDGE, b = rect.bottom - e.clientY < EDGE; var dir = ''; if (l) dir += 'L'; else if (r) dir += 'R'; if (t) dir += 'T'; else if (b) dir += 'B'; // Fix to absolute coords miniPlayerEl.style.right = 'auto'; miniPlayerEl.style.bottom = 'auto'; miniPlayerEl.style.left = rect.left + 'px'; miniPlayerEl.style.top = rect.top + 'px'; if (dir) { miniPlayerEl.style.width = rect.width + 'px'; miniPlayerEl.style.height = rect.height + 'px'; actionInfo = { type: 'resize', dir: dir, sx: e.clientX, sy: e.clientY, sl: rect.left, st: rect.top, sw: rect.width, sh: rect.height }; } else { actionInfo = { type: 'drag', sx: e.clientX, sy: e.clientY, sl: rect.left, st: rect.top }; } e.preventDefault(); }); // Hover cursor miniPlayerEl.addEventListener('mousemove', function (e) { if (actionInfo) return; var rect = miniPlayerEl.getBoundingClientRect(); var l = e.clientX - rect.left < EDGE, r = rect.right - e.clientX < EDGE, t = e.clientY - rect.top < EDGE, b = rect.bottom - e.clientY < EDGE; if ((l && t) || (r && b)) miniPlayerEl.style.cursor = 'nwse-resize'; else if ((r && t) || (l && b)) miniPlayerEl.style.cursor = 'nesw-resize'; else if (l || r) miniPlayerEl.style.cursor = 'ew-resize'; else if (t || b) miniPlayerEl.style.cursor = 'ns-resize'; else miniPlayerEl.style.cursor = 'move'; }); document.addEventListener('mousemove', function (e) { if (!actionInfo) return; var a = actionInfo; if (a.type === 'drag') { miniPlayerEl.style.left = (a.sl + e.clientX - a.sx) + 'px'; miniPlayerEl.style.top = (a.st + e.clientY - a.sy) + 'px'; } else { var RATIO = 16 / 9; var dx = e.clientX - a.sx, dy = e.clientY - a.sy; var nw = a.sw, nh = a.sh, nl = a.sl, nt = a.st; var hasH = a.dir.indexOf('L') !== -1 || a.dir.indexOf('R') !== -1; var hasV = a.dir.indexOf('T') !== -1 || a.dir.indexOf('B') !== -1; if (hasH && !hasV) { // Horizontal edge — width drives, height follows ratio if (a.dir.indexOf('R') !== -1) nw = a.sw + dx; else nw = a.sw - dx; nw = Math.max(160, Math.min(nw, window.innerWidth * 0.8)); nh = nw / RATIO; if (a.dir.indexOf('L') !== -1) nl = a.sl + a.sw - nw; } else if (hasV && !hasH) { // Vertical edge — height drives, width follows ratio if (a.dir.indexOf('B') !== -1) nh = a.sh + dy; else nh = a.sh - dy; nh = Math.max(90, Math.min(nh, window.innerHeight * 0.8)); nw = nh * RATIO; if (a.dir.indexOf('T') !== -1) nt = a.st + a.sh - nh; } else { // Corner — use the larger delta direction if (Math.abs(dx) > Math.abs(dy)) { if (a.dir.indexOf('R') !== -1) nw = a.sw + dx; else nw = a.sw - dx; nw = Math.max(160, Math.min(nw, window.innerWidth * 0.8)); nh = nw / RATIO; if (a.dir.indexOf('L') !== -1) nl = a.sl + a.sw - nw; } else { if (a.dir.indexOf('B') !== -1) nh = a.sh + dy; else nh = a.sh - dy; nh = Math.max(90, Math.min(nh, window.innerHeight * 0.8)); nw = nh * RATIO; if (a.dir.indexOf('T') !== -1) nt = a.st + a.sh - nh; } } miniPlayerEl.style.width = nw + 'px'; miniPlayerEl.style.height = nh + 'px'; miniPlayerEl.style.left = nl + 'px'; miniPlayerEl.style.top = nt + 'px'; } }); document.addEventListener('mouseup', function () { actionInfo = null; }); // Update pause/play icon state miniVideoEl.addEventListener('play', function () { miniPlayerEl.classList.remove('paused'); }); miniVideoEl.addEventListener('pause', function () { miniPlayerEl.classList.add('paused'); }); } function showMiniPlayer() { if (window.innerWidth <= 768) return; var v = findVideo() || document.querySelector('.custom-video-player video'); if (!v || v.paused || v.ended) return; buildMiniPlayer(); if (!miniVisible) { // Reset position to default miniPlayerEl.style.left = ''; miniPlayerEl.style.top = ''; miniPlayerEl.style.right = '20px'; miniPlayerEl.style.bottom = '20px'; } var src = v.src || v.currentSrc; if (!src && v.querySelector('source')) { src = v.querySelector('source').src; } if (miniVideoEl.src !== src) { miniVideoEl.src = src; } miniVideoEl.currentTime = v.currentTime; miniVideoEl.playbackRate = v.playbackRate; miniVideoEl.volume = v.volume; miniVideoEl.muted = v.muted; var p = miniVideoEl.play(); if (p && p.then) { p.then(function () { miniPlayerEl.style.display = 'block'; miniVisible = true; }).catch(function () { // autoplay blocked, show anyway miniPlayerEl.style.display = 'block'; miniVisible = true; }); } else { miniPlayerEl.style.display = 'block'; miniVisible = true; } // Sync loop if (!miniSyncTimer) { miniSyncTimer = setInterval(function () { var ov = findVideo() || document.querySelector('.custom-video-player video'); if (!ov || !miniVisible) return; if (Math.abs(miniVideoEl.currentTime - ov.currentTime) > 0.8) { miniVideoEl.currentTime = ov.currentTime; } if (ov.paused && !miniVideoEl.paused) miniVideoEl.pause(); else if (!ov.paused && miniVideoEl.paused) miniVideoEl.play().catch(function () {}); if (ov.playbackRate !== miniVideoEl.playbackRate) miniVideoEl.playbackRate = ov.playbackRate; }, 500); } } function hideMiniPlayer() { if (miniPlayerEl) miniPlayerEl.style.display = 'none'; miniVisible = false; if (miniVideoEl) miniVideoEl.pause(); if (miniSyncTimer) { clearInterval(miniSyncTimer); miniSyncTimer = null; } } function setupMiniObserver() { if (miniObserver) miniObserver.disconnect(); var pw = document.querySelector('.player-wrapper') || document.querySelector('.custom-video-player'); if (!pw) { setTimeout(setupMiniObserver, 1000); return; } miniObserver = new IntersectionObserver(function (entries) { var entry = entries[0]; if (!entry) return; var v = findVideo() || document.querySelector('.custom-video-player video'); if (entry.isIntersecting || !v || v.paused || v.ended) { hideMiniPlayer(); } else { showMiniPlayer(); } }, { threshold: 0.3 }); miniObserver.observe(pw); } function nmlCleanupMiniPlayer() { hideMiniPlayer(); if (miniObserver) { miniObserver.disconnect(); miniObserver = null; } if (miniPlayerEl && miniPlayerEl.parentNode) { miniPlayerEl.remove(); miniPlayerEl = null; miniVideoEl = null; } _miniSetupTries = 0; if (_miniSetupTimer) { clearInterval(_miniSetupTimer); _miniSetupTimer = null; } startMiniSetup(); } function startMiniSetup() { if (_miniSetupTimer) return; _miniSetupTimer = setInterval(function () { if (location.pathname.indexOf('/video/') === 0) { if (document.querySelector('.custom-video-player video')) { setupMiniObserver(); clearInterval(_miniSetupTimer); _miniSetupTimer = null; } else if (_miniSetupTries++ > 60) { clearInterval(_miniSetupTimer); _miniSetupTimer = null; } } }, 500); } var _miniSetupTries = 0; var _miniSetupTimer = null; startMiniSetup(); // Markdown renderer for comments function renderMarkdown(text) { if (!text) return ''; var html = text; // Escape HTML first (except what we generate) html = html.replace(/&/g, '&').replace(//g, '>'); // Code blocks first (before other processing) html = html.replace(/```(\w*)\n([\s\S]*?)```/g, function (m, lang, code) { return '
' + code.replace(/\n$/, '') + '
'; }); // Inline code html = html.replace(/`([^`\n]+)`/g, '$1'); // Images (before links) html = html.replace(/!\[([^\]]*)\]\(([^)\s]+(?:\s+"[^"]*")?)\)/g, '$1'); // Links html = html.replace(/\[([^\]]+)\]\(([^)\s]+)\)/g, '$1'); // Bold html = html.replace(/\*\*([^*\n]+)\*\*/g, '$1'); html = html.replace(/__([^_\n]+)__/g, '$1'); // Italic html = html.replace(/\*([^*\n]+)\*/g, '$1'); html = html.replace(/_([^_\n]+)_/g, '$1'); // Strikethrough html = html.replace(/~~([^~\n]+)~~/g, '$1'); // Split into lines for block processing var lines = html.split('\n'); var result = []; var inList = false, listType = ''; var inQuote = false; var i = 0; while (i < lines.length) { var line = lines[i]; // Empty line: close any open blocks if (/^\s*$/.test(line)) { if (inQuote) { result.push(''); inQuote = false; } if (inList) { result.push(listType === 'ul' ? '' : ''); inList = false; listType = ''; } i++; continue; } // Code block opener — already handled above, skip if (/^```/.test(line)) { i++; while (i < lines.length && !/^```/.test(lines[i])) i++; i++; // skip closing ``` continue; } // Heading var hMatch = line.match(/^(#{1,6})\s+(.+)/); if (hMatch) { if (inQuote) { result.push(''); inQuote = false; } if (inList) { result.push(listType === 'ul' ? '' : ''); inList = false; listType = ''; } var level = hMatch[1].length; result.push('' + hMatch[2] + ''); i++; continue; } // Blockquote var qMatch = line.match(/^>\s?(.*)/); if (qMatch) { if (inList) { result.push(listType === 'ul' ? '' : ''); inList = false; listType = ''; } if (!inQuote) { result.push('
'); inQuote = true; } result.push('

' + qMatch[1] + '

'); i++; continue; } else if (inQuote && !/^>/.test(line) && !/^\s*$/.test(line)) { // Continue quote on non-empty non-quote line result.push('

' + line + '

'); i++; continue; } else if (inQuote) { result.push('
'); inQuote = false; } // Unordered list var ulMatch = line.match(/^[\-\*]\s(.+)/); if (ulMatch) { if (!inList || listType !== 'ul') { if (inList) result.push(listType === 'ul' ? '' : ''); result.push('' : ''); result.push('
    '); inList = true; listType = 'ol'; } result.push('
  1. ' + olMatch[1] + '
  2. '); i++; continue; } else if (inList) { result.push(listType === 'ul' ? '' : '
'); inList = false; listType = ''; } // Horizontal rule if (/^[-*_]{3,}\s*$/.test(line)) { result.push('
'); i++; continue; } // Regular paragraph result.push('

' + line + '

'); i++; } // Close any remaining open blocks if (inQuote) result.push(''); if (inList) result.push(listType === 'ul' ? '' : ''); return result.join('\n'); } function renderCommentText(el) { if (!el || el._nmlMdRendered) return; el._nmlMdRendered = true; var raw = el.textContent || ''; if (!/[\*\_\[\]`#>~\-]/.test(raw)) return; // no MD syntax, skip var html = renderMarkdown(raw); el.innerHTML = html; // Make links open in new tab el.querySelectorAll('a').forEach(function (a) { a.setAttribute('target', '_blank'); a.setAttribute('rel', 'noopener noreferrer'); }); } function renderAllComments() { document.querySelectorAll('.comment-text:not([data-nml-md])').forEach(function (el) { el.setAttribute('data-nml-md', '1'); renderCommentText(el); }); document.querySelectorAll('.reply-list .comment-text:not([data-nml-md])').forEach(function (el) { el.setAttribute('data-nml-md', '1'); renderCommentText(el); }); } // Observe new comments being added var mdCommentObserver = new MutationObserver(function () { renderAllComments(); }); function hookMdRenderer() { mdCommentObserver.disconnect(); var list = document.querySelector('.comments-list'); if (list) { mdCommentObserver.observe(list, { childList: true, subtree: true }); } renderAllComments(); // Also render replies var replyLists = document.querySelectorAll('.reply-list'); replyLists.forEach(function (rl) { mdCommentObserver.observe(rl, { childList: true, subtree: true }); }); } // Retry hook for SPA lazy-load var _mdRetries = 0; var _mdInterval = setInterval(function () { if (document.querySelector('.comments-section')) { hookMdRenderer(); clearInterval(_mdInterval); } else if (_mdRetries++ > 60) { clearInterval(_mdInterval); } }, 500); // Inject MD toolbar & renderer styles (function () { var s = document.createElement('style'); s.id = 'nml-md-styles'; s.textContent = /* Rendered MD styles */ '.comment-text[data-nml-md] p{margin:0 0 .5em;}' + '.comment-text[data-nml-md] p:last-child{margin-bottom:0;}' + '.comment-text[data-nml-md] strong{font-weight:600;}' + '.comment-text[data-nml-md] em{font-style:italic;}' + '.comment-text[data-nml-md] del{text-decoration:line-through;opacity:.7;}' + '.comment-text[data-nml-md] code{' + 'background:var(--graph_bg_regular,rgba(0,0,0,.06));padding:1px 4px;border-radius:3px;' + 'font-family:Consolas,Monaco,monospace;font-size:.9em;' + '}' + '.comment-text[data-nml-md] pre{background:var(--graph_bg_thin,rgba(0,0,0,.04));padding:8px 12px;' + 'border-radius:6px;overflow-x:auto;margin:.5em 0;' + '}' + '.comment-text[data-nml-md] pre code{background:none;padding:0;}' + '.comment-text[data-nml-md] blockquote{' + 'border-left:3px solid var(--brand_pink,#f69);padding:2px 0 2px 12px;margin:.5em 0;' + 'color:var(--text2,#666);' + '}' + '.comment-text[data-nml-md] ul,.comment-text[data-nml-md] ol{padding-left:1.5em;margin:.3em 0;}' + '.comment-text[data-nml-md] li{margin:2px 0;}' + '.comment-text[data-nml-md] h1,.comment-text[data-nml-md] h2,.comment-text[data-nml-md] h3,' + '.comment-text[data-nml-md] h4,.comment-text[data-nml-md] h5,.comment-text[data-nml-md] h6{' + 'font-weight:600;line-height:1.3;margin:.6em 0 .3em;' + '}' + '.comment-text[data-nml-md] h1{font-size:1.4em;}.comment-text[data-nml-md] h2{font-size:1.25em;}' + '.comment-text[data-nml-md] h3{font-size:1.1em;}' + '.comment-text[data-nml-md] hr{border:none;border-top:1px solid var(--line_regular,#e5e5e5);margin:.5em 0;}' + '.comment-text[data-nml-md] img{max-width:100%;border-radius:6px;margin:.3em 0;}' + '.comment-text[data-nml-md] a{color:var(--text_link,#008ac5);text-decoration:none;}' + '.comment-text[data-nml-md] a:hover{text-decoration:underline;}'; document.head.appendChild(s); })(); var _creatorPageSizes = [10, 20, 50, 100]; var _creatorSizeSelect = null; function buildCreatorSizeSelect(currentSize) { if (_creatorSizeSelect) return _creatorSizeSelect; var sel = document.createElement('select'); sel.className = 'nml-page-size-select'; sel.style.cssText = 'margin-left:12px;padding:4px 8px;border:1px solid var(--line_regular,#ddd);border-radius:6px;font-size:13px;color:var(--text1);background:var(--bg1);cursor:pointer;height:32px;'; _creatorPageSizes.forEach(function (s) { var opt = document.createElement('option'); opt.value = s; opt.textContent = s + ' 条/页'; if (s === currentSize) opt.selected = true; sel.appendChild(opt); }); sel.addEventListener('change', function () { var newSize = parseInt(sel.value); try { localStorage.setItem('nml_creator_page_size', String(newSize)); } catch (e) {} // Update Vue ref d.value to trigger re-fetch with new page_size var app = document.getElementById('__nuxt'); if (app && app.__vue_app__) { walkVue(app.__vue_app__._instance?.vnode, function (comp) { if (comp.setupState && 'd' in comp.setupState && comp.setupState.i) { // Found the creator video-manager component: has both 'd' (page_size) and 'i' (currentPage) refs comp.setupState.d.value = newSize; // Reset to page 1 to re-fetch comp.setupState.i.value = 1; } }); } }); _creatorSizeSelect = sel; return sel; } function injectCreatorPageSize() { var pagination = document.querySelector('.creator-container .pagination, .el-pagination'); if (!pagination) return; if (pagination.parentNode.querySelector('.nml-page-size-select')) return; var currentSize = 10; try { var saved = localStorage.getItem('nml_creator_page_size'); if (saved) currentSize = parseInt(saved) || 10; } catch (e) {} var sel = buildCreatorSizeSelect(currentSize); pagination.parentNode.insertBefore(sel, pagination.nextSibling); // Apply saved size on load if (currentSize !== 10) { setTimeout(function () { var app = document.getElementById('__nuxt'); if (app && app.__vue_app__) { walkVue(app.__vue_app__._instance?.vnode, function (comp) { if (comp.setupState && 'd' in comp.setupState && comp.setupState.i) { comp.setupState.d.value = currentSize; comp.setupState.i.value = 1; } }); } }, 500); } } // Watch for creator page var _creatorCheckInterval = setInterval(function () { if (location.pathname === '/creator' || location.pathname.startsWith('/creator')) { injectCreatorPageSize(); } }, 800); // Danmaku modes (zero-width char encoding in content) let danmakuMode = 0; let advSX = 0, advSY = 50, advMX = 50, advMY = 50, advEX = 100, advEY = 50; let advSR = 0, advMR = 0, advER = 0; let advEasing1 = 0, advEasing2 = 0; let advTimeSM = 25, advTimeME = 25; var ZW = ['​','‌','‍','‎','‏','','⁠','⁡','⁢','⁣','⁤']; // 11 zero-width chars var EASINGS = ['linear','ease','ease-in','ease-out','ease-in-out', 'cubic-bezier(.68,-.55,.27,1.55)','cubic-bezier(.36,0,.66,-.56)', 'cubic-bezier(.34,1.56,.64,1)','steps(3,end)','cubic-bezier(.45,0,.55,1)', 'cubic-bezier(.22,.61,.36,1)']; var EASING_NAMES = ['直线','缓动','渐入','渐出','渐入出','回弹','弹力','弹入','阶梯','平滑','标准']; function zwVal(ch) { var i = ZW.indexOf(ch); return i >= 0 ? i : 0; } function zwChar(v) { return ZW[v] || ZW[0]; } function b11(a,b) { return a*11 + b; } function encodeContent(text, mode) { if (mode === 0 || !text) return text; if (mode >= 1 && mode <= 3) return ZW[mode - 1] + text; if (mode === 4) { var p = [advSX, advSY, advMX, advMY, advEX, advEY, Math.round(advSR / 3), Math.round(advMR / 3), Math.round(advER / 3), advEasing1, advEasing2, advTimeSM, advTimeME]; var s = ZW[10]; p.forEach(function (v) { v = Math.round(Math.max(0, Math.min(120, v))); s += zwChar(Math.floor(v / 11)) + zwChar(v % 11); }); return s + text; } return text; } function decodeContent(el) { var text = el.textContent || ''; if (!text) return { mode: 0 }; var c0 = text.charCodeAt(0); if (c0 === 0x200B) return { mode: 1 }; if (c0 === 0x200C) return { mode: 2 }; if (c0 === 0x200D) return { mode: 3 }; if (c0 === 0x2064 && text.length >= 27) { var ch = text, i = 1; var sx = b11(zwVal(ch[i]), zwVal(ch[i+1])); i += 2; var sy = b11(zwVal(ch[i]), zwVal(ch[i+1])); i += 2; var mx = b11(zwVal(ch[i]), zwVal(ch[i+1])); i += 2; var my = b11(zwVal(ch[i]), zwVal(ch[i+1])); i += 2; var ex = b11(zwVal(ch[i]), zwVal(ch[i+1])); i += 2; var ey = b11(zwVal(ch[i]), zwVal(ch[i+1])); i += 2; var sr = b11(zwVal(ch[i]), zwVal(ch[i+1])) * 3; i += 2; var mr = b11(zwVal(ch[i]), zwVal(ch[i+1])) * 3; i += 2; var er = b11(zwVal(ch[i]), zwVal(ch[i+1])) * 3; i += 2; var e1 = b11(zwVal(ch[i]), zwVal(ch[i+1])); i += 2; var e2 = b11(zwVal(ch[i]), zwVal(ch[i+1])); i += 2; var ts = b11(zwVal(ch[i]), zwVal(ch[i+1])); i += 2; var te = b11(zwVal(ch[i]), zwVal(ch[i+1])); return { mode: 4, sx: sx, sy: sy, mx: mx, my: my, ex: ex, ey: ey, sr: sr, mr: mr, er: er, easing1: Math.min(e1, 10), easing2: Math.min(e2, 10), timeSM: Math.min(ts, 100), timeME: Math.min(te, 100) }; } if (c0 === 0x2064 && text.length >= 15) { var ch = text, i = 1; var sx = b11(zwVal(ch[i]), zwVal(ch[i+1])); i += 2; var sy = b11(zwVal(ch[i]), zwVal(ch[i+1])); i += 2; var ex = b11(zwVal(ch[i]), zwVal(ch[i+1])); i += 2; var ey = b11(zwVal(ch[i]), zwVal(ch[i+1])); i += 2; var sr = b11(zwVal(ch[i]), zwVal(ch[i+1])) * 3; i += 2; var er = b11(zwVal(ch[i]), zwVal(ch[i+1])) * 3; i += 2; var ease = b11(zwVal(ch[i]), zwVal(ch[i+1])); var avgX = Math.round((sx + ex) / 2); var avgY = Math.round((sy + ey) / 2); var avgR = Math.round((sr + er) / 2); return { mode: 4, sx: sx, sy: sy, mx: avgX, my: avgY, ex: ex, ey: ey, sr: sr, mr: avgR, er: er, easing1: Math.min(ease, 10), easing2: Math.min(ease, 10), timeSM: 25, timeME: 25 }; } if ((c0 === 0xFEFF || c0 === 0x2064) && text.length >= 6) { var x = b11(zwVal(text[1]), zwVal(text[2])); var y = b11(zwVal(text[3]), zwVal(text[4])); var r = zwVal(text[5]) * 33; return { mode: 4, sx: x, sy: y, mx: x, my: y, ex: x, ey: y, sr: r, mr: r, er: r, easing1: 0, easing2: 0, timeSM: 25, timeME: 25 }; } return { mode: 0 }; } function injectContentMarker() { if (danmakuMode === 0) return; var input = document.querySelector('.danmaku-input'); if (!input) return; var text = input.value; if (!text) return; var first = text.charCodeAt(0); if (first >= 0x200B && first <= 0x2064 && first !== 0x20 && first !== 0x0A) return; var encoded = encodeContent(text, danmakuMode); console.log('[nml-dm] SEND mode=' + danmakuMode + (danmakuMode === 4 ? ' sx=' + advSX + ' sy=' + advSY + ' mx=' + advMX + ' my=' + advMY + ' ex=' + advEX + ' ey=' + advEY + ' t1=' + (advTimeSM/10).toFixed(1) + 's t2=' + (advTimeME/10).toFixed(1) + 's' : '') + ' text=' + text.slice(0, 15)); input.value = encoded; input.dispatchEvent(new Event('input', { bubbles: true })); setTimeout(function () { input.value = text; input.dispatchEvent(new Event('input', { bubbles: true })); }, 60); } document.addEventListener('click', function (e) { if (e.target.closest('.send-btn')) injectContentMarker(); }, true); document.addEventListener('keydown', function (e) { if (e.key === 'Enter' && e.target.classList.contains('danmaku-input')) injectContentMarker(); }, true); var _advDmId = 0; (function () { var kf = document.createElement('style'); kf.id = 'nml-dm-keyframes'; kf.textContent = '@keyframes nml-reverse-dm{from{left:-50%;}to{left:110%;}}'; document.head.appendChild(kf); })(); var activeDmLanes = { 1: [], 2: [], 3: [] }; var DM_LANE_TOPS = { 1: [2,7,12,17,22,27,32,37,42], 2: [68,73,78,83,88,93], 3: [8,13,18,23,28,33,38,43,48,53,58,63,68,73,78,83,88] }; function assignDmLane(mode, clone) { var lanes = activeDmLanes[mode]; var tops = DM_LANE_TOPS[mode]; // Purge clones that have been removed from DOM for (var i = lanes.length - 1; i >= 0; i--) { if (!lanes[i].parentNode) lanes.splice(i, 1); } // Find first free top value var used = {}; for (var j = 0; j < lanes.length; j++) used[lanes[j]._nmlLaneTop] = true; var freeTop = null; for (var k = 0; k < tops.length; k++) { if (!used[tops[k]]) { freeTop = tops[k]; break; } } if (freeTop == null) { // All occupied — evict oldest var oldest = lanes.shift(); if (oldest.parentNode) oldest.remove(); freeTop = oldest._nmlLaneTop; } clone._nmlLaneTop = freeTop; lanes.push(clone); return freeTop; } function applyDanmakuStyle(el, data) { var text = el.textContent || ''; var first = text.charCodeAt(0); var stripLen = 0; if (first === 0x200B || first === 0x200C || first === 0x200D) stripLen = 1; else if (first === 0x2064 || first === 0xFEFF) stripLen = text.length >= 27 ? 27 : (text.length >= 15 ? 15 : (text.length >= 6 ? 6 : 0)); if (stripLen > 0) { text = text.slice(stripLen); el.textContent = text; } var clone = el.cloneNode(true); var fs = el.style.fontSize || '24px'; var col = el.style.color || '#fff'; var base = 'position:absolute!important;z-index:999!important;' + 'white-space:nowrap!important;pointer-events:none!important;' + 'font-size:' + fs + '!important;color:' + col + '!important;' + 'text-shadow:1px 0 2px #000,-1px 0 2px #000,0 1px 2px #000,0 -1px 2px #000;'; var player = document.querySelector('.player-wrapper'); var video = document.querySelector('.custom-video-player video'); if (data.mode === 1 || data.mode === 2) { var laneTop = assignDmLane(data.mode, clone); clone.style.cssText = base + 'left:50%!important;top:' + laneTop + '%!important;' + 'transform:translateX(-50%)!important;'; } else if (data.mode === 3) { var laneTop3 = assignDmLane(3, clone); var ss = document.querySelector('.danmaku-input-wrapper input[type=range]'); var dur = 720 / (parseFloat(ss?.value) || 120); clone.style.cssText = base + 'top:' + laneTop3 + '%!important;animation:nml-reverse-dm ' + dur + 's linear forwards!important;'; } else if (data.mode === 4) { var t1 = (data.timeSM || 25) / 10; var t2 = (data.timeME || 25) / 10; var totalMs = (t1 + t2) * 1000; var midPct = t1 / (t1 + t2); var mx = data.mx != null ? data.mx : (data.sx + data.ex) / 2; var my = data.my != null ? data.my : (data.sy + data.ey) / 2; var mr = data.mr != null ? data.mr : (data.sr + data.er) / 2; var e1 = EASINGS[data.easing1 != null ? data.easing1 : (data.easing || 0)] || 'linear'; var e2 = EASINGS[data.easing2 != null ? data.easing2 : (data.easing || 0)] || 'linear'; clone.style.cssText = base; try { var anim = clone.animate([ { left: data.sx + '%', top: data.sy + '%', transform: 'translate(-50%,-50%) rotate(' + data.sr + 'deg)', offset: 0 }, { left: mx + '%', top: my + '%', transform: 'translate(-50%,-50%) rotate(' + mr + 'deg)', offset: midPct, easing: e1 }, { left: data.ex + '%', top: data.ey + '%', transform: 'translate(-50%,-50%) rotate(' + data.er + 'deg)', offset: 1, easing: e2 } ], { duration: totalMs, fill: 'forwards' }); if (video) { var syncFn = function sync() { if (!clone.parentNode) return; try { if (video.paused) { if (anim.playState === 'running') anim.pause(); } else if (anim.playState === 'paused') anim.play(); } catch (e) {} requestAnimationFrame(sync); }; requestAnimationFrame(syncFn); } anim.onfinish = function () { clone.style.transition = 'opacity 0.3s'; clone.style.opacity = '0'; setTimeout(function () { if (clone.parentNode) clone.remove(); if (el.parentNode) el.remove(); }, 300); }; } catch (e) { el.style.display = ''; } } if (player) player.appendChild(clone); el.style.display = 'none'; if (data.mode === 1 || data.mode === 2) { if (video) { var startTime = video.currentTime; (function tick() { if (!clone.parentNode) return; if (video.paused) { startTime = video.currentTime; requestAnimationFrame(tick); return; } if (video.currentTime - startTime >= 4) { clone.style.transition = 'opacity 0.6s'; clone.style.opacity = '0'; setTimeout(function () { if (clone.parentNode) clone.remove(); if (el.parentNode) el.remove(); }, 600); return; } requestAnimationFrame(tick); })(); } } else if (data.mode === 3) { if (video) { (function sync() { if (!clone.parentNode) return; clone.style.setProperty('animation-play-state', video.paused ? 'paused' : 'running', 'important'); requestAnimationFrame(sync); })(); } clone.addEventListener('animationend', function () { clone.style.transition = 'opacity 0.3s'; clone.style.opacity = '0'; setTimeout(function () { if (clone.parentNode) clone.remove(); if (el.parentNode) el.remove(); }, 300); }); } } var danmakuObserveCount = 0; var danmakuLayerObserver = new MutationObserver(function (mutations) { mutations.forEach(function (m) { m.addedNodes.forEach(function (node) { if (node.nodeType !== 1 || !node.style) return; var data = decodeContent(node); danmakuObserveCount++; // Extract visible text (strip zero-width prefix) var visibleText = node.textContent || ''; var first = visibleText.charCodeAt(0); if (first === 0x200B || first === 0x200C || first === 0x200D) visibleText = visibleText.slice(1); else if (first === 0x2064 || first === 0xFEFF) visibleText = visibleText.slice(visibleText.length >= 27 ? 27 : (visibleText.length >= 15 ? 15 : 6)); console.log('[nml-dm] OBSERVE #' + danmakuObserveCount + ' mode=' + data.mode + (data.mode === 4 ? ' sx=' + data.sx + ' sy=' + data.sy + ' mx=' + (data.mx||'?') + ' my=' + (data.my||'?') + ' ex=' + data.ex + ' ey=' + data.ey : '') + ' text=' + visibleText.slice(0, 25)); addDanmakuEntry(data, visibleText); if (data.mode > 0) applyDanmakuStyle(node, data); }); }); }); function hookDanmakuLayer() { danmakuLayerObserver.disconnect(); var layer = null; var candidates = document.querySelectorAll('.player-wrapper > div, .player-wrapper > * > div'); for (var i = 0; i < candidates.length; i++) { try { var cs = getComputedStyle(candidates[i]); if (cs.pointerEvents === 'none' && (cs.position === 'absolute' || cs.position === 'fixed')) { // Check if it contains or could contain danmaku text if (candidates[i].childElementCount > 0 || candidates[i].offsetHeight > 100) { layer = candidates[i]; break; } } } catch (e) {} } if (!layer) { var dmEls = document.querySelectorAll('[class*=danmaku]'); for (var j = 0; j < dmEls.length; j++) { if (!dmEls[j].classList.contains('danmaku-input-wrapper') && !dmEls[j].closest('.danmaku-input-wrapper')) { layer = dmEls[j]; break; } } } if (!layer) layer = document.querySelector('.player-wrapper'); if (layer) { danmakuLayerObserver.observe(layer, { childList: true, subtree: true }); } else { setTimeout(hookDanmakuLayer, 1000); } } var dmList = []; var dmListPanel = null; var dmListExpanded = false; var MODE_LABELS = ['','顶部','底部','逆向','高级']; var MODE_CLASS = ['','nml-dm-tag-top','nml-dm-tag-bottom','nml-dm-tag-rev','nml-dm-tag-adv']; function addDanmakuEntry(data, text) { var video = document.querySelector('.custom-video-player video'); dmList.push({ time: video ? video.currentTime : 0, mode: data.mode, text: text, params: data.mode === 4 ? { sx: data.sx, sy: data.sy, mx: data.mx, my: data.my, ex: data.ex, ey: data.ey, sr: data.sr, mr: data.mr, er: data.er, easing1: data.easing1, easing2: data.easing2, timeSM: data.timeSM, timeME: data.timeME } : null }); updateDanmakuListCount(); if (dmListPanel && dmListExpanded) renderDanmakuList(); } function renderDanmakuList() { if (!dmListPanel) return; var body = dmListPanel.querySelector('.nml-dm-list-body'); if (!body || !dmListExpanded) return; var html = ''; for (var i = dmList.length - 1; i >= 0; i--) { var d = dmList[i]; var t = formatTime(d.time); var tagCls = MODE_CLASS[d.mode] || ''; var tagLabel = MODE_LABELS[d.mode] || '滚动'; var textEscaped = escapeHtml(d.text.length > 30 ? d.text.slice(0, 30) + '...' : d.text); html += '
' + '' + t + '' + '' + tagLabel + '' + '' + textEscaped + ''; if (d.params) { html += '
起点(' + d.params.sx + '%,' + d.params.sy + '%) ' + '→中间(' + (d.params.mx != null ? d.params.mx : '?') + '%,' + (d.params.my != null ? d.params.my : '?') + '%) ' + '→终点(' + d.params.ex + '%,' + d.params.ey + '%) ' + '旋转' + d.params.sr + '°→' + (d.params.mr != null ? d.params.mr : '?') + '°→' + d.params.er + '° ' + '缓1:' + (EASING_NAMES[d.params.easing1 != null ? d.params.easing1 : d.params.easing] || '?') + ' ' + '缓2:' + (EASING_NAMES[d.params.easing2 != null ? d.params.easing2 : d.params.easing] || '?') + ' ' + (d.params.timeSM != null ? (d.params.timeSM/10).toFixed(1) + 's' : '?') + '/' + (d.params.timeME != null ? (d.params.timeME/10).toFixed(1) + 's' : '?') + '
'; } html += '
'; } body.innerHTML = html; } function formatTime(sec) { var m = Math.floor(sec / 60); var s = Math.floor(sec % 60); return m + ':' + (s < 10 ? '0' : '') + s; } function escapeHtml(str) { return str.replace(/&/g, '&').replace(//g, '>'); } function buildDanmakuListPanel() { if (dmListPanel) return; dmListPanel = document.createElement('div'); dmListPanel.className = 'nml-dm-list-panel'; dmListPanel.style.cssText = 'display:block!important;visibility:visible!important;opacity:1!important;'; dmListPanel.innerHTML = '
' + '弹幕列表 (' + dmList.length + ')' + '' + '
' + ''; dmListPanel.querySelector('.nml-dm-list-header').addEventListener('click', function () { dmListExpanded = !dmListExpanded; var body = dmListPanel.querySelector('.nml-dm-list-body'); var arrow = dmListPanel.querySelector('.nml-dm-list-arrow'); if (dmListExpanded) { body.style.display = ''; arrow.textContent = '▾'; } else { body.style.display = 'none'; arrow.textContent = '▸'; } dmListPanel.querySelector('.nml-dm-list-title').textContent = '弹幕列表 (' + dmList.length + ')'; renderDanmakuList(); }); // Styles var ds = document.createElement('style'); ds.textContent = '.nml-dm-list-panel{margin-bottom:16px;border:1px solid var(--line_regular,#e5e5e5);border-radius:8px;overflow:hidden;}' + '.nml-dm-list-header{display:flex;justify-content:space-between;align-items:center;padding:10px 14px;' + 'cursor:pointer;user-select:none;background:var(--graph_bg_thin,rgba(0,0,0,0.02));transition:background 0.15s;}' + '.nml-dm-list-header:hover{background:var(--graph_bg_thin,rgba(0,0,0,0.05));}' + '.nml-dm-list-title{font-size:14px;font-weight:600;color:var(--text1,#333);}' + '.nml-dm-list-arrow{font-size:14px;color:var(--text2,#999);}' + '.nml-dm-list-body{max-height:320px;overflow-y:auto;padding:8px 14px;' + 'scrollbar-width:thin;scrollbar-color:var(--brand_pink,#ff6fae) transparent;}' + '.nml-dm-list-body::-webkit-scrollbar{width:4px;}' + '.nml-dm-list-body::-webkit-scrollbar-thumb{background:var(--brand_pink,#ff6fae);border-radius:4px;}' + '.nml-dm-entry{padding:6px 0;border-bottom:1px solid var(--line_regular,rgba(0,0,0,0.05));font-size:13px;}' + '.nml-dm-entry:last-child{border-bottom:none;}' + '.nml-dm-time{color:var(--text2,#999);margin-right:8px;font-family:monospace;}' + '.nml-dm-tag{display:inline-block;padding:1px 6px;border-radius:3px;font-size:11px;margin-right:6px;}' + '.nml-dm-tag-top{background:rgba(0,200,100,0.15);color:#00c864;}' + '.nml-dm-tag-bottom{background:rgba(0,150,255,0.15);color:#0096ff;}' + '.nml-dm-tag-rev{background:rgba(255,150,0,0.15);color:#ff9600;}' + '.nml-dm-tag-adv{background:rgba(255,111,174,0.15);color:#ff6fae;}' + '.nml-dm-text{color:var(--text1,#333);}' + '.nml-dm-params{font-size:11px;color:var(--text2,#999);margin-top:2px;padding-left:60px;}'; document.head.appendChild(ds); } function findRightAd() { var related = document.querySelector('[class*=related], [class*=recommend]'); if (related) { // Search siblings and ancestors for .ad-slot var ad = related.previousElementSibling; while (ad) { if (ad.matches('.ad-slot')) return ad; ad = ad.previousElementSibling; } } var ads = document.querySelectorAll('.ad-slot'); return ads.length > 0 ? ads[ads.length - 1] : null; } function injectDanmakuListPanel() { buildDanmakuListPanel(); if (document.contains(dmListPanel)) { updateDanmakuListCount(); return; } var ad = findRightAd(); if (!ad) { console.log('[nml-dm-list] no right .ad-slot found'); return; } ad.parentNode.insertBefore(dmListPanel, ad.nextSibling); ad.style.display = 'none'; updateDanmakuListCount(); console.log('[nml-dm-list] injected, entries=' + dmList.length); } function updateDanmakuListCount() { if (!dmListPanel) return; var title = dmListPanel.querySelector('.nml-dm-list-title'); if (title) title.textContent = '弹幕列表 (' + dmList.length + ')'; } // Guard setInterval(function () { if (!location.href.includes('/video/')) return; var ad = findRightAd(); if (ad && ad.style.display !== 'none') ad.style.display = 'none'; if (!dmListPanel || !document.contains(dmListPanel)) { console.log('[nml-dm-list] re-injecting'); dmListPanel = null; injectDanmakuListPanel(); } }, 1500); let dmModeBar = null; function buildDanmakuModeBar() { if (dmModeBar) return; dmModeBar = document.createElement('div'); dmModeBar.className = 'nml-dm-mode-section'; var modeRow = document.createElement('div'); modeRow.className = 'setting-row nml-dm-mode-row'; modeRow.setAttribute('data-v-65d53e0f', ''); modeRow.innerHTML = '模式'; var btnGroup = document.createElement('div'); btnGroup.className = 'nml-dm-mode-btns'; var modes = [ { id: 0, label: '滚动' }, { id: 1, label: '顶部' }, { id: 2, label: '底部' }, { id: 3, label: '逆向' }, { id: 4, label: '高级' } ]; modes.forEach(function (m) { var btn = document.createElement('button'); btn.setAttribute('data-v-65d53e0f', ''); btn.className = 'nml-dm-mode-btn' + (m.id === danmakuMode ? ' active' : ''); btn.textContent = m.label; btn.addEventListener('mousedown', function (e) { e.stopPropagation(); e.preventDefault(); danmakuMode = m.id; btnGroup.querySelectorAll('.nml-dm-mode-btn').forEach(function (b) { b.classList.remove('active'); }); btn.classList.add('active'); toggleAdvControls(); }); btnGroup.appendChild(btn); }); modeRow.appendChild(btnGroup); dmModeBar.appendChild(modeRow); var advControls = document.createElement('div'); advControls.className = 'nml-adv-controls'; advControls.setAttribute('data-v-65d53e0f', ''); advControls.style.display = danmakuMode === 4 ? '' : 'none'; // Compact slider builder function buildAdvSlider(cfg) { var row = document.createElement('div'); row.className = 'nml-adv-col-row'; row.setAttribute('data-v-65d53e0f', ''); var disp = cfg.fmt ? cfg.fmt(cfg.val) : cfg.val + (cfg.unit || ''); row.innerHTML = '' + cfg.label + '' + '' + '' + disp + ''; var slider = row.querySelector('input'); var valSpan = row.querySelector('.setting-val'); slider.addEventListener('input', function () { var v = parseInt(slider.value); valSpan.textContent = cfg.fmt ? cfg.fmt(v) : v + (cfg.unit || ''); cfg.set(v); }); return row; } // Three-column waypoint layout var columns = document.createElement('div'); columns.className = 'nml-adv-columns'; var waypoints = [ { title: '起点', cfgs: [ { ref: function() { return advSX; }, set: function(v) { advSX = v; }, label: 'X', min: 0, max: 100, val: advSX, unit: '%' }, { ref: function() { return advSY; }, set: function(v) { advSY = v; }, label: 'Y', min: 0, max: 100, val: advSY, unit: '%' }, { ref: function() { return advSR; }, set: function(v) { advSR = v; }, label: '∠', min: 0, max: 360, step: 3, val: advSR, unit: '°' } ]}, { title: '中间', cfgs: [ { ref: function() { return advMX; }, set: function(v) { advMX = v; }, label: 'X', min: 0, max: 100, val: advMX, unit: '%' }, { ref: function() { return advMY; }, set: function(v) { advMY = v; }, label: 'Y', min: 0, max: 100, val: advMY, unit: '%' }, { ref: function() { return advMR; }, set: function(v) { advMR = v; }, label: '∠', min: 0, max: 360, step: 3, val: advMR, unit: '°' } ]}, { title: '终点', cfgs: [ { ref: function() { return advEX; }, set: function(v) { advEX = v; }, label: 'X', min: 0, max: 100, val: advEX, unit: '%' }, { ref: function() { return advEY; }, set: function(v) { advEY = v; }, label: 'Y', min: 0, max: 100, val: advEY, unit: '%' }, { ref: function() { return advER; }, set: function(v) { advER = v; }, label: '∠', min: 0, max: 360, step: 3, val: advER, unit: '°' } ]} ]; waypoints.forEach(function (wp) { var col = document.createElement('div'); col.className = 'nml-adv-col'; var title = document.createElement('div'); title.className = 'nml-adv-col-title'; title.textContent = wp.title; col.appendChild(title); wp.cfgs.forEach(function (cfg) { col.appendChild(buildAdvSlider(cfg)); }); columns.appendChild(col); }); advControls.appendChild(columns); // Timing row (full width) var timeRow = document.createElement('div'); timeRow.className = 'nml-adv-time-row'; var timeCfgs = [ { ref: function() { return advTimeSM; }, set: function(v) { advTimeSM = v; }, label: '起→中', min: 5, max: 100, val: advTimeSM, fmt: function(v) { return (v/10).toFixed(1) + 's'; } }, { ref: function() { return advTimeME; }, set: function(v) { advTimeME = v; }, label: '中→终', min: 5, max: 100, val: advTimeME, fmt: function(v) { return (v/10).toFixed(1) + 's'; } } ]; timeCfgs.forEach(function (cfg) { var d = document.createElement('div'); d.className = 'setting-row'; d.setAttribute('data-v-65d53e0f', ''); var disp = cfg.fmt ? cfg.fmt(cfg.val) : cfg.val + (cfg.unit || ''); d.innerHTML = '' + cfg.label + '' + '' + '' + disp + ''; var slider = d.querySelector('input'); var valSpan = d.querySelector('.setting-val'); slider.addEventListener('input', function () { var v = parseInt(slider.value); valSpan.textContent = cfg.fmt ? cfg.fmt(v) : v + (cfg.unit || ''); cfg.set(v); }); timeRow.appendChild(d); }); advControls.appendChild(timeRow); function buildEasingRow(label, getter, setter) { var row = document.createElement('div'); row.className = 'setting-row'; row.setAttribute('data-v-65d53e0f', ''); row.innerHTML = '' + label + ''; var btns = document.createElement('div'); btns.className = 'nml-easing-btns'; EASING_NAMES.forEach(function (name, i) { var eb = document.createElement('button'); eb.setAttribute('data-v-65d53e0f', ''); eb.className = 'nml-easing-btn' + (i === getter() ? ' active' : ''); eb.textContent = name; eb.addEventListener('mousedown', function (e) { e.stopPropagation(); e.preventDefault(); setter(i); btns.querySelectorAll('.nml-easing-btn').forEach(function (b) { b.classList.remove('active'); }); eb.classList.add('active'); }); btns.appendChild(eb); }); row.appendChild(btns); return row; } advControls.appendChild(buildEasingRow('缓动1', function() { return advEasing1; }, function(v) { advEasing1 = v; })); advControls.appendChild(buildEasingRow('缓动2', function() { return advEasing2; }, function(v) { advEasing2 = v; })); dmModeBar.appendChild(advControls); // Easing button styles if (!document.getElementById('nml-easing-style')) { var es = document.createElement('style'); es.id = 'nml-easing-style'; es.textContent = '.nml-easing-btns{display:flex;flex-wrap:wrap;gap:3px;}' + '.nml-easing-btn{font-size:11px;padding:2px 6px;border-radius:3px;cursor:pointer;' + 'color:var(--text2,#999);background:transparent;border:1px solid var(--line_regular,rgba(255,255,255,0.1));transition:all 0.15s;outline:none;}' + '.nml-easing-btn:hover{color:var(--text1,#fff);border-color:var(--brand_pink,#ff6fae);}' + '.nml-easing-btn.active{background:var(--brand_pink,#ff6fae);color:#fff;border-color:var(--brand_pink,#ff6fae);}'; document.head.appendChild(es); } // Style if (!document.getElementById('nml-dm-mode-style')) { var s = document.createElement('style'); s.id = 'nml-dm-mode-style'; s.textContent = /* Mode section container — separated from other settings rows */ '.nml-dm-mode-section{margin-top:10px;padding-top:10px;border-top:2px dashed var(--brand_pink,rgba(255,111,174,0.35));}' + /* Mode selector row (matches .setting-row layout) */ '.nml-dm-mode-btns{display:flex;gap:4px;}' + '.nml-dm-mode-btn{' + 'font-size:12px;padding:3px 10px;border-radius:4px;cursor:pointer;' + 'color:var(--text2,#999);background:transparent;border:1px solid var(--line_regular,rgba(255,255,255,0.12));' + 'transition:all 0.15s;outline:none;' + '}' + '.nml-dm-mode-btn:hover{color:var(--text1,#fff);border-color:var(--brand_pink,#ff6fae);}' + '.nml-dm-mode-btn.active{background:var(--brand_pink,#ff6fae);color:#fff;border-color:var(--brand_pink,#ff6fae);}' + /* Advanced controls block — appears directly below mode row, visually grouped */ '.nml-adv-controls{' + 'margin-top:8px;padding:8px 10px;' + 'background:var(--graph_bg_thin,rgba(255,255,255,0.03));' + 'border-radius:8px;border:1px solid var(--line_regular,rgba(255,255,255,0.06));' + '}' + '.nml-adv-columns{display:flex;gap:6px;margin-bottom:6px;}' + '.nml-adv-col{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px;}' + '.nml-adv-col-title{text-align:center;font-size:11px;font-weight:700;color:var(--brand_pink,#ff6fae);margin-bottom:1px;padding:2px 0;border-bottom:1px solid var(--brand_pink,rgba(255,111,174,0.25));}' + '.nml-adv-col-row{display:flex;align-items:center;gap:2px;}' + '.nml-adv-col-row>span:first-child{font-size:10px;color:var(--text2,#999);width:14px;flex-shrink:0;text-align:center;}' + '.nml-adv-col-row input[type=range]{flex:1;min-width:0;height:3px;}' + '.nml-adv-col-row .setting-val{font-size:10px;color:var(--text2,#999);width:26px;flex-shrink:0;text-align:right;}' + '.nml-adv-time-row{display:flex;gap:8px;margin-top:6px;margin-bottom:6px;}' + '.nml-adv-time-row .setting-row{flex:1;}' + /* Close button (X) for settings panel */ '.nml-panel-close{' + 'position:absolute!important;top:8px;right:8px;z-index:10;' + 'width:32px;height:32px;display:flex;align-items:center;justify-content:center;' + 'background:transparent;border:none;border-radius:50%;cursor:pointer;' + 'color:var(--text3,#999);transition:all 0.15s;padding:4px;outline:none;' + '}' + '.nml-panel-close:hover{background:var(--graph_bg_regular,rgba(255,255,255,0.08));color:var(--text1,#fff);}' + '.nml-panel-close:active{background:var(--graph_bg_thick,rgba(255,255,255,0.12));transform:scale(0.92);}'; document.head.appendChild(s); } } function toggleAdvControls() { var ctrls = document.querySelector('.nml-adv-controls'); if (ctrls) ctrls.style.display = danmakuMode === 4 ? '' : 'none'; } function injectDanmakuModeBar() { var panel = document.querySelector('.danmaku-input-wrapper .settings-panel'); if (!panel || panel.querySelector('.nml-dm-mode-row')) return; buildDanmakuModeBar(); panel.appendChild(dmModeBar); toggleAdvControls(); // Inject close button (X) into settings panel if (!panel.querySelector('.nml-panel-close')) { var closeBtn = document.createElement('button'); closeBtn.className = 'nml-panel-close'; closeBtn.setAttribute('data-v-65d53e0f', ''); closeBtn.innerHTML = ''; closeBtn.title = '关闭设置'; closeBtn.addEventListener('mousedown', function (e) { e.stopPropagation(); e.preventDefault(); var settingsBtn = document.querySelector('.danmaku-input-wrapper .settings-btn'); if (settingsBtn) settingsBtn.click(); }); panel.insertBefore(closeBtn, panel.firstChild); } } // Watch for settings panel being opened (Vue re-creates it each time) var settingsPanelObserver = new MutationObserver(function (mutations) { mutations.forEach(function (m) { m.addedNodes.forEach(function (node) { if (node.nodeType === 1 && node.classList && node.classList.contains('settings-panel')) { injectDanmakuModeBar(); } }); }); }); function hookSettingsPanelObserver() { settingsPanelObserver.disconnect(); var wrapper = document.querySelector('.danmaku-input-wrapper'); if (wrapper) { settingsPanelObserver.observe(wrapper, { childList: true, subtree: true }); // Check if panel is already open if (wrapper.querySelector('.settings-panel')) injectDanmakuModeBar(); } else { setTimeout(hookSettingsPanelObserver, 800); } } // Init let initAttempts = 0; // Mobile header adaptation var _mobileAdaptDone = false; var _bodyObserver = null; var _myPanelObserver = null; function setupMessageButton() { if (window.innerWidth > 760) return; // Find the 消息 nav-item in bottom bar var navItems = document.querySelectorAll('.mobile-nav-bar .nav-item'); var msgItem = null; for (var ni = 0; ni < navItems.length; ni++) { if ((navItems[ni].textContent || '').trim() === '消息') { msgItem = navItems[ni]; break; } } if (!msgItem) return; // not rendered yet, observer will retry // Hide original msgItem.style.display = 'none'; msgItem.classList.add('nml-msg-hidden'); // Create or re-insert the header clone var existing = document.querySelector('.nml-header-msg'); if (existing) return; // already in place var rightEntry = document.querySelector('.right-entry'); if (!rightEntry) return; var msgClone = document.createElement('div'); msgClone.className = 'nml-header-msg'; msgClone.innerHTML = ''; msgClone.title = '消息'; msgClone.addEventListener('click', function () { var orig = document.querySelector('.mobile-nav-bar .nml-msg-hidden'); if (!orig) { var items = document.querySelectorAll('.mobile-nav-bar .nav-item'); for (var i = 0; i < items.length; i++) { if ((items[i].textContent || '').trim() === '消息') { orig = items[i]; break; } } } if (orig) orig.click(); }); var themeToggle = rightEntry.querySelector('.theme-toggle'); if (themeToggle) { rightEntry.insertBefore(msgClone, themeToggle); } else { rightEntry.appendChild(msgClone); } } // Shared check: hide original msg in nav + recreate clone if missing function checkAndFixMessageButton() { if (window.innerWidth > 760) return; var navItems = document.querySelectorAll('.mobile-nav-bar .nav-item'); var msgItem = null; for (var i = 0; i < navItems.length; i++) { if ((navItems[i].textContent || '').trim() === '消息') { msgItem = navItems[i]; break; } } if (msgItem) { if (!msgItem.classList.contains('nml-msg-hidden')) { msgItem.style.display = 'none'; msgItem.classList.add('nml-msg-hidden'); } if (!document.querySelector('.nml-header-msg')) { setupMessageButton(); } } } // Persistent observer: watches for mobile nav rendering + header re-renders function startMobileObservers() { if (window.innerWidth > 760) return; if (_bodyObserver) _bodyObserver.disconnect(); // Run immediately in case nav is already in DOM checkAndFixMessageButton(); _bodyObserver = new MutationObserver(function () { checkAndFixMessageButton(); }); _bodyObserver.observe(document.body, { childList: true, subtree: true }); } function adaptMobileLayout() { if (window.innerWidth > 760) return; setupMessageButton(); // Retry a few times after SPA navigation (Vue may not have rendered yet) var retries = 0; (function retry() { if (retries++ < 8 && !document.querySelector('.nml-header-msg')) { setupMessageButton(); setTimeout(retry, 250); } })(); startMobileObservers(); if (_mobileAdaptDone) return; _mobileAdaptDone = true; if (_myPanelObserver) _myPanelObserver.disconnect(); _myPanelObserver = new MutationObserver(function (mutations) { mutations.forEach(function (m) { m.addedNodes.forEach(function (node) { if (node.nodeType !== 1) return; var panel = node.classList && node.classList.contains('my-menu-panel') ? node : node.querySelector ? node.querySelector('.my-menu-panel') : null; if (!panel || panel.querySelector('[data-nml-creator]')) return; var items = panel.querySelectorAll('.my-menu-item'); var historyItem = null; for (var i = 0; i < items.length; i++) { if ((items[i].textContent || '').indexOf('观看历史') !== -1 || (items[i].textContent || '').indexOf('历史') !== -1) { historyItem = items[i]; break; } } if (historyItem) { var creatorBtn = document.createElement('button'); creatorBtn.className = 'my-menu-item'; creatorBtn.setAttribute('data-v-e4d29153', ''); creatorBtn.setAttribute('data-nml-creator', ''); creatorBtn.innerHTML = ''; creatorBtn.appendChild(document.createTextNode('创作中心')); creatorBtn.addEventListener('click', function (e) { e.stopPropagation(); var overlay = document.querySelector('.my-menu-overlay'); if (overlay) overlay.click(); setTimeout(function () { // Use Vue Router for SPA navigation (not location.href which does full reload) var router = null; try { var app = document.getElementById('__nuxt').__vue_app__; router = app.config.globalProperties.$router; } catch (err) {} if (router) { router.push('/creator'); } else { window.location.href = '/creator'; } }, 200); }); historyItem.parentNode.insertBefore(creatorBtn, historyItem.nextSibling); } }); }); }); _myPanelObserver.observe(document.body, { childList: true, subtree: true }); } function doInit() { adaptMobileLayout(); setupChannelPanel(); if (location.pathname === '/' || location.pathname === '') { var channelAttempts = 0; function tryChannel() { trySelectChannel(); channelAttempts++; if (channelAttempts < 15) setTimeout(tryChannel, 400); } tryChannel(); } if (location.pathname.indexOf('/video/') === 0) { hookDanmakuLayer(); hookSettingsPanelObserver(); injectDanmakuListPanel(); hookMdRenderer(); if (mountUI()) return; initAttempts++; if (initAttempts < 50) setTimeout(doInit, 200); } } doInit(); function onNavigate() { _mobileAdaptDone = false; setTimeout(adaptMobileLayout, 600); var url = location.href; if (url.includes('/video/')) { // Clean up previous video page state var old = document.querySelector('.nml-speed-wrapper'); if (old) old.remove(); videoObserver.disconnect(); speedBtn = null; speedMenu = null; videoEl = null; mounted = false; initAttempts = 0; danmakuMode = 0; dmList = []; activeDmLanes = { 1: [], 2: [], 3: [] }; danmakuLayerObserver.disconnect(); nmlCleanupKeepControls(); nmlCleanupMiniPlayer(); mdCommentObserver.disconnect(); _mdRetries = 0; if (_mdInterval) { clearInterval(_mdInterval); _mdInterval = null; } _mdInterval = setInterval(function () { if (document.querySelector('.comments-section')) { hookMdRenderer(); clearInterval(_mdInterval); _mdInterval = null; } else if (_mdRetries++ > 60) { clearInterval(_mdInterval); _mdInterval = null; } }, 500); currentSpeed = savedSpeed; // Re-initialize after Vue renders the new page setTimeout(doInit, 800); } } var _pushState = history.pushState; history.pushState = function () { _pushState.apply(this, arguments); onNavigate(); }; var _replaceState = history.replaceState; history.replaceState = function () { _replaceState.apply(this, arguments); onNavigate(); }; window.addEventListener('popstate', onNavigate); // Start if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', function () { setTimeout(doInit, 600); }); } else { setTimeout(doInit, 600); } })();