xxx777xxxASD
commited on
Upload 4 files
Browse files- BetterChub/background.js +22 -0
- BetterChub/content.js +199 -0
- BetterChub/manifest.json +18 -0
- BetterChub/styles.css +38 -0
BetterChub/background.js
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
console.log('Background script loaded');
|
2 |
+
|
3 |
+
let apiUrl = '';
|
4 |
+
|
5 |
+
chrome.webRequest.onBeforeSendHeaders.addListener(
|
6 |
+
(details) => {
|
7 |
+
console.log('Intercepted request:', details.url);
|
8 |
+
const apiKeyHeader = details.requestHeaders.find(h => h.name === 'CH-API-KEY');
|
9 |
+
if (apiKeyHeader) {
|
10 |
+
console.log('API Key found:', apiKeyHeader.value);
|
11 |
+
chrome.storage.local.set({ apiKey: apiKeyHeader.value });
|
12 |
+
}
|
13 |
+
if (details.url.includes('api.chub.ai/search')) {
|
14 |
+
console.log('API URL found:', details.url);
|
15 |
+
apiUrl = details.url;
|
16 |
+
chrome.storage.local.set({ apiUrl: details.url });
|
17 |
+
}
|
18 |
+
return { requestHeaders: details.requestHeaders };
|
19 |
+
},
|
20 |
+
{ urls: ["https://api.chub.ai/*"] },
|
21 |
+
["requestHeaders"]
|
22 |
+
);
|
BetterChub/content.js
ADDED
@@ -0,0 +1,199 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
console.log('Content script loaded');
|
2 |
+
|
3 |
+
let currentPage = 1;
|
4 |
+
let totalPages = 1;
|
5 |
+
let apiKey = '';
|
6 |
+
let apiUrl = '';
|
7 |
+
let lastUrl = '';
|
8 |
+
|
9 |
+
function createNavigationHTML() {
|
10 |
+
return `
|
11 |
+
<div class="nav-row">
|
12 |
+
<button id="firstPage" class="ant-btn css-s6hibu ant-btn-default"><span>First</span></button>
|
13 |
+
<button id="prevPage" class="ant-btn css-s6hibu ant-btn-default"><span>Previous</span></button>
|
14 |
+
<span id="pageInfo">Loading...</span>
|
15 |
+
<button id="nextPage" class="ant-btn css-s6hibu ant-btn-default"><span>Next</span></button>
|
16 |
+
<button id="lastPage" class="ant-btn css-s6hibu ant-btn-default"><span>Last</span></button>
|
17 |
+
</div>
|
18 |
+
<div class="nav-row">
|
19 |
+
<div class="input-group">
|
20 |
+
<input type="number" id="pageInput" min="1" max="${totalPages}" value="${currentPage}">
|
21 |
+
<button id="goToPage" class="ant-btn css-s6hibu ant-btn-default"><span>Go</span></button>
|
22 |
+
</div>
|
23 |
+
</div>
|
24 |
+
`;
|
25 |
+
}
|
26 |
+
|
27 |
+
function updateNavigation() {
|
28 |
+
console.log('Updating navigation');
|
29 |
+
const navContainers = document.querySelectorAll('.navigation-container');
|
30 |
+
if (navContainers.length === 0) {
|
31 |
+
console.error('Navigation containers not found');
|
32 |
+
return;
|
33 |
+
}
|
34 |
+
|
35 |
+
navContainers.forEach(container => {
|
36 |
+
container.innerHTML = createNavigationHTML();
|
37 |
+
|
38 |
+
container.querySelector('#firstPage').addEventListener('click', () => changePage(1));
|
39 |
+
container.querySelector('#prevPage').addEventListener('click', () => changePage(currentPage - 1));
|
40 |
+
container.querySelector('#nextPage').addEventListener('click', () => changePage(currentPage + 1));
|
41 |
+
container.querySelector('#lastPage').addEventListener('click', () => changePage(totalPages));
|
42 |
+
container.querySelector('#goToPage').addEventListener('click', () => {
|
43 |
+
const pageInput = container.querySelector('#pageInput');
|
44 |
+
changePage(parseInt(pageInput.value));
|
45 |
+
});
|
46 |
+
});
|
47 |
+
|
48 |
+
updatePageInfo();
|
49 |
+
console.log('Navigation updated successfully');
|
50 |
+
}
|
51 |
+
|
52 |
+
function updatePageInfo() {
|
53 |
+
const pageInfoElements = document.querySelectorAll('#pageInfo');
|
54 |
+
pageInfoElements.forEach(element => {
|
55 |
+
element.textContent = `Page ${currentPage} of ${totalPages}`;
|
56 |
+
});
|
57 |
+
}
|
58 |
+
|
59 |
+
function changePage(newPage) {
|
60 |
+
console.log(`Changing page to ${newPage}`);
|
61 |
+
if (newPage < 1 || newPage > totalPages) {
|
62 |
+
console.warn(`Invalid page number: ${newPage}`);
|
63 |
+
return;
|
64 |
+
}
|
65 |
+
currentPage = newPage;
|
66 |
+
updateNavigation();
|
67 |
+
// Update URL and trigger page reload
|
68 |
+
const url = new URL(window.location.href);
|
69 |
+
url.searchParams.set('page', currentPage);
|
70 |
+
console.log(`Navigating to: ${url.toString()}`);
|
71 |
+
window.location.href = url.toString();
|
72 |
+
}
|
73 |
+
|
74 |
+
async function findTotalPages() {
|
75 |
+
console.log('Finding total pages');
|
76 |
+
let left = 1;
|
77 |
+
let right = 1000; // Assuming a reasonable upper limit
|
78 |
+
|
79 |
+
while (left <= right) {
|
80 |
+
const mid = Math.floor((left + right) / 2);
|
81 |
+
console.log(`Checking page ${mid}`);
|
82 |
+
const hasContent = await checkPageContent(mid);
|
83 |
+
|
84 |
+
if (hasContent) {
|
85 |
+
left = mid + 1;
|
86 |
+
} else {
|
87 |
+
right = mid - 1;
|
88 |
+
}
|
89 |
+
}
|
90 |
+
|
91 |
+
console.log(`Total pages found: ${right}`);
|
92 |
+
return right; // This will be the last page with content
|
93 |
+
}
|
94 |
+
|
95 |
+
async function checkPageContent(page) {
|
96 |
+
const url = new URL(apiUrl);
|
97 |
+
url.searchParams.set('page', page);
|
98 |
+
|
99 |
+
console.log(`Checking content for page ${page}`);
|
100 |
+
try {
|
101 |
+
const response = await fetch(url.toString(), {
|
102 |
+
headers: { 'CH-API-KEY': apiKey }
|
103 |
+
});
|
104 |
+
const data = await response.json();
|
105 |
+
console.log(`Page ${page} content:`, data);
|
106 |
+
return data.data.nodes.length > 0;
|
107 |
+
} catch (error) {
|
108 |
+
console.error(`Error checking page ${page}:`, error);
|
109 |
+
return false;
|
110 |
+
}
|
111 |
+
}
|
112 |
+
|
113 |
+
async function initializeNavigation() {
|
114 |
+
console.log('Initializing navigation');
|
115 |
+
currentPage = parseInt(new URL(window.location.href).searchParams.get('page') || '1');
|
116 |
+
console.log(`Current page: ${currentPage}`);
|
117 |
+
totalPages = await findTotalPages();
|
118 |
+
updateNavigation();
|
119 |
+
}
|
120 |
+
|
121 |
+
function waitForApiInfo() {
|
122 |
+
chrome.storage.local.get(['apiKey', 'apiUrl'], async (result) => {
|
123 |
+
console.log('Retrieved from storage:', result);
|
124 |
+
if (result.apiKey && result.apiUrl) {
|
125 |
+
apiKey = result.apiKey;
|
126 |
+
apiUrl = result.apiUrl;
|
127 |
+
console.log('API Key and URL found, initializing navigation');
|
128 |
+
await initializeNavigation();
|
129 |
+
} else {
|
130 |
+
console.log('API Key or URL missing, retrying in 1 second');
|
131 |
+
setTimeout(waitForApiInfo, 1000);
|
132 |
+
}
|
133 |
+
});
|
134 |
+
}
|
135 |
+
|
136 |
+
function addNavigationContainers() {
|
137 |
+
const containerDiv = document.querySelector('.mt-2');
|
138 |
+
if (containerDiv) {
|
139 |
+
// Remove any existing navigation containers
|
140 |
+
containerDiv.querySelectorAll('.navigation-container').forEach(el => el.remove());
|
141 |
+
|
142 |
+
// Add top navigation
|
143 |
+
const topNav = document.createElement('div');
|
144 |
+
topNav.className = 'navigation-container top-nav';
|
145 |
+
topNav.innerHTML = createNavigationHTML();
|
146 |
+
containerDiv.insertBefore(topNav, containerDiv.firstChild);
|
147 |
+
|
148 |
+
// Replace bottom navigation
|
149 |
+
const bottomNav = containerDiv.querySelector('.flex.justify-between.mt-4');
|
150 |
+
if (bottomNav) {
|
151 |
+
bottomNav.className = 'navigation-container bottom-nav';
|
152 |
+
bottomNav.innerHTML = createNavigationHTML();
|
153 |
+
} else {
|
154 |
+
const newBottomNav = document.createElement('div');
|
155 |
+
newBottomNav.className = 'navigation-container bottom-nav';
|
156 |
+
newBottomNav.innerHTML = createNavigationHTML();
|
157 |
+
containerDiv.appendChild(newBottomNav);
|
158 |
+
}
|
159 |
+
|
160 |
+
updateNavigation();
|
161 |
+
} else {
|
162 |
+
console.error('Container div not found');
|
163 |
+
}
|
164 |
+
}
|
165 |
+
|
166 |
+
function checkUrlChange() {
|
167 |
+
const currentUrl = window.location.href;
|
168 |
+
if (currentUrl !== lastUrl) {
|
169 |
+
console.log('URL changed, reinitializing navigation');
|
170 |
+
lastUrl = currentUrl;
|
171 |
+
waitForApiInfo();
|
172 |
+
}
|
173 |
+
}
|
174 |
+
|
175 |
+
// Start the process once the page is fully loaded
|
176 |
+
window.addEventListener('load', () => {
|
177 |
+
console.log('Page fully loaded, starting navigation enhancement');
|
178 |
+
lastUrl = window.location.href;
|
179 |
+
addNavigationContainers();
|
180 |
+
waitForApiInfo();
|
181 |
+
});
|
182 |
+
|
183 |
+
// Check for URL changes
|
184 |
+
setInterval(checkUrlChange, 1000);
|
185 |
+
|
186 |
+
// Observe DOM changes to add navigation containers when the character list is loaded
|
187 |
+
const observer = new MutationObserver((mutations) => {
|
188 |
+
for (const mutation of mutations) {
|
189 |
+
if (mutation.type === 'childList' && document.querySelector('.mt-2')) {
|
190 |
+
console.log('Character list container found, adding navigation containers');
|
191 |
+
addNavigationContainers();
|
192 |
+
observer.disconnect(); // Stop observing once we've added the navigation containers
|
193 |
+
}
|
194 |
+
}
|
195 |
+
});
|
196 |
+
|
197 |
+
observer.observe(document.body, { childList: true, subtree: true });
|
198 |
+
|
199 |
+
console.log('Content script finished loading');
|
BetterChub/manifest.json
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"manifest_version": 3,
|
3 |
+
"name": "BetterChub",
|
4 |
+
"version": "1.0",
|
5 |
+
"description": "Navigation does *tap* *tap*",
|
6 |
+
"permissions": ["webRequest", "storage"],
|
7 |
+
"host_permissions": ["https://chub.ai/*", "https://api.chub.ai/*"],
|
8 |
+
"background": {
|
9 |
+
"service_worker": "background.js"
|
10 |
+
},
|
11 |
+
"content_scripts": [
|
12 |
+
{
|
13 |
+
"matches": ["https://chub.ai/*"],
|
14 |
+
"js": ["content.js"],
|
15 |
+
"css": ["styles.css"]
|
16 |
+
}
|
17 |
+
]
|
18 |
+
}
|
BetterChub/styles.css
ADDED
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.navigation-container {
|
2 |
+
display: flex;
|
3 |
+
flex-direction: column;
|
4 |
+
align-items: center;
|
5 |
+
margin-top: 1rem;
|
6 |
+
margin-bottom: 1rem;
|
7 |
+
}
|
8 |
+
|
9 |
+
.nav-row {
|
10 |
+
display: flex;
|
11 |
+
justify-content: center;
|
12 |
+
align-items: center;
|
13 |
+
margin: 0.5rem 0;
|
14 |
+
}
|
15 |
+
|
16 |
+
#pageInfo {
|
17 |
+
margin: 0 1rem;
|
18 |
+
font-weight: bold;
|
19 |
+
}
|
20 |
+
|
21 |
+
.input-group {
|
22 |
+
display: flex;
|
23 |
+
justify-content: center;
|
24 |
+
align-items: center;
|
25 |
+
}
|
26 |
+
|
27 |
+
#pageInput {
|
28 |
+
width: 50px;
|
29 |
+
margin-right: 0.5rem;
|
30 |
+
}
|
31 |
+
|
32 |
+
.top-nav {
|
33 |
+
margin-bottom: 1rem;
|
34 |
+
}
|
35 |
+
|
36 |
+
.bottom-nav {
|
37 |
+
margin-top: 1rem;
|
38 |
+
}
|