NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Sturdy dragable reply box // @version 0.0.3 // @description Let's you drag a reply box // @author anone // @match https://sturdychan.help/* // @grant GM.getValue // @grant GM.setValue // @grant GM.deleteValue // @grant GM.registerMenuCommand // @run-at document-idle // @license MIT // ==/UserScript== (function() { 'use strict'; const STORAGE_KEY = 'draggableDivPosition'; const targetClasses = ['glass', 'editing', 'reply-form']; const postMine = ['glass', 'media', 'postMine']; const postMine2 = ['glass', 'postMine']; let currentForm = null; const observer = new MutationObserver((mutations) => { // Find the first matching form that hasn't been processed const form = document.querySelector('article.' + targetClasses.join('.')); if (form && form !== currentForm) { currentForm = form; setupDraggableForm(form); loadPosition(form); } const formMines = document.querySelectorAll('article.' + postMine.join('.')); const formMines2 = document.querySelectorAll('article.' + postMine2.join('.')); setTimeout(() => { formMines.forEach(formMine => { formMine.style.cssText = ` position: relative !important; z-index: auto !important; resize: none !important; overflow: auto !important; max-height: fit-content !important; left: auto !important; right: auto !important; `; formMine.style.removeProperty('top'); formMine.style.removeProperty('bottom'); }); formMines2.forEach(formMine => { formMine.style.cssText = ` position: relative !important; z-index: auto !important; resize: none !important; overflow: auto !important; max-height: fit-content !important; left: auto !important; right: auto !important; `; formMine.style.removeProperty('top'); formMine.style.removeProperty('bottom'); }); }, 5000); }); observer.observe(document.body, { childList: true, subtree: true, attributes: false, characterData: false }); function savePosition(form) { const position = { left: form.offsetLeft, top: form.offsetTop, timestamp: Date.now() }; try { localStorage.setItem(STORAGE_KEY, JSON.stringify(position)); console.log('Position saved:', position); } catch (e) { console.warn('Failed to save position:', e); } } function loadPosition(form) { try { const saved = JSON.parse(localStorage.getItem(STORAGE_KEY)); if (saved && isValidPosition(saved)) { form.style.left = `${saved.left}px`; form.style.top = `${saved.top}px`; console.log('Position loaded:', saved); } } catch (e) { console.warn('Failed to load position:', e); } } function isValidPosition(pos) { return Number.isInteger(pos.left) && Number.isInteger(pos.top) && pos.left >= 0 && pos.top >= 0 && pos.left < window.innerWidth && pos.top < window.innerHeight; } function setupDraggableForm(form) { // Add essential styling form.style.position = 'fixed'; form.style.zIndex = '9999'; form.style.resize = 'both'; // Enable resizing form.style.overflow = 'auto'; form.style.maxHeight = '25%'; // Drag functionality let isDragging = false; let offset = { x: 0, y: 0 }; const header = form.querySelector('header.spaced'); // Preserve existing positioning if any const initialLeft = parseFloat(form.style.left) || 20; const initialTop = parseFloat(form.style.top) || 20; form.style.left = `${initialLeft}px`; form.style.top = `${initialTop}px`; // Header interaction header.style.cursor = 'move'; header.addEventListener('mousedown', (e) => { // Ignore clicks on form elements if (e.target.closest('input, a, button, textarea')) return; isDragging = true; offset.x = e.clientX - form.offsetLeft; offset.y = e.clientY - form.offsetTop; form.style.zIndex = '10000'; e.preventDefault(); }); // Mouse movement handler document.addEventListener('mousemove', (e) => { if (isDragging) { form.style.left = `${e.clientX - offset.x}px`; form.style.top = `${e.clientY - offset.y}px`; savePosition(form); } }); // Cleanup on release document.addEventListener('mouseup', () => { isDragging = false; form.style.zIndex = '9999'; }); // Form controls protection form.querySelectorAll('input, textarea, button').forEach(el => { el.addEventListener('mousedown', (e) => e.stopPropagation()); }); // Window resize handler window.addEventListener('resize', () => { const rect = form.getBoundingClientRect(); form.style.left = `${rect.left}px`; form.style.top = `${rect.top}px`; }); const postControls = form.querySelector('#post-controls'); if (postControls) { // Apply required styles postControls.style.cssText = ` position: absolute !important; bottom: 0px !important; `; // Additional control styling (optional) postControls.style.width = 'calc(100% - 20px)'; postControls.style.padding = '10px 0'; } } })();