From 458d5534d48ce468c868c447c7861d4aeac82b48 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Sat, 7 Feb 2026 21:31:23 +0100 Subject: [PATCH 01/14] Views in live runs page --- src/app/runs/components/LiveRunRow.tsx | 14 ++------------ src/app/runs/page.tsx | 20 ++------------------ src/modules/live-run/actions.ts | 5 +++++ src/modules/live-run/types.ts | 1 + 4 files changed, 10 insertions(+), 30 deletions(-) diff --git a/src/app/runs/components/LiveRunRow.tsx b/src/app/runs/components/LiveRunRow.tsx index 6790689..716a8d4 100644 --- a/src/app/runs/components/LiveRunRow.tsx +++ b/src/app/runs/components/LiveRunRow.tsx @@ -16,7 +16,7 @@ export default function LiveRunRow({ run: LiveRun; }) { const now = new Date().getTime() + 1000; - const isTop = false; + const isTop = run.views > 5; return ( - - - 0 - - + {isTop ? {run.views} : run.views}
diff --git a/src/app/runs/page.tsx b/src/app/runs/page.tsx index 6af1023..a0038f1 100644 --- a/src/app/runs/page.tsx +++ b/src/app/runs/page.tsx @@ -4,7 +4,6 @@ import { Zap, Users, Timer, TrendingUp, Eye } from "lucide-react"; import { Card, CardHeader, CardTitle } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; -import { Button } from "@/components/ui/button"; import { Table, TableBody, @@ -13,9 +12,7 @@ import { TableRow, TableCell, } from "@/components/ui/table"; -import { cn } from "@/lib/utils"; import LiveRunRow from "./components/LiveRunRow"; -import StatCard from "@/components/app/StatCard"; import useAction from "@/hook/use-action"; import { getLiveRuns } from "@/modules/live-run/actions"; import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"; @@ -33,23 +30,10 @@ export default function LiveRuns() {

Live Runs

- Watch runners compete in real time. Every second counts. + 42 live runs · Streaming in real time

- {/* Stats */} -
- - - - -
- {/* Filters */}
@@ -70,7 +54,7 @@ export default function LiveRuns() { Runner Problem Time - Pace + Views Status diff --git a/src/modules/live-run/actions.ts b/src/modules/live-run/actions.ts index 9b4ebd2..7530f2b 100644 --- a/src/modules/live-run/actions.ts +++ b/src/modules/live-run/actions.ts @@ -16,12 +16,14 @@ export async function getLiveRuns(): Promise { username: "speedmaster_x", problem: "Two Sum Dash", category: "any%", + views: 670, runsCount: 1, start: new Date().getTime(), }, { id: "2", username: "algo_ninja", + views: 3, problem: "Binary Sprint", category: "any%", runsCount: 1, @@ -30,6 +32,7 @@ export async function getLiveRuns(): Promise { { id: "3", username: "stackattack", + views: 3, problem: "String Scramble", category: "any%", runsCount: 1, @@ -38,6 +41,7 @@ export async function getLiveRuns(): Promise { { id: "4", username: "reset_king", + views: 3, problem: "Linked List Rush", category: "any%", runsCount: 1, @@ -51,6 +55,7 @@ export async function getLiveRun(): Promise { id: "0", username: "selimaj", start: new Date().getTime(), + views: 230, problem: "solve", category: "any%", runsCount: 0, diff --git a/src/modules/live-run/types.ts b/src/modules/live-run/types.ts index 97154f4..f4073a4 100644 --- a/src/modules/live-run/types.ts +++ b/src/modules/live-run/types.ts @@ -1,6 +1,7 @@ export interface LiveRun { id: string; username: string; + views: number; start: number; runsCount: number; problem: string; From e24a6b8c0bd5f8b9705c3bd1d2b0eb1ddd96c775 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Sat, 7 Feb 2026 21:36:01 +0100 Subject: [PATCH 02/14] Views in live run page --- src/app/run/components/RunTimer.tsx | 17 +++++++++++++---- src/app/run/page.tsx | 2 +- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/app/run/components/RunTimer.tsx b/src/app/run/components/RunTimer.tsx index 4f0748d..da520b8 100644 --- a/src/app/run/components/RunTimer.tsx +++ b/src/app/run/components/RunTimer.tsx @@ -1,28 +1,37 @@ import { Card, CardContent } from "@/components/ui/card"; import formatTime from "@/lib/time-format"; import { LiveRun } from "@/modules/live-run/types"; +import { Eye } from "lucide-react"; import { useEffect, useState } from "react"; -export default function RunTimer({ runInfo }: { runInfo: LiveRun }) { +export default function RunTimer({ run }: { run: LiveRun }) { const [time, setTime] = useState("00:00:00"); useEffect(() => { // Update function calculates elapsed time - const updateTime = () => setTime(formatTime(Date.now() - runInfo.start)); + const updateTime = () => setTime(formatTime(Date.now() - run.start)); // Update immediately and then every second updateTime(); const interval = setInterval(updateTime, 50); return () => clearInterval(interval); // cleanup on unmount - }, [runInfo.start]); + }, [run.start]); return (

{time}

-

{runInfo.category}

+ +

+ {run.category} +

+

+ + {run.views} +

+
); diff --git a/src/app/run/page.tsx b/src/app/run/page.tsx index 32312af..e56ed70 100644 --- a/src/app/run/page.tsx +++ b/src/app/run/page.tsx @@ -40,7 +40,7 @@ export default function Run() {
- {runInfo && } + {runInfo && }
From 06aabf7f8c5f3f4b6ebea6e016a60ec9f4c1dd40 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Sat, 7 Feb 2026 21:50:27 +0100 Subject: [PATCH 03/14] Live run updates --- src/app/run/page.tsx | 4 ++-- src/hook/use-action.ts | 26 ++++++++++++++++++++++++++ src/modules/live-run/actions.ts | 2 +- 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/app/run/page.tsx b/src/app/run/page.tsx index e56ed70..8daf170 100644 --- a/src/app/run/page.tsx +++ b/src/app/run/page.tsx @@ -6,13 +6,13 @@ import RunChat, { ChatMessage } from "./components/RunChat"; import { FileTree } from "./components/FileTree"; import { File } from "lucide-react"; import Editor from "./components/Editor"; -import useAction from "@/hook/use-action"; +import useAction, { useActionInterval } from "@/hook/use-action"; import { getLiveRun } from "@/modules/live-run/actions"; import ProblemCard from "../problems/components/ProblemCard"; import ProfileCard from "./components/ProfileCard"; export default function Run() { - const [runInfo] = useAction(getLiveRun); + const [runInfo] = useActionInterval(getLiveRun, 1000); const chatMessages: ChatMessage[] = [ { user: "Alice", message: "Nice split!", time: "1:53" }, diff --git a/src/hook/use-action.ts b/src/hook/use-action.ts index 0f7468a..4df2ca0 100644 --- a/src/hook/use-action.ts +++ b/src/hook/use-action.ts @@ -17,3 +17,29 @@ export default function useAction( return [value, loaded, error]; } + +export function useActionInterval( + action: () => Promise, + interval: number, + dependencies = [], +): [T | null, boolean, Error | null] { + const [value, setValue] = useState(null); + const [loaded, setLoaded] = useState(false); + const [error, setError] = useState(null); + + useEffect(() => { + const f = () => + action() + .then(setValue) + .catch(setError) + .finally(() => setLoaded(true)); + + f(); + + const inv = setInterval(f, interval); + + return () => clearInterval(inv); + }, dependencies); + + return [value, loaded, error]; +} diff --git a/src/modules/live-run/actions.ts b/src/modules/live-run/actions.ts index 7530f2b..c25967c 100644 --- a/src/modules/live-run/actions.ts +++ b/src/modules/live-run/actions.ts @@ -54,7 +54,7 @@ export async function getLiveRun(): Promise { return { id: "0", username: "selimaj", - start: new Date().getTime(), + start: 1770497350654, views: 230, problem: "solve", category: "any%", From a98a481aa435b6c853d7eb6555bcb444389145cf Mon Sep 17 00:00:00 2001 From: Leo dev Date: Sat, 7 Feb 2026 22:06:08 +0100 Subject: [PATCH 04/14] Problem refrencing --- src/app/run/page.tsx | 23 ++++++++++------------- src/hook/use-action.ts | 4 ++-- src/modules/live-run/actions.ts | 2 +- src/modules/problems/actions.ts | 4 ++++ src/modules/problems/core.ts | 18 ++++++++++++++++-- 5 files changed, 33 insertions(+), 18 deletions(-) diff --git a/src/app/run/page.tsx b/src/app/run/page.tsx index 8daf170..a464677 100644 --- a/src/app/run/page.tsx +++ b/src/app/run/page.tsx @@ -10,9 +10,16 @@ import useAction, { useActionInterval } from "@/hook/use-action"; import { getLiveRun } from "@/modules/live-run/actions"; import ProblemCard from "../problems/components/ProblemCard"; import ProfileCard from "./components/ProfileCard"; +import { getProblem } from "@/modules/problems/actions"; export default function Run() { - const [runInfo] = useActionInterval(getLiveRun, 1000); + const [run] = useActionInterval(getLiveRun, 1000); + const [problem] = useAction( + async () => (run?.id ? getProblem(run.problem) : null), + [run], + ); + + console.log(problem); const chatMessages: ChatMessage[] = [ { user: "Alice", message: "Nice split!", time: "1:53" }, @@ -40,21 +47,11 @@ export default function Run() {
- {runInfo && } + {run && }
- + {problem && }
diff --git a/src/hook/use-action.ts b/src/hook/use-action.ts index 4df2ca0..1af384a 100644 --- a/src/hook/use-action.ts +++ b/src/hook/use-action.ts @@ -2,7 +2,7 @@ import { useEffect, useState } from "react"; export default function useAction( action: () => Promise, - dependencies = [], + dependencies: any[] = [], ): [T | null, boolean, Error | null] { const [value, setValue] = useState(null); const [loaded, setLoaded] = useState(false); @@ -21,7 +21,7 @@ export default function useAction( export function useActionInterval( action: () => Promise, interval: number, - dependencies = [], + dependencies: any[] = [], ): [T | null, boolean, Error | null] { const [value, setValue] = useState(null); const [loaded, setLoaded] = useState(false); diff --git a/src/modules/live-run/actions.ts b/src/modules/live-run/actions.ts index c25967c..b1055ec 100644 --- a/src/modules/live-run/actions.ts +++ b/src/modules/live-run/actions.ts @@ -56,7 +56,7 @@ export async function getLiveRun(): Promise { username: "selimaj", start: 1770497350654, views: 230, - problem: "solve", + problem: "fix-auth-callback", category: "any%", runsCount: 0, }; diff --git a/src/modules/problems/actions.ts b/src/modules/problems/actions.ts index a549c80..22eb133 100644 --- a/src/modules/problems/actions.ts +++ b/src/modules/problems/actions.ts @@ -6,3 +6,7 @@ import { Problem } from "./types"; export async function getProblems(): Promise { return await Core.getProblems(); } + +export async function getProblem(id: string): Promise { + return await Core.getProblem(id); +} diff --git a/src/modules/problems/core.ts b/src/modules/problems/core.ts index 0189644..707a28f 100644 --- a/src/modules/problems/core.ts +++ b/src/modules/problems/core.ts @@ -1,7 +1,7 @@ "use server"; import "server-only"; -import { ProblemDefinition } from "./types"; +import { Problem, ProblemDefinition } from "./types"; const problems: ProblemDefinition[] = [ { @@ -35,7 +35,7 @@ const problems: ProblemDefinition[] = [ }, ]; -export async function getProblems() { +export async function getProblems(): Promise { return problems.map((def) => ({ id: def.id, title: def.title, @@ -44,3 +44,17 @@ export async function getProblems() { language: def.language, })); } + +export async function getProblem(id: string): Promise { + const problem = problems.find((p) => p.id === id); + + if (!problem) return; + + return { + id: problem.id, + title: problem.title, + description: problem.description, + difficulty: problem.difficulty, + language: problem.language, + }; +} From 2f55f3b3fb2069fedd526f56b8fd6f1e3fcd264d Mon Sep 17 00:00:00 2001 From: Leo dev Date: Sat, 7 Feb 2026 22:15:41 +0100 Subject: [PATCH 05/14] improved live run row --- src/app/runs/components/LiveRunRow.tsx | 40 ++++++-------------------- src/app/runs/page.tsx | 15 ++++------ 2 files changed, 13 insertions(+), 42 deletions(-) diff --git a/src/app/runs/components/LiveRunRow.tsx b/src/app/runs/components/LiveRunRow.tsx index 716a8d4..03b8a3b 100644 --- a/src/app/runs/components/LiveRunRow.tsx +++ b/src/app/runs/components/LiveRunRow.tsx @@ -1,8 +1,6 @@ "use client"; -import { Eye } from "lucide-react"; import { Badge } from "@/components/ui/badge"; -import { Button } from "@/components/ui/button"; import { TableRow, TableCell } from "@/components/ui/table"; import { cn } from "@/lib/utils"; import { LiveRun } from "@/modules/live-run/types"; @@ -29,48 +27,26 @@ export default function LiveRunRow({ animationFillMode: "forwards", }} > - + {/* Runner */} + {isTop ? `🔥 ${run.username}` : run.username} - -
- {run.problem} - - {run.category} - -
-
+ {/* Problem + Category */} + {run.problem} + {/* Runtime */} {formatTime(now - run.start, false)} + {/* Views */} {isTop ? {run.views} : run.views} - -
- - Live -
-
- - - - + {/* Live status with category color */} + {run.category} ); } diff --git a/src/app/runs/page.tsx b/src/app/runs/page.tsx index a0038f1..3e9a71d 100644 --- a/src/app/runs/page.tsx +++ b/src/app/runs/page.tsx @@ -1,16 +1,12 @@ "use client"; -import { Zap, Users, Timer, TrendingUp, Eye } from "lucide-react"; - -import { Card, CardHeader, CardTitle } from "@/components/ui/card"; -import { Badge } from "@/components/ui/badge"; +import { Card } from "@/components/ui/card"; import { Table, TableBody, TableHead, TableHeader, TableRow, - TableCell, } from "@/components/ui/table"; import LiveRunRow from "./components/LiveRunRow"; import useAction from "@/hook/use-action"; @@ -18,7 +14,7 @@ import { getLiveRuns } from "@/modules/live-run/actions"; import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"; export default function LiveRuns() { - const [liveRuns, liveRunsLoaded, liveRunsError] = useAction(getLiveRuns); + const [liveRuns] = useAction(getLiveRuns); // Sort top runners (status === 'pb') to the top const sortedRuns = [...(liveRuns || [])]; @@ -53,10 +49,9 @@ export default function LiveRuns() { Runner Problem - Time - Views - Status - + Time + Views + Category From bfd2ce571957bf523668a0bdd417d00b4ade3519 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Sat, 7 Feb 2026 22:22:41 +0100 Subject: [PATCH 06/14] problem refrencing in live runs --- src/app/runs/components/LiveRunRow.tsx | 16 +++++++++++++++- src/modules/live-run/actions.ts | 8 ++++---- src/modules/problems/core.ts | 4 ++-- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/app/runs/components/LiveRunRow.tsx b/src/app/runs/components/LiveRunRow.tsx index 03b8a3b..5655586 100644 --- a/src/app/runs/components/LiveRunRow.tsx +++ b/src/app/runs/components/LiveRunRow.tsx @@ -5,6 +5,10 @@ import { TableRow, TableCell } from "@/components/ui/table"; import { cn } from "@/lib/utils"; import { LiveRun } from "@/modules/live-run/types"; import formatTime from "@/lib/time-format"; +import useAction from "@/hook/use-action"; +import { getProblem } from "@/modules/problems/actions"; +import Link from "next/link"; +import { LinkIcon } from "lucide-react"; export default function LiveRunRow({ index, @@ -13,6 +17,8 @@ export default function LiveRunRow({ index: number; run: LiveRun; }) { + const [problem] = useAction(() => getProblem(run.problem)); + const now = new Date().getTime() + 1000; const isTop = run.views > 5; @@ -33,7 +39,15 @@ export default function LiveRunRow({
{/* Problem + Category */} - {run.problem} + + + + {problem?.title || run.problem} + + {/* Runtime */} { { id: "1", username: "speedmaster_x", - problem: "Two Sum Dash", + problem: "fix-auth-callback", category: "any%", views: 670, runsCount: 1, @@ -24,7 +24,7 @@ export async function getLiveRuns(): Promise { id: "2", username: "algo_ninja", views: 3, - problem: "Binary Sprint", + problem: "fix-prod-build", category: "any%", runsCount: 1, start: new Date().getTime(), @@ -33,14 +33,14 @@ export async function getLiveRuns(): Promise { id: "3", username: "stackattack", views: 3, - problem: "String Scramble", + problem: "add-user-profile-endpoint", category: "any%", runsCount: 1, start: new Date().getTime(), }, { id: "4", - username: "reset_king", + username: "fix-prod-build", views: 3, problem: "Linked List Rush", category: "any%", diff --git a/src/modules/problems/core.ts b/src/modules/problems/core.ts index 707a28f..a0c2346 100644 --- a/src/modules/problems/core.ts +++ b/src/modules/problems/core.ts @@ -20,7 +20,7 @@ const problems: ProblemDefinition[] = [ description: "Production build fails while development works. Fix the build.", language: "javascript", - difficulty: "easy", + difficulty: "hard", validate100: () => true, validateAny: () => true, }, @@ -29,7 +29,7 @@ const problems: ProblemDefinition[] = [ title: "Implement Missing API Endpoint", description: "The frontend expects `/api/me`. Implement it correctly.", language: "go", - difficulty: "easy", + difficulty: "medium", validate100: () => true, validateAny: () => true, }, From 73c33691d584d446e50999fe8a6d1d114a770fac Mon Sep 17 00:00:00 2001 From: Leo dev Date: Sat, 7 Feb 2026 22:41:29 +0100 Subject: [PATCH 07/14] Proper runs structure --- .../[runId]}/components/Editor.tsx | 0 .../[runId]}/components/FileTree.tsx | 0 .../[runId]}/components/ProfileCard.tsx | 0 .../[runId]}/components/RunChat.tsx | 0 .../[runId]}/components/RunTimer.tsx | 0 .../{run => runs/[runId]}/components/theme.ts | 0 src/app/{run => runs/[runId]}/page.tsx | 2 +- src/app/runs/components/LiveRunRow.tsx | 21 +++++++++++++++---- src/app/runs/page.tsx | 7 ++++--- 9 files changed, 22 insertions(+), 8 deletions(-) rename src/app/{run => runs/[runId]}/components/Editor.tsx (100%) rename src/app/{run => runs/[runId]}/components/FileTree.tsx (100%) rename src/app/{run => runs/[runId]}/components/ProfileCard.tsx (100%) rename src/app/{run => runs/[runId]}/components/RunChat.tsx (100%) rename src/app/{run => runs/[runId]}/components/RunTimer.tsx (100%) rename src/app/{run => runs/[runId]}/components/theme.ts (100%) rename src/app/{run => runs/[runId]}/page.tsx (97%) diff --git a/src/app/run/components/Editor.tsx b/src/app/runs/[runId]/components/Editor.tsx similarity index 100% rename from src/app/run/components/Editor.tsx rename to src/app/runs/[runId]/components/Editor.tsx diff --git a/src/app/run/components/FileTree.tsx b/src/app/runs/[runId]/components/FileTree.tsx similarity index 100% rename from src/app/run/components/FileTree.tsx rename to src/app/runs/[runId]/components/FileTree.tsx diff --git a/src/app/run/components/ProfileCard.tsx b/src/app/runs/[runId]/components/ProfileCard.tsx similarity index 100% rename from src/app/run/components/ProfileCard.tsx rename to src/app/runs/[runId]/components/ProfileCard.tsx diff --git a/src/app/run/components/RunChat.tsx b/src/app/runs/[runId]/components/RunChat.tsx similarity index 100% rename from src/app/run/components/RunChat.tsx rename to src/app/runs/[runId]/components/RunChat.tsx diff --git a/src/app/run/components/RunTimer.tsx b/src/app/runs/[runId]/components/RunTimer.tsx similarity index 100% rename from src/app/run/components/RunTimer.tsx rename to src/app/runs/[runId]/components/RunTimer.tsx diff --git a/src/app/run/components/theme.ts b/src/app/runs/[runId]/components/theme.ts similarity index 100% rename from src/app/run/components/theme.ts rename to src/app/runs/[runId]/components/theme.ts diff --git a/src/app/run/page.tsx b/src/app/runs/[runId]/page.tsx similarity index 97% rename from src/app/run/page.tsx rename to src/app/runs/[runId]/page.tsx index a464677..7548e97 100644 --- a/src/app/run/page.tsx +++ b/src/app/runs/[runId]/page.tsx @@ -8,7 +8,7 @@ import { File } from "lucide-react"; import Editor from "./components/Editor"; import useAction, { useActionInterval } from "@/hook/use-action"; import { getLiveRun } from "@/modules/live-run/actions"; -import ProblemCard from "../problems/components/ProblemCard"; +import ProblemCard from "@/app/problems/components/ProblemCard"; import ProfileCard from "./components/ProfileCard"; import { getProblem } from "@/modules/problems/actions"; diff --git a/src/app/runs/components/LiveRunRow.tsx b/src/app/runs/components/LiveRunRow.tsx index 5655586..08faad2 100644 --- a/src/app/runs/components/LiveRunRow.tsx +++ b/src/app/runs/components/LiveRunRow.tsx @@ -8,7 +8,8 @@ import formatTime from "@/lib/time-format"; import useAction from "@/hook/use-action"; import { getProblem } from "@/modules/problems/actions"; import Link from "next/link"; -import { LinkIcon } from "lucide-react"; +import { Eye, LinkIcon } from "lucide-react"; +import { Button } from "@/components/ui/button"; export default function LiveRunRow({ index, @@ -57,10 +58,22 @@ export default function LiveRunRow({ {/* Views */} - {isTop ? {run.views} : run.views} + + {isTop ? {run.views} : run.views} + - {/* Live status with category color */} - {run.category} + {/* Category */} + {run.category} + + {/* Actions */} + + + + + ); } diff --git a/src/app/runs/page.tsx b/src/app/runs/page.tsx index 3e9a71d..bac9078 100644 --- a/src/app/runs/page.tsx +++ b/src/app/runs/page.tsx @@ -49,9 +49,10 @@ export default function LiveRuns() { Runner Problem - Time - Views - Category + Time + Views + Category + From ddb19a4ce822c2362a08e75d757ed0a19808d58c Mon Sep 17 00:00:00 2001 From: Leo dev Date: Sat, 7 Feb 2026 22:44:24 +0100 Subject: [PATCH 08/14] Filter UI --- src/app/runs/page.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app/runs/page.tsx b/src/app/runs/page.tsx index bac9078..51ec027 100644 --- a/src/app/runs/page.tsx +++ b/src/app/runs/page.tsx @@ -32,11 +32,11 @@ export default function LiveRuns() { {/* Filters */}
- - + + any% - + 100% From 1f969b8ff15037996402ad9cd9bc6a6ee0a08e02 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Sat, 7 Feb 2026 23:11:09 +0100 Subject: [PATCH 09/14] A little improvement on move handling --- src/app/runs/[runId]/components/Editor.tsx | 7 ++++--- src/app/runs/[runId]/page.tsx | 2 +- src/hook/use-action.ts | 20 ++++++++++++++++++++ 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/app/runs/[runId]/components/Editor.tsx b/src/app/runs/[runId]/components/Editor.tsx index 7b28440..4cc61f5 100644 --- a/src/app/runs/[runId]/components/Editor.tsx +++ b/src/app/runs/[runId]/components/Editor.tsx @@ -9,8 +9,9 @@ import { json } from "@codemirror/lang-json"; import { python } from "@codemirror/lang-python"; import { java } from "@codemirror/lang-java"; import { useEffect, useState } from "react"; -import useAction from "@/hook/use-action"; +import useAction, { useActionOnce } from "@/hook/use-action"; import { getLiveRunMoves } from "@/modules/live-run/actions"; +import { LiveRun } from "@/modules/live-run/types"; type Language = | "javascript" @@ -50,9 +51,9 @@ type Move = { }; }; -export default function Editor() { +export default function Editor({ run }: { run: LiveRun | null }) { const [editorView, setEditorView] = useState(); - const [moves] = useAction(getLiveRunMoves); + const [moves] = useActionOnce(getLiveRunMoves, [run]); useEffect(() => { if (!editorView || !moves) return; diff --git a/src/app/runs/[runId]/page.tsx b/src/app/runs/[runId]/page.tsx index 7548e97..0899a64 100644 --- a/src/app/runs/[runId]/page.tsx +++ b/src/app/runs/[runId]/page.tsx @@ -82,7 +82,7 @@ export default function Run() { myfile.js
- +
diff --git a/src/hook/use-action.ts b/src/hook/use-action.ts index 1af384a..984d771 100644 --- a/src/hook/use-action.ts +++ b/src/hook/use-action.ts @@ -18,6 +18,26 @@ export default function useAction( return [value, loaded, error]; } +export function useActionOnce( + action: () => Promise, + dependencies: any[] = [], +): [T | null, boolean, Error | null] { + const [value, setValue] = useState(null); + const [loaded, setLoaded] = useState(false); + const [error, setError] = useState(null); + + useEffect(() => { + if (loaded) return; + + action() + .then(setValue) + .catch(setError) + .finally(() => setLoaded(true)); + }, dependencies); + + return [value, loaded, error]; +} + export function useActionInterval( action: () => Promise, interval: number, From 6744a67931979bdd1a0e3ef1841a0a624f36745a Mon Sep 17 00:00:00 2001 From: Leo dev Date: Sat, 7 Feb 2026 23:25:37 +0100 Subject: [PATCH 10/14] Preparing for actual live moves --- src/app/runs/[runId]/components/Editor.tsx | 16 ++---- src/modules/live-run/actions.ts | 57 ++-------------------- src/modules/live-run/core.ts | 29 +++++++++++ src/modules/live-run/types.ts | 10 ++++ 4 files changed, 45 insertions(+), 67 deletions(-) diff --git a/src/app/runs/[runId]/components/Editor.tsx b/src/app/runs/[runId]/components/Editor.tsx index 4cc61f5..0232795 100644 --- a/src/app/runs/[runId]/components/Editor.tsx +++ b/src/app/runs/[runId]/components/Editor.tsx @@ -11,7 +11,7 @@ import { java } from "@codemirror/lang-java"; import { useEffect, useState } from "react"; import useAction, { useActionOnce } from "@/hook/use-action"; import { getLiveRunMoves } from "@/modules/live-run/actions"; -import { LiveRun } from "@/modules/live-run/types"; +import { LiveRun, LiveRunMove } from "@/modules/live-run/types"; type Language = | "javascript" @@ -41,26 +41,16 @@ function getLanguageExtension(lang: Language) { } } -type Move = { - latency: number; - cursor: number; - changes: { - from: number; - to: number; - insert: string; - }; -}; - export default function Editor({ run }: { run: LiveRun | null }) { const [editorView, setEditorView] = useState(); - const [moves] = useActionOnce(getLiveRunMoves, [run]); + const [moves] = useActionOnce(getLiveRunMoves, [run]); useEffect(() => { if (!editorView || !moves) return; let cancelled = false; - const executeMove = async (move: Move) => { + const executeMove = async (move: LiveRunMove) => { return new Promise((resolve) => { const timeout = setTimeout(() => { if (!editorView || cancelled) return resolve(); diff --git a/src/modules/live-run/actions.ts b/src/modules/live-run/actions.ts index 518c00e..df15010 100644 --- a/src/modules/live-run/actions.ts +++ b/src/modules/live-run/actions.ts @@ -1,65 +1,14 @@ "use server"; -import { createClient } from "@/lib/supabase/server"; -import { getSession, getSessionRedirect } from "../account/core"; import * as Core from "./core"; import { LiveRun } from "./types"; export async function getLiveRuns(): Promise { - const supabase = await createClient(); - - const _ = await getSession(supabase); - - return [ - { - id: "1", - username: "speedmaster_x", - problem: "fix-auth-callback", - category: "any%", - views: 670, - runsCount: 1, - start: new Date().getTime(), - }, - { - id: "2", - username: "algo_ninja", - views: 3, - problem: "fix-prod-build", - category: "any%", - runsCount: 1, - start: new Date().getTime(), - }, - { - id: "3", - username: "stackattack", - views: 3, - problem: "add-user-profile-endpoint", - category: "any%", - runsCount: 1, - start: new Date().getTime(), - }, - { - id: "4", - username: "fix-prod-build", - views: 3, - problem: "Linked List Rush", - category: "any%", - runsCount: 1, - start: new Date().getTime(), - }, - ]; + return Core.getLiveRuns(); } -export async function getLiveRun(): Promise { - return { - id: "0", - username: "selimaj", - start: 1770497350654, - views: 230, - problem: "fix-auth-callback", - category: "any%", - runsCount: 0, - }; +export async function getLiveRun(id: string): Promise { + return Core.getLiveRun(id); } export async function getLiveRunMoves() { diff --git a/src/modules/live-run/core.ts b/src/modules/live-run/core.ts index a59f71d..247a291 100644 --- a/src/modules/live-run/core.ts +++ b/src/modules/live-run/core.ts @@ -1,3 +1,32 @@ "use server"; import "server-only"; +import { LiveRun, LiveRunMove } from "./types"; + +const liveRuns: LiveRun[] = []; +const liveRunMoves: Record = {}; + +export async function getLiveRuns(): Promise { + return liveRuns; +} + +export async function getLiveRun(id: string): Promise { + return liveRuns.find((r) => r.id === id); +} + +export async function getLiveRunMoves( + id: string, +): Promise { + return liveRunMoves[id]; +} + +export async function addRun(run: LiveRun) { + if (!liveRuns.find((r) => r.id === run.id || r.username === run.username)) + throw "Duplicate run"; + + liveRuns.push(run); +} + +export async function addLiveRunMove(id: string) { + if (!liveRuns.find((r) => r.id === id)) throw "Invalid run"; +} diff --git a/src/modules/live-run/types.ts b/src/modules/live-run/types.ts index f4073a4..9673d12 100644 --- a/src/modules/live-run/types.ts +++ b/src/modules/live-run/types.ts @@ -7,3 +7,13 @@ export interface LiveRun { problem: string; category: "any%" | "100%"; } + +export interface LiveRunMove { + latency: number; + cursor: number; + changes?: { + from: number; + to: number; + insert: string; + }; +} From 1ae88f2e223d6477c012317f74f1e2500b5a0b3f Mon Sep 17 00:00:00 2001 From: Leo dev Date: Sat, 7 Feb 2026 23:36:18 +0100 Subject: [PATCH 11/14] Fixed live run page --- src/app/runs/[runId]/components/Editor.tsx | 2 +- src/app/runs/[runId]/page.tsx | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/app/runs/[runId]/components/Editor.tsx b/src/app/runs/[runId]/components/Editor.tsx index 0232795..82ccf7b 100644 --- a/src/app/runs/[runId]/components/Editor.tsx +++ b/src/app/runs/[runId]/components/Editor.tsx @@ -41,7 +41,7 @@ function getLanguageExtension(lang: Language) { } } -export default function Editor({ run }: { run: LiveRun | null }) { +export default function Editor({ run }: { run: LiveRun | null | undefined }) { const [editorView, setEditorView] = useState(); const [moves] = useActionOnce(getLiveRunMoves, [run]); diff --git a/src/app/runs/[runId]/page.tsx b/src/app/runs/[runId]/page.tsx index 0899a64..943d86f 100644 --- a/src/app/runs/[runId]/page.tsx +++ b/src/app/runs/[runId]/page.tsx @@ -11,16 +11,20 @@ import { getLiveRun } from "@/modules/live-run/actions"; import ProblemCard from "@/app/problems/components/ProblemCard"; import ProfileCard from "./components/ProfileCard"; import { getProblem } from "@/modules/problems/actions"; +import { useParams } from "next/navigation"; export default function Run() { - const [run] = useActionInterval(getLiveRun, 1000); + const params = useParams<{ runId: string }>(); + + const [run] = useActionInterval(() => getLiveRun(params.runId), 1000, [ + params, + ]); + const [problem] = useAction( async () => (run?.id ? getProblem(run.problem) : null), [run], ); - console.log(problem); - const chatMessages: ChatMessage[] = [ { user: "Alice", message: "Nice split!", time: "1:53" }, { user: "Bob", message: "Watch out for the string problem!", time: "1:54" }, From 5123ebc6b73f0a8a7e1cdf28c86db3a499f4203c Mon Sep 17 00:00:00 2001 From: Leo dev Date: Sat, 7 Feb 2026 23:39:15 +0100 Subject: [PATCH 12/14] Editor moves --- src/app/runs/[runId]/components/Editor.tsx | 5 ++- src/modules/live-run/actions.ts | 36 ++-------------------- 2 files changed, 6 insertions(+), 35 deletions(-) diff --git a/src/app/runs/[runId]/components/Editor.tsx b/src/app/runs/[runId]/components/Editor.tsx index 82ccf7b..dc9b865 100644 --- a/src/app/runs/[runId]/components/Editor.tsx +++ b/src/app/runs/[runId]/components/Editor.tsx @@ -43,7 +43,10 @@ function getLanguageExtension(lang: Language) { export default function Editor({ run }: { run: LiveRun | null | undefined }) { const [editorView, setEditorView] = useState(); - const [moves] = useActionOnce(getLiveRunMoves, [run]); + const [moves] = useActionOnce( + async () => (run ? (await getLiveRunMoves(run.id)) || [] : []), + [run], + ); useEffect(() => { if (!editorView || !moves) return; diff --git a/src/modules/live-run/actions.ts b/src/modules/live-run/actions.ts index df15010..55b95a6 100644 --- a/src/modules/live-run/actions.ts +++ b/src/modules/live-run/actions.ts @@ -11,38 +11,6 @@ export async function getLiveRun(id: string): Promise { return Core.getLiveRun(id); } -export async function getLiveRunMoves() { - const code = `// Import the necessary React library features -import { createRoot } from 'react-dom/client'; - -// Define a functional component named "App" -// Component names must start with a capital letter -function App() { - // Components return JSX markup - return ( -

Hello, world!

- ); -} - -// Target a DOM element in your HTML (e.g.,
) -const domNode = document.getElementById('root'); - -// Create a root and render your component into the DOM -const root = createRoot(domNode); -root.render();`.repeat(3); - - const chunks = []; - for (let i = 0; i < code.length; i += 4) { - chunks.push(code.slice(i, i + 4)); - } - - return chunks.map((chunk, index) => ({ - latency: 120, // 100ms per chunk - cursor: index * 4, // approximate cursor position - changes: { - from: index * 4, - to: index * 4, - insert: chunk, - }, - })); +export async function getLiveRunMoves(id: string) { + return Core.getLiveRunMoves(id); } From f05b235c4dfdd1f3f48c1283f81ddd120e6ffaa4 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Sat, 7 Feb 2026 23:45:23 +0100 Subject: [PATCH 13/14] Editor moves --- src/modules/live-run/core.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/modules/live-run/core.ts b/src/modules/live-run/core.ts index 247a291..bef5c97 100644 --- a/src/modules/live-run/core.ts +++ b/src/modules/live-run/core.ts @@ -27,6 +27,14 @@ export async function addRun(run: LiveRun) { liveRuns.push(run); } -export async function addLiveRunMove(id: string) { - if (!liveRuns.find((r) => r.id === id)) throw "Invalid run"; +export async function addLiveRunMoves(id: string, moves: LiveRunMove[]) { + if (!liveRunMoves[id]) throw "Invalid run"; + + // Add the moves immediately + liveRunMoves[id] = [...liveRunMoves[id], ...moves]; + + // After 3 seconds, remove only the moves we just added + setTimeout(() => { + liveRunMoves[id] = liveRunMoves[id].filter((move) => !moves.includes(move)); + }, 3000); } From 2faa330a3851da835c5761d8c4a41438d402ecdc Mon Sep 17 00:00:00 2001 From: Leo dev Date: Sun, 8 Feb 2026 02:39:52 +0100 Subject: [PATCH 14/14] Quick play run --- src/app/problems/components/ProblemCard.tsx | 8 +++++++- src/app/problems/page.tsx | 2 -- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/app/problems/components/ProblemCard.tsx b/src/app/problems/components/ProblemCard.tsx index 3ea94e4..8b0e4ff 100644 --- a/src/app/problems/components/ProblemCard.tsx +++ b/src/app/problems/components/ProblemCard.tsx @@ -97,7 +97,13 @@ const ProblemCard = ({ {/* Footer */} - diff --git a/src/app/problems/page.tsx b/src/app/problems/page.tsx index 2abc43d..2810e66 100644 --- a/src/app/problems/page.tsx +++ b/src/app/problems/page.tsx @@ -14,8 +14,6 @@ import { getProblems } from "@/modules/problems/actions"; export default function Problems() { const [problems, problemsLoaded, problemsError] = useAction(getProblems); - console.log(problems); - const [viewMode, setViewMode] = useState<"grid" | "list">("grid"); const difficulties = ["All", "Easy", "Medium", "Hard"];