🔅光遇聚合(26.5.31)

光遇聚合

晴天 (8653)5天前

修复已知bug
二维码导入
{
    "bookSourceComment": "如果是用的官方版阅读,或者低版本阅读,推荐更新阅读以使用新功能,否则很多功能无法使用哦\n软件下载地址:https:\/\/legado.gyks.cf\n推荐使用阅读R或者阅读Sigma\n\n\n晴天聚合重构,已适配所有安卓版本阅读\n目前不适配苹果端栖阅(可以正常阅读,发现页无法适配)\n\n更新日志请点击登录,更新书源中查看\n\n基础使用教程:\n1、搜索:\n    1)基础搜索:在发现页长按该书源,点击搜索,直接搜,搜索范围(全部来源小说)\n    2)进阶搜索:在登陆-书源设置中选择指定模式\/来源,可搜索指定模式\/来源内容\n    3)快捷搜索:使用特殊关键词(x,t,m,d,@)搜索\n         x:书名@来源  如(x:十日终焉@番茄)\n         t:书名@来源  如(t:十日终焉@番茄)\n         m:书名@来源  如(m:十日终焉@番茄)\n         d:书名@来源  如(d:十日终焉@番茄)\n         其中:x表示小说(可省略),t表示听书,m表示漫画,d表示短剧\n         冒号支持中文英文\n         \n2、发现页:\n    在书源设置中选择指定模式或者单个来源,然后刷新发现页即可\n    \n3、更多使用方式进入书源设置页面应该能一目了然",
    "bookSourceGroup": "聚合,番茄,七猫,塔读,QQ阅读,书旗,轻小说",
    "bookSourceName": "🔅光遇聚合(26.5.31)",
    "bookSourceType": 0,
    "bookSourceUrl": "光遇聚合",
    "bookUrlPattern": "https?:\\\/\\\/(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z0-9-]+(?::\\d+)?\\\/detail.*",
    "customButton": false,
    "customOrder": 4,
    "enabled": true,
    "enabledCookieJar": true,
    "enabledExplore": true,
    "eventListener": false,
    "exploreUrl": "<js>\nlet moreSettings = getVariable('更多设置');\nvar base_url = BaseUrl();\nvar source_type = getVariable('频道') || '男频';\nlet tab = moreSettings && moreSettings['搜索模式'] || '小说';\nlet sources = getVariable(\"发现页来源\") || moreSettings && moreSettings[tab] || '全部';\nlet islyc = checkEnv();\n\nif (islyc != \"苹果\" && islyc != \"安卓\"){\n\ttab = getVariable(\"发现页类型\");\n\t}\n\n\nlet js;\n\njs = getVariable('云端配置');\n\/\/java.longToast(JSON.stringify(js))\nif (!!!js) {\n    getCloudSettings(true);\n    js = getVariable('云端配置');\n }\n\n\nlet source_list = [];\ntry{\nsource_list = js[tab];\n} catch (e) {\n\tjava.longToast(e);\n\t}\n\nlet fqssionid = getFqToken();\nif (!fqssionid && (sources == '番茄' || sources == '全部')) {\n    java.toast('您还未登陆番茄账号,无法同步数据哦!');\n} \nvar fqsjurl = base_url + \"\/bookshelf?page={{page}}&ssionid=\" + fqssionid;\nvar fqtjurl = base_url + \"\/fqrecommend?page={{page}}&ssionid=\" + fqssionid;\nvar fqlsurl = base_url + \"\/fqhistory?page={{page}}&ssionid=\" + fqssionid;\n\n\nvar groupDatas = [];\nvar infoData = [ ];\nif (sources == '番茄' || sources == '全部') {\ninfoData = [{\n        \"title\": \"登录番茄\",\n        \"url\": '{\\{java.startBrowser(\"https:\/\/fanqienovel.com\/\",\"番茄登录\");}}',\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.45\n        }\n    }];\n}\n\nvar hasValidCookie = fqssionid.length > 0;\n\nif (hasValidCookie && (sources == '番茄' || sources == '全部')) {\n    function groupQuery() {\n        try {\n            var url = base_url + \"\/group_name?ssionid=\" + fqssionid;\n            var res = java.ajax(url);\n            var response = JSON.parse(res);\n\n            if (!(response && response.data)) {}\n\n            response.data.forEach(function(group) {\n                var keys = Object.keys(group);\n                if (keys.length > 0) {\n                    var key = keys[0];\n                    var value = group[key];\n                    if (value && value.length) {\n                        var option = {\n                            \"method\": \"POST\",\n                            \"body\": {\n                                \"book_ids\": value,\n                                \"page\": \"{{page}}\"\n                            }\n                        };\n                        groupDatas.push({\n                            title: key,\n                            url: base_url + \"\/bookshelf,\" + JSON.stringify(option),\n                            style: {\n                                layout_flexGrow: 1,\n                                layout_flexBasisPercent: 0.25\n                            }\n                        });\n                    }\n                }\n            });\n\n            if (groupDatas.length % 2 != 0) {\n                groupDatas.push({\n                    title: \"--\",\n                    url: \"\",\n                    style: {\n                        layout_flexGrow: 1,\n                        layout_flexBasisPercent: 0.25\n                    }\n                });\n            }\n            if (groupDatas.length % 3 != 0) {\n                groupDatas.push({\n                    title: \"--\",\n                    url: \"\",\n                    style: {\n                        layout_flexGrow: 1,\n                        layout_flexBasisPercent: 0.25\n                    }\n                });\n            }\n        } catch (e) {\n            java.longToast(\"番茄登录过期,已隐藏番茄书架\" + fqssionid);\n        }\n    }\n\n    try {\n       java.longToast(\"正在加载番茄分组数据...\");\n        var userUrl = base_url + \"\/fquser?ssionid=\" + fqssionid;\n        var userRes = java.ajax(userUrl);\n        var userData = JSON.parse(userRes);\n\n        var userName = (userData && userData.data && userData.data.name) ? userData.data.name : '未知用户';\n        if (!userName.includes('未知用户')) {\n            infoData = [{\n                title: userName+'的番茄',\n                url: '',\n                style: {\n                    layout_flexGrow: 1,\n                    layout_flexBasisPercent: 1\n                }\n            },\n            {\n                title: '番茄书架',\n                url: fqsjurl,\n                style: {\n                    layout_flexGrow: 1,\n                    layout_flexBasisPercent: 0.25\n                }\n            }, \n            {\n                title: \"个性推荐\",\n                url: fqtjurl,\n                style: {\n                    layout_flexGrow: 1,\n                    layout_flexBasisPercent: 0.25\n                }\n            }, {\n                title: \"历史阅读\",\n                url: fqlsurl,\n                style: {\n                    layout_flexGrow: 1,\n                    layout_flexBasisPercent: 0.25\n                }\n            }];\n        }\n        groupQuery();\n    } catch (e) {\n        java.longToast(\"番茄登录过期,已隐藏番茄书架\");\n    }\n}\n\nvar style_list = [];\ntry {\n    var durl = `${base_url}\/discovestyle?source=${sources}&source_type=${source_type}&tab=${tab}`;\n    var res = java.ajax(durl);\n    var result = JSON.parse(res);\n    style_list = result.data || [];\n    if (result.msg) {\n        java.toast(result.msg);\n    }\n} catch (e) {\n    java.toast(\"发现样式获取失败\");\n}\nlet qttoken = getToken();\n\/\/let qtcookie = \"\"\nlet qtop = {\n    method: \"GET\",\n    headers: {\n        cookie: `qttoken=${qttoken}`\n    },\n};\n\n\n\nqtop = JSON.stringify(qtop);\nlet qtsjurl = base_url + '\/get_book_shelf,' + qtop;\n\nlet qtsj = [];\n\nif (islyc != \"苹果\" && islyc != \"安卓\") {\n    try {\n        let hostsbk = getVariable('云端配置')['hosts'] || hosts;\n        qtsj.push(createFilter(\n            \"线路\",\n            hostsbk,\n            base_url,\n            \"线路\",\n            1,\n            \"'线路'\"\n        ));\n        qtsj.push(createFilter(\n            \"类型\",\n            [\"小说\", \"听书\", \"短剧\", \"漫画\"],\n            tab,\n            \"发现页类型\",\n            0.33,\n            \"'类型'\"\n        ));\n        qtsj.push(createFilter(\n            \"频道\",\n            [\"男频\", \"女频\"],\n            source_type,\n            \"频道\",\n            0.33,\n            \"'频道'\"\n        ));\n        qtsj.push(createFilter(\n            \"平台\",\n            source_list,\n            sources,\n            \"发现页来源\",\n            1,\n            \"'平台'\"\n        ));\n        \n        qtsj.push({\n        \"title\": \"更新配置\",\n        \"type\": \"button\",\n        \"action\": \"getCloudSettings(true)\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.25\n        }\n    })\n    qtsj.push({\n        \"title\": \"更新书源\",\n        \"type\": \"button\",\n        \"action\": \"renderVersionPage()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.25\n        }\n    })\n    qtsj.push({\n        \"title\": \"书源设置\",\n        \"type\": \"button\",\n        \"action\": \"getHtmlSettings()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.25\n        }\n    })\n       const excludeTitles = ['点击登录可切换来源', '切换后长按刷新即可'];\n       style_list = style_list.filter(item => !excludeTitles.includes(item.title));\n    } catch {}\n} else {\n\tif (islyc == '安卓'){\n\tqtsj.push({\n        \"title\": \"下载阅读Sigma最新测试版\",\n        \"url\": `{\\{java.longToast(\"本书源推荐使用最新版洛娅橙改版阅读Σ\\\\n请安装后重新导入书源\");java.startBrowser(\"https:\/\/legado.gyks.cf\",\"下载阅读Sigma最新测试版\");}}`,\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 1\n        }\n    })}\n\t}\n\nif (qttoken.length > 10) {\nqtsj.push({\n    title: \"晴天书架\",\n    url: qtsjurl,\n    style: {\n        layout_flexGrow: 1,\n        layout_flexBasisPercent: 1\n    }\n})\n} else {\nqtsj.push({\n    title: \"登录晴天书源\",\n    url: `{\\{java.startBrowser(\"${base_url}\/login\",\"登录书源\");}}`,\n    style: {\n        layout_flexGrow: 1,\n        layout_flexBasisPercent: 1\n    }\n})\n}\n\n\nvar finalData = infoData.concat(groupDatas, style_list);\nfinalData = qtsj.concat(finalData);\nJSON.stringify(finalData);\n<\/js>",
    "jsLib": "\/\/ 当前书源版本号,切勿修改,否则影响更新的识别\nlet localVersion = '26.5.31.1';\n\n\/\/ 初始服务器列表\nlet hosts = [\n    'https:\/\/v1.gyks.cf',\n    'https:\/\/v2.gyks.cf',\n    'https:\/\/v3.gyks.cf',\n    'https:\/\/v4.gyks.cf',\n    'https:\/\/v5.gyks.cf',\n    'https:\/\/v6.gyks.cf',\n    'https:\/\/v7.gyks.cf',\n    'http:\/\/101.35.133.34:8888'\n];\n\n\n\/\/ 初始化值\nconst defaultConfig = {\n    线路: hosts[0],\n    发现页来源: \"番茄\",\n    发现页类型: \"小说\"\n};\n\n\/\/ 获取云端配置\nfunction getCloudSettings(r) {\n    if (r == undefined) r = false;\n    const {\n        java,\n        cache\n    } = this;\n    let c = this.getVariable('云端配置').version;\n    if (r || !c) {\n        java.longToast(`\\n正在更新最新配置`);\n        try {\n            let url = `\/static\/source_config\/config.json`;\n            \/\/\tjava.longToast(url)\n            let js = this.request(url);\n            js = JSON.parse(String(js));\n            let intc = parseInt(c);\n            let intv = parseInt(js['version']);\n            if (intc >= intv) {\n            \t    java.longToast(`\\n已是最新配置:${js['version']}`);\n            \t    return;\n            \t} else if (!intc && intv) {\n            \t\tjava.longToast(`\\n已初始化配置:${intv}`);\n            \t\t} else if (intc < intv || !intc && intv) {\n            java.longToast(`\\n已更新配置:${intc}→${intv}`);\n            } else {\n            java.longToast(`\\n获取配置失败:请切换线路再试试~`);\n            return;\n            }\n            this.setVariable('云端配置', js);\n            \n            return js;\n        } catch (e) {\n            java.longToast(`\\n获取最新配置失败:${e}`);\n        }\n    }\n}\n\n\/\/ 统一获取和解析变量\nfunction _getParsedVariable() {\n    const {\n        source,\n        java\n    } = this;\n    let v = source.getVariable();\n    try {\n        return JSON.parse(v);\n    } catch {\n        return defaultConfig;\n    }\n}\n\n\/\/ 获取设置变量\nfunction getLoginInfo(k) {\n    const {\n        source,\n        java\n    } = this;\n    let res = source.getLoginInfoMap();\n    if (!res) {\n        res = defaultConfig;\n    }\n    if (res[k] == '❌') {\n        return '0';\n    } else if (res[k] == '✅') {\n        return '1';\n    }\n    return res[k]\n}\n\n\/\/ 还原\nfunction deleteVariable() {\n    const {\n        source,\n        java,\n        cache\n    } = this;\n    source.setVariable('');\n    cache.delete('gyksconfig');\n    java.longToast('\\n所有设置已还原到初始化导入状态');\n}\n\n\/\/ 获取源变量\nfunction getVariable(k) {\n    if (k == undefined) k = \"\";\n    const { source } = this;\n    let parsed = {}\n    try {\n        parsed = JSON.parse(source.getVariable());\n    } catch {}\n    if (k == \"\") {\n        return parsed;\n    }\n    let value = parsed[k];\n    if (value == undefined) {\n        value = defaultConfig[k];\n    }\n    return value != undefined ? value : \"\";\n}\n\n\/\/ 设置源变量\nfunction setVariable(k, v, t) {\n    if (t == undefined) t = true;\n    const {\n        source,\n        java\n    } = this;\n    const vs = this.getVariable();\n    vs[k] = v;\n    source.setVariable(JSON.stringify(vs, null, 4));\n    if (k != '云端配置' && t) {\n        java.toast(`\\n设置 ${k} 为 ${v}`)\n    }\n}\n\n\/\/ 获取正在使用的线路\nfunction BaseUrl() {\n    let hostsbk = this.getVariable('云端配置')['hosts'] || hosts;\n    let h = this.getVariable(\"线路\");\n    if (!h || String(h) == \"undefind\") {\n        h = hostsbk[0]\n    }\n    return h;\n}\n\n\/\/ 切换线路\nfunction switchToNextLine(toast) {\n    if (toast == undefined) toast = false;\n    const currentLine = this.BaseUrl();\n    let hostsbk = this.getVariable('云端配置')['hosts'] || hosts;\n    let currentIndex = hostsbk.findIndex(item => item == currentLine);\n    let nextIndex = (currentIndex + 1) % hostsbk.length;\n    const nextLine = hostsbk[nextIndex];\n    this.setVariable(\"线路\", nextLine,false);\n    let switchToast = this.getVariable(\"切换提醒\") || \"true\";\n    if (toast && switchToast  == \"true\") {\n        this.java.longToast(`\\n${toast}\\n自动切换到:${nextLine}`)\n    }\n    return nextLine;\n}\n\n\n\/\/ 备用设置方案\nfunction ste(v) {\n    const {\n        java\n    } = this;\n    let moreSettings = this.getVariable(\"更多设置\") || {};\n    let paraSettings = this.getVariable(\"段评设置\") || {};\n    if (v==\"段评\"){\n        if (paraSettings[\"段评开关\"] != \"false\"){\n            paraSettings[\"段评开关\"]= \"false\";\n            java.longToast(\"\\n❌段评已关闭\");\n        }else{\n            paraSettings[\"段评开关\"]= \"true\";\n            java.longToast(\"\\n✅段评已开启\");\n        }\n    }\n    if (v==\"小说\" || v==\"听书\" || v==\"漫画\" || v==\"短剧\"){\n        moreSettings[\"搜索模式\"]=v;\n        java.longToast(`\\n已设置搜索模式为 ${v}`);\n    }\n    if (v==\"图片\"){\n        if (moreSettings[\"显示图片\"] != \"false\"){\n            moreSettings[\"显示图片\"]= \"false\";\n            java.longToast(\"\\n❌已关闭图片显示\");\n        }else{\n            moreSettings[\"显示图片\"]= \"true\";\n            java.longToast(\"\\n✅已开启图片显示\");\n        }\n    }\n    if (v==\"简介\"){\n        if (moreSettings[\"完整简介\"] != \"false\"){\n            moreSettings[\"完整简介\"]= \"false\";\n            java.longToast(\"\\n❌已关闭完整简介\");\n        }else{\n            moreSettings[\"完整简介\"]= \"true\";\n            java.longToast(\"\\n✅已开启完整简介\");\n        }\n    }\n    if (v==\"同步\"){\n        if (moreSettings[\"同步书架\"] != \"false\"){\n            moreSettings[\"同步书架\"]= \"false\";\n            java.longToast(\"\\n❌已关闭同步书架\");\n        }else{\n            moreSettings[\"同步书架\"]= \"true\";\n            java.longToast(\"\\n✅已开启同步书架\");\n        }\n    }\n    if (v==\"强制\"){\n        if (moreSettings[\"强制搜索\"] != \"false\"){\n            moreSettings[\"强制搜索\"]= \"false\";\n            java.longToast(\"\\n❌已关闭强制搜索\");\n        }else{\n            moreSettings[\"强制搜索\"]= \"true\";\n            java.longToast(\"\\n✅已开启强制搜索\");\n        }\n    }\n    if (v==\"目录\"){\n        if (moreSettings[\"目录显示来源\"] != \"false\"){\n            moreSettings[\"目录显示来源\"]= \"false\";\n            java.longToast(\"\\n❌已关闭目录显示来源\");\n        }else{\n            moreSettings[\"目录显示来源\"]= \"true\";\n            java.longToast(\"\\n✅已开启目录显示来源\");\n        }\n    }\n    if (v==\"网络\"){\n        if (moreSettings[\"网络模式\"] != \"服务器\"){\n            moreSettings[\"网络模式\"]= \"服务器\";\n            java.longToast(\"\\n已切换为 服务器 模式\");\n        }else{\n            moreSettings[\"网络模式\"]= \"本地\";\n            java.longToast(\"\\n已切换为 本地 模式\");\n        }\n    }\n    this.setVariable(\"更多设置\", moreSettings, false);\n    this.setVariable(\"段评设置\", paraSettings, false);\n}\n\nfunction parseBadMap(str) {\n    str = str\n        .replace(\/^{|}$\/g, '');\n\n    const result = {};\n\n    str.split(',').forEach(item => {\n        const parts = item.split('=');\n\n        if (parts.length === 2) {\n            const key = parts[0].trim();\n            const value = parts[1].trim();\n\n            result[key] = value;\n        }\n    });\n\n    return result;\n}\n\nfunction findItemIndex(data, item_id) {\n    if (!data || !Array.isArray(data)) {\n        return 0;\n    }\n    for (let i = 0; i < data.length; i++) {\n        if (data[i] && data[i].item_id == item_id) {\n            return i;\n        }\n    }\n    return 0;\n}\n\n\/\/ 判断当前环境\nfunction checkEnv() {\n    const {\n        java,\n        source\n    } = this;\n\n    try {\n        java.qread();\n        return \"轻阅读\";\n    } catch (e) {}\n\n    try {\n        if (typeof java.reLoginView == 'function') {\n            return \"改版\";\n        }\n        new Packages.io.legato.kazusa.utils.TimeoutCancellationException('');\n        return \"改版\";\n    } catch (e) {}\n    try {\n        java.deviceID();\n        return \"苹果\";\n    } catch (e) {}\n\n    if (typeof source.loginUi == 'function') {\n        return \"安卓\";\n    }\n\n    return \"改版\";\n}\n\n\/\/ 获取番茄token\nfunction getFqToken() {\n    const {\n        java,\n        cookie\n    } = this;\n\n    try {\n        let cookieValue = String(cookie.getCookie('fanqienovel.com')) || String(java.getCookie('fanqienovel.com'));\n        let parts = cookieValue.split(\";\");\n        for (let part of parts) {\n            if (part.includes(\"sessionid\")) {\n                return part.split(\"=\")[1];\n            }\n        }\n    } catch {}\n    return \"\";\n}\n\n\/\/ 获取登陆token\nfunction getToken() {\n    const {\n        java,\n        cookie\n    } = this;\n    let hostsbk = this.getVariable('云端配置')['hosts'] || hosts;\n    try {\n        for (let h of hostsbk) {\n            let cookieValue = String(cookie.getCookie(h)) || String(java.getCookie(h));\n            let parts = cookieValue.split(\";\");\n            for (let part of parts) {\n                if (part.includes(\"qttoken\")) {\n                    return part.split(\"=\")[1];\n                }\n            }\n        }\n    } catch {}\n    return \"\";\n}\n\n\/\/ 移除ck\nfunction removeAllCookies() {\n    const {\n        java,\n        cookie\n    } = this;\n    let hostsbk = this.getVariable('云端配置')['hosts'] || hosts;\n    for (let h of hostsbk) {\n        cookie.removeCookie(h);\n    }\n    cookie.removeCookie('fanqienovel.com');\n    java.toast('\\n已退出登陆')\n}\n\n\/\/ 设置ck\nfunction setAllCookies(ck) {\n    const {\n        cookie\n    } = this;\n    let hostsbk = this.getVariable('云端配置')['hosts'] || hosts;\n    for (let h of hostsbk) {\n        cookie.setCookie(h, ck);\n    }\n}\n\n\/\/ 请求封装\nfunction request(url, method, body, req, index) {\n    if (method == undefined) method = 'GET';\n    if (body == undefined) body = {};\n    if (req == undefined) req = true;\n    if (index == undefined) index = 0;\n\n    let urla = url;\n    if (!url.includes('http')) {\n        urla = this.BaseUrl() + url;\n    }\n\n    const { java } = this;\n    let device = '';\n    try {\n        device = java.androidId();\n    } catch {\n        try {\n            device = java.deviceID();\n        } catch {}\n    }\n\n    let qttoken = this.getToken();\n    let options = {\n        method: method,\n        headers: {\n            'cookie': `qttoken=${qttoken};deviceId=${device};`,\n            'Content-Type': 'application\/json'\n        },\n        body: JSON.stringify(body)\n    };\n\n    urla = `${urla},${JSON.stringify(options)}`;\n    java.log(urla);\n\n    if (!req) {\n        return urla;\n    }\n\n    const startTime = new Date().getTime();\n\n    let data;\n    let requestTimedOut = false;\n    let requestFailed = false;\n\n    try {\n        data = java.ajax(urla);\n        JSON.parse(data);\n    } catch (error) {\n        requestFailed = true;\n    } finally {\n        const endTime = new Date().getTime();\n        const duration = endTime - startTime;\n        let timeout = this.getVariable('超时时间') || '5';\n        timeout = parseInt(timeout)*1000\n        if (!requestFailed && duration > timeout ) {\n            requestTimedOut = true;\n        }\n    }\n    if (requestTimedOut) {\n        let timeoutSwitch = this.getVariable('超时自动切换') || 'true';\n        if (timeoutSwitch!= \"false\") {\n             java.log('检测到当前线路较慢,已切换线路');\n            this.switchToNextLine('⚠️当前线路较慢');\n        }\n        return data;\n    }\n    if (requestFailed) {\n        java.log('线路报错,自动切换下一个');\n        let hostsbk = this.getVariable('云端配置')['hosts'] || hosts;\n        let autoSwitch = this.getVariable('自动切换') || 'true';\n\n        if (hostsbk.length > index && autoSwitch != \"false\") {\n            this.switchToNextLine('❌线路报错');\n            return this.request(url, method, body, req, index + 1);\n        } else {\n            return \"\";\n        }\n    }\n    return data;\n}\n\nfunction parseJsonSafely(str, defaultValue) {\n    if (defaultValue == null) defaultValue = null;\n    try {\n        return JSON.parse(str);\n    } catch (e) {\n        return defaultValue;\n    }\n}\n\n\n\/\/去除图片\nfunction removeAllImgTags(htmlString) {\n    const imgTagRegex = \/<img\\b[^>]*>|<\\\/img>|<img\\b[^>]*\\\/>\/gi;\n    return htmlString.replace(imgTagRegex, '');\n}\n\n\/\/ 段评气泡生成\nfunction paraForAndroid(content, sources) {\n    let {\n        java,\n        cache,\n        source\n    } = this;\n\n    const createSvg = this.createSvg.bind(this);\n\n    return content.replace(\/<p>(.*?)(?:<comment ident=\"([^\"]*)\" count=\"([^\"]*)\" \\\/>)?<\\\/p>\/g,\n        (match, text, url, count) => {\n            if (url && count) {\n                const click = 0;\n                cache.putMemory(url, click);\n                const encodedUrl = url;\n                return `<p>${text}<img src=\"${createSvg(count, encodedUrl,sources)}\"><\/p>`;\n            } else {\n                return `<p>${text}<\/p>`;\n            }\n        }\n    );\n}\n\n\nlet svgs = [\n    \"<svg t='1760002253572' class='icon' viewBox='-150 0 1224 1224' version='1.1' xmlns='http:\/\/www.w3.org\/2000\/svg' p-id='1490' width='800' height='800'><g transform='rotate(90 512 512) scale(1 1.2)'><path d='M224 149.333333h576c40.533333 0 74.666667 34.133333 74.666667 74.666667v460.8c0 40.533333-34.133333 74.666667-74.666667 74.666667h-108.8c-40.533333 0-78.933333 17.066667-104.533334 46.933333l-49.066666 55.466667c-4.266667 6.4-14.933333 10.666667-23.466667 10.666667-10.666667 0-19.2-4.266667-25.6-10.666667l-46.933333-55.466667c-25.6-29.866667-64-46.933333-104.533334-46.933333h-110.933333c-40.533333 0-74.666667-34.133333-74.666667-74.666667V224c0-40.533333 34.133333-74.666667 74.666667-74.666667z' fill='$tccolor' p-id='1529'><\/path><path d='M512 938.666667c-27.733333 0-55.466667-12.8-72.533333-34.133334l-46.933334-55.466666c-14.933333-17.066667-34.133333-25.6-57.6-25.6h-110.933333c-76.8 0-138.666667-61.866667-138.666667-138.666667V224C85.333333 147.2 147.2 85.333333 224 85.333333h573.866667c76.8 0 138.666667 61.866667 138.666666 138.666667v460.8c0 76.8-61.866667 138.666667-138.666666 138.666667h-108.8c-21.333333 0-42.666667 8.533333-55.466667 25.6l-49.066667 55.466666c-17.066667 21.333333-44.8 34.133333-72.533333 34.133334zM224 149.333333C183.466667 149.333333 149.333333 183.466667 149.333333 224v460.8c0 40.533333 34.133333 74.666667 74.666667 74.666667h110.933333c40.533333 0 78.933333 17.066667 104.533334 49.066666l46.933333 55.466667c6.4 6.4 14.933333 10.666667 25.6 10.666667 8.533333 0 19.2-4.266667 23.466667-10.666667l49.066666-55.466667c25.6-29.866667 64-46.933333 104.533334-46.933333h108.8c40.533333 0 74.666667-34.133333 74.666666-74.666667V224C874.666667 183.466667 840.533333 149.333333 800 149.333333h-576z' fill='$color' p-id='1530'><\/path> <\/g><text x='460' y='650' font-family='Arial, sans-serif' text-anchor='middle' font-size='400' fill='$fontcolor'>$displayText<\/text><\/svg>\",\n\n    \"<svg xmlns='http:\/\/www.w3.org\/2000\/svg' width='100' height='90' viewBox='0 0 25 17' fill='none' style='color: $color; opacity: 1;'><path stroke='currentColor' stroke-width='1.00' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='4.00' style='stroke: $color; stroke-opacity: 0.5; stroke-width: 1px; stroke-linejoin: round; fill: $tccolor; fill-opacity: 0.5;' d='M 7.5 0.5 L 16.5 0.5 A 7 7 0 0 1 23.5 7.5 L 23.5 7.5 A 7 7 0 0 1 16.5 14.5 L 8.5 14.5 L 5.192 16.5 L 5.5 14.5 A 7 7 30 0 1 0.5 7.5 L 0.5 7.5 A 7 7 0 0 1 7.5 0.5 Z'\/><text fill='$fontcolor' font-family='Arial, sans-serif' font-size='10' font-weight='500' text-anchor='middle' dy='0.35em' x='12.00' y='7.50'>$displayText<\/text><\/svg>\",\n\n    \"<svg xmlns='http:\/\/www.w3.org\/2000\/svg' width='100' height='90' viewBox='0 0 25 17' fill='none' style='color: $color; opacity: 1;'><path d='M24 14.5v-12a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v7.528a2 2 0 0 1-.211.894l-2.065 4.13a1 1 0 0 0 .894 1.448H22a2 2 0 0 0 2-2z' stroke='currentColor' style='stroke: $color; stroke-opacity: 0.5; stroke-width: 1px; stroke-linejoin: round; fill: $tccolor; fill-opacity: 0.5;'><\/path><text x='13.5' y='12' text-anchor='middle' alignment-baseline='auto' font-size='10' fill='currentColor' font-family='Arial, sans-serif' font-weight='500' style='fill: $fontcolor; opacity: 1;'>$displayText<\/text><\/svg>\",\n\n\n\n    \"<svg xmlns='http:\/\/www.w3.org\/2000\/svg' width='100' height='90' viewBox='6 10 88 76' style='color: $color; opacity: 1;'><path d='M12,12 L88,12 Q92,12 92,16 L92,68 Q92,72 88,72 L28,72 L12,84 L12,72 Q8,72 8,68 L8,16 Q8,12 12,12 Z' stroke='currentColor' stroke-width='4' stroke-linejoin='round' style='stroke: $color; stroke-opacity: 0.5; stroke-width: 2.5px; stroke-linejoin: round; fill: $tccolor; fill-opacity: 0.5;'><\/path><text x='50' y='60' font-family='Arial, sans-serif' text-anchor='middle' font-size='45' fill='currentColor' font-weight='500' alignment-baseline='auto' style='fill: $fontcolor; opacity: 1;'>$displayText<\/text><\/svg>\",\n\n\n\n    \"<svg xmlns='http:\/\/www.w3.org\/2000\/svg' width='100' height='90' viewBox='0 0 25 17' fill='none' style='color: $color; opacity: 1;'><rect x='0.5' y='0.5' width='24' height='16' rx='2.5' ry='2.5' stroke='currentColor' style='stroke: $color; stroke-opacity: 0.5; stroke-width: 1px; stroke-linejoin: round; fill: $tccolor; fill-opacity: 0.5;'><\/rect><text x='12.5' y='12' text-anchor='middle' alignment-baseline='auto' font-size='10' fill='currentColor' font-family='Arial, sans-serif' font-weight='500' style='fill: $fontcolor; opacity: 1;'>$displayText<\/text><\/svg>\",\n\n\n\n\n    \"<svg xmlns='http:\/\/www.w3.org\/2000\/svg' width='100' height='90' viewBox='0 0 25 17' fill='none' style='color: $color; opacity: 1;'><circle cx='12.5' cy='8.5' r='8' stroke='currentColor' style='stroke: $color; stroke-opacity: 0.5; stroke-width: 1px; stroke-linejoin: round; fill: $tccolor; fill-opacity: 0.5;'><\/circle><text x='13' y='12' text-anchor='middle' alignment-baseline='auto' font-size='10' fill='currentColor' font-family='Arial, sans-serif' font-weight='500' style='fill: $fontcolor; opacity: 1;'>$displayText<\/text><\/svg>\"\n];\n\n\n\/\/ 创建svg\nfunction createSvg(number, encodedUrl, sources) {\n    var displayText = number > 99 ? \"99+\" : number.toString();\n    let data = this.getVariable('段评设置');\n    let svgstyle = data['段评样式'] || \"0\";\n    let color = data['段评边框颜色'] || \"#A7A7A7\";\n    let tccolor = data['段评填充颜色'] || \"transparent\";\n    let fontcolor = data['段评字体颜色'] || \"#A7A7A7\";\n    let zdy = svgs[0];\n    try {\n        zdy = data['自定义段评样式'][svgstyle] || svgs[0];\n    } catch {}\n    const styleMap = {\n        \"0\": svgs[0],\n        \"1\": svgs[1],\n        \"2\": svgs[2],\n        \"3\": svgs[3],\n        \"4\": svgs[4],\n        \"5\": svgs[5],\n    };\n    let svg = styleMap[svgstyle] || zdy;\n    svg = svg.replace('$color', color)\n        .replace('$tccolor', tccolor)\n        .replace('$fontcolor', fontcolor)\n        .replace('$displayText', displayText);\n    var jc = 'js';\n    let islyc = this.checkEnv()\n    if (islyc == \"改版\") {\n        jc = 'click'\n    }\n    let style = data['气泡增大'] == \"true\" ? 'TEXT' : 'text';\n    var encodedSvg = this.java.base64Encode(svg);\n    return `data:image\/svg+xml;base64,${encodedSvg},{\"${jc}\":\"showCmt('${encodedUrl}', '${sources}' )\",\"style\":\"${style}\"}`;\n}\n\n\n\n\n\/\/ 显示弹窗\nfunction showCmt(url, sources) {\n    let {\n        java,\n        cache\n    } = this;\n    const currentTime = Date.now();\n    const click = cache.getFromMemory(url);\n    let islyc = this.checkEnv()\n    if (click < 1 && islyc == \"安卓\") {\n        cache.putMemory(url, click + 1);\n        return;\n    } else {\n        if (islyc == \"轻阅读\") {\n            java.startBrowserDp(url, sources + '段评');\n        } else if (islyc == \"改版\") {\n            let paradata = new Date().toLocaleDateString();\n            let htmlContent = cache.get(paradata);\n            if (!htmlContent || String(htmlContent).indexOf(\"评论\") == -1) {\n                java.longToast(`\\n${paradata} 初始化段评页面,请稍等~`);\n                try {\n                    htmlContent = java.ajax(url);\n                } catch {\n\n                }\n\n                cache.put(paradata, htmlContent);\n                java.longToast(`\\n${paradata} 初始化段评页面成功~`);\n            }\n            java.showBrowser(\n                url,\n                htmlContent,\n                `window.java=java;`,\n                JSON.stringify({\n                    \"expandedCornersRadius\": 20,\n                    \"dismissOnTouchOutside\": true,\n                    \"isDraggable\": true,\n                    \"shouldDimBackground\": true,\n                    \"backgroundDimAmount\": 0.5,\n                    \"hardwareAccelerated\": true,\n                    \"isNestedScrollingEnabled\": true,\n                    \"isGestureInsetBottomIgnored\": true,\n                    \"setFitToContents\": true,\n                    \"isHideable\": true,\n                    \"heightPercentage\": 0.8\n                })\n            );\n        } else {\n            java.startBrowser(url, sources + '段评');\n        }\n    }\n}\n\n\/\/源阅段评\nfunction paraForiOS(html, sources) {\n    return html.replace(\n        \/<p>(.*?)(?:<comment ident=\"([^\"]*)\" count=\"([^\"]*)\" \\\/>)?<\\\/p>\/g,\n        function(match, text, url, count) {\n            if (url && count) {\n                const encodedUrl = url.replace(\/&\/g, '&amp;');\n                return `<div rs-native>${text}<comment count=\"${count}\" onPress=\"java.showReadingBrowser('${encodedUrl}','${sources}段评')\"><\/div>`;\n            } else {\n                return `<div rs-native>${text}<\/div>`;\n            }\n        }\n    );\n}\n\n\n\n\/\/按钮切换时改变源变量并刷新发现\nfunction show(m, t) {\n    const {\n        java,\n        source\n    } = this;\n    let data = {};\n    try {\n        data = JSON.parse(source.getVariable())\n    } catch {}\n    data[t] = m;\n    if (t == \"发现页类型\") {\n        data[\"发现页来源\"] = '番茄';\n    }\n    source.setVariable(JSON.stringify(data, null, 2));\n    java.refreshExplore();\n}\n\nfunction createFilter(title, chars, defaultVal, paramKey, size, viewName) {\n    if (viewName == undefined) viewName = \"'请选择类型'\";\n    return {\n        title: title,\n        type: \"select\",\n        chars: chars,\n        default: defaultVal,\n        \"viewName\": viewName,\n        action: `show(infoMap['${title}'],'${paramKey}')`,\n        style: {\n            layout_flexGrow: 1,\n            layout_flexBasisPercent: size\n        }\n    };\n}\n\nfunction createLoginFilter(title, chars, defaultVal, paramKey, size, viewName) {\n    const {\n        source,\n        java\n    } = this;\n    if (viewName == undefined) viewName = \"'请选择类型'\";\n    \n    let loginMap = source.getLoginInfoMap();\n    \/\/java.longToast(loginMap);\n    return {\n        name: title,\n        type: \"select\",\n        chars: chars,\n        default: defaultVal,\n        \"viewName\": viewName,\n        action: `show('${loginMap[title]}','${paramKey}')`,\n        style: {\n            layout_flexGrow: 1,\n            layout_flexBasisPercent: size\n        }\n    };\n}\n\n\/\/ 书源更新\nfunction renderVersionPage() {\n    let {\n        java\n    } = this;\n    let hostsbk = this.getVariable('云端配置')['hosts'] || hosts;\n    let yd = '';\n    let html = `\n<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n  <meta charset=\"UTF-8\" \/>\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" \/>\n  <title>书源更新<\/title>\n  <!-- Font Awesome 图标库 -->\n  <link rel=\"stylesheet\" href=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/font-awesome\/6.4.0\/css\/all.min.css\" \/>\n  <style>\n    :root {\n      --primary-gradient: linear-gradient(135deg, #4e6ef2, #6b2dd8);\n      --latest-gradient: linear-gradient(135deg, #8e2de2 0%, #4a00e0 50%, #d4af37 100%);\n      --success-color: #28c76f;\n      --warning-color: #ff9f43;\n      --error-color: #ea5455;\n      --text-main: #1f2937;\n      --text-secondary: #6b7280;\n      --card-bg: #ffffff;\n      --border-color: #e5e7eb;\n      --light-bg: #f9fafb;\n      --shadow: 0 4px 12px rgba(78, 110, 242, 0.1);\n      --shadow-hover: 0 6px 18px rgba(78, 110, 242, 0.2);\n      --glow-shadow: 0 0 25px rgba(142, 45, 226, 0.5), 0 0 50px rgba(212, 175, 55, 0.3);\n      --modal-bg: rgba(31, 41, 55, 0.8);\n      --modal-content-bg: #ffffff;\n    }\n\n    * {\n      box-sizing: border-box;\n      margin: 0;\n      padding: 0;\n      font-family: 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', sans-serif;\n    }\n\n    body {\n      background: linear-gradient(135deg, #eef2ff, #f5f7ff);\n      color: var(--text-main);\n      min-height: 100vh;\n      display: flex;\n      flex-direction: column;\n      align-items: center;\n      justify-content: center;\n      padding: 16px;\n    }\n\n    \/* 加载动画 *\/\n    .loading-wrapper {\n      text-align: center;\n      animation: fadeIn 0.3s ease;\n    }\n\n    .loading-spinner {\n      width: 50px;\n      height: 50px;\n      border: 4px solid rgba(78, 110, 242, 0.3);\n      border-top-color: #4e6ef2;\n      border-radius: 50%;\n      margin: 0 auto 20px;\n      animation: spin 1s linear infinite;\n    }\n\n    .loading-text {\n      color: var(--text-main);\n      font-size: 16px;\n      font-weight: 500;\n    }\n\n    @keyframes spin {\n      to { transform: rotate(360deg); }\n    }\n\n    @keyframes fadeIn {\n      from { opacity: 0; transform: translateY(20px); }\n      to { opacity: 1; transform: translateY(0); }\n    }\n\n    @keyframes slideIn {\n      from { opacity: 0; transform: translateY(30px); }\n      to { opacity: 1; transform: translateY(0); }\n    }\n\n    @keyframes pulse {\n      0%, 100% { opacity: 1; }\n      50% { opacity: 0.7; }\n    }\n\n    @keyframes gradientAnimation {\n      0% { background-position: 0% 50%; }\n      50% { background-position: 100% 50%; }\n      100% { background-position: 0% 50%; }\n    }\n\n    @keyframes breathe {\n      0%, 100% { \n        transform: scale(1);\n        box-shadow: var(--glow-shadow), var(--shadow);\n      }\n      50% { \n        transform: scale(1.02);\n        box-shadow: 0 0 30px rgba(142, 45, 226, 0.6), 0 0 60px rgba(212, 175, 55, 0.4), var(--shadow);\n      }\n    }\n\n    @keyframes shimmer {\n      0% {\n        background-position: -200% center;\n      }\n      100% {\n        background-position: 200% center;\n      }\n    }\n\n    \/* 主容器 *\/\n    .container {\n      width: 100%;\n      max-width: 420px;\n      background: var(--card-bg);\n      border-radius: 24px;\n      overflow: hidden;\n      box-shadow: var(--shadow);\n      position: relative;\n      z-index: 1;\n      animation: slideIn 0.5s ease;\n      display: none;\n    }\n\n    \/* 头部 *\/\n    .header {\n      background: var(--primary-gradient);\n      color: #ffffff;\n      padding: 24px 16px;\n      text-align: center;\n      position: relative;\n      overflow: hidden;\n    }\n\n    .header::before {\n      content: '';\n      position: absolute;\n      top: -30px;\n      left: -30px;\n      width: 80px;\n      height: 80px;\n      background: rgba(255, 255, 255, 0.15);\n      border-radius: 50%;\n    }\n\n    .header::after {\n      content: '';\n      position: absolute;\n      bottom: -60px;\n      right: -60px;\n      width: 150px;\n      height: 150px;\n      background: rgba(255, 255, 255, 0.1);\n      border-radius: 50%;\n    }\n\n    .header h1 {\n      font-size: 1.4rem;\n      font-weight: 700;\n      margin-bottom: 8px;\n      position: relative;\n      z-index: 2;\n    }\n\n    .header p {\n      font-size: 0.9rem;\n      opacity: 0.9;\n      line-height: 1.4;\n      position: relative;\n      z-index: 2;\n    }\n\n    .header-icon {\n      font-size: 48px;\n      margin-bottom: 10px;\n      display: inline-block;\n      animation: bounce 2s ease infinite;\n    }\n\n    @keyframes bounce {\n      0%, 100% { transform: translateY(0); }\n      50% { transform: translateY(-10px); }\n    }\n\n    \/* 版本对比 *\/\n    .version-comparison {\n      display: flex;\n      flex-wrap: nowrap;\n      gap: 12px;\n      padding: 16px;\n      margin-top: 8px;\n      position: relative;\n      z-index: 10;\n    }\n\n    .version-card {\n      flex: 1;\n      min-width: 45%;\n      background: var(--card-bg);\n      border-radius: 16px;\n      padding: 28px 16px 16px;\n      box-shadow: var(--shadow);\n      text-align: center;\n      position: relative;\n      transition: transform 0.3s ease, box-shadow 0.3s ease;\n      overflow: hidden;\n      border: 1px solid rgba(120, 130, 240, 0.1);\n    }\n\n    .version-card:hover {\n      transform: translateY(-4px);\n      box-shadow: var(--shadow-hover);\n    }\n\n    .version-card.current-version {\n      background: linear-gradient(135deg, #ffffff 0%, #f8f9ff 100%);\n      border: 1px solid rgba(78, 110, 242, 0.15);\n    }\n\n    .version-card.current-version:hover {\n      box-shadow: 0 6px 20px rgba(78, 110, 242, 0.15);\n    }\n\n    .version-card.current-version h3,\n    .version-card.current-version .version-number,\n    .version-card.current-version .version-date {\n      color: var(--text-main);\n    }\n\n    .version-card.latest-version {\n      background: var(--latest-gradient);\n      background-size: 300% 300%;\n      box-shadow: var(--glow-shadow), var(--shadow);\n      color: #fff;\n      z-index: 2;\n      animation: gradientAnimation 6s ease infinite, breathe 3s ease-in-out infinite;\n      position: relative;\n      overflow: hidden;\n    }\n\n    .version-card.latest-version::before {\n      content: '';\n      position: absolute;\n      top: -50%;\n      left: -50%;\n      width: 200%;\n      height: 200%;\n      background: linear-gradient(\n        90deg,\n        transparent,\n        rgba(255, 255, 255, 0.3),\n        transparent\n      );\n      transform: rotate(45deg);\n      animation: shimmer 3s infinite;\n    }\n\n    .version-card.latest-version h3,\n    .version-card.latest-version .version-number,\n    .version-card.latest-version .version-date {\n      color: #fff;\n      text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);\n      position: relative;\n      z-index: 1;\n    }\n\n    .version-status {\n      position: absolute;\n      top: 6px;\n      right: 6px;\n      padding: 3px 7px;\n      font-size: 0.65rem;\n      font-weight: 600;\n      border-radius: 6px;\n      color: #fff;\n      line-height: 1.2;\n      white-space: nowrap;\n      z-index: 2;\n    }\n\n    .version-card.latest-version .version-status {\n      background: rgba(255, 255, 255, 0.25);\n      backdrop-filter: blur(5px);\n      border: 1px solid rgba(255, 255, 255, 0.3);\n      color: #fff;\n      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);\n    }\n\n    .status-outdated { background: var(--warning-color); }\n    .status-latest { background: var(--success-color); }\n    .status-invalid { background: var(--error-color); }\n\n    .version-card h3 {\n      font-size: 0.9rem;\n      color: var(--text-secondary);\n      margin-bottom: 8px;\n      font-weight: 500;\n      display: flex;\n      align-items: center;\n      justify-content: center;\n      gap: 6px;\n    }\n\n    .version-number {\n      font-size: 1.25rem;\n      font-weight: 700;\n      color: var(--text-main);\n      margin: 8px 0;\n      transition: all 0.3s ease;\n      font-family: 'Courier New', monospace;\n    }\n\n    .version-card.latest-version .version-number {\n      font-size: 1.4rem;\n      transform: scale(1.05);\n      text-shadow: \n        0 2px 4px rgba(0, 0, 0, 0.3),\n        0 0 10px rgba(212, 175, 55, 0.8),\n        0 0 20px rgba(212, 175, 55, 0.5);\n      animation: pulse-glow 2s ease-in-out infinite;\n    }\n\n    @keyframes pulse-glow {\n      0%, 100% {\n        text-shadow: \n          0 2px 4px rgba(0, 0, 0, 0.3),\n          0 0 10px rgba(212, 175, 55, 0.8),\n          0 0 20px rgba(212, 175, 55, 0.5);\n      }\n      50% {\n        text-shadow: \n          0 2px 4px rgba(0, 0, 0, 0.3),\n          0 0 15px rgba(212, 175, 55, 1),\n          0 0 30px rgba(212, 175, 55, 0.7);\n      }\n    }\n\n    .version-date {\n      font-size: 0.8rem;\n      color: var(--text-secondary);\n      margin-top: 4px;\n    }\n\n    \/* 版本对比指示器 *\/\n    .version-indicator {\n      position: absolute;\n      left: 50%;\n      top: 50%;\n      transform: translate(-50%, -50%);\n      z-index: 5;\n      width: 32px;\n      height: 32px;\n      border-radius: 50%;\n      display: flex;\n      align-items: center;\n      justify-content: center;\n      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);\n      color: white;\n    }\n\n    .version-indicator.update-needed {\n      background: var(--error-color);\n      box-shadow: 0 2px 8px rgba(234, 84, 85, 0.4);\n      animation: pulse-indicator 1.5s infinite;\n    }\n\n    .version-indicator.is-latest {\n      background: var(--success-color);\n      box-shadow: 0 2px 8px rgba(40, 199, 111, 0.4);\n    }\n\n    @keyframes pulse-indicator {\n      0% { transform: translate(-50%, -50%) scale(1); }\n      50% { transform: translate(-50%, -50%) scale(1.1); }\n      100% { transform: translate(-50%, -50%) scale(1); }\n    }\n\n    \/* 内容区 *\/\n    .content-container {\n      padding: 16px;\n    }\n\n    \/* 状态提示 *\/\n    .status-alert {\n      background: var(--card-bg);\n      border-radius: 16px;\n      box-shadow: var(--shadow);\n      margin-bottom: 16px;\n      overflow: hidden;\n      border: 1px solid rgba(120, 130, 240, 0.1);\n      padding: 12px 16px;\n      display: flex;\n      align-items: center;\n      justify-content: center;\n      gap: 10px;\n      font-weight: 500;\n      font-size: 14px;\n      animation: slideIn 0.5s ease 0.3s backwards;\n    }\n\n    .status-alert i {\n      font-size: 20px;\n    }\n\n    .status-alert.update-available {\n      background: linear-gradient(135deg, #ffeaa7 0%, #fdcb6e 100%);\n      color: #d63031;\n      box-shadow: 0 4px 15px rgba(253, 203, 110, 0.4);\n    }\n\n    .status-alert.up-to-date {\n      background: linear-gradient(135deg, #55efc4 0%, #00b894 100%);\n      color: white;\n      box-shadow: 0 4px 15px rgba(0, 184, 148, 0.4);\n    }\n\n    \/* 更新容器 *\/\n    .update-container {\n      background: var(--card-bg);\n      border-radius: 16px;\n      box-shadow: var(--shadow);\n      margin-bottom: 16px;\n      overflow: hidden;\n      border: 1px solid rgba(120, 130, 240, 0.1);\n      animation: slideIn 0.5s ease 0.4s backwards;\n    }\n\n    .update-header {\n      background: var(--light-bg);\n      padding: 12px 16px;\n      display: flex;\n      justify-content: space-between;\n      align-items: center;\n      border-bottom: 1px solid var(--border-color);\n    }\n\n    .update-header h2 {\n      font-size: 1rem;\n      font-weight: 600;\n      color: var(--text-main);\n      display: flex;\n      align-items: center;\n      gap: 8px;\n    }\n\n    .update-header h2 i {\n      color: #4e6ef2;\n    }\n\n    .update-tag {\n      background: rgba(78, 110, 242, 0.1);\n      color: #4e6ef2;\n      padding: 4px 8px;\n      border-radius: 8px;\n      font-size: 0.75rem;\n      font-weight: 600;\n    }\n\n    .update-content {\n      padding: 16px;\n    }\n\n    .update-date {\n      font-weight: 600;\n      color: #4e6ef2;\n      margin-bottom: 12px;\n      display: flex;\n      align-items: center;\n      gap: 6px;\n      padding: 8px 0;\n      border-bottom: 1px dashed #e0e0e0;\n    }\n\n    .update-text {\n      margin: 8px 0;\n      position: relative;\n      padding-left: 16px;\n      line-height: 1.5;\n      color: var(--text-main);\n      font-size: 0.95rem;\n      white-space: pre-wrap;\n      word-break: break-word;\n    }\n\n    .update-text::before {\n      content: '•';\n      position: absolute;\n      left: 0;\n      font-weight: bold;\n      color: #4e6ef2;\n      font-size: 1.2rem;\n      line-height: 1;\n    }\n\n    \/* 历史日志 *\/\n    .history-container {\n      background: var(--card-bg);\n      border-radius: 16px;\n      box-shadow: var(--shadow);\n      margin-bottom: 16px;\n      border: 1px solid rgba(120, 130, 240, 0.1);\n      animation: slideIn 0.5s ease 0.5s backwards;\n    }\n\n    .history-header {\n      background: var(--light-bg);\n      padding: 12px 16px;\n      display: flex;\n      justify-content: space-between;\n      align-items: center;\n      border-bottom: 1px solid var(--border-color);\n      cursor: pointer;\n      user-select: none;\n    }\n\n    .history-header:hover {\n      opacity: 0.8;\n    }\n\n    .history-header h2 {\n      font-size: 1rem;\n      font-weight: 600;\n      color: var(--text-main);\n      display: flex;\n      align-items: center;\n      gap: 8px;\n    }\n\n    .history-header h2 i {\n      color: #4e6ef2;\n    }\n\n    .toggle-history {\n      background: none;\n      border: none;\n      color: var(--text-secondary);\n      cursor: pointer;\n      font-weight: 500;\n      display: flex;\n      align-items: center;\n      gap: 4px;\n      font-size: 0.85rem;\n      transition: color 0.2s ease;\n    }\n\n    .toggle-history:hover {\n      color: #4e6ef2;\n    }\n\n    .history-content {\n      padding: 0 16px;\n      max-height: 0;\n      overflow: hidden;\n      transition: max-height 0.4s ease, padding 0.4s ease;\n    }\n\n    .history-content.expanded {\n      max-height: 60vh;\n      overflow-y: auto;\n      padding: 16px;\n      scrollbar-width: thin;\n      scrollbar-color: #4e6ef2 #f0f0f0;\n    }\n\n    .history-content.expanded::-webkit-scrollbar {\n      width: 6px;\n    }\n\n    .history-content.expanded::-webkit-scrollbar-track {\n      background: #f0f0f0;\n      border-radius: 4px;\n    }\n\n    .history-content.expanded::-webkit-scrollbar-thumb {\n      background: #4e6ef2;\n      border-radius: 4px;\n    }\n\n    .history-content.expanded::-webkit-scrollbar-thumb:hover {\n      background: #3a56d0;\n    }\n\n    .history-item {\n      margin-bottom: 16px;\n      padding-bottom: 16px;\n      border-bottom: 1px dashed var(--border-color);\n    }\n\n    .history-item:last-child {\n      border-bottom: none;\n      margin-bottom: 0;\n      padding-bottom: 0;\n    }\n\n    .history-date {\n      font-weight: 600;\n      color: var(--text-main);\n      margin-bottom: 8px;\n      display: flex;\n      align-items: center;\n      gap: 4px;\n      font-size: 0.9rem;\n      background: rgba(78, 110, 242, 0.05);\n      padding: 6px 10px;\n      border-radius: 6px;\n    }\n\n    .history-text {\n      margin: 8px 0;\n      padding-left: 16px;\n      line-height: 1.4;\n      color: var(--text-secondary);\n      position: relative;\n      font-size: 0.9rem;\n      white-space: pre-wrap;\n      word-break: break-word;\n    }\n\n    .history-text::before {\n      content: '•';\n      position: absolute;\n      left: 0;\n      color: #4e6ef2;\n      font-weight: bold;\n      font-size: 1.2rem;\n      line-height: 1;\n    }\n\n    \/* 按钮组 *\/\n    .button-group {\n      display: flex;\n      flex-direction: column;\n      gap: 10px;\n      margin-bottom: 16px;\n    }\n\n    .button {\n      display: flex;\n      align-items: center;\n      justify-content: center;\n      gap: 10px;\n      padding: 14px 28px;\n      text-align: center;\n      font-size: 1rem;\n      border: none;\n      border-radius: 12px;\n      text-decoration: none;\n      background: var(--primary-gradient);\n      color: white;\n      font-weight: 600;\n      transition: all 0.3s ease;\n      box-shadow: var(--shadow);\n      position: relative;\n      overflow: hidden;\n      cursor: pointer;\n    }\n\n    .button i {\n      font-size: 1rem;\n    }\n\n    .button::after {\n      content: '';\n      position: absolute;\n      top: -50%;\n      left: -50%;\n      width: 200%;\n      height: 200%;\n      background: rgba(255, 255, 255, 0.1);\n      transform: rotate(30deg);\n      transition: all 0.6s ease;\n      pointer-events: none;\n    }\n\n    .button:hover {\n      transform: translateY(-3px);\n      box-shadow: var(--shadow-hover);\n    }\n\n    .button:hover::after {\n      transform: rotate(30deg) translate(20%, 20%);\n    }\n\n    .button:active {\n      transform: scale(0.95);\n    }\n\n    \/* 错误状态 *\/\n    .error-state {\n      text-align: center;\n      padding: 40px 20px;\n      color: var(--text-main);\n    }\n\n    .error-icon {\n      font-size: 64px;\n      margin-bottom: 20px;\n      color: var(--error-color);\n    }\n\n    .error-text {\n      font-size: 16px;\n      line-height: 1.6;\n      margin-bottom: 20px;\n    }\n\n    .retry-button {\n      background: var(--primary-gradient);\n      color: white;\n      padding: 12px 30px;\n      border-radius: 12px;\n      border: none;\n      font-weight: 600;\n      cursor: pointer;\n      transition: all 0.3s ease;\n      font-size: 14px;\n      box-shadow: var(--shadow);\n    }\n\n    .retry-button:hover {\n      transform: translateY(-2px);\n      box-shadow: var(--shadow-hover);\n    }\n\n    .retry-button:active {\n      transform: scale(0.95);\n    }\n\n    \/* 装饰元素 *\/\n    .decoration {\n      position: absolute;\n      z-index: 0;\n      pointer-events: none;\n    }\n\n    .decoration.circle {\n      width: 120px;\n      height: 120px;\n      border-radius: 50%;\n      background: rgba(107, 45, 216, 0.05);\n      top: 10%;\n      left: 10%;\n    }\n\n    .decoration.square {\n      width: 80px;\n      height: 80px;\n      transform: rotate(45deg);\n      background: rgba(78, 110, 242, 0.05);\n      bottom: 10%;\n      right: 10%;\n    }\n\n    \/* 响应式 *\/\n    @media (max-width: 768px) {\n      body {\n        padding: 12px;\n      }\n\n      .container {\n        max-width: 100%;\n        border-radius: 20px;\n      }\n\n      .header {\n        padding: 20px 15px;\n      }\n\n      .header h1 {\n        font-size: 1.3rem;\n      }\n\n      .header-icon {\n        font-size: 40px;\n      }\n\n      .version-comparison {\n        flex-direction: row;\n        flex-wrap: nowrap;\n        gap: 10px;\n        padding: 12px;\n        margin-top: 6px;\n        overflow-x: auto;\n      }\n\n      .version-card {\n        min-width: 45%;\n        padding: 26px 12px 12px;\n      }\n\n      \/* 移动端减弱呼吸动效 *\/\n      .version-card.latest-version {\n        animation: gradientAnimation 6s ease infinite;\n      }\n\n      .version-status {\n        top: 5px;\n        right: 5px;\n        padding: 2px 5px;\n        font-size: 0.6rem;\n      }\n\n      .version-number {\n        font-size: 1.1rem;\n      }\n\n      .version-card.latest-version .version-number {\n        font-size: 1.2rem;\n      }\n\n      .update-header h2, .history-header h2 {\n        font-size: 0.9rem;\n      }\n\n      .button {\n        padding: 12px 24px;\n        font-size: 0.95rem;\n      }\n\n      .history-content.expanded {\n        max-height: 50vh;\n        -webkit-overflow-scrolling: touch;\n      }\n    }\n\n    @media (max-width: 380px) {\n      .header h1 {\n        font-size: 1.2rem;\n      }\n\n      .version-number {\n        font-size: 1rem;\n      }\n\n      .version-card.latest-version .version-number {\n        font-size: 1.1rem;\n      }\n\n      .button {\n        padding: 11px;\n        font-size: 0.9rem;\n      }\n    }\n  <\/style>\n<\/head>\n<body>\n  <div class=\"decoration circle\"><\/div>\n  <div class=\"decoration square\"><\/div>\n\n  <div id=\"loading\" class=\"loading-wrapper\">\n    <div class=\"loading-spinner\"><\/div>\n    <div class=\"loading-text\"><i class=\"fas fa-search\"><\/i> 正在检查更新...<\/div>\n  <\/div>\n\n  <div class=\"container\" id=\"container\">\n    <div class=\"header\">\n      <div class=\"header-icon\"><i class=\"fas fa-book\"><\/i><\/div>\n      <h1>光遇书源更新<\/h1>\n<p>推荐使用阅读Sigma版本<br>正式版可能存在兼容性问题-<a href='https:\/\/legado.gyks.cf' style='text-decoration: none; color: #2D9D78; font-weight: 600;'>下载<\/a><\/p>\n    <\/div>\n\n    <div class=\"version-comparison\">\n      <div class=\"version-card current-version\">\n        <div class=\"version-status status-outdated\" id=\"currentStatus\">待检查<\/div>\n        <h3><i class=\"fas fa-cube\"><\/i> 当前版本<\/h3>\n        <div class=\"version-number\" id=\"currentVersion\">-<\/div>\n        <div class=\"version-date\">您的当前版本<\/div>\n      <\/div>\n\n      <div class=\"version-indicator update-needed\" id=\"versionIndicator\" style=\"display: none;\">\n        <i class=\"fas fa-arrow-right\"><\/i>\n      <\/div>\n\n      <div class=\"version-card latest-version\">\n        <div class=\"version-status status-latest\" id=\"latestStatus\">最新版本<\/div>\n        <h3><i class=\"fas fa-star\"><\/i> 最新版本<\/h3>\n        <div class=\"version-number\" id=\"latestVersion\">-<\/div>\n        <div class=\"version-date\">可用最新版本<\/div>\n      <\/div>\n    <\/div>\n\n    <div class=\"content-container\">\n      <div class=\"status-alert\" id=\"statusAlert\" style=\"display: none;\"><\/div>\n\n      <div id=\"latestLogContainer\" style=\"display: none;\">\n        <div class=\"update-container\">\n          <div class=\"update-header\">\n            <h2><i class=\"fas fa-bolt\"><\/i> 最新更新<\/h2>\n            <div class=\"update-tag\">最新发布<\/div>\n          <\/div>\n          <div class=\"update-content\">\n            <div class=\"update-date\" id=\"latestLogDate\"><\/div>\n            <div class=\"update-text\" id=\"latestLogContent\"><\/div>\n          <\/div>\n        <\/div>\n      <\/div>\n\n      <div class=\"button-group\" id=\"buttonGroup\" style=\"display: none;\"><\/div>\n\n      <div class=\"history-container\" id=\"logs\" style=\"display: none;\">\n        <div class=\"history-header\" onclick=\"toggleLogs()\">\n          <h2><i class=\"fas fa-history\"><\/i> 历史更新 <span id=\"historyCount\"><\/span><\/h2>\n          <button class=\"toggle-history\" id=\"toggleButton\">\n            <span id=\"toggleText\">展开历史<\/span>\n            <i class=\"fas fa-chevron-down\" id=\"toggleIcon\"><\/i>\n          <\/button>\n        <\/div>\n        <div class=\"history-content\" id=\"logList\"><\/div>\n      <\/div>\n    <\/div>\n  <\/div>\n\n  <script>\n  let logsCollapsed = true;\n\n  function toggleLogs() {\n    logsCollapsed = !logsCollapsed;\n    const logList = document.getElementById('logList');\n    const toggleText = document.getElementById('toggleText');\n    const toggleIcon = document.getElementById('toggleIcon');\n    \n    if (logsCollapsed) {\n      logList.classList.remove('expanded');\n      toggleText.textContent = '展开历史';\n      toggleIcon.className = 'fas fa-chevron-down';\n    } else {\n      logList.classList.add('expanded');\n      toggleText.textContent = '收起历史';\n      toggleIcon.className = 'fas fa-chevron-up';\n    }\n  }\n\n  (async function() {\n    const loading = document.getElementById('loading');\n    const container = document.getElementById('container');\n    const currentVersion = document.getElementById('currentVersion');\n    const latestVersion = document.getElementById('latestVersion');\n    const currentStatus = document.getElementById('currentStatus');\n    const latestStatus = document.getElementById('latestStatus');\n    const versionIndicator = document.getElementById('versionIndicator');\n    const statusAlert = document.getElementById('statusAlert');\n    const buttonGroup = document.getElementById('buttonGroup');\n    const latestLogContainer = document.getElementById('latestLogContainer');\n    const latestLogDate = document.getElementById('latestLogDate');\n    const latestLogContent = document.getElementById('latestLogContent');\n    const logsContainer = document.getElementById('logs');\n    const logList = document.getElementById('logList');\n    const historyCount = document.getElementById('historyCount');\n\n    const localVer = '${String(localVersion)}';\n    let hosts = \\`${hostsbk}\\`;\n    hosts = hosts.split(',');\n    \nconst icons = ['box', 'satellite', 'link', 'bolt', 'globe', 'broadcast-tower'];\n\n\/\/ 初始化serverConfig\nconst serverConfig = {\n    main: {\n        name: '主线路导入',\n        icon: 'rocket',\n        baseUrl: 'https:\/\/sy.gyks.cf',\n        downloadPath: '\/download\/光遇聚合.json'\n    }\n};\n\nhosts.forEach((host, index) => {\n    const backupNumber = index + 1;\n    const key =\\`备用线路\\${backupNumber}\\`;\n\n    serverConfig[key] = {\n        name: \\`\\${key}导入\\`,\n        icon: icons[index] || 'broadcast-tower',\n        baseUrl: host,\n        downloadPath: '\/sy\/download\/光遇聚合.json'\n    };\n});\n\n    \/\/ 版本比较函数\n    function compareVersions(vs) {\n      const normalize = (v) => {\n        return v.split('.').map(n => {\n          const num = parseInt(n, 10);\n          return isNaN(num) ? 0 : num;\n        });\n      };\n\n      const parts1 = normalize(localVer);\n      const parts2 = normalize(vs);\n      const maxLength = Math.max(parts1.length, parts2.length);\n      \n      for (let i = 0; i < maxLength; i++) {\n        const num1 = parts1[i] || 0;\n        const num2 = parts2[i] || 0;\n        if (num1 > num2) return 1;\n        if (num1 < num2) return -1;\n      }\n      return 0;\n    }\n\nasync function fetchVersionData() {\n  const serversToCheck = Object.values(serverConfig);\n  \n  for (const server of serversToCheck) {\n    try {\n      const apiPath = server.baseUrl.includes('sy.gyks.cf') \n        ? '\/fils-groups' \n        : '\/get_sy_log';\n      \n      const response = await fetch(server.baseUrl + apiPath, { timeout: 2000 });\n      if (response.ok) {\n        return await response.json();\n      }\n    } catch (e) {\n      console.warn(\\`接口失败:\\${server.baseUrl}\\`, e);\n    }\n  }\n  throw new Error('所有更新接口都请求失败');\n}\n\n\n    function showError(message) {\n      loading.innerHTML = \\`\n        <div class=\"error-state\">\n          <div class=\"error-icon\"><i class=\"fas fa-exclamation-triangle\"><\/i><\/div>\n          <div class=\"error-text\">\\${message}<\/div>\n          <button class=\"retry-button\" onclick=\"location.reload()\"><i class=\"fas fa-redo\"><\/i> 重试<\/button>\n        <\/div>\n      \\`;\n    }\n\n    try {\n      const data = await fetchVersionData();\nconst legadoData = (data.data && data.data.find(item => item.key == 'legado_aggregate')) || {};\nconst cloudVersion = String(legadoData.current_version || '');\nconst updateLog = legadoData.update_log || [];\n\n\/\/ 显示版本信息\ncurrentVersion.textContent = \\`v\\${localVer}\\`;\nlatestVersion.textContent = \\`v\\${cloudVersion}\\`;\n\n\/\/ 处理日志(update_log 是数组格式)\nif (updateLog.length > 0) {\n\/\/ 显示最新日志\nconst latestLog = updateLog[0];\nlatestLogDate.innerHTML = \\`<i class=\"fas fa-calendar-alt\"><\/i> v\\${latestLog.version}\\`;\nlatestLogContent.innerHTML = latestLog.log;\nlatestLogContainer.style.display = 'block';\n\n\/\/ 显示历史日志\nif (updateLog.length > 1) {\nconst historyLogs = updateLog.slice(1);\nhistoryCount.textContent = \\`(\\${historyLogs.length}条)\\`;\nlogList.innerHTML = historyLogs.map(item => \\`\n<div class=\"history-item\">\n    <div class=\"history-date\">\n    <i class=\"fas fa-calendar-day\"><\/i>\n    <span>v\\${item.version}<\/span>\n    <\/div>\n    <div class=\"history-text\">\\${item.log}<\/div>\n<\/div>\n\\`).join('');\nlogsContainer.style.display = 'block';\n}\n}\n\n\/\/ 检查更新状态\nconst compareResult = compareVersions(cloudVersion);\n\n\/\/ 显示版本指示器\nversionIndicator.style.display = 'flex';\n\nif (compareResult === -1) {\n\/\/ 需要更新\ncurrentStatus.textContent = '待更新';\ncurrentStatus.className = 'version-status status-outdated';\nversionIndicator.className = 'version-indicator update-needed';\nversionIndicator.innerHTML = '<i class=\"fas fa-arrow-right\"><\/i>';\n\n\/\/ 使用统一配置生成下载按钮\nbuttonGroup.innerHTML = Object.values(serverConfig).map(server => {\nconst fullUrl = server.baseUrl + server.downloadPath;\nreturn \\`\n<a href=\"yuedu:\/\/booksource\/importonline?src=\\${encodeURIComponent(fullUrl)}\" class=\"button\">\n    <i class=\"fas fa-\\${server.icon}\"><\/i>\n    <span>\\${server.name}<\/span>\n<\/a>\n\\`;\n}).join('');\nbuttonGroup.style.display = 'flex';\n} else {\n\/\/ 已是最新版本\ncurrentStatus.textContent = '最新';\ncurrentStatus.className = 'version-status status-latest';\nversionIndicator.className = 'version-indicator is-latest';\nversionIndicator.innerHTML = '<i class=\"fas fa-check\"><\/i>';\n\nstatusAlert.className = 'status-alert up-to-date';\nstatusAlert.innerHTML = '<i class=\"fas fa-check-circle\"><\/i> <div>您已是最新版本<\/div>';\nstatusAlert.style.display = 'flex';\n}\n\n      \/\/ 显示主容器,隐藏加载\n      loading.style.display = 'none';\n      container.style.display = 'block';\n\n    } catch (err) {\n      console.error('版本检查失败:', err);\n      showError('<i class=\"fas fa-exclamation-circle\"><\/i> 检查更新失败,请稍后重试<br><small>' + err.message + '<\/small>');\n    }\n  })();\n  <\/script>\n<\/body>\n<\/html>\n`;\n    java.startBrowser(`data:text\/html;base64,${java.base64Encode(html)}`, '光遇书源更新');\n}\n\n\n\/\/ 段评设置页面\nfunction getSvgSettings() {\n    const {\n        java\n    } = this;\n    const islyc = this.checkEnv();\n    if (islyc == \"苹果\") {\n        java.longToast(\"\\n当前软件不支持设置段评样式\");\n        return;\n    }\n    let data = this.getVariable('段评设置');\n\n    let html = `\n<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no\">\n<title>光遇小说 - 段评设置<\/title>\n<link rel=\"stylesheet\" href=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/font-awesome\/6.4.0\/css\/all.min.css\">\n<style>\n* {\n  margin: 0;\n  padding: 0;\n  box-sizing: border-box;\n  -webkit-tap-highlight-color: transparent;\n}\n\n:root {\n  --color-primary: #2D9D78;\n  --color-primary-light: #3DB893;\n  --color-primary-dark: #1E6F55;\n  --color-accent: #FFB74D;\n  --color-success: #4CAF50;\n  --color-error: #F44336;\n  --color-warning: #FFC107;\n  --color-info: #2196F3;\n  \n  --bg-dark: #f5f7fa;\n  --bg-darker: #ffffff;\n  --bg-card: rgba(255, 255, 255, 0.95);\n  --bg-elevated: #f8fafb;\n  \n  --text-primary: #1a1d23;\n  --text-secondary: #6b7280;\n  --text-tertiary: #9ca3af;\n  \n  --border-color: rgba(0, 0, 0, 0.08);\n  \n  --font-display: 'Libre Baskerville', serif;\n  --font-body: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;\n}\n\n[data-theme=\"dark\"] {\n  --bg-dark: #0A1628;\n  --bg-darker: #050B14;\n  --bg-card: #142235;\n  --bg-elevated: #1A2B42;\n  \n  --text-primary: #FFFFFF;\n  --text-secondary: rgba(255, 255, 255, 0.7);\n  --text-tertiary: rgba(255, 255, 255, 0.5);\n  \n  --border-color: rgba(255, 255, 255, 0.1);\n}\n\nhtml, body {\n  width: 100%;\n  min-height: 100vh;\n  font-family: var(--font-body);\n  font-size: 16px;\n  color: var(--text-primary);\n  background: var(--bg-darker);\n  overflow-x: hidden;\n}\n\n.container {\n  padding: 20px;\n  max-width: 800px;\n  margin: 0 auto;\n}\n\n.main-card {\n  background: var(--bg-card);\n  border-radius: 20px;\n  box-shadow: 0 4px 24px rgba(0, 0, 0, 0.1);\n  border: 1px solid var(--border-color);\n  overflow: hidden;\n}\n\/* 提示文案样式 *\/\n.tip-alert {\n  background: linear-gradient(135deg, rgba(255, 193, 7, 0.12) 0%, rgba(255, 152, 0, 0.08) 100%);\n  border: 1px solid rgba(255, 193, 7, 0.25);\n  border-radius: 16px;\n  padding: 16px 18px;\n  margin: 20px;\n  display: flex;\n  align-items: flex-start;\n  gap: 14px;\n  box-shadow: 0 4px 16px rgba(255, 193, 7, 0.15), inset 0 1px 0 rgba(255, 255, 255, 0.8);\n  position: relative;\n  overflow: hidden;\n}\n\n.tip-alert::before {\n  content: '';\n  position: absolute;\n  top: -50%;\n  right: -20%;\n  width: 100px;\n  height: 100px;\n  background: rgba(255, 193, 7, 0.1);\n  border-radius: 50%;\n}\n\n.tip-alert i {\n  color: #f97316;\n  font-size: 22px;\n  flex-shrink: 0;\n  margin-top: 2px;\n  position: relative;\n  z-index: 1;\n}\n\n.tip-alert p {\n  font-size: 13px;\n  color: var(--text-secondary);\n  line-height: 1.6;\n  margin: 0;\n  position: relative;\n  z-index: 1;\n}\n\n.tip-alert .highlight-text {\n  color: #ea580c;\n  font-weight: 700;\n}\n\/* 设置项样式 *\/\n.setting-item {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  padding: 16px;\n  border-bottom: 1px solid var(--border-color);\n}\n\n.setting-item:last-child {\n  border-bottom: none;\n}\n\n.setting-info {\n  flex: 1;\n  min-width: 0;\n}\n\n.setting-title {\n  font-size: 15px;\n  font-weight: 700;\n  color: var(--text-primary);\n  margin-bottom: 4px;\n}\n\n.setting-desc {\n  font-size: 12px;\n  color: var(--text-secondary);\n  line-height: 1.4;\n}\n\n\/* 开关样式 *\/\n.switch {\n  position: relative;\n  display: inline-block;\n  width: 48px;\n  height: 28px;\n  flex-shrink: 0;\n}\n\n.switch input {\n  opacity: 0;\n  width: 0;\n  height: 0;\n}\n\n.slider {\n  position: absolute;\n  cursor: pointer;\n  top: 0;\n  left: 0;\n  right: 0;\n  bottom: 0;\n  background-color: #ccc;\n  transition: 0.3s;\n  border-radius: 28px;\n}\n\n.slider:before {\n  position: absolute;\n  content: \"\";\n  height: 22px;\n  width: 22px;\n  left: 3px;\n  bottom: 3px;\n  background-color: white;\n  transition: 0.3s;\n  border-radius: 50%;\n  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);\n}\n\ninput:checked + .slider {\n  background: linear-gradient(135deg, var(--color-primary), var(--color-primary-light));\n}\n\ninput:checked + .slider:before {\n  transform: translateX(20px);\n}\n\n\/* 选择器样式 *\/\n.select-wrapper {\n  position: relative;\n  flex-shrink: 0;\n}\n\n.select-wrapper select {\n  appearance: none;\n  -webkit-appearance: none;\n  padding: 8px 32px 8px 12px;\n  font-size: 13px;\n  font-weight: 600;\n  color: var(--text-primary);\n  background: var(--bg-elevated);\n  border: 1px solid var(--border-color);\n  border-radius: 8px;\n  cursor: pointer;\n  min-width: 120px;\n}\n\n.select-wrapper::after {\n  content: '\\\\f078';\n  font-family: 'Font Awesome 6 Free';\n  font-weight: 900;\n  position: absolute;\n  right: 8px;\n  top: 50%;\n  transform: translateY(-50%);\n  color: var(--text-secondary);\n  pointer-events: none;\n  font-size: 12px;\n}\n\n\/* 颜色选择器样式 *\/\n.color-picker-wrapper {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  flex-shrink: 0;\n}\n\n.color-input {\n  width: 40px;\n  height: 40px;\n  border: none;\n  border-radius: 8px;\n  cursor: pointer;\n  background: transparent;\n  padding: 2px;\n}\n\n.color-input::-webkit-color-swatch-wrapper {\n  padding: 0;\n}\n\n.color-input::-webkit-color-swatch {\n  border-radius: 6px;\n  border: 2px solid var(--border-color);\n}\n\n.color-text-input {\n  width: 100px;\n  padding: 8px 10px;\n  font-size: 12px;\n  font-weight: 600;\n  color: var(--text-primary);\n  background: var(--bg-elevated);\n  border: 1px solid var(--border-color);\n  border-radius: 6px;\n  text-transform: uppercase;\n}\n\n.transparent-btn {\n  padding: 8px 12px;\n  font-size: 11px;\n  font-weight: 600;\n  color: var(--color-primary);\n  background: rgba(45, 157, 120, 0.1);\n  border: 1px solid var(--color-primary);\n  border-radius: 6px;\n  cursor: pointer;\n  transition: all 0.2s ease;\n}\n\n.transparent-btn:hover {\n  background: rgba(45, 157, 120, 0.2);\n}\n\n\/* 预览区域 *\/\n\/* 自定义样式容器 *\/\n.custom-style-container {\n  padding: 0 16px 16px;\n}\n\n.custom-style-container textarea {\n  width: 100%;\n  padding: 12px;\n  font-size: 13px;\n  font-family: monospace;\n  color: var(--text-primary);\n  background: var(--bg-card);\n  border: 1px solid var(--border-color);\n  border-radius: 8px;\n  resize: vertical;\n  min-height: 100px;\n  box-sizing: border-box;\n}\n\n\/* 添加样式按钮 *\/\n.add-style-btn {\n  width: 36px;\n  height: 36px;\n  border-radius: 50%;\n  background: linear-gradient(135deg, var(--color-primary), var(--color-primary-light));\n  border: none;\n  color: white;\n  font-size: 16px;\n  cursor: pointer;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  box-shadow: 0 2px 8px rgba(45, 157, 120, 0.3);\n  transition: all 0.2s ease;\n}\n\n.add-style-btn:hover {\n  transform: scale(1.1);\n  box-shadow: 0 4px 12px rgba(45, 157, 120, 0.4);\n}\n\n\/* 自定义样式区域 *\/\n.custom-style-section {\n  padding: 0 16px 16px;\n}\n\n.custom-styles-container {\n  margin-top: 12px;\n  display: flex;\n  flex-direction: column;\n  gap: 12px;\n}\n\n.empty-custom-styles {\n  text-align: center;\n  padding: 20px;\n  color: var(--text-secondary);\n  font-size: 13px;\n  background: var(--bg-elevated);\n  border-radius: 12px;\n  border: 1px dashed var(--border-color);\n}\n\n\/* 自定义样式卡片 *\/\n.custom-style-card {\n  background: var(--bg-elevated);\n  border-radius: 12px;\n  padding: 12px;\n  border: 1px solid var(--border-color);\n}\n\n.custom-style-header {\n  display: flex;\n  align-items: center;\n  gap: 12px;\n  margin-bottom: 10px;\n}\n\n.style-name-input {\n  flex: 1;\n  padding: 6px 10px;\n  font-size: 13px;\n  font-weight: 600;\n  color: var(--text-primary);\n  background: var(--bg-card);\n  border: 1px solid var(--border-color);\n  border-radius: 6px;\n}\n\n.style-actions {\n  display: flex;\n  gap: 6px;\n}\n\n.style-btn {\n  width: 28px;\n  height: 28px;\n  border: none;\n  border-radius: 6px;\n  cursor: pointer;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  font-size: 12px;\n  transition: all 0.2s ease;\n}\n\n.preview-btn {\n  background: rgba(45, 157, 120, 0.1);\n  color: var(--color-primary);\n}\n\n.preview-btn:hover {\n  background: rgba(45, 157, 120, 0.2);\n}\n\n.delete-btn {\n  background: rgba(244, 67, 54, 0.1);\n  color: #f44336;\n}\n\n.delete-btn:hover {\n  background: rgba(244, 67, 54, 0.2);\n}\n\n.style-code-textarea {\n  width: 100%;\n  padding: 10px;\n  font-size: 12px;\n  font-family: monospace;\n  color: var(--text-primary);\n  background: var(--bg-card);\n  border: 1px solid var(--border-color);\n  border-radius: 8px;\n  resize: vertical;\n  min-height: 80px;\n  box-sizing: border-box;\n  margin-bottom: 10px;\n}\n\n.style-preview-small {\n  display: flex;\n  justify-content: center;\n  padding: 10px;\n  background: var(--bg-card);\n  border-radius: 8px;\n}\n\n.preview-svg-container svg {\n  width: 50px;\n  height: 45px;\n}\n\n.preview-section {\n  padding: 20px;\n  background: var(--bg-elevated);\n  border-top: 1px solid var(--border-color);\n}\n\n.preview-title {\n  font-size: 14px;\n  font-weight: 700;\n  color: var(--text-primary);\n  margin-bottom: 16px;\n  display: flex;\n  align-items: center;\n  gap: 8px;\n}\n\n.preview-title i {\n  color: var(--color-primary);\n}\n\n.preview-container {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 16px;\n  justify-content: center;\n  align-items: center;\n  min-height: 150px;\n}\n\n.preview-item {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  gap: 8px;\n  padding: 12px;\n  background: var(--bg-card);\n  border-radius: 12px;\n  border: 1px solid var(--border-color);\n}\n\n.preview-item.selected {\n  border-color: var(--color-primary);\n  background: rgba(45, 157, 120, 0.05);\n}\n\n.preview-item svg {\n  width: 60px;\n  height: 54px;\n  cursor: pointer;\n  transition: transform 0.2s ease;\n}\n\n.preview-item svg:hover {\n  transform: scale(1.1);\n}\n\n.preview-item .preview-label {\n  font-size: 11px;\n  color: var(--text-secondary);\n  text-align: center;\n}\n\n.preview-item.selected .preview-label {\n  color: var(--color-primary);\n  font-weight: 600;\n}\n\n\/* 高亮文本 *\/\n.highlight {\n  color: var(--color-primary);\n  font-weight: 700;\n}\n\n.highlight-red {\n  color: #ef4444;\n  font-weight: 700;\n}\n\n\/* 响应式 *\/\n@media (max-width: 600px) {\n  .container {\n    padding: 12px;\n  }\n  \n  .main-card {\n    border-radius: 16px;\n  }\n\n  .tip-alert {\n    margin: 16px;\n    padding: 14px 16px;\n  }\n    \n  .setting-item {\n    padding: 14px;\n  }\n  \n  .color-picker-wrapper {\n    flex-direction: column;\n    align-items: flex-end;\n  }\n}\n<\/style>\n<\/head>\n<body data-theme=\"light\">\n<div class=\"container\">\n  <div class=\"main-card\">\n    <div class=\"tip-alert\">\n      <p>设置完成后请点击右上角的<span class=\"highlight-text\">√<\/span>应用设置<\/p>\n    <\/div>\n    <!-- 设置项 -->\n    <div class=\"setting-item\">\n      <div class=\"setting-info\">\n        <div class=\"setting-title\">段评开关<\/div>\n        <div class=\"setting-desc\">仅 <span class=\"highlight-red\">番茄<\/span>、<span class=\"highlight\">七猫<\/span>、<span class=\"highlight-red\">塔读<\/span>、<span class=\"highlight\">QQ阅读<\/span> 支持<\/div>\n      <\/div>\n      <label class=\"switch\">\n        <input type=\"checkbox\" id=\"duanpingSwitch\" checked>\n        <span class=\"slider\"><\/span>\n      <\/label>\n    <\/div>\n    \n    <div class=\"setting-item\">\n      <div class=\"setting-info\">\n        <div class=\"setting-title\">气泡增大<\/div>\n        <div class=\"setting-desc\">开启后,气泡显示会加大一些<\/div>\n      <\/div>\n      <label class=\"switch\">\n        <input type=\"checkbox\" id=\"bubbleLargeSwitch\">\n        <span class=\"slider\"><\/span>\n      <\/label>\n    <\/div>\n    \n    <div class=\"setting-item\">\n      <div class=\"setting-info\">\n        <div class=\"setting-title\">段评样式<\/div>\n        <div class=\"setting-desc\">选择段评显示的样式风格,下方可实时预览效果<\/div>\n      <\/div>\n      <div class=\"select-wrapper\">\n        <select id=\"duanpingStyle\">\n          <option value=\"0\">起点样式<\/option>\n          <option value=\"1\">番茄样式<\/option>\n          <option value=\"2\">精致样式<\/option>\n          <option value=\"3\">对话样式<\/option>\n          <option value=\"4\">方框样式<\/option>\n          <option value=\"5\">圆圈样式<\/option>\n        <\/select>\n      <\/div>\n    <\/div>\n    \n    <div class=\"setting-item\">\n      <div class=\"setting-info\">\n        <div class=\"setting-title\">边框颜色<\/div>\n        <div class=\"setting-desc\">设置段评边框颜色<\/div>\n      <\/div>\n      <div class=\"color-picker-wrapper\">\n        <input type=\"color\" id=\"borderColor\" value=\"#A7A7A7\" class=\"color-input\">\n        <input type=\"text\" id=\"borderColorText\" value=\"#A7A7A7\" class=\"color-text-input\">\n      <\/div>\n    <\/div>\n    \n    <div class=\"setting-item\">\n      <div class=\"setting-info\">\n        <div class=\"setting-title\">字体颜色<\/div>\n        <div class=\"setting-desc\">设置段评字体颜色<\/div>\n      <\/div>\n      <div class=\"color-picker-wrapper\">\n        <input type=\"color\" id=\"fontColor\" value=\"#A7A7A7\" class=\"color-input\">\n        <input type=\"text\" id=\"fontColorText\" value=\"#A7A7A7\" class=\"color-text-input\">\n      <\/div>\n    <\/div>\n    \n    <div class=\"setting-item\">\n      <div class=\"setting-info\">\n        <div class=\"setting-title\">填充颜色<\/div>\n        <div class=\"setting-desc\">设置段评背景填充颜色<\/div>\n      <\/div>\n      <div class=\"color-picker-wrapper\">\n        <input type=\"color\" id=\"fillColor\" value=\"#FFFFFF\" class=\"color-input\">\n        <input type=\"text\" id=\"fillColorText\" value=\"transparent\" class=\"color-text-input\">\n        <button id=\"fillColorTransparent\" class=\"transparent-btn\">透明<\/button>\n      <\/div>\n    <\/div>\n    \n    <!-- 自定义样式管理 -->\n    <div class=\"custom-style-section\">\n      <div class=\"setting-item\">\n        <div class=\"setting-info\">\n          <div class=\"setting-title\">自定义样式管理<\/div>\n          <div class=\"setting-desc\">点击下方\"+\"添加新的自定义样式,支持自定义名称<\/div>\n        <\/div>\n        <button id=\"addCustomStyleBtn\" class=\"add-style-btn\">\n          <i class=\"fas fa-plus\"><\/i>\n        <\/button>\n      <\/div>\n      \n      <div id=\"customStylesContainer\" class=\"custom-styles-container\">\n      <\/div>\n    <\/div>\n    \n    <!-- 实时预览区域 -->\n    <div class=\"preview-section\">\n      <div class=\"preview-title\">\n        <i class=\"fas fa-eye\"><\/i>\n        <span>实时预览<\/span>\n      <\/div>\n      <div id=\"previewContainer\" class=\"preview-container\"><\/div>\n    <\/div>\n  <\/div>\n  \n  <!-- 隐藏的设置数据 -->\n  <div id=\"hiddenSettings\" style=\"display: none;\">\n    <div id=\"settingsDiv\">\n      <span id=\"duanpingSwitchValue\">${(data && data.段评开关) || 'true'}<\/span>\n      <span id=\"bubbleLargeSwitchValue\">${(data && data.气泡增大) || 'false'}<\/span>\n      <span id=\"duanpingStyleValue\">${(data && data.段评样式) || '0'}<\/span>\n      <span id=\"borderColorValue\">${(data && data.段评边框颜色) || '#A7A7A7'}<\/span>\n      <span id=\"fontColorValue\">${(data && data.段评字体颜色) || '#A7A7A7'}<\/span>\n      <span id=\"fillColorValue\">${(data && data.段评填充颜色) || 'transparent'}<\/span>\n    <\/div>\n    <textarea id=\"customStylesValue\" style=\"display: none;\">${typeof (data && data.自定义段评样式) == 'object' ? JSON.stringify(data.自定义段评样式) : ((data && data.自定义段评样式) || '{}')}<\/textarea>\n  <\/div>\n<\/div>\n\n<script>\n\/\/ SVG模板数据\nconst SVG_TEMPLATES = ${JSON.stringify(svgs)};\nconst STYLE_NAMES = ['起点样式', '番茄样式', '精致样式', '对话样式', '方框样式', '圆圈样式'];\n\n\/\/ 当前设置\nlet settings = {\n  '段评开关': '${(data && data.段评开关) || 'true'}',\n  '气泡增大': '${(data && data.气泡增大) || 'false'}',\n  '段评样式': '${typeof (data && data.自定义段评样式) == 'object' && (data && data.段评样式) ? data.段评样式 : ((data && data.段评样式) || '0')}',\n  '段评边框颜色': '${(data && data.段评边框颜色) || '#A7A7A7'}',\n  '段评字体颜色': '${(data && data.段评字体颜色) || '#A7A7A7'}',\n  '段评填充颜色': '${(data && data.段评填充颜色) || 'transparent'}'\n};\n\n\/\/ 自定义样式对象 {\"自定义样式1\": \"<svg>...<\/svg>\", ...}\nlet customStyles = {};\nfunction initCustomStyles() {\n  try {\n    let customStylesStr = document.getElementById('customStylesValue').textContent;\n    if (!customStylesStr) {\n      customStylesStr = '{}';\n    }\n    customStyles = JSON.parse(htmlUnescape(customStylesStr));\n  } catch (e) {\n    customStyles = {};\n  }\n}\n\n\/\/ HTML转义函数\nfunction htmlEscape(str) {\n  if (!str) return str;\n  return str.replace(\/&\/g, '&amp;')\n            .replace(\/<\/g, '&lt;')\n            .replace(\/>\/g, '&gt;')\n            .replace(\/\"\/g, '&quot;')\n            .replace(\/'\/g, '&#039;');\n}\n\n\/\/ HTML反转义函数\nfunction htmlUnescape(str) {\n  if (!str) return str;\n  return str.replace(\/&amp;\/g, '&')\n            .replace(\/&lt;\/g, '<')\n            .replace(\/&gt;\/g, '>')\n            .replace(\/&quot;\/g, '\"')\n            .replace(\/&#039;\/g, \"'\");\n}\n\n\/\/ 更新隐藏设置\nfunction updateHiddenSettings() {\n  document.getElementById('duanpingSwitchValue').textContent = settings['段评开关'];\n  document.getElementById('bubbleLargeSwitchValue').textContent = settings['气泡增大'];\n  document.getElementById('duanpingStyleValue').textContent = settings['段评样式'];\n  document.getElementById('borderColorValue').textContent = settings['段评边框颜色'];\n  document.getElementById('fontColorValue').textContent = settings['段评字体颜色'];\n  document.getElementById('fillColorValue').textContent = settings['段评填充颜色'];\n  document.getElementById('customStylesValue').textContent = htmlEscape(JSON.stringify(customStyles));\n}\n\n\/\/ 获取下一个自定义样式名称\nfunction getNextStyleName() {\n  let count = 1;\n  while (customStyles['自定义样式' + count]) {\n    count++;\n  }\n  return '自定义样式' + count;\n}\n\n\/\/ 添加自定义样式\nfunction addCustomStyle() {\n  const name = getNextStyleName();\n  customStyles[name] = '<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"100\" height=\"90\" viewBox=\"0 0 25 17\" fill=\"none\"><rect x=\"0.5\" y=\"0.5\" width=\"24\" height=\"16\" rx=\"2.5\" ry=\"2.5\" stroke=\"$color\" style=\"stroke-opacity: 0.5; fill: $tccolor; fill-opacity: 0.5;\"\/><text x=\"12.5\" y=\"12\" text-anchor=\"middle\" font-size=\"10\" fill=\"$fontcolor\" font-family=\"Arial, sans-serif\">$displayText<\/text><\/svg>';\n  renderCustomStyles();\n  updateHiddenSettings();\n  updateStyleSelector();\n  renderPreview();\n}\n\n\/\/ 获取选中的样式名称\nfunction getSelectedStyleName() {\n  const styleValue = settings['段评样式'];\n  \/\/ 如果是数字,返回默认样式名称\n  if (!isNaN(styleValue)) {\n    return STYLE_NAMES[parseInt(styleValue)] || styleValue;\n  }\n  \/\/ 如果是自定义样式,返回自定义名称\n  return styleValue;\n}\n\n\/\/ 删除自定义样式\nfunction deleteCustomStyle(name) {\n  if (confirm('确定删除 \"' + name + '\" 吗?')) {\n    delete customStyles[name];\n    \/\/ 如果删除的是当前选中的样式,切换到默认样式\n    if (settings['段评样式'] === name) {\n      settings['段评样式'] = '0';\n    }\n    renderCustomStyles();\n    updateHiddenSettings();\n    updateStyleSelector();\n    renderPreview();\n  }\n}\n\n\/\/ 更新自定义样式\nfunction updateCustomStyle(name, value) {\n  customStyles[name] = value;\n  updateHiddenSettings();\n  renderPreview();\n}\n\n\/\/ 重命名自定义样式\nfunction renameCustomStyle(oldName, newName) {\n  if (!newName || newName.trim() == '') return;\n  if (customStyles[newName] && newName != oldName) {\n    alert('名称已存在');\n    return;\n  }\n  customStyles[newName] = customStyles[oldName];\n  delete customStyles[oldName];\n  \/\/ 如果当前选中的是旧名称,更新为新名称\n  if (settings['段评样式'] === oldName) {\n    settings['段评样式'] = newName;\n  }\n  renderCustomStyles();\n  updateHiddenSettings();\n  updateStyleSelector();\n  renderPreview();\n}\n\n\/\/ 渲染自定义样式列表\nfunction renderCustomStyles() {\n  const container = document.getElementById('customStylesContainer');\n  container.innerHTML = '';\n  \n  if (Object.keys(customStyles).length === 0) {\n    container.innerHTML = '<div class=\"empty-custom-styles\">暂无自定义样式,点击上方\"+\"添加<\/div>';\n    return;\n  }\n  \n  Object.keys(customStyles).forEach((name, index) => {\n    const styleCard = document.createElement('div');\n    styleCard.className = 'custom-style-card';\n    \/\/ 转义SVG代码中的特殊字符\n    const escapedSvg = customStyles[name]\n      .replace(\/&\/g, '&amp;')\n      .replace(\/<\/g, '&lt;')\n      .replace(\/>\/g, '&gt;')\n      .replace(\/\"\/g, '&quot;')\n      .replace(\/'\/g, '&#039;');\n    \n    styleCard.innerHTML = \\`\n      <div class=\"custom-style-header\">\n        <input type=\"text\" class=\"style-name-input\" value=\"\\${name}\" data-original=\"\\${name}\">\n        <div class=\"style-actions\">\n          <button class=\"style-btn preview-btn\" title=\"预览\">\n            <i class=\"fas fa-eye\"><\/i>\n          <\/button>\n          <button class=\"style-btn delete-btn\" title=\"删除\">\n            <i class=\"fas fa-trash\"><\/i>\n          <\/button>\n        <\/div>\n      <\/div>\n      <textarea class=\"style-code-textarea\" rows=\"4\" placeholder=\"输入SVG代码...\">\\${escapedSvg}<\/textarea>\n      <div class=\"style-preview-small\">\n        <div class=\"preview-svg-container\"><\/div>\n      <\/div>\n    \\`;\n    \n    \/\/ 绑定事件\n    const nameInput = styleCard.querySelector('.style-name-input');\n    const codeTextarea = styleCard.querySelector('.style-code-textarea');\n    const deleteBtn = styleCard.querySelector('.delete-btn');\n    const previewBtn = styleCard.querySelector('.preview-btn');\n    \n    nameInput.addEventListener('change', () => {\n      const newName = nameInput.value.trim();\n      const originalName = nameInput.dataset.original;\n      if (newName != originalName) {\n        renameCustomStyle(originalName, newName);\n      }\n    });\n    \n    codeTextarea.addEventListener('input', () => {\n      updateCustomStyle(name, codeTextarea.value);\n      \/\/ 更新小预览\n      updateSmallPreview(styleCard.querySelector('.preview-svg-container'), codeTextarea.value);\n    });\n    \n    deleteBtn.addEventListener('click', () => deleteCustomStyle(name));\n    \n    previewBtn.addEventListener('click', () => {\n      \/\/ 设置为当前选中样式(使用自定义名称)\n      settings['段评样式'] = name;\n      document.getElementById('duanpingStyle').value = name;\n      updateHiddenSettings();\n      renderPreview();\n    });\n    \n    \/\/ 初始化小预览\n    updateSmallPreview(styleCard.querySelector('.preview-svg-container'), customStyles[name]);\n    \n    container.appendChild(styleCard);\n  });\n}\n\n\/\/ 更新小预览\nfunction updateSmallPreview(container, svgCode) {\n  try {\n    const preview = svgCode\n      .split('$color').join(settings['段评边框颜色'])\n      .split('$fontcolor').join(settings['段评字体颜色'])\n      .split('$tccolor').join(settings['段评填充颜色'])\n      .split('$displayText').join('99+');\n    container.innerHTML = preview;\n  } catch (e) {\n    container.innerHTML = '<span style=\"color: #999; font-size: 12px;\">解析失败<\/span>';\n  }\n}\n\n\/\/ 渲染预览\nfunction renderPreview() {\n  const container = document.getElementById('previewContainer');\n  container.innerHTML = '';\n  \n  \/\/ 添加默认样式\n  SVG_TEMPLATES.forEach((svgTemplate, index) => {\n    const svgContent = svgTemplate\n      .split('$color').join(settings['段评边框颜色'])\n      .split('$fontcolor').join(settings['段评字体颜色'])\n      .split('$tccolor').join(settings['段评填充颜色'])\n      .split('$displayText').join('99+');\n    \n    const item = document.createElement('div');\n    item.className = \\`preview-item \\${settings['段评样式'] === String(index) ? 'selected' : ''}\\`;\n    item.innerHTML = \\`\n      \\${svgContent}\n      <div class=\"preview-label\">\\${STYLE_NAMES[index]}<\/div>\n    \\`;\n    \n    item.addEventListener('click', () => {\n      settings['段评样式'] = String(index);\n      document.getElementById('duanpingStyle').value = String(index);\n      updateHiddenSettings();\n      renderPreview();\n    });\n    \n    container.appendChild(item);\n  });\n  \n  \/\/ 添加自定义样式预览\n  Object.keys(customStyles).forEach((name) => {\n    const svgCode = customStyles[name];\n    let svgContent;\n    try {\n      svgContent = svgCode\n        .split('$color').join(settings['段评边框颜色'])\n        .split('$fontcolor').join(settings['段评字体颜色'])\n        .split('$tccolor').join(settings['段评填充颜色'])\n        .split('$displayText').join('99+');\n    } catch (e) {\n      svgContent = '<svg width=\"60\" height=\"54\" viewBox=\"0 0 60 54\"><text x=\"30\" y=\"27\" text-anchor=\"middle\" fill=\"#999\" font-size=\"10\">解析失败<\/text><\/svg>';\n    }\n    \n    const item = document.createElement('div');\n    item.className = \\`preview-item \\${settings['段评样式'] === name ? 'selected' : ''}\\`;\n    item.innerHTML = \\`\n      \\${svgContent}\n      <div class=\"preview-label custom-label\">\\${name}<\/div>\n    \\`;\n    \n    item.addEventListener('click', () => {\n      settings['段评样式'] = name;\n      document.getElementById('duanpingStyle').value = name;\n      updateHiddenSettings();\n      renderPreview();\n    });\n    \n    container.appendChild(item);\n  });\n}\n\n\/\/ 初始化事件监听\nfunction setupEventListeners() {\n  \/\/ 段评开关\n  document.getElementById('duanpingSwitch').addEventListener('change', (e) => {\n    settings['段评开关'] = e.target.checked ? 'true' : 'false';\n    updateHiddenSettings();\n  });\n  \n  \/\/ 气泡增大开关\n  document.getElementById('bubbleLargeSwitch').addEventListener('change', (e) => {\n    settings['气泡增大'] = e.target.checked ? 'true' : 'false';\n    updateHiddenSettings();\n  });\n  \n  \/\/ 段评样式选择\n  document.getElementById('duanpingStyle').addEventListener('change', (e) => {\n    settings['段评样式'] = e.target.value;\n    updateHiddenSettings();\n    renderPreview();\n  });\n  \n  \/\/ 边框颜色选择器\n  document.getElementById('borderColor').addEventListener('input', (e) => {\n    const color = e.target.value;\n    settings['段评边框颜色'] = color;\n    document.getElementById('borderColorText').value = color;\n    updateHiddenSettings();\n    renderPreview();\n  });\n  \n  \/\/ 边框颜色文本输入\n  document.getElementById('borderColorText').addEventListener('input', (e) => {\n    const color = e.target.value;\n    if (\/^#[0-9A-Fa-f]{6}$\/.test(color) || \/^#[0-9A-Fa-f]{3}$\/.test(color)) {\n      settings['段评边框颜色'] = color;\n      document.getElementById('borderColor').value = color;\n      updateHiddenSettings();\n      renderPreview();\n    }\n  });\n  \n  \/\/ 字体颜色选择器\n  document.getElementById('fontColor').addEventListener('input', (e) => {\n    const color = e.target.value;\n    settings['段评字体颜色'] = color;\n    document.getElementById('fontColorText').value = color;\n    updateHiddenSettings();\n    renderPreview();\n  });\n  \n  \/\/ 字体颜色文本输入\n  document.getElementById('fontColorText').addEventListener('input', (e) => {\n    const color = e.target.value;\n    if (\/^#[0-9A-Fa-f]{6}$\/.test(color) || \/^#[0-9A-Fa-f]{3}$\/.test(color)) {\n      settings['段评字体颜色'] = color;\n      document.getElementById('fontColor').value = color;\n      updateHiddenSettings();\n      renderPreview();\n    }\n  });\n  \n  \/\/ 填充颜色选择器\n  document.getElementById('fillColor').addEventListener('input', (e) => {\n    const color = e.target.value;\n    settings['段评填充颜色'] = color;\n    document.getElementById('fillColorText').value = color;\n    updateHiddenSettings();\n    renderPreview();\n  });\n  \n  \/\/ 填充颜色文本输入\n  document.getElementById('fillColorText').addEventListener('input', (e) => {\n    const color = e.target.value;\n    if (color.toLowerCase() == 'transparent' || \/^#[0-9A-Fa-f]{6}$\/.test(color) || \/^#[0-9A-Fa-f]{3}$\/.test(color)) {\n      settings['段评填充颜色'] = color;\n      if (color.toLowerCase() != 'transparent') {\n        document.getElementById('fillColor').value = color;\n      }\n      updateHiddenSettings();\n      renderPreview();\n    }\n  });\n  \n  \/\/ 透明按钮\n  document.getElementById('fillColorTransparent').addEventListener('click', () => {\n    settings['段评填充颜色'] = 'transparent';\n    document.getElementById('fillColorText').value = 'transparent';\n    document.getElementById('fillColor').value = '#FFFFFF';\n    updateHiddenSettings();\n    renderPreview();\n  });\n  \n  \/\/ 添加自定义样式按钮\n  document.getElementById('addCustomStyleBtn').addEventListener('click', addCustomStyle);\n}\n\n\/\/ 初始化UI\nfunction initUI() {\n  \/\/ 设置段评开关\n  document.getElementById('duanpingSwitch').checked = settings['段评开关'] != 'false';\n  \n  \/\/ 设置气泡增大开关\n  document.getElementById('bubbleLargeSwitch').checked = settings['气泡增大'] == 'true';\n  \n  \/\/ 设置段评样式\n  document.getElementById('duanpingStyle').value = settings['段评样式'];\n  \n  \/\/ 设置边框颜色\n  const borderColorValue = settings['段评边框颜色'];\n  document.getElementById('borderColor').value = borderColorValue === 'transparent' ? '#A7A7A7' : borderColorValue;\n  document.getElementById('borderColorText').value = borderColorValue;\n  \n  \/\/ 设置字体颜色\n  const fontColorValue = settings['段评字体颜色'];\n  document.getElementById('fontColor').value = fontColorValue === 'transparent' ? '#A7A7A7' : fontColorValue;\n  document.getElementById('fontColorText').value = fontColorValue;\n  \n  \/\/ 设置填充颜色\n  const fillColorValue = settings['段评填充颜色'];\n  document.getElementById('fillColor').value = fillColorValue === 'transparent' ? '#FFFFFF' : fillColorValue;\n  document.getElementById('fillColorText').value = fillColorValue;\n  \n  \/\/ 渲染自定义样式列表\n  renderCustomStyles();\n  \n  \/\/ 更新段评样式选择器,添加自定义样式选项\n  updateStyleSelector();\n}\n\n\/\/ 更新段评样式选择器\nfunction updateStyleSelector() {\n  const select = document.getElementById('duanpingStyle');\n  const currentValue = settings['段评样式'];\n  \n  \/\/ 移除所有非默认选项\n  while (select.options.length > 6) {\n    select.remove(6);\n  }\n  \n  \/\/ 添加自定义样式选项\n  Object.keys(customStyles).forEach((name) => {\n    const option = document.createElement('option');\n    option.value = name;\n    option.textContent = name;\n    select.appendChild(option);\n  });\n  \n  \/\/ 设置当前选中值\n  select.value = currentValue;\n}\n\n\/\/ 页面加载完成后初始化\ninitCustomStyles();\ninitUI();\nsetupEventListeners();\nrenderPreview();\n<\/script>\n<\/body>\n<\/html>\n    `;\n    let body;\n    try {\n        body = java.startBrowserAwait(`data:text\/html;base64,${java.base64Encode(html)}`, '光遇段评设置', false).body();\n    } catch (e) {\n        if (islyc == \"安卓\") {\n            java.longToast(\"\\n当前软件版本过低,请更新软件,或者使用底部备用设置方案~\");\n        } else {\n            java.longToast(\"\\n请点击页面右上角√才能设置成功哦~\");\n        }\n        return;\n    }\n    \n    let settings = {};\n    try {\n        const getSpanText = (spanId) => {\n            const regex = new RegExp('id=\"' + spanId + '\"\\s*>\\s*([^<]*?)\\s*<\\\/span>');\n            const match = body.match(regex);\n            return match ? match[1].trim() : \"\";\n        };\n        const getTextareaValue = (textareaId) => {\n            const regex = new RegExp('<textarea[^>]*id=\"' + textareaId + '\"[^>]*>([\\\\s\\\\S]*?)<\\\/textarea>', 'i');\n            const match = body.match(regex);\n            return match ? match[1].trim() : \"\";\n        };\n        const htmlDecode = (str) => {\n            if (!str) return str;\n            return str.replace(\/&amp;\/g, '&')\n                .replace(\/&lt;\/g, '<')\n                .replace(\/&gt;\/g, '>')\n                .replace(\/&quot;\/g, '\"')\n                .replace(\/&#039;\/g, \"'\");\n        };\n\n        const customStylesValue = getTextareaValue('customStylesValue') || '{}';\n        const decodedStyles = htmlDecode(customStylesValue);\n\n        settings = {\n            '段评开关': getSpanText('duanpingSwitchValue'),\n            '气泡增大': getSpanText('bubbleLargeSwitchValue'),\n            '段评样式': getSpanText('duanpingStyleValue') || '0',\n            '段评边框颜色': getSpanText('borderColorValue') || '#A7A7A7',\n            '段评字体颜色': getSpanText('fontColorValue') || '#A7A7A7',\n            '段评填充颜色': getSpanText('fillColorValue') || 'transparent',\n            '自定义段评样式': JSON.parse(decodedStyles),\n        };\n        const settingsText = Object.keys(settings).map(key => `${key}: ${settings[key]}`).join('\\n');\n        java.longToast(\"\\n\" + settingsText);\n    } catch (e) {\n        settings = {\n            '段评开关': 'true',\n            '气泡增大': 'false',\n            '段评样式': '0',\n            '段评边框颜色': '#A7A7A7',\n            '段评字体颜色': '#A7A7A7',\n            '段评填充颜色': 'transparent',\n            '自定义段评样式': {},\n        };\n    }\n    this.setVariable(\"段评设置\", settings, false);\n}\n\n\/\/ 线路设置\nfunction getServerSettings() {\n    const {java} = this;\n    let data = this.getVariable('线路');\n    \/\/java.toast(data)\n    let hostsbk = this.getVariable('云端配置')['hosts'] || hosts;\n    \n    let html = `\n<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no\">\n<title>光遇小说 - 线路设置<\/title>\n<link rel=\"stylesheet\" href=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/font-awesome\/6.4.0\/css\/all.min.css\">\n<style>\n* {\n  margin: 0;\n  padding: 0;\n  box-sizing: border-box;\n  -webkit-tap-highlight-color: transparent;\n}\n\n:root {\n  --color-primary: #2D9D78;\n  --color-primary-light: #3DB893;\n  --bg-dark: #f5f7fa;\n  --bg-darker: #ffffff;\n  --bg-card: rgba(255, 255, 255, 0.95);\n  --bg-elevated: #f8fafb;\n  --text-primary: #1a1d23;\n  --text-secondary: #6b7280;\n  --border-color: rgba(0, 0, 0, 0.08);\n}\n\n[data-theme=\"dark\"] {\n  --bg-dark: #0A1628;\n  --bg-darker: #050B14;\n  --bg-card: #142235;\n  --bg-elevated: #1A2B42;\n  --text-primary: #FFFFFF;\n  --text-secondary: rgba(255, 255, 255, 0.7);\n  --border-color: rgba(255, 255, 255, 0.1);\n}\n\nhtml, body {\n  width: 100%;\n  min-height: 100vh;\n  font-family: -apple-system, BlinkMacSystemFont, sans-serif;\n  font-size: 16px;\n  color: var(--text-primary);\n  background: var(--bg-darker);\n  overflow-x: hidden;\n}\n\n.container {\n  padding: 16px;\n  max-width: 600px;\n  margin: 0 auto;\n}\n\n.main-card {\n  background: var(--bg-card);\n  border-radius: 20px;\n  box-shadow: 0 4px 24px rgba(0, 0, 0, 0.1);\n  border: 1px solid var(--border-color);\n  overflow: hidden;\n}\n\n.header {\n  padding: 20px 16px;\n  background: linear-gradient(135deg, var(--color-primary), var(--color-primary-light));\n  text-align: center;\n}\n\n.header-title {\n  font-size: 18px;\n  font-weight: 700;\n  color: white;\n  margin-bottom: 4px;\n}\n\n.header-desc {\n  font-size: 12px;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.last-check {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  font-size: 12px;\n  color: var(--text-secondary);\n  padding: 12px 16px;\n  border-bottom: 1px solid var(--border-color);\n}\n\n.last-check i {\n  color: var(--color-primary);\n}\n\n.server-list {\n  padding: 8px;\n}\n\n.server-card {\n  background: var(--bg-elevated);\n  border-radius: 12px;\n  padding: 14px 16px;\n  margin-bottom: 8px;\n  display: flex;\n  align-items: center;\n  gap: 12px;\n  cursor: pointer;\n  transition: all 0.2s ease;\n  border: 2px solid transparent;\n}\n\n.server-card:hover {\n  border-color: var(--color-primary);\n  background: rgba(45, 157, 120, 0.05);\n}\n\n.server-card.selected {\n  border-color: var(--color-primary);\n  background: rgba(45, 157, 120, 0.1);\n}\n\n.server-icon {\n  width: 36px;\n  height: 36px;\n  border-radius: 10px;\n  background: linear-gradient(135deg, rgba(45, 157, 120, 0.15), rgba(61, 184, 147, 0.1));\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  font-size: 16px;\n  color: var(--color-primary);\n  flex-shrink: 0;\n}\n\n.server-card.selected .server-icon {\n  background: linear-gradient(135deg, var(--color-primary), var(--color-primary-light));\n  color: white;\n}\n\n.server-info {\n  flex: 1;\n  min-width: 0;\n}\n\n.server-url {\n  font-size: 14px;\n  font-weight: 700;\n  color: var(--text-primary);\n  word-break: break-all;\n  line-height: 1.4;\n}\n\n.server-details {\n  font-size: 11px;\n  color: var(--text-secondary);\n  margin-top: 2px;\n}\n\n.server-status {\n  padding: 4px 12px;\n  border-radius: 16px;\n  font-size: 11px;\n  font-weight: 700;\n  white-space: nowrap;\n  flex-shrink: 0;\n}\n\n.server-status.checking {\n  background: rgba(255, 193, 7, 0.2);\n  color: #f57f17;\n}\n\n.server-status.online {\n  background: rgba(76, 175, 80, 0.2);\n  color: #2e7d32;\n}\n\n.server-status.slow {\n  background: rgba(255, 193, 7, 0.2);\n  color: #f57f17;\n}\n\n.server-status.offline {\n  background: rgba(244, 67, 54, 0.2);\n  color: #c62828;\n}\n\n.server-status.not-support {\n  background: rgba(156, 163, 175, 0.2);\n  color: #6b7280;\n}\n\n.selected-badge {\n  width: 20px;\n  height: 20px;\n  border-radius: 50%;\n  background: var(--color-primary);\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  flex-shrink: 0;\n}\n\n.selected-badge i {\n  color: white;\n  font-size: 12px;\n}\n\n.footer {\n  padding: 16px;\n  border-top: 1px solid var(--border-color);\n  text-align: center;\n}\n\n.footer-text {\n  font-size: 12px;\n  color: var(--text-secondary);\n  line-height: 1.5;\n}\n\n.footer-text .highlight {\n  color: var(--color-primary);\n  font-weight: 700;\n}\n\n\/* 自动切换开关样式 *\/\n.auto-switch-section {\n  padding: 14px 16px;\n  border-bottom: 1px solid var(--border-color);\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  background: var(--bg-elevated);\n}\n\n.auto-switch-section:hover {\n  background: rgba(45, 157, 120, 0.05);\n}\n\n.auto-switch-info {\n  flex: 1;\n  min-width: 0;\n}\n\n.auto-switch-title {\n  font-size: 14px;\n  font-weight: 600;\n  color: var(--text-primary);\n  display: flex;\n  align-items: center;\n  gap: 8px;\n}\n\n.auto-switch-title i {\n  color: var(--color-primary);\n  font-size: 14px;\n}\n\n.auto-switch-desc {\n  font-size: 11px;\n  color: var(--text-secondary);\n  margin-top: 3px;\n  line-height: 1.4;\n}\n\n\/* 开关基础样式 *\/\n.switch {\n  position: relative;\n  display: inline-block;\n  width: 48px;\n  height: 28px;\n  flex-shrink: 0;\n}\n\n.switch input {\n  opacity: 0;\n  width: 0;\n  height: 0;\n}\n\n.switch-slider {\n  position: absolute;\n  cursor: pointer;\n  top: 0;\n  left: 0;\n  right: 0;\n  bottom: 0;\n  background-color: #d1d5db;\n  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n  border-radius: 28px;\n  box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1);\n}\n\n.switch-slider:before {\n  position: absolute;\n  content: \"\";\n  height: 22px;\n  width: 22px;\n  left: 3px;\n  bottom: 3px;\n  background-color: white;\n  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n  border-radius: 50%;\n  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.1);\n}\n\n.switch input:checked + .switch-slider {\n  background: linear-gradient(135deg, var(--color-primary), var(--color-primary-light));\n  box-shadow: 0 2px 8px rgba(45, 157, 120, 0.3);\n}\n\n.switch input:checked + .switch-slider:before {\n  transform: translateX(20px);\n  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);\n}\n\n\/* 开关焦点状态 *\/\n.switch input:focus + .switch-slider {\n  outline: 2px solid var(--color-primary);\n  outline-offset: 2px;\n}\n\n\/* 下拉框样式 *\/\n.timeout-select {\n  padding: 6px 12px;\n  font-size: 13px;\n  font-weight: 600;\n  color: var(--text-primary);\n  background: var(--bg-card);\n  border: 2px solid var(--border-color);\n  border-radius: 10px;\n  cursor: pointer;\n  min-width: 70px;\n  appearance: none;\n  background-image: url(\"data:image\/svg+xml,%3Csvg xmlns='http:\/\/www.w3.org\/2000\/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%236b7280' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C\/polyline%3E%3C\/svg%3E\");\n  background-repeat: no-repeat;\n  background-position: right 8px center;\n  background-size: 14px;\n}\n\n.timeout-select:focus {\n  outline: none;\n  border-color: var(--color-primary);\n}\n\n.timeout-select option {\n  background: var(--bg-darker);\n  color: var(--text-primary);\n}\n\n.hidden-settings {\n  display: none;\n}\/* 提示文案样式 *\/\n.tip-alert {\n  background: linear-gradient(135deg, rgba(255, 193, 7, 0.12) 0%, rgba(255, 152, 0, 0.08) 100%);\n  border: 1px solid rgba(255, 193, 7, 0.25);\n  border-radius: 16px;\n  padding: 16px 18px;\n  margin: 20px;\n  display: flex;\n  align-items: flex-start;\n  gap: 14px;\n  box-shadow: 0 4px 16px rgba(255, 193, 7, 0.15), inset 0 1px 0 rgba(255, 255, 255, 0.8);\n  position: relative;\n  overflow: hidden;\n}\n\n.tip-alert::before {\n  content: '';\n  position: absolute;\n  top: -50%;\n  right: -20%;\n  width: 100px;\n  height: 100px;\n  background: rgba(255, 193, 7, 0.1);\n  border-radius: 50%;\n}\n\n.tip-alert i {\n  color: #f97316;\n  font-size: 22px;\n  flex-shrink: 0;\n  margin-top: 2px;\n  position: relative;\n  z-index: 1;\n}\n\n.tip-alert p {\n  font-size: 13px;\n  color: var(--text-secondary);\n  line-height: 1.6;\n  margin: 0;\n  position: relative;\n  z-index: 1;\n}\n\n.tip-alert .highlight-text {\n  color: #ea580c;\n  font-weight: 700;\n}\n\n@media (max-width: 600px) {\n  .tip-alert {\n    margin: 16px;\n    padding: 14px 16px;\n  }\n}\n<\/style>\n<\/head>\n<body data-theme=\"light\">\n<div class=\"container\">\n  <div class=\"main-card\">\n    <div class=\"header\">\n      <div class=\"header-title\">\n        <i class=\"fas fa-server\"><\/i>\n        <span style=\"margin-left: 8px;\">线路选择<\/span>\n      <\/div>\n      <div class=\"header-desc\">选择最快的服务器线路以获得最佳体验<\/div>\n    <\/div>\n    \n    <!-- 提示文案 -->\n    <div class=\"tip-alert\">\n      <p>设置完成后请点击右上角的<span class=\"highlight-text\">√<\/span>应用设置<\/p>\n    <\/div>\n    \n    <div class=\"footer\">\n      <div class=\"footer-text\">\n        当前选中: <span class=\"highlight\" id=\"currentServer\">--<\/span>\n      <\/div>\n    <\/div>\n    \n    <div class=\"last-check\">\n      <i class=\"fas fa-clock\"><\/i>\n      <span id=\"lastCheckTime\">最后检测: --:--:--<\/span>\n      <button id=\"refreshBtn\" style=\"margin-left: auto; padding: 4px 10px; font-size: 11px; font-weight: 600; color: var(--color-primary); background: rgba(45, 157, 120, 0.1); border: 1px solid var(--color-primary); border-radius: 6px; cursor: pointer;\">\n        <i class=\"fas fa-sync-alt\"><\/i>\n        刷新\n      <\/button>\n    <\/div>\n    \n    <div class=\"auto-switch-section\">\n      <div class=\"auto-switch-info\">\n        <div class=\"auto-switch-title\"><i class=\"fas fa-refresh\"><\/i>失败自动切换<\/div>\n        <div class=\"auto-switch-desc\">当前线路失败时自动尝试切换到其他可用线路<\/div>\n      <\/div>\n      <label class=\"switch\">\n        <input type=\"checkbox\" id=\"autoSwitchToggle\">\n        <span class=\"switch-slider\"><\/span>\n      <\/label>\n    <\/div>\n    \n    <div class=\"auto-switch-section\">\n      <div class=\"auto-switch-info\">\n        <div class=\"auto-switch-title\"><i class=\"fas fa-clock\"><\/i>超时自动切换<\/div>\n        <div class=\"auto-switch-desc\">请求超时后自动切换到其他可用线路<\/div>\n      <\/div>\n      <div style=\"display: flex; align-items: center; gap: 12px;\">\n        <label class=\"switch\">\n          <input type=\"checkbox\" id=\"timeoutSwitchToggle\">\n          <span class=\"switch-slider\"><\/span>\n        <\/label>\n        <select id=\"timeoutSelect\" class=\"timeout-select\">\n          <option value=\"1\">1秒<\/option>\n          <option value=\"2\">2秒<\/option>\n          <option value=\"3\">3秒<\/option>\n          <option value=\"4\">4秒<\/option>\n          <option value=\"5\" selected>5秒<\/option>\n          <option value=\"6\">6秒<\/option>\n          <option value=\"7\">7秒<\/option>\n          <option value=\"8\">8秒<\/option>\n          <option value=\"9\">9秒<\/option>\n          <option value=\"10\">10秒<\/option>\n        <\/select>\n      <\/div>\n    <\/div>\n    \n    <div class=\"auto-switch-section\">\n      <div class=\"auto-switch-info\">\n        <div class=\"auto-switch-title\"><i class=\"fas fa-bell\"><\/i>切换提醒弹窗<\/div>\n        <div class=\"auto-switch-desc\">线路切换时显示提醒弹窗通知<\/div>\n      <\/div>\n      <label class=\"switch\">\n        <input type=\"checkbox\" id=\"notifySwitchToggle\">\n        <span class=\"switch-slider\"><\/span>\n      <\/label>\n    <\/div>\n    \n    <div id=\"serverList\" class=\"server-list\"><\/div>\n    \n    <div class=\"hidden-settings\">\n      <span id=\"serverValue\">${data || hostsbk[0]}<\/span>\n      <span id=\"autoSwitchValue\">${this.getVariable('自动切换') || 'true'}<\/span>\n      <span id=\"timeoutSwitchValue\">${this.getVariable('超时自动切换') || 'true'}<\/span>\n      <span id=\"timeoutValue\">${this.getVariable('超时时间') || '5'}<\/span>\n      <span id=\"notifySwitchValue\">${this.getVariable('切换提醒') || 'true'}<\/span>\n    <\/div>\n  <\/div>\n<\/div>\n\n<script>\nconst HOSTS = ${JSON.stringify(hostsbk)};\nlet currentServer = '${data || hostsbk[0]}';\n\nfunction renderServerList() {\n  const serverList = document.getElementById('serverList');\n  serverList.innerHTML = '';\n  \n  HOSTS.forEach((host, index) => {\n    const card = document.createElement('div');\n    card.className = 'server-card' + (currentServer === host ? ' selected' : '');\n    card.dataset.host = host;\n    card.dataset.index = index;\n    \n    card.innerHTML = \\`\n      <div class=\"server-icon\">\n        <i class=\"fas fa-server\"><\/i>\n      <\/div>\n      <div class=\"server-info\">\n        <div class=\"server-url\">\\${host}<\/div>\n        <div class=\"server-details\" id=\"details-\\${index}\">状态: 检测中<\/div>\n      <\/div>\n      <span class=\"server-status checking\" id=\"status-\\${index}\">\n        <i class=\"fas fa-spinner fa-spin\"><\/i>\n        <span>检测中<\/span>\n      <\/span>\n      \\${currentServer === host ? '<div class=\"selected-badge\"><i class=\"fas fa-check\"><\/i><\/div>' : ''}\n    \\`;\n    \n    card.addEventListener('click', () => {\n      selectServer(host);\n    });\n    \n    serverList.appendChild(card);\n  });\n}\n\nfunction selectServer(host) {\n  currentServer = host;\n  \n  document.querySelectorAll('.server-card').forEach(card => {\n    card.classList.remove('selected');\n    const badge = card.querySelector('.selected-badge');\n    if (badge) badge.remove();\n  });\n  \n  const selectedCard = document.querySelector(\\`[data-host=\"\\${host}\"]\\`);\n  if (selectedCard) {\n    selectedCard.classList.add('selected');\n    const icon = selectedCard.querySelector('.server-icon');\n    icon.style.background = 'linear-gradient(135deg, var(--color-primary), var(--color-primary-light))';\n    icon.style.color = 'white';\n    \n    const statusEl = selectedCard.querySelector('.server-status');\n    const newBadge = document.createElement('div');\n    newBadge.className = 'selected-badge';\n    newBadge.innerHTML = '<i class=\"fas fa-check\"><\/i>';\n    selectedCard.appendChild(newBadge);\n  }\n  \n  document.getElementById('currentServer').textContent = host;\n  document.getElementById('serverValue').textContent = host;\n}\n\nasync function checkSingleServer(host, index) {\n  const currentProtocol = window.location.protocol;\n  const statusEl = document.getElementById(\\`status-\\${index}\\`);\n  const detailsEl = document.getElementById(\\`details-\\${index}\\`);\n  \n  try {\n    const serverProtocol = new URL(host).protocol;\n    if (currentProtocol === 'https:' && serverProtocol === 'http:') {\n      statusEl.className = 'server-status not-support';\n      statusEl.innerHTML = '<i class=\"fas fa-exclamation-triangle\"><\/i><span>不支持<\/span>';\n      detailsEl.textContent = '浏览器安全策略限制';\n      return;\n    }\n    \n    const startTime = performance.now();\n    const controller = new AbortController();\n    const timeout = setTimeout(() => controller.abort(), 5000);\n    \n    const response = await fetch(\\`\\${host}\/health?t=\\${Date.now()}\\`, {\n      signal: controller.signal,\n      method: 'GET',\n      mode: 'cors',\n      cache: 'no-cache'\n    });\n    \n    clearTimeout(timeout);\n    const responseTime = Math.round(performance.now() - startTime);\n    \n    if (response.ok) {\n      if (responseTime < 1000) {\n        statusEl.className = 'server-status online';\n        statusEl.innerHTML = \\`<i class=\"fas fa-check-circle\"><\/i><span>\\${responseTime}ms<\/span>\\`;\n      } else if (responseTime < 2000) {\n        statusEl.className = 'server-status slow';\n        statusEl.innerHTML = \\`<i class=\"fas fa-exclamation-circle\"><\/i><span>\\${responseTime}ms<\/span>\\`;\n      } else {\n        statusEl.className = 'server-status slow';\n        statusEl.innerHTML = '<i class=\"fas fa-exclamation-circle\"><\/i><span>缓慢<\/span>';\n      }\n      detailsEl.textContent = '运行正常';\n    } else {\n      statusEl.className = 'server-status offline';\n      statusEl.innerHTML = '<i class=\"fas fa-times-circle\"><\/i><span>异常<\/span>';\n      detailsEl.textContent = 'HTTP状态码: ' + response.status;\n    }\n  } catch (error) {\n    statusEl.className = 'server-status offline';\n    statusEl.innerHTML = '<i class=\"fas fa-times-circle\"><\/i><span>离线<\/span>';\n    detailsEl.textContent = '连接失败';\n  }\n}\n\nasync function checkServers() {\n  HOSTS.forEach((_, index) => {\n    const statusEl = document.getElementById(\\`status-\\${index}\\`);\n    const detailsEl = document.getElementById(\\`details-\\${index}\\`);\n    if (statusEl && detailsEl) {\n      statusEl.className = 'server-status checking';\n      statusEl.innerHTML = '<i class=\"fas fa-spinner fa-spin\"><\/i><span>检测中<\/span>';\n      detailsEl.textContent = '状态: 检测中';\n    }\n  });\n  \n  const promises = HOSTS.map((host, index) => checkSingleServer(host, index));\n  await Promise.all(promises);\n  \n  const now = new Date();\n  document.getElementById('lastCheckTime').textContent = \\`最后检测: \\${now.toLocaleTimeString()}\\`;\n}\n\ndocument.getElementById('refreshBtn').addEventListener('click', checkServers);\n\ndocument.addEventListener('DOMContentLoaded', () => {\n  renderServerList();\n  document.getElementById('currentServer').textContent = currentServer;\n  checkServers();\n  \n  \/\/ 初始化自动切换开关\n  const autoSwitchToggle = document.getElementById('autoSwitchToggle');\n  const autoSwitchValue = document.getElementById('autoSwitchValue').textContent;\n  autoSwitchToggle.checked = autoSwitchValue !== 'false';\n  \n  \/\/ 监听开关变化\n  autoSwitchToggle.addEventListener('change', (e) => {\n    document.getElementById('autoSwitchValue').textContent = e.target.checked ? 'true' : 'false';\n  });\n  \n  \/\/ 初始化超时自动切换开关\n  const timeoutSwitchToggle = document.getElementById('timeoutSwitchToggle');\n  const timeoutSwitchValue = document.getElementById('timeoutSwitchValue').textContent;\n  timeoutSwitchToggle.checked = timeoutSwitchValue !== 'false';\n  \n  \/\/ 监听超时自动切换开关变化\n  timeoutSwitchToggle.addEventListener('change', (e) => {\n    document.getElementById('timeoutSwitchValue').textContent = e.target.checked ? 'true' : 'false';\n  });\n  \n  \/\/ 初始化超时时间下拉框\n  const timeoutSelect = document.getElementById('timeoutSelect');\n  const timeoutValue = document.getElementById('timeoutValue').textContent;\n  timeoutSelect.value = timeoutValue;\n  \n  \/\/ 监听超时时间变化\n  timeoutSelect.addEventListener('change', (e) => {\n    document.getElementById('timeoutValue').textContent = e.target.value;\n  });\n  \n  \/\/ 初始化切换提醒弹窗开关\n  const notifySwitchToggle = document.getElementById('notifySwitchToggle');\n  const notifySwitchValue = document.getElementById('notifySwitchValue').textContent;\n  notifySwitchToggle.checked = notifySwitchValue !== 'false';\n  \n  \/\/ 监听切换提醒弹窗开关变化\n  notifySwitchToggle.addEventListener('change', (e) => {\n    document.getElementById('notifySwitchValue').textContent = e.target.checked ? 'true' : 'false';\n  });\n});\n<\/script>\n<\/body>\n<\/html>\n    `;\n    let body;\n    try {\n        body = java.startBrowserAwait(`data:text\/html;base64,${java.base64Encode(html)}`, '光遇服务器设置', false).body();\n    } catch (e) {\n        let islyc = this.checkEnv()\n        if (islyc == \"安卓\") {\n            this.switchToNextLine();\n        } else {\n            java.longToast(\"\\n请点击页面右上角√才能设置成功哦~\");\n        }\n        return;\n    }\n    \n    let host = hostsbk[0];\n    let autoSwitch = 'true';\n    let timeoutSwitch = 'true';\n    let timeout = '5';\n    let notifySwitch = 'true';\n    try {\n        const getSpanText = (spanId) => {\n            const regex = new RegExp('id=\"' + spanId + '\"\\s*>\\s*([^<]*?)\\s*<\\\/span>');\n            const match = body.match(regex);\n            return match ? match[1].trim() : \"\";\n        };\n        host = getSpanText('serverValue') || hostsbk[0];\n        autoSwitch = getSpanText('autoSwitchValue') || 'true';\n        timeoutSwitch = getSpanText('timeoutSwitchValue') || 'true';\n        timeout = getSpanText('timeoutValue') || '5';\n        notifySwitch = getSpanText('notifySwitchValue') || 'true';\n    } catch (e) {\n        host = hostsbk[0];\n        autoSwitch = 'true';\n        timeoutSwitch = 'true';\n        timeout = '5';\n        notifySwitch = 'true';\n    }\n    const settingsText = `线路: ${host}\\n失败自动切换: ${autoSwitch == 'true' ? '开启' : '关闭'}\\n超时自动切换: ${timeoutSwitch == 'true' ? '开启' : '关闭'}\\n超时时间: ${timeout}秒\\n切换提醒弹窗: ${notifySwitch == 'true' ? '开启' : '关闭'}`;\n    java.longToast(\"\\n\" + settingsText);\n    this.setVariable(\"线路\", host, false);\n    this.setVariable(\"自动切换\", autoSwitch, false);\n    this.setVariable(\"超时自动切换\", timeoutSwitch, false);\n    this.setVariable(\"超时时间\", timeout, false);\n    this.setVariable(\"切换提醒\", notifySwitch, false);\n}\n\n\n\/\/ 更多设置\nfunction getHtmlSettings() {\n    const {\n        java\n    } = this;\n    let data = this.getVariable('更多设置');\n    let config = this.getVariable('云端配置');\n    if (!config) {\n        try {\n            this.getCloudSettings();\n            config = this.getVariable('云端配置');\n        } catch {}\n    }\n    let html = `\n    <!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no\">\n<title>光遇小说 - 管理中心<\/title>\n<link rel=\"stylesheet\" href=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/font-awesome\/6.4.0\/css\/all.min.css\">\n<style>\n* {\n  margin: 0;\n  padding: 0;\n  box-sizing: border-box;\n  -webkit-tap-highlight-color: transparent;\n}\n\n:root {\n  --color-primary: #2D9D78;\n  --color-primary-light: #3DB893;\n  --color-primary-dark: #1E6F55;\n  --color-accent: #FFB74D;\n  --color-success: #4CAF50;\n  --color-error: #F44336;\n  --color-warning: #FFC107;\n  --color-info: #2196F3;\n  \n  --bg-dark: #f0f3f8;\n  --bg-darker: #e8edf5;\n  --bg-card: rgba(255, 255, 255, 0.98);\n  --bg-elevated: #fafbfc;\n  \n  --text-primary: #1a1d23;\n  --text-secondary: #6b7280;\n  --text-tertiary: #9ca3af;\n  \n  --border-color: rgba(0, 0, 0, 0.06);\n}\n\n[data-theme=\"dark\"] {\n  --bg-dark: #0A1628;\n  --bg-darker: #050B14;\n  --bg-card: #142235;\n  --bg-elevated: #1A2B42;\n  \n  --text-primary: #FFFFFF;\n  --text-secondary: rgba(255, 255, 255, 0.7);\n  --text-tertiary: rgba(255, 255, 255, 0.5);\n  \n  --border-color: rgba(255, 255, 255, 0.08);\n}\n\nhtml, body {\n  width: 100%;\n  min-height: 100vh;\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n  font-size: 16px;\n  color: var(--text-primary);\n  background: linear-gradient(135deg, var(--bg-darker) 0%, var(--bg-dark) 100%);\n  background-attachment: fixed;\n  overflow-x: hidden;\n  transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.container {\n  padding: 24px 20px;\n  max-width: 820px;\n  margin: 0 auto;\n  animation: fadeIn 0.5s ease-out;\n}\n\n@keyframes fadeIn {\n  from { opacity: 0; transform: translateY(10px); }\n  to { opacity: 1; transform: translateY(0); }\n}\n\n.main-card {\n  background: var(--bg-card);\n  border-radius: 24px;\n  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.08), 0 2px 8px rgba(0, 0, 0, 0.04), inset 0 1px 0 rgba(255, 255, 255, 0.8);\n  border: 1px solid var(--border-color);\n  overflow: hidden;\n  backdrop-filter: blur(20px);\n}\n\n\/* 主Tab切换 *\/\n.main-tabs {\n  display: flex;\n  background: linear-gradient(180deg, var(--bg-elevated) 0%, rgba(255, 255, 255, 0.5) 100%);\n  border-bottom: 1px solid var(--border-color);\n  position: relative;\n}\n\n.main-tabs::after {\n  content: '';\n  position: absolute;\n  bottom: 0;\n  left: 0;\n  right: 0;\n  height: 1px;\n  background: linear-gradient(90deg, transparent, var(--border-color), transparent);\n}\n\n.main-tab {\n  flex: 1;\n  padding: 20px 24px;\n  text-align: center;\n  font-size: 15px;\n  font-weight: 700;\n  color: var(--text-secondary);\n  cursor: pointer;\n  transition: all 0.35s cubic-bezier(0.4, 0, 0.2, 1);\n  border-bottom: 3px solid transparent;\n  position: relative;\n  overflow: hidden;\n}\n\n.main-tab::before {\n  content: '';\n  position: absolute;\n  top: 0;\n  left: 50%;\n  transform: translateX(-50%) scaleX(0);\n  width: 60%;\n  height: 3px;\n  background: linear-gradient(90deg, var(--color-primary), var(--color-primary-light));\n  transition: transform 0.35s cubic-bezier(0.4, 0, 0.2, 1);\n  border-radius: 0 0 3px 3px;\n}\n\n.main-tab.active {\n  color: var(--color-primary);\n  background: linear-gradient(180deg, rgba(45, 157, 120, 0.06) 0%, transparent 100%);\n}\n\n.main-tab.active::before {\n  transform: translateX(-50%) scaleX(1);\n}\n\n.main-tab:hover {\n  color: var(--color-primary);\n  background: rgba(45, 157, 120, 0.04);\n}\n\n.tab-content {\n  padding: 20px;\n  display: none;\n  animation: slideIn 0.3s ease-out;\n}\n\n.tab-content.active {\n  display: block;\n}\n\n@keyframes slideIn {\n  from { opacity: 0; transform: translateY(8px); }\n  to { opacity: 1; transform: translateY(0); }\n}\n\n\/* 来源选择子Tab *\/\n.source-subtabs {\n  display: grid;\n  grid-template-columns: repeat(4, 1fr);\n  gap: 10px;\n  margin-bottom: 20px;\n}\n\n.source-subtab {\n  padding: 14px 10px;\n  text-align: center;\n  font-size: 12px;\n  font-weight: 600;\n  color: var(--text-secondary);\n  background: var(--bg-elevated);\n  border-radius: 14px;\n  cursor: pointer;\n  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n  border: 2px solid transparent;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  gap: 6px;\n}\n\n.source-subtab i {\n  font-size: 18px;\n  transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.source-subtab.novel.active {\n  color: white;\n  background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);\n  border-color: rgba(239, 68, 68, 0.5);\n  box-shadow: 0 8px 24px rgba(239, 68, 68, 0.35);\n  transform: translateY(-2px);\n}\n\n.source-subtab.audio.active {\n  color: white;\n  background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);\n  border-color: rgba(59, 130, 246, 0.5);\n  box-shadow: 0 8px 24px rgba(59, 130, 246, 0.35);\n  transform: translateY(-2px);\n}\n\n.source-subtab.comic.active {\n  color: white;\n  background: linear-gradient(135deg, #a855f7 0%, #9333ea 100%);\n  border-color: rgba(168, 85, 247, 0.5);\n  box-shadow: 0 8px 24px rgba(168, 85, 247, 0.35);\n  transform: translateY(-2px);\n}\n\n.source-subtab.drama.active {\n  color: white;\n  background: linear-gradient(135deg, #f97316 0%, #ea580c 100%);\n  border-color: rgba(249, 115, 22, 0.5);\n  box-shadow: 0 8px 24px rgba(249, 115, 22, 0.35);\n  transform: translateY(-2px);\n}\n\n.source-subtab:hover:not(.active) {\n  border-color: var(--color-primary);\n  background: rgba(45, 157, 120, 0.06);\n  transform: translateY(-1px);\n}\n\n.source-subtab.active i {\n  transform: scale(1.1);\n}\n\n\/* 来源选项 *\/\n.source-options {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(125px, 1fr));\n  gap: 12px;\n}\n\n.source-option {\n  padding: 14px 16px;\n  background: var(--bg-elevated);\n  border-radius: 12px;\n  text-align: center;\n  font-size: 13px;\n  font-weight: 600;\n  color: var(--text-secondary);\n  cursor: pointer;\n  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n  border: 2px solid transparent;\n}\n\n.source-option:hover {\n  border-color: var(--color-primary);\n  background: rgba(45, 157, 120, 0.05);\n  transform: translateY(-2px);\n}\n\n.source-option.selected {\n  background: linear-gradient(135deg, rgba(45, 157, 120, 0.18), rgba(61, 184, 147, 0.08));\n  color: var(--color-primary);\n  border-color: var(--color-primary);\n  box-shadow: 0 4px 16px rgba(45, 157, 120, 0.2);\n  transform: translateY(-2px);\n}\n\n.source-option.all {\n  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n  color: white;\n  box-shadow: 0 4px 16px rgba(102, 126, 234, 0.3);\n}\n\n.source-option.all.selected {\n  background: linear-gradient(135deg, #764ba2 0%, #667eea 100%);\n  box-shadow: 0 6px 24px rgba(102, 126, 234, 0.45);\n  transform: translateY(-3px);\n}\n\n.source-option.all:hover {\n  border-color: transparent;\n  transform: translateY(-3px);\n}\n\n\/* 选中状态提示 *\/\n.selected-info {\n  margin-top: 20px;\n  padding: 16px 18px;\n  background: var(--bg-elevated);\n  border-radius: 12px;\n  font-size: 13px;\n  color: var(--text-secondary);\n  border: 1px solid var(--border-color);\n}\n\n.selected-info span {\n  color: var(--color-primary);\n  font-weight: 700;\n}\n\n\/* 设置项样式 *\/\n.setting-item {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  padding: 20px;\n  background: var(--bg-elevated);\n  border-radius: 16px;\n  margin-bottom: 12px;\n  border: 1px solid var(--border-color);\n  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n  position: relative;\n  overflow: hidden;\n}\n\n\n.setting-item:hover::before {\n  opacity: 1;\n}\n\n.setting-info {\n  flex: 1;\n  min-width: 0;\n}\n\n.setting-title {\n  font-size: 16px;\n  font-weight: 700;\n  color: var(--text-primary);\n  margin-bottom: 6px;\n}\n\n.setting-desc {\n  font-size: 13px;\n  color: var(--text-secondary);\n  line-height: 1.5;\n}\n\n\/* 开关样式 *\/\n.switch {\n  position: relative;\n  width: 54px;\n  height: 30px;\n  flex-shrink: 0;\n}\n\n.switch input {\n  opacity: 0;\n  width: 0;\n  height: 0;\n}\n\n.slider {\n  position: absolute;\n  cursor: pointer;\n  top: 0;\n  left: 0;\n  right: 0;\n  bottom: 0;\n  background: linear-gradient(135deg, #e5e7eb 0%, #d1d5db 100%);\n  transition: all 0.35s cubic-bezier(0.4, 0, 0.2, 1);\n  border-radius: 30px;\n  box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1), inset 0 -2px 4px rgba(0, 0, 0, 0.05);\n}\n\n.slider:before {\n  position: absolute;\n  content: \"\";\n  height: 24px;\n  width: 24px;\n  left: 3px;\n  bottom: 3px;\n  background: linear-gradient(135deg, #ffffff 0%, #f3f4f6 100%);\n  transition: all 0.35s cubic-bezier(0.4, 0, 0.2, 1);\n  border-radius: 50%;\n  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.1);\n}\n\ninput:checked + .slider {\n  background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-primary-light) 100%);\n  box-shadow: 0 4px 16px rgba(45, 157, 120, 0.35), inset 0 1px 0 rgba(255, 255, 255, 0.2);\n}\n\ninput:checked + .slider:before {\n  transform: translateX(24px);\n  background: linear-gradient(135deg, #ffffff 0%, #fefefe 100%);\n  box-shadow: 0 3px 8px rgba(0, 0, 0, 0.2), 0 1px 2px rgba(0, 0, 0, 0.1);\n}\n\n\/* 关键词高亮样式 *\/\n.highlight {\n  color: var(--color-primary);\n  font-weight: 700;\n  position: relative;\n}\n\n.highlight::after {\n  content: '';\n  position: absolute;\n  bottom: -2px;\n  left: 0;\n  width: 100%;\n  height: 2px;\n  background: linear-gradient(90deg, var(--color-primary), var(--color-primary-light));\n  border-radius: 2px;\n}\n\n.highlight-orange {\n  color: #f97316;\n  font-weight: 700;\n}\n\n.highlight-blue {\n  color: #3b82f6;\n  font-weight: 700;\n}\n\n.highlight-purple {\n  color: #a855f7;\n  font-weight: 700;\n}\n\n.highlight-red {\n  color: #ef4444;\n  font-weight: 700;\n}\n\n\/* 选择器样式 *\/\n.select-wrapper {\n  position: relative;\n  flex-shrink: 0;\n}\n\n.select-wrapper select {\n  appearance: none;\n  -webkit-appearance: none;\n  padding: 10px 34px 10px 14px;\n  font-size: 13px;\n  font-weight: 600;\n  color: var(--text-primary);\n  background: var(--bg-card);\n  border: 2px solid var(--border-color);\n  border-radius: 10px;\n  cursor: pointer;\n  min-width: 110px;\n  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.select-wrapper select:hover {\n  border-color: var(--color-primary);\n}\n\n.select-wrapper select:focus {\n  outline: none;\n  border-color: var(--color-primary);\n  box-shadow: 0 0 0 3px rgba(45, 157, 120, 0.1);\n}\n\n.select-wrapper::after {\n  content: '\\\\f078';\n  font-family: 'Font Awesome 6 Free';\n  font-weight: 900;\n  position: absolute;\n  right: 10px;\n  top: 50%;\n  transform: translateY(-50%);\n  color: var(--text-secondary);\n  pointer-events: none;\n  font-size: 13px;\n  transition: transform 0.3s ease;\n}\n\n.select-wrapper:hover::after {\n  transform: translateY(-50%) rotate(180deg);\n}\n\n\/* 提示文案样式 *\/\n.tip-alert {\n  background: linear-gradient(135deg, rgba(255, 193, 7, 0.12) 0%, rgba(255, 152, 0, 0.08) 100%);\n  border: 1px solid rgba(255, 193, 7, 0.25);\n  border-radius: 16px;\n  padding: 16px 18px;\n  margin: 20px;\n  display: flex;\n  align-items: flex-start;\n  gap: 14px;\n  box-shadow: 0 4px 16px rgba(255, 193, 7, 0.15), inset 0 1px 0 rgba(255, 255, 255, 0.8);\n  position: relative;\n  overflow: hidden;\n}\n\n.tip-alert::before {\n  content: '';\n  position: absolute;\n  top: -50%;\n  right: -20%;\n  width: 100px;\n  height: 100px;\n  background: rgba(255, 193, 7, 0.1);\n  border-radius: 50%;\n}\n\n.tip-alert i {\n  color: #f97316;\n  font-size: 22px;\n  flex-shrink: 0;\n  margin-top: 2px;\n  position: relative;\n  z-index: 1;\n}\n\n.tip-alert p {\n  font-size: 13px;\n  color: var(--text-secondary);\n  line-height: 1.6;\n  margin: 0;\n  position: relative;\n  z-index: 1;\n}\n\n.tip-alert .highlight-text {\n  color: #ea580c;\n  font-weight: 700;\n}\n\n\/* 响应式 *\/\n@media (max-width: 600px) {\n  .container {\n    padding: 16px 12px;\n  }\n  \n  .main-card {\n    border-radius: 20px;\n  }\n  \n  .tab-content {\n    padding: 16px;\n  }\n  \n  .tip-alert {\n    margin: 16px;\n    padding: 14px 16px;\n  }\n  \n  .source-subtabs {\n    grid-template-columns: repeat(2, 1fr);\n    gap: 8px;\n  }\n  \n  .source-options {\n    grid-template-columns: repeat(auto-fill, minmax(105px, 1fr));\n    gap: 10px;\n  }\n  \n  .setting-item {\n    padding: 16px;\n  }\n  \n  .setting-title {\n    font-size: 15px;\n  }\n  \n  .setting-desc {\n    font-size: 12px;\n  }\n}\n\n::-webkit-scrollbar {\n  width: 6px;\n  height: 6px;\n}\n\n::-webkit-scrollbar-track {\n  background: var(--bg-elevated);\n  border-radius: 3px;\n}\n\n::-webkit-scrollbar-thumb {\n  background: var(--text-tertiary);\n  border-radius: 3px;\n}\n\n::-webkit-scrollbar-thumb:hover {\n  background: var(--text-secondary);\n}\n<\/style>\n<\/head>\n<body data-theme=\"light\">\n<div class=\"container\">\n  <div class=\"main-card\">\n    <div class=\"tip-alert\">\n      <p>设置完成后请点击右上角的<span class=\"highlight-text\">√<\/span>应用设置<\/p>\n    <\/div>\n    \n    <div class=\"main-tabs\">\n      <div class=\"main-tab active\" id=\"tabBookSource\">书源设置<\/div>\n      <div class=\"main-tab\" id=\"tabSource\">搜索设置<\/div>\n    <\/div>\n    \n    <div class=\"tab-content active\" id=\"contentBookSource\">\n      <div class=\"setting-item\">\n        <div class=\"setting-info\">\n          <div class=\"setting-title\">搜索模式<\/div>\n          <div class=\"setting-desc\">切换后会搜索对应的类型,不推荐手动设置,可以在搜索的时候使用关键词:<span class=\"highlight-orange\">t:书名<\/span> 搜听书,<span class=\"highlight-blue\">d:书名<\/span> 搜短剧,<span class=\"highlight-purple\">m:书名<\/span> 搜漫画<\/div>\n        <\/div>\n        <div class=\"select-wrapper\">\n          <select id=\"searchMode\">\n            <option value=\"小说\">小说<\/option>\n            <option value=\"听书\">听书<\/option>\n            <option value=\"短剧\">短剧<\/option>\n            <option value=\"漫画\">漫画<\/option>\n          <\/select>\n        <\/div>\n      <\/div>\n      \n      <div class=\"setting-item\">\n        <div class=\"setting-info\">\n          <div class=\"setting-title\">显示图片<\/div>\n          <div class=\"setting-desc\">关闭后,文章内的图片将会清除<\/div>\n        <\/div>\n        <label class=\"switch\">\n          <input type=\"checkbox\" id=\"showImageSwitch\" checked>\n          <span class=\"slider\"><\/span>\n        <\/label>\n      <\/div>\n      \n      <div class=\"setting-item\">\n        <div class=\"setting-info\">\n          <div class=\"setting-title\">完整简介<\/div>\n          <div class=\"setting-desc\">关闭后,书籍详情页会精简简介内容<\/div>\n        <\/div>\n        <label class=\"switch\">\n          <input type=\"checkbox\" id=\"fullDescSwitch\" checked>\n          <span class=\"slider\"><\/span>\n        <\/label>\n      <\/div>\n      \n      <div class=\"setting-item\">\n        <div class=\"setting-info\">\n          <div class=\"setting-title\">同步书架<\/div>\n          <div class=\"setting-desc\">开启后,刷新书籍详情页会将书架书籍同步到光遇网站网页端书架<\/div>\n        <\/div>\n        <label class=\"switch\">\n          <input type=\"checkbox\" id=\"syncBookshelfSwitch\">\n          <span class=\"slider\"><\/span>\n        <\/label>\n      <\/div>\n      \n      <div class=\"setting-item\" id=\"syncPopupSection\" style=\"display: none;\">\n        <div class=\"setting-info\">\n          <div class=\"setting-title\">同步弹窗<\/div>\n          <div class=\"setting-desc\">弹窗开关可以让您知晓同步状态,关闭后不会弹窗<\/div>\n        <\/div>\n        <label class=\"switch\">\n          <input type=\"checkbox\" id=\"syncPopupSwitch\">\n          <span class=\"slider\"><\/span>\n        <\/label>\n      <\/div>\n      \n      <div class=\"setting-item\">\n        <div class=\"setting-info\">\n          <div class=\"setting-title\">网络模式<\/div>\n          <div class=\"setting-desc\">开启后部分来源会使用本地网络请求,如<span class=\"highlight-orange\">69书吧<\/span>,可能需要开梯子访问,不推荐开启<\/div>\n        <\/div>\n        <div class=\"select-wrapper\">\n          <select id=\"networkMode\">\n            <option value=\"服务器\">服务器<\/option>\n            <option value=\"本地\">本地<\/option>\n          <\/select>\n        <\/div>\n      <\/div>\n      \n      <div class=\"setting-item\">\n        <div class=\"setting-info\">\n          <div class=\"setting-title\">强制搜索<\/div>\n          <div class=\"setting-desc\">开启后搜索时会 <span class=\"highlight-red\">强制搜索全部来源<\/span>,搜索时长会 <span class=\"highlight-orange\">明显增长<\/span>,若无必要不建议开启<\/div>\n        <\/div>\n        <label class=\"switch\">\n          <input type=\"checkbox\" id=\"forceSearchSwitch\">\n          <span class=\"slider\"><\/span>\n        <\/label>\n      <\/div>\n      \n      <div class=\"setting-item\">\n        <div class=\"setting-info\">\n          <div class=\"setting-title\">目录显示来源<\/div>\n          <div class=\"setting-desc\">开启后,目录最后一个章节的标题将显示来源,方便换源的时候开启加载目录可以知道是哪个来源<\/div>\n        <\/div>\n        <label class=\"switch\">\n          <input type=\"checkbox\" id=\"showSourceInTocSwitch\" checked>\n          <span class=\"slider\"><\/span>\n        <\/label>\n      <\/div>\n    <\/div>\n    \n    <div class=\"tab-content\" id=\"contentSource\">\n      <div class=\"source-subtabs\">\n        <div class=\"source-subtab novel active\" id=\"subtabNovel\" data-type=\"小说\">\n          <i class=\"fas fa-book-open\"><\/i>\n          <span>小说<\/span>\n        <\/div>\n        <div class=\"source-subtab audio\" id=\"subtabAudio\" data-type=\"听书\">\n          <i class=\"fas fa-headphones\"><\/i>\n          <span>听书<\/span>\n        <\/div>\n        <div class=\"source-subtab comic\" id=\"subtabComic\" data-type=\"漫画\">\n          <i class=\"fas fa-image\"><\/i>\n          <span>漫画<\/span>\n        <\/div>\n        <div class=\"source-subtab drama\" id=\"subtabDrama\" data-type=\"短剧\">\n          <i class=\"fas fa-video\"><\/i>\n          <span>短剧<\/span>\n        <\/div>\n      <\/div>\n      <div class=\"selected-info\" style=\"margin-bottom: 10px; background: rgba(45, 157, 120, 0.08); border-color: rgba(45, 157, 120, 0.2);\">\n        推荐选择全部,如需要搜索特定的资源可以搜索的时候使用 <span class=\"highlight\">书名@来源<\/span> 进行搜索\n      <\/div>\n      <div id=\"sourceOptions\" class=\"source-options\"><\/div>\n      <div class=\"selected-info\" id=\"selectedInfo\">\n        当前选中: <span id=\"selectedCount\">0<\/span> 个来源\n      <\/div>\n    <\/div>\n    \n    <div id=\"hiddenSettings\" style=\"display: none;\">\n      <script type=\"application\/json\" id=\"settingsJson\">\n        ${JSON.stringify(data || {\"小说\":\"全部\",\"听书\":\"全部\",\"漫画\":\"全部\",\"短剧\":\"全部\",\"搜索模式\":\"小说\",\"显示图片\":\"true\",\"完整简介\":\"true\",\"同步书架\":\"false\",\"同步弹窗\":\"true\",\"网络模式\":\"服务器\",\"强制搜索\":\"false\",\"目录显示来源\":\"true\"})}\n      <\/script>\n      <div id=\"settingsDiv\">\n        <span id=\"novelSource\">${(data && data.小说) || '全部'}<\/span>\n        <span id=\"audioSource\">${(data && data.听书) || '全部'}<\/span>\n        <span id=\"comicSource\">${(data && data.漫画) || '全部'}<\/span>\n        <span id=\"dramaSource\">${(data && data.短剧) || '全部'}<\/span>\n        <span id=\"searchModeValue\">${(data && data.搜索模式) || '小说'}<\/span>\n        <span id=\"showImageSwitchValue\">${(data && data.显示图片) || 'true'}<\/span>\n        <span id=\"fullDescSwitchValue\">${(data && data.完整简介) || 'true'}<\/span>\n        <span id=\"syncBookshelfSwitchValue\">${(data && data.同步书架) || 'false'}<\/span>\n        <span id=\"syncPopupSwitchValue\">${(data && data.同步弹窗) || 'true'}<\/span>\n        <span id=\"networkModeValue\">${(data && data.网络模式) || '服务器'}<\/span>\n        <span id=\"forceSearchSwitchValue\">${(data && data.强制搜索) || 'false'}<\/span>\n        <span id=\"showSourceInTocValue\">${(data && data.目录显示来源) || 'true'}<\/span>\n      <\/div>\n    <\/div>\n  <\/div>\n<\/div>\n\n<script>\nconst SOURCES = ${JSON.stringify(config)};\nlet settings = ${JSON.stringify(data)};\nlet currentType = '小说';\n\ndocument.getElementById('tabSource').addEventListener('click', () => {\n  document.getElementById('tabSource').classList.add('active');\n  document.getElementById('tabBookSource').classList.remove('active');\n  document.getElementById('contentSource').classList.add('active');\n  document.getElementById('contentBookSource').classList.remove('active');\n});\n\ndocument.getElementById('tabBookSource').addEventListener('click', () => {\n  document.getElementById('tabBookSource').classList.add('active');\n  document.getElementById('tabSource').classList.remove('active');\n  document.getElementById('contentBookSource').classList.add('active');\n  document.getElementById('contentSource').classList.remove('active');\n});\n\nconst subtabs = document.querySelectorAll('.source-subtab');\nsubtabs.forEach(tab => {\n  tab.addEventListener('click', () => {\n    subtabs.forEach(t => t.classList.remove('active'));\n    tab.classList.add('active');\n    currentType = tab.dataset.type;\n    renderSourceOptions();\n  });\n});\n\nfunction renderSourceOptions() {\n  const optionsContainer = document.getElementById('sourceOptions');\n  const key = \\`搜索\\${currentType}\\`;\n  const sources = SOURCES[key] || [];\n  const savedValue = settings[currentType] || '';\n  const selectedValues = savedValue === '全部' ? ['全部'] : (savedValue ? savedValue.split(',') : []);\n  \n  optionsContainer.innerHTML = '';\n  \n  const allOption = document.createElement('div');\n  allOption.className = \\`source-option all \\${selectedValues.includes('全部') ? 'selected' : ''}\\`;\n  allOption.textContent = '全部';\n  allOption.addEventListener('click', () => selectOption('全部'));\n  optionsContainer.appendChild(allOption);\n  \n  sources.forEach(source => {\n    const option = document.createElement('div');\n    option.className = \\`source-option \\${selectedValues.includes(source) ? 'selected' : ''}\\`;\n    option.textContent = source;\n    option.addEventListener('click', () => selectOption(source));\n    optionsContainer.appendChild(option);\n  });\n  \n  updateSelectedInfo();\n}\n\nfunction updateHiddenSettings() {\n  const jsonEl = document.getElementById('settingsJson');\n  if (jsonEl) {\n    jsonEl.textContent = JSON.stringify(settings);\n  }\n  \n  const typeMap = {\n    '小说': 'novelSource',\n    '听书': 'audioSource',\n    '漫画': 'comicSource',\n    '短剧': 'dramaSource',\n    '搜索模式': 'searchModeValue',\n    '显示图片': 'showImageSwitchValue',\n    '完整简介': 'fullDescSwitchValue',\n    '同步书架': 'syncBookshelfSwitchValue',\n    '同步弹窗': 'syncPopupSwitchValue',\n    '网络模式': 'networkModeValue',\n    '强制搜索': 'forceSearchSwitchValue',\n    '目录显示来源': 'showSourceInTocValue'\n  };\n  \n  Object.keys(typeMap).forEach(type => {\n    const el = document.getElementById(typeMap[type]);\n    if (el) {\n      el.textContent = settings[type] != undefined ? settings[type] : (type.includes('开关') ? 'false' : '全部');\n    }\n  });\n}\n\nfunction setupBookSourceSettings() {\n  document.getElementById('searchMode').addEventListener('change', (e) => {\n    settings['搜索模式'] = e.target.value;\n    updateHiddenSettings();\n  });\n  \n  document.getElementById('showImageSwitch').addEventListener('change', (e) => {\n    settings['显示图片'] = e.target.checked ? 'true' : 'false';\n    updateHiddenSettings();\n  });\n  \n  document.getElementById('fullDescSwitch').addEventListener('change', (e) => {\n    settings['完整简介'] = e.target.checked ? 'true' : 'false';\n    updateHiddenSettings();\n  });\n  \n  document.getElementById('syncBookshelfSwitch').addEventListener('change', (e) => {\n    settings['同步书架'] = e.target.checked ? 'true' : 'false';\n    updateHiddenSettings();\n    \n    const syncPopupSection = document.getElementById('syncPopupSection');\n    const syncPopupSwitch = document.getElementById('syncPopupSwitch');\n    if (e.target.checked) {\n      syncPopupSection.style.display = 'flex';\n      if (settings['同步弹窗'] == undefined) {\n        settings['同步弹窗'] = 'true';\n        syncPopupSwitch.checked = true;\n        updateHiddenSettings();\n      }\n    } else {\n      syncPopupSection.style.display = 'none';\n    }\n  });\n  \n  document.getElementById('syncPopupSwitch').addEventListener('change', (e) => {\n    settings['同步弹窗'] = e.target.checked ? 'true' : 'false';\n    updateHiddenSettings();\n  });\n  \n  document.getElementById('networkMode').addEventListener('change', (e) => {\n    settings['网络模式'] = e.target.value;\n    updateHiddenSettings();\n  });\n  \n  document.getElementById('forceSearchSwitch').addEventListener('change', (e) => {\n    settings['强制搜索'] = e.target.checked ? 'true' : 'false';\n    updateHiddenSettings();\n  });\n  \n  document.getElementById('showSourceInTocSwitch').addEventListener('change', (e) => {\n    settings['目录显示来源'] = e.target.checked ? 'true' : 'false';\n    updateHiddenSettings();\n  });\n}\n\nfunction initBookSourceSettings() {\n  const searchModeEl = document.getElementById('searchMode');\n  if (searchModeEl && settings['搜索模式']) {\n    searchModeEl.value = settings['搜索模式'];\n  }\n  \n  const showImageEl = document.getElementById('showImageSwitch');\n  if (showImageEl) {\n    showImageEl.checked = settings['显示图片'] != 'false';\n  }\n  \n  const fullDescEl = document.getElementById('fullDescSwitch');\n  if (fullDescEl) {\n    fullDescEl.checked = settings['完整简介'] != 'false';\n  }\n  \n  const syncBookshelfEl = document.getElementById('syncBookshelfSwitch');\n  if (syncBookshelfEl) {\n    syncBookshelfEl.checked = settings['同步书架'] === 'true';\n  }\n  \n  const syncPopupSection = document.getElementById('syncPopupSection');\n  const syncPopupSwitch = document.getElementById('syncPopupSwitch');\n  if (syncPopupSwitch) {\n    syncPopupSwitch.checked = settings['同步弹窗'] != 'false';\n  }\n  if (syncPopupSection) {\n    syncPopupSection.style.display = (settings['同步书架'] === 'true') ? 'flex' : 'none';\n  }\n  \n  const networkModeEl = document.getElementById('networkMode');\n  if (networkModeEl && settings['网络模式']) {\n    networkModeEl.value = settings['网络模式'];\n  }\n  \n  const forceSearchEl = document.getElementById('forceSearchSwitch');\n  if (forceSearchEl) {\n    forceSearchEl.checked = settings['强制搜索'] === 'true';\n  }\n  \n  const showSourceInTocEl = document.getElementById('showSourceInTocSwitch');\n  if (showSourceInTocEl) {\n    showSourceInTocEl.checked = settings['目录显示来源'] != 'false';\n  }\n}\n\nfunction selectOption(value) {\n  const key = \\`搜索\\${currentType}\\`;\n  const sources = SOURCES[key] || [];\n  \n  if (value === '全部') {\n    settings[currentType] = '全部';\n    updateHiddenSettings();\n    renderSourceOptions();\n    return;\n  }\n  \n  let currentValue = settings[currentType] || '';\n  \n  if (currentValue === '全部') {\n    currentValue = '';\n  }\n  \n  let values = currentValue ? currentValue.split(',') : [];\n  \n  const index = values.indexOf(value);\n  if (index > -1) {\n    values.splice(index, 1);\n  } else {\n    values.push(value);\n  }\n  \n  if (values.length === 0) {\n    settings[currentType] = '全部';\n  } else {\n    settings[currentType] = values.join(',');\n  }\n  \n  updateHiddenSettings();\n  renderSourceOptions();\n}\n\nfunction updateSelectedInfo() {\n  const key = \\`搜索\\${currentType}\\`;\n  const sources = SOURCES[key] || [];\n  const savedValue = settings[currentType] || '';\n  \n  if (savedValue === '全部') {\n    document.getElementById('selectedCount').textContent = '全部';\n  } else {\n    const count = savedValue ? savedValue.split(',').length : 0;\n    document.getElementById('selectedCount').textContent = count;\n  }\n}\n\ndocument.addEventListener('DOMContentLoaded', () => {\n  let savedSettings = {};\n  try {\n    const jsonEl = document.getElementById('settingsJson');\n    if (jsonEl) {\n      savedSettings = JSON.parse(jsonEl.textContent);\n    }\n  } catch (e) {\n    console.error('读取初始设置失败:', e);\n  }\n  \n  settings = {\n    '小说': savedSettings['小说'] || '全部',\n    '听书': savedSettings['听书'] || '全部',\n    '漫画': savedSettings['漫画'] || '全部',\n    '短剧': savedSettings['短剧'] || '全部',\n    '搜索模式': savedSettings['搜索模式'] || '小说',\n    '显示图片': savedSettings['显示图片'] || 'true',\n    '完整简介': savedSettings['完整简介'] || 'true',\n    '同步书架': savedSettings['同步书架'] || 'false',\n    '同步弹窗': savedSettings['同步弹窗'] || 'true',\n    '网络模式': savedSettings['网络模式'] || '服务器',\n    '强制搜索': savedSettings['强制搜索'] || 'false',\n    '目录显示来源': savedSettings['目录显示来源'] || 'true'\n  };\n  \n  initBookSourceSettings();\n  setupBookSourceSettings();\n  renderSourceOptions();\n});\n<\/script>\n<\/body>\n<\/html>\n    `\n    let body;\n    try {\n        body = java.startBrowserAwait(`data:text\/html;base64,${java.base64Encode(html)}`, '光遇书源设置', false).body();\n    } catch (e) {\n        let islyc = this.checkEnv();\n        if (islyc == \"安卓\") {\n            java.longToast(\"\\n当前软件版本过低,请更新软件,或者使用底部备用设置方案~\");\n        } else {\n            java.longToast(\"\\n请点击页面右上角√才能设置成功哦~\");\n        }\n        return;\n    }\n    \n    let settings = {};\n    try {\n        const getSpanText = (spanId) => {\n            const regex = new RegExp('id=\"' + spanId + '\"\\s*>\\s*([^<]*?)\\s*<\\\/span>');\n            const match = body.match(regex);\n            return match ? match[1].trim() : \"\";\n        };\n        settings = {\n            '搜索模式': getSpanText('searchModeValue'),\n            '显示图片': getSpanText('showImageSwitchValue'),\n            '完整简介': getSpanText('fullDescSwitchValue'),\n            '同步书架': getSpanText('syncBookshelfSwitchValue'),\n            '同步弹窗': getSpanText('syncPopupSwitchValue'),\n            '网络模式': getSpanText('networkModeValue'),\n            '强制搜索': getSpanText('forceSearchSwitchValue'),\n            '目录显示来源': getSpanText('showSourceInTocValue'),\n            '小说': getSpanText('novelSource'),\n            '听书': getSpanText('audioSource'),\n            '漫画': getSpanText('comicSource'),\n            '短剧': getSpanText('dramaSource'),\n        };\n        const settingsText = Object.keys(settings).map(key => `${key}: ${settings[key]}`).join('\\n');\n        java.longToast(\"\\n\" + settingsText);\n\n    } catch (e) {\n        settings = {\n            '搜索模式': '小说',\n            '显示图片': 'true',\n            '完整简介': 'true',\n            '同步书架': 'false',\n            '同步弹窗': 'true',\n            '网络模式': '服务器',\n            '强制搜索': 'false',\n            '目录显示来源': 'true',\n            '小说': '全部',\n            '听书': '全部',\n            '漫画': '全部',\n            '短剧': '全部',\n        };\n    }\n    this.setVariable(\"更多设置\", settings, false)\n}\n",
    "lastUpdateTime": "1779419473953",
    "loginUi": "[\n     {\n        \"name\": \"🔭 切换线路\",\n        \"type\": \"button\",\n        \"action\": \"getServerSettings()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 1\n        }\n    },{\n        \"name\": \"⚙️ 书源设置\",\n        \"type\": \"button\",\n        \"action\": \"getHtmlSettings()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.4\n        }\n    },  {\n        \"name\": \"🗨 段评设置\",\n        \"type\": \"button\",\n        \"action\": \"getSvgSettings()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.4\n        }\n    },{\n        \"name\": \"邮箱\",\n        \"type\": \"text\"\n    }, {\n        \"name\": \"密码\",\n        \"type\": \"password\"\n    }, {\n        \"name\": \"🔅登录账号\",\n        \"type\": \"button\",\n        \"action\": \"login(true)\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.4\n        }\n    }, {\n        \"name\": \"🔐注册账号\",\n        \"type\": \"button\",\n        \"action\": \"register()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.4\n        }\n    }, {\n        \"name\": \" 🔚 退出登录\",\n        \"type\": \"button\",\n        \"action\": \"logout()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.4\n        }\n    },\n    {\n        \"name\": \"🏝用户后台\",\n        \"type\": \"button\",\n        \"action\": \"user()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.4\n        }\n    },{\n        \"name\": \"📴 清理设备\",\n        \"type\": \"button\",\n        \"action\": \"clearDevice()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.4\n        }\n    },{\n        \"name\": \"🪪 查看信息\",\n        \"type\": \"button\",\n        \"action\": \"checkStatus()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.4\n        }\n    },{\n        \"name\": \"🌷求打赏\",\n        \"type\": \"button\",\n        \"action\": \"vip()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.4\n        }\n    },{\n        \"name\": \"🎈永久发布页\",\n        \"type\": \"button\",\n        \"action\": \"fb()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.4\n        }\n    },{\n        \"name\": \"✳️ 更新配置\",\n        \"type\": \"button\",\n        \"action\": \"_getCloudSettings()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.4\n        }\n    },{\n        \"name\": \"❇️ 更新书源\",\n        \"type\": \"button\",\n        \"action\": \"renderVersionPage()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.4\n        }\n    }, {\n        \"name\": \"⛔‼️ 清空还原设置\",\n        \"type\": \"button\",\n        \"action\": \"deleteVariable()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 1\n        }\n    }, {\n        \"name\": \"===下方为备用设置方案===\",\n        \"type\": \"button\",\n        \"action\": \"\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 1\n        }\n    }, {\n        \"name\": \"📖搜索小说\",\n        \"type\": \"button\",\n        \"action\": \"ste('小说')\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.4\n        }\n    }, {\n        \"name\": \"🎧搜索听书\",\n        \"type\": \"button\",\n        \"action\": \"ste('听书')\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.4\n        }\n    }, {\n        \"name\": \"🎥搜索短剧\",\n        \"type\": \"button\",\n        \"action\": \"ste('短剧')\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.4\n        }\n    }, {\n        \"name\": \"🎨搜索漫画\",\n        \"type\": \"button\",\n        \"action\": \"ste('漫画')\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.4\n        }\n    }, {\n        \"name\": \"段评开关\",\n        \"type\": \"button\",\n        \"action\": \"ste('段评')\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.25\n        }\n    }, {\n        \"name\": \"图片开关\",\n        \"type\": \"button\",\n        \"action\": \"ste('图片')\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.25\n        }\n    }, {\n        \"name\": \"简介开关\",\n        \"type\": \"button\",\n        \"action\": \"ste('简介')\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.25\n        }\n    }, {\n        \"name\": \"同步书架\",\n        \"type\": \"button\",\n        \"action\": \"ste('同步')\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.25\n        }\n    }, {\n        \"name\": \"强制搜索\",\n        \"type\": \"button\",\n        \"action\": \"ste('强制')\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.25\n        }\n    }, {\n        \"name\": \"网络模式\",\n        \"type\": \"button\",\n        \"action\": \"ste('网络')\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.25\n        }\n    }, {\n        \"name\": \"目录显示来源开关\",\n        \"type\": \"button\",\n        \"action\": \"ste('目录')\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 1\n        }\n    }, {\n        \"name\": \"发现页来源(支持的平台请前往源变量中查看)\",\n        \"type\": \"text\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 1\n        }\n    }, {\n        \"name\": \"搜索来源(支持的平台请前往源变量中查看,多个来源用英文逗号分割)\",\n        \"type\": \"text\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 1\n        }\n    }, {\n        \"name\": \"设置发现页来源\",\n        \"type\": \"button\",\n        \"action\": \"setFindSource()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.4\n        }\n    }, {\n        \"name\": \"设置搜索来源\",\n        \"type\": \"button\",\n        \"action\": \"setSearchSource()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.4\n        }\n    }\n]",
    "loginUrl": "\/\/登陆\nfunction login(flag) {\n    const token = getToken();\n    if (String(token).length > 10) {\n        java.longToast(flag ? '\\n当前✅️已登录,请🚫退出登录后重新登录' : '\\n✅️已登录');\n        return true;\n    }\n    if (flag == undefined) {\n        result = JSON.parse(source.getLoginInfo())\n    } else {\n        java.longToast(\"\\n\\n💞正在登录中...\")\n    }\n    let email = result.邮箱;\n    let pwd = result.密码;\n\n    if (!email || !pwd) {\n        java.longToast('\\n请先输入账号密码!');\n        return false;\n    }\n    if (!email.includes('@')) {\n        java.longToast('\\n请输入正确的邮箱格式!');\n        return false;\n    }\n    try {\n        let data = request('\/login_api','POST',{\n                register_email: email,\n                password: pwd\n            })\n        const response = JSON.parse(data);\n        if (response.code == 0) {\n            setAllCookies(`qttoken=${response.key}`);\n            java.longToast(\"\\n✅️登录成功\");\n            return true;\n        } else {\n            let msg = response.msg;\n            if (msg.includes('多次登录失败')) {\n                java.longToast('\\n💔多次登录失败,请检查账号密码,切换线路后重试或等2小时后重试!');\n            } else {\n                java.longToast('\\n💔' + (msg || \"登录失败,请重试!\"));\n            }\n            return false;\n        }\n    } catch (error) {\n        java.longToast('\\n💔登录失败,服务器错误,请更换线路后重试!\\n' + error);\n        return false;\n    }\n}\n\n\/\/ 用户注册\nfunction register() {\n    java.startBrowserAwait(BaseUrl() + '\/register', '光遇看书注册');\n}\n\n\/\/用户后台\nfunction user() {\n    const token = getToken();\n    if (String(token).length < 10) {\n        java.longToast('\\n🤔请先登陆');\n        return;\n    }\n    java.startBrowserAwait(BaseUrl() + '\/user', '光遇看书用户后台');\n}\n\n\/\/ 退出登陆\nfunction logout() {\n    removeAllCookies();\n}\n\n\/\/ 获取云端配置\nfunction _getCloudSettings() {\n    getCloudSettings(true)\n}\n\n\/\/打赏\nfunction vip() {\n    const token = getToken();\n    if (String(token).length < 10) {\n        java.longToast('\\n🤔请先登陆');\n        return;\n    }\n    java.startBrowserAwait(BaseUrl() + '\/coffee', '光遇聚合会员开通');\n}\n\nfunction formatDate(timestamp) {\n    const date = new Date(timestamp * 1000);\n    return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}`;\n}\n\nfunction isVips(res) {\n    const vipEndTime = res.vip_end_time;\n    const isVipValue = res.is_vip;\n\n    let vipType;\n    if (isVipValue == 0) {\n        return '普通用户';\n    } else if (isVipValue == 1) {\n        vipType = 'VIP';\n    } else {\n        vipType = 'SVIP';\n    }\n\n    if (!vipEndTime || vipEndTime == 0) {\n        return `${vipType} (已过期)`;\n    }\n\n    const currentTime = Math.floor(Date.now() \/ 1000);\n    const remainingDays = Math.ceil((vipEndTime - currentTime) \/ (24 * 60 * 60));\n\n    if (currentTime > vipEndTime) {\n        return `${vipType} (已过期)`;\n    }\n\n    if (remainingDays <= 7) {\n        return `${vipType} 剩余${remainingDays}天`;\n    }\n\n    if (vipEndTime >= 1912946812) {\n        return `${vipType} (永久)`;\n    }\n\n    return `${vipType}(${formatDate(vipEndTime)})`;\n}\n\nfunction checkStatus() {\n    const token = getToken();\n    if (String(token).length < 10) {\n        java.longToast('\\n🤔请先登陆');\n        return;\n    }\n    java.longToast('\\n\\n♻️查询中...');\n\n    try {\n        const res = JSON.parse(request(`\/user_api`, 'POST'));\n\n        if (res.id == undefined) {\n            throw new Error(res.msg || '获取用户信息失败');\n        }\n\n        result.邮箱 = res.email;\n        source.putLoginInfo(result);\n\n        let devices = 0;\n        try {\n            devices = res.device ? Object.keys(JSON.parse(res.device)).length : 0;\n        } catch (e) {\n            devices = res.device ? 1 : 0;\n        }\n\n        const isVip = isVips(res);\n        const today = java.timeFormat(new Date()).slice(0, 10);\n        const lastReadDate = res.last_read_time ? java.timeFormat(res.last_read_time * 1000).slice(0, 10) : '';\n        const todayReadCount = today == lastReadDate ? res.day_read_count : 0;\n\n        const tips = `\n${BaseUrl()}\n┏┅┅┅┅┅┅┱┄┄┄┄┄┄┄┄┄┄┐\n  🧢昵称     ${(res.nickname || '未设置').padEnd(20, '\\t')}\n┣┅┅┅┅┅┅╉┄┄┄┄┄┄┄┄┄┄┤\n ✉️邮箱    ${res.email.replace(\/(.{3}).*?@\/, '$1***@').padEnd(20, '\\t')}\n┣┅┅┅┅┅┅╉┄┄┄┄┄┄┄┄┄┄┤\n 🔑密钥    ${(`${res.user_key.substring(0, 4)}***${res.user_key.slice(-4)}`).padEnd(20, '\\t')}\n┣┅┅┅┅┅┅╉┄┄┄┄┄┄┄┄┄┄┤\n 📅注册时间  ${java.timeFormat(res.register_time * 1000).padEnd(20, '\\t')}\n┣┅┅┅┅┅┅╉┄┄┄┄┄┄┄┄┄┄┤\n🗒️今日阅读  ${todayReadCount.toString().padEnd(20, '\\t')}\n┣┅┅┅┅┅┅╉┄┄┄┄┄┄┄┄┄┄┤\n📚累计阅读  ${res.all_read_count.toString().padEnd(20, '\\t')}\n┣┅┅┅┅┅┅╉┄┄┄┄┄┄┄┄┄┄┤\n🕓最后阅读  ${(res.last_read_time ? java.timeFormat(res.last_read_time * 1000) : '未阅读').padEnd(20, '\\t')}\n┣┅┅┅┅┅┅╉┄┄┄┄┄┄┄┄┄┄┤\n📱在线设备  ${devices.toString().padEnd(20, '\\t')}\n┣┅┅┅┅┅┅╉┄┄┄┄┄┄┄┄┄┄┤\n 👑会员状态  ${isVip.padEnd(20, '\\t')}\n┣┅┅┅┅┅┅╉┄┄┄┄┄┄┄┄┄┄┤\n 🚫封禁状态  ${res.is_banned ? '已封禁' : '正常 '}       \n┗┅┅┅┅┅┅┹┄┄┄┄┄┄┄┄┄┄┘\n`;\n\n        java.log(tips);\n        java.longToast(tips);\n\n    } catch (e) {\n        java.toast(`\\n检测登录失败\\n${e.message}`);\n    }\n}\n\nfunction clearDevice() {\n    const token = getToken();\n    if (String(token).length < 10) {\n        java.longToast('\\n🤔请先登陆');\n        return;\n    }\n    let res = request(`\/clear`, \"POST\");\n    res = JSON.parse(res);\n    java.toast(res.code == 0 ? \"\\n\\n📴设备清除成功\" : res.msg)\n    \/\/Packages.java.lang.Thread.sleep(500)\n    \/\/checkStatus()\n}\n\nfunction fb() {\n    java.startBrowser('http:\/\/vip.gyks.cf', '光遇看书');\n}\n\nfunction setFindSource() {\n\tlet findSource = result[\"发现页来源(支持的平台请前往源变量中查看)\"];\n\tif (String(findSource)==\"\" || String(findSource) == \"undefined\") {\n\t\tfindSource = \"番茄\";\n\t\t}\n\tsetVariable(\"发现页来源\",findSource);\n java.longToast(`\\n已设置发现页来源为:${findSource}\\n设置后请刷新发现页!`)\n \n\t}\n\t\n\t\nfunction setSearchSource() {\n\tlet searchSource = result[\"搜索来源(支持的平台请前往源变量中查看,多个来源用英文逗号分割)\"];\n\tif (String(searchSource)==\"\" || String(searchSource) == \"undefined\") {\n\t\tsearchSource = '全部';\n\t\t}\n\tlet moreSettings = getVariable(\"更多设置\") || {};\n\tlet tab = moreSettings && moreSettings['搜索模式'] || '小说';\n\tmoreSettings[tab] = searchSource;\n\tsetVariable(\"更多设置\",moreSettings)\n  java.longToast(`\\n已设置搜索来源为:${tab}-${moreSettings[tab]}\\n设置后请重新搜索`)\n\t}\n\n",
    "respondTime": 180000,
    "ruleBookInfo": {
        "author": "$.author",
        "canReName": "1",
        "coverUrl": "$.thumb_url",
        "init": "<js>\nlet moreSettings = getVariable('更多设置');\n\n\nfunction fetchBookDetail(result) {\n\t  let res = {};\n\t  \/\/java.log(baseUrl)\n\t  \/\/java.longToast(result)\n\t  if (String(baseUrl).startsWith(\"data:\")) {\n    res = JSON.parse(java.hexDecodeToString(result));\n    } else {\n    \treturn result;\n    \t}\n    const book_id = res.book_id;\n    const tab = res.tab;\n    const sources = res.sources || res.source;\n    const url = res.url;\n    let html = \"\";\n    const proxy  = moreSettings && moreSettings['网络模式'] || '服务器';\n\n    if (url && proxy == \"本地\") {\n        html = fetchHtmlWithFallback(url, sources);\n    }\n\n    const varia = getAndFormatCustomVariable();\n    const requestUrl = `\/detail?book_id=${book_id}&source=${sources}&tab=${tab}&variable=${varia}`;\n\n    java.log(requestUrl);\n    return request(requestUrl, 'POST', { html: html });\n}\n\nfunction fetchHtmlWithFallback(url, sources) {\n    let html;\n    let ck69 = '';\n    let headers = {};\n    let op = '';\n\n    if (sources == '69书吧') {\n        ck69 = String(cookie.getCookie('https:\/\/www.69shuba.com'));\n        headers = {\n            \"Referer\": url,\n            \"Cookie\": ck69,\n            \"User-Agent\": java.getWebViewUA()\n        };\n        op = JSON.stringify({ \"headers\": headers });\n        html = java.ajax(`${url},${op}`);\n    } else {\n        html = java.ajax(url);\n    }\n\n    if (html.includes(\"Just a moment...\") && sources == '69书吧') {\n        cookie.removeCookie('https:\/\/www.69shuba.com');\n        java.longToast('需要真人验证,请进入任意书籍详情页过验证');\n        html = java.startBrowserAwait(url, \"需要真人验证,请进入任意书籍详情页过验证\").body();\n    }\n\n    return html;\n}\n\nfunction getAndFormatCustomVariable() {\n    let varia = String(book.getVariable('custom'));\n    if (varia === 'null') {\n        varia = '';\n    }\n    return JSON.stringify({ 'custom': varia });\n}\nfetchBookDetail(result)\n<\/js>$.data",
        "intro": "<js>\nconst {\n    source:sources,\n    book_id,\n    tab,\n    book_tts,\n    tags,\n    role,\n    last_chapter_title,\n    last_chapter_update_time,\n    word_number,\n    status,\n    score,\n    abstract,\n    copyright_info,\n    read_count,\n    read_count_all\n} = result;\nlet moreSettings = getVariable('更多设置');\n\n\nconst proxy = (moreSettings && moreSettings['网络模式'] || '服务器') == \"本地\" ? \"本地网络\" : \"服务器网络\";\n\nif ((book.readConfig && book.readConfig.useReplaceRule) == null) {\n    book.setUseReplaceRule(false);\n}\n\nlet nickname = '账号状态:⚠️ 未登录 | 点击右上角 🔖 登录';\nlet qttoken = getToken();\nif (qttoken.length > 10) {\n     nickname = '账号状态:✅已登录';\n}\ntry {\n    const user_info = JSON.parse(request(`\/get_avatar`, 'GET'));\n    if (user_info.code === 0) {\n        nickname = user_info.nickname \n            ? `欢迎回来:${user_info.nickname}`\n            : `欢迎回来:${user_info.email}(请前往用户后台设置用户名)`;\n    }\n} catch (e) {\n    \n}\n\nconst loginStatus = nickname;\nconst lightDivider = \"❇️───────❇️───────❇️\";\nconst heavyDivider = \"&lrm;\\n&lrm;\";\n\nconst isValid = (value) => String(value).length > 1;\nlet ctitle =  book.durChapterTitle || '未开始';\n\nconst formatAbstract = (text) => {\n    return text.split(\"\\n\").map(line => `    ${line}`).join(\"\\n\");\n};\n\nconst buildBasicInfo = (items) => {\n    let info = \"\";\n    for (let [value, prefix, icon] of items) {\n        if (isValid(value)) {\n            info += `    ${icon} ${prefix} ${value}\\n`;\n        }\n    }\n    return info;\n};\n\nlet info = `\n    📡 当前服务:${BaseUrl()}\n    🔑 ${loginStatus}\n    🏷 数据来源:${sources}\n    🔄 当前模式:${tab}\n    ⚙️ 访问模式:${proxy}\n    📖 阅读至:${ctitle}\n`;\n\nif (tab == \"听书\") {\n    let toneId = String(book.getVariable('custom')) || '4';\n    if (isValid(book_tts)) {\n        info += `${lightDivider}\n    🎵 音色配置:${toneId}\n    ✨ AI音色请点击右上角书籍变量填写相关值,真人听书请重新搜索选择带有主播的书籍\n    ${book_tts}\n`;\n    }\n}\n\nconst fullBasicInfo = buildBasicInfo([\n    [tags, \"书籍分类:\", \"🌈\"],\n    [role, \"书籍主角:\", \"👑\"],\n    [last_chapter_title, \"最新章节:\", \"📚\"],\n    [last_chapter_update_time, \"更新时间:\", \"⏳\"],\n    [word_number, \"书籍字数:\", \"📝\"],\n    [status, \"书籍状态:\", \"🚩\"],\n    [score, \"书籍评分:\", \"⭐\"],\n    [read_count, \"今阅读人数:\", \"📃\"],\n    [read_count_all, \"总阅读人数:\", \"📈\"]\n]);\n\nif (fullBasicInfo) {\n    info += `${lightDivider}\\n${fullBasicInfo}`;\n}\n\nif (isValid(abstract)) {\n    info += `${heavyDivider}\n    📖 书籍简介:\n${formatAbstract(abstract)}\n`;\n} else {\n    info += heavyDivider;\n}\n\nif (isValid(copyright_info)) {\n    info += `${lightDivider}\n    © ${copyright_info}\n`;\n} else {\n    info += lightDivider;\n}\n\ninfo += `\n${heavyDivider}\n    数据更新于 ${new Date().toLocaleString()}\n`;\n\nconst abstractMode = moreSettings && moreSettings['完整简介'] || 'true';\nif (abstractMode == \"false\") {\n    const simplifiedBasicInfo = buildBasicInfo([\n        [last_chapter_title, \"最新章节:\", \"📚\"],\n        [last_chapter_update_time, \"更新时间:\", \"⏳\"],\n        [word_number, \"书籍字数:\", \"📊\"],\n        [status, \"书籍状态:\", \"🚩\"],\n        [score, \"书籍评分:\", \"⭐\"]\n    ]);\n    \n    info = `&lrm;\\n🏷 数据来源:${sources}\\n${simplifiedBasicInfo}${isValid(abstract) ? `\\n${heavyDivider}\\n    📖 书籍简介:\\n${formatAbstract(abstract)}` : heavyDivider}`;\n} else {\n    info = info.split(\"\\n\").map(line => line.replace(\/^ {4}\/, \"\")).join(\"\\n\");\n}\n<\/js>",
        "lastChapter": "{{$.source}} {{$.last_chapter_title}} {{$.last_chapter_update_time}}",
        "name": "$.book_name",
        "tocUrl": "<js>\nlet book_id = result.book_id;\nlet sources = result.source;\nlet book_name = result.book_name;\nlet author = result.author;\nlet abstract = result.abstract;\nlet thumb_url = result.thumb_url;\nlet tab = result.tab || getVariable('类型');\nlet url = result.toc_url || \"\";\nlet gycatalog = {\n\t  book_name,\n\t  author,\n\t  abstract,\n\t  thumb_url,\n    book_id,\n    sources,\n    tab,\n    url,\n};\ngycatalog = java.base64Encode(JSON.stringify(gycatalog));\n`data:;base64,${gycatalog},{\"type\":\"gycatalog\"}`;\n<\/js>",
        "wordCount": "$.word_number"
    },
    "ruleContent": {
        "content": "<js>\nlet DEFAULT_TONE_ID = '4';\nlet CONTENT_URL = '\/content';\nlet CONTENT_URL_WITH_REVIEW = '\/content?review=1';\nlet VERSION = localVersion;\nlet SOURCES_WITH_REVIEW = ['番茄', '七猫', '塔读', 'QQ阅读', 'svip_QQ阅读'];\nlet SOURCES_VIDEO = ['毒舌影视', 'NT动漫'];\nlet moreSettings = getVariable('更多设置');\nconst proxy  = moreSettings && moreSettings['网络模式'] || '服务器';\nconst reading = moreSettings && moreSettings['同步书架'] || 'false';\nconst reading_toast = moreSettings && moreSettings['同步弹窗'] || 'true';\n\nresult = String(java.hexDecodeToString(result));\nlet res = JSON.parse(result);\nlet { book_id, item_id, tab, title, sources, url } = res;\n\nlet islyc = checkEnv();\ntry {\n    book.imageStyle = islyc == '改版' ? 'FULL' : 'TEXT';\n} catch (e) {\n    \n}\n\nconst syncBookShelf = (book) => {\n    if (!book) {\n        return;\n    }\n    let book_order = book.order;\n    if (book_order!=0) {\n    \t   book_order = 1;\n    \t} else {\n    \t\tbook_order = 2;\n    \t\t}\n    \n    if (reading != \"true\") {\n        return;\n    }\n\n    try {\n        const ritem = item_id;\n        \nlet book_info = {\n            book_name:book.name,\n            author:book.author,\n            \/\/abstract:book.abstract,\n            thumb_url:book.coverUrl,\n            book_id,\n            tab,\n            source: sources\n        };\n        if (!book_info || typeof book_info != \"object\") {\n            return;\n        }\n        const rurl = `\/add_book_to_book_shelf`;\n        book_info.read_status = book_order;\n        book_info.last_chapter_item_id = item_id;\n        book_info.last_chapter_title = chapter.title || \"\";\n        \/\/java.toast(JSON.stringify(book_info));\n       \ttry {\n       \t\tconst check_book_url = `\/check_book_in_book_shelf`;\n    checkResponse = request(check_book_url, \"POST\", {book_id,source:sources,tab});\n} catch (e) {\n    java.log(`检查书籍请求失败: ${e}`);\n}\n    \n        const check_data = parseJsonSafely(checkResponse);\n        try {\n            if (check_data.data.id) {\n                book_info.id = check_data.data.id;\n                const uurl = `\/update_book_shelf`;\n                let up = request(uurl, \"POST\", book_info);\n                if (reading_toast == \"true\") {\n                \t    java.toast(up)\n                \t}\n                \n            } else {\n                let up = request(rurl, \"POST\", book_info);\n                if (reading_toast == \"true\") {\n                \t    java.toast(up)\n                \t}\n            }\n        } catch (e) {\n            try {\n                let up = request(rurl, \"POST\", book_info);\n                java.toast(up)\n            } catch (e) {\n                if (reading_toast == \"true\") {\n                \t    java.toast(`书架操作失败: ${e}`);\n                \t}\n            }\n\n        }\n    } catch (error) {\n        java.log(`书籍同步流程异常: ${error}`);\n        \n        if (reading_toast == \"true\") {\n                \t    java.longToast(\"\\n同步阅读进度失败,但不影响阅读,可以前往登录关闭书架同步功能。\");\n                \t}\n    }\n};\n\n\/\/java.log(book)\n\n\nif (book.durChapterIndex == chapter.index) {\n\ttry {syncBookShelf(book)} catch {}\n\t}\n\n\n\n\nlet varia1 = String(book.getVariable('custom')) || '';\nlet varia = JSON.stringify({ custom: varia1 });\n\n\nlet html = '';\n\nif (url && proxy === '本地') {\n    if (sources === '69书吧') {\n        let ck69 = String(cookie.getCookie('https:\/\/www.69shuba.com'));\n        let headers = {\n            Referer: url,\n            Cookie: ck69,\n            'User-Agent': java.getWebViewUA()\n        };\n        let op = JSON.stringify({ headers });\n        html = java.ajax(`${url},${op}`);\n    } else {\n        html = java.ajax(url);\n    }\n    \n    if (html.includes('Just a moment...') && sources == '69书吧' && book.durChapterIndex == chapter.index) {\n        cookie.removeCookie('https:\/\/www.69shuba.com');\n        java.longToast('需要真人验证,请进入任意书籍详情页过验证');\n        html = java.startBrowserAwait(url, '需要真人验证,请进入任意书籍详情页过验证').body();\n    }\n}\n\nlet tone_id = varia1 || DEFAULT_TONE_ID;\nlet base_url = BaseUrl();\nlet dpSettings = getVariable('段评设置');\nlet para = dpSettings && dpSettings['段评开关'] || 'true';\nlet show_img = moreSettings && moreSettings['显示图片'] || 'true';\n\nlet params = {\n    html,\n    item_id,\n    source: sources,\n    tab,\n    tone_id,\n    variable: varia,\n    version: VERSION\n};\n\nlet hasReview = SOURCES_WITH_REVIEW.includes(sources) && para==\"true\" && tab == '小说';\nlet content_url = hasReview ? CONTENT_URL_WITH_REVIEW : CONTENT_URL;\n\nlet data = request(content_url, 'POST', params);\ntry {\n    data = JSON.parse(data);\n    if (data.msg) {\n        java.toast(data.msg);\n    }\n} catch (e) {\n    \/\/console.error('Parse content data failed:', e);\n}\n\nlet content = data.content || '';\n\nif (show_img == 'false') {\n    content = removeAllImgTags(content);\n}\n\nif (hasReview) {\n    let fqssionid = java.getCookie('fanqienovel.com', 'sessionid');\n    content = content\n        .replace(\/ident=\"\/g, `ident=\"${base_url}`)\n        .replace(\/book_id=\/g, `book_id=${book_id}&ssionid=${fqssionid}`);\n    let deviceType = '安卓';\n    try {\n    \tjava.deviceID();\n    \tdeviceType = '苹果'\n    \t} catch {}\n    content = deviceType == '苹果' \n        ? paraForiOS(content, sources) \n        : paraForAndroid(content, sources);\n}\n\ndata = JSON.stringify({ content });\n\nlet isVideoSource = SOURCES_VIDEO.some(src => sources.includes(src));\nlet isVideoTab = tab == '短剧' || tab == '视频';\nif (islyc != '苹果' && (isVideoTab || isVideoSource)) {\n    data = JSON.stringify({\n        content: `【右上角刷新】开启播放(下一集请切换下一章刷新)\\n播放直链:\\n${content}`\n    });\n    \n    if (book.durChapterIndex == chapter.index) {\n        let video_url = `${base_url}\/online_video?book_id=${book_id}&source=${sources}&tab=${tab}`;\n        if (isVideoSource) {\n            video_url = content;\n        }\n        \/\/java.openVideoPlayer(content, title)\n        java.startBrowser(video_url, title);\n        java.toast('正在加载视频...');\n    }\n}\n\ndata;\n<\/js>$.content",
        "title": "@js:\nresult = String(java.hexDecodeToString(result))\nJSON.parse(result)['title'].replace(JSON.parse(result)['sources']+':','')"
    },
    "ruleExplore": {
        "author": "$.author",
        "bookList": "$.data",
        "bookUrl": "<js>\nlet book_id = result.book_id;\nlet sources = result.source;\nlet tab = result.tab || '小说';\nlet url = result.toc_url || '';\n\nlet gydetail = {\n    book_id: book_id,\n    sources: sources,\n    tab: tab,\n    url: url\n}\ngydetail = java.base64Encode(JSON.stringify(gydetail));\n`data:;base64,${gydetail},{\"type\":\"gydetail\"}`\n<\/js>",
        "coverUrl": "$.thumb_url",
        "intro": "$.abstract",
        "kind": "{{$.status}},{{$.score}},{{$.tags}},{{$.last_chapter_update_time}}",
        "lastChapter": "{{$.source}} {{$.last_chapter_title}}",
        "name": "$.book_name##(别名:.*?)",
        "wordCount": "$.word_number"
    },
    "ruleSearch": {
        "author": "$.author",
        "bookList": "<js>\nconst res = JSON.parse(java.hexDecodeToString(result));\nconst {\n    key,\n    tab,\n    sourcesKey,\n    page,\n    disabled_sources\n} = res;\nlet url = `\/search?title=${key}&tab=${tab}&source=${sourcesKey}&page=${page}&disabled_sources=${disabled_sources}`;\nrequest(url);\n<\/js>\n$.data",
        "bookUrl": "<js>\nlet book_id = result.book_id;\nlet sources = result.source;\nlet tab = result.tab || '小说';\nlet url = result.toc_url || '';\n\nlet gydetail = {\n    book_id: book_id,\n    sources: sources,\n    tab: tab,\n    url: url\n}\ngydetail = java.base64Encode(JSON.stringify(gydetail));\n`data:;base64,${gydetail},{\"type\":\"gydetail\"}`\n<\/js>",
        "checkKeyWord": "我的@番茄",
        "coverUrl": "$.thumb_url",
        "intro": "$.abstract",
        "kind": "{{$.status}},{{$.score}},{{$.tags}},{{$.last_chapter_update_time}}",
        "lastChapter": "{{$.source}} {{$.last_chapter_title}}",
        "name": "$.book_name##(别名:.*?)",
        "wordCount": "$.word_number"
    },
    "ruleToc": {
        "chapterList": "<js>\nconst res = JSON.parse(java.hexDecodeToString(result));\nconst {\n\tbook_name,\n\tauthor,\n\tabstract,\n\tthumb_url,\n    book_id,\n    tab,\n    sources,\n    url\n} = res;\nlet durChapterIndex = book.durChapterIndex;\njava.put('book_id', book_id);\nlet html = \"\";\nlet moreSettings = getVariable('更多设置');\nconst proxy = moreSettings && moreSettings['网络模式'] || '服务器';\nconst showSource = moreSettings && moreSettings['目录显示来源'] || 'true';\nconst gytoken = getToken();\nconst reading = moreSettings && moreSettings['同步书架'] || 'false';\nconst reading_toast = moreSettings && moreSettings['同步弹窗'] || 'true';\n\nif (url && proxy == \"本地\") {\n    if (sources == \"69书吧\") {\n        const ck69 = String(cookie.getCookie(\"https:\/\/www.69shuba.com\"));\n        const headers = {\n            \"Referer\": url,\n            \"Cookie\": ck69,\n            \"User-Agent\": java.getWebViewUA()\n        };\n        const op = JSON.stringify({\n            headers\n        });\n        html = java.ajax(`${url},${op}`);\n    } else {\n        html = java.ajax(url);\n    }\n\n    if (html.includes(\"Just a moment...\") && sources === \"69书吧\") {\n        cookie.removeCookie(\"https:\/\/www.69shuba.com\");\n        java.longToast(\"需要真人验证,请进入任意书籍详情页过验证\");\n        html = java.startBrowserAwait(url, \"需要真人验证,请进入任意书籍详情页过验证\").body();\n        java.log(html);\n    }\n}\n\n\nconst customVar = String(book.getVariable(\"custom\"));\nconst varia = JSON.stringify({\n    custom: customVar == \"null\" ? \"\" : customVar\n});\n\nconst queryString = `book_id=${encodeURIComponent(book_id)}&source=${encodeURIComponent(sources)}&tab=${encodeURIComponent(tab)}&variable=${encodeURIComponent(varia)}`;\nconst data = request(`\/catalog?${queryString}`, \"POST\", {\n    html\n});\n\nconst device_type = checkEnv();\nconst typeMap = {\n    \"小说\": {\n        \"苹果\": 0,\n        default: 8\n    },\n    \"听书\": {\n        \"苹果\": 1,\n        default: 32\n    },\n    \"漫画\": {\n        \"苹果\": 2,\n        default: 64\n    },\n    \"短剧\": {\n        \"苹果\": 3,\n        \"备用\": 4,\n        default: 8\n    }\n};\nconst currentType = typeMap[tab] || typeMap[\"小说\"];\nbook.type = currentType[device_type] || currentType.default;\n\n\n\nconst syncBookShelf = (data, book, gytoken) => {\n    if (!book) {\n        return;\n    }\n    let book_order = book.order;\n    if (book_order!=0) {\n    \t   book_order = 1;\n    \t} else {\n    \t\tbook_order = 2;\n    \t\t}\n    if (reading != \"true\") {\n        return;\n    }\n\n    try {\n        if (!data || !gytoken) {\n            java.log(\"缺少必要参数\");\n            return;\n        }\n\n        const parsedData = parseJsonSafely(data);\n        if (!parsedData || !parsedData.data || !Array.isArray(parsedData.data) || !parsedData.data[durChapterIndex]) {\n            return;\n        }\n\n        const ritem = parsedData.data[durChapterIndex];\n        \nlet book_info = {\n            book_name,\n            author,\n            abstract,\n            thumb_url,\n            book_id,\n            tab,\n            source: sources\n        };\n        if (!book_info || typeof book_info != \"object\") {\n            return;\n        }\n        const rurl = `\/add_book_to_book_shelf`;\n        book_info.read_status = book_order;\n        book_info.last_chapter_item_id = ritem.item_id || \"\";\n        book_info.last_chapter_title = ritem.title || \"\";\n        \/\/java.toast(JSON.stringify(book_info));\n       \ttry {\n       \t\tconst check_book_url = `\/check_book_in_book_shelf`;\n    checkResponse = request(check_book_url, \"POST\", {book_id,source:sources,tab});\n} catch (e) {\n    java.log(`检查书籍请求失败: ${e}`);\n}\n    \n        const check_data = parseJsonSafely(checkResponse);\n        try {\n            let chapterIndex = findItemIndex(parsedData.data,check_data.data.last_chapter_item_id)\n            durChapterIndex = chapterIndex;\n            if (book.durChapterIndex < chapterIndex) {\n            \tbook_info.last_chapter_item_id = check_data.data.last_chapter_item_id;\n            \t}\n            \n            if (chapterIndex != 0 && book.durChapterIndex < chapterIndex) {\n                book.durChapterIndex = chapterIndex;\n            }\n            if (check_data.data.id) {\n                book_info.id = check_data.data.id;\n                const uurl = `\/update_book_shelf`;\n                if (book_order==1){\n                \tlet up = request(uurl, \"POST\", book_info);\n                   if (reading_toast == \"true\") {\n                \t    java.toast(up)\n                \t  }\n                \t}\n                \n            } else {\n                if (book_order==1){\n                let up = request(rurl, \"POST\", book_info);\n                if (reading_toast == \"true\") {\n                \t    java.toast(up)\n                \t}\n            }}\n        } catch (e) {\n            try {\n                if (book_order==1){\n                let up = request(rurl, \"POST\", book_info);\n                if (reading_toast == \"true\") {\n                \t    java.toast(up)\n                \t}}\n            } catch (e) {\n                if (reading_toast == \"true\") {\n                \t    java.toast(`书架操作失败: ${e}`);\n                \t}\n            }\n\n        }\n    } catch (error) {\n        java.log(`书籍同步流程异常: ${error}`);\n        if (reading_toast == \"true\") {\n                \t    java.longToast(\"\\n同步阅读进度失败,但不影响阅读,可以前往登录关闭书架同步功能。\");\n                \t}\n        \n    }\n};\n\ntry {syncBookShelf(data, book, gytoken);}catch{}\n\n\nfunction processCatalogList(data) {\n    let catalog_list = parseJsonSafely(data).data;\n\n\n    if (catalog_list && catalog_list.length > 0) {\n\n        let lastItem = catalog_list[catalog_list.length - 1];\n\n\n        if (lastItem && typeof lastItem == 'object' && lastItem.source && lastItem.title) {\n\n            lastItem.title = lastItem.source + ':' + lastItem.title;\n        }\n    }\n    return JSON.stringify({\n        'data': catalog_list\n    });\n}\n\nif (showSource == \"true\") {\n    processCatalogList(data);\n} else {\n    data;\n}\n\n<\/js>$.data",
        "chapterName": "$.title",
        "chapterUrl": "<js>\nlet { tab, source: sources, title, item_id } = result;\nlet book_id = java.get('book_id');\nlet base_url = BaseUrl();\nlet url = result.toc_url || \"\";\n\nlet gycontent = { book_id, item_id, title, sources, tab, url };\nlet encodedGycontent = java.base64Encode(JSON.stringify(gycontent));\n\nlet content_url;\n\nif (sources == '卷') {\n    content_url = item_id;\n} else if (['番茄', '七猫', '塔读'].includes(sources) && tab == \"小说\") {\n    const fqssionid = java.getCookie('fanqienovel.com', 'sessionid');\n    const sourcess = sources.replace('svip_', '');\n    content_url = `data:;base64,${encodedGycontent},${JSON.stringify({\n        type: \"gycontent\",\n        js: `book ? result : '${base_url}\/get_review?book_id=${book_id}&item_id=${item_id}&ssionid=${fqssionid}&source=${sourcess}'`\n    })}`;\n} else {\n    content_url = `data:;base64,${encodedGycontent},${JSON.stringify({ type: \"gycontent\" })}`;\n}\n<\/js>",
        "updateTime": "$.first_pass_time"
    },
    "searchUrl": "<js>\nlet moreSettings = getVariable('更多设置');\n\nlet tab = moreSettings && moreSettings['搜索模式'] || '小说';\n\nlet sourcesKey = moreSettings && moreSettings[tab] || '全部';\n\nlet disabled_sources = moreSettings && moreSettings['强制搜索'] || \"0\";\nif (disabled_sources == \"false\") {\n    disabled_sources = \"0\";\n} else if (disabled_sources == \"true\") {\n    disabled_sources = \"1\";\n}\nlet gysearch = {\n    key: key,\n    tab: tab,\n    sourcesKey: sourcesKey,\n    page: page,\n    disabled_sources:disabled_sources\n}\ngysearch = java.base64Encode(JSON.stringify(gysearch));\n`data:;base64,${gysearch},{\"type\":\"gysearch\"}`;\n<\/js>",
    "weight": 0
}
广告