订阅源管理器

订阅源管理器

分享者: guaner001125 (317)发布时间: 6天前

仅支持洛雅橙改版阅读最新测试版3.26.022606及以上版本
https://github.com/Luoyacheng/legado

不知道干嘛,做着玩的,可以配合书源打开这个订阅源管理器,这样隐藏订阅也可以打开订阅了,初次使用要配合web服务拉取。
二维码导入
{
    "articleStyle": 0,
    "cacheFirst": false,
    "customOrder": 34,
    "enableJs": true,
    "enabled": true,
    "enabledCookieJar": false,
    "lastUpdateTime": 1772158306402,
    "loadWithBaseUrl": true,
    "preload": false,
    "preloadJs": "window.java = java;\nwindow.ajaxAwait = ajaxAwait;\nwindow.source = source;\nwindow.cache = cache;",
    "showWebLog": true,
    "singleUrl": false,
    "sourceComment": "仅支持洛雅橙改版阅读最新测试版3.26.022606及以上版本\nhttps:\/\/github.com\/Luoyacheng\/legado\n\n不知道干嘛,做着玩的,可以配合书源打开这个订阅源管理器,这样隐藏订阅也可以打开订阅了,初次使用要配合web服务拉取。",
    "sourceIcon": "https:\/\/cdn.jsdelivr.net\/gh\/gedoor\/legado@master\/app\/src\/main\/res\/mipmap-hdpi\/ic_launcher.png",
    "sourceName": "订阅源管理器",
    "sourceUrl": "订阅源管理器",
    "startHtml": "<div class=\"modal-overlay\" id=\"modalOverlay\"><\/div>\n    <div class=\"color-modal\" id=\"colorModal\">\n       \n        <div class=\"modal-header\">\n            <h2 class=\"modal-title\">颜色设置<\/h2>\n            <button class=\"close-btn\" id=\"closeModalBtn\">×<\/button>\n        <\/div>\n        <div class=\"modal-content\">\n            <div class=\"color-options\">                                           \n                <div class=\"color-option\">\n                    <div class=\"color-label\">\n                        <span class=\"color-name\">\n                            <div class=\"color-preview\" id=\"backgroundColorPreview\" style=\"background-color: #ffffff\"><\/div>\n                            <span>背景颜色<\/span>\n                        <\/span>\n                        <span class=\"color-value\" id=\"backgroundColorValue\" contenteditable=\"true\">#ffffff<\/span>\n                    <\/div>\n                    <input type=\"color\" class=\"color-input\" id=\"backgroundColorInput\" value=\"#ffffff\">\n                <\/div>        \n                <div class=\"color-option\">\n                    <div class=\"color-label\">\n                        <span class=\"color-name\">\n                            <div class=\"color-preview\" id=\"accentColorPreview\" style=\"background-color: #007aff\"><\/div>\n                            <span>强调色<\/span>\n                        <\/span>\n                        <span class=\"color-value\" id=\"accentColorValue\" contenteditable=\"true\">#007aff<\/span>\n                    <\/div>\n                    <input type=\"color\" class=\"color-input\" id=\"accentColorInput\" value=\"#007aff\">\n                <\/div>          \n                <div class=\"color-option\">\n                    <div class=\"color-label\">\n                        <span class=\"color-name\">\n                            <div class=\"color-preview\" id=\"iconBgColorPreview\" style=\"background-color: #f2f2f7\"><\/div>\n                            <span>图标背景色<\/span>\n                        <\/span>\n                        <span class=\"color-value\" id=\"iconBgColorValue\" contenteditable=\"true\">#f2f2f7<\/span>\n                    <\/div>\n                    <input type=\"color\" class=\"color-input\" id=\"iconBgColorInput\" value=\"#f2f2f7\">\n                <\/div>              \n                <div class=\"color-option\">\n                    <div class=\"color-label\">\n                        <span class=\"color-name\">\n                            <div class=\"color-preview\" id=\"iconTextColorPreview\" style=\"background-color: #1d1d1f\"><\/div>\n                            <span>图标文字色<\/span>\n                        <\/span>\n                        <span class=\"color-value\" id=\"iconTextColorValue\" contenteditable=\"true\">#1d1d1f<\/span>\n                    <\/div>\n                    <input type=\"color\" class=\"color-input\" id=\"iconTextColorInput\" value=\"#1d1d1f\">\n                <\/div>              \n                <div class=\"color-option\">\n                    <div class=\"color-label\">\n                        <span class=\"color-name\">\n                            <div class=\"color-preview\" id=\"topColorPreview\" style=\"background-color: #007aff\"><\/div>\n                            <span>顶部颜色<\/span>\n                        <\/span>\n                        <span class=\"color-value\" id=\"topColorValue\" contenteditable=\"true\">#007aff<\/span>\n                    <\/div>\n                    <input type=\"color\" class=\"color-input\" id=\"topColorInput\" value=\"#007aff\">\n                <\/div>\n            <\/div>            \n        <\/div>\n\n        <div class=\"modal-footer\">\n            <button class=\"action-btn reset-btn\" id=\"resetBtn\">重置<\/button>\n            <button class=\"action-btn apply-btn\" id=\"applyBtn\">应用<\/button>\n            <button class=\"action-btn c-btn no\" id=\"cBtn\">关闭<\/button>\n        <\/div>\n    <\/div>\n<div id=\"bottomMenu\">\n       <div style=\"display:flex;justify-content:center;align-items:center;\">\n            <input type=\"checkbox\" style=\"accent-color: var(--active-color);\">\n            <p style=\"margin-left:5px\">全选 (共选择<span>0<\/span>个)<\/p>\n       <\/div>\n       <button class=\"del\">删除<\/button>\n        <button class=\"ok\">分组<\/button>\n<\/div>\n\n<div id=\"popupMenu\">     \n    <div class=\"popup-item getrssbtn\">\n        <span style=\"width:20px;text-align:center;\">📥<\/span>\n        <span>拉取订阅<\/span>\n    <\/div>\n    \n    <div style=\"height:1px;background:#e5e7eb;\"><\/div>    \n    <div class=\"popup-item setwebbtn\">\n        <span style=\"width:20px;text-align:center;\">🌐<\/span>\n        <span>编辑网站<\/span>\n    <\/div>\n    \n    <div style=\"height:1px;background:#e5e7eb;\"><\/div>   \n    <div class=\"popup-item setgroupbtn\">\n            <span style=\"width:20px;text-align:center;\">📁<\/span>\n        <span>编辑分组<\/span>\n    <\/div>\n    \n    <div style=\"height:1px;background:#e5e7eb;\"><\/div>\n    \n    <div class=\"popup-item setthemebtn\">\n        <span style=\"width:20px;text-align:center;\">🎨<\/span>\n        <span>设置主题<\/span>\n    <\/div> \n    \n    <div style=\"height:1px;background:#e5e7eb;\"><\/div>\n    <div class=\"popup-item pushrssbtn\">\n        <span style=\"width:20px;text-align:center;\">📤<\/span>\n        <span>同步订阅<\/span>\n    <\/div>\n<\/div>\n\n\n<div class=\"header-box\">\n<div class=\"search-box\">\n  <input type=\"text\" class=\"input\" placeholder=\"筛选当前分组订阅源\">\n<\/div>\n\n<div class=\"tab-box\">\n    <div class=\"groups-box\"><\/div>\n    <div class=\"add-group\">⚙️<\/div>\n<\/div>\n<\/div>\n\n\n<div class=\"webs-c\">\n<div class=\"webs-box\">\n<\/div>\n<\/div>\n\n\n<div class=\"editgroup-m m hide\">\n<div class=\"editgroup-box box\">\n    <div class=\"editgroup-title title\">\n          <h4>编辑分组<\/h4>\n    <\/div>\n          <div class=\"editgroup-items items\"><\/div>\n     <div class=\"editgroup-btn btn\">\n         <button class=\"editgroup-add\">新增<\/button>\n         <button class=\"editgroup-ok ok\">确认<\/button>\n         <button class=\"editgroup-no no\">取消<\/button>\n     <\/div>\n<\/div>\n<\/div>\n\n\n<div class=\"editweb-m m hide\">\n<div class=\"editweb-box box\">\n      <div class=\"editweb-title title\">\n          <h4>编辑订阅源<\/h4>\n      <\/div>\n      \n      <div class=\"editweb\">\n      <div class=\"editweb-group\">\n          <p>分组<\/p>\n          <select class=\"input\" multiple>\n          <\/select>\n      <\/div>\n      \n      <div class=\"editweb-name\">\n          <p>网站名称<\/p>\n          <input class=\"input\">\n      <\/div>\n      \n      <div class=\"editweb-url\">\n          <p>源URL(唯一标识)<\/p>\n          <input class=\"input\" disabled readonly style=\"color:#888\">\n      <\/div>\n      \n      <div class=\"editweb-icon\">\n          <p>图标地址(可填一个文字或图片地址)<\/p>\n          <input class=\"input\">\n      <\/div>\n      <\/div>\n      \n      <div class=\"editweb-btn btn\">         \n         <button class=\"editweb-ok ok\">保存<\/button>\n         <button class=\"editweb-no no\">取消<\/button>\n     <\/div>\n<\/div>\n<\/div>\n\n\n<div class=\"getrss-m m hide\">\n    <div class=\"getrss-box box\">\n        <div class=\"getrss-title title\">\n            <h4>拉取订阅源<\/h4>\n        <\/div>\n      <input class=\"input rssurl\" type=\"text\" placeholder=\"请打开web服务,输入web服务的网址\">\n      <div class=\"editweb-btn btn\">         \n         <button class=\"editweb-ok ok\">确认<\/button>\n         <button class=\"editweb-no no\">取消<\/button>\n     <\/div>      \n    <\/div>\n<\/div>\n\n\n\n<div class=\"pushrss-m m hide\">\n    <div class=\"pushrss-box box\">\n        <div class=\"pushrss-title title\">\n            <h4>同步订阅源<\/h4>\n        <\/div>\n         <div class=\"pushrss-info items hide\"><\/div>\n           \n        <div class=\"pushrss-edit\">\n      <input class=\"input rssurl\" type=\"text\" placeholder=\"请打开web服务,输入web服务的网址\">\n      <p><span style=\"color:red;font-weight:bold\">注意<\/span>:此功能会修改阅读里的订阅源信息【包括分组,名称,图标地址】,根据源URL定位<\/p>\n      <p><span style=\"color:var(--active-color);font-weight:bold\">完整同步<\/span>:这里没有的订阅源,阅读的订阅源会<span style=\"color:red;font-weight:bold\">同步删除<\/span>掉(谨慎操作)<\/p>\n      <p><span style=\"color:var(--active-color);font-weight:bold\">排序同步<\/span>:会将全部里的订阅源顺序同步到阅读的订阅源,不更改其他信息<\/p>\n      <p><span style=\"color:var(--active-color);font-weight:bold\">标准同步<\/span>:只会同步信息,<span style=\"color:red;font-weight:bold\">不会删除<\/span>阅读里的订阅源<\/p>\n      <div class=\"pushrss-btn btn\">    \n        <button class=\"pushrss-ok_1 ok_1\">完整<\/button>\n         <button class=\"pushrss-order\" style=\"background:green;color:#fff\">排序<\/button>     \n        \n         <button class=\"pushrss-ok ok\">标准<\/button>\n         \n     <\/div>      \n     <\/div>\n    <\/div>\n<\/div>\n\n\n<div class=\"rsslist-m m hide\">\n    <div class=\"rsslist-box box\">\n        <div class=\"getrss-title title\">\n            <h4>添加订阅源<\/h4>\n        <\/div>\n       \n       <div class=\"rsslist-input\"> \n          <div class=\"rsslist-check\"> \n              <input type=\"checkbox\" name=\"all\">\n              <label for=\"all\" style=\"margin-left:5px;font-size:12px\">全选<\/label>\n          <\/div>\n          \n           <div class=\"rsslist-search\"> \n              <input class=\"input\" type=\"text\" placeholder=\"筛选订阅源\">             \n          <\/div>\n          \n      <\/div>\n      \n      <div class=\"rsslist-items items\">\n      <\/div>\n      \n      <div class=\"rsslist-btn btn\">         \n         <button class=\"rsslist-ok ok\">确认<\/button>\n         <button class=\"rsslist-no no\">取消<\/button>\n     <\/div>      \n    <\/div>\n<\/div>",
    "startJs": "cache.put(\"rssset\",1,300);\neval(String(java.importScript(\"https:\/\/cdn.jsdelivr.net\/npm\/sortablejs@latest\/Sortable.min.js\")));\n\nlet rssData,getRssData,groups,webs;\nlet isSwiping = false;\nlet sortableInstance = null;\nlet swipeStartX = 0;\nlet scrollTimer = null;\nlet popupVisible = false;\nlet isEditWeb = false;\n\nconst getrss_m = document.querySelector(\".getrss-m\");\nconst pushrss_m = document.querySelector(\".pushrss-m\");\nconst rsslist_m = document.querySelector(\".rsslist-m\");\nconst webs_box = document.querySelector(\".webs-box\");\nconst editgroup_m = document.querySelector(\".editgroup-m\");\nconst editweb_m = document.querySelector(\".editweb-m\");\nconst popupMenu =  document.querySelector(\"#popupMenu\");\nconst bottomMenu =  document.querySelector(\"#bottomMenu\");\nconst colorModal = document.querySelector(\"#colorModal\");\n\nupGroups();\ncheckMinusTwo();\nlet rssUrl = getRssData.rssUrl ?? \"\";\n\ncloseM(getrss_m);\ncloseM(rsslist_m);\ncloseM(editgroup_m);\ncloseM(editweb_m);\ncloseM(pushrss_m);\n\n\nnew Sortable(document.querySelector('.editgroup-items'), {\n    animation: 150,\n    fallbackOnBody: false,   \n    fallbackClass: \"sortable-fallback\",\n    fallbackTolerance: 0,   \n    ghostClass: \"sortable-ghost\",\n    dragClass: \"sortable-drag\",\n    chosenClass: \"sortable-chosen\",\n    dataIdAttr: 'data-id'\n});\n\n\nif(!rssUrl || !getRssData.webs){\n\tjava.longToast(\"请先拉取rss源\");\n\tgetRssSources(rssUrl);\n}\n\n\ndocument.querySelector(\".pushrssbtn\").addEventListener(\"click\", function(event) {\n    pushrss_m.classList.remove(\"hide\");\n    document.querySelectorAll(\".rssurl\")[1].value = rssUrl;\n    document.querySelector(\".pushrss-info\").classList.add(\"hide\");\n    document.querySelector(\".pushrss-edit\").classList.remove(\"hide\");\n    document.querySelector(\".pushrss-info\").innerHTML = \"\";\n    \n})\n\npushrss_m.addEventListener(\"click\", function(event) {\n    if(event.target.closest(\".ok\")){\n        rssUrl = document.querySelectorAll(\".rssurl\")[1].value;\n         if(!\/^http\/.test(rssUrl)){\n             java.toast(\"请输入正确的web服务地址\");\n             return\n         }\n         getRssData.rssUrl = rssUrl;\n         source.putVariable(JSON.stringify(getRssData));\n         pushRss(rssUrl)\n    }else if(event.target.closest(\".ok_1\")){\n        rssUrl = document.querySelectorAll(\".rssurl\")[1].value;\n         if(!\/^http\/.test(rssUrl)){\n             java.toast(\"请输入正确的web服务地址\");\n             return\n         }\n         getRssData.rssUrl = rssUrl;\n         source.putVariable(JSON.stringify(getRssData));\n         pushRss(rssUrl,1)\n    }else if(event.target.closest(\".pushrss-order\")){\n        rssUrl = document.querySelectorAll(\".rssurl\")[1].value;\n         if(!\/^http\/.test(rssUrl)){\n             java.toast(\"请输入正确的web服务地址\");\n             return\n         }\n         getRssData.rssUrl = rssUrl;\n         source.putVariable(JSON.stringify(getRssData));\n         pushRss(rssUrl,2)\n    }\n})\n\n\nfunction createPushRss(x,oname,ogroup,f,color){\n    let msgBox =  document.createElement('div');\n    msgBox.className = \"msg-item\";\n    msgBox.innerHTML = `\n                       <div class=\"img\">\n                                  ${\/^http|data:\/.test(x.sourceIcon)?\"<img src=\\\"\"+x.sourceIcon+\"\\\">\":x.sourceName.match(\/[\\u4e00-\\u9fa5a-zA-Z.]\/)[0] }\n                         <\/div>\n                     <div class=\"msg-name\">\n                             <p class=\"name\">\n                             ${oname===x.sourceName?\"\":\"<span>\"+oname+ \" => <\/span>\"}\n                             ${x.sourceName}\n                             <span style=\"color:${color};float:right\">&emsp;${f}<\/span>\n                             <span style=\"color:black;float:right\">\n                             ${ogroup===x.sourceGroup?\"\":\"<span>\"+(ogroup?ogroup:\"未分组\")+ \" => <\/span>\"}\n                             ${x.sourceGroup?\"\"+x.sourceGroup+\"\":\"未分组\"}\n                             <\/span>\n                             <\/p>\n                      <p class=\"rssurl\">${x.sourceUrl}<\/p>\n                <\/div>\n                       `;\n                       if(f===\"已同步\" || f === \"已删除\")\ndocument.querySelector('.pushrss-info').insertAdjacentElement('afterbegin', msgBox);\n                       if(f===\"未同步\")\n         document.querySelector(\".pushrss-info\").appendChild(msgBox)\n}\n\n\nfunction pushRss(rssUrl,f){\n    let getRssUrl = rssUrl+\"\/getRssSources\";\n    let delRssUrl = rssUrl+\"\/deleteRssSources\";\n    let saveRssUrl = rssUrl+\"\/saveRssSource\";\n    \n    ajaxAwait(getRssUrl,500)\n         .then(r=>{\n          if(\/^java.net.ConnectException: Failed|^java.net.S\/.test(r)){\n              document.querySelector(\".pushrss-info\").innerHTML = `<div class=\"error\">\n              请检查是否打开web服务<br>\n              请检查网址是否错误\n              <\/div>`;\n              java.longToast(\"\\n请检查是否打开web服务\\n请检查网址是否错误\")\n              return\n          }\n          \n          let delwebs = [];\n          document.querySelector(\".pushrss-info\").classList.remove(\"hide\");\n          document.querySelector(\".pushrss-edit\").classList.add(\"hide\");\n          let rssData = JSON.parse(r).data;\n          rssData.forEach(x=>{\n              let findweb = webs.find(web=>web.url === x.sourceUrl);\n              if(!findweb){\n                  if(f === 1){\n                      delwebs.push(x);\n                      createPushRss(x,x.sourceName,x.sourceGroup,\"已删除\",\"red\");\n                  }else{\n                      createPushRss(x,x.sourceName,x.sourceGroup,\"未同步\",\"blue\");\n                  }\n              }else{\n                  let oname = x.sourceName;\n                  let ogroup = x.sourceGroup;\n                  if(f===2){\n                      if(findweb.sort??\"\"){\n                          x.customOrder = findweb.sort\n                      }\n                  }else{\n                  let oname = x.sourceName;\n                  let ogroup = x.sourceGroup;\n                  x.sourceName = findweb.name.replace(\/\\\\n\/g,'\\n');\n                  x.sourceGroup = findweb.group[0]===\"-1\"?\"\":findweb.group.join(\",\").replace(\/,$\/g,'');\n                  x.sourceIcon = findweb.icon.length>5?findweb.icon:\"\";\n                 }\n                 fetch(saveRssUrl, {\n                     method: \"POST\",\n                     headers: {\n                         \"Content-Type\": \"application\/json\"\n                        },\n                        body: JSON.stringify(x)\n                   })\n                   .then(res =>{\n                       createPushRss(x,oname,ogroup,\"已同步\",\"green\")\n                   })\n                   .catch(error => {\n                       java.toast(\"出现错误\")\n                   });\n              }\n          });\n          if(f ==1){\n              fetch(delRssUrl, {\n                     method: \"POST\",\n                     headers: {\n                         \"Content-Type\": \"application\/json\"\n                        },\n                        body: JSON.stringify(delwebs)\n                   })\n                   .then(res =>{\n                       java.toast(\"已同步完成\")\n                   })\n                   .catch(error => {\n                       java.toast(\"出现错误\")\n                   });\n          };\n          \n          java.toast(\"已同步完成\")\n       })\n       .catch(e=>{\n           console.log(e)\n             \t document.querySelector(\".pushrss-info\").innerHTML = `<div class=\"error\">\n              请检查是否打开web服务<br>\n              请检查网址是否错误\n              <\/div>`;\n              java.longToast(\"\\n请检查是否打开web服务\\n请检查网址是否错误\")\n        });\n}\n\n\nwebs_box.addEventListener(\"click\", function(event) {\n        let web_e = event.target.closest(\".s-box\")\n        if(event.target.closest(\".web-box\") || web_e){\n            let e = event.target.closest(\".web-box\");\n            let url = e.getAttribute(\"data-url\");\n            let s = e.getAttribute(\"data-s\");\n            if(isEditWeb){\n                if(web_e){\n                    let element = web_e.closest(\".web-box\");\n                    url = element.dataset.url;\n                    const matchingWeb = webs.find(web => web.url === url);\n                    if (matchingWeb) {\n                        editweb_m.classList.remove(\"hide\");\n                        let options = \"\";\n                        groups.forEach(x=>{\n                            if(x.id!==\"-2\")\n                            options+=`<option value=\"${x.id}\"${matchingWeb.group.some(item => item === x.id)?\" selected\":\"\"}>${x.name}<\/option>`;\n                        });\n                        editweb_m.querySelector(\".editweb-group select\").innerHTML = options\n                        editweb_m.querySelector(\".editweb-name input\").value = JSON.stringify(matchingWeb.name).replace(\/^\"|\"$\/g,'');\n                        editweb_m.querySelector(\".editweb-url input\").value = JSON.stringify(matchingWeb.url).replace(\/^\"|\"$\/g,'');\n                        editweb_m.querySelector(\".editweb-icon input\").value = matchingWeb.icon;\n                    }\n                    return;\n                }\n                e.classList.contains(\"active\")?e.classList.remove(\"active\"):e.classList.add(\"active\");\n                let editwebActive= document.querySelectorAll(\".editwebBox.active\");\n                let editwebBox= document.querySelectorAll(\".editwebBox\");\n                if(editwebActive.length === editwebBox.length){\n                    bottomMenu.querySelector(\"input\").checked = true;\n                }else{\n                    bottomMenu.querySelector(\"input\").checked = false\n                }\n                bottomMenu.querySelector(\"span\").textContent = editwebActive.length;\n            }else{\n                java.open(\"rss\",null,null,url);\n            }\n        }\n });\n \neditweb_m.addEventListener(\"click\", function(event) {  \n    if(event.target.closest(\".editweb-ok\")){\n        let url = editweb_m.querySelector(\".editweb-url input\").value;\n        const matchingWeb = webs.find(web => JSON.stringify(web.url).replace(\/^\"|\"$\/g,'')=== url);\n        if (matchingWeb) {\n            editweb_m.classList.add(\"hide\");\n            matchingWeb.group = Array.from(editweb_m.querySelector(\".editweb-group select\").selectedOptions)\n  .map(option => option.value);\n            matchingWeb.name =  editweb_m.querySelector(\".editweb-name input\").value.replace(\/\\\\n\/g,'\\n');\n            matchingWeb.icon = editweb_m.querySelector(\".editweb-icon input\").value.trim();\n            getRssData.webs = webs;\n            source.putVariable(JSON.stringify(getRssData))\n            java.toast(\"更新成功\");\n            upGroups();\n       }else{\n           java.toast(\"未找到对应的订阅源\")\n      }\n    }\n});\n\ncolorModal.addEventListener(\"click\", function(event) {  \n    if(event.target.closest(\".no\") || event.target.closest(\".close-btn\") || event.target.closest(\".modal-header\")){\n        document.querySelector(\".modal-overlay\").classList.remove(\"active\");\n        document.querySelector(\".color-modal\").classList.remove(\"active\")\n    }\n})\n\n\ndocument.querySelector(\".setthemebtn\").addEventListener(\"click\", function() {  \n        document.querySelector(\".modal-overlay\").classList.add(\"active\");\n        document.querySelector(\".color-modal\").classList.add(\"active\")\n})\n\n\nbottomMenu.addEventListener(\"click\", function(event) {  \n    const targetInput = event.target.closest('input');\n    const e = event.target;\n    let editwebBox= document.querySelectorAll(\".editwebBox\");\n    let editwebActive= document.querySelectorAll(\".editwebBox.active\");\n    if (targetInput) {\n        if (targetInput.checked) {\n            editwebBox.forEach(x=>{\n                x.classList.add(\"active\")\n            });\n            bottomMenu.querySelector(\"span\").textContent = editwebBox.length;\n        } else {\n            editwebBox.forEach(x=>{\n                x.classList.remove(\"active\");\n            })\n            bottomMenu.querySelector(\"span\").textContent = 0\n        }\n    }else if(e.closest(\".del\")){\n        if(editwebActive.length ===0)return;\n        editwebActive.forEach(x => {\n            let url = x.dataset.url;\n            const index = webs.findIndex(web => web.url === url);\n            if (index !== -1) {\n                webs.splice(index, 1);\n               }\n           });\n           \n           getRssData.webs = webs;\n           source.putVariable(JSON.stringify(getRssData));\n           upGroups();\n           bottomMenu.querySelector(\"span\").textContent = 0;\n           bottomMenu.querySelector(\"input\").checked = false;\n           java.toast(\"删除成功\")\n    }else if(e.closest(\".ok\")){\n        let items = document.querySelector(\".editgroup-items\");\n        items.innerHTML = \"\";\n        editgroup_m.classList.remove(\"hide\");\n        document.querySelector(\".editgroup-title h4\").textContent = \"移入分组\";\n        groups.forEach(x=>{\n                const item = document.createElement('div');\n                item.className = \"editgroup-item\";\n                item.dataset.id = x.id;\n                item.innerHTML = `\n                <div class=\"editgroup-order\"><input type=\"checkbox\" ${(x.id===\"-1\" || x.id===\"-2\")?\" disabled \":\"\"}><\/div>\n                <div class=\"editgroup-name\" contenteditable=\"true\">${x.name}<\/div>`;\n                items.appendChild(item);\n        })\n        \n    }\n})\n  \n  \n  \nrsslist_m.addEventListener(\"click\", function(event) {\n        if(event.target.closest(\".ok\"))saveRss();\n  });\n  \n editgroup_m.addEventListener(\"click\", function(event) {\n    const items = document.querySelector(\".editgroup-items\");\n    if(event.target.closest(\".editgroup-del\")){\n        event.target.style.color = \"red\";\n        event.target.closest(\".editgroup-item\").remove();\n    }else if(event.target.closest(\".editgroup-add\")){\n        const item = document.createElement('div');\n        item.className = \"editgroup-item\";\n        item.innerHTML = `\n     <div class=\"editgroup-order\"><input type=\"checkbox\" checked><\/div>\n     <div class=\"editgroup-name\" contenteditable=\"true\"><\/div>\n     <div class=\"editgroup-del\">×<\/div>`;\n      items.appendChild(item);\n    }else if(event.target.closest(\".editgroup-ok\")){\n        let groupItem = items.querySelectorAll(\".editgroup-item\") ?? [];\nlet newGroups = [];\nlet idMap = new Map(); \nlet seenIds = new Set(); \nconst SPECIAL_GROUP_IDS = [\"-1\", \"-2\"]; \n\n\nlet selectedGroupIds = [];\n let activeWebElements = document.querySelectorAll(\".editwebBox.active\") ?? [];\nif(isEditWeb && activeWebElements.length===0){\n        editgroup_m.classList.add(\"hide\");\n        java.toast(\"没有选中订阅源\")\n        return\n    }\n\n\nfor (let item of groupItem) {\n    let nameElement = item.querySelector(\".editgroup-name\");\n    if (!nameElement) continue;\n    let name = nameElement.innerText.trim();\n    if (!name) continue;\n    let jname = JSON.stringify(name).replace(\/^\"|\"$\/g, '');\n    let oldId = item.dataset.id;\n    let isSpecialGroup = SPECIAL_GROUP_IDS.includes(oldId);\n    let newId = isSpecialGroup ? oldId : jname;\n    let checkbox = item.querySelector(\".editgroup-order input[type='checkbox']\");\n    let isChecked = checkbox?.checked ?? true; \n    \n    if (isChecked) {\n        selectedGroupIds.push(newId);\n    }\n    \n    if (seenIds.has(newId)) continue;\n    seenIds.add(newId);\n    if (oldId && !isSpecialGroup && oldId !== newId) {\n        idMap.set(oldId, newId);\n    }\n    let originalGroup = groups.find(g => g.id === oldId);\n    let sValue = originalGroup ? originalGroup.s : 1; \n    if (!isEditWeb && !isSpecialGroup) {\n        sValue = isChecked ? 1 : 0;\n    }\n    \n    let group = originalGroup || { id: newId, name: jname, s: sValue };\n    group.id = newId;\n    group.name = jname;\n    if (!isEditWeb && !isSpecialGroup) {\n        group.s = sValue;\n    } else if (!originalGroup) {\n        group.s = sValue;\n    }\n    \n    newGroups.push(group)\n}\n\nif (isEditWeb) {\n    activeWebElements.forEach(webElement => {\n        let url = webElement.dataset.url;\n        if (!url) return;\n        let webObj = webs.find(web => web.url === url);\n        if (webObj) {\n            webObj.group = selectedGroupIds.length > 0 ? [...selectedGroupIds] : [\"-1\"];\n        }\n    });\n}\nif (!isEditWeb) {\n    webs.forEach(web => {\n        if (!Array.isArray(web.group)) return; \n        const mappedGroups = web.group.map(groupId => {\n            if (SPECIAL_GROUP_IDS.includes(groupId)) return groupId;\n            \n            const mappedId = idMap.get(groupId) || groupId;\n            if (!seenIds.has(mappedId)) {\n                return \"-1\"; \n            }\n            return mappedId;\n        });\n        web.group = [...new Set(mappedGroups)];\n    });\n}\n\ngroups = newGroups;\ngetRssData.groups = groups;\ngetRssData.webs = webs;\nsource.putVariable(JSON.stringify(getRssData));\neditgroup_m.classList.add(\"hide\");\njava.toast(\"保存成功\");\nupGroups();\n\n\n    }\n});\n\n  \ndocument.querySelector(\".getrssbtn\").addEventListener(\"click\", function() {getRssSources(rssUrl);})  \ndocument.querySelector(\".setwebbtn\").addEventListener(\"click\", function() {\n      isEditWeb = true;\n      bottomMenu.style.opacity = '1';\n      bottomMenu.style.visibility = 'visible';\n      bottomMenu.style.transform = 'translateY(10)';\n      document.querySelector(\".webs-c\").style.maxHeight = \"calc(100vh - 210px)\";\n      upGroups();\n  })  \n  \n  \ndocument.querySelector(\"body\").addEventListener(\"click\", function() {\n              if (event.target.closest(\".add-group\")) {\n                  if (popupVisible || isEditWeb) {\n                      hidePopup();\n                     } else {\n                         showPopup();\n                      }\n                    document.querySelector(\".webs-c\").style.maxHeight = \"calc(100vh - 150px)\";\n                  bottomMenu.style.opacity = '0';\n                  bottomMenu.style.visibility = 'hidden';\n                  bottomMenu.style.transform = 'translateY(0)';\n                    \n                  isEditWeb = false;\n                    upGroups();\n               }else{\n                      hidePopup();\n              }\n})\n  \n\n  \nfunction showPopup() {\n    popupVisible = true;\n    popupMenu.style.opacity = '1';\n    popupMenu.style.visibility = 'visible';\n    popupMenu.style.transform = 'translateY(0)';\n}\n\nfunction hidePopup() {\n    popupVisible = false;\n    popupMenu.style.opacity = '0';\n    popupMenu.style.visibility = 'hidden';\n    popupMenu.style.transform = 'translateY(-10px)';\n}\n\n  \ndocument.querySelector(\".setgroupbtn\").addEventListener(\"click\", function() {\n        editgroup_m.classList.remove(\"hide\");\n        document.querySelector(\".editgroup-title h4\").textContent = \"编辑分组\"\n        let editgroupHtml = \"\";\n        groups.forEach(x=>{\n        \t    let del =(x.id===\"-1\" || x.id===\"-2\")?\"\": `<div class=\"editgroup-del\">×<\/div>`;\n        \t    editgroupHtml+=`<div class=\"editgroup-item\" data-id=\"${x.id}\">\n         <div class=\"editgroup-order\"><input type=\"checkbox\" name=\"${x.id}\" ${x.s===1?\" checked\":\"\"}><\/div>\n         <div class=\"editgroup-name\" contenteditable=\"true\">${x.name}<\/div>\n         ${del}\n      <\/div>`;\n        \t});\n        \tdocument.querySelector(\".editgroup-items\").innerHTML = editgroupHtml;\n})  \n\n\nfunction closeM(ele){\n    if(typeof ele === \"string\")ele = document.querySelector(ele);\n    document.addEventListener(\"click\", function(event) {\n        if (event.target.matches(\".no\") || event.target.matches(\".m\"))\n        ele.classList.add(\"hide\");\n    });\n}\n  \nfunction getRssSources(rssUrl){\n    document.querySelectorAll(\".rssurl\").forEach(x=>{\n        x.value = rssUrl;\n    });\n    getrss_m.classList.remove(\"hide\");\n    document.querySelector(\".getrss-box\").addEventListener(\"click\",\n    function(event) {\n        if (event.target.closest(\".ok\")) {\n            rssUrl = document.querySelectorAll(\".rssurl\")[0].value;\n            if(\/http\/.test(rssUrl)){\n                getRssData.rssUrl = rssUrl;\n                rsslist_m.classList.remove(\"hide\");\n                rssUrl = rssUrl+\"\/getRssSources\";\n                ajaxAwait(rssUrl,500)\n                .then(r=>{\n                    source.putVariable(JSON.stringify(getRssData));\n                    rssData = JSON.parse(r).data;\n                    let rss_item = \"\";\n                    for(let i=0;i<rssData.length;i++){\n                        let x = rssData[i];\n                        let f = webs.some(item => item.url === x.sourceUrl);\n                        rss_item += `<div class=\"rsslist-item${f?\" select\":\"\"}\">\n                        <input type=\"checkbox\" name=\"${i}\"${f?\" disabled checked\":\"\"}>\n                        <div class=\"rsslist-name\">\n                            <p class=\"name\">${x.sourceName}\n                            <span style=\"color:#000;float:right\">${x.sourceGroup?\"(\"+x.sourceGroup+\")\":\"\"}<\/span>\n                      <\/p>\n                      <p class=\"rssurl\">\n                          ${x.sourceUrl}\n                      <\/p>\n                <\/div>\n            <\/div>`;\n              }\n              document.querySelector(\".rsslist-items\").innerHTML = rss_item;\n          })\n          .catch(e=>{\n             \t document.querySelector(\".rsslist-items\").innerHTML = `<div class=\"error\">\n              请检查是否打开web服务<br>\n              请检查网址是否错误\n              <\/div>`;\n              java.longToast(\"\\n请检查是否打开web服务\\n请检查网址是否错误\")\n          })\n       }else{\n           java.toast(\"请输入正确网址\")\n       }\n  }\n});\n}\n\nfunction saveRss(){\n    const selectitem = document.querySelectorAll(\n  '.rsslist-item input[type=\"checkbox\"]:checked:not([disabled])') ?? [];\n  if(selectitem.length==0){\n      java.toast(\"暂无可添加的订阅源\");\n      return;\n  }\n  \n  selectitem.forEach((x,i)=>{\n      x.disabled = true;\n      x.closest('.rsslist-item').classList.add(\"select\");\n      let web = {};\n      let index = x.name;\n      let r = rssData[index];\n      web.name = r.sourceName;\n      web.url = r.sourceUrl\n      web.icon = r.sourceIcon;\n      web.group =(r.sourceGroup?r.sourceGroup.split(\",\"):\"\") || [\"-1\"];\n      web.group.forEach(groupItem => {\n          if (!groups.some(group => group.id === groupItem)) {\n              groups.push({\n                  id: groupItem,\n                  s: 1,\n                  name: groupItem!==\"-1\"?groupItem:\"未分组\"\n                 });\n              }\n          });\n         webs.push(web);\n    });\n    \n    if (!groups.some(x => x.f == 1)) {\n        const uncategorized = groups.find(x => x.id == \"-2\");\n        if (uncategorized) {\n            uncategorized.f = 1;\n        } \n     }\n     \n     getRssData.webs = webs;\n     getRssData.groups = groups;\n     source.putVariable(JSON.stringify(getRssData));\n     upGroups();\n     java.toast(\"添加成功\");\n}\n\nfunction upGroups(){\n     getRssData = JSON.parse(source.getVariable() || \"{}\");\n     groups = getRssData.groups ??  [{id:\"-2\",name:\"全部\",s:1},{id:\"-1\",name:\"未分组\",s:1}];\n     webs = getRssData.webs ?? [];\n     let groupHtml = \"\";\n     let f = groups.find(x=> x.f===1 && x.s === 1);\n     groups.forEach((x,i)=>{\n         if(x.s===1){\n             groupHtml+=`\n         <div class=\"group${f?(x.f===1?\" active\":\"\"):(i==0?\" active\":\"\")}\" data-id=\"${x.id}\">${x.name}<\/div>\n         `\n         }\n     });\n     document.querySelector('.groups-box').innerHTML = groupHtml;\n     initAllEvents()\n}\n\nfunction waitForElement(selector, callback, timeout = 1000) {\n    const startTime = Date.now();\n    const checkElement = () => {\n        const element = document.querySelector(selector);\n        if (element) {\n            callback(element);\n        } else if (Date.now() - startTime < timeout) {\n            setTimeout(checkElement, 100);\n        } else {\n            callback(null);\n        }\n    };\n    checkElement();\n}\n\nfunction getWebs(id){\n    let filterWebs;\n    if(id===\"-2\"){\n        filterWebs = webs.map(web => ({ ...web })).sort((a, b) => a.sort - b.sort);\n    }else{\n        filterWebs = webs.filter(x => x.group.some(item => item === id));\n    }\n    \n    let websHtml = \"\";\n    document.querySelector(\".webs-box\").innerHTML = \"\";\n    if(filterWebs.length==0){\n        websHtml = `\n        <div style=\"margin-top:200px\">\n               暂无订阅源\n        <\/div>\n        `;\n    }\n    \n    filterWebs.forEach(x => {\n    const webBox = document.createElement('div');\n    webBox.className = 'web-box';\n    webBox.setAttribute('data-url', x.url);\n    if(isEditWeb)webBox.classList.add(\"editwebBox\");\n    const iconBox = document.createElement('div');\n    iconBox.className = 'web-icon-box';\n    const iconInner = document.createElement('div');\n    let icon = x.icon.trim();\n    if (\/^http|data:\/.test(icon)) {\n        const img = document.createElement('img');\n        img.src = icon;\n        iconInner.appendChild(img);\n    } else {\n        const iconDiv = document.createElement('div');\n        const iconText = x.name.trim().match(\/[\\u4e00-\\u9fa5a-zA-Z.]\/)[0];\n        iconDiv.textContent = iconText;\n        iconInner.appendChild(iconDiv);\n    }\n    if(isEditWeb){\n        const sBox = document.createElement('div');\n        sBox.className = \"s-box\";\n        sBox.innerHTML = \"📝\"\n        webBox.appendChild(sBox);\n    }\n    \n    iconBox.appendChild(iconInner);\n    const nameBox = document.createElement('div');\n    nameBox.className = 'web-name';\n    nameBox.innerHTML = x.name.replace(\/\\n\/g, '<br>');     \n    webBox.appendChild(iconBox);\n    webBox.appendChild(nameBox);\n    document.querySelector(\".webs-box\").appendChild(webBox);\n    initSortable();\n});\n}\n\ndocument.querySelector('.rsslist-search input').addEventListener('input', function(e) {\n    const searchText = e.target.value.trim().toLowerCase();\n    document.querySelectorAll('.rsslist-item').forEach(item => {\n        const itemText = item.textContent.toLowerCase();\n        item.style.display = (searchText === '' || itemText.includes(searchText)) ? '' : 'none';\n    });\n    updateSelectAllState();\n});\n\ndocument.querySelector('.search-box input').addEventListener('input', function(e) {\n    const searchText = e.target.value.trim().toLowerCase();\n    document.querySelectorAll('.web-box').forEach(item => {\n        const itemText = item.querySelector(\".web-name\").textContent.toLowerCase()+item.dataset.url.trim().toLowerCase();\n        item.style.display = (searchText === '' || itemText.includes(searchText)) ? '' : 'none';\n    });\n});\n\ndocument.querySelector('.rsslist-check input').onchange = e => {\n    document.querySelectorAll('.rsslist-item:not([style*=\"display: none\"]) input[type=\"checkbox\"]:not([disabled])')\n        .forEach(cb => cb.checked = e.target.checked);\n};\n\ndocument.addEventListener('change', e => {\n    if (e.target.matches('.rsslist-item input[type=\"checkbox\"]:not([disabled])')) {\n        updateSelectAllState();\n    }\n});\n\ndocument.addEventListener('click', e => {\n    if (e.target.closest('.rsslist-item')) {\n        const item = e.target.closest('.rsslist-item');\n        const checkbox = item.querySelector('input[type=\"checkbox\"]');\n        if (checkbox && !e.target.matches('input[type=\"checkbox\"]') && !checkbox.disabled) {\n            checkbox.checked = !checkbox.checked;\n            checkbox.dispatchEvent(new Event('change', { bubbles: true }));\n        }\n    }\n});\n\nfunction updateSelectAllState() {\n    const visibleCheckboxes = document.querySelectorAll('.rsslist-item:not([style*=\"display: none\"]) input[type=\"checkbox\"]:not([disabled])');\n    \n    if (visibleCheckboxes.length === 0) {\n        document.querySelector('.rsslist-check input').checked = false;\n        return;\n    }\n    \n    const allChecked = Array.from(visibleCheckboxes).every(cb => cb.checked);\n    document.querySelector('.rsslist-check input').checked = allChecked;\n}\n\n\nfunction initAllEvents() {\n    document.addEventListener('click', function(e) {\n        if (e.target.classList.contains('group')) {\n            const allGroups = document.querySelectorAll('.group');\n            allGroups.forEach(g => g.classList.remove('active'));\n            e.target.classList.add('active');\n            e.target.scrollIntoView({ behavior: 'smooth', inline: 'center' });\n            const groupId = e.target.dataset.id;\n            groups.forEach(x=>{\n                x.f = x.id===groupId?1:0;\n            });\n            getRssData.groups = groups;\n            source.putVariable(JSON.stringify(getRssData));\n            bottomMenu.querySelector(\"span\").textContent = 0;\n            bottomMenu.querySelector(\"input\").checked = false;\n            getWebs(groupId);\n        }\n    });\n    let g = document.querySelector(\".group.active\")??document.querySelectorAll(\".group\")[0];\n    g.scrollIntoView({ behavior: 'smooth', inline: 'center' });\n    g.classList.add(\"active\");\n    getWebs(g.getAttribute(\"data-id\"));\n    initSwipeGesture();\n}\n\nwebs_box.addEventListener('scroll', function() {\n    clearTimeout(scrollTimer);\n    scrollTimer = setTimeout(() => {\n        initSortable();\n    }, 50);\n});\n\nfunction initSwipeGesture() {\n    const swipeArea = document.querySelector('.webs-c');\n    if (!swipeArea) {\n        console.error('找不到滑动手势区域');\n        return;\n    }\n    \n    let touchStartTime = 0;\n    let isPotentialSwipe = true;\n    let startX = 0;\n    let startY = 0;\n    let lastX = 0;\n    let lastY = 0;\n    const VERTICAL_THRESHOLD = 200; \n    const LONG_PRESS_TIME = 200; \/\/ 长按时间阈值(毫秒)\n    \n    swipeArea.addEventListener('touchstart', function(e) {\n        touchStartTime = Date.now();\n        swipeStartX = e.touches[0].clientX;\n        startX = e.touches[0].clientX;\n        startY = e.touches[0].clientY;\n        lastX = startX;\n        lastY = startY;\n        isSwiping = true;\n        isPotentialSwipe = true;\n    }, { passive: true });\n    \n    swipeArea.addEventListener('touchmove', function(e) {\n        if (!isSwiping || !isPotentialSwipe) return;\n        const currentX = e.touches[0].clientX;\n        const currentY = e.touches[0].clientY;\n        const verticalDiff = Math.abs(currentY - startY);\n        if (verticalDiff > VERTICAL_THRESHOLD) {\n            isPotentialSwipe = false;\n        }\n        \n        lastX = currentX;\n        lastY = currentY;\n    }, { passive: true });\n    \n    swipeArea.addEventListener('touchend', function(e) {\n        if (!isSwiping || !isPotentialSwipe) {\n            isSwiping = false;\n            isPotentialSwipe = true;\n            return;\n        }\n        \n        const touchDuration = Date.now() - touchStartTime;\n        const endX = e.changedTouches[0].clientX;\n        const diffX = endX - swipeStartX;\n        if (touchDuration > LONG_PRESS_TIME) {\n            isSwiping = false;\n            isPotentialSwipe = true;\n            return;\n        }\n        \n        if (Math.abs(diffX) > 40) {\n            const activeGroup = document.querySelector('.group.active');\n            if (activeGroup) {\n                const groups = Array.from(document.querySelectorAll('.group'));\n                const currentIndex = groups.indexOf(activeGroup);\n                \n                if (diffX > 0 && currentIndex > 0) {\n                    groups[currentIndex - 1].click();\n                } else if (diffX < 0 && currentIndex < groups.length - 1) {\n                    groups[currentIndex + 1].click();\n                }\n            }\n        }\n        \n        isSwiping = false;\n        isPotentialSwipe = true;\n    }, { passive: true });\n}\n\n\nfunction initSortable() {\n    if (sortableInstance) {\n        sortableInstance.destroy();\n    }\n    \n    sortableInstance = new Sortable(document.querySelector('.webs-box'), {\n        animation: 150,\n        fallbackOnBody: false,   \n        fallbackClass: \"sortable-fallback\",\n        fallbackTolerance: 0,   \n        ghostClass: \"sortable-ghost\",\n        dragClass: \"sortable-drag\",\n        chosenClass: \"sortable-chosen\",\n        dataIdAttr: 'data-url',\n        delay: 100,\n        \n        onEnd: function(evt) {\n    const activeGroup = document.querySelector('.group.active');\n    if (!activeGroup) return;\n    \n    const groupId = activeGroup.getAttribute('data-id');\n    const container = evt.to;\n    const items = Array.from(container.children);\n    \n    if (groupId === '-2') {\n    const sortedDataIds = items.map(item => item.getAttribute('data-url'));\n    sortedDataIds.forEach((dataId, sortIndex) => {\n        const web = webs.find(w => w.url.toString() === dataId);\n        if (web) {\n            web.sort = sortIndex; \n           }\n          });\n}else {\n        const sortedDataUrls = items.map(item => item.getAttribute('data-url'));\n        const currentGroupWebs = webs.filter(web => web.group === groupId);\n        if (currentGroupWebs.length === 0) {\n            return;\n        }\n        const urlToWebMap = {};\n        currentGroupWebs.forEach(web => {\n            urlToWebMap[web.url] = web;\n        });\n        const reorderedCurrentGroupWebs = [];\n        sortedDataUrls.forEach(dataUrl => {\n            if (urlToWebMap[dataUrl]) {\n                reorderedCurrentGroupWebs.push(urlToWebMap[dataUrl]);\n            }\n        });\n        webs = webs.filter(web => web.group !== groupId);\n        reorderedCurrentGroupWebs.forEach(web => {\n            webs.push(web);\n        });\n    }\n    \n    getRssData.webs = webs;\n    source.putVariable(JSON.stringify(getRssData));\n}\n\n        \n    });\n}\n\n\nfunction checkMinusTwo() {\n  const checkboxes = document.querySelectorAll('.editgroup-item input[type=\"checkbox\"]');\n  const allUnchecked = Array.from(checkboxes).every(cb => !cb.checked);\n  \n  if (allUnchecked) {\n    const minusTwo = document.querySelector('.editgroup-item[data-id=\"-2\"] input[type=\"checkbox\"]');\n    if (minusTwo) minusTwo.checked = true;\n  }\n}\n\ndocument.querySelector('.editgroup-items').addEventListener('change', function(e) {\n  if (e.target.type === 'checkbox') {\n    setTimeout(checkMinusTwo, 0);\n  }\n});\n\nfunction getTextColor(hexColor) {\n    const hex = hexColor.replace('#', '');\n    const r = parseInt(hex.substr(0, 2), 16);\n    const g = parseInt(hex.substr(2, 2), 16);\n    const b = parseInt(hex.substr(4, 2), 16);\n    const brightness = (r * 299 + g * 587 + b * 114) \/ 1000;\n    return brightness > 128 ? '#000000' : '#ffffff';\n}\n\n\nconst resetBtn = document.getElementById('resetBtn');\nconst applyBtn = document.getElementById('applyBtn');\n\n\/\/ 颜色输入元素\nconst backgroundColorInput = document.getElementById('backgroundColorInput');\nconst accentColorInput = document.getElementById('accentColorInput');\nconst iconBgColorInput = document.getElementById('iconBgColorInput');\nconst iconTextColorInput = document.getElementById('iconTextColorInput');\nconst topColorInput = document.getElementById('topColorInput');\n\n\/\/ 颜色值显示元素\nconst backgroundColorValue = document.getElementById('backgroundColorValue');\nconst accentColorValue = document.getElementById('accentColorValue');\nconst iconBgColorValue = document.getElementById('iconBgColorValue');\nconst iconTextColorValue = document.getElementById('iconTextColorValue');\nconst topColorValue = document.getElementById('topColorValue');\n\n\/\/ 颜色预览元素\nconst backgroundColorPreview = document.getElementById('backgroundColorPreview');\nconst accentColorPreview = document.getElementById('accentColorPreview');\nconst iconBgColorPreview = document.getElementById('iconBgColorPreview');\nconst iconTextColorPreview = document.getElementById('iconTextColorPreview');\nconst topColorPreview = document.getElementById('topColorPreview');\n\n\nconst readTheme = JSON.parse(java.getThemeConfig());\n\n\/\/ 默认颜色值\nconst defaultColors = {\n    backgroundColor: readTheme[\"backgroundColor\"].replace(\/#..\/,'#') || '#f8f9fa',\n    accentColor: readTheme[\"accentColor\"].replace(\/#..\/,'#') || '#367ECE',\n    iconBgColor: 'none',\n    iconTextColor: readTheme[\"accentColor\"].replace(\/#..\/,'#') || '#367ECE',\n    topColor: readTheme[\"primaryColor\"].replace(\/#..\/,'#') || '#ffffff'\n};\n\nlet currentColors = getRssData.theme ?? { ...defaultColors };\n\nfunction updateColorDisplay(type, value) {\n            const previewElement = document.getElementById(`${type}Preview`);\n            if (previewElement) {\n                previewElement.style.backgroundColor = value;\n            }\n            const valueElement = document.getElementById(`${type}Value`);\n            if (valueElement) {\n                valueElement.textContent = value;\n            }\n            currentColors[type] = value;\n            if(value !== \"none\")\n            document.getElementById(`${type}Input`).value = value;\n}\n\nfunction applyColorsToPage() {\n            let textColor = getTextColor(currentColors.accentColor);\n            let bgTextColor = getTextColor(currentColors.backgroundColor);\n         document.documentElement.style.setProperty('--text-color', textColor);\n            document.documentElement.style.setProperty('--bg-text-color', bgTextColor);\n            document.documentElement.style.setProperty('--icon-text-color', currentColors.iconTextColor);\n            document.documentElement.style.setProperty('--icon-bg-color', currentColors.iconBgColor);\n            document.documentElement.style.setProperty('--bg-color', currentColors.backgroundColor);\n            document.documentElement.style.setProperty('--active-color', currentColors.accentColor);\n            document.documentElement.style.setProperty('--top-color', currentColors.topColor);\n            getRssData.theme = currentColors;\n            source.putVariable(JSON.stringify(getRssData))\n}\n\nfunction resetColors() {\n            updateColorDisplay('backgroundColor', defaultColors.backgroundColor);\n            updateColorDisplay('accentColor', defaultColors.accentColor);\n            updateColorDisplay('iconBgColor', defaultColors.iconBgColor);\n            updateColorDisplay('iconTextColor', defaultColors.iconTextColor);\n            updateColorDisplay('topColor', defaultColors.topColor);\n            currentColors = { ...defaultColors };\n            java.toast('颜色已重置为默认值');\n}\n\nfunction initializeColors() {\n            updateColorDisplay('backgroundColor', currentColors.backgroundColor);\n            updateColorDisplay('accentColor', currentColors.accentColor);\n            updateColorDisplay('iconBgColor', currentColors.iconBgColor);\n            updateColorDisplay('iconTextColor', currentColors.iconTextColor);\n            updateColorDisplay('topColor', currentColors.topColor);\n            applyColorsToPage();\n}\n\nresetBtn.addEventListener('click', resetColors);\napplyBtn.addEventListener('click', () => {\n            applyColorsToPage();\n            java.toast('颜色设置已应用');\n            document.querySelector(\".modal-overlay\").classList.remove(\"active\");\n            document.querySelector(\".color-modal\").classList.remove(\"active\")\n});\n\nbackgroundColorInput.addEventListener('input', (e) => updateColorDisplay('backgroundColor', e.target.value));\naccentColorInput.addEventListener('input', (e) => updateColorDisplay('accentColor', e.target.value));\niconBgColorInput.addEventListener('input', (e) => updateColorDisplay('iconBgColor', e.target.value));\niconTextColorInput.addEventListener('input', (e) => updateColorDisplay('iconTextColor', e.target.value));\ntopColorInput.addEventListener('input', (e) => updateColorDisplay('topColor', e.target.value));\n\n\n\/\/ 元素映射\nconst colorElements = {\n    backgroundColorValue: 'backgroundColor',\n    accentColorValue: 'accentColor',\n    iconBgColorValue: 'iconBgColor',\n    iconTextColorValue: 'iconTextColor',\n    topColorValue: 'topColor'\n};\n\n\/\/ 为每个元素添加监听\nObject.entries(colorElements).forEach(([elementId, type]) => {\n    const element = document.getElementById(elementId);\n    if (!element) return;\n    \n    let originalValue = element.textContent;\n    \n    element.addEventListener('input', function() {\n        const text = this.textContent.replace(\/[\\n\\r]\/g, '').substring(0, 7);\n        this.textContent = text;\n    });\n    \n    element.addEventListener('keydown', function(e) {\n    if (e.key === 'Enter') {\n        e.preventDefault();\n        this.blur();\n    }\n});\n\n    element.addEventListener('blur', function() {\n        const text = this.textContent.trim();\n        const isValid = \/^#[0-9a-fA-F]{6}$|none\/.test(text);\n        \n        if (isValid) {\n            updateColorDisplay(type, text);\n            originalValue = text;\n        } else {\n            this.textContent = originalValue;\n        }\n    });\n});\n\n\n\n\ninitializeColors();\ndocument.addEventListener('DOMContentLoaded', initAllEvents);",
    "startStyle": ":root{\n\t --active-color:#367ECE;\n\t --text-color:#fff;\n\t --top-color:#fff;\n\t --bg-color:#f8f9fa;\n\t --icon-text-color:#000;\n\t --icon-bg-color:#fff;\n\t --bg-text-color:#000;\n}\n * {\n\t margin: 0;\n\t padding: 0;\n\t box-sizing: border-box \n}\n body {\n\t background-color: var(--bg-color);\n\t min-height: 100vh;\n\t padding: 20px;\n\t color: #1a1a1a;\n\t -webkit-font-smoothing: antialiased;\n\t -moz-osx-font-smoothing: grayscale;\n}\n.web-icon-box>div>div{\n    background:var(--icon-bg-color);\n    color:var(--icon-text-color)\n}\n #bottomMenu{\n\t visibility:hidden;\n\t position:fixed;\n\t bottom:0;\n\t right:0;\n\t left:0;\n\t height:60px;\n\t z-index:1000;\n\t width:100%;\n\t background:white;\n\t border-radius:10px;\n\t box-shadow:0px -8px 10px rgba(0,0,0,0.12);\n\t display:flex;\n\t justify-content:space-between;\n\t align-items:center;\n\t padding:0px 20px;\n\t opacity:0;\n\t transform:translateY(0px);\n\t transition:all 0.25s cubic-bezier(0.4,0,0.2,1);\n}\n div#popupMenu{\n\t background:white;\n\t border-radius:10px;\n\t visibility:hidden;\n\t position:fixed;\n\t right:30px;\n\t top:100px;\n\t transform:translateY(-10px);\n\t box-shadow:0 4px 20px rgba(0,0,0,0.12);\n\t opacity:0;\n\t z-index:1000;\n\t text-align:center \n}\n #bottomMenu button{\n\t padding:5px 20px;\n\t border:0px solid #000;\n\t border-radius:28px;\n\t background:var(--active-color);\n\t color:var(--text-color);\n\t box-shadow:2px 5px 6px rgba(0,0,0,0.2);\n}\n #bottomMenu button.ok{\n\t color:#fff;\n\t background:red \n}\n .popup-item{\n\t padding:10px 16px;\n\t display:flex;\n\t align-items:center;\n\t gap:10px;\n\t color:#374151;\n\t font-size:14px;\n\t transition:all 0.15s ease;\n}\n .popup-item:active {\n\t color: var(--text-color) font-weight:bold;\n\t background:var(--active-color) \n}\n\n .web-icon-box > div::after {\n\t content: '';\n\t position: absolute;\n\t top: 0;\n\t left: 0;\n\t right: 0;\n\t bottom: 0;\n\t z-index: 1;\n\t\n}\n\n .web-box.sortable-chosen:active {\n\t background-color: inherit;\n\t opacity: 0.5;\n}\n .sortable-fallback {\n\t opacity: 0.5;\n\t background: #e6f7ff;\n\t border: 2px dashed #1890ff;\n}\n\n .sortable-ghost {\n\t opacity: 0.4;\n}\n\n .sortable-drag {\n\t opacity: 0.8;\n\t border-radius:8px;\n\t z-index: 9999 !important;\n}\n .hide,.m.hide{\n\t display:none \n}\n .s-box{\n\t position:absolute;\n\t top:10px;\n\t right:5px;\n\t font-size:15px;\n     z-index:999;\n}\n .error{\n\t color:red;\n\t font-size:14px;\n\t text-align:center \n}\n\n .header-box {\n\t position:fixed;\n\t display: flex;\n\t flex-direction: column;\n\t align-items: center;\n\t background: var(--top-color);\n\t border-bottom: 1px solid #ddd;\n\t box-shadow: 0px 1px 6px rgba(0, 0, 0, 0.15);\n\t top:0;\n\t left:0;\n\t right:0;\n\t padding:20px 20px 0px;\n\t width:100%;\n}\n\n .modal-overlay {\n\t position: fixed;\n\t top: 0;\n\t left: 0;\n\t right: 0;\n\t bottom: 0;\n\t background-color: rgba(0, 0, 0, 0.5);\n\t z-index: 1000;\n\t opacity: 0;\n\t visibility: hidden;\n\t transition: opacity 0.3s ease, visibility 0.3s ease;\n}\n .modal-overlay.active {\n\t opacity: 1;\n\t visibility: visible;\n}\n\n .color-modal {\n\t position: fixed;\n\t bottom: 0;\n\t left: 0;\n\t right: 0;\n\t background-color: white;\n\t transform: translateY(100%);\n\t border-radius: 24px 24px 0 0;\n\t padding: 0;\n\t z-index: 1001;\n\t transition: transform 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94);\n\t max-height: 85vh;\n\t overflow: hidden;\n\t display: flex;\n\t flex-direction: column;\n\t box-shadow: 0 -10px 40px rgba(0, 0, 0, 0.1);\n}\n .color-modal.active {\n\t transform: translateY(0);\n}\n\n .modal-header {\n\t padding: 14px 16px;\n\t border-bottom: 1px solid #f2f2f7;\n\t display: flex;\n     box-shadow: 0 5px 6px rgba(0, 0, 0, 0.2);\n\t justify-content: space-between;\n\t align-items: center;\n     background:var(--active-color)\n}\n .modal-title {\n\t font-size: 22px;\n\t font-weight: 600;\n\t color: var(--text-color);\n}\n .close-btn {\n\t background: none;\n\t border: none;\n\t font-size: 20px;\n\t width: 32px;\n\t height: 32px;\n\t border-radius: 8px;\n\t display: flex;\n\t align-items: center;\n\t justify-content: center;\n\t cursor: pointer;\n\t color: var(--text-color);\n\t transition: all 0.2s ease;\n}\n .close-btn:hover {\n\t background-color: #f2f2f7;\n\t color: #1d1d1f;\n}\n\n .modal-content {\n\t flex: 1;\n\t overflow-y: auto;\n\t padding: 0 24px 24px;\n}\n .color-options {\n\t display: flex;\n\t flex-direction: column;\n\t gap: 24px;\n\t margin-top: 20px;\n}\n .color-option {\n\t display: flex;\n\t flex-direction: column;\n\t gap: 10px;\n}\n .color-label {\n\t display: flex;\n\t justify-content: space-between;\n\t align-items: center;\n\t font-size: 16px;\n\t font-weight: 500;\n\t color: #1d1d1f;\n}\n .color-name {\n\t display: flex;\n\t align-items: center;\n\t gap: 8px;\n}\n .color-preview {\n\t width: 24px;\n\t height: 24px;\n\t border-radius: 6px;\n\t border: 2px solid #f2f2f7;\n\t box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n}\n .color-value {\n\t font-size: 14px;\n\t color: #8e8e93;\n\t font-family: 'Monaco', 'Menlo', monospace;\n\t background-color: #f9f9f9;\n\t padding: 2px 8px;\n\t border-radius: 4px;\n}\n .color-input {\n\t width: 100%;\n\t height: 48px;\n\t border: 2px solid #e5e5ea;\n\t border-radius: 12px;\n\t padding: 0;\n\t cursor: pointer;\n\t background: none;\n\t transition: border-color 0.2s ease;\n}\n .color-input:hover {\n\t border-color: #c7c7cc;\n}\n .color-input:focus {\n\t outline: none;\n\t border-color: #007aff;\n}\n\n .modal-footer {\n\t padding: 20px 24px;\n\t border-top: 1px solid #f2f2f7;\n\t display: flex;\n\t gap: 12px;\n}\n .action-btn {\n\t flex: 1;\n\t padding: 14px;\n\t border-radius: 12px;\n\t font-size: 16px;\n\t font-weight: 500;\n\t cursor: pointer;\n\t transition: all 0.2s ease;\n\t border: none;\n}\n .reset-btn {\n\t background-color: #f2f2f7;\n\t color: #1d1d1f;\n}\n .reset-btn:hover {\n\t background-color: #e5e5ea;\n}\n\n .apply-btn {\n\t background-color: var(--active-color);\n\t color: var(--text-color);\n}\n\n .apply-btn:hover {\n\t background-color: #0056cc;\n}\n\n\n .search-box{\n\t margin-bottom:10px;\n\t width:100%;\n}\n .input{\n\t width:100%;\n\t height:30px;\n\t border-radius: 6px;\n\t border: 1px solid #ccc;\n\t padding:10px;\n}\n select.input{\n\t padding:2px \n}\n .input::placeholder{\n\t color: #999;\n\t font-style: italic;\n\t opacity: 0.8;\n}\n .input:focus {\n\t outline: 2px solid var(--active-color);\n\t color:var(--active-color);\n}\n .input:focus::placeholder {\n\t opacity: 0.2;\n}\n\n .tab-box{\n\t position: relative;\n\t display: flex;\n\t align-items: center;\n\t width:100%;\n}\n .groups-box {\n\t display: flex;\n\t max-width:90%;\n\t align-items: center;\n\t overflow: auto;\n\t flex: 1;\n}\n .add-group{\n\t position: absolute;\n\t right:-10px;\n}\n\n .group,.add-group {\n\t text-align: center;\n\t margin-right: 15px;\n\t padding-bottom:8px;\n\t flex-shrink: 0;\n\t\n\t color:#888 \n}\n .group.active{\n\t border-bottom:3px solid var(--active-color);\n\t color:var(--active-color);\n\t font-weight:bold \n}\n .webs-box{\n\t display: flex;\n\t flex-wrap: wrap;\n\t justify-content: flex-start;\n\t width:100%;\n\t max-height:calc(100vh - 150px);\n\t min-height:0;\n\t overflow-y: auto;\n\t overflow-x:hidden;\n\t position: relative;\n}\n .webs-c{\n\t margin-top:100px;\n\t width:100%;\n\t height:calc(100vh - 150px);\n\t max-height:calc(100vh - 150px);\n\t min-height:0;\n\t overflow-y: auto;\n\t overflow-x:hidden;\n}\n .web-box{\n\t position: relative;\n\t width:25%;\n\t max-height:200px;\n\t padding: 10px;\n\t text-align: center;\n\t user-select:none;\n}\n\n .web-box:active ,.web-box.editwebBox.active{\n\t border:2px dashed var(--active-color);\n\t border-radius: 5px;\n\t box-shadow: 2px 2px 6px rgba(0, 0, 0, 0.25);\n}\n .web-icon-box{\n\t width: 100%;\n\t padding-top: 100%;\n\t position: relative;\n}\n .web-icon-box>div {\n\t position: absolute;\n\t top: 0;\n\t left: 0;\n\t right: 0;\n\t bottom: 0;\n\t display: flex;\n\t align-items: center;\n\t justify-content: center;\n}\n .web-icon-box img,.web-icon-box>div>div,.msg-item img{\n\t user-select:none;\n\t width: 80%;\n\t height: 80%;\n\t display: block;\n\t border-radius: 8px;\n\t object-fit: cover;\n\t box-shadow: 2px 2px 6px rgba(0, 0, 0, 0.25);\n\t font-size:1.5em \n}\n\n\n.msg-item .img img{\n    border-radius: 2px;\n     width: 100%;\n\t height: 100%;\n}\n .web-name{\n\t font-size: 10px;\n\t line-height: 1.4;\n\t display: -webkit-box;\n\t -webkit-line-clamp: 2;\n\t -webkit-box-orient: vertical;\n\t overflow: hidden;\n\t max-height: 4.5em;\n     color:var(--bg-text-color)\n}\n .m{\n\t position: fixed;\n\t top: 0;\n\t left: 0;\n\t right: 0;\n\t bottom: 0;\n\t width: 100%;\n\t height: 100%;\n\t background: rgba(0, 0, 0, 0.5);\n\t z-index: 1000;\n\t display: flex;\n\t align-items: center;\n\t justify-content: center;\n\t padding: 20px;\n}\n .box {\n\t position: relative;\n\t width: 90%;\n\t max-width: 600px;\n\t background: #fff;\n\t border-radius: 8px;\n\t padding: 20px;\n\t box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);\n\t max-height: 80vh;\n\t display:flex;\n\t flex-direction: column;\n}\n .title{\n\t position: absolute;\n\t top:0;\n\t left:0;\n\t right:0;\n\t width:100%;\n\t background:var(--active-color);\n\t box-shadow: 0 3px 8px rgba(0, 0, 0, 0.3);\n}\n .title h4{\n\t text-align:center;\n\t color:var(--text-color);\n\t padding:10px \n}\n .editgroup-item{\n\t position: relative;\n\t height:35px;\n\t width:100%;\n\t margin-bottom:10px;\n\t border:1px dashed var(--active-color);\n\t border-radius: 5px;\n\t box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3);\n}\n .editgroup-order{\n\t position: absolute;\n\t left:10px;\n\t top:0;\n\t bottom:0;\n}\n .editgroup-name{\n\t position: absolute;\n\t text-indent: 0;\n\t left:30px;\n\t top:0;\n\t bottom:0;\n\t width:70%;\n\t padding:10px;\n\t display: flex;\n\t align-items: center;\n\t justify-content: flex-start;\n\t user-select:none;\n     overflow-y: hidden;\n\t word-wrap: normal;\n\t white-space: pre-wrap;\n\t overflow-wrap: break-word;\n\t\n\t max-height: 100%;\n\t scroll-behavior: smooth;\n}\n .editgroup-del{\n\t position: absolute;\n\t right:10px;\n\t top:0;\n\t bottom:0;\n}\n .btn button{\n\t margin:10px;\n\t padding:4px 15px;\n\t border-radius: 4px;\n\t border: none;\n\t outline: none;\n\t box-shadow: 2px 2px 6px rgba(0, 0, 0, 0.20);\n}\n .editgroup-add{\n\t background:#4CAF50;\n\t color:#fff \n}\n .btn .ok{\n\t background:var(--active-color);\n\t color:var(--text-color) \n}\n .btn .no{\n\t background:#ddd \n}\n .editgroup-del,.editgroup-order,.btn,.web-icon-box>div>div,.rsslist-check{\n\t display: flex;\n\t align-items: center;\n\t justify-content: center;\n\t user-select:none \n}\n .btn button:active,#bottomMenu button:active {\n\t animation: floatScale 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);\n}\n .editweb{\n\t display:flex;\n\t margin-top:40px;\n\t flex-direction: column;\n\t justify-content:flex-start;\n}\n .editweb p{\n\t font-size:13px;\n}\n .editweb>div{\n\t margin-bottom:10px \n}\n .input.rssurl{\n\t margin-top:45px;\n\t margin-bottom:10px;\n\t font-size:13px;\n\t width:100%;\n\t height:30px;\n}\n .rsslist-input{\n\t margin-top:40px;\n\t display:flex;\n\t width:100%;\n\t align-items: center;\n\t justify-content:space-between;\n\t margin-left:-5px;\n}\n .rsslist-items{\n\t margin-bottom:10px;\n}\n .rsslist-item,.msg-item{\n\t display:flex;\n\t align-items: center;\n\t justify-content:flex-start;\n\t padding:5px;\n\t width:100%;\n\t margin-bottom:10px;\n\t border:1px dashed var(--active-color);\n\t border-radius: 5px;\n\t box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.3);\n}\n .rsslist-item.select{\n\t border:1px dashed #ddd \n}\n .rsslist-item.select .name{\n\t color:#999 \n}\n .rsslist-name,.msg-name{\n\t width:100%;\n\t max-width: 100%;\n\t margin-left:15px;\n\t overflow: hidden;\n}\n .name{\n\t font-size:12px;\n\t color: var(--active-color);\n\t font-weight:bold;\n\t width:100%;\n}\n .rsslist-name > p,.msg-name > p {\n\t line-height: 1.4;\n\t white-space: nowrap;\n\t overflow: hidden;\n\t text-overflow: ellipsis;\n}\n .rsslist-box input:checked,input:checked{\n\t accent-color: var(--active-color);\n}\n .rssurl{\n\t color:#888;\n\t font-size:10px;\n}\n\n\n.pushrss-edit p{\n    font-size:12px;\n    margin-bottom:15px\n}\n\n\n .items{\n\t flex: 1;\n\t min-height:50px;\n\t height:400px;\n\t overflow-y: auto;\n\t overflow-x:hidden;\n\t margin-top:20px;\n}\n.msg-item .img{\n    width:20px;\n    height:auto;\n    font-size:15px;\n    display:flex;\n\t align-items: center;\n\t justify-content:center\n}\n\n .editgroup-items.items,.pushrss-info{\n\t margin-top:35px;\n}\n @keyframes floatScale {\n\t 0% {\n\t\t transform: translateY(0) scale(1);\n\t}\n\t 30% {\n\t\t transform: translateY(-3px) scale(1.05);\n\t}\n\t 50% {\n\t\t transform: translateY(-5px) scale(1.08);\n\t}\n\t 70% {\n\t\t transform: translateY(-2px) scale(1.03);\n\t}\n\t 85% {\n\t\t transform: translateY(-1px) scale(1.01);\n\t}\n\t 100% {\n\t\t transform: translateY(0) scale(1);\n\t}\n}\n \n @media (min-width: 640px) {\n\t .color-modal {\n\t\t max-width: 500px;\n\t\t left: 50%;\n\t\t transform: translate(-50%, 100%);\n\t\t border-radius: 24px;\n\t}\n\t .color-modal.active {\n\t\t transform: translate(-50%, 0);\n\t}\n}\n @media (max-height: 700px) {\n\t .modal-content {\n\t\t max-height: 50vh;\n\t}\n}",
    "type": 0
}
广告