ACWing One Theme

make acwing coding UI look more like leetcode

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         ACWing One Theme
// @name:zh-CN  ACWing One Theme
// @namespace    https://whalien.space
// @author       whalien
// @version      0.2
// @description  make acwing coding UI look more like leetcode
// @description:zh-CN 让acwing编程界面看起来更像leetcode
// @icon         https://cdn.acwing.com/static/web/img/favicon.ico
// @match        https://www.acwing.com/problem/content/*
// @run-at document-end
// @grant        GM_addStyle
// @grant        GM_log
// @license      WTFPL
// ==/UserScript==

const customCSS = `
/*---------- global settings ----------*/
.file-explorer-main-field-item.file-explorer-main-field-item-desktop {
  display: none;
}

.base_body {
  padding-top: 72px !important;
}

.base_body > .container {
  width: 98%;
}

.container > .panel.panel-default > .panel-body {
  height: calc(100vh - 72px - 20px);
  padding: 10px 15px;
}

.container > .panel > .panel-body > .row {
  height: 100%;
  margin-top: 8px;
}

.base_body .row .problem-content-container {
  max-height: calc(100% - 100px);
  overflow-y: auto;
}

.container > .panel .panel-body .problem-content-title {
  font-size: 24px;
  margin: 5px 0 10px 0;
}

.base_body .row .code-editor-container {
  height: calc(100% - 110px);
  max-height: 100%;
  overflow-y: auto;
  width: calc(50% - 24px);
}
/*---------- end of global settings ----------*/

/*---------- navigation settings ----------*/
.container > .panel .nav.nav-tabs .problem-content-sub-btn{
  font-size: 1.5rem;
}

.container > .panel .nav.nav-tabs .problem-content-sub-btn .glyphicon {
  top: 2px !important;
}
/*----------- end of navigation settings ----------*/

/*----------- remove br before code editor ----------*/
.container > .panel .panel-body .nav-tabs.nav + br {
  display: none;
}

/*----------- code editor settings toolset ----------*/
.base_body .code-editor-container #code_tool_bar {
  height: 48px;
}

.base_body #code_tool_bar #open_ac_saber_btn{
  top: 9px !important;
}

.base_body #code_tool_bar .btn {
  padding-top: 0;
}

.base_body #code_tool_bar .btn .editor_tool_btn {
  padding-right: 10px;
}

.base_body #code_tool_bar .code_editor_option_language {
  margin-top: 3px !important;
}
/*----------- end of code editor settings toolset ----------*/

/*----------- submit controls ----------*/
#code_editor + div {
  position: fixed;
  bottom: 2px;
  right: 200px;
  z-index: 10000000;
}

/*
#code_editor + div .btn {
  padding: 4px 8px;
}
*/

#data-augmentation-div {
  padding-top: 27px !important;
  padding-right: 12px !important;
  color: white !important;
}
/*----------- end of submit controls ----------*/

/*----------- evaluation block ----------*/
#submit-code-status-block {
  margin-top: 25px !important;
}

#run-code-status-block {
  margin-top: 25px !important;
}
/*----------- end of evaluation block ----------*/

/*----------- dragable wedge ----------*/
.base_body .dragable-wedge {
  float: left;
/*  display: inline-block; */
  width: 10px;
  height: calc(100% - 94px);
}

.base_body .dragable-wedge:hover {
  background-color: rgb(0, 122, 255);
  cursor: col-resize;
}
/*----------- end of dragable wedge ----------*/
`

const throttle = (fn, delay) => {
    let timer = null
    return (...args) => {
        if(timer) return
        timer = setTimeout(() => {
            clearTimeout(timer)
            timer = null
        }, delay)
        fn(...args)
    }
}

function adjustLayout() {
    const problemContainer = document.querySelector('.panel-body .row')

    // make probleDiv take half the width
    const problemDiv = document.querySelector('.panel-body .row .col-sm-9.col-xs-12')
    problemDiv.className = 'problem-content-container col-sm-6 col-xs-12'
    // make infoDiv take the same width as problemDiv
    const infoDiv = document.querySelector('.panel-body .row .col-sm-3.hidden-xs')
    infoDiv.className=''
    problemDiv.appendChild(infoDiv)

    // move code editor to the same row as problem content
    const fragment = document.createDocumentFragment()
    const codeEditorDiv = document.createElement('div')
    codeEditorDiv.className = 'code-editor-container col-sm-5'
    const codeEditorFrag = document.querySelectorAll('.panel-body > .row ~ div')
    codeEditorFrag.forEach((editorFrag) => {
        codeEditorDiv.appendChild(editorFrag)
    })
    fragment.appendChild(codeEditorDiv)
    problemContainer.appendChild(fragment)

    // add dragable resizer
    const dragableDiv = document.createElement('div')
    dragableDiv.className = 'dragable-wedge'
    problemContainer.insertBefore(dragableDiv, codeEditorDiv)

    // get the last container width and set
    const lastProblemWidth = localStorage.getItem('problemWidth')
    const lastCodeEditorWidth = localStorage.getItem('codeEditorWidth')
    if(lastProblemWidth!==null && lastCodeEditorWidth!==null) {
        problemDiv.style.width = lastProblemWidth
        codeEditorDiv.style.width = lastCodeEditorWidth
    }
}

let lastX = -1
function enableDragAndDrop() {
    const problemContent = document.querySelector('.base_body .problem-content-container')
    const codeEditor = document.querySelector('.base_body .code-editor-container')
    const resizer = document.querySelector('.base_body .dragable-wedge')
    const dragEventHandler = (e) => {
        const dx = lastX == -1 ? 0:e.clientX - lastX
        lastX = e.clientX
        if(dx > 100 || dx == 0) return

        let problemContentWidth = problemContent.getBoundingClientRect().width + dx
        let codeEditorWidth = codeEditor.getBoundingClientRect().width - dx
        if(problemContentWidth < 0 || codeEditorWidth < 0) {
            return
        }

        problemContentWidth = `${problemContentWidth}px`
        codeEditorWidth = `${codeEditorWidth}px`
        problemContent.style.width = problemContentWidth
        codeEditor.style.width = codeEditorWidth
        localStorage.setItem('problemWidth', problemContentWidth)
        localStorage.setItem('codeEditorWidth', codeEditorWidth)
    }
    resizer.addEventListener('drag', throttle(dragEventHandler, 60))
}

function bindSubmitScrollBehavior() {
    const codeEditorContainer = document.querySelector('.base_body .code-editor-container')
    const submitBtn = document.querySelector('#submit_code_btn')
    const runCodeBtn = document.querySelector('#run_code_btn')
    // FIXME(whalien): figure out why we cannot delegate click event to the outer div
    submitBtn.addEventListener('click', (e) => {
        setTimeout(()=> {
            codeEditorContainer.scrollTo({
                left: 0,
                top: codeEditorContainer.scrollHeight,
                behavior: 'smooth'
            })
        }, 1000)
    })
    runCodeBtn.addEventListener('click', (e) => {
        setTimeout(()=> {
            codeEditorContainer.scrollTo({
                left: 0,
                top: codeEditorContainer.scrollHeight,
                behavior: 'smooth'
            })
        }, 1000)
    });
}

(function() {
    'use strict';
    adjustLayout()

    GM_addStyle(customCSS)

    enableDragAndDrop()

    bindSubmitScrollBehavior()
})();