您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
infinite scroll, filter: public, private, duration, positive/negative tags
当前为
// ==UserScript== // @name thisvid infinite scroll and filter // @license MIT // @namespace http://tampermonkey.net/ // @version 2.9 // @description infinite scroll, filter: public, private, duration, positive/negative tags // @author smartacephale // @match https://*.thisvid.com/* // @exclude https://*.thisvid.com/playlist* // @exclude https://*.thisvid.com/community/* // @exclude https://*.thisvid.com/albums/* // @exclude https://*.thisvid.com/my* // @icon https://i.imgur.com/LAhknzo.jpeg // @grant GM_addStyle // @run-at document-end // ==/UserScript== const ICON = ` ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░║▓▓▓▀░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ░░░░░░░░░░░░░░░░░░░░░▒▒▒▒▒▒░░░░░░░░░░░░░░░░░▒▓▓▓▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ░░░░░░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒▒▒░▒▒▒▒▒░░░░░░░░░░╫█▓▓▓▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ░░░░░░░░░░░░▒▒▒▒▒░▒░░░░░░░░░░▒▒▒▒▒▒@@▒▒▒▒╖░▓▓▓▓▓▒░░░░░░░░░░░░░▒▒▒▒▒░░░░░░░░░░░░░ ░░░░░░░░░▒▒▒▒▒▒░▒░░░░░░░░░░░░░░░▒▒▒▒╣╣╢╢╣╣╣▓▓▓▓▓▓▓▓▓▓▓╣╣▒▒▒▒▒░░░░░░░░▒▒░░░░░░░░░ ░░░░░░░░▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░▒▒▒▒╢╢╫▓▓▓▓▓▓▓▓▓▓▓▓▓╣▒▒▒░░░░░░░░░░░░░░░░░░░░░░░ ░░░░░░▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░▒▒▒▒▒╢▓▓▓▓▓▓▓▓▓▓▓▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░ ░░░░░▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░▒░░░▒▒▒▒╫▓▓▓▓▓▓▓▓▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ░░░░▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒╫▓▓▓▓▓▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ░░░▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒╫▓▓▓▓▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ░░▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒╢▓▓▓▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ░▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒╢╣▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒╣▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒▒▒▒╣╣╣▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒▒╣╣▒▒▒╢▒▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░▒░▒▒▒▒▒▒▒╢╢╣▒▒╢╣▒▒▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░ ▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒▒╢╣╣▒▒╢▓▒▒▒▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░ ▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒▒▒▒▒▒╢╫╣╣▒▒╢▓▒▒▒▒▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░ ▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒▒▒▒▒░▒▒╣╢╣▒▒▒▒▓╣▒▒▒▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░ ▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒░▒▒▒▒▒▒╫╣▒▒▒▒▒╫╣▒▒▒▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░ ▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒▒▒▒░▒▒╢╢▒▒╢╣╣╢╫╣╣▒▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░ ▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒▒▒▒▒▒▒╢╫╣╣▒╢╣▒▒▒╣▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░ ▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒▒▒▒▒╢▒▒▒╢▓▓╣▒▒▒▒▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░ ▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒▒▒▒▒▒▒▓▓╣▓╣▒▒▒▒░▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░ ▒▒▒░▒░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒▒▒▒▒░▒▒▒╢▓▓▓╬▒▒▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░ ▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒▒▒╢▓╣▒▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒▒╣╣▒▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒▒▒▒▒▒╣╣╣▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ▒▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒▒▒▒▒╣╢╣▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ▒▒▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒▒▒╢▒╣▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ▒▒▒▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒╢▓▓╣╫▓╣▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ▒▒▒▒▒▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒╫▓▓▓▓▓▓╣▒▒░░░░░░░░░░░░░░░░░░░░░░░░░▒░░░░░░ ▒▒▒▒▒▒▒▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░▒▒▒▒╫▓▓▓▓▓▓▓▓▓╣╣▒▒░░░░░░░░░░░░░░░░░░░░▒▒░░░░░░░ ▒▒▒▒▒▒▒▒╣▒╣▒▒▒░▒░░░░░░░░░░░░░░▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓╣╫╣▒▒░░░░░░░░░░░░░░▒▒░░░░░░░░░░ ▒▒▒▒▒▒▒▒╢╣╢╢▓▓╣▒▒▒▒▒▒▒▒▒░░░▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█▓▓▓▓▓╣▒▒▒▒▒▒▒░░░▒▒▒▒▒░░░░░░░░░░░ ░░░░░░░░░░░░▒▒▒╨╨╢▒▒▒▒▒▒▒▒▒▒╨╨╜╜▒▒▒▒▒▒╜▒▒╨╨╨╨╨╨╨╨╨╨╨▀▒╫▒▒╜╣╫╝,░▒▒░]▒▒▒▒╨▒▒░╜Ñ▒╓─`.trim(); (function () { 'use strict'; console.clear(); console.log(ICON); function $(x, e = document.body) { return e.querySelector(x); } function $$(x, e = document.body) { return e.querySelectorAll(x); } function parseDOM(html) { const parsed = new DOMParser().parseFromString(html, 'text/html').body; return parsed.children.length > 1 ? parsed : parsed.firstElementChild; } function fetchHtml(url) { return fetch(url).then((r) => r.text()).then((h) => parseDOM(h)); } function parseCSSUrl(s) { return s.replace(/url\("|\"\).*/g, ''); } function timeToSeconds(t) { return (t.match(/\d+/gm) || ['0']) .reverse() .map((s, i) => parseInt(s) * 60 ** i) .reduce((a, b) => a + b) || 0; } function circularShift(n, c = 6, s = 1) { return (n + s) % c || c; } function parseIntegerOr(n, or) { return Number.isInteger(parseInt(n)) ? parseInt(n) : or; } class Observer { constructor(callback) { this.callback = callback; this.observer = this.create(); } create() { return new IntersectionObserver((entries, observer) => { for (const entry of entries) { if (entry.isIntersecting) { this.callback(entry.target, observer); } } }); } observe(element) { this.observer.observe(element); } } class Tick { constructor(interval) { this.interval = interval; } start(callback) { if (this.ticker) { this.stop(); } callback(); this.ticker = setInterval(callback, this.interval); } stop() { clearInterval(this.ticker); this.ticker = false; } } class ReactiveLocalStorage { constructor(data) { if (data) { Object.assign(this, data); this.observeProps(this); } } getLS(prop) { return JSON.parse(localStorage.getItem(prop)); } setLS(prop, value) { localStorage.setItem(prop, JSON.stringify(value)); } toObservable(obj, prop) { const lsvalue = this.getLS(prop); let value = lsvalue !== null ? lsvalue : obj[prop]; Object.defineProperty(obj, prop, { get() { return value; }, set(newValue) { this.setLS(prop, newValue); value = newValue; }, }); } observeProps(obj) { for (const [key, _] of Object.entries(obj)) { this.toObservable(obj, key); } } } class DomManager { constructor() { this.data = new Map(); this.container = this.createThumbsContainer(); this.observables = []; this.lazyImageObserver = new Observer((target, observer) => { if (!this.isFiltered(target)) { observer.unobserve(target); this.delazify(target); } }); } setObservation() { for (const observable of this.observables) { this.lazyImageObserver.observe(observable); } this.observables = []; } delazify(el) { const img = el.firstElementChild.firstElementChild; img.src = img.getAttribute('lazy-loading'); } isFiltered(el) { return el.className.includes('filtered'); } filterPositiveTags = () => { for (const [k, v] of this.data.entries()) { const containTagsNot = state.filterPositiveTags.some(tag => !k.includes(tag)); v.element.classList.toggle('filtered-tag-pos', state.filterPositive && containTagsNot); }; } filterNegativeTags = () => { for (const [k, v] of this.data.entries()) { const containTags = state.filterNegativeTags.some(tag => k.includes(tag)); v.element.classList.toggle('filtered-tag-neg', state.filterNegative && containTags); }; } thumbIsPrivate(t) { return t.firstElementChild.classList.contains('private'); } filterPrivate = (filterPrivate = state.filterPrivate) => { for (const v of this.data.values()) { v.element.classList.toggle('filtered-private', filterPrivate && this.thumbIsPrivate(v.element)) }; } filterPublic = (filterPublic = state.filterPublic) => { for (const v of this.data.values()) { v.element.classList.toggle('filtered-public', filterPublic && !this.thumbIsPrivate(v.element)); }; } filterByDuration = () => { const { filterDurationFrom: from, filterDurationTo: to, filterDuration } = state; for (const v of this.data.values()) { v.element.classList.toggle('filtered-duration', filterDuration && (v.duration < from || v.duration > to)); }; } runFilters() { if (state.filterPrivate) this.filterPrivate(); if (state.filterPublic) this.filterPublic(); if (state.filterDuration) this.filterByDuration(); if (state.filterPositive) this.filterPositiveTags(); if (state.filterNegative) this.filterNegativeTags(); } createThumbsContainer() { return parseDOM('<div class="thumbs-items"></div>'); } handleLoadedHTML = (htmlPage, mount, useGlobalContainer = true) => { const thumbs = $$('.tumbpu', htmlPage); const container = !useGlobalContainer ? this.createThumbsContainer() : this.container; for (const thumbElement of thumbs) { const url = thumbElement.getAttribute('href'); if (!url || this.data.has(url)) { thumbElement.remove(); continue; } this.data.set(url, { element: thumbElement, duration: timeToSeconds($('.thumb > .duration', thumbElement).textContent), }); const img = $('img', thumbElement); const thumb2 = $('.private', thumbElement); let imgSrc; if (thumb2) { imgSrc = parseCSSUrl(thumb2.style.background); thumb2.style.removeProperty('background'); thumb2.removeAttribute('style'); } else { imgSrc = img.getAttribute('data-original'); img.removeAttribute('data-original'); } img.setAttribute('lazy-loading', imgSrc); img.src = ''; img.classList.add('tracking'); img.removeAttribute('data-cnt'); container.appendChild(thumbElement); this.observables.push(thumbElement); } this.runFilters(container); this.setObservation(); mount.before(container); }; } class PreviewManager { constructor() { this.tick = new Tick(ANIMATION_DELAY); } iteratePreviewImages(src) { return src.replace(/(\d)\.jpg$/, (_, n) => `${circularShift(parseInt(n))}.jpg`); } animatePreview = (e) => { const { target: el, type, src } = e; if (el.tagName !== 'IMG' || !el.classList.contains('tracking')) return; if (type === 'mouseout' || type === 'touchend') { this.tick.stop(); el.src = el.getAttribute('lazy-loading'); } if (type === 'mouseover' || type === 'touchstart') { if (!src) el.src = el.getAttribute('lazy-loading'); this.tick.start(() => { el.src = this.iteratePreviewImages(el.src); }); } }; listen(e) { e.addEventListener('mouseout', this.animatePreview); e.addEventListener('mouseover', this.animatePreview); e.addEventListener("touchstart", this.animatePreview, true); e.addEventListener("touchend", this.animatePreview, true); } } class PaginationPageManager { constructor() { this.pagination = $('.pagination'); this.pagination.style.opacity = 0; unsafeWindow.$('img[alt!="Private"]').off('mouseover'); unsafeWindow.$('img[alt!="Private"]').off('mouseout'); handleLoadedHTML(document.body, this.pagination); previewManager.listen(this.pagination.parentElement); this.offsetLast = this.getOffsetLast(); this.paginationGenerator = this.createNextPageGenerator(); this.generatorDone = false; this.ui = new UI(true); this.setPagIndex = (offset) => this.ui.setPagIndex(offset, this.offsetLast); this.setPagIndex(this.getCurrentOffset()); this.paginationObserver = new Observer((target, observer) => { this.generatorConsumer(); if (!this.generatorDone) { observer.unobserve(target); setTimeout(() => { observer.observe(target); }, SCROLL_RESET_DELAY); } }); this.paginationObserver.observe(this.pagination); } async generatorConsumer() { const { value: { url, offset } = {}, done, } = this.paginationGenerator.next(); this.generatorDone = done; if (!done) { const nextPageHTML = await fetchHtml(url); const prevScrollPos = document.documentElement.scrollTop; handleLoadedHTML(nextPageHTML, this.pagination); this.setPagIndex(offset); window.scrollTo(0, prevScrollPos); } } getCurrentOffset() { return parseInt(window.location.pathname.split(/(\d+\/)$/)[1] || '1'); } getOffsetLast() { return parseInt( $('.pagination-next').previousElementSibling.firstElementChild.textContent); } createNextPageGenerator() { let { origin, pathname, search } = window.location; let offset; [pathname, offset = '1'] = pathname.split(/(\d+\/)$/); offset = parseInt(offset); pathname = pathname === '/' ? '/latest-updates/' : pathname; const offsetLast = this.getOffsetLast(); function* nextPageGenerator() { for (let c = offset + 1; c <= offsetLast; c++) { const url = `${origin}${pathname}${c}/${search}`; console.log(url); yield { url, offset: c }; } } return nextPageGenerator(); } } class Router { constructor() { this.route(); } route() { const { pathname, href } = window.location; if ($('.pagination-next')) { this.handlePaginationPage(); } else if (/\/members\/\d+\/$/.test(pathname)) { this.handleMemberPage(); } else if (/\/tag\//.test(href) || /\/?q=.*/.test(href)) { this.handlePageWithVideosButNoPagination(); } } handlePageWithVideosButNoPagination() { const vid = $('.tumbpu'); if (!vid) return; handleLoadedHTML(document.body, vid.parentElement); previewManager.listen(vid.parentElement); const ui = new UI(false); } handlePaginationPage() { this.paginationManager = new PaginationPageManager(); } handleMemberPage() { const privates = $('#list_videos_private_videos_items'); if (privates) { const mistakes = document.querySelectorAll('#list_videos_private_videos_items ~ div'); for (const m of mistakes) { if (m.id === 'list_videos_favourite_videos') break; privates.appendChild(m); } handleLoadedHTML(privates, privates, false); } const favorites = $('#list_videos_favourite_videos'); if (favorites) { const mountTo = favorites.firstElementChild.nextElementSibling; handleLoadedHTML(favorites, mountTo, false); } if (privates || favorites) { previewManager.listen((privates || favorites).parentElement); const ui = new UI(false); } } } class UI { templateHTML = (haspag) => ` <div id="tapermonkey-app"> <div class="subbox"><span id='uitoggle'>🗙</a></div> <div class="subbox"> <input type="checkbox" id="filterNegative" ${state.filterNegative ? 'checked' : ''}/> <label for="filterNegative">exclude</label> <textarea id="filterNegativeText" placeholder="tag1, tag2,..">${state.filterNegativeTags.join(',')}</textarea> </div> <div class="subbox"> <input type="checkbox" id="filterPositive" ${state.filterPositive ? 'checked' : ''}/> <label for="filterPositive">include</label> <textarea id="filterPositiveText" placeholder="tag1, tag2,..">${state.filterPositiveTags.join(',')}</textarea> </div> <div class="subbox"> <input type="checkbox" id="filterPrivate" ${state.filterPrivate ? 'checked' : ''}/> <label for="filterPrivate">private</label> <input type="checkbox" id="filterPublic" ${state.filterPublic ? 'checked' : ''}/> <label for="filterPublic">public</label> ${haspag ? '<span id="pagIndex">0/0</span>' : ''} </div> <div class="subbox"> <input type="checkbox" id="filterl" ${state.filterDuration ? 'checked' : ''}/> <label for="filterl">duration</label> <label>from</label> <input type="number" step="10" min="0" max="72000" id="minL" value=${state.filterDurationFrom} /> <label>to</label> <input type="number" step="10" min="0" max="72000" id="maxL" value=${state.filterDurationTo} /> </div> </div>`; constructor(haspag = true) { document.body.appendChild(parseDOM(this.templateHTML(haspag))); this.tapermonkeyAppTemplate = $('#tapermonkey-app'); this.control(); this.uiToggleKnob = $('#uitoggle', this.tapermonkeyAppTemplate); this.uiToggleKnob.addEventListener('click', this.toggleUI); } toggleUI(){ state.uiActive = !state.uiActive; $$('.subbox').forEach((s,i) => { if (i === 0) { s.firstElementChild.innerText = state.uiActive ? '🗙' : '⛶'; } if (i > 0) { s.style.display = state.uiActive ? 'flex' : 'none'; } }); } setPagIndex(index, total) { $('#pagIndex').innerText = `${index}/${total}`; } stringToTags(s) { return s.split(",").map(s => s.trim()).filter(s => s.length > 0); } control() { this.tapermonkeyAppTemplate.addEventListener('input', (e) => { const { id, value } = e.target; if (id === 'filterNegativeText') { state.filterNegativeTags = this.stringToTags(value); filterNegativeTags() } if (id === 'filterPositiveText') { state.filterPositiveTags = this.stringToTags(value); filterPositiveTags(); } }); this.tapermonkeyAppTemplate.addEventListener('click', (e) => { const { id, checked, value } = e.target; if (id === 'filterPublic') { state.filterPublic = checked; filterPublic(); } if (id === 'filterPrivate') { state.filterPrivate = checked; filterPrivate(); } if (id === 'filterNegative') { state.filterNegative = checked; filterNegativeTags() } if (id === 'filterPositive') { state.filterPositive = checked; filterPositiveTags(); } if (id === 'filterl') { state.filterDuration = checked; filterByDuration(); } if (id === 'minL') { state.filterDurationFrom = parseIntegerOr(value, state.filterDurationFrom); filterByDuration(); } if (id === 'maxL') { state.filterDurationTo = parseIntegerOr(value, state.filterDurationTo); filterByDuration(); } }); } } const SCROLL_RESET_DELAY = 500; const ANIMATION_DELAY = 750; const state = new ReactiveLocalStorage({ filterDurationFrom: 0, filterDurationTo: 600, filterDuration: false, filterPrivate: false, filterPublic: false, infiniteScrollTriggered: false, filterNegativeTags: [], filterNegative: false, filterPositiveTags: [], filterPositive: false, uiActive: true }); const { filterPrivate, filterPublic, filterByDuration, filterPositiveTags, filterNegativeTags, handleLoadedHTML } = new DomManager(); const previewManager = new PreviewManager(); const router = new Router(); const tampermonkeyCSS = ` #tapermonkey-app { background: #16181b; padding: 6px; position: fixed; z-index: 9999; bottom: 10px; right: 10px; width: max-content; } #uitoggle { flex: 1; text-align: right; } @media only screen and (max-width: 600px) { #tapermonkey-app { bottom: 0px; right: 0px; width: 98vw; padding: 1vw; } } #tapermonkey-app .subbox { background: #000; padding: 4px; margin: 6px; user-select: none; display: flex; flex-wrap: wrap; } #tapermonkey-app .subbox:first-child { background: none; } #tapermonkey-app .subbox input[type=number] { width: 5rem; background: #26282b; } #tapermonkey-app .subbox input[type=checkbox] { margin-left: 5px; } #tapermonkey-app .subbox label { margin: 0 10px 0 0; } #pagIndex { text-align: end; margin-right: 5px; flex: 1; } #tapermonkey-app textarea { flex: 1; height: 2rem; padding: 6px; background: #26282b; } #tapermonkey-app .subbox * { padding-left: 8px; width: auto; font-family: monospace; font-size: 0.85rem; align-self: center; } .filtered-private, .filtered-duration, .filtered-public, .filtered-tag-pos, .filtered-tag-neg { display: none !important; } `; GM_addStyle(tampermonkeyCSS); })();