Skip to content

Instantly share code, notes, and snippets.

@sputnick-dev
Last active November 26, 2025 11:40
Show Gist options
  • Select an option

  • Save sputnick-dev/5dc96574fb6adfec878412713537b97b to your computer and use it in GitHub Desktop.

Select an option

Save sputnick-dev/5dc96574fb6adfec878412713537b97b to your computer and use it in GitHub Desktop.
Fix style bug: keep same style/CSS even with dark theme from the current page
// ==UserScript==
// @name Notefoxlite
// @namespace sputnick
// @description Simple note-taking per full URL or domain. Based on Notefox.
// @version 2.4
// @author sputnick/chatgpt
// @match *://*/*
// @grant none
// @run-at document-idle
// ==/UserScript==
(function () {
'use strict';
// --- Skip injection in unwanted contexts (iframes, too small frames, etc.) ---
try {
if (window.self !== window.top) return;
const minWidth = 300;
const minHeight = 200;
if (window.innerWidth < minWidth || window.innerHeight < minHeight) return;
const frameElem = window.frameElement;
if (frameElem && (frameElem.tagName === 'IFRAME' || frameElem.tagName === 'FRAME')) return;
} catch (e) {
return;
}
const STORAGE_PREFIX = 'notefox_lite_v1:';
const AUTOSAVE_INTERVAL_MS = 2000;
const POS_KEY = STORAGE_PREFIX + 'button_position';
function keyForScope(scope) {
return scope === 'domain'
? STORAGE_PREFIX + 'domain:' + location.hostname
: STORAGE_PREFIX + 'full:' + location.href;
}
function saveMeta(meta) {
localStorage.setItem(STORAGE_PREFIX + 'meta', JSON.stringify(meta));
}
function loadMeta() {
const raw = localStorage.getItem(STORAGE_PREFIX + 'meta');
if (!raw) return { scope: 'domain' };
try { return JSON.parse(raw); } catch { return { scope: 'domain' }; }
}
function saveButtonPosition(x, y) {
localStorage.setItem(POS_KEY, JSON.stringify({ x, y }));
}
function loadButtonPosition() {
try {
const pos = JSON.parse(localStorage.getItem(POS_KEY));
return pos && typeof pos.x === 'number' && typeof pos.y === 'number' ? pos : null;
} catch {
return null;
}
}
const container = document.createElement('div');
const shadow = container.attachShadow({ mode: 'closed' });
shadow.innerHTML = `
<style>
:host {
all: initial;
font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial;
color-scheme: light;
}
.nfl-panel {
position: fixed;
right: 18px;
bottom: 60px;
width: 420px;
height: 340px;
box-shadow: 0 6px 24px rgba(0,0,0,0.25);
border-radius: 12px;
overflow: hidden;
background: #fff !important;
color: #000 !important;
border: 1px solid rgba(0,0,0,0.08) !important;
z-index: 2147483647;
display: flex;
flex-direction: column;
/* border: 1px solid rgba(0,0,0,0.08); */
}
.nfl-hidden { display: none !important; }
.nfl-topbar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 6px 10px;
background: #f9f9f9 !important;
border-bottom: 1px solid rgba(0,0,0,0.06) !important;
font-size: 13px;
color: #000 !important;
}
.nfl-buttons {
display: flex;
gap: 6px;
}
.nfl-btn {
border: 1px solid rgba(0,0,0,0.1) !important;
border-radius: 6px;
padding: 3px 8px;
background: #fff !important;
color: #000 !important;
cursor: pointer;
font-size: 13px;
transition: background 0.15s, border 0.15s;
}
.nfl-btn:hover {
background: #f2f2f2 !important;
}
.nfl-btn.active {
background: #fff7b2 !important;
border: 1px solid #e6c300 !important;
font-weight: 600;
}
.nfl-text {
flex: 1;
width: 100%;
resize: none;
border-radius: 0 0 12px 12px;
padding: 8px;
border: none;
font-family: inherit;
font-size: 13px;
box-sizing: border-box;
background: #fff !important;
color: #000 !important;
}
#floatingBtn {
position: fixed;
right: 18px;
bottom: 18px;
z-index: 2147483648;
border: 1px solid rgba(0,0,0,0.2) !important;
background: #ffeb3b !important;
color: #000 !important;
box-shadow: 0 3px 6px rgba(0,0,0,0.25);
border-radius: 8px;
padding: 8px 12px;
cursor: grab;
font-size: 13px;
font-weight: 600;
transition: background 0.2s, transform 0.1s;
user-select: none;
}
#floatingBtn:hover {
background: #ffde00 !important;
transform: scale(1.05);
}
#floatingBtn:active {
transform: scale(0.97);
cursor: grabbing;
}
</style>
<div class="nfl-panel nfl-hidden" id="panel">
<div class="nfl-topbar">
<span style="font-weight:500;">🗒️ NoteFoxLite</span>
<div class="nfl-buttons">
<button class="nfl-btn" id="btnDomain">Domain</button>
<button class="nfl-btn" id="btnFull">Full URL</button>
</div>
</div>
<textarea id="noteArea" class="nfl-text" placeholder="Type your notes here..."></textarea>
</div>
<button id="floatingBtn">✏️ Notes</button>
`;
document.documentElement.appendChild(container);
const panel = shadow.getElementById('panel');
const noteArea = shadow.getElementById('noteArea');
const floatingBtn = shadow.getElementById('floatingBtn');
const btnDomain = shadow.getElementById('btnDomain');
const btnFull = shadow.getElementById('btnFull');
let clickedInsideShadow = false;
shadow.addEventListener('mousedown', function () {
clickedInsideShadow = true;
}, true);
// Close panel when clicking outside
document.addEventListener('mousedown', function () {
if (panel.classList.contains('nfl-hidden')) return;
if (clickedInsideShadow) {
clickedInsideShadow = false;
return;
}
panel.classList.add('nfl-hidden');
});
['keydown', 'keypress', 'keyup', 'input', 'paste', 'copy', 'cut'].forEach(evt => {
noteArea.addEventListener(evt, e => e.stopPropagation(), true);
});
let meta = loadMeta();
let currentScope = meta.scope || 'domain';
let currentKey = keyForScope(currentScope);
let lastValue = localStorage.getItem(currentKey) || '';
noteArea.value = lastValue;
function saveNow() {
localStorage.setItem(currentKey, noteArea.value);
lastValue = noteArea.value;
updateButtonIcon();
}
function updateButtonIcon() {
const domainKey = keyForScope('domain');
const fullKey = keyForScope('full');
const domainNote = (localStorage.getItem(domainKey) || '').trim();
const fullNote = (localStorage.getItem(fullKey) || '').trim();
if (domainNote || fullNote) {
floatingBtn.textContent = '🗒️ Notes';
} else {
floatingBtn.textContent = '✏️ Notes';
}
}
setInterval(() => {
if (noteArea.value !== lastValue) saveNow();
}, AUTOSAVE_INTERVAL_MS);
function updateButtons() {
btnDomain.classList.toggle('active', currentScope === 'domain');
btnFull.classList.toggle('active', currentScope === 'full');
}
btnDomain.addEventListener('click', () => {
currentScope = 'domain';
meta.scope = currentScope;
saveMeta(meta);
currentKey = keyForScope(currentScope);
noteArea.value = localStorage.getItem(currentKey) || '';
lastValue = noteArea.value;
updateButtons();
updateButtonIcon();
});
btnFull.addEventListener('click', () => {
currentScope = 'full';
meta.scope = currentScope;
saveMeta(meta);
currentKey = keyForScope(currentScope);
noteArea.value = localStorage.getItem(currentKey) || '';
lastValue = noteArea.value;
updateButtons();
updateButtonIcon();
});
floatingBtn.addEventListener('click', e => {
if (isDragging) return;
panel.classList.toggle('nfl-hidden');
if (!panel.classList.contains('nfl-hidden')) noteArea.focus();
});
window.addEventListener('beforeunload', saveNow);
updateButtons();
updateButtonIcon();
// --- movable floating button ---
let isDragging = false;
let startX, startY, startLeft, startTop;
const pos = loadButtonPosition();
if (pos) {
floatingBtn.style.left = pos.x + 'px';
floatingBtn.style.top = pos.y + 'px';
floatingBtn.style.right = 'auto';
floatingBtn.style.bottom = 'auto';
}
function clamp(value, min, max) {
return Math.min(Math.max(value, min), max);
}
floatingBtn.addEventListener('mousedown', e => {
isDragging = false;
startX = e.clientX;
startY = e.clientY;
const rect = floatingBtn.getBoundingClientRect();
startLeft = rect.left;
startTop = rect.top;
function onMouseMove(ev) {
const dx = ev.clientX - startX;
const dy = ev.clientY - startY;
const newLeft = startLeft + dx;
const newTop = startTop + dy;
const btnRect = floatingBtn.getBoundingClientRect();
const tolerance = 25;
const minX = -tolerance;
const minY = -tolerance;
const maxX = window.innerWidth - btnRect.width + tolerance;
const maxY = window.innerHeight - btnRect.height + tolerance;
const clampedLeft = clamp(newLeft, minX, maxX);
const clampedTop = clamp(newTop, minY, maxY);
floatingBtn.style.left = clampedLeft + 'px';
floatingBtn.style.top = clampedTop + 'px';
floatingBtn.style.right = 'auto';
floatingBtn.style.bottom = 'auto';
isDragging = true;
}
function onMouseUp() {
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp);
if (isDragging) {
const left = parseInt(floatingBtn.style.left);
const top = parseInt(floatingBtn.style.top);
saveButtonPosition(left, top);
}
}
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);
});
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment