let serverUrl = ''; function norm(v) { return (v || '').replace(/\/+$/, ''); } async function getServerUrl() { const r = await browser.storage.sync.get('serverUrl'); serverUrl = norm(r.serverUrl); return serverUrl; } browser.storage.onChanged.addListener(changes => { if (changes.serverUrl) { serverUrl = norm(changes.serverUrl.newValue); } }); browser.runtime.onMessage.addListener((msg, sender) => { switch (msg.kind) { case 'list_users': return listUsers(); case 'save_note': return saveNote(msg); case 'upload_attachment': return uploadAttachment(msg); default: return Promise.reject(new Error('Unknown message kind')); } }); async function request(path, opts) { const base = await getServerUrl(); if (!base) { throw new Error('Server URL not set — open snotes and click "Capture" → "Apply to Extension", or set it in Options'); } opts = opts || {}; opts.credentials = 'include'; let res; try { res = await fetch(base + path, opts); } catch (e) { throw new Error('Cannot reach ' + base + ' (' + e.message + ')'); } if (!res.ok) { const err = await res.json().catch(() => ({})); throw new Error(err.detail || 'HTTP ' + res.status); } return res.json(); } async function listUsers() { const users = await request('/api/users', { headers: { 'Accept': 'application/json' } }); return { users: users }; } async function saveNote(msg) { const body = { body: msg.body }; if (msg.username) body.username = msg.username; if (msg.id) { return request('/api/notes/' + msg.id, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body), }); } return request('/api/notes', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body), }); } async function uploadAttachment(msg) { const base = await getServerUrl(); if (!base) { throw new Error('Server URL not set — open snotes and click "Capture" → "Apply to Extension", or set it in Options'); } const blob = dataUrlToBlob(msg.dataUrl); const form = new FormData(); form.append('file', blob, msg.filename || 'page.png'); if (msg.username) form.append('username', msg.username); const res = await fetch(base + '/api/attachments', { method: 'POST', body: form, credentials: 'include', }); if (!res.ok) { const err = await res.json().catch(() => ({})); throw new Error(err.detail || 'HTTP ' + res.status); } return res.json(); } function dataUrlToBlob(dataUrl) { const [meta, b64] = dataUrl.split(','); const mime = (meta.match(/data:(.*?);/) || [])[1] || 'image/png'; const bin = atob(b64); const bytes = new Uint8Array(bin.length); for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i); return new Blob([bytes], { type: mime }); }