您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
주석 번호와 설명을 복사할 수 있는 패널을 작고 세련된 UI로 제공하며, 페이지 내용과 URL 변경 시 자동 갱신합니다. iOS 호환 복사 기능 포함.
// ==UserScript== // @name 나무위키 주석 추출기 (iOS 버전) // @namespace http://tampermonkey.net/ // @version 1.0 // @description 주석 번호와 설명을 복사할 수 있는 패널을 작고 세련된 UI로 제공하며, 페이지 내용과 URL 변경 시 자동 갱신합니다. iOS 호환 복사 기능 포함. // @match https://namu.wiki/* // ==/UserScript== (function () { 'use strict'; // 공통 색상 const green = '#4CAF50'; const greenTransparent = 'rgba(76, 175, 80, 0.8)'; // UI 관련 전역 변수 let collected = []; let panel, copyBtn, pre; let fixedContainer, smallButton, expandBtn; // 주석 수집 및 UI 업데이트 함수 function collectAndUpdate() { const bodyText = document.body.innerText; const notes = [...bodyText.matchAll(/\[(\d+)\]/g)]; const uniqueNotes = [...new Set(notes.map(m => m[0]))]; const newCollected = []; uniqueNotes.forEach(n => { const parts = bodyText.split(n); if (parts.length >= 3) { const afterSecond = parts[2].trim(); const nextLine = afterSecond.split(/\n/)[0]; newCollected.push(`${n} ${nextLine}`); } }); if (JSON.stringify(newCollected) !== JSON.stringify(collected)) { collected = newCollected; if (collected.length > 0) { pre.innerText = collected.join('\n'); } else { pre.innerText = '(복사할 주석이 없습니다)'; } } } // 복사 함수 (iOS Safari 대응) function copyText(text) { try { if (typeof GM_setClipboard === 'function') { GM_setClipboard(text); } else { const ta = document.createElement('textarea'); ta.value = text; ta.style.position = 'fixed'; ta.style.top = '-9999px'; document.body.appendChild(ta); ta.focus(); ta.select(); document.execCommand('copy'); document.body.removeChild(ta); } } catch (e) { alert('복사 실패: ' + e.message); } } // UI 생성 함수 function createUI() { panel = document.createElement('div'); panel.style.position = 'fixed'; panel.style.bottom = '20px'; panel.style.right = '20px'; panel.style.zIndex = 9999; panel.style.background = 'white'; panel.style.border = '1px solid #ccc'; panel.style.padding = '10px'; panel.style.boxShadow = '0 2px 10px rgba(0,0,0,0.2)'; panel.style.maxWidth = '300px'; panel.style.maxHeight = '400px'; panel.style.overflow = 'auto'; panel.style.fontSize = '13px'; panel.style.lineHeight = '1.4'; panel.style.display = 'none'; copyBtn = document.createElement('button'); copyBtn.innerText = '주석 복사'; copyBtn.style.background = green; copyBtn.style.color = 'white'; copyBtn.style.border = 'none'; copyBtn.style.padding = '6px 12px'; copyBtn.style.fontSize = '13px'; copyBtn.style.borderRadius = '4px'; copyBtn.style.cursor = 'pointer'; copyBtn.onclick = () => { if (collected.length > 0) { copyText(collected.join('\n')); copyBtn.innerText = '복사됨!'; setTimeout(() => { copyBtn.innerText = '주석 복사'; }, 1500); } }; pre = document.createElement('pre'); pre.style.whiteSpace = 'pre-wrap'; pre.innerText = '(복사할 주석이 없습니다)'; panel.appendChild(copyBtn); panel.appendChild(document.createElement('hr')); panel.appendChild(pre); const collapseBtn = document.createElement('div'); collapseBtn.innerHTML = ` <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="white" viewBox="0 0 24 24"> <path d="M19 13H5v-2h14v2z"/> </svg>`; collapseBtn.style.position = 'absolute'; collapseBtn.style.top = '6px'; collapseBtn.style.right = '6px'; collapseBtn.style.width = '20px'; collapseBtn.style.height = '20px'; collapseBtn.style.background = 'rgba(255, 0, 0, 0.8)'; collapseBtn.style.borderRadius = '4px'; collapseBtn.style.display = 'flex'; collapseBtn.style.alignItems = 'center'; collapseBtn.style.justifyContent = 'center'; collapseBtn.style.cursor = 'pointer'; collapseBtn.title = '최소화'; collapseBtn.onclick = () => { panel.style.display = 'none'; fixedContainer.style.display = 'flex'; }; panel.appendChild(collapseBtn); fixedContainer = document.createElement('div'); fixedContainer.style.position = 'fixed'; fixedContainer.style.bottom = '20px'; fixedContainer.style.right = '20px'; fixedContainer.style.zIndex = 9998; fixedContainer.style.display = 'flex'; fixedContainer.style.alignItems = 'center'; fixedContainer.style.gap = '4px'; smallButton = document.createElement('button'); smallButton.innerText = '주석 복사'; smallButton.style.background = green; smallButton.style.color = 'white'; smallButton.style.border = 'none'; smallButton.style.padding = '6px 12px'; smallButton.style.fontSize = '13px'; smallButton.style.borderRadius = '4px'; smallButton.style.cursor = 'pointer'; smallButton.style.flexShrink = '0'; smallButton.style.height = '28px'; smallButton.onclick = () => { if (collected.length > 0) { copyText(collected.join('\n')); smallButton.innerText = '복사됨!'; setTimeout(() => { smallButton.innerText = '주석 복사'; }, 1500); } }; expandBtn = document.createElement('button'); expandBtn.title = '펼치기'; expandBtn.style.background = greenTransparent; expandBtn.style.border = 'none'; expandBtn.style.borderRadius = '4px'; expandBtn.style.height = '28px'; expandBtn.style.width = '28px'; expandBtn.style.padding = '0'; expandBtn.style.display = 'flex'; expandBtn.style.alignItems = 'center'; expandBtn.style.justifyContent = 'center'; expandBtn.style.cursor = 'pointer'; expandBtn.style.flexShrink = '0'; expandBtn.innerHTML = ` <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="white" viewBox="0 0 24 24"> <path d="M4 4h6v2H6v4H4V4zm16 0v6h-2V6h-4V4h6zm0 16h-6v-2h4v-4h2v6zM4 20v-6h2v4h4v2H4z"/> </svg>`; expandBtn.onclick = () => { panel.style.display = 'block'; fixedContainer.style.display = 'none'; }; fixedContainer.appendChild(smallButton); fixedContainer.appendChild(expandBtn); document.body.appendChild(panel); document.body.appendChild(fixedContainer); } // URL 변경 감지용 함수 function onUrlChange(callback) { window.addEventListener('popstate', callback); const pushState = history.pushState; history.pushState = function (...args) { const result = pushState.apply(this, args); callback(); return result; }; const replaceState = history.replaceState; history.replaceState = function (...args) { const result = replaceState.apply(this, args); callback(); return result; }; } // 초기 실행 createUI(); collectAndUpdate(); // DOM 변화 감지 const observer = new MutationObserver(() => { collectAndUpdate(); }); observer.observe(document.body, { childList: true, subtree: true, characterData: true }); // URL 변경 시 수집 onUrlChange(() => { setTimeout(collectAndUpdate, 500); }); })();