diff --git a/ai/usage-log.md b/ai/usage-log.md
index 7fc09d10..cfd982a3 100644
--- a/ai/usage-log.md
+++ b/ai/usage-log.md
@@ -392,6 +392,30 @@ Request to create a simple client-side auth guard that prevents browser back nav
- UX improvements: immediate redirect prevents brief flash of protected content
- Maintainability: simple, reusable component for future auth requirements
+
+## Entry 13
+
+# Date/Time:
+2025-09-19 19:00
+
+# Tool:
+Gemini (Tried to google the answer online and Google's AI overview mentioned the recommendations)
+
+# Prompt/Command:
+Ask on how to disable scrollbar and to not create additional empty spaces when there is still sufficient space to enter code.
+
+# Output Summary:
+- Explain that Monaco Editor has a field called "scrollBeyondLastLine" that when disabled will cause the scrollbar to scroll only till the last line.
+
+# Action Taken:
+- [X] Accepted as-is
+- [ ] Modified
+- [ ] Rejected
+
+# Author Notes:
+- Add the field "scrollBeyondLastLine" and set it to false in the Monaco Editor configuration
+- Tested the UI and found that the scrollbar will not appear if there is sufficient space for code to be entered. If code entered has exceeded the maximum container height, the scrollbar will only allow the user to scroll till the last line.
+
---
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index c664b2ba..a413ef56 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -9,6 +9,7 @@
"version": "0.1.0",
"dependencies": {
"@hookform/resolvers": "^5.2.1",
+ "@monaco-editor/react": "^4.7.0",
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-dropdown-menu": "^2.1.16",
"@radix-ui/react-label": "^2.1.7",
@@ -810,6 +811,29 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
+ "node_modules/@monaco-editor/loader": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.5.0.tgz",
+ "integrity": "sha512-hKoGSM+7aAc7eRTRjpqAZucPmoNOC4UUbknb/VNoTkEIkCPhqV8LfbsgM1webRM7S/z21eHEx9Fkwx8Z/C/+Xw==",
+ "license": "MIT",
+ "dependencies": {
+ "state-local": "^1.0.6"
+ }
+ },
+ "node_modules/@monaco-editor/react": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/@monaco-editor/react/-/react-4.7.0.tgz",
+ "integrity": "sha512-cyzXQCtO47ydzxpQtCGSQGOC8Gk3ZUeBXFAxD+CWXYFo5OqZyZUonFl0DwUlTyAfRHntBfw2p3w4s9R6oe1eCA==",
+ "license": "MIT",
+ "dependencies": {
+ "@monaco-editor/loader": "^1.5.0"
+ },
+ "peerDependencies": {
+ "monaco-editor": ">= 0.25.0 < 1",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
"node_modules/@napi-rs/wasm-runtime": {
"version": "0.2.12",
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz",
@@ -1974,6 +1998,13 @@
"@types/react": "^19.0.0"
}
},
+ "node_modules/@types/trusted-types": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-1.0.6.tgz",
+ "integrity": "sha512-230RC8sFeHoT6sSUlRO6a8cAnclO06eeiq1QDfiv2FGCLWFvvERWgwIQD4FWqD9A69BN7Lzee4OXwoMVnnsWDw==",
+ "license": "MIT",
+ "peer": true
+ },
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.42.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.42.0.tgz",
@@ -5383,6 +5414,16 @@
"url": "https://github.com/sponsors/isaacs"
}
},
+ "node_modules/monaco-editor": {
+ "version": "0.53.0",
+ "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.53.0.tgz",
+ "integrity": "sha512-0WNThgC6CMWNXXBxTbaYYcunj08iB5rnx4/G56UOPeL9UVIUGGHA1GR0EWIh9Ebabj7NpCRawQ5b0hfN1jQmYQ==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@types/trusted-types": "^1.0.6"
+ }
+ },
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@@ -6418,6 +6459,12 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/state-local": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz",
+ "integrity": "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==",
+ "license": "MIT"
+ },
"node_modules/stop-iteration-iterator": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz",
diff --git a/frontend/package.json b/frontend/package.json
index 8ce79a47..393ec8a3 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -12,6 +12,7 @@
},
"dependencies": {
"@hookform/resolvers": "^5.2.1",
+ "@monaco-editor/react": "^4.7.0",
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-dropdown-menu": "^2.1.16",
"@radix-ui/react-label": "^2.1.7",
diff --git a/frontend/src/app/collab/page.tsx b/frontend/src/app/collab/page.tsx
index 9d4da316..1173ba1f 100644
--- a/frontend/src/app/collab/page.tsx
+++ b/frontend/src/app/collab/page.tsx
@@ -1,11 +1,26 @@
-import ContentPage from "../components/collab/ContentPage";
+import ChatComponent from "../components/collab/ChatComponent";
+import CodingComponent from "../components/collab/CodingComponent";
+import QuestionComponent from "../components/collab/QuestionComponent";
import SessionHeader from "../components/collab/SessionHeader";
export default function CollabPage() {
return (
-
+
+
);
}
diff --git a/frontend/src/app/components/collab/ChatComponent.tsx b/frontend/src/app/components/collab/ChatComponent.tsx
new file mode 100644
index 00000000..2bdeb295
--- /dev/null
+++ b/frontend/src/app/components/collab/ChatComponent.tsx
@@ -0,0 +1,20 @@
+import { Button } from "@/components/ui/button";
+import { Input } from "@/components/ui/input";
+import { ChevronRightIcon } from "lucide-react";
+
+export default function ChatComponent() {
+ const chatMessages = ["HELLO", "HOW ARE YOU?"];
+
+ return (
+
+ );
+}
diff --git a/frontend/src/app/components/collab/CodingComponent.tsx b/frontend/src/app/components/collab/CodingComponent.tsx
new file mode 100644
index 00000000..fad88c8c
--- /dev/null
+++ b/frontend/src/app/components/collab/CodingComponent.tsx
@@ -0,0 +1,70 @@
+"use client";
+
+import Editor from "@monaco-editor/react";
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuGroup,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "@/components/ui/dropdown-menu";
+import { useState } from "react";
+import { Button } from "@/components/ui/button";
+import { ChevronDown, CircleUser } from "lucide-react";
+
+export default function CodingComponent() {
+ const [codeContent, setCodeContent] = useState("");
+ const [selectedLanguage, setSeletedLanguage] = useState("JavaScript");
+
+ function setInitialContent(value: string | undefined) {
+ if (value != undefined) {
+ setCodeContent(value);
+ }
+ }
+
+ return (
+
+
+
+
+
+ {selectedLanguage}
+
+
+
+
+ setSeletedLanguage("JavaScript")}
+ >
+ Javascript
+
+ setSeletedLanguage("Python")}>
+ Python
+
+ setSeletedLanguage("C")}>
+ C
+
+ setSeletedLanguage("C++")}>
+ C++
+
+ setSeletedLanguage("Java")}>
+ Java
+
+
+
+
+
+
derrickwong8909@gmail.com
+
+
+
+
setInitialContent(value)}
+ options={{ scrollBeyondLastLine: false }}
+ >
+
+ );
+}
diff --git a/frontend/src/app/components/collab/ContentPage.tsx b/frontend/src/app/components/collab/ContentPage.tsx
deleted file mode 100644
index 1e1a64d8..00000000
--- a/frontend/src/app/components/collab/ContentPage.tsx
+++ /dev/null
@@ -1,15 +0,0 @@
-export default function ContentPage() {
- return (
-
- {/* Question Section */}
-
-
- {/* Coding Section */}
-
-
- {/* Chat Section */}
-
-
-
- );
-}
diff --git a/frontend/src/app/components/collab/QuestionComponent.tsx b/frontend/src/app/components/collab/QuestionComponent.tsx
new file mode 100644
index 00000000..eccb7e52
--- /dev/null
+++ b/frontend/src/app/components/collab/QuestionComponent.tsx
@@ -0,0 +1,43 @@
+import { Badge } from "@/components/ui/badge";
+import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
+
+export default function QuestionComponent() {
+ const difficultyMapping = {
+ Hard: "bg-red-900 text-black",
+ Medium: "bg-yellow-900 text-black",
+ Easy: "bg-green-900 text-black",
+ };
+
+ return (
+
+ {/* Criteria */}
+
+ Two Sum
+
+ Difficult
+
+
+
+
+ {/* Question Description */}
+
+ Description : We, the citizens of Singapore, pledge ourselves as one
+ united people, regardless of race, language or religion, to build a
+ democratic society based on justice and equality so as to achieve
+ happiness, prosperity and progress for our nation.
+
+
+ {/* Examples Section */}
+
+ Input : Test case 1 Output : Correct answer Explanation : Because it
+ is correct
+
+
+ {/* Constraints Section */}
+
+ 1 is less than n There will be a total of 1 trillion test cases
+
+
+
+ );
+}
diff --git a/frontend/src/app/components/collab/SessionHeader.tsx b/frontend/src/app/components/collab/SessionHeader.tsx
index 211fa6c2..b826c350 100644
--- a/frontend/src/app/components/collab/SessionHeader.tsx
+++ b/frontend/src/app/components/collab/SessionHeader.tsx
@@ -1,7 +1,37 @@
+"use client";
+import { Button } from "@/components/ui/button";
+import { Mic } from "lucide-react";
+import { useRouter } from "next/navigation";
+
export default function SessionHeader() {
+ const router = useRouter();
+
+ function directToMatch() {
+ router.replace("/match");
+ }
+
return (
-
- Session Header
+
+
+
+
+ directToMatch()}
+ className="bg-red-500 text-black mr-3 hover:bg-red-300"
+ >
+ Leave Session
+
);
}
diff --git a/frontend/src/app/components/layout/NavbarWrapper.tsx b/frontend/src/app/components/layout/NavbarWrapper.tsx
index 05e7885c..838dc8b5 100644
--- a/frontend/src/app/components/layout/NavbarWrapper.tsx
+++ b/frontend/src/app/components/layout/NavbarWrapper.tsx
@@ -15,8 +15,9 @@ export default function NavbarWrapper() {
// Don't show navbar on auth pages
const isAuthPage = pathname.startsWith("/auth");
+ const isCollabPage = pathname.startsWith("/collab");
- if (isAuthPage) {
+ if (isAuthPage || isCollabPage) {
return null;
}
diff --git a/frontend/src/components/ui/badge.tsx b/frontend/src/components/ui/badge.tsx
new file mode 100644
index 00000000..46f988c2
--- /dev/null
+++ b/frontend/src/components/ui/badge.tsx
@@ -0,0 +1,46 @@
+import * as React from "react";
+import { Slot } from "@radix-ui/react-slot";
+import { cva, type VariantProps } from "class-variance-authority";
+
+import { cn } from "@/lib/utils";
+
+const badgeVariants = cva(
+ "inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
+ {
+ variants: {
+ variant: {
+ default:
+ "border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
+ secondary:
+ "border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
+ destructive:
+ "border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
+ outline:
+ "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ },
+ },
+);
+
+function Badge({
+ className,
+ variant,
+ asChild = false,
+ ...props
+}: React.ComponentProps<"span"> &
+ VariantProps & { asChild?: boolean }) {
+ const Comp = asChild ? Slot : "span";
+
+ return (
+
+ );
+}
+
+export { Badge, badgeVariants };