├── .gitignore
├── res
├── icon.png
└── preview.png
├── src
├── setting.json
├── css
│ ├── login.css
│ ├── sidebar.css
│ ├── app.css
│ ├── color.css
│ ├── setting.css
│ ├── chatpan.css
│ ├── sidelist.css
│ └── style.css
├── preload.js
├── config.html
├── main.js
└── renderer.js
├── package.json
├── README.md
├── manifest.json
└── LICENSE
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 |
--------------------------------------------------------------------------------
/res/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Stapxs/Stapxs-QQ-Lite-Theme/HEAD/res/icon.png
--------------------------------------------------------------------------------
/src/setting.json:
--------------------------------------------------------------------------------
1 | {
2 | "color": "0",
3 | "bg": "",
4 | "opacity": 5
5 | }
--------------------------------------------------------------------------------
/res/preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Stapxs/Stapxs-QQ-Lite-Theme/HEAD/res/preview.png
--------------------------------------------------------------------------------
/src/css/login.css:
--------------------------------------------------------------------------------
1 | #login {
2 | background-color: var(--color-card-1);
3 | }
4 | #login canvas {
5 | display: none;
6 | }
7 | #login div.operation > span {
8 | color: var(--color-main);
9 | }
10 |
11 | .qq-logo-icon {
12 | display: none;
13 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bcui-theme",
3 | "private": true,
4 | "version": "1.0.6",
5 | "description": "",
6 | "main": "index.js",
7 | "scripts": {},
8 | "keywords": [],
9 | "author": "Staps Steve",
10 | "license": "MIT",
11 | "devDependencies": {}
12 | }
13 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
3 |
4 | 主题色
5 |
13 |
14 |
15 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | 背景图模糊
26 |
27 |
28 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 huan_kong
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/css/sidebar.css:
--------------------------------------------------------------------------------
1 | /* 侧边栏 */
2 | .sidebar {
3 | background: transparent !important;
4 | }
5 | .sidebar > nav {
6 | background: transparent !important;
7 | }
8 | .qq-logo,
9 | .sidebar__avatar {
10 | display: none !important;
11 | }
12 |
13 | .sidebar__nav div.tab-item > div.is-active {
14 | background: var(--color-main) !important;
15 | opacity: 1 !important;
16 | }
17 | .sidebar__nav div.tab-item > div.is-active:hover { /* 去除鼠标悬停颜色 */
18 | background: var(--color-main) !important;
19 | opacity: 1 !important;
20 | }
21 | .sidebar__nav div.tab-item > div.is-active + div.tab-item-img {
22 | filter: brightness(0) invert(1);
23 | }
24 | /* 隐藏 .sidebar__nav > div 超过第二个的元素 */
25 | /* .sidebar__nav > div:nth-child(n+3) {
26 | display: none !important;
27 | } */
28 |
29 | /* 通话 */
30 | .avatar--phone i {
31 | color: var(--color-font) !important;
32 | }
33 | .phone_bg {
34 | width: calc(100% - 5px) !important;
35 | border-radius: 7px !important;
36 | }
37 | .av-call-status {
38 | border: 1px solid var(--color-card-2) !important;
39 | background: var(--color-card-1) !important;
40 | width: calc(100% - 40px) !important;
41 | margin: 10px 20px 0 20px;
42 | border-radius: 7px;
43 | }
44 |
45 | @media (prefers-color-scheme: dark) {
46 | .sidebar__nav div.tab-item > div.is-active + div.tab-item-img {
47 | filter: brightness(0);
48 | }
49 | }
--------------------------------------------------------------------------------
/src/css/app.css:
--------------------------------------------------------------------------------
1 | .q-button {
2 | background: var(--color-main);
3 | color: var(--color-font-r);
4 | border: unset;
5 | }
6 | .q-button:hover {
7 | background: var(--color-main) !important;
8 | opacity: 0.8;
9 | }
10 | .q-button.loading {
11 | background: var(--color-main) !important;
12 | opacity: 0.5;
13 | }
14 |
15 | .q-context-menu {
16 | min-width: 150px;
17 | }
18 | .q-context-menu > a {
19 | flex-direction: row-reverse;
20 | display: flex;
21 | }
22 | .q-context-menu > div > a > div { /* 有二级菜单的项的图标就不要了 */
23 | display: none;
24 | }
25 | .q-context-menu > a > div { /* 有二级菜单的项的图标就不要了 */
26 | margin-right: 10px;
27 | }
28 | .q-context-menu > div > a > span,
29 | .q-context-menu > a > span {
30 | margin-right: 10px;
31 | }
32 | .q-context-menu > a:hover {
33 | background: var(--color-main);
34 | color: var(--color-font-r);
35 | }
36 | .q-context-menu > a > div {
37 | margin-right: 0;
38 | }
39 |
40 | .q-notification {
41 | background: var(--color-card-1) !important;
42 | }
43 |
44 | .q-input {
45 | border-bottom: 2px solid var(--color-main) !important;
46 | border-radius: 7px !important;
47 | height: 32px;
48 | }
49 |
50 | .q-switch.is-active:hover,
51 | .q-switch.is-active {
52 | background: var(--color-main);
53 | }
54 |
55 | .q-empty-page {
56 | background: transparent;
57 | box-shadow: unset;
58 | }
59 |
60 | .q-svg-icon:hover {
61 | color: var(--color-main) !important;
62 | }
63 |
64 | /* =================================================== */
65 |
66 | .container > div.tab-container {
67 | margin-left: -16px;
68 | }
69 |
--------------------------------------------------------------------------------
/src/css/color.css:
--------------------------------------------------------------------------------
1 | @media (prefers-color-scheme: light) {
2 | :root {
3 | /* 卡片颜色 */
4 | --color-bg: #F8F9FA;
5 | --color-card: #FFFFFF;
6 | --color-card-1: #F1F3F5;
7 | --color-card-2: #e3e8ec;
8 |
9 | --color-bg-rgb: 248, 249, 250;
10 | --color-card-rgb: 255, 255, 255;
11 | --color-card-1-rgb: 241, 243, 245;
12 | --color-card-2-rgb: 227, 232, 236;
13 |
14 | /* 字体颜色 */
15 | --color-font: #50534F;
16 | --color-font-1: #5c5f5a;
17 | --color-font-2: #7d817c;
18 |
19 | /* 字体颜色(反转) */
20 | --color-font-r: var(--color-bg);
21 | --color-font-1-r: var(--color-card);
22 |
23 | /* 阴影颜色 */
24 | --color-shader: #72727240;
25 |
26 | /* 主题色 */
27 | --color-main: #606E7A;
28 |
29 | /* 内置主题色*/
30 | --color-main-0: #606E7A;
31 | --color-main-1: #92aa8a;
32 | --color-main-2: #f0a1a8;
33 | --color-main-3: #8076a3;
34 | --color-main-4: #f9a633;
35 | --color-main-5: #50534f;
36 | }
37 | }
38 |
39 | @media (prefers-color-scheme: dark) {
40 | :root {
41 | /* 卡片颜色 */
42 | --color-bg: #2D2D2D;
43 | --color-card: #3A3A3A;
44 | --color-card-1: #494949;
45 | --color-card-2: #5F5F5F;
46 |
47 | --color-bg-rgb: 45, 45, 45;
48 | --color-card-rgb: 58, 58, 58;
49 | --color-card-1-rgb: 73, 73, 73;
50 | --color-card-2-rgb: 95, 95, 95;
51 |
52 | /* 字体颜色 */
53 | --color-font: #FFFFFF;
54 | --color-font-1: #CFCFCF;
55 | --color-font-2: #B0B0B0;
56 |
57 | /* 字体颜色(反转) */
58 | --color-font-r: var(--color-bg);
59 | --color-font-1-r: var(--color-card);
60 |
61 | /* 阴影颜色 */
62 | --color-shader: #1010109c;
63 |
64 | /* 主题色 */
65 | --color-main: #c8e5ff;
66 |
67 | /* 内置主题色*/
68 | --color-main-0: #c8e5ff;
69 | --color-main-1: #d4ffcf;
70 | --color-main-2: #ffafaa;
71 | --color-main-3: #cdb7ff;
72 | --color-main-4: #f9d27d;
73 | --color-main-5: #bcbfc7;
74 | }
75 | }
--------------------------------------------------------------------------------
/src/css/setting.css:
--------------------------------------------------------------------------------
1 | .liteloader > div img {
2 | width: 20px;
3 | height: 20px;
4 | }
5 |
6 | /* 覆盖原始设置样式 */
7 | .setting-item-title {
8 | color: var(--color-font);
9 | font-weight: var(--font-bold);
10 | font-size: min(var(--font_size_3),18px);
11 | line-height: min(var(--line_height_3),24px);
12 | margin: 0px 0px 8px 16px;
13 | }
14 | .chat-page {
15 | background-color: var(--color-card);
16 | border-top-left-radius: 8px;
17 | border-top-right-radius: 8px;
18 | border-bottom-right-radius: 8px;
19 | border-bottom-left-radius: 8px;
20 | margin-bottom: 24px;
21 | padding: 0px 16px;
22 | }
23 | .chat-page__item {
24 | align-items: center;
25 | display: flex;
26 | justify-content: space-between;
27 | padding: 12px 0px;
28 | }
29 |
30 | /* 附加设置样式 */
31 | .chat-page__item div.tips {
32 | color: var(--color-font-2);
33 | margin-top: 4px;
34 | font-size: min(var(--font_size_2) ,16px);
35 | line-height: min(var(--line_height_2) ,22px);
36 | }
37 | .chat-page__item > select {
38 | border: 1px solid var(--color-card-2);
39 | height: 24px;
40 | width: 150px;
41 | }
42 | .chat-page__item button.ss-button {
43 | margin: 5px;
44 | font-weight: normal;
45 | font-size: 0.7rem;
46 | height: 24px;
47 | width: 100px;
48 | }
49 | .chat-page__item input[type="range"] {
50 | -webkit-appearance: none;
51 | background: var(--color-card-1);
52 | height: 5px;
53 | border-radius: 5px;
54 | }
55 | .chat-page__item input[type="range"]::-webkit-slider-thumb,
56 | .chat-page__item input[type="range"]::-moz-range-thumb {
57 | -webkit-appearance: none;
58 | width: 20px;
59 | height: 20px;
60 | background: var(--color-main);
61 | border-radius: 50%;
62 | cursor: pointer;
63 | }
64 |
65 | /* 设置页面顶栏 */
66 | .main-card {
67 | display: flex;
68 | flex-direction: row;
69 | align-items: stretch;
70 | overflow: hidden;
71 | margin-top: -10px;
72 | }
73 | .main-card > div.bg-left {
74 | width: calc(100px + 10%);
75 | background: var(--color-main);
76 | margin: -200px 0 -200px -100px;
77 | transform: rotate(30deg);
78 | border: 10px solid var(--color-card-2);
79 | }
80 | .main-card > div.bg-right {
81 | width: calc(100px + 10%);
82 | background: var(--color-main);
83 | margin: -200px -100px -200px 0;
84 | transform: rotate(30deg);
85 | border: 10px solid var(--color-card-2);
86 | }
87 | .info-card {
88 | display: flex;
89 | flex-direction: column;
90 | align-items: center;
91 | flex: 1;
92 | }
93 | .info-card > svg {
94 | height: 3rem;
95 | fill: var(--color-main);
96 | }
97 | .info-card > span {
98 | display: block;
99 | }
100 | .info-card > span.title {
101 | font-size: 1.1rem;
102 | color: var(--color-main);
103 | margin-top: 10px;
104 | }
105 | .info-card > span.title > span {
106 | font-size: 0.8rem;
107 | color: var(--color-font-2);
108 | }
109 | .info-card > a {
110 | color: var(--color-font-r);
111 | background: var(--color-main);
112 | padding: 3px 15px;
113 | border-radius: 5rem;
114 | margin-top: 5px;
115 | font-size: 0.8rem;
116 | }
117 | .info-copy {
118 | margin-bottom: 40px;
119 | }
120 | .info-copy > span {
121 | width: 100%;
122 | display: block;
123 | text-align: center;
124 | font-size: 0.8rem;
125 | margin-top: 5px;
126 | color: var(--color-font-2);
127 | }
--------------------------------------------------------------------------------
/src/css/chatpan.css:
--------------------------------------------------------------------------------
1 | .two-col-layout__main {
2 | box-shadow: -5px 0 4px -5px var(--color-shader);
3 | background-size: cover !important;
4 | background: var(--color-card);
5 | margin-left: 5px;
6 | }
7 | .aio {
8 | background: rgba(var(--color-card-rgb), 0.3);
9 | }
10 | .aio .chat-input-area {
11 | background: var(--color-card-1);
12 | border-radius: 7px;
13 | padding: 10px 0px;
14 | margin: 20px;
15 | z-index: 9;
16 | }
17 | .send-btn-wrap {
18 | background: var(--color-main) !important;
19 | }
20 |
21 | .chat-header {
22 | margin: 20px;
23 | border-radius: 7px;
24 | display: flex !important;
25 | align-items: center !important;
26 | padding: 0 20px !important;
27 | background: rgba(var(--color-card-1-rgb),.8);
28 | backdrop-filter: blur(50px);
29 | z-index: 10;
30 | box-shadow: 0 0 5px var(--color-shader);
31 | border: none !important;
32 | }
33 | body[q-platform="win32"] .chat-header,
34 | body[q-platform="linux"] .chat-header {
35 | margin-top: 30px !important;
36 | }
37 | body[q-platform="darwin"] .chat-header {
38 | max-height: 100px !important;
39 | }
40 |
41 | .group-chat {
42 | margin-top: -20px;
43 | height: calc(100% + 20px) !important;
44 | }
45 |
46 | /* 消息 */
47 | .message-content__wrapper > div {
48 | background: var(--color-card-1) !important;
49 | }
50 | .message-content__wrapper >div.container--self {
51 | background: var(--color-main) !important;
52 | color: var(--color-font-r) !important;
53 | }
54 | .message-content__wrapper >div.container--self span {
55 | color: var(--color-font-r) !important;
56 | }
57 | .message-content__wrapper >div.container--self div.reply-element {
58 | border-color: var(--color-font-r);
59 | }
60 | .chat-msg-area__tip--bottom {
61 | transform: translateY(10px);
62 | margin-right: 25px !important;
63 | }
64 | .chat-msg-area__tip--top > div.q-notification {
65 | transform: translateY(120%);
66 | margin-right: 25px;
67 | }
68 | .babble {
69 | background: var(--color-card-1) !important;
70 | }
71 |
72 | /* 群/好友信息栏 */
73 | .side-panel {
74 | margin-top: 10px;
75 | height: calc(100vh - 110px) !important;
76 | border-radius: 7px;
77 | box-shadow: 0 0 5px var(--color-shader);
78 | }
79 | .side-panel > div.panel-content > div {
80 | padding: 5px 10px !important;
81 | background: var(--color-card);
82 | border-radius: 7px;
83 | }
84 | .group-member {
85 | background: var(--color-card-1) !important;
86 | }
87 | .group-panel > div:last-child {
88 | background: var(--color-card-1);
89 | margin-bottom: 20px;
90 | /* margin-right: 20px; */
91 | width: 220px;
92 | }
93 | .group-panel div.group-box {
94 | border-radius: 7px;
95 | overflow: hidden;
96 | }
97 |
98 | /* 附加面板 */
99 | .expression-panel-inner {
100 | margin-bottom: 10px;
101 | }
102 | .expression-panel-inner > div {
103 | box-shadow: 0 0 5px var(--color-shader) !important;
104 | background: var(--color-card-1) !important;
105 | }
106 |
107 | .sticker-panel {
108 | height: 300px !important;
109 | }
110 | .sticker-panel > div:last-child > div:first-child {
111 | height: 250px !important;
112 | }
113 | .sticker-panel div.tabs-container-item-active:hover,
114 | .sticker-panel div.tabs-container-item-active {
115 | background: var(--color-main);
116 | }
117 | .sticker-list-item {
118 | border: 2px solid var(--color-card-2);
119 | border-radius: 7px;
120 | margin-top: 5px;
121 | padding: 2px;
122 | }
123 |
124 | /* 输入框 */
125 | .aio .chat-input-area {
126 | background: var(--color-card-1);
127 | border-radius: 7px;
128 | margin: 20px;
129 | z-index: 9;
130 | }
131 |
132 | @media (prefers-color-scheme: dark) {
133 | .sticker-panel div.tabs-container-item-active svg,
134 | .message-content__wrapper > div.container--self svg {
135 | filter: brightness(0);
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/src/css/sidelist.css:
--------------------------------------------------------------------------------
1 | /* 侧边列表 */
2 |
3 | .tab-container {
4 | background: transparent !important;
5 | }
6 |
7 |
8 | .recent-contact {
9 | background: none !important;
10 | }
11 | .recent-contact .viewport-list__inner {
12 | display: flex;
13 | flex-direction: column;
14 | gap: 8px;
15 | padding: 8px;
16 | }
17 |
18 | .recent-contact-list div.viewport-list__inner > div.recent-contact-item {
19 | border-radius: 7px;
20 | }
21 | .recent-contact-list div.viewport-list__inner > div.recent-contact-item > div {
22 | padding: 10px 15px !important;
23 | }
24 | body[q-theme="dark"][is-simple-theme="true"] .recent-contact-list div.viewport-list__inner > div.recent-contact-item--selected .item__content,
25 | .recent-contact-list div.viewport-list__inner > div.recent-contact-item--selected .item__content {
26 | background: var(--color-main) !important;
27 | }
28 | body[q-theme="dark"][is-simple-theme="true"] .recent-contact-list div.viewport-list__inner > div.recent-contact-item--selected .item__content div,
29 | .recent-contact-list div.viewport-list__inner > div.recent-contact-item--selected .item__content i,
30 | .recent-contact-list div.viewport-list__inner > div.recent-contact-item--selected .item__content div {
31 | color: var(--color-font-r) !important;
32 | }
33 |
34 | .radio-tab__item--background {
35 | background: var(--color-main) !important;
36 | }
37 | .radio-tab__item--selected {
38 | color: var(--color-font-r) !important;
39 | }
40 |
41 | .list-toggler {
42 | padding-top: 90px;
43 | pointer-events: none;
44 | }
45 | .list-toggler > div {
46 | pointer-events: all;
47 | }
48 | .list-toggler::before {
49 | color: var(--color-main) !important;
50 | margin-bottom: 10px;
51 | position: relative;
52 | margin-left: 20px;
53 | margin-top: -60px;
54 | font-weight: bold;
55 | text-wrap: nowrap;
56 | content: '信息';
57 | }
58 | .list-toggler > div {
59 | margin-left: -45px;
60 | }
61 | .recent-contact-list, .group-assistent-list {
62 | padding-right: 4px;
63 | }
64 | .recent-contact-list::-webkit-scrollbar, /* 消息列表滚动条 */
65 | .group-assistent-list::-webkit-scrollbar {
66 | display: none !important;
67 | }
68 |
69 | .contact-top-bar {
70 | margin-bottom: -100px;
71 | margin-top: 30px;
72 | }
73 |
74 | .list-item:hover {
75 | background-color: rgba(var(--color-card-1) 0.1) !important;
76 | }
77 | .list-item__container > div.list-item__content {
78 | padding: 10px !important;
79 | }
80 | .recent-contact-item .avatar {
81 | outline: 2px solid var(--color-card-2);
82 | border-radius: 7px !important;
83 | height: 33px !important;
84 | width: 33px !important;
85 | }
86 |
87 | .contact,
88 | .q-collapse-item__header,
89 | .contact div.tab-header {
90 | background: transparent !important;
91 | }
92 | .contact > div:last-child {
93 | margin-top: 25px;
94 | }
95 | .contact > div:last-child > div:first-child {
96 | margin-top: 40px;
97 | }
98 | .contact > div:last-child::before {
99 | content: '联系人';
100 | font-weight: bold;
101 | margin-left: 20px;
102 | color: var(--color-main);
103 | }
104 |
105 | .q-collapse-item__icon {
106 | background: var(--color-main);
107 | height: 40%;
108 | width: 5px;
109 | border-radius: 7px;
110 | }
111 | .q-collapse-item__icon > i {
112 | display: none;
113 | }
114 |
115 | .main-search > div:nth-child(2) > div {
116 | box-shadow: 0 0 5px var(--color-shader);
117 | height: calc(100% - 100px) !important;
118 | margin-top: 30px;
119 | margin-left: 5%;
120 | overflow: hidden;
121 | width: calc(95% - 5px);
122 | background: var(--color-card-1);
123 | border-radius: 7px;
124 | padding-top: 10px;
125 | }
126 |
127 | .network-tip {
128 | top: 100%;
129 | position: absolute;
130 | width: 95%;
131 | transform: translateY(calc(-100% - 10px));
132 | z-index: 11;
133 | border-radius: 7px;
134 | box-shadow: 0 0 5px var(--color-shader);
135 | }
136 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs')
2 | const path = require('path')
3 | const { BrowserWindow, ipcMain, dialog } = require('electron')
4 |
5 | const pluginPath = LiteLoader.plugins['border-card-ui-theme'].path.plugin.replaceAll('\\', '/')
6 | const dataPath = LiteLoader.plugins['border-card-ui-theme'].path.data.replaceAll('\\', '/')
7 | const settingPath = path.join(dataPath, 'setting.json').replaceAll('\\', '/')
8 |
9 | const log = (...args) => {
10 | console.log('\x1b[36m[bcui-theme]\x1b[0m', ...args)
11 | }
12 | const error = (...args) => {
13 | console.log('\x1b[31m[bcui-theme]\x1b[0m', ...args)
14 | }
15 |
16 | // 获取当前有哪些css
17 | const cssPath = path.join(__dirname, 'css')
18 | // 寻找这个文件夹下所有的 .css 后缀文件(包括子文件夹)
19 | function getAllCssFiles(dirPath, arrayOfFiles) {
20 | const files = fs.readdirSync(dirPath)
21 |
22 | arrayOfFiles = arrayOfFiles || []
23 |
24 | files.forEach((file) => {
25 | const filePath = path.join(dirPath, file)
26 | if (fs.statSync(filePath).isDirectory()) {
27 | arrayOfFiles = getAllCssFiles(filePath, arrayOfFiles)
28 | } else if (file.endsWith('.css')) {
29 | arrayOfFiles.push(filePath)
30 | }
31 | })
32 |
33 | return arrayOfFiles
34 | }
35 |
36 | const cssFiles = getAllCssFiles(cssPath)
37 |
38 | /*
39 | 主线程方法 ==============================================
40 | */
41 |
42 | // 更新样式
43 | function updateStyle(webContents) {
44 | cssFiles.forEach((filePath) => {
45 | const data = fs.readFileSync(filePath, 'utf-8')
46 | const id = filePath.substring(filePath.lastIndexOf('\\') + 1, filePath.lastIndexOf('.'))
47 | webContents.send('LiteLoader.bcui_theme.updateStyle', id, data)
48 | })
49 | }
50 |
51 | // 获取设置
52 | function getSetting() {
53 | try {
54 | log('正在获取全量设置……')
55 | let rawdata = fs.readFileSync(settingPath)
56 | return JSON.parse(rawdata)
57 | } catch (err) {
58 | error('获取全量设置失败:', err.toString())
59 | return null
60 | }
61 | }
62 | // 设置设置
63 | function setSetting(k, v) {
64 | try {
65 | if (!k || v === undefined) {
66 | throw Error('k 或 v 为空')
67 | }
68 | let data = fs.readFileSync(settingPath, 'utf8')
69 | let setting = JSON.parse(data)
70 | setting[k] = v
71 | const updatedData = JSON.stringify(setting, null, 4)
72 | fs.writeFileSync(settingPath, updatedData, 'utf8')
73 | log('保存设置:' + k + ' -> ' + v + ' 成功')
74 |
75 | const nowConfig = getSetting()
76 | const window = BrowserWindow.getAllWindows()
77 | window.forEach((win) => {
78 | win.webContents.send('LiteLoader.bcui_theme.updateTheme', nowConfig)
79 | })
80 | } catch (err) {
81 | error('设置设置失败:', err.toString())
82 | }
83 | }
84 |
85 | function chooseImage() {
86 | dialog.showOpenDialog({
87 | properties: ['openFile'],
88 | filters: [
89 | { name: 'Images', extensions: ['jpg', 'png', 'gif', 'webp'] },
90 | { name: 'All Files', extensions: ['*'] },
91 | ],
92 | })
93 | .then((result) => {
94 | try {
95 | let imagePath = result.filePaths[0]
96 | if (!imagePath) {
97 | return
98 | }
99 | // 将文件复制到 dataPath,名称固定为 bg.*
100 | const fileName = path.basename(imagePath)
101 | const image = fs.readFileSync(imagePath)
102 | const ext = path.extname(imagePath)
103 | const bgPath = path.join(dataPath, 'bg' + ext)
104 | // 如果文件存在就删掉它
105 | if (fs.existsSync(bgPath)) {
106 | fs.unlinkSync(bgPath)
107 | }
108 | fs.writeFileSync(bgPath, image)
109 | setSetting('bg', 'url("local:///' + bgPath + '")')
110 | setSetting('bgName', fileName)
111 | } catch (err) {
112 | error('chooseImage error', err.toString())
113 | }
114 | })
115 | .catch((err) => {
116 | error('chooseImage, error', err.toString())
117 | })
118 | }
119 |
120 | /*
121 | 主线程 IPC 事件 ==============================================
122 | */
123 |
124 | ipcMain.on('LiteLoader.bcui_theme.rendererReady', (event, message) => {
125 | const window = BrowserWindow.fromWebContents(event.sender)
126 | updateStyle(window.webContents)
127 | })
128 | ipcMain.on('LiteLoader.bcui_theme.logToMain', (_, ...args) => {
129 | log('[renderer]', ...args)
130 | })
131 | ipcMain.on('LiteLoader.bcui_theme.logToError', (_, ...args) => {
132 | error('[renderer]', ...args)
133 | })
134 |
135 | ipcMain.handle('LiteLoader.bcui_theme.getVersion', async () => {
136 | const data = fs.readFileSync(`${pluginPath}/manifest.json`, 'utf-8')
137 | return await JSON.parse(data).version
138 | })
139 | ipcMain.handle('LiteLoader.bcui_theme.getSetting', async () => {
140 | return await getSetting()
141 | })
142 | ipcMain.on('LiteLoader.bcui_theme.setSetting', (_, k, v) => {
143 | setSetting(k, v)
144 | })
145 | ipcMain.handle('LiteLoader.bcui_theme.getSettingHTML', async () => {
146 | return fs.readFileSync(`${pluginPath}/src/config.html`, 'utf-8')
147 | })
148 | ipcMain.on('LiteLoader.bcui_theme.chooseImage', () => {
149 | chooseImage()
150 | })
151 |
152 | /*
153 | 主线程事件 ==============================================
154 | */
155 |
156 | module.exports.onBrowserWindowCreated = (window) => {
157 | log('设置目录:' + settingPath)
158 | try {
159 | // 创建设置文件
160 | // 判断 setting.json 是否存在,不存在则创建
161 | if (!fs.existsSync(settingPath)) {
162 | log('初始化设置 -> ' + settingPath)
163 | fs.mkdirSync(dataPath, { recursive: true })
164 | fs.copyFileSync(`${pluginPath}/src/setting.json`, settingPath)
165 | }
166 | } catch(ex) {
167 | error('初始化设置失败:', ex.toString())
168 | }
169 | }
170 |
--------------------------------------------------------------------------------
/src/renderer.js:
--------------------------------------------------------------------------------
1 | const log = (...args) => {
2 | console.log('[bcui-theme]', ...args)
3 | bcui_theme.logToMain(...args)
4 | }
5 | const error = (...args) => {
6 | console.error('[bcui-theme]', ...args)
7 | bcui_theme.logToError(...args)
8 | }
9 |
10 | /*
11 | 渲染线程 IPC 事件 ==============================================
12 | */
13 |
14 | // 刷新样式
15 | bcui_theme.updateStyle( async (event, id, data) => {
16 | let style = document.querySelector(`style[id="${id}"]`)
17 |
18 | if (!style) {
19 | style = document.createElement('style')
20 | style.id = id
21 | document.head.appendChild(style)
22 | }
23 |
24 | style.textContent = data
25 | })
26 | bcui_theme.updateTheme( async (event, config) => {
27 | updateTheme(config)
28 | })
29 |
30 | function updateTheme(config) {
31 | log('更新主题:' + JSON.stringify(config))
32 | try {
33 | if(config) {
34 | if(config.color != undefined) {
35 | document.documentElement.style.setProperty('--color-main', 'var(--color-main-' + config.color + ')')
36 | const meta = document.getElementsByName('theme-color')[0]
37 | if(meta) {
38 | meta.content = getComputedStyle(document.documentElement)
39 | .getPropertyValue('--color-main-' + config.color)
40 | }
41 | }
42 | if(config.bg != undefined && config.bg.indexOf('url("local:///') == 0) {
43 | if(document.getElementsByClassName('two-col-layout__main')[0])
44 | document.getElementsByClassName('two-col-layout__main')[0].style
45 | .background = config.bg
46 | } else {
47 | if(document.getElementsByClassName('two-col-layout__main')[0])
48 | document.getElementsByClassName('two-col-layout__main')[0].style
49 | .background = 'var(--color-card)'
50 | }
51 | if(config.opacity && typeof config.opacity == 'number') {
52 | const list = [
53 | document.getElementsByClassName('aio')[0],
54 | document.getElementsByClassName('empty-aio')[0],
55 | ]
56 | list.forEach((item) => {
57 | if(item)
58 | item.style.backdropFilter = `blur(${config.opacity}px)`
59 | })
60 | }
61 | }
62 | } catch (err) {
63 | error('更新主题失败:', err.toString() + '\n' + err.stack)
64 |
65 | }
66 | }
67 |
68 | // 渲染线程初始化完成
69 | log('渲染线程初始化完成')
70 | error('渲染线程初始化完成')
71 | bcui_theme.rendererReady()
72 |
73 | try {
74 | if (location.pathname === '/renderer/index.html') {
75 | if (location.hash === '#/blank') {
76 | navigation.addEventListener(
77 | 'navigatesuccess',
78 | () => {
79 | if (location.hash.includes('#/main') || location.hash.includes('#/chat')) {
80 | onMessageCreate()
81 | }
82 | },
83 | { once: true },
84 | )
85 | } else if (location.hash.includes('#/main') || location.hash.includes('#/chat')) {
86 | onMessageCreate()
87 | }
88 | }
89 | } catch (err) {
90 | error('main, ERROR', err.toString())
91 | }
92 |
93 | /*
94 | 渲染线程事件 ==============================================
95 | */
96 |
97 | export const onSettingWindowCreated = (view) => {
98 | onSettingCreate(view)
99 | }
100 |
101 | const onMessageCreate = async () => {
102 | const configs = await bcui_theme.getSetting()
103 | updateTheme(configs)
104 | }
105 |
106 | // 创建设置页面
107 | const onSettingCreate = async (view) => {
108 | log('创建设置页面')
109 | const version = await bcui_theme.getVersion()
110 | // 创建顶图
111 | const topHtml = `