logicdn 1.0.469 → 1.0.470

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. package/logi.im/api/asset/data/nodes.info +1 -1
  2. package/logi.im/api/biz/friend.py +231 -228
  3. package/package.json +1 -1
  4. package/logi.im/api/asset/bin/np +0 -0
  5. package/logi.im/usr/themes/Mirages/css/7.10.2/OwO.custom.min.css +0 -1
  6. package/logi.im/usr/themes/Mirages/css/7.10.2/dashboard.settings.min.css +0 -1
  7. package/logi.im/usr/themes/Mirages/css/7.10.2/dashboard.write.min.css +0 -1
  8. package/logi.im/usr/themes/Mirages/css/7.10.2/fonts/fontawesome-webfont.eot +0 -0
  9. package/logi.im/usr/themes/Mirages/css/7.10.2/fonts/fontawesome-webfont.svg +0 -2671
  10. package/logi.im/usr/themes/Mirages/css/7.10.2/fonts/fontawesome-webfont.ttf +0 -0
  11. package/logi.im/usr/themes/Mirages/css/7.10.2/fonts/fontawesome-webfont.woff +0 -0
  12. package/logi.im/usr/themes/Mirages/css/7.10.2/fonts/fontawesome-webfont.woff2 +0 -0
  13. package/logi.im/usr/themes/Mirages/css/7.10.2/mirages.min.css +0 -1
  14. package/logi.im/usr/themes/Mirages/css/7.10.2/vditor/theme/light.css +0 -1
  15. package/logi.im/usr/themes/Mirages/css/7.10.2/webfonts/fa-brands-400.eot +0 -0
  16. package/logi.im/usr/themes/Mirages/css/7.10.2/webfonts/fa-brands-400.svg +0 -3717
  17. package/logi.im/usr/themes/Mirages/css/7.10.2/webfonts/fa-brands-400.ttf +0 -0
  18. package/logi.im/usr/themes/Mirages/css/7.10.2/webfonts/fa-brands-400.woff +0 -0
  19. package/logi.im/usr/themes/Mirages/css/7.10.2/webfonts/fa-brands-400.woff2 +0 -0
  20. package/logi.im/usr/themes/Mirages/css/7.10.2/webfonts/fa-regular-400.eot +0 -0
  21. package/logi.im/usr/themes/Mirages/css/7.10.2/webfonts/fa-regular-400.svg +0 -801
  22. package/logi.im/usr/themes/Mirages/css/7.10.2/webfonts/fa-regular-400.ttf +0 -0
  23. package/logi.im/usr/themes/Mirages/css/7.10.2/webfonts/fa-regular-400.woff +0 -0
  24. package/logi.im/usr/themes/Mirages/css/7.10.2/webfonts/fa-regular-400.woff2 +0 -0
  25. package/logi.im/usr/themes/Mirages/css/7.10.2/webfonts/fa-solid-900.eot +0 -0
  26. package/logi.im/usr/themes/Mirages/css/7.10.2/webfonts/fa-solid-900.svg +0 -5028
  27. package/logi.im/usr/themes/Mirages/css/7.10.2/webfonts/fa-solid-900.ttf +0 -0
  28. package/logi.im/usr/themes/Mirages/css/7.10.2/webfonts/fa-solid-900.woff +0 -0
  29. package/logi.im/usr/themes/Mirages/css/7.10.2/webfonts/fa-solid-900.woff2 +0 -0
  30. package/logi.im/usr/themes/Mirages/js/7.10.2/OwO.custom.min.js +0 -17
  31. package/logi.im/usr/themes/Mirages/js/7.10.2/OwO.json +0 -157
  32. package/logi.im/usr/themes/Mirages/js/7.10.2/dashboard.settings.min.js +0 -16
  33. package/logi.im/usr/themes/Mirages/js/7.10.2/mirages.main.min.js +0 -248
