diff --git a/apps/web/public/logo-dark.svg b/apps/web/public/logo-dark.svg new file mode 100644 index 0000000..e4b4966 --- /dev/null +++ b/apps/web/public/logo-dark.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/apps/web/src/CodeEditor.tsx b/apps/web/src/CodeEditor.tsx index 2cca728..a9dcfbb 100644 --- a/apps/web/src/CodeEditor.tsx +++ b/apps/web/src/CodeEditor.tsx @@ -6,4 +6,26 @@ import "monaco-editor/languages/definitions/java/register.js"; import EditorWorker from "monaco-editor/editor/editor.worker.js?worker"; window.MonacoEnvironment = { getWorker: () => new EditorWorker() }; loader.config({ monaco }); +monaco.editor.defineTheme("algostep-light", { + base: "vs", + inherit: true, + rules: [], + colors: { + "editor.background": "#f6f7fb", + "editor.foreground": "#18213a", + "editor.lineHighlightBackground": "#eaf0ff", + "editorLineNumber.foreground": "#63708a", + }, +}); +monaco.editor.defineTheme("algostep-dark", { + base: "vs-dark", + inherit: true, + rules: [], + colors: { + "editor.background": "#222d43", + "editor.foreground": "#edf1fa", + "editor.lineHighlightBackground": "#22365f", + "editorLineNumber.foreground": "#a8b5cf", + }, +}); export default Editor; diff --git a/apps/web/src/main.tsx b/apps/web/src/main.tsx index c7feaef..eab1975 100644 --- a/apps/web/src/main.tsx +++ b/apps/web/src/main.tsx @@ -17,7 +17,6 @@ import { createRoot } from "react-dom/client"; import { BookOpen, Code2, - History, ArrowRight, Sun, Moon, @@ -25,7 +24,6 @@ import { AlertCircle, Play, FileDown, - GitBranch, Search, Check, ArrowLeft, @@ -514,7 +512,12 @@ function App() { const header = (
{user && ( @@ -527,7 +530,6 @@ function App() { } onClick={() => void navigate("home")} > - 학습 홈 {!mobile && ( @@ -540,14 +542,13 @@ function App() { } onClick={() => void navigate("problems")} > - 문제 풀기 )} @@ -578,6 +579,9 @@ function App() { }} > {theme === "dark" ? : } + + 테마 · {theme === "dark" ? "다크" : "라이트"} + {user && ( - -
- 작은 단계가 만드는 이해 -
- {[2, 5, 8, 13, 21].map((v, i) => ( -
- {i} - {v} -
- ))} +

오늘은 어떤 원리를 알아볼까요?

+
+ 추천 학습 +

이진 탐색

+

범위를 절반씩 줄이는 이유

+
-

- 관찰하고 → 예측하고 → 확인하세요 -

-
- + + ) : ( +
+
+ LEARN BY SEEING +

+ 코드를 따라가면, +
+ 원리가 보입니다. +

+

+ 한 줄의 실행이 어떻게 답을 만드는지 살펴보세요. +
+ 직접 작성하고, 움직임을 확인하고, 이해를 쌓아가세요. +

+ +
+
+ BINARY SEARCH +

절반을 지울 때마다, 더 가까이

+
+ {[2, 5, 8, 13, 21].map((v, i) => ( +
+ {v} +
+ ))} +
+ mid = 2  ·  target = 13 +
+
+ )}
LEARNING PATH @@ -920,16 +950,7 @@ function App() { openLesson(t); }} > -
- - {t.category === "그래프" ? ( - - ) : ( - - )} - - {group.name} -
+ {group.name}

{t.title}

{t.body.split(". ")[0]}.

@@ -1174,6 +1195,23 @@ function App() {
+
+

문제

+

{problem.statement}

+
+ 입출력 조건과 공개 테스트 +

{problem.input_spec}

+

{problem.output_spec}

+ {problem.constraints_text} + {problem.tests.map((t: any, i: number) => ( +
+ 테스트 {i + 1} +
{t.input}
+ 예상 출력: {JSON.stringify(t.expected)} +
+ ))} +
+
@@ -1200,15 +1238,25 @@ function App() { {saveStatus}
- 코드 편집기를 준비하고 있어요.

}> + + 코드 편집기를 준비하고 있어요. +

+ } + > editCode(s ?? "")} - theme={theme === "dark" ? "vs-dark" : "light"} + theme={ + theme === "dark" ? "algostep-dark" : "algostep-light" + } options={{ fontSize: 14, + lineHeight: 24, + fontFamily: "JetBrains Mono, monospace", minimap: { enabled: false }, scrollBeyondLastLine: false, automaticLayout: true, @@ -1261,43 +1309,8 @@ function App() {
-
-

문제

-

{problem.statement}

-
- 입출력 조건과 공개 테스트 -

{problem.input_spec}

-

{problem.output_spec}

- {problem.constraints_text} - {problem.tests.map((t: any, i: number) => ( -
- 테스트 {i + 1} -
{t.input}
- 예상 출력: {JSON.stringify(t.expected)} -
- ))} -
-
-
- {[ - ["visual", "시각화"], - ["tests", "테스트 결과"], - ["review", "코드 리뷰"], - ].map(([k, v]) => ( - - ))} -
- {tab !== "review" && ( + { <>
@@ -1353,8 +1366,8 @@ function App() { )} - )} - {tab === "visual" && ( + } + { <> )} - )} - {tab === "tests" && - (selectedTest ? ( - <> -
-
- 실행 시간 - {selectedTest.elapsedMs} ms -
-
- 최대 메모리 - - {( - selectedTest.peakMemoryBytes / - 1024 / - 1024 - ).toFixed(2)}{" "} - MiB - -
-
-

- 엄격 비교 · 아래 표기는 공백과 줄바꿈을 포함해요. -

- {[ - ["입력", selectedTest.input], - [ - "예상 출력", - selectedTest.expected === null - ? "사용자 입력 실행 — 채점 없음" - : JSON.stringify(selectedTest.expected), - ], - ["실제 출력", JSON.stringify(selectedTest.actual)], - ["실행 오류", selectedTest.stderr || "없음"], - ].map(([k, v]) => ( -
-

{k}

-
{v}
-
- ))} - - ) : ( -

- 코드를 실행하면 테스트 결과가 여기에 표시돼요. -

- ))} - {tab === "review" && ( - <> -
-

코드 리뷰

- - 무료{" "} - {Math.max( - 0, - 3 - (usage?.consumed ?? 0) - (usage?.reserved ?? 0), + } +
+
+
+
+
+ {[ + ["tests", "테스트 결과"], + ["review", "코드 리뷰"], + ].map(([k, v]) => ( + + ))} +
+ {tab !== "review" && + (selectedTest ? ( + <> +
+
+ 실행 시간 + {selectedTest.elapsedMs} ms +
+
+ 최대 메모리 + + {(selectedTest.peakMemoryBytes / 1024 / 1024).toFixed( + 2, )}{" "} - / 3건 남음 - + MiB +
-

- 논리 오류 · 효율 개선 · 대안 코드를 확인해요. -
- 작성 코드, 권장 코드, 문제를 LLM에 전달합니다. -

- - {!config?.reviewEnabled && ( -

- 리뷰 서비스 연결이 준비 중이에요. 이용량은 차감되지 - 않아요. +

+

+ 엄격 비교 · 아래 표기는 공백과 줄바꿈을 포함해요. +

+ {[ + ["입력", selectedTest.input], + [ + "예상 출력", + selectedTest.expected === null + ? "사용자 입력 실행 — 채점 없음" + : JSON.stringify(selectedTest.expected), + ], + ["실제 출력", JSON.stringify(selectedTest.actual)], + ["실행 오류", selectedTest.stderr || "없음"], + ].map(([k, v]) => ( +
+

{k}

+
{v}
+
+ ))} + + ) : ( +

+ 코드를 실행하면 테스트 결과가 여기에 표시돼요. +

+ ))} + {tab === "review" && ( + <> +
+

코드 리뷰

+ + 무료{" "} + {Math.max( + 0, + 3 - (usage?.consumed ?? 0) - (usage?.reserved ?? 0), + )}{" "} + / 3건 남음 + +
+

+ 논리 오류 · 효율 개선 · 대안 코드를 확인해요. +
+ 작성 코드, 권장 코드, 문제를 LLM에 전달합니다. +

+ + {!config?.reviewEnabled && ( +

+ 리뷰 서비스 연결이 준비 중이에요. 이용량은 차감되지 + 않아요. +

+ )} +

+ KST 매월 1일 초기화 · 이월 없음 · 추가 구매는 가격 확정 후 + 제공 +

+ {record.reviews?.map((r: any) => ( +
+ {r.status} + + {new Date(r.created_at).toLocaleString("ko-KR")} + + {r.status === "FAILED" && ( +

+ 리뷰를 완료하지 못했어요. 이용량은 차감되지 않았어요.

)} -

- KST 매월 1일 초기화 · 이월 없음 · 추가 구매는 가격 확정 - 후 제공 -

- {record.reviews?.map((r: any) => ( -
- {r.status} - - {new Date(r.created_at).toLocaleString("ko-KR")} - - {r.status === "FAILED" && ( -

- 리뷰를 완료하지 못했어요. 이용량은 차감되지 - 않았어요. -

- )} - {r.result && ( - <> -

논리 오류

-

- {r.result.logicalErrors} -

-

효율 개선

-

- {r.result.efficiencyImprovements} -

-

대안 코드

-
{r.result.alternativeCode}
- - )} -
- - 리뷰 당시 코드 · {labels[r.language]} - -
{r.source}
-
-
- ))} - - )} -
-
- + {r.result && ( + <> +

논리 오류

+

{r.result.logicalErrors}

+

효율 개선

+

+ {r.result.efficiencyImprovements} +

+

대안 코드

+
{r.result.alternativeCode}
+ + )} +
+ 리뷰 당시 코드 · {labels[r.language]} +
{r.source}
+
+ + ))} + + )} + )} {actualPage === "history" && ( @@ -1580,6 +1608,34 @@ function App() { )} + {mobile && ( + + )}