├── icon.png ├── src ├── preload.js ├── main.js ├── renderer.js └── settings │ └── main.html ├── .github └── workflows │ └── upload-to-release.yml ├── README.md ├── manifest.json └── LICENSE /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hacker-frok/LiteLoaderQQNT-Plugin-Brevity-btn/HEAD/icon.png -------------------------------------------------------------------------------- /src/preload.js: -------------------------------------------------------------------------------- 1 | const { contextBridge, ipcRenderer } = require('electron') 2 | 3 | //on 监听主进程发来的消息 4 | //send 用于向主进程发送消息 5 | //invoke 发起异步调用到主进程,并等待返回结果 6 | contextBridge.exposeInMainWorld('btnBrevity', { 7 | 8 | updateSettings: (callback) => ipcRenderer.on("LiteLoader.btnBrevity.updateSettings", callback), 9 | getSettings: () => ipcRenderer.invoke("LiteLoader.btnBrevity.getSettings"), 10 | setSettings: content => ipcRenderer.invoke("LiteLoader.btnBrevity.setSettings", content), 11 | }) 12 | -------------------------------------------------------------------------------- /.github/workflows/upload-to-release.yml: -------------------------------------------------------------------------------- 1 | name: LiteLoaderQQNT-Plugin-Brevity-btn Release 2 | 3 | on: 4 | push: 5 | branches: 6 | - "main" 7 | tags: 8 | - "v*.*.*" 9 | workflow_dispatch: 10 | 11 | jobs: 12 | pre-release: 13 | name: "Pre Release" 14 | runs-on: "ubuntu-latest" 15 | 16 | steps: 17 | # ... 18 | - name: "Build & test" 19 | run: | 20 | echo "done!" 21 | - uses: "marvinpinto/action-automatic-releases@latest" 22 | with: 23 | repo_token: "${{ secrets.GAYHUB_TOKEN }}" 24 | automatic_release_tag: "test" 25 | prerelease: true 26 | title: "预构建版本" 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LiteLoaderQQNT-Plugin-Brevity-btn 2 | 3 | ### 隐藏QQNT整个侧栏部位,并在顶部添加快速显示/隐藏按钮,带来纯纯的聊天模式 4 | ### 目前仅支持LiteLoaderQQNT1.0.0及以上版本 5 | 6 | 7 | ## 用法 8 | 9 | 1. 安装 [LiteLoaderQQNT](https://github.com/mo-jinran/LiteLoaderQQNT) 本体 10 | 2. 在 LiteLoaderQQNT 插件目录克隆本仓库(或下载仓库源码放入插件目录) 11 | 3. 启动/重启 QQNT 12 | 4. 在右上角会出现图标 13 | 5. 点击开关图标 14 | 15 | ## 截图 16 | ![image](https://github.com/hacker-frok/LiteLoaderQQNT-Plugin-Brevity-btn/assets/157203458/3c01a41c-9eb5-4e20-802e-0be841df4871) 17 | 18 | ![image](https://github.com/hacker-frok/LiteLoaderQQNT-Plugin-Brevity-btn/assets/157203458/b0af0300-0bf5-41f8-b07e-49c15cb5ab12) 19 | ![image](https://github.com/hacker-frok/LiteLoaderQQNT-Plugin-Brevity-btn/assets/157203458/4ac4517a-a931-4975-96f7-f9a46386563f) 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 4, 3 | "type": "extension", 4 | "name": "QQ纯享模式", 5 | "slug": "brevity-btn", 6 | "description": "完全隐藏QQNT整个左边侧栏部位,并在顶部添加快速显示/隐藏按钮,不用再烦恼左边杂乱的图标了,带来纯纯的聊天模式.", 7 | "version": "1.0.7", 8 | "icon": "./icon.png", 9 | "thumbnail": "./icon.png", 10 | "authors": [ 11 | { 12 | "name": "hacker", 13 | "link": "https://github.com/hacker-frok" 14 | } 15 | ], 16 | "repository": { 17 | "repo": "hacker-frok/LiteLoaderQQNT-Plugin-Brevity-btn", 18 | "branch": "main", 19 | "release": { 20 | "tag": "1.0.7" 21 | } 22 | }, 23 | "platform": [ 24 | "win32", 25 | "linux", 26 | "darwin" 27 | ], 28 | "injects": { 29 | "renderer": "./src/renderer.js", 30 | "main": "./src/main.js", 31 | "preload": "./src/preload.js" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 wintrue 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | 2 | const { BrowserWindow, ipcMain, webContents } = require('electron') 3 | const fs = require('fs') 4 | const path = require('path') 5 | 6 | let mainWindow 7 | const pluginDataPath = LiteLoader.plugins["brevity-btn"].path.data; 8 | const settingsPath = path.join(pluginDataPath, "settings.json"); 9 | function log(...args) { 10 | console.log(`\x1b[31m[QQ纯享模式]\x1b[0m`, ...args); 11 | } 12 | // fs判断插件路径是否存在,如果不存在则创建(同时创建父目录(如果不存在的话)) 13 | if (!fs.existsSync(pluginDataPath)) { 14 | fs.mkdirSync(pluginDataPath, { recursive: true }); 15 | } 16 | // 判断settings.json是否存在,如果不存在则创建 17 | const defaultIcon = ' ' 18 | if (!fs.existsSync(settingsPath)) { 19 | fs.writeFileSync(settingsPath, JSON.stringify({ 20 | "mini": false, 21 | "icon": defaultIcon, 22 | })); 23 | } else { 24 | const data = fs.readFileSync(settingsPath, "utf-8"); 25 | const config = JSON.parse(data); 26 | if (config.mini == undefined || config.mini == null) { 27 | config.mini = false; 28 | } 29 | if (config.icon) { 30 | config.icon = defaultIcon 31 | } 32 | 33 | } 34 | ipcMain.handle( 35 | "LiteLoader.btnBrevity.getSettings", 36 | (event, message) => { 37 | try { 38 | const data = fs.readFileSync(settingsPath, "utf-8"); 39 | const config = JSON.parse(data); 40 | return config; 41 | } catch (error) { 42 | log(error); 43 | return {}; 44 | } 45 | } 46 | ); 47 | 48 | ipcMain.handle( 49 | "LiteLoader.btnBrevity.setSettings", 50 | (event, content) => { 51 | try { 52 | const new_config = JSON.stringify(content); 53 | fs.writeFileSync(settingsPath, new_config, "utf-8"); 54 | 55 | if (mainWindow) { 56 | mainWindow.webContents.send( 57 | "LiteLoader.btnBrevity.updateSettings", 58 | content 59 | ); 60 | } else { 61 | webContents.getAllWebContents().forEach((webContent) => { 62 | webContent.send( 63 | "LiteLoader.btnBrevity.updateSettings", 64 | content 65 | ); 66 | }); 67 | } 68 | 69 | 70 | } catch (error) { 71 | log(error); 72 | } 73 | } 74 | ); 75 | 76 | // 防抖函数 77 | function debounce(fn, time) { 78 | let timer = null; 79 | return function (...args) { 80 | timer && clearTimeout(timer); 81 | timer = setTimeout(() => { 82 | fn.apply(this, args); 83 | }, time); 84 | } 85 | } 86 | 87 | 88 | // 创建窗口时触发 89 | exports.onBrowserWindowCreated = (window) => { 90 | window.webContents.on("did-stop-loading", () => { 91 | if (window.webContents.getURL().indexOf("#/main/message") !== -1) { 92 | mainWindow = window; 93 | } 94 | }); 95 | 96 | } 97 | -------------------------------------------------------------------------------- /src/renderer.js: -------------------------------------------------------------------------------- 1 | const plugin_path = LiteLoader.plugins["brevity-btn"].path.plugin; 2 | function log(...args) { 3 | console.log(`\x1b[31m[QQ纯享模式]\x1b[0m`, ...args); 4 | } 5 | let settingsConfig = await btnBrevity.getSettings(); 6 | 7 | let macOSstyle = `right: 12px!important;left: inherit;top: 4px!important;position: fixed;app-region: no-drag;z-index: 99;cursor: pointer;pointer-events: all;right: 12px !important;` 8 | if (LiteLoader.os.platform != "darwin") { 9 | macOSstyle = '' 10 | } 11 | const BTN_HTML = ` 12 |
13 | 14 | ${settingsConfig.icon} 15 | 16 |
17 | ` 18 | function isCssSectionExist(sectionId) { 19 | return !!document.querySelector('style#' + sectionId); 20 | } 21 | 22 | function insertCssSection(sectionId, cssCode) { 23 | if (!isCssSectionExist(sectionId)) { 24 | var style = document.createElement('style'); 25 | style.id = sectionId; 26 | style.appendChild(document.createTextNode(cssCode)); 27 | document.head.appendChild(style); 28 | } 29 | } 30 | 31 | function removeCssSection(sectionId) { 32 | try { 33 | document.querySelector('style#' + sectionId).remove() 34 | } catch (error) { 35 | 36 | } 37 | 38 | } 39 | 40 | var customCssCode = ` 41 | /* ----- 隐藏式侧栏 ------ */ 42 | 43 | .sidebar-nav .sidebar__upper .qq-logo { 44 | display: none!important; 45 | } 46 | .sidebar-nav .sidebar__upper.sidebar__upper--with-logo .sidebar__avatar { 47 | margin-top: 8px; 48 | } 49 | .contact-top-bar .top-bar__search{ 50 | padding-left: 28px!important; 51 | } 52 | .contact-top-bar .top-bar__search .q-input__inner{ 53 | padding-left: 12px!important; 54 | } 55 | 56 | .container .sidebar { 57 | position: absolute; 58 | left: -85px; 59 | width: 85px !important; 60 | transition: all .3s ease-in-out; 61 | overflow: visible; 62 | & * { 63 | overflow: visible !important; 64 | } 65 | z-index: 999; 66 | .sidebar-nav { 67 | width: 85px; 68 | } 69 | .sidebar__avatar { 70 | transform: scale(calc(32 / 36)) translateX(80px) translateY(-5px); 71 | transition: all .3s ease-in-out; 72 | } 73 | 74 | } 75 | 76 | .container .contact-top-bar { 77 | padding-left: 25px !important; 78 | .q-input__prefix { 79 | display: none !important; 80 | } 81 | } 82 | /**macOS***/ 83 | body[q-platform="darwin"] .container .contact-top-bar { 84 | padding-top: 35px!important; 85 | } 86 | body[q-platform="darwin"] .nostalgic-qq-icon{ 87 | right: 12px!important; 88 | left: inherit; 89 | top: 4px; 90 | } 91 | body[q-platform="darwin"] .nostalgic-user-avatar{ 92 | margin-top: 15px!important; 93 | } 94 | body[q-platform="darwin"] .sidebar__upper { 95 | padding-top: 34px!important; 96 | } 97 | /* ------------------- */ 98 | `; 99 | 100 | 101 | const switchStatus = (status) => { 102 | if (status) { 103 | 104 | if (!isCssSectionExist('BrevityBtn')) { 105 | insertCssSection('BrevityBtn', customCssCode); 106 | document.querySelector('#brevityBtn').style.transform = 'rotate(-90deg)' 107 | // 检查是否存在.qq-logo元素 108 | let qqLogo = document.querySelector('.sidebar-nav .sidebar__upper .qq-logo'); 109 | if (qqLogo) { 110 | // 如果存在,给.sidebar__upper添加一个特定的类 111 | let sidebarUpper = document.querySelector('.sidebar-nav .sidebar__upper'); 112 | sidebarUpper.classList.add('sidebar__upper--with-logo'); 113 | } 114 | } 115 | 116 | } else { 117 | 118 | if (isCssSectionExist('BrevityBtn')) { 119 | removeCssSection('BrevityBtn') 120 | document.querySelector('#brevityBtn').style.transform = 'rotate(0deg)' 121 | } 122 | 123 | } 124 | 125 | } 126 | 127 | const findFuncMenuInterval = setInterval(() => { 128 | // 获取功能菜单 129 | let areaMenu = document.querySelector('.window-control-area') 130 | if (LiteLoader.os.platform === "darwin") { 131 | areaMenu = document.querySelector('#app') 132 | 133 | } 134 | const funcMenu = document.querySelector('.func-menu') 135 | if (areaMenu && funcMenu) { 136 | clearInterval(findFuncMenuInterval) 137 | // 插入按钮 138 | areaMenu.insertAdjacentHTML('afterbegin', BTN_HTML) 139 | // 监听按钮点击 140 | const brevityBtn = document.querySelector('#brevityBtn') 141 | switchStatus(settingsConfig.mini) 142 | brevityBtn.addEventListener('click', () => { 143 | 144 | if (isCssSectionExist('BrevityBtn')) { 145 | removeCssSection('BrevityBtn') 146 | document.querySelector('#brevityBtn').style.transform = 'scaleX(1)' 147 | settingsConfig.mini = false 148 | } else { 149 | insertCssSection('BrevityBtn', customCssCode); 150 | document.querySelector('#brevityBtn').style.transform = 'scaleX(-1)' 151 | settingsConfig.mini = true 152 | } 153 | btnBrevity.setSettings(settingsConfig); 154 | }) 155 | } 156 | //监听主进程发来的消息 157 | btnBrevity.updateSettings((event, config) => { 158 | settingsConfig.icon = config.icon 159 | const btn = document.querySelector('#brevityBtn') 160 | if (btn) { 161 | btn.innerHTML = `${config.icon}` 162 | } 163 | }); 164 | }, 100) 165 | 166 | 167 | // 打开设置界面时触发 168 | export const onSettingWindowCreated = async view => { 169 | log('打开设置界面') 170 | try { 171 | //设置设置界面的图标 172 | settingsConfig = await btnBrevity.getSettings(); 173 | document.querySelectorAll(".nav-item.liteloader").forEach(node => { 174 | //log(node.textContent) 175 | if (node.textContent === "QQ纯享模式") { 176 | node.classList.add("btnBrevity") 177 | node.classList.add('appearance') 178 | const htmlicon = `` 179 | node.querySelector(".q-icon.icon").insertAdjacentHTML('afterbegin', htmlicon) 180 | } 181 | }) 182 | 183 | const html_file_path = `local:///${plugin_path}/src/settings/main.html`; 184 | 185 | view.innerHTML = await (await fetch(html_file_path)).text(); 186 | 187 | const items = view.querySelectorAll(`.btnBrevity-icon-item`); 188 | let index = 0 189 | items.forEach(function (item) { 190 | index++ 191 | if (settingsConfig.icon.indexOf(`btnBrevity-icon${index}"`) != -1||settingsConfig.icon.indexOf(`btnBrevity-icon${index}\\"`) != -1) { 192 | item.classList.add("btnBrevity-icon-item-select") 193 | } 194 | item.addEventListener('click', function (event) { 195 | items.forEach((item) => { 196 | item.classList.remove("btnBrevity-icon-item-select") 197 | }) 198 | const isActive = event.currentTarget.classList.contains('btnBrevity-icon-item-select') 199 | if (!isActive) { 200 | event.currentTarget.classList.add("btnBrevity-icon-item-select") 201 | settingsConfig.icon = event.currentTarget.innerHTML; 202 | btnBrevity.setSettings(settingsConfig); 203 | } 204 | 205 | }); 206 | }); 207 | 208 | 209 | } catch (error) { 210 | log("[设置页面错误]", error); 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /src/settings/main.html: -------------------------------------------------------------------------------- 1 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 |
96 |
97 | 99 | 101 | 102 | 103 | 104 |
105 |
106 | 108 | 111 | 114 | 115 | 116 |
117 |
118 | 120 | 123 | 124 | 125 | 126 |
127 |
128 | 130 | 133 | 134 | 135 |
136 |
137 | 139 | 142 | 143 | 144 |
145 |
146 | 148 | 151 | 152 | 153 |
154 |
155 | 157 | 160 | 161 | 162 | 163 |
164 |
165 | 167 | 170 | 173 | 176 | 177 | 178 |
179 |
180 | 182 | 185 | 188 | 189 | 190 |
191 |
192 | 194 | 197 | 200 | 203 | 204 | 205 |
206 |
207 | 208 | 209 | 210 | 211 |
212 |
213 |
214 | 215 | 216 | 217 | 218 |
219 | 自定义图标 220 | 提交自定义图标 221 |
222 | 223 |
224 | 225 |
226 |
227 |
--------------------------------------------------------------------------------