From 820e0befac7c4610da1b2a8fb5f9f7dcf859796c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 13 May 2026 12:04:22 +0000 Subject: [PATCH 1/3] feat: improve codeforces problem workspace editor UX Agent-Logs-Url: https://github.com/enlorik/HDD/sessions/1751b72d-3bc3-45a7-920b-f66bb596fbc5 Co-authored-by: enlorik <99548776+enlorik@users.noreply.github.com> --- src/components/ProblemWorkspace.css | 58 +++++++++++-- src/components/ProblemWorkspace.jsx | 121 +++++++++++++++++++++------- 2 files changed, 145 insertions(+), 34 deletions(-) diff --git a/src/components/ProblemWorkspace.css b/src/components/ProblemWorkspace.css index cbeb10a..a2f7ff9 100644 --- a/src/components/ProblemWorkspace.css +++ b/src/components/ProblemWorkspace.css @@ -98,6 +98,12 @@ color: var(--ik-muted); } +.problem-workspace-link--secondary { + display: inline-block; + margin-top: 0.95rem; + opacity: 0.9; +} + .problem-workspace-link:hover { color: var(--ik-cf); } @@ -110,32 +116,74 @@ margin-bottom: 0.9rem; } +.problem-workspace-editor-heading { + display: flex; + align-items: center; + gap: 0.55rem; + min-width: 0; +} + .problem-workspace-header h2, .problem-workspace-panel--results h2 { font-size: 1rem; } +.problem-workspace-language-badge { + font-size: 0.72rem; + color: var(--ik-cf); + background: rgba(74, 158, 255, 0.14); + border: 1px solid rgba(74, 158, 255, 0.25); + border-radius: 999px; + padding: 0.1rem 0.42rem; + font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace; +} + +.problem-workspace-editor-problem-id { + color: var(--ik-muted); + font-size: 0.78rem; + font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace; +} + .problem-workspace-actions { display: flex; + align-items: center; gap: 0.6rem; flex-wrap: wrap; } -.problem-workspace-actions button:disabled { - opacity: 0.55; - cursor: not-allowed; +.problem-workspace-save-status { + font-size: 0.76rem; + color: var(--ik-muted); +} + +.problem-workspace-reset-btn { + border: 1px solid var(--ik-border); + background: var(--ik-surface); + color: var(--ik-text); + border-radius: var(--ik-radius-sm); + font-size: 0.78rem; + font-weight: 600; + padding: 0.35rem 0.68rem; + cursor: pointer; +} + +.problem-workspace-reset-btn:hover { + border-color: var(--ik-cf); + color: var(--ik-cf); } .problem-workspace-editor { width: 100%; - min-height: 430px; + min-height: 470px; resize: vertical; border: 1px solid var(--ik-border); border-radius: var(--ik-radius-sm); - background: #111111; + background: #101216; color: var(--ik-text); padding: 1rem; font: 0.95rem/1.5 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace; + tab-size: 4; + caret-color: #8fb6ff; } .problem-workspace-editor::placeholder { diff --git a/src/components/ProblemWorkspace.jsx b/src/components/ProblemWorkspace.jsx index c875554..2b69514 100644 --- a/src/components/ProblemWorkspace.jsx +++ b/src/components/ProblemWorkspace.jsx @@ -7,13 +7,16 @@ import { } from '../utils/problemWorkspace'; import './ProblemWorkspace.css'; -const DEFAULT_PLACEHOLDER = 'fun main() {\n\n}'; +const DEFAULT_KOTLIN_STARTER = `fun main() { + // solve here +}`; function loadDraft(storageKey) { try { - return localStorage.getItem(storageKey) || ''; + const saved = localStorage.getItem(storageKey); + return saved === null ? DEFAULT_KOTLIN_STARTER : saved; } catch { - return ''; + return DEFAULT_KOTLIN_STARTER; } } @@ -38,6 +41,7 @@ function ProblemWorkspace() { ); const [code, setCode] = useState(() => loadDraft(storageKey)); + const [hasEdited, setHasEdited] = useState(false); // Statement fetch state const [stmtStatus, setStmtStatus] = useState('idle'); // 'idle' | 'loading' | 'ok' | 'error' @@ -47,27 +51,34 @@ function ProblemWorkspace() { useEffect(() => { if (!contestId || !index) return; let cancelled = false; - setStmtStatus('loading'); - setStatement(null); - setStmtError(''); - fetchProblemStatement(contestId, index) - .then(data => { - if (!cancelled) { - setStatement(data); - setStmtStatus('ok'); - } - }) - .catch(err => { - if (!cancelled) { - setStmtError(err.message || 'Failed to load statement.'); - setStmtStatus('error'); - } - }); + queueMicrotask(() => { + if (cancelled) return; + setStmtStatus('loading'); + setStatement(null); + setStmtError(''); + fetchProblemStatement(contestId, index) + .then(data => { + if (!cancelled) { + setStatement(data); + setStmtStatus('ok'); + } + }) + .catch(err => { + if (!cancelled) { + setStmtError(err.message || 'Failed to load statement.'); + setStmtStatus('error'); + } + }); + }); return () => { cancelled = true; }; }, [contestId, index]); useEffect(() => { - setCode(loadDraft(storageKey)); + const nextDraft = loadDraft(storageKey); + queueMicrotask(() => { + setCode(nextDraft); + setHasEdited(false); + }); }, [storageKey]); useEffect(() => { @@ -78,6 +89,45 @@ function ProblemWorkspace() { } }, [code, storageKey]); + const handleEditorChange = event => { + setCode(event.target.value); + setHasEdited(true); + }; + + const handleEditorKeyDown = event => { + if (event.key !== 'Tab') { + return; + } + + event.preventDefault(); + const editor = event.currentTarget; + const start = editor.selectionStart; + const end = editor.selectionEnd; + const spaces = ' '; + const nextCode = `${code.slice(0, start)}${spaces}${code.slice(end)}`; + + setCode(nextCode); + setHasEdited(true); + + requestAnimationFrame(() => { + editor.selectionStart = start + spaces.length; + editor.selectionEnd = start + spaces.length; + }); + }; + + const handleResetDraft = () => { + const shouldReset = window.confirm( + 'Reset this local draft to the Kotlin starter code?', + ); + + if (!shouldReset) { + return; + } + + setCode(DEFAULT_KOTLIN_STARTER); + setHasEdited(true); + }; + const problemLink = `https://codeforces.com/problemset/problem/${contestId}/${index}`; const displayTitle = @@ -208,7 +258,7 @@ function ProblemWorkspace() { href={problemLink} target="_blank" rel="noopener noreferrer" - className="problem-workspace-link" + className="problem-workspace-link problem-workspace-link--secondary" > Open original Codeforces page ↗ @@ -222,13 +272,25 @@ function ProblemWorkspace() {
-

Kotlin

+
+

Kotlin

+ .kt + + {contestId} {index} + +
- -
@@ -236,8 +298,9 @@ function ProblemWorkspace() {