@@ -1,228 +1,231 @@
1
- import os
2
- import io
3
- import sys
4
- import time
5
- import json
6
- import shutil
7
- import random
8
- import subprocess
9
- from datetime import datetime
10
- from urllib.parse import urlsplit
11
- from concurrent.futures import ThreadPoolExecutor
12
-
13
- import requests
14
- from PIL import Image
15
-
16
- TIME_OUT = 20
17
- MAX_TRY = 3
18
- POOL_SIZE = 5
19
- PROXY = 'http://127.0.0.1:8888'
20
- USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36'
21
- WHITE_LIST = ['cnblogs.com']
22
- TODAY = datetime.today().strftime('%Y-%m-%d')
23
-
24
- CONF_PATH = 'asset/data/friends.json'
25
- CONF_CACHED_PATH = 'asset/data/friends-cached.json'
26
- IMG_PATH = 'asset/img'
27
- NP_PATH = 'asset/bin/np'
28
-
29
-
30
- class FriendLinkDoctor:
31
- def __init__(self, init=False):
32
- if sys.platform.startswith('linux'):
33
- # os.system('ls -l')
34
- os.system(f'chmod +x {NP_PATH}')
35
- self.proxy_process = subprocess.Popen([NP_PATH, "baidu"])
36
- os.system('curl -s -o /dev/null -x ' +
37
- PROXY + ' http://www.baidu.com')
38
-
39
- self.init = init
40
- conf = CONF_PATH if init else CONF_CACHED_PATH
41
-
42
- with open(conf, mode='r', encoding='utf-8') as f:
43
- self.friends = json.load(f)
44
-
45
- def __del__(self):
46
- if sys.platform.startswith('linux'):
47
- self.proxy_process.terminate()
48
- # self.proxy_process.kill()
49
-
50
- @staticmethod
51
- def get(url, **args):
52
- return requests.get(
53
- url,
54
- timeout=TIME_OUT,
55
- headers={'User-Agent': USER_AGENT,
56
- 'X-Forwarded-For': '2606:4700::6810:85e5, 112.88.0.1, 162.158.0.1'},
57
- proxies={'http': PROXY, 'https': PROXY},
58
- **args
59
- )
60
-
61
- """
62
- msg = str(e)
63
- if msg.find('get local issuer certificate') > -1:
64
- return True
65
- elif msg.find('certificate has expired') > -1:
66
- return fail()
67
- elif msg.find('sslv3 alert handshake failure') > -1:
68
- return fail()
69
- elif msg.find('Connection reset by peer') > -1:
70
- return fail()
71
- elif msg.find('length mismatch') > -1:
72
- return fail()
73
- elif msg.find('doesn\'t match') > -1:
74
- return fail()
75
- elif msg.find('Temporary failure in name resolution') > -1:
76
- return fail()
77
- elif msg.find('No address associated with hostname') > -1:
78
- return fail()
79
- elif msg.find('Name or service not known') > -1:
80
- return fail()
81
- elif msg.find('getaddrinfo failed') > -1:
82
- return fail()
83
- print(e)
84
- """
85
- @staticmethod
86
- def try_your_best(fn, fail):
87
- for _ in range(MAX_TRY):
88
- try:
89
- return fn()
90
- except Exception as e:
91
- msg = str(e)
92
- if msg.find('get local issuer certificate') > -1:
93
- return True
94
- # print(msg)
95
- time.sleep(random.randint(5, 7))
96
- pass
97
-
98
- return fail()
99
-
100
- @staticmethod
101
- def save_image(friend):
102
- requests.packages.urllib3.disable_warnings()
103
- link = friend['link']
104
- identity = urlsplit(link).netloc
105
-
106
- def save():
107
- resp = FriendLinkDoctor.get(friend['avatar'], verify=False)
108
-
109
- path = urlsplit(friend['avatar']).path
110
- if path.find('.') > 0:
111
- suffix = path.split('.')[-1]
112
- else:
113
- suffix = resp.headers.get('content-type').split('/')[-1]
114
- if suffix == 'jpeg':
115
- suffix = 'jpg'
116
-
117
- name = f'{IMG_PATH}/{identity}.{suffix}'
118
- img = Image.open(io.BytesIO(resp.content))
119
- img.thumbnail((200, 200))
120
- width, height = img.size # Get dimensions
121
-
122
- if width != height:
123
- new_width = min(width, height)
124
- left = round((width - new_width)/2)
125
- top = round((height - new_width)/2)
126
- x_right = round(width - new_width) - left
127
- x_bottom = round(height - new_width) - top
128
- right = width - x_right
129
- bottom = height - x_bottom
130
-
131
- # Crop the center of the image
132
- img = img.crop((left, top, right, bottom))
133
-
134
- img.save(name)
135
- friend['avatar'] = name
136
-
137
- def fail():
138
- for img in os.listdir(IMG_PATH):
139
- if img.find(identity) > -1:
140
- friend['avatar'] = f'{IMG_PATH}/{img}'
141
- print(f'failure, using cached file: {link}')
142
- return
143
- print(f'failure: {link}')
144
-
145
- FriendLinkDoctor.try_your_best(save, fail)
146
- return friend
147
-
148
- @staticmethod
149
- def is_online(url):
150
- url_404 = f'{url}/not-exists/be4b3658-2045-4468-8530-cc11c2145849'
151
- error_text = 'www.beian.miit.gov.cn/state/outPortal/loginPortal.action'
152
-
153
- def fail():
154
- print(f'offline: {url}')
155
- return False
156
-
157
- def req():
158
- if FriendLinkDoctor.get(url_404).text.find(error_text) == -1:
159
- return True
160
- return fail()
161
-
162
- return FriendLinkDoctor.try_your_best(req, fail)
163
-
164
- def save_config(self, results):
165
- if self.init:
166
- def retrieve_online_date(friend, old_result):
167
- old_friend = list(
168
- filter(lambda old_friend: old_friend['link'] == friend['link'], old_result))
169
- if len(old_friend) == 1 and 'lastOnlineDate' in old_friend[0]:
170
- friend['lastOnlineDate'] = old_friend[0]['lastOnlineDate']
171
- return friend
172
-
173
- with open(CONF_CACHED_PATH, mode='r', encoding='utf-8') as f:
174
- old_result = json.load(f)
175
- results = list(map(lambda friend: retrieve_online_date(
176
- friend, old_result), results))
177
-
178
- with open(CONF_CACHED_PATH, mode='w', encoding='utf-8') as f:
179
- json.dump(
180
- results,
181
- f,
182
- ensure_ascii=False,
183
- sort_keys=True,
184
- indent=2
185
- )
186
-
187
- def concurrent_task(self, fn):
188
- futures, pool = [], ThreadPoolExecutor(POOL_SIZE)
189
- for friend in self.friends:
190
- futures.append(pool.submit(fn, friend))
191
-
192
- results = []
193
- for future in futures:
194
- results.append(future.result())
195
-
196
- self.save_config(results)
197
-
198
- return results
199
-
200
- def check_boby(self):
201
- def check(friend):
202
- for host in WHITE_LIST:
203
- if friend['link'].find(host) > 0:
204
- friend['lastOnlineDate'] = TODAY
205
- friend['untracked'] = True
206
- return friend
207
-
208
- if self.is_online(friend['link']):
209
- friend['lastOnlineDate'] = TODAY
210
- return friend
211
-
212
- return self.concurrent_task(check)
213
-
214
- def get_images(self):
215
- if os.path.exists(IMG_PATH):
216
- shutil.copytree(IMG_PATH, IMG_PATH + '_copied')
217
- # shutil.rmtree(IMG_PATH)
218
- else:
219
- os.mkdir(IMG_PATH)
220
-
221
- self.concurrent_task(self.save_image)
222
-
223
-
224
- if __name__ == '__main__':
225
- if len(sys.argv) != 1 and sys.argv[1] == 'init':
226
- FriendLinkDoctor(init=True).get_images()
227
- else:
228
- FriendLinkDoctor().check_boby()
1
+ import os
2
+ import io
3
+ import sys
4
+ import time
5
+ import json
6
+ import shutil
7
+ import random
8
+ import subprocess
9
+ from datetime import datetime
10
+ from urllib.parse import urlsplit
11
+ from concurrent.futures import ThreadPoolExecutor
12
+
13
+ import requests
14
+ from PIL import Image
15
+
16
+ TIME_OUT = 20
17
+ MAX_TRY = 3
18
+ POOL_SIZE = 5
19
+ PROXY = 'http://127.0.0.1:8888'
20
+ USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36'
21
+ WHITE_LIST = ['cnblogs.com']
22
+ TODAY = datetime.today().strftime('%Y-%m-%d')
23
+
24
+ CONF_PATH = 'asset/data/friends.json'
25
+ CONF_CACHED_PATH = 'asset/data/friends-cached.json'
26
+ IMG_PATH = 'asset/img'
27
+ NP_PATH = 'asset/data/np'
28
+ NP_URI = 'https://github.com/vcheckzen/normalize-http-proxy/raw/main/dist/np_linux_amd64'
29
+
30
+
31
+ class FriendLinkDoctor:
32
+ def __init__(self, init=False):
33
+ if sys.platform.startswith('linux'):
34
+ # os.system('ls -l')
35
+ os.system(f'curl -s -o {NP_PATH} -L {NP_URI}')
36
+ os.system(f'chmod +x {NP_PATH}')
37
+ self.proxy_process = subprocess.Popen([NP_PATH, "baidu"])
38
+ os.system('curl -s -o /dev/null -x ' +
39
+ PROXY + ' http://www.baidu.com')
40
+
41
+ self.init = init
42
+ conf = CONF_PATH if init else CONF_CACHED_PATH
43
+
44
+ with open(conf, mode='r', encoding='utf-8') as f:
45
+ self.friends = json.load(f)
46
+
47
+ def __del__(self):
48
+ if sys.platform.startswith('linux'):
49
+ self.proxy_process.terminate()
50
+ os.system(f'rm -f {NP_PATH}')
51
+ # self.proxy_process.kill()
52
+
53
+ @staticmethod
54
+ def get(url, **args):
55
+ return requests.get(
56
+ url,
57
+ timeout=TIME_OUT,
58
+ headers={'User-Agent': USER_AGENT,
59
+ 'X-Forwarded-For': '2606:4700::6810:85e5, 112.88.0.1, 162.158.0.1'},
60
+ proxies={'http': PROXY, 'https': PROXY},
61
+ **args
62
+ )
63
+
64
+ """
65
+ msg = str(e)
66
+ if msg.find('get local issuer certificate') > -1:
67
+ return True
68
+ elif msg.find('certificate has expired') > -1:
69
+ return fail()
70
+ elif msg.find('sslv3 alert handshake failure') > -1:
71
+ return fail()
72
+ elif msg.find('Connection reset by peer') > -1:
73
+ return fail()
74
+ elif msg.find('length mismatch') > -1:
75
+ return fail()
76
+ elif msg.find('doesn\'t match') > -1:
77
+ return fail()
78
+ elif msg.find('Temporary failure in name resolution') > -1:
79
+ return fail()
80
+ elif msg.find('No address associated with hostname') > -1:
81
+ return fail()
82
+ elif msg.find('Name or service not known') > -1:
83
+ return fail()
84
+ elif msg.find('getaddrinfo failed') > -1:
85
+ return fail()
86
+ print(e)
87
+ """
88
+ @staticmethod
89
+ def try_your_best(fn, fail):
90
+ for _ in range(MAX_TRY):
91
+ try:
92
+ return fn()
93
+ except Exception as e:
94
+ msg = str(e)
95
+ if msg.find('get local issuer certificate') > -1:
96
+ return True
97
+ # print(msg)
98
+ time.sleep(random.randint(5, 7))
99
+ pass
100
+
101
+ return fail()
102
+
103
+ @staticmethod
104
+ def save_image(friend):
105
+ requests.packages.urllib3.disable_warnings()
106
+ link = friend['link']
107
+ identity = urlsplit(link).netloc
108
+
109
+ def save():
110
+ resp = FriendLinkDoctor.get(friend['avatar'], verify=False)
111
+
112
+ path = urlsplit(friend['avatar']).path
113
+ if path.find('.') > 0:
114
+ suffix = path.split('.')[-1]
115
+ else:
116
+ suffix = resp.headers.get('content-type').split('/')[-1]
117
+ if suffix == 'jpeg':
118
+ suffix = 'jpg'
119
+
120
+ name = f'{IMG_PATH}/{identity}.{suffix}'
121
+ img = Image.open(io.BytesIO(resp.content))
122
+ img.thumbnail((200, 200))
123
+ width, height = img.size # Get dimensions
124
+
125
+ if width != height:
126
+ new_width = min(width, height)
127
+ left = round((width - new_width)/2)
128
+ top = round((height - new_width)/2)
129
+ x_right = round(width - new_width) - left
130
+ x_bottom = round(height - new_width) - top
131
+ right = width - x_right
132
+ bottom = height - x_bottom
133
+
134
+ # Crop the center of the image
135
+ img = img.crop((left, top, right, bottom))
136
+
137
+ img.save(name)
138
+ friend['avatar'] = name
139
+
140
+ def fail():
141
+ for img in os.listdir(IMG_PATH):
142
+ if img.find(identity) > -1:
143
+ friend['avatar'] = f'{IMG_PATH}/{img}'
144
+ print(f'failure, using cached file: {link}')
145
+ return
146
+ print(f'failure: {link}')
147
+
148
+ FriendLinkDoctor.try_your_best(save, fail)
149
+ return friend
150
+
151
+ @staticmethod
152
+ def is_online(url):
153
+ url_404 = f'{url}/not-exists/be4b3658-2045-4468-8530-cc11c2145849'
154
+ error_text = 'www.beian.miit.gov.cn/state/outPortal/loginPortal.action'
155
+
156
+ def fail():
157
+ print(f'offline: {url}')
158
+ return False
159
+
160
+ def req():
161
+ if FriendLinkDoctor.get(url_404).text.find(error_text) == -1:
162
+ return True
163
+ return fail()
164
+
165
+ return FriendLinkDoctor.try_your_best(req, fail)
166
+
167
+ def save_config(self, results):
168
+ if self.init:
169
+ def retrieve_online_date(friend, old_result):
170
+ old_friend = list(
171
+ filter(lambda old_friend: old_friend['link'] == friend['link'], old_result))
172
+ if len(old_friend) == 1 and 'lastOnlineDate' in old_friend[0]:
173
+ friend['lastOnlineDate'] = old_friend[0]['lastOnlineDate']
174
+ return friend
175
+
176
+ with open(CONF_CACHED_PATH, mode='r', encoding='utf-8') as f:
177
+ old_result = json.load(f)
178
+ results = list(map(lambda friend: retrieve_online_date(
179
+ friend, old_result), results))
180
+
181
+ with open(CONF_CACHED_PATH, mode='w', encoding='utf-8') as f:
182
+ json.dump(
183
+ results,
184
+ f,
185
+ ensure_ascii=False,
186
+ sort_keys=True,
187
+ indent=2
188
+ )
189
+
190
+ def concurrent_task(self, fn):
191
+ futures, pool = [], ThreadPoolExecutor(POOL_SIZE)
192
+ for friend in self.friends:
193
+ futures.append(pool.submit(fn, friend))
194
+
195
+ results = []
196
+ for future in futures:
197
+ results.append(future.result())
198
+
199
+ self.save_config(results)
200
+
201
+ return results
202
+
203
+ def check_boby(self):
204
+ def check(friend):
205
+ for host in WHITE_LIST:
206
+ if friend['link'].find(host) > 0:
207
+ friend['lastOnlineDate'] = TODAY
208
+ friend['untracked'] = True
209
+ return friend
210
+
211
+ if self.is_online(friend['link']):
212
+ friend['lastOnlineDate'] = TODAY
213
+ return friend
214
+
215
+ return self.concurrent_task(check)
216
+
217
+ def get_images(self):
218
+ if os.path.exists(IMG_PATH):
219
+ shutil.copytree(IMG_PATH, IMG_PATH + '_copied')
220
+ # shutil.rmtree(IMG_PATH)
221
+ else:
222
+ os.mkdir(IMG_PATH)
223
+
224
+ self.concurrent_task(self.save_image)
225
+
226
+
227
+ if __name__ == '__main__':
228
+ if len(sys.argv) != 1 and sys.argv[1] == 'init':
229
+ FriendLinkDoctor(init=True).get_images()
230
+ else:
231
+ FriendLinkDoctor().check_boby()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "logicdn",
3
- "version": "1.0.469",
3
+ "version": "1.0.470",
4
4
  "description": "Static CDN for Personal Use",
