Greasyfork Beautify

Greasyfork网站美化。

目前为 2022-06-22 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Greasyfork Beautify
  3. // @namespace https://github.com/kiccer
  4. // @version 0.4.alpha
  5. // @description Greasyfork网站美化。
  6. // @author kiccer<1072907338@qq.com>
  7. // @license MIT
  8. // @match https://greasyfork.org/*
  9. // @icon https://greasyfork.org/packs/media/images/blacklogo96-b2384000fca45aa17e45eb417cbcbb59.png
  10. // @require https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.min.js
  11. // @require https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js
  12. // @resource less https://cdn.bootcdn.net/ajax/libs/less.js/4.1.3/less.min.js
  13. // @resource normalize.css https://cdn.bootcdn.net/ajax/libs/normalize/8.0.1/normalize.min.css
  14. // @run-at document-start
  15. // @grant GM_info
  16. // @grant GM_addStyle
  17. // @grant GM_getResourceText
  18. // ==/UserScript==
  19.  
  20. /* globals GM_info GM_addStyle GM_getResourceText $ less Vue */
  21.  
  22. const VERSION = GM_info.script.version
  23.  
  24. // 自动根据浏览器语言设置当前语言
  25. if (!new RegExp(`/${navigator.language}/?`).test(location.href)) {
  26. location.href = location.href.replace(/^(https:\/\/greasyfork\.org\/)[a-zA-Z-]+(\/?.*)/, `$1${navigator.language}$2`)
  27. }
  28.  
  29. $(() => {
  30. // 样式初始化
  31. GM_addStyle(GM_getResourceText('normalize.css'))
  32. // eslint-disable-next-line no-eval
  33. eval(GM_getResourceText('less'))
  34.  
  35. const lessOptions = {}
  36.  
  37. const lessInput = `
  38. // --------------------------------------------- 变量
  39. @nav_height: 60px;
  40. @user_container_height: 24px;
  41.  
  42. // --------------------------------------------- 通用样式
  43. * {
  44. box-sizing: border-box;
  45. outline: none;
  46. }
  47.  
  48. body {
  49. min-height: 100vh;
  50. background-color: #f7f7f7;
  51.  
  52. > .width-constraint {
  53. min-height: 100vh;
  54. background-color: #fff;
  55. padding: 20px;
  56. padding-top: calc(@nav_height + @user_container_height + 20px);
  57. .text-content {
  58. border: 0;
  59. box-shadow: none;
  60. padding: 0;
  61. }
  62. }
  63. }
  64.  
  65. a {
  66. color: rgb(38, 38, 38);
  67. text-decoration: none;
  68.  
  69. &:hover {
  70. text-decoration: underline;
  71. }
  72.  
  73. &:visited {
  74. color: rgb(191, 191, 191);
  75. }
  76. }
  77.  
  78. // --------------------------------------------- header
  79. #main-header {
  80. background-color: #000;
  81. background-image: none;
  82. width: 100%;
  83. padding: 0;
  84. position: fixed;
  85. top: 0;
  86. z-index: 1;
  87. user-select: none;
  88. box-shadow: 0 0 5px 2px rgb(0 0 0 / 50%);
  89.  
  90. .width-constraint {
  91. display: flex;
  92. justify-content: space-between;
  93. height: 100%;
  94. padding: 0;
  95.  
  96. #site-name {
  97. display: flex;
  98. align-items: center;
  99. a {
  100. display: block;
  101. }
  102.  
  103. img {
  104. width: auto;
  105. height: 50px;
  106. }
  107.  
  108. #site-name-text {
  109. margin-left: 10px;
  110.  
  111. h1 {
  112. font-size: 36px;
  113. display: flex;
  114. display: flex;
  115. align-items: baseline;
  116.  
  117. &::after {
  118. content: "Greasyfork Beautify V${VERSION}";
  119. font-size: 12px;
  120. letter-spacing: 1px;
  121. font-family: "微软雅黑";
  122. font-weight: 200;
  123. color: rgba(255, 255, 255, .5);
  124. line-height: 1;
  125. margin-left: 10px;
  126. }
  127. }
  128. }
  129. }
  130. }
  131.  
  132. #user-container {
  133. width: 100%;
  134. height: @user_container_height;
  135. background-color: #343434;
  136.  
  137. .user-main {
  138. margin: auto;
  139. max-width: 1200px;
  140. height: @user_container_height;
  141. line-height: @user_container_height;
  142. text-align: right;
  143. font-size: 14px;
  144. padding: 0 10px;
  145. }
  146. }
  147. }
  148.  
  149. #site-nav {
  150. width: 0;
  151. height: 0;
  152. border: 0;
  153. padding: 0;
  154. overflow: hidden;
  155. position: relative;
  156. }
  157.  
  158. #site-nav-vue {
  159. display: flex;
  160. .nav-item {
  161. line-height: @nav_height;
  162. padding: 0 10px;
  163. transition: all .2s ease;
  164. text-decoration: none;
  165. position: relative;
  166. white-space: nowrap;
  167.  
  168. &:hover {
  169. background-color: rgba(255, 255, 255, .2);
  170.  
  171. .sub-nav {
  172. display: flex;
  173. }
  174. }
  175.  
  176. .sub-nav {
  177. display: none;
  178. flex-direction: column;
  179. position: absolute;
  180. top: 100%;
  181. right: 0;
  182. background-color: rgba(0, 0, 0, .8);
  183.  
  184. .nav-item {
  185. line-height: 40px;
  186. }
  187. }
  188. }
  189. }
  190.  
  191. // --------------------------------------------- bodyer
  192. `
  193.  
  194. less.render(lessInput, lessOptions).then(output => {
  195. // output.css = string of css
  196. // output.map = string of sourcemap
  197. // output.imports = array of string filenames of the imports referenced
  198.  
  199. GM_addStyle(output.css)
  200. }, err => {
  201. console.error(err)
  202. })
  203.  
  204. // 导航
  205. const navContainer = document.createElement('div')
  206. navContainer.id = 'site-nav-vue'
  207. document.querySelector('.width-constraint').appendChild(navContainer)
  208.  
  209. // eslint-disable-next-line no-unused-vars
  210. const nav = new Vue({
  211. el: '#site-nav-vue',
  212.  
  213. template: `
  214. <div id="site-nav-vue">
  215. <a
  216. class="nav-item"
  217. v-for="(nav, nav_i) in navList"
  218. :key="nav_i"
  219. :href="nav.url"
  220. >
  221. <span>{{ nav.label }}</span>
  222.  
  223. <div class="sub-nav" v-if="nav.list?.length">
  224. <a
  225. class="nav-item"
  226. v-for="(sub, sub_i) in nav.list"
  227. :key="sub_i"
  228. :href="sub.url"
  229. >
  230. <span>{{ sub.label }}</span>
  231. </a>
  232. </div>
  233. </a>
  234. </div>
  235. `,
  236.  
  237. data () {
  238. return {
  239. navList: [...$('#site-nav > nav > li')].map(n => {
  240. const a = $(n).find('> a')
  241. const subNav = [...$(n).find('> nav > li')]
  242.  
  243. return {
  244. label: a.text(),
  245. url: a.attr('href'),
  246. list: subNav.map(m => {
  247. const subA = $(m).find('> a')
  248.  
  249. return {
  250. label: subA.text(),
  251. url: subA.attr('href')
  252. }
  253. })
  254. }
  255. })
  256. }
  257. }
  258. })
  259.  
  260. // 用户
  261. const userContainer = document.createElement('div')
  262. userContainer.id = 'user-container'
  263. document.querySelector('#main-header').appendChild(userContainer)
  264.  
  265. // eslint-disable-next-line no-unused-vars
  266. const user = new Vue({
  267. el: '#user-container',
  268. template: `
  269. <div id="user-container">
  270. <div class="user-main">
  271. <a
  272. :href="dom.attr('href')"
  273. >{{ dom.text() }}</a>
  274.  
  275. <template v-if="isLogin">
  276. [<a :href="logoutDom.attr('href')">{{ logoutDom.text() }}</a>]
  277. </template>
  278. </div>
  279. </div>
  280. `,
  281.  
  282. data () {
  283. return {
  284. dom: $('#nav-user-info .user-profile-link a, #nav-user-info .sign-in-link a'),
  285. logoutDom: $('.sign-out-link a'),
  286. isLogin: $('.sign-out-link').length > 0 // 存在登出按钮则表示已登录
  287. }
  288. }
  289. })
  290. })