Bringing USACO's UI into the 21st century
// ==UserScript==
// @name Modern USACO
// @namespace http://tampermonkey.net/
// @version 2026.1.1
// @description Bringing USACO's UI into the 21st century
// @author Earth1283
// @license AGPLv3
// @match http://www.usaco.org/*
// @match https://www.usaco.org/*
// @match http://usaco.org/*
// @match https://usaco.org/*
// @grant none
// ==/UserScript==
(function () {
'use strict';
// warning terrible css hacks ahead
const welcomeCss = `
/* Welcome Popup */
#usaco-welcome-backdrop {
display: flex;
position: fixed;
top: 0; left: 0; right: 0; bottom: 0;
background: rgba(0, 0, 0, 0.6);
backdrop-filter: blur(8px);
z-index: 9999999;
align-items: center;
justify-content: center;
opacity: 0;
animation: fadeIn 0.3s forwards;
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}
#usaco-welcome-modal {
background: #1c1c1e;
border: 1px solid rgba(255, 255, 255, 0.15);
border-radius: 16px;
padding: 32px;
max-width: 500px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
transform: scale(0.9);
animation: popupScale 0.3s cubic-bezier(0.16, 1, 0.3, 1) forwards;
color: #f2f2f7;
display: flex !important;
flex-direction: column !important;
text-align: left !important;
align-items: stretch !important;
justify-content: flex-start !important;
}
#usaco-welcome-modal * {
box-sizing: border-box !important;
float: none !important;
}
#usaco-welcome-modal h2 {
margin-top: 0;
color: #ffffff;
font-size: 1.8rem;
border: none !important;
padding-left: 0 !important;
display: block !important;
width: 100% !important;
text-align: left !important;
margin-bottom: 12px !important;
}
#usaco-welcome-modal p, #usaco-welcome-modal ul {
font-size: 1.05rem;
line-height: 1.6;
margin-bottom: 20px;
display: block !important;
text-align: left !important;
}
#usaco-welcome-modal ul {
padding-left: 20px;
}
#usaco-welcome-modal li {
margin-bottom: 8px;
display: list-item !important;
text-align: left !important;
}
#btn-welcome-dismiss {
background: #0a84ff !important;
color: #ffffff !important;
border: none !important;
padding: 10px 20px !important;
border-radius: 20px !important;
font-family: inherit !important;
font-weight: 600 !important;
cursor: pointer !important;
transition: all 0.25s ease !important;
font-size: 0.95rem !important;
display: inline-block !important;
width: auto !important;
margin: 0 !important;
box-sizing: border-box !important;
appearance: none !important;
-webkit-appearance: none !important;
line-height: normal !important;
text-align: center !important;
text-decoration: none !important;
vertical-align: middle !important;
outline: none !important;
box-shadow: none !important;
}
#btn-welcome-dismiss:hover {
background: #007aff !important;
transform: scale(0.97) !important;
opacity: 0.9 !important;
}
@keyframes popupScale {
to { transform: scale(1); opacity: 1; }
}
@keyframes fadeIn {
to { opacity: 1; }
}
`;
const welcomeStyleSheet = document.createElement("style");
welcomeStyleSheet.textContent = welcomeCss;
document.head.appendChild(welcomeStyleSheet);
// more terrible css hacks
const css = `
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap');
:root {
--bg-primary: #000000;
--bg-secondary: #1c1c1e;
--bg-panel: rgba(28, 28, 30, 0.75);
--accent: #0a84ff;
--highlight: #0a84ff;
--text-main: #f2f2f7;
--text-heading: #ffffff;
--text-muted: #8e8e93;
--font-ui: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
--font-mono: "SF Mono", "Menlo", "Monaco", "Consolas", "JetBrains Mono", monospace;
--border-subtle: rgba(255, 255, 255, 0.15);
--shadow-elevation: 0 10px 40px rgba(0, 0, 0, 0.5);
--transition-speed: 0.25s;
}
body {
background: var(--bg-primary) !important;
background-attachment: fixed !important;
color: var(--text-main) !important;
font-family: var(--font-ui) !important;
margin: 0 !important;
padding: 0 !important;
min-height: 100vh;
-webkit-font-smoothing: antialiased;
}
/* Base Layout */
.shadow1 {
background: none !important;
border: none !important;
box-shadow: none !important;
max-width: 1000px !important;
margin: 0 auto !important;
padding: 20px !important;
}
/* Core Document Wrapper */
.content {
background: var(--bg-panel) !important;
backdrop-filter: blur(12px) !important;
-webkit-backdrop-filter: blur(12px) !important;
border: 1px solid var(--border-subtle) !important;
border-radius: 16px !important;
padding: 32px !important;
box-shadow: var(--shadow-elevation) !important;
display: flex !important;
flex-direction: column !important;
gap: 20px !important;
}
/* Navbar */
.navbar {
background: var(--bg-panel) !important;
backdrop-filter: blur(12px) !important;
border: 1px solid var(--border-subtle) !important;
border-radius: 12px !important;
padding: 10px 20px !important;
margin-top: -10px !important;
}
.navbar ul {
display: flex !important;
gap: 15px !important;
list-style: none !important;
padding: 0 !important;
margin: 0 !important;
flex-wrap: wrap !important;
}
.navbar li {
margin: 0 !important;
display: inline-block !important; /* fallback */
}
.navbar a {
color: var(--text-heading) !important;
text-decoration: none !important;
padding: 8px 16px !important;
border-radius: 8px !important;
font-weight: 500 !important;
transition: all var(--transition-speed) ease !important;
display: block !important;
}
.navbar a:hover {
background: var(--accent) !important;
transform: translateY(-2px) !important;
}
/* Typography */
h1, h2, h3, h4, h5, h6 {
color: var(--text-heading) !important;
font-weight: 600 !important;
}
a {
color: var(--highlight) !important;
text-decoration: none !important;
transition: opacity var(--transition-speed) ease !important;
}
a:hover {
opacity: 0.8 !important;
text-decoration: underline !important;
}
/* Panels (Cards within document) */
@keyframes fadeInUp {
from { opacity: 0; transform: translateY(15px); }
to { opacity: 1; transform: translateY(0); }
}
.panel {
background: none !important;
backdrop-filter: none !important;
-webkit-backdrop-filter: none !important;
border: none !important;
border-bottom: 1px solid var(--border-subtle) !important;
border-radius: 0 !important;
padding: 24px 0 !important;
box-shadow: none !important;
color: var(--text-main) !important;
margin-bottom: 0 !important;
animation: fadeInUp 0.5s ease forwards !important;
}
.panel:last-child {
border-bottom: none !important;
}
.panel:hover {
transform: none !important;
box-shadow: none !important;
}
.panel h2 {
border-left: 4px solid var(--highlight) !important;
padding-left: 12px !important;
margin-top: 0 !important;
margin-bottom: 16px !important;
background: none !important;
border-bottom: none !important;
font-size: 1.5rem !important;
}
/* Command Palette */
#usaco-cmd-palette-backdrop {
display: none;
position: fixed;
top: 0; left: 0; right: 0; bottom: 0;
background: rgba(0, 0, 0, 0.4);
backdrop-filter: blur(4px);
z-index: 999998;
align-items: flex-start;
justify-content: center;
padding-top: 15vh;
}
#usaco-cmd-palette {
width: 100%;
max-width: 600px;
background: var(--bg-secondary);
border: 1px solid var(--border-subtle);
border-radius: 12px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
overflow: hidden;
display: flex;
flex-direction: column;
z-index: 999999;
animation: paletteScale 0.15s cubic-bezier(0.16, 1, 0.3, 1) forwards;
opacity: 0;
transform: scale(0.96);
}
@keyframes paletteScale {
to { opacity: 1; transform: scale(1); }
}
#usaco-cmd-input-wrapper {
display: flex;
align-items: center;
padding: 16px;
border-bottom: 1px solid var(--border-subtle);
}
#usaco-cmd-input {
width: 100%;
background: transparent !important;
border: none !important;
color: var(--text-heading) !important;
font-size: 1.2rem !important;
font-family: var(--font-ui) !important;
outline: none !important;
padding: 0 !important;
box-shadow: none !important;
}
#usaco-cmd-results {
max-height: 400px;
overflow-y: auto;
padding: 8px;
}
.usaco-cmd-item {
padding: 12px 16px;
color: var(--text-main) !important;
border-radius: 8px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: space-between;
text-decoration: none !important;
}
.usaco-cmd-item:hover, .usaco-cmd-item.selected {
background: #0a84ff !important;
color: #ffffff !important;
text-decoration: none !important;
opacity: 1 !important;
}
.usaco-cmd-item-title {
font-weight: 500;
}
.usaco-cmd-item-shortcut {
font-size: 0.8rem;
color: var(--text-muted);
background: rgba(255,255,255,0.1);
padding: 2px 6px;
border-radius: 4px;
}
.usaco-cmd-item.selected .usaco-cmd-item-shortcut {
color: rgba(255,255,255,0.8);
}
/* History Panels */
.historypanel {
display: flex !important;
align-items: center !important;
padding: 16px 24px !important;
}
.historypanel > div:first-child {
margin-right: 20px !important;
padding-right: 0 !important;
}
.historypanel h1 {
background: var(--accent) !important;
color: #fff !important;
padding: 12px 20px !important;
border-radius: 12px !important;
margin: 0 !important;
font-size: 1.7rem !important;
display: inline-block !important;
}
.historypanel > div:nth-child(2) {
flex-grow: 1 !important;
}
/* Headers with Medals */
h2 > img[src*="medal"] {
vertical-align: middle !important;
margin-right: 12px !important;
filter: drop-shadow(0 2px 4px rgba(0,0,0,0.5));
}
/* Problem Page Elements */
#last-status {
background: var(--bg-panel) !important;
backdrop-filter: blur(12px) !important;
border: 1px solid var(--border-subtle) !important;
border-left: 6px solid var(--accent) !important;
border-radius: 12px !important;
color: var(--text-main) !important;
box-shadow: var(--shadow-elevation) !important;
}
#last-status.status-working { border-left-color: #f39c12 !important; }
#last-status.status-error { border-left-color: #e74c3c !important; }
#last-status.status-done { border-left-color: #2ecc71 !important; }
.problem-text, .problem-text * {
background-color: transparent !important;
}
div[style*="background-color:#FFF"], div[style*="background-color: #FFF"], div[style*="background-color: rgb(255, 255, 255)"], div[style*="background: white"], div[style*="background:white"] {
background-color: transparent !important;
}
.problem-text {
line-height: 1.7 !important;
font-size: 1.1rem !important;
color: var(--text-main) !important;
background: none !important;
}
.problem-text pre {
background: #0d0d1a !important;
color: #a0a0b0 !important;
font-family: var(--font-mono) !important;
padding: 16px !important;
border-radius: 8px !important;
border: 1px solid var(--border-subtle) !important;
overflow-x: auto !important;
}
.problem-text pre.in, .problem-text pre.out {
color: #d4d4d4 !important;
}
/* Submission Form / General Form Controls */
.submission {
background: var(--bg-panel) !important;
padding: 24px !important;
border-radius: 16px !important;
border: 1px solid var(--border-subtle) !important;
}
.field2 {
margin-bottom: 16px !important;
}
select, input[type="file"], input[type="text"], input[type="password"] {
background: rgba(0,0,0,0.2) !important;
color: var(--text-heading) !important;
border: 1px solid var(--border-subtle) !important;
padding: 8px 12px !important;
border-radius: 8px !important;
font-family: var(--font-ui) !important;
}
select:focus, input:focus {
outline: none !important;
border-color: var(--accent) !important;
box-shadow: 0 0 0 2px rgba(15, 52, 96, 0.5) !important;
}
select option {
background: var(--bg-primary) !important;
color: var(--text-heading) !important;
}
/* Buttons */
button, input[type="button"], input[type="submit"] {
background: var(--accent) !important;
color: #ffffff !important;
border: none !important;
padding: 10px 20px !important;
border-radius: 20px !important;
font-family: var(--font-ui) !important;
font-weight: 600 !important;
cursor: pointer !important;
transition: all var(--transition-speed) ease !important;
font-size: 0.95rem !important;
}
button:hover, input[type="button"]:hover, input[type="submit"]:hover {
background: #007aff !important;
transform: scale(0.97) !important;
opacity: 0.9;
}
button.btn-toggle-active {
background: var(--accent) !important;
color: white !important;
border: none !important;
}
button.btn-toggle-inactive {
background: rgba(255,255,255,0.08) !important;
color: var(--text-main) !important;
border: 1px solid var(--border-subtle) !important;
}
/* Layout Tweaks for right/left floated containers */
/* Using structural selectors to override inline float styles */
div[style*="float:left"], div[style*="float:right"] {
float: none !important;
position: static !important;
right: auto !important;
left: auto !important;
top: auto !important;
width: 100% !important;
margin-bottom: 20px !important;
}
/* Clearfix removal */
br[style*="clear:both"] {
display: none !important;
}
/* Main layout grid - overriding inline styles for a modern CSS grid layout instead of floats */
.content > div[style*="width:550px"], .content > div[style*="width:300px"] {
/* Handled by script DOM modification, or we just let panels stack beautifully for now */
}
`;
function injectCSS(gradual) {
const styleSheet = document.createElement("style");
document.head.appendChild(styleSheet);
function showFinalBalloon() {
const balloon = document.createElement('div');
balloon.innerHTML = `
<div style="font-weight: 600; font-size: 1.1rem; margin-bottom: 4px;">Welcome to 2026.</div>
<div style="font-size: 0.95rem; color: #a0a0b0;">This is USACO. Good software deserves good presentation.</div>
`;
balloon.style.cssText = `
position: fixed;
bottom: 32px;
right: 32px;
background: rgba(28, 28, 30, 0.9);
backdrop-filter: blur(12px);
border: 1px solid rgba(255, 255, 255, 0.15);
color: white;
padding: 16px 20px;
border-radius: 12px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
z-index: 9999999;
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
transform: translateY(20px);
opacity: 0;
transition: all 0.5s cubic-bezier(0.16, 1, 0.3, 1);
`;
document.body.appendChild(balloon);
// Trigger animation
requestAnimationFrame(() => {
balloon.style.transform = 'translateY(0)';
balloon.style.opacity = '1';
});
// Fade out after 5 seconds
setTimeout(() => {
balloon.style.opacity = '0';
balloon.style.transform = 'translateY(10px)';
setTimeout(() => balloon.remove(), 500);
}, 5000);
}
function playTrashAnimation() {
const container = document.createElement('div');
container.style.cssText = `
position: fixed;
bottom: 120px;
left: 50%;
transform: translateX(-50%);
z-index: 9999999;
pointer-events: none;
display: flex;
flex-direction: column;
align-items: center;
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", Roboto, sans-serif;
`;
const logo = document.createElement('img');
logo.src = 'https://upload.wikimedia.org/wikipedia/commons/0/04/ChatGPT_logo.svg';
logo.style.cssText = `
width: 50px;
height: 50px;
background: white;
border-radius: 50%;
padding: 4px;
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
position: absolute;
bottom: 80px;
transition: all 0.8s cubic-bezier(0.5, -0.5, 0.5, 1.5);
`;
const trash = document.createElement('div');
trash.innerHTML = '🗑️';
trash.style.cssText = `
font-size: 64px;
position: absolute;
bottom: 0;
transition: transform 0.2s;
`;
const text = document.createElement('div');
text.innerText = "USACO disallows cheating";
text.style.cssText = `
position: absolute;
bottom: -30px;
color: #ff453a;
font-weight: bold;
font-size: 15px;
white-space: nowrap;
opacity: 0;
transition: opacity 0.5s;
text-shadow: 0 2px 4px rgba(0,0,0,0.5);
background: rgba(0,0,0,0.7);
padding: 4px 12px;
border-radius: 12px;
`;
container.appendChild(logo);
container.appendChild(trash);
container.appendChild(text);
document.body.appendChild(container);
// Animation sequence
setTimeout(() => {
// throw into trash
logo.style.transform = 'translateY(60px) scale(0) rotate(360deg)';
logo.style.opacity = '0';
}, 100);
setTimeout(() => {
text.style.opacity = '1';
trash.style.transform = 'scale(1.2)';
}, 800);
setTimeout(() => {
trash.style.transform = 'scale(1)';
}, 1000);
// Cleanup
setTimeout(() => {
container.style.transition = 'opacity 0.5s';
container.style.opacity = '0';
setTimeout(() => container.remove(), 500);
}, 4000);
}
if (gradual) {
// Create Loading Bar Element
const loadingBarContainer = document.createElement('div');
loadingBarContainer.style.cssText = `
position: fixed;
bottom: 32px;
left: 50%;
transform: translateX(-50%);
background: rgba(28, 28, 30, 0.9);
backdrop-filter: blur(12px);
border: 1px solid rgba(255, 255, 255, 0.15);
color: white;
padding: 16px 24px;
border-radius: 24px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
z-index: 9999999;
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
display: flex;
flex-direction: column;
gap: 12px;
width: 300px;
transition: opacity 0.5s ease;
`;
const loadingText = document.createElement('div');
loadingText.style.cssText = "font-size: 0.95rem; font-weight: 500; text-align: center;";
loadingText.innerText = "Updating Web Design to 1993...";
// Setup Easter Egg
const easterEggContainer = document.createElement('div');
easterEggContainer.style.cssText = `
position: fixed;
bottom: 150px;
left: 50%;
transform: translateX(-50%);
z-index: 9999998;
pointer-events: none;
display: flex;
flex-direction: column;
align-items: center;
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", Roboto, sans-serif;
opacity: 0;
transition: opacity 0.3s;
`;
const logo = document.createElement('img');
logo.src = 'https://upload.wikimedia.org/wikipedia/commons/0/04/ChatGPT_logo.svg';
logo.style.cssText = `
width: 50px;
height: 50px;
background: white;
border-radius: 50%;
padding: 4px;
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
position: absolute;
bottom: 80px;
transition: transform 0.8s cubic-bezier(0.5, -0.5, 0.5, 1.5), opacity 0.8s;
`;
const trash = document.createElement('div');
trash.innerHTML = '🗑️';
trash.style.cssText = `
font-size: 64px;
position: absolute;
bottom: 0;
transition: transform 0.2s;
`;
const easterText = document.createElement('div');
easterText.innerText = "USACO disallows cheating";
easterText.style.cssText = `
position: absolute;
bottom: -30px;
color: #ff453a;
font-weight: bold;
font-size: 15px;
white-space: nowrap;
opacity: 0;
transition: opacity 0.5s;
text-shadow: 0 2px 4px rgba(0,0,0,0.5);
background: rgba(0,0,0,0.7);
padding: 4px 12px;
border-radius: 12px;
`;
const progressBarTrack = document.createElement('div');
progressBarTrack.style.cssText = "width: 100%; height: 6px; background: rgba(255,255,255,0.1); border-radius: 3px; overflow: hidden;";
const progressBarFill = document.createElement('div');
progressBarFill.style.cssText = "height: 100%; width: 0%; background: #0a84ff; transition: width 0.1s linear;";
progressBarTrack.appendChild(progressBarFill);
loadingBarContainer.appendChild(loadingText);
loadingBarContainer.appendChild(progressBarTrack);
document.body.appendChild(loadingBarContainer);
easterEggContainer.appendChild(logo);
easterEggContainer.appendChild(trash);
easterEggContainer.appendChild(easterText);
// Render it invisibly in DOM to preload image
document.body.appendChild(easterEggContainer);
const lines = css.split('\n');
const totalLines = lines.length;
const delay = 6500 / Math.max(totalLines, 1);
let i = 0;
const startYear = 1993; // Using 1993 as requested
const endYear = 2026;
let easterState = 0;
const interval = setInterval(() => {
if (i >= lines.length) {
clearInterval(interval);
loadingBarContainer.style.opacity = '0';
setTimeout(() => loadingBarContainer.remove(), 500);
easterEggContainer.style.opacity = '0';
setTimeout(() => easterEggContainer.remove(), 500);
showFinalBalloon();
return;
}
styleSheet.textContent += lines[i] + '\n';
const progressPercentage = (i / totalLines) * 100;
const currentYear = Math.floor(startYear + ((endYear - startYear) * (i / totalLines)));
// Drive Easter Egg by year progression
if (currentYear === 2021 && easterState === 0) {
easterState = 1;
easterEggContainer.style.opacity = '1';
}
if (currentYear === 2022 && easterState === 1) {
easterState = 2;
logo.style.transform = 'translateY(60px) scale(0) rotate(360deg)';
logo.style.opacity = '0';
}
if (currentYear >= 2023 && easterState === 2) {
easterState = 3;
trash.style.transform = 'scale(1.2)';
easterText.style.opacity = '1';
setTimeout(() => trash.style.transform = 'scale(1)', 200);
}
progressBarFill.style.width = progressPercentage + '%';
loadingText.innerText = 'Updating Web Design to ' + currentYear + '...';
i++;
}, delay);
} else {
styleSheet.textContent = css;
}
}
// Additional DOM manipulation for layout
function modernizeDom() {
// Find the main split layout on landing page
const leftCol = document.querySelector('div[style*="width:550px"]');
const rightCol = document.querySelector('div[style*="width:300px"]');
if (leftCol && rightCol) {
// Create a grid container
const grid = document.createElement('div');
grid.style.display = 'grid';
grid.style.gridTemplateColumns = '2fr 1fr';
grid.style.gap = '20px';
grid.style.width = '100%';
// Insert grid
leftCol.parentNode.insertBefore(grid, leftCol);
grid.appendChild(leftCol);
grid.appendChild(rightCol);
// Strip out width, float, left, top from inline styles
leftCol.style.cssText = "";
rightCol.style.cssText = "";
// Fix sponsor wrapper
const sponsors = document.querySelector('.sponsors');
if (sponsors) {
sponsors.style.cssText = "";
// Move out of grid if it's there
grid.parentNode.insertBefore(sponsors, grid.nextSibling);
}
}
// Remove text alignments that break design
document.querySelectorAll('[align="center"], [align="left"], [align="right"]').forEach(el => el.removeAttribute('align'));
}
function initCommandPalette() {
const backdrop = document.createElement('div');
backdrop.id = 'usaco-cmd-palette-backdrop';
backdrop.innerHTML = `
<div id = "usaco-cmd-palette">
<div id="usaco-cmd-input-wrapper">
<svg style="width: 20px; height: 20px; color: var(--text-muted); margin-right: 12px;" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path></svg>
<input type="text" id="usaco-cmd-input" placeholder="Search USACO Pages or jump to links..." autocomplete="off" spellcheck="false"/>
</div>
<div id="usaco-cmd-results"></div>
</div >
`;
document.body.appendChild(backdrop);
const input = document.getElementById('usaco-cmd-input');
const resultsContainer = document.getElementById('usaco-cmd-results');
// Collect all links on page
const pageLinks = Array.from(document.querySelectorAll('a')).map(a => ({
title: a.innerText.trim() || a.href,
url: a.href,
type: 'Page Link'
})).filter(l => l.title.length > 0 && !l.url.startsWith('javascript:'));
// Define global hardcoded quick links
const globalLinks = [
{ title: "USACO Home", url: "https://usaco.org/", type: "Global" },
{ title: "Contests", url: "https://usaco.org/index.php?page=contests", type: "Global" },
{ title: "Training Pages", url: "https://train.usaco.org/", type: "Global" },
{ title: "Log Out", url: "https://usaco.org/index.php?page=logout", type: "Global" }
];
const allItems = [...globalLinks, ...pageLinks];
let selectedIndex = 0;
let currentResults = [];
function closePalette() {
backdrop.style.display = 'none';
input.value = '';
input.blur();
}
function renderResults(query = "") {
query = query.toLowerCase();
currentResults = allItems.filter(item =>
item.title.toLowerCase().includes(query) || item.url.toLowerCase().includes(query)
);
// Remove duplicates by URL roughly
const seen = new Set();
currentResults = currentResults.filter(item => {
const duplicate = seen.has(item.url);
seen.add(item.url);
return !duplicate;
}).slice(0, 10); // show top 10 max
if (currentResults.length === 0) {
resultsContainer.innerHTML = '<div style="padding: 16px; color: var(--text-muted); text-align: center;">No results found.</div>';
return;
}
resultsContainer.innerHTML = currentResults.map((item, index) => `
<a href="${item.url}" class="usaco-cmd-item ${index === selectedIndex ? 'selected' : ''}" data-index="${index}">
<span class="usaco-cmd-item-title">${item.title}</span>
<span class="usaco-cmd-item-shortcut">${item.type}</span>
</a>
`).join('');
// Add click listeners to items
document.querySelectorAll('.usaco-cmd-item').forEach(el => {
el.addEventListener('mouseenter', (e) => {
selectedIndex = parseInt(e.currentTarget.getAttribute('data-index'));
renderResults(input.value);
});
el.addEventListener('click', closePalette);
});
}
backdrop.addEventListener('click', (e) => {
if (e.target === backdrop) closePalette();
});
document.addEventListener('keydown', (e) => {
if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
e.preventDefault();
backdrop.style.display = backdrop.style.display === 'flex' ? 'none' : 'flex';
if (backdrop.style.display === 'flex') {
input.focus();
selectedIndex = 0;
renderResults();
}
}
if (backdrop.style.display === 'flex') {
if (e.key === 'Escape') closePalette();
if (e.key === 'ArrowDown') { e.preventDefault(); selectedIndex = Math.min(selectedIndex + 1, currentResults.length - 1); renderResults(input.value); }
if (e.key === 'ArrowUp') { e.preventDefault(); selectedIndex = Math.max(selectedIndex - 1, 0); renderResults(input.value); }
if (e.key === 'Enter' && currentResults[selectedIndex]) {
e.preventDefault();
window.location.href = currentResults[selectedIndex].url;
closePalette();
}
}
});
input.addEventListener('input', (e) => {
selectedIndex = 0;
renderResults(e.target.value);
});
}
function initMonacoEditor() {
const form = document.querySelector('form.submission');
if (!form) return;
const fileInput = form.querySelector('input[name="sourcefile"]');
const langSelect = form.querySelector('select[name="language"]');
if (!fileInput || !langSelect) return;
// Create editor container
const editorContainer = document.createElement('div');
editorContainer.id = "monaco-editor-container";
editorContainer.style.width = "100%";
editorContainer.style.height = "500px";
editorContainer.style.marginTop = "10px";
editorContainer.style.marginBottom = "20px";
editorContainer.style.borderRadius = "8px";
editorContainer.style.overflow = "hidden";
editorContainer.style.border = "1px solid var(--border-subtle)";
// Add tab toggle
const toggleWrapper = document.createElement('div');
toggleWrapper.innerHTML = `
< div style = "display: flex; gap: 10px; margin-top: 20px; margin-bottom: 10px;" >
<button type="button" id="btn-use-editor" class="btn-toggle-active">Use Editor</button>
<button type="button" id="btn-use-file" class="btn-toggle-inactive">Upload File</button>
</div >
`;
const fileInputParent = fileInput.parentNode;
fileInputParent.parentNode.insertBefore(toggleWrapper, fileInputParent);
// Hide file input initially
fileInputParent.style.display = 'none';
// Insert editor container
toggleWrapper.parentNode.insertBefore(editorContainer, toggleWrapper.nextSibling);
let editor = null;
let useEditor = true;
const btnEditor = document.getElementById('btn-use-editor');
const btnFile = document.getElementById('btn-use-file');
btnEditor.addEventListener('click', (e) => {
useEditor = true;
btnEditor.className = "btn-toggle-active";
btnFile.className = "btn-toggle-inactive";
editorContainer.style.display = 'block';
fileInputParent.style.display = 'none';
});
btnFile.addEventListener('click', (e) => {
useEditor = false;
btnFile.className = "btn-toggle-active";
btnEditor.className = "btn-toggle-inactive";
editorContainer.style.display = 'none';
fileInputParent.style.display = 'block';
});
// Load Monaco
const script = document.createElement('script');
script.src = 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.46.0/min/vs/loader.min.js';
script.onload = () => {
window.require.config({ paths: { 'vs': 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.46.0/min/vs' } });
window.require(['vs/editor/editor.main'], function () {
monaco.editor.defineTheme('usacoDark', {
base: 'vs-dark',
inherit: true,
rules: [{ background: '1c1c1e' }],
colors: {
'editor.background': '#1c1c1e',
'editor.lineHighlightBackground': '#2c2c2e',
}
});
// Add basic snippets/suggestions for common USACO languages since Monaco in browser
// doesn't have a backend Language Server to provide rich standard-library intellisense.
monaco.languages.registerCompletionItemProvider('cpp', {
provideCompletionItems: function (model, position) {
return {
suggestions: [
{
label: 'boilerplate',
kind: monaco.languages.CompletionItemKind.Snippet,
insertText: '#include <iostream>\n#include <vector>\n#include <algorithm>\nusing namespace std;\n\nint main() {\n ios_base::sync_with_stdio(false);\n cin.tie(NULL);\n $0\n return 0;\n}',
insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
documentation: 'USACO C++ Boilerplate'
},
{ label: 'cout', kind: monaco.languages.CompletionItemKind.Snippet, insertText: 'cout << $1 << "\\n";' },
{ label: 'cin', kind: monaco.languages.CompletionItemKind.Snippet, insertText: 'cin >> $1;' },
{ label: 'fori', kind: monaco.languages.CompletionItemKind.Snippet, insertText: 'for(int i = 0; i < $1; i++) {\n $2\n}' }
]
};
}
});
monaco.languages.registerCompletionItemProvider('java', {
provideCompletionItems: function (model, position) {
return {
suggestions: [
{
label: 'boilerplate',
kind: monaco.languages.CompletionItemKind.Snippet,
insertText: 'import java.util.*;\nimport java.io.*;\n\npublic class Solution {\n public static void main(String[] args) throws IOException {\n BufferedReader br = new BufferedReader(new InputStreamReader(System.in));\n $0\n }\n}',
insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
documentation: 'USACO Java Boilerplate'
},
{ label: 'sout', kind: monaco.languages.CompletionItemKind.Snippet, insertText: 'System.out.println($1);' }
]
};
}
});
monaco.languages.registerCompletionItemProvider('python', {
provideCompletionItems: function (model, position) {
return {
suggestions: [
{
label: 'boilerplate',
kind: monaco.languages.CompletionItemKind.Snippet,
insertText: 'import sys\n\ndef main():\n input = sys.stdin.read\n data = input().split()\n $0\n\nif __name__ == "__main__":\n main()',
insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
documentation: 'USACO Python Boilerplate'
}
]
};
}
});
function getMonacoLanguage(usacoLangStr) {
usacoLangStr = usacoLangStr.toLowerCase();
if (usacoLangStr.includes('c++')) return 'cpp';
if (usacoLangStr.includes('java')) return 'java';
if (usacoLangStr.includes('python')) return 'python';
if (usacoLangStr === 'c') return 'c';
return 'plaintext';
}
editor = monaco.editor.create(editorContainer, {
value: '// Select your language and type "boilerplate" to get started\\n',
language: getMonacoLanguage(langSelect.options[langSelect.selectedIndex].text),
theme: 'usacoDark',
automaticLayout: true,
fontSize: 14,
fontFamily: "var(--font-mono)",
minimap: { enabled: false },
scrollBeyondLastLine: false,
quickSuggestions: true,
suggestOnTriggerCharacters: true
});
langSelect.addEventListener('change', () => {
const newLang = getMonacoLanguage(langSelect.options[langSelect.selectedIndex].text);
monaco.editor.setModelLanguage(editor.getModel(), newLang);
});
});
};
document.head.appendChild(script);
// Intercept form submission
form.addEventListener('submit', (e) => {
if (useEditor && editor) {
const code = editor.getValue();
const langText = langSelect.options[langSelect.selectedIndex].text.toLowerCase();
let ext = ".txt";
if (langText.includes("c++")) ext = ".cpp";
else if (langText.includes("java")) ext = ".java";
else if (langText.includes("python")) ext = ".py";
else if (langText === "c") ext = ".c";
const file = new File([code], "solution" + ext, { type: "text/plain" });
// Polyfill for setting files using DataTransfer
const dt = new DataTransfer();
dt.items.add(file);
fileInput.files = dt.files;
}
});
}
function initWelcomePopup() {
if (localStorage.getItem('modernUsacoWelcomeShown')) {
injectCSS(false);
initAll();
return;
}
const backdrop = document.createElement('div');
backdrop.id = 'usaco-welcome-backdrop';
backdrop.innerHTML = `
<div id="usaco-welcome-modal">
<h2>Welcome to Modern USACO! 🎉</h2>
<p>It looks like this is your first time using the Modern USACO userscript. Here are a few key features to enhance your experience:</p>
<ul>
<li><strong>Apple-inspired UI:</strong> A clean, fast, dark-mode continuous document layout.</li>
<li><strong>CMD+K / CTRL+K:</strong> A global command palette to quickly search and navigate to any page.</li>
<li><strong>Monaco Editor:</strong> A built-in code editor on problem pages with competitive programming boilerplate snippets.</li>
</ul>
<p style="font-size: 0.9rem; color: #8e8e93; background: rgba(255,255,255,0.05); padding: 12px; border-radius: 8px;">
🛡️ <strong>Privacy & Security:</strong> This script operates entirely locally on your machine. It does not collect data, has absolutely no telemetry, and will never compromise your setup or identity. It is completely open-source!
</p>
<div style="text-align: right !important; margin-top: 24px !important; display: block !important; width: 100% !important;">
<button id="btn-welcome-dismiss">Got it, let's go!</button>
</div>
</div>
`;
document.body.appendChild(backdrop);
document.getElementById('btn-welcome-dismiss').addEventListener('click', () => {
backdrop.style.opacity = '0';
setTimeout(() => backdrop.remove(), 300);
localStorage.setItem('modernUsacoWelcomeShown', 'true');
injectCSS(true);
initAll();
});
}
// Run DOM manipulation when document is ready
const initAll = () => { modernizeDom(); initCommandPalette(); initMonacoEditor(); };
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initWelcomePopup);
} else {
initWelcomePopup();
}
})();