diff --git a/README.md b/README.md deleted file mode 100644 index de545f3..0000000 --- a/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# ogame_notepad - -Estensione per browser derivati da Chrome che mette un piccolo blocco note a sinistra della pagina web ogame \ No newline at end of file diff --git a/content.css b/content.css new file mode 100644 index 0000000..cb4499d --- /dev/null +++ b/content.css @@ -0,0 +1,144 @@ +/* ===== OGame Notepad - finestra flottante ===== */ +#ogame-notepad { + position: fixed; + left: 20px; + top: 20px; + width: 280px; + height: 360px; + z-index: 999999; + display: flex; + flex-direction: column; + font-family: Verdana, Arial, Helvetica, sans-serif; + background: #0b1220; + border: 1px solid #2a4a6b; + border-radius: 6px; + box-shadow: 0 6px 24px rgba(0, 0, 0, 0.65), inset 0 0 0 1px rgba(255, 255, 255, 0.03); + overflow: hidden; +} + +/* Barra del titolo (trascinabile) */ +#ogame-notepad .onp-header { + display: flex; + align-items: center; + justify-content: space-between; + background: linear-gradient(180deg, #1b2f4d, #14243c); + color: #d9b45a; + padding: 8px 10px; + border-bottom: 1px solid #2a4a6b; + font-size: 13px; + font-weight: bold; + letter-spacing: 1px; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6); + user-select: none; + cursor: move; +} + +#ogame-notepad .onp-title { + display: flex; + align-items: center; + gap: 7px; +} + +#ogame-notepad .onp-title .onp-dot { + width: 10px; + height: 10px; + background: radial-gradient(circle at 35% 35%, #7fd0ff, #2a6bb0); + border-radius: 50%; + box-shadow: 0 0 8px #2a6bb0; +} + +#ogame-notepad .onp-actions { + display: flex; + gap: 4px; +} + +#ogame-notepad .onp-btn { + background: transparent; + border: 1px solid transparent; + color: #9db8d6; + font-size: 14px; + cursor: pointer; + padding: 1px 6px; + line-height: 1; + border-radius: 3px; +} +#ogame-notepad .onp-btn:hover { + background: #2a4a6b; + color: #ffffff; + border-color: #3a6a9b; +} + +#ogame-notepad textarea { + flex: 1; + resize: none; + border: none; + background: #0b1220; + color: #dce6f2; + padding: 10px; + font-size: 13px; + line-height: 1.55; + font-family: inherit; + outline: none; +} +#ogame-notepad textarea::placeholder { + color: #4a5f7a; +} + +#ogame-notepad .onp-footer { + display: flex; + align-items: center; + justify-content: space-between; + background: #101a2c; + color: #6f87a3; + padding: 5px 10px; + border-top: 1px solid #2a4a6b; + font-size: 11px; +} + +#ogame-notepad .onp-footer .onp-status { + color: #6fbf6f; +} + +/* Impugnatura per il ridimensionamento (angolo in basso a destra) */ +#ogame-notepad .onp-resizer { + position: absolute; + right: 0; + bottom: 0; + width: 18px; + height: 18px; + cursor: nwse-resize; + z-index: 3; +} +#ogame-notepad .onp-resizer::after { + content: ''; + position: absolute; + right: 3px; + bottom: 3px; + width: 8px; + height: 8px; + border-right: 2px solid #3a6a9b; + border-bottom: 2px solid #3a6a9b; + border-radius: 0 0 3px 0; +} +#ogame-notepad .onp-resizer:hover::after { + border-color: #d9b45a; +} + +/* Pulsante per riaprire il notepad quando è chiuso */ +#ogame-notepad-toggle { + position: fixed; + left: 10px; + top: 20px; + z-index: 999999; + background: linear-gradient(180deg, #1b2f4d, #14243c); + color: #d9b45a; + border: 1px solid #2a4a6b; + border-radius: 6px; + padding: 10px 8px; + cursor: pointer; + font-size: 15px; + box-shadow: 0 4px 14px rgba(0, 0, 0, 0.5); +} +#ogame-notepad-toggle:hover { + background: linear-gradient(180deg, #2a4a6b, #1b2f4d); +} diff --git a/content.js b/content.js new file mode 100644 index 0000000..cc01fc8 --- /dev/null +++ b/content.js @@ -0,0 +1,263 @@ +(() => { + 'use strict'; + + // Doppia sicurezza: attivo solo sul dominio richiesto + if (window.location.hostname !== 's170-ar.ogame.gameforge.com') { + return; + } + + const STORAGE_KEY = 'ogame_notepad_text'; + const COLLAPSED_KEY = 'ogame_notepad_collapsed'; + const POS_KEY = 'ogame_notepad_pos'; + const SIZE_KEY = 'ogame_notepad_size'; + + // ---- Creazione elementi ---- + const container = document.createElement('div'); + container.id = 'ogame-notepad'; + + const header = document.createElement('div'); + header.className = 'onp-header'; + header.innerHTML = + 'Notepad' + + '' + + '' + + ''; + + const textarea = document.createElement('textarea'); + textarea.placeholder = 'Scrivi le tue note qui...'; + textarea.spellcheck = false; + + const footer = document.createElement('div'); + footer.className = 'onp-footer'; + footer.innerHTML = + '0 caratteri' + + '● Salvato'; + + const resizer = document.createElement('div'); + resizer.className = 'onp-resizer'; + resizer.title = 'Trascina per ridimensionare'; + + container.appendChild(header); + container.appendChild(textarea); + container.appendChild(footer); + container.appendChild(resizer); + document.body.appendChild(container); + + // Pulsante per riaprire quando chiuso + const toggleBtn = document.createElement('button'); + toggleBtn.id = 'ogame-notepad-toggle'; + toggleBtn.innerHTML = '▶'; + toggleBtn.title = 'Apri notepad'; + document.body.appendChild(toggleBtn); + + const countEl = footer.querySelector('#onp-count'); + const statusEl = footer.querySelector('.onp-status'); + + // ---- Persistenza ---- + let saveTimer = null; + let dirty = false; // true se ci sono modifiche non ancora salvate + let syncing = false; // evita loop quando aggiorniamo da un'altra tab + + function loadNote() { + try { + const saved = localStorage.getItem(STORAGE_KEY); + if (saved !== null) { + textarea.value = saved; + } + } catch (e) { + /* storage non disponibile */ + } + updateCount(); + } + + function saveNote() { + dirty = false; + try { + localStorage.setItem(STORAGE_KEY, textarea.value); + statusEl.textContent = '\u25CF Salvato'; + statusEl.style.color = '#6fbf6f'; + } catch (e) { + statusEl.textContent = '\u26A0 Errore salvataggio'; + statusEl.style.color = '#e0a050'; + } + } + + function updateCount() { + const n = textarea.value.length; + countEl.textContent = n + (n === 1 ? ' carattere' : ' caratteri'); + } + + // Salvataggio automatico con debounce + textarea.addEventListener('input', () => { + dirty = true; + updateCount(); + statusEl.textContent = '\u25CF Salvataggio...'; + statusEl.style.color = '#d8d8a0'; + clearTimeout(saveTimer); + saveTimer = setTimeout(saveNote, 400); + }); + + // Sincronizzazione in tempo reale tra le tab dello stesso dominio. + // Quando un'altra tab salva, questa riceve l'evento 'storage' e aggiorna + // la textarea, così premendo F5 qui trovi sempre i dati più recenti. + window.addEventListener('storage', (e) => { + if (e.key !== STORAGE_KEY) return; + // Non sovrascrivere mentre l'utente sta digitando in questa tab + if (document.activeElement === textarea && dirty) return; + syncing = true; + textarea.value = e.newValue !== null ? e.newValue : ''; + updateCount(); + statusEl.textContent = '\u25CF Salvato'; + statusEl.style.color = '#6fbf6f'; + syncing = false; + }); + + // ---- Trascinamento finestra ---- + function restorePosition() { + try { + const saved = localStorage.getItem(POS_KEY); + if (saved) { + const pos = JSON.parse(saved); + if (typeof pos.left === 'number' && typeof pos.top === 'number') { + container.style.left = pos.left + 'px'; + container.style.top = pos.top + 'px'; + } + } + } catch (e) {} + } + + function restoreSize() { + try { + const saved = localStorage.getItem(SIZE_KEY); + if (saved) { + const size = JSON.parse(saved); + if (typeof size.w === 'number') container.style.width = size.w + 'px'; + if (typeof size.h === 'number') container.style.height = size.h + 'px'; + } + } catch (e) {} + } + + function makeResizable() { + let startX = 0, startY = 0, startW = 0, startH = 0, resizing = false; + + resizer.addEventListener('mousedown', (e) => { + resizing = true; + startX = e.clientX; + startY = e.clientY; + startW = container.offsetWidth; + startH = container.offsetHeight; + e.preventDefault(); + e.stopPropagation(); + }); + + window.addEventListener('mousemove', (e) => { + if (!resizing) return; + const newW = Math.max(200, startW + (e.clientX - startX)); + const newH = Math.max(160, startH + (e.clientY - startY)); + container.style.width = newW + 'px'; + container.style.height = newH + 'px'; + }); + + window.addEventListener('mouseup', () => { + if (!resizing) return; + resizing = false; + try { + localStorage.setItem(SIZE_KEY, JSON.stringify({ + w: container.offsetWidth, + h: container.offsetHeight + })); + } catch (e) {} + }); + } + + function makeDraggable() { + let dragStartX = 0, dragStartY = 0, origLeft = 0, origTop = 0, dragging = false; + + header.addEventListener('mousedown', (e) => { + if (e.target.closest('.onp-btn')) return; // non trascinare quando clicchi i bottoni + dragging = true; + dragStartX = e.clientX; + dragStartY = e.clientY; + const rect = container.getBoundingClientRect(); + origLeft = rect.left; + origTop = rect.top; + e.preventDefault(); + }); + + window.addEventListener('mousemove', (e) => { + if (!dragging) return; + const dx = e.clientX - dragStartX; + const dy = e.clientY - dragStartY; + container.style.left = (origLeft + dx) + 'px'; + container.style.top = (origTop + dy) + 'px'; + }); + + window.addEventListener('mouseup', () => { + if (!dragging) return; + dragging = false; + try { + localStorage.setItem(POS_KEY, JSON.stringify({ + left: container.getBoundingClientRect().left, + top: container.getBoundingClientRect().top + })); + } catch (e) {} + }); + } + + // ---- Collapse / expand ---- + function setCollapsed(collapsed) { + container.classList.toggle('collapsed', collapsed); + toggleBtn.style.display = collapsed ? 'block' : 'none'; + try { + localStorage.setItem(COLLAPSED_KEY, collapsed ? '1' : '0'); + } catch (e) {} + } + + header.querySelector('#onp-collapse').addEventListener('click', () => { + setCollapsed(true); + }); + + toggleBtn.addEventListener('click', () => { + setCollapsed(false); + textarea.focus(); + }); + + // ---- Blocca le scorciatoie di OGame quando il focus è sul notepad ---- + // Intercetta in fase di cattura così gli handler globali di OGame + // (frecce, spazio, invio, ecc.) non reagiscono mentre scrivi. + function blockKeysWhileTyping(e) { + if (document.activeElement === textarea) { + // Blocca SOLO la propagazione verso gli handler globali di OGame, + // ma NON chiama preventDefault: così la textarea continua a inserire + // caratteri, spostare il cursore, creare nuove righe, ecc. + e.stopImmediatePropagation(); + return false; + } + } + window.addEventListener('keydown', blockKeysWhileTyping, true); + window.addEventListener('keyup', blockKeysWhileTyping, true); + window.addEventListener('keypress', blockKeysWhileTyping, true); + + // ---- Init ---- + restorePosition(); + restoreSize(); + makeDraggable(); + makeResizable(); + loadNote(); + + let collapsed = false; + try { + collapsed = localStorage.getItem(COLLAPSED_KEY) === '1'; + } catch (e) {} + setCollapsed(collapsed); + + // Salva al cambio pagina SOLO se ci sono modifiche non ancora salvate. + // In questo modo una tab "stantia" non sovrascrive i dati più recenti + // scritti da un'altra tab quando premi F5. + window.addEventListener('beforeunload', () => { + if (dirty) { + clearTimeout(saveTimer); + saveNote(); + } + }); +})(); diff --git a/icons/icon128.png b/icons/icon128.png new file mode 100644 index 0000000..6ea8fb5 Binary files /dev/null and b/icons/icon128.png differ diff --git a/icons/icon16.png b/icons/icon16.png new file mode 100644 index 0000000..7a5bb6d Binary files /dev/null and b/icons/icon16.png differ diff --git a/icons/icon32.png b/icons/icon32.png new file mode 100644 index 0000000..02a86d7 Binary files /dev/null and b/icons/icon32.png differ diff --git a/icons/icon48.png b/icons/icon48.png new file mode 100644 index 0000000..5882fd2 Binary files /dev/null and b/icons/icon48.png differ diff --git a/make_icon.py b/make_icon.py new file mode 100644 index 0000000..bb62da7 --- /dev/null +++ b/make_icon.py @@ -0,0 +1,38 @@ +from PIL import Image, ImageDraw + +def make_icon(size): + img = Image.new("RGBA", (size, size), (0,0,0,0)) + d = ImageDraw.Draw(img) + s = size / 128.0 # scala + + def P(x, y): + return (int(x*s), int(y*s)) + + # Sfondo circolare blu-navy con bordo + d.ellipse([P(4,4), P(124,124)], fill=(20,36,60,255), outline=(42,74,107,255), width=int(3*s)) + + # Corpo del notepad (rettangolo arrotondato, carta chiara) + pad = [P(30,20), P(98,112)] + d.rounded_rectangle(pad, radius=int(8*s), fill=(224,232,244,255)) + + # Spirale in alto + for i in range(3): + x = 44 + i*18 + d.ellipse([P(x,16), P(x+6,24)], fill=(42,74,107,255)) + + # Righe di testo + line_color = (150,168,196,255) + for i in range(5): + y = 38 + i*14 + d.line([P(38,y), P(90,y)], fill=line_color, width=int(3*s)) + + # Penna (diagonale, oro) + pen = [(92,30),(112,92),(104,96),(82,34)] + d.polygon(pen, fill=(217,180,90,255)) + d.polygon([(112,92),(116,86),(108,94)], fill=(217,180,90,255)) + + return img + +for sz in [16, 32, 48, 128]: + make_icon(sz).save(f"icons/icon{sz}.png") + print("creato", sz) diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..77fcede --- /dev/null +++ b/manifest.json @@ -0,0 +1,22 @@ +{ + "manifest_version": 3, + "name": "OGame Notepad", + "version": "1.0.0", + "description": "Notepad laterale per OGame s170-ar. Le note vengono salvate in modo persistente.", + "icons": { + "16": "icons/icon16.png", + "32": "icons/icon32.png", + "48": "icons/icon48.png", + "128": "icons/icon128.png" + }, + "content_scripts": [ + { + "matches": [ + "https://s170-ar.ogame.gameforge.com/*" + ], + "js": ["content.js"], + "css": ["content.css"], + "run_at": "document_idle" + } + ] +}