// ==UserScript==
// @name         고도화된 다중 검색 엔진 크롤러
// @namespace    http://tampermonkey.net/
// @version      2.4
// @description  다양한 검색 엔진의 결과를 크롤링하여 데이터를 수집하고 표시합니다. 페이지 이동 시 데이터 초기화 문제 수정, UI 개선, 로그 기능 추가, HTML 콘텐츠 뷰어, 애니메이션 및 TV 사이트 임베드 등 고도화된 기능을 포함합니다.
// @author       YourName
// @match        *://www.google.com/*
// @match        *://www.bing.com/*
// @match        *://duckduckgo.com/*
// @match        *://뉴토끼.site/*
// @match        *://lettercounter.net/*
// @match        *://tv.54.wiKi/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addValueChangeListener
// @grant        GM_xmlhttpRequest
// @connect      *
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    // 지원하는 검색 엔진 설정
    const searchEngines = {
        'www.google.com': {
            name: 'Google',
            resultSelector: 'div.g',
            titleSelector: 'h3',
            linkSelector: 'a',
            descriptionSelector: 'span.aCOpRe, .VwiC3b',
            nextButtonSelector: '#pnnext'
        },
        'www.bing.com': {
            name: 'Bing',
            resultSelector: 'li.b_algo',
            titleSelector: 'h2',
            linkSelector: 'a',
            descriptionSelector: 'p',
            nextButtonSelector: 'a.sb_pagN'
        },
        'duckduckgo.com': {
            name: 'DuckDuckGo',
            resultSelector: 'div.result',
            titleSelector: 'h2 > a.result__a',
            linkSelector: 'a.result__a',
            descriptionSelector: '.result__snippet',
            nextButtonSelector: 'a.result--more__btn'
        },
        '뉴토끼.site': { // 추가된 검색 엔진 설정
            name: '뉴토끼',
            resultSelector: '.search-result', // 예시 선택자: 실제 사이트의 검색 결과 컨테이너 클래스
            titleSelector: '.result-title > a', // 예시 선택자: 실제 사이트의 제목 링크 클래스
            linkSelector: '.result-title > a', // 예시 선택자: 실제 사이트의 제목 링크 클래스
            descriptionSelector: '.result-description', // 예시 선택자: 실제 사이트의 설명 클래스
            nextButtonSelector: '.pagination-next' // 예시 선택자: 실제 사이트의 다음 페이지 버튼 클래스
        },
        'lettercounter.net': { // 추가된 검색 엔진 설정
            name: 'LetterCounter',
            resultSelector: '.search-results .result-item', // 예시 선택자: 실제 사이트의 검색 결과 아이템 클래스
            titleSelector: '.result-title > a', // 예시 선택자: 실제 사이트의 제목 링크 클래스
            linkSelector: '.result-title > a', // 예시 선택자: 실제 사이트의 제목 링크 클래스
            descriptionSelector: '.result-snippet', // 예시 선택자: 실제 사이트의 설명 클래스
            nextButtonSelector: '.pagination-next' // 예시 선택자: 실제 사이트의 다음 페이지 버튼 클래스
        },
        'tv.54.wiKi': { // 추가된 TV 사이트 설정
            name: 'TV54',
            resultSelector: '.tv-search-result', // 예시 선택자: 실제 TV 사이트의 검색 결과 컨테이너 클래스
            titleSelector: '.tv-result-title > a', // 예시 선택자: 실제 TV 사이트의 제목 링크 클래스
            linkSelector: '.tv-result-title > a', // 예시 선택자: 실제 TV 사이트의 제목 링크 클래스
            descriptionSelector: '.tv-result-description', // 예시 선택자: 실제 TV 사이트의 설명 클래스
            nextButtonSelector: '.tv-pagination-next' // 예시 선택자: 실제 TV 사이트의 다음 페이지 버튼 클래스
        }
        // 필요한 경우 추가 검색 엔진 설정 가능
    };

    // 현재 검색 엔진 감지
    const currentHost = window.location.host;
    const engine = searchEngines[currentHost];
    if (!engine) {
        console.log('지원되지 않는 검색 엔진입니다.');
        return;
    }

    // 초기화 크롤링 상태 (기존 값이 없을 경우에만 설정)
    if (GM_getValue('isCrawling') === undefined) {
        GM_setValue('isCrawling', false);
    }
    if (GM_getValue('crawlData') === undefined) {
        GM_setValue('crawlData', JSON.stringify([]));
    }
    if (GM_getValue('currentPage') === undefined) {
        GM_setValue('currentPage', 1);
    }
    if (GM_getValue('crawlDelay') === undefined) {
        GM_setValue('crawlDelay', 1000);
    }
    if (GM_getValue('logData') === undefined) {
        GM_setValue('logData', JSON.stringify([]));
    }
    if (GM_getValue('tvPage') === undefined) {
        GM_setValue('tvPage', 54); // 초기 TV 페이지 번호 설정
    }

    // UI 생성 (한 번만 생성)
    if (!document.getElementById('crawlUI')) {
        createUI();
    }

    // UI 업데이트 함수
    function updateUI() {
        const isCrawling = GM_getValue('isCrawling');
        document.getElementById('startButton').disabled = isCrawling;
        document.getElementById('stopButton').disabled = !isCrawling;
        document.getElementById('progressBar').value = getProgressPercentage();
        document.getElementById('progressText').innerText = `${getProgressPercentage()}% 완료`;
        document.getElementById('currentPageIndicator').innerText = `📄 현재 페이지: ${GM_getValue('currentPage')}`;
        document.getElementById('currentTVPageIndicator').innerText = `📺 현재 TV 페이지: ${GM_getValue('tvPage')}`;
    }

    // 크롤링 상태 변경 리스너
    GM_addValueChangeListener('isCrawling', (name, oldValue, newValue, remote) => {
        updateUI();
        log(`🔄 크롤링 상태 변경: ${newValue ? '시작됨' : '중지됨'}`);
    });

    // UI 생성 함수
    function createUI() {
        const container = document.createElement('div');
        container.id = 'crawlUI';
        container.style.position = 'fixed';
        container.style.top = '20px';
        container.style.right = '20px';
        container.style.padding = '20px';
        container.style.backgroundColor = 'rgba(255, 255, 255, 0.95)';
        container.style.border = '2px solid #4CAF50';
        container.style.borderRadius = '10px';
        container.style.zIndex = 10000;
        container.style.boxShadow = '0 0 20px rgba(0,0,0,0.4)';
        container.style.fontFamily = 'Arial, sans-serif';
        container.style.fontSize = '14px';
        container.style.width = '600px';
        container.style.maxHeight = '95vh';
        container.style.overflowY = 'auto';

        // 제목
        const title = document.createElement('h2');
        title.innerText = '🔍 검색 엔진 크롤러';
        title.style.margin = '0 0 20px 0';
        title.style.fontSize = '20px';
        title.style.color = '#4CAF50';
        container.appendChild(title);

        // 크롤링 속도 설정
        const speedContainer = document.createElement('div');
        speedContainer.style.marginBottom = '20px';

        const speedLabel = document.createElement('label');
        speedLabel.innerText = '🕒 크롤링 속도 (ms): ';
        speedLabel.setAttribute('for', 'crawlDelayInput');
        speedLabel.style.display = 'block';
        speedLabel.style.marginBottom = '5px';
        speedLabel.style.fontWeight = 'bold';
        speedContainer.appendChild(speedLabel);

        const speedInput = document.createElement('input');
        speedInput.type = 'number';
        speedInput.id = 'crawlDelayInput';
        speedInput.value = GM_getValue('crawlDelay');
        speedInput.min = 100;
        speedInput.style.width = '100%';
        speedInput.style.padding = '8px';
        speedInput.style.border = '1px solid #ccc';
        speedInput.style.borderRadius = '4px';
        speedInput.style.boxSizing = 'border-box';
        speedInput.onchange = () => {
            const val = parseInt(speedInput.value);
            if (isNaN(val) || val < 100) {
                alert('유효한 숫자를 입력하세요 (최소 100ms).');
                speedInput.value = GM_getValue('crawlDelay');
            } else {
                GM_setValue('crawlDelay', val);
                log(`🕒 크롤링 속도 변경: ${val}ms`);
            }
        };
        speedContainer.appendChild(speedInput);
        container.appendChild(speedContainer);

        // 버튼 생성
        const buttonContainer = document.createElement('div');
        buttonContainer.style.display = 'flex';
        buttonContainer.style.justifyContent = 'space-between';
        buttonContainer.style.marginBottom = '20px';

        const startButton = document.createElement('button');
        startButton.id = 'startButton';
        startButton.innerText = '▶️ 시작';
        startButton.style.flex = '1';
        startButton.style.marginRight = '10px';
        startButton.style.padding = '10px';
        startButton.style.backgroundColor = '#4CAF50';
        startButton.style.color = 'white';
        startButton.style.border = 'none';
        startButton.style.borderRadius = '4px';
        startButton.style.cursor = 'pointer';
        startButton.style.fontSize = '16px';
        startButton.onclick = startCrawling;
        buttonContainer.appendChild(startButton);

        const stopButton = document.createElement('button');
        stopButton.id = 'stopButton';
        stopButton.innerText = '⏹️ 중지';
        stopButton.style.flex = '1';
        stopButton.style.marginLeft = '10px';
        stopButton.style.padding = '10px';
        stopButton.style.backgroundColor = '#f44336';
        stopButton.style.color = 'white';
        stopButton.style.border = 'none';
        stopButton.style.borderRadius = '4px';
        stopButton.style.cursor = 'pointer';
        stopButton.style.fontSize = '16px';
        stopButton.onclick = stopCrawling;
        stopButton.disabled = !GM_getValue('isCrawling');
        buttonContainer.appendChild(stopButton);

        container.appendChild(buttonContainer);

        // 초기화 버튼
        const resetButton = document.createElement('button');
        resetButton.innerText = '🔄 초기화';
        resetButton.style.width = '100%';
        resetButton.style.padding = '10px';
        resetButton.style.backgroundColor = '#555555';
        resetButton.style.color = 'white';
        resetButton.style.border = 'none';
        resetButton.style.borderRadius = '4px';
        resetButton.style.cursor = 'pointer';
        resetButton.style.fontSize = '16px';
        resetButton.style.marginBottom = '20px';
        resetButton.onclick = resetCrawling;
        container.appendChild(resetButton);

        // 진행률 표시
        const progressContainer = document.createElement('div');
        progressContainer.style.marginBottom = '20px';

        const progressLabel = document.createElement('label');
        progressLabel.innerText = '📊 진행률: ';
        progressLabel.style.display = 'block';
        progressLabel.style.marginBottom = '5px';
        progressLabel.style.fontWeight = 'bold';
        progressContainer.appendChild(progressLabel);

        const progressBar = document.createElement('progress');
        progressBar.id = 'progressBar';
        progressBar.value = getProgressPercentage();
        progressBar.max = 100;
        progressBar.style.width = '100%';
        progressBar.style.height = '20px';
        progressBar.style.marginBottom = '5px';
        progressContainer.appendChild(progressBar);

        const progressText = document.createElement('span');
        progressText.id = 'progressText';
        progressText.innerText = `${getProgressPercentage()}% 완료`;
        progressText.style.fontWeight = 'bold';
        progressContainer.appendChild(progressText);

        container.appendChild(progressContainer);

        // 현재 페이지 표시
        const pageIndicator = document.createElement('div');
        pageIndicator.id = 'currentPageIndicator';
        pageIndicator.innerText = `📄 현재 페이지: ${GM_getValue('currentPage')}`;
        pageIndicator.style.marginBottom = '20px';
        pageIndicator.style.fontWeight = 'bold';
        container.appendChild(pageIndicator);

        // 현재 TV 페이지 표시
        const tvPageIndicator = document.createElement('div');
        tvPageIndicator.id = 'currentTVPageIndicator';
        tvPageIndicator.innerText = `📺 현재 TV 페이지: ${GM_getValue('tvPage')}`;
        tvPageIndicator.style.marginBottom = '20px';
        tvPageIndicator.style.fontWeight = 'bold';
        container.appendChild(tvPageIndicator);

        // 로그 창
        const logContainer = document.createElement('div');
        logContainer.style.marginBottom = '20px';

        const logTitle = document.createElement('h3');
        logTitle.innerText = '📝 로그';
        logTitle.style.marginBottom = '10px';
        logTitle.style.fontSize = '18px';
        logTitle.style.color = '#333333';
        logContainer.appendChild(logTitle);

        const logArea = document.createElement('textarea');
        logArea.id = 'crawlLog';
        logArea.style.width = '100%';
        logArea.style.height = '150px';
        logArea.style.padding = '10px';
        logArea.style.border = '1px solid #ccc';
        logArea.style.borderRadius = '4px';
        logArea.style.resize = 'none';
        logArea.style.fontFamily = 'Courier New, monospace';
        logArea.style.fontSize = '12px';
        logArea.disabled = true;
        logContainer.appendChild(logArea);

        container.appendChild(logContainer);

        // 결과 테이블 생성
        const tableContainer = document.createElement('div');
        tableContainer.style.marginBottom = '20px';
        tableContainer.style.maxHeight = '300px';
        tableContainer.style.overflowY = 'auto';
        tableContainer.style.border = '1px solid #ddd';
        tableContainer.style.borderRadius = '4px';
        tableContainer.style.padding = '10px';
        tableContainer.style.backgroundColor = '#f9f9f9';

        const table = document.createElement('table');
        table.id = 'crawlResults';
        table.style.width = '100%';
        table.style.borderCollapse = 'collapse';

        const header = table.insertRow();
        ['📰 사이트 이름', '🔗 주소', '📝 설명', '📄 HTML 보기'].forEach(text => {
            const th = document.createElement('th');
            th.innerText = text;
            th.style.border = '1px solid #ddd';
            th.style.padding = '10px';
            th.style.backgroundColor = '#f2f2f2';
            th.style.textAlign = 'left';
            th.style.position = 'sticky';
            th.style.top = '0';
            th.style.background = '#f2f2f2';
            th.style.zIndex = '1';
            table.appendChild(th);
        });

        const tbody = document.createElement('tbody');
        tbody.id = 'crawlTableBody';
        table.appendChild(tbody);

        tableContainer.appendChild(table);
        container.appendChild(tableContainer);

        // HTML 뷰어 섹션 추가
        const htmlViewerContainer = document.createElement('div');
        htmlViewerContainer.id = 'htmlViewerContainer';
        htmlViewerContainer.style.display = 'none';
        htmlViewerContainer.style.marginBottom = '20px';
        htmlViewerContainer.style.border = '1px solid #ddd';
        htmlViewerContainer.style.borderRadius = '4px';
        htmlViewerContainer.style.padding = '10px';
        htmlViewerContainer.style.backgroundColor = '#f1f1f1';

        const htmlViewerTitle = document.createElement('h3');
        htmlViewerTitle.innerText = '📄 HTML 뷰어';
        htmlViewerTitle.style.marginBottom = '10px';
        htmlViewerTitle.style.fontSize = '18px';
        htmlViewerTitle.style.color = '#333333';
        htmlViewerContainer.appendChild(htmlViewerTitle);

        const closeViewerButton = document.createElement('button');
        closeViewerButton.innerText = '❌ 닫기';
        closeViewerButton.style.marginBottom = '10px';
        closeViewerButton.style.padding = '5px 10px';
        closeViewerButton.style.backgroundColor = '#f44336';
        closeViewerButton.style.color = 'white';
        closeViewerButton.style.border = 'none';
        closeViewerButton.style.borderRadius = '4px';
        closeViewerButton.style.cursor = 'pointer';
        closeViewerButton.style.fontSize = '14px';
        closeViewerButton.onclick = () => {
            htmlViewerContainer.style.display = 'none';
            htmlViewerContent.src = '';
        };
        htmlViewerContainer.appendChild(closeViewerButton);

        const htmlViewerContent = document.createElement('iframe');
        htmlViewerContent.id = 'htmlViewerContent';
        htmlViewerContent.style.width = '100%';
        htmlViewerContent.style.height = '400px';
        htmlViewerContent.style.border = '1px solid #ccc';
        htmlViewerContent.style.borderRadius = '4px';
        htmlViewerContent.style.marginTop = '10px';
        htmlViewerContainer.appendChild(htmlViewerContent);

        container.appendChild(htmlViewerContainer);

        // 애니메이션 사이트 뷰어 추가
        const animeViewerContainer = document.createElement('div');
        animeViewerContainer.id = 'animeViewerContainer';
        animeViewerContainer.style.marginBottom = '20px';
        animeViewerContainer.style.border = '1px solid #ddd';
        animeViewerContainer.style.borderRadius = '4px';
        animeViewerContainer.style.padding = '10px';
        animeViewerContainer.style.backgroundColor = '#f1f1f1';

        const animeViewerTitle = document.createElement('h3');
        animeViewerTitle.innerText = '🎬 애니메이션 보기';
        animeViewerTitle.style.marginBottom = '10px';
        animeViewerTitle.style.fontSize = '18px';
        animeViewerTitle.style.color = '#333333';
        animeViewerContainer.appendChild(animeViewerTitle);

        const animeViewerFrame = document.createElement('iframe');
        animeViewerFrame.src = 'https://linkkf.net/';
        animeViewerFrame.style.width = '100%';
        animeViewerFrame.style.height = '500px';
        animeViewerFrame.style.border = '1px solid #ccc';
        animeViewerFrame.style.borderRadius = '4px';
        animeViewerFrame.style.marginTop = '10px';
        animeViewerContainer.appendChild(animeViewerFrame);

        container.appendChild(animeViewerContainer);

        // TV 사이트 뷰어 추가
        const tvViewerContainer = document.createElement('div');
        tvViewerContainer.id = 'tvViewerContainer';
        tvViewerContainer.style.marginBottom = '20px';
        tvViewerContainer.style.border = '1px solid #ddd';
        tvViewerContainer.style.borderRadius = '4px';
        tvViewerContainer.style.padding = '10px';
        tvViewerContainer.style.backgroundColor = '#f1f1f1';

        const tvViewerTitle = document.createElement('h3');
        tvViewerTitle.innerText = '📺 TV 사이트 보기';
        tvViewerTitle.style.marginBottom = '10px';
        tvViewerTitle.style.fontSize = '18px';
        tvViewerTitle.style.color = '#333333';
        tvViewerContainer.appendChild(tvViewerTitle);

        const tvViewerFrame = document.createElement('iframe');
        tvViewerFrame.id = 'tvViewerFrame';
        tvViewerFrame.src = `https://tv.${GM_getValue('tvPage')}.wiKi/`;
        tvViewerFrame.style.width = '100%';
        tvViewerFrame.style.height = '500px';
        tvViewerFrame.style.border = '1px solid #ccc';
        tvViewerFrame.style.borderRadius = '4px';
        tvViewerFrame.style.marginTop = '10px';
        tvViewerFrame.onload = () => {
            // 페이지가 성공적으로 로드되면 로그에 기록
            log(`✅ TV 사이트 로드 완료: https://tv.${GM_getValue('tvPage')}.wiKi/`);
        };
        tvViewerFrame.onerror = () => {
            // 페이지 로드 실패 시 로그에 기록
            log(`❌ TV 사이트 로드 실패: https://tv.${GM_getValue('tvPage')}.wiKi/`);
            alert(`TV 사이트 로드 실패: https://tv.${GM_getValue('tvPage')}.wiKi/`);
        };
        tvViewerContainer.appendChild(tvViewerFrame);

        // 새로 고침 버튼 추가
        const refreshTVButton = document.createElement('button');
        refreshTVButton.innerText = '🔄 TV 사이트 새로 고침';
        refreshTVButton.style.marginTop = '10px';
        refreshTVButton.style.padding = '8px 12px';
        refreshTVButton.style.backgroundColor = '#2196F3';
        refreshTVButton.style.color = 'white';
        refreshTVButton.style.border = 'none';
        refreshTVButton.style.borderRadius = '4px';
        refreshTVButton.style.cursor = 'pointer';
        refreshTVButton.style.fontSize = '14px';
        refreshTVButton.onclick = refreshTVSite;
        tvViewerContainer.appendChild(refreshTVButton);

        container.appendChild(tvViewerContainer);

        document.body.appendChild(container);

        // 초기 테이블 데이터 로드
        loadCrawlData();
        loadLogData();
    }

    // 크롤링 시작 함수
    function startCrawling() {
        if (GM_getValue('isCrawling')) return;
        GM_setValue('isCrawling', true);
        log('🔍 크롤링 시작');
        if (GM_getValue('currentPage') === 1) {
            GM_setValue('crawlData', JSON.stringify([]));
            clearTable();
            clearLog();
        }
        crawlPage();
    }

    // 크롤링 중지 함수
    function stopCrawling() {
        if (!GM_getValue('isCrawling')) return;
        GM_setValue('isCrawling', false);
        log('⏹️ 크롤링 중지');
    }

    // 크롤링 초기화 함수
    function resetCrawling() {
        stopCrawling();
        GM_setValue('crawlData', JSON.stringify([]));
        GM_setValue('currentPage', 1);
        GM_setValue('tvPage', 54); // TV 페이지 번호 초기화
        clearTable();
        clearLog();
        log('🔄 크롤링 초기화 완료');
    }

    // 테이블 초기화
    function clearTable() {
        const tbody = document.getElementById('crawlTableBody');
        if (tbody) {
            tbody.innerHTML = '';
        }
    }

    // 로그 초기화
    function clearLog() {
        GM_setValue('logData', JSON.stringify([]));
        loadLogData();
    }

    // 크롤링 데이터 로드 및 테이블 업데이트
    function loadCrawlData() {
        const crawlData = JSON.parse(GM_getValue('crawlData'));
        crawlData.forEach(item => {
            addRowToTable(item.title, item.url, item.description);
        });
    }

    // 로그 데이터 로드 및 로그 표시
    function loadLogData() {
        const logData = JSON.parse(GM_getValue('logData'));
        const logArea = document.getElementById('crawlLog');
        logArea.value = logData.join('\n');
        logArea.scrollTop = logArea.scrollHeight;
    }

    // 로그 추가 함수
    function log(message) {
        const timestamp = new Date().toLocaleTimeString();
        const logEntry = `[${timestamp}] ${message}`;
        let logData = JSON.parse(GM_getValue('logData'));
        logData.push(logEntry);
        GM_setValue('logData', JSON.stringify(logData));

        const logArea = document.getElementById('crawlLog');
        logArea.value += logEntry + '\n';
        logArea.scrollTop = logArea.scrollHeight;
    }

    // 진행률 계산 함수
    function getProgressPercentage() {
        // 임시: 최대 페이지 수를 100으로 설정
        const maxPages = 100;
        const currentPage = GM_getValue('currentPage');
        return Math.min(Math.floor((currentPage / maxPages) * 100), 100);
    }

    // 크롤링 페이지 처리
    function crawlPage() {
        if (!GM_getValue('isCrawling')) return;

        log(`📄 페이지 ${GM_getValue('currentPage')} 크롤링 중...`);

        let crawlData = JSON.parse(GM_getValue('crawlData'));
        const results = document.querySelectorAll(engine.resultSelector);
        results.forEach(result => {
            // 광고 제외 로직 (예: 특정 클래스나 도메인 필터링)
            if (isAd(result)) return;

            const titleElement = result.querySelector(engine.titleSelector);
            const linkElement = result.querySelector(engine.linkSelector);
            const descriptionElement = result.querySelector(engine.descriptionSelector);

            if (titleElement && linkElement) {
                const title = titleElement.innerText.trim();
                const url = linkElement.href;
                const description = descriptionElement ? descriptionElement.innerText.trim() : '';

                // 중복 검사
                if (!crawlData.some(item => item.url === url)) {
                    crawlData.push({ title, url, description });
                    addRowToTable(title, url, description);
                    log(`✅ 데이터 수집: ${title} - ${url}`);
                }
            }
        });

        GM_setValue('crawlData', JSON.stringify(crawlData));
        updateUI();

        // 다음 페이지로 이동
        const nextButton = document.querySelector(engine.nextButtonSelector);
        if (nextButton && GM_getValue('currentPage') < 100 && GM_getValue('isCrawling')) { // 최대 페이지 수 설정
            GM_setValue('currentPage', GM_getValue('currentPage') + 1);
            updateUI();
            setTimeout(() => {
                nextButton.click();
            }, GM_getValue('crawlDelay'));
            log(`➡️ 다음 페이지로 이동: ${GM_getValue('currentPage')}`);
        } else {
            GM_setValue('isCrawling', false);
            log('🏁 크롤링 완료');
            alert('크롤링이 완료되었습니다!');
        }
    }

    // 테이블에 데이터 추가
    function addRowToTable(title, url, description) {
        const tbody = document.getElementById('crawlTableBody');
        if (!tbody) return;

        const row = tbody.insertRow();

        const titleCell = row.insertCell();
        titleCell.innerText = title;
        titleCell.style.border = '1px solid #ddd';
        titleCell.style.padding = '10px';
        titleCell.style.fontWeight = 'bold';

        const urlCell = row.insertCell();
        const link = document.createElement('a');
        link.href = url;
        link.innerText = url;
        link.target = '_blank';
        link.style.color = '#1a0dab';
        urlCell.appendChild(link);
        urlCell.style.border = '1px solid #ddd';
        urlCell.style.padding = '10px';
        urlCell.style.wordBreak = 'break-all';

        const descCell = row.insertCell();
        descCell.innerText = description;
        descCell.style.border = '1px solid #ddd';
        descCell.style.padding = '10px';
        descCell.style.color = '#555555';

        const viewHtmlCell = row.insertCell();
        const viewHtmlButton = document.createElement('button');
        viewHtmlButton.innerText = '📄 HTML 보기';
        viewHtmlButton.style.padding = '5px 10px';
        viewHtmlButton.style.backgroundColor = '#2196F3';
        viewHtmlButton.style.color = 'white';
        viewHtmlButton.style.border = 'none';
        viewHtmlButton.style.borderRadius = '4px';
        viewHtmlButton.style.cursor = 'pointer';
        viewHtmlButton.style.fontSize = '14px';
        viewHtmlButton.onclick = () => viewHTML(url);
        viewHtmlCell.appendChild(viewHtmlButton);
        viewHtmlCell.style.border = '1px solid #ddd';
        viewHtmlCell.style.padding = '10px';
    }

    // HTML 뷰어 열기
    function viewHTML(url) {
        log(`🔗 HTML 요청: ${url}`);
        GM_xmlhttpRequest({
            method: 'GET',
            url: url,
            onload: function(response) {
                if (response.status >= 200 && response.status < 300) {
                    displayHTML(response.responseText, url);
                    log(`✅ HTML 로드 완료: ${url}`);
                } else {
                    log(`❌ HTML 로드 실패 (${response.status}): ${url}`);
                    alert(`HTML 로드 실패: ${response.status} ${response.statusText}`);
                }
            },
            onerror: function(err) {
                log(`⚠️ HTML 요청 에러: ${url}`);
                alert(`HTML 요청 에러: ${err}`);
            }
        });
    }

    // HTML을 뷰어에 표시
    function displayHTML(htmlContent, url) {
        const htmlViewerContainer = document.getElementById('htmlViewerContainer');
        const htmlViewerContent = document.getElementById('htmlViewerContent');

        // Blob을 사용하여 안전하게 iframe에 로드
        const blob = new Blob([htmlContent], { type: 'text/html' });
        const blobURL = URL.createObjectURL(blob);
        htmlViewerContent.src = blobURL;

        htmlViewerContainer.style.display = 'block';
    }

    // TV 사이트 새로 고침 함수
    function refreshTVSite() {
        let currentTVPage = GM_getValue('tvPage');
        currentTVPage += 1;
        GM_setValue('tvPage', currentTVPage);
        const tvFrame = document.getElementById('tvViewerFrame');
        tvFrame.src = `https://tv.${currentTVPage}.wiKi/`;
        log(`📺 TV 사이트 새로 고침: https://tv.${currentTVPage}.wiKi/`);
    }

    // 페이지 이동 시 크롤링 계속
    window.addEventListener('load', () => {
        if (GM_getValue('isCrawling')) {
            setTimeout(() => {
                crawlPage();
            }, GM_getValue('crawlDelay'));
        }
    });

    // 광고 제외 필터링 함수 (필요 시 확장 가능)
    function isAd(resultElement) {
        // 예시: 광고로 간주되는 특정 클래스나 속성 체크
        const adClasses = ['ad', 'ads', 'commercial'];
        return adClasses.some(cls => resultElement.classList.contains(cls));
    }

    updateUI();

})();                   ui 개선해 ! 파이어폭스 ui 처럼!