PoeUI

美化Poe网页版UI样式

  1. // ==UserScript==
  2. // @name PoeUI
  3. // @namespace http://xiaomizha.ltd/
  4. // @version 0.1
  5. // @description 美化Poe网页版UI样式
  6. // @author xuyou
  7. // @description:en We embellished the Poe web UI style.
  8. // @description:zh-CN 美化Poe网页版UI样式
  9. // @license MIT
  10. // @match https://poe.com/*
  11. // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAolBMVEVHcEz6Uvy1QfqUQuefTOPxUPxNVb5GYLh6S9dPVL+2Qvn1UvywQfaLQuONQuP+UfpXRMbQS//8Uvy+QP////+3QPudQeyxQPekQPCUQedZRMeqQfOCQN5iQsxPRcGLQuJHSLvDQ/96QtlyQ9VqQ9DRTf/uV//KR//jVv9GVLbZU//6U/7Oivutfurz6v2YLOy3WfeUb9+/m++zLPvXrfniyfqv1uwAAAAAE3RSTlMA75uUH2/I/WaW66DF68XA6s3NFufBpwAABBtJREFUWIWVl4t2qkoMhqetdmvbfTvIrWItImpVmAr4/q92ZibJ3EA5J6zVtWzN1+TPnwEYc2Mymz//Wb3L+FDxqeJLxo8fz/PZhN2L+VbGSsS7YTgIEfOb6U+FCCIoxMe7IYwiZptiU/iIj6FGRMz6+b82MnoEt41PTXj2839mWTaA6DM0ws3PVGx6iPeeFF8a4fz/9XptGMUtKTw1rf7XMjLB2Jg+toUz0iFXaP3TdK0R/0cKmkUqwydsiGBJ4RO+0D9pehtxe6aSAY6KokjlD/dhzO1XQSU8RREi7CI2Q1L01ZxDAT4hc2sonBoA8YlViP1NksRhZGl1qS7DUXz4WzphM5EPhBQI9eJO1FuPMGOPiQoqgt/NFwS7D8GYs7clEhJVRHQ/f7EonNPm85m9LEWYIqoxwMXMQyL+sKUCaEQzBmhW7kjZMrYIyZgEQoSV6woWx4oAbfBuUXfVcIjZVlVTLzqZbBZEAgDBq3NXL66cRyAH+co9arbXRd1UF0CsNEAioHhOaqY9d6M3oZGC+mBhqPJbyL9yVJNzToiUzK0YxRW+SFIIgCK0IFGXoCu6um44ENZp1tR1B4Bsg3Pa4ppKgGLAr8+JkhOG0aSohMqpsQ8E0JYqQGwAMFK0EwctOXyqYM8NQBGwgtACiMAv0ZKihTI1E/wbnd0sCANFQA1gpGf4hCtGABgIAfC0YQESsAIYKQFwpjcACiEAQLAAsQVQS4oAmCgCyFsSIGMQgKYgEVMzEgFAZwEgDAIbEIOtOlpSGCPvAwSC5VABGalFY13rWnlCumLJpZFoPwiAS8ICIJCVW/RF27ZL66gQzqYzD6wsbaUQsoJcXt8AbsmZsV7TJR16AIDvXWjFWJ7nWMS5u+oSKN86beDslgVcu0bfjjO2zyUCGG0rtv38fScadWLYN1O2V4TcleJO1FzfxRSBlfu9XUQ3BuhITSyClZKw10WcxwAVaKEPLHYsy9IilGOAb+9eyo6SQIh8FFBzM1JJ+Mn+Ho9YxP6/AL7xPkhV/GKvh6ONOKJU5+Hg5Cu8o0dPbHo4HI6mDwB0LW5I0IbiitW1FOaO7fuYyE9mbLI72AgFqDE/oMMCjk3jbWNu8frwoAiIAEBAvrIRgwsSiWek6W5HRQiEBJTGmeqoMITQLAj08SQf03Y7CyEA51zNw4M4jWgl1HPiq00ou27vuRu6GKziER5VTyeDOJRl6bhbMnJbCbsIfFaenmyEdoW9pRYhDvVA9IvP75OHKAcRQRA4iDfzwvDgEA6Ou3M91NCqQuS/2K8sJ4hbCGCIH2Fu1HRfmh4AQIjjwetD90CEF+bFb5dwWwpAvPn5MAtfTE8Ko8W0ny8ddRqRgop4HExXVXhSeH2Ao26ny5hMX/95wD56iJe/j1P/9f9fo6ydU4aSQJkAAAAASUVORK5CYII=
  12. // @grant GM_registerMenuCommand
  13. // ==/UserScript==
  14.  
  15. ;(() => {
  16. ;('use strict')
  17.  
  18. // 重构 Login UI
  19.  
  20. // 默认配置
  21. const borderRadius = '4px'
  22. const paddingCustom = '16px'
  23. const marginCustom = '20px'
  24. const colorCustomLi = '#EFEFFC'
  25. const colorCustom = '#5D5CDE'
  26. const borderCustom = '1px solid #e6e7e9'
  27. const fontFamilyLists = [
  28. {
  29. name: 'Loto',
  30. href: 'https://fonts.googleapis.com/css2?family=Lato&display=swap'
  31. },
  32. {
  33. name: 'Pathway Extreme',
  34. href: 'https://fonts.googleapis.com/css2?family=Pathway+Extreme&display=swap'
  35. }
  36. ]
  37. const fontFamily = 'Lato, "Pathway Extreme", "Microsoft YaHei", sans-serif'
  38.  
  39. // 修改初始样式
  40. // Modify initial style
  41. const setInitialStyle = () => {
  42. // 创建<style>标签
  43. let styles = document.createElement('style')
  44. // 设置字体样式
  45. let css = `
  46. * {
  47. transition : all .3s;
  48. caret-color : ${colorCustom};
  49. font-family : ${fontFamily} !important;
  50. -webkit-tap-highlight-color: rgba(0, 0, 0, 0) !important;
  51. text-shadow : 0 0 1.15px #a4a09ad9, 0 0 1px #7b7b7bcc, 0 0 0.75px #302f2d45 !important;
  52. }
  53. ::selection {
  54. color : #FFFFFF !important;
  55. background : ${colorCustom} !important;
  56. text-shadow: none !important;
  57. }
  58. `
  59. // 添加CSS规则
  60. styles.append(document.createTextNode(css))
  61. // 将<style>标签加到head中
  62. document.head.appendChild(styles)
  63. // 动态添加 @import 规则
  64. fontFamilyLists.forEach((item) => {
  65. styles.sheet.insertRule(`@import url(${item.href});`)
  66. })
  67. }
  68.  
  69. // 美化登录页
  70. const setLoginStyle = () => {
  71. let buttonBase = document.querySelectorAll('[class^=Button_buttonBase__]')
  72. buttonBase.forEach((elem) => {
  73. setCss(elem, {
  74. borderRadius: borderRadius,
  75. paddingTop: paddingCustom,
  76. paddingBottom: paddingCustom
  77. })
  78. })
  79. }
  80.  
  81. // 全选所有历史记录
  82. GM_registerMenuCommand('全选所有历史记录', () => {
  83. // 执行全选操作
  84. let checkbox = document.querySelectorAll('.Checkbox_checkbox__zM_Lo')
  85. for (var i = 0; i < checkbox.length; i++) {
  86. if (!checkbox[i].checked) {
  87. checkbox[i].click()
  88. }
  89. }
  90. })
  91.  
  92. // 聊天区最大化
  93. const setChatStyle = () => {
  94. let leftSidebar = document.querySelector(
  95. '[class^=PageWithSidebarLayout_leftSidebar__]'
  96. )
  97. setCss(leftSidebar, { flexGrow: 1 })
  98. let mainSection = document.querySelector(
  99. '[class^=PageWithSidebarLayout_mainSection__]'
  100. )
  101. setCss(mainSection, { flexGrow: 2, maxWidth: 'initial' })
  102. let rightSidebar = document.querySelector(
  103. '[class^=PageWithSidebarLayout_rightSidebar__]'
  104. )
  105. setCss(rightSidebar, { display: 'none' })
  106. let welcomeButtonsContainer = document.querySelectorAll(
  107. '[class^=ChatWelcomeView_welcomeButtonsContainer__]'
  108. )
  109. welcomeButtonsContainer.forEach((elem) => {
  110. setCss(elem, { borderRadius: borderRadius })
  111. })
  112. let humanMessageBubble = document.querySelectorAll(
  113. '[class^=Message_humanMessageBubble__]'
  114. )
  115. humanMessageBubble.forEach((elem) => {
  116. setCss(elem, { borderRadius: borderRadius })
  117. })
  118. let botMessageBubble = document.querySelectorAll(
  119. '[class^=Message_botMessageBubble__]'
  120. )
  121. botMessageBubble.forEach((elem) => {
  122. setCss(elem, { borderRadius: borderRadius })
  123. })
  124. let textInput = document.querySelector(
  125. '[class^=ChatMessageInputView_textInput__]'
  126. )
  127. setCss(textInput, { paddingTop: marginCustom, borderRadius: borderRadius })
  128. let sendButtonWrapper = document.querySelector(
  129. '[class^=ChatMessageInputView_sendButtonWrapper__]'
  130. )
  131. setCss(sendButtonWrapper, {
  132. bottom: 'initial',
  133. top: '50%',
  134. transform: 'translateY(-50%)',
  135. right: '10px'
  136. })
  137. let sendButton = document.querySelector(
  138. '[class*=ChatMessageInputView_sendButton__]'
  139. )
  140. setCss(sendButton, { borderRadius: '50%' })
  141. let messageOverflowButton = document.querySelectorAll(
  142. '[class^=ChatMessage_messageOverflowButton__]'
  143. )
  144. messageOverflowButton.forEach((elem) => {
  145. setCss(elem, { alignSelf: 'flex-end' })
  146. })
  147. let itemList = document.querySelectorAll(
  148. '[class^=DropdownMenuItemList_itemList__]'
  149. )
  150. itemList.forEach((elem) => {
  151. setCss(elem, { borderRadius: borderRadius })
  152. })
  153. }
  154.  
  155. // 美化侧边栏
  156. const setChatSidebarStyle = () => {
  157. let sidebarContainer = document.querySelector(
  158. '[class^=ChatPageSidebar_sidebarContainer__]'
  159. )
  160. setCss(sidebarContainer, { maxWidth: 'initial' })
  161. let sidebar = document.querySelector('[class^=ChatPageSidebar_sidebar__]')
  162. setCss(sidebar, {
  163. maxWidth: 'initial',
  164. paddingLeft: 'initial',
  165. paddingRight: 'initial',
  166. gap: 'initial'
  167. })
  168. let section = document.querySelectorAll(
  169. '[class^=PageWithSidebarNavGroup_section__]'
  170. )
  171. section.forEach((elem) => {
  172. setCss(elem, {
  173. borderRadius: 'initial',
  174. borderBottom: borderCustom
  175. })
  176. })
  177. lastSection = section[section.length - 1]
  178. setCss(lastSection, { borderBottom: 'initial' })
  179. let logo = document.querySelector('[class^=ChatPageSidebar_logo__]')
  180. setCss(logo, { marginBottom: paddingCustom })
  181. let navItem = document.querySelectorAll(
  182. '[class^=PageWithSidebarNavItem_navItem__]'
  183. )
  184. navItem.forEach((elem) => {
  185. setCss(elem, { borderRadius: borderRadius, backgroundColor: 'initial' })
  186. })
  187. let active = document.querySelector(
  188. '[class*=PageWithSidebarNavItem_active__]'
  189. )
  190. setCss(active, { backgroundColor: colorCustomLi })
  191. let downloadButton = document.querySelector(
  192. '[class*=ChatPageDownloadLinks_downloadButton__]'
  193. )
  194. setCss(downloadButton, {
  195. margin: `${marginCustom} 0`,
  196. borderRadius: borderRadius,
  197. paddingTop: paddingCustom,
  198. paddingBottom: paddingCustom
  199. })
  200. }
  201.  
  202. // 其他UI
  203. const setOtherStyle = () => {
  204. let reactModal = document.querySelector('[class^=ReactModal__Content]')
  205. setCss(reactModal, { borderRadius: borderRadius })
  206. let container = document.querySelectorAll(
  207. '[class^=SettingsSection_container__]'
  208. )
  209. container.forEach((elem) => {
  210. setCss(elem, { borderRadius: borderRadius })
  211. })
  212. let sectionBubble = document.querySelector(
  213. '[class^=SettingsSubscriptionSection_sectionBubble__]'
  214. )
  215. setCss(sectionBubble, { borderRadius: borderRadius })
  216. }
  217.  
  218. // 自定义方法
  219. // 判断是否是DOM元素
  220. const isElement = (selector) => {
  221. if (typeof selector === 'string' || selector instanceof String) {
  222. return !!document.querySelector(selector)
  223. } else if (selector instanceof Element) {
  224. return true
  225. } else if (
  226. selector instanceof NodeList ||
  227. selector instanceof HTMLCollection
  228. ) {
  229. return (
  230. selector.length > 0 &&
  231. Array.from(selector).every((elem) => elem instanceof Element)
  232. )
  233. } else if (Array.isArray(selector)) {
  234. return selector.length > 0 && selector.every((elem) => isElement(elem))
  235. } else {
  236. return false
  237. }
  238. }
  239.  
  240. // 修改CSS
  241. // 调用: setCss(['#box1', '#box2'], { background: 'white', color: 'black' })
  242. const setCss = (selectors, cssObj) => {
  243. // 处理selectors为数组的情况
  244. if (!Array.isArray(selectors)) {
  245. selectors = [selectors]
  246. }
  247. selectors.forEach((selector) => {
  248. let elem = isElement(selector)
  249. ? selector
  250. : document.querySelector(selector)
  251. if (elem) {
  252. for (let prop in cssObj) {
  253. elem.style[prop] = cssObj[prop]
  254. }
  255. }
  256. })
  257. }
  258.  
  259. // 修改属性
  260. // 调用: setAttr(['#box1', '#box2'], { class: 'a', href: 'http://example.com' })
  261. const setAttr = (selectors, attrObj) => {
  262. // 处理selectors为数组的情况
  263. if (!Array.isArray(selectors)) {
  264. selectors = [selectors]
  265. }
  266. selectors.forEach((selector) => {
  267. let elem = isElement(selector)
  268. ? selector
  269. : document.querySelector(selector)
  270. if (elem) {
  271. for (let attr in attrObj) {
  272. elem.setAttribute(attr, attrObj[attr])
  273. }
  274. }
  275. })
  276. }
  277.  
  278. // 初始化
  279. const initial = () => {
  280. setInitialStyle()
  281.  
  282. let timer = setInterval(() => {
  283. setLoginStyle()
  284. setChatStyle()
  285. setChatSidebarStyle()
  286. setOtherStyle()
  287. }, 1000)
  288. }
  289.  
  290. // 网页加载成功
  291. window.onload = () => {
  292. initial()
  293. }
  294. })()