您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
AO3 Floaty Comment Box (Responsive) is a userscript created to facilitate commenting on the fly while reading on archiveofourown - specifically for mobile browsing
当前为
// ==UserScript== // @name AO3 Floaty Comment Box (Responsive) // @namespace http://tampermonkey.net/ // @version 1.0 // @description AO3 Floaty Comment Box (Responsive) is a userscript created to facilitate commenting on the fly while reading on archiveofourown - specifically for mobile browsing // @author Schildpath // @match http://archiveofourown.org/* // @match https://archiveofourown.org/* // @match http://www.archiveofourown.org/* // @match https://www.archiveofourown.org/* // @grant none // @license MIT // ==/UserScript== (function() { 'use strict'; console.log("AO3 Floaty Script Loaded"); let floatyCreated = false; function createFloaty(commentBox) { // Prevent double initialization or initialization without cause if (floatyCreated || !commentBox) return; floatyCreated = true; // Floaty toggle button const floatyButton = document.createElement('button'); floatyButton.innerHTML = '🪶'; floatyButton.id = 'floaty-toggle-button'; floatyButton.setAttribute('aria-label', 'Toggle Comment Box'); floatyButton.style.cssText = ` position: fixed; inset: auto 1rem 1rem auto; z-index: 9999; padding: 0.3em; border-radius: 0.4em; font-family: inherit; font-size: 1em; color: inherit; opacity: .9; font-weight: bold; cursor: pointer; `; floatyButton.onfocus = (e) => e.target.blur(); // Prevent AO3 focus style document.body.appendChild(floatyButton); // Floaty insert quote button const insertButton = document.createElement('button'); insertButton.innerHTML = '« »'; insertButton.id = 'floaty-insert-button'; insertButton.setAttribute('aria-label', 'Insert Quote'); insertButton.style.cssText = ` position: fixed; inset: auto 1rem 3.5rem auto; z-index: 9999; padding: .4em; padding-bottom:.6em; border-radius: 0.4em; font-family: inherit; font-size: .9em; color: inherit; opacity: .9; font-weight: bold; cursor: pointer; `; insertButton.onfocus = (e) => e.target.blur(); // Prevent AO3 focus style document.body.appendChild(insertButton); // Floaty box container const floatyContainer = document.createElement('div'); floatyContainer.id = 'floaty-container'; floatyContainer.style.cssText = ` position: fixed; bottom: 0; left: 0; right: 0; width: 100vw; height: 250px; z-index: 9998; display: none; flex-direction: column; background: inherit; border-top: 1px solid currentColor; font-family: inherit; font-size: 0.8em; color: inherit; opacity: .95; `; document.body.appendChild(floatyContainer); // Tips & info container const infoContainer = document.createElement('div'); infoContainer.style.cssText = ` position: fixed; bottom: 250px; left: 0; right: 0; z-index: 9997; display: none; flex-direction: column; padding: .9em; gap: 0.5em; font-family: inherit; background: inherit; border-top: 1px solid currentColor; border-bottom: 1px solid currentColor; `; const helpDiv = document.createElement('div'); helpDiv.innerHTML = `<p><strong>About Floaty Comment Box:</strong></p>` + '<p>Floaty Comment Box was designed to facilitate commenting while reading - to allow the user to write down feelings & thoughts on the fly - specifically for mobile browsing.</p>' + '<ul><li>🔛 <strong>Toggle function:</strong> You can minimize the floaty comment box as you read and reopen it to continue to edit your review.</li>' + '<li>💬 <strong>Insert quotes:</strong> Select favorite quotes and use the « » button to insert them your comment: they will be formatted in italics and put between quotation marks.</li>' + '<li>🔄 <strong>Syncing:</strong> Everything that is typed in the floaty comment box will be automatically synced with the real comment box below the fic.</i>' + '<li>💌 <strong>Submitting:</strong> Your comment will only be submitted once you submit it in the the real comment form below (scroll down with the ⮟ button).</li></ul>' + `<p>© Floaty Comment Box was directly inspired (with permission) by an AO3 userscript originally developed by <a href="https://ravenel.tumblr.com/post/156555172141/i-saw-this-post-by-astropixie-about-how-itd-be">ravenel</a>.</p>` ; const tipsDiv = document.createElement('div'); tipsDiv.innerHTML = `<p><strong>Suggestions for writing a comment:</strong></p>` + '<ul><li>💬 Quotes you liked (select text and click « » to include)</li>'+ '<li>🎭 Scenes that you liked, or moved you, or surprised you</li>'+ '<li>😭 What is your feeling at the end of the chapter?</li>'+ '<li>👓 What are you most looking forward to next?</li>' + '<li>🔮 Do you have any predictions for the next chapters you want to share?</li>'+ '<li>❓ Did this chapter give you any questions you can't wait to find out the answers for?</li>' + '<li>✨ Is there something unique about the story that you like?</li>'+ '<li>🤹 Does the author have a style that really works for you?</li>' + '<li>🎤 Did the author leave any comments in the notes that said what they wanted feedback on?</li>' + '<li>🗣 Even if all you have are incoherent screams of delight, authors love to hear that as well.</li></ul>' ; infoContainer.appendChild(helpDiv); infoContainer.appendChild(tipsDiv); floatyContainer.appendChild(infoContainer); // Floaty container header const header = document.createElement('div'); header.style.cssText = ` display: flex; justify-content: flex-start; align-items: center; align-content: stretch; gap: 0.5em; padding: .5em.9em; font-size: 1em; opacity: 1; `; const logo = document.createElement('div'); logo.innerHTML = '<span>🪶</span>'; const insertBtn = document.createElement('button'); insertBtn.innerHTML = '« »'; const helpBtn = document.createElement('button'); helpBtn.innerHTML = '❓'; const tipsBtn = document.createElement('button'); tipsBtn.innerHTML = '💡'; const closeBtn = document.createElement('button'); closeBtn.textContent = '✖'; const downBtn = document.createElement('button'); downBtn.innerHTML = '⮟'; [insertBtn, helpBtn, tipsBtn, downBtn, closeBtn].forEach(btn => { btn.style.cssText = ` font-family: inherit; color: inherit; cursor: pointer; padding: 0.2em 0.4em; font-size: 1em; min-height: 15px; min-width: 15px; `; }); logo.style.cssText = ` cursor: initial; font-size: 1.2em; padding: 0.2em; ` closeBtn.style.marginLeft = 'auto'; header.append(logo, insertBtn, tipsBtn, helpBtn, downBtn, closeBtn); floatyContainer.appendChild(header); // Textarea const floatyBox = document.createElement('textarea'); floatyBox.placeholder = 'Work-in-progress review...'; floatyBox.id = 'floaty-box'; floatyBox.style.cssText = ` flex: 1; padding: .9em; font-size: 1em; font-family: inherit; color: inherit; background: inherit; border: none; resize: none; outline: none; width: 100%; box-sizing: border-box; `; floatyContainer.appendChild(floatyBox); // Button actions floatyButton.addEventListener('click', () => { floatyContainer.style.display = 'flex'; floatyButton.style.display = 'none'; insertButton.style.display = 'none'; }); closeBtn.addEventListener('click', () => { floatyContainer.style.display = 'none'; floatyButton.style.display = 'block'; insertButton.style.display = 'block'; helpDiv.style.display = 'none'; tipsDiv.style.display = 'none'; }); [insertBtn, insertButton].forEach(btn => { btn.addEventListener('click', () => { const selected = window.getSelection().toString().trim(); if (selected) { floatyBox.focus(); floatyBox.setRangeText(`<i>"${selected}"</i>\n`, floatyBox.selectionStart, floatyBox.selectionEnd, 'end'); floatyBox.dispatchEvent(new Event('input')); // Sync with real box } }); }); helpBtn.addEventListener('click', () => { infoContainer.style.display = infoContainer.style.display === 'none' ? 'flex' : 'none'; helpDiv.style.display = 'block'; tipsDiv.style.display = 'none'; }); tipsBtn.addEventListener('click', () => { infoContainer.style.display = infoContainer.style.display === 'none' ? 'flex' : 'none'; tipsDiv.style.display = 'block'; helpDiv.style.display = 'none'; }); downBtn.addEventListener('click', () => { document.querySelector('textarea[name="comment[comment_content]"]').scrollIntoView(); }); // Sync between floaty and real comment box floatyBox.addEventListener('input', () => { commentBox.value = floatyBox.value; }); commentBox.addEventListener('input', () => { if (commentBox.value !== floatyBox.value) { floatyBox.value = commentBox.value; } }); // [Pending issues] // Prevent unwanted scrolling floatyBox.addEventListener('focus', (e) => { // Prevent mobile "scroll-jump" requestAnimationFrame(() => { floatyBox.scrollIntoView({ block: 'nearest', behavior: 'instant' }); // fallback in case `preventScroll` isn’t supported window.scrollTo(window.scrollX, window.scrollY); }); }); }; function waitForCommentBox(attempts = 0) { // This script only applies to story pages where you can comment, which we need to check for const box = document.querySelector('textarea[name="comment[comment_content]"]'); if (box) { createFloaty(box); } else if (attempts < 20) { setTimeout(() => waitForCommentBox(attempts + 1), 500); } else { console.log("Comment box not found after 20 attempts."); } } function init() { console.log("Init called"); waitForCommentBox(); } window.addEventListener('load', init); document.addEventListener('pjax:end', init); setInterval(() => { if (!floatyCreated) init(); }, 1500); // Safety net for mobile load quirks })();