diff --git a/.gitignore b/.gitignore
index c7fb7a4..aeb8228 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
/node_modules
/dist
-.env
\ No newline at end of file
+.env
+/public/banner.png
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index d680a89..6175e6b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "waifu-board",
- "version": "v1.2.0",
+ "version": "v1.6.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "waifu-board",
- "version": "v1.2.0",
+ "version": "v1.6.2",
"license": "ISC",
"dependencies": {
"@ryuziii/discord-rpc": "^1.0.1-rc.1",
diff --git a/src/ipc/db-handlers.js b/src/ipc/db-handlers.js
index 637b87d..eec160f 100644
--- a/src/ipc/db-handlers.js
+++ b/src/ipc/db-handlers.js
@@ -19,7 +19,7 @@ module.exports = function (db) {
'INSERT INTO favorites (id, title, image_url, thumbnail_url, tags) VALUES (?, ?, ?, ?, ?)';
db.run(
stmt,
- [fav.id, fav.title, fav.imageUrl, fav.thumbnailUrl, fav.tags],
+ [fav.id, fav.title, fav.image_url, fav.thumbnail_url, fav.tags],
function (err) {
if (err) {
if (err.code.includes('SQLITE_CONSTRAINT')) {
diff --git a/src/marketplace.js b/src/marketplace.js
index fe61112..67ec2ac 100644
--- a/src/marketplace.js
+++ b/src/marketplace.js
@@ -1,24 +1,25 @@
+const GITEA_INSTANCE = 'https://git.waifuboard.app';
const REPO_OWNER = 'ItsSkaiya';
const REPO_NAME = 'WaifuBoard-Extensions';
-const API_URL = `https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}/contents/extensions?ref=main`;
+
+let DETECTED_BRANCH = 'main';
+
+const API_URL_BASE = `${GITEA_INSTANCE}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/contents`;
document.addEventListener('DOMContentLoaded', async () => {
const browseGrid = document.getElementById('marketplace-grid');
const installedGrid = document.getElementById('installed-grid');
-
const statTotal = document.getElementById('stat-total');
const statInstalled = document.getElementById('stat-installed');
-
const tabBrowse = document.getElementById('tab-browse');
const tabInstalled = document.getElementById('tab-installed');
const viewBrowse = document.getElementById('view-browse');
const viewInstalled = document.getElementById('view-installed');
+ const messageBar = document.getElementById('message-bar');
let allRemoteExtensions = [];
let installedExtensionsList = [];
- const messageBar = document.getElementById('message-bar');
-
function showMessage(msg, type = 'success') {
if (!messageBar) return;
messageBar.textContent = msg;
@@ -34,56 +35,17 @@ document.addEventListener('DOMContentLoaded', async () => {
function showRestartToast() {
if (document.getElementById('restart-toast')) return;
-
const toast = document.createElement('div');
toast.id = 'restart-toast';
- toast.style.cssText = `
- position: fixed;
- bottom: 20px;
- right: 20px;
- background: #1f2937;
- border: 1px solid var(--accent);
- border-radius: 12px;
- padding: 1rem 1.5rem;
- display: flex;
- align-items: center;
- gap: 1rem;
- box-shadow: 0 10px 30px rgba(0,0,0,0.5);
- z-index: 2000;
- animation: slideUp 0.3s ease-out;
- `;
-
- toast.innerHTML = `
-
- Restart Required
- Changes will apply after restart.
-
-
- `;
-
+ toast.style.cssText = `position: fixed; bottom: 20px; right: 20px; background: #1f2937; border: 1px solid var(--accent); border-radius: 12px; padding: 1rem 1.5rem; display: flex; align-items: center; gap: 1rem; box-shadow: 0 10px 30px rgba(0,0,0,0.5); z-index: 2000; animation: slideUp 0.3s ease-out;`;
+ toast.innerHTML = `Restart RequiredChanges will apply after restart.
`;
document.body.appendChild(toast);
-
const btn = document.getElementById('btn-restart-now');
btn.onmouseover = () => btn.style.opacity = '0.9';
btn.onmouseout = () => btn.style.opacity = '1';
-
btn.onclick = () => {
- if (window.api && typeof window.api.restartApp === 'function') {
- window.api.restartApp();
- } else {
- console.error("Restart API not found in window.api");
- alert("Please close and reopen the application manually.");
- }
+ if (window.api && typeof window.api.restartApp === 'function') window.api.restartApp();
+ else alert("Please close and reopen the application manually.");
};
}
@@ -117,44 +79,67 @@ document.addEventListener('DOMContentLoaded', async () => {
async function fetchRemoteExtensions() {
try {
- const res = await fetch(API_URL);
- if (!res.ok) throw new Error(`GitHub API Error: ${res.status}`);
+ let res = await fetch(API_URL_BASE);
+
+ if (res.status === 404) {
+ console.warn("Default branch failed, trying 'main'...");
+ DETECTED_BRANCH = 'main';
+ res = await fetch(`${API_URL_BASE}?ref=main`);
+ }
+ if (res.status === 404) {
+ console.warn("Main branch failed, trying 'master'...");
+ DETECTED_BRANCH = 'master';
+ res = await fetch(`${API_URL_BASE}?ref=master`);
+ }
+
+ if (!res.ok) throw new Error(`Gitea API Error: ${res.status}`);
+
const data = await res.json();
- allRemoteExtensions = data.filter(item => item.name.endsWith('.js'));
- renderBrowseGrid(allRemoteExtensions);
- updateStats();
+ if (Array.isArray(data)) {
+ allRemoteExtensions = data.filter(item => item.name.endsWith('.js'));
+ renderBrowseGrid(allRemoteExtensions);
+ updateStats();
+ } else {
+ throw new Error("Invalid API response format");
+ }
+
} catch (e) {
+ console.error(e);
if(browseGrid) browseGrid.innerHTML = `Failed to load marketplace.
${e.message}
`;
}
}
+ function getRawUrl(filename) {
+ return `${GITEA_INSTANCE}/${REPO_OWNER}/${REPO_NAME}/raw/branch/main/${filename}`;
+ }
+
async function getExtensionDetails(url) {
try {
const res = await fetch(url);
+ if (!res.ok) throw new Error(`Fetch failed: ${res.status}`);
const text = await res.text();
- const baseUrlMatch = text.match(/baseUrl\s*=\s*["']([^"']+)["']/);
- let baseUrl = baseUrlMatch ? baseUrlMatch[1] : null;
+ const regex = /(?:this\.|const\s+|let\s+|var\s+)?baseUrl\s*=\s*(["'`])(.*?)\1/i;
+ const match = text.match(regex);
- if (baseUrl) {
+ let finalHostname = null;
+ if (match && match[2]) {
+ let rawUrl = match[2].trim();
+ if (!rawUrl.startsWith('http')) rawUrl = 'https://' + rawUrl;
try {
- const urlObj = new URL(baseUrl);
- baseUrl = urlObj.hostname;
- } catch(e) {
- console.warn("Invalid URL in extension:", baseUrl);
- }
+ const urlObj = new URL(rawUrl);
+ finalHostname = urlObj.hostname;
+ } catch(e) {}
}
const classMatch = text.match(/class\s+(\w+)/);
const name = classMatch ? classMatch[1] : null;
let type = 'Image';
- if (text.includes('type = "book-board"') || text.includes("type = 'book-board'")) {
- type = 'Book';
- }
+ if (text.includes('type = "book-board"') || text.includes("type = 'book-board'")) type = 'Book';
- return { baseUrl, name, type };
+ return { baseUrl: finalHostname, name, type };
} catch (e) {
return { baseUrl: null, name: null, type: 'Unknown' };
}
@@ -163,29 +148,32 @@ document.addEventListener('DOMContentLoaded', async () => {
async function createCard(item, isLocalOnly = false) {
const isInstalled = installedExtensionsList.includes(item.name);
- const downloadUrl = item.download_url || null;
- let sizeKB = item.size ? (item.size / 1024).toFixed(1) + ' KB' : 'Local';
+ const downloadUrl = getRawUrl(item.name);
+ let sizeKB = item.size ? (item.size / 1024).toFixed(1) + ' KB' : 'Local';
let iconUrl = '';
let displayName = item.name.replace('.js', '');
let typeLabel = 'Extension';
let typeClass = 'type-image';
+
+ const fallbackIcon = `data:image/svg+xml,`;
- if (downloadUrl) {
+ if (!isLocalOnly) {
const details = await getExtensionDetails(downloadUrl);
displayName = details.name || displayName;
- const domain = details.baseUrl || 'github.com';
- iconUrl = `https://www.google.com/s2/favicons?domain=${domain}&sz=128`;
+ if (details.baseUrl) {
+ iconUrl = `https://www.google.com/s2/favicons?domain=${details.baseUrl}&sz=128`;
+ } else {
+ iconUrl = `https://ui-avatars.com/api/?name=${displayName}&background=1f2937&color=fff&length=1`;
+ }
+
if (details.type === 'Book') {
typeLabel = 'Book Board';
typeClass = 'type-book';
- } else {
- typeLabel = 'Image Board';
- typeClass = 'type-image';
}
} else {
- iconUrl = `https://ui-avatars.com/api/?name=${displayName}&background=18181b&color=fff&length=1`;
+ iconUrl = `https://ui-avatars.com/api/?name=${displayName}&background=1f2937&color=fff&length=1`;
}
const card = document.createElement('div');
@@ -199,16 +187,14 @@ document.addEventListener('DOMContentLoaded', async () => {
const renderInner = (installed) => `
-
${displayName}
${item.name}
-