Shadcn-ified run
This commit is contained in:
+92
-127
@@ -1,13 +1,12 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { FileTree } from "@sinm/react-file-tree";
|
import { FileTree } from "@sinm/react-file-tree";
|
||||||
import CodeMirror, {
|
import CodeMirror, { EditorState } from "@uiw/react-codemirror";
|
||||||
EditorState,
|
|
||||||
EditorView,
|
|
||||||
ReactCodeMirrorRef,
|
|
||||||
} from "@uiw/react-codemirror";
|
|
||||||
import { vscodeDark } from "@uiw/codemirror-theme-vscode";
|
import { vscodeDark } from "@uiw/codemirror-theme-vscode";
|
||||||
import { useEffect, useRef } from "react";
|
|
||||||
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Separator } from "@/components/ui/separator";
|
||||||
|
|
||||||
type RunInfo = {
|
type RunInfo = {
|
||||||
category: "any%" | "100%";
|
category: "any%" | "100%";
|
||||||
@@ -24,45 +23,6 @@ type ChatMessage = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default function Run() {
|
export default function Run() {
|
||||||
const splits = [
|
|
||||||
{
|
|
||||||
problemName: "Two Sum Dash",
|
|
||||||
splitTime: "0:38",
|
|
||||||
delta: "-0:04",
|
|
||||||
isCompleted: true,
|
|
||||||
pbTime: "0:42",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
problemName: "Binary Sprint",
|
|
||||||
splitTime: "1:12",
|
|
||||||
delta: "+0:02",
|
|
||||||
isCompleted: true,
|
|
||||||
pbTime: "0:32",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
problemName: "String Scramble",
|
|
||||||
splitTime: "--:--",
|
|
||||||
delta: undefined,
|
|
||||||
isCompleted: false,
|
|
||||||
isCurrent: true,
|
|
||||||
pbTime: "0:55",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
problemName: "Linked List Rush",
|
|
||||||
splitTime: "--:--",
|
|
||||||
delta: undefined,
|
|
||||||
isCompleted: false,
|
|
||||||
pbTime: "1:24",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
problemName: "Stack Overflow",
|
|
||||||
splitTime: "--:--",
|
|
||||||
delta: undefined,
|
|
||||||
isCompleted: false,
|
|
||||||
pbTime: "1:42",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const runInfo: RunInfo = {
|
const runInfo: RunInfo = {
|
||||||
category: "any%",
|
category: "any%",
|
||||||
problems: 5,
|
problems: 5,
|
||||||
@@ -71,7 +31,7 @@ export default function Run() {
|
|||||||
currentAttempt: 47,
|
currentAttempt: 47,
|
||||||
};
|
};
|
||||||
|
|
||||||
const chatMessages = [
|
const chatMessages: ChatMessage[] = [
|
||||||
{ user: "Alice", message: "Nice split!", time: "1:53" },
|
{ user: "Alice", message: "Nice split!", time: "1:53" },
|
||||||
{ user: "Bob", message: "Watch out for the string problem!", time: "1:54" },
|
{ user: "Bob", message: "Watch out for the string problem!", time: "1:54" },
|
||||||
{ user: "Charlie", message: "PB incoming? 👀", time: "1:55" },
|
{ user: "Charlie", message: "PB incoming? 👀", time: "1:55" },
|
||||||
@@ -80,107 +40,112 @@ export default function Run() {
|
|||||||
return (
|
return (
|
||||||
<div className="h-svh px-20 pt-24 pb-12 flex gap-8">
|
<div className="h-svh px-20 pt-24 pb-12 flex gap-8">
|
||||||
{/* Editor */}
|
{/* Editor */}
|
||||||
<div className="h-full w-full bg-card rounded-2xl flex overflow-clip">
|
<Card className="flex-1 overflow-hidden">
|
||||||
<div className="h-full w-2xs border-r-2 p-5">
|
<CardContent className="flex h-full">
|
||||||
<FileTree
|
{/* File tree */}
|
||||||
tree={{
|
<div className="w-2xs border-r border-border">
|
||||||
type: "directory",
|
<FileTree
|
||||||
uri: "my-app",
|
tree={{
|
||||||
expanded: true,
|
type: "directory",
|
||||||
children: [
|
uri: "my-app",
|
||||||
{
|
expanded: true,
|
||||||
type: "file",
|
children: [{ type: "file", uri: "myfile.js" }],
|
||||||
uri: "myfile.js",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="h-full w-full p-5 pt-4">
|
|
||||||
<header>myfile.js</header>
|
|
||||||
<div className="pt-3 pb-5 w-full h-full">
|
|
||||||
<CodeMirror
|
|
||||||
readOnly
|
|
||||||
className="w-full h-full"
|
|
||||||
value="yo"
|
|
||||||
extensions={[
|
|
||||||
EditorState.transactionFilter.of((tr) => {
|
|
||||||
if (tr.selection && tr.isUserEvent("select")) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
return tr;
|
|
||||||
}),
|
|
||||||
]}
|
|
||||||
theme={vscodeDark}
|
|
||||||
onCreateEditor={(view) => {
|
|
||||||
view.focus();
|
|
||||||
|
|
||||||
view.dispatch({
|
|
||||||
selection: { anchor: 1 },
|
|
||||||
scrollIntoView: true,
|
|
||||||
});
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
{/* Code */}
|
||||||
|
<div className="flex-1 pl-5 flex flex-col">
|
||||||
|
<header className="text-sm font-medium text-muted-foreground">
|
||||||
|
myfile.js
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div className="flex-1 pt-3 pb-5">
|
||||||
|
<CodeMirror
|
||||||
|
readOnly
|
||||||
|
className="h-full w-full"
|
||||||
|
value="yo"
|
||||||
|
extensions={[
|
||||||
|
EditorState.transactionFilter.of((tr) => {
|
||||||
|
if (tr.selection && tr.isUserEvent("select")) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return tr;
|
||||||
|
}),
|
||||||
|
]}
|
||||||
|
theme={vscodeDark}
|
||||||
|
onCreateEditor={(view) => {
|
||||||
|
view.focus();
|
||||||
|
view.dispatch({
|
||||||
|
selection: { anchor: 1 },
|
||||||
|
scrollIntoView: true,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
{/* Timer & Chat */}
|
{/* Timer & Chat */}
|
||||||
<div className="flex flex-col gap-6 h-full">
|
<div className="flex flex-col gap-6 h-full w-sm">
|
||||||
<Timer runInfo={runInfo} />
|
<RunTimer runInfo={runInfo} />
|
||||||
<Chat chatMessages={chatMessages} />
|
<RunChat chatMessages={chatMessages} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function Timer({ runInfo }: { runInfo: RunInfo }) {
|
/* ---------------- Timer ---------------- */
|
||||||
|
|
||||||
|
function RunTimer({ runInfo }: { runInfo: RunInfo }) {
|
||||||
return (
|
return (
|
||||||
<div className="bg-card border border-border rounded-2xl p-6 text-center relative">
|
<Card className="relative text-center overflow-hidden">
|
||||||
<div className="absolute inset-0 bg-linear-to-b from-primary/5 to-transparent pointer-events-none rounded-t-2xl" />
|
<div className="absolute inset-0 bg-linear-to-b from-primary/5 to-transparent pointer-events-none" />
|
||||||
<div className="relative">
|
<CardContent className="relative">
|
||||||
<p className="timer-display text-primary text-4xl font-bold">1:52.34</p>
|
<p className="timer-display text-primary text-4xl font-bold">1:52.34</p>
|
||||||
<p className="text-muted-foreground mt-2 font-mono text-sm">
|
<p className="mt-2 font-mono text-sm text-muted-foreground">
|
||||||
PB: {runInfo.pbTime} | Diff:{" "}
|
PB: {runInfo.pbTime} | Diff:{" "}
|
||||||
<span className="text-red-400">+0:12</span>
|
<span className="text-red-400">+0:12</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</CardContent>
|
||||||
</div>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function Chat({ chatMessages }: { chatMessages: ChatMessage[] }) {
|
/* ---------------- Chat ---------------- */
|
||||||
|
|
||||||
|
function RunChat({ chatMessages }: { chatMessages: ChatMessage[] }) {
|
||||||
return (
|
return (
|
||||||
<div className="h-full flex flex-col flex-1 w-sm bg-card border border-border rounded-xl p-4">
|
<Card className="flex flex-col flex-1">
|
||||||
{/* Header */}
|
<CardHeader>
|
||||||
<div className="flex items-center justify-between mb-4">
|
<div className="flex items-center justify-between">
|
||||||
<h3 className="font-semibold text-foreground">Chat</h3>
|
<CardTitle className="text-base">Chat</CardTitle>
|
||||||
<span className="text-xs text-muted-foreground">
|
<span className="text-xs text-muted-foreground">
|
||||||
{chatMessages.length} messages
|
{chatMessages.length} messages
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
</CardHeader>
|
||||||
|
|
||||||
{/* Messages area */}
|
<Separator />
|
||||||
<div className="flex-1 overflow-y-auto space-y-2">
|
|
||||||
{chatMessages.map((msg, idx) => (
|
|
||||||
<div key={idx} className="bg-secondary/50 rounded-lg p-2">
|
|
||||||
<span className="font-semibold text-foreground">{msg.user}</span>:{" "}
|
|
||||||
<span className="text-muted-foreground">{msg.message}</span>
|
|
||||||
<span className="text-xs text-muted-foreground ml-2">
|
|
||||||
{msg.time}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Input */}
|
<CardContent className="flex flex-col flex-1 gap-3">
|
||||||
<div className="mt-4">
|
{/* Messages */}
|
||||||
<input
|
<div className="flex-1 overflow-y-auto space-y-2">
|
||||||
type="text"
|
{chatMessages.map((msg, i) => (
|
||||||
placeholder="Type a message..."
|
<div key={i} className="rounded-lg bg-secondary/50 p-2 text-sm">
|
||||||
className="w-full rounded-lg border border-border p-2 text-sm bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary"
|
<span className="font-semibold text-foreground">{msg.user}</span>:{" "}
|
||||||
/>
|
<span className="text-muted-foreground">{msg.message}</span>
|
||||||
</div>
|
<span className="ml-2 text-xs text-muted-foreground">
|
||||||
</div>
|
{msg.time}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Input */}
|
||||||
|
<Input placeholder="Type a message…" />
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user