primo commit
This commit is contained in:
@@ -1,3 +0,0 @@
|
|||||||
# ogame_notepad
|
|
||||||
|
|
||||||
Estensione per browser derivati da Chrome che mette un piccolo blocco note a sinistra della pagina web ogame
|
|
||||||
+144
@@ -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);
|
||||||
|
}
|
||||||
+263
@@ -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 =
|
||||||
|
'<span class="onp-title"><span class="onp-dot"></span>Notepad</span>' +
|
||||||
|
'<span class="onp-actions">' +
|
||||||
|
'<button class="onp-btn" id="onp-collapse" title="Chiudi">✕</button>' +
|
||||||
|
'</span>';
|
||||||
|
|
||||||
|
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 =
|
||||||
|
'<span id="onp-count">0 caratteri</span>' +
|
||||||
|
'<span class="onp-status">● Salvato</span>';
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})();
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 195 B |
Binary file not shown.
|
After Width: | Height: | Size: 291 B |
Binary file not shown.
|
After Width: | Height: | Size: 464 B |
@@ -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)
|
||||||
@@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user