Last active
December 11, 2025 06:05
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ==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