PoeUI

美化Poe网页版UI样式

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name                PoeUI
// @namespace           http://xiaomizha.ltd/
// @version             0.1
// @description         美化Poe网页版UI样式
// @author              xuyou
// @description:en      We embellished the Poe web UI style.
// @description:zh-CN   美化Poe网页版UI样式
// @license             MIT
// @match               https://poe.com/*
// @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=
// @grant               GM_registerMenuCommand
// ==/UserScript==

;(() => {
  ;('use strict')

  // 重构 Login UI

  // 默认配置
  const borderRadius = '4px'
  const paddingCustom = '16px'
  const marginCustom = '20px'
  const colorCustomLi = '#EFEFFC'
  const colorCustom = '#5D5CDE'
  const borderCustom = '1px solid #e6e7e9'
  const fontFamilyLists = [
    {
      name: 'Loto',
      href: 'https://fonts.googleapis.com/css2?family=Lato&display=swap'
    },
    {
      name: 'Pathway Extreme',
      href: 'https://fonts.googleapis.com/css2?family=Pathway+Extreme&display=swap'
    }
  ]
  const fontFamily = 'Lato, "Pathway Extreme", "Microsoft YaHei", sans-serif'

  // 修改初始样式
  // Modify initial style
  const setInitialStyle = () => {
    // 创建<style>标签
    let styles = document.createElement('style')
    // 设置字体样式
    let css = `
    * {
        transition                 : all .3s;
        caret-color                : ${colorCustom};
        font-family                : ${fontFamily} !important;
        -webkit-tap-highlight-color: rgba(0, 0, 0, 0) !important;
        text-shadow                : 0 0 1.15px #a4a09ad9, 0 0 1px #7b7b7bcc, 0 0 0.75px #302f2d45 !important;
    }
    ::selection {
        color      : #FFFFFF !important;
        background : ${colorCustom} !important;
        text-shadow: none !important;
    }
    `
    // 添加CSS规则
    styles.append(document.createTextNode(css))
    // 将<style>标签加到head中
    document.head.appendChild(styles)
    // 动态添加 @import 规则
    fontFamilyLists.forEach((item) => {
      styles.sheet.insertRule(`@import url(${item.href});`)
    })
  }

  // 美化登录页
  const setLoginStyle = () => {
    let buttonBase = document.querySelectorAll('[class^=Button_buttonBase__]')
    buttonBase.forEach((elem) => {
      setCss(elem, {
        borderRadius: borderRadius,
        paddingTop: paddingCustom,
        paddingBottom: paddingCustom
      })
    })
  }

  // 全选所有历史记录
  GM_registerMenuCommand('全选所有历史记录', () => {
    // 执行全选操作
    let checkbox = document.querySelectorAll('.Checkbox_checkbox__zM_Lo')
    for (var i = 0; i < checkbox.length; i++) {
      if (!checkbox[i].checked) {
        checkbox[i].click()
      }
    }
  })

  // 聊天区最大化
  const setChatStyle = () => {
    let leftSidebar = document.querySelector(
      '[class^=PageWithSidebarLayout_leftSidebar__]'
    )
    setCss(leftSidebar, { flexGrow: 1 })
    let mainSection = document.querySelector(
      '[class^=PageWithSidebarLayout_mainSection__]'
    )
    setCss(mainSection, { flexGrow: 2, maxWidth: 'initial' })
    let rightSidebar = document.querySelector(
      '[class^=PageWithSidebarLayout_rightSidebar__]'
    )
    setCss(rightSidebar, { display: 'none' })
    let welcomeButtonsContainer = document.querySelectorAll(
      '[class^=ChatWelcomeView_welcomeButtonsContainer__]'
    )
    welcomeButtonsContainer.forEach((elem) => {
      setCss(elem, { borderRadius: borderRadius })
    })
    let humanMessageBubble = document.querySelectorAll(
      '[class^=Message_humanMessageBubble__]'
    )
    humanMessageBubble.forEach((elem) => {
      setCss(elem, { borderRadius: borderRadius })
    })
    let botMessageBubble = document.querySelectorAll(
      '[class^=Message_botMessageBubble__]'
    )
    botMessageBubble.forEach((elem) => {
      setCss(elem, { borderRadius: borderRadius })
    })
    let textInput = document.querySelector(
      '[class^=ChatMessageInputView_textInput__]'
    )
    setCss(textInput, { paddingTop: marginCustom, borderRadius: borderRadius })
    let sendButtonWrapper = document.querySelector(
      '[class^=ChatMessageInputView_sendButtonWrapper__]'
    )
    setCss(sendButtonWrapper, {
      bottom: 'initial',
      top: '50%',
      transform: 'translateY(-50%)',
      right: '10px'
    })
    let sendButton = document.querySelector(
      '[class*=ChatMessageInputView_sendButton__]'
    )
    setCss(sendButton, { borderRadius: '50%' })
    let messageOverflowButton = document.querySelectorAll(
      '[class^=ChatMessage_messageOverflowButton__]'
    )
    messageOverflowButton.forEach((elem) => {
      setCss(elem, { alignSelf: 'flex-end' })
    })
    let itemList = document.querySelectorAll(
      '[class^=DropdownMenuItemList_itemList__]'
    )
    itemList.forEach((elem) => {
      setCss(elem, { borderRadius: borderRadius })
    })
  }

  // 美化侧边栏
  const setChatSidebarStyle = () => {
    let sidebarContainer = document.querySelector(
      '[class^=ChatPageSidebar_sidebarContainer__]'
    )
    setCss(sidebarContainer, { maxWidth: 'initial' })
    let sidebar = document.querySelector('[class^=ChatPageSidebar_sidebar__]')
    setCss(sidebar, {
      maxWidth: 'initial',
      paddingLeft: 'initial',
      paddingRight: 'initial',
      gap: 'initial'
    })
    let section = document.querySelectorAll(
      '[class^=PageWithSidebarNavGroup_section__]'
    )
    section.forEach((elem) => {
      setCss(elem, {
        borderRadius: 'initial',
        borderBottom: borderCustom
      })
    })
    lastSection = section[section.length - 1]
    setCss(lastSection, { borderBottom: 'initial' })
    let logo = document.querySelector('[class^=ChatPageSidebar_logo__]')
    setCss(logo, { marginBottom: paddingCustom })
    let navItem = document.querySelectorAll(
      '[class^=PageWithSidebarNavItem_navItem__]'
    )
    navItem.forEach((elem) => {
      setCss(elem, { borderRadius: borderRadius, backgroundColor: 'initial' })
    })
    let active = document.querySelector(
      '[class*=PageWithSidebarNavItem_active__]'
    )
    setCss(active, { backgroundColor: colorCustomLi })
    let downloadButton = document.querySelector(
      '[class*=ChatPageDownloadLinks_downloadButton__]'
    )
    setCss(downloadButton, {
      margin: `${marginCustom} 0`,
      borderRadius: borderRadius,
      paddingTop: paddingCustom,
      paddingBottom: paddingCustom
    })
  }

  // 其他UI
  const setOtherStyle = () => {
    let reactModal = document.querySelector('[class^=ReactModal__Content]')
    setCss(reactModal, { borderRadius: borderRadius })
    let container = document.querySelectorAll(
      '[class^=SettingsSection_container__]'
    )
    container.forEach((elem) => {
      setCss(elem, { borderRadius: borderRadius })
    })
    let sectionBubble = document.querySelector(
      '[class^=SettingsSubscriptionSection_sectionBubble__]'
    )
    setCss(sectionBubble, { borderRadius: borderRadius })
  }

  // 自定义方法
  // 判断是否是DOM元素
  const isElement = (selector) => {
    if (typeof selector === 'string' || selector instanceof String) {
      return !!document.querySelector(selector)
    } else if (selector instanceof Element) {
      return true
    } else if (
      selector instanceof NodeList ||
      selector instanceof HTMLCollection
    ) {
      return (
        selector.length > 0 &&
        Array.from(selector).every((elem) => elem instanceof Element)
      )
    } else if (Array.isArray(selector)) {
      return selector.length > 0 && selector.every((elem) => isElement(elem))
    } else {
      return false
    }
  }

  // 修改CSS
  // 调用: setCss(['#box1', '#box2'], { background: 'white', color: 'black' })
  const setCss = (selectors, cssObj) => {
    // 处理selectors为数组的情况
    if (!Array.isArray(selectors)) {
      selectors = [selectors]
    }
    selectors.forEach((selector) => {
      let elem = isElement(selector)
        ? selector
        : document.querySelector(selector)
      if (elem) {
        for (let prop in cssObj) {
          elem.style[prop] = cssObj[prop]
        }
      }
    })
  }

  // 修改属性
  // 调用: setAttr(['#box1', '#box2'], { class: 'a', href: 'http://example.com' })
  const setAttr = (selectors, attrObj) => {
    // 处理selectors为数组的情况
    if (!Array.isArray(selectors)) {
      selectors = [selectors]
    }
    selectors.forEach((selector) => {
      let elem = isElement(selector)
        ? selector
        : document.querySelector(selector)
      if (elem) {
        for (let attr in attrObj) {
          elem.setAttribute(attr, attrObj[attr])
        }
      }
    })
  }

  // 初始化
  const initial = () => {
    setInitialStyle()

    let timer = setInterval(() => {
      setLoginStyle()
      setChatStyle()
      setChatSidebarStyle()
      setOtherStyle()
    }, 1000)
  }

  // 网页加载成功
  window.onload = () => {
    initial()
  }
})()