PoeUI

美化Poe网页版UI样式

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, Greasemonkey alebo Violentmonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey alebo Userscripts.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie správcu používateľských skriptov.

(Už mám správcu používateľských skriptov, nechajte ma ho nainštalovať!)

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

(Už mám správcu používateľských štýlov, nechajte ma ho nainštalovať!)

// ==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()
  }
})()