Last active
November 26, 2025 11:40
-
-
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.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