Skip to content

Instantly share code, notes, and snippets.

@sputnick-dev
Last active December 11, 2025 06:05
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.5
// @author sputnick/chatgpt
// @match *://*/*
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_addValueChangeListener
// @run-at document-idle
// ==/UserScript==
(async function () {
'use strict';
// --- Skip injection in unwanted contexts ---
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;
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' }; }
}
// --- Load/Save button position globally via GM_*
async function loadButtonPosition() {
const pos = await GM_getValue('floatingBtnPos', null);
return pos && typeof pos.x === 'number' && typeof pos.y === 'number' ? pos : null;
}
function saveButtonPosition(x, y) {
GM_setValue('floatingBtnPos', {x, y});
}
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;
}
.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');
// --- Shadow DOM click detection ---
let clickedInsideShadow = false;
shadow.addEventListener('mousedown', function () {
clickedInsideShadow = true;
}, true);
// Close panel only when clicking truly 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 = await loadButtonPosition();
if(pos){
floatingBtn.style.left=pos.x+'px';
floatingBtn.style.top=pos.y+'px';
floatingBtn.style.right='auto';
floatingBtn.style.bottom='auto';
}
// Listen to position changes from other tabs
GM_addValueChangeListener('floatingBtnPos',(name,oldValue,newValue,remote)=>{
if(remote && newValue){
floatingBtn.style.left=newValue.x+'px';
floatingBtn.style.top=newValue.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