// ==UserScript== // @name Futaba-Float-Window // @namespace http://tampermonkey.net/ // @version 2025-06-19-2 // @description try to take over the world! // @author You // @match http://*.2chan.net/* // @match https://*.2chan.net/* // @icon https://www.google.com/s2/favicons?sz=64&domain=2chan.net // @grant none // ==/UserScript== (function() { // 対象のページじゃなかったら、何もしないでおしまいっ if (!/https?:\/\/(?:[^.]+\.)?2chan\.net\//.test(location.href)) { return; } // 投稿フォーム(IDが "fm" の要素)を探してみますっ! var form = document.getElementById('fm'); if (!form) { console.log("フォームが見つかりませんでした💦"); return; } // フロートウィンドウをつくりますっ! var floatingWindow = document.createElement('div'); floatingWindow.style.position = 'fixed'; floatingWindow.style.top = '50px'; // 上から50pxの位置にふんわり配置っ floatingWindow.style.right = '15px'; // 右側にぴたっと寄せますっ floatingWindow.style.backgroundColor = '#ffffee'; // ちょっときいろっぽい背景がやさしい〜♪ floatingWindow.style.border = '1px solid rgba(0, 0, 0, 0.2)'; // やわらかい枠線ですっ floatingWindow.style.borderRadius = '12px'; // 角をまる〜くして、かわいくっ! floatingWindow.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.1)'; // ふんわり影をつけて、立体感♪ floatingWindow.style.padding = '15px'; floatingWindow.style.zIndex = '9999'; // 一番手前に出るようにしますっ floatingWindow.style.overflow = 'hidden'; floatingWindow.style.maxHeight = '80vh'; // 画面の高さの80%まで! // ボタンのかたまりをつくりますっ♪ var buttonGroup = document.createElement('div'); buttonGroup.style.position = 'absolute'; buttonGroup.style.top = '10px'; buttonGroup.style.right = '10px'; buttonGroup.style.display = 'flex'; buttonGroup.style.gap = '5px'; buttonGroup.style.zIndex = '10000'; // ボタンに共通のかわいいスタイルをつける関数ですっ function styleButton(button, label) { button.textContent = label; button.style.backgroundColor = '#eeaa88'; // ピンクオレンジっぽくておしゃれ〜♪ button.style.color = '#800000'; // 文字色も落ち着いてますっ button.style.border = 'none'; button.style.padding = '5px 10px'; button.style.cursor = 'pointer'; button.style.width = '30px'; // 幅を固定して、見た目がびしっ!と安定しますっ♪ button.style.height = '30px'; // 高さもそろえて、正方形っぽくかわいくっ♪ button.style.textAlign = 'center'; // テキストを中央にそろえますっ button.style.display = 'flex'; // フレックスで中身をぴったり配置っ♪ button.style.alignItems = 'center'; // 縦方向の中央ぞろえ〜 button.style.justifyContent = 'center'; // 横方向もばっちりっ return button; } // ↑ 上までスクロールするボタンっ! var homeButton = styleButton(document.createElement('button'), '↑'); homeButton.addEventListener('click', () => window.scrollTo({ top: 0, behavior: 'smooth' })); // ↓ 下までスクロールするボタンです〜! var endButton = styleButton(document.createElement('button'), '↓'); endButton.addEventListener('click', () => window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' })); // ⟳ ページをリロードするボタンをつくりますっ! var reloadButton = styleButton(document.createElement('button'), '⟳'); reloadButton.style.fontSize = '20px'; // ← ここで文字をちょっと大きくして目立たせますっ! reloadButton.addEventListener('click', () => location.reload()); // 押すとページが更新されます〜! // + ウィンドウを開いたり閉じたりするボタンですっ! var toggleButton = styleButton(document.createElement('button'), '+'); // ボタンたちをグループに追加〜♪ buttonGroup.appendChild(homeButton); buttonGroup.appendChild(endButton); buttonGroup.appendChild(reloadButton); // ← リロードボタンも仲間入りっ♪ buttonGroup.appendChild(toggleButton); // 投稿フォームとボタンたちをフロートウィンドウにぺたっ! floatingWindow.appendChild(buttonGroup); floatingWindow.appendChild(form); document.body.appendChild(floatingWindow); // フォームのサイズに合わせてウィンドウを調整する関数ですっ♪ function resizeFloatingWindowToForm() { form.style.display = 'block'; form.style.marginTop = '30px'; // ← ボタンとの間にスペースをつくりますっ♪ const rect = form.getBoundingClientRect(); floatingWindow.style.width = '500px'; floatingWindow.style.height = rect.height + 30 + 'px'; // パディングやボタンぶんも足してます〜 } // 最小化するときのサイズと状態を用意しておきますっ var minimizedWidth = 125; var minimizedHeight = 20; var isMinimized = true; // 最初はちっちゃくなってます〜♪ floatingWindow.style.width = minimizedWidth + 'px'; floatingWindow.style.height = minimizedHeight + 'px'; form.style.display = 'none'; // +ボタンを押したら、びよーん!って開いたり、しゅっ!って閉じたりしますっ! toggleButton.addEventListener('click', function () { if (isMinimized) { resizeFloatingWindowToForm(); // ← フォームにあわせてサイズ調整しますっ♪ toggleButton.textContent = '-'; } else { floatingWindow.style.width = minimizedWidth + 'px'; floatingWindow.style.height = minimizedHeight + 'px'; form.style.display = 'none'; toggleButton.textContent = '+'; } isMinimized = !isMinimized; }); // ドラッグできるようにするための準備ですっ var isDragging = false; var offsetX, offsetY; // ドラッグ開始エリアかどうかを確認する関数っ function isInDragArea(x, y) { const buffer = 10; const rect = floatingWindow.getBoundingClientRect(); return ( (x >= rect.left - buffer && x <= rect.left + buffer) || (x >= rect.right - buffer && x <= rect.right + buffer) || (y >= rect.top - buffer && y <= rect.top + buffer) || (y >= rect.bottom - buffer && y <= rect.bottom + buffer) ); } // ドラッグを始めるときの処理〜! function startDrag(x, y) { isDragging = true; const rect = floatingWindow.getBoundingClientRect(); offsetX = x - rect.left; offsetY = y - rect.top; floatingWindow.style.left = rect.left + 'px'; floatingWindow.style.top = rect.top + 'px'; floatingWindow.style.right = ''; floatingWindow.style.bottom = ''; floatingWindow.style.cursor = 'move'; } // ドラッグ中の処理です〜♪ function dragMove(x, y) { if (isDragging) { floatingWindow.style.left = (x - offsetX) + 'px'; floatingWindow.style.top = (y - offsetY) + 'px'; } } // ドラッグを終わらせるときの処理っ function endDrag() { if (isDragging) { isDragging = false; floatingWindow.style.cursor = 'default'; const rect = floatingWindow.getBoundingClientRect(); floatingWindow.style.right = window.innerWidth - rect.right + 'px'; floatingWindow.style.top = rect.top + 'px'; floatingWindow.style.left = ''; floatingWindow.style.bottom = ''; } } // マウスの操作イベントを設定しますっ♪ floatingWindow.addEventListener('mousedown', function (e) { if (isInDragArea(e.clientX, e.clientY)) { startDrag(e.clientX, e.clientY); } }); document.addEventListener('mousemove', function (e) { dragMove(e.clientX, e.clientY); }); document.addEventListener('mouseup', function () { endDrag(); }); // タッチ操作の対応ですっ!スマホでもつかえるように…えへへっ♪ floatingWindow.addEventListener('touchstart', function (e) { const touch = e.touches[0]; if (isInDragArea(touch.clientX, touch.clientY)) { startDrag(touch.clientX, touch.clientY); } }, { passive: false }); document.addEventListener('touchmove', function (e) { if (isDragging) { e.preventDefault(); // スクロールしないようにしますっ! const touch = e.touches[0]; dragMove(touch.clientX, touch.clientY); } }, { passive: false }); document.addEventListener('touchend', function () { endDrag(); }, { passive: false }); // いらないパーツは、そっと非表示にしちゃいますっ! var formSwitchElement = document.getElementById('reszb'); if (formSwitchElement) formSwitchElement.style.display = 'none'; var unnecessaryElements = document.querySelectorAll('.ftb2'); unnecessaryElements.forEach(el => el.remove()); })();