Codeforces Modern Design

A modern UI redesign for Codeforces platform

  1. // ==UserScript==
  2. // @name Codeforces Modern Design
  3. // @name:en Codeforces Modern Design
  4. // @namespace https://github.com/funcdfs
  5. // @version 1.2
  6. // @description A modern UI redesign for Codeforces platform
  7. // @description:en A modern UI redesign for Codeforces platform
  8. // @description:zh-CN Codeforces 平台的现代化界面重设计
  9. // @author funcdfs
  10. // @match https://atcoder.jp/contests/*
  11. // @match https://codeforces.com/*/problem/*
  12. // @match https://codeforces.com/contest/*
  13. // @icon https://www.google.com/s2/favicons?sz=64&domain=codeforces.com
  14. // @grant none
  15. // @license MIT
  16. // ==/UserScript==
  17.  
  18. (function () {
  19. 'use strict';
  20. const codeforces_css = () => {
  21. const styleTag = document.createElement('style');
  22. const cssRules = `
  23. pre {
  24. font-family: source code pro;
  25. }
  26. div.ttypography tbody td {
  27. text-align: left;
  28. border-top: 0px solid #ccc;
  29. }
  30. body {
  31. background-color: #e7e9ed0f;
  32. }
  33. .roundbox-lt,
  34. .roundbox-rt,
  35. .roundbox-lb,
  36. .roundbox-rb {
  37. display: none;
  38. }
  39. .roundbox {
  40. padding-bottom: 10px;
  41. border-radius: 10px;
  42. }
  43. .sidebox.roundbox {
  44. margin-top: 10px;
  45. }
  46. .menu-box {
  47. margin-top: 0px !important;
  48. -webkit-transition: none !important;
  49. -moz-transition: none !important;
  50. -ms-transition: none !important;
  51. -o-transition: none !important;
  52. transition: none !important;
  53. }
  54. .menu-box:hover {
  55. -webkit-box-shadow: none !important;
  56. -moz-box-shadow: none !important;
  57. box-shadow: none !important;
  58. }
  59. /** Custom sidebar */
  60. .roundbox {
  61. margin-top: 10px;
  62. border: none;
  63. -webkit-transition: all 0.3s linear 0s;
  64. -moz-transition: all 0.3s linear 0s;
  65. -ms-transition: all 0.3s linear 0s;
  66. -o-transition: all 0.3s linear 0s;
  67. transition: all 0.3s linear 0s;
  68. }
  69. .roundbox:hover {
  70. -webkit-box-shadow: 0 1px 15px 0 rgba(0, 0, 0, 0.1);
  71. -moz-box-shadow: 0 1px 15px 0 rgba(0, 0, 0, 0.1);
  72. box-shadow: 0 1px 15px 0 rgba(0, 0, 0, 0.1);
  73. }
  74. .roundbox .titled {
  75. padding: 10px;
  76. border-bottom-color: #e4e6eb;
  77. }
  78. .sidebox div {
  79. border-bottom-color: #e4e6eb !important;
  80. }
  81.  
  82. /** Sidebar avatar custom */
  83. .personal-sidebar .for-avatar {
  84. float: none !important;
  85. display: block;
  86. font-size: 18px;
  87. padding: 10px;
  88. }
  89.  
  90. .personal-sidebar .for-avatar .avatar img {
  91. border-radius: 50px;
  92. width: 90px;
  93. height: 90px;
  94. object-fit: cover;
  95. }
  96.  
  97. /** Sidebar remove property links*/
  98. .personal-sidebar .propertyLinks {
  99. display: none;
  100. }
  101.  
  102. .personal-sidebar .nav-links {
  103. border-top-style: solid;
  104. border-top-width: 1px;
  105. border-top-color: #e4e6eb;
  106. padding-top: 10px;
  107. }
  108.  
  109. .personal-sidebar .nav-links li {
  110. list-style-type: none !important;
  111. padding: 10px;
  112. font-size: 13px;
  113. border-radius: 5px;
  114. -webkit-transition: all 0.2s linear 0s;
  115. -moz-transition: all 0.2s linear 0s;
  116. -ms-transition: all 0.2s linear 0s;
  117. -o-transition: all 0.2s linear 0s;
  118. transition: all 0.2s linear 0s;
  119. }
  120.  
  121. .personal-sidebar .nav-links li a,
  122. .personal-sidebar .nav-links li a:visited {
  123. color: #050505;
  124. }
  125.  
  126. .personal-sidebar .nav-links li:after {
  127. content: '→';
  128. float: right;
  129. color: #050505;
  130. }
  131.  
  132. .personal-sidebar .nav-links li:hover {
  133. background-color: rgba(0, 0, 0, 0.05);
  134. }
  135.  
  136. .personal-sidebar .nav-links li a {
  137. text-decoration: none !important;
  138. }
  139.  
  140. .roundbox .bottom-links {
  141. padding: 10px;
  142. background-color: #fff;
  143. border-top-color: #e4e6eb !important;
  144. border-bottom-left-radius: 10px !important;
  145. border-bottom-right-radius: 10px !important;
  146. }
  147.  
  148. .roundbox .rtable .dark {
  149. background-color: #fff;
  150. }
  151. /** Topic custom */
  152. .topic {
  153. margin-top: 10px;
  154. padding: 20px;
  155. background-color: #fff;
  156. border-radius: 10px;
  157. -webkit-transition: all 0.3s linear 0s;
  158. -moz-transition: all 0.3s linear 0s;
  159. -ms-transition: all 0.3s linear 0s;
  160. -o-transition: all 0.3s linear 0s;
  161. transition: all 0.3s linear 0s;
  162. }
  163.  
  164. .topic:hover {
  165. -webkit-box-shadow: 0px 1px 15px 0px rgba(0, 0, 0, 0.1);
  166. -moz-box-shadow: 0px 1px 15px 0px rgba(0, 0, 0, 0.1);
  167. box-shadow: 0px 1px 15px 0px rgba(0, 0, 0, 0.1);
  168. }
  169.  
  170. .topic .roundbox {
  171. border-style: solid;
  172. border-color: #e4e6eb;
  173. }
  174.  
  175. .topic .meta a {
  176. text-decoration: none;
  177. }
  178.  
  179. .meta {
  180. padding-bottom: 0px !important;
  181. }
  182.  
  183. .topic .content {
  184. border-left-color: #e4e6eb;
  185. }
  186. .roundbox .rtable td,
  187. .roundbox .rtable th {
  188. border-bottom: 1px solid #e4e6eb;
  189. border-left: none;
  190. padding: 7px;
  191. }
  192.  
  193. /** Header custom */
  194. #header {
  195. padding: 20px;
  196. margin-top: -14px;
  197. background-color: #fff;
  198. }
  199.  
  200. .menu-box {
  201. padding: 10px;
  202. border-radius: 0;
  203. border-top: none;
  204. border-left: none;
  205. border-right: none;
  206. border-bottom-left-radius: 20px;
  207. border-bottom-right-radius: 20px;
  208. border-bottom-color: #e4e6eb;
  209. }
  210.  
  211. /** Footer custom */
  212. #footer {
  213. background-color: #fff;
  214. width: 100%;
  215. height: 100%;
  216. border-top-left-radius: 20px;
  217. border-top-right-radius: 20px;
  218. border-top-color: #e4e6eb;
  219. }
  220.  
  221. /** Contest data table */
  222. .datatable .lt,
  223. .datatable .rt,
  224. .datatable .lb,
  225. .datatable .rb,
  226. .datatable .ilt,
  227. .datatable .irt {
  228. display: none;
  229. }
  230.  
  231. .datatable table {
  232. margin-top: 10px;
  233. border-style: solid;
  234. border-color: #e4e6eb;
  235. border-width: 1px;
  236. }
  237.  
  238. .datatable table .dark {
  239. background-color: #fff;
  240. }
  241.  
  242. .datatable table td {
  243. padding-top: 5px;
  244. padding-bottom: 5px;
  245. padding-left: 10px;
  246. padding-right: 10px;
  247. }
  248.  
  249. .datatable {
  250. padding: 20px !important;
  251. width: inherit;
  252. padding-bottom: 40px !important;
  253. background-color: #fff !important;
  254. border-radius: 10px;
  255. -webkit-transition: all 0.3s linear 0s;
  256. -moz-transition: all 0.3s linear 0s;
  257. -ms-transition: all 0.3s linear 0s;
  258. -o-transition: all 0.3s linear 0s;
  259. transition: all 0.3s linear 0s;
  260. }
  261.  
  262. .datatable:hover {
  263. -webkit-box-shadow: 0px 1px 15px 0px rgba(0, 0, 0, 0.1);
  264. -moz-box-shadow: 0px 1px 15px 0px rgba(0, 0, 0, 0.1);
  265. box-shadow: 0px 1px 15px 0px rgba(0, 0, 0, 0.1);
  266. }
  267.  
  268. .contests-table::first-line {
  269. font-size: 20px;
  270. font-weight: bold !important;
  271. }
  272.  
  273.  
  274. .datatable {
  275. margin-top: 10px !important;
  276. }
  277.  
  278. /** Custom input */
  279. input[type="file"] {
  280. padding: 2px 2px!important;
  281. border: none !important;
  282. }
  283.  
  284. input,
  285. button {
  286. outline: none;
  287. height: auto !important;
  288. padding: 5px 30px 5px 30px !important;
  289. border-radius: 10px;
  290. border-width: 1.5px;
  291. border-color: #3b5998;
  292. font-weight: normal;
  293. -webkit-transition: all 0.3s linear 0s;
  294. -moz-transition: all 0.3s linear 0s;
  295. -ms-transition: all 0.3s linear 0s;
  296. -o-transition: all 0.3s linear 0s;
  297. transition: all 0.3s linear 0s;
  298. }
  299.  
  300. input:hover,
  301. button:hover {
  302. color: rgba(0, 0, 0, 0.7);
  303. background-color: #e4e6eb;
  304. }
  305.  
  306. label[for=searchByProblemCheckbox] {
  307. margin-top: 20px !important;
  308. color: rgba(0, 0, 0, 0.5);
  309. }
  310.  
  311. .notice {
  312. margin-top: 10px !important;
  313. }
  314.  
  315. .highlighted-row td,
  316. .highlighted-row th {
  317. background-color: rgba(221, 238, 255, 0.47) !important;
  318. }
  319.  
  320. input[name=sourceFile] {
  321. border-style: dashed;
  322. border-width: 2px;
  323. border-color: #e4e6eb;
  324. border-radius: 10px;
  325. padding: 5px;
  326. width: 100%;
  327. }
  328.  
  329. .submit-form {
  330. margin-top: 20px;
  331. background-color: #fff;
  332. padding: 20px 0 20px 0;
  333. border-radius: 10px;
  334. -webkit-transition: all 0.3s linear 0s;
  335. -moz-transition: all 0.3s linear 0s;
  336. -ms-transition: all 0.3s linear 0s;
  337. -o-transition: all 0.3s linear 0s;
  338. transition: all 0.3s linear 0s;
  339. }
  340.  
  341. .submit-form:hover {
  342. -webkit-box-shadow: 0px 1px 15px 0px rgba(0, 0, 0, 0.1);
  343. -moz-box-shadow: 0px 1px 15px 0px rgba(0, 0, 0, 0.1);
  344. box-shadow: 0px 1px 15px 0px rgba(0, 0, 0, 0.1);
  345. }
  346.  
  347. .source-popup pre {
  348. padding: 10px;
  349. border-style: solid;
  350. border-width: 2px;
  351. border-color: #e4e6eb;
  352. }
  353.  
  354. hr {
  355. border: 0;
  356. height: 2px;
  357. background-image: linear-gradient(to right, rgba(228, 230, 235, 0.1), rgba(228, 230, 235, 1), rgba(228, 230, 235, 0.1));
  358. }
  359.  
  360. .popup-input-div div,
  361. .popup-output-div div,
  362. .popup-answer-div div,
  363. .popup-checker-div div {
  364. margin-top: 10px;
  365. margin-bottom: 5px;
  366. }
  367.  
  368. .table-form {
  369. padding: 10px;
  370. }
  371. .diff-notifier {
  372. display: none !important;
  373. }
  374. /*main*/
  375. .problemindexholder {
  376. box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;
  377. margin-top: 10px;
  378. background-color: #fff;
  379. padding: 30px;
  380. border-radius: 10px;
  381. -webkit-transition: all 0.3s linear 0s;
  382. -moz-transition: all 0.3s linear 0s;
  383. -ms-transition: all 0.3s linear 0s;
  384. -o-transition: all 0.3s linear 0s;
  385. transition: all 0.3s linear 0s;
  386. }
  387.  
  388. .sample-test {
  389. margin-top: 10px;
  390. }
  391.  
  392. .sample-test .input .title,
  393. .sample-test .output .title {
  394. font-size: 15px;
  395. }
  396.  
  397. .sample-test pre {
  398. padding-top: 10px !important;
  399. padding-bottom: 10px !important;
  400. padding-left: 5px !important;
  401. padding-right: 5px !important;
  402. }
  403.  
  404. .placeholder {
  405. margin-bottom: 10px !important;
  406. }
  407.  
  408. a[href^="/passwordRecovery"] {
  409. display: block;
  410. margin-top: 20px !important;
  411. }
  412.  
  413. .backLava {
  414. border-radius: 10px;
  415. }
  416.  
  417. #body {
  418. max-width: 1220px;
  419. min-width: 980px;
  420. }
  421.  
  422. ::selection {
  423. background-color: #c4b5fd!important;
  424. color: #000!important;
  425. }
  426.  
  427. ::-webkit-scrollbar-thumb {
  428. background: #323536!important;
  429. }
  430. ::-webkit-scrollbar {
  431. width: 10px;
  432. height: 10px;
  433. }
  434. div.ttypography a:hover {
  435. color: #9e88f5 !important;
  436. background: #cfecdc;
  437. }
  438. div.ttypography li,
  439. div.ttypography p {
  440. font-size: 1.1em;
  441. line-height: 1.4em;
  442. }
  443. /** Footer custom */
  444. #footer {
  445. /** background-color: #fff;
  446. width: 100%;
  447. height: 100%;
  448. border-top-left-radius: 20px;
  449. border-top-right-radius: 20px;
  450. border-top-color: #e4e6eb;
  451. **/
  452. display: none !important;
  453. }
  454. pre {
  455. tab-size: 4;
  456. }
  457. .sidebar-menu ul {
  458. font-size: 1.4rem;
  459. }
  460. body {
  461. zoom: 111% !important;
  462. }
  463. /** move pos, if you want the pos, just delete below string **/
  464. #header {
  465. position: absolute !important;
  466. top: 20px;
  467. z-index: 1;
  468. right: 20px;
  469. /* 将元素右对齐 */
  470. width: 220px;
  471. background: #00000000;
  472. }
  473. .menu-box {
  474. background: #00000000;
  475. width: 70%;
  476. }
  477. .property-title,
  478. #header > div:nth-child(1) {
  479. display: none !important;
  480. }
  481. /** info remove to bottom */
  482. .time-limit {
  483. position: fixed !important;
  484. right: 30px;
  485. bottom: 70px;
  486. z-index: 9;
  487. }
  488. .memory-limit {
  489. position: fixed !important;
  490. right: 30px;
  491. bottom: 50px;
  492. z-index: 9;
  493.  
  494. }
  495. .input-file {
  496. position: fixed !important;
  497. right: 30px;
  498. bottom: 30px;
  499. z-index: 9;
  500. }
  501. .output-file {
  502. position: fixed !important;
  503. right: 30px;
  504. bottom: 10px;
  505. z-index: 9;
  506. }
  507. `;
  508. styleTag.appendChild(document.createTextNode(cssRules));
  509. document.head.appendChild(styleTag);
  510. };
  511. const codeforces_luogu = () => {
  512. function getProblemId(str) {
  513. let pos = str.indexOf("contest/") + "contest/".length;
  514. let contestNum = "";
  515. while (str[pos] >= '0' && str[pos] <= '9') {
  516. contestNum += str[pos];
  517. pos++;
  518. }
  519. return contestNum;
  520. }
  521. function getProblemChar(str) {
  522. let pos = str.indexOf("problem/") + "problem/".length;
  523. let problemLetter = str.substr(pos);
  524. return problemLetter;
  525. }
  526. // jump to luogu.com 跳转到洛谷 delete luogu
  527. let str = window.location.href
  528. let problemId = getProblemId(str);
  529. let problemChar = getProblemChar(str);
  530. let luogulink = document.createElement('li');
  531. luogulink.setAttribute('style', 'color: green !important;');
  532. luogulink.innerHTML = `<a href="https://www.luogu.com.cn/problem/CF${problemId}${problemChar}" target="_blank"> 洛谷 </a>`;
  533. luogulink.classList.add('luogulink')
  534. document.querySelector("#sidebar > div.roundbox.sidebox.sidebar-menu.borderTopRound > ul").appendChild(luogulink)
  535. };
  536. const codeforces_friend_status = () => {
  537. // friends-status-button 好友提交历史记录
  538. let uuuuuuuuurl = window.location;
  539. let cccontestId = uuuuuuuuurl.toString().split("/").filter((x) => {
  540. if (typeof x !== 'string') { return; }
  541. const num = Number(x);
  542. if (Number.isInteger(num)) { return num; }
  543. })[0];
  544. let s = uuuuuuuuurl.toString().split("/");
  545. let iiid = s[s.length - 1];
  546. let friendBtn = document.createElement('li');
  547. friendBtn.innerHTML = `<a href="https://codeforces.com/contest/${cccontestId}/status/${iiid}?friends=on" target="_blank">Friends Status</a>`;
  548. friendBtn.classList.add('friendBtn')
  549. document.querySelector(".second-level-menu-list").appendChild(friendBtn);
  550. };
  551. const codeforces_timeLimits = () => {
  552. // time limits part 时间限制等 放到一个位置
  553. /* const timeLimits = document.querySelector(".time-limit");
  554. const memoryLimits = document.querySelector(".memory-limit");
  555. const inputLimits = document.querySelector(".input-file");
  556. const outputLimits = document.querySelector(".output-file");
  557. let informationSpace = document.querySelector();
  558. informationSpace.parentNode.insertBefore(informationSpace, info); */
  559. //informationSpace.appendChild(timeLimits);
  560. //informationSpace.appendChild(memoryLimits);
  561. //informationSpace.appendChild(inputLimits);
  562. //informationSpace.appendChild(outputLimits);
  563. };
  564. const KEY_PREFIX = 'funcdfs';
  565. const atcoder_navigation = () => {
  566. const contest = location.href.match(/^https:\/\/atcoder\.jp\/contests\/([^\/?]+)/)[1];
  567. const key = KEY_PREFIX + 'atcoder-' + contest;
  568. if (location.href.match(/^https:\/\/atcoder\.jp\/contests\/([^\/]+)\/tasks\/?$/)) {
  569. const problems = [];
  570. const rows = document.querySelectorAll('tbody>tr');
  571. for (let i = 0; i < rows.length; i++) {
  572. const links = rows[i].querySelectorAll('a');
  573. const href = links[0].getAttribute('href');
  574. const text = links[0].textContent + ' - ' + links[1].textContent;
  575. problems.push({
  576. href: href,
  577. text: text
  578. });
  579. }
  580. localStorage[key] = JSON.stringify(problems);
  581. }
  582. if (key in localStorage) {
  583. let problems = JSON.parse(localStorage[key]);
  584. const problemsBar = document.createElement('ul');
  585. problemsBar.className = 'nav nav-tabs';
  586. for (let i = 0; i < problems.length; i++) {
  587. const link = document.createElement('a');
  588. link.setAttribute('style', 'margin-left: 10px; margin-right: 10px; white-space: nowrap');
  589. link.setAttribute('href', problems[i].href);
  590. link.textContent = problems[i].text;
  591. const span = document.createElement('span');
  592. span.textContent = ' ';
  593. span.appendChild(link);
  594. problemsBar.appendChild(span);
  595. }
  596. document.getElementById('contest-nav-tabs').appendChild(problemsBar);
  597. }
  598. };
  599. const codeforces_navigation = () => {
  600. const contest = location.href.match(/^https:\/\/codeforces\.com\/contest\/([^\/?]+)/)[1];
  601. const key = KEY_PREFIX + 'codeforces-' + contest;
  602. if (location.href.match(/^https:\/\/codeforces\.com\/contest\/([^\/]+)\/?$/)) {
  603. const problems = [];
  604. const rows = document.querySelectorAll('.problems>tbody>tr');
  605. for (let i = 1; i < rows.length; i++) {
  606. const links = rows[i].querySelectorAll('a');
  607. const href = links[0].getAttribute('href');
  608. const text = links[0].textContent.trim() + '. ' + links[1].textContent;
  609. problems.push({
  610. href: href,
  611. text: text
  612. });
  613. }
  614. localStorage[key] = JSON.stringify(problems);
  615. }
  616.  
  617. if (key in localStorage) {
  618. let problems = JSON.parse(localStorage[key]);
  619. const problemsBar = document.createElement('ul');
  620. problemsBar.setAttribute('style', 'margin-left: 15px; margin-right: 280px; padding-top: 30px');
  621. for (let i = 0; i < problems.length; i++) {
  622. const link = document.createElement('a');
  623. link.setAttribute('style', 'margin-right: 20px; white-space: nowrap');
  624. link.setAttribute('href', problems[i].href);
  625. link.textContent = problems[i].text;
  626. const span = document.createElement('span');
  627. span.textContent = ' ';
  628. span.appendChild(link);
  629. problemsBar.appendChild(span);
  630. }
  631.  
  632. const content = document.getElementById('pageContent');
  633. content.parentNode.insertBefore(problemsBar, content);
  634. }
  635. };
  636. if (location.href.match(/^https:\/\/atcoder\.jp\/contests\//)) {
  637. atcoder_navigation();
  638. } else {
  639. if (location.href.match(/^https:\/\/codeforces\.com\/contest\//)) {
  640. codeforces_navigation();
  641. }
  642. codeforces_css();
  643. codeforces_luogu();
  644. codeforces_friend_status();
  645. codeforces_timeLimits();
  646. }
  647. })();