您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Makes Chub.ai card titles into links (copying existing link), styles them, and adds pagination.
// ==UserScript== // @name Improved Chub.ai (Card Title Links, Styling, and Pagination) // @namespace http://tampermonkey.net/ // @version 1.8 // @description Makes Chub.ai card titles into links (copying existing link), styles them, and adds pagination. // @author Marcal91 // @match https://chub.ai/* // @grant none // @license MIT // ==/UserScript== (function() { 'use strict'; function createTitleLink(cardElement) { // 1. Find the title element (innermost span holding the text). const titleSpan = cardElement.querySelector('.ant-card-head-title .ant-row span span'); if (!titleSpan) { // If the specific inner span is not found, it might have already been replaced by our link. // Check if the link already exists within the title container. const titleContainer = cardElement.querySelector('.ant-card-head-title .ant-row > span'); if (titleContainer && titleContainer.querySelector('a.chub-card-title-link')) { return; // Already processed } if(!titleSpan) return; // If still not found, bail. } // 2. Get the existing card link (the parent <a> of the whole card). const cardLink = cardElement.closest('a'); if (!cardLink) { console.error("Chub.ai: Could not find parent card link for title:", cardElement); return; } // 3. Get the URL from the existing card link. const cardURL = cardLink.href; // 4. Create the new <a> element for the title. const linkElement = document.createElement('a'); linkElement.href = cardURL; linkElement.textContent = titleSpan.textContent.trim(); linkElement.target = '_blank'; linkElement.rel = 'noopener noreferrer'; linkElement.classList.add('chub-card-title-link'); // 5. Replace the title span with the link. if (titleSpan.parentNode) { titleSpan.parentNode.replaceChild(linkElement, titleSpan); } } function addPagination() { const buttonContainer = document.querySelector('.flex.justify-between.mt-4'); if (!buttonContainer) return; const currentURL = new URL(window.location.href); let currentPage = parseInt(currentURL.searchParams.get('page') || '1', 10); const paginationContainer = document.createElement('div'); paginationContainer.classList.add('pagination-container'); function createPageButton(pageNumber, text) { const pageButton = document.createElement('button'); pageButton.type = 'button'; pageButton.textContent = text || pageNumber.toString(); pageButton.classList.add('ant-btn', 'css-s6hibu', 'ant-btn-default', 'pagination-link'); if (!text && pageNumber === currentPage) { // Only style numeric current page pageButton.classList.add('current-page'); } pageButton.addEventListener('click', (event) => { event.preventDefault(); const url = new URL(window.location.href); url.searchParams.set('page', pageNumber.toString()); window.location.href = url.toString(); }); return pageButton; } const firstPageButton = createPageButton(1, 'First'); paginationContainer.appendChild(firstPageButton); let startPage = Math.max(1, currentPage - 2); let endPage = startPage + 4; const assumedTotalPages = 1000; if (endPage > assumedTotalPages) { endPage = assumedTotalPages; startPage = Math.max(1, endPage - 4); } startPage = Math.max(1, startPage); for (let i = startPage; i <= endPage; i++) { const pageButton = createPageButton(i); paginationContainer.appendChild(pageButton); } const randomButton = document.createElement('button'); randomButton.type = 'button'; randomButton.textContent = 'Random'; randomButton.classList.add('ant-btn', 'css-s6hibu', 'ant-btn-default', 'pagination-link'); randomButton.addEventListener('click', (event) => { event.preventDefault(); const randomPage = Math.floor(Math.random() * assumedTotalPages) + 1; const url = new URL(window.location.href); url.searchParams.set('page', randomPage.toString()); window.location.href = url.toString(); }); paginationContainer.appendChild(randomButton); const existingPagination = buttonContainer.querySelector('.pagination-container'); if (existingPagination) { buttonContainer.replaceChild(paginationContainer, existingPagination); } else { buttonContainer.appendChild(paginationContainer); } } const observer = new MutationObserver((mutations) => { let cardsChanged = false; let buttonsChanged = false; mutations.forEach((mutation) => { mutation.addedNodes.forEach((node) => { if (node.nodeType === Node.ELEMENT_NODE) { if (node.matches('.ant-card.char-card-class') || node.querySelector('.ant-card.char-card-class')) { cardsChanged = true; } if (node.matches('.flex.justify-between.mt-4') || node.querySelector('.flex.justify-between.mt-4')) { buttonsChanged = true; } } }); }); if (cardsChanged) { document.querySelectorAll('.ant-card.char-card-class').forEach(createTitleLink); } if (buttonsChanged) { addPagination(); } }); observer.observe(document.body, { childList: true, subtree: true }); let lastUrl = window.location.href; const urlObserver = new MutationObserver(() => { if (window.location.href !== lastUrl) { lastUrl = window.location.href; setTimeout(addPagination, 100); } }); urlObserver.observe(document, {childList: true, subtree: true}); document.querySelectorAll('.ant-card.char-card-class').forEach(createTitleLink); addPagination(); const styleElement = document.createElement('style'); styleElement.textContent = ` .chub-card-title-link { color: white; /* Unvisited */ } .chub-card-title-link:visited { color: yellow; /* Visited */ } .pagination-container { display: flex; align-items: center; gap: 0.5rem; } .pagination-container .pagination-link { /* Applied to all buttons in pagination */ background-color: #141414 !important; border: 1px solid #424242 !important; color: rgba(242,228,214,0.85) !important; min-width: 32px; height: 32px; padding: 0px 15px !important; line-height: 1.5 !important; display: inline-flex; align-items: center; justify-content: center; border-radius: 6px !important; } .pagination-container .pagination-link:not(.current-page):hover { background-color: #1f1f1f !important; border-color: #5852a5 !important; color: #5852a5 !important; } .pagination-container .pagination-link.current-page { background-color: #2e2b74 !important; border-color: #5852a5 !important; color: white !important; font-weight: bold; } /* --- Styles for bigger tag scrollbar --- */ div.custom-scroll:hover::-webkit-scrollbar { height: 8px !important; /* Increase height from site's 2px to 8px */ /* The site CSS already positions it with 'position: absolute; bottom: 0;' which is fine. */ } div.custom-scroll:hover::-webkit-scrollbar-thumb { background-color: #888 !important; /* A more visible scrollbar thumb color */ border-radius: 4px !important; /* Rounded corners for the thumb */ } /* Optional: Add a subtle track background for the scrollbar area when hovering the tag container */ div.custom-scroll:hover::-webkit-scrollbar-track { background-color: rgba(50, 50, 50, 0.2) !important; /* Light grey track background */ border-radius: 4px !important; } `; document.head.appendChild(styleElement); })();