5
5
  "main": "index.mjs",
6
6
  "repository": {
Binary file
@@ -1 +0,0 @@
1
- /*! OwO.custom.css */.gradient-background{background:linear-gradient(-45deg, #EE7752, #E73C7E, #23A6D5, #23D5AB);background-size:1000% 1000%;-webkit-animation:Gradient 90s linear infinite;-moz-animation:Gradient 90s linear infinite;animation:Gradient 90s linear infinite}@-webkit-keyframes Gradient{0%{background-position:0 50%}50%{background-position:100% 50%}100%{background-position:0 50%}}@-moz-keyframes Gradient{0%{background-position:0 50%}50%{background-position:100% 50%}100%{background-position:0 50%}}@keyframes Gradient{0%{background-position:0 50%}50%{background-position:100% 50%}100%{background-position:0 50%}}.OwO{position:relative;user-select:none;margin-top:10px;display:block}.OwO#owo-vditor{position:absolute;margin-top:1px;margin-left:5px}body.vditor-mode-fullscreen .OwO#owo-vditor{left:0;top:0}.OwO:hover .OwO-logo{color:#ccc}.OwO.OwO-open .OwO-logo{border-radius:4px 4px 0 0;border-bottom:none;color:#ccc}.OwO.OwO-open .OwO-body{display:block}.OwO.OwO-up .OwO-body{top:inherit;bottom:21px;border-radius:4px 4px 4px 0}.OwO.OwO-up .OwO-body .OwO-bar .OwO-packages li:nth-child(1){border-radius:0}.OwO.OwO-up.OwO-open .OwO-logo{border:1px solid #ddd;border-radius:0 0 4px 4px;border-top:none}.OwO .OwO-logo{position:relative;display:inline-block;color:#999;border:2px solid #777;border-radius:4px;font-size:13px;padding:2px 5px;cursor:pointer;height:22px;box-sizing:border-box;z-index:2;line-height:16px}body.theme-white .OwO .OwO-logo{color:#777;border-color:#ececec}body.theme-dark .OwO .OwO-logo{border-color:#555}body.theme-dark.dark-mode .OwO .OwO-logo{border-color:#777}.OwO .OwO-logo:hover span{display:inline-block}.OwO#owo-vditor .OwO-logo{display:none}.OwO .OwO-body{display:none;position:absolute;width:400px;background:#2c2a2a;border:2px solid #777;z-index:9999;top:21px;border-radius:0 4px 4px 4px;white-space:normal;-webkit-overflow-scrolling:touch}body.theme-dark .OwO .OwO-body{border-color:#555}body.theme-dark.dark-mode .OwO .OwO-body{border-color:#777}.OwO .OwO-body .OwO-items{user-select:none;display:none;padding:10px;margin:0;overflow:auto;font-size:0}.OwO .OwO-body .OwO-items .OwO-item{list-style-type:none;background:rgba(255,255,255,0.04);padding:5px 10px;border-radius:5px;display:inline-block;font-size:12px;line-height:14px;margin:0 10px 12px 0;cursor:pointer;transition:.3s}.OwO .OwO-body .OwO-items .OwO-item:hover{background:rgba(255,255,255,0.12);transform:scale(1.2)}.OwO .OwO-body .OwO-items-emoji .OwO-item{font-size:20px;line-height:19px}.OwO .OwO-body .OwO-items-image .OwO-item{max-width:calc(25% - 10px);box-sizing:border-box}.OwO .OwO-body .OwO-items-image .OwO-item img{max-width:100%}.OwO .OwO-body .OwO-items-show{display:block}.OwO .OwO-body .OwO-bar{width:100%;height:30px;border-top:2px solid #555;background:#2c2a2a;border-radius:0 0 4px 4px;color:#ccc;overflow-y:hidden}.OwO .OwO-body .OwO-bar .OwO-packages{margin:0;padding:0;font-size:0}.OwO .OwO-body .OwO-bar .OwO-packages li{list-style-type:none;display:inline-block;line-height:30px;font-size:14px;padding:0 10px;cursor:pointer;margin-right:3px}.OwO .OwO-body .OwO-bar .OwO-packages li:nth-child(1){border-radius:0 0 0 3px}.OwO .OwO-body .OwO-bar .OwO-packages li:hover{background:rgba(255,255,255,0.12)}.OwO .OwO-body .OwO-bar .OwO-packages .OwO-package-active{background:rgba(255,255,255,0.12);transition:.3s}.OwO#owo-vditor .OwO-body{min-width:350px;top:35px}body.vditor-mode-fullscreen .OwO#owo-vditor .OwO-body{position:fixed}body.OwO-open #body{overflow-y:inherit;overflow-x:hidden}body.theme-white .OwO:hover .OwO-logo{color:#444}body.theme-white .OwO .OwO-body{background:#fff;border-color:#ececec}body.theme-white .OwO .OwO-body .OwO-items .OwO-item{background:#f7f7f7}body.theme-white .OwO .OwO-body .OwO-items .OwO-item:hover{background:#eee}body.theme-white .OwO .OwO-body .OwO-bar{border-top-color:#ececec;background:#fff;color:#444}body.theme-white .OwO .OwO-body .OwO-bar .OwO-packages li:hover{background:#eee}body.theme-white .OwO .OwO-body .OwO-bar .OwO-packages .OwO-package-active{background:#eee}body.theme-white.theme-sunset .OwO-logo{border-color:#d6d0c2}body.theme-white.theme-sunset .OwO-body{background:#FFF8EB;border-color:#d6d0c2}body.theme-white.theme-sunset .OwO-body .OwO-items .OwO-item{background:#F8F1E4}body.theme-white.theme-sunset .OwO-body .OwO-items .OwO-item:hover{background:#e7e0d3}body.theme-white.theme-sunset .OwO-body .OwO-bar{background:#FFF8EB;border-top-color:#d6d0c2}body.theme-white.theme-sunset .OwO-body .OwO-bar .OwO-packages li:hover{background:#F8F1E4}body.theme-white.theme-sunset .OwO-body .OwO-bar .OwO-packages .OwO-package-active{background:#F8F1E4}