logicdn 1.0.469 → 1.0.473

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/logi.im/api/asset/data/bundle.json +1 -1
  2. package/logi.im/api/asset/data/friends-cached.json +68 -68
  3. package/logi.im/api/asset/data/nodes.info +1 -1
  4. package/logi.im/api/biz/friend.py +231 -228
  5. package/package.json +1 -1
  6. package/logi.im/api/asset/bin/np +0 -0
  7. package/logi.im/usr/themes/Mirages/css/7.10.2/OwO.custom.min.css +0 -1
  8. package/logi.im/usr/themes/Mirages/css/7.10.2/dashboard.settings.min.css +0 -1
  9. package/logi.im/usr/themes/Mirages/css/7.10.2/dashboard.write.min.css +0 -1
  10. package/logi.im/usr/themes/Mirages/css/7.10.2/fonts/fontawesome-webfont.eot +0 -0
  11. package/logi.im/usr/themes/Mirages/css/7.10.2/fonts/fontawesome-webfont.svg +0 -2671
  12. package/logi.im/usr/themes/Mirages/css/7.10.2/fonts/fontawesome-webfont.ttf +0 -0
  13. package/logi.im/usr/themes/Mirages/css/7.10.2/fonts/fontawesome-webfont.woff +0 -0
  14. package/logi.im/usr/themes/Mirages/css/7.10.2/fonts/fontawesome-webfont.woff2 +0 -0
  15. package/logi.im/usr/themes/Mirages/css/7.10.2/mirages.min.css +0 -1
  16. package/logi.im/usr/themes/Mirages/css/7.10.2/vditor/theme/light.css +0 -1
  17. package/logi.im/usr/themes/Mirages/css/7.10.2/webfonts/fa-brands-400.eot +0 -0
  18. package/logi.im/usr/themes/Mirages/css/7.10.2/webfonts/fa-brands-400.svg +0 -3717
  19. package/logi.im/usr/themes/Mirages/css/7.10.2/webfonts/fa-brands-400.ttf +0 -0
  20. package/logi.im/usr/themes/Mirages/css/7.10.2/webfonts/fa-brands-400.woff +0 -0
  21. package/logi.im/usr/themes/Mirages/css/7.10.2/webfonts/fa-brands-400.woff2 +0 -0
  22. package/logi.im/usr/themes/Mirages/css/7.10.2/webfonts/fa-regular-400.eot +0 -0
  23. package/logi.im/usr/themes/Mirages/css/7.10.2/webfonts/fa-regular-400.svg +0 -801
  24. package/logi.im/usr/themes/Mirages/css/7.10.2/webfonts/fa-regular-400.ttf +0 -0
  25. package/logi.im/usr/themes/Mirages/css/7.10.2/webfonts/fa-regular-400.woff +0 -0
  26. package/logi.im/usr/themes/Mirages/css/7.10.2/webfonts/fa-regular-400.woff2 +0 -0
  27. package/logi.im/usr/themes/Mirages/css/7.10.2/webfonts/fa-solid-900.eot +0 -0
  28. package/logi.im/usr/themes/Mirages/css/7.10.2/webfonts/fa-solid-900.svg +0 -5028
  29. package/logi.im/usr/themes/Mirages/css/7.10.2/webfonts/fa-solid-900.ttf +0 -0
  30. package/logi.im/usr/themes/Mirages/css/7.10.2/webfonts/fa-solid-900.woff +0 -0
  31. package/logi.im/usr/themes/Mirages/css/7.10.2/webfonts/fa-solid-900.woff2 +0 -0
  32. package/logi.im/usr/themes/Mirages/js/7.10.2/OwO.custom.min.js +0 -17
  33. package/logi.im/usr/themes/Mirages/js/7.10.2/OwO.json +0 -157
  34. package/logi.im/usr/themes/Mirages/js/7.10.2/dashboard.settings.min.js +0 -16
  35. 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.473",
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}