diff --git a/extensions/mal-firefox/README.md b/extensions/mal-firefox/README.md deleted file mode 100644 index bdc3af3..0000000 --- a/extensions/mal-firefox/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# MAL Firefox Extension (dev) - -## Load in Firefox - -1. Open `about:debugging#/runtime/this-firefox` -2. Click **Load Temporary Add-on…** -3. Select `extensions/mal-firefox/manifest.json` - -## Usage - -- Click the toolbar icon to open the popup and log in. -- After login, select text on any page → right click → **MyAnimeList** → **Add to Watchlist** → pick a status. diff --git a/extensions/mal-firefox/background.js b/extensions/mal-firefox/background.js deleted file mode 100644 index 2e92638..0000000 --- a/extensions/mal-firefox/background.js +++ /dev/null @@ -1,103 +0,0 @@ -const MENU_ROOT_ID = 'mal-root'; -const MENU_WATCHLIST_ID = 'mal-watchlist'; -const MENU_STATUS_PREFIX = 'mal-status:'; -const STATUSES = [ - { value: 'watching', label: 'Watching' }, - { value: 'completed', label: 'Completed' }, - { value: 'on_hold', label: 'On Hold' }, - { value: 'dropped', label: 'Dropped' }, - { value: 'plan_to_watch', label: 'Plan to Watch' }, -]; - -async function getSettings() { - const { authToken, apiBaseUrl } = await browser.storage.local.get(['authToken', 'apiBaseUrl']); - return { - authToken: authToken || '', - apiBaseUrl: apiBaseUrl || 'https://mal.mkelvers.tech', - }; -} - -async function apiFetch(path, init = {}) { - const { authToken, apiBaseUrl } = await getSettings(); - const url = apiBaseUrl.replace(/\/+$/, '') + path; - const headers = new Headers(init.headers || {}); - if (authToken) headers.set('Authorization', `Bearer ${authToken}`); - const res = await fetch(url, { ...init, headers }); - if (!res.ok) { - const msg = await res.text().catch(() => ''); - throw new Error(msg || `HTTP ${res.status}`); - } - return res; -} - -async function ensureContextMenu() { - const { authToken } = await getSettings(); - await browser.contextMenus.removeAll(); - if (!authToken) return; - - browser.contextMenus.create({ - id: MENU_ROOT_ID, - title: 'MyAnimeList', - contexts: ['selection'], - }); - - browser.contextMenus.create({ - id: MENU_WATCHLIST_ID, - parentId: MENU_ROOT_ID, - title: 'Add to Watchlist', - contexts: ['selection'], - }); - - for (const s of STATUSES) { - browser.contextMenus.create({ - id: MENU_STATUS_PREFIX + s.value, - parentId: MENU_WATCHLIST_ID, - title: s.label, - contexts: ['selection'], - }); - } -} - -browser.runtime.onInstalled.addListener(() => { - ensureContextMenu(); -}); - -browser.runtime.onStartup.addListener(() => { - ensureContextMenu(); -}); - -browser.storage.onChanged.addListener((changes, area) => { - if (area !== 'local') return; - if (changes.authToken) ensureContextMenu(); -}); - -browser.contextMenus.onClicked.addListener(async info => { - if (typeof info.menuItemId !== 'string') return; - if (!info.menuItemId.startsWith(MENU_STATUS_PREFIX)) return; - - const status = info.menuItemId.slice(MENU_STATUS_PREFIX.length); - const text = (info.selectionText || '').trim().replace(/\s+/g, ' ').slice(0, 120); - if (!text) return; - - try { - const searchRes = await apiFetch(`/api/search-quick?q=${encodeURIComponent(text)}`); - const items = await searchRes.json(); - const top = items && items[0]; - if (!top || !top.id) { - await browser.notifications?.create?.({ - type: 'basic', - title: 'MyAnimeList', - message: `No matches for: ${text}`, - }); - return; - } - - await apiFetch('/api/watchlist', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ animeId: top.id, status }), - }); - } catch { - // Silent failure by default; can be extended with notifications later. - } -}); diff --git a/extensions/mal-firefox/icon.svg b/extensions/mal-firefox/icon.svg deleted file mode 100644 index 016dbb5..0000000 --- a/extensions/mal-firefox/icon.svg +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/extensions/mal-firefox/manifest.json b/extensions/mal-firefox/manifest.json deleted file mode 100644 index 8ec94a1..0000000 --- a/extensions/mal-firefox/manifest.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "manifest_version": 3, - "name": "MyAnimeList", - "version": "0.1.0", - "description": "Right-click selected anime titles and add them to your watchlist.", - "permissions": ["contextMenus", "storage"], - "host_permissions": [""], - "background": { - "scripts": ["background.js"] - }, - "action": { - "default_title": "MAL Watchlist", - "default_popup": "popup.html" - }, - "icons": { - "48": "icon.svg" - } -} diff --git a/extensions/mal-firefox/popup.css b/extensions/mal-firefox/popup.css deleted file mode 100644 index ff6b720..0000000 --- a/extensions/mal-firefox/popup.css +++ /dev/null @@ -1,229 +0,0 @@ -:root { - color-scheme: light dark; - --bg: #0b0f1a; - --card: rgba(255, 255, 255, 0.06); - --border: rgba(255, 255, 255, 0.12); - --text: rgba(255, 255, 255, 0.92); - --muted: rgba(255, 255, 255, 0.65); - --accent: #6ea8fe; - --danger: #ff6b6b; - --ok: #4ade80; -} - -@media (prefers-color-scheme: light) { - :root { - --bg: #f6f7fb; - --card: rgba(0, 0, 0, 0.03); - --border: rgba(0, 0, 0, 0.1); - --text: rgba(0, 0, 0, 0.88); - --muted: rgba(0, 0, 0, 0.6); - --accent: #1f6feb; - --danger: #b42318; - } -} - -html, -body { - margin: 0; - padding: 0; - background: var(--bg); - color: var(--text); - font: - 14px/1.4 system-ui, - -apple-system, - Segoe UI, - Roboto, - sans-serif; -} - -body { - width: 380px; - min-width: 380px; -} - -#app { - padding: 10px; -} - -.panel { - background: transparent; - border-radius: 0; - padding: 12px; - display: grid; - gap: 10px; -} - -.brand { - display: flex; - align-items: center; - gap: 8px; -} - -.brandIcon { - width: 28px; - height: 28px; - border-radius: 8px; -} - -.title { - font-weight: 650; - letter-spacing: 0.2px; -} - -.header { - display: flex; - align-items: center; - justify-content: space-between; -} - -.link { - background: transparent; - color: var(--accent); - border: 0; - padding: 6px 0; - cursor: pointer; -} - -.divider { - height: 1px; - background: transparent; - opacity: 0.9; -} - -.subtitle { - font-weight: 600; - color: var(--muted); -} - -.label { - display: grid; - gap: 4px; - color: var(--muted); -} - -.input { - width: 100%; - box-sizing: border-box; - padding: 9px 10px; - border-radius: 0; - border: 1px solid var(--border); - background: rgba(0, 0, 0, 0.15); - color: var(--text); - outline: none; -} - -.input:focus { - border: 1px solid var(--border); - outline: none; -} - -.btn { - width: 100%; - padding: 10px 12px; - border-radius: 0; - border: 0; - background: rgba(110, 168, 254, 0.18); - color: var(--text); - cursor: pointer; -} - -.btn.danger { - background: rgba(255, 107, 107, 0.18); -} - -.error { - color: var(--danger); -} - -.body { - color: var(--muted); -} - -.login { - display: grid; - gap: 8px; -} - -.statusRow { - display: flex; - align-items: center; - gap: 8px; - color: var(--muted); -} - -.statusDot { - width: 8px; - height: 8px; - border-radius: 999px; - background: var(--ok); -} - -.statusText { - font-size: 12px; -} - -[hidden] { - display: none !important; -} - -.list { - display: grid; - gap: 8px; -} - -.item { - display: grid; - grid-template-columns: 44px 1fr; - gap: 10px; - align-items: center; - padding: 8px; - border-radius: 10px; - border: 0; -} - -.thumb { - width: 44px; - height: 62px; - border-radius: 8px; - object-fit: cover; - background: rgba(255, 255, 255, 0.08); -} - -.meta { - display: grid; - gap: 4px; -} - -.metaTitle { - font-weight: 650; -} - -.metaSub { - color: var(--muted); - font-size: 12px; -} - -.row { - display: flex; - gap: 8px; - align-items: center; - flex-wrap: wrap; -} - -.select { - padding: 8px 10px; - border-radius: 10px; - border: 0; - background: rgba(0, 0, 0, 0.15); - color: var(--text); - flex: 1; -} - -.mini { - padding: 8px 10px; - border-radius: 10px; - border: 0; - background: rgba(110, 168, 254, 0.18); - color: var(--text); - cursor: pointer; -} diff --git a/extensions/mal-firefox/popup.html b/extensions/mal-firefox/popup.html deleted file mode 100644 index d0075e0..0000000 --- a/extensions/mal-firefox/popup.html +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - MAL Watchlist - - - -
-
-
-
- -
MyAnimeList
-
- -
- -
- -
- Select an anime title on any page, then right click to open the context menu. Under - “MyAnimeList”, choose “Add to Watchlist” and pick a status to save it to your watchlist. -
- -
- - - - -
-
- - - - diff --git a/extensions/mal-firefox/popup.js b/extensions/mal-firefox/popup.js deleted file mode 100644 index eb72d05..0000000 --- a/extensions/mal-firefox/popup.js +++ /dev/null @@ -1,74 +0,0 @@ -function qs(id) { - return document.getElementById(id); -} - -async function getSettings() { - const { authToken, apiBaseUrl } = await browser.storage.local.get(['authToken', 'apiBaseUrl']); - return { - authToken: authToken || '', - apiBaseUrl: apiBaseUrl || 'https://mal.mkelvers.tech', - }; -} - -async function setSettings(patch) { - await browser.storage.local.set(patch); -} - -function show(el, on) { - el.hidden = !on; -} - -async function render() { - const settings = await getSettings(); - document.body.dataset.state = settings.authToken ? 'in' : 'out'; - - const logoutBtn = qs('logoutBtn'); - logoutBtn.addEventListener('click', async () => { - await setSettings({ authToken: '' }); - await render(); - }); - - const hasToken = !!settings.authToken; - show(logoutBtn, hasToken); - show(qs('login'), !hasToken); - show(qs('loggedIn'), hasToken); - - if (!hasToken) { - setupLogin(); - return; - } -} - -function setupLogin() { - const loginErr = qs('loginErr'); - show(loginErr, false); - - qs('loginBtn').onclick = async () => { - show(loginErr, false); - const username = qs('username').value.trim(); - const password = qs('password').value; - if (!username || !password) { - loginErr.textContent = 'Missing username or password'; - show(loginErr, true); - return; - } - - try { - const { apiBaseUrl } = await getSettings(); - const res = await fetch(apiBaseUrl.replace(/\/+$/, '') + '/api/auth/login', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ username, password, name: 'Firefox extension' }), - }); - if (!res.ok) throw new Error('Invalid username or password'); - const data = await res.json(); - await setSettings({ authToken: data.token }); - await render(); - } catch (e) { - loginErr.textContent = e.message || 'Login failed'; - show(loginErr, true); - } - }; -} - -render(); diff --git a/internal/server/cors.go b/internal/server/cors.go index 02e716e..4d22d2b 100644 --- a/internal/server/cors.go +++ b/internal/server/cors.go @@ -32,9 +32,6 @@ func CORSMiddleware() gin.HandlerFunc { } func isAllowedOrigin(origin string) bool { - if strings.HasPrefix(origin, "moz-extension://") { - return true - } if strings.HasPrefix(origin, "http://localhost:") || strings.HasPrefix(origin, "https://localhost:") { return true }