Via 浏览器 404 和网络连接失败页面美化

为 Via 浏览器错误页面(404 Not Found 和网络连接失败)注入美化,404 页面采用幻境风格,网络连接失败页面采用生命美学与科幻风格,支持日夜模式自动切换

// ==UserScript==
// @name         Via 浏览器 404 和网络连接失败页面美化
// @namespace    https://viayoo.com/
// @version      1.11
// @description  为 Via 浏览器错误页面(404 Not Found 和网络连接失败)注入美化,404 页面采用幻境风格,网络连接失败页面采用生命美学与科幻风格,支持日夜模式自动切换
// @author       是小白呀 & Grok
// @match        *://*/*
// @license      MIT
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // 提取错误页面中的 URL(用于网络连接失败页面)
    function extractErrorUrl() {
        const bodyText = document.body ? document.body.innerText : '';
        const urlMatch = bodyText.match(/(https?:\/\/[^\s]+)/i);
        return urlMatch ? urlMatch[0] : '未知地址';
    }

    // 提取错误代码和服务器信息(用于 404 页面)
    function extractErrorInfo() {
        const bodyText = document.body ? document.body.innerText : '';
        const errorCodeMatch = bodyText.match(/net::ERR_[A-Z_]+/) || document.title.match(/404 Not Found/) || ['404 Not Found'];
        const serverMatch = bodyText.match(/nginx/i) ? 'nginx' : '';
        return {
            errorCode: errorCodeMatch[0],
            server: serverMatch
        };
    }

    // 检查是否为 404 错误页面(严格匹配“404 Not Found”和“nginx”)
    function checkFor404Page() {
        let has404 = false;
        let hasNginx = false;

        // 检查标题
        if (/404 Not Found/i.test(document.title)) {
            has404 = true;
        }
        // 检查 body 内容
        if (document.body) {
            const text = document.body.innerText || '';
            if (/404 Not Found/i.test(text)) {
                has404 = true;
            }
            if (/nginx/i.test(text)) {
                hasNginx = true;
            }
        }
        // 必须同时满足“404 Not Found”和“nginx”
        return has404 && hasNginx;
    }

    // 检查是否为网络连接失败页面
    function checkForConnectionErrorPage() {
        // 排除 404 页面
        if (checkFor404Page()) {
            return false;
        }
        // 快速检查标题
        if (/无法打开|Error/i.test(document.title)) {
            return true;
        }
        // 检查 body 内容
        if (document.body) {
            const text = document.body.innerText || '';
            if (/net::ERR_|网页无法打开|无法连接|ERR_NAME_NOT_RESOLVED/i.test(text)) {
                return true;
            }
            // 空页面
            if (document.body.innerHTML.trim() === '') {
                return true;
            }
        }
        return false;
    }

    // 替换 404 错误页面(幻境风格)
    function replace404Page() {
        const { errorCode, server } = extractErrorInfo();
        document.documentElement.innerHTML = `
            <!DOCTYPE html>
            <html lang="zh-CN">
            <head>
                <meta charset="UTF-8">
                <meta name="viewport" content="width=device-width, initial-scale=1.0">
                <title>迷失幻境</title>
                <style>
                    * {
                        margin: 0;
                        padding: 0;
                        box-sizing: border-box;
                        font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
                    }
                    body {
                        height: 100vh;
                        display: flex;
                        justify-content: center;
                        align-items: center;
                        overflow: hidden;
                        position: relative;
                        margin: 0;
                    }
                    /* 夜间模式(幻境风格) */
                    @media (prefers-color-scheme: dark) {
                        body {
                            background: radial-gradient(ellipse at center, #1a1a3a 0%, #0a0a1a 70%, #000000 100%);
                        }
                        .fog-layer {
                            background: linear-gradient(45deg, rgba(74, 0, 224, 0.1), rgba(0, 210, 255, 0.1));
                            animation: fogFlow 20s infinite linear;
                        }
                        .mist {
                            background: radial-gradient(circle, rgba(74, 0, 224, 0.2) 0%, rgba(74, 0, 224, 0) 70%);
                            filter: blur(30px);
                        }
                        .fragment {
                            border: 1px solid rgba(100, 100, 255, 0.3);
                            box-shadow: 0 0 20px rgba(74, 0, 224, 0.4);
                        }
                        .star-dust {
                            background: #b8b8ff;
                        }
                        .ripple {
                            border: 2px solid rgba(100, 100, 255, 0.2);
                            box-shadow: 0 0 20px rgba(74, 0, 224, 0.3);
                        }
                        .btn-particle {
                            background: #b8b8ff;
                        }
                        h1, .icon, .error-code, .server {
                            background: linear-gradient(135deg, #8e2de2, #4a00e0, #00d2ff);
                            -webkit-background-clip: text;
                            background-clip: text;
                            color: transparent;
                            text-shadow: 0 0 15px rgba(74, 0, 224, 0.5);
                        }
                        p {
                            color: #b8b8ff;
                            text-shadow: 0 0 10px rgba(74, 0, 224, 0.3);
                        }
                        .btn {
                            background: linear-gradient(135deg, #4a00e0, #8e2de2);
                            box-shadow: 0 4px 15px rgba(74, 0, 224, 0.4);
                        }
                        .btn:hover {
                            box-shadow: 0 8px 20px rgba(74, 0, 224, 0.6);
                        }
                    }
                    /* 白天模式(幻境风格) */
                    @media (prefers-color-scheme: light) {
                        body {
                            background: radial-gradient(ellipse at center, #e6e6fa 0%, #f0f0ff 70%, #ffffff 100%);
                        }
                        .fog-layer {
                            background: linear-gradient(45deg, rgba(79, 209, 197, 0.1), rgba(246, 224, 94, 0.1));
                            animation: fogFlow 20s infinite linear;
                        }
                        .mist {
                            background: radial-gradient(circle, rgba(246, 224, 94, 0.3) 0%, rgba(246, 224, 94, 0) 70%);
                            filter: blur(25px);
                        }
                        .fragment {
                            border: 1px solid rgba(79, 209, 197, 0.3);
                            box-shadow: 0 0 20px rgba(246, 224, 94, 0.4);
                        }
                        .star-dust {
                            background: #f6e05e;
                        }
                        .ripple {
                            border: 2px solid rgba(79, 209, 197, 0.2);
                            box-shadow: 0 0 20px rgba(246, 224, 94, 0.3);
                        }
                        .btn-particle {
                            background: #f6e05e;
                        }
                        h1, .icon, .error-code, .server {
                            background: linear-gradient(135deg, #4fd1c5, #f6e05e, #63b3ed);
                            -webkit-background-clip: text;
                            background-clip: text;
                            color: transparent;
                            text-shadow: 0 0 15px rgba(246, 224, 94, 0.5);
                        }
                        p {
                            color: #2d3748;
                            text-shadow: 0 0 10px rgba(246, 224, 94, 0.3);
                        }
                        .btn {
                            background: linear-gradient(135deg, #4fd1c5, #f6e05e);
                            box-shadow: 0 4px 15px rgba(79, 209, 197, 0.3);
                        }
                        .btn:hover {
                            box-shadow: 0 8px 20px rgba(79, 209, 197, 0.5);
                        }
                    }
                    /* 幻境元素 */
                    .fog-layer {
                        position: absolute;
                        width: 200%;
                        height: 200%;
                        top: -50%;
                        left: -50%;
                        opacity: 0.3;
                        z-index: 0;
                    }
                    .mist {
                        position: absolute;
                        width: 200px;
                        height: 200px;
                        border-radius: 50%;
                        animation: float 12s infinite ease-in-out;
                        opacity: 0.5;
                        z-index: 1;
                    }
                    .mist:nth-child(2) {
                        top: 20%;
                        left: 30%;
                        animation-delay: 0s;
                    }
                    .mist:nth-child(3) {
                        top: 50%;
                        left: 70%;
                        width: 150px;
                        height: 150px;
                        animation-delay: 4s;
                    }
                    .mist:nth-child(4) {
                        top: 80%;
                        left: 40%;
                        width: 180px;
                        height: 180px;
                        animation-delay: 8s;
                    }
                    .fragment {
                        position: absolute;
                        background: transparent;
                        opacity: 0.6;
                        animation: float 15s infinite ease-in-out, rotate 10s infinite linear;
                        z-index: 2;
                    }
                    .fragment.triangle {
                        width: 0;
                        height: 0;
                        border-left: 20px solid transparent;
                        border-right: 20px solid transparent;
                        border-bottom: 35px solid rgba(255, 255, 255, 0.2);
                    }
                    .fragment.pentagon {
                        width: 30px;
                        height: 30px;
                        clip-path: polygon(50% 0%, 100% 38%, 82% 100%, 18% 100%, 0% 38%);
                        background: rgba(255, 255, 255, 0.2);
                    }
                    .star-dust {
                        position: absolute;
                        width: 2px;
                        height: 2px;
                        border-radius: 50%;
                        animation: drift 6s infinite ease-in-out;
                        animation-delay: calc(var(--delay) * 1s);
                    }
                    .ripple {
                        position: absolute;
                        width: 100px;
                        height: 100px;
                        border-radius: 50%;
                        opacity: 0;
                        animation: ripple 5s infinite ease-out;
                        z-index: 1;
                    }
                    .ripple:nth-child(2) { animation-delay: 0s; }
                    .ripple:nth-child(3) { animation-delay: 1.5s; }
                    .ripple:nth-child(4) { animation-delay: 3s; }
                    .btn-particle {
                        position: absolute;
                        width: 3px;
                        height: 3px;
                        border-radius: 50%;
                        animation: orbit 4s infinite ease-in-out;
                        z-index: 3;
                    }
                    .content {
                        position: relative;
                        z-index: 3;
                        text-align: center;
                        animation: fadeIn 1s ease-out;
                    }
                    .icon-wrapper {
                        position: relative;
                        display: inline-block;
                    }
                    .error-code, .server {
                        font-size: 18px;
                        margin: 8px 0;
                        animation: float 6s infinite ease-in-out;
                    }
                    .server {
                        font-size: 14px;
                    }
                    h1 {
                        font-size: 28px;
                        font-weight: 600;
                        margin: 0 0 12px;
                        animation: gradientShift 8s ease infinite;
                        background-size: 200% 200%;
                    }
                    p {
                        font-size: 16px;
                        line-height: 1.5;
                        margin: 0 0 20px;
                    }
                    .btn-wrapper {
                        position: relative;
                        display: inline-block;
                    }
                    .btn {
                        padding: 10px 24px;
                        font-size: 14px;
                        font-weight: 500;
                        color: #ffffff;
                        border: none;
                        border-radius: 30px;
                        cursor: pointer;
                        transition: all 0.3s ease;
                        animation: pulse 2s infinite;
                    }
                    .btn:hover {
                        transform: translateY(-3px);
                    }
                    .icon {
                        font-size: 48px;
                        margin-bottom: 16px;
                        animation: breathe 3s infinite ease-in-out;
                    }
                    @keyframes fogFlow {
                        0% { transform: translateX(0); }
                        100% { transform: translateX(-50%); }
                    }
                    @keyframes float {
                        0% { transform: translate(0, 0); }
                        33% { transform: translate(5px, 10px); }
                        66% { transform: translate(-5px, -10px); }
                        100% { transform: translate(0, 0); }
                    }
                    @keyframes rotate {
                        0% { transform: rotate(0deg); }
                        100% { transform: rotate(360deg); }
                    }
                    @keyframes drift {
                        0% { transform: translate(0, 0); opacity: 0.3; }
                        50% { transform: translate(20px, -10px); opacity: 1; }
                        100% { transform: translate(0, 0); opacity: 0.3; }
                    }
                    @keyframes ripple {
                        0% { width: 100px; height: 100px; opacity: 0.5; transform: translate(-50%, -50%); }
                        100% { width: 300px; height: 300px; opacity: 0; transform: translate(-50%, -50%); }
                    }
                    @keyframes orbit {
                        0% { transform: translate(0, 0); opacity: 0.5; }
                        50% { transform: translate(15px, 10px); opacity: 1; }
                        100% { transform: translate(0, 0); opacity: 0.5; }
                    }
                    @keyframes fadeIn {
                        from { opacity: 0; transform: translateY(20px); }
                        to { opacity: 1; transform: translateY(0); }
                    }
                    @keyframes breathe {
                        0% { opacity: 0.7; transform: scale(1); }
                        50% { opacity: 1; transform: scale(1.2); }
                        100% { opacity: 0.7; transform: scale(1); }
                    }
                    @keyframes pulse {
                        0% { transform: scale(1); }
                        50% { transform: scale(1.05); }
                        100% { transform: scale(1); }
                    }
                    @keyframes gradientShift {
                        0% { background-position: 0% 50%; }
                        50% { background-position: 100% 50%; }
                        100% { background-position: 0% 50%; }
                    }
                    @media (max-width: 600px) {
                        h1 {
                            font-size: 24px;
                        }
                        p {
                            font-size: 14px;
                        }
                        .btn {
                            padding: 8px 20px;
                            font-size: 13px;
                        }
                        .icon {
                            font-size: 40px;
                        }
                        .error-code {
                            font-size: 16px;
                        }
                        .server {
                            font-size: 12px;
                        }
                        .fragment.triangle {
                            border-left: 15px solid transparent;
                            border-right: 15px solid transparent;
                            border-bottom: 25px solid rgba(255, 255, 255, 0.2);
                        }
                        .fragment.pentagon {
                            width: 20px;
                            height: 20px;
                        }
                        .ripple {
                            width: 80px;
                            height: 80px;
                        }
                        @keyframes ripple {
                            0% { width: 80px; height: 80px; opacity: 0.5; transform: translate(-50%, -50%); }
                            100% { width: 200px; height: 200px; opacity: 0; transform: translate(-50%, -50%); }
                        }
                    }
                </style>
            </head>
            <body>
                <!-- 流动迷雾层 -->
                <div class="fog-layer"></div>
                <!-- 迷雾效果 -->
                <div class="mist"></div>
                <div class="mist"></div>
                <div class="mist"></div>
                <!-- 幻境碎片 -->
                <div class="fragment triangle" style="top: 15%; left: 20%; animation-delay: 0s;"></div>
                <div class="fragment pentagon" style="top: 25%; left: 80%; animation-delay: 2s;"></div>
                <div class="fragment triangle" style="top: 70%; left: 30%; animation-delay: 4s;"></div>
                <div class="fragment pentagon" style="top: 60%; left: 85%; animation-delay: 6s;"></div>
                <div class="fragment triangle" style="top: 40%; left: 10%; animation-delay: 8s;"></div>
                <div class="fragment pentagon" style="top: 20%; left: 50%; animation-delay: 10s;"></div>
                <div class="fragment triangle" style="top: 80%; left: 70%; animation-delay: 12s;"></div>
                <!-- 中心涟漪 -->
                <div class="ripple" style="top: 50%; left: 50%;"></div>
                <div class="ripple" style="top: 50%; left: 50%;"></div>
                <div class="ripple" style="top: 50%; left: 50%;"></div>
                <!-- 中心内容 -->
                <div class="content">
                    <div class="icon-wrapper">
                        <div class="icon">✨</div>
                        <div class="btn-particle" style="animation-delay: 0s;"></div>
                        <div class="btn-particle" style="animation-delay: 1s;"></div>
                        <div class="btn-particle" style="animation-delay: 2s;"></div>
                    </div>
                    <h1>迷失幻境</h1>
                    <div class="error-code">${errorCode}</div>
                    <div class="server">${server}</div>
                    <p>路径已断,迷雾笼罩,尝试返回现实吧。</p>
                    <div class="btn-wrapper">
                        <button class="btn" onclick="history.back()">返回现实</button>
                        <div class="btn-particle" style="animation-delay: 0s;"></div>
                        <div class="btn-particle" style="animation-delay: 1s;"></div>
                        <div class="btn-particle" style="animation-delay: 2s;"></div>
                    </div>
                </div>
                <script>
                    // 随机星尘粒子
                    function addStarDust() {
                        for (let i = 0; i < 50; i++) {
                            const dust = document.createElement('div');
                            dust.className = 'star-dust';
                            dust.style.left = Math.random() * 100 + '%';
                            dust.style.top = Math.random() * 100 + '%';
                            dust.style.setProperty('--delay', Math.random() * 4);
                            document.body.appendChild(dust);
                        }
                    }
                    addStarDust();
                    // 监听主题切换
                    window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
                        document.querySelectorAll('.star-dust').forEach(dust => dust.remove());
                        addStarDust();
                    });
                </script>
            </body>
            </html>
        `;
    }

    // 替换网络连接失败页面(科幻风格,生命美学日间模式)
    function replaceConnectionErrorPage(errorUrl) {
        document.documentElement.innerHTML = `
            <!DOCTYPE html>
            <html lang="zh-CN">
            <head>
                <meta charset="UTF-8">
                <meta name="viewport" content="width=device-width, initial-scale=1.0">
                <title>连接中断</title>
                <style>
                    * {
                        margin: 0;
                        padding: 0;
                        box-sizing: border-box;
                        font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
                    }
                    body {
                        height: 100vh;
                        display: flex;
                        justify-content: center;
                        align-items: center;
                        color: #e0e0ff;
                        overflow: hidden;
                        position: relative;
                        margin: 0;
                    }
                    /* 夜间模式(科幻风格) */
                    @media (prefers-color-scheme: dark) {
                        body {
                            background: radial-gradient(ellipse at center, #1a1a3a 0%, #0a0a1a 70%, #000000 100%);
                        }
                        .stars {
                            position: fixed;
                            top: 0;
                            left: 0;
                            width: 100%;
                            height: 100%;
                            background: transparent url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><circle cx="50" cy="50" r="0.5" fill="white" opacity="0.8"/></svg>') repeat;
                            z-index: 1;
                        }
                        .star {
                            position: absolute;
                            width: 2px;
                            height: 2px;
                            background: white;
                            border-radius: 50%;
                            animation: twinkle 4s infinite;
                            animation-delay: calc(var(--delay) * 1s);
                        }
                        .big-dipper {
                            position: fixed;
                            top: 10%;
                            left: 5%;
                            width: 100px;
                            height: 60px;
                            z-index: 1;
                        }
                        .big-dipper-star {
                            position: absolute;
                            width: 3px;
                            height: 3px;
                            background: #b8b8ff;
                            border-radius: 50%;
                            animation: twinkle 3s infinite;
                        }
                        .big-dipper-star:nth-child(1) { left: 20px; top: 10px; }
                        .big-dipper-star:nth-child(2) { left: 35px; top: 15px; }
                        .big-dipper-star:nth-child(3) { left: 50px; top: 20px; }
                        .big-dipper-star:nth-child(4) { left: 65px; top: 25px; }
                        .big-dipper-star:nth-child(5) { left: 55px; top: 40px; }
                        .big-dipper-star:nth-child(6) { left: 45px; top: 50px; }
                        .big-dipper-star:nth-child(7) { left: 35px; top: 60px; }
                        .tao-flow {
                            background: radial-gradient(circle, rgba(74, 0, 224, 0.3) 0%, rgba(74, 0, 224, 0) 70%);
                            filter: blur(20px);
                        }
                        .container {
                            background: rgba(10, 10, 30, 0.8);
                            backdrop-filter: blur(10px);
                            border: 1px solid rgba(100, 100, 255, 0.2);
                            box-shadow: 0 8px 30px rgba(74, 0, 224, 0.3);
                        }
                        h1, .icon {
                            background: linear-gradient(135deg, #8e2de2, #4a00e0, #00d2ff);
                            -webkit-background-clip: text;
                            background-clip: text;
                            color: transparent;
                        }
                        p {
                            color: #b8b8ff;
                        }
                        .btn {
                            background: linear-gradient(135deg, #4a00e0, #8e2de2);
                            box-shadow: 0 4px 15px rgba(74, 0, 224, 0.4);
                        }
                        .btn:hover {
                            box-shadow: 0 8px 20px rgba(74, 0, 224, 0.6);
                        }
                        .failed-url {
                            background: rgba(20, 20, 50, 0.5);
                            color: #b8b8ff;
                        }
                        .error-code {
                            color: #8888cc;
                        }
                    }
                    /* 白天模式(生命美学风格,基于 1.8 版本) */
                    @media (prefers-color-scheme: light) {
                        body {
                            background: radial-gradient(ellipse at center, #e8f5e9 0%, #f1f8e9 70%, #ffffff 100%); /* 浅绿到白色,模拟自然光晕 */
                        }
                        .stars {
                            position: fixed;
                            top: 0;
                            left: 0;
                            width: 100%;
                            height: 100%;
                            background: transparent url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><circle cx="50" cy="50" r="0.5" fill="#c8e6c9" opacity="0.4"/></svg>') repeat; /* 淡绿星点 */
                            z-index: 1;
                            animation: twinkle 6s infinite alternate;
                        }
                        .tao-flow {
                            background: radial-gradient(circle, rgba(165, 214, 167, 0.3) 0%, rgba(255, 245, 157, 0) 70%); /* 淡绿到浅金色 */
                            filter: blur(15px);
                        }
                        .tao-flow:nth-child(2) {
                            top: 20%;
                            left: 50%;
                            width: 150px;
                            height: 150px;
                            animation-delay: 3s;
                            transform: translateX(-50%);
                        }
                        .tao-flow:nth-child(3) {
                            top: 50%;
                            left: 50%;
                            width: 180px;
                            height: 180px;
                            animation-delay: 6s;
                            transform: translateX(-50%);
                        }
                        .container {
                            background: rgba(255, 255, 255, 0.85); /* 更柔和的透明效果 */
                            backdrop-filter: blur(12px);
                            border: 1px solid rgba(165, 214, 167, 0.3); /* 淡绿边框 */
                            box-shadow: 0 8px 30px rgba(165, 214, 167, 0.2); /* 淡绿阴影 */
                        }
                        h1, .icon {
                            background: linear-gradient(135deg, #a5d6a7, #fff59d, #aed581); /* 淡绿到浅金色渐变 */
                            -webkit-background-clip: text;
                            background-clip: text;
                            color: transparent;
                        }
                        p {
                            color: #37474f; /* 深灰色文字 */
                        }
                        .btn {
                            background: linear-gradient(135deg, #a5d6a7, #fff59d); /* 淡绿到浅金色按钮 */
                            box-shadow: 0 4px 15px rgba(165, 214, 167, 0.3);
                        }
                        .btn:hover {
                            box-shadow: 0 8px 20px rgba(165, 214, 167, 0.5);
                        }
                        .failed-url {
                            background: rgba(240, 244, 195, 0.5); /* 浅金色背景 */
                            color: #455a64; /* 深灰色文字 */
                        }
                        .error-code {
                            color: #78909c; /* 浅灰色 */
                        }
                    }
                    /* 通用样式(科幻风格) */
                    .tao-flow {
                        position: absolute;
                        width: 200px;
                        height: 200px;
                        border-radius: 50%;
                        animation: float 12s infinite ease-in-out;
                        opacity: 0.6;
                        z-index: 2;
                    }
                    .container {
                        position: absolute;
                        top: 50%;
                        left: 50%;
                        transform: translate(-50%, -50%);
                        z-index: 3;
                        text-align: center;
                        padding: 24px;
                        border-radius: 16px;
                        width: 90%;
                        max-width: 400px;
                        animation: fadeIn 0.6s ease-out;
                    }
                    h1 {
                        font-size: 26px;
                        font-weight: 600;
                        margin: 0 0 12px;
                        animation: gradientShift 8s ease infinite;
                        background-size: 200% 200%;
                    }
                    p {
                        font-size: 15px;
                        line-height: 1.5;
                        margin: 0 0 20px;
                    }
                    .btn {
                        display: inline-block;
                        padding: 10px 24px;
                        font-size: 14px;
                        font-weight: 500;
                        color: #ffffff;
                        border: none;
                        border-radius: 30px;
                        cursor: pointer;
                        transition: all 0.3s ease;
                    }
                    .btn:hover {
                        transform: translateY(-3px);
                    }
                    .failed-url {
                        font-size: 13px;
                        margin: 16px 0;
                        padding: 8px 12px;
                        border-radius: 8px;
                        word-break: break-all;
                    }
                    .error-code {
                        font-size: 12px;
                        margin-top: 12px;
                    }
                    .icon {
                        font-size: 48px;
                        margin-bottom: 16px;
                        animation: pulse 2s infinite;
                    }
                    @keyframes twinkle {
                        0% { opacity: 0.3; }
                        50% { opacity: 1; }
                        100% { opacity: 0.3; }
                    }
                    @keyframes float {
                        0% { transform: translate(0, 0); }
                        33% { transform: translate(30px, 30px); }
                        66% { transform: translate(-30px, -20px); }
                        100% { transform: translate(0, 0); }
                    }
                    @keyframes fadeIn {
                        from { opacity: 0; }
                        to { opacity: 1; }
                    }
                    @keyframes pulse {
                        0% { transform: scale(1); }
                        50% { transform: scale(1.1); }
                        100% { transform: scale(1); }
                    }
                    @keyframes gradientShift {
                        0% { background-position: 0% 50%; }
                        50% { background-position: 100% 50%; }
                        100% { background-position: 0% 50%; }
                    }
                    @media (max-width: 600px) {
                        .container {
                            padding: 16px;
                            width: 90%;
                            max-width: 340px;
                            left: 50%;
                            transform: translate(-50%, -50%);
                        }
                        h1 {
                            font-size: 22px;
                        }
                        p {
                            font-size: 14px;
                        }
                        .btn {
                            padding: 8px 20px;
                            font-size: 13px;
                        }
                        .icon {
                            font-size: 40px;
                        }
                        .big-dipper {
                            width: 80px;
                            height: 48px;
                        }
                    }
                </style>
            </head>
            <body>
                <div class="stars"></div>
                <div class="big-dipper">
                    <div class="big-dipper-star"></div>
                    <div class="big-dipper-star"></div>
                    <div class="big-dipper-star"></div>
                    <div class="big-dipper-star"></div>
                    <div class="big-dipper-star"></div>
                    <div class="big-dipper-star"></div>
                    <div class="big-dipper-star"></div>
                </div>
                <div class="tao-flow"></div>
                <div class="tao-flow"></div>
                <div class="container">
                    <div class="icon">⚡️</div>
                    <h1>连接中断</h1>
                    <p>无法感知星辰脉动,请检查网络或稍后重试。</p>
                    <button class="btn" onclick="window.location.reload()">重新连接</button>
                    <div class="failed-url">目标地址: ${errorUrl}</div>
                    <div class="error-code">错误代码: net::ERR_CONNECTION_ABORTED</div>
                </div>
                <script>
                    // 随机星星闪烁
                    function addRandomStars() {
                        if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
                            for (let i = 0; i < 20; i++) {
                                const star = document.createElement('div');
                                star.className = 'star';
                                star.style.left = Math.random() * 100 + 'vw';
                                star.style.top = Math.random() * 100 + 'vh';
                                star.style.setProperty('--delay', Math.random() * 4);
                                document.body.appendChild(star);
                            }
                        }
                    }
                    addRandomStars();
                    // 监听主题切换
                    window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
                        document.querySelectorAll('.star').forEach(star => star.remove());
                        addRandomStars();
                    });
                </script>
            </body>
            </html>
        `;
    }

    // 快速注入机制
    function initInjection() {
        // 优先检查 404 页面
        if (checkFor404Page()) {
            replace404Page();
            return true;
        }
        // 再检查网络连接失败页面
        if (checkForConnectionErrorPage()) {
            const errorUrl = extractErrorUrl();
            replaceConnectionErrorPage(errorUrl);
            return true;
        }
        return false;
    }

    // 立即检查
    initInjection();

    // 早期高频检查
    let attempts = 0;
    const maxAttempts = 20; // 最多检查 1 秒(50ms x 20)
    const earlyCheckInterval = setInterval(() => {
        if (initInjection()) {
            clearInterval(earlyCheckInterval);
        } else if (attempts >= maxAttempts) {
            clearInterval(earlyCheckInterval);
        }
        attempts++;
    }, 50);

    // 监听 DOM 变化
    const observer = new MutationObserver((mutations, obs) => {
        if (initInjection()) {
            obs.disconnect();
        }
    });

    // 尽早观察 documentElement
    observer.observe(document.documentElement, { childList: true, subtree: true });

    // 页面加载完成时再检查
    document.addEventListener('DOMContentLoaded', () => {
        initInjection();
    }, { once: true });
})();